1 /*
2  * This program source code file is part of KiCad, a free EDA CAD application.
3  *
4  * Copyright (C) 2016-2017 Cirilo Bernardo <cirilo.bernardo@gmail.com>
5  * Copyright (C) 2021 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 #define GLM_FORCE_RADIANS
27 
28 #include <glm/glm.hpp>
29 #include <glm/gtc/type_ptr.hpp>
30 #include <cmath>
31 
32 #include "wrlfacet.h"
33 
34 #define LOWER_LIMIT (1e-12)
35 
36 
VDegenerate(glm::vec3 * pts)37 static bool VDegenerate( glm::vec3* pts )
38 {
39     // note: only checks the degenerate case of zero length sides; it
40     // does not detect the case of 3 distinct collinear points
41 
42     double dx, dy, dz;
43 
44     dx = double{ pts[1].x } - pts[0].x;
45     dy = double{ pts[1].y } - pts[0].y;
46     dz = double{ pts[1].z } - pts[0].z;
47 
48     if( ( dx*dx + dy*dy + dz*dz ) < LOWER_LIMIT )
49         return true;
50 
51     dx = double{ pts[2].x } - pts[0].x;
52     dy = double{ pts[2].y } - pts[0].y;
53     dz = double{ pts[2].z } - pts[0].z;
54 
55     if( ( dx*dx + dy*dy + dz*dz ) < LOWER_LIMIT )
56         return true;
57 
58     dx = double{ pts[2].x } - pts[1].x;
59     dy = double{ pts[2].y } - pts[1].y;
60     dz = double{ pts[2].z } - pts[1].z;
61 
62     if( ( dx*dx + dy*dy + dz*dz ) < LOWER_LIMIT )
63         return true;
64 
65     return false;
66 }
67 
68 
VCalcTriNorm(const WRLVEC3F & p1,const WRLVEC3F & p2,const WRLVEC3F & p3)69 static WRLVEC3F VCalcTriNorm( const WRLVEC3F& p1, const WRLVEC3F& p2, const WRLVEC3F& p3 )
70 {
71     // note: p1 = reference vertex
72     glm::vec3 tri = glm::vec3( 0.0, 0.0, 0.0 );
73     glm::vec3 pts[3];
74 
75     pts[0] = p1;
76     pts[1] = p2;
77     pts[2] = p3;
78 
79     // degenerate points are given a default 0, 0, 0 normal
80     if( VDegenerate( pts ) )
81         return tri;
82 
83     // normal
84     tri = glm::cross( pts[2] - pts[0], pts[1] - pts[0] );
85 
86     float dn = sqrtf( tri.x * tri.x + tri.y * tri.y + tri.z * tri.z );
87 
88     if( dn > LOWER_LIMIT )
89     {
90         tri.x /= dn;
91         tri.y /= dn;
92         tri.z /= dn;
93     }
94 
95     return tri;
96 }
97 
98 
VCalcCosAngle(const WRLVEC3F & p1,const WRLVEC3F & p2,const WRLVEC3F & p3)99 static float VCalcCosAngle( const WRLVEC3F& p1, const WRLVEC3F& p2, const WRLVEC3F& p3 )
100 {
101     // note: p1 = reference vertex
102     float l12, l13;
103     float dx, dy, dz;
104 
105     dx = p2.x - p1.x;
106     dy = p2.y - p1.y;
107     dz = p2.z - p1.z;
108     float p12 = dx*dx + dy*dy + dz*dz;
109     l12 = sqrtf( p12 );
110 
111     dx = p3.x - p2.x;
112     dy = p3.y - p2.y;
113     dz = p3.z - p2.z;
114     float p23 = dx*dx + dy*dy + dz*dz;
115 
116     dx = p3.x - p1.x;
117     dy = p3.y - p1.y;
118     dz = p3.z - p1.z;
119     float p13 = dx*dx + dy*dy + dz*dz;
120     l13 = sqrtf( p13 );
121 
122     float dn = 2.0f * l12 * l13;
123 
124     // place a limit to prevent calculations from blowing up
125     if( dn < LOWER_LIMIT )
126     {
127         if( ( p12 + p13 - p23 ) < FLT_EPSILON )
128             return -1.0f;
129 
130         if( ( p12 + p13 - p23 ) > FLT_EPSILON )
131             return 1.0f;
132 
133         return 0.0f;
134     }
135 
136     float cosAngle = ( p12 + p13 - p23 ) / dn;
137 
138     // check the domain; errors in the cosAngle calculation can result in domain errors
139     if( cosAngle > 1.0f )
140         cosAngle = 1.0f;
141     else if( cosAngle < -1.0f )
142         cosAngle = -1.0f;
143 
144     // note: we are guaranteed that acosf() is never negative
145     return cosAngle;
146 }
147 
148 
FACET()149 FACET::FACET()
150 {
151     face_normal.x = 0.0;
152     face_normal.y = 0.0;
153     face_normal.z = 0.0;
154     maxIdx = 0;
155 }
156 
157 
Init()158 void FACET::Init()
159 {
160     vertices.clear();
161     colors.clear();
162     indices.clear();
163     norms.clear();
164     vnweight.clear();
165 
166     face_normal.x = 0.0;
167     face_normal.y = 0.0;
168     face_normal.z = 0.0;
169     maxIdx = 0;
170 }
171 
172 
HasMinPoints()173 bool FACET::HasMinPoints()
174 {
175     if( vertices.size() < 3 )
176         return false;
177 
178     return true;
179 }
180 
181 
HasColors()182 bool FACET::HasColors()
183 {
184     if( colors.empty() )
185         return false;
186 
187     return true;
188 }
189 
190 
AddVertex(WRLVEC3F & aVertex,int aIndex)191 void FACET::AddVertex( WRLVEC3F& aVertex, int aIndex )
192 {
193     if( aIndex < 0 )
194         return;
195 
196     vertices.push_back( aVertex );
197     indices.push_back( aIndex );
198 
199     if( aIndex > maxIdx )
200         maxIdx = aIndex;
201 }
202 
203 
AddColor(const SGCOLOR & aColor)204 void FACET::AddColor( const SGCOLOR& aColor )
205 {
206     colors.push_back( aColor );
207 
208     return;
209 }
210 
211 
CalcFaceNormal()212 float FACET::CalcFaceNormal()
213 {
214     // note: this calculation assumes that the face is a convex polygon;
215     // concave polygons may be supported in the future via functions which
216     // split the polygon into triangles
217 
218     if( vertices.size() < 3 )
219         return 0.0;
220 
221     // check if the values were already calculated
222     if( vertices.size() == vnweight.size() )
223         return 0.0;
224 
225     WRLVEC3F lCPts[3];
226 
227     std::vector< WRLVEC3F >::iterator sV = vertices.begin();
228     std::vector< WRLVEC3F >::iterator eV = vertices.end();
229 
230     lCPts[0] = vertices.back();
231     lCPts[1] = *sV;
232     ++sV;
233     lCPts[2] = *sV;
234     ++sV;
235 
236     face_normal = VCalcTriNorm( lCPts[1], lCPts[0], lCPts[2] );
237 
238     vnweight.clear();
239     WRLVEC3F wnorm = face_normal;
240 
241     // calculate area:
242     size_t nv = vertices.size();
243     float a1 = 0.0;
244     glm::vec3 sum( 0.0, 0.0, 0.0 );
245     size_t j = 0;
246 
247     for( size_t i = 1; i < nv; ++i, ++j )
248         sum += glm::cross( vertices[j], vertices[i] );
249 
250     a1 = fabs( glm::dot( face_normal, sum ) );
251     float a2 = acosf( VCalcCosAngle(  lCPts[1], lCPts[0], lCPts[2] ) );
252 
253     wnorm.x *= a1 * a2;
254     wnorm.y *= a1 * a2;
255     wnorm.z *= a1 * a2;
256     vnweight.push_back( wnorm );
257 
258     float maxV = fabs( wnorm.x );
259     float tV = fabs( wnorm.y );
260 
261     if( tV > maxV )
262         maxV = tV;
263 
264     tV = fabs( wnorm.z );
265 
266     if( tV > maxV )
267         maxV = tV;
268 
269     while( sV != eV )
270     {
271         lCPts[0] = lCPts[1];
272         lCPts[1] = lCPts[2];
273         lCPts[2] = *sV;
274         ++sV;
275 
276         wnorm = face_normal;
277         a2 = acosf( VCalcCosAngle(  lCPts[1], lCPts[0], lCPts[2] ) );
278         wnorm.x *= a1 * a2;
279         wnorm.y *= a1 * a2;
280         wnorm.z *= a1 * a2;
281         vnweight.push_back( wnorm );
282 
283         tV = fabs( wnorm.x );
284 
285         if( tV > maxV )
286             maxV = tV;
287 
288         tV = fabs( wnorm.y );
289 
290         if( tV > maxV )
291             maxV = tV;
292 
293         tV = fabs( wnorm.z );
294 
295         if( tV > maxV )
296             maxV = tV;
297     }
298 
299     lCPts[0] = lCPts[1];
300     lCPts[1] = lCPts[2];
301     lCPts[2] = vertices.front();
302 
303     wnorm = face_normal;
304     a2 = acosf( VCalcCosAngle(  lCPts[1], lCPts[0], lCPts[2] ) );
305     wnorm.x *= a1 * a2;
306     wnorm.y *= a1 * a2;
307     wnorm.z *= a1 * a2;
308     vnweight.push_back( wnorm );
309 
310     tV = fabs( wnorm.x );
311 
312     if( tV > maxV )
313         maxV = tV;
314 
315     tV = fabs( wnorm.y );
316 
317     if( tV > maxV )
318         maxV = tV;
319 
320     tV = fabs( wnorm.z );
321 
322     if( tV > maxV )
323         maxV = tV;
324 
325     return maxV;
326 }
327 
328 
CalcVertexNormal(int aIndex,std::list<FACET * > & aFacetList,float aCreaseLimit)329 void FACET::CalcVertexNormal( int aIndex, std::list< FACET* > &aFacetList, float aCreaseLimit )
330 {
331     if( vertices.size() < 3 )
332         return;
333 
334     if( vnweight.size() != vertices.size() )
335         return;
336 
337     if( norms.size() != vertices.size() )
338         norms.resize( vertices.size() );
339 
340     std::vector< int >::iterator sI = indices.begin();
341     std::vector< int >::iterator eI = indices.end();
342     int idx = 0;
343 
344     WRLVEC3F fp[2]; // vectors to calculate facet angle
345     fp[0].x = 0.0;
346     fp[0].y = 0.0;
347     fp[0].z = 0.0;
348 
349     while( sI != eI )
350     {
351         if( *sI == aIndex )
352         {
353             // first set the default (weighted) normal value
354             norms[idx] = vnweight[idx];
355 
356             // iterate over adjacent facets
357             std::list< FACET* >::iterator sF = aFacetList.begin();
358             std::list< FACET* >::iterator eF = aFacetList.end();
359 
360             while( sF != eF )
361             {
362                 if( this == *sF )
363                 {
364                     ++sF;
365                     continue;
366                 }
367 
368                 // check the crease angle limit
369                 (*sF)->GetFaceNormal( fp[1] );
370 
371                 float thrs = VCalcCosAngle( fp[0], face_normal, fp[1] );
372 
373                 if( aCreaseLimit <= thrs && (*sF)->GetWeightedNormal( aIndex, fp[1] ) )
374                 {
375                     norms[idx].x += fp[1].x;
376                     norms[idx].y += fp[1].y;
377                     norms[idx].z += fp[1].z;
378                 }
379 
380                 ++sF;
381             }
382 
383             // normalize the vector
384             float dn = sqrtf( norms[idx].x * norms[idx].x
385                             + norms[idx].y * norms[idx].y
386                             + norms[idx].z * norms[idx].z );
387 
388             if( dn > LOWER_LIMIT )
389             {
390                 norms[idx].x /= dn;
391                 norms[idx].y /= dn;
392                 norms[idx].z /= dn;
393             }
394 
395             // if the normals is an invalid normal this test will pass
396             if( fabs( norms[idx].x ) < 0.5
397                 && fabs( norms[idx].y ) < 0.5
398                 && fabs( norms[idx].z ) < 0.5 )
399             {
400                 norms[idx] = face_normal;
401             }
402 
403             return;
404         }
405 
406         ++idx;
407         ++sI;
408     }
409 }
410 
411 
GetWeightedNormal(int aIndex,WRLVEC3F & aNorm)412 bool FACET::GetWeightedNormal( int aIndex, WRLVEC3F& aNorm )
413 {
414     // the default weighted normal shall have no effect even if accidentally included
415     aNorm.x = 0.0;
416     aNorm.y = 0.0;
417     aNorm.z = 0.0;
418 
419     if( vertices.size() < 3 )
420         return false;
421 
422     if( vnweight.size() != vertices.size() )
423         return false;
424 
425     std::vector< int >::iterator sI = indices.begin();
426     std::vector< int >::iterator eI = indices.end();
427     int idx = 0;
428 
429     while( sI != eI )
430     {
431         if( *sI == aIndex )
432         {
433             aNorm = vnweight[idx];
434             return true;
435         }
436 
437         ++idx;
438         ++sI;
439     }
440 
441     return false;
442 }
443 
444 
GetFaceNormal(WRLVEC3F & aNorm)445 bool FACET::GetFaceNormal( WRLVEC3F& aNorm )
446 {
447     aNorm.x = 0.0;
448     aNorm.y = 0.0;
449     aNorm.z = 0.0;
450 
451     if( vertices.size() < 3 )
452         return false;
453 
454     if( vnweight.size() != vertices.size() )
455         return false;
456 
457     aNorm = face_normal;
458     return true;
459 }
460 
461 
GetData(std::vector<WRLVEC3F> & aVertexList,std::vector<WRLVEC3F> & aNormalsList,std::vector<SGCOLOR> & aColorsList,WRL1_ORDER aVertexOrder)462 bool FACET::GetData( std::vector< WRLVEC3F >& aVertexList, std::vector< WRLVEC3F >& aNormalsList,
463                      std::vector< SGCOLOR >& aColorsList, WRL1_ORDER aVertexOrder )
464 {
465     // if no normals are calculated we simply return
466     if( norms.empty() )
467         return false;
468 
469     // the output must always be triangle sets in order to conform to the
470     // requirements of the SG* classes
471     int idx[3];
472 
473     idx[0] = 0;
474     idx[1] = 1;
475     idx[2] = 2;
476     WRLVEC3F tnorm;
477 
478     if( aVertexOrder != WRL1_ORDER::ORD_CLOCKWISE )
479     {
480         aVertexList.push_back( vertices[idx[0]] );
481         aVertexList.push_back( vertices[idx[1]] );
482         aVertexList.push_back( vertices[idx[2]] );
483 
484         aNormalsList.push_back( norms[idx[0]] );
485         aNormalsList.push_back( norms[idx[1]] );
486         aNormalsList.push_back( norms[idx[2]] );
487     }
488 
489     if( aVertexOrder != WRL1_ORDER::ORD_CCW )
490     {
491         aVertexList.push_back( vertices[idx[0]] );
492         aVertexList.push_back( vertices[idx[2]] );
493         aVertexList.push_back( vertices[idx[1]] );
494 
495         tnorm = norms[idx[0]];
496         tnorm.x = -tnorm.x;
497         tnorm.y = -tnorm.y;
498         tnorm.z = -tnorm.z;
499         aNormalsList.push_back( tnorm );
500 
501         tnorm = norms[idx[2]];
502         tnorm.x = -tnorm.x;
503         tnorm.y = -tnorm.y;
504         tnorm.z = -tnorm.z;
505         aNormalsList.push_back( tnorm );
506 
507         tnorm = norms[idx[1]];
508         tnorm.x = -tnorm.x;
509         tnorm.y = -tnorm.y;
510         tnorm.z = -tnorm.z;
511         aNormalsList.push_back( tnorm );
512     }
513 
514     bool hasColor = false;
515     bool perVC = false; // per-vertex colors?
516 
517     if( !colors.empty() )
518     {
519         hasColor = true;
520 
521         if( colors.size() >= vertices.size() )
522             perVC = true;
523 
524         if( perVC )
525         {
526             if( aVertexOrder != WRL1_ORDER::ORD_CLOCKWISE )
527             {
528                 aColorsList.push_back( colors[idx[0]] );
529                 aColorsList.push_back( colors[idx[1]] );
530                 aColorsList.push_back( colors[idx[2]] );
531             }
532 
533             if( aVertexOrder != WRL1_ORDER::ORD_CCW )
534             {
535                 aColorsList.push_back( colors[idx[0]] );
536                 aColorsList.push_back( colors[idx[2]] );
537                 aColorsList.push_back( colors[idx[1]] );
538             }
539         }
540         else
541         {
542             if( aVertexOrder != WRL1_ORDER::ORD_CLOCKWISE )
543             {
544                 aColorsList.push_back( colors[0] );
545                 aColorsList.push_back( colors[0] );
546                 aColorsList.push_back( colors[0] );
547             }
548 
549             if( aVertexOrder != WRL1_ORDER::ORD_CCW )
550             {
551                 aColorsList.push_back( colors[0] );
552                 aColorsList.push_back( colors[0] );
553                 aColorsList.push_back( colors[0] );
554             }
555         }
556     }
557 
558     int lim = (int) vertices.size() - 1;
559 
560     while( idx[2] <  lim )
561     {
562         idx[1] = idx[2];
563         ++idx[2];
564 
565         if( aVertexOrder != WRL1_ORDER::ORD_CLOCKWISE )
566         {
567             aVertexList.push_back( vertices[idx[0]] );
568             aVertexList.push_back( vertices[idx[1]] );
569             aVertexList.push_back( vertices[idx[2]] );
570 
571             aNormalsList.push_back( norms[idx[0]] );
572             aNormalsList.push_back( norms[idx[1]] );
573             aNormalsList.push_back( norms[idx[2]] );
574         }
575 
576         if( aVertexOrder != WRL1_ORDER::ORD_CCW )
577         {
578             aVertexList.push_back( vertices[idx[0]] );
579             aVertexList.push_back( vertices[idx[2]] );
580             aVertexList.push_back( vertices[idx[1]] );
581 
582             tnorm = norms[idx[0]];
583             tnorm.x = -tnorm.x;
584             tnorm.y = -tnorm.y;
585             tnorm.z = -tnorm.z;
586             aNormalsList.push_back( tnorm );
587 
588             tnorm = norms[idx[2]];
589             tnorm.x = -tnorm.x;
590             tnorm.y = -tnorm.y;
591             tnorm.z = -tnorm.z;
592             aNormalsList.push_back( tnorm );
593 
594             tnorm = norms[idx[1]];
595             tnorm.x = -tnorm.x;
596             tnorm.y = -tnorm.y;
597             tnorm.z = -tnorm.z;
598             aNormalsList.push_back( tnorm );
599         }
600 
601         if( hasColor )
602         {
603             if( perVC )
604             {
605                 if( aVertexOrder != WRL1_ORDER::ORD_CLOCKWISE )
606                 {
607                     aColorsList.push_back( colors[idx[0]] );
608                     aColorsList.push_back( colors[idx[1]] );
609                     aColorsList.push_back( colors[idx[2]] );
610                 }
611 
612                 if( aVertexOrder != WRL1_ORDER::ORD_CCW )
613                 {
614                     aColorsList.push_back( colors[idx[0]] );
615                     aColorsList.push_back( colors[idx[2]] );
616                     aColorsList.push_back( colors[idx[1]] );
617                 }
618             }
619             else
620             {
621                 if( aVertexOrder != WRL1_ORDER::ORD_CLOCKWISE )
622                 {
623                     aColorsList.push_back( colors[0] );
624                     aColorsList.push_back( colors[0] );
625                     aColorsList.push_back( colors[0] );
626                 }
627 
628                 if( aVertexOrder != WRL1_ORDER::ORD_CCW )
629                 {
630                     aColorsList.push_back( colors[0] );
631                     aColorsList.push_back( colors[0] );
632                     aColorsList.push_back( colors[0] );
633                 }
634             }
635         }
636     }
637 
638     return true;
639 }
640 
641 
CollectVertices(std::vector<std::list<FACET * >> & aFacetList)642 void FACET::CollectVertices( std::vector< std::list< FACET* > >& aFacetList )
643 {
644     // check if this facet may contribute anything at all
645     if( vertices.size() < 3 )
646         return;
647 
648     // note: in principle this should never be invoked
649     if( (maxIdx + 1) >= (int)aFacetList.size() )
650         aFacetList.resize( static_cast<std::size_t>( maxIdx ) + 1 );
651 
652     std::vector< int >::iterator sI = indices.begin();
653     std::vector< int >::iterator eI = indices.end();
654 
655     while( sI != eI )
656     {
657         aFacetList[*sI].push_back( this );
658         ++sI;
659     }
660 }
661 
662 
Renormalize(float aMaxValue)663 void FACET::Renormalize( float aMaxValue )
664 {
665     if( vnweight.empty() || aMaxValue < LOWER_LIMIT )
666         return;
667 
668     size_t vs = vnweight.size();
669 
670     for( size_t i = 0; i < vs; ++i )
671     {
672         vnweight[i].x /= aMaxValue;
673         vnweight[i].y /= aMaxValue;
674         vnweight[i].z /= aMaxValue;
675     }
676 }
677 
678 
~SHAPE()679 SHAPE::~SHAPE()
680 {
681     std::list< FACET* >::iterator sF = facets.begin();
682     std::list< FACET* >::iterator eF = facets.end();
683 
684     while( sF != eF )
685     {
686         delete *sF;
687         ++sF;
688     }
689 
690     facets.clear();
691     return;
692 }
693 
694 
NewFacet()695 FACET* SHAPE::NewFacet()
696 {
697     FACET* fp = new FACET;
698     facets.push_back( fp );
699     return fp;
700 }
701 
702 
CalcShape(SGNODE * aParent,SGNODE * aColor,WRL1_ORDER aVertexOrder,float aCreaseLimit,bool isVRML2)703 SGNODE* SHAPE::CalcShape( SGNODE* aParent, SGNODE* aColor, WRL1_ORDER aVertexOrder,
704                           float aCreaseLimit, bool isVRML2 )
705 {
706     if( facets.empty() || !facets.front()->HasMinPoints() )
707         return nullptr;
708 
709     std::vector< std::list< FACET* > > flist;
710 
711     // determine the max. index and size flist as appropriate
712     std::list< FACET* >::iterator sF = facets.begin();
713     std::list< FACET* >::iterator eF = facets.end();
714 
715     int maxIdx = 0;
716     int tmi;
717     float maxV = 0.0;
718     float tV = 0.0;
719 
720     while( sF != eF )
721     {
722         tV = ( *sF )->CalcFaceNormal();
723         tmi = ( *sF )->GetMaxIndex();
724 
725         if( tmi > maxIdx )
726             maxIdx = tmi;
727 
728         if( tV > maxV )
729             maxV = tV;
730 
731         ++sF;
732     }
733 
734     ++maxIdx;
735 
736     if( maxIdx < 3 )
737         return nullptr;
738 
739     flist.resize( maxIdx );
740 
741     // create the lists of facets common to indices
742     sF = facets.begin();
743 
744     while( sF != eF )
745     {
746         ( *sF )->Renormalize( tV );
747         ( *sF )->CollectVertices( flist );
748         ++sF;
749     }
750 
751     // calculate the normals
752     size_t vs = flist.size();
753 
754     for( size_t i = 0; i < vs; ++i )
755     {
756         sF = flist[i].begin();
757         eF = flist[i].end();
758 
759         while( sF != eF )
760         {
761             ( *sF )->CalcVertexNormal( static_cast<int>( i ), flist[i], aCreaseLimit );
762             ++sF;
763         }
764     }
765 
766     std::vector< WRLVEC3F > vertices;
767     std::vector< WRLVEC3F > normals;
768     std::vector< SGCOLOR >  colors;
769 
770     // push the facet data to the final output list
771     sF = facets.begin();
772     eF = facets.end();
773 
774     while( sF != eF )
775     {
776         ( *sF )->GetData( vertices, normals, colors, aVertexOrder );
777         ++sF;
778     }
779 
780     flist.clear();
781 
782     if( vertices.size() < 3 )
783         return nullptr;
784 
785     IFSG_SHAPE shapeNode( false );
786 
787     if( !isVRML2 )
788     {
789         shapeNode.NewNode( aParent );
790 
791         if( aColor )
792         {
793             if( nullptr == S3D::GetSGNodeParent( aColor ) )
794                 shapeNode.AddChildNode( aColor );
795             else
796                 shapeNode.AddRefNode( aColor );
797         }
798     }
799 
800     std::vector< SGPOINT >  lCPts;  // vertex points in SGPOINT (double) format
801     std::vector< SGVECTOR > lCNorm; // per-vertex normals
802     vs = vertices.size();
803 
804     for( size_t i = 0; i < vs; ++i )
805     {
806         SGPOINT pt;
807         pt.x = vertices[i].x;
808         pt.y = vertices[i].y;
809         pt.z = vertices[i].z;
810         lCPts.push_back( pt );
811         lCNorm.emplace_back( normals[i].x, normals[i].y, normals[i].z );
812     }
813 
814     vertices.clear();
815     normals.clear();
816 
817     IFSG_FACESET fsNode( false );
818 
819     if( !isVRML2 )
820         fsNode.NewNode( shapeNode );
821     else
822         fsNode.NewNode( aParent );
823 
824     IFSG_COORDS cpNode( fsNode );
825     cpNode.SetCoordsList( lCPts.size(), &lCPts[0] );
826     IFSG_COORDINDEX ciNode( fsNode );
827 
828     for( int i = 0; i < (int)lCPts.size(); ++i )
829         ciNode.AddIndex( i );
830 
831     IFSG_NORMALS nmNode( fsNode );
832     nmNode.SetNormalList( lCNorm.size(), &lCNorm[0] );
833 
834     if( !colors.empty() )
835     {
836         IFSG_COLORS nmColor( fsNode );
837         nmColor.SetColorList( colors.size(), &colors[0] );
838         colors.clear();
839     }
840 
841     if( !isVRML2 )
842         return shapeNode.GetRawPtr();
843 
844     return fsNode.GetRawPtr();
845 }
846