|
|
/*
* This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2015-2017 Cirilo Bernardo <cirilo.bernardo@gmail.com> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, you may find one here: * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
* or you may search the http://www.gnu.org website for the version 2 license,
* or you may write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */
#include <iostream>
#include <sstream>
#include <fstream>
#include <wx/filename.h>
#include <wx/log.h>
#include "plugins/3dapi/ifsg_api.h"
#include "plugins/3dapi/sg_version.h"
#include "streamwrapper.h"
#include "3d_cache/sg/sg_node.h"
#include "3d_cache/sg/scenegraph.h"
#include "3d_cache/sg/sg_appearance.h"
#include "3d_cache/sg/sg_shape.h"
#include "3d_cache/sg/sg_helpers.h"
#ifdef DEBUG
static char BadNode[] = " * [BUG] NULL pointer passed for aNode\n";#endif
// version format of the cache file
#define SG_VERSION_TAG "VERSION:2"
static void formatMaterial( SMATERIAL& mat, SGAPPEARANCE const* app ){ float v0, v1, v2;
app->ambient.GetColor( v0, v1, v2 ); mat.m_Ambient.x = v0; mat.m_Ambient.y = v1; mat.m_Ambient.z = v2;
app->diffuse.GetColor( v0, v1, v2 ); mat.m_Diffuse.x = v0; mat.m_Diffuse.y = v1; mat.m_Diffuse.z = v2; mat.m_Ambient.x *= v0; mat.m_Ambient.y *= v1; mat.m_Ambient.z *= v2;
app->emissive.GetColor( v0, v1, v2 ); mat.m_Emissive.x = v0; mat.m_Emissive.y = v1; mat.m_Emissive.z = v2;
app->specular.GetColor( v0, v1, v2 ); mat.m_Specular.x = v0; mat.m_Specular.y = v1; mat.m_Specular.z = v2;
mat.m_Shininess = app->shininess; mat.m_Transparency = app->transparency;
return;}
bool S3D::WriteVRML( const char* filename, bool overwrite, SGNODE* aTopNode, bool reuse, bool renameNodes ){ if( NULL == filename || filename[0] == 0 ) return false;
wxString ofile = wxString::FromUTF8Unchecked( filename );
if( wxFileName::Exists( ofile ) ) { if( !overwrite ) return false;
// make sure we make no attempt to write a directory
if( !wxFileName::FileExists( ofile ) ) return false; }
if( NULL == aTopNode ) { #ifdef DEBUG
do { std::ostringstream ostr; ostr << __FILE__ << ": " << __FUNCTION__ << ": " << __LINE__ << "\n"; ostr << " * [BUG] NULL pointer passed for aTopNode"; wxLogTrace( MASK_3D_SG, "%s\n", ostr.str().c_str() ); } while( 0 ); #endif
return false; }
if( S3D::SGTYPE_TRANSFORM != aTopNode->GetNodeType() ) { #ifdef DEBUG
do { std::ostringstream ostr; ostr << __FILE__ << ": " << __FUNCTION__ << ": " << __LINE__ << "\n"; ostr << " * [BUG] aTopNode is not a SCENEGRAPH object"; wxLogTrace( MASK_3D_SG, "%s\n", ostr.str().c_str() ); } while( 0 ); #endif
return false; }
OPEN_OSTREAM( op, filename );
if( op.fail() ) { wxString errmsg; errmsg << __FILE__ << ": " << __FUNCTION__ << ": " << __LINE__ << "\n"; errmsg << " * [INFO] " << "failed to open file" << " '" << filename << "'"; wxLogTrace( MASK_3D_SG, errmsg ); return false; }
op.imbue( std::locale( "C" ) ); op << "#VRML V2.0 utf8\n";
if( renameNodes ) { aTopNode->ResetNodeIndex(); aTopNode->ReNameNodes(); }
aTopNode->WriteVRML( op, reuse );
if( !op.fail() ) { CLOSE_STREAM( op ); return true; }
CLOSE_STREAM( op );
wxString errmsg; errmsg << __FILE__ << ": " << __FUNCTION__ << ": " << __LINE__ << "\n"; errmsg << " * [INFO] " << "problems encountered writing file" << " '" << filename << "'"; wxLogTrace( MASK_3D_SG, errmsg );
return false;}
void S3D::ResetNodeIndex( SGNODE* aNode ){ if( NULL == aNode ) { #ifdef DEBUG
do { std::ostringstream ostr; ostr << __FILE__ << ": " << __FUNCTION__ << ": " << __LINE__ << "\n"; ostr << BadNode; wxLogTrace( MASK_3D_SG, "%s", ostr.str().c_str() ); } while( 0 ); #endif
return; }
aNode->ResetNodeIndex();
return;}
void S3D::RenameNodes( SGNODE* aNode ){ if( NULL == aNode ) { #ifdef DEBUG
do { std::ostringstream ostr; ostr << __FILE__ << ": " << __FUNCTION__ << ": " << __LINE__ << "\n"; ostr << BadNode; wxLogTrace( MASK_3D_SG, "%s", ostr.str().c_str() ); } while( 0 ); #endif
return; }
aNode->ReNameNodes();
return;}
void S3D::DestroyNode( SGNODE* aNode ){ if( NULL == aNode ) { #ifdef DEBUG
do { std::ostringstream ostr; ostr << __FILE__ << ": " << __FUNCTION__ << ": " << __LINE__ << "\n"; ostr << BadNode; wxLogTrace( MASK_3D_SG, "%s", ostr.str().c_str() ); } while( 0 ); #endif
return; }
delete aNode;
return;}
bool S3D::WriteCache( const char* aFileName, bool overwrite, SGNODE* aNode, const char* aPluginInfo ){ if( NULL == aFileName || aFileName[0] == 0 ) return false;
wxString ofile = wxString::FromUTF8Unchecked( aFileName );
if( NULL == aNode ) { #ifdef DEBUG
do { std::ostringstream ostr; ostr << __FILE__ << ": " << __FUNCTION__ << ": " << __LINE__ << "\n"; ostr << BadNode; wxLogTrace( MASK_3D_SG, "%s", ostr.str().c_str() ); } while( 0 ); #endif
return false; }
if( wxFileName::Exists( ofile ) ) { if( !overwrite ) { wxString errmsg; errmsg << __FILE__ << ": " << __FUNCTION__ << ": " << __LINE__ << "\n"; errmsg << " * [INFO] " << "file exists; not overwriting" << " '"; errmsg << aFileName << "'"; wxLogTrace( MASK_3D_SG, errmsg );
return false; }
// make sure we make no attempt to write a directory
if( !wxFileName::FileExists( aFileName ) ) { wxString errmsg; errmsg << __FILE__ << ": " << __FUNCTION__ << ": " << __LINE__ << "\n"; errmsg << " * [INFO] " << "specified path is a directory" << " '"; errmsg << aFileName << "'"; wxLogTrace( MASK_3D_SG, errmsg ); return false; } }
OPEN_OSTREAM( output, aFileName );
if( output.fail() ) { wxString errmsg; errmsg << __FILE__ << ": " << __FUNCTION__ << ": " << __LINE__ << "\n"; errmsg << " * [INFO] " << "failed to open file" << " '" << aFileName << "'"; wxLogTrace( MASK_3D_SG, errmsg ); return false; }
output << "(" << SG_VERSION_TAG << ")";
if( NULL != aPluginInfo && aPluginInfo[0] != 0 ) output << "(" << aPluginInfo << ")"; else output << "(INTERNAL:0.0.0.0)";
bool rval = aNode->WriteCache( output, NULL ); CLOSE_STREAM( output );
if( !rval ) { #ifdef DEBUG
do { std::ostringstream ostr; ostr << __FILE__ << ": " << __FUNCTION__ << ": " << __LINE__ << "\n"; ostr << " * [INFO] problems encountered writing cache file '"; ostr << aFileName << "'"; wxLogTrace( MASK_3D_SG, "%s\n", ostr.str().c_str() ); } while( 0 ); #endif
// delete the defective file
wxRemoveFile( ofile ); }
return rval;}
SGNODE* S3D::ReadCache( const char* aFileName, void* aPluginMgr, bool (*aTagCheck)( const char*, void* ) ){ if( NULL == aFileName || aFileName[0] == 0 ) return NULL;
wxString ofile = wxString::FromUTF8Unchecked( aFileName );
if( !wxFileName::FileExists( aFileName ) ) { std::ostringstream ostr; ostr << __FILE__ << ": " << __FUNCTION__ << ": " << __LINE__ << "\n"; wxString errmsg = _( "no such file" ); ostr << " * [INFO] " << errmsg.ToUTF8() << " '"; ostr << aFileName << "'"; wxLogTrace( MASK_3D_SG, "%s\n", ostr.str().c_str() );
return NULL; }
SGNODE* np = new SCENEGRAPH( NULL );
if( NULL == np ) { #ifdef DEBUG
do { std::ostringstream ostr; ostr << __FILE__ << ": " << __FUNCTION__ << ": " << __LINE__ << "\n"; ostr << " * [INFO] failed to instantiate SCENEGRAPH"; wxLogTrace( MASK_3D_SG, "%s\n", ostr.str().c_str() ); } while( 0 ); #endif
return NULL; }
OPEN_ISTREAM( file, aFileName );
if( file.fail() ) { delete np; std::ostringstream ostr; ostr << __FILE__ << ": " << __FUNCTION__ << ": " << __LINE__ << "\n"; wxString errmsg = _( "failed to open file" ); ostr << " * [INFO] " << errmsg.ToUTF8() << " '"; ostr << aFileName << "'"; wxLogTrace( MASK_3D_SG, "%s\n", ostr.str().c_str() ); return NULL; }
// from SG_VERSION_TAG 1, read the version tag; if it's not the expected tag
// then we fail to read the cache file
do { std::string name; char schar; file.get( schar );
if( '(' != schar ) { #ifdef DEBUG
do { std::ostringstream ostr; ostr << __FILE__ << ": " << __FUNCTION__ << ": " << __LINE__ << "\n"; ostr << " * [INFO] corrupt data; missing left parenthesis at position "; ostr << file.tellg(); wxLogTrace( MASK_3D_SG, "%s\n", ostr.str().c_str() ); } while( 0 ); #endif
CLOSE_STREAM( file ); return NULL; }
file.get( schar );
while( ')' != schar && file.good() ) { name.push_back( schar ); file.get( schar ); }
if( name.compare( SG_VERSION_TAG ) ) { CLOSE_STREAM( file ); return NULL; }
} while( 0 );
// from SG_VERSION_TAG 2, read the PluginInfo string and check that it matches
// version tag; if it's not the expected tag then we fail to read the file
do { std::string name; char schar; file.get( schar );
if( '(' != schar ) { #ifdef DEBUG
do { std::ostringstream ostr; ostr << __FILE__ << ": " << __FUNCTION__ << ": " << __LINE__ << "\n"; ostr << " * [INFO] corrupt data; missing left parenthesis at position "; ostr << file.tellg(); wxLogTrace( MASK_3D_SG, "%s\n", ostr.str().c_str() ); } while( 0 ); #endif
CLOSE_STREAM( file ); return NULL; }
file.get( schar );
while( ')' != schar && file.good() ) { name.push_back( schar ); file.get( schar ); }
// check the plugin tag
if( NULL != aTagCheck && NULL != aPluginMgr && !aTagCheck( name.c_str(), aPluginMgr ) ) { CLOSE_STREAM( file ); return NULL; }
} while( 0 );
bool rval = np->ReadCache( file, NULL ); CLOSE_STREAM( file );
if( !rval ) { delete np; std::ostringstream ostr; ostr << __FILE__ << ": " << __FUNCTION__ << ": " << __LINE__ << "\n"; wxString errmsg = "problems encountered reading cache file"; ostr << " * [INFO] " << errmsg.ToUTF8() << " '"; ostr << aFileName << "'"; wxLogTrace( MASK_3D_SG, "%s\n", ostr.str().c_str() ); return NULL; }
return np;}
S3DMODEL* S3D::GetModel( SCENEGRAPH* aNode ){ if( NULL == aNode ) return NULL;
if( aNode->GetNodeType() != S3D::SGTYPE_TRANSFORM ) return NULL;
S3D::MATLIST materials; std::vector< SMESH > meshes;
// the materials list shall have a default color; although the VRML
// default is an opaque black, the default used here shall be a median
// gray in hopes that it may help highlight faulty models; this color is
// also typical of MCAD applications. When a model has no associated
// material color it shall be assigned the index 0.
SGAPPEARANCE app( NULL ); app.ambient = SGCOLOR( 0.6f, 0.6f, 0.6f ); app.diffuse = SGCOLOR( 0.6f, 0.6f, 0.6f ); app.specular = app.diffuse; app.shininess = 0.05f; app.transparency = 0.0f;
materials.matorder.push_back( &app ); materials.matmap.insert( std::pair< SGAPPEARANCE const*, int >( &app, 0 ) );
if( aNode->Prepare( NULL, materials, meshes ) ) { if( meshes.empty() ) return NULL;
S3DMODEL* model = S3D::New3DModel();
// add all the materials
size_t j = materials.matorder.size(); SMATERIAL* lmat = new SMATERIAL[j];
for( size_t i = 0; i < j; ++i ) formatMaterial( lmat[i], materials.matorder[i] );
model->m_Materials = lmat; model->m_MaterialsSize = j;
// add all the meshes
j = meshes.size(); SMESH* lmesh = new SMESH[j];
for( size_t i = 0; i < j; ++i ) lmesh[i] = meshes[i];
model->m_Meshes = lmesh; model->m_MeshesSize = j;
return model; }
size_t j = meshes.size();
for( size_t i = 0; i < j; ++i ) S3D::Free3DMesh( meshes[i] );
return NULL;}
void S3D::Destroy3DModel( S3DMODEL** aModel ){ if( NULL == aModel || NULL == *aModel ) return;
S3DMODEL* m = *aModel; S3D::FREE_S3DMODEL( *m ); delete m; *aModel = NULL;
return;}
void Free3DModel( S3DMODEL& aModel ){ S3D::FREE_S3DMODEL( aModel ); return;}
void S3D::Free3DMesh( SMESH& aMesh ){ S3D::FREE_SMESH( aMesh ); return;}
S3DMODEL* S3D::New3DModel( void ){ S3DMODEL* mp = new S3DMODEL; S3D::INIT_S3DMODEL( *mp ); return mp;}
void S3D::Init3DMaterial( SMATERIAL& aMat ){ S3D::INIT_SMATERIAL( aMat ); return;}
void S3D::Init3DMesh( SMESH& aMesh ){ S3D::INIT_SMESH( aMesh );
return;}
void S3D::GetLibVersion( unsigned char* Major, unsigned char* Minor, unsigned char* Patch, unsigned char* Revision ){ if( Major ) *Major = KICADSG_VERSION_MAJOR;
if( Minor ) *Minor = KICADSG_VERSION_MINOR;
if( Revision ) *Revision = KICADSG_VERSION_REVISION;
if( Patch ) *Patch = KICADSG_VERSION_PATCH;
return;}
SGVECTOR S3D::CalcTriNorm( const SGPOINT& p1, const SGPOINT& p2, const SGPOINT& p3 ){ glm::dvec3 tri = glm::dvec3( 0.0, 0.0, 0.0 ); glm::dvec3 pts[3];
pts[0] = glm::dvec3( p1.x, p1.y, p1.z ); pts[1] = glm::dvec3( p2.x, p2.y, p2.z ); pts[2] = glm::dvec3( p3.x, p3.y, p3.z );
// degenerate points are given a default 0, 0, 1 normal
if( S3D::degenerate( pts ) ) return SGVECTOR( 0.0, 0.0, 1.0 );
// normal
tri = glm::cross( pts[1] - pts[0], pts[2] - pts[0] ); glm::normalize( tri );
return SGVECTOR( tri.x, tri.y, tri.z );}
S3D::SGTYPES S3D::GetSGNodeType( SGNODE* aNode ){ if( NULL == aNode ) return SGTYPE_END;
return aNode->GetNodeType();}
SGNODE* S3D::GetSGNodeParent( SGNODE* aNode ){ if( NULL == aNode ) return NULL;
return aNode->GetParent();}
bool S3D::AddSGNodeRef( SGNODE* aParent, SGNODE* aChild ){ if( NULL == aParent || NULL == aChild ) return false;
return aParent->AddRefNode( aChild );}
bool S3D::AddSGNodeChild( SGNODE* aParent, SGNODE* aChild ){ if( NULL == aParent || NULL == aChild ) return false;
return aParent->AddChildNode( aChild );}
void S3D::AssociateSGNodeWrapper( SGNODE* aObject, SGNODE** aRefPtr ){ if( NULL == aObject || NULL == aRefPtr || aObject != *aRefPtr ) return;
aObject->AssociateWrapper( aRefPtr );
return;}
|