载入中...
搜索中...
未找到
DynamicBoneSolver.cpp
浏览该文件的文档.
2
5
6#include <algorithm>
7#include <cmath>
8
9namespace eve::animation {
10namespace {
11constexpr float kEpsilon = 1e-6f;
12float clamp01(float value) { return std::clamp(value, 0.f, 1.f); }
13float length(float x, float y, float z) { return std::sqrt(x * x + y * y + z * z); }
14struct Quat { float x = 0.f, y = 0.f, z = 0.f, w = 1.f; };
15Quat multiply(const Quat &a, const Quat &b) {
16 return {a.w*b.x+a.x*b.w+a.y*b.z-a.z*b.y, a.w*b.y-a.x*b.z+a.y*b.w+a.z*b.x,
17 a.w*b.z+a.x*b.y-a.y*b.x+a.z*b.w, a.w*b.w-a.x*b.x-a.y*b.y-a.z*b.z};
18}
19Quat inverse(const Quat &q) { return {-q.x, -q.y, -q.z, q.w}; }
20Quat normalized(Quat q) {
21 const float n = std::sqrt(q.x*q.x + q.y*q.y + q.z*q.z + q.w*q.w);
22 if (n <= kEpsilon) return {};
23 q.x/=n; q.y/=n; q.z/=n; q.w/=n; return q;
24}
25Quat fromTo(float ax, float ay, float az, float bx, float by, float bz) {
26 const float al=length(ax,ay,az), bl=length(bx,by,bz);
27 if (al<=kEpsilon || bl<=kEpsilon) return {};
28 ax/=al; ay/=al; az/=al; bx/=bl; by/=bl; bz/=bl;
29 const float dot=std::clamp(ax*bx+ay*by+az*bz,-1.f,1.f);
30 if (dot < -0.9999f) {
31 const float ox=std::fabs(ax)<0.8f?1.f:0.f, oy=std::fabs(ax)<0.8f?0.f:1.f;
32 return normalized({-az*oy, az*ox, ax*oy-ay*ox, 0.f});
33 }
34 return normalized({ay*bz-az*by, az*bx-ax*bz, ax*by-ay*bx, 1.f+dot});
35}
36Quat blend(Quat a, Quat b, float weight) {
37 if (a.x*b.x+a.y*b.y+a.z*b.z+a.w*b.w < 0.f) { b.x=-b.x; b.y=-b.y; b.z=-b.z; b.w=-b.w; }
38 const float t=clamp01(weight);
39 return normalized({a.x+(b.x-a.x)*t,a.y+(b.y-a.y)*t,a.z+(b.z-a.z)*t,a.w+(b.w-a.w)*t});
40}
41void rotate(const Quat &q, float x, float y, float z, float &ox, float &oy, float &oz) {
42 const Quat r=multiply(multiply(q,{x,y,z,0.f}),inverse(q)); ox=r.x; oy=r.y; oz=r.z;
43}
44} // namespace
45
46DynamicBoneSolver::DynamicBoneSolver(AnimSkeleton *skeleton) : skeleton_(skeleton) {}
48
50 if (skeleton_ == skeleton) return;
51 skeleton_ = skeleton; clearChains(); clearColliders();
52}
53
54std::vector<int> DynamicBoneSolver::buildChainByIndexes(int rootBone, int endBone) const {
55 std::vector<int> result;
56 if (!skeleton_ || rootBone<0 || endBone<0 || rootBone>=skeleton_->getBoneCount() || endBone>=skeleton_->getBoneCount()) return result;
57 for (int bone=endBone; bone>=0; bone=skeleton_->getParent(bone)) { result.push_back(bone); if (bone==rootBone) break; }
58 if (result.empty() || result.back()!=rootBone) return {};
59 std::reverse(result.begin(),result.end()); return result;
60}
61
62bool DynamicBoneSolver::validateChain(const std::vector<int> &bones) const {
63 if (!skeleton_ || bones.size()<2) return false;
64 for (size_t i=0;i<bones.size();++i) {
65 if (bones[i]<0 || bones[i]>=skeleton_->getBoneCount()) return false;
66 if (i>0 && skeleton_->getParent(bones[i])!=bones[i-1]) return false;
67 }
68 return true;
69}
70
71int DynamicBoneSolver::addChain(int rootBone,int endBone,float stiffness,float damping,float inertia,float gravityScale,float radius,int iterations) {
72 auto bones=buildChainByIndexes(rootBone,endBone);
73 if (!validateChain(bones) || iterations<=0) return -1;
74 Chain c; c.bones=std::move(bones); c.stiffness=clamp01(stiffness); c.damping=clamp01(damping);
75 c.inertia=clamp01(inertia); c.gravityScale=gravityScale; c.radius=std::max(radius,0.f); c.iterations=iterations;
76 chains_.push_back(std::move(c)); return static_cast<int>(chains_.size())-1;
77}
78
79int DynamicBoneSolver::addChainByName(const std::string &root,const std::string &end,float stiffness,float damping,float inertia,float gravityScale,float radius,int iterations) {
80 if (!skeleton_) return -1;
81 return addChain(skeleton_->findBone(root),skeleton_->findBone(end),stiffness,damping,inertia,gravityScale,radius,iterations);
82}
83int DynamicBoneSolver::getChainCount() const { return static_cast<int>(chains_.size()); }
84void DynamicBoneSolver::clearChains() { chains_.clear(); }
85void DynamicBoneSolver::setChainEnabled(int index,bool enabled) { if (index>=0 && index<static_cast<int>(chains_.size())) chains_[static_cast<size_t>(index)].enabled=enabled; }
86bool DynamicBoneSolver::isChainEnabled(int index) const { return index>=0 && index<static_cast<int>(chains_.size()) && chains_[static_cast<size_t>(index)].enabled; }
87void DynamicBoneSolver::setChainFreezeAxis(int index,int axis) { if (index>=0 && index<static_cast<int>(chains_.size())) chains_[static_cast<size_t>(index)].freezeAxis=axis>=1 && axis<=3?axis:0; }
88void DynamicBoneSolver::setChainSelfCollision(int index,bool enabled) { if(index>=0&&index<static_cast<int>(chains_.size()))chains_[static_cast<size_t>(index)].selfCollision=enabled; }
89void DynamicBoneSolver::setChainParticleParameters(int index,int particle,float stiffness,float damping,float inertia,float gravityScale,float radius) {
90 if (index<0 || index>=static_cast<int>(chains_.size())) return;
91 auto &chain=chains_[static_cast<size_t>(index)];
92 const size_t count=chain.bones.size()+(chain.endMode==0?0U:1U);
93 if (particle<0 || static_cast<size_t>(particle)>=count) return;
94 if (chain.particleParameters.size()!=count) chain.particleParameters.assign(count,{chain.stiffness,chain.damping,chain.inertia,chain.gravityScale,chain.radius});
95 chain.particleParameters[static_cast<size_t>(particle)]={clamp01(stiffness),clamp01(damping),clamp01(inertia),gravityScale,std::max(radius,0.f)};
96}
97void DynamicBoneSolver::setChainEndLength(int index,float value) { if (index>=0 && index<static_cast<int>(chains_.size())) { auto &chain=chains_[static_cast<size_t>(index)]; chain.endLength=std::max(value,0.f); chain.endMode=chain.endLength>0.f?1:0; chain.particleParameters.clear(); chain.initialized=false; } }
98void DynamicBoneSolver::setChainEndOffset(int index,float x,float y,float z) { if (index>=0 && index<static_cast<int>(chains_.size())) { auto &chain=chains_[static_cast<size_t>(index)]; chain.endOffset={x,y,z}; chain.endMode=length(x,y,z)>kEpsilon?2:0; chain.particleParameters.clear(); chain.initialized=false; } }
99void DynamicBoneSolver::clearChainEnd(int index) { if (index>=0 && index<static_cast<int>(chains_.size())) { auto &chain=chains_[static_cast<size_t>(index)]; chain.endMode=0; chain.particleParameters.clear(); chain.initialized=false; } }
100void DynamicBoneSolver::setGlobalGravity(float x,float y,float z) { globalGravityX_=x; globalGravityY_=y; globalGravityZ_=z; }
101void DynamicBoneSolver::setExternalForce(float x,float y,float z) { externalForceX_=x; externalForceY_=y; externalForceZ_=z; }
102void DynamicBoneSolver::setWeight(float value) { weight_=clamp01(value); }
103void DynamicBoneSolver::setUpdateRate(float value) { updateRate_=std::max(value,0.f); }
104void DynamicBoneSolver::setTeleportThreshold(float value) { teleportThreshold_=std::max(value,0.f); }
105void DynamicBoneSolver::setObjectMoveResponse(float value) { objectMoveResponse_=clamp01(value); }
106void DynamicBoneSolver::setDistanceReference(float x,float y,float z) { distanceReference_={x,y,z}; }
107void DynamicBoneSolver::setDistanceLimit(float value) { distanceLimit_=std::max(value,0.f); }
108bool DynamicBoneSolver::isChainSleeping(int index) const { return index>=0 && index<static_cast<int>(chains_.size()) && chains_[static_cast<size_t>(index)].sleeping; }
109void DynamicBoneSolver::addColliderSphere(float x,float y,float z,float radius) { if (radius>0.f) colliders_.push_back({-1,{},{},{x,y,z},{x,y,z},radius,false,true,false}); }
110void DynamicBoneSolver::addBoneColliderSphere(int bone,float x,float y,float z,float radius) { if (skeleton_ && bone>=0 && bone<skeleton_->getBoneCount() && radius>0.f) colliders_.push_back({bone,{x,y,z},{x,y,z},{},{},radius,false,true,false}); }
111void DynamicBoneSolver::addColliderCapsule(float sx,float sy,float sz,float ex,float ey,float ez,float radius) { if (radius>0.f) colliders_.push_back({-1,{},{},{sx,sy,sz},{ex,ey,ez},radius,true,true,false}); }
112void DynamicBoneSolver::addBoneColliderCapsule(int bone,float sx,float sy,float sz,float ex,float ey,float ez,float radius) { if (skeleton_ && bone>=0 && bone<skeleton_->getBoneCount() && radius>0.f) colliders_.push_back({bone,{sx,sy,sz},{ex,ey,ez},{},{},radius,true,true,false}); }
113void DynamicBoneSolver::clearColliders() { colliders_.clear(); }
114int DynamicBoneSolver::getColliderCount() const { return static_cast<int>(colliders_.size()); }
115void DynamicBoneSolver::removeCollider(int index) { if (index>=0 && index<static_cast<int>(colliders_.size())) colliders_.erase(colliders_.begin()+index); }
116void DynamicBoneSolver::setColliderEnabled(int index,bool enabled) { if (index>=0 && index<static_cast<int>(colliders_.size())) colliders_[static_cast<size_t>(index)].enabled=enabled; }
117void DynamicBoneSolver::setColliderInside(int index,bool inside) { if (index>=0 && index<static_cast<int>(colliders_.size())) colliders_[static_cast<size_t>(index)].inside=inside; }
118void DynamicBoneSolver::setColliderRadius(int index,float radius) { if (index>=0 && index<static_cast<int>(colliders_.size()) && radius>0.f) colliders_[static_cast<size_t>(index)].radius=radius; }
119void DynamicBoneSolver::reset() { for (auto &chain:chains_) chain.initialized=false; }
120
121void DynamicBoneSolver::captureTargets(Chain &chain,const AnimPose &pose) {
122 chain.target.resize(chain.bones.size());
123 for (size_t i=0;i<chain.bones.size();++i) { const auto &w=pose.world(chain.bones[i]); chain.target[i]={w.px,w.py,w.pz}; }
124 if (chain.endMode==1) {
125 const Vec3 a=chain.target[chain.target.size()-2],b=chain.target.back();
126 const float dx=b.x-a.x,dy=b.y-a.y,dz=b.z-a.z,d=length(dx,dy,dz);
127 if (d>kEpsilon) chain.target.push_back({b.x+dx*chain.endLength/d,b.y+dy*chain.endLength/d,b.z+dz*chain.endLength/d});
128 } else if (chain.endMode==2) {
129 const auto &w=pose.world(chain.bones.back()); Vec3 end;
130 rotate({w.qx,w.qy,w.qz,w.qw},chain.endOffset.x*w.sx,chain.endOffset.y*w.sy,chain.endOffset.z*w.sz,end.x,end.y,end.z);
131 chain.target.push_back({w.px+end.x,w.py+end.y,w.pz+end.z});
132 }
133 if (chain.particleParameters.size()!=chain.target.size()) chain.particleParameters.assign(chain.target.size(),{chain.stiffness,chain.damping,chain.inertia,chain.gravityScale,chain.radius});
134}
135void DynamicBoneSolver::initializeChain(Chain &chain) {
136 chain.current=chain.target;
137 chain.previous=chain.target;
138 chain.restLength.resize(chain.target.size()-1);
139 for (size_t i=1;i<chain.target.size();++i) chain.restLength[i-1]=length(chain.target[i].x-chain.target[i-1].x,chain.target[i].y-chain.target[i-1].y,chain.target[i].z-chain.target[i-1].z);
140 chain.initialized=true;
141}
142bool DynamicBoneSolver::resolveCollider(const Collider &c,float particleRadius,Vec3 &p) const {
143 if (!c.enabled) return false;
144 Vec3 center=c.center;
145 if (c.capsule) {
146 const float sx=c.end.x-c.center.x,sy=c.end.y-c.center.y,sz=c.end.z-c.center.z;
147 const float segmentLengthSquared=sx*sx+sy*sy+sz*sz;
148 if (segmentLengthSquared>kEpsilon) {
149 const float t=std::clamp(((p.x-c.center.x)*sx+(p.y-c.center.y)*sy+(p.z-c.center.z)*sz)/segmentLengthSquared,0.f,1.f);
150 center={c.center.x+sx*t,c.center.y+sy*t,c.center.z+sz*t};
151 }
152 }
153 const float dx=p.x-center.x,dy=p.y-center.y,dz=p.z-center.z,d=length(dx,dy,dz);
154 const float limit=c.inside?std::max(c.radius-particleRadius,0.f):c.radius+particleRadius;
155 if ((!c.inside && d>=limit) || (c.inside && d<=limit)) return false;
156 if (d<=kEpsilon) { p.y=center.y+limit; return true; }
157 const float s=limit/d; p={center.x+dx*s,center.y+dy*s,center.z+dz*s}; return true;
158}
159void DynamicBoneSolver::simulateChain(Chain &chain,float dt) {
160 chain.current[0]=chain.target[0]; chain.previous[0]=chain.target[0];
161 const float dt2=dt*dt;
162 for (size_t i=1;i<chain.current.size();++i) {
163 const auto &parameters=chain.particleParameters[i];
164 const float retention=(1.f-parameters.damping)*parameters.inertia;
165 float forceX=externalForceX_,forceY=externalForceY_,forceZ=externalForceZ_;
166 if (forceField_) { float x=0.f,y=0.f,z=0.f; forceField_->sampleForce(chain.current[i].x,chain.current[i].y,chain.current[i].z,simulationTime_,x,y,z); forceX+=x; forceY+=y; forceZ+=z; }
167 const Vec3 old=chain.current[i];
168 chain.current[i].x+=(chain.current[i].x-chain.previous[i].x)*retention+(globalGravityX_+forceX)*parameters.gravityScale*dt2;
169 chain.current[i].y+=(chain.current[i].y-chain.previous[i].y)*retention+(globalGravityY_+forceY)*parameters.gravityScale*dt2;
170 chain.current[i].z+=(chain.current[i].z-chain.previous[i].z)*retention+(globalGravityZ_+forceZ)*parameters.gravityScale*dt2;
171 chain.current[i].x+=(chain.target[i].x-chain.current[i].x)*parameters.stiffness;
172 chain.current[i].y+=(chain.target[i].y-chain.current[i].y)*parameters.stiffness;
173 chain.current[i].z+=(chain.target[i].z-chain.current[i].z)*parameters.stiffness;
174 chain.previous[i]=old;
175 }
176 for (int pass=0;pass<chain.iterations;++pass) {
177 chain.current[0]=chain.target[0];
178 if(chain.selfCollision)for(size_t i=1;i<chain.current.size();++i)for(size_t j=i+2;j<chain.current.size();++j){++lastUpdateStats_.selfCollisionTests;Vec3& a=chain.current[i];Vec3& b=chain.current[j];const float dx=b.x-a.x,dy=b.y-a.y,dz=b.z-a.z,d=length(dx,dy,dz);const float minimum=chain.particleParameters[i].radius+chain.particleParameters[j].radius;if(d<minimum&&minimum>0.f){const float nx=d>kEpsilon?dx/d:1.f,ny=d>kEpsilon?dy/d:0.f,nz=d>kEpsilon?dz/d:0.f,correction=(minimum-d)*0.5f;a.x-=nx*correction;a.y-=ny*correction;a.z-=nz*correction;b.x+=nx*correction;b.y+=ny*correction;b.z+=nz*correction;}}
179 for (size_t i=1;i<chain.current.size();++i) {
180 Vec3 &a=chain.current[i-1],&b=chain.current[i];
181 const float dx=b.x-a.x,dy=b.y-a.y,dz=b.z-a.z,d=length(dx,dy,dz);
182 if (d>kEpsilon) { const float s=chain.restLength[i-1]/d; b={a.x+dx*s,a.y+dy*s,a.z+dz*s}; }
183 for (const auto &collider:colliders_) { ++lastUpdateStats_.colliderTests; resolveCollider(collider,chain.particleParameters[i].radius,b); }
184 constrainSegment(chain,i);
185 }
186 }
187}
188void DynamicBoneSolver::constrainSegment(Chain &chain,size_t i) const {
189 Vec3 &a=chain.current[i-1],&b=chain.current[i];
190 if (chain.freezeAxis==0) {
191 const float dx=b.x-a.x,dy=b.y-a.y,dz=b.z-a.z,d=length(dx,dy,dz);
192 if (d>kEpsilon) { const float s=chain.restLength[i-1]/d; b={a.x+dx*s,a.y+dy*s,a.z+dz*s}; }
193 return;
194 }
195 float *coordinates[3]={&b.x,&b.y,&b.z};
196 const float targetCoordinates[3]={chain.target[i].x,chain.target[i].y,chain.target[i].z};
197 const float parentCoordinates[3]={a.x,a.y,a.z};
198 const int frozen=chain.freezeAxis-1;
199 *coordinates[frozen]=targetCoordinates[frozen];
200 const int first=(frozen+1)%3,second=(frozen+2)%3;
201 float d1=*coordinates[first]-parentCoordinates[first],d2=*coordinates[second]-parentCoordinates[second];
202 const float frozenDelta=targetCoordinates[frozen]-parentCoordinates[frozen];
203 const float freeLength=std::sqrt(std::max(chain.restLength[i-1]*chain.restLength[i-1]-frozenDelta*frozenDelta,0.f));
204 float freeDistance=length(d1,d2,0.f);
205 if (freeDistance<=kEpsilon) { d1=targetCoordinates[first]-parentCoordinates[first]; d2=targetCoordinates[second]-parentCoordinates[second]; freeDistance=length(d1,d2,0.f); }
206 if (freeDistance>kEpsilon) { *coordinates[first]=parentCoordinates[first]+d1*freeLength/freeDistance; *coordinates[second]=parentCoordinates[second]+d2*freeLength/freeDistance; }
207}
208void DynamicBoneSolver::updateColliderCenters(const AnimPose &pose) {
209 for (auto &c:colliders_) {
210 if (c.bone<0) continue;
211 const auto &w=pose.world(c.bone);
212 rotate({w.qx,w.qy,w.qz,w.qw},c.offset.x*w.sx,c.offset.y*w.sy,c.offset.z*w.sz,c.center.x,c.center.y,c.center.z);
213 c.center.x+=w.px; c.center.y+=w.py; c.center.z+=w.pz;
214 rotate({w.qx,w.qy,w.qz,w.qw},c.endOffset.x*w.sx,c.endOffset.y*w.sy,c.endOffset.z*w.sz,c.end.x,c.end.y,c.end.z);
215 c.end.x+=w.px; c.end.y+=w.py; c.end.z+=w.pz;
216 }
217}
218void DynamicBoneSolver::writeChainRotations(const Chain &chain,AnimPose &pose) const {
219 const size_t rotationCount=std::min(chain.bones.size(),chain.current.size()-1);
220 for (size_t i=0;i<rotationCount;++i) {
221 const int bone=chain.bones[i]; pose.computeWorld(skeleton_);
222 const auto &w=pose.world(bone);
223 float animatedX,animatedY,animatedZ;
224 if (i+1<chain.bones.size()) { const auto &cw=pose.world(chain.bones[i+1]); animatedX=cw.px-w.px; animatedY=cw.py-w.py; animatedZ=cw.pz-w.pz; }
225 else { animatedX=chain.target[i+1].x-chain.target[i].x; animatedY=chain.target[i+1].y-chain.target[i].y; animatedZ=chain.target[i+1].z-chain.target[i].z; }
226 const Quat delta=fromTo(animatedX,animatedY,animatedZ,chain.current[i+1].x-chain.current[i].x,chain.current[i+1].y-chain.current[i].y,chain.current[i+1].z-chain.current[i].z);
227 const Quat desiredWorld=multiply(delta,{w.qx,w.qy,w.qz,w.qw});
228 Quat desiredLocal=desiredWorld;
229 const int parent=skeleton_->getParent(bone);
230 if (parent>=0) { const auto &pw=pose.world(parent); desiredLocal=multiply(inverse({pw.qx,pw.qy,pw.qz,pw.qw}),desiredWorld); }
231 const auto &local=pose.local(bone); const Quat result=blend({local.qx,local.qy,local.qz,local.qw},desiredLocal,weight_);
232 pose.setLocalRotation(bone,result.x,result.y,result.z,result.w);
233 }
234}
235void DynamicBoneSolver::update(AnimPose *pose,float dt) {
236 lastUpdateStats_={};
237 if (!skeleton_ || !pose || !std::isfinite(dt) || dt<=0.f || weight_<=0.f || skeleton_->getBoneCount()!=pose->getBoneCount()) return;
238 pose->computeWorld(skeleton_); updateColliderCenters(*pose);
239 const float preferred=updateRate_>0.f?1.f/updateRate_:dt;
240 const int steps=std::clamp(static_cast<int>(std::ceil(dt/preferred)),1,8);
241 const float stepDt=std::min(dt/static_cast<float>(steps),preferred);
242 for (auto &chain:chains_) {
243 if (!chain.enabled || chain.bones.size()<2) continue;
244 captureTargets(chain,*pose);
245 const float rootDistance=length(chain.target[0].x-distanceReference_.x,chain.target[0].y-distanceReference_.y,chain.target[0].z-distanceReference_.z);
246 if (distanceLimit_>0.f && rootDistance>distanceLimit_) { chain.sleeping=true; chain.initialized=false; ++lastUpdateStats_.sleepingChains; continue; }
247 chain.sleeping=false;
248 ++lastUpdateStats_.activeChains;lastUpdateStats_.particles+=static_cast<int>(chain.target.size());lastUpdateStats_.substeps+=steps;
249 if (chain.initialized) {
250 const float moveX=chain.target[0].x-chain.current[0].x,moveY=chain.target[0].y-chain.current[0].y,moveZ=chain.target[0].z-chain.current[0].z;
251 if (teleportThreshold_>0.f && length(moveX,moveY,moveZ)>teleportThreshold_) chain.initialized=false;
252 else if (objectMoveResponse_>0.f) for (size_t i=0;i<chain.current.size();++i) {
253 chain.current[i].x+=moveX*objectMoveResponse_; chain.current[i].y+=moveY*objectMoveResponse_; chain.current[i].z+=moveZ*objectMoveResponse_;
254 chain.previous[i].x+=moveX*objectMoveResponse_; chain.previous[i].y+=moveY*objectMoveResponse_; chain.previous[i].z+=moveZ*objectMoveResponse_;
255 }
256 }
257 if (!chain.initialized) initializeChain(chain);
258 for (int step=0;step<steps;++step) simulateChain(chain,stepDt);
259 writeChainRotations(chain,*pose);
260 }
261 pose->computeWorld(skeleton_);
262 simulationTime_+=dt;
263}
264DynamicBoneDebugSnapshot DynamicBoneSolver::debugSnapshot() const {
266 for(size_t chainIndex=0;chainIndex<chains_.size();++chainIndex){const auto& chain=chains_[chainIndex];for(size_t particle=0;particle<chain.current.size();++particle){const auto& point=chain.current[particle];const float radius=particle<chain.particleParameters.size()?chain.particleParameters[particle].radius:chain.radius;result.particles.push_back({static_cast<int>(chainIndex),static_cast<int>(particle),point.x,point.y,point.z,radius,chain.sleeping});}}
267 for(const auto& collider:colliders_)result.colliders.push_back({collider.center.x,collider.center.y,collider.center.z,collider.end.x,collider.end.y,collider.end.z,collider.radius,collider.capsule,collider.inside,collider.enabled});
268 return result;
269}
270
271} // namespace eve::animation
double value
float w
Definition AnimClip.cpp:738
float y
Definition AnimClip.cpp:738
float x
Definition AnimClip.cpp:738
float z
Definition AnimClip.cpp:738
int root
Definition AnimSmr.cpp:119
eve::EntitySpatialPose pose
const std::string & s
int bz
Definition CaveMesh.cpp:114
int ax
Definition CaveMesh.cpp:113
int ay
Definition CaveMesh.cpp:113
int bx
Definition CaveMesh.cpp:114
float length
Definition CaveMesh.cpp:94
int az
Definition CaveMesh.cpp:113
int by
Definition CaveMesh.cpp:114
float nx
float nz
float ny
glm::vec4 p[6]
float minimum[3]
glm::vec3 n
Definition Grass.cpp:63
std::array< double, 10 > q
double r
std::int32_t second
std::int32_t c
std::int32_t first
float blend
std::string local
std::vector< Bone > bones
std::uint32_t bone
std::int32_t parent
MeleePoint3 b
Definition MeleeHit.cpp:41
MeleePoint3 a
Definition MeleeHit.cpp:40
float radius
float d
int steps
float t
float dz
float dy
float dx
std::uint32_t count
int limit
Definition TreeMesh.cpp:164
float inertia
Definition TreeMesh.cpp:309
int iterations
Definition TreeMesh.cpp:311
float step
Definition TreeMesh.cpp:314
uint32_t index
double oy
std::vector< char > inside
double ox
glm::vec3 point
Evaluated local (and optional world) pose for an AnimSkeleton. Script type: AnimPose.
Definition AnimPose.h:17
3D bone hierarchy + bind-pose local TRS for skeletal animation. Independent of ik::Skeleton3D (FABRIK...
int getBoneCount() const
Returns the bone count.
int getParent(int boneIndex) const
Returns the parent.
int findBone(const std::string &name) const
Finds bone.
void setChainParticleParameters(int chainIndex, int particleIndex, float stiffness, float damping, float inertia, float gravityScale, float radius)
Override simulation parameters for one chain particle, where zero is the root.
int addChain(int rootBone, int endBone, float stiffness, float damping, float inertia, float gravityScale=1.f, float radius=0.f, int iterations=4)
Add an ancestor-to-descendant spring chain.
DynamicBoneSolver(AnimSkeleton *skeleton)
Construct for a borrowed skeleton, which may be null.
void clearColliders()
Remove all colliders.
~DynamicBoneSolver()
Dynamic bone solver.
bool isChainEnabled(int chainIndex) const
Return enabled state, or false for an invalid index.
void setChainSelfCollision(int chainIndex, bool enabled)
Enable or disable non-adjacent particle self-collision for a chain.
void setChainEnabled(int chainIndex, bool enabled)
Set a valid chain's enabled state; invalid indices are ignored.
int getChainCount() const
Return configured chain count.
void clearChains()
Remove all chains and their simulation state.
void setSkeleton(AnimSkeleton *skeleton)
Rebind the borrowed skeleton; changing it clears index-dependent state.
int addChainByName(const std::string &rootBone, const std::string &endBone, float stiffness, float damping, float inertia, float gravityScale=1.f, float radius=0.f, int iterations=4)
Name-based addChain variant; returns -1 for invalid input.
void setChainFreezeAxis(int chainIndex, int axis)
Restrict a chain to its animated model-space plane.
double dot(const Vec2 &a, const Vec2 &b)
Dot.
Definition UrbanTypes.h:38
bool enabled
Owning renderer-neutral snapshot safe after the solver changes.
std::vector< DynamicBoneDebugParticle > particles
std::vector< DynamicBoneDebugCollider > colliders