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