Skip to content

Commit

Permalink
GLTF Import - Edge and Vertex support Open-Cascade-SAS#242
Browse files Browse the repository at this point in the history
Added functionality to import Points and Lines from GLTF format
  • Loading branch information
mzernova committed Feb 2, 2025
1 parent 5bfd611 commit c86c669
Show file tree
Hide file tree
Showing 16 changed files with 329 additions and 72 deletions.
123 changes: 94 additions & 29 deletions src/RWGltf/RWGltf_GltfJsonParser.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
#include "RWGltf_GltfJsonParser.hxx"

#include <BRep_Builder.hxx>
#include <gp_Quaternion.hxx>
#include <FSD_Base64.hxx>
#include <Message.hxx>
#include <Message_Messenger.hxx>
#include <Message_ProgressScope.hxx>
Expand All @@ -24,11 +24,13 @@
#include <OSD_Path.hxx>
#include <OSD_ThreadPool.hxx>
#include <Precision.hxx>
#include <FSD_Base64.hxx>
#include <RWGltf_TriangulationReader.hxx>
#include <TDataStd_NamedData.hxx>
#include <TopExp_Explorer.hxx>
#include <TopoDS.hxx>
#include <TopoDS_Edge.hxx>
#include <TopoDS_Iterator.hxx>
#include <gp_Quaternion.hxx>

#include <fstream>

