Pārlūkot izejas kodu

Merge pull request #240 from dgough/next

Next
Sean Paul Taylor 13 gadi atpakaļ
vecāks
revīzija
054a652115

+ 5 - 0
gameplay-encoder/src/Animations.cpp

@@ -62,4 +62,9 @@ Animation* Animations::getAnimation(unsigned int index) const
     return _animations[index];
 }
 
+void Animations::removeAnimation(unsigned int index)
+{
+    _animations.erase(_animations.begin() + index);
+}
+
 }

+ 1 - 0
gameplay-encoder/src/Animations.h

@@ -32,6 +32,7 @@ public:
     void add(Animation* animation);
     unsigned int getAnimationCount() const;
     Animation* getAnimation(unsigned int index) const;
+    void removeAnimation(unsigned int index);
 
 private:
 

+ 0 - 25
gameplay-encoder/src/DAESceneEncoder.cpp

@@ -34,31 +34,6 @@ unsigned int getMaxOffset(domInputLocalOffset_Array& inputArray)
     return maxOffset;
 }
 
-/**
- * Prompts the user if they want to group animations automatically.
- * If the user enters an invalid response, the question is asked again.
- * 
- * @return True if the user wants to group animations, false otherwise.
- */
-bool promptUserGroupAnimations()
-{
-    char buffer[80];
-    for (;;)
-    {
-        printf("Do you want to group animations ? (y/n)\n");
-        std::cin.getline(buffer, 80);
-        
-        if (buffer[0] == 'y' || buffer[0] == 'Y' || buffer[0] == '\0')
-        {
-            return true;
-        }
-        else if (buffer[0] == 'n' || buffer[0] == 'N')
-        {
-            return false;
-        }
-    }
-}
-
 void DAESceneEncoder::optimizeCOLLADA(const EncoderArguments& arguments, domCOLLADA* dom)
 {
     const std::vector<std::string>& groupAnimatioNodeIds = arguments.getGroupAnimationNodeId();

+ 75 - 1
gameplay-encoder/src/FBXSceneEncoder.cpp

@@ -154,13 +154,24 @@ void addScaleChannel(Animation* animation, FbxNode* fbxNode, float startTime, fl
 
 void addTranslateChannel(Animation* animation, FbxNode* fbxNode, float startTime, float stopTime);
 
+/**
+ * Determines if it is possible to automatically group animations for mesh skins.
+ * 
+ * @param fbxScene The FBX scene to search.
+ * 
+ * @return True if there is at least one mesh skin that has animations that can be grouped.
+ */
+bool isGroupAnimationPossible(FbxScene* fbxScene);
+bool isGroupAnimationPossible(FbxNode* fbxNode);
+bool isGroupAnimationPossible(FbxMesh* fbxMesh);
+
 
 ////////////////////////////////////
 // Member Functions
 ////////////////////////////////////
 
 FBXSceneEncoder::FBXSceneEncoder()
-    : _groupAnimation(NULL)
+    : _groupAnimation(NULL), _autoGroupAnimations(false)
 {
 }
 
@@ -187,6 +198,16 @@ void FBXSceneEncoder::write(const std::string& filepath, const EncoderArguments&
     print("Loading FBX file.");
     importer->Import(fbxScene);
     importer->Destroy();
+
+    // Determine if animations should be grouped.
+    if (arguments.getGroupAnimationAnimationId().empty() && isGroupAnimationPossible(fbxScene))
+    {
+        if (promptUserGroupAnimations())
+        {
+            _autoGroupAnimations = true;
+        }
+    }
+
     print("Loading Scene.");
     loadScene(fbxScene);
     print("Loading animations.");
@@ -195,6 +216,10 @@ void FBXSceneEncoder::write(const std::string& filepath, const EncoderArguments&
 
     print("Optimizing GamePlay Binary.");
     _gamePlayFile.adjust();
+    if (_autoGroupAnimations)
+    {
+        _gamePlayFile.groupMeshSkinAnimations();
+    }
     
     std::string outputFilePath = arguments.getOutputFilePath();
 
@@ -1473,4 +1498,53 @@ void copyMatrix(const FbxMatrix& fbxMatrix, Matrix& matrix)
     }
 }
 
+bool isGroupAnimationPossible(FbxScene* fbxScene)
+{
+    FbxNode* rootNode = fbxScene->GetRootNode();
+    if (rootNode)
+    {
+        if (isGroupAnimationPossible(rootNode))
+            return true;
+    }
+    return false;
+}
+
+bool isGroupAnimationPossible(FbxNode* fbxNode)
+{
+    if (fbxNode)
+    {
+        FbxMesh* fbxMesh = fbxNode->GetMesh();
+        if (isGroupAnimationPossible(fbxMesh))
+            return true;
+        const int childCount = fbxNode->GetChildCount();
+        for (int i = 0; i < childCount; ++i)
+        {
+            if (isGroupAnimationPossible(fbxNode->GetChild(i)))
+                return true;
+        }
+    }
+    return false;
+}
+
+bool isGroupAnimationPossible(FbxMesh* fbxMesh)
+{
+    if (fbxMesh)
+    {
+        const int deformerCount = fbxMesh->GetDeformerCount();
+        for (int i = 0; i < deformerCount; ++i)
+        {
+            FbxDeformer* deformer = fbxMesh->GetDeformer(i);
+            if (deformer->GetDeformerType() == FbxDeformer::eSkin)
+            {
+                FbxSkin* fbxSkin = static_cast<FbxSkin*>(deformer);
+                if (fbxSkin)
+                {
+                    return true;
+                }
+            }
+        }
+    }
+    return false;
+}
+
 #endif

+ 6 - 1
gameplay-encoder/src/FBXSceneEncoder.h

@@ -215,9 +215,14 @@ private:
     std::map<FbxUInt64, Mesh*> _meshes;
 
     /**
-     * The animation that channels should be added to it the user is using the -groupAnimation command line argument. May be NULL.
+     * The animation that channels should be added to if the user is using the -groupAnimation command line argument. May be NULL.
      */
     Animation* _groupAnimation;
+
+    /**
+     * Indicates if the animations for mesh skins should be grouped before writing out the GPB file.
+     */
+    bool _autoGroupAnimations;
 };
 
 #endif

+ 19 - 0
gameplay-encoder/src/FileIO.cpp

@@ -184,4 +184,23 @@ void writeVectorText(const Vector4& v, FILE* file)
     fprintf(file, "%f %f %f %f\n", v.x, v.y, v.z, v.w);
 }
 
