UNPKG

babylon-mmd

Version:
409 lines (408 loc) 15.3 kB
import { Vector3 } from "@babylonjs/core/Maths/math.vector"; import { AlignedDataSerializer } from "../../Loader/Optimized/alignedDataSerializer"; import { PmxObject } from "../../Loader/Parser/pmxObject"; /** * mmd model metadata representation in binary * * boneCount: uint32 * appendTransformCount: uint32 * ikCount: uint32 * { * restPosition: float32[3] * absoluteInverseBindMatrix: float32[16] * parentBoneIndex: int32 * transformOrder: int32 * flag: uint16 * -- padding: uint16 * appendTransform: { // optional * parentIndex: int32 * ratio: float32 * } * axisLimit: { // optional * axis: float32[3] * } * ik: { // optional * target: int32 * iteration: int32 * rotationConstraint: float32 * linkCount: uint32 * { * target: int32 * hasLimitation: uint8 * -- padding: uint8 * -- padding: uint16 * limitation: { // optional * minimumAngle: float32[3] * maximumAngle: float32[3] * } * }[linkCount] * } * }[boneCount] * * morphCount: uint32 * { // if boneMorph * kind: uint8 * -- padding: uint8 * -- padding: uint16 * boneCount: uint32 * indices: int32[boneCount] * positions: float32[boneCount * 3] * rotations: float32[boneCount * 4] * } | { // if groupMorph * kind: uint8 * -- padding: uint8 * -- padding: uint16 * indexCount: uint32 * indices: int32[indexCount] * ratios: float32[indexCount] * }[morphCount] * * physicsInfoKind: uint8 // 0: no physics, 1: striped rigid bodies, 2: full physics * -- padding: uint8 * -- padding: uint16 * * { // if physicsInfoKind === 1 * rigidBodyCount: uint32 * { * boneIndex: int32 * physicsMode: uint8 * -- padding: uint8 * -- padding: uint16 * }[rigidBodyCount] * } * { // if physicsInfoKind === 2 * physicsWorldId: uint32 * kinematicSharedPhysicsWorldIdCount: uint32 * kinematicSharedPhysicsWorldIds: uint32[kinematicSharedPhysicsWorldIdCount] * modelInitialWorldMatrix: float32[16] * disableOffsetForConstraintFrame: uint8 * -- padding: uint8 * -- padding: uint16 * * rigidBodyCount: uint32 * { * boneIndex: int32 * collisionGroup: uint8 * shapeType: uint8 * collisionMask: uint16 * shapeSize: float32[4] * shapePosition: float32[3] * shapeRotation: float32[3] * mass: float32 * linearDamping: float32 * angularDamping: float32 * repulsion: float32 * friction: float32 * physicsMode: uint8 * -- padding: uint8 * -- padding: uint16 * }[rigidBodyCount] * * jointCount: uint32 * { * type: uint8 * -- padding: uint8 * -- padding: uint16 * rigidBodyIndexA: int32 * rigidBodyIndexB: int32 * position: float32[3] * rotation: float32[3] * positionMin: float32[3] * positionMax: float32[3] * rotationMin: float32[3] * rotationMax: float32[3] * springPosition: float32[3] * springRotation: float32[3] * }[jointCount] * } */ /** * @internal */ export class MmdMetadataEncoder { _logger; _encodePhysicsOptions; constructor(logger) { this._logger = logger; this._encodePhysicsOptions = true; } setEncodePhysicsOptions(options) { if (typeof options === "boolean") { this._encodePhysicsOptions = options; } else { let validatedWorldId = options.worldId; if (validatedWorldId !== undefined) { if (validatedWorldId < 0 || 0xFFFFFFFF < validatedWorldId) { validatedWorldId = undefined; this._logger.warn(`WorldId ${options.worldId} is out of range`); } } const validatedKinematicSharedWorldIds = []; if (options.kinematicSharedWorldIds !== undefined) { const kinematicSharedWorldIds = new Set(options.kinematicSharedWorldIds); for (const kinematicWorldId of kinematicSharedWorldIds) { if (kinematicWorldId === validatedWorldId) { this._logger.warn(`Kinematic shared worldId ${kinematicWorldId} is same as worldId`); } else if (kinematicWorldId < 0 || 0xFFFFFFFF < kinematicWorldId) { this._logger.warn(`Kinematic shared worldId ${kinematicWorldId} is out of range`); } else { validatedKinematicSharedWorldIds.push(kinematicWorldId); } } } this._encodePhysicsOptions = { worldId: validatedWorldId, kinematicSharedWorldIds: validatedKinematicSharedWorldIds, disableOffsetForConstraintFrame: options.disableOffsetForConstraintFrame }; } } _computeBonesSize(metadata) { let dataLength = 4 // boneCount + 4 // appendTransformCount + 4; // ikCount const bones = metadata.bones; for (let i = 0; i < bones.length; ++i) { dataLength += 4 * 3 // restPosition + 4 * 16 // absoluteInverseBindMatrix + 4 // parentBoneIndex + 4 // transformOrder + 2 // flag + 2; // padding const bone = bones[i]; if (bone.appendTransform) { dataLength += 4 // parentIndex + 4; // ratio } if (bone.axisLimit) { dataLength += 4 * 3; // axis } if (bone.ik) { dataLength += 4 // target + 4 // iteration + 4 // rotationConstraint + 4; // linkCount const links = bone.ik.links; for (let j = 0; j < links.length; ++j) { dataLength += 4 // target + 1 // hasLimitation + 3; // padding const link = links[j]; if (link.limitation) { dataLength += 4 * 3 // minimumAngle + 4 * 3; // maximumAngle } } } } return dataLength; } _computeMorphsSize(metadata) { let dataLength = 4; // morphCount const morphs = metadata.morphs; for (let i = 0; i < morphs.length; ++i) { const morph = morphs[i]; switch (morph.type) { case PmxObject.Morph.Type.BoneMorph: { const indices = morph.indices; dataLength += 1 // kind + 3 // padding + 4 // boneCount + 4 * indices.length // indices + 4 * 3 * indices.length // positions + 4 * 4 * indices.length; // rotations break; } case PmxObject.Morph.Type.GroupMorph: { const indices = morph.indices; dataLength += 1 // kind + 3 // padding + 4 // indexCount + 4 * indices.length // indices + 4 * indices.length; // ratios break; } } } return dataLength; } _computePhysicsSize(metadata) { if (metadata === null) { return 1 // physicsInfoKind + 3; // padding } let dataLength = 1 // physicsInfoKind + 3 // padding + 4; // rigidBodyCount const rigidBodies = metadata.rigidBodies; for (let i = 0; i < rigidBodies.length; ++i) { dataLength += 4 // boneIndex + 1 // physicsMode + 3; // padding } return dataLength; } computeSize(mmdMesh) { const metadata = mmdMesh.metadata; const dataLength = this._computeBonesSize(metadata) + this._computeMorphsSize(metadata) + this._computePhysicsSize(this._encodePhysicsOptions ? metadata : null); return dataLength; } _encodeBones(serializer, metadata, linkedBones) { const restPosition = new Vector3(); const bones = metadata.bones; serializer.setUint32(bones.length); // boneCount let appendTransformCount = 0; let ikCount = 0; for (let i = 0; i < bones.length; ++i) { const bone = bones[i]; if (bone.appendTransform) { appendTransformCount += 1; } if (bone.ik) { ikCount += 1; } } serializer.setUint32(appendTransformCount); // appendTransformCount serializer.setUint32(ikCount); // ikCount for (let i = 0; i < bones.length; ++i) { const bone = bones[i]; const linkedBone = linkedBones[i]; let flag = bone.flag; flag &= (bone.appendTransform === undefined ? ~(PmxObject.Bone.Flag.HasAppendMove & PmxObject.Bone.Flag.HasAppendRotate) : ~0) & ~PmxObject.Bone.Flag.HasAxisLimit & ~PmxObject.Bone.Flag.IsIkEnabled; if (bone.axisLimit) { flag |= PmxObject.Bone.Flag.HasAxisLimit; } if (bone.ik) { flag |= PmxObject.Bone.Flag.IsIkEnabled; } serializer.setFloat32Array(linkedBone.getRestMatrix().getTranslationToRef(restPosition).asArray()); // restPosition serializer.setFloat32Array(linkedBone.getAbsoluteInverseBindMatrix().m); // absoluteInverseBindMatrix serializer.setInt32(bone.parentBoneIndex); // parentBoneIndex serializer.setInt32(bone.transformOrder); // transformOrder serializer.setUint16(bone.flag); // flag serializer.offset += 2; // padding if (bone.appendTransform) { serializer.setInt32(bone.appendTransform.parentIndex); // parentIndex serializer.setFloat32(bone.appendTransform.ratio); // ratio } if (bone.axisLimit) { serializer.setFloat32Array(bone.axisLimit); // axis } if (bone.ik) { serializer.setInt32(bone.ik.target); // target serializer.setInt32(bone.ik.iteration); // iteration serializer.setFloat32(bone.ik.rotationConstraint); // rotationConstraint serializer.setUint32(bone.ik.links.length); // linkCount const links = bone.ik.links; for (let j = 0; j < links.length; ++j) { const link = links[j]; serializer.setInt32(link.target); // target serializer.setUint8(link.limitation ? 1 : 0); // hasLimitation serializer.offset += 3; // padding if (link.limitation) { serializer.setFloat32Array(link.limitation.minimumAngle); // minimumAngle serializer.setFloat32Array(link.limitation.maximumAngle); // maximumAngle } } } } } _encodeMorphs(serializer, metadata) { const morphs = metadata.morphs; let morphCount = 0; for (let i = 0; i < morphs.length; ++i) { const morph = morphs[i]; switch (morph.type) { case PmxObject.Morph.Type.BoneMorph: case PmxObject.Morph.Type.GroupMorph: morphCount += 1; break; } } const wasmMorphMap = new Int32Array(morphs.length).fill(-1); for (let i = 0, nextIndex = 0; i < morphs.length; ++i) { const morph = morphs[i]; if (morph.type !== PmxObject.Morph.Type.BoneMorph && morph.type !== PmxObject.Morph.Type.GroupMorph) { continue; } wasmMorphMap[i] = nextIndex; nextIndex += 1; } serializer.setUint32(morphCount); // morphCount for (let i = 0; i < morphs.length; ++i) { const morph = morphs[i]; switch (morph.type) { case PmxObject.Morph.Type.BoneMorph: { serializer.setUint8(morph.type); // kind serializer.offset += 3; // padding serializer.setUint32(morph.indices.length); // boneCount serializer.setInt32Array(morph.indices); // indices serializer.setFloat32Array(morph.positions); // positions serializer.setFloat32Array(morph.rotations); // rotations } break; case PmxObject.Morph.Type.GroupMorph: { serializer.setUint8(morph.type); // kind serializer.offset += 3; // padding serializer.setUint32(morph.indices.length); // indexCount const indices = morph.indices; const remappedIndices = new Int32Array(indices.length); for (let j = 0; j < remappedIndices.length; ++j) { remappedIndices[j] = wasmMorphMap[indices[j]]; } serializer.setInt32Array(remappedIndices); // indices serializer.setFloat32Array(morph.ratios); // ratios } break; } } return wasmMorphMap; } _encodePhysics(serializer, metadata, _rootTransform) { if (metadata === null) { serializer.setUint8(0); // physicsInfoKind serializer.offset += 3; // padding return; } serializer.setUint8(1); // physicsInfoKind serializer.offset += 3; // padding const bones = metadata.bones; const boneNameMap = new Map(); for (let i = 0; i < bones.length; ++i) { boneNameMap.set(bones[i].name, i); } const rigidBodies = metadata.rigidBodies; serializer.setUint32(rigidBodies.length); // rigidBodyCount for (let i = 0; i < rigidBodies.length; ++i) { const rigidBody = rigidBodies[i]; const boneIndex = rigidBody.boneIndex < 0 || bones.length <= rigidBody.boneIndex ? boneNameMap.get(rigidBody.name) ?? -1 // fallback to name : rigidBody.boneIndex; serializer.setInt32(boneIndex); // boneIndex serializer.setUint8(rigidBody.physicsMode); // physicsMode serializer.offset += 3; // padding } } encode(mmdMesh, linkedBones, buffer) { const metadata = mmdMesh.metadata; const serializer = new AlignedDataSerializer(buffer.buffer); serializer.offset = buffer.byteOffset; this._encodeBones(serializer, metadata, linkedBones); const wasmMorphMap = this._encodeMorphs(serializer, metadata); this._encodePhysics(serializer, this._encodePhysicsOptions ? metadata : null, mmdMesh); return wasmMorphMap; } }