rev |
line source |
nuclear@0
|
1 /*
|
nuclear@0
|
2 Open Asset Import Library (assimp)
|
nuclear@0
|
3 ----------------------------------------------------------------------
|
nuclear@0
|
4
|
nuclear@0
|
5 Copyright (c) 2006-2012, assimp team
|
nuclear@0
|
6 All rights reserved.
|
nuclear@0
|
7
|
nuclear@0
|
8 Redistribution and use of this software in source and binary forms,
|
nuclear@0
|
9 with or without modification, are permitted provided that the
|
nuclear@0
|
10 following conditions are met:
|
nuclear@0
|
11
|
nuclear@0
|
12 * Redistributions of source code must retain the above
|
nuclear@0
|
13 copyright notice, this list of conditions and the
|
nuclear@0
|
14 following disclaimer.
|
nuclear@0
|
15
|
nuclear@0
|
16 * Redistributions in binary form must reproduce the above
|
nuclear@0
|
17 copyright notice, this list of conditions and the
|
nuclear@0
|
18 following disclaimer in the documentation and/or other
|
nuclear@0
|
19 materials provided with the distribution.
|
nuclear@0
|
20
|
nuclear@0
|
21 * Neither the name of the assimp team, nor the names of its
|
nuclear@0
|
22 contributors may be used to endorse or promote products
|
nuclear@0
|
23 derived from this software without specific prior
|
nuclear@0
|
24 written permission of the assimp team.
|
nuclear@0
|
25
|
nuclear@0
|
26 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
nuclear@0
|
27 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
nuclear@0
|
28 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
nuclear@0
|
29 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
nuclear@0
|
30 OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
nuclear@0
|
31 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
nuclear@0
|
32 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
nuclear@0
|
33 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
nuclear@0
|
34 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
nuclear@0
|
35 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
nuclear@0
|
36 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
nuclear@0
|
37
|
nuclear@0
|
38 ----------------------------------------------------------------------
|
nuclear@0
|
39 */
|
nuclear@0
|
40
|
nuclear@0
|
41 /** @file FBXDocument.cpp
|
nuclear@0
|
42 * @brief Implementation of the FBX DOM classes
|
nuclear@0
|
43 */
|
nuclear@0
|
44 #include "AssimpPCH.h"
|
nuclear@0
|
45
|
nuclear@0
|
46 #ifndef ASSIMP_BUILD_NO_FBX_IMPORTER
|
nuclear@0
|
47
|
nuclear@0
|
48 #include <functional>
|
nuclear@0
|
49
|
nuclear@0
|
50 #include "FBXParser.h"
|
nuclear@0
|
51 #include "FBXDocument.h"
|
nuclear@0
|
52 #include "FBXUtil.h"
|
nuclear@0
|
53 #include "FBXImporter.h"
|
nuclear@0
|
54 #include "FBXImportSettings.h"
|
nuclear@0
|
55 #include "FBXDocumentUtil.h"
|
nuclear@0
|
56 #include "FBXProperties.h"
|
nuclear@0
|
57
|
nuclear@0
|
58 namespace Assimp {
|
nuclear@0
|
59 namespace FBX {
|
nuclear@0
|
60
|
nuclear@0
|
61 using namespace Util;
|
nuclear@0
|
62
|
nuclear@0
|
63 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
64 LazyObject::LazyObject(uint64_t id, const Element& element, const Document& doc)
|
nuclear@0
|
65 : doc(doc)
|
nuclear@0
|
66 , element(element)
|
nuclear@0
|
67 , id(id)
|
nuclear@0
|
68 , flags()
|
nuclear@0
|
69 {
|
nuclear@0
|
70
|
nuclear@0
|
71 }
|
nuclear@0
|
72
|
nuclear@0
|
73 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
74 LazyObject::~LazyObject()
|
nuclear@0
|
75 {
|
nuclear@0
|
76
|
nuclear@0
|
77 }
|
nuclear@0
|
78
|
nuclear@0
|
79 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
80 const Object* LazyObject::Get(bool dieOnError)
|
nuclear@0
|
81 {
|
nuclear@0
|
82 if(IsBeingConstructed() || FailedToConstruct()) {
|
nuclear@0
|
83 return NULL;
|
nuclear@0
|
84 }
|
nuclear@0
|
85
|
nuclear@0
|
86 if (object.get()) {
|
nuclear@0
|
87 return object.get();
|
nuclear@0
|
88 }
|
nuclear@0
|
89
|
nuclear@0
|
90 // if this is the root object, we return a dummy since there
|
nuclear@0
|
91 // is no root object int he fbx file - it is just referenced
|
nuclear@0
|
92 // with id 0.
|
nuclear@0
|
93 if(id == 0L) {
|
nuclear@0
|
94 object.reset(new Object(id, element, "Model::RootNode"));
|
nuclear@0
|
95 return object.get();
|
nuclear@0
|
96 }
|
nuclear@0
|
97
|
nuclear@0
|
98 const Token& key = element.KeyToken();
|
nuclear@0
|
99 const TokenList& tokens = element.Tokens();
|
nuclear@0
|
100
|
nuclear@0
|
101 if(tokens.size() < 3) {
|
nuclear@0
|
102 DOMError("expected at least 3 tokens: id, name and class tag",&element);
|
nuclear@0
|
103 }
|
nuclear@0
|
104
|
nuclear@0
|
105 const char* err;
|
nuclear@0
|
106 std::string name = ParseTokenAsString(*tokens[1],err);
|
nuclear@0
|
107 if (err) {
|
nuclear@0
|
108 DOMError(err,&element);
|
nuclear@0
|
109 }
|
nuclear@0
|
110
|
nuclear@0
|
111 // small fix for binary reading: binary fbx files don't use
|
nuclear@0
|
112 // prefixes such as Model:: in front of their names. The
|
nuclear@0
|
113 // loading code expects this at many places, though!
|
nuclear@0
|
114 // so convert the binary representation (a 0x0001) to the
|
nuclear@0
|
115 // double colon notation.
|
nuclear@0
|
116 if(tokens[1]->IsBinary()) {
|
nuclear@0
|
117 for (size_t i = 0; i < name.length(); ++i) {
|
nuclear@0
|
118 if (name[i] == 0x0 && name[i+1] == 0x1) {
|
nuclear@0
|
119 name = name.substr(i+2) + "::" + name.substr(0,i);
|
nuclear@0
|
120 }
|
nuclear@0
|
121 }
|
nuclear@0
|
122 }
|
nuclear@0
|
123
|
nuclear@0
|
124 const std::string classtag = ParseTokenAsString(*tokens[2],err);
|
nuclear@0
|
125 if (err) {
|
nuclear@0
|
126 DOMError(err,&element);
|
nuclear@0
|
127 }
|
nuclear@0
|
128
|
nuclear@0
|
129 // prevent recursive calls
|
nuclear@0
|
130 flags |= BEING_CONSTRUCTED;
|
nuclear@0
|
131
|
nuclear@0
|
132 try {
|
nuclear@0
|
133 // this needs to be relatively fast since it happens a lot,
|
nuclear@0
|
134 // so avoid constructing strings all the time.
|
nuclear@0
|
135 const char* obtype = key.begin();
|
nuclear@0
|
136 const size_t length = static_cast<size_t>(key.end()-key.begin());
|
nuclear@0
|
137 if (!strncmp(obtype,"Geometry",length)) {
|
nuclear@0
|
138 if (!strcmp(classtag.c_str(),"Mesh")) {
|
nuclear@0
|
139 object.reset(new MeshGeometry(id,element,name,doc));
|
nuclear@0
|
140 }
|
nuclear@0
|
141 }
|
nuclear@0
|
142 else if (!strncmp(obtype,"NodeAttribute",length)) {
|
nuclear@0
|
143 if (!strcmp(classtag.c_str(),"Camera")) {
|
nuclear@0
|
144 object.reset(new Camera(id,element,doc,name));
|
nuclear@0
|
145 }
|
nuclear@0
|
146 else if (!strcmp(classtag.c_str(),"CameraSwitcher")) {
|
nuclear@0
|
147 object.reset(new CameraSwitcher(id,element,doc,name));
|
nuclear@0
|
148 }
|
nuclear@0
|
149 else if (!strcmp(classtag.c_str(),"Light")) {
|
nuclear@0
|
150 object.reset(new Light(id,element,doc,name));
|
nuclear@0
|
151 }
|
nuclear@0
|
152 else if (!strcmp(classtag.c_str(),"Null")) {
|
nuclear@0
|
153 object.reset(new Null(id,element,doc,name));
|
nuclear@0
|
154 }
|
nuclear@0
|
155 else if (!strcmp(classtag.c_str(),"LimbNode")) {
|
nuclear@0
|
156 object.reset(new LimbNode(id,element,doc,name));
|
nuclear@0
|
157 }
|
nuclear@0
|
158 }
|
nuclear@0
|
159 else if (!strncmp(obtype,"Deformer",length)) {
|
nuclear@0
|
160 if (!strcmp(classtag.c_str(),"Cluster")) {
|
nuclear@0
|
161 object.reset(new Cluster(id,element,doc,name));
|
nuclear@0
|
162 }
|
nuclear@0
|
163 else if (!strcmp(classtag.c_str(),"Skin")) {
|
nuclear@0
|
164 object.reset(new Skin(id,element,doc,name));
|
nuclear@0
|
165 }
|
nuclear@0
|
166 }
|
nuclear@0
|
167 else if (!strncmp(obtype,"Model",length)) {
|
nuclear@0
|
168 // FK and IK effectors are not supported
|
nuclear@0
|
169 if (strcmp(classtag.c_str(),"IKEffector") && strcmp(classtag.c_str(),"FKEffector")) {
|
nuclear@0
|
170 object.reset(new Model(id,element,doc,name));
|
nuclear@0
|
171 }
|
nuclear@0
|
172 }
|
nuclear@0
|
173 else if (!strncmp(obtype,"Material",length)) {
|
nuclear@0
|
174 object.reset(new Material(id,element,doc,name));
|
nuclear@0
|
175 }
|
nuclear@0
|
176 else if (!strncmp(obtype,"Texture",length)) {
|
nuclear@0
|
177 object.reset(new Texture(id,element,doc,name));
|
nuclear@0
|
178 }
|
nuclear@0
|
179 else if (!strncmp(obtype,"AnimationStack",length)) {
|
nuclear@0
|
180 object.reset(new AnimationStack(id,element,name,doc));
|
nuclear@0
|
181 }
|
nuclear@0
|
182 else if (!strncmp(obtype,"AnimationLayer",length)) {
|
nuclear@0
|
183 object.reset(new AnimationLayer(id,element,name,doc));
|
nuclear@0
|
184 }
|
nuclear@0
|
185 // note: order matters for these two
|
nuclear@0
|
186 else if (!strncmp(obtype,"AnimationCurve",length)) {
|
nuclear@0
|
187 object.reset(new AnimationCurve(id,element,name,doc));
|
nuclear@0
|
188 }
|
nuclear@0
|
189 else if (!strncmp(obtype,"AnimationCurveNode",length)) {
|
nuclear@0
|
190 object.reset(new AnimationCurveNode(id,element,name,doc));
|
nuclear@0
|
191 }
|
nuclear@0
|
192 }
|
nuclear@0
|
193 catch(std::exception& ex) {
|
nuclear@0
|
194 flags &= ~BEING_CONSTRUCTED;
|
nuclear@0
|
195 flags |= FAILED_TO_CONSTRUCT;
|
nuclear@0
|
196
|
nuclear@0
|
197 if(dieOnError || doc.Settings().strictMode) {
|
nuclear@0
|
198 throw;
|
nuclear@0
|
199 }
|
nuclear@0
|
200
|
nuclear@0
|
201 // note: the error message is already formatted, so raw logging is ok
|
nuclear@0
|
202 if(!DefaultLogger::isNullLogger()) {
|
nuclear@0
|
203 DefaultLogger::get()->error(ex.what());
|
nuclear@0
|
204 }
|
nuclear@0
|
205 return NULL;
|
nuclear@0
|
206 }
|
nuclear@0
|
207
|
nuclear@0
|
208 if (!object.get()) {
|
nuclear@0
|
209 //DOMError("failed to convert element to DOM object, class: " + classtag + ", name: " + name,&element);
|
nuclear@0
|
210 }
|
nuclear@0
|
211
|
nuclear@0
|
212 flags &= ~BEING_CONSTRUCTED;
|
nuclear@0
|
213 return object.get();
|
nuclear@0
|
214 }
|
nuclear@0
|
215
|
nuclear@0
|
216 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
217 Object::Object(uint64_t id, const Element& element, const std::string& name)
|
nuclear@0
|
218 : element(element)
|
nuclear@0
|
219 , name(name)
|
nuclear@0
|
220 , id(id)
|
nuclear@0
|
221 {
|
nuclear@0
|
222
|
nuclear@0
|
223 }
|
nuclear@0
|
224
|
nuclear@0
|
225 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
226 Object::~Object()
|
nuclear@0
|
227 {
|
nuclear@0
|
228
|
nuclear@0
|
229 }
|
nuclear@0
|
230
|
nuclear@0
|
231
|
nuclear@0
|
232 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
233 FileGlobalSettings::FileGlobalSettings(const Document& doc, boost::shared_ptr<const PropertyTable> props)
|
nuclear@0
|
234 : props(props)
|
nuclear@0
|
235 , doc(doc)
|
nuclear@0
|
236 {
|
nuclear@0
|
237
|
nuclear@0
|
238 }
|
nuclear@0
|
239
|
nuclear@0
|
240
|
nuclear@0
|
241 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
242 FileGlobalSettings::~FileGlobalSettings()
|
nuclear@0
|
243 {
|
nuclear@0
|
244
|
nuclear@0
|
245 }
|
nuclear@0
|
246
|
nuclear@0
|
247
|
nuclear@0
|
248 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
249 Document::Document(const Parser& parser, const ImportSettings& settings)
|
nuclear@0
|
250 : settings(settings)
|
nuclear@0
|
251 , parser(parser)
|
nuclear@0
|
252 {
|
nuclear@0
|
253 // cannot use array default initialization syntax because vc8 fails on it
|
nuclear@0
|
254 for (unsigned int i = 0; i < 7; ++i) {
|
nuclear@0
|
255 creationTimeStamp[i] = 0;
|
nuclear@0
|
256 }
|
nuclear@0
|
257
|
nuclear@0
|
258 ReadHeader();
|
nuclear@0
|
259 ReadPropertyTemplates();
|
nuclear@0
|
260
|
nuclear@0
|
261 ReadGlobalSettings();
|
nuclear@0
|
262
|
nuclear@0
|
263 // this order is important, connections need parsed objects to check
|
nuclear@0
|
264 // whether connections are ok or not. Objects may not be evaluated yet,
|
nuclear@0
|
265 // though, since this may require valid connections.
|
nuclear@0
|
266 ReadObjects();
|
nuclear@0
|
267 ReadConnections();
|
nuclear@0
|
268 }
|
nuclear@0
|
269
|
nuclear@0
|
270
|
nuclear@0
|
271 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
272 Document::~Document()
|
nuclear@0
|
273 {
|
nuclear@0
|
274 BOOST_FOREACH(ObjectMap::value_type& v, objects) {
|
nuclear@0
|
275 delete v.second;
|
nuclear@0
|
276 }
|
nuclear@0
|
277 }
|
nuclear@0
|
278
|
nuclear@0
|
279
|
nuclear@0
|
280 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
281 void Document::ReadHeader()
|
nuclear@0
|
282 {
|
nuclear@0
|
283 // read ID objects from "Objects" section
|
nuclear@0
|
284 const Scope& sc = parser.GetRootScope();
|
nuclear@0
|
285 const Element* const ehead = sc["FBXHeaderExtension"];
|
nuclear@0
|
286 if(!ehead || !ehead->Compound()) {
|
nuclear@0
|
287 DOMError("no FBXHeaderExtension dictionary found");
|
nuclear@0
|
288 }
|
nuclear@0
|
289
|
nuclear@0
|
290 const Scope& shead = *ehead->Compound();
|
nuclear@0
|
291 fbxVersion = ParseTokenAsInt(GetRequiredToken(GetRequiredElement(shead,"FBXVersion",ehead),0));
|
nuclear@0
|
292
|
nuclear@0
|
293
|
nuclear@0
|
294 if(fbxVersion < 7200 || fbxVersion > 7300) {
|
nuclear@0
|
295 if(Settings().strictMode) {
|
nuclear@0
|
296 DOMError("unsupported format version, supported are only FBX 2012 and FBX 2013"\
|
nuclear@0
|
297 " in ASCII format (turn off strict mode to try anyhow) ");
|
nuclear@0
|
298 }
|
nuclear@0
|
299 else {
|
nuclear@0
|
300 DOMWarning("unsupported format version, supported are only FBX 2012 and FBX 2013, trying to read it nevertheless");
|
nuclear@0
|
301 }
|
nuclear@0
|
302 }
|
nuclear@0
|
303
|
nuclear@0
|
304
|
nuclear@0
|
305 const Element* const ecreator = shead["Creator"];
|
nuclear@0
|
306 if(ecreator) {
|
nuclear@0
|
307 creator = ParseTokenAsString(GetRequiredToken(*ecreator,0));
|
nuclear@0
|
308 }
|
nuclear@0
|
309
|
nuclear@0
|
310 const Element* const etimestamp = shead["CreationTimeStamp"];
|
nuclear@0
|
311 if(etimestamp && etimestamp->Compound()) {
|
nuclear@0
|
312 const Scope& stimestamp = *etimestamp->Compound();
|
nuclear@0
|
313 creationTimeStamp[0] = ParseTokenAsInt(GetRequiredToken(GetRequiredElement(stimestamp,"Year"),0));
|
nuclear@0
|
314 creationTimeStamp[1] = ParseTokenAsInt(GetRequiredToken(GetRequiredElement(stimestamp,"Month"),0));
|
nuclear@0
|
315 creationTimeStamp[2] = ParseTokenAsInt(GetRequiredToken(GetRequiredElement(stimestamp,"Day"),0));
|
nuclear@0
|
316 creationTimeStamp[3] = ParseTokenAsInt(GetRequiredToken(GetRequiredElement(stimestamp,"Hour"),0));
|
nuclear@0
|
317 creationTimeStamp[4] = ParseTokenAsInt(GetRequiredToken(GetRequiredElement(stimestamp,"Minute"),0));
|
nuclear@0
|
318 creationTimeStamp[5] = ParseTokenAsInt(GetRequiredToken(GetRequiredElement(stimestamp,"Second"),0));
|
nuclear@0
|
319 creationTimeStamp[6] = ParseTokenAsInt(GetRequiredToken(GetRequiredElement(stimestamp,"Millisecond"),0));
|
nuclear@0
|
320 }
|
nuclear@0
|
321 }
|
nuclear@0
|
322
|
nuclear@0
|
323 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
324 void Document::ReadGlobalSettings()
|
nuclear@0
|
325 {
|
nuclear@0
|
326 const Scope& sc = parser.GetRootScope();
|
nuclear@0
|
327 const Element* const ehead = sc["GlobalSettings"];
|
nuclear@0
|
328 if(!ehead || !ehead->Compound()) {
|
nuclear@0
|
329 DOMWarning("no GlobalSettings dictionary found");
|
nuclear@0
|
330
|
nuclear@0
|
331 globals.reset(new FileGlobalSettings(*this, boost::make_shared<const PropertyTable>()));
|
nuclear@0
|
332 return;
|
nuclear@0
|
333 }
|
nuclear@0
|
334
|
nuclear@0
|
335 boost::shared_ptr<const PropertyTable> props = GetPropertyTable(*this, "", *ehead, *ehead->Compound(), true);
|
nuclear@0
|
336
|
nuclear@0
|
337 if(!props) {
|
nuclear@0
|
338 DOMError("GlobalSettings dictionary contains no property table");
|
nuclear@0
|
339 }
|
nuclear@0
|
340
|
nuclear@0
|
341 globals.reset(new FileGlobalSettings(*this, props));
|
nuclear@0
|
342 }
|
nuclear@0
|
343
|
nuclear@0
|
344
|
nuclear@0
|
345 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
346 void Document::ReadObjects()
|
nuclear@0
|
347 {
|
nuclear@0
|
348 // read ID objects from "Objects" section
|
nuclear@0
|
349 const Scope& sc = parser.GetRootScope();
|
nuclear@0
|
350 const Element* const eobjects = sc["Objects"];
|
nuclear@0
|
351 if(!eobjects || !eobjects->Compound()) {
|
nuclear@0
|
352 DOMError("no Objects dictionary found");
|
nuclear@0
|
353 }
|
nuclear@0
|
354
|
nuclear@0
|
355 // add a dummy entry to represent the Model::RootNode object (id 0),
|
nuclear@0
|
356 // which is only indirectly defined in the input file
|
nuclear@0
|
357 objects[0] = new LazyObject(0L, *eobjects, *this);
|
nuclear@0
|
358
|
nuclear@0
|
359 const Scope& sobjects = *eobjects->Compound();
|
nuclear@0
|
360 BOOST_FOREACH(const ElementMap::value_type& el, sobjects.Elements()) {
|
nuclear@0
|
361
|
nuclear@0
|
362 // extract ID
|
nuclear@0
|
363 const TokenList& tok = el.second->Tokens();
|
nuclear@0
|
364
|
nuclear@0
|
365 if (tok.empty()) {
|
nuclear@0
|
366 DOMError("expected ID after object key",el.second);
|
nuclear@0
|
367 }
|
nuclear@0
|
368
|
nuclear@0
|
369 const char* err;
|
nuclear@0
|
370
|
nuclear@0
|
371 const uint64_t id = ParseTokenAsID(*tok[0], err);
|
nuclear@0
|
372 if(err) {
|
nuclear@0
|
373 DOMError(err,el.second);
|
nuclear@0
|
374 }
|
nuclear@0
|
375
|
nuclear@0
|
376 // id=0 is normally implicit
|
nuclear@0
|
377 if(id == 0L) {
|
nuclear@0
|
378 DOMError("encountered object with implicitly defined id 0",el.second);
|
nuclear@0
|
379 }
|
nuclear@0
|
380
|
nuclear@0
|
381 if(objects.find(id) != objects.end()) {
|
nuclear@0
|
382 DOMWarning("encountered duplicate object id, ignoring first occurrence",el.second);
|
nuclear@0
|
383 }
|
nuclear@0
|
384
|
nuclear@0
|
385 objects[id] = new LazyObject(id, *el.second, *this);
|
nuclear@0
|
386
|
nuclear@0
|
387 // grab all animation stacks upfront since there is no listing of them
|
nuclear@0
|
388 if(!strcmp(el.first.c_str(),"AnimationStack")) {
|
nuclear@0
|
389 animationStacks.push_back(id);
|
nuclear@0
|
390 }
|
nuclear@0
|
391 }
|
nuclear@0
|
392 }
|
nuclear@0
|
393
|
nuclear@0
|
394
|
nuclear@0
|
395 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
396 void Document::ReadPropertyTemplates()
|
nuclear@0
|
397 {
|
nuclear@0
|
398 const Scope& sc = parser.GetRootScope();
|
nuclear@0
|
399 // read property templates from "Definitions" section
|
nuclear@0
|
400 const Element* const edefs = sc["Definitions"];
|
nuclear@0
|
401 if(!edefs || !edefs->Compound()) {
|
nuclear@0
|
402 DOMWarning("no Definitions dictionary found");
|
nuclear@0
|
403 return;
|
nuclear@0
|
404 }
|
nuclear@0
|
405
|
nuclear@0
|
406 const Scope& sdefs = *edefs->Compound();
|
nuclear@0
|
407 const ElementCollection otypes = sdefs.GetCollection("ObjectType");
|
nuclear@0
|
408 for(ElementMap::const_iterator it = otypes.first; it != otypes.second; ++it) {
|
nuclear@0
|
409 const Element& el = *(*it).second;
|
nuclear@0
|
410 const Scope* sc = el.Compound();
|
nuclear@0
|
411 if(!sc) {
|
nuclear@0
|
412 DOMWarning("expected nested scope in ObjectType, ignoring",&el);
|
nuclear@0
|
413 continue;
|
nuclear@0
|
414 }
|
nuclear@0
|
415
|
nuclear@0
|
416 const TokenList& tok = el.Tokens();
|
nuclear@0
|
417 if(tok.empty()) {
|
nuclear@0
|
418 DOMWarning("expected name for ObjectType element, ignoring",&el);
|
nuclear@0
|
419 continue;
|
nuclear@0
|
420 }
|
nuclear@0
|
421
|
nuclear@0
|
422 const std::string& oname = ParseTokenAsString(*tok[0]);
|
nuclear@0
|
423
|
nuclear@0
|
424 const ElementCollection templs = sc->GetCollection("PropertyTemplate");
|
nuclear@0
|
425 for(ElementMap::const_iterator it = templs.first; it != templs.second; ++it) {
|
nuclear@0
|
426 const Element& el = *(*it).second;
|
nuclear@0
|
427 const Scope* sc = el.Compound();
|
nuclear@0
|
428 if(!sc) {
|
nuclear@0
|
429 DOMWarning("expected nested scope in PropertyTemplate, ignoring",&el);
|
nuclear@0
|
430 continue;
|
nuclear@0
|
431 }
|
nuclear@0
|
432
|
nuclear@0
|
433 const TokenList& tok = el.Tokens();
|
nuclear@0
|
434 if(tok.empty()) {
|
nuclear@0
|
435 DOMWarning("expected name for PropertyTemplate element, ignoring",&el);
|
nuclear@0
|
436 continue;
|
nuclear@0
|
437 }
|
nuclear@0
|
438
|
nuclear@0
|
439 const std::string& pname = ParseTokenAsString(*tok[0]);
|
nuclear@0
|
440
|
nuclear@0
|
441 const Element* Properties70 = (*sc)["Properties70"];
|
nuclear@0
|
442 if(Properties70) {
|
nuclear@0
|
443 boost::shared_ptr<const PropertyTable> props = boost::make_shared<const PropertyTable>(
|
nuclear@0
|
444 *Properties70,boost::shared_ptr<const PropertyTable>(static_cast<const PropertyTable*>(NULL))
|
nuclear@0
|
445 );
|
nuclear@0
|
446
|
nuclear@0
|
447 templates[oname+"."+pname] = props;
|
nuclear@0
|
448 }
|
nuclear@0
|
449 }
|
nuclear@0
|
450 }
|
nuclear@0
|
451 }
|
nuclear@0
|
452
|
nuclear@0
|
453
|
nuclear@0
|
454
|
nuclear@0
|
455 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
456 void Document::ReadConnections()
|
nuclear@0
|
457 {
|
nuclear@0
|
458 const Scope& sc = parser.GetRootScope();
|
nuclear@0
|
459 // read property templates from "Definitions" section
|
nuclear@0
|
460 const Element* const econns = sc["Connections"];
|
nuclear@0
|
461 if(!econns || !econns->Compound()) {
|
nuclear@0
|
462 DOMError("no Connections dictionary found");
|
nuclear@0
|
463 }
|
nuclear@0
|
464
|
nuclear@0
|
465 uint64_t insertionOrder = 0l;
|
nuclear@0
|
466
|
nuclear@0
|
467 const Scope& sconns = *econns->Compound();
|
nuclear@0
|
468 const ElementCollection conns = sconns.GetCollection("C");
|
nuclear@0
|
469 for(ElementMap::const_iterator it = conns.first; it != conns.second; ++it) {
|
nuclear@0
|
470 const Element& el = *(*it).second;
|
nuclear@0
|
471 const std::string& type = ParseTokenAsString(GetRequiredToken(el,0));
|
nuclear@0
|
472 const uint64_t src = ParseTokenAsID(GetRequiredToken(el,1));
|
nuclear@0
|
473 const uint64_t dest = ParseTokenAsID(GetRequiredToken(el,2));
|
nuclear@0
|
474
|
nuclear@0
|
475 // OO = object-object connection
|
nuclear@0
|
476 // OP = object-property connection, in which case the destination property follows the object ID
|
nuclear@0
|
477 const std::string& prop = (type == "OP" ? ParseTokenAsString(GetRequiredToken(el,3)) : "");
|
nuclear@0
|
478
|
nuclear@0
|
479 if(objects.find(src) == objects.end()) {
|
nuclear@0
|
480 DOMWarning("source object for connection does not exist",&el);
|
nuclear@0
|
481 continue;
|
nuclear@0
|
482 }
|
nuclear@0
|
483
|
nuclear@0
|
484 // dest may be 0 (root node) but we added a dummy object before
|
nuclear@0
|
485 if(objects.find(dest) == objects.end()) {
|
nuclear@0
|
486 DOMWarning("destination object for connection does not exist",&el);
|
nuclear@0
|
487 continue;
|
nuclear@0
|
488 }
|
nuclear@0
|
489
|
nuclear@0
|
490 // add new connection
|
nuclear@0
|
491 const Connection* const c = new Connection(insertionOrder++,src,dest,prop,*this);
|
nuclear@0
|
492 src_connections.insert(ConnectionMap::value_type(src,c));
|
nuclear@0
|
493 dest_connections.insert(ConnectionMap::value_type(dest,c));
|
nuclear@0
|
494 }
|
nuclear@0
|
495 }
|
nuclear@0
|
496
|
nuclear@0
|
497
|
nuclear@0
|
498 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
499 const std::vector<const AnimationStack*>& Document::AnimationStacks() const
|
nuclear@0
|
500 {
|
nuclear@0
|
501 if (!animationStacksResolved.empty() || !animationStacks.size()) {
|
nuclear@0
|
502 return animationStacksResolved;
|
nuclear@0
|
503 }
|
nuclear@0
|
504
|
nuclear@0
|
505 animationStacksResolved.reserve(animationStacks.size());
|
nuclear@0
|
506 BOOST_FOREACH(uint64_t id, animationStacks) {
|
nuclear@0
|
507 LazyObject* const lazy = GetObject(id);
|
nuclear@0
|
508 const AnimationStack* stack;
|
nuclear@0
|
509 if(!lazy || !(stack = lazy->Get<AnimationStack>())) {
|
nuclear@0
|
510 DOMWarning("failed to read AnimationStack object");
|
nuclear@0
|
511 continue;
|
nuclear@0
|
512 }
|
nuclear@0
|
513 animationStacksResolved.push_back(stack);
|
nuclear@0
|
514 }
|
nuclear@0
|
515
|
nuclear@0
|
516 return animationStacksResolved;
|
nuclear@0
|
517 }
|
nuclear@0
|
518
|
nuclear@0
|
519
|
nuclear@0
|
520 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
521 LazyObject* Document::GetObject(uint64_t id) const
|
nuclear@0
|
522 {
|
nuclear@0
|
523 ObjectMap::const_iterator it = objects.find(id);
|
nuclear@0
|
524 return it == objects.end() ? NULL : (*it).second;
|
nuclear@0
|
525 }
|
nuclear@0
|
526
|
nuclear@0
|
527 #define MAX_CLASSNAMES 6
|
nuclear@0
|
528
|
nuclear@0
|
529 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
530 std::vector<const Connection*> Document::GetConnectionsSequenced(uint64_t id,
|
nuclear@0
|
531 const ConnectionMap& conns) const
|
nuclear@0
|
532 {
|
nuclear@0
|
533 std::vector<const Connection*> temp;
|
nuclear@0
|
534
|
nuclear@0
|
535 const std::pair<ConnectionMap::const_iterator,ConnectionMap::const_iterator> range =
|
nuclear@0
|
536 conns.equal_range(id);
|
nuclear@0
|
537
|
nuclear@0
|
538 temp.reserve(std::distance(range.first,range.second));
|
nuclear@0
|
539 for (ConnectionMap::const_iterator it = range.first; it != range.second; ++it) {
|
nuclear@0
|
540 temp.push_back((*it).second);
|
nuclear@0
|
541 }
|
nuclear@0
|
542
|
nuclear@0
|
543 std::sort(temp.begin(), temp.end(), std::mem_fun(&Connection::Compare));
|
nuclear@0
|
544
|
nuclear@0
|
545 return temp; // NRVO should handle this
|
nuclear@0
|
546 }
|
nuclear@0
|
547
|
nuclear@0
|
548
|
nuclear@0
|
549 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
550 std::vector<const Connection*> Document::GetConnectionsSequenced(uint64_t id, bool is_src,
|
nuclear@0
|
551 const ConnectionMap& conns,
|
nuclear@0
|
552 const char* const* classnames,
|
nuclear@0
|
553 size_t count) const
|
nuclear@0
|
554
|
nuclear@0
|
555 {
|
nuclear@0
|
556 ai_assert(classnames);
|
nuclear@0
|
557 ai_assert(count != 0 && count <= MAX_CLASSNAMES);
|
nuclear@0
|
558
|
nuclear@0
|
559 size_t lenghts[MAX_CLASSNAMES];
|
nuclear@0
|
560
|
nuclear@0
|
561 const size_t c = count;
|
nuclear@0
|
562 for (size_t i = 0; i < c; ++i) {
|
nuclear@0
|
563 lenghts[i] = strlen(classnames[i]);
|
nuclear@0
|
564 }
|
nuclear@0
|
565
|
nuclear@0
|
566 std::vector<const Connection*> temp;
|
nuclear@0
|
567
|
nuclear@0
|
568 const std::pair<ConnectionMap::const_iterator,ConnectionMap::const_iterator> range =
|
nuclear@0
|
569 conns.equal_range(id);
|
nuclear@0
|
570
|
nuclear@0
|
571 temp.reserve(std::distance(range.first,range.second));
|
nuclear@0
|
572 for (ConnectionMap::const_iterator it = range.first; it != range.second; ++it) {
|
nuclear@0
|
573 const Token& key = (is_src
|
nuclear@0
|
574 ? (*it).second->LazyDestinationObject()
|
nuclear@0
|
575 : (*it).second->LazySourceObject()
|
nuclear@0
|
576 ).GetElement().KeyToken();
|
nuclear@0
|
577
|
nuclear@0
|
578 const char* obtype = key.begin();
|
nuclear@0
|
579
|
nuclear@0
|
580 for (size_t i = 0; i < c; ++i) {
|
nuclear@0
|
581 ai_assert(classnames[i]);
|
nuclear@0
|
582 if(static_cast<size_t>(std::distance(key.begin(),key.end())) == lenghts[i] && !strncmp(classnames[i],obtype,lenghts[i])) {
|
nuclear@0
|
583 obtype = NULL;
|
nuclear@0
|
584 break;
|
nuclear@0
|
585 }
|
nuclear@0
|
586 }
|
nuclear@0
|
587
|
nuclear@0
|
588 if(obtype) {
|
nuclear@0
|
589 continue;
|
nuclear@0
|
590 }
|
nuclear@0
|
591
|
nuclear@0
|
592 temp.push_back((*it).second);
|
nuclear@0
|
593 }
|
nuclear@0
|
594
|
nuclear@0
|
595 std::sort(temp.begin(), temp.end(), std::mem_fun(&Connection::Compare));
|
nuclear@0
|
596 return temp; // NRVO should handle this
|
nuclear@0
|
597 }
|
nuclear@0
|
598
|
nuclear@0
|
599
|
nuclear@0
|
600 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
601 std::vector<const Connection*> Document::GetConnectionsBySourceSequenced(uint64_t source) const
|
nuclear@0
|
602 {
|
nuclear@0
|
603 return GetConnectionsSequenced(source, ConnectionsBySource());
|
nuclear@0
|
604 }
|
nuclear@0
|
605
|
nuclear@0
|
606
|
nuclear@0
|
607
|
nuclear@0
|
608 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
609 std::vector<const Connection*> Document::GetConnectionsBySourceSequenced(uint64_t dest,
|
nuclear@0
|
610 const char* classname) const
|
nuclear@0
|
611 {
|
nuclear@0
|
612 const char* arr[] = {classname};
|
nuclear@0
|
613 return GetConnectionsBySourceSequenced(dest, arr,1);
|
nuclear@0
|
614 }
|
nuclear@0
|
615
|
nuclear@0
|
616
|
nuclear@0
|
617
|
nuclear@0
|
618 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
619 std::vector<const Connection*> Document::GetConnectionsBySourceSequenced(uint64_t source,
|
nuclear@0
|
620 const char* const* classnames, size_t count) const
|
nuclear@0
|
621 {
|
nuclear@0
|
622 return GetConnectionsSequenced(source, true, ConnectionsBySource(),classnames, count);
|
nuclear@0
|
623 }
|
nuclear@0
|
624
|
nuclear@0
|
625
|
nuclear@0
|
626 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
627 std::vector<const Connection*> Document::GetConnectionsByDestinationSequenced(uint64_t dest,
|
nuclear@0
|
628 const char* classname) const
|
nuclear@0
|
629 {
|
nuclear@0
|
630 const char* arr[] = {classname};
|
nuclear@0
|
631 return GetConnectionsByDestinationSequenced(dest, arr,1);
|
nuclear@0
|
632 }
|
nuclear@0
|
633
|
nuclear@0
|
634
|
nuclear@0
|
635 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
636 std::vector<const Connection*> Document::GetConnectionsByDestinationSequenced(uint64_t dest) const
|
nuclear@0
|
637 {
|
nuclear@0
|
638 return GetConnectionsSequenced(dest, ConnectionsByDestination());
|
nuclear@0
|
639 }
|
nuclear@0
|
640
|
nuclear@0
|
641
|
nuclear@0
|
642 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
643 std::vector<const Connection*> Document::GetConnectionsByDestinationSequenced(uint64_t dest,
|
nuclear@0
|
644 const char* const* classnames, size_t count) const
|
nuclear@0
|
645
|
nuclear@0
|
646 {
|
nuclear@0
|
647 return GetConnectionsSequenced(dest, false, ConnectionsByDestination(),classnames, count);
|
nuclear@0
|
648 }
|
nuclear@0
|
649
|
nuclear@0
|
650
|
nuclear@0
|
651 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
652 Connection::Connection(uint64_t insertionOrder, uint64_t src, uint64_t dest, const std::string& prop,
|
nuclear@0
|
653 const Document& doc)
|
nuclear@0
|
654
|
nuclear@0
|
655 : insertionOrder(insertionOrder)
|
nuclear@0
|
656 , prop(prop)
|
nuclear@0
|
657 , src(src)
|
nuclear@0
|
658 , dest(dest)
|
nuclear@0
|
659 , doc(doc)
|
nuclear@0
|
660 {
|
nuclear@0
|
661 ai_assert(doc.Objects().find(src) != doc.Objects().end());
|
nuclear@0
|
662 // dest may be 0 (root node)
|
nuclear@0
|
663 ai_assert(!dest || doc.Objects().find(dest) != doc.Objects().end());
|
nuclear@0
|
664 }
|
nuclear@0
|
665
|
nuclear@0
|
666
|
nuclear@0
|
667 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
668 Connection::~Connection()
|
nuclear@0
|
669 {
|
nuclear@0
|
670
|
nuclear@0
|
671 }
|
nuclear@0
|
672
|
nuclear@0
|
673
|
nuclear@0
|
674 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
675 LazyObject& Connection::LazySourceObject() const
|
nuclear@0
|
676 {
|
nuclear@0
|
677 LazyObject* const lazy = doc.GetObject(src);
|
nuclear@0
|
678 ai_assert(lazy);
|
nuclear@0
|
679 return *lazy;
|
nuclear@0
|
680 }
|
nuclear@0
|
681
|
nuclear@0
|
682
|
nuclear@0
|
683 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
684 LazyObject& Connection::LazyDestinationObject() const
|
nuclear@0
|
685 {
|
nuclear@0
|
686 LazyObject* const lazy = doc.GetObject(dest);
|
nuclear@0
|
687 ai_assert(lazy);
|
nuclear@0
|
688 return *lazy;
|
nuclear@0
|
689 }
|
nuclear@0
|
690
|
nuclear@0
|
691
|
nuclear@0
|
692 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
693 const Object* Connection::SourceObject() const
|
nuclear@0
|
694 {
|
nuclear@0
|
695 LazyObject* const lazy = doc.GetObject(src);
|
nuclear@0
|
696 ai_assert(lazy);
|
nuclear@0
|
697 return lazy->Get();
|
nuclear@0
|
698 }
|
nuclear@0
|
699
|
nuclear@0
|
700
|
nuclear@0
|
701 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
702 const Object* Connection::DestinationObject() const
|
nuclear@0
|
703 {
|
nuclear@0
|
704 LazyObject* const lazy = doc.GetObject(dest);
|
nuclear@0
|
705 ai_assert(lazy);
|
nuclear@0
|
706 return lazy->Get();
|
nuclear@0
|
707 }
|
nuclear@0
|
708
|
nuclear@0
|
709 } // !FBX
|
nuclear@0
|
710 } // !Assimp
|
nuclear@0
|
711
|
nuclear@0
|
712 #endif
|
nuclear@0
|
713
|