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

Work on re-introducing 3D icon generation

This commit is contained in:
Eclipse
2025-10-16 08:35:57 +00:00
parent 7f67a40c13
commit 535d40c468
8 changed files with 39 additions and 57 deletions

View File

@@ -14,13 +14,10 @@ import net.minecraft.client.gui.render.state.pip.OversizedItemRenderState;
import net.minecraft.client.renderer.item.TrackingItemStackRenderState; import net.minecraft.client.renderer.item.TrackingItemStackRenderState;
import net.minecraft.world.item.ItemDisplayContext; import net.minecraft.world.item.ItemDisplayContext;
import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.ItemStack;
import org.geysermc.rainbow.CodecUtil;
import org.geysermc.rainbow.mapping.geometry.GeometryRenderer; import org.geysermc.rainbow.mapping.geometry.GeometryRenderer;
import org.geysermc.rainbow.client.mixin.PictureInPictureRendererAccessor; import org.geysermc.rainbow.client.mixin.PictureInPictureRendererAccessor;
import org.joml.Matrix3x2fStack; import org.joml.Matrix3x2fStack;
import java.io.IOException;
import java.nio.file.Path;
import java.util.Objects; 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 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(); public static final MinecraftGeometryRenderer INSTANCE = new MinecraftGeometryRenderer();
@Override @Override
public boolean render(ItemStack stack, Path path) { public NativeImage render(ItemStack stack) {
TrackingItemStackRenderState itemRenderState = new TrackingItemStackRenderState(); TrackingItemStackRenderState itemRenderState = new TrackingItemStackRenderState();
Minecraft.getInstance().getItemModelResolver().updateForTopItem(itemRenderState, stack, ItemDisplayContext.GUI, null, null, 0); Minecraft.getInstance().getItemModelResolver().updateForTopItem(itemRenderState, stack, ItemDisplayContext.GUI, null, null, 0);
itemRenderState.setOversizedInGui(true); itemRenderState.setOversizedInGui(true);
@@ -43,13 +40,12 @@ public class MinecraftGeometryRenderer implements GeometryRenderer {
//noinspection DataFlowIssue //noinspection DataFlowIssue
((PictureInPictureCopyRenderer) itemRenderer).rainbow$allowTextureCopy(); ((PictureInPictureCopyRenderer) itemRenderer).rainbow$allowTextureCopy();
itemRenderer.prepare(oversizedRenderState, new GuiRenderState(), 4); 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 // Simplified TextureUtil#writeAsPNG with some modifications to just write to a NativeImage, flip the image and just generate it at full size
private static void writeAsPNG(Path path, GpuTexture texture) { private static NativeImage writeToImage(GpuTexture texture) {
RenderSystem.assertOnRenderThread(); RenderSystem.assertOnRenderThread();
int width = texture.getWidth(0); int width = texture.getWidth(0);
int height = texture.getHeight(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); GpuBuffer buffer = RenderSystem.getDevice().createBuffer(() -> "Texture output buffer", GpuBuffer.USAGE_COPY_DST | GpuBuffer.USAGE_MAP_READ, bufferSize);
CommandEncoder commandEncoder = RenderSystem.getDevice().createCommandEncoder(); CommandEncoder commandEncoder = RenderSystem.getDevice().createCommandEncoder();
NativeImage image = new NativeImage(width, height, false);
Runnable writer = () -> { Runnable writer = () -> {
try (GpuBuffer.MappedView mappedView = commandEncoder.mapBuffer(buffer, true, false)) { 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 y = 0; y < height; y++) { for (int x = 0; x < width; x++) {
for (int x = 0; x < width; x++) { int colour = mappedView.data().getInt((x + y * width) * texture.getFormat().pixelSize());
int colour = mappedView.data().getInt((x + y * width) * texture.getFormat().pixelSize()); image.setPixelABGR(x, height - y - 1, colour);
nativeImage.setPixelABGR(x, height - y - 1, colour);
}
} }
CodecUtil.ensureDirectoryExists(path.getParent());
nativeImage.writeToFile(path);
} catch (IOException var19) {
// TODO
} }
} }
buffer.close(); buffer.close();
}; };
commandEncoder.copyTextureToBuffer(texture, buffer, 0, writer, 0); commandEncoder.copyTextureToBuffer(texture, buffer, 0, writer, 0);
return image;
} }
} }

View File

