MisskeyRoom (#5267)

* wip

* Add pemcil

* Fix bug

* Update .gitattributes

* Add 🍮

* Better 🍮

* Add color boxes

* Add pc

* Add keyboard

* Add 📦

* Add more 📦

* ✌️

* carpet

* Add plant

* ✌️

* ✌️

* Update room.vue

* Add plant

* ✌️

* ✌️

* ✌️

* ✌️

* ✌️

* 段ボール箱がてかりすぎているのを修正

* Update room.ts

* Render username

* ✌️

* Add new 📦

* Update room.ts

* Remove blender backup files

* Refactor

* Improve performance

* Update room.ts

* Update .gitattributes

* Update room.ts

* Better fan

* Better tissue rendering

* Add 🐧

* Create photoframe2.glb

* chairs

* Add 📖

* fix: HiDPi環境でオブジェクトを選択できない (#5268)

* Better monitor

* ✌️

* 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 📺

* 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

* マットの色を変えられるように

* ✌️

* 異方性フィルタリングするように

* グラフィックの品質をフィルタリングに反映

* Add bloom effect when ultra graphics

* Add posters

* 🎨
This commit is contained in:
syuilo 2019-08-18 14:41:33 +09:00 committed by GitHub
parent 08d9a6a720
commit 4fbb3e581f
143 changed files with 2030 additions and 4 deletions

4
.gitattributes vendored
View File

@ -1,6 +1,10 @@
*.svg -diff -text
*.psd -diff -text
*.ai -diff -text
*.mqo -diff -text
*.glb -diff -text
*.blend -diff -text
*.afdesign -diff -text
*.zip filter=lfs diff=lfs merge=lfs -text
*.xcf filter=lfs diff=lfs merge=lfs -text
*.ai filter=lfs diff=lfs merge=lfs -text

7
.gitignore vendored
View File

@ -30,3 +30,10 @@ api-docs.json
.DS_Store
/files
ormconfig.json
# blender backups
*.blend1
*.blend2
*.blend3
*.blend4
*.blend5

View File

@ -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?

View File

@ -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/**/*',

View File

@ -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: "ポスター(縦長)"

View File

@ -0,0 +1,13 @@
import {MigrationInterface, QueryRunner} from "typeorm";
export class room1565634203341 implements MigrationInterface {
public async up(queryRunner: QueryRunner): Promise<any> {
await queryRunner.query(`ALTER TABLE "user_profile" ADD "room" jsonb NOT NULL DEFAULT '{}'`);
}
public async down(queryRunner: QueryRunner): Promise<any> {
await queryRunner.query(`ALTER TABLE "user_profile" DROP COLUMN "room"`);
}
}

View File

@ -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",

View File

@ -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>;
};

View File

@ -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,
},
},
},
},
]

View File

@ -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);
}
}

View File

@ -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 }); }

View File

@ -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>

View File

@ -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>

View File

@ -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 },

View File

