Browse Source

New ObjImporter plugin.

pull/51/head
Vladimír Vondruš 12 years ago
parent
commit
77e38fcce3
  1. 3
      CMakeLists.txt
  2. 1
      doc/building.dox
  3. 2
      doc/cmake.dox
  4. 4
      modules/FindMagnum.cmake
  5. 4
      src/MagnumPlugins/CMakeLists.txt
  6. 53
      src/MagnumPlugins/ObjImporter/CMakeLists.txt
  7. 0
      src/MagnumPlugins/ObjImporter/ObjImporter.conf
  8. 408
      src/MagnumPlugins/ObjImporter/ObjImporter.cpp
  9. 86
      src/MagnumPlugins/ObjImporter/ObjImporter.h
  10. 31
      src/MagnumPlugins/ObjImporter/Test/CMakeLists.txt
  11. 737
      src/MagnumPlugins/ObjImporter/Test/Test.cpp
  12. 26
      src/MagnumPlugins/ObjImporter/Test/configure.h.cmake
  13. 1
      src/MagnumPlugins/ObjImporter/Test/emptyFile.obj
  14. 7
      src/MagnumPlugins/ObjImporter/Test/keywords.obj
  15. 8
      src/MagnumPlugins/ObjImporter/Test/lineMesh.obj
  16. 23
      src/MagnumPlugins/ObjImporter/Test/missingData.obj
  17. 13
      src/MagnumPlugins/ObjImporter/Test/mixedPrimitives.obj
  18. 21
      src/MagnumPlugins/ObjImporter/Test/moreMeshes.obj
  19. 2
      src/MagnumPlugins/ObjImporter/Test/namedMesh.obj
  20. 12
      src/MagnumPlugins/ObjImporter/Test/normals.obj
  21. 17
      src/MagnumPlugins/ObjImporter/Test/optionalCoordinates.obj
  22. 10
      src/MagnumPlugins/ObjImporter/Test/pointMesh.obj
  23. 12
      src/MagnumPlugins/ObjImporter/Test/textureCoordinates.obj
  24. 17
      src/MagnumPlugins/ObjImporter/Test/textureCoordinatesNormals.obj
  25. 9
      src/MagnumPlugins/ObjImporter/Test/triangleMesh.obj
  26. 2
      src/MagnumPlugins/ObjImporter/Test/unnamedFirstMesh.obj
  27. 13
      src/MagnumPlugins/ObjImporter/Test/wrongIndexCount.obj
  28. 29
      src/MagnumPlugins/ObjImporter/Test/wrongNumberCount.obj
  29. 20
      src/MagnumPlugins/ObjImporter/Test/wrongNumbers.obj
  30. 29
      src/MagnumPlugins/ObjImporter/pluginRegistration.cpp

3
CMakeLists.txt

@ -41,7 +41,7 @@ option(WITH_FIND_MODULE "Install FindMagnum.cmake module into CMake's module dir
# Parts of the library # Parts of the library
option(WITH_AUDIO "Build Audio library" OFF) option(WITH_AUDIO "Build Audio library" OFF)
option(WITH_DEBUGTOOLS "Build DebugTools library" ON) 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_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_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) cmake_dependent_option(WITH_SHADERS "Build Shaders library" ON "NOT WITH_DEBUGTOOLS" ON)
@ -85,6 +85,7 @@ endif()
# Plugins # Plugins
cmake_dependent_option(WITH_MAGNUMFONT "Build MagnumFont plugin" OFF "WITH_TEXT" OFF) 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) 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_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_TGAIMPORTER "Build TgaImporter plugin" OFF "NOT WITH_MAGNUMFONT" ON)
cmake_dependent_option(WITH_WAVAUDIOIMPORTER "Build WavAudioImporter plugin" OFF "WITH_AUDIO" OFF) cmake_dependent_option(WITH_WAVAUDIOIMPORTER "Build WavAudioImporter plugin" OFF "WITH_AUDIO" OFF)

1
doc/building.dox

@ -216,6 +216,7 @@ default.
- `WITH_MAGNUMFONTCONVERTER` -- @ref Text::MagnumFontConverter "MagnumFontConverter" - `WITH_MAGNUMFONTCONVERTER` -- @ref Text::MagnumFontConverter "MagnumFontConverter"
plugin. Available only if `WITH_TEXT` is enabled. Enables also building of plugin. Available only if `WITH_TEXT` is enabled. Enables also building of
@ref Trade::TgaImageConverter "TgaImageConverter" plugin. @ref Trade::TgaImageConverter "TgaImageConverter" plugin.
- `WITH_OBJIMPORTER` -- @ref Trade::ObjImporter "ObjImporter" plugin.
- `WITH_TGAIMPORTER` -- @ref Trade::TgaImporter "TgaImporter" plugin. - `WITH_TGAIMPORTER` -- @ref Trade::TgaImporter "TgaImporter" plugin.
- `WITH_TGAIMAGECONVERTER` -- @ref Trade::TgaImageConverter "TgaImageConverter" - `WITH_TGAIMAGECONVERTER` -- @ref Trade::TgaImageConverter "TgaImageConverter"
plugin. plugin.

