mirror of
https://github.com/misskey-dev/misskey.git
synced 2026-05-14 08:45:44 +02:00
wip
This commit is contained in:
@@ -35,6 +35,7 @@ export class RoomController {
|
|||||||
public isReady = ref(false);
|
public isReady = ref(false);
|
||||||
public isSitting = ref(false);
|
public isSitting = ref(false);
|
||||||
public isEditMode = ref(false);
|
public isEditMode = ref(false);
|
||||||
|
public isRoomLightOn = ref(true);
|
||||||
public grabbing = ref<{ forInstall: boolean } | null>(null);
|
public grabbing = ref<{ forInstall: boolean } | null>(null);
|
||||||
public gridSnapping = ref({ enabled: true, scale: cm(4) });
|
public gridSnapping = ref({ enabled: true, scale: cm(4) });
|
||||||
public selected = ref<{
|
public selected = ref<{
|
||||||
@@ -279,6 +280,7 @@ export class RoomController {
|
|||||||
this.isReady.value = false;
|
this.isReady.value = false;
|
||||||
this.isSitting.value = false;
|
this.isSitting.value = false;
|
||||||
this.isEditMode.value = false;
|
this.isEditMode.value = false;
|
||||||
|
this.isRoomLightOn.value = true;
|
||||||
this.grabbing.value = null;
|
this.grabbing.value = null;
|
||||||
this.selected.value = null;
|
this.selected.value = null;
|
||||||
this.initializeProgress.value = 0;
|
this.initializeProgress.value = 0;
|
||||||
@@ -372,7 +374,12 @@ export class RoomController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public toggleRoomLight() {
|
public toggleRoomLight() {
|
||||||
this.call('toggleRoomLight');
|
if (this.isRoomLightOn.value) {
|
||||||
|
this.call('turnOffRoomLight');
|
||||||
|
} else {
|
||||||
|
this.call('turnOnRoomLight');
|
||||||
|
}
|
||||||
|
this.isRoomLightOn.value = !this.isRoomLightOn.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public resize() {
|
public resize() {
|
||||||
|
|||||||
@@ -124,8 +124,6 @@ export class RoomEngine extends EventEmitter {
|
|||||||
private useGlow: boolean;
|
private useGlow: boolean;
|
||||||
private engine: BABYLON.WebGPUEngine;
|
private engine: BABYLON.WebGPUEngine;
|
||||||
public scene: BABYLON.Scene;
|
public scene: BABYLON.Scene;
|
||||||
private shadowGeneratorForRoomLight: BABYLON.ShadowGenerator | null = null;
|
|
||||||
private shadowGeneratorForSunLight: BABYLON.ShadowGenerator | null = null;
|
|
||||||
public camera: BABYLON.UniversalCamera;
|
public camera: BABYLON.UniversalCamera;
|
||||||
public objectEntities: Map<string, {
|
public objectEntities: Map<string, {
|
||||||
rootMesh: BABYLON.Mesh;
|
rootMesh: BABYLON.Mesh;
|
||||||
@@ -187,7 +185,6 @@ export class RoomEngine extends EventEmitter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private putParticleSystem: BABYLON.ParticleSystem;
|
private putParticleSystem: BABYLON.ParticleSystem;
|
||||||
private roomLight: BABYLON.SpotLight; // TODO: これの管理もenv側に持ってく?
|
|
||||||
public lightContainer: BABYLON.ClusteredLightContainer;
|
public lightContainer: BABYLON.ClusteredLightContainer;
|
||||||
private gridMaterial: GridMaterial | null = null;
|
private gridMaterial: GridMaterial | null = null;
|
||||||
private gridPlane: BABYLON.Mesh;
|
private gridPlane: BABYLON.Mesh;
|
||||||
@@ -196,6 +193,7 @@ export class RoomEngine extends EventEmitter {
|
|||||||
public sr: BABYLON.SnapshotRenderingHelper;
|
public sr: BABYLON.SnapshotRenderingHelper;
|
||||||
private gl: BABYLON.GlowLayer | null = null;
|
private gl: BABYLON.GlowLayer | null = null;
|
||||||
public timer: Timer = new Timer();
|
public timer: Timer = new Timer();
|
||||||
|
public graphicsQuality: number;
|
||||||
|
|
||||||
private _isEditMode = false;
|
private _isEditMode = false;
|
||||||
get isEditMode() {
|
get isEditMode() {
|
||||||
@@ -233,9 +231,9 @@ export class RoomEngine extends EventEmitter {
|
|||||||
options: { ...getObjectDef(o.type).options.default, ...o.options },
|
options: { ...getObjectDef(o.type).options.default, ...o.options },
|
||||||
})),
|
})),
|
||||||
};
|
};
|
||||||
|
this.graphicsQuality = options.graphicsQuality;
|
||||||
this.fps = options.fps;
|
this.fps = options.fps;
|
||||||
this.useGlow = options.graphicsQuality >= GRAPHICS_QUALITY_MEDIUM;
|
this.useGlow = this.graphicsQuality >= GRAPHICS_QUALITY_MEDIUM;
|
||||||
this.time = TIME_MAP[new Date().getHours() as keyof typeof TIME_MAP];
|
this.time = TIME_MAP[new Date().getHours() as keyof typeof TIME_MAP];
|
||||||
|
|
||||||
registerBuiltInLoaders();
|
registerBuiltInLoaders();
|
||||||
@@ -279,51 +277,12 @@ export class RoomEngine extends EventEmitter {
|
|||||||
|
|
||||||
//this.scene.activeCamera = this.camera;
|
//this.scene.activeCamera = this.camera;
|
||||||
|
|
||||||
this.roomLight = new BABYLON.SpotLight('roomLight', new BABYLON.Vector3(0, cm(249), 0), new BABYLON.Vector3(0, -1, 0), 16, 8, this.scene);
|
|
||||||
this.roomLight.diffuse = new BABYLON.Color3(...roomState.roomLightColor);
|
|
||||||
this.roomLight.shadowMinZ = cm(10);
|
|
||||||
this.roomLight.shadowMaxZ = cm(300);
|
|
||||||
this.roomLight.radius = cm(30);
|
|
||||||
|
|
||||||
if (options.graphicsQuality >= GRAPHICS_QUALITY_MEDIUM) {
|
|
||||||
this.shadowGeneratorForRoomLight = new BABYLON.ShadowGenerator(options.graphicsQuality <= GRAPHICS_QUALITY_MEDIUM ? 1024 : 2048, this.roomLight);
|
|
||||||
this.shadowGeneratorForRoomLight.forceBackFacesOnly = true;
|
|
||||||
this.shadowGeneratorForRoomLight.bias = 0.00001;
|
|
||||||
this.shadowGeneratorForRoomLight.normalBias = 0.005;
|
|
||||||
this.shadowGeneratorForRoomLight.usePercentageCloserFiltering = true;
|
|
||||||
this.shadowGeneratorForRoomLight.filteringQuality = BABYLON.ShadowGenerator.QUALITY_HIGH;
|
|
||||||
if (options.graphicsQuality <= GRAPHICS_QUALITY_MEDIUM) {
|
|
||||||
this.shadowGeneratorForRoomLight.getShadowMap().refreshRate = 60;
|
|
||||||
}
|
|
||||||
//this.shadowGeneratorForRoomLight.useContactHardeningShadow = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (options.graphicsQuality >= GRAPHICS_QUALITY_MEDIUM) {
|
|
||||||
const sunLight = new BABYLON.DirectionalLight('sunLight', new BABYLON.Vector3(0.2, -1, -1), this.scene);
|
|
||||||
sunLight.position = new BABYLON.Vector3(cm(-20), cm(1000), cm(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.intensity = this.time === 0 ? 3 : this.time === 1 ? 1 : 0.25;
|
|
||||||
sunLight.shadowMinZ = cm(1000);
|
|
||||||
sunLight.shadowMaxZ = cm(2000);
|
|
||||||
|
|
||||||
this.shadowGeneratorForSunLight = new BABYLON.ShadowGenerator(options.graphicsQuality <= GRAPHICS_QUALITY_MEDIUM ? 1024 : 2048, sunLight);
|
|
||||||
this.shadowGeneratorForSunLight.forceBackFacesOnly = true;
|
|
||||||
this.shadowGeneratorForSunLight.bias = 0.00001;
|
|
||||||
this.shadowGeneratorForSunLight.usePercentageCloserFiltering = true;
|
|
||||||
this.shadowGeneratorForSunLight.usePoissonSampling = true;
|
|
||||||
if (options.graphicsQuality <= GRAPHICS_QUALITY_MEDIUM) {
|
|
||||||
this.shadowGeneratorForSunLight.getShadowMap().refreshRate = 60;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.lightContainer = new BABYLON.ClusteredLightContainer('clustered', [], this.scene);
|
this.lightContainer = new BABYLON.ClusteredLightContainer('clustered', [], this.scene);
|
||||||
this.lightContainer.maxRange = options.graphicsQuality >= GRAPHICS_QUALITY_HIGH ? cm(200) : options.graphicsQuality >= GRAPHICS_QUALITY_MEDIUM ? cm(90) : cm(30);
|
this.lightContainer.maxRange = this.graphicsQuality >= GRAPHICS_QUALITY_HIGH ? cm(200) : this.graphicsQuality >= GRAPHICS_QUALITY_MEDIUM ? cm(90) : cm(30);
|
||||||
this.lightContainer.verticalTiles = 32;
|
this.lightContainer.verticalTiles = 32;
|
||||||
this.lightContainer.horizontalTiles = 32;
|
this.lightContainer.horizontalTiles = 32;
|
||||||
this.lightContainer.depthSlices = 32;
|
this.lightContainer.depthSlices = 32;
|
||||||
|
|
||||||
this.turnOnRoomLight(true);
|
|
||||||
|
|
||||||
if (this.useGlow) {
|
if (this.useGlow) {
|
||||||
this.gl = new BABYLON.GlowLayer('glow', this.scene, {
|
this.gl = new BABYLON.GlowLayer('glow', this.scene, {
|
||||||
//mainTextureFixedSize: 512,
|
//mainTextureFixedSize: 512,
|
||||||
@@ -363,13 +322,13 @@ export class RoomEngine extends EventEmitter {
|
|||||||
this.gridPlane.isVisible = false;
|
this.gridPlane.isVisible = false;
|
||||||
this.gridPlane.setEnabled(false);
|
this.gridPlane.setEnabled(false);
|
||||||
|
|
||||||
if (options.graphicsQuality >= GRAPHICS_QUALITY_MEDIUM) {
|
if (this.graphicsQuality >= GRAPHICS_QUALITY_MEDIUM) {
|
||||||
this.selectionOutlineLayer = new BABYLON.SelectionOutlineLayer('outliner', this.scene);
|
this.selectionOutlineLayer = new BABYLON.SelectionOutlineLayer('outliner', this.scene);
|
||||||
this.scene.setRenderingAutoClearDepthStencil(this.selectionOutlineLayer.renderingGroupId, false);
|
this.scene.setRenderingAutoClearDepthStencil(this.selectionOutlineLayer.renderingGroupId, false);
|
||||||
this.sr.updateMeshesForEffectLayer(this.selectionOutlineLayer);
|
this.sr.updateMeshesForEffectLayer(this.selectionOutlineLayer);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options.graphicsQuality >= GRAPHICS_QUALITY_HIGH) {
|
if (this.graphicsQuality >= GRAPHICS_QUALITY_HIGH) {
|
||||||
const pipeline = new BABYLON.DefaultRenderingPipeline('default', true, this.scene);
|
const pipeline = new BABYLON.DefaultRenderingPipeline('default', true, this.scene);
|
||||||
if (options.antialias) {
|
if (options.antialias) {
|
||||||
pipeline.samples = 4;
|
pipeline.samples = 4;
|
||||||
@@ -574,238 +533,6 @@ export class RoomEngine extends EventEmitter {
|
|||||||
this.startRenderLoop();
|
this.startRenderLoop();
|
||||||
}
|
}
|
||||||
|
|
||||||
public cameraMove(vector: { x: number; y: number; }, dash: boolean) {
|
|
||||||
(this.camera.inputs.attached.manual as FreeCameraManualInput).setMoveVector(dash ? { x: vector.x * 3, y: vector.y * 3 } : vector);
|
|
||||||
}
|
|
||||||
|
|
||||||
public cameraRotate(vector: { x: number; y: number; }) {
|
|
||||||
(this.camera.inputs.attached.manual as FreeCameraManualInput).setRotationVector(vector);
|
|
||||||
}
|
|
||||||
|
|
||||||
public cameraJoystickMove(vector: { x: number; y: number; }) {
|
|
||||||
(this.camera.inputs.attached.manual as FreeCameraManualInput).setMoveVector(vector);
|
|
||||||
}
|
|
||||||
|
|
||||||
public selectObject(objectId: string | null) {
|
|
||||||
this.sr.disableSnapshotRendering(); // snapshot rendering中にbake/unbakeするとエラーになる。なおこのメソッドは参照カウント方式な点に留意
|
|
||||||
|
|
||||||
const currentSelected = this.selected;
|
|
||||||
if (currentSelected != null) {
|
|
||||||
this.selected = null;
|
|
||||||
this.clearHighlight();
|
|
||||||
currentSelected.objectEntity.model.bakeMesh();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (objectId != null) {
|
|
||||||
const entity = this.objectEntities.get(objectId);
|
|
||||||
if (entity != null) {
|
|
||||||
entity.model.unbakeMesh();
|
|
||||||
//this.gizmoManager.positionGizmoEnabled = true;
|
|
||||||
//this.gizmoManager.gizmos.positionGizmo.updateGizmoRotationToMatchAttachedMesh = false;
|
|
||||||
//this.gizmoManager.attachToMesh(entity.rootMesh);
|
|
||||||
this.highlightMeshes(entity.rootMesh.getChildMeshes());
|
|
||||||
const state = this.roomState.installedObjects.find(o => o.id === objectId)!;
|
|
||||||
this.selected = {
|
|
||||||
objectId,
|
|
||||||
objectEntity: entity,
|
|
||||||
objectState: state,
|
|
||||||
objectDef: getObjectDef(state.type),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.sr.enableSnapshotRendering(); // このメソッドは参照カウント方式な点に留意
|
|
||||||
}
|
|
||||||
|
|
||||||
private handleGrabbing() {
|
|
||||||
if (this.grabbingCtx == null) return;
|
|
||||||
const grabbing = this.grabbingCtx;
|
|
||||||
|
|
||||||
const placement = getObjectDef(grabbing.objectType).placement;
|
|
||||||
|
|
||||||
const dir = this.camera.getDirection(BABYLON.Axis.Z).scale(this.scene.useRightHandedSystem ? -1 : 1);
|
|
||||||
let newPos = this.camera.position.add(dir.scale(grabbing.distance)).add(grabbing.originalDiffOfPosition);
|
|
||||||
const newRotation = new BABYLON.Vector3(0, this.camera.rotation.y + grabbing.originalDiffOfRotation.y + grabbing.rotation, 0);
|
|
||||||
grabbing.ghost.position = newPos.clone();
|
|
||||||
grabbing.ghost.rotation = newRotation.clone();
|
|
||||||
|
|
||||||
let stickyOtherObject: string | null = null;
|
|
||||||
let sticky = false;
|
|
||||||
|
|
||||||
const isCollisionTarget = (m: BABYLON.AbstractMesh) => {
|
|
||||||
return m.metadata?.objectId !== grabbing.objectId &&
|
|
||||||
!m.metadata?.isGhost &&
|
|
||||||
!grabbing.descendantStickyObjectIds.includes(m.metadata?.objectId);
|
|
||||||
};
|
|
||||||
|
|
||||||
const pos = new BABYLON.Vector3(this.camera.position.x, this.camera.position.y, this.camera.position.z);
|
|
||||||
const _dir = newPos.subtract(pos).normalize();
|
|
||||||
for (let i = 0; i < 1000; i++) {
|
|
||||||
if (cm(i) > grabbing.distance) break;
|
|
||||||
const prevSticky = sticky;
|
|
||||||
// posを1cmずつnewPosの方向に動かす
|
|
||||||
pos.addInPlace(_dir.scale(cm(1)));
|
|
||||||
|
|
||||||
if (placement === 'side' || placement === 'wall') {
|
|
||||||
// 前方に向かってレイを飛ばす
|
|
||||||
const ray = new BABYLON.Ray(pos, dir, cm(1000));
|
|
||||||
const hit = placement === 'side' ? this.scene.pickWithRay(ray, (m) => isCollisionTarget(m) && (m.name.includes('__ROOM_WALL__') || m.name.includes('__SIDE__'))) : this.scene.pickWithRay(ray, (m) => isCollisionTarget(m) && (m.name.includes('__ROOM_WALL__')));
|
|
||||||
if (hit != null && hit.pickedPoint != null && hit.pickedMesh != null) {
|
|
||||||
sticky = true;
|
|
||||||
const pickedMeshNormal = hit.getNormal(true, true)!;
|
|
||||||
const targetRotationY = Math.atan2(pickedMeshNormal.x, pickedMeshNormal.z);
|
|
||||||
newRotation.y = targetRotationY;
|
|
||||||
newRotation.z = grabbing.originalDiffOfRotation.z + grabbing.rotation;
|
|
||||||
newPos = hit.pickedPoint;
|
|
||||||
stickyOtherObject = hit.pickedMesh.metadata?.objectId ?? null;
|
|
||||||
|
|
||||||
if (this.gridSnapping.enabled) {
|
|
||||||
newPos.y = Math.round(newPos.y / this.gridSnapping.scale) * this.gridSnapping.scale;
|
|
||||||
if (getYRotationDirection(targetRotationY) === '+x' || getYRotationDirection(targetRotationY) === '-x') {
|
|
||||||
newPos.z = Math.round(newPos.z / this.gridSnapping.scale) * this.gridSnapping.scale;
|
|
||||||
} else {
|
|
||||||
newPos.x = Math.round(newPos.x / this.gridSnapping.scale) * this.gridSnapping.scale;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.gridPlane.rotationQuaternion = null;
|
|
||||||
this.gridPlane.rotation.x = Math.PI;
|
|
||||||
this.gridPlane.rotation.y = targetRotationY;
|
|
||||||
this.gridPlane.position = newPos.add(pickedMeshNormal.scale(cm(0.1)));
|
|
||||||
if (getYRotationDirection(targetRotationY) === '+x' || getYRotationDirection(targetRotationY) === '-x') {
|
|
||||||
this.gridPlane.position.y = 0;
|
|
||||||
this.gridPlane.position.z = 0;
|
|
||||||
} else {
|
|
||||||
this.gridPlane.position.y = 0;
|
|
||||||
this.gridPlane.position.x = 0;
|
|
||||||
}
|
|
||||||
this.gridPlane.isVisible = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (placement === 'bottom' || placement === 'ceiling') {
|
|
||||||
// 上に向かってレイを飛ばす
|
|
||||||
const ray = new BABYLON.Ray(pos, new BABYLON.Vector3(0, 1, 0), cm(1000));
|
|
||||||
const hit = placement === 'bottom' ? this.scene.pickWithRay(ray, (m) => isCollisionTarget(m) && (m.name.includes('__ROOM_CEILING__') || m.name.includes('__ROOM_BOTTOM__') || m.name.includes('__BOTTOM__'))) : this.scene.pickWithRay(ray, (m) => isCollisionTarget(m) && (m.name.includes('__ROOM_CEILING__')));
|
|
||||||
if (hit != null && hit.pickedPoint != null && hit.pickedMesh != null) {
|
|
||||||
sticky = true;
|
|
||||||
newPos = hit.pickedPoint;
|
|
||||||
stickyOtherObject = hit.pickedMesh.metadata?.objectId ?? null;
|
|
||||||
|
|
||||||
if (this.gridSnapping.enabled) {
|
|
||||||
newPos.x = Math.round(newPos.x / this.gridSnapping.scale) * this.gridSnapping.scale;
|
|
||||||
newPos.z = Math.round(newPos.z / this.gridSnapping.scale) * this.gridSnapping.scale;
|
|
||||||
|
|
||||||
this.gridPlane.rotationQuaternion = null;
|
|
||||||
this.gridPlane.rotation.x = Math.PI * 1.5;
|
|
||||||
this.gridPlane.rotation.y = 0;
|
|
||||||
this.gridPlane.position = new BABYLON.Vector3(grabbing.mesh.position.x, grabbing.mesh.position.y - cm(0.1), grabbing.mesh.position.z);
|
|
||||||
this.gridPlane.position.x = 0;
|
|
||||||
this.gridPlane.position.z = 0;
|
|
||||||
this.gridPlane.isVisible = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else { // top or floor
|
|
||||||
// 下に向かってレイを飛ばす
|
|
||||||
const ray = new BABYLON.Ray(pos, new BABYLON.Vector3(0, -1, 0), cm(1000));
|
|
||||||
const hit = placement === 'top' ? this.scene.pickWithRay(ray, (m) => isCollisionTarget(m) && (m.name.includes('__ROOM_FLOOR__') || m.name.includes('__ROOM_TOP__') || m.name.includes('__TOP__'))) : this.scene.pickWithRay(ray, (m) => isCollisionTarget(m) && (m.name.includes('__ROOM_FLOOR__')));
|
|
||||||
if (hit != null && hit.pickedPoint != null && hit.pickedMesh != null) {
|
|
||||||
sticky = true;
|
|
||||||
newPos = hit.pickedPoint;
|
|
||||||
stickyOtherObject = hit.pickedMesh.metadata?.objectId ?? null;
|
|
||||||
|
|
||||||
if (this.gridSnapping.enabled) {
|
|
||||||
newPos.x = Math.round(newPos.x / this.gridSnapping.scale) * this.gridSnapping.scale;
|
|
||||||
newPos.z = Math.round(newPos.z / this.gridSnapping.scale) * this.gridSnapping.scale;
|
|
||||||
|
|
||||||
this.gridPlane.rotationQuaternion = null;
|
|
||||||
this.gridPlane.rotation.x = Math.PI / 2;
|
|
||||||
this.gridPlane.rotation.y = 0;
|
|
||||||
this.gridPlane.position = new BABYLON.Vector3(grabbing.mesh.position.x, grabbing.mesh.position.y + cm(0.1), grabbing.mesh.position.z);
|
|
||||||
this.gridPlane.position.x = 0;
|
|
||||||
this.gridPlane.position.z = 0;
|
|
||||||
this.gridPlane.isVisible = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!sticky && prevSticky) {
|
|
||||||
sticky = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sticky) {
|
|
||||||
if (this.gridSnapping.enabled) {
|
|
||||||
newRotation.y = Math.round(newRotation.y / (Math.PI / 8)) * (Math.PI / 8);
|
|
||||||
}
|
|
||||||
|
|
||||||
grabbing.mesh.position = newPos;
|
|
||||||
grabbing.mesh.rotation = newRotation;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!sticky) {
|
|
||||||
this.gridPlane.isVisible = false;
|
|
||||||
//for (const mesh of grabbing.ghost.getChildMeshes()) {
|
|
||||||
//if (mesh.material instanceof BABYLON.MultiMaterial) {
|
|
||||||
// for (const subMat of mesh.material.subMaterials) {
|
|
||||||
// if (subMat instanceof BABYLON.PBRMaterial) {
|
|
||||||
// subMat.emissiveColor = new BABYLON.Color3(1, 0, 0);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//} else {
|
|
||||||
// mesh.material.emissiveColor = new BABYLON.Color3(1, 0, 0);
|
|
||||||
//}
|
|
||||||
//}
|
|
||||||
}
|
|
||||||
|
|
||||||
//const pos = new BABYLON.Vector3(this.camera.position.x, this.camera.position.y, this.camera.position.z);
|
|
||||||
//const _dir = newPos.subtract(pos).normalize();
|
|
||||||
//for (let i = 0; i < grabbing.distance; i++) {
|
|
||||||
// // posを1cmずつnewPosの方向に動かす
|
|
||||||
// pos.addInPlace(_dir.scale(cm(1)));
|
|
||||||
// // 前方に向かってレイを飛ばして衝突チェック
|
|
||||||
// const ray = new BABYLON.Ray(this.camera.position, dir, i);
|
|
||||||
// const hit = this.scene.pickWithRay(ray, (m) => isCollisionTarget(m));
|
|
||||||
// if (hit != null && hit.pickedPoint != null && hit.pickedMesh != null) {
|
|
||||||
// //const isCollided = grabbing.mesh.intersectsMesh(hit.pickedMesh, false);
|
|
||||||
// //if (isCollided) {
|
|
||||||
// break;
|
|
||||||
// //}
|
|
||||||
// }
|
|
||||||
// grabbing.mesh.position = pos.clone();
|
|
||||||
//}
|
|
||||||
|
|
||||||
//const ray = new BABYLON.Ray(this.camera.position, this.camera.getDirection(BABYLON.Axis.Z), cm(1000));
|
|
||||||
//const hit = this.scene.pickWithRay(ray, (m) => m.name.includes('__COLLISION_WALL__'))!;
|
|
||||||
//if (hit.pickedMesh != null) {
|
|
||||||
// const grabbingBox = this.grabbing.mesh.getBoundingInfo().boundingBox;
|
|
||||||
// const grabDistanceVector = this.grabbing.mesh.position.subtract(this.camera.position);
|
|
||||||
// if (grabDistanceVector.length() > hit.distance) {
|
|
||||||
// this.grabbing.mesh.position = this.camera.position.add(dir.scale(hit.distance));
|
|
||||||
// this.grabbing.mesh.position.y = y;
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
|
|
||||||
//const displacementVector = new BABYLON.Vector3(
|
|
||||||
// this.grabbing.ghost.position.x - this.grabbing.mesh.position.x,
|
|
||||||
// 0,
|
|
||||||
// this.grabbing.ghost.position.z - this.grabbing.mesh.position.z,
|
|
||||||
//);
|
|
||||||
//this.grabbing.mesh.moveWithCollisions(displacementVector);
|
|
||||||
//this.grabbing.mesh.position.y = y;
|
|
||||||
|
|
||||||
//for (const soid of stickyObjectIds) {
|
|
||||||
// //const soMesh = this.objectMeshs.get(soid)!;
|
|
||||||
// //const offset = this.grabbing.mesh!.position.subtract(soMeshStartPosition);
|
|
||||||
// //soMesh.position = this.grabbing.mesh!.position.subtract(offset);
|
|
||||||
//}
|
|
||||||
|
|
||||||
grabbing.onMove?.({
|
|
||||||
position: newPos,
|
|
||||||
rotation: newRotation,
|
|
||||||
sticky: stickyOtherObject,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public async changeEnvType(type: RoomState['env']['type'], forInit = false) {
|
public async changeEnvType(type: RoomState['env']['type'], forInit = false) {
|
||||||
this.roomState.env.type = type;
|
this.roomState.env.type = type;
|
||||||
|
|
||||||
@@ -829,9 +556,6 @@ export class RoomEngine extends EventEmitter {
|
|||||||
|
|
||||||
m.isPickable = false;
|
m.isPickable = false;
|
||||||
m.checkCollisions = false;
|
m.checkCollisions = false;
|
||||||
m.receiveShadows = true;
|
|
||||||
this.shadowGeneratorForRoomLight?.addShadowCaster(m);
|
|
||||||
this.shadowGeneratorForSunLight?.addShadowCaster(m);
|
|
||||||
if (m.material != null) {
|
if (m.material != null) {
|
||||||
(m.material as BABYLON.PBRMaterial).useGLTFLightFalloff = true; // Clustered Lightingではphysical falloffを持つマテリアルはアーチファクトが発生する https://doc.babylonjs.com/features/featuresDeepDive/lights/clusteredLighting/#materials-with-a-physical-falloff-may-cause-artefacts
|
(m.material as BABYLON.PBRMaterial).useGLTFLightFalloff = true; // Clustered Lightingではphysical falloffを持つマテリアルはアーチファクトが発生する https://doc.babylonjs.com/features/featuresDeepDive/lights/clusteredLighting/#materials-with-a-physical-falloff-may-cause-artefacts
|
||||||
}
|
}
|
||||||
@@ -848,7 +572,7 @@ export class RoomEngine extends EventEmitter {
|
|||||||
envManager = new MuseumEnvManager(onMeshUpdatedCallback);
|
envManager = new MuseumEnvManager(onMeshUpdatedCallback);
|
||||||
}
|
}
|
||||||
|
|
||||||
await envManager.load(this.roomState.env.options, this.scene);
|
await envManager.load(this.roomState.env.options, this.scene, this);
|
||||||
envManager.setTime(this.time);
|
envManager.setTime(this.time);
|
||||||
|
|
||||||
for (const mat of this.scene.materials) {
|
for (const mat of this.scene.materials) {
|
||||||
@@ -867,6 +591,7 @@ export class RoomEngine extends EventEmitter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.envManager = envManager;
|
this.envManager = envManager;
|
||||||
|
this.turnOnRoomLight(true);
|
||||||
|
|
||||||
this.camera.maxZ = this.envManager.maxCameraZ;
|
this.camera.maxZ = this.envManager.maxCameraZ;
|
||||||
|
|
||||||
@@ -1112,8 +837,7 @@ export class RoomEngine extends EventEmitter {
|
|||||||
if (def.receiveShadows !== false) mesh.receiveShadows = true;
|
if (def.receiveShadows !== false) mesh.receiveShadows = true;
|
||||||
if (def.castShadows !== false) {
|
if (def.castShadows !== false) {
|
||||||
// TODO: メモリリークしそうだからいい感じにする
|
// TODO: メモリリークしそうだからいい感じにする
|
||||||
this.shadowGeneratorForRoomLight?.addShadowCaster(mesh);
|
this.envManager.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);
|
||||||
@@ -1206,6 +930,238 @@ export class RoomEngine extends EventEmitter {
|
|||||||
return { root, objectInstance };
|
return { root, objectInstance };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public cameraMove(vector: { x: number; y: number; }, dash: boolean) {
|
||||||
|
(this.camera.inputs.attached.manual as FreeCameraManualInput).setMoveVector(dash ? { x: vector.x * 3, y: vector.y * 3 } : vector);
|
||||||
|
}
|
||||||
|
|
||||||
|
public cameraRotate(vector: { x: number; y: number; }) {
|
||||||
|
(this.camera.inputs.attached.manual as FreeCameraManualInput).setRotationVector(vector);
|
||||||
|
}
|
||||||
|
|
||||||
|
public cameraJoystickMove(vector: { x: number; y: number; }) {
|
||||||
|
(this.camera.inputs.attached.manual as FreeCameraManualInput).setMoveVector(vector);
|
||||||
|
}
|
||||||
|
|
||||||
|
public selectObject(objectId: string | null) {
|
||||||
|
this.sr.disableSnapshotRendering(); // snapshot rendering中にbake/unbakeするとエラーになる。なおこのメソッドは参照カウント方式な点に留意
|
||||||
|
|
||||||
|
const currentSelected = this.selected;
|
||||||
|
if (currentSelected != null) {
|
||||||
|
this.selected = null;
|
||||||
|
this.clearHighlight();
|
||||||
|
currentSelected.objectEntity.model.bakeMesh();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (objectId != null) {
|
||||||
|
const entity = this.objectEntities.get(objectId);
|
||||||
|
if (entity != null) {
|
||||||
|
entity.model.unbakeMesh();
|
||||||
|
//this.gizmoManager.positionGizmoEnabled = true;
|
||||||
|
//this.gizmoManager.gizmos.positionGizmo.updateGizmoRotationToMatchAttachedMesh = false;
|
||||||
|
//this.gizmoManager.attachToMesh(entity.rootMesh);
|
||||||
|
this.highlightMeshes(entity.rootMesh.getChildMeshes());
|
||||||
|
const state = this.roomState.installedObjects.find(o => o.id === objectId)!;
|
||||||
|
this.selected = {
|
||||||
|
objectId,
|
||||||
|
objectEntity: entity,
|
||||||
|
objectState: state,
|
||||||
|
objectDef: getObjectDef(state.type),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.sr.enableSnapshotRendering(); // このメソッドは参照カウント方式な点に留意
|
||||||
|
}
|
||||||
|
|
||||||
|
private handleGrabbing() {
|
||||||
|
if (this.grabbingCtx == null) return;
|
||||||
|
const grabbing = this.grabbingCtx;
|
||||||
|
|
||||||
|
const placement = getObjectDef(grabbing.objectType).placement;
|
||||||
|
|
||||||
|
const dir = this.camera.getDirection(BABYLON.Axis.Z).scale(this.scene.useRightHandedSystem ? -1 : 1);
|
||||||
|
let newPos = this.camera.position.add(dir.scale(grabbing.distance)).add(grabbing.originalDiffOfPosition);
|
||||||
|
const newRotation = new BABYLON.Vector3(0, this.camera.rotation.y + grabbing.originalDiffOfRotation.y + grabbing.rotation, 0);
|
||||||
|
grabbing.ghost.position = newPos.clone();
|
||||||
|
grabbing.ghost.rotation = newRotation.clone();
|
||||||
|
|
||||||
|
let stickyOtherObject: string | null = null;
|
||||||
|
let sticky = false;
|
||||||
|
|
||||||
|
const isCollisionTarget = (m: BABYLON.AbstractMesh) => {
|
||||||
|
return m.metadata?.objectId !== grabbing.objectId &&
|
||||||
|
!m.metadata?.isGhost &&
|
||||||
|
!grabbing.descendantStickyObjectIds.includes(m.metadata?.objectId);
|
||||||
|
};
|
||||||
|
|
||||||
|
const pos = new BABYLON.Vector3(this.camera.position.x, this.camera.position.y, this.camera.position.z);
|
||||||
|
const _dir = newPos.subtract(pos).normalize();
|
||||||
|
for (let i = 0; i < 1000; i++) {
|
||||||
|
if (cm(i) > grabbing.distance) break;
|
||||||
|
const prevSticky = sticky;
|
||||||
|
// posを1cmずつnewPosの方向に動かす
|
||||||
|
pos.addInPlace(_dir.scale(cm(1)));
|
||||||
|
|
||||||
|
if (placement === 'side' || placement === 'wall') {
|
||||||
|
// 前方に向かってレイを飛ばす
|
||||||
|
const ray = new BABYLON.Ray(pos, dir, cm(1000));
|
||||||
|
const hit = placement === 'side' ? this.scene.pickWithRay(ray, (m) => isCollisionTarget(m) && (m.name.includes('__ROOM_WALL__') || m.name.includes('__SIDE__'))) : this.scene.pickWithRay(ray, (m) => isCollisionTarget(m) && (m.name.includes('__ROOM_WALL__')));
|
||||||
|
if (hit != null && hit.pickedPoint != null && hit.pickedMesh != null) {
|
||||||
|
sticky = true;
|
||||||
|
const pickedMeshNormal = hit.getNormal(true, true)!;
|
||||||
|
const targetRotationY = Math.atan2(pickedMeshNormal.x, pickedMeshNormal.z);
|
||||||
|
newRotation.y = targetRotationY;
|
||||||
|
newRotation.z = grabbing.originalDiffOfRotation.z + grabbing.rotation;
|
||||||
|
newPos = hit.pickedPoint;
|
||||||
|
stickyOtherObject = hit.pickedMesh.metadata?.objectId ?? null;
|
||||||
|
|
||||||
|
if (this.gridSnapping.enabled) {
|
||||||
|
newPos.y = Math.round(newPos.y / this.gridSnapping.scale) * this.gridSnapping.scale;
|
||||||
|
if (getYRotationDirection(targetRotationY) === '+x' || getYRotationDirection(targetRotationY) === '-x') {
|
||||||
|
newPos.z = Math.round(newPos.z / this.gridSnapping.scale) * this.gridSnapping.scale;
|
||||||
|
} else {
|
||||||
|
newPos.x = Math.round(newPos.x / this.gridSnapping.scale) * this.gridSnapping.scale;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.gridPlane.rotationQuaternion = null;
|
||||||
|
this.gridPlane.rotation.x = Math.PI;
|
||||||
|
this.gridPlane.rotation.y = targetRotationY;
|
||||||
|
this.gridPlane.position = newPos.add(pickedMeshNormal.scale(cm(0.1)));
|
||||||
|
if (getYRotationDirection(targetRotationY) === '+x' || getYRotationDirection(targetRotationY) === '-x') {
|
||||||
|
this.gridPlane.position.y = 0;
|
||||||
|
this.gridPlane.position.z = 0;
|
||||||
|
} else {
|
||||||
|
this.gridPlane.position.y = 0;
|
||||||
|
this.gridPlane.position.x = 0;
|
||||||
|
}
|
||||||
|
this.gridPlane.isVisible = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (placement === 'bottom' || placement === 'ceiling') {
|
||||||
|
// 上に向かってレイを飛ばす
|
||||||
|
const ray = new BABYLON.Ray(pos, new BABYLON.Vector3(0, 1, 0), cm(1000));
|
||||||
|
const hit = placement === 'bottom' ? this.scene.pickWithRay(ray, (m) => isCollisionTarget(m) && (m.name.includes('__ROOM_CEILING__') || m.name.includes('__ROOM_BOTTOM__') || m.name.includes('__BOTTOM__'))) : this.scene.pickWithRay(ray, (m) => isCollisionTarget(m) && (m.name.includes('__ROOM_CEILING__')));
|
||||||
|
if (hit != null && hit.pickedPoint != null && hit.pickedMesh != null) {
|
||||||
|
sticky = true;
|
||||||
|
newPos = hit.pickedPoint;
|
||||||
|
stickyOtherObject = hit.pickedMesh.metadata?.objectId ?? null;
|
||||||
|
|
||||||
|
if (this.gridSnapping.enabled) {
|
||||||
|
newPos.x = Math.round(newPos.x / this.gridSnapping.scale) * this.gridSnapping.scale;
|
||||||
|
newPos.z = Math.round(newPos.z / this.gridSnapping.scale) * this.gridSnapping.scale;
|
||||||
|
|
||||||
|
this.gridPlane.rotationQuaternion = null;
|
||||||
|
this.gridPlane.rotation.x = Math.PI * 1.5;
|
||||||
|
this.gridPlane.rotation.y = 0;
|
||||||
|
this.gridPlane.position = new BABYLON.Vector3(grabbing.mesh.position.x, grabbing.mesh.position.y - cm(0.1), grabbing.mesh.position.z);
|
||||||
|
this.gridPlane.position.x = 0;
|
||||||
|
this.gridPlane.position.z = 0;
|
||||||
|
this.gridPlane.isVisible = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else { // top or floor
|
||||||
|
// 下に向かってレイを飛ばす
|
||||||
|
const ray = new BABYLON.Ray(pos, new BABYLON.Vector3(0, -1, 0), cm(1000));
|
||||||
|
const hit = placement === 'top' ? this.scene.pickWithRay(ray, (m) => isCollisionTarget(m) && (m.name.includes('__ROOM_FLOOR__') || m.name.includes('__ROOM_TOP__') || m.name.includes('__TOP__'))) : this.scene.pickWithRay(ray, (m) => isCollisionTarget(m) && (m.name.includes('__ROOM_FLOOR__')));
|
||||||
|
if (hit != null && hit.pickedPoint != null && hit.pickedMesh != null) {
|
||||||
|
sticky = true;
|
||||||
|
newPos = hit.pickedPoint;
|
||||||
|
stickyOtherObject = hit.pickedMesh.metadata?.objectId ?? null;
|
||||||
|
|
||||||
|
if (this.gridSnapping.enabled) {
|
||||||
|
newPos.x = Math.round(newPos.x / this.gridSnapping.scale) * this.gridSnapping.scale;
|
||||||
|
newPos.z = Math.round(newPos.z / this.gridSnapping.scale) * this.gridSnapping.scale;
|
||||||
|
|
||||||
|
this.gridPlane.rotationQuaternion = null;
|
||||||
|
this.gridPlane.rotation.x = Math.PI / 2;
|
||||||
|
this.gridPlane.rotation.y = 0;
|
||||||
|
this.gridPlane.position = new BABYLON.Vector3(grabbing.mesh.position.x, grabbing.mesh.position.y + cm(0.1), grabbing.mesh.position.z);
|
||||||
|
this.gridPlane.position.x = 0;
|
||||||
|
this.gridPlane.position.z = 0;
|
||||||
|
this.gridPlane.isVisible = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!sticky && prevSticky) {
|
||||||
|
sticky = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sticky) {
|
||||||
|
if (this.gridSnapping.enabled) {
|
||||||
|
newRotation.y = Math.round(newRotation.y / (Math.PI / 8)) * (Math.PI / 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
grabbing.mesh.position = newPos;
|
||||||
|
grabbing.mesh.rotation = newRotation;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!sticky) {
|
||||||
|
this.gridPlane.isVisible = false;
|
||||||
|
//for (const mesh of grabbing.ghost.getChildMeshes()) {
|
||||||
|
//if (mesh.material instanceof BABYLON.MultiMaterial) {
|
||||||
|
// for (const subMat of mesh.material.subMaterials) {
|
||||||
|
// if (subMat instanceof BABYLON.PBRMaterial) {
|
||||||
|
// subMat.emissiveColor = new BABYLON.Color3(1, 0, 0);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//} else {
|
||||||
|
// mesh.material.emissiveColor = new BABYLON.Color3(1, 0, 0);
|
||||||
|
//}
|
||||||
|
//}
|
||||||
|
}
|
||||||
|
|
||||||
|
//const pos = new BABYLON.Vector3(this.camera.position.x, this.camera.position.y, this.camera.position.z);
|
||||||
|
//const _dir = newPos.subtract(pos).normalize();
|
||||||
|
//for (let i = 0; i < grabbing.distance; i++) {
|
||||||
|
// // posを1cmずつnewPosの方向に動かす
|
||||||
|
// pos.addInPlace(_dir.scale(cm(1)));
|
||||||
|
// // 前方に向かってレイを飛ばして衝突チェック
|
||||||
|
// const ray = new BABYLON.Ray(this.camera.position, dir, i);
|
||||||
|
// const hit = this.scene.pickWithRay(ray, (m) => isCollisionTarget(m));
|
||||||
|
// if (hit != null && hit.pickedPoint != null && hit.pickedMesh != null) {
|
||||||
|
// //const isCollided = grabbing.mesh.intersectsMesh(hit.pickedMesh, false);
|
||||||
|
// //if (isCollided) {
|
||||||
|
// break;
|
||||||
|
// //}
|
||||||
|
// }
|
||||||
|
// grabbing.mesh.position = pos.clone();
|
||||||
|
//}
|
||||||
|
|
||||||
|
//const ray = new BABYLON.Ray(this.camera.position, this.camera.getDirection(BABYLON.Axis.Z), cm(1000));
|
||||||
|
//const hit = this.scene.pickWithRay(ray, (m) => m.name.includes('__COLLISION_WALL__'))!;
|
||||||
|
//if (hit.pickedMesh != null) {
|
||||||
|
// const grabbingBox = this.grabbing.mesh.getBoundingInfo().boundingBox;
|
||||||
|
// const grabDistanceVector = this.grabbing.mesh.position.subtract(this.camera.position);
|
||||||
|
// if (grabDistanceVector.length() > hit.distance) {
|
||||||
|
// this.grabbing.mesh.position = this.camera.position.add(dir.scale(hit.distance));
|
||||||
|
// this.grabbing.mesh.position.y = y;
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
|
||||||
|
//const displacementVector = new BABYLON.Vector3(
|
||||||
|
// this.grabbing.ghost.position.x - this.grabbing.mesh.position.x,
|
||||||
|
// 0,
|
||||||
|
// this.grabbing.ghost.position.z - this.grabbing.mesh.position.z,
|
||||||
|
//);
|
||||||
|
//this.grabbing.mesh.moveWithCollisions(displacementVector);
|
||||||
|
//this.grabbing.mesh.position.y = y;
|
||||||
|
|
||||||
|
//for (const soid of stickyObjectIds) {
|
||||||
|
// //const soMesh = this.objectMeshs.get(soid)!;
|
||||||
|
// //const offset = this.grabbing.mesh!.position.subtract(soMeshStartPosition);
|
||||||
|
// //soMesh.position = this.grabbing.mesh!.position.subtract(offset);
|
||||||
|
//}
|
||||||
|
|
||||||
|
grabbing.onMove?.({
|
||||||
|
position: newPos,
|
||||||
|
rotation: newRotation,
|
||||||
|
sticky: stickyOtherObject,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private highlightMeshes(meshes: BABYLON.AbstractMesh[]) {
|
private highlightMeshes(meshes: BABYLON.AbstractMesh[]) {
|
||||||
if (this.selectionOutlineLayer == null) return;
|
if (this.selectionOutlineLayer == null) return;
|
||||||
|
|
||||||
@@ -1422,15 +1378,14 @@ export class RoomEngine extends EventEmitter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public updateRoomLightColor(color: [number, number, number]) {
|
public updateRoomLightColor(color: [number, number, number]) {
|
||||||
if (this.roomLight.diffuse.equalsFloats(...color)) return;
|
this.envManager.updateRoomLightColor(new BABYLON.Color3(...color));
|
||||||
this.roomLight.diffuse = new BABYLON.Color3(...color);
|
|
||||||
this.roomState.roomLightColor = color;
|
this.roomState.roomLightColor = color;
|
||||||
this.ev('changeRoomState', { roomState: this.roomState });
|
this.ev('changeRoomState', { roomState: this.roomState });
|
||||||
}
|
}
|
||||||
|
|
||||||
private turnOnRoomLight(forInit = false) {
|
public turnOnRoomLight(forInit = false) {
|
||||||
if (!forInit) this.sr.disableSnapshotRendering(); // このメソッドは参照カウント方式な点に留意
|
if (!forInit) this.sr.disableSnapshotRendering(); // このメソッドは参照カウント方式な点に留意
|
||||||
this.roomLight.intensity = 18 * WORLD_SCALE * WORLD_SCALE;
|
this.envManager.turnOnRoomLight();
|
||||||
if (this.envManager?.envMapIndoor != null) this.envManager.envMapIndoor.level = 0.6;
|
if (this.envManager?.envMapIndoor != null) this.envManager.envMapIndoor.level = 0.6;
|
||||||
for (const m of this.scene.materials) {
|
for (const m of this.scene.materials) {
|
||||||
if (m.metadata?.disableEnvMap) {
|
if (m.metadata?.disableEnvMap) {
|
||||||
@@ -1446,9 +1401,9 @@ export class RoomEngine extends EventEmitter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private turnOffRoomLight() {
|
public turnOffRoomLight() {
|
||||||
this.sr.disableSnapshotRendering(); // このメソッドは参照カウント方式な点に留意
|
this.sr.disableSnapshotRendering(); // このメソッドは参照カウント方式な点に留意
|
||||||
this.roomLight.intensity = 0;
|
this.envManager.turnOffRoomLight();
|
||||||
if (this.envManager?.envMapIndoor != null) this.envManager.envMapIndoor.level = 0.025;
|
if (this.envManager?.envMapIndoor != null) this.envManager.envMapIndoor.level = 0.025;
|
||||||
for (const m of this.scene.materials) {
|
for (const m of this.scene.materials) {
|
||||||
if (m.metadata?.disableEnvMap) {
|
if (m.metadata?.disableEnvMap) {
|
||||||
@@ -1462,14 +1417,6 @@ export class RoomEngine extends EventEmitter {
|
|||||||
}, 10);
|
}, 10);
|
||||||
}
|
}
|
||||||
|
|
||||||
public toggleRoomLight() {
|
|
||||||
if (this.roomLight.intensity > 0) {
|
|
||||||
this.turnOffRoomLight();
|
|
||||||
} else {
|
|
||||||
this.turnOnRoomLight();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private createGhost(mesh: BABYLON.Mesh): BABYLON.Mesh {
|
private createGhost(mesh: BABYLON.Mesh): BABYLON.Mesh {
|
||||||
// 対象のメッシュの子に、「子にlightを持つメッシュ」が含まれているとエンジンがクラッシュするので、とりあえず適当なメッシュを使う
|
// 対象のメッシュの子に、「子にlightを持つメッシュ」が含まれているとエンジンがクラッシュするので、とりあえず適当なメッシュを使う
|
||||||
/*
|
/*
|
||||||
|
|||||||
@@ -6,7 +6,9 @@
|
|||||||
|
|
||||||
import * as BABYLON from '@babylonjs/core';
|
import * as BABYLON from '@babylonjs/core';
|
||||||
import { cm, WORLD_SCALE } from '../utility.js';
|
import { cm, WORLD_SCALE } from '../utility.js';
|
||||||
import { findMaterial } from './utility.js';
|
import { findMaterial, SYSTEM_HEYA_MESH_NAMES } from './utility.js';
|
||||||
|
import { GRAPHICS_QUALITY_MEDIUM } from './engine.js';
|
||||||
|
import type { RoomEngine } from './engine.js';
|
||||||
|
|
||||||
//export interface EnvManager<T = any> {
|
//export interface EnvManager<T = any> {
|
||||||
// constructor(onMeshUpdatedCallback?: ((meshes: BABYLON.AbstractMesh[]) => void) | null): void;
|
// constructor(onMeshUpdatedCallback?: ((meshes: BABYLON.AbstractMesh[]) => void) | null): void;
|
||||||
@@ -19,15 +21,36 @@ export abstract class EnvManager<T = any> {
|
|||||||
protected onMeshUpdatedCallback: ((meshes: BABYLON.AbstractMesh[]) => void) | null = null;
|
protected onMeshUpdatedCallback: ((meshes: BABYLON.AbstractMesh[]) => void) | null = null;
|
||||||
public abstract envMapIndoor: BABYLON.CubeTexture | null;
|
public abstract envMapIndoor: BABYLON.CubeTexture | null;
|
||||||
public abstract maxCameraZ: number;
|
public abstract maxCameraZ: number;
|
||||||
|
protected shadowGenerators: BABYLON.ShadowGenerator[] = [];
|
||||||
|
|
||||||
constructor(onMeshUpdatedCallback?: ((meshes: BABYLON.AbstractMesh[]) => void) | null) {
|
constructor(onMeshUpdatedCallback?: ((meshes: BABYLON.AbstractMesh[]) => void) | null) {
|
||||||
this.onMeshUpdatedCallback = onMeshUpdatedCallback ?? null;
|
this.onMeshUpdatedCallback = onMeshUpdatedCallback ?? null;
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract load(options: T, scene: BABYLON.Scene): Promise<void>;
|
abstract load(options: T, scene: BABYLON.Scene, engine: RoomEngine): Promise<void>;
|
||||||
abstract applyOptions(options: T): void;
|
abstract applyOptions(options: T): void;
|
||||||
abstract setTime(time: number): void;
|
abstract setTime(time: number): void;
|
||||||
abstract dispose(): void;
|
abstract updateRoomLightColor(color: BABYLON.Color3): void;
|
||||||
|
abstract turnOnRoomLight(): void;
|
||||||
|
abstract turnOffRoomLight(): void;
|
||||||
|
|
||||||
|
public addShadowCaster(mesh: BABYLON.AbstractMesh) {
|
||||||
|
for (const shadowGen of this.shadowGenerators) {
|
||||||
|
shadowGen.addShadowCaster(mesh);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public removeShadowCaster(mesh: BABYLON.AbstractMesh) {
|
||||||
|
for (const shadowGen of this.shadowGenerators) {
|
||||||
|
shadowGen.removeShadowCaster(mesh);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public dispose() {
|
||||||
|
for (const shadowGen of this.shadowGenerators) {
|
||||||
|
shadowGen.dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export type SimpleEnvOptions = {
|
export type SimpleEnvOptions = {
|
||||||
@@ -74,6 +97,8 @@ export class SimpleEnvManager extends EnvManager<SimpleEnvOptions> {
|
|||||||
private floorMaterial: BABYLON.PBRMaterial | null = null;
|
private floorMaterial: BABYLON.PBRMaterial | null = null;
|
||||||
private skybox: BABYLON.Mesh | null = null;
|
private skybox: BABYLON.Mesh | null = null;
|
||||||
private skyboxMat: BABYLON.StandardMaterial | null = null;
|
private skyboxMat: BABYLON.StandardMaterial | null = null;
|
||||||
|
private roomLight: BABYLON.SpotLight | null = null;
|
||||||
|
private sunLight: BABYLON.DirectionalLight | null = null;
|
||||||
public envMapIndoor: BABYLON.CubeTexture | null = null;
|
public envMapIndoor: BABYLON.CubeTexture | null = null;
|
||||||
public maxCameraZ = cm(1000);
|
public maxCameraZ = cm(1000);
|
||||||
|
|
||||||
@@ -81,7 +106,7 @@ export class SimpleEnvManager extends EnvManager<SimpleEnvOptions> {
|
|||||||
super(onMeshUpdatedCallback);
|
super(onMeshUpdatedCallback);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async load(options: SimpleEnvOptions, scene: BABYLON.Scene) {
|
public async load(options: SimpleEnvOptions, scene: BABYLON.Scene, engine: RoomEngine) {
|
||||||
this.skybox = BABYLON.MeshBuilder.CreateBox('skybox', { size: cm(1000) }, scene);
|
this.skybox = BABYLON.MeshBuilder.CreateBox('skybox', { size: cm(1000) }, scene);
|
||||||
this.skyboxMat = new BABYLON.StandardMaterial('skyboxMat', scene);
|
this.skyboxMat = new BABYLON.StandardMaterial('skyboxMat', scene);
|
||||||
this.skyboxMat.backFaceCulling = false;
|
this.skyboxMat.backFaceCulling = false;
|
||||||
@@ -89,6 +114,43 @@ export class SimpleEnvManager extends EnvManager<SimpleEnvOptions> {
|
|||||||
this.skybox.material = this.skyboxMat;
|
this.skybox.material = this.skyboxMat;
|
||||||
this.skybox.infiniteDistance = true;
|
this.skybox.infiniteDistance = true;
|
||||||
|
|
||||||
|
this.roomLight = new BABYLON.SpotLight('roomLight', new BABYLON.Vector3(0, cm(249), 0), new BABYLON.Vector3(0, -1, 0), 16, 8, scene);
|
||||||
|
this.roomLight.diffuse = new BABYLON.Color3(...engine.roomState.roomLightColor);
|
||||||
|
this.roomLight.shadowMinZ = cm(10);
|
||||||
|
this.roomLight.shadowMaxZ = cm(300);
|
||||||
|
this.roomLight.radius = cm(30);
|
||||||
|
|
||||||
|
if (engine.graphicsQuality >= GRAPHICS_QUALITY_MEDIUM) {
|
||||||
|
const shadowGeneratorForRoomLight = new BABYLON.ShadowGenerator(engine.graphicsQuality <= GRAPHICS_QUALITY_MEDIUM ? 1024 : 2048, this.roomLight);
|
||||||
|
shadowGeneratorForRoomLight.forceBackFacesOnly = true;
|
||||||
|
shadowGeneratorForRoomLight.bias = 0.00001;
|
||||||
|
shadowGeneratorForRoomLight.normalBias = 0.005;
|
||||||
|
shadowGeneratorForRoomLight.usePercentageCloserFiltering = true;
|
||||||
|
shadowGeneratorForRoomLight.filteringQuality = BABYLON.ShadowGenerator.QUALITY_HIGH;
|
||||||
|
if (engine.graphicsQuality <= GRAPHICS_QUALITY_MEDIUM) {
|
||||||
|
shadowGeneratorForRoomLight.getShadowMap().refreshRate = 60;
|
||||||
|
}
|
||||||
|
//this.shadowGeneratorForRoomLight.useContactHardeningShadow = true;
|
||||||
|
this.shadowGenerators.push(shadowGeneratorForRoomLight);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (engine.graphicsQuality >= GRAPHICS_QUALITY_MEDIUM) {
|
||||||
|
this.sunLight = new BABYLON.DirectionalLight('sunLight', new BABYLON.Vector3(0.2, -1, -1), scene);
|
||||||
|
this.sunLight.position = new BABYLON.Vector3(cm(-20), cm(1000), cm(1000));
|
||||||
|
this.sunLight.shadowMinZ = cm(1000);
|
||||||
|
this.sunLight.shadowMaxZ = cm(2000);
|
||||||
|
|
||||||
|
const shadowGeneratorForSunLight = new BABYLON.ShadowGenerator(engine.graphicsQuality <= GRAPHICS_QUALITY_MEDIUM ? 1024 : 2048, this.sunLight);
|
||||||
|
shadowGeneratorForSunLight.forceBackFacesOnly = true;
|
||||||
|
shadowGeneratorForSunLight.bias = 0.00001;
|
||||||
|
shadowGeneratorForSunLight.usePercentageCloserFiltering = true;
|
||||||
|
shadowGeneratorForSunLight.usePoissonSampling = true;
|
||||||
|
if (engine.graphicsQuality <= GRAPHICS_QUALITY_MEDIUM) {
|
||||||
|
shadowGeneratorForSunLight.getShadowMap().refreshRate = 60;
|
||||||
|
}
|
||||||
|
this.shadowGenerators.push(shadowGeneratorForSunLight);
|
||||||
|
}
|
||||||
|
|
||||||
this.loaderResult = await BABYLON.ImportMeshAsync('/client-assets/room/envs/default/300.glb', scene);
|
this.loaderResult = await BABYLON.ImportMeshAsync('/client-assets/room/envs/default/300.glb', scene);
|
||||||
|
|
||||||
this.envMapIndoor = BABYLON.CubeTexture.CreateFromPrefilteredData('/client-assets/room/indoor.env', scene);
|
this.envMapIndoor = BABYLON.CubeTexture.CreateFromPrefilteredData('/client-assets/room/indoor.env', scene);
|
||||||
@@ -182,6 +244,15 @@ export class SimpleEnvManager extends EnvManager<SimpleEnvOptions> {
|
|||||||
const baseboardMaterial = findMaterial(this.meshes[0], '__BASEBOARD__');
|
const baseboardMaterial = findMaterial(this.meshes[0], '__BASEBOARD__');
|
||||||
//baseboardMaterial.metadata.disableEnvMap = true;
|
//baseboardMaterial.metadata.disableEnvMap = true;
|
||||||
|
|
||||||
|
for (const mesh of this.meshes) {
|
||||||
|
if (SYSTEM_HEYA_MESH_NAMES.some(name => mesh.name.includes(name))) continue;
|
||||||
|
mesh.receiveShadows = true;
|
||||||
|
|
||||||
|
if (mesh.material !== this.floorMaterial) { // 床は他の何にも影を落とさないことが確定している
|
||||||
|
this.addShadowCaster(mesh);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
await this.applyOptions(options);
|
await this.applyOptions(options);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -193,6 +264,26 @@ export class SimpleEnvManager extends EnvManager<SimpleEnvOptions> {
|
|||||||
} else {
|
} else {
|
||||||
this.skyboxMat.emissiveColor = new BABYLON.Color3(0.05, 0.05, 0.2);
|
this.skyboxMat.emissiveColor = new BABYLON.Color3(0.05, 0.05, 0.2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.sunLight != null) {
|
||||||
|
this.sunLight.diffuse = time === 0 ? new BABYLON.Color3(1.0, 0.9, 0.8) : time === 1 ? new BABYLON.Color3(1.0, 0.8, 0.6) : new BABYLON.Color3(0.6, 0.8, 1.0);
|
||||||
|
this.sunLight.intensity = time === 0 ? 3 : time === 1 ? 1 : 0.25;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public updateRoomLightColor(color: BABYLON.Color3): void {
|
||||||
|
if (this.roomLight == null) return;
|
||||||
|
this.roomLight.diffuse = color;
|
||||||
|
}
|
||||||
|
|
||||||
|
public turnOnRoomLight(): void {
|
||||||
|
if (this.roomLight == null) return;
|
||||||
|
this.roomLight.intensity = 18 * WORLD_SCALE * WORLD_SCALE;
|
||||||
|
}
|
||||||
|
|
||||||
|
public turnOffRoomLight(): void {
|
||||||
|
if (this.roomLight == null) return;
|
||||||
|
this.roomLight.intensity = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public applyOptions(options: SimpleEnvOptions) {
|
public applyOptions(options: SimpleEnvOptions) {
|
||||||
@@ -362,6 +453,9 @@ export class SimpleEnvManager extends EnvManager<SimpleEnvOptions> {
|
|||||||
this.skybox?.dispose();
|
this.skybox?.dispose();
|
||||||
this.skyboxMat?.dispose();
|
this.skyboxMat?.dispose();
|
||||||
this.envMapIndoor?.dispose();
|
this.envMapIndoor?.dispose();
|
||||||
|
this.roomLight?.dispose();
|
||||||
|
this.sunLight?.dispose();
|
||||||
|
super.dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -431,5 +525,6 @@ export class MuseumEnvManager extends EnvManager<MuseumEnvOptions> {
|
|||||||
m.dispose(false, true);
|
m.dispose(false, true);
|
||||||
}
|
}
|
||||||
this.envMapIndoor?.dispose();
|
this.envMapIndoor?.dispose();
|
||||||
|
super.dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user