From 7f67a40c13fc424608dd6a67d9b17929b8f19574 Mon Sep 17 00:00:00 2001 From: Eclipse Date: Thu, 16 Oct 2025 07:38:59 +0000 Subject: [PATCH] Sort Geyser mappings, async texture stitching, fix datagen --- .../geysermc/rainbow/client/PackManager.java | 12 +++---- .../rainbow/datagen/RainbowModelProvider.java | 17 +++++---- .../definition/GeyserGroupDefinition.java | 29 ++++++++++++++- .../definition/GeyserItemDefinition.java | 9 +++++ .../rainbow/definition/GeyserMapping.java | 2 +- .../rainbow/definition/GeyserMappings.java | 6 +++- .../rainbow/mapping/BedrockItemMapper.java | 5 ++- .../geysermc/rainbow/mapping/PackContext.java | 7 +--- .../mapping/attachable/AttachableMapper.java | 14 +++++--- .../geometry/BedrockGeometryContext.java | 33 ++++++++++------- .../mapping/geometry/GeometryMapper.java | 7 ++-- .../mapping/geometry/StitchedTextures.java | 14 ++++---- .../mapping/geometry/TextureHolder.java | 4 +++ .../geysermc/rainbow/pack/BedrockItem.java | 35 +++++++++++++------ .../geysermc/rainbow/pack/BedrockPack.java | 22 +++++------- .../rainbow/pack/BedrockTextures.java | 2 +- .../pack/geometry/BedrockGeometry.java | 2 ++ 17 files changed, 145 insertions(+), 75 deletions(-) diff --git a/client/src/main/java/org/geysermc/rainbow/client/PackManager.java b/client/src/main/java/org/geysermc/rainbow/client/PackManager.java index f6d9a52..f3e92ab 100644 --- a/client/src/main/java/org/geysermc/rainbow/client/PackManager.java +++ b/client/src/main/java/org/geysermc/rainbow/client/PackManager.java @@ -87,9 +87,9 @@ public final class PackManager { } Set bedrockItems = pack.getBedrockItems(); - long attachables = bedrockItems.stream().filter(item -> item.attachable().isPresent()).count(); - long geometries = bedrockItems.stream().filter(item -> item.geometry().geometry().isPresent()).count(); - long animations = bedrockItems.stream().filter(item -> item.geometry().animation().isPresent()).count(); + //long attachables = bedrockItems.stream().filter(item -> item.attachableCreator().isPresent()).count(); + long geometries = bedrockItems.stream().filter(item -> item.geometryContext().geometry().isPresent()).count(); + long animations = bedrockItems.stream().filter(item -> item.geometryContext().animation().isPresent()).count(); return """ -- PACK GENERATION REPORT -- @@ -98,15 +98,15 @@ public final class PackManager { Generated pack: %s Mappings written: %d Item texture atlas size: %d -Attachables tried to export: %d +Attachables tried to export: FIXME Geometry files tried to export: %d Animations tried to export: %d -Textures tried to export: %d +Textures tried to export: FIXME -- MAPPING TREE REPORT -- %s """.formatted(randomSummaryComment(), pack.name(), pack.getMappings(), pack.getItemTextureAtlasSize(), - attachables, geometries, animations, pack.getAdditionalExportedTextures(), problems); + geometries, animations, problems); } private static String randomSummaryComment() { diff --git a/datagen/src/main/java/org/geysermc/rainbow/datagen/RainbowModelProvider.java b/datagen/src/main/java/org/geysermc/rainbow/datagen/RainbowModelProvider.java index 0562a3c..c807130 100644 --- a/datagen/src/main/java/org/geysermc/rainbow/datagen/RainbowModelProvider.java +++ b/datagen/src/main/java/org/geysermc/rainbow/datagen/RainbowModelProvider.java @@ -70,7 +70,7 @@ public abstract class RainbowModelProvider extends FabricModelProvider { CompletableFuture bedrockPack = ClientPackLoader.openClientResources() .thenCompose(resourceManager -> registries.thenApply(registries -> { try (resourceManager) { - BedrockPack pack = createBedrockPack(outputRoot, new Serializer(output, resourceManager, registries), + BedrockPack pack = createBedrockPack(outputRoot, new Serializer(output, registries), new DatagenResolver(resourceManager, equipmentInfos, itemInfos, models)).build(); for (Item item : itemInfos.keySet()) { @@ -105,7 +105,7 @@ public abstract class RainbowModelProvider extends FabricModelProvider { this.models = models; } - private record Serializer(CachedOutput output, ResourceManager resourceManager, HolderLookup.Provider registries) implements PackSerializer { + private record Serializer(CachedOutput output, HolderLookup.Provider registries) implements PackSerializer { @Override public CompletableFuture saveJson(Codec codec, T object, Path path) { @@ -113,12 +113,10 @@ public abstract class RainbowModelProvider extends FabricModelProvider { } @Override - public CompletableFuture saveTexture(ResourceLocation texture, Path path) { + public CompletableFuture saveTexture(byte[] texture, Path path) { return CompletableFuture.runAsync(() -> { - ResourceLocation texturePath = texture.withPath(p -> "textures/" + p + ".png"); - try (InputStream inputTexture = resourceManager.open(texturePath)) { - byte[] textureBytes = inputTexture.readAllBytes(); - output.writeIfNeeded(path, textureBytes, HashCode.fromBytes(textureBytes)); + try { + output.writeIfNeeded(path, texture, HashCode.fromBytes(texture)); } catch (IOException exception) { LOGGER.error("Failed to save file to {}", path, exception); } @@ -181,5 +179,10 @@ public abstract class RainbowModelProvider extends FabricModelProvider { public Optional getEquipmentInfo(ResourceKey key) { return Optional.ofNullable(equipmentInfos.get(key)); } + + @Override + public InputStream openAsset(ResourceLocation location) throws IOException { + return resourceManager.open(location); + } } } diff --git a/rainbow/src/main/java/org/geysermc/rainbow/definition/GeyserGroupDefinition.java b/rainbow/src/main/java/org/geysermc/rainbow/definition/GeyserGroupDefinition.java index 20e1aa7..9f8a611 100644 --- a/rainbow/src/main/java/org/geysermc/rainbow/definition/GeyserGroupDefinition.java +++ b/rainbow/src/main/java/org/geysermc/rainbow/definition/GeyserGroupDefinition.java @@ -3,9 +3,12 @@ package org.geysermc.rainbow.definition; import com.mojang.serialization.MapCodec; import com.mojang.serialization.codecs.RecordCodecBuilder; import net.minecraft.resources.ResourceLocation; +import org.jetbrains.annotations.NotNull; +import java.util.Comparator; import java.util.List; import java.util.Optional; +import java.util.function.Function; import java.util.stream.Stream; public record GeyserGroupDefinition(Optional model, List definitions) implements GeyserMapping { @@ -18,7 +21,9 @@ public record GeyserGroupDefinition(Optional model, List model) { @@ -53,4 +58,26 @@ public record GeyserGroupDefinition(Optional model, List otherModel, List otherDefinitions)) { + if (model.isPresent() && otherModel.isPresent()) { + return model.get().compareTo(otherModel.get()); + } else if (model.isPresent()) { + return 1; // Groups with models are always greater than groups without + } else if (otherModel.isPresent()) { + return -1; + } else if (definitions.isEmpty() && otherDefinitions.isEmpty()) { + return 0; + } else if (definitions.isEmpty()) { + return -1; // Groups with definitions are always greater than groups without + } else if (otherDefinitions.isEmpty()) { + return 1; + } + // Compare the first definition as a last resort + return definitions.getFirst().compareTo(otherDefinitions.getFirst()); + } + return 1; // Groups are always greater than individual mappings + } } diff --git a/rainbow/src/main/java/org/geysermc/rainbow/definition/GeyserItemDefinition.java b/rainbow/src/main/java/org/geysermc/rainbow/definition/GeyserItemDefinition.java index d09894a..1d3fe96 100644 --- a/rainbow/src/main/java/org/geysermc/rainbow/definition/GeyserItemDefinition.java +++ b/rainbow/src/main/java/org/geysermc/rainbow/definition/GeyserItemDefinition.java @@ -1,6 +1,7 @@ package org.geysermc.rainbow.definition; import net.minecraft.resources.ResourceLocation; +import org.jetbrains.annotations.NotNull; import java.util.Optional; @@ -9,4 +10,12 @@ public interface GeyserItemDefinition extends GeyserMapping { GeyserBaseDefinition base(); boolean conflictsWith(Optional parentModel, GeyserItemDefinition other); + + @Override + default int compareTo(@NotNull GeyserMapping other) { + if (other instanceof GeyserItemDefinition itemDefinition) { + return base().bedrockIdentifier().compareTo(itemDefinition.base().bedrockIdentifier()); + } + return -1; // Groups are always greater than individual mappings + } } diff --git a/rainbow/src/main/java/org/geysermc/rainbow/definition/GeyserMapping.java b/rainbow/src/main/java/org/geysermc/rainbow/definition/GeyserMapping.java index d3cc862..a46fd1c 100644 --- a/rainbow/src/main/java/org/geysermc/rainbow/definition/GeyserMapping.java +++ b/rainbow/src/main/java/org/geysermc/rainbow/definition/GeyserMapping.java @@ -6,7 +6,7 @@ import com.mojang.serialization.MapCodec; import net.minecraft.util.StringRepresentable; import org.jetbrains.annotations.NotNull; -public interface GeyserMapping { +public interface GeyserMapping extends Comparable { Codec CODEC = Codec.lazyInitialized(() -> Type.CODEC.dispatch(GeyserMapping::type, Type::codec)); // Not perfect since we're not checking single definitions in groups without a model... but good enough diff --git a/rainbow/src/main/java/org/geysermc/rainbow/definition/GeyserMappings.java b/rainbow/src/main/java/org/geysermc/rainbow/definition/GeyserMappings.java index 5a887a2..40e789e 100644 --- a/rainbow/src/main/java/org/geysermc/rainbow/definition/GeyserMappings.java +++ b/rainbow/src/main/java/org/geysermc/rainbow/definition/GeyserMappings.java @@ -11,6 +11,7 @@ import org.geysermc.rainbow.CodecUtil; import java.util.ArrayList; import java.util.Collection; +import java.util.Comparator; import java.util.List; import java.util.Map; import java.util.Optional; @@ -26,7 +27,10 @@ public class GeyserMappings { ).apply(instance, (format, mappings) -> new GeyserMappings(mappings)) ); - private final Multimap, GeyserMapping> mappings = MultimapBuilder.hashKeys().hashSetValues().build(); + private final Multimap, GeyserMapping> mappings = MultimapBuilder + .hashKeys() + .treeSetValues(Comparator.comparing(mapping -> mapping)) + .build(); public GeyserMappings() {} diff --git a/rainbow/src/main/java/org/geysermc/rainbow/mapping/BedrockItemMapper.java b/rainbow/src/main/java/org/geysermc/rainbow/mapping/BedrockItemMapper.java index 02e12f2..9690619 100644 --- a/rainbow/src/main/java/org/geysermc/rainbow/mapping/BedrockItemMapper.java +++ b/rainbow/src/main/java/org/geysermc/rainbow/mapping/BedrockItemMapper.java @@ -123,7 +123,7 @@ public class BedrockItemMapper { bedrockIdentifier = itemModelLocation; } - BedrockGeometryContext geometry = BedrockGeometryContext.create(itemModel); + BedrockGeometryContext geometry = BedrockGeometryContext.create(bedrockIdentifier, itemModel, context.packContext); if (context.packContext.reportSuccesses()) { // Not a problem, but just report to get the model printed in the report file context.report("creating mapping for block model " + itemModelLocation); @@ -238,9 +238,8 @@ public class BedrockItemMapper { return; } - // TODO move attachable mapping somewhere else for cleaner code? packContext.itemConsumer().accept(new BedrockItem(bedrockIdentifier, base.textureName(), geometry, - AttachableMapper.mapItem(stack.getComponentsPatch(), bedrockIdentifier, geometry, packContext.assetResolver(), packContext.additionalTextureConsumer()))); + AttachableMapper.mapItem(packContext.assetResolver(), geometry, stack.getComponentsPatch()))); } public void report(String problem) { diff --git a/rainbow/src/main/java/org/geysermc/rainbow/mapping/PackContext.java b/rainbow/src/main/java/org/geysermc/rainbow/mapping/PackContext.java index d581eb3..d632638 100644 --- a/rainbow/src/main/java/org/geysermc/rainbow/mapping/PackContext.java +++ b/rainbow/src/main/java/org/geysermc/rainbow/mapping/PackContext.java @@ -2,12 +2,7 @@ package org.geysermc.rainbow.mapping; import org.geysermc.rainbow.mapping.geometry.GeometryRenderer; import org.geysermc.rainbow.definition.GeyserMappings; -import org.geysermc.rainbow.mapping.geometry.TextureHolder; import org.geysermc.rainbow.pack.PackPaths; -import java.util.function.Consumer; - public record PackContext(GeyserMappings mappings, PackPaths paths, BedrockItemConsumer itemConsumer, AssetResolver assetResolver, - GeometryRenderer geometryRenderer, Consumer additionalTextureConsumer, - boolean reportSuccesses) { -} + GeometryRenderer geometryRenderer, boolean reportSuccesses) {} diff --git a/rainbow/src/main/java/org/geysermc/rainbow/mapping/attachable/AttachableMapper.java b/rainbow/src/main/java/org/geysermc/rainbow/mapping/attachable/AttachableMapper.java index c5fc157..4a24acb 100644 --- a/rainbow/src/main/java/org/geysermc/rainbow/mapping/attachable/AttachableMapper.java +++ b/rainbow/src/main/java/org/geysermc/rainbow/mapping/attachable/AttachableMapper.java @@ -19,12 +19,12 @@ import java.util.function.Consumer; public class AttachableMapper { - public static Optional mapItem(DataComponentPatch components, ResourceLocation bedrockIdentifier, BedrockGeometryContext geometryContext, - AssetResolver assetResolver, Consumer textureConsumer) { + public static AttachableCreator mapItem(AssetResolver assetResolver, BedrockGeometryContext geometryContext, DataComponentPatch components) { // Crazy optional statement // Unfortunately we can't have both equippables and custom models, so we prefer the latter :( - return geometryContext.geometry() - .map(geometry -> BedrockAttachable.geometry(bedrockIdentifier, geometry.definitions().getFirst(), geometryContext.texture().location().getPath())) + return (bedrockIdentifier, stitchedGeometry, textureConsumer) -> stitchedGeometry + .map(BedrockGeometryContext.StitchedGeometry::geometry) + .map(geometry -> BedrockAttachable.geometry(bedrockIdentifier, geometry.definitions().getFirst(), geometryContext.icon().location().getPath())) .or(() -> Optional.ofNullable(components.get(DataComponents.EQUIPPABLE)) .flatMap(optional -> (Optional) optional) .flatMap(equippable -> equippable.assetId().flatMap(assetResolver::getEquipmentInfo).map(info -> Pair.of(equippable.slot(), info))) @@ -55,4 +55,10 @@ public class AttachableMapper { private static ResourceLocation getTexture(List info, EquipmentClientInfo.LayerType layer) { return info.getFirst().textureId().withPath(path -> "entity/equipment/" + layer.getSerializedName() + "/" + path); } + + @FunctionalInterface + public interface AttachableCreator { + + Optional create(ResourceLocation bedrockIdentifier, Optional geometry, Consumer textureConsumer); + } } diff --git a/rainbow/src/main/java/org/geysermc/rainbow/mapping/geometry/BedrockGeometryContext.java b/rainbow/src/main/java/org/geysermc/rainbow/mapping/geometry/BedrockGeometryContext.java index 026a606..56891f6 100644 --- a/rainbow/src/main/java/org/geysermc/rainbow/mapping/geometry/BedrockGeometryContext.java +++ b/rainbow/src/main/java/org/geysermc/rainbow/mapping/geometry/BedrockGeometryContext.java @@ -1,53 +1,62 @@ 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.resources.ResourceLocation; import org.geysermc.rainbow.Rainbow; +import org.geysermc.rainbow.mapping.PackContext; import org.geysermc.rainbow.mapping.animation.AnimationMapper; import org.geysermc.rainbow.mapping.animation.BedrockAnimationContext; import org.geysermc.rainbow.pack.geometry.BedrockGeometry; import java.util.List; import java.util.Optional; +import java.util.function.Supplier; import java.util.stream.Stream; -public record BedrockGeometryContext(Optional geometry, Optional animation, TextureHolder texture, +public record BedrockGeometryContext(Optional> geometry, + Optional animation, TextureHolder icon, boolean handheld) { private static final List HANDHELD_MODELS = Stream.of("item/handheld", "item/handheld_rod", "item/handheld_mace") .map(ResourceLocation::withDefaultNamespace) .toList(); - public static BedrockGeometryContext create(ResolvedModel model) { + public static BedrockGeometryContext create(ResourceLocation bedrockIdentifier, ResolvedModel model, PackContext context) { ResolvedModel parentModel = model.parent(); // debugName() returns the resource location of the model as a string boolean handheld = parentModel != null && HANDHELD_MODELS.contains(ResourceLocation.parse(parentModel.debugName())); TextureSlots textures = model.getTopTextureSlots(); Material layer0Texture = textures.getMaterial("layer0"); - Optional geometry; + Optional> geometry; Optional animation; - TextureHolder texture; + TextureHolder icon; if (layer0Texture != null) { geometry = Optional.empty(); animation = Optional.empty(); - texture = new TextureHolder(layer0Texture.texture()); + icon = new TextureHolder(layer0Texture.texture()); } else { // 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) ResourceLocation modelLocation = ResourceLocation.parse(model.debugName()); - String safeIdentifier = Rainbow.fileSafeResourceLocation(modelLocation); - - StitchedTextures stitchedTextures = StitchedTextures.stitchModelTextures(textures); - geometry = GeometryMapper.mapGeometry(safeIdentifier, "bone", model, stitchedTextures); + String safeIdentifier = Rainbow.fileSafeResourceLocation(bedrockIdentifier); + + geometry = Optional.of(Suppliers.memoize(() -> { + StitchedTextures stitchedTextures = StitchedTextures.stitchModelTextures(textures, context); + BedrockGeometry mappedGeometry = GeometryMapper.mapGeometry(safeIdentifier, "bone", model, stitchedTextures); + return new StitchedGeometry(mappedGeometry, new TextureHolder(modelLocation.withSuffix("_stitched"), stitchedTextures.stitched())); + })); + animation = Optional.of(AnimationMapper.mapAnimation(safeIdentifier, "bone", model.getTopTransforms())); - texture = new TextureHolder(modelLocation.withSuffix("_stitched"), Optional.of(stitchedTextures.stitched())); - // TODO geometry rendering + icon = new TextureHolder(modelLocation); // TODO } - return new BedrockGeometryContext(geometry, animation, texture, handheld); + return new BedrockGeometryContext(geometry, animation, icon, handheld); } + + public record StitchedGeometry(BedrockGeometry geometry, TextureHolder stitchedTextures) {} } diff --git a/rainbow/src/main/java/org/geysermc/rainbow/mapping/geometry/GeometryMapper.java b/rainbow/src/main/java/org/geysermc/rainbow/mapping/geometry/GeometryMapper.java index a2794ba..8722caf 100644 --- a/rainbow/src/main/java/org/geysermc/rainbow/mapping/geometry/GeometryMapper.java +++ b/rainbow/src/main/java/org/geysermc/rainbow/mapping/geometry/GeometryMapper.java @@ -14,15 +14,14 @@ import org.joml.Vector3f; import org.joml.Vector3fc; import java.util.Map; -import java.util.Optional; public class GeometryMapper { private static final Vector3fc CENTRE_OFFSET = new Vector3f(8.0F, 0.0F, 8.0F); - public static Optional mapGeometry(String identifier, String boneName, ResolvedModel model, StitchedTextures textures) { + public static BedrockGeometry mapGeometry(String identifier, String boneName, ResolvedModel model, StitchedTextures textures) { UnbakedGeometry top = model.getTopGeometry(); if (top == UnbakedGeometry.EMPTY) { - return Optional.empty(); + return BedrockGeometry.EMPTY; } BedrockGeometry.Builder builder = BedrockGeometry.builder(identifier); @@ -54,7 +53,7 @@ public class GeometryMapper { // Bind to the bone of the current item slot bone.withBinding("q.item_slot_to_bone_name(context.item_slot)"); - return Optional.of(builder.withBone(bone).build()); + return builder.withBone(bone).build(); } private static BedrockGeometry.Cube.Builder mapBlockElement(BlockElement element, StitchedTextures textures) { diff --git a/rainbow/src/main/java/org/geysermc/rainbow/mapping/geometry/StitchedTextures.java b/rainbow/src/main/java/org/geysermc/rainbow/mapping/geometry/StitchedTextures.java index fab5938..eff4435 100644 --- a/rainbow/src/main/java/org/geysermc/rainbow/mapping/geometry/StitchedTextures.java +++ b/rainbow/src/main/java/org/geysermc/rainbow/mapping/geometry/StitchedTextures.java @@ -11,6 +11,8 @@ import net.minecraft.client.resources.metadata.animation.FrameSize; import net.minecraft.client.resources.model.Material; import net.minecraft.data.AtlasIds; import net.minecraft.resources.ResourceLocation; +import org.geysermc.rainbow.Rainbow; +import org.geysermc.rainbow.mapping.PackContext; import org.geysermc.rainbow.mixin.SpriteContentsAccessor; import org.geysermc.rainbow.mixin.SpriteLoaderAccessor; import org.geysermc.rainbow.mixin.TextureSlotsAccessor; @@ -33,9 +35,9 @@ public record StitchedTextures(Map sprites, Supplier return Optional.ofNullable(sprites.get(key)); } - public static StitchedTextures stitchModelTextures(TextureSlots textures) { + public static StitchedTextures stitchModelTextures(TextureSlots textures, PackContext context) { Map materials = ((TextureSlotsAccessor) textures).getResolvedValues(); - SpriteLoader.Preparations preparations = prepareStitching(materials.values().stream().map(Material::texture)); + SpriteLoader.Preparations preparations = prepareStitching(materials.values().stream().map(Material::texture), context); Map sprites = new HashMap<>(); for (Map.Entry material : materials.entrySet()) { @@ -44,19 +46,19 @@ public record StitchedTextures(Map sprites, Supplier return new StitchedTextures(Map.copyOf(sprites), () -> stitchTextureAtlas(preparations), preparations.width(), preparations.height()); } - private static SpriteLoader.Preparations prepareStitching(Stream textures) { + private static SpriteLoader.Preparations prepareStitching(Stream textures, PackContext context) { // Atlas ID doesn't matter much here, but BLOCKS is the most appropriate // Not sure if 1024 should be the max supported texture size, but it seems to work SpriteLoader spriteLoader = new SpriteLoader(AtlasIds.BLOCKS, 1024, 16, 16); - List sprites = textures.distinct().map(StitchedTextures::readSpriteContents).toList(); + List sprites = textures.distinct().map(texture -> readSpriteContents(texture, context)).toList(); return ((SpriteLoaderAccessor) spriteLoader).invokeStitch(sprites, 0, Util.backgroundExecutor()); } - private static SpriteContents readSpriteContents(ResourceLocation location) { + private static SpriteContents readSpriteContents(ResourceLocation location, PackContext context) { // TODO decorate path util // TODO don't use ResourceManager // TODO IO is on main thread here? - try (InputStream textureStream = Minecraft.getInstance().getResourceManager().open(location.withPath(path -> "textures/" + path + ".png"))) { + try (InputStream textureStream = context.assetResolver().openAsset(Rainbow.decorateTextureLocation(location))) { NativeImage texture = NativeImage.read(textureStream); return new SpriteContents(location, new FrameSize(texture.getWidth(), texture.getHeight()), texture); } catch (IOException exception) { diff --git a/rainbow/src/main/java/org/geysermc/rainbow/mapping/geometry/TextureHolder.java b/rainbow/src/main/java/org/geysermc/rainbow/mapping/geometry/TextureHolder.java index cef5482..10813ca 100644 --- a/rainbow/src/main/java/org/geysermc/rainbow/mapping/geometry/TextureHolder.java +++ b/rainbow/src/main/java/org/geysermc/rainbow/mapping/geometry/TextureHolder.java @@ -8,6 +8,10 @@ import java.util.function.Supplier; public record TextureHolder(ResourceLocation location, Optional> supplier) { + public TextureHolder(ResourceLocation location, Supplier supplier) { + this(location, Optional.of(supplier)); + } + public TextureHolder(ResourceLocation location) { this(location, Optional.empty()); } diff --git a/rainbow/src/main/java/org/geysermc/rainbow/pack/BedrockItem.java b/rainbow/src/main/java/org/geysermc/rainbow/pack/BedrockItem.java index 2742206..847b9e0 100644 --- a/rainbow/src/main/java/org/geysermc/rainbow/pack/BedrockItem.java +++ b/rainbow/src/main/java/org/geysermc/rainbow/pack/BedrockItem.java @@ -3,24 +3,39 @@ package org.geysermc.rainbow.pack; import net.minecraft.resources.ResourceLocation; import org.geysermc.rainbow.Rainbow; import org.geysermc.rainbow.mapping.PackSerializer; +import org.geysermc.rainbow.mapping.attachable.AttachableMapper; import org.geysermc.rainbow.mapping.geometry.BedrockGeometryContext; +import org.geysermc.rainbow.mapping.geometry.TextureHolder; import org.geysermc.rainbow.pack.attachable.BedrockAttachable; import java.nio.file.Path; +import java.util.ArrayList; import java.util.List; import java.util.Optional; import java.util.concurrent.CompletableFuture; -import java.util.stream.Stream; +import java.util.function.Function; +import java.util.function.Supplier; -public record BedrockItem(ResourceLocation identifier, String textureName, BedrockGeometryContext geometry, Optional attachable) { +public record BedrockItem(ResourceLocation identifier, String textureName, BedrockGeometryContext geometryContext, AttachableMapper.AttachableCreator attachableCreator) { - public List> save(PackSerializer serializer, Path attachableDirectory, Path geometryDirectory, Path animationDirectory) { - return Stream.concat( - attachable.stream().map(present -> present.save(serializer, attachableDirectory)), - Stream.concat( - geometry.geometry().stream().map(present -> present.save(serializer, geometryDirectory)), - geometry.animation().stream().map(context -> context.animation().save(serializer, animationDirectory, Rainbow.fileSafeResourceLocation(identifier))) - ) - ).toList(); + public CompletableFuture save(PackSerializer serializer, Path attachableDirectory, Path geometryDirectory, Path animationDirectory, + Function> textureSaver) { + return CompletableFuture.supplyAsync(() -> geometryContext.geometry().map(Supplier::get)) + .thenCompose(stitchedGeometry -> { + List attachableTextures = new ArrayList<>(); + Optional createdAttachable = attachableCreator.create(identifier, stitchedGeometry, attachableTextures::add); + return CompletableFuture.allOf( + textureSaver.apply(geometryContext.icon()), + createdAttachable.map(attachable -> attachable.save(serializer, attachableDirectory)).orElse(noop()), + CompletableFuture.allOf(attachableTextures.stream().map(textureSaver).toArray(CompletableFuture[]::new)), + stitchedGeometry.map(BedrockGeometryContext.StitchedGeometry::geometry).map(geometry -> geometry.save(serializer, geometryDirectory)).orElse(noop()), + stitchedGeometry.map(BedrockGeometryContext.StitchedGeometry::stitchedTextures).map(textureSaver).orElse(noop()), + geometryContext.animation().map(context -> context.animation().save(serializer, animationDirectory, Rainbow.fileSafeResourceLocation(identifier))).orElse(noop()) + ); + }); + } + + private static CompletableFuture noop() { + return CompletableFuture.completedFuture(null); } } diff --git a/rainbow/src/main/java/org/geysermc/rainbow/pack/BedrockPack.java b/rainbow/src/main/java/org/geysermc/rainbow/pack/BedrockPack.java index 5455726..a48b39d 100644 --- a/rainbow/src/main/java/org/geysermc/rainbow/pack/BedrockPack.java +++ b/rainbow/src/main/java/org/geysermc/rainbow/pack/BedrockPack.java @@ -35,6 +35,7 @@ import java.util.Set; import java.util.UUID; import java.util.concurrent.CompletableFuture; import java.util.concurrent.atomic.AtomicBoolean; +import java.util.function.Function; import java.util.function.UnaryOperator; public class BedrockPack { @@ -45,7 +46,6 @@ public class BedrockPack { private final BedrockTextures.Builder itemTextures = BedrockTextures.builder(); private final Set bedrockItems = new HashSet<>(); - private final Set texturesToExport = new HashSet<>(); private final Set modelsMapped = new HashSet<>(); private final IntSet customModelDataMapped = new IntOpenHashSet(); @@ -63,9 +63,8 @@ public class BedrockPack { // Not reading existing item mappings/texture atlas for now since that doesn't work all that well yet this.context = new PackContext(new GeyserMappings(), paths, item -> { itemTextures.withItemTexture(item); - texturesToExport.add(item.geometry().texture()); bedrockItems.add(item); - }, assetResolver, geometryRenderer, texturesToExport::add, reportSuccesses); + }, assetResolver, geometryRenderer, reportSuccesses); this.reporter = reporter; } @@ -128,13 +127,10 @@ public class BedrockPack { futures.add(serializer.saveJson(GeyserMappings.CODEC, context.mappings(), paths.mappings())); futures.add(serializer.saveJson(PackManifest.CODEC, manifest, paths.manifest())); futures.add(serializer.saveJson(BedrockTextureAtlas.CODEC, BedrockTextureAtlas.itemAtlas(name, itemTextures), paths.itemAtlas())); - for (BedrockItem item : bedrockItems) { - futures.addAll(item.save(serializer, paths.attachables(), paths.geometry(), paths.animation())); - } - for (TextureHolder texture : texturesToExport) { + Function> textureSaver = texture -> { ResourceLocation textureLocation = Rainbow.decorateTextureLocation(texture.location()); - texture.supplier() + return texture.supplier() .flatMap(image -> { try { return Optional.of(NativeImageUtil.writeToByteArray(image.get())); @@ -152,7 +148,11 @@ public class BedrockPack { } }) .map(bytes -> serializer.saveTexture(bytes, paths.packRoot().resolve(textureLocation.getPath()))) - .ifPresent(futures::add); + .orElse(CompletableFuture.completedFuture(null)); + }; + + for (BedrockItem item : bedrockItems) { + futures.add(item.save(serializer, paths.attachables(), paths.geometry(), paths.animation(), textureSaver)); } if (paths.zipOutput().isPresent()) { @@ -178,10 +178,6 @@ public class BedrockPack { return itemTextures.build().size(); } - public int getAdditionalExportedTextures() { - return texturesToExport.size(); - } - public ProblemReporter.Collector getReporter() { return reporter; } diff --git a/rainbow/src/main/java/org/geysermc/rainbow/pack/BedrockTextures.java b/rainbow/src/main/java/org/geysermc/rainbow/pack/BedrockTextures.java index c14aae6..2b2345f 100644 --- a/rainbow/src/main/java/org/geysermc/rainbow/pack/BedrockTextures.java +++ b/rainbow/src/main/java/org/geysermc/rainbow/pack/BedrockTextures.java @@ -33,7 +33,7 @@ public record BedrockTextures(Map textures) { private final Map textures = new HashMap<>(); public Builder withItemTexture(BedrockItem item) { - return withTexture(item.textureName(), TEXTURES_FOLDER + item.geometry().texture().location().getPath()); + return withTexture(item.textureName(), TEXTURES_FOLDER + item.geometryContext().icon().location().getPath()); } public Builder withTexture(String name, String texture) { diff --git a/rainbow/src/main/java/org/geysermc/rainbow/pack/geometry/BedrockGeometry.java b/rainbow/src/main/java/org/geysermc/rainbow/pack/geometry/BedrockGeometry.java index 29048fe..e63a430 100644 --- a/rainbow/src/main/java/org/geysermc/rainbow/pack/geometry/BedrockGeometry.java +++ b/rainbow/src/main/java/org/geysermc/rainbow/pack/geometry/BedrockGeometry.java @@ -32,6 +32,8 @@ public record BedrockGeometry(BedrockVersion formatVersion, List save(PackSerializer serializer, Path geometryDirectory) { return serializer.saveJson(CODEC, this, geometryDirectory.resolve(definitions.getFirst().info.identifier + ".geo.json")); }