2
0
Эх сурвалжийг харах

Added example of manual bone control to CharacterDemo. Added a version of Skeleton::GetBone() that takes a C string parameter to avoid ambiguity.

Lasse Öörni 12 жил өмнө
parent
commit
a26d5b98d5

+ 16 - 5
Bin/Data/LuaScripts/18_CharacterDemo.lua

@@ -2,6 +2,7 @@
 -- This sample demonstrates:
 --     - Controlling a humanoid character through physics
 --     - Driving animations using the AnimationController component
+--     - Manual control of a bone scene node
 --     - Implementing 1st and 3rd person cameras, using raycasts to avoid the 3rd person camera clipping into scenery
 --     - Saving and loading the variables of a script object
 
@@ -154,6 +155,9 @@ function CreateCharacter()
     object.castShadows = true
     characterNode:CreateComponent("AnimationController")
 
+    -- Set the head bone for manual control
+    object.skeleton:GetBone("Bip01_Head").animated = false;
+
     -- Create rigidbody, and set non-zero mass so that the body becomes dynamic
     local body = characterNode:CreateComponent("RigidBody")
     body.collisionLayer = 1
@@ -264,13 +268,20 @@ function HandlePostUpdate(eventType, eventData)
     local rot = characterNode.rotation
     local dir = rot * Quaternion(character.controls.pitch, Vector3(1.0, 0.0, 0.0))
 
+    -- Turn head to camera pitch, but limit to avoid unnatural animation
+    local headNode = characterNode:GetChild("Bip01_Head", true);
+    local limitPitch = Clamp(character.controls.pitch, -45.0, 45.0);
+    local headDir = rot * Quaternion(limitPitch, Vector3(1.0, 0.0, 0.0));
+    -- This could be expanded to look at an arbitrary target, now just look at a point in front
+    local headWorldTarget = headNode.worldPosition + headDir * Vector3(0.0, 0.0, 1.0);
+    headNode:LookAt(headWorldTarget, Vector3(0.0, 1.0, 0.0));
+    -- Correct head orientation because LookAt assumes Z = forward, but the bone has been authored differently (Y = forward)
+    headNode:Rotate(Quaternion(0.0, 90.0, 90.0));
+
     if firstPerson then
         -- First person camera: position to the head bone + offset slightly forward & up
-        local headNode = characterNode:GetChild("Bip01_Head", true)
-        if headNode ~= nil then
-            cameraNode.position = headNode.worldPosition + rot * Vector3(0.0, 0.15, 0.2)
-            cameraNode.rotation = dir
-        end
+        cameraNode.position = headNode.worldPosition + rot * Vector3(0.0, 0.15, 0.2)
+        cameraNode.rotation = dir
     else
         -- Third person camera: position behind the character
         local aimPoint = characterNode.position + rot * Vector3(0.0, 1.7, 0.0)

+ 16 - 6
Bin/Data/Scripts/18_CharacterDemo.as

@@ -2,6 +2,7 @@
 // This sample demonstrates:
 //     - Controlling a humanoid character through physics
 //     - Driving animations using the AnimationController component
+//     - Manual control of a bone scene node
 //     - Implementing 1st and 3rd person cameras, using raycasts to avoid the 3rd person camera clipping into scenery
 //     - Saving and loading the variables of a script object
 
@@ -149,6 +150,9 @@ void CreateCharacter()
     object.castShadows = true;
     characterNode.CreateComponent("AnimationController");
 
+    // Set the head bone for manual control
+    object.skeleton.GetBone("Bip01_Head").animated = false;
+
     // Create rigidbody, and set non-zero mass so that the body becomes dynamic
     RigidBody@ body = characterNode.CreateComponent("RigidBody");
     body.collisionLayer = 1;
@@ -261,15 +265,21 @@ void HandlePostUpdate(StringHash eventType, VariantMap& eventData)
     Quaternion rot = characterNode.rotation;
     Quaternion dir = rot * Quaternion(character.controls.pitch, Vector3(1.0f, 0.0f, 0.0f));
 
