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