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