diff --git a/packages/frontend/assets/room/objects/speaker-stand/speaker-stand.blend b/packages/frontend/assets/room/objects/speaker-stand/speaker-stand.blend new file mode 100644 index 0000000000..2641e1a88b Binary files /dev/null and b/packages/frontend/assets/room/objects/speaker-stand/speaker-stand.blend differ diff --git a/packages/frontend/assets/room/objects/speaker-stand/speaker-stand.glb b/packages/frontend/assets/room/objects/speaker-stand/speaker-stand.glb new file mode 100644 index 0000000000..2f9413d7e1 Binary files /dev/null and b/packages/frontend/assets/room/objects/speaker-stand/speaker-stand.glb differ diff --git a/packages/frontend/src/world/room/object-defs.ts b/packages/frontend/src/world/room/object-defs.ts index 760ac86d4a..c5dbcff3e3 100644 --- a/packages/frontend/src/world/room/object-defs.ts +++ b/packages/frontend/src/world/room/object-defs.ts @@ -70,6 +70,7 @@ import { router } from './objects/router.js'; import { siphon } from './objects/siphon.js'; import { snakeplant } from './objects/snakeplant.js'; import { speaker } from './objects/speaker.js'; +import { speakerStand } from './objects/speakerStand.js'; import { sprayer } from './objects/sprayer.js'; import { steelRack } from './objects/steelRack.js'; import { tabletopCalendar } from './objects/tabletopCalendar.js'; @@ -161,6 +162,7 @@ export const OBJECT_DEFS = [ siphon, snakeplant, speaker, + speakerStand, sprayer, steelRack, tabletopCalendar, diff --git a/packages/frontend/src/world/room/objects/speakerStand.ts b/packages/frontend/src/world/room/objects/speakerStand.ts new file mode 100644 index 0000000000..4ab149435d --- /dev/null +++ b/packages/frontend/src/world/room/objects/speakerStand.ts @@ -0,0 +1,65 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import * as BABYLON from '@babylonjs/core'; +import { defineObject } from '../object.js'; + +export const speakerStand = defineObject({ + id: 'speakerStand', + name: 'speakerStand', + options: { + schema: { + bodyColor: { + type: 'color', + label: 'Body color', + }, + height: { + type: 'range', + label: 'height', + min: 0, + max: 1, + step: 0.01, + }, + }, + default: { + bodyColor: [0.2, 0.2, 0.2], + height: 0.1, + }, + }, + placement: 'top', + hasCollisions: false, + hasTexture: false, + createInstance: ({ options, model }) => { + const bodyMaterial = model.findMaterial('__X_BODY__'); + + const applyBodyColor = () => { + const [r, g, b] = options.bodyColor; + bodyMaterial.albedoColor = new BABYLON.Color3(r, g, b); + }; + + applyBodyColor(); + + const applySize = () => { + for (const mesh of model.root.getChildMeshes()) { + if (mesh.morphTargetManager != null && mesh.morphTargetManager.getTargetByName('Height') != null) { + mesh.morphTargetManager.getTargetByName('Height').influence = options.height; + } + } + model.updated(); + }; + + applySize(); + + return { + onOptionsUpdated: ([k, v]) => { + switch (k) { + case 'bodyColor': applyBodyColor(); break; + case 'height': applySize(); break; + } + }, + interactions: {}, + }; + }, +});