+    // Turn head to camera pitch, but limit to avoid unnatural animation
+    Node@ headNode = characterNode.GetChild("Bip01_Head", true);
+    float limitPitch = Clamp(character.controls.pitch, -45.0f, 45.0f);
+    Quaternion headDir = rot * Quaternion(limitPitch, Vector3(1.0f, 0.0f, 0.0f));
+    // This could be expanded to look at an arbitrary target, now just look at a point in front
+    Vector3 headWorldTarget = headNode.worldPosition + headDir * Vector3(0.0f, 0.0f, 1.0f);
+    headNode.LookAt(headWorldTarget, Vector3(0.0f, 1.0f, 0.0f));
+    // Correct head orientation because LookAt assumes Z = forward, but the bone has been authored differently (Y = forward)
+    headNode.Rotate(Quaternion(0.0f, 90.0f, 90.0f));
+
     if (firstPerson)
     {
         // First person camera: position to the head bone + offset slightly forward & up
-        Node@ headNode = characterNode.GetChild("Bip01_Head", true);
-        if (headNode !is null)
-        {
-            cameraNode.position = headNode.worldPosition + rot * Vector3(0.0f, 0.15f, 0.2f);
-            cameraNode.rotation = dir;
-        }
+        cameraNode.position = headNode.worldPosition + rot * Vector3(0.0f, 0.15f, 0.2f);
+        cameraNode.rotation = dir;
     }
     else
     {

+ 6 - 7
Source/Engine/Graphics/Skeleton.cpp

@@ -141,13 +141,12 @@ Bone* Skeleton::GetBone(unsigned index)
 
 Bone* Skeleton::GetBone(const String& name)
 {
-    for (Vector<Bone>::Iterator i = bones_.Begin(); i != bones_.End(); ++i)
-    {
-        if (i->name_ == name)
-            return &(*i);
-    }
-    
-    return 0;
+    return GetBone(StringHash(name));
+}
+
+Bone* Skeleton::GetBone(const char* name)
+{
+    return GetBone(StringHash(name));
 }
 
 Bone* Skeleton::GetBone(StringHash nameHash)

+ 2 - 0
Source/Engine/Graphics/Skeleton.h

@@ -109,6 +109,8 @@ public:
     Bone* GetBone(unsigned index);
     /// Return bone by name.
     Bone* GetBone(const String& boneName);
+    /// Return bone by name.
+    Bone* GetBone(const char* boneName);
     /// Return bone by name hash.
     Bone* GetBone(StringHash boneNameHash);
     

+ 1 - 1
Source/Engine/Math/MathDefs.h

@@ -80,7 +80,7 @@ inline float Clamp(float value, float min, float max)
         return value;
 }
 
-/// Smoothly damp between values
+/// Smoothly damp between values.
 inline float SmoothStep(float lhs, float rhs, float t)
 {
     t = Clamp((t - lhs) / (rhs - lhs), 0.0f, 1.0f); // Saturate t

+ 15 - 7
Source/Samples/18_CharacterDemo/CharacterDemo.cpp

@@ -189,6 +189,9 @@ void CharacterDemo::CreateCharacter()
     object->SetCastShadows(true);
     objectNode->CreateComponent<AnimationController>();
     
+    // Set the head bone for manual control
+    object->GetSkeleton().GetBone("Bip01_Head")->animated_ = false;
+
     // Create rigidbody, and set non-zero mass so that the body becomes dynamic
     RigidBody* body = objectNode->CreateComponent<RigidBody>();
     body->SetCollisionLayer(1);
@@ -309,15 +312,20 @@ void CharacterDemo::HandlePostUpdate(StringHash eventType, VariantMap& eventData
     Quaternion rot = characterNode->GetRotation();
     Quaternion dir = rot * Quaternion(character_->controls_.pitch_, Vector3::RIGHT);
     
+    // Turn head to camera pitch, but limit to avoid unnatural animation
+    Node* headNode = characterNode->GetChild("Bip01_Head", true);
+    float limitPitch = Clamp(character_->controls_.pitch_, -45.0f, 45.0f);
+    Quaternion headDir = rot * Quaternion(limitPitch, Vector3(1.0f, 0.0f, 0.0f));
+    // This could be expanded to look at an arbitrary target, now just look at a point in front
+    Vector3 headWorldTarget = headNode->GetWorldPosition() + headDir * Vector3(0.0f, 0.0f, 1.0f);
+    headNode->LookAt(headWorldTarget, Vector3(0.0f, 1.0f, 0.0f));
+    // Correct head orientation because LookAt assumes Z = forward, but the bone has been authored differently (Y = forward)
+    headNode->Rotate(Quaternion(0.0f, 90.0f, 90.0f));
+
     if (firstPerson_)
     {
-        // First person camera: position to the head bone + offset slightly forward & up
-        Node* headNode = characterNode->GetChild("Bip01_Head", true);
-        if (headNode)
-        {
-            cameraNode_->SetPosition(headNode->GetWorldPosition() + rot * Vector3(0.0f, 0.15f, 0.2f));
-            cameraNode_->SetRotation(dir);
-        }
+        cameraNode_->SetPosition(headNode->GetWorldPosition() + rot * Vector3(0.0f, 0.15f, 0.2f));
+        cameraNode_->SetRotation(dir);
     }
     else
     {

+ 1 - 0
Source/Samples/18_CharacterDemo/CharacterDemo.h

@@ -38,6 +38,7 @@ class Character;
 /// This sample demonstrates:
 ///     - Controlling a humanoid character through physics
 ///     - Driving animations using the AnimationController component
+///     - Manual control of a bone scene node
 ///     - Implementing 1st and 3rd person cameras, using raycasts to avoid the 3rd person camera clipping into scenery
 ///     - Defining attributes of a custom component so that it can be saved and loaded
 class CharacterDemo : public Sample