diff --git a/src/main/java/org/geysermc/packgenerator/mapping/geometry/GeometryRenderer.java b/src/main/java/org/geysermc/packgenerator/mapping/geometry/GeometryRenderer.java new file mode 100644 index 0000000..087ff89 --- /dev/null +++ b/src/main/java/org/geysermc/packgenerator/mapping/geometry/GeometryRenderer.java @@ -0,0 +1,75 @@ +package org.geysermc.packgenerator.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.packgenerator.mixin.PictureInPictureRendererAccessor; +import org.geysermc.packgenerator.render.PictureInPictureCopyRenderer; +import org.joml.Matrix3x2fStack; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.Objects; + +public class 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).geyser_mappings_generator$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); + } + } + + nativeImage.writeToFile(path); + } catch (IOException var19) { + // TODO + } + } + + buffer.close(); + }; + commandEncoder.copyTextureToBuffer(texture, buffer, 0, writer, 0); + } +} diff --git a/src/main/java/org/geysermc/packgenerator/mixin/GuiItemRenderStateMixin.java b/src/main/java/org/geysermc/packgenerator/mixin/GuiItemRenderStateMixin.java new file mode 100644 index 0000000..d75d476 --- /dev/null +++ b/src/main/java/org/geysermc/packgenerator/mixin/GuiItemRenderStateMixin.java @@ -0,0 +1,16 @@ +package org.geysermc.packgenerator.mixin; + +import net.minecraft.client.gui.render.state.GuiItemRenderState; +import net.minecraft.client.gui.render.state.ScreenArea; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.Constant; +import org.spongepowered.asm.mixin.injection.ModifyConstant; + +@Mixin(GuiItemRenderState.class) +public abstract class GuiItemRenderStateMixin implements ScreenArea { + + @ModifyConstant(method = "calculateOversizedItemBounds", constant = @Constant(intValue = 16)) + public int neverReturnNull(int i) { + return -1; + } +} diff --git a/src/main/java/org/geysermc/packgenerator/mixin/PictureInPictureRendererAccessor.java b/src/main/java/org/geysermc/packgenerator/mixin/PictureInPictureRendererAccessor.java new file mode 100644 index 0000000..39aa091 --- /dev/null +++ b/src/main/java/org/geysermc/packgenerator/mixin/PictureInPictureRendererAccessor.java @@ -0,0 +1,13 @@ +package org.geysermc.packgenerator.mixin; + +import com.mojang.blaze3d.textures.GpuTexture; +import net.minecraft.client.gui.render.pip.PictureInPictureRenderer; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +@Mixin(PictureInPictureRenderer.class) +public interface PictureInPictureRendererAccessor { + + @Accessor + GpuTexture getTexture(); +} diff --git a/src/main/java/org/geysermc/packgenerator/mixin/PictureInPictureRendererMixin.java b/src/main/java/org/geysermc/packgenerator/mixin/PictureInPictureRendererMixin.java new file mode 100644 index 0000000..342f54e --- /dev/null +++ b/src/main/java/org/geysermc/packgenerator/mixin/PictureInPictureRendererMixin.java @@ -0,0 +1,34 @@ +package org.geysermc.packgenerator.mixin; + +import com.mojang.blaze3d.textures.GpuTexture; +import net.minecraft.client.gui.render.pip.PictureInPictureRenderer; +import org.geysermc.packgenerator.render.PictureInPictureCopyRenderer; +import org.jetbrains.annotations.Nullable; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.Constant; +import org.spongepowered.asm.mixin.injection.ModifyConstant; + +@Mixin(PictureInPictureRenderer.class) +public abstract class PictureInPictureRendererMixin implements AutoCloseable, PictureInPictureCopyRenderer { + + @Shadow + private @Nullable GpuTexture texture; + + @Unique + private boolean allowTextureCopy = false; + + @Override + public void geyser_mappings_generator$allowTextureCopy() { + if (texture != null) { + throw new IllegalStateException("texture already created"); + } + allowTextureCopy = true; + } + + @ModifyConstant(method = "prepareTexturesAndProjection", constant = @Constant(intValue = 12)) + public int allowUsageCopySrc(int usage) { + return allowTextureCopy ? usage | GpuTexture.USAGE_COPY_SRC : usage; + } +} diff --git a/src/main/java/org/geysermc/packgenerator/render/PictureInPictureCopyRenderer.java b/src/main/java/org/geysermc/packgenerator/render/PictureInPictureCopyRenderer.java new file mode 100644 index 0000000..6a2bef6 --- /dev/null +++ b/src/main/java/org/geysermc/packgenerator/render/PictureInPictureCopyRenderer.java @@ -0,0 +1,6 @@ +package org.geysermc.packgenerator.render; + +public interface PictureInPictureCopyRenderer { + + void geyser_mappings_generator$allowTextureCopy(); +} diff --git a/src/main/resources/geyser-mappings-generator.mixins.json b/src/main/resources/geyser-mappings-generator.mixins.json index 968ff39..de34904 100644 --- a/src/main/resources/geyser-mappings-generator.mixins.json +++ b/src/main/resources/geyser-mappings-generator.mixins.json @@ -9,7 +9,10 @@ "BlockModelWrapperMixin$UnbakedMixin", "ConditionalItemModelAccessor", "EntityRenderDispatcherAccessor", + "GuiItemRenderStateMixin", "ModelManagerMixin", + "PictureInPictureRendererAccessor", + "PictureInPictureRendererMixin", "SelectItemModelAccessor", "SelectItemModelMixin", "SelectItemModelMixin$UnbakedSwitchMixin",