1
This commit is contained in:
17
index.html
17
index.html
@ -45,7 +45,7 @@
|
|||||||
const config = {
|
const config = {
|
||||||
container: document.querySelector('#renderDom'),
|
container: document.querySelector('#renderDom'),
|
||||||
modelUrlList: ['https://sdk.zguiy.com/resurces/model/model.glb'],
|
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);
|
kernel.init(config);
|
||||||
@ -60,6 +60,20 @@
|
|||||||
|
|
||||||
kernel.on('model:loaded', (data) => {
|
kernel.on('model:loaded', (data) => {
|
||||||
console.log('模型加载完成', 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>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
|
|||||||
@ -21,13 +21,18 @@ export class AppCamera extends Monobehiver {
|
|||||||
const canvas = AppConfig.container;
|
const canvas = AppConfig.container;
|
||||||
if (!scene || !canvas) return;
|
if (!scene || !canvas) return;
|
||||||
|
|
||||||
// 创建弧形旋转相机:水平角70度,垂直角80度,距离5,目标点(0,1,0)
|
// 创建弧形旋转相机:水平角70度,垂直角85度(接近上帝视角),距离5,目标点(0,2,0)
|
||||||
this.object = new ArcRotateCamera('Camera', Tools.ToRadians(70), Tools.ToRadians(80), 5, new Vector3(0, 0, 0), scene);
|
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.attachControl(canvas, true);
|
||||||
this.object.minZ = 0.01; // 近裁剪面
|
this.object.minZ = 0.01; // 近裁剪面
|
||||||
this.object.wheelPrecision =999999; // 滚轮缩放精度
|
this.object.wheelPrecision =999999; // 滚轮缩放精度
|
||||||
this.object.panningSensibility = 0;
|
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);
|
this.setTarget(0, 2, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -43,8 +48,11 @@ export class AppCamera extends Monobehiver {
|
|||||||
/** 重置相机到默认位置 */
|
/** 重置相机到默认位置 */
|
||||||
reset(): void {
|
reset(): void {
|
||||||
if (!this.object) return;
|
if (!this.object) return;
|
||||||
this.object.radius = 2; this.setTarget(0, 0, 0);
|
this.object.radius = 5;
|
||||||
this.object.position = new Vector3(0, 1.5, 2);
|
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 {
|
update(): void {
|
||||||
|
|||||||
@ -149,4 +149,29 @@ export class AppModel extends Monobehiver {
|
|||||||
this.skeletonMerged = false;
|
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)
|
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 }
|
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 { AppModel } from './AppModel';
|
||||||
import { AppConfig } from './AppConfig';
|
import { AppConfig } from './AppConfig';
|
||||||
import { AppRay } from './AppRay';
|
import { AppRay } from './AppRay';
|
||||||
|
import { GameManager } from './GameManager';
|
||||||
import { EventBridge } from '../event/bridge';
|
import { EventBridge } from '../event/bridge';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -25,6 +26,7 @@ export class MainApp {
|
|||||||
appLight: AppLight;
|
appLight: AppLight;
|
||||||
appEnv: AppEnv;
|
appEnv: AppEnv;
|
||||||
appRay: AppRay;
|
appRay: AppRay;
|
||||||
|
gameManager: GameManager;
|
||||||
|
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
@ -35,6 +37,7 @@ export class MainApp {
|
|||||||
this.appLight = new AppLight(this);
|
this.appLight = new AppLight(this);
|
||||||
this.appEnv = new AppEnv(this);
|
this.appEnv = new AppEnv(this);
|
||||||
this.appRay = new AppRay(this);
|
this.appRay = new AppRay(this);
|
||||||
|
this.gameManager = new GameManager(this);
|
||||||
|
|
||||||
window.addEventListener("resize", () => this.appEngin.handleResize());
|
window.addEventListener("resize", () => this.appEngin.handleResize());
|
||||||
}
|
}
|
||||||
@ -44,13 +47,15 @@ export class MainApp {
|
|||||||
* @param config 配置对象
|
* @param config 配置对象
|
||||||
*/
|
*/
|
||||||
loadAConfig(config: any): void {
|
loadAConfig(config: any): void {
|
||||||
AppConfig.container = config.container ;
|
AppConfig.container = config.container;
|
||||||
AppConfig.modelUrlList = config.modelUrlList || [];
|
AppConfig.modelUrlList = config.modelUrlList || [];
|
||||||
AppConfig.env = config.env;
|
AppConfig.env = config.env;
|
||||||
}
|
}
|
||||||
|
|
||||||
loadModel(): void {
|
async loadModel(): Promise<void> {
|
||||||
this.appModel.loadModel();
|
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 {
|
static sceneReady(payload: SceneReadyPayload): Emitter {
|
||||||
return emit("scene:ready", payload);
|
return emit("scene:ready", payload);
|
||||||
}
|
}
|
||||||
|
static allReady(payload: SceneReadyPayload): Emitter {
|
||||||
|
return emit("all:ready", payload);
|
||||||
|
}
|
||||||
|
|
||||||
// Listeners
|
// Listeners
|
||||||
static onModelLoadProgress(callback: (payload: ModelLoadProgressPayload) => void, context?: unknown): Emitter {
|
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 {
|
static onSceneReady(callback: (payload: SceneReadyPayload) => void, context?: unknown): Emitter {
|
||||||
return on("scene:ready", callback, context);
|
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 {
|
static onceSceneReady(callback: (payload: SceneReadyPayload) => void, context?: unknown): Emitter {
|
||||||
return once("scene:ready", callback, context);
|
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 auth from './components/auth';
|
||||||
import { on, off, once, emit } from './event/bus';
|
import { on, off, once, emit } from './event/bus';
|
||||||
import { EventBridge } from './event/bridge';
|
import { EventBridge } from './event/bridge';
|
||||||
|
import { KernelAdapter } from './kernel/Adapter';
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
interface Window {
|
interface Window {
|
||||||
@ -25,6 +26,7 @@ type InitParams = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let mainApp: MainApp | null = null;
|
let mainApp: MainApp | null = null;
|
||||||
|
let kernelAdapter: KernelAdapter | null = null;
|
||||||
|
|
||||||
const kernel = {
|
const kernel = {
|
||||||
// 事件工具,提供给外部订阅/退订
|
// 事件工具,提供给外部订阅/退订
|
||||||
@ -37,6 +39,10 @@ const kernel = {
|
|||||||
if (!params) { console.error('params is required'); return; }
|
if (!params) { console.error('params is required'); return; }
|
||||||
|
|
||||||
mainApp = new MainApp();
|
mainApp = new MainApp();
|
||||||
|
kernelAdapter = new KernelAdapter(mainApp);
|
||||||
|
|
||||||
|
// 展开转接器的属性和方法到kernel对象
|
||||||
|
Object.assign(kernel, kernelAdapter);
|
||||||
|
|
||||||
const container = (typeof params.container === 'string'
|
const container = (typeof params.container === 'string'
|
||||||
? (document.querySelector(params.container) || document.getElementById(params.container))
|
? (document.querySelector(params.container) || document.getElementById(params.container))
|
||||||
@ -52,7 +58,7 @@ const kernel = {
|
|||||||
|
|
||||||
await mainApp.Awake();
|
await mainApp.Awake();
|
||||||
await mainApp.loadModel();
|
await mainApp.loadModel();
|
||||||
},
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!window.faceSDK) {
|
if (!window.faceSDK) {
|
||||||
|
|||||||
Reference in New Issue
Block a user