加入剖切

This commit is contained in:
2026-03-12 21:50:07 +08:00
parent 7fdbf19951
commit 248226e553
5 changed files with 272 additions and 13 deletions

View File

@ -0,0 +1,7 @@
{
"permissions": {
"allow": [
"Bash(cd /d/VscodeProject/zhengte.babylonjs-sdk && npm run build 2>&1 | head -50)"
]
}
}

View File

@ -4,8 +4,7 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>3D模型展示SDK - TS</title>
<style>
<title>3D Model Showcase SDK - TS</title><style>
* {
margin: 0;
padding: 0;
@ -44,7 +43,7 @@
const config = {
container: document.querySelector('#renderDom'),
modelUrlList: ['https://sdk.zguiy.com/resurces/model/model.glb'],
modelUrlList: ['https://sdk.zguiy.com/resurces/model/model_new.glb'],
env: { envPath: 'https://sdk.zguiy.com/resurces/hdr/hdr.env', intensity: 1.2, rotationY: 0.3, background: false },
};
@ -67,7 +66,7 @@
});
kernel.on('all:ready', (data) => {
console.log('所有模块加载完', data);
console.log('所有模块加载完,', data);
kernel.material.apply({
target: 'Material__2',
attribute: 'alpha',
@ -80,6 +79,22 @@
kernel.on('model:click', (data) => {
console.log('模型点击事件', data);
// 切换卷帘门开关
kernel.door.toggle({ speed: 1.2 });
// Y轴剖切调整这个高度值来找到合适的剖切位置
const clipHeight = 30; // 增加高度原来是3.5
console.log('设置剖切:', clipHeight);
kernel.clipping.setY(clipHeight, true);
// 验证剖切是否生效
setTimeout(() => {
const scene = kernel.mainApp?.appScene?.object;
console.log('Scene:', scene);
console.log('Scene clipPlane:', scene?.clipPlane);
console.log('Scene meshes count:', scene?.meshes?.length);
}, 100);
});
@ -87,4 +102,7 @@
</script>
</body>
</html>
</html>

View File

@ -29,8 +29,8 @@ export class AppCamera extends Monobehiver {
this.object.panningSensibility = 0;
// 限制垂直角范围,实现上帝视角
this.object.upperBetaLimit = Tools.ToRadians(60); // 最大垂直角接近90度避免万向锁
this.object.lowerBetaLimit = Tools.ToRadians(60); // 最小垂直角
// 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);

View File

@ -1,8 +1,21 @@
import { Mesh, PBRMaterial, Texture, AbstractMesh } from "@babylonjs/core";
import { Mesh, PBRMaterial, Texture, AbstractMesh, Plane, Vector3, Scene, Color3 } from "@babylonjs/core";
import { Observer } from "@babylonjs/core/Misc/observable";
import { Nullable } from "@babylonjs/core/types";
import { Monobehiver } from '../base/Monobehiver';
import { Dictionary } from '../utils/Dictionary';
import { AppConfig } from './AppConfig';
type RollerDoorOptions = {
/** 目标升起高度,缺省为初始 y + 3 */
upY?: number;
/** 落下终点,缺省为初始 y */
downY?: number;
/** 运动速度(单位/秒),缺省 1 */
speed?: number;
/** 自定义门体网格名列表,不传使用默认两个卷帘门 */
meshNames?: string[];
};
/**
* 游戏管理器类 - 负责管理游戏逻辑、材质和纹理
*/
@ -10,6 +23,14 @@ export class GameManager extends Monobehiver {
private materialDic: Dictionary<PBRMaterial>;
private meshDic: Dictionary<any>;
private oldTextureDic: Dictionary<any>;
private rollerDoorMeshes: AbstractMesh[];
private rollerDoorInitialY: Map<string, number>;
private rollerDoorObserver: Nullable<Observer<Scene>>;
private rollerDoorIsOpen: boolean;
private rollerDoorNames: string[];
private yClipPlane: Plane | null;
private yClipTargets: string[] | null;
private clipPlaneVisualization: Mesh | null;
// 记录加载失败的贴图
private failedTextures: Array<{
@ -25,9 +46,22 @@ export class GameManager extends Monobehiver {
this.materialDic = new Dictionary<PBRMaterial>();
this.meshDic = new Dictionary<any>();
this.oldTextureDic = new Dictionary<any>();
this.rollerDoorMeshes = [];
this.rollerDoorInitialY = new Map();
this.rollerDoorObserver = null;
this.rollerDoorIsOpen = false;
this.rollerDoorNames = ["Box006.001", "Box005.001"];
this.yClipPlane = null;
this.yClipTargets = null;
this.clipPlaneVisualization = null;
this.failedTextures = [];
}
/** 调试:返回当前场景中所有网格名称 */
listMeshNames(): string[] {
return this.meshDic.Keys();
}
/** 初始化游戏管理器 */
async Awake() {
const scene = this.mainApp.appScene?.object;
@ -52,6 +86,7 @@ export class GameManager extends Monobehiver {
this.meshDic.Set(mesh.name, mesh);
}
}
this.cacheRollerDoorMeshes();
console.log('材质字典:', this.materialDic);
}
@ -331,6 +366,165 @@ export class GameManager extends Monobehiver {
}
}
/** 卷帘门开合:再次调用会反向动作 */
toggleRollerDoor(options?: RollerDoorOptions): void {
this.setRollerDoorState(!this.rollerDoorIsOpen, options);
}
/** 直接设置卷帘门状态 */
setRollerDoorState(open: boolean, options?: RollerDoorOptions): void {
const scene = this.mainApp.appScene?.object;
if (!scene) {
console.warn('Scene not found for roller door');
return;
}
this.cacheRollerDoorMeshes(options?.meshNames);
if (!this.rollerDoorMeshes.length) {
console.warn('Roller door meshes not found');
return;
}
const speed = Math.max(options?.speed ?? 1, 0.01);
const targets = this.rollerDoorMeshes.map(mesh => {
const baseY = this.rollerDoorInitialY.get(mesh.name) ?? mesh.position.y;
const targetY = open ? (options?.upY ?? baseY + 3) : (options?.downY ?? baseY);
const direction = targetY >= mesh.position.y ? 1 : -1;
return { mesh, targetY, direction };
});
const alreadyAtTarget = targets.every(({ mesh, targetY }) => Math.abs(mesh.position.y - targetY) < 0.001);
if (alreadyAtTarget) {
this.rollerDoorIsOpen = open;
return;
}
this.rollerDoorIsOpen = open;
this.stopRollerDoorAnimation();
this.rollerDoorObserver = scene.onBeforeRenderObservable.add(() => {
const dt = scene.getEngine().getDeltaTime() / 1000;
const step = speed * dt;
let finished = true;
for (const item of targets) {
const current = item.mesh.position.y;
const dir = item.direction;
let next = current + dir * step;
if ((dir > 0 && next >= item.targetY) || (dir < 0 && next <= item.targetY)) {
next = item.targetY;
} else {
finished = false;
}
item.mesh.position.y = next;
}
if (finished) {
this.stopRollerDoorAnimation();
this.rollerDoorIsOpen = open;
}
});
}
/** 当前卷帘门是否开启 */
isRollerDoorOpen(): boolean {
return this.rollerDoorIsOpen;
}
/**
* 设置基于 Y 轴的剖切平面keepAbove=true 时保留平面以上部分
* 使用场景级别的 clipPlane作用于所有网格
*/
setYAxisClip(
height: number,
keepAbove = true,
onlyMeshNames?: string[],
excludeMeshNames?: string[]
): void {
const scene = this.mainApp.appScene?.object;
if (!scene) {
console.warn('Scene not found for clipping');
return;
}
const normal = new Vector3(0, keepAbove ? 1 : -1, 0);
this.yClipPlane = Plane.FromPositionAndNormal(new Vector3(0, height, 0), normal);
// 直接设置场景的剖切平面,作用于所有网格
scene.clipPlane = this.yClipPlane;
// 创建可视化平面
this.createClipPlaneVisualization(height, scene);
console.log('[clipping] Scene clipPlane set:', { height, keepAbove, normal: normal.asArray() });
}
/** 关闭 Y 轴剖切 */
clearYAxisClip(): void {
const scene = this.mainApp.appScene?.object;
if (scene) {
scene.clipPlane = null;
}
this.yClipPlane = null;
this.yClipTargets = null;
// 移除可视化平面
if (this.clipPlaneVisualization) {
this.clipPlaneVisualization.dispose();
this.clipPlaneVisualization = null;
}
}
private cacheRollerDoorMeshes(customNames?: string[]): void {
const names = customNames?.length ? customNames : this.rollerDoorNames;
this.rollerDoorMeshes = [];
for (const name of names) {
const mesh = this.meshDic.Get(name);
if (mesh) {
this.rollerDoorMeshes.push(mesh);
if (!this.rollerDoorInitialY.has(name)) {
this.rollerDoorInitialY.set(name, mesh.position.y);
}
} else {
console.warn(`Roller door mesh not found: ${name}`);
}
}
}
private stopRollerDoorAnimation(): void {
const scene = this.mainApp.appScene?.object;
if (scene && this.rollerDoorObserver) {
scene.onBeforeRenderObservable.remove(this.rollerDoorObserver);
}
this.rollerDoorObserver = null;
}
/** 创建剖切平面的可视化 */
private createClipPlaneVisualization(height: number, scene: Scene): void {
// 移除旧的可视化平面
if (this.clipPlaneVisualization) {
this.clipPlaneVisualization.dispose();
}
// 创建一个半透明的平面网格
const plane = Mesh.CreatePlane("clipPlaneVisualization", 20, scene);
plane.position.y = height;
plane.rotation.x = Math.PI / 2; // 旋转使其水平
// 创建半透明红色材质
const mat = new PBRMaterial("clipPlaneMat", scene);
mat.albedoColor = new Color3(1, 0, 0); // 红色
mat.alpha = 0.3; // 半透明
mat.backFaceCulling = false; // 双面显示
plane.material = mat;
this.clipPlaneVisualization = plane;
console.log('[clipping] Visualization plane created at height:', height);
}
/** 获取公共URL */
private getPublicUrl(): string {
// 尝试从环境变量获取
@ -343,6 +537,12 @@ export class GameManager extends Monobehiver {
/** 清理资源 */
dispose() {
this.stopRollerDoorAnimation();
this.clearYAxisClip();
this.rollerDoorMeshes = [];
this.rollerDoorInitialY.clear();
this.rollerDoorIsOpen = false;
// 清理所有材质资源
this.materialDic.Values().forEach((material) => {
if (material && material.dispose) {

View File

@ -1,7 +1,7 @@
import { MainApp } from '../babylonjs/MainApp';
import { MainApp } from '../babylonjs/MainApp';
/**
* Kernel 转接器类 - 封装 mainApp 的功能,提供统一API 接口
* Kernel 转接器类 - 封装 mainApp 的功能,提供统一<EFBFBD>?API 接口
*/
export class KernelAdapter {
private mainApp: MainApp;
@ -13,8 +13,7 @@ export class KernelAdapter {
/** 模型管理 */
model = {
/**
* 销毁指定模
* @param modelName 模型名称
* 销毁指定模<EFBFBD>? * @param modelName 模型名称
*/
destroy: (modelName: string): void => {
this.mainApp.appModel.destroyModel(modelName);
@ -32,6 +31,34 @@ export class KernelAdapter {
}
};
/** 卷帘门控<E997A8>?*/
door = {
/** 再次调用会自动反向动<E59091>?*/
toggle: (options?: { upY?: number; downY?: number; speed?: number; meshNames?: string[] }): void => {
this.mainApp.gameManager.toggleRollerDoor(options);
},
/** 显式设置开/<2F>?*/
setState: (open: boolean, options?: { upY?: number; downY?: number; speed?: number; meshNames?: string[] }): void => {
this.mainApp.gameManager.setRollerDoorState(open, options);
},
/** 当前是否已开<E5B7B2>?*/
isOpen: (): boolean => {
return this.mainApp.gameManager.isRollerDoorOpen();
}
};
/** Y 轴剖<E8BDB4>?*/
clipping = {
/** <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>и߶ȣ<DFB6>keepAbove=true ʱ<><CAB1><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ϲ<EFBFBD><CFB2>֣<EFBFBD>onlyMeshNames Ϊ<><CEAA>Ĭ<EFBFBD>Ͻ<EFBFBD><CFBD><EFBFBD><EFBFBD>þ<EFBFBD><C3BE><EFBFBD><EFBFBD>ţ<EFBFBD>excludeMeshNames <20><><EFBFBD><EFBFBD><EFBFBD>ų<EFBFBD><C5B3><EFBFBD><EFBFBD><EFBFBD><EFBFBD>е<EFBFBD><D0B5><EFBFBD><EFBFBD><EFBFBD> */
setY: (height: number, keepAbove = true, onlyMeshNames?: string[], excludeMeshNames?: string[]): void => {
this.mainApp.gameManager.setYAxisClip(height, keepAbove, onlyMeshNames, excludeMeshNames);
},
/** 关闭剖切 */
clear: (): void => {
this.mainApp.gameManager.clearYAxisClip();
}
};
/** 热点管理 */
hotspot = {
/**
@ -42,4 +69,11 @@ export class KernelAdapter {
this.mainApp.appRay.renderHotspots(hotspots);
}
};
}
/** 调试工具 */
debug = {
/** 列出当前场景网格名称 */
listMeshNames: (): string[] => {
return this.mainApp.gameManager.listMeshNames();
}
};
}