0032107: Data Exchange, RWGltf_CafReader - reading glTF document back loses sharing
[occt.git] / src / RWMesh / RWMesh_CafReader.cxx
1 // Author: Kirill Gavrilov
2 // Copyright (c) 2016-2019 OPEN CASCADE SAS
3 //
4 // This file is part of Open CASCADE Technology software library.
5 //
6 // This library is free software; you can redistribute it and/or modify it under
7 // the terms of the GNU Lesser General Public License version 2.1 as published
8 // by the Free Software Foundation, with special exception defined in the file
9 // OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
10 // distribution for complete text of the license and disclaimer of any warranty.
11 //
12 // Alternatively, this file may be used under the terms of Open CASCADE
13 // commercial license or contractual agreement.
14
15 #include <RWMesh_CafReader.hxx>
16
17 #include <XCAFPrs_DocumentExplorer.hxx>
18
19 #include <Message.hxx>
20 #include <Message_Messenger.hxx>
21 #include <Message_ProgressScope.hxx>
22 #include <BRep_Builder.hxx>
23 #include <OSD_Path.hxx>
24 #include <OSD_Timer.hxx>
25 #include <TDataStd_Name.hxx>
26 #include <TDocStd_Document.hxx>
27 #include <TopExp_Explorer.hxx>
28 #include <TopoDS.hxx>
29 #include <TopoDS_Iterator.hxx>
30 #include <XCAFDoc_ColorTool.hxx>
31 #include <XCAFDoc_ColorType.hxx>
32 #include <XCAFDoc_DocumentTool.hxx>
33 #include <XCAFDoc_ShapeMapTool.hxx>
34 #include <XCAFDoc_ShapeTool.hxx>
35 #include <XCAFDoc_VisMaterialTool.hxx>
36
37 IMPLEMENT_STANDARD_RTTIEXT(RWMesh_CafReader, Standard_Transient)
38
39 // =======================================================================
40 // function : RWMesh_CafReader
41 // purpose  :
42 // =======================================================================
43 RWMesh_CafReader::RWMesh_CafReader()
44 : myToFillDoc (Standard_True),
45   myToFillIncomplete (Standard_True),
46   myMemoryLimitMiB (-1),
47   myExtraStatus (RWMesh_CafReaderStatusEx_NONE)
48 {
49   //
50 }
51
52 // =======================================================================
53 // function : ~RWMesh_CafReader
54 // purpose  :
55 // =======================================================================
56 RWMesh_CafReader::~RWMesh_CafReader()
57 {
58   //
59 }
60
61 void RWMesh_CafReader::SetDocument(const Handle(TDocStd_Document)& theDoc)
62 {
63   myXdeDoc = theDoc;
64   Standard_Real aScaleFactorM = 1.;
65   if (XCAFDoc_DocumentTool::GetLengthUnit(theDoc, aScaleFactorM))
66   {
67     SetSystemLengthUnit(aScaleFactorM);
68   }
69 }
70
71 // =======================================================================
72 // function : SingleShape
73 // purpose  :
74 // =======================================================================
75 TopoDS_Shape RWMesh_CafReader::SingleShape() const
76 {
77   if (myRootShapes.Size() > 1)
78   {
79     BRep_Builder aBuilder;
80     TopoDS_Compound aCompound;
81     aBuilder.MakeCompound (aCompound);
82     for (TopTools_SequenceOfShape::Iterator aRootIter (myRootShapes); aRootIter.More(); aRootIter.Next())
83     {
84       aBuilder.Add (aCompound, aRootIter.Value());
85     }
86     return aCompound;
87   }
88   else if (!myRootShapes.IsEmpty())
89   {
90     return myRootShapes.First();
91   }
92   return TopoDS_Shape();
93 }
94
95 // =======================================================================
96 // function : perform
97 // purpose  :
98 // =======================================================================
99 Standard_Boolean RWMesh_CafReader::perform (const TCollection_AsciiString& theFile,
100                                             const Message_ProgressRange& theProgress,
101                                             const Standard_Boolean theToProbe)
102 {
103   Standard_Integer aNewRootsLower = 1;
104   if (!myXdeDoc.IsNull())
105   {
106     TDF_LabelSequence aRootLabels;
107     XCAFDoc_DocumentTool::ShapeTool (myXdeDoc->Main())->GetFreeShapes (aRootLabels);
108     aNewRootsLower = aRootLabels.Upper() + 1;
109   }
110
111   OSD_Timer aLoadingTimer;
112   aLoadingTimer.Start();
113   const Standard_Boolean isDone = performMesh (theFile, theProgress, theToProbe);
114   if (theToProbe || theProgress.UserBreak())
115   {
116     return isDone;
117   }
118   else if (!isDone)
119   {
120     if (!myToFillIncomplete)
121     {
122       return Standard_False;
123     }
124
125     myExtraStatus |= RWMesh_CafReaderStatusEx_Partial;
126   }
127
128   TopLoc_Location aDummyLoc;
129   Standard_Integer aNbNodes = 0, aNbElems = 0, aNbFaces = 0;
130   for (TopTools_SequenceOfShape::Iterator aRootIter (myRootShapes); aRootIter.More(); aRootIter.Next())
131   {
132     for (TopExp_Explorer aFaceIter (aRootIter.Value(), TopAbs_FACE); aFaceIter.More(); aFaceIter.Next())
133     {
134       const TopoDS_Face& aFace = TopoDS::Face (aFaceIter.Current());
135       if (const Handle(Poly_Triangulation)& aPolyTri = BRep_Tool::Triangulation (aFace, aDummyLoc))
136       {
137         ++aNbFaces;
138         aNbNodes += aPolyTri->NbNodes();
139         aNbElems += aPolyTri->NbTriangles();
140       }
141     }
142   }
143   if (!isDone && aNbElems < 100)
144   {
145     return Standard_False;
146   }
147
148   fillDocument();
149   generateNames (theFile, aNewRootsLower, Standard_False);
150
151   aLoadingTimer.Stop();
152
153   Message::SendInfo (TCollection_AsciiString ("Mesh ") + theFile
154                    + "\n[" + aNbNodes + " nodes] [" + aNbElems + " 2d elements]"
155                    + "\n[" + (!isDone ? "PARTIALLY " : "") + "read in " + aLoadingTimer.ElapsedTime() + " s]");
156   return Standard_True;
157 }
158
159 // =======================================================================
160 // function : fillDocument
161 // purpose  :
162 // =======================================================================
163 void RWMesh_CafReader::fillDocument()
164 {
165   if (!myToFillDoc
166     || myXdeDoc.IsNull()
167     || myRootShapes.IsEmpty())
168   {
169     return;
170   }
171
172   // set units
173   Standard_Real aLengthUnit = 1.;
174   if (!XCAFDoc_DocumentTool::GetLengthUnit(myXdeDoc, aLengthUnit))
175   {
176     XCAFDoc_DocumentTool::SetLengthUnit(myXdeDoc, SystemLengthUnit());
177   }
178   else if (aLengthUnit != SystemLengthUnit())
179   {
180     Message::SendWarning("Warning: Length unit of document not equal to the system length unit");
181   }
182
183   const Standard_Boolean wasAutoNaming = XCAFDoc_ShapeTool::AutoNaming();
184   XCAFDoc_ShapeTool::SetAutoNaming (Standard_False);
185   const TCollection_AsciiString aRootName; // = generateRootName (theFile);
186   CafDocumentTools aTools;
187   aTools.ShapeTool = XCAFDoc_DocumentTool::ShapeTool (myXdeDoc->Main());
188   aTools.ColorTool = XCAFDoc_DocumentTool::ColorTool (myXdeDoc->Main());
189   aTools.VisMaterialTool = XCAFDoc_DocumentTool::VisMaterialTool (myXdeDoc->Main());
190   for (TopTools_SequenceOfShape::Iterator aRootIter (myRootShapes); aRootIter.More(); aRootIter.Next())
191   {
192     addShapeIntoDoc (aTools, aRootIter.Value(), TDF_Label(), aRootName);
193   }
194   XCAFDoc_DocumentTool::ShapeTool (myXdeDoc->Main())->UpdateAssemblies();
195   XCAFDoc_ShapeTool::SetAutoNaming (wasAutoNaming);
196 }
197
198 // =======================================================================
199 // function : setShapeName
200 // purpose  :
201 // =======================================================================
202 void RWMesh_CafReader::setShapeName (const TDF_Label& theLabel,
203                                      const TopAbs_ShapeEnum theShapeType,
204                                      const TCollection_AsciiString& theName,
205                                      const TDF_Label& theParentLabel,
206                                      const TCollection_AsciiString& theParentName)
207 {
208   if (!theName.IsEmpty())
209   {
210     TDataStd_Name::Set (theLabel, theName);
211   }
212   else if (!theParentLabel.IsNull())
213   {
214     TDataStd_Name::Set (theLabel, shapeTypeToString (theShapeType));
215   }
216   else if (theParentLabel.IsNull()
217        && !theParentName.IsEmpty())
218   {
219     TDataStd_Name::Set (theLabel, theParentName);
220   }
221 }
222
223 // =======================================================================
224 // function : setShapeStyle
225 // purpose  :
226 // =======================================================================
227 void RWMesh_CafReader::setShapeStyle (const CafDocumentTools& theTools,
228                                       const TDF_Label& theLabel,
229                                       const XCAFPrs_Style& theStyle)
230 {
231   if (theStyle.IsSetColorSurf())
232   {
233     theTools.ColorTool->SetColor (theLabel, theStyle.GetColorSurfRGBA(), XCAFDoc_ColorSurf);
234   }
235   if (theStyle.IsSetColorCurv())
236   {
237     theTools.ColorTool->SetColor (theLabel, theStyle.GetColorCurv(), XCAFDoc_ColorCurv);
238   }
239   if (!theStyle.Material().IsNull())
240   {
241     TDF_Label aMaterialLabel = theStyle.Material()->Label();
242     if (aMaterialLabel.IsNull())
243     {
244       const TCollection_AsciiString aMatName = !theStyle.Material()->RawName().IsNull()
245                                              ?  theStyle.Material()->RawName()->String()
246                                              :  "";
247       aMaterialLabel = theTools.VisMaterialTool->AddMaterial (theStyle.Material(), aMatName);
248     }
249     theTools.VisMaterialTool->SetShapeMaterial (theLabel, aMaterialLabel);
250   }
251 }
252
253 // =======================================================================
254 // function : setShapeNamedData
255 // purpose  :
256 // =======================================================================
257 void RWMesh_CafReader::setShapeNamedData (const CafDocumentTools& ,
258                                           const TDF_Label& theLabel,
259                                           const Handle(TDataStd_NamedData)& theNameData)
260 {
261   if (theNameData.IsNull())
262   {
263     return;
264   }
265
266   const TDF_Label aNameDataLabel = theNameData->Label();
267   Handle(TDataStd_NamedData) anOtherNamedData;
268   if (theLabel.FindAttribute (theNameData->ID(), anOtherNamedData))
269   {
270     if (anOtherNamedData->Label() != aNameDataLabel)
271     {
272       Message::SendAlarm ("Error! Different NamedData is already set to shape");
273     }
274   }
275   else
276   {
277     if (aNameDataLabel.IsNull())
278     {
279       theLabel.AddAttribute (theNameData);
280     }
281     else
282     {
283       Message::SendAlarm ("Error! Skipped NamedData instance shared across shapes");
284     }
285   }
286 }
287
288 // =======================================================================
289 // function : addShapeIntoDoc
290 // purpose  :
291 // =======================================================================
292 Standard_Boolean RWMesh_CafReader::addShapeIntoDoc (CafDocumentTools& theTools,
293                                                     const TopoDS_Shape& theShape,
294                                                     const TDF_Label& theLabel,
295                                                     const TCollection_AsciiString& theParentName)
296 {
297   if (theShape.IsNull()
298    || myXdeDoc.IsNull())
299   {
300     return Standard_False;
301   }
302
303   const TopAbs_ShapeEnum aShapeType = theShape.ShapeType();
304   TopoDS_Shape aShapeToAdd = theShape;
305   const TopoDS_Shape aShapeNoLoc = theShape.Located (TopLoc_Location());
306   Standard_Boolean toMakeAssembly = Standard_False;
307   if (theShape.ShapeType() == TopAbs_COMPOUND)
308   {
309     RWMesh_NodeAttributes aSubFaceAttribs;
310     for (TopoDS_Iterator aSubShapeIter (theShape, Standard_True, Standard_False); !toMakeAssembly && aSubShapeIter.More(); aSubShapeIter.Next())
311     {
312       if (aSubShapeIter.Value().ShapeType() != TopAbs_FACE)
313       {
314         toMakeAssembly = Standard_True;
315         break;
316       }
317
318       const TopoDS_Face& aFace = TopoDS::Face (aSubShapeIter.Value());
319       toMakeAssembly = toMakeAssembly
320                     || (myAttribMap.Find (aFace, aSubFaceAttribs) && !aSubFaceAttribs.Name.IsEmpty());
321     }
322
323     if (toMakeAssembly)
324     {
325       // create an empty Compound to add as assembly, so that we can add children one-by-one via AddComponent()
326       TopoDS_Compound aCompound;
327       BRep_Builder aBuilder;
328       aBuilder.MakeCompound (aCompound);
329       aCompound.Location (theShape.Location(), Standard_False);
330       aShapeToAdd = aCompound;
331     }
332   }
333
334   TDF_Label aNewLabel, anOldLabel;
335   if (theLabel.IsNull())
336   {
337     // add new shape
338     aNewLabel = theTools.ShapeTool->AddShape (aShapeToAdd, toMakeAssembly);
339   }
340   else if (theTools.ShapeTool->IsAssembly (theLabel))
341   {
342     // add shape as component
343     if (theTools.ComponentMap.Find (aShapeNoLoc, anOldLabel))
344     {
345       aNewLabel = theTools.ShapeTool->AddComponent (theLabel, anOldLabel, theShape.Location());
346     }
347     else
348     {
349       aNewLabel = theTools.ShapeTool->AddComponent (theLabel, aShapeToAdd, toMakeAssembly);
350
351       TDF_Label aRefLabel = aNewLabel;
352       theTools.ShapeTool->GetReferredShape (aNewLabel, aRefLabel);
353       if (!aRefLabel.IsNull())
354       {
355         theTools.ComponentMap.Bind (aShapeNoLoc, aRefLabel);
356       }
357     }
358   }
359   else
360   {
361     // add shape as sub-shape
362     aNewLabel = theTools.ShapeTool->AddSubShape (theLabel, theShape);
363     if (!aNewLabel.IsNull())
364     {
365       Handle(XCAFDoc_ShapeMapTool) aShapeMapTool = XCAFDoc_ShapeMapTool::Set (aNewLabel);
366       aShapeMapTool->SetShape (theShape);
367     }
368   }
369   if (aNewLabel.IsNull())
370   {
371     return Standard_False;
372   }
373
374   // if new label is a reference get referred shape
375   TDF_Label aNewRefLabel = aNewLabel;
376   theTools.ShapeTool->GetReferredShape (aNewLabel, aNewRefLabel);
377
378   RWMesh_NodeAttributes aRefShapeAttribs;
379   myAttribMap.Find (aShapeNoLoc, aRefShapeAttribs);
380
381   bool hasProductName = false;
382   if (aNewLabel != aNewRefLabel)
383   {
384     // put attributes to the Instance (overrides Product attributes)
385     RWMesh_NodeAttributes aShapeAttribs;
386     if (!theShape.Location().IsIdentity()
387       && myAttribMap.Find (theShape, aShapeAttribs))
388     {
389       if (!aShapeAttribs.Style.IsEqual (aRefShapeAttribs.Style))
390       {
391         setShapeStyle (theTools, aNewLabel, aShapeAttribs.Style);
392       }
393       if (aShapeAttribs.NamedData != aRefShapeAttribs.NamedData)
394       {
395         setShapeNamedData (theTools, aNewLabel, aShapeAttribs.NamedData);
396       }
397       setShapeName (aNewLabel, aShapeType, aShapeAttribs.Name, theLabel, theParentName);
398       if (aRefShapeAttribs.Name.IsEmpty()
399       && !aShapeAttribs.Name.IsEmpty())
400       {
401         // it is not nice having unnamed Product, so copy name from first Instance (probably the only one)
402         hasProductName = true;
403         setShapeName (aNewRefLabel, aShapeType, aShapeAttribs.Name, theLabel, theParentName);
404       }
405       else if (aShapeAttribs.Name.IsEmpty()
406            && !aRefShapeAttribs.Name.IsEmpty())
407       {
408         // copy name from Product
409         setShapeName (aNewLabel, aShapeType, aRefShapeAttribs.Name, theLabel, theParentName);
410       }
411     }
412     else
413     {
414       // copy name from Product
415       setShapeName (aNewLabel, aShapeType, aRefShapeAttribs.Name, theLabel, theParentName);
416     }
417   }
418
419   if (!anOldLabel.IsNull())
420   {
421     // already defined in the document
422     return Standard_True;
423   }
424
425   // put attributes to the Product (shared across Instances)
426   if (!hasProductName)
427   {
428     setShapeName (aNewRefLabel, aShapeType, aRefShapeAttribs.Name, theLabel, theParentName);
429   }
430   setShapeStyle (theTools, aNewRefLabel, aRefShapeAttribs.Style);
431   setShapeNamedData (theTools, aNewRefLabel, aRefShapeAttribs.NamedData);
432
433   if (theTools.ShapeTool->IsAssembly (aNewRefLabel))
434   {
435     // store sub-shapes (iterator is set to not inherit Location of parent object)
436     TCollection_AsciiString aDummyName;
437     for (TopoDS_Iterator aSubShapeIter (theShape, Standard_True, Standard_False); aSubShapeIter.More(); aSubShapeIter.Next())
438     {
439       addShapeIntoDoc (theTools, aSubShapeIter.Value(), aNewRefLabel, aDummyName);
440     }
441   }
442   else
443   {
444     // store a plain list of sub-shapes in case if they have custom attributes (usually per-face color)
445     for (TopoDS_Iterator aSubShapeIter (theShape, Standard_True, Standard_False); aSubShapeIter.More(); aSubShapeIter.Next())
446     {
447       addSubShapeIntoDoc(theTools, aSubShapeIter.Value(), aNewRefLabel);
448     }
449   }
450   return Standard_True;
451 }
452
453 // =======================================================================
454 // function : addSubShapeIntoDoc
455 // purpose  :
456 // =======================================================================
457 Standard_Boolean RWMesh_CafReader::addSubShapeIntoDoc (CafDocumentTools& theTools,
458                                                        const TopoDS_Shape& theShape,
459                                                        const TDF_Label& theParentLabel)
460 {
461   if (theShape.IsNull()
462    || myXdeDoc.IsNull())
463   {
464     return Standard_False;
465   }
466
467   RWMesh_NodeAttributes aShapeAttribs;
468   const TopAbs_ShapeEnum aShapeType = theShape.ShapeType();
469   const Standard_Boolean aHasAttribs = myAttribMap.Find(theShape.Located(TopLoc_Location()), aShapeAttribs);
470
471   // check for the attribute
472   // shell or wire may not contain an attribute, but its subshapes need to be checked
473   if (!aHasAttribs && aShapeType != TopAbs_SHELL &&
474     aShapeType != TopAbs_WIRE)
475   {
476     return Standard_False;
477   }
478
479   for (TopoDS_Iterator aSubShapeIter(theShape, Standard_True, Standard_False); aSubShapeIter.More(); aSubShapeIter.Next())
480   {
481     addSubShapeIntoDoc(theTools, aSubShapeIter.Value(), theParentLabel);
482   }
483
484   if (!aHasAttribs)
485   {
486     return Standard_False;
487   }
488
489   TDF_Label aNewLabel = theTools.ShapeTool->AddSubShape (theParentLabel, theShape);
490   if (aNewLabel.IsNull())
491   {
492     return Standard_False;
493   }
494
495   Handle(XCAFDoc_ShapeMapTool) aShapeMapTool = XCAFDoc_ShapeMapTool::Set(aNewLabel);
496   aShapeMapTool->SetShape(theShape);
497
498   // if new label is a reference get referred shape
499   TDF_Label aNewRefLabel = aNewLabel;
500   theTools.ShapeTool->GetReferredShape (aNewLabel, aNewRefLabel);
501
502   // put attributes to the Product (shared across Instances)
503   static const TCollection_AsciiString anEmptyString;
504   setShapeName (aNewRefLabel, aShapeType, aShapeAttribs.Name, TDF_Label(), anEmptyString);
505   setShapeStyle (theTools, aNewRefLabel, aShapeAttribs.Style);
506   setShapeNamedData (theTools, aNewRefLabel, aShapeAttribs.NamedData);
507
508   return Standard_True;
509 }
510
511 // =======================================================================
512 // function : generateNames
513 // purpose  :
514 // =======================================================================
515 void RWMesh_CafReader::generateNames (const TCollection_AsciiString& theFile,
516                                       const Standard_Integer theRootLower,
517                                       const Standard_Boolean theWithSubLabels)
518 {
519   if (myXdeDoc.IsNull())
520   {
521     return;
522   }
523
524   TCollection_AsciiString aDummyFolder, aFileName;
525   OSD_Path::FolderAndFileFromPath (theFile, aDummyFolder, aFileName);
526   const TCollection_AsciiString aRootName = myRootPrefix + aFileName;
527
528   Handle(XCAFDoc_ShapeTool) aShapeTool = XCAFDoc_DocumentTool::ShapeTool (myXdeDoc->Main());
529   TDF_LabelSequence aRootLabels;
530   aShapeTool->GetFreeShapes (aRootLabels);
531   if (aRootLabels.Upper() < theRootLower)
532   {
533     return;
534   }
535
536   // replace empty names
537   Handle(TDataStd_Name) aNodeName;
538   Standard_Integer aRootIndex = aRootLabels.Lower();
539   TDF_LabelSequence aNewRootLabels;
540   for (TDF_LabelSequence::Iterator aRootIter (aRootLabels); aRootIter.More(); ++aRootIndex, aRootIter.Next())
541   {
542     if (aRootIndex < theRootLower)
543     {
544       continue;
545     }
546     else if (theWithSubLabels)
547     {
548       aNewRootLabels.Append (aRootIter.Value());
549     }
550
551     const TDF_Label aLabel = aRootIter.Value();
552     TDF_Label aRefLab = aLabel;
553     XCAFDoc_ShapeTool::GetReferredShape (aLabel, aRefLab);
554     if (!aRefLab.FindAttribute (TDataStd_Name::GetID(), aNodeName))
555     {
556       TDataStd_Name::Set (aRefLab, aRootName);
557     }
558     if (aLabel != aRefLab
559     && !aLabel.FindAttribute (TDataStd_Name::GetID(), aNodeName))
560     {
561       TDataStd_Name::Set (aLabel, aRootName);
562     }
563   }
564
565   if (theWithSubLabels)
566   {
567     for (XCAFPrs_DocumentExplorer aDocIter (myXdeDoc, aNewRootLabels, XCAFPrs_DocumentExplorerFlags_NoStyle);
568          aDocIter.More(); aDocIter.Next())
569     {
570       if (aDocIter.CurrentDepth() == 0
571        || aDocIter.Current().RefLabel.FindAttribute (TDataStd_Name::GetID(), aNodeName))
572       {
573         continue;
574       }
575
576       const TopoDS_Shape aShape = XCAFDoc_ShapeTool::GetShape (aDocIter.Current().RefLabel);
577       if (!aShape.IsNull())
578       {
579         TDataStd_Name::Set (aDocIter.Current().RefLabel, shapeTypeToString (aShape.ShapeType()));
580       }
581     }
582   }
583 }