mdx-m3-viewer
Version:
A browser WebGL model viewer. Mainly focused on models of the games Warcraft 3 and Starcraft 2.
187 lines (168 loc) • 6.67 kB
text/typescript
import reverse from '../../common/stringreverse';
import BinaryStream from '../../common/binarystream';
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 {
index: IndexEntry[];
tag: string;
offset: number;
version: number;
entries: any[] | TypedArray;
constructor(reader: BinaryStream, index: IndexEntry[]) {
let tag = reverse(reader.read(4));
let offset = reader.readUint32();
let entriesCount = reader.readUint32();
let version = reader.readUint32();
this.index = index;
this.tag = tag;
this.offset = offset;
this.version = version;
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 === 'I16_') {
this.entries = reader.readInt16Array(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 {
this.entries = [];
throw new Error(': Unsupported object tag - tag ' + tag + ' and version ' + version);
}
reader.seek(readerOffset);
}
}