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