1
0
mirror of https://github.com/misskey-dev/misskey.git synced 2026-05-13 16:25:44 +02:00
This commit is contained in:
syuilo
2026-05-09 13:03:57 +09:00
parent 17333fd7e5
commit f3a7f10319
3 changed files with 81 additions and 50 deletions

View File

@@ -106,9 +106,7 @@ const recentlyUsedDefs = computed(() => {
onMounted(async () => {
engine.value = await createRoomObjectPreviewEngine(canvas.value!);
engine.value.init();
canvas.value!.focus();
await engine.value.init();
});
onUnmounted(() => {
@@ -120,14 +118,21 @@ watch(selectedId, (newId) => {
if (newId == null) {
engine.value!.clear();
engine.value!.pauseRender();
selectedInstanceId.value = null;
selectedObjectOptionsState.value = null;
} else {
const closeWaiting = os.waiting();
nextTick(() => {
engine.value!.load(newId).then(res => {
selectedInstanceId.value = res.id;
selectedObjectOptionsState.value = deepClone(res.options);
engine.value!.resize();
engine.value!.resumeRender();
closeWaiting();
}).catch(err => {
console.error(err);
closeWaiting();
});
});
}

View File

@@ -6,7 +6,7 @@
import * as BABYLON from '@babylonjs/core';
import { registerBuiltInLoaders } from '@babylonjs/loaders/dynamic.js';
import { GridMaterial } from '@babylonjs/materials';
import { camelToKebab, WORLD_SCALE, cm, getMeshesBoundingBox, Timer } from '../utility.js';
import { camelToKebab, WORLD_SCALE, cm, getMeshesBoundingBox, Timer, sleep } from '../utility.js';
import { getObjectDef } from './object-defs.js';
import { SYSTEM_MESH_NAMES, ModelManager, GRAPHICS_QUALITY } from './utility.js';
import type { RoomObjectInstance } from './object.js';
@@ -143,7 +143,9 @@ export class RoomObjectPreviewEngine {
}
}
public async init() {
private currentRafId: number | null = null;
private startRenderLoop() {
if (this.fps == null) {
this.engine.runRenderLoop(() => {
this.scene.render();
@@ -155,7 +157,8 @@ export class RoomObjectPreviewEngine {
const renderLoop = (timeStamp: number) => {
if (this.disposed) return;
window.requestAnimationFrame(renderLoop);
// workerで実行される可能性がある
this.currentRafId = requestAnimationFrame(renderLoop);
const delta = timeStamp - then;
if (delta <= interval) return;
@@ -166,10 +169,28 @@ export class RoomObjectPreviewEngine {
this.engine.endFrame();
};
window.requestAnimationFrame(renderLoop);
// workerで実行される可能性がある
this.currentRafId = requestAnimationFrame(renderLoop);
}
}
public pauseRender() { // TODO: srと同じく参照カウント方式にした方が便利そう
this.engine.stopRenderLoop();
if (this.currentRafId != null) {
// workerで実行される可能性がある
cancelAnimationFrame(this.currentRafId);
this.currentRafId = null;
}
}
public resumeRender() {
this.startRenderLoop();
}
public async init() {
// 特になし
}
public async load(type: string) {
this.clear();
@@ -188,53 +209,51 @@ export class RoomObjectPreviewEngine {
id,
});
// なぜかちょっと待たないとbounding boxのサイズが正しくない
window.setTimeout(() => {
const boundingInfo = getMeshesBoundingBox(this.objectMesh!.getChildMeshes().filter(m => m.isEnabled() && m.isVisible));
const boundingInfo = getMeshesBoundingBox(this.objectMesh!.getChildMeshes().filter(m => m.isEnabled() && m.isVisible), true);
this.pipeline.removeCamera(this.camera);
this.camera.dispose();
this.pipeline.removeCamera(this.camera);
this.camera.dispose();
this.camera = new BABYLON.ArcRotateCamera('camera', Math.PI / 2, Math.PI / 2.5, cm(300), new BABYLON.Vector3(0, cm(90), 0), this.scene);
this.camera.attachControl(this.canvas);
this.camera.minZ = cm(1);
this.camera.maxZ = cm(100000);
this.camera.fov = 0.5;
this.camera.lowerRadiusLimit = cm(50);
this.camera.upperRadiusLimit = cm(1000);
this.camera.useAutoRotationBehavior = true;
this.camera.autoRotationBehavior!.idleRotationSpeed = 0.3;
this.camera.panningSensibility = 0;
this.camera.wheelDeltaPercentage = 0.01;
//this.camera.mode = BABYLON.Camera.ORTHOGRAPHIC_CAMERA;
this.camera.setTarget(new BABYLON.Vector3(0, boundingInfo.center.y, 0));
this.camera = new BABYLON.ArcRotateCamera('camera', Math.PI / 2, Math.PI / 2.5, cm(300), new BABYLON.Vector3(0, cm(90), 0), this.scene);
this.camera.attachControl(this.canvas);
this.camera.minZ = cm(1);
this.camera.maxZ = cm(100000);
this.camera.fov = 0.5;
this.camera.lowerRadiusLimit = cm(50);
this.camera.upperRadiusLimit = cm(1000);
this.camera.useAutoRotationBehavior = true;
this.camera.autoRotationBehavior!.idleRotationSpeed = 0.3;
this.camera.panningSensibility = 0;
this.camera.wheelDeltaPercentage = 0.01;
//this.camera.mode = BABYLON.Camera.ORTHOGRAPHIC_CAMERA;
this.camera.setTarget(new BABYLON.Vector3(0, boundingInfo.centerWorld.y, 0));
console.log(boundingInfo.centerWorld.y);
if (def.placement === 'wall' || def.placement === 'side') {
this.camera.lowerBetaLimit = 0;
this.camera.upperBetaLimit = Math.PI;
this.zGridPreviewPlane.rotation = new BABYLON.Vector3(0, Math.PI, 0);
} else if (def.placement === 'ceiling' || def.placement === 'bottom') {
this.camera.lowerBetaLimit = (Math.PI / 2) - 0.1;
this.camera.upperBetaLimit = Math.PI;
this.camera.beta = Math.PI / 1.75;
this.zGridPreviewPlane.rotation = new BABYLON.Vector3(-Math.PI / 2, 0, 0);
} else {
this.camera.lowerBetaLimit = 0;
this.camera.upperBetaLimit = (Math.PI / 2) + 0.1;
this.zGridPreviewPlane.rotation = new BABYLON.Vector3(Math.PI / 2, 0, 0);
}
if (def.placement === 'wall' || def.placement === 'side') {
this.camera.lowerBetaLimit = 0;
this.camera.upperBetaLimit = Math.PI;
this.zGridPreviewPlane.rotation = new BABYLON.Vector3(0, Math.PI, 0);
} else if (def.placement === 'ceiling' || def.placement === 'bottom') {
this.camera.lowerBetaLimit = (Math.PI / 2) - 0.1;
this.camera.upperBetaLimit = Math.PI;
this.camera.beta = Math.PI / 1.75;
this.zGridPreviewPlane.rotation = new BABYLON.Vector3(-Math.PI / 2, 0, 0);
} else {
this.camera.lowerBetaLimit = 0;
this.camera.upperBetaLimit = (Math.PI / 2) + 0.1;
this.zGridPreviewPlane.rotation = new BABYLON.Vector3(Math.PI / 2, 0, 0);
}
// zoom to fit
const size = boundingInfo.extendSize;
const distance = Math.max(size.x, size.y, size.z) * 2;
this.camera.radius = distance * 3;
//this.camera.orthoLeft = -distance;
//this.camera.orthoRight = distance;
//this.camera.orthoTop = distance;
//this.camera.orthoBottom = -distance;
// zoom to fit
const size = boundingInfo.extendSize;
const distance = Math.max(size.x, size.y, size.z) * 2;
this.camera.radius = distance * 3;
//this.camera.orthoLeft = -distance;
//this.camera.orthoRight = distance;
//this.camera.orthoTop = distance;
//this.camera.orthoBottom = -distance;
this.pipeline.addCamera(this.camera);
}, 10);
this.pipeline.addCamera(this.camera);
return {
id,
@@ -348,6 +367,12 @@ export class RoomObjectPreviewEngine {
}
public destroy() {
this.engine.stopRenderLoop();
if (this.currentRafId != null) {
// workerで実行される可能性がある
cancelAnimationFrame(this.currentRafId);
this.currentRafId = null;
}
if (this.timerForEachObject != null) {
this.timerForEachObject.dispose();
}

View File

@@ -271,11 +271,12 @@ export const camelToKebab = (s: string) => {
};
// この実装方法だとマイナスの座標をうまく処理できず結果がおかしくなるので応急処置で全体を+10000cmオフセットしてから計算している
export function getMeshesBoundingBox(meshes: BABYLON.Mesh[]): BABYLON.BoundingBox {
export function getMeshesBoundingBox(meshes: BABYLON.Mesh[], forceComputeWorldMatrix = false): BABYLON.BoundingBox {
let min = new BABYLON.Vector3(Number.MAX_VALUE, Number.MAX_VALUE, Number.MAX_VALUE);
let max = new BABYLON.Vector3(Number.MIN_VALUE, Number.MIN_VALUE, Number.MIN_VALUE);
for (const mesh of meshes) {
if (forceComputeWorldMatrix) mesh.computeWorldMatrix(true);
const boundingInfo = mesh.getBoundingInfo();
min = BABYLON.Vector3.Minimize(min, boundingInfo.boundingBox.minimumWorld.add(new BABYLON.Vector3(10000, 10000, 10000)));
max = BABYLON.Vector3.Maximize(max, boundingInfo.boundingBox.maximumWorld.add(new BABYLON.Vector3(10000, 10000, 10000)));