playcanvas
Version:
PlayCanvas WebGL game engine
215 lines (212 loc) • 6.83 kB
JavaScript
import { AnimBinder } from './anim-binder.js';
import { AnimTarget } from '../evaluator/anim-target.js';
import { Entity } from '../../entity.js';
class DefaultAnimBinder {
constructor(graph){
this._isPathInMask = (path, checkMaskValue)=>{
const maskItem = this._mask[path];
if (!maskItem) return false;
else if (maskItem.children || checkMaskValue && maskItem.value !== false) return true;
return false;
};
this.graph = graph;
if (!graph) return;
this._mask = null;
const nodes = {};
const flatten = function(node) {
nodes[node.name] = node;
for(let i = 0; i < node.children.length; ++i){
flatten(node.children[i]);
}
};
flatten(graph);
this.nodes = nodes;
this.targetCache = {};
const findMeshInstances = function(node) {
let object = node;
while(object && !(object instanceof Entity)){
object = object.parent;
}
let meshInstances;
if (object) {
if (object.render) {
meshInstances = object.render.meshInstances;
} else if (object.model) {
meshInstances = object.model.meshInstances;
}
}
return meshInstances;
};
this.nodeCounts = {};
this.activeNodes = [];
this.handlers = {
'localPosition': function(node) {
const object = node.localPosition;
const func = function(value) {
object.set(...value);
};
return DefaultAnimBinder.createAnimTarget(func, 'vector', 3, node, 'localPosition');
},
'localRotation': function(node) {
const object = node.localRotation;
const func = function(value) {
object.set(...value);
};
return DefaultAnimBinder.createAnimTarget(func, 'quaternion', 4, node, 'localRotation');
},
'localScale': function(node) {
const object = node.localScale;
const func = function(value) {
object.set(...value);
};
return DefaultAnimBinder.createAnimTarget(func, 'vector', 3, node, 'localScale');
},
'weight': function(node, weightName) {
if (weightName.indexOf('name.') === 0) {
weightName = weightName.replace('name.', '');
} else {
weightName = Number(weightName);
}
const meshInstances = findMeshInstances(node);
const instances = [];
if (meshInstances) {
for(let i = 0; i < meshInstances.length; ++i){
if (meshInstances[i].node.name === node.name && meshInstances[i].morphInstance) {
instances.push(meshInstances[i].morphInstance);
}
}
}
if (instances.length > 0) {
const func = {
set: (value)=>{
for(let i = 0; i < instances.length; ++i){
instances[i].setWeight(weightName, value[0]);
}
},
get: ()=>{
return [
instances[0].getWeight(weightName)
];
}
};
return DefaultAnimBinder.createAnimTarget(func, 'number', 1, node, `weight.${weightName}`);
}
return null;
},
'materialTexture': (node, textureName)=>{
const meshInstances = findMeshInstances(node);
if (meshInstances) {
let meshInstance;
for(let i = 0; i < meshInstances.length; ++i){
if (meshInstances[i].node.name === node.name) {
meshInstance = meshInstances[i];
break;
}
}
if (meshInstance) {
const func = (value)=>{
const textureAsset = this.animComponent.system.app.assets.get(value[0]);
if (textureAsset && textureAsset.resource && textureAsset.type === 'texture') {
meshInstance.material[textureName] = textureAsset.resource;
meshInstance.material.update();
}
};
return DefaultAnimBinder.createAnimTarget(func, 'vector', 1, node, 'materialTexture', 'material');
}
}
return null;
}
};
}
_isPathActive(path) {
if (!this._mask) return true;
const rootNodeNames = [
path.entityPath[0],
this.graph.name
];
for(let j = 0; j < rootNodeNames.length; ++j){
let currEntityPath = rootNodeNames[j];
if (this._isPathInMask(currEntityPath, path.entityPath.length === 1)) return true;
for(let i = 1; i < path.entityPath.length; i++){
currEntityPath += `/${path.entityPath[i]}`;
if (this._isPathInMask(currEntityPath, i === path.entityPath.length - 1)) return true;
}
}
return false;
}
findNode(path) {
if (!this._isPathActive(path)) {
return null;
}
let node;
if (this.graph) {
node = this.graph.findByPath(path.entityPath);
if (!node) {
node = this.graph.findByPath(path.entityPath.slice(1));
}
}
if (!node) {
node = this.nodes[path.entityPath[path.entityPath.length - 1] || ''];
}
return node;
}
static createAnimTarget(func, type, valueCount, node, propertyPath, componentType) {
const targetPath = AnimBinder.encode(node.path, componentType ? componentType : 'entity', propertyPath);
return new AnimTarget(func, type, valueCount, targetPath);
}
resolve(path) {
const encodedPath = AnimBinder.encode(path.entityPath, path.component, path.propertyPath);
let target = this.targetCache[encodedPath];
if (target) return target;
const node = this.findNode(path);
if (!node) {
return null;
}
const handler = this.handlers[path.propertyPath];
if (!handler) {
return null;
}
target = handler(node);
if (!target) {
return null;
}
this.targetCache[encodedPath] = target;
if (!this.nodeCounts[node.path]) {
this.activeNodes.push(node);
this.nodeCounts[node.path] = 1;
} else {
this.nodeCounts[node.path]++;
}
return target;
}
unresolve(path) {
if (path.component !== 'graph') {
return;
}
const node = this.nodes[path.entityPath[path.entityPath.length - 1] || ''];
this.nodeCounts[node.path]--;
if (this.nodeCounts[node.path] === 0) {
const activeNodes = this.activeNodes;
const i = activeNodes.indexOf(node.node);
const len = activeNodes.length;
if (i < len - 1) {
activeNodes[i] = activeNodes[len - 1];
}
activeNodes.pop();
}
}
update(deltaTime) {
const activeNodes = this.activeNodes;
for(let i = 0; i < activeNodes.length; ++i){
activeNodes[i]._dirtifyLocal();
}
}
assignMask(mask) {
if (mask !== this._mask) {
this._mask = mask;
return true;
}
return false;
}
}
export { DefaultAnimBinder };