This commit is contained in:
syuilo
2026-02-14 21:43:02 +09:00
parent ad48f43524
commit 1dec481a7e
2 changed files with 73 additions and 28 deletions

View File

@@ -7,13 +7,16 @@ SPDX-License-Identifier: AGPL-3.0-only
<div :class="$style.root" class="_pageScrollable">
<canvas ref="canvas" :class="$style.canvas"></canvas>
<div v-if="engine != null" class="_buttons" :class="$style.controls">
<MkButton @click="grab">Grab</MkButton>
<MkButton @click="toggleLight">Toggle Light</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 === 2" @click="engine.gridSnappingScale.value = 2">Snap: 2cm</MkButton>
<MkButton v-if="engine.enableGridSnapping.value" :primary="engine.gridSnappingScale.value === 4" @click="engine.gridSnappingScale.value = 4">Snap: 4cm</MkButton>
<MkButton v-if="engine.enableGridSnapping.value" :primary="engine.gridSnappingScale.value === 8" @click="engine.gridSnappingScale.value = 8">Snap: 8cm</MkButton>
<MkButton :primary="engine.isEditMode.value" @click="toggleEditMode">Edit mode: {{ engine.isEditMode.value ? 'on' : 'off' }}</MkButton>
<template v-if="engine.isEditMode.value">
<MkButton @click="grab">Grab</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 === 2" @click="engine.gridSnappingScale.value = 2">Snap: 2cm</MkButton>
<MkButton v-if="engine.enableGridSnapping.value" :primary="engine.gridSnappingScale.value === 4" @click="engine.gridSnappingScale.value = 4">Snap: 4cm</MkButton>
<MkButton v-if="engine.enableGridSnapping.value" :primary="engine.gridSnappingScale.value === 8" @click="engine.gridSnappingScale.value = 8">Snap: 8cm</MkButton>
</template>
</div>
</div>
</template>
@@ -224,6 +227,11 @@ function toggleGridSnapping() {
canvas.value!.focus();
}
function toggleEditMode() {
engine.value.isEditMode.value = !engine.value.isEditMode.value;
canvas.value!.focus();
}
definePage(() => ({
title: 'Room',
icon: 'ti ti-door',

View File

@@ -45,6 +45,7 @@ type ObjectDef = {
placement: 'top' | 'side' | 'bottom' | 'wall' | 'ceiling' | 'floor';
receiveShadows?: boolean;
castShadows?: boolean;
isChair?: boolean;
onInit?: (room: RoomEngine, o: RoomDef['objects'][0], rootNode: BABYLON.TransformNode) => void;
};
@@ -262,6 +263,7 @@ const OBJECTS = {
},
'chair': {
placement: 'floor',
isChair: true,
},
'energy-drink': {
placement: 'top',
@@ -302,12 +304,6 @@ const OBJECTS = {
},
} as Record<string, ObjectDef>;
function vecToLocal(vector: BABYLON.Vector3, mesh: BABYLON.Mesh): BABYLON.Vector3 {
const m = mesh.getWorldMatrix();
const v = BABYLON.Vector3.TransformCoordinates(vector, m);
return v;
}
const _assumedFramesPerSecond = 60;
class HorizontalCameraKeyboardMoveInput extends BABYLON.BaseCameraPointersInput {
@@ -323,6 +319,7 @@ class HorizontalCameraKeyboardMoveInput extends BABYLON.BaseCameraPointersInput
codesRight = ['KeyD'];
onCanvasBlurObserver = null;
onKeyboardObserver = null;
public canMove = true;
constructor(camera: BABYLON.UniversalCamera) {
super();
@@ -408,7 +405,10 @@ class HorizontalCameraKeyboardMoveInput extends BABYLON.BaseCameraPointersInput
dir.normalize();
const rate = this.preShift ? 3 : 1;
const move = dir.scale(this.moveSpeed * rate);
this.camera.cameraDirection.addInPlace(move);
if (this.canMove) {
this.camera.cameraDirection.addInPlace(move);
}
}
}
@@ -428,7 +428,8 @@ export class RoomEngine {
private shadowGenerator1: BABYLON.ShadowGenerator;
private shadowGenerator2: BABYLON.ShadowGenerator;
private camera: BABYLON.UniversalCamera;
private camera2: BABYLON.ArcRotateCamera;
private fixedCamera: BABYLON.UniversalCamera;
private birdeyeCamera: BABYLON.ArcRotateCamera;
private intervalIds: number[] = [];
private timeoutIds: number[] = [];
private objectMeshs: Map<string, BABYLON.TransformNode> = new Map();
@@ -458,6 +459,8 @@ export class RoomEngine {
private xGridPreviewPlane: BABYLON.Mesh;
private yGridPreviewPlane: BABYLON.Mesh;
private zGridPreviewPlane: BABYLON.Mesh;
public isEditMode = ref(false);
public isSitting = ref(false);
constructor(def: RoomDef, options: {
canvas: HTMLCanvasElement;
@@ -520,15 +523,22 @@ export class RoomEngine {
this.camera.applyGravity = true;
this.camera.needMoveForGravity = true;
this.camera2 = new BABYLON.ArcRotateCamera('camera2', -Math.PI / 2, Math.PI / 2.5, 300/*cm*/, new BABYLON.Vector3(0, 90/*cm*/, 0), this.scene);
this.camera2.attachControl(this.canvas);
this.camera2.minZ = 1/*cm*/;
this.camera2.maxZ = 100000/*cm*/;
this.camera2.fov = 0.5;
this.camera2.lowerBetaLimit = 0;
this.camera2.upperBetaLimit = (Math.PI / 2) + 0.1;
this.camera2.lowerRadiusLimit = 50/*cm*/;
this.camera2.upperRadiusLimit = 1000/*cm*/;
this.fixedCamera = new BABYLON.UniversalCamera('fixedCamera', new BABYLON.Vector3(0, 0, 0), this.scene);
this.fixedCamera.minZ = 1/*cm*/;
this.fixedCamera.maxZ = 100000/*cm*/;
this.fixedCamera.fov = 1;
this.fixedCamera.inputs.removeByType('FreeCameraKeyboardMoveInput');
this.fixedCamera.attachControl(this.canvas);
this.birdeyeCamera = new BABYLON.ArcRotateCamera('birdeyeCamera', -Math.PI / 2, Math.PI / 2.5, 300/*cm*/, new BABYLON.Vector3(0, 90/*cm*/, 0), this.scene);
this.birdeyeCamera.attachControl(this.canvas);
this.birdeyeCamera.minZ = 1/*cm*/;
this.birdeyeCamera.maxZ = 100000/*cm*/;
this.birdeyeCamera.fov = 0.5;
this.birdeyeCamera.lowerBetaLimit = 0;
this.birdeyeCamera.upperBetaLimit = (Math.PI / 2) + 0.1;
this.birdeyeCamera.lowerRadiusLimit = 50/*cm*/;
this.birdeyeCamera.upperRadiusLimit = 1000/*cm*/;
this.scene.activeCamera = this.camera;
@@ -570,9 +580,10 @@ export class RoomEngine {
gl.intensity = 0.5;
{
const postProcess = new BABYLON.ImageProcessingPostProcess('processing', 1.0, this.camera);
postProcess.exposure = 1.1;
postProcess.contrast = 0.9;
//const postProcess = new BABYLON.ImageProcessingPostProcess('processing', 1.0, this.camera);
//postProcess.exposure = 1.1;
//postProcess.contrast = 0.9;
//const curve = new BABYLON.ColorCurves();
//curve.highlightsHue = 40;
//curve.highlightsDensity = 50;
@@ -583,7 +594,7 @@ export class RoomEngine {
//postProcess.colorCurvesEnabled = true;
//postProcess.colorCurves = curve;
//const postProcess2 = new BABYLON.ImageProcessingPostProcess('processing2', 1.0, this.camera2);
//const postProcess2 = new BABYLON.ImageProcessingPostProcess('processing2', 1.0, this.birdeyeCamera);
//postProcess2.exposure = 2;
//postProcess2.contrast = 0.9;
@@ -698,7 +709,11 @@ export class RoomEngine {
if (ev.code === 'KeyE') {
ev.preventDefault();
ev.stopPropagation();
this.toggleGrab();
if (this.isEditMode.value) {
this.toggleGrab();
} else {
this.interact();
}
} else if (ev.code === 'KeyR') {
ev.preventDefault();
ev.stopPropagation();
@@ -1229,6 +1244,28 @@ export class RoomEngine {
});
}
private interact() {
if (this.selectedObjectId == null) return;
const o = this.def.objects.find(o => o.id === this.selectedObjectId)!;
const mesh = this.objectMeshs.get(o.id)!;
const objDef = OBJECTS[o.type];
if (objDef.isChair) {
if (this.isSitting.value) {
this.isSitting.value = false;
this.scene.activeCamera = this.camera;
this.fixedCamera.parent = null;
} else {
this.isSitting.value = true;
this.fixedCamera.parent = this.objectMeshs.get(o.id);
this.fixedCamera.position = new BABYLON.Vector3(0, 120/*cm*/, 0);
this.fixedCamera.rotation = new BABYLON.Vector3(0, 0, 0);
this.scene.activeCamera = this.fixedCamera;
}
}
}
private turnOnRoomLight() {
this.roomLight.intensity = 300000;
this.envMapIndoor.level = 0.3;