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