From 45b972c059e9841da742538b8a948eefca7c9153 Mon Sep 17 00:00:00 2001 From: syuilo Date: Sun, 18 Aug 2019 14:41:33 +0900 Subject: [PATCH] MisskeyRoom (#5267) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 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: --- .gitattributes | 4 + .gitignore | 7 + CONTRIBUTING.md | 10 + gulpfile.ts | 6 +- locales/ja-JP.yml | 63 ++ migration/1565634203341-room.ts | 13 + package.json | 1 + .../app/common/scripts/room/furniture.ts | 21 + .../app/common/scripts/room/furnitures.json5 | 324 ++++++++ src/client/app/common/scripts/room/room.ts | 690 ++++++++++++++++++ .../views/components/settings/settings.vue | 23 + .../app/common/views/pages/room/preview.vue | 98 +++ .../app/common/views/pages/room/room.vue | 237 ++++++ src/client/app/desktop/script.ts | 1 + .../views/components/ui.header.account.vue | 11 +- src/client/app/store.ts | 2 + src/client/app/tsconfig.json | 8 +- .../assets/room/furnitures/bed/bed.blend | Bin 0 -> 573584 bytes src/client/assets/room/furnitures/bed/bed.glb | Bin 0 -> 8944 bytes .../assets/room/furnitures/book/book.blend | Bin 0 -> 511864 bytes .../assets/room/furnitures/book/book.glb | Bin 0 -> 4304 bytes .../assets/room/furnitures/book2/barcode.png | Bin 0 -> 8924 bytes .../assets/room/furnitures/book2/book2.blend | Bin 0 -> 521456 bytes .../assets/room/furnitures/book2/book2.glb | Bin 0 -> 82920 bytes .../room/furnitures/book2/texture.afdesign | Bin 0 -> 875434 bytes .../assets/room/furnitures/book2/texture.png | Bin 0 -> 65589 bytes .../assets/room/furnitures/book2/uv.png | Bin 0 -> 38259 bytes .../cardboard-box/cardboard-box.blend | Bin 0 -> 515036 bytes .../cardboard-box/cardboard-box.glb | Bin 0 -> 4568 bytes .../cardboard-box2/cardboard-box2.blend | Bin 0 -> 545788 bytes .../cardboard-box2/cardboard-box2.glb | Bin 0 -> 24084 bytes .../furnitures/cardboard-box2/texture.png | Bin 0 -> 19086 bytes .../room/furnitures/cardboard-box2/uv.png | Bin 0 -> 22896 bytes .../cardboard-box3/cardboard-box3.blend | Bin 0 -> 547868 bytes .../cardboard-box3/cardboard-box3.glb | Bin 0 -> 23668 bytes .../furnitures/cardboard-box3/texture.png | Bin 0 -> 18670 bytes .../furnitures/cardboard-box3/texture.xcf | Bin 0 -> 242066 bytes .../room/furnitures/cardboard-box3/uv.png | Bin 0 -> 22098 bytes .../carpet-stripe/carpet-stripe.blend | Bin 0 -> 481784 bytes .../carpet-stripe/carpet-stripe.glb | Bin 0 -> 4956 bytes .../assets/room/furnitures/chair/chair.blend | Bin 0 -> 500696 bytes .../assets/room/furnitures/chair/chair.glb | Bin 0 -> 4252 bytes .../room/furnitures/chair2/chair2.blend | Bin 0 -> 523036 bytes .../assets/room/furnitures/chair2/chair2.glb | Bin 0 -> 6140 bytes .../room/furnitures/color-box/color-box.blend | Bin 0 -> 631168 bytes .../room/furnitures/color-box/color-box.glb | Bin 0 -> 7492 bytes .../room/furnitures/corkboard/corkboard.blend | Bin 0 -> 565404 bytes .../room/furnitures/corkboard/corkboard.glb | Bin 0 -> 7180 bytes .../assets/room/furnitures/cube/cube.blend | Bin 0 -> 646184 bytes .../assets/room/furnitures/cube/cube.glb | Bin 0 -> 188392 bytes .../assets/room/furnitures/desk/desk.blend | Bin 0 -> 541980 bytes .../assets/room/furnitures/desk/desk.glb | Bin 0 -> 10184 bytes .../assets/room/furnitures/eraser/cover.png | Bin 0 -> 11006 bytes .../assets/room/furnitures/eraser/cover.psd | Bin 0 -> 219123 bytes .../room/furnitures/eraser/eraser-uv.png | Bin 0 -> 28597 bytes .../room/furnitures/eraser/eraser.blend | Bin 0 -> 527996 bytes .../assets/room/furnitures/eraser/eraser.glb | Bin 0 -> 15748 bytes .../facial-tissue/facial-tissue-uv.png | Bin 0 -> 23516 bytes .../facial-tissue/facial-tissue.blend | Bin 0 -> 533220 bytes .../facial-tissue/facial-tissue.glb | Bin 0 -> 8332 bytes .../facial-tissue/facial-tissue.png | Bin 0 -> 3888 bytes .../facial-tissue/facial-tissue.psd | Bin 0 -> 111968 bytes .../assets/room/furnitures/fan/fan.blend | Bin 0 -> 772732 bytes src/client/assets/room/furnitures/fan/fan.glb | Bin 0 -> 51612 bytes .../room/furnitures/keyboard/keyboard.blend | Bin 0 -> 1005588 bytes .../room/furnitures/keyboard/keyboard.glb | Bin 0 -> 132360 bytes .../room/furnitures/low-table/low-table.blend | Bin 0 -> 497336 bytes .../room/furnitures/low-table/low-table.glb | Bin 0 -> 2364 bytes .../assets/room/furnitures/mat/mat.blend | Bin 0 -> 489304 bytes src/client/assets/room/furnitures/mat/mat.glb | Bin 0 -> 1700 bytes .../assets/room/furnitures/milk/milk-uv.png | Bin 0 -> 50388 bytes .../assets/room/furnitures/milk/milk.blend | Bin 0 -> 539836 bytes .../assets/room/furnitures/milk/milk.glb | Bin 0 -> 8456 bytes .../assets/room/furnitures/milk/milk.png | Bin 0 -> 3846 bytes .../assets/room/furnitures/milk/milk.psd | Bin 0 -> 125415 bytes .../room/furnitures/monitor/monitor.blend | Bin 0 -> 586836 bytes .../room/furnitures/monitor/monitor.glb | Bin 0 -> 40092 bytes .../room/furnitures/monitor/monitor.psd | Bin 0 -> 3692204 bytes .../room/furnitures/monitor/screen-uv.png | Bin 0 -> 5951 bytes .../assets/room/furnitures/monitor/screen.jpg | Bin 0 -> 29629 bytes .../assets/room/furnitures/moon/moon.blend | Bin 0 -> 725536 bytes .../assets/room/furnitures/moon/moon.glb | Bin 0 -> 122112 bytes .../assets/room/furnitures/moon/moon.jpg | Bin 0 -> 87155 bytes .../room/furnitures/mousepad/mousepad.blend | Bin 0 -> 495008 bytes .../room/furnitures/mousepad/mousepad.glb | Bin 0 -> 4360 bytes .../room/furnitures/pc/motherboard-uv.png | Bin 0 -> 21944 bytes .../room/furnitures/pc/motherboard-uv.psd | Bin 0 -> 7131432 bytes .../assets/room/furnitures/pc/motherboard.jpg | Bin 0 -> 68860 bytes src/client/assets/room/furnitures/pc/pc.blend | Bin 0 -> 818152 bytes src/client/assets/room/furnitures/pc/pc.glb | Bin 0 -> 103620 bytes .../room/furnitures/pencil/pencil.blend | Bin 0 -> 683032 bytes .../assets/room/furnitures/pencil/pencil.glb | Bin 0 -> 7788 bytes .../room/furnitures/photoframe/photo-uv.png | Bin 0 -> 20828 bytes .../room/furnitures/photoframe/photo.jpg | Bin 0 -> 31845 bytes .../furnitures/photoframe/photoframe.blend | Bin 0 -> 551416 bytes .../room/furnitures/photoframe/photoframe.glb | Bin 0 -> 37612 bytes .../assets/room/furnitures/piano/piano.blend | Bin 0 -> 713228 bytes .../assets/room/furnitures/piano/piano.glb | Bin 0 -> 73320 bytes .../room/furnitures/pinguin/pinguin.glb | Bin 0 -> 60192 bytes .../room/furnitures/plant/plant-soil-uv.png | Bin 0 -> 23726 bytes .../room/furnitures/plant/plant-soil.png | Bin 0 -> 13370 bytes .../room/furnitures/plant/plant-soil.psd | Bin 0 -> 390188 bytes .../assets/room/furnitures/plant/plant.blend | Bin 0 -> 604488 bytes .../assets/room/furnitures/plant/plant.glb | Bin 0 -> 27664 bytes .../room/furnitures/plant2/plant2.blend | Bin 0 -> 1060816 bytes .../assets/room/furnitures/plant2/plant2.glb | Bin 0 -> 133212 bytes .../assets/room/furnitures/plant2/soil.png | Bin 0 -> 13370 bytes .../room/furnitures/poster-h/poster-h.blend | Bin 0 -> 667604 bytes .../room/furnitures/poster-h/poster-h.glb | Bin 0 -> 1280 bytes .../assets/room/furnitures/poster-h/uv.png | Bin 0 -> 20629 bytes .../room/furnitures/poster-v/poster-v.blend | Bin 0 -> 650740 bytes .../room/furnitures/poster-v/poster-v.glb | Bin 0 -> 1280 bytes .../assets/room/furnitures/poster-v/uv.png | Bin 0 -> 22374 bytes .../room/furnitures/pudding/pudding.blend | Bin 0 -> 694784 bytes .../room/furnitures/pudding/pudding.glb | Bin 0 -> 10612 bytes .../furnitures/rubik-cube/rubik-cube.blend | Bin 0 -> 745960 bytes .../room/furnitures/rubik-cube/rubik-cube.glb | Bin 0 -> 67688 bytes .../assets/room/furnitures/server/rack-uv.png | Bin 0 -> 27318 bytes .../assets/room/furnitures/server/rack.png | Bin 0 -> 9641 bytes .../room/furnitures/server/server.blend | Bin 0 -> 684372 bytes .../assets/room/furnitures/server/server.glb | Bin 0 -> 149480 bytes .../assets/room/furnitures/server/server.png | Bin 0 -> 124668 bytes .../assets/room/furnitures/server/uv.png | Bin 0 -> 22827 bytes .../assets/room/furnitures/tv/screen-uv.png | Bin 0 -> 21040 bytes src/client/assets/room/furnitures/tv/tv.blend | Bin 0 -> 587640 bytes src/client/assets/room/furnitures/tv/tv.glb | Bin 0 -> 8580 bytes .../furnitures/wall-clock/wall-clock.blend | Bin 0 -> 574128 bytes .../room/furnitures/wall-clock/wall-clock.glb | Bin 0 -> 47848 bytes .../assets/room/rooms/default/default.blend | Bin 0 -> 618364 bytes .../assets/room/rooms/default/default.glb | Bin 0 -> 12280 bytes .../assets/room/rooms/washitsu/husuma-uv.png | Bin 0 -> 22586 bytes .../assets/room/rooms/washitsu/husuma.png | Bin 0 -> 4336 bytes .../room/rooms/washitsu/tatami-single1600.png | Bin 0 -> 1191346 bytes .../assets/room/rooms/washitsu/tatami-uv.png | Bin 0 -> 22804 bytes .../room/rooms/washitsu/tatami.afdesign | Bin 0 -> 2501804 bytes .../assets/room/rooms/washitsu/tatami.png | Bin 0 -> 315585 bytes .../assets/room/rooms/washitsu/washitsu.blend | Bin 0 -> 664000 bytes .../assets/room/rooms/washitsu/washitsu.glb | Bin 0 -> 415180 bytes src/models/entities/user-profile.ts | 6 + src/server/api/endpoints/room/show.ts | 92 +++ src/server/api/endpoints/room/update.ts | 48 ++ tsconfig.json | 1 + yarn.lock | 5 + 143 files changed, 1667 insertions(+), 4 deletions(-) create mode 100644 migration/1565634203341-room.ts create mode 100644 src/client/app/common/scripts/room/furniture.ts create mode 100644 src/client/app/common/scripts/room/furnitures.json5 create mode 100644 src/client/app/common/scripts/room/room.ts create mode 100644 src/client/app/common/views/pages/room/preview.vue create mode 100644 src/client/app/common/views/pages/room/room.vue create mode 100644 src/client/assets/room/furnitures/bed/bed.blend create mode 100644 src/client/assets/room/furnitures/bed/bed.glb create mode 100644 src/client/assets/room/furnitures/book/book.blend create mode 100644 src/client/assets/room/furnitures/book/book.glb create mode 100644 src/client/assets/room/furnitures/book2/barcode.png create mode 100644 src/client/assets/room/furnitures/book2/book2.blend create mode 100644 src/client/assets/room/furnitures/book2/book2.glb create mode 100644 src/client/assets/room/furnitures/book2/texture.afdesign create mode 100644 src/client/assets/room/furnitures/book2/texture.png create mode 100644 src/client/assets/room/furnitures/book2/uv.png create mode 100644 src/client/assets/room/furnitures/cardboard-box/cardboard-box.blend create mode 100644 src/client/assets/room/furnitures/cardboard-box/cardboard-box.glb create mode 100644 src/client/assets/room/furnitures/cardboard-box2/cardboard-box2.blend create mode 100644 src/client/assets/room/furnitures/cardboard-box2/cardboard-box2.glb create mode 100644 src/client/assets/room/furnitures/cardboard-box2/texture.png create mode 100644 src/client/assets/room/furnitures/cardboard-box2/uv.png create mode 100644 src/client/assets/room/furnitures/cardboard-box3/cardboard-box3.blend create mode 100644 src/client/assets/room/furnitures/cardboard-box3/cardboard-box3.glb create mode 100644 src/client/assets/room/furnitures/cardboard-box3/texture.png create mode 100644 src/client/assets/room/furnitures/cardboard-box3/texture.xcf create mode 100644 src/client/assets/room/furnitures/cardboard-box3/uv.png create mode 100644 src/client/assets/room/furnitures/carpet-stripe/carpet-stripe.blend create mode 100644 src/client/assets/room/furnitures/carpet-stripe/carpet-stripe.glb create mode 100644 src/client/assets/room/furnitures/chair/chair.blend create mode 100644 src/client/assets/room/furnitures/chair/chair.glb create mode 100644 src/client/assets/room/furnitures/chair2/chair2.blend create mode 100644 src/client/assets/room/furnitures/chair2/chair2.glb create mode 100644 src/client/assets/room/furnitures/color-box/color-box.blend create mode 100644 src/client/assets/room/furnitures/color-box/color-box.glb create mode 100644 src/client/assets/room/furnitures/corkboard/corkboard.blend create mode 100644 src/client/assets/room/furnitures/corkboard/corkboard.glb create mode 100644 src/client/assets/room/furnitures/cube/cube.blend create mode 100644 src/client/assets/room/furnitures/cube/cube.glb create mode 100644 src/client/assets/room/furnitures/desk/desk.blend create mode 100644 src/client/assets/room/furnitures/desk/desk.glb create mode 100644 src/client/assets/room/furnitures/eraser/cover.png create mode 100644 src/client/assets/room/furnitures/eraser/cover.psd create mode 100644 src/client/assets/room/furnitures/eraser/eraser-uv.png create mode 100644 src/client/assets/room/furnitures/eraser/eraser.blend create mode 100644 src/client/assets/room/furnitures/eraser/eraser.glb create mode 100644 src/client/assets/room/furnitures/facial-tissue/facial-tissue-uv.png create mode 100644 src/client/assets/room/furnitures/facial-tissue/facial-tissue.blend create mode 100644 src/client/assets/room/furnitures/facial-tissue/facial-tissue.glb create mode 100644 src/client/assets/room/furnitures/facial-tissue/facial-tissue.png create mode 100644 src/client/assets/room/furnitures/facial-tissue/facial-tissue.psd create mode 100644 src/client/assets/room/furnitures/fan/fan.blend create mode 100644 src/client/assets/room/furnitures/fan/fan.glb create mode 100644 src/client/assets/room/furnitures/keyboard/keyboard.blend create mode 100644 src/client/assets/room/furnitures/keyboard/keyboard.glb create mode 100644 src/client/assets/room/furnitures/low-table/low-table.blend create mode 100644 src/client/assets/room/furnitures/low-table/low-table.glb create mode 100644 src/client/assets/room/furnitures/mat/mat.blend create mode 100644 src/client/assets/room/furnitures/mat/mat.glb create mode 100644 src/client/assets/room/furnitures/milk/milk-uv.png create mode 100644 src/client/assets/room/furnitures/milk/milk.blend create mode 100644 src/client/assets/room/furnitures/milk/milk.glb create mode 100644 src/client/assets/room/furnitures/milk/milk.png create mode 100644 src/client/assets/room/furnitures/milk/milk.psd create mode 100644 src/client/assets/room/furnitures/monitor/monitor.blend create mode 100644 src/client/assets/room/furnitures/monitor/monitor.glb create mode 100644 src/client/assets/room/furnitures/monitor/monitor.psd create mode 100644 src/client/assets/room/furnitures/monitor/screen-uv.png create mode 100644 src/client/assets/room/furnitures/monitor/screen.jpg create mode 100644 src/client/assets/room/furnitures/moon/moon.blend create mode 100644 src/client/assets/room/furnitures/moon/moon.glb create mode 100644 src/client/assets/room/furnitures/moon/moon.jpg create mode 100644 src/client/assets/room/furnitures/mousepad/mousepad.blend create mode 100644 src/client/assets/room/furnitures/mousepad/mousepad.glb create mode 100644 src/client/assets/room/furnitures/pc/motherboard-uv.png create mode 100644 src/client/assets/room/furnitures/pc/motherboard-uv.psd create mode 100644 src/client/assets/room/furnitures/pc/motherboard.jpg create mode 100644 src/client/assets/room/furnitures/pc/pc.blend create mode 100644 src/client/assets/room/furnitures/pc/pc.glb create mode 100644 src/client/assets/room/furnitures/pencil/pencil.blend create mode 100644 src/client/assets/room/furnitures/pencil/pencil.glb create mode 100644 src/client/assets/room/furnitures/photoframe/photo-uv.png create mode 100644 src/client/assets/room/furnitures/photoframe/photo.jpg create mode 100644 src/client/assets/room/furnitures/photoframe/photoframe.blend create mode 100644 src/client/assets/room/furnitures/photoframe/photoframe.glb create mode 100644 src/client/assets/room/furnitures/piano/piano.blend create mode 100644 src/client/assets/room/furnitures/piano/piano.glb create mode 100644 src/client/assets/room/furnitures/pinguin/pinguin.glb create mode 100644 src/client/assets/room/furnitures/plant/plant-soil-uv.png create mode 100644 src/client/assets/room/furnitures/plant/plant-soil.png create mode 100644 src/client/assets/room/furnitures/plant/plant-soil.psd create mode 100644 src/client/assets/room/furnitures/plant/plant.blend create mode 100644 src/client/assets/room/furnitures/plant/plant.glb create mode 100644 src/client/assets/room/furnitures/plant2/plant2.blend create mode 100644 src/client/assets/room/furnitures/plant2/plant2.glb create mode 100644 src/client/assets/room/furnitures/plant2/soil.png create mode 100644 src/client/assets/room/furnitures/poster-h/poster-h.blend create mode 100644 src/client/assets/room/furnitures/poster-h/poster-h.glb create mode 100644 src/client/assets/room/furnitures/poster-h/uv.png create mode 100644 src/client/assets/room/furnitures/poster-v/poster-v.blend create mode 100644 src/client/assets/room/furnitures/poster-v/poster-v.glb create mode 100644 src/client/assets/room/furnitures/poster-v/uv.png create mode 100644 src/client/assets/room/furnitures/pudding/pudding.blend create mode 100644 src/client/assets/room/furnitures/pudding/pudding.glb create mode 100644 src/client/assets/room/furnitures/rubik-cube/rubik-cube.blend create mode 100644 src/client/assets/room/furnitures/rubik-cube/rubik-cube.glb create mode 100644 src/client/assets/room/furnitures/server/rack-uv.png create mode 100644 src/client/assets/room/furnitures/server/rack.png create mode 100644 src/client/assets/room/furnitures/server/server.blend create mode 100644 src/client/assets/room/furnitures/server/server.glb create mode 100644 src/client/assets/room/furnitures/server/server.png create mode 100644 src/client/assets/room/furnitures/server/uv.png create mode 100644 src/client/assets/room/furnitures/tv/screen-uv.png create mode 100644 src/client/assets/room/furnitures/tv/tv.blend create mode 100644 src/client/assets/room/furnitures/tv/tv.glb create mode 100644 src/client/assets/room/furnitures/wall-clock/wall-clock.blend create mode 100644 src/client/assets/room/furnitures/wall-clock/wall-clock.glb create mode 100644 src/client/assets/room/rooms/default/default.blend create mode 100644 src/client/assets/room/rooms/default/default.glb create mode 100644 src/client/assets/room/rooms/washitsu/husuma-uv.png create mode 100644 src/client/assets/room/rooms/washitsu/husuma.png create mode 100644 src/client/assets/room/rooms/washitsu/tatami-single1600.png create mode 100644 src/client/assets/room/rooms/washitsu/tatami-uv.png create mode 100644 src/client/assets/room/rooms/washitsu/tatami.afdesign create mode 100644 src/client/assets/room/rooms/washitsu/tatami.png create mode 100644 src/client/assets/room/rooms/washitsu/washitsu.blend create mode 100644 src/client/assets/room/rooms/washitsu/washitsu.glb create mode 100644 src/server/api/endpoints/room/show.ts create mode 100644 src/server/api/endpoints/room/update.ts diff --git a/.gitattributes b/.gitattributes index 952d6cd0e9..a175917f31 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,3 +1,7 @@ *.svg -diff -text *.psd -diff -text *.ai -diff -text +*.mqo -diff -text +*.glb -diff -text +*.blend -diff -text +*.afdesign -diff -text diff --git a/.gitignore b/.gitignore index e438ad890c..4baefc147f 100644 --- a/.gitignore +++ b/.gitignore @@ -30,3 +30,10 @@ api-docs.json .DS_Store /files ormconfig.json + +# blender backups +*.blend1 +*.blend2 +*.blend3 +*.blend4 +*.blend5 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index ba45f5d23c..0c3bfa42f7 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -38,6 +38,16 @@ Documentation of Vue I18n is available at http://kazupon.github.io/vue-i18n/intr Misskey uses CircleCI for executing automated tests. Configuration files are located in [`/.circleci`](/.circleci). +## Adding MisskeyRoom items +Currently, we accept only 3D models created with [Blender](https://www.blender.org/). + +* Use English for material, object and texture names +* Use meter for unit of length +* Your PR must include all source files of your models (for later editing) +* Your PR must include the glTF binary files (.glb) of your models + +You can find information on glTF 2.0 at [glTF 2.0 — Blender Manual]( https://docs.blender.org/manual/en/dev/addons/io_scene_gltf2.html). + ## FAQ ### How to resolve conflictions occurred at yarn.lock? diff --git a/gulpfile.ts b/gulpfile.ts index 85305cd0e4..6ed597731f 100644 --- a/gulpfile.ts +++ b/gulpfile.ts @@ -47,7 +47,11 @@ gulp.task('build:copy:views', () => gulp.src('./src/server/web/views/**/*').pipe(gulp.dest('./built/server/web/views')) ); -gulp.task('build:copy', gulp.parallel('build:copy:views', () => +gulp.task('build:copy:fonts', () => + gulp.src('./node_modules/three/examples/fonts/**/*').pipe(gulp.dest('./built/client/assets/fonts/')) +); + +gulp.task('build:copy', gulp.parallel('build:copy:views', 'build:copy:fonts', () => gulp.src([ './src/const.json', './src/server/web/views/**/*', diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index dc9dd87a7f..8a34ea935b 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -308,6 +308,16 @@ common: saved: "保存しました" home-profile: "ホームのプロファイル" deck-profile: "デッキのプロファイル" + room: "ルーム" + _room: + graphicsQuality: "グラフィックの品質" + _graphicsQuality: + ultra: "最高" + high: "高" + medium: "中" + low: "低" + cheep: "最低" + useOrthographicCamera: "平行投影カメラを使用" search: "検索" delete: "削除" @@ -1250,6 +1260,7 @@ desktop/views/components/ui.header.account.vue: groups: "グループ" follow-requests: "フォロー申請" admin: "管理" + room: "ルーム" desktop/views/components/ui.header.nav.vue: game: "ゲーム" @@ -2281,3 +2292,55 @@ pages: enviromentVariables: "環境変数" pageVariables: "ページ要素" argVariables: "入力スロット" + +room: + add-furniture: "家具を置く" + translate: "移動" + rotate: "回転" + exit: "戻る" + remove: "しまう" + save: "保存" + chooseImage: "画像を選択" + room-type: "部屋のタイプ" + carpet-color: "床の色" + rooms: + default: "デフォルト" + washitsu: "和室" + furnitures: + milk: "牛乳パック" + bed: "ベッド" + low-table: "ローテーブル" + desk: "デスク" + chair: "チェア" + chair2: "チェア2" + fan: "換気扇" + pc: "パソコン" + plant: "観葉植物" + plant2: "観葉植物2" + eraser: "消しゴム" + pencil: "鉛筆" + pudding: "プリン" + cardboard-box: "段ボール箱" + cardboard-box2: "段ボール箱2" + cardboard-box3: "段ボール箱3" + book: "本" + book2: "本2" + piano: "ピアノ" + facial-tissue: "ティッシュボックス" + server: "サーバー" + moon: "月" + corkboard: "コルクボード" + mousepad: "マウスパッド" + monitor: "モニター" + keyboard: "キーボード" + carpet-stripe: "カーペット(縞)" + mat: "マット" + color-box: "カラーボックス" + wall-clock: "壁掛け時計" + photoframe: "額縁" + cube: "キューブ" + tv: "テレビ" + pinguin: "ピンギン" + rubik-cube: "ルービックキューブ" + poster-h: "ポスター(横長)" + poster-v: "ポスター(縦長)" diff --git a/migration/1565634203341-room.ts b/migration/1565634203341-room.ts new file mode 100644 index 0000000000..783b375ff9 --- /dev/null +++ b/migration/1565634203341-room.ts @@ -0,0 +1,13 @@ +import {MigrationInterface, QueryRunner} from "typeorm"; + +export class room1565634203341 implements MigrationInterface { + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(`ALTER TABLE "user_profile" ADD "room" jsonb NOT NULL DEFAULT '{}'`); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query(`ALTER TABLE "user_profile" DROP COLUMN "room"`); + } + +} diff --git a/package.json b/package.json index 5cd0299c0e..85beac348c 100644 --- a/package.json +++ b/package.json @@ -223,6 +223,7 @@ "syuilo-password-strength": "0.0.1", "terser-webpack-plugin": "1.3.0", "textarea-caret": "3.1.0", + "three": "0.107.0", "tinycolor2": "1.4.1", "tmp": "0.1.0", "ts-loader": "5.3.3", 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; +}; 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: { +// : +// } +// +// proptype一覧: +// * image ... 画像選択ダイアログを出し、その画像のURLが格納されます +// * color ... 色選択コントロールを出し、選択された色が格納されます + +// 家具にカスタムテクスチャを適用できるようにするには、textureプロパティに以下の追加の情報を含めます: +// 便宜上そのUVのどの部分にカスタムテクスチャを貼り合わせるかのエリアをテクスチャエリアと呼びます。 +// UVは1024*1024だと仮定します。 +// +// : { +// prop: <プロパティ名>, +// uv: { +// x: <テクスチャエリアX座標>, +// y: <テクスチャエリアY座標>, +// width: <テクスチャエリアの幅>, +// height: <テクスチャエリアの高さ>, +// }, +// } +// +// には、カスタムテクスチャを適用したいメッシュ名を指定します +// <プロパティ名>には、カスタムテクスチャとして使用する画像を格納するプロパティ(前述)名を指定します + +// 家具にカスタムカラーを適用できるようにするには、colorプロパティに以下の追加の情報を含めます: +// +// : <プロパティ名> +// +// には、カスタムカラーを適用したいマテリアル名を指定します +// <プロパティ名>には、カスタムカラーとして使用する色を格納するプロパティ(前述)名を指定します + +[ + { + 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((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 @@ + +
+
{{ $t('@._settings.room') }}
+ + + + + + + + + {{ $t('@._settings._room.useOrthographicCamera') }} +
@@ -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 @@ + + + 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 @@ + + + + + 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 @@ +
  • + + + {{ $t('room') }} + + +