+bool promptUserGroupAnimations()
+{
+    char buffer[80];
+    for (;;)
+    {
+        printf("Do you want to group animations? (y/n)\n");
+        std::cin.getline(buffer, 80);
+        
+        if (buffer[0] == 'y' || buffer[0] == 'Y' || buffer[0] == '\0')
+        {
+            return true;
+        }
+        else if (buffer[0] == 'n' || buffer[0] == 'N')
+        {
+            return false;
+        }
+    }
+}
+
 }

+ 8 - 0
gameplay-encoder/src/FileIO.h

@@ -136,6 +136,14 @@ void writeVectorBinary(const Vector4& v, FILE* file);
 
 void writeVectorText(const Vector4& v, FILE* file);
 
+/**
+ * Prompts the user if they want to group animations automatically.
+ * If the user enters an invalid response, the question is asked again.
+ * 
+ * @return True if the user wants to group animations, false otherwise.
+ */
+bool promptUserGroupAnimations();
+
 }
 
 #endif

+ 106 - 9
gameplay-encoder/src/GPBFile.cpp

@@ -1,6 +1,7 @@
 #include "Base.h"
 #include "GPBFile.h"
 #include "Transform.h"
+#include "StringUtil.h"
 
 #define EPSILON 1.2e-7f;
 
@@ -14,6 +15,26 @@ static GPBFile* __instance = NULL;
  */
 static bool isAlmostOne(float value);
 
+/**
+ * Gets the common node ancestor for the given list of nodes.
+ * This function assumes that the nodes share a common ancestor.
+ * 
+ * @param nodes The list of nodes.
+ * 
+ * @return The common node ancestor or NULL if the list of was empty.
+ */
+static Node* getCommonNodeAncestor(const std::vector<Node*>& nodes);
+
+/**
+ * Gets the list of node ancestors for the given node.
+ * 
+ * @param node The node to get the ancestors for.
+ * @param ancestors The output list of ancestors. 
+ *                  The first element is the root node and the last element is the direct parent of the node.
+ */
+static void getNodeAncestors(Node* node, std::list<Node*>& ancestors);
+
+
 GPBFile::GPBFile(void)
     : _file(NULL), _animationsAdded(false)
 {
@@ -304,6 +325,30 @@ void GPBFile::adjust()
     //   This can be merged into one animation. Same for scale animations.
 }
 
+void GPBFile::groupMeshSkinAnimations()
+{
+    for (std::list<Node*>::iterator it = _nodes.begin(); it != _nodes.end(); ++it)
+    {
+        if (Model* model = (*it)->getModel())
+        {
+            if (MeshSkin* skin = model->getSkin())
+            {
+                const std::vector<Node*>& joints = skin->getJoints();
+                Node* commonAncestor = getCommonNodeAncestor(joints);
+                if (commonAncestor)
+                {
+                    // group the animation channels that target this common ancestor and its child nodes
+                    Animation* animation = new Animation();
+                    animation->setId("animations");
+
+                    moveAnimationChannels(commonAncestor, animation);
+                    _animations.add(animation);
+                }
+            }
+        }
+    }
+}
+
 void GPBFile::renameAnimations(std::vector<std::string>& animationIds, const char* newId)
 {
     const unsigned int animationCount = _animations.getAnimationCount();
@@ -328,14 +373,6 @@ void GPBFile::computeBounds(Node* node)
         {
             mesh->computeBounds();
         }
-        if (MeshSkin* skin = model->getSkin())
-        {
-            skin->computeBounds();
-        }
-    }
-    for (Node* child = node->getFirstChild(); child != NULL; child = child->getNextSibling())
-    {
-        computeBounds(child);
     }
 }
 
