diff --git a/packages/frontend/src/world/room/engine.ts b/packages/frontend/src/world/room/engine.ts index 4fab74c1c8..c92fba4c4e 100644 --- a/packages/frontend/src/world/room/engine.ts +++ b/packages/frontend/src/world/room/engine.ts @@ -883,6 +883,7 @@ export class RoomEngine extends EventEmitter { const objectInstance = await def.createInstance({ room: this, scene: this.scene, + sr: this.sr, root, options: args.options, model, diff --git a/packages/frontend/src/world/room/object.ts b/packages/frontend/src/world/room/object.ts index 596b7d4867..68513c3ba7 100644 --- a/packages/frontend/src/world/room/object.ts +++ b/packages/frontend/src/world/room/object.ts @@ -114,6 +114,7 @@ export type ObjectDef = { createInstance: (args: { room?: RoomEngine | null; scene: BABYLON.Scene; + sr: BABYLON.SnapshotRenderingHelper; root: BABYLON.Mesh; options: Readonly>; model: ModelManager; diff --git a/packages/frontend/src/world/room/objects/ceilingFanLight.ts b/packages/frontend/src/world/room/objects/ceilingFanLight.ts index 266e4ca995..10e1192c82 100644 --- a/packages/frontend/src/world/room/objects/ceilingFanLight.ts +++ b/packages/frontend/src/world/room/objects/ceilingFanLight.ts @@ -24,7 +24,7 @@ export const ceilingFanLight = defineObject({ hasCollisions: false, receiveShadows: false, castShadows: false, - createInstance: ({ options, room, scene, model }) => { + createInstance: ({ options, sr, scene, model }) => { const shadeMaterial = model.findMaterial('__X_SHADE__'); const applyShadeColor = () => { @@ -49,7 +49,7 @@ export const ceilingFanLight = defineObject({ ]); rotor.animations = [anim]; animationObserver = scene.onAfterAnimationsObservable.add(() => { - room?.sr.updateMesh([rotor, ...rotor.getChildMeshes()], false); + sr.updateMesh([rotor, ...rotor.getChildMeshes()], false); }); scene.beginAnimation(rotor, 0, 100, true); }, diff --git a/packages/frontend/src/world/room/objects/lavaLamp.ts b/packages/frontend/src/world/room/objects/lavaLamp.ts index 0619bc7a48..8d5e244bfc 100644 --- a/packages/frontend/src/world/room/objects/lavaLamp.ts +++ b/packages/frontend/src/world/room/objects/lavaLamp.ts @@ -39,7 +39,7 @@ export const lavaLamp = defineObject({ placement: 'top', hasCollisions: false, canPreMeshesMerging: true, - createInstance: ({ options, room, scene, root, model, graphicsQuality }) => { + createInstance: ({ options, room, scene, sr, root, model, graphicsQuality }) => { const bodyMaterial = model.findMaterial('__X_BODY__'); const glassMaterial = model.findMaterial('__X_GLASS__'); const lightMaterial = model.findMaterial('__X_LIGHT__'); @@ -111,7 +111,7 @@ export const lavaLamp = defineObject({ scene.beginAnimation(sphere3, 0, 800, true, 0.6); animationObserver = scene.onAfterAnimationsObservable.add(() => { - room?.sr.updateMesh([sphere, sphere2, sphere3], false); + sr.updateMesh([sphere, sphere2, sphere3], false); }); const emitter = new BABYLON.TransformNode('emitter', scene); @@ -139,7 +139,7 @@ export const lavaLamp = defineObject({ ps.preWarmCycles = 100; ps.start(); - room?.sr.fixParticleSystem(ps); + sr.fixParticleSystem(ps); return { onInited: () => { diff --git a/packages/frontend/src/world/room/objects/radiometer.ts b/packages/frontend/src/world/room/objects/radiometer.ts index 9e23a1223a..816b68592b 100644 --- a/packages/frontend/src/world/room/objects/radiometer.ts +++ b/packages/frontend/src/world/room/objects/radiometer.ts @@ -15,7 +15,7 @@ export const radiometer = defineObject({ }, placement: 'top', hasCollisions: false, - createInstance: ({ room, scene, model }) => { + createInstance: ({ sr, scene, model }) => { const vanes = model.findTransformNode('__X_VANES__'); model.bakeExcludeMeshes = [...vanes.getChildMeshes()]; @@ -31,7 +31,7 @@ export const radiometer = defineObject({ ]); vanes.animations = [anim]; animationObserver = scene.onAfterAnimationsObservable.add(() => { - room?.sr.updateMesh([...vanes.getChildMeshes()], true); + sr.updateMesh([...vanes.getChildMeshes()], true); }); scene.beginAnimation(vanes, 0, 240, true); }, diff --git a/packages/frontend/src/world/room/objects/tabletopDigitalClock.ts b/packages/frontend/src/world/room/objects/tabletopDigitalClock.ts index c3a60f934a..5d58e14320 100644 --- a/packages/frontend/src/world/room/objects/tabletopDigitalClock.ts +++ b/packages/frontend/src/world/room/objects/tabletopDigitalClock.ts @@ -35,7 +35,7 @@ export const tabletopDigitalClock = defineObject({ placement: 'top', hasCollisions: false, canPreMeshesMerging: false, - createInstance: ({ root, room, options, model, scene, timer }) => { + createInstance: ({ sr, options, model, timer }) => { const matrix = model.root.getWorldMatrix(true); const scale = new BABYLON.Vector3(); matrix.decompose(scale); @@ -123,7 +123,7 @@ export const tabletopDigitalClock = defineObject({ mesh.position.y = isVisible ? defaultSegMeshDepth : defaultSegMeshDepth - (cm(2) / Math.abs(scale.y)); } - room?.sr.updateMesh([...Object.values(segmentMeshes), ...colonMeshes]); + sr.updateMesh([...Object.values(segmentMeshes), ...colonMeshes]); }, 1000); }, onOptionsUpdated: ([k, v]) => { diff --git a/packages/frontend/src/world/room/objects/wallClock.ts b/packages/frontend/src/world/room/objects/wallClock.ts index 7546a9f619..3dde5163d5 100644 --- a/packages/frontend/src/world/room/objects/wallClock.ts +++ b/packages/frontend/src/world/room/objects/wallClock.ts @@ -22,7 +22,7 @@ export const wallClock = defineObject({ }, placement: 'side', hasCollisions: false, - createInstance: ({ room, timer, options, model }) => { + createInstance: ({ sr, timer, options, model }) => { const hourHand = model.findMesh('HandH'); const minuteHand = model.findMesh('HandM'); @@ -48,7 +48,7 @@ export const wallClock = defineObject({ const mAngle = -(minutes / 60) * Math.PI * 2; hourHand.rotation = new BABYLON.Vector3(0, 0, hAngle); minuteHand.rotation = new BABYLON.Vector3(0, 0, mAngle); - room?.sr.updateMesh([hourHand, minuteHand], false); + sr.updateMesh([hourHand, minuteHand], false); }, 1000); }, onOptionsUpdated: ([k, v]) => { diff --git a/packages/frontend/src/world/room/previewEngine.ts b/packages/frontend/src/world/room/previewEngine.ts index 40d48505fc..e8550c67bb 100644 --- a/packages/frontend/src/world/room/previewEngine.ts +++ b/packages/frontend/src/world/room/previewEngine.ts @@ -26,6 +26,7 @@ export class RoomObjectPreviewEngine { private canvas: HTMLCanvasElement; private engine: BABYLON.WebGPUEngine; private scene: BABYLON.Scene; + private sr: BABYLON.SnapshotRenderingHelper; private shadowGenerator: BABYLON.ShadowGenerator; private camera: BABYLON.ArcRotateCamera; private objectMesh: BABYLON.Mesh | null = null; @@ -50,10 +51,11 @@ export class RoomObjectPreviewEngine { this.engine = options.engine; this.scene = new BABYLON.Scene(this.engine); - this.scene.clearColor = new BABYLON.Color4(0, 0, 0, 0); - this.scene.autoClear = true; + this.scene.autoClear = false; this.scene.skipPointerMovePicking = true; + this.sr = new BABYLON.SnapshotRenderingHelper(this.scene); + this.camera = new BABYLON.ArcRotateCamera('camera', -Math.PI / 2, Math.PI / 2.5, cm(300), new BABYLON.Vector3(0, cm(90), 0), this.scene); this.envMapIndoor = BABYLON.CubeTexture.CreateFromPrefilteredData('/client-assets/room/indoor.env', this.scene); @@ -95,6 +97,7 @@ export class RoomObjectPreviewEngine { }); gl.intensity = 0.5; this.scene.setRenderingAutoClearDepthStencil(gl.renderingGroupId, false); + this.sr.updateMeshesForEffectLayer(gl); this.pipeline = new BABYLON.DefaultRenderingPipeline('default', true, this.scene); this.pipeline.samples = 4; @@ -188,10 +191,12 @@ export class RoomObjectPreviewEngine { } public async init() { - // 特になし + await this.scene.whenReadyAsync(); + this.sr.enableSnapshotRendering(); } public async load(type: string) { + this.sr.disableSnapshotRendering(); this.clear(); const id = genId(); @@ -254,6 +259,8 @@ export class RoomObjectPreviewEngine { this.pipeline.addCamera(this.camera); + this.sr.enableSnapshotRendering(); + return { id, objectInstance: this.objectInstance, @@ -317,6 +324,10 @@ export class RoomObjectPreviewEngine { const model = new ModelManager(subRoot, loaderResult.meshes.filter(m => !m.isDisposed() && m !== subRoot), def.hasTexture, (meshes) => { updateMeshes(meshes); }); + //model.updatedCallback = () => { + // this.sr.disableSnapshotRendering(); + // this.sr.enableSnapshotRendering(); + //}; updateMeshes(subRoot.getChildMeshes()); @@ -325,6 +336,7 @@ export class RoomObjectPreviewEngine { const objectInstance = await def.createInstance({ room: null, scene: this.scene, + sr: this.sr, root, options: args.options, model, @@ -341,12 +353,15 @@ export class RoomObjectPreviewEngine { } public updateObjectOption(key: string, value: any) { + this.sr.disableSnapshotRendering(); this.objectOptions[key] = value; this.objectInstance?.onOptionsUpdated?.([key, value]); + this.sr.enableSnapshotRendering(); return this.objectOptions; } public clear() { + this.sr.disableSnapshotRendering(); if (this.timerForEachObject != null) { this.timerForEachObject.dispose(); this.timerForEachObject = null; @@ -359,10 +374,20 @@ export class RoomObjectPreviewEngine { this.objectMesh = null; this.objectType = null; } + this.sr.enableSnapshotRendering(); } public resize() { + // 一旦snapshot renderingを無効にしておかないとエラーが出る(babylonのバグ?) + // ~~...が、一旦無効にしたらしたで複数のマテリアルがそれぞれ入れ替わる(?)という謎の現象が発生するためコメントアウトしとく(エラー出てもレンダリングが止まったりするわけでもないし)~~ + // ↑追記: engine.resizeした後に一瞬待つことで回避できることが判明 + this.sr.disableSnapshotRendering(); this.engine.resize(); + // workerで実行される可能性がある + // eslint-disable-next-line no-restricted-globals + setTimeout(() => { + this.sr.enableSnapshotRendering(); + }, 1); } public destroy() { diff --git a/packages/frontend/src/world/room/utility.ts b/packages/frontend/src/world/room/utility.ts index 3c48aa7e6c..3421035ecd 100644 --- a/packages/frontend/src/world/room/utility.ts +++ b/packages/frontend/src/world/room/utility.ts @@ -203,6 +203,7 @@ export function findMaterial(rootMesh: BABYLON.AbstractMesh, keyword: string, al export class ModelManager { public root: BABYLON.Mesh; public bakedCallback: (() => void) | null = null; + public updatedCallback: (() => void) | null = null; public bakeExcludeMeshes: BABYLON.Mesh[] = []; private originalMeshes: BABYLON.Mesh[] = []; private bakedMeshes: BABYLON.Mesh[] = []; @@ -241,6 +242,7 @@ export class ModelManager { } public updated() { + this.updatedCallback?.(); } public bakeMesh() {