source-mdl
Version:
This package can read source engines MDL files. To parse a model, it needs the VTX, VVD and MDL file.
153 lines (131 loc) • 5.91 kB
JavaScript
const studio = require('./studio.js');
const optimize = require('./optimize.js');
const OPTIMIZED_MODEL_FILE_VERSION = 7;
const STRIP_IS_TRILIST = 0x01;
const STRIP_IS_TRISTRIP = 0x02;
const STRIPGROUP_IS_FLEXED = 0x01;
const STRIPGROUP_IS_HWSKINNED = 0x02;
const STRIPGROUP_IS_DELTA_FLEXED = 0x04;
const STRIPGROUP_SUPPRESS_HW_MORPH = 0x08;
/**
* MDL scheme: https://developer.valvesoftware.com/wiki/MDL
* VTX scheme: https://developer.valvesoftware.com/wiki/VTX
* VVD scheme: https://developer.valvesoftware.com/wiki/VVD
* PHY scheme: https://developer.valvesoftware.com/wiki/PHY
*/
/**
* Importer class for Source Engine models
*/
class Import {
constructor(data, mdl) {
this.mdl = mdl;
this.importMDL = null;
this.importVTX = null;
this.importVVD = null;
let id = data.mdlData.readInt32BE(0);
if (id != 0x49445354) throw new Error("Unkown MDL id: " + id);
if (data.mdlData) {
this.importMDL = studio.studiohdr_t.report(data.mdlData, 0, { hideReferenceValues: true, monitorUsage: true });
this.headerMDL = this.importMDL.data;
}
if (data.vtxData) {
this.importVTX = optimize.FileHeader_t.report(data.vtxData, 0, { hideReferenceValues: true, monitorUsage: true });
this.headerVTX = this.importVTX.data;
}
if (data.vvdData) {
this.importVVD = studio.vertexFileHeader_t.report(data.vvdData, 0, { hideReferenceValues: true, monitorUsage: true });
this.headerVVD = this.importVVD.data;
}
mdl.raw = {
MDL: this.headerMDL,
VTX: this.headerVTX,
VVD: this.headerVVD
}
this.mdl.name = this.headerMDL.name;
this.mdl.surfaceProp = this.headerMDL.surfaceProp;
this.mdl.textureDirs = this.headerMDL.texturedirs.map(t => t.name);
this.mdl.textures = this.headerMDL.textures.map(t => t.name);
this.mdl.hullMin = this.headerMDL.hull_min;
this.mdl.hullMax = this.headerMDL.hull_max;
this.mdl.vertices = [];
this.mdl.meshes = [];
if (!this.headerVTX || !this.headerVVD) {
return;
}
this.mdl.hasGeometry = true;
if (this.headerMDL.checksum != this.headerVTX.checkSum) throw new Error("Checksums don't match! (MDL <-> VTX)");
if (this.headerMDL.checksum != this.headerVVD.checksum) throw new Error("Checksums don't match! (MDL <-> VVD)");
if (this.headerVTX.version != OPTIMIZED_MODEL_FILE_VERSION) throw new Error("Incompatible VTX version!");
for (let i = 0; i < this.headerVVD.numLODs; i++) {
this.mdl.vertices[i] = [];
this.mdl.meshes[i] = [];
}
if (this.headerVVD.fixups.length == 0) {
for (let i = 0; i < this.headerVVD.numLODVertexes0; i++) {
let address = this.headerVVD.vertexDataStart + i * 48;
let vtx = studio.mstudiovertex_t.read(data.vvdData, address);
this.mdl.vertices[0].push({
position: [vtx.m_vecPosition.x, vtx.m_vecPosition.y, vtx.m_vecPosition.z],
normal: [vtx.m_vecNormal.x, vtx.m_vecNormal.y, vtx.m_vecNormal.z],
texCoords: [vtx.m_vecTexCoord.x, vtx.m_vecTexCoord.y]
});
}
} else {
for (let fixup of this.headerVVD.fixups) {
for (let i = 0; i < fixup.numVertexes; i++) {
let address = this.headerVVD.vertexDataStart + (i + fixup.sourceVertexId) * 48;
for (let j = fixup.lod; j >= 0; j--) {
let vtx = studio.mstudiovertex_t.read(data.vvdData, address)
this.mdl.vertices[j].push({
position: [vtx.m_vecPosition.x, vtx.m_vecPosition.y, vtx.m_vecPosition.z],
normal: [vtx.m_vecNormal.x, vtx.m_vecNormal.y, vtx.m_vecNormal.z],
texCoords: [vtx.m_vecTexCoord.x, vtx.m_vecTexCoord.y]
});
}
}
}
}
for (let i = 0; i < this.headerMDL.bodyparts.length; i++) {
let bodyPartsMDL = this.headerMDL.bodyparts[i];
let bodyPartsVTX = this.headerVTX.bodyParts[i];
for (let j = 0; j < bodyPartsMDL.models.length; j++) {
let modelMDL = bodyPartsMDL.models[j];
let movelVTX = bodyPartsVTX.models[j];
for (let k = 0; k < modelMDL.meshes.length; k++) {
let meshMDL = modelMDL.meshes[k];
for (let lod_level = 0; lod_level < this.headerVVD.numLODs; lod_level++) {
let meshVTX = movelVTX.LODs[lod_level].meshes[k];
let mesh = {
indices: [],
material: meshMDL.material
};
this.mdl.meshes[lod_level].push(mesh);
for (let stripGroup of meshVTX.stripGroups) {
for (let strip of stripGroup.strips) {
if (strip.flags & STRIP_IS_TRILIST) {
for (let i = strip.numIndices - 1; i >= 0; i--) {
let vtxId = stripGroup.indices[i + strip.indexOffset];
let vtx = stripGroup.vertices[vtxId];
mesh.indices.push(vtx.origMeshVertID + meshMDL.vertexoffset);
}
} else if (strip.flags & STRIP_IS_TRISTRIP) {
/**
* @todo Test it with a model that uses tristrips
*/
for (let i = strip.numIndices + strip.indexOffset; i >= strip.indexOffset + 2; i--) {
mesh.indices.push(
stripGroup.vertices[stripGroup.indices[i]].origMeshVertID + meshMDL.vertexoffset,
stripGroup.vertices[stripGroup.indices[i - 2]].origMeshVertID + meshMDL.vertexoffset,
stripGroup.vertices[stripGroup.indices[i - 1]].origMeshVertID + meshMDL.vertexoffset
);
}
}
}
}
}
}
}
}
}
}
module.exports = Import;