Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

AssimpImporter: import unrecognized materials + compatibility with 5.1.1 #116

Closed
wants to merge 48 commits into from
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
cff46e5
AssimpImporter: add light names
pezcode Nov 7, 2021
67c5985
AssimpImporter: import ambient lights as LightData::Type::Ambient
pezcode Nov 13, 2021
060bf34
AssimpImporter: import camera name and aspect ratio
pezcode Nov 13, 2021
3923e07
AssimpImporter: fix try_compile for assimp 5.1.0
pezcode Nov 13, 2021
aadd958
AssimpImporter: streamline version check in tests
pezcode Nov 13, 2021
32314c4
AssimpImporter: import scene name
pezcode Nov 13, 2021
5621134
AssimpImporter: import orthographic cameras
pezcode Nov 13, 2021
977ee6f
AssimpImporter: ignore flags in aiPrimitiveType mask
pezcode Nov 14, 2021
86f1e58
[wip] AssimpImporter: fix tests for 5.1.1
pezcode Nov 15, 2021
e2ee78c
TinyGltfImporter: make test files compatible with AssimpImporter
pezcode Nov 22, 2021
c579a93
AssimpImporter: patch broken FBX animation mTicksPerSecond
pezcode Nov 22, 2021
6564c8c
AssimpImporter: this transformation makes no sense
pezcode Nov 22, 2021
8c9516a
AssimpImporter: clean up comments
pezcode Nov 22, 2021
690e8bc
AssimpImporter: fix possible unsigned underflow
pezcode Nov 22, 2021
47cfea9
AssimpImporter: import raw material data
pezcode Nov 22, 2021
b4cac36
AssimpImporter: extract raw aiString and bool material properties
pezcode Nov 24, 2021
b0faba1
AssimpImporter: document new raw material data config options
pezcode Nov 25, 2021
66cff86
AssimpImporter: add some todos
pezcode Nov 25, 2021
b241ce3
AssimpImporter: don't need this anymore
pezcode Nov 25, 2021
69d0c26
AssimpImporter: test layered materials
pezcode Nov 25, 2021
2c329ae
AssimpImporter: test raw material data
pezcode Nov 25, 2021
7287d08
AssimpImporter: fix raw material test for assimp 5.1.0+
pezcode Nov 25, 2021
feb3527
AssimpImporter: enable the C language
pezcode Nov 25, 2021
41bf8fd
AssimpImporter: fix warnings
pezcode Nov 25, 2021
5942ec1
AssimpImporter: Containers::InPlaceInit is deprecated
pezcode Nov 25, 2021
5408d05
AssimpImporter: fix compiler errors
pezcode Nov 26, 2021
786d8a9
AssimpImporter: use ASCII .fbx material test files
pezcode Nov 26, 2021
18f5d0f
AssimpImporter: simplify material-raw.gltf
pezcode Nov 26, 2021
b7d5545
AssimpImporter: test texture coordinate set for raw material import
pezcode Nov 26, 2021
a5f1a94
AssimpImporter: no need for constexpr here
pezcode Nov 26, 2021
d41e8a0
AssimpImporter: doc++
pezcode Nov 26, 2021
cc0ea71
AssimpImporter: Utility::formatString understands StringView
pezcode Nov 26, 2021
137a9d0
AssimpImporter: clarify comments
pezcode Nov 26, 2021
3e78883
AssimpImporter: don't special-case glTF bugs for raw materials
pezcode Nov 26, 2021
094b50b
Revert "TinyGltfImporter: make test files compatible with AssimpImpor…
pezcode Nov 26, 2021
294fc00
AssimpImporter: use our own camera and skin-without-meshes gltf test …
pezcode Nov 26, 2021
7195b8f
AssimpImporter: detect if assimp was built with double support
pezcode Nov 26, 2021
467b476
AssimpImporter: don't test glTF texture coordinate set attributes for…
pezcode Nov 26, 2021
6111570
AssimpImporter: comments++
pezcode Nov 26, 2021
63a48e7
AssimpImporter: work around possibly undefined aiTextureType
pezcode Nov 26, 2021
c9d42e9
AssimpImporter: don't use possibly undefined material keys
pezcode Nov 26, 2021
670ad45
AssimpImporter: fix the camera name test
pezcode Nov 27, 2021
e41cd93
AssimpImporter: work around
pezcode Nov 27, 2021
7e4b8cc
AssimpImporter: don't assert for unknown material attribute types
pezcode Nov 27, 2021
3c713e8
AssimpImporter: cleanup
pezcode Nov 27, 2021
2d12495
AssimpImporter: skip layered and one raw material test on assimp < 5
pezcode Nov 27, 2021
ba7c80e
AssimpImporter: actually test raw import of new texture types
pezcode Nov 27, 2021
9366ae0
AssimpImporter: fix raw FBX property test on Assimp < 5
pezcode Nov 27, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions src/MagnumPlugins/AssimpImporter/AssimpImporter.conf
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,17 @@ mergeSkins=false
# AI_CONFIG_* values, can be changed only before the first file is opened
ImportColladaIgnoreUpDirection=false

