osrscachereader
Version:
Read Old-School Runescape Cache files
1,294 lines (1,103 loc) • 97.8 kB
JavaScript
import _ from "lodash";
import IndexType from "../cacheTypes/IndexType.js";
import ConfigType from "../cacheTypes/ConfigType.js";
import Matrix from "../cacheTypes/anim/Matrix.js";
/**
* @typedef AnimationData
* @property {Array<Vector3>} vertexData Vector3 is an array with 3 numbers
* @property {Array<number>} lengths Animation frame length
*/
class Vector3f {
x;
y;
z;
magnitude;
constructor(otherVertexNormal) {
this.x = otherVertexNormal?.x ?? 0;
this.y = otherVertexNormal?.y ?? 0;
this.z = otherVertexNormal?.z ?? 0;
this.magnitude = otherVertexNormal?.magnitude ?? 0;
}
}
class VertexNormal extends Vector3f { }
class FaceNormal extends Vector3f { }
/**
* @class ModelDefinition
* @category Definitions
* @hideconstructor
*/
export class ModelDefinition {
static normalMergeCount = 0;
static field1935 = new Array(10000);
static field1936 = new Array(10000);
/**
* The ID of this Model
* @type {number}
*/
id;
/**
* Used to offset vertices during merge
* @type {number}
*/
translation = { x: 0, y: 0, z: 0 };
/**
* Used to rotate vertices during merge
* @type {number}
*/
rotation = { x: 0, y: 0, z: 0 };
/**
* How many verticies this models has
* @type {number}
*/
vertexCount = 0;
/**
* How many faces this models has
* @type {number}
*/
faceCount = 0;
/**
* How many textured faces this models has
* @type {number}
*/
numTextureFaces = 0;
/**
* Vertex X Position Array
* @type {Array<number>}
*/
vertexPositionsX = [];
/**
* Vertex Y Position Array
* @type {Array<number>}
*/
vertexPositionsY = [];
/**
* Vertex Z Position Array
* @type {Array<number>}
*/
vertexPositionsZ = [];
/**
* Which Vertex XYZ to use for the 1st index
* @type {Array<number>}
*/
faceVertexIndices1 = [];
/**
* Which Vertex XYZ to use for the 2nd index
* @type {Array<number>}
*/
faceVertexIndices2 = [];
/**
* Which Vertex XYZ to use for the 3rd index
* @type {Array<number>}
*/
faceVertexIndices3 = [];
/**
* Used for animations
* @type {Array<number>}
*/
vertexSkins;
/**
* Changes how this face will render (lighting style, invisible, etc.)
* @type {Array<number>}
*/
faceRenderTypes;
/**
* Local render priority when combined with other models
* @type {Array<number>}
*/
faceRenderPriorities = [];
/**
* Overall priority
* @type {number}
*/
priority;
/**
* Used to set transparency of faces
* @type {Array<Byte>}
*/
faceAlphas = [];
/**
* Used for animations
* @type {Array<number>}
*/
faceSkins;
/**
* Texture IDs for faces
* @type {Array<number>}
*/
faceTextures = [];
/**
* Texture UV coords for mapping
* @type {Array<number>}
*/
textureCoords;
/**
* Used for new Animaya animations
* @type {Array<number>}
*/
animayaGroups;
/**
* Used for new Animaya animations
* @type {Array<number>}
*/
animayaScales;
/**
* Used to compute Texture UV coords
* @type {Array<number>}
*/
texIndices1 = [];
/**
* Used to compute Texture UV coords
* @type {Array<number>}
*/
texIndices2 = [];
/**
* Used to compute Texture UV coords
* @type {Array<number>}
*/
texIndices3 = [];
/**
* Face color
* @type {Array<number>}
*/
faceColors = [];
/**
* Changes how this face's texture will render (lighting style, invisible, etc.)
* @type {Array<number>}
*/
textureRenderTypes = [];
/** @type {Array<number>} */
aShortArray2574;
/** @type {Array<number>} */
aShortArray2575;
/** @type {Array<number>} */
aShortArray2586;
/** @type {Array<number>} */
aShortArray2577;
/** @type {Array<Byte>} */
aByteArray2580;
/** @type {Array<number>} */
aShortArray2578;
vertexGroups = [];
position = { x: 0, y: 0, z: 0 };
rotation = { x: 0, y: 0, z: 0 };
constructor(x = 0, y = 0, heightOffset = 0, color) {
this.position.x = x;
this.position.y = y;
this.color = color;
this.position.z = heightOffset;
}
addVertex(x, y, z, color = this.color, uvs = [0, 0, 0, 0, 1, 1]) {
this.vertexPositionsX.push(this.position.x + x);
this.vertexPositionsY.push(y + this.this.position.z);
this.vertexPositionsZ.push(this.position.y + z);
if (this.vertexNormals == undefined) this.vertexNormals = [];
this.vertexNormals.push({
x: Math.random(),
y: Math.random(),
z: Math.random(),
magnitude: Math.random(),
});
this.vertexCount = this.vertexPositionsX.length;
if (this.vertexCount >= 3) {
this.faceCount = this.vertexCount - 2;
}
if (this.faceCount > 0) {
this.faceVertexIndices1.push(0);
this.faceVertexIndices2.push(this.vertexCount - 1);
this.faceVertexIndices3.push(this.vertexCount - 2);
this.faceAlphas.push(256);
this.faceColors.push(color);
}
if (this.faceTextureUCoordinates == undefined) this.faceTextureUCoordinates = [];
if (this.faceTextureVCoordinates == undefined) this.faceTextureVCoordinates = [];
this.faceTextureUCoordinates.push([0, 0, 1]);
this.faceTextureVCoordinates.push([0, 1, 1]);
}
rotate(degrees, size, posX = 0, posY = 0) {
for (let i = 0; i < this.vertexCount; i++) {
let x = this.vertexPositionsX[i] - posX - (size / 2);
let z = this.vertexPositionsZ[i] - posY - (size / 2);
this.vertexPositionsX[i] = (x * Math.cos(degrees) - z * Math.sin(degrees)) + posX + (size / 2);
this.vertexPositionsZ[i] = (z * Math.cos(degrees) + x * Math.sin(degrees)) + posY + (size / 2);
}
}
static rotated(pos, degrees, size, posX = 0, posY = 0) {
for (let i = 0; i < pos.vertexPositionsX.length; i++) {
let x = pos.vertexPositionsX[i] - posX - (size / 2);
let z = pos.vertexPositionsZ[i] - posY - (size / 2);
pos.vertexPositionsX[i] = (x * Math.cos(degrees) - z * Math.sin(degrees)) + posX + (size / 2);
pos.vertexPositionsZ[i] = (z * Math.cos(degrees) + x * Math.sin(degrees)) + posY + (size / 2);
}
return pos;
}
translate(x, y, z) {
for (let i = 0; i < this.vertexCount; i++) {
this.vertexPositionsX[i] -= x;
this.vertexPositionsY[i] += z;
this.vertexPositionsZ[i] += y;
}
}
static translated(pos, x, y, z) {
for (let i = 0; i < pos.vertexPositionsX.length; i++) {
pos.vertexPositionsX[i] -= x;
pos.vertexPositionsY[i] += y;
pos.vertexPositionsZ[i] += z;
}
return pos;
}
method1194() {
let var1;
for (var1 = 0; var1 < this.vertexCount; ++var1) {
this.vertexPositionsZ[var1] = -this.vertexPositionsZ[var1];
}
for (var1 = 0; var1 < this.faceCount; ++var1) {
let var2 = this.faceVertexIndices1[var1];
this.faceVertexIndices1[var1] = this.faceVertexIndices3[var1];
this.faceVertexIndices3[var1] = var2;
}
this.invalidate();
}
method1206(degrees) {
let var2 = Math.sin(2 * Math.PI * (degrees / 2048));
let var3 = Math.cos(2 * Math.PI * (degrees / 2048));
for (let var4 = 0; var4 < this.vertexCount; ++var4) {
let var5 = var2 * this.vertexPositionsZ[var4] + var3 * this.vertexPositionsX[var4];
this.vertexPositionsZ[var4] = var3 * this.vertexPositionsZ[var4] - var2 * this.vertexPositionsX[var4];
this.vertexPositionsX[var4] = var5;
}
this.invalidate();
}
changeOffset(var1, var2, var3) {
for (let var4 = 0; var4 < this.vertexCount; ++var4) {
this.vertexPositionsX[var4] += var1;
this.vertexPositionsY[var4] += var2;
this.vertexPositionsZ[var4] += var3;
}
this.invalidate();
}
method1188() {
for (let var1 = 0; var1 < this.vertexCount; ++var1) {
let var2 = this.vertexPositionsX[var1];
this.vertexPositionsX[var1] = this.vertexPositionsZ[var1];
this.vertexPositionsZ[var1] = -var2;
}
this.invalidate();
}
method1190() {
for (let var1 = 0; var1 < this.vertexCount; ++var1) {
this.vertexPositionsX[var1] = -this.vertexPositionsX[var1];
this.vertexPositionsZ[var1] = -this.vertexPositionsZ[var1];
}
this.invalidate();
}
method1189() {
for (let var1 = 0; var1 < this.vertexCount; ++var1) {
let var2 = this.vertexPositionsZ[var1];
this.vertexPositionsZ[var1] = this.vertexPositionsX[var1];
this.vertexPositionsX[var1] = -var2;
}
this.invalidate();
}
resize(var1, var2, var3) {
for (let var4 = 0; var4 < this.vertexCount; ++var4) {
this.vertexPositionsX[var4] = this.vertexPositionsX[var4] * var1 / 128;
this.vertexPositionsY[var4] = var2 * this.vertexPositionsY[var4] / 128;
this.vertexPositionsZ[var4] = var3 * this.vertexPositionsZ[var4] / 128;
}
this.invalidate();
}
invalidate() {
this.vertexNormals = null;
this.vertexVertices = null;
this.faceNormals = null;
this.isBoundsCalculated = false;
}
recolor(var1, var2) {
for (let var3 = 0; var3 < this.faceCount; ++var3) {
if (this.faceColors[var3] == var1) {
this.faceColors[var3] = var2;
}
}
}
retexture(var1, var2) {
if (this.faceTextures != null) {
for (let var3 = 0; var3 < this.faceCount; ++var3) {
if (this.faceTextures[var3] == var1) {
this.faceTextures[var3] = var2;
}
}
}
}
removeCommonVerticies() {
//doesnt actually remove the verticies, just makes them disappear
const TOLERANCE = 1;
let alreadyMerged = [];
outerLoop:
for (let i = 0; i < this.vertexCount; i++) {
for (let j = 0; j < this.vertexCount; j++) {
if (i == j) continue;
//if(alreadyMerged[j]) continue;
if (Math.abs(this.vertexPositionsX[i] - this.vertexPositionsX[j]) <= TOLERANCE &&
Math.abs(this.vertexPositionsY[i] - this.vertexPositionsY[j]) <= TOLERANCE &&
Math.abs(this.vertexPositionsZ[i] - this.vertexPositionsZ[j]) <= TOLERANCE) {
alreadyMerged[j] = true;
this.faceVertexIndices1 = this.faceVertexIndices1.map(x => x == j ? i : x);
this.faceVertexIndices2 = this.faceVertexIndices2.map(x => x == j ? i : x);
this.faceVertexIndices3 = this.faceVertexIndices3.map(x => x == j ? i : x);
if (this.vertexNormals[i] != undefined && this.vertexNormals[j] != undefined) {
this.vertexNormals[j].x = this.vertexNormals[i].x = (this.vertexNormals[i].x + this.vertexNormals[j].x);
this.vertexNormals[j].y = this.vertexNormals[i].y = (this.vertexNormals[i].y + this.vertexNormals[j].y);
this.vertexNormals[j].z = this.vertexNormals[i].z = (this.vertexNormals[i].z + this.vertexNormals[j].z);
this.vertexNormals[j].magnitude = this.vertexNormals[i].magnitude = (this.vertexNormals[i].magnitude + this.vertexNormals[j].magnitude);
}
//continue outerLoop;
}
}
}
//this.vertexNormals = newNormals;
}
overlapsWith(otherModel) { //aabb collision
this.calculateBounds();
otherModel.calculateBounds();
return (
this.minX + this.position.x <= otherModel.maxX + otherModel.position.x &&
this.maxX + this.position.x >= otherModel.minX + otherModel.position.x &&
this.minY + this.position.y <= otherModel.height + otherModel.position.y &&
this.height + this.position.y >= otherModel.minY + otherModel.position.y &&
this.minZ + this.position.z <= otherModel.maxZ + otherModel.position.z &&
this.maxZ + this.position.z >= otherModel.minZ + otherModel.position.z
);
}
overlapsWith2(otherModel) { //aabb collision
this.calculateBounds();
otherModel.calculateBounds();
return (
this.minZ + this.position.x <= otherModel.maxZ + otherModel.position.x &&
this.maxZ + this.position.x >= otherModel.minZ + otherModel.position.x &&
this.minY + this.position.y <= otherModel.height + otherModel.position.y &&
this.height + this.position.y >= otherModel.minY + otherModel.position.y &&
this.minX + this.position.z <= otherModel.maxX + otherModel.position.z &&
this.maxX + this.position.z >= otherModel.minX + otherModel.position.z
);
}
calculateBounds() {
if (!this.isBoundsCalculated) {
this.height = 0;
this.minY = 0;
this.maxY = 0;
this.minX = 999999;
this.maxX = -999999;
this.maxZ = -99999;
this.minZ = 99999;
let rotatedVerts = {
vertexPositionsX: Object.assign([], this.vertexPositionsX),
vertexPositionsY: Object.assign([], this.vertexPositionsY),
vertexPositionsZ: Object.assign([], this.vertexPositionsZ)
}
rotatedVerts = ModelDefinition.rotated(rotatedVerts, this.rotation.y, 1);
for (let var1 = 0; var1 < this.vertexCount; ++var1) {
let vertX = this.vertexPositionsX[var1];
let vertY = this.vertexPositionsY[var1];
let vertZ = this.vertexPositionsZ[var1];
if (vertX < this.minX) {
this.minX = vertX;
}
if (vertX > this.maxX) {
this.maxX = vertX;
}
if (vertZ < this.minZ) {
this.minZ = vertZ;
}
if (vertZ > this.maxZ) {
this.maxZ = vertZ;
}
if (-vertY > this.height) {
this.height = -vertY;
}
if (vertY < this.minY) {
this.minY = vertY;
}
if (vertY > this.maxY) {
this.maxY = vertY;
}
}
this.isBoundsCalculated = true;
}
}
mergeNormals2(otherModel, var5) {
this.calculateBounds();
this.computeNormals();
otherModel.calculateBounds();
otherModel.computeNormals();
++ModelDefinition.normalMergeCount;
let var2 = otherModel.position.x - this.position.x;
let var3 = this.position.y - otherModel.position.y;
let var4 = this.position.z - otherModel.position.z;
let var6 = 0;
let var7 = otherModel.vertexPositionsX;
let var9;
for (var9 = 0; var9 < this.vertexCount; ++var9) {
let var10 = this.vertexNormals[var9];
if (var10.magnitude != 0) {
let var11 = this.vertexPositionsY[var9] - var3;
if (var11 <= otherModel.maxY) {
let var12 = this.vertexPositionsX[var9] - var2;
if (var12 >= otherModel.minX && var12 <= otherModel.maxX) {
let var13 = this.vertexPositionsZ[var9] - var4;
if (var13 >= otherModel.minZ && var13 <= otherModel.maxZ) {
for (let var14 = 0; var14 < otherModel.vertexCount; ++var14) {
let var15 = otherModel.vertexNormals[var14];
if (var12 == var7[var14] && var13 == otherModel.vertexPositionsZ[var14] && var11 == otherModel.vertexPositionsY[var14] && var15.magnitude != 0) { //|| (Math.abs(var12 - var7[var14]) == 0 && Math.abs(var13 - otherModel.vertexPositionsZ[var14]) == 128 && Math.abs(var11 - otherModel.vertexPositionsY[var14]) == 0))
if (this.vertexVertices == null) {
this.vertexVertices = new Array(this.vertexCount);
}
if (otherModel.vertexVertices == null) {
otherModel.vertexVertices = new Array(otherModel.vertexCount);
}
let var16 = this.vertexVertices[var9];
if (var16 == null) {
var16 = this.vertexVertices[var9] = new VertexNormal(var10);
}
let var17 = otherModel.vertexVertices[var14];
if (var17 == null) {
var17 = otherModel.vertexVertices[var14] = new VertexNormal(var15);
}
var16.x += var15.x;
var16.y += var15.y;
var16.z += var15.z;
var16.magnitude += var15.magnitude;
var17.x += var10.x;
var17.y += var10.y;
var17.z += var10.z;
var17.magnitude += var10.magnitude;
++var6;
ModelDefinition.field1935[var9] = ModelDefinition.normalMergeCount;
ModelDefinition.field1936[var14] = ModelDefinition.normalMergeCount;
}
}
}
}
}
}
}
if (var6 >= 3 && var5) {
for (var9 = 0; var9 < this.faceCount; ++var9) {
if (ModelDefinition.field1935[this.faceVertexIndices1[var9]] == ModelDefinition.normalMergeCount && ModelDefinition.field1935[this.faceVertexIndices2[var9]] == ModelDefinition.normalMergeCount && ModelDefinition.field1935[this.faceVertexIndices3[var9]] == ModelDefinition.normalMergeCount) {
if (this.faceRenderTypes == null) {
this.faceRenderTypes = new Array(this.faceCount);
}
this.faceRenderTypes[var9] = 2;
}
}
for (var9 = 0; var9 < otherModel.faceCount; ++var9) {
if (ModelDefinition.normalMergeCount == ModelDefinition.field1936[otherModel.faceVertexIndices1[var9]] && ModelDefinition.normalMergeCount == ModelDefinition.field1936[otherModel.faceVertexIndices2[var9]] && ModelDefinition.normalMergeCount == ModelDefinition.field1936[otherModel.faceVertexIndices3[var9]]) {
if (otherModel.faceRenderTypes == null) {
otherModel.faceRenderTypes = new Array(otherModel.faceCount);
}
otherModel.faceRenderTypes[var9] = 2;
}
}
}
}
static mergeNormals(var0, var1, var2, var3, var4, var5) {
var0.calculateBounds();
var0.computeNormals();
var1.calculateBounds();
var1.computeNormals();
++ModelDefinition.normalMergeCount;
let var6 = 0;
let var7 = var1.vertexPositionsX;
let var8 = var1.vertexCount;
let var9;
for (var9 = 0; var9 < var0.vertexCount; ++var9) {
let var10 = var0.vertexNormals[var9];
if (var10.magnitude != 0) {
let var11 = var0.vertexPositionsY[var9] - var3;
if (var11 <= var1.minY) {
let var12 = var0.vertexPositionsX[var9] - var2;
if (var12 >= var1.minX && var12 <= var1.maxX) {
let var13 = var0.vertexPositionsZ[var9] - var4;
if (var13 >= var1.minZ && var13 <= var1.maxZ) {
for (let var14 = 0; var14 < var8; ++var14) {
let var15 = var1.vertexNormals[var14];
if (var12 == var7[var14] && var13 == var1.vertexPositionsZ[var14] && var11 == var1.vertexPositionsY[var14] && var15.magnitude != 0) {
if (var0.vertexVertices == null) {
var0.vertexVertices = new Array(var0.vertexCount);
}
if (var1.vertexVertices == null) {
var1.vertexVertices = new Array(var8);
}
let var16 = var0.vertexVertices[var9];
if (var16 == null) {
var16 = var0.vertexVertices[var9] = Object.assign({}, var10);
}
let var17 = var1.vertexVertices[var14];
if (var17 == null) {
var17 = var1.vertexVertices[var14] = Object.assign({}, var15);
}
var16.x += var15.x;
var16.y += var15.y;
var16.z += var15.z;
var16.magnitude += var15.magnitude;
var17.x += var10.x;
var17.y += var10.y;
var17.z += var10.z;
var17.magnitude += var10.magnitude;
++var6;
ModelDefinition.field1935[var9] = ModelDefinition.normalMergeCount;
ModelDefinition.field1936[var14] = ModelDefinition.normalMergeCount;
}
}
}
}
}
}
}
if (var6 >= 3 && var5) {
for (var9 = 0; var9 < var0.faceCount; ++var9) {
if (ModelDefinition.field1935[var0.faceVertexIndices1[var9]] == ModelDefinition.normalMergeCount && ModelDefinition.field1935[var0.faceVertexIndices2[var9]] == ModelDefinition.normalMergeCount && ModelDefinition.field1935[var0.faceVertexIndices3[var9]] == ModelDefinition.normalMergeCount) {
if (var0.faceRenderTypes == null) {
var0.faceRenderTypes = new byte[var0.faceCount];
}
var0.faceRenderTypes[var9] = 2;
}
}
for (var9 = 0; var9 < var1.faceCount; ++var9) {
if (ModelDefinition.normalMergeCount == ModelDefinition.field1936[var1.faceVertexIndices1[var9]] && ModelDefinition.normalMergeCount == ModelDefinition.field1936[var1.faceVertexIndices2[var9]] && ModelDefinition.normalMergeCount == ModelDefinition.field1936[var1.faceVertexIndices3[var9]]) {
if (var1.faceRenderTypes == null) {
var1.faceRenderTypes = new byte[var1.faceCount];
}
var1.faceRenderTypes[var9] = 2;
}
}
}
}
/**
* Merge this model with another model
* @param {ModelDefinition} otherModel Other model to combine with this
* @param {boolean} init initialise empty arrays if necessary. set to false if merging a model into a blank model
* @returns ModelDefinition
*/
mergeWith(otherModel, init = true) {
let verticesCount = this.vertexPositionsX.length;
this.vertexPositionsX = [...this.vertexPositionsX, ...otherModel.vertexPositionsX];
this.vertexPositionsY = [...this.vertexPositionsY, ...otherModel.vertexPositionsY];
this.vertexPositionsZ = [...this.vertexPositionsZ, ...otherModel.vertexPositionsZ];
this.faceVertexIndices1 = [
...this.faceVertexIndices1,
...otherModel.faceVertexIndices1.map((x) => x + verticesCount),
];
this.faceVertexIndices2 = [
...this.faceVertexIndices2,
...otherModel.faceVertexIndices2.map((x) => x + verticesCount),
];
this.faceVertexIndices3 = [
...this.faceVertexIndices3,
...otherModel.faceVertexIndices3.map((x) => x + verticesCount),
];
let otherVertexGroup = otherModel.vertexGroups.map((x) => x.map((y) => y + verticesCount));
let newVertexGroups =
this.vertexGroups.length > otherVertexGroup.length
? Array(this.vertexGroups.length)
: Array(otherModel.vertexGroups.length);
for (let i = 0; i < newVertexGroups.length; i++) {
if (this.vertexGroups[i] == undefined) {
newVertexGroups[i] = otherVertexGroup[i];
continue;
}
if (otherVertexGroup[i] == undefined) {
newVertexGroups[i] = this.vertexGroups[i];
continue;
}
newVertexGroups[i] = this.vertexGroups[i].concat(otherVertexGroup[i]);
}
this.vertexGroups = newVertexGroups;
if (init && (this.faceTextures == undefined || this.faceTextures.length == 0))
this.faceTextures = new Array(this.faceCount).fill(-1);
if (init && (otherModel.faceTextures == undefined || otherModel.faceTextures.length == 0))
otherModel.faceTextures = new Array(otherModel.faceCount).fill(-1);
if (init && (this.faceRenderTypes == undefined || this.faceRenderTypes.length == 0))
this.faceRenderTypes = new Array(this.faceCount).fill(-1);
if (init && (otherModel.faceRenderTypes == undefined || otherModel.faceRenderTypes.length == 0))
otherModel.faceRenderTypes = new Array(otherModel.faceCount).fill(-1);
this.vertexCount += otherModel.vertexCount;
this.faceCount += otherModel.faceCount;
if (init && this.animayaGroups == undefined) this.animayaGroups = new Array(this.vertexCount).fill([0]);
if (init && otherModel.animayaGroups == undefined)
otherModel.animayaGroups = new Array(otherModel.vertexCount).fill([0]);
if (init && this.animayaScales == undefined) this.animayaScales = new Array(this.vertexCount).fill([255]);
if (init && otherModel.animayaScales == undefined)
otherModel.animayaScales = new Array(otherModel.vertexCount).fill([255]);
if (init && this.faceAlphas == undefined) this.faceAlphas = new Array(this.vertexCount).fill(0);
if (init && otherModel.faceAlphas == undefined)
otherModel.faceAlphas = new Array(otherModel.vertexCount).fill(0);
let copy = (property) => {
if (this[property] == undefined && otherModel[property] != undefined) {
this[property] = otherModel[property];
} else if (this[property] != undefined && otherModel[property] != undefined) {
this[property] = [...this[property], ...otherModel[property]];
}
};
copy("vertexSkins");
copy("faceRenderTypes");
copy("faceRenderPriorities");
copy("faceAlphas");
copy("faceSkins");
copy("faceColors");
copy("faceTextures");
copy("textureCoords");
copy("vertexNormals");
copy("animayaGroups");
copy("animayaScales");
copy("faceTextureUCoordinates");
copy("faceTextureVCoordinates");
copy("overlayColors");
return this;
}
async loadSkeletonAnims(cache, model, id, invertZ = true) {
let frameDefs = (await cache.getAllFiles(IndexType.FRAMES.id, id)).map((x) => x.def);
let loadedAnims = frameDefs.map((frameDef) => this.loadFrame(model, frameDef, invertZ));
return loadedAnims;
}
/**
*
* @param {RSCache} cache OSRSCache object used to grab other files for the animation
* @param {number} animationId Animation ID you want to play on this model
* @param {boolean} compress Perform run-length encoding on animations to compress the sequence. Useful for maya anims.
* @returns AnimationData
*/
async loadAnimation(cache, animationId, invertZ = true, compress = false) {
let animation = (await cache.getFile(IndexType.CONFIGS.id, ConfigType.SEQUENCE.id, animationId)).def;
let vertexData;
let lengths;
if (animation.animMayaID != undefined && animation.animMayaID != -1) {
let index = IndexType.FRAMES;
if (this.rev229) {
index = IndexType.KEYFRAMES;
}
let framesInfo = await cache.getAllFiles(index.id, animation.animMayaID >> 16, {
isAnimaya: true,
});
const rawVertexData = this.loadMayaAnimation(framesInfo[0].def, animation, invertZ);
// run-length encode maya animations
if (compress) {
let last = rawVertexData[0]
let currentRun = 1;
vertexData = [];
lengths = [];
for (let i = 1; i < rawVertexData.length; ++i) {
if (_.isEqual(rawVertexData[i], last)) {
currentRun++;
} else {
lengths.push(currentRun);
currentRun = 1;
vertexData.push(last);
last = rawVertexData[i];
}
}
vertexData.push(last);
lengths.push(currentRun);
} else {
vertexData = rawVertexData;
lengths = new Array(vertexData.length).fill(1);
}
} else {
let shiftedId = animation.frameIDs[0] >> 16;
let frameDefs = (await cache.getAllFiles(IndexType.FRAMES.id, shiftedId & 65535)).map((x) => x.def);
let frames;
if (!(animation.frameIDs.length == 1 && animation.frameIDs[0] & 65535 == 0)) {
frames = await Promise.all(animation.frameIDs.map(async (frameId) =>
await cache.getDef(IndexType.FRAMES.id, frameId >> 16, frameId & 65535)
));
}
let loadedFrames = frames.map((x) => this.loadFrame(this, x, invertZ));
vertexData = loadedFrames.map((x) => x.vertices);
lengths = animation.frameLengths;
}
return {
vertexData,
lengths,
};
}
loadFrame(model, frame, invertZ = true) {
let verticesX = [...model.vertexPositionsX];
let verticesY = [...model.vertexPositionsY];
let verticesZ = invertZ ? model.vertexPositionsZ.map((z) => -z) : [...model.vertexPositionsZ];
let framemap = frame.framemap;
let animOffsets = {
x: 0,
y: 0,
z: 0,
};
for (let j = 0; j < frame.translator_x.length; ++j) {
let type = frame.indexFrameIds[j];
let fmType = framemap.types[type];
let fm = framemap.frameMaps[type];
let dx = frame.translator_x[j];
let dy = frame.translator_y[j];
let dz = frame.translator_z[j];
this.animate(model.vertexGroups, verticesX, verticesY, verticesZ, fmType, fm, dx, dy, dz, animOffsets);
}
frame.vertices = [];
for (let i = 0; i < verticesX.length; i++) {
frame.vertices.push([verticesX[i], -verticesY[i], -verticesZ[i]]);
}
return frame;
}
animate(vertexGroups, verticesX, verticesY, verticesZ, type, frameMap, dx, dy, dz, animOffsets) {
let var6 = frameMap.length;
let var7;
let var8;
let var11;
let var12;
if (type == 0) {
//change rotation origin
var7 = 0;
animOffsets.x = 0;
animOffsets.y = 0;
animOffsets.z = 0;
for (var8 = 0; var8 < frameMap.length; ++var8) {
let boneGroup = frameMap[var8];
if (boneGroup < vertexGroups.length) {
let bones = vertexGroups[boneGroup];
for (var11 = 0; var11 < bones.length; ++var11) {
var12 = bones[var11];
animOffsets.x += verticesX[var12];
animOffsets.y += verticesY[var12];
animOffsets.z += verticesZ[var12];
++var7;
}
}
}
if (var7 > 0) {
animOffsets.x = dx + animOffsets.x / var7;
animOffsets.y = dy + animOffsets.y / var7;
animOffsets.z = dz + animOffsets.z / var7;
} else {
animOffsets.x = dx;
animOffsets.y = dy;
animOffsets.z = dz;
}
} else {
let var18;
let var19;
if (type == 1) {
//translation
for (var7 = 0; var7 < frameMap.length; ++var7) {
var8 = frameMap[var7];
if (var8 < vertexGroups.length) {
var18 = 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) {
//rotation
for (var7 = 0; var7 < frameMap.length; ++var7) {
var8 = frameMap[var7];
if (var8 < vertexGroups.length) {
var18 = vertexGroups[var8];
for (var19 = 0; var19 < var18.length; ++var19) {
var11 = var18[var19];
verticesX[var11] -= animOffsets.x;
verticesY[var11] -= animOffsets.y;
verticesZ[var11] -= animOffsets.z;
var12 = (dx & 255) * 8;
let var13 = (dy & 255) * 8;
let var14 = (dz & 255) * 8;
let var15;
let var16;
let var17;
if (var14 != 0) {
var15 = Math.floor(65536 * Math.sin((var14 * Math.PI) / 1024));
var16 = Math.floor(65536 * Math.cos((var14 * Math.PI) / 1024));
var17 = (var15 * verticesY[var11] + var16 * verticesX[var11]) >> 16;
verticesY[var11] = (var16 * verticesY[var11] - var15 * verticesX[var11]) >> 16;
verticesX[var11] = var17;
}
if (var12 != 0) {
var15 = Math.floor(65536 * Math.sin((var12 * Math.PI) / 1024));
var16 = Math.floor(65536 * Math.cos((var12 * Math.PI) / 1024));
var17 = (var16 * verticesY[var11] - var15 * verticesZ[var11]) >> 16;
verticesZ[var11] = (var15 * verticesY[var11] + var16 * verticesZ[var11]) >> 16;
verticesY[var11] = var17;
}
if (var13 != 0) {
var15 = Math.floor(65536 * Math.sin((var13 * Math.PI) / 1024));
var16 = Math.floor(65536 * Math.cos((var13 * Math.PI) / 1024));
var17 = (var15 * verticesZ[var11] + var16 * verticesX[var11]) >> 16;
verticesZ[var11] = (var16 * verticesZ[var11] - var15 * verticesX[var11]) >> 16;
verticesX[var11] = var17;
}
verticesX[var11] += animOffsets.x;
verticesY[var11] += animOffsets.y;
verticesZ[var11] += animOffsets.z;
}
}
}
} else if (type == 3) {
//scaling
for (var7 = 0; var7 < frameMap.length; ++var7) {
var8 = frameMap[var7];
if (var8 < vertexGroups.length) {
var18 = vertexGroups[var8];
for (var19 = 0; var19 < var18.length; ++var19) {
var11 = var18[var19];
verticesX[var11] -= animOffsets.x;
verticesY[var11] -= animOffsets.y;
verticesZ[var11] -= animOffsets.z;
verticesX[var11] = (dx * verticesX[var11]) / 128;
verticesY[var11] = (dy * verticesY[var11]) / 128;
verticesZ[var11] = (dz * verticesZ[var11]) / 128;
verticesX[var11] += animOffsets.x;
verticesY[var11] += animOffsets.y;
verticesZ[var11] += animOffsets.z;
}
}
}
} else if (type == 5) {
}
}
}
loadMayaAnimation(frameDef, sequenceDefinition, invertZ = true) {
let animations = [];
let animayaSkeleton = frameDef.framemap.animayaSkeleton;
let verticesX = [];
let verticesY = [];
let verticesZ = [];
for (let currentFrame = 0; currentFrame < sequenceDefinition.animMayaEnd; currentFrame++) {
let var6 = 0;
let bones = frameDef.framemap.animayaSkeleton.getAllBones();
for (let index = 0; index < bones.length; ++index) {
let bone = bones[index];
frameDef.method727(currentFrame, bone, var6, frameDef.field1257);
++var6;
}
if (this.animayaGroups != null) {
let animatedFrameVertices = [];
for (let vertexIndex = 0; vertexIndex < this.vertexCount; ++vertexIndex) {
let bones = this.animayaGroups[vertexIndex];
if (bones != null && bones.length != 0) {
let scales = this.animayaScales[vertexIndex];
let matrix = new Matrix();
matrix.zero();
for (let i = 0; i < bones.length; ++i) {
let boneIndex = bones[i];
let bone = animayaSkeleton.getBone(boneIndex);
if (bone != null) {
let matrix2 = new Matrix();
let matrix3 = new Matrix();
matrix2.setScale(scales[i] / 255);
matrix3.copy(bone.method687(frameDef.field1257));
matrix3.multiply(matrix2);
matrix.add(matrix3);
}
}
let var3 = this.vertexPositionsX[vertexIndex];
let var4 = -this.vertexPositionsY[vertexIndex];
let var5 = (invertZ ? 1 : -1) * this.vertexPositionsZ[vertexIndex];
let var6 = 1.0;
verticesX[vertexIndex] =
matrix.matrixVals[0] * var3 +
matrix.matrixVals[4] * var4 +
matrix.matrixVals[8] * var5 +
matrix.matrixVals[12] * var6;
verticesY[vertexIndex] = -(
matrix.matrixVals[1] * var3 +
matrix.matrixVals[5] * var4 +
matrix.matrixVals[9] * var5 +
matrix.matrixVals[13] * var6
);
verticesZ[vertexIndex] = -(
matrix.matrixVals[2] * var3 +
matrix.matrixVals[6] * var4 +
matrix.matrixVals[10] * var5 +
matrix.matrixVals[14] * var6
);
animatedFrameVertices.push([
verticesX[vertexIndex],
-verticesY[vertexIndex],
-verticesZ[vertexIndex],
]);
}
}
animations.push(animatedFrameVertices);
}
}
return animations;
}
/**
*
* @param {ModelDefinition} otherModel Check if this model is the same
* @returns boolean
*/
equals(otherModel) {
if (this.vertexCount != otherModel.vertexCount) return false;
if (this.faceCount != otherModel.faceCount) return false;
let sameFaceColors = this.faceColors.every((x, i) => x == otherModel.faceColors[i]);
let sameVerticiesX = this.vertexPositionsX.every((x, i) => x == otherModel.vertexPositionsX[i]);
let sameVerticiesY = this.vertexPositionsY.every((x, i) => x == otherModel.vertexPositionsY[i]);
let sameVerticiesZ = this.vertexPositionsZ.every((x, i) => x == otherModel.vertexPositionsZ[i]);
let sameRenderTypes = true;
if (this.faceRenderTypes != undefined && otherModel.faceRenderTypes != undefined) {
sameRenderTypes = this.faceRenderTypes.every((x, i) => x == otherModel.faceRenderTypes[i])
}
let sameNormals = true;
if (this.vertexVertices != undefined && otherModel.vertexVertices != undefined) {
sameNormals = this.vertexVertices.every((x, i) => x == otherModel.vertexVertices[i]);
} else if (this.vertexVertices == undefined ^ otherModel.vertexVertices == undefined) {
sameNormals = false;
}
return sameVerticiesX && sameVerticiesY && sameVerticiesZ && sameFaceColors && sameFaceColors && sameNormals && sameRenderTypes;
}
computeAnimationTables() {
var groupCounts = [];
var numGroups = 0;
var var3, var4, var10002;
if (this.vertexSkins != null) {
for (var3 = 0; var3 < this.vertexCount; ++var3) {
var4 = this.vertexSkins[var3];
++groupCounts[var4];
if (var4 > numGroups) {
numGroups = var4;
}
}
this.vertexGroups = [];
for (var3 = 0; var3 <= numGroups; ++var3) {
this.vertexGroups[var3] = [];
groupCounts[var3] = 0;
}
for (var3 = 0; var3 < this.vertexCount; this.vertexGroups[var4][groupCounts[var4]++] = var3++) {
var4 = this.vertexSkins[var3];
}
this.vertexSkins = null;
}
if (this.faceSkins != null) {
// L: 785
groupCounts = []; // L: 786
numGroups = 0; // L: 787
for (var3 = 0; var3 < this.faceCount; ++var3) {
// L: 788
var4 = this.faceSkins[var3]; // L: 789
var10002 = groupCounts[var4]++; // L: 790
if (var4 > numGroups) {
// L: 791
numGroups = var4;
}
}
this.faceLabelsAlpha = []; // L: 793
for (var3 = 0; var3 <= numGroups; ++var3) {
// L: 794
this.faceLabelsAlpha[var3] = []; // L: 795
groupCounts[var3] = 0; // L: 796
}
for (var3 = 0; var3 < this.faceCount; this.faceLabelsAlpha[var4][groupCounts[var4]++] = var3++) {
// L: 798 800
var4 = this.faceSkins[var3]; // L: 799
}
this.faceSkins = null; // L: 802
}
// triangleSkinValues is here
}
computeTextureUVCoordinates(def) {
this.faceTextureUCoordinates = new Array(this.faceCount).fill([0, 0, 0]);
this.faceTextureVCoordinates = new Array(this.faceCount).fill([0, 0, 0]);
if (this.faceTextures == null) {
return;
}
for (let i = 0; i < this.faceCount; i++) {
if (this.faceTextures[i] == -1) {
continue;
}
let u0, u1, u2, v0, v1, v2;
if (this.textureCoords != null && this.textureCoords[i] != -1) {
let tfaceIdx = this.textureCoords[i] & 0xff;
let triangleA = this.faceVertexIndices1[i];
let triangleB = this.faceVertexIndices2[i];
let triangleC = this.faceVertexIndices3[i];
let texA = this.texIndices1[tfaceIdx];
let texB = this.texIndices2[tfaceIdx];
let texC = this.texIndices3[tfaceIdx];
// v1 = vertex[texA]
let v1x = this.vertexPositionsX[texA];
let v1y = this.vertexPositionsY[texA];
let v1z = this.vertexPositionsZ[texA];
// v2 = vertex[texB] - v1
let v2x = this.vertexPositionsX[texB] - v1x;
let v2y = this.vertexPositionsY[texB] - v1y;
let v2z = this.vertexPositionsZ[texB] - v1z;
// v3 = vertex[texC] - v1
let v3x = this.vertexPositionsX[texC] - v1x;
let v3y = this.vertexPositionsY[texC] - v1y;
let v3z = this.vertexPositionsZ[texC] - v1z;
// v4 = vertex[triangleA] - v1
let v4x = this.vertexPositionsX[triangleA] - v1x;
let v4y = this.vertexPositionsY[triangleA] - v1y;
let v4z = this.vertexPositionsZ[triangleA] - v1z;
// v5 = vertex[triangleB] - v1
let v5x = this.vertexPositionsX[triangleB] - v1x;
let v5y = this.vertexPositionsY[triangleB] - v1y;
let v5z = this.vertexPositionsZ[triangleB] - v1z;
// v6 = vertex[triangleC] - v1
let v6x = this.vertexPositionsX[triangleC] - v1x;
let v6y = this.vertexPositionsY[triangleC] - v1y;
let v6z = this.vertexPositionsZ[triangleC] - v1z;
// v7 = v2 x v3
let v7x = v2y * v3z - v2z * v3y;
let v7y = v2z * v3x - v2x * v3z;
let v7z = v2x * v3y - v2y * v3x;
// v8 = v3 x v7
let v8x = v3y * v7z - v3z * v7y;
let v8y = v3z * v7x - v3x * v7z;
let v8z = v3x * v7y - v3y * v7x;
// f = 1 / (v8 ⋅ v2)
let f = 1.0 / (v8x * v2x + v8y * v2y + v8z * v2z);
// u0 = (v8 ⋅ v4) * f
u0 = (v8x * v4x + v8y * v4y + v8z * v4z) * f;
// u1 = (v8 ⋅ v5) * f
u1 = (v8x * v5x + v8y * v5y + v8z * v5z) * f;
// u2 = (v8 ⋅ v6) * f
u2 = (v8x * v6x + v8y * v6y + v8z * v6z) * f;
// v8 = v2 x v7
v8x = v2y * v7z - v2z * v7y;
v8y = v2z * v7x - v2x * v7z;
v8z = v2x * v7y - v2y * v7x;
// f = 1 / (v8 ⋅ v3)
f = 1.0 / (v8x * v3x + v8y * v3y + v8z * v3z);
// v0 = (v8 ⋅ v4) * f
v0 = (v8x * v4x + v8y * v4y + v8z * v4z) * f;
// v1 = (v8 ⋅ v5) * f
v1 = (v8x * v5x + v8y * v5y + v8z * v5z) * f;
// v2 = (v8 ⋅ v6) * f
v2 = (v8x * v6x + v8y * v6y + v8z * v6z) * f;
} else {
// Without a texture face, the client assigns tex = triangle, but the resulting
// calculations can be reduced:
//
// v1 = vertex[texA]
// v2 = vertex[texB] - v1
// v3 = vertex[texC] - v1
//
// v4 = 0
// v5 = v2
// v6 = v3
//
// v7 = v2 x v3
//
// v8 = v3 x v7
// u0 = (v8 . v4) / (v8 . v2) // 0 because v4 is 0
// u1 = (v8 . v5) / (v8 . v2) // 1 because v5=v2
// u2 = (v8 . v6) / (v8 . v2) // 0 because v8 is perpendicular to v3/v6
//
// v8 = v2 x v7
// v0 = (v8 . v4) / (v8 ⋅ v3) // 0 because v4 is 0
// v1 = (v8 . v5) / (v8 ⋅ v3) // 0 because v8 is perpendicular to v5/v2
// v2 = (v8 . v6) / (v8 ⋅ v3) // 1 because v6=v3
u0 = 0;
v0 = 0;
u1 = 1;
v1 = 0;