import { AbstractMesh, AnimationGroup, ArcRotateCamera, BoundingBoxGizmo, Color3, Color4, CubeTexture, DirectionalLight, Engine, EventState, HemisphericLight, HighlightLayer, IParticleSystem, Mesh, MeshBuilder, Observable, PickingInfo, PointerEventTypes, Quaternion, Scene, ShadowGenerator, Skeleton, StandardMaterial, Texture, TransformNode, Vector3, } from '@babylonjs/core'; import '@babylonjs/core/Debug/debugLayer'; import '@babylonjs/inspector'; import { GridMaterial } from '@babylonjs/materials'; import { ModelData, ModelType } from '../model/data/model-data/model-data'; import { ModelInfo } from '../model/info/model/model-info'; import { ModelInfo_building } from '../model/info/model/model-info-building'; import { MyArcRotateCameraPointersInput } from '../tool/myArcRotateCameraPointersInput'; import { BabylonTool } from '../tool/babylon-tool'; import { GizmoTool } from '../tool/gizmo-tool'; import { TsTool } from '../tool/ts-tool'; import { FacilityWindow } from '../view/facility-window/facility-window'; import { FacilityInfoInSceneWindow } from '../view/facilityinfoinscene-window/facilityinfoinscene-window'; import { InfoManager } from './info-manager'; import { ModeManager } from './mode-manager'; import { MarkWindow } from '../view/mark-window/mark-window'; import { ModelInfo_facility } from '../model/info/model/model-info-facility'; import { ModelInfo_mark } from '../model/info/mark/model-info-mark'; import { MarkData, MarkType } from '../model/data/mark/mark-data'; import { Event_KeyboardInput } from './event-manager/events/event-keyboard-input'; import { ModelInfo_mark_area } from '../model/info/mark/other/mark-plan-area-info'; import { classToClass, plainToClass } from 'class-transformer'; import { MarkData_Area } from '../model/data/mark/other/mark-data-area'; import { MarkData_Line } from '../model/data/mark/other/mark-data-line'; import { ModelInfo_mark_line } from '../model/info/mark/other/mark-plan-line-info'; import { MarkData_multiLine } from '../model/data/mark/other/mark-data-multi-line'; import { ModelInfo_mark_multiLine } from '../model/info/mark/other/mark-plan-multi-line-info'; import { MarkData_multiArrow_CT, MarkData_multiArrow_JG } from '../model/data/mark/other/mark-data-multi-arrow'; import { ModelInfo_mark_multiArrow } from '../model/info/mark/other/mark-plan-multi-arrow'; import { ModelInfo_mark_particle } from '../model/info/mark/other/mark-plan-particle-info'; import { FacilityPosType, ModelData_facility } from '../model/data/model-data/model-data-facility'; import { LoadTool } from '../tool/load-tool'; //场景管理器 export class SceneManager { //----------------Camera-----------------\\ public engine: Engine; public canvas: HTMLCanvasElement; public scene: Scene; public defaultCamera: ArcRotateCamera; private hemisphericLight: HemisphericLight; public shadowGenerator: ShadowGenerator;//阴影 public sunLight: DirectionalLight;//太阳光,用于产生阴影 public skyBox: Mesh;//天空球 public ground3D: Mesh;//无天空盒时的背景地面 static s_openLight: boolean = true;//开启光照效果(关闭是要同时关闭阴影) static s_openShadow: boolean = true;//开启阴影(必须开启阴影) static s_openSkyBox: boolean = true;//使用天空盒 static s_environmentCubeTexture: CubeTexture;//环境所用的cubeTexture static s_openEnvironmentReflection: boolean = true;//使用环境反射 static s_allModelInfo: ModelInfo[] = []; //所有建筑模型信息 static s_facilityInfoInSceneWindow: FacilityInfoInSceneWindow; //场景中设备 界面 static s_facilityWindow: FacilityWindow; //可用设备 界面 static s_markWindow: MarkWindow;//可用的标绘素材 界面 //#region 单例 private static instance: SceneManager; public static get Instance() { if (SceneManager.instance == null) { throw new Error('Method not implemented.'); } return SceneManager.instance; } //#endregion //#region 初始化 //初始化 public static init(scene: Scene, canvas: HTMLCanvasElement, engine: Engine): SceneManager { if (SceneManager.instance != null) { throw new Error("sceneManager can't be reinitialized"); } else { SceneManager.instance = new SceneManager(); SceneManager.instance.engine; SceneManager.instance.scene = scene; SceneManager.instance.canvas = canvas; scene.onBeforeRenderObservable.add(() => { if (SceneManager.instance != null) { SceneManager.instance.onUpdate(); } }); } return SceneManager.instance; } //#endregion onUpdate() { if (SceneManager.s_openSkyBox && this.skyBox != null) { let skyMat = this.skyBox.material; let skyTexture = ((skyMat as StandardMaterial).reflectionTexture as CubeTexture); if (skyTexture != null) { let addSpeed = SceneManager.instance.scene.deltaTime * 0.001 * 0.003;//天空盒旋转速度 if (addSpeed > 0) { skyTexture.rotationY += addSpeed; } while (skyTexture.rotationY > Math.PI * 2) { skyTexture.rotationY -= Math.PI * 2; } } } } dispose() { SceneManager.instance = null; } //#region 摄像机 /** * 摄像机模式-是正交状态 */ public cameraMode_Is2D = false; //初始化自由旋转相机 public initArcRotateCamera() { let camera = new ArcRotateCamera( 'Camera', //名称 0, //alpha: 定义相机沿垂直轴的旋转 1.2, //beta: 定义相机沿水平轴的旋转 10, //摄像机与目标位置的距离 Vector3.Zero(), //目标位置 this.scene //定义摄像机所属的场景 ); camera.minZ = 0;//最近拍摄距离 camera.maxZ = 6000; //摄像机拍摄的最远距离 // camera.upperBetaLimit = 1.5; //beta方向上的旋转限制(防止看到模型底面) camera.lowerRadiusLimit = 1; //相机距离拍摄目标的最小距离(防止穿插) camera.setTarget(Vector3.Zero()); //设置拍摄目标 camera.radius = 100; camera.attachControl(this.canvas, true); //把相机连接到画布 this.defaultCamera = camera; camera.inputs.removeByType("ArcRotateCameraPointersInput"); camera.inputs.removeByType("ArcRotateCameraKeyboardMoveInput"); //为了配合快捷键,屏蔽了 camera.inputs.add(new MyArcRotateCameraPointersInput()); } /** * 改变摄像机模式(正交还是透视) * @param is2D true表示正交 */ public changeCameraMode(is2D: boolean) { this.cameraMode_Is2D = is2D; BabylonTool.changeCameraMode(this.defaultCamera, is2D); } //#endregion //#region 光照与背景 //默认的半球光 public initLight() { this.hemisphericLight = new HemisphericLight( 'light1', new Vector3(0, 1, 0), this.scene ); this.updateLightSetting(); this.updateShadow(); } /** * 更新光照设置 */ updateLightSetting() { if (SceneManager.s_openLight) { if (this.sunLight == null) { this.sunLight = new DirectionalLight("sun", new Vector3(0.49, -0.79, 0.38), this.scene); } else { this.sunLight.setEnabled(true); } if (SceneManager.s_openEnvironmentReflection) { this.hemisphericLight.intensity = 0.1;//有环境反射时,本身会比较亮(跟当前使用的环境贴图有关) this.sunLight.intensity = 3; } else { this.hemisphericLight.intensity = 0.25; this.sunLight.intensity = 3; } } else { if (this.sunLight == null) { this.sunLight.setEnabled(false); } this.hemisphericLight.intensity = 10; } } /** * 初始化阴影 */ public updateShadow() { if (SceneManager.s_openShadow) { if (this.shadowGenerator == null) { this.shadowGenerator = new ShadowGenerator(2048, this.sunLight); } this.shadowGenerator.usePercentageCloserFiltering = true; this.shadowGenerator.filteringQuality = ShadowGenerator.QUALITY_MEDIUM; // this.shadowGenerator.blurKernel = 32; if (this.sunLight != null) { this.sunLight.autoCalcShadowZBounds = true; //投影矩阵距离自适应 } } else { if (this.shadowGenerator != null) { this.shadowGenerator.usePercentageCloserFiltering = false; } if (this.sunLight != null) { this.sunLight.autoCalcShadowZBounds = false; //投影矩阵距离自适应 } } } //#endregion //#region 高亮层 /** * 高亮层 */ highLightLayer: HighlightLayer; /** * 添加到高亮层 * @param mesh * @param color */ addToHighLight(mesh: Mesh, color: Color3) { if (this.highLightLayer == null) { this.highLightLayer = new HighlightLayer("highLight", this.scene, { mainTextureRatio: 2, blurVerticalSize: 1.5, blurHorizontalSize: 1.5, }); this.openInnerGlow(true); // this.highLightLayer.innerGlow = true; this.highLightLayer.outerGlow = true; } let allMesh = mesh.getChildMeshes(); allMesh.push(mesh); for (let i = 0; i < allMesh.length; i++) { let childMesh = allMesh[i]; if (childMesh instanceof Mesh) { this.highLightLayer.addMesh(childMesh, color); // childMesh.material.alphaMode = this.highLightNum++; } } } /** * 开启内发光 */ openInnerGlow(open: boolean) { if (this.highLightLayer != null) { this.highLightLayer.innerGlow = open; } } highLightNum = 0; /** * 移除出高亮层 * @param mesh */ removeFromHighLight(mesh: Mesh) { if (this.highLightLayer == null) { return; } let allMesh = mesh.getChildMeshes(); allMesh.push(mesh); for (let i = 0; i < allMesh.length; i++) { let childMesh = allMesh[i]; if (childMesh instanceof Mesh) { this.highLightLayer.removeMesh(childMesh); this.highLightNum--; } } } // /** // * 清空高亮层 // */ // clearHighLight() { // this.highLightLayer.removeAllMeshes(); // } //#endregion //#region 三维场景的背景 public updateSceneBG() { this.scene.clearColor = new Color4(0, 0, 0, 1);//0.51, 0.63, 0.89 this.scene.ambientColor = new Color3(1, 1, 1); if (SceneManager.s_openSkyBox) { if (this.skyBox == null) { this.skyBox = Mesh.CreateBox("skyBox", 4000.0, this.scene); let skyboxMaterial = new StandardMaterial("skyBox", this.scene); skyboxMaterial.backFaceCulling = false; skyboxMaterial.reflectionTexture = new CubeTexture("assets/skybox/default/default", this.scene); skyboxMaterial.reflectionTexture.coordinatesMode = Texture.SKYBOX_MODE; (skyboxMaterial.reflectionTexture as CubeTexture).rotationY = 0; skyboxMaterial.diffuseColor = new Color3(0, 0, 0); skyboxMaterial.specularColor = new Color3(0, 0, 0); skyboxMaterial.disableLighting = true; this.skyBox.material = skyboxMaterial; this.skyBox.renderingGroupId = -1; } else { this.skyBox.setEnabled(true); } if (this.ground3D != null) { this.ground3D.setEnabled(false); } } else { if (this.ground3D == null) { this.ground3D = MeshBuilder.CreateGround("ground", { width: 10000, height: 10000 }); this.ground3D.position.y = -1.5; let mat_grid = new GridMaterial("mat_ground", SceneManager.Instance.scene); mat_grid.gridRatio = 10; mat_grid.mainColor = new Color3(0, 0, 0); mat_grid.lineColor = new Color3(0, 0.4, 1); this.ground3D.material = mat_grid; this.ground3D.renderingGroupId = -1; } else { this.ground3D.setEnabled(true); } } //环境反射 if (SceneManager.s_openEnvironmentReflection) { this.scene.environmentTexture = new CubeTexture("assets/skybox/city/city.env", this.scene); SceneManager.s_environmentCubeTexture = this.scene.environmentTexture as CubeTexture; } } //#endregion //#region 场景声明周期与事件 public initSceneEvent() { this.scene.onBeforeRenderObservable.add(SceneManager.onBeforeRender); this.scene.onPointerDown = SceneManager.onPointerDown; this.scene.onPointerUp = SceneManager.onPointerUp; this.scene.onPointerMove = SceneManager.onPointerMove; document.onkeydown = SceneManager.onKeyDown; } static showDebug = false; //按键按下 static onKeyDown(this: GlobalEventHandlers, ev: KeyboardEvent) { if (ev.altKey && ev.key == 'd' && ModeManager.isDebug) { SceneManager.showDebug = !SceneManager.showDebug; if (SceneManager.showDebug) { SceneManager.Instance.scene.debugLayer.show({ embedMode: true }); } else { SceneManager.Instance.scene.debugLayer.hide(); } } Event_KeyboardInput.dispatch(ev); //派发给其他 } //场景渲染前 static onBeforeRender(eventData: Scene, eventState: EventState) { if (SceneManager.s_isPointerDown) { SceneManager.s_downTime += SceneManager.Instance.scene.deltaTime; } } static s_isPointerDrag: boolean = false; //拖拽中 static s_isPointerDown = false; //按下中 static s_downTime = 0;//按下的时间 static readonly c_dragTime = 200;//超过则表示拖拽 //焦点按下 private static onPointerDown( evt: PointerEvent, pickInfo: PickingInfo, type: PointerEventTypes ) { SceneManager.s_isPointerDown = true; SceneManager.s_downTime = 0; } //焦点移动 private static onPointerMove( evt: PointerEvent, pickInfo: PickingInfo, type: PointerEventTypes ) { if (SceneManager.s_isPointerDown && SceneManager.s_downTime > SceneManager.c_dragTime) { SceneManager.s_isPointerDrag = true; } } //焦点抬起 private static onPointerUp(evt: PointerEvent, pickInfo: PickingInfo) { SceneManager.s_isPointerDown = false; SceneManager.s_isPointerDrag = false; SceneManager.s_downTime = 0; } //#endregion //#region 操作模型 //创建模型 /** * 加载模型 * @param modelType * @param modelData * @param needBox * @param isNew * @param tag 加载的原因 * @param onSuccess * @param onError * @param index 重试次数,超过五次就停止 */ static createModel( modelType: ModelType, modelData: ModelData, needBox: boolean, isNew: boolean, tag: string, onSuccess?: ( meshes: AbstractMesh[], box: AbstractMesh, modelInfo: ModelInfo ) => void, onError?: ( message: string ) => void, index = 0 ) { // console.log("准备加载"); let defaultMesh = MeshBuilder.CreateBox(modelData.key + "Box", { size: 0.01 }); defaultMesh.scaling.y = 0.01; defaultMesh.visibility = 0; let modelInfo: ModelInfo; if (modelType == ModelType.Building) { modelInfo = InfoManager.newModelInfo_building( modelData.key, modelData, null, defaultMesh ); } else if (modelType == ModelType.Facility) { modelInfo = new ModelInfo_facility(modelData.key, modelData, null, defaultMesh, null, isNew); modelInfo.showFollowUI(false); } else if (modelType == ModelType.Mark) { switch ((modelData as MarkData).type) { case MarkType.QYSDA: case MarkType.QYSDB: modelData = plainToClass(MarkData_Area, modelData); modelInfo = new ModelInfo_mark_area(modelData as MarkData, null, defaultMesh, null, isNew); break; case MarkType.JJX: modelData = plainToClass(MarkData_Line, modelData); modelInfo = new ModelInfo_mark_line(modelData as MarkData, null, defaultMesh, null, isNew); break; case MarkType.JGLX: modelData = plainToClass(MarkData_multiArrow_JG, modelData); modelInfo = new ModelInfo_mark_multiArrow(modelData as MarkData, null, defaultMesh, null, isNew); break; case MarkType.CT: modelData = plainToClass(MarkData_multiArrow_CT, modelData); modelInfo = new ModelInfo_mark_multiArrow(modelData as MarkData, null, defaultMesh, null, isNew); break; case MarkType.H: case MarkType.SNH: case MarkType.YWA: case MarkType.YWB: modelInfo = new ModelInfo_mark_particle(modelData as MarkData, null, defaultMesh, null, isNew); break; default: modelInfo = new ModelInfo_mark(modelData as MarkData, null, defaultMesh, null, isNew); break; } modelInfo.showFollowUI(false); } if (modelData.isModel) { let importMeshSyncData = SceneManager.getImportMeshSyncData(modelData.resPath, modelData.resName); if (importMeshSyncData != null) //已经在加载了 { importMeshSyncData.onsuccessObservable.add((eventData: ImportMeshSyncCallBack) => { let box = SceneManager.importMeshSuccess(eventData.newMeshes, eventData.particleSystems, eventData.skeletons, eventData.animationGroups, eventData.modelInfo, eventData.needBox, eventData.modelData); onSuccess(eventData.newMeshes, box, eventData.modelInfo); }) } else { importMeshSyncData = SceneManager.startLoadMesh(modelData.resPath, modelData.resName); BabylonTool.importMeshSync( '', modelData.resPath, modelData.resName, SceneManager.Instance.scene, tag, function (newMeshes: AbstractMesh[], particleSystems: IParticleSystem[], skeletons: Skeleton[], animationGroups: AnimationGroup[]) { let callBack = new ImportMeshSyncCallBack(newMeshes, particleSystems, skeletons, animationGroups, modelInfo, needBox, modelData); let allImportMeshSyncDatas = SceneManager.getAllImportMeshSyncData(modelData.resPath, modelData.resName); for (let i = 0; i < allImportMeshSyncDatas.length; i++) { allImportMeshSyncDatas[i].onsuccessObservable.notifyObservers(callBack); } SceneManager.endLoadMesh(modelData.resPath, modelData.resName); let box = SceneManager.importMeshSuccess(newMeshes, particleSystems, skeletons, animationGroups, modelInfo, needBox, modelData); //console.log("加载模型完成", modelData.resName); onSuccess(newMeshes, box, modelInfo); }, null, function (scene: Scene, message: string, exception?: any) { if (index < 5) { let l_index = index + 1; modelInfo.dispose(); importMeshSyncData.isBreak = true;//中断 SceneManager.createModel(modelType, modelData, needBox, isNew, tag, onSuccess, onError, l_index); //console.log("重新开始加载" + l_index, modelData); console.log(exception); } else { modelInfo.dispose(); LoadTool.remove(modelData.resPath + modelData.resName); console.log(message, exception); alert("模型加载失败,请刷新页面重试: " + message + "==" + exception); // alert(exception); if (onError) { onError(message) } } } ); } } else { onSuccess([], defaultMesh, modelInfo); } } /** * 加载完成的回调 * @param newMeshes * @param particleSystems * @param skeletons * @param animationGroups */ static importMeshSuccess(newMeshes: AbstractMesh[], particleSystems: IParticleSystem[], skeletons: Skeleton[], animationGroups: AnimationGroup[], modelInfo: ModelInfo, needBox: boolean, modelData: ModelData) { SceneManager.addModel(modelInfo); let box: AbstractMesh = null; if (needBox) { box = BoundingBoxGizmo.MakeNotPickableAndWrapInBoundingBox( newMeshes[0] as Mesh ); box.isPickable = false; box.name = modelData.key; box.rotationQuaternion = undefined; modelInfo.models = newMeshes; modelInfo.modelBox = box; modelInfo.animationGroups = animationGroups; if (animationGroups != null && animationGroups.length > 0) { animationGroups[0].stop(); } let canPick = false; for (let i = 0; i < newMeshes.length; i++) { //if (newMeshes[i].name.match("Floor") || newMeshes[i].name.match("floor") || newMeshes[i].name.match("Terrain")) { newMeshes[i].isPickable = true; canPick = true; //} if (SceneManager.s_openShadow) { newMeshes[i].receiveShadows = true; // let mat = newMeshes[i].material; //if (mat instanceof PBRMaterial && TsTool.stringContain(mat.name, "glass")) { // mat.refractionTexture = SceneManager.s_environmentCubeTexture;//折射 // mat.reflectionTexture = SceneManager.s_prefabSkyBoxCubeTexture;//反射 // mat.invertRefractionY = true; //} } } if (!SceneManager.s_openLight) { BabylonTool.setMatToUnlit(newMeshes, true, "Color"); //无光材质 BabylonTool.setMatSheen(newMeshes, true, true); } // SceneManager.instance.shadowGenerator.addShadowCaster(newMeshes[0], true); //如果是建筑,内部还没找到可接受pick的特定mesh,则整体可接受pick if (modelInfo instanceof ModelInfo_building && !canPick) { //box.isPickable = true; let ground = MeshBuilder.CreateBox("ground", { size: 1 }); ground.scaling = new Vector3(box.scaling.x, 0.1, box.scaling.z); ground.setParent(box); ground.rotationQuaternion = Quaternion.Zero(); ground.position = new Vector3(0, -0.5, 0); ground.visibility = 0.01; } // if (modelInfo instanceof ModelInfo_building) { // for (let i = 0; i < newMeshes.length; i++) { // newMeshes[i].receiveShadows = true; // } // } } return box } /** * 正在加载的模型数据 */ static s_ImportMeshSyncData: ImportMeshSyncData[] = []; /** * 开始加载模型 */ static startLoadMesh(path: string, name: string) { let importMeshSyncData = new ImportMeshSyncData(path, name); SceneManager.s_ImportMeshSyncData.push(importMeshSyncData); return importMeshSyncData } /** * 结束加载模型 * @param path * @param name */ static endLoadMesh(path: string, name: string) { let datas = SceneManager.getAllImportMeshSyncData(path, name); for (let i = 0; i < datas.length; i++) { TsTool.arrayRemove(SceneManager.s_ImportMeshSyncData, datas[i]); } } /** * 查询加载模型的数据(确实在加载的) * @param path * @param name */ static getImportMeshSyncData(path: string, name: string) { let result: ImportMeshSyncData = null; for (let i = 0; i < SceneManager.s_ImportMeshSyncData.length; i++) { let data = SceneManager.s_ImportMeshSyncData[i]; if (data.path == path && data.name == name && data.isBreak == false) { result = data; break; } } return result; } /** * 获取所有的加载信息(失败时,会产生多份) * @param path * @param name */ static getAllImportMeshSyncData(path: string, name: string) { let result: ImportMeshSyncData[] = []; for (let i = 0; i < SceneManager.s_ImportMeshSyncData.length; i++) { let data = SceneManager.s_ImportMeshSyncData[i]; if (data.path == path && data.name == name) { result.push(data); } } return result; } //克隆模型 static cloneMesh( key, modelInfo: ModelInfo, prefab: Mesh, name?: string ): Mesh { let result = BabylonTool.cloneMesh(prefab); if (SceneManager.s_openShadow) { SceneManager.Instance.shadowGenerator.addShadowCaster(result); } SceneManager.addModel(modelInfo); return result; } //获取模型 static getModel(modelKey: string): ModelInfo { let result = null; if (SceneManager.s_allModelInfo != null) { for (let i = 0; i < SceneManager.s_allModelInfo.length; i++) { let l_modelInfo = SceneManager.s_allModelInfo[i]; if (l_modelInfo.key == modelKey) { return l_modelInfo; } } } return result; } //获取模型 static getModelById(uniqueId: number): ModelInfo { let result = null; if (SceneManager.s_allModelInfo != null) { for (let i = 0; i < SceneManager.s_allModelInfo.length; i++) { let l_modelInfo = SceneManager.s_allModelInfo[i]; if (l_modelInfo.modelBox.uniqueId == uniqueId) { return l_modelInfo; } } } console.log('没找到:' + uniqueId); return result; } //删除模型 static destroyModel(model: string | ModelInfo) { let modelInfo: ModelInfo; if (model instanceof ModelInfo) { modelInfo = model; } else { modelInfo = SceneManager.getModel(model); } if (modelInfo == null) { return; } else { if (modelInfo.modelData instanceof ModelData_facility && modelInfo.modelData.posType == FacilityPosType.In) { //来自建筑模型中的设备 modelInfo.dispose(false); } else { TsTool.arrayRemove(SceneManager.s_allModelInfo, modelInfo); modelInfo.dispose(); } } } //添加模型 private static addModel(modelInfo: ModelInfo) { SceneManager.s_allModelInfo.push(modelInfo); } /** * 聚焦当前选中的目标 */ public static lookAtCurrentSelect() { if (GizmoTool.s_nowPickAim_mesh != null) { BabylonTool.changeCameraTarget(SceneManager.Instance.defaultCamera, GizmoTool.s_nowPickAim_mesh, true); } else { ModeManager.log("没有选中的目标"); } } /** * 模型的Y方向吸附 * @param mover 行动者 * @param target 吸附的目标物 */ public static meshAdsorbY(mover: AbstractMesh, target: AbstractMesh, pickPos: Vector3) { ModeManager.log("吸附模型" + target); let targetRoot = SceneManager.getRootTransformNode(target); if (mover == targetRoot) { return; } let aimPos_y: number = 0; let direction = 0.5; if (mover.absolutePosition.y < pickPos.y) { direction = -0.5; } aimPos_y += mover.scaling.y * direction; let newPos = new Vector3(mover.absolutePosition.x, pickPos.y + aimPos_y, mover.absolutePosition.z) mover.setAbsolutePosition(newPos); } //获取根节点 public static getRootTransformNode(mesh: AbstractMesh): TransformNode { let result: TransformNode = mesh; let parent: any = mesh; while (true) { if (parent.id == "ground" || parent.id.match("Floor")) { result = parent; return result; } parent = parent.parent; if (parent == null) { return mesh; } else if (parent.id == "box") { result = parent; return result; } else { result = parent; } } } //#endregion } /** * 正在异步导入模型的数据 */ class ImportMeshSyncData { path: string; name: string; isBreak: boolean; //中断了 onsuccessObservable: Observable; constructor(path: string, name: string) { this.isBreak = false; this.path = path; this.name = name; this.onsuccessObservable = new Observable(); } } /** * 异步导入模型的回调 */ class ImportMeshSyncCallBack { newMeshes: AbstractMesh[]; particleSystems: IParticleSystem[]; skeletons: Skeleton[]; animationGroups: AnimationGroup[]; modelInfo: ModelInfo; needBox: boolean; modelData: ModelData; constructor(newMeshes: AbstractMesh[], particleSystems: IParticleSystem[], skeletons: Skeleton[], animationGroups: AnimationGroup[], modelInfo: ModelInfo, needBox: boolean, modelData: ModelData) { this.newMeshes = newMeshes; this.particleSystems = particleSystems; this.skeletons = skeletons; this.animationGroups = animationGroups; this.modelInfo = modelInfo; this.needBox = needBox; this.modelData = modelData; } }