UNPKG

playcanvas

Version:

PlayCanvas WebGL game engine

245 lines (242 loc) 7.97 kB
import { AnimTarget } from '../../anim/evaluator/anim-target.js'; import { DefaultAnimBinder } from '../../anim/binder/default-anim-binder.js'; import { AnimBinder } from '../../anim/binder/anim-binder.js'; import { Color } from '../../../core/math/color.js'; import { Quat } from '../../../core/math/quat.js'; import { Vec2 } from '../../../core/math/vec2.js'; import { Vec3 } from '../../../core/math/vec3.js'; import { Vec4 } from '../../../core/math/vec4.js'; const v2 = new Vec2(); const v3 = new Vec3(); const v4 = new Vec4(); const c = new Color(); const q = new Quat(); class AnimComponentBinder extends DefaultAnimBinder { constructor(animComponent, graph, layerName, mask, layerIndex){ super(graph); this.animComponent = animComponent; this._mask = mask; this.layerName = layerName; this.layerIndex = layerIndex; } static _packFloat(values) { return values[0]; } static _packBoolean(values) { return !!values[0]; } static _packVec2(values) { v2.x = values[0]; v2.y = values[1]; return v2; } static _packVec3(values) { v3.x = values[0]; v3.y = values[1]; v3.z = values[2]; return v3; } static _packVec4(values) { v4.x = values[0]; v4.y = values[1]; v4.z = values[2]; v4.w = values[3]; return v4; } static _packColor(values) { c.r = values[0]; c.g = values[1]; c.b = values[2]; c.a = values[3]; return c; } static _packQuat(values) { q.x = values[0]; q.y = values[1]; q.z = values[2]; q.w = values[3]; return q; } resolve(path) { const encodedPath = AnimBinder.encode(path.entityPath, path.component, path.propertyPath); let target = this.targetCache[encodedPath]; if (target) return target; let entity; let propertyComponent; let targetPath; switch(path.component){ case 'entity': entity = this._getEntityFromHierarchy(path.entityPath); targetPath = AnimBinder.encode(entity.path, 'entity', path.propertyPath); propertyComponent = entity; break; case 'graph': propertyComponent = this.findNode(path); if (!propertyComponent) return null; targetPath = AnimBinder.encode(propertyComponent.path, 'graph', path.propertyPath); break; default: entity = this._getEntityFromHierarchy(path.entityPath); propertyComponent = entity.findComponent(path.component); if (!propertyComponent) { return null; } targetPath = AnimBinder.encode(entity.path, path.component, path.propertyPath); break; } target = this._createAnimTargetForProperty(propertyComponent, path.propertyPath, targetPath); this.targetCache[encodedPath] = target; return target; } update(deltaTime) { const activeNodes = this.activeNodes; if (activeNodes) { for(let i = 0; i < activeNodes.length; i++){ activeNodes[i]._dirtifyLocal(); } } } _getEntityFromHierarchy(entityHierarchy) { if (!this.animComponent.entity.name === entityHierarchy[0]) { return null; } const currEntity = this.animComponent.entity; if (entityHierarchy.length === 1) { return currEntity; } return currEntity._parent.findByPath(entityHierarchy); } _resolvePath(object, path, resolveLeaf) { const steps = path.length - (resolveLeaf ? 0 : 1); for(let i = 0; i < steps; i++){ object = object[path[i]]; } return object; } _setter(object, path, packFunc) { const obj = this._resolvePath(object, path); const key = path[path.length - 1]; const setterFuncName = `set${key.substring(0, 1).toUpperCase()}${key.substring(1)}`; if (obj[setterFuncName]) { const getterFunc = obj[`get${key.substring(0, 1).toUpperCase()}${key.substring(1)}`].bind(obj); let baseValues = getterFunc(); baseValues = [ baseValues.x, baseValues.y, baseValues.z, baseValues.w ]; const setterFunc = obj[setterFuncName].bind(obj); return { set: (values)=>{ setterFunc(packFunc(values)); }, get: ()=>baseValues }; } const prop = obj[key]; if (typeof prop === 'object' && prop.hasOwnProperty('copy')) { return function(values) { prop.copy(packFunc(values)); }; } if ([ Vec2, Vec3, Vec4, Color, Quat ].indexOf(obj.constructor) !== -1 && path.length > 1) { const parent = path.length > 2 ? this._resolvePath(object, path.slice(0, -1)) : object; const objKey = path[path.length - 2]; return function(values) { obj[key] = packFunc(values); parent[objKey] = obj; }; } return function(values) { obj[key] = packFunc(values); }; } _createAnimTargetForProperty(propertyComponent, propertyHierarchy, targetPath) { if (this.handlers && propertyHierarchy[0].startsWith('weight.')) { return this.handlers.weight(propertyComponent, propertyHierarchy[0].replace('weight.', '')); } else if (this.handlers && propertyHierarchy[0] === 'material' && propertyHierarchy.length === 2) { const materialPropertyName = propertyHierarchy[1]; if (materialPropertyName.endsWith('Map')) { return this.handlers.materialTexture(propertyComponent, materialPropertyName); } } const property = this._resolvePath(propertyComponent, propertyHierarchy, true); if (typeof property === 'undefined') { return null; } let setter; let animDataType; let animDataComponents; if (typeof property === 'number') { setter = this._setter(propertyComponent, propertyHierarchy, AnimComponentBinder._packFloat); animDataType = 'vector'; animDataComponents = 1; } else if (typeof property === 'boolean') { setter = this._setter(propertyComponent, propertyHierarchy, AnimComponentBinder._packBoolean); animDataType = 'vector'; animDataComponents = 1; } else if (typeof property === 'object') { switch(property.constructor){ case Vec2: setter = this._setter(propertyComponent, propertyHierarchy, AnimComponentBinder._packVec2); animDataType = 'vector'; animDataComponents = 2; break; case Vec3: setter = this._setter(propertyComponent, propertyHierarchy, AnimComponentBinder._packVec3); animDataType = 'vector'; animDataComponents = 3; break; case Vec4: setter = this._setter(propertyComponent, propertyHierarchy, AnimComponentBinder._packVec4); animDataType = 'vector'; animDataComponents = 4; break; case Color: setter = this._setter(propertyComponent, propertyHierarchy, AnimComponentBinder._packColor); animDataType = 'vector'; animDataComponents = 4; break; case Quat: setter = this._setter(propertyComponent, propertyHierarchy, AnimComponentBinder._packQuat); animDataType = 'quaternion'; animDataComponents = 4; break; default: return null; } } if (propertyHierarchy.indexOf('material') !== -1) { return new AnimTarget((values)=>{ setter(values); propertyComponent.material.update(); }, animDataType, animDataComponents, targetPath); } return new AnimTarget(setter, animDataType, animDataComponents, targetPath); } rebind() { this.targetCache = {}; if (this.animComponent.rootBone) { this.graph = this.animComponent.rootBone; } else { this.graph = this.animComponent.entity; } const nodes = {}; const flatten = function(node) { nodes[node.name] = node; for(let i = 0; i < node.children.length; ++i){ flatten(node.children[i]); } }; flatten(this.graph); this.nodes = nodes; } } export { AnimComponentBinder };