feat: refine Memento headset expressions
This commit is contained in:
parent
1515a7c6cf
commit
271a0dfa2f
@ -318,11 +318,37 @@ const fixedSkinColorBehavior = (behavior?: AvatarBehaviorLibrary): AvatarBehavio
|
|||||||
return { expressions: [...expressions, ...scaredExpressions], sequences }
|
return { expressions: [...expressions, ...scaredExpressions], sequences }
|
||||||
}
|
}
|
||||||
|
|
||||||
const mementoBehavior = (behavior?: AvatarBehaviorLibrary): AvatarBehaviorLibrary => {
|
const mementoHeadsetWorkingExpressionId = 'memento-headset-working-joyful-eyes'
|
||||||
|
const mementoHeadsetShyExpressionId = 'memento-headset-shy-left'
|
||||||
|
|
||||||
|
const copyExpressionEyes = (target: Expression, source: Expression): Expression => ({
|
||||||
|
...target,
|
||||||
|
widthLeft: source.widthLeft,
|
||||||
|
widthRight: source.widthRight,
|
||||||
|
heightLeft: source.heightLeft,
|
||||||
|
heightRight: source.heightRight,
|
||||||
|
curvatureLeft: source.curvatureLeft,
|
||||||
|
curvatureRight: source.curvatureRight,
|
||||||
|
spacing: source.spacing,
|
||||||
|
positionXLeft: source.positionXLeft,
|
||||||
|
positionXRight: source.positionXRight,
|
||||||
|
positionYLeft: source.positionYLeft,
|
||||||
|
positionYRight: source.positionYRight,
|
||||||
|
leftAngle: source.leftAngle,
|
||||||
|
rightAngle: source.rightAngle,
|
||||||
|
eyeMotion: source.eyeMotion,
|
||||||
|
})
|
||||||
|
|
||||||
|
const mementoBehavior = (
|
||||||
|
behavior?: AvatarBehaviorLibrary,
|
||||||
|
customizeHeadsetExpressions = false
|
||||||
|
): AvatarBehaviorLibrary => {
|
||||||
const sourceExpressions = (behavior?.expressions ?? initialExpressions).filter(
|
const sourceExpressions = (behavior?.expressions ?? initialExpressions).filter(
|
||||||
expression =>
|
expression =>
|
||||||
expression.semanticKey !== 'onboarding-listening' &&
|
expression.semanticKey !== 'onboarding-listening' &&
|
||||||
expression.semanticKey !== 'onboarding-curious'
|
expression.semanticKey !== 'onboarding-curious' &&
|
||||||
|
expression.semanticKey !== mementoHeadsetWorkingExpressionId &&
|
||||||
|
expression.semanticKey !== mementoHeadsetShyExpressionId
|
||||||
)
|
)
|
||||||
const expressions = sourceExpressions.map(expression => {
|
const expressions = sourceExpressions.map(expression => {
|
||||||
const fixedColorExpression = { ...expression }
|
const fixedColorExpression = { ...expression }
|
||||||
@ -366,10 +392,53 @@ const mementoBehavior = (behavior?: AvatarBehaviorLibrary): AvatarBehaviorLibrar
|
|||||||
steps: sequence.steps.map(step => ({ ...step })),
|
steps: sequence.steps.map(step => ({ ...step })),
|
||||||
blink: { ...sequence.blink },
|
blink: { ...sequence.blink },
|
||||||
}))
|
}))
|
||||||
|
const workingPose = expressions.find(expression => expression.semanticKey === 'angry-right')
|
||||||
|
const joyfulEyes = expressions.find(expression => expression.semanticKey === 'joyful-wide')
|
||||||
|
const headsetWorkingExpression =
|
||||||
|
customizeHeadsetExpressions && workingPose && joyfulEyes
|
||||||
|
? {
|
||||||
|
...copyExpressionEyes(workingPose, joyfulEyes),
|
||||||
|
id: mementoHeadsetWorkingExpressionId,
|
||||||
|
semanticKey: mementoHeadsetWorkingExpressionId,
|
||||||
|
}
|
||||||
|
: undefined
|
||||||
|
const shySource = expressions.find(expression => expression.semanticKey === 'upward-side-glance')
|
||||||
|
const headsetShyExpression =
|
||||||
|
customizeHeadsetExpressions && shySource
|
||||||
|
? {
|
||||||
|
...shySource,
|
||||||
|
id: mementoHeadsetShyExpressionId,
|
||||||
|
semanticKey: mementoHeadsetShyExpressionId,
|
||||||
|
headY: -Math.abs(shySource.headY),
|
||||||
|
spacing: shySource.spacing - 10,
|
||||||
|
positionYLeft: shySource.positionYLeft + 10,
|
||||||
|
positionYRight: shySource.positionYRight + 10,
|
||||||
|
}
|
||||||
|
: undefined
|
||||||
|
const sequenceFirstExpression = new Map<string, string>([
|
||||||
|
...(headsetWorkingExpression ? [['working', headsetWorkingExpression.id] as const] : []),
|
||||||
|
...(headsetShyExpression ? [['shy', headsetShyExpression.id] as const] : []),
|
||||||
|
])
|
||||||
|
const resolvedSequences = sequences.map(sequence => {
|
||||||
|
const expressionId = sequenceFirstExpression.get(sequence.semanticKey ?? '')
|
||||||
|
return expressionId
|
||||||
|
? {
|
||||||
|
...sequence,
|
||||||
|
steps: sequence.steps.map((step, index) =>
|
||||||
|
index === 0 ? { ...step, expressionId } : step
|
||||||
|
),
|
||||||
|
}
|
||||||
|
: sequence
|
||||||
|
})
|
||||||
return {
|
return {
|
||||||
expressions: [...expressions, ...onboardingExpressions],
|
expressions: [
|
||||||
|
...expressions,
|
||||||
|
...(headsetWorkingExpression ? [headsetWorkingExpression] : []),
|
||||||
|
...(headsetShyExpression ? [headsetShyExpression] : []),
|
||||||
|
...onboardingExpressions,
|
||||||
|
],
|
||||||
sequences: [
|
sequences: [
|
||||||
...sequences.filter(sequence => sequence.semanticKey !== 'onboarding'),
|
...resolvedSequences.filter(sequence => sequence.semanticKey !== 'onboarding'),
|
||||||
...(onboardingExpressions.length === 2
|
...(onboardingExpressions.length === 2
|
||||||
? [
|
? [
|
||||||
{
|
{
|
||||||
@ -884,7 +953,7 @@ const primitiveBundledAvatars: StudioAvatar[] = [
|
|||||||
? { ...filledAvatarRenderStyle }
|
? { ...filledAvatarRenderStyle }
|
||||||
: { ...defaultAvatarRenderStyle },
|
: { ...defaultAvatarRenderStyle },
|
||||||
...(surface === 'ghost'
|
...(surface === 'ghost'
|
||||||
? { behavior: mementoBehavior() }
|
? { behavior: mementoBehavior(undefined, id === 'primitive-ghost-headset') }
|
||||||
: surface === 'apple-bite'
|
: surface === 'apple-bite'
|
||||||
? { behavior: fixedSkinColorBehavior() }
|
? { behavior: fixedSkinColorBehavior() }
|
||||||
: surface === 'lock'
|
: surface === 'lock'
|
||||||
@ -1023,7 +1092,10 @@ export const ensurePrimitiveBundledAvatars = (library: AvatarLibrary): AvatarLib
|
|||||||
avatar.id
|
avatar.id
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
return { ...avatar, behavior: mementoBehavior(avatar.behavior) }
|
return {
|
||||||
|
...avatar,
|
||||||
|
behavior: mementoBehavior(avatar.behavior, avatar.id === 'primitive-ghost-headset'),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (filledBundledAvatarIds.has(avatar.id)) {
|
if (filledBundledAvatarIds.has(avatar.id)) {
|
||||||
return { ...avatar, behavior: fixedSkinColorBehavior(avatar.behavior) }
|
return { ...avatar, behavior: fixedSkinColorBehavior(avatar.behavior) }
|
||||||
@ -1177,7 +1249,10 @@ export const ensurePrimitiveBundledAvatars = (library: AvatarLibrary): AvatarLib
|
|||||||
].includes(avatarWithAppleBiteEyes.id)
|
].includes(avatarWithAppleBiteEyes.id)
|
||||||
? {
|
? {
|
||||||
...avatarWithAppleBiteEyes,
|
...avatarWithAppleBiteEyes,
|
||||||
behavior: mementoBehavior(avatarWithAppleBiteEyes.behavior),
|
behavior: mementoBehavior(
|
||||||
|
avatarWithAppleBiteEyes.behavior,
|
||||||
|
avatarWithAppleBiteEyes.id === 'primitive-ghost-headset'
|
||||||
|
),
|
||||||
}
|
}
|
||||||
: filledBundledAvatarIds.has(avatarWithAppleBiteEyes.id)
|
: filledBundledAvatarIds.has(avatarWithAppleBiteEyes.id)
|
||||||
? {
|
? {
|
||||||
|
|||||||
@ -221,6 +221,53 @@ describe('Studio document', () => {
|
|||||||
expect(memento?.colors).toEqual({ body: '#ff9d45', eyes: '#111316' })
|
expect(memento?.colors).toEqual({ body: '#ff9d45', eyes: '#111316' })
|
||||||
expect(headsetMemento?.colors).toEqual({ body: '#ff9d45', eyes: '#222222' })
|
expect(headsetMemento?.colors).toEqual({ body: '#ff9d45', eyes: '#222222' })
|
||||||
expect(strokeMemento?.colors).toEqual({ body: '#ff9d45', eyes: '#ff9d45' })
|
expect(strokeMemento?.colors).toEqual({ body: '#ff9d45', eyes: '#ff9d45' })
|
||||||
|
const headsetWorkingFirst = headsetMemento?.behavior?.sequences.find(
|
||||||
|
sequence => sequence.semanticKey === 'working'
|
||||||
|
)?.steps[0]
|
||||||
|
const headsetJoyfulFirst = headsetMemento?.behavior?.sequences.find(
|
||||||
|
sequence => sequence.semanticKey === 'joyful'
|
||||||
|
)?.steps[0]
|
||||||
|
const headsetShyFirst = headsetMemento?.behavior?.sequences.find(
|
||||||
|
sequence => sequence.semanticKey === 'shy'
|
||||||
|
)?.steps[0]
|
||||||
|
const headsetWorkingExpression = headsetMemento?.behavior?.expressions.find(
|
||||||
|
expression => expression.id === headsetWorkingFirst?.expressionId
|
||||||
|
)
|
||||||
|
const headsetJoyfulExpression = headsetMemento?.behavior?.expressions.find(
|
||||||
|
expression => expression.id === headsetJoyfulFirst?.expressionId
|
||||||
|
)
|
||||||
|
const headsetShyExpression = headsetMemento?.behavior?.expressions.find(
|
||||||
|
expression => expression.id === headsetShyFirst?.expressionId
|
||||||
|
)
|
||||||
|
expect(headsetWorkingExpression).toMatchObject({
|
||||||
|
widthLeft: headsetJoyfulExpression?.widthLeft,
|
||||||
|
widthRight: headsetJoyfulExpression?.widthRight,
|
||||||
|
heightLeft: headsetJoyfulExpression?.heightLeft,
|
||||||
|
heightRight: headsetJoyfulExpression?.heightRight,
|
||||||
|
spacing: headsetJoyfulExpression?.spacing,
|
||||||
|
leftAngle: headsetJoyfulExpression?.leftAngle,
|
||||||
|
rightAngle: headsetJoyfulExpression?.rightAngle,
|
||||||
|
})
|
||||||
|
expect(headsetWorkingExpression).toMatchObject({
|
||||||
|
headX: initialExpressions[7].headX,
|
||||||
|
headY: initialExpressions[7].headY,
|
||||||
|
headZ: initialExpressions[7].headZ,
|
||||||
|
})
|
||||||
|
expect(headsetJoyfulFirst?.expressionId).toBe('expression-11')
|
||||||
|
expect(headsetShyExpression).toMatchObject({
|
||||||
|
headY: -Math.abs(initialExpressions[0].headY),
|
||||||
|
spacing: initialExpressions[0].spacing - 10,
|
||||||
|
positionYLeft: initialExpressions[0].positionYLeft + 10,
|
||||||
|
positionYRight: initialExpressions[0].positionYRight + 10,
|
||||||
|
})
|
||||||
|
expect(
|
||||||
|
memento?.behavior?.sequences.find(sequence => sequence.semanticKey === 'joyful')?.steps[0]
|
||||||
|
.expressionId
|
||||||
|
).toBe('expression-11')
|
||||||
|
expect(
|
||||||
|
strokeMemento?.behavior?.sequences.find(sequence => sequence.semanticKey === 'joyful')
|
||||||
|
?.steps[0].expressionId
|
||||||
|
).toBe('expression-11')
|
||||||
expect(
|
expect(
|
||||||
strokeMemento?.behavior?.sequences.some(sequence => sequence.semanticKey === 'onboarding')
|
strokeMemento?.behavior?.sequences.some(sequence => sequence.semanticKey === 'onboarding')
|
||||||
).toBe(true)
|
).toBe(true)
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user