From 20f2ecd76efe85373e9f9a85260ce04eb0a05d42 Mon Sep 17 00:00:00 2001 From: BDenysovets Date: Mon, 25 Jul 2022 10:47:56 +0300 Subject: [PATCH 1/3] fix(avatar): default props value changed to idle in the pose-model component --- src/components/Models/PoseModel/PoseModel.component.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Models/PoseModel/PoseModel.component.tsx b/src/components/Models/PoseModel/PoseModel.component.tsx index 54e77447..19b6c191 100644 --- a/src/components/Models/PoseModel/PoseModel.component.tsx +++ b/src/components/Models/PoseModel/PoseModel.component.tsx @@ -14,7 +14,7 @@ interface PoseModelProps { emotion?: Emotion; } -export const PoseModel: FC = ({ modelUrl, poseUrl, modelRef, scale = 1, emotion = 'happy' }) => { +export const PoseModel: FC = ({ modelUrl, poseUrl, modelRef, scale = 1, emotion = 'idle' }) => { const { scene } = useLoader(GLTFLoader, modelUrl); const { nodes } = useGraph(scene); const pose = useLoader(GLTFLoader, poseUrl); From d4219f1a8cc0e59ddb6ff9cf761ae7c6cd11f157 Mon Sep 17 00:00:00 2001 From: BDenysovets Date: Mon, 25 Jul 2022 14:03:02 +0300 Subject: [PATCH 2/3] fix(avatar): emotions config removed from visage --- src/components/Avatar/Avatar.component.tsx | 69 +++++++++++++++++- src/components/Avatar/Avatar.stories.tsx | 13 +--- .../HalfBodyModel/HalfBodyModel.component.tsx | 4 +- .../Models/PoseModel/PoseModel.component.tsx | 4 +- src/services/Models.service.ts | 52 +------------ src/types.ts | 73 ------------------- 6 files changed, 77 insertions(+), 138 deletions(-) diff --git a/src/components/Avatar/Avatar.component.tsx b/src/components/Avatar/Avatar.component.tsx index 2f826be4..3ce87f92 100644 --- a/src/components/Avatar/Avatar.component.tsx +++ b/src/components/Avatar/Avatar.component.tsx @@ -4,7 +4,7 @@ import { PresetsType } from '@react-three/drei/helpers/environment-assets'; import { Vector3 } from 'three'; import { CameraLighting } from 'src/components/SceneControls/CameraLighting.component'; import { AnimationModel } from 'src/components/Models/AnimationModel/AnimationModel.component'; -import { Emotion, LightingProps } from 'src/types'; +import { LightingProps } from 'src/types'; import { BaseCanvas } from 'src/components/BaseCanvas'; import { HalfBodyModel, StaticModel, PoseModel } from 'src/components/Models'; import { isValidGlbUrl } from 'src/services'; @@ -32,6 +32,73 @@ export const CAMERA = { } }; +export type HeadBlendShapeType = + | 'browDownLeft' + | 'browDownRight' + | 'browInnerUp' + | 'browOuterUpLeft' + | 'browOuterUpRight' + | 'cheekPuff' + | 'cheekSquintLeft' + | 'cheekSquintRight' + | 'eyeBlinkLeft' + | 'eyeBlinkRight' + | 'eyeSquintLeft' + | 'eyeSquintRight' + | 'eyeWideLeft' + | 'eyeWideRight' + | 'eyesClosed' + | 'eyesLookDown' + | 'eyesLookUp' + | 'jawForward' + | 'jawLeft' + | 'jawOpen' + | 'jawRight' + | 'mouthClose' + | 'mouthDimpleLeft' + | 'mouthDimpleRight' + | 'mouthFrownLeft' + | 'mouthFrownRight' + | 'mouthFunnel' + | 'mouthLeft' + | 'mouthLowerDownLeft' + | 'mouthLowerDownRight' + | 'mouthOpen' + | 'mouthPressLeft' + | 'mouthPressRight' + | 'mouthPucker' + | 'mouthRight' + | 'mouthRollLower' + | 'mouthRollUpper' + | 'mouthShrugLower' + | 'mouthShrugUpper' + | 'mouthSmile' + | 'mouthSmileLeft' + | 'mouthSmileRight' + | 'mouthStretchLeft' + | 'mouthStretchRight' + | 'mouthUpperUpLeft' + | 'mouthUpperUpRight' + | 'noseSneerLeft' + | 'noseSneerRight' + | 'viseme_CH' + | 'viseme_DD' + | 'viseme_E' + | 'viseme_FF' + | 'viseme_I' + | 'viseme_O' + | 'viseme_PP' + | 'viseme_RR' + | 'viseme_SS' + | 'viseme_TH' + | 'viseme_U' + | 'viseme_aa' + | 'viseme_kk' + | 'viseme_nn' + | 'viseme_sil'; + +export type Emotion = Record; + export interface AvatarProps extends LightingProps { /** * Path to `.glb` file of the 3D model. diff --git a/src/components/Avatar/Avatar.stories.tsx b/src/components/Avatar/Avatar.stories.tsx index a1ccb1e7..4bf027c7 100644 --- a/src/components/Avatar/Avatar.stories.tsx +++ b/src/components/Avatar/Avatar.stories.tsx @@ -23,8 +23,7 @@ Static.args = { spotLightColor: '#fff5b6', spotLightAngle: 0.314, cameraTarget: CAMERA.TARGET.FULL_BODY, - cameraInitialDistance: CAMERA.CONTROLS.FULL_BODY.MAX_DISTANCE, - emotion: 'idle' + cameraInitialDistance: CAMERA.CONTROLS.FULL_BODY.MAX_DISTANCE }; export default { @@ -69,13 +68,3 @@ Posing.args = { cameraTarget: CAMERA.TARGET.FULL_BODY, cameraInitialDistance: CAMERA.CONTROLS.FULL_BODY.MAX_DISTANCE }; - -export const Emotions = Template.bind({}); -Emotions.args = { - ...Static.args, - modelUrl: getStoryAssetPath('male.glb'), - poseUrl: getStoryAssetPath('male-pose-standing.glb'), - emotion: 'angry', - cameraTarget: CAMERA.TARGET.FULL_BODY, - cameraInitialDistance: CAMERA.CONTROLS.FULL_BODY.MAX_DISTANCE -}; diff --git a/src/components/Models/HalfBodyModel/HalfBodyModel.component.tsx b/src/components/Models/HalfBodyModel/HalfBodyModel.component.tsx index 2a5aba57..0ac5e6a4 100644 --- a/src/components/Models/HalfBodyModel/HalfBodyModel.component.tsx +++ b/src/components/Models/HalfBodyModel/HalfBodyModel.component.tsx @@ -4,7 +4,7 @@ import { GLTFLoader } from 'three-stdlib'; import { Model } from 'src/components/Models/Model'; import { useEmotion, useHeadMovement } from 'src/services'; import { Group } from 'three'; -import { Emotion } from '../../../types'; +import { Emotion } from '../../Avatar/Avatar.component'; interface HalfBodyModelProps { modelUrl: string; @@ -21,7 +21,7 @@ export const HalfBodyModel: FC = ({ scale = 1, rotation = 20 * (Math.PI / 180), idleRotation = false, - emotion = 'idle' + emotion }) => { const ref = useRef(); const { scene } = useLoader(GLTFLoader, modelUrl); diff --git a/src/components/Models/PoseModel/PoseModel.component.tsx b/src/components/Models/PoseModel/PoseModel.component.tsx index 19b6c191..79b3a690 100644 --- a/src/components/Models/PoseModel/PoseModel.component.tsx +++ b/src/components/Models/PoseModel/PoseModel.component.tsx @@ -4,7 +4,7 @@ import { GLTFLoader } from 'three-stdlib'; import { Model } from 'src/components/Models/Model'; import { Group } from 'three'; import { mutatePose, useEmotion } from 'src/services'; -import { Emotion } from '../../../types'; +import { Emotion } from '../../Avatar/Avatar.component'; interface PoseModelProps { modelUrl: string; @@ -14,7 +14,7 @@ interface PoseModelProps { emotion?: Emotion; } -export const PoseModel: FC = ({ modelUrl, poseUrl, modelRef, scale = 1, emotion = 'idle' }) => { +export const PoseModel: FC = ({ modelUrl, poseUrl, modelRef, scale = 1, emotion }) => { const { scene } = useLoader(GLTFLoader, modelUrl); const { nodes } = useGraph(scene); const pose = useLoader(GLTFLoader, poseUrl); diff --git a/src/services/Models.service.ts b/src/services/Models.service.ts index 3f4c2a94..d046c9ac 100644 --- a/src/services/Models.service.ts +++ b/src/services/Models.service.ts @@ -1,8 +1,7 @@ import { LinearFilter, MeshStandardMaterial, Material, Vector2, Object3D, SkinnedMesh } from 'three'; import { useFrame } from '@react-three/fiber'; import type { ObjectMap } from '@react-three/fiber'; -import { useMemo } from 'react'; -import { Emotions, Emotion } from '../types'; +import { Emotion } from '../components/Avatar/Avatar.component'; export const getStoryAssetPath = (publicAsset: string) => `${process.env.NODE_ENV === 'production' ? '/visage' : ''}/${publicAsset}`; @@ -114,51 +113,8 @@ export const mutatePose = (targetNodes?: ObjectMap['nodes'], sourceNodes?: Objec } }; -const emotions: Emotions = { - idle: {}, - impressed: { - mouthOpen: 0.7, - mouthSmile: 0.3, - mouthDimpleLeft: 0.4, - mouthDimpleRight: 0.4, - eyeWideLeft: 0.75, - eyeWideRight: 0.75, - browInnerUp: 0.3 - }, - sad: { - mouthSmile: -0.35, - browDownLeft: -0.45, - browDownRight: -0.45, - noseSneerLeft: -0.35, - noseSneerRight: -0.35 - }, - angry: { - browDownLeft: 0.95, - browDownRight: 0.95, - mouthDimpleLeft: -0.5, - mouthDimpleRight: -0.5, - mouthLowerDownRight: -0.3, - mouthLowerDownLeft: -0.3, - noseSneerLeft: 0.35, - noseSneerRight: 0.35, - eyeBlinkLeft: 0.15, - eyeBlinkRight: 0.15 - }, - happy: { - mouthSmile: 0.6, - mouthOpen: 0.3, - browDownRight: -0.5, - browDownLeft: -0.5, - mouthDimpleLeft: 0.7, - mouthDimpleRight: 0.7, - noseSneerLeft: 0.45, - noseSneerRight: 0.45 - } -}; - -export const useEmotion = (nodes: ObjectMap['nodes'], emotion: Emotion) => { +export const useEmotion = (nodes: ObjectMap['nodes'], emotion?: Emotion) => { const headMesh = (nodes.Wolf3D_Head || nodes.Wolf3D_Avatar) as SkinnedMesh; - const selectedEmotion = useMemo(() => emotions[emotion], [emotion]); const resetEmotions = () => headMesh?.morphTargetInfluences?.forEach((_, index) => { @@ -170,10 +126,10 @@ export const useEmotion = (nodes: ObjectMap['nodes'], emotion: Emotion) => { return; } - if (emotion !== 'idle') { + if (emotion) { resetEmotions(); - Object.entries(selectedEmotion).forEach(([shape, value]) => { + Object.entries(emotion).forEach(([shape, value]) => { const shapeId = headMesh!.morphTargetDictionary?.[shape]; if (shapeId) { diff --git a/src/types.ts b/src/types.ts index 26596da9..80fe3e44 100644 --- a/src/types.ts +++ b/src/types.ts @@ -20,76 +20,3 @@ export interface LightingProps { spotLightColor?: string; spotLightAngle?: number; } - -export type Emotion = 'idle' | 'sad' | 'angry' | 'happy' | 'impressed'; - -export type HeadBlendShapeType = - | 'browDownLeft' - | 'browDownRight' - | 'browInnerUp' - | 'browOuterUpLeft' - | 'browOuterUpRight' - | 'cheekPuff' - | 'cheekSquintLeft' - | 'cheekSquintRight' - | 'eyeBlinkLeft' - | 'eyeBlinkRight' - | 'eyeSquintLeft' - | 'eyeSquintRight' - | 'eyeWideLeft' - | 'eyeWideRight' - | 'eyesClosed' - | 'eyesLookDown' - | 'eyesLookUp' - | 'jawForward' - | 'jawLeft' - | 'jawOpen' - | 'jawRight' - | 'mouthClose' - | 'mouthDimpleLeft' - | 'mouthDimpleRight' - | 'mouthFrownLeft' - | 'mouthFrownRight' - | 'mouthFunnel' - | 'mouthLeft' - | 'mouthLowerDownLeft' - | 'mouthLowerDownRight' - | 'mouthOpen' - | 'mouthPressLeft' - | 'mouthPressRight' - | 'mouthPucker' - | 'mouthRight' - | 'mouthRollLower' - | 'mouthRollUpper' - | 'mouthShrugLower' - | 'mouthShrugUpper' - | 'mouthSmile' - | 'mouthSmileLeft' - | 'mouthSmileRight' - | 'mouthStretchLeft' - | 'mouthStretchRight' - | 'mouthUpperUpLeft' - | 'mouthUpperUpRight' - | 'noseSneerLeft' - | 'noseSneerRight' - | 'viseme_CH' - | 'viseme_DD' - | 'viseme_E' - | 'viseme_FF' - | 'viseme_I' - | 'viseme_O' - | 'viseme_PP' - | 'viseme_RR' - | 'viseme_SS' - | 'viseme_TH' - | 'viseme_U' - | 'viseme_aa' - | 'viseme_kk' - | 'viseme_nn' - | 'viseme_sil'; - -export type EmotionConfig = Record; - -export type Emotions = { - [name in Emotion]: Partial; -}; From c83b26ea09d6a735a7b7baf03a84b81d330ebf0f Mon Sep 17 00:00:00 2001 From: BDenysovets Date: Mon, 25 Jul 2022 15:07:56 +0300 Subject: [PATCH 3/3] fix(avatar): blendshape type moved to types.ts --- src/components/Avatar/Avatar.component.tsx | 67 +--------------------- src/types.ts | 65 +++++++++++++++++++++ 2 files changed, 66 insertions(+), 66 deletions(-) diff --git a/src/components/Avatar/Avatar.component.tsx b/src/components/Avatar/Avatar.component.tsx index 3ce87f92..30db88e4 100644 --- a/src/components/Avatar/Avatar.component.tsx +++ b/src/components/Avatar/Avatar.component.tsx @@ -4,7 +4,7 @@ import { PresetsType } from '@react-three/drei/helpers/environment-assets'; import { Vector3 } from 'three'; import { CameraLighting } from 'src/components/SceneControls/CameraLighting.component'; import { AnimationModel } from 'src/components/Models/AnimationModel/AnimationModel.component'; -import { LightingProps } from 'src/types'; +import { HeadBlendShapeType, LightingProps } from 'src/types'; import { BaseCanvas } from 'src/components/BaseCanvas'; import { HalfBodyModel, StaticModel, PoseModel } from 'src/components/Models'; import { isValidGlbUrl } from 'src/services'; @@ -32,71 +32,6 @@ export const CAMERA = { } }; -export type HeadBlendShapeType = - | 'browDownLeft' - | 'browDownRight' - | 'browInnerUp' - | 'browOuterUpLeft' - | 'browOuterUpRight' - | 'cheekPuff' - | 'cheekSquintLeft' - | 'cheekSquintRight' - | 'eyeBlinkLeft' - | 'eyeBlinkRight' - | 'eyeSquintLeft' - | 'eyeSquintRight' - | 'eyeWideLeft' - | 'eyeWideRight' - | 'eyesClosed' - | 'eyesLookDown' - | 'eyesLookUp' - | 'jawForward' - | 'jawLeft' - | 'jawOpen' - | 'jawRight' - | 'mouthClose' - | 'mouthDimpleLeft' - | 'mouthDimpleRight' - | 'mouthFrownLeft' - | 'mouthFrownRight' - | 'mouthFunnel' - | 'mouthLeft' - | 'mouthLowerDownLeft' - | 'mouthLowerDownRight' - | 'mouthOpen' - | 'mouthPressLeft' - | 'mouthPressRight' - | 'mouthPucker' - | 'mouthRight' - | 'mouthRollLower' - | 'mouthRollUpper' - | 'mouthShrugLower' - | 'mouthShrugUpper' - | 'mouthSmile' - | 'mouthSmileLeft' - | 'mouthSmileRight' - | 'mouthStretchLeft' - | 'mouthStretchRight' - | 'mouthUpperUpLeft' - | 'mouthUpperUpRight' - | 'noseSneerLeft' - | 'noseSneerRight' - | 'viseme_CH' - | 'viseme_DD' - | 'viseme_E' - | 'viseme_FF' - | 'viseme_I' - | 'viseme_O' - | 'viseme_PP' - | 'viseme_RR' - | 'viseme_SS' - | 'viseme_TH' - | 'viseme_U' - | 'viseme_aa' - | 'viseme_kk' - | 'viseme_nn' - | 'viseme_sil'; - export type Emotion = Record; export interface AvatarProps extends LightingProps { diff --git a/src/types.ts b/src/types.ts index 80fe3e44..d1a09e79 100644 --- a/src/types.ts +++ b/src/types.ts @@ -20,3 +20,68 @@ export interface LightingProps { spotLightColor?: string; spotLightAngle?: number; } + +export type HeadBlendShapeType = + | 'browDownLeft' + | 'browDownRight' + | 'browInnerUp' + | 'browOuterUpLeft' + | 'browOuterUpRight' + | 'cheekPuff' + | 'cheekSquintLeft' + | 'cheekSquintRight' + | 'eyeBlinkLeft' + | 'eyeBlinkRight' + | 'eyeSquintLeft' + | 'eyeSquintRight' + | 'eyeWideLeft' + | 'eyeWideRight' + | 'eyesClosed' + | 'eyesLookDown' + | 'eyesLookUp' + | 'jawForward' + | 'jawLeft' + | 'jawOpen' + | 'jawRight' + | 'mouthClose' + | 'mouthDimpleLeft' + | 'mouthDimpleRight' + | 'mouthFrownLeft' + | 'mouthFrownRight' + | 'mouthFunnel' + | 'mouthLeft' + | 'mouthLowerDownLeft' + | 'mouthLowerDownRight' + | 'mouthOpen' + | 'mouthPressLeft' + | 'mouthPressRight' + | 'mouthPucker' + | 'mouthRight' + | 'mouthRollLower' + | 'mouthRollUpper' + | 'mouthShrugLower' + | 'mouthShrugUpper' + | 'mouthSmile' + | 'mouthSmileLeft' + | 'mouthSmileRight' + | 'mouthStretchLeft' + | 'mouthStretchRight' + | 'mouthUpperUpLeft' + | 'mouthUpperUpRight' + | 'noseSneerLeft' + | 'noseSneerRight' + | 'viseme_CH' + | 'viseme_DD' + | 'viseme_E' + | 'viseme_FF' + | 'viseme_I' + | 'viseme_O' + | 'viseme_PP' + | 'viseme_RR' + | 'viseme_SS' + | 'viseme_TH' + | 'viseme_U' + | 'viseme_aa' + | 'viseme_kk' + | 'viseme_nn' + | 'viseme_sil';