邵佳豪 3 years ago
parent
commit
b9346b2ef4
  1. 24
      README.md
  2. 27
      src/app/babylon/controller/scene-manager.ts
  3. 28
      src/app/babylon/model/info/model/model-info-building.ts
  4. 65
      src/app/babylon/tool/babylon-tool.ts
  5. 2
      src/app/babylon/tool/mesh-pool.ts
  6. 111
      src/app/babylon/view/building-window/building-window.ts
  7. 4
      src/app/pages/left-domain/left-domain.component.ts
  8. 4
      src/app/pages/plan/plan.component.html
  9. 8
      src/app/pages/plan/plan.component.ts

24
README.md

@ -1,5 +1,29 @@
# 中国石化加油站项目 # 中国石化加油站项目
## 🌼v1.1.0
### 🌻 简介
> 时间:2022、1、22 周六
> 优化加载
> 增加镜头限制、增加吸附功能
### 🌻 详情
> 前端部分
- [🚩 重大变更]
- [🌱 新增]
- [🍀 完善]
> 三维部分
- [🚩 重大变更] 增加对关联复制模型的支持,可以大幅减少 bin 文件的大小,增加解析速度与渲染速度
- [🚩 重大变更] 暂时停止 1+n 的模型加载模式(indexDB 问题以在另外的地方进行解决),充分利用并发加载
- [🌱 新增] 摄像头旋转时设置限制,避免看到场景的底部
- [🌱 新增] 增加建筑吸附、对齐到场景中的功能,确定对齐规则,使用“WAI”节点作为插槽
- [🍀 完善]
## 🌼v1.0.9 ## 🌼v1.0.9
### 🌻 简介 ### 🌻 简介

27
src/app/babylon/controller/scene-manager.ts

