fix: preserve dog ear depth across poses

This commit is contained in:
Наер Михаил Александрович 2026-08-25 19:16:41 +03:00
parent 9cca5fa079
commit 9114def84e
3 changed files with 18 additions and 21 deletions

View File

@ -195,11 +195,7 @@ export const simpleBundledAvatarIds = [
const simpleBundledAvatarOrder = new Map(simpleBundledAvatarIds.map((id, index) => [id, index])) const simpleBundledAvatarOrder = new Map(simpleBundledAvatarIds.map((id, index) => [id, index]))
// Remove hidden bundled characters from both fresh and previously persisted local libraries. // Remove hidden bundled characters from both fresh and previously persisted local libraries.
const hiddenBundledAvatarIds = new Set([ const hiddenBundledAvatarIds = new Set(['memento-piglet', 'oneworks-cat', 'oneworks-cat-cow'])
'memento-piglet',
'oneworks-cat',
'oneworks-cat-cow',
])
const retiredBundledAvatarIds = new Set([ const retiredBundledAvatarIds = new Set([
'memento-arctic-fox', 'memento-arctic-fox',
@ -579,13 +575,13 @@ const oneWorksDogAvatar = (
oneWorksNode('dog-ear-left', 'Left ear', 'teardrop', [-82, -65, 0], 0.18, 0.34, { oneWorksNode('dog-ear-left', 'Left ear', 'teardrop', [-82, -65, 0], 0.18, 0.34, {
scaleZ: 0.18, scaleZ: 0.18,
rotation: [-4, -10, 22], rotation: [-4, -10, 22],
layer: 'front', layer: 'auto',
color: earColor, color: earColor,
}), }),
oneWorksNode('dog-ear-right', 'Right ear', 'teardrop', [82, -65, 0], 0.18, 0.34, { oneWorksNode('dog-ear-right', 'Right ear', 'teardrop', [82, -65, 0], 0.18, 0.34, {
scaleZ: 0.18, scaleZ: 0.18,
rotation: [-4, 10, -22], rotation: [-4, 10, -22],
layer: 'front', layer: 'auto',
color: earColor, color: earColor,
}), }),
], ],

View File

@ -98,18 +98,12 @@ describe('rendered avatar scene', () => {
expect(forcedFront.frontNodeIds).toContain(avatar.body.nodes[0].id) expect(forcedFront.frontNodeIds).toContain(avatar.body.nodes[0].id)
}) })
it('anchors dog ears in front of the head across expressions', () => { it('moves dog ears between front and back with head depth', () => {
const studio = loadStudioDocument({ getItem: () => null }) const studio = loadStudioDocument({ getItem: () => null })
const avatar = studio.library.avatars.find(item => item.id === 'oneworks-dog')! const avatar = studio.library.avatars.find(item => item.id === 'oneworks-dog')!
const detachedEars: string[] = [] const detachedEars: string[] = []
const neutral = renderAvatar( let splitDepthExpressions = 0
poseFromExpression(defaultExpression),
avatar.body.primary as SurfaceConfig,
1,
{ bodyNodes: avatar.body.nodes as BodyNode[] }
)
expect(neutral.frontNodeIds).toEqual(['dog-ear-left', 'dog-ear-right'])
studio.expressions.forEach(expression => { studio.expressions.forEach(expression => {
const geometry = renderAvatar( const geometry = renderAvatar(
@ -121,11 +115,18 @@ describe('rendered avatar scene', () => {
const head = pathBounds(geometry.headPath) const head = pathBounds(geometry.headPath)
const earIds = ['dog-ear-left', 'dog-ear-right'] as const const earIds = ['dog-ear-left', 'dog-ear-right'] as const
if (
geometry.frontNodeIds.some(id => earIds.includes(id as (typeof earIds)[number])) &&
geometry.backNodeIds.some(id => earIds.includes(id as (typeof earIds)[number]))
) {
splitDepthExpressions += 1
}
earIds.forEach(nodeId => { earIds.forEach(nodeId => {
const frontIndex = geometry.frontNodeIds.indexOf(nodeId) const frontIndex = geometry.frontNodeIds.indexOf(nodeId)
expect(geometry.backNodeIds).not.toContain(nodeId) const backIndex = geometry.backNodeIds.indexOf(nodeId)
expect(frontIndex).toBeGreaterThanOrEqual(0) expect(Number(frontIndex >= 0) + Number(backIndex >= 0)).toBe(1)
const earPath = geometry.frontPaths[frontIndex] const earPath =
frontIndex >= 0 ? geometry.frontPaths[frontIndex] : geometry.backPaths[backIndex]
const ear = pathBounds(earPath) const ear = pathBounds(earPath)
const headOverlap = overlap(ear, head) const headOverlap = overlap(ear, head)
if (headOverlap.x <= 0 || headOverlap.y <= 0) { if (headOverlap.x <= 0 || headOverlap.y <= 0) {
@ -134,9 +135,9 @@ describe('rendered avatar scene', () => {
) )
} }
}) })
}) })
expect(splitDepthExpressions).toBeGreaterThan(0)
expect(detachedEars, 'detached dog ears').toEqual([]) expect(detachedEars, 'detached dog ears').toEqual([])
}) })