1
0
mirror of https://git.dev.opencascade.org/repos/occt.git synced 2025-08-04 13:13:25 +03:00
occt/src/RWGltf/RWGltf_TriangulationReader.cxx
kgv 2e84f455db 0032654: Data Exchange - fix misprint in RWGltf_TriangulationReader::readDracoBuffer()
Added missing break.
Fixes reading of glTF files with Normal attributes preceding Position
(and eliminates redundant / erroneous copy when Position attributes precede Normal).
2021-11-19 10:12:36 +03:00

922 lines
35 KiB
C++

// 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 <RWGltf_TriangulationReader.hxx>
#include <Message.hxx>
#include <OSD_FileSystem.hxx>
#include <RWGltf_GltfLatePrimitiveArray.hxx>
#include <RWGltf_GltfPrimArrayData.hxx>
#include <Standard_ArrayStreamBuffer.hxx>
#include <Standard_ReadBuffer.hxx>
#ifdef HAVE_DRACO
#include <Standard_WarningsDisable.hxx>
#include <draco/compression/decode.h>
#include <Standard_WarningsRestore.hxx>
#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
}
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();
opencascade::std::shared_ptr<std::istream> 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<RWGltf_GltfPrimArrayData>::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();
opencascade::std::shared_ptr<std::istream> 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<char> aReadData;
aReadData.resize (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<std::unique_ptr<draco::Mesh>> 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<Standard_Integer>::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<const Graphic3d_Vec3* >(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<const Graphic3d_Vec3* >(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<const Graphic3d_Vec2* >(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<RWGltf_GltfPrimArrayData>::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<Standard_Integer>::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<uint16_t> (theStream))
{
aVec3.ChangeValue (1) = THE_LOWER_NODE_INDEX + *anIndex0;
}
if (const uint16_t* anIndex1 = aBuffer.ReadChunk<uint16_t> (theStream))
{
aVec3.ChangeValue (2) = THE_LOWER_NODE_INDEX + *anIndex1;
}
if (const uint16_t* anIndex2 = aBuffer.ReadChunk<uint16_t> (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<Standard_Integer>::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<uint32_t> (theStream))
{
aVec3.ChangeValue (1) = THE_LOWER_NODE_INDEX + *anIndex0;
}
if (const uint32_t* anIndex1 = aBuffer.ReadChunk<uint32_t> (theStream))
{
aVec3.ChangeValue (2) = THE_LOWER_NODE_INDEX + *anIndex1;
}
if (const uint32_t* anIndex2 = aBuffer.ReadChunk<uint32_t> (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<Standard_Integer>::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<uint8_t> (theStream))
{
aVec3.ChangeValue (1) = THE_LOWER_NODE_INDEX + (Standard_Integer )*anIndex0;
}
if (const uint8_t* anIndex1 = aBuffer.ReadChunk<uint8_t> (theStream))
{
aVec3.ChangeValue (2) = THE_LOWER_NODE_INDEX + (Standard_Integer )*anIndex1;
}
if (const uint8_t* anIndex2 = aBuffer.ReadChunk<uint8_t> (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<Standard_Integer>::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<Graphic3d_Vec3> (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<Graphic3d_Vec3> (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<Standard_Integer>::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<Graphic3d_Vec3> (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<Graphic3d_Vec3> (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<Standard_Integer>::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<Graphic3d_Vec2> (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;
}