1
This commit is contained in:
17
index.html
17
index.html
@ -45,7 +45,7 @@
|
||||
const config = {
|
||||
container: document.querySelector('#renderDom'),
|
||||
modelUrlList: ['https://sdk.zguiy.com/resurces/model/model.glb'],
|
||||
env: { envPath: 'https://sdk.zguiy.com/resurces/hdr/hdr.env', intensity: 1.2, rotationY: 0.3,background: false },
|
||||
env: { envPath: 'https://sdk.zguiy.com/resurces/hdr/hdr.env', intensity: 1.2, rotationY: 0.3, background: false },
|
||||
};
|
||||
|
||||
kernel.init(config);
|
||||
@ -60,6 +60,20 @@
|
||||
|
||||
kernel.on('model:loaded', (data) => {
|
||||
console.log('模型加载完成', data);
|
||||
|
||||
|
||||
|
||||
|
||||
});
|
||||
|
||||
kernel.on('all:ready', (data) => {
|
||||
console.log('所有模块加载完成', data);
|
||||
kernel.material.apply({
|
||||
target: 'Material__2',
|
||||
attribute: 'alpha',
|
||||
value: 0.5,
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
||||
@ -70,7 +84,6 @@
|
||||
|
||||
|
||||
|
||||
|
||||
</script>
|
||||
</body>
|
||||
|
||||
|
||||
@ -21,13 +21,18 @@ export class AppCamera extends Monobehiver {
|
||||
const canvas = AppConfig.container;
|
||||
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);
|
||||
// 创建弧形旋转相机:水平角70度,垂直角85度(接近上帝视角),距离5,目标点(0,2,0)
|
||||
this.object = new ArcRotateCamera('Camera', Tools.ToRadians(70), Tools.ToRadians(85), 5, new Vector3(0, 2, 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.object.upperBetaLimit = Tools.ToRadians(60); // 最大垂直角(接近90度,避免万向锁)
|
||||
this.object.lowerBetaLimit = Tools.ToRadians(60); // 最小垂直角
|
||||
|
||||
this.object.position = new Vector3(-0, 100, 0);
|
||||
this.setTarget(0, 2, 0);
|
||||
}
|
||||
|
||||
@ -43,8 +48,11 @@ export class AppCamera extends Monobehiver {
|
||||
/** 重置相机到默认位置 */
|
||||
reset(): void {
|
||||
if (!this.object) return;
|
||||
this.object.radius = 2; this.setTarget(0, 0, 0);
|
||||
this.object.position = new Vector3(0, 1.5, 2);
|
||||
this.object.radius = 5;
|
||||
this.object.alpha = Tools.ToRadians(60); // 水平角
|
||||
this.object.beta = Tools.ToRadians(60); // 垂直角(上帝视角)
|
||||
this.setTarget(0, 2, 0);
|
||||
this.object.position = new Vector3(-0, 100, 0);
|
||||
}
|
||||
|
||||
update(): void {
|
||||
|
||||
@ -149,4 +149,29 @@ export class AppModel extends Monobehiver {
|
||||
this.skeletonMerged = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 销毁指定模型
|
||||
* @param modelName 模型名称
|
||||
*/
|
||||
destroyModel(modelName: string): void {
|
||||
// 遍历模型字典,查找匹配的模型
|
||||
const keys = this.modelDic.Keys();
|
||||
for (const key of keys) {
|
||||
if (key.includes(modelName)) {
|
||||
const meshes = this.modelDic.Get(key);
|
||||
if (meshes) {
|
||||
// 销毁所有网格
|
||||
meshes.forEach(mesh => mesh?.dispose());
|
||||
// 从字典中移除
|
||||
this.modelDic.Remove(key);
|
||||
// 从加载的网格列表中移除
|
||||
this.loadedMeshes = this.loadedMeshes.filter(mesh => !meshes.includes(mesh));
|
||||
console.log(`Model destroyed: ${modelName}`);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
console.warn(`Model not found: ${modelName}`);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -119,6 +119,25 @@ class AppRay extends Monobehiver {
|
||||
console.error('清除高亮失败:', error)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 渲染热点
|
||||
* @param hotspots 热点数据
|
||||
*/
|
||||
renderHotspots(hotspots: any[]): void {
|
||||
console.log('Rendering hotspots:', hotspots);
|
||||
|
||||
// 这里需要根据实际的热点渲染逻辑实现
|
||||
// 示例实现:
|
||||
// 1. 清除现有的热点
|
||||
// 2. 根据热点数据创建新的热点标记
|
||||
// 3. 为热点添加交互事件
|
||||
|
||||
hotspots.forEach((hotspot, index) => {
|
||||
console.log(`Rendering hotspot ${index}:`, hotspot);
|
||||
// 这里需要根据实际的热点数据结构实现
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export { AppRay }
|
||||
|
||||
475
src/babylonjs/GameManager.ts
Normal file
475
src/babylonjs/GameManager.ts
Normal file
@ -0,0 +1,475 @@
|
||||
import { Mesh, PBRMaterial, Texture, AbstractMesh } from "@babylonjs/core";
|
||||
import { Monobehiver } from '../base/Monobehiver';
|
||||
import { Dictionary } from '../utils/Dictionary';
|
||||
import { AppConfig } from './AppConfig';
|
||||
|
||||
/**
|
||||
* 游戏管理器类 - 负责管理游戏逻辑、材质和纹理
|
||||
*/
|
||||
export class GameManager extends Monobehiver {
|
||||
private materialDic: Dictionary<PBRMaterial>;
|
||||
private meshDic: Dictionary<any>;
|
||||
private oldTextureDic: Dictionary<any>;
|
||||
|
||||
// 记录加载失败的贴图
|
||||
private failedTextures: Array<{
|
||||
path: string;
|
||||
materialName?: string;
|
||||
textureType?: string;
|
||||
error?: string;
|
||||
timestamp: Date;
|
||||
}>;
|
||||
|
||||
constructor(mainApp: any) {
|
||||
super(mainApp);
|
||||
this.materialDic = new Dictionary<PBRMaterial>();
|
||||
this.meshDic = new Dictionary<any>();
|
||||
this.oldTextureDic = new Dictionary<any>();
|
||||
this.failedTextures = [];
|
||||
}
|
||||
|
||||
/** 初始化游戏管理器 */
|
||||
async Awake() {
|
||||
const scene = this.mainApp.appScene?.object;
|
||||
if (!scene) {
|
||||
console.warn('Scene not found');
|
||||
return;
|
||||
}
|
||||
|
||||
// 初始化材质字典
|
||||
for (const mat of scene.materials) {
|
||||
if (!this.materialDic.Has(mat.name)) {
|
||||
// 初始化材质属性
|
||||
mat.transparencyMode = PBRMaterial.PBRMATERIAL_ALPHABLEND;
|
||||
this.materialDic.Set(mat.name, mat as PBRMaterial);
|
||||
}
|
||||
}
|
||||
|
||||
// 初始化网格字典
|
||||
for (const mesh of scene.meshes) {
|
||||
if (mesh instanceof Mesh) {
|
||||
|
||||
this.meshDic.Set(mesh.name, mesh);
|
||||
}
|
||||
}
|
||||
console.log('材质字典:', this.materialDic);
|
||||
}
|
||||
|
||||
/** 初始化设置材质 */
|
||||
async initSetMaterial(oldObject: any) {
|
||||
if (!oldObject?.Component?.length) return;
|
||||
|
||||
|
||||
const { degreeId, Component } = oldObject;
|
||||
let degreeTextureDic = this.oldTextureDic.Get(degreeId) || {};
|
||||
const texturePromises: Promise<void>[] = [];
|
||||
|
||||
// 处理每个组件
|
||||
for (const component of Component) {
|
||||
const {
|
||||
name,
|
||||
albedoTexture,
|
||||
bumpTexture,
|
||||
alphaTexture,
|
||||
aoTexture,
|
||||
} = component;
|
||||
|
||||
if (!name) continue;
|
||||
|
||||
// 获取材质
|
||||
const mat = this.materialDic.Get(name);
|
||||
if (!mat) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// 获取或初始化纹理字典
|
||||
const textureDic = degreeTextureDic[name] || {
|
||||
albedo: null,
|
||||
bump: null,
|
||||
alpha: null,
|
||||
ao: null
|
||||
};
|
||||
|
||||
// 定义纹理任务
|
||||
const textureTasks = [
|
||||
{
|
||||
key: "albedo",
|
||||
path: albedoTexture,
|
||||
property: "albedoTexture"
|
||||
},
|
||||
{
|
||||
key: "bump",
|
||||
path: bumpTexture,
|
||||
property: "bumpTexture"
|
||||
},
|
||||
{
|
||||
key: "alpha",
|
||||
path: alphaTexture,
|
||||
property: "opacityTexture"
|
||||
},
|
||||
{
|
||||
key: "ao",
|
||||
path: aoTexture,
|
||||
property: "ambientTexture"
|
||||
}
|
||||
];
|
||||
|
||||
// 处理每个纹理任务
|
||||
for (const task of textureTasks) {
|
||||
const { key, path, property } = task;
|
||||
if (!path) continue;
|
||||
|
||||
const fullPath = this.getPublicUrl() + path;
|
||||
let texture = textureDic[key];
|
||||
|
||||
if (!texture) {
|
||||
try {
|
||||
texture = this.createTextureWithFallback(fullPath);
|
||||
if (!texture) {
|
||||
// 记录失败的贴图信息
|
||||
this.failedTextures.push({
|
||||
path: fullPath,
|
||||
materialName: name,
|
||||
textureType: key,
|
||||
error: '贴图创建失败',
|
||||
timestamp: new Date()
|
||||
});
|
||||
continue;
|
||||
}
|
||||
|
||||
// 设置非ktx2格式的vScale
|
||||
if (!fullPath.toLowerCase().endsWith('.ktx2')) {
|
||||
texture.vScale = -1;
|
||||
}
|
||||
textureDic[key] = texture;
|
||||
} catch (error: any) {
|
||||
// 记录失败的贴图信息
|
||||
this.failedTextures.push({
|
||||
path: fullPath,
|
||||
materialName: name,
|
||||
textureType: key,
|
||||
error: error.message || error.toString(),
|
||||
timestamp: new Date()
|
||||
});
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// 将纹理赋值任务加入队列
|
||||
texturePromises.push(
|
||||
this.handleTextureAssignment(mat, textureDic, key, (texture) => {
|
||||
(mat as any)[property] = texture;
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
// 更新纹理字典
|
||||
degreeTextureDic[name] = textureDic;
|
||||
}
|
||||
|
||||
// 等待所有纹理任务完成
|
||||
try {
|
||||
await Promise.all(texturePromises);
|
||||
|
||||
// 在所有贴图加载完成后设置材质属性
|
||||
for (const component of Component) {
|
||||
const { name, transparencyMode, bumpTextureLevel } = component;
|
||||
if (!name) continue;
|
||||
|
||||
const mat = this.materialDic.Get(name);
|
||||
if (!mat) continue;
|
||||
|
||||
mat.transparencyMode = transparencyMode;
|
||||
|
||||
if (mat.bumpTexture) {
|
||||
mat.bumpTexture.level = bumpTextureLevel;
|
||||
}
|
||||
|
||||
// 应用新的PBR材质属性
|
||||
this.applyPBRProperties(mat, component);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error loading textures:', error);
|
||||
} finally {
|
||||
if (this.mainApp.appDom?.load3D) {
|
||||
this.mainApp.appDom.load3D.style.display = "none";
|
||||
}
|
||||
}
|
||||
|
||||
// 保存更新后的纹理字典
|
||||
this.oldTextureDic.Set(degreeId, degreeTextureDic);
|
||||
}
|
||||
|
||||
/**
|
||||
* 应用PBR材质属性
|
||||
* @param mat - PBR材质对象
|
||||
* @param component - 配置组件对象
|
||||
*/
|
||||
private applyPBRProperties(mat: PBRMaterial, component: any) {
|
||||
// 定义PBR属性映射任务
|
||||
const pbrTasks = [
|
||||
{
|
||||
key: "fresnel",
|
||||
value: component.fresnel,
|
||||
apply: (value: number) => {
|
||||
mat.indexOfRefraction = value;
|
||||
}
|
||||
},
|
||||
{
|
||||
key: "clearcoat",
|
||||
value: component.clearcoat,
|
||||
apply: (value: number) => {
|
||||
mat.clearCoat.isEnabled = true;
|
||||
mat.clearCoat.intensity = value;
|
||||
}
|
||||
},
|
||||
{
|
||||
key: "clearcoatRoughness",
|
||||
value: component.clearcoatRoughness,
|
||||
apply: (value: number) => {
|
||||
mat.clearCoat.roughness = value;
|
||||
}
|
||||
},
|
||||
{
|
||||
key: "roughness",
|
||||
value: component.roughness,
|
||||
apply: (value: number) => {
|
||||
mat.roughness = value;
|
||||
}
|
||||
},
|
||||
{
|
||||
key: "metallic",
|
||||
value: component.metallic,
|
||||
apply: (value: number) => {
|
||||
mat.metallic = value;
|
||||
}
|
||||
},
|
||||
{
|
||||
key: "alpha",
|
||||
value: component.alpha,
|
||||
apply: (value: number) => {
|
||||
mat.alpha = value;
|
||||
}
|
||||
},
|
||||
{
|
||||
key: "environmentIntensity",
|
||||
value: component.environmentIntensity,
|
||||
apply: (value: number) => {
|
||||
mat.environmentIntensity = value;
|
||||
}
|
||||
},
|
||||
{
|
||||
key: "baseColor",
|
||||
value: component.baseColor,
|
||||
apply: (value: any) => {
|
||||
if (value && typeof value === 'object') {
|
||||
const { r, g, b } = value;
|
||||
if (r !== null && r !== undefined &&
|
||||
g !== null && g !== undefined &&
|
||||
b !== null && b !== undefined) {
|
||||
mat.albedoColor.set(r, g, b);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
// 处理每个PBR属性任务
|
||||
for (const task of pbrTasks) {
|
||||
if (task.value !== null && task.value !== undefined) {
|
||||
try {
|
||||
task.apply(task.value);
|
||||
} catch (error) {
|
||||
console.warn('Error applying PBR property:', task.key, error);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** 通用的批量卸载贴图资源的方法 */
|
||||
private clearTextures(textureDic: Dictionary<any>): Promise<void> {
|
||||
return new Promise<void>((resolve) => {
|
||||
textureDic.Values().forEach((textures) => {
|
||||
for (const key in textures) {
|
||||
const texture = textures[key];
|
||||
if (texture && texture instanceof Texture) {
|
||||
texture.dispose();
|
||||
}
|
||||
}
|
||||
});
|
||||
textureDic.Clear();
|
||||
resolve();
|
||||
});
|
||||
}
|
||||
|
||||
/** 处理纹理赋值 */
|
||||
private async handleTextureAssignment(mat: any, oldtextureDic: any, textureKey: string, assignCallback: (texture: Texture) => void) {
|
||||
const texture = oldtextureDic[textureKey];
|
||||
if (texture) {
|
||||
await this.checkTextureLoadedWithPromise(texture);
|
||||
assignCallback(texture);
|
||||
}
|
||||
}
|
||||
|
||||
/** 检查纹理是否加载完成 */
|
||||
private checkTextureLoadedWithPromise(texture: Texture): Promise<void> {
|
||||
return new Promise((resolve) => {
|
||||
if (texture.isReady()) {
|
||||
resolve();
|
||||
} else {
|
||||
texture.onLoadObservable.addOnce(() => {
|
||||
resolve();
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/** 重置相机位置 */
|
||||
reSet() {
|
||||
if (this.mainApp.appCamera?.object?.position) {
|
||||
this.mainApp.appCamera.object.position.set(160, 50, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/** 获取公共URL */
|
||||
private getPublicUrl(): string {
|
||||
// 尝试从环境变量获取
|
||||
if (import.meta && import.meta.env && import.meta.env.VITE_PUBLIC_URL) {
|
||||
return import.meta.env.VITE_PUBLIC_URL;
|
||||
}
|
||||
// 默认返回空字符串
|
||||
return '';
|
||||
}
|
||||
|
||||
/** 清理资源 */
|
||||
dispose() {
|
||||
// 清理所有材质资源
|
||||
this.materialDic.Values().forEach((material) => {
|
||||
if (material && material.dispose) {
|
||||
material.dispose();
|
||||
}
|
||||
});
|
||||
this.materialDic.Clear();
|
||||
|
||||
// 清理所有贴图资源
|
||||
this.clearTextures(this.oldTextureDic);
|
||||
|
||||
// 清理所有网格
|
||||
this.meshDic.Values().forEach((mesh) => {
|
||||
if (mesh && mesh.dispose) {
|
||||
mesh.dispose();
|
||||
}
|
||||
});
|
||||
this.meshDic.Clear();
|
||||
|
||||
// 清空失败贴图记录
|
||||
this.failedTextures = [];
|
||||
}
|
||||
|
||||
/** 更新 */
|
||||
update() { }
|
||||
|
||||
/** 尝试创建贴图的方法,支持多种格式回退 */
|
||||
private createTextureWithFallback(texturePath: string): Texture | null {
|
||||
const failureReasons: string[] = [];
|
||||
|
||||
try {
|
||||
const texture = new Texture(texturePath);
|
||||
|
||||
if (texture) {
|
||||
return texture;
|
||||
} else {
|
||||
failureReasons.push(`原始路径创建失败: ${texturePath}`);
|
||||
throw new Error('Texture creation returned null');
|
||||
}
|
||||
} catch (error: any) {
|
||||
const errorMessage = error.message || error.toString();
|
||||
|
||||
// 特别处理KTX错误
|
||||
if (errorMessage.includes('KTX identifier') || errorMessage.includes('missing KTX') ||
|
||||
(texturePath.toLowerCase().endsWith('.ktx2') && errorMessage)) {
|
||||
this.failedTextures.push({
|
||||
path: texturePath,
|
||||
textureType: 'KTX2',
|
||||
error: `KTX错误: ${errorMessage}`,
|
||||
timestamp: new Date()
|
||||
});
|
||||
}
|
||||
|
||||
failureReasons.push(`原始路径加载异常: ${texturePath} - ${errorMessage}`);
|
||||
|
||||
// 如果是ktx2文件加载失败,尝试查找对应的jpg/png文件
|
||||
if (texturePath.toLowerCase().endsWith('.ktx2')) {
|
||||
// 尝试jpg格式
|
||||
const jpgPath = texturePath.replace(/\.ktx2$/i, '.jpg');
|
||||
try {
|
||||
const jpgTexture = new Texture(jpgPath);
|
||||
if (jpgTexture) {
|
||||
return jpgTexture;
|
||||
}
|
||||
} catch (jpgError: any) {
|
||||
failureReasons.push(`JPG回退失败: ${jpgPath} - ${jpgError}`);
|
||||
}
|
||||
|
||||
// 尝试png格式
|
||||
const pngPath = texturePath.replace(/\.ktx2$/i, '.png');
|
||||
try {
|
||||
const pngTexture = new Texture(pngPath);
|
||||
if (pngTexture) {
|
||||
return pngTexture;
|
||||
}
|
||||
} catch (pngError: any) {
|
||||
failureReasons.push(`PNG回退失败: ${pngPath} - ${pngError}`);
|
||||
}
|
||||
}
|
||||
|
||||
// 所有格式都失败,记录详细失败信息
|
||||
this.failedTextures.push({
|
||||
path: texturePath,
|
||||
textureType: '回退机制',
|
||||
error: failureReasons.join('; '),
|
||||
timestamp: new Date()
|
||||
});
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 应用材质
|
||||
* @param target 目标对象
|
||||
* @param material 材质路径
|
||||
*/
|
||||
applyMaterial(target: string, attribute: string, value: number | string): void {
|
||||
// 这里需要根据实际的材质管理逻辑实现
|
||||
console.log(`Applying attribute ${attribute} to ${value}`);
|
||||
|
||||
// 示例实现:根据目标和材质路径应用材质
|
||||
// 1. 查找目标网格
|
||||
const targetMaterials: PBRMaterial[] = [];
|
||||
this.materialDic.Values().forEach(material => {
|
||||
if (material.name.includes(target)) {
|
||||
console.log(`${this.materialDic.Get(material.name)}`,material);
|
||||
targetMaterials.push(material);
|
||||
}
|
||||
});
|
||||
|
||||
if (targetMaterials.length === 0) {
|
||||
console.warn(`Target not found: ${target}`);
|
||||
return;
|
||||
}
|
||||
|
||||
// 2. 处理材质路径
|
||||
// 这里可以根据材质路径加载对应的材质配置
|
||||
// 例如:paint/blue 可以映射到特定的材质配置
|
||||
|
||||
// 3. 应用材质到目标网格
|
||||
targetMaterials.forEach(material => {
|
||||
if (material[attribute]) {
|
||||
material[attribute] = value;
|
||||
}
|
||||
console.log(`Applying attribute ${attribute} to ${value} to mesh: ${material.name}`);
|
||||
// 这里需要根据实际的材质系统实现
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -11,6 +11,7 @@ import { AppEnv } from './AppEnv';
|
||||
import { AppModel } from './AppModel';
|
||||
import { AppConfig } from './AppConfig';
|
||||
import { AppRay } from './AppRay';
|
||||
import { GameManager } from './GameManager';
|
||||
import { EventBridge } from '../event/bridge';
|
||||
|
||||
/**
|
||||
@ -25,6 +26,7 @@ export class MainApp {
|
||||
appLight: AppLight;
|
||||
appEnv: AppEnv;
|
||||
appRay: AppRay;
|
||||
gameManager: GameManager;
|
||||
|
||||
|
||||
constructor() {
|
||||
@ -35,6 +37,7 @@ export class MainApp {
|
||||
this.appLight = new AppLight(this);
|
||||
this.appEnv = new AppEnv(this);
|
||||
this.appRay = new AppRay(this);
|
||||
this.gameManager = new GameManager(this);
|
||||
|
||||
window.addEventListener("resize", () => this.appEngin.handleResize());
|
||||
}
|
||||
@ -44,13 +47,15 @@ export class MainApp {
|
||||
* @param config 配置对象
|
||||
*/
|
||||
loadAConfig(config: any): void {
|
||||
AppConfig.container = config.container ;
|
||||
AppConfig.container = config.container;
|
||||
AppConfig.modelUrlList = config.modelUrlList || [];
|
||||
AppConfig.env = config.env;
|
||||
}
|
||||
|
||||
loadModel(): void {
|
||||
this.appModel.loadModel();
|
||||
async loadModel(): Promise<void> {
|
||||
await this.appModel.loadModel();
|
||||
await this.gameManager.Awake();
|
||||
EventBridge.allReady({ scene: this.appScene.object });
|
||||
}
|
||||
|
||||
/** 唤醒/初始化所有子模块 */
|
||||
|
||||
@ -31,6 +31,9 @@ export class EventBridge {
|
||||
static sceneReady(payload: SceneReadyPayload): Emitter {
|
||||
return emit("scene:ready", payload);
|
||||
}
|
||||
static allReady(payload: SceneReadyPayload): Emitter {
|
||||
return emit("all:ready", payload);
|
||||
}
|
||||
|
||||
// Listeners
|
||||
static onModelLoadProgress(callback: (payload: ModelLoadProgressPayload) => void, context?: unknown): Emitter {
|
||||
@ -52,7 +55,9 @@ export class EventBridge {
|
||||
static onSceneReady(callback: (payload: SceneReadyPayload) => void, context?: unknown): Emitter {
|
||||
return on("scene:ready", callback, context);
|
||||
}
|
||||
|
||||
static onAllReady(callback: (payload: SceneReadyPayload) => void, context?: unknown): Emitter {
|
||||
return on("all:ready", callback, context);
|
||||
}
|
||||
static onceSceneReady(callback: (payload: SceneReadyPayload) => void, context?: unknown): Emitter {
|
||||
return once("scene:ready", callback, context);
|
||||
}
|
||||
|
||||
45
src/kernel/Adapter.ts
Normal file
45
src/kernel/Adapter.ts
Normal file
@ -0,0 +1,45 @@
|
||||
import { MainApp } from '../babylonjs/MainApp';
|
||||
|
||||
/**
|
||||
* Kernel 转接器类 - 封装 mainApp 的功能,提供统一的 API 接口
|
||||
*/
|
||||
export class KernelAdapter {
|
||||
private mainApp: MainApp;
|
||||
|
||||
constructor(mainApp: MainApp) {
|
||||
this.mainApp = mainApp;
|
||||
}
|
||||
|
||||
/** 模型管理 */
|
||||
model = {
|
||||
/**
|
||||
* 销毁指定模型
|
||||
* @param modelName 模型名称
|
||||
*/
|
||||
destroy: (modelName: string): void => {
|
||||
this.mainApp.appModel.destroyModel(modelName);
|
||||
}
|
||||
};
|
||||
|
||||
/** 材质管理 */
|
||||
material = {
|
||||
/**
|
||||
* 应用材质
|
||||
* @param options 材质应用选项
|
||||
*/
|
||||
apply: (options: { target: string; attribute: string,value:number|string }): void => {
|
||||
this.mainApp.gameManager.applyMaterial(options.target, options.attribute,options.value);
|
||||
}
|
||||
};
|
||||
|
||||
/** 热点管理 */
|
||||
hotspot = {
|
||||
/**
|
||||
* 渲染热点
|
||||
* @param hotspots 热点数据
|
||||
*/
|
||||
render: (hotspots: any[]): void => {
|
||||
this.mainApp.appRay.renderHotspots(hotspots);
|
||||
}
|
||||
};
|
||||
}
|
||||
@ -4,6 +4,7 @@ import configurator, { ConfiguratorParams } from './components/conf';
|
||||
import auth from './components/auth';
|
||||
import { on, off, once, emit } from './event/bus';
|
||||
import { EventBridge } from './event/bridge';
|
||||
import { KernelAdapter } from './kernel/Adapter';
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
@ -25,6 +26,7 @@ type InitParams = {
|
||||
};
|
||||
|
||||
let mainApp: MainApp | null = null;
|
||||
let kernelAdapter: KernelAdapter | null = null;
|
||||
|
||||
const kernel = {
|
||||
// 事件工具,提供给外部订阅/退订
|
||||
@ -37,6 +39,10 @@ const kernel = {
|
||||
if (!params) { console.error('params is required'); return; }
|
||||
|
||||
mainApp = new MainApp();
|
||||
kernelAdapter = new KernelAdapter(mainApp);
|
||||
|
||||
// 展开转接器的属性和方法到kernel对象
|
||||
Object.assign(kernel, kernelAdapter);
|
||||
|
||||
const container = (typeof params.container === 'string'
|
||||
? (document.querySelector(params.container) || document.getElementById(params.container))
|
||||
@ -52,7 +58,7 @@ const kernel = {
|
||||
|
||||
await mainApp.Awake();
|
||||
await mainApp.loadModel();
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
if (!window.faceSDK) {
|
||||
|
||||
Reference in New Issue
Block a user