summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorsyuilo <Syuilotan@yahoo.co.jp>2019-08-18 14:41:33 +0900
committerGitHub <noreply@github.com>2019-08-18 14:41:33 +0900
commit45b972c059e9841da742538b8a948eefca7c9153 (patch)
treee1cd44014f09ef3de7d3d317ddd021a7fecd0539 /src
parentupdateHashtagを並列で行わないように (#5284) (diff)
downloadsharkey-45b972c059e9841da742538b8a948eefca7c9153.tar.gz
sharkey-45b972c059e9841da742538b8a948eefca7c9153.tar.bz2
sharkey-45b972c059e9841da742538b8a948eefca7c9153.zip
MisskeyRoom (#5267)
* wip * Add pemcil * Fix bug * Update .gitattributes * Add :custard: * Better :custard: * Add color boxes * Add pc * Add keyboard * Add :package: * Add more :package: * :v: * carpet * Add plant * :v: * :v: * Update room.vue * Add plant * :v: * :v: * :v: * :v: * :v: * 段ボール箱がてかりすぎているのを修正 * Update room.ts * Render username * :v: * Add new :package: * Update room.ts * Remove blender backup files * Refactor * Improve performance * Update room.ts * Update .gitattributes * Update room.ts * Better fan * Better tissue rendering * Add :penguin: * Create photoframe2.glb * chairs * Add :book: * fix: HiDPi環境でオブジェクトを選択できない (#5268) * Better monitor * :v: * Add corkboard * Add missing blend * mousepad * Add missing blend * Add cube * 額縁やモニターなどに任意の画像を表示できるように * Update MisskeyRoom section of CONTRIBUTING.md (#5272) * Update MisskeyRoom section of CONTRIBUTING.md * Update CONTRIBUTING.md * Update CONTRIBUTING.md * Refactor * カスタムテクスチャがずれないように * Remove debug code * Update furnitures.json5 * 一部の家具の色を自由に変えられるように * Update ja-JP.yml * Type annotation * 家具の色やテクスチャをすぐ反映するように * Update room.vue * Update furnitures.json5 * Add :tv: * Update ja-JP.yml * 床の色を変えられるように * 和室にできるように * Update washitsu * Use MeshLambertMaterial to improve performance * Use MeshLambertMaterial * Fix bug * Refactor * Update room.ts * Fix washitsu * Update room.vue * Update washistu * Use MeshLambertMaterial * Update room.ts * Set current property value * Disable reactivity to improve performance a bit * Fix bug * Set current carpet color * Update ja-JP.yml * Add rubik-cube (#5278) * Update ja-JP.yml (#5279) * Update UI * ルームの設定を追加 * Add room link * 家具をドラッグで移動や回転できるように * esnextにする (#5286) * Fix moduleResolution * Use uuid v4 * Fix bug * マットの色を変えられるように * :v: * 異方性フィルタリングするように * グラフィックの品質をフィルタリングに反映 * Add bloom effect when ultra graphics * Add posters * :art:
Diffstat (limited to 'src')
-rw-r--r--src/client/app/common/scripts/room/furniture.ts21
-rw-r--r--src/client/app/common/scripts/room/furnitures.json5324
-rw-r--r--src/client/app/common/scripts/room/room.ts690
-rw-r--r--src/client/app/common/views/components/settings/settings.vue23
-rw-r--r--src/client/app/common/views/pages/room/preview.vue98
-rw-r--r--src/client/app/common/views/pages/room/room.vue237
-rw-r--r--src/client/app/desktop/script.ts1
-rw-r--r--src/client/app/desktop/views/components/ui.header.account.vue11
-rw-r--r--src/client/app/store.ts2
-rw-r--r--src/client/app/tsconfig.json8
-rw-r--r--src/client/assets/room/furnitures/bed/bed.blendbin0 -> 573584 bytes
-rw-r--r--src/client/assets/room/furnitures/bed/bed.glbbin0 -> 8944 bytes
-rw-r--r--src/client/assets/room/furnitures/book/book.blendbin0 -> 511864 bytes
-rw-r--r--src/client/assets/room/furnitures/book/book.glbbin0 -> 4304 bytes
-rw-r--r--src/client/assets/room/furnitures/book2/barcode.pngbin0 -> 8924 bytes
-rw-r--r--src/client/assets/room/furnitures/book2/book2.blendbin0 -> 521456 bytes
-rw-r--r--src/client/assets/room/furnitures/book2/book2.glbbin0 -> 82920 bytes
-rw-r--r--src/client/assets/room/furnitures/book2/texture.afdesignbin0 -> 875434 bytes
-rw-r--r--src/client/assets/room/furnitures/book2/texture.pngbin0 -> 65589 bytes
-rw-r--r--src/client/assets/room/furnitures/book2/uv.pngbin0 -> 38259 bytes
-rw-r--r--src/client/assets/room/furnitures/cardboard-box/cardboard-box.blendbin0 -> 515036 bytes
-rw-r--r--src/client/assets/room/furnitures/cardboard-box/cardboard-box.glbbin0 -> 4568 bytes
-rw-r--r--src/client/assets/room/furnitures/cardboard-box2/cardboard-box2.blendbin0 -> 545788 bytes
-rw-r--r--src/client/assets/room/furnitures/cardboard-box2/cardboard-box2.glbbin0 -> 24084 bytes
-rw-r--r--src/client/assets/room/furnitures/cardboard-box2/texture.pngbin0 -> 19086 bytes
-rw-r--r--src/client/assets/room/furnitures/cardboard-box2/uv.pngbin0 -> 22896 bytes
-rw-r--r--src/client/assets/room/furnitures/cardboard-box3/cardboard-box3.blendbin0 -> 547868 bytes
-rw-r--r--src/client/assets/room/furnitures/cardboard-box3/cardboard-box3.glbbin0 -> 23668 bytes
-rw-r--r--src/client/assets/room/furnitures/cardboard-box3/texture.pngbin0 -> 18670 bytes
-rw-r--r--src/client/assets/room/furnitures/cardboard-box3/texture.xcfbin0 -> 242066 bytes
-rw-r--r--src/client/assets/room/furnitures/cardboard-box3/uv.pngbin0 -> 22098 bytes
-rw-r--r--src/client/assets/room/furnitures/carpet-stripe/carpet-stripe.blendbin0 -> 481784 bytes
-rw-r--r--src/client/assets/room/furnitures/carpet-stripe/carpet-stripe.glbbin0 -> 4956 bytes
-rw-r--r--src/client/assets/room/furnitures/chair/chair.blendbin0 -> 500696 bytes
-rw-r--r--src/client/assets/room/furnitures/chair/chair.glbbin0 -> 4252 bytes
-rw-r--r--src/client/assets/room/furnitures/chair2/chair2.blendbin0 -> 523036 bytes
-rw-r--r--src/client/assets/room/furnitures/chair2/chair2.glbbin0 -> 6140 bytes
-rw-r--r--src/client/assets/room/furnitures/color-box/color-box.blendbin0 -> 631168 bytes
-rw-r--r--src/client/assets/room/furnitures/color-box/color-box.glbbin0 -> 7492 bytes
-rw-r--r--src/client/assets/room/furnitures/corkboard/corkboard.blendbin0 -> 565404 bytes
-rw-r--r--src/client/assets/room/furnitures/corkboard/corkboard.glbbin0 -> 7180 bytes
-rw-r--r--src/client/assets/room/furnitures/cube/cube.blendbin0 -> 646184 bytes
-rw-r--r--src/client/assets/room/furnitures/cube/cube.glbbin0 -> 188392 bytes
-rw-r--r--src/client/assets/room/furnitures/desk/desk.blendbin0 -> 541980 bytes
-rw-r--r--src/client/assets/room/furnitures/desk/desk.glbbin0 -> 10184 bytes
-rw-r--r--src/client/assets/room/furnitures/eraser/cover.pngbin0 -> 11006 bytes
-rw-r--r--src/client/assets/room/furnitures/eraser/cover.psdbin0 -> 219123 bytes
-rw-r--r--src/client/assets/room/furnitures/eraser/eraser-uv.pngbin0 -> 28597 bytes
-rw-r--r--src/client/assets/room/furnitures/eraser/eraser.blendbin0 -> 527996 bytes
-rw-r--r--src/client/assets/room/furnitures/eraser/eraser.glbbin0 -> 15748 bytes
-rw-r--r--src/client/assets/room/furnitures/facial-tissue/facial-tissue-uv.pngbin0 -> 23516 bytes
-rw-r--r--src/client/assets/room/furnitures/facial-tissue/facial-tissue.blendbin0 -> 533220 bytes
-rw-r--r--src/client/assets/room/furnitures/facial-tissue/facial-tissue.glbbin0 -> 8332 bytes
-rw-r--r--src/client/assets/room/furnitures/facial-tissue/facial-tissue.pngbin0 -> 3888 bytes
-rw-r--r--src/client/assets/room/furnitures/facial-tissue/facial-tissue.psdbin0 -> 111968 bytes
-rw-r--r--src/client/assets/room/furnitures/fan/fan.blendbin0 -> 772732 bytes
-rw-r--r--src/client/assets/room/furnitures/fan/fan.glbbin0 -> 51612 bytes
-rw-r--r--src/client/assets/room/furnitures/keyboard/keyboard.blendbin0 -> 1005588 bytes
-rw-r--r--src/client/assets/room/furnitures/keyboard/keyboard.glbbin0 -> 132360 bytes
-rw-r--r--src/client/assets/room/furnitures/low-table/low-table.blendbin0 -> 497336 bytes
-rw-r--r--src/client/assets/room/furnitures/low-table/low-table.glbbin0 -> 2364 bytes
-rw-r--r--src/client/assets/room/furnitures/mat/mat.blendbin0 -> 489304 bytes
-rw-r--r--src/client/assets/room/furnitures/mat/mat.glbbin0 -> 1700 bytes
-rw-r--r--src/client/assets/room/furnitures/milk/milk-uv.pngbin0 -> 50388 bytes
-rw-r--r--src/client/assets/room/furnitures/milk/milk.blendbin0 -> 539836 bytes
-rw-r--r--src/client/assets/room/furnitures/milk/milk.glbbin0 -> 8456 bytes
-rw-r--r--src/client/assets/room/furnitures/milk/milk.pngbin0 -> 3846 bytes
-rw-r--r--src/client/assets/room/furnitures/milk/milk.psdbin0 -> 125415 bytes
-rw-r--r--src/client/assets/room/furnitures/monitor/monitor.blendbin0 -> 586836 bytes
-rw-r--r--src/client/assets/room/furnitures/monitor/monitor.glbbin0 -> 40092 bytes
-rw-r--r--src/client/assets/room/furnitures/monitor/monitor.psdbin0 -> 3692204 bytes
-rw-r--r--src/client/assets/room/furnitures/monitor/screen-uv.pngbin0 -> 5951 bytes
-rw-r--r--src/client/assets/room/furnitures/monitor/screen.jpgbin0 -> 29629 bytes
-rw-r--r--src/client/assets/room/furnitures/moon/moon.blendbin0 -> 725536 bytes
-rw-r--r--src/client/assets/room/furnitures/moon/moon.glbbin0 -> 122112 bytes
-rw-r--r--src/client/assets/room/furnitures/moon/moon.jpgbin0 -> 87155 bytes
-rw-r--r--src/client/assets/room/furnitures/mousepad/mousepad.blendbin0 -> 495008 bytes
-rw-r--r--src/client/assets/room/furnitures/mousepad/mousepad.glbbin0 -> 4360 bytes
-rw-r--r--src/client/assets/room/furnitures/pc/motherboard-uv.pngbin0 -> 21944 bytes
-rw-r--r--src/client/assets/room/furnitures/pc/motherboard-uv.psdbin0 -> 7131432 bytes
-rw-r--r--src/client/assets/room/furnitures/pc/motherboard.jpgbin0 -> 68860 bytes
-rw-r--r--src/client/assets/room/furnitures/pc/pc.blendbin0 -> 818152 bytes
-rw-r--r--src/client/assets/room/furnitures/pc/pc.glbbin0 -> 103620 bytes
-rw-r--r--src/client/assets/room/furnitures/pencil/pencil.blendbin0 -> 683032 bytes
-rw-r--r--src/client/assets/room/furnitures/pencil/pencil.glbbin0 -> 7788 bytes
-rw-r--r--src/client/assets/room/furnitures/photoframe/photo-uv.pngbin0 -> 20828 bytes
-rw-r--r--src/client/assets/room/furnitures/photoframe/photo.jpgbin0 -> 31845 bytes
-rw-r--r--src/client/assets/room/furnitures/photoframe/photoframe.blendbin0 -> 551416 bytes
-rw-r--r--src/client/assets/room/furnitures/photoframe/photoframe.glbbin0 -> 37612 bytes
-rw-r--r--src/client/assets/room/furnitures/piano/piano.blendbin0 -> 713228 bytes
-rw-r--r--src/client/assets/room/furnitures/piano/piano.glbbin0 -> 73320 bytes
-rw-r--r--src/client/assets/room/furnitures/pinguin/pinguin.glbbin0 -> 60192 bytes
-rw-r--r--src/client/assets/room/furnitures/plant/plant-soil-uv.pngbin0 -> 23726 bytes
-rw-r--r--src/client/assets/room/furnitures/plant/plant-soil.pngbin0 -> 13370 bytes
-rw-r--r--src/client/assets/room/furnitures/plant/plant-soil.psdbin0 -> 390188 bytes
-rw-r--r--src/client/assets/room/furnitures/plant/plant.blendbin0 -> 604488 bytes
-rw-r--r--src/client/assets/room/furnitures/plant/plant.glbbin0 -> 27664 bytes
-rw-r--r--src/client/assets/room/furnitures/plant2/plant2.blendbin0 -> 1060816 bytes
-rw-r--r--src/client/assets/room/furnitures/plant2/plant2.glbbin0 -> 133212 bytes
-rw-r--r--src/client/assets/room/furnitures/plant2/soil.pngbin0 -> 13370 bytes
-rw-r--r--src/client/assets/room/furnitures/poster-h/poster-h.blendbin0 -> 667604 bytes
-rw-r--r--src/client/assets/room/furnitures/poster-h/poster-h.glbbin0 -> 1280 bytes
-rw-r--r--src/client/assets/room/furnitures/poster-h/uv.pngbin0 -> 20629 bytes
-rw-r--r--src/client/assets/room/furnitures/poster-v/poster-v.blendbin0 -> 650740 bytes
-rw-r--r--src/client/assets/room/furnitures/poster-v/poster-v.glbbin0 -> 1280 bytes
-rw-r--r--src/client/assets/room/furnitures/poster-v/uv.pngbin0 -> 22374 bytes
-rw-r--r--src/client/assets/room/furnitures/pudding/pudding.blendbin0 -> 694784 bytes
-rw-r--r--src/client/assets/room/furnitures/pudding/pudding.glbbin0 -> 10612 bytes
-rw-r--r--src/client/assets/room/furnitures/rubik-cube/rubik-cube.blendbin0 -> 745960 bytes
-rw-r--r--src/client/assets/room/furnitures/rubik-cube/rubik-cube.glbbin0 -> 67688 bytes
-rw-r--r--src/client/assets/room/furnitures/server/rack-uv.pngbin0 -> 27318 bytes
-rw-r--r--src/client/assets/room/furnitures/server/rack.pngbin0 -> 9641 bytes
-rw-r--r--src/client/assets/room/furnitures/server/server.blendbin0 -> 684372 bytes
-rw-r--r--src/client/assets/room/furnitures/server/server.glbbin0 -> 149480 bytes
-rw-r--r--src/client/assets/room/furnitures/server/server.pngbin0 -> 124668 bytes
-rw-r--r--src/client/assets/room/furnitures/server/uv.pngbin0 -> 22827 bytes
-rw-r--r--src/client/assets/room/furnitures/tv/screen-uv.pngbin0 -> 21040 bytes
-rw-r--r--src/client/assets/room/furnitures/tv/tv.blendbin0 -> 587640 bytes
-rw-r--r--src/client/assets/room/furnitures/tv/tv.glbbin0 -> 8580 bytes
-rw-r--r--src/client/assets/room/furnitures/wall-clock/wall-clock.blendbin0 -> 574128 bytes
-rw-r--r--src/client/assets/room/furnitures/wall-clock/wall-clock.glbbin0 -> 47848 bytes
-rw-r--r--src/client/assets/room/rooms/default/default.blendbin0 -> 618364 bytes
-rw-r--r--src/client/assets/room/rooms/default/default.glbbin0 -> 12280 bytes
-rw-r--r--src/client/assets/room/rooms/washitsu/husuma-uv.pngbin0 -> 22586 bytes
-rw-r--r--src/client/assets/room/rooms/washitsu/husuma.pngbin0 -> 4336 bytes
-rw-r--r--src/client/assets/room/rooms/washitsu/tatami-single1600.pngbin0 -> 1191346 bytes
-rw-r--r--src/client/assets/room/rooms/washitsu/tatami-uv.pngbin0 -> 22804 bytes
-rw-r--r--src/client/assets/room/rooms/washitsu/tatami.afdesignbin0 -> 2501804 bytes
-rw-r--r--src/client/assets/room/rooms/washitsu/tatami.pngbin0 -> 315585 bytes
-rw-r--r--src/client/assets/room/rooms/washitsu/washitsu.blendbin0 -> 664000 bytes
-rw-r--r--src/client/assets/room/rooms/washitsu/washitsu.glbbin0 -> 415180 bytes
-rw-r--r--src/models/entities/user-profile.ts6
-rw-r--r--src/server/api/endpoints/room/show.ts92
-rw-r--r--src/server/api/endpoints/room/update.ts48
134 files changed, 1558 insertions, 3 deletions
diff --git a/src/client/app/common/scripts/room/furniture.ts b/src/client/app/common/scripts/room/furniture.ts
new file mode 100644
index 0000000000..7734e32668
--- /dev/null
+++ b/src/client/app/common/scripts/room/furniture.ts
@@ -0,0 +1,21 @@
+export type RoomInfo = {
+ roomType: string;
+ carpetColor: string;
+ furnitures: Furniture[];
+};
+
+export type Furniture = {
+ id: string; // 同じ家具が複数ある場合にそれぞれを識別するためのIDであり、家具IDではない
+ type: string; // こっちが家具ID(chairとか)
+ position: {
+ x: number;
+ y: number;
+ z: number;
+ };
+ rotation: {
+ x: number;
+ y: number;
+ z: number;
+ };
+ props?: Record<string, any>;
+};
diff --git a/src/client/app/common/scripts/room/furnitures.json5 b/src/client/app/common/scripts/room/furnitures.json5
new file mode 100644
index 0000000000..d88228ca36
--- /dev/null
+++ b/src/client/app/common/scripts/room/furnitures.json5
@@ -0,0 +1,324 @@
+// 家具メタデータ
+
+// 家具にはユーザーが設定できるプロパティを設定可能です:
+//
+// props: {
+// <propname>: <proptype>
+// }
+//
+// proptype一覧:
+// * image ... 画像選択ダイアログを出し、その画像のURLが格納されます
+// * color ... 色選択コントロールを出し、選択された色が格納されます
+
+// 家具にカスタムテクスチャを適用できるようにするには、textureプロパティに以下の追加の情報を含めます:
+// 便宜上そのUVのどの部分にカスタムテクスチャを貼り合わせるかのエリアをテクスチャエリアと呼びます。
+// UVは1024*1024だと仮定します。
+//
+// <key>: {
+// prop: <プロパティ名>,
+// uv: {
+// x: <テクスチャエリアX座標>,
+// y: <テクスチャエリアY座標>,
+// width: <テクスチャエリアの幅>,
+// height: <テクスチャエリアの高さ>,
+// },
+// }
+//
+// <key>には、カスタムテクスチャを適用したいメッシュ名を指定します
+// <プロパティ名>には、カスタムテクスチャとして使用する画像を格納するプロパティ(前述)名を指定します
+
+// 家具にカスタムカラーを適用できるようにするには、colorプロパティに以下の追加の情報を含めます:
+//
+// <key>: <プロパティ名>
+//
+// <key>には、カスタムカラーを適用したいマテリアル名を指定します
+// <プロパティ名>には、カスタムカラーとして使用する色を格納するプロパティ(前述)名を指定します
+
+[
+ {
+ id: "milk",
+ place: "floor"
+ },
+ {
+ id: "bed",
+ place: "floor"
+ },
+ {
+ id: "low-table",
+ place: "floor",
+ props: {
+ color: 'color'
+ },
+ color: {
+ Table: 'color'
+ }
+ },
+ {
+ id: "desk",
+ place: "floor",
+ props: {
+ color: 'color'
+ },
+ color: {
+ Board: 'color'
+ }
+ },
+ {
+ id: "chair",
+ place: "floor",
+ props: {
+ color: 'color'
+ },
+ color: {
+ Chair: 'color'
+ }
+ },
+ {
+ id: "chair2",
+ place: "floor",
+ props: {
+ color1: 'color',
+ color2: 'color'
+ },
+ color: {
+ Cushion: 'color1',
+ Leg: 'color2'
+ }
+ },
+ {
+ id: "fan",
+ place: "wall"
+ },
+ {
+ id: "pc",
+ place: "floor"
+ },
+ {
+ id: "plant",
+ place: "floor"
+ },
+ {
+ id: "plant2",
+ place: "floor"
+ },
+ {
+ id: "eraser",
+ place: "floor"
+ },
+ {
+ id: "pencil",
+ place: "floor"
+ },
+ {
+ id: "pudding",
+ place: "floor"
+ },
+ {
+ id: "cardboard-box",
+ place: "floor"
+ },
+ {
+ id: "cardboard-box2",
+ place: "floor"
+ },
+ {
+ id: "cardboard-box3",
+ place: "floor"
+ },
+ {
+ id: "book",
+ place: "floor",
+ props: {
+ color: 'color'
+ },
+ color: {
+ Cover: 'color'
+ }
+ },
+ {
+ id: "book2",
+ place: "floor"
+ },
+ {
+ id: "piano",
+ place: "floor"
+ },
+ {
+ id: "facial-tissue",
+ place: "floor"
+ },
+ {
+ id: "server",
+ place: "floor"
+ },
+ {
+ id: "moon",
+ place: "floor"
+ },
+ {
+ id: "corkboard",
+ place: "wall"
+ },
+ {
+ id: "mousepad",
+ place: "floor",
+ props: {
+ color: 'color'
+ },
+ color: {
+ Pad: 'color'
+ }
+ },
+ {
+ id: "monitor",
+ place: "floor",
+ props: {
+ screen: 'image'
+ },
+ texture: {
+ Screen: {
+ prop: 'screen',
+ uv: {
+ x: 0,
+ y: 434,
+ width: 1024,
+ height: 588,
+ },
+ },
+ },
+ },
+ {
+ id: "tv",
+ place: "floor",
+ props: {
+ screen: 'image'
+ },
+ texture: {
+ Screen: {
+ prop: 'screen',
+ uv: {
+ x: 0,
+ y: 434,
+ width: 1024,
+ height: 588,
+ },
+ },
+ },
+ },
+ {
+ id: "keyboard",
+ place: "floor"
+ },
+ {
+ id: "carpet-stripe",
+ place: "floor",
+ props: {
+ color1: 'color',
+ color2: 'color'
+ },
+ color: {
+ CarpetAreaA: 'color1',
+ CarpetAreaB: 'color2'
+ },
+ },
+ {
+ id: "mat",
+ place: "floor",
+ props: {
+ color: 'color'
+ },
+ color: {
+ Mat: 'color'
+ }
+ },
+ {
+ id: "color-box",
+ place: "floor",
+ props: {
+ color: 'color'
+ },
+ color: {
+ main: 'color'
+ }
+ },
+ {
+ id: "wall-clock",
+ place: "wall"
+ },
+ {
+ id: "cube",
+ place: "floor",
+ props: {
+ color: 'color'
+ },
+ color: {
+ Cube: 'color'
+ }
+ },
+ {
+ id: "photoframe",
+ place: "wall",
+ props: {
+ photo: 'image',
+ color: 'color'
+ },
+ texture: {
+ Photo: {
+ prop: 'photo',
+ uv: {
+ x: 0,
+ y: 342,
+ width: 1024,
+ height: 683,
+ },
+ },
+ },
+ color: {
+ Frame: 'color'
+ }
+ },
+ {
+ id: "pinguin",
+ place: "floor"
+ },
+ {
+ id: "rubik-cube",
+ place: "floor",
+ },
+ {
+ id: "poster-h",
+ place: "wall",
+ props: {
+ picture: 'image'
+ },
+ texture: {
+ Poster: {
+ prop: 'picture',
+ uv: {
+ x: 0,
+ y: 277,
+ width: 1024,
+ height: 745,
+ },
+ },
+ },
+ },
+ {
+ id: "poster-v",
+ place: "wall",
+ props: {
+ picture: 'image'
+ },
+ texture: {
+ Poster: {
+ prop: 'picture',
+ uv: {
+ x: 0,
+ y: 0,
+ width: 745,
+ height: 1024,
+ },
+ },
+ },
+ },
+]
diff --git a/src/client/app/common/scripts/room/room.ts b/src/client/app/common/scripts/room/room.ts
new file mode 100644
index 0000000000..2a06f5bc5b
--- /dev/null
+++ b/src/client/app/common/scripts/room/room.ts
@@ -0,0 +1,690 @@
+import autobind from 'autobind-decorator';
+import * as THREE from 'three';
+import { GLTFLoader, GLTF } from 'three/examples/jsm/loaders/GLTFLoader';
+import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
+import { EffectComposer } from 'three/examples/jsm/postprocessing/EffectComposer.js';
+import { RenderPass } from 'three/examples/jsm/postprocessing/RenderPass.js';
+import { ShaderPass } from 'three/examples/jsm/postprocessing/ShaderPass.js';
+import { BloomPass } from 'three/examples/jsm/postprocessing/BloomPass.js';
+import { FXAAShader } from 'three/examples/jsm/shaders/FXAAShader.js';
+import { TransformControls } from 'three/examples/jsm/controls/TransformControls.js';
+import { Furniture, RoomInfo } from './furniture';
+import { v4 as uuid } from 'uuid';
+const furnitureDefs = require('./furnitures.json5');
+
+THREE.ImageUtils.crossOrigin = '';
+
+type Options = {
+ graphicsQuality: Room['graphicsQuality'];
+ onChangeSelect: Room['onChangeSelect'];
+ useOrthographicCamera: boolean;
+};
+
+export class Room {
+ private clock: THREE.Clock;
+ private scene: THREE.Scene;
+ private renderer: THREE.WebGLRenderer;
+ private camera: THREE.PerspectiveCamera | THREE.OrthographicCamera;
+ private controls: OrbitControls;
+ private composer: EffectComposer;
+ private mixers: THREE.AnimationMixer[] = [];
+ private furnitureControl: TransformControls;
+ private roomInfo: RoomInfo;
+ private graphicsQuality: 'cheep' | 'low' | 'medium' | 'high' | 'ultra';
+ private roomObj: THREE.Object3D;
+ private objects: THREE.Object3D[] = [];
+ private selectedObject: THREE.Object3D = null;
+ private onChangeSelect: Function;
+ private isTransformMode = false;
+ public canvas: HTMLCanvasElement;
+
+ private get furnitures(): Furniture[] {
+ return this.roomInfo.furnitures;
+ }
+
+ private set furnitures(furnitures: Furniture[]) {
+ this.roomInfo.furnitures = furnitures;
+ }
+
+ private get enableShadow() {
+ return this.graphicsQuality != 'cheep';
+ }
+
+ private get usePostFXs() {
+ return this.graphicsQuality !== 'cheep' && this.graphicsQuality !== 'low';
+ }
+
+ private get shadowQuality() {
+ return (
+ this.graphicsQuality === 'ultra' ? 16384 :
+ this.graphicsQuality === 'high' ? 8192 :
+ this.graphicsQuality === 'medium' ? 4096 :
+ this.graphicsQuality === 'low' ? 1024 :
+ 0); // cheep
+ }
+
+ constructor(user, isMyRoom, roomInfo: RoomInfo, container, options: Options) {
+ this.roomInfo = roomInfo;
+ this.graphicsQuality = options.graphicsQuality;
+ this.onChangeSelect = options.onChangeSelect;
+
+ this.clock = new THREE.Clock(true);
+
+ //#region Init a scene
+ this.scene = new THREE.Scene();
+
+ const width = window.innerWidth;
+ const height = window.innerHeight;
+
+ //#region Init a renderer
+ this.renderer = new THREE.WebGLRenderer({
+ antialias: false,
+ stencil: false,
+ alpha: false,
+ powerPreference:
+ this.graphicsQuality === 'ultra' ? 'high-performance' :
+ this.graphicsQuality === 'high' ? 'high-performance' :
+ this.graphicsQuality === 'medium' ? 'default' :
+ this.graphicsQuality === 'low' ? 'low-power' :
+ 'low-power' // cheep
+ });
+
+ this.renderer.setPixelRatio(window.devicePixelRatio);
+ this.renderer.setSize(width, height);
+ this.renderer.autoClear = false;
+ this.renderer.setClearColor(new THREE.Color(0x051f2d));
+ this.renderer.shadowMap.enabled = this.enableShadow;
+ this.renderer.gammaOutput = true;
+ this.renderer.shadowMap.type =
+ this.graphicsQuality === 'ultra' ? THREE.PCFSoftShadowMap :
+ this.graphicsQuality === 'high' ? THREE.PCFSoftShadowMap :
+ this.graphicsQuality === 'medium' ? THREE.PCFShadowMap :
+ this.graphicsQuality === 'low' ? THREE.BasicShadowMap :
+ THREE.BasicShadowMap; // cheep
+
+ this.canvas = this.renderer.domElement;
+ container.appendChild(this.renderer.domElement);
+ //#endregion
+
+ //#region Init a camera
+ this.camera = options.useOrthographicCamera
+ ? new THREE.OrthographicCamera(
+ width / - 2, width / 2, height / 2, height / - 2, -10, 10)
+ : new THREE.PerspectiveCamera(45, width / height);
+
+ if (options.useOrthographicCamera) {
+ this.camera.position.x = 2;
+ this.camera.position.y = 2;
+ this.camera.position.z = 2;
+ this.camera.zoom = 100;
+ this.camera.updateProjectionMatrix();
+ } else {
+ this.camera.position.x = 5;
+ this.camera.position.y = 2;
+ this.camera.position.z = 5;
+ }
+
+ this.scene.add(this.camera);
+ //#endregion
+
+ //#region AmbientLight
+ const ambientLight = new THREE.AmbientLight(0xffffff, 1);
+ this.scene.add(ambientLight);
+ //#endregion
+
+ if (this.graphicsQuality !== 'cheep') {
+ //#region Room light
+ const roomLight = new THREE.SpotLight(0xffffff, 0.1);
+
+ roomLight.position.set(0, 8, 0);
+ roomLight.castShadow = this.enableShadow;
+ roomLight.shadow.bias = -0.0001;
+ roomLight.shadow.mapSize.width = this.shadowQuality;
+ roomLight.shadow.mapSize.height = this.shadowQuality;
+ roomLight.shadow.camera.near = 0.1;
+ roomLight.shadow.camera.far = 9;
+ roomLight.shadow.camera.fov = 45;
+
+ this.scene.add(roomLight);
+ //#endregion
+ }
+
+ //#region Out light
+ const outLight = new THREE.SpotLight(0xffffff, 0.4);
+
+ outLight.position.set(9, 3, -2);
+ outLight.castShadow = this.enableShadow;
+ outLight.shadow.bias = -0.001; // アクネ、アーチファクト対策 その代わりピーターパンが発生する可能性がある
+ outLight.shadow.mapSize.width = this.shadowQuality;
+ outLight.shadow.mapSize.height = this.shadowQuality;
+ outLight.shadow.camera.near = 6;
+ outLight.shadow.camera.far = 15;
+ outLight.shadow.camera.fov = 45;
+
+ this.scene.add(outLight);
+ //#endregion
+
+ //#region Init a controller
+ this.controls = new OrbitControls(this.camera, this.renderer.domElement);
+
+ this.controls.target.set(0, 1, 0);
+ this.controls.enableZoom = true;
+ this.controls.enablePan = isMyRoom;
+ this.controls.minPolarAngle = 0;
+ this.controls.maxPolarAngle = Math.PI / 2;
+ this.controls.minAzimuthAngle = 0;
+ this.controls.maxAzimuthAngle = Math.PI / 2;
+ this.controls.enableDamping = true;
+ this.controls.dampingFactor = 0.2;
+ this.controls.mouseButtons.LEFT = 1;
+ this.controls.mouseButtons.MIDDLE = 2;
+ this.controls.mouseButtons.RIGHT = 0;
+ //#endregion
+
+ //#region POST FXs
+ if (!this.usePostFXs) {
+ this.composer = null;
+ } else {
+ const renderTarget = new THREE.WebGLRenderTarget(width, height, {
+ minFilter: THREE.LinearFilter,
+ magFilter: THREE.LinearFilter,
+ format: THREE.RGBFormat,
+ stencilBuffer: false,
+ });
+
+ const fxaa = new ShaderPass(FXAAShader);
+ fxaa.uniforms['resolution'].value = new THREE.Vector2(1 / width, 1 / height);
+ fxaa.renderToScreen = true;
+
+ this.composer = new EffectComposer(this.renderer, renderTarget);
+ this.composer.addPass(new RenderPass(this.scene, this.camera));
+ if (this.graphicsQuality === 'ultra') {
+ this.composer.addPass(new BloomPass(0.25, 30, 128.0, 512));
+ }
+ this.composer.addPass(fxaa);
+ }
+ //#endregion
+ //#endregion
+
+ //#region Label
+ //#region Avatar
+ const avatarUrl = user.avatarUrl;
+
+ const iconTexture = new THREE.TextureLoader().load(avatarUrl);
+ iconTexture.wrapS = THREE.RepeatWrapping;
+ iconTexture.wrapT = THREE.RepeatWrapping;
+ iconTexture.anisotropy = 16;
+
+ const avatarMaterial = new THREE.MeshLambertMaterial({
+ emissive: 0x111111,
+ map: iconTexture,
+ side: THREE.DoubleSide,
+ alphaTest: 0.5
+ });
+
+ const iconGeometry = new THREE.PlaneGeometry(1, 1);
+
+ const avatarObject = new THREE.Mesh(iconGeometry, avatarMaterial);
+ avatarObject.position.set(-3, 2.5, 2);
+ avatarObject.rotation.y = Math.PI / 2;
+ avatarObject.castShadow = false;
+
+ this.scene.add(avatarObject);
+ //#endregion
+
+ //#region Username
+ const name = user.username;
+
+ new THREE.FontLoader().load('/assets/fonts/helvetiker_regular.typeface.json', font => {
+ const nameGeometry = new THREE.TextGeometry(name, {
+ size: 0.5,
+ height: 0,
+ curveSegments: 8,
+ font: font,
+ bevelThickness: 0,
+ bevelSize: 0,
+ bevelEnabled: false
+ });
+
+ const nameMaterial = new THREE.MeshLambertMaterial({
+ color: 0xffffff
+ });
+
+ const nameObject = new THREE.Mesh(nameGeometry, nameMaterial);
+ nameObject.position.set(-3, 2.25, 1.25);
+ nameObject.rotation.y = Math.PI / 2;
+ nameObject.castShadow = false;
+
+ this.scene.add(nameObject);
+ });
+ //#endregion
+ //#endregion
+
+ //#region Interaction
+ if (isMyRoom) {
+ this.furnitureControl = new TransformControls(this.camera, this.renderer.domElement);
+ this.scene.add(this.furnitureControl);
+
+ // Hover highlight
+ this.renderer.domElement.onmousemove = this.onmousemove;
+
+ // Click
+ this.renderer.domElement.onmousedown = this.onmousedown;
+ }
+ //#endregion
+
+ //#region Init room
+ this.loadRoom();
+ //#endregion
+
+ //#region Load furnitures
+ for (const furniture of this.furnitures) {
+ this.loadFurniture(furniture).then(obj => {
+ this.scene.add(obj.scene);
+ this.objects.push(obj.scene);
+ });
+ }
+ //#endregion
+
+ // Start render
+ if (this.usePostFXs) {
+ this.renderWithPostFXs();
+ } else {
+ this.renderWithoutPostFXs();
+ }
+ }
+
+ @autobind
+ private renderWithoutPostFXs() {
+ requestAnimationFrame(this.renderWithoutPostFXs);
+
+ // Update animations
+ const clock = this.clock.getDelta();
+ for (const mixer of this.mixers) {
+ mixer.update(clock);
+ }
+
+ this.controls.update();
+ this.renderer.render(this.scene, this.camera);
+ }
+
+ @autobind
+ private renderWithPostFXs() {
+ requestAnimationFrame(this.renderWithPostFXs);
+
+ // Update animations
+ const clock = this.clock.getDelta();
+ for (const mixer of this.mixers) {
+ mixer.update(clock);
+ }
+
+ this.controls.update();
+ this.renderer.clear();
+ this.composer.render();
+ }
+
+ @autobind
+ private loadRoom() {
+ new GLTFLoader().load(`/assets/room/rooms/${this.roomInfo.roomType}/${this.roomInfo.roomType}.glb`, gltf => {
+ gltf.scene.traverse(child => {
+ if (!(child instanceof THREE.Mesh)) return;
+
+ child.receiveShadow = this.enableShadow;
+
+ child.material = new THREE.MeshLambertMaterial({
+ color: (child.material as THREE.MeshStandardMaterial).color,
+ map: (child.material as THREE.MeshStandardMaterial).map,
+ name: (child.material as THREE.MeshStandardMaterial).name,
+ });
+
+ // 異方性フィルタリング
+ if ((child.material as THREE.MeshLambertMaterial).map && this.graphicsQuality !== 'cheep') {
+ (child.material as THREE.MeshLambertMaterial).map.minFilter = THREE.LinearMipMapLinearFilter;
+ (child.material as THREE.MeshLambertMaterial).map.magFilter = THREE.LinearMipMapLinearFilter;
+ (child.material as THREE.MeshLambertMaterial).map.anisotropy = 8;
+ }
+ });
+
+ gltf.scene.position.set(0, 0, 0);
+
+ this.scene.add(gltf.scene);
+ this.roomObj = gltf.scene;
+ if (this.roomInfo.roomType === 'default') {
+ this.applyCarpetColor();
+ }
+ });
+ }
+
+ @autobind
+ private loadFurniture(furniture: Furniture) {
+ const def = furnitureDefs.find(d => d.id === furniture.type);
+ return new Promise<GLTF>((res, rej) => {
+ const loader = new GLTFLoader();
+ loader.load(`/assets/room/furnitures/${furniture.type}/${furniture.type}.glb`, gltf => {
+ const model = gltf.scene;
+
+ // Load animation
+ if (gltf.animations.length > 0) {
+ const mixer = new THREE.AnimationMixer(model);
+ this.mixers.push(mixer);
+ for (const clip of gltf.animations) {
+ mixer.clipAction(clip).play();
+ }
+ }
+
+ model.name = furniture.id;
+ model.position.x = furniture.position.x;
+ model.position.y = furniture.position.y;
+ model.position.z = furniture.position.z;
+ model.rotation.x = furniture.rotation.x;
+ model.rotation.y = furniture.rotation.y;
+ model.rotation.z = furniture.rotation.z;
+
+ model.traverse(child => {
+ if (!(child instanceof THREE.Mesh)) return;
+ child.castShadow = this.enableShadow;
+ child.receiveShadow = this.enableShadow;
+ child.material = new THREE.MeshLambertMaterial({
+ color: (child.material as THREE.MeshStandardMaterial).color,
+ map: (child.material as THREE.MeshStandardMaterial).map,
+ name: (child.material as THREE.MeshStandardMaterial).name,
+ });
+
+ // 異方性フィルタリング
+ if ((child.material as THREE.MeshLambertMaterial).map && this.graphicsQuality !== 'cheep') {
+ (child.material as THREE.MeshLambertMaterial).map.minFilter = THREE.LinearMipMapLinearFilter;
+ (child.material as THREE.MeshLambertMaterial).map.magFilter = THREE.LinearMipMapLinearFilter;
+ (child.material as THREE.MeshLambertMaterial).map.anisotropy = 8;
+ }
+ });
+
+ if (def.color) { // カスタムカラー
+ this.applyCustomColor(model);
+ }
+
+ if (def.texture) { // カスタムテクスチャ
+ this.applyCustomTexture(model);
+ }
+
+ res(gltf);
+ }, null, rej);
+ });
+ }
+
+ @autobind
+ private applyCarpetColor() {
+ this.roomObj.traverse(child => {
+ if (!(child instanceof THREE.Mesh)) return;
+ if (child.material && (child.material as THREE.MeshStandardMaterial).name && (child.material as THREE.MeshStandardMaterial).name === 'Carpet') {
+ (child.material as THREE.MeshStandardMaterial).color.setHex(parseInt(this.roomInfo.carpetColor.substr(1), 16));
+ }
+ });
+ }
+
+ @autobind
+ public applyCustomColor(model: THREE.Object3D) {
+ const furniture = this.furnitures.find(furniture => furniture.id === model.name);
+ const def = furnitureDefs.find(d => d.id === furniture.type);
+ if (def.color == null) return;
+ model.traverse(child => {
+ if (!(child instanceof THREE.Mesh)) return;
+ for (const t of Object.keys(def.color)) {
+ if (!child.material || !(child.material as THREE.MeshStandardMaterial).name || (child.material as THREE.MeshStandardMaterial).name !== t) continue;
+
+ const prop = def.color[t];
+ const val = furniture.props ? furniture.props[prop] : undefined;
+
+ if (val == null) continue;
+
+ (child.material as THREE.MeshStandardMaterial).color.setHex(parseInt(val.substr(1), 16));
+ }
+ });
+ }
+
+ @autobind
+ public applyCustomTexture(model: THREE.Object3D) {
+ const furniture = this.furnitures.find(furniture => furniture.id === model.name);
+ const def = furnitureDefs.find(d => d.id === furniture.type);
+ if (def.texture == null) return;
+ model.traverse(child => {
+ if (!(child instanceof THREE.Mesh)) return;
+ for (const t of Object.keys(def.texture)) {
+ if (child.name !== t) continue;
+
+ const prop = def.texture[t].prop;
+ const val = furniture.props ? furniture.props[prop] : undefined;
+
+ if (val == null) continue;
+
+ const canvas = document.createElement('canvas');
+ canvas.height = 1024;
+ canvas.width = 1024;
+
+ child.material = new THREE.MeshLambertMaterial({
+ emissive: 0x111111,
+ side: THREE.DoubleSide,
+ alphaTest: 0.5,
+ });
+
+ const img = new Image();
+ img.onload = () => {
+ const uvInfo = def.texture[t].uv;
+
+ const ctx = canvas.getContext('2d');
+ ctx.drawImage(img, 0, 0, img.width, img.height, uvInfo.x, uvInfo.y, uvInfo.width, uvInfo.height);
+
+ const texture = new THREE.Texture(canvas);
+ texture.wrapS = THREE.RepeatWrapping;
+ texture.wrapT = THREE.RepeatWrapping;
+ texture.anisotropy = 16;
+ texture.flipY = false;
+
+ (child.material as THREE.MeshLambertMaterial).map = texture;
+ (child.material as THREE.MeshLambertMaterial).needsUpdate = true;
+ (child.material as THREE.MeshLambertMaterial).map.needsUpdate = true;
+ };
+ img.src = val;
+ }
+ });
+ }
+
+ @autobind
+ private onmousemove(ev: MouseEvent) {
+ if (this.isTransformMode) return;
+
+ const rect = (ev.target as HTMLElement).getBoundingClientRect();
+ const x = (((ev.clientX * window.devicePixelRatio) - rect.left) / this.renderer.domElement.width) * 2 - 1;
+ const y = -(((ev.clientY * window.devicePixelRatio) - rect.top) / this.renderer.domElement.height) * 2 + 1;
+ const pos = new THREE.Vector2(x, y);
+
+ this.camera.updateMatrixWorld();
+
+ const raycaster = new THREE.Raycaster();
+ raycaster.setFromCamera(pos, this.camera);
+
+ const intersects = raycaster.intersectObjects(this.objects, true);
+
+ for (const object of this.objects) {
+ if (this.isSelectedObject(object)) continue;
+ object.traverse(child => {
+ if (child instanceof THREE.Mesh) {
+ (child.material as THREE.MeshStandardMaterial).emissive.setHex(0x000000);
+ }
+ });
+ }
+
+ if (intersects.length > 0) {
+ const intersected = this.getRoot(intersects[0].object);
+ if (!this.isSelectedObject(intersected)) {
+ intersected.traverse(child => {
+ if (child instanceof THREE.Mesh) {
+ (child.material as THREE.MeshStandardMaterial).emissive.setHex(0x191919);
+ }
+ });
+ }
+ }
+ }
+
+ @autobind
+ private onmousedown(ev: MouseEvent) {
+ if (this.isTransformMode) return;
+ if (ev.target !== this.renderer.domElement || ev.button !== 0) return;
+
+ const rect = (ev.target as HTMLElement).getBoundingClientRect();
+ const x = (((ev.clientX * window.devicePixelRatio) - rect.left) / this.renderer.domElement.width) * 2 - 1;
+ const y = -(((ev.clientY * window.devicePixelRatio) - rect.top) / this.renderer.domElement.height) * 2 + 1;
+ const pos = new THREE.Vector2(x, y);
+
+ this.camera.updateMatrixWorld();
+
+ const raycaster = new THREE.Raycaster();
+ raycaster.setFromCamera(pos, this.camera);
+
+ const intersects = raycaster.intersectObjects(this.objects, true);
+
+ for (const object of this.objects) {
+ object.traverse(child => {
+ if (child instanceof THREE.Mesh) {
+ (child.material as THREE.MeshStandardMaterial).emissive.setHex(0x000000);
+ }
+ });
+ }
+
+ if (intersects.length > 0) {
+ const selectedObj = this.getRoot(intersects[0].object);
+ this.selectFurniture(selectedObj);
+ } else {
+ this.selectedObject = null;
+ this.onChangeSelect(null);
+ }
+ }
+
+ @autobind
+ private getRoot(obj: THREE.Object3D): THREE.Object3D {
+ let found = false;
+ let x = obj.parent;
+ while (!found) {
+ if (x.parent.parent == null) {
+ found = true;
+ } else {
+ x = x.parent;
+ }
+ }
+ return x;
+ }
+
+ @autobind
+ private isSelectedObject(obj: THREE.Object3D): boolean {
+ if (this.selectedObject == null) {
+ return false;
+ } else {
+ return obj.name === this.selectedObject.name;
+ }
+ }
+
+ @autobind
+ private selectFurniture(obj: THREE.Object3D) {
+ this.selectedObject = obj;
+ this.onChangeSelect(obj);
+ obj.traverse(child => {
+ if (child instanceof THREE.Mesh) {
+ (child.material as THREE.MeshStandardMaterial).emissive.setHex(0xff0000);
+ }
+ });
+ }
+
+ @autobind
+ public enterTransformMode(type: 'translate' | 'rotate') {
+ this.isTransformMode = true;
+ this.furnitureControl.setMode(type);
+ this.furnitureControl.attach(this.selectedObject);
+ }
+
+ @autobind
+ public exitTransformMode() {
+ this.isTransformMode = false;
+ this.furnitureControl.detach();
+ }
+
+ @autobind
+ public updateProp(key: string, value: any) {
+ const furniture = this.furnitures.find(furniture => furniture.id === this.selectedObject.name);
+ if (furniture.props == null) furniture.props = {};
+ furniture.props[key] = value;
+ this.applyCustomColor(this.selectedObject);
+ this.applyCustomTexture(this.selectedObject);
+ }
+
+ @autobind
+ public addFurniture(type: string) {
+ const furniture = {
+ id: uuid(),
+ type: type,
+ position: {
+ x: 0,
+ y: 0,
+ z: 0,
+ },
+ rotation: {
+ x: 0,
+ y: 0,
+ z: 0,
+ },
+ };
+
+ this.furnitures.push(furniture);
+
+ this.loadFurniture(furniture).then(obj => {
+ this.scene.add(obj.scene);
+ this.objects.push(obj.scene);
+ });
+ }
+
+ @autobind
+ public removeFurniture() {
+ const obj = this.selectedObject;
+ this.scene.remove(obj);
+ this.objects = this.objects.filter(object => object.name !== obj.name);
+ this.furnitures = this.furnitures.filter(furniture => furniture.id !== obj.name);
+ this.selectedObject = null;
+ this.onChangeSelect(null);
+ }
+
+ @autobind
+ public updateCarpetColor(color: string) {
+ this.roomInfo.carpetColor = color;
+ this.applyCarpetColor();
+ }
+
+ @autobind
+ public changeRoomType(type: string) {
+ this.roomInfo.roomType = type;
+ this.scene.remove(this.roomObj);
+ this.loadRoom();
+ }
+
+ @autobind
+ public getRoomInfo() {
+ for (const obj of this.objects) {
+ const furniture = this.furnitures.find(f => f.id === obj.name);
+ furniture.position.x = obj.position.x;
+ furniture.position.y = obj.position.y;
+ furniture.position.z = obj.position.z;
+ furniture.rotation.x = obj.rotation.x;
+ furniture.rotation.y = obj.rotation.y;
+ furniture.rotation.z = obj.rotation.z;
+ }
+
+ return this.roomInfo;
+ }
+
+ @autobind
+ public getSelectedObject() {
+ return this.selectedObject;
+ }
+
+ @autobind
+ public findFurnitureById(id: string) {
+ return this.furnitures.find(furniture => furniture.id === id);
+ }
+}
diff --git a/src/client/app/common/views/components/settings/settings.vue b/src/client/app/common/views/components/settings/settings.vue
index c609a96722..914d5bf84f 100644
--- a/src/client/app/common/views/components/settings/settings.vue
+++ b/src/client/app/common/views/components/settings/settings.vue
@@ -159,6 +159,19 @@
<template #desc>{{ $t('@._settings.paste-dialog-desc') }}</template>
</ui-switch>
</section>
+
+ <section>
+ <header>{{ $t('@._settings.room') }}</header>
+ <ui-select v-model="roomGraphicsQuality">
+ <template #label>{{ $t('@._settings._room.graphicsQuality') }}</template>
+ <option value="ultra">{{ $t('@._settings._room._graphicsQuality.ultra') }}</option>
+ <option value="high">{{ $t('@._settings._room._graphicsQuality.high') }}</option>
+ <option value="medium">{{ $t('@._settings._room._graphicsQuality.medium') }}</option>
+ <option value="low">{{ $t('@._settings._room._graphicsQuality.low') }}</option>
+ <option value="cheep">{{ $t('@._settings._room._graphicsQuality.cheep') }}</option>
+ </ui-select>
+ <ui-switch v-model="roomUseOrthographicCamera">{{ $t('@._settings._room.useOrthographicCamera') }}</ui-switch>
+ </section>
</ui-card>
<ui-card>
@@ -503,6 +516,16 @@ export default Vue.extend({
set(value) { this.$store.dispatch('settings/set', { key: 'iLikeSushi', value }); }
},
+ roomUseOrthographicCamera: {
+ get() { return this.$store.state.device.roomUseOrthographicCamera; },
+ set(value) { this.$store.commit('device/set', { key: 'roomUseOrthographicCamera', value }); }
+ },
+
+ roomGraphicsQuality: {
+ get() { return this.$store.state.device.roomGraphicsQuality; },
+ set(value) { this.$store.commit('device/set', { key: 'roomGraphicsQuality', value }); }
+ },
+
games_reversi_showBoardLabels: {
get() { return this.$store.state.settings.gamesReversiShowBoardLabels; },
set(value) { this.$store.dispatch('settings/set', { key: 'gamesReversiShowBoardLabels', value }); }
diff --git a/src/client/app/common/views/pages/room/preview.vue b/src/client/app/common/views/pages/room/preview.vue
new file mode 100644
index 0000000000..3f849d8c04
--- /dev/null
+++ b/src/client/app/common/views/pages/room/preview.vue
@@ -0,0 +1,98 @@
+<template>
+<canvas width=224 height=128></canvas>
+</template>
+
+<script lang="ts">
+import Vue from 'vue';
+import * as THREE from 'three';
+
+export default Vue.extend({
+ data() {
+ return {
+ selected: null,
+ objectHeight: 0
+ };
+ },
+
+ mounted() {
+ const canvas = this.$el;
+
+ const width = canvas.width;
+ const height = canvas.height;
+
+ const scene = new THREE.Scene();
+
+ const renderer = new THREE.WebGLRenderer({
+ canvas: canvas,
+ antialias: true,
+ alpha: false
+ });
+ renderer.setPixelRatio(window.devicePixelRatio);
+ renderer.setSize(width, height);
+ renderer.setClearColor(0x000000);
+ renderer.autoClear = false;
+ renderer.shadowMap.enabled = true;
+ renderer.shadowMap.cullFace = THREE.CullFaceBack;
+
+ const camera = new THREE.PerspectiveCamera(75, width / height, 0.1, 100);
+ camera.zoom = 10;
+ camera.position.x = 0;
+ camera.position.y = 2;
+ camera.position.z = 0;
+ camera.updateProjectionMatrix();
+ scene.add(camera);
+
+ const ambientLight = new THREE.AmbientLight(0xffffff, 1);
+ ambientLight.castShadow = false;
+ scene.add(ambientLight);
+
+ const light = new THREE.PointLight(0xffffff, 1, 100);
+ light.position.set(3, 3, 3);
+ scene.add(light);
+
+ const grid = new THREE.GridHelper(5, 16, 0x444444, 0x222222);
+ scene.add(grid);
+
+ const render = () => {
+ const timer = Date.now() * 0.0004;
+ requestAnimationFrame(render);
+
+ camera.position.y = 2 + this.objectHeight / 2;
+ camera.position.z = Math.cos(timer) * 10;
+ camera.position.x = Math.sin(timer) * 10;
+ camera.lookAt(new THREE.Vector3(0, this.objectHeight / 2, 0));
+ renderer.render(scene, camera);
+ };
+
+ this.selected = selected => {
+ const obj = selected.clone();
+
+ // Remove current object
+ const current = scene.getObjectByName('obj');
+ if (current != null) {
+ scene.remove(current);
+ }
+
+ // Add new object
+ obj.name = 'obj';
+ obj.position.x = 0;
+ obj.position.y = 0;
+ obj.position.z = 0;
+ obj.rotation.x = 0;
+ obj.rotation.y = 0;
+ obj.rotation.z = 0;
+ obj.traverse(child => {
+ if (child instanceof THREE.Mesh) {
+ child.material = child.material.clone();
+ return child.material.emissive.setHex(0x000000);
+ }
+ });
+ const objectBoundingBox = new THREE.Box3().setFromObject(obj);
+ this.objectHeight = objectBoundingBox.max.y - objectBoundingBox.min.y;
+ scene.add(obj);
+ };
+
+ render();
+ },
+});
+</script>
diff --git a/src/client/app/common/views/pages/room/room.vue b/src/client/app/common/views/pages/room/room.vue
new file mode 100644
index 0000000000..955e35701e
--- /dev/null
+++ b/src/client/app/common/views/pages/room/room.vue
@@ -0,0 +1,237 @@
+<template>
+<div class="hveuntkp">
+ <div class="controller" v-if="objectSelected">
+ <section>
+ <p class="name">{{ selectedFurnitureName }}</p>
+ <x-preview ref="preview"/>
+ <template v-if="selectedFurnitureInfo.props">
+ <div v-for="k in Object.keys(selectedFurnitureInfo.props)" :key="k">
+ <p>{{ k }}</p>
+ <template v-if="selectedFurnitureInfo.props[k] === 'image'">
+ <ui-button @click="chooseImage(k)">{{ $t('chooseImage') }}</ui-button>
+ </template>
+ <template v-else-if="selectedFurnitureInfo.props[k] === 'color'">
+ <input type="color" :value="selectedFurnitureProps ? selectedFurnitureProps[k] : null" @change="updateColor(k, $event)"/>
+ </template>
+ </div>
+ </template>
+ </section>
+ <section>
+ <ui-button @click="translate()" :primary="isTranslateMode"><fa :icon="faArrowsAlt"/> {{ $t('translate') }}</ui-button>
+ <ui-button @click="rotate()" :primary="isRotateMode"><fa :icon="faUndo"/> {{ $t('rotate') }}</ui-button>
+ <ui-button v-if="isTranslateMode || isRotateMode" @click="exit()"><fa :icon="faBan"/> {{ $t('exit') }}</ui-button>
+ </section>
+ <section>
+ <ui-button @click="remove()"><fa :icon="faTrashAlt"/> {{ $t('remove') }}</ui-button>
+ </section>
+ </div>
+ <div class="menu">
+ <section>
+ <ui-button @click="add()"><fa :icon="faBoxOpen"/> {{ $t('add-furniture') }}</ui-button>
+ </section>
+ <section>
+ <ui-select :value="roomType" @input="updateRoomType($event)">
+ <template #label>{{ $t('room-type') }}</template>
+ <option value="default">{{ $t('rooms.default') }}</option>
+ <option value="washitsu">{{ $t('rooms.washitsu') }}</option>
+ </ui-select>
+ <label v-if="roomType === 'default'">
+ <span>{{ $t('carpet-color') }}</span>
+ <input type="color" :value="carpetColor" @change="updateCarpetColor($event)"/>
+ </label>
+ </section>
+ <section>
+ <ui-button primary @click="save()"><fa :icon="faSave"/> {{ $t('save') }}</ui-button>
+ </section>
+ </div>
+</div>
+</template>
+
+<script lang="ts">
+import Vue from 'vue';
+import i18n from '../../../../i18n';
+import { Room } from '../../../scripts/room/room';
+import parseAcct from '../../../../../../misc/acct/parse';
+import XPreview from './preview.vue';
+const storeItems = require('../../../scripts/room/furnitures.json5');
+import { faBoxOpen, faUndo, faArrowsAlt, faBan } from '@fortawesome/free-solid-svg-icons';
+import { faSave, faTrashAlt } from '@fortawesome/free-regular-svg-icons';
+
+let room: Room;
+
+export default Vue.extend({
+ i18n: i18n('room'),
+
+ components: {
+ XPreview
+ },
+
+ props: {
+ acct: {
+ type: String,
+ required: true
+ },
+ },
+
+ data() {
+ return {
+ objectSelected: false,
+ selectedFurnitureName: null,
+ selectedFurnitureInfo: null,
+ selectedFurnitureProps: null,
+ roomType: null,
+ carpetColor: null,
+ isTranslateMode: false,
+ isRotateMode: false,
+ faBoxOpen, faSave, faTrashAlt, faUndo, faArrowsAlt, faBan,
+ };
+ },
+
+ async mounted() {
+ const user = await this.$root.api('users/show', {
+ ...parseAcct(this.acct)
+ });
+
+ const roomInfo = await this.$root.api('room/show', {
+ userId: user.id
+ });
+
+ this.roomType = roomInfo.roomType;
+ this.carpetColor = roomInfo.carpetColor;
+
+ room = new Room(user, this.$store.getters.isSignedIn && this.$store.state.i.id === user.id, roomInfo, this.$el, {
+ graphicsQuality: this.$store.state.device.roomGraphicsQuality,
+ onChangeSelect: obj => {
+ this.objectSelected = obj != null;
+ if (obj) {
+ const f = room.findFurnitureById(obj.name);
+ this.selectedFurnitureName = this.$t('furnitures.' + f.type);
+ this.selectedFurnitureInfo = storeItems.find(x => x.id === f.type);
+ this.selectedFurnitureProps = f.props
+ ? JSON.parse(JSON.stringify(f.props)) // Disable reactivity
+ : null;
+ this.$nextTick(() => {
+ this.$refs.preview.selected(obj);
+ });
+ }
+ },
+ useOrthographicCamera: this.$store.state.device.roomUseOrthographicCamera
+ });
+ },
+
+ methods: {
+ async add() {
+ const { canceled, result: id } = await this.$root.dialog({
+ type: null,
+ title: this.$t('add-furniture'),
+ select: {
+ items: storeItems.map(item => ({
+ value: item.id, text: this.$t('furnitures.' + item.id)
+ }))
+ },
+ showCancelButton: true
+ });
+ if (canceled) return;
+ room.addFurniture(id);
+ },
+
+ remove() {
+ room.removeFurniture();
+ },
+
+ save() {
+ this.$root.api('room/update', {
+ room: room.getRoomInfo()
+ });
+ },
+
+ chooseImage(key) {
+ this.$chooseDriveFile({
+ multiple: false
+ }).then(file => {
+ room.updateProp(key, file.thumbnailUrl);
+ this.$refs.preview.selected(room.getSelectedObject());
+ });
+ },
+
+ updateColor(key, ev) {
+ room.updateProp(key, ev.target.value);
+ this.$refs.preview.selected(room.getSelectedObject());
+ },
+
+ updateCarpetColor(ev) {
+ room.updateCarpetColor(ev.target.value);
+ this.carpetColor = ev.target.value;
+ },
+
+ updateRoomType(type) {
+ room.changeRoomType(type);
+ this.roomType = type;
+ },
+
+ translate() {
+ if (this.isTranslateMode) {
+ this.exit();
+ } else {
+ this.isRotateMode = false;
+ this.isTranslateMode = true;
+ room.enterTransformMode('translate');
+ }
+ },
+
+ rotate() {
+ if (this.isRotateMode) {
+ this.exit();
+ } else {
+ this.isTranslateMode = false;
+ this.isRotateMode = true;
+ room.enterTransformMode('rotate');
+ }
+ },
+
+ exit() {
+ this.isTranslateMode = false;
+ this.isRotateMode = false;
+ room.exitTransformMode();
+ }
+ }
+});
+</script>
+
+<style lang="stylus" scoped>
+.hveuntkp
+ > .controller
+ > .menu
+ position fixed
+ z-index 1
+ padding 16px
+ background var(--face)
+ color var(--text)
+
+ > section
+ padding 16px 0
+
+ &:first-child
+ padding-top 0
+
+ &:last-child
+ padding-bottom 0
+
+ &:not(:last-child)
+ border-bottom solid 1px var(--faceDivider)
+
+ > .controller
+ top 16px
+ left 16px
+ width 256px
+
+ > section
+ > .name
+ margin 0
+
+ > .menu
+ top 16px
+ right 16px
+ width 256px
+
+</style>
diff --git a/src/client/app/desktop/script.ts b/src/client/app/desktop/script.ts
index 32e4886de3..f77a00ae6b 100644
--- a/src/client/app/desktop/script.ts
+++ b/src/client/app/desktop/script.ts
@@ -187,6 +187,7 @@ init(async (launch, os) => {
{ path: '/i/drive/folder/:folder', component: MkDrive },
{ path: '/i/settings', component: MkSettings },
{ path: '/selectdrive', component: MkSelectDrive },
+ { path: '/@:acct/room', props: true, component: () => import('../common/views/pages/room/room.vue').then(m => m.default) },
{ path: '/share', component: MkShare },
{ path: '/games/reversi/:game?', component: MkReversi },
{ path: '/authorize-follow', component: MkFollow },
diff --git a/src/client/app/desktop/views/components/ui.header.account.vue b/src/client/app/desktop/views/components/ui.header.account.vue
index 5d7ed8c302..690f3a5587 100644
--- a/src/client/app/desktop/views/components/ui.header.account.vue
+++ b/src/client/app/desktop/views/components/ui.header.account.vue
@@ -56,6 +56,13 @@
<i><fa icon="angle-right"/></i>
</router-link>
</li>
+ <li>
+ <router-link :to="`/@${ $store.state.i.username }/room`">
+ <i><fa :icon="faDoorOpen" fixed-width/></i>
+ <span>{{ $t('room') }}</span>
+ <i><fa icon="angle-right"/></i>
+ </router-link>
+ </li>
</ul>
<ul>
<li>
@@ -106,7 +113,7 @@ import i18n from '../../../i18n';
// import MkSettingsWindow from './settings-window.vue';
import MkDriveWindow from './drive-window.vue';
import contains from '../../../common/scripts/contains';
-import { faHome, faColumns, faUsers } from '@fortawesome/free-solid-svg-icons';
+import { faHome, faColumns, faUsers, faDoorOpen } from '@fortawesome/free-solid-svg-icons';
import { faMoon, faSun, faStickyNote } from '@fortawesome/free-regular-svg-icons';
export default Vue.extend({
@@ -114,7 +121,7 @@ export default Vue.extend({
data() {
return {
isOpen: false,
- faHome, faColumns, faMoon, faSun, faStickyNote, faUsers
+ faHome, faColumns, faMoon, faSun, faStickyNote, faUsers, faDoorOpen
};
},
computed: {
diff --git a/src/client/app/store.ts b/src/client/app/store.ts
index d9f1631a82..92fb1568b0 100644
--- a/src/client/app/store.ts
+++ b/src/client/app/store.ts
@@ -76,6 +76,8 @@ const defaultDeviceSettings = {
expandUsersPhotos: true,
expandUsersActivity: true,
enableMobileQuickNotificationView: false,
+ roomGraphicsQuality: 'medium',
+ roomUseOrthographicCamera: true,
};
export default (os: MiOS) => new Vuex.Store({
diff --git a/src/client/app/tsconfig.json b/src/client/app/tsconfig.json
index 8605da75ab..3ec0271f63 100644
--- a/src/client/app/tsconfig.json
+++ b/src/client/app/tsconfig.json
@@ -11,11 +11,17 @@
"sourceMap": false,
"target": "es2017",
"module": "esnext",
+ "moduleResolution": "node",
"removeComments": false,
"noLib": false,
"strict": true,
"strictNullChecks": false,
- "experimentalDecorators": true
+ "experimentalDecorators": true,
+ "resolveJsonModule": true,
+ "typeRoots": [
+ "node_modules/@types",
+ "src/@types"
+ ]
},
"compileOnSave": false,
"include": [
diff --git a/src/client/assets/room/furnitures/bed/bed.blend b/src/client/assets/room/furnitures/bed/bed.blend
new file mode 100644
index 0000000000..731df76d0c
--- /dev/null
+++ b/src/client/assets/room/furnitures/bed/bed.blend
Binary files differ
diff --git a/src/client/assets/room/furnitures/bed/bed.glb b/src/client/assets/room/furnitures/bed/bed.glb
new file mode 100644
index 0000000000..f35ecb9ef4
--- /dev/null
+++ b/src/client/assets/room/furnitures/bed/bed.glb
Binary files differ
diff --git a/src/client/assets/room/furnitures/book/book.blend b/src/client/assets/room/furnitures/book/book.blend
new file mode 100644
index 0000000000..0d4899d4ae
--- /dev/null
+++ b/src/client/assets/room/furnitures/book/book.blend
Binary files differ
diff --git a/src/client/assets/room/furnitures/book/book.glb b/src/client/assets/room/furnitures/book/book.glb
new file mode 100644
index 0000000000..546893da06
--- /dev/null
+++ b/src/client/assets/room/furnitures/book/book.glb
Binary files differ
diff --git a/src/client/assets/room/furnitures/book2/barcode.png b/src/client/assets/room/furnitures/book2/barcode.png
new file mode 100644
index 0000000000..2694854227
--- /dev/null
+++ b/src/client/assets/room/furnitures/book2/barcode.png
Binary files differ
diff --git a/src/client/assets/room/furnitures/book2/book2.blend b/src/client/assets/room/furnitures/book2/book2.blend
new file mode 100644
index 0000000000..e0fdb48101
--- /dev/null
+++ b/src/client/assets/room/furnitures/book2/book2.blend
Binary files differ
diff --git a/src/client/assets/room/furnitures/book2/book2.glb b/src/client/assets/room/furnitures/book2/book2.glb
new file mode 100644
index 0000000000..2b26402f8c
--- /dev/null
+++ b/src/client/assets/room/furnitures/book2/book2.glb
Binary files differ
diff --git a/src/client/assets/room/furnitures/book2/texture.afdesign b/src/client/assets/room/furnitures/book2/texture.afdesign
new file mode 100644
index 0000000000..b63771607a
--- /dev/null
+++ b/src/client/assets/room/furnitures/book2/texture.afdesign
Binary files differ
diff --git a/src/client/assets/room/furnitures/book2/texture.png b/src/client/assets/room/furnitures/book2/texture.png
new file mode 100644
index 0000000000..f34328cb4a
--- /dev/null
+++ b/src/client/assets/room/furnitures/book2/texture.png
Binary files differ
diff --git a/src/client/assets/room/furnitures/book2/uv.png b/src/client/assets/room/furnitures/book2/uv.png
new file mode 100644
index 0000000000..9d282d0b69
--- /dev/null
+++ b/src/client/assets/room/furnitures/book2/uv.png
Binary files differ
diff --git a/src/client/assets/room/furnitures/cardboard-box/cardboard-box.blend b/src/client/assets/room/furnitures/cardboard-box/cardboard-box.blend
new file mode 100644
index 0000000000..3a528de32a
--- /dev/null
+++ b/src/client/assets/room/furnitures/cardboard-box/cardboard-box.blend
Binary files differ
diff --git a/src/client/assets/room/furnitures/cardboard-box/cardboard-box.glb b/src/client/assets/room/furnitures/cardboard-box/cardboard-box.glb
new file mode 100644
index 0000000000..bed372e94f
--- /dev/null
+++ b/src/client/assets/room/furnitures/cardboard-box/cardboard-box.glb
Binary files differ
diff --git a/src/client/assets/room/furnitures/cardboard-box2/cardboard-box2.blend b/src/client/assets/room/furnitures/cardboard-box2/cardboard-box2.blend
new file mode 100644
index 0000000000..5f146267ac
--- /dev/null
+++ b/src/client/assets/room/furnitures/cardboard-box2/cardboard-box2.blend
Binary files differ
diff --git a/src/client/assets/room/furnitures/cardboard-box2/cardboard-box2.glb b/src/client/assets/room/furnitures/cardboard-box2/cardboard-box2.glb
new file mode 100644
index 0000000000..85fcb5c0b6
--- /dev/null
+++ b/src/client/assets/room/furnitures/cardboard-box2/cardboard-box2.glb
Binary files differ
diff --git a/src/client/assets/room/furnitures/cardboard-box2/texture.png b/src/client/assets/room/furnitures/cardboard-box2/texture.png
new file mode 100644
index 0000000000..abc1b3de64
--- /dev/null
+++ b/src/client/assets/room/furnitures/cardboard-box2/texture.png
Binary files differ
diff --git a/src/client/assets/room/furnitures/cardboard-box2/uv.png b/src/client/assets/room/furnitures/cardboard-box2/uv.png
new file mode 100644
index 0000000000..104c141b2c
--- /dev/null
+++ b/src/client/assets/room/furnitures/cardboard-box2/uv.png
Binary files differ
diff --git a/src/client/assets/room/furnitures/cardboard-box3/cardboard-box3.blend b/src/client/assets/room/furnitures/cardboard-box3/cardboard-box3.blend
new file mode 100644
index 0000000000..00681a3cfd
--- /dev/null
+++ b/src/client/assets/room/furnitures/cardboard-box3/cardboard-box3.blend
Binary files differ
diff --git a/src/client/assets/room/furnitures/cardboard-box3/cardboard-box3.glb b/src/client/assets/room/furnitures/cardboard-box3/cardboard-box3.glb
new file mode 100644
index 0000000000..1ef0427689
--- /dev/null
+++ b/src/client/assets/room/furnitures/cardboard-box3/cardboard-box3.glb
Binary files differ
diff --git a/src/client/assets/room/furnitures/cardboard-box3/texture.png b/src/client/assets/room/furnitures/cardboard-box3/texture.png
new file mode 100644
index 0000000000..e68292dce0
--- /dev/null
+++ b/src/client/assets/room/furnitures/cardboard-box3/texture.png
Binary files differ
diff --git a/src/client/assets/room/furnitures/cardboard-box3/texture.xcf b/src/client/assets/room/furnitures/cardboard-box3/texture.xcf
new file mode 100644
index 0000000000..7ffb3e3439
--- /dev/null
+++ b/src/client/assets/room/furnitures/cardboard-box3/texture.xcf
Binary files differ
diff --git a/src/client/assets/room/furnitures/cardboard-box3/uv.png b/src/client/assets/room/furnitures/cardboard-box3/uv.png
new file mode 100644
index 0000000000..3ac23ef584
--- /dev/null
+++ b/src/client/assets/room/furnitures/cardboard-box3/uv.png
Binary files differ
diff --git a/src/client/assets/room/furnitures/carpet-stripe/carpet-stripe.blend b/src/client/assets/room/furnitures/carpet-stripe/carpet-stripe.blend
new file mode 100644
index 0000000000..750343d4f0
--- /dev/null
+++ b/src/client/assets/room/furnitures/carpet-stripe/carpet-stripe.blend
Binary files differ
diff --git a/src/client/assets/room/furnitures/carpet-stripe/carpet-stripe.glb b/src/client/assets/room/furnitures/carpet-stripe/carpet-stripe.glb
new file mode 100644
index 0000000000..3066a69e35
--- /dev/null
+++ b/src/client/assets/room/furnitures/carpet-stripe/carpet-stripe.glb
Binary files differ
diff --git a/src/client/assets/room/furnitures/chair/chair.blend b/src/client/assets/room/furnitures/chair/chair.blend
new file mode 100644
index 0000000000..79c29a8401
--- /dev/null
+++ b/src/client/assets/room/furnitures/chair/chair.blend
Binary files differ
diff --git a/src/client/assets/room/furnitures/chair/chair.glb b/src/client/assets/room/furnitures/chair/chair.glb
new file mode 100644
index 0000000000..08ee1a0bb0
--- /dev/null
+++ b/src/client/assets/room/furnitures/chair/chair.glb
Binary files differ
diff --git a/src/client/assets/room/furnitures/chair2/chair2.blend b/src/client/assets/room/furnitures/chair2/chair2.blend
new file mode 100644
index 0000000000..c6a1acd96f
--- /dev/null
+++ b/src/client/assets/room/furnitures/chair2/chair2.blend
Binary files differ
diff --git a/src/client/assets/room/furnitures/chair2/chair2.glb b/src/client/assets/room/furnitures/chair2/chair2.glb
new file mode 100644
index 0000000000..5ea2f3518b
--- /dev/null
+++ b/src/client/assets/room/furnitures/chair2/chair2.glb
Binary files differ
diff --git a/src/client/assets/room/furnitures/color-box/color-box.blend b/src/client/assets/room/furnitures/color-box/color-box.blend
new file mode 100644
index 0000000000..f96a4ff766
--- /dev/null
+++ b/src/client/assets/room/furnitures/color-box/color-box.blend
Binary files differ
diff --git a/src/client/assets/room/furnitures/color-box/color-box.glb b/src/client/assets/room/furnitures/color-box/color-box.glb
new file mode 100644
index 0000000000..43f2abcae8
--- /dev/null
+++ b/src/client/assets/room/furnitures/color-box/color-box.glb
Binary files differ
diff --git a/src/client/assets/room/furnitures/corkboard/corkboard.blend b/src/client/assets/room/furnitures/corkboard/corkboard.blend
new file mode 100644
index 0000000000..9a7e1878cd
--- /dev/null
+++ b/src/client/assets/room/furnitures/corkboard/corkboard.blend
Binary files differ
diff --git a/src/client/assets/room/furnitures/corkboard/corkboard.glb b/src/client/assets/room/furnitures/corkboard/corkboard.glb
new file mode 100644
index 0000000000..fee108fb91
--- /dev/null
+++ b/src/client/assets/room/furnitures/corkboard/corkboard.glb
Binary files differ
diff --git a/src/client/assets/room/furnitures/cube/cube.blend b/src/client/assets/room/furnitures/cube/cube.blend
new file mode 100644
index 0000000000..1af5bf40a9
--- /dev/null
+++ b/src/client/assets/room/furnitures/cube/cube.blend
Binary files differ
diff --git a/src/client/assets/room/furnitures/cube/cube.glb b/src/client/assets/room/furnitures/cube/cube.glb
new file mode 100644
index 0000000000..4ac8b6036d
--- /dev/null
+++ b/src/client/assets/room/furnitures/cube/cube.glb
Binary files differ
diff --git a/src/client/assets/room/furnitures/desk/desk.blend b/src/client/assets/room/furnitures/desk/desk.blend
new file mode 100644
index 0000000000..c88d01f0b2
--- /dev/null
+++ b/src/client/assets/room/furnitures/desk/desk.blend
Binary files differ
diff --git a/src/client/assets/room/furnitures/desk/desk.glb b/src/client/assets/room/furnitures/desk/desk.glb
new file mode 100644
index 0000000000..4a58513095
--- /dev/null
+++ b/src/client/assets/room/furnitures/desk/desk.glb
Binary files differ
diff --git a/src/client/assets/room/furnitures/eraser/cover.png b/src/client/assets/room/furnitures/eraser/cover.png
new file mode 100644
index 0000000000..e64bfde11a
--- /dev/null
+++ b/src/client/assets/room/furnitures/eraser/cover.png
Binary files differ
diff --git a/src/client/assets/room/furnitures/eraser/cover.psd b/src/client/assets/room/furnitures/eraser/cover.psd
new file mode 100644
index 0000000000..c393337833
--- /dev/null
+++ b/src/client/assets/room/furnitures/eraser/cover.psd
Binary files differ
diff --git a/src/client/assets/room/furnitures/eraser/eraser-uv.png b/src/client/assets/room/furnitures/eraser/eraser-uv.png
new file mode 100644
index 0000000000..2124114e70
--- /dev/null
+++ b/src/client/assets/room/furnitures/eraser/eraser-uv.png
Binary files differ
diff --git a/src/client/assets/room/furnitures/eraser/eraser.blend b/src/client/assets/room/furnitures/eraser/eraser.blend
new file mode 100644
index 0000000000..103c54fbae
--- /dev/null
+++ b/src/client/assets/room/furnitures/eraser/eraser.blend
Binary files differ
diff --git a/src/client/assets/room/furnitures/eraser/eraser.glb b/src/client/assets/room/furnitures/eraser/eraser.glb
new file mode 100644
index 0000000000..016b60df20
--- /dev/null
+++ b/src/client/assets/room/furnitures/eraser/eraser.glb
Binary files differ
diff --git a/src/client/assets/room/furnitures/facial-tissue/facial-tissue-uv.png b/src/client/assets/room/furnitures/facial-tissue/facial-tissue-uv.png
new file mode 100644
index 0000000000..b6dfa8f308
--- /dev/null
+++ b/src/client/assets/room/furnitures/facial-tissue/facial-tissue-uv.png
Binary files differ
diff --git a/src/client/assets/room/furnitures/facial-tissue/facial-tissue.blend b/src/client/assets/room/furnitures/facial-tissue/facial-tissue.blend
new file mode 100644
index 0000000000..d59f87c1ee
--- /dev/null
+++ b/src/client/assets/room/furnitures/facial-tissue/facial-tissue.blend
Binary files differ
diff --git a/src/client/assets/room/furnitures/facial-tissue/facial-tissue.glb b/src/client/assets/room/furnitures/facial-tissue/facial-tissue.glb
new file mode 100644
index 0000000000..48b36ef347
--- /dev/null
+++ b/src/client/assets/room/furnitures/facial-tissue/facial-tissue.glb
Binary files differ
diff --git a/src/client/assets/room/furnitures/facial-tissue/facial-tissue.png b/src/client/assets/room/furnitures/facial-tissue/facial-tissue.png
new file mode 100644
index 0000000000..f25c882027
--- /dev/null
+++ b/src/client/assets/room/furnitures/facial-tissue/facial-tissue.png
Binary files differ
diff --git a/src/client/assets/room/furnitures/facial-tissue/facial-tissue.psd b/src/client/assets/room/furnitures/facial-tissue/facial-tissue.psd
new file mode 100644
index 0000000000..cd59fc007b
--- /dev/null
+++ b/src/client/assets/room/furnitures/facial-tissue/facial-tissue.psd
Binary files differ
diff --git a/src/client/assets/room/furnitures/fan/fan.blend b/src/client/assets/room/furnitures/fan/fan.blend
new file mode 100644
index 0000000000..8c8106e5fe
--- /dev/null
+++ b/src/client/assets/room/furnitures/fan/fan.blend
Binary files differ
diff --git a/src/client/assets/room/furnitures/fan/fan.glb b/src/client/assets/room/furnitures/fan/fan.glb
new file mode 100644
index 0000000000..d9367f3534
--- /dev/null
+++ b/src/client/assets/room/furnitures/fan/fan.glb
Binary files differ
diff --git a/src/client/assets/room/furnitures/keyboard/keyboard.blend b/src/client/assets/room/furnitures/keyboard/keyboard.blend
new file mode 100644
index 0000000000..ab33d134b3
--- /dev/null
+++ b/src/client/assets/room/furnitures/keyboard/keyboard.blend
Binary files differ
diff --git a/src/client/assets/room/furnitures/keyboard/keyboard.glb b/src/client/assets/room/furnitures/keyboard/keyboard.glb
new file mode 100644
index 0000000000..15dc69f47a
--- /dev/null
+++ b/src/client/assets/room/furnitures/keyboard/keyboard.glb
Binary files differ
diff --git a/src/client/assets/room/furnitures/low-table/low-table.blend b/src/client/assets/room/furnitures/low-table/low-table.blend
new file mode 100644
index 0000000000..e1592174d9
--- /dev/null
+++ b/src/client/assets/room/furnitures/low-table/low-table.blend
Binary files differ
diff --git a/src/client/assets/room/furnitures/low-table/low-table.glb b/src/client/assets/room/furnitures/low-table/low-table.glb
new file mode 100644
index 0000000000..c69bf35d7b
--- /dev/null
+++ b/src/client/assets/room/furnitures/low-table/low-table.glb
Binary files differ
diff --git a/src/client/assets/room/furnitures/mat/mat.blend b/src/client/assets/room/furnitures/mat/mat.blend
new file mode 100644
index 0000000000..a1e1a68c55
--- /dev/null
+++ b/src/client/assets/room/furnitures/mat/mat.blend
Binary files differ
diff --git a/src/client/assets/room/furnitures/mat/mat.glb b/src/client/assets/room/furnitures/mat/mat.glb
new file mode 100644
index 0000000000..87ccd44e1a
--- /dev/null
+++ b/src/client/assets/room/furnitures/mat/mat.glb
Binary files differ
diff --git a/src/client/assets/room/furnitures/milk/milk-uv.png b/src/client/assets/room/furnitures/milk/milk-uv.png
new file mode 100644
index 0000000000..70060c9190
--- /dev/null
+++ b/src/client/assets/room/furnitures/milk/milk-uv.png
Binary files differ
diff --git a/src/client/assets/room/furnitures/milk/milk.blend b/src/client/assets/room/furnitures/milk/milk.blend
new file mode 100644
index 0000000000..2df508d5b9
--- /dev/null
+++ b/src/client/assets/room/furnitures/milk/milk.blend
Binary files differ
diff --git a/src/client/assets/room/furnitures/milk/milk.glb b/src/client/assets/room/furnitures/milk/milk.glb
new file mode 100644
index 0000000000..b335fe3d02
--- /dev/null
+++ b/src/client/assets/room/furnitures/milk/milk.glb
Binary files differ
diff --git a/src/client/assets/room/furnitures/milk/milk.png b/src/client/assets/room/furnitures/milk/milk.png
new file mode 100644
index 0000000000..2ce010ce78
--- /dev/null
+++ b/src/client/assets/room/furnitures/milk/milk.png
Binary files differ
diff --git a/src/client/assets/room/furnitures/milk/milk.psd b/src/client/assets/room/furnitures/milk/milk.psd
new file mode 100644
index 0000000000..f31e439277
--- /dev/null
+++ b/src/client/assets/room/furnitures/milk/milk.psd
Binary files differ
diff --git a/src/client/assets/room/furnitures/monitor/monitor.blend b/src/client/assets/room/furnitures/monitor/monitor.blend
new file mode 100644
index 0000000000..bf37cd5da2
--- /dev/null
+++ b/src/client/assets/room/furnitures/monitor/monitor.blend
Binary files differ
diff --git a/src/client/assets/room/furnitures/monitor/monitor.glb b/src/client/assets/room/furnitures/monitor/monitor.glb
new file mode 100644
index 0000000000..efe42bd71c
--- /dev/null
+++ b/src/client/assets/room/furnitures/monitor/monitor.glb
Binary files differ
diff --git a/src/client/assets/room/furnitures/monitor/monitor.psd b/src/client/assets/room/furnitures/monitor/monitor.psd
new file mode 100644
index 0000000000..57afff9cd9
--- /dev/null
+++ b/src/client/assets/room/furnitures/monitor/monitor.psd
Binary files differ
diff --git a/src/client/assets/room/furnitures/monitor/screen-uv.png b/src/client/assets/room/furnitures/monitor/screen-uv.png
new file mode 100644
index 0000000000..5599087364
--- /dev/null
+++ b/src/client/assets/room/furnitures/monitor/screen-uv.png
Binary files differ
diff --git a/src/client/assets/room/furnitures/monitor/screen.jpg b/src/client/assets/room/furnitures/monitor/screen.jpg
new file mode 100644
index 0000000000..6f03a9ee90
--- /dev/null
+++ b/src/client/assets/room/furnitures/monitor/screen.jpg
Binary files differ
diff --git a/src/client/assets/room/furnitures/moon/moon.blend b/src/client/assets/room/furnitures/moon/moon.blend
new file mode 100644
index 0000000000..4ff3deab8e
--- /dev/null
+++ b/src/client/assets/room/furnitures/moon/moon.blend
Binary files differ
diff --git a/src/client/assets/room/furnitures/moon/moon.glb b/src/client/assets/room/furnitures/moon/moon.glb
new file mode 100644
index 0000000000..07fa7e4c02
--- /dev/null
+++ b/src/client/assets/room/furnitures/moon/moon.glb
Binary files differ
diff --git a/src/client/assets/room/furnitures/moon/moon.jpg b/src/client/assets/room/furnitures/moon/moon.jpg
new file mode 100644
index 0000000000..1b0798755c
--- /dev/null
+++ b/src/client/assets/room/furnitures/moon/moon.jpg
Binary files differ
diff --git a/src/client/assets/room/furnitures/mousepad/mousepad.blend b/src/client/assets/room/furnitures/mousepad/mousepad.blend
new file mode 100644
index 0000000000..14bd139c94
--- /dev/null
+++ b/src/client/assets/room/furnitures/mousepad/mousepad.blend
Binary files differ
diff --git a/src/client/assets/room/furnitures/mousepad/mousepad.glb b/src/client/assets/room/furnitures/mousepad/mousepad.glb
new file mode 100644
index 0000000000..681ada49cd
--- /dev/null
+++ b/src/client/assets/room/furnitures/mousepad/mousepad.glb
Binary files differ
diff --git a/src/client/assets/room/furnitures/pc/motherboard-uv.png b/src/client/assets/room/furnitures/pc/motherboard-uv.png
new file mode 100644
index 0000000000..490ae86810
--- /dev/null
+++ b/src/client/assets/room/furnitures/pc/motherboard-uv.png
Binary files differ
diff --git a/src/client/assets/room/furnitures/pc/motherboard-uv.psd b/src/client/assets/room/furnitures/pc/motherboard-uv.psd
new file mode 100644
index 0000000000..971f33f79e
--- /dev/null
+++ b/src/client/assets/room/furnitures/pc/motherboard-uv.psd
Binary files differ
diff --git a/src/client/assets/room/furnitures/pc/motherboard.jpg b/src/client/assets/room/furnitures/pc/motherboard.jpg
new file mode 100644
index 0000000000..415f8d0b1d
--- /dev/null
+++ b/src/client/assets/room/furnitures/pc/motherboard.jpg
Binary files differ
diff --git a/src/client/assets/room/furnitures/pc/pc.blend b/src/client/assets/room/furnitures/pc/pc.blend
new file mode 100644
index 0000000000..13dfec6ccc
--- /dev/null
+++ b/src/client/assets/room/furnitures/pc/pc.blend
Binary files differ
diff --git a/src/client/assets/room/furnitures/pc/pc.glb b/src/client/assets/room/furnitures/pc/pc.glb
new file mode 100644
index 0000000000..44a48b18ae
--- /dev/null
+++ b/src/client/assets/room/furnitures/pc/pc.glb
Binary files differ
diff --git a/src/client/assets/room/furnitures/pencil/pencil.blend b/src/client/assets/room/furnitures/pencil/pencil.blend
new file mode 100644
index 0000000000..0fc6bdd776
--- /dev/null
+++ b/src/client/assets/room/furnitures/pencil/pencil.blend
Binary files differ
diff --git a/src/client/assets/room/furnitures/pencil/pencil.glb b/src/client/assets/room/furnitures/pencil/pencil.glb
new file mode 100644
index 0000000000..a938b5cdcc
--- /dev/null
+++ b/src/client/assets/room/furnitures/pencil/pencil.glb
Binary files differ
diff --git a/src/client/assets/room/furnitures/photoframe/photo-uv.png b/src/client/assets/room/furnitures/photoframe/photo-uv.png
new file mode 100644
index 0000000000..befa32c367
--- /dev/null
+++ b/src/client/assets/room/furnitures/photoframe/photo-uv.png
Binary files differ
diff --git a/src/client/assets/room/furnitures/photoframe/photo.jpg b/src/client/assets/room/furnitures/photoframe/photo.jpg
new file mode 100644
index 0000000000..9597571fef
--- /dev/null
+++ b/src/client/assets/room/furnitures/photoframe/photo.jpg
Binary files differ
diff --git a/src/client/assets/room/furnitures/photoframe/photoframe.blend b/src/client/assets/room/furnitures/photoframe/photoframe.blend
new file mode 100644
index 0000000000..4224cde45b
--- /dev/null
+++ b/src/client/assets/room/furnitures/photoframe/photoframe.blend
Binary files differ
diff --git a/src/client/assets/room/furnitures/photoframe/photoframe.glb b/src/client/assets/room/furnitures/photoframe/photoframe.glb
new file mode 100644
index 0000000000..4255a77de6
--- /dev/null
+++ b/src/client/assets/room/furnitures/photoframe/photoframe.glb
Binary files differ
diff --git a/src/client/assets/room/furnitures/piano/piano.blend b/src/client/assets/room/furnitures/piano/piano.blend
new file mode 100644
index 0000000000..7653cdf672
--- /dev/null
+++ b/src/client/assets/room/furnitures/piano/piano.blend
Binary files differ
diff --git a/src/client/assets/room/furnitures/piano/piano.glb b/src/client/assets/room/furnitures/piano/piano.glb
new file mode 100644
index 0000000000..7242e78ceb
--- /dev/null
+++ b/src/client/assets/room/furnitures/piano/piano.glb
Binary files differ
diff --git a/src/client/assets/room/furnitures/pinguin/pinguin.glb b/src/client/assets/room/furnitures/pinguin/pinguin.glb
new file mode 100644
index 0000000000..0ddf611ad9
--- /dev/null
+++ b/src/client/assets/room/furnitures/pinguin/pinguin.glb
Binary files differ
diff --git a/src/client/assets/room/furnitures/plant/plant-soil-uv.png b/src/client/assets/room/furnitures/plant/plant-soil-uv.png
new file mode 100644
index 0000000000..ca690fdeb8
--- /dev/null
+++ b/src/client/assets/room/furnitures/plant/plant-soil-uv.png
Binary files differ
diff --git a/src/client/assets/room/furnitures/plant/plant-soil.png b/src/client/assets/room/furnitures/plant/plant-soil.png
new file mode 100644
index 0000000000..dc26ba062e
--- /dev/null
+++ b/src/client/assets/room/furnitures/plant/plant-soil.png
Binary files differ
diff --git a/src/client/assets/room/furnitures/plant/plant-soil.psd b/src/client/assets/room/furnitures/plant/plant-soil.psd
new file mode 100644
index 0000000000..1457b7ea5b
--- /dev/null
+++ b/src/client/assets/room/furnitures/plant/plant-soil.psd
Binary files differ
diff --git a/src/client/assets/room/furnitures/plant/plant.blend b/src/client/assets/room/furnitures/plant/plant.blend
new file mode 100644
index 0000000000..aa38c7b54e
--- /dev/null
+++ b/src/client/assets/room/furnitures/plant/plant.blend
Binary files differ
diff --git a/src/client/assets/room/furnitures/plant/plant.glb b/src/client/assets/room/furnitures/plant/plant.glb
new file mode 100644
index 0000000000..38422b4a9b
--- /dev/null
+++ b/src/client/assets/room/furnitures/plant/plant.glb
Binary files differ
diff --git a/src/client/assets/room/furnitures/plant2/plant2.blend b/src/client/assets/room/furnitures/plant2/plant2.blend
new file mode 100644
index 0000000000..6592c5d98d
--- /dev/null
+++ b/src/client/assets/room/furnitures/plant2/plant2.blend
Binary files differ
diff --git a/src/client/assets/room/furnitures/plant2/plant2.glb b/src/client/assets/room/furnitures/plant2/plant2.glb
new file mode 100644
index 0000000000..223e6f5834
--- /dev/null
+++ b/src/client/assets/room/furnitures/plant2/plant2.glb
Binary files differ
diff --git a/src/client/assets/room/furnitures/plant2/soil.png b/src/client/assets/room/furnitures/plant2/soil.png
new file mode 100644
index 0000000000..dc26ba062e
--- /dev/null
+++ b/src/client/assets/room/furnitures/plant2/soil.png
Binary files differ
diff --git a/src/client/assets/room/furnitures/poster-h/poster-h.blend b/src/client/assets/room/furnitures/poster-h/poster-h.blend
new file mode 100644
index 0000000000..40f944f3c1
--- /dev/null
+++ b/src/client/assets/room/furnitures/poster-h/poster-h.blend
Binary files differ
diff --git a/src/client/assets/room/furnitures/poster-h/poster-h.glb b/src/client/assets/room/furnitures/poster-h/poster-h.glb
new file mode 100644
index 0000000000..c6032c1009
--- /dev/null
+++ b/src/client/assets/room/furnitures/poster-h/poster-h.glb
Binary files differ
diff --git a/src/client/assets/room/furnitures/poster-h/uv.png b/src/client/assets/room/furnitures/poster-h/uv.png
new file mode 100644
index 0000000000..700e6b1cf0
--- /dev/null
+++ b/src/client/assets/room/furnitures/poster-h/uv.png
Binary files differ
diff --git a/src/client/assets/room/furnitures/poster-v/poster-v.blend b/src/client/assets/room/furnitures/poster-v/poster-v.blend
new file mode 100644
index 0000000000..07fe971634
--- /dev/null
+++ b/src/client/assets/room/furnitures/poster-v/poster-v.blend
Binary files differ
diff --git a/src/client/assets/room/furnitures/poster-v/poster-v.glb b/src/client/assets/room/furnitures/poster-v/poster-v.glb
new file mode 100644
index 0000000000..6e3782f193
--- /dev/null
+++ b/src/client/assets/room/furnitures/poster-v/poster-v.glb
Binary files differ
diff --git a/src/client/assets/room/furnitures/poster-v/uv.png b/src/client/assets/room/furnitures/poster-v/uv.png
new file mode 100644
index 0000000000..225b0061a2
--- /dev/null
+++ b/src/client/assets/room/furnitures/poster-v/uv.png
Binary files differ
diff --git a/src/client/assets/room/furnitures/pudding/pudding.blend b/src/client/assets/room/furnitures/pudding/pudding.blend
new file mode 100644
index 0000000000..bba40ce161
--- /dev/null
+++ b/src/client/assets/room/furnitures/pudding/pudding.blend
Binary files differ
diff --git a/src/client/assets/room/furnitures/pudding/pudding.glb b/src/client/assets/room/furnitures/pudding/pudding.glb
new file mode 100644
index 0000000000..06c9ed80cc
--- /dev/null
+++ b/src/client/assets/room/furnitures/pudding/pudding.glb
Binary files differ
diff --git a/src/client/assets/room/furnitures/rubik-cube/rubik-cube.blend b/src/client/assets/room/furnitures/rubik-cube/rubik-cube.blend
new file mode 100644
index 0000000000..6c09067e78
--- /dev/null
+++ b/src/client/assets/room/furnitures/rubik-cube/rubik-cube.blend
Binary files differ
diff --git a/src/client/assets/room/furnitures/rubik-cube/rubik-cube.glb b/src/client/assets/room/furnitures/rubik-cube/rubik-cube.glb
new file mode 100644
index 0000000000..d640df9b06
--- /dev/null
+++ b/src/client/assets/room/furnitures/rubik-cube/rubik-cube.glb
Binary files differ
diff --git a/src/client/assets/room/furnitures/server/rack-uv.png b/src/client/assets/room/furnitures/server/rack-uv.png
new file mode 100644
index 0000000000..c317bd0b1a
--- /dev/null
+++ b/src/client/assets/room/furnitures/server/rack-uv.png
Binary files differ
diff --git a/src/client/assets/room/furnitures/server/rack.png b/src/client/assets/room/furnitures/server/rack.png
new file mode 100644
index 0000000000..2c8231f878
--- /dev/null
+++ b/src/client/assets/room/furnitures/server/rack.png
Binary files differ
diff --git a/src/client/assets/room/furnitures/server/server.blend b/src/client/assets/room/furnitures/server/server.blend
new file mode 100644
index 0000000000..6675dfbdc2
--- /dev/null
+++ b/src/client/assets/room/furnitures/server/server.blend
Binary files differ
diff --git a/src/client/assets/room/furnitures/server/server.glb b/src/client/assets/room/furnitures/server/server.glb
new file mode 100644
index 0000000000..a8b530a2d2
--- /dev/null
+++ b/src/client/assets/room/furnitures/server/server.glb
Binary files differ
diff --git a/src/client/assets/room/furnitures/server/server.png b/src/client/assets/room/furnitures/server/server.png
new file mode 100644
index 0000000000..ac62a8b165
--- /dev/null
+++ b/src/client/assets/room/furnitures/server/server.png
Binary files differ
diff --git a/src/client/assets/room/furnitures/server/uv.png b/src/client/assets/room/furnitures/server/uv.png
new file mode 100644
index 0000000000..6c7e495014
--- /dev/null
+++ b/src/client/assets/room/furnitures/server/uv.png
Binary files differ
diff --git a/src/client/assets/room/furnitures/tv/screen-uv.png b/src/client/assets/room/furnitures/tv/screen-uv.png
new file mode 100644
index 0000000000..c02e01c2e5
--- /dev/null
+++ b/src/client/assets/room/furnitures/tv/screen-uv.png
Binary files differ
diff --git a/src/client/assets/room/furnitures/tv/tv.blend b/src/client/assets/room/furnitures/tv/tv.blend
new file mode 100644
index 0000000000..490e298e7b
--- /dev/null
+++ b/src/client/assets/room/furnitures/tv/tv.blend
Binary files differ
diff --git a/src/client/assets/room/furnitures/tv/tv.glb b/src/client/assets/room/furnitures/tv/tv.glb
new file mode 100644
index 0000000000..b9bd23896b
--- /dev/null
+++ b/src/client/assets/room/furnitures/tv/tv.glb
Binary files differ
diff --git a/src/client/assets/room/furnitures/wall-clock/wall-clock.blend b/src/client/assets/room/furnitures/wall-clock/wall-clock.blend
new file mode 100644
index 0000000000..0a61c8f01e
--- /dev/null
+++ b/src/client/assets/room/furnitures/wall-clock/wall-clock.blend
Binary files differ
diff --git a/src/client/assets/room/furnitures/wall-clock/wall-clock.glb b/src/client/assets/room/furnitures/wall-clock/wall-clock.glb
new file mode 100644
index 0000000000..b9f0093a8d
--- /dev/null
+++ b/src/client/assets/room/furnitures/wall-clock/wall-clock.glb
Binary files differ
diff --git a/src/client/assets/room/rooms/default/default.blend b/src/client/assets/room/rooms/default/default.blend
new file mode 100644
index 0000000000..661154724a
--- /dev/null
+++ b/src/client/assets/room/rooms/default/default.blend
Binary files differ
diff --git a/src/client/assets/room/rooms/default/default.glb b/src/client/assets/room/rooms/default/default.glb
new file mode 100644
index 0000000000..3d378deee2
--- /dev/null
+++ b/src/client/assets/room/rooms/default/default.glb
Binary files differ
diff --git a/src/client/assets/room/rooms/washitsu/husuma-uv.png b/src/client/assets/room/rooms/washitsu/husuma-uv.png
new file mode 100644
index 0000000000..3d0b08f488
--- /dev/null
+++ b/src/client/assets/room/rooms/washitsu/husuma-uv.png
Binary files differ
diff --git a/src/client/assets/room/rooms/washitsu/husuma.png b/src/client/assets/room/rooms/washitsu/husuma.png
new file mode 100644
index 0000000000..fd1f5ef231
--- /dev/null
+++ b/src/client/assets/room/rooms/washitsu/husuma.png
Binary files differ
diff --git a/src/client/assets/room/rooms/washitsu/tatami-single1600.png b/src/client/assets/room/rooms/washitsu/tatami-single1600.png
new file mode 100644
index 0000000000..49fd2d6476
--- /dev/null
+++ b/src/client/assets/room/rooms/washitsu/tatami-single1600.png
Binary files differ
diff --git a/src/client/assets/room/rooms/washitsu/tatami-uv.png b/src/client/assets/room/rooms/washitsu/tatami-uv.png
new file mode 100644
index 0000000000..b4bba33850
--- /dev/null
+++ b/src/client/assets/room/rooms/washitsu/tatami-uv.png
Binary files differ
diff --git a/src/client/assets/room/rooms/washitsu/tatami.afdesign b/src/client/assets/room/rooms/washitsu/tatami.afdesign
new file mode 100644
index 0000000000..9300a26950
--- /dev/null
+++ b/src/client/assets/room/rooms/washitsu/tatami.afdesign
Binary files differ
diff --git a/src/client/assets/room/rooms/washitsu/tatami.png b/src/client/assets/room/rooms/washitsu/tatami.png
new file mode 100644
index 0000000000..2137d4eca7
--- /dev/null
+++ b/src/client/assets/room/rooms/washitsu/tatami.png
Binary files differ
diff --git a/src/client/assets/room/rooms/washitsu/washitsu.blend b/src/client/assets/room/rooms/washitsu/washitsu.blend
new file mode 100644
index 0000000000..84dc11374d
--- /dev/null
+++ b/src/client/assets/room/rooms/washitsu/washitsu.blend
Binary files differ
diff --git a/src/client/assets/room/rooms/washitsu/washitsu.glb b/src/client/assets/room/rooms/washitsu/washitsu.glb
new file mode 100644
index 0000000000..5b4767bc73
--- /dev/null
+++ b/src/client/assets/room/rooms/washitsu/washitsu.glb
Binary files differ
diff --git a/src/models/entities/user-profile.ts b/src/models/entities/user-profile.ts
index 61e80049c3..1244fa4390 100644
--- a/src/models/entities/user-profile.ts
+++ b/src/models/entities/user-profile.ts
@@ -99,6 +99,12 @@ export class UserProfile {
})
public clientData: Record<string, any>;
+ @Column('jsonb', {
+ default: {},
+ comment: 'The room data of the User.'
+ })
+ public room: Record<string, any>;
+
@Column('boolean', {
default: false,
})
diff --git a/src/server/api/endpoints/room/show.ts b/src/server/api/endpoints/room/show.ts
new file mode 100644
index 0000000000..17634834ac
--- /dev/null
+++ b/src/server/api/endpoints/room/show.ts
@@ -0,0 +1,92 @@
+import $ from 'cafy';
+import define from '../../define';
+import { ApiError } from '../../error';
+import { Users, UserProfiles } from '../../../../models';
+import { ID } from '../../../../misc/cafy-id';
+import { ensure } from '../../../../prelude/ensure';
+import { toPunyNullable } from '../../../../misc/convert-host';
+
+export const meta = {
+ desc: {
+ 'ja-JP': '指定した部屋の情報を取得します。',
+ },
+
+ tags: ['room'],
+
+ requireCredential: false,
+
+ params: {
+ userId: {
+ validator: $.optional.type(ID),
+ desc: {
+ 'ja-JP': '対象のユーザーのID',
+ 'en-US': 'Target user ID'
+ }
+ },
+
+ username: {
+ validator: $.optional.str
+ },
+
+ host: {
+ validator: $.optional.nullable.str
+ },
+ },
+
+ errors: {
+ noSuchUser: {
+ message: 'No such user.',
+ code: 'NO_SUCH_USER',
+ id: '7ad3fa3e-5e12-42f0-b23a-f3d13f10ee4b'
+ }
+ }
+};
+
+export default define(meta, async (ps, me) => {
+ const user = await Users.findOne(ps.userId != null
+ ? { id: ps.userId }
+ : { usernameLower: ps.username!.toLowerCase(), host: toPunyNullable(ps.host) });
+
+ if (user == null) {
+ throw new ApiError(meta.errors.noSuchUser);
+ }
+
+ const profile = await UserProfiles.findOne(user.id).then(ensure);
+
+ if (profile.room.furnitures == null) {
+ await UserProfiles.update({ userId: user.id }, {
+ room: {
+ furnitures: [],
+ ...profile.room
+ }
+ });
+
+ profile.room.furnitures = [];
+ }
+
+ if (profile.room.roomType == null) {
+ const initialType = 'default';
+ await UserProfiles.update({ userId: user.id }, {
+ room: {
+ roomType: initialType as any,
+ ...profile.room
+ }
+ });
+
+ profile.room.roomType = initialType;
+ }
+
+ if (profile.room.carpetColor == null) {
+ const initialColor = '#85CAF0';
+ await UserProfiles.update({ userId: user.id }, {
+ room: {
+ carpetColor: initialColor as any,
+ ...profile.room
+ }
+ });
+
+ profile.room.carpetColor = initialColor;
+ }
+
+ return profile.room;
+});
diff --git a/src/server/api/endpoints/room/update.ts b/src/server/api/endpoints/room/update.ts
new file mode 100644
index 0000000000..897f65f2d2
--- /dev/null
+++ b/src/server/api/endpoints/room/update.ts
@@ -0,0 +1,48 @@
+import $ from 'cafy';
+import { publishMainStream } from '../../../../services/stream';
+import define from '../../define';
+import { Users, UserProfiles } from '../../../../models';
+
+export const meta = {
+ requireCredential: true,
+
+ params: {
+ room: {
+ validator: $.obj({
+ furnitures: $.arr($.obj({
+ id: $.str,
+ type: $.str,
+ position: $.obj({
+ x: $.num,
+ y: $.num,
+ z: $.num,
+ }),
+ rotation: $.obj({
+ x: $.num,
+ y: $.num,
+ z: $.num,
+ }),
+ props: $.optional.nullable.obj(),
+ })),
+ roomType: $.str,
+ carpetColor: $.str
+ })
+ },
+ },
+};
+
+export default define(meta, async (ps, user) => {
+ await UserProfiles.update({ userId: user.id }, {
+ room: ps.room as any
+ });
+
+ const iObj = await Users.pack(user.id, user, {
+ detail: true,
+ includeSecrets: true
+ });
+
+ // Publish meUpdated event
+ publishMainStream(user.id, 'meUpdated', iObj);
+
+ return iObj;
+});