Merge branch 'master' into master

pull/2638/head
Kim Kulling 2019-09-07 18:53:26 +02:00 committed by GitHub
commit 5d8482dae6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 427 additions and 140 deletions

View File

@ -253,7 +253,7 @@ ELSEIF(MSVC)
IF(MSVC12) IF(MSVC12)
ADD_COMPILE_OPTIONS(/wd4351) ADD_COMPILE_OPTIONS(/wd4351)
ENDIF() ENDIF()
SET(CMAKE_CXX_FLAGS_DEBUG "/D_DEBUG /MDd /Ob2") SET(CMAKE_CXX_FLAGS_DEBUG "/D_DEBUG /MDd /Ob2 /DEBUG:FULL /Zi")
ELSEIF ( "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang" ) ELSEIF ( "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang" )
IF(NOT HUNTER_ENABLED) IF(NOT HUNTER_ENABLED)
SET(CMAKE_CXX_FLAGS "-fPIC -std=c++11 ${CMAKE_CXX_FLAGS}") SET(CMAKE_CXX_FLAGS "-fPIC -std=c++11 ${CMAKE_CXX_FLAGS}")

View File

@ -61,83 +61,66 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
using namespace Assimp; using namespace Assimp;
// maximum path length #ifdef _WIN32
// XXX http://insanecoding.blogspot.com/2007/11/pathmax-simply-isnt.html static std::wstring Utf8ToWide(const char* in)
#ifdef PATH_MAX {
# define PATHLIMIT PATH_MAX int size = MultiByteToWideChar(CP_UTF8, 0, in, -1, nullptr, 0);
#else // size includes terminating null; std::wstring adds null automatically
# define PATHLIMIT 4096 std::wstring out(static_cast<size_t>(size) - 1, L'\0');
MultiByteToWideChar(CP_UTF8, 0, in, -1, &out[0], size);
return out;
}
static std::string WideToUtf8(const wchar_t* in)
{
int size = WideCharToMultiByte(CP_UTF8, 0, in, -1, nullptr, 0, nullptr, nullptr);
// size includes terminating null; std::string adds null automatically
std::string out(static_cast<size_t>(size) - 1, '\0');
WideCharToMultiByte(CP_UTF8, 0, in, -1, &out[0], size, nullptr, nullptr);
return out;
}
#endif #endif
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Tests for the existence of a file at the given path. // Tests for the existence of a file at the given path.
bool DefaultIOSystem::Exists( const char* pFile) const bool DefaultIOSystem::Exists(const char* pFile) const
{ {
#ifdef _WIN32 #ifdef _WIN32
wchar_t fileName16[PATHLIMIT]; struct __stat64 filestat;
if (_wstat64(Utf8ToWide(pFile).c_str(), &filestat) != 0) {
#ifndef WindowsStore return false;
bool isUnicode = IsTextUnicode(pFile, static_cast<int>(strlen(pFile)), NULL) != 0;
if (isUnicode) {
MultiByteToWideChar(CP_UTF8, MB_PRECOMPOSED, pFile, -1, fileName16, PATHLIMIT);
struct __stat64 filestat;
if (0 != _wstat64(fileName16, &filestat)) {
return false;
}
} else {
#endif
FILE* file = ::fopen(pFile, "rb");
if (!file)
return false;
::fclose(file);
#ifndef WindowsStore
} }
#endif
#else #else
FILE* file = ::fopen( pFile, "rb"); FILE* file = ::fopen(pFile, "rb");
if( !file) if (!file)
return false; return false;
::fclose( file); ::fclose(file);
#endif #endif
return true; return true;
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Open a new file with a given path. // Open a new file with a given path.
IOStream* DefaultIOSystem::Open( const char* strFile, const char* strMode) IOStream* DefaultIOSystem::Open(const char* strFile, const char* strMode)
{ {
ai_assert(NULL != strFile); ai_assert(strFile != nullptr);
ai_assert(NULL != strMode); ai_assert(strMode != nullptr);
FILE* file; FILE* file;
#ifdef _WIN32 #ifdef _WIN32
wchar_t fileName16[PATHLIMIT]; file = ::_wfopen(Utf8ToWide(strFile).c_str(), Utf8ToWide(strMode).c_str());
#ifndef WindowsStore
bool isUnicode = IsTextUnicode(strFile, static_cast<int>(strlen(strFile)), NULL) != 0;
if (isUnicode) {
MultiByteToWideChar(CP_UTF8, MB_PRECOMPOSED, strFile, -1, fileName16, PATHLIMIT);
std::string mode8(strMode);
file = ::_wfopen(fileName16, std::wstring(mode8.begin(), mode8.end()).c_str());
} else {
#endif
file = ::fopen(strFile, strMode);
#ifndef WindowsStore
}
#endif
#else #else
file = ::fopen(strFile, strMode); file = ::fopen(strFile, strMode);
#endif #endif
if (nullptr == file) if (!file)
return nullptr; return nullptr;
return new DefaultIOStream(file, (std::string) strFile); return new DefaultIOStream(file, strFile);
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Closes the given file and releases all resources associated with it. // Closes the given file and releases all resources associated with it.
void DefaultIOSystem::Close( IOStream* pFile) void DefaultIOSystem::Close(IOStream* pFile)
{ {
delete pFile; delete pFile;
} }
@ -155,78 +138,56 @@ char DefaultIOSystem::getOsSeparator() const
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// IOSystem default implementation (ComparePaths isn't a pure virtual function) // IOSystem default implementation (ComparePaths isn't a pure virtual function)
bool IOSystem::ComparePaths (const char* one, const char* second) const bool IOSystem::ComparePaths(const char* one, const char* second) const
{ {
return !ASSIMP_stricmp(one,second); return !ASSIMP_stricmp(one, second);
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Convert a relative path into an absolute path // Convert a relative path into an absolute path
inline static void MakeAbsolutePath (const char* in, char* _out) inline static std::string MakeAbsolutePath(const char* in)
{ {
ai_assert(in && _out); ai_assert(in);
#if defined( _MSC_VER ) || defined( __MINGW32__ ) std::string out;
#ifndef WindowsStore #ifdef _WIN32
bool isUnicode = IsTextUnicode(in, static_cast<int>(strlen(in)), NULL) != 0; wchar_t* ret = ::_wfullpath(nullptr, Utf8ToWide(in).c_str(), 0);
if (isUnicode) { if (ret) {
wchar_t out16[PATHLIMIT]; out = WideToUtf8(ret);
wchar_t in16[PATHLIMIT]; free(ret);
MultiByteToWideChar(CP_UTF8, MB_PRECOMPOSED, in, -1, out16, PATHLIMIT); }
wchar_t* ret = ::_wfullpath(out16, in16, PATHLIMIT); #else
if (ret) { char* ret = realpath(in, nullptr);
WideCharToMultiByte(CP_UTF8, MB_PRECOMPOSED, out16, -1, _out, PATHLIMIT, nullptr, nullptr); if (ret) {
} out = ret;
if (!ret) { free(ret);
// preserve the input path, maybe someone else is able to fix
// the path before it is accessed (e.g. our file system filter)
ASSIMP_LOG_WARN_F("Invalid path: ", std::string(in));
strcpy(_out, in);
}
} else {
#endif
char* ret = :: _fullpath(_out, in, PATHLIMIT);
if (!ret) {
// preserve the input path, maybe someone else is able to fix
// the path before it is accessed (e.g. our file system filter)
ASSIMP_LOG_WARN_F("Invalid path: ", std::string(in));
strcpy(_out, in);
}
#ifndef WindowsStore
} }
#endif #endif
#else if (!ret) {
// use realpath
char* ret = realpath(in, _out);
if(!ret) {
// preserve the input path, maybe someone else is able to fix // preserve the input path, maybe someone else is able to fix
// the path before it is accessed (e.g. our file system filter) // the path before it is accessed (e.g. our file system filter)
ASSIMP_LOG_WARN_F("Invalid path: ", std::string(in)); ASSIMP_LOG_WARN_F("Invalid path: ", std::string(in));
strcpy(_out,in); out = in;
} }
#endif return out;
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// DefaultIOSystem's more specialized implementation // DefaultIOSystem's more specialized implementation
bool DefaultIOSystem::ComparePaths (const char* one, const char* second) const bool DefaultIOSystem::ComparePaths(const char* one, const char* second) const
{ {
// chances are quite good both paths are formatted identically, // chances are quite good both paths are formatted identically,
// so we can hopefully return here already // so we can hopefully return here already
if( !ASSIMP_stricmp(one,second) ) if (!ASSIMP_stricmp(one, second))
return true; return true;
char temp1[PATHLIMIT]; std::string temp1 = MakeAbsolutePath(one);
char temp2[PATHLIMIT]; std::string temp2 = MakeAbsolutePath(second);
MakeAbsolutePath (one, temp1); return !ASSIMP_stricmp(temp1, temp2);
MakeAbsolutePath (second, temp2);
return !ASSIMP_stricmp(temp1,temp2);
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
std::string DefaultIOSystem::fileName( const std::string &path ) std::string DefaultIOSystem::fileName(const std::string& path)
{ {
std::string ret = path; std::string ret = path;
std::size_t last = ret.find_last_of("\\/"); std::size_t last = ret.find_last_of("\\/");
@ -235,16 +196,16 @@ std::string DefaultIOSystem::fileName( const std::string &path )
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
std::string DefaultIOSystem::completeBaseName( const std::string &path ) std::string DefaultIOSystem::completeBaseName(const std::string& path)
{ {
std::string ret = fileName(path); std::string ret = fileName(path);
std::size_t pos = ret.find_last_of('.'); std::size_t pos = ret.find_last_of('.');
if(pos != ret.npos) ret = ret.substr(0, pos); if (pos != std::string::npos) ret = ret.substr(0, pos);
return ret; return ret;
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
std::string DefaultIOSystem::absolutePath( const std::string &path ) std::string DefaultIOSystem::absolutePath(const std::string& path)
{ {
std::string ret = path; std::string ret = path;
std::size_t last = ret.find_last_of("\\/"); std::size_t last = ret.find_last_of("\\/");
@ -253,5 +214,3 @@ std::string DefaultIOSystem::absolutePath( const std::string &path )
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
#undef PATHLIMIT

View File

@ -444,7 +444,10 @@ aiReturn Exporter::Export( const aiScene* pScene, const char* pFormatId, const c
} }
ExportProperties emptyProperties; // Never pass NULL ExportProperties so Exporters don't have to worry. ExportProperties emptyProperties; // Never pass NULL ExportProperties so Exporters don't have to worry.
exp.mExportFunction(pPath,pimpl->mIOSystem.get(),scenecopy.get(), pProperties ? pProperties : &emptyProperties); ExportProperties* pProp = pProperties ? (ExportProperties*)pProperties : &emptyProperties;
pProp->SetPropertyBool("bJoinIdenticalVertices", must_join_again);
exp.mExportFunction(pPath,pimpl->mIOSystem.get(),scenecopy.get(), pProp);
exp.mExportFunction(pPath,pimpl->mIOSystem.get(),scenecopy.get(), pProp);
pimpl->mProgressHandler->UpdateFileWrite(4, 4); pimpl->mProgressHandler->UpdateFileWrite(4, 4);
} catch (DeadlyExportError& err) { } catch (DeadlyExportError& err) {

View File

@ -1091,6 +1091,35 @@ void SceneCombiner::Copy( aiMesh** _dest, const aiMesh* src ) {
aiFace& f = dest->mFaces[i]; aiFace& f = dest->mFaces[i];
GetArrayCopy(f.mIndices,f.mNumIndices); GetArrayCopy(f.mIndices,f.mNumIndices);
} }
// make a deep copy of all blend shapes
CopyPtrArray(dest->mAnimMeshes, dest->mAnimMeshes, dest->mNumAnimMeshes);
}
// ------------------------------------------------------------------------------------------------
void SceneCombiner::Copy(aiAnimMesh** _dest, const aiAnimMesh* src) {
if (nullptr == _dest || nullptr == src) {
return;
}
aiAnimMesh* dest = *_dest = new aiAnimMesh();
// get a flat copy
::memcpy(dest, src, sizeof(aiAnimMesh));
// and reallocate all arrays
GetArrayCopy(dest->mVertices, dest->mNumVertices);
GetArrayCopy(dest->mNormals, dest->mNumVertices);
GetArrayCopy(dest->mTangents, dest->mNumVertices);
GetArrayCopy(dest->mBitangents, dest->mNumVertices);
unsigned int n = 0;
while (dest->HasTextureCoords(n))
GetArrayCopy(dest->mTextureCoords[n++], dest->mNumVertices);
n = 0;
while (dest->HasVertexColors(n))
GetArrayCopy(dest->mColors[n++], dest->mNumVertices);
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------

View File

@ -67,6 +67,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <vector> #include <vector>
#include <array> #include <array>
#include <unordered_set> #include <unordered_set>
#include <numeric>
// RESOURCES: // RESOURCES:
// https://code.blender.org/2013/08/fbx-binary-file-format-specification/ // https://code.blender.org/2013/08/fbx-binary-file-format-specification/
@ -1005,6 +1006,9 @@ void FBXExporter::WriteObjects ()
object_node.EndProperties(outstream, binary, indent); object_node.EndProperties(outstream, binary, indent);
object_node.BeginChildren(outstream, binary, indent); object_node.BeginChildren(outstream, binary, indent);
bool bJoinIdenticalVertices = mProperties->GetPropertyBool("bJoinIdenticalVertices", true);
std::vector<std::vector<int32_t>> vVertexIndice;//save vertex_indices as it is needed later
// geometry (aiMesh) // geometry (aiMesh)
mesh_uids.clear(); mesh_uids.clear();
indent = 1; indent = 1;
@ -1031,21 +1035,35 @@ void FBXExporter::WriteObjects ()
std::vector<int32_t> vertex_indices; std::vector<int32_t> vertex_indices;
// map of vertex value to its index in the data vector // map of vertex value to its index in the data vector
std::map<aiVector3D,size_t> index_by_vertex_value; std::map<aiVector3D,size_t> index_by_vertex_value;
int32_t index = 0; if(bJoinIdenticalVertices){
for (size_t vi = 0; vi < m->mNumVertices; ++vi) { int32_t index = 0;
aiVector3D vtx = m->mVertices[vi]; for (size_t vi = 0; vi < m->mNumVertices; ++vi) {
auto elem = index_by_vertex_value.find(vtx); aiVector3D vtx = m->mVertices[vi];
if (elem == index_by_vertex_value.end()) { auto elem = index_by_vertex_value.find(vtx);
vertex_indices.push_back(index); if (elem == index_by_vertex_value.end()) {
index_by_vertex_value[vtx] = index; vertex_indices.push_back(index);
flattened_vertices.push_back(vtx[0]); index_by_vertex_value[vtx] = index;
flattened_vertices.push_back(vtx[1]); flattened_vertices.push_back(vtx[0]);
flattened_vertices.push_back(vtx[2]); flattened_vertices.push_back(vtx[1]);
++index; flattened_vertices.push_back(vtx[2]);
} else { ++index;
vertex_indices.push_back(int32_t(elem->second)); } else {
vertex_indices.push_back(int32_t(elem->second));
}
} }
} }
else { // do not join vertex, respect the export flag
vertex_indices.resize(m->mNumVertices);
std::iota(vertex_indices.begin(), vertex_indices.end(), 0);
for(unsigned int v = 0; v < m->mNumVertices; ++ v) {
aiVector3D vtx = m->mVertices[v];
flattened_vertices.push_back(vtx.x);
flattened_vertices.push_back(vtx.y);
flattened_vertices.push_back(vtx.z);
}
}
vVertexIndice.push_back(vertex_indices);
FBX::Node::WritePropertyNode( FBX::Node::WritePropertyNode(
"Vertices", flattened_vertices, outstream, binary, indent "Vertices", flattened_vertices, outstream, binary, indent
); );
@ -1798,28 +1816,8 @@ void FBXExporter::WriteObjects ()
// connect it // connect it
connections.emplace_back("C", "OO", deformer_uid, mesh_uids[mi]); connections.emplace_back("C", "OO", deformer_uid, mesh_uids[mi]);
// we will be indexing by vertex... //computed before
// but there might be a different number of "vertices" std::vector<int32_t>& vertex_indices = vVertexIndice[mi];
// between assimp and our output FBX.
// this code is cut-and-pasted from the geometry section above...
// ideally this should not be so.
// ---
// index of original vertex in vertex data vector
std::vector<int32_t> vertex_indices;
// map of vertex value to its index in the data vector
std::map<aiVector3D,size_t> index_by_vertex_value;
int32_t index = 0;
for (size_t vi = 0; vi < m->mNumVertices; ++vi) {
aiVector3D vtx = m->mVertices[vi];
auto elem = index_by_vertex_value.find(vtx);
if (elem == index_by_vertex_value.end()) {
vertex_indices.push_back(index);
index_by_vertex_value[vtx] = index;
++index;
} else {
vertex_indices.push_back(int32_t(elem->second));
}
}
// TODO, FIXME: this won't work if anything is not in the bind pose. // TODO, FIXME: this won't work if anything is not in the bind pose.
// for now if such a situation is detected, we throw an exception. // for now if such a situation is detected, we throw an exception.

View File

@ -1041,7 +1041,7 @@ aiNodeAnim* CreateNodeAnim(glTF2::Asset& r, Node& node, AnimationSamplers& sampl
delete[] values; delete[] values;
} else if (node.rotation.isPresent) { } else if (node.rotation.isPresent) {
anim->mNumRotationKeys = 1; anim->mNumRotationKeys = 1;
anim->mRotationKeys = new aiQuatKey(); anim->mRotationKeys = new aiQuatKey[anim->mNumRotationKeys];
anim->mRotationKeys->mTime = 0.f; anim->mRotationKeys->mTime = 0.f;
anim->mRotationKeys->mValue.x = node.rotation.value[0]; anim->mRotationKeys->mValue.x = node.rotation.value[0];
anim->mRotationKeys->mValue.y = node.rotation.value[1]; anim->mRotationKeys->mValue.y = node.rotation.value[1];
@ -1064,7 +1064,7 @@ aiNodeAnim* CreateNodeAnim(glTF2::Asset& r, Node& node, AnimationSamplers& sampl
delete[] values; delete[] values;
} else if (node.scale.isPresent) { } else if (node.scale.isPresent) {
anim->mNumScalingKeys = 1; anim->mNumScalingKeys = 1;
anim->mScalingKeys = new aiVectorKey(); anim->mScalingKeys = new aiVectorKey[anim->mNumScalingKeys];
anim->mScalingKeys->mTime = 0.f; anim->mScalingKeys->mTime = 0.f;
anim->mScalingKeys->mValue.x = node.scale.value[0]; anim->mScalingKeys->mValue.x = node.scale.value[0];
anim->mScalingKeys->mValue.y = node.scale.value[1]; anim->mScalingKeys->mValue.y = node.scale.value[1];
@ -1130,6 +1130,7 @@ void glTF2Importer::ImportAnimations(glTF2::Asset& r)
// Use the latest keyframe for the duration of the animation // Use the latest keyframe for the duration of the animation
double maxDuration = 0; double maxDuration = 0;
unsigned int maxNumberOfKeys = 0;
for (unsigned int j = 0; j < ai_anim->mNumChannels; ++j) { for (unsigned int j = 0; j < ai_anim->mNumChannels; ++j) {
auto chan = ai_anim->mChannels[j]; auto chan = ai_anim->mChannels[j];
if (chan->mNumPositionKeys) { if (chan->mNumPositionKeys) {
@ -1137,21 +1138,25 @@ void glTF2Importer::ImportAnimations(glTF2::Asset& r)
if (lastPosKey.mTime > maxDuration) { if (lastPosKey.mTime > maxDuration) {
maxDuration = lastPosKey.mTime; maxDuration = lastPosKey.mTime;
} }
maxNumberOfKeys = std::max(maxNumberOfKeys, chan->mNumPositionKeys);
} }
if (chan->mNumRotationKeys) { if (chan->mNumRotationKeys) {
auto lastRotKey = chan->mRotationKeys[chan->mNumRotationKeys - 1]; auto lastRotKey = chan->mRotationKeys[chan->mNumRotationKeys - 1];
if (lastRotKey.mTime > maxDuration) { if (lastRotKey.mTime > maxDuration) {
maxDuration = lastRotKey.mTime; maxDuration = lastRotKey.mTime;
} }
maxNumberOfKeys = std::max(maxNumberOfKeys, chan->mNumRotationKeys);
} }
if (chan->mNumScalingKeys) { if (chan->mNumScalingKeys) {
auto lastScaleKey = chan->mScalingKeys[chan->mNumScalingKeys - 1]; auto lastScaleKey = chan->mScalingKeys[chan->mNumScalingKeys - 1];
if (lastScaleKey.mTime > maxDuration) { if (lastScaleKey.mTime > maxDuration) {
maxDuration = lastScaleKey.mTime; maxDuration = lastScaleKey.mTime;
} }
maxNumberOfKeys = std::max(maxNumberOfKeys, chan->mNumScalingKeys);
} }
} }
ai_anim->mDuration = maxDuration; ai_anim->mDuration = maxDuration;
ai_anim->mTicksPerSecond = (maxNumberOfKeys > 0 && maxDuration > 0) ? (maxNumberOfKeys / (maxDuration/1000)) : 30;
mScene->mAnimations[i] = ai_anim; mScene->mAnimations[i] = ai_anim;
} }

View File

@ -65,6 +65,7 @@ struct aiLight;
struct aiMetadata; struct aiMetadata;
struct aiBone; struct aiBone;
struct aiMesh; struct aiMesh;
struct aiAnimMesh;
struct aiAnimation; struct aiAnimation;
struct aiNodeAnim; struct aiNodeAnim;
@ -363,6 +364,7 @@ public:
static void Copy (aiMesh** dest, const aiMesh* src); static void Copy (aiMesh** dest, const aiMesh* src);
// similar to Copy(): // similar to Copy():
static void Copy (aiAnimMesh** dest, const aiAnimMesh* src);
static void Copy (aiMaterial** dest, const aiMaterial* src); static void Copy (aiMaterial** dest, const aiMaterial* src);
static void Copy (aiTexture** dest, const aiTexture* src); static void Copy (aiTexture** dest, const aiTexture* src);
static void Copy (aiAnimation** dest, const aiAnimation* src); static void Copy (aiAnimation** dest, const aiAnimation* src);

View File

@ -0,0 +1,282 @@
{
"accessors": [
{
"bufferView": 0,
"componentType": 5126,
"count": 24,
"type": "VEC3"
},
{
"bufferView": 1,
"componentType": 5126,
"count": 24,
"type": "VEC4"
},
{
"bufferView": 2,
"componentType": 5126,
"count": 24,
"type": "VEC3",
"max": [
0.0100000035,
0.0100000035,
0.01
],
"min": [
-0.0100000044,
-0.0100000054,
-0.01
]
},
{
"bufferView": 3,
"componentType": 5126,
"count": 24,
"type": "VEC3",
"name": "thin"
},
{
"bufferView": 4,
"componentType": 5126,
"count": 24,
"type": "VEC3",
"max": [
0.0,
0.01893253,
0.0
],
"min": [
0.0,
0.0,
0.0
],
"name": "thin"
},
{
"bufferView": 5,
"componentType": 5126,
"count": 24,
"type": "VEC3",
"name": "thin"
},
{
"bufferView": 6,
"componentType": 5126,
"count": 24,
"type": "VEC3",
"name": "angle"
},
{
"bufferView": 7,
"componentType": 5126,
"count": 24,
"type": "VEC3",
"max": [
0.0,
0.0198908355,
0.0
],
"min": [
0.0,
0.0,
0.0
],
"name": "angle"
},
{
"bufferView": 8,
"componentType": 5126,
"count": 24,
"type": "VEC3",
"name": "angle"
},
{
"bufferView": 9,
"componentType": 5123,
"count": 36,
"type": "SCALAR"
},
{
"bufferView": 10,
"componentType": 5126,
"count": 127,
"type": "SCALAR",
"max": [
4.19999743
],
"min": [
0.0
]
},
{
"bufferView": 11,
"componentType": 5126,
"count": 254,
"type": "SCALAR"
}
],
"animations": [
{
"channels": [
{
"sampler": 0,
"target": {
"node": 0,
"path": "weights"
}
}
],
"samplers": [
{
"input": 10,
"interpolation": "LINEAR",
"output": 11
}
],
"name": "Square"
}
],
"asset": {
"generator": "glTF Tools for Unity",
"version": "2.0"
},
"bufferViews": [
{
"buffer": 0,
"byteLength": 288
},
{
"buffer": 0,
"byteOffset": 288,
"byteLength": 384
},
{
"buffer": 0,
"byteOffset": 672,
"byteLength": 288
},
{
"buffer": 0,
"byteOffset": 960,
"byteLength": 288
},
{
"buffer": 0,
"byteOffset": 1248,
"byteLength": 288
},
{
"buffer": 0,
"byteOffset": 1536,
"byteLength": 288
},
{
"buffer": 0,
"byteOffset": 1824,
"byteLength": 288
},
{
"buffer": 0,
"byteOffset": 2112,
"byteLength": 288
},
{
"buffer": 0,
"byteOffset": 2400,
"byteLength": 288
},
{
"buffer": 0,
"byteOffset": 2688,
"byteLength": 72
},
{
"buffer": 0,
"byteOffset": 2760,
"byteLength": 508
},
{
"buffer": 0,
"byteOffset": 3268,
"byteLength": 1016
}
],
"buffers": [
{
"uri": "AnimatedMorphCube.bin",
"byteLength": 4284
}
],
"meshes": [
{
"primitives": [
{
"attributes": {
"NORMAL": 0,
"TANGENT": 1,
"POSITION": 2
},
"indices": 9,
"material": 0,
"targets": [
{
"NORMAL": 3,
"POSITION": 4,
"TANGENT": 5
},
{
"NORMAL": 6,
"POSITION": 7,
"TANGENT": 8
}
]
}
],
"weights": [
0.0,
0.0
],
"name": "Cube"
}
],
"materials": [
{
"pbrMetallicRoughness": {
"baseColorFactor": [
0.6038274,
0.6038274,
0.6038274,
1.0
],
"metallicFactor": 0.0,
"roughnessFactor": 0.5
},
"name": "Material"
}
],
"nodes": [
{
"mesh": 0,
"rotation": [
0.0,
0.7071067,
-0.7071068,
0.0
],
"scale": [
100.0,
100.0,
100.0
],
"name": "AnimatedMorphCube"
}
],
"scene": 0,
"scenes": [
{
"nodes": [
0
]
}
]
}

View File

@ -380,4 +380,13 @@ TEST_F( utglTF2ImportExport, exportglTF2FromFileTest ) {
EXPECT_TRUE( exporterTest() ); EXPECT_TRUE( exporterTest() );
} }
TEST_F( utglTF2ImportExport, crash_in_anim_mesh_destructor ) {
Assimp::Importer importer;
const aiScene *scene = importer.ReadFile( ASSIMP_TEST_MODELS_DIR "/glTF2/glTF-Sample-Models/AnimatedMorphCube-glTF/AnimatedMorphCube.gltf",
aiProcess_ValidateDataStructure);
ASSERT_NE( nullptr, scene );
Assimp::Exporter exporter;
ASSERT_EQ(aiReturn_SUCCESS, exporter.Export(scene, "glb2", ASSIMP_TEST_MODELS_DIR "/glTF2/glTF-Sample-Models/AnimatedMorphCube-glTF/AnimatedMorphCube_out.glTF"));
}
#endif // ASSIMP_BUILD_NO_EXPORT #endif // ASSIMP_BUILD_NO_EXPORT