mirror of
https://github.com/misskey-dev/misskey.git
synced 2026-06-05 22:14:10 +02:00
wip
This commit is contained in:
@@ -5,12 +5,15 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div :class="$style.root" class="_pageScrollable">
|
<div :class="$style.root" class="_pageScrollable">
|
||||||
<canvas ref="canvas" :class="$style.canvas"></canvas>
|
<canvas ref="canvas" :class="$style.canvas" @keypress="onKeypress" @wheel="onWheel"></canvas>
|
||||||
<div v-if="engine != null" class="_buttons" :class="$style.controls">
|
<div v-if="engine != null" class="_buttons" :class="$style.controls">
|
||||||
<MkButton @click="toggleLight">Toggle Light</MkButton>
|
<!--<MkButton v-for="action in actions" :key="action.key" @click="action.fn">{{ action.label }}{{ hotkeyToLabel(action.hotkey) }}</MkButton>-->
|
||||||
<MkButton :primary="engine.isEditMode.value" @click="toggleEditMode">Edit mode: {{ engine.isEditMode.value ? 'on' : 'off' }}</MkButton>
|
<MkButton :primary="engine.isEditMode.value" @click="toggleEditMode">Edit mode: {{ engine.isEditMode.value ? 'on' : 'off' }}</MkButton>
|
||||||
<template v-if="engine.isEditMode.value">
|
<template v-if="engine.isEditMode.value">
|
||||||
<MkButton @click="grab">Grab (E)</MkButton>
|
<MkButton v-if="engine.ui.isGrabbing" @click="endGrabbing">Put (E)</MkButton>
|
||||||
|
<MkButton v-else-if="engine.ui.isGrabbingForInstall" @click="endGrabbing">Install (E)</MkButton>
|
||||||
|
<MkButton v-else @click="beginSelectedInstalledObjectGrabbing">Grab (E)</MkButton>
|
||||||
|
|
||||||
<MkButton :primary="engine.enableGridSnapping.value" @click="toggleGridSnapping">Grid Snap: {{ engine.enableGridSnapping.value ? 'on' : 'off' }}</MkButton>
|
<MkButton :primary="engine.enableGridSnapping.value" @click="toggleGridSnapping">Grid Snap: {{ engine.enableGridSnapping.value ? 'on' : 'off' }}</MkButton>
|
||||||
<MkButton v-if="engine.enableGridSnapping.value" :primary="engine.gridSnappingScale.value === 1" @click="engine.gridSnappingScale.value = 1">Snap: 1cm</MkButton>
|
<MkButton v-if="engine.enableGridSnapping.value" :primary="engine.gridSnappingScale.value === 1" @click="engine.gridSnappingScale.value = 1">Snap: 1cm</MkButton>
|
||||||
<MkButton v-if="engine.enableGridSnapping.value" :primary="engine.gridSnappingScale.value === 2" @click="engine.gridSnappingScale.value = 2">Snap: 2cm</MkButton>
|
<MkButton v-if="engine.enableGridSnapping.value" :primary="engine.gridSnappingScale.value === 2" @click="engine.gridSnappingScale.value = 2">Snap: 2cm</MkButton>
|
||||||
@@ -27,7 +30,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { defineAsyncComponent, onMounted, onUnmounted, ref, shallowRef, useTemplateRef, watch } from 'vue';
|
import { computed, defineAsyncComponent, onMounted, onUnmounted, ref, shallowRef, useTemplateRef, watch } from 'vue';
|
||||||
import { definePage } from '@/page.js';
|
import { definePage } from '@/page.js';
|
||||||
import { i18n } from '@/i18n.js';
|
import { i18n } from '@/i18n.js';
|
||||||
import { ensureSignin } from '@/i';
|
import { ensureSignin } from '@/i';
|
||||||
@@ -50,6 +53,95 @@ function resize() {
|
|||||||
if (engine.value != null) engine.value.resize();
|
if (engine.value != null) engine.value.resize();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Action = {
|
||||||
|
key: string;
|
||||||
|
label: string;
|
||||||
|
fn: () => void;
|
||||||
|
hotkey?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
function hotkeyToLabel(hotkey: string) {
|
||||||
|
if (hotkey.startsWith('Key')) {
|
||||||
|
return hotkey.slice(3);
|
||||||
|
} else if (hotkey.startsWith('Digit')) {
|
||||||
|
return hotkey.slice(5);
|
||||||
|
} else {
|
||||||
|
return hotkey;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const actions = computed<Action[]>(() => {
|
||||||
|
if (engine.value == null) return [];
|
||||||
|
|
||||||
|
const actions: Action[] = [];
|
||||||
|
|
||||||
|
if (engine.value.isEditMode.value) {
|
||||||
|
actions.push({
|
||||||
|
key: 'grab',
|
||||||
|
label: 'Grab',
|
||||||
|
fn: () => {
|
||||||
|
engine.value!.beginSelectedInstalledObjectGrabbing();
|
||||||
|
canvas.value!.focus();
|
||||||
|
},
|
||||||
|
hotkey: 'KeyE',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (engine.value.isSitting.value) {
|
||||||
|
actions.push({
|
||||||
|
key: 'standUp',
|
||||||
|
label: 'Stand Up',
|
||||||
|
fn: () => engine.value!.standUp(),
|
||||||
|
hotkey: 'KeyQ',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return actions;
|
||||||
|
});
|
||||||
|
|
||||||
|
function onKeypress(ev: KeyboardEvent) {
|
||||||
|
if (engine.value == null) return;
|
||||||
|
|
||||||
|
/* todo
|
||||||
|
if (ev.code === 'KeyE') {
|
||||||
|
ev.preventDefault();
|
||||||
|
ev.stopPropagation();
|
||||||
|
if (engine.value.isEditMode.value) {
|
||||||
|
if (engine.value.ui.isGrabbing || engine.value.ui.isGrabbingForInstall) {
|
||||||
|
this.endGrabbing();
|
||||||
|
} else {
|
||||||
|
this.beginSelectedInstalledObjectGrabbing();
|
||||||
|
}
|
||||||
|
} else if (this.selectedObjectId.value != null) {
|
||||||
|
this.interact(this.selectedObjectId.value);
|
||||||
|
}
|
||||||
|
} else if (ev.code === 'KeyR') {
|
||||||
|
ev.preventDefault();
|
||||||
|
ev.stopPropagation();
|
||||||
|
if (this.grabbingCtx != null) {
|
||||||
|
this.grabbingCtx.rotation += Math.PI / 8;
|
||||||
|
}
|
||||||
|
} else if (ev.code === 'KeyQ') {
|
||||||
|
ev.preventDefault();
|
||||||
|
ev.stopPropagation();
|
||||||
|
if (this.isSitting.value) {
|
||||||
|
this.standUp();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
function onWheel(ev: WheelEvent) {
|
||||||
|
if (engine.value == null) return;
|
||||||
|
|
||||||
|
if (engine.value.ui.isGrabbing || engine.value.ui.isGrabbingForInstall) {
|
||||||
|
ev.preventDefault();
|
||||||
|
ev.stopPropagation();
|
||||||
|
|
||||||
|
engine.value.changeGrabbingDistance(ev.deltaY * 0.025);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
engine.value = new RoomEngine({
|
engine.value = new RoomEngine({
|
||||||
roomType: 'default',
|
roomType: 'default',
|
||||||
@@ -316,14 +408,16 @@ onUnmounted(() => {
|
|||||||
window.removeEventListener('resize', resize);
|
window.removeEventListener('resize', resize);
|
||||||
});
|
});
|
||||||
|
|
||||||
// todo: 掴む/離す(or 設置)のボタン出し分け
|
function beginSelectedInstalledObjectGrabbing() {
|
||||||
// grabbing中かどうか、家具設置中かどうかをengineからリアクティブにもらう必要あり?
|
|
||||||
|
|
||||||
function grab() {
|
|
||||||
engine.value.beginSelectedInstalledObjectGrabbing();
|
engine.value.beginSelectedInstalledObjectGrabbing();
|
||||||
canvas.value!.focus();
|
canvas.value!.focus();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function endGrabbing() {
|
||||||
|
engine.value.endGrabbing();
|
||||||
|
canvas.value!.focus();
|
||||||
|
}
|
||||||
|
|
||||||
function toggleLight() {
|
function toggleLight() {
|
||||||
engine.value.toggleRoomLight();
|
engine.value.toggleRoomLight();
|
||||||
canvas.value!.focus();
|
canvas.value!.focus();
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ import { registerBuiltInLoaders } from '@babylonjs/loaders/dynamic';
|
|||||||
import { BoundingBoxRenderer } from '@babylonjs/core/Rendering/boundingBoxRenderer';
|
import { BoundingBoxRenderer } from '@babylonjs/core/Rendering/boundingBoxRenderer';
|
||||||
import { GridMaterial } from '@babylonjs/materials';
|
import { GridMaterial } from '@babylonjs/materials';
|
||||||
import { ShowInspector } from '@babylonjs/inspector';
|
import { ShowInspector } from '@babylonjs/inspector';
|
||||||
import { ref, watch } from 'vue';
|
import { reactive, ref, watch } from 'vue';
|
||||||
import { genId } from '../id.js';
|
import { genId } from '../id.js';
|
||||||
import { getObjectDef } from './object-defs.js';
|
import { getObjectDef } from './object-defs.js';
|
||||||
import { HorizontalCameraKeyboardMoveInput } from './utility.js';
|
import { HorizontalCameraKeyboardMoveInput } from './utility.js';
|
||||||
@@ -167,6 +167,10 @@ export class RoomEngine {
|
|||||||
private zGridPreviewPlane: BABYLON.Mesh;
|
private zGridPreviewPlane: BABYLON.Mesh;
|
||||||
public isEditMode = ref(false);
|
public isEditMode = ref(false);
|
||||||
public isSitting = ref(false);
|
public isSitting = ref(false);
|
||||||
|
public ui = reactive({
|
||||||
|
isGrabbing: false,
|
||||||
|
isGrabbingForInstall: false,
|
||||||
|
});
|
||||||
|
|
||||||
constructor(roomState: RoomState, options: {
|
constructor(roomState: RoomState, options: {
|
||||||
canvas: HTMLCanvasElement;
|
canvas: HTMLCanvasElement;
|
||||||
@@ -414,43 +418,6 @@ export class RoomEngine {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
this.canvas.addEventListener('keypress', (ev) => {
|
|
||||||
if (ev.code === 'KeyE') {
|
|
||||||
ev.preventDefault();
|
|
||||||
ev.stopPropagation();
|
|
||||||
if (this.isEditMode.value) {
|
|
||||||
if (this.grabbingCtx != null) {
|
|
||||||
this.endGrabbing();
|
|
||||||
} else {
|
|
||||||
this.beginSelectedInstalledObjectGrabbing();
|
|
||||||
}
|
|
||||||
} else if (this.selectedObjectId.value != null) {
|
|
||||||
this.interact(this.selectedObjectId.value);
|
|
||||||
}
|
|
||||||
} else if (ev.code === 'KeyR') {
|
|
||||||
ev.preventDefault();
|
|
||||||
ev.stopPropagation();
|
|
||||||
if (this.grabbingCtx != null) {
|
|
||||||
this.grabbingCtx.rotation += Math.PI / 8;
|
|
||||||
}
|
|
||||||
} else if (ev.code === 'KeyQ') {
|
|
||||||
ev.preventDefault();
|
|
||||||
ev.stopPropagation();
|
|
||||||
if (this.isSitting.value) {
|
|
||||||
this.standUp();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
this.canvas.addEventListener('wheel', (ev) => {
|
|
||||||
if (this.grabbingCtx != null) {
|
|
||||||
ev.preventDefault();
|
|
||||||
ev.stopPropagation();
|
|
||||||
this.grabbingCtx.distance -= ev.deltaY * 0.025;
|
|
||||||
if (this.grabbingCtx.distance < 5/*cm*/) this.grabbingCtx.distance = 5/*cm*/;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (_DEV_) {
|
if (_DEV_) {
|
||||||
const axes = new AxesViewer(this.scene, 30);
|
const axes = new AxesViewer(this.scene, 30);
|
||||||
axes.xAxis.position = new BABYLON.Vector3(0, 30, 0);
|
axes.xAxis.position = new BABYLON.Vector3(0, 30, 0);
|
||||||
@@ -953,6 +920,8 @@ export class RoomEngine {
|
|||||||
|
|
||||||
let sticky: string | null;
|
let sticky: string | null;
|
||||||
|
|
||||||
|
this.ui.isGrabbing = true;
|
||||||
|
|
||||||
this.grabbingCtx = {
|
this.grabbingCtx = {
|
||||||
objectId: selectedObject.metadata.objectId,
|
objectId: selectedObject.metadata.objectId,
|
||||||
objectType: selectedObject.metadata.objectType,
|
objectType: selectedObject.metadata.objectType,
|
||||||
@@ -968,9 +937,13 @@ export class RoomEngine {
|
|||||||
sticky = info.sticky;
|
sticky = info.sticky;
|
||||||
},
|
},
|
||||||
onCancel: () => {
|
onCancel: () => {
|
||||||
|
this.ui.isGrabbing = false;
|
||||||
|
|
||||||
// todo: initialPositionなどを復元
|
// todo: initialPositionなどを復元
|
||||||
},
|
},
|
||||||
onDone: () => { // todo: sticky状態などを引数でもらうようにしたい
|
onDone: () => { // todo: sticky状態などを引数でもらうようにしたい
|
||||||
|
this.ui.isGrabbing = false;
|
||||||
|
|
||||||
// 親から先に外していく
|
// 親から先に外していく
|
||||||
const removeStickyParentRecursively = (mesh: BABYLON.Mesh) => {
|
const removeStickyParentRecursively = (mesh: BABYLON.Mesh) => {
|
||||||
const stickyObjectIds = Array.from(this.roomState.installedObjects.filter(o => o.sticky === mesh.metadata.objectId)).map(o => o.id);
|
const stickyObjectIds = Array.from(this.roomState.installedObjects.filter(o => o.sticky === mesh.metadata.objectId)).map(o => o.id);
|
||||||
@@ -1011,7 +984,7 @@ export class RoomEngine {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private endGrabbing() {
|
public endGrabbing() {
|
||||||
if (this.grabbingCtx == null) return;
|
if (this.grabbingCtx == null) return;
|
||||||
|
|
||||||
//this.grabbing.ghost.dispose(false, true);
|
//this.grabbing.ghost.dispose(false, true);
|
||||||
@@ -1113,6 +1086,8 @@ export class RoomEngine {
|
|||||||
|
|
||||||
let sticky: string | null;
|
let sticky: string | null;
|
||||||
|
|
||||||
|
this.ui.isGrabbingForInstall = true;
|
||||||
|
|
||||||
this.grabbingCtx = {
|
this.grabbingCtx = {
|
||||||
objectId: id,
|
objectId: id,
|
||||||
objectType: type,
|
objectType: type,
|
||||||
@@ -1128,9 +1103,13 @@ export class RoomEngine {
|
|||||||
sticky = info.sticky;
|
sticky = info.sticky;
|
||||||
},
|
},
|
||||||
onCancel: () => {
|
onCancel: () => {
|
||||||
|
this.ui.isGrabbingForInstall = false;
|
||||||
|
|
||||||
// todo
|
// todo
|
||||||
},
|
},
|
||||||
onDone: () => { // todo: sticky状態などを引数でもらうようにしたい
|
onDone: () => { // todo: sticky状態などを引数でもらうようにしたい
|
||||||
|
this.ui.isGrabbingForInstall = false;
|
||||||
|
|
||||||
const pos = this.grabbingCtx.mesh.position.clone();
|
const pos = this.grabbingCtx.mesh.position.clone();
|
||||||
const rotation = this.grabbingCtx.mesh.rotation.clone();
|
const rotation = this.grabbingCtx.mesh.rotation.clone();
|
||||||
|
|
||||||
@@ -1165,6 +1144,12 @@ export class RoomEngine {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public changeGrabbingDistance(delta: number) {
|
||||||
|
if (this.grabbingCtx == null) return;
|
||||||
|
this.grabbingCtx.distance -= delta;
|
||||||
|
if (this.grabbingCtx.distance < 5/*cm*/) this.grabbingCtx.distance = 5/*cm*/;
|
||||||
|
}
|
||||||
|
|
||||||
public resize() {
|
public resize() {
|
||||||
this.engine.resize();
|
this.engine.resize();
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user