diff --git a/src/Components/Core/Presentation/AvatarEditor/AvatarEditorPreview/AvatarEditorPreviewModel/AvatarEditorPreviewModelView.ts b/src/Components/Core/Presentation/AvatarEditor/AvatarEditorPreview/AvatarEditorPreviewModel/AvatarEditorPreviewModelView.ts index 8a558619..c8a187e8 100644 --- a/src/Components/Core/Presentation/AvatarEditor/AvatarEditorPreview/AvatarEditorPreviewModel/AvatarEditorPreviewModelView.ts +++ b/src/Components/Core/Presentation/AvatarEditor/AvatarEditorPreview/AvatarEditorPreviewModel/AvatarEditorPreviewModelView.ts @@ -1,5 +1,11 @@ import AvatarEditorPreviewModelViewModel from "./AvatarEditorPreviewModelViewModel"; -import { Mesh, Texture, TransformNode } from "@babylonjs/core"; +import { + AnimationGroup, + Mesh, + Skeleton, + Texture, + TransformNode, +} from "@babylonjs/core"; import IScenePresenter from "../../../Babylon/SceneManagement/IScenePresenter"; import CoreDIContainer from "~DependencyInjection/CoreDIContainer"; import SCENE_TYPES, { @@ -9,7 +15,10 @@ import AvatarEditorPreviewSceneDefinition from "../AvatarEditorPreviewSceneDefin import { AvatarBeardModels, AvatarHairModels, + AvatarPantsModels, + AvatarShoesModels, AvatarNoneModel, + AvatarShirtModels, } from "src/Components/Core/Domain/AvatarModels/AvatarModelTypes"; import ILoadAvatarConfigUseCase from "src/Components/Core/Application/UseCases/LoadAvatarConfig/ILoadAvatarConfigUseCase"; import USECASE_TYPES from "~DependencyInjection/UseCases/USECASE_TYPES"; @@ -25,6 +34,12 @@ const baseModelLink = require("../../../../../../Assets/3dModels/sharedModels/av export default class AvatarEditorPreviewModelView { private scenePresenter: IScenePresenter; + private baseAnimatonGroups: AnimationGroup[]; + + private baseModelSkeleton: Skeleton; + + private idleAnimationName: string = "ac_anim_idle2"; + constructor(private viewModel: AvatarEditorPreviewModelViewModel) { this.scenePresenter = CoreDIContainer.get( SCENE_TYPES.ScenePresenterFactory, @@ -38,9 +53,22 @@ export default class AvatarEditorPreviewModelView { async asyncSetup(): Promise { // load base model and position it const result = await this.scenePresenter.loadGLTFModel(baseModelLink); + this.baseAnimatonGroups = result.animationGroups; + this.baseModelSkeleton = result.skeletons[0]; + this.viewModel.baseModelMeshes = result.meshes as Mesh[]; this.viewModel.baseModelMeshes[0].position.y = -1; + // Default-Meshes ausblenden + ["defaultPants", "defaultShoes", "defaultTop"].forEach((meshName) => { + const meshToHide = this.viewModel.baseModelMeshes.find( + (m) => m.name === meshName, + ); + if (meshToHide) { + meshToHide.setEnabled(false); + } + }); + // find anchor nodes this.viewModel.hairAnchorNode = result.transformNodes.find( (node) => node.name === "anker_hair", @@ -48,6 +76,33 @@ export default class AvatarEditorPreviewModelView { this.viewModel.beardAnchorNode = result.transformNodes.find( (node) => node.name === "anker_beard", )!; + this.viewModel.shirtAnchorNode = result.transformNodes.find( + (node) => node.name === "anker_top", + )!; + this.viewModel.pantsAnchorNode = result.transformNodes.find( + (node) => node.name === "anker_pants", + )!; + this.viewModel.shoesAnchorNode = result.transformNodes.find( + (node) => node.name === "anker_shoes", + )!; + this.viewModel.shirtAnchorNode = result.transformNodes.find( + (node) => node.name === "anker_top", + )!; + + this.viewModel.baseModelMeshes.forEach((mesh) => { + if (mesh.name === "defaultTop") { + mesh.dispose(); + this.updateModelShirt("shirts-sweatshirt"); + } + if (mesh.name === "defaultPants") { + mesh.dispose(); + this.updateModelPants("pants-jeans"); + } + if (mesh.name === "defaultShoes") { + mesh.dispose(); + this.updateModelShoes("shoes-trainers"); + } + }); await CoreDIContainer.get( USECASE_TYPES.ILoadAvatarConfigUseCase, @@ -59,6 +114,9 @@ export default class AvatarEditorPreviewModelView { 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); } private onAvatarConfigChanged(): void { @@ -75,9 +133,16 @@ export default class AvatarEditorPreviewModelView { this.updateNose(this.viewModel.avatarConfigDiff.Value.nose); if (this.viewModel.avatarConfigDiff.Value.mouth !== undefined) this.updateMouth(this.viewModel.avatarConfigDiff.Value.mouth); + if (this.viewModel.avatarConfigDiff.Value.shirt !== undefined) + this.updateModelShirt(this.viewModel.avatarConfigDiff.Value.shirt); + if (this.viewModel.avatarConfigDiff.Value.pants !== undefined) + this.updateModelPants(this.viewModel.avatarConfigDiff.Value.pants); + if (this.viewModel.avatarConfigDiff.Value.shoes !== undefined) + this.updateModelShoes(this.viewModel.avatarConfigDiff.Value.shoes); } private updateModelHair(hair?: AvatarHairModels | undefined) { + if (!hair) return; this.updateModel( hair, "hair/hairstyle", @@ -87,6 +152,7 @@ export default class AvatarEditorPreviewModelView { } private updateModelBeard(beard?: AvatarBeardModels | undefined) { + if (!beard) return; this.updateModel( beard, "hair/beards", @@ -95,46 +161,77 @@ export default class AvatarEditorPreviewModelView { ); } + private updateModelShirt(shirt?: AvatarShirtModels | undefined) { + this.updateModel( + shirt, + "clothing/shirts", + this.viewModel.shirtMeshes, + this.viewModel.shirtAnchorNode, + ); + } + + private updateModelPants(pants?: AvatarPantsModels | undefined) { + this.updateModel( + pants, + "clothing/pants", + this.viewModel.pantsMeshes, + this.viewModel.pantsAnchorNode, + ); + } + + private updateModelShoes(shirt?: AvatarShoesModels | undefined) { + this.updateModel( + shirt, + "clothing/shoes", + this.viewModel.shoesMeshes, + this.viewModel.shoesAnchorNode, + ); + } + private updateEyeBrows(eyebrow?: number) { + if (eyebrow === undefined || eyebrow === null) return; let eyebrowMat = this.viewModel.baseModelMeshes.find((mesh) => mesh.material?.name.includes("Eyebrow_mat"), )?.material!; let texture = eyebrowMat.getActiveTextures()[0] as Texture; - const tmp = AvatarEyeBrowTexture[eyebrow!]; + const tmp = AvatarEyeBrowTexture[eyebrow]; texture.uOffset = tmp.uOffset; texture.vOffset = tmp.vOffset; } private updateEyes(eyes?: number) { + if (eyes === undefined || eyes === null) return; let eyeMat = this.viewModel.baseModelMeshes.find((mesh) => mesh.material?.name.includes("Eyes_mat"), )?.material!; let texture = eyeMat.getActiveTextures()[0] as Texture; - const tmp = AvatarEyeTexture[eyes!]; + const tmp = AvatarEyeTexture[eyes]; texture.uOffset = tmp.uOffset; texture.vOffset = tmp.vOffset; } private updateNose(nose?: number) { + if (nose === undefined || nose === null) return; let noseMat = this.viewModel.baseModelMeshes.find((mesh) => mesh.material?.name.includes("Nose_mat"), )?.material!; let texture = noseMat.getActiveTextures()[0] as Texture; - const tmp = AvatarNoseTexture[nose!]; + const tmp = AvatarNoseTexture[nose]; texture.uOffset = tmp.uOffset; texture.vOffset = tmp.vOffset; } private updateMouth(mouth?: number) { + if (mouth === undefined || mouth === null) return; let mouthMat = this.viewModel.baseModelMeshes.find((mesh) => mesh.material?.name.includes("Mouth_mat"), )?.material!; let texture = mouthMat.getActiveTextures()[0] as Texture; - const tmp = AvatarMouthTexture[mouth!]; + const tmp = AvatarMouthTexture[mouth]; texture.uOffset = tmp.uOffset; texture.vOffset = tmp.vOffset; } @@ -160,6 +257,13 @@ export default class AvatarEditorPreviewModelView { `../../../../../../Assets/3dModels/sharedModels/avatar/${modelFolder}/aa-${newModel}.glb`, ), ); + 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; } @@ -167,7 +271,9 @@ export default class AvatarEditorPreviewModelView { // set all meshes to invisible except the new model modelMap.forEach((meshes, type) => { const newIsVisible = type === newModel; - meshes.forEach((mesh) => (mesh.isVisible = newIsVisible)); + meshes.forEach((mesh) => { + mesh.isVisible = newIsVisible; + }); }); } } diff --git a/src/Components/Core/Presentation/AvatarEditor/AvatarEditorPreview/AvatarEditorPreviewModel/AvatarEditorPreviewModelViewModel.ts b/src/Components/Core/Presentation/AvatarEditor/AvatarEditorPreview/AvatarEditorPreviewModel/AvatarEditorPreviewModelViewModel.ts index 6c2b61d8..b8025915 100644 --- a/src/Components/Core/Presentation/AvatarEditor/AvatarEditorPreview/AvatarEditorPreviewModel/AvatarEditorPreviewModelViewModel.ts +++ b/src/Components/Core/Presentation/AvatarEditor/AvatarEditorPreview/AvatarEditorPreviewModel/AvatarEditorPreviewModelViewModel.ts @@ -3,6 +3,9 @@ import AvatarConfigTO from "../../../../../Core/Application/DataTransferObjects/ import { AvatarBeardModels, AvatarHairModels, + AvatarPantsModels, + AvatarShoesModels, + AvatarShirtModels, } from "../../../../../Core/Domain/AvatarModels/AvatarModelTypes"; import Observable from "../../../../../../Lib/Observable"; @@ -12,10 +15,16 @@ export default class AvatarEditorPreviewModelViewModel { // anchor nodes hairAnchorNode: TransformNode; beardAnchorNode: TransformNode; + shirtAnchorNode: TransformNode; + pantsAnchorNode: TransformNode; + shoesAnchorNode: TransformNode; // mesh maps hairMeshes: Map = new Map(); beardMeshes: Map = new Map(); + shirtMeshes: Map = new Map(); + pantsMeshes: Map = new Map(); + shoesMeshes: Map = new Map(); currentAvatarConfig: Observable = new Observable(); diff --git a/src/Components/Core/Presentation/AvatarEditor/AvatarEditorPreview/AvatarEditorPreviewSceneDefinition.ts b/src/Components/Core/Presentation/AvatarEditor/AvatarEditorPreview/AvatarEditorPreviewSceneDefinition.ts index 54c40cf9..3474cf64 100644 --- a/src/Components/Core/Presentation/AvatarEditor/AvatarEditorPreview/AvatarEditorPreviewSceneDefinition.ts +++ b/src/Components/Core/Presentation/AvatarEditor/AvatarEditorPreview/AvatarEditorPreviewSceneDefinition.ts @@ -126,5 +126,7 @@ export default class AvatarEditorPreviewSceneDefinition extends AbstractSceneDef shadowGenerator.useBlurExponentialShadowMap = true; shadowGenerator.blurScale = 10; shadowGenerator.darkness = 0.6; + + // this.scene.debugLayer.show(); } }