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:
@@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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) {}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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() {
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
Reference in New Issue
Block a user