1
0
mirror of https://github.com/GeyserMC/Rainbow.git synced 2025-12-19 14:59:16 +00:00

Split the mod in half

This commit is contained in:
Eclipse
2025-10-14 08:33:38 +00:00
parent 73bd8222be
commit 466427e974
32 changed files with 307 additions and 160 deletions

View File

@@ -1,3 +1,9 @@
plugins {
id("rainbow.base-conventions")
}
dependencies {
// Implement namedElements so IntelliJ can use it correctly, but include the remapped build
implementation(project(path = ":rainbow", configuration = "namedElements"))
include(project(":rainbow"))
}

View File

@@ -0,0 +1,41 @@
package org.geysermc.rainbow.client;
import net.minecraft.client.renderer.item.ClientItem;
import net.minecraft.client.resources.model.EquipmentClientInfo;
import net.minecraft.client.resources.model.ResolvedModel;
import net.minecraft.core.HolderLookup;
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.equipment.EquipmentAsset;
import org.geysermc.rainbow.mapping.AssetResolver;
import java.io.InputStream;
import java.util.Optional;
public class MinecraftAssetResolver implements AssetResolver {
@Override
public Optional<ResolvedModel> getResolvedModel(ResourceLocation location) {
return Optional.empty();
}
@Override
public Optional<ClientItem> getClientItem(ResourceLocation location) {
return Optional.empty();
}
@Override
public EquipmentClientInfo getEquipmentInfo(ResourceKey<EquipmentAsset> key) {
return null;
}
@Override
public InputStream getTexture(ResourceLocation location) {
return null;
}
@Override
public HolderLookup.Provider registries() {
return null;
}
}

View File

