mirror of
https://github.com/misskey-dev/misskey.git
synced 2026-05-13 23:25:41 +02:00
wip
This commit is contained in:
@@ -35,6 +35,7 @@ export class RoomController {
|
||||
public isReady = ref(false);
|
||||
public isSitting = ref(false);
|
||||
public isEditMode = ref(false);
|
||||
public isRoomLightOn = ref(true);
|
||||
public grabbing = ref<{ forInstall: boolean } | null>(null);
|
||||
public gridSnapping = ref({ enabled: true, scale: cm(4) });
|
||||
public selected = ref<{
|
||||
@@ -279,6 +280,7 @@ export class RoomController {
|
||||
this.isReady.value = false;
|
||||
this.isSitting.value = false;
|
||||
this.isEditMode.value = false;
|
||||
this.isRoomLightOn.value = true;
|
||||
this.grabbing.value = null;
|
||||
this.selected.value = null;
|
||||
this.initializeProgress.value = 0;
|
||||
@@ -372,7 +374,12 @@ export class RoomController {
|
||||
}
|
||||
|
||||
public toggleRoomLight() {
|
||||
this.call('toggleRoomLight');
|
||||
if (this.isRoomLightOn.value) {
|
||||
this.call('turnOffRoomLight');
|
||||
} else {
|
||||
this.call('turnOnRoomLight');
|
||||
}
|
||||
this.isRoomLightOn.value = !this.isRoomLightOn.value;
|
||||
}
|
||||
|
||||
public resize() {
|
||||
|
||||
@@ -124,8 +124,6 @@ export class RoomEngine extends EventEmitter {
|
||||
private useGlow: boolean;
|
||||
private engine: BABYLON.WebGPUEngine;
|
||||
public scene: BABYLON.Scene;
|
||||
private shadowGeneratorForRoomLight: BABYLON.ShadowGenerator | null = null;
|
||||
private shadowGeneratorForSunLight: BABYLON.ShadowGenerator | null = null;
|
||||
public camera: BABYLON.UniversalCamera;
|
||||
public objectEntities: Map<string, {
|
||||
rootMesh: BABYLON.Mesh;
|
||||
@@ -187,7 +185,6 @@ export class RoomEngine extends EventEmitter {
|
||||
}
|
||||
|
||||
private putParticleSystem: BABYLON.ParticleSystem;
|
||||
private roomLight: BABYLON.SpotLight; // TODO: これの管理もenv側に持ってく?
|
||||
public lightContainer: BABYLON.ClusteredLightContainer;
|
||||
private gridMaterial: GridMaterial | null = null;
|
||||
private gridPlane: BABYLON.Mesh;
|
||||
@@ -196,6 +193,7 @@ export class RoomEngine extends EventEmitter {
|
||||
public sr: BABYLON.SnapshotRenderingHelper;
|
||||
private gl: BABYLON.GlowLayer | null = null;
|
||||
public timer: Timer = new Timer();
|
||||
public graphicsQuality: number;
|
||||
|
||||
private _isEditMode = false;
|
||||
get isEditMode() {
|
||||
@@ -233,9 +231,9 @@ export class RoomEngine extends EventEmitter {
|
||||
options: { ...getObjectDef(o.type).options.default, ...o.options },
|
||||
})),
|
||||
};
|
||||
|
||||
this.graphicsQuality = options.graphicsQuality;
|
||||
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];
|
||||
|
||||
registerBuiltInLoaders();
|
||||
@@ -279,51 +277,12 @@ export class RoomEngine extends EventEmitter {
|
||||
|
||||
//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.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.horizontalTiles = 32;
|
||||
this.lightContainer.depthSlices = 32;
|
||||
|
||||
this.turnOnRoomLight(true);
|
||||
|
||||
if (this.useGlow) {
|
||||
this.gl = new BABYLON.GlowLayer('glow', this.scene, {
|
||||
//mainTextureFixedSize: 512,
|
||||
@@ -363,13 +322,13 @@ export class RoomEngine extends EventEmitter {
|
||||
this.gridPlane.isVisible = 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.scene.setRenderingAutoClearDepthStencil(this.selectionOutlineLayer.renderingGroupId, false);
|
||||
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);
|
||||
if (options.antialias) {
|
||||
pipeline.samples = 4;
|
||||
@@ -574,238 +533,6 @@ export class RoomEngine extends EventEmitter {
|
||||
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) {
|
||||
this.roomState.env.type = type;
|
||||
|
||||
@@ -829,9 +556,6 @@ export class RoomEngine extends EventEmitter {
|
||||
|
||||
m.isPickable = false;
|
||||
m.checkCollisions = false;
|
||||
m.receiveShadows = true;
|
||||
this.shadowGeneratorForRoomLight?.addShadowCaster(m);
|
||||
this.shadowGeneratorForSunLight?.addShadowCaster(m);
|
||||
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
|
||||
}
|
||||
@@ -848,7 +572,7 @@ export class RoomEngine extends EventEmitter {
|
||||
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);
|
||||
|
||||
for (const mat of this.scene.materials) {
|
||||
@@ -867,6 +591,7 @@ export class RoomEngine extends EventEmitter {
|
||||
}
|
||||
|
||||
this.envManager = envManager;
|
||||
this.turnOnRoomLight(true);
|
||||
|
||||
this.camera.maxZ = this.envManager.maxCameraZ;
|
||||
|
||||
@@ -1112,8 +837,7 @@ export class RoomEngine extends EventEmitter {
|
||||
if (def.receiveShadows !== false) mesh.receiveShadows = true;
|
||||
if (def.castShadows !== false) {
|
||||
// TODO: メモリリークしそうだからいい感じにする
|
||||
this.shadowGeneratorForRoomLight?.addShadowCaster(mesh);
|
||||
this.shadowGeneratorForSunLight?.addShadowCaster(mesh);
|
||||
this.envManager.addShadowCaster(mesh);
|
||||
}
|
||||
|
||||
//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 };
|
||||
}
|
||||
|
||||
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[]) {
|
||||
if (this.selectionOutlineLayer == null) return;
|
||||
|
||||
@@ -1422,15 +1378,14 @@ export class RoomEngine extends EventEmitter {
|
||||
}
|
||||
|
||||
public updateRoomLightColor(color: [number, number, number]) {
|
||||
if (this.roomLight.diffuse.equalsFloats(...color)) return;
|
||||
this.roomLight.diffuse = new BABYLON.Color3(...color);
|
||||
this.envManager.updateRoomLightColor(new BABYLON.Color3(...color));
|
||||
this.roomState.roomLightColor = color;
|
||||
this.ev('changeRoomState', { roomState: this.roomState });
|
||||
}
|
||||
|
||||
private turnOnRoomLight(forInit = false) {
|
||||
public turnOnRoomLight(forInit = false) {
|
||||
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;
|
||||
for (const m of this.scene.materials) {
|
||||
if (m.metadata?.disableEnvMap) {
|
||||
@@ -1446,9 +1401,9 @@ export class RoomEngine extends EventEmitter {
|
||||
}
|
||||
}
|
||||
|
||||
private turnOffRoomLight() {
|
||||
public turnOffRoomLight() {
|
||||
this.sr.disableSnapshotRendering(); // このメソッドは参照カウント方式な点に留意
|
||||
this.roomLight.intensity = 0;
|
||||
this.envManager.turnOffRoomLight();
|
||||
if (this.envManager?.envMapIndoor != null) this.envManager.envMapIndoor.level = 0.025;
|
||||
for (const m of this.scene.materials) {
|
||||
if (m.metadata?.disableEnvMap) {
|
||||
@@ -1462,14 +1417,6 @@ export class RoomEngine extends EventEmitter {
|
||||
}, 10);
|
||||
}
|
||||
|
||||
public toggleRoomLight() {
|
||||
if (this.roomLight.intensity > 0) {
|
||||
this.turnOffRoomLight();
|
||||
} else {
|
||||
this.turnOnRoomLight();
|
||||
}
|
||||
}
|
||||
|
||||
private createGhost(mesh: BABYLON.Mesh): BABYLON.Mesh {
|
||||
// 対象のメッシュの子に、「子にlightを持つメッシュ」が含まれているとエンジンがクラッシュするので、とりあえず適当なメッシュを使う
|
||||
/*
|
||||
|
||||
@@ -6,7 +6,9 @@
|
||||
|
||||
import * as BABYLON from '@babylonjs/core';
|
||||
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> {
|
||||
// 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;
|
||||
public abstract envMapIndoor: BABYLON.CubeTexture | null;
|
||||
public abstract maxCameraZ: number;
|
||||
protected shadowGenerators: BABYLON.ShadowGenerator[] = [];
|
||||
|
||||
constructor(onMeshUpdatedCallback?: ((meshes: BABYLON.AbstractMesh[]) => void) | 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 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 = {
|
||||
@@ -74,6 +97,8 @@ export class SimpleEnvManager extends EnvManager<SimpleEnvOptions> {
|
||||
private floorMaterial: BABYLON.PBRMaterial | null = null;
|
||||
private skybox: BABYLON.Mesh | 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 maxCameraZ = cm(1000);
|
||||
|
||||
@@ -81,7 +106,7 @@ export class SimpleEnvManager extends EnvManager<SimpleEnvOptions> {
|
||||
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.skyboxMat = new BABYLON.StandardMaterial('skyboxMat', scene);
|
||||
this.skyboxMat.backFaceCulling = false;
|
||||
@@ -89,6 +114,43 @@ export class SimpleEnvManager extends EnvManager<SimpleEnvOptions> {
|
||||
this.skybox.material = this.skyboxMat;
|
||||
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.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__');
|
||||
//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);
|
||||
}
|
||||
|
||||
@@ -193,6 +264,26 @@ export class SimpleEnvManager extends EnvManager<SimpleEnvOptions> {
|
||||
} else {
|
||||
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) {
|
||||
@@ -362,6 +453,9 @@ export class SimpleEnvManager extends EnvManager<SimpleEnvOptions> {
|
||||
this.skybox?.dispose();
|
||||
this.skyboxMat?.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);
|
||||
}
|
||||
this.envMapIndoor?.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user