diff --git a/src/MagnumPlugins/AssimpImporter/AssimpImporter.conf b/src/MagnumPlugins/AssimpImporter/AssimpImporter.conf index b339675e7..6b4d270aa 100644 --- a/src/MagnumPlugins/AssimpImporter/AssimpImporter.conf +++ b/src/MagnumPlugins/AssimpImporter/AssimpImporter.conf @@ -29,6 +29,26 @@ provides=XglImporter # Controls the workaround for an Assimp 4.1 "white ambient" bug forceWhiteAmbientToBlack=true +# Optimize imported linearly-interpolated quaternion animation tracks to +# ensure shortest path is always chosen. This can be controlled separately for +# each animation import. +optimizeQuaternionShortestPath=true + +# Normalize quaternion animation tracks, if they are not already. +# This can be controlled separately for each animation import. +normalizeQuaternions=true + +# Merge all animations into a single clip. Useful for preserving cinematic +# animations when using the Blender glTF exporter, as it exports animation of +# every object as a separate clip. See https://blender.stackexchange.com/q/5689 +# and https://github.com/KhronosGroup/glTF-Blender-Exporter/pull/166 for more +# information. +mergeAnimationClips=false + +# Remove dummy animation tracks inserted by Assimp that contain only a single +# key/value pair with the default node transformation +removeDummyAnimationTracks=true + # AI_CONFIG_* values, can be changed only before the first file is opened ImportColladaIgnoreUpDirection=false diff --git a/src/MagnumPlugins/AssimpImporter/AssimpImporter.cpp b/src/MagnumPlugins/AssimpImporter/AssimpImporter.cpp index f22f12b37..61a8c5c0a 100644 --- a/src/MagnumPlugins/AssimpImporter/AssimpImporter.cpp +++ b/src/MagnumPlugins/AssimpImporter/AssimpImporter.cpp @@ -30,6 +30,7 @@ #include #include +#include #include #include #include @@ -40,9 +41,11 @@ #include #include #include +#include #include #include #include +#include #include #include #include @@ -54,14 +57,17 @@ #include #include -#include #include #include +#include #include #include #include +#include #include +#include "configureInternal.h" + namespace Magnum { namespace Math { namespace Implementation { template<> struct VectorConverter<3, Float, aiColor3D> { @@ -82,6 +88,8 @@ namespace Magnum { namespace Trade { struct AssimpImporter::File { Containers::Optional filePath; + const char* importerName = nullptr; + bool importerIsGltf = false; const aiScene* scene = nullptr; std::vector nodes; /* (materialPointer, propertyIndexInsideMaterial, imageIndex) tuple, @@ -96,6 +104,8 @@ struct AssimpImporter::File { std::unordered_map materialIndicesForName; std::unordered_map textureIndices; + Containers::Optional> animationIndicesForName; + /* Mapping for multi-mesh nodes: (in the following, "node" is an aiNode, "object" is Magnum::Trade::ObjectData3D) @@ -128,6 +138,11 @@ void fillDefaultConfiguration(Utility::ConfigurationGroup& conf) { conf.setValue("forceWhiteAmbientToBlack", true); + conf.setValue("optimizeQuaternionShortestPath", true); + conf.setValue("normalizeQuaternions", true); + conf.setValue("mergeAnimationClips", false); + conf.setValue("removeDummyAnimationTracks", true); + conf.setValue("ImportColladaIgnoreUpDirection", false); Utility::ConfigurationGroup& postprocess = *conf.addGroup("postprocess"); @@ -341,11 +356,22 @@ void AssimpImporter::doOpenData(const Containers::ArrayView data) { CORRADE_INTERNAL_ASSERT(_f->scene); + /* Get name of importer. Useful for workarounds based on importer/file type. */ + _f->importerName = "unknown"; + const int importerIndex = _importer->GetPropertyInteger("importerIndex", -1); + if(importerIndex != -1) { + const aiImporterDesc* info = _importer->GetImporterInfo(importerIndex); + if(info) _f->importerName = info->mName; + } + + using namespace Containers::Literals; + + _f->importerIsGltf = _f->importerName == "glTF2 Importer"_s; + /* Fill hashmaps for index lookup for materials/textures/meshes/nodes */ _f->materialIndicesForName.reserve(_f->scene->mNumMaterials); aiString matName; - aiString texturePath; UnsignedInt textureIndex = 0; std::unordered_map uniqueImages; for(std::size_t i = 0; i < _f->scene->mNumMaterials; ++i) { @@ -359,22 +385,22 @@ void AssimpImporter::doOpenData(const Containers::ArrayView data) { /* Store first possible texture index for this material, next textures use successive indices. */ _f->textureIndices[mat] = textureIndex; - for(std::size_t i = 0; i != mat->mNumProperties; ++i) { + for(std::size_t j = 0; j != mat->mNumProperties; ++j) { /* We're only interested in AI_MATKEY_TEXTURE_* properties */ - const aiMaterialProperty& property = *mat->mProperties[i]; + const aiMaterialProperty& property = *mat->mProperties[j]; if(Containers::StringView{property.mKey.C_Str(), property.mKey.length} != _AI_MATKEY_TEXTURE_BASE) continue; /* For images ensure we have an unique path so each file isn't - imported more than once. Each image then points to i-th property + imported more than once. Each image then points to j-th property of the material, which is then used to retrieve its path again. */ Containers::StringView texturePath = materialPropertyString(property); auto uniqueImage = uniqueImages.emplace(texturePath, _f->images.size()); - if(uniqueImage.second) _f->images.emplace_back(mat, i); + if(uniqueImage.second) _f->images.emplace_back(mat, j); - /* Each texture points to i-th property of the material, which is + /* Each texture points to j-th property of the material, which is then used to retrieve related info, plus an index into the unique images array */ - _f->textures.emplace_back(mat, i, uniqueImage.first->second); + _f->textures.emplace_back(mat, j, uniqueImage.first->second); ++textureIndex; } } @@ -448,6 +474,18 @@ void AssimpImporter::doOpenData(const Containers::ArrayView data) { } } } + + /* Before https://github.com/assimp/assimp/commit/e3083c21f0a7beae6c37a2265b7919a02cbf83c4 + Assimp incorrectly read spline tangents as values in glTF animation tracks. + Quick and dirty check to see if we're dealing with a possibly affected file + and Assimp version. This might produce false-positives on files without + spline-interpolated animations, but for doOpenState and doOpenFile we + have no access to the file content to check if the file contains "CUBICSPLINE". */ + if(_f->scene->HasAnimations() && _f->importerIsGltf && ASSIMP_HAS_BROKEN_GLTF_SPLINES) { + Warning{} << "Trade::AssimpImporter::openData(): spline-interpolated animations imported " + "from this file are most likely broken using this version of Assimp. Consult the " + "importer documentation for more information."; + } } void AssimpImporter::doOpenState(const void* state, const std::string& filePath) { @@ -1118,6 +1156,324 @@ Containers::Optional AssimpImporter::doImage2D(const UnsignedInt id return importer->image2D(0, level); } +UnsignedInt AssimpImporter::doAnimationCount() const { + /* If the animations are merged, there's at most one */ + if(configuration().value("mergeAnimationClips")) + return _f->scene->mNumAnimations ? 1 : 0; + + return _f->scene->mNumAnimations; +} + +Int AssimpImporter::doAnimationForName(const std::string& name) { + /* If the animations are merged, don't report any names */ + if(configuration().value("mergeAnimationClips")) return -1; + + if(!_f->animationIndicesForName) { + _f->animationIndicesForName.emplace(); + _f->animationIndicesForName->reserve(_f->scene->mNumAnimations); + for(std::size_t i = 0; i != _f->scene->mNumAnimations; ++i) + _f->animationIndicesForName->emplace(std::string(_f->scene->mAnimations[i]->mName.C_Str()), i); + } + + const auto found = _f->animationIndicesForName->find(name); + return found == _f->animationIndicesForName->end() ? -1 : found->second; +} + +std::string AssimpImporter::doAnimationName(UnsignedInt id) { + /* If the animations are merged, don't report any names */ + if(configuration().value("mergeAnimationClips")) return {}; + return _f->scene->mAnimations[id]->mName.C_Str(); +} + +namespace { + +Animation::Extrapolation extrapolationFor(aiAnimBehaviour behaviour) { + /* This code is not covered by tests since there is currently + no code in Assimp that sets this except for the .irr importer. + So it'll be aiAnimBehaviour_DEFAULT for 99.99% of users, + and there are tests that check that this becomes + Extrapolation::Constant. */ + switch(behaviour) { + case aiAnimBehaviour_DEFAULT: + /** @todo emulate this correctly by inserting keyframes + with the default node transformation */ + return Animation::Extrapolation::Constant; + case aiAnimBehaviour_CONSTANT: + return Animation::Extrapolation::Constant; + case aiAnimBehaviour_LINEAR: + return Animation::Extrapolation::Extrapolated; + case aiAnimBehaviour_REPEAT: + return Animation::Extrapolation::Constant; + default: CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */ + } +} + +} + +Containers::Optional AssimpImporter::doAnimation(UnsignedInt id) { + const bool verbose{flags() & ImporterFlag::Verbose}; + const bool removeDummyAnimationTracks = configuration().value("removeDummyAnimationTracks"); + const bool mergeAnimationClips = configuration().value("mergeAnimationClips"); + + /* Import either a single animation or all of them together. At the moment, + Blender doesn't really support cinematic animations (affecting multiple + objects): https://blender.stackexchange.com/q/5689. And since + https://github.com/KhronosGroup/glTF-Blender-Exporter/pull/166, these + are exported as a set of object-specific clips, which may not be wanted, + so we give the users an option to merge them all together. */ + const std::size_t animationBegin = mergeAnimationClips ? 0 : id; + const std::size_t animationEnd = mergeAnimationClips ? _f->scene->mNumAnimations : id + 1; + + /* Calculate total channel count */ + std::size_t channelCount = 0; + for(std::size_t a = animationBegin; a != animationEnd; ++a) { + channelCount += _f->scene->mAnimations[a]->mNumChannels; + } + + /* Presence of translation/rotation/scaling keys for each channel. + We might skip certain tracks (see comments in the next loop) + but we need to know the correct track count and data array size + up front, so determine this and remember it for the actual + loop that extracts the tracks. + BigEnumSet because EnumSet requires binary-exclusive enum + values. */ + typedef Containers::BigEnumSet TargetTypes; + Containers::Array channelTargetTypes{channelCount}; + + /* Calculate total data size and track count. If merging all animations together, + this is the sum of all clip track counts. */ + std::size_t dataSize = 0; + std::size_t trackCount = 0; + std::size_t currentChannel = 0; + for(std::size_t a = animationBegin; a != animationEnd; ++a) { + const aiAnimation* animation = _f->scene->mAnimations[a]; + for(std::size_t c = 0; c != animation->mNumChannels; ++c) { + const aiNodeAnim* channel = animation->mChannels[c]; + + UnsignedInt translationKeyCount = channel->mNumPositionKeys; + UnsignedInt rotationKeyCount = channel->mNumRotationKeys; + UnsignedInt scalingKeyCount = channel->mNumScalingKeys; + + /* Assimp adds useless 1-key tracks with default node + translation/rotation/scale if the channel doesn't + target all 3 of them. Ignore those. */ + if(removeDummyAnimationTracks && + (translationKeyCount == 1 || rotationKeyCount == 1 || scalingKeyCount == 1)) + { + const aiNode* node = _f->scene->mRootNode->FindNode(channel->mNodeName); + CORRADE_INTERNAL_ASSERT(node); + + aiVector3D nodeTranslation; + aiQuaternion nodeRotation; + aiVector3D nodeScaling; + /* This might not perfectly restore the T/R/S components, but it's okay + if the comparison fails, this whole fix is a best-effort attempt */ + node->mTransformation.Decompose(nodeScaling, nodeRotation, nodeTranslation); + + if(translationKeyCount == 1 && channel->mPositionKeys[0].mTime == 0.0) { + const Vector3 value = Vector3::from(&channel->mPositionKeys[0].mValue[0]); + const Vector3 nodeValue = Vector3::from(&nodeTranslation[0]); + if(value == nodeValue) { + if(verbose) { + Debug{} << "Trade::AssimpImporter::animation(): ignoring dummy translation " + "track in animation" << a << Debug::nospace << ", channel" << c; + } + translationKeyCount = 0; + } + } + if(rotationKeyCount == 1 && channel->mRotationKeys[0].mTime == 0.0) { + const aiQuaternion& valueQuat = channel->mRotationKeys[0].mValue; + const Quaternion value{{valueQuat.x, valueQuat.y, valueQuat.z}, valueQuat.w}; + const aiQuaternion& nodeQuat = nodeRotation; + const Quaternion nodeValue{{nodeQuat.x, nodeQuat.y, nodeQuat.z}, nodeQuat.w}; + if(value == nodeValue) { + if(verbose) { + Debug{} << "Trade::AssimpImporter::animation(): ignoring dummy rotation " + "track in animation" << a << Debug::nospace << ", channel" << c; + } + rotationKeyCount = 0; + } + } + if(scalingKeyCount == 1 && channel->mScalingKeys[0].mTime == 0.0) { + const Vector3 value = Vector3::from(&channel->mScalingKeys[0].mValue[0]); + const Vector3 nodeValue = Vector3::from(&nodeScaling[0]); + if(value == nodeValue) { + if(verbose) { + Debug{} << "Trade::AssimpImporter::animation(): ignoring dummy scaling " + "track in animation" << a << Debug::nospace << ", channel" << c; + } + scalingKeyCount = 0; + } + } + } + + TargetTypes targetTypes; + if(translationKeyCount > 0) targetTypes |= AnimationTrackTargetType::Translation3D; + if(rotationKeyCount > 0) targetTypes |= AnimationTrackTargetType::Rotation3D; + if(scalingKeyCount > 0) targetTypes |= AnimationTrackTargetType::Scaling3D; + channelTargetTypes[currentChannel++] = targetTypes; + + /** @todo handle alignment once we do more than just four-byte types */ + dataSize += translationKeyCount*(sizeof(Float) + sizeof(Vector3)) + + rotationKeyCount*(sizeof(Float) + sizeof(Quaternion)) + + scalingKeyCount*(sizeof(Float) + sizeof(Vector3)); + + trackCount += (translationKeyCount > 0 ? 1 : 0) + + (rotationKeyCount > 0 ? 1 : 0) + + (scalingKeyCount > 0 ? 1 : 0); + } + } + + /* Populate the data array */ + Containers::Array data{dataSize}; + + const bool optimizeQuaternionShortestPath = configuration().value("optimizeQuaternionShortestPath"); + const bool normalizeQuaternions = configuration().value("normalizeQuaternions"); + + /* Import all tracks */ + bool hadToRenormalize = false; + std::size_t dataOffset = 0; + std::size_t trackId = 0; + currentChannel = 0; + Containers::Array tracks{trackCount}; + for(std::size_t a = animationBegin; a != animationEnd; ++a) { + const aiAnimation* animation = _f->scene->mAnimations[a]; + for(std::size_t c = 0; c != animation->mNumChannels; ++c) { + const aiNodeAnim* channel = animation->mChannels[c]; + const Int target = doObject3DForName(channel->mNodeName.C_Str()); + CORRADE_INTERNAL_ASSERT(target != -1); + + /* Assimp only supports linear interpolation. For glTF splines + it simply uses the spline control points. */ + constexpr Animation::Interpolation interpolation = Animation::Interpolation::Linear; + + /* Extrapolation modes */ + const Animation::Extrapolation extrapolationBefore = extrapolationFor(channel->mPreState); + const Animation::Extrapolation extrapolationAfter = extrapolationFor(channel->mPostState); + + /* Key times are given as ticks, convert to milliseconds. Default value of 25 is + taken from the assimp_view tool. */ + double ticksPerSecond = animation->mTicksPerSecond != 0.0 ? animation->mTicksPerSecond : 25.0; + + /* For glTF files mTicksPerSecond is completely useless before + https://github.com/assimp/assimp/commit/09d80ff478d825a80bce6fb787e8b19df9f321a8 + but can be assumed to always be 1000. */ + constexpr Double GltfTicksPerSecond = 1000.0; + if(_f->importerIsGltf && !Math::equal(ticksPerSecond, GltfTicksPerSecond)) { + if(verbose) { + Debug{} << "Trade::AssimpImporter::animation():" << ticksPerSecond << + "ticks per second is incorrect for glTF, patching to" << GltfTicksPerSecond; + } + ticksPerSecond = GltfTicksPerSecond; + } + + const TargetTypes targetTypes = channelTargetTypes[currentChannel++]; + + /* Translation */ + if(targetTypes & AnimationTrackTargetType::Translation3D) { + const size_t keyCount = channel->mNumPositionKeys; + const auto keys = Containers::arrayCast( + data.suffix(dataOffset).prefix(keyCount*sizeof(Float))); + dataOffset += keys.size()*sizeof(keys[0]); + const auto values = Containers::arrayCast( + data.suffix(dataOffset).prefix(keyCount*sizeof(Vector3))); + dataOffset += values.size()*sizeof(values[0]); + + for(size_t k = 0; k < channel->mNumPositionKeys; ++k) { + /* Convert double to float keys */ + keys[k] = channel->mPositionKeys[k].mTime / ticksPerSecond; + values[k] = Vector3::from(&channel->mPositionKeys[k].mValue[0]); + } + + const auto track = Animation::TrackView{ + keys, values, interpolation, + animationInterpolatorFor(interpolation), + extrapolationBefore, extrapolationAfter}; + + tracks[trackId++] = AnimationTrackData{AnimationTrackType::Vector3, + AnimationTrackType::Vector3, AnimationTrackTargetType::Translation3D, + static_cast(target), track }; + } + + /* Rotation */ + if(targetTypes & AnimationTrackTargetType::Rotation3D) { + const size_t keyCount = channel->mNumRotationKeys; + const auto keys = Containers::arrayCast( + data.suffix(dataOffset).prefix(keyCount*sizeof(Float))); + dataOffset += keys.size()*sizeof(keys[0]); + const auto values = Containers::arrayCast( + data.suffix(dataOffset).prefix(keyCount*sizeof(Quaternion))); + dataOffset += values.size()*sizeof(values[0]); + + for(size_t k = 0; k < channel->mNumRotationKeys; ++k) { + keys[k] = channel->mRotationKeys[k].mTime / ticksPerSecond; + const aiQuaternion& quat = channel->mRotationKeys[k].mValue; + values[k] = Quaternion{{quat.x, quat.y, quat.z}, quat.w}; + } + + /* Ensure shortest path is always chosen. */ + if(optimizeQuaternionShortestPath) { + Float flip = 1.0f; + for(std::size_t i = 0; i != values.size() - 1; ++i) { + if(Math::dot(values[i], values[i + 1]*flip) < 0) flip = -flip; + values[i + 1] *= flip; + } + } + + /* Normalize the quaternions if not already. Don't attempt + to normalize every time to avoid tiny differences, only + when the quaternion looks to be off. */ + if(normalizeQuaternions) { + for(auto& i: values) if(!i.isNormalized()) { + i = i.normalized(); + hadToRenormalize = true; + } + } + + const auto track = Animation::TrackView{ + keys, values, interpolation, + animationInterpolatorFor(interpolation), + extrapolationBefore, extrapolationAfter}; + + tracks[trackId++] = AnimationTrackData{AnimationTrackType::Quaternion, + AnimationTrackType::Quaternion, AnimationTrackTargetType::Rotation3D, + static_cast(target), track}; + } + + /* Scale */ + if(targetTypes & AnimationTrackTargetType::Scaling3D) { + const size_t keyCount = channel->mNumScalingKeys; + const auto keys = Containers::arrayCast( + data.suffix(dataOffset).prefix(keyCount*sizeof(Float))); + dataOffset += keys.size()*sizeof(keys[0]); + const auto values = Containers::arrayCast( + data.suffix(dataOffset).prefix(keyCount*sizeof(Vector3))); + dataOffset += values.size()*sizeof(values[0]); + + for(size_t k = 0; k < channel->mNumScalingKeys; ++k) { + keys[k] = channel->mScalingKeys[k].mTime / ticksPerSecond; + values[k] = Vector3::from(&channel->mScalingKeys[k].mValue[0]); + } + + const auto track = Animation::TrackView{ + keys, values, interpolation, + animationInterpolatorFor(interpolation), + extrapolationBefore, + extrapolationAfter}; + tracks[trackId++] = AnimationTrackData{AnimationTrackType::Vector3, + AnimationTrackType::Vector3, AnimationTrackTargetType::Scaling3D, + static_cast(target), track}; + } + } + } + + if(hadToRenormalize) + Warning{} << "Trade::AssimpImporter::animation(): quaternions in some rotation tracks were renormalized"; + + return AnimationData{std::move(data), std::move(tracks), + mergeAnimationClips ? nullptr : &_f->scene->mAnimations[id]}; +} + const void* AssimpImporter::doImporterState() const { return _f->scene; } diff --git a/src/MagnumPlugins/AssimpImporter/AssimpImporter.h b/src/MagnumPlugins/AssimpImporter/AssimpImporter.h index 0e071a399..8b601b69c 100644 --- a/src/MagnumPlugins/AssimpImporter/AssimpImporter.h +++ b/src/MagnumPlugins/AssimpImporter/AssimpImporter.h @@ -185,8 +185,8 @@ See @ref building-plugins, @ref cmake-plugins, @ref plugins and @section Trade-AssimpImporter-behavior Behavior and limitations -The plugin supports @ref ImporterFeature::OpenData and -@ref ImporterFeature::FileCallback features. The Assimp library loads +The plugin supports @ref ImporterFeature::OpenData, @ref ImporterFeature::OpenState +and @ref ImporterFeature::FileCallback features. The Assimp library loads everything during initial import, meaning all external file loading callbacks are called with @ref InputFileCallbackPolicy::LoadTemporary and the resources can be safely freed right after the @ref openData() / @ref openFile() function @@ -197,13 +197,57 @@ with @ref InputFileCallbackPolicy::LoadTemporary and @ref InputFileCallbackPolicy::Close is emitted right after the file is fully read. -Import of animation data is not supported at the moment. +Import of skeleton, skin and morph data is not supported at the moment. The importer recognizes @ref ImporterFlag::Verbose, enabling verbose logging in Assimp when the flag is enabled. However please note that since Assimp handles logging through a global singleton, it's not possible to have different verbosity levels in each instance. +@subsection Trade-AssimpImporter-behavior-animation Animation import + +- Assimp sometimes adds dummy animation tracks with a single key-value pair + and the default node transformation. If found, the importer removes these dummy + tracks and prints a message if verbose logging is enabled. Can be disabled + per-animation with the @cb{.ini} removeDummyAnimationTracks @ce option, see + @ref Trade-AssimpImporter-configuration "below". +- Channel order within animations is not always preserved + by Assimp, depending on file type and compiler. You may have to manually + order tracks by type and target after importing. +- Quaternion rotation tracks are postprocessed in order to make it + possible to use the faster + @ref Math::lerp(const Quaternion&, const Quaternion&, T) "Math::lerp()" / + @ref Math::slerp(const Quaternion&, const Quaternion&, T) "Math::slerp()" + functions instead of + @ref Math::lerpShortestPath(const Quaternion&, const Quaternion&, T) "Math::lerpShortestPath()" / + @ref Math::slerpShortestPath(const Quaternion&, const Quaternion&, T) "Math::slerpShortestPath()". Can be disabled per-animation with the + @cb{.ini} optimizeQuaternionShortestPath @ce option, see + @ref Trade-AssimpImporter-configuration "below". +- If quaternion rotation tracks are not normalized, the importer + prints a warning and normalizes them. Can be disabled per-animation with + the @cb{.ini} normalizeQuaternions @ce option, see + @ref Trade-AssimpImporter-configuration "below". +- Skeletons and skins are not supported +- Morph targets are not supported +- Animation tracks are always imported with + @ref Animation::Interpolation::Linear, because Assimp doesn't expose + any interpolation modes +- Animation tracks using `aiAnimBehaviour_DEFAULT` or `aiAnimBehaviour_REPEAT` + fall back to using @ref Animation::Extrapolation::Constant +- It's possible to request all animation clips to be merged into one using + the @cb{.ini} mergeAnimationClips @ce option in order to for example + preserve cinematic animations when using the Blender glTF exporter (as it + otherwise outputs a separate clip for each object). When this option is + enabled, @ref animationCount() always report either @cpp 0 @ce or + @cpp 1 @ce and the merged animation has no name. With this option enabled, + however, it can happen that multiple conflicting tracks affecting the same + node are merged in the same clip, causing the animation to misbehave. +- Assimp versions before commit [e3083c21f0a7beae6c37a2265b7919a02cbf83c4](https://github.com/assimp/assimp/commit/e3083c21f0a7beae6c37a2265b7919a02cbf83c4) + read spline-interpolated glTF animation tracks incorrectly and produce broken + animations. A warning is printed when importing glTF animations on these versions. + Because it's impossible to detect the actual brokenness, the warning is + printed even if the imported data may be correct. + @subsection Trade-AssimpImporter-behavior-materials Material import - Only materials with shading mode `aiShadingMode_Phong` are supported @@ -326,6 +370,7 @@ importer state methods: - @ref LightData::importerState() returns `aiLight` - @ref ImageData2D::importerState() may return `aiTexture`, if texture was embedded into the loaded file. + - @ref AnimationData::importerState() returns `aiAnimation` - @ref openState() expects a pointer to an Assimp scene (i.e., `const aiScene*`) and optionally a path (in order to be able to load textures, if needed) @@ -401,6 +446,11 @@ class MAGNUM_ASSIMPIMPORTER_EXPORT AssimpImporter: public AbstractImporter { MAGNUM_ASSIMPIMPORTER_LOCAL UnsignedInt doImage2DLevelCount(UnsignedInt id) override; MAGNUM_ASSIMPIMPORTER_LOCAL Containers::Optional doImage2D(UnsignedInt id, UnsignedInt level) override; + MAGNUM_ASSIMPIMPORTER_LOCAL UnsignedInt doAnimationCount() const override; + MAGNUM_ASSIMPIMPORTER_LOCAL std::string doAnimationName(UnsignedInt id) override; + MAGNUM_ASSIMPIMPORTER_LOCAL Int doAnimationForName(const std::string& name) override; + MAGNUM_ASSIMPIMPORTER_LOCAL Containers::Optional doAnimation(UnsignedInt id) override; + MAGNUM_ASSIMPIMPORTER_LOCAL const void* doImporterState() const override; Containers::Pointer _importer; diff --git a/src/MagnumPlugins/AssimpImporter/CMakeLists.txt b/src/MagnumPlugins/AssimpImporter/CMakeLists.txt index 136c8535c..85758af02 100644 --- a/src/MagnumPlugins/AssimpImporter/CMakeLists.txt +++ b/src/MagnumPlugins/AssimpImporter/CMakeLists.txt @@ -34,6 +34,30 @@ endif() configure_file(${CMAKE_CURRENT_SOURCE_DIR}/configure.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/configure.h) +# Go through all versions of interest and pick the highest one that compiles +foreach(_version 20201123 20190915 20151031) + try_compile(_works ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_CURRENT_SOURCE_DIR}/checkAssimpVersion.cpp + LINK_LIBRARIES Assimp::Assimp + COMPILE_DEFINITIONS + -DCHECK_VERSION=${_version} + # I thought I could make this use C++11 by linking to + # Corrade::Utility, but for some reason the effect of the + # CORRADE_CXX_STANDARD property doesn't get passed through. So I + # have to use an internal variable for that instead. + ${CORRADE_CXX11_STANDARD_FLAG} + OUTPUT_VARIABLE _version_output) + if(_works) + set(ASSIMP_VERSION ${_version}) + break() + endif() +endforeach() +if(NOT ASSIMP_VERSION) + message(SEND_ERROR "Could not detect Assimp version. Maybe even older than 3.2? ${_version_output}") +endif() +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/configureInternal.h.cmake + ${CMAKE_CURRENT_BINARY_DIR}/configureInternal.h) + # AssimpImporter plugin add_plugin(AssimpImporter "${MAGNUM_PLUGINS_IMPORTER_DEBUG_BINARY_INSTALL_DIR};${MAGNUM_PLUGINS_IMPORTER_DEBUG_LIBRARY_INSTALL_DIR}" @@ -41,6 +65,7 @@ add_plugin(AssimpImporter AssimpImporter.conf AssimpImporter.cpp AssimpImporter.h) +target_include_directories(AssimpImporter PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) if(MAGNUM_ASSIMPIMPORTER_BUILD_STATIC AND BUILD_STATIC_PIC) set_target_properties(AssimpImporter PROPERTIES POSITION_INDEPENDENT_CODE ON) endif() diff --git a/src/MagnumPlugins/AssimpImporter/Test/AssimpImporterTest.cpp b/src/MagnumPlugins/AssimpImporter/Test/AssimpImporterTest.cpp index d2ac66b6b..2936c7578 100644 --- a/src/MagnumPlugins/AssimpImporter/Test/AssimpImporterTest.cpp +++ b/src/MagnumPlugins/AssimpImporter/Test/AssimpImporterTest.cpp @@ -31,28 +31,33 @@ #include #include #include +#include #include +#include #include #include #include #include #include #include +#include +#include #include -#include -#include #include +#include #include +#include #include +#include +#include #include +#include #include #include #include #include #include #include -#include -#include #include /* in assimp 3.0, version.h is missing this include for ASSIMP_API */ #include @@ -61,6 +66,7 @@ #include #include "configure.h" +#include "MagnumPlugins/AssimpImporter/configureInternal.h" namespace Magnum { namespace Trade { namespace Test { namespace { @@ -74,6 +80,23 @@ struct AssimpImporterTest: TestSuite::Tester { void openData(); void openDataFailed(); + void animation(); + void animationGltf(); + + void animationGltfNoScene(); + void animationGltfBrokenSplineWarning(); + void animationGltfSpline(); + void animationGltfTicksPerSecondPatching(); + + void animationDummyTracksRemovalEnabled(); + void animationDummyTracksRemovalDisabled(); + void animationShortestPathOptimizationEnabled(); + void animationShortestPathOptimizationDisabled(); + void animationQuaternionNormalizationEnabled(); + void animationQuaternionNormalizationDisabled(); + void animationMergeEmpty(); + void animationMerge(); + void camera(); void light(); void lightUnsupported(); @@ -122,6 +145,26 @@ struct AssimpImporterTest: TestSuite::Tester { PluginManager::Manager _manager; }; +/* Used by animation() to store transformation results */ +struct AnimationNode { + const char* name; + Vector3 translation; + Quaternion rotation; + Vector3 scaling; +}; + +void correctAnimationNodeDefault(AnimationNode&) { } + +void correctAnimationNodeCollada(AnimationNode& node) { + /* Blender's Collada exporter doesn't seem to apply axis change + inside animations correctly. Do it manually, consistent with + export up and forward axis (see exported-animation.md): + y = z, z = -y */ + const Quaternion correction = Quaternion::rotation(-90.0_degf, Vector3::xAxis()); + node.translation = correction.transformVector(node.translation); + node.rotation = correction * node.rotation; +} + constexpr struct { const char* name; ImporterFlags flags; @@ -130,6 +173,16 @@ constexpr struct { {"verbose", ImporterFlag::Verbose} }; +constexpr struct { + const char* name; + const char* suffix; + void (*correctAnimationNode)(AnimationNode&); +} ExportedAnimationFileData[]{ + {"Collada", ".dae", &correctAnimationNodeCollada}, + {"FBX", ".fbx", &correctAnimationNodeDefault}, + {"glTF", ".gltf", &correctAnimationNodeDefault} +}; + constexpr struct { const char* name; const char* file; @@ -144,13 +197,33 @@ constexpr struct { AssimpImporterTest::AssimpImporterTest() { addInstancedTests({&AssimpImporterTest::openFile}, - Containers::arraySize(VerboseData)); + Containers::arraySize(VerboseData)); addTests({&AssimpImporterTest::openFileFailed, &AssimpImporterTest::openData, - &AssimpImporterTest::openDataFailed, + &AssimpImporterTest::openDataFailed}); + + addInstancedTests({&AssimpImporterTest::animation}, + Containers::arraySize(ExportedAnimationFileData)); + + addTests({&AssimpImporterTest::animationGltf, + &AssimpImporterTest::animationGltfNoScene, + &AssimpImporterTest::animationGltfBrokenSplineWarning, + &AssimpImporterTest::animationGltfSpline}); + + addInstancedTests({&AssimpImporterTest::animationGltfTicksPerSecondPatching, + &AssimpImporterTest::animationDummyTracksRemovalEnabled, + &AssimpImporterTest::animationDummyTracksRemovalDisabled}, + Containers::arraySize(VerboseData)); - &AssimpImporterTest::camera, + addTests({&AssimpImporterTest::animationShortestPathOptimizationEnabled, + &AssimpImporterTest::animationShortestPathOptimizationDisabled, + &AssimpImporterTest::animationQuaternionNormalizationEnabled, + &AssimpImporterTest::animationQuaternionNormalizationDisabled, + &AssimpImporterTest::animationMergeEmpty, + &AssimpImporterTest::animationMerge}); + + addTests({&AssimpImporterTest::camera, &AssimpImporterTest::light, &AssimpImporterTest::lightUnsupported, @@ -175,7 +248,7 @@ AssimpImporterTest::AssimpImporterTest() { addInstancedTests({&AssimpImporterTest::upDirectionPatching, &AssimpImporterTest::upDirectionPatchingPreTransformVertices}, - Containers::arraySize(UpDirectionPatchingData)); + Containers::arraySize(UpDirectionPatchingData)); addTests({&AssimpImporterTest::imageEmbedded, &AssimpImporterTest::imageExternal, @@ -286,6 +359,892 @@ void AssimpImporterTest::openDataFailed() { CORRADE_COMPARE(out.str(), "Trade::AssimpImporter::openData(): loading failed: No suitable reader found for the file format of file \"$$$___magic___$$$.\".\n"); } +/* This does not indicate general assimp animation support, only used to skip + tests on certain versions and test files. */ +bool supportsAnimation(const Containers::StringView fileName) { + /* 5.0.0 supports all of Collada, FBX, glTF */ + if(ASSIMP_IS_VERSION_5) + return true; + else if(fileName.hasSuffix(".gltf")) + return false; + else { + const unsigned int version = aiGetVersionMajor()*100 + aiGetVersionMinor(); + CORRADE_INTERNAL_ASSERT(fileName.hasSuffix(".dae") || fileName.hasSuffix(".fbx")); + /* That's as far back as I checked, both Collada and FBX animations supported */ + return version >= 302; + } +} + +void AssimpImporterTest::animation() { + auto&& data = ExportedAnimationFileData[testCaseInstanceId()]; + setTestCaseDescription(data.name); + + if(!supportsAnimation(data.suffix)) + CORRADE_SKIP("Animation for this file type is not supported with the current version of Assimp"); + + /* Animation created and exported with Blender. Most animation tracks + got resampled and/or merged during export, so there's no use + comparing against exact key/value arrays. Just apply all tracks + and check if the transformation roughly matches at specific + points in time. + animationGltf() test covers that AssimpImporter correctly + passes on what Assimp outputs. */ + + Containers::Pointer importer = _manager.instantiate("AssimpImporter"); + CORRADE_VERIFY(importer->openFile(Utility::Directory::join(ASSIMPIMPORTER_TEST_DIR, + "exported-animation" + std::string{data.suffix}))); + + /* Find animation target nodes by their name */ + AnimationNode nodes[3] = { + { "Rotating" }, + { "Scaling" }, + { "Translating" } + }; + + AnimationNode* nodeMap[3]{}; + + const UnsignedInt objectCount = importer->object3DCount(); + CORRADE_COMPARE(objectCount, Containers::arraySize(nodes)); + + for(UnsignedInt i = 0; i < objectCount; i++) { + const std::string name = importer->object3DName(i); + for(AnimationNode& n : nodes) { + /* Exported Collada files have spaces replaced with underscores, + so check for the first words only */ + if(name.find(n.name) == 0) { + /* Node names in the test files are unique */ + CORRADE_VERIFY(!nodeMap[i]); + nodeMap[i] = &n; + } + } + CORRADE_VERIFY(nodeMap[i]); + } + + CORRADE_VERIFY(importer->animationCount() > 0); + + Animation::Player player; + Containers::Array> animationData{importer->animationCount()}; + + for(UnsignedInt i = 0; i < importer->animationCount(); i++) { + auto animation = importer->animation(i); + CORRADE_VERIFY(animation); + + for(UnsignedInt j = 0; j < animation->trackCount(); j++) { + const auto track = animation->track(j); + /* All imported animations are linear */ + CORRADE_COMPARE(track.interpolation(), Animation::Interpolation::Linear); + + const UnsignedInt target = animation->trackTarget(j); + switch(animation->trackTargetType(j)) { + case AnimationTrackTargetType::Translation3D: + player.add(animation->track(j), nodeMap[target]->translation); + break; + case AnimationTrackTargetType::Rotation3D: + player.add(animation->track(j), nodeMap[target]->rotation); + break; + case AnimationTrackTargetType::Scaling3D: + player.add(animation->track(j), nodeMap[target]->scaling); + break; + default: CORRADE_FAIL_IF(true, "Unexpected track target type"); + } + } + + animationData[i] = animation->release(); + } + + /* Check values at known keyframes: + 4 keyframes at 0/60/120/180, exported at 24fps (60/24 = 2.5, etc.). */ + constexpr UnsignedInt KeyCount = 4; + constexpr Float keys[KeyCount]{0.0f, 2.5f, 5.0f, 7.5f}; + + CORRADE_VERIFY(player.duration().contains({ keys[0], keys[Containers::arraySize(keys) - 1] })); + player.play(0.0f); + + /* Some Blender exporters (e.g. FBX) and our manual Collada correction + flip axes by adding a non-identity default rotation to all nodes. Store + them so we can later compare against the rotation from key 0 to key i. */ + Quaternion initialRotation[Containers::arraySize(nodes)]; + + player.advance(keys[0]); + for(UnsignedInt i = 0; i < Containers::arraySize(nodes); i++) { + data.correctAnimationNode(nodes[i]); + initialRotation[i] = nodes[i].rotation; + } + + constexpr Vector3 translationData[KeyCount]{ + {0.0f, 0.0f, 3.0f}, + {5.0f, 0.0f, 3.0f}, + {5.0f, 0.0f, 8.0f}, + {0.0f, 0.0f, 8.0f} + }; + constexpr Vector3 rotationData[KeyCount]{ + {0.0f, 0.0f, 0.0f}, + {0.0f, 0.0f, Float(Rad(-90.0_degf))}, + {Float(Rad(90.0_degf)), 0.0f, Float(Rad(-90.0_degf))}, + {Float(Rad(135.0_degf)), 0.0f, Float(Rad(-90.0_degf))} + }; + constexpr Vector3 scalingData[KeyCount]{ + {1.0f, 1.0f, 1.0f}, + {5.0f, 1.0f, 1.0f}, + {1.0f, 5.0f, 1.0f}, + {0.5f, 0.5f, 0.5f} + }; + + for(UnsignedInt i = 0; i < Containers::arraySize(keys); i++) { + player.advance(keys[i]); + for(AnimationNode& n : nodes) + data.correctAnimationNode(n); + + /* Rotation from initial to current key */ + const Vector3 rotation{(nodes[0].rotation * initialRotation[0].inverted()).toEuler()}; + /* Apply possible non-identity rotation to get the correct scale */ + const Vector3 scaling = Math::abs(nodes[1].rotation.transformVector(nodes[1].scaling)); + const Vector3& translation = nodes[2].translation; + + /* Be lenient, resampling during export takes its toll */ + constexpr Vector3 Epsilon{0.05f}; + CORRADE_VERIFY(Math::abs(rotation - rotationData[i]) < Epsilon); + CORRADE_VERIFY(Math::abs(scaling - scalingData[i]) < Epsilon); + CORRADE_VERIFY(Math::abs(translation - translationData[i]) < Epsilon); + } +} + +struct AnimationTarget { + AnimationTrackTargetType type; + UnsignedInt keyCount; + UnsignedInt channel; +}; + +const Containers::StaticArray<3, AnimationTarget> AnimationGltfLinearTargets{ + AnimationTarget{AnimationTrackTargetType::Rotation3D, 2, ~0u}, + AnimationTarget{AnimationTrackTargetType::Translation3D, 4, ~0u}, + AnimationTarget{AnimationTrackTargetType::Scaling3D, 4, ~0u} +}; + +void AssimpImporterTest::animationGltf() { + if(!supportsAnimation(".gltf")) + CORRADE_SKIP("glTF 2 animation is not supported with the current version of Assimp"); + + /* Using the same files as TinyGltfImporterTest, but modified to include a + scene, because Assimp refuses to import animations if there is no scene. */ + + Containers::Pointer importer = _manager.instantiate("AssimpImporter"); + CORRADE_VERIFY(importer->openFile(Utility::Directory::join(ASSIMPIMPORTER_TEST_DIR, + "animation.gltf"))); + + CORRADE_COMPARE(importer->animationCount(), 3); + + /* Empty animation */ + { + CORRADE_COMPARE(importer->animationName(0), "empty"); + CORRADE_COMPARE(importer->animationForName("empty"), 0); + + auto animation = importer->animation(0); + CORRADE_VERIFY(animation); + CORRADE_VERIFY(animation->data().empty()); + CORRADE_COMPARE(animation->trackCount(), 0); + + /* Translation/rotation/scaling animation */ + } { + CORRADE_COMPARE(importer->animationName(1), "TRS animation"); + CORRADE_COMPARE(importer->animationForName("TRS animation"), 1); + + std::ostringstream out; + Debug redirectDebug{&out}; + + auto animation = importer->animation(1); + CORRADE_VERIFY(animation); + CORRADE_VERIFY(animation->importerState()); + /* Two rotation keys, four translation and scaling keys. */ + CORRADE_COMPARE(animation->data().size(), + 2*(sizeof(Float) + sizeof(Quaternion)) + + 2*4*(sizeof(Float) + sizeof(Vector3))); + CORRADE_COMPARE(animation->trackCount(), 3); + + /* Channel order is not preserved by Assimp so we can't directly + compare tracks by their glTF sampler id. Find channel through + target id, then use that. */ + + Containers::StaticArray<3, AnimationTarget> targets = AnimationGltfLinearTargets; + + for(UnsignedInt i = 0; i < animation->trackCount(); i++) { + CORRADE_VERIFY(animation->trackTarget(i) < Containers::arraySize(targets)); + AnimationTarget& target = targets[animation->trackTarget(i)]; + CORRADE_COMPARE(target.channel, ~0u); + target.channel = i; + } + + /* Rotation, linearly interpolated */ + const UnsignedInt ch0 = targets[0].channel; + CORRADE_COMPARE(animation->trackType(ch0), AnimationTrackType::Quaternion); + CORRADE_COMPARE(animation->trackResultType(ch0), AnimationTrackType::Quaternion); + CORRADE_COMPARE(animation->trackTargetType(ch0), AnimationTrackTargetType::Rotation3D); + CORRADE_COMPARE(animation->trackTarget(ch0), 0); + Animation::TrackView rotation = animation->track(ch0); + CORRADE_COMPARE(rotation.interpolation(), Animation::Interpolation::Linear); + CORRADE_COMPARE(rotation.before(), Animation::Extrapolation::Constant); + CORRADE_COMPARE(rotation.after(), Animation::Extrapolation::Constant); + constexpr Float rotationKeys[]{ + 1.25f, + 2.50f + }; + const Quaternion rotationValues[]{ + Quaternion::rotation(0.0_degf, Vector3::xAxis()), + Quaternion::rotation(180.0_degf, Vector3::xAxis()) + }; + CORRADE_COMPARE_AS(rotation.keys(), Containers::stridedArrayView(rotationKeys), TestSuite::Compare::Container); + CORRADE_COMPARE_AS(rotation.values(), Containers::stridedArrayView(rotationValues), TestSuite::Compare::Container); + CORRADE_COMPARE(rotation.at(1.875f), Quaternion::rotation(90.0_degf, Vector3::xAxis())); + + constexpr Float translationScalingKeys[]{ + 0.0f, + 1.25f, + 2.5f, + 3.75f + }; + + /* Translation, constant interpolated, sharing keys with scaling */ + const UnsignedInt ch1 = targets[1].channel; + CORRADE_COMPARE(animation->trackType(ch1), AnimationTrackType::Vector3); + CORRADE_COMPARE(animation->trackResultType(ch1), AnimationTrackType::Vector3); + CORRADE_COMPARE(animation->trackTargetType(ch1), AnimationTrackTargetType::Translation3D); + CORRADE_COMPARE(animation->trackTarget(ch1), 1); + Animation::TrackView translation = animation->track(ch1); + CORRADE_COMPARE(translation.interpolation(), Animation::Interpolation::Linear); + CORRADE_COMPARE(translation.before(), Animation::Extrapolation::Constant); + CORRADE_COMPARE(translation.after(), Animation::Extrapolation::Constant); + constexpr Vector3 translationData[]{ + Vector3::yAxis(0.0f), + Vector3::yAxis(2.5f), + Vector3::yAxis(2.5f), + Vector3::yAxis(0.0f) + }; + CORRADE_COMPARE_AS(translation.keys(), Containers::stridedArrayView(translationScalingKeys), TestSuite::Compare::Container); + CORRADE_COMPARE_AS(translation.values(), Containers::stridedArrayView(translationData), TestSuite::Compare::Container); + CORRADE_COMPARE(translation.at(1.5f), Vector3::yAxis(2.5f)); + + /* Scaling, linearly interpolated, sharing keys with translation */ + const UnsignedInt ch2 = targets[2].channel; + CORRADE_COMPARE(animation->trackType(ch2), AnimationTrackType::Vector3); + CORRADE_COMPARE(animation->trackResultType(ch2), AnimationTrackType::Vector3); + CORRADE_COMPARE(animation->trackTargetType(ch2), AnimationTrackTargetType::Scaling3D); + CORRADE_COMPARE(animation->trackTarget(ch2), 2); + Animation::TrackView scaling = animation->track(ch2); + CORRADE_COMPARE(scaling.interpolation(), Animation::Interpolation::Linear); + CORRADE_COMPARE(scaling.before(), Animation::Extrapolation::Constant); + CORRADE_COMPARE(scaling.after(), Animation::Extrapolation::Constant); + constexpr Vector3 scalingData[]{ + Vector3{1.0f}, + Vector3::zScale(5.0f), + Vector3::zScale(6.0f), + Vector3(1.0f), + }; + CORRADE_COMPARE_AS(scaling.keys(), Containers::stridedArrayView(translationScalingKeys), TestSuite::Compare::Container); + CORRADE_COMPARE_AS(scaling.values(), Containers::stridedArrayView(scalingData), TestSuite::Compare::Container); + CORRADE_COMPARE(scaling.at(1.5f), Vector3::zScale(5.2f)); + } +} + +void AssimpImporterTest::animationGltfNoScene() { + if(!supportsAnimation(".gltf")) + CORRADE_SKIP("glTF 2 animation is not supported with the current version of Assimp"); + + /* This reuses the TinyGltfImporter test files, not the corrected ones used by other tests. */ + Containers::Pointer importer = _manager.instantiate("AssimpImporter"); + CORRADE_VERIFY(importer->openFile(Utility::Directory::join(TINYGLTFIMPORTER_TEST_DIR, + "animation.gltf"))); + + CORRADE_EXPECT_FAIL("Assimp refuses to import glTF animations if the file has no scenes."); + CORRADE_COMPARE(importer->animationCount(), 3); +} + +void AssimpImporterTest::animationGltfBrokenSplineWarning() { + if(!supportsAnimation(".gltf")) + CORRADE_SKIP("glTF 2 animation is not supported with the current version of Assimp"); + + if(!ASSIMP_HAS_BROKEN_GLTF_SPLINES) + CORRADE_SKIP("Current version of assimp correctly imports glTF spline-interpolated animations."); + + Containers::Pointer importer = _manager.instantiate("AssimpImporter"); + + std::ostringstream out; + { + Warning redirectWarning{&out}; + CORRADE_VERIFY(importer->openFile(Utility::Directory::join(ASSIMPIMPORTER_TEST_DIR, + "animation.gltf"))); + } + CORRADE_COMPARE(out.str(), "Trade::AssimpImporter::openData(): spline-interpolated animations imported " + "from this file are most likely broken using this version of Assimp. Consult the " + "importer documentation for more information.\n"); +} + +void AssimpImporterTest::animationGltfSpline() { + if(!supportsAnimation(".gltf")) + CORRADE_SKIP("glTF 2 animation is not supported with the current version of Assimp"); + + Containers::Pointer importer = _manager.instantiate("AssimpImporter"); + CORRADE_VERIFY(importer->openFile(Utility::Directory::join(ASSIMPIMPORTER_TEST_DIR, + "animation.gltf"))); + + CORRADE_COMPARE(importer->animationCount(), 3); + CORRADE_COMPARE(importer->animationName(2), "TRS animation, splines"); + + constexpr Float keys[]{ 0.5f, 3.5f, 4.0f, 5.0f }; + + auto animation = importer->animation(2); + CORRADE_VERIFY(animation); + CORRADE_VERIFY(animation->importerState()); + /* Four T/R/S keys */ + CORRADE_COMPARE(animation->data().size(), + 4*(sizeof(Float) + sizeof(Quaternion)) + + 2*4*(sizeof(Float) + sizeof(Vector3))); + CORRADE_COMPARE(animation->trackCount(), 3); + + /* Map from target to channel id */ + Containers::StaticArray<3, AnimationTarget> targets = AnimationGltfLinearTargets; + + for(UnsignedInt i = 0; i < animation->trackCount(); i++) { + /* Nodes 3-5 */ + CORRADE_VERIFY(animation->trackTarget(i) >= Containers::arraySize(targets) && + animation->trackTarget(i) < (2*Containers::arraySize(targets))); + AnimationTarget& target = targets[animation->trackTarget(i) - Containers::arraySize(targets)]; + CORRADE_COMPARE(target.channel, ~0u); + target.channel = i; + } + + /* Rotation */ + const UnsignedInt ch0 = targets[0].channel; + CORRADE_COMPARE(animation->trackType(ch0), AnimationTrackType::Quaternion); + CORRADE_COMPARE(animation->trackResultType(ch0), AnimationTrackType::Quaternion); + CORRADE_COMPARE(animation->trackTargetType(ch0), AnimationTrackTargetType::Rotation3D); + CORRADE_COMPARE(animation->trackTarget(ch0), 3); + Animation::TrackView rotation = animation->track(ch0); + CORRADE_COMPARE(rotation.interpolation(), Animation::Interpolation::Linear); + CORRADE_COMPARE(rotation.before(), Animation::Extrapolation::Constant); + CORRADE_COMPARE(rotation.after(), Animation::Extrapolation::Constant); + CORRADE_COMPARE_AS(rotation.keys(), Containers::stridedArrayView(keys), TestSuite::Compare::Container); + { + #if ASSIMP_HAS_BROKEN_GLTF_SPLINES + CORRADE_EXPECT_FAIL("Current version of assimp incorrectly imports glTF spline-interpolated animations."); + #endif + + constexpr Quaternion rotationValues[]{ + {{0.780076f, 0.0260025f, 0.598059f}, 0.182018f}, + {{-0.711568f, 0.391362f, 0.355784f}, 0.462519f}, + {{0.598059f, 0.182018f, 0.0260025f}, 0.780076f}, + {{0.711568f, -0.355784f, -0.462519f}, -0.391362f} + }; + CORRADE_COMPARE_AS(rotation.values(), Containers::stridedArrayView(rotationValues), TestSuite::Compare::Container); + } + + /* Translation */ + const UnsignedInt ch1 = targets[1].channel; + CORRADE_COMPARE(animation->trackType(ch1), AnimationTrackType::Vector3); + CORRADE_COMPARE(animation->trackResultType(ch1), AnimationTrackType::Vector3); + CORRADE_COMPARE(animation->trackTargetType(ch1), AnimationTrackTargetType::Translation3D); + CORRADE_COMPARE(animation->trackTarget(ch1), 4); + Animation::TrackView translation = animation->track(ch1); + CORRADE_COMPARE(translation.interpolation(), Animation::Interpolation::Linear); + CORRADE_COMPARE(translation.before(), Animation::Extrapolation::Constant); + CORRADE_COMPARE(translation.after(), Animation::Extrapolation::Constant); + CORRADE_COMPARE_AS(translation.keys(), Containers::stridedArrayView(keys), TestSuite::Compare::Container); + { + #if ASSIMP_HAS_BROKEN_GLTF_SPLINES + CORRADE_EXPECT_FAIL("Current version of assimp incorrectly imports glTF spline-interpolated animations."); + #endif + + constexpr Vector3 translationValues[]{ + {3.0f, 0.1f, 2.5f}, + {-2.0f, 1.1f, -4.3f}, + {1.5f, 9.8f, -5.1f}, + {5.1f, 0.1f, -7.3f} + }; + CORRADE_COMPARE_AS(translation.values(), Containers::stridedArrayView(translationValues), TestSuite::Compare::Container); + } + + /* Scaling */ + const UnsignedInt ch2 = targets[2].channel; + CORRADE_COMPARE(animation->trackType(ch2), AnimationTrackType::Vector3); + CORRADE_COMPARE(animation->trackResultType(ch2), AnimationTrackType::Vector3); + CORRADE_COMPARE(animation->trackTargetType(ch2), AnimationTrackTargetType::Scaling3D); + CORRADE_COMPARE(animation->trackTarget(ch2), 5); + Animation::TrackView scaling = animation->track(ch2); + CORRADE_COMPARE(scaling.interpolation(), Animation::Interpolation::Linear); + CORRADE_COMPARE(scaling.before(), Animation::Extrapolation::Constant); + CORRADE_COMPARE(scaling.after(), Animation::Extrapolation::Constant); + CORRADE_COMPARE_AS(scaling.keys(), Containers::stridedArrayView(keys), TestSuite::Compare::Container); + { + #if ASSIMP_HAS_BROKEN_GLTF_SPLINES + CORRADE_EXPECT_FAIL("Current version of assimp incorrectly imports glTF spline-interpolated animations."); + #endif + + constexpr Vector3 scalingData[]{ + {-2.0f, 1.1f, -4.3f}, + {5.1f, 0.1f, -7.3f}, + {3.0f, 0.1f, 2.5f}, + {1.5f, 9.8f, -5.1f} + }; + CORRADE_COMPARE_AS(scaling.values(), Containers::stridedArrayView(scalingData), TestSuite::Compare::Container); + } +} + +void AssimpImporterTest::animationGltfTicksPerSecondPatching() { + auto&& data = VerboseData[testCaseInstanceId()]; + setTestCaseDescription(data.name); + + if(!supportsAnimation(".gltf")) + CORRADE_SKIP("glTF 2 animation is not supported with the current version of Assimp"); + + /* This was fixed right after 5.0.0, but 5.0.1 only selected compilation + fixes and didn't bump the minor version. Boldly assuming the next + minor version will have fixes from 2019. */ + const unsigned int version = aiGetVersionMajor()*100 + aiGetVersionMinor(); + const bool hasInvalidTicksPerSecond = version <= 500; + if(!hasInvalidTicksPerSecond) + CORRADE_SKIP("Current version of assimp correctly sets glTF ticks per second."); + + Containers::Pointer importer = _manager.instantiate("AssimpImporter"); + importer->setFlags(data.flags); + CORRADE_VERIFY(importer->openFile(Utility::Directory::join(ASSIMPIMPORTER_TEST_DIR, + "animation.gltf"))); + + std::ostringstream out; + { + Debug redirectDebug{&out}; + CORRADE_VERIFY(importer->animation(1)); + } + + if(data.flags >= ImporterFlag::Verbose) { + CORRADE_VERIFY(Containers::StringView{out.str()}.contains( + " ticks per second is incorrect for glTF, patching to 1000\n")); + } else + CORRADE_VERIFY(out.str().empty()); +} + +void AssimpImporterTest::animationDummyTracksRemovalEnabled() { + auto&& data = VerboseData[testCaseInstanceId()]; + setTestCaseDescription(data.name); + + if(!supportsAnimation(".gltf")) + CORRADE_SKIP("glTF 2 animation is not supported with the current version of Assimp"); + + /* Correct removal is already implicitly tested in animationGltf(), + only check for track count/size and the message here */ + + Containers::Pointer importer = _manager.instantiate("AssimpImporter"); + importer->setFlags(data.flags); + /* Enabled by default */ + CORRADE_VERIFY(importer->configuration().value("removeDummyAnimationTracks")); + CORRADE_VERIFY(importer->openFile(Utility::Directory::join(ASSIMPIMPORTER_TEST_DIR, + "animation.gltf"))); + + Containers::Optional animation; + std::ostringstream out; + { + Debug redirectDebug{&out}; + animation = importer->animation(1); + CORRADE_VERIFY(animation); + } + + /* Animation for each target object id */ + Containers::StaticArray<3, AnimationTarget> targets = AnimationGltfLinearTargets; + + /* 1 track for each object */ + CORRADE_COMPARE(animation->trackCount(), Containers::arraySize(targets)); + for(UnsignedInt i = 0; i < animation->trackCount(); i++) { + CORRADE_VERIFY(animation->trackTarget(i) < Containers::arraySize(targets)); + AnimationTarget& target = targets[animation->trackTarget(i)]; + CORRADE_COMPARE(target.channel, ~0u); + target.channel = i; + CORRADE_COMPARE(animation->trackTargetType(i), target.type); + CORRADE_COMPARE(animation->track(i).size(), target.keyCount); + } + + if(data.flags >= ImporterFlag::Verbose) { + const std::string str = out.str(); + CORRADE_VERIFY(Containers::StringView{str}.contains( + Utility::formatString( + "Trade::AssimpImporter::animation(): ignoring dummy translation track in animation 1, channel {}\n" + "Trade::AssimpImporter::animation(): ignoring dummy scaling track in animation 1, channel {}\n", + targets[0].channel, targets[0].channel))); + CORRADE_VERIFY(Containers::StringView{str}.contains( + Utility::formatString( + "Trade::AssimpImporter::animation(): ignoring dummy rotation track in animation 1, channel {}\n" + "Trade::AssimpImporter::animation(): ignoring dummy scaling track in animation 1, channel {}\n", + targets[1].channel, targets[1].channel))); + CORRADE_VERIFY(Containers::StringView{str}.contains( + Utility::formatString( + "Trade::AssimpImporter::animation(): ignoring dummy translation track in animation 1, channel {}\n" + "Trade::AssimpImporter::animation(): ignoring dummy rotation track in animation 1, channel {}\n", + targets[2].channel, targets[2].channel))); + } else + CORRADE_VERIFY(out.str().empty()); +} + +void AssimpImporterTest::animationDummyTracksRemovalDisabled() { + auto&& data = VerboseData[testCaseInstanceId()]; + setTestCaseDescription(data.name); + + if(!supportsAnimation(".gltf")) + CORRADE_SKIP("glTF 2 animation is not supported with the current version of Assimp"); + + Containers::Pointer importer = _manager.instantiate("AssimpImporter"); + importer->setFlags(data.flags); + /* Explicitly disable */ + importer->configuration().setValue("removeDummyAnimationTracks", false); + CORRADE_VERIFY(importer->openFile(Utility::Directory::join(ASSIMPIMPORTER_TEST_DIR, + "animation.gltf"))); + + Containers::Optional animation; + std::ostringstream out; + { + Debug redirectDebug{&out}; + animation = importer->animation(1); + CORRADE_VERIFY(animation); + } + + /* Non-dummy animation for each target object id */ + Containers::StaticArray<3, AnimationTarget> targets = AnimationGltfLinearTargets; + + /* Animation type for each track within a channel. + Tracks within channels are always added in the order T,R,S */ + constexpr AnimationTrackTargetType trackAnimationType[]{ + AnimationTrackTargetType::Translation3D, + AnimationTrackTargetType::Rotation3D, + AnimationTrackTargetType::Scaling3D + }; + + /* T/R/S tracks (1 original + 2 dummy tracks) for each object */ + CORRADE_COMPARE(animation->trackCount(), + Containers::arraySize(targets)*Containers::arraySize(trackAnimationType)); + + for(UnsignedInt i = 0; i < animation->trackCount(); i++) { + CORRADE_VERIFY(animation->trackTarget(i) < Containers::arraySize(targets)); + const UnsignedInt channel = i / Containers::arraySize(trackAnimationType); + const UnsignedInt indexWithinChannel = i % Containers::arraySize(trackAnimationType); + AnimationTarget& target = targets[animation->trackTarget(i)]; + CORRADE_VERIFY(target.channel == ~0u || target.channel == channel); + target.channel = channel; + CORRADE_COMPARE(animation->trackTargetType(i), trackAnimationType[indexWithinChannel]); + const UnsignedInt keyCount = animation->trackTargetType(i) == target.type + ? target.keyCount : 1; + CORRADE_COMPARE(animation->track(i).size(), keyCount); + } + + const std::string str = out.str(); + CORRADE_VERIFY(!Containers::StringView{str}.contains( + "Trade::AssimpImporter::animation(): ignoring dummy translation track in animation")); + CORRADE_VERIFY(!Containers::StringView{str}.contains( + "Trade::AssimpImporter::animation(): ignoring dummy rotation track in animation")); + CORRADE_VERIFY(!Containers::StringView{str}.contains( + "Trade::AssimpImporter::animation(): ignoring dummy scaling track in animation")); +} + +void AssimpImporterTest::animationShortestPathOptimizationEnabled() { + if(!supportsAnimation(".gltf")) + CORRADE_SKIP("glTF 2 animation is not supported with the current version of Assimp"); + + Containers::Pointer importer = _manager.instantiate("AssimpImporter"); + /* Enabled by default */ + CORRADE_VERIFY(importer->configuration().value("optimizeQuaternionShortestPath")); + CORRADE_VERIFY(importer->openFile(Utility::Directory::join(ASSIMPIMPORTER_TEST_DIR, + "animation-patching.gltf"))); + + CORRADE_COMPARE(importer->animationCount(), 2); + CORRADE_COMPARE(importer->animationName(0), "Quaternion shortest-path patching"); + + auto animation = importer->animation(0); + CORRADE_VERIFY(animation); + CORRADE_COMPARE(animation->trackCount(), 1); + CORRADE_COMPARE(animation->trackType(0), AnimationTrackType::Quaternion); + Animation::TrackView track = animation->track(0); + constexpr Quaternion rotationValues[]{ + {{0.0f, 0.0f, 0.92388f}, -0.382683f}, // 0 s: 225° + {{0.0f, 0.0f, 0.707107f}, -0.707107f}, // 1 s: 270° + {{0.0f, 0.0f, 0.382683f}, -0.92388f}, // 2 s: 315° + {{0.0f, 0.0f, 0.0f}, -1.0f}, // 3 s: 360° / 0° + {{0.0f, 0.0f, -0.382683f}, -0.92388f}, // 4 s: 45° (flipped) + {{0.0f, 0.0f, -0.707107f}, -0.707107f}, // 5 s: 90° (flipped) + {{0.0f, 0.0f, -0.92388f}, -0.382683f}, // 6 s: 135° (flipped back) + {{0.0f, 0.0f, -1.0f}, 0.0f}, // 7 s: 180° (flipped back) + {{0.0f, 0.0f, -0.92388f}, 0.382683f} // 8 s: 225° (flipped) + }; + CORRADE_COMPARE_AS(track.values(), Containers::stridedArrayView(rotationValues), TestSuite::Compare::Container); + + CORRADE_COMPARE(track.at(Math::slerp, 0.5f).axis(), Vector3::zAxis()); + CORRADE_COMPARE(track.at(Math::slerp, 1.5f).axis(), Vector3::zAxis()); + CORRADE_COMPARE(track.at(Math::slerp, 2.5f).axis(), Vector3::zAxis()); + CORRADE_COMPARE(track.at(Math::slerp, 3.5f).axis(), -Vector3::zAxis()); + CORRADE_COMPARE(track.at(Math::slerp, 4.5f).axis(), -Vector3::zAxis()); + CORRADE_COMPARE(track.at(Math::slerp, 5.5f).axis(), -Vector3::zAxis()); + CORRADE_COMPARE(track.at(Math::slerp, 6.5f).axis(), -Vector3::zAxis()); + CORRADE_COMPARE(track.at(Math::slerp, 7.5f).axis(), -Vector3::zAxis()); + + /* Some are negated because of the flipped axis but other than that it's + nicely monotonic */ + CORRADE_COMPARE(track.at(Math::slerp, 0.5f).angle(), 247.5_degf); + CORRADE_COMPARE(track.at(Math::slerp, 1.5f).angle(), 292.5_degf); + CORRADE_COMPARE(track.at(Math::slerp, 2.5f).angle(), 337.5_degf); + CORRADE_COMPARE(track.at(Math::slerp, 3.5f).angle(), 360.0_degf - 22.5_degf); + CORRADE_COMPARE(track.at(Math::slerp, 4.5f).angle(), 360.0_degf - 67.5_degf); + CORRADE_COMPARE(track.at(Math::slerp, 5.5f).angle(), 360.0_degf - 112.5_degf); + CORRADE_COMPARE(track.at(Math::slerp, 6.5f).angle(), 360.0_degf - 157.5_degf); + CORRADE_COMPARE(track.at(Math::slerp, 7.5f).angle(), 360.0_degf - 202.5_degf); +} + +void AssimpImporterTest::animationShortestPathOptimizationDisabled() { + if(!supportsAnimation(".gltf")) + CORRADE_SKIP("glTF 2 animation is not supported with the current version of Assimp"); + + Containers::Pointer importer = _manager.instantiate("AssimpImporter"); + /* Explicitly disable */ + importer->configuration().setValue("optimizeQuaternionShortestPath", false); + CORRADE_VERIFY(importer->openFile(Utility::Directory::join(ASSIMPIMPORTER_TEST_DIR, + "animation-patching.gltf"))); + + CORRADE_COMPARE(importer->animationCount(), 2); + CORRADE_COMPARE(importer->animationName(0), "Quaternion shortest-path patching"); + + auto animation = importer->animation(0); + CORRADE_VERIFY(animation); + CORRADE_COMPARE(animation->trackCount(), 1); + CORRADE_COMPARE(animation->trackType(0), AnimationTrackType::Quaternion); + Animation::TrackView track = animation->track(0); + + /* Should be the same as in animation-patching.bin.in */ + constexpr Quaternion rotationValues[]{ + {{0.0f, 0.0f, 0.92388f}, -0.382683f}, // 0 s: 225° + {{0.0f, 0.0f, 0.707107f}, -0.707107f}, // 1 s: 270° + {{0.0f, 0.0f, 0.382683f}, -0.92388f}, // 2 s: 315° + {{0.0f, 0.0f, 0.0f}, -1.0f}, // 3 s: 360° / 0° + {{0.0f, 0.0f, 0.382683f}, 0.92388f}, // 4 s: 45° (longer path) + {{0.0f, 0.0f, 0.707107f}, 0.707107f}, // 5 s: 90° + {{0.0f, 0.0f, -0.92388f}, -0.382683f}, // 6 s: 135° (longer path) + {{0.0f, 0.0f, -1.0f}, 0.0f}, // 7 s: 180° + {{0.0f, 0.0f, 0.92388f}, -0.382683f} // 8 s: 225° (longer path) + }; + CORRADE_COMPARE_AS(track.values(), Containers::stridedArrayView(rotationValues), TestSuite::Compare::Container); + + CORRADE_COMPARE(track.at(Math::slerpShortestPath, 0.5f).axis(), Vector3::zAxis()); + CORRADE_COMPARE(track.at(Math::slerpShortestPath, 1.5f).axis(), Vector3::zAxis()); + CORRADE_COMPARE(track.at(Math::slerpShortestPath, 2.5f).axis(), Vector3::zAxis()); + CORRADE_COMPARE(track.at(Math::slerpShortestPath, 3.5f).axis(), Vector3::zAxis()); + CORRADE_COMPARE(track.at(Math::slerpShortestPath, 4.5f).axis(), Vector3::zAxis()); + CORRADE_COMPARE(track.at(Math::slerpShortestPath, 5.5f).axis(), -Vector3::zAxis()); + CORRADE_COMPARE(track.at(Math::slerpShortestPath, 6.5f).axis(), -Vector3::zAxis()); + CORRADE_COMPARE(track.at(Math::slerpShortestPath, 7.5f).axis(), Vector3::zAxis()); + + /* Some are negated because of the flipped axis but other than that it's + nicely monotonic because slerpShortestPath() ensures that */ + CORRADE_COMPARE(track.at(Math::slerpShortestPath, 0.5f).angle(), 247.5_degf); + CORRADE_COMPARE(track.at(Math::slerpShortestPath, 1.5f).angle(), 292.5_degf); + CORRADE_COMPARE(track.at(Math::slerpShortestPath, 2.5f).angle(), 337.5_degf); + CORRADE_COMPARE(track.at(Math::slerpShortestPath, 3.5f).angle(), 22.5_degf); + CORRADE_COMPARE(track.at(Math::slerpShortestPath, 4.5f).angle(), 67.5_degf); + CORRADE_COMPARE(track.at(Math::slerpShortestPath, 5.5f).angle(), 360.0_degf - 112.5_degf); + CORRADE_COMPARE(track.at(Math::slerpShortestPath, 6.5f).angle(), 360.0_degf - 157.5_degf); + CORRADE_COMPARE(track.at(Math::slerpShortestPath, 7.5f).angle(), 202.5_degf); + + CORRADE_COMPARE(track.at(Math::slerp, 0.5f).axis(), Vector3::zAxis()); + CORRADE_COMPARE(track.at(Math::slerp, 1.5f).axis(), Vector3::zAxis()); + CORRADE_COMPARE(track.at(Math::slerp, 2.5f).axis(), Vector3::zAxis()); + CORRADE_COMPARE(track.at(Math::slerp, 3.5f).axis(), Vector3::zAxis()); + CORRADE_COMPARE(track.at(Math::slerp, 4.5f).axis(), Vector3::zAxis()); + CORRADE_COMPARE(track.at(Math::slerp, 5.5f).axis(), -Vector3::zAxis()); + CORRADE_COMPARE(track.at(Math::slerp, 6.5f).axis(), -Vector3::zAxis()); + CORRADE_COMPARE(track.at(Math::slerp, 7.5f).axis(), -Vector3::zAxis(1.00004f)); /* ?! */ + + /* Things are a complete chaos when using non-SP slerp */ + CORRADE_COMPARE(track.at(Math::slerp, 0.5f).angle(), 247.5_degf); + CORRADE_COMPARE(track.at(Math::slerp, 1.5f).angle(), 292.5_degf); + CORRADE_COMPARE(track.at(Math::slerp, 2.5f).angle(), 337.5_degf); + CORRADE_COMPARE(track.at(Math::slerp, 3.5f).angle(), 202.5_degf); + CORRADE_COMPARE(track.at(Math::slerp, 4.5f).angle(), 67.5_degf); + CORRADE_COMPARE(track.at(Math::slerp, 5.5f).angle(), 67.5_degf); + CORRADE_COMPARE(track.at(Math::slerp, 6.5f).angle(), 202.5_degf); + CORRADE_COMPARE(track.at(Math::slerp, 7.5f).angle(), 337.5_degf); +} + +void AssimpImporterTest::animationQuaternionNormalizationEnabled() { + if(!supportsAnimation(".gltf")) + CORRADE_SKIP("glTF 2 animation is not supported with the current version of Assimp"); + + Containers::Pointer importer = _manager.instantiate("AssimpImporter"); + /* Enabled by default */ + CORRADE_VERIFY(importer->configuration().value("normalizeQuaternions")); + CORRADE_VERIFY(importer->openFile(Utility::Directory::join(ASSIMPIMPORTER_TEST_DIR, + "animation-patching.gltf"))); + CORRADE_COMPARE(importer->animationCount(), 2); + CORRADE_COMPARE(importer->animationName(1), "Quaternion normalization patching"); + + Containers::Optional animation; + std::ostringstream out; + { + Warning redirectWarning{&out}; + animation = importer->animation(1); + } + CORRADE_VERIFY(animation); + CORRADE_VERIFY(Containers::StringView{out.str()}.contains( + "Trade::AssimpImporter::animation(): quaternions in some rotation tracks were renormalized\n")); + CORRADE_COMPARE(animation->trackCount(), 1); + CORRADE_COMPARE(animation->trackType(0), AnimationTrackType::Quaternion); + + Animation::TrackView track = animation->track(0); + constexpr Quaternion rotationValues[]{ + {{0.0f, 0.0f, 0.382683f}, 0.92388f}, // is normalized + {{0.0f, 0.0f, 0.707107f}, 0.707107f}, // is not, renormalized + {{0.0f, 0.0f, 0.382683f}, 0.92388f}, // is not, renormalized + }; + /* There is a *ridiculous* bug in Assimp 5.0.1(?) with glTF animations that makes it + ignore the value sampler size and always uses the key sampler size + (instead of using the minimum of the two). Wouldn't be surprised + if this produces an out-of-bounds access somewhere, too. */ + CORRADE_COMPARE_AS(track.values().prefix(Containers::arraySize(rotationValues)), + Containers::stridedArrayView(rotationValues), TestSuite::Compare::Container); +} + +void AssimpImporterTest::animationQuaternionNormalizationDisabled() { + if(!supportsAnimation(".gltf")) + CORRADE_SKIP("glTF 2 animation is not supported with the current version of Assimp"); + + Containers::Pointer importer = _manager.instantiate("AssimpImporter"); + /* Explicitly disable */ + CORRADE_VERIFY(importer->configuration().setValue("normalizeQuaternions", false)); + CORRADE_VERIFY(importer->openFile(Utility::Directory::join(ASSIMPIMPORTER_TEST_DIR, + "animation-patching.gltf"))); + CORRADE_COMPARE(importer->animationCount(), 2); + CORRADE_COMPARE(importer->animationName(1), "Quaternion normalization patching"); + + auto animation = importer->animation(1); + CORRADE_VERIFY(animation); + CORRADE_COMPARE(animation->trackCount(), 1); + CORRADE_COMPARE(animation->trackType(0), AnimationTrackType::Quaternion); + + Animation::TrackView track = animation->track(0); + const Quaternion rotationValues[]{ + Quaternion{{0.0f, 0.0f, 0.382683f}, 0.92388f}, // is normalized + Quaternion{{0.0f, 0.0f, 0.707107f}, 0.707107f}*2, // is not + Quaternion{{0.0f, 0.0f, 0.382683f}, 0.92388f}*2, // is not + }; + CORRADE_COMPARE_AS(track.values().prefix(Containers::arraySize(rotationValues)), + Containers::stridedArrayView(rotationValues), TestSuite::Compare::Container); +} + +void AssimpImporterTest::animationMergeEmpty() { + if(!supportsAnimation(".gltf")) + CORRADE_SKIP("glTF 2 animation is not supported with the current version of Assimp"); + + Containers::Pointer importer = _manager.instantiate("AssimpImporter"); + /* Enable animation merging */ + importer->configuration().setValue("mergeAnimationClips", true); + CORRADE_VERIFY(importer->openFile(Utility::Directory::join(TINYGLTFIMPORTER_TEST_DIR, + "empty.gltf"))); + + CORRADE_COMPARE(importer->animationCount(), 0); + CORRADE_COMPARE(importer->animationForName(""), -1); +} + +void AssimpImporterTest::animationMerge() { + if(!supportsAnimation(".gltf")) + CORRADE_SKIP("glTF 2 animation is not supported with the current version of Assimp"); + + Containers::Pointer importer = _manager.instantiate("AssimpImporter"); + /* Enable animation merging */ + importer->configuration().setValue("mergeAnimationClips", true); + CORRADE_VERIFY(importer->openFile(Utility::Directory::join(ASSIMPIMPORTER_TEST_DIR, + "animation.gltf"))); + + CORRADE_COMPARE(importer->animationCount(), 1); + CORRADE_COMPARE(importer->animationName(0), ""); + CORRADE_COMPARE(importer->animationForName(""), -1); + + auto animation = importer->animation(0); + CORRADE_VERIFY(animation); + CORRADE_VERIFY(!animation->importerState()); /* No particular clip */ + /* + - Nothing from the first animation + - Two rotation keys, four translation and scaling keys + from the second animation + - Four T/R/S keys from the third animation + */ + CORRADE_COMPARE(animation->data().size(), + 2*(sizeof(Float) + sizeof(Quaternion)) + + 2*4*(sizeof(Float) + sizeof(Vector3)) + + 4*(sizeof(Float) + sizeof(Quaternion)) + + 2*4*(sizeof(Float) + sizeof(Vector3))); + + CORRADE_COMPARE(animation->trackCount(), 6); + + /* Map from target to channel id */ + + Containers::StaticArray<3, AnimationTarget> linearTargets = AnimationGltfLinearTargets; + /* The order is the same (target = linear target + 3), we can re-use + the data as long as we don't use keyCount */ + Containers::StaticArray<3, AnimationTarget> splineTargets = AnimationGltfLinearTargets; + + for(UnsignedInt i = 0; i < animation->trackCount(); i++) { + const UnsignedInt targetId = animation->trackTarget(i); + CORRADE_VERIFY(targetId < (Containers::arraySize(linearTargets) + + Containers::arraySize(splineTargets))); + AnimationTarget& target = targetId < Containers::arraySize(linearTargets) + ? linearTargets[targetId] + : splineTargets[targetId - Containers::arraySize(linearTargets)]; + CORRADE_COMPARE(target.channel, ~0u); + target.channel = i; + } + + /* Rotation, linearly interpolated */ + const UnsignedInt ch0 = linearTargets[0].channel; + CORRADE_COMPARE(animation->trackType(ch0), AnimationTrackType::Quaternion); + CORRADE_COMPARE(animation->trackTargetType(ch0), AnimationTrackTargetType::Rotation3D); + CORRADE_COMPARE(animation->trackTarget(ch0), 0); + Animation::TrackView rotation = animation->track(ch0); + CORRADE_COMPARE(rotation.interpolation(), Animation::Interpolation::Linear); + CORRADE_COMPARE(rotation.at(1.875f), Quaternion::rotation(90.0_degf, Vector3::xAxis())); + + /* Translation, constant interpolated, sharing keys with scaling */ + const UnsignedInt ch1 = linearTargets[1].channel; + CORRADE_COMPARE(animation->trackType(ch1), AnimationTrackType::Vector3); + CORRADE_COMPARE(animation->trackTargetType(ch1), AnimationTrackTargetType::Translation3D); + CORRADE_COMPARE(animation->trackTarget(ch1), 1); + Animation::TrackView translation = animation->track(ch1); + CORRADE_COMPARE(translation.interpolation(), Animation::Interpolation::Linear); + CORRADE_COMPARE(translation.at(1.5f), Vector3::yAxis(2.5f)); + + /* Scaling, linearly interpolated, sharing keys with translation */ + const UnsignedInt ch2 = linearTargets[2].channel; + CORRADE_COMPARE(animation->trackType(ch2), AnimationTrackType::Vector3); + CORRADE_COMPARE(animation->trackTargetType(ch2), AnimationTrackTargetType::Scaling3D); + CORRADE_COMPARE(animation->trackTarget(ch2), 2); + Animation::TrackView scaling = animation->track(ch2); + CORRADE_COMPARE(scaling.interpolation(), Animation::Interpolation::Linear); + CORRADE_COMPARE(scaling.at(1.5f), Vector3::zScale(5.2f)); + + /* Rotation, spline interpolated */ + const UnsignedInt ch3 = splineTargets[0].channel; + CORRADE_COMPARE(animation->trackType(ch3), AnimationTrackType::Quaternion); + CORRADE_COMPARE(animation->trackTargetType(ch3), AnimationTrackTargetType::Rotation3D); + CORRADE_COMPARE(animation->trackTarget(ch3), 3); + Animation::TrackView rotation2 = animation->track(ch3); + CORRADE_COMPARE(rotation2.interpolation(), Animation::Interpolation::Linear); + + /* Translation, spline interpolated */ + const UnsignedInt ch4 = splineTargets[1].channel; + CORRADE_COMPARE(animation->trackType(ch4), AnimationTrackType::Vector3); + CORRADE_COMPARE(animation->trackTargetType(ch4), AnimationTrackTargetType::Translation3D); + CORRADE_COMPARE(animation->trackTarget(ch4), 4); + Animation::TrackView translation2 = animation->track(ch4); + CORRADE_COMPARE(translation2.interpolation(), Animation::Interpolation::Linear); + + /* Scaling, spline interpolated */ + const UnsignedInt ch5 = splineTargets[2].channel; + CORRADE_COMPARE(animation->trackType(ch5), AnimationTrackType::Vector3); + CORRADE_COMPARE(animation->trackTargetType(ch5), AnimationTrackTargetType::Scaling3D); + CORRADE_COMPARE(animation->trackTarget(ch5), 5); + Animation::TrackView scaling2 = animation->track(ch5); + CORRADE_COMPARE(scaling2.interpolation(), Animation::Interpolation::Linear); +} + void AssimpImporterTest::camera() { Containers::Pointer importer = _manager.instantiate("AssimpImporter"); CORRADE_VERIFY(importer->openFile(Utility::Directory::join(ASSIMPIMPORTER_TEST_DIR, "camera.dae"))); @@ -936,7 +1895,7 @@ void AssimpImporterTest::emptyGltf() { Containers::Pointer importer = _manager.instantiate("AssimpImporter"); - CORRADE_VERIFY(importer->openFile(Utility::Directory::join(ASSIMPIMPORTER_TEST_DIR, "empty.gltf"))); + CORRADE_VERIFY(importer->openFile(Utility::Directory::join(TINYGLTFIMPORTER_TEST_DIR, "empty.gltf"))); CORRADE_COMPARE(importer->defaultScene(), -1); CORRADE_COMPARE(importer->sceneCount(), 0); CORRADE_COMPARE(importer->object3DCount(), 0); @@ -1470,10 +2429,11 @@ void AssimpImporterTest::fileCallbackNotFound() { /* Assimp 5.0 changed the error string. aiGetVersion*() returns 401 for assimp 5, FFS, so we have to check differently. See CMakeLists.txt for details. */ - if(ASSIMP_IS_VERSION_5) + #if ASSIMP_IS_VERSION_5 CORRADE_COMPARE(out.str(), "Trade::AssimpImporter::openFile(): failed to open some-file.dae: Failed to open file 'some-file.dae'.\n"); - else + #else CORRADE_COMPARE(out.str(), "Trade::AssimpImporter::openFile(): failed to open some-file.dae: Failed to open file some-file.dae.\n"); + #endif } void AssimpImporterTest::fileCallbackEmptyFile() { diff --git a/src/MagnumPlugins/AssimpImporter/Test/CMakeLists.txt b/src/MagnumPlugins/AssimpImporter/Test/CMakeLists.txt index 73d954360..57fa9f0e2 100644 --- a/src/MagnumPlugins/AssimpImporter/Test/CMakeLists.txt +++ b/src/MagnumPlugins/AssimpImporter/Test/CMakeLists.txt @@ -24,9 +24,11 @@ # if(CORRADE_TARGET_EMSCRIPTEN OR CORRADE_TARGET_ANDROID) + set(TINYGLTFIMPORTER_TEST_DIR ".") set(ASSIMPIMPORTER_TEST_DIR ".") else() - set(ASSIMPIMPORTER_TEST_DIR "${CMAKE_CURRENT_SOURCE_DIR}") + set(TINYGLTFIMPORTER_TEST_DIR ${PROJECT_SOURCE_DIR}/src/MagnumPlugins/TinyGltfImporter/Test) + set(ASSIMPIMPORTER_TEST_DIR ${CMAKE_CURRENT_SOURCE_DIR}) endif() # CMake before 3.8 has broken $ expressions for iOS (see @@ -43,14 +45,6 @@ if(NOT MAGNUM_ASSIMPIMPORTER_BUILD_STATIC) endif() endif() -# Assimp 5. Of all the things that could break, this version reports itself as -# 4.1. Since some of the insane awful bugs got fixed in version 5, the test has -# to check against the version in order to adjust expectations. The only way I -# could make this work is checking for the getEpsilon() function added in -# https://github.com/assimp/assimp/commit/8b95479bb00b4bf8fb875f2c5b0605ddfd203b7f -# Related bug: https://github.com/assimp/assimp/issues/2693 -try_compile(ASSIMP_IS_VERSION_5 ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/CheckAssimpVersion5.cpp LINK_LIBRARIES Assimp::Assimp) - # First replace ${} variables, then $<> generator expressions configure_file(${CMAKE_CURRENT_SOURCE_DIR}/configure.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/configure.h.in) @@ -61,10 +55,17 @@ corrade_add_test(AssimpImporterTest AssimpImporterTest.cpp LIBRARIES Magnum::Trade FILES empty.dae - empty.gltf + animation.gltf # Same as in TinyGltfImporterTest, but added a scene + animation.bin + animation-no-scene.gltf + animation-patching.bin + animation-patching.gltf camera.dae diffuse_texture.png embedded-texture.blend + exported-animation.dae # Converted from exported-animation.blend + exported-animation.fbx + exported-animation.gltf image-filename-trailing-space.mtl image-filename-trailing-space.obj image-mips.mtl @@ -87,7 +88,12 @@ corrade_add_test(AssimpImporterTest AssimpImporterTest.cpp y-up.dae z-up.dae) target_link_libraries(AssimpImporterTest PRIVATE Assimp::Assimp) -target_include_directories(AssimpImporterTest PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/$) +target_include_directories(AssimpImporterTest PRIVATE + ${CMAKE_CURRENT_BINARY_DIR}/$ + # The test needs access to configureInternal.h written by + # AssimpImporter. The dynamic library doesn't get linked to + # and hence doesn't get the binary dir in the include dirs. + ${PROJECT_BINARY_DIR}/src) if(MAGNUM_ASSIMPIMPORTER_BUILD_STATIC) target_link_libraries(AssimpImporterTest PRIVATE AssimpImporter Magnum::AnyImageImporter) diff --git a/src/MagnumPlugins/AssimpImporter/Test/animation-no-scene.gltf b/src/MagnumPlugins/AssimpImporter/Test/animation-no-scene.gltf new file mode 100644 index 000000000..81af956b7 --- /dev/null +++ b/src/MagnumPlugins/AssimpImporter/Test/animation-no-scene.gltf @@ -0,0 +1 @@ +{"asset":{"version":"2.0"},"animations":[{"name":"empty","channels":[],"samplers":[]},{"name":"TRS animation","channels":[{"sampler":0,"target":{"node":0,"path":"rotation"}},{"sampler":1,"target":{"node":1,"path":"translation"}},{"sampler":2,"target":{"node":2,"path":"scale"}}],"samplers":[{"input":0,"interpolation":"LINEAR","output":1},{"input":2,"interpolation":"STEP","output":3},{"input":2,"interpolation":"LINEAR","output":4}]},{"name":"TRS animation, splines","channels":[{"sampler":0,"target":{"node":3,"path":"rotation"}},{"sampler":1,"target":{"node":4,"path":"translation"}},{"sampler":2,"target":{"node":5,"path":"scale"}}],"samplers":[{"input":5,"interpolation":"CUBICSPLINE","output":7},{"input":5,"interpolation":"CUBICSPLINE","output":8},{"input":5,"interpolation":"CUBICSPLINE","output":9}]}],"accessors":[{"bufferView":0,"byteOffset":0,"componentType":5126,"count":2,"type":"SCALAR"},{"bufferView":1,"byteOffset":0,"componentType":5126,"count":2,"type":"VEC4"},{"bufferView":0,"byteOffset":8,"componentType":5126,"count":4,"type":"SCALAR"},{"bufferView":2,"byteOffset":0,"componentType":5126,"count":4,"type":"VEC3"},{"bufferView":2,"byteOffset":12,"componentType":5126,"count":4,"type":"VEC3"},{"bufferView":3,"byteOffset":0,"componentType":5126,"count":4,"type":"SCALAR"},{"bufferView":3,"byteOffset":16,"componentType":5126,"count":4,"type":"SCALAR"},{"bufferView":4,"byteOffset":0,"componentType":5126,"count":12,"type":"VEC4"},{"bufferView":5,"byteOffset":0,"componentType":5126,"count":12,"type":"VEC3"},{"bufferView":6,"byteOffset":0,"componentType":5126,"count":12,"type":"VEC3"}],"bufferViews":[{"buffer":0,"byteOffset":0,"byteLength":24},{"buffer":0,"byteOffset":24,"byteLength":32},{"buffer":0,"byteOffset":56,"byteLength":96,"byteStride":24},{"buffer":0,"byteOffset":152,"byteLength":32},{"buffer":0,"byteOffset":184,"byteLength":192},{"buffer":0,"byteOffset":376,"byteLength":144},{"buffer":0,"byteOffset":520,"byteLength":144}],"buffers":[{"byteLength":664,"uri":"data:application/octet-stream;base64,AACgPwAAIEAAAAAAAACgPwAAIEAAAHBAAAAAAAAAAAAAAAAAAACAPwAAgD8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIA/AACAPwAAgD8AAAAAAAAgQAAAAAAAAIA/AACAPwAAoEAAAAAAAAAgQAAAAAAAAIA/AACAPwAAwEAAAAAAAAAAAAAAAAAAAIA/AACAPwAAgD8AAAA/AABgQAAAgEAAAKBAMzMzP83MjD8AAEBAAACwQAAAAAAAAAAAAAAAAAAAAAAQs0c/MgPVPGUaGT/tYjo+q6qqvgAAAADNzMw9iYgIPlVV1T/NzMw9vLu7PquqKj5SKTa/mmDIPlIptj5Kz+w+AABAQJqZGT8AAAhCAABgwWZmJkAAAAAAzczMPpqZGUBlGhk/7WI6PjID1TwQs0c/zczMPc3MTD4zM+PAmpnZP2Zmpj8AAAA/AACAPwAAAABSKTY/Uim2vkrP7L6aYMi+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQEDNzMw9AAAgQKuqqr4AAAAAzczMPVVV1T/NzMw9vLu7PgAAAMDNzIw/mpmJwAAAQECamRk/AAAIQmZmJkAAAAAAzczMPgAAwD/NzBxBMzOjwM3MzD3NzEw+MzPjwGZmpj8AAAA/AACAPzMzo0DNzMw9mpnpwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMDNzIw/mpmJwAAAAD/NzMw9VVW1QN7d3T6rqio+q6qqPjMzo0DNzMw9mpnpwAAAAMAAAAAAmpkZP83MTD7NzMw+MzNjwQAAQEDNzMw9AAAgQAAAoECamZk+zcyMP2Zmpj8AAAAAzcxMPgAAwD/NzBxBMzOjwAAAAAAAAAAAAAAAAA=="}],"nodes":[{},{},{},{},{},{}]} \ No newline at end of file diff --git a/src/MagnumPlugins/AssimpImporter/Test/animation-patching.bin b/src/MagnumPlugins/AssimpImporter/Test/animation-patching.bin new file mode 100644 index 000000000..a8a20ee74 Binary files /dev/null and b/src/MagnumPlugins/AssimpImporter/Test/animation-patching.bin differ diff --git a/src/MagnumPlugins/AssimpImporter/Test/animation-patching.gltf b/src/MagnumPlugins/AssimpImporter/Test/animation-patching.gltf new file mode 100644 index 000000000..2e823800d --- /dev/null +++ b/src/MagnumPlugins/AssimpImporter/Test/animation-patching.gltf @@ -0,0 +1,97 @@ +{ + "asset": { + "version": "2.0" + }, + "animations": [ + { + "name": "Quaternion shortest-path patching", + "channels": [ + { + "sampler": 0, + "target": { + "node": 0, + "path": "rotation" + } + } + ], + "samplers": [ + { + "input": 0, + "interpolation": "LINEAR", + "output": 1 + } + ] + }, + { + "name": "Quaternion normalization patching", + "channels": [ + { + "sampler": 0, + "target": { + "node": 0, + "path": "rotation" + } + } + ], + "samplers": [ + { + "input": 0, + "interpolation": "LINEAR", + "output": 2 + } + ] + } + ], + "accessors": [ + { + "bufferView": 0, + "byteOffset": 0, + "componentType": 5126, + "count": 9, + "type": "SCALAR" + }, + { + "bufferView": 1, + "byteOffset": 0, + "componentType": 5126, + "count": 9, + "type": "VEC4" + }, + { + "bufferView": 1, + "byteOffset": 144, + "componentType": 5126, + "count": 3, + "type": "VEC4" + } + ], + "bufferViews": [ + { + "buffer": 0, + "byteOffset": 0, + "byteLength": 36 + }, + { + "buffer": 0, + "byteOffset": 36, + "byteLength": 192 + } + ], + "buffers": [ + { + "byteLength": 228, + "uri": "animation-patching.bin" + } + ], + "nodes": [ + {} + ], + "scenes": [ + { + "nodes": [ + 0 + ] + } + ], + "scene": 0 +} diff --git a/src/MagnumPlugins/AssimpImporter/Test/animation.bin b/src/MagnumPlugins/AssimpImporter/Test/animation.bin new file mode 100644 index 000000000..463bae1af Binary files /dev/null and b/src/MagnumPlugins/AssimpImporter/Test/animation.bin differ diff --git a/src/MagnumPlugins/AssimpImporter/Test/animation.gltf b/src/MagnumPlugins/AssimpImporter/Test/animation.gltf new file mode 100644 index 000000000..c477645bb --- /dev/null +++ b/src/MagnumPlugins/AssimpImporter/Test/animation.gltf @@ -0,0 +1,281 @@ +{ + "asset": { + "version": "2.0" + }, + "animations": [ + { + "name": "empty", + "channels": [], + "samplers": [] + }, + { + "name": "TRS animation", + "channels": [ + { + "sampler": 0, + "target": { + "node": 0, + "path": "rotation" + } + }, + { + "sampler": 1, + "target": { + "node": 1, + "path": "translation" + } + }, + { + "sampler": 2, + "target": { + "node": 2, + "path": "scale" + } + } + ], + "samplers": [ + { + "input": 0, + "interpolation": "LINEAR", + "output": 1 + }, + { + "input": 2, + "interpolation": "STEP", + "output": 3 + }, + { + "input": 2, + "interpolation": "LINEAR", + "output": 4 + } + ] + }, + { + "name": "TRS animation, splines", + "channels": [ + { + "sampler": 0, + "target": { + "node": 3, + "path": "rotation" + } + }, + { + "sampler": 1, + "target": { + "node": 4, + "path": "translation" + } + }, + { + "sampler": 2, + "target": { + "node": 5, + "path": "scale" + } + } + ], + "samplers": [ + { + "input": 5, + "interpolation": "CUBICSPLINE", + "output": 7 + }, + { + "input": 5, + "interpolation": "CUBICSPLINE", + "output": 8 + }, + { + "input": 5, + "interpolation": "CUBICSPLINE", + "output": 9 + } + ] + } + ], + "accessors": [ + { + "bufferView": 0, + "byteOffset": 0, + "componentType": 5126, + "count": 2, + "type": "SCALAR" + }, + { + "bufferView": 1, + "byteOffset": 0, + "componentType": 5126, + "count": 2, + "type": "VEC4" + }, + { + "bufferView": 0, + "byteOffset": 8, + "componentType": 5126, + "count": 4, + "type": "SCALAR" + }, + { + "bufferView": 2, + "byteOffset": 0, + "componentType": 5126, + "count": 4, + "type": "VEC3" + }, + { + "bufferView": 2, + "byteOffset": 12, + "componentType": 5126, + "count": 4, + "type": "VEC3" + }, + { + "bufferView": 3, + "byteOffset": 0, + "componentType": 5126, + "count": 4, + "type": "SCALAR" + }, + { + "bufferView": 3, + "byteOffset": 16, + "componentType": 5126, + "count": 4, + "type": "SCALAR" + }, + { + "bufferView": 4, + "byteOffset": 0, + "componentType": 5126, + "count": 12, + "type": "VEC4" + }, + { + "bufferView": 5, + "byteOffset": 0, + "componentType": 5126, + "count": 12, + "type": "VEC3" + }, + { + "bufferView": 6, + "byteOffset": 0, + "componentType": 5126, + "count": 12, + "type": "VEC3" + } + ], + "bufferViews": [ + { + "buffer": 0, + "byteOffset": 0, + "byteLength": 24 + }, + { + "buffer": 0, + "byteOffset": 24, + "byteLength": 32 + }, + { + "buffer": 0, + "byteOffset": 56, + "byteLength": 96, + "byteStride": 24 + }, + { + "buffer": 0, + "byteOffset": 152, + "byteLength": 32 + }, + { + "buffer": 0, + "byteOffset": 184, + "byteLength": 192 + }, + { + "buffer": 0, + "byteOffset": 376, + "byteLength": 144 + }, + { + "buffer": 0, + "byteOffset": 520, + "byteLength": 144 + } + ], + "buffers": [ + { + "byteLength": 664, + "uri": "animation.bin" + } + ], + "nodes": [ + { + "rotation": [ + 0, + 0, + 0, + 1 + ], + "scale": [ + 1, + 2, + 3 + ], + "translation": [ + -4, + -5, + 6 + ] + }, + { + "rotation": [ + 0, + 0, + 0, + 1 + ], + "scale": [ + 1, + 2, + 3 + ], + "translation": [ + -4, + -5, + 6 + ] + }, + { + "rotation": [ + 0, + 0, + 0, + 1 + ], + "scale": [ + 1, + 2, + 3 + ], + "translation": [ + -4, + -5, + 6 + ] + }, + {}, + {}, + {} + ], + "scenes": [ + { + "nodes": [ + 0, 1, 2, 3, 4, 5 + ] + } + ], + "scene": 0 +} diff --git a/src/MagnumPlugins/AssimpImporter/Test/configure.h.cmake b/src/MagnumPlugins/AssimpImporter/Test/configure.h.cmake index 03cfae95d..1bf33c38a 100644 --- a/src/MagnumPlugins/AssimpImporter/Test/configure.h.cmake +++ b/src/MagnumPlugins/AssimpImporter/Test/configure.h.cmake @@ -26,5 +26,5 @@ #cmakedefine ASSIMPIMPORTER_PLUGIN_FILENAME "${ASSIMPIMPORTER_PLUGIN_FILENAME}" #cmakedefine DDSIMPORTER_PLUGIN_FILENAME "${DDSIMPORTER_PLUGIN_FILENAME}" #cmakedefine STBIMAGEIMPORTER_PLUGIN_FILENAME "${STBIMAGEIMPORTER_PLUGIN_FILENAME}" +#define TINYGLTFIMPORTER_TEST_DIR "${TINYGLTFIMPORTER_TEST_DIR}" #define ASSIMPIMPORTER_TEST_DIR "${ASSIMPIMPORTER_TEST_DIR}" -#cmakedefine01 ASSIMP_IS_VERSION_5 diff --git a/src/MagnumPlugins/AssimpImporter/Test/empty.gltf b/src/MagnumPlugins/AssimpImporter/Test/empty.gltf deleted file mode 100644 index 49aed606b..000000000 --- a/src/MagnumPlugins/AssimpImporter/Test/empty.gltf +++ /dev/null @@ -1,5 +0,0 @@ -{ - "asset": { - "version": "2.0" - } -} diff --git a/src/MagnumPlugins/AssimpImporter/Test/exported-animation.blend b/src/MagnumPlugins/AssimpImporter/Test/exported-animation.blend new file mode 100644 index 000000000..0f61e71e8 Binary files /dev/null and b/src/MagnumPlugins/AssimpImporter/Test/exported-animation.blend differ diff --git a/src/MagnumPlugins/AssimpImporter/Test/exported-animation.dae b/src/MagnumPlugins/AssimpImporter/Test/exported-animation.dae new file mode 100644 index 000000000..6a8ae53bf --- /dev/null +++ b/src/MagnumPlugins/AssimpImporter/Test/exported-animation.dae @@ -0,0 +1,437 @@ + + + + + Blender User + Blender 2.93.0 commit date:2021-06-02, commit time:11:21, hash:84da05a8b806 + + 2021-06-27T21:10:49 + 2021-06-27T21:10:49 + + Z_UP + + + + + + + -1 -1 -1 -1 -1 1 -1 1 -1 -1 1 1 1 -1 -1 1 -1 1 1 1 -1 1 1 1 + + + + + + + + + + -1 0 0 0 1 0 1 0 0 0 -1 0 0 0 -1 0 0 1 + + + + + + + + + + 0.625 0 0.375 0.25 0.375 0 0.625 0.25 0.375 0.5 0.375 0.25 0.625 0.5 0.375 0.75 0.375 0.5 0.625 0.75 0.375 1 0.375 0.75 0.375 0.5 0.125 0.75 0.125 0.5 0.875 0.5 0.625 0.75 0.625 0.5 0.625 0 0.625 0.25 0.375 0.25 0.625 0.25 0.625 0.5 0.375 0.5 0.625 0.5 0.625 0.75 0.375 0.75 0.625 0.75 0.625 1 0.375 1 0.375 0.5 0.375 0.75 0.125 0.75 0.875 0.5 0.875 0.75 0.625 0.75 + + + + + + + + + + + + + + +