2
doc/cmake.dox

@ -103,6 +103,8 @@ dependencies, you need to find the dependency and then link to it.
`%Text` component and `TgaImporter` plugin) `%Text` component and `TgaImporter` plugin)
- `MagnumFontConverter` -- @ref Text::MagnumFontConverter "MagnumFontConverter" - `MagnumFontConverter` -- @ref Text::MagnumFontConverter "MagnumFontConverter"
plugin (depends on `%Text` component and `%TgaImageConverter` plugin) plugin (depends on `%Text` component and `%TgaImageConverter` plugin)
- `ObjImporter` -- @ref Trade::ObjImporter "ObjImporter" plugin (depends on
`%MeshTools` component)
- `TgaImageConverter` -- @ref Trade::TgaImageConverter "TgaImageConverter" - `TgaImageConverter` -- @ref Trade::TgaImageConverter "TgaImageConverter"
plugin plugin
- `TgaImporter` -- @ref Trade::TgaImporter "TgaImporter" plugin - `TgaImporter` -- @ref Trade::TgaImporter "TgaImporter" plugin

4
modules/FindMagnum.cmake

@ -35,6 +35,7 @@
# and TgaImporter plugin) # and TgaImporter plugin)
# MagnumFontConverter - Magnum bitmap font converter plugin (depends on Text # MagnumFontConverter - Magnum bitmap font converter plugin (depends on Text
# component and TgaImageConverter plugin) # component and TgaImageConverter plugin)
# ObjImporter - OBJ importer plugin
# TgaImageConverter - TGA image converter plugin # TgaImageConverter - TGA image converter plugin
# TgaImporter - TGA importer plugin # TgaImporter - TGA importer plugin
# WavAudioImporter - WAV audio importer plugin (depends on Audio component) # 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) set(_MAGNUM_${_COMPONENT}_INCLUDE_PATH_NAMES Atlas.h)
endif() endif()
# The plugins don't have any dependencies, nothing additional to do for
# them
# Try to find the includes # Try to find the includes
if(_MAGNUM_${_COMPONENT}_INCLUDE_PATH_NAMES) if(_MAGNUM_${_COMPONENT}_INCLUDE_PATH_NAMES)
find_path(_MAGNUM_${_COMPONENT}_INCLUDE_DIR find_path(_MAGNUM_${_COMPONENT}_INCLUDE_DIR

4
src/MagnumPlugins/CMakeLists.txt

@ -40,6 +40,10 @@ if(WITH_TEXT AND WITH_MAGNUMFONTCONVERTER AND NOT MAGNUM_TARGET_GLES)
add_subdirectory(MagnumFontConverter) add_subdirectory(MagnumFontConverter)
endif() endif()
if(WITH_OBJIMPORTER)
add_subdirectory(ObjImporter)
endif()
if(WITH_TGAIMAGECONVERTER) if(WITH_TGAIMAGECONVERTER)
add_subdirectory(TgaImageConverter) add_subdirectory(TgaImageConverter)
endif() endif()

53
src/MagnumPlugins/ObjImporter/CMakeLists.txt

@ -0,0 +1,53 @@
#
# This file is part of Magnum.
#
# Copyright © 2010, 2011, 2012, 2013, 2014
# Vladimír Vondruš <mosra@centrum.cz>
#
# 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
$<TARGET_OBJECTS:ObjImporterObjects>
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_OBJECTS:ObjImporterObjects>)
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()

0
src/MagnumPlugins/ObjImporter/ObjImporter.conf

408
src/MagnumPlugins/ObjImporter/ObjImporter.cpp

@ -0,0 +1,408 @@
/*
This file is part of Magnum.
Copyright © 2010, 2011, 2012, 2013, 2014
Vladimír Vondruš <mosra@centrum.cz>
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 <fstream>
#include <limits>
#include <sstream>
#include <unordered_map>
#include <Corrade/Containers/Array.h>
#include <Corrade/Utility/String.h>
#include "Magnum/Math/Vector3.h"
#include "Magnum/Trade/MeshData3D.h"
#include <Magnum/Mesh.h>
#include <Magnum/MeshTools/CombineIndexedArrays.h>
#include <Magnum/MeshTools/Duplicate.h>
namespace Magnum { namespace Trade {
struct ObjImporter::File {
std::unordered_map<std::string, UnsignedInt> meshesForName;
std::vector<std::string> meshNames;
std::vector<std::pair<std::streampos, std::streampos>> meshes;
std::unique_ptr<std::istream> in;
};
namespace {
void ignoreLine(std::istream& in) {
in.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
}
template<std::size_t size> Math::Vector<size, Float> extractFloatData(std::string str, Float* extra = nullptr) {
std::vector<std::string> 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<size, Float> 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<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;
}
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<std::istream> 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<const unsigned char> data) {
/* Open file in *text* mode (to avoid \r handling) */
_file.reset(new File);
_file->in.reset(new std::istringstream{{reinterpret_cast<const char*>(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<MeshData3D> ObjImporter::doMesh3D(UnsignedInt id) {
/* Seek the file */
std::streampos begin, end;
std::tie(begin, end) = _file->meshes[id];
_file->in->seekg(begin);
std::optional<MeshPrimitive> primitive;
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;
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<Float>::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<Float>::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<std::string> 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<std::string> 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<UnsignedInt> indices;
if(!normalIndices.empty() || !textureCoordinateIndices.empty()) {
std::vector<std::reference_wrapper<std::vector<UnsignedInt>>> 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));
}
}}

86
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š <mosra@centrum.cz>
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<const unsigned char> 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<MeshData3D> doMesh3D(UnsignedInt id) override;
void parseMeshNames();
std::unique_ptr<File> _file;
};
}}
#endif

