新增场景释放机制和Dispose方法
This commit is contained in:
parent
4a648c2f97
commit
efb220612e
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "material-editor",
|
||||
"private": true,
|
||||
"version": "1.0.2",
|
||||
"version": "1.0.3",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
|
@ -29,8 +29,6 @@ async function textureRenderUpdate(textureRecord:TextureTableRecord){
|
||||
*/
|
||||
export class MaterialEditor extends Singleton
|
||||
{
|
||||
private _pointerLocked = false;
|
||||
|
||||
Geometrys: Map<string, Geometry | BufferGeometry>;
|
||||
|
||||
CurGeometryName = "球";
|
||||
@ -38,6 +36,7 @@ export class MaterialEditor extends Singleton
|
||||
ShowObject: Object3D;
|
||||
ShowMesh: Mesh;
|
||||
Viewer: Viewer;
|
||||
CameraControl?: MaterialEditorCameraControl;
|
||||
|
||||
private _MeshMaterial: MeshPhysicalMaterial = new MeshPhysicalMaterial({});
|
||||
//构造
|
||||
@ -72,7 +71,7 @@ export class MaterialEditor extends Singleton
|
||||
// this.Viewer.PreViewer.Cursor.CursorObject.visible = false;
|
||||
// this.Viewer.UsePass = false;
|
||||
this.initScene();
|
||||
new MaterialEditorCameraControl(this.Viewer, this.ShowObject.position);
|
||||
this.CameraControl = new MaterialEditorCameraControl(this.Viewer, this.ShowObject.position);
|
||||
this.Viewer.ZoomAll();
|
||||
|
||||
// 初始化相机位置到观察物体的正后方
|
||||
@ -82,6 +81,9 @@ export class MaterialEditor extends Singleton
|
||||
this.Viewer.UpdateRender();
|
||||
this.Viewer.Fov = 90;
|
||||
}
|
||||
else {
|
||||
console.warn("Viewer has already been initialized");
|
||||
}
|
||||
}
|
||||
SetViewer(canvas: HTMLElement)
|
||||
{
|
||||
@ -186,8 +188,24 @@ export class MaterialEditor extends Singleton
|
||||
this.Update();
|
||||
}
|
||||
|
||||
dispose()
|
||||
Dispose()
|
||||
{
|
||||
this.Geometrys.clear();
|
||||
this.Geometrys = undefined;
|
||||
this.Canvas = undefined;
|
||||
// object在Viewer中进行释放
|
||||
this.ShowObject = undefined;
|
||||
this.ShowMesh = undefined;
|
||||
|
||||
this.CameraControl?.dispose();
|
||||
this.Viewer?.Dispose();
|
||||
this.CameraControl = undefined;
|
||||
this.Viewer = undefined;
|
||||
this.exrPromise = undefined;
|
||||
this.exrTexture?.dispose();
|
||||
this.exrTexture = undefined;
|
||||
this.metaTexture?.dispose();
|
||||
this.metaTexture = undefined;
|
||||
}
|
||||
|
||||
async Update()
|
||||
|
@ -24,12 +24,24 @@ export class MaterialEditorCameraControl {
|
||||
|
||||
initMouseControl() {
|
||||
let el = this.Viewer.Renderer.domElement;
|
||||
el.addEventListener("pointerdown", (e) => { this.pointId = e.pointerId; }, false);
|
||||
el.addEventListener("pointerdown", this.onPointerDown, false);
|
||||
el.addEventListener("mousedown", this.onMouseDown, false);
|
||||
el.addEventListener("mousemove", this.onMouseMove, false);
|
||||
el.addEventListener("mouseup", this.onMouseUp, false);
|
||||
el.addEventListener('wheel', this.onMouseWheel, false);
|
||||
}
|
||||
|
||||
dispose() {
|
||||
const el = this.Viewer.Renderer.domElement;
|
||||
el.removeEventListener("pointerdown", this.onPointerDown);
|
||||
el.removeEventListener("mousedown", this.onMouseDown);
|
||||
el.removeEventListener("mousemove", this.onMouseMove);
|
||||
el.removeEventListener("mouseup", this.onMouseUp);
|
||||
el.removeEventListener('wheel', this.onMouseWheel, false);
|
||||
}
|
||||
|
||||
onPointerDown = (event: PointerEvent) => { this.pointId = event.pointerId; };
|
||||
|
||||
onMouseDown = (event: MouseEvent) => {
|
||||
this.requestPointerLock();
|
||||
this._MouseIsDown = true;
|
||||
@ -49,14 +61,11 @@ export class MaterialEditorCameraControl {
|
||||
else this.Viewer.Rotate(this._movement);
|
||||
}
|
||||
};
|
||||
onMouseWheel = (event: WheelEvent) =>
|
||||
{
|
||||
if (event.deltaY < 0)
|
||||
{
|
||||
onMouseWheel = (event: WheelEvent) => {
|
||||
if (event.deltaY < 0) {
|
||||
this.Viewer.Zoom(0.6);
|
||||
}
|
||||
else if (event.deltaY > 0)
|
||||
{
|
||||
else if (event.deltaY > 0) {
|
||||
this.Viewer.Zoom(1.4);
|
||||
}
|
||||
};
|
||||
|
@ -2,13 +2,11 @@
|
||||
|
||||
let instanceMap = new Map();
|
||||
|
||||
export interface PrototypeType<T> extends Function
|
||||
{
|
||||
export interface PrototypeType<T> extends Function {
|
||||
prototype: T;
|
||||
}
|
||||
|
||||
export interface ConstructorFunctionType<T = any> extends PrototypeType<T>
|
||||
{
|
||||
export interface ConstructorFunctionType<T = any> extends PrototypeType<T> {
|
||||
new(...args: any[]): T;
|
||||
}
|
||||
|
||||
@ -23,13 +21,11 @@ export type ConstructorType<T = unknown, Static extends Record<string, any> = Pr
|
||||
* //获得单例
|
||||
* let a = A.GetInstance();
|
||||
*/
|
||||
export class Singleton
|
||||
{
|
||||
export class Singleton {
|
||||
protected constructor() { }
|
||||
|
||||
//ref:https://github.com/Microsoft/TypeScript/issues/5863
|
||||
static GetInstance<T extends Singleton>(this: ConstructorType<T, typeof Singleton>): T
|
||||
{
|
||||
static GetInstance<T extends Singleton>(this: ConstructorType<T, typeof Singleton>): T {
|
||||
if (instanceMap.has(this))
|
||||
return instanceMap.get(this);
|
||||
//@ts-ignore
|
||||
@ -37,4 +33,23 @@ export class Singleton
|
||||
instanceMap.set(this, __instance__);
|
||||
return __instance__;
|
||||
}
|
||||
|
||||
/**
|
||||
* 释放指定类型的单例实例。
|
||||
* @returns {boolean} 如果实例存在并被释放则返回 true,否则返回 false。
|
||||
*/
|
||||
static ReleaseInstance<T extends Singleton>(this: ConstructorType<T, typeof Singleton>): boolean {
|
||||
if (instanceMap.has(this)) {
|
||||
const instance = instanceMap.get(this);
|
||||
// 如果实例有清理方法,可以在这里调用
|
||||
if (typeof (instance as any).dispose === 'function') {
|
||||
(instance as any).dispose();
|
||||
}
|
||||
instanceMap.delete(this);
|
||||
console.log(`Singleton instance of ${this.name} has been released.`);
|
||||
return true;
|
||||
}
|
||||
console.log(`No singleton instance of ${this.name} found to release.`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -1,20 +1,25 @@
|
||||
import { Raycaster, Scene, Vector3, WebGLRenderer, type Vector, type WebGLRendererParameters } from "three";
|
||||
import { Raycaster, Scene, Vector3, WebGLRenderer, type WebGLRendererParameters, Object3D, Mesh, Line, Points, Material, Texture } from "three";
|
||||
import { cZeroVec, GetBox, GetBoxArr } from "./GeUtils";
|
||||
import { PlaneExt } from "./PlaneExt";
|
||||
import { CameraType, CameraUpdate } from "webcad_ue4_api";
|
||||
import { disposeNode } from "../helpers/helper.three";
|
||||
|
||||
|
||||
export class Viewer {
|
||||
m_LookTarget: any;
|
||||
m_Camera: CameraUpdate = new CameraUpdate();
|
||||
protected NeedUpdate: boolean = true;
|
||||
Renderer: WebGLRenderer;//渲染器 //暂时只用这个类型
|
||||
m_DomEl: HTMLElement; //画布容器
|
||||
Renderer!: WebGLRenderer; // 在 initRender 中初始化,使用 ! 断言
|
||||
m_DomEl: HTMLElement;
|
||||
|
||||
_Height: number = 0;
|
||||
_Width: number = 0;
|
||||
|
||||
_Scene: Scene = new Scene();
|
||||
|
||||
private m_IsDisposed: boolean = false; // 添加一个标志来停止渲染循环和防止重复释放
|
||||
private m_AnimationFrameId: number | null = null; // 用于取消动画帧
|
||||
|
||||
get Scene() {
|
||||
return this._Scene;
|
||||
}
|
||||
@ -28,90 +33,95 @@ export class Viewer {
|
||||
this.NeedUpdate = true;
|
||||
}
|
||||
|
||||
UpdateRender()
|
||||
{
|
||||
UpdateRender() {
|
||||
this.NeedUpdate = true;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {HTMLElement} canvasContainer 可以传入一个div或者一个画布
|
||||
* @memberof Viewer
|
||||
*/
|
||||
constructor(canvasContainer: HTMLElement) {
|
||||
|
||||
this.m_DomEl = canvasContainer;
|
||||
this.initRender(canvasContainer);
|
||||
this.OnSize();
|
||||
this.StartRender();
|
||||
window.addEventListener("resize", () => {
|
||||
this.OnSize();
|
||||
});
|
||||
this.SetSize(); // 初始尺寸设置
|
||||
this.StartRender(); // 开始渲染循环
|
||||
|
||||
// 注意: OnSize 是一个箭头函数属性,它的 this 上下文是固定的
|
||||
// 因此可以直接用于添加和移除事件监听器
|
||||
window.addEventListener("resize", this.OnResize);
|
||||
this.InitCamera();
|
||||
}
|
||||
|
||||
//初始化render
|
||||
initRender(canvasContainer: HTMLElement) {
|
||||
let params: WebGLRendererParameters = {
|
||||
antialias: true,//antialias:true/false是否开启反锯齿
|
||||
precision: "highp",//precision:highp/mediump/lowp着色精度选择
|
||||
alpha: true//alpha:true/false是否可以设置背景色透明
|
||||
antialias: true,
|
||||
precision: "highp",
|
||||
alpha: true
|
||||
};
|
||||
this.Renderer = new WebGLRenderer(params);
|
||||
//加到画布
|
||||
canvasContainer.appendChild(this.Renderer.domElement);
|
||||
|
||||
this.Renderer.autoClear = true;
|
||||
|
||||
//如果设置,那么它希望所有的纹理和颜色都是预乘的伽玛。默认值为false。
|
||||
// this.Renderer.gammaInput = true;
|
||||
// this.Renderer.gammaOutput = true;
|
||||
// this.Renderer.shadowMap.enabled = true;
|
||||
// this.Renderer.toneMapping = ReinhardToneMapping;
|
||||
//设置设备像素比。 这通常用于HiDPI设备,以防止模糊输出画布。
|
||||
this.Renderer.setPixelRatio(window.devicePixelRatio);
|
||||
this.Renderer.physicallyCorrectLights = true;
|
||||
//this.Renderer.toneMappingExposure = Math.pow(1, 5.0); // to allow for very bright scenes.
|
||||
|
||||
//设置它的背景色为黑色
|
||||
this.Renderer.physicallyCorrectLights = true; // r150 后默认 false, 根据需要设置
|
||||
this.Renderer.setClearColor(0xffffff, 1);
|
||||
|
||||
|
||||
this.OnSize();
|
||||
// this.OnSize(); // 在构造函数中调用,这里可以不用重复
|
||||
}
|
||||
|
||||
InitCamera() {
|
||||
this.m_Camera.CameraType = CameraType.PerspectiveCamera;
|
||||
this.m_Camera.Near = 0.001;
|
||||
if (this.m_Camera.Camera) { // 确保 CameraUpdate 内部的 Camera 已初始化
|
||||
this.m_Camera.Camera.far = 1000000000;
|
||||
}
|
||||
OnSize = (width?: number, height?: number) => {
|
||||
}
|
||||
|
||||
OnResize = (evt: UIEvent) => this.SetSize();
|
||||
|
||||
SetSize = (width?: number, height?: number) => {
|
||||
if (this.m_IsDisposed || !this.m_DomEl) return; // 如果已销毁或容器不存在,则不执行
|
||||
|
||||
this._Width = width ? width : this.m_DomEl.clientWidth;
|
||||
this._Height = height ? height : this.m_DomEl.clientHeight;
|
||||
|
||||
//校验.成为2的倍数 避免外轮廓错误.
|
||||
if (this._Width % 2 == 1)
|
||||
this._Width -= 1;
|
||||
if (this._Height % 2 == 1)
|
||||
this._Height -= 1;
|
||||
if (this._Width % 2 === 1) this._Width -= 1;
|
||||
if (this._Height % 2 === 1) this._Height -= 1;
|
||||
|
||||
if (this.Renderer) {
|
||||
this.Renderer.setSize(this._Width, this._Height);
|
||||
}
|
||||
if (this.m_Camera) {
|
||||
this.m_Camera.SetSize(this._Width, this._Height);
|
||||
}
|
||||
this.NeedUpdate = true; // 尺寸变化后需要重新渲染
|
||||
};
|
||||
|
||||
StartRender = () => {
|
||||
requestAnimationFrame(this.StartRender);
|
||||
if (this._Scene != null && this.NeedUpdate) {
|
||||
if (this.m_IsDisposed) {
|
||||
if (this.m_AnimationFrameId !== null) {
|
||||
cancelAnimationFrame(this.m_AnimationFrameId);
|
||||
this.m_AnimationFrameId = null;
|
||||
}
|
||||
return;
|
||||
}
|
||||
this.m_AnimationFrameId = requestAnimationFrame(this.StartRender);
|
||||
|
||||
if (this._Scene && this.NeedUpdate) { // 确保 _Scene 存在
|
||||
this.Render();
|
||||
if (this.m_Camera) { // 确保 m_Camera 存在
|
||||
this.m_Camera.Update();
|
||||
}
|
||||
this.NeedUpdate = false;
|
||||
}
|
||||
};
|
||||
|
||||
Render() {
|
||||
if (this.m_IsDisposed || !this.Renderer || !this._Scene || !this.m_Camera || !this.m_Camera.Camera) {
|
||||
return;
|
||||
}
|
||||
this.Renderer.render(this._Scene, this.m_Camera.Camera);
|
||||
}
|
||||
|
||||
// ScreenToWorld, WorldToScreen, UpdateLockTarget, Rotate, Pan, Zoom, etc. methods remain the same...
|
||||
// ... (省略了您提供的其他方法,它们与 dispose 无直接关系,保持不变) ...
|
||||
ScreenToWorld(pt: Vector3, planVec?: Vector3) {
|
||||
if (this.m_IsDisposed || !this._Scene || !this.m_Camera || !this.m_Camera.Camera) return;
|
||||
//变换和求交点
|
||||
let plan = new PlaneExt(planVec || new Vector3(0, 0, 1));
|
||||
let raycaster = new Raycaster();
|
||||
@ -123,9 +133,10 @@ export class Viewer {
|
||||
}
|
||||
, this.m_Camera.Camera
|
||||
);
|
||||
plan.intersectRay(raycaster.ray, pt, true);
|
||||
plan.intersectRay(raycaster.ray, pt, true); // 假设 intersectRay 修改 pt
|
||||
}
|
||||
WorldToScreen(pt: Vector3) {
|
||||
if (this.m_IsDisposed || !this.m_Camera || !this.m_Camera.Camera) return;
|
||||
let widthHalf = this._Width * 0.5;
|
||||
let heightHalf = this._Height * 0.5;
|
||||
|
||||
@ -135,51 +146,133 @@ export class Viewer {
|
||||
pt.y = - (pt.y * heightHalf) + heightHalf;
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新视角观测目标(物体中心)
|
||||
*
|
||||
* @memberof Viewer
|
||||
*/
|
||||
UpdateLockTarget() {
|
||||
let renderList = this.Renderer.renderLists.get(this._Scene, this.m_Camera.Camera);
|
||||
let box = GetBoxArr(renderList.opaque.map(o => o.object));
|
||||
if (this.m_IsDisposed || !this.Renderer || !this._Scene || !this.m_Camera || !this.m_Camera.Camera) return;
|
||||
// @ts-ignore access to private renderLists
|
||||
const renderList = this.Renderer.renderLists.get(this._Scene, this.m_Camera.Camera);
|
||||
// @ts-ignore
|
||||
const box = GetBoxArr(renderList.opaque.map(o => o.object)); // 确保 GetBoxArr 定义正确
|
||||
if (box)
|
||||
// @ts-ignore
|
||||
this.m_LookTarget = box.getCenter(new Vector3());
|
||||
else
|
||||
this.m_LookTarget = cZeroVec;
|
||||
// @ts-ignore
|
||||
this.m_LookTarget = cZeroVec; // 确保 cZeroVec 定义正确
|
||||
}
|
||||
Rotate(mouseMove: Vector3) {
|
||||
if (this.m_IsDisposed || !this.m_Camera) return;
|
||||
this.m_Camera.Rotate(mouseMove, this.m_LookTarget);
|
||||
this.NeedUpdate = true;
|
||||
}
|
||||
RotateAround(movement: Vector3, center: Vector3) {
|
||||
if (this.m_IsDisposed || !this.m_Camera) return;
|
||||
this.m_Camera.Rotate(movement, center);
|
||||
this.NeedUpdate = true;
|
||||
}
|
||||
Pan(mouseMove: Vector3) {
|
||||
if (this.m_IsDisposed || !this.m_Camera) return;
|
||||
this.m_Camera.Pan(mouseMove);
|
||||
this.NeedUpdate = true;
|
||||
}
|
||||
Zoom(scale: number, center?: Vector3) {
|
||||
if (this.m_IsDisposed || !this.m_Camera) return;
|
||||
this.m_Camera.Zoom(scale, center);
|
||||
this.NeedUpdate = true;
|
||||
}
|
||||
ZoomAll()
|
||||
{
|
||||
this.m_Camera.ZoomExtentsBox3(GetBox(this._Scene, true));
|
||||
ZoomAll() {
|
||||
if (this.m_IsDisposed || !this.m_Camera || !this._Scene) return;
|
||||
// @ts-ignore
|
||||
this.m_Camera.ZoomExtentsBox3(GetBox(this._Scene, true)); // 确保 GetBox 定义正确
|
||||
this.NeedUpdate = true;
|
||||
}
|
||||
|
||||
ViewToTop() {
|
||||
if (this.m_IsDisposed || !this.m_Camera) return;
|
||||
this.m_Camera.LookAt(new Vector3(0, 0, -1));
|
||||
this.NeedUpdate = true;
|
||||
}
|
||||
ViewToFront() {
|
||||
if (this.m_IsDisposed || !this.m_Camera) return;
|
||||
this.m_Camera.LookAt(new Vector3(0, 1, 0));
|
||||
this.NeedUpdate = true;
|
||||
}
|
||||
ViewToSwiso() {
|
||||
if (this.m_IsDisposed || !this.m_Camera) return;
|
||||
this.m_Camera.LookAt(new Vector3(1, 1, -1));
|
||||
this.NeedUpdate = true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 销毁 Viewer 实例并释放所有相关资源。
|
||||
*/
|
||||
public Dispose(): void {
|
||||
if (this.m_IsDisposed) {
|
||||
return; // 防止重复释放
|
||||
}
|
||||
|
||||
console.log("Disposing Viewer...");
|
||||
|
||||
// 1. 停止渲染循环
|
||||
this.m_IsDisposed = true; // 设置标志,渲染循环将在下一帧停止
|
||||
if (this.m_AnimationFrameId !== null) {
|
||||
cancelAnimationFrame(this.m_AnimationFrameId);
|
||||
this.m_AnimationFrameId = null;
|
||||
}
|
||||
|
||||
|
||||
// 2. 移除事件监听器
|
||||
window.removeEventListener("resize", this.OnResize);
|
||||
// 如果 m_DomEl 上有其他直接添加的事件监听器,也应在此移除
|
||||
|
||||
// 3. 销毁场景内容 (几何体、材质、纹理)
|
||||
if (this._Scene) {
|
||||
// 遍历场景中的所有对象并释放它们的资源
|
||||
this._Scene.traverse((object) => {
|
||||
disposeNode(object);
|
||||
});
|
||||
|
||||
// 从场景中移除所有子对象
|
||||
// scene.clear() 也可以,但在此之前我们已经遍历并释放了资源
|
||||
while (this._Scene.children.length > 0) {
|
||||
const child = this._Scene.children[0];
|
||||
// 确保子对象的资源已通过上面的 traverse 处理
|
||||
this._Scene.remove(child);
|
||||
}
|
||||
// console.log("Scene cleared and resources disposed.");
|
||||
}
|
||||
|
||||
|
||||
// 4. 销毁渲染器
|
||||
if (this.Renderer) {
|
||||
// @ts-ignore access to private renderLists
|
||||
if (this.Renderer.renderLists) { // 检查 renderLists 是否存在
|
||||
// @ts-ignore
|
||||
this.Renderer.renderLists.dispose();
|
||||
// console.log("Renderer renderLists disposed.");
|
||||
}
|
||||
this.Renderer.dispose();
|
||||
// console.log("Renderer disposed.");
|
||||
|
||||
// 从 DOM 中移除渲染器的 canvas 元素
|
||||
if (this.Renderer.domElement && this.Renderer.domElement.parentElement) {
|
||||
this.Renderer.domElement.parentElement.removeChild(this.Renderer.domElement);
|
||||
// console.log("Renderer DOM element removed.");
|
||||
}
|
||||
}
|
||||
|
||||
// 5. 清除引用 (帮助垃圾回收)
|
||||
// @ts-ignore
|
||||
this._Scene = null;
|
||||
// @ts-ignore
|
||||
this.Renderer = null;
|
||||
// @ts-ignore
|
||||
this.m_DomEl = null;
|
||||
// @ts-ignore
|
||||
this.m_Camera = null;
|
||||
// @ts-ignore
|
||||
this.m_LookTarget = null;
|
||||
|
||||
console.log("Viewer disposed successfully.");
|
||||
}
|
||||
}
|
@ -5,7 +5,7 @@
|
||||
</CfFlex>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { onMounted, ref, useTemplateRef } from 'vue';
|
||||
import { onBeforeUnmount, onMounted, onUnmounted, ref, useTemplateRef } from 'vue';
|
||||
import MaterialAdjuster from './MaterialAdjuster.vue';
|
||||
import { useScene } from '../stores/sceneStore';
|
||||
import CfFlex from './CfFlex.vue';
|
||||
@ -21,15 +21,26 @@ const textureSrc = ref(config.textureSrc);
|
||||
|
||||
// 禁用右键菜单
|
||||
document.addEventListener('contextmenu', (e) => e.preventDefault());
|
||||
|
||||
onMounted(() => {
|
||||
scene.Initial(container.value);
|
||||
eventbus.Subscribe('submit', () => adjusterRef.value.Upload());
|
||||
eventbus.Subscribe('update-texture', () => {
|
||||
textureSrc.value = config.textureSrc
|
||||
eventbus.Subscribe('submit', HandleUpload);
|
||||
eventbus.Subscribe('update-texture', HandleChangeTexture);
|
||||
});
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
eventbus.Unsubscribe('submit', HandleUpload);
|
||||
eventbus.Unsubscribe('update-texture', HandleChangeTexture);
|
||||
scene.Dispose();
|
||||
});
|
||||
|
||||
function HandleUpload() {
|
||||
adjusterRef.value.Upload();
|
||||
}
|
||||
|
||||
function HandleChangeTexture() {
|
||||
textureSrc.value = config.textureSrc;
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
39
src/helpers/helper.three.ts
Normal file
39
src/helpers/helper.three.ts
Normal file
@ -0,0 +1,39 @@
|
||||
import type { Material, Texture, Object3D, Mesh, Line, Points } from "three";
|
||||
|
||||
/** 深度释放材质和纹理 */
|
||||
export function disposeMaterial(material: Material | Material[]): void {
|
||||
if (Array.isArray(material)) {
|
||||
material.forEach(mat => disposeSingleMaterial(mat));
|
||||
} else if (material) { // 确保 material 不是 undefined 或 null
|
||||
disposeSingleMaterial(material);
|
||||
}
|
||||
}
|
||||
|
||||
export function disposeSingleMaterial(material: Material): void {
|
||||
material.dispose();
|
||||
// 遍历材质的属性,查找并释放纹理
|
||||
for (const key in material) {
|
||||
const value = (material as any)[key] as unknown; // 使用 unknown 来进行更安全的类型检查
|
||||
if (value && typeof value === 'object' && 'isTexture' in value && (value as Texture).isTexture) {
|
||||
(value as Texture).dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** 释放节点的几何体、材质和纹理 */
|
||||
export function disposeNode(node: Object3D): void {
|
||||
// 类型守卫,检查节点是否为 Mesh, Line, 或 Points
|
||||
if ((node as Mesh).isMesh || (node as Line).isLine || (node as Points).isPoints) {
|
||||
const obj = node as Mesh | Line | Points;
|
||||
|
||||
if (obj.geometry) {
|
||||
obj.geometry.dispose();
|
||||
// console.log("Disposed geometry:", obj.geometry.uuid);
|
||||
}
|
||||
|
||||
if (obj.material) {
|
||||
disposeMaterial(obj.material);
|
||||
// console.log("Disposed material(s) for node:", obj.uuid);
|
||||
}
|
||||
}
|
||||
}
|
@ -12,7 +12,11 @@ export const useEvent = defineStore('event', () => {
|
||||
events[name]?.forEach(e => e(...args));
|
||||
}
|
||||
|
||||
return { Subscribe, Publish };
|
||||
function Unsubscribe(name: EventNames, callback: Function) {
|
||||
events[name] = events[name]?.filter(e => e !== callback) || [];
|
||||
}
|
||||
|
||||
return { Subscribe, Publish, Unsubscribe };
|
||||
});
|
||||
|
||||
export type EventNames = 'submit' | 'update-texture'
|
@ -8,7 +8,7 @@ import { materialRenderer } from "../common/MaterialRenderer";
|
||||
import { MaterialOut } from "../common/MaterialSerializer";
|
||||
|
||||
export const useScene = defineStore('scene', () => {
|
||||
let _editor: MaterialEditor;
|
||||
let _editor: MaterialEditor | undefined;
|
||||
const _currGeometry = ref<string>('球');
|
||||
const _currTexture = ref<Texture>();
|
||||
const CurrGeometry = computed({
|
||||
@ -37,11 +37,22 @@ export const useScene = defineStore('scene', () => {
|
||||
|
||||
const view = _editor.Viewer;
|
||||
window.onresize = () => {
|
||||
view.OnSize(canvas.clientWidth, canvas.clientHeight);
|
||||
view.SetSize(canvas.clientWidth, canvas.clientHeight);
|
||||
view.UpdateRender();
|
||||
}
|
||||
}
|
||||
|
||||
function Dispose() {
|
||||
console.log("Disposing...");
|
||||
Material.value.GoodBye();
|
||||
_editor?.Dispose();
|
||||
_editor = undefined;
|
||||
window.onresize = undefined;
|
||||
|
||||
// 释放Singleton
|
||||
MaterialEditor.ReleaseInstance();
|
||||
}
|
||||
|
||||
function ChangeGeometry(geoName: string) {
|
||||
_currGeometry.value = geoName;
|
||||
let geo = _editor.Geometrys.get(_currGeometry.value);
|
||||
@ -121,7 +132,8 @@ export const useScene = defineStore('scene', () => {
|
||||
ChangeTextureAsync,
|
||||
UpdateTexture,
|
||||
SerializeMaterialAsync,
|
||||
GenerateMaterialLogoAsync
|
||||
GenerateMaterialLogoAsync,
|
||||
Dispose
|
||||
};
|
||||
});
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user