diff --git a/packages/frontend/src/pages/room.vue b/packages/frontend/src/pages/room.vue index 08f1e50c7f..4a812fdafd 100644 --- a/packages/frontend/src/pages/room.vue +++ b/packages/frontend/src/pages/room.vue @@ -51,9 +51,9 @@ function resize() { onMounted(() => { engine.value = new RoomEngine({ roomType: 'default', - objects: [/*{ + objects: [{ id: 'a', - type: 'cardboard-box', + type: 'cardboardBox', position: [120, 0, 50], rotation: [0, 0.2, 0], options: { @@ -61,13 +61,13 @@ onMounted(() => { }, }, { id: 'a2', - type: 'opened-cardboard-box', + type: 'openedCardboardBox', position: [115, 0, -20], rotation: [0, -0.1, 0], options: {}, }, { id: 'b', - type: 'cardboard-box', + type: 'cardboardBox', position: [120, 31, 50], rotation: [0, 0.1, 0], sticky: 'a', @@ -76,7 +76,7 @@ onMounted(() => { }, }, { id: '1', - type: 'cardboard-box', + type: 'cardboardBox', position: [80, 0, 110], rotation: [0, 2, 0], options: { @@ -140,13 +140,13 @@ onMounted(() => { options: {}, }, { id: 'i', - type: 'lava-lamp', + type: 'lavaLamp', position: [60, 90, 170], rotation: [0, 0, 0], options: {}, }, { id: 'j', - type: 'steel-rack', + type: 'steelRack', position: [130, 0, 115], rotation: [0, Math.PI / 2, 0], options: {}, @@ -159,14 +159,14 @@ onMounted(() => { options: {}, }, { id: 'j3', - type: 'power-strip', + type: 'powerStrip', position: [130, 13, 115], rotation: [0, Math.PI / 2, 0], sticky: 'j', options: {}, }, { id: 'k', - type: 'cup-noodle', + type: 'cupNoodle', position: [-100, 70, 40], rotation: [0, -2, 0], sticky: 'c', @@ -180,7 +180,7 @@ onMounted(() => { options: {}, }, { id: 'm', - type: 'energy-drink', + type: 'energyDrink', position: [-100, 70, 120], rotation: [0, -1, 0], sticky: 'c', @@ -194,7 +194,7 @@ onMounted(() => { options: {}, }, { id: 'o', - type: 'facial-tissue', + type: 'facialTissue', position: [-100, 70, 138], rotation: [0, -1.5, 0], sticky: 'c', @@ -207,7 +207,7 @@ onMounted(() => { options: {}, }, { id: 'q', - type: 'color-box', + type: 'colorBox', position: [-135, 0, -5], rotation: [0, -Math.PI / 2, 0], options: {}, @@ -219,13 +219,13 @@ onMounted(() => { options: {}, }, { id: 's', - type: 'wall-clock', + type: 'wallClock', position: [-150, 200, 100], rotation: [0, -Math.PI / 2, 0], options: {}, }, { id: 's2', - type: 'wood-sound-absorbing-panel', + type: 'woodSoundAbsorbingPanel', position: [-150, 140, 80], rotation: [0, -Math.PI / 2, 0], options: {}, @@ -245,38 +245,38 @@ onMounted(() => { options: {}, }, { id: 'v', - type: 'ceiling-fan-light', + type: 'ceilingFanLight', position: [0, 250, 0], rotation: [0, 0, 0], isMainLight: true, options: {}, }, { id: 'w', - type: 'round-rug', + type: 'roundRug', position: [0, 0, 0], rotation: [0, 0, 0], options: {}, - }, */{ - id: 'x', - type: 'blind', - position: [-35, 194, 185], - rotation: [0, Math.PI, 0], - options: { - blades: 24, - angle: 0.5, - open: 0.8, - }, - }, { - id: 'x2', - type: 'blind', - position: [35, 194, 185], - rotation: [0, Math.PI, 0], - options: { - blades: 24, - angle: -0.4, - open: 0.5, - }, - }], + }, { + id: 'x', + type: 'blind', + position: [-35, 194, 185], + rotation: [0, Math.PI, 0], + options: { + blades: 24, + angle: 0.5, + open: 0.8, + }, + }, { + id: 'x2', + type: 'blind', + position: [35, 194, 185], + rotation: [0, Math.PI, 0], + options: { + blades: 24, + angle: -0.4, + open: 0.5, + }, + }], }, { canvas: canvas.value!, }); diff --git a/packages/frontend/src/utility/room/engine.ts b/packages/frontend/src/utility/room/engine.ts index eb951a6f6c..04bb445112 100644 --- a/packages/frontend/src/utility/room/engine.ts +++ b/packages/frontend/src/utility/room/engine.ts @@ -44,32 +44,6 @@ type RoomSetting = { objects: RoomSettingObject[]; }; -function yuge(room: RoomEngine, mesh: BABYLON.Mesh, offset: BABYLON.Vector3) { - const emitter = new BABYLON.TransformNode('emitter', room.scene); - emitter.parent = mesh; - emitter.position = offset; - const ps = new BABYLON.ParticleSystem('steamParticleSystem', 8, room.scene); - ps.particleTexture = new BABYLON.Texture('/client-assets/room/steam.png'); - ps.emitter = emitter; - ps.minEmitBox = new BABYLON.Vector3(-1/*cm*/, 0, -1/*cm*/); - ps.maxEmitBox = new BABYLON.Vector3(1/*cm*/, 0, 1/*cm*/); - ps.minEmitPower = 10; - ps.maxEmitPower = 12; - ps.minLifeTime = 2; - ps.maxLifeTime = 3; - ps.addSizeGradient(0, 10/*cm*/, 12/*cm*/); - ps.addSizeGradient(1, 18/*cm*/, 20/*cm*/); - ps.direction1 = new BABYLON.Vector3(-0.3, 1, 0.3); - ps.direction2 = new BABYLON.Vector3(0.3, 1, -0.3); - ps.emitRate = 0.5; - ps.blendMode = BABYLON.ParticleSystem.BLENDMODE_ADD; - ps.color1 = new BABYLON.Color4(1, 1, 1, 0.3); - ps.color2 = new BABYLON.Color4(1, 1, 1, 0.2); - ps.colorDead = new BABYLON.Color4(1, 1, 1, 0); - ps.preWarmCycles = Math.random() * 1000; - ps.start(); -} - type RoomObjectInstance = { onInited?: (room: RoomEngine, o: RoomSettingObject, rootNode: BABYLON.Mesh) => void; interactions: Record = { fn: () => void; }>; primaryInteraction?: string | null; + dispose?: () => void; }; export const WORLD_SCALE = 100; @@ -88,6 +63,7 @@ type ObjectDef> = { isChair?: boolean; createInstance: (args: { room: RoomEngine; + root: BABYLON.Mesh; o: RoomSettingObject; loaderResult: BABYLON.ISceneLoaderAsyncResult; meshUpdated: () => void; @@ -98,247 +74,6 @@ export function defineObject>(def: ObjectDef return def; } -const OBJECTS = { - plant: { - placement: 'top', - }, - mug: { - placement: 'top', - onInited: (room, o, rootNode) => { - yuge(room, rootNode, new BABYLON.Vector3(0, 5/*cm*/, 0)); - }, - }, - 'cup-noodle': { - placement: 'top', - onInited: (room, o, rootNode) => { - yuge(room, rootNode, new BABYLON.Vector3(0, 10/*cm*/, 0)); - }, - }, - stickyNote: { - placement: 'side', - }, - 'cardboard-box': { - placement: 'top', - onInited: (room, o, rootNode) => { - const boxMesh = rootNode.getChildMeshes().find(m => m.name === 'Box') as BABYLON.Mesh; - if (o.variation === 'mikan') { - const tex = new BABYLON.Texture('/client-assets/room/objects/cardboard-box/textures/mikan.png', room.scene, false, false); - (boxMesh.material as BABYLON.PBRMaterial).albedoTexture = tex; - (boxMesh.material as BABYLON.PBRMaterial).albedoColor = new BABYLON.Color3(1, 1, 1); - } else if (o.variation === 'aizon') { - const tex = new BABYLON.Texture('/client-assets/room/objects/cardboard-box/textures/aizon.png', room.scene, false, false); - (boxMesh.material as BABYLON.PBRMaterial).albedoTexture = tex; - (boxMesh.material as BABYLON.PBRMaterial).albedoColor = new BABYLON.Color3(1, 1, 1); - } - }, - }, - 'book': { - placement: 'top', - onInited: (room, o, rootNode) => { - const mesh = rootNode.getChildMeshes()[1] as BABYLON.Mesh; - mesh.markVerticesDataAsUpdatable(BABYLON.VertexBuffer.UVKind, true); - const index = o.variation; - const x = index % 8; - const y = Math.floor(index / 8); - - const uvs = mesh.getVerticesData(BABYLON.VertexBuffer.UVKind)!; - for (let i = 0; i < uvs.length / 2; i++) { - const u = uvs[i * 2]; - const v = uvs[i * 2 + 1]; - uvs[i * 2] = (u / 8) + (x / 8); - uvs[i * 2 + 1] = (v / 8) + (y / 8); - } - mesh.updateVerticesData(BABYLON.VertexBuffer.UVKind, uvs); - }, - }, - 'lava-lamp': { - placement: 'top', - onInited: (room, o, rootNode) => { - const light = new BABYLON.PointLight('lavaLampLight', new BABYLON.Vector3(0, 11/*cm*/, 0), room.scene); - light.parent = rootNode; - light.diffuse = new BABYLON.Color3(1.0, 0.5, 0.2); - light.intensity = 300; - light.range = 100/*cm*/; - - const sphere = BABYLON.MeshBuilder.CreateSphere('lavaLampLightSphere', { diameter: 4/*cm*/ }, room.scene); - sphere.parent = rootNode; - sphere.position = new BABYLON.Vector3(0, 15/*cm*/, 0); - const mat = new BABYLON.StandardMaterial('lavaLampLightMat', room.scene); - mat.emissiveColor = new BABYLON.Color3(1.0, 0.5, 0.2); - - mat.alpha = 0.5; - //mat.disableLighting = true; - sphere.material = mat; - - const anim = new BABYLON.Animation('lavaLampLightAnim', 'position.y', 60, BABYLON.Animation.ANIMATIONTYPE_FLOAT, BABYLON.Animation.ANIMATIONLOOPMODE_CYCLE); - anim.setKeys([ - { frame: 0, value: 11/*cm*/ }, - { frame: 500, value: 38/*cm*/ }, - ]); - sphere.animations = [anim]; - room.scene.beginAnimation(sphere, 0, 500, true); - - const emitter = new BABYLON.TransformNode('emitter', room.scene); - emitter.parent = rootNode; - emitter.position = new BABYLON.Vector3(0, 10/*cm*/, 0); - const ps = new BABYLON.ParticleSystem('', 32, room.scene); - ps.particleTexture = new BABYLON.Texture('/client-assets/room/objects/lava-lamp/bubble.png'); - ps.emitter = emitter; - ps.isLocal = true; - ps.minEmitBox = new BABYLON.Vector3(-1/*cm*/, 0, -1/*cm*/); - ps.maxEmitBox = new BABYLON.Vector3(1/*cm*/, 0, 1/*cm*/); - ps.minEmitPower = 2; - ps.maxEmitPower = 3; - ps.minLifeTime = 9; - ps.maxLifeTime = 9; - ps.minSize = 0.5/*cm*/; - ps.maxSize = 1/*cm*/; - ps.direction1 = new BABYLON.Vector3(0, 1, 0); - ps.direction2 = new BABYLON.Vector3(0, 1, 0); - ps.emitRate = 1; - ps.blendMode = BABYLON.ParticleSystem.BLENDMODE_ADD; - ps.color1 = new BABYLON.Color4(1, 1, 1, 0.3); - ps.color2 = new BABYLON.Color4(1, 1, 1, 0.2); - ps.colorDead = new BABYLON.Color4(1, 1, 1, 0); - ps.preWarmCycles = Math.random() * 1000; - ps.start(); - }, - }, - 'wall-clock': { - placement: 'side', - onInited: (room, o, rootNode) => { - const hourHand = rootNode.getChildMeshes().find(m => m.name === 'HandH') as BABYLON.Mesh; - const minuteHand = rootNode.getChildMeshes().find(m => m.name === 'HandM') as BABYLON.Mesh; - room.intervalIds.push(window.setInterval(() => { - const now = new Date(); - const hours = now.getHours() % 12; - const minutes = now.getMinutes(); - const hAngle = -(hours / 12) * Math.PI * 2 - (minutes / 60) * (Math.PI * 2 / 12); - const mAngle = -(minutes / 60) * Math.PI * 2; - hourHand.rotation = new BABYLON.Vector3(0, 0, hAngle); - minuteHand.rotation = new BABYLON.Vector3(0, 0, mAngle); - }, 1000)); - }, - }, - aircon: { - placement: 'wall', - }, - 'monstera': { - placement: 'top', - }, - 'color-box': { - placement: 'floor', - }, - 'steel-rack': { - placement: 'floor', - }, - 'plant2': { - placement: 'top', - }, - 'tv': { - placement: 'top', - }, - 'opened-cardboard-box': { - placement: 'top', - }, - 'bed': { - placement: 'floor', - }, - 'aquarium': { - placement: 'top', - onInited: (room, o, rootNode) => { - const noiseTexture = new BABYLON.NoiseProceduralTexture('perlin', 256, room.scene); - noiseTexture.animationSpeedFactor = 70; - noiseTexture.persistence = 10; - noiseTexture.brightness = 0.5; - noiseTexture.octaves = 5; - - const emitter = new BABYLON.TransformNode('emitter', room.scene); - emitter.parent = rootNode; - emitter.position = new BABYLON.Vector3(17/*cm*/, 7/*cm*/, -9/*cm*/); - const ps = new BABYLON.ParticleSystem('', 128, room.scene); - ps.particleTexture = new BABYLON.Texture('/client-assets/room/objects/lava-lamp/bubble.png'); - ps.emitter = emitter; - ps.isLocal = true; - ps.minEmitBox = new BABYLON.Vector3(-2/*cm*/, 0, -2/*cm*/); - ps.maxEmitBox = new BABYLON.Vector3(2/*cm*/, 0, 2/*cm*/); - ps.minEmitPower = 40; - ps.maxEmitPower = 60; - ps.minLifeTime = 0.5; - ps.maxLifeTime = 0.5; - ps.minSize = 0.1/*cm*/; - ps.maxSize = 1/*cm*/; - ps.direction1 = new BABYLON.Vector3(0, 1, 0); - ps.direction2 = new BABYLON.Vector3(0, 1, 0); - ps.noiseTexture = noiseTexture; - ps.noiseStrength = new BABYLON.Vector3(500, 0, 500); - ps.emitRate = 32; - ps.blendMode = BABYLON.ParticleSystem.BLENDMODE_ADD; - //ps.color1 = new BABYLON.Color4(1, 1, 1, 0.3); - //ps.color2 = new BABYLON.Color4(1, 1, 1, 0.2); - //ps.colorDead = new BABYLON.Color4(1, 1, 1, 0); - ps.preWarmCycles = Math.random() * 1000; - ps.start(); - }, - }, - 'desk': { - placement: 'floor', - }, - 'chair': { - placement: 'floor', - isChair: true, - }, - 'energy-drink': { - placement: 'top', - }, - 'banknote': { - placement: 'top', - }, - 'facial-tissue': { - placement: 'top', - }, - 'milk': { - placement: 'top', - }, - 'monitor': { - placement: 'top', - }, - 'keyboard': { - placement: 'top', - }, - 'ceiling-fan-light': { - placement: 'ceiling', - receiveShadows: false, - castShadows: false, - onInited: (room, o, rootNode) => { - const rotor = rootNode.getChildMeshes().find(m => m.name === 'Rotor') as BABYLON.Mesh; - rotor.rotation = rotor.rotationQuaternion != null ? rotor.rotationQuaternion.toEulerAngles() : rotor.rotation; - const anim = new BABYLON.Animation('', 'rotation.y', 60, BABYLON.Animation.ANIMATIONTYPE_FLOAT, BABYLON.Animation.ANIMATIONLOOPMODE_CYCLE); - anim.setKeys([ - { frame: 0, value: 0 }, - { frame: 100, value: Math.PI * 2 }, - ]); - rotor.animations = [anim]; - room.scene.beginAnimation(rotor, 0, 100, true); - }, - }, - 'round-rug': { - placement: 'floor', - }, - 'wood-sound-absorbing-panel': { - placement: 'side', - }, - 'power-strip': { - placement: 'top', - }, - 'snakeplant': { - placement: 'top', - }, - 'blind': { - placement: 'bottom', - }, -}; - const _assumedFramesPerSecond = 60; class HorizontalCameraKeyboardMoveInput extends BABYLON.BaseCameraPointersInput { @@ -465,8 +200,8 @@ export class RoomEngine { private camera: BABYLON.UniversalCamera; private fixedCamera: BABYLON.UniversalCamera; private birdeyeCamera: BABYLON.ArcRotateCamera; - private intervalIds: number[] = []; - private timeoutIds: number[] = []; + public intervalIds: number[] = []; + public timeoutIds: number[] = []; private objectMeshs: Map = new Map(); public objectInstances: Map> = new Map(); private grabbing: { @@ -1110,9 +845,11 @@ export class RoomEngine { private async loadObject(o: RoomSetting['objects'][0]) { const def = getObjectDef(o.type); + const camelToKebab = (str: string) => str.replace(/([a-z0-9])([A-Z])/g, '$1-$2').toLowerCase(); + const root = new BABYLON.Mesh(`object_${o.id}_${o.type}`, this.scene); - const loaderResult = await BABYLON.ImportMeshAsync(`/client-assets/room/objects/${o.type}/${o.type}.glb`, this.scene); + const loaderResult = await BABYLON.ImportMeshAsync(`/client-assets/room/objects/${camelToKebab(o.type)}/${camelToKebab(o.type)}.glb`, this.scene); let hasCollisionMesh = false; for (const mesh of loaderResult.meshes) { @@ -1135,7 +872,7 @@ export class RoomEngine { root.addChild(subRoot); - if (_DEV_) root.showBoundingBox = true; + //if (_DEV_) root.showBoundingBox = true; root.position = new BABYLON.Vector3(...o.position); root.rotation = new BABYLON.Vector3(o.rotation[0], -o.rotation[1], o.rotation[2]); @@ -1172,7 +909,7 @@ export class RoomEngine { this.shadowGenerator2.addShadowCaster(mesh); } - mesh.renderOutline = false; + mesh.renderOutline = this.selectedObjectId.value === o.id; mesh.outlineWidth = 0.003; mesh.outlineColor = new BABYLON.Color3(1, 0, 0); //if (mesh.material) (mesh.material as BABYLON.PBRMaterial).ambientColor = new BABYLON.Color3(0.2, 0.2, 0.2); @@ -1189,6 +926,7 @@ export class RoomEngine { const objectInstance = def.createInstance({ room: this, + root, o: o, loaderResult: loaderResult, meshUpdated: () => { @@ -1199,7 +937,7 @@ export class RoomEngine { this.objectInstances.set(o.id, objectInstance); if (objectInstance.onInited != null) { - objectInstance.onInited(this, o, root); + objectInstance.onInited(); } if (o.isMainLight) { diff --git a/packages/frontend/src/utility/room/object-defs.ts b/packages/frontend/src/utility/room/object-defs.ts index baf9543401..6378ae35f0 100644 --- a/packages/frontend/src/utility/room/object-defs.ts +++ b/packages/frontend/src/utility/room/object-defs.ts @@ -3,10 +3,68 @@ * SPDX-License-Identifier: AGPL-3.0-only */ +import { aircon } from './objects/aircon.js'; +import { aquarium } from './objects/aquarium.js'; +import { banknote } from './objects/banknote.js'; +import { bed } from './objects/bed.js'; import { blind } from './objects/blind.js'; +import { book } from './objects/book.js'; +import { cardboardBox } from './objects/cardboardBox.js'; +import { ceilingFanLight } from './objects/ceilingFanLight.js'; +import { chair } from './objects/chair.js'; +import { colorBox } from './objects/colorBox.js'; +import { cupNoodle } from './objects/cupNoodle.js'; +import { desk } from './objects/desk.js'; +import { energyDrink } from './objects/energyDrink.js'; +import { facialTissue } from './objects/facialTissue.js'; +import { keyboard } from './objects/keyboard.js'; +import { lavaLamp } from './objects/lavaLamp.js'; +import { milk } from './objects/milk.js'; +import { monitor } from './objects/monitor.js'; +import { monstera } from './objects/monstera.js'; +import { mug } from './objects/mug.js'; +import { openedCardboardBox } from './objects/openedCardboardBox.js'; +import { plant } from './objects/plant.js'; +import { plant2 } from './objects/plant2.js'; +import { powerStrip } from './objects/powerStrip.js'; +import { roundRug } from './objects/roundRug.js'; +import { snakeplant } from './objects/snakeplant.js'; +import { steelRack } from './objects/steelRack.js'; +import { tv } from './objects/tv.js'; +import { wallClock } from './objects/wallClock.js'; +import { woodSoundAbsorbingPanel } from './objects/woodSoundAbsorbingPanel.js'; export const OBJECT_DEFS = [ + aircon, + aquarium, + banknote, + bed, blind, + book, + cardboardBox, + ceilingFanLight, + chair, + colorBox, + cupNoodle, + desk, + energyDrink, + facialTissue, + keyboard, + lavaLamp, + milk, + monitor, + monstera, + mug, + openedCardboardBox, + plant, + plant2, + powerStrip, + roundRug, + snakeplant, + steelRack, + tv, + wallClock, + woodSoundAbsorbingPanel, ]; export function getObjectDef(type: string): typeof OBJECT_DEFS[number] { diff --git a/packages/frontend/src/utility/room/objects/aircon.ts b/packages/frontend/src/utility/room/objects/aircon.ts new file mode 100644 index 0000000000..2ce8bbf800 --- /dev/null +++ b/packages/frontend/src/utility/room/objects/aircon.ts @@ -0,0 +1,17 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { defineObject } from '../engine.js'; + +export const aircon = defineObject({ + id: 'aircon', + defaultOptions: {}, + placement: 'wall', + createInstance: () => { + return { + interactions: {}, + }; + }, +}); diff --git a/packages/frontend/src/utility/room/objects/aquarium.ts b/packages/frontend/src/utility/room/objects/aquarium.ts new file mode 100644 index 0000000000..91f29d36e8 --- /dev/null +++ b/packages/frontend/src/utility/room/objects/aquarium.ts @@ -0,0 +1,52 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import * as BABYLON from '@babylonjs/core'; +import { defineObject } from '../engine.js'; + +export const aquarium = defineObject({ + id: 'aquarium', + defaultOptions: {}, + placement: 'top', + createInstance: ({ room, o, root }) => { + return { + onInited: () => { + const noiseTexture = new BABYLON.NoiseProceduralTexture('perlin', 256, room.scene); + noiseTexture.animationSpeedFactor = 70; + noiseTexture.persistence = 10; + noiseTexture.brightness = 0.5; + noiseTexture.octaves = 5; + + const emitter = new BABYLON.TransformNode('emitter', room.scene); + emitter.parent = root; + emitter.position = new BABYLON.Vector3(17/*cm*/, 7/*cm*/, -9/*cm*/); + const ps = new BABYLON.ParticleSystem('', 128, room.scene); + ps.particleTexture = new BABYLON.Texture('/client-assets/room/objects/lava-lamp/bubble.png'); + ps.emitter = emitter; + ps.isLocal = true; + ps.minEmitBox = new BABYLON.Vector3(-2/*cm*/, 0, -2/*cm*/); + ps.maxEmitBox = new BABYLON.Vector3(2/*cm*/, 0, 2/*cm*/); + ps.minEmitPower = 40; + ps.maxEmitPower = 60; + ps.minLifeTime = 0.5; + ps.maxLifeTime = 0.5; + ps.minSize = 0.1/*cm*/; + ps.maxSize = 1/*cm*/; + ps.direction1 = new BABYLON.Vector3(0, 1, 0); + ps.direction2 = new BABYLON.Vector3(0, 1, 0); + ps.noiseTexture = noiseTexture; + ps.noiseStrength = new BABYLON.Vector3(500, 0, 500); + ps.emitRate = 32; + ps.blendMode = BABYLON.ParticleSystem.BLENDMODE_ADD; + //ps.color1 = new BABYLON.Color4(1, 1, 1, 0.3); + //ps.color2 = new BABYLON.Color4(1, 1, 1, 0.2); + //ps.colorDead = new BABYLON.Color4(1, 1, 1, 0); + ps.preWarmCycles = Math.random() * 1000; + ps.start(); + }, + interactions: {}, + }; + }, +}); diff --git a/packages/frontend/src/utility/room/objects/banknote.ts b/packages/frontend/src/utility/room/objects/banknote.ts new file mode 100644 index 0000000000..fce1cdbbca --- /dev/null +++ b/packages/frontend/src/utility/room/objects/banknote.ts @@ -0,0 +1,17 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { defineObject } from '../engine.js'; + +export const banknote = defineObject({ + id: 'banknote', + defaultOptions: {}, + placement: 'top', + createInstance: () => { + return { + interactions: {}, + }; + }, +}); diff --git a/packages/frontend/src/utility/room/objects/bed.ts b/packages/frontend/src/utility/room/objects/bed.ts new file mode 100644 index 0000000000..9002d121c9 --- /dev/null +++ b/packages/frontend/src/utility/room/objects/bed.ts @@ -0,0 +1,17 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { defineObject } from '../engine.js'; + +export const bed = defineObject({ + id: 'bed', + defaultOptions: {}, + placement: 'floor', + createInstance: () => { + return { + interactions: {}, + }; + }, +}); diff --git a/packages/frontend/src/utility/room/objects/blind.ts b/packages/frontend/src/utility/room/objects/blind.ts index 18d241e99f..f53a00c18b 100644 --- a/packages/frontend/src/utility/room/objects/blind.ts +++ b/packages/frontend/src/utility/room/objects/blind.ts @@ -40,6 +40,7 @@ export const blind = defineObject({ const applyAngle = () => { for (const b of [blade, ...blades]) { b.rotation.x = o.options.angle; + b.rotation.x += Math.random() * 0.3 - 0.15; } }; diff --git a/packages/frontend/src/utility/room/objects/book.ts b/packages/frontend/src/utility/room/objects/book.ts new file mode 100644 index 0000000000..ac2f81c5c0 --- /dev/null +++ b/packages/frontend/src/utility/room/objects/book.ts @@ -0,0 +1,35 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ +import * as BABYLON from '@babylonjs/core'; +import { defineObject } from '../engine.js'; + +export const book = defineObject({ + id: 'book', + defaultOptions: { + variation: null as number | null, + }, + placement: 'top', + createInstance: ({ room, o, root }) => { + return { + onInited: () => { + const mesh = root.getChildMeshes()[1] as BABYLON.Mesh; + mesh.markVerticesDataAsUpdatable(BABYLON.VertexBuffer.UVKind, true); + const index = o.options.variation ?? 0; + const x = index % 8; + const y = Math.floor(index / 8); + + const uvs = mesh.getVerticesData(BABYLON.VertexBuffer.UVKind)!; + for (let i = 0; i < uvs.length / 2; i++) { + const u = uvs[i * 2]; + const v = uvs[i * 2 + 1]; + uvs[i * 2] = (u / 8) + (x / 8); + uvs[i * 2 + 1] = (v / 8) + (y / 8); + } + mesh.updateVerticesData(BABYLON.VertexBuffer.UVKind, uvs); + }, + interactions: {}, + }; + }, +}); diff --git a/packages/frontend/src/utility/room/objects/cardboardBox.ts b/packages/frontend/src/utility/room/objects/cardboardBox.ts new file mode 100644 index 0000000000..e3debf509e --- /dev/null +++ b/packages/frontend/src/utility/room/objects/cardboardBox.ts @@ -0,0 +1,32 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import * as BABYLON from '@babylonjs/core'; +import { defineObject } from '../engine.js'; + +export const cardboardBox = defineObject({ + id: 'cardboardBox', + defaultOptions: { + variation: null as string | null, + }, + placement: 'top', + createInstance: ({ room, o, root }) => { + return { + onInited: () => { + const boxMesh = root.getChildMeshes().find(m => m.name === 'Box') as BABYLON.Mesh; + if (o.options.variation === 'mikan') { + const tex = new BABYLON.Texture('/client-assets/room/objects/cardboard-box/textures/mikan.png', room.scene, false, false); + (boxMesh.material as BABYLON.PBRMaterial).albedoTexture = tex; + (boxMesh.material as BABYLON.PBRMaterial).albedoColor = new BABYLON.Color3(1, 1, 1); + } else if (o.options.variation === 'aizon') { + const tex = new BABYLON.Texture('/client-assets/room/objects/cardboard-box/textures/aizon.png', room.scene, false, false); + (boxMesh.material as BABYLON.PBRMaterial).albedoTexture = tex; + (boxMesh.material as BABYLON.PBRMaterial).albedoColor = new BABYLON.Color3(1, 1, 1); + } + }, + interactions: {}, + }; + }, +}); diff --git a/packages/frontend/src/utility/room/objects/ceilingFanLight.ts b/packages/frontend/src/utility/room/objects/ceilingFanLight.ts new file mode 100644 index 0000000000..bd6d52ea99 --- /dev/null +++ b/packages/frontend/src/utility/room/objects/ceilingFanLight.ts @@ -0,0 +1,29 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import * as BABYLON from '@babylonjs/core'; +import { defineObject } from '../engine.js'; + +export const ceilingFanLight = defineObject({ + id: 'ceilingFanLight', + defaultOptions: {}, + placement: 'ceiling', + createInstance: ({ room, o, root }) => { + return { + onInited: () => { + const rotor = root.getChildMeshes().find(m => m.name === 'Rotor') as BABYLON.Mesh; + rotor.rotation = rotor.rotationQuaternion != null ? rotor.rotationQuaternion.toEulerAngles() : rotor.rotation; + const anim = new BABYLON.Animation('', 'rotation.y', 60, BABYLON.Animation.ANIMATIONTYPE_FLOAT, BABYLON.Animation.ANIMATIONLOOPMODE_CYCLE); + anim.setKeys([ + { frame: 0, value: 0 }, + { frame: 100, value: Math.PI * 2 }, + ]); + rotor.animations = [anim]; + room.scene.beginAnimation(rotor, 0, 100, true); + }, + interactions: {}, + }; + }, +}); diff --git a/packages/frontend/src/utility/room/objects/chair.ts b/packages/frontend/src/utility/room/objects/chair.ts new file mode 100644 index 0000000000..e766b62bed --- /dev/null +++ b/packages/frontend/src/utility/room/objects/chair.ts @@ -0,0 +1,18 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { defineObject } from '../engine.js'; + +export const chair = defineObject({ + id: 'chair', + defaultOptions: {}, + placement: 'floor', + isChair: true, + createInstance: () => { + return { + interactions: {}, + }; + }, +}); diff --git a/packages/frontend/src/utility/room/objects/colorBox.ts b/packages/frontend/src/utility/room/objects/colorBox.ts new file mode 100644 index 0000000000..6861017b23 --- /dev/null +++ b/packages/frontend/src/utility/room/objects/colorBox.ts @@ -0,0 +1,17 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { defineObject } from '../engine.js'; + +export const colorBox = defineObject({ + id: 'colorBox', + defaultOptions: {}, + placement: 'floor', + createInstance: () => { + return { + interactions: {}, + }; + }, +}); diff --git a/packages/frontend/src/utility/room/objects/cupNoodle.ts b/packages/frontend/src/utility/room/objects/cupNoodle.ts new file mode 100644 index 0000000000..528f2c9e1a --- /dev/null +++ b/packages/frontend/src/utility/room/objects/cupNoodle.ts @@ -0,0 +1,22 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import * as BABYLON from '@babylonjs/core'; +import { defineObject } from '../engine.js'; +import { yuge } from '../utility.js'; + +export const cupNoodle = defineObject({ + id: 'cupNoodle', + defaultOptions: {}, + placement: 'top', + createInstance: ({ room, root }) => { + return { + onInited: () => { + yuge(room, root, new BABYLON.Vector3(0, 10/*cm*/, 0)); + }, + interactions: {}, + }; + }, +}); diff --git a/packages/frontend/src/utility/room/objects/desk.ts b/packages/frontend/src/utility/room/objects/desk.ts new file mode 100644 index 0000000000..d97b60d19c --- /dev/null +++ b/packages/frontend/src/utility/room/objects/desk.ts @@ -0,0 +1,17 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { defineObject } from '../engine.js'; + +export const desk = defineObject({ + id: 'desk', + defaultOptions: {}, + placement: 'floor', + createInstance: () => { + return { + interactions: {}, + }; + }, +}); diff --git a/packages/frontend/src/utility/room/objects/energyDrink.ts b/packages/frontend/src/utility/room/objects/energyDrink.ts new file mode 100644 index 0000000000..e7bcccb0d3 --- /dev/null +++ b/packages/frontend/src/utility/room/objects/energyDrink.ts @@ -0,0 +1,17 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { defineObject } from '../engine.js'; + +export const energyDrink = defineObject({ + id: 'energyDrink', + defaultOptions: {}, + placement: 'top', + createInstance: () => { + return { + interactions: {}, + }; + }, +}); diff --git a/packages/frontend/src/utility/room/objects/facialTissue.ts b/packages/frontend/src/utility/room/objects/facialTissue.ts new file mode 100644 index 0000000000..a19db98ccd --- /dev/null +++ b/packages/frontend/src/utility/room/objects/facialTissue.ts @@ -0,0 +1,17 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { defineObject } from '../engine.js'; + +export const facialTissue = defineObject({ + id: 'facialTissue', + defaultOptions: {}, + placement: 'top', + createInstance: () => { + return { + interactions: {}, + }; + }, +}); diff --git a/packages/frontend/src/utility/room/objects/keyboard.ts b/packages/frontend/src/utility/room/objects/keyboard.ts new file mode 100644 index 0000000000..1f1f3a31d7 --- /dev/null +++ b/packages/frontend/src/utility/room/objects/keyboard.ts @@ -0,0 +1,17 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { defineObject } from '../engine.js'; + +export const keyboard = defineObject({ + id: 'keyboard', + defaultOptions: {}, + placement: 'top', + createInstance: () => { + return { + interactions: {}, + }; + }, +}); diff --git a/packages/frontend/src/utility/room/objects/lavaLamp.ts b/packages/frontend/src/utility/room/objects/lavaLamp.ts new file mode 100644 index 0000000000..b08aab90e0 --- /dev/null +++ b/packages/frontend/src/utility/room/objects/lavaLamp.ts @@ -0,0 +1,68 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ +import * as BABYLON from '@babylonjs/core'; +import { defineObject } from '../engine.js'; + +export const lavaLamp = defineObject({ + id: 'lavaLamp', + defaultOptions: {}, + placement: 'top', + createInstance: ({ room, o, root }) => { + return { + onInited: () => { + const light = new BABYLON.PointLight('lavaLampLight', new BABYLON.Vector3(0, 11/*cm*/, 0), room.scene); + light.parent = root; + light.diffuse = new BABYLON.Color3(1.0, 0.5, 0.2); + light.intensity = 300; + light.range = 100/*cm*/; + + const sphere = BABYLON.MeshBuilder.CreateSphere('lavaLampLightSphere', { diameter: 4/*cm*/ }, room.scene); + sphere.parent = root; + sphere.position = new BABYLON.Vector3(0, 15/*cm*/, 0); + const mat = new BABYLON.StandardMaterial('lavaLampLightMat', room.scene); + mat.emissiveColor = new BABYLON.Color3(1.0, 0.5, 0.2); + + mat.alpha = 0.5; + //mat.disableLighting = true; + sphere.material = mat; + + const anim = new BABYLON.Animation('lavaLampLightAnim', 'position.y', 60, BABYLON.Animation.ANIMATIONTYPE_FLOAT, BABYLON.Animation.ANIMATIONLOOPMODE_CYCLE); + anim.setKeys([ + { frame: 0, value: 11/*cm*/ }, + { frame: 500, value: 38/*cm*/ }, + ]); + sphere.animations = [anim]; + room.scene.beginAnimation(sphere, 0, 500, true); + + const emitter = new BABYLON.TransformNode('emitter', room.scene); + emitter.parent = root; + emitter.position = new BABYLON.Vector3(0, 10/*cm*/, 0); + const ps = new BABYLON.ParticleSystem('', 32, room.scene); + ps.particleTexture = new BABYLON.Texture('/client-assets/room/objects/lava-lamp/bubble.png'); + ps.emitter = emitter; + ps.isLocal = true; + ps.minEmitBox = new BABYLON.Vector3(-1/*cm*/, 0, -1/*cm*/); + ps.maxEmitBox = new BABYLON.Vector3(1/*cm*/, 0, 1/*cm*/); + ps.minEmitPower = 2; + ps.maxEmitPower = 3; + ps.minLifeTime = 9; + ps.maxLifeTime = 9; + ps.minSize = 0.5/*cm*/; + ps.maxSize = 1/*cm*/; + ps.direction1 = new BABYLON.Vector3(0, 1, 0); + ps.direction2 = new BABYLON.Vector3(0, 1, 0); + ps.emitRate = 1; + ps.blendMode = BABYLON.ParticleSystem.BLENDMODE_ADD; + ps.color1 = new BABYLON.Color4(1, 1, 1, 0.3); + ps.color2 = new BABYLON.Color4(1, 1, 1, 0.2); + ps.colorDead = new BABYLON.Color4(1, 1, 1, 0); + ps.preWarmCycles = Math.random() * 1000; + ps.start(); + }, + interactions: {}, + }; + }, + +}); diff --git a/packages/frontend/src/utility/room/objects/milk.ts b/packages/frontend/src/utility/room/objects/milk.ts new file mode 100644 index 0000000000..f8f996b3c8 --- /dev/null +++ b/packages/frontend/src/utility/room/objects/milk.ts @@ -0,0 +1,17 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { defineObject } from '../engine.js'; + +export const milk = defineObject({ + id: 'milk', + defaultOptions: {}, + placement: 'top', + createInstance: () => { + return { + interactions: {}, + }; + }, +}); diff --git a/packages/frontend/src/utility/room/objects/monitor.ts b/packages/frontend/src/utility/room/objects/monitor.ts new file mode 100644 index 0000000000..aa5b9fed9a --- /dev/null +++ b/packages/frontend/src/utility/room/objects/monitor.ts @@ -0,0 +1,17 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { defineObject } from '../engine.js'; + +export const monitor = defineObject({ + id: 'monitor', + defaultOptions: {}, + placement: 'top', + createInstance: () => { + return { + interactions: {}, + }; + }, +}); diff --git a/packages/frontend/src/utility/room/objects/monstera.ts b/packages/frontend/src/utility/room/objects/monstera.ts new file mode 100644 index 0000000000..a2bf4559f1 --- /dev/null +++ b/packages/frontend/src/utility/room/objects/monstera.ts @@ -0,0 +1,17 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { defineObject } from '../engine.js'; + +export const monstera = defineObject({ + id: 'monstera', + defaultOptions: {}, + placement: 'top', + createInstance: () => { + return { + interactions: {}, + }; + }, +}); diff --git a/packages/frontend/src/utility/room/objects/mug.ts b/packages/frontend/src/utility/room/objects/mug.ts new file mode 100644 index 0000000000..5b3a4172be --- /dev/null +++ b/packages/frontend/src/utility/room/objects/mug.ts @@ -0,0 +1,22 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import * as BABYLON from '@babylonjs/core'; +import { defineObject } from '../engine.js'; +import { yuge } from '../utility.js'; + +export const mug = defineObject({ + id: 'mug', + defaultOptions: {}, + placement: 'top', + createInstance: ({ room, root }) => { + return { + onInited: () => { + yuge(room, root, new BABYLON.Vector3(0, 5/*cm*/, 0)); + }, + interactions: {}, + }; + }, +}); diff --git a/packages/frontend/src/utility/room/objects/openedCardboardBox.ts b/packages/frontend/src/utility/room/objects/openedCardboardBox.ts new file mode 100644 index 0000000000..330b4dc244 --- /dev/null +++ b/packages/frontend/src/utility/room/objects/openedCardboardBox.ts @@ -0,0 +1,17 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { defineObject } from '../engine.js'; + +export const openedCardboardBox = defineObject({ + id: 'openedCardboardBox', + defaultOptions: {}, + placement: 'top', + createInstance: () => { + return { + interactions: {}, + }; + }, +}); diff --git a/packages/frontend/src/utility/room/objects/plant.ts b/packages/frontend/src/utility/room/objects/plant.ts new file mode 100644 index 0000000000..f0dd275643 --- /dev/null +++ b/packages/frontend/src/utility/room/objects/plant.ts @@ -0,0 +1,17 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { defineObject } from '../engine.js'; + +export const plant = defineObject({ + id: 'plant', + defaultOptions: {}, + placement: 'top', + createInstance: () => { + return { + interactions: {}, + }; + }, +}); diff --git a/packages/frontend/src/utility/room/objects/plant2.ts b/packages/frontend/src/utility/room/objects/plant2.ts new file mode 100644 index 0000000000..652ddd6dc9 --- /dev/null +++ b/packages/frontend/src/utility/room/objects/plant2.ts @@ -0,0 +1,17 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { defineObject } from '../engine.js'; + +export const plant2 = defineObject({ + id: 'plant2', + defaultOptions: {}, + placement: 'top', + createInstance: () => { + return { + interactions: {}, + }; + }, +}); diff --git a/packages/frontend/src/utility/room/objects/powerStrip.ts b/packages/frontend/src/utility/room/objects/powerStrip.ts new file mode 100644 index 0000000000..899fed935f --- /dev/null +++ b/packages/frontend/src/utility/room/objects/powerStrip.ts @@ -0,0 +1,17 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { defineObject } from '../engine.js'; + +export const powerStrip = defineObject({ + id: 'powerStrip', + defaultOptions: {}, + placement: 'top', + createInstance: () => { + return { + interactions: {}, + }; + }, +}); diff --git a/packages/frontend/src/utility/room/objects/roundRug.ts b/packages/frontend/src/utility/room/objects/roundRug.ts new file mode 100644 index 0000000000..88c6466daa --- /dev/null +++ b/packages/frontend/src/utility/room/objects/roundRug.ts @@ -0,0 +1,17 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { defineObject } from '../engine.js'; + +export const roundRug = defineObject({ + id: 'roundRug', + defaultOptions: {}, + placement: 'floor', + createInstance: () => { + return { + interactions: {}, + }; + }, +}); diff --git a/packages/frontend/src/utility/room/objects/snakeplant.ts b/packages/frontend/src/utility/room/objects/snakeplant.ts new file mode 100644 index 0000000000..8cad1922c8 --- /dev/null +++ b/packages/frontend/src/utility/room/objects/snakeplant.ts @@ -0,0 +1,17 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { defineObject } from '../engine.js'; + +export const snakeplant = defineObject({ + id: 'snakeplant', + defaultOptions: {}, + placement: 'top', + createInstance: () => { + return { + interactions: {}, + }; + }, +}); diff --git a/packages/frontend/src/utility/room/objects/steelRack.ts b/packages/frontend/src/utility/room/objects/steelRack.ts new file mode 100644 index 0000000000..986dd8535c --- /dev/null +++ b/packages/frontend/src/utility/room/objects/steelRack.ts @@ -0,0 +1,17 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { defineObject } from '../engine.js'; + +export const steelRack = defineObject({ + id: 'steelRack', + defaultOptions: {}, + placement: 'floor', + createInstance: () => { + return { + interactions: {}, + }; + }, +}); diff --git a/packages/frontend/src/utility/room/objects/tv.ts b/packages/frontend/src/utility/room/objects/tv.ts new file mode 100644 index 0000000000..23f46c2a6d --- /dev/null +++ b/packages/frontend/src/utility/room/objects/tv.ts @@ -0,0 +1,17 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { defineObject } from '../engine.js'; + +export const tv = defineObject({ + id: 'tv', + defaultOptions: {}, + placement: 'top', + createInstance: () => { + return { + interactions: {}, + }; + }, +}); diff --git a/packages/frontend/src/utility/room/objects/wallClock.ts b/packages/frontend/src/utility/room/objects/wallClock.ts new file mode 100644 index 0000000000..49a39f4773 --- /dev/null +++ b/packages/frontend/src/utility/room/objects/wallClock.ts @@ -0,0 +1,31 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import * as BABYLON from '@babylonjs/core'; +import { defineObject } from '../engine.js'; + +export const wallClock = defineObject({ + id: 'wallClock', + defaultOptions: {}, + placement: 'side', + createInstance: ({ room, o, root }) => { + return { + onInited: () => { + const hourHand = root.getChildMeshes().find(m => m.name === 'HandH') as BABYLON.Mesh; + const minuteHand = root.getChildMeshes().find(m => m.name === 'HandM') as BABYLON.Mesh; + room.intervalIds.push(window.setInterval(() => { + const now = new Date(); + const hours = now.getHours() % 12; + const minutes = now.getMinutes(); + const hAngle = -(hours / 12) * Math.PI * 2 - (minutes / 60) * (Math.PI * 2 / 12); + const mAngle = -(minutes / 60) * Math.PI * 2; + hourHand.rotation = new BABYLON.Vector3(0, 0, hAngle); + minuteHand.rotation = new BABYLON.Vector3(0, 0, mAngle); + }, 1000)); + }, + interactions: {}, + }; + }, +}); diff --git a/packages/frontend/src/utility/room/objects/woodSoundAbsorbingPanel.ts b/packages/frontend/src/utility/room/objects/woodSoundAbsorbingPanel.ts new file mode 100644 index 0000000000..e94eacd50c --- /dev/null +++ b/packages/frontend/src/utility/room/objects/woodSoundAbsorbingPanel.ts @@ -0,0 +1,17 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { defineObject } from '../engine.js'; + +export const woodSoundAbsorbingPanel = defineObject({ + id: 'woodSoundAbsorbingPanel', + defaultOptions: {}, + placement: 'side', + createInstance: () => { + return { + interactions: {}, + }; + }, +}); diff --git a/packages/frontend/src/utility/room/utility.ts b/packages/frontend/src/utility/room/utility.ts new file mode 100644 index 0000000000..b629bd6d7d --- /dev/null +++ b/packages/frontend/src/utility/room/utility.ts @@ -0,0 +1,33 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import * as BABYLON from '@babylonjs/core'; +import type { RoomEngine } from './engine.js'; + +export function yuge(room: RoomEngine, mesh: BABYLON.Mesh, offset: BABYLON.Vector3) { + const emitter = new BABYLON.TransformNode('emitter', room.scene); + emitter.parent = mesh; + emitter.position = offset; + const ps = new BABYLON.ParticleSystem('steamParticleSystem', 8, room.scene); + ps.particleTexture = new BABYLON.Texture('/client-assets/room/steam.png'); + ps.emitter = emitter; + ps.minEmitBox = new BABYLON.Vector3(-1/*cm*/, 0, -1/*cm*/); + ps.maxEmitBox = new BABYLON.Vector3(1/*cm*/, 0, 1/*cm*/); + ps.minEmitPower = 10; + ps.maxEmitPower = 12; + ps.minLifeTime = 2; + ps.maxLifeTime = 3; + ps.addSizeGradient(0, 10/*cm*/, 12/*cm*/); + ps.addSizeGradient(1, 18/*cm*/, 20/*cm*/); + ps.direction1 = new BABYLON.Vector3(-0.3, 1, 0.3); + ps.direction2 = new BABYLON.Vector3(0.3, 1, -0.3); + ps.emitRate = 0.5; + ps.blendMode = BABYLON.ParticleSystem.BLENDMODE_ADD; + ps.color1 = new BABYLON.Color4(1, 1, 1, 0.3); + ps.color2 = new BABYLON.Color4(1, 1, 1, 0.2); + ps.colorDead = new BABYLON.Color4(1, 1, 1, 0); + ps.preWarmCycles = Math.random() * 1000; + ps.start(); +}