From 309665c8ffa9242030889183716953149fa8f082 Mon Sep 17 00:00:00 2001 From: Adam Date: Sun, 24 Jun 2018 15:54:55 -0400 Subject: [PATCH] modelviewer: add basic animation support --- .../java/net/runelite/cache/NpcManager.java | 25 +- .../cache/definitions/FrameDefinition.java | 5 +- .../cache/definitions/FramemapDefinition.java | 4 +- .../cache/definitions/ModelDefinition.java | 227 ++++++++++++++++++ .../cache/definitions/SequenceDefinition.java | 2 +- .../definitions/loaders/FrameLoader.java | 15 +- .../definitions/loaders/FramemapLoader.java | 12 +- .../definitions/loaders/ModelLoader.java | 1 + .../java/net/runelite/cache/FrameDumper.java | 32 ++- .../runelite/modelviewer/ModelManager.java | 42 ++-- .../net/runelite/modelviewer/ModelViewer.java | 181 +++++++++++--- 11 files changed, 465 insertions(+), 81 deletions(-) diff --git a/cache/src/main/java/net/runelite/cache/NpcManager.java b/cache/src/main/java/net/runelite/cache/NpcManager.java index 8a7b8691cc..3124eb455f 100644 --- a/cache/src/main/java/net/runelite/cache/NpcManager.java +++ b/cache/src/main/java/net/runelite/cache/NpcManager.java @@ -26,8 +26,10 @@ package net.runelite.cache; import java.io.File; import java.io.IOException; -import java.util.ArrayList; -import java.util.List; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; import net.runelite.cache.definitions.NpcDefinition; import net.runelite.cache.definitions.exporters.NpcExporter; import net.runelite.cache.definitions.loaders.NpcLoader; @@ -42,7 +44,7 @@ import net.runelite.cache.util.IDClass; public class NpcManager { private final Store store; - private final List npcs = new ArrayList<>(); + private final Map npcs = new HashMap<>(); public NpcManager(Store store) { @@ -63,20 +65,25 @@ public class NpcManager for (FSFile f : files.getFiles()) { NpcDefinition npc = loader.load(f.getFileId(), f.getContents()); - npcs.add(npc); + npcs.put(f.getFileId(), npc); } } - - public List getNpcs() + + public Collection getNpcs() { - return npcs; + return Collections.unmodifiableCollection(npcs.values()); + } + + public NpcDefinition get(int npcId) + { + return npcs.get(npcId); } public void dump(File out) throws IOException { out.mkdirs(); - for (NpcDefinition def : npcs) + for (NpcDefinition def : npcs.values()) { NpcExporter exporter = new NpcExporter(def); @@ -90,7 +97,7 @@ public class NpcManager java.mkdirs(); try (IDClass ids = IDClass.create(java, "NpcID")) { - for (NpcDefinition def : npcs) + for (NpcDefinition def : npcs.values()) { if (def.name.equalsIgnoreCase("NULL")) { diff --git a/cache/src/main/java/net/runelite/cache/definitions/FrameDefinition.java b/cache/src/main/java/net/runelite/cache/definitions/FrameDefinition.java index 2b68a02e87..07fb2c7e31 100644 --- a/cache/src/main/java/net/runelite/cache/definitions/FrameDefinition.java +++ b/cache/src/main/java/net/runelite/cache/definitions/FrameDefinition.java @@ -26,11 +26,12 @@ package net.runelite.cache.definitions; public class FrameDefinition { + public int id; // file id public FramemapDefinition framemap; public int[] translator_x; public int[] translator_y; public int[] translator_z; - public int field1310 = -1; + public int translatorCount = -1; public int[] indexFrameIds; - public boolean field1315; + public boolean showing; } diff --git a/cache/src/main/java/net/runelite/cache/definitions/FramemapDefinition.java b/cache/src/main/java/net/runelite/cache/definitions/FramemapDefinition.java index 05e06635d8..250204d24b 100644 --- a/cache/src/main/java/net/runelite/cache/definitions/FramemapDefinition.java +++ b/cache/src/main/java/net/runelite/cache/definitions/FramemapDefinition.java @@ -27,7 +27,7 @@ package net.runelite.cache.definitions; public class FramemapDefinition { public int id; - public int[] field1456; - public int[][] field1457; + public int[] types; + public int[][] frameMaps; public int length; } diff --git a/cache/src/main/java/net/runelite/cache/definitions/ModelDefinition.java b/cache/src/main/java/net/runelite/cache/definitions/ModelDefinition.java index 23c1f4603f..efcac3c3fd 100644 --- a/cache/src/main/java/net/runelite/cache/definitions/ModelDefinition.java +++ b/cache/src/main/java/net/runelite/cache/definitions/ModelDefinition.java @@ -1,5 +1,6 @@ package net.runelite.cache.definitions; +import java.util.Arrays; import net.runelite.cache.models.CircularAngle; import net.runelite.cache.models.FaceNormal; import net.runelite.cache.models.VertexNormal; @@ -47,6 +48,14 @@ public class ModelDefinition public byte[] aByteArray2580; public short[] aShortArray2586; + private transient int[][] vertexGroups; + + private transient int[] origVX; + private transient int[] origVY; + private transient int[] origVZ; + + public static transient int animOffsetX, animOffsetY, animOffsetZ; + public void computeNormals() { if (this.vertexNormals != null) @@ -259,6 +268,43 @@ public class ModelDefinition } } + public void computeAnimationTables() + { + if (this.vertexSkins != null) + { + int[] groupCounts = new int[256]; + int numGroups = 0; + int var3, var4; + + for (var3 = 0; var3 < this.vertexCount; ++var3) + { + var4 = this.vertexSkins[var3]; + ++groupCounts[var4]; + if (var4 > numGroups) + { + numGroups = var4; + } + } + + this.vertexGroups = new int[numGroups + 1][]; + + for (var3 = 0; var3 <= numGroups; ++var3) + { + this.vertexGroups[var3] = new int[groupCounts[var3]]; + groupCounts[var3] = 0; + } + + for (var3 = 0; var3 < this.vertexCount; this.vertexGroups[var4][groupCounts[var4]++] = var3++) + { + var4 = this.vertexSkins[var3]; + } + + this.vertexSkins = null; + } + + // triangleSkinValues is here + } + public void rotate(int orientation) { int sin = CircularAngle.SINE[orientation]; @@ -276,6 +322,187 @@ public class ModelDefinition reset(); } + public void resetAnim() + { + if (origVX == null) + { + return; + } + + System.arraycopy(origVX, 0, vertexPositionsX, 0, origVX.length); + System.arraycopy(origVY, 0, vertexPositionsY, 0, origVY.length); + System.arraycopy(origVZ, 0, vertexPositionsZ, 0, origVZ.length); + } + + public void animate(int type, int[] frameMap, int dx, int dy, int dz) + { + if (origVX == null) + { + origVX = Arrays.copyOf(vertexPositionsX, vertexPositionsX.length); + origVY = Arrays.copyOf(vertexPositionsY, vertexPositionsY.length); + origVZ = Arrays.copyOf(vertexPositionsZ, vertexPositionsZ.length); + } + + final int[] verticesX = vertexPositionsX; + final int[] verticesY = vertexPositionsY; + final int[] verticesZ = vertexPositionsZ; + int var6 = frameMap.length; + int var7; + int var8; + int var11; + int var12; + if (type == 0) + { + var7 = 0; + animOffsetX = 0; + animOffsetY = 0; + animOffsetZ = 0; + + for (var8 = 0; var8 < var6; ++var8) + { + int var9 = frameMap[var8]; + if (var9 < this.vertexGroups.length) + { + int[] var10 = this.vertexGroups[var9]; + + for (var11 = 0; var11 < var10.length; ++var11) + { + var12 = var10[var11]; + animOffsetX += verticesX[var12]; + animOffsetY += verticesY[var12]; + animOffsetZ += verticesZ[var12]; + ++var7; + } + } + } + + if (var7 > 0) + { + animOffsetX = dx + animOffsetX / var7; + animOffsetY = dy + animOffsetY / var7; + animOffsetZ = dz + animOffsetZ / var7; + } + else + { + animOffsetX = dx; + animOffsetY = dy; + animOffsetZ = dz; + } + + } + else + { + int[] var18; + int var19; + if (type == 1) + { + for (var7 = 0; var7 < var6; ++var7) + { + var8 = frameMap[var7]; + if (var8 < this.vertexGroups.length) + { + var18 = this.vertexGroups[var8]; + + for (var19 = 0; var19 < var18.length; ++var19) + { + var11 = var18[var19]; + verticesX[var11] += dx; + verticesY[var11] += dy; + verticesZ[var11] += dz; + } + } + } + + } + else if (type == 2) + { + for (var7 = 0; var7 < var6; ++var7) + { + var8 = frameMap[var7]; + if (var8 < this.vertexGroups.length) + { + var18 = this.vertexGroups[var8]; + + for (var19 = 0; var19 < var18.length; ++var19) + { + var11 = var18[var19]; + verticesX[var11] -= animOffsetX; + verticesY[var11] -= animOffsetY; + verticesZ[var11] -= animOffsetZ; + var12 = (dx & 255) * 8; + int var13 = (dy & 255) * 8; + int var14 = (dz & 255) * 8; + int var15; + int var16; + int var17; + if (var14 != 0) + { + var15 = CircularAngle.SINE[var14]; + var16 = CircularAngle.COSINE[var14]; + var17 = var15 * verticesY[var11] + var16 * verticesX[var11] >> 16; + verticesY[var11] = var16 * verticesY[var11] - var15 * verticesX[var11] >> 16; + verticesX[var11] = var17; + } + + if (var12 != 0) + { + var15 = CircularAngle.SINE[var12]; + var16 = CircularAngle.COSINE[var12]; + var17 = var16 * verticesY[var11] - var15 * verticesZ[var11] >> 16; + verticesZ[var11] = var15 * verticesY[var11] + var16 * verticesZ[var11] >> 16; + verticesY[var11] = var17; + } + + if (var13 != 0) + { + var15 = CircularAngle.SINE[var13]; + var16 = CircularAngle.COSINE[var13]; + var17 = var15 * verticesZ[var11] + var16 * verticesX[var11] >> 16; + verticesZ[var11] = var16 * verticesZ[var11] - var15 * verticesX[var11] >> 16; + verticesX[var11] = var17; + } + + verticesX[var11] += animOffsetX; + verticesY[var11] += animOffsetY; + verticesZ[var11] += animOffsetZ; + } + } + } + + } + else if (type == 3) + { + for (var7 = 0; var7 < var6; ++var7) + { + var8 = frameMap[var7]; + if (var8 < this.vertexGroups.length) + { + var18 = this.vertexGroups[var8]; + + for (var19 = 0; var19 < var18.length; ++var19) + { + var11 = var18[var19]; + verticesX[var11] -= animOffsetX; + verticesY[var11] -= animOffsetY; + verticesZ[var11] -= animOffsetZ; + verticesX[var11] = dx * verticesX[var11] / 128; + verticesY[var11] = dy * verticesY[var11] / 128; + verticesZ[var11] = dz * verticesZ[var11] / 128; + verticesX[var11] += animOffsetX; + verticesY[var11] += animOffsetY; + verticesZ[var11] += animOffsetZ; + } + } + } + + } + else if (type == 5) + { + // alpha animation + } + } + } + public void method1493() { int var1; diff --git a/cache/src/main/java/net/runelite/cache/definitions/SequenceDefinition.java b/cache/src/main/java/net/runelite/cache/definitions/SequenceDefinition.java index 7f29ee7b49..257be8398a 100644 --- a/cache/src/main/java/net/runelite/cache/definitions/SequenceDefinition.java +++ b/cache/src/main/java/net/runelite/cache/definitions/SequenceDefinition.java @@ -32,7 +32,7 @@ public class SequenceDefinition { @Getter private final int id; - public int[] frameIDs; + public int[] frameIDs; // top 16 bits are FrameDefinition ids public int[] field3048; public int[] frameLenghts; public int rightHandItem = -1; diff --git a/cache/src/main/java/net/runelite/cache/definitions/loaders/FrameLoader.java b/cache/src/main/java/net/runelite/cache/definitions/loaders/FrameLoader.java index bfef12669b..5ec512d62f 100644 --- a/cache/src/main/java/net/runelite/cache/definitions/loaders/FrameLoader.java +++ b/cache/src/main/java/net/runelite/cache/definitions/loaders/FrameLoader.java @@ -30,12 +30,13 @@ import net.runelite.cache.io.InputStream; public class FrameLoader { - public FrameDefinition load(FramemapDefinition framemap, byte[] b) + public FrameDefinition load(FramemapDefinition framemap, int id, byte[] b) { FrameDefinition def = new FrameDefinition(); InputStream in = new InputStream(b); InputStream data = new InputStream(b); + def.id = id; def.framemap = framemap; int framemapArchiveIndex = in.readUnsignedShort(); @@ -59,11 +60,11 @@ public class FrameLoader continue; } - if (def.framemap.field1456[i] != 0) + if (def.framemap.types[i] != 0) { for (int var10 = i - 1; var10 > lastI; --var10) { - if (def.framemap.field1456[var10] == 0) + if (def.framemap.types[var10] == 0) { indexFrameIds[index] = var10; scratchTranslatorX[index] = 0; @@ -77,7 +78,7 @@ public class FrameLoader indexFrameIds[index] = i; short var11 = 0; - if (def.framemap.field1456[i] == 3) + if (def.framemap.types[i] == 3) { var11 = 128; } @@ -111,9 +112,9 @@ public class FrameLoader lastI = i; ++index; - if (def.framemap.field1456[i] == 5) + if (def.framemap.types[i] == 5) { - def.field1315 = true; + def.showing = true; } } @@ -122,7 +123,7 @@ public class FrameLoader throw new RuntimeException(); } - def.field1310 = index; + def.translatorCount = index; def.indexFrameIds = new int[index]; def.translator_x = new int[index]; def.translator_y = new int[index]; diff --git a/cache/src/main/java/net/runelite/cache/definitions/loaders/FramemapLoader.java b/cache/src/main/java/net/runelite/cache/definitions/loaders/FramemapLoader.java index ba2038c44f..b649af8e47 100644 --- a/cache/src/main/java/net/runelite/cache/definitions/loaders/FramemapLoader.java +++ b/cache/src/main/java/net/runelite/cache/definitions/loaders/FramemapLoader.java @@ -37,24 +37,24 @@ public class FramemapLoader def.id = id; def.length = in.readUnsignedByte(); - def.field1456 = new int[def.length]; - def.field1457 = new int[def.length][]; + def.types = new int[def.length]; + def.frameMaps = new int[def.length][]; for (int i = 0; i < def.length; ++i) { - def.field1456[i] = in.readUnsignedByte(); + def.types[i] = in.readUnsignedByte(); } for (int i = 0; i < def.length; ++i) { - def.field1457[i] = new int[in.readUnsignedByte()]; + def.frameMaps[i] = new int[in.readUnsignedByte()]; } for (int i = 0; i < def.length; ++i) { - for (int j = 0; j < def.field1457[i].length; ++j) + for (int j = 0; j < def.frameMaps[i].length; ++j) { - def.field1457[i][j] = in.readUnsignedByte(); + def.frameMaps[i][j] = in.readUnsignedByte(); } } diff --git a/cache/src/main/java/net/runelite/cache/definitions/loaders/ModelLoader.java b/cache/src/main/java/net/runelite/cache/definitions/loaders/ModelLoader.java index 312fcc7f8c..4529f8a069 100644 --- a/cache/src/main/java/net/runelite/cache/definitions/loaders/ModelLoader.java +++ b/cache/src/main/java/net/runelite/cache/definitions/loaders/ModelLoader.java @@ -21,6 +21,7 @@ public class ModelLoader def.computeNormals(); def.computeTextureUVCoordinates(); + def.computeAnimationTables(); return def; } diff --git a/cache/src/test/java/net/runelite/cache/FrameDumper.java b/cache/src/test/java/net/runelite/cache/FrameDumper.java index 6142b85a9e..2bd7e2ca17 100644 --- a/cache/src/test/java/net/runelite/cache/FrameDumper.java +++ b/cache/src/test/java/net/runelite/cache/FrameDumper.java @@ -37,10 +37,14 @@ import net.runelite.cache.definitions.FramemapDefinition; import net.runelite.cache.definitions.loaders.FrameLoader; import net.runelite.cache.definitions.loaders.FramemapLoader; import net.runelite.cache.fs.Archive; +import net.runelite.cache.fs.ArchiveFiles; +import net.runelite.cache.fs.FSFile; import net.runelite.cache.fs.Index; import net.runelite.cache.fs.Storage; import net.runelite.cache.fs.Store; +import org.junit.Ignore; import org.junit.Rule; +import org.junit.Test; import org.junit.rules.TemporaryFolder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -54,7 +58,8 @@ public class FrameDumper private final Gson gson = new GsonBuilder().setPrettyPrinting().create(); - //@Test + @Test + @Ignore public void extract() throws IOException { File base = StoreLocation.LOCATION, @@ -75,21 +80,26 @@ public class FrameDumper List frames = new ArrayList<>(); byte[] archiveData = storage.loadArchive(archive); - byte[] contents = archive.decompress(archiveData); - int framemapArchiveId = (contents[0] & 0xff) << 8 | contents[1] & 0xff; + ArchiveFiles archiveFiles = archive.getFiles(archiveData); + for (FSFile archiveFile : archiveFiles.getFiles()) + { + byte[] contents = archiveFile.getContents(); - Archive framemapArchive = framemapIndex.getArchives().get(framemapArchiveId); - archiveData = storage.loadArchive(framemapArchive); - byte[] framemapContents = framemapArchive.decompress(archiveData); + int framemapArchiveId = (contents[0] & 0xff) << 8 | contents[1] & 0xff; - FramemapLoader fmloader = new FramemapLoader(); - FramemapDefinition framemap = fmloader.load(0, framemapContents); + Archive framemapArchive = framemapIndex.getArchives().get(framemapArchiveId); + archiveData = storage.loadArchive(framemapArchive); + byte[] framemapContents = framemapArchive.decompress(archiveData); - FrameLoader frameLoader = new FrameLoader(); - FrameDefinition frame = frameLoader.load(framemap, contents); + FramemapLoader fmloader = new FramemapLoader(); + FramemapDefinition framemap = fmloader.load(framemapArchive.getArchiveId(), framemapContents); - frames.add(frame); + FrameLoader frameLoader = new FrameLoader(); + FrameDefinition frame = frameLoader.load(framemap, archiveFile.getFileId(), contents); + + frames.add(frame); + } Files.write(gson.toJson(frames), new File(outDir, archive.getArchiveId() + ".json"), Charset.defaultCharset()); ++count; diff --git a/model-viewer/src/main/java/net/runelite/modelviewer/ModelManager.java b/model-viewer/src/main/java/net/runelite/modelviewer/ModelManager.java index b24faa08ec..3a57914468 100644 --- a/model-viewer/src/main/java/net/runelite/modelviewer/ModelManager.java +++ b/model-viewer/src/main/java/net/runelite/modelviewer/ModelManager.java @@ -24,14 +24,17 @@ */ package net.runelite.modelviewer; -import java.io.File; import java.io.IOException; -import java.nio.file.Files; import java.util.HashMap; import java.util.Map; +import net.runelite.cache.IndexType; import net.runelite.cache.definitions.ModelDefinition; import net.runelite.cache.definitions.ObjectDefinition; import net.runelite.cache.definitions.loaders.ModelLoader; +import net.runelite.cache.fs.Archive; +import net.runelite.cache.fs.Index; +import net.runelite.cache.fs.Storage; +import net.runelite.cache.fs.Store; import net.runelite.cache.region.Location; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -40,16 +43,23 @@ public class ModelManager { private static final Logger logger = LoggerFactory.getLogger(ModelManager.class); - private static Map models = new HashMap<>(); + private final Store store; + private final Map models = new HashMap<>(); - public static ModelDefinition getModel(int id, ObjectDefinition object, Location location) + public ModelManager(Store store) + { + this.store = store; + } + + public ModelDefinition getModel(int id, ObjectDefinition object, Location location) { LocationKey key; - int rot = location.getOrientation(); + Integer rot = null; if (location != null) { + rot = location.getOrientation(); key = new LocationKey(id, location.getType(), rot); } else @@ -63,12 +73,22 @@ public class ModelManager return md; } + Storage storage = store.getStorage(); + Index index = store.getIndex(IndexType.MODELS); + + Archive modelArchive = index.getArchive(id); + byte[] contents; try { - byte[] b = Files.readAllBytes(new File("models/" + id + ".model").toPath()); + contents = modelArchive.decompress(storage.loadArchive(modelArchive)); + } + catch (IOException e) + { + throw new RuntimeException(e); + } - ModelLoader loader = new ModelLoader(); - md = loader.load(id, b); + ModelLoader loader = new ModelLoader(); + md = loader.load(modelArchive.getArchiveId(), contents); if (object != null && location != null) { @@ -78,12 +98,6 @@ public class ModelManager models.put(key, md); return md; - } - catch (IOException ex) - { - logger.warn(null, ex); - return null; - } } // this logic is from method3697 in 140 diff --git a/model-viewer/src/main/java/net/runelite/modelviewer/ModelViewer.java b/model-viewer/src/main/java/net/runelite/modelviewer/ModelViewer.java index 759dbae155..3a999c2844 100644 --- a/model-viewer/src/main/java/net/runelite/modelviewer/ModelViewer.java +++ b/model-viewer/src/main/java/net/runelite/modelviewer/ModelViewer.java @@ -24,34 +24,46 @@ */ package net.runelite.modelviewer; -import com.google.gson.Gson; +import com.google.common.collect.HashMultimap; +import com.google.common.collect.Multimap; import java.awt.Color; import java.io.File; -import java.io.FileInputStream; -import java.io.InputStreamReader; +import java.io.IOException; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Optional; +import net.runelite.cache.ConfigType; import net.runelite.cache.IndexType; +import net.runelite.cache.NpcManager; import net.runelite.cache.ObjectManager; import net.runelite.cache.OverlayManager; import net.runelite.cache.SpriteManager; import net.runelite.cache.TextureManager; import net.runelite.cache.UnderlayManager; +import net.runelite.cache.definitions.FrameDefinition; +import net.runelite.cache.definitions.FramemapDefinition; import net.runelite.cache.definitions.KitDefinition; import net.runelite.cache.definitions.LocationsDefinition; import net.runelite.cache.definitions.MapDefinition; import net.runelite.cache.definitions.ModelDefinition; import net.runelite.cache.definitions.NpcDefinition; import net.runelite.cache.definitions.ObjectDefinition; +import net.runelite.cache.definitions.SequenceDefinition; import net.runelite.cache.definitions.SpriteDefinition; import net.runelite.cache.definitions.TextureDefinition; +import net.runelite.cache.definitions.loaders.FrameLoader; +import net.runelite.cache.definitions.loaders.FramemapLoader; import net.runelite.cache.definitions.loaders.LocationsLoader; import net.runelite.cache.definitions.loaders.MapLoader; +import net.runelite.cache.definitions.loaders.SequenceLoader; import net.runelite.cache.fs.Archive; +import net.runelite.cache.fs.ArchiveFiles; +import net.runelite.cache.fs.FSFile; import net.runelite.cache.fs.Index; import net.runelite.cache.fs.Storage; import net.runelite.cache.fs.Store; @@ -87,13 +99,15 @@ public class ModelViewer * size of a tile in local coordinates */ private static final int TILE_SCALE = 128; - private static final int HEIGHT_MOD = 4; private static ObjectManager objectManager; private static TextureManager textureManager; private static SpriteManager spriteManager; + private static ModelManager modelManager; private static Map textures = new HashMap<>(); + private static Map seqs = new HashMap<>(); + private static Multimap frames = HashMultimap.create(); public static void main(String[] args) throws Exception { @@ -101,8 +115,6 @@ public class ModelViewer options.addOption(null, "store", true, "store directory"); - options.addOption(null, "npcdir", true, "npc directory"); - options.addOption(null, "mapdir", true, "maps directory"); options.addOption(null, "objectdir", true, "objects directory"); options.addOption(null, "npc", true, "npc to render"); @@ -110,19 +122,18 @@ public class ModelViewer options.addOption(null, "model", true, "model to render"); options.addOption(null, "map", true, "map region to render"); options.addOption(null, "kits", true, "kits to render"); + options.addOption(null, "seq", true, "sequence id"); CommandLineParser parser = new DefaultParser(); CommandLine cmd = parser.parse(options, args); - String npcdir = cmd.getOptionValue("npcdir"); - String mapdir = cmd.getOptionValue("mapdir"); - String objectdir = cmd.getOptionValue("objectdir"); - NpcDefinition npcdef = null; ObjectDefinition objdef = null; Store store = null; UnderlayManager underlayManager = null; OverlayManager overlayManager = null; + NpcManager npcManager = null; + Integer seq = null; List models = new ArrayList<>(); Region region = null; @@ -146,27 +157,35 @@ public class ModelViewer spriteManager = new SpriteManager(store); spriteManager.load(); + + npcManager = new NpcManager(store); + npcManager.load(); + + modelManager = new ModelManager(store); + + if (cmd.hasOption("seq")) + { + loadSeqs(store); + loadFrames(store); + } } if (cmd.hasOption("model")) { // render model String model = cmd.getOptionValue("model"); - ModelDefinition md = ModelManager.getModel(Integer.parseInt(model), null, null); + ModelDefinition md = modelManager.getModel(Integer.parseInt(model), null, null); models.add(md); } if (cmd.hasOption("npc")) { String npc = cmd.getOptionValue("npc"); - try (FileInputStream fin = new FileInputStream(npcdir + "/" + npc + ".json")) - { - npcdef = new Gson().fromJson(new InputStreamReader(fin), NpcDefinition.class); - } + npcdef = npcManager.get(Integer.parseInt(npc)); for (int model : npcdef.models) { - ModelDefinition md = ModelManager.getModel(model, null, null); + ModelDefinition md = modelManager.getModel(model, null, null); models.add(md); } } @@ -174,14 +193,11 @@ public class ModelViewer { String obj = cmd.getOptionValue("object"); - try (FileInputStream fin = new FileInputStream(objectdir + "/" + obj + ".json")) - { - objdef = new Gson().fromJson(new InputStreamReader(fin), ObjectDefinition.class); - } + objdef = objectManager.getObject(Integer.parseInt(obj)); for (int model : objdef.getObjectModels()) { - ModelDefinition md = ModelManager.getModel(model, null, null); + ModelDefinition md = modelManager.getModel(model, null, null); models.add(md); } } @@ -228,11 +244,15 @@ public class ModelViewer KitDefinition kit = KitManager.getKit(kitId); for (int model : kit.modelIds) { - ModelDefinition md = ModelManager.getModel(model, null, null); + ModelDefinition md = modelManager.getModel(model, null, null); models.add(md); } } } + if (cmd.hasOption("seq")) + { + seq = Integer.parseInt(cmd.getOptionValue("seq")); + } Display.setDisplayMode(new DisplayMode(800, 600)); Display.setTitle("Model Viewer"); @@ -259,14 +279,66 @@ public class ModelViewer long last = 0; Camera camera = new Camera(); - Scene scene = new Scene(underlayManager, overlayManager); - scene.loadRegion(region); + Scene scene = null; + if (region != null) + { + scene = new Scene(underlayManager, overlayManager); + scene.loadRegion(region); + } + + SequenceDefinition sequenceDefinition = null; + int frameCount = 0; + int frameLength = 0; + if (seq != null) + { + sequenceDefinition = seqs.get(seq); + frameCount = 0; + frameLength = sequenceDefinition.frameLenghts[0]; + } while (!Display.isCloseRequested()) { // Clear the screen and depth buffer GL11.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT); + if (seq != null) + { + if (frameLength-- <= 0) + { + frameCount++; + frameLength = sequenceDefinition.frameLenghts[frameCount % sequenceDefinition.frameIDs.length]; + } + + int seqFrameId = sequenceDefinition.frameIDs[frameCount % sequenceDefinition.frameIDs.length]; + Collection frames = ModelViewer.frames.get(seqFrameId >>> 16); + int frameFileId = seqFrameId & 65535; + + Optional first = frames.stream().filter(frame -> frame.id == frameFileId).findFirst(); + FrameDefinition frame = first.get(); + FramemapDefinition framemap = frame.framemap; + + ModelDefinition.animOffsetX = ModelDefinition.animOffsetY = ModelDefinition.animOffsetZ = 0; + + for (ModelDefinition def : models) + { + def.resetAnim(); + } + for (int i = 0; i < frame.translatorCount; ++i) + { + int type = frame.indexFrameIds[i]; + int fmType = framemap.types[type]; + int[] fm = framemap.frameMaps[type]; + int dx = frame.translator_x[i]; + int dy = frame.translator_y[i]; + int dz = frame.translator_z[i]; + + for (ModelDefinition def : models) + { + def.animate(fmType, fm, dx, dy, dz); + } + } + } + for (ModelDefinition def : models) { short[] recolourToFind = null, recolourToReplace = null; @@ -284,11 +356,13 @@ public class ModelViewer drawModel(def, recolourToFind, recolourToReplace); } - drawScene(region, scene); - //drawRegion(region); + if (region != null) + { + drawScene(region, scene); + } Display.update(); - Display.sync(20); // fps + Display.sync(50); // fps long delta = System.currentTimeMillis() - last; last = System.currentTimeMillis(); @@ -611,7 +685,7 @@ public class ModelViewer int regionX = objectPos.getX() - region.getBaseX(); int regionY = objectPos.getY() - region.getBaseY(); - int height = -region.getTileHeight(objectPos.getZ(), regionX, regionY); // / HEIGHT_MOD; + int height = -region.getTileHeight(objectPos.getZ(), regionX, regionY); GL11.glMatrixMode(GL11.GL_MODELVIEW); // TILE_SCALE/2 to draw the object from the center of the tile it is on @@ -624,7 +698,7 @@ public class ModelViewer continue; } - ModelDefinition md = ModelManager.getModel(object.getObjectModels()[i], object, location); + ModelDefinition md = modelManager.getModel(object.getObjectModels()[i], object, location); if (md == null) { @@ -702,6 +776,55 @@ public class ModelViewer return texture; } + private static void loadSeqs(Store store) throws IOException + { + Storage storage = store.getStorage(); + Index index = store.getIndex(IndexType.CONFIGS); + Archive archive = index.getArchive(ConfigType.SEQUENCE.getId()); + + byte[] archiveData = storage.loadArchive(archive); + ArchiveFiles files = archive.getFiles(archiveData); + + for (FSFile file : files.getFiles()) + { + SequenceLoader loader = new SequenceLoader(); + SequenceDefinition seq = loader.load(file.getFileId(), file.getContents()); + + seqs.put(file.getFileId(), seq); + } + } + + private static void loadFrames(Store store) throws IOException + { + Storage storage = store.getStorage(); + Index frameIndex = store.getIndex(IndexType.FRAMES); + Index framemapIndex = store.getIndex(IndexType.FRAMEMAPS); + + for (Archive archive : frameIndex.getArchives()) + { + byte[] archiveData = storage.loadArchive(archive); + ArchiveFiles archiveFiles = archive.getFiles(archiveData); + for (FSFile archiveFile : archiveFiles.getFiles()) + { + byte[] contents = archiveFile.getContents(); + + int framemapArchiveId = (contents[0] & 0xff) << 8 | contents[1] & 0xff; + + Archive framemapArchive = framemapIndex.getArchives().get(framemapArchiveId); + archiveData = storage.loadArchive(framemapArchive); + byte[] framemapContents = framemapArchive.decompress(archiveData); + + FramemapLoader fmloader = new FramemapLoader(); + FramemapDefinition framemap = fmloader.load(framemapArchive.getArchiveId(), framemapContents); + + FrameLoader frameLoader = new FrameLoader(); + FrameDefinition frame = frameLoader.load(framemap, archiveFile.getFileId(), contents); + + frames.put(archive.getArchiveId(), frame); + } + } + } + // found these two functions here https://www.rune-server.org/runescape-development/rs2-client/tools/589900-rs2-hsb-color-picker.html public static int RGB_to_RS2HSB(int red, int green, int blue) {