modelviewer: add basic animation support

This commit is contained in:
Adam
2018-06-24 15:54:55 -04:00
parent 5f558657a8
commit 309665c8ff
11 changed files with 465 additions and 81 deletions

View File

@@ -26,8 +26,10 @@ package net.runelite.cache;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.Collection;
import java.util.List; import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import net.runelite.cache.definitions.NpcDefinition; import net.runelite.cache.definitions.NpcDefinition;
import net.runelite.cache.definitions.exporters.NpcExporter; import net.runelite.cache.definitions.exporters.NpcExporter;
import net.runelite.cache.definitions.loaders.NpcLoader; import net.runelite.cache.definitions.loaders.NpcLoader;
@@ -42,7 +44,7 @@ import net.runelite.cache.util.IDClass;
public class NpcManager public class NpcManager
{ {
private final Store store; private final Store store;
private final List<NpcDefinition> npcs = new ArrayList<>(); private final Map<Integer, NpcDefinition> npcs = new HashMap<>();
public NpcManager(Store store) public NpcManager(Store store)
{ {
@@ -63,20 +65,25 @@ public class NpcManager
for (FSFile f : files.getFiles()) for (FSFile f : files.getFiles())
{ {
NpcDefinition npc = loader.load(f.getFileId(), f.getContents()); NpcDefinition npc = loader.load(f.getFileId(), f.getContents());
npcs.add(npc); npcs.put(f.getFileId(), npc);
} }
} }
public List<NpcDefinition> getNpcs() public Collection<NpcDefinition> getNpcs()
{ {
return npcs; return Collections.unmodifiableCollection(npcs.values());
}
public NpcDefinition get(int npcId)
{
return npcs.get(npcId);
} }
public void dump(File out) throws IOException public void dump(File out) throws IOException
{ {
out.mkdirs(); out.mkdirs();
for (NpcDefinition def : npcs) for (NpcDefinition def : npcs.values())
{ {
NpcExporter exporter = new NpcExporter(def); NpcExporter exporter = new NpcExporter(def);
@@ -90,7 +97,7 @@ public class NpcManager
java.mkdirs(); java.mkdirs();
try (IDClass ids = IDClass.create(java, "NpcID")) try (IDClass ids = IDClass.create(java, "NpcID"))
{ {
for (NpcDefinition def : npcs) for (NpcDefinition def : npcs.values())
{ {
if (def.name.equalsIgnoreCase("NULL")) if (def.name.equalsIgnoreCase("NULL"))
{ {

View File

@@ -26,11 +26,12 @@ package net.runelite.cache.definitions;
public class FrameDefinition public class FrameDefinition
{ {
public int id; // file id
public FramemapDefinition framemap; public FramemapDefinition framemap;
public int[] translator_x; public int[] translator_x;
public int[] translator_y; public int[] translator_y;
public int[] translator_z; public int[] translator_z;
public int field1310 = -1; public int translatorCount = -1;
public int[] indexFrameIds; public int[] indexFrameIds;
public boolean field1315; public boolean showing;
} }

View File

@@ -27,7 +27,7 @@ package net.runelite.cache.definitions;
public class FramemapDefinition public class FramemapDefinition
{ {
public int id; public int id;
public int[] field1456; public int[] types;
public int[][] field1457; public int[][] frameMaps;
public int length; public int length;
} }

View File

@@ -1,5 +1,6 @@
package net.runelite.cache.definitions; package net.runelite.cache.definitions;
import java.util.Arrays;
import net.runelite.cache.models.CircularAngle; import net.runelite.cache.models.CircularAngle;
import net.runelite.cache.models.FaceNormal; import net.runelite.cache.models.FaceNormal;
import net.runelite.cache.models.VertexNormal; import net.runelite.cache.models.VertexNormal;
@@ -47,6 +48,14 @@ public class ModelDefinition
public byte[] aByteArray2580; public byte[] aByteArray2580;
public short[] aShortArray2586; 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() public void computeNormals()
{ {
if (this.vertexNormals != null) 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) public void rotate(int orientation)
{ {
int sin = CircularAngle.SINE[orientation]; int sin = CircularAngle.SINE[orientation];
@@ -276,6 +322,187 @@ public class ModelDefinition
reset(); 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() public void method1493()
{ {
int var1; int var1;

View File

@@ -32,7 +32,7 @@ public class SequenceDefinition
{ {
@Getter @Getter
private final int id; private final int id;
public int[] frameIDs; public int[] frameIDs; // top 16 bits are FrameDefinition ids
public int[] field3048; public int[] field3048;
public int[] frameLenghts; public int[] frameLenghts;
public int rightHandItem = -1; public int rightHandItem = -1;

View File

@@ -30,12 +30,13 @@ import net.runelite.cache.io.InputStream;
public class FrameLoader public class FrameLoader
{ {
public FrameDefinition load(FramemapDefinition framemap, byte[] b) public FrameDefinition load(FramemapDefinition framemap, int id, byte[] b)
{ {
FrameDefinition def = new FrameDefinition(); FrameDefinition def = new FrameDefinition();
InputStream in = new InputStream(b); InputStream in = new InputStream(b);
InputStream data = new InputStream(b); InputStream data = new InputStream(b);
def.id = id;
def.framemap = framemap; def.framemap = framemap;
int framemapArchiveIndex = in.readUnsignedShort(); int framemapArchiveIndex = in.readUnsignedShort();
@@ -59,11 +60,11 @@ public class FrameLoader
continue; continue;
} }
if (def.framemap.field1456[i] != 0) if (def.framemap.types[i] != 0)
{ {
for (int var10 = i - 1; var10 > lastI; --var10) for (int var10 = i - 1; var10 > lastI; --var10)
{ {
if (def.framemap.field1456[var10] == 0) if (def.framemap.types[var10] == 0)
{ {
indexFrameIds[index] = var10; indexFrameIds[index] = var10;
scratchTranslatorX[index] = 0; scratchTranslatorX[index] = 0;
@@ -77,7 +78,7 @@ public class FrameLoader
indexFrameIds[index] = i; indexFrameIds[index] = i;
short var11 = 0; short var11 = 0;
if (def.framemap.field1456[i] == 3) if (def.framemap.types[i] == 3)
{ {
var11 = 128; var11 = 128;
} }
@@ -111,9 +112,9 @@ public class FrameLoader
lastI = i; lastI = i;
++index; ++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(); throw new RuntimeException();
} }
def.field1310 = index; def.translatorCount = index;
def.indexFrameIds = new int[index]; def.indexFrameIds = new int[index];
def.translator_x = new int[index]; def.translator_x = new int[index];
def.translator_y = new int[index]; def.translator_y = new int[index];

View File

@@ -37,24 +37,24 @@ public class FramemapLoader
def.id = id; def.id = id;
def.length = in.readUnsignedByte(); def.length = in.readUnsignedByte();
def.field1456 = new int[def.length]; def.types = new int[def.length];
def.field1457 = new int[def.length][]; def.frameMaps = new int[def.length][];
for (int i = 0; i < def.length; ++i) 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) 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 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();
} }
} }

View File

@@ -21,6 +21,7 @@ public class ModelLoader
def.computeNormals(); def.computeNormals();
def.computeTextureUVCoordinates(); def.computeTextureUVCoordinates();
def.computeAnimationTables();
return def; return def;
} }

View File

@@ -37,10 +37,14 @@ import net.runelite.cache.definitions.FramemapDefinition;
import net.runelite.cache.definitions.loaders.FrameLoader; import net.runelite.cache.definitions.loaders.FrameLoader;
import net.runelite.cache.definitions.loaders.FramemapLoader; import net.runelite.cache.definitions.loaders.FramemapLoader;
import net.runelite.cache.fs.Archive; 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.Index;
import net.runelite.cache.fs.Storage; import net.runelite.cache.fs.Storage;
import net.runelite.cache.fs.Store; import net.runelite.cache.fs.Store;
import org.junit.Ignore;
import org.junit.Rule; import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder; import org.junit.rules.TemporaryFolder;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@@ -54,7 +58,8 @@ public class FrameDumper
private final Gson gson = new GsonBuilder().setPrettyPrinting().create(); private final Gson gson = new GsonBuilder().setPrettyPrinting().create();
//@Test @Test
@Ignore
public void extract() throws IOException public void extract() throws IOException
{ {
File base = StoreLocation.LOCATION, File base = StoreLocation.LOCATION,
@@ -75,7 +80,11 @@ public class FrameDumper
List<FrameDefinition> frames = new ArrayList<>(); List<FrameDefinition> frames = new ArrayList<>();
byte[] archiveData = storage.loadArchive(archive); byte[] archiveData = storage.loadArchive(archive);
byte[] contents = archive.decompress(archiveData);
ArchiveFiles archiveFiles = archive.getFiles(archiveData);
for (FSFile archiveFile : archiveFiles.getFiles())
{
byte[] contents = archiveFile.getContents();
int framemapArchiveId = (contents[0] & 0xff) << 8 | contents[1] & 0xff; int framemapArchiveId = (contents[0] & 0xff) << 8 | contents[1] & 0xff;
@@ -84,12 +93,13 @@ public class FrameDumper
byte[] framemapContents = framemapArchive.decompress(archiveData); byte[] framemapContents = framemapArchive.decompress(archiveData);
FramemapLoader fmloader = new FramemapLoader(); FramemapLoader fmloader = new FramemapLoader();
FramemapDefinition framemap = fmloader.load(0, framemapContents); FramemapDefinition framemap = fmloader.load(framemapArchive.getArchiveId(), framemapContents);
FrameLoader frameLoader = new FrameLoader(); FrameLoader frameLoader = new FrameLoader();
FrameDefinition frame = frameLoader.load(framemap, contents); FrameDefinition frame = frameLoader.load(framemap, archiveFile.getFileId(), contents);
frames.add(frame); frames.add(frame);
}
Files.write(gson.toJson(frames), new File(outDir, archive.getArchiveId() + ".json"), Charset.defaultCharset()); Files.write(gson.toJson(frames), new File(outDir, archive.getArchiveId() + ".json"), Charset.defaultCharset());
++count; ++count;

View File

@@ -24,14 +24,17 @@
*/ */
package net.runelite.modelviewer; package net.runelite.modelviewer;
import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.nio.file.Files;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import net.runelite.cache.IndexType;
import net.runelite.cache.definitions.ModelDefinition; import net.runelite.cache.definitions.ModelDefinition;
import net.runelite.cache.definitions.ObjectDefinition; import net.runelite.cache.definitions.ObjectDefinition;
import net.runelite.cache.definitions.loaders.ModelLoader; 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 net.runelite.cache.region.Location;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@@ -40,16 +43,23 @@ public class ModelManager
{ {
private static final Logger logger = LoggerFactory.getLogger(ModelManager.class); private static final Logger logger = LoggerFactory.getLogger(ModelManager.class);
private static Map<LocationKey, ModelDefinition> models = new HashMap<>(); private final Store store;
private final Map<LocationKey, ModelDefinition> 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; LocationKey key;
int rot = location.getOrientation(); Integer rot = null;
if (location != null) if (location != null)
{ {
rot = location.getOrientation();
key = new LocationKey(id, location.getType(), rot); key = new LocationKey(id, location.getType(), rot);
} }
else else
@@ -63,12 +73,22 @@ public class ModelManager
return md; return md;
} }
Storage storage = store.getStorage();
Index index = store.getIndex(IndexType.MODELS);
Archive modelArchive = index.getArchive(id);
byte[] contents;
try 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(); ModelLoader loader = new ModelLoader();
md = loader.load(id, b); md = loader.load(modelArchive.getArchiveId(), contents);
if (object != null && location != null) if (object != null && location != null)
{ {
@@ -79,12 +99,6 @@ public class ModelManager
models.put(key, md); models.put(key, md);
return md; return md;
} }
catch (IOException ex)
{
logger.warn(null, ex);
return null;
}
}
// this logic is from method3697 in 140 // this logic is from method3697 in 140
private static void rotate(ModelDefinition md, ObjectDefinition object, Location location, int rot) private static void rotate(ModelDefinition md, ObjectDefinition object, Location location, int rot)

View File

@@ -24,34 +24,46 @@
*/ */
package net.runelite.modelviewer; 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.awt.Color;
import java.io.File; import java.io.File;
import java.io.FileInputStream; import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Optional;
import net.runelite.cache.ConfigType;
import net.runelite.cache.IndexType; import net.runelite.cache.IndexType;
import net.runelite.cache.NpcManager;
import net.runelite.cache.ObjectManager; import net.runelite.cache.ObjectManager;
import net.runelite.cache.OverlayManager; import net.runelite.cache.OverlayManager;
import net.runelite.cache.SpriteManager; import net.runelite.cache.SpriteManager;
import net.runelite.cache.TextureManager; import net.runelite.cache.TextureManager;
import net.runelite.cache.UnderlayManager; 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.KitDefinition;
import net.runelite.cache.definitions.LocationsDefinition; import net.runelite.cache.definitions.LocationsDefinition;
import net.runelite.cache.definitions.MapDefinition; import net.runelite.cache.definitions.MapDefinition;
import net.runelite.cache.definitions.ModelDefinition; import net.runelite.cache.definitions.ModelDefinition;
import net.runelite.cache.definitions.NpcDefinition; import net.runelite.cache.definitions.NpcDefinition;
import net.runelite.cache.definitions.ObjectDefinition; import net.runelite.cache.definitions.ObjectDefinition;
import net.runelite.cache.definitions.SequenceDefinition;
import net.runelite.cache.definitions.SpriteDefinition; import net.runelite.cache.definitions.SpriteDefinition;
import net.runelite.cache.definitions.TextureDefinition; 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.LocationsLoader;
import net.runelite.cache.definitions.loaders.MapLoader; 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.Archive;
import net.runelite.cache.fs.ArchiveFiles;
import net.runelite.cache.fs.FSFile;
import net.runelite.cache.fs.Index; import net.runelite.cache.fs.Index;
import net.runelite.cache.fs.Storage; import net.runelite.cache.fs.Storage;
import net.runelite.cache.fs.Store; import net.runelite.cache.fs.Store;
@@ -87,13 +99,15 @@ public class ModelViewer
* size of a tile in local coordinates * size of a tile in local coordinates
*/ */
private static final int TILE_SCALE = 128; private static final int TILE_SCALE = 128;
private static final int HEIGHT_MOD = 4;
private static ObjectManager objectManager; private static ObjectManager objectManager;
private static TextureManager textureManager; private static TextureManager textureManager;
private static SpriteManager spriteManager; private static SpriteManager spriteManager;
private static ModelManager modelManager;
private static Map<Integer, Texture> textures = new HashMap<>(); private static Map<Integer, Texture> textures = new HashMap<>();
private static Map<Integer, SequenceDefinition> seqs = new HashMap<>();
private static Multimap<Integer, FrameDefinition> frames = HashMultimap.create();
public static void main(String[] args) throws Exception 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, "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, "objectdir", true, "objects directory");
options.addOption(null, "npc", true, "npc to render"); 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, "model", true, "model to render");
options.addOption(null, "map", true, "map region to render"); options.addOption(null, "map", true, "map region to render");
options.addOption(null, "kits", true, "kits to render"); options.addOption(null, "kits", true, "kits to render");
options.addOption(null, "seq", true, "sequence id");
CommandLineParser parser = new DefaultParser(); CommandLineParser parser = new DefaultParser();
CommandLine cmd = parser.parse(options, args); CommandLine cmd = parser.parse(options, args);
String npcdir = cmd.getOptionValue("npcdir");
String mapdir = cmd.getOptionValue("mapdir");
String objectdir = cmd.getOptionValue("objectdir");
NpcDefinition npcdef = null; NpcDefinition npcdef = null;
ObjectDefinition objdef = null; ObjectDefinition objdef = null;
Store store = null; Store store = null;
UnderlayManager underlayManager = null; UnderlayManager underlayManager = null;
OverlayManager overlayManager = null; OverlayManager overlayManager = null;
NpcManager npcManager = null;
Integer seq = null;
List<ModelDefinition> models = new ArrayList<>(); List<ModelDefinition> models = new ArrayList<>();
Region region = null; Region region = null;
@@ -146,27 +157,35 @@ public class ModelViewer
spriteManager = new SpriteManager(store); spriteManager = new SpriteManager(store);
spriteManager.load(); spriteManager.load();
npcManager = new NpcManager(store);
npcManager.load();
modelManager = new ModelManager(store);
if (cmd.hasOption("seq"))
{
loadSeqs(store);
loadFrames(store);
}
} }
if (cmd.hasOption("model")) if (cmd.hasOption("model"))
{ {
// render model // render model
String model = cmd.getOptionValue("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); models.add(md);
} }
if (cmd.hasOption("npc")) if (cmd.hasOption("npc"))
{ {
String npc = cmd.getOptionValue("npc"); String npc = cmd.getOptionValue("npc");
try (FileInputStream fin = new FileInputStream(npcdir + "/" + npc + ".json")) npcdef = npcManager.get(Integer.parseInt(npc));
{
npcdef = new Gson().fromJson(new InputStreamReader(fin), NpcDefinition.class);
}
for (int model : npcdef.models) for (int model : npcdef.models)
{ {
ModelDefinition md = ModelManager.getModel(model, null, null); ModelDefinition md = modelManager.getModel(model, null, null);
models.add(md); models.add(md);
} }
} }
@@ -174,14 +193,11 @@ public class ModelViewer
{ {
String obj = cmd.getOptionValue("object"); String obj = cmd.getOptionValue("object");
try (FileInputStream fin = new FileInputStream(objectdir + "/" + obj + ".json")) objdef = objectManager.getObject(Integer.parseInt(obj));
{
objdef = new Gson().fromJson(new InputStreamReader(fin), ObjectDefinition.class);
}
for (int model : objdef.getObjectModels()) for (int model : objdef.getObjectModels())
{ {
ModelDefinition md = ModelManager.getModel(model, null, null); ModelDefinition md = modelManager.getModel(model, null, null);
models.add(md); models.add(md);
} }
} }
@@ -228,11 +244,15 @@ public class ModelViewer
KitDefinition kit = KitManager.getKit(kitId); KitDefinition kit = KitManager.getKit(kitId);
for (int model : kit.modelIds) for (int model : kit.modelIds)
{ {
ModelDefinition md = ModelManager.getModel(model, null, null); ModelDefinition md = modelManager.getModel(model, null, null);
models.add(md); models.add(md);
} }
} }
} }
if (cmd.hasOption("seq"))
{
seq = Integer.parseInt(cmd.getOptionValue("seq"));
}
Display.setDisplayMode(new DisplayMode(800, 600)); Display.setDisplayMode(new DisplayMode(800, 600));
Display.setTitle("Model Viewer"); Display.setTitle("Model Viewer");
@@ -259,14 +279,66 @@ public class ModelViewer
long last = 0; long last = 0;
Camera camera = new Camera(); Camera camera = new Camera();
Scene scene = new Scene(underlayManager, overlayManager); Scene scene = null;
if (region != null)
{
scene = new Scene(underlayManager, overlayManager);
scene.loadRegion(region); 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()) while (!Display.isCloseRequested())
{ {
// Clear the screen and depth buffer // Clear the screen and depth buffer
GL11.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT); 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<FrameDefinition> frames = ModelViewer.frames.get(seqFrameId >>> 16);
int frameFileId = seqFrameId & 65535;
Optional<FrameDefinition> 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) for (ModelDefinition def : models)
{ {
short[] recolourToFind = null, recolourToReplace = null; short[] recolourToFind = null, recolourToReplace = null;
@@ -284,11 +356,13 @@ public class ModelViewer
drawModel(def, recolourToFind, recolourToReplace); drawModel(def, recolourToFind, recolourToReplace);
} }
if (region != null)
{
drawScene(region, scene); drawScene(region, scene);
//drawRegion(region); }
Display.update(); Display.update();
Display.sync(20); // fps Display.sync(50); // fps
long delta = System.currentTimeMillis() - last; long delta = System.currentTimeMillis() - last;
last = System.currentTimeMillis(); last = System.currentTimeMillis();
@@ -611,7 +685,7 @@ public class ModelViewer
int regionX = objectPos.getX() - region.getBaseX(); int regionX = objectPos.getX() - region.getBaseX();
int regionY = objectPos.getY() - region.getBaseY(); 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); GL11.glMatrixMode(GL11.GL_MODELVIEW);
// TILE_SCALE/2 to draw the object from the center of the tile it is on // TILE_SCALE/2 to draw the object from the center of the tile it is on
@@ -624,7 +698,7 @@ public class ModelViewer
continue; continue;
} }
ModelDefinition md = ModelManager.getModel(object.getObjectModels()[i], object, location); ModelDefinition md = modelManager.getModel(object.getObjectModels()[i], object, location);
if (md == null) if (md == null)
{ {
@@ -702,6 +776,55 @@ public class ModelViewer
return texture; 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 // 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) public static int RGB_to_RS2HSB(int red, int green, int blue)
{ {