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