UNPKG

mdx-m3-viewer

Version:

A browser WebGL model viewer. Mainly focused on models of the games Warcraft 3 and Starcraft 2.

238 lines 11 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.createSkeletalNodes = exports.SkeletalNode = void 0; const gl_matrix_1 = require("gl-matrix"); const gl_matrix_addon_1 = require("../common/gl-matrix-addon"); const locationHeap = gl_matrix_1.vec3.create(); const rotationHeap = gl_matrix_1.quat.create(); const scalingHeap = gl_matrix_1.vec3.create(); const cameraRayHeap = gl_matrix_1.vec3.create(); const rotationHeap2 = gl_matrix_1.quat.create(); /** * A skeletal node used for skeletons. * * Expected to be created with createSkeletalNodes() below. */ class SkeletalNode { constructor(pivot, localLocation, localRotation, localScale, worldLocation, worldRotation, worldScale, inverseWorldLocation, inverseWorldRotation, inverseWorldScale, localMatrix, worldMatrix) { this.dontInheritTranslation = false; this.dontInheritRotation = false; this.dontInheritScaling = false; this.billboarded = false; this.billboardedX = false; this.billboardedY = false; this.billboardedZ = false; this.dirty = true; this.wasDirty = false; this.parent = null; this.children = []; /** * The object associated with this node, if there is any. */ this.object = null; this.pivot = pivot; this.localLocation = localLocation; this.localRotation = localRotation; this.localScale = localScale; this.worldLocation = worldLocation; this.worldRotation = worldRotation; this.worldScale = worldScale; this.inverseWorldLocation = inverseWorldLocation; this.inverseWorldRotation = inverseWorldRotation; this.inverseWorldScale = inverseWorldScale; this.localMatrix = localMatrix; this.worldMatrix = worldMatrix; this.localRotation[3] = 1; this.localScale.fill(1); this.localMatrix[0] = 1; this.localMatrix[5] = 1; this.localMatrix[10] = 1; this.localMatrix[15] = 1; } /** * Recalculate this skeletal node. */ recalculateTransformation(instance) { const scene = instance.scene; const localMatrix = this.localMatrix; const localLocation = this.localLocation; const localRotation = this.localRotation; const localScale = this.localScale; const worldMatrix = this.worldMatrix; const worldLocation = this.worldLocation; const worldRotation = this.worldRotation; const worldScale = this.worldScale; const pivot = this.pivot; const inverseWorldLocation = this.inverseWorldLocation; const inverseWorldRotation = this.inverseWorldRotation; const inverseWorldScale = this.inverseWorldScale; const parent = this.parent; let computedLocation; let computedRotation; let computedScaling; if (this.dontInheritTranslation) { gl_matrix_1.vec3.add(locationHeap, parent.inverseWorldLocation, worldLocation); computedLocation = gl_matrix_1.vec3.add(locationHeap, locationHeap, localLocation); } else { computedLocation = localLocation; } if (this.dontInheritScaling) { gl_matrix_1.vec3.mul(locationHeap, parent.inverseWorldScale, instance.worldScale); computedScaling = gl_matrix_1.vec3.mul(locationHeap, locationHeap, localScale); } else { computedScaling = localScale; } if (this.billboarded) { computedRotation = rotationHeap; gl_matrix_1.quat.copy(computedRotation, parent.inverseWorldRotation); gl_matrix_1.quat.mul(computedRotation, computedRotation, scene.camera.inverseRotation); this.convertBasis(computedRotation); gl_matrix_1.quat.mul(computedRotation, computedRotation, localRotation); } else { const { billboardedX, billboardedY, billboardedZ } = this; if (billboardedX || billboardedY || billboardedZ) { if (billboardedX) { if (computedScaling === localScale) { computedScaling = scalingHeap; gl_matrix_1.vec3.copy(computedScaling, localScale); } // (Original comment from Retera's Warsmash) // It took me many hours to deduce from playing around that this negative one // multiplier should be here. I suggest a lot of testing before you remove it. computedScaling[2] *= -1; } // Inverse that local rotation rotationHeap2[0] = -localRotation[0]; rotationHeap2[1] = -localRotation[1]; rotationHeap2[2] = -localRotation[2]; gl_matrix_1.quat.mul(rotationHeap2, rotationHeap2, parent.inverseWorldRotation); gl_matrix_1.vec3.transformQuat(cameraRayHeap, scene.camera.billboardedVectors[6], rotationHeap2); if (billboardedX) { gl_matrix_1.quat.setAxisAngle(rotationHeap2, gl_matrix_addon_1.VEC3_UNIT_X, Math.atan2(cameraRayHeap[2], cameraRayHeap[1])); } else if (billboardedY) { gl_matrix_1.quat.setAxisAngle(rotationHeap2, gl_matrix_addon_1.VEC3_UNIT_Y, Math.atan2(-cameraRayHeap[2], cameraRayHeap[0])); } else { gl_matrix_1.quat.setAxisAngle(rotationHeap2, gl_matrix_addon_1.VEC3_UNIT_Z, Math.atan2(cameraRayHeap[1], cameraRayHeap[0])); } computedRotation = rotationHeap; gl_matrix_1.quat.mul(computedRotation, localRotation, rotationHeap2); } else { computedRotation = localRotation; } } if (this.dontInheritRotation) { gl_matrix_1.quat.mul(rotationHeap2, parent.inverseWorldRotation, instance.worldRotation); computedRotation = gl_matrix_1.quat.mul(rotationHeap, rotationHeap2, computedRotation); } // Local matrix gl_matrix_1.mat4.fromRotationTranslationScaleOrigin(localMatrix, computedRotation, computedLocation, computedScaling, pivot); // World matrix gl_matrix_1.mat4.mul(worldMatrix, parent.worldMatrix, localMatrix); // World location // vec3.transformMat4(worldLocation, pivot, worldMatrix); const x = pivot[0]; const y = pivot[1]; const z = pivot[2]; worldLocation[0] = worldMatrix[0] * x + worldMatrix[4] * y + worldMatrix[8] * z + worldMatrix[12]; worldLocation[1] = worldMatrix[1] * x + worldMatrix[5] * y + worldMatrix[9] * z + worldMatrix[13]; worldLocation[2] = worldMatrix[2] * x + worldMatrix[6] * y + worldMatrix[10] * z + worldMatrix[14]; // Inverse world location inverseWorldLocation[0] = -worldLocation[0]; inverseWorldLocation[1] = -worldLocation[1]; inverseWorldLocation[2] = -worldLocation[2]; // World rotation gl_matrix_1.quat.mul(worldRotation, parent.worldRotation, computedRotation); // Inverse world rotation inverseWorldRotation[0] = -worldRotation[0]; inverseWorldRotation[1] = -worldRotation[1]; inverseWorldRotation[2] = -worldRotation[2]; inverseWorldRotation[3] = worldRotation[3]; // World scale const parentScale = parent.worldScale; worldScale[0] = parentScale[0] * computedScaling[0]; worldScale[1] = parentScale[1] * computedScaling[1]; worldScale[2] = parentScale[2] * computedScaling[2]; // Inverse world scale inverseWorldScale[0] = 1 / worldScale[0]; inverseWorldScale[1] = 1 / worldScale[1]; inverseWorldScale[2] = 1 / worldScale[2]; } /** * Allows inherited node classes to run extra transformations when billboarding. * * This is needed because the different model formats are in different vector spaces. */ convertBasis(_rotation) { } } exports.SkeletalNode = SkeletalNode; const NODE_SHARED_SIZE = 65; /** * Creates an array of skeletal nodes with shared memory. * * The returned object contains the node array itself, the backing buffer, and all of the different shared arrays. */ function createSkeletalNodes(count, Node = SkeletalNode) { const data = new Float32Array(count * NODE_SHARED_SIZE); const nodes = []; let offset = 0; const count3 = count * 3; const count4 = count * 4; const count16 = count * 16; const pivots = data.subarray(offset, offset + count3); offset += count3; const localLocations = data.subarray(offset, offset + count3); offset += count3; const localRotations = data.subarray(offset, offset + count4); offset += count4; const localScales = data.subarray(offset, offset + count3); offset += count3; const worldLocations = data.subarray(offset, offset + count3); offset += count3; const worldRotations = data.subarray(offset, offset + count4); offset += count4; const worldScales = data.subarray(offset, offset + count3); offset += count3; const inverseWorldLocations = data.subarray(offset, offset + count3); offset += count3; const invereseWorldRotations = data.subarray(offset, offset + count4); offset += count4; const inverseWorldScales = data.subarray(offset, offset + count3); offset += count3; const localMatrices = data.subarray(offset, offset + count16); offset += count16; const worldMatrices = data.subarray(offset, offset + count16); for (let i = 0; i < count; i++) { const i3 = i * 3; const i33 = i3 + 3; const i4 = i * 4; const i44 = i4 + 4; const i16 = i * 16; const i1616 = i16 + 16; nodes[i] = new Node(pivots.subarray(i3, i33), localLocations.subarray(i3, i33), localRotations.subarray(i4, i44), localScales.subarray(i3, i33), worldLocations.subarray(i3, i33), worldRotations.subarray(i4, i44), worldScales.subarray(i3, i33), inverseWorldLocations.subarray(i3, i33), invereseWorldRotations.subarray(i4, i44), inverseWorldScales.subarray(i3, i33), localMatrices.subarray(i16, i1616), worldMatrices.subarray(i16, i1616)); } return { data, nodes, pivots, localLocations, localRotations, localScales, worldLocations, worldRotations, worldScales, inverseWorldLocations, invereseWorldRotations, inverseWorldScales, localMatrices, worldMatrices, }; } exports.createSkeletalNodes = createSkeletalNodes; //# sourceMappingURL=skeletalnode.js.map