diff --git a/packages/frontend/assets/room/objects/aroma-reed-diffuser/aroma-reed-diffuser.blend b/packages/frontend/assets/room/objects/aroma-reed-diffuser/aroma-reed-diffuser.blend index e1c6ddbe4d..ac45f88398 100644 Binary files a/packages/frontend/assets/room/objects/aroma-reed-diffuser/aroma-reed-diffuser.blend and b/packages/frontend/assets/room/objects/aroma-reed-diffuser/aroma-reed-diffuser.blend differ diff --git a/packages/frontend/assets/room/objects/aroma-reed-diffuser/aroma-reed-diffuser.glb b/packages/frontend/assets/room/objects/aroma-reed-diffuser/aroma-reed-diffuser.glb index 0ccd12a307..4129aa8e08 100644 Binary files a/packages/frontend/assets/room/objects/aroma-reed-diffuser/aroma-reed-diffuser.glb and b/packages/frontend/assets/room/objects/aroma-reed-diffuser/aroma-reed-diffuser.glb differ diff --git a/packages/frontend/assets/room/objects/cactus-s/cactus-s.blend b/packages/frontend/assets/room/objects/cactus-s/cactus-s.blend index 2a5735fd46..c0e6f7e9c4 100644 Binary files a/packages/frontend/assets/room/objects/cactus-s/cactus-s.blend and b/packages/frontend/assets/room/objects/cactus-s/cactus-s.blend differ diff --git a/packages/frontend/assets/room/objects/cactus-s/cactus-s.glb b/packages/frontend/assets/room/objects/cactus-s/cactus-s.glb index 8e0e1fcd74..1e2d6423d5 100644 Binary files a/packages/frontend/assets/room/objects/cactus-s/cactus-s.glb and b/packages/frontend/assets/room/objects/cactus-s/cactus-s.glb differ diff --git a/packages/frontend/assets/room/objects/chair/chair.blend b/packages/frontend/assets/room/objects/chair/chair.blend index 93c9fc5019..e781c8a1de 100644 Binary files a/packages/frontend/assets/room/objects/chair/chair.blend and b/packages/frontend/assets/room/objects/chair/chair.blend differ diff --git a/packages/frontend/assets/room/objects/chair/chair.glb b/packages/frontend/assets/room/objects/chair/chair.glb index 0c379a4447..dfccfd79ed 100644 Binary files a/packages/frontend/assets/room/objects/chair/chair.glb and b/packages/frontend/assets/room/objects/chair/chair.glb differ diff --git a/packages/frontend/src/utility/room/engine.ts b/packages/frontend/src/utility/room/engine.ts index e2f82d5c3b..c737e1c248 100644 --- a/packages/frontend/src/utility/room/engine.ts +++ b/packages/frontend/src/utility/room/engine.ts @@ -243,7 +243,12 @@ class ModelManager { const excludeMeshes = [...this.bakeExcludeMeshes, ...this.root.getChildMeshes().filter(m => SYSTEM_MESH_NAMES.some(s => m.name.includes(s)))]; - const childMeshes = this.root.getChildMeshes().filter(m => !excludeMeshes.some(x => x === m) && m.isVisible); + const childMeshes = this.root.getChildMeshes().filter(m => !excludeMeshes.some(x => x === m) && m.isVisible && !m.isDisposed()); + + if (childMeshes.length <= 1) { + this.bakedCallback?.([...childMeshes, ...excludeMeshes]); + return; + } const _toMerge = [] as BABYLON.Mesh[]; for (const mesh of childMeshes) { @@ -301,7 +306,12 @@ class ModelManager { toMerge.push(newMesh); } - const merged = BABYLON.Mesh.MergeMeshes(toMerge, true, true, undefined, false, true); + if (toMerge.length === 0) { + this.bakedCallback?.([...childMeshes, ...excludeMeshes]); + return; + } + + const merged = BABYLON.Mesh.MergeMeshes(toMerge, true, false, undefined, false, true); merged.parent = this.root; merged.material.freeze(); if (merged.material instanceof BABYLON.MultiMaterial) { @@ -333,6 +343,44 @@ class ModelManager { } } +function mergeMeshes(meshes: BABYLON.Mesh[], root: BABYLON.Mesh, hasTexture: boolean) { + const excludeMeshes = root.getChildMeshes().filter(m => SYSTEM_MESH_NAMES.some(s => m.name.includes(s))); + + const childMeshes = root.getChildMeshes().filter(m => !excludeMeshes.some(x => x === m) && m.isVisible && !m.isDisposed()); + + const toMerge = [] as BABYLON.Mesh[]; + for (const mesh of childMeshes) { + if (mesh instanceof BABYLON.InstancedMesh) { + continue; + } + + if (mesh.hasInstances) continue; + + if (mesh instanceof BABYLON.Mesh) { + toMerge.push(mesh); + } + } + + for (const mesh of toMerge) { + if (hasTexture) { + if (mesh.getVerticesData(BABYLON.VertexBuffer.UVKind) == null) { + const vertexCount = mesh.getTotalVertices(); + const uvs = new Array(vertexCount * 2).fill(0); + mesh.setVerticesData(BABYLON.VertexBuffer.UVKind, uvs, false, 2); + } + if (mesh.getVerticesData(BABYLON.VertexBuffer.UV2Kind) == null) { + const vertexCount = mesh.getTotalVertices(); + const uvs = new Array(vertexCount * 2).fill(0); + mesh.setVerticesData(BABYLON.VertexBuffer.UV2Kind, uvs, false, 2); + } + } + } + + const merged = BABYLON.Mesh.MergeMeshes(toMerge, true, false, undefined, false, true); + + return merged; +} + export type ObjectDef = { id: string; name: string; @@ -344,6 +392,7 @@ export type ObjectDef = { placement: 'top' | 'side' | 'bottom' | 'wall' | 'ceiling' | 'floor'; hasCollisions?: boolean; hasTexture?: boolean; + canPreMeshesMerging?: boolean; //groupingMeshes: string[]; // multi-materialなメッシュは複数のメッシュに分割されるが、それだと不便な場合に追加の親メッシュでグルーピングするための指定 isChair?: boolean; treatLoaderResult?: (loaderResult: BABYLON.AssetContainer) => void; @@ -833,13 +882,13 @@ export class RoomEngine extends EventEmitter { // TODO: __PICK__考慮 const pickingInfo = this.scene.pick(this.scene.pointerX, this.scene.pointerY, - (m) => m.metadata?.objectId != null && this.objectEntities.has(m.metadata.objectId)); + (m) => m.name.includes('__PICK__') || (m.isVisible && m.isEnabled() && m.metadata?.objectId != null && this.objectEntities.has(m.metadata.objectId))); if (pickingInfo.pickedMesh != null) { const oid = pickingInfo.pickedMesh.metadata.objectId; if (oid != null && this.objectEntities.has(oid)) { const o = this.objectEntities.get(oid)!; - const boundingInfo = getMeshesBoundingBox(o.rootMesh.getChildMeshes().filter(m => m.isEnabled() && m.isVisible)); + const boundingInfo = getMeshesBoundingBox(o.rootMesh.getChildMeshes().filter(m => m.isEnabled() && m.isVisible && !m.isDisposed())); this.selectObject(oid); { // camera animation @@ -1169,6 +1218,19 @@ export class RoomEngine extends EventEmitter { } } + if (def.canPreMeshesMerging) { + const merged = mergeMeshes(loaderResult.meshes, subRoot, def.hasTexture); + merged.setParent(subRoot); + merged.name = 'preMerged'; + + // TODO: 再帰的にする + for (const m of loaderResult.transformNodes) { + if (m.getChildren().length === 0) { + m.dispose(); + } + } + } + if (BAKE_TRANSFORM) { subRoot.scaling = new BABYLON.Vector3(1, 1, 1); subRoot.rotationQuaternion = null; @@ -1330,7 +1392,7 @@ export class RoomEngine extends EventEmitter { root.rotation = args.rotation.clone(); root.metadata = metadata; - const model = new ModelManager(BAKE_TRANSFORM ? root : subRoot, loaderResult.meshes.filter(m => m.name !== '__root__'), def.hasTexture, (meshes) => { + const model = new ModelManager(BAKE_TRANSFORM ? root : subRoot, loaderResult.meshes.filter(m => !m.isDisposed() && m.name !== '__root__'), def.hasTexture, (meshes) => { if (this.selected?.objectId === args.id) { this.highlightMeshes(meshes); } @@ -2082,6 +2144,20 @@ export class RoomObjectPreviewEngine { const filePath = def.path != null ? `/client-assets/room/objects/${def.path}.glb` : `/client-assets/room/objects/${camelToKebab(args.type)}/${camelToKebab(args.type)}.glb`; const loaderResult = await BABYLON.LoadAssetContainerAsync(filePath, this.scene); + // 不要なUVを掃除 + if (!def.hasTexture) { + for (const m of loaderResult.meshes) { + if (m.geometry != null) { + m.geometry.removeVerticesData(BABYLON.VertexBuffer.UVKind); + m.geometry.removeVerticesData(BABYLON.VertexBuffer.UV2Kind); + m.geometry.removeVerticesData(BABYLON.VertexBuffer.UV3Kind); + m.geometry.removeVerticesData(BABYLON.VertexBuffer.UV4Kind); + m.geometry.removeVerticesData(BABYLON.VertexBuffer.UV5Kind); + m.geometry.removeVerticesData(BABYLON.VertexBuffer.UV6Kind); + } + } + } + // babylonによって自動で追加される右手系変換用ノード const subRoot = loaderResult.meshes[0]; subRoot.scaling = subRoot.scaling.scale(WORLD_SCALE);// cmをmに @@ -2090,7 +2166,7 @@ export class RoomObjectPreviewEngine { root.addChild(subRoot); - const model = new ModelManager(subRoot, loaderResult.meshes.filter(m => m !== subRoot), def.hasTexture, (meshes) => { + const model = new ModelManager(subRoot, loaderResult.meshes.filter(m => !m.isDisposed() && m !== subRoot), def.hasTexture, (meshes) => { for (const m of meshes) { const mesh = m; diff --git a/packages/frontend/src/utility/room/objects/aircon.ts b/packages/frontend/src/utility/room/objects/aircon.ts index 547a72da0e..12044454a7 100644 --- a/packages/frontend/src/utility/room/objects/aircon.ts +++ b/packages/frontend/src/utility/room/objects/aircon.ts @@ -14,6 +14,7 @@ export const aircon = defineObject({ }, placement: 'wall', hasCollisions: false, + canPreMeshesMerging: true, createInstance: () => { return { interactions: {}, diff --git a/packages/frontend/src/utility/room/objects/aromaReedDiffuser.ts b/packages/frontend/src/utility/room/objects/aromaReedDiffuser.ts index c297d5d7c7..fa1f941b4c 100644 --- a/packages/frontend/src/utility/room/objects/aromaReedDiffuser.ts +++ b/packages/frontend/src/utility/room/objects/aromaReedDiffuser.ts @@ -28,12 +28,10 @@ export const aromaReedDiffuser = defineObject({ placement: 'top', hasCollisions: false, hasTexture: true, + canPreMeshesMerging: true, createInstance: ({ options, model }) => { - const bottleMesh = model.findMesh('__X_BOTTLE__'); - const bottleMaterial = bottleMesh.material as BABYLON.PBRMaterial; - - const oilMesh = model.findMesh('__X_OIL__'); - const oilMaterial = oilMesh.material as BABYLON.PBRMaterial; + const bottleMaterial = model.findMaterial('__X_BOTTLE__'); + const oilMaterial = model.findMaterial('__X_OIL__'); const applyBottleColor = () => { const [r, g, b] = options.bottleColor; diff --git a/packages/frontend/src/utility/room/objects/banknote.ts b/packages/frontend/src/utility/room/objects/banknote.ts index 23f8cd728d..75cc5d5c44 100644 --- a/packages/frontend/src/utility/room/objects/banknote.ts +++ b/packages/frontend/src/utility/room/objects/banknote.ts @@ -15,6 +15,7 @@ export const banknote = defineObject({ placement: 'top', hasCollisions: false, hasTexture: true, + canPreMeshesMerging: true, createInstance: () => { return { interactions: {}, diff --git a/packages/frontend/src/utility/room/objects/beamLamp.ts b/packages/frontend/src/utility/room/objects/beamLamp.ts index 1011532da3..85b29fd6b1 100644 --- a/packages/frontend/src/utility/room/objects/beamLamp.ts +++ b/packages/frontend/src/utility/room/objects/beamLamp.ts @@ -16,6 +16,7 @@ export const beamLamp = defineObject({ }, placement: 'top', hasCollisions: false, + canPreMeshesMerging: true, createInstance: ({ room, root, scene }) => { return { onInited: () => { diff --git a/packages/frontend/src/utility/room/objects/cactusS.ts b/packages/frontend/src/utility/room/objects/cactusS.ts index f79f5bd314..e16bb4fdb1 100644 --- a/packages/frontend/src/utility/room/objects/cactusS.ts +++ b/packages/frontend/src/utility/room/objects/cactusS.ts @@ -23,9 +23,9 @@ export const cactusS = defineObject({ placement: 'top', hasCollisions: false, hasTexture: true, + canPreMeshesMerging: true, createInstance: ({ options, model }) => { - const potMesh = model.findMesh('__X_POT__'); - const potMaterial = potMesh.material as BABYLON.PBRMaterial; + const potMaterial = model.findMaterial('__X_POT__'); const applyPotColor = () => { const [r, g, b] = options.potColor; diff --git a/packages/frontend/src/utility/room/objects/chair.ts b/packages/frontend/src/utility/room/objects/chair.ts index e8a43c0ae3..1ade6539b7 100644 --- a/packages/frontend/src/utility/room/objects/chair.ts +++ b/packages/frontend/src/utility/room/objects/chair.ts @@ -28,12 +28,10 @@ export const chair = defineObject({ placement: 'floor', hasCollisions: true, isChair: true, + canPreMeshesMerging: true, createInstance: ({ model, options }) => { - const primaryMesh = model.findMesh('__X_PRIMARY__'); - const primaryMaterial = primaryMesh.material as BABYLON.PBRMaterial; - - const secondaryMesh = model.findMesh('__X_SECONDARY__'); - const secondaryMaterial = secondaryMesh.material as BABYLON.PBRMaterial; + const primaryMaterial = model.findMaterial('__X_PRIMARY__'); + const secondaryMaterial = model.findMaterial('__X_SECONDARY__'); const applyPrimaryColor = () => { const [r, g, b] = options.primaryColor; diff --git a/packages/frontend/src/utility/room/objects/coffeeCup.ts b/packages/frontend/src/utility/room/objects/coffeeCup.ts index 7b6e4e2c91..a38cd8d1a7 100644 --- a/packages/frontend/src/utility/room/objects/coffeeCup.ts +++ b/packages/frontend/src/utility/room/objects/coffeeCup.ts @@ -15,6 +15,7 @@ export const coffeeCup = defineObject({ placement: 'top', hasCollisions: false, hasTexture: true, + canPreMeshesMerging: true, createInstance: () => { return { interactions: {}, diff --git a/packages/frontend/src/utility/room/objects/cupNoodle.ts b/packages/frontend/src/utility/room/objects/cupNoodle.ts index 13333d919e..a3cb80486d 100644 --- a/packages/frontend/src/utility/room/objects/cupNoodle.ts +++ b/packages/frontend/src/utility/room/objects/cupNoodle.ts @@ -17,6 +17,7 @@ export const cupNoodle = defineObject({ placement: 'top', hasCollisions: false, hasTexture: true, + canPreMeshesMerging: true, createInstance: ({ scene, root }) => { let yugeDispose: (() => void) | null = null; diff --git a/packages/frontend/src/utility/room/objects/custardPudding.ts b/packages/frontend/src/utility/room/objects/custardPudding.ts index 743a5449bf..dfd007fcf5 100644 --- a/packages/frontend/src/utility/room/objects/custardPudding.ts +++ b/packages/frontend/src/utility/room/objects/custardPudding.ts @@ -14,6 +14,7 @@ export const custardPudding = defineObject({ }, placement: 'top', hasCollisions: false, + canPreMeshesMerging: true, createInstance: () => { return { interactions: {}, diff --git a/packages/frontend/src/utility/room/objects/desktopPc.ts b/packages/frontend/src/utility/room/objects/desktopPc.ts index 320b96d359..5cb59ed075 100644 --- a/packages/frontend/src/utility/room/objects/desktopPc.ts +++ b/packages/frontend/src/utility/room/objects/desktopPc.ts @@ -48,6 +48,7 @@ export const desktopPc = defineObject({ }, placement: 'top', hasCollisions: true, + canPreMeshesMerging: true, createInstance: ({ options, model, root, scene, room }) => { const light1 = new BABYLON.SpotLight('', new BABYLON.Vector3(0, cm(10), cm(22)), new BABYLON.Vector3(0, 0, 1), Math.PI / 1, 2, scene, room?.lightContainer != null); light1.parent = root; diff --git a/packages/frontend/src/utility/room/objects/djMixer.ts b/packages/frontend/src/utility/room/objects/djMixer.ts index 679844c6ea..51ec3734aa 100644 --- a/packages/frontend/src/utility/room/objects/djMixer.ts +++ b/packages/frontend/src/utility/room/objects/djMixer.ts @@ -15,6 +15,7 @@ export const djMixer = defineObject({ placement: 'top', hasCollisions: false, hasTexture: true, + canPreMeshesMerging: true, createInstance: () => { return { interactions: {}, diff --git a/packages/frontend/src/utility/room/objects/facialTissue.ts b/packages/frontend/src/utility/room/objects/facialTissue.ts index c2ee98371b..5285bb828c 100644 --- a/packages/frontend/src/utility/room/objects/facialTissue.ts +++ b/packages/frontend/src/utility/room/objects/facialTissue.ts @@ -15,6 +15,7 @@ export const facialTissue = defineObject({ placement: 'top', hasCollisions: false, hasTexture: true, + canPreMeshesMerging: true, createInstance: () => { return { interactions: {}, diff --git a/packages/frontend/src/utility/room/objects/hangingTShirt.ts b/packages/frontend/src/utility/room/objects/hangingTShirt.ts index be1a578921..822afcc081 100644 --- a/packages/frontend/src/utility/room/objects/hangingTShirt.ts +++ b/packages/frontend/src/utility/room/objects/hangingTShirt.ts @@ -15,6 +15,7 @@ export const hangingTShirt = defineObject({ placement: 'side', hasCollisions: false, hasTexture: true, + canPreMeshesMerging: true, createInstance: () => { return { interactions: {}, diff --git a/packages/frontend/src/utility/room/objects/keyboard.ts b/packages/frontend/src/utility/room/objects/keyboard.ts index 8e5095b4ab..cbd860a884 100644 --- a/packages/frontend/src/utility/room/objects/keyboard.ts +++ b/packages/frontend/src/utility/room/objects/keyboard.ts @@ -14,6 +14,7 @@ export const keyboard = defineObject({ }, placement: 'top', hasCollisions: false, + canPreMeshesMerging: true, createInstance: () => { return { interactions: {}, diff --git a/packages/frontend/src/utility/room/objects/letterCase.ts b/packages/frontend/src/utility/room/objects/letterCase.ts index 290e75af70..3d3a6eab71 100644 --- a/packages/frontend/src/utility/room/objects/letterCase.ts +++ b/packages/frontend/src/utility/room/objects/letterCase.ts @@ -13,6 +13,7 @@ export const letterCase = defineObject({ default: {}, }, placement: 'top', + canPreMeshesMerging: true, createInstance: () => { return { interactions: {}, diff --git a/packages/frontend/src/utility/room/objects/miPlate.ts b/packages/frontend/src/utility/room/objects/miPlate.ts index 5dc451bbe8..d2c10053fb 100644 --- a/packages/frontend/src/utility/room/objects/miPlate.ts +++ b/packages/frontend/src/utility/room/objects/miPlate.ts @@ -14,6 +14,7 @@ export const miPlate = defineObject({ }, placement: 'top', hasCollisions: false, + canPreMeshesMerging: true, createInstance: () => { return { interactions: {}, diff --git a/packages/frontend/src/utility/room/objects/miPlateDisplayed.ts b/packages/frontend/src/utility/room/objects/miPlateDisplayed.ts index 63329511ee..d1bdfc926a 100644 --- a/packages/frontend/src/utility/room/objects/miPlateDisplayed.ts +++ b/packages/frontend/src/utility/room/objects/miPlateDisplayed.ts @@ -14,6 +14,7 @@ export const miPlateDisplayed = defineObject({ }, placement: 'top', hasCollisions: false, + canPreMeshesMerging: true, createInstance: () => { return { interactions: {}, diff --git a/packages/frontend/src/utility/room/objects/pachira.ts b/packages/frontend/src/utility/room/objects/pachira.ts index ebf3f3d03f..4f5f191b05 100644 --- a/packages/frontend/src/utility/room/objects/pachira.ts +++ b/packages/frontend/src/utility/room/objects/pachira.ts @@ -15,6 +15,7 @@ export const pachira = defineObject({ placement: 'top', hasCollisions: true, hasTexture: true, + canPreMeshesMerging: true, createInstance: () => { return { interactions: {}, diff --git a/packages/frontend/src/utility/room/objects/piano.ts b/packages/frontend/src/utility/room/objects/piano.ts index 992650f0f4..da3875c04f 100644 --- a/packages/frontend/src/utility/room/objects/piano.ts +++ b/packages/frontend/src/utility/room/objects/piano.ts @@ -22,6 +22,7 @@ export const piano = defineObject({ }, placement: 'floor', hasCollisions: true, + canPreMeshesMerging: true, createInstance: ({ options, model }) => { const bodyMaterial = model.findMaterial('__X_BODY__'); diff --git a/packages/frontend/src/utility/room/objects/pizza.ts b/packages/frontend/src/utility/room/objects/pizza.ts index 22827503f0..2e93d58f89 100644 --- a/packages/frontend/src/utility/room/objects/pizza.ts +++ b/packages/frontend/src/utility/room/objects/pizza.ts @@ -14,6 +14,7 @@ export const pizza = defineObject({ }, placement: 'top', hasCollisions: false, + canPreMeshesMerging: true, createInstance: () => { return { interactions: {}, diff --git a/packages/frontend/src/utility/room/objects/plant.ts b/packages/frontend/src/utility/room/objects/plant.ts index 2c50158d9c..ba064e07fc 100644 --- a/packages/frontend/src/utility/room/objects/plant.ts +++ b/packages/frontend/src/utility/room/objects/plant.ts @@ -15,6 +15,7 @@ export const plant = defineObject({ placement: 'top', hasCollisions: false, hasTexture: true, + canPreMeshesMerging: true, createInstance: () => { return { interactions: {}, diff --git a/packages/frontend/src/utility/room/objects/tabletopCalendar.ts b/packages/frontend/src/utility/room/objects/tabletopCalendar.ts index d2a01e6199..a3ee36f77c 100644 --- a/packages/frontend/src/utility/room/objects/tabletopCalendar.ts +++ b/packages/frontend/src/utility/room/objects/tabletopCalendar.ts @@ -15,6 +15,7 @@ export const tabletopCalendar = defineObject({ placement: 'top', hasCollisions: false, hasTexture: true, + canPreMeshesMerging: true, createInstance: () => { return { interactions: {}, diff --git a/packages/frontend/src/utility/room/utility.ts b/packages/frontend/src/utility/room/utility.ts index 2cce48f6e6..475178121c 100644 --- a/packages/frontend/src/utility/room/utility.ts +++ b/packages/frontend/src/utility/room/utility.ts @@ -417,18 +417,26 @@ export function createPlaneUvMapper(mesh: BABYLON.Mesh) { }; } -export function findMaterial(rootMesh: BABYLON.AbstractMesh, keyword: string): BABYLON.PBRMaterial { +export function findMaterial(rootMesh: BABYLON.AbstractMesh, keyword: string, allowMultiMaterial = false): BABYLON.PBRMaterial { for (const m of rootMesh.getChildMeshes()) { if (m.material == null) continue; - if (m.material.name.includes(keyword)) { - return m.material as BABYLON.PBRMaterial; - } else if ((m.material as BABYLON.MultiMaterial).subMaterials != null) { + if (m.material instanceof BABYLON.MultiMaterial) { + if (allowMultiMaterial && m.material.name.includes(keyword)) { + return m.material as BABYLON.MultiMaterial; + } + + if ((m.material as BABYLON.MultiMaterial).subMaterials == null) continue; + for (const sm of (m.material as BABYLON.MultiMaterial).subMaterials) { if (sm == null) continue; if (sm.name.includes(keyword)) { return sm as BABYLON.PBRMaterial; } } + } else { + if (m.material.name.includes(keyword)) { + return m.material as BABYLON.PBRMaterial; + } } } throw new Error(`Material with keyword "${keyword}" not found`);