0024856: CLang warnings -Wunused-private-field
[occt.git] / src / VrmlData / VrmlData_ShapeConvert.cxx
CommitLineData
b311480e 1// Created on: 2007-08-04
2// Created by: Alexander GRIGORIEV
973c2be1 3// Copyright (c) 2007-2014 OPEN CASCADE SAS
b311480e 4//
973c2be1 5// This file is part of Open CASCADE Technology software library.
b311480e 6//
d5f74e42 7// This library is free software; you can redistribute it and/or modify it under
8// the terms of the GNU Lesser General Public License version 2.1 as published
973c2be1 9// by the Free Software Foundation, with special exception defined in the file
10// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
11// distribution for complete text of the license and disclaimer of any warranty.
b311480e 12//
973c2be1 13// Alternatively, this file may be used under the terms of Open CASCADE
14// commercial license or contractual agreement.
7fd59977 15
16#include <VrmlData_ShapeConvert.hxx>
17#include <VrmlData_Scene.hxx>
18#include <VrmlData_Group.hxx>
19#include <VrmlData_Coordinate.hxx>
20#include <VrmlData_IndexedFaceSet.hxx>
21#include <VrmlData_IndexedLineSet.hxx>
22#include <VrmlData_ShapeNode.hxx>
23#include <BRepMesh_IncrementalMesh.hxx>
24#include <BRep_Builder.hxx>
25#include <BRep_Tool.hxx>
26#include <Geom_Surface.hxx>
27#include <NCollection_DataMap.hxx>
28#include <Poly_Triangulation.hxx>
29#include <Poly_Connect.hxx>
30#include <Poly_PolygonOnTriangulation.hxx>
31#include <Poly_Polygon3D.hxx>
32#include <Precision.hxx>
33#include <TColgp_Array1OfPnt2d.hxx>
34#include <TopExp_Explorer.hxx>
35#include <TopoDS.hxx>
36#include <TopoDS_Edge.hxx>
37#include <TopoDS_Face.hxx>
38#include <TopoDS_Shape.hxx>
39#include <TopoDS_Wire.hxx>
40#include <GCPnts_TangentialDeflection.hxx>
41#include <BRepAdaptor_Curve.hxx>
42#include <TColStd_Array1OfReal.hxx>
43#include <TColStd_HArray1OfReal.hxx>
44#include <TShort_Array1OfShortReal.hxx>
45#include <GeomLib.hxx>
46#include <TShort_HArray1OfShortReal.hxx>
47
48//=======================================================================
49//function : IsEqual
50//purpose : for NCollection_DataMap interface
51//=======================================================================
52
53inline Standard_Boolean IsEqual (const TopoDS_Shape& one,
54 const TopoDS_Shape& two)
55{
56 return one == two;
57}
58
59//=======================================================================
60//function : AddShape
61//purpose :
62//=======================================================================
63
64void VrmlData_ShapeConvert::AddShape (const TopoDS_Shape& theShape,
65 const char * theName)
66{
67 ShapeData aData;/* = { - compilation problem on SUN
68 TCollection_AsciiString(),
69 theShape,
70 NULL
71 };*/
72 aData.Shape = theShape;
73 aData.Node = NULL;
74
75 if (theName) {
76 char buf[2048], * optr = &buf[0];
77 char * eptr = &buf[sizeof(buf)-1];
78 for (const char * ptr = theName;; ptr++) {
8263fcd3 79 char sym = *ptr;
7fd59977 80 if (sym == '\0' || sym == '\n' || sym == '\r') {
81 * optr = '\0';
82 break;
83 }
84 if (sym == '\"' || sym == '\\')
85 * optr = '/';
86 else if (sym == '.')
87 * optr = '_';
88 else
89 * optr = sym;
90 if (++optr >= eptr) {
91 *optr = '\0';
92 break;
93 }
94 }
95 aData.Name = buf;
96 }
97 myShapes.Append (aData);
98}
99
100//=======================================================================
101//function : Convert
102//purpose :
103//=======================================================================
104
105void VrmlData_ShapeConvert::Convert (const Standard_Boolean theExtractFaces,
106 const Standard_Boolean theExtractEdges,
107 const Standard_Real theDeflection,
108 const Standard_Real theDeflAngle)
109{
110 const Standard_Real aDeflection =
111 theDeflection < 0.0001 ? 0.0001 : theDeflection;
112
113 Standard_Boolean Extract[2] = {theExtractFaces, theExtractEdges};
114 TopAbs_ShapeEnum ShapeType[2] = {TopAbs_FACE, TopAbs_EDGE};
115 Standard_Integer i;
116
117 const Handle(NCollection_IncAllocator) anAlloc = new NCollection_IncAllocator;
118
119 // Relocation map for converted shapes. We should distinguish both TShape
120 // and Orientation in this map.
121 NCollection_DataMap <TopoDS_Shape,Handle(VrmlData_Geometry)>
122 aRelMap (100, anAlloc);
123
124
125 NCollection_List<ShapeData>::Iterator anIter (myShapes);
126 for (; anIter.More(); anIter.Next()) {
127
128 ShapeData& aData = anIter.ChangeValue();
129 Handle(VrmlData_Group) aGroup =
130 new VrmlData_Group (myScene, aData.Name.ToCString());
131 myScene.AddNode (aGroup);
132
133 for(i = 0; i < 2; ++i) {
134
135 if(!Extract[i]) continue;
136
137 TopExp_Explorer anExp (aData.Shape, ShapeType[i]);
138 for (; anExp.More(); anExp.Next()) {
139 const TopoDS_Shape& aShape = anExp.Current();
140 TopLoc_Location aLoc;
141 Handle(VrmlData_Geometry) aTShapeNode;
142 const Standard_Boolean isReverse=(aShape.Orientation()==TopAbs_REVERSED);
143
144 TopoDS_Shape aTestedShape;
145 aTestedShape.TShape (aShape.TShape());
146 aTestedShape.Orientation (isReverse ? TopAbs_REVERSED : TopAbs_FORWARD);
147 Standard_Boolean isTessellate (Standard_False);
148 switch (ShapeType[i]) {
149 case TopAbs_FACE:
150 {
151 const TopoDS_Face& aFace = TopoDS::Face (aShape);
152 if (aFace.IsNull() == Standard_False) {
153 Handle(Poly_Triangulation) aTri =
154 BRep_Tool::Triangulation (aFace, aLoc);
155
156 if (aRelMap.IsBound (aTestedShape)) {
157 aTShapeNode = aRelMap(aTestedShape);
158 break;
159 }
160
161 if (aTri.IsNull())
162 isTessellate = Standard_True;
163 // Check the existing deflection
164 else if (aTri->Deflection() > aDeflection+ Precision::Confusion())
165 isTessellate = Standard_True;
166 if (isTessellate) {
167 // Triangulate the face by the standard OCC mesher
168 BRepMesh_IncrementalMesh IM (aFace, aDeflection, Standard_False, theDeflAngle);
169 aTri = BRep_Tool::Triangulation (aFace, aLoc);
170 }
171 if (aTri.IsNull() == Standard_False) {
172 TopoDS_Shape aTestedShapeRev = aTestedShape;
173 aTestedShapeRev.Orientation (isReverse ?
174 TopAbs_FORWARD : TopAbs_REVERSED);
175 Handle(VrmlData_IndexedFaceSet) aFaceSetToReuse;
176 if (aRelMap.IsBound (aTestedShapeRev))
177 aFaceSetToReuse = Handle(VrmlData_IndexedFaceSet)::DownCast
178 (aRelMap(aTestedShapeRev));
179
180 Handle(VrmlData_Coordinate) aCoordToReuse;
181 if (aFaceSetToReuse.IsNull() == Standard_False)
182 aCoordToReuse = aFaceSetToReuse->Coordinates();
183
184 aTShapeNode = triToIndexedFaceSet (aTri, aFace, aCoordToReuse);
185 myScene.AddNode (aTShapeNode, Standard_False);
186 // Bind the converted face
187 aRelMap.Bind (aTestedShape, aTShapeNode);
188 }
189 }
190 }
191 break;
192 case TopAbs_WIRE:
193 {
194 const TopoDS_Wire& aWire = TopoDS::Wire (aShape);
195 if (aWire.IsNull() == Standard_False) {
196 }
197 }
198 break;
199 case TopAbs_EDGE:
200 {
201 const TopoDS_Edge& aEdge = TopoDS::Edge (aShape);
202 if (aEdge.IsNull() == Standard_False) {
203 Handle(Poly_Polygon3D) aPol = BRep_Tool::Polygon3D (aEdge, aLoc);
204
205 if (aRelMap.IsBound (aTestedShape)) {
206 aTShapeNode = aRelMap(aTestedShape);
207 break;
208 }
209 // Check the presence of reversly oriented Edge. It can also be used
210 // because we do not distinguish the orientation for edges.
211 aTestedShape.Orientation (isReverse ?
212 TopAbs_FORWARD : TopAbs_REVERSED);
213 if (aRelMap.IsBound (aTestedShape)) {
214 aTShapeNode = aRelMap(aTestedShape);
215 break;
216 }
217
218 if (aPol.IsNull())
219 isTessellate = Standard_True;
220 // Check the existing deflection
221 else if (aPol->Deflection() > aDeflection+ Precision::Confusion()
222 && BRep_Tool::IsGeometric(aEdge))
223 isTessellate = Standard_True;
224
225 if (isTessellate && BRep_Tool::IsGeometric(aEdge)) {
226 //try to find PolygonOnTriangulation
227 Handle(Poly_PolygonOnTriangulation) aPT;
228 Handle(Poly_Triangulation) aT;
229 TopLoc_Location aL;
230
231 Standard_Boolean found = Standard_False;
302f96fb 232 for(i = 1; ; i++) {
7fd59977 233
234 BRep_Tool::PolygonOnTriangulation(aEdge, aPT, aT, aL, i);
235
236 if(aPT.IsNull() || aT.IsNull()) break;
237
238 if(aPT->Deflection() <= aDeflection + Precision::Confusion() &&
239 aPT->HasParameters()) {
240 found = Standard_True;
241 break;
242 }
243
244 }
245
246 if(found) {
247
248 BRepAdaptor_Curve aCurve(aEdge);
249 Handle(TColStd_HArray1OfReal) aPrs = aPT->Parameters();
250 Standard_Integer nbNodes = aPT->NbNodes();
251 TColgp_Array1OfPnt arrNodes(1, nbNodes);
252 TColStd_Array1OfReal arrUVNodes(1, nbNodes);
253
d497b314
P
254 for(Standard_Integer j = 1; j <= nbNodes; j++) {
255 arrUVNodes(j) = aPrs->Value(aPrs->Lower() + j - 1);
256 arrNodes(j) = aCurve.Value(arrUVNodes(j));
7fd59977 257 }
258 aPol = new Poly_Polygon3D(arrNodes, arrUVNodes);
259 aPol->Deflection (aPT->Deflection());
260 }
261 else{
262
263 BRepAdaptor_Curve aCurve(aEdge);
264 const Standard_Real aFirst = aCurve.FirstParameter();
265 const Standard_Real aLast = aCurve.LastParameter();
266
267 GCPnts_TangentialDeflection TD (aCurve, aFirst, aLast,
268 theDeflAngle, aDeflection, 2);
269 const Standard_Integer nbNodes = TD.NbPoints();
270
271 TColgp_Array1OfPnt arrNodes(1, nbNodes);
272 TColStd_Array1OfReal arrUVNodes(1, nbNodes);
d497b314
P
273 for (Standard_Integer j = 1; j <= nbNodes; j++) {
274 arrNodes(j) = TD.Value(j);
275 arrUVNodes(j) = TD.Parameter(j);
7fd59977 276 }
277 aPol = new Poly_Polygon3D(arrNodes, arrUVNodes);
278 aPol->Deflection (aDeflection);
279 }
280
281 BRep_Builder aBld;
282 aBld.UpdateEdge (aEdge, aPol);
283 }
284 aTShapeNode = polToIndexedLineSet (aPol);
285 myScene.AddNode (aTShapeNode, Standard_False);
286 // Bind the converted face
287 aRelMap.Bind (aTestedShape, aTShapeNode);
288 }
289 }
290 break;
566f8441 291 default:
292 break;
7fd59977 293 }
294
295 if (aTShapeNode.IsNull() == Standard_False) {
296 const Handle(VrmlData_ShapeNode) aShapeNode =
297 new VrmlData_ShapeNode (myScene, 0L);
298 aShapeNode->SetAppearance (ShapeType[i] == TopAbs_FACE ?
299 defaultMaterialFace():defaultMaterialEdge());
300 myScene.AddNode (aShapeNode, Standard_False);
301 aShapeNode->SetGeometry (aTShapeNode);
302 if (aLoc.IsIdentity())
303 // Store the shape node directly into the main Group.
304 aGroup->AddNode (aShapeNode);
305 else {
306 // Create a Transform grouping node
307 Handle(VrmlData_Group) aTrans = new VrmlData_Group (myScene, 0L,
308 Standard_True);
309 gp_Trsf aTrsf (aLoc);
310 if (fabs(myScale - 1.) > Precision::Confusion()) {
311 const gp_XYZ aTransl = aTrsf.TranslationPart() * myScale;
312 aTrsf.SetTranslationPart (aTransl);
313 }
314 aTrans->SetTransform (aTrsf);
315 myScene.AddNode (aTrans, Standard_False);
316 aGroup->AddNode (aTrans);
317
318 // Store the shape node under the transform.
319 aTrans->AddNode (aShapeNode);
320 }
321 }
322 }
323 }
324 }
325 myShapes.Clear();
326}
327
328//=======================================================================
329//function : triToIndexedFaceSet
330//purpose :
331//=======================================================================
332
333Handle_VrmlData_Geometry VrmlData_ShapeConvert::triToIndexedFaceSet
334 (const Handle_Poly_Triangulation& theTri,
335 const TopoDS_Face& theFace,
336 const Handle_VrmlData_Coordinate& theCoord)
337{
338 Standard_Integer i;
339 const Standard_Integer nNodes (theTri->NbNodes());
340 const Standard_Integer nTriangles (theTri->NbTriangles());
341 const TColgp_Array1OfPnt& arrPolyNodes = theTri->Nodes();
342 const Poly_Array1OfTriangle& arrTriangles = theTri->Triangles();
343
344 const Handle(VrmlData_IndexedFaceSet) aFaceSet =
345 new VrmlData_IndexedFaceSet (myScene,
346 0L, // no name
347 Standard_True, // IsCCW
348 Standard_False, // IsSolid
349 Standard_False); // IsConvex
350 const Handle(NCollection_IncAllocator)& anAlloc = myScene.Allocator();
351 const Standard_Boolean isReverse = (theFace.Orientation() == TopAbs_REVERSED);
352
353 // Create the array of triangles
354 const Standard_Integer ** arrPolygons = static_cast<const Standard_Integer **>
355 (anAlloc->Allocate (nTriangles * sizeof(const Standard_Integer *)));
356 aFaceSet->SetPolygons (nTriangles, arrPolygons);
357
358 // Store the triangles
359 for (i = 0; i < nTriangles; i++) {
360 Standard_Integer * aPolygon = static_cast<Standard_Integer *>
361 (anAlloc->Allocate (4*sizeof(Standard_Integer)));
362 aPolygon[0] = 3;
363 arrTriangles(i+1).Get (aPolygon[1],aPolygon[2],aPolygon[3]);
364 aPolygon[1]--;
365 if (isReverse) {
366 const Standard_Integer aTmp = aPolygon[2]-1;
367 aPolygon[2] = aPolygon[3]-1;
368 aPolygon[3] = aTmp;
369 } else {
370 aPolygon[2]--;
371 aPolygon[3]--;
372 }
373 arrPolygons[i] = aPolygon;
374 }
375
376 // Create the Coordinates node
377 if (theCoord.IsNull() == Standard_False)
378 aFaceSet->SetCoordinates (theCoord);
379 else {
380 gp_XYZ * arrNodes = static_cast <gp_XYZ *>
381 (anAlloc->Allocate (nNodes * sizeof(gp_XYZ)));
382 for (i = 0; i < nNodes; i++)
383 arrNodes[i] = arrPolyNodes(i+1).XYZ() * myScale;
384
385 const Handle(VrmlData_Coordinate) aCoordNode =
386 new VrmlData_Coordinate (myScene, 0L, nNodes, arrNodes);
387 myScene.AddNode (aCoordNode, Standard_False);
388 aFaceSet->SetCoordinates (aCoordNode);
389 }
390
391 // Create the Normals node if theTri has normals
392 if(theTri->HasNormals()) {
393 gp_XYZ * arrVec = static_cast <gp_XYZ *>
394 (anAlloc->Allocate (nNodes * sizeof(gp_XYZ)));
395 const TShort_Array1OfShortReal& Norm = theTri->Normals();
396 Standard_Integer j;
397 for (i = 0, j = 1; i < nNodes; i++, j += 3) {
398
399 gp_XYZ aNormal(Norm(j), Norm(j+1), Norm(j+2));
400 arrVec[i] = aNormal;
401
402 }
403 const Handle(VrmlData_Normal) aNormalNode =
404 new VrmlData_Normal (myScene, 0L, nNodes, arrVec);
405 myScene.AddNode (aNormalNode, Standard_False);
406 aFaceSet->SetNormals (aNormalNode);
407 return aFaceSet;
408 }
409
410 Poly_Connect PC(theTri);
411 // Create the Normals node (if UV- values are available)
412 TopLoc_Location aLoc;
08cd2f6b 413 const Standard_Real aConf2 = Precision::SquareConfusion();
7fd59977 414 const Handle(Geom_Surface) aSurface = BRep_Tool::Surface (theFace, aLoc);
415 if (theTri->HasUVNodes() && aSurface.IsNull() == Standard_False) {
416 if (aSurface->IsCNu(1) && aSurface->IsCNv(1))
417 {
418 Standard_Integer nbNormVal = nNodes * 3;
419 Handle(TShort_HArray1OfShortReal) Normals =
420 new TShort_HArray1OfShortReal(1, nbNormVal);
421
422 const TColgp_Array1OfPnt2d& arrUV = theTri->UVNodes();
423 gp_XYZ * arrVec = static_cast <gp_XYZ *>
424 (anAlloc->Allocate (nNodes * sizeof(gp_XYZ)));
425
426 // Compute the normal vectors
427 Standard_Real Tol = Sqrt(aConf2);
428 for (i = 0; i < nNodes; i++) {
429 const gp_Pnt2d& aUV = arrUV(i+1);
430
431 gp_Dir aNormal;
432
433 if (GeomLib::NormEstim(aSurface, aUV, Tol, aNormal) > 1) {
434 //Try to estimate as middle normal of adjacent triangles
435 Standard_Integer n[3];
436
437 gp_XYZ eqPlan(0., 0., 0.);
438 for (PC.Initialize(i+1); PC.More(); PC.Next()) {
439 arrTriangles(PC.Value()).Get(n[0], n[1], n[2]);
440 gp_XYZ v1(arrPolyNodes(n[1]).Coord()-arrPolyNodes(n[0]).Coord());
441 gp_XYZ v2(arrPolyNodes(n[2]).Coord()-arrPolyNodes(n[1]).Coord());
442 gp_XYZ vv = v1^v2;
443
444 Standard_Real mod = vv.Modulus();
445 if (mod < Tol)
446 continue;
447
448 eqPlan += vv/mod;
449 }
450
451 if (eqPlan.SquareModulus() > gp::Resolution())
452 aNormal = gp_Dir(eqPlan);
453 }
454 if (isReverse)
455 aNormal.Reverse();
456
457 if (aNormal.X()*aNormal.X() < aConf2)
458 aNormal.SetX(0.);
459 if (aNormal.Y()*aNormal.Y() < aConf2)
460 aNormal.SetY(0.);
461 if (aNormal.Z()*aNormal.Z() < aConf2)
462 aNormal.SetZ(0.);
463 arrVec[i] = aNormal.XYZ();
464
465 Standard_Integer j = i * 3;
7f4c4756 466 Normals->SetValue(j + 1, (Standard_ShortReal)aNormal.X());
467 Normals->SetValue(j + 2, (Standard_ShortReal)aNormal.Y());
468 Normals->SetValue(j + 3, (Standard_ShortReal)aNormal.Z());
7fd59977 469
470 }
471
472 theTri->SetNormals(Normals);
473
474 const Handle(VrmlData_Normal) aNormalNode =
475 new VrmlData_Normal (myScene, 0L, nNodes, arrVec);
476 myScene.AddNode (aNormalNode, Standard_False);
477 aFaceSet->SetNormals (aNormalNode);
478 }
479 }
480
481 return aFaceSet;
482}
483
484//=======================================================================
485//function : polToIndexedLineSet
486//purpose : single polygon3D => IndexedLineSet
487//=======================================================================
488
489Handle_VrmlData_Geometry VrmlData_ShapeConvert::polToIndexedLineSet
490 (const Handle_Poly_Polygon3D& thePol)
491{
492 Standard_Integer i;
493 const Standard_Integer nNodes (thePol->NbNodes());
494 const TColgp_Array1OfPnt& arrPolyNodes = thePol->Nodes();
495 const Handle(NCollection_IncAllocator)& anAlloc = myScene.Allocator();
496
497 const Handle(VrmlData_IndexedLineSet) aLineSet =
498 new VrmlData_IndexedLineSet (myScene, 0L);
499
500 // Create the array of polygons (1 member)
501 const Standard_Integer ** arrPolygons = static_cast<const Standard_Integer **>
502 (anAlloc->Allocate (sizeof(const Standard_Integer *)));
503 aLineSet->SetPolygons (1, arrPolygons);
504
505 // Store the polygon
506 Standard_Integer * aPolygon = static_cast<Standard_Integer *>
507 (anAlloc->Allocate ((nNodes+1)*sizeof(Standard_Integer)));
508 aPolygon[0] = nNodes;
509 for (i = 1; i <= nNodes; i++)
510 aPolygon[i] = i-1;
511 arrPolygons[0] = aPolygon;
512
513 // Create the Coordinates node
514 gp_XYZ * arrNodes = static_cast <gp_XYZ *>
515 (anAlloc->Allocate (nNodes * sizeof(gp_XYZ)));
516 for (i = 0; i < nNodes; i++)
517 arrNodes[i] = arrPolyNodes(i+1).XYZ() * myScale;
518
519 const Handle(VrmlData_Coordinate) aCoordNode =
520 new VrmlData_Coordinate (myScene, 0L, nNodes, arrNodes);
521 myScene.AddNode (aCoordNode, Standard_False);
522 aLineSet->SetCoordinates (aCoordNode);
523
524 return aLineSet;
525}
526
527//=======================================================================
528//function : defaultMaterialFace
529//purpose :
530//=======================================================================
531
532Handle(VrmlData_Appearance) VrmlData_ShapeConvert::defaultMaterialFace () const
533{
534 static char aNodeName[] = "__defaultMaterialFace";
535 Handle(VrmlData_Appearance) anAppearance =
536 Handle(VrmlData_Appearance)::DownCast(myScene.FindNode(aNodeName));
537 if (anAppearance.IsNull()) {
538 const Handle(VrmlData_Material) aMaterial =
539 new VrmlData_Material (myScene, 0L, 1.0, 0.022, 0.);
540 aMaterial->SetDiffuseColor (Quantity_Color(0.780392, 0.568627, 0.113725,
541 Quantity_TOC_RGB));
542 aMaterial->SetEmissiveColor(Quantity_Color(0.329412, 0.223529, 0.027451,
543 Quantity_TOC_RGB));
544 aMaterial->SetSpecularColor(Quantity_Color(0.992157, 0.941176, 0.807843,
545 Quantity_TOC_RGB));
546 myScene.AddNode (aMaterial, Standard_False);
547 anAppearance = new VrmlData_Appearance (myScene, aNodeName);
548 anAppearance->SetMaterial (aMaterial);
549 myScene.AddNode (anAppearance, Standard_False);
550 }
551 return anAppearance;
552}
553
554//=======================================================================
555//function : defaultMaterialEdge
556//purpose :
557//=======================================================================
558
559Handle(VrmlData_Appearance) VrmlData_ShapeConvert::defaultMaterialEdge () const
560{
561 static char aNodeName[] = "__defaultMaterialEdge";
562 Handle(VrmlData_Appearance) anAppearance =
563 Handle(VrmlData_Appearance)::DownCast(myScene.FindNode(aNodeName));
564 if (anAppearance.IsNull()) {
565 const Handle(VrmlData_Material) aMaterial =
566 new VrmlData_Material (myScene, 0L, 0.2, 0.2, 0.2);
567 aMaterial->SetDiffuseColor (Quantity_Color(0.2, 0.7, 0.2,
568 Quantity_TOC_RGB));
569 aMaterial->SetEmissiveColor(Quantity_Color(0.2, 0.7, 0.2,
570 Quantity_TOC_RGB));
571 aMaterial->SetSpecularColor(Quantity_Color(0.2, 0.7, 0.2,
572 Quantity_TOC_RGB));
573 myScene.AddNode (aMaterial, Standard_False);
574 anAppearance = new VrmlData_Appearance (myScene, aNodeName);
575 anAppearance->SetMaterial (aMaterial);
576 myScene.AddNode (anAppearance, Standard_False);
577 }
578 return anAppearance;
579}