Expand Down Expand Up @@ -1581,7 +1583,10 @@ bool RWGltf_GltfJsonParser::gltfParseSceneNodes(TopTools_SequenceOfShape& the
{
continue;
}
else if (myToSkipEmptyNodes && !TopExp_Explorer(aNodeShape, TopAbs_FACE).More())
else if (myToSkipEmptyNodes
&& !TopExp_Explorer (aNodeShape, TopAbs_FACE).More()
&& !TopExp_Explorer (aNodeShape, TopAbs_EDGE).More()
&& !TopExp_Explorer (aNodeShape, TopAbs_VERTEX).More())
{
continue;
}
Expand Down Expand Up @@ -1838,7 +1843,9 @@ bool RWGltf_GltfJsonParser::gltfParsePrimArray(TopoDS_Shape& th
return false;
}
}
if (aMode != RWGltf_GltfPrimitiveMode_Triangles)
if (aMode != RWGltf_GltfPrimitiveMode_Triangles
&& aMode != RWGltf_GltfPrimitiveMode_Lines
&& aMode != RWGltf_GltfPrimitiveMode_Points)
{
Message::SendWarning(TCollection_AsciiString() + "Primitive array within Mesh '" + theMeshId
+ "' skipped due to unsupported mode");
Expand Down Expand Up @@ -1887,21 +1894,15 @@ bool RWGltf_GltfJsonParser::gltfParsePrimArray(TopoDS_Shape& th
{
if (myAttribMap != NULL)
{
// sharing just triangulation is not much useful
// Handle(RWGltf_GltfLatePrimitiveArray) aLateData =
// Handle(RWGltf_GltfLatePrimitiveArray)::DownCast (BRep_Tool::Triangulation (TopoDS::Face
// (thePrimArrayShape), aDummy)); TopoDS_Face aFaceCopy; BRep_Builder().MakeFace (aFaceCopy,
// aLateData);

// make a located Face copy
TopoDS_Shape aFaceCopy = thePrimArrayShape;
aFaceCopy.Location(TopLoc_Location(gp_Trsf()));
// make a located Shape copy
TopoDS_Shape aShapeCopy = thePrimArrayShape;
aShapeCopy.Location (TopLoc_Location (gp_Trsf()));
RWMesh_NodeAttributes aShapeAttribs;
aShapeAttribs.RawName = theMeshName;
aShapeAttribs.Style.SetMaterial(aMat);
myAttribMap->Bind(aFaceCopy, aShapeAttribs);
myShapeMap[ShapeMapGroup_PrimArray].Bind(aPrimArrayIdWithMat, aFaceCopy);
thePrimArrayShape = aFaceCopy;
aShapeAttribs.Style.SetMaterial (aMat);
myAttribMap->Bind (aShapeCopy, aShapeAttribs);
myShapeMap[ShapeMapGroup_PrimArray].Bind (aPrimArrayIdWithMat, aShapeCopy);
thePrimArrayShape = aShapeCopy;
}
return true;
}
Expand Down Expand Up @@ -1978,31 +1979,87 @@ bool RWGltf_GltfJsonParser::gltfParsePrimArray(TopoDS_Shape& th
return false;
}
}
else
else if (aMode == RWGltf_GltfPrimitiveMode_Triangles)
{
aMeshData->SetNbDeferredTriangles(aMeshData->NbDeferredNodes() / 3);
}
else
{
aMeshData->SetNbDeferredTriangles (0);
}

if (!aMeshData->Data().IsEmpty())
{
TopoDS_Face aFace;
BRep_Builder aBuilder;
aBuilder.MakeFace(aFace, aMeshData);
if (myAttribMap != NULL && aMeshData->HasStyle())
if (aMode != RWGltf_GltfPrimitiveMode_Triangles)
{
Message::SendWarning("Deferred loading is available only for triangulations. Other elements "
"will be loaded immediately.");
Handle(RWGltf_TriangulationReader) aReader = new RWGltf_TriangulationReader();
aReader->SetCoordinateSystemConverter(myCSTrsf);
aMeshData->SetReader(aReader);
aMeshData->LoadDeferredData();
}

TopoDS_Shape aShape;
switch (aMode)
{
case RWGltf_GltfPrimitiveMode_Points: {
BRep_Builder aBuilder;
TopoDS_Compound aVertices;
aBuilder.MakeCompound(aVertices);
for (Standard_Integer aNodeIdx = 1; aNodeIdx <= aMeshData->NbNodes(); ++aNodeIdx)
{
TopoDS_Vertex aVertex;
aBuilder.MakeVertex(aVertex, aMeshData->Node(aNodeIdx), Precision::Confusion());
aBuilder.Add(aVertices, aVertex);
}
aShape = aVertices;
break;
}
case RWGltf_GltfPrimitiveMode_Lines: {
TColgp_Array1OfPnt aNodes(1, aMeshData->NbEdges());
for (Standard_Integer anEdgeIdx = 1; anEdgeIdx <= aMeshData->NbEdges(); ++anEdgeIdx)
{
Standard_Integer aNodeIdx = aMeshData->Edge(anEdgeIdx);
aNodes.SetValue(anEdgeIdx, aMeshData->Node(aNodeIdx));
}
TopoDS_Edge anEdge;
BRep_Builder aBuilder;
Handle(Poly_Polygon3D) aPoly = new Poly_Polygon3D(aNodes);
aBuilder.MakeEdge(anEdge, aPoly);

aShape = anEdge;
break;
}
case RWGltf_GltfPrimitiveMode_Triangles: {
TopoDS_Face aFace;
BRep_Builder aBuilder;
aBuilder.MakeFace(aFace, aMeshData);
aShape = aFace;
myFaceList.Append(aFace);
break;
}
default: {
Message::SendFail("Unsupported primitive mode.");
return false;
break;
}
}
if (myAttribMap != NULL
&& aMeshData->HasStyle())
{
RWMesh_NodeAttributes aShapeAttribs;
aShapeAttribs.RawName = theMeshName;

// assign material and not color
// aShapeAttribs.Style.SetColorSurf (aMeshData->BaseColor());
//aShapeAttribs.Style.SetColorSurf (aMeshData->BaseColor());
aShapeAttribs.Style.SetMaterial(aMat);

myAttribMap->Bind(aFace, aShapeAttribs);
myAttribMap->Bind (aShape, aShapeAttribs);
}
myFaceList.Append(aFace);
myShapeMap[ShapeMapGroup_PrimArray].Bind(aPrimArrayId, aFace);
myShapeMap[ShapeMapGroup_PrimArray].Bind(aPrimArrayIdWithMat, aFace);
thePrimArrayShape = aFace;
myShapeMap[ShapeMapGroup_PrimArray].Bind (aPrimArrayId, aShape);
myShapeMap[ShapeMapGroup_PrimArray].Bind (aPrimArrayIdWithMat, aShape);
thePrimArrayShape = aShape;
}
return true;
}
Expand Down Expand Up @@ -2147,7 +2204,15 @@ bool RWGltf_GltfJsonParser::gltfParseAccessor(
}
else if (theType == RWGltf_GltfArrayType_Indices)
{
theMeshData->SetNbDeferredTriangles((Standard_Integer)(aStruct.Count / 3));
if (theMeshData->PrimitiveMode() == RWGltf_GltfPrimitiveMode_Triangles)
{
theMeshData->SetNbDeferredTriangles ((Standard_Integer)(aStruct.Count / 3));
}
else
{
theMeshData->SetNbDeferredNodes ((Standard_Integer)(aStruct.Count));
theMeshData->SetNbDeferredTriangles (0);
}
}

