diff --git a/packages/frontend/assets/room/objects/pet-bottle/pet-bottle.blend b/packages/frontend/assets/room/objects/pet-bottle/pet-bottle.blend index 903226567f..fc11beb20d 100644 Binary files a/packages/frontend/assets/room/objects/pet-bottle/pet-bottle.blend and b/packages/frontend/assets/room/objects/pet-bottle/pet-bottle.blend differ diff --git a/packages/frontend/assets/room/objects/pet-bottle/pet-bottle.glb b/packages/frontend/assets/room/objects/pet-bottle/pet-bottle.glb index e1bb3863f7..34724d7e83 100644 Binary files a/packages/frontend/assets/room/objects/pet-bottle/pet-bottle.glb and b/packages/frontend/assets/room/objects/pet-bottle/pet-bottle.glb differ diff --git a/packages/frontend/assets/room/objects/pet-bottle/textures/label.png b/packages/frontend/assets/room/objects/pet-bottle/textures/label.png index 60280d157b..eaf63bb16c 100644 Binary files a/packages/frontend/assets/room/objects/pet-bottle/textures/label.png and b/packages/frontend/assets/room/objects/pet-bottle/textures/label.png differ diff --git a/packages/frontend/src/pages/room.vue b/packages/frontend/src/pages/room.vue index 497d616f3c..8f5171b1c3 100644 --- a/packages/frontend/src/pages/room.vue +++ b/packages/frontend/src/pages/room.vue @@ -33,6 +33,9 @@ SPDX-License-Identifier: AGPL-3.0-only
+
+ +
@@ -63,6 +66,7 @@ import { getObjectDef, OBJECT_DEFS } from '@/utility/room/object-defs.js'; import MkSelect from '@/components/MkSelect.vue'; import * as os from '@/os.js'; import MkInput from '@/components/MkInput.vue'; +import MkSwitch from '@/components/MkSwitch.vue'; const canvas = useTemplateRef('canvas'); diff --git a/packages/frontend/src/utility/room/engine.ts b/packages/frontend/src/utility/room/engine.ts index bedea70bcb..719e102edf 100644 --- a/packages/frontend/src/utility/room/engine.ts +++ b/packages/frontend/src/utility/room/engine.ts @@ -73,6 +73,11 @@ type NumberOptionSchema = { step?: number; }; +type BooleanOptionSchema = { + type: 'boolean'; + label: string; +}; + type ColorOptionSchema = { type: 'color'; label: string; @@ -84,10 +89,10 @@ type EnumOptionSchema = { enum: string[]; }; -type OptionsSchema = Record; +type OptionsSchema = Record; type GetOptionsSchemaValues = { - [K in keyof T]: T[K] extends NumberOptionSchema ? number : T[K] extends ColorOptionSchema ? [number, number, number] : T[K] extends EnumOptionSchema ? T[K]['enum'][number] : never; + [K in keyof T]: T[K] extends NumberOptionSchema ? number : T[K] extends BooleanOptionSchema ? boolean : T[K] extends ColorOptionSchema ? [number, number, number] : T[K] extends EnumOptionSchema ? T[K]['enum'][number] : never; }; type ObjectDef = { @@ -496,62 +501,6 @@ export class RoomEngine { //const sphere = BABYLON.MeshBuilder.CreateSphere('sphere', { diameter: 1/*cm*/ }, this.scene); - // update tv texure - const tvProgramId = 'shopping'; - const tvProgram = TV_PROGRAMS[tvProgramId]; - const tvScreenMaterial = new BABYLON.StandardMaterial('tvScreenMaterial', this.scene); - tvScreenMaterial.diffuseColor = new BABYLON.Color3(0, 0, 0); - tvScreenMaterial.ambientColor = new BABYLON.Color3(0, 0, 0); - tvScreenMaterial.specularColor = new BABYLON.Color3(0, 0, 0); - tvScreenMaterial.emissiveTexture = new BABYLON.Texture(`/client-assets/room/tv/${tvProgramId}/${tvProgramId}.png`, this.scene, false, false); - tvScreenMaterial.emissiveTexture.level = 0.5; - tvScreenMaterial.emissiveColor = new BABYLON.Color3(0.4, 0.4, 0.4); - tvScreenMaterial.freeze(); - - const applyTvTexture = (tlIndex: number) => { - const [index, duration] = tvProgram.timeline[tlIndex]; - const tvIds = this.roomState.installedObjects.entries().filter(([id, o]) => o.type === 'tv').map(([id, o]) => o.id); - - for (const tvId of tvIds) { - const tvMesh = this.objectMeshs.get(tvId); - const screenMesh = tvMesh?.getChildMeshes().find(m => m.name.includes('__TV_SCREEN__'))! as BABYLON.Mesh; - screenMesh.material = tvScreenMaterial; - - const aspect = 16 / 9; - - const x = index % tvProgram.textureColumns; - const y = Math.floor(index / tvProgram.textureColumns); - - const ax = x / tvProgram.textureColumns; - const ay = y / tvProgram.textureRows / aspect; - const bx = (x + 1) / tvProgram.textureColumns; - const by = ay; - const cx = ax; - const cy = (y + 1) / tvProgram.textureRows / aspect; - const dx = bx; - const dy = cy; - - const uvs = screenMesh.getVerticesData(BABYLON.VertexBuffer.UVKind); - uvs[0] = dx; - uvs[1] = dy; - uvs[2] = bx; - uvs[3] = by; - uvs[4] = cx; - uvs[5] = cy; - uvs[6] = ax; - uvs[7] = ay; - screenMesh.updateVerticesData(BABYLON.VertexBuffer.UVKind, uvs); - } - - const timeoutId = window.setTimeout(() => { - this.timeoutIds = this.timeoutIds.filter(id => id !== timeoutId); - applyTvTexture((tlIndex + 1) % tvProgram.timeline.length); - }, duration); - this.timeoutIds.push(timeoutId); - }; - - applyTvTexture(0); - this.engine.runRenderLoop(() => { this.scene.render(); }); @@ -874,10 +823,6 @@ export class RoomEngine { mesh.metadata = metadata; mesh.checkCollisions = !hasCollisionMesh; - if (mesh.name.includes('__TV_SCREEN__')) { - mesh.markVerticesDataAsUpdatable(BABYLON.VertexBuffer.UVKind, true); - } - if (mesh.name.includes('__COLLISION__')) { mesh.receiveShadows = false; mesh.isVisible = false; @@ -1252,40 +1197,3 @@ export class RoomEngine { this.engine.dispose(); } } - -const TV_PROGRAMS = { - shopping: { - textureColumns: 8, - textureRows: 8, - timeline: [ - [0, 500], - [1, 500], - [0, 500], - [1, 500], - [0, 500], - [1, 500], - [2, 500], - [3, 500], - [2, 500], - [3, 500], - [4, 500], - [5, 500], - [4, 500], - [5, 500], - [6, 500], - [7, 500], - [8, 500], - [9, 500], - [8, 500], - [9, 500], - [2, 500], - [3, 500], - [2, 500], - [3, 500], - ], - }, -} satisfies Record; diff --git a/packages/frontend/src/utility/room/objects/petBottle.ts b/packages/frontend/src/utility/room/objects/petBottle.ts index e3d9f4b763..5b2ca3fbdb 100644 --- a/packages/frontend/src/utility/room/objects/petBottle.ts +++ b/packages/frontend/src/utility/room/objects/petBottle.ts @@ -3,18 +3,37 @@ * SPDX-License-Identifier: AGPL-3.0-only */ +import * as BABYLON from '@babylonjs/core'; import { defineObject } from '../engine.js'; export const petBottle = defineObject({ id: 'petBottle', name: 'PET Bottle', options: { - schema: {}, - default: {}, + schema: { + withCap: { + type: 'boolean', + label: 'With Cap', + }, + }, + default: { + withCap: true, + }, }, placement: 'top', - createInstance: () => { + createInstance: ({ root, options }) => { + const capMesh = root.getChildMeshes().find(m => m.name.includes('__X_CAP__')) as BABYLON.Mesh; + + const applyWithCap = () => { + capMesh.setEnabled(options.withCap); + }; + + applyWithCap(); + return { + onOptionsUpdated: ([k, v]) => { + applyWithCap(); + }, interactions: {}, }; }, diff --git a/packages/frontend/src/utility/room/objects/tv.ts b/packages/frontend/src/utility/room/objects/tv.ts index 6e46527039..b84b21538f 100644 --- a/packages/frontend/src/utility/room/objects/tv.ts +++ b/packages/frontend/src/utility/room/objects/tv.ts @@ -3,7 +3,9 @@ * SPDX-License-Identifier: AGPL-3.0-only */ +import * as BABYLON from '@babylonjs/core'; import { defineObject } from '../engine.js'; +import { initTv } from '../utility.js'; export const tv = defineObject({ id: 'tv', @@ -13,7 +15,12 @@ export const tv = defineObject({ default: {}, }, placement: 'top', - createInstance: () => { + createInstance: ({ room, root }) => { + const screenMesh = root.getChildMeshes().find(m => m.name.includes('__TV_SCREEN__')) as BABYLON.Mesh; + screenMesh.markVerticesDataAsUpdatable(BABYLON.VertexBuffer.UVKind, true); + + initTv(room, screenMesh); + return { interactions: {}, }; diff --git a/packages/frontend/src/utility/room/utility.ts b/packages/frontend/src/utility/room/utility.ts index b96689e76c..ebe9445f06 100644 --- a/packages/frontend/src/utility/room/utility.ts +++ b/packages/frontend/src/utility/room/utility.ts @@ -238,3 +238,96 @@ export function createOverridedStates any)>>(sta return result; } + +const TV_PROGRAMS = { + shopping: { + textureColumns: 8, + textureRows: 8, + timeline: [ + [0, 500], + [1, 500], + [0, 500], + [1, 500], + [0, 500], + [1, 500], + [2, 500], + [3, 500], + [2, 500], + [3, 500], + [4, 500], + [5, 500], + [4, 500], + [5, 500], + [6, 500], + [7, 500], + [8, 500], + [9, 500], + [8, 500], + [9, 500], + [2, 500], + [3, 500], + [2, 500], + [3, 500], + ], + }, +} satisfies Record; + +let tvScreenMaterial: BABYLON.StandardMaterial | null = null; + +export function initTv(room: RoomEngine, screenMesh: BABYLON.Mesh) { + const tvProgramId = 'shopping'; + const tvProgram = TV_PROGRAMS[tvProgramId]; + if (tvScreenMaterial == null) { + tvScreenMaterial = new BABYLON.StandardMaterial('tvScreenMaterial', room.scene); + tvScreenMaterial.diffuseColor = new BABYLON.Color3(0, 0, 0); + tvScreenMaterial.ambientColor = new BABYLON.Color3(0, 0, 0); + tvScreenMaterial.specularColor = new BABYLON.Color3(0, 0, 0); + tvScreenMaterial.emissiveTexture = new BABYLON.Texture(`/client-assets/room/tv/${tvProgramId}/${tvProgramId}.png`, room.scene, false, false); + tvScreenMaterial.emissiveTexture.level = 0.5; + tvScreenMaterial.emissiveColor = new BABYLON.Color3(0.4, 0.4, 0.4); + tvScreenMaterial.freeze(); + } + + const applyTvTexture = (tlIndex: number) => { + const [index, duration] = tvProgram.timeline[tlIndex]; + + screenMesh.material = tvScreenMaterial; + + const aspect = 16 / 9; + + const x = index % tvProgram.textureColumns; + const y = Math.floor(index / tvProgram.textureColumns); + + const ax = x / tvProgram.textureColumns; + const ay = y / tvProgram.textureRows / aspect; + const bx = (x + 1) / tvProgram.textureColumns; + const by = ay; + const cx = ax; + const cy = (y + 1) / tvProgram.textureRows / aspect; + const dx = bx; + const dy = cy; + + const uvs = screenMesh.getVerticesData(BABYLON.VertexBuffer.UVKind); + uvs[0] = dx; + uvs[1] = dy; + uvs[2] = bx; + uvs[3] = by; + uvs[4] = cx; + uvs[5] = cy; + uvs[6] = ax; + uvs[7] = ay; + screenMesh.updateVerticesData(BABYLON.VertexBuffer.UVKind, uvs); + + const timeoutId = window.setTimeout(() => { + room.timeoutIds = room.timeoutIds.filter(id => id !== timeoutId); + applyTvTexture((tlIndex + 1) % tvProgram.timeline.length); + }, duration); + room.timeoutIds.push(timeoutId); + }; + + applyTvTexture(0); +}