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