From 77e38fcce3a162863bed42190be4b51ecafa575d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Tue, 4 Mar 2014 13:10:27 +0100 Subject: [PATCH] New ObjImporter plugin. --- CMakeLists.txt | 3 +- doc/building.dox | 1 + doc/cmake.dox | 2 + modules/FindMagnum.cmake | 4 + src/MagnumPlugins/CMakeLists.txt | 4 + src/MagnumPlugins/ObjImporter/CMakeLists.txt | 53 ++ .../ObjImporter/ObjImporter.conf | 0 src/MagnumPlugins/ObjImporter/ObjImporter.cpp | 408 ++++++++++ src/MagnumPlugins/ObjImporter/ObjImporter.h | 86 ++ .../ObjImporter/Test/CMakeLists.txt | 31 + src/MagnumPlugins/ObjImporter/Test/Test.cpp | 737 ++++++++++++++++++ .../ObjImporter/Test/configure.h.cmake | 26 + .../ObjImporter/Test/emptyFile.obj | 1 + .../ObjImporter/Test/keywords.obj | 7 + .../ObjImporter/Test/lineMesh.obj | 8 + .../ObjImporter/Test/missingData.obj | 23 + .../ObjImporter/Test/mixedPrimitives.obj | 13 + .../ObjImporter/Test/moreMeshes.obj | 21 + .../ObjImporter/Test/namedMesh.obj | 2 + .../ObjImporter/Test/normals.obj | 12 + .../ObjImporter/Test/optionalCoordinates.obj | 17 + .../ObjImporter/Test/pointMesh.obj | 10 + .../ObjImporter/Test/textureCoordinates.obj | 12 + .../Test/textureCoordinatesNormals.obj | 17 + .../ObjImporter/Test/triangleMesh.obj | 9 + .../ObjImporter/Test/unnamedFirstMesh.obj | 2 + .../ObjImporter/Test/wrongIndexCount.obj | 13 + .../ObjImporter/Test/wrongNumberCount.obj | 29 + .../ObjImporter/Test/wrongNumbers.obj | 20 + .../ObjImporter/pluginRegistration.cpp | 29 + 30 files changed, 1599 insertions(+), 1 deletion(-) create mode 100644 src/MagnumPlugins/ObjImporter/CMakeLists.txt create mode 100644 src/MagnumPlugins/ObjImporter/ObjImporter.conf create mode 100644 src/MagnumPlugins/ObjImporter/ObjImporter.cpp create mode 100644 src/MagnumPlugins/ObjImporter/ObjImporter.h create mode 100644 src/MagnumPlugins/ObjImporter/Test/CMakeLists.txt create mode 100644 src/MagnumPlugins/ObjImporter/Test/Test.cpp create mode 100644 src/MagnumPlugins/ObjImporter/Test/configure.h.cmake create mode 100644 src/MagnumPlugins/ObjImporter/Test/emptyFile.obj create mode 100644 src/MagnumPlugins/ObjImporter/Test/keywords.obj create mode 100644 src/MagnumPlugins/ObjImporter/Test/lineMesh.obj create mode 100644 src/MagnumPlugins/ObjImporter/Test/missingData.obj create mode 100644 src/MagnumPlugins/ObjImporter/Test/mixedPrimitives.obj create mode 100644 src/MagnumPlugins/ObjImporter/Test/moreMeshes.obj create mode 100644 src/MagnumPlugins/ObjImporter/Test/namedMesh.obj create mode 100644 src/MagnumPlugins/ObjImporter/Test/normals.obj create mode 100644 src/MagnumPlugins/ObjImporter/Test/optionalCoordinates.obj create mode 100644 src/MagnumPlugins/ObjImporter/Test/pointMesh.obj create mode 100644 src/MagnumPlugins/ObjImporter/Test/textureCoordinates.obj create mode 100644 src/MagnumPlugins/ObjImporter/Test/textureCoordinatesNormals.obj create mode 100644 src/MagnumPlugins/ObjImporter/Test/triangleMesh.obj create mode 100644 src/MagnumPlugins/ObjImporter/Test/unnamedFirstMesh.obj create mode 100644 src/MagnumPlugins/ObjImporter/Test/wrongIndexCount.obj create mode 100644 src/MagnumPlugins/ObjImporter/Test/wrongNumberCount.obj create mode 100644 src/MagnumPlugins/ObjImporter/Test/wrongNumbers.obj create mode 100644 src/MagnumPlugins/ObjImporter/pluginRegistration.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index ee3a229dc..b3ff23af4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -41,7 +41,7 @@ option(WITH_FIND_MODULE "Install FindMagnum.cmake module into CMake's module dir # Parts of the library option(WITH_AUDIO "Build Audio library" OFF) option(WITH_DEBUGTOOLS "Build DebugTools library" ON) -cmake_dependent_option(WITH_MESHTOOLS "Build MeshTools library" ON "NOT WITH_DEBUGTOOLS" ON) +cmake_dependent_option(WITH_MESHTOOLS "Build MeshTools library" ON "NOT WITH_DEBUGTOOLS;NOT WITH_OBJIMPORTER" ON) cmake_dependent_option(WITH_PRIMITIVES "Builf Primitives library" ON "NOT WITH_DEBUGTOOLS" ON) cmake_dependent_option(WITH_SCENEGRAPH "Build SceneGraph library" ON "NOT WITH_DEBUGTOOLS;NOT WITH_SHAPES" ON) cmake_dependent_option(WITH_SHADERS "Build Shaders library" ON "NOT WITH_DEBUGTOOLS" ON) @@ -85,6 +85,7 @@ endif() # Plugins cmake_dependent_option(WITH_MAGNUMFONT "Build MagnumFont plugin" OFF "WITH_TEXT" OFF) cmake_dependent_option(WITH_MAGNUMFONTCONVERTER "Build MagnumFontConverter plugin" OFF "NOT MAGNUM_TARGET_GLES;WITH_TEXT" OFF) +option(WITH_OBJIMPORTER "Build ObjImporter plugin" OFF) cmake_dependent_option(WITH_TGAIMAGECONVERTER "Build TgaImageConverter plugin" OFF "NOT WITH_MAGNUMFONTCONVERTER" ON) cmake_dependent_option(WITH_TGAIMPORTER "Build TgaImporter plugin" OFF "NOT WITH_MAGNUMFONT" ON) cmake_dependent_option(WITH_WAVAUDIOIMPORTER "Build WavAudioImporter plugin" OFF "WITH_AUDIO" OFF) diff --git a/doc/building.dox b/doc/building.dox index 15516371b..2173ab1de 100644 --- a/doc/building.dox +++ b/doc/building.dox @@ -216,6 +216,7 @@ default. - `WITH_MAGNUMFONTCONVERTER` -- @ref Text::MagnumFontConverter "MagnumFontConverter" plugin. Available only if `WITH_TEXT` is enabled. Enables also building of @ref Trade::TgaImageConverter "TgaImageConverter" plugin. +- `WITH_OBJIMPORTER` -- @ref Trade::ObjImporter "ObjImporter" plugin. - `WITH_TGAIMPORTER` -- @ref Trade::TgaImporter "TgaImporter" plugin. - `WITH_TGAIMAGECONVERTER` -- @ref Trade::TgaImageConverter "TgaImageConverter" plugin. diff --git a/doc/cmake.dox b/doc/cmake.dox index c1b60c6e9..d106817c5 100644 --- a/doc/cmake.dox +++ b/doc/cmake.dox @@ -103,6 +103,8 @@ dependencies, you need to find the dependency and then link to it. `%Text` component and `TgaImporter` plugin) - `MagnumFontConverter` -- @ref Text::MagnumFontConverter "MagnumFontConverter" plugin (depends on `%Text` component and `%TgaImageConverter` plugin) +- `ObjImporter` -- @ref Trade::ObjImporter "ObjImporter" plugin (depends on + `%MeshTools` component) - `TgaImageConverter` -- @ref Trade::TgaImageConverter "TgaImageConverter" plugin - `TgaImporter` -- @ref Trade::TgaImporter "TgaImporter" plugin diff --git a/modules/FindMagnum.cmake b/modules/FindMagnum.cmake index 3257507ee..f4c822644 100644 --- a/modules/FindMagnum.cmake +++ b/modules/FindMagnum.cmake @@ -35,6 +35,7 @@ # and TgaImporter plugin) # MagnumFontConverter - Magnum bitmap font converter plugin (depends on Text # component and TgaImageConverter plugin) +# ObjImporter - OBJ importer plugin # TgaImageConverter - TGA image converter plugin # TgaImporter - TGA importer plugin # WavAudioImporter - WAV audio importer plugin (depends on Audio component) @@ -334,6 +335,9 @@ foreach(component ${Magnum_FIND_COMPONENTS}) set(_MAGNUM_${_COMPONENT}_INCLUDE_PATH_NAMES Atlas.h) endif() + # The plugins don't have any dependencies, nothing additional to do for + # them + # Try to find the includes if(_MAGNUM_${_COMPONENT}_INCLUDE_PATH_NAMES) find_path(_MAGNUM_${_COMPONENT}_INCLUDE_DIR diff --git a/src/MagnumPlugins/CMakeLists.txt b/src/MagnumPlugins/CMakeLists.txt index 188c83af9..549a69ba0 100644 --- a/src/MagnumPlugins/CMakeLists.txt +++ b/src/MagnumPlugins/CMakeLists.txt @@ -40,6 +40,10 @@ if(WITH_TEXT AND WITH_MAGNUMFONTCONVERTER AND NOT MAGNUM_TARGET_GLES) add_subdirectory(MagnumFontConverter) endif() +if(WITH_OBJIMPORTER) + add_subdirectory(ObjImporter) +endif() + if(WITH_TGAIMAGECONVERTER) add_subdirectory(TgaImageConverter) endif() diff --git a/src/MagnumPlugins/ObjImporter/CMakeLists.txt b/src/MagnumPlugins/ObjImporter/CMakeLists.txt new file mode 100644 index 000000000..5ab131c03 --- /dev/null +++ b/src/MagnumPlugins/ObjImporter/CMakeLists.txt @@ -0,0 +1,53 @@ +# +# This file is part of Magnum. +# +# Copyright © 2010, 2011, 2012, 2013, 2014 +# Vladimír Vondruš +# +# Permission is hereby granted, free of charge, to any person obtaining a +# copy of this software and associated documentation files (the "Software"), +# to deal in the Software without restriction, including without limitation +# the rights to use, copy, modify, merge, publish, distribute, sublicense, +# and/or sell copies of the Software, and to permit persons to whom the +# Software is furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +# DEALINGS IN THE SOFTWARE. +# + +add_library(ObjImporterObjects OBJECT ObjImporter.cpp) +if(NOT BUILD_STATIC OR BUILD_STATIC_PIC) + set_target_properties(ObjImporterObjects PROPERTIES COMPILE_FLAGS "${CMAKE_SHARED_LIBRARY_CXX_FLAGS}") +endif() + +add_plugin(ObjImporter ${MAGNUM_PLUGINS_IMPORTER_INSTALL_DIR} + ObjImporter.conf + $ + pluginRegistration.cpp) +target_link_libraries(ObjImporter Magnum MagnumMeshTools) + +install(FILES ObjImporter.h DESTINATION ${MAGNUM_PLUGINS_INCLUDE_INSTALL_DIR}/ObjImporter) + +if(BUILD_TESTS) + add_library(ObjImporterTestLib STATIC $) + target_link_libraries(ObjImporterTestLib Magnum MagnumMeshTools) + + # On Windows we need to install first and then run the tests to avoid "DLL + # not found" hell, thus we need to install this too + if(WIN32 AND NOT CMAKE_CROSSCOMPILING) + install(TARGETS ObjImporterTestLib + RUNTIME DESTINATION ${MAGNUM_BINARY_INSTALL_DIR} + LIBRARY DESTINATION ${MAGNUM_LIBRARY_INSTALL_DIR} + ARCHIVE DESTINATION ${MAGNUM_LIBRARY_INSTALL_DIR}) + endif() + + add_subdirectory(Test) +endif() diff --git a/src/MagnumPlugins/ObjImporter/ObjImporter.conf b/src/MagnumPlugins/ObjImporter/ObjImporter.conf new file mode 100644 index 000000000..e69de29bb diff --git a/src/MagnumPlugins/ObjImporter/ObjImporter.cpp b/src/MagnumPlugins/ObjImporter/ObjImporter.cpp new file mode 100644 index 000000000..1d223fcae --- /dev/null +++ b/src/MagnumPlugins/ObjImporter/ObjImporter.cpp @@ -0,0 +1,408 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014 + Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +*/ + +#include "ObjImporter.h" + +#include +#include +#include +#include +#include +#include + +#include "Magnum/Math/Vector3.h" +#include "Magnum/Trade/MeshData3D.h" +#include +#include +#include + +namespace Magnum { namespace Trade { + +struct ObjImporter::File { + std::unordered_map meshesForName; + std::vector meshNames; + std::vector> meshes; + std::unique_ptr in; +}; + +namespace { + +void ignoreLine(std::istream& in) { + in.ignore(std::numeric_limits::max(), '\n'); +} + +template Math::Vector extractFloatData(std::string str, Float* extra = nullptr) { + std::vector data = Utility::String::splitWithoutEmptyParts(str, ' '); + if(data.size() < size || data.size() > size + (extra ? 1 : 0)) { + Error() << "Trade::ObjImporter::mesh3D(): invalid float array size"; + throw 0; + } + + Math::Vector output; + for(std::size_t i = 0; i != size; ++i) + output[i] = std::stof(data[i]); + + if(data.size() == size+1) *extra = std::stof(data.back()); + + return output; +} + +template void reindex(const std::vector& indices, 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; + } + + data = MeshTools::duplicate(indices, data); +} + +} + +ObjImporter::ObjImporter() = default; + +ObjImporter::ObjImporter(PluginManager::AbstractManager& manager, std::string plugin): AbstractImporter(manager, std::move(plugin)) {} + +ObjImporter::~ObjImporter() = default; + +auto ObjImporter::doFeatures() const -> Features { return Feature::OpenData; } + +void ObjImporter::doClose() { _file.reset(); } + +bool ObjImporter::doIsOpened() const { return !!_file; } + +void ObjImporter::doOpenFile(const std::string& filename) { + /* Open file in *text* mode (to avoid \r handling) */ + std::unique_ptr in{new std::ifstream{filename}}; + if(!in->good()) { + Error() << "Trade::ObjImporter::openFile(): cannot open file" << filename; + return; + } + + _file.reset(new File); + _file->in = std::move(in); + parseMeshNames(); +} + +void ObjImporter::doOpenData(Containers::ArrayReference data) { + /* Open file in *text* mode (to avoid \r handling) */ + _file.reset(new File); + _file->in.reset(new std::istringstream{{reinterpret_cast(data.begin()), data.size()}}); + + parseMeshNames(); +} + +void ObjImporter::parseMeshNames() { + bool hasData = false; + std::string name; + std::streampos begin = 0; + while(_file->in->good()) { + /* The previous object ends at the beginning of this line */ + const std::streampos end = _file->in->tellg(); + + /* Comment line */ + if(_file->in->peek() == '#') { + ignoreLine(*_file->in); + continue; + } + + /* Parse the keyword */ + std::string keyword; + *_file->in >> keyword; + + /* Object name */ + if(keyword == "o") { + /* If there was any previous object, this is name of new object, + save the previous one */ + if(hasData) { + if(!name.empty()) _file->meshesForName.emplace(name, _file->meshes.size()); + _file->meshNames.emplace_back(std::move(name)); + _file->meshes.emplace_back(begin, end); + + /* Otherwise it's the name of first object (there weren't any data + before) */ + } else hasData = true; + + /* Get name of new object, the object then starts at the next line */ + std::getline(*_file->in, name); + name = Utility::String::trim(name); + begin = _file->in->tellg(); + continue; + } + + /* If there are any data before the first name, it means that the first + object can be unnamed */ + if(!hasData) for(const std::string& data: {"v", "vt", "vn", "p", "l", "f"}) { + if(keyword == data) { + hasData = true; + break; + } + } + + /* Ignore the rest of the line */ + ignoreLine(*_file->in); + } + + /* Add also the last object */ + _file->in->clear(); + _file->in->seekg(0, std::ios::end); + if(!name.empty()) _file->meshesForName.emplace(name, _file->meshes.size()); + _file->meshNames.emplace_back(std::move(name)); + _file->meshes.emplace_back(begin, _file->in->tellg()); +} + +UnsignedInt ObjImporter::doMesh3DCount() const { return _file->meshes.size(); } + +Int ObjImporter::doMesh3DForName(const std::string& name) { + const auto it = _file->meshesForName.find(name); + return it == _file->meshesForName.end() ? -1 : it->second; +} + +std::string ObjImporter::doMesh3DName(UnsignedInt id) { + return _file->meshNames[id]; +} + +std::optional ObjImporter::doMesh3D(UnsignedInt id) { + /* Seek the file */ + std::streampos begin, end; + std::tie(begin, end) = _file->meshes[id]; + _file->in->seekg(begin); + + std::optional primitive; + std::vector positions; + std::vector> textureCoordinates; + std::vector> normals; + std::vector positionIndices; + std::vector textureCoordinateIndices; + std::vector normalIndices; + + try { while(_file->in->good() && _file->in->tellg() < end) { + /* Ignore comments */ + if(_file->in->peek() == '#') { + ignoreLine(*_file->in); + continue; + } + + /* Get the line */ + std::string line; + std::getline(*_file->in, line); + line = Utility::String::trim(line); + + /* Ignore empty lines */ + if(line.empty()) continue; + + /* Split the line into keyword and contents */ + const std::size_t keywordEnd = line.find(' '); + const std::string keyword = line.substr(0, keywordEnd); + const std::string contents = keywordEnd != std::string::npos ? + Utility::String::ltrim(line.substr(keywordEnd+1)) : ""; + + /* Vertex position */ + if(keyword == "v") { + Float extra{1.0f}; + const Vector3 data = extractFloatData<3>(contents, &extra); + if(!Math::TypeTraits::equals(extra, 1.0f)) { + Error() << "Trade::ObjImporter::mesh3D(): homogeneous coordinates are not supported"; + return std::nullopt; + } + + positions.push_back(data); + + /* Texture coordinate */ + } else if(keyword == "vt") { + Float extra{0.0f}; + const auto data = extractFloatData<2>(contents, &extra); + if(!Math::TypeTraits::equals(extra, 0.0f)) { + Error() << "Trade::ObjImporter::mesh3D(): 3D texture coordinates are not supported"; + return std::nullopt; + } + + if(textureCoordinates.empty()) textureCoordinates.push_back({}); + textureCoordinates.front().push_back(data); + + /* Normal */ + } else if(keyword == "vn") { + if(normals.empty()) normals.push_back({}); + normals.front().push_back(extractFloatData<3>(contents)); + + /* Indices */ + } else if(keyword == "p" || keyword == "l" || keyword == "f") { + const std::vector indexTuples = Utility::String::splitWithoutEmptyParts(contents, ' '); + + /* 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 std::nullopt; + } + + /* Check vertex count per primitive */ + if(indexTuples.size() != 1) { + Error() << "Trade::ObjImporter::mesh3D(): wrong index count for point"; + return std::nullopt; + } + + primitive = MeshPrimitive::Points; + + /* 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 std::nullopt; + } + + /* Check vertex count per primitive */ + if(indexTuples.size() != 2) { + Error() << "Trade::ObjImporter::mesh3D(): wrong index count for line"; + return std::nullopt; + } + + primitive = MeshPrimitive::Lines; + + /* 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 std::nullopt; + } + + /* Check vertex count per primitive */ + if(indexTuples.size() < 3) { + Error() << "Trade::ObjImporter::mesh3D(): wrong index count for triangle"; + return std::nullopt; + } else if(indexTuples.size() != 3) { + Error() << "Trade::ObjImporter::mesh3D(): polygons are not supported"; + return std::nullopt; + } + + primitive = MeshPrimitive::Triangles; + + } else CORRADE_ASSERT_UNREACHABLE(); + + for(const std::string& indexTuple: indexTuples) { + std::vector indices = Utility::String::split(indexTuple, '/'); + if(indices.size() > 3) { + Error() << "Trade::ObjImporter::mesh3D(): invalid index data"; + return std::nullopt; + } + + /* Indices in OBJ file start from 1 */ + + /* Position indices */ + positionIndices.push_back(std::stoul(indices[0])-1); + + /* Texture coordinates */ + if(indices.size() == 2 || (indices.size() == 3 && !indices[1].empty())) + textureCoordinateIndices.push_back(std::stoul(indices[1])-1); + + /* Normal indices */ + if(indices.size() == 3) + normalIndices.push_back(std::stoul(indices[2])-1); + } + + /* 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 std::nullopt; + } + + }} catch(std::exception) { + Error() << "Trade::ObjImporter::mesh3D(): error while converting numeric data"; + return std::nullopt; + } catch(...) { + /* Error message already printed */ + return std::nullopt; + } + + /* There should be at least indexed position data */ + if(positions.empty() || positionIndices.empty()) { + Error() << "Trade::ObjImporter::mesh3D(): incomplete position data"; + return std::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 std::nullopt; + } + if(textureCoordinates.empty() != textureCoordinateIndices.empty()) { + Error() << "Trade::ObjImporter::mesh3D(): incomplete texture coordinate data"; + return std::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 std::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 std::nullopt; + } + + /* Merge index arrays, if there aren't just the positions */ + std::vector indices; + if(!normalIndices.empty() || !textureCoordinateIndices.empty()) { + std::vector>> arrays; + arrays.reserve(3); + arrays.push_back(positionIndices); + if(!normalIndices.empty()) arrays.push_back(normalIndices); + if(!textureCoordinateIndices.empty()) arrays.push_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 std::nullopt; + } + + /* Otherwise just use the original position index array. Don't forget to + check range */ + } else { + indices = std::move(positionIndices); + for(UnsignedInt i: indices) if(i >= positions.size()) { + Error() << "Trade::ObjImporter::mesh3D(): index out of range"; + return std::nullopt; + } + } + + return MeshData3D(*primitive, std::move(indices), {std::move(positions)}, std::move(normals), std::move(textureCoordinates)); +} + +}} diff --git a/src/MagnumPlugins/ObjImporter/ObjImporter.h b/src/MagnumPlugins/ObjImporter/ObjImporter.h new file mode 100644 index 000000000..cf6931667 --- /dev/null +++ b/src/MagnumPlugins/ObjImporter/ObjImporter.h @@ -0,0 +1,86 @@ +#ifndef Magnum_Trade_ObjImporter_h +#define Magnum_Trade_ObjImporter_h +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014 + Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +*/ + +/** @file + * @brief Class @ref Magnum::Trade::ObjImporter + */ + +#include "Magnum/Trade/AbstractImporter.h" + +namespace Magnum { namespace Trade { + +/** +@brief OBJ importer plugin + +Supported features: +- multiple objects +- vertex positions, normals and 2D texture coordinates +- triangles, lines and points + +Polygons (quads etc.), automatic normal generation and material properties are +currently not supported. + +This plugin is built if `WITH_OBJIMPORTER` is enabled when building %Magnum. To +use dynamic plugin, you need to load `%ObjImporter` plugin from +`MAGNUM_PLUGINS_IMPORTER_DIR`. To use static plugin or use this as a dependency +of another plugin, you need to request `%ObjImporter` component of `%Magnum` +package in CMake and link to `${MAGNUM_OBJIMPORTER_LIBRARIES}`. See +@ref building, @ref cmake and @ref plugins for more information. +*/ +class ObjImporter: public AbstractImporter { + public: + /** @brief Default constructor */ + explicit ObjImporter(); + + /** @brief Plugin manager constructor */ + explicit ObjImporter(PluginManager::AbstractManager& manager, std::string plugin); + + ~ObjImporter(); + + private: + struct File; + + Features doFeatures() const override; + + bool doIsOpened() const override; + void doOpenData(Containers::ArrayReference data) override; + void doOpenFile(const std::string& filename) override; + void doClose() override; + + UnsignedInt doMesh3DCount() const override; + Int doMesh3DForName(const std::string& name) override; + std::string doMesh3DName(UnsignedInt id) override; + std::optional doMesh3D(UnsignedInt id) override; + + void parseMeshNames(); + + std::unique_ptr _file; +}; + +}} + +#endif diff --git a/src/MagnumPlugins/ObjImporter/Test/CMakeLists.txt b/src/MagnumPlugins/ObjImporter/Test/CMakeLists.txt new file mode 100644 index 000000000..c5dec7146 --- /dev/null +++ b/src/MagnumPlugins/ObjImporter/Test/CMakeLists.txt @@ -0,0 +1,31 @@ +# +# This file is part of Magnum. +# +# Copyright © 2010, 2011, 2012, 2013, 2014 +# Vladimír Vondruš +# +# Permission is hereby granted, free of charge, to any person obtaining a +# copy of this software and associated documentation files (the "Software"), +# to deal in the Software without restriction, including without limitation +# the rights to use, copy, modify, merge, publish, distribute, sublicense, +# and/or sell copies of the Software, and to permit persons to whom the +# Software is furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +# DEALINGS IN THE SOFTWARE. +# + +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/configure.h.cmake + ${CMAKE_CURRENT_BINARY_DIR}/configure.h) + +include_directories(BEFORE ${CMAKE_CURRENT_BINARY_DIR}) + +corrade_add_test(ObjImporterTest Test.cpp LIBRARIES ObjImporterTestLib) diff --git a/src/MagnumPlugins/ObjImporter/Test/Test.cpp b/src/MagnumPlugins/ObjImporter/Test/Test.cpp new file mode 100644 index 000000000..9b4d858e9 --- /dev/null +++ b/src/MagnumPlugins/ObjImporter/Test/Test.cpp @@ -0,0 +1,737 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014 + Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +*/ + +#include +#include +#include + +#include "Magnum/Mesh.h" +#include "Magnum/Math/Vector3.h" +#include "Magnum/Trade/MeshData3D.h" +#include "MagnumPlugins/ObjImporter/ObjImporter.h" + +#include "configure.h" + +namespace Magnum { namespace Trade { namespace Test { + +class ObjImporterTest: public TestSuite::Tester { + public: + explicit ObjImporterTest(); + + void pointMesh(); + void lineMesh(); + void triangleMesh(); + void mixedPrimitives(); + + void positionsOnly(); + void textureCoordinates(); + void normals(); + void textureCoordinatesNormals(); + + void emptyFile(); + void unnamedMesh(); + void namedMesh(); + void moreMeshes(); + void unnamedFirstMesh(); + + void wrongFloat(); + void wrongInteger(); + void unmergedIndexOutOfRange(); + void mergedIndexOutOfRange(); + void zeroIndex(); + + void explicitOptionalPositionCoordinate(); + void explicitOptionalTextureCoordinate(); + void unsupportedOptionalPositionCoordinate(); + void unsupportedOptionalTextureCoordinate(); + + void shortFloatData(); + void longFloatData(); + void longOptionalFloatData(); + + void longIndexData(); + void wrongPointIndexData(); + void wrongLineIndexData(); + void wrongTriangleIndexData(); + void polygonIndexData(); + + void missingPositionData(); + void missingNormalData(); + void missingTextureCoordinateData(); + void missingPositionIndices(); + void missingNormalIndices(); + void missingTextureCoordinateIndices(); + + void wrongTextureCoordinateIndexCount(); + void wrongNormalIndexCount(); + + void unsupportedKeyword(); + void unknownKeyword(); +}; + +ObjImporterTest::ObjImporterTest() { + addTests({&ObjImporterTest::pointMesh, + &ObjImporterTest::lineMesh, + &ObjImporterTest::triangleMesh, + &ObjImporterTest::mixedPrimitives, + + &ObjImporterTest::positionsOnly, + &ObjImporterTest::textureCoordinates, + &ObjImporterTest::normals, + &ObjImporterTest::textureCoordinatesNormals, + + &ObjImporterTest::emptyFile, + &ObjImporterTest::unnamedMesh, + &ObjImporterTest::namedMesh, + &ObjImporterTest::moreMeshes, + &ObjImporterTest::unnamedFirstMesh, + + &ObjImporterTest::wrongFloat, + &ObjImporterTest::wrongInteger, + &ObjImporterTest::unmergedIndexOutOfRange, + &ObjImporterTest::mergedIndexOutOfRange, + &ObjImporterTest::zeroIndex, + + &ObjImporterTest::explicitOptionalPositionCoordinate, + &ObjImporterTest::explicitOptionalTextureCoordinate, + &ObjImporterTest::unsupportedOptionalPositionCoordinate, + &ObjImporterTest::unsupportedOptionalTextureCoordinate, + + &ObjImporterTest::shortFloatData, + &ObjImporterTest::longFloatData, + &ObjImporterTest::longOptionalFloatData, + + &ObjImporterTest::longIndexData, + &ObjImporterTest::wrongPointIndexData, + &ObjImporterTest::wrongLineIndexData, + &ObjImporterTest::wrongTriangleIndexData, + + &ObjImporterTest::missingPositionData, + &ObjImporterTest::missingNormalData, + &ObjImporterTest::missingTextureCoordinateData, + &ObjImporterTest::missingPositionIndices, + &ObjImporterTest::missingNormalIndices, + &ObjImporterTest::missingTextureCoordinateIndices, + + &ObjImporterTest::wrongTextureCoordinateIndexCount, + &ObjImporterTest::wrongNormalIndexCount, + + &ObjImporterTest::unsupportedKeyword, + &ObjImporterTest::unknownKeyword}); +} + +void ObjImporterTest::pointMesh() { + ObjImporter importer; + CORRADE_VERIFY(importer.openFile(Utility::Directory::join(OBJIMPORTER_TEST_DIR, "pointMesh.obj"))); + CORRADE_COMPARE(importer.mesh3DCount(), 1); + + const std::optional data = importer.mesh3D(0); + CORRADE_VERIFY(data); + CORRADE_COMPARE(data->primitive(), MeshPrimitive::Points); + CORRADE_COMPARE(data->positionArrayCount(), 1); + CORRADE_COMPARE(data->positions(0), (std::vector{ + {0.5f, 2.0f, 3.0f}, + {0.0f, 1.5f, 1.0f}, + {2.0f, 3.0f, 5.0f} + })); + CORRADE_COMPARE(data->indices(), (std::vector{ + 0, 2, 1, 0 + })); +} + +void ObjImporterTest::lineMesh() { + ObjImporter importer; + CORRADE_VERIFY(importer.openFile(Utility::Directory::join(OBJIMPORTER_TEST_DIR, "lineMesh.obj"))); + CORRADE_COMPARE(importer.mesh3DCount(), 1); + + const std::optional data = importer.mesh3D(0); + CORRADE_VERIFY(data); + CORRADE_COMPARE(data->primitive(), MeshPrimitive::Lines); + CORRADE_COMPARE(data->positionArrayCount(), 1); + CORRADE_COMPARE(data->positions(0), (std::vector{ + {0.5f, 2.0f, 3.0f}, + {0.0f, 1.5f, 1.0f}, + {2.0f, 3.0f, 5.0f} + })); + CORRADE_COMPARE(data->indices(), (std::vector{ + 0, 1, 1, 2 + })); +} + +void ObjImporterTest::triangleMesh() { + ObjImporter importer; + CORRADE_VERIFY(importer.openFile(Utility::Directory::join(OBJIMPORTER_TEST_DIR, "triangleMesh.obj"))); + CORRADE_COMPARE(importer.mesh3DCount(), 1); + + const std::optional data = importer.mesh3D(0); + CORRADE_VERIFY(data); + CORRADE_COMPARE(data->primitive(), MeshPrimitive::Triangles); + CORRADE_COMPARE(data->positionArrayCount(), 1); + CORRADE_COMPARE(data->positions(0), (std::vector{ + {0.5f, 2.0f, 3.0f}, + {0.0f, 1.5f, 1.0f}, + {2.0f, 3.0f, 5.0f}, + {2.5f, 0.0f, 1.0f} + })); + CORRADE_COMPARE(data->indices(), (std::vector{ + 0, 1, 2, 3, 1, 0 + })); +} + +void ObjImporterTest::mixedPrimitives() { + ObjImporter importer; + CORRADE_VERIFY(importer.openFile(Utility::Directory::join(OBJIMPORTER_TEST_DIR, "mixedPrimitives.obj"))); + CORRADE_COMPARE(importer.mesh3DCount(), 1); + + std::ostringstream out; + Error::setOutput(&out); + CORRADE_VERIFY(!importer.mesh3D(0)); + CORRADE_COMPARE(out.str(), "Trade::ObjImporter::mesh3D(): mixed primitive MeshPrimitive::Points and MeshPrimitive::Lines\n"); +} + +void ObjImporterTest::positionsOnly() { + ObjImporter importer; + CORRADE_VERIFY(importer.openFile(Utility::Directory::join(OBJIMPORTER_TEST_DIR, "triangleMesh.obj"))); + CORRADE_COMPARE(importer.mesh3DCount(), 1); + + const std::optional data = importer.mesh3D(0); + CORRADE_VERIFY(data); + CORRADE_COMPARE(data->positionArrayCount(), 1); + CORRADE_VERIFY(!data->hasNormals()); + CORRADE_VERIFY(!data->hasTextureCoords2D()); +} + +void ObjImporterTest::textureCoordinates() { + ObjImporter importer; + CORRADE_VERIFY(importer.openFile(Utility::Directory::join(OBJIMPORTER_TEST_DIR, "textureCoordinates.obj"))); + CORRADE_COMPARE(importer.mesh3DCount(), 1); + + const std::optional data = importer.mesh3D(0); + CORRADE_VERIFY(data); + CORRADE_COMPARE(data->primitive(), MeshPrimitive::Lines); + CORRADE_COMPARE(data->positionArrayCount(), 1); + CORRADE_VERIFY(!data->hasNormals()); + CORRADE_COMPARE(data->textureCoords2DArrayCount(), 1); + CORRADE_COMPARE(data->positions(0), (std::vector{ + {0.5f, 2.0f, 3.0f}, + {0.0f, 1.5f, 1.0f}, + {0.5f, 2.0f, 3.0f}, + {0.0f, 1.5f, 1.0f} + })); + CORRADE_COMPARE(data->textureCoords2D(0), (std::vector{ + {1.0f, 0.5f}, + {1.0f, 0.5f}, + {0.5f, 1.0f}, + {0.5f, 1.0f} + })); + CORRADE_COMPARE(data->indices(), (std::vector{ + 0, 1, 2, 3, 1, 0 + })); +} + +void ObjImporterTest::normals() { + ObjImporter importer; + CORRADE_VERIFY(importer.openFile(Utility::Directory::join(OBJIMPORTER_TEST_DIR, "normals.obj"))); + CORRADE_COMPARE(importer.mesh3DCount(), 1); + + const std::optional data = importer.mesh3D(0); + CORRADE_VERIFY(data); + CORRADE_COMPARE(data->primitive(), MeshPrimitive::Lines); + CORRADE_COMPARE(data->positionArrayCount(), 1); + CORRADE_VERIFY(!data->hasTextureCoords2D()); + CORRADE_COMPARE(data->normalArrayCount(), 1); + CORRADE_COMPARE(data->positions(0), (std::vector{ + {0.5f, 2.0f, 3.0f}, + {0.0f, 1.5f, 1.0f}, + {0.5f, 2.0f, 3.0f}, + {0.0f, 1.5f, 1.0f} + })); + CORRADE_COMPARE(data->normals(0), (std::vector{ + {1.0f, 0.5f, 3.5f}, + {1.0f, 0.5f, 3.5f}, + {0.5f, 1.0f, 0.5f}, + {0.5f, 1.0f, 0.5f} + })); + CORRADE_COMPARE(data->indices(), (std::vector{ + 0, 1, 2, 3, 1, 0 + })); +} + +void ObjImporterTest::textureCoordinatesNormals() { + ObjImporter importer; + CORRADE_VERIFY(importer.openFile(Utility::Directory::join(OBJIMPORTER_TEST_DIR, "textureCoordinatesNormals.obj"))); + CORRADE_COMPARE(importer.mesh3DCount(), 1); + + const std::optional data = importer.mesh3D(0); + CORRADE_VERIFY(data); + CORRADE_COMPARE(data->primitive(), MeshPrimitive::Lines); + CORRADE_COMPARE(data->positionArrayCount(), 1); + CORRADE_COMPARE(data->textureCoords2DArrayCount(), 1); + CORRADE_COMPARE(data->normalArrayCount(), 1); + CORRADE_COMPARE(data->positions(0), (std::vector{ + {0.5f, 2.0f, 3.0f}, + {0.0f, 1.5f, 1.0f}, + {0.5f, 2.0f, 3.0f}, + {0.0f, 1.5f, 1.0f}, + {0.0f, 1.5f, 1.0f} + })); + CORRADE_COMPARE(data->textureCoords2D(0), (std::vector{ + {1.0f, 0.5f}, + {1.0f, 0.5f}, + {0.5f, 1.0f}, + {0.5f, 1.0f}, + {0.5f, 1.0f} + })); + CORRADE_COMPARE(data->normals(0), (std::vector{ + {1.0f, 0.5f, 3.5f}, + {0.5f, 1.0f, 0.5f}, + {0.5f, 1.0f, 0.5f}, + {1.0f, 0.5f, 3.5f}, + {0.5f, 1.0f, 0.5f} + })); + CORRADE_COMPARE(data->indices(), (std::vector{ + 0, 1, 2, 3, 1, 0, 4, 2 + })); +} + +void ObjImporterTest::emptyFile() { + ObjImporter importer; + CORRADE_VERIFY(importer.openFile(Utility::Directory::join(OBJIMPORTER_TEST_DIR, "emptyFile.obj"))); + CORRADE_COMPARE(importer.mesh3DCount(), 1); +} + +void ObjImporterTest::unnamedMesh() { + ObjImporter importer; + CORRADE_VERIFY(importer.openFile(Utility::Directory::join(OBJIMPORTER_TEST_DIR, "emptyFile.obj"))); + CORRADE_COMPARE(importer.mesh3DCount(), 1); + CORRADE_COMPARE(importer.mesh3DName(0), ""); + CORRADE_COMPARE(importer.mesh3DForName(""), -1); +} + +void ObjImporterTest::namedMesh() { + ObjImporter importer; + CORRADE_VERIFY(importer.openFile(Utility::Directory::join(OBJIMPORTER_TEST_DIR, "namedMesh.obj"))); + CORRADE_COMPARE(importer.mesh3DCount(), 1); + CORRADE_COMPARE(importer.mesh3DName(0), "MyMesh"); + CORRADE_COMPARE(importer.mesh3DForName("MyMesh"), 0); +} + +void ObjImporterTest::moreMeshes() { + ObjImporter importer; + CORRADE_VERIFY(importer.openFile(Utility::Directory::join(OBJIMPORTER_TEST_DIR, "moreMeshes.obj"))); + CORRADE_COMPARE(importer.mesh3DCount(), 3); + + CORRADE_COMPARE(importer.mesh3DName(0), "PointMesh"); + CORRADE_COMPARE(importer.mesh3DForName("PointMesh"), 0); + const std::optional data = importer.mesh3D(0); + CORRADE_VERIFY(data); + CORRADE_COMPARE(data->primitive(), MeshPrimitive::Points); + CORRADE_COMPARE(data->positionArrayCount(), 1); + CORRADE_COMPARE(data->positions(0), (std::vector{ + {0.5f, 2.0f, 3.0f}, + {0.0f, 1.5f, 1.0f} + })); + CORRADE_COMPARE(data->indices(), (std::vector{ + 0, 1 + })); + + CORRADE_COMPARE(importer.mesh3DName(1), "LineMesh"); + CORRADE_COMPARE(importer.mesh3DForName("LineMesh"), 1); + const std::optional data1 = importer.mesh3D(1); + CORRADE_VERIFY(data1); + CORRADE_COMPARE(data1->primitive(), MeshPrimitive::Lines); + CORRADE_COMPARE(data1->positionArrayCount(), 1); + CORRADE_COMPARE(data1->positions(0), (std::vector{ + {0.5f, 2.0f, 3.0f}, + {0.0f, 1.5f, 1.0f} + })); + CORRADE_COMPARE(data1->indices(), (std::vector{ + 0, 1, 1, 0 + })); + + CORRADE_COMPARE(importer.mesh3DName(2), "TriangleMesh"); + CORRADE_COMPARE(importer.mesh3DForName("TriangleMesh"), 2); + const std::optional data2 = importer.mesh3D(2); + CORRADE_VERIFY(data2); + CORRADE_COMPARE(data2->primitive(), MeshPrimitive::Triangles); + CORRADE_COMPARE(data2->positionArrayCount(), 1); + CORRADE_COMPARE(data2->positions(0), (std::vector{ + {0.5f, 2.0f, 3.0f}, + {0.0f, 1.5f, 1.0f}, + {2.0f, 3.0f, 5.5f} + })); + CORRADE_COMPARE(data2->indices(), (std::vector{ + 0, 1, 2, 2, 1, 0 + })); +} + +void ObjImporterTest::unnamedFirstMesh() { + ObjImporter importer; + CORRADE_VERIFY(importer.openFile(Utility::Directory::join(OBJIMPORTER_TEST_DIR, "unnamedFirstMesh.obj"))); + CORRADE_COMPARE(importer.mesh3DCount(), 2); + + CORRADE_COMPARE(importer.mesh3DName(0), ""); + CORRADE_COMPARE(importer.mesh3DForName(""), -1); + + CORRADE_COMPARE(importer.mesh3DName(1), "SecondMesh"); + CORRADE_COMPARE(importer.mesh3DForName("SecondMesh"), 1); +} + +void ObjImporterTest::wrongFloat() { + ObjImporter importer; + CORRADE_VERIFY(importer.openFile(Utility::Directory::join(OBJIMPORTER_TEST_DIR, "wrongNumbers.obj"))); + const Int id = importer.mesh3DForName("WrongFloat"); + CORRADE_VERIFY(id > -1); + + std::ostringstream out; + Error::setOutput(&out); + CORRADE_VERIFY(!importer.mesh3D(id)); + CORRADE_COMPARE(out.str(), "Trade::ObjImporter::mesh3D(): error while converting numeric data\n"); +} + +void ObjImporterTest::wrongInteger() { + ObjImporter importer; + CORRADE_VERIFY(importer.openFile(Utility::Directory::join(OBJIMPORTER_TEST_DIR, "wrongNumbers.obj"))); + const Int id = importer.mesh3DForName("WrongInteger"); + CORRADE_VERIFY(id > -1); + + std::ostringstream out; + Error::setOutput(&out); + CORRADE_VERIFY(!importer.mesh3D(id)); + CORRADE_COMPARE(out.str(), "Trade::ObjImporter::mesh3D(): error while converting numeric data\n"); +} + +void ObjImporterTest::unmergedIndexOutOfRange() { + ObjImporter importer; + CORRADE_VERIFY(importer.openFile(Utility::Directory::join(OBJIMPORTER_TEST_DIR, "wrongNumbers.obj"))); + const Int id = importer.mesh3DForName("PositionIndexOutOfRange"); + CORRADE_VERIFY(id > -1); + + std::ostringstream out; + Error::setOutput(&out); + CORRADE_VERIFY(!importer.mesh3D(id)); + CORRADE_COMPARE(out.str(), "Trade::ObjImporter::mesh3D(): index out of range\n"); +} + +void ObjImporterTest::mergedIndexOutOfRange() { + ObjImporter importer; + CORRADE_VERIFY(importer.openFile(Utility::Directory::join(OBJIMPORTER_TEST_DIR, "wrongNumbers.obj"))); + const Int id = importer.mesh3DForName("TextureIndexOutOfRange"); + CORRADE_VERIFY(id > -1); + + std::ostringstream out; + Error::setOutput(&out); + CORRADE_VERIFY(!importer.mesh3D(id)); + CORRADE_COMPARE(out.str(), "Trade::ObjImporter::mesh3D(): index out of range\n"); +} + +void ObjImporterTest::zeroIndex() { + ObjImporter importer; + CORRADE_VERIFY(importer.openFile(Utility::Directory::join(OBJIMPORTER_TEST_DIR, "wrongNumbers.obj"))); + const Int id = importer.mesh3DForName("ZeroIndex"); + CORRADE_VERIFY(id > -1); + + std::ostringstream out; + Error::setOutput(&out); + CORRADE_VERIFY(!importer.mesh3D(id)); + CORRADE_COMPARE(out.str(), "Trade::ObjImporter::mesh3D(): index out of range\n"); +} + +void ObjImporterTest::explicitOptionalPositionCoordinate() { + ObjImporter importer; + CORRADE_VERIFY(importer.openFile(Utility::Directory::join(OBJIMPORTER_TEST_DIR, "optionalCoordinates.obj"))); + const Int id = importer.mesh3DForName("SupportedPositionW"); + CORRADE_VERIFY(id > -1); + + const std::optional data = importer.mesh3D(id); + CORRADE_VERIFY(data); + CORRADE_COMPARE(data->positionArrayCount(), 1); + CORRADE_COMPARE(data->positions(0), (std::vector{ + {1.5f, 2.0f, 3.0f} + })); +} + +void ObjImporterTest::explicitOptionalTextureCoordinate() { + ObjImporter importer; + CORRADE_VERIFY(importer.openFile(Utility::Directory::join(OBJIMPORTER_TEST_DIR, "optionalCoordinates.obj"))); + const Int id = importer.mesh3DForName("SupportedTextureW"); + CORRADE_VERIFY(id > -1); + + const std::optional data = importer.mesh3D(id); + CORRADE_VERIFY(data); + CORRADE_COMPARE(data->textureCoords2DArrayCount(), 1); + CORRADE_COMPARE(data->textureCoords2D(0), (std::vector{ + {0.5f, 0.7f} + })); +} + +void ObjImporterTest::unsupportedOptionalPositionCoordinate() { + ObjImporter importer; + CORRADE_VERIFY(importer.openFile(Utility::Directory::join(OBJIMPORTER_TEST_DIR, "optionalCoordinates.obj"))); + const Int id = importer.mesh3DForName("UnsupportedPositionW"); + CORRADE_VERIFY(id > -1); + + std::ostringstream out; + Error::setOutput(&out); + CORRADE_VERIFY(!importer.mesh3D(id)); + CORRADE_COMPARE(out.str(), "Trade::ObjImporter::mesh3D(): homogeneous coordinates are not supported\n"); +} + +void ObjImporterTest::unsupportedOptionalTextureCoordinate() { + ObjImporter importer; + CORRADE_VERIFY(importer.openFile(Utility::Directory::join(OBJIMPORTER_TEST_DIR, "optionalCoordinates.obj"))); + const Int id = importer.mesh3DForName("UnsupportedTextureW"); + CORRADE_VERIFY(id > -1); + + std::ostringstream out; + Error::setOutput(&out); + CORRADE_VERIFY(!importer.mesh3D(id)); + CORRADE_COMPARE(out.str(), "Trade::ObjImporter::mesh3D(): 3D texture coordinates are not supported\n"); +} + +void ObjImporterTest::shortFloatData() { + ObjImporter importer; + CORRADE_VERIFY(importer.openFile(Utility::Directory::join(OBJIMPORTER_TEST_DIR, "wrongNumberCount.obj"))); + const Int id = importer.mesh3DForName("ShortFloat"); + CORRADE_VERIFY(id > -1); + + std::ostringstream out; + Error::setOutput(&out); + CORRADE_VERIFY(!importer.mesh3D(id)); + CORRADE_COMPARE(out.str(), "Trade::ObjImporter::mesh3D(): invalid float array size\n"); +} + +void ObjImporterTest::longFloatData() { + ObjImporter importer; + CORRADE_VERIFY(importer.openFile(Utility::Directory::join(OBJIMPORTER_TEST_DIR, "wrongNumberCount.obj"))); + const Int id = importer.mesh3DForName("LongFloat"); + CORRADE_VERIFY(id > -1); + + std::ostringstream out; + Error::setOutput(&out); + CORRADE_VERIFY(!importer.mesh3D(id)); + CORRADE_COMPARE(out.str(), "Trade::ObjImporter::mesh3D(): invalid float array size\n"); +} + +void ObjImporterTest::longOptionalFloatData() { + ObjImporter importer; + CORRADE_VERIFY(importer.openFile(Utility::Directory::join(OBJIMPORTER_TEST_DIR, "wrongNumberCount.obj"))); + const Int id = importer.mesh3DForName("LongOptionalFloat"); + CORRADE_VERIFY(id > -1); + + std::ostringstream out; + Error::setOutput(&out); + CORRADE_VERIFY(!importer.mesh3D(id)); + CORRADE_COMPARE(out.str(), "Trade::ObjImporter::mesh3D(): invalid float array size\n"); +} + +void ObjImporterTest::longIndexData() { + ObjImporter importer; + CORRADE_VERIFY(importer.openFile(Utility::Directory::join(OBJIMPORTER_TEST_DIR, "wrongNumberCount.obj"))); + const Int id = importer.mesh3DForName("InvalidIndices"); + CORRADE_VERIFY(id > -1); + + std::ostringstream out; + Error::setOutput(&out); + CORRADE_VERIFY(!importer.mesh3D(id)); + CORRADE_COMPARE(out.str(), "Trade::ObjImporter::mesh3D(): invalid index data\n"); +} + +void ObjImporterTest::wrongPointIndexData() { + ObjImporter importer; + CORRADE_VERIFY(importer.openFile(Utility::Directory::join(OBJIMPORTER_TEST_DIR, "wrongNumberCount.obj"))); + const Int id = importer.mesh3DForName("WrongPointIndices"); + CORRADE_VERIFY(id > -1); + + std::ostringstream out; + Error::setOutput(&out); + CORRADE_VERIFY(!importer.mesh3D(id)); + CORRADE_COMPARE(out.str(), "Trade::ObjImporter::mesh3D(): wrong index count for point\n"); +} + +void ObjImporterTest::wrongLineIndexData() { + ObjImporter importer; + CORRADE_VERIFY(importer.openFile(Utility::Directory::join(OBJIMPORTER_TEST_DIR, "wrongNumberCount.obj"))); + const Int id = importer.mesh3DForName("WrongLineIndices"); + CORRADE_VERIFY(id > -1); + + std::ostringstream out; + Error::setOutput(&out); + CORRADE_VERIFY(!importer.mesh3D(id)); + CORRADE_COMPARE(out.str(), "Trade::ObjImporter::mesh3D(): wrong index count for line\n"); +} + +void ObjImporterTest::wrongTriangleIndexData() { + ObjImporter importer; + CORRADE_VERIFY(importer.openFile(Utility::Directory::join(OBJIMPORTER_TEST_DIR, "wrongNumberCount.obj"))); + const Int id = importer.mesh3DForName("WrongTriangleIndices"); + CORRADE_VERIFY(id > -1); + + std::ostringstream out; + Error::setOutput(&out); + CORRADE_VERIFY(!importer.mesh3D(id)); + CORRADE_COMPARE(out.str(), "Trade::ObjImporter::mesh3D(): wrong index count for triangle\n"); +} + +void ObjImporterTest::polygonIndexData() { + ObjImporter importer; + CORRADE_VERIFY(importer.openFile(Utility::Directory::join(OBJIMPORTER_TEST_DIR, "wrongNumberCount.obj"))); + const Int id = importer.mesh3DForName("PolygonIndices"); + CORRADE_VERIFY(id > -1); + + std::ostringstream out; + Error::setOutput(&out); + CORRADE_VERIFY(!importer.mesh3D(id)); + CORRADE_COMPARE(out.str(), "Trade::ObjImporter::mesh3D(): polygons are not supported\n"); +} + +void ObjImporterTest::missingPositionData() { + ObjImporter importer; + CORRADE_VERIFY(importer.openFile(Utility::Directory::join(OBJIMPORTER_TEST_DIR, "missingData.obj"))); + const Int id = importer.mesh3DForName("MissingPositionData"); + CORRADE_VERIFY(id > -1); + + std::ostringstream out; + Error::setOutput(&out); + CORRADE_VERIFY(!importer.mesh3D(id)); + CORRADE_COMPARE(out.str(), "Trade::ObjImporter::mesh3D(): incomplete position data\n"); +} + +void ObjImporterTest::missingPositionIndices() { + ObjImporter importer; + CORRADE_VERIFY(importer.openFile(Utility::Directory::join(OBJIMPORTER_TEST_DIR, "missingData.obj"))); + const Int id = importer.mesh3DForName("MissingPositionIndices"); + CORRADE_VERIFY(id > -1); + + std::ostringstream out; + Error::setOutput(&out); + CORRADE_VERIFY(!importer.mesh3D(id)); + CORRADE_COMPARE(out.str(), "Trade::ObjImporter::mesh3D(): incomplete position data\n"); +} + +void ObjImporterTest::missingNormalData() { + ObjImporter importer; + CORRADE_VERIFY(importer.openFile(Utility::Directory::join(OBJIMPORTER_TEST_DIR, "missingData.obj"))); + const Int id = importer.mesh3DForName("MissingNormalData"); + CORRADE_VERIFY(id > -1); + + std::ostringstream out; + Error::setOutput(&out); + CORRADE_VERIFY(!importer.mesh3D(id)); + CORRADE_COMPARE(out.str(), "Trade::ObjImporter::mesh3D(): incomplete normal data\n"); +} + +void ObjImporterTest::missingNormalIndices() { + ObjImporter importer; + CORRADE_VERIFY(importer.openFile(Utility::Directory::join(OBJIMPORTER_TEST_DIR, "missingData.obj"))); + const Int id = importer.mesh3DForName("MissingNormalIndices"); + CORRADE_VERIFY(id > -1); + + std::ostringstream out; + Error::setOutput(&out); + CORRADE_VERIFY(!importer.mesh3D(id)); + CORRADE_COMPARE(out.str(), "Trade::ObjImporter::mesh3D(): incomplete normal data\n"); +} + +void ObjImporterTest::missingTextureCoordinateData() { + ObjImporter importer; + CORRADE_VERIFY(importer.openFile(Utility::Directory::join(OBJIMPORTER_TEST_DIR, "missingData.obj"))); + const Int id = importer.mesh3DForName("MissingTextureData"); + CORRADE_VERIFY(id > -1); + + std::ostringstream out; + Error::setOutput(&out); + CORRADE_VERIFY(!importer.mesh3D(id)); + CORRADE_COMPARE(out.str(), "Trade::ObjImporter::mesh3D(): incomplete texture coordinate data\n"); +} + +void ObjImporterTest::missingTextureCoordinateIndices() { + ObjImporter importer; + CORRADE_VERIFY(importer.openFile(Utility::Directory::join(OBJIMPORTER_TEST_DIR, "missingData.obj"))); + const Int id = importer.mesh3DForName("MissingTextureIndices"); + CORRADE_VERIFY(id > -1); + + std::ostringstream out; + Error::setOutput(&out); + CORRADE_VERIFY(!importer.mesh3D(id)); + CORRADE_COMPARE(out.str(), "Trade::ObjImporter::mesh3D(): incomplete texture coordinate data\n"); +} + +void ObjImporterTest::wrongNormalIndexCount() { + ObjImporter importer; + CORRADE_VERIFY(importer.openFile(Utility::Directory::join(OBJIMPORTER_TEST_DIR, "wrongIndexCount.obj"))); + const Int id = importer.mesh3DForName("ShortNormalIndices"); + CORRADE_VERIFY(id > -1); + + std::ostringstream out; + Error::setOutput(&out); + CORRADE_VERIFY(!importer.mesh3D(id)); + CORRADE_COMPARE(out.str(), "Trade::ObjImporter::mesh3D(): some normal indices are missing\n"); +} + +void ObjImporterTest::wrongTextureCoordinateIndexCount() { + ObjImporter importer; + CORRADE_VERIFY(importer.openFile(Utility::Directory::join(OBJIMPORTER_TEST_DIR, "wrongIndexCount.obj"))); + const Int id = importer.mesh3DForName("ShortTextureIndices"); + CORRADE_VERIFY(id > -1); + + std::ostringstream out; + Error::setOutput(&out); + CORRADE_VERIFY(!importer.mesh3D(id)); + CORRADE_COMPARE(out.str(), "Trade::ObjImporter::mesh3D(): some texture coordinate indices are missing\n"); +} + +void ObjImporterTest::unsupportedKeyword() { + ObjImporter importer; + CORRADE_VERIFY(importer.openFile(Utility::Directory::join(OBJIMPORTER_TEST_DIR, "keywords.obj"))); + const Int id = importer.mesh3DForName("UnsupportedKeyword"); + CORRADE_VERIFY(id > -1); + + /* Everything should be parsed properly */ + const std::optional data = importer.mesh3D(id); + CORRADE_VERIFY(data); + CORRADE_COMPARE(data->primitive(), MeshPrimitive::Points); + CORRADE_COMPARE(data->positionArrayCount(), 1); + CORRADE_COMPARE(data->positions(0), (std::vector{ + {0.0f, 1.0f, 2.0f} + })); + CORRADE_COMPARE(data->indices(), std::vector{0}); +} + +void ObjImporterTest::unknownKeyword() { + ObjImporter importer; + CORRADE_VERIFY(importer.openFile(Utility::Directory::join(OBJIMPORTER_TEST_DIR, "keywords.obj"))); + const Int id = importer.mesh3DForName("UnknownKeyword"); + CORRADE_VERIFY(id > -1); + + std::ostringstream out; + Error::setOutput(&out); + CORRADE_VERIFY(!importer.mesh3D(id)); + CORRADE_COMPARE(out.str(), "Trade::ObjImporter::mesh3D(): unknown keyword bleh\n"); +} + +}}} + +CORRADE_TEST_MAIN(Magnum::Trade::Test::ObjImporterTest) diff --git a/src/MagnumPlugins/ObjImporter/Test/configure.h.cmake b/src/MagnumPlugins/ObjImporter/Test/configure.h.cmake new file mode 100644 index 000000000..484448999 --- /dev/null +++ b/src/MagnumPlugins/ObjImporter/Test/configure.h.cmake @@ -0,0 +1,26 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014 + Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +*/ + +#define OBJIMPORTER_TEST_DIR "${CMAKE_CURRENT_SOURCE_DIR}" diff --git a/src/MagnumPlugins/ObjImporter/Test/emptyFile.obj b/src/MagnumPlugins/ObjImporter/Test/emptyFile.obj new file mode 100644 index 000000000..2abea3a74 --- /dev/null +++ b/src/MagnumPlugins/ObjImporter/Test/emptyFile.obj @@ -0,0 +1 @@ +# Nothinng to see here diff --git a/src/MagnumPlugins/ObjImporter/Test/keywords.obj b/src/MagnumPlugins/ObjImporter/Test/keywords.obj new file mode 100644 index 000000000..c93dce27f --- /dev/null +++ b/src/MagnumPlugins/ObjImporter/Test/keywords.obj @@ -0,0 +1,7 @@ +o UnsupportedKeyword +g VertexGroup +v 0 1 2 +p 1 + +o UnknownKeyword +bleh diff --git a/src/MagnumPlugins/ObjImporter/Test/lineMesh.obj b/src/MagnumPlugins/ObjImporter/Test/lineMesh.obj new file mode 100644 index 000000000..d6eae8940 --- /dev/null +++ b/src/MagnumPlugins/ObjImporter/Test/lineMesh.obj @@ -0,0 +1,8 @@ +# Positions +v 0.5 2 3 +v 0 1.5 1 +v 2 3 5.0 + +# Lines +l 1 2 +l 2 3 diff --git a/src/MagnumPlugins/ObjImporter/Test/missingData.obj b/src/MagnumPlugins/ObjImporter/Test/missingData.obj new file mode 100644 index 000000000..e86b403bc --- /dev/null +++ b/src/MagnumPlugins/ObjImporter/Test/missingData.obj @@ -0,0 +1,23 @@ +o MissingPositionData +p 1 + +o MissingPositionIndices +v 1 2 3 + +o MissingNormalData +v 1 2 3 +p 1//1 + +o MissingNormalIndices +v 1 2 3 +vn 1 2 3 +p 1 + +o MissingTextureData +v 1 2 3 +p 1/1 + +o MissingTextureIndices +v 1 2 3 +vt 1 2 +p 1 diff --git a/src/MagnumPlugins/ObjImporter/Test/mixedPrimitives.obj b/src/MagnumPlugins/ObjImporter/Test/mixedPrimitives.obj new file mode 100644 index 000000000..e4ab55130 --- /dev/null +++ b/src/MagnumPlugins/ObjImporter/Test/mixedPrimitives.obj @@ -0,0 +1,13 @@ +# Positions +v 0.5 2 3 +v 0 1.5 1 +v 2 3 5.0 + +# Points +p 1 +p 3 +p 2 + +# Lines +l 1 2 +l 2 3 diff --git a/src/MagnumPlugins/ObjImporter/Test/moreMeshes.obj b/src/MagnumPlugins/ObjImporter/Test/moreMeshes.obj new file mode 100644 index 000000000..256cc06d8 --- /dev/null +++ b/src/MagnumPlugins/ObjImporter/Test/moreMeshes.obj @@ -0,0 +1,21 @@ +# Points +o PointMesh +v 0.5 2 3 +v 0 1.5 1 +p 1 +p 2 + +# Lines +o LineMesh +v 0.5 2 3 +v 0 1.5 1 +l 1 2 +l 2 1 + +# Triangles +o TriangleMesh +v 0.5 2 3 +v 0 1.5 1 +v 2 3 5.5 +f 1 2 3 +f 3 2 1 diff --git a/src/MagnumPlugins/ObjImporter/Test/namedMesh.obj b/src/MagnumPlugins/ObjImporter/Test/namedMesh.obj new file mode 100644 index 000000000..16802830f --- /dev/null +++ b/src/MagnumPlugins/ObjImporter/Test/namedMesh.obj @@ -0,0 +1,2 @@ +# Named mesh +o MyMesh diff --git a/src/MagnumPlugins/ObjImporter/Test/normals.obj b/src/MagnumPlugins/ObjImporter/Test/normals.obj new file mode 100644 index 000000000..15711d5ca --- /dev/null +++ b/src/MagnumPlugins/ObjImporter/Test/normals.obj @@ -0,0 +1,12 @@ +# Positions +v 0.5 2 3 +v 0 1.5 1 + +# Normals (don't have to be normalized) +vn 1 0.5 3.5 +vn 0.5 1 0.5 + +# Lines +l 1//1 2//1 +l 1//2 2//2 +l 2//1 1//1 diff --git a/src/MagnumPlugins/ObjImporter/Test/optionalCoordinates.obj b/src/MagnumPlugins/ObjImporter/Test/optionalCoordinates.obj new file mode 100644 index 000000000..6fe88d715 --- /dev/null +++ b/src/MagnumPlugins/ObjImporter/Test/optionalCoordinates.obj @@ -0,0 +1,17 @@ +o SupportedPositionW +v 1.5 2 3 1.0 +p 1 + +o SupportedTextureW +v 1.5 2 3 +vt 0.5 0.7 0.0 +p 1/1 + +o UnsupportedPositionW +v 1.5 2 3 0.8 +p 1 + +o UnsupportedTextureW +v 1.5 2 3 +vt 0.5 0.7 0.5 +p 1/1 diff --git a/src/MagnumPlugins/ObjImporter/Test/pointMesh.obj b/src/MagnumPlugins/ObjImporter/Test/pointMesh.obj new file mode 100644 index 000000000..c5991619c --- /dev/null +++ b/src/MagnumPlugins/ObjImporter/Test/pointMesh.obj @@ -0,0 +1,10 @@ +# Positions +v 0.5 2 3 +v 0 1.5 1 +v 2 3 5.0 + +# Points +p 1 +p 3 +p 2 +p 1 diff --git a/src/MagnumPlugins/ObjImporter/Test/textureCoordinates.obj b/src/MagnumPlugins/ObjImporter/Test/textureCoordinates.obj new file mode 100644 index 000000000..ae72e239c --- /dev/null +++ b/src/MagnumPlugins/ObjImporter/Test/textureCoordinates.obj @@ -0,0 +1,12 @@ +# Positions +v 0.5 2 3 +v 0 1.5 1 + +# Texture coordinates +vt 1 0.5 +vt 0.5 1 + +# Lines +l 1/1 2/1 +l 1/2 2/2 +l 2/1 1/1 diff --git a/src/MagnumPlugins/ObjImporter/Test/textureCoordinatesNormals.obj b/src/MagnumPlugins/ObjImporter/Test/textureCoordinatesNormals.obj new file mode 100644 index 000000000..c1750ecb2 --- /dev/null +++ b/src/MagnumPlugins/ObjImporter/Test/textureCoordinatesNormals.obj @@ -0,0 +1,17 @@ +# Positions +v 0.5 2 3 +v 0 1.5 1 + +# Texture coordinates +vt 1 0.5 +vt 0.5 1 + +# Normals +vn 1 0.5 3.5 +vn 0.5 1 0.5 + +# Lines +l 1/1/1 2/1/2 +l 1/2/2 2/2/1 +l 2/1/2 1/1/1 +l 2/2/2 1/2/2 diff --git a/src/MagnumPlugins/ObjImporter/Test/triangleMesh.obj b/src/MagnumPlugins/ObjImporter/Test/triangleMesh.obj new file mode 100644 index 000000000..32fb5196b --- /dev/null +++ b/src/MagnumPlugins/ObjImporter/Test/triangleMesh.obj @@ -0,0 +1,9 @@ +# Positions +v 0.5 2 3 +v 0 1.5 1 +v 2 3 5.0 +v 2.5 0 1 + +# Triangles +f 1 2 3 +f 4 2 1 diff --git a/src/MagnumPlugins/ObjImporter/Test/unnamedFirstMesh.obj b/src/MagnumPlugins/ObjImporter/Test/unnamedFirstMesh.obj new file mode 100644 index 000000000..71752c6d9 --- /dev/null +++ b/src/MagnumPlugins/ObjImporter/Test/unnamedFirstMesh.obj @@ -0,0 +1,2 @@ +v 1 2 3 +o SecondMesh diff --git a/src/MagnumPlugins/ObjImporter/Test/wrongIndexCount.obj b/src/MagnumPlugins/ObjImporter/Test/wrongIndexCount.obj new file mode 100644 index 000000000..960ef3c10 --- /dev/null +++ b/src/MagnumPlugins/ObjImporter/Test/wrongIndexCount.obj @@ -0,0 +1,13 @@ +o ShortNormalIndices +v 1 2 3 +vn 1 2 3 +p 1//1 +p 1 +p 1//1 + +o ShortTextureIndices +v 1 2 3 +vt 1 2 +p 1/1 +p 1 +p 1/1 diff --git a/src/MagnumPlugins/ObjImporter/Test/wrongNumberCount.obj b/src/MagnumPlugins/ObjImporter/Test/wrongNumberCount.obj new file mode 100644 index 000000000..c0797fb47 --- /dev/null +++ b/src/MagnumPlugins/ObjImporter/Test/wrongNumberCount.obj @@ -0,0 +1,29 @@ +o ShortFloat +v 0.5 1.0 + +o LongFloat +v 0.5 1 2 +vn 0.5 1.0 2.3 7.4 + +o LongOptionalFloat +v 0.5 1 2 0.0 3.5 + +o InvalidIndices +v 1 2 3 +p 1/1/1/1 + +o WrongPointIndices +v 1 2 3 +p 1 1 + +o WrongLineIndices +v 1 2 3 +l 1 + +o WrongTriangleIndices +v 1 2 3 +f 1 1 + +o PolygonIndices +v 1 2 3 +f 1 1 1 1 diff --git a/src/MagnumPlugins/ObjImporter/Test/wrongNumbers.obj b/src/MagnumPlugins/ObjImporter/Test/wrongNumbers.obj new file mode 100644 index 000000000..c795997c2 --- /dev/null +++ b/src/MagnumPlugins/ObjImporter/Test/wrongNumbers.obj @@ -0,0 +1,20 @@ +o WrongFloat +v 1 bleh 2 +p 1 + +o WrongInteger +v 1 0 2 +p bleh + +o PositionIndexOutOfRange +v 1 0 2 +p 2 + +o TextureIndexOutOfRange +v 1 0 2 +vt 0 1 +p 1/2 + +o ZeroIndex +v 1 0 2 +p 0 diff --git a/src/MagnumPlugins/ObjImporter/pluginRegistration.cpp b/src/MagnumPlugins/ObjImporter/pluginRegistration.cpp new file mode 100644 index 000000000..4ded696b1 --- /dev/null +++ b/src/MagnumPlugins/ObjImporter/pluginRegistration.cpp @@ -0,0 +1,29 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014 + Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +*/ + +#include "MagnumPlugins/ObjImporter/ObjImporter.h" + +CORRADE_PLUGIN_REGISTER(ObjImporter, Magnum::Trade::ObjImporter, + "cz.mosra.magnum.Trade.AbstractImporter/0.3")