UNPKG

rw-parser

Version:

Parses RenderWare DFF and TXD files into usable format!

410 lines (409 loc) 18 kB
"use strict"; var __extends = (this && this.__extends) || (function () { var extendStatics = function (d, b) { extendStatics = Object.setPrototypeOf || ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; }; return extendStatics(d, b); }; return function (d, b) { if (typeof b !== "function" && b !== null) throw new TypeError("Class extends value " + String(b) + " is not a constructor or null"); extendStatics(d, b); function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); }; })(); var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.DffParser = void 0; var RwFile_1 = require("../RwFile"); var RwSections_1 = require("../RwSections"); var RwParseError_1 = require("../errors/RwParseError"); var RwVersion_1 = __importDefault(require("../utils/RwVersion")); var DffParser = /** @class */ (function (_super) { __extends(DffParser, _super); function DffParser(buffer) { return _super.call(this, buffer) || this; } DffParser.prototype.parse = function () { var version; var versionNumber; var atomics = []; var dummies = []; var animNodes = []; var geometryList = null; var frameList = null; while (this.getPosition() < this.getSize()) { var header = this.readSectionHeader(); if (header.sectionType === 0) { break; } if (header.sectionSize == 0) { continue; } switch (header.sectionType) { case RwSections_1.RwSections.RwClump: // Multiple clumps are used in SA player models, so we should eventually support it versionNumber = RwVersion_1.default.unpackVersion(header.versionNumber); version = RwVersion_1.default.versions[versionNumber]; break; case RwSections_1.RwSections.RwFrameList: frameList = this.readFrameList(); break; case RwSections_1.RwSections.RwExtension: var extensionHeader = this.readSectionHeader(); switch (extensionHeader.sectionType) { case RwSections_1.RwSections.RwNodeName: dummies.push(this.readString(extensionHeader.sectionSize)); break; case RwSections_1.RwSections.RwAnim: animNodes.push(this.readAnimNode()); break; default: console.debug("Extension type ".concat(extensionHeader.sectionType, " (").concat(extensionHeader.sectionType.toString(16), ") not found at offset (").concat(this.getPosition().toString(16), "). Skipping ").concat(extensionHeader.sectionSize, " bytes.")); this.skip(extensionHeader.sectionSize); break; } break; case RwSections_1.RwSections.RwGeometryList: geometryList = this.readGeometryList(); break; case RwSections_1.RwSections.RwAtomic: var atomic = this.readAtomic(); atomics[atomic.geometryIndex] = atomic.frameIndex; break; case RwSections_1.RwSections.RwNodeName: // For some reason, this frame is outside RwExtension. dummies.push(this.readString(header.sectionSize)); break; case RwSections_1.RwSections.RwAnim: // For III / VC models animNodes.push(this.readAnimNode()); break; default: console.debug("Section type ".concat(header.sectionType, " (").concat(header.sectionType.toString(16), ") not found at offset (").concat(this.getPosition().toString(16), "). Skipping ").concat(header.sectionSize, " bytes.")); this.skip(header.sectionSize); break; } } if (!version || !versionNumber) { throw new RwParseError_1.RwParseStructureNotFoundError('version'); } return { version: version, versionNumber: versionNumber, geometryList: geometryList, frameList: frameList, atomics: atomics, dummies: dummies, animNodes: animNodes, }; }; DffParser.prototype.readClump = function () { var versionNumber = this.readSectionHeader().versionNumber; var atomicCount = this.readUint32(); var lightCount; var cameraCount; if (versionNumber > 0x33000) { lightCount = this.readUint32(); cameraCount = this.readUint32(); } return { atomicCount: atomicCount, lightCount: lightCount, cameraCount: cameraCount }; }; DffParser.prototype.readFrameList = function () { this.readSectionHeader(); var frameCount = this.readUint32(); var frames = []; for (var i = 0; i < frameCount; i++) { // All these could probably be moved to readFrameData() var rotationMatrix = { right: { x: this.readFloat(), y: this.readFloat(), z: this.readFloat() }, up: { x: this.readFloat(), y: this.readFloat(), z: this.readFloat() }, at: { x: this.readFloat(), y: this.readFloat(), z: this.readFloat() }, }; var coordinatesOffset = { x: this.readFloat(), y: this.readFloat(), z: this.readFloat() }; var parentFrame = this.readInt32(); // Skip matrix creation internal flags // They are read by the game but are not used this.skip(4); frames.push({ rotationMatrix: rotationMatrix, coordinatesOffset: coordinatesOffset, parentFrame: parentFrame }); } return { frameCount: frameCount, frames: frames }; }; DffParser.prototype.readGeometryList = function () { var header = this.readSectionHeader(); var geometricObjectCount = this.readUint32(); var geometries = []; for (var i = 0; i < geometricObjectCount; i++) { this.readSectionHeader(); this.readSectionHeader(); var versionNumber = RwVersion_1.default.unpackVersion(header.versionNumber); var geometryData = this.readGeometry(versionNumber); geometries.push(geometryData); } return { geometricObjectCount: geometricObjectCount, geometries: geometries }; }; DffParser.prototype.readGeometry = function (versionNumber) { var flags = this.readUint16(); var textureCoordinatesCount = this.readUint8(); var _nativeGeometryFlags = this.readUint8(); var triangleCount = this.readUint32(); var vertexCount = this.readUint32(); var _morphTargetCount = this.readUint32(); // Surface properties var _ambient; var _specular; var _diffuse; if (versionNumber < 0x34000) { _ambient = this.readFloat(); _specular = this.readFloat(); _diffuse = this.readFloat(); } var _isTriangleStrip = (flags & (1 << 0)) !== 0; var _vertexTranslation = (flags & (1 << 1)) !== 0; var isTexturedUV1 = (flags & (1 << 2)) !== 0; var isGeometryPrelit = (flags & (1 << 3)) !== 0; var _hasNormals = (flags & (1 << 4)) !== 0; var _isGeometryLit = (flags & (1 << 5)) !== 0; var _shouldModulateMaterialColor = (flags & (1 << 6)) !== 0; var isTexturedUV2 = (flags & (1 << 7)) !== 0; var vertexColorInformation = []; var textureMappingInformation = []; var triangleInformation = []; // Geometry is marked as prelit if (isGeometryPrelit) { for (var i = 0; i < vertexCount; i++) { vertexColorInformation[i] = { r: this.readUint8(), g: this.readUint8(), b: this.readUint8(), a: this.readUint8() }; } } // Geometry either has first or second texture if (isTexturedUV1 || isTexturedUV2) { for (var textureCoordinateIndex = 0; textureCoordinateIndex < textureCoordinatesCount; textureCoordinateIndex++) { textureMappingInformation[textureCoordinateIndex] = []; for (var vertexIndex = 0; vertexIndex < vertexCount; vertexIndex++) { textureMappingInformation[textureCoordinateIndex][vertexIndex] = { u: this.readFloat(), v: this.readFloat() }; } } } for (var i = 0; i < triangleCount; i++) { // Information is written in this order var vertex2 = this.readUint16(); var vertex1 = this.readUint16(); var materialId = this.readUint16(); var vertex3 = this.readUint16(); triangleInformation[i] = { vector: { x: vertex1, y: vertex2, z: vertex3 }, materialId: materialId }; } // We are sure that there's only one morph target, but if // we are wrong, we have to loop these through morphTargetCount var boundingSphere = { vector: { x: this.readFloat(), y: this.readFloat(), z: this.readFloat() }, radius: this.readFloat(), }; var hasVertices = !!this.readUint32(); var hasNormals = !!this.readUint32(); var vertexInformation = []; if (hasVertices) { for (var i = 0; i < vertexCount; i++) { vertexInformation[i] = { x: this.readFloat(), y: this.readFloat(), z: this.readFloat() }; } } var normalInformation = []; if (hasNormals) { for (var i = 0; i < vertexCount; i++) { normalInformation[i] = { x: this.readFloat(), y: this.readFloat(), z: this.readFloat() }; } } var materialList = this.readMaterialList(); var sectionSize = this.readSectionHeader().sectionSize; var position = this.getPosition(); var binMesh = this.readBinMesh(); var skin = undefined; if (this.readSectionHeader().sectionType == RwSections_1.RwSections.RwSkin) { skin = this.readSkin(vertexCount); } this.setPosition(position + sectionSize); return { textureCoordinatesCount: textureCoordinatesCount, textureMappingInformation: textureMappingInformation, boundingSphere: boundingSphere, hasVertices: hasVertices, hasNormals: hasNormals, vertexColorInformation: vertexColorInformation, vertexInformation: vertexInformation, normalInformation: normalInformation, triangleInformation: triangleInformation, materialList: materialList, binMesh: binMesh, skin: skin, }; }; DffParser.prototype.readBinMesh = function () { this.readSectionHeader(); // Flags (0: triangle list, 1: triangle strip) this.skip(4); var meshCount = this.readUint32(); // Total number of indices this.skip(4); var meshes = []; for (var i = 0; i < meshCount; i++) { meshes.push(this.readMesh()); } return { meshCount: meshCount, meshes: meshes }; }; DffParser.prototype.readSkin = function (vertexCount) { var boneCount = this.readUint8(); var usedBoneCount = this.readUint8(); var maxWeightsPerVertex = this.readUint8(); this.skip(1); // Padding this.skip(usedBoneCount); // Skipping special indices var boneVertexIndices = []; var vertexWeights = []; var inverseBoneMatrices = []; for (var i = 0; i < vertexCount; i++) { var indices = []; for (var j = 0; j < 4; j++) { indices.push(this.readUint8()); } boneVertexIndices.push(indices); } for (var i = 0; i < vertexCount; i++) { var weights = []; for (var j = 0; j < 4; j++) { weights.push(this.readFloat()); } vertexWeights.push(weights); } for (var i = 0; i < boneCount; i++) { var matrix4x4 = { right: { x: this.readFloat(), y: this.readFloat(), z: this.readFloat(), t: this.readFloat() }, up: { x: this.readFloat(), y: this.readFloat(), z: this.readFloat(), t: this.readFloat() }, at: { x: this.readFloat(), y: this.readFloat(), z: this.readFloat(), t: this.readFloat() }, transform: { x: this.readFloat(), y: this.readFloat(), z: this.readFloat(), t: this.readFloat() }, }; inverseBoneMatrices.push(matrix4x4); } return { boneCount: boneCount, usedBoneCount: usedBoneCount, maxWeightsPerVertex: maxWeightsPerVertex, boneVertexIndices: boneVertexIndices, vertexWeights: vertexWeights, inverseBoneMatrices: inverseBoneMatrices, }; }; DffParser.prototype.readAnimNode = function () { this.skip(4); // Skipping AnimVersion property (0x100) var boneId = this.readInt32(); var boneCount = this.readInt32(); var bones = []; if (boneId == 0) { this.skip(8); // Skipping flags and keyFrameSize properties } if (boneCount > 0) { for (var i = 0; i < boneCount; i++) { bones.push({ boneId: this.readInt32(), boneIndex: this.readInt32(), flags: this.readInt32() }); } } return { boneId: boneId, bonesCount: boneCount, bones: bones }; }; DffParser.prototype.readMesh = function () { var indexCount = this.readUint32(); var materialIndex = this.readUint32(); var indices = []; for (var i = 0; i < indexCount; i++) { indices.push(this.readUint32()); } return { indexCount: indexCount, materialIndex: materialIndex, indices: indices }; }; DffParser.prototype.readMaterialList = function () { this.readSectionHeader(); this.readSectionHeader(); var materialInstanceCount = this.readUint32(); var materialIndices = []; for (var i = 0; i < materialInstanceCount; i++) { var materialIndex = this.readInt32(); materialIndices.push(materialIndex); } var materialData = []; for (var i = 0; i < materialInstanceCount; i++) { var materialIndex = materialIndices[i]; if (materialIndex == -1) { materialData.push(this.readMaterial()); } else { materialData.push(materialData[materialIndex]); } } return { materialInstanceCount: materialInstanceCount, materialData: materialData }; }; DffParser.prototype.readMaterial = function () { this.readSectionHeader(); var header = this.readSectionHeader(); // Flags - not used this.skip(4); var color = { r: this.readUint8(), g: this.readUint8(), b: this.readUint8(), a: this.readUint8() }; // Unknown - not used this.skip(4); var isTextured = this.readUint32() > 0; // Surface properties var ambient; var specular; var diffuse; if (header.versionNumber > 0x30400) { ambient = this.readFloat(); specular = this.readFloat(); diffuse = this.readFloat(); } var texture; if (isTextured) { texture = this.readTexture(); } // Skip various unused extensions this.skip(this.readSectionHeader().sectionSize); return { color: color, isTextured: isTextured, ambient: ambient, specular: specular, diffuse: diffuse, texture: texture }; }; DffParser.prototype.readTexture = function () { this.readSectionHeader(); this.readSectionHeader(); var textureData = this.readUint32(); var textureFiltering = (textureData & 0xFF); var uAddressing = (textureData & 0xF00) >> 8; var vAddressing = (textureData & 0xF000) >> 12; var usesMipLevels = (textureData & (1 << 16)) !== 0; var textureNameSize = this.readSectionHeader().sectionSize; var textureName = this.readString(textureNameSize); // Skip various unused extensions this.skip(this.readSectionHeader().sectionSize); this.skip(this.readSectionHeader().sectionSize); return { textureFiltering: textureFiltering, uAddressing: uAddressing, vAddressing: vAddressing, usesMipLevels: usesMipLevels, textureName: textureName }; }; DffParser.prototype.readAtomic = function () { this.readSectionHeader(); var frameIndex = this.readUint32(); var geometryIndex = this.readUint32(); var flags = this.readUint32(); // Skip unused bytes this.skip(4); return { frameIndex: frameIndex, geometryIndex: geometryIndex, flags: flags }; }; return DffParser; }(RwFile_1.RwFile)); exports.DffParser = DffParser;