@hology/core 0.0.272 → 0.0.273

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.
@@ -1,4 +1,4 @@
1
- import{__decorate as t}from"tslib";import{ActorComponent as e,Component as o}from"../../../component.js";import{canCrossFadeFromAnimationAction as i}from"../../../../animation/animation-action.js";import{RootMotionClip as s}from"../../../../animation/root-motion.js";import{inject as r}from"../../../../inject.js";import{ViewController as n}from"../../../../services/render.js";import{AnimationMixer as a,Bone as l,Object3D as h,LoopOnce as c,Raycaster as d,Vector3 as p,SkinnedMesh as u,Skeleton as f,Quaternion as g,Matrix4 as m,MathUtils as k,ArrowHelper as y,Group as S}from"three";import{getCharacterMovementLike as I}from"./character-movement-like.js";import{CCDIKSolver as P}from"three/addons/animation/CCDIKSolver.js";import{World as w}from"../../../../services/world.js";import{PhysicsSystem as v,RayTestResult as b}from"../../../../services/physics/physics-system.js";import{ArrayMap as x}from"../../../../../utils/collections.js";import{getAnimationClipRigId as L,getAnimationRigChainBoneNames as B,getAnimationRigDataFromObject as R,getAnimationRigIdFromObject as F,setAnimationClipRigId as A}from"../../../../animation/retarget.js";import{FootContactAnchor as D,getAnimationFootContacts as C,isFootInContact as M,setAnimationFootContacts as T}from"../../../../animation/foot-contact.js";const W=new p(0,-1,0),O=new p(0,1,0),q=new p(1,1,1),U=new g,V=new p,E=new p,H=new p,N=new p,z=new p,j=new g,Q=new g,G=new g,_=new g,$=new m,K=new m,Y=new p(1,0,0),J=new p(0,1,0),X=new p(0,0,1);function Z(){return{point:new p,normal:new p,hasNormal:!1}}const tt=["mixamorigHips","Hips","hips","Pelvis","pelvis"],et={upper:["LeftUpLeg","leftUpLeg","left_up_leg"],lower:["LeftLeg","leftLeg","left_leg"],foot:["LeftFoot","leftFoot","left_foot"]},ot={upper:["RightUpLeg","rightUpLeg","right_up_leg"],lower:["RightLeg","rightLeg","right_leg"],foot:["RightFoot","rightFoot","right_foot"]},it=["left","right"];let st=class extends e{constructor(){super(...arguments),this.viewController=r(n),this.poseProcessors=new x,this.transitionProcessors=[],this.managedActions=[],this.stateMachines=[],this.upperStateMachines=[],this.fadeTime=.2,this.movementSpeed=null,this.upperBodyTimer=0,this.upperBodyOverride=!1,this.fullBodyTimer=0,this.currentFullBodyPriority=-1,this.currentUpperBodyPriority=-1,this.sequenceAnimationControlled=!1,this.sequenceAnimationReleasePending=!1,this.sequenceAnimationReleaseFrames=0,this.externalControlActive=!1,this.world=r(w),this.physicsSystem=r(v),this._footIkEnabled=!1,this.footIkMoving=!1,this.footIkLocomotionEnabled=!1,this.footIkLocomotionActive=!0,this.footIkMaxPlantTurnAngleDeg=35,this.footIkMaxPlantDrift=.18,this.footIkMaxPlantPelvisDrop=.12,this.footIkRayStartHeight=.5,this.footIkRayLength=1.5,this.footIkRaycastMode="physics",this.footIkFootOffset=.08,this.footIkAutoContactOffset=!1,this.footIkPositionLerpSpeed=28,this.footIkRotationLerpSpeed=14,this.footIkWeightInSpeed=34,this.footIkWeightOutSpeed=30,this.footIkMaxSlopeAngleDeg=40,this.footIkForwardSampleDistance=.14,this.footIkSideSampleDistance=.08,this.footIkMaxHorizontalOffset=.22,this.footIkMaxVerticalOffsetDown=.75,this.footIkMaxVerticalOffsetUp=.2,this.footIkPelvisEnabled=!0,this.footIkPelvisBone="mixamorigHips",this.footIkPelvisLerpSpeed=24,this.footIkPelvisMaxOffsetDown=.4,this.footIkPelvisMaxOffsetUp=.08,this.footIkRequireGrounded=!0,this.footIkSimpleTiltOnly=!1,this.footIkDebug=!1,this.footIkDebugNormalLength=.25,this.footIkDebugAxisLength=.2,this.footIkExtraClearance=.01,this.footIkPlantReleaseUpSpeed=.08,this.footIkPlantReleaseExtension=.9,this.footIkPlantAttachExtension=.96,this.footIkPlantReleaseLift=.055,this.footIkPlantAttachLift=.02,this.leftLegIkBones={upperLeg:"mixamorigLeftUpLeg",lowerLeg:"mixamorigLeftLeg",foot:"mixamorigLeftFoot"},this.rightLegIkBones={upperLeg:"mixamorigRightUpLeg",lowerLeg:"mixamorigRightLeg",foot:"mixamorigRightFoot"},this.ikPelvisOffsetY=0,this.ikPelvisDescendantStack=[],this.ikWorldUpdateNodes=[],this.footIkStateReset=!0,this.footIkUsingContacts=!1,this.footIkLastRootPosition=new p,this.footIkHasRootSample=!1,this.ikPreSolvePelvisPosition=new p,this.footIkPoseApplied=!1,this.footIkAnimationPoseCaptured=!1,this.footIkContactTransitionTime=0,this.footIkTransitionLeftContact=!1,this.footIkTransitionRightContact=!1,this.ikLegState={left:{anchor:new D,contactRequested:!1,targetPosition:new p,normal:new p(0,1,0),forward:new p(0,0,1),pitch:0,contactOffset:0,planted:!1,lastFootWorldPos:new p,hasLastFootSample:!1,weight:0,top:!1},right:{anchor:new D,contactRequested:!1,targetPosition:new p,normal:new p(0,1,0),forward:new p(0,0,1),pitch:0,contactOffset:0,planted:!1,lastFootWorldPos:new p,hasLastFootSample:!1,weight:0,top:!1}},this.ikWarnedMissingBones=!1,this.raycaster=new d,this.raycastIntersections=[],this.footIkRayTo=new p,this.footIkRayTestResult=new b,this.footIkRayTestOptions={debug:!1,excludeTriggers:!0,resolveActor:!1,collisionFilter:-2},this.footIkHits={center:Z(),toe:Z(),heel:Z(),side:Z()},this.footIkAxesScratch={up:new p,forward:new p,right:new p},this.footIkSampleAxesScratch={forward:new p,right:new p},this.footIkScratch={footWorldPos:new p,centerRayStart:new p,toeRayStart:new p,heelRayStart:new p,sideRayStart:new p,desiredTargetPos:new p,desiredNormal:new p,desiredForward:new p,debugRayEndA:new p,debugRayEndB:new p,debugRayEndC:new p,debugRayEndD:new p,debugRayEndE:new p,debugRayEndF:new p},this.ikPreSolve={left:{upper:new g,lower:new g,foot:new g,footWorld:new g},right:{upper:new g,lower:new g,foot:new g,footWorld:new g}},this.ikSolveScratch={hip:new p,knee:new p,foot:new p,direction:new p,animatedDirection:new p,pole:new p,kneeTarget:new p,target:new p,currentDirection:new p,desiredDirection:new p,rotation:new g},this.getFullBodyClip=lt(t=>t.uuid,t=>rt(this.fullBodyMask,t)),this.getUpperBodyClip=lt(t=>t.uuid,t=>rt(this.upperBodyMask,t))}get isSequenceControlled(){return this.externalControlActive||this.sequenceAnimationControlled||this.sequenceAnimationReleasePending}get hasUpperBodyOverride(){return this.upperBodyOverride}get footIkEnabled(){return this._footIkEnabled}set footIkEnabled(t){this._footIkEnabled!==t&&(this.restoreFootIkPose(),this._footIkEnabled=t,null!=this.ikRoot&&(this.disposeFootIkDebug(),this.initializeFootIk(this.ikRoot,this.ikRootBone),this.syncFootIkDebug()))}onInit(){this.viewController.onUpdate(this.actor).subscribe(t=>this.updateInternal(t)),this.characterMovement=I(this.actor),this.footIkRayTestOptions.excludeActor=this.actor}onEndPlay(){this.restoreFootIkPose(),this.resetFootIkContacts(),this.disposeFootIkDebug()}raycastDownFirstValid(t,e,o){const i=this.world.scene;this.raycaster.set(t,W),this.raycaster.near=0,this.raycaster.far=e,this.raycastIntersections.length=0,this.raycaster.intersectObject(i,!0,this.raycastIntersections);for(const t of this.raycastIntersections)if(!(this.isFootIkDebugObject(t.object)||this.isRelatedToActorHierarchy(t.object)||this.isSkinnedMeshObject(t.object)))return o.point.copy(t.point),null!=t.face?(o.normal.copy(t.face.normal).transformDirection(t.object.matrixWorld).normalize(),o.hasNormal=!0):o.hasNormal=!1,this.raycastIntersections.length=0,!0;return this.raycastIntersections.length=0,!1}rayTestDown(t,e,o){const i=this.footIkRayTo.copy(t).addScaledVector(W,e),s=this.physicsSystem.rayTest(t,i,this.footIkRayTestResult,this.footIkRayTestOptions);return!!s.hasHit&&(o.point.copy(s.hitPoint),o.normal.copy(s.hitNormal),o.normal.lengthSq()>1e-8?(o.normal.normalize(),o.hasNormal=!0):o.hasNormal=!1,!0)}isRelatedToActorHierarchy(t){const e=this.actor?.object;return null!=e&&null!=t&&(ct(t,e)||ct(e,t))}isFootIkDebugObject(t){let e=t;for(;null!=e;){if(!0===e.userData?.footIkDebug)return!0;e=e.parent}return!1}isSkinnedMeshObject(t){let e=t;for(;null!=e;){if(e instanceof u||!0===e.isSkinnedMesh)return!0;e=e.parent}return!1}getRootMotionAction(){if(this.fullBodyAction.getClip()instanceof s)return this.fullBodyAction}getFullBodyAction(){return this.fullBodyAction}getFullBodySourceClip(){const t=this.fullBodyClip;return null!=t&&this.fullBodyAction?.getClip()===this.getFullBodyClip(t)?t:void 0}setup(t,e,o){this.restoreFootIkPose(),null!=e&&(this.upperBodyMask=nt(e),this.fullBodyMask=function(t,e){const o=new Set(nt(e).map(t=>t.uuid)),i=[];return t.traverse(t=>{(t instanceof l||t.isBone)&&!o.has(t.uuid)&&i.push(t)}),i}(ht(t),e)),null!=this.mixer&&this.mixer.stopAllAction(),this.mixer=new a(t),this.managedActions.length=0,this.animationRigId=F(t),this.animationRigData=R(t),this.ikRoot=t,this.ikRootBone=o,this.disposeFootIkDebug(),this.initializeFootIk(t,o),this.syncFootIkDebug()}updateStateMachines(t){this.stateMachines.forEach(e=>{e.step(t);const o=e.current.clip;null!=o&&this.play(o,{priority:e.current.options.priority??0,loop:e.current.options.loop??!0})}),this.upperStateMachines.forEach(e=>{e.step(t);const o=e.current.clip;null!=o?this.playUpper(o,{priority:e.current.options.priority??0,loop:e.current.options.loop??!0}):this.play(this.fullBodyClip)})}addPoseProcessor(t){const e={id:t.id,phase:t.phase??"afterMixer",order:t.order??0,update:t.update};this.removePoseProcessor(e.id);const o=this.poseProcessors.get(e.phase);let i=o.findIndex(t=>t.order>e.order);return-1===i&&(i=o.length),o.splice(i,0,e),()=>{const t=o.indexOf(e);-1!==t&&(o.splice(t,1),0===o.length&&this.poseProcessors.delete(e.phase))}}removePoseProcessor(t){for(const[e,o]of this.poseProcessors.entries()){const i=o.findIndex(e=>e.id===t);if(-1!==i)return o.splice(i,1),void(0===o.length&&this.poseProcessors.delete(e))}}addTransitionProcessor(t){const e=this.transitionProcessors.findIndex(e=>e.id===t.id);return e>=0&&this.transitionProcessors.splice(e,1),this.transitionProcessors.push(t),()=>{const e=this.transitionProcessors.indexOf(t);e>=0&&this.transitionProcessors.splice(e,1)}}preparePoseTransition(t,e,o){if(0===this.transitionProcessors.length)return!1;const i=this.fullBodyAction,s=this.getFullBodySourceClip();if(null==i||null==s||i.getClip().uuid===e.uuid||this.isSequenceControlled||o.sequenceControlled||this.upperBodyOverride)return!1;const r=this.transitionContext??(this.transitionContext={});r.animation=this,r.source=i,r.sourceClip=s,r.targetClip=t,r.targetPoseClip=e,r.targetTimeScale=this.getPlaybackTimeScale(e,o.timeScale??1),r.options=o;for(const t of this.transitionProcessors)if(t.prepare(r))return!0;return!1}runPoseProcessors(t,e){if(0===this.poseProcessors.size||null==this.mixer||!this.poseProcessors.present(t))return;const o=this.mixer.getRoot();if(!(o instanceof h))return;const i=this.poseProcessorContext??(this.poseProcessorContext={deltaTime:e,phase:t,animation:this,mixer:this.mixer,root:o});i.deltaTime=e,i.phase=t,i.mixer=this.mixer,i.root=o;const s=this.poseProcessors.get(t);for(let t=0;t<s.length;t++)s[t].update(i)}updateInternal(t){if(null==this.mixer)return;if(this.externalControlActive)return;this.restoreFootIkPose(),this.upperBodyTimer+=t*(this.upperBodyAction?.timeScale??1),this.fullBodyTimer+=t*(this.fullBodyAction?.timeScale??1);let e=!1;this.sequenceAnimationReleasePending&&(this.sequenceAnimationReleaseFrames>0?(e=!0,this.sequenceAnimationReleaseFrames--):(this.sequenceAnimationReleasePending=!1,this.currentFullBodyPriority=-1,this.sequenceAnimationControlled=!1,this.overrideFadeTime=null,this.fullBodyTimer=0)),this.upperBodyAction&&this.upperBodyOverride&&this.upperBodyAction.getClip().duration-2*(this.overrideFadeTimeUpper??this.fadeTime)<this.upperBodyTimer&&(this.upperBodyOverride=!1,null!=this.fullBodyClip&&this.transition(this.upperBodyAction,this.getUpperBodyClip(this.fullBodyClip)),this.upperBodyAction=null,this.overrideFadeTimeUpper=null),!this.sequenceAnimationControlled&&!this.sequenceAnimationReleasePending&&this.fullBodyAction&&this.fullBodyAction.loop===c&&this.fullBodyAction.getClip().duration-2*(this.overrideFadeTime??this.fadeTime)<this.fullBodyTimer&&(this.currentFullBodyPriority=-1,this.overrideFadeTime=null),null!=this.characterMovement&&(this.movementSpeed=this.characterMovement.horizontalSpeed),e||this.updateStateMachines(t),this.syncMovementSpeed(this.fullBodyAction),this.upperBodyOverride||this.syncMovementSpeed(this.upperBodyAction),this.runPoseProcessors("beforeMixer",t),this.mixer.update(t),this.runPoseProcessors("afterMixer",t),this.captureFootIkAnimationPose(),this.runPoseProcessors("beforeIk",t),this.updateFootIk(t),this.runPoseProcessors("afterIk",t)}initializeFootIk(t,e){if(this.resetFootIkContacts(),this.ikSolver=null,this.ikSkinnedMesh=null,this.ikBoneRefs=null,this.ikLegLengths=null,this.ikPelvisBone=null,this.ikPelvisOffsetY=0,this.ikFootAxes=null,this.ikTargetBones=null,this.ikWorldUpdateNodes.length=0,this.footIkStateReset=!0,this.ikWarnedMissingBones=!1,this.ikLegState.left.weight=0,this.ikLegState.right.weight=0,!this.footIkEnabled)return;const o=this.findFirstSkinnedMesh(t);if(null==o||null==o.skeleton)return;this.ikSkinnedMesh=o,t.updateWorldMatrix(!0,!0);const i=o.skeleton,s=B(this.animationRigData,"leftLeg"),r=B(this.animationRigData,"rightLeg"),n=this.resolveLegBoneRefs(i,this.leftLegIkBones,et,s),a=this.resolveLegBoneRefs(i,this.rightLegIkBones,ot,r);if(null==n||null==a)return void(this.ikWarnedMissingBones||(this.ikWarnedMissingBones=!0,console.warn(`[CharacterAnimationComponent] Foot IK disabled: missing leg bones. Left=${this.leftLegIkBones.upperLeg}/${this.leftLegIkBones.lowerLeg}/${this.leftLegIkBones.foot}, Right=${this.rightLegIkBones.upperLeg}/${this.rightLegIkBones.lowerLeg}/${this.rightLegIkBones.foot}`)));this.ikBoneRefs={left:n,right:a},this.ikLegLengths={left:this.computeLegLengths(n),right:this.computeLegLengths(a)},this.ikFootAxes={left:this.detectFootAxisBasis(n.foot),right:this.detectFootAxisBasis(a.foot)};const h=this.animationRigData?.bones.find(t=>t.id===this.animationRigData?.pelvisBoneId)?.name;this.ikPelvisBone=this.resolvePelvisBone(i,n,a,h),this.rebuildFootIkWorldUpdateNodes(),this.ikLegState.left.targetPosition.copy(n.foot.getWorldPosition(V)),this.ikLegState.right.targetPosition.copy(a.foot.getWorldPosition(V)),this.ikLegState.left.normal.set(0,1,0),this.ikLegState.right.normal.set(0,1,0),this.ikLegState.left.pitch=0,this.ikLegState.right.pitch=0,this.ikLegState.left.contactOffset=this.estimateFootContactOffset(i,"left",n.foot),this.ikLegState.right.contactOffset=this.estimateFootContactOffset(i,"right",a.foot),this.ikLegState.left.planted=!1,this.ikLegState.right.planted=!1,this.ikLegState.left.lastFootWorldPos.copy(n.foot.getWorldPosition(V)),this.ikLegState.right.lastFootWorldPos.copy(a.foot.getWorldPosition(V)),this.ikLegState.left.hasLastFootSample=!1,this.ikLegState.right.hasLastFootSample=!1;const c=this.resolveFootAxes("left",n.foot),d=this.resolveFootAxes("right",a.foot);this.ikLegState.left.forward.copy(c.forward),this.ikLegState.right.forward.copy(d.forward);const p=e??ht(t);if(null==p)return;let u=i.getBoneByName("_IKTargetLeftFoot"),g=i.getBoneByName("_IKTargetRightFoot"),k=!1;if(null==u&&(u=new l,u.name="_IKTargetLeftFoot",p.add(u),k=!0),null==g&&(g=new l,g.name="_IKTargetRightFoot",p.add(g),k=!0),this.setBoneWorldPosition(u,n.foot.getWorldPosition(V)),this.setBoneWorldPosition(g,a.foot.getWorldPosition(V)),k||i.bones.indexOf(u)<0||i.bones.indexOf(g)<0){const e=i.bones.slice(),o=i.boneInverses.map(t=>t.clone());e.indexOf(u)<0&&(e.push(u),o.push((new m).copy(u.matrixWorld).invert())),e.indexOf(g)<0&&(e.push(g),o.push((new m).copy(g.matrixWorld).invert()));const s=new f(e,o);t.traverse(t=>{const e=t;!0===e.isSkinnedMesh&&e.skeleton===i&&e.bind(s,e.bindMatrix)})}this.ikTargetBones={left:u,right:g},this.rebuildFootIkWorldUpdateNodes();const y=o.skeleton.bones,S={target:y.indexOf(u),effector:y.indexOf(n.foot),links:[{index:y.indexOf(n.lower)},{index:y.indexOf(n.upper)}],iteration:8},I={target:y.indexOf(g),effector:y.indexOf(a.foot),links:[{index:y.indexOf(a.lower)},{index:y.indexOf(a.upper)}],iteration:8};if(S.target<0||S.effector<0||S.links.some(t=>t.index<0)||I.target<0||I.effector<0||I.links.some(t=>t.index<0))return console.warn("[CharacterAnimationComponent] Foot IK disabled: failed to resolve IK indexes in skeleton."),void(this.ikSolver=null);this.ikSolver=new P(o,[S,I])}findFirstSkinnedMesh(t){let e;return t.traverse(t=>{null==e&&(t instanceof u||!0===t.isSkinnedMesh)&&(e=t)}),e}resolveLegBoneRefs(t,e,o,i=[]){const s=this.findBoneByNames(t,[i[0],e.upperLeg,...o.upper].filter(t=>null!=t)),r=this.findBoneByNames(t,[i.at(-2),e.lowerLeg,...o.lower].filter(t=>null!=t)),n=this.findBoneByNames(t,[i.at(-1),e.foot,...o.foot].filter(t=>null!=t));if(null!=s&&null!=r&&null!=n)return{upper:s,lower:r,foot:n}}findBoneByNames(t,e){for(const o of e){const e=t.getBoneByName(o);if(null!=e)return e}}resolvePelvisBone(t,e,o,i){const s=this.findBoneByNames(t,[i,this.footIkPelvisBone,...tt].filter(t=>null!=t));if(null!=s)return s;const r=e.upper.parent,n=o.upper.parent;return null!=r&&r===n&&(r instanceof l||!0===r.isBone)||null!=r&&(r instanceof l||!0===r.isBone)?r:void 0}computeLegLengths(t){const e=t.upper.getWorldPosition(V),o=t.lower.getWorldPosition(E),i=t.foot.getWorldPosition(H),s=e.distanceTo(o),r=o.distanceTo(i);return{upper:s,lower:r,total:s+r}}setBoneWorldPosition(t,e){const o=t.parent;null==o?t.position.copy(e):t.position.copy(V.copy(e).applyMatrix4($.copy(o.matrixWorld).invert())),t.quaternion.copy(U),t.scale.copy(q),t.updateMatrixWorld(!0)}syncFootIkDebug(){this.footIkDebug&&null!=this.ikRoot?(null!=this.ikDebugRoot&&null!=this.ikDebugLegs||(this.ikDebugRoot=new S,this.ikDebugRoot.name="FootIKDebug",this.ikDebugRoot.userData.footIkDebug=!0,this.ikDebugLegs={left:this.createDebugLeg("LeftFootIK",53503),right:this.createDebugLeg("RightFootIK",16755200)},this.ikDebugRoot.add(this.ikDebugLegs.left.root,this.ikDebugLegs.right.root),this.world.scene.add(this.ikDebugRoot)),this.ikDebugRoot.visible=!0):null!=this.ikDebugRoot&&(this.ikDebugRoot.visible=!1)}disposeFootIkDebug(){null!=this.ikDebugRoot&&(this.ikDebugRoot.removeFromParent(),this.ikDebugRoot.clear()),this.ikDebugRoot=null,this.ikDebugLegs=null}createDebugLeg(t,e){const o=new S;o.name=t,o.userData.footIkDebug=!0;const i=this.createDebugArrow(e),s=this.createDebugArrow(e),r=this.createDebugArrow(e),n=this.createDebugArrow(e),a=this.createDebugArrow(65280),l=this.createDebugArrow(6750054),h=this.createDebugArrow(16711935),c=this.createDebugArrow(16776960),d=this.createDebugArrow(16737792);return o.add(i,s,r,n,a,l,h,c,d),{root:o,rayCenter:i,rayToe:s,rayHeel:r,raySide:n,hitNormal:a,footUp:l,footToTarget:h,solveUpper:c,solveLower:d}}createDebugArrow(t){const e=new y(O,new p,.001,t);return e.userData.footIkDebug=!0,e.traverse(t=>{t.userData.footIkDebug=!0,t.raycast=()=>{}}),e.visible=!1,e}setDebugArrow(t,e,o){V.subVectors(o,e);const i=V.length();i<1e-6?t.visible=!1:(t.visible=!0,t.position.copy(e),t.setDirection(V.multiplyScalar(1/i)),t.setLength(i,Math.min(.08,.25*i),Math.min(.05,.2*i)))}resetDebugVisibility(){null!=this.ikDebugLegs&&(this.ikDebugLegs.left.rayCenter.visible=!1,this.ikDebugLegs.left.rayToe.visible=!1,this.ikDebugLegs.left.rayHeel.visible=!1,this.ikDebugLegs.left.raySide.visible=!1,this.ikDebugLegs.left.hitNormal.visible=!1,this.ikDebugLegs.left.footUp.visible=!1,this.ikDebugLegs.left.footToTarget.visible=!1,this.ikDebugLegs.left.solveUpper.visible=!1,this.ikDebugLegs.left.solveLower.visible=!1,this.ikDebugLegs.right.rayCenter.visible=!1,this.ikDebugLegs.right.rayToe.visible=!1,this.ikDebugLegs.right.rayHeel.visible=!1,this.ikDebugLegs.right.raySide.visible=!1,this.ikDebugLegs.right.hitNormal.visible=!1,this.ikDebugLegs.right.footUp.visible=!1,this.ikDebugLegs.right.footToTarget.visible=!1,this.ikDebugLegs.right.solveUpper.visible=!1,this.ikDebugLegs.right.solveLower.visible=!1)}updateFootIk(t){if(this.syncFootIkDebug(),!this.footIkEnabled||null==this.ikBoneRefs||null==this.ikRoot)return void this.resetDebugVisibility();if(this.footIkUsingContacts=!1,this.footIkLocomotionEnabled){const e=this.fullBodyAction,o=e?.getClip(),i=C(o);if(!this.footIkLocomotionActive||this.isSequenceControlled||null==i||null==e||e.getEffectiveWeight()<.65||!Number.isFinite(t)||t<0)return this.resetFootIkContacts(),void this.resetDebugVisibility();(this.footIkContactClip!==o||t>.2)&&(this.resetFootIkContacts(),this.footIkContactClip=o);const s=this.actor.object.getWorldPosition(V),r=(this.movementSpeed??0)*Math.max(0,t);this.footIkHasRootSample&&this.footIkLastRootPosition.distanceToSquared(s)>(1+r)**2&&(this.resetFootIkContacts(),this.footIkContactClip=o),this.footIkLastRootPosition.copy(s),this.footIkHasRootSample=!0,this.footIkUsingContacts=!0;const n=e.loop!==c;this.footIkContactTransitionTime=Math.max(0,this.footIkContactTransitionTime-t),this.ikLegState.left.contactRequested=this.footIkContactTransitionTime>0?this.footIkTransitionLeftContact:M(i.left,e.time,o.duration,n),this.ikLegState.right.contactRequested=this.footIkContactTransitionTime>0?this.footIkTransitionRightContact:M(i.right,e.time,o.duration,n)}if(!this.footIkUsingContacts&&!this.footIkMoving&&(this.movementSpeed??0)>.01)return this.footIkStateReset||(this.syncFootIkWorldMatrices(),this.resetFootIkState(),this.footIkStateReset=!0),void this.resetDebugVisibility();this.syncFootIkWorldMatrices(),this.footIkStateReset=!1;const e=this.characterMovement?.isGrounded??!0;if(this.footIkSimpleTiltOnly)return this.updatePelvisOffset(t,!1),this.updateLegTiltSimple("left",this.ikBoneRefs.left.foot,this.ikLegState.left,e,t),void this.updateLegTiltSimple("right",this.ikBoneRefs.right.foot,this.ikLegState.right,e,t);if(null==this.ikLegLengths||null==this.ikTargetBones||null==this.ikSkinnedMesh||null==this.ikSolver)return this.updatePelvisOffset(t,!1),void this.resetDebugVisibility();if(this.footIkUsingContacts&&null!=this.ikPelvisBone&&this.ikPreSolvePelvisPosition.copy(this.ikPelvisBone.position),this.updateLegIkTarget("left",this.ikBoneRefs.left,this.ikLegLengths.left,this.ikTargetBones.left,this.ikLegState.left,e,t),this.updateLegIkTarget("right",this.ikBoneRefs.right,this.ikLegLengths.right,this.ikTargetBones.right,this.ikLegState.right,e,t),this.updatePelvisOffset(t,!0),this.setBoneWorldPosition(this.ikTargetBones.left,this.ikLegState.left.targetPosition),this.setBoneWorldPosition(this.ikTargetBones.right,this.ikLegState.right.targetPosition),this.ikPreSolve.left.upper.copy(this.ikBoneRefs.left.upper.quaternion),this.ikPreSolve.left.lower.copy(this.ikBoneRefs.left.lower.quaternion),this.ikPreSolve.left.foot.copy(this.ikBoneRefs.left.foot.quaternion),this.footIkUsingContacts&&!this.footIkAnimationPoseCaptured&&this.getCurrentWorldQuaternion(this.ikBoneRefs.left.foot,this.ikPreSolve.left.footWorld),this.ikPreSolve.right.upper.copy(this.ikBoneRefs.right.upper.quaternion),this.ikPreSolve.right.lower.copy(this.ikBoneRefs.right.lower.quaternion),this.ikPreSolve.right.foot.copy(this.ikBoneRefs.right.foot.quaternion),this.footIkUsingContacts&&!this.footIkAnimationPoseCaptured&&this.getCurrentWorldQuaternion(this.ikBoneRefs.right.foot,this.ikPreSolve.right.footWorld),null!=this.ikDebugLegs&&(this.ikDebugLegs.left.solveUpper.visible=!1,this.ikDebugLegs.left.solveLower.visible=!1,this.ikDebugLegs.right.solveUpper.visible=!1,this.ikDebugLegs.right.solveLower.visible=!1),this.footIkUsingContacts?(this.ikLegState.left.weight>.001&&this.solveTwoBoneLeg("left",this.ikBoneRefs.left,this.ikLegState.left.targetPosition),this.ikLegState.right.weight>.001&&this.solveTwoBoneLeg("right",this.ikBoneRefs.right,this.ikLegState.right.targetPosition)):this.ikSolver.update(),this.blendLegAfterSolve(this.ikBoneRefs.left,this.ikPreSolve.left,this.ikLegState.left.weight),this.blendLegAfterSolve(this.ikBoneRefs.right,this.ikPreSolve.right,this.ikLegState.right.weight),this.footIkUsingContacts){this.syncFootIkWorldMatrices();for(const t of it){const e=this.ikBoneRefs[t].foot,o=this.getCurrentWorldQuaternion(e.parent,j);e.quaternion.copy(o.invert().multiply(this.ikPreSolve[t].footWorld))}this.footIkPoseApplied=!0}this.applySimpleFootPitch("left",this.ikBoneRefs.left.foot,this.ikLegState.left),this.applySimpleFootPitch("right",this.ikBoneRefs.right.foot,this.ikLegState.right)}resetFootIkState(){if(null!=this.ikBoneRefs){for(const t of it){const e=this.ikBoneRefs[t],o=this.ikLegState[t],i=V.setFromMatrixPosition(e.foot.matrixWorld);o.targetPosition.copy(i),o.lastFootWorldPos.copy(i),o.hasLastFootSample=!0,o.weight=0,o.normal.set(0,1,0);const s=this.resolveFootAxes(t,e.foot,this.footIkAxesScratch);o.forward.copy(s.forward),o.pitch=0,this.footIkAutoContactOffset?o.contactOffset=Math.max(o.contactOffset,this.footIkFootOffset+this.footIkExtraClearance):o.contactOffset=this.footIkFootOffset+this.footIkExtraClearance,o.planted=!1,o.anchor.reset()}this.ikPelvisOffsetY=0}}resetFootIkContacts(){for(const t of it){const e=this.ikLegState[t];e.anchor.reset(),e.contactRequested=!1,e.planted=!1,e.weight=0,e.hasLastFootSample=!1}this.footIkContactClip=void 0,this.footIkHasRootSample=!1,this.ikPelvisOffsetY=0,this.footIkContactTransitionTime=0}preserveFootIkContacts(t,e){this.footIkEnabled&&this.footIkLocomotionEnabled&&e>0&&(this.footIkContactClip=t,this.footIkTransitionLeftContact=this.ikLegState.left.anchor.locked,this.footIkTransitionRightContact=this.ikLegState.right.anchor.locked,this.footIkContactTransitionTime=e)}restoreFootIkPose(){if(this.footIkPoseApplied&&null!=this.ikBoneRefs){for(const t of it){const e=this.ikBoneRefs[t],o=this.ikPreSolve[t];e.upper.quaternion.copy(o.upper),e.lower.quaternion.copy(o.lower),e.foot.quaternion.copy(o.foot)}this.ikPelvisBone?.position.copy(this.ikPreSolvePelvisPosition),this.footIkPoseApplied=!1}}captureFootIkAnimationPose(){this.footIkAnimationPoseCaptured=!1,this.footIkEnabled&&this.footIkLocomotionEnabled&&this.footIkLocomotionActive&&!this.isSequenceControlled&&null!=this.ikBoneRefs&&null!=C(this.fullBodyAction?.getClip())&&(this.syncFootIkWorldMatrices(),this.getCurrentWorldQuaternion(this.ikBoneRefs.left.foot,this.ikPreSolve.left.footWorld),this.getCurrentWorldQuaternion(this.ikBoneRefs.right.foot,this.ikPreSolve.right.footWorld),this.footIkAnimationPoseCaptured=!0)}updatePelvisOffset(t,e){if(null==this.ikPelvisBone)return;let o=0;if(e&&this.footIkPelvisEnabled&&null!=this.ikBoneRefs&&null!=this.ikLegLengths){this.ikLegState.left.top=!1,this.ikLegState.right.top=!1;for(const t of it){const e=this.ikBoneRefs[t],i=this.ikLegLengths[t],s=this.ikLegState[t];if(!s.planted)continue;const r=V.setFromMatrixPosition(e.upper.matrixWorld),n=.998*i.total,a=this.computeRequiredPelvisDrop(r,s.targetPosition,n);if(a>0){const e="left"===t?"right":"left";this.ikLegState[e].top=!0}o=Math.min(o,-a)}const t=this.footIkUsingContacts?Math.min(this.footIkPelvisMaxOffsetDown,this.footIkMaxPlantPelvisDrop):this.footIkPelvisMaxOffsetDown;o=k.clamp(o,-t,this.footIkPelvisMaxOffsetUp)}const i=1-Math.exp(-this.footIkPelvisLerpSpeed*t);this.ikPelvisOffsetY=k.lerp(this.ikPelvisOffsetY,o,i);const s=E.setFromMatrixPosition(this.ikPelvisBone.matrixWorld),r=H.copy(s).addScaledVector(O,this.ikPelvisOffsetY);null==this.ikPelvisBone.parent?this.ikPelvisBone.position.copy(r):this.ikPelvisBone.position.copy(r.applyMatrix4($.copy(this.ikPelvisBone.parent.matrixWorld).invert())),this.updatePelvisWorldTranslation(s)}updatePelvisWorldTranslation(t){const e=this.ikPelvisBone;if(null==e)return;e.updateMatrix(),null==e.parent?e.matrixWorld.copy(e.matrix):e.matrixWorld.multiplyMatrices(e.parent.matrixWorld,e.matrix),e.matrixWorldNeedsUpdate=!1;const o=e.matrixWorld.elements,i=o[12]-t.x,s=o[13]-t.y,r=o[14]-t.z;if(0===i&&0===s&&0===r)return;const n=this.ikPelvisDescendantStack;n.length=0;for(let t=0;t<e.children.length;t++)n.push(e.children[t]);for(;n.length>0;){const t=n.pop(),e=t.matrixWorld.elements;e[12]+=i,e[13]+=s,e[14]+=r,t.matrixWorldNeedsUpdate=!1;for(let e=0;e<t.children.length;e++)n.push(t.children[e])}}rebuildFootIkWorldUpdateNodes(){const t=this.ikRoot,e=this.ikBoneRefs;if(null==t||null==e)return void(this.ikWorldUpdateNodes.length=0);const o=new Set,i=[this.ikPelvisBone,e.left.upper,e.left.lower,e.left.foot,e.right.upper,e.right.lower,e.right.foot,this.ikTargetBones?.left,this.ikTargetBones?.right];for(const e of i){let i=e;for(;null!=i&&(o.add(i),i!==t);)i=i.parent??void 0}this.ikWorldUpdateNodes.length=0,t.traverse(t=>{o.has(t)&&this.ikWorldUpdateNodes.push(t)})}syncFootIkWorldMatrices(){const t=this.ikRoot;if(null!=t){t.updateWorldMatrix(!0,!1);for(const e of this.ikWorldUpdateNodes)e!==t&&(e.matrixAutoUpdate&&e.updateMatrix(),e.matrixWorldAutoUpdate&&(null==e.parent?e.matrixWorld.copy(e.matrix):e.matrixWorld.multiplyMatrices(e.parent.matrixWorld,e.matrix)),e.matrixWorldNeedsUpdate=!1)}}computeFootVerticalSpeed(t,e,o){return!t.hasLastFootSample||o<=1e-6?0:(e.y-t.lastFootWorldPos.y)/o}updateFootSample(t,e){t.lastFootWorldPos.copy(e),t.hasLastFootSample=!0}estimateFootContactOffset(t,e,o){const i=this.footIkFootOffset+this.footIkExtraClearance;if(!this.footIkAutoContactOffset)return i;const s=t.bones.indexOf(o);if(s<0)return i;const r=t.boneInverses[s];if(null==r)return i;const n=this.ikFootAxes?.[e]??this.detectFootAxisBasis(o),a=o.getWorldPosition(E),h=H.copy(n.upLocal).applyQuaternion(this.getCurrentWorldQuaternion(o,j)).normalize();let c=0;return o.traverse(e=>{if(!(e instanceof l)||e===o)return;const i=t.bones.indexOf(e);if(i<0)return;const s=$.copy(t.boneInverses[i]).invert(),n=K.copy(r).multiply(s),d=V.setFromMatrixPosition(n),p=N.copy(d).applyMatrix4(o.matrixWorld).sub(a);c=Math.min(c,p.dot(h))}),Math.max(i,-c+this.footIkExtraClearance)}shouldPlantFoot(t,e,o,i,s,r){if(!e||!o)return!1;if(this.footIkUsingContacts)return t.contactRequested;const n=this.footIkPlantReleaseExtension,a=this.footIkPlantAttachExtension,l=this.footIkPlantReleaseLift,h=this.footIkPlantAttachLift;return t.planted?!(i>this.footIkPlantReleaseUpSpeed&&(s<n||r>l)):s>=a&&r<=h||i<=0&&s>=n}computeLegExtensionRatio(t,e){const o=V.setFromMatrixPosition(t.upper.matrixWorld),i=E.setFromMatrixPosition(t.foot.matrixWorld),s=o.distanceTo(i);return e.total<=1e-6?1:k.clamp(s/e.total,0,1.2)}computeRequiredPelvisDrop(t,e,o){const i=N.subVectors(t,e),s=i.length();if(s<=o+1e-6)return 0;const r=i.dot(O),n=o*o-Math.max(s*s-r*r,0);if(n<=0)return s-o;const a=Math.sqrt(n),l=r-a,h=r+a;return l>=0?l:h>=0?h:0}updateLegTiltSimple(t,e,o,i,s){const r=this.footIkScratch,n=this.footIkHits,a=r.footWorldPos.setFromMatrixPosition(e.matrixWorld),l=this.computeFootVerticalSpeed(o,a,s),h=this.resolveFootAxes(t,e,this.footIkAxesScratch),c=this.resolveGroundSampleAxes(t,e,h,this.footIkSampleAxesScratch),d=r.centerRayStart.copy(a).addScaledVector(O,this.footIkRayStartHeight),p=r.toeRayStart.copy(d).addScaledVector(c.forward,this.footIkForwardSampleDistance),u=r.heelRayStart.copy(d).addScaledVector(c.forward,-this.footIkForwardSampleDistance),f=r.sideRayStart.copy(d).addScaledVector(c.right,this.footIkSideSampleDistance),g=this.getFirstGroundHit(d,n.center),m=this.getFirstGroundHit(p,n.toe),y=this.getFirstGroundHit(u,n.heel),S=this.getFirstGroundHit(f,n.side),I=r.desiredNormal.set(0,1,0),P=r.desiredForward.copy(c.forward);let w=0,v=0,b=this.footIkWeightOutSpeed;const x=null!=g||null!=m||null!=y;let L=a.y;null!=g?L=g.point.y:null!=m&&null!=y?L=.5*(m.point.y+y.point.y):null!=m?L=m.point.y:null!=y&&(L=y.point.y);const B=this.ikBoneRefs?.[t],R=this.ikLegLengths?.[t],F=null!=B&&null!=R?this.computeLegExtensionRatio(B,R):1,A=Math.max(0,a.y-L,a.y-o.targetPosition.y),D=this.shouldPlantFoot(o,!this.footIkRequireGrounded||i,x,l,F,A);D?this.computeDesiredGroundNormal(c,g,m,y,S,I)&&(this.computeDesiredFootForward(c.forward,c.right,I,m,y,P),w=this.computeDesiredPitchAngle(m,y,c.forward),v=1,b=this.footIkWeightInSpeed):this.computeDesiredFootForward(c.forward,c.right,I,null,null,P);const C=1-Math.exp(-this.footIkPositionLerpSpeed*s),M=1-Math.exp(-b*s);o.normal.lerp(I,C).normalize(),o.forward.lerp(P,C).normalize(),o.pitch=k.lerp(o.pitch,w,C),o.weight=k.lerp(o.weight,v,M),o.planted=D&&v>.001,this.updateFootSample(o,a),this.applySimpleFootPitch(t,e,o),this.footIkDebug&&this.updateLegDebug(t,{footWorldPos:a,centerRayStart:d,toeRayStart:p,heelRayStart:u,sideRayStart:f,centerHit:g,toeHit:m,heelHit:y,sideHit:S,desiredNormal:o.normal,desiredTargetPos:r.footWorldPos,weight:o.weight})}updateLegIkTarget(t,e,o,i,s,r,n){const a=this.footIkScratch,l=this.footIkHits,h=e.foot,c=a.footWorldPos.setFromMatrixPosition(h.matrixWorld);if(this.footIkUsingContacts&&!s.contactRequested)return s.anchor.reset(),s.planted=!1,s.weight*=Math.exp(-this.footIkWeightOutSpeed*n),s.pitch*=Math.exp(-this.footIkRotationLerpSpeed*n),s.weight<=.001?s.targetPosition.copy(c):s.targetPosition.lerp(c,1-Math.exp(-this.footIkPositionLerpSpeed*n)),this.clampDesiredFootTarget(e,o,c,s.targetPosition),this.updateFootSample(s,c),this.setBoneWorldPosition(i,s.targetPosition),void(null!=this.ikDebugLegs&&(this.ikDebugLegs[t].root.visible=!1));const d=this.computeFootVerticalSpeed(s,c,n),p=this.resolveFootAxes(t,h,this.footIkAxesScratch),u=this.resolveGroundSampleAxes(t,h,p,this.footIkSampleAxesScratch),f=this.footIkUsingContacts&&s.anchor.locked&&s.contactRequested?s.anchor.position:c,g=a.centerRayStart.copy(f).addScaledVector(O,this.footIkRayStartHeight),m=a.toeRayStart.copy(g).addScaledVector(u.forward,this.footIkForwardSampleDistance),y=a.heelRayStart.copy(g).addScaledVector(u.forward,-this.footIkForwardSampleDistance),S=a.sideRayStart.copy(g).addScaledVector(u.right,this.footIkSideSampleDistance),I=this.getFirstGroundHit(g,l.center),P=this.getFirstGroundHit(m,l.toe),w=this.getFirstGroundHit(y,l.heel),v=this.getFirstGroundHit(S,l.side),b=a.desiredTargetPos.copy(c),x=a.desiredNormal.set(0,1,0),L=a.desiredForward.copy(u.forward);let B=0,R=0,F=this.footIkWeightOutSpeed;const A=null!=I||null!=P||null!=w;let D=c.y;null!=I?D=I.point.y:null!=P&&null!=w?D=.5*(P.point.y+w.point.y):null!=P?D=P.point.y:null!=w&&(D=w.point.y);const C=this.computeLegExtensionRatio(e,o),M=Math.max(0,c.y-D,c.y-s.targetPosition.y),T=this.shouldPlantFoot(s,!this.footIkRequireGrounded||r,A,d,C,M);if(T){if(b.copy(c),this.computeDesiredGroundNormal(u,I,P,w,v,x)){this.computeDesiredFootForward(u.forward,u.right,x,P,w,L),B=this.computeDesiredPitchAngle(P,w,u.forward);const t=Math.max(0,Math.sin(B))*this.footIkForwardSampleDistance*.45,e=Math.max(this.footIkFootOffset+this.footIkExtraClearance,s.contactOffset);b.y=D+e+t,s.top&&!this.footIkUsingContacts&&(b.y-=this.ikPelvisOffsetY/2),R=1,F=this.footIkWeightInSpeed}}else this.computeDesiredFootForward(u.forward,u.right,x,null,null,L),b.copy(c),this.footIkUsingContacts||(b.y+=this.footIkFootOffset);if(this.footIkUsingContacts){const t=z.setFromMatrixPosition(e.upper.matrixWorld),i=this.footIkPelvisEnabled?Math.min(this.footIkMaxPlantPelvisDrop,this.footIkPelvisMaxOffsetDown):0;s.anchor.locked&&Math.abs(b.y-s.anchor.position.y)>.08&&s.anchor.release();s.anchor.update(s.contactRequested,(!this.footIkRequireGrounded||r)&&null!=I&&R>0,c,b,u.forward,t,.995*o.total,i,Math.min(this.footIkMaxPlantDrift,this.footIkMaxHorizontalOffset),k.degToRad(this.footIkMaxPlantTurnAngleDeg))?b.copy(s.anchor.position):(b.copy(c),R=0,F=this.footIkWeightOutSpeed)}const W=1-Math.exp(-this.footIkPositionLerpSpeed*n),q=1-Math.exp(-F*n);this.footIkUsingContacts&&s.anchor.locked?s.targetPosition.copy(b):s.targetPosition.lerp(b,W),this.clampDesiredFootTarget(e,o,c,s.targetPosition),s.normal.lerp(x,W).normalize(),s.forward.lerp(L,W).normalize(),s.pitch=k.lerp(s.pitch,B,W),s.weight=k.lerp(s.weight,R,q),s.planted=T&&R>.001,this.updateFootSample(s,c),this.setBoneWorldPosition(i,s.targetPosition),this.footIkDebug&&this.updateLegDebug(t,{footWorldPos:c,centerRayStart:g,toeRayStart:m,heelRayStart:y,sideRayStart:S,centerHit:I,toeHit:P,heelHit:w,sideHit:v,desiredNormal:x,desiredTargetPos:s.targetPosition,weight:s.weight})}updateLegDebug(t,e){if(!this.footIkDebug||null==this.ikDebugLegs||null==this.ikBoneRefs)return;const o=this.ikDebugLegs[t],i=this.ikBoneRefs[t],s=this.resolveFootAxes(t,i.foot,this.footIkAxesScratch),r=this.footIkScratch,n=e.centerHit?.point??r.debugRayEndA.copy(e.centerRayStart).addScaledVector(W,this.footIkRayLength),a=e.toeHit?.point??r.debugRayEndB.copy(e.toeRayStart).addScaledVector(W,this.footIkRayLength),l=e.heelHit?.point??r.debugRayEndC.copy(e.heelRayStart).addScaledVector(W,this.footIkRayLength),h=e.sideHit?.point??r.debugRayEndD.copy(e.sideRayStart).addScaledVector(W,this.footIkRayLength);this.setDebugArrow(o.rayCenter,e.centerRayStart,n),this.setDebugArrow(o.rayToe,e.toeRayStart,a),this.setDebugArrow(o.rayHeel,e.heelRayStart,l),this.setDebugArrow(o.raySide,e.sideRayStart,h);const c=e.centerHit?.point??e.toeHit?.point??e.heelHit?.point??e.sideHit?.point??e.footWorldPos;this.setDebugArrow(o.hitNormal,c,r.debugRayEndE.copy(c).addScaledVector(e.desiredNormal,this.footIkDebugNormalLength)),this.setDebugArrow(o.footUp,e.footWorldPos,r.debugRayEndF.copy(e.footWorldPos).addScaledVector(s.up,this.footIkDebugAxisLength*(.15+e.weight))),this.setDebugArrow(o.footToTarget,e.footWorldPos,e.desiredTargetPos)}getFirstGroundHit(t,e){return"physics"===this.footIkRaycastMode?this.rayTestDown(t,this.footIkRayLength,e)?e:null:"physicsThenRender"===this.footIkRaycastMode?this.rayTestDown(t,this.footIkRayLength,e)||this.raycastDownFirstValid(t,this.footIkRayLength,e)?e:null:this.raycastDownFirstValid(t,this.footIkRayLength,e)?e:null}resolveGroundSampleAxes(t,e,o,i){const s=o??this.resolveFootAxes(t,e),r=i??{forward:new p,right:new p},n=this.getCurrentWorldDirection(this.actor.object,N);n.addScaledVector(O,-n.dot(O)),n.lengthSq()<1e-8&&n.copy(s.forward).addScaledVector(O,-s.forward.dot(O)),n.lengthSq()<1e-8&&n.set(0,0,1),n.normalize();const a=z.crossVectors(O,n);return a.lengthSq()<1e-8?a.copy(s.right):a.normalize(),a.dot(s.right)<0&&a.multiplyScalar(-1),r.forward.copy(n),r.right.copy(a),r}computeDesiredGroundNormal(t,e,o,i,s,r){const n=this.computeAverageHitNormal(e,o,i,s,N);let a=!1,l=0,h=0;if(null!=o&&null!=i){const e=E.subVectors(o.point,i.point).dot(t.forward);Math.abs(e)>1e-5&&(l=(o.point.y-i.point.y)/e,a=!0)}if(null!=s&&null!=e){const o=H.subVectors(s.point,e.point).dot(t.right);Math.abs(o)>1e-5&&(h=(s.point.y-e.point.y)/o,a=!0)}if(a&&(r.copy(O),r.addScaledVector(t.forward,-l),r.addScaledVector(t.right,-h),r.lengthSq()>1e-8?r.normalize():a=!1),a)null!=n&&r.dot(n)<0&&r.multiplyScalar(-1);else{if(null==n)return!1;r.copy(n),a=!0}return r.dot(O)<0&&r.multiplyScalar(-1),this.clampSlopeNormal(r),!0}computeAverageHitNormal(t,e,o,i,s){s.set(0,0,0);let r=0;return null!=t&&t.hasNormal&&(s.add(t.normal),r++),null!=e&&e.hasNormal&&(s.add(e.normal),r++),null!=o&&o.hasNormal&&(s.add(o.normal),r++),null!=i&&i.hasNormal&&(s.add(i.normal),r++),0===r||s.lengthSq()<1e-8?null:(s.multiplyScalar(1/r).normalize(),s)}computeDesiredFootForward(t,e,o,i,s,r){null!=i&&null!=s?r.subVectors(i.point,s.point):r.copy(t),r.addScaledVector(o,-r.dot(o)),r.lengthSq()<1e-8&&(r.copy(this.getCurrentWorldDirection(this.actor.object,N)),r.addScaledVector(o,-r.dot(o))),r.lengthSq()<1e-8&&r.crossVectors(o,e),r.lengthSq()<1e-8&&r.set(0,0,1),r.normalize();const n=N.copy(this.getCurrentWorldDirection(this.actor.object,N));n.addScaledVector(o,-n.dot(o)),n.lengthSq()<1e-8&&(n.copy(t),n.addScaledVector(o,-n.dot(o))),n.lengthSq()>1e-8&&n.normalize(),r.dot(n)<0&&r.multiplyScalar(-1)}computeDesiredPitchAngle(t,e,o){if(null==t||null==e)return 0;const i=E.subVectors(t.point,e.point),s=Math.abs(i.dot(o));if(s<1e-5)return 0;const r=t.point.y-e.point.y,n=k.degToRad(this.footIkMaxSlopeAngleDeg);return k.clamp(-Math.atan2(r,s),-n,n)}applySimpleFootPitch(t,e,o){if(null==e.parent||o.weight<=.001)return;const i=this.ikFootAxes?.[t]??this.detectFootAxisBasis(e),s=o.pitch*o.weight;if(Math.abs(s)<=1e-5)return;const r=this.resolveLocalPitchSign(i),n=j.copy(e.quaternion),a=Q.setFromAxisAngle(i.rightLocal,s*r);e.quaternion.copy(n.multiply(a))}resolveLocalPitchSign(t){const e=G.setFromAxisAngle(t.rightLocal,.15);return H.copy(t.upLocal).applyQuaternion(e).sub(t.upLocal).dot(t.forwardLocal)>=0?1:-1}clampDesiredFootTarget(t,e,o,i){const s=E.subVectors(i,o),r=s.dot(O),n=H.copy(s).addScaledVector(O,-r),a=n.length();a>this.footIkMaxHorizontalOffset&&a>1e-6&&n.multiplyScalar(this.footIkMaxHorizontalOffset/a);const l=k.clamp(r,-this.footIkMaxVerticalOffsetDown,this.footIkMaxVerticalOffsetUp);if(i.copy(o),i.add(n),i.addScaledVector(O,l),this.footIkPelvisEnabled)return;const h=t.upper.getWorldPosition(V).clone(),c=E.subVectors(i,h),d=c.length(),p=.995*e.total;d>p&&d>1e-6&&i.copy(h).addScaledVector(c,p/d)}solveTwoBoneLeg(t,e,o){const i=this.ikSolveScratch,s=i.hip.setFromMatrixPosition(e.upper.matrixWorld),r=i.knee.setFromMatrixPosition(e.lower.matrixWorld),n=i.foot.setFromMatrixPosition(e.foot.matrixWorld),a=s.distanceTo(r),l=r.distanceTo(n);if(a<1e-5||l<1e-5)return;const h=i.direction.subVectors(o,s),c=h.length();if(c<1e-5)return;const d=Math.abs(a-l)+1e-5,p=a+l-1e-5,u=k.clamp(c,d,p),f=h.multiplyScalar(1/c),g=i.animatedDirection.subVectors(n,s).normalize(),m=i.pole.subVectors(r,s);m.addScaledVector(g,-m.dot(g)),m.lengthSq()<a*a*1e-4&&(this.getCurrentWorldDirection(this.actor.object,m),m.addScaledVector(g,-m.dot(g)),m.lengthSq()<1e-8&&(Math.abs(g.x)<.9?m.set(1,0,0):m.set(0,1,0),m.addScaledVector(g,-m.dot(g)))),m.normalize(),i.rotation.setFromUnitVectors(g,f),m.applyQuaternion(i.rotation);const y=(u*u+a*a-l*l)/(2*u),S=Math.max(a*a-y*y,0),I=Math.sqrt(S),P=i.kneeTarget.copy(s).addScaledVector(f,y).addScaledVector(m,I),w=i.target.copy(s).addScaledVector(f,u);this.rotateBoneToward(e.upper,s,r,P),e.upper.updateMatrixWorld(!0);const v=r.setFromMatrixPosition(e.lower.matrixWorld),b=n.setFromMatrixPosition(e.foot.matrixWorld);this.rotateBoneToward(e.lower,v,b,w),e.lower.updateMatrixWorld(!0),this.updateSolveDebug(t,s,P,w)}updateSolveDebug(t,e,o,i){if(!this.footIkDebug||null==this.ikDebugLegs)return;const s=this.ikDebugLegs[t];this.setDebugArrow(s.solveUpper,e,o),this.setDebugArrow(s.solveLower,o,i)}rotateBoneToward(t,e,o,i){const s=this.ikSolveScratch.currentDirection.subVectors(o,e),r=this.ikSolveScratch.desiredDirection.subVectors(i,e);if(s.lengthSq()<1e-10||r.lengthSq()<1e-10)return;if(s.normalize(),r.normalize(),s.dot(r)>1-1e-10)return;const n=this.ikSolveScratch.rotation.setFromUnitVectors(s,r),a=this.getCurrentWorldQuaternion(t,j),l=Q.copy(n).multiply(a);if(null==t.parent)return void t.quaternion.copy(l);const h=this.getCurrentWorldQuaternion(t.parent,G),c=_.copy(h).invert().multiply(l);t.quaternion.copy(c)}detectFootAxisBasis(t){const e=this.getCurrentWorldQuaternion(t,j),o=this.getCurrentWorldDirection(this.actor.object,V);o.lengthSq()<1e-6&&o.set(0,0,1),o.normalize();const i=[{local:Y.clone(),world:Y.clone().applyQuaternion(e)},{local:J.clone(),world:J.clone().applyQuaternion(e)},{local:X.clone(),world:X.clone().applyQuaternion(e)}];i.push({local:Y.clone().multiplyScalar(-1),world:Y.clone().multiplyScalar(-1).applyQuaternion(e)}),i.push({local:J.clone().multiplyScalar(-1),world:J.clone().multiplyScalar(-1).applyQuaternion(e)}),i.push({local:X.clone().multiplyScalar(-1),world:X.clone().multiplyScalar(-1).applyQuaternion(e)});let s=i[0],r=-1/0;for(const t of i){const e=t.world.dot(O);e>r&&(r=e,s=t)}let n=i[0],a=-1/0;for(const t of i){if(Math.abs(t.world.dot(s.world))>.6)continue;const e=Math.abs(t.world.dot(o));e>a&&(a=e,n=t)}n.world.dot(o)<0&&(n={local:n.local.clone().multiplyScalar(-1),world:n.world.clone().multiplyScalar(-1)});const l=s.local.clone().normalize(),h=n.local.clone().normalize(),c=l.clone().cross(h).normalize();return{upLocal:l,forwardLocal:c.clone().cross(l).normalize(),rightLocal:c}}resolveFootAxes(t,e,o){const i=this.ikFootAxes?.[t]??this.detectFootAxisBasis(e),s=o??{up:new p,forward:new p,right:new p},r=this.getCurrentWorldQuaternion(e,j);return s.up.copy(i.upLocal).applyQuaternion(r).normalize(),s.forward.copy(i.forwardLocal).applyQuaternion(r).normalize(),s.right.copy(i.rightLocal).applyQuaternion(r).normalize(),s}getCurrentWorldQuaternion(t,e){return t.matrixWorld.decompose(z,e,N),e}getCurrentWorldDirection(t,e){const o=t.matrixWorld.elements;return e.set(o[8],o[9],o[10]).normalize()}clampSlopeNormal(t){const e=k.clamp(t.dot(O),-1,1),o=Math.acos(e),i=k.degToRad(this.footIkMaxSlopeAngleDeg);if(o<=i||o<1e-5)return;const s=i/o;t.lerpVectors(O,t,s).normalize()}blendLegAfterSolve(t,e,o){if(!(o>=.999)){if(o<=.001)return t.upper.quaternion.copy(e.upper),t.lower.quaternion.copy(e.lower),void t.foot.quaternion.copy(e.foot);this.blendBoneQuaternion(t.upper,e.upper,o),this.blendBoneQuaternion(t.lower,e.lower,o),this.blendBoneQuaternion(t.foot,e.foot,o)}}blendBoneQuaternion(t,e,o){j.copy(t.quaternion),t.quaternion.copy(e).slerp(j,o)}alignFootToGroundNormal(t,e,o,i){if(o.weight<=.001||null==e.parent)return;const s=this.ikFootAxes?.[t]??this.detectFootAxisBasis(e),r=V.copy(o.normal).normalize(),n=E.copy(o.forward);if(n.addScaledVector(r,-n.dot(r)),n.lengthSq()<1e-8){const o=this.resolveFootAxes(t,e);n.copy(o.forward).addScaledVector(r,-o.forward.dot(r))}if(n.lengthSq()<1e-8)return;n.normalize();const a=H.crossVectors(r,n);if(a.lengthSq()<1e-8)return;a.normalize(),n.copy(N.crossVectors(a,r)).normalize(),$.makeBasis(s.rightLocal,s.upLocal,s.forwardLocal),j.setFromRotationMatrix($),K.makeBasis(a,r,n),Q.setFromRotationMatrix(K);const l=G.copy(Q).multiply(_.copy(j).invert()),h=this.getCurrentWorldQuaternion(e.parent,_),c=Q.copy(h).invert().multiply(l),d=(1-Math.exp(-this.footIkRotationLerpSpeed*i))*o.weight;e.quaternion.slerp(c,k.clamp(d,0,1))}getMixer(){return this.mixer}evaluateExternalPose(t=0){null!=this.mixer&&(this.restoreFootIkPose(),this.runPoseProcessors("beforeMixer",t),this.mixer.update(t),this.runPoseProcessors("afterMixer",t),this.captureFootIkAnimationPose(),this.runPoseProcessors("beforeIk",t),this.updateFootIk(t),this.runPoseProcessors("afterIk",t))}beginExternalControl(){this.externalControlActive=!0}endExternalControl(){this.externalControlActive=!1}stopSequenceAnimation(){this.currentFullBodyPriority=-1,this.externalControlActive=!1,this.sequenceAnimationControlled=!1,this.sequenceAnimationReleasePending=!1,this.sequenceAnimationReleaseFrames=0,this.upperBodyOverride=!1,this.overrideFadeTimeUpper=null,this.fullBodyTimer=0}requestSequenceAnimationRelease(){this.externalControlActive=!1,this.sequenceAnimationControlled=!1,this.currentFullBodyPriority=-1,this.sequenceAnimationReleasePending=!0,this.sequenceAnimationReleaseFrames=1,this.upperBodyOverride=!1,this.overrideFadeTimeUpper=null}cancelRootMotionAction(t){null!=t&&this.fullBodyAction!==t||(this.currentFullBodyPriority=-1,this.externalControlActive=!1,this.fullBodyAction?.stop(),this.fullBodyAction=null,this.fullBodyTimer=0)}beginExternalAnimationControl(t,e={}){at(null!=this.mixer,"Can't begin external control before setup is called"),this.assertClipRigCompatible(t),this.mixer.stopAllAction(),this.currentFullBodyPriority=99,this.fullBodyTimer=0;const o=this.mixer.clipAction(t);return this.managedActions.indexOf(o)<0&&this.managedActions.push(o),o.setLoop(c,1),o.clampWhenFinished=!0,o.timeScale=e.timeScale??1,o.reset(),o.play(),this.fullBodyAction=o,this.externalControlActive=!0,o}syncMovementSpeed(t){null!=t&&(t.timeScale=this.getPlaybackTimeScale(t.getClip(),t.timeScale))}getPlaybackTimeScale(t,e){if(!(t instanceof s&&t.fixedInPlace&&null!=this.movementSpeed))return e;const o=this.mixer.getRoot(),i=o instanceof h?o.scale.x:1;return t.duration/t.displacement*this.movementSpeed/i}playStateMachine(t){this.stateMachines.push(t)}playUpperStateMachine(t){this.upperStateMachines.push(t)}removeStateMachine(t){const e=this.stateMachines.indexOf(t);e>=0&&this.stateMachines.splice(e,1)}removeUpperStateMachine(t){const e=this.upperStateMachines.indexOf(t);e>=0&&this.upperStateMachines.splice(e,1)}playUpper(t,e={}){this.assertClipRigCompatible(t);const o=e?.priority??1;o<this.currentUpperBodyPriority||(this.currentUpperBodyPriority=o,this.upperBodyAction=this.transition(this.upperBodyAction,this.getUpperBodyClip(t),e),this.upperBodyAction.timeScale=e?.timeScale??1,this.upperBodyTimer=0,this.upperBodyOverride=!0,this.overrideFadeTimeUpper=e.fadeTime)}play(t,e={}){at(null!=this.mixer,"Can't play animation before setup is called"),this.assertClipRigCompatible(t);const o=e.priority??1;if(o<this.currentFullBodyPriority)return;this.currentFullBodyPriority=o,this.fullBodyTimer=0,e.sequenceControlled&&(this.sequenceAnimationControlled=!0,this.sequenceAnimationReleasePending=!1,this.sequenceAnimationReleaseFrames=0);const i=this.getFullBodyClip(t);if(this.preparePoseTransition(t,i,e)){for(const t of this.managedActions)t.stop();this.managedActions.length=0,this.fullBodyAction=null,this.upperBodyAction=null}this.upperBodyOverride||(this.upperBodyAction=this.transition(this.upperBodyAction,this.getUpperBodyClip(t),e),this.upperBodyAction.timeScale=e?.timeScale??1),this.fullBodyClip=t,this.fullBodyAction=this.transition(this.fullBodyAction,i,e),this.fullBodyAction.timeScale=e?.timeScale??1,this.fullBodyAction.getClip().uuid==this.upperBodyAction.getClip().uuid&&this.upperBodyAction.syncWith(this.fullBodyAction),this.overrideFadeTime=e.fadeTime}assertClipRigCompatible(t){const e=L(t);if(e===this.animationRigId||null==e&&null==this.animationRigId)return;const o=`${t.uuid}|${e??""}|${this.animationRigId??""}`;if(!dt.has(o)){if(dt.add(o),null==e||null==this.animationRigId)return void console.warn(`Animation clip "${t.name}" or its character has no rig assigned. Playing without rig compatibility validation for legacy compatibility.`);console.warn(`Animation clip "${t.name}" targets rig ${e??"no rig"}, but this character uses rig ${this.animationRigId??"no rig"}. Generate and assign a retargeted clip for the character rig.`)}}onActionDone(t){return new Promise(e=>{const o=i=>{i.action===t&&(e(i.action),this.mixer.removeEventListener("finished",o))};this.mixer.addEventListener("finished",o)})}transition(t,e,o={}){const s=o?.fadeTime??this.fadeTime;if(null!=t&&t.getClip().uuid===e.uuid)return t.isRunning()||!1!==o.loop||!t.clampWhenFinished||o.sequenceControlled?(t.isRunning()||(t.reset(),null!=o.offset&&(t.time=o.offset),t.enabled=!0,t.weight=1,t.setEffectiveWeight(1),t.setEffectiveTimeScale(1),t.play(),!1===o.loop&&(t.setLoop(c,1),t.clampWhenFinished=!0)),t):t;if(i(t)){const i=t,r=this.mixer.clipAction(e);r.reset(),null!=o.offset&&(r.time=o.offset),r.play(),r.enabled=!0,r.setEffectiveTimeScale(1),r.weight=1,i.crossFadeTo(r,s,!1),t=r}else(t=this.mixer.clipAction(e)).reset(),null!=o.offset&&(t.time=o.offset),t.enabled=!0,t.weight=1,t.setEffectiveWeight(1),t.setEffectiveTimeScale(1),t.play();return!1===o.loop&&(t.setLoop(c,1),t.clampWhenFinished=!0),this.managedActions.indexOf(t)<0&&this.managedActions.push(t),t}};st=t([o({inEditor:!0})],st);export{st as CharacterAnimationComponent};function rt(t,e){if(null==t)return e;const o=e.clone();A(o,L(e)),T(o,C(e));const i=new Set(t.map(t=>t.name));return o.tracks=o.tracks.filter(t=>i.has(t.name.split(".")[0])),o}function nt(t){return t.flatMap(t=>function(t){const e=[];return t.traverse(t=>{e.push(t)}),e}(t)).filter(t=>t instanceof l)}function at(t,e){if(!1===t||"function"==typeof t&&!1===t())throw new Error(e)}function lt(t,e){const o=new Map;return(i,...s)=>{const r=t(i);return o.has(r)||o.set(r,e(i,...s)),o.get(r)}}function ht(t){let e;return t.traverse(t=>{(t instanceof l||t.isBone)&&null==e&&(e=t)}),e}function ct(t,e){let o=e;for(;null!=o;){if(o===t)return!0;o=o.parent}return!1}const dt=new Set;/*
1
+ import{__decorate as t}from"tslib";import{ActorComponent as e,Component as o}from"../../../component.js";import{canCrossFadeFromAnimationAction as i}from"../../../../animation/animation-action.js";import{RootMotionClip as s}from"../../../../animation/root-motion.js";import{inject as r}from"../../../../inject.js";import{ViewController as n}from"../../../../services/render.js";import{AnimationMixer as a,Bone as l,Object3D as h,LoopOnce as c,Raycaster as d,Vector3 as p,SkinnedMesh as u,Skeleton as f,Quaternion as g,Matrix4 as m,MathUtils as k,ArrowHelper as y,Group as S}from"three";import{getCharacterMovementLike as I}from"./character-movement-like.js";import{CCDIKSolver as P}from"three/addons/animation/CCDIKSolver.js";import{World as w}from"../../../../services/world.js";import{PhysicsSystem as v,RayTestResult as b}from"../../../../services/physics/physics-system.js";import{ArrayMap as x}from"../../../../../utils/collections.js";import{getAnimationClipRigId as L,getAnimationRigChainBoneNames as B,getAnimationRigDataFromObject as R,getAnimationRigIdFromObject as F,setAnimationClipRigId as A}from"../../../../animation/retarget.js";import{FootContactAnchor as D,getAnimationFootContacts as C,isFootInContact as M,setAnimationFootContacts as T}from"../../../../animation/foot-contact.js";const W=new p(0,-1,0),O=new p(0,1,0),q=new p(1,1,1),U=new g,V=new p,E=new p,H=new p,N=new p,z=new p,j=new g,Q=new g,G=new g,_=new g,$=new m,K=new m,Y=new p(1,0,0),J=new p(0,1,0),X=new p(0,0,1);function Z(){return{point:new p,normal:new p,hasNormal:!1}}const tt=["mixamorigHips","Hips","hips","Pelvis","pelvis"],et={upper:["LeftUpLeg","leftUpLeg","left_up_leg"],lower:["LeftLeg","leftLeg","left_leg"],foot:["LeftFoot","leftFoot","left_foot"]},ot={upper:["RightUpLeg","rightUpLeg","right_up_leg"],lower:["RightLeg","rightLeg","right_leg"],foot:["RightFoot","rightFoot","right_foot"]},it=["left","right"];let st=class extends e{constructor(){super(...arguments),this.viewController=r(n),this.poseProcessors=new x,this.transitionProcessors=[],this.managedActions=[],this.stateMachines=[],this.upperStateMachines=[],this.fadeTime=.2,this.movementSpeed=null,this.upperBodyTimer=0,this.upperBodyOverride=!1,this.fullBodyTimer=0,this.currentFullBodyPriority=-1,this.currentUpperBodyPriority=-1,this.sequenceAnimationControlled=!1,this.sequenceAnimationReleasePending=!1,this.sequenceAnimationReleaseFrames=0,this.externalControlActive=!1,this.world=r(w),this.physicsSystem=r(v),this._footIkEnabled=!1,this.footIkMoving=!1,this.footIkLocomotionEnabled=!1,this.footIkLocomotionActive=!0,this.footIkMaxPlantTurnAngleDeg=35,this.footIkMaxPlantDrift=.18,this.footIkMaxPlantPelvisDrop=.12,this.footIkRayStartHeight=.5,this.footIkRayLength=1.5,this.footIkRaycastMode="physics",this.footIkFootOffset=.08,this.footIkAutoContactOffset=!1,this.footIkPositionLerpSpeed=28,this.footIkRotationLerpSpeed=14,this.footIkWeightInSpeed=34,this.footIkWeightOutSpeed=30,this.footIkMaxSlopeAngleDeg=40,this.footIkForwardSampleDistance=.14,this.footIkSideSampleDistance=.08,this.footIkMaxHorizontalOffset=.22,this.footIkMaxVerticalOffsetDown=.75,this.footIkMaxVerticalOffsetUp=.2,this.footIkPelvisEnabled=!0,this.footIkPelvisBone="mixamorigHips",this.footIkPelvisLerpSpeed=24,this.footIkPelvisMaxOffsetDown=.4,this.footIkPelvisMaxOffsetUp=.08,this.footIkRequireGrounded=!0,this.footIkSimpleTiltOnly=!1,this.footIkDebug=!1,this.footIkDebugNormalLength=.25,this.footIkDebugAxisLength=.2,this.footIkExtraClearance=.01,this.footIkPlantReleaseUpSpeed=.08,this.footIkPlantReleaseExtension=.9,this.footIkPlantAttachExtension=.96,this.footIkPlantReleaseLift=.055,this.footIkPlantAttachLift=.02,this.leftLegIkBones={upperLeg:"mixamorigLeftUpLeg",lowerLeg:"mixamorigLeftLeg",foot:"mixamorigLeftFoot"},this.rightLegIkBones={upperLeg:"mixamorigRightUpLeg",lowerLeg:"mixamorigRightLeg",foot:"mixamorigRightFoot"},this.ikPelvisOffsetY=0,this.ikPelvisDescendantStack=[],this.ikWorldUpdateNodes=[],this.footIkStateReset=!0,this.footIkUsingContacts=!1,this.footIkLastRootPosition=new p,this.footIkHasRootSample=!1,this.ikPreSolvePelvisPosition=new p,this.footIkPoseApplied=!1,this.footIkAnimationPoseCaptured=!1,this.footIkContactTransitionTime=0,this.footIkTransitionLeftContact=!1,this.footIkTransitionRightContact=!1,this.ikLegState={left:{anchor:new D,contactRequested:!1,targetPosition:new p,normal:new p(0,1,0),forward:new p(0,0,1),pitch:0,contactOffset:0,planted:!1,lastFootWorldPos:new p,hasLastFootSample:!1,weight:0,top:!1},right:{anchor:new D,contactRequested:!1,targetPosition:new p,normal:new p(0,1,0),forward:new p(0,0,1),pitch:0,contactOffset:0,planted:!1,lastFootWorldPos:new p,hasLastFootSample:!1,weight:0,top:!1}},this.ikWarnedMissingBones=!1,this.raycaster=new d,this.raycastIntersections=[],this.footIkRayTo=new p,this.footIkRayTestResult=new b,this.footIkRayTestOptions={debug:!1,excludeTriggers:!0,resolveActor:!1,collisionFilter:-2},this.footIkHits={center:Z(),toe:Z(),heel:Z(),side:Z()},this.footIkAxesScratch={up:new p,forward:new p,right:new p},this.footIkSampleAxesScratch={forward:new p,right:new p},this.footIkScratch={footWorldPos:new p,centerRayStart:new p,toeRayStart:new p,heelRayStart:new p,sideRayStart:new p,desiredTargetPos:new p,desiredNormal:new p,desiredForward:new p,debugRayEndA:new p,debugRayEndB:new p,debugRayEndC:new p,debugRayEndD:new p,debugRayEndE:new p,debugRayEndF:new p},this.ikPreSolve={left:{upper:new g,lower:new g,foot:new g,footWorld:new g},right:{upper:new g,lower:new g,foot:new g,footWorld:new g}},this.ikSolveScratch={hip:new p,knee:new p,foot:new p,direction:new p,animatedDirection:new p,pole:new p,kneeTarget:new p,target:new p,currentDirection:new p,desiredDirection:new p,rotation:new g},this.getFullBodyClip=lt(t=>t.uuid,t=>rt(this.fullBodyMask,t)),this.getUpperBodyClip=lt(t=>t.uuid,t=>rt(this.upperBodyMask,t))}get isSequenceControlled(){return this.externalControlActive||this.sequenceAnimationControlled||this.sequenceAnimationReleasePending}get hasUpperBodyOverride(){return this.upperBodyOverride}get footIkEnabled(){return this._footIkEnabled}set footIkEnabled(t){this._footIkEnabled!==t&&(this.restoreFootIkPose(),this._footIkEnabled=t,null!=this.ikRoot&&(this.disposeFootIkDebug(),this.initializeFootIk(this.ikRoot,this.ikRootBone),this.syncFootIkDebug()))}onInit(){this.viewController.onUpdate(this.actor).subscribe(t=>this.updateInternal(t)),this.characterMovement=I(this.actor),this.footIkRayTestOptions.excludeActor=this.actor}onEndPlay(){this.restoreFootIkPose(),this.resetFootIkContacts(),this.disposeFootIkDebug()}raycastDownFirstValid(t,e,o){const i=this.world.scene;this.raycaster.set(t,W),this.raycaster.near=0,this.raycaster.far=e,this.raycastIntersections.length=0,this.raycaster.intersectObject(i,!0,this.raycastIntersections);for(const t of this.raycastIntersections)if(!(this.isFootIkDebugObject(t.object)||this.isRelatedToActorHierarchy(t.object)||this.isSkinnedMeshObject(t.object)))return o.point.copy(t.point),null!=t.face?(o.normal.copy(t.face.normal).transformDirection(t.object.matrixWorld).normalize(),o.hasNormal=!0):o.hasNormal=!1,this.raycastIntersections.length=0,!0;return this.raycastIntersections.length=0,!1}rayTestDown(t,e,o){const i=this.footIkRayTo.copy(t).addScaledVector(W,e),s=this.physicsSystem.rayTest(t,i,this.footIkRayTestResult,this.footIkRayTestOptions);return!!s.hasHit&&(o.point.copy(s.hitPoint),o.normal.copy(s.hitNormal),o.normal.lengthSq()>1e-8?(o.normal.normalize(),o.hasNormal=!0):o.hasNormal=!1,!0)}isRelatedToActorHierarchy(t){const e=this.actor?.object;return null!=e&&null!=t&&(ct(t,e)||ct(e,t))}isFootIkDebugObject(t){let e=t;for(;null!=e;){if(!0===e.userData?.footIkDebug)return!0;e=e.parent}return!1}isSkinnedMeshObject(t){let e=t;for(;null!=e;){if(e instanceof u||!0===e.isSkinnedMesh)return!0;e=e.parent}return!1}getRootMotionAction(){if(this.fullBodyAction.getClip()instanceof s)return this.fullBodyAction}getFullBodyAction(){return this.fullBodyAction}getFullBodySourceClip(){const t=this.fullBodyClip;return null!=t&&this.fullBodyAction?.getClip()===this.getFullBodyClip(t)?t:void 0}setup(t,e,o){this.restoreFootIkPose(),null!=e&&(this.upperBodyMask=nt(e),this.fullBodyMask=function(t,e){const o=new Set(nt(e).map(t=>t.uuid)),i=[];return t.traverse(t=>{(t instanceof l||t.isBone)&&!o.has(t.uuid)&&i.push(t)}),i}(ht(t),e)),null!=this.mixer&&this.mixer.stopAllAction(),this.mixer=new a(t),this.managedActions.length=0,this.animationRigId=F(t),this.animationRigData=R(t),this.ikRoot=t,this.ikRootBone=o,this.disposeFootIkDebug(),this.initializeFootIk(t,o),this.syncFootIkDebug()}updateStateMachines(t){this.stateMachines.forEach(e=>{e.step(t);const o=e.current.clip;null!=o&&this.play(o,{priority:e.current.options.priority??0,loop:e.current.options.loop??!0})}),this.upperStateMachines.forEach(e=>{e.step(t);const o=e.current.clip;null!=o?this.playUpper(o,{priority:e.current.options.priority??0,loop:e.current.options.loop??!0}):this.play(this.fullBodyClip)})}addPoseProcessor(t){const e={id:t.id,phase:t.phase??"afterMixer",order:t.order??0,update:t.update};this.removePoseProcessor(e.id);const o=this.poseProcessors.get(e.phase);let i=o.findIndex(t=>t.order>e.order);return-1===i&&(i=o.length),o.splice(i,0,e),()=>{const t=o.indexOf(e);-1!==t&&(o.splice(t,1),0===o.length&&this.poseProcessors.delete(e.phase))}}removePoseProcessor(t){for(const[e,o]of this.poseProcessors.entries()){const i=o.findIndex(e=>e.id===t);if(-1!==i)return o.splice(i,1),void(0===o.length&&this.poseProcessors.delete(e))}}addTransitionProcessor(t){const e=this.transitionProcessors.findIndex(e=>e.id===t.id);return e>=0&&this.transitionProcessors.splice(e,1),this.transitionProcessors.push(t),()=>{const e=this.transitionProcessors.indexOf(t);e>=0&&this.transitionProcessors.splice(e,1)}}preparePoseTransition(t,e,o){if(0===this.transitionProcessors.length)return!1;const i=this.fullBodyAction,s=this.getFullBodySourceClip();if(null==i||null==s||i.getClip().uuid===e.uuid||this.isSequenceControlled||o.sequenceControlled||this.upperBodyOverride)return!1;const r=this.transitionContext??(this.transitionContext={});r.animation=this,r.source=i,r.sourceClip=s,r.targetClip=t,r.targetPoseClip=e,r.targetTimeScale=this.getPlaybackTimeScale(e,o.timeScale??1),r.options=o;for(const t of this.transitionProcessors)if(t.prepare(r))return!0;return!1}runPoseProcessors(t,e){if(0===this.poseProcessors.size||null==this.mixer||!this.poseProcessors.present(t))return;const o=this.mixer.getRoot();if(!(o instanceof h))return;const i=this.poseProcessorContext??(this.poseProcessorContext={deltaTime:e,phase:t,animation:this,mixer:this.mixer,root:o});i.deltaTime=e,i.phase=t,i.mixer=this.mixer,i.root=o;const s=this.poseProcessors.get(t);for(let t=0;t<s.length;t++)s[t].update(i)}updateInternal(t){if(null==this.mixer)return;if(this.externalControlActive)return;this.restoreFootIkPose(),this.upperBodyTimer+=t*(this.upperBodyAction?.timeScale??1),this.fullBodyTimer+=t*(this.fullBodyAction?.timeScale??1);let e=!1;this.sequenceAnimationReleasePending&&(this.sequenceAnimationReleaseFrames>0?(e=!0,this.sequenceAnimationReleaseFrames--):(this.sequenceAnimationReleasePending=!1,this.currentFullBodyPriority=-1,this.sequenceAnimationControlled=!1,this.overrideFadeTime=null,this.fullBodyTimer=0)),this.upperBodyAction&&this.upperBodyOverride&&this.upperBodyAction.getClip().duration-2*(this.overrideFadeTimeUpper??this.fadeTime)<this.upperBodyTimer&&(this.upperBodyOverride=!1,null!=this.fullBodyClip&&this.transition(this.upperBodyAction,this.getUpperBodyClip(this.fullBodyClip)),this.upperBodyAction=null,this.overrideFadeTimeUpper=null),!this.sequenceAnimationControlled&&!this.sequenceAnimationReleasePending&&this.fullBodyAction&&this.fullBodyAction.loop===c&&this.fullBodyAction.getClip().duration-2*(this.overrideFadeTime??this.fadeTime)<this.fullBodyTimer&&(this.currentFullBodyPriority=-1,this.overrideFadeTime=null),null!=this.characterMovement&&(this.movementSpeed=this.characterMovement.horizontalSpeed),e||this.updateStateMachines(t),this.syncMovementSpeed(this.fullBodyAction),this.upperBodyOverride||this.syncMovementSpeed(this.upperBodyAction),this.runPoseProcessors("beforeMixer",t),this.mixer.update(t),this.runPoseProcessors("afterMixer",t),this.captureFootIkAnimationPose(),this.runPoseProcessors("beforeIk",t),this.updateFootIk(t),this.runPoseProcessors("afterIk",t)}initializeFootIk(t,e){if(this.resetFootIkContacts(),this.ikSolver=null,this.ikSkinnedMesh=null,this.ikBoneRefs=null,this.ikLegLengths=null,this.ikPelvisBone=null,this.ikPelvisOffsetY=0,this.ikFootAxes=null,this.ikTargetBones=null,this.ikWorldUpdateNodes.length=0,this.footIkStateReset=!0,this.ikWarnedMissingBones=!1,this.ikLegState.left.weight=0,this.ikLegState.right.weight=0,!this.footIkEnabled)return;const o=this.findFirstSkinnedMesh(t);if(null==o||null==o.skeleton)return;this.ikSkinnedMesh=o,t.updateWorldMatrix(!0,!0);const i=o.skeleton,s=B(this.animationRigData,"leftLeg"),r=B(this.animationRigData,"rightLeg"),n=this.resolveLegBoneRefs(i,this.leftLegIkBones,et,s),a=this.resolveLegBoneRefs(i,this.rightLegIkBones,ot,r);if(null==n||null==a)return void(this.ikWarnedMissingBones||(this.ikWarnedMissingBones=!0,console.warn(`[CharacterAnimationComponent] Foot IK disabled: missing leg bones. Left=${this.leftLegIkBones.upperLeg}/${this.leftLegIkBones.lowerLeg}/${this.leftLegIkBones.foot}, Right=${this.rightLegIkBones.upperLeg}/${this.rightLegIkBones.lowerLeg}/${this.rightLegIkBones.foot}`)));this.ikBoneRefs={left:n,right:a},this.ikLegLengths={left:this.computeLegLengths(n),right:this.computeLegLengths(a)},this.ikFootAxes={left:this.detectFootAxisBasis(n.foot),right:this.detectFootAxisBasis(a.foot)};const h=this.animationRigData?.bones.find(t=>t.id===this.animationRigData?.pelvisBoneId)?.name;this.ikPelvisBone=this.resolvePelvisBone(i,n,a,h),this.rebuildFootIkWorldUpdateNodes(),this.ikLegState.left.targetPosition.copy(n.foot.getWorldPosition(V)),this.ikLegState.right.targetPosition.copy(a.foot.getWorldPosition(V)),this.ikLegState.left.normal.set(0,1,0),this.ikLegState.right.normal.set(0,1,0),this.ikLegState.left.pitch=0,this.ikLegState.right.pitch=0,this.ikLegState.left.contactOffset=this.estimateFootContactOffset(i,"left",n.foot),this.ikLegState.right.contactOffset=this.estimateFootContactOffset(i,"right",a.foot),this.ikLegState.left.planted=!1,this.ikLegState.right.planted=!1,this.ikLegState.left.lastFootWorldPos.copy(n.foot.getWorldPosition(V)),this.ikLegState.right.lastFootWorldPos.copy(a.foot.getWorldPosition(V)),this.ikLegState.left.hasLastFootSample=!1,this.ikLegState.right.hasLastFootSample=!1;const c=this.resolveFootAxes("left",n.foot),d=this.resolveFootAxes("right",a.foot);this.ikLegState.left.forward.copy(c.forward),this.ikLegState.right.forward.copy(d.forward);const p=e??ht(t);if(null==p)return;let u=i.getBoneByName("_IKTargetLeftFoot"),g=i.getBoneByName("_IKTargetRightFoot"),k=!1;if(null==u&&(u=new l,u.name="_IKTargetLeftFoot",p.add(u),k=!0),null==g&&(g=new l,g.name="_IKTargetRightFoot",p.add(g),k=!0),this.setBoneWorldPosition(u,n.foot.getWorldPosition(V)),this.setBoneWorldPosition(g,a.foot.getWorldPosition(V)),k||i.bones.indexOf(u)<0||i.bones.indexOf(g)<0){const e=i.bones.slice(),o=i.boneInverses.map(t=>t.clone());e.indexOf(u)<0&&(e.push(u),o.push((new m).copy(u.matrixWorld).invert())),e.indexOf(g)<0&&(e.push(g),o.push((new m).copy(g.matrixWorld).invert()));const s=new f(e,o);t.traverse(t=>{const e=t;!0===e.isSkinnedMesh&&e.skeleton===i&&e.bind(s,e.bindMatrix)})}this.ikTargetBones={left:u,right:g},this.rebuildFootIkWorldUpdateNodes();const y=o.skeleton.bones,S={target:y.indexOf(u),effector:y.indexOf(n.foot),links:[{index:y.indexOf(n.lower)},{index:y.indexOf(n.upper)}],iteration:8},I={target:y.indexOf(g),effector:y.indexOf(a.foot),links:[{index:y.indexOf(a.lower)},{index:y.indexOf(a.upper)}],iteration:8};if(S.target<0||S.effector<0||S.links.some(t=>t.index<0)||I.target<0||I.effector<0||I.links.some(t=>t.index<0))return console.warn("[CharacterAnimationComponent] Foot IK disabled: failed to resolve IK indexes in skeleton."),void(this.ikSolver=null);this.ikSolver=new P(o,[S,I])}findFirstSkinnedMesh(t){let e;return t.traverse(t=>{null==e&&(t instanceof u||!0===t.isSkinnedMesh)&&(e=t)}),e}resolveLegBoneRefs(t,e,o,i=[]){const s=this.findBoneByNames(t,[i[0],e.upperLeg,...o.upper].filter(t=>null!=t)),r=this.findBoneByNames(t,[i.at(-2),e.lowerLeg,...o.lower].filter(t=>null!=t)),n=this.findBoneByNames(t,[i.at(-1),e.foot,...o.foot].filter(t=>null!=t));if(null!=s&&null!=r&&null!=n)return{upper:s,lower:r,foot:n}}findBoneByNames(t,e){for(const o of e){const e=t.getBoneByName(o);if(null!=e)return e}}resolvePelvisBone(t,e,o,i){const s=this.findBoneByNames(t,[i,this.footIkPelvisBone,...tt].filter(t=>null!=t));if(null!=s)return s;const r=e.upper.parent,n=o.upper.parent;return null!=r&&r===n&&(r instanceof l||!0===r.isBone)||null!=r&&(r instanceof l||!0===r.isBone)?r:void 0}computeLegLengths(t){const e=t.upper.getWorldPosition(V),o=t.lower.getWorldPosition(E),i=t.foot.getWorldPosition(H),s=e.distanceTo(o),r=o.distanceTo(i);return{upper:s,lower:r,total:s+r}}setBoneWorldPosition(t,e){const o=t.parent;null==o?t.position.copy(e):t.position.copy(V.copy(e).applyMatrix4($.copy(o.matrixWorld).invert())),t.quaternion.copy(U),t.scale.copy(q),t.updateMatrixWorld(!0)}syncFootIkDebug(){this.footIkDebug&&null!=this.ikRoot?(null!=this.ikDebugRoot&&null!=this.ikDebugLegs||(this.ikDebugRoot=new S,this.ikDebugRoot.name="FootIKDebug",this.ikDebugRoot.userData.footIkDebug=!0,this.ikDebugLegs={left:this.createDebugLeg("LeftFootIK",53503),right:this.createDebugLeg("RightFootIK",16755200)},this.ikDebugRoot.add(this.ikDebugLegs.left.root,this.ikDebugLegs.right.root),this.world.scene.add(this.ikDebugRoot)),this.ikDebugRoot.visible=!0):null!=this.ikDebugRoot&&(this.ikDebugRoot.visible=!1)}disposeFootIkDebug(){null!=this.ikDebugRoot&&(this.ikDebugRoot.removeFromParent(),this.ikDebugRoot.clear()),this.ikDebugRoot=null,this.ikDebugLegs=null}createDebugLeg(t,e){const o=new S;o.name=t,o.userData.footIkDebug=!0;const i=this.createDebugArrow(e),s=this.createDebugArrow(e),r=this.createDebugArrow(e),n=this.createDebugArrow(e),a=this.createDebugArrow(65280),l=this.createDebugArrow(6750054),h=this.createDebugArrow(16711935),c=this.createDebugArrow(16776960),d=this.createDebugArrow(16737792);return o.add(i,s,r,n,a,l,h,c,d),{root:o,rayCenter:i,rayToe:s,rayHeel:r,raySide:n,hitNormal:a,footUp:l,footToTarget:h,solveUpper:c,solveLower:d}}createDebugArrow(t){const e=new y(O,new p,.001,t);return e.userData.footIkDebug=!0,e.traverse(t=>{t.userData.footIkDebug=!0,t.raycast=()=>{}}),e.visible=!1,e}setDebugArrow(t,e,o){V.subVectors(o,e);const i=V.length();i<1e-6?t.visible=!1:(t.visible=!0,t.position.copy(e),t.setDirection(V.multiplyScalar(1/i)),t.setLength(i,Math.min(.08,.25*i),Math.min(.05,.2*i)))}resetDebugVisibility(){null!=this.ikDebugLegs&&(this.ikDebugLegs.left.rayCenter.visible=!1,this.ikDebugLegs.left.rayToe.visible=!1,this.ikDebugLegs.left.rayHeel.visible=!1,this.ikDebugLegs.left.raySide.visible=!1,this.ikDebugLegs.left.hitNormal.visible=!1,this.ikDebugLegs.left.footUp.visible=!1,this.ikDebugLegs.left.footToTarget.visible=!1,this.ikDebugLegs.left.solveUpper.visible=!1,this.ikDebugLegs.left.solveLower.visible=!1,this.ikDebugLegs.right.rayCenter.visible=!1,this.ikDebugLegs.right.rayToe.visible=!1,this.ikDebugLegs.right.rayHeel.visible=!1,this.ikDebugLegs.right.raySide.visible=!1,this.ikDebugLegs.right.hitNormal.visible=!1,this.ikDebugLegs.right.footUp.visible=!1,this.ikDebugLegs.right.footToTarget.visible=!1,this.ikDebugLegs.right.solveUpper.visible=!1,this.ikDebugLegs.right.solveLower.visible=!1)}updateFootIk(t){if(this.syncFootIkDebug(),!this.footIkEnabled||null==this.ikBoneRefs||null==this.ikRoot)return void this.resetDebugVisibility();if(this.footIkUsingContacts=!1,this.footIkLocomotionEnabled){const e=this.fullBodyAction,o=e?.getClip(),i=C(o);if(!this.footIkLocomotionActive||this.isSequenceControlled||null==i||null==e||e.getEffectiveWeight()<.65||!Number.isFinite(t)||t<0)return this.resetFootIkContacts(),void this.resetDebugVisibility();(this.footIkContactClip!==o||t>.2)&&(this.resetFootIkContacts(),this.footIkContactClip=o);const s=this.actor.object.getWorldPosition(V),r=(this.movementSpeed??0)*Math.max(0,t);this.footIkHasRootSample&&this.footIkLastRootPosition.distanceToSquared(s)>(1+r)**2&&(this.resetFootIkContacts(),this.footIkContactClip=o),this.footIkLastRootPosition.copy(s),this.footIkHasRootSample=!0,this.footIkUsingContacts=!0;const n=e.loop!==c;this.footIkContactTransitionTime=Math.max(0,this.footIkContactTransitionTime-t),this.ikLegState.left.contactRequested=this.footIkContactTransitionTime>0?this.footIkTransitionLeftContact:M(i.left,e.time,o.duration,n),this.ikLegState.right.contactRequested=this.footIkContactTransitionTime>0?this.footIkTransitionRightContact:M(i.right,e.time,o.duration,n)}if(!this.footIkUsingContacts&&!this.footIkMoving&&(this.movementSpeed??0)>.01)return this.footIkStateReset||(this.syncFootIkWorldMatrices(),this.resetFootIkState(),this.footIkStateReset=!0),void this.resetDebugVisibility();this.syncFootIkWorldMatrices(),this.footIkStateReset=!1;const e=this.characterMovement?.isGrounded??!0;if(this.footIkSimpleTiltOnly)return this.updatePelvisOffset(t,!1),this.updateLegTiltSimple("left",this.ikBoneRefs.left.foot,this.ikLegState.left,e,t),void this.updateLegTiltSimple("right",this.ikBoneRefs.right.foot,this.ikLegState.right,e,t);if(null==this.ikLegLengths||null==this.ikTargetBones||null==this.ikSkinnedMesh||null==this.ikSolver)return this.updatePelvisOffset(t,!1),void this.resetDebugVisibility();if(this.footIkUsingContacts&&null!=this.ikPelvisBone&&this.ikPreSolvePelvisPosition.copy(this.ikPelvisBone.position),this.updateLegIkTarget("left",this.ikBoneRefs.left,this.ikLegLengths.left,this.ikTargetBones.left,this.ikLegState.left,e,t),this.updateLegIkTarget("right",this.ikBoneRefs.right,this.ikLegLengths.right,this.ikTargetBones.right,this.ikLegState.right,e,t),this.updatePelvisOffset(t,!0),this.setBoneWorldPosition(this.ikTargetBones.left,this.ikLegState.left.targetPosition),this.setBoneWorldPosition(this.ikTargetBones.right,this.ikLegState.right.targetPosition),this.ikPreSolve.left.upper.copy(this.ikBoneRefs.left.upper.quaternion),this.ikPreSolve.left.lower.copy(this.ikBoneRefs.left.lower.quaternion),this.ikPreSolve.left.foot.copy(this.ikBoneRefs.left.foot.quaternion),this.footIkUsingContacts&&!this.footIkAnimationPoseCaptured&&this.getCurrentWorldQuaternion(this.ikBoneRefs.left.foot,this.ikPreSolve.left.footWorld),this.ikPreSolve.right.upper.copy(this.ikBoneRefs.right.upper.quaternion),this.ikPreSolve.right.lower.copy(this.ikBoneRefs.right.lower.quaternion),this.ikPreSolve.right.foot.copy(this.ikBoneRefs.right.foot.quaternion),this.footIkUsingContacts&&!this.footIkAnimationPoseCaptured&&this.getCurrentWorldQuaternion(this.ikBoneRefs.right.foot,this.ikPreSolve.right.footWorld),null!=this.ikDebugLegs&&(this.ikDebugLegs.left.solveUpper.visible=!1,this.ikDebugLegs.left.solveLower.visible=!1,this.ikDebugLegs.right.solveUpper.visible=!1,this.ikDebugLegs.right.solveLower.visible=!1),this.footIkUsingContacts?(this.ikLegState.left.weight>.001&&this.solveTwoBoneLeg("left",this.ikBoneRefs.left,this.ikLegState.left.targetPosition),this.ikLegState.right.weight>.001&&this.solveTwoBoneLeg("right",this.ikBoneRefs.right,this.ikLegState.right.targetPosition)):this.ikSolver.update(),this.blendLegAfterSolve(this.ikBoneRefs.left,this.ikPreSolve.left,this.ikLegState.left.weight),this.blendLegAfterSolve(this.ikBoneRefs.right,this.ikPreSolve.right,this.ikLegState.right.weight),this.footIkUsingContacts){this.syncFootIkWorldMatrices();for(const t of it){const e=this.ikBoneRefs[t].foot,o=this.getCurrentWorldQuaternion(e.parent,j);e.quaternion.copy(o.invert().multiply(this.ikPreSolve[t].footWorld))}this.footIkPoseApplied=!0}this.applySimpleFootPitch("left",this.ikBoneRefs.left.foot,this.ikLegState.left),this.applySimpleFootPitch("right",this.ikBoneRefs.right.foot,this.ikLegState.right)}resetFootIkState(){if(null!=this.ikBoneRefs){for(const t of it){const e=this.ikBoneRefs[t],o=this.ikLegState[t],i=V.setFromMatrixPosition(e.foot.matrixWorld);o.targetPosition.copy(i),o.lastFootWorldPos.copy(i),o.hasLastFootSample=!0,o.weight=0,o.normal.set(0,1,0);const s=this.resolveFootAxes(t,e.foot,this.footIkAxesScratch);o.forward.copy(s.forward),o.pitch=0,this.footIkAutoContactOffset?o.contactOffset=Math.max(o.contactOffset,this.footIkFootOffset+this.footIkExtraClearance):o.contactOffset=this.footIkFootOffset+this.footIkExtraClearance,o.planted=!1,o.anchor.reset()}this.ikPelvisOffsetY=0}}resetFootIkContacts(){for(const t of it){const e=this.ikLegState[t];e.anchor.reset(),e.contactRequested=!1,e.planted=!1,e.weight=0,e.hasLastFootSample=!1}this.footIkContactClip=void 0,this.footIkHasRootSample=!1,this.ikPelvisOffsetY=0,this.footIkContactTransitionTime=0}preserveFootIkContacts(t,e){this.footIkEnabled&&this.footIkLocomotionEnabled&&e>0&&(this.footIkContactClip=t,this.footIkTransitionLeftContact=this.ikLegState.left.anchor.locked,this.footIkTransitionRightContact=this.ikLegState.right.anchor.locked,this.footIkContactTransitionTime=e)}restoreFootIkPose(){if(this.footIkPoseApplied&&null!=this.ikBoneRefs){for(const t of it){const e=this.ikBoneRefs[t],o=this.ikPreSolve[t];e.upper.quaternion.copy(o.upper),e.lower.quaternion.copy(o.lower),e.foot.quaternion.copy(o.foot)}this.ikPelvisBone?.position.copy(this.ikPreSolvePelvisPosition),this.footIkPoseApplied=!1}}captureFootIkAnimationPose(){this.footIkAnimationPoseCaptured=!1,this.footIkEnabled&&this.footIkLocomotionEnabled&&this.footIkLocomotionActive&&!this.isSequenceControlled&&null!=this.ikBoneRefs&&null!=C(this.fullBodyAction?.getClip())&&(this.syncFootIkWorldMatrices(),this.getCurrentWorldQuaternion(this.ikBoneRefs.left.foot,this.ikPreSolve.left.footWorld),this.getCurrentWorldQuaternion(this.ikBoneRefs.right.foot,this.ikPreSolve.right.footWorld),this.footIkAnimationPoseCaptured=!0)}updatePelvisOffset(t,e){if(null==this.ikPelvisBone)return;let o=0;if(e&&this.footIkPelvisEnabled&&null!=this.ikBoneRefs&&null!=this.ikLegLengths){this.ikLegState.left.top=!1,this.ikLegState.right.top=!1;for(const t of it){const e=this.ikBoneRefs[t],i=this.ikLegLengths[t],s=this.ikLegState[t];if(!s.planted)continue;const r=V.setFromMatrixPosition(e.upper.matrixWorld),n=.998*i.total,a=this.computeRequiredPelvisDrop(r,s.targetPosition,n);if(a>0){const e="left"===t?"right":"left";this.ikLegState[e].top=!0}o=Math.min(o,-a)}const t=this.footIkUsingContacts?Math.min(this.footIkPelvisMaxOffsetDown,this.footIkMaxPlantPelvisDrop):this.footIkPelvisMaxOffsetDown;o=k.clamp(o,-t,this.footIkPelvisMaxOffsetUp)}const i=1-Math.exp(-this.footIkPelvisLerpSpeed*t);this.ikPelvisOffsetY=k.lerp(this.ikPelvisOffsetY,o,i);const s=E.setFromMatrixPosition(this.ikPelvisBone.matrixWorld),r=H.copy(s).addScaledVector(O,this.ikPelvisOffsetY);null==this.ikPelvisBone.parent?this.ikPelvisBone.position.copy(r):this.ikPelvisBone.position.copy(r.applyMatrix4($.copy(this.ikPelvisBone.parent.matrixWorld).invert())),this.updatePelvisWorldTranslation(s)}updatePelvisWorldTranslation(t){const e=this.ikPelvisBone;if(null==e)return;e.updateMatrix(),null==e.parent?e.matrixWorld.copy(e.matrix):e.matrixWorld.multiplyMatrices(e.parent.matrixWorld,e.matrix),e.matrixWorldNeedsUpdate=!1;const o=e.matrixWorld.elements,i=o[12]-t.x,s=o[13]-t.y,r=o[14]-t.z;if(0===i&&0===s&&0===r)return;const n=this.ikPelvisDescendantStack;n.length=0;for(let t=0;t<e.children.length;t++)n.push(e.children[t]);for(;n.length>0;){const t=n.pop(),e=t.matrixWorld.elements;e[12]+=i,e[13]+=s,e[14]+=r,t.matrixWorldNeedsUpdate=!1;for(let e=0;e<t.children.length;e++)n.push(t.children[e])}}rebuildFootIkWorldUpdateNodes(){const t=this.ikRoot,e=this.ikBoneRefs;if(null==t||null==e)return void(this.ikWorldUpdateNodes.length=0);const o=new Set,i=[this.ikPelvisBone,e.left.upper,e.left.lower,e.left.foot,e.right.upper,e.right.lower,e.right.foot,this.ikTargetBones?.left,this.ikTargetBones?.right];for(const e of i){let i=e;for(;null!=i&&(o.add(i),i!==t);)i=i.parent??void 0}this.ikWorldUpdateNodes.length=0,t.traverse(t=>{o.has(t)&&this.ikWorldUpdateNodes.push(t)})}syncFootIkWorldMatrices(){const t=this.ikRoot;if(null!=t){t.updateWorldMatrix(!0,!1);for(const e of this.ikWorldUpdateNodes)e!==t&&(e.matrixAutoUpdate&&e.updateMatrix(),e.matrixWorldAutoUpdate&&(null==e.parent?e.matrixWorld.copy(e.matrix):e.matrixWorld.multiplyMatrices(e.parent.matrixWorld,e.matrix)),e.matrixWorldNeedsUpdate=!1)}}computeFootVerticalSpeed(t,e,o){return!t.hasLastFootSample||o<=1e-6?0:(e.y-t.lastFootWorldPos.y)/o}updateFootSample(t,e){t.lastFootWorldPos.copy(e),t.hasLastFootSample=!0}estimateFootContactOffset(t,e,o){const i=this.footIkFootOffset+this.footIkExtraClearance;if(!this.footIkAutoContactOffset)return i;const s=t.bones.indexOf(o);if(s<0)return i;const r=t.boneInverses[s];if(null==r)return i;const n=this.ikFootAxes?.[e]??this.detectFootAxisBasis(o),a=o.getWorldPosition(E),h=H.copy(n.upLocal).applyQuaternion(this.getCurrentWorldQuaternion(o,j)).normalize();let c=0;return o.traverse(e=>{if(!(e instanceof l)||e===o)return;const i=t.bones.indexOf(e);if(i<0)return;const s=$.copy(t.boneInverses[i]).invert(),n=K.copy(r).multiply(s),d=V.setFromMatrixPosition(n),p=N.copy(d).applyMatrix4(o.matrixWorld).sub(a);c=Math.min(c,p.dot(h))}),Math.max(i,-c+this.footIkExtraClearance)}shouldPlantFoot(t,e,o,i,s,r){if(!e||!o)return!1;if(this.footIkUsingContacts)return t.contactRequested;const n=this.footIkPlantReleaseExtension,a=this.footIkPlantAttachExtension,l=this.footIkPlantReleaseLift,h=this.footIkPlantAttachLift;return t.planted?!(i>this.footIkPlantReleaseUpSpeed&&(s<n||r>l)):s>=a&&r<=h||i<=0&&s>=n}computeLegExtensionRatio(t,e){const o=V.setFromMatrixPosition(t.upper.matrixWorld),i=E.setFromMatrixPosition(t.foot.matrixWorld),s=o.distanceTo(i);return e.total<=1e-6?1:k.clamp(s/e.total,0,1.2)}computeRequiredPelvisDrop(t,e,o){const i=N.subVectors(t,e),s=i.length();if(s<=o+1e-6)return 0;const r=i.dot(O),n=o*o-Math.max(s*s-r*r,0);if(n<=0)return s-o;const a=Math.sqrt(n),l=r-a,h=r+a;return l>=0?l:h>=0?h:0}updateLegTiltSimple(t,e,o,i,s){const r=this.footIkScratch,n=this.footIkHits,a=r.footWorldPos.setFromMatrixPosition(e.matrixWorld),l=this.computeFootVerticalSpeed(o,a,s),h=this.resolveFootAxes(t,e,this.footIkAxesScratch),c=this.resolveGroundSampleAxes(t,e,h,this.footIkSampleAxesScratch),d=r.centerRayStart.copy(a).addScaledVector(O,this.footIkRayStartHeight),p=r.toeRayStart.copy(d).addScaledVector(c.forward,this.footIkForwardSampleDistance),u=r.heelRayStart.copy(d).addScaledVector(c.forward,-this.footIkForwardSampleDistance),f=r.sideRayStart.copy(d).addScaledVector(c.right,this.footIkSideSampleDistance),g=this.getFirstGroundHit(d,n.center),m=this.getFirstGroundHit(p,n.toe),y=this.getFirstGroundHit(u,n.heel),S=this.getFirstGroundHit(f,n.side),I=r.desiredNormal.set(0,1,0),P=r.desiredForward.copy(c.forward);let w=0,v=0,b=this.footIkWeightOutSpeed;const x=null!=g||null!=m||null!=y;let L=a.y;null!=g?L=g.point.y:null!=m&&null!=y?L=.5*(m.point.y+y.point.y):null!=m?L=m.point.y:null!=y&&(L=y.point.y);const B=this.ikBoneRefs?.[t],R=this.ikLegLengths?.[t],F=null!=B&&null!=R?this.computeLegExtensionRatio(B,R):1,A=Math.max(0,a.y-L,a.y-o.targetPosition.y),D=this.shouldPlantFoot(o,!this.footIkRequireGrounded||i,x,l,F,A);D?this.computeDesiredGroundNormal(c,g,m,y,S,I)&&(this.computeDesiredFootForward(c.forward,c.right,I,m,y,P),w=this.computeDesiredPitchAngle(m,y,c.forward),v=1,b=this.footIkWeightInSpeed):this.computeDesiredFootForward(c.forward,c.right,I,null,null,P);const C=1-Math.exp(-this.footIkPositionLerpSpeed*s),M=1-Math.exp(-b*s);o.normal.lerp(I,C).normalize(),o.forward.lerp(P,C).normalize(),o.pitch=k.lerp(o.pitch,w,C),o.weight=k.lerp(o.weight,v,M),o.planted=D&&v>.001,this.updateFootSample(o,a),this.applySimpleFootPitch(t,e,o),this.footIkDebug&&this.updateLegDebug(t,{footWorldPos:a,centerRayStart:d,toeRayStart:p,heelRayStart:u,sideRayStart:f,centerHit:g,toeHit:m,heelHit:y,sideHit:S,desiredNormal:o.normal,desiredTargetPos:r.footWorldPos,weight:o.weight})}updateLegIkTarget(t,e,o,i,s,r,n){const a=this.footIkScratch,l=this.footIkHits,h=e.foot,c=a.footWorldPos.setFromMatrixPosition(h.matrixWorld);if(this.footIkUsingContacts&&!s.contactRequested)return s.anchor.reset(),s.planted=!1,s.weight*=Math.exp(-this.footIkWeightOutSpeed*n),s.pitch*=Math.exp(-this.footIkRotationLerpSpeed*n),s.weight<=.001?s.targetPosition.copy(c):s.targetPosition.lerp(c,1-Math.exp(-this.footIkPositionLerpSpeed*n)),this.clampDesiredFootTarget(e,o,c,s.targetPosition),this.updateFootSample(s,c),this.setBoneWorldPosition(i,s.targetPosition),void(null!=this.ikDebugLegs&&(this.ikDebugLegs[t].root.visible=!1));const d=this.computeFootVerticalSpeed(s,c,n),p=this.resolveFootAxes(t,h,this.footIkAxesScratch),u=this.resolveGroundSampleAxes(t,h,p,this.footIkSampleAxesScratch),f=this.footIkUsingContacts&&s.anchor.locked&&s.contactRequested?s.anchor.position:c,g=a.centerRayStart.copy(f).addScaledVector(O,this.footIkRayStartHeight),m=a.toeRayStart.copy(g).addScaledVector(u.forward,this.footIkForwardSampleDistance),y=a.heelRayStart.copy(g).addScaledVector(u.forward,-this.footIkForwardSampleDistance),S=a.sideRayStart.copy(g).addScaledVector(u.right,this.footIkSideSampleDistance),I=this.getFirstGroundHit(g,l.center),P=this.getFirstGroundHit(m,l.toe),w=this.getFirstGroundHit(y,l.heel),v=this.getFirstGroundHit(S,l.side),b=a.desiredTargetPos.copy(c),x=a.desiredNormal.set(0,1,0),L=a.desiredForward.copy(u.forward);let B=0,R=0,F=this.footIkWeightOutSpeed;const A=null!=I||null!=P||null!=w;let D=c.y;null!=I?D=I.point.y:null!=P&&null!=w?D=.5*(P.point.y+w.point.y):null!=P?D=P.point.y:null!=w&&(D=w.point.y);const C=this.computeLegExtensionRatio(e,o),M=Math.max(0,c.y-D,c.y-s.targetPosition.y),T=this.shouldPlantFoot(s,!this.footIkRequireGrounded||r,A,d,C,M);if(T){if(b.copy(c),this.computeDesiredGroundNormal(u,I,P,w,v,x)){this.computeDesiredFootForward(u.forward,u.right,x,P,w,L),B=this.computeDesiredPitchAngle(P,w,u.forward);const t=Math.max(0,Math.sin(B))*this.footIkForwardSampleDistance*.45,e=Math.max(this.footIkFootOffset+this.footIkExtraClearance,s.contactOffset);b.y=D+e+t,s.top&&!this.footIkUsingContacts&&(b.y-=this.ikPelvisOffsetY/2),R=1,F=this.footIkWeightInSpeed}}else this.computeDesiredFootForward(u.forward,u.right,x,null,null,L),b.copy(c),this.footIkUsingContacts||(b.y+=this.footIkFootOffset);if(this.footIkUsingContacts){const t=z.setFromMatrixPosition(e.upper.matrixWorld),i=this.footIkPelvisEnabled?Math.min(this.footIkMaxPlantPelvisDrop,this.footIkPelvisMaxOffsetDown):0;s.anchor.locked&&Math.abs(b.y-s.anchor.position.y)>.08&&s.anchor.release();s.anchor.update(s.contactRequested,(!this.footIkRequireGrounded||r)&&null!=I&&R>0,c,b,u.forward,t,.995*o.total,i,Math.min(this.footIkMaxPlantDrift,this.footIkMaxHorizontalOffset),k.degToRad(this.footIkMaxPlantTurnAngleDeg))?b.copy(s.anchor.position):(b.copy(c),R=0,F=this.footIkWeightOutSpeed)}const W=1-Math.exp(-this.footIkPositionLerpSpeed*n),q=1-Math.exp(-F*n);this.footIkUsingContacts&&s.anchor.locked?s.targetPosition.copy(b):s.targetPosition.lerp(b,W),this.clampDesiredFootTarget(e,o,c,s.targetPosition),s.normal.lerp(x,W).normalize(),s.forward.lerp(L,W).normalize(),s.pitch=k.lerp(s.pitch,B,W),s.weight=k.lerp(s.weight,R,q),s.planted=T&&R>.001,this.updateFootSample(s,c),this.setBoneWorldPosition(i,s.targetPosition),this.footIkDebug&&this.updateLegDebug(t,{footWorldPos:c,centerRayStart:g,toeRayStart:m,heelRayStart:y,sideRayStart:S,centerHit:I,toeHit:P,heelHit:w,sideHit:v,desiredNormal:x,desiredTargetPos:s.targetPosition,weight:s.weight})}updateLegDebug(t,e){if(!this.footIkDebug||null==this.ikDebugLegs||null==this.ikBoneRefs)return;const o=this.ikDebugLegs[t],i=this.ikBoneRefs[t],s=this.resolveFootAxes(t,i.foot,this.footIkAxesScratch),r=this.footIkScratch,n=e.centerHit?.point??r.debugRayEndA.copy(e.centerRayStart).addScaledVector(W,this.footIkRayLength),a=e.toeHit?.point??r.debugRayEndB.copy(e.toeRayStart).addScaledVector(W,this.footIkRayLength),l=e.heelHit?.point??r.debugRayEndC.copy(e.heelRayStart).addScaledVector(W,this.footIkRayLength),h=e.sideHit?.point??r.debugRayEndD.copy(e.sideRayStart).addScaledVector(W,this.footIkRayLength);this.setDebugArrow(o.rayCenter,e.centerRayStart,n),this.setDebugArrow(o.rayToe,e.toeRayStart,a),this.setDebugArrow(o.rayHeel,e.heelRayStart,l),this.setDebugArrow(o.raySide,e.sideRayStart,h);const c=e.centerHit?.point??e.toeHit?.point??e.heelHit?.point??e.sideHit?.point??e.footWorldPos;this.setDebugArrow(o.hitNormal,c,r.debugRayEndE.copy(c).addScaledVector(e.desiredNormal,this.footIkDebugNormalLength)),this.setDebugArrow(o.footUp,e.footWorldPos,r.debugRayEndF.copy(e.footWorldPos).addScaledVector(s.up,this.footIkDebugAxisLength*(.15+e.weight))),this.setDebugArrow(o.footToTarget,e.footWorldPos,e.desiredTargetPos)}getFirstGroundHit(t,e){return"physics"===this.footIkRaycastMode?this.rayTestDown(t,this.footIkRayLength,e)?e:null:"physicsThenRender"===this.footIkRaycastMode?this.rayTestDown(t,this.footIkRayLength,e)||this.raycastDownFirstValid(t,this.footIkRayLength,e)?e:null:this.raycastDownFirstValid(t,this.footIkRayLength,e)?e:null}resolveGroundSampleAxes(t,e,o,i){const s=o??this.resolveFootAxes(t,e),r=i??{forward:new p,right:new p},n=this.getCurrentWorldDirection(this.actor.object,N);n.addScaledVector(O,-n.dot(O)),n.lengthSq()<1e-8&&n.copy(s.forward).addScaledVector(O,-s.forward.dot(O)),n.lengthSq()<1e-8&&n.set(0,0,1),n.normalize();const a=z.crossVectors(O,n);return a.lengthSq()<1e-8?a.copy(s.right):a.normalize(),a.dot(s.right)<0&&a.multiplyScalar(-1),r.forward.copy(n),r.right.copy(a),r}computeDesiredGroundNormal(t,e,o,i,s,r){const n=this.computeAverageHitNormal(e,o,i,s,N);let a=!1,l=0,h=0;if(null!=o&&null!=i){const e=E.subVectors(o.point,i.point).dot(t.forward);Math.abs(e)>1e-5&&(l=(o.point.y-i.point.y)/e,a=!0)}if(null!=s&&null!=e){const o=H.subVectors(s.point,e.point).dot(t.right);Math.abs(o)>1e-5&&(h=(s.point.y-e.point.y)/o,a=!0)}if(a&&(r.copy(O),r.addScaledVector(t.forward,-l),r.addScaledVector(t.right,-h),r.lengthSq()>1e-8?r.normalize():a=!1),a)null!=n&&r.dot(n)<0&&r.multiplyScalar(-1);else{if(null==n)return!1;r.copy(n),a=!0}return r.dot(O)<0&&r.multiplyScalar(-1),this.clampSlopeNormal(r),!0}computeAverageHitNormal(t,e,o,i,s){s.set(0,0,0);let r=0;return null!=t&&t.hasNormal&&(s.add(t.normal),r++),null!=e&&e.hasNormal&&(s.add(e.normal),r++),null!=o&&o.hasNormal&&(s.add(o.normal),r++),null!=i&&i.hasNormal&&(s.add(i.normal),r++),0===r||s.lengthSq()<1e-8?null:(s.multiplyScalar(1/r).normalize(),s)}computeDesiredFootForward(t,e,o,i,s,r){null!=i&&null!=s?r.subVectors(i.point,s.point):r.copy(t),r.addScaledVector(o,-r.dot(o)),r.lengthSq()<1e-8&&(r.copy(this.getCurrentWorldDirection(this.actor.object,N)),r.addScaledVector(o,-r.dot(o))),r.lengthSq()<1e-8&&r.crossVectors(o,e),r.lengthSq()<1e-8&&r.set(0,0,1),r.normalize();const n=N.copy(this.getCurrentWorldDirection(this.actor.object,N));n.addScaledVector(o,-n.dot(o)),n.lengthSq()<1e-8&&(n.copy(t),n.addScaledVector(o,-n.dot(o))),n.lengthSq()>1e-8&&n.normalize(),r.dot(n)<0&&r.multiplyScalar(-1)}computeDesiredPitchAngle(t,e,o){if(null==t||null==e)return 0;const i=E.subVectors(t.point,e.point),s=Math.abs(i.dot(o));if(s<1e-5)return 0;const r=t.point.y-e.point.y,n=k.degToRad(this.footIkMaxSlopeAngleDeg);return k.clamp(-Math.atan2(r,s),-n,n)}applySimpleFootPitch(t,e,o){if(null==e.parent||o.weight<=.001)return;const i=this.ikFootAxes?.[t]??this.detectFootAxisBasis(e),s=o.pitch*o.weight;if(Math.abs(s)<=1e-5)return;const r=this.resolveLocalPitchSign(i),n=j.copy(e.quaternion),a=Q.setFromAxisAngle(i.rightLocal,s*r);e.quaternion.copy(n.multiply(a))}resolveLocalPitchSign(t){const e=G.setFromAxisAngle(t.rightLocal,.15);return H.copy(t.upLocal).applyQuaternion(e).sub(t.upLocal).dot(t.forwardLocal)>=0?1:-1}clampDesiredFootTarget(t,e,o,i){const s=E.subVectors(i,o),r=s.dot(O),n=H.copy(s).addScaledVector(O,-r),a=n.length();a>this.footIkMaxHorizontalOffset&&a>1e-6&&n.multiplyScalar(this.footIkMaxHorizontalOffset/a);const l=k.clamp(r,-this.footIkMaxVerticalOffsetDown,this.footIkMaxVerticalOffsetUp);if(i.copy(o),i.add(n),i.addScaledVector(O,l),this.footIkPelvisEnabled)return;const h=t.upper.getWorldPosition(V).clone(),c=E.subVectors(i,h),d=c.length(),p=.995*e.total;d>p&&d>1e-6&&i.copy(h).addScaledVector(c,p/d)}solveTwoBoneLeg(t,e,o){const i=this.ikSolveScratch,s=i.hip.setFromMatrixPosition(e.upper.matrixWorld),r=i.knee.setFromMatrixPosition(e.lower.matrixWorld),n=i.foot.setFromMatrixPosition(e.foot.matrixWorld),a=s.distanceTo(r),l=r.distanceTo(n);if(a<1e-5||l<1e-5)return;const h=i.direction.subVectors(o,s),c=h.length();if(c<1e-5)return;const d=Math.abs(a-l)+1e-5,p=a+l-1e-5,u=k.clamp(c,d,p),f=h.multiplyScalar(1/c),g=i.animatedDirection.subVectors(n,s).normalize(),m=i.pole.subVectors(r,s);m.addScaledVector(g,-m.dot(g)),m.lengthSq()<a*a*1e-4&&(this.getCurrentWorldDirection(this.actor.object,m),m.addScaledVector(g,-m.dot(g)),m.lengthSq()<1e-8&&(Math.abs(g.x)<.9?m.set(1,0,0):m.set(0,1,0),m.addScaledVector(g,-m.dot(g)))),m.normalize(),i.rotation.setFromUnitVectors(g,f),m.applyQuaternion(i.rotation);const y=(u*u+a*a-l*l)/(2*u),S=Math.max(a*a-y*y,0),I=Math.sqrt(S),P=i.kneeTarget.copy(s).addScaledVector(f,y).addScaledVector(m,I),w=i.target.copy(s).addScaledVector(f,u);this.rotateBoneToward(e.upper,s,r,P),e.upper.updateMatrixWorld(!0);const v=r.setFromMatrixPosition(e.lower.matrixWorld),b=n.setFromMatrixPosition(e.foot.matrixWorld);this.rotateBoneToward(e.lower,v,b,w),e.lower.updateMatrixWorld(!0),this.updateSolveDebug(t,s,P,w)}updateSolveDebug(t,e,o,i){if(!this.footIkDebug||null==this.ikDebugLegs)return;const s=this.ikDebugLegs[t];this.setDebugArrow(s.solveUpper,e,o),this.setDebugArrow(s.solveLower,o,i)}rotateBoneToward(t,e,o,i){const s=this.ikSolveScratch.currentDirection.subVectors(o,e),r=this.ikSolveScratch.desiredDirection.subVectors(i,e);if(s.lengthSq()<1e-10||r.lengthSq()<1e-10)return;if(s.normalize(),r.normalize(),s.dot(r)>1-1e-10)return;const n=this.ikSolveScratch.rotation.setFromUnitVectors(s,r),a=this.getCurrentWorldQuaternion(t,j),l=Q.copy(n).multiply(a);if(null==t.parent)return void t.quaternion.copy(l);const h=this.getCurrentWorldQuaternion(t.parent,G),c=_.copy(h).invert().multiply(l);t.quaternion.copy(c)}detectFootAxisBasis(t){const e=this.getCurrentWorldQuaternion(t,j),o=this.getCurrentWorldDirection(this.actor.object,V);o.lengthSq()<1e-6&&o.set(0,0,1),o.normalize();const i=[{local:Y.clone(),world:Y.clone().applyQuaternion(e)},{local:J.clone(),world:J.clone().applyQuaternion(e)},{local:X.clone(),world:X.clone().applyQuaternion(e)}];i.push({local:Y.clone().multiplyScalar(-1),world:Y.clone().multiplyScalar(-1).applyQuaternion(e)}),i.push({local:J.clone().multiplyScalar(-1),world:J.clone().multiplyScalar(-1).applyQuaternion(e)}),i.push({local:X.clone().multiplyScalar(-1),world:X.clone().multiplyScalar(-1).applyQuaternion(e)});let s=i[0],r=-1/0;for(const t of i){const e=t.world.dot(O);e>r&&(r=e,s=t)}let n=i[0],a=-1/0;for(const t of i){if(Math.abs(t.world.dot(s.world))>.6)continue;const e=Math.abs(t.world.dot(o));e>a&&(a=e,n=t)}n.world.dot(o)<0&&(n={local:n.local.clone().multiplyScalar(-1),world:n.world.clone().multiplyScalar(-1)});const l=s.local.clone().normalize(),h=n.local.clone().normalize(),c=l.clone().cross(h).normalize();return{upLocal:l,forwardLocal:c.clone().cross(l).normalize(),rightLocal:c}}resolveFootAxes(t,e,o){const i=this.ikFootAxes?.[t]??this.detectFootAxisBasis(e),s=o??{up:new p,forward:new p,right:new p},r=this.getCurrentWorldQuaternion(e,j);return s.up.copy(i.upLocal).applyQuaternion(r).normalize(),s.forward.copy(i.forwardLocal).applyQuaternion(r).normalize(),s.right.copy(i.rightLocal).applyQuaternion(r).normalize(),s}getCurrentWorldQuaternion(t,e){return t.matrixWorld.decompose(z,e,N),e}getCurrentWorldDirection(t,e){const o=t.matrixWorld.elements;return e.set(o[8],o[9],o[10]).normalize()}clampSlopeNormal(t){const e=k.clamp(t.dot(O),-1,1),o=Math.acos(e),i=k.degToRad(this.footIkMaxSlopeAngleDeg);if(o<=i||o<1e-5)return;const s=i/o;t.lerpVectors(O,t,s).normalize()}blendLegAfterSolve(t,e,o){if(!(o>=.999)){if(o<=.001)return t.upper.quaternion.copy(e.upper),t.lower.quaternion.copy(e.lower),void t.foot.quaternion.copy(e.foot);this.blendBoneQuaternion(t.upper,e.upper,o),this.blendBoneQuaternion(t.lower,e.lower,o),this.blendBoneQuaternion(t.foot,e.foot,o)}}blendBoneQuaternion(t,e,o){j.copy(t.quaternion),t.quaternion.copy(e).slerp(j,o)}alignFootToGroundNormal(t,e,o,i){if(o.weight<=.001||null==e.parent)return;const s=this.ikFootAxes?.[t]??this.detectFootAxisBasis(e),r=V.copy(o.normal).normalize(),n=E.copy(o.forward);if(n.addScaledVector(r,-n.dot(r)),n.lengthSq()<1e-8){const o=this.resolveFootAxes(t,e);n.copy(o.forward).addScaledVector(r,-o.forward.dot(r))}if(n.lengthSq()<1e-8)return;n.normalize();const a=H.crossVectors(r,n);if(a.lengthSq()<1e-8)return;a.normalize(),n.copy(N.crossVectors(a,r)).normalize(),$.makeBasis(s.rightLocal,s.upLocal,s.forwardLocal),j.setFromRotationMatrix($),K.makeBasis(a,r,n),Q.setFromRotationMatrix(K);const l=G.copy(Q).multiply(_.copy(j).invert()),h=this.getCurrentWorldQuaternion(e.parent,_),c=Q.copy(h).invert().multiply(l),d=(1-Math.exp(-this.footIkRotationLerpSpeed*i))*o.weight;e.quaternion.slerp(c,k.clamp(d,0,1))}getMixer(){return this.mixer}evaluateExternalPose(t=0){null!=this.mixer&&(this.restoreFootIkPose(),this.runPoseProcessors("beforeMixer",t),this.mixer.update(t),this.runPoseProcessors("afterMixer",t),this.captureFootIkAnimationPose(),this.runPoseProcessors("beforeIk",t),this.updateFootIk(t),this.runPoseProcessors("afterIk",t))}beginExternalControl(){this.externalControlActive=!0}endExternalControl(){this.externalControlActive=!1}stopSequenceAnimation(){this.currentFullBodyPriority=-1,this.externalControlActive=!1,this.sequenceAnimationControlled=!1,this.sequenceAnimationReleasePending=!1,this.sequenceAnimationReleaseFrames=0,this.upperBodyOverride=!1,this.overrideFadeTimeUpper=null,this.fullBodyTimer=0}requestSequenceAnimationRelease(){this.externalControlActive=!1,this.sequenceAnimationControlled=!1,this.currentFullBodyPriority=-1,this.sequenceAnimationReleasePending=!0,this.sequenceAnimationReleaseFrames=1,this.upperBodyOverride=!1,this.overrideFadeTimeUpper=null}cancelRootMotionAction(t){null!=t&&this.fullBodyAction!==t||(this.currentFullBodyPriority=-1,this.externalControlActive=!1,this.fullBodyAction?.stop(),this.fullBodyAction=null,this.fullBodyTimer=0)}beginExternalAnimationControl(t,e={}){at(null!=this.mixer,"Can't begin external control before setup is called"),this.assertClipRigCompatible(t),this.mixer.stopAllAction(),this.currentFullBodyPriority=99,this.fullBodyTimer=0;const o=this.mixer.clipAction(t);return this.managedActions.indexOf(o)<0&&this.managedActions.push(o),o.setLoop(c,1),o.clampWhenFinished=!0,o.timeScale=e.timeScale??1,o.reset(),o.play(),this.fullBodyAction=o,this.externalControlActive=!0,o}syncMovementSpeed(t){null!=t&&(t.timeScale=this.getPlaybackTimeScale(t.getClip(),t.timeScale))}getPlaybackTimeScale(t,e){if(!(t instanceof s&&t.fixedInPlace&&null!=this.movementSpeed))return e;const o=this.mixer.getRoot(),i=o instanceof h?o.scale.x:1;return t.duration/t.displacement*this.movementSpeed/i}playStateMachine(t){this.stateMachines.push(t)}playUpperStateMachine(t){this.upperStateMachines.push(t)}removeStateMachine(t){const e=this.stateMachines.indexOf(t);e>=0&&this.stateMachines.splice(e,1)}removeUpperStateMachine(t){const e=this.upperStateMachines.indexOf(t);e>=0&&this.upperStateMachines.splice(e,1)}playUpper(t,e={}){this.assertClipRigCompatible(t);const o=e?.priority??1;o<this.currentUpperBodyPriority||(this.currentUpperBodyPriority=o,this.upperBodyAction=this.transition(this.upperBodyAction,this.getUpperBodyClip(t),e),this.upperBodyAction.timeScale=e?.timeScale??1,this.upperBodyTimer=0,this.upperBodyOverride=!0,this.overrideFadeTimeUpper=e.fadeTime)}cancelUpper(t){this.upperBodyOverride&&this.upperBodyAction?.getClip()===this.getUpperBodyClip(t)&&(this.upperBodyOverride=!1,this.currentUpperBodyPriority=-1,null!=this.fullBodyClip?this.upperBodyAction=this.transition(this.upperBodyAction,this.getUpperBodyClip(this.fullBodyClip)):(this.upperBodyAction?.stop(),this.upperBodyAction=null),this.overrideFadeTimeUpper=null)}play(t,e={}){at(null!=this.mixer,"Can't play animation before setup is called"),this.assertClipRigCompatible(t);const o=e.priority??1;if(o<this.currentFullBodyPriority)return;this.currentFullBodyPriority=o,this.fullBodyTimer=0,e.sequenceControlled&&(this.sequenceAnimationControlled=!0,this.sequenceAnimationReleasePending=!1,this.sequenceAnimationReleaseFrames=0);const i=this.getFullBodyClip(t);if(this.preparePoseTransition(t,i,e)){for(const t of this.managedActions)t.stop();this.managedActions.length=0,this.fullBodyAction=null,this.upperBodyAction=null}this.upperBodyOverride||(this.upperBodyAction=this.transition(this.upperBodyAction,this.getUpperBodyClip(t),e),this.upperBodyAction.timeScale=e?.timeScale??1),this.fullBodyClip=t,this.fullBodyAction=this.transition(this.fullBodyAction,i,e),this.fullBodyAction.timeScale=e?.timeScale??1,this.fullBodyAction.getClip().uuid==this.upperBodyAction.getClip().uuid&&this.upperBodyAction.syncWith(this.fullBodyAction),this.overrideFadeTime=e.fadeTime}assertClipRigCompatible(t){const e=L(t);if(e===this.animationRigId||null==e&&null==this.animationRigId)return;const o=`${t.uuid}|${e??""}|${this.animationRigId??""}`;if(!dt.has(o)){if(dt.add(o),null==e||null==this.animationRigId)return void console.warn(`Animation clip "${t.name}" or its character has no rig assigned. Playing without rig compatibility validation for legacy compatibility.`);console.warn(`Animation clip "${t.name}" targets rig ${e??"no rig"}, but this character uses rig ${this.animationRigId??"no rig"}. Generate and assign a retargeted clip for the character rig.`)}}onActionDone(t){return new Promise(e=>{const o=i=>{i.action===t&&(e(i.action),this.mixer.removeEventListener("finished",o))};this.mixer.addEventListener("finished",o)})}transition(t,e,o={}){const s=o?.fadeTime??this.fadeTime;if(null!=t&&t.getClip().uuid===e.uuid)return t.isRunning()||!1!==o.loop||!t.clampWhenFinished||o.sequenceControlled?(t.isRunning()||(t.reset(),null!=o.offset&&(t.time=o.offset),t.enabled=!0,t.weight=1,t.setEffectiveWeight(1),t.setEffectiveTimeScale(1),t.play(),!1===o.loop&&(t.setLoop(c,1),t.clampWhenFinished=!0)),t):t;if(i(t)){const i=t,r=this.mixer.clipAction(e);r.reset(),null!=o.offset&&(r.time=o.offset),r.play(),r.enabled=!0,r.setEffectiveTimeScale(1),r.weight=1,i.crossFadeTo(r,s,!1),t=r}else(t=this.mixer.clipAction(e)).reset(),null!=o.offset&&(t.time=o.offset),t.enabled=!0,t.weight=1,t.setEffectiveWeight(1),t.setEffectiveTimeScale(1),t.play();return!1===o.loop&&(t.setLoop(c,1),t.clampWhenFinished=!0),this.managedActions.indexOf(t)<0&&this.managedActions.push(t),t}};st=t([o({inEditor:!0})],st);export{st as CharacterAnimationComponent};function rt(t,e){if(null==t)return e;const o=e.clone();A(o,L(e)),T(o,C(e));const i=new Set(t.map(t=>t.name));return o.tracks=o.tracks.filter(t=>i.has(t.name.split(".")[0])),o}function nt(t){return t.flatMap(t=>function(t){const e=[];return t.traverse(t=>{e.push(t)}),e}(t)).filter(t=>t instanceof l)}function at(t,e){if(!1===t||"function"==typeof t&&!1===t())throw new Error(e)}function lt(t,e){const o=new Map;return(i,...s)=>{const r=t(i);return o.has(r)||o.set(r,e(i,...s)),o.get(r)}}function ht(t){let e;return t.traverse(t=>{(t instanceof l||t.isBone)&&null==e&&(e=t)}),e}function ct(t,e){let o=e;for(;null!=o;){if(o===t)return!0;o=o.parent}return!1}const dt=new Set;/*
2
2
  * Copyright (©) 2026 Hology Interactive AB. All rights reserved.
3
3
  * See the LICENSE.md file for details.
4
4
  */
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=vfx-backend-partition.test.d.ts.map
@@ -0,0 +1,4 @@
1
+ import{describe as t,expect as e,it as i}from"vitest";import{partitionVfxBackends as s}from"../effects/vfx/vfx-backend-partition.js";const n=(t,e,i)=>({id:t,name:t,output:{type:e,space:"world"},children:i});t("VFX backend partition",()=>{i("keeps independent visual roots on GPU and whole light dependency trees on CPU",()=>{const t=n("visual","sprite"),i=n("light","pointLight"),o=n("parent","mesh",[n("child-light","pointLight")]),p={id:"effect",vfx:{emitters:[t,i,o]}},r=s(p);e(r.gpu.vfx.emitters).toEqual([t]),e(r.cpu.vfx.emitters).toEqual([i,o]),e(r.cpu.vfx.emitters[1]).toBe(o),e(p.vfx.emitters).toHaveLength(3),e(s(p).gpu).toBe(r.gpu)}),i("invalidates cached partitions on revision changes",()=>{const t={id:"effect",vfx:{emitters:[n("light","pointLight")]}};e(s(t,1).gpu.vfx.emitters).toHaveLength(0),t.vfx.emitters.push(n("visual","sprite")),e(s(t,2).gpu.vfx.emitters).toHaveLength(1)})});/*
2
+ * Copyright (©) 2026 Hology Interactive AB. All rights reserved.
3
+ * See the LICENSE.md file for details.
4
+ */
@@ -1,4 +1,4 @@
1
- import{Object3D as e,Vector3 as t}from"three";import{describe as o,expect as a,it as n,vi as r}from"vitest";r.mock("../gameplay/actors/builtin/index.js",()=>({builtInActors:{},default:{}})),r.mock("../gameplay/actors/index.js",()=>({CharacterAnimationComponent:class{},CharacterMovementComponent:class{}})),r.mock("../rendering.js",()=>({RenderingView:class{},setRenderingPaused:r.fn()})),r.hoisted(()=>{const e=new Proxy({},{get:(e,t)=>e[t]??(e[t]=r.fn()),set:(e,t,o)=>(e[t]=o,!0)});Object.defineProperty(HTMLCanvasElement.prototype,"getContext",{configurable:!0,value:()=>e})}),o("VFX playback transform",()=>{n("refreshes a reused GPU effect before resuming emission",async()=>{const{VfxActor:o}=await import("../effects/vfx/vfx-actor.js"),n=Object.create(o.prototype),s=new e;s.position.set(12,0,8),n.object=new e,n.object.position.set(0,0,1.75),s.add(n.object),n._worldPos=new t;const i=new t;n.gpuPrepared={},n.gpuHandle={active:!0,setPosition:r.fn(e=>i.copy(e)),setTransform:r.fn(),play:r.fn(()=>a(i.toArray()).toEqual([12,0,9.75])),restart:r.fn(()=>a(i.toArray()).toEqual([20,0,9.75]))},n.play(),a(n.gpuHandle.setTransform).toHaveBeenCalledWith(n.object.matrixWorld),a(n.gpuHandle.play).toHaveBeenCalledOnce(),s.position.x=20,n.restart(),a(n.gpuHandle.restart).toHaveBeenCalledOnce()})});/*
1
+ import{Object3D as e,Vector3 as t}from"three";import{describe as a,expect as n,it as o,vi as s}from"vitest";s.mock("../gameplay/actors/builtin/index.js",()=>({builtInActors:{},default:{}})),s.mock("../gameplay/actors/index.js",()=>({CharacterAnimationComponent:class{},CharacterMovementComponent:class{}})),s.mock("../rendering.js",()=>({RenderingView:class{},setRenderingPaused:s.fn()})),s.hoisted(()=>{const e=new Proxy({},{get:(e,t)=>e[t]??(e[t]=s.fn()),set:(e,t,a)=>(e[t]=a,!0)});Object.defineProperty(HTMLCanvasElement.prototype,"getContext",{configurable:!0,value:()=>e})}),a("VFX playback transform",()=>{o("refreshes a reused GPU effect before resuming emission",async()=>{const{VfxActor:a}=await import("../effects/vfx/vfx-actor.js"),o=Object.create(a.prototype),r=new e;r.position.set(12,0,8),o.object=new e,o.object.position.set(0,0,1.75),r.add(o.object),o._worldPos=new t;const i=new t;o.gpuPrepared={},o.gpuHandle={active:!0,setPosition:s.fn(e=>i.copy(e)),setTransform:s.fn(),play:s.fn(()=>n(i.toArray()).toEqual([12,0,9.75])),restart:s.fn(()=>n(i.toArray()).toEqual([20,0,9.75]))},o.play(),n(o.gpuHandle.setTransform).toHaveBeenCalledWith(o.object.matrixWorld),n(o.gpuHandle.play).toHaveBeenCalledOnce(),r.position.x=20,o.restart(),n(o.gpuHandle.restart).toHaveBeenCalledOnce()})}),a("mixed VFX playback",()=>{async function a(){const{VfxActor:a}=await import("../effects/vfx/vfx-actor.js"),{Rate:n}=await import("@hology/nebula"),{Subject:o}=await import("rxjs"),{Euler:r,Quaternion:i}=await import("three"),l=Object.create(a.prototype),p={dead:!1,rate:new n(1,1),particles:[{age:1,life:10}],clearParticlesRecursive:s.fn(),removeAllParticles:s.fn()};return Object.assign(l,{object:new e,_worldPos:new t,_worldRot:new i,_worldScale:new t,_worldEul:new r,_timescale:.5,system:{emitters:[p],update:s.fn(),getCount:s.fn(()=>1)},gpuPrepared:{},gpuHandle:{active:!0,setPosition:s.fn(),setTransform:s.fn(),setTimescale:s.fn(),play:s.fn(),pause:s.fn(),stop:s.fn(),restart:s.fn(),kill:s.fn(),expire:s.fn()},onComplete:new o,paused:!1}),{actor:l,emitter:p}}o("updates CPU simulation with timescale and pauses/resumes both groups",async()=>{const{actor:e}=await a();e.onUpdate(.2),n(e.system.update).toHaveBeenCalledWith(.1),e.pause(),e.onUpdate(.2),n(e.system.update).toHaveBeenCalledTimes(1),n(e.gpuHandle.pause).toHaveBeenCalledOnce(),e.play(),n(e.gpuHandle.play).toHaveBeenCalledOnce(),n(e.paused).toBe(!1)}),o("restarts, stops, expires and kills CPU lights alongside GPU particles",async()=>{const{actor:e,emitter:t}=await a();e.restart(),n(t.clearParticlesRecursive).toHaveBeenCalledOnce(),n(e.gpuHandle.restart).toHaveBeenCalledOnce(),e.stop(),n(t.rate.nextTime).toBe(1/0),n(e.gpuHandle.stop).toHaveBeenCalledOnce(),e.applyClipEndBehavior("expire",.2),n(t.particles[0].life).toBe(1.2),n(e.gpuHandle.expire).toHaveBeenCalledWith(.2),e.applyClipEndBehavior("kill"),n(t.removeAllParticles).toHaveBeenCalledOnce(),n(e.gpuHandle.kill).toHaveBeenCalledOnce()}),o("waits for both groups before completing and does not replay a finished GPU tail on resume",async()=>{const{actor:e,emitter:t}=await a(),o=s.fn();e.onComplete.subscribe(o),e.gpuHandle.active=!1,e.updateCompletionState(),n(o).not.toHaveBeenCalled(),e.pause(),e.play(),n(e.gpuHandle.play).not.toHaveBeenCalled(),t.dead=!0,e.updateCompletionState(),e.updateCompletionState(),n(o).toHaveBeenCalledOnce()}),o("waits for delayed light emission, then completes after its last particle drains",async()=>{const{actor:e,emitter:t}=await a(),{OnceRate:o}=await import("../effects/vfx/rates.js");t.rate=new o(.5,1),t.particles=[],e.gpuHandle.active=!1;const r=s.fn();e.onComplete.subscribe(r),e.updateCompletionState(),n(r).not.toHaveBeenCalled(),n(t.rate.getValue(.6)).toBe(1),t.particles.push({age:0,life:1}),e.updateCompletionState(),n(r).not.toHaveBeenCalled(),t.particles.length=0,e.updateCompletionState(),n(r).toHaveBeenCalledOnce()}),o("can restart a completed GPU group while paused and spawn it on the next play",async()=>{const{actor:e}=await a(),t=e.gpuHandle;t.active=!1,t.release=s.fn(),e.paused=!0,e.inputRuntime={getDefinitions:()=>[]},e.gpuPrepared.spawn=s.fn(()=>(t.active=!0,t)),e.restart(),n(t.release).toHaveBeenCalledOnce(),n(e.gpuHandle).toBeNull(),e.play(),n(e.gpuPrepared.spawn).toHaveBeenCalledOnce(),n(e.gpuHandle.active).toBe(!0)})}),o("materializes only light roots on CPU when the visual roots prepare on GPU",async()=>{const{VfxActor:t}=await import("../effects/vfx/vfx-actor.js"),{VfxInputRuntime:a}=await import("../effects/vfx/vfx-input-runtime.js"),o=Object.create(t.prototype),r=(e,t)=>({id:e,name:e,output:t,rate:{type:"once",count:1,delay:0},position:{x:0,y:0,z:0},rotation:{x:0,y:0,z:0},initializers:[],behaviours:[]}),i=r("visual",{type:"sprite",space:"world"}),l={id:"mixed",type:"vfx",vfx:{emitters:[i,r("light",{type:"pointLight",space:"local",maxLights:1})]}};Object.assign(o,{object:new e,world:{scene:new e},inputOverrides:new Map,inputRuntime:new a,view:{vfxBackendPreference:"auto"},gpuWorld:{prepare:s.fn(async()=>({}))}}),await o.fromAsset(l),n(o.runtimeBackend).toBe("mixed"),n(o.gpuWorld.prepare.mock.calls[0][0].vfx.emitters).toEqual([i]),n(o.system.emitters).toHaveLength(1),n(o.renderBodies).toHaveLength(1),n(o.renderBodies[0].type).toBe("pointLight"),o.system.destroy(),o.disposeSystem()});/*
2
2
  * Copyright (©) 2026 Hology Interactive AB. All rights reserved.
3
3
  * See the LICENSE.md file for details.
4
4
  */