@@ -123,7 +123,7 @@ public class BedrockItemMapper {
bedrockIdentifier = itemModelLocation; bedrockIdentifier = itemModelLocation;
} }
BedrockGeometryContext geometry = BedrockGeometryContext.create(bedrockIdentifier, itemModel, context.packContext); BedrockGeometryContext geometry = BedrockGeometryContext.create(bedrockIdentifier, context.stack, itemModel, context.packContext);
if (context.packContext.reportSuccesses()) { if (context.packContext.reportSuccesses()) {
// Not a problem, but just report to get the model printed in the report file // Not a problem, but just report to get the model printed in the report file
context.report("creating mapping for block model " + itemModelLocation); context.report("creating mapping for block model " + itemModelLocation);

View File

@@ -4,5 +4,7 @@ import org.geysermc.rainbow.mapping.geometry.GeometryRenderer;
import org.geysermc.rainbow.definition.GeyserMappings; import org.geysermc.rainbow.definition.GeyserMappings;
import org.geysermc.rainbow.pack.PackPaths; import org.geysermc.rainbow.pack.PackPaths;
import java.util.Optional;
public record PackContext(GeyserMappings mappings, PackPaths paths, BedrockItemConsumer itemConsumer, AssetResolver assetResolver, public record PackContext(GeyserMappings mappings, PackPaths paths, BedrockItemConsumer itemConsumer, AssetResolver assetResolver,
GeometryRenderer geometryRenderer, boolean reportSuccesses) {} Optional<GeometryRenderer> geometryRenderer, boolean reportSuccesses) {}

View File

@@ -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.Material;
import net.minecraft.client.resources.model.ResolvedModel; import net.minecraft.client.resources.model.ResolvedModel;
import net.minecraft.resources.ResourceLocation; import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.ItemStack;
import org.geysermc.rainbow.Rainbow; import org.geysermc.rainbow.Rainbow;
import org.geysermc.rainbow.mapping.PackContext; import org.geysermc.rainbow.mapping.PackContext;
import org.geysermc.rainbow.mapping.animation.AnimationMapper; import org.geysermc.rainbow.mapping.animation.AnimationMapper;
@@ -23,7 +24,7 @@ public record BedrockGeometryContext(Optional<Supplier<StitchedGeometry>> geomet
.map(ResourceLocation::withDefaultNamespace) .map(ResourceLocation::withDefaultNamespace)
.toList(); .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(); ResolvedModel parentModel = model.parent();
// debugName() returns the resource location of the model as a string // debugName() returns the resource location of the model as a string
boolean handheld = parentModel != null && HANDHELD_MODELS.contains(ResourceLocation.parse(parentModel.debugName())); boolean handheld = parentModel != null && HANDHELD_MODELS.contains(ResourceLocation.parse(parentModel.debugName()));
@@ -52,7 +53,7 @@ public record BedrockGeometryContext(Optional<Supplier<StitchedGeometry>> geomet
})); }));
animation = Optional.of(AnimationMapper.mapAnimation(safeIdentifier, "bone", model.getTopTransforms())); 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); return new BedrockGeometryContext(geometry, animation, icon, handheld);

View File

@@ -1,10 +1,9 @@
package org.geysermc.rainbow.mapping.geometry; package org.geysermc.rainbow.mapping.geometry;
import com.mojang.blaze3d.platform.NativeImage;
import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.ItemStack;
import java.nio.file.Path;
public interface GeometryRenderer { public interface GeometryRenderer {
boolean render(ItemStack stack, Path path); NativeImage render(ItemStack stack);
} }

View File

@@ -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;
}
}

View File

