@hology/core 0.0.236 → 0.0.237
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/effects/sequence/sequence-player.d.ts +3 -0
- package/dist/effects/sequence/sequence-player.js +1 -1
- package/dist/gameplay/actors/builtin/components/character/character-animation.d.ts +5 -0
- package/dist/gameplay/actors/builtin/components/character/character-animation.js +1 -1
- package/dist/test/character-animation-transition.test.js +1 -1
- package/dist/test/sequence-animation-retiming.test.js +1 -1
- package/package.json +1 -1
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{expect as e,test as t,vi as o}from"vitest";import*as i from"three";import{BaseActor as n}from"../gameplay/actors/actor.js";import{CharacterAnimationComponent as s,OldCharacterMovementComponent as
|
|
1
|
+
import{expect as e,test as t,vi as o}from"vitest";import*as i from"three";import{BaseActor as n}from"../gameplay/actors/actor.js";import{CharacterAnimationComponent as s,OldCharacterMovementComponent as a}from"../gameplay/actors/index.js";import{RootMotionClip as r}from"../gameplay/animation/root-motion.js";import{createAnimationClipInstance as c,createAnimationSubTrack as l,createSpawnClipInstance as m,createSpawnTrack as p}from"../effects/sequence/sequence-data.js";import{evaluateAnimationClipOutputProgress as u,evaluateAnimationClipSourceProgress as d,normalizeAnimationClipRetiming as h,buildRetimedAnimationClip as f}from"../effects/sequence/sequence-animation-retiming.js";import{SequencePlayer as g}from"../effects/sequence/sequence-player.js";function A(e){return Array.from(e).map(e=>Math.round(1e3*e)/1e3)}o.mock("../gameplay/actors/builtin/index.js",()=>({builtInActors:{},default:{}})),o.mock("../gameplay/actors/index.js",()=>({CharacterAnimationComponent:class{},OldCharacterMovementComponent:class{},getCharacterMovementLike:e=>e.movement??void 0})),o.mock("../effects/vfx/vfx-actor.js",()=>({VfxActor:class{}})),o.mock("../effects/vfx/vfx-param.js",()=>({VisualEffect:class{}})),o.mock("../effects/vfx/vfx-service.js",()=>({VfxService:class{}})),o.hoisted(()=>{if("undefined"==typeof AudioBuffer&&Object.defineProperty(globalThis,"AudioBuffer",{configurable:!0,value:class{}}),"undefined"==typeof HTMLCanvasElement)return;const e=new Proxy({},{get:(e,t)=>(t in e||(e[t]=("string"!=typeof t||!t.startsWith("is"))&&o.fn()),e[t]),set:(e,t,o)=>(e[t]=o,!0)});Object.defineProperty(HTMLCanvasElement.prototype,"getContext",{configurable:!0,value:()=>e})}),t("uniform retiming mapping stays linear",()=>{e(d(void 0,.25)).toBeCloseTo(.25),e(u(void 0,.75)).toBeCloseTo(.75),e(d({mode:"uniform",points:[]},.6)).toBeCloseTo(.6)}),t("impact retiming redistributes time around the impact marker",()=>{const t={mode:"impact",points:[{id:"impact",clipProgress:.35,sourceProgress:.55}]};e(d(t,.35)).toBeCloseTo(.55),e(u(t,.55)).toBeCloseTo(.35),e(d(t,.7)).toBeGreaterThan(.7)}),t("advanced retiming sanitization stays monotonic and bounded",()=>{const t=h({mode:"advanced",points:[{id:"a",clipProgress:.9,sourceProgress:.2},{id:"b",clipProgress:-4,sourceProgress:.8},{id:"c",clipProgress:.9,sourceProgress:2},{id:"d",clipProgress:.4,sourceProgress:.1}]});e(t.points).toHaveLength(3);for(let o=1;o<t.points.length;o++)e(t.points[o].clipProgress).toBeGreaterThan(t.points[o-1].clipProgress),e(t.points[o].sourceProgress).toBeGreaterThan(t.points[o-1].sourceProgress);e(t.points[0].clipProgress).toBeGreaterThan(0),e(t.points[t.points.length-1].sourceProgress).toBeLessThan(1)}),t("retimed clip baking preserves trim boundary poses and remaps track times to clip duration",()=>{const t=new i.AnimationClip("trim-test",2,[new i.NumberKeyframeTrack("weight",[0,1,2],[0,10,20])]),o=c("anim",0,1);o.clipStartOffset=.5,o.clipEndOffset=.25,o.retiming={mode:"advanced",points:[]};const n=f(t,o),s=n.tracks[0];e(n.duration).toBeCloseTo(1),e(A(s.times)).toEqual([0,.4,1]),e(A(s.values)).toEqual([5,10,17.5])}),t("root motion track baking stays aligned with the retimed visual clip",()=>{const t=new i.AnimationClip("root-motion-test",2,[new i.VectorKeyframeTrack("hips.position",[0,1,2],[0,0,0,1,0,0,2,0,0])]),o=c("anim",0,1);o.retiming={mode:"impact",points:[{id:"impact",clipProgress:.25,sourceProgress:.5}]};const n=f(t,o),s=n.tracks[0],a=r.fromClip(n,!1);e(A(s.times)).toEqual([0,.25,1]),e(A(a.motionTrack.times)).toEqual([0,.25,1]),e(A(a.motionTrack.values)).toEqual([0,0,0,1,0,0,2,0,0])}),t("sequence player keeps uniform clips on the existing playback path",()=>{const t=new g,o=l(),n=c("anim",0,1);n.playbackRate=1.75,n.clipStartOffset=.25,o.clips.push(n);const s=new i.AnimationClip("uniform-test",2,[new i.NumberKeyframeTrack("weight",[0,1,2],[0,1,2])]);t.animationClips.set("anim",s);const a=new i.Object3D,r={target:a,mixer:new i.AnimationMixer(a),activeActions:new Map};t.evaluateAnimationSubTrack(o,r,.1);const m=r.activeActions.get(`${o.id}-${n.id}`);e(m).toBeDefined(),e(m.getClip()).toBe(s),e(m.timeScale).toBeCloseTo(1.75),e(m.time).toBeCloseTo(.25)}),t("sequence player uses baked retimed clips and still wires root motion through the action",()=>{const t=new g,o=l(),n=c("anim",0,1.5);n.rootMotion=!0,n.playbackRate=2,n.retiming={mode:"impact",points:[{id:"impact",clipProgress:.3,sourceProgress:.55}]},o.clips.push(n);const s=new i.AnimationClip("retimed-root",2,[new i.VectorKeyframeTrack("hips.position",[0,1,2],[0,0,0,1,0,0,2,0,0]),new i.NumberKeyframeTrack("weight",[0,1,2],[0,1,2])]);t.animationClips.set("anim",s);const a=new y,m=new v(a.object),p=new w;a.charAnim=m,a.movement=p;const u={target:a,activeActions:new Map};t.evaluateAnimationSubTrack(o,u,.1);const d=u.activeActions.get(`${o.id}-${n.id}`);e(d).toBeDefined(),e(d.timeScale).toBeCloseTo(1),e(d.getClip()).toBeInstanceOf(r),e(d.getClip().duration).toBeCloseTo(1.5),e(p.lastRootMotionAction).toBe(d),e(t.retimedAnimationClips.size).toBe(1)}),t("external sequence control previews root motion deterministically while scrubbing",()=>{const t=new g;t.externalTimeControl=!0;const o=l(),n=c("anim",0,2);n.rootMotion=!0,o.clips.push(n);const s=new i.AnimationClip("root-motion-preview",2,[new i.VectorKeyframeTrack("hips.position",[0,1,2],[0,0,0,1,1,0,2,2,0])]);t.animationClips.set("anim",s);const a=new y;a.object.position.set(10,3,0),a.object.scale.setScalar(2);const r=new i.Bone;r.name="hips",a.object.add(r),a.charAnim=new v(a.object);const m={target:a,activeActions:new Map};t.evaluateSubTracks([o],m,.25),e(a.object.position.x).toBeCloseTo(10.5),e(a.object.position.y).toBeCloseTo(3),t.evaluateSubTracks([o],m,1.5),e(a.object.position.x).toBeCloseTo(13),e(a.object.position.y).toBeCloseTo(3),t.evaluateSubTracks([o],m,.5),e(a.object.position.x).toBeCloseTo(11),e(a.object.position.y).toBeCloseTo(3)}),t("ending a role-bound spawn proxy releases its clamped character animation",()=>{const t=new g,o=p(),n=m(0,1),s=l(),a=c("anim",0,1);o.clips.push(n),o.subTracks.push(s),s.clips.push(a);const r=new y,u=new v(r.object);r.charAnim=u,u.play(new i.AnimationClip("ability",1,[]));const d=`${s.id}-${a.id}`,h=`${o.id}-${n.id}`,f={track:o,target:r,activeActions:new Map([[d,u.getFullBodyAction()]]),charAnimComponent:u,charAnimActionKeys:new Set([d])};t.activeClips.add(h),t.spawnedInstances.set(h,{type:"proxy",target:r}),t.evaluateSpawnTrack(o,f,1),e(f.activeActions.size).toBe(0),e(f.charAnimActionKeys.size).toBe(0),e(u.sequenceReleaseRequests).toBe(1)}),t("sequence completion releases character animation when its proxy overhangs the timeline",()=>{const t=new g,o=p(),n=l(),s=c("anim",0,2.06);o.clips.push(m(0,2.07)),o.subTracks.push(n),n.clips.push(s);const a=new y,r=new v(a.object);a.charAnim=r,r.play(new i.AnimationClip("overhanging-ability",2.06,[]));const u=`${n.id}-${s.id}`,d={track:o,target:a,activeActions:new Map([[u,r.getFullBodyAction()]]),charAnimComponent:r,charAnimActionKeys:new Set([u])};t.bindings.set(o.id,d),t.deactivateCharacterAnimationActions(),e(d.activeActions.size).toBe(0),e(d.charAnimActionKeys.size).toBe(0),e(r.sequenceReleaseRequests).toBe(1)});class y extends n{constructor(){super(...arguments),this.charAnim=null,this.movement=null}getComponent(e){return e===s?this.charAnim:e===a?this.movement:void 0}}class v{constructor(e){this.fullBodyAction=null,this.sequenceReleaseRequests=0,this.mixer=new i.AnimationMixer(e)}play(e,t={}){this.fullBodyAction=this.mixer.clipAction(e),this.fullBodyAction.setLoop(i.LoopOnce,1),this.fullBodyAction.clampWhenFinished=!0,this.fullBodyAction.timeScale=t.timeScale??1,null!=t.offset&&(this.fullBodyAction.time=t.offset),this.fullBodyAction.play()}beginExternalAnimationControl(e,t={}){return this.play(e,t),this.getFullBodyAction()}getFullBodyAction(){if(null==this.fullBodyAction)throw new Error("No full body action is active");return this.fullBodyAction}getMixer(){return this.mixer}beginExternalControl(){}endExternalControl(){}stopSequenceAnimation(){}requestSequenceAnimationRelease(){this.sequenceReleaseRequests++}}class w{constructor(){this.lastRootMotionAction=null}setRootMotionAction(e){this.lastRootMotionAction=e}}/*
|
|
2
2
|
* Copyright (©) 2026 Hology Interactive AB. All rights reserved.
|
|
3
3
|
* See the LICENSE.md file for details.
|
|
4
4
|
*/
|