diff --git a/packages/front/src/core/PostproductionRenderer/src/simple-outline-pass.ts b/packages/front/src/core/PostproductionRenderer/src/simple-outline-pass.ts index 07cf2cd9..3d5809a7 100644 --- a/packages/front/src/core/PostproductionRenderer/src/simple-outline-pass.ts +++ b/packages/front/src/core/PostproductionRenderer/src/simple-outline-pass.ts @@ -90,7 +90,7 @@ export class SimpleOutlinePass extends Pass { * source meshes, their `attributes`, and their `index` belong to * fragments and are never mutated here. */ - private _outlinedTiles = new Map(); + private _outlinedTiles = new Map(); private _nextId = 1; private _maxThickness = 2; @@ -327,6 +327,11 @@ export class SimpleOutlinePass extends Pass { if (name === DEFAULT_GROUP) return; const group = this._groups.get(name); if (!group) return; + for (const [key, target] of this._outlinedTiles) { + if (target.groupName !== name) continue; + target.proxy.removeFromParent(); + this._outlinedTiles.delete(key); + } while (group.container.children.length) { group.container.children[0].removeFromParent(); } @@ -385,21 +390,13 @@ export class SimpleOutlinePass extends Pass { let group = this._groups.get(groupName); if (!group) group = this.addGroup(groupName); - const existing = this._outlinedTiles.get(tile); + const key = this.getOutlinedTileKey(tile, groupName); + const existing = this._outlinedTiles.get(key); if (existing) { - // Reuse the proxy shell; rebuild geometry groups against the new - // chunks, and re-home it if the group changed. const proxy = existing.proxy; const proxyGeom = proxy.geometry; proxyGeom.clearGroups(); this.fillProxyGroups(proxyGeom, chunks); - if (existing.groupName !== groupName) { - proxy.removeFromParent(); - // Material must stay as a single-element array; see comment below. - proxy.material = [group.material]; - group.container.add(proxy); - existing.groupName = groupName; - } return; } @@ -423,7 +420,7 @@ export class SimpleOutlinePass extends Pass { proxy.matrixWorld.copy(tile.matrixWorld); group.container.add(proxy); - this._outlinedTiles.set(tile, { source: tile, proxy, groupName }); + this._outlinedTiles.set(key, { source: tile, proxy, groupName }); } /** @@ -440,15 +437,31 @@ export class SimpleOutlinePass extends Pass { * The proxy's tiny BufferGeometry wrapper (just metadata) is left to * GC. Attributes and index belong to fragments. */ - detachOutlinedTile(tile: THREE.Mesh) { - const target = this._outlinedTiles.get(tile); - if (!target) return; - target.proxy.removeFromParent(); - this._outlinedTiles.delete(tile); + detachOutlinedTile(tile: THREE.Mesh, groupName?: string) { + if (groupName !== undefined) { + const key = this.getOutlinedTileKey(tile, groupName); + const target = this._outlinedTiles.get(key); + if (!target) return; + target.proxy.removeFromParent(); + this._outlinedTiles.delete(key); + return; + } + + for (const [key, target] of this._outlinedTiles) { + if (target.source !== tile) continue; + target.proxy.removeFromParent(); + this._outlinedTiles.delete(key); + } } - hasOutlinedTile(tile: THREE.Mesh) { - return this._outlinedTiles.has(tile); + hasOutlinedTile(tile: THREE.Mesh, groupName?: string) { + if (groupName !== undefined) { + return this._outlinedTiles.has(this.getOutlinedTileKey(tile, groupName)); + } + for (const target of this._outlinedTiles.values()) { + if (target.source === tile) return true; + } + return false; } /** @@ -626,6 +639,10 @@ export class SimpleOutlinePass extends Pass { } } + private getOutlinedTileKey(tile: THREE.Mesh, groupName: string) { + return `${groupName}:${tile.uuid}`; + } + // --------------------------------------------------------------------------- // Internals // --------------------------------------------------------------------------- diff --git a/packages/front/src/fragments/Outliner/index.ts b/packages/front/src/fragments/Outliner/index.ts index 91bfe8b2..5757e94b 100644 --- a/packages/front/src/fragments/Outliner/index.ts +++ b/packages/front/src/fragments/Outliner/index.ts @@ -234,7 +234,7 @@ export class Outliner extends OBC.Component implements OBC.Disposable { } } - this.detachAll(group); + this.detachAll(group, name); this._groups.delete(name); if (this.world) { @@ -311,15 +311,15 @@ export class Outliner extends OBC.Component implements OBC.Disposable { if (!state) return; state.map = {}; state.activeStyles.clear(); - this.detachAll(state); + this.detachAll(state, group); if (group === DEFAULT_GROUP) this.cleanPoints(); return; } - for (const [, state] of this._groups) { + for (const [name, state] of this._groups) { state.map = {}; state.activeStyles.clear(); - this.detachAll(state); + this.detachAll(state, name); } this.cleanPoints(); } @@ -537,7 +537,7 @@ export class Outliner extends OBC.Component implements OBC.Disposable { for (const [modelId, prev] of previouslyAttached) { const next = nextAttached.get(modelId) ?? new Set(); for (const tile of prev) { - if (!next.has(tile)) pass.detachOutlinedTile(tile); + if (!next.has(tile)) pass.detachOutlinedTile(tile, name); } } @@ -548,14 +548,14 @@ export class Outliner extends OBC.Component implements OBC.Disposable { * Detach every proxy this group has attached and clear the tracking * map. Used by `clean` and `remove`. */ - private detachAll(state: OutlineGroupState) { + private detachAll(state: OutlineGroupState, group = DEFAULT_GROUP) { if (!this.world) { state.attached = new Map(); return; } const pass = this.getRenderer().postproduction.outlinePass; for (const [, tiles] of state.attached) { - for (const tile of tiles) pass.detachOutlinedTile(tile); + for (const tile of tiles) pass.detachOutlinedTile(tile, group); } state.attached = new Map(); } @@ -576,7 +576,7 @@ export class Outliner extends OBC.Component implements OBC.Disposable { for (const [groupName, state] of this._groups) { const localIds = state.map[modelId]; if (localIds && localIds.size > 0) { - void this.updateGroup(groupName); + this.updateGroup(groupName).catch(() => undefined); } } };