@@ -1,4 +1,4 @@
package org.geysermc.rainbow;
package org.geysermc.rainbow.client;
import org.geysermc.rainbow.pack.BedrockPack;
@@ -6,7 +6,6 @@ import java.io.IOException;
import java.nio.file.Path;
import java.util.Optional;
import java.util.function.Consumer;
import java.util.function.Function;
public final class PackManager {
@@ -17,7 +16,7 @@ public final class PackManager {
throw new IllegalStateException("Already started a pack (" + currentPack.get().name() + ")");
}
currentPack = Optional.of(new BedrockPack(name));
currentPack = Optional.of(new BedrockPack(name, new MinecraftAssetResolver()));
}
public void run(Consumer<BedrockPack> consumer) {

View File

@@ -0,0 +1,27 @@
package org.geysermc.rainbow.client;
import net.fabricmc.api.ClientModInitializer;
import net.fabricmc.fabric.api.client.command.v2.ClientCommandRegistrationCallback;
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents;
import net.fabricmc.fabric.api.command.v2.ArgumentTypeRegistry;
import net.minecraft.commands.synchronization.SingletonArgumentInfo;
import org.geysermc.rainbow.Rainbow;
import org.geysermc.rainbow.client.command.CommandSuggestionsArgumentType;
import org.geysermc.rainbow.client.command.PackGeneratorCommand;
import org.geysermc.rainbow.client.mapper.PackMapper;
public class RainbowClient implements ClientModInitializer {
private final PackManager packManager = new PackManager();
private final PackMapper packMapper = new PackMapper(packManager);
// TODO export language overrides
@Override
public void onInitializeClient() {
ClientCommandRegistrationCallback.EVENT.register((dispatcher, buildContext) -> PackGeneratorCommand.register(dispatcher, packManager, packMapper));
ClientTickEvents.START_CLIENT_TICK.register(packMapper::tick);
ArgumentTypeRegistry.registerArgumentType(Rainbow.getModdedLocation("command_suggestions"),
CommandSuggestionsArgumentType.class, SingletonArgumentInfo.contextFree(CommandSuggestionsArgumentType::new));
}
}

View File

@@ -1,4 +1,4 @@
package org.geysermc.rainbow.accessor;
package org.geysermc.rainbow.client.accessor;
import net.minecraft.client.renderer.item.ClientItem;
import net.minecraft.client.resources.model.ResolvedModel;

View File

@@ -1,4 +1,4 @@
package org.geysermc.rainbow.command;
package org.geysermc.rainbow.client.command;
import com.mojang.brigadier.StringReader;
import com.mojang.brigadier.arguments.ArgumentType;

View File

@@ -1,4 +1,4 @@
package org.geysermc.rainbow.command;
package org.geysermc.rainbow.client.command;
import com.mojang.brigadier.Command;
import com.mojang.brigadier.CommandDispatcher;
@@ -10,9 +10,9 @@ import net.minecraft.network.chat.ClickEvent;
import net.minecraft.network.chat.Component;
import net.minecraft.world.entity.player.Inventory;
import net.minecraft.world.item.ItemStack;
import org.geysermc.rainbow.PackManager;
import org.geysermc.rainbow.mapper.InventoryMapper;
import org.geysermc.rainbow.mapper.PackMapper;
import org.geysermc.rainbow.client.PackManager;
import org.geysermc.rainbow.client.mapper.InventoryMapper;
import org.geysermc.rainbow.client.mapper.PackMapper;
import org.geysermc.rainbow.pack.BedrockPack;
import java.nio.file.Path;

View File

@@ -1,4 +1,4 @@
package org.geysermc.rainbow.mapper;
package org.geysermc.rainbow.client.mapper;
import net.minecraft.client.multiplayer.ClientPacketListener;
import net.minecraft.client.player.LocalPlayer;

View File

@@ -1,4 +1,4 @@
package org.geysermc.rainbow.mapper;
package org.geysermc.rainbow.client.mapper;
import net.minecraft.client.multiplayer.ClientPacketListener;
import net.minecraft.client.player.LocalPlayer;

View File

@@ -1,4 +1,4 @@
package org.geysermc.rainbow.mapper;
package org.geysermc.rainbow.client.mapper;
import net.minecraft.client.multiplayer.ClientPacketListener;
import net.minecraft.client.player.LocalPlayer;

View File

@@ -1,14 +1,13 @@
package org.geysermc.rainbow.mapper;
package org.geysermc.rainbow.client.mapper;
import net.minecraft.client.Minecraft;
import net.minecraft.client.multiplayer.ClientPacketListener;
import net.minecraft.client.player.LocalPlayer;
import net.minecraft.network.chat.Component;
import org.geysermc.rainbow.PackManager;
import org.geysermc.rainbow.client.PackManager;
import org.geysermc.rainbow.pack.BedrockPack;
import java.util.Objects;
import java.util.Optional;
public class PackMapper {
private final PackManager packManager;

View File

@@ -1,4 +1,4 @@
package org.geysermc.rainbow.mixin;
package org.geysermc.rainbow.client.mixin;
import net.minecraft.client.renderer.entity.EntityRenderDispatcher;
import net.minecraft.client.resources.model.EquipmentAssetManager;

View File

@@ -1,4 +1,4 @@
package org.geysermc.rainbow.mixin;
package org.geysermc.rainbow.client.mixin;
import net.minecraft.client.gui.render.state.GuiItemRenderState;
import net.minecraft.client.gui.render.state.ScreenArea;

View File

@@ -1,4 +1,4 @@
package org.geysermc.rainbow.mixin;
package org.geysermc.rainbow.client.mixin;
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
@@ -9,7 +9,7 @@ import net.minecraft.client.resources.model.ModelManager;
import net.minecraft.client.resources.model.ResolvedModel;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.packs.resources.PreparableReloadListener;
import org.geysermc.rainbow.accessor.ResolvedModelAccessor;
import org.geysermc.rainbow.client.accessor.ResolvedModelAccessor;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;

View File

@@ -1,4 +1,4 @@
package org.geysermc.rainbow.mixin;
package org.geysermc.rainbow.client.mixin;
import com.mojang.blaze3d.textures.GpuTexture;
import net.minecraft.client.gui.render.pip.PictureInPictureRenderer;

View File

@@ -1,8 +1,8 @@
package org.geysermc.rainbow.mixin;
package org.geysermc.rainbow.client.mixin;
import com.mojang.blaze3d.textures.GpuTexture;
import net.minecraft.client.gui.render.pip.PictureInPictureRenderer;
import org.geysermc.rainbow.render.PictureInPictureCopyRenderer;
import org.geysermc.rainbow.client.render.PictureInPictureCopyRenderer;
import org.jetbrains.annotations.Nullable;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;

View File

@@ -1,4 +1,4 @@
package org.geysermc.rainbow.mixin;
package org.geysermc.rainbow.client.mixin;
import net.minecraft.client.gui.components.SplashRenderer;
import org.spongepowered.asm.mixin.Mixin;

View File

@@ -0,0 +1,81 @@
package org.geysermc.rainbow.client.render;
import com.mojang.blaze3d.buffers.GpuBuffer;
import com.mojang.blaze3d.platform.NativeImage;
import com.mojang.blaze3d.systems.CommandEncoder;
import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.blaze3d.textures.GpuTexture;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.navigation.ScreenRectangle;
import net.minecraft.client.gui.render.pip.OversizedItemRenderer;
import net.minecraft.client.gui.render.state.GuiItemRenderState;
import net.minecraft.client.gui.render.state.GuiRenderState;
import net.minecraft.client.gui.render.state.pip.OversizedItemRenderState;
import net.minecraft.client.renderer.item.TrackingItemStackRenderState;
import net.minecraft.world.item.ItemDisplayContext;
import net.minecraft.world.item.ItemStack;
import org.geysermc.rainbow.CodecUtil;
import org.geysermc.rainbow.mapping.geometry.GeometryRenderer;
import org.geysermc.rainbow.client.mixin.PictureInPictureRendererAccessor;
import org.joml.Matrix3x2fStack;
import java.io.IOException;
import java.nio.file.Path;
import java.util.Objects;
// TODO maybe just use this even for normal 2D items, not sure, could be useful for composite models and stuff
// TODO output in a size bedrock likes
public class MinecraftGeometryRenderer implements GeometryRenderer {
@Override
public boolean render(ItemStack stack, Path path) {
TrackingItemStackRenderState itemRenderState = new TrackingItemStackRenderState();
Minecraft.getInstance().getItemModelResolver().updateForTopItem(itemRenderState, stack, ItemDisplayContext.GUI, null, null, 0);
itemRenderState.setOversizedInGui(true);
GuiItemRenderState guiItemRenderState = new GuiItemRenderState("geometry_render", new Matrix3x2fStack(16), itemRenderState, 0, 0, null);
ScreenRectangle sizeBounds = guiItemRenderState.oversizedItemBounds();
Objects.requireNonNull(sizeBounds);
OversizedItemRenderState oversizedRenderState = new OversizedItemRenderState(guiItemRenderState, sizeBounds.left(), sizeBounds.top(), sizeBounds.right() + 4, sizeBounds.bottom() + 4);
try (OversizedItemRenderer itemRenderer = new OversizedItemRenderer(Minecraft.getInstance().renderBuffers().bufferSource())) {
//noinspection DataFlowIssue
((PictureInPictureCopyRenderer) itemRenderer).rainbow$allowTextureCopy();
itemRenderer.prepare(oversizedRenderState, new GuiRenderState(), 4);
writeAsPNG(path, ((PictureInPictureRendererAccessor) itemRenderer).getTexture());
}
return true;
}
// Simplified TextureUtil#writeAsPNG with some modifications to flip the image and just generate it at full size
private static void writeAsPNG(Path path, GpuTexture texture) {
RenderSystem.assertOnRenderThread();
int width = texture.getWidth(0);
int height = texture.getHeight(0);
int bufferSize = texture.getFormat().pixelSize() * width * height;
GpuBuffer buffer = RenderSystem.getDevice().createBuffer(() -> "Texture output buffer", GpuBuffer.USAGE_COPY_DST | GpuBuffer.USAGE_MAP_READ, bufferSize);
CommandEncoder commandEncoder = RenderSystem.getDevice().createCommandEncoder();
Runnable writer = () -> {
try (GpuBuffer.MappedView mappedView = commandEncoder.mapBuffer(buffer, true, false)) {
try (NativeImage nativeImage = new NativeImage(width, height, false)) {
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
int colour = mappedView.data().getInt((x + y * width) * texture.getFormat().pixelSize());
nativeImage.setPixelABGR(x, height - y - 1, colour);
}
}
CodecUtil.ensureDirectoryExists(path.getParent());
nativeImage.writeToFile(path);
} catch (IOException var19) {
// TODO
}
}
buffer.close();
};
commandEncoder.copyTextureToBuffer(texture, buffer, 0, writer, 0);
}
}