const RWGltf_JsonValue* aBufferView =
Expand Down
3 changes: 2 additions & 1 deletion src/RWGltf/RWGltf_GltfLatePrimitiveArray.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,8 @@ Handle(Poly_Triangulation) RWGltf_GltfLatePrimitiveArray::LoadStreamData() const
{
return Handle(Poly_Triangulation)();
}
Handle(Poly_Triangulation) aResult = createNewEntity();

Handle(RWMesh_TriangulationSource) aResult = new RWMesh_TriangulationSource();
if (!aGltfReader->LoadStreamData(this, aResult))
{
return Handle(Poly_Triangulation)();
Expand Down
2 changes: 1 addition & 1 deletion src/RWGltf/RWGltf_GltfLatePrimitiveArray.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ public:
//! that can be loaded using LoadDeferredData().
virtual Standard_Boolean HasDeferredData() const Standard_OVERRIDE
{
return !myData.IsEmpty() && RWMesh_TriangulationSource::HasDeferredData();
return !myData.IsEmpty() && (NbDeferredTriangles() > 0 || NbDeferredNodes() > 0);
}

//! Load primitive array saved as stream buffer to new triangulation object.
Expand Down
71 changes: 43 additions & 28 deletions src/RWGltf/RWGltf_TriangulationReader.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -583,8 +583,10 @@ bool RWGltf_TriangulationReader::readBuffer(
RWGltf_GltfArrayType theType) const

{
const TCollection_AsciiString& aName = theSourceMesh->Id();
if (theSourceMesh->PrimitiveMode() != RWGltf_GltfPrimitiveMode_Triangles)
const TCollection_AsciiString& aName = theSourceMesh->Id();
const RWGltf_GltfPrimitiveMode aPrimMode = theSourceMesh->PrimitiveMode();
if (aPrimMode != RWGltf_GltfPrimitiveMode_Triangles && aPrimMode != RWGltf_GltfPrimitiveMode_Lines
&& aPrimMode != RWGltf_GltfPrimitiveMode_Points)
{
Message::SendWarning(TCollection_AsciiString("Buffer '") + aName
+ "' skipped unsupported primitive array");
Expand All @@ -608,51 +610,64 @@ bool RWGltf_TriangulationReader::readBuffer(
return false;
}

const Standard_Integer aNbTris = (Standard_Integer)(theAccessor.Count / 3);
if (!setNbTriangles(theDestMesh, aNbTris))
const Standard_Boolean isTriangles = aPrimMode == RWGltf_GltfPrimitiveMode_Triangles;
const Standard_Integer aCounter = isTriangles
? (Standard_Integer )(theAccessor.Count / 3)
: (Standard_Integer )(theAccessor.Count);
if ((isTriangles && !setNbTriangles(theDestMesh, aCounter))
|| !setNbEdges(theDestMesh, aCounter))
{
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)
const size_t aStride = theAccessor.ByteStride != 0
? theAccessor.ByteStride
: sizeof(uint16_t);
Standard_ReadBuffer aBuffer (theAccessor.Count * aStride, aStride);
Standard_Integer aLastIndex = 0;
for (Standard_Integer aTriIter = 0; aTriIter < aCounter; ++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;
Standard_Integer wasSet = false;
if (isTriangles)
{
for (Standard_Integer anIter = 1; anIter <= 3; ++anIter)
{
const uint16_t* anIndex = aBuffer.ReadChunk<uint16_t>(theStream);
if (anIndex == nullptr)
{
reportError(TCollection_AsciiString("Buffer '") + aName + "' reading error.");
return false;
}
aVec3.ChangeValue(anIter) = THE_LOWER_NODE_INDEX + *anIndex;
}
wasSet = setTriangle(theDestMesh, THE_LOWER_TRI_INDEX + aLastIndex, aVec3);
}
else
{
reportError(TCollection_AsciiString("Buffer '") + aName + "' reading error.");
return false;
const uint16_t* anIndex = aBuffer.ReadChunk<uint16_t>(theStream);
if (anIndex == nullptr)
{
reportError(TCollection_AsciiString("Buffer '") + aName + "' reading error.");
return false;
}
wasSet = setEdge(theDestMesh,
THE_LOWER_TRI_INDEX + aLastIndex,
THE_LOWER_NODE_INDEX + *anIndex);
}

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++;
aLastIndex++;
}
}
const Standard_Integer aNbDegenerate = aNbTris - aLastTriIndex;
const Standard_Integer aNbDegenerate = aCounter - aLastIndex;
if (aNbDegenerate > 0)
{
if (aNbDegenerate == aNbTris)
if (aNbDegenerate == aCounter)
{
Message::SendWarning(TCollection_AsciiString("Buffer '") + aName
+ "' has been skipped (all elements are degenerative in)");
Expand All @@ -666,7 +681,7 @@ bool RWGltf_TriangulationReader::readBuffer(
+ " degenerate triangles have been skipped while reading glTF triangulation '" + aName
+ "'");
}
if (!setNbTriangles(theDestMesh, aLastTriIndex, true))
if (!setNbTriangles (theDestMesh, aLastIndex, true))
{
return false;
}
Expand Down
2 changes: 1 addition & 1 deletion src/RWGltf/RWGltf_TriangulationReader.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ protected:
const Handle(OSD_FileSystem)& theFileSystem) const;

protected:
Handle(Poly_Triangulation) myTriangulation;
Handle(RWMesh_TriangulationSource) myTriangulation;
};

#endif // _RWGltf_TriangulationReader_HeaderFile
34 changes: 34 additions & 0 deletions src/RWMesh/RWMesh_TriangulationReader.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -162,3 +162,37 @@ bool RWMesh_TriangulationReader::finalizeLoading(
}
return true;
}

