@ -32,12 +32,18 @@
# include <unordered_map>
# include <Corrade/Containers/ArrayView.h>
# include <Corrade/Utility/String.h>
# include <Corrade/Utility/Directory.h>
# include "Magnum/Mesh.h"
# include "Magnum/MeshTools/CombineIndexedArrays.h"
# include "Magnum/MeshTools/Duplicate.h"
# include "Magnum/Math/Vector3.h"
# include "Magnum/Math/Color.h"
# include "Magnum/Trade/MeshData3D.h"
# include "Magnum/Trade/ImageData.h"
# include "Magnum/Trade/PhongMaterialData.h"
# include "MagnumPlugins/TgaImporter/TgaImporter.h"
# if defined(CORRADE_TARGET_NACL_NEWLIB) || defined(CORRADE_TARGET_ANDROID)
# include <sstream>
@ -47,78 +53,154 @@ using namespace Corrade::Containers;
namespace Magnum { namespace Trade {
struct ImporterState {
std : : unordered_map < std : : string , UnsignedInt > meshesForName ;
std : : vector < std : : string > meshNames ;
std : : vector < std : : tuple < std : : streampos , std : : streampos , UnsignedInt , UnsignedInt , UnsignedInt > > meshes ;
std : : optional < MeshPrimitive > primitive ;
// Not using PhongMaterialData, since we may want to set color and texture to
// later decide which flags we set. We do not know whether we have a texture
// or color beforehand.
struct ObjMaterial {
std : : string name ;
Color3 ambient ;
Color3 diffuse ;
Color3 specular ;
Float specularity ;
UnsignedInt ambientTexture = - 1 ;
UnsignedInt diffuseTexture = - 1 ;
UnsignedInt specularTexture = - 1 ;
} ;
// Semantic object for a set of indices belonging to a mesh.
struct ObjMesh {
std : : vector < std : : array < int , 3 > > indices ;
} ;
// Kinda misleading: *Not* similar to the OpenGexImporter Importer State.
// This just holds all the members of the importer to 1. avoid huge includes
// in the header and 2. not have to switch file when adding new ones (I needed
// to be done quickly ;) )
struct ImporterState {
std : : vector < Vector3 > positions ;
std : : vector < std : : vector < Vector2 > > textureCoordinates ;
std : : vector < std : : vector < Vector3 > > normals ;
std : : vector < UnsignedInt > positionIndices ;
std : : vector < UnsignedInt > textureCoordinateIndices ;
std : : vector < UnsignedInt > normalIndices ;
std : : vector < Vector2 > textureCoordinates ;
std : : vector < Vector3 > normals ;
std : : vector < ObjMaterial > materials ;
std : : vector < Int > meshMaterials ;
std : : unordered_map < std : : string , int > materialIndices ;
std : : vector < std : : unique_ptr < ObjMesh > > meshes ;
std : : vector < std : : string > textures ;
std : : unordered_map < std : : string , int > textureIndices ;
} ;
namespace {
int findNext ( const ArrayView < char > & pos , char c ) {
// entire contents of data in a std::string
std : : string arrayToString ( const Containers : : ArrayView < const char > data ) {
/* Propagate errors */
if ( ! data ) return " " ;
if ( data . empty ( ) ) {
return " " ;
}
std : : string out ;
for ( const char * i = data ; i ! = data . end ( ) ; + + i ) {
const char c = * i ;
out + = c ;
}
return out ;
}
// find first index of c in pos, cancel search at newline or whitespace if flag set.
int findNext ( const ArrayView < char > & pos , char c , bool termByNewline = false , bool termByWhitespace = false ) {
for ( int i = 0 ; i < pos . size ( ) ; + + i ) {
if ( pos [ i ] = = c ) {
return i ;
}
if ( ( pos [ i ] = = ' \n ' & & termByNewline ) | | ( pos [ i ] = = ' ' & & termByWhitespace ) ) {
return - 1 ;
}
}
return - 1 ;
}
ArrayView < char > skipWhitespaces ( const ArrayView < char > & pos ) {
// find next non-whitespace char and return suffix at that point.
// param strict if "true", '\n' and '\r' are not considered whitespace
ArrayView < char > skipWhitespaces ( const ArrayView < char > & pos , bool strict = false ) {
for ( int i = 0 ; i < pos . size ( ) ; + + i ) {
if ( pos [ i ] ! = ' ' & & pos [ i ] ! = ' \t ' ) {
const char c = pos [ i ] ;
if ( c ! = ' ' & & c ! = ' \t ' & & ( strict | | ( c ! = ' \n ' & & c ! = ' \r ' ) ) ) {
return pos . suffix ( i ) ;
}
}
return ArrayView < char > { } ;
}
// returns suffix from after next '\n'
ArrayView < char > ignoreLine ( const ArrayView < char > & pos ) {
return pos . suffix ( findNext ( pos , ' \n ' ) ) ;
return pos . suffix ( findNext ( pos , ' \n ' ) + 1 ) ;
}
// same as ignoreLine, but also returns the content "ignored".
// Different description: Get string until next '\n' and return ArrayView of data
// after it.
std : : tuple < std : : string , ArrayView < char > > nextLine ( const ArrayView < char > & pos ) {
int i = 0 ;
for ( ; i < pos . size ( ) ; + + i ) {
if ( pos [ i ] = = ' \n ' ) {
break ;
}
int i = Math : : min ( findNext ( pos , ' \n ' ) , findNext ( pos , ' \r ' ) ) ;
if ( i = = - 1 ) {
i = pos . size ( ) ;
std : : string str = arrayToString ( pos . prefix ( i ) ) ;
return std : : make_tuple ( str , pos . suffix ( i ) ) ;
}
std : : string str = pos . prefix ( i ) . data ( ) ;
std : : string str = arrayToString ( pos . prefix ( i ) ) ;
return std : : make_tuple ( str , pos . suffix ( i + 1 ) ) ;
}
std : : tuple < std : : string , ArrayView < char > > nextWord ( const ArrayView < char > & pos ) {
int i = 0 ;
for ( ; i < pos . size ( ) ; + + i ) {
if ( pos [ i ] = = ' ' ) {
break ;
// Parse a "v/n/t" string to indices
// Warning: I'm not handing cases like "v/n", where the normal is not terminated by '/', but ' '!
// Same for "v", where even the normal is omitted.
std : : tuple < std : : array < int , 3 > , ArrayView < char > > parseVertex ( const ArrayView < char > & pos ) {
std : : array < int , 3 > indices { - 1 , - 1 , - 1 } ;
int i = findNext ( pos , ' / ' ) ;
indices [ 0 ] = stoi ( arrayToString ( pos . prefix ( i ) ) ) ;
auto endpos = pos . suffix ( i + 1 ) ;
i = findNext ( endpos , ' / ' , true , true ) ;
if ( i ! = - 1 ) { // there may not be a normal! Eg. "1//2"
auto prefix = endpos . prefix ( i ) ;
if ( ! prefix . empty ( ) ) {
indices [ 1 ] = stoi ( arrayToString ( prefix ) ) ;
endpos = endpos . suffix ( i + 1 ) ;
}
}
std : : string str = pos . prefix ( i ) . data ( ) ;
return std : : make_tuple ( str , pos . suffix ( i + 1 ) ) ;
}
std : : tuple < float , ArrayView < char > > asFloat ( const ArrayView < char > & pos ) {
i = findNext ( endpos , ' ' , true , true ) ; // texture coordinates are not terminated by '/', but ' ' or newline
if ( i = = - 1 )
i = findNext ( endpos , ' \r ' , true , true ) ;
if ( i = = - 1 )
i = findNext ( endpos , ' \n ' , true , true ) ;
if ( i ! = - 1 ) { // there may not be a texCoord! Eg. "1//"
auto prefix = endpos . prefix ( i ) ;
if ( ! prefix . empty ( ) ) {
indices [ 2 ] = stoi ( arrayToString ( prefix ) ) ;
endpos = endpos . suffix ( i + 1 ) ;
}
}
return std : : make_tuple ( indices , endpos ) ;
}
template < class T > void reindex ( const std : : vector < UnsignedInt > & indices , std : : vector < T > & data ) {
/* Check that indices are in range */
for ( UnsignedInt i : indices ) if ( i > = data . size ( ) ) {
Error ( ) < < " Trade::ObjImporter::mesh3D(): index out of range " ;
throw 0 ;
// get string of content until next ' ' and also return ArrayView for data after the word.
std : : tuple < std : : string , ArrayView < char > > nextWord ( const ArrayView < char > & pos ) {
int i = 0 ;
for ( ; i < pos . size ( ) ; + + i ) {
if ( pos [ i ] = = ' ' | | pos [ i ] = = ' \r ' | | pos [ i ] = = ' \n ' ) {
break ;
}
data = MeshTools : : duplicate ( indices , data ) ;
}
std : : string str = arrayToString ( pos . prefix ( i ) ) ;
return std : : make_tuple ( str , pos . suffix ( i + 1 ) ) ;
}
}
@ -135,29 +217,30 @@ bool ObjImporter::doIsOpened() const { return _in; }
void ObjImporter : : doClose ( ) { _in = nullptr ; }
void ObjImporter : : doOpenFile ( const std : : string & filename ) {
_fileRoot = filename . substr ( 0 , filename . find_last_of ( ' / ' ) + 1 ) ;
AbstractImporter : : doOpenFile ( filename ) ;
}
void ObjImporter : : doOpenData ( Containers : : ArrayView < const char > data ) {
_in = Containers : : Array < char > { data . size ( ) } ;
std : : copy ( data . begin ( ) , data . end ( ) , _in . begin ( ) ) ;
_state . reset ( new ImporterState ) ;
parseMeshNames ( ) ;
parse ( ) ;
}
void ObjImporter : : parseMeshNames ( ) {
/* First mesh starts at the beginning, its indices start from 1. The end
offset will be updated to proper value later . */
UnsignedInt positionIndexOffset = 1 ;
UnsignedInt normalIndexOffset = 1 ;
UnsignedInt textureCoordinateIndexOffset = 1 ;
_state - > meshes . emplace_back ( 0 , 0 , positionIndexOffset , normalIndexOffset , textureCoordinateIndexOffset ) ;
void ObjImporter : : parse ( ) {
ArrayView < char > pos = _in ;
ObjMesh * mesh = new ObjMesh ;
_state - > meshNames . emplace_back ( ) ;
int currentMaterialIndex = - 1 ;
ArrayView < char > pos = _in ;
while ( ! pos . empty ( ) ) {
/* Comment line */
if ( pos [ 0 ] = = ' # ' ) {
pos = ignoreLine ( pos ) ;
pos = skipWhitespaces ( pos ) ;
continue ;
}
@ -165,208 +248,364 @@ void ObjImporter::parseMeshNames() {
std : : string keyword ;
std : : tie ( keyword , pos ) = nextWord ( pos ) ;
/* Mesh name */
if ( keyword = = " o " ) {
// TODO
continue ;
}
pos = skipWhitespaces ( pos ) ;
/* Vertex position */
if ( keyword = = " v " ) {
Float extra { 1.0f } ;
const Vector3 data = extractFloatData < 3 > ( pos , & extra ) ;
if ( ! Math : : TypeTraits < Float > : : equals ( extra , 1.0f ) ) {
Error ( ) < < " Trade::ObjImporter::mesh3D(): homogeneous coordinates are not supported " ;
return Containers : : NullOpt ;
}
std : : string word ;
// All of this code should be abstracted away in a function.
// Currently copy pasted to vt, vn, Ka, Ks, Kd...
std : : tie ( word , pos ) = nextWord ( pos ) ;
const float x = std : : stof ( word ) ;
positions . push_back ( data ) ;
std : : tie ( word , pos ) = nextWord ( pos ) ;
const float y = std : : stof ( word ) ;
std : : tie ( word , pos ) = nextWord ( pos ) ;
const float z = std : : stof ( word ) ;
_state - > positions . push_back ( Vector3 { x , y , z } ) ;
/* Texture coordinate */
} else if ( keyword = = " vt " ) {
Float extra { 0.0f } ;
const auto data = extractFloatData < 2 > ( contents , & extra ) ;
if ( ! Math : : TypeTraits < Float > : : equals ( extra , 0.0f ) ) {
Error ( ) < < " Trade::ObjImporter::mesh3D(): 3D texture coordinates are not supported " ;
return Containers : : NullOpt ;
}
std : : string word ;
if ( textureCoordinates . empty ( ) ) textureCoordinates . emplace_back ( ) ;
textureCoordinates . front ( ) . emplace_back ( data ) ;
std : : tie ( word , pos ) = nextWord ( pos ) ;
const float x = std : : stof ( word ) ;
std : : tie ( word , pos ) = nextWord ( pos ) ;
const float y = std : : stof ( word ) ;
_state - > textureCoordinates . push_back ( Vector2 { x , y } ) ;
/* Normal */
} else if ( keyword = = " vn " ) {
if ( normals . empty ( ) ) normals . emplace_back ( ) ;
normals . front ( ) . emplace_back ( extractFloatData < 3 > ( contents ) ) ;
std : : string word ;
std : : tie ( word , pos ) = nextWord ( pos ) ;
const float x = std : : stof ( word ) ;
std : : tie ( word , pos ) = nextWord ( pos ) ;
const float y = std : : stof ( word ) ;
std : : tie ( word , pos ) = nextWord ( pos ) ;
const float z = std : : stof ( word ) ;
_state - > normals . push_back ( Vector3 { x , y , z } ) ;
/* Indices */
} else if ( keyword = = " p " | | keyword = = " l " | | keyword = = " f " ) {
const std : : vector < std : : string > indexTuples = Utility : : String : : splitWithoutEmptyParts ( contents , ' ' ) ;
} else if ( keyword = = " f " ) {
// Not handling case that there are more than three vertices!
for ( int i = 0 ; i < 3 ; + + i ) {
std : : array < int , 3 > indices ;
std : : tie ( indices , pos ) = parseVertex ( pos ) ;
mesh - > indices . push_back ( indices ) ;
pos = skipWhitespaces ( pos , true ) ;
}
/* Load a material library */
} else if ( keyword = = " mtllib " ) {
std : : string word ;
pos = skipWhitespaces ( pos ) ;
std : : tie ( word , pos ) = nextWord ( pos ) ;
parseMaterialLibrary ( word ) ;
/* Set current material and add a new mesh for it */
} else if ( keyword = = " usemtl " ) {
std : : string word ;
pos = skipWhitespaces ( pos ) ;
std : : tie ( word , pos ) = nextWord ( pos ) ;
int materialIndex = _state - > materialIndices [ word ] ;
if ( materialIndex ! = currentMaterialIndex ) {
currentMaterialIndex = materialIndex ;
/* switching the material here, need to create a new mesh */
_state - > meshMaterials . push_back ( materialIndex ) ;
if ( ! mesh - > indices . empty ( ) ) {
_state - > meshes . push_back ( std : : unique_ptr < ObjMesh > ( mesh ) ) ;
} /* empty meshes are skipped */
mesh = new ObjMesh ;
} // else: usemtl did not result in material switch, no need to create new mesh
/* Ignore unsupported keywords, error out on unknown keywords */
} else {
Warning ( ) < < " Trade::ObjImporter::parse(): unknown keyword: " < < keyword ;
}
/* Points */
if ( keyword = = " p " ) {
/* Check that we don't mix the primitives in one mesh */
if ( primitive & & primitive ! = MeshPrimitive : : Points ) {
Error ( ) < < " Trade::ObjImporter::mesh3D(): mixed primitive " < < * primitive < < " and " < < MeshPrimitive : : Points ;
return Containers : : NullOpt ;
/* Ignore the rest of the line */
pos = skipWhitespaces ( ignoreLine ( pos ) ) ;
}
if ( ! mesh - > indices . empty ( ) ) {
_state - > meshes . push_back ( std : : unique_ptr < ObjMesh > ( mesh ) ) ;
}
}
/* Check vertex count per primitive */
if ( indexTuples . size ( ) ! = 1 ) {
Error ( ) < < " Trade::ObjImporter::mesh3D(): wrong index count for point " ;
return Containers : : NullOpt ;
void ObjImporter : : parseMaterialLibrary ( std : : string libname ) {
std : : string filename = _fileRoot + libname ;
/* Open file */
if ( ! Utility : : Directory : : fileExists ( filename ) ) {
Error ( ) < < " Trade::AbstractImporter::parseMaterialLibrary(): cannot open file " < < filename ;
return ;
}
primitive = MeshPrimitive : : Points ;
Containers : : Array < char > contents = Utility : : Directory : : read ( filename ) ;
ArrayView < char > pos = contents ;
ObjMaterial * mat = nullptr ;
while ( ! pos . empty ( ) ) {
/* Lines */
} else if ( keyword = = " l " ) {
/* Check that we don't mix the primitives in one mesh */
if ( primitive & & primitive ! = MeshPrimitive : : Lines ) {
Error ( ) < < " Trade::ObjImporter::mesh3D(): mixed primitive " < < * primitive < < " and " < < MeshPrimitive : : Lines ;
return Containers : : NullOpt ;
/* Comment line */
if ( pos [ 0 ] = = ' # ' ) {
pos = ignoreLine ( pos ) ;
pos = skipWhitespaces ( pos ) ;
continue ;
}
/* Check vertex count per primitive */
if ( indexTuples . size ( ) ! = 2 ) {
Error ( ) < < " Trade::ObjImporter::mesh3D(): wrong index count for line " ;
return Containers : : NullOpt ;
/* Parse the keyword */
std : : string keyword ;
std : : tie ( keyword , pos ) = nextWord ( pos ) ;
if ( keyword . empty ( ) ) {
continue ;
}
primitive = MeshPrimitive : : Lines ;
pos = skipWhitespaces ( pos ) ;
/* Faces */
} else if ( keyword = = " f " ) {
/* Check that we don't mix the primitives in one mesh */
if ( primitive & & primitive ! = MeshPrimitive : : Triangles ) {
Error ( ) < < " Trade::ObjImporter::mesh3D(): mixed primitive " < < * primitive < < " and " < < MeshPrimitive : : Triangles ;
return Containers : : NullOpt ;
if ( keyword = = " newmtl " ) {
if ( mat ! = nullptr ) {
_state - > materials . push_back ( * mat ) ;
_state - > materialIndices . insert (
std : : make_pair ( mat - > name , _state - > materials . size ( ) - 1 ) ) ;
delete mat ;
}
mat = new ObjMaterial ;
std : : tie ( mat - > name , pos ) = nextWord ( pos ) ;
continue ;
} else if ( mat = = nullptr ) {
Error ( ) < < " Expected newmtl keyword, got " < < keyword ;
}
/* Check vertex count per primitive */
if ( indexTuples . size ( ) < 3 ) {
Error ( ) < < " Trade::ObjImporter::mesh3D(): wrong index count for triangle " ;
return Containers : : NullOpt ;
} else if ( indexTuples . size ( ) ! = 3 ) {
Error ( ) < < " Trade::ObjImporter::mesh3D(): polygons are not supported " ;
return Containers : : NullOpt ;
/* Ambient color */
if ( keyword = = " Ka " ) {
std : : string word ;
// again, this code is duplicated alot.
std : : tie ( word , pos ) = nextWord ( pos ) ;
const float r = std : : stof ( word ) ;
std : : tie ( word , pos ) = nextWord ( pos ) ;
const float g = std : : stof ( word ) ;
std : : tie ( word , pos ) = nextWord ( pos ) ;
const float b = std : : stof ( word ) ;
mat - > ambient = Color3 ( r , g , b ) ;
/* Diffuse color */
} else if ( keyword = = " Kd " ) {
std : : string word ;
std : : tie ( word , pos ) = nextWord ( pos ) ;
const float r = std : : stof ( word ) ;
std : : tie ( word , pos ) = nextWord ( pos ) ;
const float g = std : : stof ( word ) ;
std : : tie ( word , pos ) = nextWord ( pos ) ;
const float b = std : : stof ( word ) ;
mat - > diffuse = Color3 ( r , g , b ) ;
/* Specular color */
} else if ( keyword = = " Ks " ) {
std : : string word ;
std : : tie ( word , pos ) = nextWord ( pos ) ;
const float r = std : : stof ( word ) ;
std : : tie ( word , pos ) = nextWord ( pos ) ;
const float g = std : : stof ( word ) ;
std : : tie ( word , pos ) = nextWord ( pos ) ;
const float b = std : : stof ( word ) ;
mat - > specular = Color3 ( r , g , b ) ;
/* Specularity */
} else if ( keyword = = " Ns " ) {
std : : string word ;
std : : tie ( word , pos ) = nextWord ( pos ) ;
const float f = std : : stof ( word ) ;
mat - > specularity = f ;
/* Ambient texture */
} else if ( keyword = = " map_Ka " ) {
std : : string texture ;
std : : tie ( texture , pos ) = nextLine ( pos ) ;
// This is also very similar code for every "map_*"
if ( _state - > textureIndices . find ( texture ) = = _state - > textureIndices . end ( ) ) {
/* new texture, create it */
int index = _state - > textures . size ( ) ;
_state - > textures . push_back ( texture ) ;
_state - > textureIndices [ texture ] = index ;
mat - > ambientTexture = index ;
} else {
mat - > ambientTexture = _state - > textureIndices [ texture ] ;
}
primitive = MeshPrimitive : : Triangles ;
/* Diffuse texture */
} else if ( keyword = = " map_Kd " ) {
std : : string texture ;
std : : tie ( texture , pos ) = nextLine ( pos ) ;
} else CORRADE_ASSERT_UNREACHABLE ( ) ; /* LCOV_EXCL_LINE */
if ( _state - > textureIndices . find ( texture ) = = _state - > textureIndices . end ( ) ) {
/* new texture, create it */
int index = _state - > textures . size ( ) ;
_state - > textures . push_back ( texture ) ;
for ( const std : : string & indexTuple : indexTuples ) {
std : : vector < std : : string > indices = Utility : : String : : split ( indexTuple , ' / ' ) ;
if ( indices . size ( ) > 3 ) {
Error ( ) < < " Trade::ObjImporter::mesh3D(): invalid index data " ;
return Containers : : NullOpt ;
_state - > textureIndices [ texture ] = index ;
mat - > diffuseTexture = index ;
} else {
mat - > diffuseTexture = _state - > textureIndices [ texture ] ;
}
/* Position indices */
positionIndices . push_back ( std : : stoul ( indices [ 0 ] ) - positionIndexOffset ) ;
/* Specular texture */
} else if ( keyword = = " map_Ks " ) {
std : : string texture ;
std : : tie ( texture , pos ) = nextLine ( pos ) ;
/* Texture coordinates */
if ( indices . size ( ) = = 2 | | ( indices . size ( ) = = 3 & & ! indices [ 1 ] . empty ( ) ) )
textureCoordinateIndices . push_back ( std : : stoul ( indices [ 1 ] ) - textureCoordinateIndexOffset ) ;
if ( _state - > textureIndices . find ( texture ) = = _state - > textureIndices . end ( ) ) {
/* new texture, create it */
int index = _state - > textures . size ( ) ;
_state - > textures . push_back ( texture ) ;
/* Normal indices */
if ( indices . size ( ) = = 3 )
normalIndices . push_back ( std : : stoul ( indices [ 2 ] ) - normalIndexOffset ) ;
_state - > textureIndices [ texture ] = index ;
mat - > specularTexture = index ;
} else {
mat - > specularTexture = _state - > textureIndices [ texture ] ;
}
/* Ignore unsupported keywords, error out on unknown keywords */
} else if ( ! [ & keyword ] ( ) {
/* Using lambda to emulate for-else construct like in Python */
for ( const std : : string expected : { " mtllib " , " usemtl " , " g " , " s " } )
if ( keyword = = expected ) return true ;
return false ;
} ( ) ) {
Error ( ) < < " Trade::ObjImporter::mesh3D(): unknown keyword " < < keyword ;
return Containers : : NullOpt ;
} else {
Warning ( ) < < " Trade::ObjImporter::parseMaterialLibrary(): unknown keyword: " < < keyword ;
}
/* Ignore the rest of the line */
pos = ignoreLine ( pos ) ;
pos = skipWhitespaces ( ignoreLine ( pos ) ) ;
}
// add last currently active material to materials vector. Usually added in "newmtl" keyword handling
if ( mat ! = nullptr ) {
_state - > materials . push_back ( * mat ) ;
_state - > materialIndices . insert (
std : : make_pair ( mat - > name , _state - > materials . size ( ) - 1 ) ) ;
delete mat ;
}
}
UnsignedInt ObjImporter : : doMesh3DCount ( ) const { return _state - > meshes . size ( ) ; }
UnsignedInt ObjImporter : : doMaterialCount ( ) const { return _state - > meshMaterials . size ( ) ; }
UnsignedInt ObjImporter : : doImage2DCount ( ) const { return _state - > textures . size ( ) ; }
Int ObjImporter : : doMesh3DForName ( const std : : string & name ) {
const auto it = _state - > meshesForName . find ( name ) ;
return it = = _state - > meshesForName . end ( ) ? - 1 : it - > second ;
return 0 ;
}
std : : string ObjImporter : : doMesh3DName ( UnsignedInt id ) {
return _state - > meshNames [ id ] ;
return " " ;
}
std : : optional < MeshData3D > ObjImporter : : doMesh3D ( UnsignedInt id ) {
/* Seek the file, set mesh parsing parameters */
std : : streampos begin , end ;
UnsignedInt positionIndexOffset , textureCoordinateIndexOffset , normalIndexOffset ;
std : : tie ( begin , end , positionIndexOffset , textureCoordinateIndexOffset , normalIndexOffset ) = _state - > meshes [ id ] ;
/* There should be at least indexed position data */
if ( positions . empty ( ) | | positionIndices . empty ( ) ) {
Error ( ) < < " Trade::ObjImporter::mesh3D(): incomplete position data " ;
return Containers : : NullOpt ;
}
/* If there are index data, there should be also vertex data (and also the other way) */
if ( normals . empty ( ) ! = normalIndices . empty ( ) ) {
Error ( ) < < " Trade::ObjImporter::mesh3D(): incomplete normal data " ;
return Containers : : NullOpt ;
}
if ( textureCoordinates . empty ( ) ! = textureCoordinateIndices . empty ( ) ) {
Error ( ) < < " Trade::ObjImporter::mesh3D(): incomplete texture coordinate data " ;
return Containers : : NullOpt ;
}
/* All index arrays should have the same length */
if ( ! normalIndices . empty ( ) & & normalIndices . size ( ) ! = positionIndices . size ( ) ) {
CORRADE_INTERNAL_ASSERT ( normalIndices . size ( ) < positionIndices . size ( ) ) ;
Error ( ) < < " Trade::ObjImporter::mesh3D(): some normal indices are missing " ;
return Containers : : NullOpt ;
}
if ( ! textureCoordinates . empty ( ) & & textureCoordinateIndices . size ( ) ! = positionIndices . size ( ) ) {
CORRADE_INTERNAL_ASSERT ( textureCoordinateIndices . size ( ) < positionIndices . size ( ) ) ;
Error ( ) < < " Trade::ObjImporter::mesh3D(): some texture coordinate indices are missing " ;
return Containers : : NullOpt ;
}
/* Merge index arrays, if there aren't just the positions */
std : : vector < UnsignedInt > indices ;
if ( ! normalIndices . empty ( ) | | ! textureCoordinateIndices . empty ( ) ) {
std : : vector < std : : reference_wrapper < std : : vector < UnsignedInt > > > arrays ;
arrays . reserve ( 3 ) ;
arrays . emplace_back ( positionIndices ) ;
if ( ! normalIndices . empty ( ) ) arrays . emplace_back ( normalIndices ) ;
if ( ! textureCoordinateIndices . empty ( ) ) arrays . emplace_back ( textureCoordinateIndices ) ;
indices = MeshTools : : combineIndexArrays ( arrays ) ;
/* Reindex data arrays */
try {
reindex ( positionIndices , positions ) ;
if ( ! normalIndices . empty ( ) ) reindex ( normalIndices , normals . front ( ) ) ;
if ( ! textureCoordinateIndices . empty ( ) ) reindex ( textureCoordinateIndices , textureCoordinates . front ( ) ) ;
} catch ( . . . ) {
/* Error message already printed */
return Containers : : NullOpt ;
}
/* Otherwise just use the original position index array. Don't forget to
check range */
ObjMesh & mesh = * _state - > meshes [ id ] ;
std : : vector < Vector3 > positions , normals ;
std : : vector < Vector2 > textureCoords ;
Debug ( ) < < " Have " < < mesh . indices . size ( ) < < " vertices " ; //
// resolve indices... probably use combineIndexArrays instead?
positions . reserve ( mesh . indices . size ( ) ) ;
normals . reserve ( mesh . indices . size ( ) ) ;
textureCoords . reserve ( mesh . indices . size ( ) ) ;
for ( std : : array < int , 3 > & indexArray : mesh . indices ) {
positions . push_back ( _state - > positions [ indexArray [ 0 ] - 1 ] ) ;
if ( indexArray [ 1 ] ! = - 1 ) // even though this looks like it's handling the case, it's not, this is utterly useless.
// may result in differently sized arrays, which is not valid.
normals . push_back ( _state - > normals [ indexArray [ 1 ] - 1 ] ) ;
if ( indexArray [ 2 ] ! = - 1 )
textureCoords . push_back ( _state - > textureCoordinates [ indexArray [ 2 ] - 1 ] ) ;
}
return MeshData3D (
MeshPrimitive : : Triangles ,
{ } ,
{ positions } ,
{ normals } ,
{ textureCoords } ) ;
}
std : : unique_ptr < AbstractMaterialData > ObjImporter : : doMaterial ( UnsignedInt id ) {
ObjMaterial & objMat = _state - > materials [ _state - > meshMaterials [ id ] ] ;
PhongMaterialData : : Flags flags ;
if ( objMat . ambientTexture ! = - 1 ) {
flags | = PhongMaterialData : : Flag : : AmbientTexture ;
}
if ( objMat . diffuseTexture ! = - 1 ) {
flags | = PhongMaterialData : : Flag : : DiffuseTexture ;
}
if ( objMat . specularTexture ! = - 1 ) {
flags | = PhongMaterialData : : Flag : : SpecularTexture ;
}
PhongMaterialData * mat = new PhongMaterialData {
flags ,
objMat . specularity } ;
if ( objMat . ambientTexture = = - 1 ) {
mat - > ambientColor ( ) = objMat . ambient ;
} else {
mat - > ambientTexture ( ) = objMat . ambientTexture ;
}
if ( objMat . diffuseTexture = = - 1 ) {
mat - > diffuseColor ( ) = objMat . diffuse ;
} else {
mat - > diffuseTexture ( ) = objMat . diffuseTexture ;
}
if ( objMat . specularTexture = = - 1 ) {
mat - > specularColor ( ) = objMat . specular ;
} else {
indices = std : : move ( positionIndices ) ;
for ( UnsignedInt i : indices ) if ( i > = positions . size ( ) ) {
Error ( ) < < " Trade::ObjImporter::mesh3D(): index out of range " ;
return Containers : : NullOpt ;
mat - > specularTexture ( ) = objMat . specularTexture ;
}
return std : : unique_ptr < AbstractMaterialData > ( mat ) ;
}
std : : optional < ImageData2D > ObjImporter : : doImage2D ( UnsignedInt id ) {
CORRADE_ASSERT ( manager ( ) , " Trade::ObjImporter::image2D(): the plugin must be instantiated with access to plugin manager in order to open image files " , { } ) ;
std : : unique_ptr < AbstractImporter > imageImporter = manager ( ) - > loadAndInstantiate ( " TgaImporter " ) ; // probably AnyImageImporter would be the way to go here...
if ( ! imageImporter - > openFile ( _fileRoot + _state - > textures [ id ] ) ) {
return std : : nullopt ;
}
return MeshData3D { * primitive , std : : move ( indices ) , { std : : move ( positions ) } , std : : move ( normals ) , std : : move ( textureCoordinates ) , { } , nullptr } ;
Debug ( ) < < " Loading image2D " < < _fileRoot + _state - > textures [ id ] ;
return imageImporter - > image2D ( 0 ) ;
}
} }