mirror of
https://github.com/misskey-dev/misskey.git
synced 2026-05-27 17:44:37 +02:00
wip
This commit is contained in:
@@ -148,12 +148,14 @@ type GetOptionsSchemaValues<T extends OptionsSchema> = {
|
|||||||
|
|
||||||
class ModelManager {
|
class ModelManager {
|
||||||
public root: BABYLON.Mesh;
|
public root: BABYLON.Mesh;
|
||||||
public updatedCallback: (() => void) | null = null;
|
public bakedCallback: (() => void) | null = null;
|
||||||
|
private originalMeshes: BABYLON.Mesh[] = [];
|
||||||
private bakedMeshes: BABYLON.Mesh[] = [];
|
private bakedMeshes: BABYLON.Mesh[] = [];
|
||||||
|
|
||||||
constructor(root: BABYLON.Mesh, updatedCallback: (() => void) | null = null) {
|
constructor(root: BABYLON.Mesh, originalMeshes: BABYLON.Mesh[], bakedCallback: (() => void) | null = null) {
|
||||||
this.root = root;
|
this.root = root;
|
||||||
this.updatedCallback = updatedCallback;
|
this.originalMeshes = originalMeshes;
|
||||||
|
this.bakedCallback = bakedCallback;
|
||||||
}
|
}
|
||||||
|
|
||||||
public findMesh(keyword: string) {
|
public findMesh(keyword: string) {
|
||||||
@@ -183,7 +185,6 @@ class ModelManager {
|
|||||||
|
|
||||||
public updated() {
|
public updated() {
|
||||||
this.bakeMesh();
|
this.bakeMesh();
|
||||||
this.updatedCallback?.(this.bakedMeshes.length > 0 ? this.bakedMeshes : this.root.getChildMeshes());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public bakeMesh() {
|
public bakeMesh() {
|
||||||
@@ -195,10 +196,6 @@ class ModelManager {
|
|||||||
|
|
||||||
const childMeshes = this.root.getChildMeshes().filter(m => !m.name.includes('__TOP__') && !m.name.includes('__SIDE__') && !m.name.includes('__COLLISION__'));
|
const childMeshes = this.root.getChildMeshes().filter(m => !m.name.includes('__TOP__') && !m.name.includes('__SIDE__') && !m.name.includes('__COLLISION__'));
|
||||||
|
|
||||||
if (childMeshes.length <= 1) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const _toMerge = [] as BABYLON.Mesh[];
|
const _toMerge = [] as BABYLON.Mesh[];
|
||||||
for (const mesh of childMeshes) {
|
for (const mesh of childMeshes) {
|
||||||
let fixedMesh = mesh;
|
let fixedMesh = mesh;
|
||||||
@@ -248,6 +245,8 @@ class ModelManager {
|
|||||||
merged.parent = this.root;
|
merged.parent = this.root;
|
||||||
|
|
||||||
this.bakedMeshes = [merged];
|
this.bakedMeshes = [merged];
|
||||||
|
|
||||||
|
this.bakedCallback?.(this.bakedMeshes);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('Failed to bake mesh for object', this.root.metadata?.objectType, err);
|
console.error('Failed to bake mesh for object', this.root.metadata?.objectType, err);
|
||||||
}
|
}
|
||||||
@@ -263,8 +262,8 @@ type ObjectDef<OpSc extends OptionsSchema = OptionsSchema> = {
|
|||||||
};
|
};
|
||||||
placement: 'top' | 'side' | 'bottom' | 'wall' | 'ceiling' | 'floor';
|
placement: 'top' | 'side' | 'bottom' | 'wall' | 'ceiling' | 'floor';
|
||||||
//groupingMeshes: string[]; // multi-materialなメッシュは複数のメッシュに分割されるが、それだと不便な場合に追加の親メッシュでグルーピングするための指定
|
//groupingMeshes: string[]; // multi-materialなメッシュは複数のメッシュに分割されるが、それだと不便な場合に追加の親メッシュでグルーピングするための指定
|
||||||
mergeMeshes?: string[] | null; // multi-materialなメッシュは複数のメッシュに分割されるが、それだと不便な場合にメッシュをマージするための指定
|
|
||||||
isChair?: boolean;
|
isChair?: boolean;
|
||||||
|
treatLoaderResult?: (loaderResult: BABYLON.AssetContainer) => void;
|
||||||
createInstance: (args: {
|
createInstance: (args: {
|
||||||
room?: RoomEngine | null;
|
room?: RoomEngine | null;
|
||||||
scene: BABYLON.Scene;
|
scene: BABYLON.Scene;
|
||||||
@@ -320,7 +319,6 @@ const TIME_MAP = {
|
|||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
const USE_GLOW = false; // ドローコールが増えて重い
|
const USE_GLOW = false; // ドローコールが増えて重い
|
||||||
const ENABLE_SUN_LIGHT = true; // ドローコールが増えて重い
|
|
||||||
|
|
||||||
export async function createRoomEngine(roomState: RoomState, canvas: HTMLCanvasElement) {
|
export async function createRoomEngine(roomState: RoomState, canvas: HTMLCanvasElement) {
|
||||||
const babylonEngine = new BABYLON.WebGPUEngine(canvas);
|
const babylonEngine = new BABYLON.WebGPUEngine(canvas);
|
||||||
@@ -335,7 +333,7 @@ export class RoomEngine {
|
|||||||
private engine: BABYLON.WebGPUEngine;
|
private engine: BABYLON.WebGPUEngine;
|
||||||
public scene: BABYLON.Scene;
|
public scene: BABYLON.Scene;
|
||||||
private shadowGeneratorForRoomLight: BABYLON.ShadowGenerator;
|
private shadowGeneratorForRoomLight: BABYLON.ShadowGenerator;
|
||||||
private shadowGeneratorForSunLight: BABYLON.ShadowGenerator | null = null;
|
private shadowGeneratorForSunLight: BABYLON.ShadowGenerator;
|
||||||
public camera: BABYLON.UniversalCamera;
|
public camera: BABYLON.UniversalCamera;
|
||||||
private fixedCamera: BABYLON.UniversalCamera;
|
private fixedCamera: BABYLON.UniversalCamera;
|
||||||
private birdeyeCamera: BABYLON.ArcRotateCamera;
|
private birdeyeCamera: BABYLON.ArcRotateCamera;
|
||||||
@@ -496,21 +494,19 @@ export class RoomEngine {
|
|||||||
this.shadowGeneratorForRoomLight.getShadowMap().refreshRate = 60;
|
this.shadowGeneratorForRoomLight.getShadowMap().refreshRate = 60;
|
||||||
//this.shadowGenerator1.useContactHardeningShadow = true;
|
//this.shadowGenerator1.useContactHardeningShadow = true;
|
||||||
|
|
||||||
if (ENABLE_SUN_LIGHT) {
|
const sunLight = new BABYLON.DirectionalLight('sunLight', new BABYLON.Vector3(0.2, -1, -1), this.scene);
|
||||||
const sunLight = new BABYLON.DirectionalLight('sunLight', new BABYLON.Vector3(0.2, -1, -1), this.scene);
|
sunLight.position = new BABYLON.Vector3(-20, 1000, 1000);
|
||||||
sunLight.position = new BABYLON.Vector3(-20, 1000, 1000);
|
sunLight.diffuse = this.time === 0 ? new BABYLON.Color3(1.0, 0.9, 0.8) : this.time === 1 ? new BABYLON.Color3(1.0, 0.8, 0.6) : new BABYLON.Color3(0.6, 0.8, 1.0);
|
||||||
sunLight.diffuse = this.time === 0 ? new BABYLON.Color3(1.0, 0.9, 0.8) : this.time === 1 ? new BABYLON.Color3(1.0, 0.8, 0.6) : new BABYLON.Color3(0.6, 0.8, 1.0);
|
sunLight.intensity = this.time === 0 ? 2 : this.time === 1 ? 1 : 0.25;
|
||||||
sunLight.intensity = this.time === 0 ? 2 : this.time === 1 ? 1 : 0.25;
|
sunLight.shadowMinZ = 1000/*cm*/;
|
||||||
sunLight.shadowMinZ = 1000/*cm*/;
|
sunLight.shadowMaxZ = 2000/*cm*/;
|
||||||
sunLight.shadowMaxZ = 2000/*cm*/;
|
|
||||||
|
|
||||||
this.shadowGeneratorForSunLight = new BABYLON.ShadowGenerator(4096, sunLight);
|
this.shadowGeneratorForSunLight = new BABYLON.ShadowGenerator(4096, sunLight);
|
||||||
this.shadowGeneratorForSunLight.forceBackFacesOnly = true;
|
this.shadowGeneratorForSunLight.forceBackFacesOnly = true;
|
||||||
this.shadowGeneratorForSunLight.bias = 0.0001;
|
this.shadowGeneratorForSunLight.bias = 0.0001;
|
||||||
this.shadowGeneratorForSunLight.usePercentageCloserFiltering = true;
|
this.shadowGeneratorForSunLight.usePercentageCloserFiltering = true;
|
||||||
this.shadowGeneratorForSunLight.usePoissonSampling = true;
|
this.shadowGeneratorForSunLight.usePoissonSampling = true;
|
||||||
this.shadowGeneratorForSunLight.getShadowMap().refreshRate = 60;
|
this.shadowGeneratorForSunLight.getShadowMap().refreshRate = 60;
|
||||||
}
|
|
||||||
|
|
||||||
this.turnOnRoomLight();
|
this.turnOnRoomLight();
|
||||||
|
|
||||||
@@ -954,7 +950,7 @@ export class RoomEngine {
|
|||||||
m.checkCollisions = false;
|
m.checkCollisions = false;
|
||||||
m.receiveShadows = true;
|
m.receiveShadows = true;
|
||||||
this.shadowGeneratorForRoomLight.addShadowCaster(m);
|
this.shadowGeneratorForRoomLight.addShadowCaster(m);
|
||||||
if (this.shadowGeneratorForSunLight != null) this.shadowGeneratorForSunLight.addShadowCaster(m);
|
this.shadowGeneratorForSunLight.addShadowCaster(m);
|
||||||
//if (m.material) (m.material as BABYLON.PBRMaterial).ambientColor = new BABYLON.Color3(1, 1, 1);
|
//if (m.material) (m.material as BABYLON.PBRMaterial).ambientColor = new BABYLON.Color3(1, 1, 1);
|
||||||
if (m.material) {
|
if (m.material) {
|
||||||
(m.material as BABYLON.PBRMaterial).reflectionTexture = this.enableReflectionProbe ? this.reflectionProbe.cubeTexture : this.envMapIndoor;
|
(m.material as BABYLON.PBRMaterial).reflectionTexture = this.enableReflectionProbe ? this.reflectionProbe.cubeTexture : this.envMapIndoor;
|
||||||
@@ -983,21 +979,13 @@ export class RoomEngine {
|
|||||||
|
|
||||||
const root = new BABYLON.TransformNode(`object_${args.id}_${args.type}`, this.scene);
|
const root = new BABYLON.TransformNode(`object_${args.id}_${args.type}`, this.scene);
|
||||||
|
|
||||||
const loaderResult = await BABYLON.ImportMeshAsync(`/client-assets/room/objects/${camelToKebab(args.type)}/${camelToKebab(args.type)}.glb`, this.scene);
|
const loaderResult = await BABYLON.LoadAssetContainerAsync(`/client-assets/room/objects/${camelToKebab(args.type)}/${camelToKebab(args.type)}.glb`, this.scene);
|
||||||
|
|
||||||
// babylonによって自動で追加される右手系変換用ノード
|
// babylonによって自動で追加される右手系変換用ノード
|
||||||
const subRoot = loaderResult.meshes[0];
|
const subRoot = loaderResult.meshes[0];
|
||||||
subRoot.scaling = subRoot.scaling.scale(WORLD_SCALE);// cmをmに
|
subRoot.scaling = subRoot.scaling.scale(WORLD_SCALE);// cmをmに
|
||||||
|
|
||||||
if (def.mergeMeshes != null) {
|
def.treatLoaderResult?.(loaderResult);
|
||||||
for (const groupingMeshKeyword of def.mergeMeshes) {
|
|
||||||
const meshes = loaderResult.meshes.filter(m => m.name.includes(groupingMeshKeyword));
|
|
||||||
const merged = BABYLON.Mesh.MergeMeshes(meshes as BABYLON.Mesh[], true, true, undefined, false, true);
|
|
||||||
merged.name = `${groupingMeshKeyword}.grouped`;
|
|
||||||
merged.setParent(subRoot);
|
|
||||||
loaderResult.meshes.push(merged);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let hasCollisionMesh = false;
|
let hasCollisionMesh = false;
|
||||||
for (const mesh of loaderResult.meshes) {
|
for (const mesh of loaderResult.meshes) {
|
||||||
@@ -1020,10 +1008,12 @@ export class RoomEngine {
|
|||||||
root.rotation = args.rotation.clone();
|
root.rotation = args.rotation.clone();
|
||||||
root.metadata = metadata;
|
root.metadata = metadata;
|
||||||
|
|
||||||
const meshUpdated = (meshes: BABYLON.Mesh[]) => {
|
const model = new ModelManager(subRoot, loaderResult.meshes.filter(m => m !== subRoot), (meshes) => {
|
||||||
if (this.selected.value?.objectId === args.id) {
|
if (this.selected.value?.objectId === args.id) {
|
||||||
this.selectionOutlineLayer.clearSelection();
|
if (this.selectionOutlineLayer != null) {
|
||||||
this.selectionOutlineLayer.addSelection(meshes);
|
this.selectionOutlineLayer.clearSelection();
|
||||||
|
this.selectionOutlineLayer.addSelection(meshes);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const m of meshes) {
|
for (const m of meshes) {
|
||||||
@@ -1047,21 +1037,21 @@ export class RoomEngine {
|
|||||||
if (def.receiveShadows !== false) mesh.receiveShadows = true;
|
if (def.receiveShadows !== false) mesh.receiveShadows = true;
|
||||||
if (def.castShadows !== false) {
|
if (def.castShadows !== false) {
|
||||||
this.shadowGeneratorForRoomLight.addShadowCaster(mesh);
|
this.shadowGeneratorForRoomLight.addShadowCaster(mesh);
|
||||||
if (this.shadowGeneratorForSunLight != null) this.shadowGeneratorForSunLight.addShadowCaster(mesh);
|
this.shadowGeneratorForSunLight.addShadowCaster(mesh);
|
||||||
}
|
}
|
||||||
|
|
||||||
//if (mesh.material) (mesh.material as BABYLON.PBRMaterial).ambientColor = new BABYLON.Color3(0.2, 0.2, 0.2);
|
//if (mesh.material) (mesh.material as BABYLON.PBRMaterial).ambientColor = new BABYLON.Color3(0.2, 0.2, 0.2);
|
||||||
if (mesh.material) {
|
if (mesh.material) {
|
||||||
(mesh.material as BABYLON.PBRMaterial).reflectionTexture = this.enableReflectionProbe ? this.reflectionProbe.cubeTexture : this.envMapIndoor;
|
if (mesh.material instanceof BABYLON.MultiMaterial) {
|
||||||
|
for (const subMat of mesh.material.subMaterials) {
|
||||||
|
(subMat as BABYLON.PBRMaterial).reflectionTexture = this.enableReflectionProbe ? this.reflectionProbe.cubeTexture : this.envMapIndoor;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
(mesh.material as BABYLON.PBRMaterial).reflectionTexture = this.enableReflectionProbe ? this.reflectionProbe.cubeTexture : this.envMapIndoor;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
meshUpdated(loaderResult.meshes);
|
|
||||||
|
|
||||||
const model = new ModelManager(subRoot, (meshes) => {
|
|
||||||
meshUpdated(meshes);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const objectInstance = await def.createInstance({
|
const objectInstance = await def.createInstance({
|
||||||
@@ -1074,7 +1064,7 @@ export class RoomEngine {
|
|||||||
|
|
||||||
objectInstance.onInited?.();
|
objectInstance.onInited?.();
|
||||||
|
|
||||||
model.updated();
|
model.bakeMesh();
|
||||||
|
|
||||||
this.objectEntities.set(args.id, { instance: objectInstance, rootMesh: root, model });
|
this.objectEntities.set(args.id, { instance: objectInstance, rootMesh: root, model });
|
||||||
|
|
||||||
@@ -1589,15 +1579,7 @@ export class RoomObjectPreviewEngine {
|
|||||||
const subRoot = loaderResult.meshes[0];
|
const subRoot = loaderResult.meshes[0];
|
||||||
subRoot.scaling = subRoot.scaling.scale(WORLD_SCALE);// cmをmに
|
subRoot.scaling = subRoot.scaling.scale(WORLD_SCALE);// cmをmに
|
||||||
|
|
||||||
if (def.mergeMeshes != null) {
|
def.treatLoaderResult?.(loaderResult);
|
||||||
for (const groupingMeshKeyword of def.mergeMeshes) {
|
|
||||||
const meshes = loaderResult.meshes.filter(m => m.name.includes(groupingMeshKeyword));
|
|
||||||
const merged = BABYLON.Mesh.MergeMeshes(meshes as BABYLON.Mesh[], true, true, undefined, false, true);
|
|
||||||
merged.name = `${groupingMeshKeyword}.grouped`;
|
|
||||||
merged.setParent(subRoot);
|
|
||||||
loaderResult.meshes.push(merged);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let hasCollisionMesh = false;
|
let hasCollisionMesh = false;
|
||||||
for (const mesh of loaderResult.meshes) {
|
for (const mesh of loaderResult.meshes) {
|
||||||
@@ -1609,44 +1591,44 @@ export class RoomObjectPreviewEngine {
|
|||||||
|
|
||||||
root.addChild(subRoot);
|
root.addChild(subRoot);
|
||||||
|
|
||||||
const meshUpdated = (meshes: BABYLON.Mesh[]) => {
|
|
||||||
for (const m of meshes) {
|
|
||||||
const mesh = m;
|
|
||||||
|
|
||||||
// シェイプキー(morph)を考慮してbounding boxを更新するために必要
|
|
||||||
mesh.refreshBoundingInfo({ applyMorph: true });
|
|
||||||
|
|
||||||
mesh.checkCollisions = !hasCollisionMesh;
|
|
||||||
|
|
||||||
if (mesh.name.includes('__COLLISION__')) {
|
|
||||||
mesh.receiveShadows = false;
|
|
||||||
mesh.isVisible = false;
|
|
||||||
mesh.checkCollisions = true;
|
|
||||||
} else if (mesh.name.includes('__TOP__') || mesh.name.includes('__SIDE__')) {
|
|
||||||
mesh.receiveShadows = false;
|
|
||||||
mesh.isVisible = false;
|
|
||||||
} else {
|
|
||||||
if (def.receiveShadows !== false) mesh.receiveShadows = true;
|
|
||||||
if (def.castShadows !== false) {
|
|
||||||
this.shadowGenerator1.addShadowCaster(mesh);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mesh.material) {
|
|
||||||
(mesh.material as BABYLON.PBRMaterial).reflectionTexture = this.envMapIndoor;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
meshUpdated(loaderResult.meshes);
|
|
||||||
|
|
||||||
const objectInstance = await def.createInstance({
|
const objectInstance = await def.createInstance({
|
||||||
room: null,
|
room: null,
|
||||||
scene: this.scene,
|
scene: this.scene,
|
||||||
root,
|
root,
|
||||||
options: args.options,
|
options: args.options,
|
||||||
model: new ModelManager(subRoot, (meshes) => {
|
model: new ModelManager(subRoot, loaderResult.meshes.filter(m => m !== subRoot), (meshes) => {
|
||||||
meshUpdated(meshes);
|
for (const m of meshes) {
|
||||||
|
const mesh = m;
|
||||||
|
|
||||||
|
// シェイプキー(morph)を考慮してbounding boxを更新するために必要
|
||||||
|
mesh.refreshBoundingInfo({ applyMorph: true });
|
||||||
|
|
||||||
|
mesh.checkCollisions = !hasCollisionMesh;
|
||||||
|
|
||||||
|
if (mesh.name.includes('__COLLISION__')) {
|
||||||
|
mesh.receiveShadows = false;
|
||||||
|
mesh.isVisible = false;
|
||||||
|
mesh.checkCollisions = true;
|
||||||
|
} else if (mesh.name.includes('__TOP__') || mesh.name.includes('__SIDE__')) {
|
||||||
|
mesh.receiveShadows = false;
|
||||||
|
mesh.isVisible = false;
|
||||||
|
} else {
|
||||||
|
if (def.receiveShadows !== false) mesh.receiveShadows = true;
|
||||||
|
if (def.castShadows !== false) {
|
||||||
|
this.shadowGenerator1.addShadowCaster(mesh);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mesh.material) {
|
||||||
|
if (mesh.material instanceof BABYLON.MultiMaterial) {
|
||||||
|
for (const subMat of mesh.material.subMaterials) {
|
||||||
|
(subMat as BABYLON.PBRMaterial).reflectionTexture = this.envMapIndoor;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
(mesh.material as BABYLON.PBRMaterial).reflectionTexture = this.envMapIndoor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,8 @@
|
|||||||
import * as BABYLON from '@babylonjs/core';
|
import * as BABYLON from '@babylonjs/core';
|
||||||
import { defineObject } from '../engine.js';
|
import { defineObject } from '../engine.js';
|
||||||
|
|
||||||
|
const mergeMeshes = ['__X_BOOK_1__', '__X_BOOK_2__', '__X_BOOK_3__', '__X_BOOK_4__', '__X_BOOK_5__', '__X_BOOK_6__', '__X_BOOK_7__', '__X_BOOK_8__', '__X_BOOK_9__', '__X_BOOK_10__'];
|
||||||
|
|
||||||
export const books = defineObject({
|
export const books = defineObject({
|
||||||
id: 'books',
|
id: 'books',
|
||||||
name: 'Books',
|
name: 'Books',
|
||||||
@@ -22,7 +24,16 @@ export const books = defineObject({
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
placement: 'top',
|
placement: 'top',
|
||||||
mergeMeshes: ['__X_BOOK_1__', '__X_BOOK_2__', '__X_BOOK_3__', '__X_BOOK_4__', '__X_BOOK_5__', '__X_BOOK_6__', '__X_BOOK_7__', '__X_BOOK_8__', '__X_BOOK_9__', '__X_BOOK_10__'],
|
treatLoaderResult: (loaderResult) => {
|
||||||
|
const subRoot = loaderResult.meshes[0];
|
||||||
|
for (const groupingMeshKeyword of mergeMeshes) {
|
||||||
|
const meshes = loaderResult.meshes.filter(m => m.name.includes(groupingMeshKeyword));
|
||||||
|
const merged = BABYLON.Mesh.MergeMeshes(meshes as BABYLON.Mesh[], true, true, undefined, false, true);
|
||||||
|
merged.name = `${groupingMeshKeyword}.grouped`;
|
||||||
|
merged.setParent(subRoot);
|
||||||
|
loaderResult.meshes.push(merged);
|
||||||
|
}
|
||||||
|
},
|
||||||
createInstance: ({ scene, options, model }) => {
|
createInstance: ({ scene, options, model }) => {
|
||||||
const coverMaterial = model.findMaterial('__X_COVER__');
|
const coverMaterial = model.findMaterial('__X_COVER__');
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user