b70ff5a80bd8714da714955a456bff272b268155
[occt.git] / src / VrmlData / VrmlData_ShapeConvert.cxx
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
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
59 inline 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
70 void 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++) {
85       char sym = *ptr;
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
111 void 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;
238                 for(i = 1; ; i++) {
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
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));
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);
279                   for (Standard_Integer j = 1; j <= nbNodes; j++) {
280                     arrNodes(j) = TD.Value(j);
281                     arrUVNodes(j) = TD.Parameter(j);
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;
297         }
298         
299         if (aTShapeNode.IsNull() == Standard_False) {
300           const Handle(VrmlData_ShapeNode) aShapeNode =
301             new VrmlData_ShapeNode (myScene, 0L);
302           aShapeNode->SetAppearance (ShapeType[i] == TopAbs_FACE ?
303                                      defaultMaterialFace():defaultMaterialEdge());
304           myScene.AddNode (aShapeNode, Standard_False);
305           aShapeNode->SetGeometry (aTShapeNode);
306           if (aLoc.IsIdentity())
307             // Store the shape node directly into the main Group.
308             aGroup->AddNode (aShapeNode);
309           else {
310             // Create a Transform grouping node
311             Handle(VrmlData_Group) aTrans = new VrmlData_Group (myScene, 0L,
312                                                                 Standard_True);
313             gp_Trsf aTrsf (aLoc);
314             if (fabs(myScale - 1.) > Precision::Confusion()) {
315               const gp_XYZ aTransl = aTrsf.TranslationPart() * myScale;
316               aTrsf.SetTranslationPart (aTransl);
317             }
318             aTrans->SetTransform (aTrsf);
319             myScene.AddNode (aTrans, Standard_False);
320             aGroup->AddNode (aTrans);
321             
322             // Store the shape node under the transform.
323             aTrans->AddNode (aShapeNode);
324           }
325         }
326       }
327     }
328   }
329   myShapes.Clear();
330 }
331
332 //=======================================================================
333 //function : triToIndexedFaceSet
334 //purpose  : 
335 //=======================================================================
336
337 Handle_VrmlData_Geometry VrmlData_ShapeConvert::triToIndexedFaceSet
338                                   (const Handle_Poly_Triangulation&  theTri,
339                                    const TopoDS_Face&                theFace,
340                                    const Handle_VrmlData_Coordinate& theCoord)
341 {
342   Standard_Integer i;
343   const Standard_Integer nNodes         (theTri->NbNodes());
344   const Standard_Integer nTriangles     (theTri->NbTriangles());
345   const TColgp_Array1OfPnt&    arrPolyNodes = theTri->Nodes();
346   const Poly_Array1OfTriangle& arrTriangles = theTri->Triangles();
347
348   const Handle(VrmlData_IndexedFaceSet) aFaceSet =
349     new VrmlData_IndexedFaceSet (myScene,
350                                  0L,                    // no name
351                                  Standard_True,         // IsCCW
352                                  Standard_False,        // IsSolid
353                                  Standard_False);       // IsConvex
354   const Handle(NCollection_IncAllocator)& anAlloc = myScene.Allocator();
355   const Standard_Boolean isReverse = (theFace.Orientation() == TopAbs_REVERSED);
356
357   // Create the array of triangles
358   const Standard_Integer ** arrPolygons = static_cast<const Standard_Integer **>
359     (anAlloc->Allocate (nTriangles * sizeof(const Standard_Integer *)));
360   aFaceSet->SetPolygons (nTriangles, arrPolygons);
361
362   // Store the triangles
363   for (i = 0; i < nTriangles; i++) {
364     Standard_Integer * aPolygon = static_cast<Standard_Integer *>
365       (anAlloc->Allocate (4*sizeof(Standard_Integer)));
366     aPolygon[0] = 3;
367     arrTriangles(i+1).Get (aPolygon[1],aPolygon[2],aPolygon[3]);
368     aPolygon[1]--;
369     if (isReverse) {
370       const Standard_Integer aTmp = aPolygon[2]-1;
371       aPolygon[2] = aPolygon[3]-1;
372       aPolygon[3] = aTmp;
373     } else {
374       aPolygon[2]--;
375       aPolygon[3]--;
376     }
377     arrPolygons[i] = aPolygon;
378   }
379
380   // Create the Coordinates node
381   if (theCoord.IsNull() == Standard_False)
382     aFaceSet->SetCoordinates (theCoord);
383   else {
384     gp_XYZ * arrNodes = static_cast <gp_XYZ *>
385       (anAlloc->Allocate (nNodes * sizeof(gp_XYZ)));
386     for  (i = 0; i < nNodes; i++)
387       arrNodes[i] = arrPolyNodes(i+1).XYZ() * myScale;
388
389     const Handle(VrmlData_Coordinate) aCoordNode =
390       new VrmlData_Coordinate (myScene, 0L, nNodes, arrNodes);
391     myScene.AddNode (aCoordNode, Standard_False);
392     aFaceSet->SetCoordinates (aCoordNode);
393   }
394
395   // Create the Normals node if theTri has normals
396   if(theTri->HasNormals()) {
397     gp_XYZ * arrVec = static_cast <gp_XYZ *>
398       (anAlloc->Allocate (nNodes * sizeof(gp_XYZ)));
399     const TShort_Array1OfShortReal& Norm = theTri->Normals();
400     Standard_Integer j;
401     for  (i = 0, j = 1; i < nNodes; i++, j += 3) {
402       
403       gp_XYZ aNormal(Norm(j), Norm(j+1), Norm(j+2));
404       arrVec[i] = aNormal;
405
406     }
407     const Handle(VrmlData_Normal) aNormalNode =
408       new VrmlData_Normal (myScene, 0L, nNodes, arrVec);
409     myScene.AddNode (aNormalNode, Standard_False);
410     aFaceSet->SetNormals (aNormalNode);
411     return aFaceSet;
412   }
413
414   Poly_Connect PC(theTri);
415   // Create the Normals node (if UV- values are available)
416   TopLoc_Location aLoc;
417   const Standard_Real aConf2 = Precision::SquareConfusion();
418   const Handle(Geom_Surface) aSurface = BRep_Tool::Surface (theFace, aLoc);
419   if (theTri->HasUVNodes() && aSurface.IsNull() == Standard_False) {
420     if (aSurface->IsCNu(1) && aSurface->IsCNv(1))
421     {
422       Standard_Integer nbNormVal  = nNodes * 3; 
423       Handle(TShort_HArray1OfShortReal) Normals = 
424         new TShort_HArray1OfShortReal(1, nbNormVal);
425
426       const TColgp_Array1OfPnt2d& arrUV = theTri->UVNodes();
427       gp_XYZ * arrVec = static_cast <gp_XYZ *>
428         (anAlloc->Allocate (nNodes * sizeof(gp_XYZ)));
429
430       // Compute the normal vectors
431       Standard_Real Tol = Sqrt(aConf2);
432       for  (i = 0; i < nNodes; i++) {
433         const gp_Pnt2d& aUV = arrUV(i+1);
434         
435         gp_Dir aNormal;
436         
437         if (GeomLib::NormEstim(aSurface, aUV, Tol, aNormal) > 1) {
438           //Try to estimate as middle normal of adjacent triangles
439           Standard_Integer n[3];
440
441           gp_XYZ eqPlan(0., 0., 0.);
442           for (PC.Initialize(i+1);  PC.More(); PC.Next()) {
443             arrTriangles(PC.Value()).Get(n[0], n[1], n[2]);
444             gp_XYZ v1(arrPolyNodes(n[1]).Coord()-arrPolyNodes(n[0]).Coord());
445             gp_XYZ v2(arrPolyNodes(n[2]).Coord()-arrPolyNodes(n[1]).Coord());
446             gp_XYZ vv = v1^v2;
447
448             Standard_Real mod = vv.Modulus();
449             if (mod < Tol)
450               continue;
451
452             eqPlan += vv/mod;
453           }
454
455           if (eqPlan.SquareModulus() > gp::Resolution())
456             aNormal = gp_Dir(eqPlan);
457         }
458         if (isReverse)
459           aNormal.Reverse();
460
461         if (aNormal.X()*aNormal.X() < aConf2)
462           aNormal.SetX(0.);
463         if (aNormal.Y()*aNormal.Y() < aConf2)
464           aNormal.SetY(0.);
465         if (aNormal.Z()*aNormal.Z() < aConf2)
466           aNormal.SetZ(0.);
467         arrVec[i] = aNormal.XYZ();
468
469         Standard_Integer j = i * 3;
470         Normals->SetValue(j + 1, (Standard_ShortReal)aNormal.X());
471         Normals->SetValue(j + 2, (Standard_ShortReal)aNormal.Y());
472         Normals->SetValue(j + 3, (Standard_ShortReal)aNormal.Z());
473
474       }
475
476       theTri->SetNormals(Normals);  
477   
478       const Handle(VrmlData_Normal) aNormalNode =
479         new VrmlData_Normal (myScene, 0L, nNodes, arrVec);
480       myScene.AddNode (aNormalNode, Standard_False);
481       aFaceSet->SetNormals (aNormalNode);
482     }
483   }
484
485   return aFaceSet;
486 }
487
488 //=======================================================================
489 //function : polToIndexedLineSet
490 //purpose  : single polygon3D => IndexedLineSet
491 //=======================================================================
492
493 Handle_VrmlData_Geometry VrmlData_ShapeConvert::polToIndexedLineSet
494                                         (const Handle_Poly_Polygon3D& thePol)
495 {
496   Standard_Integer i;
497   const Standard_Integer    nNodes (thePol->NbNodes());
498   const TColgp_Array1OfPnt& arrPolyNodes = thePol->Nodes();
499   const Handle(NCollection_IncAllocator)& anAlloc = myScene.Allocator();
500
501   const Handle(VrmlData_IndexedLineSet) aLineSet =
502     new VrmlData_IndexedLineSet (myScene, 0L);
503
504   // Create the array of polygons (1 member)
505   const Standard_Integer ** arrPolygons = static_cast<const Standard_Integer **>
506     (anAlloc->Allocate (sizeof(const Standard_Integer *)));
507   aLineSet->SetPolygons (1, arrPolygons);
508
509   // Store the polygon
510   Standard_Integer * aPolygon = static_cast<Standard_Integer *>
511     (anAlloc->Allocate ((nNodes+1)*sizeof(Standard_Integer)));
512   aPolygon[0] = nNodes;
513   for (i = 1; i <= nNodes; i++)
514     aPolygon[i] = i-1;
515   arrPolygons[0] = aPolygon;
516
517   // Create the Coordinates node
518   gp_XYZ * arrNodes = static_cast <gp_XYZ *>
519     (anAlloc->Allocate (nNodes * sizeof(gp_XYZ)));
520   for  (i = 0; i < nNodes; i++)
521     arrNodes[i] = arrPolyNodes(i+1).XYZ() * myScale;
522
523   const Handle(VrmlData_Coordinate) aCoordNode =
524     new VrmlData_Coordinate (myScene, 0L, nNodes, arrNodes);
525   myScene.AddNode (aCoordNode, Standard_False);
526   aLineSet->SetCoordinates (aCoordNode);
527
528   return aLineSet;
529 }
530
531 //=======================================================================
532 //function : defaultMaterialFace
533 //purpose  : 
534 //=======================================================================
535
536 Handle(VrmlData_Appearance) VrmlData_ShapeConvert::defaultMaterialFace () const
537 {
538   static char aNodeName[] = "__defaultMaterialFace";
539   Handle(VrmlData_Appearance) anAppearance =
540     Handle(VrmlData_Appearance)::DownCast(myScene.FindNode(aNodeName));
541   if (anAppearance.IsNull()) {
542     const Handle(VrmlData_Material) aMaterial =
543       new VrmlData_Material (myScene, 0L, 1.0, 0.022, 0.);
544     aMaterial->SetDiffuseColor (Quantity_Color(0.780392, 0.568627, 0.113725,
545                                                Quantity_TOC_RGB));
546     aMaterial->SetEmissiveColor(Quantity_Color(0.329412, 0.223529, 0.027451,
547                                                Quantity_TOC_RGB));
548     aMaterial->SetSpecularColor(Quantity_Color(0.992157, 0.941176, 0.807843,
549                                                Quantity_TOC_RGB));
550     myScene.AddNode (aMaterial, Standard_False);
551     anAppearance = new VrmlData_Appearance (myScene, aNodeName);
552     anAppearance->SetMaterial (aMaterial);
553     myScene.AddNode (anAppearance, Standard_False);
554   }
555   return anAppearance;
556 }
557
558 //=======================================================================
559 //function : defaultMaterialEdge
560 //purpose  : 
561 //=======================================================================
562
563 Handle(VrmlData_Appearance) VrmlData_ShapeConvert::defaultMaterialEdge () const
564 {
565   static char aNodeName[] = "__defaultMaterialEdge";
566   Handle(VrmlData_Appearance) anAppearance =
567     Handle(VrmlData_Appearance)::DownCast(myScene.FindNode(aNodeName));
568   if (anAppearance.IsNull()) { 
569     const Handle(VrmlData_Material) aMaterial =
570       new VrmlData_Material (myScene, 0L, 0.2, 0.2, 0.2);
571     aMaterial->SetDiffuseColor (Quantity_Color(0.2, 0.7, 0.2,
572                                                Quantity_TOC_RGB));
573     aMaterial->SetEmissiveColor(Quantity_Color(0.2, 0.7, 0.2,
574                                                Quantity_TOC_RGB));
575     aMaterial->SetSpecularColor(Quantity_Color(0.2, 0.7, 0.2,
576                                                Quantity_TOC_RGB));
577     myScene.AddNode (aMaterial, Standard_False);
578     anAppearance = new VrmlData_Appearance (myScene, aNodeName);
579     anAppearance->SetMaterial (aMaterial);
580     myScene.AddNode (anAppearance, Standard_False);
581   }
582   return anAppearance;
583 }