// Author: Kirill Gavrilov // Copyright (c) 2019 OPEN CASCADE SAS // // This file is part of Open CASCADE Technology software library. // // This library is free software; you can redistribute it and/or modify it under // the terms of the GNU Lesser General Public License version 2.1 as published // by the Free Software Foundation, with special exception defined in the file // OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT // distribution for complete text of the license and disclaimer of any warranty. // // Alternatively, this file may be used under the terms of Open CASCADE // commercial license or contractual agreement. #include #include #include #include #include #include #include #ifdef HAVE_DRACO #include #include #include #endif namespace { static const Standard_Integer THE_LOWER_TRI_INDEX = 1; static const Standard_Integer THE_LOWER_NODE_INDEX = 1; static const Standard_ShortReal THE_NORMAL_PREC2 = 0.001f; #ifdef HAVE_DRACO //! Return array type from Draco attribute type. static RWGltf_GltfArrayType arrayTypeFromDraco(draco::GeometryAttribute::Type theType) { switch (theType) { case draco::GeometryAttribute::POSITION: return RWGltf_GltfArrayType_Position; case draco::GeometryAttribute::NORMAL: return RWGltf_GltfArrayType_Normal; case draco::GeometryAttribute::COLOR: return RWGltf_GltfArrayType_Color; case draco::GeometryAttribute::TEX_COORD: return RWGltf_GltfArrayType_TCoord0; default: return RWGltf_GltfArrayType_UNKNOWN; } } //! Return layout from Draco number of components. static RWGltf_GltfAccessorLayout layoutFromDraco(int8_t theNbComps) { switch (theNbComps) { case 1: return RWGltf_GltfAccessorLayout_Scalar; case 2: return RWGltf_GltfAccessorLayout_Vec2; case 3: return RWGltf_GltfAccessorLayout_Vec3; case 4: return RWGltf_GltfAccessorLayout_Vec4; } return RWGltf_GltfAccessorLayout_UNKNOWN; } //! Return component type from Draco data type. static RWGltf_GltfAccessorCompType compTypeFromDraco(draco::DataType theType) { switch (theType) { case draco::DT_INT8: return RWGltf_GltfAccessorCompType_Int8; case draco::DT_UINT8: return RWGltf_GltfAccessorCompType_UInt8; case draco::DT_INT16: return RWGltf_GltfAccessorCompType_Int16; case draco::DT_UINT16: return RWGltf_GltfAccessorCompType_UInt16; case draco::DT_INT32: case draco::DT_UINT32: return RWGltf_GltfAccessorCompType_UInt32; // case draco::DT_INT64: // case draco::DT_UINT64: case draco::DT_FLOAT32: return RWGltf_GltfAccessorCompType_Float32; // case draco::DT_FLOAT64: // case draco::DT_BOOL: default: return RWGltf_GltfAccessorCompType_UNKNOWN; } } #endif } // namespace IMPLEMENT_STANDARD_RTTIEXT(RWGltf_TriangulationReader, RWMesh_TriangulationReader) // ======================================================================= // function : RWGltf_TriangulationReader // purpose : // ======================================================================= RWGltf_TriangulationReader::RWGltf_TriangulationReader() { // } // ======================================================================= // function : reportError // purpose : // ======================================================================= void RWGltf_TriangulationReader::reportError(const TCollection_AsciiString& theText) const { Message::SendFail(TCollection_AsciiString("File '") + myFileName + "' defines invalid glTF!\n" + theText); } // ======================================================================= // function : LoadStreamData // purpose : // ======================================================================= bool RWGltf_TriangulationReader::LoadStreamData( const Handle(RWMesh_TriangulationSource)& theSourceMesh, const Handle(Poly_Triangulation)& theDestMesh) const { Standard_ASSERT_RETURN(!theDestMesh.IsNull(), "The destination mesh should be initialized before loading data to it", false); theDestMesh->Clear(); theDestMesh->SetDoublePrecision(myIsDoublePrecision); if (!loadStreamData(theSourceMesh, theDestMesh)) { theDestMesh->Clear(); return false; } if (!finalizeLoading(theSourceMesh, theDestMesh)) { theDestMesh->Clear(); return false; } return true; } // ======================================================================= // function : readStreamData // purpose : // ======================================================================= bool RWGltf_TriangulationReader::readStreamData( const Handle(RWGltf_GltfLatePrimitiveArray)& theSourceGltfMesh, const RWGltf_GltfPrimArrayData& theGltfData, const Handle(Poly_Triangulation)& theDestMesh) const { Standard_ArrayStreamBuffer aStreamBuffer((const char*)theGltfData.StreamData->Data(), theGltfData.StreamData->Size()); std::istream aStream(&aStreamBuffer); aStream.seekg((std::streamoff)theGltfData.StreamOffset, std::ios_base::beg); if (!readBuffer(theSourceGltfMesh, theDestMesh, aStream, theGltfData.Accessor, theGltfData.Type)) { return false; } return true; } // ======================================================================= // function : readFileData // purpose : // ======================================================================= bool RWGltf_TriangulationReader::readFileData( const Handle(RWGltf_GltfLatePrimitiveArray)& theSourceGltfMesh, const RWGltf_GltfPrimArrayData& theGltfData, const Handle(Poly_Triangulation)& theDestMesh, const Handle(OSD_FileSystem)& theFileSystem) const { const Handle(OSD_FileSystem)& aFileSystem = !theFileSystem.IsNull() ? theFileSystem : OSD_FileSystem::DefaultFileSystem(); std::shared_ptr aSharedStream = aFileSystem->OpenIStream(theGltfData.StreamUri, std::ios::in | std::ios::binary, theGltfData.StreamOffset); if (aSharedStream.get() == NULL) { reportError(TCollection_AsciiString("Buffer '") + theSourceGltfMesh->Id() + "' refers to invalid file '" + theGltfData.StreamUri + "'."); return false; } if (!readBuffer(theSourceGltfMesh, theDestMesh, *aSharedStream.get(), theGltfData.Accessor, theGltfData.Type)) { return false; } return true; } // ======================================================================= // function : loadStreamData // purpose : // ======================================================================= bool RWGltf_TriangulationReader::loadStreamData( const Handle(RWMesh_TriangulationSource)& theSourceMesh, const Handle(Poly_Triangulation)& theDestMesh, bool theToResetStream) const { const Handle(RWGltf_GltfLatePrimitiveArray) aSourceGltfMesh = Handle(RWGltf_GltfLatePrimitiveArray)::DownCast(theSourceMesh); if (aSourceGltfMesh.IsNull() || aSourceGltfMesh->PrimitiveMode() == RWGltf_GltfPrimitiveMode_UNKNOWN) { return false; } bool wasLoaded = false; for (NCollection_Sequence::Iterator aDataIter(aSourceGltfMesh->Data()); aDataIter.More(); aDataIter.Next()) { RWGltf_GltfPrimArrayData& aData = aDataIter.ChangeValue(); if (aData.StreamData.IsNull()) { continue; } if (!readStreamData(aSourceGltfMesh, aData, theDestMesh)) { return false; } if (theToResetStream) { aData.StreamData.Nullify(); } wasLoaded = true; } return wasLoaded; } // ======================================================================= // function : readDracoBuffer // purpose : // ======================================================================= bool RWGltf_TriangulationReader::readDracoBuffer( const Handle(RWGltf_GltfLatePrimitiveArray)& theSourceGltfMesh, const RWGltf_GltfPrimArrayData& theGltfData, const Handle(Poly_Triangulation)& theDestMesh, const Handle(OSD_FileSystem)& theFileSystem) const { const TCollection_AsciiString& aName = theSourceGltfMesh->Id(); const Handle(OSD_FileSystem)& aFileSystem = !theFileSystem.IsNull() ? theFileSystem : OSD_FileSystem::DefaultFileSystem(); std::shared_ptr aSharedStream = aFileSystem->OpenIStream(theGltfData.StreamUri, std::ios::in | std::ios::binary, theGltfData.StreamOffset); if (aSharedStream.get() == NULL) { reportError(TCollection_AsciiString("Buffer '") + aName + "' refers to invalid file '" + theGltfData.StreamUri + "'."); return false; } #ifdef HAVE_DRACO std::vector aReadData; aReadData.resize((size_t)theGltfData.StreamLength); aSharedStream->read(aReadData.data(), (std::streamsize)theGltfData.StreamLength); if (!aSharedStream->good()) { reportError(TCollection_AsciiString("Buffer '") + aName + "' refers to file that cannot be read '" + theGltfData.StreamUri + "'."); return false; } draco::DecoderBuffer aDracoBuf; aDracoBuf.Init(aReadData.data(), aReadData.size()); draco::Decoder aDracoDecoder; draco::StatusOr> aDracoStat = aDracoDecoder.DecodeMeshFromBuffer(&aDracoBuf); if (!aDracoStat.ok() || aDracoStat.value().get() == NULL) { reportError(TCollection_AsciiString("Buffer '") + aName + "' refers to Draco data that cannot be decoded '" + theGltfData.StreamUri + "'."); return false; } const Standard_Integer aNbNodes = (Standard_Integer)aDracoStat.value()->num_points(); const Standard_Integer aNbTris = (Standard_Integer)aDracoStat.value()->num_faces(); if (aNbNodes < 0) { reportError(TCollection_AsciiString("Buffer '") + aName + "' defines an empty array."); return false; } if ((int64_t)aDracoStat.value()->num_points() > std::numeric_limits::max()) { reportError(TCollection_AsciiString("Buffer '") + aName + "' defines too big array."); return false; } if (!setNbPositionNodes(theDestMesh, aNbNodes)) { return false; } if (aNbTris > 0 && !setNbTriangles(theDestMesh, aNbTris)) { return false; } // copy vertex attributes for (int32_t anAttrIter = 0; anAttrIter < aDracoStat.value()->num_attributes(); ++anAttrIter) { const draco::PointAttribute* anAttrib = aDracoStat.value()->attribute(anAttrIter); const RWGltf_GltfArrayType aWrapType = arrayTypeFromDraco(anAttrib->attribute_type()); const RWGltf_GltfAccessorLayout aWrapLayout = layoutFromDraco(anAttrib->num_components()); const RWGltf_GltfAccessorCompType aWrapCompType = compTypeFromDraco(anAttrib->data_type()); switch (aWrapType) { case RWGltf_GltfArrayType_Position: { if (aWrapCompType != RWGltf_GltfAccessorCompType_Float32 || aWrapLayout != RWGltf_GltfAccessorLayout_Vec3) { reportError(TCollection_AsciiString("Buffer '") + aName + "' has unsupported position data type."); return false; } for (Standard_Integer aVertIter = 0; aVertIter < aNbNodes; ++aVertIter) { const Graphic3d_Vec3* aVec3 = reinterpret_cast( anAttrib->GetAddressOfMappedIndex(draco::PointIndex(aVertIter))); if (aVec3 == NULL) { reportError(TCollection_AsciiString("Buffer '") + aName + "' reading error."); return false; } gp_Pnt anXYZ(aVec3->x(), aVec3->y(), aVec3->z()); myCoordSysConverter.TransformPosition(anXYZ.ChangeCoord()); setNodePosition(theDestMesh, THE_LOWER_NODE_INDEX + aVertIter, anXYZ); } break; } case RWGltf_GltfArrayType_Normal: { if (aWrapCompType != RWGltf_GltfAccessorCompType_Float32 || aWrapLayout != RWGltf_GltfAccessorLayout_Vec3) { Message::SendTrace(TCollection_AsciiString() + "Vertex normals in unsupported format have been skipped while " "reading glTF triangulation '" + aName + "'"); break; } if (!setNbNormalNodes(theDestMesh, aNbNodes)) { return false; } for (Standard_Integer aVertIter = 0; aVertIter < aNbNodes; ++aVertIter) { const Graphic3d_Vec3* aVec3 = reinterpret_cast( anAttrib->GetAddressOfMappedIndex(draco::PointIndex(aVertIter))); if (aVec3 == NULL) { reportError(TCollection_AsciiString("Buffer '") + aName + "' reading error."); return false; } if (aVec3->SquareModulus() >= THE_NORMAL_PREC2) { Graphic3d_Vec3 aVec3Copy = *aVec3; myCoordSysConverter.TransformNormal(aVec3Copy); setNodeNormal(theDestMesh, THE_LOWER_NODE_INDEX + aVertIter, aVec3Copy); } else { setNodeNormal(theDestMesh, THE_LOWER_NODE_INDEX + aVertIter, gp_Vec3f(0.0, 0.0, 1.0)); } } break; } case RWGltf_GltfArrayType_TCoord0: { if (aWrapCompType != RWGltf_GltfAccessorCompType_Float32 || aWrapLayout != RWGltf_GltfAccessorLayout_Vec2) { Message::SendTrace(TCollection_AsciiString() + "Vertex UV coordinates in unsupported format have been skipped " "while reading glTF triangulation '" + aName + "'"); break; } if (!setNbUVNodes(theDestMesh, aNbNodes)) { return false; } for (int aVertIter = 0; aVertIter < aNbNodes; ++aVertIter) { const Graphic3d_Vec2* aVec2 = reinterpret_cast( anAttrib->GetAddressOfMappedIndex(draco::PointIndex(aVertIter))); if (aVec2 == NULL) { reportError(TCollection_AsciiString("Buffer '") + aName + "' reading error."); return false; } // Y should be flipped (relative to image layout used by OCCT) float aTexY = 1.0f - aVec2->y(); setNodeUV(theDestMesh, THE_LOWER_NODE_INDEX + aVertIter, gp_Pnt2d(aVec2->x(), aTexY)); } break; } default: { break; } } } // copy triangles Standard_Integer aLastTriIndex = 0; for (Standard_Integer aFaceIter = 0; aFaceIter < aNbTris; ++aFaceIter) { const draco::Mesh::Face& aFace = aDracoStat.value()->face(draco::FaceIndex(aFaceIter)); Poly_Triangle aVec3; aVec3.ChangeValue(1) = THE_LOWER_NODE_INDEX + aFace[0].value(); aVec3.ChangeValue(2) = THE_LOWER_NODE_INDEX + aFace[1].value(); aVec3.ChangeValue(3) = THE_LOWER_NODE_INDEX + aFace[2].value(); const Standard_Integer wasSet = setTriangle(theDestMesh, THE_LOWER_TRI_INDEX + aLastTriIndex, aVec3); if (!wasSet) { reportError(TCollection_AsciiString("Buffer '") + aName + "' refers to invalid indices."); } if (wasSet > 0) { ++aLastTriIndex; } } const Standard_Integer aNbDegenerate = aNbTris - aLastTriIndex; if (aNbDegenerate > 0) { if (aNbDegenerate == aNbTris) { Message::SendWarning(TCollection_AsciiString("Buffer '") + aName + "' has been skipped (all elements are degenerative in)"); return false; } theSourceGltfMesh->ChangeDegeneratedTriNb() += aNbDegenerate; if (myLoadingStatistic == NULL && myToPrintDebugMessages) { Message::SendTrace( TCollection_AsciiString() + aNbDegenerate + " degenerate triangles have been skipped while reading glTF triangulation '" + aName + "'"); } if (!setNbTriangles(theDestMesh, aLastTriIndex, true)) { return false; } } return true; #else (void)theDestMesh; reportError(TCollection_AsciiString("Buffer '") + aName + "' refers to unsupported compressed data."); return false; #endif } // ======================================================================= // function : load // purpose : // ======================================================================= bool RWGltf_TriangulationReader::load(const Handle(RWMesh_TriangulationSource)& theSourceMesh, const Handle(Poly_Triangulation)& theDestMesh, const Handle(OSD_FileSystem)& theFileSystem) const { const Handle(RWGltf_GltfLatePrimitiveArray) aSourceGltfMesh = Handle(RWGltf_GltfLatePrimitiveArray)::DownCast(theSourceMesh); if (aSourceGltfMesh.IsNull() || aSourceGltfMesh->PrimitiveMode() == RWGltf_GltfPrimitiveMode_UNKNOWN) { return false; } bool hasCompressed = false; for (NCollection_Sequence::Iterator aDataIter(aSourceGltfMesh->Data()); aDataIter.More(); aDataIter.Next()) { const RWGltf_GltfPrimArrayData& aData = aDataIter.Value(); const TCollection_AsciiString& aName = aSourceGltfMesh->Id(); if (!aData.StreamData.IsNull()) { Message::SendWarning( TCollection_AsciiString("Buffer '") + aName + "' contains stream data that cannot be loaded during deferred data loading."); continue; } else if (aData.StreamUri.IsEmpty()) { reportError(TCollection_AsciiString("Buffer '") + aName + "' does not define uri."); return false; } if (aData.Accessor.IsCompressed) { if (hasCompressed) { // already decoded (compressed stream defines all attributes at once) continue; } if (!readDracoBuffer(aSourceGltfMesh, aData, theDestMesh, theFileSystem)) { return false; } // keep decoding - there are might be uncompressed attributes in addition to compressed hasCompressed = true; } else if (!readFileData(aSourceGltfMesh, aData, theDestMesh, theFileSystem)) { return false; } } return true; } // ======================================================================= // function : finalizeLoading // purpose : // ======================================================================= bool RWGltf_TriangulationReader::finalizeLoading( const Handle(RWMesh_TriangulationSource)& theSourceMesh, const Handle(Poly_Triangulation)& theDestMesh) const { if (theDestMesh->NbNodes() < 1) { return false; } if (theDestMesh->NbTriangles() < 1) { const Handle(RWGltf_GltfLatePrimitiveArray) aSourceGltfMesh = Handle(RWGltf_GltfLatePrimitiveArray)::DownCast(theSourceMesh); if (!aSourceGltfMesh.IsNull() && aSourceGltfMesh->PrimitiveMode() == RWGltf_GltfPrimitiveMode_Triangles) { // reconstruct indexes const Standard_Integer aNbTris = theDestMesh->NbNodes() / 3; if (!setNbTriangles(theDestMesh, aNbTris)) { return false; } for (Standard_Integer aTriIter = 0; aTriIter < aNbTris; ++aTriIter) { if (!setTriangle(theDestMesh, THE_LOWER_TRI_INDEX + aTriIter, Poly_Triangle(THE_LOWER_NODE_INDEX + aTriIter * 3 + 0, THE_LOWER_NODE_INDEX + aTriIter * 3 + 1, THE_LOWER_NODE_INDEX + aTriIter * 3 + 2))) { return false; } } } } return RWMesh_TriangulationReader::finalizeLoading(theSourceMesh, theDestMesh); } // ======================================================================= // function : readBuffer // purpose : // ======================================================================= bool RWGltf_TriangulationReader::readBuffer( const Handle(RWGltf_GltfLatePrimitiveArray)& theSourceMesh, const Handle(Poly_Triangulation)& theDestMesh, std::istream& theStream, const RWGltf_GltfAccessor& theAccessor, RWGltf_GltfArrayType theType) const { const TCollection_AsciiString& aName = theSourceMesh->Id(); if (theSourceMesh->PrimitiveMode() != RWGltf_GltfPrimitiveMode_Triangles) { Message::SendWarning(TCollection_AsciiString("Buffer '") + aName + "' skipped unsupported primitive array"); return true; } switch (theType) { case RWGltf_GltfArrayType_Indices: { if (theAccessor.Type != RWGltf_GltfAccessorLayout_Scalar) { break; } Poly_Triangle aVec3; if (theAccessor.ComponentType == RWGltf_GltfAccessorCompType_UInt16) { if ((theAccessor.Count / 3) > std::numeric_limits::max()) { reportError(TCollection_AsciiString("Buffer '") + aName + "' defines too big array."); return false; } const Standard_Integer aNbTris = (Standard_Integer)(theAccessor.Count / 3); if (!setNbTriangles(theDestMesh, aNbTris)) { return false; } const size_t aStride = theAccessor.ByteStride != 0 ? theAccessor.ByteStride : sizeof(uint16_t); Standard_ReadBuffer aBuffer(theAccessor.Count * aStride, aStride); Standard_Integer aLastTriIndex = 0; for (Standard_Integer aTriIter = 0; aTriIter < aNbTris; ++aTriIter) { if (const uint16_t* anIndex0 = aBuffer.ReadChunk(theStream)) { aVec3.ChangeValue(1) = THE_LOWER_NODE_INDEX + *anIndex0; } if (const uint16_t* anIndex1 = aBuffer.ReadChunk(theStream)) { aVec3.ChangeValue(2) = THE_LOWER_NODE_INDEX + *anIndex1; } if (const uint16_t* anIndex2 = aBuffer.ReadChunk(theStream)) { aVec3.ChangeValue(3) = THE_LOWER_NODE_INDEX + *anIndex2; } else { reportError(TCollection_AsciiString("Buffer '") + aName + "' reading error."); return false; } const Standard_Integer wasSet = setTriangle(theDestMesh, THE_LOWER_TRI_INDEX + aLastTriIndex, aVec3); if (!wasSet) { reportError(TCollection_AsciiString("Buffer '") + aName + "' refers to invalid indices."); } if (wasSet > 0) { aLastTriIndex++; } } const Standard_Integer aNbDegenerate = aNbTris - aLastTriIndex; if (aNbDegenerate > 0) { if (aNbDegenerate == aNbTris) { Message::SendWarning(TCollection_AsciiString("Buffer '") + aName + "' has been skipped (all elements are degenerative in)"); return false; } theSourceMesh->ChangeDegeneratedTriNb() += aNbDegenerate; if ((myLoadingStatistic == NULL) && myToPrintDebugMessages) { Message::SendTrace( TCollection_AsciiString() + aNbDegenerate + " degenerate triangles have been skipped while reading glTF triangulation '" + aName + "'"); } if (!setNbTriangles(theDestMesh, aLastTriIndex, true)) { return false; } } } else if (theAccessor.ComponentType == RWGltf_GltfAccessorCompType_UInt32) { if ((theAccessor.Count / 3) > std::numeric_limits::max()) { reportError(TCollection_AsciiString("Buffer '") + aName + "' defines too big array."); return false; } const int aNbTris = (Standard_Integer)(theAccessor.Count / 3); if (!setNbTriangles(theDestMesh, aNbTris)) { return false; } const size_t aStride = theAccessor.ByteStride != 0 ? theAccessor.ByteStride : sizeof(uint32_t); Standard_ReadBuffer aBuffer(theAccessor.Count * aStride, aStride); Standard_Integer aLastTriIndex = 0; for (Standard_Integer aTriIter = 0; aTriIter < aNbTris; ++aTriIter) { if (const uint32_t* anIndex0 = aBuffer.ReadChunk(theStream)) { aVec3.ChangeValue(1) = THE_LOWER_NODE_INDEX + *anIndex0; } if (const uint32_t* anIndex1 = aBuffer.ReadChunk(theStream)) { aVec3.ChangeValue(2) = THE_LOWER_NODE_INDEX + *anIndex1; } if (const uint32_t* anIndex2 = aBuffer.ReadChunk(theStream)) { aVec3.ChangeValue(3) = THE_LOWER_NODE_INDEX + *anIndex2; } else { reportError(TCollection_AsciiString("Buffer '") + aName + "' reading error."); return false; } const Standard_Integer wasSet = setTriangle(theDestMesh, THE_LOWER_TRI_INDEX + aLastTriIndex, aVec3); if (!wasSet) { reportError(TCollection_AsciiString("Buffer '") + aName + "' refers to invalid indices."); } if (wasSet > 0) { aLastTriIndex++; } } const Standard_Integer aNbDegenerate = aNbTris - aLastTriIndex; if (aNbDegenerate > 0) { if (aNbDegenerate == aNbTris) { Message::SendWarning(TCollection_AsciiString("Buffer '") + aName + "' has been skipped (all elements are degenerative in)"); return false; } theSourceMesh->ChangeDegeneratedTriNb() += aNbDegenerate; if (myLoadingStatistic == NULL && myToPrintDebugMessages) { Message::SendTrace( TCollection_AsciiString() + aNbDegenerate + " degenerate triangles have been skipped while reading glTF triangulation '" + aName + "'"); } if (!setNbTriangles(theDestMesh, aLastTriIndex, true)) { return false; } } } else if (theAccessor.ComponentType == RWGltf_GltfAccessorCompType_UInt8) { if ((theAccessor.Count / 3) > std::numeric_limits::max()) { reportError(TCollection_AsciiString("Buffer '") + aName + "' defines too big array."); return false; } const Standard_Integer aNbTris = (Standard_Integer)(theAccessor.Count / 3); if (!setNbTriangles(theDestMesh, aNbTris)) { return false; } const size_t aStride = theAccessor.ByteStride != 0 ? theAccessor.ByteStride : sizeof(uint8_t); Standard_ReadBuffer aBuffer(theAccessor.Count * aStride, aStride); Standard_Integer aLastTriIndex = 0; for (Standard_Integer aTriIter = 0; aTriIter < aNbTris; ++aTriIter) { if (const uint8_t* anIndex0 = aBuffer.ReadChunk(theStream)) { aVec3.ChangeValue(1) = THE_LOWER_NODE_INDEX + (Standard_Integer)*anIndex0; } if (const uint8_t* anIndex1 = aBuffer.ReadChunk(theStream)) { aVec3.ChangeValue(2) = THE_LOWER_NODE_INDEX + (Standard_Integer)*anIndex1; } if (const uint8_t* anIndex2 = aBuffer.ReadChunk(theStream)) { aVec3.ChangeValue(3) = THE_LOWER_NODE_INDEX + (Standard_Integer)*anIndex2; } else { reportError(TCollection_AsciiString("Buffer '") + aName + "' reading error."); return false; } const Standard_Integer wasSet = setTriangle(theDestMesh, THE_LOWER_TRI_INDEX + aLastTriIndex, aVec3); if (!wasSet) { reportError(TCollection_AsciiString("Buffer '") + aName + "' refers to invalid indices."); } if (wasSet > 0) { aLastTriIndex++; } } const Standard_Integer aNbDegenerate = aNbTris - aLastTriIndex; if (aNbDegenerate > 0) { if (aNbDegenerate == aNbTris) { Message::SendWarning(TCollection_AsciiString("Buffer '") + aName + "' has been skipped (all elements are degenerative in)"); return false; } theSourceMesh->ChangeDegeneratedTriNb() += aNbDegenerate; if (myLoadingStatistic == NULL && myToPrintDebugMessages) { Message::SendTrace( TCollection_AsciiString() + aNbDegenerate + " degenerate triangles have been skipped while reading glTF triangulation '" + aName + "'"); } if (!setNbTriangles(theDestMesh, aLastTriIndex, true)) { return false; } } } else { break; } break; } case RWGltf_GltfArrayType_Position: { if (theAccessor.ComponentType != RWGltf_GltfAccessorCompType_Float32 || theAccessor.Type != RWGltf_GltfAccessorLayout_Vec3) { break; } else if (theAccessor.Count > std::numeric_limits::max()) { reportError(TCollection_AsciiString("Buffer '") + aName + "' defines too big array."); return false; } const size_t aStride = theAccessor.ByteStride != 0 ? theAccessor.ByteStride : sizeof(Graphic3d_Vec3); const Standard_Integer aNbNodes = (Standard_Integer)theAccessor.Count; if (!setNbPositionNodes(theDestMesh, aNbNodes)) { return false; } Standard_ReadBuffer aBuffer(theAccessor.Count * aStride - (aStride - sizeof(Graphic3d_Vec3)), aStride, true); if (!myCoordSysConverter.IsEmpty()) { for (Standard_Integer aVertIter = 0; aVertIter < aNbNodes; ++aVertIter) { const Graphic3d_Vec3* aVec3 = aBuffer.ReadChunk(theStream); if (aVec3 == NULL) { reportError(TCollection_AsciiString("Buffer '") + aName + "' reading error."); return false; } gp_Pnt anXYZ(aVec3->x(), aVec3->y(), aVec3->z()); myCoordSysConverter.TransformPosition(anXYZ.ChangeCoord()); setNodePosition(theDestMesh, THE_LOWER_NODE_INDEX + aVertIter, anXYZ); } } else { for (Standard_Integer aVertIter = 0; aVertIter < aNbNodes; ++aVertIter) { const Graphic3d_Vec3* aVec3 = aBuffer.ReadChunk(theStream); if (aVec3 == NULL) { reportError(TCollection_AsciiString("Buffer '") + aName + "' reading error."); return false; } setNodePosition(theDestMesh, THE_LOWER_NODE_INDEX + aVertIter, gp_Pnt(aVec3->x(), aVec3->y(), aVec3->z())); } } break; } case RWGltf_GltfArrayType_Normal: { if (theAccessor.ComponentType != RWGltf_GltfAccessorCompType_Float32 || theAccessor.Type != RWGltf_GltfAccessorLayout_Vec3) { break; } else if (theAccessor.Count > std::numeric_limits::max()) { reportError(TCollection_AsciiString("Buffer '") + aName + "' defines too big array."); return false; } const size_t aStride = theAccessor.ByteStride != 0 ? theAccessor.ByteStride : sizeof(Graphic3d_Vec3); const Standard_Integer aNbNodes = (Standard_Integer)theAccessor.Count; if (!setNbNormalNodes(theDestMesh, aNbNodes)) { return false; } Standard_ReadBuffer aBuffer(theAccessor.Count * aStride - (aStride - sizeof(Graphic3d_Vec3)), aStride, true); if (!myCoordSysConverter.IsEmpty()) { for (Standard_Integer aVertIter = 0; aVertIter < aNbNodes; ++aVertIter) { Graphic3d_Vec3* aVec3 = aBuffer.ReadChunk(theStream); if (aVec3 == NULL) { reportError(TCollection_AsciiString("Buffer '") + aName + "' reading error."); return false; } if (aVec3->SquareModulus() >= THE_NORMAL_PREC2) { myCoordSysConverter.TransformNormal(*aVec3); setNodeNormal(theDestMesh, THE_LOWER_NODE_INDEX + aVertIter, *aVec3); } else { setNodeNormal(theDestMesh, THE_LOWER_NODE_INDEX + aVertIter, gp_Vec3f(0.0, 0.0, 1.0)); } } } else { for (Standard_Integer aVertIter = 0; aVertIter < aNbNodes; ++aVertIter) { const Graphic3d_Vec3* aVec3 = aBuffer.ReadChunk(theStream); if (aVec3 == NULL) { reportError(TCollection_AsciiString("Buffer '") + aName + "' reading error."); return false; } if (aVec3->SquareModulus() >= THE_NORMAL_PREC2) { setNodeNormal(theDestMesh, THE_LOWER_NODE_INDEX + aVertIter, *aVec3); } else { setNodeNormal(theDestMesh, THE_LOWER_NODE_INDEX + aVertIter, gp_Vec3f(0.0, 0.0, 1.0)); } } } break; } case RWGltf_GltfArrayType_TCoord0: { if (theAccessor.ComponentType != RWGltf_GltfAccessorCompType_Float32 || theAccessor.Type != RWGltf_GltfAccessorLayout_Vec2) { break; } else if (theAccessor.Count > std::numeric_limits::max()) { reportError(TCollection_AsciiString("Buffer '") + aName + "' defines too big array."); return false; } const size_t aStride = theAccessor.ByteStride != 0 ? theAccessor.ByteStride : sizeof(Graphic3d_Vec2); const Standard_Integer aNbNodes = (Standard_Integer)theAccessor.Count; if (!setNbUVNodes(theDestMesh, aNbNodes)) { return false; } Standard_ReadBuffer aBuffer(theAccessor.Count * aStride - (aStride - sizeof(Graphic3d_Vec2)), aStride, true); for (int aVertIter = 0; aVertIter < aNbNodes; ++aVertIter) { Graphic3d_Vec2* aVec2 = aBuffer.ReadChunk(theStream); if (aVec2 == NULL) { reportError(TCollection_AsciiString("Buffer '") + aName + "' reading error."); return false; } // Y should be flipped (relative to image layout used by OCCT) aVec2->y() = 1.0f - aVec2->y(); setNodeUV(theDestMesh, THE_LOWER_NODE_INDEX + aVertIter, gp_Pnt2d(aVec2->x(), aVec2->y())); } break; } case RWGltf_GltfArrayType_Color: case RWGltf_GltfArrayType_TCoord1: case RWGltf_GltfArrayType_Joint: case RWGltf_GltfArrayType_Weight: { return true; } case RWGltf_GltfArrayType_UNKNOWN: { return false; } } return true; }