mirror of
https://git.dev.opencascade.org/repos/occt.git
synced 2025-04-10 18:51:21 +03:00
Update empty method guards to new style with regex (see PR). Used clang-format 18.1.8. New actions to validate code formatting is added. Update .clang-format with disabling of include sorting. It is temporary changes, then include will be sorted. Apply formatting for /src and /tools folder. The files with .hxx,.cxx,.lxx,.h,.pxx,.hpp,*.cpp extensions.
997 lines
35 KiB
C++
997 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
|
|
} // 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<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();
|
|
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((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<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;
|
|
}
|