cione-comp-lib
Version:
`$ yarn add cione-comp-lib` 或者 `$ npm i cione-comp-lib -S`
367 lines (366 loc) • 10.9 kB
JavaScript
import Base from "./core/Base.mjs";
import Vector3 from "./math/Vector3.mjs";
import Quaternion from "./math/Quaternion.mjs";
import Matrix4 from "./math/Matrix4.mjs";
import mat4 from "./glmatrix/mat4.mjs";
import BoundingBox from "./math/BoundingBox.mjs";
var nameId = 0;
var Node = Base.extend(
{
name: "",
position: null,
rotation: null,
scale: null,
worldTransform: null,
localTransform: null,
autoUpdateLocalTransform: true,
_parent: null,
_scene: null,
_needsUpdateWorldTransform: true,
_inIterating: false,
__depth: 0
},
function() {
if (!this.name) {
this.name = (this.type || "NODE") + "_" + nameId++;
}
if (!this.position) {
this.position = new Vector3();
}
if (!this.rotation) {
this.rotation = new Quaternion();
}
if (!this.scale) {
this.scale = new Vector3(1, 1, 1);
}
this.worldTransform = new Matrix4();
this.localTransform = new Matrix4();
this._children = [];
},
{
target: null,
invisible: false,
isSkinnedMesh: function() {
return false;
},
isRenderable: function() {
return false;
},
setName: function(name) {
var scene = this._scene;
if (scene) {
var nodeRepository = scene._nodeRepository;
delete nodeRepository[this.name];
nodeRepository[name] = this;
}
this.name = name;
},
add: function(node) {
var originalParent = node._parent;
if (originalParent === this) {
return;
}
if (originalParent) {
originalParent.remove(node);
}
node._parent = this;
this._children.push(node);
var scene = this._scene;
if (scene && scene !== node.scene) {
node.traverse(this._addSelfToScene, this);
}
node._needsUpdateWorldTransform = true;
},
remove: function(node) {
var children = this._children;
var idx = children.indexOf(node);
if (idx < 0) {
return;
}
children.splice(idx, 1);
node._parent = null;
if (this._scene) {
node.traverse(this._removeSelfFromScene, this);
}
},
removeAll: function() {
var children = this._children;
for (var idx = 0; idx < children.length; idx++) {
children[idx]._parent = null;
if (this._scene) {
children[idx].traverse(this._removeSelfFromScene, this);
}
}
this._children = [];
},
getScene: function() {
return this._scene;
},
getParent: function() {
return this._parent;
},
_removeSelfFromScene: function(descendant) {
descendant._scene.removeFromScene(descendant);
descendant._scene = null;
},
_addSelfToScene: function(descendant) {
this._scene.addToScene(descendant);
descendant._scene = this._scene;
},
isAncestor: function(node) {
var parent = node._parent;
while (parent) {
if (parent === this) {
return true;
}
parent = parent._parent;
}
return false;
},
children: function() {
return this._children.slice();
},
childAt: function(idx) {
return this._children[idx];
},
getChildByName: function(name) {
var children = this._children;
for (var i = 0; i < children.length; i++) {
if (children[i].name === name) {
return children[i];
}
}
},
getDescendantByName: function(name) {
var children = this._children;
for (var i = 0; i < children.length; i++) {
var child = children[i];
if (child.name === name) {
return child;
} else {
var res = child.getDescendantByName(name);
if (res) {
return res;
}
}
}
},
queryNode: function(path) {
if (!path) {
return;
}
var pathArr = path.split("/");
var current = this;
for (var i = 0; i < pathArr.length; i++) {
var name = pathArr[i];
if (!name) {
continue;
}
var found = false;
var children = current._children;
for (var j = 0; j < children.length; j++) {
var child = children[j];
if (child.name === name) {
current = child;
found = true;
break;
}
}
if (!found) {
return;
}
}
return current;
},
getPath: function(rootNode) {
if (!this._parent) {
return "/";
}
var current = this._parent;
var path = this.name;
while (current._parent) {
path = current.name + "/" + path;
if (current._parent == rootNode) {
break;
}
current = current._parent;
}
if (!current._parent && rootNode) {
return null;
}
return path;
},
traverse: function(callback, context) {
callback.call(context, this);
var _children = this._children;
for (var i = 0, len = _children.length; i < len; i++) {
_children[i].traverse(callback, context);
}
},
eachChild: function(callback, context) {
var _children = this._children;
for (var i = 0, len = _children.length; i < len; i++) {
var child = _children[i];
callback.call(context, child, i);
}
},
setLocalTransform: function(matrix) {
mat4.copy(this.localTransform.array, matrix.array);
this.decomposeLocalTransform();
},
decomposeLocalTransform: function(keepScale) {
var scale = !keepScale ? this.scale : null;
this.localTransform.decomposeMatrix(scale, this.rotation, this.position);
},
setWorldTransform: function(matrix) {
mat4.copy(this.worldTransform.array, matrix.array);
this.decomposeWorldTransform();
},
decomposeWorldTransform: function() {
var tmp = mat4.create();
return function(keepScale) {
var localTransform = this.localTransform;
var worldTransform = this.worldTransform;
if (this._parent) {
mat4.invert(tmp, this._parent.worldTransform.array);
mat4.multiply(localTransform.array, tmp, worldTransform.array);
} else {
mat4.copy(localTransform.array, worldTransform.array);
}
var scale = !keepScale ? this.scale : null;
localTransform.decomposeMatrix(scale, this.rotation, this.position);
};
}(),
transformNeedsUpdate: function() {
return this.position._dirty || this.rotation._dirty || this.scale._dirty;
},
updateLocalTransform: function() {
var position = this.position;
var rotation = this.rotation;
var scale = this.scale;
if (this.transformNeedsUpdate()) {
var m = this.localTransform.array;
mat4.fromRotationTranslation(m, rotation.array, position.array);
mat4.scale(m, m, scale.array);
rotation._dirty = false;
scale._dirty = false;
position._dirty = false;
this._needsUpdateWorldTransform = true;
}
},
_updateWorldTransformTopDown: function() {
var localTransform = this.localTransform.array;
var worldTransform = this.worldTransform.array;
if (this._parent) {
mat4.multiplyAffine(
worldTransform,
this._parent.worldTransform.array,
localTransform
);
} else {
mat4.copy(worldTransform, localTransform);
}
},
updateWorldTransform: function() {
var rootNodeIsDirty = this;
while (rootNodeIsDirty && rootNodeIsDirty.getParent() && rootNodeIsDirty.getParent().transformNeedsUpdate()) {
rootNodeIsDirty = rootNodeIsDirty.getParent();
}
rootNodeIsDirty.update();
},
update: function(forceUpdateWorld) {
if (this.autoUpdateLocalTransform) {
this.updateLocalTransform();
} else {
forceUpdateWorld = true;
}
if (forceUpdateWorld || this._needsUpdateWorldTransform) {
this._updateWorldTransformTopDown();
forceUpdateWorld = true;
this._needsUpdateWorldTransform = false;
}
var children = this._children;
for (var i = 0, len = children.length; i < len; i++) {
children[i].update(forceUpdateWorld);
}
},
getBoundingBox: function() {
function defaultFilter(el) {
return !el.invisible && el.geometry;
}
var tmpBBox = new BoundingBox();
var tmpMat4 = new Matrix4();
var invWorldTransform = new Matrix4();
return function(filter, out) {
out = out || new BoundingBox();
if (this._parent) {
Matrix4.invert(invWorldTransform, this._parent.worldTransform);
} else {
Matrix4.identity(invWorldTransform);
}
this.traverse(function(mesh) {
if (mesh.geometry && mesh.geometry.boundingBox) {
tmpBBox.copy(mesh.geometry.boundingBox);
Matrix4.multiply(tmpMat4, invWorldTransform, mesh.worldTransform);
tmpBBox.applyTransform(tmpMat4);
out.union(tmpBBox);
}
}, this, defaultFilter);
return out;
};
}(),
getWorldPosition: function(out) {
if (this.transformNeedsUpdate()) {
this.updateWorldTransform();
}
var m = this.worldTransform.array;
if (out) {
var arr = out.array;
arr[0] = m[12];
arr[1] = m[13];
arr[2] = m[14];
return out;
} else {
return new Vector3(m[12], m[13], m[14]);
}
},
clone: function() {
var node = new this.constructor();
var children = this._children;
node.setName(this.name);
node.position.copy(this.position);
node.rotation.copy(this.rotation);
node.scale.copy(this.scale);
for (var i = 0; i < children.length; i++) {
node.add(children[i].clone());
}
return node;
},
rotateAround: function() {
var v = new Vector3();
var RTMatrix = new Matrix4();
return function(point, axis, angle) {
v.copy(this.position).subtract(point);
var localTransform = this.localTransform;
localTransform.identity();
localTransform.translate(point);
localTransform.rotate(angle, axis);
RTMatrix.fromRotationTranslation(this.rotation, v);
localTransform.multiply(RTMatrix);
localTransform.scale(this.scale);
this.decomposeLocalTransform();
this._needsUpdateWorldTransform = true;
};
}(),
lookAt: function() {
var m = new Matrix4();
return function(target, up) {
m.lookAt(this.position, target, up || this.localTransform.y).invert();
this.setLocalTransform(m);
this.target = target;
};
}()
}
);
var Node3D = Node;
export { Node3D as default };