UNPKG

@openhps/core

Version:

Open Hybrid Positioning System - Core component

280 lines (252 loc) 10 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.skinning = exports.default = exports.computeSkinning = void 0; var _Node = _interopRequireDefault(require("../core/Node.js")); var _constants = require("../core/constants.js"); var _TSLBase = require("../tsl/TSLBase.js"); var _AttributeNode = require("../core/AttributeNode.js"); var _ReferenceNode = require("./ReferenceNode.js"); var _OperatorNode = require("../math/OperatorNode.js"); var _Normal = require("./Normal.js"); var _Position = require("./Position.js"); var _Tangent = require("./Tangent.js"); var _UniformNode = require("../core/UniformNode.js"); var _BufferNode = require("./BufferNode.js"); var _NodeUtils = require("../core/NodeUtils.js"); var _StorageBufferNode = require("./StorageBufferNode.js"); var _InstancedBufferAttribute = require("../../core/InstancedBufferAttribute.js"); var _IndexNode = require("../core/IndexNode.js"); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } const _frameId = new WeakMap(); /** * This node implements the vertex transformation shader logic which is required * for skinning/skeletal animation. * * @augments Node */ class SkinningNode extends _Node.default { static get type() { return 'SkinningNode'; } /** * Constructs a new skinning node. * * @param {SkinnedMesh} skinnedMesh - The skinned mesh. */ constructor(skinnedMesh) { super('void'); /** * The skinned mesh. * * @type {SkinnedMesh} */ this.skinnedMesh = skinnedMesh; /** * The update type overwritten since skinning nodes are updated per object. * * @type {string} */ this.updateType = _constants.NodeUpdateType.OBJECT; // /** * The skin index attribute. * * @type {AttributeNode} */ this.skinIndexNode = (0, _AttributeNode.attribute)('skinIndex', 'uvec4'); /** * The skin weight attribute. * * @type {AttributeNode} */ this.skinWeightNode = (0, _AttributeNode.attribute)('skinWeight', 'vec4'); /** * The bind matrix node. * * @type {Node<mat4>} */ this.bindMatrixNode = (0, _ReferenceNode.reference)('bindMatrix', 'mat4'); /** * The bind matrix inverse node. * * @type {Node<mat4>} */ this.bindMatrixInverseNode = (0, _ReferenceNode.reference)('bindMatrixInverse', 'mat4'); /** * The bind matrices as a uniform buffer node. * * @type {Node} */ this.boneMatricesNode = (0, _ReferenceNode.referenceBuffer)('skeleton.boneMatrices', 'mat4', skinnedMesh.skeleton.bones.length); /** * The current vertex position in local space. * * @type {Node<vec3>} */ this.positionNode = _Position.positionLocal; /** * The result of vertex position in local space. * * @type {Node<vec3>} */ this.toPositionNode = _Position.positionLocal; /** * The previous bind matrices as a uniform buffer node. * Required for computing motion vectors. * * @type {?Node} * @default null */ this.previousBoneMatricesNode = null; } /** * Transforms the given vertex position via skinning. * * @param {Node} [boneMatrices=this.boneMatricesNode] - The bone matrices * @param {Node<vec3>} [position=this.positionNode] - The vertex position in local space. * @return {Node<vec3>} The transformed vertex position. */ getSkinnedPosition(boneMatrices = this.boneMatricesNode, position = this.positionNode) { const { skinIndexNode, skinWeightNode, bindMatrixNode, bindMatrixInverseNode } = this; const boneMatX = boneMatrices.element(skinIndexNode.x); const boneMatY = boneMatrices.element(skinIndexNode.y); const boneMatZ = boneMatrices.element(skinIndexNode.z); const boneMatW = boneMatrices.element(skinIndexNode.w); // POSITION const skinVertex = bindMatrixNode.mul(position); const skinned = (0, _OperatorNode.add)(boneMatX.mul(skinWeightNode.x).mul(skinVertex), boneMatY.mul(skinWeightNode.y).mul(skinVertex), boneMatZ.mul(skinWeightNode.z).mul(skinVertex), boneMatW.mul(skinWeightNode.w).mul(skinVertex)); return bindMatrixInverseNode.mul(skinned).xyz; } /** * Transforms the given vertex normal via skinning. * * @param {Node} [boneMatrices=this.boneMatricesNode] - The bone matrices * @param {Node<vec3>} [normal=normalLocal] - The vertex normal in local space. * @return {Node<vec3>} The transformed vertex normal. */ getSkinnedNormal(boneMatrices = this.boneMatricesNode, normal = _Normal.normalLocal) { const { skinIndexNode, skinWeightNode, bindMatrixNode, bindMatrixInverseNode } = this; const boneMatX = boneMatrices.element(skinIndexNode.x); const boneMatY = boneMatrices.element(skinIndexNode.y); const boneMatZ = boneMatrices.element(skinIndexNode.z); const boneMatW = boneMatrices.element(skinIndexNode.w); // NORMAL let skinMatrix = (0, _OperatorNode.add)(skinWeightNode.x.mul(boneMatX), skinWeightNode.y.mul(boneMatY), skinWeightNode.z.mul(boneMatZ), skinWeightNode.w.mul(boneMatW)); skinMatrix = bindMatrixInverseNode.mul(skinMatrix).mul(bindMatrixNode); return skinMatrix.transformDirection(normal).xyz; } /** * Transforms the given vertex normal via skinning. * * @param {NodeBuilder} builder - The current node builder. * @return {Node<vec3>} The skinned position from the previous frame. */ getPreviousSkinnedPosition(builder) { const skinnedMesh = builder.object; if (this.previousBoneMatricesNode === null) { skinnedMesh.skeleton.previousBoneMatrices = new Float32Array(skinnedMesh.skeleton.boneMatrices); this.previousBoneMatricesNode = (0, _ReferenceNode.referenceBuffer)('skeleton.previousBoneMatrices', 'mat4', skinnedMesh.skeleton.bones.length); } return this.getSkinnedPosition(this.previousBoneMatricesNode, _Position.positionPrevious); } /** * Returns `true` if bone matrices from the previous frame are required. * * @param {NodeBuilder} builder - The current node builder. * @return {boolean} Whether bone matrices from the previous frame are required or not. */ needsPreviousBoneMatrices(builder) { const mrt = builder.renderer.getMRT(); return mrt && mrt.has('velocity') || (0, _NodeUtils.getDataFromObject)(builder.object).useVelocity === true; } /** * Setups the skinning node by assigning the transformed vertex data to predefined node variables. * * @param {NodeBuilder} builder - The current node builder. * @return {Node<vec3>} The transformed vertex position. */ setup(builder) { if (this.needsPreviousBoneMatrices(builder)) { _Position.positionPrevious.assign(this.getPreviousSkinnedPosition(builder)); } const skinPosition = this.getSkinnedPosition(); if (this.toPositionNode) this.toPositionNode.assign(skinPosition); // if (builder.hasGeometryAttribute('normal')) { const skinNormal = this.getSkinnedNormal(); _Normal.normalLocal.assign(skinNormal); if (builder.hasGeometryAttribute('tangent')) { _Tangent.tangentLocal.assign(skinNormal); } } return skinPosition; } /** * Generates the code snippet of the skinning node. * * @param {NodeBuilder} builder - The current node builder. * @param {string} output - The current output. * @return {string} The generated code snippet. */ generate(builder, output) { if (output !== 'void') { return super.generate(builder, output); } } /** * Updates the state of the skinned mesh by updating the skeleton once per frame. * * @param {NodeFrame} frame - The current node frame. */ update(frame) { const skeleton = frame.object && frame.object.skeleton ? frame.object.skeleton : this.skinnedMesh.skeleton; if (_frameId.get(skeleton) === frame.frameId) return; _frameId.set(skeleton, frame.frameId); if (this.previousBoneMatricesNode !== null) skeleton.previousBoneMatrices.set(skeleton.boneMatrices); skeleton.update(); } } var _default = exports.default = SkinningNode; /** * TSL function for creating a skinning node. * * @tsl * @function * @param {SkinnedMesh} skinnedMesh - The skinned mesh. * @returns {SkinningNode} */ const skinning = skinnedMesh => (0, _TSLBase.nodeObject)(new SkinningNode(skinnedMesh)); /** * TSL function for computing skinning. * * @tsl * @function * @param {SkinnedMesh} skinnedMesh - The skinned mesh. * @param {Node<vec3>} [toPosition=null] - The target position. * @returns {SkinningNode} */ exports.skinning = skinning; const computeSkinning = (skinnedMesh, toPosition = null) => { const node = new SkinningNode(skinnedMesh); node.positionNode = (0, _StorageBufferNode.storage)(new _InstancedBufferAttribute.InstancedBufferAttribute(skinnedMesh.geometry.getAttribute('position').array, 3), 'vec3').setPBO(true).toReadOnly().element(_IndexNode.instanceIndex).toVar(); node.skinIndexNode = (0, _StorageBufferNode.storage)(new _InstancedBufferAttribute.InstancedBufferAttribute(new Uint32Array(skinnedMesh.geometry.getAttribute('skinIndex').array), 4), 'uvec4').setPBO(true).toReadOnly().element(_IndexNode.instanceIndex).toVar(); node.skinWeightNode = (0, _StorageBufferNode.storage)(new _InstancedBufferAttribute.InstancedBufferAttribute(skinnedMesh.geometry.getAttribute('skinWeight').array, 4), 'vec4').setPBO(true).toReadOnly().element(_IndexNode.instanceIndex).toVar(); node.bindMatrixNode = (0, _UniformNode.uniform)(skinnedMesh.bindMatrix, 'mat4'); node.bindMatrixInverseNode = (0, _UniformNode.uniform)(skinnedMesh.bindMatrixInverse, 'mat4'); node.boneMatricesNode = (0, _BufferNode.buffer)(skinnedMesh.skeleton.boneMatrices, 'mat4', skinnedMesh.skeleton.bones.length); node.toPositionNode = toPosition; return (0, _TSLBase.nodeObject)(node); }; exports.computeSkinning = computeSkinning;