0032086: Visualization - support deferred data loading
[occt.git] / src / RWGltf / RWGltf_TriangulationReader.cxx
1 // Author: Kirill Gavrilov
2 // Copyright (c) 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 <RWGltf_TriangulationReader.hxx>
16
17 #include <Message.hxx>
18 #include <OSD_FileSystem.hxx>
19 #include <RWGltf_GltfLatePrimitiveArray.hxx>
20 #include <RWGltf_GltfPrimArrayData.hxx>
21 #include <Standard_ArrayStreamBuffer.hxx>
22 #include <Standard_ReadBuffer.hxx>
23
24 namespace
25 {
26   static const Standard_Integer   THE_LOWER_TRI_INDEX  = 1;
27   static const Standard_Integer   THE_LOWER_NODE_INDEX = 1;
28   static const Standard_ShortReal THE_NORMAL_PREC2 = 0.001f;
29 }
30
31 IMPLEMENT_STANDARD_RTTIEXT(RWGltf_TriangulationReader, RWMesh_TriangulationReader)
32
33 // =======================================================================
34 // function : RWGltf_TriangulationReader
35 // purpose  :
36 // =======================================================================
37 RWGltf_TriangulationReader::RWGltf_TriangulationReader()
38 {
39   //
40 }
41
42 // =======================================================================
43 // function : reportError
44 // purpose  :
45 // =======================================================================
46 void RWGltf_TriangulationReader::reportError (const TCollection_AsciiString& theText) const
47 {
48   Message::SendFail (TCollection_AsciiString("File '") + myFileName + "' defines invalid glTF!\n" + theText);
49 }
50
51 // =======================================================================
52 // function : load
53 // purpose  :
54 // =======================================================================
55 bool RWGltf_TriangulationReader::load (const Handle(RWMesh_TriangulationSource)& theSourceMesh,
56                                        const Handle(Poly_Triangulation)& theDestMesh,
57                                        const Handle(OSD_FileSystem)& theFileSystem) const
58 {
59   const Handle(RWGltf_GltfLatePrimitiveArray) aSourceGltfMesh = Handle(RWGltf_GltfLatePrimitiveArray)::DownCast(theSourceMesh);
60   if (aSourceGltfMesh.IsNull()
61    || aSourceGltfMesh->PrimitiveMode() == RWGltf_GltfPrimitiveMode_UNKNOWN)
62   {
63     return false;
64   }
65
66   for (NCollection_Sequence<RWGltf_GltfPrimArrayData>::Iterator aDataIter (aSourceGltfMesh->Data()); aDataIter.More(); aDataIter.Next())
67   {
68     const RWGltf_GltfPrimArrayData& aData = aDataIter.Value();
69     if (!aData.StreamData.IsNull())
70     {
71       Standard_ArrayStreamBuffer aStreamBuffer ((const char* )aData.StreamData->Data(), aData.StreamData->Size());
72       std::istream aStream (&aStreamBuffer);
73       aStream.seekg ((std::streamoff )aData.StreamOffset, std::ios_base::beg);
74       if (!readBuffer (aSourceGltfMesh, theDestMesh, aStream, aData.Accessor, aData.Type))
75       {
76         return false;
77       }
78       continue;
79     }
80     else if (aData.StreamUri.IsEmpty())
81     {
82       reportError (TCollection_AsciiString ("Buffer '") + aSourceGltfMesh->Id() + "' does not define uri.");
83       return false;
84     }
85
86     const Handle(OSD_FileSystem)& aFileSystem = !theFileSystem.IsNull() ? theFileSystem : OSD_FileSystem::DefaultFileSystem();
87     opencascade::std::shared_ptr<std::istream> aSharedStream = aFileSystem->OpenIStream (aData.StreamUri, std::ios::in | std::ios::binary, aData.StreamOffset);
88     if (aSharedStream.get() == NULL)
89     {
90       reportError (TCollection_AsciiString ("Buffer '") + aSourceGltfMesh->Id() + "refers to invalid file '" + aData.StreamUri + "'.");
91       return false;
92     }
93     if (!readBuffer (aSourceGltfMesh, theDestMesh, *aSharedStream.get(), aData.Accessor, aData.Type))
94     {
95       return false;
96     }
97   }
98   return true;
99 }
100
101 // =======================================================================
102 // function : finalizeLoading
103 // purpose  :
104 // =======================================================================
105 bool RWGltf_TriangulationReader::finalizeLoading (const Handle(RWMesh_TriangulationSource)& theSourceMesh,
106                                                   const Handle(Poly_Triangulation)& theDestMesh) const
107 {
108   if (theDestMesh->NbNodes() < 1)
109   {
110     return false;
111   }
112   if (theDestMesh->NbTriangles() < 1)
113   {
114     const Handle(RWGltf_GltfLatePrimitiveArray) aSourceGltfMesh = Handle(RWGltf_GltfLatePrimitiveArray)::DownCast(theSourceMesh);
115     if (!aSourceGltfMesh.IsNull() && aSourceGltfMesh->PrimitiveMode() == RWGltf_GltfPrimitiveMode_Triangles)
116     {
117       // reconstruct indexes
118       const Standard_Integer aNbTris = theDestMesh->NbNodes() / 3;
119       if (!setNbTriangles (theDestMesh, aNbTris))
120       {
121         return false;
122       }
123       for (Standard_Integer aTriIter = 0; aTriIter < aNbTris; ++aTriIter)
124       {
125         if (!setTriangle (theDestMesh, THE_LOWER_TRI_INDEX + aTriIter,
126                           Poly_Triangle (THE_LOWER_NODE_INDEX + aTriIter * 3 + 0,
127                                          THE_LOWER_NODE_INDEX + aTriIter * 3 + 1,
128                                          THE_LOWER_NODE_INDEX + aTriIter * 3 + 2)))
129         {
130           return false;
131         }
132       }
133     }
134   }
135   return RWMesh_TriangulationReader::finalizeLoading (theSourceMesh, theDestMesh);
136 }
137
138 // =======================================================================
139 // function : readBuffer
140 // purpose  :
141 // =======================================================================
142 bool RWGltf_TriangulationReader::readBuffer (const Handle(RWGltf_GltfLatePrimitiveArray)& theSourceMesh,
143                                              const Handle(Poly_Triangulation)& theDestMesh,
144                                              std::istream& theStream,
145                                              const RWGltf_GltfAccessor& theAccessor,
146                                              RWGltf_GltfArrayType theType) const
147
148 {
149   const TCollection_AsciiString& aName = theSourceMesh->Id();
150   if (theSourceMesh->PrimitiveMode() != RWGltf_GltfPrimitiveMode_Triangles)
151   {
152     Message::SendWarning (TCollection_AsciiString("Buffer '") + aName + "' skipped unsupported primitive array");
153     return true;
154   }
155
156   switch (theType)
157   {
158     case RWGltf_GltfArrayType_Indices:
159     {
160       if (theAccessor.Type != RWGltf_GltfAccessorLayout_Scalar)
161       {
162         break;
163       }
164
165       Poly_Triangle aVec3;
166       if (theAccessor.ComponentType == RWGltf_GltfAccessorCompType_UInt16)
167       {
168         if ((theAccessor.Count / 3) > std::numeric_limits<Standard_Integer>::max())
169         {
170           reportError (TCollection_AsciiString ("Buffer '") + aName + "' defines too big array.");
171           return false;
172         }
173
174         const Standard_Integer aNbTris = (Standard_Integer )(theAccessor.Count / 3);
175         if (!setNbTriangles (theDestMesh, aNbTris))
176         {
177           return false;
178         }
179         const size_t aStride = theAccessor.ByteStride != 0
180                              ? theAccessor.ByteStride
181                              : sizeof(uint16_t);
182         Standard_ReadBuffer aBuffer (theAccessor.Count * aStride, aStride);
183         Standard_Integer aLastTriIndex = 0;
184         for (Standard_Integer aTriIter = 0; aTriIter < aNbTris; ++aTriIter)
185         {
186           if (const uint16_t* anIndex0 = aBuffer.ReadChunk<uint16_t> (theStream))
187           {
188             aVec3.ChangeValue (1) = THE_LOWER_NODE_INDEX + *anIndex0;
189           }
190           if (const uint16_t* anIndex1 = aBuffer.ReadChunk<uint16_t> (theStream))
191           {
192             aVec3.ChangeValue (2) = THE_LOWER_NODE_INDEX + *anIndex1;
193           }
194           if (const uint16_t* anIndex2 = aBuffer.ReadChunk<uint16_t> (theStream))
195           {
196             aVec3.ChangeValue (3) = THE_LOWER_NODE_INDEX + *anIndex2;
197           }
198           else
199           {
200             reportError (TCollection_AsciiString ("Buffer '") + aName + "' reading error.");
201             return false;
202           }
203
204           const Standard_Integer wasSet = setTriangle (theDestMesh, THE_LOWER_TRI_INDEX + aLastTriIndex, aVec3);
205           if (!wasSet)
206           {
207             reportError (TCollection_AsciiString ("Buffer '") + aName + "' refers to invalid indices.");
208           }
209           if (wasSet > 0)
210           {
211             aLastTriIndex++;
212           }
213         }
214         const Standard_Integer aNbDegenerate = aNbTris - aLastTriIndex;
215         if (aNbDegenerate > 0)
216         {
217           if (aNbDegenerate == aNbTris)
218           {
219             Message::SendWarning (TCollection_AsciiString("Buffer '") + aName + "' has been skipped (all elements are degenerative in)");
220             return false;
221           }
222           theSourceMesh->ChangeDegeneratedTriNb() += aNbDegenerate;
223           if ((myLoadingStatistic == NULL) && myToPrintDebugMessages)
224           {
225             Message::SendTrace (TCollection_AsciiString() + aNbDegenerate
226                                 + " degenerate triangles have been skipped while reading glTF triangulation '" + aName + "'");
227           }
228           if (!setNbTriangles (theDestMesh, aLastTriIndex, true))
229           {
230             return false;
231           }
232         }
233       }
234       else if (theAccessor.ComponentType == RWGltf_GltfAccessorCompType_UInt32)
235       {
236         if ((theAccessor.Count / 3) > std::numeric_limits<Standard_Integer>::max())
237         {
238           reportError (TCollection_AsciiString ("Buffer '") + aName + "' defines too big array.");
239           return false;
240         }
241
242         const int aNbTris = (Standard_Integer )(theAccessor.Count / 3);
243         if (!setNbTriangles (theDestMesh, aNbTris))
244         {
245           return false;
246         }
247         const size_t aStride = theAccessor.ByteStride != 0
248                              ? theAccessor.ByteStride
249                              : sizeof(uint32_t);
250         Standard_ReadBuffer aBuffer (theAccessor.Count * aStride, aStride);
251         Standard_Integer aLastTriIndex = 0;
252         for (Standard_Integer aTriIter = 0; aTriIter < aNbTris; ++aTriIter)
253         {
254           if (const uint32_t* anIndex0 = aBuffer.ReadChunk<uint32_t> (theStream))
255           {
256             aVec3.ChangeValue (1) = THE_LOWER_NODE_INDEX + *anIndex0;
257           }
258           if (const uint32_t* anIndex1 = aBuffer.ReadChunk<uint32_t> (theStream))
259           {
260             aVec3.ChangeValue (2) = THE_LOWER_NODE_INDEX + *anIndex1;
261           }
262           if (const uint32_t* anIndex2 = aBuffer.ReadChunk<uint32_t> (theStream))
263           {
264             aVec3.ChangeValue (3) = THE_LOWER_NODE_INDEX + *anIndex2;
265           }
266           else
267           {
268             reportError (TCollection_AsciiString ("Buffer '") + aName + "' reading error.");
269             return false;
270           }
271
272           const Standard_Integer wasSet = setTriangle (theDestMesh, THE_LOWER_TRI_INDEX + aLastTriIndex, aVec3);
273           if (!wasSet)
274           {
275             reportError (TCollection_AsciiString ("Buffer '") + aName + "' refers to invalid indices.");
276           }
277           if (wasSet > 0)
278           {
279             aLastTriIndex++;
280           }
281         }
282         const Standard_Integer aNbDegenerate = aNbTris - aLastTriIndex;
283         if (aNbDegenerate > 0)
284         {
285           if (aNbDegenerate == aNbTris)
286           {
287             Message::SendWarning (TCollection_AsciiString("Buffer '") + aName + "' has been skipped (all elements are degenerative in)");
288             return false;
289           }
290           theSourceMesh->ChangeDegeneratedTriNb() += aNbDegenerate;
291           if (myLoadingStatistic == NULL && myToPrintDebugMessages)
292           {
293             Message::SendTrace (TCollection_AsciiString() + aNbDegenerate
294                                 + " degenerate triangles have been skipped while reading glTF triangulation '" + aName + "'");
295           }
296           if (!setNbTriangles (theDestMesh, aLastTriIndex, true))
297           {
298             return false;
299           }
300         }
301       }
302       else if (theAccessor.ComponentType == RWGltf_GltfAccessorCompType_UInt8)
303       {
304         if ((theAccessor.Count / 3) > std::numeric_limits<Standard_Integer>::max())
305         {
306           reportError (TCollection_AsciiString ("Buffer '") + aName + "' defines too big array.");
307           return false;
308         }
309
310         const Standard_Integer aNbTris = (Standard_Integer )(theAccessor.Count / 3);
311         if (!setNbTriangles (theDestMesh, aNbTris))
312         {
313           return false;
314         }
315         const size_t aStride = theAccessor.ByteStride != 0
316                              ? theAccessor.ByteStride
317                              : sizeof(uint8_t);
318         Standard_ReadBuffer aBuffer (theAccessor.Count * aStride, aStride);
319         Standard_Integer aLastTriIndex = 0;
320         for (Standard_Integer aTriIter = 0; aTriIter < aNbTris; ++aTriIter)
321         {
322           if (const uint8_t* anIndex0 = aBuffer.ReadChunk<uint8_t> (theStream))
323           {
324             aVec3.ChangeValue (1) = THE_LOWER_NODE_INDEX + (Standard_Integer )*anIndex0;
325           }
326           if (const uint8_t* anIndex1 = aBuffer.ReadChunk<uint8_t> (theStream))
327           {
328             aVec3.ChangeValue (2) = THE_LOWER_NODE_INDEX + (Standard_Integer )*anIndex1;
329           }
330           if (const uint8_t* anIndex2 = aBuffer.ReadChunk<uint8_t> (theStream))
331           {
332             aVec3.ChangeValue (3) = THE_LOWER_NODE_INDEX + (Standard_Integer )*anIndex2;
333           }
334           else
335           {
336             reportError (TCollection_AsciiString ("Buffer '") + aName + "' reading error.");
337             return false;
338           }
339
340           const Standard_Integer wasSet = setTriangle (theDestMesh, THE_LOWER_TRI_INDEX + aLastTriIndex, aVec3);
341           if (!wasSet)
342           {
343             reportError (TCollection_AsciiString ("Buffer '") + aName + "' refers to invalid indices.");
344           }
345           if (wasSet > 0)
346           {
347             aLastTriIndex++;
348           }
349         }
350         const Standard_Integer aNbDegenerate = aNbTris - aLastTriIndex;
351         if (aNbDegenerate > 0)
352         {
353           if (aNbDegenerate == aNbTris)
354           {
355             Message::SendWarning (TCollection_AsciiString("Buffer '") + aName + "' has been skipped (all elements are degenerative in)");
356             return false;
357           }
358           theSourceMesh->ChangeDegeneratedTriNb() += aNbDegenerate;
359           if (myLoadingStatistic == NULL && myToPrintDebugMessages)
360           {
361             Message::SendTrace (TCollection_AsciiString() + aNbDegenerate
362                                 + " degenerate triangles have been skipped while reading glTF triangulation '" + aName + "'");
363           }
364           if (!setNbTriangles (theDestMesh, aLastTriIndex, true))
365           {
366             return false;
367           }
368         }
369       }
370       else
371       {
372         break;
373       }
374
375       break;
376     }
377     case RWGltf_GltfArrayType_Position:
378     {
379       if (theAccessor.ComponentType != RWGltf_GltfAccessorCompType_Float32
380        || theAccessor.Type != RWGltf_GltfAccessorLayout_Vec3)
381       {
382         break;
383       }
384       else if (theAccessor.Count > std::numeric_limits<Standard_Integer>::max())
385       {
386         reportError (TCollection_AsciiString ("Buffer '") + aName + "' defines too big array.");
387         return false;
388       }
389
390       const size_t aStride = theAccessor.ByteStride != 0
391                            ? theAccessor.ByteStride
392                            : sizeof(Graphic3d_Vec3);
393       const Standard_Integer aNbNodes = (Standard_Integer )theAccessor.Count;
394       if (!setNbPositionNodes (theDestMesh, aNbNodes))
395       {
396         return false;
397       }
398
399       Standard_ReadBuffer aBuffer (theAccessor.Count * aStride - (aStride - sizeof(Graphic3d_Vec3)), aStride, true);
400       if (!myCoordSysConverter.IsEmpty())
401       {
402         for (Standard_Integer aVertIter = 0; aVertIter < aNbNodes; ++aVertIter)
403         {
404           const Graphic3d_Vec3* aVec3 = aBuffer.ReadChunk<Graphic3d_Vec3> (theStream);
405           if (aVec3 == NULL)
406           {
407             reportError (TCollection_AsciiString ("Buffer '") + aName + "' reading error.");
408             return false;
409           }
410
411           gp_Pnt anXYZ (aVec3->x(), aVec3->y(), aVec3->z());
412           myCoordSysConverter.TransformPosition (anXYZ.ChangeCoord());
413           setNodePosition (theDestMesh, THE_LOWER_NODE_INDEX + aVertIter, anXYZ);
414         }
415       }
416       else
417       {
418         for (Standard_Integer aVertIter = 0; aVertIter < aNbNodes; ++aVertIter)
419         {
420           const Graphic3d_Vec3* aVec3 = aBuffer.ReadChunk<Graphic3d_Vec3> (theStream);
421           if (aVec3 == NULL)
422           {
423             reportError (TCollection_AsciiString ("Buffer '") + aName + "' reading error.");
424             return false;
425           }
426           setNodePosition (theDestMesh, THE_LOWER_NODE_INDEX + aVertIter, gp_Pnt (aVec3->x(), aVec3->y(), aVec3->z()));
427         }
428       }
429       break;
430     }
431     case RWGltf_GltfArrayType_Normal:
432     {
433       if (theAccessor.ComponentType != RWGltf_GltfAccessorCompType_Float32
434        || theAccessor.Type != RWGltf_GltfAccessorLayout_Vec3)
435       {
436         break;
437       }
438       else if (theAccessor.Count > std::numeric_limits<Standard_Integer>::max())
439       {
440         reportError (TCollection_AsciiString ("Buffer '") + aName + "' defines too big array.");
441         return false;
442       }
443
444       const size_t aStride = theAccessor.ByteStride != 0
445                            ? theAccessor.ByteStride
446                            : sizeof(Graphic3d_Vec3);
447       const Standard_Integer aNbNodes = (Standard_Integer )theAccessor.Count;
448       if (!setNbNormalNodes (theDestMesh, aNbNodes))
449       {
450         return false;
451       }
452       Standard_ReadBuffer aBuffer (theAccessor.Count * aStride - (aStride - sizeof(Graphic3d_Vec3)), aStride, true);
453       if (!myCoordSysConverter.IsEmpty())
454       {
455         for (Standard_Integer aVertIter = 0; aVertIter < aNbNodes; ++aVertIter)
456         {
457           Graphic3d_Vec3* aVec3 = aBuffer.ReadChunk<Graphic3d_Vec3> (theStream);
458           if (aVec3 == NULL)
459           {
460             reportError (TCollection_AsciiString ("Buffer '") + aName + "' reading error.");
461             return false;
462           }
463           if (aVec3->SquareModulus() >= THE_NORMAL_PREC2)
464           {
465             myCoordSysConverter.TransformNormal (*aVec3);
466             setNodeNormal (theDestMesh, THE_LOWER_NODE_INDEX + aVertIter, *aVec3);
467           }
468           else
469           {
470             setNodeNormal (theDestMesh, THE_LOWER_NODE_INDEX + aVertIter, gp_Vec3f(0.0, 0.0, 1.0));
471           }
472         }
473       }
474       else
475       {
476         for (Standard_Integer aVertIter = 0; aVertIter < aNbNodes; ++aVertIter)
477         {
478           const Graphic3d_Vec3* aVec3 = aBuffer.ReadChunk<Graphic3d_Vec3> (theStream);
479           if (aVec3 == NULL)
480           {
481             reportError (TCollection_AsciiString ("Buffer '") + aName + "' reading error.");
482             return false;
483           }
484           if (aVec3->SquareModulus() >= THE_NORMAL_PREC2)
485           {
486             setNodeNormal (theDestMesh, THE_LOWER_NODE_INDEX + aVertIter, *aVec3);
487           }
488           else
489           {
490             setNodeNormal (theDestMesh, THE_LOWER_NODE_INDEX + aVertIter, gp_Vec3f(0.0, 0.0, 1.0));
491           }
492         }
493       }
494       break;
495     }
496     case RWGltf_GltfArrayType_TCoord0:
497     {
498       if (theAccessor.ComponentType != RWGltf_GltfAccessorCompType_Float32
499        || theAccessor.Type != RWGltf_GltfAccessorLayout_Vec2)
500       {
501         break;
502       }
503       else if (theAccessor.Count > std::numeric_limits<Standard_Integer>::max())
504       {
505         reportError (TCollection_AsciiString ("Buffer '") + aName + "' defines too big array.");
506         return false;
507       }
508
509       const size_t aStride = theAccessor.ByteStride != 0
510                            ? theAccessor.ByteStride
511                            : sizeof(Graphic3d_Vec2);
512       const Standard_Integer aNbNodes = (Standard_Integer )theAccessor.Count;
513       if (!setNbUVNodes (theDestMesh, aNbNodes))
514       {
515         return false;
516       }
517
518       Standard_ReadBuffer aBuffer (theAccessor.Count * aStride - (aStride - sizeof(Graphic3d_Vec2)), aStride, true);
519       for (int aVertIter = 0; aVertIter < aNbNodes; ++aVertIter)
520       {
521         Graphic3d_Vec2* aVec2 = aBuffer.ReadChunk<Graphic3d_Vec2> (theStream);
522         if (aVec2 == NULL)
523         {
524           reportError (TCollection_AsciiString ("Buffer '") + aName + "' reading error.");
525           return false;
526         }
527
528         // Y should be flipped (relative to image layout used by OCCT)
529         aVec2->y() = 1.0f - aVec2->y();
530         setNodeUV (theDestMesh, THE_LOWER_NODE_INDEX + aVertIter, gp_Pnt2d (aVec2->x(), aVec2->y()));
531       }
532       break;
533     }
534     case RWGltf_GltfArrayType_Color:
535     case RWGltf_GltfArrayType_TCoord1:
536     case RWGltf_GltfArrayType_Joint:
537     case RWGltf_GltfArrayType_Weight:
538     {
539       return true;
540     }
541     case RWGltf_GltfArrayType_UNKNOWN:
542     {
543       return false;
544     }
545   }
546   return true;
547 }