1 0 0 2 0 1 0 0 2 3 1 3 6 1 4 2 1 5 7 2 6 4 2 7 6 2 8 5 3 9 0 3 10 4 3 11 6 4 12 0 4 13 2 4 14 3 5 15 5 5 16 7 5 17 1 0 18 3 0 19 2 0 20 3 1 21 7 1 22 6 1 23 7 2 24 5 2 25 4 2 26 5 3 27 1 3 28 0 3 29 6 4 30 4 4 31 0 4 32 3 5 33 1 5 34 5 5 35

+
+
+
+ + + + -1 -1 -1 -1 -1 1 -1 1 -1 -1 1 1 1 -1 -1 1 -1 1 1 1 -1 1 1 1 + + + + + + + + + + -1 0 0 0 1 0 1 0 0 0 -1 0 0 0 -1 0 0 1 + + + + + + + + + + 0.625 0 0.375 0.25 0.375 0 0.625 0.25 0.375 0.5 0.375 0.25 0.625 0.5 0.375 0.75 0.375 0.5 0.625 0.75 0.375 1 0.375 0.75 0.375 0.5 0.125 0.75 0.125 0.5 0.875 0.5 0.625 0.75 0.625 0.5 0.625 0 0.625 0.25 0.375 0.25 0.625 0.25 0.625 0.5 0.375 0.5 0.625 0.5 0.625 0.75 0.375 0.75 0.625 0.75 0.625 1 0.375 1 0.375 0.5 0.375 0.75 0.125 0.75 0.875 0.5 0.875 0.75 0.625 0.75 + + + + + + + + + + + + + + +

