Skip to content
2 changes: 2 additions & 0 deletions src/renderware/RwSections.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ export enum RwSections {
RwTextureNative = 0x0015,
RwTextureDictionary = 0x0016,
RwGeometryList = 0x001A,
RwSkin = 0x116,
RwAnim = 0x11E,

// Toolkit
RwMaterialEffectsPLG = 0x0120,
Expand Down
129 changes: 128 additions & 1 deletion src/renderware/dff/DffParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export interface RwDff {
frameList: RwFrameList | null,
atomics: number[],
dummies: string[],
animNodes: RwAnimNode[],
}

export interface RwClump {
Expand All @@ -18,6 +19,19 @@ export interface RwClump {
cameraCount?: number,
}

export interface RwAnimNode {
boneId: number,
bonesCount: number,
bones: RwBone[],
}

export interface RwBone {
boneId: number,
boneIndex: number,
flags: number,
}


export interface RwFrame {
rotationMatrix: RwMatrix3,
coordinatesOffset: RwVector3,
Expand Down Expand Up @@ -63,6 +77,7 @@ export interface RwGeometry {
boundingSphere?: RwSphere,
materialList: RwMaterialList,
binMesh: RwBinMesh,
skin?: RwSkin,
}

export interface RwGeometryList {
Expand All @@ -81,6 +96,15 @@ export interface RwBinMesh {
meshes: RwMesh[],
}

export interface RwSkin {
boneCount: number,
usedBoneCount: number,
maxWeightsPerVertex: number,
boneVertexIndices: number[][],
vertexWeights: number[][],
inverseBoneMatrices: RwMatrix4[],
}

export interface RwMesh {
materialIndex: number,
indexCount: number,
Expand All @@ -93,6 +117,13 @@ export interface RwMatrix3 {
at: RwVector3,
}

export interface RwMatrix4 {
right: RwVector4,
up: RwVector4,
at: RwVector4,
transform: RwVector4,
}

export interface RwColor {
r: number,
g: number,
Expand All @@ -110,6 +141,12 @@ export interface RwVector3 {
y: number,
z: number,
}
export interface RwVector4 {
x: number,
y: number,
z: number,
t: number,
}

export interface RwTextureCoordinate {
u: number,
Expand Down Expand Up @@ -137,6 +174,7 @@ export class DffParser extends RwFile {
let versionNumber: number | undefined;
let atomics: number[] = [];
let dummies: string[] = [];
let animNodes: RwAnimNode[] = [];
let geometryList: RwGeometryList | null = null;
let frameList: RwFrameList | null = null;

Expand Down Expand Up @@ -166,6 +204,9 @@ export class DffParser extends RwFile {
case RwSections.RwNodeName:
dummies.push(this.readString(extensionHeader.sectionSize));
break;
case RwSections.RwAnim:
animNodes.push(this.readAnimNode());
break;
default:
console.debug(`Extension type ${extensionHeader.sectionType} (${extensionHeader.sectionType.toString(16)}) not found at offset (${this.getPosition().toString(16)}). Skipping ${extensionHeader.sectionSize} bytes.`);
this.skip(extensionHeader.sectionSize);
Expand All @@ -183,6 +224,10 @@ export class DffParser extends RwFile {
// For some reason, this frame is outside RwExtension.
dummies.push(this.readString(header.sectionSize));
break;
case RwSections.RwAnim:
// For III / VC models
animNodes.push(this.readAnimNode());
break;
default:
console.debug(`Section type ${header.sectionType} (${header.sectionType.toString(16)}) not found at offset (${this.getPosition().toString(16)}). Skipping ${header.sectionSize} bytes.`);
this.skip(header.sectionSize);
Expand All @@ -201,6 +246,7 @@ export class DffParser extends RwFile {
frameList: frameList,
atomics: atomics,
dummies: dummies,
animNodes: animNodes,
};
}

Expand Down Expand Up @@ -350,10 +396,14 @@ export class DffParser extends RwFile {
}

let materialList = this.readMaterialList();

let sectionSize = this.readSectionHeader().sectionSize;
let position = this.getPosition();
let binMesh = this.readBinMesh();
let skin = undefined;

if (this.readSectionHeader().sectionType == RwSections.RwSkin) {
skin = this.readSkin(vertexCount);
}

this.setPosition(position + sectionSize);

Expand All @@ -369,6 +419,7 @@ export class DffParser extends RwFile {
triangleInformation,
materialList,
binMesh,
skin,
};
}

Expand All @@ -394,6 +445,82 @@ export class DffParser extends RwFile {
};
}

public readSkin(vertexCount : number): RwSkin {
const boneCount = this.readUint8();
const usedBoneCount = this.readUint8();
const maxWeightsPerVertex = this.readUint8();

this.skip(1); // Padding
this.skip(usedBoneCount); // Skipping special indices

const boneVertexIndices: number[][] = [];
const vertexWeights: number[][] = [];
const inverseBoneMatrices: RwMatrix4[] = [];

for (let i = 0; i < vertexCount; i++) {
const indices: number[] = [];
for (let j = 0; j < 4; j++) {
indices.push(this.readUint8());
}
boneVertexIndices.push(indices);
}

for (let i = 0; i < vertexCount; i++) {
const weights: number[] = [];
for (let j = 0; j < 4; j++) {
weights.push(this.readFloat());
}
vertexWeights.push(weights);
}

for (let i = 0; i < boneCount; i++) {
const matrix4x4: RwMatrix4 = {
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,
usedBoneCount,
maxWeightsPerVertex,
boneVertexIndices,
vertexWeights,
inverseBoneMatrices,
}
}

public readAnimNode() :RwAnimNode {
this.skip(4); // Skipping AnimVersion property (0x100)
const boneId = this.readInt32();
const boneCount = this.readInt32();
const bones :RwBone[] = [];

if (boneId == 0) {
this.skip(8); // Skipping flags and keyFrameSize properties
}

if (boneCount > 0) {
for (let i = 0; i < boneCount; i++){
bones.push({
boneId: this.readInt32(),
boneIndex: this.readInt32(),
flags: this.readInt32()
});
}
}

return {
boneId: boneId,
bonesCount: boneCount,
bones: bones
}
}

public readMesh(): RwMesh {
const indexCount = this.readUint32();
const materialIndex = this.readUint32();
Expand Down
Binary file added tests/assets/wuzimu.dff
Binary file not shown.
Loading