1
0
mirror of https://github.com/misskey-dev/misskey.git synced 2026-05-13 15:15:45 +02:00
This commit is contained in:
syuilo
2026-04-07 15:43:06 +09:00
parent d51d1191c5
commit afd731797e
7 changed files with 181 additions and 12 deletions

View File

@@ -17,12 +17,15 @@
* - 後からモデルを調整したくなった時に備え、モディファイアを駆使するなどして、なるべく非破壊的なモデリングを心がけることを推奨します。
* - モディファイアをapplyしないとならないシチュエーションでは、apply前の状態を複製して(非表示にした上で)残すことを推奨します。
* - 上記の非破壊的なモデリングの原則に反しない限り、なるべくscaleはapplyした状態で(=scaleが1, 1, 1の状態で)エクスポートすること。そうしないとbake前後で法線が変わるのかレンダリング結果が異なる現象が発生することがあります。
* - 現在のMisskey RoomのGLB root除去システムの実装上、スケールに1以外の値を持つメッシュにシェイプキーを設定することはサポートされていません。
*/
// TODO: 家具設置時のコリジョン判定(めりこんで設置されないようにする)
// TODO: 近くのオブジェクトの端にスナップオプション
// TODO: 近くのオブジェクトの原点に軸を揃えるオプション
const BAKE_TRANSFORM = false; // 実験的
import * as BABYLON from '@babylonjs/core';
import { AxesViewer } from '@babylonjs/core/Debug/axesViewer';
import { registerBuiltInLoaders } from '@babylonjs/loaders/dynamic';
@@ -33,7 +36,7 @@ import { reactive, ref, shallowRef, triggerRef, watch } from 'vue';
import { genId } from '../id.js';
import { deepClone } from '../clone.js';
import { getObjectDef } from './object-defs.js';
import { HorizontalCameraKeyboardMoveInput, applyMorphTargetsToMesh, camelToKebab, findMaterial } from './utility.js';
import { HorizontalCameraKeyboardMoveInput, applyMorphTargetsToMesh, camelToKebab, findMaterial, scaleMorph } from './utility.js';
import * as sound from '@/utility/sound.js';
// babylonのドメイン知識は持たない
@@ -1044,8 +1047,136 @@ export class RoomEngine {
const loaderResult = await BABYLON.LoadAssetContainerAsync(`/client-assets/room/objects/${camelToKebab(args.type)}/${camelToKebab(args.type)}.glb`, this.scene);
// babylonによって自動で追加される右手系変換用ード
const subRoot = loaderResult.meshes[0];
subRoot.scaling = subRoot.scaling.scale(WORLD_SCALE);// cmをmに
const subRoot = loaderResult.meshes[0] as BABYLON.Mesh;
if (BAKE_TRANSFORM) {
subRoot.scaling = new BABYLON.Vector3(1, 1, 1);
subRoot.rotationQuaternion = null;
subRoot.rotation = new BABYLON.Vector3(0, 0, 0);
//subRoot.scaling = subRoot.scaling.scale(WORLD_SCALE);// cmをmに
//subRoot.bakeCurrentTransformIntoVertices();
//subRoot.bakeTransformIntoVertices(BABYLON.Matrix.Scaling(WORLD_SCALE, WORLD_SCALE, WORLD_SCALE));
for (const m of loaderResult.transformNodes) {
if (m.name === '__root__') continue;
if (m.parent === subRoot) {
m.setParent(root);
//m.parent = root;
}
}
for (const m of loaderResult.meshes) {
if (m.name === '__root__') continue;
if (m.parent === subRoot) {
m.setParent(root);
//m.parent = root;
}
}
const bakeTransformNode = (m: BABYLON.TransformNode) => {
m.position.x *= -WORLD_SCALE;
m.position.y *= WORLD_SCALE;
m.position.z *= WORLD_SCALE;
m.rotation = m.rotationQuaternion.toEulerAngles();
m.rotationQuaternion = null;
//m.rotation.x = -m.rotation.x;
m.rotation.y = -m.rotation.y;
m.rotation.z = -m.rotation.z;
for (const child of m.getChildren()) {
if (child instanceof BABYLON.Mesh) {
//child.scaling = child.scaling.scale(WORLD_SCALE);// cmをmに
//child.position = child.position.scale(WORLD_SCALE);
const pos = child.position.clone();
const scaling = child.scaling.clone();
child.scaling.x = -WORLD_SCALE;
child.scaling.y = WORLD_SCALE;
child.scaling.z = WORLD_SCALE;
const rotation = child.rotationQuaternion ? child.rotationQuaternion.toEulerAngles() : child.rotation.clone();
child.rotationQuaternion = null;
child.position = new BABYLON.Vector3(0, 0, 0);
child.parent = root;
child.bakeCurrentTransformIntoVertices();
child.parent = m;
child.scaling = scaling;
child.position.x = pos.x * -WORLD_SCALE;
child.position.y = pos.y * WORLD_SCALE;
child.position.z = pos.z * WORLD_SCALE;
child.rotation = rotation;
scaleMorph(child, [-WORLD_SCALE, WORLD_SCALE, WORLD_SCALE]);
//const indices = child.getIndices();
//const positions = child.getVerticesData(BABYLON.VertexBuffer.PositionKind);
//const normals = child.getVerticesData(BABYLON.VertexBuffer.NormalKind);
//BABYLON.VertexData.ComputeNormals(positions, indices, normals);
//child.updateVerticesData(BABYLON.VertexBuffer.NormalKind, normals, false, false);
} else if (child instanceof BABYLON.InstancedMesh) {
const pos = child.position.clone();
child.position.x = pos.x * -WORLD_SCALE;
child.position.y = pos.y * WORLD_SCALE;
child.position.z = pos.z * WORLD_SCALE;
} else if (child instanceof BABYLON.TransformNode) {
bakeTransformNode(child);
}
}
};
const bakeChildren = (node: BABYLON.Node) => {
for (const m of node.getChildren(undefined, true)) {
if (m instanceof BABYLON.Mesh) {
const scaling = m.scaling.clone();
m.scaling.x = -WORLD_SCALE;
m.scaling.y = WORLD_SCALE;
m.scaling.z = WORLD_SCALE;
//m.position.x *= -WORLD_SCALE;
//m.position.y *= WORLD_SCALE;
//m.position.z *= WORLD_SCALE;
const pos = m.position.clone();
const rotation = m.rotationQuaternion.toEulerAngles();
m.rotationQuaternion = null;
m.rotation = new BABYLON.Vector3(0, 0, 0);
m.position = new BABYLON.Vector3(0, 0, 0);
m.bakeCurrentTransformIntoVertices();
m.scaling.x = scaling.x;
m.scaling.y = scaling.y;
m.scaling.z = scaling.z;
m.position.x = pos.x * -WORLD_SCALE;
m.position.y = pos.y * WORLD_SCALE;
m.position.z = pos.z * WORLD_SCALE;
m.rotation = rotation;
//m.rotation.x = -m.rotation.x;
m.rotation.y = -m.rotation.y;
m.rotation.z = -m.rotation.z;
scaleMorph(m, [-WORLD_SCALE, WORLD_SCALE, WORLD_SCALE]);
} else if (m instanceof BABYLON.InstancedMesh) {
//const pos = m.position.clone();
//m.position.x = pos.x * -WORLD_SCALE;
//m.position.y = pos.y * WORLD_SCALE;
//m.position.z = pos.z * WORLD_SCALE;
m.position.x *= -WORLD_SCALE;
m.position.y *= WORLD_SCALE;
m.position.z *= WORLD_SCALE;
m.rotation = m.rotationQuaternion.toEulerAngles();
m.rotationQuaternion = null;
m.rotation.x = -m.rotation.x;
m.rotation.y = -m.rotation.y;
m.rotation.z = -m.rotation.z;
} else if (m instanceof BABYLON.TransformNode) {
bakeTransformNode(m);
}
}
};
bakeChildren(root);
subRoot.dispose();
} else {
subRoot.scaling = subRoot.scaling.scale(WORLD_SCALE);// cmをmに
}
def.treatLoaderResult?.(loaderResult);
@@ -1055,13 +1186,15 @@ export class RoomEngine {
objectType: args.type,
};
root.addChild(subRoot);
if (!BAKE_TRANSFORM) {
root.addChild(subRoot);
}
root.position = args.position.clone();
root.rotation = args.rotation.clone();
root.metadata = metadata;
const model = new ModelManager(subRoot, loaderResult.meshes.filter(m => m !== subRoot), (meshes) => {
const model = new ModelManager(BAKE_TRANSFORM ? root : subRoot, loaderResult.meshes.filter(m => m.name !== subRoot), (meshes) => {
if (this.selected.value?.objectId === args.id) {
this.highlightMeshes(meshes);
}

View File

@@ -4,7 +4,7 @@
*/
import * as BABYLON from '@babylonjs/core';
import { defineObject, WORLD_SCALE } from '../engine.js';
import { defineObject } from '../engine.js';
import { createPlaneUvMapper } from '../utility.js';
export const allInOnePc = defineObject({
@@ -47,7 +47,11 @@ export const allInOnePc = defineObject({
},
placement: 'top',
createInstance: async ({ room, scene, options, model }) => {
const light = new BABYLON.SpotLight('', new BABYLON.Vector3(0/*cm*/, 30/*cm*/ / WORLD_SCALE, 0), new BABYLON.Vector3(0, 0, 1), Math.PI / 1, 2, scene, room?.lightContainer != null);
const matrix = model.root.getWorldMatrix(true);
const scale = new BABYLON.Vector3();
matrix.decompose(scale);
const light = new BABYLON.SpotLight('', new BABYLON.Vector3(0/*cm*/, 30/*cm*/ / Math.abs(scale.y), 0), new BABYLON.Vector3(0, 0, 1), Math.PI / 1, 2, scene, room?.lightContainer != null);
light.parent = model.root;
light.diffuse = new BABYLON.Color3(1.0, 1.0, 1.0);
light.range = 100/*cm*/;

View File

@@ -4,7 +4,7 @@
*/
import * as BABYLON from '@babylonjs/core';
import { defineObject, WORLD_SCALE } from '../engine.js';
import { defineObject } from '../engine.js';
import { createOverridedStates } from '../utility.js';
export const blind = defineObject({
@@ -57,12 +57,16 @@ export const blind = defineObject({
}
blades = [];
const matrix = blade.parent.getWorldMatrix(true);
const scale = new BABYLON.Vector3();
matrix.decompose(scale);
for (let i = 0; i < options.blades; i++) {
const b = blade.clone('blade_' + i); // createInstanceを使いたいが、削除するときになぜかエラーになる
if (i / options.blades < temp.open) {
b.position.y -= (i * 4/*cm*/) / WORLD_SCALE;
b.position.y -= (i * 4/*cm*/) / Math.abs(scale.y);
} else {
b.position.y -= (((options.blades - 1) * temp.open * 4/*cm*/) + (i * 0.3/*cm*/)) / WORLD_SCALE;
b.position.y -= (((options.blades - 1) * temp.open * 4/*cm*/) + (i * 0.3/*cm*/)) / Math.abs(scale.y);
}
blades.push(b);
}

View File

@@ -4,7 +4,7 @@
*/
import * as BABYLON from '@babylonjs/core';
import { defineObject, WORLD_SCALE } from '../engine.js';
import { defineObject } from '../engine.js';
import { createPlaneUvMapper } from '../utility.js';
export const laptopPc = defineObject({
@@ -55,10 +55,14 @@ export const laptopPc = defineObject({
},
placement: 'top',
createInstance: async ({ room, scene, options, model }) => {
const matrix = model.root.getWorldMatrix(true);
const scale = new BABYLON.Vector3();
matrix.decompose(scale);
const screenMesh = model.findMesh('__X_SCREEN__');
const hutaNode = model.findTransformNode('__X_HUTA__');
const light = new BABYLON.SpotLight('', new BABYLON.Vector3(0/*cm*/, 10/*cm*/ / WORLD_SCALE, 0), new BABYLON.Vector3(0, 0, 1), Math.PI / 1, 2, scene, room?.lightContainer != null);
const light = new BABYLON.SpotLight('', new BABYLON.Vector3(0/*cm*/, 10/*cm*/ / Math.abs(scale.y), 0), new BABYLON.Vector3(0, 0, 1), Math.PI / 1, 2, scene, room?.lightContainer != null);
light.parent = hutaNode;
light.diffuse = new BABYLON.Color3(1.0, 1.0, 1.0);
light.range = 100/*cm*/;

View File

@@ -491,6 +491,30 @@ export function findMaterial(rootMesh: BABYLON.AbstractMesh, keyword: string): B
throw new Error(`Material with keyword "${keyword}" not found`);
}
export function scaleMorph(mesh: BABYLON.Mesh, scale: [number, number, number], offset: [number, number, number] = [0, 0, 0]) {
if (!mesh.morphTargetManager) {
return;
}
const morphTargetManager = mesh.morphTargetManager;
for (let targetIndex = 0; targetIndex < morphTargetManager.numTargets; targetIndex++) {
const target = morphTargetManager.getTarget(targetIndex);
const newPos = target.getPositions();
for (let i = 0; i < newPos.length; i += 3) {
newPos[i] = (newPos[i] + offset[0]) * scale[0];
newPos[i + 1] = (newPos[i + 1] + offset[1]) * scale[1];
newPos[i + 2] = (newPos[i + 2] + offset[2]) * scale[2];
}
target.setPositions(newPos);
}
morphTargetManager.synchronize();
mesh.refreshBoundingInfo();
mesh.computeWorldMatrix(true);
}
export function applyMorphTargetsToMesh(mesh: BABYLON.Mesh) {
if (!mesh.morphTargetManager) {
return;