feat(sdk): 迁移脚本执行系统与编辑器热更新对齐 Astral
- Viewer.ts: enableScript setter 修复写回 options,脚本状态变量改为实例级 - Viewer.ts: 新增 InstallScriptsOptions、reinstallObjectScripts、invokeInstalledScriptEvent - Viewer.ts: installScripts 支持 loadedScriptNames 精准触发 loaded,补充 bindDataChange 分发 - Viewer.ts: unInstallScripts 移除错误的 enableScript 提前返回守卫 - Signals.ts: 脚本信号统一改用 reinstallObjectScripts 热更新 - Signals.ts: sceneCleared 先卸载脚本再清场 - Signals.ts: registerSignal/dispose 模式防止信号泄漏 - CodeEditor.vue: Monaco 忽略 TS80002(构造函数转 class 建议)
This commit is contained in:
parent
d8f98ee295
commit
e8615c6796
@ -78,6 +78,7 @@ async function initMonaco() {
|
|||||||
monaco.languages.typescript.javascriptDefaults.setDiagnosticsOptions({
|
monaco.languages.typescript.javascriptDefaults.setDiagnosticsOptions({
|
||||||
noSemanticValidation: false,
|
noSemanticValidation: false,
|
||||||
noSyntaxValidation: false,
|
noSyntaxValidation: false,
|
||||||
|
diagnosticCodesToIgnore: [80002],
|
||||||
});
|
});
|
||||||
// 如果使用 Webpack 或其他打包工具,可以使用 importScripts
|
// 如果使用 Webpack 或其他打包工具,可以使用 importScripts
|
||||||
monaco.languages.typescript.javascriptDefaults.setCompilerOptions({
|
monaco.languages.typescript.javascriptDefaults.setCompilerOptions({
|
||||||
|
|||||||
@ -119,6 +119,11 @@ export interface ViewerModules {
|
|||||||
tilesManage:TilesManage,
|
tilesManage:TilesManage,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface InstallScriptsOptions {
|
||||||
|
invokeLoaded?: boolean;
|
||||||
|
loadedScriptNames?: string[];
|
||||||
|
}
|
||||||
|
|
||||||
CameraControls.install({
|
CameraControls.install({
|
||||||
THREE: {
|
THREE: {
|
||||||
Vector2: THREE.Vector2,
|
Vector2: THREE.Vector2,
|
||||||
@ -140,25 +145,14 @@ const onDoubleClickPosition = new THREE.Vector2();
|
|||||||
// 表示animate()函数被多次调用累积时间,用于限制FPS
|
// 表示animate()函数被多次调用累积时间,用于限制FPS
|
||||||
let timeStamp = 0;
|
let timeStamp = 0;
|
||||||
|
|
||||||
// 事件绑定
|
|
||||||
const Fn: any = {
|
|
||||||
pointerdown: null,
|
|
||||||
pointerup: null,
|
|
||||||
pointermove: null,
|
|
||||||
keydown: null,
|
|
||||||
keyup: null,
|
|
||||||
touchstart: null,
|
|
||||||
dblclick: null,
|
|
||||||
}
|
|
||||||
|
|
||||||
// 脚本管理数据结构
|
// 脚本管理数据结构
|
||||||
type EventHandlers = {
|
type EventHandlers = {
|
||||||
[eventName: string]: {
|
[eventName: string]: {
|
||||||
[uuid: string]: Function[];
|
[uuid: string]: Function[];
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
// 脚本中可写的所有事件
|
|
||||||
let events: EventHandlers = {
|
const createScriptEvents = (): EventHandlers => ({
|
||||||
loaded: {},
|
loaded: {},
|
||||||
beforeAnimation: {},
|
beforeAnimation: {},
|
||||||
afterAnimation: {},
|
afterAnimation: {},
|
||||||
@ -168,6 +162,7 @@ let events: EventHandlers = {
|
|||||||
afterDestroy: {},
|
afterDestroy: {},
|
||||||
onPick: {},
|
onPick: {},
|
||||||
onDoubleClick: {},
|
onDoubleClick: {},
|
||||||
|
bindDataChange: {},
|
||||||
onKeyDown: {},
|
onKeyDown: {},
|
||||||
onKeyUp: {},
|
onKeyUp: {},
|
||||||
onPointerDown: {},
|
onPointerDown: {},
|
||||||
@ -175,9 +170,7 @@ let events: EventHandlers = {
|
|||||||
onPointerMove: {},
|
onPointerMove: {},
|
||||||
onTouchStart: {},
|
onTouchStart: {},
|
||||||
onTouchEnd: {},
|
onTouchEnd: {},
|
||||||
};
|
});
|
||||||
// UUID 到事件的映射
|
|
||||||
const uuidEventMap: Map<string, Map<string, { name: string, fn: Function }[]>> = new Map();
|
|
||||||
|
|
||||||
export default class Viewer extends THREE.EventDispatcher<ViewerEventMap> {
|
export default class Viewer extends THREE.EventDispatcher<ViewerEventMap> {
|
||||||
public container: HTMLElement;
|
public container: HTMLElement;
|
||||||
@ -197,6 +190,18 @@ export default class Viewer extends THREE.EventDispatcher<ViewerEventMap> {
|
|||||||
public css2DRenderer: CSS2DRenderer = new CSS2DRenderer();
|
public css2DRenderer: CSS2DRenderer = new CSS2DRenderer();
|
||||||
public css3DRenderer: CSS3DRenderer = new CSS3DRenderer();
|
public css3DRenderer: CSS3DRenderer = new CSS3DRenderer();
|
||||||
public timer = new Timer();
|
public timer = new Timer();
|
||||||
|
public events: EventHandlers = createScriptEvents();
|
||||||
|
public uuidEventMap: Map<string, Map<string, { name: string, fn: Function }[]>> = new Map();
|
||||||
|
public fns: any = {
|
||||||
|
mousedown: null,
|
||||||
|
pointerdown: null,
|
||||||
|
pointerup: null,
|
||||||
|
pointermove: null,
|
||||||
|
keydown: null,
|
||||||
|
keyup: null,
|
||||||
|
touchstart: null,
|
||||||
|
dblclick: null,
|
||||||
|
};
|
||||||
//整个主场景的box3
|
//整个主场景的box3
|
||||||
public sceneBox3 = new THREE.Box3();
|
public sceneBox3 = new THREE.Box3();
|
||||||
public package: Package;
|
public package: Package;
|
||||||
@ -362,6 +367,8 @@ export default class Viewer extends THREE.EventDispatcher<ViewerEventMap> {
|
|||||||
set enableScript(enable: boolean) {
|
set enableScript(enable: boolean) {
|
||||||
if (enable === this.enableScript) return;
|
if (enable === this.enableScript) return;
|
||||||
|
|
||||||
|
this.options.enableScript = enable;
|
||||||
|
|
||||||
if (enable) {
|
if (enable) {
|
||||||
this.installScripts();
|
this.installScripts();
|
||||||
} else {
|
} else {
|
||||||
@ -591,14 +598,14 @@ export default class Viewer extends THREE.EventDispatcher<ViewerEventMap> {
|
|||||||
* 初始化事件监听
|
* 初始化事件监听
|
||||||
*/
|
*/
|
||||||
initEvent() {
|
initEvent() {
|
||||||
Fn.pointerdown = this.onPointerDown.bind(this);
|
this.fns.pointerdown = this.onPointerDown.bind(this);
|
||||||
this.container.addEventListener('pointerdown', Fn.pointerdown);
|
this.container.addEventListener('pointerdown', this.fns.pointerdown);
|
||||||
Fn.pointermove = this.onPointerMove.bind(this);
|
this.fns.pointermove = this.onPointerMove.bind(this);
|
||||||
this.container.addEventListener('pointermove', Fn.pointermove);
|
this.container.addEventListener('pointermove', this.fns.pointermove);
|
||||||
Fn.touchstart = this.onTouchStart.bind(this);
|
this.fns.touchstart = this.onTouchStart.bind(this);
|
||||||
this.container.addEventListener('touchstart', Fn.touchstart);
|
this.container.addEventListener('touchstart', this.fns.touchstart);
|
||||||
Fn.dblclick = this.onDoubleClick.bind(this)
|
this.fns.dblclick = this.onDoubleClick.bind(this)
|
||||||
this.container.addEventListener('dblclick', Fn.dblclick);
|
this.container.addEventListener('dblclick', this.fns.dblclick);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -606,9 +613,13 @@ export default class Viewer extends THREE.EventDispatcher<ViewerEventMap> {
|
|||||||
* @param uuids 传入此参数则仅组装此数组下Object.uuid的脚本
|
* @param uuids 传入此参数则仅组装此数组下Object.uuid的脚本
|
||||||
* @param filterName 传入此参数则仅组装此数组下Object.uuid的脚本中name匹配的脚本
|
* @param filterName 传入此参数则仅组装此数组下Object.uuid的脚本中name匹配的脚本
|
||||||
*/
|
*/
|
||||||
installScripts(uuids?: string | string[], filterName: string = "") {
|
installScripts(uuids?: string | string[], filterName: string = "", options: InstallScriptsOptions = {}) {
|
||||||
if (!this.enableScript) return;
|
if (!this.enableScript) return;
|
||||||
|
|
||||||
|
const { invokeLoaded = false, loadedScriptNames = [] } = options;
|
||||||
|
const loadedScriptNameSet = new Set(loadedScriptNames.filter(Boolean));
|
||||||
|
const shouldInvokeLoadedAfterInstall = invokeLoaded || loadedScriptNameSet.size > 0;
|
||||||
|
|
||||||
// 注册 Helper
|
// 注册 Helper
|
||||||
const helper = new ScriptHelper(this.scene);
|
const helper = new ScriptHelper(this.scene);
|
||||||
|
|
||||||
@ -622,7 +633,7 @@ export default class Viewer extends THREE.EventDispatcher<ViewerEventMap> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 拼接下方闭包函数返回的结构,即返回脚本中写的支持的事件函数
|
// 拼接下方闭包函数返回的结构,即返回脚本中写的支持的事件函数
|
||||||
const validEvents = Object.keys(events);
|
const validEvents = Object.keys(this.events);
|
||||||
|
|
||||||
// 准备返回结构
|
// 准备返回结构
|
||||||
validEvents.forEach(eventName => {
|
validEvents.forEach(eventName => {
|
||||||
@ -646,7 +657,8 @@ export default class Viewer extends THREE.EventDispatcher<ViewerEventMap> {
|
|||||||
|
|
||||||
// 一个模型允许存在多个脚本
|
// 一个模型允许存在多个脚本
|
||||||
const scripts = App.scripts[uuid] || [];
|
const scripts = App.scripts[uuid] || [];
|
||||||
const uuidEvents = uuidEventMap.get(uuid) || new Map<string, { name: string, fn: Function }[]>();
|
const uuidEvents = this.uuidEventMap.get(uuid) || new Map<string, { name: string, fn: Function }[]>();
|
||||||
|
const scriptsNeedLoaded = new Set<string>();
|
||||||
|
|
||||||
scripts.forEach(script => {
|
scripts.forEach(script => {
|
||||||
// 如果存在需要按照name过滤
|
// 如果存在需要按照name过滤
|
||||||
@ -660,6 +672,11 @@ export default class Viewer extends THREE.EventDispatcher<ViewerEventMap> {
|
|||||||
this.camera, this.modules.controls, this.timer, fns.render
|
this.camera, this.modules.controls, this.timer, fns.render
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const shouldInvokeLoaded = loadedScriptNameSet.size > 0 ? loadedScriptNameSet.has(script.name) : invokeLoaded;
|
||||||
|
if (shouldInvokeLoaded && typeof functions.loaded === "function") {
|
||||||
|
scriptsNeedLoaded.add(script.name);
|
||||||
|
}
|
||||||
|
|
||||||
Object.entries(functions).forEach(([name, fn]) => {
|
Object.entries(functions).forEach(([name, fn]) => {
|
||||||
if (!fn || !validEvents.includes(name)) {
|
if (!fn || !validEvents.includes(name)) {
|
||||||
if (fn && !validEvents.includes(name)) {
|
if (fn && !validEvents.includes(name)) {
|
||||||
@ -672,12 +689,16 @@ export default class Viewer extends THREE.EventDispatcher<ViewerEventMap> {
|
|||||||
const {type, target, ...params} = e;
|
const {type, target, ...params} = e;
|
||||||
|
|
||||||
// 点击事件只分发给对应模型
|
// 点击事件只分发给对应模型
|
||||||
if (["onPick", "onDoubleClick"].includes(name)) {
|
if (["onPick", "onDoubleClick", "bindDataChange"].includes(name)) {
|
||||||
const {intersect, object: _object} = params;
|
const {intersect, object: _object, data, config, index} = params;
|
||||||
|
|
||||||
if (_object.uuid !== object.uuid) return;
|
if (_object.uuid !== object.uuid) return;
|
||||||
|
|
||||||
(fn as Function).bind(object)(intersect as THREE.Intersection);
|
if (name === "bindDataChange") {
|
||||||
|
(fn as Function).bind(object)(data, config, index);
|
||||||
|
} else {
|
||||||
|
(fn as Function).bind(object)(intersect as THREE.Intersection);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
if (isEmptyObject(params)) {
|
if (isEmptyObject(params)) {
|
||||||
(fn as Function).bind(object)();
|
(fn as Function).bind(object)();
|
||||||
@ -688,8 +709,8 @@ export default class Viewer extends THREE.EventDispatcher<ViewerEventMap> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 添加到全局事件集合
|
// 添加到全局事件集合
|
||||||
if (!events[name][uuid]) events[name][uuid] = [];
|
if (!this.events[name][uuid]) this.events[name][uuid] = [];
|
||||||
events[name][uuid].push(boundFn);
|
this.events[name][uuid].push(boundFn);
|
||||||
|
|
||||||
// 添加到 UUID 事件映射
|
// 添加到 UUID 事件映射
|
||||||
if (!uuidEvents.has(name)) uuidEvents.set(name, []);
|
if (!uuidEvents.has(name)) uuidEvents.set(name, []);
|
||||||
@ -704,7 +725,13 @@ export default class Viewer extends THREE.EventDispatcher<ViewerEventMap> {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// 更新 UUID 映射
|
// 更新 UUID 映射
|
||||||
uuidEventMap.set(uuid, uuidEvents);
|
this.uuidEventMap.set(uuid, uuidEvents);
|
||||||
|
|
||||||
|
if (shouldInvokeLoadedAfterInstall) {
|
||||||
|
scriptsNeedLoaded.forEach(scriptName => {
|
||||||
|
this.invokeInstalledScriptEvent(uuid, scriptName, "loaded");
|
||||||
|
});
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 处理指定 UUID 或全部
|
// 处理指定 UUID 或全部
|
||||||
@ -714,15 +741,15 @@ export default class Viewer extends THREE.EventDispatcher<ViewerEventMap> {
|
|||||||
Object.keys(App.scripts).forEach(processUuid);
|
Object.keys(App.scripts).forEach(processUuid);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!Fn.keydown) {
|
if (!this.fns.keydown) {
|
||||||
Fn.keydown = (event: KeyboardEvent) => {
|
this.fns.keydown = (event: KeyboardEvent) => {
|
||||||
this.dispatchEvent({type: "onKeyDown", event})
|
this.dispatchEvent({type: "onKeyDown", event})
|
||||||
}
|
}
|
||||||
window.addEventListener('keydown', Fn.keydown);
|
window.addEventListener('keydown', this.fns.keydown);
|
||||||
Fn.keyup = (event: KeyboardEvent) => {
|
this.fns.keyup = (event: KeyboardEvent) => {
|
||||||
this.dispatchEvent({type: "onKeyUp", event})
|
this.dispatchEvent({type: "onKeyUp", event})
|
||||||
}
|
}
|
||||||
window.addEventListener('keyup', Fn.keyup);
|
window.addEventListener('keyup', this.fns.keyup);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -732,9 +759,9 @@ export default class Viewer extends THREE.EventDispatcher<ViewerEventMap> {
|
|||||||
* @param filterName 传入此参数则仅卸载此Object.uuid的脚本中name匹配的脚本
|
* @param filterName 传入此参数则仅卸载此Object.uuid的脚本中name匹配的脚本
|
||||||
*/
|
*/
|
||||||
uninstallScriptsByUuid(uuid: string, filterName: string = "") {
|
uninstallScriptsByUuid(uuid: string, filterName: string = "") {
|
||||||
if (!uuidEventMap.has(uuid)) return;
|
if (!this.uuidEventMap.has(uuid)) return;
|
||||||
|
|
||||||
const uuidEvents = uuidEventMap.get(uuid)!;
|
const uuidEvents = this.uuidEventMap.get(uuid)!;
|
||||||
|
|
||||||
const uuidEventsArray = Array.from(uuidEvents);
|
const uuidEventsArray = Array.from(uuidEvents);
|
||||||
for (let i = uuidEventsArray.length - 1; i >= 0; i--) {
|
for (let i = uuidEventsArray.length - 1; i >= 0; i--) {
|
||||||
@ -756,7 +783,7 @@ export default class Viewer extends THREE.EventDispatcher<ViewerEventMap> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 全局事件集合
|
// 全局事件集合
|
||||||
const es = events[eventName][uuid];
|
const es = this.events[eventName][uuid];
|
||||||
// 移除相应函数
|
// 移除相应函数
|
||||||
const ei = es.findIndex(f => f === sc.fn);
|
const ei = es.findIndex(f => f === sc.fn);
|
||||||
if (ei !== -1) {
|
if (ei !== -1) {
|
||||||
@ -764,40 +791,79 @@ export default class Viewer extends THREE.EventDispatcher<ViewerEventMap> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (es.length === 0) {
|
if (es.length === 0) {
|
||||||
delete events[eventName][uuid];
|
delete this.events[eventName][uuid];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 清理 UUID 映射
|
// 清理 UUID 映射
|
||||||
if (Array.from(uuidEvents.keys()).length === 0) {
|
if (Array.from(uuidEvents.keys()).length === 0) {
|
||||||
uuidEventMap.delete(uuid);
|
this.uuidEventMap.delete(uuid);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
invokeInstalledScriptEvent<T extends keyof ViewerEventMap>(
|
||||||
|
uuid: string,
|
||||||
|
scriptName: string,
|
||||||
|
eventName: T,
|
||||||
|
payload: Partial<ViewerEventMap[T]> = {}
|
||||||
|
): boolean {
|
||||||
|
const uuidEvents = this.uuidEventMap.get(uuid);
|
||||||
|
if (!uuidEvents) return false;
|
||||||
|
|
||||||
|
const handlers = uuidEvents.get(String(eventName));
|
||||||
|
if (!handlers || handlers.length === 0) return false;
|
||||||
|
|
||||||
|
let executed = false;
|
||||||
|
handlers.forEach(handler => {
|
||||||
|
if (handler.name !== scriptName) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
handler.fn({
|
||||||
|
...payload,
|
||||||
|
type: eventName,
|
||||||
|
target: this,
|
||||||
|
});
|
||||||
|
executed = true;
|
||||||
|
} catch (error) {
|
||||||
|
const message = error instanceof Error ? error.message : String(error);
|
||||||
|
App.log.error(`[Script] 执行 ${scriptName}.${String(eventName)} 失败: ${message}`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return executed;
|
||||||
|
}
|
||||||
|
|
||||||
|
reinstallObjectScripts(uuid: string, loadedScriptNames: string[] = []): void {
|
||||||
|
if (!uuid) return;
|
||||||
|
|
||||||
|
const uniqueLoadedScriptNames = Array.from(new Set(loadedScriptNames.filter(Boolean)));
|
||||||
|
|
||||||
|
this.uninstallScriptsByUuid(uuid);
|
||||||
|
this.installScripts([uuid], "", uniqueLoadedScriptNames.length > 0 ? { loadedScriptNames: uniqueLoadedScriptNames } : undefined);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 卸载所有脚本
|
* 卸载所有脚本
|
||||||
*/
|
*/
|
||||||
unInstallScripts() {
|
unInstallScripts() {
|
||||||
if (this.enableScript) return;
|
|
||||||
|
|
||||||
// 直接遍历 UUID 映射,复杂度 O(n)
|
// 直接遍历 UUID 映射,复杂度 O(n)
|
||||||
uuidEventMap.forEach((_, uuid) => {
|
this.uuidEventMap.forEach((_, uuid) => {
|
||||||
this.uninstallScriptsByUuid(uuid);
|
this.uninstallScriptsByUuid(uuid);
|
||||||
});
|
});
|
||||||
|
|
||||||
// 重置数据结构
|
// 重置数据结构
|
||||||
uuidEventMap.clear();
|
this.uuidEventMap.clear();
|
||||||
Object.keys(events).forEach(event => {
|
Object.keys(this.events).forEach(event => {
|
||||||
events[event] = {};
|
this.events[event] = {};
|
||||||
});
|
});
|
||||||
|
|
||||||
if (Fn.keydown) {
|
if (this.fns.keydown) {
|
||||||
window.removeEventListener('keydown', Fn.keydown);
|
window.removeEventListener('keydown', this.fns.keydown);
|
||||||
Fn.keydown = null;
|
this.fns.keydown = null;
|
||||||
|
|
||||||
window.removeEventListener('keyup', Fn.keyup);
|
window.removeEventListener('keyup', this.fns.keyup);
|
||||||
Fn.keyup = null;
|
this.fns.keyup = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -904,8 +970,8 @@ export default class Viewer extends THREE.EventDispatcher<ViewerEventMap> {
|
|||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
const array = getMousePosition(this.container, event.clientX, event.clientY);
|
const array = getMousePosition(this.container, event.clientX, event.clientY);
|
||||||
onDownPosition.fromArray(array);
|
onDownPosition.fromArray(array);
|
||||||
Fn.pointerup = this.onPointerUp.bind(this);
|
this.fns.pointerup = this.onPointerUp.bind(this);
|
||||||
document.addEventListener('pointerup', Fn.pointerup);
|
document.addEventListener('pointerup', this.fns.pointerup);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -918,8 +984,8 @@ export default class Viewer extends THREE.EventDispatcher<ViewerEventMap> {
|
|||||||
const array = getMousePosition(this.container, event.clientX, event.clientY);
|
const array = getMousePosition(this.container, event.clientX, event.clientY);
|
||||||
onUpPosition.fromArray(array);
|
onUpPosition.fromArray(array);
|
||||||
this.handleClick();
|
this.handleClick();
|
||||||
document.removeEventListener('pointerup', Fn.pointerup);
|
document.removeEventListener('pointerup', this.fns.pointerup);
|
||||||
Fn.pointerup = null;
|
this.fns.pointerup = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -940,8 +1006,8 @@ export default class Viewer extends THREE.EventDispatcher<ViewerEventMap> {
|
|||||||
const touch = event.changedTouches[0];
|
const touch = event.changedTouches[0];
|
||||||
const array = getMousePosition(this.container, touch.clientX, touch.clientY);
|
const array = getMousePosition(this.container, touch.clientX, touch.clientY);
|
||||||
onDownPosition.fromArray(array);
|
onDownPosition.fromArray(array);
|
||||||
Fn.pointerup = this.onTouchEnd.bind(this);
|
this.fns.pointerup = this.onTouchEnd.bind(this);
|
||||||
document.addEventListener('touchend', Fn.pointerup);
|
document.addEventListener('touchend', this.fns.pointerup);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -955,8 +1021,8 @@ export default class Viewer extends THREE.EventDispatcher<ViewerEventMap> {
|
|||||||
const array = getMousePosition(this.container, touch.clientX, touch.clientY);
|
const array = getMousePosition(this.container, touch.clientX, touch.clientY);
|
||||||
onUpPosition.fromArray(array);
|
onUpPosition.fromArray(array);
|
||||||
this.handleClick();
|
this.handleClick();
|
||||||
document.removeEventListener('touchend', Fn.pointerup);
|
document.removeEventListener('touchend', this.fns.pointerup);
|
||||||
Fn.pointerup = null;
|
this.fns.pointerup = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1133,14 +1199,14 @@ export default class Viewer extends THREE.EventDispatcher<ViewerEventMap> {
|
|||||||
dispose() {
|
dispose() {
|
||||||
this.dispatchEvent({type: "beforeDestroy"});
|
this.dispatchEvent({type: "beforeDestroy"});
|
||||||
|
|
||||||
this.container.removeEventListener('mousedown', Fn.mousedown);
|
this.container.removeEventListener('mousedown', this.fns.mousedown);
|
||||||
Fn.mousedown = null;
|
this.fns.mousedown = null;
|
||||||
this.container.removeEventListener('pointermove', Fn.pointermove);
|
this.container.removeEventListener('pointermove', this.fns.pointermove);
|
||||||
Fn.pointermove = null;
|
this.fns.pointermove = null;
|
||||||
this.container.removeEventListener('touchstart', Fn.touchstart);
|
this.container.removeEventListener('touchstart', this.fns.touchstart);
|
||||||
Fn.touchstart = null;
|
this.fns.touchstart = null;
|
||||||
this.container.removeEventListener('dblclick', Fn.dblclick);
|
this.container.removeEventListener('dblclick', this.fns.dblclick);
|
||||||
Fn.dblclick = null;
|
this.fns.dblclick = null;
|
||||||
|
|
||||||
Object.keys(this.modules).forEach(key => {
|
Object.keys(this.modules).forEach(key => {
|
||||||
if (this.modules[key].dispose) {
|
if (this.modules[key].dispose) {
|
||||||
|
|||||||
@ -2,15 +2,21 @@ import * as THREE from "three";
|
|||||||
import {RoomEnvironment} from "three/examples/jsm/environments/RoomEnvironment.js";
|
import {RoomEnvironment} from "three/examples/jsm/environments/RoomEnvironment.js";
|
||||||
import {ShaderPass} from "three/examples/jsm/postprocessing/ShaderPass.js";
|
import {ShaderPass} from "three/examples/jsm/postprocessing/ShaderPass.js";
|
||||||
import {Effect} from "./Effect";
|
import {Effect} from "./Effect";
|
||||||
import {useAddSignal} from "@/hooks";
|
import {useAddSignal, useRemoveSignal} from "@/hooks";
|
||||||
import Viewer from "../Viewer";
|
import Viewer from "../Viewer";
|
||||||
import App from "@/core/app/App";
|
import App from "@/core/app/App";
|
||||||
import {focusObject} from "@/utils/scene/controls.ts";
|
import {focusObject} from "@/utils/scene/controls.ts";
|
||||||
|
|
||||||
|
type SignalListenerRecord = {
|
||||||
|
name: string;
|
||||||
|
listener: (...params: any[]) => void;
|
||||||
|
};
|
||||||
|
|
||||||
export class Signals {
|
export class Signals {
|
||||||
private readonly viewer: Viewer;
|
private readonly viewer: Viewer;
|
||||||
|
|
||||||
private useBackgroundAsEnvironment = false;
|
private useBackgroundAsEnvironment = false;
|
||||||
|
private readonly signalListeners: SignalListenerRecord[] = [];
|
||||||
|
|
||||||
constructor(viewer:Viewer) {
|
constructor(viewer:Viewer) {
|
||||||
this.viewer = viewer;
|
this.viewer = viewer;
|
||||||
@ -18,41 +24,46 @@ export class Signals {
|
|||||||
this.init();
|
this.init();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private registerSignal(name: string, listener: (...params: any[]) => void) {
|
||||||
|
this.signalListeners.push({ name, listener });
|
||||||
|
useAddSignal(name, listener);
|
||||||
|
}
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
useAddSignal("sceneCleared", this.sceneCleared.bind(this));
|
this.registerSignal("sceneCleared", this.sceneCleared.bind(this));
|
||||||
useAddSignal("transformModeChanged", this.transformModeChanged.bind(this));
|
this.registerSignal("transformModeChanged", this.transformModeChanged.bind(this));
|
||||||
useAddSignal("snapChanged", this.snapChanged.bind(this));
|
this.registerSignal("snapChanged", this.snapChanged.bind(this));
|
||||||
useAddSignal("spaceChanged", this.spaceChanged.bind(this));
|
this.registerSignal("spaceChanged", this.spaceChanged.bind(this));
|
||||||
useAddSignal("effectEnabledChange", this.effectEnabledChange.bind(this));
|
this.registerSignal("effectEnabledChange", this.effectEnabledChange.bind(this));
|
||||||
|
|
||||||
useAddSignal("rendererUpdated", this.rendererUpdated.bind(this));
|
this.registerSignal("rendererUpdated", this.rendererUpdated.bind(this));
|
||||||
useAddSignal("rendererCreated", this.rendererCreated.bind(this));
|
this.registerSignal("rendererCreated", this.rendererCreated.bind(this));
|
||||||
useAddSignal("rendererConfigUpdate", this.rendererConfigUpdate.bind(this));
|
this.registerSignal("rendererConfigUpdate", this.rendererConfigUpdate.bind(this));
|
||||||
useAddSignal("rendererDetectKTX2Support", this.rendererDetectKTX2Support.bind(this));
|
this.registerSignal("rendererDetectKTX2Support", this.rendererDetectKTX2Support.bind(this));
|
||||||
|
|
||||||
useAddSignal("sceneBackgroundChanged", this.sceneBackgroundChanged.bind(this));
|
this.registerSignal("sceneBackgroundChanged", this.sceneBackgroundChanged.bind(this));
|
||||||
useAddSignal("sceneEnvironmentChanged", this.sceneEnvironmentChanged.bind(this));
|
this.registerSignal("sceneEnvironmentChanged", this.sceneEnvironmentChanged.bind(this));
|
||||||
useAddSignal("sceneGraphChanged", this.sceneGraphChanged.bind(this));
|
this.registerSignal("sceneGraphChanged", this.sceneGraphChanged.bind(this));
|
||||||
useAddSignal("cameraChanged", this.cameraChanged.bind(this));
|
this.registerSignal("cameraChanged", this.cameraChanged.bind(this));
|
||||||
useAddSignal("cameraReset", this.viewer.updateAspectRatio.bind(this.viewer));
|
this.registerSignal("cameraReset", this.viewer.updateAspectRatio.bind(this.viewer));
|
||||||
useAddSignal("viewportCameraChanged", this.viewportCameraChanged.bind(this));
|
this.registerSignal("viewportCameraChanged", this.viewportCameraChanged.bind(this));
|
||||||
useAddSignal("viewportShadingChanged", this.viewportShadingChanged.bind(this));
|
this.registerSignal("viewportShadingChanged", this.viewportShadingChanged.bind(this));
|
||||||
|
|
||||||
useAddSignal("objectSelected", this.objectSelected.bind(this));
|
this.registerSignal("objectSelected", this.objectSelected.bind(this));
|
||||||
useAddSignal("objectFocused", this.objectFocused.bind(this));
|
this.registerSignal("objectFocused", this.objectFocused.bind(this));
|
||||||
useAddSignal("objectAdded", this.objectAdded.bind(this));
|
this.registerSignal("objectAdded", this.objectAdded.bind(this));
|
||||||
useAddSignal("objectChanged", this.objectChanged.bind(this));
|
this.registerSignal("objectChanged", this.objectChanged.bind(this));
|
||||||
useAddSignal("objectRemoved", this.objectRemoved.bind(this));
|
this.registerSignal("objectRemoved", this.objectRemoved.bind(this));
|
||||||
|
|
||||||
useAddSignal("geometryChanged", this.geometryChanged.bind(this));
|
this.registerSignal("geometryChanged", this.geometryChanged.bind(this));
|
||||||
useAddSignal("materialChanged", this.materialChanged.bind(this));
|
this.registerSignal("materialChanged", this.materialChanged.bind(this));
|
||||||
|
|
||||||
useAddSignal("sceneResize", this.sceneResize.bind(this));
|
this.registerSignal("sceneResize", this.sceneResize.bind(this));
|
||||||
useAddSignal("showGridChanged", this.showGridChanged.bind(this));
|
this.registerSignal("showGridChanged", this.showGridChanged.bind(this));
|
||||||
|
|
||||||
useAddSignal("scriptAdded",this.scriptAdded.bind(this));
|
this.registerSignal("scriptAdded",this.scriptAdded.bind(this));
|
||||||
useAddSignal("scriptRemoved",this.scriptRemoved.bind(this));
|
this.registerSignal("scriptRemoved",this.scriptRemoved.bind(this));
|
||||||
useAddSignal("scriptChanged",this.scriptChanged.bind(this));
|
this.registerSignal("scriptChanged",this.scriptChanged.bind(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -66,6 +77,8 @@ export class Signals {
|
|||||||
* 清空
|
* 清空
|
||||||
*/
|
*/
|
||||||
sceneCleared() {
|
sceneCleared() {
|
||||||
|
this.viewer.unInstallScripts();
|
||||||
|
|
||||||
this.viewer.modules.controls.setTarget(0, 0, 0,true);
|
this.viewer.modules.controls.setTarget(0, 0, 0,true);
|
||||||
this.viewer.pathtracer?.reset();
|
this.viewer.pathtracer?.reset();
|
||||||
|
|
||||||
@ -453,15 +466,29 @@ export class Signals {
|
|||||||
/**
|
/**
|
||||||
* 添加脚本
|
* 添加脚本
|
||||||
*/
|
*/
|
||||||
scriptAdded(object:THREE.Object3D, _:ISceneScript){
|
scriptAdded(object:THREE.Object3D, sc:ISceneScript){
|
||||||
this.viewer.installScripts([object.uuid]);
|
if (!object?.uuid || !sc?.name) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
this.viewer.reinstallObjectScripts(object.uuid, [sc.name]);
|
||||||
|
} catch (error) {
|
||||||
|
const message = error instanceof Error ? error.message : String(error);
|
||||||
|
App.log.error(`[Script] 热安装脚本 ${sc.name} 失败: ${message}`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 移除脚本
|
* 移除脚本
|
||||||
*/
|
*/
|
||||||
scriptRemoved(object:THREE.Object3D, sc:ISceneScript){
|
scriptRemoved(object:THREE.Object3D, sc:ISceneScript){
|
||||||
this.viewer.uninstallScriptsByUuid(object.uuid,sc.name);
|
if (!object?.uuid) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
this.viewer.reinstallObjectScripts(object.uuid);
|
||||||
|
} catch (error) {
|
||||||
|
const message = error instanceof Error ? error.message : String(error);
|
||||||
|
App.log.error(`[Script] 卸载脚本 ${sc?.name || "unknown"} 失败: ${message}`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -469,8 +496,14 @@ export class Signals {
|
|||||||
*/
|
*/
|
||||||
scriptChanged(attributeName:string,object:THREE.Object3D, sc:ISceneScript){
|
scriptChanged(attributeName:string,object:THREE.Object3D, sc:ISceneScript){
|
||||||
if(attributeName !== "source") return;
|
if(attributeName !== "source") return;
|
||||||
|
if (!object?.uuid || !sc?.name) return;
|
||||||
|
|
||||||
this.viewer.installScripts([object.uuid],sc.name);
|
try {
|
||||||
|
this.viewer.reinstallObjectScripts(object.uuid, [sc.name]);
|
||||||
|
} catch (error) {
|
||||||
|
const message = error instanceof Error ? error.message : String(error);
|
||||||
|
App.log.error(`[Script] 热更新脚本 ${sc.name} 失败: ${message}`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -479,4 +512,11 @@ export class Signals {
|
|||||||
render(){
|
render(){
|
||||||
this.viewer.render();
|
this.viewer.render();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dispose() {
|
||||||
|
this.signalListeners.forEach(({ name, listener }) => {
|
||||||
|
useRemoveSignal(name, listener);
|
||||||
|
});
|
||||||
|
this.signalListeners.length = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Loading…
Reference in New Issue
Block a user