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