summaryrefslogtreecommitdiff
path: root/libs/assimp/code/AssetLib/BVH/BVHLoader.cpp
diff options
context:
space:
mode:
authorsanine <sanine.not@pm.me>2022-04-16 11:55:09 -0500
committersanine <sanine.not@pm.me>2022-04-16 11:55:09 -0500
commitdb81b925d776103326128bf629cbdda576a223e7 (patch)
tree58bea8155c686733310009f6bed7363f91fbeb9d /libs/assimp/code/AssetLib/BVH/BVHLoader.cpp
parent55860037b14fb3893ba21cf2654c83d349cc1082 (diff)
move 3rd-party librarys into libs/ and add built-in honeysuckle
Diffstat (limited to 'libs/assimp/code/AssetLib/BVH/BVHLoader.cpp')
-rw-r--r--libs/assimp/code/AssetLib/BVH/BVHLoader.cpp528
1 files changed, 528 insertions, 0 deletions
diff --git a/libs/assimp/code/AssetLib/BVH/BVHLoader.cpp b/libs/assimp/code/AssetLib/BVH/BVHLoader.cpp
new file mode 100644
index 0000000..1748368
--- /dev/null
+++ b/libs/assimp/code/AssetLib/BVH/BVHLoader.cpp
@@ -0,0 +1,528 @@
+/** Implementation of the BVH loader */
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (assimp)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2022, assimp team
+
+
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the following
+conditions are met:
+
+* Redistributions of source code must retain the above
+copyright notice, this list of conditions and the
+following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the
+following disclaimer in the documentation and/or other
+materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+contributors may be used to endorse or promote products
+derived from this software without specific prior
+written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+
+#ifndef ASSIMP_BUILD_NO_BVH_IMPORTER
+
+#include "BVHLoader.h"
+#include <assimp/SkeletonMeshBuilder.h>
+#include <assimp/TinyFormatter.h>
+#include <assimp/fast_atof.h>
+#include <assimp/importerdesc.h>
+#include <assimp/scene.h>
+#include <assimp/IOSystem.hpp>
+#include <assimp/Importer.hpp>
+#include <map>
+#include <memory>
+
+using namespace Assimp;
+using namespace Assimp::Formatter;
+
+static const aiImporterDesc desc = {
+ "BVH Importer (MoCap)",
+ "",
+ "",
+ "",
+ aiImporterFlags_SupportTextFlavour,
+ 0,
+ 0,
+ 0,
+ 0,
+ "bvh"
+};
+
+// ------------------------------------------------------------------------------------------------
+// Aborts the file reading with an exception
+template<typename... T>
+AI_WONT_RETURN void BVHLoader::ThrowException(T&&... args) {
+ throw DeadlyImportError(mFileName, ":", mLine, " - ", args...);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+BVHLoader::BVHLoader() :
+ mLine(),
+ mAnimTickDuration(),
+ mAnimNumFrames(),
+ noSkeletonMesh() {}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor, private as well
+BVHLoader::~BVHLoader() {}
+
+// ------------------------------------------------------------------------------------------------
+// Returns whether the class can handle the format of the given file.
+bool BVHLoader::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool /*checkSig*/) const {
+ static const char *tokens[] = { "HIERARCHY" };
+ return SearchFileHeaderForToken(pIOHandler, pFile, tokens, AI_COUNT_OF(tokens));
+}
+
+// ------------------------------------------------------------------------------------------------
+void BVHLoader::SetupProperties(const Importer *pImp) {
+ noSkeletonMesh = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_NO_SKELETON_MESHES, 0) != 0;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Loader meta information
+const aiImporterDesc *BVHLoader::GetInfo() const {
+ return &desc;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Imports the given file into the given scene structure.
+void BVHLoader::InternReadFile(const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler) {
+ mFileName = pFile;
+
+ // read file into memory
+ std::unique_ptr<IOStream> file(pIOHandler->Open(pFile));
+ if (file.get() == nullptr) {
+ throw DeadlyImportError("Failed to open file ", pFile, ".");
+ }
+
+ size_t fileSize = file->FileSize();
+ if (fileSize == 0) {
+ throw DeadlyImportError("File is too small.");
+ }
+
+ mBuffer.resize(fileSize);
+ file->Read(&mBuffer.front(), 1, fileSize);
+
+ // start reading
+ mReader = mBuffer.begin();
+ mLine = 1;
+ ReadStructure(pScene);
+
+ if (!noSkeletonMesh) {
+ // build a dummy mesh for the skeleton so that we see something at least
+ SkeletonMeshBuilder meshBuilder(pScene);
+ }
+
+ // construct an animation from all the motion data we read
+ CreateAnimation(pScene);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads the file
+void BVHLoader::ReadStructure(aiScene *pScene) {
+ // first comes hierarchy
+ std::string header = GetNextToken();
+ if (header != "HIERARCHY")
+ ThrowException("Expected header string \"HIERARCHY\".");
+ ReadHierarchy(pScene);
+
+ // then comes the motion data
+ std::string motion = GetNextToken();
+ if (motion != "MOTION")
+ ThrowException("Expected beginning of motion data \"MOTION\".");
+ ReadMotion(pScene);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads the hierarchy
+void BVHLoader::ReadHierarchy(aiScene *pScene) {
+ std::string root = GetNextToken();
+ if (root != "ROOT")
+ ThrowException("Expected root node \"ROOT\".");
+
+ // Go read the hierarchy from here
+ pScene->mRootNode = ReadNode();
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads a node and recursively its children and returns the created node;
+aiNode *BVHLoader::ReadNode() {
+ // first token is name
+ std::string nodeName = GetNextToken();
+ if (nodeName.empty() || nodeName == "{")
+ ThrowException("Expected node name, but found \"", nodeName, "\".");
+
+ // then an opening brace should follow
+ std::string openBrace = GetNextToken();
+ if (openBrace != "{")
+ ThrowException("Expected opening brace \"{\", but found \"", openBrace, "\".");
+
+ // Create a node
+ aiNode *node = new aiNode(nodeName);
+ std::vector<aiNode *> childNodes;
+
+ // and create an bone entry for it
+ mNodes.push_back(Node(node));
+ Node &internNode = mNodes.back();
+
+ // now read the node's contents
+ std::string siteToken;
+ while (1) {
+ std::string token = GetNextToken();
+
+ // node offset to parent node
+ if (token == "OFFSET")
+ ReadNodeOffset(node);
+ else if (token == "CHANNELS")
+ ReadNodeChannels(internNode);
+ else if (token == "JOINT") {
+ // child node follows
+ aiNode *child = ReadNode();
+ child->mParent = node;
+ childNodes.push_back(child);
+ } else if (token == "End") {
+ // The real symbol is "End Site". Second part comes in a separate token
+ siteToken.clear();
+ siteToken = GetNextToken();
+ if (siteToken != "Site")
+ ThrowException("Expected \"End Site\" keyword, but found \"", token, " ", siteToken, "\".");
+
+ aiNode *child = ReadEndSite(nodeName);
+ child->mParent = node;
+ childNodes.push_back(child);
+ } else if (token == "}") {
+ // we're done with that part of the hierarchy
+ break;
+ } else {
+ // everything else is a parse error
+ ThrowException("Unknown keyword \"", token, "\".");
+ }
+ }
+
+ // add the child nodes if there are any
+ if (childNodes.size() > 0) {
+ node->mNumChildren = static_cast<unsigned int>(childNodes.size());
+ node->mChildren = new aiNode *[node->mNumChildren];
+ std::copy(childNodes.begin(), childNodes.end(), node->mChildren);
+ }
+
+ // and return the sub-hierarchy we built here
+ return node;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads an end node and returns the created node.
+aiNode *BVHLoader::ReadEndSite(const std::string &pParentName) {
+ // check opening brace
+ std::string openBrace = GetNextToken();
+ if (openBrace != "{")
+ ThrowException("Expected opening brace \"{\", but found \"", openBrace, "\".");
+
+ // Create a node
+ aiNode *node = new aiNode("EndSite_" + pParentName);
+
+ // now read the node's contents. Only possible entry is "OFFSET"
+ std::string token;
+ while (1) {
+ token.clear();
+ token = GetNextToken();
+
+ // end node's offset
+ if (token == "OFFSET") {
+ ReadNodeOffset(node);
+ } else if (token == "}") {
+ // we're done with the end node
+ break;
+ } else {
+ // everything else is a parse error
+ ThrowException("Unknown keyword \"", token, "\".");
+ }
+ }
+
+ // and return the sub-hierarchy we built here
+ return node;
+}
+// ------------------------------------------------------------------------------------------------
+// Reads a node offset for the given node
+void BVHLoader::ReadNodeOffset(aiNode *pNode) {
+ // Offset consists of three floats to read
+ aiVector3D offset;
+ offset.x = GetNextTokenAsFloat();
+ offset.y = GetNextTokenAsFloat();
+ offset.z = GetNextTokenAsFloat();
+
+ // build a transformation matrix from it
+ pNode->mTransformation = aiMatrix4x4(1.0f, 0.0f, 0.0f, offset.x,
+ 0.0f, 1.0f, 0.0f, offset.y,
+ 0.0f, 0.0f, 1.0f, offset.z,
+ 0.0f, 0.0f, 0.0f, 1.0f);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads the animation channels for the given node
+void BVHLoader::ReadNodeChannels(BVHLoader::Node &pNode) {
+ // number of channels. Use the float reader because we're lazy
+ float numChannelsFloat = GetNextTokenAsFloat();
+ unsigned int numChannels = (unsigned int)numChannelsFloat;
+
+ for (unsigned int a = 0; a < numChannels; a++) {
+ std::string channelToken = GetNextToken();
+
+ if (channelToken == "Xposition")
+ pNode.mChannels.push_back(Channel_PositionX);
+ else if (channelToken == "Yposition")
+ pNode.mChannels.push_back(Channel_PositionY);
+ else if (channelToken == "Zposition")
+ pNode.mChannels.push_back(Channel_PositionZ);
+ else if (channelToken == "Xrotation")
+ pNode.mChannels.push_back(Channel_RotationX);
+ else if (channelToken == "Yrotation")
+ pNode.mChannels.push_back(Channel_RotationY);
+ else if (channelToken == "Zrotation")
+ pNode.mChannels.push_back(Channel_RotationZ);
+ else
+ ThrowException("Invalid channel specifier \"", channelToken, "\".");
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads the motion data
+void BVHLoader::ReadMotion(aiScene * /*pScene*/) {
+ // Read number of frames
+ std::string tokenFrames = GetNextToken();
+ if (tokenFrames != "Frames:")
+ ThrowException("Expected frame count \"Frames:\", but found \"", tokenFrames, "\".");
+
+ float numFramesFloat = GetNextTokenAsFloat();
+ mAnimNumFrames = (unsigned int)numFramesFloat;
+
+ // Read frame duration
+ std::string tokenDuration1 = GetNextToken();
+ std::string tokenDuration2 = GetNextToken();
+ if (tokenDuration1 != "Frame" || tokenDuration2 != "Time:")
+ ThrowException("Expected frame duration \"Frame Time:\", but found \"", tokenDuration1, " ", tokenDuration2, "\".");
+
+ mAnimTickDuration = GetNextTokenAsFloat();
+
+ // resize value vectors for each node
+ for (std::vector<Node>::iterator it = mNodes.begin(); it != mNodes.end(); ++it)
+ it->mChannelValues.reserve(it->mChannels.size() * mAnimNumFrames);
+
+ // now read all the data and store it in the corresponding node's value vector
+ for (unsigned int frame = 0; frame < mAnimNumFrames; ++frame) {
+ // on each line read the values for all nodes
+ for (std::vector<Node>::iterator it = mNodes.begin(); it != mNodes.end(); ++it) {
+ // get as many values as the node has channels
+ for (unsigned int c = 0; c < it->mChannels.size(); ++c)
+ it->mChannelValues.push_back(GetNextTokenAsFloat());
+ }
+
+ // after one frame worth of values for all nodes there should be a newline, but we better don't rely on it
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Retrieves the next token
+std::string BVHLoader::GetNextToken() {
+ // skip any preceding whitespace
+ while (mReader != mBuffer.end()) {
+ if (!isspace((unsigned char)*mReader))
+ break;
+
+ // count lines
+ if (*mReader == '\n')
+ mLine++;
+
+ ++mReader;
+ }
+
+ // collect all chars till the next whitespace. BVH is easy in respect to that.
+ std::string token;
+ while (mReader != mBuffer.end()) {
+ if (isspace((unsigned char)*mReader))
+ break;
+
+ token.push_back(*mReader);
+ ++mReader;
+
+ // little extra logic to make sure braces are counted correctly
+ if (token == "{" || token == "}")
+ break;
+ }
+
+ // empty token means end of file, which is just fine
+ return token;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads the next token as a float
+float BVHLoader::GetNextTokenAsFloat() {
+ std::string token = GetNextToken();
+ if (token.empty())
+ ThrowException("Unexpected end of file while trying to read a float");
+
+ // check if the float is valid by testing if the atof() function consumed every char of the token
+ const char *ctoken = token.c_str();
+ float result = 0.0f;
+ ctoken = fast_atoreal_move<float>(ctoken, result);
+
+ if (ctoken != token.c_str() + token.length())
+ ThrowException("Expected a floating point number, but found \"", token, "\".");
+
+ return result;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Constructs an animation for the motion data and stores it in the given scene
+void BVHLoader::CreateAnimation(aiScene *pScene) {
+ // create the animation
+ pScene->mNumAnimations = 1;
+ pScene->mAnimations = new aiAnimation *[1];
+ aiAnimation *anim = new aiAnimation;
+ pScene->mAnimations[0] = anim;
+
+ // put down the basic parameters
+ anim->mName.Set("Motion");
+ anim->mTicksPerSecond = 1.0 / double(mAnimTickDuration);
+ anim->mDuration = double(mAnimNumFrames - 1);
+
+ // now generate the tracks for all nodes
+ anim->mNumChannels = static_cast<unsigned int>(mNodes.size());
+ anim->mChannels = new aiNodeAnim *[anim->mNumChannels];
+
+ // FIX: set the array elements to nullptr to ensure proper deletion if an exception is thrown
+ for (unsigned int i = 0; i < anim->mNumChannels; ++i)
+ anim->mChannels[i] = nullptr;
+
+ for (unsigned int a = 0; a < anim->mNumChannels; a++) {
+ const Node &node = mNodes[a];
+ const std::string nodeName = std::string(node.mNode->mName.data);
+ aiNodeAnim *nodeAnim = new aiNodeAnim;
+ anim->mChannels[a] = nodeAnim;
+ nodeAnim->mNodeName.Set(nodeName);
+ std::map<BVHLoader::ChannelType, int> channelMap;
+
+ //Build map of channels
+ for (unsigned int channel = 0; channel < node.mChannels.size(); ++channel) {
+ channelMap[node.mChannels[channel]] = channel;
+ }
+
+ // translational part, if given
+ if (node.mChannels.size() == 6) {
+ nodeAnim->mNumPositionKeys = mAnimNumFrames;
+ nodeAnim->mPositionKeys = new aiVectorKey[mAnimNumFrames];
+ aiVectorKey *poskey = nodeAnim->mPositionKeys;
+ for (unsigned int fr = 0; fr < mAnimNumFrames; ++fr) {
+ poskey->mTime = double(fr);
+
+ // Now compute all translations
+ for (BVHLoader::ChannelType channel = Channel_PositionX; channel <= Channel_PositionZ; channel = (BVHLoader::ChannelType)(channel + 1)) {
+ //Find channel in node
+ std::map<BVHLoader::ChannelType, int>::iterator mapIter = channelMap.find(channel);
+
+ if (mapIter == channelMap.end())
+ throw DeadlyImportError("Missing position channel in node ", nodeName);
+ else {
+ int channelIdx = mapIter->second;
+ switch (channel) {
+ case Channel_PositionX:
+ poskey->mValue.x = node.mChannelValues[fr * node.mChannels.size() + channelIdx];
+ break;
+ case Channel_PositionY:
+ poskey->mValue.y = node.mChannelValues[fr * node.mChannels.size() + channelIdx];
+ break;
+ case Channel_PositionZ:
+ poskey->mValue.z = node.mChannelValues[fr * node.mChannels.size() + channelIdx];
+ break;
+
+ default:
+ break;
+ }
+ }
+ }
+ ++poskey;
+ }
+ } else {
+ // if no translation part is given, put a default sequence
+ aiVector3D nodePos(node.mNode->mTransformation.a4, node.mNode->mTransformation.b4, node.mNode->mTransformation.c4);
+ nodeAnim->mNumPositionKeys = 1;
+ nodeAnim->mPositionKeys = new aiVectorKey[1];
+ nodeAnim->mPositionKeys[0].mTime = 0.0;
+ nodeAnim->mPositionKeys[0].mValue = nodePos;
+ }
+
+ // rotation part. Always present. First find value offsets
+ {
+
+ // Then create the number of rotation keys
+ nodeAnim->mNumRotationKeys = mAnimNumFrames;
+ nodeAnim->mRotationKeys = new aiQuatKey[mAnimNumFrames];
+ aiQuatKey *rotkey = nodeAnim->mRotationKeys;
+ for (unsigned int fr = 0; fr < mAnimNumFrames; ++fr) {
+ aiMatrix4x4 temp;
+ aiMatrix3x3 rotMatrix;
+ for (unsigned int channelIdx = 0; channelIdx < node.mChannels.size(); ++ channelIdx) {
+ switch (node.mChannels[channelIdx]) {
+ case Channel_RotationX:
+ {
+ const float angle = node.mChannelValues[fr * node.mChannels.size() + channelIdx] * float(AI_MATH_PI) / 180.0f;
+ aiMatrix4x4::RotationX( angle, temp); rotMatrix *= aiMatrix3x3( temp);
+ }
+ break;
+ case Channel_RotationY:
+ {
+ const float angle = node.mChannelValues[fr * node.mChannels.size() + channelIdx] * float(AI_MATH_PI) / 180.0f;
+ aiMatrix4x4::RotationY( angle, temp); rotMatrix *= aiMatrix3x3( temp);
+ }
+ break;
+ case Channel_RotationZ:
+ {
+ const float angle = node.mChannelValues[fr * node.mChannels.size() + channelIdx] * float(AI_MATH_PI) / 180.0f;
+ aiMatrix4x4::RotationZ( angle, temp); rotMatrix *= aiMatrix3x3( temp);
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ rotkey->mTime = double(fr);
+ rotkey->mValue = aiQuaternion(rotMatrix);
+ ++rotkey;
+ }
+ }
+
+ // scaling part. Always just a default track
+ {
+ nodeAnim->mNumScalingKeys = 1;
+ nodeAnim->mScalingKeys = new aiVectorKey[1];
+ nodeAnim->mScalingKeys[0].mTime = 0.0;
+ nodeAnim->mScalingKeys[0].mValue.Set(1.0f, 1.0f, 1.0f);
+ }
+ }
+}
+
+#endif // !! ASSIMP_BUILD_NO_BVH_IMPORTER