View File

@@ -1,4 +1,4 @@
package org.geysermc.rainbow.render;
package org.geysermc.rainbow.client.render;
public interface PictureInPictureCopyRenderer {

View File

Before

Width:  |  Height:  |  Size: 282 B

After

Width:  |  Height:  |  Size: 282 B

View File

@@ -0,0 +1,38 @@
{
"schemaVersion": 1,
"id": "rainbow-client",
"version": "${version}",
"name": "Rainbow",
"description": "Rainbow is a mod to generate Geyser item mappings and bedrock resourcepacks for use with Geyser's custom item API (v2)",
"authors": [
"GeyserMC contributors"
],
"contact": {
"homepage": "https://github.com/GeyserMC/rainbow",
"issues": "https://github.com/GeyserMC/rainbow/issues",
"sources": "https://github.com/GeyserMC/rainbow"
},
"license": "MIT",
"icon": "assets/rainbow/icon.png",
"environment": "client",
"entrypoints": {
"client": [
"org.geysermc.rainbow.client.RainbowClient"
]
},
"mixins": [
"rainbow-client.mixins.json"
],
"depends": {
"fabricloader": ">=${loader_version}",
"fabric-api": "*",
"minecraft": "${supported_versions}"
},
"custom": {
"modmenu": {
"links": {
"modmenu.discord": "https://discord.gg/GeyserMC"
}
}
}
}

View File

@@ -0,0 +1,18 @@
{
"required": true,
"minVersion": "0.8",
"package": "org.geysermc.rainbow.client.mixin",
"compatibilityLevel": "JAVA_21",
"mixins": [],
"client": [
"EntityRenderDispatcherAccessor",
"GuiItemRenderStateMixin",
"ModelManagerMixin",
"PictureInPictureRendererAccessor",
"PictureInPictureRendererMixin",
"SplashRendererAccessor"
],
"injectors": {
"defaultRequire": 1
}
}

View File

@@ -1,36 +1,15 @@
package org.geysermc.rainbow;
import com.mojang.logging.LogUtils;
import net.fabricmc.api.ClientModInitializer;
import net.fabricmc.fabric.api.client.command.v2.ClientCommandRegistrationCallback;
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents;
import net.fabricmc.fabric.api.command.v2.ArgumentTypeRegistry;
import net.minecraft.commands.synchronization.SingletonArgumentInfo;
import net.minecraft.resources.ResourceLocation;
import org.geysermc.rainbow.command.CommandSuggestionsArgumentType;
import org.geysermc.rainbow.command.PackGeneratorCommand;
import org.geysermc.rainbow.mapper.PackMapper;
import org.slf4j.Logger;
public class Rainbow implements ClientModInitializer {
public class Rainbow {
public static final String MOD_ID = "rainbow";
public static final String MOD_NAME = "Rainbow";
public static final Logger LOGGER = LogUtils.getLogger();
private final PackManager packManager = new PackManager();
private final PackMapper packMapper = new PackMapper(packManager);
// TODO export language overrides
@Override
public void onInitializeClient() {
ClientCommandRegistrationCallback.EVENT.register((dispatcher, buildContext) -> PackGeneratorCommand.register(dispatcher, packManager, packMapper));
ClientTickEvents.START_CLIENT_TICK.register(packMapper::tick);
ArgumentTypeRegistry.registerArgumentType(getModdedLocation("command_suggestions"),
CommandSuggestionsArgumentType.class, SingletonArgumentInfo.contextFree(CommandSuggestionsArgumentType::new));
}
public static ResourceLocation getModdedLocation(String path) {
return ResourceLocation.fromNamespaceAndPath(MOD_ID, path);
}

View File

@@ -0,0 +1,25 @@
package org.geysermc.rainbow.mapping;
import net.minecraft.client.renderer.item.ClientItem;
import net.minecraft.client.resources.model.EquipmentClientInfo;
import net.minecraft.client.resources.model.ResolvedModel;
import net.minecraft.core.HolderLookup;
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.equipment.EquipmentAsset;
import java.io.InputStream;
import java.util.Optional;
public interface AssetResolver {
Optional<ResolvedModel> getResolvedModel(ResourceLocation location);
Optional<ClientItem> getClientItem(ResourceLocation location);
EquipmentClientInfo getEquipmentInfo(ResourceKey<EquipmentAsset> key);
InputStream getTexture(ResourceLocation location);
HolderLookup.Provider registries();
}

View File

@@ -1,6 +1,5 @@
package org.geysermc.rainbow.mapping;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.item.BlockModelWrapper;
import net.minecraft.client.renderer.item.ClientItem;
import net.minecraft.client.renderer.item.ConditionalItemModel;
@@ -38,13 +37,11 @@ import net.minecraft.world.item.component.ItemAttributeModifiers;
import net.minecraft.world.item.equipment.trim.TrimMaterial;
import net.minecraft.world.level.Level;
import org.apache.commons.lang3.ArrayUtils;
import org.geysermc.rainbow.accessor.ResolvedModelAccessor;
import org.geysermc.rainbow.mapping.animation.AnimationMapper;
import org.geysermc.rainbow.mapping.animation.BedrockAnimationContext;
import org.geysermc.rainbow.mapping.attachable.AttachableMapper;
import org.geysermc.rainbow.mapping.geometry.BedrockGeometryContext;
import org.geysermc.rainbow.mapping.geometry.GeometryMapper;
import org.geysermc.rainbow.mapping.geometry.GeometryRenderer;
import org.geysermc.rainbow.mapping.geyser.GeyserBaseDefinition;
import org.geysermc.rainbow.mapping.geyser.GeyserItemDefinition;
import org.geysermc.rainbow.mapping.geyser.GeyserLegacyDefinition;
@@ -72,23 +69,19 @@ public class BedrockItemMapper {
.map(ResourceLocation::withDefaultNamespace)
.toList();
private static ResolvedModelAccessor getModels() {
return (ResolvedModelAccessor) Minecraft.getInstance().getModelManager();
}
private static ResourceLocation getModelId(ItemModel.Unbaked model) {
//noinspection unchecked
return ((LateBoundIdMapperAccessor<ResourceLocation, ?>) ItemModels.ID_MAPPER).getIdToValue().inverse().get(model.type());
}
public static void tryMapStack(ItemStack stack, ResourceLocation modelLocation, ProblemReporter reporter, PackContext context) {
getModels().rainbow$getClientItem(modelLocation).map(ClientItem::model)
context.assetResolver().getClientItem(modelLocation).map(ClientItem::model)
.ifPresentOrElse(model -> mapItem(model, stack, reporter.forChild(() -> "client item definition " + modelLocation + " "), base -> new GeyserSingleDefinition(base, Optional.of(modelLocation)), context),
() -> reporter.report(() -> "missing client item definition " + modelLocation));
}
public static void tryMapStack(ItemStack stack, int customModelData, ProblemReporter reporter, PackContext context) {
ItemModel.Unbaked vanillaModel = getModels().rainbow$getClientItem(stack.get(DataComponents.ITEM_MODEL)).map(ClientItem::model).orElseThrow();
ItemModel.Unbaked vanillaModel = context.assetResolver().getClientItem(stack.get(DataComponents.ITEM_MODEL)).map(ClientItem::model).orElseThrow();
ProblemReporter childReporter = reporter.forChild(() -> "item model " + vanillaModel + " with custom model data " + customModelData + " ");
if (vanillaModel instanceof RangeSelectItemModel.Unbaked(RangeSelectItemModelProperty property, float scale, List<RangeSelectItemModel.Entry> entries, Optional<ItemModel.Unbaked> fallback)) {
// WHY, Mojang?
@@ -130,7 +123,7 @@ public class BedrockItemMapper {
private static void mapBlockModelWrapper(BlockModelWrapper.Unbaked model, MappingContext context) {
ResourceLocation itemModelLocation = model.model();
getModels().rainbow$getResolvedModel(itemModelLocation)
context.packContext().assetResolver().getResolvedModel(itemModelLocation)
.ifPresentOrElse(itemModel -> {
ResolvedModel parentModel = itemModel.parent();
// debugName() returns the resource location of the model as a string
@@ -290,13 +283,15 @@ public class BedrockItemMapper {
boolean exportTexture = true;
if (customModel.isPresent()) {
texture = texture.withPath(path -> path + "_icon");
GeometryRenderer.render(stack, packContext.packPath().resolve(BedrockTextures.TEXTURES_FOLDER + texture.getPath() + ".png"));
exportTexture = false;
// FIXME Bit of a hack, preferably render geometry at a later stage
exportTexture = !packContext.geometryRenderer().render(stack, packContext.packPath().resolve(BedrockTextures.TEXTURES_FOLDER + texture.getPath() + ".png"));
packContext.additionalTextureConsumer().accept(geometryTexture);
}
packContext.itemConsumer().accept(new BedrockItem(bedrockIdentifier, base.textureName(), texture, exportTexture,
AttachableMapper.mapItem(stack.getComponentsPatch(), bedrockIdentifier, bedrockGeometry, bedrockAnimation, packContext.additionalTextureConsumer()),
AttachableMapper.mapItem(stack.getComponentsPatch(), bedrockIdentifier, bedrockGeometry, bedrockAnimation,
packContext.assetResolver(),
packContext.additionalTextureConsumer()),
bedrockGeometry.map(BedrockGeometryContext::geometry), bedrockAnimation.map(BedrockAnimationContext::animation)));
}

View File

@@ -1,10 +1,12 @@
package org.geysermc.rainbow.mapping;
import net.minecraft.resources.ResourceLocation;
import org.geysermc.rainbow.mapping.geometry.GeometryRenderer;
import org.geysermc.rainbow.mapping.geyser.GeyserMappings;
import java.nio.file.Path;
import java.util.function.Consumer;
public record PackContext(GeyserMappings mappings, Path packPath, BedrockItemConsumer itemConsumer, Consumer<ResourceLocation> additionalTextureConsumer) {
public record PackContext(GeyserMappings mappings, Path packPath, BedrockItemConsumer itemConsumer, AssetResolver assetResolver,
GeometryRenderer geometryRenderer, Consumer<ResourceLocation> additionalTextureConsumer) {
}

View File

@@ -1,7 +1,6 @@
package org.geysermc.rainbow.mapping.attachable;
import com.mojang.datafixers.util.Pair;
import net.minecraft.client.Minecraft;
import net.minecraft.client.resources.model.EquipmentAssetManager;
import net.minecraft.client.resources.model.EquipmentClientInfo;
import net.minecraft.core.component.DataComponentPatch;
@@ -9,9 +8,9 @@ import net.minecraft.core.component.DataComponents;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.entity.EquipmentSlot;
import net.minecraft.world.item.equipment.Equippable;
import org.geysermc.rainbow.mapping.AssetResolver;
import org.geysermc.rainbow.mapping.animation.BedrockAnimationContext;
import org.geysermc.rainbow.mapping.geometry.BedrockGeometryContext;
import org.geysermc.rainbow.mixin.EntityRenderDispatcherAccessor;
import org.geysermc.rainbow.pack.attachable.BedrockAttachable;
import java.util.List;
@@ -21,17 +20,14 @@ import java.util.function.Consumer;
public class AttachableMapper {
public static Optional<BedrockAttachable> mapItem(DataComponentPatch components, ResourceLocation bedrockIdentifier, Optional<BedrockGeometryContext> customGeometry,
Optional<BedrockAnimationContext> customAnimation, Consumer<ResourceLocation> textureConsumer) {
Optional<BedrockAnimationContext> customAnimation, AssetResolver assetResolver, Consumer<ResourceLocation> textureConsumer) {
// Crazy optional statement
// Unfortunately we can't have both equippables and custom models, so we prefer the latter :(
return customGeometry
.map(geometry -> BedrockAttachable.geometry(bedrockIdentifier, geometry.geometry().definitions().getFirst(), geometry.texture().getPath()))
.or(() -> Optional.ofNullable(components.get(DataComponents.EQUIPPABLE))
.flatMap(optional -> (Optional<Equippable>) optional)
.flatMap(equippable -> {
EquipmentAssetManager equipmentAssets = ((EntityRenderDispatcherAccessor) Minecraft.getInstance().getEntityRenderDispatcher()).getEquipmentAssets();
return equippable.assetId().map(asset -> Pair.of(equippable.slot(), equipmentAssets.get(asset)));
})
.flatMap(equippable -> equippable.assetId().map(asset -> Pair.of(equippable.slot(), assetResolver.getEquipmentInfo(asset))))
.filter(assetInfo -> assetInfo.getSecond() != EquipmentAssetManager.MISSING)
.map(assetInfo -> assetInfo
.mapSecond(info -> info.getLayers(getLayer(assetInfo.getFirst()))))

View File

@@ -1,79 +1,10 @@
package org.geysermc.rainbow.mapping.geometry;
import com.mojang.blaze3d.buffers.GpuBuffer;
import com.mojang.blaze3d.platform.NativeImage;
import com.mojang.blaze3d.systems.CommandEncoder;
import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.blaze3d.textures.GpuTexture;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.navigation.ScreenRectangle;
import net.minecraft.client.gui.render.pip.OversizedItemRenderer;
import net.minecraft.client.gui.render.state.GuiItemRenderState;
import net.minecraft.client.gui.render.state.GuiRenderState;
import net.minecraft.client.gui.render.state.pip.OversizedItemRenderState;
import net.minecraft.client.renderer.item.TrackingItemStackRenderState;
import net.minecraft.world.item.ItemDisplayContext;
import net.minecraft.world.item.ItemStack;
import org.geysermc.rainbow.CodecUtil;
import org.geysermc.rainbow.mixin.PictureInPictureRendererAccessor;
import org.geysermc.rainbow.render.PictureInPictureCopyRenderer;
import org.joml.Matrix3x2fStack;
import java.io.IOException;
import java.nio.file.Path;
import java.util.Objects;
// TODO maybe just use this even for normal 2D items, not sure, could be useful for composite models and stuff
// TODO output in a size bedrock likes
public class GeometryRenderer {
public interface GeometryRenderer {
public static void render(ItemStack stack, Path path) {
TrackingItemStackRenderState itemRenderState = new TrackingItemStackRenderState();
Minecraft.getInstance().getItemModelResolver().updateForTopItem(itemRenderState, stack, ItemDisplayContext.GUI, null, null, 0);
itemRenderState.setOversizedInGui(true);
GuiItemRenderState guiItemRenderState = new GuiItemRenderState("geometry_render", new Matrix3x2fStack(16), itemRenderState, 0, 0, null);
ScreenRectangle sizeBounds = guiItemRenderState.oversizedItemBounds();
Objects.requireNonNull(sizeBounds);
OversizedItemRenderState oversizedRenderState = new OversizedItemRenderState(guiItemRenderState, sizeBounds.left(), sizeBounds.top(), sizeBounds.right() + 4, sizeBounds.bottom() + 4);
try (OversizedItemRenderer itemRenderer = new OversizedItemRenderer(Minecraft.getInstance().renderBuffers().bufferSource())) {
//noinspection DataFlowIssue
((PictureInPictureCopyRenderer) itemRenderer).rainbow$allowTextureCopy();
itemRenderer.prepare(oversizedRenderState, new GuiRenderState(), 4);
writeAsPNG(path, ((PictureInPictureRendererAccessor) itemRenderer).getTexture());
}
}
// Simplified TextureUtil#writeAsPNG with some modifications to flip the image and just generate it at full size
private static void writeAsPNG(Path path, GpuTexture texture) {
RenderSystem.assertOnRenderThread();
int width = texture.getWidth(0);
int height = texture.getHeight(0);
int bufferSize = texture.getFormat().pixelSize() * width * height;
GpuBuffer buffer = RenderSystem.getDevice().createBuffer(() -> "Texture output buffer", GpuBuffer.USAGE_COPY_DST | GpuBuffer.USAGE_MAP_READ, bufferSize);
CommandEncoder commandEncoder = RenderSystem.getDevice().createCommandEncoder();
Runnable writer = () -> {
try (GpuBuffer.MappedView mappedView = commandEncoder.mapBuffer(buffer, true, false)) {
try (NativeImage nativeImage = new NativeImage(width, height, false)) {
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
int colour = mappedView.data().getInt((x + y * width) * texture.getFormat().pixelSize());
nativeImage.setPixelABGR(x, height - y - 1, colour);
}
}
CodecUtil.ensureDirectoryExists(path.getParent());
nativeImage.writeToFile(path);
} catch (IOException var19) {
// TODO
}
}
buffer.close();
};
commandEncoder.copyTextureToBuffer(texture, buffer, 0, writer, 0);
}
boolean render(ItemStack stack, Path path);
}

View File

@@ -0,0 +1,14 @@
package org.geysermc.rainbow.mapping.geometry;
import net.minecraft.world.item.ItemStack;
import java.nio.file.Path;
public class NoopGeometryRenderer implements GeometryRenderer {
public static final NoopGeometryRenderer INSTANCE = new NoopGeometryRenderer();
@Override
public boolean render(ItemStack stack, Path path) {
return false;
}
}

View File

@@ -4,8 +4,6 @@ import com.mojang.serialization.JsonOps;
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
import it.unimi.dsi.fastutil.ints.IntSet;
import net.fabricmc.loader.api.FabricLoader;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.components.SplashRenderer;
import net.minecraft.core.component.DataComponents;
import net.minecraft.resources.RegistryOps;
import net.minecraft.resources.ResourceLocation;
@@ -18,10 +16,11 @@ import org.apache.commons.io.IOUtils;
import org.geysermc.rainbow.CodecUtil;
import org.geysermc.rainbow.PackConstants;
import org.geysermc.rainbow.Rainbow;
import org.geysermc.rainbow.mapping.AssetResolver;
import org.geysermc.rainbow.mapping.BedrockItemMapper;
import org.geysermc.rainbow.mapping.PackContext;
import org.geysermc.rainbow.mapping.geometry.NoopGeometryRenderer;
import org.geysermc.rainbow.mapping.geyser.GeyserMappings;
import org.geysermc.rainbow.mixin.SplashRendererAccessor;
import org.jetbrains.annotations.NotNull;
import java.io.FileOutputStream;
@@ -74,7 +73,9 @@ public class BedrockPack {
private final ProblemReporter.Collector reporter;
public BedrockPack(String name) throws IOException {
private final PackContext context;
public BedrockPack(String name, AssetResolver assetResolver) throws IOException {
this.name = name;
// Not reading existing item mappings/texture atlas for now since that doesn't work all that well yet
@@ -88,6 +89,14 @@ public class BedrockPack {
itemTextures = BedrockTextures.builder();
reporter = new ProblemReporter.Collector(() -> "Bedrock pack " + name + " ");
context = new PackContext(mappings, packPath, item -> {
itemTextures.withItemTexture(item);
if (item.exportTexture()) {
texturesToExport.add(item.texture());
}
bedrockItems.add(item);
}, assetResolver, NoopGeometryRenderer.INSTANCE, texturesToExport::add);
}
public String name() {
@@ -113,13 +122,6 @@ public class BedrockPack {
reporter.report(problem);
}
};
PackContext context = new PackContext(mappings, packPath, item -> {
itemTextures.withItemTexture(item);
if (item.exportTexture()) {
texturesToExport.add(item.texture());
}
bedrockItems.add(item);
}, texturesToExport::add);
Optional<? extends ResourceLocation> patchedModel = stack.getComponentsPatch().get(DataComponents.ITEM_MODEL);
//noinspection OptionalAssignedToNull - annoying Mojang
@@ -148,7 +150,7 @@ public class BedrockPack {
boolean success = true;
try {
CodecUtil.trySaveJson(GeyserMappings.CODEC, mappings, exportPath.resolve(MAPPINGS_FILE), RegistryOps.create(JsonOps.INSTANCE, Minecraft.getInstance().level.registryAccess()));
CodecUtil.trySaveJson(GeyserMappings.CODEC, mappings, exportPath.resolve(MAPPINGS_FILE), RegistryOps.create(JsonOps.INSTANCE, context.assetResolver().registries()));
CodecUtil.trySaveJson(PackManifest.CODEC, manifest, packPath.resolve(MANIFEST_FILE));
CodecUtil.trySaveJson(BedrockTextureAtlas.CODEC, BedrockTextureAtlas.itemAtlas(name, itemTextures), packPath.resolve(ITEM_ATLAS_FILE));
} catch (IOException | NullPointerException exception) {
@@ -165,8 +167,8 @@ public class BedrockPack {
}
for (ResourceLocation texture : texturesToExport) {
texture = texture.withPath(path -> "textures/" + path + ".png");
try (InputStream inputTexture = Minecraft.getInstance().getResourceManager().open(texture)) {
texture = texture.withPath(path -> "textures/" + path + ".png"); // FIXME
try (InputStream inputTexture = context.assetResolver().getTexture(texture)) {
Path texturePath = packPath.resolve(texture.getPath());
CodecUtil.ensureDirectoryExists(texturePath.getParent());
try (OutputStream outputTexture = new FileOutputStream(texturePath.toFile())) {
@@ -227,11 +229,11 @@ Textures tried to export: %d
private static String randomSummaryComment() {
if (RANDOM.nextDouble() < 0.6) {
SplashRenderer splash = Minecraft.getInstance().getSplashManager().getSplash();
/*SplashRenderer splash = Minecraft.getInstance().getSplashManager().getSplash();
if (splash == null) {
return "Undefined Undefined :(";
}
return ((SplashRendererAccessor) splash).getSplash();
return ((SplashRendererAccessor) splash).getSplash();*/ // TODO
}
return randomBuiltinSummaryComment();
}

View File

@@ -5,14 +5,8 @@
"compatibilityLevel": "JAVA_21",
"mixins": [],
"client": [
"EntityRenderDispatcherAccessor",
"GuiItemRenderStateMixin",
"LateBoundIdMapperAccessor",
"ModelManagerMixin",
"PictureInPictureRendererAccessor",
"PictureInPictureRendererMixin",
"RangeSelectItemModelAccessor",
"SplashRendererAccessor",
"TextureSlotsAccessor"
],
"injectors": {