@ -1,978 +0,0 @@
/*
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Open Asset Import Library ( assimp )
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Copyright ( c ) 2006 - 2020 , assimp team
All rights reserved .
Redistribution and use of this software in source and binary forms ,
with or without modification , are permitted provided that the following
conditions are met :
* Redistributions of source code must retain the above
copyright notice , this list of conditions and the
following disclaimer .
* Redistributions in binary form must reproduce the above
copyright notice , this list of conditions and the
following disclaimer in the documentation and / or other
materials provided with the distribution .
* Neither the name of the assimp team , nor the names of its
contributors may be used to endorse or promote products
derived from this software without specific prior
written permission of the assimp team .
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
" AS IS " AND ANY EXPRESS OR IMPLIED WARRANTIES , INCLUDING , BUT NOT
LIMITED TO , THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED . IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT , INDIRECT , INCIDENTAL ,
SPECIAL , EXEMPLARY , OR CONSEQUENTIAL DAMAGES ( INCLUDING , BUT NOT
LIMITED TO , PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES ; LOSS OF USE ,
DATA , OR PROFITS ; OR BUSINESS INTERRUPTION ) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY , WHETHER IN CONTRACT , STRICT LIABILITY , OR TORT
( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE , EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE .
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*/
/// \file AMFImporter_Postprocess.cpp
/// \brief Convert built scenegraph and objects to Assimp scenegraph.
/// \date 2016
/// \author smal.root@gmail.com
# ifndef ASSIMP_BUILD_NO_AMF_IMPORTER
# include "AMFImporter.hpp"
// Header files, Assimp.
# include <assimp/SceneCombiner.h>
# include <assimp/StandardShapes.h>
# include <assimp/StringUtils.h>
// Header files, stdlib.
# include <iterator>
namespace Assimp
{
aiColor4D AMFImporter : : SPP_Material : : GetColor ( const float /*pX*/ , const float /*pY*/ , const float /*pZ*/ ) const
{
aiColor4D tcol ;
// Check if stored data are supported.
if ( ! Composition . empty ( ) )
{
throw DeadlyImportError ( " IME. GetColor for composition " ) ;
}
else if ( Color - > Composed )
{
throw DeadlyImportError ( " IME. GetColor, composed color " ) ;
}
else
{
tcol = Color - > Color ;
}
// Check if default color must be used
if ( ( tcol . r = = 0 ) & & ( tcol . g = = 0 ) & & ( tcol . b = = 0 ) & & ( tcol . a = = 0 ) )
{
tcol . r = 0.5f ;
tcol . g = 0.5f ;
tcol . b = 0.5f ;
tcol . a = 1 ;
}
return tcol ;
}
void AMFImporter : : PostprocessHelper_CreateMeshDataArray ( const AMFMesh & pNodeElement , std : : vector < aiVector3D > & pVertexCoordinateArray ,
std : : vector < AMFColor * > & pVertexColorArray ) const
{
AMFVertices * vn = nullptr ;
size_t col_idx ;
// All data stored in "vertices", search for it.
for ( AMFNodeElementBase * ne_child : pNodeElement . Child )
{
if ( ne_child - > Type = = AMFNodeElementBase : : ENET_Vertices ) vn = ( AMFVertices * ) ne_child ;
}
// If "vertices" not found then no work for us.
if ( vn = = nullptr ) return ;
pVertexCoordinateArray . reserve ( vn - > Child . size ( ) ) ; // all coordinates stored as child and we need to reserve space for future push_back's.
pVertexColorArray . resize ( vn - > Child . size ( ) ) ; // colors count equal vertices count.
col_idx = 0 ;
// Inside vertices collect all data and place to arrays
for ( AMFNodeElementBase * vn_child : vn - > Child )
{
// vertices, colors
if ( vn_child - > Type = = AMFNodeElementBase : : ENET_Vertex )
{
// by default clear color for current vertex
pVertexColorArray [ col_idx ] = nullptr ;
for ( AMFNodeElementBase * vtx : vn_child - > Child )
{
if ( vtx - > Type = = AMFNodeElementBase : : ENET_Coordinates )
{
pVertexCoordinateArray . push_back ( ( ( AMFCoordinates * ) vtx ) - > Coordinate ) ;
continue ;
}
if ( vtx - > Type = = AMFNodeElementBase : : ENET_Color )
{
pVertexColorArray [ col_idx ] = ( AMFColor * ) vtx ;
continue ;
}
} // for(CAMFImporter_NodeElement* vtx: vn_child->Child)
col_idx + + ;
} // if(vn_child->Type == CAMFImporter_NodeElement::ENET_Vertex)
} // for(CAMFImporter_NodeElement* vn_child: vn->Child)
}
size_t AMFImporter : : PostprocessHelper_GetTextureID_Or_Create ( const std : : string & pID_R , const std : : string & pID_G , const std : : string & pID_B ,
const std : : string & pID_A )
{
size_t TextureConverted_Index ;
std : : string TextureConverted_ID ;
// check input data
if ( pID_R . empty ( ) & & pID_G . empty ( ) & & pID_B . empty ( ) & & pID_A . empty ( ) )
throw DeadlyImportError ( " PostprocessHelper_GetTextureID_Or_Create. At least one texture ID must be defined. " ) ;
// Create ID
TextureConverted_ID = pID_R + " _ " + pID_G + " _ " + pID_B + " _ " + pID_A ;
// Check if texture specified by set of IDs is converted already.
TextureConverted_Index = 0 ;
for ( const SPP_Texture & tex_convd : mTexture_Converted )
{
if ( tex_convd . ID = = TextureConverted_ID ) {
return TextureConverted_Index ;
} else {
+ + TextureConverted_Index ;
}
}
//
// Converted texture not found, create it.
//
AMFTexture * src_texture [ 4 ] { nullptr } ;
std : : vector < AMFTexture * > src_texture_4check ;
SPP_Texture converted_texture ;
{ // find all specified source textures
AMFNodeElementBase * t_tex ;
// R
if ( ! pID_R . empty ( ) )
{
if ( ! Find_NodeElement ( pID_R , AMFNodeElementBase : : ENET_Texture , & t_tex ) ) Throw_ID_NotFound ( pID_R ) ;
src_texture [ 0 ] = ( AMFTexture * ) t_tex ;
src_texture_4check . push_back ( ( AMFTexture * ) t_tex ) ;
}
else
{
src_texture [ 0 ] = nullptr ;
}
// G
if ( ! pID_G . empty ( ) )
{
if ( ! Find_NodeElement ( pID_G , AMFNodeElementBase : : ENET_Texture , & t_tex ) ) Throw_ID_NotFound ( pID_G ) ;
src_texture [ 1 ] = ( AMFTexture * ) t_tex ;
src_texture_4check . push_back ( ( AMFTexture * ) t_tex ) ;
}
else
{
src_texture [ 1 ] = nullptr ;
}
// B
if ( ! pID_B . empty ( ) )
{
if ( ! Find_NodeElement ( pID_B , AMFNodeElementBase : : ENET_Texture , & t_tex ) ) Throw_ID_NotFound ( pID_B ) ;
src_texture [ 2 ] = ( AMFTexture * ) t_tex ;
src_texture_4check . push_back ( ( AMFTexture * ) t_tex ) ;
}
else
{
src_texture [ 2 ] = nullptr ;
}
// A
if ( ! pID_A . empty ( ) )
{
if ( ! Find_NodeElement ( pID_A , AMFNodeElementBase : : ENET_Texture , & t_tex ) ) Throw_ID_NotFound ( pID_A ) ;
src_texture [ 3 ] = ( AMFTexture * ) t_tex ;
src_texture_4check . push_back ( ( AMFTexture * ) t_tex ) ;
}
else
{
src_texture [ 3 ] = nullptr ;
}
} // END: find all specified source textures
// check that all textures has same size
if ( src_texture_4check . size ( ) > 1 )
{
for ( size_t i = 0 , i_e = ( src_texture_4check . size ( ) - 1 ) ; i < i_e ; i + + )
{
if ( ( src_texture_4check [ i ] - > Width ! = src_texture_4check [ i + 1 ] - > Width ) | | ( src_texture_4check [ i ] - > Height ! = src_texture_4check [ i + 1 ] - > Height ) | |
( src_texture_4check [ i ] - > Depth ! = src_texture_4check [ i + 1 ] - > Depth ) )
{
throw DeadlyImportError ( " PostprocessHelper_GetTextureID_Or_Create. Source texture must has the same size. " ) ;
}
}
} // if(src_texture_4check.size() > 1)
// set texture attributes
converted_texture . Width = src_texture_4check [ 0 ] - > Width ;
converted_texture . Height = src_texture_4check [ 0 ] - > Height ;
converted_texture . Depth = src_texture_4check [ 0 ] - > Depth ;
// if one of source texture is tiled then converted texture is tiled too.
converted_texture . Tiled = false ;
for ( uint8_t i = 0 ; i < src_texture_4check . size ( ) ; i + + ) converted_texture . Tiled | = src_texture_4check [ i ] - > Tiled ;
// Create format hint.
strcpy ( converted_texture . FormatHint , " rgba0000 " ) ; // copy initial string.
if ( ! pID_R . empty ( ) ) converted_texture . FormatHint [ 4 ] = ' 8 ' ;
if ( ! pID_G . empty ( ) ) converted_texture . FormatHint [ 5 ] = ' 8 ' ;
if ( ! pID_B . empty ( ) ) converted_texture . FormatHint [ 6 ] = ' 8 ' ;
if ( ! pID_A . empty ( ) ) converted_texture . FormatHint [ 7 ] = ' 8 ' ;
//
// С opy data of textures.
//
size_t tex_size = 0 ;
size_t step = 0 ;
size_t off_g = 0 ;
size_t off_b = 0 ;
// Calculate size of the target array and rule how data will be copied.
if ( ! pID_R . empty ( ) & & nullptr ! = src_texture [ 0 ] ) {
tex_size + = src_texture [ 0 ] - > Data . size ( ) ; step + + , off_g + + , off_b + + ;
}
if ( ! pID_G . empty ( ) & & nullptr ! = src_texture [ 1 ] ) {
tex_size + = src_texture [ 1 ] - > Data . size ( ) ; step + + , off_b + + ;
}
if ( ! pID_B . empty ( ) & & nullptr ! = src_texture [ 2 ] ) {
tex_size + = src_texture [ 2 ] - > Data . size ( ) ; step + + ;
}
if ( ! pID_A . empty ( ) & & nullptr ! = src_texture [ 3 ] ) {
tex_size + = src_texture [ 3 ] - > Data . size ( ) ; step + + ;
}
// Create target array.
converted_texture . Data = new uint8_t [ tex_size ] ;
// And copy data
auto CopyTextureData = [ & ] ( const std : : string & pID , const size_t pOffset , const size_t pStep , const uint8_t pSrcTexNum ) - > void
{
if ( ! pID . empty ( ) )
{
for ( size_t idx_target = pOffset , idx_src = 0 ; idx_target < tex_size ; idx_target + = pStep , idx_src + + ) {
AMFTexture * tex = src_texture [ pSrcTexNum ] ;
ai_assert ( tex ) ;
converted_texture . Data [ idx_target ] = tex - > Data . at ( idx_src ) ;
}
}
} ; // auto CopyTextureData = [&](const size_t pOffset, const size_t pStep, const uint8_t pSrcTexNum) -> void
CopyTextureData ( pID_R , 0 , step , 0 ) ;
CopyTextureData ( pID_G , off_g , step , 1 ) ;
CopyTextureData ( pID_B , off_b , step , 2 ) ;
CopyTextureData ( pID_A , step - 1 , step , 3 ) ;
// Store new converted texture ID
converted_texture . ID = TextureConverted_ID ;
// Store new converted texture
mTexture_Converted . push_back ( converted_texture ) ;
return TextureConverted_Index ;
}
void AMFImporter : : PostprocessHelper_SplitFacesByTextureID ( std : : list < SComplexFace > & pInputList , std : : list < std : : list < SComplexFace > > & pOutputList_Separated )
{
auto texmap_is_equal = [ ] ( const AMFTexMap * pTexMap1 , const AMFTexMap * pTexMap2 ) - > bool
{
if ( ( pTexMap1 = = nullptr ) & & ( pTexMap2 = = nullptr ) ) return true ;
if ( pTexMap1 = = nullptr ) return false ;
if ( pTexMap2 = = nullptr ) return false ;
if ( pTexMap1 - > TextureID_R ! = pTexMap2 - > TextureID_R ) return false ;
if ( pTexMap1 - > TextureID_G ! = pTexMap2 - > TextureID_G ) return false ;
if ( pTexMap1 - > TextureID_B ! = pTexMap2 - > TextureID_B ) return false ;
if ( pTexMap1 - > TextureID_A ! = pTexMap2 - > TextureID_A ) return false ;
return true ;
} ;
pOutputList_Separated . clear ( ) ;
if ( pInputList . empty ( ) ) return ;
do
{
SComplexFace face_start = pInputList . front ( ) ;
std : : list < SComplexFace > face_list_cur ;
for ( std : : list < SComplexFace > : : iterator it = pInputList . begin ( ) , it_end = pInputList . end ( ) ; it ! = it_end ; )
{
if ( texmap_is_equal ( face_start . TexMap , it - > TexMap ) )
{
auto it_old = it ;
+ + it ;
face_list_cur . push_back ( * it_old ) ;
pInputList . erase ( it_old ) ;
}
else
{
+ + it ;
}
}
if ( ! face_list_cur . empty ( ) ) pOutputList_Separated . push_back ( face_list_cur ) ;
} while ( ! pInputList . empty ( ) ) ;
}
void AMFImporter : : Postprocess_AddMetadata ( const std : : list < AMFMetadata * > & metadataList , aiNode & sceneNode ) const
{
if ( ! metadataList . empty ( ) )
{
if ( sceneNode . mMetaData ! = nullptr ) throw DeadlyImportError ( " Postprocess. MetaData member in node are not nullptr. Something went wrong. " ) ;
// copy collected metadata to output node.
sceneNode . mMetaData = aiMetadata : : Alloc ( static_cast < unsigned int > ( metadataList . size ( ) ) ) ;
size_t meta_idx ( 0 ) ;
for ( const AMFMetadata & metadata : metadataList )
{
sceneNode . mMetaData - > Set ( static_cast < unsigned int > ( meta_idx + + ) , metadata . Type , aiString ( metadata . Value ) ) ;
}
} // if(!metadataList.empty())
}
void AMFImporter : : Postprocess_BuildNodeAndObject ( const AMFObject & pNodeElement , std : : list < aiMesh * > & pMeshList , aiNode * * pSceneNode )
{
AMFColor * object_color = nullptr ;
// create new aiNode and set name as <object> has.
* pSceneNode = new aiNode ;
( * pSceneNode ) - > mName = pNodeElement . ID ;
// read mesh and color
for ( const AMFNodeElementBase * ne_child : pNodeElement . Child )
{
std : : vector < aiVector3D > vertex_arr ;
std : : vector < AMFColor * > color_arr ;
// color for object
if ( ne_child - > Type = = AMFNodeElementBase : : ENET_Color ) object_color = ( AMFColor * ) ne_child ;
if ( ne_child - > Type = = AMFNodeElementBase : : ENET_Mesh )
{
// Create arrays from children of mesh: vertices.
PostprocessHelper_CreateMeshDataArray ( * ( ( AMFMesh * ) ne_child ) , vertex_arr , color_arr ) ;
// Use this arrays as a source when creating every aiMesh
Postprocess_BuildMeshSet ( * ( ( AMFMesh * ) ne_child ) , vertex_arr , color_arr , object_color , pMeshList , * * pSceneNode ) ;
}
} // for(const CAMFImporter_NodeElement* ne_child: pNodeElement)
}
void AMFImporter : : Postprocess_BuildMeshSet ( const AMFMesh & pNodeElement , const std : : vector < aiVector3D > & pVertexCoordinateArray ,
const std : : vector < AMFColor * > & pVertexColorArray ,
const AMFColor * pObjectColor , std : : list < aiMesh * > & pMeshList , aiNode & pSceneNode )
{
std : : list < unsigned int > mesh_idx ;
// all data stored in "volume", search for it.
for ( const AMFNodeElementBase * ne_child : pNodeElement . Child )
{
const AMFColor * ne_volume_color = nullptr ;
const SPP_Material * cur_mat = nullptr ;
if ( ne_child - > Type = = AMFNodeElementBase : : ENET_Volume )
{
/******************* Get faces *******************/
const AMFVolume * ne_volume = reinterpret_cast < const AMFVolume * > ( ne_child ) ;
std : : list < SComplexFace > complex_faces_list ; // List of the faces of the volume.
std : : list < std : : list < SComplexFace > > complex_faces_toplist ; // List of the face list for every mesh.
// check if volume use material
if ( ! ne_volume - > MaterialID . empty ( ) )
{
if ( ! Find_ConvertedMaterial ( ne_volume - > MaterialID , & cur_mat ) ) Throw_ID_NotFound ( ne_volume - > MaterialID ) ;
}
// inside "volume" collect all data and place to arrays or create new objects
for ( const AMFNodeElementBase * ne_volume_child : ne_volume - > Child )
{
// color for volume
if ( ne_volume_child - > Type = = AMFNodeElementBase : : ENET_Color )
{
ne_volume_color = reinterpret_cast < const AMFColor * > ( ne_volume_child ) ;
}
else if ( ne_volume_child - > Type = = AMFNodeElementBase : : ENET_Triangle ) // triangles, triangles colors
{
const AMFTriangle & tri_al = * reinterpret_cast < const AMFTriangle * > ( ne_volume_child ) ;
SComplexFace complex_face ;
// initialize pointers
complex_face . Color = nullptr ;
complex_face . TexMap = nullptr ;
// get data from triangle children: color, texture coordinates.
if ( tri_al . Child . size ( ) )
{
for ( const AMFNodeElementBase * ne_triangle_child : tri_al . Child )
{
if ( ne_triangle_child - > Type = = AMFNodeElementBase : : ENET_Color )
complex_face . Color = reinterpret_cast < const AMFColor * > ( ne_triangle_child ) ;
else if ( ne_triangle_child - > Type = = AMFNodeElementBase : : ENET_TexMap )
complex_face . TexMap = reinterpret_cast < const AMFTexMap * > ( ne_triangle_child ) ;
}
} // if(tri_al.Child.size())
// create new face and store it.
complex_face . Face . mNumIndices = 3 ;
complex_face . Face . mIndices = new unsigned int [ 3 ] ;
complex_face . Face . mIndices [ 0 ] = static_cast < unsigned int > ( tri_al . V [ 0 ] ) ;
complex_face . Face . mIndices [ 1 ] = static_cast < unsigned int > ( tri_al . V [ 1 ] ) ;
complex_face . Face . mIndices [ 2 ] = static_cast < unsigned int > ( tri_al . V [ 2 ] ) ;
complex_faces_list . push_back ( complex_face ) ;
}
} // for(const CAMFImporter_NodeElement* ne_volume_child: ne_volume->Child)
/**** Split faces list: one list per mesh ****/
PostprocessHelper_SplitFacesByTextureID ( complex_faces_list , complex_faces_toplist ) ;
/***** Create mesh for every faces list ******/
for ( std : : list < SComplexFace > & face_list_cur : complex_faces_toplist )
{
auto VertexIndex_GetMinimal = [ ] ( const std : : list < SComplexFace > & pFaceList , const size_t * pBiggerThan ) - > size_t
{
size_t rv ;
if ( pBiggerThan ! = nullptr )
{
bool found = false ;
for ( const SComplexFace & face : pFaceList )
{
for ( size_t idx_vert = 0 ; idx_vert < face . Face . mNumIndices ; idx_vert + + )
{
if ( face . Face . mIndices [ idx_vert ] > * pBiggerThan )
{
rv = face . Face . mIndices [ idx_vert ] ;
found = true ;
break ;
}
}
if ( found ) break ;
}
if ( ! found ) return * pBiggerThan ;
}
else
{
rv = pFaceList . front ( ) . Face . mIndices [ 0 ] ;
} // if(pBiggerThan != nullptr) else
for ( const SComplexFace & face : pFaceList )
{
for ( size_t vi = 0 ; vi < face . Face . mNumIndices ; vi + + )
{
if ( face . Face . mIndices [ vi ] < rv )
{
if ( pBiggerThan ! = nullptr )
{
if ( face . Face . mIndices [ vi ] > * pBiggerThan ) rv = face . Face . mIndices [ vi ] ;
}
else
{
rv = face . Face . mIndices [ vi ] ;
}
}
}
} // for(const SComplexFace& face: pFaceList)
return rv ;
} ; // auto VertexIndex_GetMinimal = [](const std::list<SComplexFace>& pFaceList, const size_t* pBiggerThan) -> size_t
auto VertexIndex_Replace = [ ] ( std : : list < SComplexFace > & pFaceList , const size_t pIdx_From , const size_t pIdx_To ) - > void
{
for ( const SComplexFace & face : pFaceList )
{
for ( size_t vi = 0 ; vi < face . Face . mNumIndices ; vi + + )
{
if ( face . Face . mIndices [ vi ] = = pIdx_From ) face . Face . mIndices [ vi ] = static_cast < unsigned int > ( pIdx_To ) ;
}
}
} ; // auto VertexIndex_Replace = [](std::list<SComplexFace>& pFaceList, const size_t pIdx_From, const size_t pIdx_To) -> void
auto Vertex_CalculateColor = [ & ] ( const size_t pIdx ) - > aiColor4D
{
// Color priorities(In descending order):
// 1. triangle color;
// 2. vertex color;
// 3. volume color;
// 4. object color;
// 5. material;
// 6. default - invisible coat.
//
// Fill vertices colors in color priority list above that's points from 1 to 6.
if ( ( pIdx < pVertexColorArray . size ( ) ) & & ( pVertexColorArray [ pIdx ] ! = nullptr ) ) // check for vertex color
{
if ( pVertexColorArray [ pIdx ] - > Composed )
throw DeadlyImportError ( " IME: vertex color composed " ) ;
else
return pVertexColorArray [ pIdx ] - > Color ;
}
else if ( ne_volume_color ! = nullptr ) // check for volume color
{
if ( ne_volume_color - > Composed )
throw DeadlyImportError ( " IME: volume color composed " ) ;
else
return ne_volume_color - > Color ;
}
else if ( pObjectColor ! = nullptr ) // check for object color
{
if ( pObjectColor - > Composed )
throw DeadlyImportError ( " IME: object color composed " ) ;
else
return pObjectColor - > Color ;
}
else if ( cur_mat ! = nullptr ) // check for material
{
return cur_mat - > GetColor ( pVertexCoordinateArray . at ( pIdx ) . x , pVertexCoordinateArray . at ( pIdx ) . y , pVertexCoordinateArray . at ( pIdx ) . z ) ;
}
else // set default color.
{
return { 0 , 0 , 0 , 0 } ;
} // if((vi < pVertexColorArray.size()) && (pVertexColorArray[vi] != nullptr)) else
} ; // auto Vertex_CalculateColor = [&](const size_t pIdx) -> aiColor4D
aiMesh * tmesh = new aiMesh ;
tmesh - > mPrimitiveTypes = aiPrimitiveType_TRIANGLE ; // Only triangles is supported by AMF.
//
// set geometry and colors (vertices)
//
// copy faces/triangles
tmesh - > mNumFaces = static_cast < unsigned int > ( face_list_cur . size ( ) ) ;
tmesh - > mFaces = new aiFace [ tmesh - > mNumFaces ] ;
// Create vertices list and optimize indices. Optimisation mean following.In AMF all volumes use one big list of vertices. And one volume
// can use only part of vertices list, for example: vertices list contain few thousands of vertices and volume use vertices 1, 3, 10.
// Do you need all this thousands of garbage? Of course no. So, optimisation step transformate sparse indices set to continuous.
size_t VertexCount_Max = tmesh - > mNumFaces * 3 ; // 3 - triangles.
std : : vector < aiVector3D > vert_arr , texcoord_arr ;
std : : vector < aiColor4D > col_arr ;
vert_arr . reserve ( VertexCount_Max * 2 ) ; // "* 2" - see below TODO.
col_arr . reserve ( VertexCount_Max * 2 ) ;
{ // fill arrays
size_t vert_idx_from , vert_idx_to ;
// first iteration.
vert_idx_to = 0 ;
vert_idx_from = VertexIndex_GetMinimal ( face_list_cur , nullptr ) ;
vert_arr . push_back ( pVertexCoordinateArray . at ( vert_idx_from ) ) ;
col_arr . push_back ( Vertex_CalculateColor ( vert_idx_from ) ) ;
if ( vert_idx_from ! = vert_idx_to ) VertexIndex_Replace ( face_list_cur , vert_idx_from , vert_idx_to ) ;
// rest iterations
do
{
vert_idx_from = VertexIndex_GetMinimal ( face_list_cur , & vert_idx_to ) ;
if ( vert_idx_from = = vert_idx_to ) break ; // all indices are transferred,
vert_arr . push_back ( pVertexCoordinateArray . at ( vert_idx_from ) ) ;
col_arr . push_back ( Vertex_CalculateColor ( vert_idx_from ) ) ;
vert_idx_to + + ;
if ( vert_idx_from ! = vert_idx_to ) VertexIndex_Replace ( face_list_cur , vert_idx_from , vert_idx_to ) ;
} while ( true ) ;
} // fill arrays. END.
//
// check if triangle colors are used and create additional faces if needed.
//
for ( const SComplexFace & face_cur : face_list_cur )
{
if ( face_cur . Color ! = nullptr )
{
aiColor4D face_color ;
size_t vert_idx_new = vert_arr . size ( ) ;
if ( face_cur . Color - > Composed )
throw DeadlyImportError ( " IME: face color composed " ) ;
else
face_color = face_cur . Color - > Color ;
for ( size_t idx_ind = 0 ; idx_ind < face_cur . Face . mNumIndices ; idx_ind + + )
{
vert_arr . push_back ( vert_arr . at ( face_cur . Face . mIndices [ idx_ind ] ) ) ;
col_arr . push_back ( face_color ) ;
face_cur . Face . mIndices [ idx_ind ] = static_cast < unsigned int > ( vert_idx_new + + ) ;
}
} // if(face_cur.Color != nullptr)
} // for(const SComplexFace& face_cur: face_list_cur)
//
// if texture is used then copy texture coordinates too.
//
if ( face_list_cur . front ( ) . TexMap ! = nullptr )
{
size_t idx_vert_new = vert_arr . size ( ) ;
///TODO: clean unused vertices. "* 2": in certain cases - mesh full of triangle colors - vert_arr will contain duplicated vertices for
/// colored triangles and initial vertices (for colored vertices) which in real became unused. This part need more thinking about
/// optimisation.
bool * idx_vert_used ;
idx_vert_used = new bool [ VertexCount_Max * 2 ] ;
for ( size_t i = 0 , i_e = VertexCount_Max * 2 ; i < i_e ; i + + ) idx_vert_used [ i ] = false ;
// This ID's will be used when set materials ID in scene.
tmesh - > mMaterialIndex = static_cast < unsigned int > ( PostprocessHelper_GetTextureID_Or_Create ( face_list_cur . front ( ) . TexMap - > TextureID_R ,
face_list_cur . front ( ) . TexMap - > TextureID_G ,
face_list_cur . front ( ) . TexMap - > TextureID_B ,
face_list_cur . front ( ) . TexMap - > TextureID_A ) ) ;
texcoord_arr . resize ( VertexCount_Max * 2 ) ;
for ( const SComplexFace & face_cur : face_list_cur )
{
for ( size_t idx_ind = 0 ; idx_ind < face_cur . Face . mNumIndices ; idx_ind + + )
{
const size_t idx_vert = face_cur . Face . mIndices [ idx_ind ] ;
if ( ! idx_vert_used [ idx_vert ] )
{
texcoord_arr . at ( idx_vert ) = face_cur . TexMap - > TextureCoordinate [ idx_ind ] ;
idx_vert_used [ idx_vert ] = true ;
}
else if ( texcoord_arr . at ( idx_vert ) ! = face_cur . TexMap - > TextureCoordinate [ idx_ind ] )
{
// in that case one vertex is shared with many texture coordinates. We need to duplicate vertex with another texture
// coordinates.
vert_arr . push_back ( vert_arr . at ( idx_vert ) ) ;
col_arr . push_back ( col_arr . at ( idx_vert ) ) ;
texcoord_arr . at ( idx_vert_new ) = face_cur . TexMap - > TextureCoordinate [ idx_ind ] ;
face_cur . Face . mIndices [ idx_ind ] = static_cast < unsigned int > ( idx_vert_new + + ) ;
}
} // for(size_t idx_ind = 0; idx_ind < face_cur.Face.mNumIndices; idx_ind++)
} // for(const SComplexFace& face_cur: face_list_cur)
delete [ ] idx_vert_used ;
// shrink array
texcoord_arr . resize ( idx_vert_new ) ;
} // if(face_list_cur.front().TexMap != nullptr)
//
// copy collected data to mesh
//
tmesh - > mNumVertices = static_cast < unsigned int > ( vert_arr . size ( ) ) ;
tmesh - > mVertices = new aiVector3D [ tmesh - > mNumVertices ] ;
tmesh - > mColors [ 0 ] = new aiColor4D [ tmesh - > mNumVertices ] ;
memcpy ( tmesh - > mVertices , vert_arr . data ( ) , tmesh - > mNumVertices * sizeof ( aiVector3D ) ) ;
memcpy ( tmesh - > mColors [ 0 ] , col_arr . data ( ) , tmesh - > mNumVertices * sizeof ( aiColor4D ) ) ;
if ( texcoord_arr . size ( ) > 0 )
{
tmesh - > mTextureCoords [ 0 ] = new aiVector3D [ tmesh - > mNumVertices ] ;
memcpy ( tmesh - > mTextureCoords [ 0 ] , texcoord_arr . data ( ) , tmesh - > mNumVertices * sizeof ( aiVector3D ) ) ;
tmesh - > mNumUVComponents [ 0 ] = 2 ; // U and V stored in "x", "y" of aiVector3D.
}
size_t idx_face = 0 ;
for ( const SComplexFace & face_cur : face_list_cur ) tmesh - > mFaces [ idx_face + + ] = face_cur . Face ;
// store new aiMesh
mesh_idx . push_back ( static_cast < unsigned int > ( pMeshList . size ( ) ) ) ;
pMeshList . push_back ( tmesh ) ;
} // for(const std::list<SComplexFace>& face_list_cur: complex_faces_toplist)
} // if(ne_child->Type == CAMFImporter_NodeElement::ENET_Volume)
} // for(const CAMFImporter_NodeElement* ne_child: pNodeElement.Child)
// if meshes was created then assign new indices with current aiNode
if ( ! mesh_idx . empty ( ) )
{
std : : list < unsigned int > : : const_iterator mit = mesh_idx . begin ( ) ;
pSceneNode . mNumMeshes = static_cast < unsigned int > ( mesh_idx . size ( ) ) ;
pSceneNode . mMeshes = new unsigned int [ pSceneNode . mNumMeshes ] ;
for ( size_t i = 0 ; i < pSceneNode . mNumMeshes ; i + + ) pSceneNode . mMeshes [ i ] = * mit + + ;
} // if(mesh_idx.size() > 0)
}
void AMFImporter : : Postprocess_BuildMaterial ( const AMFMaterial & pMaterial )
{
SPP_Material new_mat ;
new_mat . ID = pMaterial . ID ;
for ( const AMFNodeElementBase * mat_child : pMaterial . Child )
{
if ( mat_child - > Type = = AMFNodeElementBase : : ENET_Color )
{
new_mat . Color = ( AMFColor * ) mat_child ;
}
else if ( mat_child - > Type = = AMFNodeElementBase : : ENET_Metadata )
{
new_mat . Metadata . push_back ( ( AMFMetadata * ) mat_child ) ;
}
} // for(const CAMFImporter_NodeElement* mat_child; pMaterial.Child)
// place converted material to special list
mMaterial_Converted . push_back ( new_mat ) ;
}
void AMFImporter : : Postprocess_BuildConstellation ( AMFConstellation & pConstellation , std : : list < aiNode * > & pNodeList ) const
{
aiNode * con_node ;
std : : list < aiNode * > ch_node ;
// We will build next hierarchy:
// aiNode as parent (<constellation>) for set of nodes as a children
// |- aiNode for transformation (<instance> -> <delta...>, <r...>) - aiNode for pointing to object ("objectid")
// ...
// \_ aiNode for transformation (<instance> -> <delta...>, <r...>) - aiNode for pointing to object ("objectid")
con_node = new aiNode ;
con_node - > mName = pConstellation . ID ;
// Walk through children and search for instances of another objects, constellations.
for ( const AMFNodeElementBase * ne : pConstellation . Child )
{
aiMatrix4x4 tmat ;
aiNode * t_node ;
aiNode * found_node ;
if ( ne - > Type = = AMFNodeElementBase : : ENET_Metadata ) continue ;
if ( ne - > Type ! = AMFNodeElementBase : : ENET_Instance ) throw DeadlyImportError ( " Only <instance> nodes can be in <constellation>. " ) ;
// create alias for conveniance
AMFInstance & als = * ( ( AMFInstance * ) ne ) ;
// find referenced object
if ( ! Find_ConvertedNode ( als . ObjectID , pNodeList , & found_node ) ) Throw_ID_NotFound ( als . ObjectID ) ;
// create node for applying transformation
t_node = new aiNode ;
t_node - > mParent = con_node ;
// apply transformation
aiMatrix4x4 : : Translation ( als . Delta , tmat ) , t_node - > mTransformation * = tmat ;
aiMatrix4x4 : : RotationX ( als . Rotation . x , tmat ) , t_node - > mTransformation * = tmat ;
aiMatrix4x4 : : RotationY ( als . Rotation . y , tmat ) , t_node - > mTransformation * = tmat ;
aiMatrix4x4 : : RotationZ ( als . Rotation . z , tmat ) , t_node - > mTransformation * = tmat ;
// create array for one child node
t_node - > mNumChildren = 1 ;
t_node - > mChildren = new aiNode * [ t_node - > mNumChildren ] ;
SceneCombiner : : Copy ( & t_node - > mChildren [ 0 ] , found_node ) ;
t_node - > mChildren [ 0 ] - > mParent = t_node ;
ch_node . push_back ( t_node ) ;
} // for(const CAMFImporter_NodeElement* ne: pConstellation.Child)
// copy found aiNode's as children
if ( ch_node . empty ( ) ) throw DeadlyImportError ( " <constellation> must have at least one <instance>. " ) ;
size_t ch_idx = 0 ;
con_node - > mNumChildren = static_cast < unsigned int > ( ch_node . size ( ) ) ;
con_node - > mChildren = new aiNode * [ con_node - > mNumChildren ] ;
for ( aiNode * node : ch_node ) con_node - > mChildren [ ch_idx + + ] = node ;
// and place "root" of <constellation> node to node list
pNodeList . push_back ( con_node ) ;
}
void AMFImporter : : Postprocess_BuildScene ( aiScene * pScene )
{
std : : list < aiNode * > node_list ;
std : : list < aiMesh * > mesh_list ;
std : : list < AMFMetadata * > meta_list ;
//
// Because for AMF "material" is just complex colors mixing so aiMaterial will not be used.
// For building aiScene we are must to do few steps:
// at first creating root node for aiScene.
pScene - > mRootNode = new aiNode ;
pScene - > mRootNode - > mParent = nullptr ;
pScene - > mFlags | = AI_SCENE_FLAGS_ALLOW_SHARED ;
// search for root(<amf>) element
AMFNodeElementBase * root_el = nullptr ;
for ( AMFNodeElementBase * ne : mNodeElement_List )
{
if ( ne - > Type ! = AMFNodeElementBase : : ENET_Root ) continue ;
root_el = ne ;
break ;
} // for(const CAMFImporter_NodeElement* ne: mNodeElement_List)
// Check if root element are found.
if ( root_el = = nullptr ) throw DeadlyImportError ( " Root(<amf>) element not found. " ) ;
// after that walk through children of root and collect data. Five types of nodes can be placed at top level - in <amf>: <object>, <material>, <texture>,
// <constellation> and <metadata>. But at first we must read <material> and <texture> because they will be used in <object>. <metadata> can be read
// at any moment.
//
// 1. <material>
// 2. <texture> will be converted later when processing triangles list. \sa Postprocess_BuildMeshSet
for ( const AMFNodeElementBase * root_child : root_el - > Child )
{
if ( root_child - > Type = = AMFNodeElementBase : : ENET_Material ) Postprocess_BuildMaterial ( * ( ( AMFMaterial * ) root_child ) ) ;
}
// After "appearance" nodes we must read <object> because it will be used in <constellation> -> <instance>.
//
// 3. <object>
for ( const AMFNodeElementBase * root_child : root_el - > Child )
{
if ( root_child - > Type = = AMFNodeElementBase : : ENET_Object )
{
aiNode * tnode = nullptr ;
// for <object> mesh and node must be built: object ID assigned to aiNode name and will be used in future for <instance>
Postprocess_BuildNodeAndObject ( * ( ( AMFObject * ) root_child ) , mesh_list , & tnode ) ;
if ( tnode ! = nullptr ) node_list . push_back ( tnode ) ;
}
} // for(const CAMFImporter_NodeElement* root_child: root_el->Child)
// And finally read rest of nodes.
//
for ( const AMFNodeElementBase * root_child : root_el - > Child )
{
// 4. <constellation>
if ( root_child - > Type = = AMFNodeElementBase : : ENET_Constellation )
{
// <object> and <constellation> at top of self abstraction use aiNode. So we can use only aiNode list for creating new aiNode's.
Postprocess_BuildConstellation ( * ( ( AMFConstellation * ) root_child ) , node_list ) ;
}
// 5, <metadata>
if ( root_child - > Type = = AMFNodeElementBase : : ENET_Metadata ) meta_list . push_back ( ( AMFMetadata * ) root_child ) ;
} // for(const CAMFImporter_NodeElement* root_child: root_el->Child)
// at now we can add collected metadata to root node
Postprocess_AddMetadata ( meta_list , * pScene - > mRootNode ) ;
//
// Check constellation children
//
// As said in specification:
// "When multiple objects and constellations are defined in a single file, only the top level objects and constellations are available for printing."
// What that means? For example: if some object is used in constellation then you must show only constellation but not original object.
// And at this step we are checking that relations.
nl_clean_loop :
if ( node_list . size ( ) > 1 )
{
// walk through all nodes
for ( std : : list < aiNode * > : : iterator nl_it = node_list . begin ( ) ; nl_it ! = node_list . end ( ) ; + + nl_it )
{
// and try to find them in another top nodes.
std : : list < aiNode * > : : const_iterator next_it = nl_it ;
+ + next_it ;
for ( ; next_it ! = node_list . end ( ) ; + + next_it )
{
if ( ( * next_it ) - > FindNode ( ( * nl_it ) - > mName ) ! = nullptr )
{
// if current top node(nl_it) found in another top node then erase it from node_list and restart search loop.
node_list . erase ( nl_it ) ;
goto nl_clean_loop ;
}
} // for(; next_it != node_list.end(); next_it++)
} // for(std::list<aiNode*>::const_iterator nl_it = node_list.begin(); nl_it != node_list.end(); nl_it++)
}
//
// move created objects to aiScene
//
//
// Nodes
if ( ! node_list . empty ( ) )
{
std : : list < aiNode * > : : const_iterator nl_it = node_list . begin ( ) ;
pScene - > mRootNode - > mNumChildren = static_cast < unsigned int > ( node_list . size ( ) ) ;
pScene - > mRootNode - > mChildren = new aiNode * [ pScene - > mRootNode - > mNumChildren ] ;
for ( size_t i = 0 ; i < pScene - > mRootNode - > mNumChildren ; i + + )
{
// Objects and constellation that must be showed placed at top of hierarchy in <amf> node. So all aiNode's in node_list must have
// mRootNode only as parent.
( * nl_it ) - > mParent = pScene - > mRootNode ;
pScene - > mRootNode - > mChildren [ i ] = * nl_it + + ;
}
} // if(node_list.size() > 0)
//
// Meshes
if ( ! mesh_list . empty ( ) )
{
std : : list < aiMesh * > : : const_iterator ml_it = mesh_list . begin ( ) ;
pScene - > mNumMeshes = static_cast < unsigned int > ( mesh_list . size ( ) ) ;
pScene - > mMeshes = new aiMesh * [ pScene - > mNumMeshes ] ;
for ( size_t i = 0 ; i < pScene - > mNumMeshes ; i + + ) pScene - > mMeshes [ i ] = * ml_it + + ;
} // if(mesh_list.size() > 0)
//
// Textures
pScene - > mNumTextures = static_cast < unsigned int > ( mTexture_Converted . size ( ) ) ;
if ( pScene - > mNumTextures > 0 )
{
size_t idx ;
idx = 0 ;
pScene - > mTextures = new aiTexture * [ pScene - > mNumTextures ] ;
for ( const SPP_Texture & tex_convd : mTexture_Converted )
{
pScene - > mTextures [ idx ] = new aiTexture ;
pScene - > mTextures [ idx ] - > mWidth = static_cast < unsigned int > ( tex_convd . Width ) ;
pScene - > mTextures [ idx ] - > mHeight = static_cast < unsigned int > ( tex_convd . Height ) ;
pScene - > mTextures [ idx ] - > pcData = ( aiTexel * ) tex_convd . Data ;
// texture format description.
strcpy ( pScene - > mTextures [ idx ] - > achFormatHint , tex_convd . FormatHint ) ;
idx + + ;
} // for(const SPP_Texture& tex_convd: mTexture_Converted)
// Create materials for embedded textures.
idx = 0 ;
pScene - > mNumMaterials = static_cast < unsigned int > ( mTexture_Converted . size ( ) ) ;
pScene - > mMaterials = new aiMaterial * [ pScene - > mNumMaterials ] ;
for ( const SPP_Texture & tex_convd : mTexture_Converted )
{
const aiString texture_id ( AI_EMBEDDED_TEXNAME_PREFIX + to_string ( idx ) ) ;
const int mode = aiTextureOp_Multiply ;
const int repeat = tex_convd . Tiled ? 1 : 0 ;
pScene - > mMaterials [ idx ] = new aiMaterial ;
pScene - > mMaterials [ idx ] - > AddProperty ( & texture_id , AI_MATKEY_TEXTURE_DIFFUSE ( 0 ) ) ;
pScene - > mMaterials [ idx ] - > AddProperty ( & mode , 1 , AI_MATKEY_TEXOP_DIFFUSE ( 0 ) ) ;
pScene - > mMaterials [ idx ] - > AddProperty ( & repeat , 1 , AI_MATKEY_MAPPINGMODE_U_DIFFUSE ( 0 ) ) ;
pScene - > mMaterials [ idx ] - > AddProperty ( & repeat , 1 , AI_MATKEY_MAPPINGMODE_V_DIFFUSE ( 0 ) ) ;
idx + + ;
}
} // if(pScene->mNumTextures > 0)
} // END: after that walk through children of root and collect data
} // namespace Assimp
# endif // !ASSIMP_BUILD_NO_AMF_IMPORTER