0885c8b371
- import 3MF prima implementazione.
915 lines
34 KiB
C++
915 lines
34 KiB
C++
//----------------------------------------------------------------------------
|
|
// EgalTech 2014-2014
|
|
//----------------------------------------------------------------------------
|
|
// File : Import3MF.h Data : 29.01.21 Versione : 2.3b1
|
|
// Contenuto : Dichiarazione della classe Import3MF.
|
|
//
|
|
//
|
|
//
|
|
// Modifiche : 21.01.21 SP Creazione modulo.
|
|
//
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
|
|
//--------------------------- Include ----------------------------------------
|
|
|
|
#include "stdafx.h"
|
|
#include "Import3MF.h"
|
|
#include "DllMain.h"
|
|
#include "pugixml.hpp"
|
|
#include "/EgtDev/Include/EExDllMain.h"
|
|
#include "/EgtDev/Include/EGkStmFromTriangleSoup.h"
|
|
#include "/EgtDev/Extern/libzip/Include/zip.h"
|
|
|
|
|
|
using namespace std ;
|
|
|
|
//----------------------------------------------------------------------------
|
|
IImport3MF*
|
|
CreateImport3MF( void)
|
|
{
|
|
// verifico la chiave e le opzioni
|
|
if ( ! TestKeyForEEx( GetEExKey(), KEYOPT_EEX_INPBASE, GetEExLogger()))
|
|
return nullptr ;
|
|
// creo l'oggetto
|
|
return static_cast<IImport3MF*> ( new( nothrow) Import3MF) ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool
|
|
Import3MF::Import( const string& sFile, IGeomDB* pGDB, int nIdGroup)
|
|
{
|
|
LOG_INFO( GetEExLogger(), ( "Import3MF : " + sFile).c_str()) ;
|
|
|
|
// verifico il DB geometrico
|
|
if ( pGDB == nullptr) {
|
|
LOG_ERROR( GetEExLogger(), " Import3MF : Error on GeomDB") ;
|
|
return false ;
|
|
}
|
|
m_pGDB = pGDB ;
|
|
|
|
// verifico l'Id di gruppo
|
|
if ( ! m_pGDB->ExistsObj( nIdGroup)) {
|
|
LOG_ERROR( GetEExLogger(), " Import3MF : Error on IdGroup") ;
|
|
return false ;
|
|
}
|
|
m_nIdGroup = nIdGroup ;
|
|
|
|
// Apertura file 3MF
|
|
int nErrorZip ;
|
|
m_ziparchive = zip_open( sFile.c_str(), ZIP_RDONLY, &nErrorZip) ;
|
|
if ( m_ziparchive == nullptr){
|
|
string sError = " Import3MF : Error opening file. " ;
|
|
if ( nErrorZip == ZIP_ER_INVAL)
|
|
sError += "Invalid path" ;
|
|
else if ( nErrorZip == ZIP_ER_MEMORY)
|
|
sError += "Required memory could not be allocated" ;
|
|
else if ( nErrorZip == ZIP_ER_NOENT)
|
|
sError += "Path does not exist" ;
|
|
else if ( nErrorZip == ZIP_ER_NOZIP)
|
|
sError += "File is not a zip archive" ;
|
|
else if ( nErrorZip == ZIP_ER_OPEN)
|
|
sError += "Path could not be opened" ;
|
|
else if ( nErrorZip == ZIP_ER_READ)
|
|
sError += "A read error occurred" ;
|
|
LOG_ERROR( GetEExLogger() , sError.c_str()) ;
|
|
}
|
|
|
|
// Apertura del file con il modello 3D
|
|
string sModelFileName = "3D/3dmodel.model" ;
|
|
struct zip_stat sb ;
|
|
if ( zip_stat( m_ziparchive, sModelFileName.c_str(), 0, &sb) == -1){
|
|
// se il nome del file non è standard lo cerco dal file .rels
|
|
sModelFileName = FindFileName() ;
|
|
if ( zip_stat( m_ziparchive, sModelFileName.c_str(), 0, &sb) == -1){
|
|
LOG_ERROR( GetEExLogger(), " Import3MF : file .model not found") ;
|
|
return false ;
|
|
}
|
|
}
|
|
|
|
zip_file_t * file = zip_fopen( m_ziparchive, sModelFileName.c_str(), ZIP_FL_UNCHANGED) ;
|
|
if ( file == nullptr){
|
|
LOG_ERROR( GetEExLogger(), " Import3MF : Error opening .model file") ;
|
|
return false ;
|
|
}
|
|
|
|
size_t filesize = ( size_t) sb.size ;
|
|
char * contents = new char[filesize] ;
|
|
if ( zip_fread( file, contents, sb.size) == -1){
|
|
LOG_ERROR( GetEExLogger(), " Import3MF : Error reading .model file") ;
|
|
return false ;
|
|
}
|
|
|
|
// Lettura file
|
|
pugi::xml_document doc ;
|
|
pugi::xml_parse_result ParseResult = doc.load_buffer( contents, filesize) ; // inizializzo il documento
|
|
if ( ! ParseResult) {
|
|
string sError = " Import3MF : Error parsing file. " ;
|
|
sError += ParseResult.description() ;
|
|
LOG_ERROR( GetEExLogger(), sError.c_str()) ;
|
|
return false ;
|
|
}
|
|
|
|
pugi::xml_node rootNode = doc.first_child() ;
|
|
std::string sUnit = rootNode.attribute( "unit").value() ;
|
|
if ( sUnit == "micron")
|
|
m_nScaleFactor = 0.001 ;
|
|
else if ( sUnit == "centimeter")
|
|
m_nScaleFactor = 10.0 ;
|
|
else if ( sUnit == "meter")
|
|
m_nScaleFactor = 1000.0 ;
|
|
else if ( sUnit == "inch")
|
|
m_nScaleFactor = 25.4 ;
|
|
else if ( sUnit == "foot")
|
|
m_nScaleFactor = 304.8 ;
|
|
else
|
|
m_nScaleFactor = 1.0 ; // mm
|
|
|
|
// Check sulle estensioni
|
|
string sExtensions = rootNode.attribute( "requiredextensions").value() ;
|
|
if ( sExtensions.find( "b") != string::npos)
|
|
LOG_ERROR( GetEExLogger(), " Warning : Beam Lattice Extension is not supported") ;
|
|
if ( sExtensions.find( "s") != string::npos)
|
|
LOG_ERROR( GetEExLogger(), " Warning : Slice Extension is not supported") ;
|
|
if ( sExtensions.find( "enc") != string::npos)
|
|
LOG_ERROR( GetEExLogger(), " Warning : Secure Content Extension is not supported") ;
|
|
|
|
// Analisi metadata
|
|
for ( pugi::xml_node_iterator it = rootNode.first_child() ; it != rootNode.end() ; it++){
|
|
string sName = it->name() ;
|
|
if ( sName == "metadata"){
|
|
string sValue = it->text().get() ;
|
|
m_pGDB->SetInfo( m_nIdGroup, it->attribute( "name").value(), sValue) ;
|
|
}
|
|
}
|
|
|
|
// Resource node
|
|
pugi::xml_node baseResources = rootNode.child( "resources") ;
|
|
if ( baseResources == nullptr){
|
|
LOG_ERROR( GetEExLogger(), " Error reading 3MF model : no resources node") ;
|
|
return false ;
|
|
}
|
|
if ( baseResources.child( "m:texture2d") != nullptr || baseResources.child( "m:texture2dgroup") != nullptr)
|
|
LOG_ERROR( GetEExLogger(), " Warning : textures are ignored") ;
|
|
if ( baseResources.child( "m:pbspeculardisplayproperties") != nullptr || baseResources.child( "m:pbmetallicdisplayproperties") != nullptr
|
|
|| baseResources.child( "m:pbspeculartexturedisplayproperties") != nullptr || baseResources.child( "m:pbmetallictexturedisplayproperties") != nullptr
|
|
|| baseResources.child( "m:translucentdisplayproperties") != nullptr)
|
|
LOG_ERROR( GetEExLogger(), " Warning : display properties are ignored") ;
|
|
|
|
// Build node
|
|
bool bReadOk ;
|
|
pugi::xml_node build_node = rootNode.child( "build") ;
|
|
if ( build_node == nullptr){
|
|
LOG_ERROR( GetEExLogger(), " Error reading 3MF model : no build node") ;
|
|
return false ;
|
|
}
|
|
// Attributes
|
|
for ( pugi::xml_attribute attr = build_node.first_attribute() ; attr ; attr = attr.next_attribute()){
|
|
if ( ! attr)
|
|
break ;
|
|
string sValue = attr.value() ;
|
|
m_pGDB->SetInfo( m_nIdGroup, attr.name(), sValue) ;
|
|
}
|
|
// Scorro tutti gli items
|
|
for ( pugi::xml_node_iterator itBuild = build_node.first_child() ; itBuild != build_node.end() ; itBuild ++){
|
|
std::string sName = itBuild->name() ;
|
|
if ( sName == "item"){
|
|
bReadOk = ReadItem( *itBuild, baseResources) ;
|
|
if ( ! bReadOk)
|
|
return false ;
|
|
}
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------------------
|
|
string
|
|
Import3MF::FindFileName()
|
|
{
|
|
struct zip_stat sb ;
|
|
if ( zip_stat( m_ziparchive, "_rels/.rels", 0, &sb) == -1){
|
|
LOG_ERROR( GetEExLogger(), " Import3MF : Error opening .rels file") ;
|
|
return " " ;
|
|
}
|
|
|
|
zip_file_t * file = zip_fopen( m_ziparchive, "_rels/.rels", ZIP_FL_UNCHANGED) ;
|
|
if ( file == nullptr){
|
|
LOG_ERROR( GetEExLogger(), " Import3MF : Error opening .rels file") ;
|
|
return " " ;
|
|
}
|
|
|
|
size_t filesize = ( size_t) sb.size ;
|
|
char * contents = new char[ filesize] ;
|
|
if ( zip_fread( file, contents, sb.size) == -1){
|
|
LOG_ERROR( GetEExLogger(), " Import3MF : Error reading .rels file") ;
|
|
return " " ;
|
|
}
|
|
|
|
// Lettura file
|
|
pugi::xml_document doc ;
|
|
pugi::xml_parse_result ParseResult = doc.load_buffer( contents, filesize) ;
|
|
|
|
if ( ! ParseResult) {
|
|
string sError = " Import3MF : Error parsing file. " ;
|
|
sError += ParseResult.description() ;
|
|
LOG_ERROR( GetEExLogger(), sError.c_str()) ;
|
|
return " " ;
|
|
}
|
|
|
|
pugi::xml_node root = doc.first_child() ;
|
|
for ( pugi::xml_node_iterator it = root.first_child() ; it != root.end() ; it ++ ){
|
|
string sNodeName = it->name() ;
|
|
if ( sNodeName == "Relationship"){
|
|
if ( it->attribute( "Target") != nullptr){
|
|
string sTarget = it->attribute( "Target").value() ;
|
|
if ( sTarget.substr( 0, 11) != "/Thumbnails")
|
|
return sTarget.erase( 0, 1) ; // elimino il "/" iniziale nel nome del file
|
|
}
|
|
}
|
|
}
|
|
|
|
return " " ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------------------------------
|
|
bool
|
|
Import3MF::ReadItem( pugi::xml_node node, pugi::xml_node resources)
|
|
{
|
|
int id = node.attribute( "objectid").as_int() ;
|
|
|
|
// Trasformazione
|
|
DBLVECTOR vTransform ;
|
|
if ( node.attribute( "transform") != nullptr){
|
|
string sTransform = node.attribute( "transform").value() ;
|
|
vTransform = ReadTransform( sTransform, m_nScaleFactor) ;
|
|
if ( vTransform.empty())
|
|
return false ;
|
|
}
|
|
|
|
// Attributes
|
|
ATTRVECTOR attrItem ;
|
|
for ( pugi::xml_attribute attr = node.first_attribute() ; attr ; attr = attr.next_attribute()){
|
|
string sAttrName = attr.name() ;
|
|
string sAttrVal = attr.value() ;
|
|
if ( sAttrName != "" && sAttrVal != "" && sAttrName != "objectid" && sAttrName != "transform" && sAttrName != "p:path")
|
|
attrItem.push_back( make_pair( "Item " + sAttrName, sAttrVal)) ;
|
|
}
|
|
if ( node.child( "metadatagroup") != nullptr){
|
|
bool bReadOk = ReadMetadataGroup( node.child( "metadatagroup"), attrItem, ITEM) ;
|
|
if ( ! bReadOk)
|
|
return false ;
|
|
}
|
|
|
|
if ( node.attribute( "p:path") == nullptr ){ // elemento nello stesso file
|
|
bool bReadOk = ReadObject( resources, id, attrItem, vTransform, m_nScaleFactor) ;
|
|
if ( ! bReadOk)
|
|
return false ;
|
|
}
|
|
else { // elemento in un altro file
|
|
string sFileName = node.attribute( "p:path").value() ;
|
|
bool bReadOk = ReadFromFile( sFileName, id, attrItem, vTransform) ;
|
|
if ( ! bReadOk)
|
|
return false ;
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
//-------------------------------------------------------------------------------------------------
|
|
bool
|
|
Import3MF::ReadObject( pugi::xml_node resources, int& id, ATTRVECTOR& attributes, DBLVECTOR& transform, double& nScaleFactor){
|
|
|
|
bool bFound = false ;
|
|
pugi::xml_node node ;
|
|
// Cerco nodo fra i children del nodo resources
|
|
for ( pugi::xml_node_iterator it = resources.begin() ; it != resources.end() && ! bFound ; it++){
|
|
if ( *it == nullptr)
|
|
return false ;
|
|
int nResId = it->attribute( "id").as_int() ;
|
|
if ( nResId == id){
|
|
node = *it ;
|
|
bFound = true ;
|
|
}
|
|
}
|
|
if ( ! bFound){
|
|
LOG_ERROR( GetEExLogger(), " Error reading 3MF model : Object not found") ;
|
|
return false ;
|
|
}
|
|
|
|
// bool per capire se object contiene mesh
|
|
bool bObjMesh = false ;
|
|
if ( node.child( "mesh") != nullptr)
|
|
bObjMesh = true ;
|
|
|
|
// Attributes
|
|
int pid = -1 ;
|
|
int pindex = -1 ;
|
|
if ( node.attribute( "pid") != nullptr)
|
|
pid = node.attribute( "pid").as_int() ;
|
|
if ( node.attribute( "pindex") != nullptr)
|
|
pindex = node.attribute( "pindex").as_int() ;
|
|
// Attributes generici
|
|
for ( pugi::xml_attribute attr = node.first_attribute() ; attr ; attr = attr.next_attribute()){
|
|
string sAttrName = attr.name() ;
|
|
string sAttrVal = attr.value() ;
|
|
if ( sAttrName != "" && sAttrName != "pid" && sAttrName != "pindex" && sAttrName != "id" && sAttrVal != "" )
|
|
if ( bObjMesh)
|
|
attributes.push_back( make_pair( sAttrName, sAttrVal)) ;
|
|
else
|
|
attributes.push_back( make_pair( "Object " + sAttrName, sAttrVal)) ;
|
|
}
|
|
|
|
// Sottonodi
|
|
bool bReadOk = true ;
|
|
for ( pugi::xml_node_iterator itObj = node.first_child() ; itObj != node.end() ; itObj ++){
|
|
std::string sName = itObj->name() ;
|
|
if ( sName == "metadatagroup")
|
|
bReadOk = ReadMetadataGroup( *itObj, attributes, ( bObjMesh ? MESH : COMPONENT)) ;
|
|
else if ( sName == "mesh")
|
|
bReadOk = ReadMesh( *itObj, resources, pid, pindex, attributes, transform, nScaleFactor) ;
|
|
else if ( sName == "components")
|
|
bReadOk = ReadComponents( *itObj, resources, attributes, transform, nScaleFactor) ;
|
|
|
|
if ( ! bReadOk)
|
|
return false ;
|
|
}
|
|
|
|
return true ;
|
|
|
|
}
|
|
|
|
//----------------------------------------------------------------------------------------------------------------
|
|
bool
|
|
Import3MF::ReadMesh( pugi::xml_node node, pugi::xml_node resources, int& pid, int& pindex, ATTRVECTOR& attributes, DBLVECTOR& transform,
|
|
double& nScaleFactor)
|
|
{
|
|
StmFromTriangleSoup Stm ;
|
|
bool bStmOk = Stm.Start() ;
|
|
if ( ! bStmOk) {
|
|
LOG_ERROR( GetEExLogger(), " Error reading 3MF mesh : stm.start error") ;
|
|
return false ;
|
|
}
|
|
|
|
// Vettore vertici
|
|
std::vector< Point3d> vVertices ;
|
|
pugi::xml_node vertNode = node.child( "vertices") ;
|
|
if ( vertNode == nullptr){
|
|
LOG_ERROR( GetEExLogger(), " Error reading 3MF mesh : no vertices") ;
|
|
return false ;
|
|
}
|
|
|
|
for ( pugi::xml_node_iterator itVert = vertNode.first_child() ; itVert != vertNode.end() ; itVert ++ ){
|
|
std::string sName = itVert->name() ;
|
|
if ( sName == "vertex"){
|
|
Point3d pt ;
|
|
// Controllo ci siano tutti gli attributes richiesti
|
|
if ( itVert->attribute( "x") == nullptr || itVert->attribute( "y") == nullptr || itVert->attribute( "z") == nullptr){
|
|
LOG_ERROR( GetEExLogger(), " Error reading 3MF mesh : missing vertex coordinate") ;
|
|
return false;
|
|
}
|
|
// Applico eventuale trasformazione alle coordinate
|
|
if ( ! transform.empty()) {
|
|
DBLVECTOR x { itVert->attribute( "x").as_double(), itVert->attribute( "y").as_double(),
|
|
itVert->attribute( "z").as_double(), 1} ;
|
|
DBLVECTOR res( 3, 0.0) ;
|
|
x[0] *= nScaleFactor ;
|
|
x[1] *= nScaleFactor ;
|
|
x[2] *= nScaleFactor ;
|
|
for ( size_t i = 0 ; i < 3 ; i++) {
|
|
for ( size_t j = 0 ; j < 4 ; j++)
|
|
res[i] += x[j]*transform[4*j+i] ;
|
|
}
|
|
|
|
pt.Set( res[0], res[1], res[2]) ;
|
|
}
|
|
else {
|
|
pt.Set( itVert->attribute( "x").as_double(), itVert->attribute( "y").as_double(), itVert->attribute( "z").as_double()) ;
|
|
pt *= nScaleFactor ;
|
|
}
|
|
|
|
vVertices.push_back( pt) ;
|
|
}
|
|
}
|
|
|
|
// Lettura triangoli
|
|
pugi::xml_node trgNode = node.child( "triangles") ;
|
|
if ( trgNode == nullptr){
|
|
LOG_ERROR( GetEExLogger(), " Error reading 3MF mesh : missing triangles") ;
|
|
return false ;
|
|
}
|
|
for ( pugi::xml_node_iterator itTrg = trgNode.first_child() ; itTrg != trgNode.end() ; itTrg ++){
|
|
if ( *itTrg == nullptr){
|
|
LOG_ERROR( GetEExLogger(), " Error reading 3MF mesh : missing triangles") ;
|
|
return false ;
|
|
}
|
|
|
|
std::string sName = itTrg->name() ;
|
|
if ( sName == "triangle"){
|
|
if ( itTrg->attribute( "v1") == nullptr || itTrg->attribute( "v2") == nullptr || itTrg->attribute( "v3") == nullptr){
|
|
LOG_ERROR( GetEExLogger(), " Error reading 3MF mesh : missing triangle vertex") ;
|
|
return false ;
|
|
}
|
|
int v1 = itTrg->attribute( "v1").as_int() ;
|
|
int v2 = itTrg->attribute( "v2").as_int() ;
|
|
int v3 = itTrg->attribute( "v3").as_int() ;
|
|
|
|
bool bTAddOk = Stm.AddTriangle( vVertices[v1], vVertices[v2], vVertices[v3]) ;
|
|
if ( ! bTAddOk) {
|
|
LOG_ERROR( GetEExLogger(), " Error reading 3MF mesh : stm.AddTriangle error") ;
|
|
return false ;
|
|
}
|
|
}
|
|
}
|
|
bStmOk = Stm.End() ;
|
|
if ( ! bStmOk) {
|
|
LOG_ERROR( GetEExLogger(), " Error reading 3MF mesh : stm.end error") ;
|
|
return false ;
|
|
}
|
|
|
|
ISurfTriMesh* pSurf = Stm.GetSurf() ;
|
|
if ( pSurf == nullptr) {
|
|
LOG_ERROR( GetEExLogger(), " Error reading 3MF mesh : stm.GetSurf error") ;
|
|
return false ;
|
|
}
|
|
|
|
int nId = m_pGDB->AddGeoObj( GDB_ID_NULL, m_nIdGroup, pSurf) ;
|
|
if ( nId == GDB_ID_NULL) {
|
|
LOG_ERROR( GetEExLogger(), " Error reading 3MF mesh : GDB.AddGeoOjb error") ;
|
|
return false ;
|
|
}
|
|
|
|
// Colore
|
|
if ( pid != -1 && pindex != -1) {
|
|
Color col = FindColor( resources, pid, pindex) ;
|
|
if ( ! ( col == Color( 0, 0, 0, 0)))
|
|
m_pGDB->SetMaterial( nId, col) ;
|
|
}
|
|
|
|
// Attributes
|
|
for ( size_t i = 0 ; i < attributes.size() ; i++){
|
|
if ( attributes[i].first == "name")
|
|
m_pGDB->SetName( nId, attributes[i].second) ;
|
|
else
|
|
m_pGDB->SetInfo( nId, attributes[i].first, attributes[i].second) ;
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------------
|
|
bool
|
|
Import3MF::ReadComponents( pugi::xml_node node, pugi::xml_node resources, ATTRVECTOR attributes, DBLVECTOR vItemTransform, double& nScaleFactor)
|
|
{
|
|
// Scorro tutte le componenti
|
|
for ( pugi::xml_node_iterator it = node.first_child() ; it != node.end() ; it++){
|
|
string sName = it->name() ;
|
|
if ( sName == "component"){
|
|
int id = it->attribute( "objectid").as_int() ;
|
|
|
|
// Attributes
|
|
for ( pugi::xml_attribute attr = it->first_attribute() ; attr ; attr = attr.next_attribute()){
|
|
string sAttrName = attr.name() ;
|
|
string sAttrVal = attr.value() ;
|
|
if ( sAttrName != "" && sAttrName != "transform" && sAttrName != "p:path" && sAttrName != "objectid" && sAttrVal != "") {
|
|
attributes.push_back( make_pair( "Component " + sAttrName, sAttrVal)) ;
|
|
}
|
|
}
|
|
|
|
// Trasformazioni
|
|
bool bCompTransf = false ;
|
|
DBLVECTOR vCompTransform ;
|
|
DBLVECTOR vResTransform( 16, 0.0) ;
|
|
if ( it->attribute( "transform") != nullptr){
|
|
bCompTransf = true ;
|
|
string sTransform = it->attribute( "transform").value() ;
|
|
vCompTransform = ReadTransform( sTransform, nScaleFactor) ;
|
|
if ( vCompTransform.empty())
|
|
return false ;
|
|
}
|
|
|
|
if ( ! vItemTransform.empty() && bCompTransf ){
|
|
// Combino le trasformazioni dell'item e del component
|
|
for ( size_t i = 0 ; i < 4 ; i++)
|
|
for ( size_t j = 0 ; j < 4 ; j++)
|
|
for ( size_t k = 0 ; k < 4 ; k++)
|
|
vResTransform[4*i+j] += vCompTransform[4*i+k]*vItemTransform[4*k+j] ;
|
|
}
|
|
else if ( bCompTransf) // Ho solo la trasformazione della component
|
|
vResTransform = vCompTransform ;
|
|
else // Ho solo la trasformazione dell'item
|
|
vResTransform = vItemTransform ;
|
|
|
|
if ( it->attribute( "p:path") == nullptr){
|
|
bool bReadOk = ReadObject( resources, id, attributes, vResTransform, nScaleFactor) ;
|
|
if ( ! bReadOk)
|
|
return false ;
|
|
}
|
|
// elemento si trova in un altro file
|
|
else {
|
|
string sFileName = it->attribute( "p:path").value() ;
|
|
bool bReadOk = ReadFromFile( sFileName, id, attributes, vResTransform) ;
|
|
if ( ! bReadOk)
|
|
return false ;
|
|
}
|
|
}
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------
|
|
DBLVECTOR
|
|
Import3MF::ReadTransform( string& sTransform, double& nScaleFactor)
|
|
{
|
|
DBLVECTOR vTransform ;
|
|
// aggiungo uno spazio alla fine della stringa per gestire correttamente anche l'ultimo valore con il while
|
|
sTransform.push_back( ' ') ;
|
|
size_t str_start = 0 ;
|
|
size_t str_end ;
|
|
while( ( str_end = sTransform.find( " ", str_start)) != std::string::npos ){
|
|
string sval = sTransform.substr( str_start, str_end - str_start) ;
|
|
if ( ! sval.empty()) // check per evitare il caso di due spazi consecutivi
|
|
vTransform.push_back( std::stod( sval)) ;
|
|
str_start = str_end + 1 ;
|
|
}
|
|
|
|
if ( vTransform.size() != 12){
|
|
LOG_ERROR( GetEExLogger(), " Error reading 3MF Transform : missing transformation component") ;
|
|
vTransform.clear() ;
|
|
return vTransform ;
|
|
}
|
|
|
|
// Aggiungo ultima colonna per renderla matrice 4x4
|
|
vTransform.insert( vTransform.begin() + 3, 0.0 ) ;
|
|
vTransform.insert( vTransform.begin() + 7, 0.0 ) ;
|
|
vTransform.insert( vTransform.begin() + 11, 0.0 ) ;
|
|
vTransform.push_back( 1.0) ;
|
|
|
|
// Scale factor
|
|
vTransform[12] *= nScaleFactor ;
|
|
vTransform[13] *= nScaleFactor ;
|
|
vTransform[14] *= nScaleFactor ;
|
|
|
|
return vTransform ;
|
|
}
|
|
|
|
|
|
//------------------------------------------------------------------------------------
|
|
Color
|
|
Import3MF::FindColor( pugi::xml_node resources, int pid, int pindex)
|
|
{
|
|
// Cerco la resources con quell'id
|
|
pugi::xml_node node ;
|
|
bool bFound = false ;
|
|
for ( pugi::xml_node_iterator it = resources.begin() ; it != resources.end() && ! bFound ; it++){
|
|
int nResId = it->attribute( "id").as_int() ;
|
|
if ( nResId == pid){
|
|
node = *it ;
|
|
bFound = true ;
|
|
}
|
|
}
|
|
if ( ! bFound){
|
|
LOG_ERROR( GetEExLogger(), " Error reading 3MF : object not found") ;
|
|
return false ;
|
|
}
|
|
|
|
std::string sName = node.name() ;
|
|
// contatore per pindex
|
|
int count = -1 ;
|
|
|
|
if ( sName == "m:colorgroup" || sName == "r:colorgroup"){
|
|
for ( pugi::xml_node_iterator itColor = node.first_child() ; itColor != node.end() ; itColor ++){
|
|
std::string sColName = itColor->name() ;
|
|
if ( sColName == "m:color" || sColName == "r:color")
|
|
count ++ ;
|
|
if ( count == pindex){
|
|
std::string sCol = itColor->attribute( "color").value() ;
|
|
return ColorConverter( sCol) ;
|
|
}
|
|
}
|
|
}
|
|
else if ( sName == "basematerials"){
|
|
for ( pugi::xml_node_iterator itBase = node.first_child() ; itBase != node.end() ; itBase ++){
|
|
std::string sBaseName = itBase->name() ;
|
|
if ( sBaseName == "base")
|
|
count ++ ;
|
|
if ( count == pindex){
|
|
std::string sCol = itBase->attribute( "displaycolor").value() ;
|
|
return ColorConverter( sCol) ;
|
|
}
|
|
}
|
|
}
|
|
else if ( sName == "m:multiproperties")
|
|
return ReadMultiPropertiesColor( node, resources, pid, pindex) ;
|
|
else if ( sName == "m:compositematerials")
|
|
return ReadCompositeMaterialColor( node, resources, pid, pindex) ;
|
|
|
|
return Color( 0, 0, 0, 0) ; // colore trasparente
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------
|
|
Color
|
|
Import3MF::ReadCompositeMaterialColor( pugi::xml_node node, pugi::xml_node resources, int pid, int pindex)
|
|
{
|
|
if ( node.attribute( "matid") == nullptr){
|
|
LOG_ERROR( GetEExLogger(), " Error reading 3MF : CompositeMaterial has no BaseMaterial") ;
|
|
return Color( 0, 0, 0, 0) ;
|
|
}
|
|
int nBaseMatId = node.attribute( "matid").as_int() ;
|
|
|
|
// matindices -> da stringa vanno messi in vettore di interi
|
|
string sMatIndices = node.attribute( "matindices").value() ;
|
|
sMatIndices.push_back( ' ') ;
|
|
vector<int> vMatIndices ;
|
|
size_t str_start = 0 ;
|
|
size_t str_end ;
|
|
while( ( str_end = sMatIndices.find( " ", str_start)) != std::string::npos ){
|
|
string sval = sMatIndices.substr( str_start, str_end - str_start) ;
|
|
if ( ! sval.empty())
|
|
vMatIndices.push_back( std::stoi( sval)) ;
|
|
str_start = str_end + 1 ;
|
|
}
|
|
|
|
int count = -1 ;
|
|
string sCompValues ;
|
|
for ( pugi::xml_node_iterator it = node.first_child() ; it != node.end() ; it++){
|
|
string sNodeName = it->name() ;
|
|
if ( sNodeName == "m:composite")
|
|
count ++ ;
|
|
if ( count == pindex){
|
|
if ( it->attribute( "values") == nullptr){
|
|
LOG_ERROR( GetEExLogger(), " Error reading 3MF : CompositeMaterial has no BaseMaterial") ;
|
|
return Color( 0, 0, 0, 0) ;
|
|
}
|
|
sCompValues = it->attribute( "values").value() ;
|
|
}
|
|
}
|
|
if ( sCompValues.empty()){
|
|
LOG_ERROR( GetEExLogger(), " Error reading 3MF : CompositeMaterial has no BaseMaterial") ;
|
|
return Color( 0, 0, 0, 0) ;
|
|
}
|
|
|
|
// sCompValues -> da stringa a vettore
|
|
vector<double> vCompValues ;
|
|
sCompValues.push_back( ' ') ;
|
|
str_start = 0 ;
|
|
while( ( str_end = sCompValues.find( " ", str_start)) != std::string::npos ){
|
|
string sval = sCompValues.substr( str_start, str_end - str_start) ;
|
|
if ( ! sval.empty())
|
|
vCompValues.push_back( std::stoi( sval)) ;
|
|
str_start = str_end + 1 ;
|
|
}
|
|
|
|
// check sui valori di vCompValues
|
|
vCompValues.resize( vMatIndices.size(), 0.0) ;
|
|
double sum = 0.0 ;
|
|
for ( size_t i = 0 ; i < vCompValues.size() ; i++)
|
|
sum += vCompValues[i] ;
|
|
if ( abs( sum - 1.0) < EPS_SMALL)
|
|
for ( size_t i = 0 ; i < vCompValues.size() ; i++)
|
|
vCompValues[i] /= sum ;
|
|
if ( abs( sum) < EPS_SMALL)
|
|
vCompValues.resize( vMatIndices.size(), 1.0) ;
|
|
|
|
vector<double> col(3, 0.0) ;
|
|
Color BaseMatColor ;
|
|
for ( size_t i = 0 ; i < vMatIndices.size() ; i++){
|
|
BaseMatColor = FindColor( resources, nBaseMatId, vMatIndices[i]) ;
|
|
if ( BaseMatColor == Color( 0, 0, 0, 0))
|
|
return BaseMatColor ;
|
|
col[0] += vCompValues[i] * BaseMatColor.GetRed() ;
|
|
col[1] += vCompValues[i] * BaseMatColor.GetGreen() ;
|
|
col[2] += vCompValues[i] * BaseMatColor.GetBlue() ;
|
|
}
|
|
|
|
return Color( col[0], col[1], col[2]) ;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------
|
|
Color
|
|
Import3MF::ReadMultiPropertiesColor( pugi::xml_node node, pugi::xml_node resources, int pid, int pindex)
|
|
{
|
|
|
|
if ( node.attribute( "pids") == nullptr){
|
|
LOG_ERROR( GetEExLogger(), " Error reading 3MF : MultiProperties has no pids") ;
|
|
return Color( 0, 0, 0, 0) ;
|
|
}
|
|
// pids -> da stringa vanno messi in vettore di interi
|
|
string spids = node.attribute( "pids").value() ;
|
|
spids.push_back( ' ') ;
|
|
vector<int> vpids ;
|
|
size_t str_start = 0 ;
|
|
size_t str_end ;
|
|
while( ( str_end = spids.find( " ", str_start)) != std::string::npos ){
|
|
string sval = spids.substr( str_start, str_end - str_start) ;
|
|
if ( ! sval.empty())
|
|
vpids.push_back( std::stoi( sval)) ;
|
|
str_start = str_end + 1 ;
|
|
}
|
|
|
|
int new_id = -1 ; // id elemento di MultiProperties che contiene un colore
|
|
size_t new_pos = -1 ; // posizione nel vettore pids associata a new_id
|
|
int count = -1 ; // contatore per pindices
|
|
|
|
// Scorro il vettore di pids
|
|
for ( size_t j = 0 ; j < vpids.size() ; j++){
|
|
bool bFound = false ;
|
|
pugi::xml_node colorNode ;
|
|
// Cerco resource con id corrispondente al pids considerato
|
|
for ( pugi::xml_node_iterator it = resources.begin() ; it != resources.end() && ! bFound ; it++){
|
|
int nResId = it->attribute( "id").as_int() ;
|
|
if ( nResId == vpids[j]){
|
|
colorNode = *it ;
|
|
bFound = true ;
|
|
}
|
|
}
|
|
// se non trovi resource con quell'id passo al pids successivo
|
|
if ( ! bFound)
|
|
continue ;
|
|
|
|
std::string colorNodeName = colorNode.name() ;
|
|
if ( colorNodeName == "basematerial" || colorNodeName == "m:colorgroup" || colorNodeName == "r:colorgroup"){
|
|
new_id = vpids[j] ;
|
|
new_pos = j ;
|
|
break ;
|
|
}
|
|
}
|
|
|
|
// se quando ho finito i pids non ho trovato un elemento che potrebbe contenere un colore
|
|
if ( new_id == -1){
|
|
LOG_ERROR( GetEExLogger(), " Error reading 3MF mesh : no object defining color") ;
|
|
return Color( 0, 0, 0, 0) ;
|
|
}
|
|
|
|
// altrimenti cerco l'elemento multi del multiproperties corrispondente al pindex
|
|
for ( pugi::xml_node_iterator itMP = node.first_child() ; itMP != node.end() ; itMP ++){
|
|
std::string sMPName = itMP->name() ;
|
|
if ( sMPName == "m:multi")
|
|
count ++ ;
|
|
if ( count == pindex){
|
|
if ( itMP->attribute( "pindices") == nullptr){
|
|
LOG_ERROR( GetEExLogger(), " Error reading 3MF : MultiProperties pindices missing") ;
|
|
return Color( 0, 0, 0, 0) ;
|
|
}
|
|
string spindices = itMP->attribute( "pindices").value() ;
|
|
spindices.push_back( ' ') ;
|
|
int new_index = -1 ;
|
|
// Estraggo dalla stringa il valore in posizione new_pos
|
|
int count2 = -1 ;
|
|
str_start = 0 ;
|
|
while( ( str_end = spindices.find( " ", str_start)) != std::string::npos ){
|
|
count2 ++ ;
|
|
if ( count2 == new_pos){
|
|
string sval = spindices.substr( str_start, str_end - str_start) ;
|
|
if ( ! sval.empty())
|
|
new_index = std::stoi( sval) ;
|
|
else
|
|
new_index = 0 ;
|
|
break ;
|
|
}
|
|
str_start = str_end + 1 ;
|
|
}
|
|
|
|
if ( new_index == -1)
|
|
new_index = 0 ;
|
|
return FindColor( resources, new_id, new_index) ;
|
|
}
|
|
}
|
|
|
|
return Color( 0, 0, 0, 0) ;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------------------
|
|
Color
|
|
Import3MF::ColorConverter( std::string s)
|
|
{
|
|
Color col ;
|
|
vector<int> vCol ;
|
|
|
|
if ( s.length() != 9 && s.length() != 7){
|
|
LOG_ERROR( GetEExLogger(), " Error reading 3MF Color : undefined color") ;
|
|
return Color( 0, 0, 0, 0) ;
|
|
}
|
|
|
|
int pos = 1 ;
|
|
while ( pos != s.length()){
|
|
std::string substr = s.substr( pos, 2) ;
|
|
vCol.push_back( strtol( substr.c_str(), NULL, 16)) ;
|
|
pos +=2 ;
|
|
}
|
|
|
|
// alpha
|
|
if ( vCol.size() == 4)
|
|
vCol[3] *= 100 / 255 ;
|
|
else if ( vCol.size() == 3)
|
|
vCol.push_back( 100) ;
|
|
|
|
if ( vCol[3] == 0)
|
|
vCol[3] = 100 ; // il colore è sempre assunto come completamente opaco
|
|
|
|
return Color( &vCol[0]) ;
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------------------
|
|
bool
|
|
Import3MF::ReadFromFile( string sFileName, int id, ATTRVECTOR& attributes, DBLVECTOR& vTransform)
|
|
{
|
|
// E' necessario togliere "/" dal nome del file
|
|
sFileName.erase( 0, 1) ;
|
|
|
|
// Se documento non è mai stato letto prima
|
|
if ( m_AuxDocuments.find( sFileName) == m_AuxDocuments.end()){
|
|
|
|
auto res = m_AuxDocuments.emplace( sFileName, pugi::xml_document()) ;
|
|
if ( ! res.second)
|
|
return false ;
|
|
|
|
struct zip_stat sb ;
|
|
if ( zip_stat( m_ziparchive, sFileName.c_str(), 0, &sb) == -1){
|
|
LOG_ERROR( GetEExLogger(), " Import3MF : Error opening auxiliary .model file ") ;
|
|
return false ;
|
|
}
|
|
zip_file_t * file = zip_fopen( m_ziparchive, sFileName.c_str(), ZIP_FL_UNCHANGED) ;
|
|
size_t filesize = ( size_t) sb.size ;
|
|
char * contents = new char[filesize] ;
|
|
if ( zip_fread( file, contents, sb.size) == -1){
|
|
LOG_ERROR( GetEExLogger(), " Import3MF : Error reading auxiliary .model file") ;
|
|
return false ;
|
|
}
|
|
|
|
// Lettura file
|
|
pugi::xml_parse_result ParseResult = m_AuxDocuments[ sFileName].load_buffer( contents, filesize) ; // inizializzo il documento
|
|
if ( ! ParseResult) {
|
|
string sError = " Import3MF : Error parsing file : " ;
|
|
sError += ParseResult.description() ;
|
|
LOG_ERROR( GetEExLogger(), sError.c_str()) ;
|
|
return false ;
|
|
}
|
|
}
|
|
|
|
// Lettura del documento
|
|
pugi::xml_node auxrootNode = m_AuxDocuments[ sFileName].first_child() ; // node model
|
|
std::string sUnit = auxrootNode.attribute( "unit").value() ;
|
|
double nScaleFactor ;
|
|
if ( sUnit == "micron")
|
|
nScaleFactor = 0.001 ;
|
|
else if ( sUnit == "centimeter")
|
|
nScaleFactor = 10.0 ;
|
|
else if ( sUnit == "meter")
|
|
nScaleFactor = 1000.0 ;
|
|
else if ( sUnit == "inch")
|
|
nScaleFactor = 25.4 ;
|
|
else if ( sUnit == "foot")
|
|
nScaleFactor = 304.8 ;
|
|
else
|
|
nScaleFactor = 1.0 ; // mm
|
|
|
|
// Cerco nodo resources
|
|
pugi::xml_node auxResources = auxrootNode.child( "resources") ;
|
|
if ( auxResources == nullptr){
|
|
LOG_ERROR( GetEExLogger(), " Error reading 3MF model : no resources node found ") ;
|
|
return false ;
|
|
}
|
|
|
|
bool bReadOk = ReadObject( auxResources, id, attributes, vTransform, nScaleFactor) ;
|
|
if ( ! bReadOk)
|
|
return false ;
|
|
|
|
return true ;
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------------------------------------
|
|
bool
|
|
Import3MF::ReadMetadataGroup( pugi::xml_node node, ATTRVECTOR& v, MDGCaller caller)
|
|
{
|
|
for ( pugi::xml_node_iterator it = node.begin() ; it != node.end() ; it++){
|
|
string sNodeName = it->name() ;
|
|
if ( sNodeName == "metadata"){
|
|
if ( it->attribute( "name") == nullptr){
|
|
LOG_ERROR( GetEExLogger(), " Error reading 3MF model : metadata name missing") ;
|
|
return false ;
|
|
}
|
|
|
|
string sName ;
|
|
switch ( caller){
|
|
case ITEM :
|
|
sName = "Item " ;
|
|
break ;
|
|
case COMPONENT :
|
|
sName = "Object " ;
|
|
break ;
|
|
}
|
|
sName += it->attribute( "name").value() ;
|
|
string sValue = it->text().get() ;
|
|
v.push_back( make_pair( sName, sValue)) ;
|
|
}
|
|
}
|
|
|
|
return true ;
|
|
}
|