@renderlayer/core
Version:
675 lines (669 loc) • 20.5 kB
JavaScript
import { generateUUID, Vector3, Euler, Quaternion, Matrix4, Matrix3, Ray } from '@renderlayer/math';
class EventDispatcher {
addEventListener(type, listener) {
if (this._listeners === void 0)
this._listeners = {};
const listeners = this._listeners;
if (listeners[type] === void 0) {
listeners[type] = [];
}
if (!listeners[type].includes(listener)) {
listeners[type].push(listener);
}
}
hasEventListener(type, listener) {
if (this._listeners === void 0)
return false;
const listeners = this._listeners;
return listeners[type] !== void 0 && listeners[type].includes(listener);
}
removeEventListener(type, listener) {
if (this._listeners === void 0)
return;
const listeners = this._listeners;
const listenerArray = listeners[type];
if (listenerArray !== void 0) {
const index = listenerArray.indexOf(listener);
if (index !== -1) {
listenerArray.splice(index, 1);
}
}
}
dispatchEvent(event) {
if (this._listeners === void 0)
return;
const listeners = this._listeners;
const listenerArray = listeners[event.type];
if (listenerArray !== void 0) {
event.target = this;
const array = listenerArray.slice(0);
for (let i = 0, l = array.length; i < l; i++) {
array[i].call(this, event);
}
event.target = null;
}
}
}
class Layers {
constructor() {
this.mask = 1 | 0;
}
set(channel) {
this.mask = (1 << channel | 0) >>> 0;
}
enable(channel) {
this.mask |= 1 << channel | 0;
}
enableAll() {
this.mask = 4294967295 | 0;
}
toggle(channel) {
this.mask ^= 1 << channel | 0;
}
disable(channel) {
this.mask &= ~(1 << channel | 0);
}
disableAll() {
this.mask = 0;
}
test(layers) {
return (this.mask & layers.mask) !== 0;
}
isEnabled(channel) {
return (this.mask & (1 << channel | 0)) !== 0;
}
}
let _object3DId = 0;
const _v1 = /* @__PURE__ */ new Vector3();
const _q1 = /* @__PURE__ */ new Quaternion();
const _m1 = /* @__PURE__ */ new Matrix4();
const _target = /* @__PURE__ */ new Vector3();
const _position = /* @__PURE__ */ new Vector3();
const _scale = /* @__PURE__ */ new Vector3();
const _quaternion = /* @__PURE__ */ new Quaternion();
const _xAxis = /* @__PURE__ */ new Vector3(1, 0, 0);
const _yAxis = /* @__PURE__ */ new Vector3(0, 1, 0);
const _zAxis = /* @__PURE__ */ new Vector3(0, 0, 1);
const _addedEvent = { type: "added" };
const _removedEvent = { type: "removed" };
class Object3D extends EventDispatcher {
constructor() {
super();
this.isObject3D = true;
Object.defineProperty(this, "id", { value: _object3DId++ });
this.uuid = generateUUID();
this.name = "";
this.type = "Object3D";
this.parent = null;
this.children = [];
this.up = Object3D.DEFAULT_UP.clone();
const position = new Vector3();
const rotation = new Euler();
const quaternion = new Quaternion();
const scale = new Vector3(1, 1, 1);
this.position = position;
this.rotation = rotation;
this.quaternion = quaternion;
this.scale = scale;
Object.defineProperties(this, {
position: {
writable: false
},
rotation: {
writable: false
},
quaternion: {
writable: false
},
scale: {
writable: false
},
modelViewMatrix: {
// enumerable: false,
// configurable: false,
// writable: false,
value: new Matrix4()
},
normalMatrix: {
// enumerable: false,
// configurable: false,
// writable: false,
value: new Matrix3()
}
});
function onRotationChange() {
quaternion.setFromEuler(rotation, false);
}
function onQuaternionChange() {
rotation.setFromQuaternion(quaternion, void 0, false);
}
rotation._onChange(onRotationChange);
quaternion._onChange(onQuaternionChange);
this.matrix = new Matrix4();
this.matrixWorld = new Matrix4();
this.matrixAutoUpdate = Object3D.DEFAULT_MATRIX_AUTO_UPDATE;
this.matrixWorldNeedsUpdate = false;
this.matrixWorldAutoUpdate = Object3D.DEFAULT_MATRIX_WORLD_AUTO_UPDATE;
this.layers = new Layers();
this.visible = true;
this.castShadow = false;
this.receiveShadow = false;
this.frustumCulled = true;
this.renderOrder = 0;
this.animations = [];
this.userData = {};
}
onBeforeRender() {
}
onAfterRender() {
}
applyMatrix4(matrix) {
if (this.matrixAutoUpdate)
this.updateMatrix();
this.matrix.premultiply(matrix);
this.matrix.decompose(this.position, this.quaternion, this.scale);
}
applyQuaternion(q) {
this.quaternion.premultiply(q);
return this;
}
setRotationFromAxisAngle(axis, angle) {
this.quaternion.setFromAxisAngle(axis, angle);
}
setRotationFromEuler(euler) {
this.quaternion.setFromEuler(euler, true);
}
setRotationFromMatrix(m) {
this.quaternion.setFromRotationMatrix(m);
}
setRotationFromQuaternion(q) {
this.quaternion.copy(q);
}
rotateOnAxis(axis, angle) {
_q1.setFromAxisAngle(axis, angle);
this.quaternion.multiply(_q1);
return this;
}
rotateOnWorldAxis(axis, angle) {
_q1.setFromAxisAngle(axis, angle);
this.quaternion.premultiply(_q1);
return this;
}
rotateX(angle) {
return this.rotateOnAxis(_xAxis, angle);
}
rotateY(angle) {
return this.rotateOnAxis(_yAxis, angle);
}
rotateZ(angle) {
return this.rotateOnAxis(_zAxis, angle);
}
translateOnAxis(axis, distance) {
_v1.copy(axis).applyQuaternion(this.quaternion);
this.position.add(_v1.multiplyScalar(distance));
return this;
}
translateX(distance) {
return this.translateOnAxis(_xAxis, distance);
}
translateY(distance) {
return this.translateOnAxis(_yAxis, distance);
}
translateZ(distance) {
return this.translateOnAxis(_zAxis, distance);
}
localToWorld(vector) {
this.updateWorldMatrix(true, false);
return vector.applyMatrix4(this.matrixWorld);
}
worldToLocal(vector) {
this.updateWorldMatrix(true, false);
return vector.applyMatrix4(_m1.copy(this.matrixWorld).invert());
}
lookAt(x, y, z) {
if (x.isVector3) {
_target.copy(x);
} else {
_target.set(x, y, z);
}
const parent = this.parent;
this.updateWorldMatrix(true, false);
_position.setFromMatrixPosition(this.matrixWorld);
if (this.isCamera || this.isLight) {
_m1.lookAt(_position, _target, this.up);
} else {
_m1.lookAt(_target, _position, this.up);
}
this.quaternion.setFromRotationMatrix(_m1);
if (parent) {
_m1.extractRotation(parent.matrixWorld);
_q1.setFromRotationMatrix(_m1);
this.quaternion.premultiply(_q1.invert());
}
}
add(object) {
if (arguments.length > 1) {
for (let i = 0; i < arguments.length; i++) {
this.add(arguments[i]);
}
return this;
}
if (object === this) {
console.error("Object3D.add: object can't be added as a child of itself.", object);
return this;
}
if (object && object.isObject3D) {
if (object.parent !== null) {
object.parent.remove(object);
}
object.parent = this;
this.children.push(object);
object.dispatchEvent(_addedEvent);
} else {
console.error("Object3D.add: object not an instance of Object3D.", object);
}
return this;
}
remove(object) {
if (arguments.length > 1) {
for (let i = 0; i < arguments.length; i++) {
this.remove(arguments[i]);
}
return this;
}
const index = this.children.indexOf(object);
if (index !== -1) {
object.parent = null;
this.children.splice(index, 1);
object.dispatchEvent(_removedEvent);
}
return this;
}
removeFromParent() {
const parent = this.parent;
if (parent !== null) {
parent.remove(this);
}
return this;
}
clear() {
return this.remove(...this.children);
}
attach(object) {
this.updateWorldMatrix(true, false);
_m1.copy(this.matrixWorld).invert();
if (object.parent !== null) {
object.parent.updateWorldMatrix(true, false);
_m1.multiply(object.parent.matrixWorld);
}
object.applyMatrix4(_m1);
this.add(object);
object.updateWorldMatrix(false, true);
return this;
}
getObjectById(id) {
return this.getObjectByProperty("id", id);
}
getObjectByName(name) {
return this.getObjectByProperty("name", name);
}
getObjectByProperty(name, value) {
if (this[name] === value)
return this;
for (let i = 0, l = this.children.length; i < l; i++) {
const child = this.children[i];
const object = child.getObjectByProperty(name, value);
if (object !== void 0) {
return object;
}
}
return void 0;
}
getObjectsByProperty(name, value) {
let result = [];
if (this[name] === value)
result.push(this);
for (let i = 0, l = this.children.length; i < l; i++) {
const childResult = this.children[i].getObjectsByProperty(name, value);
if (childResult.length > 0) {
result = result.concat(childResult);
}
}
return result;
}
getWorldPosition(target) {
this.updateWorldMatrix(true, false);
return target.setFromMatrixPosition(this.matrixWorld);
}
getWorldQuaternion(target) {
this.updateWorldMatrix(true, false);
this.matrixWorld.decompose(_position, target, _scale);
return target;
}
getWorldScale(target) {
this.updateWorldMatrix(true, false);
this.matrixWorld.decompose(_position, _quaternion, target);
return target;
}
getWorldDirection(target) {
this.updateWorldMatrix(true, false);
const e = this.matrixWorld.elements;
return target.set(e[8], e[9], e[10]).normalize();
}
raycast() {
}
traverse(callback) {
callback(this);
const children = this.children;
for (let i = 0, l = children.length; i < l; i++) {
children[i].traverse(callback);
}
}
traverseVisible(callback) {
if (this.visible === false)
return;
callback(this);
const children = this.children;
for (let i = 0, l = children.length; i < l; i++) {
children[i].traverseVisible(callback);
}
}
traverseAncestors(callback) {
const parent = this.parent;
if (parent !== null) {
callback(parent);
parent.traverseAncestors(callback);
}
}
updateMatrix() {
this.matrix.compose(this.position, this.quaternion, this.scale);
this.matrixWorldNeedsUpdate = true;
}
updateMatrixWorld(force) {
if (this.matrixAutoUpdate)
this.updateMatrix();
if (this.matrixWorldNeedsUpdate || force) {
if (this.parent === null) {
this.matrixWorld.copy(this.matrix);
} else {
this.matrixWorld.multiplyMatrices(this.parent.matrixWorld, this.matrix);
}
this.matrixWorldNeedsUpdate = false;
force = true;
}
const children = this.children;
for (let i = 0, l = children.length; i < l; i++) {
const child = children[i];
if (child.matrixWorldAutoUpdate === true || force === true) {
child.updateMatrixWorld(force);
}
}
}
updateWorldMatrix(updateParents, updateChildren) {
const parent = this.parent;
if (updateParents === true && parent !== null && parent.matrixWorldAutoUpdate === true) {
parent.updateWorldMatrix(true, false);
}
if (this.matrixAutoUpdate)
this.updateMatrix();
if (this.parent === null) {
this.matrixWorld.copy(this.matrix);
} else {
this.matrixWorld.multiplyMatrices(this.parent.matrixWorld, this.matrix);
}
if (updateChildren === true) {
const children = this.children;
for (let i = 0, l = children.length; i < l; i++) {
const child = children[i];
if (child.matrixWorldAutoUpdate === true) {
child.updateWorldMatrix(false, true);
}
}
}
}
toJSON(meta) {
const isRootObject = meta === void 0 || typeof meta === "string";
const output = {};
if (isRootObject) {
meta = {
geometries: {},
materials: {},
textures: {},
images: {},
shapes: {},
skeletons: {},
animations: {},
nodes: {}
};
output.metadata = {
version: 4.5,
type: "Object",
generator: "Object3D.toJSON"
};
}
const object = {};
object.uuid = this.uuid;
object.type = this.type;
if (this.name !== "")
object.name = this.name;
if (this.castShadow === true)
object.castShadow = true;
if (this.receiveShadow === true)
object.receiveShadow = true;
if (this.visible === false)
object.visible = false;
if (this.frustumCulled === false)
object.frustumCulled = false;
if (this.renderOrder !== 0)
object.renderOrder = this.renderOrder;
if (Object.keys(this.userData).length > 0)
object.userData = this.userData;
object.layers = this.layers.mask;
object.matrix = this.matrix.toArray();
object.up = this.up.toArray();
if (this.matrixAutoUpdate === false)
object.matrixAutoUpdate = false;
if (this.isInstancedMesh) {
object.type = "InstancedMesh";
object.count = this.count;
object.instanceMatrix = this.instanceMatrix.toJSON();
if (this.instanceColor !== null)
object.instanceColor = this.instanceColor.toJSON();
}
function serialize(library, element) {
if (library[element.uuid] === void 0) {
library[element.uuid] = element.toJSON(meta);
}
return element.uuid;
}
if (this.isScene) {
if (this.background) {
if (this.background.isColor) {
object.background = this.background.toJSON();
} else if (this.background.isTexture) {
object.background = this.background.toJSON(meta).uuid;
}
}
if (this.environment && this.environment.isTexture && this.environment.isRenderTargetTexture !== true) {
object.environment = this.environment.toJSON(meta).uuid;
}
} else if (this.isMesh || this.isLine || this.isPoints) {
object.geometry = serialize(meta.geometries, this.geometry);
const parameters = this.geometry.parameters;
if (parameters !== void 0 && parameters.shapes !== void 0) {
const shapes = parameters.shapes;
if (Array.isArray(shapes)) {
for (let i = 0, l = shapes.length; i < l; i++) {
const shape = shapes[i];
serialize(meta.shapes, shape);
}
} else {
serialize(meta.shapes, shapes);
}
}
}
if (this.isSkinnedMesh) {
object.bindMode = this.bindMode;
object.bindMatrix = this.bindMatrix.toArray();
if (this.skeleton !== void 0) {
serialize(meta.skeletons, this.skeleton);
object.skeleton = this.skeleton.uuid;
}
}
if (this.material !== void 0) {
if (Array.isArray(this.material)) {
const uuids = [];
for (let i = 0, l = this.material.length; i < l; i++) {
uuids.push(serialize(meta.materials, this.material[i]));
}
object.material = uuids;
} else {
object.material = serialize(meta.materials, this.material);
}
}
if (this.children.length > 0) {
object.children = [];
for (let i = 0; i < this.children.length; i++) {
object.children.push(this.children[i].toJSON(meta).object);
}
}
if (this.animations.length > 0) {
object.animations = [];
for (let i = 0; i < this.animations.length; i++) {
const animation = this.animations[i];
object.animations.push(serialize(meta.animations, animation));
}
}
if (isRootObject) {
const geometries = extractFromCache(meta.geometries);
const materials = extractFromCache(meta.materials);
const textures = extractFromCache(meta.textures);
const images = extractFromCache(meta.images);
const shapes = extractFromCache(meta.shapes);
const skeletons = extractFromCache(meta.skeletons);
const animations = extractFromCache(meta.animations);
const nodes = extractFromCache(meta.nodes);
if (geometries.length > 0)
output.geometries = geometries;
if (materials.length > 0)
output.materials = materials;
if (textures.length > 0)
output.textures = textures;
if (images.length > 0)
output.images = images;
if (shapes.length > 0)
output.shapes = shapes;
if (skeletons.length > 0)
output.skeletons = skeletons;
if (animations.length > 0)
output.animations = animations;
if (nodes.length > 0)
output.nodes = nodes;
}
output.object = object;
return output;
function extractFromCache(cache) {
const values = [];
for (const key in cache) {
const data = cache[key];
delete data.metadata;
values.push(data);
}
return values;
}
}
clone(recursive) {
return new this.constructor().copy(this, recursive);
}
copy(source, recursive = true) {
this.name = source.name;
this.up.copy(source.up);
this.position.copy(source.position);
this.rotation.order = source.rotation.order;
this.rotation.copy(source.rotation, false);
this.quaternion.copy(source.quaternion, false);
this.scale.copy(source.scale);
this.matrix.copy(source.matrix);
this.matrixWorld.copy(source.matrixWorld);
this.matrixAutoUpdate = source.matrixAutoUpdate;
this.matrixWorldNeedsUpdate = source.matrixWorldNeedsUpdate;
this.matrixWorldAutoUpdate = source.matrixWorldAutoUpdate;
this.layers.mask = source.layers.mask;
this.visible = source.visible;
this.castShadow = source.castShadow;
this.receiveShadow = source.receiveShadow;
this.frustumCulled = source.frustumCulled;
this.renderOrder = source.renderOrder;
this.animations = source.animations.slice();
this.userData = JSON.parse(JSON.stringify(source.userData));
if (recursive === true) {
for (let i = 0; i < source.children.length; i++) {
const child = source.children[i];
this.add(child.clone());
}
}
return this;
}
}
Object3D.DEFAULT_UP = /* @__PURE__ */ new Vector3(0, 1, 0);
Object3D.DEFAULT_MATRIX_AUTO_UPDATE = true;
Object3D.DEFAULT_MATRIX_WORLD_AUTO_UPDATE = true;
class Raycaster {
constructor(origin, direction, near = 0, far = Infinity) {
this.ray = new Ray(origin, direction);
this.near = near;
this.far = far;
this.camera = null;
this.layers = new Layers();
this.params = {
Mesh: {},
Line: { threshold: 1 },
LOD: {},
Points: { threshold: 1 },
Sprite: {}
};
}
set(origin, direction) {
this.ray.set(origin, direction);
}
setFromCamera(coords, camera) {
if (camera.isPerspectiveCamera) {
this.ray.origin.setFromMatrixPosition(camera.matrixWorld);
this.ray.direction.set(coords.x, coords.y, 0.5).unproject(camera).sub(this.ray.origin).normalize();
this.camera = camera;
} else if (camera.isOrthographicCamera) {
this.ray.origin.set(coords.x, coords.y, (camera.near + camera.far) / (camera.near - camera.far)).unproject(camera);
this.ray.direction.set(0, 0, -1).transformDirection(camera.matrixWorld);
this.camera = camera;
} else {
console.error("Raycaster: Unsupported camera type: " + camera.type);
}
}
intersectObject(object, recursive = true, intersects = []) {
intersectObject(object, this, intersects, recursive);
intersects.sort(ascSort);
return intersects;
}
intersectObjects(objects, recursive = true, intersects = []) {
for (let i = 0, l = objects.length; i < l; i++) {
intersectObject(objects[i], this, intersects, recursive);
}
intersects.sort(ascSort);
return intersects;
}
}
function ascSort(a, b) {
return a.distance - b.distance;
}
function intersectObject(object, raycaster, intersects, recursive) {
if (object.layers.test(raycaster.layers)) {
object.raycast(raycaster, intersects);
}
if (recursive === true) {
const children = object.children;
for (let i = 0, l = children.length; i < l; i++) {
intersectObject(children[i], raycaster, intersects, true);
}
}
}
export { EventDispatcher, Layers, Object3D, Raycaster };