init
This commit is contained in:
52
src/babylonjs/AppCamera.ts
Normal file
52
src/babylonjs/AppCamera.ts
Normal file
@ -0,0 +1,52 @@
|
||||
import { ArcRotateCamera } from '@babylonjs/core/Cameras/arcRotateCamera';
|
||||
import { Vector3 } from '@babylonjs/core/Maths/math.vector';
|
||||
import { Tools } from '@babylonjs/core/Misc/tools';
|
||||
import { Monobehiver } from '../base/Monobehiver';
|
||||
|
||||
/**
|
||||
* 相机控制类- 负责创建和控制弧形旋转相机
|
||||
*/
|
||||
export class AppCamera extends Monobehiver {
|
||||
object: ArcRotateCamera | null;
|
||||
|
||||
constructor(mainApp: any) {
|
||||
super(mainApp);
|
||||
this.object = null;
|
||||
}
|
||||
|
||||
/** 初始化相机 */
|
||||
Awake(): void {
|
||||
const scene = this.mainApp.appScene.object;
|
||||
const canvas = this.mainApp.appDom.renderDom;
|
||||
if (!scene || !canvas) return;
|
||||
|
||||
// 创建弧形旋转相机:水平角70度,垂直角80度,距离5,目标点(0,1,0)
|
||||
this.object = new ArcRotateCamera('Camera', Tools.ToRadians(70), Tools.ToRadians(80), 5, new Vector3(0, 0, 0), scene);
|
||||
this.object.attachControl(canvas, true);
|
||||
this.object.minZ = 0.01; // 近裁剪面
|
||||
this.object.wheelPrecision =999999; // 滚轮缩放精度
|
||||
this.object.panningSensibility = 0;
|
||||
this.object.position = new Vector3(-0, 0, 100);
|
||||
this.setTarget(0, 2, 0);
|
||||
}
|
||||
|
||||
/** 设置相机目标点 */
|
||||
setTarget(x: number, y: number, z: number): void {
|
||||
if (this.object) {
|
||||
this.object.target = new Vector3(x, y, z);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** 重置相机到默认位置 */
|
||||
reset(): void {
|
||||
if (!this.object) return;
|
||||
this.object.radius = 2; this.setTarget(0, 0, 0);
|
||||
this.object.position = new Vector3(0, 1.5, 2);
|
||||
}
|
||||
|
||||
update(): void {
|
||||
|
||||
}
|
||||
}
|
||||
13
src/babylonjs/AppConfig.ts
Normal file
13
src/babylonjs/AppConfig.ts
Normal file
@ -0,0 +1,13 @@
|
||||
|
||||
type OptionalCallback = (() => void) | null | undefined;
|
||||
type ErrorCallback = ((error?: unknown) => void) | null | undefined;
|
||||
|
||||
/**
|
||||
* 共享运行时配置对象
|
||||
*/
|
||||
export const AppConfig = {
|
||||
container: 'renderDom',
|
||||
modelUrlList: [] as string[],
|
||||
success: null as OptionalCallback,
|
||||
error: null as ErrorCallback
|
||||
};
|
||||
21
src/babylonjs/AppDom.ts
Normal file
21
src/babylonjs/AppDom.ts
Normal file
@ -0,0 +1,21 @@
|
||||
import { AppConfig } from './AppConfig';
|
||||
|
||||
/**
|
||||
* 负责获取渲染容器 DOM
|
||||
*/
|
||||
export class AppDom {
|
||||
private _renderDom: HTMLCanvasElement | null;
|
||||
|
||||
constructor() {
|
||||
this._renderDom = null;
|
||||
}
|
||||
|
||||
get renderDom(): HTMLCanvasElement | null {
|
||||
return this._renderDom;
|
||||
}
|
||||
|
||||
Awake(): void {
|
||||
const dom = document.getElementById(AppConfig.container) || document.querySelector('#renderDom');
|
||||
this._renderDom = (dom as HTMLCanvasElement) ?? null;
|
||||
}
|
||||
}
|
||||
38
src/babylonjs/AppEngin.ts
Normal file
38
src/babylonjs/AppEngin.ts
Normal file
@ -0,0 +1,38 @@
|
||||
import { Engine } from '@babylonjs/core/Engines/engine';
|
||||
import { Monobehiver } from '../base/Monobehiver';
|
||||
|
||||
/**
|
||||
* 渲染引擎管理类 - 负责创建和管理3D渲染引擎
|
||||
*/
|
||||
export class AppEngin extends Monobehiver {
|
||||
object: Engine | null;
|
||||
canvas: HTMLCanvasElement | null;
|
||||
|
||||
constructor(mainApp: any) {
|
||||
super(mainApp);
|
||||
this.object = null;
|
||||
this.canvas = null;
|
||||
}
|
||||
|
||||
Awake(): void {
|
||||
this.canvas = this.mainApp.appDom.renderDom;
|
||||
if (!this.canvas) {
|
||||
throw new Error('Render canvas not found');
|
||||
}
|
||||
this.object = new Engine(this.canvas, true, {
|
||||
preserveDrawingBuffer: false, // 不保留绘图缓冲区
|
||||
stencil: true, // 启用模板缓冲
|
||||
alpha: true // 启用透明背景
|
||||
});
|
||||
this.object.setSize(window.innerWidth, window.innerHeight);
|
||||
this.object.setHardwareScalingLevel(1); // 1:1像素比例
|
||||
}
|
||||
|
||||
/** 处理窗口大小变化 */
|
||||
handleResize(): void {
|
||||
if (this.object) {
|
||||
this.object.setSize(window.innerWidth, window.innerHeight);
|
||||
this.object.resize();
|
||||
}
|
||||
}
|
||||
}
|
||||
58
src/babylonjs/AppEnv.ts
Normal file
58
src/babylonjs/AppEnv.ts
Normal file
@ -0,0 +1,58 @@
|
||||
import { CubeTexture } from '@babylonjs/core/Materials/Textures/cubeTexture';
|
||||
import { Monobehiver } from '../base/Monobehiver';
|
||||
|
||||
/**
|
||||
* 环境管理类- 负责创建和管理HDR环境贴图
|
||||
*/
|
||||
export class AppEnv extends Monobehiver {
|
||||
object: CubeTexture | null;
|
||||
|
||||
constructor(mainApp: any) {
|
||||
super(mainApp);
|
||||
this.object = null;
|
||||
}
|
||||
|
||||
/** 初始化 - 创建默认HDR环境 */
|
||||
Awake(): void {
|
||||
this.createHDR();
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建HDR环境贴图
|
||||
* @param hdrPath HDR文件路径
|
||||
*/
|
||||
createHDR(hdrPath = '/hdr/sanGiuseppeBridge.env'): void {
|
||||
const scene = this.mainApp.appScene.object;
|
||||
if (!scene) return;
|
||||
const reflectionTexture = CubeTexture.CreateFromPrefilteredData(hdrPath, scene);
|
||||
scene.environmentIntensity = 1.5;
|
||||
scene.environmentTexture = reflectionTexture;
|
||||
this.object = reflectionTexture;
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改HDR环境光强度
|
||||
* @param intensity 强度值
|
||||
*/
|
||||
changeHDRIntensity(intensity: number): void {
|
||||
if (this.mainApp.appScene.object) {
|
||||
this.mainApp.appScene.object.environmentIntensity = intensity;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 旋转HDR环境贴图
|
||||
* @param angle 旋转角度(弧度)
|
||||
*/
|
||||
rotateHDR(angle: number): void {
|
||||
if (this.object) this.object.rotationY = angle;
|
||||
}
|
||||
|
||||
/** 清理资源 */
|
||||
clean(): void {
|
||||
if (this.object) {
|
||||
this.object.dispose();
|
||||
this.object = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
209
src/babylonjs/AppLight.ts
Normal file
209
src/babylonjs/AppLight.ts
Normal file
@ -0,0 +1,209 @@
|
||||
import { SpotLight } from '@babylonjs/core/Lights/spotLight';
|
||||
import { ShadowGenerator } from '@babylonjs/core/Lights/Shadows/shadowGenerator';
|
||||
import '@babylonjs/core/Lights/Shadows/shadowGeneratorSceneComponent';
|
||||
import { Vector3, Quaternion } from '@babylonjs/core/Maths/math.vector';
|
||||
import { Monobehiver } from '../base/Monobehiver';
|
||||
import { MeshBuilder } from '@babylonjs/core/Meshes/meshBuilder';
|
||||
import { StandardMaterial } from '@babylonjs/core/Materials/standardMaterial';
|
||||
import { Color3 } from '@babylonjs/core/Maths/math.color';
|
||||
import { GizmoManager } from '@babylonjs/core/Gizmos/gizmoManager';
|
||||
import { AbstractMesh } from '@babylonjs/core/Meshes/abstractMesh';
|
||||
import { Mesh } from '@babylonjs/core/Meshes/mesh';
|
||||
|
||||
type DebugMarkers = {
|
||||
marker: Mesh;
|
||||
arrow: Mesh;
|
||||
gizmoManager: GizmoManager;
|
||||
onKey: (e: KeyboardEvent) => void;
|
||||
};
|
||||
|
||||
/**
|
||||
* 灯光管理类- 负责创建和管理场景灯光
|
||||
*/
|
||||
export class AppLight extends Monobehiver {
|
||||
lightList: SpotLight[];
|
||||
shadowGenerator: ShadowGenerator | null;
|
||||
debugMarkers?: DebugMarkers;
|
||||
coneMesh?: Mesh;
|
||||
updateCone?: () => void;
|
||||
|
||||
constructor(mainApp: any) {
|
||||
super(mainApp);
|
||||
this.lightList = [];
|
||||
this.shadowGenerator = null;
|
||||
}
|
||||
|
||||
/** 初始化灯光并开启阴影 */
|
||||
Awake(): void {
|
||||
const light = new SpotLight(
|
||||
"mainLight",
|
||||
new Vector3(-0.6, 2.12, 2),
|
||||
new Vector3(0, -0.5, -1),
|
||||
Math.PI * 0.6, // angle 弧度
|
||||
);
|
||||
|
||||
light.angle = 1.5;
|
||||
light.innerAngle = 1;
|
||||
light.exponent = 2;
|
||||
light.diffuse = new Color3(1, 0.86, 0.80);
|
||||
light.specular = new Color3(1, 1, 1);
|
||||
light.intensity = 60;
|
||||
light.shadowMinZ = 0.01;
|
||||
light.shadowMaxZ = 100;
|
||||
light.range = 5000;
|
||||
|
||||
const generator = new ShadowGenerator(4096, light);
|
||||
generator.usePercentageCloserFiltering = true;
|
||||
generator.filteringQuality = ShadowGenerator.QUALITY_HIGH;
|
||||
generator.transparencyShadow = true;
|
||||
|
||||
this.lightList.push(light);
|
||||
this.shadowGenerator = generator;
|
||||
}
|
||||
|
||||
/** 将网格添加为阴影投射者 */
|
||||
addShadowCaster(mesh: AbstractMesh): void {
|
||||
if (this.shadowGenerator) {
|
||||
this.shadowGenerator.addShadowCaster(mesh);
|
||||
}
|
||||
}
|
||||
|
||||
/** 设置主光源强度 */
|
||||
setIntensity(intensity: number): void {
|
||||
if (this.lightList[0]) this.lightList[0].intensity = intensity;
|
||||
}
|
||||
|
||||
/** 创建灯光可视化调试器 - W键拖拽位置,E键旋转方向 */
|
||||
enableLightDebug(): void {
|
||||
const scene = this.mainApp.appScene.object;
|
||||
const light = this.lightList[0];
|
||||
if (!light || !scene) return;
|
||||
|
||||
const marker = MeshBuilder.CreateSphere("lightMarker", { diameter: 0.3 }, scene);
|
||||
marker.position = light.position.clone();
|
||||
const mat = new StandardMaterial("lightMat", scene);
|
||||
mat.emissiveColor = Color3.Yellow();
|
||||
marker.material = mat;
|
||||
|
||||
const arrow = MeshBuilder.CreateCylinder("lightArrow", { height: 1, diameterTop: 0, diameterBottom: 0.1 }, scene);
|
||||
arrow.parent = marker;
|
||||
arrow.position.set(0, 0, 0.6);
|
||||
arrow.rotation.x = Math.PI / 2;
|
||||
const arrowMat = new StandardMaterial("arrowMat", scene);
|
||||
arrowMat.emissiveColor = Color3.Red();
|
||||
arrow.material = arrowMat;
|
||||
|
||||
const dir = light.direction.normalize();
|
||||
marker.rotation.y = Math.atan2(dir.x, dir.z);
|
||||
marker.rotation.x = -Math.asin(dir.y);
|
||||
|
||||
const gizmoManager = new GizmoManager(scene);
|
||||
gizmoManager.attachableMeshes = [marker];
|
||||
gizmoManager.usePointerToAttachGizmos = false;
|
||||
gizmoManager.attachToMesh(marker);
|
||||
|
||||
scene.onBeforeRenderObservable.add(() => {
|
||||
light.position.copyFrom(marker.position);
|
||||
const forward = new Vector3(0, 0, 1);
|
||||
const rotationMatrix = marker.getWorldMatrix().getRotationMatrix();
|
||||
light.direction = Vector3.TransformNormal(forward, rotationMatrix).normalize();
|
||||
});
|
||||
|
||||
const onKey = (e: KeyboardEvent) => {
|
||||
if (e.key === 'w' || e.key === 'W') {
|
||||
gizmoManager.positionGizmoEnabled = true;
|
||||
gizmoManager.rotationGizmoEnabled = false;
|
||||
} else if (e.key === 'e' || e.key === 'E') {
|
||||
gizmoManager.positionGizmoEnabled = false;
|
||||
gizmoManager.rotationGizmoEnabled = true;
|
||||
}
|
||||
};
|
||||
window.addEventListener('keydown', onKey);
|
||||
gizmoManager.positionGizmoEnabled = true;
|
||||
|
||||
this.debugMarkers = { marker, arrow, gizmoManager, onKey };
|
||||
}
|
||||
|
||||
/** 隐藏灯光调试器 */
|
||||
disableLightDebug(): void {
|
||||
if (this.debugMarkers) {
|
||||
window.removeEventListener('keydown', this.debugMarkers.onKey);
|
||||
this.debugMarkers.gizmoManager.dispose();
|
||||
this.debugMarkers.arrow.dispose();
|
||||
this.debugMarkers.marker.dispose();
|
||||
this.debugMarkers = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
/** 创建聚光灯可视化Gizmo - 带光锥范围 */
|
||||
createLightGizmo(): void {
|
||||
const scene = this.mainApp.appScene.object;
|
||||
const light = this.lightList[0];
|
||||
if (!light || !scene) return;
|
||||
|
||||
const coneLength = 3;
|
||||
const updateCone = () => {
|
||||
if (this.coneMesh) this.coneMesh.dispose();
|
||||
const radius = Math.tan(light.angle) * coneLength;
|
||||
const cone = MeshBuilder.CreateCylinder("lightCone", {
|
||||
height: coneLength,
|
||||
diameterTop: radius * 2,
|
||||
diameterBottom: 0
|
||||
}, scene);
|
||||
const mat = new StandardMaterial("coneMat", scene);
|
||||
mat.emissiveColor = Color3.Yellow();
|
||||
mat.alpha = 0.2;
|
||||
mat.wireframe = true;
|
||||
cone.material = mat;
|
||||
|
||||
cone.position = light.position.add(light.direction.scale(coneLength / 2));
|
||||
const up = new Vector3(0, 1, 0);
|
||||
const axis = Vector3.Cross(up, light.direction).normalize();
|
||||
const angle = Math.acos(Vector3.Dot(up, light.direction.normalize()));
|
||||
if (axis.length() > 0.001) cone.rotationQuaternion = Quaternion.RotationAxis(axis, angle);
|
||||
|
||||
this.coneMesh = cone;
|
||||
};
|
||||
|
||||
updateCone();
|
||||
this.updateCone = updateCone;
|
||||
}
|
||||
|
||||
/** 创建angle和innerAngle调试滑动条 */
|
||||
createAngleSliders(): void {
|
||||
const light = this.lightList[0];
|
||||
if (!light) return;
|
||||
|
||||
const container = document.createElement('div');
|
||||
container.style.cssText = 'position:fixed;top:10px;right:10px;background:rgba(0,0,0,0.7);padding:10px;border-radius:5px;color:#fff;font-size:12px;z-index:1000';
|
||||
|
||||
const createSlider = (label: string, value: number, min: number, max: number, onChange: (v: number) => void) => {
|
||||
const wrap = document.createElement('div');
|
||||
wrap.style.marginBottom = '8px';
|
||||
const lbl = document.createElement('div');
|
||||
lbl.textContent = `${label}: ${value}`;
|
||||
const slider = document.createElement('input');
|
||||
slider.type = 'range';
|
||||
slider.min = String(min);
|
||||
slider.max = String(max);
|
||||
slider.value = String(value);
|
||||
slider.style.width = '150px';
|
||||
slider.oninput = () => {
|
||||
lbl.textContent = `${label}: ${slider.value}`;
|
||||
onChange(Number(slider.value));
|
||||
};
|
||||
wrap.append(lbl, slider);
|
||||
return wrap;
|
||||
};
|
||||
|
||||
const toRad = (deg: number) => deg * Math.PI / 180;
|
||||
const toDeg = (rad: number) => Math.round(rad * 180 / Math.PI);
|
||||
|
||||
container.append(
|
||||
createSlider('angle', toDeg(light.angle), 1, 180, v => { light.angle = toRad(v); this.updateCone?.(); }),
|
||||
createSlider('innerAngle', toDeg(light.innerAngle), 0, 180, v => { light.innerAngle = toRad(v); })
|
||||
);
|
||||
|
||||
document.body.appendChild(container);
|
||||
}
|
||||
}
|
||||
119
src/babylonjs/AppModel.ts
Normal file
119
src/babylonjs/AppModel.ts
Normal file
@ -0,0 +1,119 @@
|
||||
import { ImportMeshAsync } from '@babylonjs/core/Loading/sceneLoader';
|
||||
import '@babylonjs/loaders/glTF';
|
||||
import { AbstractMesh } from '@babylonjs/core/Meshes/abstractMesh';
|
||||
import { Scene } from '@babylonjs/core/scene';
|
||||
import { Monobehiver } from '../base/Monobehiver';
|
||||
import { Dictionary } from '../utils/Dictionary';
|
||||
import { AppConfig } from './AppConfig';
|
||||
|
||||
type LoadResult = {
|
||||
success: boolean;
|
||||
meshes?: AbstractMesh[];
|
||||
skeletons?: unknown[];
|
||||
error?: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* 模型管理类- 负责加载、缓存和管理3D模型
|
||||
*/
|
||||
export class AppModel extends Monobehiver {
|
||||
private modelDic: Dictionary<AbstractMesh[]>;
|
||||
private loadedMeshes: AbstractMesh[];
|
||||
private skeletonManager: any;
|
||||
private outfitManager: any;
|
||||
private isLoading: boolean;
|
||||
private skeletonMerged: boolean;
|
||||
|
||||
constructor(mainApp: any) {
|
||||
super(mainApp);
|
||||
this.modelDic = new Dictionary<AbstractMesh[]>();
|
||||
this.loadedMeshes = [];
|
||||
this.skeletonManager = null;
|
||||
this.outfitManager = null;
|
||||
this.isLoading = false;
|
||||
this.skeletonMerged = false;
|
||||
}
|
||||
|
||||
/** 初始化子管理器(占位:实际实现已移除) */
|
||||
initManagers(): void {
|
||||
// 这里原本会初始化 SkeletonManager 和 OutfitManager,已留空以避免恢复已删除的实现
|
||||
}
|
||||
|
||||
/** 加载配置中的所有模型 */
|
||||
async loadModel(): Promise<void> {
|
||||
if (!AppConfig.modelUrlList?.length || this.isLoading) return;
|
||||
this.isLoading = true;
|
||||
try {
|
||||
for (const url of AppConfig.modelUrlList) {
|
||||
await this.loadSingleModel(url);
|
||||
}
|
||||
|
||||
} finally {
|
||||
this.isLoading = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载单个模型
|
||||
* @param modelUrl 模型URL
|
||||
*/
|
||||
async loadSingleModel(modelUrl: string): Promise<LoadResult> {
|
||||
try {
|
||||
const cached = this.getCachedMeshes(modelUrl);
|
||||
if (cached) return { success: true, meshes: cached };
|
||||
|
||||
const scene: Scene | null = this.mainApp.appScene.object;
|
||||
if (!scene) return { success: false, error: '场景未初始化' };
|
||||
|
||||
// ImportMeshAsync的签名与当前调用不完全一致,使用any规避编译报错
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||
const result: any = await (ImportMeshAsync as any)(modelUrl, scene);
|
||||
if (!result?.meshes?.length) return { success: false, error: '未找到网格' };
|
||||
|
||||
this.modelDic.Set(modelUrl, result.meshes);
|
||||
this.loadedMeshes.push(...result.meshes);
|
||||
|
||||
this.setupShadows(result.meshes as AbstractMesh[]);
|
||||
|
||||
return { success: true, meshes: result.meshes, skeletons: result.skeletons };
|
||||
} catch (e: any) {
|
||||
console.error(`模型加载失败: ${modelUrl}`, e);
|
||||
return { success: false, error: e?.message };
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/** 为网格设置阴影(投射和接收) */
|
||||
setupShadows(meshes: AbstractMesh[]): void {
|
||||
const appLight = this.mainApp.appLight;
|
||||
if (!appLight) return;
|
||||
|
||||
meshes.forEach(mesh => {
|
||||
if (mesh.getTotalVertices() > 0) {
|
||||
appLight.addShadowCaster(mesh);
|
||||
mesh.receiveShadows = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/** 获取缓存的网格 */
|
||||
getCachedMeshes(url: string): AbstractMesh[] | undefined {
|
||||
return this.modelDic.Get(url);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/** 清理所有资源 */
|
||||
clean(): void {
|
||||
this.modelDic.Clear();
|
||||
this.loadedMeshes.forEach(m => m?.dispose());
|
||||
this.loadedMeshes = [];
|
||||
this.skeletonManager?.clean();
|
||||
this.outfitManager?.clean();
|
||||
this.isLoading = false;
|
||||
this.skeletonMerged = false;
|
||||
}
|
||||
}
|
||||
28
src/babylonjs/AppScene.ts
Normal file
28
src/babylonjs/AppScene.ts
Normal file
@ -0,0 +1,28 @@
|
||||
import { Scene } from '@babylonjs/core/scene';
|
||||
import { ImageProcessingConfiguration } from '@babylonjs/core/Materials';
|
||||
import { Color4 } from '@babylonjs/core/Maths/math.color';
|
||||
import { Monobehiver } from '../base/Monobehiver';
|
||||
|
||||
/**
|
||||
* 场景管理类- 负责创建和管理3D场景
|
||||
*/
|
||||
export class AppScene extends Monobehiver {
|
||||
object: Scene | null;
|
||||
|
||||
constructor(mainApp: any) {
|
||||
super(mainApp);
|
||||
this.object = null;
|
||||
}
|
||||
|
||||
/** 初始化场景 */
|
||||
Awake(): void {
|
||||
this.object = new Scene(this.mainApp.appEngin.object);
|
||||
this.object.clearColor = new Color4(0, 0, 0, 0); // 透明背景
|
||||
this.object.skipFrustumClipping = true; // 跳过视锥剔除优化性能
|
||||
// 1. 开启色调映射(Tone mapping)
|
||||
// this.object.imageProcessingConfiguration.toneMappingEnabled = true;
|
||||
|
||||
// 2. 设置色调映射类型为ACES
|
||||
// this.object.imageProcessingConfiguration.toneMappingType = ImageProcessingConfiguration.TONEMAPPING_ACES;
|
||||
}
|
||||
}
|
||||
83
src/babylonjs/MainApp.ts
Normal file
83
src/babylonjs/MainApp.ts
Normal file
@ -0,0 +1,83 @@
|
||||
/**
|
||||
* @file MainApp.ts
|
||||
* @description 主应用类,负责初始化和协调所有子模块
|
||||
*/
|
||||
|
||||
import { AppDom } from './AppDom';
|
||||
import { AppEngin } from './AppEngin';
|
||||
import { AppScene } from './AppScene';
|
||||
import { AppCamera } from './AppCamera';
|
||||
import { AppLight } from './AppLight';
|
||||
import { AppEnv } from './AppEnv';
|
||||
import { AppModel } from './AppModel';
|
||||
import { AppConfig } from './AppConfig';
|
||||
|
||||
/**
|
||||
* 主应用类 - 3D场景的核心控制器
|
||||
* 负责管理DOM、引擎、场景、相机、灯光、环境、模型和动画等子模块
|
||||
*/
|
||||
export class MainApp {
|
||||
appDom: AppDom;
|
||||
appEngin: AppEngin;
|
||||
appScene: AppScene;
|
||||
appCamera: AppCamera;
|
||||
appModel: AppModel;
|
||||
appLight: AppLight;
|
||||
appEnv: AppEnv;
|
||||
|
||||
|
||||
constructor() {
|
||||
this.appDom = new AppDom();
|
||||
this.appEngin = new AppEngin(this);
|
||||
this.appScene = new AppScene(this);
|
||||
this.appCamera = new AppCamera(this);
|
||||
this.appModel = new AppModel(this);
|
||||
this.appLight = new AppLight(this);
|
||||
this.appEnv = new AppEnv(this);
|
||||
|
||||
window.addEventListener("resize", () => this.appEngin.handleResize());
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载应用配置
|
||||
* @param config 配置对象
|
||||
*/
|
||||
loadAConfig(config: any): void {
|
||||
AppConfig.container = config.container || 'renderDom';
|
||||
AppConfig.modelUrlList = config.modelUrlList || [];
|
||||
AppConfig.success = config.success;
|
||||
AppConfig.error = config.error;
|
||||
}
|
||||
|
||||
loadModel(): void {
|
||||
this.appModel.loadModel();
|
||||
}
|
||||
|
||||
/** 唤醒/初始化所有子模块 */
|
||||
async Awake(): Promise<void> {
|
||||
this.appDom.Awake();
|
||||
this.appEngin.Awake();
|
||||
this.appScene.Awake();
|
||||
this.appCamera.Awake();
|
||||
this.appLight.Awake();
|
||||
this.appEnv.Awake();
|
||||
|
||||
this.appModel.initManagers();
|
||||
this.update();
|
||||
}
|
||||
|
||||
/** 启动渲染循环 */
|
||||
update(): void {
|
||||
if (!this.appEngin.object) return;
|
||||
this.appEngin.object.runRenderLoop(() => {
|
||||
this.appScene.object?.render();
|
||||
this.appCamera.update();
|
||||
});
|
||||
}
|
||||
|
||||
/** 销毁应用,释放所有资源 */
|
||||
async dispose(): Promise<void> {
|
||||
this.appModel?.clean();
|
||||
this.appEnv?.clean();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user