// =======================================================================
// function : setNbEdges
// purpose :
// =======================================================================
bool RWMesh_TriangulationReader::setNbEdges(const Handle(Poly_Triangulation)& theMesh,
const Standard_Integer theNbTris,
const Standard_Boolean theToCopyData) const
{
Handle(RWMesh_TriangulationSource) aMesh = Handle(RWMesh_TriangulationSource)::DownCast(theMesh);
if (theNbTris >= 1)
{
aMesh->ResizeEdges(theNbTris, theToCopyData);
return true;
}
return false;
}

// =======================================================================
// function : setEdge
// purpose :
// =======================================================================
Standard_Integer RWMesh_TriangulationReader::setEdge(const Handle(Poly_Triangulation)& theMesh,
const Standard_Integer theIndex,
const Standard_Integer theEdge) const
{
Handle(RWMesh_TriangulationSource) aMesh = Handle(RWMesh_TriangulationSource)::DownCast(theMesh);
if (theEdge < 1 || theEdge > theMesh->NbNodes())
{
return 0;
}
aMesh->SetEdge(theIndex, theEdge);
return 1;
}
19 changes: 19 additions & 0 deletions src/RWMesh/RWMesh_TriangulationReader.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,25 @@ protected: //! @name interface for filling triangulation data
return 1;
}

//! Resizes array of edges to specified size.
//! @param[in] theMesh triangulation source to be modified
//! @param[in] theNbEdges elements number
//! @param[in] theToCopyData copy old edges into new array
//! @return TRUE in case of success operation
Standard_EXPORT virtual bool setNbEdges(const Handle(Poly_Triangulation)& theMesh,
const Standard_Integer theNbEdges,
const Standard_Boolean theToCopyData = false) const;

//! Adds edge element.
//! @param[in] theMesh triangulation source to be modified
//! @param theIndex edge index starting from 1
//! @param theEdge edge nodes starting from 1
//! @return 0 if node indexes are out of range,
//! 1 in case of success operation.
Standard_EXPORT virtual Standard_Integer setEdge(const Handle(Poly_Triangulation)& theMesh,
const Standard_Integer theIndex,
const Standard_Integer theEdge) const;

protected:
RWMesh_CoordinateSystemConverter myCoordSysConverter; //!< coordinate system converter
// clang-format off
Expand Down
Loading

0 comments on commit c86c669

Please sign in to comment.