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