@@ -20,19 +20,21 @@ public record BedrockItem(ResourceLocation identifier, String textureName, Bedro
public CompletableFuture<?> save(PackSerializer serializer, Path attachableDirectory, Path geometryDirectory, Path animationDirectory, public CompletableFuture<?> save(PackSerializer serializer, Path attachableDirectory, Path geometryDirectory, Path animationDirectory,
Function<TextureHolder, CompletableFuture<?>> textureSaver) { Function<TextureHolder, CompletableFuture<?>> textureSaver) {
return CompletableFuture.supplyAsync(() -> geometryContext.geometry().map(Supplier::get)) return CompletableFuture.allOf(
.thenCompose(stitchedGeometry -> { textureSaver.apply(geometryContext.icon()),
List<TextureHolder> attachableTextures = new ArrayList<>(); CompletableFuture.supplyAsync(() -> geometryContext.geometry().map(Supplier::get))
Optional<BedrockAttachable> createdAttachable = attachableCreator.create(identifier, stitchedGeometry, attachableTextures::add); .thenCompose(stitchedGeometry -> {
return CompletableFuture.allOf( List<TextureHolder> attachableTextures = new ArrayList<>();
textureSaver.apply(geometryContext.icon()), Optional<BedrockAttachable> createdAttachable = attachableCreator.create(identifier, stitchedGeometry, attachableTextures::add);
createdAttachable.map(attachable -> attachable.save(serializer, attachableDirectory)).orElse(noop()), return CompletableFuture.allOf(
CompletableFuture.allOf(attachableTextures.stream().map(textureSaver).toArray(CompletableFuture[]::new)), createdAttachable.map(attachable -> attachable.save(serializer, attachableDirectory)).orElse(noop()),
stitchedGeometry.map(BedrockGeometryContext.StitchedGeometry::geometry).map(geometry -> geometry.save(serializer, geometryDirectory)).orElse(noop()), CompletableFuture.allOf(attachableTextures.stream().map(textureSaver).toArray(CompletableFuture[]::new)),
stitchedGeometry.map(BedrockGeometryContext.StitchedGeometry::stitchedTextures).map(textureSaver).orElse(noop()), stitchedGeometry.map(BedrockGeometryContext.StitchedGeometry::geometry).map(geometry -> geometry.save(serializer, geometryDirectory)).orElse(noop()),
geometryContext.animation().map(context -> context.animation().save(serializer, animationDirectory, Rainbow.fileSafeResourceLocation(identifier))).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 <T> CompletableFuture<T> noop() { private static <T> CompletableFuture<T> noop() {

View File

@@ -19,7 +19,6 @@ import org.geysermc.rainbow.mapping.BedrockItemMapper;
import org.geysermc.rainbow.mapping.PackContext; import org.geysermc.rainbow.mapping.PackContext;
import org.geysermc.rainbow.mapping.PackSerializer; import org.geysermc.rainbow.mapping.PackSerializer;
import org.geysermc.rainbow.mapping.geometry.GeometryRenderer; import org.geysermc.rainbow.mapping.geometry.GeometryRenderer;
import org.geysermc.rainbow.mapping.geometry.NoopGeometryRenderer;
import org.geysermc.rainbow.definition.GeyserMappings; import org.geysermc.rainbow.definition.GeyserMappings;
import org.geysermc.rainbow.mapping.geometry.TextureHolder; import org.geysermc.rainbow.mapping.geometry.TextureHolder;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
@@ -36,6 +35,7 @@ import java.util.UUID;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Function; import java.util.function.Function;
import java.util.function.Supplier;
import java.util.function.UnaryOperator; import java.util.function.UnaryOperator;
public class BedrockPack { public class BedrockPack {
@@ -53,7 +53,7 @@ public class BedrockPack {
private final ProblemReporter.Collector reporter; private final ProblemReporter.Collector reporter;
public BedrockPack(String name, PackManifest manifest, PackPaths paths, PackSerializer serializer, AssetResolver assetResolver, public BedrockPack(String name, PackManifest manifest, PackPaths paths, PackSerializer serializer, AssetResolver assetResolver,
GeometryRenderer geometryRenderer, ProblemReporter.Collector reporter, Optional<GeometryRenderer> geometryRenderer, ProblemReporter.Collector reporter,
boolean reportSuccesses) { boolean reportSuccesses) {
this.name = name; this.name = name;
this.manifest = manifest; this.manifest = manifest;
@@ -206,7 +206,7 @@ public class BedrockPack {
private UnaryOperator<Path> manifestPath = resolve(MANIFEST_FILE); private UnaryOperator<Path> manifestPath = resolve(MANIFEST_FILE);
private UnaryOperator<Path> itemAtlasPath = resolve(ITEM_ATLAS_FILE); private UnaryOperator<Path> itemAtlasPath = resolve(ITEM_ATLAS_FILE);
private Path packZipFile = null; private Path packZipFile = null;
private GeometryRenderer geometryRenderer = NoopGeometryRenderer.INSTANCE; private GeometryRenderer geometryRenderer = null;
private ProblemReporter.Collector reporter; private ProblemReporter.Collector reporter;
private boolean reportSuccesses = false; private boolean reportSuccesses = false;
@@ -294,7 +294,7 @@ public class BedrockPack {
PackPaths paths = new PackPaths(mappingsPath, packRootPath, attachablesPath.apply(packRootPath), PackPaths paths = new PackPaths(mappingsPath, packRootPath, attachablesPath.apply(packRootPath),
geometryPath.apply(packRootPath), animationPath.apply(packRootPath), manifestPath.apply(packRootPath), geometryPath.apply(packRootPath), animationPath.apply(packRootPath), manifestPath.apply(packRootPath),
itemAtlasPath.apply(packRootPath), Optional.ofNullable(packZipFile)); 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<Path> resolve(Path child) { private static UnaryOperator<Path> resolve(Path child) {