diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index e6272e4946..9ab6a31864 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -3568,6 +3568,7 @@ _miWorld: failedToInitialize: "初期化に失敗しました" crushed_description: "描画が継続できなくなりました。デバイスのリソース不足の可能性が考えられます。" antialiasing: "アンチエイリアス" + avatar: "アバター" _miRoom: snapToGrid: "グリッドにスナップ" diff --git a/packages/frontend-misskey-world-engine/src/avatarPreviewEngine.ts b/packages/frontend-misskey-world-engine/src/avatarPreviewEngine.ts index 597ac92668..a6576aa6f7 100644 --- a/packages/frontend-misskey-world-engine/src/avatarPreviewEngine.ts +++ b/packages/frontend-misskey-world-engine/src/avatarPreviewEngine.ts @@ -105,9 +105,8 @@ export class AvatarPreviewEngine extends EngineBase<{ }); } - public async loadAvatar(profile: PlayerProfile) { + public async load(profile: PlayerProfile) { this.sr.disableSnapshotRendering(); - this.clearAvatar(); this.playerContainer = new PlayerContainer({ id: '', @@ -188,16 +187,6 @@ export class AvatarPreviewEngine extends EngineBase<{ } } - public clearAvatar() { - this.sr.disableSnapshotRendering(); - if (this.playerContainer != null) { - this.playerContainer.destroy(); - this.playerContainer = null; - this.avatarOptions = null; - } - this.sr.enableSnapshotRendering(); - } - public cameraRotate(vector: { x: number; y: number; }) { (this.camera.inputs.attached.manual as ArcRotateCameraManualInput).setRotationVector(vector); } diff --git a/packages/frontend-misskey-world-engine/src/avatarPreviewEngineWorker.ts b/packages/frontend-misskey-world-engine/src/avatarPreviewEngineWorker.ts index 5996205472..38249684d3 100644 --- a/packages/frontend-misskey-world-engine/src/avatarPreviewEngineWorker.ts +++ b/packages/frontend-misskey-world-engine/src/avatarPreviewEngineWorker.ts @@ -27,7 +27,7 @@ onmessage = async (event) => { if (event.data.options.resolution === 2) babylonEngine.setHardwareScalingLevel(0.5); if (event.data.options.resolution === 0.5) babylonEngine.setHardwareScalingLevel(2); - engine = new RoomObjectPreviewEngine({ + engine = new AvatarPreviewEngine({ engine: babylonEngine, ...event.data.options, }); diff --git a/packages/frontend-misskey-world-engine/src/room/utility.ts b/packages/frontend-misskey-world-engine/src/room/utility.ts index a0e8d772e7..f8d6fbbc0b 100644 --- a/packages/frontend-misskey-world-engine/src/room/utility.ts +++ b/packages/frontend-misskey-world-engine/src/room/utility.ts @@ -5,7 +5,17 @@ import * as BABYLON from '@babylonjs/core'; import { cm } from 'misskey-world/src/utility.js'; -import { applyMorphTargetsToMesh, getPlaneUvIndexes, Timer } from '../utility.js'; +import { applyMorphTargetsToMesh, getPlaneUvIndexes, GRAPHICS_QUALITY, Timer } from '../utility.js'; + +export function getLightRangeFactorByGraphicsQuality(quality: number) { + if (quality >= GRAPHICS_QUALITY.HIGH) { + return 1; + } else if (quality >= GRAPHICS_QUALITY.MEDIUM) { + return 0.5; + } else { + return 0.25; + } +} export const SYSTEM_MESH_NAMES = ['__TOP__', '__SIDE__', '__BOTTOM__', '__PICK__', '__COLLISION__']; export const SYSTEM_HEYA_MESH_NAMES = ['__ROOM_WALL__', '__ROOM_SIDE__', '__ROOM_FLOOR__', '__ROOM_CEILING__', '__ROOM_TOP__', '__ROOM_BOTTOM__', '__COLLISION__']; diff --git a/packages/frontend-misskey-world-engine/src/utility.ts b/packages/frontend-misskey-world-engine/src/utility.ts index ca29bd98a0..4ae115359f 100644 --- a/packages/frontend-misskey-world-engine/src/utility.ts +++ b/packages/frontend-misskey-world-engine/src/utility.ts @@ -11,16 +11,6 @@ export const GRAPHICS_QUALITY = { LOW: -1, } as const; -export function getLightRangeFactorByGraphicsQuality(quality: number) { - if (quality >= GRAPHICS_QUALITY.HIGH) { - return 1; - } else if (quality >= GRAPHICS_QUALITY.MEDIUM) { - return 0.5; - } else { - return 0.25; - } -} - export const TIME_MAP = { 0: 2, 1: 2, diff --git a/packages/frontend/src/pages/rooms/edit-world-avatar-dialog.vue b/packages/frontend/src/pages/rooms/edit-world-avatar-dialog.vue index c38d8f3e05..7410323216 100644 --- a/packages/frontend/src/pages/rooms/edit-world-avatar-dialog.vue +++ b/packages/frontend/src/pages/rooms/edit-world-avatar-dialog.vue @@ -20,8 +20,8 @@ SPDX-License-Identifier: AGPL-3.0-only -
-
+
+
@@ -47,11 +47,11 @@ SPDX-License-Identifier: AGPL-3.0-only import { ref, useTemplateRef, watch, onMounted, onUnmounted, reactive, nextTick, shallowRef, computed, triggerRef, markRaw } from 'vue'; import * as Misskey from 'misskey-js'; import { OBJECT_SCHEMA_DEFS } from 'misskey-world/src/room/object-schema-defs.js'; -import XObjectCustomizeForm from './room.object-customize-form.vue'; -import XItem from './room.add-object-dialog.item.vue'; -import type { PreviewEngineControllerOptions } from '@/world/room/previewEngineController.js'; import type { RawOptions } from 'misskey-world/src/room/object.js'; import type { RoomAttachments } from 'misskey-world/src/room/type.js'; +import type { WorldAvatar } from 'misskey-world/src/types.js'; +import type { AvatarPreviewEngineControllerOptions } from '@/world/avatarPreviewEngineController.js'; +import { AvatarPreviewEngineController } from '@/world/avatarPreviewEngineController.js'; import { i18n } from '@/i18n.js'; import MkModalWindow from '@/components/MkModalWindow.vue'; import * as os from '@/os.js'; @@ -59,15 +59,13 @@ import MkButton from '@/components/MkButton.vue'; import { prefer } from '@/preferences.js'; import { deepClone } from '@/utility/clone.js'; import { store } from '@/store.js'; -import MkFoldableSection from '@/components/MkFoldableSection.vue'; -import { PreviewEngineController } from '@/world/room/previewEngineController.js'; import MkInput from '@/components/MkInput.vue'; import { withTimeout } from '@/utility/promise-timeout.js'; - -// TODO: instanceのidと紛らわしいのでid -> typeにする +import { $i } from '@/i.js'; const props = defineProps<{ graphicsQuality: number; + avatar: WorldAvatar | null; }>(); const emit = defineEmits<{ @@ -82,39 +80,44 @@ const emit = defineEmits<{ const dialog = useTemplateRef('dialog'); const canvas = useTemplateRef('canvas'); -const selectedId = ref(null); -const showPreview = ref(false); -const selectedInstanceId = ref(null); -const selectedObjectOptionsState = ref(null); -const selectedObjectSchema = computed(() => selectedId.value == null ? null : OBJECT_SCHEMA_DEFS[selectedId.value]); const showObjectOptions = ref(false); -const searchKeyword = ref(''); -const attachments = { - files: [], -} as RoomAttachments; +const avatar: WorldAvatar = props.avatar != null ? deepClone(props.avatar) : { + type: 'default', + body: { + color: [0.8, 0.8, 0.8], + roughness: 1, + metallic: 0, + }, + eyes: { + type: 'a', + color: [0, 0, 0], + }, + mouth: { + type: 'a', + color: [0, 0, 0], + }, + accessories: [], +}; -function addFileAttachment(file: Misskey.entities.DriveFile) { - attachments.files.push(file); -} - -const previewEngineControllerOptions = computed(() => ({ +const avatarPreviewEngineControllerOptions = computed(() => ({ graphicsQuality: props.graphicsQuality, fps: null, resolution: 1, workerMode: prefer.s['world.separateRenderingThread'], })); -const controller = markRaw(new PreviewEngineController(previewEngineControllerOptions.value)); - -const recentlyUsedSchemas = computed(() => { - const recentlyUsed = store.s.recentlyUsedRoomObjects; - return recentlyUsed.map(id => OBJECT_SCHEMA_DEFS[id]).filter((def): def is typeof OBJECT_SCHEMA_DEFS[string] => def != null); -}); +const controller = markRaw(new AvatarPreviewEngineController(avatarPreviewEngineControllerOptions.value)); onMounted(async () => { try { await controller.init(canvas.value!); + await controller.load({ + name: $i.name, + username: $i.username, + avatarUrl: $i.avatarUrl, + worldAvatar: avatar, + }); } catch (err) { console.error(err); os.alert({ @@ -132,42 +135,6 @@ onUnmounted(() => { controller.destroy(); }); -watch(selectedId, (newId) => { - showObjectOptions.value = false; - showPreview.value = false; - - if (!controller.isReady.value) return; - - if (newId == null) { - controller.clearObject(); - controller.pauseRender(); - selectedInstanceId.value = null; - selectedObjectOptionsState.value = null; - } else { - const closeWaiting = os.waiting(); - nextTick(() => { - try { - withTimeout(controller.loadObject(newId), 10000).then(res => { - selectedInstanceId.value = res.id; - selectedObjectOptionsState.value = deepClone(res.options); - controller.resumeRender(); - closeWaiting(); - showPreview.value = true; - nextTick(() => { - controller.resize(); - }); - }).catch(err => { - closeWaiting(); - throw err; - }); - } catch (err) { - closeWaiting(); - throw err; - } - }); - } -}); - function updateObjectOption(k: string, v: any) { controller.updateObjectOption(k, v, attachments); selectedObjectOptionsState.value![k] = v; diff --git a/packages/frontend/src/pages/settings/preferences.vue b/packages/frontend/src/pages/settings/preferences.vue index b5e35b787f..a999707259 100644 --- a/packages/frontend/src/pages/settings/preferences.vue +++ b/packages/frontend/src/pages/settings/preferences.vue @@ -823,6 +823,19 @@ SPDX-License-Identifier: AGPL-3.0-only + + + + + + +
+
+ +
+
+
+
@@ -1220,6 +1233,18 @@ function testNotification(): void { }, 300); } +async function createWorldAvatar(ev: PointerEvent) { + const { dispose } = await os.popupAsyncWithDialog(import('../rooms/edit-world-avatar-dialog.vue').then(x => x.default), { + }, { + ok: async (res) => { + console.log(res); + }, + closed: () => { + dispose(); + }, + }); +} + const headerActions = computed(() => []); const headerTabs = computed(() => []); diff --git a/packages/frontend/src/world/avatarPreviewEngineController.ts b/packages/frontend/src/world/avatarPreviewEngineController.ts index 9d0f5e6230..7048bc49c8 100644 --- a/packages/frontend/src/world/avatarPreviewEngineController.ts +++ b/packages/frontend/src/world/avatarPreviewEngineController.ts @@ -3,7 +3,8 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { EngineControllerBase } from './EngineControllerBase.js'; +import { EngineControllerBase } from './engineControllerBase.js'; +import type { PlayerProfile } from 'misskey-world-engine/src/PlayerContainer.js'; import type { AvatarPreviewEngine } from 'misskey-world-engine/src/avatarPreviewEngine.js'; export type AvatarPreviewEngineControllerOptions = { @@ -44,11 +45,7 @@ export class AvatarPreviewEngineController extends EngineControllerBase