1 /*
2 ---------------------------------------------------------------------------
3 Open Asset Import Library (assimp)
4 ---------------------------------------------------------------------------
5 
6 Copyright (c) 2006-2015, assimp team
7 
8 All rights reserved.
9 
10 Redistribution and use of this software in source and binary forms,
11 with or without modification, are permitted provided that the following
12 conditions are met:
13 
14 * Redistributions of source code must retain the above
15   copyright notice, this list of conditions and the
16   following disclaimer.
17 
18 * Redistributions in binary form must reproduce the above
19   copyright notice, this list of conditions and the
20   following disclaimer in the documentation and/or other
21   materials provided with the distribution.
22 
23 * Neither the name of the assimp team, nor the names of its
24   contributors may be used to endorse or promote products
25   derived from this software without specific prior
26   written permission of the assimp team.
27 
28 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
29 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
30 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
31 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
32 OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
33 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
34 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
35 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
36 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
37 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
38 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
39 ---------------------------------------------------------------------------
40 */
41 
42 /** @file  CSMLoader.cpp
43  *  Implementation of the CSM importer class.
44  */
45 
46 
47 
48 #ifndef ASSIMP_BUILD_NO_CSM_IMPORTER
49 
50 #include "CSMLoader.h"
51 #include "SkeletonMeshBuilder.h"
52 #include "ParsingUtils.h"
53 #include "fast_atof.h"
54 #include "../include/assimp/Importer.hpp"
55 #include <boost/scoped_ptr.hpp>
56 #include "../include/assimp/IOSystem.hpp"
57 #include "../include/assimp/anim.h"
58 #include "../include/assimp/DefaultLogger.hpp"
59 #include "../include/assimp/scene.h"
60 
61 
62 using namespace Assimp;
63 
64 static const aiImporterDesc desc = {
65     "CharacterStudio Motion Importer (MoCap)",
66     "",
67     "",
68     "",
69     aiImporterFlags_SupportTextFlavour,
70     0,
71     0,
72     0,
73     0,
74     "csm"
75 };
76 
77 
78 // ------------------------------------------------------------------------------------------------
79 // Constructor to be privately used by Importer
CSMImporter()80 CSMImporter::CSMImporter()
81 : noSkeletonMesh()
82 {}
83 
84 // ------------------------------------------------------------------------------------------------
85 // Destructor, private as well
~CSMImporter()86 CSMImporter::~CSMImporter()
87 {}
88 
89 // ------------------------------------------------------------------------------------------------
90 // Returns whether the class can handle the format of the given file.
CanRead(const std::string & pFile,IOSystem * pIOHandler,bool checkSig) const91 bool CSMImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const
92 {
93     // check file extension
94     const std::string extension = GetExtension(pFile);
95 
96     if( extension == "csm")
97         return true;
98 
99     if ((checkSig || !extension.length()) && pIOHandler) {
100         const char* tokens[] = {"$Filename"};
101         return SearchFileHeaderForToken(pIOHandler,pFile,tokens,1);
102     }
103     return false;
104 }
105 
106 // ------------------------------------------------------------------------------------------------
107 // Build a string of all file extensions supported
GetInfo() const108 const aiImporterDesc* CSMImporter::GetInfo () const
109 {
110     return &desc;
111 }
112 
113 // ------------------------------------------------------------------------------------------------
114 // Setup configuration properties for the loader
SetupProperties(const Importer * pImp)115 void CSMImporter::SetupProperties(const Importer* pImp)
116 {
117     noSkeletonMesh = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_NO_SKELETON_MESHES,0) != 0;
118 }
119 
120 // ------------------------------------------------------------------------------------------------
121 // Imports the given file into the given scene structure.
InternReadFile(const std::string & pFile,aiScene * pScene,IOSystem * pIOHandler)122 void CSMImporter::InternReadFile( const std::string& pFile,
123     aiScene* pScene, IOSystem* pIOHandler)
124 {
125     boost::scoped_ptr<IOStream> file( pIOHandler->Open( pFile, "rb"));
126 
127     // Check whether we can read from the file
128     if( file.get() == NULL) {
129         throw DeadlyImportError( "Failed to open CSM file " + pFile + ".");
130     }
131 
132     // allocate storage and copy the contents of the file to a memory buffer
133     std::vector<char> mBuffer2;
134     TextFileToBuffer(file.get(),mBuffer2);
135     const char* buffer = &mBuffer2[0];
136 
137     aiAnimation* anim = new aiAnimation();
138     int first = 0, last = 0x00ffffff;
139 
140     // now process the file and look out for '$' sections
141     while (1)   {
142         SkipSpaces(&buffer);
143         if ('\0' == *buffer)
144             break;
145 
146         if ('$'  == *buffer)    {
147             ++buffer;
148             if (TokenMatchI(buffer,"firstframe",10))    {
149                 SkipSpaces(&buffer);
150                 first = strtol10(buffer,&buffer);
151             }
152             else if (TokenMatchI(buffer,"lastframe",9))     {
153                 SkipSpaces(&buffer);
154                 last = strtol10(buffer,&buffer);
155             }
156             else if (TokenMatchI(buffer,"rate",4))  {
157                 SkipSpaces(&buffer);
158                 float d;
159                 buffer = fast_atoreal_move<float>(buffer,d);
160                 anim->mTicksPerSecond = d;
161             }
162             else if (TokenMatchI(buffer,"order",5)) {
163                 std::vector< aiNodeAnim* > anims_temp;
164                 anims_temp.reserve(30);
165                 while (1)   {
166                     SkipSpaces(&buffer);
167                     if (IsLineEnd(*buffer) && SkipSpacesAndLineEnd(&buffer) && *buffer == '$')
168                         break; // next section
169 
170                     // Construct a new node animation channel and setup its name
171                     anims_temp.push_back(new aiNodeAnim());
172                     aiNodeAnim* nda = anims_temp.back();
173 
174                     char* ot = nda->mNodeName.data;
175                     while (!IsSpaceOrNewLine(*buffer))
176                         *ot++ = *buffer++;
177 
178                     *ot = '\0';
179                     nda->mNodeName.length = (size_t)(ot-nda->mNodeName.data);
180                 }
181 
182                 anim->mNumChannels = anims_temp.size();
183                 if (!anim->mNumChannels)
184                     throw DeadlyImportError("CSM: Empty $order section");
185 
186                 // copy over to the output animation
187                 anim->mChannels = new aiNodeAnim*[anim->mNumChannels];
188                 ::memcpy(anim->mChannels,&anims_temp[0],sizeof(aiNodeAnim*)*anim->mNumChannels);
189             }
190             else if (TokenMatchI(buffer,"points",6))    {
191                 if (!anim->mNumChannels)
192                     throw DeadlyImportError("CSM: \'$order\' section is required to appear prior to \'$points\'");
193 
194                 // If we know how many frames we'll read, we can preallocate some storage
195                 unsigned int alloc = 100;
196                 if (last != 0x00ffffff)
197                 {
198                     alloc = last-first;
199                     alloc += alloc>>2u; // + 25%
200                     for (unsigned int i = 0; i < anim->mNumChannels;++i)
201                         anim->mChannels[i]->mPositionKeys = new aiVectorKey[alloc];
202                 }
203 
204                 unsigned int filled = 0;
205 
206                 // Now read all point data.
207                 while (1)   {
208                     SkipSpaces(&buffer);
209                     if (IsLineEnd(*buffer) && (!SkipSpacesAndLineEnd(&buffer) || *buffer == '$'))   {
210                         break; // next section
211                     }
212 
213                     // read frame
214                     const int frame = ::strtoul10(buffer,&buffer);
215                     last  = std::max(frame,last);
216                     first = std::min(frame,last);
217                     for (unsigned int i = 0; i < anim->mNumChannels;++i)    {
218 
219                         aiNodeAnim* s = anim->mChannels[i];
220                         if (s->mNumPositionKeys == alloc)   { /* need to reallocate? */
221 
222                             aiVectorKey* old = s->mPositionKeys;
223                             s->mPositionKeys = new aiVectorKey[s->mNumPositionKeys = alloc*2];
224                             ::memcpy(s->mPositionKeys,old,sizeof(aiVectorKey)*alloc);
225                             delete[] old;
226                         }
227 
228                         // read x,y,z
229                         if(!SkipSpacesAndLineEnd(&buffer))
230                             throw DeadlyImportError("CSM: Unexpected EOF occured reading sample x coord");
231 
232                         if (TokenMatchI(buffer, "DROPOUT", 7))  {
233                             // seems this is invalid marker data; at least the doc says it's possible
234                             DefaultLogger::get()->warn("CSM: Encountered invalid marker data (DROPOUT)");
235                         }
236                         else    {
237                             aiVectorKey* sub = s->mPositionKeys + s->mNumPositionKeys;
238                             sub->mTime = (double)frame;
239                             buffer = fast_atoreal_move<float>(buffer, (float&)sub->mValue.x);
240 
241                             if(!SkipSpacesAndLineEnd(&buffer))
242                                 throw DeadlyImportError("CSM: Unexpected EOF occured reading sample y coord");
243                             buffer = fast_atoreal_move<float>(buffer, (float&)sub->mValue.y);
244 
245                             if(!SkipSpacesAndLineEnd(&buffer))
246                                 throw DeadlyImportError("CSM: Unexpected EOF occured reading sample z coord");
247                             buffer = fast_atoreal_move<float>(buffer, (float&)sub->mValue.z);
248 
249                             ++s->mNumPositionKeys;
250                         }
251                     }
252 
253                     // update allocation granularity
254                     if (filled == alloc)
255                         alloc *= 2;
256 
257                     ++filled;
258                 }
259                 // all channels must be complete in order to continue safely.
260                 for (unsigned int i = 0; i < anim->mNumChannels;++i)    {
261 
262                     if (!anim->mChannels[i]->mNumPositionKeys)
263                         throw DeadlyImportError("CSM: Invalid marker track");
264                 }
265             }
266         }
267         else    {
268             // advance to the next line
269             SkipLine(&buffer);
270         }
271     }
272 
273     // Setup a proper animation duration
274     anim->mDuration = last - std::min( first, 0 );
275 
276     // build a dummy root node with the tiny markers as children
277     pScene->mRootNode = new aiNode();
278     pScene->mRootNode->mName.Set("$CSM_DummyRoot");
279 
280     pScene->mRootNode->mNumChildren = anim->mNumChannels;
281     pScene->mRootNode->mChildren = new aiNode* [anim->mNumChannels];
282 
283     for (unsigned int i = 0; i < anim->mNumChannels;++i)    {
284         aiNodeAnim* na = anim->mChannels[i];
285 
286         aiNode* nd  = pScene->mRootNode->mChildren[i] = new aiNode();
287         nd->mName   = anim->mChannels[i]->mNodeName;
288         nd->mParent = pScene->mRootNode;
289 
290         aiMatrix4x4::Translation(na->mPositionKeys[0].mValue, nd->mTransformation);
291     }
292 
293     // Store the one and only animation in the scene
294     pScene->mAnimations    = new aiAnimation*[pScene->mNumAnimations=1];
295     pScene->mAnimations[0] = anim;
296     anim->mName.Set("$CSM_MasterAnim");
297 
298     // mark the scene as incomplete and run SkeletonMeshBuilder on it
299     pScene->mFlags |= AI_SCENE_FLAGS_INCOMPLETE;
300 
301     if (!noSkeletonMesh) {
302         SkeletonMeshBuilder maker(pScene,pScene->mRootNode,true);
303     }
304 }
305 
306 #endif // !! ASSIMP_BUILD_NO_CSM_IMPORTER
307