source-mdl
Version:
This package can read source engines MDL files. To parse a model, it needs the VTX, VVD and MDL file.
122 lines (108 loc) • 4.2 kB
JavaScript
// xmodel_export specification: https://wiki.zeroy.com/index.php?title=Call_of_Duty:_XMODEL_formats
module.exports = function(mdl) {
const LOD = 0;
const vertices = mdl.vertices[LOD];
const meshes = mdl.meshes[LOD];
const data = {
'vertices': [],
'normals': [],
'uvs': [],
'materials': [],
'faces': [],
};
for(let i = 0; i < vertices.length; i++) {
let vert = vertices[i];
// Cod's model converter doesn't like decimal points longer than six digits most of the time.
data.vertices[i] = {'x': vert.position[0].toFixed(6), 'y': vert.position[1].toFixed(6), 'z': vert.position[2].toFixed(6)};
data.normals[i] = {'x': vert.normal[0].toFixed(6), 'y': vert.normal[1].toFixed(6), 'z': vert.normal[2].toFixed(6)};
data.uvs[i] = {'u': vert.texCoords[0].toFixed(6), 'v': vert.texCoords[1].toFixed(6)};
}
let face = 0;
for(let m = 0; m < meshes.length; m++) {
let mesh = meshes[m];
for(let i = 0; i < mesh.indices.length; i += 3) {
data.faces[face] = {
'object': m,
'material': mesh.material,
'tris': []
};
for(let j = 0; j < 3; j++) {
let index = mesh.indices[i + j];
// For some reason, some verts don't have normals or uv's defined with the same index.
if(typeof data.normals[index] == "undefined" || typeof data.normals[index] == "undefined")
continue;
data.faces[face].tris.push({
'vert': index,
'normal': data.normals[index],
'uv': data.uvs[index],
});
}
// Unlike the OBJ format, xmodel_export doesn't tolerate tris with less than 3 points or tris with invalid verts/normals.
// So, we gotta make sure that invalid triangles with 2 or 1 vertices are skipped.
if(data.faces[face].tris.length == 3)
face++;
}
}
let time = new Date().toString();
// model header
let result = "// File generated by source-mdl | https://github.com/LordVonAdel/source-mdl\n"
+ "// xmodel exporter written by johndoe | https://github.com/myuce\n"
+ `// original filename: ${mdl.name}\n`
+ `// Export time: ${time}\n\n`
+ "MODEL\n"
+ "VERSION 6\n\n"
+ "NUMBONES 1\n"
+ "BONE 0 -1 \"tag_origin\"\n\n"
+ "BONE 0\n"
+ "OFFSET 0.000000 0.000000 0.000000\n"
+ "SCALE 1.000000 1.000000 1.000000\n"
+ "X 1.000000 0.000000 0.000000\n"
+ "Y 0.000000 1.000000 0.000000\n"
+ "Z 0.000000 0.000000 1.000000\n\n";
// Verts
result += `NUMVERTS ${data.vertices.length}\n`;
for(let i = 0; i < data.vertices.length; i++) {
let vert = data.vertices[i];
result += `VERT ${i}\n`
+ `OFFSET ${vert.x} ${vert.y} ${vert.z}\n`
+ "BONES 1\n"
+ "BONE 0 1.000000\n\n";
}
// Faces
result += `NUMFACES ${data.faces.length}\n`;
for(face of data.faces) {
result += `TRI ${face.object} ${face.material} 0 0\n`;
// Face index order doesn't work the same as the OBJ format in xmodel_export
let tris = [face.tris[0], face.tris[2], face.tris[1]];
for(tri of tris) {
result += `VERT ${tri.vert}\n`
+ `NORMAL ${tri.normal.x} ${tri.normal.y} ${tri.normal.z}\n`
+ "COLOR 1.000000 1.000000 1.000000 1.000000\n"
+ `UV 1 ${tri.uv.u} ${tri.uv.v}\n\n`;
}
}
// Object groups
result += `NUMOBJECTS ${meshes.length}\n`;
for(let i = 0; i < meshes.length; i++) {
result += `OBJECT ${i} johndoeMesh_${i}\n`;
}
result += "\n";
// Materials
result += `NUMMATERIALS ${mdl.textures.length}\n`;
for(let i = 0; i < mdl.textures.length; i++) {
result += `MATERIAL ${i} "${mdl.textures[i]}" "Phong" "404.tga"\n`
+ "COLOR 0.000000 0.000000 0.000000 1.000000\n"
+ "TRANSPARENCY 0.000000 0.000000 0.000000 1.000000\n"
+ "AMBIENTCOLOR 0.000000 0.000000 0.000000 1.000000\n"
+ "INCANDESCENCE 0.000000 0.000000 0.000000 1.000000\n"
+ "COEFFS 0.800000 0.000000\n"
+ "GLOW 0.000000 0\n"
+ "REFRACTIVE 6 1.000000\n"
+ "SPECULARCOLOR -1.000000 -1.000000 -1.000000 1.000000\n"
+ "REFLECTIVECOLOR -1.000000 -1.000000 -1.000000 1.000000\n"
+ "REFLECTIVE -1 1.000000\n"
+ "BLINN -1.000000 -1.000000\n"
+ "PHONG -1.000000\n\n";
}
return result;
}