0030691: Data Exchange - implement import of mesh data from files in glTF format
[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 <RWMesh_CoordinateSystemConverter.hxx>
18 #include <Standard_ReadBuffer.hxx>
19
20 #include <BRep_Builder.hxx>
21 #include <Graphic3d_Vec.hxx>
22 #include <Message.hxx>
23 #include <Message_Messenger.hxx>
24 #include <TopoDS.hxx>
25 #include <TopoDS_Iterator.hxx>
26
27 namespace
28 {
29   static const Standard_Integer   THE_LOWER_TRI_INDEX  = 1;
30   static const Standard_Integer   THE_LOWER_NODE_INDEX = 1;
31   static const Standard_ShortReal THE_NORMAL_PREC2 = 0.001f;
32 }
33
34 IMPLEMENT_STANDARD_RTTIEXT(RWGltf_TriangulationReader, RWGltf_PrimitiveArrayReader)
35
36 // =======================================================================
37 // function : RWGltf_TriangulationReader
38 // purpose  :
39 // =======================================================================
40 RWGltf_TriangulationReader::RWGltf_TriangulationReader()
41 {
42   //
43 }
44
45 // =======================================================================
46 // function : reset
47 // purpose  :
48 // =======================================================================
49 void RWGltf_TriangulationReader::reset()
50 {
51   myTriangulation = new Poly_Triangulation (1, 1, true);
52   {
53     TColgp_Array1OfPnt anEmpty;
54     myTriangulation->ChangeNodes().Move (anEmpty);
55   }
56   {
57     TColgp_Array1OfPnt2d anEmpty;
58     myTriangulation->ChangeUVNodes().Move (anEmpty);
59   }
60   {
61     Poly_Array1OfTriangle anEmpty;
62     myTriangulation->ChangeTriangles().Move (anEmpty);
63   }
64 }
65
66 // =======================================================================
67 // function : result
68 // purpose  :
69 // =======================================================================
70 Handle(Poly_Triangulation) RWGltf_TriangulationReader::result()
71 {
72   if (myTriangulation->NbNodes() < 1)
73   {
74     return Handle(Poly_Triangulation)();
75   }
76   if (myTriangulation->UVNodes().Size() != myTriangulation->NbNodes())
77   {
78     myTriangulation->RemoveUVNodes();
79   }
80
81   if (myTriangulation->NbTriangles() < 1)
82   {
83     // reconstruct indexes
84     const Standard_Integer aNbTris = myTriangulation->NbNodes() / 3;
85     if (!setNbTriangles (aNbTris))
86     {
87       return Handle(Poly_Triangulation)();
88     }
89
90     for (Standard_Integer aTriIter = 0; aTriIter < aNbTris; ++aTriIter)
91     {
92       setTriangle (THE_LOWER_TRI_INDEX + aTriIter,
93                    Poly_Triangle (THE_LOWER_NODE_INDEX + aTriIter * 3 + 0,
94                                   THE_LOWER_NODE_INDEX + aTriIter * 3 + 1,
95                                   THE_LOWER_NODE_INDEX + aTriIter * 3 + 2));
96     }
97   }
98
99   return myTriangulation;
100 }
101
102 // =======================================================================
103 // function : readBuffer
104 // purpose  :
105 // =======================================================================
106 bool RWGltf_TriangulationReader::readBuffer (std::istream& theStream,
107                                              const TCollection_AsciiString& theName,
108                                              const RWGltf_GltfAccessor& theAccessor,
109                                              RWGltf_GltfArrayType theType,
110                                              RWGltf_GltfPrimitiveMode theMode)
111 {
112   if (theMode != RWGltf_GltfPrimitiveMode_Triangles)
113   {
114     Message::DefaultMessenger()->Send (TCollection_AsciiString("Buffer '") + theName + "' skipped unsupported primitive array.", Message_Warning);
115     return true;
116   }
117
118   switch (theType)
119   {
120     case RWGltf_GltfArrayType_Indices:
121     {
122       if (theAccessor.Type != RWGltf_GltfAccessorLayout_Scalar)
123       {
124         break;
125       }
126
127       Poly_Triangle aVec3;
128       if (theAccessor.ComponentType == RWGltf_GltfAccessorCompType_UInt16)
129       {
130         if ((theAccessor.Count / 3) > std::numeric_limits<Standard_Integer>::max())
131         {
132           reportError (TCollection_AsciiString ("Buffer '") + theName + "' defines too big array.");
133           return false;
134         }
135
136         const Standard_Integer aNbTris = (Standard_Integer )(theAccessor.Count / 3);
137         if (!setNbTriangles (aNbTris))
138         {
139           return false;
140         }
141         const size_t aStride = theAccessor.ByteStride != 0
142                              ? theAccessor.ByteStride
143                              : sizeof(uint16_t);
144         Standard_ReadBuffer aBuffer (theAccessor.Count * aStride, aStride);
145         for (Standard_Integer aTriIter = 0; aTriIter < aNbTris; ++aTriIter)
146         {
147           if (const uint16_t* anIndex0 = aBuffer.ReadChunk<uint16_t> (theStream))
148           {
149             aVec3.ChangeValue (1) = THE_LOWER_NODE_INDEX + *anIndex0;
150           }
151           if (const uint16_t* anIndex1 = aBuffer.ReadChunk<uint16_t> (theStream))
152           {
153             aVec3.ChangeValue (2) = THE_LOWER_NODE_INDEX + *anIndex1;
154           }
155           if (const uint16_t* anIndex2 = aBuffer.ReadChunk<uint16_t> (theStream))
156           {
157             aVec3.ChangeValue (3) = THE_LOWER_NODE_INDEX + *anIndex2;
158           }
159           else
160           {
161             reportError (TCollection_AsciiString ("Buffer '") + theName + "' reading error.");
162             return false;
163           }
164
165           if (!setTriangle (THE_LOWER_TRI_INDEX + aTriIter, aVec3))
166           {
167             reportError (TCollection_AsciiString ("Buffer '") + theName + "' refers to invalid indices.");
168           }
169         }
170       }
171       else if (theAccessor.ComponentType == RWGltf_GltfAccessorCompType_UInt32)
172       {
173         if ((theAccessor.Count / 3) > std::numeric_limits<Standard_Integer>::max())
174         {
175           reportError (TCollection_AsciiString ("Buffer '") + theName + "' defines too big array.");
176           return false;
177         }
178
179         const int aNbTris = (Standard_Integer )(theAccessor.Count / 3);
180         if (!setNbTriangles (aNbTris))
181         {
182           return false;
183         }
184         const size_t aStride = theAccessor.ByteStride != 0
185                              ? theAccessor.ByteStride
186                              : sizeof(uint32_t);
187         Standard_ReadBuffer aBuffer (theAccessor.Count * aStride, aStride);
188         for (Standard_Integer aTriIter = 0; aTriIter < aNbTris; ++aTriIter)
189         {
190           if (const uint32_t* anIndex0 = aBuffer.ReadChunk<uint32_t> (theStream))
191           {
192             aVec3.ChangeValue (1) = THE_LOWER_NODE_INDEX + *anIndex0;
193           }
194           if (const uint32_t* anIndex1 = aBuffer.ReadChunk<uint32_t> (theStream))
195           {
196             aVec3.ChangeValue (2) = THE_LOWER_NODE_INDEX + *anIndex1;
197           }
198           if (const uint32_t* anIndex2 = aBuffer.ReadChunk<uint32_t> (theStream))
199           {
200             aVec3.ChangeValue (3) = THE_LOWER_NODE_INDEX + *anIndex2;
201           }
202           else
203           {
204             reportError (TCollection_AsciiString ("Buffer '") + theName + "' reading error.");
205             return false;
206           }
207
208           if (!setTriangle (THE_LOWER_TRI_INDEX + aTriIter, aVec3))
209           {
210             reportError (TCollection_AsciiString ("Buffer '") + theName + "' refers to invalid indices.");
211           }
212         }
213       }
214       else if (theAccessor.ComponentType == RWGltf_GltfAccessorCompType_UInt8)
215       {
216         if ((theAccessor.Count / 3) > std::numeric_limits<Standard_Integer>::max())
217         {
218           reportError (TCollection_AsciiString ("Buffer '") + theName + "' defines too big array.");
219           return false;
220         }
221
222         const Standard_Integer aNbTris = (Standard_Integer )(theAccessor.Count / 3);
223         if (!setNbTriangles (aNbTris))
224         {
225           return false;
226         }
227         const size_t aStride = theAccessor.ByteStride != 0
228                              ? theAccessor.ByteStride
229                              : sizeof(uint8_t);
230         Standard_ReadBuffer aBuffer (theAccessor.Count * aStride, aStride);
231         for (Standard_Integer aTriIter = 0; aTriIter < aNbTris; ++aTriIter)
232         {
233           if (const uint8_t* anIndex0 = aBuffer.ReadChunk<uint8_t> (theStream))
234           {
235             aVec3.ChangeValue (1) = THE_LOWER_NODE_INDEX + (Standard_Integer )*anIndex0;
236           }
237           if (const uint8_t* anIndex1 = aBuffer.ReadChunk<uint8_t> (theStream))
238           {
239             aVec3.ChangeValue (2) = THE_LOWER_NODE_INDEX + (Standard_Integer )*anIndex1;
240           }
241           if (const uint8_t* anIndex2 = aBuffer.ReadChunk<uint8_t> (theStream))
242           {
243             aVec3.ChangeValue (3) = THE_LOWER_NODE_INDEX + (Standard_Integer )*anIndex2;
244           }
245           else
246           {
247             reportError (TCollection_AsciiString ("Buffer '") + theName + "' reading error.");
248             return false;
249           }
250
251           if (!setTriangle (THE_LOWER_TRI_INDEX + aTriIter, aVec3))
252           {
253             reportError (TCollection_AsciiString ("Buffer '") + theName + "' refers to invalid indices.");
254           }
255         }
256       }
257       else
258       {
259         break;
260       }
261
262       break;
263     }
264     case RWGltf_GltfArrayType_Position:
265     {
266       if (theAccessor.ComponentType != RWGltf_GltfAccessorCompType_Float32
267        || theAccessor.Type != RWGltf_GltfAccessorLayout_Vec3)
268       {
269         break;
270       }
271       else if (theAccessor.Count > std::numeric_limits<Standard_Integer>::max())
272       {
273         reportError (TCollection_AsciiString ("Buffer '") + theName + "' defines too big array.");
274         return false;
275       }
276
277       const size_t aStride = theAccessor.ByteStride != 0
278                            ? theAccessor.ByteStride
279                            : sizeof(Graphic3d_Vec3);
280       const Standard_Integer aNbNodes = (Standard_Integer )theAccessor.Count;
281       if (!setNbPositionNodes (aNbNodes))
282       {
283         return false;
284       }
285
286       Standard_ReadBuffer aBuffer (theAccessor.Count * aStride, aStride);
287       if (!myCoordSysConverter.IsEmpty())
288       {
289         for (Standard_Integer aVertIter = 0; aVertIter < aNbNodes; ++aVertIter)
290         {
291           const Graphic3d_Vec3* aVec3 = aBuffer.ReadChunk<Graphic3d_Vec3> (theStream);
292           if (aVec3 == NULL)
293           {
294             reportError (TCollection_AsciiString ("Buffer '") + theName + "' reading error.");
295             return false;
296           }
297
298           gp_Pnt anXYZ (aVec3->x(), aVec3->y(), aVec3->z());
299           myCoordSysConverter.TransformPosition (anXYZ.ChangeCoord());
300           setNodePosition (THE_LOWER_NODE_INDEX + aVertIter, anXYZ);
301         }
302       }
303       else
304       {
305         for (Standard_Integer aVertIter = 0; aVertIter < aNbNodes; ++aVertIter)
306         {
307           const Graphic3d_Vec3* aVec3 = aBuffer.ReadChunk<Graphic3d_Vec3> (theStream);
308           if (aVec3 == NULL)
309           {
310             reportError (TCollection_AsciiString ("Buffer '") + theName + "' reading error.");
311             return false;
312           }
313           setNodePosition (THE_LOWER_NODE_INDEX + aVertIter, gp_Pnt (aVec3->x(), aVec3->y(), aVec3->z()));
314         }
315       }
316       break;
317     }
318     case RWGltf_GltfArrayType_Normal:
319     {
320       if (theAccessor.ComponentType != RWGltf_GltfAccessorCompType_Float32
321        || theAccessor.Type != RWGltf_GltfAccessorLayout_Vec3)
322       {
323         break;
324       }
325       else if (theAccessor.Count > std::numeric_limits<Standard_Integer>::max())
326       {
327         reportError (TCollection_AsciiString ("Buffer '") + theName + "' defines too big array.");
328         return false;
329       }
330
331       const size_t aStride = theAccessor.ByteStride != 0
332                            ? theAccessor.ByteStride
333                            : sizeof(Graphic3d_Vec3);
334       const Standard_Integer aNbNodes = (Standard_Integer )theAccessor.Count;
335       if (!setNbNormalNodes (aNbNodes))
336       {
337         return false;
338       }
339       Standard_ReadBuffer aBuffer (theAccessor.Count * aStride, aStride);
340       if (!myCoordSysConverter.IsEmpty())
341       {
342         for (Standard_Integer aVertIter = 0; aVertIter < aNbNodes; ++aVertIter)
343         {
344           Graphic3d_Vec3* aVec3 = aBuffer.ReadChunk<Graphic3d_Vec3> (theStream);
345           if (aVec3 == NULL)
346           {
347             reportError (TCollection_AsciiString ("Buffer '") + theName + "' reading error.");
348             return false;
349           }
350           if (aVec3->SquareModulus() >= THE_NORMAL_PREC2)
351           {
352             myCoordSysConverter.TransformNormal (*aVec3);
353             setNodeNormal (THE_LOWER_NODE_INDEX + aVertIter, gp_Dir (aVec3->x(), aVec3->y(), aVec3->z()));
354           }
355           else
356           {
357             setNodeNormal (THE_LOWER_NODE_INDEX + aVertIter, gp_Dir (0.0, 0.0, 1.0));
358           }
359         }
360       }
361       else
362       {
363         for (Standard_Integer aVertIter = 0; aVertIter < aNbNodes; ++aVertIter)
364         {
365           const Graphic3d_Vec3* aVec3 = aBuffer.ReadChunk<Graphic3d_Vec3> (theStream);
366           if (aVec3 == NULL)
367           {
368             reportError (TCollection_AsciiString ("Buffer '") + theName + "' reading error.");
369             return false;
370           }
371           if (aVec3->SquareModulus() >= THE_NORMAL_PREC2)
372           {
373             setNodeNormal (THE_LOWER_NODE_INDEX + aVertIter, gp_Dir (aVec3->x(), aVec3->y(), aVec3->z()));
374           }
375           else
376           {
377             setNodeNormal (THE_LOWER_NODE_INDEX + aVertIter, gp_Dir (0.0, 0.0, 1.0));
378           }
379         }
380       }
381       break;
382     }
383     case RWGltf_GltfArrayType_TCoord0:
384     {
385       if (theAccessor.ComponentType != RWGltf_GltfAccessorCompType_Float32
386        || theAccessor.Type != RWGltf_GltfAccessorLayout_Vec2)
387       {
388         break;
389       }
390       else if (theAccessor.Count > std::numeric_limits<Standard_Integer>::max())
391       {
392         reportError (TCollection_AsciiString ("Buffer '") + theName + "' defines too big array.");
393         return false;
394       }
395
396       const size_t aStride = theAccessor.ByteStride != 0
397                            ? theAccessor.ByteStride
398                            : sizeof(Graphic3d_Vec2);
399       const Standard_Integer aNbNodes = (Standard_Integer )theAccessor.Count;
400       if (!setNbUVNodes (aNbNodes))
401       {
402         return false;
403       }
404
405       Standard_ReadBuffer aBuffer (theAccessor.Count * aStride, aStride);
406       for (int aVertIter = 0; aVertIter < aNbNodes; ++aVertIter)
407       {
408         Graphic3d_Vec2* aVec2 = aBuffer.ReadChunk<Graphic3d_Vec2> (theStream);
409         if (aVec2 == NULL)
410         {
411           reportError (TCollection_AsciiString ("Buffer '") + theName + "' reading error.");
412           return false;
413         }
414
415         // Y should be flipped (relative to image layout used by OCCT)
416         aVec2->y() = 1.0f - aVec2->y();
417         setNodeUV (THE_LOWER_NODE_INDEX + aVertIter, gp_Pnt2d (aVec2->x(), aVec2->y()));
418       }
419       break;
420     }
421     case RWGltf_GltfArrayType_Color:
422     case RWGltf_GltfArrayType_TCoord1:
423     case RWGltf_GltfArrayType_Joint:
424     case RWGltf_GltfArrayType_Weight:
425     {
426       return true;
427     }
428     case RWGltf_GltfArrayType_UNKNOWN:
429     {
430       return false;
431     }
432   }
433   return true;
434 }