/******************************************************************************/ #include "stdafx.h" namespace EE{ /****************************************************************************** TODO: Ragdoll actor shape creation should be verified across many models, if no universal code is found, Ragdoll Editor should be added. /******************************************************************************/ static Shape ShapeBone(C Vec &from, C Vec &to, Flt width) { Shape shape; if(width>=0.5f) { shape.type=SHAPE_BALL; Ball &ball=shape.ball; ball.pos=Avg(from, to); ball.r =width*(to-from).length(); }else { shape.type=SHAPE_CAPSULE; Capsule &capsule=shape.capsule; capsule.pos=Avg(from, to); capsule.up = to-from; capsule.h =capsule.up.normalize(); capsule.r =Max(0.01f, width*capsule.h); Flt eps=capsule.r*0.5f; capsule.pos-=eps*capsule.up; capsule.h +=eps*2; } return shape; } inline static Shape ShapeBone(C SkelBone &bone) {return ShapeBone(bone.pos, bone.to(), bone.width);} // return shape from bone /******************************************************************************/ void Ragdoll::zero() { _scale=0; _skel =null; } Ragdoll::Ragdoll() {zero();} Ragdoll& Ragdoll::del() { _joints.del(); _bones .del(); _resets.del(); _aggr .del(); // delete aggregate after actors, so they won't be re-inserted into scene when aggregate is deleted zero(); return T; } Bool Ragdoll::createTry(C AnimatedSkeleton &anim_skel, Flt density, Bool kinematic) { del(); if(T._skel=anim_skel.skeleton()) { T._scale=anim_skel.scale(); Memt shapes; C Skeleton &skel=*anim_skel.skeleton(); Int body=-1; FREPA(skel.bones) // order is important, main bones should be added at start, in case the skeleton doesn't have "body" bone, and as main bone (zero) should be set some different { C SkelBone &sbon=skel.bones[i]; if(sbon.flag&BONE_RAGDOLL) { Vec from =sbon.pos, to =sbon.to(); Flt width=sbon.width; if(sbon.type==BONE_FOOT) { C SkelBone *b=skel.findBone(BONE_TOE, sbon.type_index); if(b){from=Avg(sbon.pos, sbon.to()); to=b->to(); width=Avg(width, b->width)*0.5f; Vec down=sbon.dir*(width*Dist(from, to)*0.5f); from-=down; to-=down;}else width*=0.8f; }else if(sbon.type==BONE_HAND) { C SkelBone *b=skel.findBone(BONE_FINGER, (sbon.type_index>=0) ? 2 : -3); // find middle finger (2 for right, -3 for left hand) if(b){to=b->to(); width*=0.6f;} }else if(sbon.type==BONE_SPINE && sbon.type_sub==0) { body=_bones.elms(); _resets.add(i); // add main bone for resetting } Shape &s =shapes.New(); s=ShapeBone(from, to, width); Bone &rb=_bones.New(); Set(rb.name, sbon.name); rb.skel_bone =i; rb.rbon_parent=0xFF; if(!rb.actor.createTry(s*T._scale, density, &VecZero, kinematic))return false; }else { _resets.add(i); } } // force 'body' bone to have index=0 if(body>0) { Swap(_bones[0], _bones[body]); Swap(shapes[0], shapes[body]); } // set parents, damping and solver iterations REPA(T) { // find first parent which has an actor Bone &rb=bone(i); C SkelBone &sb=skel.bones[rb.skel_bone]; if(i) // skip the main bone { Byte skel_bone_parent =sb.parent; if( skel_bone_parent!=0xFF) // if has a parent { Int rbone =findBoneIndexFromSkelBone(skel_bone_parent); // find ragdoll bone assigned to skeleton parent bone if( rbone>=0)rb.rbon_parent=rbone; // if exists, then set as ragdoll parent } } if(sb.type==BONE_HEAD)rb.actor.adamping(7); else rb.actor.adamping(4); rb.actor. damping(0.5f).sleepEnergy(0.1f); } if(!kinematic) { // joints REPA(_bones) if(i) // skip the main bone { Bone &rb= bone (i); C SkelBone &sb=skel.bones[rb.skel_bone]; Byte rbon_parent=((rb.rbon_parent==0xFF) ? 0 : rb.rbon_parent); // if doesn't have a parent then use the main bone //if(rbon_parent!=0xFF) { Bone &rp= _bones[ rbon_parent]; C SkelBone &sp=skel.bones[rp.skel_bone ]; if(sb.type==BONE_HEAD) { if(sp.type==BONE_NECK)_joints.New().createBodySpherical(rb.actor, rp.actor, sb.pos*_scale, sb.dir, DegToRad(30), DegToRad(35)); else _joints.New().createBodySpherical(rb.actor, rp.actor, sb.pos*_scale, sb.dir, DegToRad(50), DegToRad(40)); }else if(sb.type==BONE_NECK )_joints.New().createBodySpherical(rb.actor, rp.actor, sb.pos*_scale, sb.dir, DegToRad( 20), DegToRad( 5));else if(sb.type==BONE_SHOULDER )_joints.New().createBodySpherical(rb.actor, rp.actor, sb.pos*_scale, sb.dir, DegToRad( 5), DegToRad( 5));else if(sb.type==BONE_UPPER_ARM )_joints.New().createBodySpherical(rb.actor, rp.actor, sb.pos*_scale, sb.dir, DegToRad( 80), DegToRad(30));else if(sb.type==BONE_UPPER_LEG )_joints.New().createBodySpherical(rb.actor, rp.actor, sb.pos*_scale, sb.dir, DegToRad(155), DegToRad(25));else if(sb.type==BONE_SPINE )_joints.New().createBodySpherical(rb.actor, rp.actor, sb.pos*_scale, sb.dir, DegToRad( 40), DegToRad(30));else if(sb.type==BONE_FOOT )_joints.New().createBodyHinge (rb.actor, rp.actor, sb.pos*_scale, Vec(1, 0,0), -DegToRad(45), DegToRad( 45));else if(sb.type==BONE_HAND && sb.type_index< 0)_joints.New().createBodyHinge (rb.actor, rp.actor, sb.pos*_scale, Vec(0, 1,0), -DegToRad(80), DegToRad( 80));else if(sb.type==BONE_HAND && sb.type_index>=0)_joints.New().createBodyHinge (rb.actor, rp.actor, sb.pos*_scale, Vec(0,-1,0), -DegToRad(80), DegToRad( 80));else if(sb.type==BONE_LOWER_ARM && sb.type_index< 0)_joints.New().createBodyHinge (rb.actor, rp.actor, sb.pos*_scale, Vec(0, 1,0), 0 , DegToRad(140));else if(sb.type==BONE_LOWER_ARM && sb.type_index>=0)_joints.New().createBodyHinge (rb.actor, rp.actor, sb.pos*_scale, Vec(0,-1,0), 0 , DegToRad(140));else if(sb.type==BONE_LOWER_LEG )_joints.New().createBodyHinge (rb.actor, rp.actor, sb.pos*_scale, Vec(1, 0,0), 0 , DegToRad(150));else _joints.New().createBodySpherical(rb.actor, rp.actor, sb.pos*_scale, sb.dir, DegToRad(40), DegToRad(30)); } } // ignore REPA(T) REPD(j, i) if(Cuts(shapes[i], shapes[j])) bone(i).actor.ignore(bone(j).actor); } _aggr.create(bones()); REPA(T)_aggr.add(bone(i).actor); return true; } return false; } /******************************************************************************/ Ragdoll& Ragdoll::create(C AnimatedSkeleton &anim_skel, Flt density, Bool kinematic) { if(!createTry(anim_skel, density, kinematic))Exit("Can't create Ragdoll"); return T; } /****************************************************************************** void updateShapes(AnimatedSkeleton &anim_skel); // update 'shape_t' according to 'shape' and skeleton animation, 'anim_skel' must be set to the same skeleton which ragdoll was created from void Ragdoll::updateShape(AnimatedSkeleton &anim_skel) { if(T.skel==anim_skel.skeleton())REP(bones)shape_t[i]=shape[i]*anim_skel.bone(bone[i].skel_bone).matrix; } /******************************************************************************/ Ragdoll& Ragdoll::fromSkel(C AnimatedSkeleton &anim_skel, C Vec &vel, Bool immediate_even_for_kinematic_ragdoll) { if(_skel==anim_skel.skeleton()) { Bool scaled =(_scale!=1), kinematic=(immediate_even_for_kinematic_ragdoll ? false : T.kinematic()); REPA(T) { Bone &rbon =bone(i); Actor &actor=rbon.actor; #if 1 Matrix matrix=anim_skel.bones[rbon.skel_bone]._matrix; #else Matrix matrix=(i ? anim_skel.bone(rbon.skel_bone)._matrix : anim_skel.matrix); #endif if(scaled)matrix.orn()/=_scale; if(kinematic)actor.kinematicMoveTo(matrix); else actor.matrix (matrix).vel(vel).angVel(VecZero); } } return T; } Ragdoll& Ragdoll::toSkel(AnimatedSkeleton &anim_skel) { if(_skel==anim_skel.skeleton() && bones()) { C Skeleton &skel=*_skel; // reset the orientation of non-ragdoll bones (and main and root) for default pose (the one from default Skeleton) anim_skel.root .clear(); REPA(_resets)anim_skel.bones[_resets[i]].clear(); #if 0 { Byte sbone =_resets[i]; Orient &bone_orn = anim_skel.bone(sbone).orn; SkelBone &skel_bone= skel.bone(sbone); Byte sparent = skel_bone.parent; if(sparent==0xFF)bone_orn=GetAnimOrient(skel_bone); else bone_orn=GetAnimOrient(skel_bone, &skel.bone(sparent)); } #endif // set bone oriantation according to actors Matrix body= bone(0).actor.matrix(); Matrix3 ibody; body.orn().inverse(ibody, true); for(Int i=1; ibones))for(;;) { if(_skel->bones[skel_bone_index].flag&BONE_RAGDOLL) // if skeleton bone should contain a bone in the ragdoll { REPA(T)if(bone(i).skel_bone==skel_bone_index)return i; } skel_bone_index=_skel->bones[skel_bone_index].parent; if(skel_bone_index==0xFF)break; } return 0; } return -1; } Int Ragdoll::findBoneIndexFromVtxMatrix(Byte matrix_index)C { return findBoneIndexFromSkelBone(matrix_index-1); } /******************************************************************************/ void Ragdoll::draw(C Color &col)C { REPAO(_bones).actor.draw(col); } /******************************************************************************/ #pragma pack(push, 1) struct RagdollDesc { Byte group, dominance, material; UInt user, flag; Flt sleep_energy; }; struct RagdollActorDesc { Vec vel, ang_vel; Matrix matrix; }; #pragma pack(pop) Bool Ragdoll::saveState(File &f, Bool include_matrix_vel)C { f.cmpUIntV(0); RagdollDesc desc; _Unaligned(desc.material , 0); Unaligned(desc.group , group()); Unaligned(desc.dominance , dominance()); _Unaligned(desc.user , (UIntPtr)user()); Unaligned(desc.sleep_energy, sleepEnergy()); UInt flag=0; if(kinematic())flag|=ACTOR_KINEMATIC; if(gravity ())flag|=ACTOR_GRAVITY; if(ray ())flag|=ACTOR_RAY; if(collision())flag|=ACTOR_COLLISION; if(sleep ())flag|=ACTOR_SLEEP; if(ccd ())flag|=ACTOR_CCD; Unaligned(desc.flag, flag); f<=1) // if there was at least one bone saved { RagdollActorDesc ad; f>>ad; // load first bone f.skip((bones-1)*SIZE(ad)); // skip the rest FREPA(T) // set all bones basing on the first { bone(i).actor.vel(Unaligned(ad.vel)).angVel(Unaligned(ad.ang_vel)).matrix(Unaligned(ad.matrix)); } } }else FREPA(T) { RagdollActorDesc ad; f>>ad; bone(i).actor.vel(Unaligned(ad.vel)).angVel(Unaligned(ad.ang_vel)).matrix(Unaligned(ad.matrix)); } } if(f.ok())return true; } }break; } return false; } /******************************************************************************/ } /******************************************************************************/