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