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

Work on improving model generation/stitching of multiple textures

This commit is contained in:
Eclipse
2025-10-15 16:41:02 +00:00
parent 94f73dbd06
commit 0ccc78e827
24 changed files with 438 additions and 130 deletions

View File

@@ -8,20 +8,25 @@ import net.minecraft.client.resources.model.ModelManager;
import net.minecraft.client.resources.model.ResolvedModel;
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.packs.resources.ResourceManager;
import net.minecraft.world.item.equipment.EquipmentAsset;
import org.geysermc.rainbow.client.accessor.ResolvedModelAccessor;
import org.geysermc.rainbow.client.mixin.EntityRenderDispatcherAccessor;
import org.geysermc.rainbow.mapping.AssetResolver;
import java.io.IOException;
import java.io.InputStream;
import java.util.Optional;
public class MinecraftAssetResolver implements AssetResolver {
private final ModelManager modelManager;
private final EquipmentAssetManager equipmentAssetManager;
private final ResourceManager resourceManager;
public MinecraftAssetResolver(Minecraft minecraft) {
modelManager = minecraft.getModelManager();
equipmentAssetManager = ((EntityRenderDispatcherAccessor) minecraft.getEntityRenderDispatcher()).getEquipmentAssets();
resourceManager = minecraft.getResourceManager();
}
@Override
@@ -38,4 +43,9 @@ public class MinecraftAssetResolver implements AssetResolver {
public Optional<EquipmentClientInfo> getEquipmentInfo(ResourceKey<EquipmentAsset> key) {
return Optional.of(equipmentAssetManager.get(key));
}
@Override
public InputStream openAsset(ResourceLocation location) throws IOException {
return resourceManager.open(location);
}
}

View File

@@ -16,7 +16,6 @@ import org.geysermc.rainbow.mapping.PackSerializer;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.file.Path;
import java.util.Objects;
@@ -24,11 +23,9 @@ import java.util.concurrent.CompletableFuture;
public class MinecraftPackSerializer implements PackSerializer {
private final HolderLookup.Provider registries;
private final ResourceManager resourceManager;
public MinecraftPackSerializer(Minecraft minecraft) {
registries = Objects.requireNonNull(minecraft.level).registryAccess();
resourceManager = minecraft.getResourceManager();
}
@Override
@@ -44,13 +41,12 @@ public class MinecraftPackSerializer implements PackSerializer {
}
@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)) {
try {
CodecUtil.ensureDirectoryExists(path.getParent());
try (OutputStream outputTexture = new FileOutputStream(path.toFile())) {
IOUtils.copy(inputTexture, outputTexture);
outputTexture.write(texture);
}
} catch (IOException exception) {
// TODO log

View File

@@ -88,8 +88,8 @@ public final class PackManager {
Set<BedrockItem> bedrockItems = pack.getBedrockItems();
long attachables = bedrockItems.stream().filter(item -> item.attachable().isPresent()).count();
long geometries = bedrockItems.stream().filter(item -> item.geometry().isPresent()).count();
long animations = bedrockItems.stream().filter(item -> item.animation().isPresent()).count();
long geometries = bedrockItems.stream().filter(item -> item.geometry().geometry().isPresent()).count();
long animations = bedrockItems.stream().filter(item -> item.geometry().animation().isPresent()).count();
return """
-- PACK GENERATION REPORT --

View File

@@ -1,14 +1,33 @@
package org.geysermc.rainbow.client;
import com.mojang.blaze3d.platform.NativeImage;
import com.mojang.brigadier.arguments.StringArgumentType;
import net.fabricmc.api.ClientModInitializer;
import net.fabricmc.fabric.api.client.command.v2.ClientCommandManager;
import net.fabricmc.fabric.api.client.command.v2.ClientCommandRegistrationCallback;
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents;
import net.fabricmc.fabric.api.command.v2.ArgumentTypeRegistry;
import net.fabricmc.loader.api.FabricLoader;
import net.minecraft.Util;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.texture.SpriteContents;
import net.minecraft.client.renderer.texture.SpriteLoader;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import net.minecraft.client.resources.metadata.animation.FrameSize;
import net.minecraft.commands.synchronization.SingletonArgumentInfo;
import net.minecraft.data.AtlasIds;
import net.minecraft.resources.ResourceLocation;
import org.geysermc.rainbow.Rainbow;
import org.geysermc.rainbow.client.command.CommandSuggestionsArgumentType;
import org.geysermc.rainbow.client.command.PackGeneratorCommand;
import org.geysermc.rainbow.client.mapper.PackMapper;
import org.geysermc.rainbow.mixin.SpriteContentsAccessor;
import org.geysermc.rainbow.mixin.SpriteLoaderAccessor;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.List;
public class RainbowClient implements ClientModInitializer {
@@ -18,10 +37,53 @@ public class RainbowClient implements ClientModInitializer {
// TODO export language overrides
@Override
public void onInitializeClient() {
ClientCommandRegistrationCallback.EVENT.register((dispatcher, buildContext) -> PackGeneratorCommand.register(dispatcher, packManager, packMapper));
ClientCommandRegistrationCallback.EVENT.register((dispatcher, buildContext) -> {
PackGeneratorCommand.register(dispatcher, packManager, packMapper);
dispatcher.register(
ClientCommandManager.literal("DEBUGTEST")
.then(ClientCommandManager.argument("textures", StringArgumentType.greedyString())
.executes(context -> {
SpriteLoader spriteLoader = new SpriteLoader(AtlasIds.BLOCKS, 1024, 16, 16);
List<SpriteContents> sprites = Arrays.stream(StringArgumentType.getString(context, "textures").split(" "))
.map(ResourceLocation::tryParse)
.map(RainbowClient::readSpriteContents)
.toList();
SpriteLoader.Preparations preparations = ((SpriteLoaderAccessor) spriteLoader).invokeStitch(sprites, 0, Util.backgroundExecutor());
try (NativeImage stitched = stitchTextureAtlas(preparations)) {
stitched.writeToFile(FabricLoader.getInstance().getGameDir().resolve("test.png"));
} catch (IOException exception) {
throw new RuntimeException(exception);
}
return 0;
})
)
);
});
ClientTickEvents.START_CLIENT_TICK.register(packMapper::tick);
ArgumentTypeRegistry.registerArgumentType(Rainbow.getModdedLocation("command_suggestions"),
CommandSuggestionsArgumentType.class, SingletonArgumentInfo.contextFree(CommandSuggestionsArgumentType::new));
}
private static SpriteContents readSpriteContents(ResourceLocation location) {
try (InputStream textureStream = Minecraft.getInstance().getResourceManager().open(location.withPath(path -> "textures/" + path + ".png"))) {
NativeImage texture = NativeImage.read(textureStream);
return new SpriteContents(location, new FrameSize(texture.getWidth(), texture.getHeight()), texture);
} catch (IOException exception) {
throw new RuntimeException(exception);
}
}
private static NativeImage stitchTextureAtlas(SpriteLoader.Preparations preparations) {
NativeImage stitched = new NativeImage(preparations.width(), preparations.height(), false);
for (TextureAtlasSprite sprite : preparations.regions().values()) {
try (SpriteContents contents = sprite.contents()) {
((SpriteContentsAccessor) contents).getOriginalImage().copyRect(stitched, 0, 0,
sprite.getX(), sprite.getY(), contents.width(), contents.height(), false, false);
}
}
return stitched;
}
}