diff --git a/packages/frontend/assets/room/objects/tabletop-digital-clock/tabletop-digital-clock.blend b/packages/frontend/assets/room/objects/tabletop-digital-clock/tabletop-digital-clock.blend new file mode 100644 index 0000000000..4a9c65f58e Binary files /dev/null and b/packages/frontend/assets/room/objects/tabletop-digital-clock/tabletop-digital-clock.blend differ diff --git a/packages/frontend/assets/room/objects/tabletop-digital-clock/tabletop-digital-clock.glb b/packages/frontend/assets/room/objects/tabletop-digital-clock/tabletop-digital-clock.glb new file mode 100644 index 0000000000..8c5502e67e Binary files /dev/null and b/packages/frontend/assets/room/objects/tabletop-digital-clock/tabletop-digital-clock.glb differ diff --git a/packages/frontend/src/pages/room.vue b/packages/frontend/src/pages/room.vue index 13520eda9d..6dea979c7f 100644 --- a/packages/frontend/src/pages/room.vue +++ b/packages/frontend/src/pages/room.vue @@ -211,6 +211,12 @@ onMounted(() => { position: [-55, 90, 175], rotation: [0, 0, 0], options: {}, + }, { + id: 'f22', + type: 'tabletopDigitalClock', + position: [-35, 90, 175], + rotation: [0, Math.PI, 0], + options: {}, }, { id: 'f3', type: 'snakeplant', diff --git a/packages/frontend/src/utility/room/object-defs.ts b/packages/frontend/src/utility/room/object-defs.ts index d641bcea9b..bdc6910be8 100644 --- a/packages/frontend/src/utility/room/object-defs.ts +++ b/packages/frontend/src/utility/room/object-defs.ts @@ -33,6 +33,7 @@ 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 { tabletopDigitalClock } from './objects/tabletopDigitalClock.js'; import { tv } from './objects/tv.js'; import { wallClock } from './objects/wallClock.js'; import { woodSoundAbsorbingPanel } from './objects/woodSoundAbsorbingPanel.js'; @@ -68,6 +69,7 @@ export const OBJECT_DEFS = [ roundRug, snakeplant, steelRack, + tabletopDigitalClock, tv, wallClock, woodSoundAbsorbingPanel, diff --git a/packages/frontend/src/utility/room/objects/tabletopDigitalClock.ts b/packages/frontend/src/utility/room/objects/tabletopDigitalClock.ts new file mode 100644 index 0000000000..de69f545f2 --- /dev/null +++ b/packages/frontend/src/utility/room/objects/tabletopDigitalClock.ts @@ -0,0 +1,59 @@ +/* + * 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 { get7segMeshesOfCurrentTime, yuge } from '../utility.js'; + +export const tabletopDigitalClock = defineObject({ + id: 'tabletopDigitalClock', + defaultOptions: {}, + placement: 'top', + createInstance: ({ room, root }) => { + return { + onInited: () => { + room.intervalIds.push(window.setInterval(() => { + const meshes = { + '1a': root.getChildMeshes().find(m => m.name.includes('__TIME_7SEG_1A__')), + '1b': root.getChildMeshes().find(m => m.name.includes('__TIME_7SEG_1B__')), + '1c': root.getChildMeshes().find(m => m.name.includes('__TIME_7SEG_1C__')), + '1d': root.getChildMeshes().find(m => m.name.includes('__TIME_7SEG_1D__')), + '1e': root.getChildMeshes().find(m => m.name.includes('__TIME_7SEG_1E__')), + '1f': root.getChildMeshes().find(m => m.name.includes('__TIME_7SEG_1F__')), + '1g': root.getChildMeshes().find(m => m.name.includes('__TIME_7SEG_1G__')), + '2a': root.getChildMeshes().find(m => m.name.includes('__TIME_7SEG_2A__')), + '2b': root.getChildMeshes().find(m => m.name.includes('__TIME_7SEG_2B__')), + '2c': root.getChildMeshes().find(m => m.name.includes('__TIME_7SEG_2C__')), + '2d': root.getChildMeshes().find(m => m.name.includes('__TIME_7SEG_2D__')), + '2e': root.getChildMeshes().find(m => m.name.includes('__TIME_7SEG_2E__')), + '2f': root.getChildMeshes().find(m => m.name.includes('__TIME_7SEG_2F__')), + '2g': root.getChildMeshes().find(m => m.name.includes('__TIME_7SEG_2G__')), + '3a': root.getChildMeshes().find(m => m.name.includes('__TIME_7SEG_3A__')), + '3b': root.getChildMeshes().find(m => m.name.includes('__TIME_7SEG_3B__')), + '3c': root.getChildMeshes().find(m => m.name.includes('__TIME_7SEG_3C__')), + '3d': root.getChildMeshes().find(m => m.name.includes('__TIME_7SEG_3D__')), + '3e': root.getChildMeshes().find(m => m.name.includes('__TIME_7SEG_3E__')), + '3f': root.getChildMeshes().find(m => m.name.includes('__TIME_7SEG_3F__')), + '3g': root.getChildMeshes().find(m => m.name.includes('__TIME_7SEG_3G__')), + '4a': root.getChildMeshes().find(m => m.name.includes('__TIME_7SEG_4A__')), + '4b': root.getChildMeshes().find(m => m.name.includes('__TIME_7SEG_4B__')), + '4c': root.getChildMeshes().find(m => m.name.includes('__TIME_7SEG_4C__')), + '4d': root.getChildMeshes().find(m => m.name.includes('__TIME_7SEG_4D__')), + '4e': root.getChildMeshes().find(m => m.name.includes('__TIME_7SEG_4E__')), + '4f': root.getChildMeshes().find(m => m.name.includes('__TIME_7SEG_4F__')), + '4g': root.getChildMeshes().find(m => m.name.includes('__TIME_7SEG_4G__')), + }; + + const onMeshes = get7segMeshesOfCurrentTime(meshes); + + for (const mesh of Object.values(meshes)) { + mesh.isVisible = onMeshes.includes(mesh); + } + }, 1000)); + }, + interactions: {}, + }; + }, +}); diff --git a/packages/frontend/src/utility/room/utility.ts b/packages/frontend/src/utility/room/utility.ts index d60f118a60..0d45e95258 100644 --- a/packages/frontend/src/utility/room/utility.ts +++ b/packages/frontend/src/utility/room/utility.ts @@ -148,3 +148,68 @@ export class HorizontalCameraKeyboardMoveInput extends BABYLON.BaseCameraPointer return 'horizontalkeyboard'; } } + +const nanasegNumberMap = [ + ['a', 'b', 'c', 'd', 'e', 'f'], // 0 + ['b', 'c'], // 1 + ['a', 'b', 'd', 'e', 'g'], // 2 + ['a', 'b', 'c', 'd', 'g'], // 3 + ['b', 'c', 'f', 'g'], // 4 + ['a', 'c', 'd', 'f', 'g'], // 5 + ['a', 'c', 'd', 'e', 'f', 'g'], // 6 + ['a', 'b', 'c'], // 7 + ['a', 'b', 'c', 'd', 'e', 'f', 'g'], // 8 + ['a', 'b', 'c', 'd', 'f', 'g'], // 9 +]; + +export function get7segMeshesOfCurrentTime(meshes: { + '1a'?: BABYLON.AbstractMesh; + '1b'?: BABYLON.AbstractMesh; + '1c'?: BABYLON.AbstractMesh; + '1d'?: BABYLON.AbstractMesh; + '1e'?: BABYLON.AbstractMesh; + '1f'?: BABYLON.AbstractMesh; + '1g'?: BABYLON.AbstractMesh; + '2a'?: BABYLON.AbstractMesh; + '2b'?: BABYLON.AbstractMesh; + '2c'?: BABYLON.AbstractMesh; + '2d'?: BABYLON.AbstractMesh; + '2e'?: BABYLON.AbstractMesh; + '2f'?: BABYLON.AbstractMesh; + '2g'?: BABYLON.AbstractMesh; + '3a'?: BABYLON.AbstractMesh; + '3b'?: BABYLON.AbstractMesh; + '3c'?: BABYLON.AbstractMesh; + '3d'?: BABYLON.AbstractMesh; + '3e'?: BABYLON.AbstractMesh; + '3f'?: BABYLON.AbstractMesh; + '3g'?: BABYLON.AbstractMesh; + '4a'?: BABYLON.AbstractMesh; + '4b'?: BABYLON.AbstractMesh; + '4c'?: BABYLON.AbstractMesh; + '4d'?: BABYLON.AbstractMesh; + '4e'?: BABYLON.AbstractMesh; + '4f'?: BABYLON.AbstractMesh; + '4g'?: BABYLON.AbstractMesh; +}) { + const now = new Date(); + const h = now.getHours(); + const m = now.getMinutes(); + + const chars = [Math.floor(h / 10), h % 10, Math.floor(m / 10), m % 10]; + + const result: BABYLON.AbstractMesh[] = []; + + for (let i = 0; i < chars.length; i++) { + const char = chars[i]; + const segs = nanasegNumberMap[char]; + for (const seg of segs) { + const mesh = meshes[`${i + 1}${seg}`]; + if (mesh) { + result.push(mesh); + } + } + } + + return result; +}