@@ -442,9 +479,69 @@ void GPBFile::decomposeTransformAnimationChannel(Animation* animation, const Ani
     animation->add(translateChannel);
 }
 
-static bool isAlmostOne(float value)
+void GPBFile::moveAnimationChannels(Node* node, Animation* dstAnimation)
+{
+    // Loop through the animations and channels backwards because they will be removed when found.
+    int animationCount = _animations.getAnimationCount();
+    for (int i = animationCount - 1; i >= 0; --i)
+    {
+        Animation* animation = _animations.getAnimation(i);
+        int channelCount = animation->getAnimationChannelCount();
+        for (int j = channelCount - 1; j >= 0; --j)
+        {
+            AnimationChannel* channel = animation->getAnimationChannel(j);
+            if (equals(channel->getTargetId(), node->getId()))
+            {
+                animation->remove(channel);
+                dstAnimation->add(channel);
+            }
+        }
+        if (animation->getAnimationChannelCount() == 0)
+        {
+            _animations.removeAnimation(i);
+        }
+    }
+    for (Node* child = node->getFirstChild(); child != NULL; child = child->getNextSibling())
+    {
+        moveAnimationChannels(child, dstAnimation);
+    }
+}
+
+bool isAlmostOne(float value)
 {
     return std::fabs(value - 1.0f) < EPSILON;
 }
 
+Node* getCommonNodeAncestor(const std::vector<Node*>& nodes)
+{
+    if (nodes.empty())
+        return NULL;
+    if (nodes.size() == 1)
+        return nodes.front();
+
+    std::list<Node*> ancestors;
+    size_t minAncestorCount = INT_MAX;
+    for (std::vector<Node*>::const_iterator it = nodes.begin(); it != nodes.end(); ++it)
+    {
+        Node* node = *it;
+        getNodeAncestors(node, ancestors);
+        ancestors.push_back(node);
+        minAncestorCount = std::min(minAncestorCount, ancestors.size());
+    }
+    ancestors.resize(minAncestorCount);
+
+    return ancestors.back();
+}
+
+void getNodeAncestors(Node* node, std::list<Node*>& ancestors)
+{
+    ancestors.clear();
+    Node* parent = node->getParent();
+    while (parent != NULL)
+    {
+        ancestors.push_front(parent);
+        parent = parent->getParent();
+    }
+}
+
 }

+ 16 - 0
gameplay-encoder/src/GPBFile.h

@@ -102,6 +102,11 @@ public:
      */
     void adjust();
 
+    /**
+     * Groups the animations of all mesh skins to be under one animation per mesh skin.
+     */
+    void groupMeshSkinAnimations();
+
     /**
      * Renames the animations in the list of animation ids to the new animation id.
      * 
@@ -125,6 +130,14 @@ private:
      */
     void decomposeTransformAnimationChannel(Animation* animation, const AnimationChannel* channel);
 
+    /**
+     * Moves the animation channels that target the given node and its children to be under the given animation.
+     * 
+     * @param node The node to recursively search from.
+     * @param animation The animation to move the channels to.
+     */
+    void moveAnimationChannels(Node* node, Animation* animation);
+
 private:
 
     FILE* _file;
@@ -132,6 +145,9 @@ private:
     std::list<Camera*> _cameras;
     std::list<Light*> _lights;
     std::list<Mesh*> _geometry;
+    /**
+     * The flat list of all nodes.
+     */
     std::list<Node*> _nodes;
     Animations _animations;
     bool _animationsAdded;

+ 5 - 0
gameplay-encoder/src/MeshSkin.cpp

@@ -109,6 +109,11 @@ const std::vector<std::string>& MeshSkin::getJointNames()
     return _jointNames;
 }
 
+const std::vector<Node*>& MeshSkin::getJoints() const
+{
+    return _joints;
+}
+
 void MeshSkin::setJoints(const std::vector<Node*>& list)
 {
     _joints = list;

+ 3 - 1
gameplay-encoder/src/MeshSkin.h

@@ -38,9 +38,11 @@ public:
 
     void setVertexInfluenceCount(unsigned int count);
 
+    const std::vector<std::string>& getJointNames();
+
     void setJointNames(const std::vector<std::string>& list);
 
-    const std::vector<std::string>& getJointNames();
+    const std::vector<Node*>& getJoints() const;
 
     void setJoints(const std::vector<Node*>& list);