import { EventEmitter } from '@feng3d/event'; import { Node, NodeEventMap } from '../../core/Node'; import { Matrix4 } from '../../thirdparty/three/exports'; import { Group, Mesh, Vector3 } from '../../thirdparty/three/imports'; import { Mesh3D } from '../objects/Mesh3D'; export interface Node3DEventMap extends NodeEventMap { } export interface Node3D { /** * 父对象 */ get parent(): Node3D; /** * 子对象列表 */ get children(): Node3D[]; set children(v: Node3D[]); /** * 获取指定位置的子对象 * * @param index 子对象位置。 */ getChildAt(index: number): Node3D; /** * 根据名称查找对象 * * @param name 对象名称 */ find(name: string): Node3D; /** * 添加子对象 * * @param child 子对象 * * @returns 返回自身。 */ addChild(child: Node3D): this; } /** * 3D节点,包装`three`中`Group`作为3D节点。 */ export class Node3D extends Node { declare emitter: EventEmitter; declare __class__: 'Node3D'; declare protected _parent: Node3D; declare protected _children: Node3D[]; /** * @private */ _group: Group; get position() { return this._group.position; } set position(v) { this._group.position.set(v.x, v.y, v.z); } get rotation() { return this._group.rotation; } set rotation(v) { this._group.rotation.set(v.x, v.y, v.z, v.order); } get scale() { return this._group.scale; } set scale(v) { this._group.scale.set(v.x, v.y, v.z); } get quaternion() { return this._group.quaternion; } get up() { return this._group.up; } get visible() { return this._group.visible; } set visible(v) { this._group.visible = v; } /** * 是否拥有`Camera3D`组件。 * * 为了解决`three.js`中摄像机、灯光独特`lookAt`运算。 * @see https://github.com/mrdoob/three.js/blob/r149/src/core/Object3D.js#L284 */ get isCamera() { return !!this._group['isCamera']; } set isCamera(v) { this._group['isCamera'] = v; } get isPerspectiveCamera() { return !!this._group['isPerspectiveCamera']; } set isPerspectiveCamera(v) { this._group['isPerspectiveCamera'] = v; } get isOrthographicCamera() { return !!this._group['isOrthographicCamera']; } set isOrthographicCamera(v) { this._group['isOrthographicCamera'] = v; } /** * 是否拥有`Mesh3D`组件。 */ get isMesh() { return !!this._group['isMesh']; } set isMesh(v) { this._group['isMesh'] = v; } get isInstancedMesh() { return !!this._group['isInstancedMesh']; } set isInstancedMesh(v) { this._group['isInstancedMesh'] = v; } get isBrush() { return !!this._group['isBrush']; } set isBrush(v) { this._group['isBrush'] = v; } get isPoints() { return !!this._group['isPoints']; } set isPoints(v) { this._group['isPoints'] = v; } get isSprite() { return !!this._group['isSprite']; } set isSprite(v) { this._group['isSprite'] = v; } get isLine() { return !!this._group['isLine']; } set isLine(v) { this._group['isLine'] = v; } get isLineSegments() { return !!this._group['isLineSegments']; } set isLineSegments(v) { this._group['isLineSegments'] = v; } /** * 是否拥有`Light3D`组件。 */ get isLight() { return !!this._group['isLight']; } set isLight(v) { this._group['isLight'] = v; } get matrix() { return this._group.matrix; } set matrix(v) { this._group.matrix = v; } get matrixWorld() { return this._group.matrixWorld; } constructor() { super(); this._group = new Group(); this.emitter.on('added', this._onAdded, this); this.emitter.on('removed', this._onRemoved, this); } getObjectByName(name: string) { const object = this._group.getObjectByName(name); return object; } destroy() { super.destroy(); this._group = null; } lookAt(x: number, y: number, z: number): void; lookAt(vector: Vector3): void; lookAt(...args: any) { if (typeof args[0] === 'number') { this._group.lookAt(args[0], args[1], args[2]); } else { this._group.lookAt(args[0]); } } translateX(distance: number) { this._group.translateX(distance); return this; } translateY(distance: number) { this._group.translateY(distance); return this; } translateZ(distance: number) { this._group.translateZ(distance); return this; } updateMatrix() { this._group.updateMatrix(); } updateMatrixWorld(force?: boolean) { this._group.updateMatrixWorld(force); } applyMatrix4(matrix: Matrix4) { this._group.applyMatrix4(matrix); } private _onAdded() { this.parent._group.add(this._group); } private _onRemoved() { this._group.parent.remove(this._group); } static get(group: Group) { let node3D = this._map.get(group); if (!node3D) { node3D = new Node3D(); node3D._group = group; this._map.set(group, node3D); // group.children.forEach((child) => { if (child.constructor === Mesh) { const mesh3D: Mesh3D = Mesh3D.get(child); node3D.addChild(mesh3D.entity); } }); } return node3D; } private static _map = new Map(); }