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
26 #include <iostream>
27 #include <sstream>
28 #include <wx/log.h>
29
30 #include "3d_cache/sg/sg_shape.h"
31 #include "3d_cache/sg/sg_faceset.h"
32 #include "3d_cache/sg/sg_appearance.h"
33 #include "3d_cache/sg/sg_helpers.h"
34 #include "3d_cache/sg/sg_coordindex.h"
35 #include "3d_cache/sg/sg_coords.h"
36 #include "3d_cache/sg/sg_colors.h"
37 #include "3d_cache/sg/sg_normals.h"
38
39
SGSHAPE(SGNODE * aParent)40 SGSHAPE::SGSHAPE( SGNODE* aParent ) : SGNODE( aParent )
41 {
42 m_SGtype = S3D::SGTYPE_SHAPE;
43 m_Appearance = nullptr;
44 m_RAppearance = nullptr;
45 m_FaceSet = nullptr;
46 m_RFaceSet = nullptr;
47
48 if( nullptr != aParent && S3D::SGTYPE_TRANSFORM != aParent->GetNodeType() )
49 {
50 m_Parent = nullptr;
51
52 wxLogTrace( MASK_3D_SG, "%s:%s:%d * [BUG] inappropriate parent to SGSHAPE (type %d)",
53 __FILE__, __FUNCTION__, __LINE__, aParent->GetNodeType() );
54 }
55 else if( nullptr != aParent && S3D::SGTYPE_TRANSFORM == aParent->GetNodeType() )
56 {
57 m_Parent->AddChildNode( this );
58 }
59 }
60
61
~SGSHAPE()62 SGSHAPE::~SGSHAPE()
63 {
64 // drop references
65 if( m_RAppearance )
66 {
67 m_RAppearance->delNodeRef( this );
68 m_RAppearance = nullptr;
69 }
70
71 if( m_RFaceSet )
72 {
73 m_RFaceSet->delNodeRef( this );
74 m_RFaceSet = nullptr;
75 }
76
77 // delete objects
78 if( m_Appearance )
79 {
80 m_Appearance->SetParent( nullptr, false );
81 delete m_Appearance;
82 m_Appearance = nullptr;
83 }
84
85 if( m_FaceSet )
86 {
87 m_FaceSet->SetParent( nullptr, false );
88 delete m_FaceSet;
89 m_FaceSet = nullptr;
90 }
91 }
92
93
SetParent(SGNODE * aParent,bool notify)94 bool SGSHAPE::SetParent( SGNODE* aParent, bool notify )
95 {
96 if( nullptr != m_Parent )
97 {
98 if( aParent == m_Parent )
99 return true;
100
101 // handle the change in parents
102 if( notify )
103 m_Parent->unlinkChildNode( this );
104
105 m_Parent = nullptr;
106
107 if( nullptr == aParent )
108 return true;
109 }
110
111 // only a SGTRANSFORM may be parent to a SGSHAPE
112 if( nullptr != aParent && S3D::SGTYPE_TRANSFORM != aParent->GetNodeType() )
113 return false;
114
115 m_Parent = aParent;
116
117 if( m_Parent )
118 m_Parent->AddChildNode( this );
119
120 return true;
121 }
122
123
FindNode(const char * aNodeName,const SGNODE * aCaller)124 SGNODE* SGSHAPE::FindNode( const char* aNodeName, const SGNODE* aCaller )
125 {
126 if( nullptr == aNodeName || 0 == aNodeName[0] )
127 return nullptr;
128
129 if( !m_Name.compare( aNodeName ) )
130 return this;
131
132 SGNODE* tmp = nullptr;
133
134 if( nullptr != m_Appearance )
135 {
136 tmp = m_Appearance->FindNode( aNodeName, this );
137
138 if( tmp )
139 {
140 return tmp;
141 }
142 }
143
144 if( nullptr != m_FaceSet )
145 {
146 tmp = m_FaceSet->FindNode( aNodeName, this );
147
148 if( tmp )
149 {
150 return tmp;
151 }
152 }
153
154 // query the parent if appropriate
155 if( aCaller == m_Parent || nullptr == m_Parent )
156 return nullptr;
157
158 return m_Parent->FindNode( aNodeName, this );
159 }
160
161
unlinkNode(const SGNODE * aNode,bool isChild)162 void SGSHAPE::unlinkNode( const SGNODE* aNode, bool isChild )
163 {
164 if( nullptr == aNode )
165 return;
166
167 if( isChild )
168 {
169 if( aNode == m_Appearance )
170 {
171 m_Appearance = nullptr;
172 return;
173 }
174
175 if( aNode == m_FaceSet )
176 {
177 m_FaceSet = nullptr;
178 return;
179 }
180 }
181 else
182 {
183 if( aNode == m_RAppearance )
184 {
185 delNodeRef( this );
186 m_RAppearance = nullptr;
187 return;
188 }
189
190 if( aNode == m_RFaceSet )
191 {
192 delNodeRef( this );
193 m_RFaceSet = nullptr;
194 return;
195 }
196 }
197
198 wxLogTrace( MASK_3D_SG, "%s:%s:%d * [BUG] unlinkNode() did not find its target",
199 __FILE__, __FUNCTION__, __LINE__ );
200 }
201
202
unlinkChildNode(const SGNODE * aNode)203 void SGSHAPE::unlinkChildNode( const SGNODE* aNode )
204 {
205 unlinkNode( aNode, true );
206 }
207
208
unlinkRefNode(const SGNODE * aNode)209 void SGSHAPE::unlinkRefNode( const SGNODE* aNode )
210 {
211 unlinkNode( aNode, false );
212 }
213
214
addNode(SGNODE * aNode,bool isChild)215 bool SGSHAPE::addNode( SGNODE* aNode, bool isChild )
216 {
217 wxCHECK( aNode, false );
218
219 if( S3D::SGTYPE_APPEARANCE == aNode->GetNodeType() )
220 {
221 if( m_Appearance || m_RAppearance )
222 {
223 if( aNode != m_Appearance && aNode != m_RAppearance )
224 {
225 wxLogTrace( MASK_3D_SG, "%s:%s:%d * [BUG] assigning multiple Appearance nodes",
226 __FILE__, __FUNCTION__, __LINE__ );
227
228 return false;
229 }
230
231 return true;
232 }
233
234 if( isChild )
235 {
236 m_Appearance = (SGAPPEARANCE*)aNode;
237 m_Appearance->SetParent( this );
238 }
239 else
240 {
241 m_RAppearance = (SGAPPEARANCE*)aNode;
242 m_RAppearance->addNodeRef( this );
243 }
244
245 return true;
246 }
247
248 if( S3D::SGTYPE_FACESET == aNode->GetNodeType() )
249 {
250 if( m_FaceSet || m_RFaceSet )
251 {
252 if( aNode != m_FaceSet && aNode != m_RFaceSet )
253 {
254 wxLogTrace( MASK_3D_SG, "%s:%s:%d * [BUG] assigning multiple FaceSet nodes",
255 __FILE__, __FUNCTION__, __LINE__ );
256
257 return false;
258 }
259
260 return true;
261 }
262
263 if( isChild )
264 {
265 m_FaceSet = (SGFACESET*)aNode;
266 m_FaceSet->SetParent( this );
267 }
268 else
269 {
270 m_RFaceSet = (SGFACESET*)aNode;
271 m_RFaceSet->addNodeRef( this );
272 }
273
274 return true;
275 }
276
277 wxLogTrace( MASK_3D_SG, "%s:%s:%d * [BUG] object %s is not a valid type for this object (%d)",
278 __FILE__, __FUNCTION__, __LINE__, aNode->GetName(), aNode->GetNodeType() );
279
280 return false;
281 }
282
283
AddRefNode(SGNODE * aNode)284 bool SGSHAPE::AddRefNode( SGNODE* aNode )
285 {
286 return addNode( aNode, false );
287 }
288
289
AddChildNode(SGNODE * aNode)290 bool SGSHAPE::AddChildNode( SGNODE* aNode )
291 {
292 return addNode( aNode, true );
293 }
294
295
ReNameNodes(void)296 void SGSHAPE::ReNameNodes( void )
297 {
298 m_written = false;
299
300 // rename this node
301 m_Name.clear();
302 GetName();
303
304 // rename Appearance
305 if( m_Appearance )
306 m_Appearance->ReNameNodes();
307
308 // rename FaceSet
309 if( m_FaceSet )
310 m_FaceSet->ReNameNodes();
311 }
312
313
WriteVRML(std::ostream & aFile,bool aReuseFlag)314 bool SGSHAPE::WriteVRML( std::ostream& aFile, bool aReuseFlag )
315 {
316 if( !m_Appearance && !m_RAppearance && !m_FaceSet && !m_RFaceSet )
317 {
318 return false;
319 }
320
321 if( aReuseFlag )
322 {
323 if( !m_written )
324 {
325 aFile << "DEF " << GetName() << " Shape {\n";
326 m_written = true;
327 }
328 else
329 {
330 aFile << " USE " << GetName() << "\n";
331 return true;
332 }
333 }
334 else
335 {
336 aFile << " Shape {\n";
337 }
338
339 if( m_Appearance )
340 m_Appearance->WriteVRML( aFile, aReuseFlag );
341
342 if( m_RAppearance )
343 m_RAppearance->WriteVRML( aFile, aReuseFlag );
344
345 if( m_FaceSet )
346 m_FaceSet->WriteVRML( aFile, aReuseFlag );
347
348 if( m_RFaceSet )
349 m_RFaceSet->WriteVRML( aFile, aReuseFlag );
350
351 aFile << "}\n";
352
353 return true;
354 }
355
356
WriteCache(std::ostream & aFile,SGNODE * parentNode)357 bool SGSHAPE::WriteCache( std::ostream& aFile, SGNODE* parentNode )
358 {
359 if( nullptr == parentNode )
360 {
361 wxCHECK( m_Parent, false );
362
363 SGNODE* np = m_Parent;
364
365 while( nullptr != np->GetParent() )
366 np = np->GetParent();
367
368 if( np->WriteCache( aFile, nullptr ) )
369 {
370 m_written = true;
371 return true;
372 }
373
374 return false;
375 }
376
377 wxCHECK( parentNode == m_Parent, false );
378
379 if( !aFile.good() )
380 {
381 wxLogTrace( MASK_3D_SG, "%s:%s:%d * [BUG] bad stream", __FILE__, __FUNCTION__, __LINE__ );
382
383 return false;
384 }
385
386 // check if any references are unwritten and swap parents if so
387 if( nullptr != m_RAppearance && !m_RAppearance->isWritten() )
388 m_RAppearance->SwapParent(this);
389
390 if( nullptr != m_RFaceSet && !m_RFaceSet->isWritten() )
391 m_RFaceSet->SwapParent( this );
392
393 aFile << "[" << GetName() << "]";
394 #define NITEMS 4
395 bool items[NITEMS];
396 int i;
397
398 for( i = 0; i < NITEMS; ++i )
399 items[i] = 0;
400
401 i = 0;
402
403 if( nullptr != m_Appearance )
404 items[i] = true;
405
406 ++i;
407
408 if( nullptr != m_RAppearance )
409 items[i] = true;
410
411 ++i;
412
413 if( nullptr != m_FaceSet )
414 items[i] = true;
415
416 ++i;
417
418 if( nullptr != m_RFaceSet )
419 items[i] = true;
420
421 for( int jj = 0; jj < NITEMS; ++jj )
422 aFile.write( (char*)&items[jj], sizeof(bool) );
423
424 if( items[0] )
425 m_Appearance->WriteCache( aFile, this );
426
427 if( items[1] )
428 aFile << "[" << m_RAppearance->GetName() << "]";
429
430 if( items[2] )
431 m_FaceSet->WriteCache( aFile, this );
432
433 if( items[3] )
434 aFile << "[" << m_RFaceSet->GetName() << "]";
435
436 if( aFile.fail() )
437 return false;
438
439 m_written = true;
440 return true;
441 }
442
443
ReadCache(std::istream & aFile,SGNODE * parentNode)444 bool SGSHAPE::ReadCache( std::istream& aFile, SGNODE* parentNode )
445 {
446 wxCHECK( m_Appearance == nullptr && m_RAppearance == nullptr && m_FaceSet == nullptr &&
447 m_RFaceSet == nullptr, false );
448
449 #define NITEMS 4
450 bool items[NITEMS];
451
452 for( int i = 0; i < NITEMS; ++i )
453 aFile.read( (char*)&items[i], sizeof(bool) );
454
455 if( ( items[0] && items[1] ) || ( items[2] && items[3] ) )
456 {
457 wxLogTrace( MASK_3D_SG,
458 "%s:%s:%d * [INFO] corrupt data; multiple item definitions at position %ul",
459 __FILE__, __FUNCTION__, __LINE__, static_cast<unsigned long>( aFile.tellg() ) );
460
461 return false;
462 }
463
464 std::string name;
465
466 if( items[0] )
467 {
468 if( S3D::SGTYPE_APPEARANCE != S3D::ReadTag( aFile, name ) )
469 {
470 wxLogTrace( MASK_3D_SG,
471 "%s:%s:%d * [INFO] corrupt data; bad child appearance tag at position %ul",
472 __FILE__, __FUNCTION__, __LINE__,
473 static_cast<unsigned long>( aFile.tellg() ) );
474
475 return false;
476 }
477
478 m_Appearance = new SGAPPEARANCE( this );
479 m_Appearance->SetName( name.c_str() );
480
481 if( !m_Appearance->ReadCache( aFile, this ) )
482 {
483 wxLogTrace( MASK_3D_SG, "%s:%s:%d * [INFO] corrupt data while reading appearance '%s'",
484 __FILE__, __FUNCTION__, __LINE__, name );
485
486 return false;
487 }
488 }
489
490 if( items[1] )
491 {
492 if( S3D::SGTYPE_APPEARANCE != S3D::ReadTag( aFile, name ) )
493 {
494 wxLogTrace( MASK_3D_SG,
495 "%s:%s:%d * [INFO] corrupt data; bad ref appearance tag at position %ul",
496 __FILE__, __FUNCTION__, __LINE__,
497 static_cast<unsigned long>( aFile.tellg() ) );
498
499 return false;
500 }
501
502 SGNODE* np = FindNode( name.c_str(), this );
503
504 if( !np )
505 {
506 wxLogTrace( MASK_3D_SG,
507 "%s:%s:%d * [INFO] corrupt data: cannot find ref appearance '%s'",
508 __FILE__, __FUNCTION__, __LINE__, name );
509
510 return false;
511 }
512
513 if( S3D::SGTYPE_APPEARANCE != np->GetNodeType() )
514 {
515 wxLogTrace( MASK_3D_SG,
516 "%s:%s:%d * [INFO] corrupt data: type is not SGAPPEARANCE '%s'",
517 __FILE__, __FUNCTION__, __LINE__, name );
518
519 return false;
520 }
521
522 m_RAppearance = (SGAPPEARANCE*)np;
523 m_RAppearance->addNodeRef( this );
524 }
525
526 if( items[2] )
527 {
528 if( S3D::SGTYPE_FACESET != S3D::ReadTag( aFile, name ) )
529 {
530 wxLogTrace( MASK_3D_SG,
531 "%s:%s:%d * [INFO] corrupt data; bad child face set tag at position %ul",
532 __FILE__, __FUNCTION__, __LINE__,
533 static_cast<unsigned long>( aFile.tellg() ) );
534
535 return false;
536 }
537
538 m_FaceSet = new SGFACESET( this );
539 m_FaceSet->SetName( name.c_str() );
540
541 if( !m_FaceSet->ReadCache( aFile, this ) )
542 {
543 wxLogTrace( MASK_3D_SG,
544 "%s:%s:%d * [INFO] corrupt data while reading face set '%s'",
545 __FILE__, __FUNCTION__, __LINE__, name );
546
547 return false;
548 }
549 }
550
551 if( items[3] )
552 {
553 if( S3D::SGTYPE_FACESET != S3D::ReadTag( aFile, name ) )
554 {
555 wxLogTrace( MASK_3D_SG,
556 "%s:%s:%d * [INFO] corrupt data; bad ref face set tag at position %ul",
557 __FILE__, __FUNCTION__, __LINE__,
558 static_cast<unsigned long>( aFile.tellg() ) );
559
560 return false;
561 }
562
563 SGNODE* np = FindNode( name.c_str(), this );
564
565 if( !np )
566 {
567 wxLogTrace( MASK_3D_SG,
568 "%s:%s:%d * [INFO] corrupt data: cannot find ref face set '%s'",
569 __FILE__, __FUNCTION__, __LINE__, name );
570
571 return false;
572 }
573
574 if( S3D::SGTYPE_FACESET != np->GetNodeType() )
575 {
576 wxLogTrace( MASK_3D_SG,
577 "%s:%s:%d * [INFO] corrupt data: type is not SGFACESET '%s'",
578 __FILE__, __FUNCTION__, __LINE__, name );
579
580 return false;
581 }
582
583 m_RFaceSet = (SGFACESET*)np;
584 m_RFaceSet->addNodeRef( this );
585 }
586
587 if( aFile.fail() )
588 return false;
589
590 return true;
591 }
592
593
Prepare(const glm::dmat4 * aTransform,S3D::MATLIST & materials,std::vector<SMESH> & meshes)594 bool SGSHAPE::Prepare( const glm::dmat4* aTransform, S3D::MATLIST& materials,
595 std::vector< SMESH >& meshes )
596 {
597 SMESH m;
598 S3D::INIT_SMESH( m );
599
600 SGAPPEARANCE* pa = m_Appearance;
601 SGFACESET* pf = m_FaceSet;
602
603 if( nullptr == pa )
604 pa = m_RAppearance;
605
606 if( nullptr == pf )
607 pf = m_RFaceSet;
608
609 // no face sets = nothing to render, which is valid though pointless
610 if( nullptr == pf )
611 return true;
612
613 if( !pf->validate() )
614 {
615 wxLogTrace( MASK_3D_SG, "%s:%s:%d * [INFO] bad model; inconsistent data",
616 __FILE__, __FUNCTION__, __LINE__ );
617
618 return true;
619 }
620
621 if( nullptr == pa )
622 {
623 m.m_MaterialIdx = 0;
624 }
625 else
626 {
627 int idx;
628
629 if( !S3D::GetMatIndex( materials, pa, idx ) )
630 {
631 m.m_MaterialIdx = 0;
632 }
633 else
634 {
635 m.m_MaterialIdx = idx;
636 }
637 }
638
639 SGCOLORS* pc = pf->m_Colors;
640 SGCOORDS* pv = pf->m_Coords;
641 SGCOORDINDEX* vidx = pf->m_CoordIndices;
642 SGNORMALS* pn = pf->m_Normals;
643
644 if( nullptr == pc )
645 pc = pf->m_RColors;
646
647 if( nullptr == pv )
648 pv = pf->m_RCoords;
649
650 if( nullptr == pn )
651 pn = pf->m_RNormals;
652
653 // set the vertex points and indices
654 size_t nCoords = 0;
655 SGPOINT* pCoords = nullptr;
656 pv->GetCoordsList( nCoords, pCoords );
657
658 size_t nColors = 0;
659 SGCOLOR* pColors = nullptr;
660
661 if( pc )
662 {
663 // check the vertex colors
664 pc->GetColorList( nColors, pColors );
665
666 if( nColors < nCoords )
667 {
668 wxLogTrace( MASK_3D_SG,
669 "%s:%s:%d * [INFO] bad model; not enough colors per vertex (%ul vs %ul)",
670 __FILE__, __FUNCTION__, __LINE__, static_cast<unsigned long>( nColors ),
671 static_cast<unsigned long>( nCoords ) );
672
673 return true;
674 }
675 }
676
677 // set the vertex indices
678 size_t nvidx = 0;
679 int* lv = nullptr;
680 vidx->GetIndices( nvidx, lv );
681
682 // note: reduce the vertex set to include only the referenced vertices
683 std::vector< int > vertices; // store the list of temp vertex indices
684 std::map< int, unsigned int > indexmap; // map temp vertex to true vertex
685 std::map< int, unsigned int >::iterator mit;
686
687 for( unsigned int i = 0; i < nvidx; ++i )
688 {
689 mit = indexmap.find( lv[i] );
690
691 if( mit == indexmap.end() )
692 {
693 indexmap.insert( std::pair< int, unsigned int >( lv[i], vertices.size() ) );
694 vertices.push_back( lv[i] );
695 }
696 }
697
698 if( vertices.size() < 3 )
699 {
700 wxLogTrace( MASK_3D_SG, "%s:%s:%d * [INFO] bad model; not enough vertices",
701 __FILE__, __FUNCTION__, __LINE__ );
702
703 return true;
704 }
705
706 // construct the final vertex/color list
707 SFVEC3F* lColors = nullptr;
708 SFVEC3F* lCoords = new SFVEC3F[ vertices.size() ];
709 int ti;
710
711 if( pc )
712 {
713 lColors = new SFVEC3F[vertices.size()];
714 m.m_Color = lColors;
715 }
716
717 if( pc )
718 {
719 for( size_t i = 0; i < vertices.size(); ++i )
720 {
721 ti = vertices[i];
722 glm::dvec4 pt( pCoords[ti].x, pCoords[ti].y, pCoords[ti].z, 1.0 );
723 pt = (*aTransform) * pt;
724 pColors[ti].GetColor( lColors[i].x, lColors[i].y, lColors[i].z );
725 lCoords[i] = SFVEC3F( pt.x, pt.y, pt.z );
726 }
727 }
728 else
729 {
730 for( size_t i = 0; i < vertices.size(); ++i )
731 {
732 ti = vertices[i];
733 glm::dvec4 pt( pCoords[ti].x, pCoords[ti].y, pCoords[ti].z, 1.0 );
734 pt = (*aTransform) * pt;
735 lCoords[i] = SFVEC3F( pt.x, pt.y, pt.z );
736 }
737 }
738
739 m.m_VertexSize = (unsigned int) vertices.size();
740 m.m_Positions = lCoords;
741 unsigned int* lvidx = new unsigned int[ nvidx ];
742
743 for( unsigned int i = 0; i < nvidx; ++i )
744 {
745 mit = indexmap.find( lv[i] );
746 lvidx[i] = mit->second;
747 }
748
749 m.m_FaceIdxSize = (unsigned int )nvidx;
750 m.m_FaceIdx = lvidx;
751
752 // set the per-vertex normals
753 size_t nNorms = 0;
754 SGVECTOR* pNorms = nullptr;
755 double x, y, z;
756
757 pn->GetNormalList( nNorms, pNorms );
758 SFVEC3F* lNorms = new SFVEC3F[ vertices.size() ];
759
760 for( size_t i = 0; i < vertices.size(); ++i )
761 {
762 ti = vertices[i];
763 pNorms[ti].GetVector( x, y, z );
764 glm::dvec4 pt( x, y, z, 0.0 );
765 pt = (*aTransform) * pt;
766
767 lNorms[i] = SFVEC3F( pt.x, pt.y, pt.z );
768 }
769
770 m.m_Normals = lNorms;
771 meshes.push_back( m );
772
773 return true;
774 }
775