mdx-m3-viewer
Version:
A browser WebGL model viewer. Mainly focused on models of the games Warcraft 3 and Starcraft 2.
185 lines (169 loc) • 6.38 kB
JavaScript
import reverse from '../../common/stringreverse';
import Md34 from './md34';
import ModelHeader from './modelheader';
import Sequence from './sequence';
import Stc from './stc';
import Stg from './stg';
import Sts from './sts';
import Bone from './bone';
import Division from './division';
import Region from './region';
import Batch from './batch';
import MaterialReference from './materialreference';
import StandardMaterial from './standardmaterial';
import Layer from './layer';
import Event from './event';
import BoundingSphere from './boundingsphere';
import AttachmentPoint from './attachmentpoint';
import Camera from './camera';
import Sd from './sd';
import UnsupportedEntry from './unsupportedentry';
// Mapping from entry tags, to their constructors and known version->size values.
let tagMapping = {
// Objects
MD34: [Md34, {11: 24}],
MODL: [ModelHeader, {23: 784, 25: 808, 26: 820, 28: 844, 29: 856}],
SEQS: [Sequence, {1: 96, 2: 92}],
STC_: [Stc, {4: 204}],
STG_: [Stg, {0: 24}],
STS_: [Sts, {0: 28}],
BONE: [Bone, {1: 160}],
DIV_: [Division, {2: 52}],
REGN: [Region, {3: 36, 4: 40, 5: 48}],
BAT_: [Batch, {1: 14}],
MATM: [MaterialReference, {0: 8}],
MAT_: [StandardMaterial, {15: 268, 16: 280, 17: 280, 18: 280, 19: 340}],
LAYR: [Layer, {22: 356, 24: 436, 25: 468, 26: 464}],
EVNT: [Event, {0: 96, 1: 104, 2: 108}],
BNDS: [BoundingSphere, {0: 28}],
ATT_: [AttachmentPoint, {1: 20}],
CAM_: [Camera, {3: 180, 5: 264}],
SDEV: [Sd, {0: 32}],
SDU6: [Sd, {0: 32}],
SDFG: [Sd, {0: 32}],
SDS6: [Sd, {0: 32}],
SDR3: [Sd, {0: 32}],
SD2V: [Sd, {0: 32}],
SD3V: [Sd, {0: 32}],
SD4Q: [Sd, {0: 32}],
SDCC: [Sd, {0: 32}],
SDMB: [Sd, {0: 32}],
FLAG: [Sd, {0: 32}],
// Unsupported entries
MSEC: [UnsupportedEntry, {1: 72}],
LITE: [UnsupportedEntry, {7: 212}],
ATVL: [UnsupportedEntry, {0: 116}],
PATU: [UnsupportedEntry, {4: 152}],
TRGD: [UnsupportedEntry, {0: 24}],
DIS_: [UnsupportedEntry, {4: 68}],
CMS_: [UnsupportedEntry, {0: 24}],
CMP_: [UnsupportedEntry, {2: 28}],
TER_: [UnsupportedEntry, {0: 24, 1: 28}],
VOL_: [UnsupportedEntry, {0: 84}],
VON_: [UnsupportedEntry, {0: 268}],
CREP: [UnsupportedEntry, {0: 24, 1: 28}],
STBM: [UnsupportedEntry, {0: 48}],
LFSB: [UnsupportedEntry, {2: 56}],
LFLR: [UnsupportedEntry, {2: 80, 3: 152}],
PAR_: [UnsupportedEntry, {12: 1316, 17: 1460, 18: 1464, 19: 1464, 21: 1464, 22: 1484, 23: 1492, 24: 1496}],
PARC: [UnsupportedEntry, {0: 40}],
PROJ: [UnsupportedEntry, {4: 388, 5: 382}],
PHYJ: [UnsupportedEntry, {0: 180}],
PHCC: [UnsupportedEntry, {0: 76}],
PHAC: [UnsupportedEntry, {0: 32}],
PHCL: [UnsupportedEntry, {2: 128}],
FOR_: [UnsupportedEntry, {1: 104, 2: 104}],
DMSE: [UnsupportedEntry, {0: 4}],
PHSH: [UnsupportedEntry, {1: 132, 3: 300}],
PHRB: [UnsupportedEntry, {2: 104, 4: 80}],
SSGS: [UnsupportedEntry, {1: 108}],
BBSC: [UnsupportedEntry, {0: 48}],
SRIB: [UnsupportedEntry, {0: 272}],
RIB_: [UnsupportedEntry, {6: 748, 8: 756, 9: 760}],
IKJT: [UnsupportedEntry, {0: 32}],
SHBX: [UnsupportedEntry, {0: 64}],
WRP_: [UnsupportedEntry, {1: 132}],
};
/**
* An index entry.
*/
export default class IndexEntry {
/**
* @param {BinaryReader} reader
* @param {Array<IndexEntry>} index
*/
constructor(reader, index) {
let tag = reverse(reader.read(4));
let offset = reader.readUint32();
let entriesCount = reader.readUint32();
let version = reader.readUint32();
/** @member {Array<IndexEntry>} */
this.index = index;
/** @member {string} */
this.tag = tag;
/** @member {number} */
this.offset = offset;
/** @member {number} */
this.version = version;
/** @member {null|Array<?>|Uint8Array|Uint16Array|Uint32Array|Int32Array|Float32Array} */
this.entries = null;
let mapping = tagMapping[tag];
let readerOffset = reader.tell();
reader.seek(offset);
// This is an object
if (mapping) {
let constructor = mapping[0];
let entrySize = mapping[1][version];
if (!entrySize) {
// Yey found a new version!
throw new Error(': Unsupported object version - tag ' + tag + ' and version ' + version);
}
this.entries = [];
for (let i = 0, l = entriesCount; i < l; i++) {
// A sub stream is given for each object constructor.
// This allows for parsing to work consistently, even if we don't quite know exactly how the structures look.
// If some bytes aren't read, the error will not carry to the next object.
// Since new versions of objects usually add data to the end, this allows the parser to work, even if trying to load newer versions.
// Of course, the new version size needs to be added to IndexEntry.tagMapping, when finding one.
this.entries[i] = new constructor(reader.substream(entrySize), version, index);
reader.skip(entrySize);
}
// This is maybe a typed array?
} else if (tag === 'CHAR' || tag === 'SCHR') {
this.entries = reader.readCharArray(entriesCount);
} else if (tag === 'U8__') {
this.entries = reader.readUint8Array(entriesCount);
} else if (tag === 'U16_') {
this.entries = reader.readUint16Array(entriesCount);
} else if (tag === 'U32_') {
this.entries = reader.readUint32Array(entriesCount);
} else if (tag === 'I32_') {
this.entries = reader.readInt32Array(entriesCount);
} else if (tag === 'REAL') {
this.entries = reader.readFloat32Array(entriesCount);
} else if (tag === 'VEC2') {
this.entries = [];
for (let i = 0; i < entriesCount; i++) {
this.entries[i] = reader.readFloat32Array(2);
}
} else if (tag === 'VEC3' || tag === 'SVC3') {
this.entries = [];
for (let i = 0; i < entriesCount; i++) {
this.entries[i] = reader.readFloat32Array(3);
}
} else if (tag === 'VEC4' || tag === 'QUAT') {
this.entries = [];
for (let i = 0; i < entriesCount; i++) {
this.entries[i] = reader.readFloat32Array(4);
}
} else if (tag === 'IREF') {
this.entries = [];
for (let i = 0; i < entriesCount; i++) {
this.entries[i] = reader.readFloat32Array(16);
}
} else {
throw new Error(': Unsupported object tag - tag ' + tag + ' and version ' + version);
}
reader.seek(readerOffset);
}
}