From 9f058b596d99306bc5d36994aab1c9dd9592d1c6 Mon Sep 17 00:00:00 2001 From: REDxEYE Date: Sat, 25 Nov 2023 14:03:19 +0300 Subject: [PATCH 01/19] Map tile export --- .../viewer/model/dmf/DMFAttachmentNode.java | 4 + .../viewer/model/dmf/DMFCompositeModel.java | 4 + .../ui/data/viewer/model/dmf/DMFExporter.java | 241 +++++++++++++++++- .../ui/data/viewer/model/dmf/DMFInstance.java | 4 + .../ui/data/viewer/model/dmf/DMFLodModel.java | 4 + .../ui/data/viewer/model/dmf/DMFMapTile.java | 34 +++ .../ui/data/viewer/model/dmf/DMFModel.java | 4 + .../ui/data/viewer/model/dmf/DMFNode.java | 4 + .../ui/data/viewer/model/dmf/DMFNodeType.java | 1 + .../ui/data/viewer/texture/TextureViewer.java | 93 ++++--- .../texture/controls/ImageProvider.java | 2 + 11 files changed, 345 insertions(+), 50 deletions(-) create mode 100644 modules/decima-ext-model-exporter/src/main/java/com/shade/decima/ui/data/viewer/model/dmf/DMFMapTile.java diff --git a/modules/decima-ext-model-exporter/src/main/java/com/shade/decima/ui/data/viewer/model/dmf/DMFAttachmentNode.java b/modules/decima-ext-model-exporter/src/main/java/com/shade/decima/ui/data/viewer/model/dmf/DMFAttachmentNode.java index 5e2b1384f..b696cf6bd 100644 --- a/modules/decima-ext-model-exporter/src/main/java/com/shade/decima/ui/data/viewer/model/dmf/DMFAttachmentNode.java +++ b/modules/decima-ext-model-exporter/src/main/java/com/shade/decima/ui/data/viewer/model/dmf/DMFAttachmentNode.java @@ -10,4 +10,8 @@ public DMFAttachmentNode(@NotNull String name, @NotNull String boneName, @NotNul this.boneName = boneName; this.transform = transform; } + + public boolean isEmpty() { + return false; + } } diff --git a/modules/decima-ext-model-exporter/src/main/java/com/shade/decima/ui/data/viewer/model/dmf/DMFCompositeModel.java b/modules/decima-ext-model-exporter/src/main/java/com/shade/decima/ui/data/viewer/model/dmf/DMFCompositeModel.java index 37a509635..ec74ee35b 100644 --- a/modules/decima-ext-model-exporter/src/main/java/com/shade/decima/ui/data/viewer/model/dmf/DMFCompositeModel.java +++ b/modules/decima-ext-model-exporter/src/main/java/com/shade/decima/ui/data/viewer/model/dmf/DMFCompositeModel.java @@ -9,4 +9,8 @@ public DMFCompositeModel(@NotNull String name, int skeletonId) { super(name, DMFNodeType.SKINNED_MODEL); this.skeletonId = skeletonId; } + + public boolean isEmpty() { + return children.isEmpty(); + } } diff --git a/modules/decima-ext-model-exporter/src/main/java/com/shade/decima/ui/data/viewer/model/dmf/DMFExporter.java b/modules/decima-ext-model-exporter/src/main/java/com/shade/decima/ui/data/viewer/model/dmf/DMFExporter.java index 844931c0e..1621b6c58 100644 --- a/modules/decima-ext-model-exporter/src/main/java/com/shade/decima/ui/data/viewer/model/dmf/DMFExporter.java +++ b/modules/decima-ext-model-exporter/src/main/java/com/shade/decima/ui/data/viewer/model/dmf/DMFExporter.java @@ -26,23 +26,28 @@ import com.shade.decima.ui.data.viewer.texture.TextureViewer; import com.shade.decima.ui.data.viewer.texture.controls.ImageProvider; import com.shade.decima.ui.data.viewer.texture.exporter.TextureExporterPNG; +import com.shade.decima.ui.data.viewer.texture.exporter.TextureExporterTIFF; import com.shade.platform.model.runtime.ProgressMonitor; import com.shade.platform.model.util.IOUtils; import com.shade.util.NotNull; import com.shade.util.Nullable; +import org.joml.Vector2f; +import org.joml.Vector3f; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.awt.*; import java.io.*; import java.lang.reflect.Type; import java.nio.channels.Channels; import java.nio.file.Files; import java.nio.file.Path; +import java.util.List; import java.util.*; public class DMFExporter extends BaseModelExporter implements ModelExporter { private static final Logger log = LoggerFactory.getLogger(DMFExporter.class); - private static final Map SEMANTICS = Map.ofEntries( + private static final Map SEMANTICS_REMAP = Map.ofEntries( Map.entry("Pos", "POSITION"), Map.entry("TangentBFlip", "TANGENT"), Map.entry("Tangent", "TANGENT"), @@ -72,9 +77,12 @@ public class DMFExporter extends BaseModelExporter implements ModelExporter { private final Path output; private final Stack collectionStack = new Stack<>(); private final Map instances = new HashMap<>(); + private final Map tiles = new HashMap<>(); private int depth = 0; private DMFSceneFile scene; private DMFSkeleton masterSkeleton; + private int nodesRemoved = 0; + private int nodesVisited = 0; public DMFExporter(@NotNull Project project, @NotNull Set options, @NotNull Path output) { this.project = project; @@ -82,6 +90,29 @@ public DMFExporter(@NotNull Project project, @NotNull Set generateGrid(int gridSize) { + List vertices = new ArrayList<>(); + + float dx = 1.0f / gridSize; // Delta for UV mapping + float dy = 1.0f / gridSize; + + for (int i = 0; i <= gridSize; i++) { + for (int j = 0; j <= gridSize; j++) { + // Position + float x = (float) i / gridSize; + float y = 0; // Assuming grid is flat on the y-axis + float z = (float) j / gridSize; + + // UV Coordinates + float u = i * dx; + float v = j * dy; + + vertices.add(new Vertex(new Vector3f(x, y, z), new Vector2f(u, v))); + } + } + return vertices; + } + @Override public void export( @NotNull ProgressMonitor monitor, @@ -91,9 +122,109 @@ public void export( @NotNull Writer writer ) throws Exception { final var scene = export(monitor, core, object, resourceName); + nodesRemoved = 0; + nodesVisited = 0; + for (DMFNode node : scene.models) { + countNodes(node); + } + for (DMFNode node : scene.instances) { + countNodes(node); + } + for (DMFNode node : scene.instances) { + removeEmpty(node); + } + optimizeModelList(scene.instances); + optimizeInstances(scene); + for (DMFNode node : scene.instances) { + optimize(node); + } + optimizeInstances(scene); + optimizeModelList(scene.instances); + optimizeModelList(scene.models); + for (DMFNode node : scene.models) { + removeEmpty(node); + } + for (DMFNode node : scene.models) { + optimize(node); + } + optimizeModelList(scene.models); + for (Point point : tiles.keySet()) { + generateTileMesh(tiles.get(point)); + // System.out.printf("Tile: %f %f%n", point.getX(), point.getY()); + } + + System.out.printf("Removed %d(%f%%) nodes out of %d%n", nodesRemoved, ((float) nodesRemoved / nodesVisited) * 100.f, nodesVisited); gson.toJson(scene, scene.getClass(), createJsonWriter(writer)); } + private void generateTileMesh(TileData tileData) { + DMFMapTile mapTile = new DMFMapTile("Tile_%d_%d".formatted(tileData.gridCoordinate.x, tileData.gridCoordinate.y)); + mapTile.textures.putAll(tileData.textures); + mapTile.bboxMin = new float[]{tileData.bboxMin.x(), tileData.bboxMin.y(), tileData.bboxMin.z()}; + mapTile.bboxMax = new float[]{tileData.bboxMax.x(), tileData.bboxMax.y(), tileData.bboxMax.z()}; + mapTile.gridCoordinate = new int[]{tileData.gridCoordinate.x, tileData.gridCoordinate.y}; + scene.models.add(mapTile); + // Vector3f dimensions = tileData.bboxMax.sub(tileData.bboxMin).absolute(); + // scene.models.add() + } + + private void countNodes(DMFNode node) { + nodesVisited++; + node.children.forEach(this::countNodes); + } + + private void optimizeInstances(DMFSceneFile scene) { + for (int i = 0; i < scene.instances.size(); i++) { + DMFNode node = scene.instances.get(i); + optimize(node); + removeEmpty(node); + if (node.getClass() == DMFInstance.class) { + DMFNode instanceNode = scene.instances.get(((DMFInstance) node).instanceId); + if (instanceNode.children.isEmpty() && instanceNode.getClass() == DMFInstance.class) { + scene.instances.set(i, instanceNode); + } + } + } + } + + private void optimizeModelList(List nodes) { + for (int i = 0; i < nodes.size(); i++) { + DMFNode node = nodes.get(i); + if ((node.getClass() == DMFNode.class || node.getClass() == DMFModelGroup.class) && node.transform == null) { + if (node.children.size() == 1) { + nodesRemoved++; + nodes.set(i, node.children.get(0)); + } + } + } + } + + private void removeEmpty(DMFNode parentNode) { + parentNode.children.forEach(this::removeEmpty); + parentNode.children.removeIf(DMFNode::isEmpty); + } + + private void optimize(DMFNode parentNode) { + parentNode.children.forEach(this::optimize); + + List copyArray = new ArrayList<>(parentNode.children); + for (int i = 0; i < parentNode.children.size(); i++) { + DMFNode node = parentNode.children.get(i); + if (node.getClass() == DMFNode.class && node.transform == null) { + copyArray.remove(node); + nodesRemoved++; + copyArray.addAll(node.children); + } + if (node.getClass() == DMFModelGroup.class && node.transform == null) { + copyArray.remove(node); + nodesRemoved++; + copyArray.addAll(node.children); + } + } + parentNode.children.clear(); + parentNode.children.addAll(copyArray); + } + @NotNull private DMFSceneFile export( @NotNull ProgressMonitor monitor, @@ -239,7 +370,7 @@ private void exportTerrain( realElementSize = stride - offset; } final String elementType = element.str("Type"); - final String semantic = SEMANTICS.get(elementType); + final String semantic = SEMANTICS_REMAP.get(elementType); if (semantic == null) { continue; } @@ -474,6 +605,9 @@ private DMFNode toModel( case "MultiMeshResource" -> multiMeshResourceToModel(monitor, core, object, resourceName); case "RegularSkinnedMeshResource", "StaticMeshResource" -> regularSkinnedMeshResourceToModel(monitor, core, object, resourceName); + case "WorldDataTextureMap" -> worldDataTextureMapToModel(monitor, core, object, resourceName); + case "TerrainTileData" -> terrainTileDataToModel(monitor, core, object, resourceName); + default -> { log.info("{}Cannot export {}", "\t".repeat(depth), object.type().getTypeName()); yield null; @@ -483,6 +617,59 @@ private DMFNode toModel( return res; } + private DMFNode terrainTileDataToModel( + @NotNull ProgressMonitor monitor, + @NotNull CoreBinary core, + @NotNull RTTIObject object, + @NotNull String resourceName + ) throws IOException { + RTTIObject gridCoordinates = object.get("GridCoordinates"); + Point gridPoint = new Point(gridCoordinates.i32("X"), gridCoordinates.i32("Y")); + TileData tileData = tiles.getOrDefault(gridPoint, new TileData(gridPoint)); + RTTIObject bbox = object.get("BoundingBox"); + tileData.bboxMin = new Vector3f(bbox.obj("Min").f32("X"), bbox.obj("Min").f32("Y"), bbox.obj("Min").f32("Z")); + tileData.bboxMax = new Vector3f(bbox.obj("Max").f32("X"), bbox.obj("Max").f32("Y"), bbox.obj("Max").f32("Z")); + tiles.put(gridPoint, tileData); + return null; + } + + private DMFNode worldDataTextureMapToModel( + @NotNull ProgressMonitor monitor, + @NotNull CoreBinary core, + @NotNull RTTIObject object, + @NotNull String resourceName + ) throws IOException { + + RTTIObject gridCoordinates = object.get("GridCoordinates"); + Point gridPoint = new Point(gridCoordinates.i32("X"), gridCoordinates.i32("Y")); + TileData tileData = tiles.getOrDefault(gridPoint, new TileData(gridPoint)); + + final RTTIReference.FollowResult resultTextureRef = object.ref("ResultTexture").follow(project, core); + if (resultTextureRef != null) { + DMFTexture texture = exportTexture(resultTextureRef.object(), resourceName); + DMFMapTile.TileTextureInfo textureInfo = new DMFMapTile.TileTextureInfo(); + textureInfo.textureId = scene.textures.indexOf(texture); + for (RTTIReference entryRef : object.refs("Entries")) { + final RTTIReference.FollowResult entryRefRes = entryRef.follow(project, core); + if (entryRefRes == null) { + continue; + } + final RTTIReference.FollowResult typeRef = entryRefRes.object().ref("Type").follow(project, core); + if (typeRef == null) { + continue; + } + final RTTIObject typeInfo = typeRef.object(); + final String channel = entryRefRes.object().str("Channel"); + final String usage = typeRef.object().str("Name"); + textureInfo.channels.put(channel, new DMFMapTile.TileTextureInfo.TileTextureChannelInfo(usage, typeInfo.obj("Range").f32("Min"), typeInfo.obj("Range").f32("Max"))); + // texture.usageType + } + tileData.textures.put(resourceName, textureInfo); + } + tiles.put(gridPoint, tileData); + return null; + } + private DMFNode skinnedModelResourceToModel( @NotNull ProgressMonitor monitor, @NotNull CoreBinary core, @@ -768,14 +955,12 @@ private DMFNode objectCollectionToModel( ) throws IOException { final RTTIReference[] objects = object.get("Objects"); final DMFModelGroup group = new DMFModelGroup("Collection %s".formatted(resourceName)); - int itemId = 0; try (ProgressMonitor.Task task = monitor.begin("Exporting ObjectCollection Objects", objects.length)) { for (RTTIReference rttiReference : objects) { final RTTIReference.FollowResult refObject = Objects.requireNonNull(rttiReference.follow(project, core)); - final DMFNode node = toModel(task.split(1), refObject.binary(), refObject.object(), "%s_Object_%d".formatted(nameFromReference(rttiReference, resourceName), itemId)); + final DMFNode node = toModel(task.split(1), refObject.binary(), refObject.object(), nameFromReference(rttiReference, resourceName)); if (node != null) { group.children.add(node); - itemId++; } } } @@ -1064,7 +1249,7 @@ private void exportHZDMeshData(@NotNull ProgressMonitor monitor, @NotNull CoreBi realElementSize = stride - offset; } final String elementType = element.str("Type"); - final String semantic = SEMANTICS.get(elementType); + final String semantic = SEMANTICS_REMAP.get(elementType); if (semantic == null) { continue; } @@ -1189,7 +1374,7 @@ private void exportDSMeshData(@NotNull ProgressMonitor monitor, @NotNull CoreBin realElementSize = stride - offset; } final String elementType = element.str("Type"); - final String semantic = SEMANTICS.get(elementType); + final String semantic = SEMANTICS_REMAP.get(elementType); final DMFVertexAttribute attribute = new DMFVertexAttribute(); final DMFComponentType componentTypea = DMFComponentType.fromString(element.str("StorageType")); attribute.offset = offset; @@ -1356,7 +1541,14 @@ private DMFTexture exportTexture(@NotNull RTTIObject texture, @NotNull String te return null; } final ByteArrayOutputStream stream = new ByteArrayOutputStream(); - new TextureExporterPNG().export(imageProvider, Set.of(), Channels.newChannel(stream)); + final String ext; + if (imageProvider.getBitsPerChannel() > 1) { + new TextureExporterTIFF().export(imageProvider, Set.of(), Channels.newChannel(stream)); + ext = ".tiff"; + } else { + new TextureExporterPNG().export(imageProvider, Set.of(), Channels.newChannel(stream)); + ext = ".png"; + } final byte[] src = stream.toByteArray(); final DMFTexture dmfTexture; final DMFBuffer buffer; @@ -1365,8 +1557,8 @@ private DMFTexture exportTexture(@NotNull RTTIObject texture, @NotNull String te // fixme buffer = new DMFInternalBuffer(textureName, new ByteArrayDataProvider(src)); } else { - Files.write(getBuffersPath().resolve(textureName + ".png"), src); - buffer = new DMFExternalBuffer(textureName, textureName + ".png", new ByteArrayDataProvider(src)); + Files.write(getBuffersPath().resolve(textureName + ext), src); + buffer = new DMFExternalBuffer(textureName, textureName + ext, new ByteArrayDataProvider(src)); } scene.buffers.add(buffer); @@ -1449,6 +1641,9 @@ private JsonWriter createJsonWriter(@NotNull Writer writer) throws IOException { return jsonWriter; } + public record Vertex(Vector3f pos, Vector2f uv) {} + + public static class Provider implements ModelExporterProvider { @NotNull @Override @@ -1517,6 +1712,32 @@ public int length() { } } + private static final class TileData { + public final Map textures; + public final Point gridCoordinate; + public Vector3f bboxMin; + public Vector3f bboxMax; + + private TileData(Map textures, Point gridCoordinate, Vector3f bboxMin, Vector3f bboxMax) { + this.textures = textures; + this.gridCoordinate = gridCoordinate; + this.bboxMin = bboxMin; + this.bboxMax = bboxMax; + } + + public TileData(Point gridCoordinate) { + this(new HashMap<>(), gridCoordinate, null, null); + } + + public String toString() { + return "TileData[" + + "textures=" + textures + ", " + + "gridCoordinate=" + gridCoordinate + ", " + + "bboxMin=" + bboxMin + ", " + + "bboxMax=" + bboxMax + ']'; + } + } + private class JsonBufferSerializer implements JsonSerializer { @Override public JsonElement serialize(DMFBuffer src, Type type, JsonSerializationContext context) { diff --git a/modules/decima-ext-model-exporter/src/main/java/com/shade/decima/ui/data/viewer/model/dmf/DMFInstance.java b/modules/decima-ext-model-exporter/src/main/java/com/shade/decima/ui/data/viewer/model/dmf/DMFInstance.java index 564f9978c..7911590f8 100644 --- a/modules/decima-ext-model-exporter/src/main/java/com/shade/decima/ui/data/viewer/model/dmf/DMFInstance.java +++ b/modules/decima-ext-model-exporter/src/main/java/com/shade/decima/ui/data/viewer/model/dmf/DMFInstance.java @@ -10,4 +10,8 @@ public DMFInstance(@NotNull String name, int instanceId) { super(name, DMFNodeType.INSTANCE); this.instanceId = instanceId; } + + public boolean isEmpty() { + return false; + } } diff --git a/modules/decima-ext-model-exporter/src/main/java/com/shade/decima/ui/data/viewer/model/dmf/DMFLodModel.java b/modules/decima-ext-model-exporter/src/main/java/com/shade/decima/ui/data/viewer/model/dmf/DMFLodModel.java index fe6f3d247..20208c5d8 100644 --- a/modules/decima-ext-model-exporter/src/main/java/com/shade/decima/ui/data/viewer/model/dmf/DMFLodModel.java +++ b/modules/decima-ext-model-exporter/src/main/java/com/shade/decima/ui/data/viewer/model/dmf/DMFLodModel.java @@ -17,4 +17,8 @@ public void addLod(@NotNull DMFNode model, float distance) { } public record Lod(@NotNull DMFNode model, int id, float distance) {} + + public boolean isEmpty() { + return children.isEmpty() && lods.isEmpty(); + } } diff --git a/modules/decima-ext-model-exporter/src/main/java/com/shade/decima/ui/data/viewer/model/dmf/DMFMapTile.java b/modules/decima-ext-model-exporter/src/main/java/com/shade/decima/ui/data/viewer/model/dmf/DMFMapTile.java new file mode 100644 index 000000000..48443a9ed --- /dev/null +++ b/modules/decima-ext-model-exporter/src/main/java/com/shade/decima/ui/data/viewer/model/dmf/DMFMapTile.java @@ -0,0 +1,34 @@ +package com.shade.decima.ui.data.viewer.model.dmf; + +import com.shade.util.NotNull; + +import java.util.HashMap; +import java.util.Map; + +public class DMFMapTile extends DMFNode { + int[] gridCoordinate; + float[] bboxMin; + float[] bboxMax; + Map textures = new HashMap<>(); + + public DMFMapTile(@NotNull String name) { + super(name, DMFNodeType.MAP_TILE); + } + + public static final class TileTextureInfo { + public final Map channels = new HashMap<>(); + public Integer textureId = null; + + public static class TileTextureChannelInfo { + public String usage; + public float minRange; + public float maxRange; + + public TileTextureChannelInfo(String usage, float minRange, float maxRange) { + this.usage = usage; + this.minRange = minRange; + this.maxRange = maxRange; + } + } + } +} diff --git a/modules/decima-ext-model-exporter/src/main/java/com/shade/decima/ui/data/viewer/model/dmf/DMFModel.java b/modules/decima-ext-model-exporter/src/main/java/com/shade/decima/ui/data/viewer/model/dmf/DMFModel.java index 25e01f3c0..32dab712b 100644 --- a/modules/decima-ext-model-exporter/src/main/java/com/shade/decima/ui/data/viewer/model/dmf/DMFModel.java +++ b/modules/decima-ext-model-exporter/src/main/java/com/shade/decima/ui/data/viewer/model/dmf/DMFModel.java @@ -17,4 +17,8 @@ public void setSkeleton(@NotNull DMFSkeleton skeleton, @NotNull DMFSceneFile sce scene.skeletons.add(skeleton); skeletonId = scene.skeletons.indexOf(skeleton); } + + public boolean isEmpty() { + return children.isEmpty() && (mesh==null || mesh.primitives.isEmpty()); + } } diff --git a/modules/decima-ext-model-exporter/src/main/java/com/shade/decima/ui/data/viewer/model/dmf/DMFNode.java b/modules/decima-ext-model-exporter/src/main/java/com/shade/decima/ui/data/viewer/model/dmf/DMFNode.java index 3a1925fc2..9816be33e 100644 --- a/modules/decima-ext-model-exporter/src/main/java/com/shade/decima/ui/data/viewer/model/dmf/DMFNode.java +++ b/modules/decima-ext-model-exporter/src/main/java/com/shade/decima/ui/data/viewer/model/dmf/DMFNode.java @@ -25,4 +25,8 @@ protected DMFNode(@NotNull String name, @NotNull DMFNodeType type) { public void addToCollection(@NotNull DMFCollection collection, @NotNull DMFSceneFile scene) { collectionIds.add(scene.collections.indexOf(collection)); } + + public boolean isEmpty() { + return children.isEmpty(); + } } diff --git a/modules/decima-ext-model-exporter/src/main/java/com/shade/decima/ui/data/viewer/model/dmf/DMFNodeType.java b/modules/decima-ext-model-exporter/src/main/java/com/shade/decima/ui/data/viewer/model/dmf/DMFNodeType.java index ebc88c15f..5b8d6b3f2 100644 --- a/modules/decima-ext-model-exporter/src/main/java/com/shade/decima/ui/data/viewer/model/dmf/DMFNodeType.java +++ b/modules/decima-ext-model-exporter/src/main/java/com/shade/decima/ui/data/viewer/model/dmf/DMFNodeType.java @@ -9,4 +9,5 @@ public enum DMFNodeType { MODEL, SKINNED_MODEL, ATTACHMENT, + MAP_TILE, } diff --git a/modules/decima-ext-texture-viewer/src/main/java/com/shade/decima/ui/data/viewer/texture/TextureViewer.java b/modules/decima-ext-texture-viewer/src/main/java/com/shade/decima/ui/data/viewer/texture/TextureViewer.java index da0efd11a..b8ac5bf0c 100644 --- a/modules/decima-ext-texture-viewer/src/main/java/com/shade/decima/ui/data/viewer/texture/TextureViewer.java +++ b/modules/decima-ext-texture-viewer/src/main/java/com/shade/decima/ui/data/viewer/texture/TextureViewer.java @@ -51,35 +51,6 @@ public class TextureViewer implements ValueViewer { private static final Logger log = LoggerFactory.getLogger(TextureViewer.class); - @NotNull - @Override - public JComponent createComponent() { - return new TextureViewerPanel(); - } - - @Override - public void refresh(@NotNull JComponent component, @NotNull ValueController controller) { - final TextureInfo info = Objects.requireNonNull(getTextureInfo(controller)); - final HwTextureHeader header = info.texture.obj("Header").cast(); - final TextureViewerPanel panel = (TextureViewerPanel) component; - - panel.setStatusText("%sx%s (%s, %s)".formatted( - header.getWidth(), header.getHeight(), - header.getType(), header.getPixelFormat() - )); - - SwingUtilities.invokeLater(() -> { - final ImageProvider provider = getImageProvider(info.texture, controller.getProject().getPackfileManager()); - panel.getImagePanel().setProvider(provider, info.channels); - panel.getImagePanel().fit(); - }); - } - - @Override - public boolean canView(@NotNull ValueController controller) { - return getTextureInfo(controller) != null; - } - @Nullable public static ImageProvider getImageProvider(RTTIObject value, @NotNull PackfileManager manager) { final HwTextureHeader header = value.get("Header").cast(); @@ -178,9 +149,51 @@ public static EnumSet getChannels(int packedData, int packingInfo) { return channels; } + @NotNull + @Override + public JComponent createComponent() { + return new TextureViewerPanel(); + } + + @Override + public void refresh(@NotNull JComponent component, @NotNull ValueController controller) { + final TextureInfo info = Objects.requireNonNull(getTextureInfo(controller)); + final HwTextureHeader header = info.texture.obj("Header").cast(); + final TextureViewerPanel panel = (TextureViewerPanel) component; + + panel.setStatusText("%sx%s (%s, %s)".formatted( + header.getWidth(), header.getHeight(), + header.getType(), header.getPixelFormat() + )); + + SwingUtilities.invokeLater(() -> { + final ImageProvider provider = getImageProvider(info.texture, controller.getProject().getPackfileManager()); + panel.getImagePanel().setProvider(provider, info.channels); + panel.getImagePanel().fit(); + }); + } + + @Override + public boolean canView(@NotNull ValueController controller) { + return getTextureInfo(controller) != null; + } + public record TextureInfo(@NotNull RTTIObject texture, @Nullable EnumSet channels) {} private record MyImageProvider(@NotNull HwTextureHeader header, @NotNull HwTextureData data, @NotNull PackfileManager manager, @NotNull ImageReaderProvider readerProvider) implements ImageProvider { + @NotNull + private static Dimension getTextureDimension(@NotNull ImageReader reader, @NotNull Dimension dimension, int mip) { + return new Dimension( + Math.max(dimension.width >> mip, reader.getBlockSize()), + Math.max(dimension.height >> mip, reader.getBlockSize()) + ); + } + + private static int getTextureSize(@NotNull ImageReader reader, @NotNull Dimension dimension, int mip) { + final Dimension scaled = getTextureDimension(reader, dimension, mip); + return scaled.width * scaled.height * reader.getPixelBits() / 8; + } + @NotNull @Override public BufferedImage getImage(int mip, int slice) { @@ -311,17 +324,17 @@ public String getPixelFormat() { return header.getPixelFormat(); } - @NotNull - private static Dimension getTextureDimension(@NotNull ImageReader reader, @NotNull Dimension dimension, int mip) { - return new Dimension( - Math.max(dimension.width >> mip, reader.getBlockSize()), - Math.max(dimension.height >> mip, reader.getBlockSize()) - ); - } - - private static int getTextureSize(@NotNull ImageReader reader, @NotNull Dimension dimension, int mip) { - final Dimension scaled = getTextureDimension(reader, dimension, mip); - return scaled.width * scaled.height * reader.getPixelBits() / 8; + @Override + public int getBitsPerChannel() { + return switch (getPixelFormat()) { + case "RGBA_8888", "BC4U", "BC7", "BC5S", "BC5U", "BC4S", "BC3", "BC2", "BC1", "R_INT_8", "RG_INT_8", "RGBA_INT_8", "R_UINT_8", "RG_UINT_8", "RGBA_UINT_8", "R_NORM_8", "RG_NORM_8", "RGBA_NORM_8", "R_UNORM_8", "RG_UNORM_8", "RGBA_UNORM_8" -> + 1; + case "RGBA_FLOAT_16", "RGB_FLOAT_16", "RG_FLOAT_16", "R_FLOAT_16", "BC6S", "BC6U", "RGBA_UNORM_10_10_10_2", "RGB_FLOAT_11_11_10", "R_INT_16", "RG_INT_16", "RGBA_INT_16", "R_UINT_16", "RG_UINT_16", "RGBA_UINT_16", "RG_NORM_16", "RGBA_NORM_16", "R_UNORM_16", "RG_UNORM_16", "RGBA_UNORM_16", "R_NORM_16" -> + 2; + case "RGBA_FLOAT_32", "RGB_FLOAT_32", "RG_FLOAT_32", "R_FLOAT_32", "R_INT_32", "RG_INT_32", "RGBA_INT_32", "R_UINT_32", "RG_UINT_32", "RGBA_UINT_32", "RGBA_UNORM_32" -> + 4; + default -> 0; + }; } private record ImageData(@NotNull ImageReader reader, @NotNull ByteBuffer buffer, int width, int height) {} diff --git a/modules/decima-ext-texture-viewer/src/main/java/com/shade/decima/ui/data/viewer/texture/controls/ImageProvider.java b/modules/decima-ext-texture-viewer/src/main/java/com/shade/decima/ui/data/viewer/texture/controls/ImageProvider.java index 456785d5a..cc86a30b1 100644 --- a/modules/decima-ext-texture-viewer/src/main/java/com/shade/decima/ui/data/viewer/texture/controls/ImageProvider.java +++ b/modules/decima-ext-texture-viewer/src/main/java/com/shade/decima/ui/data/viewer/texture/controls/ImageProvider.java @@ -39,4 +39,6 @@ enum Type { @NotNull String getPixelFormat(); + + int getBitsPerChannel(); } From 39120be0f1fe2f2daf85df7b7d10c15d608cab20 Mon Sep 17 00:00:00 2001 From: REDxEYE Date: Sat, 25 Nov 2023 23:46:37 +0300 Subject: [PATCH 02/19] Fix issues --- .../viewer/model/dmf/DMFAttachmentNode.java | 1 + .../viewer/model/dmf/DMFCompositeModel.java | 1 + .../ui/data/viewer/model/dmf/DMFExporter.java | 123 +----------------- .../ui/data/viewer/model/dmf/DMFInstance.java | 1 + .../ui/data/viewer/model/dmf/DMFLodModel.java | 1 + .../ui/data/viewer/model/dmf/DMFMapTile.java | 8 +- .../ui/data/viewer/model/dmf/DMFModel.java | 1 + .../ui/data/viewer/texture/TextureViewer.java | 58 ++++----- 8 files changed, 44 insertions(+), 150 deletions(-) diff --git a/modules/decima-ext-model-exporter/src/main/java/com/shade/decima/ui/data/viewer/model/dmf/DMFAttachmentNode.java b/modules/decima-ext-model-exporter/src/main/java/com/shade/decima/ui/data/viewer/model/dmf/DMFAttachmentNode.java index b696cf6bd..412e5a450 100644 --- a/modules/decima-ext-model-exporter/src/main/java/com/shade/decima/ui/data/viewer/model/dmf/DMFAttachmentNode.java +++ b/modules/decima-ext-model-exporter/src/main/java/com/shade/decima/ui/data/viewer/model/dmf/DMFAttachmentNode.java @@ -11,6 +11,7 @@ public DMFAttachmentNode(@NotNull String name, @NotNull String boneName, @NotNul this.transform = transform; } + @Override public boolean isEmpty() { return false; } diff --git a/modules/decima-ext-model-exporter/src/main/java/com/shade/decima/ui/data/viewer/model/dmf/DMFCompositeModel.java b/modules/decima-ext-model-exporter/src/main/java/com/shade/decima/ui/data/viewer/model/dmf/DMFCompositeModel.java index ec74ee35b..6ee3c5807 100644 --- a/modules/decima-ext-model-exporter/src/main/java/com/shade/decima/ui/data/viewer/model/dmf/DMFCompositeModel.java +++ b/modules/decima-ext-model-exporter/src/main/java/com/shade/decima/ui/data/viewer/model/dmf/DMFCompositeModel.java @@ -10,6 +10,7 @@ public DMFCompositeModel(@NotNull String name, int skeletonId) { this.skeletonId = skeletonId; } + @Override public boolean isEmpty() { return children.isEmpty(); } diff --git a/modules/decima-ext-model-exporter/src/main/java/com/shade/decima/ui/data/viewer/model/dmf/DMFExporter.java b/modules/decima-ext-model-exporter/src/main/java/com/shade/decima/ui/data/viewer/model/dmf/DMFExporter.java index 1621b6c58..70686b1b2 100644 --- a/modules/decima-ext-model-exporter/src/main/java/com/shade/decima/ui/data/viewer/model/dmf/DMFExporter.java +++ b/modules/decima-ext-model-exporter/src/main/java/com/shade/decima/ui/data/viewer/model/dmf/DMFExporter.java @@ -81,8 +81,6 @@ public class DMFExporter extends BaseModelExporter implements ModelExporter { private int depth = 0; private DMFSceneFile scene; private DMFSkeleton masterSkeleton; - private int nodesRemoved = 0; - private int nodesVisited = 0; public DMFExporter(@NotNull Project project, @NotNull Set options, @NotNull Path output) { this.project = project; @@ -90,29 +88,6 @@ public DMFExporter(@NotNull Project project, @NotNull Set generateGrid(int gridSize) { - List vertices = new ArrayList<>(); - - float dx = 1.0f / gridSize; // Delta for UV mapping - float dy = 1.0f / gridSize; - - for (int i = 0; i <= gridSize; i++) { - for (int j = 0; j <= gridSize; j++) { - // Position - float x = (float) i / gridSize; - float y = 0; // Assuming grid is flat on the y-axis - float z = (float) j / gridSize; - - // UV Coordinates - float u = i * dx; - float v = j * dy; - - vertices.add(new Vertex(new Vector3f(x, y, z), new Vector2f(u, v))); - } - } - return vertices; - } - @Override public void export( @NotNull ProgressMonitor monitor, @@ -122,107 +97,21 @@ public void export( @NotNull Writer writer ) throws Exception { final var scene = export(monitor, core, object, resourceName); - nodesRemoved = 0; - nodesVisited = 0; - for (DMFNode node : scene.models) { - countNodes(node); - } - for (DMFNode node : scene.instances) { - countNodes(node); - } - for (DMFNode node : scene.instances) { - removeEmpty(node); - } - optimizeModelList(scene.instances); - optimizeInstances(scene); - for (DMFNode node : scene.instances) { - optimize(node); - } - optimizeInstances(scene); - optimizeModelList(scene.instances); - optimizeModelList(scene.models); - for (DMFNode node : scene.models) { - removeEmpty(node); - } - for (DMFNode node : scene.models) { - optimize(node); - } - optimizeModelList(scene.models); + for (Point point : tiles.keySet()) { - generateTileMesh(tiles.get(point)); - // System.out.printf("Tile: %f %f%n", point.getX(), point.getY()); + generateMapTileNode(tiles.get(point)); } - System.out.printf("Removed %d(%f%%) nodes out of %d%n", nodesRemoved, ((float) nodesRemoved / nodesVisited) * 100.f, nodesVisited); gson.toJson(scene, scene.getClass(), createJsonWriter(writer)); } - private void generateTileMesh(TileData tileData) { + private void generateMapTileNode(TileData tileData) { DMFMapTile mapTile = new DMFMapTile("Tile_%d_%d".formatted(tileData.gridCoordinate.x, tileData.gridCoordinate.y)); mapTile.textures.putAll(tileData.textures); mapTile.bboxMin = new float[]{tileData.bboxMin.x(), tileData.bboxMin.y(), tileData.bboxMin.z()}; mapTile.bboxMax = new float[]{tileData.bboxMax.x(), tileData.bboxMax.y(), tileData.bboxMax.z()}; mapTile.gridCoordinate = new int[]{tileData.gridCoordinate.x, tileData.gridCoordinate.y}; scene.models.add(mapTile); - // Vector3f dimensions = tileData.bboxMax.sub(tileData.bboxMin).absolute(); - // scene.models.add() - } - - private void countNodes(DMFNode node) { - nodesVisited++; - node.children.forEach(this::countNodes); - } - - private void optimizeInstances(DMFSceneFile scene) { - for (int i = 0; i < scene.instances.size(); i++) { - DMFNode node = scene.instances.get(i); - optimize(node); - removeEmpty(node); - if (node.getClass() == DMFInstance.class) { - DMFNode instanceNode = scene.instances.get(((DMFInstance) node).instanceId); - if (instanceNode.children.isEmpty() && instanceNode.getClass() == DMFInstance.class) { - scene.instances.set(i, instanceNode); - } - } - } - } - - private void optimizeModelList(List nodes) { - for (int i = 0; i < nodes.size(); i++) { - DMFNode node = nodes.get(i); - if ((node.getClass() == DMFNode.class || node.getClass() == DMFModelGroup.class) && node.transform == null) { - if (node.children.size() == 1) { - nodesRemoved++; - nodes.set(i, node.children.get(0)); - } - } - } - } - - private void removeEmpty(DMFNode parentNode) { - parentNode.children.forEach(this::removeEmpty); - parentNode.children.removeIf(DMFNode::isEmpty); - } - - private void optimize(DMFNode parentNode) { - parentNode.children.forEach(this::optimize); - - List copyArray = new ArrayList<>(parentNode.children); - for (int i = 0; i < parentNode.children.size(); i++) { - DMFNode node = parentNode.children.get(i); - if (node.getClass() == DMFNode.class && node.transform == null) { - copyArray.remove(node); - nodesRemoved++; - copyArray.addAll(node.children); - } - if (node.getClass() == DMFModelGroup.class && node.transform == null) { - copyArray.remove(node); - nodesRemoved++; - copyArray.addAll(node.children); - } - } - parentNode.children.clear(); - parentNode.children.addAll(copyArray); } @NotNull @@ -622,10 +511,10 @@ private DMFNode terrainTileDataToModel( @NotNull CoreBinary core, @NotNull RTTIObject object, @NotNull String resourceName - ) throws IOException { + ){ RTTIObject gridCoordinates = object.get("GridCoordinates"); Point gridPoint = new Point(gridCoordinates.i32("X"), gridCoordinates.i32("Y")); - TileData tileData = tiles.getOrDefault(gridPoint, new TileData(gridPoint)); + TileData tileData = tiles.computeIfAbsent(gridPoint, TileData::new); RTTIObject bbox = object.get("BoundingBox"); tileData.bboxMin = new Vector3f(bbox.obj("Min").f32("X"), bbox.obj("Min").f32("Y"), bbox.obj("Min").f32("Z")); tileData.bboxMax = new Vector3f(bbox.obj("Max").f32("X"), bbox.obj("Max").f32("Y"), bbox.obj("Max").f32("Z")); @@ -642,7 +531,7 @@ private DMFNode worldDataTextureMapToModel( RTTIObject gridCoordinates = object.get("GridCoordinates"); Point gridPoint = new Point(gridCoordinates.i32("X"), gridCoordinates.i32("Y")); - TileData tileData = tiles.getOrDefault(gridPoint, new TileData(gridPoint)); + TileData tileData = tiles.computeIfAbsent(gridPoint, TileData::new); final RTTIReference.FollowResult resultTextureRef = object.ref("ResultTexture").follow(project, core); if (resultTextureRef != null) { diff --git a/modules/decima-ext-model-exporter/src/main/java/com/shade/decima/ui/data/viewer/model/dmf/DMFInstance.java b/modules/decima-ext-model-exporter/src/main/java/com/shade/decima/ui/data/viewer/model/dmf/DMFInstance.java index 7911590f8..e7359cef8 100644 --- a/modules/decima-ext-model-exporter/src/main/java/com/shade/decima/ui/data/viewer/model/dmf/DMFInstance.java +++ b/modules/decima-ext-model-exporter/src/main/java/com/shade/decima/ui/data/viewer/model/dmf/DMFInstance.java @@ -11,6 +11,7 @@ public DMFInstance(@NotNull String name, int instanceId) { this.instanceId = instanceId; } + @Override public boolean isEmpty() { return false; } diff --git a/modules/decima-ext-model-exporter/src/main/java/com/shade/decima/ui/data/viewer/model/dmf/DMFLodModel.java b/modules/decima-ext-model-exporter/src/main/java/com/shade/decima/ui/data/viewer/model/dmf/DMFLodModel.java index 20208c5d8..3443f8184 100644 --- a/modules/decima-ext-model-exporter/src/main/java/com/shade/decima/ui/data/viewer/model/dmf/DMFLodModel.java +++ b/modules/decima-ext-model-exporter/src/main/java/com/shade/decima/ui/data/viewer/model/dmf/DMFLodModel.java @@ -18,6 +18,7 @@ public void addLod(@NotNull DMFNode model, float distance) { public record Lod(@NotNull DMFNode model, int id, float distance) {} + @Override public boolean isEmpty() { return children.isEmpty() && lods.isEmpty(); } diff --git a/modules/decima-ext-model-exporter/src/main/java/com/shade/decima/ui/data/viewer/model/dmf/DMFMapTile.java b/modules/decima-ext-model-exporter/src/main/java/com/shade/decima/ui/data/viewer/model/dmf/DMFMapTile.java index 48443a9ed..6041a7354 100644 --- a/modules/decima-ext-model-exporter/src/main/java/com/shade/decima/ui/data/viewer/model/dmf/DMFMapTile.java +++ b/modules/decima-ext-model-exporter/src/main/java/com/shade/decima/ui/data/viewer/model/dmf/DMFMapTile.java @@ -6,10 +6,10 @@ import java.util.Map; public class DMFMapTile extends DMFNode { - int[] gridCoordinate; - float[] bboxMin; - float[] bboxMax; - Map textures = new HashMap<>(); + public int[] gridCoordinate; + public float[] bboxMin; + public float[] bboxMax; + public Map textures = new HashMap<>(); public DMFMapTile(@NotNull String name) { super(name, DMFNodeType.MAP_TILE); diff --git a/modules/decima-ext-model-exporter/src/main/java/com/shade/decima/ui/data/viewer/model/dmf/DMFModel.java b/modules/decima-ext-model-exporter/src/main/java/com/shade/decima/ui/data/viewer/model/dmf/DMFModel.java index 32dab712b..f2304427d 100644 --- a/modules/decima-ext-model-exporter/src/main/java/com/shade/decima/ui/data/viewer/model/dmf/DMFModel.java +++ b/modules/decima-ext-model-exporter/src/main/java/com/shade/decima/ui/data/viewer/model/dmf/DMFModel.java @@ -18,6 +18,7 @@ public void setSkeleton(@NotNull DMFSkeleton skeleton, @NotNull DMFSceneFile sce skeletonId = scene.skeletons.indexOf(skeleton); } + @Override public boolean isEmpty() { return children.isEmpty() && (mesh==null || mesh.primitives.isEmpty()); } diff --git a/modules/decima-ext-texture-viewer/src/main/java/com/shade/decima/ui/data/viewer/texture/TextureViewer.java b/modules/decima-ext-texture-viewer/src/main/java/com/shade/decima/ui/data/viewer/texture/TextureViewer.java index b8ac5bf0c..f6d453fa1 100644 --- a/modules/decima-ext-texture-viewer/src/main/java/com/shade/decima/ui/data/viewer/texture/TextureViewer.java +++ b/modules/decima-ext-texture-viewer/src/main/java/com/shade/decima/ui/data/viewer/texture/TextureViewer.java @@ -51,6 +51,35 @@ public class TextureViewer implements ValueViewer { private static final Logger log = LoggerFactory.getLogger(TextureViewer.class); + @NotNull + @Override + public JComponent createComponent() { + return new TextureViewerPanel(); + } + + @Override + public void refresh(@NotNull JComponent component, @NotNull ValueController controller) { + final TextureInfo info = Objects.requireNonNull(getTextureInfo(controller)); + final HwTextureHeader header = info.texture.obj("Header").cast(); + final TextureViewerPanel panel = (TextureViewerPanel) component; + + panel.setStatusText("%sx%s (%s, %s)".formatted( + header.getWidth(), header.getHeight(), + header.getType(), header.getPixelFormat() + )); + + SwingUtilities.invokeLater(() -> { + final ImageProvider provider = getImageProvider(info.texture, controller.getProject().getPackfileManager()); + panel.getImagePanel().setProvider(provider, info.channels); + panel.getImagePanel().fit(); + }); + } + + @Override + public boolean canView(@NotNull ValueController controller) { + return getTextureInfo(controller) != null; + } + @Nullable public static ImageProvider getImageProvider(RTTIObject value, @NotNull PackfileManager manager) { final HwTextureHeader header = value.get("Header").cast(); @@ -149,35 +178,6 @@ public static EnumSet getChannels(int packedData, int packingInfo) { return channels; } - @NotNull - @Override - public JComponent createComponent() { - return new TextureViewerPanel(); - } - - @Override - public void refresh(@NotNull JComponent component, @NotNull ValueController controller) { - final TextureInfo info = Objects.requireNonNull(getTextureInfo(controller)); - final HwTextureHeader header = info.texture.obj("Header").cast(); - final TextureViewerPanel panel = (TextureViewerPanel) component; - - panel.setStatusText("%sx%s (%s, %s)".formatted( - header.getWidth(), header.getHeight(), - header.getType(), header.getPixelFormat() - )); - - SwingUtilities.invokeLater(() -> { - final ImageProvider provider = getImageProvider(info.texture, controller.getProject().getPackfileManager()); - panel.getImagePanel().setProvider(provider, info.channels); - panel.getImagePanel().fit(); - }); - } - - @Override - public boolean canView(@NotNull ValueController controller) { - return getTextureInfo(controller) != null; - } - public record TextureInfo(@NotNull RTTIObject texture, @Nullable EnumSet channels) {} private record MyImageProvider(@NotNull HwTextureHeader header, @NotNull HwTextureData data, @NotNull PackfileManager manager, @NotNull ImageReaderProvider readerProvider) implements ImageProvider { From 3653b965e3d85374d56e0564875f8f37ef1efdb9 Mon Sep 17 00:00:00 2001 From: REDxEYE Date: Sat, 25 Nov 2023 23:49:40 +0300 Subject: [PATCH 03/19] make bbox vectors constant --- .../shade/decima/ui/data/viewer/model/dmf/DMFExporter.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/modules/decima-ext-model-exporter/src/main/java/com/shade/decima/ui/data/viewer/model/dmf/DMFExporter.java b/modules/decima-ext-model-exporter/src/main/java/com/shade/decima/ui/data/viewer/model/dmf/DMFExporter.java index 70686b1b2..81043bfaf 100644 --- a/modules/decima-ext-model-exporter/src/main/java/com/shade/decima/ui/data/viewer/model/dmf/DMFExporter.java +++ b/modules/decima-ext-model-exporter/src/main/java/com/shade/decima/ui/data/viewer/model/dmf/DMFExporter.java @@ -33,6 +33,7 @@ import com.shade.util.Nullable; import org.joml.Vector2f; import org.joml.Vector3f; +import org.joml.Vector3fc; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -1604,8 +1605,8 @@ public int length() { private static final class TileData { public final Map textures; public final Point gridCoordinate; - public Vector3f bboxMin; - public Vector3f bboxMax; + public Vector3fc bboxMin; + public Vector3fc bboxMax; private TileData(Map textures, Point gridCoordinate, Vector3f bboxMin, Vector3f bboxMax) { this.textures = textures; From bfe1341fcb89033ee4d364b30a1fa2004785e80f Mon Sep 17 00:00:00 2001 From: REDxEYE Date: Sun, 26 Nov 2023 16:01:28 +0300 Subject: [PATCH 04/19] Add common function to export some types --- .../ui/data/viewer/model/dmf/DMFExporter.java | 81 ++++--------------- 1 file changed, 16 insertions(+), 65 deletions(-) diff --git a/modules/decima-ext-model-exporter/src/main/java/com/shade/decima/ui/data/viewer/model/dmf/DMFExporter.java b/modules/decima-ext-model-exporter/src/main/java/com/shade/decima/ui/data/viewer/model/dmf/DMFExporter.java index 81043bfaf..16225a894 100644 --- a/modules/decima-ext-model-exporter/src/main/java/com/shade/decima/ui/data/viewer/model/dmf/DMFExporter.java +++ b/modules/decima-ext-model-exporter/src/main/java/com/shade/decima/ui/data/viewer/model/dmf/DMFExporter.java @@ -139,20 +139,28 @@ private void exportResource( switch (object.type().getTypeName()) { case "Terrain" -> exportTerrain(monitor, core, object, resourceName); case "ControlledEntityResource" -> exportControlledEntityResource(monitor, core, object, resourceName); - case "SkinnedModelResource" -> exportSkinnedModelResource(monitor, core, object, resourceName); + case "SkinnedModelResource", "StreamingTileResource", + "LodMeshResource", "MultiMeshResource", + "ObjectCollection", "RegularSkinnedMeshResource", + "StaticMeshResource" -> exportModelGeneric(monitor, core, object, resourceName); case "ArtPartsDataResource" -> exportArtPartsDataResource(monitor, core, object, resourceName); - case "ObjectCollection" -> exportObjectCollection(monitor, core, object, resourceName); case "TileBasedStreamingStrategyResource" -> exportTileBasedStreamingStrategyResource(monitor, core, object, resourceName); - case "StreamingTileResource" -> exportStreamingTileResource(monitor, core, object, resourceName); - case "LodMeshResource" -> exportLodMeshResource(monitor, core, object, resourceName); - case "MultiMeshResource" -> exportMultiMeshResource(monitor, core, object, resourceName); - case "RegularSkinnedMeshResource", "StaticMeshResource" -> - exportRegularSkinnedMeshResource(monitor, core, object, resourceName); default -> throw new IllegalArgumentException("Unsupported resource: " + object.type()); } } + private void exportModelGeneric( + @NotNull ProgressMonitor monitor, + @NotNull CoreBinary core, + @NotNull RTTIObject object, + @NotNull String resourceName) throws IOException { + final DMFNode node = toModel(monitor, core, object, resourceName); + if (node != null && !node.isEmpty()) { + scene.models.add(node); + } + } + private void exportControlledEntityResource( @NotNull ProgressMonitor monitor, @NotNull CoreBinary core, @@ -320,15 +328,6 @@ private void exportTerrain( } } - private void exportSkinnedModelResource( - @NotNull ProgressMonitor monitor, - @NotNull CoreBinary core, - @NotNull RTTIObject object, - @NotNull String resourceName - ) throws IOException { - scene.models.add(skinnedModelResourceToModel(monitor, core, object, resourceName)); - } - private void exportArtPartsDataResource( @NotNull ProgressMonitor monitor, @NotNull CoreBinary core, @@ -394,41 +393,6 @@ private void exportArtPartsDataResource( scene.models.add(compositeModel); } - private void exportLodMeshResource( - @NotNull ProgressMonitor monitor, - @NotNull CoreBinary core, - @NotNull RTTIObject object, - @NotNull String resourceName - ) throws IOException { - scene.models.add(lodMeshResourceToModel(monitor, core, object, resourceName)); - } - - private void exportMultiMeshResource( - @NotNull ProgressMonitor monitor, - @NotNull CoreBinary core, - @NotNull RTTIObject object, @NotNull String resourceName - ) throws IOException { - scene.models.add(multiMeshResourceToModel(monitor, core, object, resourceName)); - } - - private void exportObjectCollection( - @NotNull ProgressMonitor monitor, - @NotNull CoreBinary core, - @NotNull RTTIObject object, - @NotNull String resourceName - ) throws IOException { - scene.models.add(objectCollectionToModel(monitor, core, object, resourceName)); - } - - private void exportStreamingTileResource( - @NotNull ProgressMonitor monitor, - @NotNull CoreBinary core, - @NotNull RTTIObject object, - @NotNull String resourceName - ) throws IOException { - scene.models.add(streamingTileResourceToModel(monitor, core, object, resourceName)); - } - private void exportTileBasedStreamingStrategyResource( @NotNull ProgressMonitor monitor, @NotNull CoreBinary core, @@ -446,7 +410,6 @@ private void exportTileBasedStreamingStrategyResource( continue; } - DMFNode node = toModel(task.split(1), tileRefRes.binary(), tileRefRes.object(), nameFromReference(ref, "%s_Tile_%d".formatted(resourceName, i))); if (node != null) { group.children.add(node); @@ -455,18 +418,6 @@ private void exportTileBasedStreamingStrategyResource( } } - private void exportRegularSkinnedMeshResource( - @NotNull ProgressMonitor monitor, - @NotNull CoreBinary core, - @NotNull RTTIObject object, - @NotNull String resourceName - ) throws IOException { - final DMFNode node = regularSkinnedMeshResourceToModel(monitor, core, object, resourceName); - if (node != null) { - scene.models.add(node); - } - } - @Nullable private DMFNode toModel( @NotNull ProgressMonitor monitor, @@ -512,7 +463,7 @@ private DMFNode terrainTileDataToModel( @NotNull CoreBinary core, @NotNull RTTIObject object, @NotNull String resourceName - ){ + ) { RTTIObject gridCoordinates = object.get("GridCoordinates"); Point gridPoint = new Point(gridCoordinates.i32("X"), gridCoordinates.i32("Y")); TileData tileData = tiles.computeIfAbsent(gridPoint, TileData::new); From 36c9e2e14c46a6cd12cffa640238962ce27b5985 Mon Sep 17 00:00:00 2001 From: REDxEYE Date: Sun, 26 Nov 2023 16:08:57 +0300 Subject: [PATCH 05/19] Add identity constructor to Quaternion class --- .../decima/ui/data/viewer/model/dmf/DMFExporter.java | 8 ++++---- .../decima/ui/data/viewer/model/utils/Quaternion.java | 5 +++++ 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/modules/decima-ext-model-exporter/src/main/java/com/shade/decima/ui/data/viewer/model/dmf/DMFExporter.java b/modules/decima-ext-model-exporter/src/main/java/com/shade/decima/ui/data/viewer/model/dmf/DMFExporter.java index 16225a894..e9586bdc2 100644 --- a/modules/decima-ext-model-exporter/src/main/java/com/shade/decima/ui/data/viewer/model/dmf/DMFExporter.java +++ b/modules/decima-ext-model-exporter/src/main/java/com/shade/decima/ui/data/viewer/model/dmf/DMFExporter.java @@ -352,7 +352,7 @@ private void exportArtPartsDataResource( if (defaultRot.length > 0) { rotations = new Quaternion(defaultRot[i].f32("X"), defaultRot[i].f32("Y"), defaultRot[i].f32("Z"), defaultRot[i].f32("W")); } else { - rotations = new Quaternion(0d, 0d, 0d, 1d); + rotations = Quaternion.identity(); } DMFTransform matrix = new DMFTransform( @@ -502,8 +502,8 @@ private DMFNode worldDataTextureMapToModel( final RTTIObject typeInfo = typeRef.object(); final String channel = entryRefRes.object().str("Channel"); final String usage = typeRef.object().str("Name"); - textureInfo.channels.put(channel, new DMFMapTile.TileTextureInfo.TileTextureChannelInfo(usage, typeInfo.obj("Range").f32("Min"), typeInfo.obj("Range").f32("Max"))); - // texture.usageType + textureInfo.channels.put(channel, new DMFMapTile.TileTextureInfo.TileTextureChannelInfo(usage, + typeInfo.obj("Range").f32("Min"), typeInfo.obj("Range").f32("Max"))); } tileData.textures.put(resourceName, textureInfo); } @@ -602,7 +602,7 @@ private DMFNode artPartsSubModelResourceToModel( if (defaultRot.length > 0) { rotations = new Quaternion(defaultRot[i].f32("X"), defaultRot[i].f32("Y"), defaultRot[i].f32("Z"), defaultRot[i].f32("W")); } else { - rotations = new Quaternion(0d, 0d, 0d, 1d); + rotations = Quaternion.identity(); } DMFTransform matrix = new DMFTransform( diff --git a/modules/decima-ext-model-exporter/src/main/java/com/shade/decima/ui/data/viewer/model/utils/Quaternion.java b/modules/decima-ext-model-exporter/src/main/java/com/shade/decima/ui/data/viewer/model/utils/Quaternion.java index 07de508ee..0d8e3e2b6 100644 --- a/modules/decima-ext-model-exporter/src/main/java/com/shade/decima/ui/data/viewer/model/utils/Quaternion.java +++ b/modules/decima-ext-model-exporter/src/main/java/com/shade/decima/ui/data/viewer/model/utils/Quaternion.java @@ -7,6 +7,11 @@ public Quaternion(@NotNull double[] xyzw) { this(xyzw[0], xyzw[1], xyzw[2], xyzw[3]); } + @NotNull + public static Quaternion identity() { + return new Quaternion(0, 0, 0, 1); + } + @NotNull public Quaternion add(@NotNull Quaternion other) { return new Quaternion(x() + other.x(), y() + other.y(), z() + other.y(), w() + other.w()); From 6100b935e183c34bf3db88dfe031f762059057c3 Mon Sep 17 00:00:00 2001 From: REDxEYE Date: Mon, 27 Nov 2023 19:23:15 +0300 Subject: [PATCH 06/19] Formatting fixes --- .../ui/data/viewer/model/dmf/DMFExporter.java | 7 ++--- .../ui/data/viewer/texture/TextureViewer.java | 27 +++++++++---------- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/modules/decima-ext-model-exporter/src/main/java/com/shade/decima/ui/data/viewer/model/dmf/DMFExporter.java b/modules/decima-ext-model-exporter/src/main/java/com/shade/decima/ui/data/viewer/model/dmf/DMFExporter.java index e9586bdc2..95f702a25 100644 --- a/modules/decima-ext-model-exporter/src/main/java/com/shade/decima/ui/data/viewer/model/dmf/DMFExporter.java +++ b/modules/decima-ext-model-exporter/src/main/java/com/shade/decima/ui/data/viewer/model/dmf/DMFExporter.java @@ -99,8 +99,8 @@ public void export( ) throws Exception { final var scene = export(monitor, core, object, resourceName); - for (Point point : tiles.keySet()) { - generateMapTileNode(tiles.get(point)); + for (TileData tile : tiles.values()) { + generateMapTileNode(tile); } gson.toJson(scene, scene.getClass(), createJsonWriter(writer)); @@ -154,7 +154,8 @@ private void exportModelGeneric( @NotNull ProgressMonitor monitor, @NotNull CoreBinary core, @NotNull RTTIObject object, - @NotNull String resourceName) throws IOException { + @NotNull String resourceName + ) throws IOException { final DMFNode node = toModel(monitor, core, object, resourceName); if (node != null && !node.isEmpty()) { scene.models.add(node); diff --git a/modules/decima-ext-texture-viewer/src/main/java/com/shade/decima/ui/data/viewer/texture/TextureViewer.java b/modules/decima-ext-texture-viewer/src/main/java/com/shade/decima/ui/data/viewer/texture/TextureViewer.java index f6d453fa1..04453ea66 100644 --- a/modules/decima-ext-texture-viewer/src/main/java/com/shade/decima/ui/data/viewer/texture/TextureViewer.java +++ b/modules/decima-ext-texture-viewer/src/main/java/com/shade/decima/ui/data/viewer/texture/TextureViewer.java @@ -181,19 +181,6 @@ public static EnumSet getChannels(int packedData, int packingInfo) { public record TextureInfo(@NotNull RTTIObject texture, @Nullable EnumSet channels) {} private record MyImageProvider(@NotNull HwTextureHeader header, @NotNull HwTextureData data, @NotNull PackfileManager manager, @NotNull ImageReaderProvider readerProvider) implements ImageProvider { - @NotNull - private static Dimension getTextureDimension(@NotNull ImageReader reader, @NotNull Dimension dimension, int mip) { - return new Dimension( - Math.max(dimension.width >> mip, reader.getBlockSize()), - Math.max(dimension.height >> mip, reader.getBlockSize()) - ); - } - - private static int getTextureSize(@NotNull ImageReader reader, @NotNull Dimension dimension, int mip) { - final Dimension scaled = getTextureDimension(reader, dimension, mip); - return scaled.width * scaled.height * reader.getPixelBits() / 8; - } - @NotNull @Override public BufferedImage getImage(int mip, int slice) { @@ -324,6 +311,19 @@ public String getPixelFormat() { return header.getPixelFormat(); } + @NotNull + private static Dimension getTextureDimension(@NotNull ImageReader reader, @NotNull Dimension dimension, int mip) { + return new Dimension( + Math.max(dimension.width >> mip, reader.getBlockSize()), + Math.max(dimension.height >> mip, reader.getBlockSize()) + ); + } + + private static int getTextureSize(@NotNull ImageReader reader, @NotNull Dimension dimension, int mip) { + final Dimension scaled = getTextureDimension(reader, dimension, mip); + return scaled.width * scaled.height * reader.getPixelBits() / 8; + } + @Override public int getBitsPerChannel() { return switch (getPixelFormat()) { @@ -336,7 +336,6 @@ public int getBitsPerChannel() { default -> 0; }; } - private record ImageData(@NotNull ImageReader reader, @NotNull ByteBuffer buffer, int width, int height) {} } } From 460cb87c689dffc7493781b96d03aacd74ee9ccf Mon Sep 17 00:00:00 2001 From: REDxEYE Date: Tue, 28 Nov 2023 19:27:21 +0300 Subject: [PATCH 07/19] Fix function return value getBitsPerChannel --- .../shade/decima/ui/data/viewer/model/dmf/DMFExporter.java | 2 +- .../shade/decima/ui/data/viewer/texture/TextureViewer.java | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/modules/decima-ext-model-exporter/src/main/java/com/shade/decima/ui/data/viewer/model/dmf/DMFExporter.java b/modules/decima-ext-model-exporter/src/main/java/com/shade/decima/ui/data/viewer/model/dmf/DMFExporter.java index 95f702a25..66bedf213 100644 --- a/modules/decima-ext-model-exporter/src/main/java/com/shade/decima/ui/data/viewer/model/dmf/DMFExporter.java +++ b/modules/decima-ext-model-exporter/src/main/java/com/shade/decima/ui/data/viewer/model/dmf/DMFExporter.java @@ -1384,7 +1384,7 @@ private DMFTexture exportTexture(@NotNull RTTIObject texture, @NotNull String te } final ByteArrayOutputStream stream = new ByteArrayOutputStream(); final String ext; - if (imageProvider.getBitsPerChannel() > 1) { + if (imageProvider.getBitsPerChannel() > 8) { new TextureExporterTIFF().export(imageProvider, Set.of(), Channels.newChannel(stream)); ext = ".tiff"; } else { diff --git a/modules/decima-ext-texture-viewer/src/main/java/com/shade/decima/ui/data/viewer/texture/TextureViewer.java b/modules/decima-ext-texture-viewer/src/main/java/com/shade/decima/ui/data/viewer/texture/TextureViewer.java index 04453ea66..d96471d3c 100644 --- a/modules/decima-ext-texture-viewer/src/main/java/com/shade/decima/ui/data/viewer/texture/TextureViewer.java +++ b/modules/decima-ext-texture-viewer/src/main/java/com/shade/decima/ui/data/viewer/texture/TextureViewer.java @@ -328,11 +328,11 @@ private static int getTextureSize(@NotNull ImageReader reader, @NotNull Dimensio public int getBitsPerChannel() { return switch (getPixelFormat()) { case "RGBA_8888", "BC4U", "BC7", "BC5S", "BC5U", "BC4S", "BC3", "BC2", "BC1", "R_INT_8", "RG_INT_8", "RGBA_INT_8", "R_UINT_8", "RG_UINT_8", "RGBA_UINT_8", "R_NORM_8", "RG_NORM_8", "RGBA_NORM_8", "R_UNORM_8", "RG_UNORM_8", "RGBA_UNORM_8" -> - 1; + 8; case "RGBA_FLOAT_16", "RGB_FLOAT_16", "RG_FLOAT_16", "R_FLOAT_16", "BC6S", "BC6U", "RGBA_UNORM_10_10_10_2", "RGB_FLOAT_11_11_10", "R_INT_16", "RG_INT_16", "RGBA_INT_16", "R_UINT_16", "RG_UINT_16", "RGBA_UINT_16", "RG_NORM_16", "RGBA_NORM_16", "R_UNORM_16", "RG_UNORM_16", "RGBA_UNORM_16", "R_NORM_16" -> - 2; + 16; case "RGBA_FLOAT_32", "RGB_FLOAT_32", "RG_FLOAT_32", "R_FLOAT_32", "R_INT_32", "RG_INT_32", "RGBA_INT_32", "R_UINT_32", "RG_UINT_32", "RGBA_UINT_32", "RGBA_UNORM_32" -> - 4; + 32; default -> 0; }; } From 51da4ce300540b6376beab2e7e34531a71b29f73 Mon Sep 17 00:00:00 2001 From: REDxEYE Date: Tue, 28 Nov 2023 20:34:14 +0300 Subject: [PATCH 08/19] Add check for bpc in png exporter only allowing 8 bits per channel images --- .../ui/data/viewer/texture/exporter/TextureExporterPNG.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/decima-ext-texture-viewer/src/main/java/com/shade/decima/ui/data/viewer/texture/exporter/TextureExporterPNG.java b/modules/decima-ext-texture-viewer/src/main/java/com/shade/decima/ui/data/viewer/texture/exporter/TextureExporterPNG.java index b29ef8604..37d61b553 100644 --- a/modules/decima-ext-texture-viewer/src/main/java/com/shade/decima/ui/data/viewer/texture/exporter/TextureExporterPNG.java +++ b/modules/decima-ext-texture-viewer/src/main/java/com/shade/decima/ui/data/viewer/texture/exporter/TextureExporterPNG.java @@ -32,7 +32,7 @@ public void export(@NotNull ImageProvider provider, @NotNull Set