From 52a1a868b65af13fb3e23e7aec6048ac3371dd85 Mon Sep 17 00:00:00 2001 From: Squareys Date: Sat, 10 Jun 2017 23:36:03 +0200 Subject: [PATCH] [WIP] Address the "easy" review comments Signed-off-by: Squareys --- src/MagnumPlugins/ObjImporter/ObjImporter.cpp | 309 +++++++++--------- src/MagnumPlugins/ObjImporter/ObjImporter.h | 4 +- src/MagnumPlugins/ObjImporter/Test/Test.cpp | 14 +- .../ObjImporter/Test/multiMaterial.mtl | 3 +- .../ObjImporter/Test/multiMaterial.obj | 1 + 5 files changed, 164 insertions(+), 167 deletions(-) diff --git a/src/MagnumPlugins/ObjImporter/ObjImporter.cpp b/src/MagnumPlugins/ObjImporter/ObjImporter.cpp index ff7dede1c..33ca2bc5b 100644 --- a/src/MagnumPlugins/ObjImporter/ObjImporter.cpp +++ b/src/MagnumPlugins/ObjImporter/ObjImporter.cpp @@ -28,8 +28,7 @@ #include #include #include -#include -#include +#include #include #include #include @@ -72,28 +71,26 @@ struct ObjMesh; struct ObjMeshData { ObjGroup& group; - int materialId; /* from 'usemtl' keyword */ + Int materialId; /* From 'usemtl' keyword */ - ObjMesh* points = nullptr; + ObjMesh* poInts = nullptr; ObjMesh* lines = nullptr; ObjMesh* faces = nullptr; - ObjMeshData()=delete; - ObjMeshData(ObjGroup& g, int matId): group(g), materialId(matId) {} + explicit ObjMeshData(ObjGroup& g, Int matId): group(g), materialId(matId) {} }; struct ObjGroup { ObjObject& object; - std::string name; /* from 'g' keyword */ + std::string name; /* From 'g' keyword */ std::vector> meshes; - std::unordered_map meshPerMaterial; + std::unordered_map meshPerMaterial; - ObjGroup()=delete; - ObjGroup(ObjObject& o): object(o) {} + explicit ObjGroup(ObjObject& o): object(o) {} /* Create or get mesh for given material id */ - ObjMeshData& meshDataForMaterial(int materialId) { + ObjMeshData& meshDataForMaterial(Int materialId) { if(meshPerMaterial.find(materialId) != meshPerMaterial.end()) { return *meshes[meshPerMaterial[materialId]]; } else { @@ -106,25 +103,25 @@ struct ObjGroup { }; struct ObjObject { - std::string name; /* from 'o' keyword */ + std::string name; /* From 'o' keyword */ std::vector> groups; ObjObject(): name{""} {} - ObjObject(ArrayView name): name{name.data(), name.size()} {} + ObjObject(ArrayView name): name{name.data(), name.size()} {} }; -/* An intermediate object representing a mesh and it's properties as well as +/* An Intermediate object representing a mesh and it's properties as well as * where to find the data associated to it */ struct ObjMesh { - ObjMeshData& data; /* parent object containing data shared meshes for different primitives */ + ObjMeshData& data; /* Parent object containing data shared meshes for different primitives */ MeshPrimitive primitive; /* Sections of the file belonging to this mesh */ - std::vector> sections; + std::vector> sections; - int minPrimitives = 0; /* For smarter vector memory allocation later */ + Int minPrimitives = 0; /* For smarter vector memory allocation later */ - ObjMesh(ObjMeshData& d, MeshPrimitive p): data(d), primitive(p) {} + explicit ObjMesh(ObjMeshData& d, MeshPrimitive p): data(d), primitive(p) {} std::string name() { std::string name = data.group.object.name; @@ -137,8 +134,8 @@ struct ObjMesh { name += "$" + std::to_string(data.materialId); } - const int numPrimitiveTypes = - ((data.points) ? 1 : 0) + const Int numPrimitiveTypes = + ((data.poInts) ? 1 : 0) + ((data.lines) ? 1 : 0) + ((data.faces) ? 1 : 0); if(numPrimitiveTypes > 1) { @@ -154,19 +151,22 @@ struct ObjMesh { /* The state of the imported generated by openData() */ struct ImporterState { + Containers::Array in; + std::string fileRoot; + std::vector materials; - std::unordered_map materialIds; + std::unordered_map materialIds; std::vector textures; - std::unordered_map textureIds; + std::unordered_map textureIds; std::vector> objects; std::vector> meshes; - std::unordered_map meshIds; + std::unordered_map meshIds; std::vector meshlessObjects; - std::unordered_map meshlessObjectIds; + std::unordered_map meshlessObjectIds; std::vector positions; std::vector texCoords; @@ -175,9 +175,10 @@ struct ImporterState { namespace { -int strToInt(const ArrayView str) { +/* Returns the given string parsed as Int */ +Int strToInt(const ArrayView str) { char* err; - const int i = int(strtol(str.data(), &err, 10)); + const Int i = Int(strtol(str.data(), &err, 10)); if(err == nullptr) { Error() << "Trade::ObjImporter::mesh3D(): error while converting numeric data"; return 0; @@ -186,9 +187,10 @@ int strToInt(const ArrayView str) { return i; } -float strToFloat(const ArrayView str) { +/* Returns the given string parsed as Float */ +Float strToFloat(const ArrayView str) { char* err; - const float f = strtof(str.data(), &err); + const Float f = strtof(str.data(), &err); if(err == nullptr) { Error() << "Trade::ObjImporter::mesh3D(): error while converting numeric data"; return 0; @@ -197,10 +199,15 @@ float strToFloat(const ArrayView str) { return f; } -// find first index of c in pos, cancel search at newline or whitespace if flag set. -int findNext(const ArrayView pos, char c, bool termByNewline=false, bool termByWhitespace=false) { - const int size = pos.size(); - for(int i = 0; i < size; ++i) { +/* + * Returns the index of the next occurrence of `c` or -1, if it could not be found. + * + * @param termByNewline if `true`, the search will terminate at '\n' and return -1. + * @param termByWhitespace if `true`, the search will terminate at ' ' and return -1. + */ +Int findNext(const ArrayView pos, char c, bool termByNewline=false, bool termByWhitespace=false) { + const Int size = pos.size(); + for(Int i = 0; i < size; ++i) { if(pos[i] == c) { return i; } @@ -212,11 +219,13 @@ int findNext(const ArrayView pos, char c, bool termByNewline=false, bool t return -1; } -// find next non-whitespace char and return suffix at that point. -// param newlineIsWhitespace if "true", '\n' and '\r' are skipped also -ArrayView skipWhitespaces(const ArrayView pos, bool newlineIsWhitespace=true) { - const int size = pos.size(); - for(int i = 0; i < size; ++i) { +/* + * Return suffix beginning at next non-whitespace character + * @param newlineIsWhitespace if `true`, '\n' and '\r' are skipped also + */ +ArrayView skipWhitespaces(const ArrayView pos, bool newlineIsWhitespace=true) { + const Int size = pos.size(); + for(Int i = 0; i < size; ++i) { const char c = pos[i]; if(c != ' ' && c != '\t' && c != '\0' && (!newlineIsWhitespace || (c != '\n' && c != '\r'))) { return pos.suffix(i); @@ -225,53 +234,53 @@ ArrayView skipWhitespaces(const ArrayView pos, bool newlineIsWhitesp return {}; } -// returns suffix after next '\n' -ArrayView ignoreLine(const ArrayView pos) { +/* Returns suffix after the next '\n' or the given ArrayView if no newline could be found */ +ArrayView ignoreLine(const ArrayView pos) { 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, ArrayView> nextLine(const ArrayView& pos) { - int i = Math::min(findNext(pos, '\n'), findNext(pos, '\r')); +/* Returns a prefix until the next '\n' and the suffix after the newline. */ +std::pair, ArrayView> nextLine(const ArrayView& pos) { + Int i = Math::min(findNext(pos, '\n'), findNext(pos, '\r')); if(i == -1) { i = pos.size(); - return std::make_tuple(pos.prefix(i), pos.suffix(i)); + return {pos.prefix(i), pos.suffix(i)}; } - return std::make_tuple(pos.prefix(i), pos.suffix(i+1)); + return {pos.prefix(i), pos.suffix(i+1)}; } -bool atEndOfLine(const ArrayView pos) { - int i = 0; +/* Returns true if the next non-whitespace character is a newline character */ +bool atEndOfLine(const ArrayView pos) { + Int i = 0; while(pos[i] == ' ') ++i; return (pos[i] == '\n' || pos[i] == '\r'); } -// get string of content until next ' ' and also return ArrayView for data after the word. -std::tuple, ArrayView> nextWord(const ArrayView pos) { - int i = 0; - const int size = pos.size(); +/* Returns the prefix until the next ' ' character and the suffix starting with it */ +std::pair, ArrayView> nextWord(const ArrayView pos) { + Int i = 0; + const Int size = pos.size(); for(; i < size; ++i) { if(pos[i] == ' ' || pos[i] == '\r' || pos[i] == '\n' || pos[i] == '\0') { break; } } - return std::make_tuple(pos.prefix(i), pos.suffix(i)); + return {pos.prefix(i), pos.suffix(i)}; } -std::tuple, ArrayView> parseLine(const ArrayView pos) { - std::array indices{0, 0}; - ArrayView endpos{}; +/* Parse indices for a line, e.g. "1/2" */ +std::pair, ArrayView> parseLine(const ArrayView pos) { + std::array indices{0, 0}; + ArrayView endpos{}; - int i = findNext(pos, '/', true, true); + Int i = findNext(pos, '/', true, true); if(i == -1) { /* v1 v2 rather than v1/t1 v2/t2 or v1/ v2/ */ - ArrayView word; + ArrayView word; std::tie(word, endpos) = nextWord(pos); indices[0] = strToInt(word); - return std::make_tuple(indices, endpos); + return {indices, endpos}; } indices[0] = strToInt(pos.prefix(i)); @@ -291,30 +300,28 @@ std::tuple, ArrayView> parseLine(const ArrayView } } - return std::make_tuple(indices, endpos); + return {indices, endpos}; } -// 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, ArrayView> parseVertex(const ArrayView pos) { - std::array indices{0, 0, 0}; - ArrayView endpos{}; +/* Parse a "v/n/t" string to indices for a face */ +std::pair, ArrayView> parseVertex(const ArrayView pos) { + std::array indices{0, 0, 0}; + ArrayView endpos{}; - int i = findNext(pos, '/', true, true); + Int i = findNext(pos, '/', true, true); if(i == -1) { /* v1 v2 rather than v1/t1 v2/t2 or v1/ v2/ */ - ArrayView word; + ArrayView word; std::tie(word, endpos) = nextWord(pos); indices[0] = strToInt(word); - return std::make_tuple(indices, endpos); + return {indices, endpos}; } indices[0] = strToInt(pos.prefix(i)); endpos = pos.suffix(i+1); i = findNext(endpos, '/', true, true); - if(i != -1) { /* there may not be a normal! Eg. "1//2", in which case the indices of the / are 1 apart */ + if(i != -1) { /* There may not be a normal! Eg. "1//2", in which case the indices of the / are 1 apart */ auto prefix = endpos.prefix(i); if(!prefix.empty()) { indices[1] = strToInt(prefix); @@ -322,13 +329,13 @@ std::tuple, ArrayView> parseVertex(const ArrayView, ArrayView> parseVertex(const ArrayView -ArrayView getVector(ArrayView pos, Math::Vector& v) { - ArrayView word; +template Math::Vector parseVector(ArrayView& pos) { + ArrayView word; + Math::Vector v; - for(int i = 0; i < int(D); ++i) { - pos = skipWhitespaces(pos); - std::tie(word, pos) = nextWord(pos); + for(Int i = 0; i < Int(dimensions); ++i) { + std::tie(word, pos) = nextWord(skipWhitespaces(pos)); v[i] = strToFloat(word); } - return pos; + return v; } -template std::vector reindex(const std::vector& indices, std::vector& data) { +template std::vector reindex(const std::vector& indices, const std::vector& 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; + return {}; } return MeshTools::duplicate(indices, data); @@ -372,32 +378,32 @@ ObjImporter::~ObjImporter() = default; auto ObjImporter::doFeatures() const -> Features { return Feature::OpenData; } -bool ObjImporter::doIsOpened() const { return _in; } +void ObjImporter::doClose() { _state->in = nullptr; } -void ObjImporter::doClose() { _in = nullptr; } +bool ObjImporter::doIsOpened() const { return _state->in; } void ObjImporter::doOpenFile(const std::string& filename) { - _fileRoot = filename.substr(0, filename.find_last_of('/')+1); + _state->fileRoot = Utility::Directory::path(filename); AbstractImporter::doOpenFile(filename); } void ObjImporter::doOpenData(Containers::ArrayView data) { - _in = Containers::Array{data.size()}; - std::copy(data.begin(), data.end(), _in.begin()); + _state->in = Containers::Array{data.size()}; + std::copy(data.begin(), data.end(), _state->in.begin()); _state.reset(new ImporterState); parse(); } void ObjImporter::parse() { - ArrayView line = _in; /* points to beginning of current line */ - ArrayView pos = _in; /* points to current character in line */ + ArrayView line = _state->in; /* Points to beginning of current line */ + ArrayView pos = _state->in; /* Points to current character in line */ ObjObject* object = nullptr; ObjGroup* group = nullptr; ObjMeshData* meshData = nullptr; - ArrayView section{nullptr}; - int minSectionPrimitives = 0; + ArrayView section{nullptr}; + Int minSectionPrimitives = 0; char sectionPrimitive = '?'; /* Set index 0 of data to default value */ @@ -444,12 +450,12 @@ void ObjImporter::parse() { ObjMesh* mesh; if(sectionPrimitive == 'p') { - if(!meshData->points) { + if(!meshData->poInts) { // TODO: C++ 17 _state->meshes.emplace_back(new ObjMesh{*meshData, MeshPrimitive::Points}); - meshData->points = _state->meshes.back().get(); + meshData->poInts = _state->meshes.back().get(); } - mesh = meshData->points; + mesh = meshData->poInts; } else if(sectionPrimitive == 'l') { if(!meshData->lines) { // TODO: C++ 17 @@ -457,13 +463,15 @@ void ObjImporter::parse() { meshData->lines = _state->meshes.back().get(); } mesh = meshData->lines; - } else { /* sectionPrimitive == 'f' */ + } else if(sectionPrimitive == 'f') { if(!meshData->faces) { // TODO: C++ 17 _state->meshes.emplace_back(new ObjMesh{*meshData, MeshPrimitive::Triangles}); meshData->faces = _state->meshes.back().get(); } mesh = meshData->faces; + } else { + CORRADE_ASSERT_UNREACHABLE(); } if(line.data() == nullptr) { /* Usually for the last line */ @@ -495,7 +503,7 @@ void ObjImporter::parse() { /* Parse the keyword */ std::string keyword; - ArrayView word; + ArrayView word; std::tie(word, pos) = nextWord(pos); keyword = std::string(word.data(), word.size()); @@ -504,23 +512,15 @@ void ObjImporter::parse() { /* Vertex position */ if(keyword == "v") { - // TODO C++17: Could be one slick line with emplace_back ref return. - Vector3 v; - pos = getVector<3>(pos, v); - _state->positions.push_back(v); + _state->positions.push_back(parseVector<3>(pos)); + /* Texture coordinate */ } else if(keyword == "vt") { - // TODO C++17: Could be one slick line with emplace_back ref return. - Vector2 tc; - pos = getVector<2>(pos, tc); - _state->texCoords.push_back(tc); + _state->texCoords.push_back(parseVector<2>(pos)); /* Normal */ } else if(keyword == "vn") { - // TODO C++17: Could be one slick line with emplace_back ref return. - Vector3 n; - pos = getVector<3>(pos, n); - _state->normals.push_back(n); + _state->normals.push_back(parseVector<3>(pos)); /* Indices */ } else if(keyword == "f" || keyword == "l" || keyword == "p") { @@ -544,7 +544,7 @@ void ObjImporter::parse() { finishSection(); finishObject(); - ArrayView name; + ArrayView name; std::tie(name, pos) = nextWord(pos); _state->objects.emplace_back(new ObjObject{name}); @@ -561,13 +561,13 @@ void ObjImporter::parse() { meshData = nullptr; - ArrayView name; + ArrayView name; std::tie(name, pos) = nextWord(pos); group->name = std::string{name.data(), name.size()}; /* Load a material library */ } else if(keyword == "mtllib") { - ArrayView word; + ArrayView word; pos = skipWhitespaces(pos); std::tie(word, pos) = nextWord(pos); @@ -575,11 +575,11 @@ void ObjImporter::parse() { /* Set current material and add a new mesh for it */ } else if(keyword == "usemtl") { - ArrayView word; - pos = skipWhitespaces(pos); + ArrayView word; + pos = skipWhitespaces(pos); std::tie(word, pos) = nextWord(pos); - const int materialIndex = _state->materialIds[std::string{word.data(), word.size()}]; + const Int materialIndex = _state->materialIds[std::string{word.data(), word.size()}]; if(meshData == nullptr || materialIndex != meshData->materialId) { /* Switching the material here, need to create a new mesh */ //TODO C++17 @@ -603,14 +603,14 @@ void ObjImporter::parse() { finishSection(); finishObject(); - int i = _state->meshes.size(); + Int i = _state->meshes.size(); for(auto name : _state->meshlessObjects) { _state->meshlessObjectIds[name] = i++; } } -void ObjImporter::parseMaterialLibrary(const ArrayView libname) { - std::string filename = _fileRoot + std::string(libname.data(), libname.size()); +void ObjImporter::parseMaterialLibrary(const ArrayView libname) { + std::string filename = _state->fileRoot + std::string(libname.data(), libname.size()); /* Open file */ if(!Utility::Directory::fileExists(filename)) { @@ -619,7 +619,7 @@ void ObjImporter::parseMaterialLibrary(const ArrayView libname) { } Containers::Array contents = Utility::Directory::read(filename); - ArrayView pos = contents; /* points to current character in line */ + ArrayView pos = contents; /* Points to current character in line */ ObjMaterial* mat = nullptr; @@ -633,7 +633,7 @@ void ObjImporter::parseMaterialLibrary(const ArrayView libname) { } /* Parse the keyword */ - ArrayView word; + ArrayView word; std::tie(word, pos) = nextWord(pos); std::string keyword{word.data(), word.size()}; @@ -659,49 +659,49 @@ void ObjImporter::parseMaterialLibrary(const ArrayView libname) { /* Ambient color */ if(keyword == "Ka") { - ArrayView word; + ArrayView word; - for(int i : {0, 1, 2}) { + for(Int i : {0, 1, 2}) { std::tie(word, pos) = nextWord(pos); mat->ambient[i] = strToFloat(word); } /* Diffuse color */ } else if(keyword == "Kd") { - ArrayView word; + ArrayView word; - for(int i : {0, 1, 2}) { + for(Int i : {0, 1, 2}) { std::tie(word, pos) = nextWord(pos); mat->diffuse[i] = strToFloat(word); } /* Specular color */ } else if(keyword == "Ks") { - ArrayView word; + ArrayView word; - for(int i : {0, 1, 2}) { + for(Int i : {0, 1, 2}) { std::tie(word, pos) = nextWord(pos); mat->specular[i] = strToFloat(word); } /* Specularity */ } else if(keyword == "Ns") { - ArrayView word; + ArrayView word; std::tie(word, pos) = nextWord(pos); - const float f = strToFloat(word); + const Float f = strToFloat(word); mat->specularity = f; /* Ambient texture */ } else if(keyword.substr(0, 4) == "map_") { - ArrayView line; + ArrayView line; std::tie(line, pos) = nextLine(pos); std::string texture{line.data(), line.size()}; - int textureId = -1; + Int textureId = -1; if(_state->textureIds.find(texture) == _state->textureIds.end()) { /* new texture, create it */ - int index = _state->textures.size(); + Int index = _state->textures.size(); _state->textures.push_back(texture); _state->textureIds[texture] = index; @@ -731,12 +731,6 @@ void ObjImporter::parseMaterialLibrary(const ArrayView libname) { } } -UnsignedInt ObjImporter::doMesh3DCount() const { return _state->meshes.size(); } - -UnsignedInt ObjImporter::doMaterialCount() const { return _state->materials.size(); } - -UnsignedInt ObjImporter::doImage2DCount() const { return _state->textures.size(); } - UnsignedInt ObjImporter::doObject3DCount() const { return _state->meshes.size() + _state->meshlessObjects.size(); } Int ObjImporter::doObject3DForName(const std::string& name) { @@ -754,23 +748,25 @@ Int ObjImporter::doObject3DForName(const std::string& name) { } std::string ObjImporter::doObject3DName(UnsignedInt id) { - const size_t numMeshes = _state->meshes.size(); - if(id >= numMeshes) { - return _state->meshlessObjects[id - numMeshes]; + const size_t lastMesh = _state->meshes.size() - 1; + if(id > lastMesh) { + return _state->meshlessObjects[id - lastMesh]; } - return _state->meshes[id]->name(); /* Intentional, objects are just meshes + material */ + return _state->meshes[id]->name(); } std::unique_ptr ObjImporter::doObject3D(UnsignedInt id) { - const size_t numMeshes = _state->meshes.size(); - if(id > numMeshes) { - return std::unique_ptr{new ObjectData3D{{}, {}, &_state->meshlessObjects[id - numMeshes]}}; + const size_t lastMesh = _state->meshes.size() - 1; + if(id > lastMesh) { + return std::unique_ptr{new ObjectData3D{{}, {}, &_state->meshlessObjects[id - lastMesh]}}; } const ObjMesh& mesh = *_state->meshes[id]; return std::unique_ptr{ new MeshObjectData3D{{}, {}, id, mesh.data.materialId, _state->meshes[id].get()}}; } +UnsignedInt ObjImporter::doMesh3DCount() const { return _state->meshes.size(); } + Int ObjImporter::doMesh3DForName(const std::string& name) { return _state->meshIds[name]; } @@ -782,7 +778,7 @@ std::string ObjImporter::doMesh3DName(UnsignedInt id) { std::optional ObjImporter::doMesh3D(UnsignedInt id) { const ObjMesh& mesh = *_state->meshes[id]; - const int primitiveSize = (mesh.primitive == MeshPrimitive::Triangles) + const Int primitiveSize = (mesh.primitive == MeshPrimitive::Triangles) ? 3 : ((mesh.primitive == MeshPrimitive::Lines) ? 2 : 1); std::vector positionIndices; @@ -812,8 +808,8 @@ std::optional ObjImporter::doMesh3D(UnsignedInt id) { CORRADE_ASSERT(pos[0] == 'f' && pos[1] == ' ', "Unexpected primitive keyword for Triangles", {}); pos = pos.suffix(2); - for(int i = 0; i < 3; ++i) { - std::array vertex; + for(Int i = 0; i < 3; ++i) { + std::array vertex; std::tie(vertex, pos) = parseVertex(pos); positionIndices.push_back(vertex[0]); textureCoordinateIndices.push_back(vertex[1]); @@ -833,7 +829,7 @@ std::optional ObjImporter::doMesh3D(UnsignedInt id) { pos = pos.suffix(2); while(!atEndOfLine(pos)) { - std::array line; + std::array line; std::tie(line, pos) = parseLine(pos); positionIndices.push_back(line[0]); textureCoordinateIndices.push_back(line[1]); @@ -850,7 +846,7 @@ std::optional ObjImporter::doMesh3D(UnsignedInt id) { CORRADE_ASSERT(pos[0] == 'p' && pos[1] == ' ', "Unexpected primitive keyword for Points", {}); pos = pos.suffix(2); - ArrayView word; + ArrayView word; while(!atEndOfLine(pos)) { std::tie(word, pos) = nextWord(pos); positionIndices.push_back(strToInt(word)); @@ -879,19 +875,16 @@ std::optional ObjImporter::doMesh3D(UnsignedInt id) { indices = MeshTools::combineIndexArrays(arrays); /* Reindex data arrays */ - try { - positionLayers.push_back(reindex(positionIndices, _state->positions)); - if(hasNormals) normalLayers.push_back(reindex(normalIndices, _state->normals)); - if(hasTexCoords) texCoordLayers.push_back(reindex(textureCoordinateIndices, _state->texCoords)); - } catch(...) { - /* Error message already printed */ - return std::nullopt; - } + positionLayers.push_back(reindex(positionIndices, _state->positions)); + if(hasNormals) normalLayers.push_back(reindex(normalIndices, _state->normals)); + if(hasTexCoords) texCoordLayers.push_back(reindex(textureCoordinateIndices, _state->texCoords)); return MeshData3D(mesh.primitive, std::move(indices), std::move(positionLayers), std::move(normalLayers), std::move(texCoordLayers), {}, &mesh); } +UnsignedInt ObjImporter::doMaterialCount() const { return _state->materials.size(); } + std::unique_ptr ObjImporter::doMaterial(const UnsignedInt id) { ObjMaterial& objMat = _state->materials[id]; PhongMaterialData::Flags flags; @@ -931,11 +924,13 @@ std::unique_ptr ObjImporter::doMaterial(const UnsignedInt return std::unique_ptr(mat); } +UnsignedInt ObjImporter::doImage2DCount() const { return _state->textures.size(); } + std::optional 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 imageImporter = manager()->loadAndInstantiate("TgaImporter"); // probably AnyImageImporter would be the way to go here... - if(!imageImporter->openFile(_fileRoot + _state->textures[id])) { + if(!imageImporter->openFile(_state->fileRoot + _state->textures[id])) { return std::nullopt; } diff --git a/src/MagnumPlugins/ObjImporter/ObjImporter.h b/src/MagnumPlugins/ObjImporter/ObjImporter.h index b13d37ced..efc8d3365 100644 --- a/src/MagnumPlugins/ObjImporter/ObjImporter.h +++ b/src/MagnumPlugins/ObjImporter/ObjImporter.h @@ -112,11 +112,9 @@ class MAGNUM_OBJIMPORTER_EXPORT ObjImporter: public AbstractImporter { MAGNUM_OBJIMPORTER_LOCAL std::optional doImage2D(UnsignedInt id); MAGNUM_OBJIMPORTER_LOCAL void parse(); - MAGNUM_OBJIMPORTER_LOCAL void parseMaterialLibrary(Containers::ArrayView libname); + MAGNUM_OBJIMPORTER_LOCAL void parseMaterialLibrary(Containers::ArrayView libname); - Containers::Array _in; std::unique_ptr _state; - std::string _fileRoot; }; }} diff --git a/src/MagnumPlugins/ObjImporter/Test/Test.cpp b/src/MagnumPlugins/ObjImporter/Test/Test.cpp index ad341af8e..3d6e8b359 100644 --- a/src/MagnumPlugins/ObjImporter/Test/Test.cpp +++ b/src/MagnumPlugins/ObjImporter/Test/Test.cpp @@ -429,6 +429,7 @@ void ObjImporterTest::wrongFloat() { std::ostringstream out; Error redirectError{&out}; CORRADE_COMPARE(out.str(), "Trade::ObjImporter::mesh3D(): error while converting numeric data\n"); + CORRADE_VERIFY(!importer.mesh3D(id)); } void ObjImporterTest::wrongInteger() { @@ -735,12 +736,12 @@ void ObjImporterTest::multiMaterialObject() { CORRADE_COMPARE(data->primitive(), MeshPrimitive::Triangles); CORRADE_COMPARE(data->positionArrayCount(), 1); CORRADE_COMPARE(data->positions(0), (std::vector{ - {Vector3(1.72414, 18.9233, -3.20162), - Vector3(2.74428, -0.499733, -3.50576), - Vector3(-1.92235, -0.846268, 2.9722), - Vector3(1.72414, 18.9233, -3.20162), - Vector3(-1.92235, -0.846268, 2.9722), - Vector3(2.43556, 18.8755, 2.23745)} + {{1.72414f, 18.9233f, -3.20162f}, + {2.74428f, -0.499733f, -3.50576f}, + {-1.92235f, -0.846268f, 2.9722f}, + {1.72414f, 18.9233f, -3.20162f}, + {-1.92235f, -0.846268f, 2.9722f}, + {2.43556f, 18.8755f, 2.23745f}} })); data = importer.mesh3D(1); @@ -762,6 +763,7 @@ void ObjImporterTest::unsupportedKeyword() { CORRADE_COMPARE(data->positions(0), (std::vector{ {0.0f, 1.0f, 2.0f} })); + CORRADE_COMPARE(data->indices(), std::vector{0}); } void ObjImporterTest::unknownKeyword() { diff --git a/src/MagnumPlugins/ObjImporter/Test/multiMaterial.mtl b/src/MagnumPlugins/ObjImporter/Test/multiMaterial.mtl index fcb923d86..5a4b92bc7 100644 --- a/src/MagnumPlugins/ObjImporter/Test/multiMaterial.mtl +++ b/src/MagnumPlugins/ObjImporter/Test/multiMaterial.mtl @@ -5,4 +5,5 @@ map_Ka Textures/mat_0.tga newmtl mat_1 Ka 1 1 1 -map_Ka Textures/mat_1.tga \ No newline at end of file +map_Ka Textures/mat_1.tga + diff --git a/src/MagnumPlugins/ObjImporter/Test/multiMaterial.obj b/src/MagnumPlugins/ObjImporter/Test/multiMaterial.obj index 4da632842..f809fe2fc 100644 --- a/src/MagnumPlugins/ObjImporter/Test/multiMaterial.obj +++ b/src/MagnumPlugins/ObjImporter/Test/multiMaterial.obj @@ -56,3 +56,4 @@ usemtl mat_1 # faces f 5/5/5 6/6/6 7/7/7 f 5/5/5 7/7/7 8/8/8 +