1 // Author: Kirill Gavrilov
2 // Copyright (c) 2019 OPEN CASCADE SAS
4 // This file is part of Open CASCADE Technology software library.
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.
12 // Alternatively, this file may be used under the terms of Open CASCADE
13 // commercial license or contractual agreement.
15 #include <RWGltf_TriangulationReader.hxx>
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>
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;
31 IMPLEMENT_STANDARD_RTTIEXT(RWGltf_TriangulationReader, RWMesh_TriangulationReader)
33 // =======================================================================
34 // function : RWGltf_TriangulationReader
36 // =======================================================================
37 RWGltf_TriangulationReader::RWGltf_TriangulationReader()
42 // =======================================================================
43 // function : reportError
45 // =======================================================================
46 void RWGltf_TriangulationReader::reportError (const TCollection_AsciiString& theText) const
48 Message::SendFail (TCollection_AsciiString("File '") + myFileName + "' defines invalid glTF!\n" + theText);
51 // =======================================================================
54 // =======================================================================
55 bool RWGltf_TriangulationReader::load (const Handle(RWMesh_TriangulationSource)& theSourceMesh,
56 const Handle(Poly_Triangulation)& theDestMesh,
57 const Handle(OSD_FileSystem)& theFileSystem) const
59 const Handle(RWGltf_GltfLatePrimitiveArray) aSourceGltfMesh = Handle(RWGltf_GltfLatePrimitiveArray)::DownCast(theSourceMesh);
60 if (aSourceGltfMesh.IsNull()
61 || aSourceGltfMesh->PrimitiveMode() == RWGltf_GltfPrimitiveMode_UNKNOWN)
66 for (NCollection_Sequence<RWGltf_GltfPrimArrayData>::Iterator aDataIter (aSourceGltfMesh->Data()); aDataIter.More(); aDataIter.Next())
68 const RWGltf_GltfPrimArrayData& aData = aDataIter.Value();
69 if (!aData.StreamData.IsNull())
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))
80 else if (aData.StreamUri.IsEmpty())
82 reportError (TCollection_AsciiString ("Buffer '") + aSourceGltfMesh->Id() + "' does not define uri.");
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)
90 reportError (TCollection_AsciiString ("Buffer '") + aSourceGltfMesh->Id() + "refers to invalid file '" + aData.StreamUri + "'.");
93 if (!readBuffer (aSourceGltfMesh, theDestMesh, *aSharedStream.get(), aData.Accessor, aData.Type))
101 // =======================================================================
102 // function : finalizeLoading
104 // =======================================================================
105 bool RWGltf_TriangulationReader::finalizeLoading (const Handle(RWMesh_TriangulationSource)& theSourceMesh,
106 const Handle(Poly_Triangulation)& theDestMesh) const
108 if (theDestMesh->NbNodes() < 1)
112 if (theDestMesh->NbTriangles() < 1)
114 const Handle(RWGltf_GltfLatePrimitiveArray) aSourceGltfMesh = Handle(RWGltf_GltfLatePrimitiveArray)::DownCast(theSourceMesh);
115 if (!aSourceGltfMesh.IsNull() && aSourceGltfMesh->PrimitiveMode() == RWGltf_GltfPrimitiveMode_Triangles)
117 // reconstruct indexes
118 const Standard_Integer aNbTris = theDestMesh->NbNodes() / 3;
119 if (!setNbTriangles (theDestMesh, aNbTris))
123 for (Standard_Integer aTriIter = 0; aTriIter < aNbTris; ++aTriIter)
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)))
135 return RWMesh_TriangulationReader::finalizeLoading (theSourceMesh, theDestMesh);
138 // =======================================================================
139 // function : readBuffer
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
149 const TCollection_AsciiString& aName = theSourceMesh->Id();
150 if (theSourceMesh->PrimitiveMode() != RWGltf_GltfPrimitiveMode_Triangles)
152 Message::SendWarning (TCollection_AsciiString("Buffer '") + aName + "' skipped unsupported primitive array");
158 case RWGltf_GltfArrayType_Indices:
160 if (theAccessor.Type != RWGltf_GltfAccessorLayout_Scalar)
166 if (theAccessor.ComponentType == RWGltf_GltfAccessorCompType_UInt16)
168 if ((theAccessor.Count / 3) > std::numeric_limits<Standard_Integer>::max())
170 reportError (TCollection_AsciiString ("Buffer '") + aName + "' defines too big array.");
174 const Standard_Integer aNbTris = (Standard_Integer )(theAccessor.Count / 3);
175 if (!setNbTriangles (theDestMesh, aNbTris))
179 const size_t aStride = theAccessor.ByteStride != 0
180 ? theAccessor.ByteStride
182 Standard_ReadBuffer aBuffer (theAccessor.Count * aStride, aStride);
183 Standard_Integer aLastTriIndex = 0;
184 for (Standard_Integer aTriIter = 0; aTriIter < aNbTris; ++aTriIter)
186 if (const uint16_t* anIndex0 = aBuffer.ReadChunk<uint16_t> (theStream))
188 aVec3.ChangeValue (1) = THE_LOWER_NODE_INDEX + *anIndex0;
190 if (const uint16_t* anIndex1 = aBuffer.ReadChunk<uint16_t> (theStream))
192 aVec3.ChangeValue (2) = THE_LOWER_NODE_INDEX + *anIndex1;
194 if (const uint16_t* anIndex2 = aBuffer.ReadChunk<uint16_t> (theStream))
196 aVec3.ChangeValue (3) = THE_LOWER_NODE_INDEX + *anIndex2;
200 reportError (TCollection_AsciiString ("Buffer '") + aName + "' reading error.");
204 const Standard_Integer wasSet = setTriangle (theDestMesh, THE_LOWER_TRI_INDEX + aLastTriIndex, aVec3);
207 reportError (TCollection_AsciiString ("Buffer '") + aName + "' refers to invalid indices.");
214 const Standard_Integer aNbDegenerate = aNbTris - aLastTriIndex;
215 if (aNbDegenerate > 0)
217 if (aNbDegenerate == aNbTris)
219 Message::SendWarning (TCollection_AsciiString("Buffer '") + aName + "' has been skipped (all elements are degenerative in)");
222 theSourceMesh->ChangeDegeneratedTriNb() += aNbDegenerate;
223 if ((myLoadingStatistic == NULL) && myToPrintDebugMessages)
225 Message::SendTrace (TCollection_AsciiString() + aNbDegenerate
226 + " degenerate triangles have been skipped while reading glTF triangulation '" + aName + "'");
228 if (!setNbTriangles (theDestMesh, aLastTriIndex, true))
234 else if (theAccessor.ComponentType == RWGltf_GltfAccessorCompType_UInt32)
236 if ((theAccessor.Count / 3) > std::numeric_limits<Standard_Integer>::max())
238 reportError (TCollection_AsciiString ("Buffer '") + aName + "' defines too big array.");
242 const int aNbTris = (Standard_Integer )(theAccessor.Count / 3);
243 if (!setNbTriangles (theDestMesh, aNbTris))
247 const size_t aStride = theAccessor.ByteStride != 0
248 ? theAccessor.ByteStride
250 Standard_ReadBuffer aBuffer (theAccessor.Count * aStride, aStride);
251 Standard_Integer aLastTriIndex = 0;
252 for (Standard_Integer aTriIter = 0; aTriIter < aNbTris; ++aTriIter)
254 if (const uint32_t* anIndex0 = aBuffer.ReadChunk<uint32_t> (theStream))
256 aVec3.ChangeValue (1) = THE_LOWER_NODE_INDEX + *anIndex0;
258 if (const uint32_t* anIndex1 = aBuffer.ReadChunk<uint32_t> (theStream))
260 aVec3.ChangeValue (2) = THE_LOWER_NODE_INDEX + *anIndex1;
262 if (const uint32_t* anIndex2 = aBuffer.ReadChunk<uint32_t> (theStream))
264 aVec3.ChangeValue (3) = THE_LOWER_NODE_INDEX + *anIndex2;
268 reportError (TCollection_AsciiString ("Buffer '") + aName + "' reading error.");
272 const Standard_Integer wasSet = setTriangle (theDestMesh, THE_LOWER_TRI_INDEX + aLastTriIndex, aVec3);
275 reportError (TCollection_AsciiString ("Buffer '") + aName + "' refers to invalid indices.");
282 const Standard_Integer aNbDegenerate = aNbTris - aLastTriIndex;
283 if (aNbDegenerate > 0)
285 if (aNbDegenerate == aNbTris)
287 Message::SendWarning (TCollection_AsciiString("Buffer '") + aName + "' has been skipped (all elements are degenerative in)");
290 theSourceMesh->ChangeDegeneratedTriNb() += aNbDegenerate;
291 if (myLoadingStatistic == NULL && myToPrintDebugMessages)
293 Message::SendTrace (TCollection_AsciiString() + aNbDegenerate
294 + " degenerate triangles have been skipped while reading glTF triangulation '" + aName + "'");
296 if (!setNbTriangles (theDestMesh, aLastTriIndex, true))
302 else if (theAccessor.ComponentType == RWGltf_GltfAccessorCompType_UInt8)
304 if ((theAccessor.Count / 3) > std::numeric_limits<Standard_Integer>::max())
306 reportError (TCollection_AsciiString ("Buffer '") + aName + "' defines too big array.");
310 const Standard_Integer aNbTris = (Standard_Integer )(theAccessor.Count / 3);
311 if (!setNbTriangles (theDestMesh, aNbTris))
315 const size_t aStride = theAccessor.ByteStride != 0
316 ? theAccessor.ByteStride
318 Standard_ReadBuffer aBuffer (theAccessor.Count * aStride, aStride);
319 Standard_Integer aLastTriIndex = 0;
320 for (Standard_Integer aTriIter = 0; aTriIter < aNbTris; ++aTriIter)
322 if (const uint8_t* anIndex0 = aBuffer.ReadChunk<uint8_t> (theStream))
324 aVec3.ChangeValue (1) = THE_LOWER_NODE_INDEX + (Standard_Integer )*anIndex0;
326 if (const uint8_t* anIndex1 = aBuffer.ReadChunk<uint8_t> (theStream))
328 aVec3.ChangeValue (2) = THE_LOWER_NODE_INDEX + (Standard_Integer )*anIndex1;
330 if (const uint8_t* anIndex2 = aBuffer.ReadChunk<uint8_t> (theStream))
332 aVec3.ChangeValue (3) = THE_LOWER_NODE_INDEX + (Standard_Integer )*anIndex2;
336 reportError (TCollection_AsciiString ("Buffer '") + aName + "' reading error.");
340 const Standard_Integer wasSet = setTriangle (theDestMesh, THE_LOWER_TRI_INDEX + aLastTriIndex, aVec3);
343 reportError (TCollection_AsciiString ("Buffer '") + aName + "' refers to invalid indices.");
350 const Standard_Integer aNbDegenerate = aNbTris - aLastTriIndex;
351 if (aNbDegenerate > 0)
353 if (aNbDegenerate == aNbTris)
355 Message::SendWarning (TCollection_AsciiString("Buffer '") + aName + "' has been skipped (all elements are degenerative in)");
358 theSourceMesh->ChangeDegeneratedTriNb() += aNbDegenerate;
359 if (myLoadingStatistic == NULL && myToPrintDebugMessages)
361 Message::SendTrace (TCollection_AsciiString() + aNbDegenerate
362 + " degenerate triangles have been skipped while reading glTF triangulation '" + aName + "'");
364 if (!setNbTriangles (theDestMesh, aLastTriIndex, true))
377 case RWGltf_GltfArrayType_Position:
379 if (theAccessor.ComponentType != RWGltf_GltfAccessorCompType_Float32
380 || theAccessor.Type != RWGltf_GltfAccessorLayout_Vec3)
384 else if (theAccessor.Count > std::numeric_limits<Standard_Integer>::max())
386 reportError (TCollection_AsciiString ("Buffer '") + aName + "' defines too big array.");
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))
399 Standard_ReadBuffer aBuffer (theAccessor.Count * aStride - (aStride - sizeof(Graphic3d_Vec3)), aStride, true);
400 if (!myCoordSysConverter.IsEmpty())
402 for (Standard_Integer aVertIter = 0; aVertIter < aNbNodes; ++aVertIter)
404 const Graphic3d_Vec3* aVec3 = aBuffer.ReadChunk<Graphic3d_Vec3> (theStream);
407 reportError (TCollection_AsciiString ("Buffer '") + aName + "' reading error.");
411 gp_Pnt anXYZ (aVec3->x(), aVec3->y(), aVec3->z());
412 myCoordSysConverter.TransformPosition (anXYZ.ChangeCoord());
413 setNodePosition (theDestMesh, THE_LOWER_NODE_INDEX + aVertIter, anXYZ);
418 for (Standard_Integer aVertIter = 0; aVertIter < aNbNodes; ++aVertIter)
420 const Graphic3d_Vec3* aVec3 = aBuffer.ReadChunk<Graphic3d_Vec3> (theStream);
423 reportError (TCollection_AsciiString ("Buffer '") + aName + "' reading error.");
426 setNodePosition (theDestMesh, THE_LOWER_NODE_INDEX + aVertIter, gp_Pnt (aVec3->x(), aVec3->y(), aVec3->z()));
431 case RWGltf_GltfArrayType_Normal:
433 if (theAccessor.ComponentType != RWGltf_GltfAccessorCompType_Float32
434 || theAccessor.Type != RWGltf_GltfAccessorLayout_Vec3)
438 else if (theAccessor.Count > std::numeric_limits<Standard_Integer>::max())
440 reportError (TCollection_AsciiString ("Buffer '") + aName + "' defines too big array.");
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))
452 Standard_ReadBuffer aBuffer (theAccessor.Count * aStride - (aStride - sizeof(Graphic3d_Vec3)), aStride, true);
453 if (!myCoordSysConverter.IsEmpty())
455 for (Standard_Integer aVertIter = 0; aVertIter < aNbNodes; ++aVertIter)
457 Graphic3d_Vec3* aVec3 = aBuffer.ReadChunk<Graphic3d_Vec3> (theStream);
460 reportError (TCollection_AsciiString ("Buffer '") + aName + "' reading error.");
463 if (aVec3->SquareModulus() >= THE_NORMAL_PREC2)
465 myCoordSysConverter.TransformNormal (*aVec3);
466 setNodeNormal (theDestMesh, THE_LOWER_NODE_INDEX + aVertIter, *aVec3);
470 setNodeNormal (theDestMesh, THE_LOWER_NODE_INDEX + aVertIter, gp_Vec3f(0.0, 0.0, 1.0));
476 for (Standard_Integer aVertIter = 0; aVertIter < aNbNodes; ++aVertIter)
478 const Graphic3d_Vec3* aVec3 = aBuffer.ReadChunk<Graphic3d_Vec3> (theStream);
481 reportError (TCollection_AsciiString ("Buffer '") + aName + "' reading error.");
484 if (aVec3->SquareModulus() >= THE_NORMAL_PREC2)
486 setNodeNormal (theDestMesh, THE_LOWER_NODE_INDEX + aVertIter, *aVec3);
490 setNodeNormal (theDestMesh, THE_LOWER_NODE_INDEX + aVertIter, gp_Vec3f(0.0, 0.0, 1.0));
496 case RWGltf_GltfArrayType_TCoord0:
498 if (theAccessor.ComponentType != RWGltf_GltfAccessorCompType_Float32
499 || theAccessor.Type != RWGltf_GltfAccessorLayout_Vec2)
503 else if (theAccessor.Count > std::numeric_limits<Standard_Integer>::max())
505 reportError (TCollection_AsciiString ("Buffer '") + aName + "' defines too big array.");
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))
518 Standard_ReadBuffer aBuffer (theAccessor.Count * aStride - (aStride - sizeof(Graphic3d_Vec2)), aStride, true);
519 for (int aVertIter = 0; aVertIter < aNbNodes; ++aVertIter)
521 Graphic3d_Vec2* aVec2 = aBuffer.ReadChunk<Graphic3d_Vec2> (theStream);
524 reportError (TCollection_AsciiString ("Buffer '") + aName + "' reading error.");
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()));
534 case RWGltf_GltfArrayType_Color:
535 case RWGltf_GltfArrayType_TCoord1:
536 case RWGltf_GltfArrayType_Joint:
537 case RWGltf_GltfArrayType_Weight:
541 case RWGltf_GltfArrayType_UNKNOWN: