1/*
2Open Asset Import Library (assimp)
3----------------------------------------------------------------------
4
5Copyright (c) 2006-2017, assimp team
6
7All rights reserved.
8
9Redistribution and use of this software in source and binary forms,
10with or without modification, are permitted provided that the
11following conditions are met:
12
13* Redistributions of source code must retain the above
14copyright notice, this list of conditions and the
15following disclaimer.
16
17* Redistributions in binary form must reproduce the above
18copyright notice, this list of conditions and the
19following disclaimer in the documentation and/or other
20materials provided with the distribution
21
22* Neither the name of the assimp team, nor the names of its
23contributors may be used to endorse or promote products
24derived from this software without specific prior
25written permission of the assimp team.
26
27THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
28"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
29LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
30A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
31OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
32SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
33LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
34DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
35THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
36(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
37OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
38
39----------------------------------------------------------------------
40*/
41#ifndef ASSIMP_BUILD_NO_OPENGEX_IMPORTER
42
43#include "OpenGEXImporter.h"
44#include <assimp/DefaultIOSystem.h>
45#include <assimp/DefaultLogger.hpp>
46#include "MakeVerboseFormat.h"
47#include "StringComparison.h"
48
49#include <openddlparser/OpenDDLParser.h>
50#include <assimp/scene.h>
51#include <assimp/ai_assert.h>
52#include <assimp/importerdesc.h>
53
54#include <vector>
55
56static const aiImporterDesc desc = {
57 "Open Game Engine Exchange",
58 "",
59 "",
60 "",
61 aiImporterFlags_SupportTextFlavour,
62 0,
63 0,
64 0,
65 0,
66 "ogex"
67};
68
69namespace Grammar {
70 static const std::string MetricType = "Metric";
71 static const std::string Metric_DistanceType = "distance";
72 static const std::string Metric_AngleType = "angle";
73 static const std::string Metric_TimeType = "time";
74 static const std::string Metric_UpType = "up";
75 static const std::string NameType = "Name";
76 static const std::string ObjectRefType = "ObjectRef";
77 static const std::string MaterialRefType = "MaterialRef";
78 static const std::string MetricKeyType = "key";
79 static const std::string GeometryNodeType = "GeometryNode";
80 static const std::string CameraNodeType = "CameraNode";
81 static const std::string LightNodeType = "LightNode";
82 static const std::string GeometryObjectType = "GeometryObject";
83 static const std::string CameraObjectType = "CameraObject";
84 static const std::string LightObjectType = "LightObject";
85 static const std::string TransformType = "Transform";
86 static const std::string MeshType = "Mesh";
87 static const std::string VertexArrayType = "VertexArray";
88 static const std::string IndexArrayType = "IndexArray";
89 static const std::string MaterialType = "Material";
90 static const std::string ColorType = "Color";
91 static const std::string ParamType = "Param";
92 static const std::string TextureType = "Texture";
93 static const std::string AttenType = "Atten";
94
95 static const std::string DiffuseColorToken = "diffuse";
96 static const std::string SpecularColorToken = "specular";
97 static const std::string EmissionColorToken = "emission";
98
99 static const std::string DiffuseTextureToken = "diffuse";
100 static const std::string DiffuseSpecularTextureToken = "specular";
101 static const std::string SpecularPowerTextureToken = "specular_power";
102 static const std::string EmissionTextureToken = "emission";
103 static const std::string OpacyTextureToken = "opacity";
104 static const std::string TransparencyTextureToken = "transparency";
105 static const std::string NormalTextureToken = "normal";
106
107 enum TokenType {
108 NoneType = -1,
109 MetricToken,
110 NameToken,
111 ObjectRefToken,
112 MaterialRefToken,
113 MetricKeyToken,
114 GeometryNodeToken,
115 CameraNodeToken,
116 LightNodeToken,
117 GeometryObjectToken,
118 CameraObjectToken,
119 LightObjectToken,
120 TransformToken,
121 MeshToken,
122 VertexArrayToken,
123 IndexArrayToken,
124 MaterialToken,
125 ColorToken,
126 ParamToken,
127 TextureToken,
128 AttenToken
129 };
130
131 static const std::string ValidMetricToken[ 4 ] = {
132 Metric_DistanceType,
133 Metric_AngleType,
134 Metric_TimeType,
135 Metric_UpType
136 };
137
138 static int isValidMetricType( const char *token ) {
139 if( nullptr == token ) {
140 return false;
141 }
142
143 int idx( -1 );
144 for( size_t i = 0; i < 4; i++ ) {
145 if( ValidMetricToken[ i ] == token ) {
146 idx = (int) i;
147 break;
148 }
149 }
150
151 return idx;
152 }
153
154 static TokenType matchTokenType( const char *tokenType ) {
155 if( MetricType == tokenType ) {
156 return MetricToken;
157 } else if( NameType == tokenType ) {
158 return NameToken;
159 } else if( ObjectRefType == tokenType ) {
160 return ObjectRefToken;
161 } else if( MaterialRefType == tokenType ) {
162 return MaterialRefToken;
163 } else if( MetricKeyType == tokenType ) {
164 return MetricKeyToken;
165 } else if ( GeometryNodeType == tokenType ) {
166 return GeometryNodeToken;
167 } else if ( CameraNodeType == tokenType ) {
168 return CameraNodeToken;
169 } else if ( LightNodeType == tokenType ) {
170 return LightNodeToken;
171 } else if ( GeometryObjectType == tokenType ) {
172 return GeometryObjectToken;
173 } else if ( CameraObjectType == tokenType ) {
174 return CameraObjectToken;
175 } else if ( LightObjectType == tokenType ) {
176 return LightObjectToken;
177 } else if( TransformType == tokenType ) {
178 return TransformToken;
179 } else if( MeshType == tokenType ) {
180 return MeshToken;
181 } else if( VertexArrayType == tokenType ) {
182 return VertexArrayToken;
183 } else if( IndexArrayType == tokenType ) {
184 return IndexArrayToken;
185 } else if( MaterialType == tokenType ) {
186 return MaterialToken;
187 } else if ( ColorType == tokenType ) {
188 return ColorToken;
189 } else if ( ParamType == tokenType ) {
190 return ParamToken;
191 } else if( TextureType == tokenType ) {
192 return TextureToken;
193 } else if ( AttenType == tokenType ) {
194 return AttenToken;
195 }
196
197 return NoneType;
198 }
199} // Namespace Grammar
200
201namespace Assimp {
202namespace OpenGEX {
203
204USE_ODDLPARSER_NS
205
206//------------------------------------------------------------------------------------------------
207static void propId2StdString( Property *prop, std::string &name, std::string &key ) {
208 name = key = "";
209 if ( nullptr == prop ) {
210 return;
211 }
212
213 if ( nullptr != prop->m_key ) {
214 name = prop->m_key->m_buffer;
215 if ( Value::ddl_string == prop->m_value->m_type ) {
216 key = prop->m_value->getString();
217 }
218 }
219}
220
221//------------------------------------------------------------------------------------------------
222OpenGEXImporter::VertexContainer::VertexContainer()
223: m_numVerts( 0 )
224, m_vertices( nullptr )
225, m_numColors( 0 )
226, m_colors( nullptr )
227, m_numNormals( 0 )
228, m_normals( nullptr )
229, m_numUVComps()
230, m_textureCoords() {
231 // empty
232}
233
234//------------------------------------------------------------------------------------------------
235OpenGEXImporter::VertexContainer::~VertexContainer() {
236 delete[] m_vertices;
237 delete[] m_colors;
238 delete[] m_normals;
239
240 for(auto &texcoords : m_textureCoords) {
241 delete [] texcoords;
242 }
243}
244
245//------------------------------------------------------------------------------------------------
246OpenGEXImporter::RefInfo::RefInfo( aiNode *node, Type type, std::vector<std::string> &names )
247: m_node( node )
248, m_type( type )
249, m_Names( names ) {
250 // empty
251}
252
253//------------------------------------------------------------------------------------------------
254OpenGEXImporter::RefInfo::~RefInfo() {
255 // empty
256}
257
258//------------------------------------------------------------------------------------------------
259OpenGEXImporter::OpenGEXImporter()
260: m_root( nullptr )
261, m_nodeChildMap()
262, m_meshCache()
263, m_mesh2refMap()
264, m_material2refMap()
265, m_ctx( nullptr )
266, m_metrics()
267, m_currentNode( nullptr )
268, m_currentVertices()
269, m_currentMesh( nullptr )
270, m_currentMaterial( nullptr )
271, m_currentLight( nullptr )
272, m_currentCamera( nullptr )
273, m_tokenType( Grammar::NoneType )
274, m_materialCache()
275, m_cameraCache()
276, m_lightCache()
277, m_nodeStack()
278, m_unresolvedRefStack() {
279 // empty
280}
281
282//------------------------------------------------------------------------------------------------
283OpenGEXImporter::~OpenGEXImporter() {
284 m_ctx = nullptr;
285}
286
287//------------------------------------------------------------------------------------------------
288bool OpenGEXImporter::CanRead( const std::string &file, IOSystem *pIOHandler, bool checkSig ) const {
289 bool canRead( false );
290 if( !checkSig ) {
291 canRead = SimpleExtensionCheck( file, "ogex" );
292 } else {
293 static const char *token[] = { "Metric", "GeometryNode", "VertexArray (attrib", "IndexArray" };
294 canRead = BaseImporter::SearchFileHeaderForToken( pIOHandler, file, token, 4 );
295 }
296
297 return canRead;
298}
299
300//------------------------------------------------------------------------------------------------
301void OpenGEXImporter::InternReadFile( const std::string &filename, aiScene *pScene, IOSystem *pIOHandler ) {
302 // open source file
303 IOStream *file = pIOHandler->Open( filename, "rb" );
304 if( !file ) {
305 throw DeadlyImportError( "Failed to open file " + filename );
306 }
307
308 std::vector<char> buffer;
309 TextFileToBuffer( file, buffer );
310 pIOHandler->Close( file );
311
312 OpenDDLParser myParser;
313 myParser.setBuffer( &buffer[ 0 ], buffer.size() );
314 bool success( myParser.parse() );
315 if( success ) {
316 m_ctx = myParser.getContext();
317 pScene->mRootNode = new aiNode;
318 pScene->mRootNode->mName.Set( filename );
319 handleNodes( m_ctx->m_root, pScene );
320 }
321
322 copyMeshes( pScene );
323 copyCameras( pScene );
324 copyLights( pScene );
325 copyMaterials( pScene );
326 resolveReferences();
327 createNodeTree( pScene );
328}
329
330//------------------------------------------------------------------------------------------------
331const aiImporterDesc *OpenGEXImporter::GetInfo() const {
332 return &desc;
333}
334
335//------------------------------------------------------------------------------------------------
336void OpenGEXImporter::SetupProperties( const Importer *pImp ) {
337 if( nullptr == pImp ) {
338 return;
339 }
340}
341
342//------------------------------------------------------------------------------------------------
343void OpenGEXImporter::handleNodes( DDLNode *node, aiScene *pScene ) {
344 if( nullptr == node ) {
345 return;
346 }
347
348 DDLNode::DllNodeList childs = node->getChildNodeList();
349 for( DDLNode::DllNodeList::iterator it = childs.begin(); it != childs.end(); ++it ) {
350 Grammar::TokenType tokenType( Grammar::matchTokenType( ( *it )->getType().c_str() ) );
351 switch( tokenType ) {
352 case Grammar::MetricToken:
353 handleMetricNode( *it, pScene );
354 break;
355
356 case Grammar::NameToken:
357 handleNameNode( *it, pScene );
358 break;
359
360 case Grammar::ObjectRefToken:
361 handleObjectRefNode( *it, pScene );
362 break;
363
364 case Grammar::MaterialRefToken:
365 handleMaterialRefNode( *it, pScene );
366 break;
367
368 case Grammar::MetricKeyToken:
369 break;
370
371 case Grammar::GeometryNodeToken:
372 handleGeometryNode( *it, pScene );
373 break;
374
375 case Grammar::CameraNodeToken:
376 handleCameraNode( *it, pScene );
377 break;
378
379 case Grammar::LightNodeToken:
380 handleLightNode( *it, pScene );
381 break;
382
383 case Grammar::GeometryObjectToken:
384 handleGeometryObject( *it, pScene );
385 break;
386
387 case Grammar::CameraObjectToken:
388 handleCameraObject( *it, pScene );
389 break;
390
391 case Grammar::LightObjectToken:
392 handleLightObject( *it, pScene );
393 break;
394
395 case Grammar::TransformToken:
396 handleTransformNode( *it, pScene );
397 break;
398
399 case Grammar::MeshToken:
400 handleMeshNode( *it, pScene );
401 break;
402
403 case Grammar::VertexArrayToken:
404 handleVertexArrayNode( *it, pScene );
405 break;
406
407 case Grammar::IndexArrayToken:
408 handleIndexArrayNode( *it, pScene );
409 break;
410
411 case Grammar::MaterialToken:
412 handleMaterialNode( *it, pScene );
413 break;
414
415 case Grammar::ColorToken:
416 handleColorNode( *it, pScene );
417 break;
418
419 case Grammar::ParamToken:
420 handleParamNode( *it, pScene );
421 break;
422
423 case Grammar::TextureToken:
424 handleTextureNode( *it, pScene );
425 break;
426
427 default:
428 break;
429 }
430 }
431}
432
433//------------------------------------------------------------------------------------------------
434void OpenGEXImporter::handleMetricNode( DDLNode *node, aiScene * /*pScene*/ ) {
435 if( nullptr == node || nullptr == m_ctx ) {
436 return;
437 }
438
439 if( m_ctx->m_root != node->getParent() ) {
440 return;
441 }
442
443 Property *prop( node->getProperties() );
444 while( nullptr != prop ) {
445 if( nullptr != prop->m_key ) {
446 if( Value::ddl_string == prop->m_value->m_type ) {
447 std::string valName( ( char* ) prop->m_value->m_data );
448 int type( Grammar::isValidMetricType( valName.c_str() ) );
449 if( Grammar::NoneType != type ) {
450 Value *val( node->getValue() );
451 if( nullptr != val ) {
452 if( Value::ddl_float == val->m_type ) {
453 m_metrics[ type ].m_floatValue = val->getFloat();
454 } else if( Value::ddl_int32 == val->m_type ) {
455 m_metrics[ type ].m_intValue = val->getInt32();
456 } else if( Value::ddl_string == val->m_type ) {
457 m_metrics[type].m_stringValue = std::string( val->getString() );
458 } else {
459 throw DeadlyImportError( "OpenGEX: invalid data type for Metric node." );
460 }
461 }
462 }
463 }
464 }
465 prop = prop->m_next;
466 }
467}
468
469//------------------------------------------------------------------------------------------------
470void OpenGEXImporter::handleNameNode( DDLNode *node, aiScene * /*pScene*/ ) {
471 if( nullptr == m_currentNode ) {
472 throw DeadlyImportError( "No current node for name." );
473 return;
474 }
475
476 Value *val( node->getValue() );
477 if( nullptr != val ) {
478 if( Value::ddl_string != val->m_type ) {
479 throw DeadlyImportError( "OpenGEX: invalid data type for value in node name." );
480 return;
481 }
482
483 const std::string name( val->getString() );
484 if( m_tokenType == Grammar::GeometryNodeToken || m_tokenType == Grammar::LightNodeToken
485 || m_tokenType == Grammar::CameraNodeToken ) {
486 m_currentNode->mName.Set( name.c_str() );
487 } else if( m_tokenType == Grammar::MaterialToken ) {
488 aiString aiName;
489 aiName.Set( name );
490 m_currentMaterial->AddProperty( &aiName, AI_MATKEY_NAME );
491 m_material2refMap[ name ] = m_materialCache.size() - 1;
492 }
493 }
494}
495
496//------------------------------------------------------------------------------------------------
497static void getRefNames( DDLNode *node, std::vector<std::string> &names ) {
498 ai_assert( nullptr != node );
499
500 Reference *ref = node->getReferences();
501 if( nullptr != ref ) {
502 for( size_t i = 0; i < ref->m_numRefs; i++ ) {
503 Name *currentName( ref->m_referencedName[ i ] );
504 if( nullptr != currentName && nullptr != currentName->m_id ) {
505 const std::string name( currentName->m_id->m_buffer );
506 if( !name.empty() ) {
507 names.push_back( name );
508 }
509 }
510 }
511 }
512}
513
514//------------------------------------------------------------------------------------------------
515void OpenGEXImporter::handleObjectRefNode( DDLNode *node, aiScene * /*pScene*/ ) {
516 if( nullptr == m_currentNode ) {
517 throw DeadlyImportError( "No parent node for name." );
518 return;
519 }
520
521 std::vector<std::string> objRefNames;
522 getRefNames( node, objRefNames );
523
524 // when we are dealing with a geometry node prepare the mesh cache
525 if ( m_tokenType == Grammar::GeometryNodeToken ) {
526 m_currentNode->mNumMeshes = static_cast<unsigned int>(objRefNames.size());
527 m_currentNode->mMeshes = new unsigned int[ objRefNames.size() ];
528 if ( !objRefNames.empty() ) {
529 m_unresolvedRefStack.push_back( std::unique_ptr<RefInfo>( new RefInfo( m_currentNode, RefInfo::MeshRef, objRefNames ) ) );
530 }
531 } else if ( m_tokenType == Grammar::LightNodeToken ) {
532 // TODO!
533 } else if ( m_tokenType == Grammar::CameraNodeToken ) {
534 // TODO!
535 }
536}
537
538//------------------------------------------------------------------------------------------------
539void OpenGEXImporter::handleMaterialRefNode( ODDLParser::DDLNode *node, aiScene * /*pScene*/ ) {
540 if( nullptr == m_currentNode ) {
541 throw DeadlyImportError( "No parent node for name." );
542 return;
543 }
544
545 std::vector<std::string> matRefNames;
546 getRefNames( node, matRefNames );
547 if( !matRefNames.empty() ) {
548 m_unresolvedRefStack.push_back( std::unique_ptr<RefInfo>( new RefInfo( m_currentNode, RefInfo::MaterialRef, matRefNames ) ) );
549 }
550}
551
552//------------------------------------------------------------------------------------------------
553void OpenGEXImporter::handleGeometryNode( DDLNode *node, aiScene *pScene ) {
554 aiNode *newNode = new aiNode;
555 pushNode( newNode, pScene );
556 m_tokenType = Grammar::GeometryNodeToken;
557 m_currentNode = newNode;
558 handleNodes( node, pScene );
559
560 popNode();
561}
562
563//------------------------------------------------------------------------------------------------
564void OpenGEXImporter::handleCameraNode( DDLNode *node, aiScene *pScene ) {
565 aiCamera *camera( new aiCamera );
566 m_cameraCache.push_back( camera );
567 m_currentCamera = camera;
568
569 aiNode *newNode = new aiNode;
570 pushNode( newNode, pScene );
571 m_tokenType = Grammar::CameraNodeToken;
572 m_currentNode = newNode;
573
574 handleNodes( node, pScene );
575
576 popNode();
577
578 m_currentCamera->mName.Set( newNode->mName.C_Str() );
579}
580
581//------------------------------------------------------------------------------------------------
582void OpenGEXImporter::handleLightNode( ODDLParser::DDLNode *node, aiScene *pScene ) {
583 aiLight *light( new aiLight );
584 m_lightCache.push_back( light );
585 m_currentLight = light;
586
587 aiNode *newNode = new aiNode;
588 m_tokenType = Grammar::LightNodeToken;
589 m_currentNode = newNode;
590 pushNode( newNode, pScene );
591
592 handleNodes( node, pScene );
593
594 popNode();
595
596 m_currentLight->mName.Set( newNode->mName.C_Str() );
597}
598
599//------------------------------------------------------------------------------------------------
600void OpenGEXImporter::handleGeometryObject( DDLNode *node, aiScene *pScene ) {
601 // parameters will be parsed normally in the tree, so just go for it
602 handleNodes( node, pScene );
603}
604
605//------------------------------------------------------------------------------------------------
606void OpenGEXImporter::handleCameraObject( ODDLParser::DDLNode *node, aiScene *pScene ) {
607 // parameters will be parsed normally in the tree, so just go for it
608
609 handleNodes( node, pScene );
610}
611
612//------------------------------------------------------------------------------------------------
613void OpenGEXImporter::handleLightObject( ODDLParser::DDLNode *node, aiScene *pScene ) {
614 aiLight *light( new aiLight );
615 m_lightCache.push_back( light );
616 std::string objName = node->getName();
617 if ( !objName.empty() ) {
618 light->mName.Set( objName );
619 }
620 m_currentLight = light;
621
622 Property *prop( node->findPropertyByName( "type" ) );
623 if ( nullptr != prop ) {
624 if ( nullptr != prop->m_value ) {
625 std::string typeStr( prop->m_value->getString() );
626 if ( "point" == typeStr ) {
627 m_currentLight->mType = aiLightSource_POINT;
628 } else if ( "spot" == typeStr ) {
629 m_currentLight->mType = aiLightSource_SPOT;
630 } else if ( "infinite" == typeStr ) {
631 m_currentLight->mType = aiLightSource_DIRECTIONAL;
632 }
633 }
634 }
635
636 // parameters will be parsed normally in the tree, so just go for it
637 handleNodes( node, pScene );
638}
639
640//------------------------------------------------------------------------------------------------
641static void setMatrix( aiNode *node, DataArrayList *transformData ) {
642 ai_assert( nullptr != node );
643 ai_assert( nullptr != transformData );
644
645 float m[ 16 ];
646 size_t i( 1 );
647 Value *next( transformData->m_dataList->m_next );
648 m[ 0 ] = transformData->m_dataList->getFloat();
649 while( next != nullptr ) {
650 m[ i ] = next->getFloat();
651 next = next->m_next;
652 i++;
653 }
654
655 ai_assert(i == 16);
656
657 node->mTransformation.a1 = m[ 0 ];
658 node->mTransformation.a2 = m[ 4 ];
659 node->mTransformation.a3 = m[ 8 ];
660 node->mTransformation.a4 = m[ 12 ];
661
662 node->mTransformation.b1 = m[ 1 ];
663 node->mTransformation.b2 = m[ 5 ];
664 node->mTransformation.b3 = m[ 9 ];
665 node->mTransformation.b4 = m[ 13 ];
666
667 node->mTransformation.c1 = m[ 2 ];
668 node->mTransformation.c2 = m[ 6 ];
669 node->mTransformation.c3 = m[ 10 ];
670 node->mTransformation.c4 = m[ 14 ];
671
672 node->mTransformation.d1 = m[ 3 ];
673 node->mTransformation.d2 = m[ 7 ];
674 node->mTransformation.d3 = m[ 11 ];
675 node->mTransformation.d4 = m[ 15 ];
676}
677
678//------------------------------------------------------------------------------------------------
679void OpenGEXImporter::handleTransformNode( ODDLParser::DDLNode *node, aiScene * /*pScene*/ ) {
680 if( nullptr == m_currentNode ) {
681 throw DeadlyImportError( "No parent node for name." );
682 return;
683 }
684
685 DataArrayList *transformData( node->getDataArrayList() );
686 if( nullptr != transformData ) {
687 if( transformData->m_numItems != 16 ) {
688 throw DeadlyImportError( "Invalid number of data for transform matrix." );
689 return;
690 }
691 setMatrix( m_currentNode, transformData );
692 }
693}
694
695//------------------------------------------------------------------------------------------------
696void OpenGEXImporter::handleMeshNode( ODDLParser::DDLNode *node, aiScene *pScene ) {
697 m_currentMesh = new aiMesh;
698 const size_t meshidx( m_meshCache.size() );
699 m_meshCache.push_back( m_currentMesh );
700
701 Property *prop = node->getProperties();
702 if( nullptr != prop ) {
703 std::string propName, propKey;
704 propId2StdString( prop, propName, propKey );
705 if( "primitive" == propName ) {
706 if ( "points" == propKey ) {
707 m_currentMesh->mPrimitiveTypes |= aiPrimitiveType_POINT;
708 } else if ( "lines" == propKey ) {
709 m_currentMesh->mPrimitiveTypes |= aiPrimitiveType_LINE;
710 } else if( "triangles" == propKey ) {
711 m_currentMesh->mPrimitiveTypes |= aiPrimitiveType_TRIANGLE;
712 } else if ( "quads" == propKey ) {
713 m_currentMesh->mPrimitiveTypes |= aiPrimitiveType_POLYGON;
714 } else {
715 DefaultLogger::get()->warn( propKey + " is not supported primitive type." );
716 }
717 }
718 }
719
720 handleNodes( node, pScene );
721
722 DDLNode *parent( node->getParent() );
723 if( nullptr != parent ) {
724 const std::string &name = parent->getName();
725 m_mesh2refMap[ name ] = meshidx;
726 }
727}
728
729//------------------------------------------------------------------------------------------------
730enum MeshAttribute {
731 None,
732 Position,
733 Color,
734 Normal,
735 TexCoord
736};
737
738//------------------------------------------------------------------------------------------------
739static MeshAttribute getAttributeByName( const char *attribName ) {
740 ai_assert( nullptr != attribName );
741
742 if ( 0 == strncmp( "position", attribName, strlen( "position" ) ) ) {
743 return Position;
744 } else if ( 0 == strncmp( "color", attribName, strlen( "color" ) ) ) {
745 return Color;
746 } else if( 0 == strncmp( "normal", attribName, strlen( "normal" ) ) ) {
747 return Normal;
748 } else if( 0 == strncmp( "texcoord", attribName, strlen( "texcoord" ) ) ) {
749 return TexCoord;
750 }
751
752 return None;
753}
754
755//------------------------------------------------------------------------------------------------
756static void fillVector3( aiVector3D *vec3, Value *vals ) {
757 ai_assert( nullptr != vec3 );
758 ai_assert( nullptr != vals );
759
760 float x( 0.0f ), y( 0.0f ), z( 0.0f );
761 Value *next( vals );
762 x = next->getFloat();
763 next = next->m_next;
764 y = next->getFloat();
765 next = next->m_next;
766 if( nullptr != next ) {
767 z = next->getFloat();
768 }
769
770 vec3->Set( x, y, z );
771}
772
773//------------------------------------------------------------------------------------------------
774static void fillColor4( aiColor4D *col4, Value *vals ) {
775 ai_assert( nullptr != col4 );
776 ai_assert( nullptr != vals );
777
778 Value *next( vals );
779 col4->r = next->getFloat();
780 next = next->m_next;
781 col4->g = next->getFloat();
782 next = next->m_next;
783 col4->b = next->getFloat();
784 next = next->m_next;
785 col4->a = next->getFloat();
786}
787
788//------------------------------------------------------------------------------------------------
789static size_t countDataArrayListItems( DataArrayList *vaList ) {
790 size_t numItems( 0 );
791 if( nullptr == vaList ) {
792 return numItems;
793 }
794
795 DataArrayList *next( vaList );
796 while( nullptr != next ) {
797 if( nullptr != vaList->m_dataList ) {
798 numItems++;
799 }
800 next = next->m_next;
801 }
802
803 return numItems;
804}
805
806//------------------------------------------------------------------------------------------------
807static void copyVectorArray( size_t numItems, DataArrayList *vaList, aiVector3D *vectorArray ) {
808 for( size_t i = 0; i < numItems; i++ ) {
809 Value *next( vaList->m_dataList );
810 fillVector3( &vectorArray[ i ], next );
811 vaList = vaList->m_next;
812 }
813}
814
815//------------------------------------------------------------------------------------------------
816static void copyColor4DArray( size_t numItems, DataArrayList *vaList, aiColor4D *colArray ) {
817 for ( size_t i = 0; i < numItems; i++ ) {
818 Value *next( vaList->m_dataList );
819 fillColor4( &colArray[ i ], next );
820 }
821}
822
823//------------------------------------------------------------------------------------------------
824void OpenGEXImporter::handleVertexArrayNode( ODDLParser::DDLNode *node, aiScene * /*pScene*/ ) {
825 if( nullptr == node ) {
826 throw DeadlyImportError( "No parent node for name." );
827 return;
828 }
829
830 Property *prop( node->getProperties() );
831 if( nullptr != prop ) {
832 std::string propName, propKey;
833 propId2StdString( prop, propName, propKey );
834 MeshAttribute attribType( getAttributeByName( propKey.c_str() ) );
835 if( None == attribType ) {
836 return;
837 }
838
839 DataArrayList *vaList = node->getDataArrayList();
840 if( nullptr == vaList ) {
841 return;
842 }
843
844 const size_t numItems( countDataArrayListItems( vaList ) );
845
846 if( Position == attribType ) {
847 m_currentVertices.m_numVerts = numItems;
848 m_currentVertices.m_vertices = new aiVector3D[ numItems ];
849 copyVectorArray( numItems, vaList, m_currentVertices.m_vertices );
850 } else if ( Color == attribType ) {
851 m_currentVertices.m_numColors = numItems;
852 m_currentVertices.m_colors = new aiColor4D[ numItems ];
853 copyColor4DArray( numItems, vaList, m_currentVertices.m_colors );
854 } else if( Normal == attribType ) {
855 m_currentVertices.m_numNormals = numItems;
856 m_currentVertices.m_normals = new aiVector3D[ numItems ];
857 copyVectorArray( numItems, vaList, m_currentVertices.m_normals );
858 } else if( TexCoord == attribType ) {
859 m_currentVertices.m_numUVComps[ 0 ] = numItems;
860 m_currentVertices.m_textureCoords[ 0 ] = new aiVector3D[ numItems ];
861 copyVectorArray( numItems, vaList, m_currentVertices.m_textureCoords[ 0 ] );
862 }
863 }
864}
865
866//------------------------------------------------------------------------------------------------
867void OpenGEXImporter::handleIndexArrayNode( ODDLParser::DDLNode *node, aiScene * /*pScene*/ ) {
868 if( nullptr == node ) {
869 throw DeadlyImportError( "No parent node for name." );
870 return;
871 }
872
873 if( nullptr == m_currentMesh ) {
874 throw DeadlyImportError( "No current mesh for index data found." );
875 return;
876 }
877
878 DataArrayList *vaList = node->getDataArrayList();
879 if( nullptr == vaList ) {
880 return;
881 }
882
883 const size_t numItems( countDataArrayListItems( vaList ) );
884 m_currentMesh->mNumFaces = static_cast<unsigned int>(numItems);
885 m_currentMesh->mFaces = new aiFace[ numItems ];
886 m_currentMesh->mNumVertices = static_cast<unsigned int>(numItems * 3);
887 m_currentMesh->mVertices = new aiVector3D[ m_currentMesh->mNumVertices ];
888 bool hasColors( false );
889 if ( m_currentVertices.m_numColors > 0 ) {
890 m_currentMesh->mColors[0] = new aiColor4D[ m_currentVertices.m_numColors ];
891 hasColors = true;
892 }
893 bool hasNormalCoords( false );
894 if ( m_currentVertices.m_numNormals > 0 ) {
895 m_currentMesh->mNormals = new aiVector3D[ m_currentMesh->mNumVertices ];
896 hasNormalCoords = true;
897 }
898 bool hasTexCoords( false );
899 if ( m_currentVertices.m_numUVComps[ 0 ] > 0 ) {
900 m_currentMesh->mTextureCoords[ 0 ] = new aiVector3D[ m_currentMesh->mNumVertices ];
901 hasTexCoords = true;
902 }
903
904 unsigned int index( 0 );
905 for( size_t i = 0; i < m_currentMesh->mNumFaces; i++ ) {
906 aiFace &current( m_currentMesh->mFaces[ i ] );
907 current.mNumIndices = 3;
908 current.mIndices = new unsigned int[ current.mNumIndices ];
909 Value *next( vaList->m_dataList );
910 for( size_t indices = 0; indices < current.mNumIndices; indices++ ) {
911 const int idx( next->getUnsignedInt32() );
912 ai_assert( static_cast<size_t>( idx ) <= m_currentVertices.m_numVerts );
913 ai_assert( index < m_currentMesh->mNumVertices );
914 aiVector3D &pos = ( m_currentVertices.m_vertices[ idx ] );
915 m_currentMesh->mVertices[ index ].Set( pos.x, pos.y, pos.z );
916 if ( hasColors ) {
917 aiColor4D &col = m_currentVertices.m_colors[ idx ];
918 m_currentMesh->mColors[ 0 ][ index ] = col;
919 }
920 if ( hasNormalCoords ) {
921 aiVector3D &normal = ( m_currentVertices.m_normals[ idx ] );
922 m_currentMesh->mNormals[ index ].Set( normal.x, normal.y, normal.z );
923 }
924 if ( hasTexCoords ) {
925 aiVector3D &tex = ( m_currentVertices.m_textureCoords[ 0 ][ idx ] );
926 m_currentMesh->mTextureCoords[ 0 ][ index ].Set( tex.x, tex.y, tex.z );
927 }
928 current.mIndices[ indices ] = index;
929 index++;
930
931 next = next->m_next;
932 }
933 vaList = vaList->m_next;
934 }
935}
936
937//------------------------------------------------------------------------------------------------
938static void getColorRGB3( aiColor3D *pColor, DataArrayList *colList ) {
939 if( nullptr == pColor || nullptr == colList ) {
940 return;
941 }
942
943 ai_assert( 3 == colList->m_numItems );
944 Value *val( colList->m_dataList );
945 pColor->r = val->getFloat();
946 val = val->getNext();
947 pColor->g = val->getFloat();
948 val = val->getNext();
949 pColor->b = val->getFloat();
950}
951
952//------------------------------------------------------------------------------------------------
953static void getColorRGB4( aiColor4D *pColor, DataArrayList *colList ) {
954 if ( nullptr == pColor || nullptr == colList ) {
955 return;
956 }
957
958 ai_assert( 4 == colList->m_numItems );
959 Value *val( colList->m_dataList );
960 pColor->r = val->getFloat();
961 val = val->getNext();
962 pColor->g = val->getFloat();
963 val = val->getNext();
964 pColor->b = val->getFloat();
965 val = val->getNext();
966 pColor->a = val->getFloat();
967}
968
969//------------------------------------------------------------------------------------------------
970enum ColorType {
971 NoneColor = 0,
972 DiffuseColor,
973 SpecularColor,
974 EmissionColor,
975 LightColor
976};
977
978//------------------------------------------------------------------------------------------------
979static ColorType getColorType( Text *id ) {
980 if ( nullptr == id ) {
981 return NoneColor;
982 }
983
984 if( *id == Grammar::DiffuseColorToken ) {
985 return DiffuseColor;
986 } else if( *id == Grammar::SpecularColorToken ) {
987 return SpecularColor;
988 } else if( *id == Grammar::EmissionColorToken ) {
989 return EmissionColor;
990 } else if ( *id == "light" ) {
991 return LightColor;
992 }
993
994 return NoneColor;
995}
996
997//------------------------------------------------------------------------------------------------
998void OpenGEXImporter::handleMaterialNode( ODDLParser::DDLNode *node, aiScene *pScene ) {
999 m_currentMaterial = new aiMaterial;
1000 m_materialCache.push_back( m_currentMaterial );
1001 m_tokenType = Grammar::MaterialToken;
1002 handleNodes( node, pScene );
1003}
1004
1005//------------------------------------------------------------------------------------------------
1006void OpenGEXImporter::handleColorNode( ODDLParser::DDLNode *node, aiScene * /*pScene*/ ) {
1007 if( nullptr == node ) {
1008 return;
1009 }
1010
1011 Property *prop = node->findPropertyByName( "attrib" );
1012 if( nullptr != prop ) {
1013 if( nullptr != prop->m_value ) {
1014 DataArrayList *colList( node->getDataArrayList() );
1015 if( nullptr == colList ) {
1016 return;
1017 }
1018 aiColor3D col;
1019 if ( 3 == colList->m_numItems ) {
1020 aiColor3D col3;
1021 getColorRGB3( &col3, colList );
1022 col = col3;
1023 } else {
1024 aiColor4D col4;
1025 getColorRGB4( &col4, colList );
1026 col.r = col4.r;
1027 col.g = col4.g;
1028 col.b = col4.b;
1029 }
1030 const ColorType colType( getColorType( prop->m_key ) );
1031 if( DiffuseColor == colType ) {
1032 m_currentMaterial->AddProperty( &col, 1, AI_MATKEY_COLOR_DIFFUSE );
1033 } else if( SpecularColor == colType ) {
1034 m_currentMaterial->AddProperty( &col, 1, AI_MATKEY_COLOR_SPECULAR );
1035 } else if( EmissionColor == colType ) {
1036 m_currentMaterial->AddProperty( &col, 1, AI_MATKEY_COLOR_EMISSIVE );
1037 } else if ( LightColor == colType ) {
1038 m_currentLight->mColorDiffuse = col;
1039 }
1040 }
1041 }
1042}
1043
1044//------------------------------------------------------------------------------------------------
1045void OpenGEXImporter::handleTextureNode( ODDLParser::DDLNode *node, aiScene * /*pScene*/ ) {
1046 if( nullptr == node ) {
1047 return;
1048 }
1049
1050 Property *prop = node->findPropertyByName( "attrib" );
1051 if( nullptr != prop ) {
1052 if( nullptr != prop->m_value ) {
1053 Value *val( node->getValue() );
1054 if( nullptr != val ) {
1055 aiString tex;
1056 tex.Set( val->getString() );
1057 if( prop->m_value->getString() == Grammar::DiffuseTextureToken ) {
1058 m_currentMaterial->AddProperty( &tex, AI_MATKEY_TEXTURE_DIFFUSE( 0 ) );
1059 } else if( prop->m_value->getString() == Grammar::SpecularPowerTextureToken ) {
1060 m_currentMaterial->AddProperty( &tex, AI_MATKEY_TEXTURE_SPECULAR( 0 ) );
1061 } else if( prop->m_value->getString() == Grammar::EmissionTextureToken ) {
1062 m_currentMaterial->AddProperty( &tex, AI_MATKEY_TEXTURE_EMISSIVE( 0 ) );
1063 } else if( prop->m_value->getString() == Grammar::OpacyTextureToken ) {
1064 m_currentMaterial->AddProperty( &tex, AI_MATKEY_TEXTURE_OPACITY( 0 ) );
1065 } else if( prop->m_value->getString() == Grammar::TransparencyTextureToken ) {
1066 // ToDo!
1067 // m_currentMaterial->AddProperty( &tex, AI_MATKEY_TEXTURE_DIFFUSE( 0 ) );
1068 } else if( prop->m_value->getString() == Grammar::NormalTextureToken ) {
1069 m_currentMaterial->AddProperty( &tex, AI_MATKEY_TEXTURE_NORMALS( 0 ) );
1070 } else {
1071 ai_assert( false );
1072 }
1073 }
1074 }
1075 }
1076}
1077
1078//------------------------------------------------------------------------------------------------
1079void OpenGEXImporter::handleParamNode( ODDLParser::DDLNode *node, aiScene * /*pScene*/ ) {
1080 if ( nullptr == node ) {
1081 return;
1082 }
1083
1084 Property *prop = node->findPropertyByName( "attrib" );
1085 if ( nullptr == prop ) {
1086 return;
1087 }
1088
1089 if ( nullptr != prop->m_value ) {
1090 Value *val( node->getValue() );
1091 if ( nullptr == val ) {
1092 return;
1093 }
1094 const float floatVal( val->getFloat() );
1095 if ( prop->m_value != nullptr ) {
1096 if ( 0 == ASSIMP_strincmp( "fov", prop->m_value->getString(), 3 ) ) {
1097 m_currentCamera->mHorizontalFOV = floatVal;
1098 } else if ( 0 == ASSIMP_strincmp( "near", prop->m_value->getString(), 3 ) ) {
1099 m_currentCamera->mClipPlaneNear = floatVal;
1100 } else if ( 0 == ASSIMP_strincmp( "far", prop->m_value->getString(), 3 ) ) {
1101 m_currentCamera->mClipPlaneFar = floatVal;
1102 }
1103 }
1104 }
1105}
1106
1107//------------------------------------------------------------------------------------------------
1108void OpenGEXImporter::handleAttenNode( ODDLParser::DDLNode *node, aiScene * /*pScene*/ ) {
1109 if ( nullptr == node ) {
1110 return;
1111 }
1112
1113 Property *prop = node->findPropertyByName( "curve" );
1114 if ( nullptr != prop ) {
1115 if ( nullptr != prop->m_value ) {
1116 Value *val( node->getValue() );
1117 const float floatVal( val->getFloat() );
1118 if ( 0 == strncmp( "scale", prop->m_value->getString(), strlen( "scale" ) ) ) {
1119 m_currentLight->mAttenuationQuadratic = floatVal;
1120 }
1121 }
1122 }
1123}
1124
1125//------------------------------------------------------------------------------------------------
1126void OpenGEXImporter::copyMeshes( aiScene *pScene ) {
1127 ai_assert( nullptr != pScene );
1128
1129 if( m_meshCache.empty() ) {
1130 return;
1131 }
1132
1133 pScene->mNumMeshes = static_cast<unsigned int>(m_meshCache.size());
1134 pScene->mMeshes = new aiMesh*[ pScene->mNumMeshes ];
1135 std::copy( m_meshCache.begin(), m_meshCache.end(), pScene->mMeshes );
1136}
1137
1138//------------------------------------------------------------------------------------------------
1139void OpenGEXImporter::copyCameras( aiScene *pScene ) {
1140 ai_assert( nullptr != pScene );
1141
1142 if ( m_cameraCache.empty() ) {
1143 return;
1144 }
1145
1146 pScene->mNumCameras = static_cast<unsigned int>(m_cameraCache.size());
1147 pScene->mCameras = new aiCamera*[ pScene->mNumCameras ];
1148 std::copy( m_cameraCache.begin(), m_cameraCache.end(), pScene->mCameras );
1149}
1150
1151//------------------------------------------------------------------------------------------------
1152void OpenGEXImporter::copyLights( aiScene *pScene ) {
1153 ai_assert( nullptr != pScene );
1154
1155 if ( m_lightCache.empty() ) {
1156 return;
1157 }
1158
1159 pScene->mNumLights = static_cast<unsigned int>(m_lightCache.size());
1160 pScene->mLights = new aiLight*[ pScene->mNumLights ];
1161 std::copy( m_lightCache.begin(), m_lightCache.end(), pScene->mLights );
1162}
1163
1164//------------------------------------------------------------------------------------------------
1165void OpenGEXImporter::copyMaterials( aiScene *pScene ) {
1166 ai_assert( nullptr != pScene );
1167
1168 if ( m_materialCache.empty() ) {
1169 return;
1170 }
1171
1172 pScene->mNumMaterials = static_cast<unsigned int>(m_materialCache.size());
1173 pScene->mMaterials = new aiMaterial*[ pScene->mNumMaterials ];
1174 std::copy( m_materialCache.begin(), m_materialCache.end(), pScene->mMaterials );
1175}
1176
1177//------------------------------------------------------------------------------------------------
1178void OpenGEXImporter::resolveReferences() {
1179 if( m_unresolvedRefStack.empty() ) {
1180 return;
1181 }
1182
1183 RefInfo *currentRefInfo( nullptr );
1184 for( auto it = m_unresolvedRefStack.begin(); it != m_unresolvedRefStack.end(); ++it ) {
1185 currentRefInfo = it->get();
1186 if( nullptr != currentRefInfo ) {
1187 aiNode *node( currentRefInfo->m_node );
1188 if( RefInfo::MeshRef == currentRefInfo->m_type ) {
1189 for( size_t i = 0; i < currentRefInfo->m_Names.size(); ++i ) {
1190 const std::string &name( currentRefInfo->m_Names[ i ] );
1191 ReferenceMap::const_iterator it( m_mesh2refMap.find( name ) );
1192 if( m_mesh2refMap.end() != it ) {
1193 unsigned int meshIdx = static_cast<unsigned int>(m_mesh2refMap[ name ]);
1194 node->mMeshes[ i ] = meshIdx;
1195 }
1196 }
1197 } else if( RefInfo::MaterialRef == currentRefInfo->m_type ) {
1198 for ( size_t i = 0; i < currentRefInfo->m_Names.size(); ++i ) {
1199 const std::string name( currentRefInfo->m_Names[ i ] );
1200 ReferenceMap::const_iterator it( m_material2refMap.find( name ) );
1201 if ( m_material2refMap.end() != it ) {
1202 if ( nullptr != m_currentMesh ) {
1203 unsigned int matIdx = static_cast< unsigned int >( m_material2refMap[ name ] );
1204 if ( m_currentMesh->mMaterialIndex != 0 ) {
1205 DefaultLogger::get()->warn( "Override of material reference in current mesh by material reference." );
1206 }
1207 m_currentMesh->mMaterialIndex = matIdx;
1208 } else {
1209 DefaultLogger::get()->warn( "Cannot resolve material reference, because no current mesh is there." );
1210
1211 }
1212 }
1213 }
1214 } else {
1215 throw DeadlyImportError( "Unknown reference info to resolve." );
1216 }
1217 }
1218 }
1219}
1220
1221//------------------------------------------------------------------------------------------------
1222void OpenGEXImporter::createNodeTree( aiScene *pScene ) {
1223 if( nullptr == m_root ) {
1224 return;
1225 }
1226
1227 if( m_root->m_children.empty() ) {
1228 return;
1229 }
1230
1231 pScene->mRootNode->mNumChildren = static_cast<unsigned int>(m_root->m_children.size());
1232 pScene->mRootNode->mChildren = new aiNode*[ pScene->mRootNode->mNumChildren ];
1233 std::copy( m_root->m_children.begin(), m_root->m_children.end(), pScene->mRootNode->mChildren );
1234}
1235
1236//------------------------------------------------------------------------------------------------
1237void OpenGEXImporter::pushNode( aiNode *node, aiScene *pScene ) {
1238 ai_assert( nullptr != pScene );
1239
1240 if ( nullptr == node ) {
1241 return;
1242 }
1243
1244 ChildInfo *info( nullptr );
1245 if( m_nodeStack.empty() ) {
1246 node->mParent = pScene->mRootNode;
1247 NodeChildMap::iterator it( m_nodeChildMap.find( node->mParent ) );
1248 if( m_nodeChildMap.end() == it ) {
1249 info = new ChildInfo;
1250 m_root = info;
1251 m_nodeChildMap[ node->mParent ] = std::unique_ptr<ChildInfo>(info);
1252 } else {
1253 info = it->second.get();
1254 }
1255 info->m_children.push_back( node );
1256 } else {
1257 aiNode *parent( m_nodeStack.back() );
1258 ai_assert( nullptr != parent );
1259 node->mParent = parent;
1260 NodeChildMap::iterator it( m_nodeChildMap.find( node->mParent ) );
1261 if( m_nodeChildMap.end() == it ) {
1262 info = new ChildInfo;
1263 m_nodeChildMap[ node->mParent ] = std::unique_ptr<ChildInfo>(info);
1264 } else {
1265 info = it->second.get();
1266 }
1267 info->m_children.push_back( node );
1268 }
1269 m_nodeStack.push_back( node );
1270}
1271
1272//------------------------------------------------------------------------------------------------
1273aiNode *OpenGEXImporter::popNode() {
1274 if( m_nodeStack.empty() ) {
1275 return nullptr;
1276 }
1277
1278 aiNode *node( top() );
1279 m_nodeStack.pop_back();
1280
1281 return node;
1282}
1283
1284//------------------------------------------------------------------------------------------------
1285aiNode *OpenGEXImporter::top() const {
1286 if( m_nodeStack.empty() ) {
1287 return nullptr;
1288 }
1289
1290 return m_nodeStack.back();
1291}
1292
1293//------------------------------------------------------------------------------------------------
1294void OpenGEXImporter::clearNodeStack() {
1295 m_nodeStack.clear();
1296}
1297
1298//------------------------------------------------------------------------------------------------
1299
1300} // Namespace OpenGEX
1301} // Namespace Assimp
1302
1303#endif // ASSIMP_BUILD_NO_OPENGEX_IMPORTER
1304