From 3ba5c216dbf9d30e3f60acfa5d87b68e3da4d747 Mon Sep 17 00:00:00 2001 From: Mountler Date: Wed, 29 Jan 2025 10:54:57 +0100 Subject: [PATCH 1/7] removed unnecessary console output --- src/Components/Core/Domain/AvatarModels/AvatarFaceUVTexture.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Components/Core/Domain/AvatarModels/AvatarFaceUVTexture.ts b/src/Components/Core/Domain/AvatarModels/AvatarFaceUVTexture.ts index 52fcf4584..6fbbb7b4d 100644 --- a/src/Components/Core/Domain/AvatarModels/AvatarFaceUVTexture.ts +++ b/src/Components/Core/Domain/AvatarModels/AvatarFaceUVTexture.ts @@ -39,7 +39,6 @@ for (let y = 0; y < 9; y++) { vOffset: y * 0.09975, }); } - console.log("hello"); } export { AvatarEyeTexture }; From ca626bef84053e5ec860c0f9e75d3d71286f9259 Mon Sep 17 00:00:00 2001 From: Mountler Date: Wed, 29 Jan 2025 10:55:39 +0100 Subject: [PATCH 2/7] added loading of avatar config after successful login --- .../SignInAndOutComponent/LoginComponent.tsx | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/Components/Core/Presentation/React/WelcomePage/SignInAndOutComponent/LoginComponent.tsx b/src/Components/Core/Presentation/React/WelcomePage/SignInAndOutComponent/LoginComponent.tsx index c2d9b929e..68989391e 100644 --- a/src/Components/Core/Presentation/React/WelcomePage/SignInAndOutComponent/LoginComponent.tsx +++ b/src/Components/Core/Presentation/React/WelcomePage/SignInAndOutComponent/LoginComponent.tsx @@ -13,6 +13,7 @@ import tailwindMerge from "../../../Utils/TailwindMerge"; import StyledInputField from "~ReactComponents/ReactRelated/ReactBaseComponents/StyledInputField"; import StyledPasswordField from "~ReactComponents/ReactRelated/ReactBaseComponents/StyledPasswordField"; import { useTranslation } from "react-i18next"; +import ILoadAvatarConfigUseCase from "src/Components/Core/Application/UseCases/LoadAvatarConfig/ILoadAvatarConfigUseCase"; /** * React Component that displays a login button. When clicked, a modal will be overlayed. @@ -25,7 +26,10 @@ export default function LoginComponent({ SignInAndOutComponentController >(BUILDER_TYPES.ISignInAndOutComponentBuilder); const getLoginStatusUseCase = useInjection( - USECASE_TYPES.IGetLoginStatusUseCase + USECASE_TYPES.IGetLoginStatusUseCase, + ); + const loadAvatarConfigUseCase = useInjection( + USECASE_TYPES.ILoadAvatarConfigUseCase, ); const { t: translate } = useTranslation("start"); @@ -38,13 +42,19 @@ export default function LoginComponent({ const [, setModalVisible] = useObservable(viewModel?.modalVisible); const [userLoggedIn, setUserLoggedIn] = useObservable( - viewModel?.userLoggedIn + viewModel?.userLoggedIn, ); const [loginFailed] = useObservable(viewModel?.loginFailed); useEffect(() => { const loginStatus = getLoginStatusUseCase.execute(); setUserLoggedIn(loginStatus.isLoggedIn); + if (loginStatus.isLoggedIn) { + const loadConfig = async () => { + await loadAvatarConfigUseCase.executeAsync(); + }; + loadConfig(); + } }, [getLoginStatusUseCase, setUserLoggedIn, setModalVisible]); if (!controller || !viewModel) return null; From 986a8aac280e64c24d47527ec43f90421c0b9045 Mon Sep 17 00:00:00 2001 From: Mountler Date: Wed, 29 Jan 2025 11:37:00 +0100 Subject: [PATCH 3/7] added load avatar config usecase to dependency array in react --- .../WelcomePage/SignInAndOutComponent/LoginComponent.tsx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/Components/Core/Presentation/React/WelcomePage/SignInAndOutComponent/LoginComponent.tsx b/src/Components/Core/Presentation/React/WelcomePage/SignInAndOutComponent/LoginComponent.tsx index 68989391e..d64b3978d 100644 --- a/src/Components/Core/Presentation/React/WelcomePage/SignInAndOutComponent/LoginComponent.tsx +++ b/src/Components/Core/Presentation/React/WelcomePage/SignInAndOutComponent/LoginComponent.tsx @@ -55,7 +55,12 @@ export default function LoginComponent({ }; loadConfig(); } - }, [getLoginStatusUseCase, setUserLoggedIn, setModalVisible]); + }, [ + getLoginStatusUseCase, + loadAvatarConfigUseCase, + setUserLoggedIn, + setModalVisible, + ]); if (!controller || !viewModel) return null; From a320af43278844fd69a4b27858c16ff4adf4ce74 Mon Sep 17 00:00:00 2001 From: Mountler Date: Wed, 29 Jan 2025 11:37:45 +0100 Subject: [PATCH 4/7] added export to avataruvoffset interface --- src/Components/Core/Domain/AvatarModels/AvatarFaceUVTexture.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Components/Core/Domain/AvatarModels/AvatarFaceUVTexture.ts b/src/Components/Core/Domain/AvatarModels/AvatarFaceUVTexture.ts index 6fbbb7b4d..b2cabcc0c 100644 --- a/src/Components/Core/Domain/AvatarModels/AvatarFaceUVTexture.ts +++ b/src/Components/Core/Domain/AvatarModels/AvatarFaceUVTexture.ts @@ -1,4 +1,4 @@ -interface AvatarUVOffset { +export interface AvatarUVOffset { id: number; name: string; uOffset: number; From 2c89d6a65e90e8ee6b20f71e1256b26114e4c2d4 Mon Sep 17 00:00:00 2001 From: Mountler Date: Wed, 29 Jan 2025 11:49:14 +0100 Subject: [PATCH 5/7] exported common avatar editor data into seperate modules --- .../Domain/AvatarModels/AvatarModelMaterialNames.ts | 8 ++++++++ .../Core/Domain/AvatarModels/AvatarModelPaths.ts | 13 +++++++++++++ .../Domain/AvatarModels/AvatarModelTransforms.ts | 11 +++++++++++ 3 files changed, 32 insertions(+) create mode 100644 src/Components/Core/Domain/AvatarModels/AvatarModelMaterialNames.ts create mode 100644 src/Components/Core/Domain/AvatarModels/AvatarModelPaths.ts create mode 100644 src/Components/Core/Domain/AvatarModels/AvatarModelTransforms.ts diff --git a/src/Components/Core/Domain/AvatarModels/AvatarModelMaterialNames.ts b/src/Components/Core/Domain/AvatarModels/AvatarModelMaterialNames.ts new file mode 100644 index 000000000..2cea47c20 --- /dev/null +++ b/src/Components/Core/Domain/AvatarModels/AvatarModelMaterialNames.ts @@ -0,0 +1,8 @@ +const AvatarModelMaterialNames = { + eyebrows: "mat_Eyebrows", + eyes: "mat_Eyes", + nose: "mat_Nose", + mouth: "mat_Mouth", +} as const; + +export default AvatarModelMaterialNames; diff --git a/src/Components/Core/Domain/AvatarModels/AvatarModelPaths.ts b/src/Components/Core/Domain/AvatarModels/AvatarModelPaths.ts new file mode 100644 index 000000000..84472d389 --- /dev/null +++ b/src/Components/Core/Domain/AvatarModels/AvatarModelPaths.ts @@ -0,0 +1,13 @@ +const AvatarModelAssetPaths = { + hairPath: "hair/hairstyle", + beardPath: "hair/beards", + headGearPath: "accessoires/headgear", + glassesPath: "accessoires/glasses", + backpackPath: "accessoires/backpack", + otherPath: "accessoires/other", + shirtPath: "clothing/shirts", + pantsPath: "clothing/pants", + shoesPath: "clothing/shoes", +} as const; + +export default AvatarModelAssetPaths; diff --git a/src/Components/Core/Domain/AvatarModels/AvatarModelTransforms.ts b/src/Components/Core/Domain/AvatarModels/AvatarModelTransforms.ts new file mode 100644 index 000000000..946e25f93 --- /dev/null +++ b/src/Components/Core/Domain/AvatarModels/AvatarModelTransforms.ts @@ -0,0 +1,11 @@ +import { Mesh, Vector3 } from "@babylonjs/core"; + +const AvatarModelTransforms = { + backpack: (mesh: Mesh) => { + mesh.position = new Vector3(0, 0.36, 0); + }, + sheriffStar: (mesh: Mesh) => { + mesh.position = new Vector3(0, 0.28, 0.04); + }, +} as const; +export default AvatarModelTransforms; From 8640e254211588d7d2bc0166b8243fe8e6724917 Mon Sep 17 00:00:00 2001 From: Mountler Date: Wed, 29 Jan 2025 11:50:42 +0100 Subject: [PATCH 6/7] added utility class for avatar editor --- .../AvatarEditor/AvatarEditorUtils.ts | 103 ++++++++++++++++++ 1 file changed, 103 insertions(+) create mode 100644 src/Components/Core/Presentation/AvatarEditor/AvatarEditorUtils.ts diff --git a/src/Components/Core/Presentation/AvatarEditor/AvatarEditorUtils.ts b/src/Components/Core/Presentation/AvatarEditor/AvatarEditorUtils.ts new file mode 100644 index 000000000..2a41ccd30 --- /dev/null +++ b/src/Components/Core/Presentation/AvatarEditor/AvatarEditorUtils.ts @@ -0,0 +1,103 @@ +import { + ISceneLoaderAsyncResult, + Mesh, + Skeleton, + Texture, + TransformNode, + Vector3, +} from "@babylonjs/core"; +import IScenePresenter from "../Babylon/SceneManagement/IScenePresenter"; +import { AvatarNoneModel } from "../../Domain/AvatarModels/AvatarModelTypes"; +import { AvatarUVOffset } from "../../Domain/AvatarModels/AvatarFaceUVTexture"; + +export default class AvatarEditorUtils { + public static async setupAvatarAssetModel( + scenePresenter: IScenePresenter, + avatartSkeleton: Skeleton, + newModel: T, + modelFolder: string, + anchorNode: TransformNode, + onMeshLoaded?: (mesh: Mesh) => void, + ): Promise { + if ( + newModel === undefined || + newModel === null || + newModel === AvatarNoneModel.None + ) + return; + + const result = await scenePresenter.loadGLTFModel( + require( + `src/Assets/3dModels/sharedModels/avatar/${modelFolder}/aa-${newModel}.glb`, + ), + ); + result.animationGroups.forEach((animation) => { + animation.dispose(); + }); + result.meshes.forEach((mesh) => { + if (mesh instanceof Mesh) { + mesh.skeleton?.dispose(); + mesh.skeleton = avatartSkeleton; + } + }); + result.meshes[0].parent = anchorNode; + if (onMeshLoaded) onMeshLoaded(result.meshes[0] as Mesh); + return result; + } + + public static getAvatarAnchorNodes(nodes: TransformNode[]): { + hairNode: TransformNode; + beardNode: TransformNode; + shirtNode: TransformNode; + pantsNode: TransformNode; + shoesNode: TransformNode; + headGearNode: TransformNode; + glassesNode: TransformNode; + backpackNode: TransformNode; + otherNode: TransformNode; + } { + const hairAnchorNode = nodes.find((node) => node.name === "anchor_hair")!; + const beardAnchorNode = nodes.find((node) => node.name === "anchor_beard")!; + const shirtAnchorNode = nodes.find((node) => node.name === "anchor_top")!; + const pantsAnchorNode = nodes.find((node) => node.name === "anchor_pants")!; + const shoesAnchorNode = nodes.find((node) => node.name === "anchor_shoes")!; + const headGearAnchorNode = nodes.find( + (node) => node.name === "anchor_hat", + )!; + const glassesAnchorNode = nodes.find( + (node) => node.name === "anchor_glasses", + )!; + const backpackAnchorNode = nodes.find((node) => node.name === "Spine")!; + const otherAnchorNode = nodes.find((node) => node.name === "Spine")!; + // models are per default mirrored + shirtAnchorNode.scaling = new Vector3(-1, 1, 1); + pantsAnchorNode.scaling = new Vector3(-1, 1, 1); + shoesAnchorNode.scaling = new Vector3(-1, 1, 1); + + return { + hairNode: hairAnchorNode, + beardNode: beardAnchorNode, + shirtNode: shirtAnchorNode, + pantsNode: pantsAnchorNode, + shoesNode: shoesAnchorNode, + headGearNode: headGearAnchorNode, + glassesNode: glassesAnchorNode, + backpackNode: backpackAnchorNode, + otherNode: otherAnchorNode, + }; + } + + public static setupAvatarTextures( + textureIndex: number, + meshes: Mesh[], + materialName: string, + textureOffset: AvatarUVOffset[], + ) { + if (textureIndex === undefined || textureIndex === null) return; + const texture = meshes + .find((mesh) => mesh.material?.name.includes(materialName)) + ?.material!.getActiveTextures()[0] as Texture; + texture.uOffset = textureOffset[textureIndex].uOffset; + texture.vOffset = textureOffset[textureIndex].vOffset; + } +} From c762b934e28021c288144fa7ba4a6ea62ba85160 Mon Sep 17 00:00:00 2001 From: Mountler Date: Wed, 29 Jan 2025 11:51:34 +0100 Subject: [PATCH 7/7] applied avatar config data to learning space avatar --- .../AvatarEditorPreviewModelView.ts | 127 +++++-------- .../Presentation/Babylon/Avatar/AvatarView.ts | 167 +++++++++++++++++- 2 files changed, 201 insertions(+), 93 deletions(-) diff --git a/src/Components/Core/Presentation/AvatarEditor/AvatarEditorPreview/AvatarEditorPreviewModel/AvatarEditorPreviewModelView.ts b/src/Components/Core/Presentation/AvatarEditor/AvatarEditorPreview/AvatarEditorPreviewModel/AvatarEditorPreviewModelView.ts index 7ef41930b..dc413fa6e 100644 --- a/src/Components/Core/Presentation/AvatarEditor/AvatarEditorPreview/AvatarEditorPreviewModel/AvatarEditorPreviewModelView.ts +++ b/src/Components/Core/Presentation/AvatarEditor/AvatarEditorPreview/AvatarEditorPreviewModel/AvatarEditorPreviewModelView.ts @@ -1,11 +1,5 @@ import AvatarEditorPreviewModelViewModel from "./AvatarEditorPreviewModelViewModel"; -import { - Mesh, - Skeleton, - Texture, - TransformNode, - Vector3, -} from "@babylonjs/core"; +import { Mesh, Skeleton, Texture, TransformNode } from "@babylonjs/core"; import IScenePresenter from "../../../Babylon/SceneManagement/IScenePresenter"; import CoreDIContainer from "~DependencyInjection/CoreDIContainer"; import SCENE_TYPES, { @@ -32,6 +26,10 @@ import { AvatarMouthTexture, AvatarNoseTexture, } from "src/Components/Core/Domain/AvatarModels/AvatarFaceUVTexture"; +import AvatarEditorUtils from "../../AvatarEditorUtils"; +import AvatarModelAssetPaths from "src/Components/Core/Domain/AvatarModels/AvatarModelPaths"; +import AvatarModelTransforms from "src/Components/Core/Domain/AvatarModels/AvatarModelTransforms"; +import AvatarModelMaterialNames from "src/Components/Core/Domain/AvatarModels/AvatarModelMaterialNames"; const baseModelLink = require("../../../../../../Assets/3dModels/sharedModels/avatar/a-avatar-skeleton.glb"); @@ -75,72 +73,43 @@ export default class AvatarEditorPreviewModelView { }); // find anchor nodes - this.viewModel.hairAnchorNode = result.transformNodes.find( - (node) => node.name === "anchor_hair", - )!; - this.viewModel.beardAnchorNode = result.transformNodes.find( - (node) => node.name === "anchor_beard", - )!; - this.viewModel.shirtAnchorNode = result.transformNodes.find( - (node) => node.name === "anchor_top", - )!; - this.viewModel.pantsAnchorNode = result.transformNodes.find( - (node) => node.name === "anchor_pants", - )!; - this.viewModel.shoesAnchorNode = result.transformNodes.find( - (node) => node.name === "anchor_shoes", - )!; - this.viewModel.headGearAnchorNode = result.transformNodes.find( - (node) => node.name === "anchor_hat", - )!; - this.viewModel.glassesAnchorNode = result.transformNodes.find( - (node) => node.name === "anchor_glasses", - )!; - this.viewModel.backpackAnchorNode = result.transformNodes.find( - (node) => node.name === "Spine", - )!; - this.viewModel.otherAnchorNode = result.transformNodes.find( - (node) => node.name === "Spine", - )!; - - console.log(this.viewModel.backpackAnchorNode); - result.transformNodes.forEach((node) => { - console.log(node.name, " / ", node.parent?.name); - }); - - // this.viewModel.baseModelMeshes.forEach((mesh) => { - // if (mesh.name === "defaultTop") { - // mesh.dispose(); - // } - // if (mesh.name === "defaultPants") { - // mesh.dispose(); - // } - // if (mesh.name === "defaultShoes") { - // mesh.dispose(); - // } - // }); + const anchorNodes = AvatarEditorUtils.getAvatarAnchorNodes( + result.transformNodes, + ); + this.viewModel.hairAnchorNode = anchorNodes.hairNode; + this.viewModel.beardAnchorNode = anchorNodes.beardNode; + this.viewModel.shirtAnchorNode = anchorNodes.shirtNode; + this.viewModel.pantsAnchorNode = anchorNodes.pantsNode; + this.viewModel.shoesAnchorNode = anchorNodes.shoesNode; + this.viewModel.headGearAnchorNode = anchorNodes.headGearNode; + this.viewModel.glassesAnchorNode = anchorNodes.glassesNode; + this.viewModel.backpackAnchorNode = anchorNodes.backpackNode; + this.viewModel.otherAnchorNode = anchorNodes.otherNode; await CoreDIContainer.get( USECASE_TYPES.ILoadAvatarConfigUseCase, ).executeAsync(); + // hair this.updateModelHair(this.viewModel.currentAvatarConfig.Value.hair); this.updateModelBeard(this.viewModel.currentAvatarConfig.Value.beard); + // face this.updateEyeBrows(this.viewModel.currentAvatarConfig.Value.eyebrows); this.updateEyes(this.viewModel.currentAvatarConfig.Value.eyes); this.updateNose(this.viewModel.currentAvatarConfig.Value.nose); this.updateMouth(this.viewModel.currentAvatarConfig.Value.mouth); - this.updateModelShirt(this.viewModel.currentAvatarConfig.Value.shirt); - this.updateModelPants(this.viewModel.currentAvatarConfig.Value.pants); - this.updateModelShoes(this.viewModel.currentAvatarConfig.Value.shoes); + // accessoires this.updateHeadGear(this.viewModel.currentAvatarConfig.Value.headgear); this.updateGlasses(this.viewModel.currentAvatarConfig.Value.glasses); this.updateBackPack(this.viewModel.currentAvatarConfig.Value.backpack); this.updateOther(this.viewModel.currentAvatarConfig.Value.other); + // clothing + this.updateModelShirt(this.viewModel.currentAvatarConfig.Value.shirt); + this.updateModelPants(this.viewModel.currentAvatarConfig.Value.pants); + this.updateModelShoes(this.viewModel.currentAvatarConfig.Value.shoes); } private onAvatarConfigChanged(): void { - //console.log(this.viewModel.avatarConfigDiff.Value); if (this.viewModel.avatarConfigDiff.Value.beard) this.updateModelBeard(this.viewModel.avatarConfigDiff.Value.beard); if (this.viewModel.avatarConfigDiff.Value.hair) @@ -172,7 +141,7 @@ export default class AvatarEditorPreviewModelView { private updateModelHair(hair?: AvatarHairModels | undefined) { this.updateModel( hair, - "hair/hairstyle", + AvatarModelAssetPaths.hairPath, this.viewModel.hairMeshes, this.viewModel.hairAnchorNode, ); @@ -181,7 +150,7 @@ export default class AvatarEditorPreviewModelView { private updateModelBeard(beard?: AvatarBeardModels | undefined) { this.updateModel( beard, - "hair/beards", + AvatarModelAssetPaths.beardPath, this.viewModel.beardMeshes, this.viewModel.beardAnchorNode, ); @@ -190,7 +159,7 @@ export default class AvatarEditorPreviewModelView { private updateHeadGear(headgear?: AvatarHeadgearModels | undefined) { this.updateModel( headgear, - "accessoires/headgear", + AvatarModelAssetPaths.headGearPath, this.viewModel.headGearMeshes, this.viewModel.headGearAnchorNode, ); @@ -199,7 +168,7 @@ export default class AvatarEditorPreviewModelView { private updateGlasses(glasses?: AvatarGlassesModels | undefined) { this.updateModel( glasses, - "accessoires/glasses", + AvatarModelAssetPaths.glassesPath, this.viewModel.glassesMeshes, this.viewModel.glassesAnchorNode, ); @@ -208,31 +177,27 @@ export default class AvatarEditorPreviewModelView { private updateBackPack(backpack?: AvatarBackpackModels | undefined) { this.updateModel( backpack, - "accessoires/backpack", + AvatarModelAssetPaths.backpackPath, this.viewModel.backpackMeshes, this.viewModel.backpackAnchorNode, - (mesh) => { - mesh.position = this.viewModel.backpackPositionOffset; - }, + AvatarModelTransforms.backpack, ); } private updateOther(other?: AvatarOtherModels) { this.updateModel( other, - "accessoires/other", + AvatarModelAssetPaths.otherPath, this.viewModel.otherMeshes, this.viewModel.otherAnchorNode, - (mesh) => { - mesh.position = new Vector3(0, 0.28, 0.04); - }, + AvatarModelTransforms.sheriffStar, ); } private updateModelShirt(shirt?: AvatarShirtModels | undefined) { this.updateModel( shirt, - "clothing/shirts", + AvatarModelAssetPaths.shirtPath, this.viewModel.shirtMeshes, this.viewModel.shirtAnchorNode, ); @@ -241,7 +206,7 @@ export default class AvatarEditorPreviewModelView { private updateModelPants(pants?: AvatarPantsModels | undefined) { this.updateModel( pants, - "clothing/pants", + AvatarModelAssetPaths.pantsPath, this.viewModel.pantsMeshes, this.viewModel.pantsAnchorNode, ); @@ -250,7 +215,7 @@ export default class AvatarEditorPreviewModelView { private updateModelShoes(shoes?: AvatarShoesModels | undefined) { this.updateModel( shoes, - "clothing/shoes", + AvatarModelAssetPaths.shoesPath, this.viewModel.shoesMeshes, this.viewModel.shoesAnchorNode, ); @@ -322,21 +287,15 @@ export default class AvatarEditorPreviewModelView { // load model if not already loaded if (!modelMap.has(newModel)) { - const result = await this.scenePresenter.loadGLTFModel( - require( - `../../../../../../Assets/3dModels/sharedModels/avatar/${modelFolder}/aa-${newModel}.glb`, - ), + const result = await AvatarEditorUtils.setupAvatarAssetModel( + this.scenePresenter, + this.baseModelSkeleton, + newModel, + modelFolder, + anchorNode, + onMeshLoad, ); - result.meshes.forEach((mesh) => { - if (mesh instanceof Mesh) { - // Stelle sicher, dass es ein Mesh ist - mesh.skeleton = this.baseModelSkeleton; - } - }); - - modelMap.set(newModel, result.meshes as Mesh[]); - result.meshes[0].parent = anchorNode; - if (onMeshLoad) onMeshLoad(result.meshes[0] as Mesh); + modelMap.set(newModel, result!.meshes as Mesh[]); } // set all meshes to invisible except the new model diff --git a/src/Components/Core/Presentation/Babylon/Avatar/AvatarView.ts b/src/Components/Core/Presentation/Babylon/Avatar/AvatarView.ts index badf6f5e6..a7d45ba93 100644 --- a/src/Components/Core/Presentation/Babylon/Avatar/AvatarView.ts +++ b/src/Components/Core/Presentation/Babylon/Avatar/AvatarView.ts @@ -5,7 +5,11 @@ import { TransformNode, Vector3, } from "@babylonjs/core"; -import type { AnimationGroup, Nullable, Texture } from "@babylonjs/core"; +import type { + AnimationGroup, + ISceneLoaderAsyncResult, + Nullable, +} from "@babylonjs/core"; import SCENE_TYPES, { ScenePresenterFactory, } from "~DependencyInjection/Scenes/SCENE_TYPES"; @@ -22,8 +26,21 @@ import { config } from "src/config"; import bind from "bind-decorator"; import ICharacterAnimator from "../CharacterAnimator/ICharacterAnimator"; import ICharacterNavigator from "../CharacterNavigator/ICharacterNavigator"; +import IEntityContainer from "src/Components/Core/Domain/EntityContainer/IEntityContainer"; +import CORE_TYPES from "~DependencyInjection/CoreTypes"; +import UserDataEntity from "src/Components/Core/Domain/Entities/UserDataEntity"; +import AvatarEditorUtils from "../../AvatarEditor/AvatarEditorUtils"; +import AvatarModelAssetPaths from "src/Components/Core/Domain/AvatarModels/AvatarModelPaths"; +import AvatarModelTransforms from "src/Components/Core/Domain/AvatarModels/AvatarModelTransforms"; +import { + AvatarEyeBrowTexture, + AvatarEyeTexture, + AvatarMouthTexture, + AvatarNoseTexture, +} from "src/Components/Core/Domain/AvatarModels/AvatarFaceUVTexture"; +import AvatarModelMaterialNames from "src/Components/Core/Domain/AvatarModels/AvatarModelMaterialNames"; -const modelLink = require("../../../../../Assets/3dModels/sharedModels/a_avatar_standardmale.glb"); +const modelLink = require("../../../../../Assets/3dModels/sharedModels/avatar/a-avatar-skeleton.glb"); export default class AvatarView { private scenePresenter: IScenePresenter; @@ -59,9 +76,26 @@ export default class AvatarView { this.viewModel.parentNode = this.scenePresenter.Scene.getTransformNodeByName("AvatarParentNode")!; - let result = await this.scenePresenter.loadGLTFModel(modelLink); + const result = await this.scenePresenter.loadGLTFModel(modelLink); + + // Default-Meshes ausblenden + [ + "defaultPants_primitive0", + "defaultPants_primitive1", + "defaultShoes_primitive0", + "defaultShoes_primitive1", + "defaultTop", + ].forEach((meshName) => { + const meshToHide = result.meshes.find((m) => m.name === meshName); + if (meshToHide) { + meshToHide.dispose(); + } + }); + this.viewModel.meshes = result.meshes as Mesh[]; + await this.loadCustomizedAvatarAssets(result); + // create separate root node for model // so that it can be rotated without affecting gltf coordinate system conversions in __root__ node created by Babylon this.viewModel.modelRootNode = new TransformNode( @@ -97,12 +131,11 @@ export default class AvatarView { @bind private setupBlinkAnimation(): void { - const eyeMaterial = this.viewModel.meshes.find( - (mesh) => mesh.material?.name === "Eyes_mat", - )?.material!; - this.viewModel.eyeTextures = eyeMaterial.getActiveTextures() as Texture[]; - - this.setBlinkTimeout(); + // const eyeMaterial = this.viewModel.meshes.find( + // (mesh) => mesh.material?.name === "Eyes_mat", + // )?.material!; + // this.viewModel.eyeTextures = eyeMaterial.getActiveTextures() as Texture[]; + // this.setBlinkTimeout(); } @bind @@ -190,4 +223,120 @@ export default class AvatarView { clearTimeout(this.viewModel.setEyeTimer); }); } + + private async loadCustomizedAvatarAssets( + result: ISceneLoaderAsyncResult, + ): Promise { + const userDataEntity = CoreDIContainer.get( + CORE_TYPES.IEntityContainer, + ); + const avatarEntity = + userDataEntity.getEntitiesOfType(UserDataEntity)[0].avatar; + const anchorNodes = AvatarEditorUtils.getAvatarAnchorNodes( + result.transformNodes, + ); + const baseSkeleton = result.skeletons[0]; + // hair + AvatarEditorUtils.setupAvatarAssetModel( + this.scenePresenter, + baseSkeleton, + avatarEntity.hair, + AvatarModelAssetPaths.hairPath, + anchorNodes.hairNode, + ); + // beard + AvatarEditorUtils.setupAvatarAssetModel( + this.scenePresenter, + baseSkeleton, + avatarEntity.beard, + AvatarModelAssetPaths.beardPath, + anchorNodes.beardNode, + ); + // headgear + AvatarEditorUtils.setupAvatarAssetModel( + this.scenePresenter, + baseSkeleton, + avatarEntity.headgear, + AvatarModelAssetPaths.headGearPath, + anchorNodes.headGearNode, + ); + // glasses + AvatarEditorUtils.setupAvatarAssetModel( + this.scenePresenter, + baseSkeleton, + avatarEntity.glasses, + AvatarModelAssetPaths.glassesPath, + anchorNodes.glassesNode, + ); + // backpack + AvatarEditorUtils.setupAvatarAssetModel( + this.scenePresenter, + baseSkeleton, + avatarEntity.backpack, + AvatarModelAssetPaths.backpackPath, + anchorNodes.backpackNode, + AvatarModelTransforms.backpack, + ); + // other + AvatarEditorUtils.setupAvatarAssetModel( + this.scenePresenter, + baseSkeleton, + avatarEntity.other, + AvatarModelAssetPaths.otherPath, + anchorNodes.otherNode, + AvatarModelTransforms.sheriffStar, + ); + // shirt + AvatarEditorUtils.setupAvatarAssetModel( + this.scenePresenter, + baseSkeleton, + avatarEntity.shirt, + AvatarModelAssetPaths.shirtPath, + anchorNodes.shirtNode, + ); + // pants + AvatarEditorUtils.setupAvatarAssetModel( + this.scenePresenter, + baseSkeleton, + avatarEntity.pants, + AvatarModelAssetPaths.pantsPath, + anchorNodes.pantsNode, + ); + // shoes + AvatarEditorUtils.setupAvatarAssetModel( + this.scenePresenter, + baseSkeleton, + avatarEntity.shoes, + AvatarModelAssetPaths.shoesPath, + anchorNodes.shoesNode, + ); + // eyebrows + AvatarEditorUtils.setupAvatarTextures( + avatarEntity.eyebrows, + result.meshes as Mesh[], + AvatarModelMaterialNames.eyebrows, + AvatarEyeBrowTexture, + ); + // eyes + AvatarEditorUtils.setupAvatarTextures( + avatarEntity.eyes, + result.meshes as Mesh[], + AvatarModelMaterialNames.eyes, + AvatarEyeTexture, + ); + // nose + AvatarEditorUtils.setupAvatarTextures( + avatarEntity.nose, + result.meshes as Mesh[], + AvatarModelMaterialNames.nose, + AvatarNoseTexture, + ); + // mouth + AvatarEditorUtils.setupAvatarTextures( + avatarEntity.mouth, + result.meshes as Mesh[], + AvatarModelMaterialNames.mouth, + AvatarMouthTexture, + ); + } }