31
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š <mosra@centrum.cz>
#
# 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)

737
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š <mosra@centrum.cz>
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 <sstream>
#include <Corrade/TestSuite/Tester.h>
#include <Corrade/Utility/Directory.h>
#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<MeshData3D> 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<Vector3>{
{0.5f, 2.0f, 3.0f},
{0.0f, 1.5f, 1.0f},
{2.0f, 3.0f, 5.0f}
}));
CORRADE_COMPARE(data->indices(), (std::vector<UnsignedInt>{
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<MeshData3D> 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<Vector3>{
{0.5f, 2.0f, 3.0f},
{0.0f, 1.5f, 1.0f},
{2.0f, 3.0f, 5.0f}
}));
CORRADE_COMPARE(data->indices(), (std::vector<UnsignedInt>{
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<MeshData3D> 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<Vector3>{
{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<UnsignedInt>{
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<MeshData3D> 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<MeshData3D> 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<Vector3>{
{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<Vector2>{
{1.0f, 0.5f},
{1.0f, 0.5f},
{0.5f, 1.0f},
{0.5f, 1.0f}
}));
CORRADE_COMPARE(data->indices(), (std::vector<UnsignedInt>{
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<MeshData3D> 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<Vector3>{
{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<Vector3>{
{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<UnsignedInt>{
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<MeshData3D> 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<Vector3>{
{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<Vector2>{
{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<Vector3>{
{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<UnsignedInt>{
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<MeshData3D> 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<Vector3>{
{0.5f, 2.0f, 3.0f},
{0.0f, 1.5f, 1.0f}
}));
CORRADE_COMPARE(data->indices(), (std::vector<UnsignedInt>{
0, 1
}));
CORRADE_COMPARE(importer.mesh3DName(1), "LineMesh");
CORRADE_COMPARE(importer.mesh3DForName("LineMesh"), 1);
const std::optional<MeshData3D> 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<Vector3>{
{0.5f, 2.0f, 3.0f},
{0.0f, 1.5f, 1.0f}
}));
CORRADE_COMPARE(data1->indices(), (std::vector<UnsignedInt>{
0, 1, 1, 0
}));
CORRADE_COMPARE(importer.mesh3DName(2), "TriangleMesh");
CORRADE_COMPARE(importer.mesh3DForName("TriangleMesh"), 2);
const std::optional<MeshData3D> 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<Vector3>{
{0.5f, 2.0f, 3.0f},
{0.0f, 1.5f, 1.0f},
{2.0f, 3.0f, 5.5f}
}));
CORRADE_COMPARE(data2->indices(), (std::vector<UnsignedInt>{
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<MeshData3D> data = importer.mesh3D(id);
CORRADE_VERIFY(data);
CORRADE_COMPARE(data->positionArrayCount(), 1);
CORRADE_COMPARE(data->positions(0), (std::vector<Vector3>{
{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<MeshData3D> data = importer.mesh3D(id);
CORRADE_VERIFY(data);
CORRADE_COMPARE(data->textureCoords2DArrayCount(), 1);
CORRADE_COMPARE(data->textureCoords2D(0), (std::vector<Vector2>{
{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<MeshData3D> 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<Vector3>{
{0.0f, 1.0f, 2.0f}
}));
CORRADE_COMPARE(data->indices(), std::vector<UnsignedInt>{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)

26
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š <mosra@centrum.cz>
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}"

1
src/MagnumPlugins/ObjImporter/Test/emptyFile.obj

@ -0,0 +1 @@
# Nothinng to see here

7
src/MagnumPlugins/ObjImporter/Test/keywords.obj

@ -0,0 +1,7 @@
o UnsupportedKeyword
g VertexGroup
v 0 1 2
p 1
o UnknownKeyword
bleh

8
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

23
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

13
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

21
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

2
src/MagnumPlugins/ObjImporter/Test/namedMesh.obj

@ -0,0 +1,2 @@
# Named mesh
o MyMesh

12
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

17
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

10
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

12
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

17
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

9
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

2
src/MagnumPlugins/ObjImporter/Test/unnamedFirstMesh.obj

@ -0,0 +1,2 @@
v 1 2 3
o SecondMesh

13
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

29
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

20
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

29
src/MagnumPlugins/ObjImporter/pluginRegistration.cpp

@ -0,0 +1,29 @@
/*
This file is part of Magnum.
Copyright © 2010, 2011, 2012, 2013, 2014
Vladimír Vondruš <mosra@centrum.cz>
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")
Loading…
Cancel
Save