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
{ const c = getRgb(v); if (c != null) engine.updateObjectOption(engine.selected.value.objectId, k, c); }">
+
+ engine.updateObjectOption(engine.selected.value.objectId, k, v)">
+
engine.updateObjectOption(engine.selected.value.objectId, k, v)">
@@ -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);
+}