You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

671 lines
16 KiB

  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2015-2017 Cirilo Bernardo <cirilo.bernardo@gmail.com>
  5. *
  6. * This program is free software; you can redistribute it and/or
  7. * modify it under the terms of the GNU General Public License
  8. * as published by the Free Software Foundation; either version 2
  9. * of the License, or (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with this program; if not, you may find one here:
  18. * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
  19. * or you may search the http://www.gnu.org website for the version 2 license,
  20. * or you may write to the Free Software Foundation, Inc.,
  21. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
  22. */
  23. #include <iostream>
  24. #include <sstream>
  25. #include <fstream>
  26. #include <wx/filename.h>
  27. #include <wx/log.h>
  28. #include "plugins/3dapi/ifsg_api.h"
  29. #include "plugins/3dapi/sg_version.h"
  30. #include "streamwrapper.h"
  31. #include "3d_cache/sg/sg_node.h"
  32. #include "3d_cache/sg/scenegraph.h"
  33. #include "3d_cache/sg/sg_appearance.h"
  34. #include "3d_cache/sg/sg_shape.h"
  35. #include "3d_cache/sg/sg_helpers.h"
  36. #ifdef DEBUG
  37. static char BadNode[] = " * [BUG] NULL pointer passed for aNode\n";
  38. #endif
  39. // version format of the cache file
  40. #define SG_VERSION_TAG "VERSION:2"
  41. static void formatMaterial( SMATERIAL& mat, SGAPPEARANCE const* app )
  42. {
  43. float v0, v1, v2;
  44. app->ambient.GetColor( v0, v1, v2 );
  45. mat.m_Ambient.x = v0;
  46. mat.m_Ambient.y = v1;
  47. mat.m_Ambient.z = v2;
  48. app->diffuse.GetColor( v0, v1, v2 );
  49. mat.m_Diffuse.x = v0;
  50. mat.m_Diffuse.y = v1;
  51. mat.m_Diffuse.z = v2;
  52. mat.m_Ambient.x *= v0;
  53. mat.m_Ambient.y *= v1;
  54. mat.m_Ambient.z *= v2;
  55. app->emissive.GetColor( v0, v1, v2 );
  56. mat.m_Emissive.x = v0;
  57. mat.m_Emissive.y = v1;
  58. mat.m_Emissive.z = v2;
  59. app->specular.GetColor( v0, v1, v2 );
  60. mat.m_Specular.x = v0;
  61. mat.m_Specular.y = v1;
  62. mat.m_Specular.z = v2;
  63. mat.m_Shininess = app->shininess;
  64. mat.m_Transparency = app->transparency;
  65. return;
  66. }
  67. bool S3D::WriteVRML( const char* filename, bool overwrite, SGNODE* aTopNode,
  68. bool reuse, bool renameNodes )
  69. {
  70. if( NULL == filename || filename[0] == 0 )
  71. return false;
  72. wxString ofile = wxString::FromUTF8Unchecked( filename );
  73. if( wxFileName::Exists( ofile ) )
  74. {
  75. if( !overwrite )
  76. return false;
  77. // make sure we make no attempt to write a directory
  78. if( !wxFileName::FileExists( ofile ) )
  79. return false;
  80. }
  81. if( NULL == aTopNode )
  82. {
  83. #ifdef DEBUG
  84. do {
  85. std::ostringstream ostr;
  86. ostr << __FILE__ << ": " << __FUNCTION__ << ": " << __LINE__ << "\n";
  87. ostr << " * [BUG] NULL pointer passed for aTopNode";
  88. wxLogTrace( MASK_3D_SG, "%s\n", ostr.str().c_str() );
  89. } while( 0 );
  90. #endif
  91. return false;
  92. }
  93. if( S3D::SGTYPE_TRANSFORM != aTopNode->GetNodeType() )
  94. {
  95. #ifdef DEBUG
  96. do {
  97. std::ostringstream ostr;
  98. ostr << __FILE__ << ": " << __FUNCTION__ << ": " << __LINE__ << "\n";
  99. ostr << " * [BUG] aTopNode is not a SCENEGRAPH object";
  100. wxLogTrace( MASK_3D_SG, "%s\n", ostr.str().c_str() );
  101. } while( 0 );
  102. #endif
  103. return false;
  104. }
  105. OPEN_OSTREAM( op, filename );
  106. if( op.fail() )
  107. {
  108. wxString errmsg;
  109. errmsg << __FILE__ << ": " << __FUNCTION__ << ": " << __LINE__ << "\n";
  110. errmsg << " * [INFO] " << "failed to open file" << " '" << filename << "'";
  111. wxLogTrace( MASK_3D_SG, errmsg );
  112. return false;
  113. }
  114. op.imbue( std::locale( "C" ) );
  115. op << "#VRML V2.0 utf8\n";
  116. if( renameNodes )
  117. {
  118. aTopNode->ResetNodeIndex();
  119. aTopNode->ReNameNodes();
  120. }
  121. aTopNode->WriteVRML( op, reuse );
  122. if( !op.fail() )
  123. {
  124. CLOSE_STREAM( op );
  125. return true;
  126. }
  127. CLOSE_STREAM( op );
  128. wxString errmsg;
  129. errmsg << __FILE__ << ": " << __FUNCTION__ << ": " << __LINE__ << "\n";
  130. errmsg << " * [INFO] " << "problems encountered writing file" << " '" << filename << "'";
  131. wxLogTrace( MASK_3D_SG, errmsg );
  132. return false;
  133. }
  134. void S3D::ResetNodeIndex( SGNODE* aNode )
  135. {
  136. if( NULL == aNode )
  137. {
  138. #ifdef DEBUG
  139. do {
  140. std::ostringstream ostr;
  141. ostr << __FILE__ << ": " << __FUNCTION__ << ": " << __LINE__ << "\n";
  142. ostr << BadNode;
  143. wxLogTrace( MASK_3D_SG, "%s", ostr.str().c_str() );
  144. } while( 0 );
  145. #endif
  146. return;
  147. }
  148. aNode->ResetNodeIndex();
  149. return;
  150. }
  151. void S3D::RenameNodes( SGNODE* aNode )
  152. {
  153. if( NULL == aNode )
  154. {
  155. #ifdef DEBUG
  156. do {
  157. std::ostringstream ostr;
  158. ostr << __FILE__ << ": " << __FUNCTION__ << ": " << __LINE__ << "\n";
  159. ostr << BadNode;
  160. wxLogTrace( MASK_3D_SG, "%s", ostr.str().c_str() );
  161. } while( 0 );
  162. #endif
  163. return;
  164. }
  165. aNode->ReNameNodes();
  166. return;
  167. }
  168. void S3D::DestroyNode( SGNODE* aNode )
  169. {
  170. if( NULL == aNode )
  171. {
  172. #ifdef DEBUG
  173. do {
  174. std::ostringstream ostr;
  175. ostr << __FILE__ << ": " << __FUNCTION__ << ": " << __LINE__ << "\n";
  176. ostr << BadNode;
  177. wxLogTrace( MASK_3D_SG, "%s", ostr.str().c_str() );
  178. } while( 0 );
  179. #endif
  180. return;
  181. }
  182. delete aNode;
  183. return;
  184. }
  185. bool S3D::WriteCache( const char* aFileName, bool overwrite, SGNODE* aNode,
  186. const char* aPluginInfo )
  187. {
  188. if( NULL == aFileName || aFileName[0] == 0 )
  189. return false;
  190. wxString ofile = wxString::FromUTF8Unchecked( aFileName );
  191. if( NULL == aNode )
  192. {
  193. #ifdef DEBUG
  194. do {
  195. std::ostringstream ostr;
  196. ostr << __FILE__ << ": " << __FUNCTION__ << ": " << __LINE__ << "\n";
  197. ostr << BadNode;
  198. wxLogTrace( MASK_3D_SG, "%s", ostr.str().c_str() );
  199. } while( 0 );
  200. #endif
  201. return false;
  202. }
  203. if( wxFileName::Exists( ofile ) )
  204. {
  205. if( !overwrite )
  206. {
  207. wxString errmsg;
  208. errmsg << __FILE__ << ": " << __FUNCTION__ << ": " << __LINE__ << "\n";
  209. errmsg << " * [INFO] " << "file exists; not overwriting" << " '";
  210. errmsg << aFileName << "'";
  211. wxLogTrace( MASK_3D_SG, errmsg );
  212. return false;
  213. }
  214. // make sure we make no attempt to write a directory
  215. if( !wxFileName::FileExists( aFileName ) )
  216. {
  217. wxString errmsg;
  218. errmsg << __FILE__ << ": " << __FUNCTION__ << ": " << __LINE__ << "\n";
  219. errmsg << " * [INFO] " << "specified path is a directory" << " '";
  220. errmsg << aFileName << "'";
  221. wxLogTrace( MASK_3D_SG, errmsg );
  222. return false;
  223. }
  224. }
  225. OPEN_OSTREAM( output, aFileName );
  226. if( output.fail() )
  227. {
  228. wxString errmsg;
  229. errmsg << __FILE__ << ": " << __FUNCTION__ << ": " << __LINE__ << "\n";
  230. errmsg << " * [INFO] " << "failed to open file" << " '" << aFileName << "'";
  231. wxLogTrace( MASK_3D_SG, errmsg );
  232. return false;
  233. }
  234. output << "(" << SG_VERSION_TAG << ")";
  235. if( NULL != aPluginInfo && aPluginInfo[0] != 0 )
  236. output << "(" << aPluginInfo << ")";
  237. else
  238. output << "(INTERNAL:0.0.0.0)";
  239. bool rval = aNode->WriteCache( output, NULL );
  240. CLOSE_STREAM( output );
  241. if( !rval )
  242. {
  243. #ifdef DEBUG
  244. do {
  245. std::ostringstream ostr;
  246. ostr << __FILE__ << ": " << __FUNCTION__ << ": " << __LINE__ << "\n";
  247. ostr << " * [INFO] problems encountered writing cache file '";
  248. ostr << aFileName << "'";
  249. wxLogTrace( MASK_3D_SG, "%s\n", ostr.str().c_str() );
  250. } while( 0 );
  251. #endif
  252. // delete the defective file
  253. wxRemoveFile( ofile );
  254. }
  255. return rval;
  256. }
  257. SGNODE* S3D::ReadCache( const char* aFileName, void* aPluginMgr,
  258. bool (*aTagCheck)( const char*, void* ) )
  259. {
  260. if( NULL == aFileName || aFileName[0] == 0 )
  261. return NULL;
  262. wxString ofile = wxString::FromUTF8Unchecked( aFileName );
  263. if( !wxFileName::FileExists( aFileName ) )
  264. {
  265. std::ostringstream ostr;
  266. ostr << __FILE__ << ": " << __FUNCTION__ << ": " << __LINE__ << "\n";
  267. wxString errmsg = _( "no such file" );
  268. ostr << " * [INFO] " << errmsg.ToUTF8() << " '";
  269. ostr << aFileName << "'";
  270. wxLogTrace( MASK_3D_SG, "%s\n", ostr.str().c_str() );
  271. return NULL;
  272. }
  273. SGNODE* np = new SCENEGRAPH( NULL );
  274. if( NULL == np )
  275. {
  276. #ifdef DEBUG
  277. do {
  278. std::ostringstream ostr;
  279. ostr << __FILE__ << ": " << __FUNCTION__ << ": " << __LINE__ << "\n";
  280. ostr << " * [INFO] failed to instantiate SCENEGRAPH";
  281. wxLogTrace( MASK_3D_SG, "%s\n", ostr.str().c_str() );
  282. } while( 0 );
  283. #endif
  284. return NULL;
  285. }
  286. OPEN_ISTREAM( file, aFileName );
  287. if( file.fail() )
  288. {
  289. delete np;
  290. std::ostringstream ostr;
  291. ostr << __FILE__ << ": " << __FUNCTION__ << ": " << __LINE__ << "\n";
  292. wxString errmsg = _( "failed to open file" );
  293. ostr << " * [INFO] " << errmsg.ToUTF8() << " '";
  294. ostr << aFileName << "'";
  295. wxLogTrace( MASK_3D_SG, "%s\n", ostr.str().c_str() );
  296. return NULL;
  297. }
  298. // from SG_VERSION_TAG 1, read the version tag; if it's not the expected tag
  299. // then we fail to read the cache file
  300. do
  301. {
  302. std::string name;
  303. char schar;
  304. file.get( schar );
  305. if( '(' != schar )
  306. {
  307. #ifdef DEBUG
  308. do {
  309. std::ostringstream ostr;
  310. ostr << __FILE__ << ": " << __FUNCTION__ << ": " << __LINE__ << "\n";
  311. ostr << " * [INFO] corrupt data; missing left parenthesis at position ";
  312. ostr << file.tellg();
  313. wxLogTrace( MASK_3D_SG, "%s\n", ostr.str().c_str() );
  314. } while( 0 );
  315. #endif
  316. CLOSE_STREAM( file );
  317. return NULL;
  318. }
  319. file.get( schar );
  320. while( ')' != schar && file.good() )
  321. {
  322. name.push_back( schar );
  323. file.get( schar );
  324. }
  325. if( name.compare( SG_VERSION_TAG ) )
  326. {
  327. CLOSE_STREAM( file );
  328. return NULL;
  329. }
  330. } while( 0 );
  331. // from SG_VERSION_TAG 2, read the PluginInfo string and check that it matches
  332. // version tag; if it's not the expected tag then we fail to read the file
  333. do
  334. {
  335. std::string name;
  336. char schar;
  337. file.get( schar );
  338. if( '(' != schar )
  339. {
  340. #ifdef DEBUG
  341. do {
  342. std::ostringstream ostr;
  343. ostr << __FILE__ << ": " << __FUNCTION__ << ": " << __LINE__ << "\n";
  344. ostr << " * [INFO] corrupt data; missing left parenthesis at position ";
  345. ostr << file.tellg();
  346. wxLogTrace( MASK_3D_SG, "%s\n", ostr.str().c_str() );
  347. } while( 0 );
  348. #endif
  349. CLOSE_STREAM( file );
  350. return NULL;
  351. }
  352. file.get( schar );
  353. while( ')' != schar && file.good() )
  354. {
  355. name.push_back( schar );
  356. file.get( schar );
  357. }
  358. // check the plugin tag
  359. if( NULL != aTagCheck && NULL != aPluginMgr && !aTagCheck( name.c_str(), aPluginMgr ) )
  360. {
  361. CLOSE_STREAM( file );
  362. return NULL;
  363. }
  364. } while( 0 );
  365. bool rval = np->ReadCache( file, NULL );
  366. CLOSE_STREAM( file );
  367. if( !rval )
  368. {
  369. delete np;
  370. std::ostringstream ostr;
  371. ostr << __FILE__ << ": " << __FUNCTION__ << ": " << __LINE__ << "\n";
  372. wxString errmsg = "problems encountered reading cache file";
  373. ostr << " * [INFO] " << errmsg.ToUTF8() << " '";
  374. ostr << aFileName << "'";
  375. wxLogTrace( MASK_3D_SG, "%s\n", ostr.str().c_str() );
  376. return NULL;
  377. }
  378. return np;
  379. }
  380. S3DMODEL* S3D::GetModel( SCENEGRAPH* aNode )
  381. {
  382. if( NULL == aNode )
  383. return NULL;
  384. if( aNode->GetNodeType() != S3D::SGTYPE_TRANSFORM )
  385. return NULL;
  386. S3D::MATLIST materials;
  387. std::vector< SMESH > meshes;
  388. // the materials list shall have a default color; although the VRML
  389. // default is an opaque black, the default used here shall be a median
  390. // gray in hopes that it may help highlight faulty models; this color is
  391. // also typical of MCAD applications. When a model has no associated
  392. // material color it shall be assigned the index 0.
  393. SGAPPEARANCE app( NULL );
  394. app.ambient = SGCOLOR( 0.6f, 0.6f, 0.6f );
  395. app.diffuse = SGCOLOR( 0.6f, 0.6f, 0.6f );
  396. app.specular = app.diffuse;
  397. app.shininess = 0.05f;
  398. app.transparency = 0.0f;
  399. materials.matorder.push_back( &app );
  400. materials.matmap.insert( std::pair< SGAPPEARANCE const*, int >( &app, 0 ) );
  401. if( aNode->Prepare( NULL, materials, meshes ) )
  402. {
  403. if( meshes.empty() )
  404. return NULL;
  405. S3DMODEL* model = S3D::New3DModel();
  406. // add all the materials
  407. size_t j = materials.matorder.size();
  408. SMATERIAL* lmat = new SMATERIAL[j];
  409. for( size_t i = 0; i < j; ++i )
  410. formatMaterial( lmat[i], materials.matorder[i] );
  411. model->m_Materials = lmat;
  412. model->m_MaterialsSize = j;
  413. // add all the meshes
  414. j = meshes.size();
  415. SMESH* lmesh = new SMESH[j];
  416. for( size_t i = 0; i < j; ++i )
  417. lmesh[i] = meshes[i];
  418. model->m_Meshes = lmesh;
  419. model->m_MeshesSize = j;
  420. return model;
  421. }
  422. size_t j = meshes.size();
  423. for( size_t i = 0; i < j; ++i )
  424. S3D::Free3DMesh( meshes[i] );
  425. return NULL;
  426. }
  427. void S3D::Destroy3DModel( S3DMODEL** aModel )
  428. {
  429. if( NULL == aModel || NULL == *aModel )
  430. return;
  431. S3DMODEL* m = *aModel;
  432. S3D::FREE_S3DMODEL( *m );
  433. delete m;
  434. *aModel = NULL;
  435. return;
  436. }
  437. void Free3DModel( S3DMODEL& aModel )
  438. {
  439. S3D::FREE_S3DMODEL( aModel );
  440. return;
  441. }
  442. void S3D::Free3DMesh( SMESH& aMesh )
  443. {
  444. S3D::FREE_SMESH( aMesh );
  445. return;
  446. }
  447. S3DMODEL* S3D::New3DModel( void )
  448. {
  449. S3DMODEL* mp = new S3DMODEL;
  450. S3D::INIT_S3DMODEL( *mp );
  451. return mp;
  452. }
  453. void S3D::Init3DMaterial( SMATERIAL& aMat )
  454. {
  455. S3D::INIT_SMATERIAL( aMat );
  456. return;
  457. }
  458. void S3D::Init3DMesh( SMESH& aMesh )
  459. {
  460. S3D::INIT_SMESH( aMesh );
  461. return;
  462. }
  463. void S3D::GetLibVersion( unsigned char* Major, unsigned char* Minor,
  464. unsigned char* Patch, unsigned char* Revision )
  465. {
  466. if( Major )
  467. *Major = KICADSG_VERSION_MAJOR;
  468. if( Minor )
  469. *Minor = KICADSG_VERSION_MINOR;
  470. if( Revision )
  471. *Revision = KICADSG_VERSION_REVISION;
  472. if( Patch )
  473. *Patch = KICADSG_VERSION_PATCH;
  474. return;
  475. }
  476. SGVECTOR S3D::CalcTriNorm( const SGPOINT& p1, const SGPOINT& p2, const SGPOINT& p3 )
  477. {
  478. glm::dvec3 tri = glm::dvec3( 0.0, 0.0, 0.0 );
  479. glm::dvec3 pts[3];
  480. pts[0] = glm::dvec3( p1.x, p1.y, p1.z );
  481. pts[1] = glm::dvec3( p2.x, p2.y, p2.z );
  482. pts[2] = glm::dvec3( p3.x, p3.y, p3.z );
  483. // degenerate points are given a default 0, 0, 1 normal
  484. if( S3D::degenerate( pts ) )
  485. return SGVECTOR( 0.0, 0.0, 1.0 );
  486. // normal
  487. tri = glm::cross( pts[1] - pts[0], pts[2] - pts[0] );
  488. glm::normalize( tri );
  489. return SGVECTOR( tri.x, tri.y, tri.z );
  490. }
  491. S3D::SGTYPES S3D::GetSGNodeType( SGNODE* aNode )
  492. {
  493. if( NULL == aNode )
  494. return SGTYPE_END;
  495. return aNode->GetNodeType();
  496. }
  497. SGNODE* S3D::GetSGNodeParent( SGNODE* aNode )
  498. {
  499. if( NULL == aNode )
  500. return NULL;
  501. return aNode->GetParent();
  502. }
  503. bool S3D::AddSGNodeRef( SGNODE* aParent, SGNODE* aChild )
  504. {
  505. if( NULL == aParent || NULL == aChild )
  506. return false;
  507. return aParent->AddRefNode( aChild );
  508. }
  509. bool S3D::AddSGNodeChild( SGNODE* aParent, SGNODE* aChild )
  510. {
  511. if( NULL == aParent || NULL == aChild )
  512. return false;
  513. return aParent->AddChildNode( aChild );
  514. }
  515. void S3D::AssociateSGNodeWrapper( SGNODE* aObject, SGNODE** aRefPtr )
  516. {
  517. if( NULL == aObject || NULL == aRefPtr || aObject != *aRefPtr )
  518. return;
  519. aObject->AssociateWrapper( aRefPtr );
  520. return;
  521. }