Ragdoll.cs 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  1. //
  2. // Copyright (c) 2008-2015 the Urho3D project.
  3. // Copyright (c) 2015 Xamarin Inc
  4. // Copyright (c) 2016 THUNDERBEAST GAMES LLC
  5. //
  6. // Permission is hereby granted, free of charge, to any person obtaining a copy
  7. // of this software and associated documentation files (the "Software"), to deal
  8. // in the Software without restriction, including without limitation the rights
  9. // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  10. // copies of the Software, and to permit persons to whom the Software is
  11. // furnished to do so, subject to the following conditions:
  12. //
  13. // The above copyright notice and this permission notice shall be included in
  14. // all copies or substantial portions of the Software.
  15. //
  16. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  17. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  18. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  19. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  20. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  21. // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  22. // THE SOFTWARE.
  23. //
  24. using System;
  25. using AtomicEngine;
  26. namespace FeatureExamples
  27. {
  28. class Ragdoll : CSComponent
  29. {
  30. public void Start()
  31. {
  32. SubscribeToEvent<NodeCollisionEvent>(Node, HandleNodeCollision);
  33. }
  34. void HandleNodeCollision(NodeCollisionEvent e)
  35. {
  36. // Get the other colliding body, make sure it is moving (has nonzero mass)
  37. RigidBody otherBody = e.OtherBody;
  38. if (otherBody.Mass > 0.0f)
  39. {
  40. // We do not need the physics components in the AnimatedModel's root scene node anymore
  41. Node.RemoveComponent<RigidBody>();
  42. Node.RemoveComponent<CollisionShape>();
  43. // Create RigidBody & CollisionShape components to bones
  44. CreateRagdollBone("Bip01_Pelvis", ShapeType.SHAPE_BOX, new Vector3(0.3f, 0.2f, 0.25f), new Vector3(0.0f, 0.0f, 0.0f),
  45. new Quaternion(0.0f, 0.0f, 0.0f));
  46. CreateRagdollBone("Bip01_Spine1", ShapeType.SHAPE_BOX, new Vector3(0.35f, 0.2f, 0.3f), new Vector3(0.15f, 0.0f, 0.0f),
  47. new Quaternion(0.0f, 0.0f, 0.0f));
  48. CreateRagdollBone("Bip01_L_Thigh", ShapeType.SHAPE_CAPSULE, new Vector3(0.175f, 0.45f, 0.175f), new Vector3(0.25f, 0.0f, 0.0f),
  49. new Quaternion(0.0f, 0.0f, 90.0f));
  50. CreateRagdollBone("Bip01_R_Thigh", ShapeType.SHAPE_CAPSULE, new Vector3(0.175f, 0.45f, 0.175f), new Vector3(0.25f, 0.0f, 0.0f),
  51. new Quaternion(0.0f, 0.0f, 90.0f));
  52. CreateRagdollBone("Bip01_L_Calf", ShapeType.SHAPE_CAPSULE, new Vector3(0.15f, 0.55f, 0.15f), new Vector3(0.25f, 0.0f, 0.0f),
  53. new Quaternion(0.0f, 0.0f, 90.0f));
  54. CreateRagdollBone("Bip01_R_Calf", ShapeType.SHAPE_CAPSULE, new Vector3(0.15f, 0.55f, 0.15f), new Vector3(0.25f, 0.0f, 0.0f),
  55. new Quaternion(0.0f, 0.0f, 90.0f));
  56. CreateRagdollBone("Bip01_Head", ShapeType.SHAPE_BOX, new Vector3(0.2f, 0.2f, 0.2f), new Vector3(0.1f, 0.0f, 0.0f),
  57. new Quaternion(0.0f, 0.0f, 0.0f));
  58. CreateRagdollBone("Bip01_L_UpperArm", ShapeType.SHAPE_CAPSULE, new Vector3(0.15f, 0.35f, 0.15f), new Vector3(0.1f, 0.0f, 0.0f),
  59. new Quaternion(0.0f, 0.0f, 90.0f));
  60. CreateRagdollBone("Bip01_R_UpperArm", ShapeType.SHAPE_CAPSULE, new Vector3(0.15f, 0.35f, 0.15f), new Vector3(0.1f, 0.0f, 0.0f),
  61. new Quaternion(0.0f, 0.0f, 90.0f));
  62. CreateRagdollBone("Bip01_L_Forearm", ShapeType.SHAPE_CAPSULE, new Vector3(0.125f, 0.4f, 0.125f), new Vector3(0.2f, 0.0f, 0.0f),
  63. new Quaternion(0.0f, 0.0f, 90.0f));
  64. CreateRagdollBone("Bip01_R_Forearm", ShapeType.SHAPE_CAPSULE, new Vector3(0.125f, 0.4f, 0.125f), new Vector3(0.2f, 0.0f, 0.0f),
  65. new Quaternion(0.0f, 0.0f, 90.0f));
  66. Vector3 back = new Vector3(0f, 0f, -1f);
  67. Vector3 forward = new Vector3(0f, 0f, 1f);
  68. Vector3 left = new Vector3(-1f, 0f, 0f);
  69. Vector3 down = new Vector3(0f, -1f, 0f);
  70. Vector3 up = new Vector3(0f, 1f, 0f);
  71. // Create Constraints between bones
  72. CreateRagdollConstraint("Bip01_L_Thigh", "Bip01_Pelvis", ConstraintType.CONSTRAINT_CONETWIST, back, forward,
  73. new Vector2(45.0f, 45.0f), Vector2.Zero);
  74. CreateRagdollConstraint("Bip01_R_Thigh", "Bip01_Pelvis", ConstraintType.CONSTRAINT_CONETWIST, back, forward,
  75. new Vector2(45.0f, 45.0f), Vector2.Zero);
  76. CreateRagdollConstraint("Bip01_L_Calf", "Bip01_L_Thigh", ConstraintType.CONSTRAINT_HINGE, back, back,
  77. new Vector2(90.0f, 0.0f), Vector2.Zero);
  78. CreateRagdollConstraint("Bip01_R_Calf", "Bip01_R_Thigh", ConstraintType.CONSTRAINT_HINGE, back, back,
  79. new Vector2(90.0f, 0.0f), Vector2.Zero);
  80. CreateRagdollConstraint("Bip01_Spine1", "Bip01_Pelvis", ConstraintType.CONSTRAINT_HINGE, forward, forward,
  81. new Vector2(45.0f, 0.0f), new Vector2(-10.0f, 0.0f));
  82. CreateRagdollConstraint("Bip01_Head", "Bip01_Spine1", ConstraintType.CONSTRAINT_CONETWIST, left, left,
  83. new Vector2(0.0f, 30.0f), Vector2.Zero);
  84. CreateRagdollConstraint("Bip01_L_UpperArm", "Bip01_Spine1", ConstraintType.CONSTRAINT_CONETWIST, down, up,
  85. new Vector2(45.0f, 45.0f), Vector2.Zero, false);
  86. CreateRagdollConstraint("Bip01_R_UpperArm", "Bip01_Spine1", ConstraintType.CONSTRAINT_CONETWIST, down, up,
  87. new Vector2(45.0f, 45.0f), Vector2.Zero, false);
  88. CreateRagdollConstraint("Bip01_L_Forearm", "Bip01_L_UpperArm", ConstraintType.CONSTRAINT_HINGE, back, back,
  89. new Vector2(90.0f, 0.0f), Vector2.Zero);
  90. CreateRagdollConstraint("Bip01_R_Forearm", "Bip01_R_UpperArm", ConstraintType.CONSTRAINT_HINGE, back, back,
  91. new Vector2(90.0f, 0.0f), Vector2.Zero);
  92. // Disable keyframe animation from all bones so that they will not interfere with the ragdoll
  93. AnimatedModel model = GetComponent<AnimatedModel>();
  94. Skeleton skeleton = model.Skeleton;
  95. for (uint i = 0; i < skeleton.GetNumBones(); ++i)
  96. {
  97. skeleton.GetBone(i).Animated = false;
  98. }
  99. // Finally remove self from the scene node. Note that this must be the last operation performed in the function
  100. Remove();
  101. }
  102. }
  103. void CreateRagdollBone(string boneName, ShapeType type, Vector3 size, Vector3 position, Quaternion rotation)
  104. {
  105. // Find the correct child scene node recursively
  106. Node boneNode = Node.GetChild(boneName, true);
  107. if (boneNode == null)
  108. {
  109. Log.Warn($"Could not find bone {boneName} for creating ragdoll physics components");
  110. return;
  111. }
  112. RigidBody body = boneNode.CreateComponent<RigidBody>();
  113. // Set mass to make movable
  114. body.Mass = 1.0f;
  115. // Set damping parameters to smooth out the motion
  116. body.LinearDamping = 0.05f;
  117. body.AngularDamping = 0.85f;
  118. // Set rest thresholds to ensure the ragdoll rigid bodies come to rest to not consume CPU endlessly
  119. body.LinearRestThreshold = 1.5f;
  120. body.AngularRestThreshold = 2.5f;
  121. CollisionShape shape = boneNode.CreateComponent<CollisionShape>();
  122. // We use either a box or a capsule shape for all of the bones
  123. if (type == ShapeType.SHAPE_BOX)
  124. shape.SetBox(size, position, rotation);
  125. else
  126. shape.SetCapsule(size.X, size.Y, position, rotation);
  127. }
  128. void CreateRagdollConstraint(string boneName, string parentName, ConstraintType type,
  129. Vector3 axis, Vector3 parentAxis, Vector2 highLimit, Vector2 lowLimit, bool disableCollision = true)
  130. {
  131. Node boneNode = Node.GetChild(boneName, true);
  132. Node parentNode = Node.GetChild(parentName, true);
  133. if (boneNode == null)
  134. {
  135. Log.Warn($"Could not find bone {boneName} for creating ragdoll constraint");
  136. return;
  137. }
  138. if (parentNode == null)
  139. {
  140. Log.Warn($"Could not find bone {parentName} for creating ragdoll constraint");
  141. return;
  142. }
  143. Constraint constraint = boneNode.CreateComponent<Constraint>();
  144. constraint.ConstraintType = type;
  145. // Most of the constraints in the ragdoll will work better when the connected bodies don't collide against each other
  146. constraint.DisableCollision = disableCollision;
  147. // The connected body must be specified before setting the world position
  148. constraint.OtherBody = parentNode.GetComponent<RigidBody>();
  149. // Position the constraint at the child bone we are connecting
  150. constraint.SetWorldPosition(boneNode.WorldPosition);
  151. // Configure axes and limits
  152. constraint.SetAxis(axis);
  153. constraint.SetOtherAxis(parentAxis);
  154. constraint.HighLimit = highLimit;
  155. constraint.LowLimit = lowLimit;
  156. }
  157. }
  158. }