mirror of
https://github.com/GeyserMC/Rainbow.git
synced 2025-12-19 14:59:16 +00:00
Re-use mapped geometry when the textures and geometry is the same, let GeometryRenderer return TextureHolder, fix client geometry rendering
This commit is contained in:
@@ -1,24 +1,9 @@
|
|||||||
package org.geysermc.rainbow.client.render;
|
package org.geysermc.rainbow.client.render;
|
||||||
|
|
||||||
import com.mojang.blaze3d.buffers.GpuBuffer;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
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 net.minecraft.world.item.ItemStack;
|
||||||
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.mapping.texture.TextureHolder;
|
||||||
import org.joml.Matrix3x2fStack;
|
|
||||||
|
|
||||||
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
|
||||||
// TODO output in a size bedrock likes
|
// TODO output in a size bedrock likes
|
||||||
@@ -26,49 +11,7 @@ public class MinecraftGeometryRenderer implements GeometryRenderer {
|
|||||||
public static final MinecraftGeometryRenderer INSTANCE = new MinecraftGeometryRenderer();
|
public static final MinecraftGeometryRenderer INSTANCE = new MinecraftGeometryRenderer();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public NativeImage render(ItemStack stack) {
|
public TextureHolder render(ResourceLocation location, ItemStack stack) {
|
||||||
TrackingItemStackRenderState itemRenderState = new TrackingItemStackRenderState();
|
return new RenderedTextureHolder(location, stack);
|
||||||
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);
|
|
||||||
return writeToImage(((PictureInPictureRendererAccessor) itemRenderer).getTexture());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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);
|
|
||||||
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();
|
|
||||||
|
|
||||||
NativeImage image = new NativeImage(width, height, false);
|
|
||||||
Runnable writer = () -> {
|
|
||||||
try (GpuBuffer.MappedView mappedView = commandEncoder.mapBuffer(buffer, true, 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());
|
|
||||||
image.setPixelABGR(x, height - y - 1, colour);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
buffer.close();
|
|
||||||
};
|
|
||||||
commandEncoder.copyTextureToBuffer(texture, buffer, 0, writer, 0);
|
|
||||||
|
|
||||||
return image;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,95 @@
|
|||||||
|
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.resources.ResourceLocation;
|
||||||
|
import net.minecraft.util.ProblemReporter;
|
||||||
|
import net.minecraft.world.item.ItemDisplayContext;
|
||||||
|
import net.minecraft.world.item.ItemStack;
|
||||||
|
import org.geysermc.rainbow.RainbowIO;
|
||||||
|
import org.geysermc.rainbow.client.mixin.PictureInPictureRendererAccessor;
|
||||||
|
import org.geysermc.rainbow.image.NativeImageUtil;
|
||||||
|
import org.geysermc.rainbow.mapping.AssetResolver;
|
||||||
|
import org.geysermc.rainbow.mapping.PackSerializer;
|
||||||
|
import org.geysermc.rainbow.mapping.texture.TextureHolder;
|
||||||
|
import org.joml.Matrix3x2fStack;
|
||||||
|
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
|
||||||
|
public class RenderedTextureHolder extends TextureHolder {
|
||||||
|
private final ItemStack stackToRender;
|
||||||
|
|
||||||
|
public RenderedTextureHolder(ResourceLocation location, ItemStack stackToRender) {
|
||||||
|
super(location);
|
||||||
|
this.stackToRender = stackToRender;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Optional<byte[]> load(AssetResolver assetResolver, ProblemReporter reporter) {
|
||||||
|
throw new UnsupportedOperationException("Rendered texture does not support loading");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CompletableFuture<?> save(AssetResolver assetResolver, PackSerializer serializer, Path path, ProblemReporter reporter) {
|
||||||
|
TrackingItemStackRenderState itemRenderState = new TrackingItemStackRenderState();
|
||||||
|
Minecraft.getInstance().getItemModelResolver().updateForTopItem(itemRenderState, stackToRender, 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(serializer, path, ((PictureInPictureRendererAccessor) itemRenderer).getTexture());
|
||||||
|
}
|
||||||
|
return CompletableFuture.completedFuture(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Simplified TextureUtil#writeAsPNG with some modifications to flip the image and just generate it at full size
|
||||||
|
private static void writeAsPNG(PackSerializer serializer, 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)) {
|
||||||
|
RainbowIO.safeIO(() -> {
|
||||||
|
try (NativeImage image = 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());
|
||||||
|
image.setPixelABGR(x, height - y - 1, colour);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
serializer.saveTexture(NativeImageUtil.writeToByteArray(image), path);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer.close();
|
||||||
|
};
|
||||||
|
commandEncoder.copyTextureToBuffer(texture, buffer, 0, writer, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
package org.geysermc.rainbow.datagen;
|
package org.geysermc.rainbow.datagen;
|
||||||
|
|
||||||
import com.google.common.hash.HashCode;
|
import com.google.common.hash.HashCode;
|
||||||
|
import com.google.common.hash.Hashing;
|
||||||
import com.mojang.serialization.Codec;
|
import com.mojang.serialization.Codec;
|
||||||
import net.fabricmc.fabric.api.client.datagen.v1.provider.FabricModelProvider;
|
import net.fabricmc.fabric.api.client.datagen.v1.provider.FabricModelProvider;
|
||||||
import net.fabricmc.fabric.api.datagen.v1.FabricDataOutput;
|
import net.fabricmc.fabric.api.datagen.v1.FabricDataOutput;
|
||||||
@@ -125,7 +126,7 @@ public abstract class RainbowModelProvider extends FabricModelProvider {
|
|||||||
public CompletableFuture<?> saveTexture(byte[] texture, Path path) {
|
public CompletableFuture<?> saveTexture(byte[] texture, Path path) {
|
||||||
return CompletableFuture.runAsync(() -> {
|
return CompletableFuture.runAsync(() -> {
|
||||||
try {
|
try {
|
||||||
output.writeIfNeeded(path, texture, HashCode.fromBytes(texture));
|
output.writeIfNeeded(path, texture, Hashing.sha1().hashBytes(texture));
|
||||||
} catch (IOException exception) {
|
} catch (IOException exception) {
|
||||||
LOGGER.error("Failed to save texture to {}", path, exception);
|
LOGGER.error("Failed to save texture to {}", path, exception);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
"schemaVersion": 1,
|
"schemaVersion": 1,
|
||||||
"id": "rainbow-datagen",
|
"id": "rainbow-datagen",
|
||||||
"version": "${version}",
|
"version": "${version}",
|
||||||
"name": "Rainbow",
|
"name": "Rainbow-datagen",
|
||||||
"description": "Rainbow's datagen module",
|
"description": "Rainbow's datagen module",
|
||||||
"authors": [
|
"authors": [
|
||||||
"GeyserMC contributors"
|
"GeyserMC contributors"
|
||||||
|
|||||||
@@ -128,7 +128,7 @@ public class BedrockItemMapper {
|
|||||||
bedrockIdentifier = itemModelLocation;
|
bedrockIdentifier = itemModelLocation;
|
||||||
}
|
}
|
||||||
|
|
||||||
BedrockGeometryContext geometry = BedrockGeometryContext.create(bedrockIdentifier, context.stack, itemModel, context.packContext);
|
BedrockGeometryContext geometry = BedrockGeometryContext.create(bedrockIdentifier, itemModel, context.stack, 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);
|
||||||
|
|||||||
@@ -2,9 +2,55 @@ package org.geysermc.rainbow.mapping;
|
|||||||
|
|
||||||
import org.geysermc.rainbow.mapping.geometry.GeometryRenderer;
|
import org.geysermc.rainbow.mapping.geometry.GeometryRenderer;
|
||||||
import org.geysermc.rainbow.definition.GeyserMappings;
|
import org.geysermc.rainbow.definition.GeyserMappings;
|
||||||
|
import org.geysermc.rainbow.mapping.geometry.MappedGeometryCache;
|
||||||
import org.geysermc.rainbow.pack.PackPaths;
|
import org.geysermc.rainbow.pack.PackPaths;
|
||||||
|
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
public record PackContext(GeyserMappings mappings, PackPaths paths, BedrockItemConsumer itemConsumer, AssetResolver assetResolver,
|
public final class PackContext {
|
||||||
Optional<GeometryRenderer> geometryRenderer, boolean reportSuccesses) {}
|
private final GeyserMappings mappings;
|
||||||
|
private final PackPaths paths;
|
||||||
|
private final BedrockItemConsumer itemConsumer;
|
||||||
|
private final AssetResolver assetResolver;
|
||||||
|
private final Optional<GeometryRenderer> geometryRenderer;
|
||||||
|
private final boolean reportSuccesses;
|
||||||
|
private final MappedGeometryCache geometryCache = new MappedGeometryCache();
|
||||||
|
|
||||||
|
public PackContext(GeyserMappings mappings, PackPaths paths, BedrockItemConsumer itemConsumer, AssetResolver assetResolver,
|
||||||
|
Optional<GeometryRenderer> geometryRenderer, boolean reportSuccesses) {
|
||||||
|
this.mappings = mappings;
|
||||||
|
this.paths = paths;
|
||||||
|
this.itemConsumer = itemConsumer;
|
||||||
|
this.assetResolver = assetResolver;
|
||||||
|
this.geometryRenderer = geometryRenderer;
|
||||||
|
this.reportSuccesses = reportSuccesses;
|
||||||
|
}
|
||||||
|
|
||||||
|
public GeyserMappings mappings() {
|
||||||
|
return mappings;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PackPaths paths() {
|
||||||
|
return paths;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BedrockItemConsumer itemConsumer() {
|
||||||
|
return itemConsumer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AssetResolver assetResolver() {
|
||||||
|
return assetResolver;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Optional<GeometryRenderer> geometryRenderer() {
|
||||||
|
return geometryRenderer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean reportSuccesses() {
|
||||||
|
return reportSuccesses;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MappedGeometryCache geometryCache() {
|
||||||
|
return geometryCache;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -10,8 +10,8 @@ import net.minecraft.world.entity.EquipmentSlot;
|
|||||||
import net.minecraft.world.item.equipment.Equippable;
|
import net.minecraft.world.item.equipment.Equippable;
|
||||||
import org.geysermc.rainbow.mapping.AssetResolver;
|
import org.geysermc.rainbow.mapping.AssetResolver;
|
||||||
import org.geysermc.rainbow.mapping.geometry.BedrockGeometryContext;
|
import org.geysermc.rainbow.mapping.geometry.BedrockGeometryContext;
|
||||||
import org.geysermc.rainbow.mapping.geometry.StitchedGeometry;
|
import org.geysermc.rainbow.mapping.geometry.MappedGeometry;
|
||||||
import org.geysermc.rainbow.mapping.geometry.TextureHolder;
|
import org.geysermc.rainbow.mapping.texture.TextureHolder;
|
||||||
import org.geysermc.rainbow.pack.attachable.BedrockAttachable;
|
import org.geysermc.rainbow.pack.attachable.BedrockAttachable;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -23,8 +23,8 @@ public class AttachableMapper {
|
|||||||
public static AttachableCreator mapItem(AssetResolver assetResolver, BedrockGeometryContext geometryContext, DataComponentPatch components) {
|
public static AttachableCreator mapItem(AssetResolver assetResolver, BedrockGeometryContext geometryContext, DataComponentPatch components) {
|
||||||
// Crazy optional statement
|
// Crazy optional statement
|
||||||
// Unfortunately we can't have both equippables and custom models, so we prefer the latter :(
|
// Unfortunately we can't have both equippables and custom models, so we prefer the latter :(
|
||||||
return (bedrockIdentifier, stitchedGeometry, textureConsumer) -> stitchedGeometry
|
return (bedrockIdentifier, textureConsumer) -> geometryContext.geometry()
|
||||||
.map(stitched -> BedrockAttachable.geometry(bedrockIdentifier, stitched.geometry().definitions().getFirst(), stitched.stitchedTextures().location().getPath()))
|
.map(geometry -> BedrockAttachable.geometry(bedrockIdentifier, geometry))
|
||||||
.or(() -> Optional.ofNullable(components.get(DataComponents.EQUIPPABLE))
|
.or(() -> Optional.ofNullable(components.get(DataComponents.EQUIPPABLE))
|
||||||
.flatMap(optional -> (Optional<Equippable>) optional)
|
.flatMap(optional -> (Optional<Equippable>) optional)
|
||||||
.flatMap(equippable -> equippable.assetId().flatMap(assetResolver::getEquipmentInfo).map(info -> Pair.of(equippable.slot(), info)))
|
.flatMap(equippable -> equippable.assetId().flatMap(assetResolver::getEquipmentInfo).map(info -> Pair.of(equippable.slot(), info)))
|
||||||
@@ -34,7 +34,7 @@ public class AttachableMapper {
|
|||||||
.filter(assetInfo -> !assetInfo.getSecond().isEmpty())
|
.filter(assetInfo -> !assetInfo.getSecond().isEmpty())
|
||||||
.map(assetInfo -> {
|
.map(assetInfo -> {
|
||||||
ResourceLocation equipmentTexture = getTexture(assetInfo.getSecond(), getLayer(assetInfo.getFirst()));
|
ResourceLocation equipmentTexture = getTexture(assetInfo.getSecond(), getLayer(assetInfo.getFirst()));
|
||||||
textureConsumer.accept(TextureHolder.createFromResources(equipmentTexture));
|
textureConsumer.accept(TextureHolder.createBuiltIn(equipmentTexture));
|
||||||
return BedrockAttachable.equipment(bedrockIdentifier, assetInfo.getFirst(), equipmentTexture.getPath());
|
return BedrockAttachable.equipment(bedrockIdentifier, assetInfo.getFirst(), equipmentTexture.getPath());
|
||||||
}))
|
}))
|
||||||
.map(attachable -> {
|
.map(attachable -> {
|
||||||
@@ -59,6 +59,6 @@ public class AttachableMapper {
|
|||||||
@FunctionalInterface
|
@FunctionalInterface
|
||||||
public interface AttachableCreator {
|
public interface AttachableCreator {
|
||||||
|
|
||||||
Optional<BedrockAttachable> create(ResourceLocation bedrockIdentifier, Optional<StitchedGeometry> geometry, Consumer<TextureHolder> textureConsumer);
|
Optional<BedrockAttachable> create(ResourceLocation bedrockIdentifier, Consumer<TextureHolder> textureConsumer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
package org.geysermc.rainbow.mapping.geometry;
|
package org.geysermc.rainbow.mapping.geometry;
|
||||||
|
|
||||||
import com.google.common.base.Suppliers;
|
|
||||||
import net.minecraft.client.renderer.block.model.TextureSlots;
|
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;
|
||||||
@@ -10,51 +9,41 @@ 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;
|
||||||
import org.geysermc.rainbow.mapping.animation.BedrockAnimationContext;
|
import org.geysermc.rainbow.mapping.animation.BedrockAnimationContext;
|
||||||
import org.geysermc.rainbow.pack.geometry.BedrockGeometry;
|
import org.geysermc.rainbow.mapping.texture.TextureHolder;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.function.Supplier;
|
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
public record BedrockGeometryContext(Optional<Supplier<StitchedGeometry>> geometry,
|
public record BedrockGeometryContext(Optional<MappedGeometry> geometry,
|
||||||
Optional<BedrockAnimationContext> animation, TextureHolder icon,
|
Optional<BedrockAnimationContext> animation, TextureHolder icon,
|
||||||
boolean handheld) {
|
boolean handheld) {
|
||||||
private static final List<ResourceLocation> HANDHELD_MODELS = Stream.of("item/handheld", "item/handheld_rod", "item/handheld_mace")
|
private static final List<ResourceLocation> HANDHELD_MODELS = Stream.of("item/handheld", "item/handheld_rod", "item/handheld_mace")
|
||||||
.map(ResourceLocation::withDefaultNamespace)
|
.map(ResourceLocation::withDefaultNamespace)
|
||||||
.toList();
|
.toList();
|
||||||
|
|
||||||
public static BedrockGeometryContext create(ResourceLocation bedrockIdentifier, ItemStack stackToRender, ResolvedModel model, PackContext context) {
|
public static BedrockGeometryContext create(ResourceLocation bedrockIdentifier, ResolvedModel model, ItemStack stackToRender, 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()));
|
||||||
|
|
||||||
TextureSlots textures = model.getTopTextureSlots();
|
TextureSlots textures = model.getTopTextureSlots();
|
||||||
Material layer0Texture = textures.getMaterial("layer0");
|
Material layer0Texture = textures.getMaterial("layer0");
|
||||||
Optional<Supplier<StitchedGeometry>> geometry;
|
Optional<MappedGeometry> geometry;
|
||||||
Optional<BedrockAnimationContext> animation;
|
Optional<BedrockAnimationContext> animation;
|
||||||
TextureHolder icon;
|
TextureHolder icon;
|
||||||
|
|
||||||
if (layer0Texture != null) {
|
if (layer0Texture != null) {
|
||||||
geometry = Optional.empty();
|
geometry = Optional.empty();
|
||||||
animation = Optional.empty();
|
animation = Optional.empty();
|
||||||
icon = TextureHolder.createFromResources(layer0Texture.texture());
|
icon = TextureHolder.createBuiltIn(layer0Texture.texture());
|
||||||
} else {
|
} else {
|
||||||
// Unknown model (doesn't use layer0), so we immediately assume the geometry is custom
|
// Unknown model (doesn't use layer0), so we immediately assume the geometry is custom
|
||||||
// This check should probably be done differently (actually check if the model is 2D or 3D)
|
// This check should probably be done differently (actually check if the model is 2D or 3D)
|
||||||
|
|
||||||
ResourceLocation modelLocation = ResourceLocation.parse(model.debugName());
|
geometry = Optional.of(context.geometryCache().mapGeometry(bedrockIdentifier, model, stackToRender, context));
|
||||||
String safeIdentifier = Rainbow.safeResourceLocation(bedrockIdentifier);
|
animation = Optional.of(AnimationMapper.mapAnimation(Rainbow.safeResourceLocation(bedrockIdentifier), "bone", model.getTopTransforms()));
|
||||||
|
icon = geometry.get().icon();
|
||||||
geometry = Optional.of(Suppliers.memoize(() -> {
|
|
||||||
StitchedTextures stitchedTextures = StitchedTextures.stitchModelTextures(textures, context);
|
|
||||||
BedrockGeometry mappedGeometry = GeometryMapper.mapGeometry(safeIdentifier, "bone", model, stitchedTextures);
|
|
||||||
return new StitchedGeometry(mappedGeometry, TextureHolder.createProvided(modelLocation.withSuffix("_stitched"), stitchedTextures.stitched()));
|
|
||||||
}));
|
|
||||||
|
|
||||||
animation = Optional.of(AnimationMapper.mapAnimation(safeIdentifier, "bone", model.getTopTransforms()));
|
|
||||||
icon = context.geometryRenderer().isPresent() ? TextureHolder.createProvided(modelLocation, () -> context.geometryRenderer().orElseThrow().render(stackToRender))
|
|
||||||
: TextureHolder.createNonExistent(modelLocation);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return new BedrockGeometryContext(geometry, animation, icon, handheld);
|
return new BedrockGeometryContext(geometry, animation, icon, handheld);
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import net.minecraft.client.renderer.block.model.SimpleUnbakedGeometry;
|
|||||||
import net.minecraft.client.resources.model.ResolvedModel;
|
import net.minecraft.client.resources.model.ResolvedModel;
|
||||||
import net.minecraft.client.resources.model.UnbakedGeometry;
|
import net.minecraft.client.resources.model.UnbakedGeometry;
|
||||||
import net.minecraft.core.Direction;
|
import net.minecraft.core.Direction;
|
||||||
|
import org.geysermc.rainbow.mapping.texture.StitchedTextures;
|
||||||
import org.geysermc.rainbow.mixin.FaceBakeryAccessor;
|
import org.geysermc.rainbow.mixin.FaceBakeryAccessor;
|
||||||
import org.geysermc.rainbow.pack.geometry.BedrockGeometry;
|
import org.geysermc.rainbow.pack.geometry.BedrockGeometry;
|
||||||
import org.joml.Vector2f;
|
import org.joml.Vector2f;
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
package org.geysermc.rainbow.mapping.geometry;
|
package org.geysermc.rainbow.mapping.geometry;
|
||||||
|
|
||||||
import com.mojang.blaze3d.platform.NativeImage;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
import net.minecraft.world.item.ItemStack;
|
import net.minecraft.world.item.ItemStack;
|
||||||
|
import org.geysermc.rainbow.mapping.texture.TextureHolder;
|
||||||
|
|
||||||
public interface GeometryRenderer {
|
public interface GeometryRenderer {
|
||||||
|
|
||||||
NativeImage render(ItemStack stack);
|
TextureHolder render(ResourceLocation location, ItemStack stack);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,34 @@
|
|||||||
|
package org.geysermc.rainbow.mapping.geometry;
|
||||||
|
|
||||||
|
import org.geysermc.rainbow.mapping.PackSerializer;
|
||||||
|
import org.geysermc.rainbow.mapping.texture.TextureHolder;
|
||||||
|
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
public interface MappedGeometry {
|
||||||
|
|
||||||
|
String identifier();
|
||||||
|
|
||||||
|
TextureHolder stitchedTextures();
|
||||||
|
|
||||||
|
TextureHolder icon();
|
||||||
|
|
||||||
|
CompletableFuture<?> save(PackSerializer serializer, Path geometryDirectory, Function<TextureHolder, CompletableFuture<?>> textureSaver);
|
||||||
|
|
||||||
|
default CachedGeometry cachedCopy() {
|
||||||
|
if (this instanceof CachedGeometry cached) {
|
||||||
|
return cached;
|
||||||
|
}
|
||||||
|
return new CachedGeometry(identifier(), TextureHolder.createCopy(stitchedTextures()), TextureHolder.createCopy(icon()));
|
||||||
|
}
|
||||||
|
|
||||||
|
record CachedGeometry(String identifier, TextureHolder stitchedTextures, TextureHolder icon) implements MappedGeometry {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CompletableFuture<?> save(PackSerializer serializer, Path geometryDirectory, Function<TextureHolder, CompletableFuture<?>> textureSaver) {
|
||||||
|
return CompletableFuture.completedFuture(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
package org.geysermc.rainbow.mapping.geometry;
|
||||||
|
|
||||||
|
import com.google.common.base.Suppliers;
|
||||||
|
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.client.resources.model.UnbakedGeometry;
|
||||||
|
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.texture.StitchedTextures;
|
||||||
|
import org.geysermc.rainbow.mapping.texture.TextureHolder;
|
||||||
|
import org.geysermc.rainbow.mixin.TextureSlotsAccessor;
|
||||||
|
import org.geysermc.rainbow.pack.geometry.BedrockGeometry;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class MappedGeometryCache {
|
||||||
|
private final Map<GeometryCacheKey, MappedGeometryInstance> cachedGeometry = new HashMap<>();
|
||||||
|
|
||||||
|
public MappedGeometry mapGeometry(ResourceLocation bedrockIdentifier, ResolvedModel model, ItemStack stackToRender, PackContext context) {
|
||||||
|
GeometryCacheKey cacheKey = new GeometryCacheKey(model);
|
||||||
|
MappedGeometry cached = cachedGeometry.get(cacheKey);
|
||||||
|
if (cached != null) {
|
||||||
|
return cached.cachedCopy();
|
||||||
|
}
|
||||||
|
|
||||||
|
ResourceLocation modelLocation = ResourceLocation.parse(model.debugName());
|
||||||
|
ResourceLocation stitchedTexturesLocation = modelLocation.withSuffix("_stitched");
|
||||||
|
String safeIdentifier = Rainbow.safeResourceLocation(bedrockIdentifier);
|
||||||
|
|
||||||
|
StitchedTextures stitchedTextures = StitchedTextures.stitchModelTextures(model.getTopTextureSlots(), context);
|
||||||
|
BedrockGeometry geometry = GeometryMapper.mapGeometry(safeIdentifier, "bone", model, stitchedTextures);
|
||||||
|
TextureHolder icon = context.geometryRenderer().isPresent() ? context.geometryRenderer().orElseThrow().render(modelLocation, stackToRender)
|
||||||
|
: TextureHolder.createNonExistent(modelLocation);
|
||||||
|
MappedGeometryInstance instance = new MappedGeometryInstance(geometry, TextureHolder.createCustom(stitchedTexturesLocation, stitchedTextures.stitched()), icon);
|
||||||
|
cachedGeometry.put(cacheKey, instance);
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
private record GeometryCacheKey(UnbakedGeometry geometry, Map<String, Material> textures) {
|
||||||
|
|
||||||
|
private GeometryCacheKey(ResolvedModel model) {
|
||||||
|
this(model.getTopGeometry(), ((TextureSlotsAccessor) model.getTopTextureSlots()).getResolvedValues());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
package org.geysermc.rainbow.mapping.geometry;
|
||||||
|
|
||||||
|
import org.geysermc.rainbow.mapping.PackSerializer;
|
||||||
|
import org.geysermc.rainbow.mapping.texture.TextureHolder;
|
||||||
|
import org.geysermc.rainbow.pack.geometry.BedrockGeometry;
|
||||||
|
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
public record MappedGeometryInstance(BedrockGeometry geometry, TextureHolder stitchedTextures, TextureHolder icon) implements MappedGeometry {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String identifier() {
|
||||||
|
return geometry.definitions().getFirst().info().identifier();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CompletableFuture<?> save(PackSerializer serializer, Path geometryDirectory, Function<TextureHolder, CompletableFuture<?>> textureSaver) {
|
||||||
|
return CompletableFuture.allOf(
|
||||||
|
geometry.save(serializer, geometryDirectory),
|
||||||
|
textureSaver.apply(stitchedTextures)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
package org.geysermc.rainbow.mapping.geometry;
|
|
||||||
|
|
||||||
import org.geysermc.rainbow.pack.geometry.BedrockGeometry;
|
|
||||||
|
|
||||||
public record StitchedGeometry(BedrockGeometry geometry, TextureHolder stitchedTextures) {}
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
package org.geysermc.rainbow.mapping.geometry;
|
|
||||||
|
|
||||||
import com.mojang.blaze3d.platform.NativeImage;
|
|
||||||
import net.minecraft.resources.ResourceLocation;
|
|
||||||
import net.minecraft.util.ProblemReporter;
|
|
||||||
import org.geysermc.rainbow.Rainbow;
|
|
||||||
import org.geysermc.rainbow.RainbowIO;
|
|
||||||
import org.geysermc.rainbow.image.NativeImageUtil;
|
|
||||||
import org.geysermc.rainbow.mapping.AssetResolver;
|
|
||||||
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.util.Optional;
|
|
||||||
import java.util.function.Supplier;
|
|
||||||
|
|
||||||
public record TextureHolder(ResourceLocation location, Optional<Supplier<NativeImage>> supplier, boolean existsInResources) {
|
|
||||||
|
|
||||||
public Optional<byte[]> load(AssetResolver assetResolver, ProblemReporter reporter) {
|
|
||||||
if (existsInResources) {
|
|
||||||
return RainbowIO.safeIO(() -> {
|
|
||||||
try (InputStream texture = assetResolver.openAsset(Rainbow.decorateTextureLocation(location))) {
|
|
||||||
return texture.readAllBytes();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else if (supplier.isPresent()) {
|
|
||||||
return RainbowIO.safeIO(() -> NativeImageUtil.writeToByteArray(supplier.get().get()));
|
|
||||||
}
|
|
||||||
reporter.report(() -> "missing texture for " + location + "; please provide it manually");
|
|
||||||
return Optional.empty();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static TextureHolder createProvided(ResourceLocation location, Supplier<NativeImage> supplier) {
|
|
||||||
return new TextureHolder(location, Optional.of(supplier), false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static TextureHolder createFromResources(ResourceLocation location) {
|
|
||||||
return new TextureHolder(location, Optional.empty(), true);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static TextureHolder createNonExistent(ResourceLocation location) {
|
|
||||||
return new TextureHolder(location, Optional.empty(), false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
package org.geysermc.rainbow.mapping.texture;
|
||||||
|
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
import net.minecraft.util.ProblemReporter;
|
||||||
|
import org.geysermc.rainbow.Rainbow;
|
||||||
|
import org.geysermc.rainbow.RainbowIO;
|
||||||
|
import org.geysermc.rainbow.mapping.AssetResolver;
|
||||||
|
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
public class BuiltInTextureHolder extends TextureHolder {
|
||||||
|
private final ResourceLocation source;
|
||||||
|
|
||||||
|
public BuiltInTextureHolder(ResourceLocation location, ResourceLocation source) {
|
||||||
|
super(location);
|
||||||
|
this.source = source;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Optional<byte[]> load(AssetResolver assetResolver, ProblemReporter reporter) {
|
||||||
|
return RainbowIO.safeIO(() -> {
|
||||||
|
try (InputStream texture = assetResolver.openAsset(Rainbow.decorateTextureLocation(source))) {
|
||||||
|
return texture.readAllBytes();
|
||||||
|
} catch (FileNotFoundException | NullPointerException exception) {
|
||||||
|
reportMissing(reporter);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
package org.geysermc.rainbow.mapping.texture;
|
||||||
|
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
import net.minecraft.util.ProblemReporter;
|
||||||
|
import org.geysermc.rainbow.mapping.AssetResolver;
|
||||||
|
import org.geysermc.rainbow.mapping.PackSerializer;
|
||||||
|
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
|
||||||
|
public class CopyTextureHolder extends TextureHolder {
|
||||||
|
|
||||||
|
public CopyTextureHolder(ResourceLocation location) {
|
||||||
|
super(location);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Optional<byte[]> load(AssetResolver assetResolver, ProblemReporter reporter) {
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CompletableFuture<?> save(AssetResolver assetResolver, PackSerializer serializer, Path path, ProblemReporter reporter) {
|
||||||
|
return CompletableFuture.completedFuture(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
package org.geysermc.rainbow.mapping.texture;
|
||||||
|
|
||||||
|
import com.mojang.blaze3d.platform.NativeImage;
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
import net.minecraft.util.ProblemReporter;
|
||||||
|
import org.geysermc.rainbow.RainbowIO;
|
||||||
|
import org.geysermc.rainbow.image.NativeImageUtil;
|
||||||
|
import org.geysermc.rainbow.mapping.AssetResolver;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
public class CustomTextureHolder extends TextureHolder {
|
||||||
|
private final Supplier<NativeImage> supplier;
|
||||||
|
|
||||||
|
public CustomTextureHolder(ResourceLocation location, Supplier<NativeImage> supplier) {
|
||||||
|
super(location);
|
||||||
|
this.supplier = supplier;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Optional<byte[]> load(AssetResolver assetResolver, ProblemReporter reporter) {
|
||||||
|
return RainbowIO.safeIO(() -> NativeImageUtil.writeToByteArray(supplier.get()));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
package org.geysermc.rainbow.mapping.texture;
|
||||||
|
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
import net.minecraft.util.ProblemReporter;
|
||||||
|
import org.geysermc.rainbow.mapping.AssetResolver;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
public class MissingTextureHolder extends TextureHolder {
|
||||||
|
|
||||||
|
public MissingTextureHolder(ResourceLocation location) {
|
||||||
|
super(location);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Optional<byte[]> load(AssetResolver assetResolver, ProblemReporter reporter) {
|
||||||
|
reportMissing(reporter);
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package org.geysermc.rainbow.mapping.geometry;
|
package org.geysermc.rainbow.mapping.texture;
|
||||||
|
|
||||||
import com.mojang.blaze3d.platform.NativeImage;
|
import com.mojang.blaze3d.platform.NativeImage;
|
||||||
import net.minecraft.Util;
|
import net.minecraft.Util;
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
package org.geysermc.rainbow.mapping.texture;
|
||||||
|
|
||||||
|
import com.mojang.blaze3d.platform.NativeImage;
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
import net.minecraft.util.ProblemReporter;
|
||||||
|
import org.geysermc.rainbow.mapping.AssetResolver;
|
||||||
|
import org.geysermc.rainbow.mapping.PackSerializer;
|
||||||
|
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
public abstract class TextureHolder {
|
||||||
|
protected final ResourceLocation location;
|
||||||
|
|
||||||
|
public TextureHolder(ResourceLocation location) {
|
||||||
|
this.location = location;
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract Optional<byte[]> load(AssetResolver assetResolver, ProblemReporter reporter);
|
||||||
|
|
||||||
|
public CompletableFuture<?> save(AssetResolver assetResolver, PackSerializer serializer, Path path, ProblemReporter reporter) {
|
||||||
|
return load(assetResolver, reporter)
|
||||||
|
.map(bytes -> serializer.saveTexture(bytes, path))
|
||||||
|
.orElse(CompletableFuture.completedFuture(null));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static TextureHolder createCustom(ResourceLocation location, Supplier<NativeImage> supplier) {
|
||||||
|
return new CustomTextureHolder(location, supplier);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static TextureHolder createBuiltIn(ResourceLocation location, ResourceLocation source) {
|
||||||
|
return new BuiltInTextureHolder(location, source);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static TextureHolder createBuiltIn(ResourceLocation location) {
|
||||||
|
return createBuiltIn(location, location);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static TextureHolder createNonExistent(ResourceLocation location) {
|
||||||
|
return new MissingTextureHolder(location);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static TextureHolder createCopy(TextureHolder original) {
|
||||||
|
return new CopyTextureHolder(original.location);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ResourceLocation location() {
|
||||||
|
return location;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void reportMissing(ProblemReporter reporter) {
|
||||||
|
reporter.report(() -> "missing texture for " + location + "; please provide it manually");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,8 +5,7 @@ import org.geysermc.rainbow.Rainbow;
|
|||||||
import org.geysermc.rainbow.mapping.PackSerializer;
|
import org.geysermc.rainbow.mapping.PackSerializer;
|
||||||
import org.geysermc.rainbow.mapping.attachable.AttachableMapper;
|
import org.geysermc.rainbow.mapping.attachable.AttachableMapper;
|
||||||
import org.geysermc.rainbow.mapping.geometry.BedrockGeometryContext;
|
import org.geysermc.rainbow.mapping.geometry.BedrockGeometryContext;
|
||||||
import org.geysermc.rainbow.mapping.geometry.StitchedGeometry;
|
import org.geysermc.rainbow.mapping.texture.TextureHolder;
|
||||||
import org.geysermc.rainbow.mapping.geometry.TextureHolder;
|
|
||||||
import org.geysermc.rainbow.pack.attachable.BedrockAttachable;
|
import org.geysermc.rainbow.pack.attachable.BedrockAttachable;
|
||||||
|
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
@@ -15,26 +14,19 @@ import java.util.List;
|
|||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import java.util.function.Supplier;
|
|
||||||
|
|
||||||
public record BedrockItem(ResourceLocation identifier, String textureName, BedrockGeometryContext geometryContext, AttachableMapper.AttachableCreator attachableCreator) {
|
public record BedrockItem(ResourceLocation identifier, String textureName, BedrockGeometryContext geometryContext, AttachableMapper.AttachableCreator attachableCreator) {
|
||||||
|
|
||||||
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) {
|
||||||
|
List<TextureHolder> attachableTextures = new ArrayList<>();
|
||||||
|
Optional<BedrockAttachable> createdAttachable = attachableCreator.create(identifier, attachableTextures::add);
|
||||||
return CompletableFuture.allOf(
|
return CompletableFuture.allOf(
|
||||||
textureSaver.apply(geometryContext.icon()),
|
textureSaver.apply(geometryContext.icon()),
|
||||||
CompletableFuture.supplyAsync(() -> geometryContext.geometry().map(Supplier::get))
|
createdAttachable.map(attachable -> attachable.save(serializer, attachableDirectory)).orElse(noop()),
|
||||||
.thenCompose(stitchedGeometry -> {
|
CompletableFuture.allOf(attachableTextures.stream().map(textureSaver).toArray(CompletableFuture[]::new)),
|
||||||
List<TextureHolder> attachableTextures = new ArrayList<>();
|
geometryContext.geometry().map(geometry -> geometry.save(serializer, geometryDirectory, textureSaver)).orElse(noop()),
|
||||||
Optional<BedrockAttachable> createdAttachable = attachableCreator.create(identifier, stitchedGeometry, attachableTextures::add);
|
geometryContext.animation().map(context -> context.animation().save(serializer, animationDirectory, Rainbow.safeResourceLocation(identifier))).orElse(noop())
|
||||||
return CompletableFuture.allOf(
|
|
||||||
createdAttachable.map(attachable -> attachable.save(serializer, attachableDirectory)).orElse(noop()),
|
|
||||||
CompletableFuture.allOf(attachableTextures.stream().map(textureSaver).toArray(CompletableFuture[]::new)),
|
|
||||||
stitchedGeometry.map(StitchedGeometry::geometry).map(geometry -> geometry.save(serializer, geometryDirectory)).orElse(noop()),
|
|
||||||
stitchedGeometry.map(StitchedGeometry::stitchedTextures).map(textureSaver).orElse(noop()),
|
|
||||||
geometryContext.animation().map(context -> context.animation().save(serializer, animationDirectory, Rainbow.safeResourceLocation(identifier))).orElse(noop())
|
|
||||||
);
|
|
||||||
})
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ 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.definition.GeyserMappings;
|
import org.geysermc.rainbow.definition.GeyserMappings;
|
||||||
import org.geysermc.rainbow.mapping.geometry.TextureHolder;
|
import org.geysermc.rainbow.mapping.texture.TextureHolder;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
@@ -127,9 +127,7 @@ public class BedrockPack {
|
|||||||
|
|
||||||
Function<TextureHolder, CompletableFuture<?>> textureSaver = texture -> {
|
Function<TextureHolder, CompletableFuture<?>> textureSaver = texture -> {
|
||||||
ResourceLocation textureLocation = Rainbow.decorateTextureLocation(texture.location());
|
ResourceLocation textureLocation = Rainbow.decorateTextureLocation(texture.location());
|
||||||
return texture.load(context.assetResolver(), reporter)
|
return texture.save(context.assetResolver(), serializer, paths.packRoot().resolve(textureLocation.getPath()), reporter);
|
||||||
.map(bytes -> serializer.saveTexture(bytes, paths.packRoot().resolve(textureLocation.getPath())))
|
|
||||||
.orElse(CompletableFuture.completedFuture(null));
|
|
||||||
};
|
};
|
||||||
|
|
||||||
for (BedrockItem item : bedrockItems) {
|
for (BedrockItem item : bedrockItems) {
|
||||||
|
|||||||
@@ -12,9 +12,9 @@ import net.minecraft.world.entity.EquipmentSlot;
|
|||||||
import org.geysermc.rainbow.PackConstants;
|
import org.geysermc.rainbow.PackConstants;
|
||||||
import org.geysermc.rainbow.Rainbow;
|
import org.geysermc.rainbow.Rainbow;
|
||||||
import org.geysermc.rainbow.mapping.PackSerializer;
|
import org.geysermc.rainbow.mapping.PackSerializer;
|
||||||
|
import org.geysermc.rainbow.mapping.geometry.MappedGeometry;
|
||||||
import org.geysermc.rainbow.pack.BedrockTextures;
|
import org.geysermc.rainbow.pack.BedrockTextures;
|
||||||
import org.geysermc.rainbow.pack.BedrockVersion;
|
import org.geysermc.rainbow.pack.BedrockVersion;
|
||||||
import org.geysermc.rainbow.pack.geometry.BedrockGeometry;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
@@ -62,13 +62,13 @@ public record BedrockAttachable(BedrockVersion formatVersion, AttachableInfo inf
|
|||||||
.withRenderController(VanillaRenderControllers.ARMOR);
|
.withRenderController(VanillaRenderControllers.ARMOR);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static BedrockAttachable.Builder geometry(ResourceLocation identifier, BedrockGeometry.GeometryDefinition geometry, String texture) {
|
public static BedrockAttachable.Builder geometry(ResourceLocation identifier, MappedGeometry geometry) {
|
||||||
return builder(identifier)
|
return builder(identifier)
|
||||||
.withMaterial(DisplaySlot.DEFAULT, VanillaMaterials.ENTITY)
|
.withMaterial(DisplaySlot.DEFAULT, VanillaMaterials.ENTITY)
|
||||||
.withMaterial(DisplaySlot.ENCHANTED, VanillaMaterials.ENTITY_ALPHATEST_GLINT)
|
.withMaterial(DisplaySlot.ENCHANTED, VanillaMaterials.ENTITY_ALPHATEST_GLINT)
|
||||||
.withTexture(DisplaySlot.DEFAULT, texture)
|
.withTexture(DisplaySlot.DEFAULT, geometry.stitchedTextures().location().getPath())
|
||||||
.withTexture(DisplaySlot.ENCHANTED, VanillaTextures.ENCHANTED_ITEM_GLINT)
|
.withTexture(DisplaySlot.ENCHANTED, VanillaTextures.ENCHANTED_ITEM_GLINT)
|
||||||
.withGeometry(DisplaySlot.DEFAULT, geometry.info().identifier())
|
.withGeometry(DisplaySlot.DEFAULT, geometry.identifier())
|
||||||
.withRenderController(VanillaRenderControllers.ITEM_DEFAULT);
|
.withRenderController(VanillaRenderControllers.ITEM_DEFAULT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user