modelviewer: add basic animation support
This commit is contained in:
@@ -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"))
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@ public class ModelLoader
|
||||
|
||||
def.computeNormals();
|
||||
def.computeTextureUVCoordinates();
|
||||
def.computeAnimationTables();
|
||||
|
||||
return def;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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<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;
|
||||
|
||||
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
|
||||
|
||||
@@ -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<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
|
||||
{
|
||||
@@ -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<ModelDefinition> 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<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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user