# By default material attributes that aren't recognized as builtin attributes
# are imported with raw Assimp names and types. Enabling this option completely
# ignores unrecognized attributes instead.
ignoreUnrecognizedMaterialData=false

# Don't attempt to recognize builtin material attributes and always import them
# as raw material data (so e.g. instead of DiffuseColor you'll get
# $clr.diffuse). Implicitly disables ignoreUnrecognizedMaterialData. Mainly for
# testing purposes.
forceRawMaterialData=false

# aiPostProcessSteps, applied to each opened file
[configuration/postprocess]
JoinIdenticalVertices=true
Expand Down
187 changes: 160 additions & 27 deletions src/MagnumPlugins/AssimpImporter/AssimpImporter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@

#include "AssimpImporter.h"

#include <cctype>
#include <unordered_map>
#include <Corrade/Containers/ArrayView.h>
#include <Corrade/Containers/ArrayViewStl.h>
Expand Down Expand Up @@ -172,6 +173,8 @@ void fillDefaultConfiguration(Utility::ConfigurationGroup& conf) {
conf.setValue("mergeSkins", false);

conf.setValue("ImportColladaIgnoreUpDirection", false);
conf.setValue("ignoreUnrecognizedMaterialData", false);
conf.setValue("forceRawMaterialData", false);

Utility::ConfigurationGroup& postprocess = *conf.addGroup("postprocess");
postprocess.setValue("JoinIdenticalVertices", true);
Expand Down Expand Up @@ -1145,6 +1148,53 @@ MaterialAttributeData materialColor(MaterialAttribute attribute, const aiMateria
else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */
}

Containers::String customMaterialKey(Containers::StringView key, const aiTextureType semantic) {
using namespace Containers::Literals;

/* Create a non-owning key String, add texture type to it if
present */
auto keyString = Containers::String::nullTerminatedView(key);
if(semantic != aiTextureType_NONE) {
Containers::StringView keyExtra;
switch(semantic) {
#define _c(type) case aiTextureType_ ## type: \
keyExtra = #type ## _s; \
break;
_c(AMBIENT)
_c(DIFFUSE)
_c(SPECULAR)
_c(EMISSIVE)
_c(OPACITY)
_c(NORMALS)
_c(HEIGHT)
_c(DISPLACEMENT)
_c(LIGHTMAP)
_c(REFLECTION)
_c(UNKNOWN)
_c(BASE_COLOR)
_c(SHININESS)
_c(NORMAL_CAMERA)
_c(EMISSION_COLOR)
_c(METALNESS)
_c(DIFFUSE_ROUGHNESS)
_c(AMBIENT_OCCLUSION)
#undef _c

case aiTextureType_NONE:
case _aiTextureType_Force32Bit:
CORRADE_INTERNAL_ASSERT_UNREACHABLE();
}

CORRADE_INTERNAL_ASSERT(!keyExtra.isEmpty());

/** @todo use format() and drop data() + FormatStl include
when format() is converted to StringViews */
keyString = Utility::formatString("{}.{}", key.data(), keyExtra.data());
pezcode marked this conversation as resolved.
Show resolved Hide resolved
}

return keyString;
}

#ifndef _CORRADE_HELPER_DEFER
template<std::size_t size> constexpr Containers::StringView extractMaterialKey(const char(&data)[size], int, int) {
return Containers::Literals::operator"" _s(data, size - 1);
Expand All @@ -1154,6 +1204,9 @@ template<std::size_t size> constexpr Containers::StringView extractMaterialKey(c
}

Containers::Optional<MaterialData> AssimpImporter::doMaterial(const UnsignedInt id) {
const bool forceRaw = configuration().value<bool>("forceRawMaterialData");
const bool unrecognizedAsRaw = forceRaw || !configuration().value<bool>("ignoreUnrecognizedMaterialData");

const aiMaterial* mat = _f->scene->mMaterials[id];

/* Calculate how many layers there are in the material */
Expand All @@ -1167,7 +1220,7 @@ Containers::Optional<MaterialData> AssimpImporter::doMaterial(const UnsignedInt
arrayReserve(attributes, mat->mNumProperties);
Containers::Array<UnsignedInt> layers{maxLayer + 1};

/* Go through each layer add then for each add all its properties so they
/* Go through each layer and then for each add all its properties so they
are consecutive in the array */
for(UnsignedInt layer = 0; layer <= maxLayer; ++layer) {
/* Save offset of this layer */
Expand Down Expand Up @@ -1196,35 +1249,38 @@ Containers::Optional<MaterialData> AssimpImporter::doMaterial(const UnsignedInt

const Containers::StringView key{property.mKey.C_Str(), property.mKey.length, Containers::StringViewFlag::NullTerminated};

/* AI_MATKEY_* are in form "bla",0,0, so extract the first part
and turn it into a StringView for string comparison. The
_s literal is there to avoid useless strlen() calls in every
branch. _CORRADE_HELPER_DEFER is not implementable on MSVC
(see docs in Utility/Macros.h), so there it's a constexpr
function instead. */
#ifdef _CORRADE_HELPER_DEFER
#define _str2(name, i, j) name ## _s
#define _str(name) _CORRADE_HELPER_DEFER(_str2, name)
#else
#define _str extractMaterialKey
#endif

/* Material name is available through materialName() /
materialForName() already, ignore it */
if(key == _str(AI_MATKEY_NAME) && property.mType == aiPTI_String)
continue;

/* Recognize known attributes if they have expected types and
sizes */
sizes. If they don't, they'll be treated as custom attribs in
the code below. It's also possible to skip this by enabling the
forceRawMaterialData configuration option. */
MaterialAttributeData data;
MaterialAttribute attribute{};
MaterialAttributeType type{};
{
/* AI_MATKEY_* are in form "bla",0,0, so extract the first part
and turn it into a StringView for string comparison. The
_s literal is there to avoid useless strlen() calls in every
branch. _CORRADE_HELPER_DEFER is not implementable on MSVC
(see docs in Utility/Macros.h), so there it's a constexpr
function instead. */
#ifdef _CORRADE_HELPER_DEFER
#define _str2(name, i, j) name ## _s
#define _str(name) _CORRADE_HELPER_DEFER(_str2, name)
#else
#define _str extractMaterialKey
#endif
if(!forceRaw) {
/* Properties not tied to a particular texture */
if(property.mSemantic == aiTextureType_NONE) {
/* Material name is available through materialName() /
materialForName() already, ignore it */
if(key == _str(AI_MATKEY_NAME) && property.mType == aiPTI_String) {
continue;

/* Colors. Some formats have them three-components (OBJ),
some four-component (glTF). Documentation states it's
always three-component. FFS. */
} else if(key == _str(AI_MATKEY_COLOR_AMBIENT) && property.mType == aiPTI_Float && (property.mDataLength == 4*4 || property.mDataLength == 4*3)) {
if(key == _str(AI_MATKEY_COLOR_AMBIENT) && property.mType == aiPTI_Float && (property.mDataLength == 4*4 || property.mDataLength == 4*3)) {
data = materialColor(MaterialAttribute::AmbientColor, property);

/* Assimp 4.1 forces ambient color to white for STL
Expand Down Expand Up @@ -1303,10 +1359,11 @@ Containers::Optional<MaterialData> AssimpImporter::doMaterial(const UnsignedInt
}
}
}
#undef _str
#undef _str2
}

#undef _str
#undef _str2

/* If the attribute data is already constructed (parsed from a
string value etc), put it directly in */
if(data.type() != MaterialAttributeType{}) {
Expand All @@ -1319,21 +1376,97 @@ Containers::Optional<MaterialData> AssimpImporter::doMaterial(const UnsignedInt
CORRADE_INTERNAL_ASSERT(type != MaterialAttributeType{} && type != MaterialAttributeType::String);
arrayAppend(attributes, InPlaceInit, attribute, type, property.mData);

/* Otherwise ignore for now. At a later point remaining attributes
will be imported as custom, but that needs a lot of testing
which I don't have time for right now. */
/* Otherwise figure out its type, do some additional checking and
put it there as a custom attribute */
} else if(unrecognizedAsRaw) {
/* Attribute names starting with uppercase letters are reserved
for Magnum. All assimp material keys either start with '$'
or '?'. The only format importing raw material names is FBX
and that prefixes them with "$raw.". A quick search through
the 5.0.1 code confirms that no importer sets attributes
starting uppercase, so an assert should be fine here.
Revisit if this breaks for someone. */
/** @todo $ conflicts with Magnum's material layer names */
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sigh, I didn't fix this yet? 😅

CORRADE_ASSERT(!key.isEmpty() && !std::isupper(key.front()),
"Trade::AssimpImporter::material(): uppercase attribute names are reserved", Containers::NullOpt);
pezcode marked this conversation as resolved.
Show resolved Hide resolved

MaterialAttributeType type;
if(property.mType == aiPTI_Integer) {
if(property.mDataLength == 1)
type = MaterialAttributeType::Bool;
else if(property.mDataLength/4 == 1)
type = MaterialAttributeType::Int;
else if(property.mDataLength/4 == 2)
type = MaterialAttributeType::Vector2i;
else if(property.mDataLength/4 == 3)
type = MaterialAttributeType::Vector3i;
else if(property.mDataLength/4 == 4)
type = MaterialAttributeType::Vector4i;
else {
Warning{} << "Trade::AssimpImporter::material(): property" << key << "is integer array of" << property.mDataLength/4 << "items, saving as a typeless buffer";
type = MaterialAttributeType::String;
}
} else if(property.mType == aiPTI_Float) {
if(property.mDataLength/4 == 1)
type = MaterialAttributeType::Float;
else if(property.mDataLength/4 == 2)
type = MaterialAttributeType::Vector2;
else if(property.mDataLength/4 == 3)
type = MaterialAttributeType::Vector3;
else if(property.mDataLength/4 == 4)
type = MaterialAttributeType::Vector4;
else {
Warning{} << "Trade::AssimpImporter::material(): property" << key << "is a float array of" << property.mDataLength/4 << "items, saving as a typeless buffer";
type = MaterialAttributeType::String;
}
} else if(property.mType == aiPTI_Double) {
Warning{} << "Trade::AssimpImporter::material():" << key << "is a double precision property, saving as a typeless buffer";
type = MaterialAttributeType::String;
} else if(property.mType == aiPTI_String || property.mType == aiPTI_Buffer) {
type = MaterialAttributeType::String;
mosra marked this conversation as resolved.
Show resolved Hide resolved
} else CORRADE_INTERNAL_ASSERT_UNREACHABLE();

Containers::StringView value;
std::size_t valueSize;
const void* valuePointer;
if(type == MaterialAttributeType::String) {
value = {property.mData, property.mDataLength};
/* +2 is null byte + size */
valueSize = property.mDataLength + 2;
valuePointer = &value;
} else {
valueSize = materialAttributeTypeSize(type);
valuePointer = property.mData;
}

const Containers::String keyString = customMaterialKey(key, aiTextureType(property.mSemantic));

/* +1 is null byte for the key */
if(valueSize + keyString.size() + 1 + sizeof(MaterialAttributeType) > sizeof(MaterialAttributeData)) {
Error{} << "Trade::AssimpImporter::material(): property" << keyString << "is too large with" << valueSize << "bytes, skipping";
continue;
}

Debug{} << "raw material" << key << "of type" << type << "imported as" << keyString;

arrayAppend(attributes, Containers::InPlaceInit, keyString, type, valuePointer);
}
}
}

/** @todo Import flat materials (AI_MATKEY_SHADING_MODEL +
aiShadingMode_NoShading). In 5.1.0 this is also exposed for glTF (used
to be AI_MATKEY_GLTF_UNLIT). */
const MaterialType materialType = forceRaw ? MaterialType{} : MaterialType::Phong;

/* Save offset for the last layer */
layers.back() = attributes.size();

/* Can't use growable deleters in a plugin, convert back to the default
deleter */
arrayShrink(attributes, DefaultInit);
/** @todo detect PBR properties and add relevant types accordingly */
pezcode marked this conversation as resolved.
Show resolved Hide resolved
return MaterialData{MaterialType::Phong, std::move(attributes), std::move(layers), mat};
return MaterialData{materialType, std::move(attributes), std::move(layers), mat};
}

UnsignedInt AssimpImporter::doTextureCount() const { return _f->textures.size(); }
Expand Down
3 changes: 3 additions & 0 deletions src/MagnumPlugins/AssimpImporter/Test/AssimpImporterTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1715,6 +1715,7 @@ void AssimpImporterTest::lightUnsupported() {

void AssimpImporterTest::materialColor() {
Containers::Pointer<AbstractImporter> importer = _manager.instantiate("AssimpImporter");
importer->configuration().setValue("ignoreUnrecognizedMaterialData", true);
CORRADE_VERIFY(importer->openFile(Utility::Directory::join(ASSIMPIMPORTER_TEST_DIR, "material-color.dae")));

CORRADE_COMPARE(importer->materialCount(), 1);
Expand Down Expand Up @@ -1748,6 +1749,7 @@ void AssimpImporterTest::materialColor() {

void AssimpImporterTest::materialTexture() {
Containers::Pointer<AbstractImporter> importer = _manager.instantiate("AssimpImporter");
importer->configuration().setValue("ignoreUnrecognizedMaterialData", true);
CORRADE_VERIFY(importer->openFile(Utility::Directory::join(ASSIMPIMPORTER_TEST_DIR, "material-texture.dae")));

CORRADE_COMPARE(importer->materialCount(), 1);
Expand Down Expand Up @@ -1784,6 +1786,7 @@ void AssimpImporterTest::materialTexture() {

void AssimpImporterTest::materialColorTexture() {
Containers::Pointer<AbstractImporter> importer = _manager.instantiate("AssimpImporter");
importer->configuration().setValue("ignoreUnrecognizedMaterialData", true);
pezcode marked this conversation as resolved.
Show resolved Hide resolved
CORRADE_VERIFY(importer->openFile(Utility::Directory::join(ASSIMPIMPORTER_TEST_DIR, "material-color-texture.obj")));

{
Expand Down