diff --git a/packages/frontend/assets/room/objects/random-books/texture.png b/packages/frontend/assets/room/objects/random-books/texture.png index e4e3a6419a..9b8d75bbd8 100644 Binary files a/packages/frontend/assets/room/objects/random-books/texture.png and b/packages/frontend/assets/room/objects/random-books/texture.png differ diff --git a/packages/frontend/src/utility/room/engine.ts b/packages/frontend/src/utility/room/engine.ts index e69e572a08..e1b37336e0 100644 --- a/packages/frontend/src/utility/room/engine.ts +++ b/packages/frontend/src/utility/room/engine.ts @@ -320,6 +320,7 @@ type ObjectDef = { root: BABYLON.Mesh; options: Readonly>; model: ModelManager; + id: string; }) => RoomObjectInstance> | Promise>>; // TODO: createInstanceをasyncにするのではなく、別にreadyみたいなものを返させる }; @@ -1245,6 +1246,7 @@ export class RoomEngine { root, options: args.options, model, + id: args.id, }); objectInstance.onInited?.(); @@ -1852,9 +1854,12 @@ export class RoomObjectPreviewEngine { const options = deepClone(def.options.default); + const id = genId(); + await this.loadObject({ type, options, + id, }); // なぜかちょっと待たないとbounding boxのサイズが正しくない @@ -1877,6 +1882,7 @@ export class RoomObjectPreviewEngine { private async loadObject(args: { type: string; options: any; + id: string; }) { const def = getObjectDef(args.type); @@ -1931,6 +1937,7 @@ export class RoomObjectPreviewEngine { root, options: args.options, model, + id: args.id, }); objectInstance.onInited?.(); diff --git a/packages/frontend/src/utility/room/objects/randomBooks.ts b/packages/frontend/src/utility/room/objects/randomBooks.ts index f515585898..f317a6bb8d 100644 --- a/packages/frontend/src/utility/room/objects/randomBooks.ts +++ b/packages/frontend/src/utility/room/objects/randomBooks.ts @@ -2,7 +2,9 @@ * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ + import * as BABYLON from '@babylonjs/core'; +import seedrandom from 'seedrandom'; import { defineObject, WORLD_SCALE } from '../engine.js'; const randomRange = (min: number, max: number) => Math.random() * (max - min) + min; @@ -20,13 +22,21 @@ export const randomBooks = defineObject({ type: 'boolean', label: 'Plain cover', }, + seed: { + type: 'range', + label: 'Seed', + min: 0, + max: 1000, + step: 1, + }, }, default: { plainCover: false, + seed: 0, }, }, placement: 'top', - createInstance: ({ options, model, scene }) => { + createInstance: ({ options, model, scene, id }) => { const bodyMesh = model.findMesh('__X_BODY__'); const tex = new BABYLON.Texture('/client-assets/room/objects/random-books/texture.png', scene, { invertY: false, @@ -36,54 +46,74 @@ export const randomBooks = defineObject({ const TEXTURE_DIVISION = 8; const count = 10; + let bookMeshes: BABYLON.Mesh[] = []; - let accumulatedPos = 0; - const meshes: BABYLON.Mesh[] = []; - - for (let i = 0; i < count; i++) { - const mesh = bodyMesh.clone(); - mesh.makeGeometryUnique(); - mesh.morphTargetManager = bodyMesh.morphTargetManager.clone(); - mesh.markVerticesDataAsUpdatable(BABYLON.VertexBuffer.UVKind, true); - - const index = Math.floor(Math.random() * 27); - const x = index % TEXTURE_DIVISION; - const y = Math.floor(index / TEXTURE_DIVISION); - - const uvs = mesh.getVerticesData(BABYLON.VertexBuffer.UVKind)!; - for (let uvi = 0; uvi < uvs.length; uvi += 2) { - const u = uvs[uvi]; - const v = uvs[uvi + 1]; - uvs[uvi] = (u / TEXTURE_DIVISION) + (x / TEXTURE_DIVISION); - uvs[uvi + 1] = (v / TEXTURE_DIVISION) + (y / TEXTURE_DIVISION); + const gen = () => { + for (const m of bookMeshes) { + m.dispose(); } - mesh.updateVerticesData(BABYLON.VertexBuffer.UVKind, uvs); + bookMeshes = []; - const width = randomRange(0.1, 0.2); - const height = randomRange(0.3, 0.4); - const thickness = randomRange(0, 0.03); - mesh.morphTargetManager!.getTargetByName('Width')!.influence = width; - mesh.morphTargetManager!.getTargetByName('Height')!.influence = height; - mesh.morphTargetManager!.getTargetByName('Thickness')!.influence = thickness; - const thicknessCm = 2 + remap(thickness, 0, 1, 0, 100); - const gap = 0.25; - mesh.position.x = (accumulatedPos + (thicknessCm / 2)) / WORLD_SCALE; - accumulatedPos += thicknessCm + gap; - meshes.push(mesh); - } + const rng = seedrandom(options.seed === 0 ? id : options.seed.toString()); - // centering - for (let i = 0; i < count; i++) { - meshes[i].position.x -= accumulatedPos / 2 / WORLD_SCALE; - } + let accumulatedPos = 0; - bodyMesh.isVisible = false; + for (let i = 0; i < count; i++) { + const mesh = bodyMesh.clone(); + mesh.isVisible = true; + mesh.setEnabled(true); + mesh.makeGeometryUnique(); + mesh.morphTargetManager = bodyMesh.morphTargetManager.clone(); + mesh.markVerticesDataAsUpdatable(BABYLON.VertexBuffer.UVKind, true); - model.updated(); + const index = Math.floor(rng() * 44); + const x = index % TEXTURE_DIVISION; + const y = Math.floor(index / TEXTURE_DIVISION); + + const uvs = mesh.getVerticesData(BABYLON.VertexBuffer.UVKind)!; + for (let uvi = 0; uvi < uvs.length; uvi += 2) { + const u = uvs[uvi]; + const v = uvs[uvi + 1]; + uvs[uvi] = (u / TEXTURE_DIVISION) + (x / TEXTURE_DIVISION); + uvs[uvi + 1] = (v / TEXTURE_DIVISION) + (y / TEXTURE_DIVISION); + } + mesh.updateVerticesData(BABYLON.VertexBuffer.UVKind, uvs); + + const width = randomRange(0.125, 0.175); + const height = randomRange(0.3, 0.4); + const thickness = randomRange(0, 0.03); + mesh.morphTargetManager!.getTargetByName('Width')!.influence = width; + mesh.morphTargetManager!.getTargetByName('Height')!.influence = height; + mesh.morphTargetManager!.getTargetByName('Thickness')!.influence = thickness; + const thicknessCm = 2 + remap(thickness, 0, 1, 0, 100); + const widthCm = 2 + remap(width, 0, 1, 0, 100); + const gap = 0.25; + mesh.position.x = (accumulatedPos + (thicknessCm / 2)) / WORLD_SCALE; + mesh.position.z = widthCm / 2 / WORLD_SCALE; + accumulatedPos += thicknessCm + gap; + bookMeshes.push(mesh); + } + + // centering + for (let i = 0; i < count; i++) { + bookMeshes[i].position.x -= accumulatedPos / 2 / WORLD_SCALE; + } + + bodyMesh.isVisible = false; + + model.updated(); + }; + + gen(); return { onInited: () => { + }, + onOptionsUpdated: ([k, v]) => { + switch (k) { + case 'seed': gen(); break; + } }, interactions: {}, };