1 0 0 2 0 1 0 0 2 3 1 3 6 1 4 2 1 5 7 2 6 4 2 7 6 2 8 5 3 9 0 3 10 4 3 11 6 4 12 0 4 13 2 4 14 3 5 15 5 5 16 7 5 17 1 0 18 3 0 19 2 0 20 3 1 21 7 1 22 6 1 23 7 2 24 5 2 25 4 2 26 5 3 27 1 3 28 0 3 29 6 4 30 4 4 31 0 4 32 3 5 33 1 5 34 5 5 35

+
+
+
+ + + + -1 -1 -1 -1 -1 1 -1 1 -1 -1 1 1 1 -1 -1 1 -1 1 1 1 -1 1 1 1 + + + + + + + + + + -1 0 0 0 1 0 1 0 0 0 -1 0 0 0 -1 0 0 1 + + + + + + + + + + 0.625 0 0.375 0.25 0.375 0 0.625 0.25 0.375 0.5 0.375 0.25 0.625 0.5 0.375 0.75 0.375 0.5 0.625 0.75 0.375 1 0.375 0.75 0.375 0.5 0.125 0.75 0.125 0.5 0.875 0.5 0.625 0.75 0.625 0.5 0.625 0 0.625 0.25 0.375 0.25 0.625 0.25 0.625 0.5 0.375 0.5 0.625 0.5 0.625 0.75 0.375 0.75 0.625 0.75 0.625 1 0.375 1 0.375 0.5 0.375 0.75 0.125 0.75 0.875 0.5 0.875 0.75 0.625 0.75 + + + + + + + + + + + + + + +