@ -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: {

View File

@ -76,6 +76,8 @@ const defaultDeviceSettings = {
expandUsersPhotos: true,
expandUsersActivity: true,
enableMobileQuickNotificationView: false,
roomGraphicsQuality: 'medium',
roomUseOrthographicCamera: true,
};
export default (os: MiOS) => new Vuex.Store({

View File

@ -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": [

View File

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:15ccf449b85d73ef668a7742b6d1c7123c622061685644ba3acd7a179cb2abea
size 573584

View File

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:b29c807a0128c4e11e8acf2c523f8932102491a9d25fffad08ac2e46d2accdb7
size 8944

BIN
src/client/assets/room/furnitures/book/book.blend (Stored with Git LFS) Normal file

Binary file not shown.

View File

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:3b0018d07eb02779d2d4df30832cf5dea8093f2e7c8e192c022088b81e2309f3
size 4304

View File

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:8ee1f98f2f9817f33852b8e22fb94383c4320b2d419353b1c09bebb49a5ca9fe
size 8924

BIN
src/client/assets/room/furnitures/book2/book2.blend (Stored with Git LFS) Normal file

Binary file not shown.

BIN
src/client/assets/room/furnitures/book2/book2.glb (Stored with Git LFS) Normal file

Binary file not shown.

BIN
src/client/assets/room/furnitures/book2/texture.afdesign (Stored with Git LFS) Normal file

Binary file not shown.

BIN
src/client/assets/room/furnitures/book2/texture.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
src/client/assets/room/furnitures/book2/uv.png (Stored with Git LFS) Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
src/client/assets/room/furnitures/cardboard-box2/texture.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
src/client/assets/room/furnitures/cardboard-box2/uv.png (Stored with Git LFS) Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
src/client/assets/room/furnitures/cardboard-box3/texture.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
src/client/assets/room/furnitures/cardboard-box3/texture.xcf (Stored with Git LFS) Normal file

Binary file not shown.

BIN
src/client/assets/room/furnitures/cardboard-box3/uv.png (Stored with Git LFS) Normal file

Binary file not shown.

View File

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:c67916c00bbb2238fd8801bd33ddd8619aaf2f58a53da8dd5b4c096279487613
size 481784

Binary file not shown.

BIN
src/client/assets/room/furnitures/chair/chair.blend (Stored with Git LFS) Normal file

Binary file not shown.

BIN
src/client/assets/room/furnitures/chair/chair.glb (Stored with Git LFS) Normal file

Binary file not shown.

BIN
src/client/assets/room/furnitures/chair2/chair2.blend (Stored with Git LFS) Normal file

Binary file not shown.

BIN
src/client/assets/room/furnitures/chair2/chair2.glb (Stored with Git LFS) Normal file

Binary file not shown.

BIN
src/client/assets/room/furnitures/color-box/color-box.blend (Stored with Git LFS) Normal file

Binary file not shown.

BIN
src/client/assets/room/furnitures/color-box/color-box.glb (Stored with Git LFS) Normal file

Binary file not shown.

View File

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:0a7f3e3c3716f284a89f09392f50c4a8c9988ef9905f08749c08e5e6b93d58a2
size 565404

BIN
src/client/assets/room/furnitures/corkboard/corkboard.glb (Stored with Git LFS) Normal file

Binary file not shown.

BIN
src/client/assets/room/furnitures/cube/cube.blend (Stored with Git LFS) Normal file

Binary file not shown.

View File

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:f70817e90861ddc17173e79986f5ac02447635c0cc6edd8eec8f8eabd685678a
size 188392

BIN
src/client/assets/room/furnitures/desk/desk.blend (Stored with Git LFS) Normal file

Binary file not shown.

BIN
src/client/assets/room/furnitures/desk/desk.glb (Stored with Git LFS) Normal file

Binary file not shown.

BIN
src/client/assets/room/furnitures/eraser/cover.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
src/client/assets/room/furnitures/eraser/cover.psd (Stored with Git LFS) Normal file

Binary file not shown.

BIN
src/client/assets/room/furnitures/eraser/eraser-uv.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
src/client/assets/room/furnitures/eraser/eraser.blend (Stored with Git LFS) Normal file

Binary file not shown.

BIN
src/client/assets/room/furnitures/eraser/eraser.glb (Stored with Git LFS) Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:e59f36a617f6b8f7bea5a1ae7e8830a58e2cc549796a6054190523a7211d89f0
size 8332

Binary file not shown.

Binary file not shown.

BIN
src/client/assets/room/furnitures/fan/fan.blend (Stored with Git LFS) Normal file

Binary file not shown.

View File

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:65593e078bbb6861788cade8525b170364243780e2abf522f190691b4c87c46c
size 51612

BIN
src/client/assets/room/furnitures/keyboard/keyboard.blend (Stored with Git LFS) Normal file

Binary file not shown.

BIN
src/client/assets/room/furnitures/keyboard/keyboard.glb (Stored with Git LFS) Normal file

Binary file not shown.

BIN
src/client/assets/room/furnitures/low-table/low-table.blend (Stored with Git LFS) Normal file

Binary file not shown.

BIN
src/client/assets/room/furnitures/low-table/low-table.glb (Stored with Git LFS) Normal file

Binary file not shown.

BIN
src/client/assets/room/furnitures/mat/mat.blend (Stored with Git LFS) Normal file

Binary file not shown.

View File

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:191fc3f4da2323cce9148168e9e324f640a1143036a39b22905fa77ee98225c2
size 1700

View File

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:110906edad9c5e13d77e8599387480e7f6242912f6f8246ba8f2b9fe278625ee
size 50388

BIN
src/client/assets/room/furnitures/milk/milk.blend (Stored with Git LFS) Normal file

Binary file not shown.

View File

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:f3d012ad177ecb771bab7a9e746800ed09a31db9b6106632c4777a97544301a6
size 8456

BIN
src/client/assets/room/furnitures/milk/milk.png (Stored with Git LFS) Normal file

Binary file not shown.

View File

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:8e68dd2bcf0f4b0794b1acea8e38d8f38ff83c3c9c1ac68c3112845c3ed9cba9
size 125415

BIN
src/client/assets/room/furnitures/monitor/monitor.blend (Stored with Git LFS) Normal file

Binary file not shown.

BIN
src/client/assets/room/furnitures/monitor/monitor.glb (Stored with Git LFS) Normal file

Binary file not shown.

BIN
src/client/assets/room/furnitures/monitor/monitor.psd (Stored with Git LFS) Normal file

Binary file not shown.

BIN
src/client/assets/room/furnitures/monitor/screen-uv.png (Stored with Git LFS) Normal file

Binary file not shown.

View File

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:0c533a8b9110fc597650f9112702103da1956a6cbe8acead3211186610dbc4e2
size 29629

View File

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:fc9289bebf658662e17b537f4004894861de229a0ca2b81aa47388cc18885bc6
size 725536

View File

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:5c8e31e99fc62a29b493749f6b94fa46c8284e76bd758f8324ad1d617843cb1f
size 122112

BIN
src/client/assets/room/furnitures/moon/moon.jpg (Stored with Git LFS) Normal file

Binary file not shown.

BIN
src/client/assets/room/furnitures/mousepad/mousepad.blend (Stored with Git LFS) Normal file

Binary file not shown.

BIN
src/client/assets/room/furnitures/mousepad/mousepad.glb (Stored with Git LFS) Normal file

Binary file not shown.

BIN
src/client/assets/room/furnitures/pc/motherboard-uv.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
src/client/assets/room/furnitures/pc/motherboard-uv.psd (Stored with Git LFS) Normal file

Binary file not shown.

BIN
src/client/assets/room/furnitures/pc/motherboard.jpg (Stored with Git LFS) Normal file

Binary file not shown.

View File

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:881e55e5ea3865aee9c25431c71333ffc676ed21b6d64353762f459942fbcc29
size 818152

BIN
src/client/assets/room/furnitures/pc/pc.glb (Stored with Git LFS) Normal file

Binary file not shown.

BIN
src/client/assets/room/furnitures/pencil/pencil.blend (Stored with Git LFS) Normal file

Binary file not shown.

View File

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:e07e8d7fdc061604bcb94a4fbbd7350a0dbad5eff4805204133c5f0af432e0ca
size 7788

BIN
src/client/assets/room/furnitures/photoframe/photo-uv.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
src/client/assets/room/furnitures/photoframe/photo.jpg (Stored with Git LFS) Normal file

Binary file not shown.

Binary file not shown.

BIN
src/client/assets/room/furnitures/photoframe/photoframe.glb (Stored with Git LFS) Normal file

Binary file not shown.

BIN
src/client/assets/room/furnitures/piano/piano.blend (Stored with Git LFS) Normal file

Binary file not shown.

View File

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:3eaf2446b3c9feba2d42bc65d5c494dc8221d63b62641bfe17e44820cb97314a
size 73320

View File

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:60963376ddbcf4635e73880ee29107ab8951c6a97314f03727900416c0e7db3b
size 60192

BIN
src/client/assets/room/furnitures/plant/plant-soil-uv.png (Stored with Git LFS) Normal file

Binary file not shown.

Some files were not shown because too many files have changed in this diff Show More