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.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<NpcDefinition> npcs = new ArrayList<>();
private final Map<Integer, NpcDefinition> 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<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
{
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"))
{

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;

View File

@@ -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;

View File

@@ -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];

View File

@@ -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();
}
}

View File

@@ -21,6 +21,7 @@ public class ModelLoader
def.computeNormals();
def.computeTextureUVCoordinates();
def.computeAnimationTables();
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.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<FrameDefinition> 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;