1 0 0 2 0 1 0 0 2 3 1 3 6 1 4 2 1 5 7 2 6 4 2 7 6 2 8 5 3 9 0 3 10 4 3 11 6 4 12 0 4 13 2 4 14 3 5 15 5 5 16 7 5 17 1 0 18 3 0 19 2 0 20 3 1 21 7 1 22 6 1 23 7 2 24 5 2 25 4 2 26 5 3 27 1 3 28 0 3 29 6 4 30 4 4 31 0 4 32 3 5 33 1 5 34 5 5 35

+
+
+
+
+ + + + + 0 0.04166662 0.08333331 0.125 0.1666666 0.2083333 0.25 0.2916666 0.3333333 0.375 0.4166666 0.4583333 0.5 0.5416667 0.5833333 0.625 0.6666667 0.7083333 0.75 0.7916667 0.8333333 0.875 0.9166667 0.9583333 1 1.041667 1.083333 1.125 1.166667 1.208333 1.25 1.291667 1.333333 1.375 1.416667 1.458333 1.5 1.541667 1.583333 1.625 1.666667 1.708333 1.75 1.791667 1.833333 1.875 1.916667 1.958333 2 2.041667 2.083333 2.125 2.166667 2.208333 2.25 2.291667 2.333333 2.375 2.416667 2.458333 2.5 2.541667 2.583333 2.625 2.666667 2.708333 2.75 2.791667 2.833333 2.875 2.916667 2.958333 3 3.041667 3.083333 3.125 3.166667 3.208333 3.25 3.291667 3.333333 3.375 3.416667 3.458333 3.5 3.541667 3.583333 3.625 3.666667 3.708333 3.75 3.791667 3.833333 3.875 3.916667 3.958333 4 4.041666 4.083333 4.125 4.166666 4.208333 4.25 4.291666 4.333333 4.375 4.416666 4.458333 4.5 4.541666 4.583333 4.625 4.666666 4.708333 4.75 4.791666 4.833333 4.875 4.916666 4.958333 5 5.041666 5.083333 5.125 5.166666 5.208333 5.25 5.291666 5.333333 5.375 5.416666 5.458333 5.5 5.541666 5.583333 5.625 5.666666 5.708333 5.75 5.791666 5.833333 5.875 5.916666 5.958333 6 6.041666 6.083333 6.125 6.166666 6.208333 6.25 6.291666 6.333333 6.375 6.416666 6.458333 6.5 6.541666 6.583333 6.625 6.666666 6.708333 6.75 6.791666 6.833333 6.875 6.916666 6.958333 7 7.041666 7.083333 7.125 7.166666 7.208333 7.25 7.291666 7.333333 7.375 7.416666 7.458333 7.5 + + + + + + + + 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1.12757e-5 3.52696e-5 -1.06829e-4 2.00263e-4 3.00609e-4 -2.70703e-4 -6.44047e-4 8.09974e-4 -7.94801e-4 9.88267e-4 -4.6596e-5 -8.23317e-4 -0.001708984 -0.002333283 0.001665174 0.002335548 -0.001499176 -0.002713859 0.001005887 8.21775e-4 -0.00440669 -0.002943873 -0.003749966 -0.00250107 0.009544551 -0.006051063 -0.005689024 -0.0014171 0.008366465 -0.01375323 0.003897249 -0.01233822 -0.006401419 0.01445537 -0.02451014 0.00194478 -0.001159012 -0.01313722 0.00547719 -0.006664872 0.01335299 -0.01275253 -0.01383519 0.00973469 0.00466609 0.002507209 0.01457923 4.50887e-4 0.01174861 0.01563465 -0.007085084 -0.005193293 -0.001585364 -0.006084084 -0.003864407 -0.0193513 -0.005607426 0.001835048 3.41592e-4 -0.01256364 0.02819061 0.1385139 0.3171014 0.5794721 0.8968456 1.278944 1.679311 2.175644 2.719395 3.319481 3.979621 4.688361 5.425393 6.186198 7.031567 7.89382 8.812631 9.712822 10.67551 11.67646 12.68361 13.69823 14.77877 15.81732 16.93447 18.01933 19.13528 20.24976 21.36608 22.50669 23.63151 24.75282 25.86339 26.97742 28.08011 29.15177 30.22353 31.28487 32.30588 33.33785 34.31761 35.31719 36.20272 37.1124 37.97687 38.78798 39.57774 40.32164 41.03234 41.68122 42.25549 42.80435 43.30276 43.76669 44.10105 44.41888 44.67058 44.87212 44.95766 45.02122 + + + + + + + + LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR + + + + + + + + + + + + + + + + 0 0.04166662 0.08333331 0.125 0.1666666 0.2083333 0.25 0.2916666 0.3333333 0.375 0.4166666 0.4583333 0.5 0.5416667 0.5833333 0.625 0.6666667 0.7083333 0.75 0.7916667 0.8333333 0.875 0.9166667 0.9583333 1 1.041667 1.083333 1.125 1.166667 1.208333 1.25 1.291667 1.333333 1.375 1.416667 1.458333 1.5 1.541667 1.583333 1.625 1.666667 1.708333 1.75 1.791667 1.833333 1.875 1.916667 1.958333 2 2.041667 2.083333 2.125 2.166667 2.208333 2.25 2.291667 2.333333 2.375 2.416667 2.458333 2.5 2.541667 2.583333 2.625 2.666667 2.708333 2.75 2.791667 2.833333 2.875 2.916667 2.958333 3 3.041667 3.083333 3.125 3.166667 3.208333 3.25 3.291667 3.333333 3.375 3.416667 3.458333 3.5 3.541667 3.583333 3.625 3.666667 3.708333 3.75 3.791667 3.833333 3.875 3.916667 3.958333 4 4.041666 4.083333 4.125 4.166666 4.208333 4.25 4.291666 4.333333 4.375 4.416666 4.458333 4.5 4.541666 4.583333 4.625 4.666666 4.708333 4.75 4.791666 4.833333 4.875 4.916666 4.958333 5 5.041666 5.083333 5.125 5.166666 5.208333 5.25 5.291666 5.333333 5.375 5.416666 5.458333 5.5 5.541666 5.583333 5.625 5.666666 5.708333 5.75 5.791666 5.833333 5.875 5.916666 5.958333 6 6.041666 6.083333 6.125 6.166666 6.208333 6.25 6.291666 6.333333 6.375 6.416666 6.458333 6.5 6.541666 6.583333 6.625 6.666666 6.708333 6.75 6.791666 6.833333 6.875 6.916666 6.958333 7 7.041666 7.083333 7.125 7.166666 7.208333 7.25 7.291666 7.333333 7.375 7.416666 7.458333 7.5 + + + + + + + + 0 0.07415217 0.293276 0.6523724 1.146443 1.770487 2.519507 3.388504 4.372477 5.466431 6.665362 7.964274 9.358171 10.84204 12.4109 14.05975 15.78358 17.57739 19.4362 21.35499 23.32877 25.35253 27.42129 29.53005 31.6738 33.84754 36.04627 38.26501 40.49873 42.74246 44.99119 47.23992 49.48365 51.71738 53.93611 56.13484 58.30858 60.45232 62.56109 64.62983 66.65363 68.6274 70.54618 72.40499 74.19882 75.92265 77.57147 79.14035 80.62422 82.01812 83.31703 84.51598 85.60992 86.5939 87.46289 88.21191 88.83594 89.33001 89.68912 89.90824 89.98239 89.98238 89.98239 89.98239 89.98238 89.98239 89.9824 89.98238 89.98239 89.98239 89.98239 89.98239 89.98239 89.98239 89.98239 89.98239 89.98239 89.98239 89.9824 89.98239 89.98239 89.98239 89.98239 89.98239 89.98239 89.98239 89.98239 89.98239 89.98239 89.98239 89.98239 89.98239 89.98239 89.98238 89.98239 89.98239 89.98238 89.98238 89.98239 89.98239 89.98239 89.98239 89.98239 89.98239 89.98238 89.98239 89.98239 89.98238 89.98239 89.98239 89.98239 89.98239 89.98239 89.98239 89.98239 89.98239 89.98239 89.98239 89.98239 89.98239 89.98239 89.98239 89.98238 89.98238 89.98239 89.98239 89.98239 89.98238 89.98238 89.98238 89.98239 89.98239 89.98239 89.98239 89.98238 89.98239 89.98238 89.98239 89.98239 89.98239 89.98239 89.98239 89.98239 89.98239 89.98238 89.98239 89.98239 89.98239 89.98239 89.98239 89.98239 89.98239 89.98239 89.98239 89.98239 89.98239 89.98239 89.98239 89.98239 89.98239 89.98239 89.98239 89.98239 89.98239 89.98239 89.98239 89.98239 89.98239 89.98239 89.98239 89.98239 89.98239 89.98239 89.98239 89.98239 89.98239 89.98239 89.98239 89.98239 89.98239 89.98239 + + + + + + + + LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR + + + + + + + + + + + + + + + + 0 0.04166662 0.08333331 0.125 0.1666666 0.2083333 0.25 0.2916666 0.3333333 0.375 0.4166666 0.4583333 0.5 0.5416667 0.5833333 0.625 0.6666667 0.7083333 0.75 0.7916667 0.8333333 0.875 0.9166667 0.9583333 1 1.041667 1.083333 1.125 1.166667 1.208333 1.25 1.291667 1.333333 1.375 1.416667 1.458333 1.5 1.541667 1.583333 1.625 1.666667 1.708333 1.75 1.791667 1.833333 1.875 1.916667 1.958333 2 2.041667 2.083333 2.125 2.166667 2.208333 2.25 2.291667 2.333333 2.375 2.416667 2.458333 2.5 2.541667 2.583333 2.625 2.666667 2.708333 2.75 2.791667 2.833333 2.875 2.916667 2.958333 3 3.041667 3.083333 3.125 3.166667 3.208333 3.25 3.291667 3.333333 3.375 3.416667 3.458333 3.5 3.541667 3.583333 3.625 3.666667 3.708333 3.75 3.791667 3.833333 3.875 3.916667 3.958333 4 4.041666 4.083333 4.125 4.166666 4.208333 4.25 4.291666 4.333333 4.375 4.416666 4.458333 4.5 4.541666 4.583333 4.625 4.666666 4.708333 4.75 4.791666 4.833333 4.875 4.916666 4.958333 5 5.041666 5.083333 5.125 5.166666 5.208333 5.25 5.291666 5.333333 5.375 5.416666 5.458333 5.5 5.541666 5.583333 5.625 5.666666 5.708333 5.75 5.791666 5.833333 5.875 5.916666 5.958333 6 6.041666 6.083333 6.125 6.166666 6.208333 6.25 6.291666 6.333333 6.375 6.416666 6.458333 6.5 6.541666 6.583333 6.625 6.666666 6.708333 6.75 6.791666 6.833333 6.875 6.916666 6.958333 7 7.041666 7.083333 7.125 7.166666 7.208333 7.25 7.291666 7.333333 7.375 7.416666 7.458333 7.5 + + + + + + + + 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -0.07413142 -0.2932618 -0.6524791 -1.146173 -1.770666 -2.520709 -3.388543 -4.372961 -5.467864 -6.665779 -7.96844 -9.362901 -10.8439 -12.41277 -14.05775 -15.78061 -17.57712 -19.44111 -21.36097 -23.32804 -25.35841 -27.42397 -29.54372 -31.68508 -33.84848 -36.04831 -38.27301 -40.51455 -42.74691 -45.01224 -47.22633 -49.4923 -51.72329 -53.91454 -56.17096 -58.30061 -60.46962 -62.57535 -64.61927 -66.66268 -68.64148 -70.5687 -72.42087 -74.17724 -75.94079 -77.56175 -79.14631 -80.6141 -82.00772 -83.32174 -84.52201 -85.61045 -86.59677 -87.46883 -88.24846 -88.87812 -89.33636 -89.67314 -89.9391 -90.0008 -89.99021 -89.99715 -89.9868 -89.96733 -89.97451 -89.98069 -90.01648 -90.02372 -89.97389 -89.98189 -89.99822 -89.96888 -89.96631 -90.02326 -89.97745 -90.00614 -89.98288 -90.02886 -89.9727 -89.98674 -89.98052 -89.99629 -89.98717 -90.03237 -89.97457 -89.986 -89.99273 -90.01967 -89.99945 -89.97858 -89.98007 -89.97471 -90.01986 -89.9764 -90.01325 -89.98709 -89.98157 -89.97006 -89.99793 -89.98364 -89.96604 -89.97495 -89.99713 -89.97775 -89.96984 -89.97912 -89.97496 -89.9839 -89.98706 -90.01616 -89.98995 -90.01847 -90.00894 -89.96981 -89.98393 -89.99552 -89.97649 -89.97789 -89.98429 -89.96865 + + + + + + + + LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR + + + + + + + + + + + + + + + + + + 0 0.04166662 0.08333331 0.125 0.1666666 0.2083333 0.25 0.2916666 0.3333333 0.375 0.4166666 0.4583333 0.5 0.5416667 0.5833333 0.625 0.6666667 0.7083333 0.75 0.7916667 0.8333333 0.875 0.9166667 0.9583333 1 1.041667 1.083333 1.125 1.166667 1.208333 1.25 1.291667 1.333333 1.375 1.416667 1.458333 1.5 1.541667 1.583333 1.625 1.666667 1.708333 1.75 1.791667 1.833333 1.875 1.916667 1.958333 2 2.041667 2.083333 2.125 2.166667 2.208333 2.25 2.291667 2.333333 2.375 2.416667 2.458333 2.5 2.541667 2.583333 2.625 2.666667 2.708333 2.75 2.791667 2.833333 2.875 2.916667 2.958333 3 3.041667 3.083333 3.125 3.166667 3.208333 3.25 3.291667 3.333333 3.375 3.416667 3.458333 3.5 3.541667 3.583333 3.625 3.666667 3.708333 3.75 3.791667 3.833333 3.875 3.916667 3.958333 4 4.041666 4.083333 4.125 4.166666 4.208333 4.25 4.291666 4.333333 4.375 4.416666 4.458333 4.5 4.541666 4.583333 4.625 4.666666 4.708333 4.75 4.791666 4.833333 4.875 4.916666 4.958333 5 5.041666 5.083333 5.125 5.166666 5.208333 5.25 5.291666 5.333333 5.375 5.416666 5.458333 5.5 5.541666 5.583333 5.625 5.666666 5.708333 5.75 5.791666 5.833333 5.875 5.916666 5.958333 6 6.041666 6.083333 6.125 6.166666 6.208333 6.25 6.291666 6.333333 6.375 6.416666 6.458333 6.5 6.541666 6.583333 6.625 6.666666 6.708333 6.75 6.791666 6.833333 6.875 6.916666 6.958333 7 7.041666 7.083333 7.125 7.166666 7.208333 7.25 7.291666 7.333333 7.375 7.416666 7.458333 7.5 + + + + + + + + 1 1.003296 1.013037 1.029 1.050963 1.078704 1.112 1.15063 1.19437 1.243 1.296296 1.354037 1.416 1.481963 1.551704 1.625 1.70163 1.78137 1.864 1.949296 2.037037 2.127 2.218963 2.312704 2.408 2.50463 2.60237 2.701 2.800296 2.900037 3 3.099963 3.199704 3.299 3.39763 3.49537 3.592 3.687296 3.781037 3.873 3.962963 4.050704 4.136 4.21863 4.29837 4.375 4.448297 4.518037 4.584 4.645963 4.703704 4.757 4.80563 4.84937 4.888 4.921297 4.949037 4.971 4.986963 4.996704 5 4.997113 4.988574 4.974563 4.955259 4.930845 4.9015 4.867405 4.828741 4.785687 4.738426 4.687137 4.632 4.573197 4.510907 4.445312 4.376592 4.304928 4.2305 4.153489 4.074074 3.992437 3.908759 3.82322 3.736 3.64728 3.55724 3.466063 3.373926 3.281012 3.1875 3.093572 2.999407 2.905188 2.811093 2.717303 2.624 2.531363 2.439574 2.348813 2.259259 2.171095 2.0845 1.999655 1.916741 1.835937 1.757426 1.681386 1.608 1.537446 1.469907 1.405562 1.344592 1.287178 1.2335 1.183738 1.138074 1.096688 1.05976 1.02747 1 0.9754144 0.9516481 0.9286875 0.9065185 0.8851273 0.8645 0.8446227 0.8254814 0.8070625 0.7893519 0.7723357 0.756 0.7403311 0.7253148 0.7109375 0.6971852 0.684044 0.6715 0.6595394 0.6481482 0.6373125 0.6270185 0.6172524 0.608 0.5992476 0.5909816 0.5831875 0.5758519 0.5689606 0.5625 0.556456 0.5508148 0.5455625 0.5406852 0.5361689 0.532 0.5281644 0.5246481 0.5214375 0.5185186 0.5158774 0.5135 0.5113728 0.5094814 0.5078125 0.5063518 0.5050856 0.504 0.503081 0.5023148 0.5016874 0.5011853 0.5007939 0.5005001 0.5002894 0.5001481 0.5000626 0.5000185 0.5000023 0.5 + + + + + + + + LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR + + + + + + + + + + + + + + + + 0 0.04166662 0.08333331 0.125 0.1666666 0.2083333 0.25 0.2916666 0.3333333 0.375 0.4166666 0.4583333 0.5 0.5416667 0.5833333 0.625 0.6666667 0.7083333 0.75 0.7916667 0.8333333 0.875 0.9166667 0.9583333 1 1.041667 1.083333 1.125 1.166667 1.208333 1.25 1.291667 1.333333 1.375 1.416667 1.458333 1.5 1.541667 1.583333 1.625 1.666667 1.708333 1.75 1.791667 1.833333 1.875 1.916667 1.958333 2 2.041667 2.083333 2.125 2.166667 2.208333 2.25 2.291667 2.333333 2.375 2.416667 2.458333 2.5 2.541667 2.583333 2.625 2.666667 2.708333 2.75 2.791667 2.833333 2.875 2.916667 2.958333 3 3.041667 3.083333 3.125 3.166667 3.208333 3.25 3.291667 3.333333 3.375 3.416667 3.458333 3.5 3.541667 3.583333 3.625 3.666667 3.708333 3.75 3.791667 3.833333 3.875 3.916667 3.958333 4 4.041666 4.083333 4.125 4.166666 4.208333 4.25 4.291666 4.333333 4.375 4.416666 4.458333 4.5 4.541666 4.583333 4.625 4.666666 4.708333 4.75 4.791666 4.833333 4.875 4.916666 4.958333 5 5.041666 5.083333 5.125 5.166666 5.208333 5.25 5.291666 5.333333 5.375 5.416666 5.458333 5.5 5.541666 5.583333 5.625 5.666666 5.708333 5.75 5.791666 5.833333 5.875 5.916666 5.958333 6 6.041666 6.083333 6.125 6.166666 6.208333 6.25 6.291666 6.333333 6.375 6.416666 6.458333 6.5 6.541666 6.583333 6.625 6.666666 6.708333 6.75 6.791666 6.833333 6.875 6.916666 6.958333 7 7.041666 7.083333 7.125 7.166666 7.208333 7.25 7.291666 7.333333 7.375 7.416666 7.458333 7.5 + + + + + + + + 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0.999588 0.9983704 0.996375 0.9936296 0.990162 0.986 0.9811713 0.9757037 0.969625 0.9629629 0.9557454 0.948 0.9397546 0.9310371 0.921875 0.9122963 0.9023287 0.892 0.881338 0.8703703 0.859125 0.8476297 0.8359121 0.824 0.8119214 0.7997037 0.787375 0.774963 0.7624953 0.75 0.7375046 0.725037 0.712625 0.7002963 0.6880788 0.676 0.664088 0.6523704 0.640875 0.6296296 0.618662 0.608 0.5976713 0.5877037 0.578125 0.568963 0.5602454 0.552 0.5442547 0.537037 0.530375 0.5242962 0.5188287 0.514 0.5098379 0.5063704 0.503625 0.5016297 0.5004121 0.5 + + + + + + + + LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR + + + + + + + + + + + + + + + + 0 0.04166662 0.08333331 0.125 0.1666666 0.2083333 0.25 0.2916666 0.3333333 0.375 0.4166666 0.4583333 0.5 0.5416667 0.5833333 0.625 0.6666667 0.7083333 0.75 0.7916667 0.8333333 0.875 0.9166667 0.9583333 1 1.041667 1.083333 1.125 1.166667 1.208333 1.25 1.291667 1.333333 1.375 1.416667 1.458333 1.5 1.541667 1.583333 1.625 1.666667 1.708333 1.75 1.791667 1.833333 1.875 1.916667 1.958333 2 2.041667 2.083333 2.125 2.166667 2.208333 2.25 2.291667 2.333333 2.375 2.416667 2.458333 2.5 2.541667 2.583333 2.625 2.666667 2.708333 2.75 2.791667 2.833333 2.875 2.916667 2.958333 3 3.041667 3.083333 3.125 3.166667 3.208333 3.25 3.291667 3.333333 3.375 3.416667 3.458333 3.5 3.541667 3.583333 3.625 3.666667 3.708333 3.75 3.791667 3.833333 3.875 3.916667 3.958333 4 4.041666 4.083333 4.125 4.166666 4.208333 4.25 4.291666 4.333333 4.375 4.416666 4.458333 4.5 4.541666 4.583333 4.625 4.666666 4.708333 4.75 4.791666 4.833333 4.875 4.916666 4.958333 5 5.041666 5.083333 5.125 5.166666 5.208333 5.25 5.291666 5.333333 5.375 5.416666 5.458333 5.5 5.541666 5.583333 5.625 5.666666 5.708333 5.75 5.791666 5.833333 5.875 5.916666 5.958333 6 6.041666 6.083333 6.125 6.166666 6.208333 6.25 6.291666 6.333333 6.375 6.416666 6.458333 6.5 6.541666 6.583333 6.625 6.666666 6.708333 6.75 6.791666 6.833333 6.875 6.916666 6.958333 7 7.041666 7.083333 7.125 7.166666 7.208333 7.25 7.291666 7.333333 7.375 7.416666 7.458333 7.5 + + + + + + + + 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1.003296 1.013037 1.029 1.050963 1.078704 1.112 1.15063 1.19437 1.243 1.296296 1.354037 1.416 1.481963 1.551704 1.625 1.70163 1.78137 1.864 1.949296 2.037037 2.127 2.218963 2.312704 2.408 2.50463 2.60237 2.701 2.800296 2.900037 3 3.099963 3.199704 3.299 3.39763 3.49537 3.592 3.687296 3.781037 3.873 3.962963 4.050704 4.136 4.21863 4.29837 4.375 4.448297 4.518037 4.584 4.645963 4.703704 4.757 4.80563 4.84937 4.888 4.921297 4.949037 4.971 4.986963 4.996704 5 4.996292 4.985333 4.967375 4.942667 4.911458 4.874 4.830542 4.781333 4.726625 4.666666 4.601708 4.532 4.457792 4.379333 4.296875 4.210667 4.120958 4.028 3.932042 3.833333 3.732125 3.628667 3.523208 3.416 3.307291 3.197333 3.086375 2.974667 2.862458 2.75 2.637542 2.525333 2.413625 2.302667 2.192708 2.084 1.976792 1.871333 1.767876 1.666666 1.567958 1.472 1.379042 1.289333 1.203125 1.120667 1.042209 0.9679999 0.8982916 0.833333 0.7733746 0.7186666 0.6694579 0.6259999 0.588542 0.557333 0.5326257 0.5146666 0.5037088 0.5 + + + + + + + + LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR + + + + + + + + + + + + + + + + + + 0 0.04166662 0.08333331 0.125 0.1666666 0.2083333 0.25 0.2916666 0.3333333 0.375 0.4166666 0.4583333 0.5 0.5416667 0.5833333 0.625 0.6666667 0.7083333 0.75 0.7916667 0.8333333 0.875 0.9166667 0.9583333 1 1.041667 1.083333 1.125 1.166667 1.208333 1.25 1.291667 1.333333 1.375 1.416667 1.458333 1.5 1.541667 1.583333 1.625 1.666667 1.708333 1.75 1.791667 1.833333 1.875 1.916667 1.958333 2 2.041667 2.083333 2.125 2.166667 2.208333 2.25 2.291667 2.333333 2.375 2.416667 2.458333 2.5 2.541667 2.583333 2.625 2.666667 2.708333 2.75 2.791667 2.833333 2.875 2.916667 2.958333 3 3.041667 3.083333 3.125 3.166667 3.208333 3.25 3.291667 3.333333 3.375 3.416667 3.458333 3.5 3.541667 3.583333 3.625 3.666667 3.708333 3.75 3.791667 3.833333 3.875 3.916667 3.958333 4 4.041666 4.083333 4.125 4.166666 4.208333 4.25 4.291666 4.333333 4.375 4.416666 4.458333 4.5 4.541666 4.583333 4.625 4.666666 4.708333 4.75 4.791666 4.833333 4.875 4.916666 4.958333 5 5.041666 5.083333 5.125 5.166666 5.208333 5.25 5.291666 5.333333 5.375 5.416666 5.458333 5.5 5.541666 5.583333 5.625 5.666666 5.708333 5.75 5.791666 5.833333 5.875 5.916666 5.958333 6 6.041666 6.083333 6.125 6.166666 6.208333 6.25 6.291666 6.333333 6.375 6.416666 6.458333 6.5 6.541666 6.583333 6.625 6.666666 6.708333 6.75 6.791666 6.833333 6.875 6.916666 6.958333 7 7.041666 7.083333 7.125 7.166666 7.208333 7.25 7.291666 7.333333 7.375 7.416666 7.458333 7.5 + + + + + + + + 0 0.004120349 0.01629626 0.03624999 0.06370371 0.09837961 0.14 0.188287 0.242963 0.30375 0.3703704 0.4425463 0.5200001 0.6024537 0.6896296 0.78125 0.8770372 0.976713 1.08 1.18662 1.296296 1.40875 1.523704 1.640879 1.76 1.880787 2.002963 2.12625 2.25037 2.375046 2.5 2.624954 2.74963 2.87375 2.997037 3.119213 3.24 3.35912 3.476296 3.591249 3.703704 3.81338 3.92 4.023287 4.122963 4.21875 4.310369 4.397546 4.48 4.557454 4.62963 4.69625 4.757037 4.811712 4.860001 4.90162 4.936296 4.96375 4.983704 4.995879 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 4.99588 4.983704 4.96375 4.936296 4.90162 4.86 4.811713 4.757037 4.69625 4.62963 4.557454 4.48 4.397546 4.31037 4.21875 4.122963 4.023287 3.92 3.81338 3.703704 3.59125 3.476296 3.359121 3.24 3.119213 2.997037 2.87375 2.749629 2.624954 2.5 2.375046 2.25037 2.12625 2.002963 1.880787 1.76 1.64088 1.523704 1.408751 1.296296 1.18662 1.08 0.9767127 0.8770371 0.78125 0.6896305 0.6024537 0.52 0.4425464 0.3703704 0.3037496 0.2429628 0.1882877 0.1399993 0.09837961 0.06370353 0.03625011 0.01629638 0.004120826 0 + + + + + + + + LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR + + + + + + + + + + + + + + + + 0 0.04166662 0.08333331 0.125 0.1666666 0.2083333 0.25 0.2916666 0.3333333 0.375 0.4166666 0.4583333 0.5 0.5416667 0.5833333 0.625 0.6666667 0.7083333 0.75 0.7916667 0.8333333 0.875 0.9166667 0.9583333 1 1.041667 1.083333 1.125 1.166667 1.208333 1.25 1.291667 1.333333 1.375 1.416667 1.458333 1.5 1.541667 1.583333 1.625 1.666667 1.708333 1.75 1.791667 1.833333 1.875 1.916667 1.958333 2 2.041667 2.083333 2.125 2.166667 2.208333 2.25 2.291667 2.333333 2.375 2.416667 2.458333 2.5 2.541667 2.583333 2.625 2.666667 2.708333 2.75 2.791667 2.833333 2.875 2.916667 2.958333 3 3.041667 3.083333 3.125 3.166667 3.208333 3.25 3.291667 3.333333 3.375 3.416667 3.458333 3.5 3.541667 3.583333 3.625 3.666667 3.708333 3.75 3.791667 3.833333 3.875 3.916667 3.958333 4 4.041666 4.083333 4.125 4.166666 4.208333 4.25 4.291666 4.333333 4.375 4.416666 4.458333 4.5 4.541666 4.583333 4.625 4.666666 4.708333 4.75 4.791666 4.833333 4.875 4.916666 4.958333 5 5.041666 5.083333 5.125 5.166666 5.208333 5.25 5.291666 5.333333 5.375 5.416666 5.458333 5.5 5.541666 5.583333 5.625 5.666666 5.708333 5.75 5.791666 5.833333 5.875 5.916666 5.958333 6 6.041666 6.083333 6.125 6.166666 6.208333 6.25 6.291666 6.333333 6.375 6.416666 6.458333 6.5 6.541666 6.583333 6.625 6.666666 6.708333 6.75 6.791666 6.833333 6.875 6.916666 6.958333 7 7.041666 7.083333 7.125 7.166666 7.208333 7.25 7.291666 7.333333 7.375 7.416666 7.458333 7.5 + + + + + + + + -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3.00412 -3.016296 -3.03625 -3.063704 -3.09838 -3.14 -3.188287 -3.242963 -3.30375 -3.37037 -3.442546 -3.52 -3.602454 -3.68963 -3.78125 -3.877037 -3.976713 -4.08 -4.18662 -4.296297 -4.40875 -4.523704 -4.64088 -4.76 -4.880787 -5.002963 -5.12625 -5.25037 -5.375046 -5.5 -5.624954 -5.74963 -5.87375 -5.997037 -6.119212 -6.239999 -6.35912 -6.476296 -6.59125 -6.703704 -6.813379 -6.920001 -7.023287 -7.122963 -7.21875 -7.310369 -7.397546 -7.48 -7.557454 -7.62963 -7.69625 -7.757037 -7.811712 -7.860001 -7.90162 -7.936295 -7.963749 -7.983705 -7.995878 -8 -8 -8 -8 -8 -8 -8 -8 -8 -8 -8 -8 -8 -8 -8 -8 -8 -8 -8 -8 -8 -8 -8 -8 -8 -8 -8 -8 -8 -8 -8 -8 -8 -8 -8 -8 -8 -8 -8 -8 -8 -8 -8 -8 -8 -8 -8 -8 -8 -8 -8 -8 -8 -8 -8 -8 -8 -8 -8 -8 -8 + + + + + + + + LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR + + + + + + + + + + + + + + + + + + + 1 1 1 + 0 0 1 0 + 0 1 0 0 + 1 0 0 0 + 0 -8 0 + + + + 0.5 0.5 0.5 + 0 0 1 0 + 0 1 0 0 + 1 0 0 0 + 0 3 0 + + + + 1 1 1 + 0 0 1 -89.97488 + 0 1 0 89.98238 + 1 0 0 45 + 0 0 0 + + + + + + + +
\ No newline at end of file diff --git a/src/MagnumPlugins/AssimpImporter/Test/exported-animation.fbx b/src/MagnumPlugins/AssimpImporter/Test/exported-animation.fbx new file mode 100644 index 000000000..bf8e57fc3 Binary files /dev/null and b/src/MagnumPlugins/AssimpImporter/Test/exported-animation.fbx differ diff --git a/src/MagnumPlugins/AssimpImporter/Test/exported-animation.gltf b/src/MagnumPlugins/AssimpImporter/Test/exported-animation.gltf new file mode 100644 index 000000000..90a80390a --- /dev/null +++ b/src/MagnumPlugins/AssimpImporter/Test/exported-animation.gltf @@ -0,0 +1,341 @@ +{ + "asset" : { + "generator" : "Khronos glTF Blender I/O v1.6.16", + "version" : "2.0" + }, + "scene" : 0, + "scenes" : [ + { + "name" : "Scene", + "nodes" : [ + 0, + 1, + 2 + ] + } + ], + "nodes" : [ + { + "mesh" : 0, + "name" : "Rotating cube" + }, + { + "mesh" : 1, + "name" : "Scaling cube", + "translation" : [ + 0, + 0, + -3 + ] + }, + { + "mesh" : 2, + "name" : "Translating cube", + "translation" : [ + 0, + 0, + 3 + ] + } + ], + "animations" : [ + { + "channels" : [ + { + "sampler" : 0, + "target" : { + "node" : 0, + "path" : "rotation" + } + } + ], + "name" : "Rotation animation", + "samplers" : [ + { + "input" : 10, + "interpolation" : "LINEAR", + "output" : 11 + } + ] + }, + { + "channels" : [ + { + "sampler" : 0, + "target" : { + "node" : 1, + "path" : "scale" + } + } + ], + "name" : "Scaling animation", + "samplers" : [ + { + "input" : 10, + "interpolation" : "LINEAR", + "output" : 12 + } + ] + }, + { + "channels" : [ + { + "sampler" : 0, + "target" : { + "node" : 2, + "path" : "translation" + } + } + ], + "name" : "Translation animation", + "samplers" : [ + { + "input" : 10, + "interpolation" : "LINEAR", + "output" : 13 + } + ] + } + ], + "meshes" : [ + { + "name" : "Cube 1", + "primitives" : [ + { + "attributes" : { + "POSITION" : 0, + "NORMAL" : 1, + "TEXCOORD_0" : 2 + }, + "indices" : 3 + } + ] + }, + { + "name" : "Cube 2", + "primitives" : [ + { + "attributes" : { + "POSITION" : 4, + "NORMAL" : 5, + "TEXCOORD_0" : 6 + }, + "indices" : 3 + } + ] + }, + { + "name" : "Cube 3", + "primitives" : [ + { + "attributes" : { + "POSITION" : 7, + "NORMAL" : 8, + "TEXCOORD_0" : 9 + }, + "indices" : 3 + } + ] + } + ], + "accessors" : [ + { + "bufferView" : 0, + "componentType" : 5126, + "count" : 24, + "max" : [ + 1, + 1, + 1 + ], + "min" : [ + -1, + -1, + -1 + ], + "type" : "VEC3" + }, + { + "bufferView" : 1, + "componentType" : 5126, + "count" : 24, + "type" : "VEC3" + }, + { + "bufferView" : 2, + "componentType" : 5126, + "count" : 24, + "type" : "VEC2" + }, + { + "bufferView" : 3, + "componentType" : 5123, + "count" : 36, + "type" : "SCALAR" + }, + { + "bufferView" : 4, + "componentType" : 5126, + "count" : 24, + "max" : [ + 1, + 1, + 1 + ], + "min" : [ + -1, + -1, + -1 + ], + "type" : "VEC3" + }, + { + "bufferView" : 5, + "componentType" : 5126, + "count" : 24, + "type" : "VEC3" + }, + { + "bufferView" : 6, + "componentType" : 5126, + "count" : 24, + "type" : "VEC2" + }, + { + "bufferView" : 7, + "componentType" : 5126, + "count" : 24, + "max" : [ + 1, + 1, + 1 + ], + "min" : [ + -1, + -1, + -1 + ], + "type" : "VEC3" + }, + { + "bufferView" : 8, + "componentType" : 5126, + "count" : 24, + "type" : "VEC3" + }, + { + "bufferView" : 9, + "componentType" : 5126, + "count" : 24, + "type" : "VEC2" + }, + { + "bufferView" : 10, + "componentType" : 5126, + "count" : 181, + "max" : [ + 7.5 + ], + "min" : [ + 0 + ], + "type" : "SCALAR" + }, + { + "bufferView" : 11, + "componentType" : 5126, + "count" : 181, + "type" : "VEC4" + }, + { + "bufferView" : 12, + "componentType" : 5126, + "count" : 181, + "type" : "VEC3" + }, + { + "bufferView" : 13, + "componentType" : 5126, + "count" : 181, + "type" : "VEC3" + } + ], + "bufferViews" : [ + { + "buffer" : 0, + "byteLength" : 288, + "byteOffset" : 0 + }, + { + "buffer" : 0, + "byteLength" : 288, + "byteOffset" : 288 + }, + { + "buffer" : 0, + "byteLength" : 192, + "byteOffset" : 576 + }, + { + "buffer" : 0, + "byteLength" : 72, + "byteOffset" : 768 + }, + { + "buffer" : 0, + "byteLength" : 288, + "byteOffset" : 840 + }, + { + "buffer" : 0, + "byteLength" : 288, + "byteOffset" : 1128 + }, + { + "buffer" : 0, + "byteLength" : 192, + "byteOffset" : 1416 + }, + { + "buffer" : 0, + "byteLength" : 288, + "byteOffset" : 1608 + }, + { + "buffer" : 0, + "byteLength" : 288, + "byteOffset" : 1896 + }, + { + "buffer" : 0, + "byteLength" : 192, + "byteOffset" : 2184 + }, + { + "buffer" : 0, + "byteLength" : 724, + "byteOffset" : 2376 + }, + { + "buffer" : 0, + "byteLength" : 2896, + "byteOffset" : 3100 + }, + { + "buffer" : 0, + "byteLength" : 2172, + "byteOffset" : 5996 + }, + { + "buffer" : 0, + "byteLength" : 2172, + "byteOffset" : 8168 + } + ], + "buffers" : [ + { + "byteLength" : 10340, + "uri" : "data:application/octet-stream;base64,AACAvwAAgL8AAIA/AACAvwAAgL8AAIA/AACAvwAAgL8AAIA/AACAvwAAgD8AAIA/AACAvwAAgD8AAIA/AACAvwAAgD8AAIA/AACAvwAAgL8AAIC/AACAvwAAgL8AAIC/AACAvwAAgL8AAIC/AACAvwAAgD8AAIC/AACAvwAAgD8AAIC/AACAvwAAgD8AAIC/AACAPwAAgL8AAIA/AACAPwAAgL8AAIA/AACAPwAAgL8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgL8AAIC/AACAPwAAgL8AAIC/AACAPwAAgL8AAIC/AACAPwAAgD8AAIC/AACAPwAAgD8AAIC/AACAPwAAgD8AAIC/AACAvwAAAAAAAACAAAAAAAAAgL8AAACAAAAAAAAAAAAAAIA/AACAvwAAAAAAAACAAAAAAAAAAAAAAIA/AAAAAAAAgD8AAACAAACAvwAAAAAAAACAAAAAAAAAgL8AAACAAAAAAAAAAAAAAIC/AACAvwAAAAAAAACAAAAAAAAAAAAAAIC/AAAAAAAAgD8AAACAAAAAAAAAgL8AAACAAAAAAAAAAAAAAIA/AACAPwAAAAAAAACAAAAAAAAAAAAAAIA/AAAAAAAAgD8AAACAAACAPwAAAAAAAACAAAAAAAAAgL8AAACAAAAAAAAAAAAAAIC/AACAPwAAAAAAAACAAAAAAAAAAAAAAIC/AAAAAAAAgD8AAACAAACAPwAAAAAAAACAAADAPgAAgD8AAAA+AACAPgAAwD4AAAAAAAAgPwAAgD8AACA/AAAAAAAAYD8AAIA+AADAPgAAQD8AAAA+AAAAPwAAwD4AAEA/AAAgPwAAQD8AACA/AABAPwAAYD8AAAA/AADAPgAAgD4AAMA+AACAPgAAwD4AAIA+AAAgPwAAgD4AACA/AACAPgAAID8AAIA+AADAPgAAAD8AAMA+AAAAPwAAwD4AAAA/AAAgPwAAAD8AACA/AAAAPwAAID8AAAA/AAADAAkAAAAJAAYACAAKABUACAAVABMAFAAXABEAFAARAA4ADQAPAAQADQAEAAIABwASAAwABwAMAAEAFgALAAUAFgAFABAAAACAvwAAgL8AAIA/AACAvwAAgL8AAIA/AACAvwAAgL8AAIA/AACAvwAAgD8AAIA/AACAvwAAgD8AAIA/AACAvwAAgD8AAIA/AACAvwAAgL8AAIC/AACAvwAAgL8AAIC/AACAvwAAgL8AAIC/AACAvwAAgD8AAIC/AACAvwAAgD8AAIC/AACAvwAAgD8AAIC/AACAPwAAgL8AAIA/AACAPwAAgL8AAIA/AACAPwAAgL8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgL8AAIC/AACAPwAAgL8AAIC/AACAPwAAgL8AAIC/AACAPwAAgD8AAIC/AACAPwAAgD8AAIC/AACAPwAAgD8AAIC/AACAvwAAAAAAAACAAAAAAAAAgL8AAACAAAAAAAAAAAAAAIA/AACAvwAAAAAAAACAAAAAAAAAAAAAAIA/AAAAAAAAgD8AAACAAACAvwAAAAAAAACAAAAAAAAAgL8AAACAAAAAAAAAAAAAAIC/AACAvwAAAAAAAACAAAAAAAAAAAAAAIC/AAAAAAAAgD8AAACAAAAAAAAAgL8AAACAAAAAAAAAAAAAAIA/AACAPwAAAAAAAACAAAAAAAAAAAAAAIA/AAAAAAAAgD8AAACAAACAPwAAAAAAAACAAAAAAAAAgL8AAACAAAAAAAAAAAAAAIC/AACAPwAAAAAAAACAAAAAAAAAAAAAAIC/AAAAAAAAgD8AAACAAACAPwAAAAAAAACAAADAPgAAgD8AAAA+AACAPgAAwD4AAAAAAAAgPwAAgD8AACA/AAAAAAAAYD8AAIA+AADAPgAAQD8AAAA+AAAAPwAAwD4AAEA/AAAgPwAAQD8AACA/AABAPwAAYD8AAAA/AADAPgAAgD4AAMA+AACAPgAAwD4AAIA+AAAgPwAAgD4AACA/AACAPgAAID8AAIA+AADAPgAAAD8AAMA+AAAAPwAAwD4AAAA/AAAgPwAAAD8AACA/AAAAPwAAID8AAAA/AACAvwAAgL8AAIA/AACAvwAAgL8AAIA/AACAvwAAgL8AAIA/AACAvwAAgD8AAIA/AACAvwAAgD8AAIA/AACAvwAAgD8AAIA/AACAvwAAgL8AAIC/AACAvwAAgL8AAIC/AACAvwAAgL8AAIC/AACAvwAAgD8AAIC/AACAvwAAgD8AAIC/AACAvwAAgD8AAIC/AACAPwAAgL8AAIA/AACAPwAAgL8AAIA/AACAPwAAgL8AAIA/AACAPwAAgD8AAIA/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" + } + ] +} diff --git a/src/MagnumPlugins/AssimpImporter/Test/exported-animation.md b/src/MagnumPlugins/AssimpImporter/Test/exported-animation.md new file mode 100644 index 000000000..e3a1b0943 --- /dev/null +++ b/src/MagnumPlugins/AssimpImporter/Test/exported-animation.md @@ -0,0 +1,25 @@ +# Exporting the animation files + +1. Open the `exported-animation.blend` file, make desired changes and save the blend + file. +2. Export to Collada: + - set *Forward Axis* to *Y* + - set *Up Axis* to *Z* + - in the *Anim* tab: + - enable *Include Animations* + - set *Key Type* to *Samples* + - overwrite the `exported-animation.dae` file +3. Export to FBX: + - set *Scale* to 0.01 + - enable *Bake Animation* + - overwrite the `exported-animation.fbx` file +4. Export to glTF: + - set *Format* to *glTF Embedded (.gltf)* + - in the *Animation* field: + - enable *Animation* + - in the *Animation* field: + - enable *Always Sample Animations* + - overwrite the `exported-animation.gltf` file +5. Verify the animations play back correctly +6. Commit the updated `exported-animation.*` files, do a sanity check that the sizes + didn't change much. diff --git a/src/MagnumPlugins/AssimpImporter/checkAssimpVersion.cpp b/src/MagnumPlugins/AssimpImporter/checkAssimpVersion.cpp new file mode 100644 index 000000000..6decc6c2d --- /dev/null +++ b/src/MagnumPlugins/AssimpImporter/checkAssimpVersion.cpp @@ -0,0 +1,64 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020, 2021 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. +*/ + +#if CHECK_VERSION >= 20190915 +#include +#endif + +#include +#include + +#ifndef CHECK_VERSION +#error CHECK_VERSION not defined +#define CHECK_VERSION 0xffffffff +#endif + +int main() { + int ret = 0; + + /* First version that correctly parses glTF2 spline-interpolated animation data: + https://github.com/assimp/assimp/commit/e3083c21f0a7beae6c37a2265b7919a02cbf83c4. + Check for Scene::mName added in + https://github.com/assimp/assimp/commit/afd69bea8a6a870a986b5c8ad1a07bf127b0eaa0 */ + #if CHECK_VERSION >= 20201123 + aiScene scene; + scene.mName = ""; + #endif + + /* Assimp 5. Of all the things that could break, this version reports itself as + 4.1. Since some of the insane awful bugs got fixed in version 5, the test has + to check against the version in order to adjust expectations. The only way I + could make this work is checking for the getEpsilon() function added in + https://github.com/assimp/assimp/commit/8b95479bb00b4bf8fb875f2c5b0605ddfd203b7f + Related bug: https://github.com/assimp/assimp/issues/2693 */ + #if CHECK_VERSION >= 20190915 + ret = static_cast(Assimp::Math::getEpsilon()); + #endif + + unsigned int version = aiGetVersionMajor()*100 + aiGetVersionMinor(); + ret = static_cast(version); + + return ret; +} diff --git a/src/MagnumPlugins/AssimpImporter/Test/CheckAssimpVersion5.cpp b/src/MagnumPlugins/AssimpImporter/configureInternal.h.cmake similarity index 88% rename from src/MagnumPlugins/AssimpImporter/Test/CheckAssimpVersion5.cpp rename to src/MagnumPlugins/AssimpImporter/configureInternal.h.cmake index f3b511b7b..551bbad5e 100644 --- a/src/MagnumPlugins/AssimpImporter/Test/CheckAssimpVersion5.cpp +++ b/src/MagnumPlugins/AssimpImporter/configureInternal.h.cmake @@ -23,8 +23,6 @@ DEALINGS IN THE SOFTWARE. */ -#include - -int main() { - return !(Assimp::Math::getEpsilon() > 0.0f); -} +#define ASSIMP_VERSION ${ASSIMP_VERSION} +#define ASSIMP_IS_VERSION_5 (ASSIMP_VERSION >= 20190915) +#define ASSIMP_HAS_BROKEN_GLTF_SPLINES (ASSIMP_VERSION < 20201123)