Adding very basic reading support for CSM (CharacterStudio Motion). No proper hierarchy reconstruction yet, just plain marker import.

git-svn-id: https://assimp.svn.sourceforge.net/svnroot/assimp/trunk@408 67173fc5-114c-0410-ac8e-9d2fd5bffc1f
pull/1/head
aramis_acg 2009-04-26 15:09:03 +00:00
parent 55a3d845c9
commit d7b4dd6ae3
12 changed files with 2929 additions and 202 deletions

View File

@ -95,6 +95,11 @@ SOURCE_GROUP(DXF FILES
DXFLoader.h
)
SOURCE_GROUP(CSM FILES
CSMLoader.cpp
CSMLoader.h
)
SOURCE_GROUP(HMP FILES
HMPFileData.h
HMPLoader.cpp
@ -380,6 +385,8 @@ ADD_LIBRARY( assimp SHARED
ConvertToLHProcess.h
DXFLoader.cpp
DXFLoader.h
CSMLoader.cpp
CSMLoader.h
DefaultIOStream.cpp
DefaultIOStream.h
DefaultIOSystem.cpp

283
code/CSMLoader.cpp 100644
View File

@ -0,0 +1,283 @@
/*
---------------------------------------------------------------------------
Open Asset Import Library (ASSIMP)
---------------------------------------------------------------------------
Copyright (c) 2006-2009, ASSIMP Development 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 Development 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.
---------------------------------------------------------------------------
*/
/** @file CSMLoader.cpp
* Implementation of the CSM importer class.
*/
#include "AssimpPCH.h"
#ifndef AI_BUILD_NO_CSM_IMPORTER
#include "CSMLoader.h"
#include "SkeletonMeshBuilder.h"
#include "ParsingUtils.h"
#include "fast_atof.h"
using namespace Assimp;
// ------------------------------------------------------------------------------------------------
// Constructor to be privately used by Importer
CSMImporter::CSMImporter()
{}
// ------------------------------------------------------------------------------------------------
// Destructor, private as well
CSMImporter::~CSMImporter()
{}
// ------------------------------------------------------------------------------------------------
// Returns whether the class can handle the format of the given file.
bool CSMImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const
{
// check file extension
const std::string extension = GetExtension(pFile);
if( extension == "csm")
return true;
if ((checkSig || !extension.length()) && pIOHandler) {
const char* tokens[] = {"$Filename"};
return SearchFileHeaderForToken(pIOHandler,pFile,tokens,1);
}
return false;
}
// ------------------------------------------------------------------------------------------------
// Build a string of all file extensions supported
void CSMImporter::GetExtensionList(std::string& append)
{
append.append("*.csm");
}
// ------------------------------------------------------------------------------------------------
// Setup configuration properties for the loader
void CSMImporter::SetupProperties(const Importer* pImp)
{
// nothing to be done for the moment
}
// ------------------------------------------------------------------------------------------------
// Imports the given file into the given scene structure.
void CSMImporter::InternReadFile( const std::string& pFile,
aiScene* pScene, IOSystem* pIOHandler)
{
boost::scoped_ptr<IOStream> file( pIOHandler->Open( pFile, "rb"));
// Check whether we can read from the file
if( file.get() == NULL)
throw new ImportErrorException( "Failed to open CSM file " + pFile + ".");
size_t fileSize = file->FileSize();
// allocate storage and copy the contents of the file to a memory buffer
std::vector<char> mBuffer2(fileSize+1);
file->Read(&mBuffer2[0], 1, fileSize);mBuffer2[fileSize] = '\0';
const char* buffer = &mBuffer2[0];
aiAnimation* anim = new aiAnimation();
int first = 0, last = 0x00ffffff;
// now process the file and look out for '$' sections
while (1) {
SkipSpaces(&buffer);
if ('\0' == *buffer)
break;
if ('$' == *buffer) {
++buffer;
if (TokenMatchI(buffer,"firstframe",10)) {
SkipSpaces(&buffer);
first = strtol10s(buffer,&buffer);
}
else if (TokenMatchI(buffer,"lastframe",9)) {
SkipSpaces(&buffer);
last = strtol10s(buffer,&buffer);
}
else if (TokenMatchI(buffer,"rate",4)) {
SkipSpaces(&buffer);
float d;
buffer = fast_atof_move(buffer,d);
anim->mTicksPerSecond = d;
}
else if (TokenMatchI(buffer,"order",5)) {
std::vector< aiNodeAnim* > anims_temp;
anims_temp.reserve(30);
while (1) {
SkipSpaces(&buffer);
if (IsLineEnd(*buffer) && SkipSpacesAndLineEnd(&buffer) && *buffer == '$')
break; // next section
// Construct a new node animation channel and setup its name
anims_temp.push_back(new aiNodeAnim());
aiNodeAnim* nda = anims_temp.back();
char* ot = nda->mNodeName.data;
while (!IsSpaceOrNewLine(*buffer))
*ot++ = *buffer++;
*ot = '\0';
nda->mNodeName.length = (size_t)(ot-nda->mNodeName.data);
}
anim->mNumChannels = anims_temp.size();
if (!anim->mNumChannels)
throw new ImportErrorException("CSM: Empty $order section");
// copy over to the output animation
anim->mChannels = new aiNodeAnim*[anim->mNumChannels];
::memcpy(anim->mChannels,&anims_temp[0],sizeof(aiNodeAnim*)*anim->mNumChannels);
}
else if (TokenMatchI(buffer,"points",6)) {
if (!anim->mNumChannels)
throw new ImportErrorException("CSM: \'$order\' section is required to appear prior to \'$points\'");
// If we know how many frames we'll read, we can preallocate some storage
unsigned int alloc = 100;
if (last != 0x00ffffff)
{
alloc = last-first;
alloc += alloc>>2u; // + 25%
for (unsigned int i = 0; i < anim->mNumChannels;++i)
anim->mChannels[i]->mPositionKeys = new aiVectorKey[alloc];
}
unsigned int filled = 0;
// Now read all point data.
while (1) {
SkipSpaces(&buffer);
if (IsLineEnd(*buffer) && (!SkipSpacesAndLineEnd(&buffer) || *buffer == '$')) {
break; // next section
}
// read frame
const int frame = ::strtol10(buffer,&buffer);
last = std::max(frame,last);
first = std::min(frame,last);
for (unsigned int i = 0; i < anim->mNumChannels;++i) {
aiNodeAnim* s = anim->mChannels[i];
if (s->mNumPositionKeys == alloc) { /* need to reallocate? */
aiVectorKey* old = s->mPositionKeys;
s->mPositionKeys = new aiVectorKey[s->mNumPositionKeys = alloc*2];
::memcpy(s->mPositionKeys,old,sizeof(aiVectorKey)*alloc);
delete[] old;
}
// read x,y,z
if(!SkipSpacesAndLineEnd(&buffer))
throw new ImportErrorException("CSM: Unexpected EOF occured reading sample x coord");
if (TokenMatchI(buffer, "DROPOUT", 7)) {
// seems this is invalid marker data; at least the doc says it's possible
DefaultLogger::get()->warn("CSM: Encountered invalid marker data (DROPOUT)");
}
else {
aiVectorKey* sub = s->mPositionKeys + s->mNumPositionKeys;
sub->mTime = (double)frame;
buffer = fast_atof_move(buffer, (float&)sub->mValue.x);
if(!SkipSpacesAndLineEnd(&buffer))
throw new ImportErrorException("CSM: Unexpected EOF occured reading sample y coord");
buffer = fast_atof_move(buffer, (float&)sub->mValue.y);
if(!SkipSpacesAndLineEnd(&buffer))
throw new ImportErrorException("CSM: Unexpected EOF occured reading sample z coord");
buffer = fast_atof_move(buffer, (float&)sub->mValue.z);
++s->mNumPositionKeys;
}
}
// update allocation granularity
if (filled == alloc)
alloc *= 2;
++filled;
}
// all channels must be complete in order to continue safely.
for (unsigned int i = 0; i < anim->mNumChannels;++i) {
if (!anim->mChannels[i]->mNumPositionKeys)
throw new ImportErrorException("CSM: Invalid marker track");
}
}
}
else {
// advance to the next line
SkipLine(&buffer);
}
}
// Setup a proper animation duration
anim->mDuration = last - std::min( first, 0 );
// build a dummy root node with the tiny markers as children
pScene->mRootNode = new aiNode();
pScene->mRootNode->mName.Set("$CSM_DummyRoot");
pScene->mRootNode->mNumChildren = anim->mNumChannels;
pScene->mRootNode->mChildren = new aiNode* [anim->mNumChannels];
for (unsigned int i = 0; i < anim->mNumChannels;++i) {
aiNodeAnim* na = anim->mChannels[i];
aiNode* nd = pScene->mRootNode->mChildren[i] = new aiNode();
nd->mName = anim->mChannels[i]->mNodeName;
nd->mParent = pScene->mRootNode;
aiMatrix4x4::Translation(na->mPositionKeys[0].mValue, nd->mTransformation);
}
// Store the one and only animation in the scene
pScene->mAnimations = new aiAnimation*[pScene->mNumAnimations=1];
pScene->mAnimations[0] = anim;
anim->mName.Set("$CSM_MasterAnim");
// mark the scene as incomplete and run SkeletonMeshBuilder on it
pScene->mFlags |= AI_SCENE_FLAGS_INCOMPLETE;
SkeletonMeshBuilder maker(pScene,pScene->mRootNode,true);
}
#endif // !! AI_BUILD_NO_CSM_IMPORTER

96
code/CSMLoader.h 100644
View File

@ -0,0 +1,96 @@
/*
Open Asset Import Library (ASSIMP)
----------------------------------------------------------------------
Copyright (c) 2006-2008, ASSIMP Development 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 Development 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.
----------------------------------------------------------------------
*/
/** @file CSMLoader.h
* Declaration of the CharacterStudio Motion importer class.
*/
#ifndef INCLUDED_AI_CSM_LOADER_H
#define INCLUDED_AI_CSM_LOADER_H
#include "BaseImporter.h"
namespace Assimp {
// ---------------------------------------------------------------------------
/** @brief Importer class to load MOCAPs in CharacterStudio Motion format.
*
* A very rudimentary loader for the moment. No support for the hierarchy,
* every marker is returned as child of root.
*
* Link to file format specification:
* <max_8_dvd>\samples\Motion\Docs\CSM.rtf
*/
class CSMImporter : public BaseImporter
{
friend class Importer;
protected:
/** Constructor to be privately used by Importer */
CSMImporter();
/** Destructor, private as well */
~CSMImporter();
public:
// -------------------------------------------------------------------
bool CanRead( const std::string& pFile, IOSystem* pIOHandler,
bool checkSig) const;
protected:
// -------------------------------------------------------------------
void GetExtensionList(std::string& append);
// -------------------------------------------------------------------
void SetupProperties(const Importer* pImp);
// -------------------------------------------------------------------
void InternReadFile( const std::string& pFile, aiScene* pScene,
IOSystem* pIOHandler);
private:
}; // !class CSMImporter
} // end of namespace Assimp
#endif // AI_AC3DIMPORTER_H_INC

View File

@ -143,9 +143,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef AI_BUILD_NO_TERRAGEN_IMPORTER
# include "TerragenLoader.h"
#endif
//#ifndef AI_BUILD_NO_CSM_IMPORTER
//# include "CSMLoader.h"
//#endif
#ifndef AI_BUILD_NO_CSM_IMPORTER
# include "CSMLoader.h"
#endif
#ifndef AI_BUILD_NO_3D_IMPORTER
# include "UnrealLoader.h"
#endif
@ -351,9 +351,9 @@ Importer::Importer()
#if (!defined AI_BUILD_NO_TERRAGEN_IMPORTER)
pimpl->mImporter.push_back( new TerragenImporter());
#endif
//#if (!defined AI_BUILD_NO_CSM_IMPORTER)
// mImporter.push_back( new CSMImporter());
//#endif
#if (!defined AI_BUILD_NO_CSM_IMPORTER)
pimpl->mImporter.push_back( new CSMImporter());
#endif
#if (!defined AI_BUILD_NO_3D_IMPORTER)
pimpl->mImporter.push_back( new UnrealImporter());
#endif

View File

@ -1,5 +1,3 @@
/** Implementation of a little class to construct a dummy mesh for a skeleton */
/*
Open Asset Import Library (ASSIMP)
----------------------------------------------------------------------
@ -40,6 +38,10 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
----------------------------------------------------------------------
*/
/** @file SkeletonMeshBuilder.cpp
* @brief Implementation of a little class to construct a dummy mesh for a skeleton
*/
#include "AssimpPCH.h"
#include "../include/aiScene.h"
#include "SkeletonMeshBuilder.h"
@ -48,7 +50,7 @@ using namespace Assimp;
// ------------------------------------------------------------------------------------------------
// The constructor processes the given scene and adds a mesh there.
SkeletonMeshBuilder::SkeletonMeshBuilder( aiScene* pScene, aiNode* root)
SkeletonMeshBuilder::SkeletonMeshBuilder( aiScene* pScene, aiNode* root, bool bKnobsOnly)
{
// nothing to do if there's mesh data already present at the scene
if( pScene->mNumMeshes > 0 || pScene->mRootNode == NULL)
@ -57,6 +59,8 @@ SkeletonMeshBuilder::SkeletonMeshBuilder( aiScene* pScene, aiNode* root)
if (!root)
root = pScene->mRootNode;
mKnobsOnly = bKnobsOnly;
// build some faces around each node
CreateGeometry( root );
@ -83,7 +87,7 @@ void SkeletonMeshBuilder::CreateGeometry( const aiNode* pNode)
const unsigned int vertexStartIndex = mVertices.size();
// now build the geometry.
if( pNode->mNumChildren > 0)
if( pNode->mNumChildren > 0 && !mKnobsOnly)
{
// If the node has children, we build little pointers to each of them
for( unsigned int a = 0; a < pNode->mNumChildren; a++)
@ -103,7 +107,7 @@ void SkeletonMeshBuilder::CreateGeometry( const aiNode* pNode)
aiVector3D front = (up ^ orth).Normalize();
aiVector3D side = (front ^ up).Normalize();
unsigned int localVertexStart = mVertices.size();
unsigned int localVertexStart = mVertices.size();
mVertices.push_back( -front * distanceToChild * 0.1f);
mVertices.push_back( childpos);
mVertices.push_back( -side * distanceToChild * 0.1f);
@ -222,10 +226,8 @@ aiMesh* SkeletonMeshBuilder::CreateMesh()
outface.mIndices[1] = inface.mIndices[1];
outface.mIndices[2] = inface.mIndices[2];
// Compute per-face normals ... we don't want the bones to be
// smoothed ... they're built to visualize the skeleton,
// so it's good if there's a visual difference to the rest
// of the geometry
// Compute per-face normals ... we don't want the bones to be smoothed ... they're built to visualize
// the skeleton, so it's good if there's a visual difference to the rest of the geometry
aiVector3D nor = ((mVertices[inface.mIndices[2]] - mVertices[inface.mIndices[0]]) ^
(mVertices[inface.mIndices[1]] - mVertices[inface.mIndices[0]]));

View File

@ -40,6 +40,11 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
----------------------------------------------------------------------
*/
/** @file SkeletonMeshBuilder.h
* Declares SkeletonMeshBuilder, a tiny utility to build dummy meshes
* for animation skeletons.
*/
#ifndef AI_SKELETONMESHBUILDER_H_INC
#define AI_SKELETONMESHBUILDER_H_INC
@ -49,9 +54,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
struct aiScene;
struct aiNode;
namespace Assimp
{
namespace Assimp {
// ---------------------------------------------------------------------------
/**
* This little helper class constructs a dummy mesh for a given scene
* the resembles the node hierarchy. This is useful for file formats
@ -60,42 +65,56 @@ namespace Assimp
class ASSIMP_API SkeletonMeshBuilder
{
public:
/** The constructor processes the given scene and adds a mesh there. Does nothing
* if the scene already has mesh data.
* @param pScene The scene for which a skeleton mesh should be constructed.
* @param root The node to start with. NULL is the scene root
*/
SkeletonMeshBuilder( aiScene* pScene, aiNode* root = NULL);
// -------------------------------------------------------------------
/** The constructor processes the given scene and adds a mesh there.
*
* Does nothing if the scene already has mesh data.
* @param pScene The scene for which a skeleton mesh should be constructed.
* @param root The node to start with. NULL is the scene root
* @param bKnobsOnly Set this to true if you don't want the connectors
* between the knobs representing the nodes.
*/
SkeletonMeshBuilder( aiScene* pScene, aiNode* root = NULL,
bool bKnobsOnly = false);
protected:
/** Recursively builds a simple mesh representation for the given node and also creates
* a joint for the node that affects this part of the mesh.
* @param pNode The node to build geometry for.
*/
void CreateGeometry( const aiNode* pNode);
/** Creates the mesh from the internally accumulated stuff and returns it. */
aiMesh* CreateMesh();
// -------------------------------------------------------------------
/** Recursively builds a simple mesh representation for the given node
* and also creates a joint for the node that affects this part of
* the mesh.
* @param pNode The node to build geometry for.
*/
void CreateGeometry( const aiNode* pNode);
/** Creates a dummy material and returns it. */
aiMaterial* CreateMaterial();
// -------------------------------------------------------------------
/** Creates the mesh from the internally accumulated stuff and returns it.
*/
aiMesh* CreateMesh();
// -------------------------------------------------------------------
/** Creates a dummy material and returns it. */
aiMaterial* CreateMaterial();
protected:
/** space to assemble the mesh data: points */
std::vector<aiVector3D> mVertices;
/** space to assemble the mesh data: points */
std::vector<aiVector3D> mVertices;
/** faces */
struct Face
{
unsigned int mIndices[3];
Face();
Face( unsigned int p0, unsigned int p1, unsigned int p2)
{ mIndices[0] = p0; mIndices[1] = p1; mIndices[2] = p2; }
};
std::vector<Face> mFaces;
/** bones */
std::vector<aiBone*> mBones;
/** faces */
struct Face
{
unsigned int mIndices[3];
Face();
Face( unsigned int p0, unsigned int p1, unsigned int p2)
{ mIndices[0] = p0; mIndices[1] = p1; mIndices[2] = p2; }
};
std::vector<Face> mFaces;
/** bones */
std::vector<aiBone*> mBones;
bool mKnobsOnly;
};
} // end of namespace Assimp

View File

@ -520,20 +520,6 @@ enum aiComponent
#define AI_CONFIG_IMPORT_MD5_NO_ANIM_AUTOLOAD \
"IMPORT_MD5_NO_ANIM_AUTOLOAD"
#if 0
// ---------------------------------------------------------------------------
/** @brief Specifies the shape of the scene returned by the CSM format loader.
*
* If this property is set to 1, the loader tries to build a hierarchy from
* the capture points laoded from the file. A dummy mesh representing the
* recorded human is build. Otherwise, no meshes are returned, there's just
* a single root node with several children. These children represent the
* capture points, their translation channel is absolute.
* Property type: integer. Default value: 1
*/
#define AI_CONFIG_IMPORT_CSM_BUILD_HIERARCHY "imp.csm.mkhier"
#endif
// ---------------------------------------------------------------------------
/** @brief Defines the begin of the time range for which the LWS loader
* evaluates animations and computes aiNodeAnim's.

View File

@ -157,7 +157,6 @@ public:
void FromEulerAnglesXYZ(float x, float y, float z);
void FromEulerAnglesXYZ(const aiVector3D& blubb);
public:
// -------------------------------------------------------------------
/** @brief Returns a rotation matrix for a rotation around the x axis
@ -209,7 +208,6 @@ public:
*/
static aiMatrix4x4& Scaling( const aiVector3D& v, aiMatrix4x4& out);
// -------------------------------------------------------------------
/** @brief A function for creating a rotation matrix that rotates a
* vector called "from" into another vector called "to".

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1 @@
Recorded with vicon IQ, july 2008. Free for any purpose.

View File

@ -1328,146 +1328,6 @@
<Filter
Name="sources"
>
<Filter
Name="extra"
>
@ -2087,6 +1947,18 @@
>
</File>
</Filter>
<Filter
Name="csm"
>
<File
RelativePath="..\..\code\CSMLoader.cpp"
>
</File>
<File
RelativePath="..\..\code\CSMLoader.h"
>
</File>
</Filter>
</Filter>
<Filter
Name="process"

View File

@ -1910,6 +1910,14 @@
<Filter
Name="csm"
>
<File
RelativePath="..\..\code\CSMLoader.cpp"
>
</File>
<File
RelativePath="..\..\code\CSMLoader.h"
>
</File>
</Filter>
<Filter
Name="unreal"