From 535d40c46829a6a44335e0fef1bad1f9f2f5534c Mon Sep 17 00:00:00 2001 From: Eclipse Date: Thu, 16 Oct 2025 08:35:57 +0000 Subject: [PATCH] Work on re-introducing 3D icon generation --- .../render/MinecraftGeometryRenderer.java | 30 +++++++------------ .../rainbow/mapping/BedrockItemMapper.java | 2 +- .../geysermc/rainbow/mapping/PackContext.java | 4 ++- .../geometry/BedrockGeometryContext.java | 5 ++-- .../mapping/geometry/GeometryRenderer.java | 5 ++-- .../geometry/NoopGeometryRenderer.java | 14 --------- .../geysermc/rainbow/pack/BedrockItem.java | 28 +++++++++-------- .../geysermc/rainbow/pack/BedrockPack.java | 8 ++--- 8 files changed, 39 insertions(+), 57 deletions(-) delete mode 100644 rainbow/src/main/java/org/geysermc/rainbow/mapping/geometry/NoopGeometryRenderer.java diff --git a/client/src/main/java/org/geysermc/rainbow/client/render/MinecraftGeometryRenderer.java b/client/src/main/java/org/geysermc/rainbow/client/render/MinecraftGeometryRenderer.java index d7e8f19..b86ba75 100644 --- a/client/src/main/java/org/geysermc/rainbow/client/render/MinecraftGeometryRenderer.java +++ b/client/src/main/java/org/geysermc/rainbow/client/render/MinecraftGeometryRenderer.java @@ -14,13 +14,10 @@ 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 @@ -29,7 +26,7 @@ public class MinecraftGeometryRenderer implements GeometryRenderer { public static final MinecraftGeometryRenderer INSTANCE = new MinecraftGeometryRenderer(); @Override - public boolean render(ItemStack stack, Path path) { + public NativeImage render(ItemStack stack) { TrackingItemStackRenderState itemRenderState = new TrackingItemStackRenderState(); Minecraft.getInstance().getItemModelResolver().updateForTopItem(itemRenderState, stack, ItemDisplayContext.GUI, null, null, 0); itemRenderState.setOversizedInGui(true); @@ -43,13 +40,12 @@ public class MinecraftGeometryRenderer implements GeometryRenderer { //noinspection DataFlowIssue ((PictureInPictureCopyRenderer) itemRenderer).rainbow$allowTextureCopy(); itemRenderer.prepare(oversizedRenderState, new GuiRenderState(), 4); - writeAsPNG(path, ((PictureInPictureRendererAccessor) itemRenderer).getTexture()); + return writeToImage(((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) { + // Simplified TextureUtil#writeAsPNG with some modifications to just write to a NativeImage, flip the image and just generate it at full size + private static NativeImage writeToImage(GpuTexture texture) { RenderSystem.assertOnRenderThread(); int width = texture.getWidth(0); int height = texture.getHeight(0); @@ -58,25 +54,21 @@ public class MinecraftGeometryRenderer implements GeometryRenderer { GpuBuffer buffer = RenderSystem.getDevice().createBuffer(() -> "Texture output buffer", GpuBuffer.USAGE_COPY_DST | GpuBuffer.USAGE_MAP_READ, bufferSize); CommandEncoder commandEncoder = RenderSystem.getDevice().createCommandEncoder(); + NativeImage image = new NativeImage(width, height, false); 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); - } + 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()); + image.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); + + return image; } } diff --git a/rainbow/src/main/java/org/geysermc/rainbow/mapping/BedrockItemMapper.java b/rainbow/src/main/java/org/geysermc/rainbow/mapping/BedrockItemMapper.java index 9690619..86ddd9a 100644 --- a/rainbow/src/main/java/org/geysermc/rainbow/mapping/BedrockItemMapper.java +++ b/rainbow/src/main/java/org/geysermc/rainbow/mapping/BedrockItemMapper.java @@ -123,7 +123,7 @@ public class BedrockItemMapper { bedrockIdentifier = itemModelLocation; } - BedrockGeometryContext geometry = BedrockGeometryContext.create(bedrockIdentifier, itemModel, context.packContext); + BedrockGeometryContext geometry = BedrockGeometryContext.create(bedrockIdentifier, context.stack, itemModel, context.packContext); if (context.packContext.reportSuccesses()) { // Not a problem, but just report to get the model printed in the report file context.report("creating mapping for block model " + itemModelLocation); diff --git a/rainbow/src/main/java/org/geysermc/rainbow/mapping/PackContext.java b/rainbow/src/main/java/org/geysermc/rainbow/mapping/PackContext.java index d632638..96ad637 100644 --- a/rainbow/src/main/java/org/geysermc/rainbow/mapping/PackContext.java +++ b/rainbow/src/main/java/org/geysermc/rainbow/mapping/PackContext.java @@ -4,5 +4,7 @@ import org.geysermc.rainbow.mapping.geometry.GeometryRenderer; import org.geysermc.rainbow.definition.GeyserMappings; import org.geysermc.rainbow.pack.PackPaths; +import java.util.Optional; + public record PackContext(GeyserMappings mappings, PackPaths paths, BedrockItemConsumer itemConsumer, AssetResolver assetResolver, - GeometryRenderer geometryRenderer, boolean reportSuccesses) {} + Optional geometryRenderer, boolean reportSuccesses) {} diff --git a/rainbow/src/main/java/org/geysermc/rainbow/mapping/geometry/BedrockGeometryContext.java b/rainbow/src/main/java/org/geysermc/rainbow/mapping/geometry/BedrockGeometryContext.java index 56891f6..63aa980 100644 --- a/rainbow/src/main/java/org/geysermc/rainbow/mapping/geometry/BedrockGeometryContext.java +++ b/rainbow/src/main/java/org/geysermc/rainbow/mapping/geometry/BedrockGeometryContext.java @@ -5,6 +5,7 @@ import net.minecraft.client.renderer.block.model.TextureSlots; import net.minecraft.client.resources.model.Material; import net.minecraft.client.resources.model.ResolvedModel; import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.ItemStack; import org.geysermc.rainbow.Rainbow; import org.geysermc.rainbow.mapping.PackContext; import org.geysermc.rainbow.mapping.animation.AnimationMapper; @@ -23,7 +24,7 @@ public record BedrockGeometryContext(Optional> geomet .map(ResourceLocation::withDefaultNamespace) .toList(); - public static BedrockGeometryContext create(ResourceLocation bedrockIdentifier, ResolvedModel model, PackContext context) { + public static BedrockGeometryContext create(ResourceLocation bedrockIdentifier, ItemStack stackToRender, ResolvedModel model, PackContext context) { ResolvedModel parentModel = model.parent(); // debugName() returns the resource location of the model as a string boolean handheld = parentModel != null && HANDHELD_MODELS.contains(ResourceLocation.parse(parentModel.debugName())); @@ -52,7 +53,7 @@ public record BedrockGeometryContext(Optional> geomet })); animation = Optional.of(AnimationMapper.mapAnimation(safeIdentifier, "bone", model.getTopTransforms())); - icon = new TextureHolder(modelLocation); // TODO + icon = new TextureHolder(modelLocation, context.geometryRenderer().map(renderer -> () -> renderer.render(stackToRender))); } return new BedrockGeometryContext(geometry, animation, icon, handheld); diff --git a/rainbow/src/main/java/org/geysermc/rainbow/mapping/geometry/GeometryRenderer.java b/rainbow/src/main/java/org/geysermc/rainbow/mapping/geometry/GeometryRenderer.java index 37799d7..4607641 100644 --- a/rainbow/src/main/java/org/geysermc/rainbow/mapping/geometry/GeometryRenderer.java +++ b/rainbow/src/main/java/org/geysermc/rainbow/mapping/geometry/GeometryRenderer.java @@ -1,10 +1,9 @@ package org.geysermc.rainbow.mapping.geometry; +import com.mojang.blaze3d.platform.NativeImage; import net.minecraft.world.item.ItemStack; -import java.nio.file.Path; - public interface GeometryRenderer { - boolean render(ItemStack stack, Path path); + NativeImage render(ItemStack stack); } diff --git a/rainbow/src/main/java/org/geysermc/rainbow/mapping/geometry/NoopGeometryRenderer.java b/rainbow/src/main/java/org/geysermc/rainbow/mapping/geometry/NoopGeometryRenderer.java deleted file mode 100644 index 9836042..0000000 --- a/rainbow/src/main/java/org/geysermc/rainbow/mapping/geometry/NoopGeometryRenderer.java +++ /dev/null @@ -1,14 +0,0 @@ -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; - } -} diff --git a/rainbow/src/main/java/org/geysermc/rainbow/pack/BedrockItem.java b/rainbow/src/main/java/org/geysermc/rainbow/pack/BedrockItem.java index 847b9e0..237ea63 100644 --- a/rainbow/src/main/java/org/geysermc/rainbow/pack/BedrockItem.java +++ b/rainbow/src/main/java/org/geysermc/rainbow/pack/BedrockItem.java @@ -20,19 +20,21 @@ public record BedrockItem(ResourceLocation identifier, String textureName, Bedro public CompletableFuture save(PackSerializer serializer, Path attachableDirectory, Path geometryDirectory, Path animationDirectory, Function> textureSaver) { - return CompletableFuture.supplyAsync(() -> geometryContext.geometry().map(Supplier::get)) - .thenCompose(stitchedGeometry -> { - List attachableTextures = new ArrayList<>(); - Optional createdAttachable = attachableCreator.create(identifier, stitchedGeometry, attachableTextures::add); - return CompletableFuture.allOf( - textureSaver.apply(geometryContext.icon()), - createdAttachable.map(attachable -> attachable.save(serializer, attachableDirectory)).orElse(noop()), - CompletableFuture.allOf(attachableTextures.stream().map(textureSaver).toArray(CompletableFuture[]::new)), - stitchedGeometry.map(BedrockGeometryContext.StitchedGeometry::geometry).map(geometry -> geometry.save(serializer, geometryDirectory)).orElse(noop()), - stitchedGeometry.map(BedrockGeometryContext.StitchedGeometry::stitchedTextures).map(textureSaver).orElse(noop()), - geometryContext.animation().map(context -> context.animation().save(serializer, animationDirectory, Rainbow.fileSafeResourceLocation(identifier))).orElse(noop()) - ); - }); + return CompletableFuture.allOf( + textureSaver.apply(geometryContext.icon()), + CompletableFuture.supplyAsync(() -> geometryContext.geometry().map(Supplier::get)) + .thenCompose(stitchedGeometry -> { + List attachableTextures = new ArrayList<>(); + Optional createdAttachable = attachableCreator.create(identifier, stitchedGeometry, attachableTextures::add); + return CompletableFuture.allOf( + createdAttachable.map(attachable -> attachable.save(serializer, attachableDirectory)).orElse(noop()), + CompletableFuture.allOf(attachableTextures.stream().map(textureSaver).toArray(CompletableFuture[]::new)), + stitchedGeometry.map(BedrockGeometryContext.StitchedGeometry::geometry).map(geometry -> geometry.save(serializer, geometryDirectory)).orElse(noop()), + stitchedGeometry.map(BedrockGeometryContext.StitchedGeometry::stitchedTextures).map(textureSaver).orElse(noop()), + geometryContext.animation().map(context -> context.animation().save(serializer, animationDirectory, Rainbow.fileSafeResourceLocation(identifier))).orElse(noop()) + ); + }) + ); } private static CompletableFuture noop() { diff --git a/rainbow/src/main/java/org/geysermc/rainbow/pack/BedrockPack.java b/rainbow/src/main/java/org/geysermc/rainbow/pack/BedrockPack.java index a48b39d..182855b 100644 --- a/rainbow/src/main/java/org/geysermc/rainbow/pack/BedrockPack.java +++ b/rainbow/src/main/java/org/geysermc/rainbow/pack/BedrockPack.java @@ -19,7 +19,6 @@ import org.geysermc.rainbow.mapping.BedrockItemMapper; import org.geysermc.rainbow.mapping.PackContext; import org.geysermc.rainbow.mapping.PackSerializer; import org.geysermc.rainbow.mapping.geometry.GeometryRenderer; -import org.geysermc.rainbow.mapping.geometry.NoopGeometryRenderer; import org.geysermc.rainbow.definition.GeyserMappings; import org.geysermc.rainbow.mapping.geometry.TextureHolder; import org.jetbrains.annotations.NotNull; @@ -36,6 +35,7 @@ import java.util.UUID; import java.util.concurrent.CompletableFuture; import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.Function; +import java.util.function.Supplier; import java.util.function.UnaryOperator; public class BedrockPack { @@ -53,7 +53,7 @@ public class BedrockPack { private final ProblemReporter.Collector reporter; public BedrockPack(String name, PackManifest manifest, PackPaths paths, PackSerializer serializer, AssetResolver assetResolver, - GeometryRenderer geometryRenderer, ProblemReporter.Collector reporter, + Optional geometryRenderer, ProblemReporter.Collector reporter, boolean reportSuccesses) { this.name = name; this.manifest = manifest; @@ -206,7 +206,7 @@ public class BedrockPack { private UnaryOperator manifestPath = resolve(MANIFEST_FILE); private UnaryOperator itemAtlasPath = resolve(ITEM_ATLAS_FILE); private Path packZipFile = null; - private GeometryRenderer geometryRenderer = NoopGeometryRenderer.INSTANCE; + private GeometryRenderer geometryRenderer = null; private ProblemReporter.Collector reporter; private boolean reportSuccesses = false; @@ -294,7 +294,7 @@ public class BedrockPack { PackPaths paths = new PackPaths(mappingsPath, packRootPath, attachablesPath.apply(packRootPath), geometryPath.apply(packRootPath), animationPath.apply(packRootPath), manifestPath.apply(packRootPath), itemAtlasPath.apply(packRootPath), Optional.ofNullable(packZipFile)); - return new BedrockPack(name, manifest, paths, packSerializer, assetResolver, geometryRenderer, reporter, reportSuccesses); + return new BedrockPack(name, manifest, paths, packSerializer, assetResolver, Optional.ofNullable(geometryRenderer), reporter, reportSuccesses); } private static UnaryOperator resolve(Path child) {