@@ -1,4 +1,4 @@
1
- import{describe as e,expect as t,it as n,vi as o}from"vitest";import*as a from"three";import{NodeShaderMaterial as s}from"three-shader-graph";import{RenderingView as r}from"../rendering.js";import{createVfxRenderPreparation as i,prepareVfxRendering as c}from"../effects/vfx/vfx-render-preparation.js";import{StretchedSprite as l}from"../effects/vfx/stretched-sprite.js";import{materializeVfx as d}from"../effects/vfx/vfx-materializer.js";o.hoisted(()=>{if("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,n)=>(e[t]=n,!0)});Object.defineProperty(HTMLCanvasElement.prototype,"getContext",{configurable:!0,value:()=>e})}),e("VFX rendering preload",()=>{n("batches concurrent effects and keeps their probe resources alive until compilation finishes",async()=>{let e;const n=new Promise(t=>{e=t}),r=o.fn(e=>n),i={compileAsync:r,activeBackend:"webgpu"},l={getCamera:()=>new a.PerspectiveCamera},d=new s({}),m=new a.PlaneGeometry,p=[0,1].map(()=>c([new a.Mesh(m,d)],l,i));await new Promise(e=>setTimeout(e,5)),t(r).toHaveBeenCalledTimes(1);const f=r.mock.calls[0][0];t(f.children).toHaveLength(2);let v=0;f.traverse(e=>{e instanceof a.Mesh&&v++}),t(v).toBe(2);const u=f.children[0].children[0],w=o.spyOn(u.geometry,"dispose"),y=o.spyOn(m,"dispose"),h=o.spyOn(d,"dispose");t(w).not.toHaveBeenCalled(),e(),await Promise.all(p),t(w).toHaveBeenCalledOnce(),t(y).not.toHaveBeenCalled(),t(h).not.toHaveBeenCalled(),t(f.children).toHaveLength(0)}),n("rejects every effect in a failed batch and allows subsequent preparation",async()=>{const e=o.fn().mockRejectedValueOnce(new Error("compile failed")).mockResolvedValue(void 0),n=o.fn(),s={compileAsync:e,initTextures:n,activeBackend:"webgl"},r={getCamera:()=>new a.PerspectiveCamera},i=await Promise.allSettled([0,1].map(()=>c([],r,s)));t(i.map(e=>e.status)).toEqual(["rejected","rejected"]),t(e).toHaveBeenCalledTimes(1),t(n).not.toHaveBeenCalled(),await c([],r,s),t(e).toHaveBeenCalledTimes(2),t(n).toHaveBeenCalledOnce()}),n("includes delayed and nested collision outputs without emitting any particles",async()=>{const e=(e,t=[])=>({id:e,name:e,position:[0,0,0],rotation:[0,0,0],rate:{type:"once",count:0,delay:100},initializers:[],behaviours:[],children:t,spawnEvent:"collision",output:{type:"sprite",texture:e,space:"world",color:"#ffffff",materialSource:"default"}}),n={type:"vfx",id:"test",vfx:{emitters:[e("root",[e("child",[e("grandchild")])])]}},s={getCamera:()=>new a.PerspectiveCamera},r=await d(n,new a.Object3D,{getTexture:o.fn(async()=>new a.Texture)},s,{},{},{},{});t(r.renderBodies).toHaveLength(3),t(r.system.getCount()).toBe(0);const c=i(r.renderBodies,s);t(c.scene.children).toHaveLength(3),t(r.system.getCount()).toBe(0),c.dispose(),r.dispose(),r.system.destroy()}),n("prepares mesh, sprite and stretched output variants without disposing source resources",()=>{const e=new s({});e.transparent=!0;const n=new a.PlaneGeometry,r=new a.Mesh(n,e),c=new l(new a.PlaneGeometry,new s({})),d=new a.Sprite(new a.SpriteMaterial),m=o.spyOn(n,"dispose"),p=o.spyOn(e,"dispose"),f=i([r,c,d],{getCamera:()=>new a.PerspectiveCamera}),v=[];f.scene.traverse(e=>{(e instanceof a.Mesh||e instanceof a.Sprite)&&v.push(e)}),t(v).toHaveLength(3);const u=v.find(e=>e instanceof a.InstancedMesh);t(u.count).toBe(1),t(u.material).toBe(e),t(u.geometry.getAttribute("particleData")).toBeDefined(),t(u.geometry).not.toBe(n),f.dispose(),t(m).not.toHaveBeenCalled(),t(p).not.toHaveBeenCalled(),t(f.scene.children).toHaveLength(0)}),n("waits for every concurrent compilation and recovers after a failed request",async()=>{let e;const n=new Promise(t=>{e=t}),s=o.fn().mockImplementationOnce(()=>n).mockRejectedValueOnce(new Error("compile failed")).mockResolvedValue(void 0),i=Object.create(r.prototype);i.compileTail=Promise.resolve(),i.compileSceneAsync=s;const c=[new a.Scene,new a.Scene,new a.Scene],l=c.map(e=>i.compileAsync(e)),d=Promise.allSettled(l);await Promise.resolve(),await Promise.resolve(),t(s).toHaveBeenCalledTimes(1),e();const m=await d;t(s.mock.calls.map(e=>e[0])).toEqual(c),t(m.map(e=>e.status)).toEqual(["fulfilled","rejected","fulfilled"]),t(i.compileInProgress).toBe(!1)})});/*
1
+ import{describe as e,expect as t,it as n,vi as o}from"vitest";import*as a from"three";import{NodeShaderMaterial as s}from"three-shader-graph";import{RenderingView as i}from"../rendering.js";import{createVfxRenderPreparation as r,prepareVfxRendering as c}from"../effects/vfx/vfx-render-preparation.js";import{StretchedSprite as l}from"../effects/vfx/stretched-sprite.js";import{materializeVfx as d}from"../effects/vfx/vfx-materializer.js";o.hoisted(()=>{if("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,n)=>(e[t]=n,!0)});Object.defineProperty(HTMLCanvasElement.prototype,"getContext",{configurable:!0,value:()=>e})}),e("VFX rendering preload",()=>{n("batches concurrent effects and keeps their probe resources alive until compilation finishes",async()=>{let e;const n=new Promise(t=>{e=t}),i=o.fn(e=>n),r={compileAsync:i,activeBackend:"webgpu"},l={getCamera:()=>new a.PerspectiveCamera},d=new s({}),p=new a.PlaneGeometry,m=[0,1].map(()=>c([new a.Mesh(p,d)],l,r));await new Promise(e=>setTimeout(e,5)),t(i).toHaveBeenCalledTimes(1);const f=i.mock.calls[0][0];t(f.children).toHaveLength(2);let u=0;f.traverse(e=>{e instanceof a.Mesh&&u++}),t(u).toBe(2);const v=f.children[0].children[0],h=o.spyOn(v.geometry,"dispose"),w=o.spyOn(p,"dispose"),y=o.spyOn(d,"dispose");t(h).not.toHaveBeenCalled(),e(),await Promise.all(m),t(h).toHaveBeenCalledOnce(),t(w).not.toHaveBeenCalled(),t(y).not.toHaveBeenCalled(),t(f.children).toHaveLength(0)}),n("rejects every effect in a failed batch and allows subsequent preparation",async()=>{const e=o.fn().mockRejectedValueOnce(new Error("compile failed")).mockResolvedValue(void 0),n=o.fn(),s={compileAsync:e,initTextures:n,activeBackend:"webgl"},i={getCamera:()=>new a.PerspectiveCamera},r=await Promise.allSettled([0,1].map(()=>c([],i,s)));t(r.map(e=>e.status)).toEqual(["rejected","rejected"]),t(e).toHaveBeenCalledTimes(1),t(n).not.toHaveBeenCalled(),await c([],i,s),t(e).toHaveBeenCalledTimes(2),t(n).toHaveBeenCalledOnce()}),n("includes delayed and nested collision outputs without emitting any particles",async()=>{const e=(e,t=[])=>({id:e,name:e,position:[0,0,0],rotation:[0,0,0],rate:{type:"once",count:0,delay:100},initializers:[],behaviours:[],children:t,spawnEvent:"collision",output:{type:"sprite",texture:e,space:"world",color:"#ffffff",materialSource:"default"}}),n={type:"vfx",id:"test",vfx:{emitters:[e("root",[e("child",[e("grandchild")])])]}},s={getCamera:()=>new a.PerspectiveCamera},i=await d(n,new a.Object3D,{getTexture:o.fn(async()=>new a.Texture)},s,{},{},{},{});t(i.renderBodies).toHaveLength(3),t(i.system.getCount()).toBe(0);const c=r(i.renderBodies,s);t(c.scene.children).toHaveLength(3),t(i.system.getCount()).toBe(0),c.dispose(),i.dispose(),i.system.destroy()}),n("prepares mesh, sprite and stretched output variants without disposing source resources",()=>{const e=new s({});e.transparent=!0;const n=new a.PlaneGeometry,i=new a.Mesh(n,e),c=new l(new a.PlaneGeometry,new s({})),d=new a.Sprite(new a.SpriteMaterial),p=o.spyOn(n,"dispose"),m=o.spyOn(e,"dispose"),f=r([i,c,d],{getCamera:()=>new a.PerspectiveCamera}),u=[];f.scene.traverse(e=>{(e instanceof a.Mesh||e instanceof a.Sprite)&&u.push(e)}),t(u).toHaveLength(3);const v=u.find(e=>e instanceof a.InstancedMesh);t(v.count).toBe(1),t(v.material).toBe(e),t(v.geometry.getAttribute("particleData")).toBeDefined(),t(v.geometry).not.toBe(n),f.dispose(),t(p).not.toHaveBeenCalled(),t(m).not.toHaveBeenCalled(),t(f.scene.children).toHaveLength(0)}),n("waits for every concurrent compilation and recovers after a failed request",async()=>{let e;const n=new Promise(t=>{e=t}),s=o.fn().mockImplementationOnce(()=>n).mockRejectedValueOnce(new Error("compile failed")).mockResolvedValue(void 0),r=Object.create(i.prototype);r.compileTail=Promise.resolve(),r.compileSceneAsync=s;const c=[new a.Scene,new a.Scene,new a.Scene],l=c.map(e=>r.compileAsync(e)),d=Promise.allSettled(l);await Promise.resolve(),await Promise.resolve(),t(s).toHaveBeenCalledTimes(1),e();const p=await d;t(s.mock.calls.map(e=>e[0])).toEqual(c),t(p.map(e=>e.status)).toEqual(["fulfilled","rejected","fulfilled"]),t(r.compileInProgress).toBe(!1)})}),e("VFX point lights",()=>{n("caps live lights, reuses slots, and maps alpha/scale without unlimited radii",async()=>{const{Particle:e}=await import("@hology/nebula"),{PointLightRenderer:n}=await import("../effects/vfx/vfx-renderers.js"),o=new a.Object3D,s=new n(o),i={type:"pointLight",output:{type:"pointLight",color:"#ffffff",intensity:8,radius:4,maxLights:1}},r=()=>{const t=new e;return t.body=i,t.scale=.5,t.useAlpha=!0,t.alpha=.25,t.position.set(1,2,3),t},c=r();s.onParticleCreated(c);const l=c.target;t(l.position.toArray()).toEqual([1,2,3]),t(l.intensity).toBe(2),t(l.distance).toBe(2),t(l.castShadow).toBe(!1);const d=r();s.onParticleCreated(d),t(d.target).toBeUndefined(),t(o.children).toHaveLength(1),s.onParticleDead(c),t(o.children).toHaveLength(0);const p=r();s.onParticleCreated(p),t(p.target).toBe(l),p.scale=0,s.onParticleUpdate(p),t(l.visible).toBe(!1),t(l.distance).toBeGreaterThan(0),s.dispose(),t(o.children).toHaveLength(0)})});/*
2
2
  * Copyright (©) 2026 Hology Interactive AB. All rights reserved.
3
3
  * See the LICENSE.md file for details.
4
4
  */
@@ -1,5 +1,5 @@
1
1
  import * as THREE from 'three';
2
- import type { Object3D, SkinnedMesh } from 'three';
2
+ import { Object3D, SkinnedMesh } from 'three';
3
3
  export declare class NoRaycastMesh extends THREE.Mesh {
4
4
  raycast(...args: any[]): void;
5
5
  }
@@ -1,4 +1,4 @@
1
- import*as e from"three";import{clone as t}from"three/examples/jsm/utils/SkeletonUtils.js";import{mergeGeometries as n,mergeGroups as r}from"three/examples/jsm/utils/BufferGeometryUtils.js";export class NoRaycastMesh extends e.Mesh{raycast(...e){}}export function cloneMesh(e){const n=t(e),r=[],o=[];e.traverse(e=>r.push(e)),n.traverse(e=>o.push(e));const i=new Map,l=[];for(let e=0;e<r.length;e++){const t=r[e],n=o[e];if(!0!==t?.isSkinnedMesh||!0!==n?.isSkinnedMesh)continue;let a=i.get(t.skeleton);null==a&&(a=l.find(e=>s(t.skeleton,e.source))?.clone),null!=a?(i.set(t.skeleton,a),n.bind(a,n.bindMatrix)):(i.set(t.skeleton,n.skeleton),l.push({source:t.skeleton,clone:n.skeleton}))}return n}function s(e,t){if(e.bones.length!==t.bones.length||e.boneInverses.length!==t.boneInverses.length)return!1;for(let n=0;n<e.bones.length;n++){if(e.bones[n].name!==t.bones[n].name)return!1;const r=e.boneInverses[n].elements,s=t.boneInverses[n].elements;for(let e=0;e<16;e++)if(r[e]!==s[e])return!1}return!0}export function mergeCompatibleSkinnedMeshes(t,s=()=>!1){t.updateWorldMatrix(!0,!0);const l=t.matrixWorld.clone().invert(),a=[];t.traverse(e=>{const t=e;if(!0!==t.isSkinnedMesh||Array.isArray(t.material)||null==t.material||s(t))return;if(Object.keys(t.geometry.morphAttributes).length>0)return;const n=l.clone().multiply(t.matrixWorld),r=a.find(e=>{return e.skeleton===t.skeleton&&o(e.bindMatrix,t.bindMatrix)&&o(e.relativeMatrix,n)&&(r=e.material,s=t.material,r===s||r.name.length>0&&r.name===s.name)&&function(e,t){return e.visible===t.visible&&e.renderOrder===t.renderOrder&&e.frustumCulled===t.frustumCulled&&e.layers.mask===t.layers.mask}(e.meshes[0],t);var r,s});null==r?a.push({material:t.material,skeleton:t.skeleton,bindMatrix:t.bindMatrix.clone(),relativeMatrix:n,meshes:[t],geometries:[t.geometry]}):(r.meshes.push(t),r.geometries.push(t.geometry))});for(const s of a){if(s.meshes.length<2||!i(s.geometries))continue;const o=n(s.geometries,!1);if(null==o)continue;o.groups.length>1&&r(o);const l=new e.SkinnedMesh(o,s.material);l.copy(s.meshes[0],!1),l.geometry=o,l.material=s.material,s.relativeMatrix.decompose(l.position,l.quaternion,l.scale),l.matrix.copy(s.relativeMatrix),l.bind(s.skeleton,s.bindMatrix);for(const e of s.meshes)e.removeFromParent(),e.geometry.dispose();t.add(l)}}function o(e,t,n=1e-6){return e.elements.every((e,r)=>Math.abs(e-t.elements[r])<=n)}function i(e){const t=e[0],n=Object.keys(t.attributes).sort();return e.slice(1).every(e=>null==e.index==(null==t.index)&&Object.keys(e.attributes).sort().join("|")===n.join("|")&&n.every(n=>{const r=t.getAttribute(n),s=e.getAttribute(n);return r.itemSize===s.itemSize&&r.normalized===s.normalized&&r.array.constructor===s.array.constructor}))}/*
1
+ import*as e from"three";import{clone as t}from"three/examples/jsm/utils/SkeletonUtils.js";import{mergeGeometries as r,mergeGroups as n}from"three/examples/jsm/utils/BufferGeometryUtils.js";import{Matrix4 as s}from"three";export class NoRaycastMesh extends e.Mesh{raycast(...e){}}export function cloneMesh(e){const r=t(e),n=[],s=[];e.traverse(e=>n.push(e)),r.traverse(e=>s.push(e));const i=new Map,l=[];for(let e=0;e<n.length;e++){const t=n[e],r=s[e];if(!0!==t?.isSkinnedMesh||!0!==r?.isSkinnedMesh)continue;let a=i.get(t.skeleton);null==a&&(a=l.find(e=>o(t.skeleton,e.source))?.clone),null!=a?(i.set(t.skeleton,a),r.bind(a,r.bindMatrix)):(i.set(t.skeleton,r.skeleton),l.push({source:t.skeleton,clone:r.skeleton}))}return r}function o(e,t){if(e.bones.length!==t.bones.length||e.boneInverses.length!==t.boneInverses.length)return!1;for(let r=0;r<e.bones.length;r++){if(e.bones[r].name!==t.bones[r].name)return!1;const n=e.boneInverses[r].elements,s=t.boneInverses[r].elements;for(let e=0;e<16;e++)if(n[e]!==s[e])return!1}return!0}export function mergeCompatibleSkinnedMeshes(t,o=()=>!1){t.updateWorldMatrix(!0,!0);const a=t.matrixWorld.clone().invert(),m=[],u=new s;t.traverse(e=>{const t=e;if(!0!==t.isSkinnedMesh||Array.isArray(t.material)||null==t.material||o(t))return;if(!function(e){for(const t in e)if(Object.hasOwn(e,t))return!1;return!0}(t.geometry.morphAttributes))return;u.copy(a).multiply(t.matrixWorld);const r=m.find(e=>{return e.skeleton===t.skeleton&&i(e.bindMatrix,t.bindMatrix)&&i(e.relativeMatrix,u)&&(r=e.material,n=t.material,r===n||r.name.length>0&&r.name===n.name)&&function(e,t){return e.visible===t.visible&&e.renderOrder===t.renderOrder&&e.frustumCulled===t.frustumCulled&&e.layers.mask===t.layers.mask}(e.meshes[0],t);var r,n});null==r?m.push({material:t.material,skeleton:t.skeleton,bindMatrix:t.bindMatrix.clone(),relativeMatrix:u,meshes:[t],geometries:[t.geometry]}):(r.meshes.push(t),r.geometries.push(t.geometry))});for(const s of m){if(s.meshes.length<2||!l(s.geometries))continue;const o=r(s.geometries,!1);if(null==o)continue;o.groups.length>1&&n(o);const i=new e.SkinnedMesh(o,s.material);i.copy(s.meshes[0],!1),i.geometry=o,i.material=s.material,s.relativeMatrix.decompose(i.position,i.quaternion,i.scale),i.matrix.copy(s.relativeMatrix),i.bind(s.skeleton,s.bindMatrix);for(const e of s.meshes)e.removeFromParent(),e.geometry.dispose();t.add(i)}}function i(e,t,r=1e-6){const n=e.elements,s=t.elements;for(let e=0;e<16;e++)if(Math.abs(n[e]-s[e])>r)return!1;return!0}function l(e){const t=e[0],r=Object.keys(t.attributes).sort();return e.slice(1).every(e=>null==e.index==(null==t.index)&&Object.keys(e.attributes).sort().join("|")===r.join("|")&&r.every(r=>{const n=t.getAttribute(r),s=e.getAttribute(r);return n.itemSize===s.itemSize&&n.normalized===s.normalized&&n.array.constructor===s.array.constructor}))}/*
2
2
  * Copyright (©) 2026 Hology Interactive AB. All rights reserved.
3
3
  * See the LICENSE.md file for details.
4
4
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hology/core",
3
- "version": "0.0.272",
3
+ "version": "0.0.273",
4
4
  "description": "",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",