Merge branch 'master' into fix_gltf2_export_componentType_error
commit
327a43c26f
|
@ -208,8 +208,15 @@ OPTION(ASSIMP_BUILD_ALL_IMPORTERS_BY_DEFAULT "default value of all ASSIMP_BUILD_
|
||||||
# macro to add the CMake Option ADD_ASSIMP_IMPORTER_<name> which enables compile of loader
|
# macro to add the CMake Option ADD_ASSIMP_IMPORTER_<name> which enables compile of loader
|
||||||
# this way selective loaders can be compiled (reduces filesize + compile time)
|
# this way selective loaders can be compiled (reduces filesize + compile time)
|
||||||
MACRO(ADD_ASSIMP_IMPORTER name)
|
MACRO(ADD_ASSIMP_IMPORTER name)
|
||||||
OPTION(ASSIMP_BUILD_${name}_IMPORTER "build the ${name} importer" ${ASSIMP_BUILD_ALL_IMPORTERS_BY_DEFAULT})
|
IF (ASSIMP_BUILD_ALL_IMPORTERS_BY_DEFAULT)
|
||||||
IF(ASSIMP_BUILD_${name}_IMPORTER)
|
set(ASSIMP_IMPORTER_ENABLED TRUE)
|
||||||
|
IF (DEFINED ASSIMP_BUILD_${name}_IMPORTER AND NOT ASSIMP_BUILD_${name}_IMPORTER)
|
||||||
|
set(ASSIMP_IMPORTER_ENABLED FALSE)
|
||||||
|
ENDIF ()
|
||||||
|
ELSE ()
|
||||||
|
set(ASSIMP_IMPORTER_ENABLED ${ASSIMP_BUILD_${name}_IMPORTER})
|
||||||
|
ENDIF ()
|
||||||
|
IF (ASSIMP_IMPORTER_ENABLED)
|
||||||
LIST(APPEND ASSIMP_LOADER_SRCS ${ARGN})
|
LIST(APPEND ASSIMP_LOADER_SRCS ${ARGN})
|
||||||
SET(ASSIMP_IMPORTERS_ENABLED "${ASSIMP_IMPORTERS_ENABLED} ${name}")
|
SET(ASSIMP_IMPORTERS_ENABLED "${ASSIMP_IMPORTERS_ENABLED} ${name}")
|
||||||
SET(${name}_SRCS ${ARGN})
|
SET(${name}_SRCS ${ARGN})
|
||||||
|
|
|
@ -154,9 +154,9 @@ Exporter::ExportFormatEntry gExporters[] =
|
||||||
aiProcess_JoinIdenticalVertices | aiProcess_Triangulate | aiProcess_SortByPType ),
|
aiProcess_JoinIdenticalVertices | aiProcess_Triangulate | aiProcess_SortByPType ),
|
||||||
Exporter::ExportFormatEntry( "glb", "GL Transmission Format (binary)", "glb", &ExportSceneGLB,
|
Exporter::ExportFormatEntry( "glb", "GL Transmission Format (binary)", "glb", &ExportSceneGLB,
|
||||||
aiProcess_JoinIdenticalVertices | aiProcess_Triangulate | aiProcess_SortByPType ),
|
aiProcess_JoinIdenticalVertices | aiProcess_Triangulate | aiProcess_SortByPType ),
|
||||||
Exporter::ExportFormatEntry( "gltf2", "GL Transmission Format v. 2", "gltf2", &ExportSceneGLTF2,
|
Exporter::ExportFormatEntry( "gltf2", "GL Transmission Format v. 2", "gltf", &ExportSceneGLTF2,
|
||||||
aiProcess_JoinIdenticalVertices | aiProcess_Triangulate | aiProcess_SortByPType ),
|
aiProcess_JoinIdenticalVertices | aiProcess_Triangulate | aiProcess_SortByPType ),
|
||||||
Exporter::ExportFormatEntry( "glb2", "GL Transmission Format v. 2 (binary)", "glb2", &ExportSceneGLB2,
|
Exporter::ExportFormatEntry( "glb2", "GL Transmission Format v. 2 (binary)", "glb", &ExportSceneGLB2,
|
||||||
aiProcess_JoinIdenticalVertices | aiProcess_Triangulate | aiProcess_SortByPType ),
|
aiProcess_JoinIdenticalVertices | aiProcess_Triangulate | aiProcess_SortByPType ),
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
@ -874,7 +874,7 @@ void Converter::ConvertModel( const Model& model, aiNode& nd, const aiMatrix4x4&
|
||||||
|
|
||||||
const MeshGeometry* const mesh = dynamic_cast< const MeshGeometry* >( geo );
|
const MeshGeometry* const mesh = dynamic_cast< const MeshGeometry* >( geo );
|
||||||
if ( mesh ) {
|
if ( mesh ) {
|
||||||
const std::vector<unsigned int>& indices = ConvertMesh( *mesh, model, node_global_transform );
|
const std::vector<unsigned int>& indices = ConvertMesh( *mesh, model, node_global_transform, nd);
|
||||||
std::copy( indices.begin(), indices.end(), std::back_inserter( meshes ) );
|
std::copy( indices.begin(), indices.end(), std::back_inserter( meshes ) );
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -891,7 +891,7 @@ void Converter::ConvertModel( const Model& model, aiNode& nd, const aiMatrix4x4&
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<unsigned int> Converter::ConvertMesh( const MeshGeometry& mesh, const Model& model,
|
std::vector<unsigned int> Converter::ConvertMesh( const MeshGeometry& mesh, const Model& model,
|
||||||
const aiMatrix4x4& node_global_transform )
|
const aiMatrix4x4& node_global_transform, aiNode& nd)
|
||||||
{
|
{
|
||||||
std::vector<unsigned int> temp;
|
std::vector<unsigned int> temp;
|
||||||
|
|
||||||
|
@ -915,17 +915,17 @@ std::vector<unsigned int> Converter::ConvertMesh( const MeshGeometry& mesh, cons
|
||||||
const MatIndexArray::value_type base = mindices[ 0 ];
|
const MatIndexArray::value_type base = mindices[ 0 ];
|
||||||
for( MatIndexArray::value_type index : mindices ) {
|
for( MatIndexArray::value_type index : mindices ) {
|
||||||
if ( index != base ) {
|
if ( index != base ) {
|
||||||
return ConvertMeshMultiMaterial( mesh, model, node_global_transform );
|
return ConvertMeshMultiMaterial( mesh, model, node_global_transform, nd);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// faster code-path, just copy the data
|
// faster code-path, just copy the data
|
||||||
temp.push_back( ConvertMeshSingleMaterial( mesh, model, node_global_transform ) );
|
temp.push_back( ConvertMeshSingleMaterial( mesh, model, node_global_transform, nd) );
|
||||||
return temp;
|
return temp;
|
||||||
}
|
}
|
||||||
|
|
||||||
aiMesh* Converter::SetupEmptyMesh( const MeshGeometry& mesh )
|
aiMesh* Converter::SetupEmptyMesh( const MeshGeometry& mesh, aiNode& nd)
|
||||||
{
|
{
|
||||||
aiMesh* const out_mesh = new aiMesh();
|
aiMesh* const out_mesh = new aiMesh();
|
||||||
meshes.push_back( out_mesh );
|
meshes.push_back( out_mesh );
|
||||||
|
@ -940,15 +940,19 @@ aiMesh* Converter::SetupEmptyMesh( const MeshGeometry& mesh )
|
||||||
if ( name.length() ) {
|
if ( name.length() ) {
|
||||||
out_mesh->mName.Set( name );
|
out_mesh->mName.Set( name );
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
out_mesh->mName = nd.mName;
|
||||||
|
}
|
||||||
|
|
||||||
return out_mesh;
|
return out_mesh;
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned int Converter::ConvertMeshSingleMaterial( const MeshGeometry& mesh, const Model& model,
|
unsigned int Converter::ConvertMeshSingleMaterial( const MeshGeometry& mesh, const Model& model,
|
||||||
const aiMatrix4x4& node_global_transform )
|
const aiMatrix4x4& node_global_transform, aiNode& nd)
|
||||||
{
|
{
|
||||||
const MatIndexArray& mindices = mesh.GetMaterialIndices();
|
const MatIndexArray& mindices = mesh.GetMaterialIndices();
|
||||||
aiMesh* const out_mesh = SetupEmptyMesh( mesh );
|
aiMesh* const out_mesh = SetupEmptyMesh(mesh, nd);
|
||||||
|
|
||||||
const std::vector<aiVector3D>& vertices = mesh.GetVertices();
|
const std::vector<aiVector3D>& vertices = mesh.GetVertices();
|
||||||
const std::vector<unsigned int>& faces = mesh.GetFaceIndexCounts();
|
const std::vector<unsigned int>& faces = mesh.GetFaceIndexCounts();
|
||||||
|
@ -1072,7 +1076,7 @@ unsigned int Converter::ConvertMeshSingleMaterial( const MeshGeometry& mesh, con
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<unsigned int> Converter::ConvertMeshMultiMaterial( const MeshGeometry& mesh, const Model& model,
|
std::vector<unsigned int> Converter::ConvertMeshMultiMaterial( const MeshGeometry& mesh, const Model& model,
|
||||||
const aiMatrix4x4& node_global_transform )
|
const aiMatrix4x4& node_global_transform, aiNode& nd)
|
||||||
{
|
{
|
||||||
const MatIndexArray& mindices = mesh.GetMaterialIndices();
|
const MatIndexArray& mindices = mesh.GetMaterialIndices();
|
||||||
ai_assert( mindices.size() );
|
ai_assert( mindices.size() );
|
||||||
|
@ -1083,7 +1087,7 @@ std::vector<unsigned int> Converter::ConvertMeshMultiMaterial( const MeshGeometr
|
||||||
for( MatIndexArray::value_type index : mindices ) {
|
for( MatIndexArray::value_type index : mindices ) {
|
||||||
if ( had.find( index ) == had.end() ) {
|
if ( had.find( index ) == had.end() ) {
|
||||||
|
|
||||||
indices.push_back( ConvertMeshMultiMaterial( mesh, model, index, node_global_transform ) );
|
indices.push_back( ConvertMeshMultiMaterial( mesh, model, index, node_global_transform, nd) );
|
||||||
had.insert( index );
|
had.insert( index );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1093,9 +1097,10 @@ std::vector<unsigned int> Converter::ConvertMeshMultiMaterial( const MeshGeometr
|
||||||
|
|
||||||
unsigned int Converter::ConvertMeshMultiMaterial( const MeshGeometry& mesh, const Model& model,
|
unsigned int Converter::ConvertMeshMultiMaterial( const MeshGeometry& mesh, const Model& model,
|
||||||
MatIndexArray::value_type index,
|
MatIndexArray::value_type index,
|
||||||
const aiMatrix4x4& node_global_transform )
|
const aiMatrix4x4& node_global_transform,
|
||||||
|
aiNode& nd)
|
||||||
{
|
{
|
||||||
aiMesh* const out_mesh = SetupEmptyMesh( mesh );
|
aiMesh* const out_mesh = SetupEmptyMesh(mesh, nd);
|
||||||
|
|
||||||
const MatIndexArray& mindices = mesh.GetMaterialIndices();
|
const MatIndexArray& mindices = mesh.GetMaterialIndices();
|
||||||
const std::vector<aiVector3D>& vertices = mesh.GetVertices();
|
const std::vector<aiVector3D>& vertices = mesh.GetVertices();
|
||||||
|
|
|
@ -172,23 +172,23 @@ private:
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
// MeshGeometry -> aiMesh, return mesh index + 1 or 0 if the conversion failed
|
// MeshGeometry -> aiMesh, return mesh index + 1 or 0 if the conversion failed
|
||||||
std::vector<unsigned int> ConvertMesh(const MeshGeometry& mesh, const Model& model,
|
std::vector<unsigned int> ConvertMesh(const MeshGeometry& mesh, const Model& model,
|
||||||
const aiMatrix4x4& node_global_transform);
|
const aiMatrix4x4& node_global_transform, aiNode& nd);
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
aiMesh* SetupEmptyMesh(const MeshGeometry& mesh);
|
aiMesh* SetupEmptyMesh(const MeshGeometry& mesh, aiNode& nd);
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
unsigned int ConvertMeshSingleMaterial(const MeshGeometry& mesh, const Model& model,
|
unsigned int ConvertMeshSingleMaterial(const MeshGeometry& mesh, const Model& model,
|
||||||
const aiMatrix4x4& node_global_transform);
|
const aiMatrix4x4& node_global_transform, aiNode& nd);
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
std::vector<unsigned int> ConvertMeshMultiMaterial(const MeshGeometry& mesh, const Model& model,
|
std::vector<unsigned int> ConvertMeshMultiMaterial(const MeshGeometry& mesh, const Model& model,
|
||||||
const aiMatrix4x4& node_global_transform);
|
const aiMatrix4x4& node_global_transform, aiNode& nd);
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
unsigned int ConvertMeshMultiMaterial(const MeshGeometry& mesh, const Model& model,
|
unsigned int ConvertMeshMultiMaterial(const MeshGeometry& mesh, const Model& model,
|
||||||
MatIndexArray::value_type index,
|
MatIndexArray::value_type index,
|
||||||
const aiMatrix4x4& node_global_transform);
|
const aiMatrix4x4& node_global_transform, aiNode& nd);
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
static const unsigned int NO_MATERIAL_SEPARATION = /* std::numeric_limits<unsigned int>::max() */
|
static const unsigned int NO_MATERIAL_SEPARATION = /* std::numeric_limits<unsigned int>::max() */
|
||||||
|
|
|
@ -108,6 +108,125 @@ void JoinVerticesProcess::Execute( aiScene* pScene)
|
||||||
pScene->mFlags |= AI_SCENE_FLAGS_NON_VERBOSE_FORMAT;
|
pScene->mFlags |= AI_SCENE_FLAGS_NON_VERBOSE_FORMAT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
bool areVerticesEqual(const Vertex &lhs, const Vertex &rhs, bool complex)
|
||||||
|
{
|
||||||
|
// A little helper to find locally close vertices faster.
|
||||||
|
// Try to reuse the lookup table from the last step.
|
||||||
|
const static float epsilon = 1e-5f;
|
||||||
|
// Squared because we check against squared length of the vector difference
|
||||||
|
static const float squareEpsilon = epsilon * epsilon;
|
||||||
|
|
||||||
|
// Square compare is useful for animeshes vertexes compare
|
||||||
|
if ((lhs.position - rhs.position).SquareLength() > squareEpsilon) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We just test the other attributes even if they're not present in the mesh.
|
||||||
|
// In this case they're initialized to 0 so the comparison succeeds.
|
||||||
|
// By this method the non-present attributes are effectively ignored in the comparison.
|
||||||
|
if ((lhs.normal - rhs.normal).SquareLength() > squareEpsilon) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((lhs.texcoords[0] - rhs.texcoords[0]).SquareLength() > squareEpsilon) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((lhs.tangent - rhs.tangent).SquareLength() > squareEpsilon) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((lhs.bitangent - rhs.bitangent).SquareLength() > squareEpsilon) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Usually we won't have vertex colors or multiple UVs, so we can skip from here
|
||||||
|
// Actually this increases runtime performance slightly, at least if branch
|
||||||
|
// prediction is on our side.
|
||||||
|
if (complex) {
|
||||||
|
for (int i = 0; i < 8; i++) {
|
||||||
|
if (i > 0 && (lhs.texcoords[i] - rhs.texcoords[i]).SquareLength() > squareEpsilon) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (GetColorDifference(lhs.colors[i], rhs.colors[i]) > squareEpsilon) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class XMesh>
|
||||||
|
void updateXMeshVertices(XMesh *pMesh, std::vector<Vertex> &uniqueVertices) {
|
||||||
|
// replace vertex data with the unique data sets
|
||||||
|
pMesh->mNumVertices = (unsigned int)uniqueVertices.size();
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// NOTE - we're *not* calling Vertex::SortBack() because it would check for
|
||||||
|
// presence of every single vertex component once PER VERTEX. And our CPU
|
||||||
|
// dislikes branches, even if they're easily predictable.
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// Position, if present (check made for aiAnimMesh)
|
||||||
|
if (pMesh->mVertices)
|
||||||
|
{
|
||||||
|
delete [] pMesh->mVertices;
|
||||||
|
pMesh->mVertices = new aiVector3D[pMesh->mNumVertices];
|
||||||
|
for (unsigned int a = 0; a < pMesh->mNumVertices; a++) {
|
||||||
|
pMesh->mVertices[a] = uniqueVertices[a].position;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Normals, if present
|
||||||
|
if (pMesh->mNormals)
|
||||||
|
{
|
||||||
|
delete [] pMesh->mNormals;
|
||||||
|
pMesh->mNormals = new aiVector3D[pMesh->mNumVertices];
|
||||||
|
for( unsigned int a = 0; a < pMesh->mNumVertices; a++) {
|
||||||
|
pMesh->mNormals[a] = uniqueVertices[a].normal;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Tangents, if present
|
||||||
|
if (pMesh->mTangents)
|
||||||
|
{
|
||||||
|
delete [] pMesh->mTangents;
|
||||||
|
pMesh->mTangents = new aiVector3D[pMesh->mNumVertices];
|
||||||
|
for (unsigned int a = 0; a < pMesh->mNumVertices; a++) {
|
||||||
|
pMesh->mTangents[a] = uniqueVertices[a].tangent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Bitangents as well
|
||||||
|
if (pMesh->mBitangents)
|
||||||
|
{
|
||||||
|
delete [] pMesh->mBitangents;
|
||||||
|
pMesh->mBitangents = new aiVector3D[pMesh->mNumVertices];
|
||||||
|
for (unsigned int a = 0; a < pMesh->mNumVertices; a++) {
|
||||||
|
pMesh->mBitangents[a] = uniqueVertices[a].bitangent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Vertex colors
|
||||||
|
for (unsigned int a = 0; pMesh->HasVertexColors(a); a++)
|
||||||
|
{
|
||||||
|
delete [] pMesh->mColors[a];
|
||||||
|
pMesh->mColors[a] = new aiColor4D[pMesh->mNumVertices];
|
||||||
|
for( unsigned int b = 0; b < pMesh->mNumVertices; b++) {
|
||||||
|
pMesh->mColors[a][b] = uniqueVertices[b].colors[a];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Texture coords
|
||||||
|
for (unsigned int a = 0; pMesh->HasTextureCoords(a); a++)
|
||||||
|
{
|
||||||
|
delete [] pMesh->mTextureCoords[a];
|
||||||
|
pMesh->mTextureCoords[a] = new aiVector3D[pMesh->mNumVertices];
|
||||||
|
for (unsigned int b = 0; b < pMesh->mNumVertices; b++) {
|
||||||
|
pMesh->mTextureCoords[a][b] = uniqueVertices[b].texcoords[a];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
// Unites identical vertices in the given mesh
|
// Unites identical vertices in the given mesh
|
||||||
int JoinVerticesProcess::ProcessMesh( aiMesh* pMesh, unsigned int meshIndex)
|
int JoinVerticesProcess::ProcessMesh( aiMesh* pMesh, unsigned int meshIndex)
|
||||||
|
@ -132,9 +251,6 @@ int JoinVerticesProcess::ProcessMesh( aiMesh* pMesh, unsigned int meshIndex)
|
||||||
static_assert(AI_MAX_VERTICES == 0x7fffffff, "AI_MAX_VERTICES == 0x7fffffff");
|
static_assert(AI_MAX_VERTICES == 0x7fffffff, "AI_MAX_VERTICES == 0x7fffffff");
|
||||||
std::vector<unsigned int> replaceIndex( pMesh->mNumVertices, 0xffffffff);
|
std::vector<unsigned int> replaceIndex( pMesh->mNumVertices, 0xffffffff);
|
||||||
|
|
||||||
// A little helper to find locally close vertices faster.
|
|
||||||
// Try to reuse the lookup table from the last step.
|
|
||||||
const static float epsilon = 1e-5f;
|
|
||||||
// float posEpsilonSqr;
|
// float posEpsilonSqr;
|
||||||
SpatialSort* vertexFinder = NULL;
|
SpatialSort* vertexFinder = NULL;
|
||||||
SpatialSort _vertexFinder;
|
SpatialSort _vertexFinder;
|
||||||
|
@ -156,9 +272,6 @@ int JoinVerticesProcess::ProcessMesh( aiMesh* pMesh, unsigned int meshIndex)
|
||||||
// posEpsilonSqr = ComputePositionEpsilon(pMesh);
|
// posEpsilonSqr = ComputePositionEpsilon(pMesh);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Squared because we check against squared length of the vector difference
|
|
||||||
static const float squareEpsilon = epsilon * epsilon;
|
|
||||||
|
|
||||||
// Again, better waste some bytes than a realloc ...
|
// Again, better waste some bytes than a realloc ...
|
||||||
std::vector<unsigned int> verticesFound;
|
std::vector<unsigned int> verticesFound;
|
||||||
verticesFound.reserve(10);
|
verticesFound.reserve(10);
|
||||||
|
@ -166,6 +279,16 @@ int JoinVerticesProcess::ProcessMesh( aiMesh* pMesh, unsigned int meshIndex)
|
||||||
// Run an optimized code path if we don't have multiple UVs or vertex colors.
|
// Run an optimized code path if we don't have multiple UVs or vertex colors.
|
||||||
// This should yield false in more than 99% of all imports ...
|
// This should yield false in more than 99% of all imports ...
|
||||||
const bool complex = ( pMesh->GetNumColorChannels() > 0 || pMesh->GetNumUVChannels() > 1);
|
const bool complex = ( pMesh->GetNumColorChannels() > 0 || pMesh->GetNumUVChannels() > 1);
|
||||||
|
const bool hasAnimMeshes = pMesh->mNumAnimMeshes > 0;
|
||||||
|
|
||||||
|
// We'll never have more vertices afterwards.
|
||||||
|
std::vector<std::vector<Vertex>> uniqueAnimatedVertices;
|
||||||
|
if (hasAnimMeshes) {
|
||||||
|
uniqueAnimatedVertices.resize(pMesh->mNumAnimMeshes);
|
||||||
|
for (unsigned int animMeshIndex = 0; animMeshIndex < pMesh->mNumAnimMeshes; animMeshIndex++) {
|
||||||
|
uniqueAnimatedVertices[animMeshIndex].reserve(pMesh->mNumVertices);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Now check each vertex if it brings something new to the table
|
// Now check each vertex if it brings something new to the table
|
||||||
for( unsigned int a = 0; a < pMesh->mNumVertices; a++) {
|
for( unsigned int a = 0; a < pMesh->mNumVertices; a++) {
|
||||||
|
@ -178,74 +301,32 @@ int JoinVerticesProcess::ProcessMesh( aiMesh* pMesh, unsigned int meshIndex)
|
||||||
|
|
||||||
// check all unique vertices close to the position if this vertex is already present among them
|
// check all unique vertices close to the position if this vertex is already present among them
|
||||||
for( unsigned int b = 0; b < verticesFound.size(); b++) {
|
for( unsigned int b = 0; b < verticesFound.size(); b++) {
|
||||||
|
|
||||||
const unsigned int vidx = verticesFound[b];
|
const unsigned int vidx = verticesFound[b];
|
||||||
const unsigned int uidx = replaceIndex[ vidx];
|
const unsigned int uidx = replaceIndex[ vidx];
|
||||||
if( uidx & 0x80000000)
|
if( uidx & 0x80000000)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
const Vertex& uv = uniqueVertices[ uidx];
|
const Vertex& uv = uniqueVertices[ uidx];
|
||||||
// Position mismatch is impossible - the vertex finder already discarded all non-matching positions
|
|
||||||
|
|
||||||
// We just test the other attributes even if they're not present in the mesh.
|
if (!areVerticesEqual(v, uv, complex)) {
|
||||||
// In this case they're initialized to 0 so the comparison succeeds.
|
|
||||||
// By this method the non-present attributes are effectively ignored in the comparison.
|
|
||||||
if( (uv.normal - v.normal).SquareLength() > squareEpsilon)
|
|
||||||
continue;
|
|
||||||
if( (uv.texcoords[0] - v.texcoords[0]).SquareLength() > squareEpsilon)
|
|
||||||
continue;
|
|
||||||
if( (uv.tangent - v.tangent).SquareLength() > squareEpsilon)
|
|
||||||
continue;
|
|
||||||
if( (uv.bitangent - v.bitangent).SquareLength() > squareEpsilon)
|
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// Usually we won't have vertex colors or multiple UVs, so we can skip from here
|
if (hasAnimMeshes) {
|
||||||
// Actually this increases runtime performance slightly, at least if branch
|
// If given vertex is animated, then it has to be preserver 1 to 1 (base mesh and animated mesh require same topology)
|
||||||
// prediction is on our side.
|
// NOTE: not doing this totaly breaks anim meshes as they don't have their own faces (they use pMesh->mFaces)
|
||||||
if (complex){
|
bool breaksAnimMesh = false;
|
||||||
// manually unrolled because continue wouldn't work as desired in an inner loop,
|
for (unsigned int animMeshIndex = 0; animMeshIndex < pMesh->mNumAnimMeshes; animMeshIndex++) {
|
||||||
// also because some compilers seem to fail the task. Colors and UV coords
|
const Vertex& animatedUV = uniqueAnimatedVertices[animMeshIndex][ uidx];
|
||||||
// are interleaved since the higher entries are most likely to be
|
Vertex aniMeshVertex(pMesh->mAnimMeshes[animMeshIndex], a);
|
||||||
// zero and thus useless. By interleaving the arrays, vertices are,
|
if (!areVerticesEqual(aniMeshVertex, animatedUV, complex)) {
|
||||||
// on average, rejected earlier.
|
breaksAnimMesh = true;
|
||||||
|
break;
|
||||||
if( (uv.texcoords[1] - v.texcoords[1]).SquareLength() > squareEpsilon)
|
}
|
||||||
continue;
|
}
|
||||||
if( GetColorDifference( uv.colors[0], v.colors[0]) > squareEpsilon)
|
if (breaksAnimMesh) {
|
||||||
continue;
|
|
||||||
|
|
||||||
if( (uv.texcoords[2] - v.texcoords[2]).SquareLength() > squareEpsilon)
|
|
||||||
continue;
|
|
||||||
if( GetColorDifference( uv.colors[1], v.colors[1]) > squareEpsilon)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if( (uv.texcoords[3] - v.texcoords[3]).SquareLength() > squareEpsilon)
|
|
||||||
continue;
|
|
||||||
if( GetColorDifference( uv.colors[2], v.colors[2]) > squareEpsilon)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if( (uv.texcoords[4] - v.texcoords[4]).SquareLength() > squareEpsilon)
|
|
||||||
continue;
|
|
||||||
if( GetColorDifference( uv.colors[3], v.colors[3]) > squareEpsilon)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if( (uv.texcoords[5] - v.texcoords[5]).SquareLength() > squareEpsilon)
|
|
||||||
continue;
|
|
||||||
if( GetColorDifference( uv.colors[4], v.colors[4]) > squareEpsilon)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if( (uv.texcoords[6] - v.texcoords[6]).SquareLength() > squareEpsilon)
|
|
||||||
continue;
|
|
||||||
if( GetColorDifference( uv.colors[5], v.colors[5]) > squareEpsilon)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if( (uv.texcoords[7] - v.texcoords[7]).SquareLength() > squareEpsilon)
|
|
||||||
continue;
|
|
||||||
if( GetColorDifference( uv.colors[6], v.colors[6]) > squareEpsilon)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if( GetColorDifference( uv.colors[7], v.colors[7]) > squareEpsilon)
|
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// we're still here -> this vertex perfectly matches our given vertex
|
// we're still here -> this vertex perfectly matches our given vertex
|
||||||
|
@ -264,6 +345,12 @@ int JoinVerticesProcess::ProcessMesh( aiMesh* pMesh, unsigned int meshIndex)
|
||||||
// no unique vertex matches it up to now -> so add it
|
// no unique vertex matches it up to now -> so add it
|
||||||
replaceIndex[a] = (unsigned int)uniqueVertices.size();
|
replaceIndex[a] = (unsigned int)uniqueVertices.size();
|
||||||
uniqueVertices.push_back( v);
|
uniqueVertices.push_back( v);
|
||||||
|
if (hasAnimMeshes) {
|
||||||
|
for (unsigned int animMeshIndex = 0; animMeshIndex < pMesh->mNumAnimMeshes; animMeshIndex++) {
|
||||||
|
Vertex aniMeshVertex(pMesh->mAnimMeshes[animMeshIndex], a);
|
||||||
|
uniqueAnimatedVertices[animMeshIndex].push_back(aniMeshVertex);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -281,64 +368,10 @@ int JoinVerticesProcess::ProcessMesh( aiMesh* pMesh, unsigned int meshIndex)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// replace vertex data with the unique data sets
|
updateXMeshVertices(pMesh, uniqueVertices);
|
||||||
pMesh->mNumVertices = (unsigned int)uniqueVertices.size();
|
if (hasAnimMeshes) {
|
||||||
|
for (unsigned int animMeshIndex = 0; animMeshIndex < pMesh->mNumAnimMeshes; animMeshIndex++) {
|
||||||
// ----------------------------------------------------------------------------
|
updateXMeshVertices(pMesh->mAnimMeshes[animMeshIndex], uniqueAnimatedVertices[animMeshIndex]);
|
||||||
// NOTE - we're *not* calling Vertex::SortBack() because it would check for
|
|
||||||
// presence of every single vertex component once PER VERTEX. And our CPU
|
|
||||||
// dislikes branches, even if they're easily predictable.
|
|
||||||
// ----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
// Position
|
|
||||||
delete [] pMesh->mVertices;
|
|
||||||
pMesh->mVertices = new aiVector3D[pMesh->mNumVertices];
|
|
||||||
for( unsigned int a = 0; a < pMesh->mNumVertices; a++)
|
|
||||||
pMesh->mVertices[a] = uniqueVertices[a].position;
|
|
||||||
|
|
||||||
// Normals, if present
|
|
||||||
if( pMesh->mNormals)
|
|
||||||
{
|
|
||||||
delete [] pMesh->mNormals;
|
|
||||||
pMesh->mNormals = new aiVector3D[pMesh->mNumVertices];
|
|
||||||
for( unsigned int a = 0; a < pMesh->mNumVertices; a++) {
|
|
||||||
pMesh->mNormals[a] = uniqueVertices[a].normal;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Tangents, if present
|
|
||||||
if( pMesh->mTangents)
|
|
||||||
{
|
|
||||||
delete [] pMesh->mTangents;
|
|
||||||
pMesh->mTangents = new aiVector3D[pMesh->mNumVertices];
|
|
||||||
for( unsigned int a = 0; a < pMesh->mNumVertices; a++) {
|
|
||||||
pMesh->mTangents[a] = uniqueVertices[a].tangent;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Bitangents as well
|
|
||||||
if( pMesh->mBitangents)
|
|
||||||
{
|
|
||||||
delete [] pMesh->mBitangents;
|
|
||||||
pMesh->mBitangents = new aiVector3D[pMesh->mNumVertices];
|
|
||||||
for( unsigned int a = 0; a < pMesh->mNumVertices; a++) {
|
|
||||||
pMesh->mBitangents[a] = uniqueVertices[a].bitangent;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Vertex colors
|
|
||||||
for( unsigned int a = 0; pMesh->HasVertexColors(a); a++)
|
|
||||||
{
|
|
||||||
delete [] pMesh->mColors[a];
|
|
||||||
pMesh->mColors[a] = new aiColor4D[pMesh->mNumVertices];
|
|
||||||
for( unsigned int b = 0; b < pMesh->mNumVertices; b++) {
|
|
||||||
pMesh->mColors[a][b] = uniqueVertices[b].colors[a];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Texture coords
|
|
||||||
for( unsigned int a = 0; pMesh->HasTextureCoords(a); a++)
|
|
||||||
{
|
|
||||||
delete [] pMesh->mTextureCoords[a];
|
|
||||||
pMesh->mTextureCoords[a] = new aiVector3D[pMesh->mNumVertices];
|
|
||||||
for( unsigned int b = 0; b < pMesh->mNumVertices; b++) {
|
|
||||||
pMesh->mTextureCoords[a][b] = uniqueVertices[b].texcoords[a];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -114,14 +114,13 @@ static const std::string MaterialExt = ".mtl";
|
||||||
ObjExporter::ObjExporter(const char* _filename, const aiScene* pScene, bool noMtl)
|
ObjExporter::ObjExporter(const char* _filename, const aiScene* pScene, bool noMtl)
|
||||||
: filename(_filename)
|
: filename(_filename)
|
||||||
, pScene(pScene)
|
, pScene(pScene)
|
||||||
, vp()
|
|
||||||
, vn()
|
, vn()
|
||||||
, vt()
|
, vt()
|
||||||
, vc()
|
, vp()
|
||||||
, mVpMap()
|
, useVc(false)
|
||||||
, mVnMap()
|
, mVnMap()
|
||||||
, mVtMap()
|
, mVtMap()
|
||||||
, mVcMap()
|
, mVpMap()
|
||||||
, mMeshes()
|
, mMeshes()
|
||||||
, endl("\n") {
|
, endl("\n") {
|
||||||
// make sure that all formatting happens using the standard, C locale and not the user's current locale
|
// make sure that all formatting happens using the standard, C locale and not the user's current locale
|
||||||
|
@ -268,27 +267,22 @@ void ObjExporter::WriteGeometryFile(bool noMtl) {
|
||||||
AddNode(pScene->mRootNode, mBase);
|
AddNode(pScene->mRootNode, mBase);
|
||||||
|
|
||||||
// write vertex positions with colors, if any
|
// write vertex positions with colors, if any
|
||||||
mVpMap.getVectors( vp );
|
mVpMap.getKeys( vp );
|
||||||
mVcMap.getColors( vc );
|
if ( !useVc ) {
|
||||||
if ( vc.empty() ) {
|
|
||||||
mOutput << "# " << vp.size() << " vertex positions" << endl;
|
mOutput << "# " << vp.size() << " vertex positions" << endl;
|
||||||
for ( const aiVector3D& v : vp ) {
|
for ( const vertexData& v : vp ) {
|
||||||
mOutput << "v " << v.x << " " << v.y << " " << v.z << endl;
|
mOutput << "v " << v.vp.x << " " << v.vp.y << " " << v.vp.z << endl;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
mOutput << "# " << vp.size() << " vertex positions and colors" << endl;
|
mOutput << "# " << vp.size() << " vertex positions and colors" << endl;
|
||||||
size_t colIdx = 0;
|
for ( const vertexData& v : vp ) {
|
||||||
for ( const aiVector3D& v : vp ) {
|
mOutput << "v " << v.vp.x << " " << v.vp.y << " " << v.vp.z << " " << v.vc.r << " " << v.vc.g << " " << v.vc.b << endl;
|
||||||
if ( colIdx < vc.size() ) {
|
|
||||||
mOutput << "v " << v.x << " " << v.y << " " << v.z << " " << vc[ colIdx ].r << " " << vc[ colIdx ].g << " " << vc[ colIdx ].b << endl;
|
|
||||||
}
|
|
||||||
++colIdx;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
mOutput << endl;
|
mOutput << endl;
|
||||||
|
|
||||||
// write uv coordinates
|
// write uv coordinates
|
||||||
mVtMap.getVectors(vt);
|
mVtMap.getKeys(vt);
|
||||||
mOutput << "# " << vt.size() << " UV coordinates" << endl;
|
mOutput << "# " << vt.size() << " UV coordinates" << endl;
|
||||||
for(const aiVector3D& v : vt) {
|
for(const aiVector3D& v : vt) {
|
||||||
mOutput << "vt " << v.x << " " << v.y << " " << v.z << endl;
|
mOutput << "vt " << v.x << " " << v.y << " " << v.z << endl;
|
||||||
|
@ -296,7 +290,7 @@ void ObjExporter::WriteGeometryFile(bool noMtl) {
|
||||||
mOutput << endl;
|
mOutput << endl;
|
||||||
|
|
||||||
// write vertex normals
|
// write vertex normals
|
||||||
mVnMap.getVectors(vn);
|
mVnMap.getKeys(vn);
|
||||||
mOutput << "# " << vn.size() << " vertex normals" << endl;
|
mOutput << "# " << vn.size() << " vertex normals" << endl;
|
||||||
for(const aiVector3D& v : vn) {
|
for(const aiVector3D& v : vn) {
|
||||||
mOutput << "vn " << v.x << " " << v.y << " " << v.z << endl;
|
mOutput << "vn " << v.x << " " << v.y << " " << v.z << endl;
|
||||||
|
@ -337,54 +331,15 @@ void ObjExporter::WriteGeometryFile(bool noMtl) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
|
||||||
int ObjExporter::vecIndexMap::getIndex(const aiVector3D& vec) {
|
|
||||||
vecIndexMap::dataType::iterator vertIt = vecMap.find(vec);
|
|
||||||
// vertex already exists, so reference it
|
|
||||||
if(vertIt != vecMap.end()){
|
|
||||||
return vertIt->second;
|
|
||||||
}
|
|
||||||
vecMap[vec] = mNextIndex;
|
|
||||||
int ret = mNextIndex;
|
|
||||||
mNextIndex++;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
|
||||||
void ObjExporter::vecIndexMap::getVectors( std::vector<aiVector3D>& vecs ) {
|
|
||||||
vecs.resize(vecMap.size());
|
|
||||||
for(vecIndexMap::dataType::iterator it = vecMap.begin(); it != vecMap.end(); ++it){
|
|
||||||
vecs[it->second-1] = it->first;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
|
||||||
int ObjExporter::colIndexMap::getIndex( const aiColor4D& col ) {
|
|
||||||
colIndexMap::dataType::iterator vertIt = colMap.find( col );
|
|
||||||
// vertex already exists, so reference it
|
|
||||||
if ( vertIt != colMap.end() ) {
|
|
||||||
return vertIt->second;
|
|
||||||
}
|
|
||||||
colMap[ col ] = mNextIndex;
|
|
||||||
int ret = mNextIndex;
|
|
||||||
mNextIndex++;
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
|
||||||
void ObjExporter::colIndexMap::getColors( std::vector<aiColor4D> &colors ) {
|
|
||||||
colors.resize( colMap.size() );
|
|
||||||
for ( colIndexMap::dataType::iterator it = colMap.begin(); it != colMap.end(); ++it ) {
|
|
||||||
colors[ it->second - 1 ] = it->first;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
void ObjExporter::AddMesh(const aiString& name, const aiMesh* m, const aiMatrix4x4& mat) {
|
void ObjExporter::AddMesh(const aiString& name, const aiMesh* m, const aiMatrix4x4& mat) {
|
||||||
mMeshes.push_back(MeshInstance() );
|
mMeshes.push_back(MeshInstance() );
|
||||||
MeshInstance& mesh = mMeshes.back();
|
MeshInstance& mesh = mMeshes.back();
|
||||||
|
|
||||||
|
if ( nullptr != m->mColors[ 0 ] ) {
|
||||||
|
useVc = true;
|
||||||
|
}
|
||||||
|
|
||||||
mesh.name = std::string( name.data, name.length );
|
mesh.name = std::string( name.data, name.length );
|
||||||
mesh.matname = GetMaterialName(m->mMaterialIndex);
|
mesh.matname = GetMaterialName(m->mMaterialIndex);
|
||||||
|
|
||||||
|
@ -410,7 +365,13 @@ void ObjExporter::AddMesh(const aiString& name, const aiMesh* m, const aiMatrix4
|
||||||
const unsigned int idx = f.mIndices[a];
|
const unsigned int idx = f.mIndices[a];
|
||||||
|
|
||||||
aiVector3D vert = mat * m->mVertices[idx];
|
aiVector3D vert = mat * m->mVertices[idx];
|
||||||
face.indices[a].vp = mVpMap.getIndex(vert);
|
|
||||||
|
if ( nullptr != m->mColors[ 0 ] ) {
|
||||||
|
aiColor4D col4 = m->mColors[ 0 ][ idx ];
|
||||||
|
face.indices[a].vp = mVpMap.getIndex({vert, aiColor3D(col4.r, col4.g, col4.b)});
|
||||||
|
} else {
|
||||||
|
face.indices[a].vp = mVpMap.getIndex({vert, aiColor3D(0,0,0)});
|
||||||
|
}
|
||||||
|
|
||||||
if (m->mNormals) {
|
if (m->mNormals) {
|
||||||
aiVector3D norm = aiMatrix3x3(mat) * m->mNormals[idx];
|
aiVector3D norm = aiMatrix3x3(mat) * m->mNormals[idx];
|
||||||
|
@ -419,13 +380,6 @@ void ObjExporter::AddMesh(const aiString& name, const aiMesh* m, const aiMatrix4
|
||||||
face.indices[a].vn = 0;
|
face.indices[a].vn = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( nullptr != m->mColors[ 0 ] ) {
|
|
||||||
aiColor4D col4 = m->mColors[ 0 ][ idx ];
|
|
||||||
face.indices[ a ].vc = mVcMap.getIndex( col4 );
|
|
||||||
} else {
|
|
||||||
face.indices[ a ].vc = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( m->mTextureCoords[ 0 ] ) {
|
if ( m->mTextureCoords[ 0 ] ) {
|
||||||
face.indices[a].vt = mVtMap.getIndex(m->mTextureCoords[0][idx]);
|
face.indices[a].vt = mVtMap.getIndex(m->mTextureCoords[0][idx]);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -77,13 +77,12 @@ private:
|
||||||
FaceVertex()
|
FaceVertex()
|
||||||
: vp()
|
: vp()
|
||||||
, vn()
|
, vn()
|
||||||
, vt()
|
, vt() {
|
||||||
, vc() {
|
|
||||||
// empty
|
// empty
|
||||||
}
|
}
|
||||||
|
|
||||||
// one-based, 0 means: 'does not exist'
|
// one-based, 0 means: 'does not exist'
|
||||||
unsigned int vp, vn, vt, vc;
|
unsigned int vp, vn, vt;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Face {
|
struct Face {
|
||||||
|
@ -106,8 +105,37 @@ private:
|
||||||
private:
|
private:
|
||||||
std::string filename;
|
std::string filename;
|
||||||
const aiScene* const pScene;
|
const aiScene* const pScene;
|
||||||
std::vector<aiVector3D> vp, vn, vt;
|
|
||||||
|
struct vertexData {
|
||||||
|
aiVector3D vp;
|
||||||
|
aiColor3D vc; // OBJ does not support 4D color
|
||||||
|
};
|
||||||
|
|
||||||
|
std::vector<aiVector3D> vn, vt;
|
||||||
std::vector<aiColor4D> vc;
|
std::vector<aiColor4D> vc;
|
||||||
|
std::vector<vertexData> vp;
|
||||||
|
bool useVc;
|
||||||
|
|
||||||
|
struct vertexDataCompare {
|
||||||
|
bool operator() ( const vertexData& a, const vertexData& b ) const {
|
||||||
|
// position
|
||||||
|
if (a.vp.x < b.vp.x) return true;
|
||||||
|
if (a.vp.x > b.vp.x) return false;
|
||||||
|
if (a.vp.y < b.vp.y) return true;
|
||||||
|
if (a.vp.y > b.vp.y) return false;
|
||||||
|
if (a.vp.z < b.vp.z) return true;
|
||||||
|
if (a.vp.z > b.vp.z) return false;
|
||||||
|
|
||||||
|
// color
|
||||||
|
if (a.vc.r < b.vc.r) return true;
|
||||||
|
if (a.vc.r > b.vc.r) return false;
|
||||||
|
if (a.vc.g < b.vc.g) return true;
|
||||||
|
if (a.vc.g > b.vc.g) return false;
|
||||||
|
if (a.vc.b < b.vc.b) return true;
|
||||||
|
if (a.vc.b > b.vc.b) return false;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
struct aiVectorCompare {
|
struct aiVectorCompare {
|
||||||
bool operator() (const aiVector3D& a, const aiVector3D& b) const {
|
bool operator() (const aiVector3D& a, const aiVector3D& b) const {
|
||||||
|
@ -120,52 +148,37 @@ private:
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct aiColor4Compare {
|
template <class T, class Compare = std::less<T>>
|
||||||
bool operator() ( const aiColor4D& a, const aiColor4D& b ) const {
|
class indexMap {
|
||||||
if ( a.r < b.r ) return true;
|
|
||||||
if ( a.r > b.r ) return false;
|
|
||||||
if ( a.g < b.g ) return true;
|
|
||||||
if ( a.g > b.g ) return false;
|
|
||||||
if ( a.b < b.b ) return true;
|
|
||||||
if ( a.b > b.b ) return false;
|
|
||||||
if ( a.a < b.a ) return true;
|
|
||||||
if ( a.a > b.a ) return false;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class vecIndexMap {
|
|
||||||
int mNextIndex;
|
int mNextIndex;
|
||||||
typedef std::map<aiVector3D, int, aiVectorCompare> dataType;
|
typedef std::map<T, int, Compare> dataType;
|
||||||
dataType vecMap;
|
dataType vecMap;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
vecIndexMap()
|
indexMap()
|
||||||
: mNextIndex(1) {
|
: mNextIndex(1) {
|
||||||
// empty
|
// empty
|
||||||
}
|
}
|
||||||
|
|
||||||
int getIndex(const aiVector3D& vec);
|
int getIndex(const T& key) {
|
||||||
void getVectors( std::vector<aiVector3D>& vecs );
|
typename dataType::iterator vertIt = vecMap.find(key);
|
||||||
|
// vertex already exists, so reference it
|
||||||
|
if(vertIt != vecMap.end()){
|
||||||
|
return vertIt->second;
|
||||||
|
}
|
||||||
|
return vecMap[key] = mNextIndex++;
|
||||||
|
};
|
||||||
|
|
||||||
|
void getKeys( std::vector<T>& keys ) {
|
||||||
|
keys.resize(vecMap.size());
|
||||||
|
for(typename dataType::iterator it = vecMap.begin(); it != vecMap.end(); ++it){
|
||||||
|
keys[it->second-1] = it->first;
|
||||||
|
}
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
class colIndexMap {
|
indexMap<aiVector3D, aiVectorCompare> mVnMap, mVtMap;
|
||||||
int mNextIndex;
|
indexMap<vertexData, vertexDataCompare> mVpMap;
|
||||||
typedef std::map<aiColor4D, int, aiColor4Compare> dataType;
|
|
||||||
dataType colMap;
|
|
||||||
|
|
||||||
public:
|
|
||||||
colIndexMap()
|
|
||||||
: mNextIndex( 1 ) {
|
|
||||||
// empty
|
|
||||||
}
|
|
||||||
|
|
||||||
int getIndex( const aiColor4D& col );
|
|
||||||
void getColors( std::vector<aiColor4D> &colors );
|
|
||||||
};
|
|
||||||
|
|
||||||
vecIndexMap mVpMap, mVnMap, mVtMap;
|
|
||||||
colIndexMap mVcMap;
|
|
||||||
std::vector<MeshInstance> mMeshes;
|
std::vector<MeshInstance> mMeshes;
|
||||||
|
|
||||||
// this endl() doesn't flush() the stream
|
// this endl() doesn't flush() the stream
|
||||||
|
|
|
@ -194,16 +194,16 @@ PlyExporter::PlyExporter(const char* _filename, const aiScene* pScene, bool bina
|
||||||
|
|
||||||
for (unsigned int n = PLY_EXPORT_HAS_COLORS, c = 0; (components & n) && c != AI_MAX_NUMBER_OF_COLOR_SETS; n <<= 1, ++c) {
|
for (unsigned int n = PLY_EXPORT_HAS_COLORS, c = 0; (components & n) && c != AI_MAX_NUMBER_OF_COLOR_SETS; n <<= 1, ++c) {
|
||||||
if (!c) {
|
if (!c) {
|
||||||
mOutput << "property " << typeName << " r" << endl;
|
mOutput << "property " << "uchar" << " red" << endl;
|
||||||
mOutput << "property " << typeName << " g" << endl;
|
mOutput << "property " << "uchar" << " green" << endl;
|
||||||
mOutput << "property " << typeName << " b" << endl;
|
mOutput << "property " << "uchar" << " blue" << endl;
|
||||||
mOutput << "property " << typeName << " a" << endl;
|
mOutput << "property " << "uchar" << " alpha" << endl;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
mOutput << "property " << typeName << " r" << c << endl;
|
mOutput << "property " << "uchar" << " red" << c << endl;
|
||||||
mOutput << "property " << typeName << " g" << c << endl;
|
mOutput << "property " << "uchar" << " green" << c << endl;
|
||||||
mOutput << "property " << typeName << " b" << c << endl;
|
mOutput << "property " << "uchar" << " blue" << c << endl;
|
||||||
mOutput << "property " << typeName << " a" << c << endl;
|
mOutput << "property " << "uchar" << " alpha" << c << endl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -288,13 +288,13 @@ void PlyExporter::WriteMeshVerts(const aiMesh* m, unsigned int components)
|
||||||
for (unsigned int n = PLY_EXPORT_HAS_COLORS, c = 0; (components & n) && c != AI_MAX_NUMBER_OF_COLOR_SETS; n <<= 1, ++c) {
|
for (unsigned int n = PLY_EXPORT_HAS_COLORS, c = 0; (components & n) && c != AI_MAX_NUMBER_OF_COLOR_SETS; n <<= 1, ++c) {
|
||||||
if (m->HasVertexColors(c)) {
|
if (m->HasVertexColors(c)) {
|
||||||
mOutput <<
|
mOutput <<
|
||||||
" " << m->mColors[c][i].r <<
|
" " << (int)(m->mColors[c][i].r * 255) <<
|
||||||
" " << m->mColors[c][i].g <<
|
" " << (int)(m->mColors[c][i].g * 255) <<
|
||||||
" " << m->mColors[c][i].b <<
|
" " << (int)(m->mColors[c][i].b * 255) <<
|
||||||
" " << m->mColors[c][i].a;
|
" " << (int)(m->mColors[c][i].a * 255);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
mOutput << " -1.0 -1.0 -1.0 -1.0";
|
mOutput << " 0 0 0";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -85,8 +85,6 @@ void SortByPTypeProcess::SetupProperties(const Importer* pImp)
|
||||||
// Update changed meshes in all nodes
|
// Update changed meshes in all nodes
|
||||||
void UpdateNodes(const std::vector<unsigned int>& replaceMeshIndex, aiNode* node)
|
void UpdateNodes(const std::vector<unsigned int>& replaceMeshIndex, aiNode* node)
|
||||||
{
|
{
|
||||||
// std::vector<unsigned int>::const_iterator it;
|
|
||||||
|
|
||||||
if (node->mNumMeshes)
|
if (node->mNumMeshes)
|
||||||
{
|
{
|
||||||
unsigned int newSize = 0;
|
unsigned int newSize = 0;
|
||||||
|
@ -133,10 +131,8 @@ void UpdateNodes(const std::vector<unsigned int>& replaceMeshIndex, aiNode* node
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
// Executes the post processing step on the given imported data.
|
// Executes the post processing step on the given imported data.
|
||||||
void SortByPTypeProcess::Execute( aiScene* pScene)
|
void SortByPTypeProcess::Execute( aiScene* pScene) {
|
||||||
{
|
if ( 0 == pScene->mNumMeshes) {
|
||||||
if (!pScene->mNumMeshes)
|
|
||||||
{
|
|
||||||
ASSIMP_LOG_DEBUG("SortByPTypeProcess skipped, there are no meshes");
|
ASSIMP_LOG_DEBUG("SortByPTypeProcess skipped, there are no meshes");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -152,42 +148,38 @@ void SortByPTypeProcess::Execute( aiScene* pScene)
|
||||||
|
|
||||||
std::vector<unsigned int> replaceMeshIndex(pScene->mNumMeshes*4,UINT_MAX);
|
std::vector<unsigned int> replaceMeshIndex(pScene->mNumMeshes*4,UINT_MAX);
|
||||||
std::vector<unsigned int>::iterator meshIdx = replaceMeshIndex.begin();
|
std::vector<unsigned int>::iterator meshIdx = replaceMeshIndex.begin();
|
||||||
for (unsigned int i = 0; i < pScene->mNumMeshes;++i)
|
for (unsigned int i = 0; i < pScene->mNumMeshes; ++i) {
|
||||||
{
|
|
||||||
aiMesh* const mesh = pScene->mMeshes[i];
|
aiMesh* const mesh = pScene->mMeshes[i];
|
||||||
ai_assert(0 != mesh->mPrimitiveTypes);
|
ai_assert(0 != mesh->mPrimitiveTypes);
|
||||||
|
|
||||||
// if there's just one primitive type in the mesh there's nothing to do for us
|
// if there's just one primitive type in the mesh there's nothing to do for us
|
||||||
unsigned int num = 0;
|
unsigned int num = 0;
|
||||||
if (mesh->mPrimitiveTypes & aiPrimitiveType_POINT)
|
if (mesh->mPrimitiveTypes & aiPrimitiveType_POINT) {
|
||||||
{
|
|
||||||
++aiNumMeshesPerPType[0];
|
++aiNumMeshesPerPType[0];
|
||||||
++num;
|
++num;
|
||||||
}
|
}
|
||||||
if (mesh->mPrimitiveTypes & aiPrimitiveType_LINE)
|
if (mesh->mPrimitiveTypes & aiPrimitiveType_LINE) {
|
||||||
{
|
|
||||||
++aiNumMeshesPerPType[1];
|
++aiNumMeshesPerPType[1];
|
||||||
++num;
|
++num;
|
||||||
}
|
}
|
||||||
if (mesh->mPrimitiveTypes & aiPrimitiveType_TRIANGLE)
|
if (mesh->mPrimitiveTypes & aiPrimitiveType_TRIANGLE) {
|
||||||
{
|
|
||||||
++aiNumMeshesPerPType[2];
|
++aiNumMeshesPerPType[2];
|
||||||
++num;
|
++num;
|
||||||
}
|
}
|
||||||
if (mesh->mPrimitiveTypes & aiPrimitiveType_POLYGON)
|
if (mesh->mPrimitiveTypes & aiPrimitiveType_POLYGON) {
|
||||||
{
|
|
||||||
++aiNumMeshesPerPType[3];
|
++aiNumMeshesPerPType[3];
|
||||||
++num;
|
++num;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (1 == num)
|
if (1 == num) {
|
||||||
{
|
if (!(configRemoveMeshes & mesh->mPrimitiveTypes)) {
|
||||||
if (!(configRemoveMeshes & mesh->mPrimitiveTypes))
|
*meshIdx = static_cast<unsigned int>( outMeshes.size() );
|
||||||
{
|
|
||||||
*meshIdx = (unsigned int) outMeshes.size();
|
|
||||||
outMeshes.push_back(mesh);
|
outMeshes.push_back(mesh);
|
||||||
|
} else {
|
||||||
|
delete mesh;
|
||||||
|
pScene->mMeshes[ i ] = nullptr;
|
||||||
|
bAnyChanges = true;
|
||||||
}
|
}
|
||||||
else bAnyChanges = true;
|
|
||||||
|
|
||||||
meshIdx += 4;
|
meshIdx += 4;
|
||||||
continue;
|
continue;
|
||||||
|
@ -195,14 +187,13 @@ void SortByPTypeProcess::Execute( aiScene* pScene)
|
||||||
bAnyChanges = true;
|
bAnyChanges = true;
|
||||||
|
|
||||||
// reuse our current mesh arrays for the submesh
|
// reuse our current mesh arrays for the submesh
|
||||||
// with the largest numer of primitives
|
// with the largest number of primitives
|
||||||
unsigned int aiNumPerPType[4] = {0,0,0,0};
|
unsigned int aiNumPerPType[4] = {0,0,0,0};
|
||||||
aiFace* pFirstFace = mesh->mFaces;
|
aiFace* pFirstFace = mesh->mFaces;
|
||||||
aiFace* const pLastFace = pFirstFace + mesh->mNumFaces;
|
aiFace* const pLastFace = pFirstFace + mesh->mNumFaces;
|
||||||
|
|
||||||
unsigned int numPolyVerts = 0;
|
unsigned int numPolyVerts = 0;
|
||||||
for (;pFirstFace != pLastFace; ++pFirstFace)
|
for (;pFirstFace != pLastFace; ++pFirstFace) {
|
||||||
{
|
|
||||||
if (pFirstFace->mNumIndices <= 3)
|
if (pFirstFace->mNumIndices <= 3)
|
||||||
++aiNumPerPType[pFirstFace->mNumIndices-1];
|
++aiNumPerPType[pFirstFace->mNumIndices-1];
|
||||||
else
|
else
|
||||||
|
|
|
@ -890,6 +890,8 @@ unsigned int glTF2Exporter::ExportNodeHierarchy(const aiNode* n)
|
||||||
{
|
{
|
||||||
Ref<Node> node = mAsset->nodes.Create(mAsset->FindUniqueID(n->mName.C_Str(), "node"));
|
Ref<Node> node = mAsset->nodes.Create(mAsset->FindUniqueID(n->mName.C_Str(), "node"));
|
||||||
|
|
||||||
|
node->name = n->mName.C_Str();
|
||||||
|
|
||||||
if (!n->mTransformation.IsIdentity()) {
|
if (!n->mTransformation.IsIdentity()) {
|
||||||
node->matrix.isPresent = true;
|
node->matrix.isPresent = true;
|
||||||
CopyValue(n->mTransformation, node->matrix.value);
|
CopyValue(n->mTransformation, node->matrix.value);
|
||||||
|
|
|
@ -134,6 +134,30 @@ public:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
/** Extract a particular vertex from a anim mesh and interleave all components */
|
||||||
|
explicit Vertex(const aiAnimMesh* msh, unsigned int idx) {
|
||||||
|
ai_assert(idx < msh->mNumVertices);
|
||||||
|
position = msh->mVertices[idx];
|
||||||
|
|
||||||
|
if (msh->HasNormals()) {
|
||||||
|
normal = msh->mNormals[idx];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (msh->HasTangentsAndBitangents()) {
|
||||||
|
tangent = msh->mTangents[idx];
|
||||||
|
bitangent = msh->mBitangents[idx];
|
||||||
|
}
|
||||||
|
|
||||||
|
for (unsigned int i = 0; msh->HasTextureCoords(i); ++i) {
|
||||||
|
texcoords[i] = msh->mTextureCoords[i][idx];
|
||||||
|
}
|
||||||
|
|
||||||
|
for (unsigned int i = 0; msh->HasVertexColors(i); ++i) {
|
||||||
|
colors[i] = msh->mColors[i][idx];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
Vertex& operator += (const Vertex& v) {
|
Vertex& operator += (const Vertex& v) {
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
g cube
|
g cube
|
||||||
|
|
||||||
v 0.0 0.0 0.0 124 110 120
|
v 0.0 0.0 0.0 0.48627 0.43137 0.47059
|
||||||
v 0.0 0.0 1.0 24 0 121
|
v 0.0 0.0 1.0 0.09412 0.00000 0.47451
|
||||||
v 0.0 1.0 0.0 4 0 44
|
v 0.0 1.0 0.0 0.01569 0.00000 0.17255
|
||||||
v 0.0 1.0 1.0 224 0 10
|
v 0.0 1.0 1.0 0.87843 0.00000 0.03922
|
||||||
v 1.0 0.0 0.0 24 200 25
|
v 1.0 0.0 0.0 0.09412 0.78431 0.09804
|
||||||
v 1.0 0.0 1.0 124 10 56
|
v 1.0 0.0 1.0 0.48627 0.03922 0.21961
|
||||||
v 1.0 1.0 0.0 78 10 50
|
v 1.0 1.0 0.0 0.30588 0.03922 0.19608
|
||||||
v 1.0 1.0 1.0 23 0 200
|
v 1.0 1.0 1.0 0.09020 0.00000 0.78431
|
||||||
|
|
||||||
vn 0.0 0.0 1.0
|
vn 0.0 0.0 1.0
|
||||||
vn 0.0 0.0 -1.0
|
vn 0.0 0.0 -1.0
|
||||||
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
g cube
|
||||||
|
|
||||||
|
v 0.0 0.0 0.0 0.0 0.0 0.0
|
||||||
|
v 0.0 0.0 1.0 1.0 0.6 0.3
|
||||||
|
v 0.0 1.0 0.0 0.0 0.0 0.0
|
||||||
|
v 0.0 1.0 1.0 0.3 0.6 1.0
|
||||||
|
v 1.0 0.0 0.0 0.0 0.0 0.0
|
||||||
|
v 1.0 0.0 1.0 1.0 0.6 0.3
|
||||||
|
v 1.0 1.0 0.0 0.0 0.0 0.0
|
||||||
|
v 1.0 1.0 1.0 0.3 0.6 1.0
|
||||||
|
|
||||||
|
vn 0.0 0.0 1.0
|
||||||
|
vn 0.0 0.0 -1.0
|
||||||
|
vn 0.0 1.0 0.0
|
||||||
|
vn 0.0 -1.0 0.0
|
||||||
|
vn 1.0 0.0 0.0
|
||||||
|
vn -1.0 0.0 0.0
|
||||||
|
|
||||||
|
f 1//2 7//2 5//2
|
||||||
|
f 1//2 3//2 7//2
|
||||||
|
f 1//6 4//6 3//6
|
||||||
|
f 1//6 2//6 4//6
|
||||||
|
f 3//3 8//3 7//3
|
||||||
|
f 3//3 4//3 8//3
|
||||||
|
f 5//5 7//5 8//5
|
||||||
|
f 5//5 8//5 6//5
|
||||||
|
f 1//4 5//4 6//4
|
||||||
|
f 1//4 6//4 2//4
|
||||||
|
f 2//1 6//1 8//1
|
||||||
|
f 2//1 8//1 4//1
|
|
@ -267,6 +267,28 @@ TEST_F( utObjImportExport, issue809_vertex_color_Test ) {
|
||||||
#endif // ASSIMP_BUILD_NO_EXPORT
|
#endif // ASSIMP_BUILD_NO_EXPORT
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F( utObjImportExport, issue1923_vertex_color_Test ) {
|
||||||
|
::Assimp::Importer importer;
|
||||||
|
const aiScene *scene = importer.ReadFile( ASSIMP_TEST_MODELS_DIR "/OBJ/cube_with_vertexcolors_uni.obj", aiProcess_ValidateDataStructure );
|
||||||
|
EXPECT_NE( nullptr, scene );
|
||||||
|
|
||||||
|
scene = importer.GetOrphanedScene();
|
||||||
|
|
||||||
|
#ifndef ASSIMP_BUILD_NO_EXPORT
|
||||||
|
::Assimp::Exporter exporter;
|
||||||
|
const aiExportDataBlob* blob = exporter.ExportToBlob( scene, "obj");
|
||||||
|
EXPECT_NE( nullptr, blob );
|
||||||
|
|
||||||
|
const aiScene *sceneReImport = importer.ReadFileFromMemory( blob->data, blob->size, aiProcess_ValidateDataStructure );
|
||||||
|
EXPECT_NE( nullptr, scene );
|
||||||
|
|
||||||
|
SceneDiffer differ;
|
||||||
|
EXPECT_TRUE( differ.isEqual( scene, sceneReImport ) );
|
||||||
|
#endif // ASSIMP_BUILD_NO_EXPORT
|
||||||
|
|
||||||
|
delete scene;
|
||||||
|
}
|
||||||
|
|
||||||
TEST_F( utObjImportExport, issue1453_segfault ) {
|
TEST_F( utObjImportExport, issue1453_segfault ) {
|
||||||
static const std::string ObjModel =
|
static const std::string ObjModel =
|
||||||
"v 0.0 0.0 0.0\n"
|
"v 0.0 0.0 0.0\n"
|
||||||
|
|
Loading…
Reference in New Issue