@ -11,6 +11,7 @@ import {
EventState, EventState,
HemisphericLight, HemisphericLight,
HighlightLayer, HighlightLayer,
InstancedMesh,
IParticleSystem, IParticleSystem,
ISceneLoaderProgressEvent, ISceneLoaderProgressEvent,
Mesh, Mesh,
@ -57,10 +58,13 @@ import { ModelInfo_mark_multiArrow } from '../model/info/mark/other/mark-plan-mu
import { ModelInfo_mark_particle } from '../model/info/mark/other/mark-plan-particle-info'; 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 { FacilityPosType, ModelData_facility } from '../model/data/model-data/model-data-facility';
import { LoadTool } from '../tool/load-tool'; import { LoadTool } from '../tool/load-tool';
import { flatten } from 'earcut';
//场景管理器 //场景管理器
export class SceneManager { export class SceneManager {
//----------------Camera-----------------\\ //----------------Camera-----------------\\
public engine: Engine; public engine: Engine;
@ -75,7 +79,7 @@ export class SceneManager {
static s_openLight: boolean = true;//开启光照效果(关闭是要同时关闭阴影) static s_openLight: boolean = true;//开启光照效果(关闭是要同时关闭阴影)
static s_openShadow: boolean = true;//开启阴影(必须开启阴影 static s_openShadow: boolean = true;//开启阴影(必须开启光照
static s_openSkyBox: boolean = true;//使用天空盒 static s_openSkyBox: boolean = true;//使用天空盒
static s_environmentCubeTexture: CubeTexture;//环境所用的cubeTexture static s_environmentCubeTexture: CubeTexture;//环境所用的cubeTexture
static s_openEnvironmentReflection: boolean = true;//使用环境反射 static s_openEnvironmentReflection: boolean = true;//使用环境反射
@ -161,7 +165,7 @@ export class SceneManager {
); );
camera.minZ = 0;//最近拍摄距离 camera.minZ = 0;//最近拍摄距离
camera.maxZ = 6000; //摄像机拍摄的最远距离 camera.maxZ = 6000; //摄像机拍摄的最远距离
// camera.upperBetaLimit = 1.5; //beta方向上的旋转限制(防止看到模型底面) camera.upperBetaLimit = 1.5; //beta方向上的旋转限制(防止看到模型底面)
camera.lowerRadiusLimit = 1; //相机距离拍摄目标的最小距离(防止穿插) camera.lowerRadiusLimit = 1; //相机距离拍摄目标的最小距离(防止穿插)
camera.setTarget(Vector3.Zero()); //设置拍摄目标 camera.setTarget(Vector3.Zero()); //设置拍摄目标
camera.radius = 100; camera.radius = 100;
@ -229,7 +233,7 @@ export class SceneManager {
} }
else { else {
if (this.sunLight == null) { if (this.sunLight != null) {
this.sunLight.setEnabled(false); this.sunLight.setEnabled(false);
} }
@ -241,7 +245,7 @@ export class SceneManager {
* *
*/ */
public updateShadow() { public updateShadow() {
if (SceneManager.s_openShadow) { if (SceneManager.s_openLight && SceneManager.s_openShadow) {
if (this.shadowGenerator == null) { if (this.shadowGenerator == null) {
this.shadowGenerator = new ShadowGenerator(2048, this.sunLight); this.shadowGenerator = new ShadowGenerator(2048, this.sunLight);
} }
@ -663,7 +667,7 @@ export class SceneManager {
canPick = true; canPick = true;
//} //}
if (SceneManager.s_openShadow) { if (SceneManager.s_openShadow && !(newMeshes[i] instanceof InstancedMesh)) {
newMeshes[i].receiveShadows = true; newMeshes[i].receiveShadows = true;
// let mat = newMeshes[i].material; // let mat = newMeshes[i].material;
@ -873,13 +877,22 @@ export class SceneManager {
*/ */
public static meshAdsorbY(mover: AbstractMesh, target: AbstractMesh, pickPos: Vector3) { public static meshAdsorbY(mover: AbstractMesh, target: AbstractMesh, pickPos: Vector3) {
ModeManager.log("吸附模型" + target); // console.log("吸附模型" + target);
let targetRoot = SceneManager.getRootTransformNode(target); let targetRoot = SceneManager.getRootTransformNode(target) as AbstractMesh;
if (mover == targetRoot) { if (mover == targetRoot) {
return; return;
} }
//先通过吸附点吸附
let byAdsorbPoint = BabylonTool.adsorb2MeshByPoint(mover, targetRoot, "WAI")
//完成吸附了
if (byAdsorbPoint) {
return;
}
let aimPos_y: number = 0; let aimPos_y: number = 0;
let direction = 0.5; let direction = 0.5;

28
src/app/babylon/model/info/model/model-info-building.ts

@ -12,6 +12,7 @@ import { StatusManager } from "src/app/babylon/controller/status/status-manager"
import { BabylonUIStyleTool } from "src/app/babylon/tool/babylon-ui-style-tool"; import { BabylonUIStyleTool } from "src/app/babylon/tool/babylon-ui-style-tool";
import { GizmoTool } from "src/app/babylon/tool/gizmo-tool"; import { GizmoTool } from "src/app/babylon/tool/gizmo-tool";
import { TsTool } from "src/app/babylon/tool/ts-tool"; import { TsTool } from "src/app/babylon/tool/ts-tool";
import { BuildingWindow } from "src/app/babylon/view/building-window/building-window";
import { FacilityWindow } from "src/app/babylon/view/facility-window/facility-window"; import { FacilityWindow } from "src/app/babylon/view/facility-window/facility-window";
@ -27,7 +28,7 @@ export class ModelInfo_building extends ModelInfo {
facilityInfos: FacilityInfoByType[] = []; facilityInfos: FacilityInfoByType[] = [];
buildingType: BuildingType = BuildingType.Normal; buildingType: BuildingType = BuildingType.Normal;
neiRoot: TransformNode; //facilityRoot参照的节点 anchorRoot: TransformNode; //facilityRoot参照的节点
updateObserver: Observer<Scene>; updateObserver: Observer<Scene>;
onSetModelBox() { onSetModelBox() {
@ -38,30 +39,29 @@ export class ModelInfo_building extends ModelInfo {
initFacilityRoot() { initFacilityRoot() {
let allTransformNode = this.modelBox.getChildTransformNodes(); let allTransformNode = this.modelBox.getChildTransformNodes();
this.neiRoot = null; this.anchorRoot = null;
for (let i = 0; i < allTransformNode.length; i++) { for (let i = 0; i < allTransformNode.length; i++) {
if (TsTool.stringContain(allTransformNode[i].name, "nei")) { if (TsTool.stringContain(allTransformNode[i].name, "nei")) {
this.neiRoot = allTransformNode[i]; this.anchorRoot = allTransformNode[i];
break; break;
} }
} }
if (this.neiRoot == null) { if (this.anchorRoot == null) {
for (let i = 0; i < allTransformNode.length; i++) { for (let i = 0; i < allTransformNode.length; i++) {
if (TsTool.stringContain(allTransformNode[i].name, "WAI")) { if (TsTool.stringContain(allTransformNode[i].name, "WAI")) {
this.neiRoot = allTransformNode[i]; this.anchorRoot = allTransformNode[i];
break; break;
} }
} }
} }
if (this.neiRoot == null) { if (this.anchorRoot == null) {
if (!TsTool.stringContain(this.modelBox.name, "Box")) { if (!TsTool.stringContain(this.modelBox.name, "Box")) {
console.error("没有关键节点", this.modelBox.name); console.error("没有关键节点", this.modelBox.name);
} }
this.neiRoot = this.modelBox; this.anchorRoot = this.modelBox;
} }
if (this.facilityRoot != null) { if (this.facilityRoot != null) {
@ -207,13 +207,17 @@ export class ModelInfo_building extends ModelInfo {
onBeforeRender() { onBeforeRender() {
// console.log(this.modelBox.absolutePosition); // console.log(this.modelBox.absolutePosition);
this.facilityRoot.position = this.neiRoot.absolutePosition.clone(); this.facilityRoot.position = this.anchorRoot.absolutePosition.clone();
this.facilityRoot.rotation = this.neiRoot.rotation.clone(); this.facilityRoot.rotation = this.anchorRoot.rotation.clone();
//if (this.modelBox.rotationQuaternion != null) { //if (this.modelBox.rotationQuaternion != null) {
this.facilityRoot.rotationQuaternion = this.neiRoot.absoluteRotationQuaternion.clone(); this.facilityRoot.rotationQuaternion = this.anchorRoot.absoluteRotationQuaternion.clone();
if (this.neiRoot.parent != null && (this.neiRoot.parent as TransformNode).scaling.y < 0) { if (this.anchorRoot.parent != null && (this.anchorRoot.parent as TransformNode).scaling.y < 0) {
this.facilityRoot.scaling.y = -1; this.facilityRoot.scaling.y = -1;
} }
if (TsTool.stringContain(this.anchorRoot.name, "WAI") && BuildingWindow.instance != null) {
BuildingWindow.instance.setLimitCameraY_min(this.anchorRoot.absolutePosition.y);
}
//} //}
} }

65
src/app/babylon/tool/babylon-tool.ts

@ -10,6 +10,7 @@ import {
ISceneLoaderPluginAsync, ISceneLoaderPluginAsync,
ISceneLoaderProgressEvent, ISceneLoaderProgressEvent,
Mesh, Mesh,
Node,
NodeMaterial, NodeMaterial,
PBRMaterial, PBRMaterial,
QuadraticEase, QuadraticEase,
@ -146,10 +147,14 @@ export class BabylonTool {
).then(function (result) { ).then(function (result) {
LoadTool.remove(modelPath); LoadTool.remove(modelPath);
isSuccess = true; isSuccess = true;
onSuccess(result.meshes, result.particleSystems, result.skeletons, result.animationGroups); if (onSuccess) {
}).catch(function (result) { onSuccess(result.meshes, result.particleSystems, result.skeletons, result.animationGroups);
onError(scene, "load error", result); }
}).catch(function (result) {
if (onError) {
onError(scene, "load error", result);
}
}); });
// setTimeout(() => { // setTimeout(() => {
@ -274,14 +279,14 @@ export class BabylonTool {
} }
else { else {
let alpha = BabylonTool.alpha_N; let alpha = BabylonTool.alpha_N;
camera.target = BabylonTool.getWorldPosition(mesh).clone(); camera.target = BabylonTool.getWorldPosition(mesh);
camera.radius = maxScaleAxis + 50; camera.radius = maxScaleAxis + 50;
camera.alpha = alpha; camera.alpha = alpha;
camera.beta = 1; camera.beta = 1;
BabylonTool.AnimMoveCameraTarget( BabylonTool.AnimMoveCameraTarget(
camera, camera,
60, 60,
BabylonTool.getWorldPosition(mesh).clone(), BabylonTool.getWorldPosition(mesh),
maxScaleAxis + 5 maxScaleAxis + 5
); );
} }
@ -537,6 +542,56 @@ export class BabylonTool {
} }
/**
* mesh
* key的节点
* @param mover
* @param targetRoot
* @param key
*/
public static adsorb2MeshByPoint(mover: AbstractMesh, targetRoot: AbstractMesh, key: string) {
//吸附成功
let isSuccess = false;
//找到移动者的对齐点
let moverAdsorbNode = BabylonTool.getNodeContainKey(mover, key);
if (moverAdsorbNode != null && moverAdsorbNode.length > 0 && targetRoot instanceof Mesh) {
let moverAdsorbPoint = moverAdsorbNode[0] as AbstractMesh;
let aimNode = BabylonTool.getNodeContainKey(targetRoot, moverAdsorbPoint.name);
if (aimNode != null && aimNode.length > 0) {
let aimAdsorbPoint = aimNode[0] as AbstractMesh;
let aimPos = aimAdsorbPoint.getAbsolutePosition();
//世界坐标下,移动者的吸附点相对于根节点的偏移
let moverLocalPos = moverAdsorbPoint.getAbsolutePosition().subtract(mover.getAbsolutePosition());
let moverNewPos = aimPos.subtract(moverLocalPos);
mover.setAbsolutePosition(moverNewPos);
isSuccess = true;
}
}
return isSuccess;
}
/**
*
* Adsorb_
* @param mesh
*/
public static getNodeContainKey(mesh: AbstractMesh, key: string) {
let moverAdsorbNode = mesh.getChildren((node: Node) => {
if (TsTool.stringContain(node.name, key)) {
return true;
}
else {
return false;
}
}, false)
return moverAdsorbNode;
}
} }
//输入提示界面 //输入提示界面

2
src/app/babylon/tool/mesh-pool.ts

@ -307,7 +307,7 @@ export class MeshPoolInfo {
// let box = MeshBuilder.CreateBox("box", { size: 1 }); //用于测试阴影 // let box = MeshBuilder.CreateBox("box", { size: 1 }); //用于测试阴影
// box.setParent(modelInfo.modelBox); // box.setParent(modelInfo.modelBox);
// box.position = new Vector3(0, 2, 0); // box.position = new Vector3(0, 2, 0);
if (SceneManager.s_openShadow) { if (SceneManager.s_openLight && SceneManager.s_openShadow) {
SceneManager.Instance.shadowGenerator.addShadowCaster(modelInfo.modelBox, true); SceneManager.Instance.shadowGenerator.addShadowCaster(modelInfo.modelBox, true);
// console.log("添加到阴影", modelInfo.modelBox.name); // console.log("添加到阴影", modelInfo.modelBox.name);
} }

111
src/app/babylon/view/building-window/building-window.ts

@ -342,6 +342,7 @@ export class BuildingWindow extends UIBase {
onUpdate() { onUpdate() {
this.updateUVAnim(); this.updateUVAnim();
this.updateLimitCameraY();
} }
@ -553,47 +554,58 @@ export class BuildingWindow extends UIBase {
} }
} }
if (allModelNumber > 1) { //暂时关闭 1+ n的加载模式
// if (allModelNumber > 1) {
if (firstType == BuildingType.Environment) { // if (firstType == BuildingType.Environment) {
instance.createOneBuildingItem(firstItem, 0, (uiItem, index) => { // instance.createOneBuildingItem(firstItem, 0, (uiItem, index) => {
instance.onChangeCurrentBuildingItem(uiItem, false); // instance.onChangeCurrentBuildingItem(uiItem, false);
// uiItem.lookAt(false); // // uiItem.lookAt(false);
if (buildingDatas_Environment.length > 1) { // if (buildingDatas_Environment.length > 1) {
instance.addBuildings(BuildingType.Environment, 1); // instance.addBuildings(BuildingType.Environment, 1);
} // }
instance.addBuildings(BuildingType.Normal, 0); // instance.addBuildings(BuildingType.Normal, 0);
}); // });
} // }
else { // else {
instance.createOneBuildingItem(firstItem, 0, (uiItem, index) => { // instance.createOneBuildingItem(firstItem, 0, (uiItem, index) => {
instance.onChangeCurrentBuildingItem(uiItem, false); // instance.onChangeCurrentBuildingItem(uiItem, false);
// uiItem.lookAt(false); // // uiItem.lookAt(false);
if (buildingDatas_mormal.length > 1) { // if (buildingDatas_mormal.length > 1) {
instance.addBuildings(BuildingType.Normal, 1); // instance.addBuildings(BuildingType.Normal, 1);
} // }
instance.addBuildings(BuildingType.Environment, 0); // instance.addBuildings(BuildingType.Environment, 0);
}); // });
} // }
} // }
else { // else {
instance.addBuildings(BuildingType.Normal, 0, (uiItem, index) => { // instance.addBuildings(BuildingType.Normal, 0, (uiItem, index) => {
if (index == 0 && uiItem.buildingInfo.isEnable) { // if (index == 0 && uiItem.buildingInfo.isEnable) {
instance.onChangeCurrentBuildingItem(uiItem, false); // instance.onChangeCurrentBuildingItem(uiItem, false);
//uiItem.lookAt(false); // //uiItem.lookAt(false);
// console.log("默认选中" + uiItem.buildingInfo.buildingData.normalData.key); // // console.log("默认选中" + uiItem.buildingInfo.buildingData.normalData.key);
} // }
}); // });
instance.addBuildings(BuildingType.Environment, 0); // instance.addBuildings(BuildingType.Environment, 0);
} // }
instance.addBuildings(BuildingType.Normal, 0, (uiItem, index) => {
if (index == 0 && uiItem.buildingInfo.isEnable) {
instance.onChangeCurrentBuildingItem(uiItem, false);
//uiItem.lookAt(false);
// console.log("默认选中" + uiItem.buildingInfo.buildingData.normalData.key);
}
});
instance.addBuildings(BuildingType.Environment, 0);
} }
@ -1091,8 +1103,6 @@ export class BuildingWindow extends UIBase {
} }
/** /**
* *
*/ */
@ -1128,6 +1138,39 @@ export class BuildingWindow extends UIBase {
//#endregion //#endregion
//#region 限制摄像机Y
/**
* Y高度的限制
*/
limitCameraY_min: number = null;
/**
* target最小Y
* @param value
*/
setLimitCameraY_min(value: number) {
this.limitCameraY_min = value;
}
/**
* Y
*/
updateLimitCameraY() {
if (SceneManager.Instance.defaultCamera != null) {
if (this.limitCameraY_min != null) {
if (SceneManager.Instance.defaultCamera.target.y < this.limitCameraY_min) {
SceneManager.Instance.defaultCamera.target.y = this.limitCameraY_min;
}
}
}
}
//#endregion
} }

4
src/app/pages/left-domain/left-domain.component.ts

@ -371,8 +371,8 @@ export class LeftDomainComponent implements OnInit {
} else if (this.selectPlanId === item.id && this.selectNodeId === e.id) { //取消选中 } else if (this.selectPlanId === item.id && this.selectNodeId === e.id) { //取消选中
this.selectPlanId = null this.selectPlanId = null
this.selectNodeId = null this.selectNodeId = null
PlanComponent.instance.beforeEmergencyPlan = new MarkPlanData(-99, "请选择节点") PlanComponent.instance.beforeEmergencyPlan = new MarkPlanData(-99, "请选择应急预案")
PlanComponent.instance.beforePlanNode = new MarkNodeData(-99, "请选择节点") PlanComponent.instance.beforePlanNode = new MarkNodeData(-99, "请选择应急预案")
this.updateFatherData(index) //更新/初始化父组件 数据 this.updateFatherData(index) //更新/初始化父组件 数据
MarkWindow.instance.selectMarkNode(null, null) MarkWindow.instance.selectMarkNode(null, null)
} }

4
src/app/pages/plan/plan.component.html

@ -143,8 +143,8 @@
<button title="平移" (click)="translation()" [ngClass]="{'selectRightTopFast': selectRightTopFast == 0}"></button> <button title="平移" (click)="translation()" [ngClass]="{'selectRightTopFast': selectRightTopFast == 0}"></button>
<button title="旋转" (click)="revolve()" [ngClass]="{'selectRightTopFast': selectRightTopFast == 1}"></button> <button title="旋转" (click)="revolve()" [ngClass]="{'selectRightTopFast': selectRightTopFast == 1}"></button>
<button title="缩放" (click)="scale()" [ngClass]="{'selectRightTopFast': selectRightTopFast == 2}"></button> <button title="缩放" (click)="scale()" [ngClass]="{'selectRightTopFast': selectRightTopFast == 2}"></button>
<!-- <button title="吸附" (click)="adsorb()" [ngClass]="{'leftFastIsTure': selectAdsorb }"></button> <button title="吸附" (click)="adsorb()" [ngClass]="{'leftFastIsTure': selectAdsorb }"></button>
<button title="切换至顶视图" (click)="toggleTopLevelView()" [ngClass]="{'leftFastIsTure': topLevelView }"></button> --> <!-- <button title="切换至顶视图" (click)="toggleTopLevelView()" [ngClass]="{'leftFastIsTure': topLevelView }"></button> -->
</div> </div>
<div class="save"> <div class="save">
<button (click)="preserve(false)" title="保存模块"><i nz-icon nzType="file-done" nzTheme="outline"></i></button> <button (click)="preserve(false)" title="保存模块"><i nz-icon nzType="file-done" nzTheme="outline"></i></button>

8
src/app/pages/plan/plan.component.ts

@ -497,8 +497,8 @@ export class PlanComponent implements OnInit {
} }
allMarkPlanData: AllMarkPlanData; //处置预案节点 数据 allMarkPlanData: AllMarkPlanData; //处置预案节点 数据
beforeEmergencyPlan: MarkPlanData = new MarkPlanData(-99, "请选择节点"); //当前选择 应急预案 beforeEmergencyPlan: MarkPlanData = new MarkPlanData(-99, "请选择应急预案"); //当前选择 应急预案
beforePlanNode: MarkNodeData = new MarkNodeData(-99, "请选择节点"); //当前选择 预案节点 beforePlanNode: MarkNodeData = new MarkNodeData(-99, "请选择应急预案"); //当前选择 预案节点
nzCurrent: number = -1; //当前选择 预案节点Index nzCurrent: number = -1; //当前选择 预案节点Index
isSuspend: boolean = false; //是否暂停 自动切换节点 isSuspend: boolean = false; //是否暂停 自动切换节点
progressList: number[] = []; //进度条 条/值 progressList: number[] = []; //进度条 条/值
@ -506,8 +506,8 @@ export class PlanComponent implements OnInit {
//初始化 应急预案模块 //初始化 应急预案模块
initializePlan() { initializePlan() {
this.beforeEmergencyPlan = new MarkPlanData(-99, "请选择节点") this.beforeEmergencyPlan = new MarkPlanData(-99, "请选择应急预案")
this.beforePlanNode = new MarkNodeData(-99, "请选择节点") this.beforePlanNode = new MarkNodeData(-99, "请选择应急预案")
this.isSuspend = false //初始化暂停状态 this.isSuspend = false //初始化暂停状态
this.progressList = [] this.progressList = []
window.clearTimeout(this.updateTimer) //清除定时器 window.clearTimeout(this.updateTimer) //清除定时器

Loading…
Cancel
Save