mirror of
https://github.com/misskey-dev/misskey.git
synced 2026-08-01 12:56:03 +00:00
wip
This commit is contained in:
@@ -5,6 +5,7 @@
|
||||
|
||||
import * as BABYLON from '@babylonjs/core/pure.js';
|
||||
import EventEmitter from 'eventemitter3';
|
||||
import { PlayerContainer, type PlayerProfile, type PlayerState } from './PlayerContainer.js';
|
||||
|
||||
const IN_WEB_WORKER = typeof window === 'undefined';
|
||||
|
||||
@@ -22,9 +23,15 @@ export abstract class EngineBase<EVs extends EngineBaseEvents> extends EventEmit
|
||||
public scene: BABYLON.Scene;
|
||||
abstract sr: BABYLON.SnapshotRenderingHelper;
|
||||
abstract lightContainer: BABYLON.ClusteredLightContainer;
|
||||
abstract getEnvMap(): BABYLON.CubeTexture | null;
|
||||
protected fps: number | null = null;
|
||||
protected disposed = false;
|
||||
|
||||
protected playerProfiles: Record<string, PlayerProfile> = {};
|
||||
protected playerContainers: PlayerContainer[] = [];
|
||||
protected showUsernameOnAvatar: boolean;
|
||||
protected show2dAvatarOnAvatar: boolean;
|
||||
|
||||
public inputs: EventEmitter<{
|
||||
'click': (event: { x: number; y: number; }) => void;
|
||||
'keydown': (event: { code: string; shiftKey: boolean; }) => void;
|
||||
@@ -114,6 +121,105 @@ export abstract class EngineBase<EVs extends EngineBaseEvents> extends EventEmit
|
||||
|
||||
public abstract resize(): void;
|
||||
|
||||
public updatePlayerProfiles(profiles: Record<string, PlayerProfile>) {
|
||||
this.playerProfiles = profiles;
|
||||
|
||||
for (const playerContainer of this.playerContainers) {
|
||||
if (this.playerProfiles[playerContainer.id] == null) {
|
||||
this.sr.disableSnapshotRendering();
|
||||
playerContainer.destroy();
|
||||
this.sr.enableSnapshotRendering();
|
||||
}
|
||||
}
|
||||
this.playerContainers = this.playerContainers.filter(p => this.playerProfiles[p.id] != null);
|
||||
}
|
||||
|
||||
public updatePlayerStates(states: Record<string, PlayerState>) {
|
||||
for (const [k, v] of Object.entries(this.playerProfiles)) {
|
||||
const playerContainer = this.playerContainers.find(p => p.id === k);
|
||||
if (playerContainer == null) {
|
||||
const p = new PlayerContainer({
|
||||
id: k,
|
||||
profile: v,
|
||||
state: states[k],
|
||||
scene: this.scene,
|
||||
sr: this.sr,
|
||||
showUsername: this.showUsernameOnAvatar,
|
||||
show2dAvatar: this.show2dAvatarOnAvatar,
|
||||
});
|
||||
// TODO: loadFurnitureのものとある程度共通化
|
||||
p.registerMeshes = (meshes) => {
|
||||
for (const mesh of meshes) {
|
||||
mesh.receiveShadows = false;
|
||||
mesh.metadata = { isPlayer: true, playerId: k };
|
||||
|
||||
//if (mesh.material) (mesh.material as BABYLON.PBRMaterial).ambientColor = new BABYLON.Color3(0.2, 0.2, 0.2);
|
||||
if (mesh.material) {
|
||||
if (mesh.material instanceof BABYLON.MultiMaterial) {
|
||||
for (const subMat of mesh.material.subMaterials) {
|
||||
if ((subMat as BABYLON.PBRMaterial).subSurface.isRefractionEnabled) {
|
||||
(subMat as BABYLON.PBRMaterial).subSurface.isRefractionEnabled = false; // 有効にするとドローコールが激増する
|
||||
(subMat as BABYLON.PBRMaterial).transparencyMode = BABYLON.PBRMaterial.PBRMATERIAL_ALPHABLEND;
|
||||
(subMat as BABYLON.PBRMaterial).alpha = 0.5;
|
||||
(subMat as BABYLON.PBRMaterial).metallic = 1;
|
||||
}
|
||||
(subMat as BABYLON.PBRMaterial).reflectionTexture = this.getEnvMap();
|
||||
if ((subMat as BABYLON.PBRMaterial).metadata == null) (subMat as BABYLON.PBRMaterial).metadata = {};
|
||||
(subMat as BABYLON.PBRMaterial).metadata.useEnvMap = true;
|
||||
(subMat 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
|
||||
(subMat as BABYLON.PBRMaterial).anisotropy.isEnabled = false; // なんかきれいにレンダリングされないため
|
||||
}
|
||||
} else {
|
||||
if ((mesh.material as BABYLON.PBRMaterial).subSurface.isRefractionEnabled) {
|
||||
(mesh.material as BABYLON.PBRMaterial).subSurface.isRefractionEnabled = false; // 有効にするとドローコールが激増する
|
||||
(mesh.material as BABYLON.PBRMaterial).transparencyMode = BABYLON.PBRMaterial.PBRMATERIAL_ALPHABLEND;
|
||||
(mesh.material as BABYLON.PBRMaterial).alpha = 0.5;
|
||||
(mesh.material as BABYLON.PBRMaterial).metallic = 1;
|
||||
}
|
||||
(mesh.material as BABYLON.PBRMaterial).reflectionTexture = this.getEnvMap();
|
||||
if ((mesh.material as BABYLON.PBRMaterial).metadata == null) (mesh.material as BABYLON.PBRMaterial).metadata = {};
|
||||
(mesh.material as BABYLON.PBRMaterial).metadata.useEnvMap = true;
|
||||
(mesh.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
|
||||
(mesh.material as BABYLON.PBRMaterial).anisotropy.isEnabled = false; // なんかきれいにレンダリングされないため
|
||||
}
|
||||
}
|
||||
|
||||
if (!this.scene.meshes.includes(mesh)) this.scene.addMesh(mesh);
|
||||
}
|
||||
};
|
||||
p.loadAvatar().then(() => {
|
||||
this.sr.disableSnapshotRendering();
|
||||
this.sr.enableSnapshotRendering();
|
||||
});
|
||||
this.playerContainers.push(p);
|
||||
} else {
|
||||
if (states[k] != null) {
|
||||
playerContainer.applyState(states[k]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public clearPlayers() {
|
||||
this.sr.disableSnapshotRendering();
|
||||
for (const playerContainer of this.playerContainers) {
|
||||
playerContainer.destroy();
|
||||
}
|
||||
this.sr.enableSnapshotRendering();
|
||||
this.playerContainers = [];
|
||||
}
|
||||
|
||||
public updateAvatarDisplayOptions(options: { showUsername: boolean; show2dAvatar: boolean }) {
|
||||
this.showUsernameOnAvatar = options.showUsername;
|
||||
this.show2dAvatarOnAvatar = options.show2dAvatar;
|
||||
|
||||
this.sr.disableSnapshotRendering();
|
||||
for (const playerContainer of this.playerContainers) {
|
||||
playerContainer.updateUserInfoDisplayOptions(options);
|
||||
}
|
||||
this.sr.enableSnapshotRendering();
|
||||
}
|
||||
|
||||
public destroy() {
|
||||
this.engine.stopRenderLoop();
|
||||
if (this.currentRafId != null) {
|
||||
@@ -121,6 +227,9 @@ export abstract class EngineBase<EVs extends EngineBaseEvents> extends EventEmit
|
||||
cancelAnimationFrame(this.currentRafId);
|
||||
this.currentRafId = null;
|
||||
}
|
||||
for (const playerContainer of this.playerContainers) {
|
||||
playerContainer.destroy();
|
||||
}
|
||||
this.engine.dispose();
|
||||
this.scene.dispose();
|
||||
this.disposed = true;
|
||||
|
||||
@@ -40,10 +40,6 @@ export class WorldEngine extends EngineBase<{
|
||||
private fov: number;
|
||||
private useGlow: boolean;
|
||||
public graphicsQuality: number;
|
||||
private playerProfiles: Record<string, PlayerProfile> = {};
|
||||
private playerContainers: PlayerContainer[] = [];
|
||||
private showUsernameOnAvatar: boolean;
|
||||
private show2dAvatarOnAvatar: boolean;
|
||||
private envManager: WorldEnvManager | null = null;
|
||||
private inited = false;
|
||||
private isGodMode = false;
|
||||
|
||||
@@ -195,10 +195,9 @@ export class RoomEngine extends EngineBase<{
|
||||
this.ev('changeSittingState', { isSitting: v });
|
||||
}
|
||||
|
||||
private playerProfiles: Record<string, PlayerProfile> = {};
|
||||
private playerContainers: PlayerContainer[] = [];
|
||||
private showUsernameOnAvatar: boolean;
|
||||
private show2dAvatarOnAvatar: boolean;
|
||||
public getEnvMap(): BABYLON.CubeTexture | null {
|
||||
return this.envManager?.envMapIndoor ?? null;
|
||||
}
|
||||
|
||||
private inited = false;
|
||||
|
||||
@@ -1556,109 +1555,6 @@ export class RoomEngine extends EngineBase<{
|
||||
this.ev('playSfxUrl', { url, options });
|
||||
}
|
||||
|
||||
public updatePlayerProfiles(profiles: Record<string, PlayerProfile>) {
|
||||
this.playerProfiles = profiles;
|
||||
|
||||
for (const playerContainer of this.playerContainers) {
|
||||
if (this.playerProfiles[playerContainer.id] == null) {
|
||||
this.sr.disableSnapshotRendering();
|
||||
playerContainer.destroy();
|
||||
this.sr.enableSnapshotRendering();
|
||||
}
|
||||
}
|
||||
this.playerContainers = this.playerContainers.filter(p => this.playerProfiles[p.id] != null);
|
||||
}
|
||||
|
||||
public updatePlayerStates(states: Record<string, PlayerState>) {
|
||||
for (const [k, v] of Object.entries(this.playerProfiles)) {
|
||||
const playerContainer = this.playerContainers.find(p => p.id === k);
|
||||
if (playerContainer == null) {
|
||||
const p = new PlayerContainer({
|
||||
id: k,
|
||||
profile: v,
|
||||
state: states[k],
|
||||
scene: this.scene,
|
||||
sr: this.sr,
|
||||
showUsername: this.showUsernameOnAvatar,
|
||||
show2dAvatar: this.show2dAvatarOnAvatar,
|
||||
});
|
||||
// TODO: loadFurnitureのものとある程度共通化
|
||||
p.registerMeshes = (meshes) => {
|
||||
for (const mesh of meshes) {
|
||||
mesh.receiveShadows = false;
|
||||
if (SYSTEM_MESH_NAMES.some(n => mesh.name.includes(n))) {
|
||||
mesh.isVisible = false;
|
||||
} else {
|
||||
mesh.metadata = { isPlayer: true, playerId: k };
|
||||
|
||||
//if (mesh.material) (mesh.material as BABYLON.PBRMaterial).ambientColor = new BABYLON.Color3(0.2, 0.2, 0.2);
|
||||
if (mesh.material) {
|
||||
if (mesh.material instanceof BABYLON.MultiMaterial) {
|
||||
for (const subMat of mesh.material.subMaterials) {
|
||||
if ((subMat as BABYLON.PBRMaterial).subSurface.isRefractionEnabled) {
|
||||
(subMat as BABYLON.PBRMaterial).subSurface.isRefractionEnabled = false; // 有効にするとドローコールが激増する
|
||||
(subMat as BABYLON.PBRMaterial).transparencyMode = BABYLON.PBRMaterial.PBRMATERIAL_ALPHABLEND;
|
||||
(subMat as BABYLON.PBRMaterial).alpha = 0.5;
|
||||
(subMat as BABYLON.PBRMaterial).metallic = 1;
|
||||
}
|
||||
(subMat as BABYLON.PBRMaterial).reflectionTexture = this.envManager?.envMapIndoor;
|
||||
if ((subMat as BABYLON.PBRMaterial).metadata == null) (subMat as BABYLON.PBRMaterial).metadata = {};
|
||||
(subMat as BABYLON.PBRMaterial).metadata.useEnvMap = true;
|
||||
(subMat 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
|
||||
(subMat as BABYLON.PBRMaterial).anisotropy.isEnabled = false; // なんかきれいにレンダリングされないため
|
||||
}
|
||||
} else {
|
||||
if ((mesh.material as BABYLON.PBRMaterial).subSurface.isRefractionEnabled) {
|
||||
(mesh.material as BABYLON.PBRMaterial).subSurface.isRefractionEnabled = false; // 有効にするとドローコールが激増する
|
||||
(mesh.material as BABYLON.PBRMaterial).transparencyMode = BABYLON.PBRMaterial.PBRMATERIAL_ALPHABLEND;
|
||||
(mesh.material as BABYLON.PBRMaterial).alpha = 0.5;
|
||||
(mesh.material as BABYLON.PBRMaterial).metallic = 1;
|
||||
}
|
||||
(mesh.material as BABYLON.PBRMaterial).reflectionTexture = this.envManager?.envMapIndoor;
|
||||
if ((mesh.material as BABYLON.PBRMaterial).metadata == null) (mesh.material as BABYLON.PBRMaterial).metadata = {};
|
||||
(mesh.material as BABYLON.PBRMaterial).metadata.useEnvMap = true;
|
||||
(mesh.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
|
||||
(mesh.material as BABYLON.PBRMaterial).anisotropy.isEnabled = false; // なんかきれいにレンダリングされないため
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!this.scene.meshes.includes(mesh)) this.scene.addMesh(mesh);
|
||||
}
|
||||
};
|
||||
p.loadAvatar().then(() => {
|
||||
this.sr.disableSnapshotRendering();
|
||||
this.sr.enableSnapshotRendering();
|
||||
});
|
||||
this.playerContainers.push(p);
|
||||
} else {
|
||||
if (states[k] != null) {
|
||||
playerContainer.applyState(states[k]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public clearPlayers() {
|
||||
this.sr.disableSnapshotRendering();
|
||||
for (const playerContainer of this.playerContainers) {
|
||||
playerContainer.destroy();
|
||||
}
|
||||
this.sr.enableSnapshotRendering();
|
||||
this.playerContainers = [];
|
||||
}
|
||||
|
||||
public updateAvatarDisplayOptions(options: { showUsername: boolean; show2dAvatar: boolean }) {
|
||||
this.showUsernameOnAvatar = options.showUsername;
|
||||
this.show2dAvatarOnAvatar = options.show2dAvatar;
|
||||
|
||||
this.sr.disableSnapshotRendering();
|
||||
for (const playerContainer of this.playerContainers) {
|
||||
playerContainer.updateUserInfoDisplayOptions(options);
|
||||
}
|
||||
this.sr.enableSnapshotRendering();
|
||||
}
|
||||
|
||||
public resize() {
|
||||
// 一旦snapshot renderingを無効にしておかないとエラーが出る
|
||||
// ~~...が、一旦無効にしたらしたで複数のマテリアルがそれぞれ入れ替わる(?)という謎の現象が発生するためコメントアウトしとく(エラー出てもレンダリングが止まったりするわけでもないし)~~
|
||||
|
||||
@@ -85,7 +85,7 @@ import { prefer } from '@/preferences.js';
|
||||
import { isTouchUsing } from '@/utility/touch.js';
|
||||
import { deviceKind } from '@/utility/device-kind.js';
|
||||
import MkProgressBar from '@/components/MkProgressBar.vue';
|
||||
import { RoomMultiplayer } from '@/world/room/multiplayer.js';
|
||||
import { WorldMultiplayer } from '@/world/multiplayer.js';
|
||||
import { $i } from '@/i.js';
|
||||
import { userPage } from '@/filters/user.js';
|
||||
import MkUserCardMini from '@/components/MkUserCardMini.vue';
|
||||
@@ -132,7 +132,7 @@ const worldControllerOptions = computed<WorldEngineControllerOptions>(() => ({
|
||||
}));
|
||||
|
||||
const controller = markRaw(new WorldEngineController(worldControllerOptions.value));
|
||||
const multiplayer = markRaw(new RoomMultiplayer(0, controller));
|
||||
const multiplayer = markRaw(new WorldMultiplayer('0', controller));
|
||||
|
||||
const pointedPlayerInfo = ref<PlayerProfile | null>(null);
|
||||
const isMenuShowing = ref(!isNarrow);
|
||||
@@ -200,12 +200,12 @@ onMounted(async () => {
|
||||
});
|
||||
|
||||
useInterval(() => {
|
||||
//multiplayer.updateState(controller.myPlayerState.value);
|
||||
multiplayer.updateState(controller.myPlayerState.value);
|
||||
}, 100, { immediate: false, afterMounted: true });
|
||||
|
||||
onDeactivated(() => {
|
||||
controller.destroy();
|
||||
//multiplayer.dispose();
|
||||
multiplayer.dispose();
|
||||
|
||||
window.removeEventListener('resize', resize);
|
||||
});
|
||||
@@ -216,7 +216,7 @@ onActivated(() => {
|
||||
|
||||
onUnmounted(() => {
|
||||
controller.destroy();
|
||||
//multiplayer.dispose();
|
||||
multiplayer.dispose();
|
||||
|
||||
window.removeEventListener('resize', resize);
|
||||
});
|
||||
@@ -336,9 +336,20 @@ function showOtherMenu(ev: PointerEvent) {
|
||||
}], ev.currentTarget ?? ev.target);
|
||||
}
|
||||
|
||||
function leaveOnline() {
|
||||
multiplayer.left();
|
||||
}
|
||||
|
||||
function enterOnline() {
|
||||
const closeWaiting = os.waiting();
|
||||
multiplayer.enter().finally(() => {
|
||||
closeWaiting();
|
||||
});
|
||||
}
|
||||
|
||||
definePage(() => ({
|
||||
title: 'Room',
|
||||
icon: 'ti ti-door',
|
||||
title: 'World',
|
||||
icon: 'ti ti-universe',
|
||||
needWideArea: true,
|
||||
}));
|
||||
</script>
|
||||
|
||||
@@ -16,7 +16,7 @@ import { deepEqual } from '@/utility/deep-equal.js';
|
||||
export class WorldMultiplayer {
|
||||
public isOnline = ref(false);
|
||||
private controller: WorldEngineController;
|
||||
private connection: Misskey.IChannelConnection<Misskey.Channels['worldRoom']> | null = null;
|
||||
private connection: Misskey.IChannelConnection<Misskey.Channels['world']> | null = null;
|
||||
private dimensionId: string;
|
||||
public playerProfiles: Record<string, PlayerProfile> = {};
|
||||
|
||||
@@ -31,7 +31,7 @@ export class WorldMultiplayer {
|
||||
|
||||
public enter() {
|
||||
const p = new Promise<void>((resolve, reject) => {
|
||||
this.connection = useStream().useChannel('worldRoom', {
|
||||
this.connection = useStream().useChannel('world', {
|
||||
roomId: this.dimensionId,
|
||||
});
|
||||
this.connection.once('entered', ({ playerProfiles }) => {
|
||||
|
||||
Reference in New Issue
Block a user