Skip to content

Commit

Permalink
GLTF Import - Edge and Vertex support #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 Jan 24, 2025
1 parent c716233 commit ef27677
Show file tree
Hide file tree
Showing 16 changed files with 326 additions and 67 deletions.
117 changes: 91 additions & 26 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 @@ -1530,7 +1532,9 @@ bool RWGltf_GltfJsonParser::gltfParseSceneNodes (TopTools_SequenceOfShape& theSh
continue;
}
else if (myToSkipEmptyNodes
&& !TopExp_Explorer (aNodeShape, TopAbs_FACE).More())
&& !TopExp_Explorer (aNodeShape, TopAbs_FACE).More()
&& !TopExp_Explorer (aNodeShape, TopAbs_EDGE).More()
&& !TopExp_Explorer (aNodeShape, TopAbs_VERTEX).More())
{
continue;
}
Expand Down Expand Up @@ -1782,7 +1786,9 @@ bool RWGltf_GltfJsonParser::gltfParsePrimArray (TopoDS_Shape& thePrimArrayShape,
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");
return true;
Expand Down Expand Up @@ -1827,19 +1833,15 @@ bool RWGltf_GltfJsonParser::gltfParsePrimArray (TopoDS_Shape& thePrimArrayShape,
{
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;
myAttribMap->Bind (aShapeCopy, aShapeAttribs);
myShapeMap[ShapeMapGroup_PrimArray].Bind (aPrimArrayIdWithMat, aShapeCopy);
thePrimArrayShape = aShapeCopy;
}
return true;
}
Expand Down Expand Up @@ -1906,32 +1908,87 @@ bool RWGltf_GltfJsonParser::gltfParsePrimArray (TopoDS_Shape& thePrimArrayShape,
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 (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())
&& aMeshData->HasStyle())
{
RWMesh_NodeAttributes aShapeAttribs;
aShapeAttribs.RawName = theMeshName;

// assign material and not color
//aShapeAttribs.Style.SetColorSurf (aMeshData->BaseColor());
aShapeAttribs.Style.SetMaterial (aMat);
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 @@ -2085,7 +2142,15 @@ bool RWGltf_GltfJsonParser::gltfParseAccessor (const Handle(RWGltf_GltfLatePrimi
}
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 = myGltfRoots[RWGltf_GltfRootElement_BufferViews].FindChild (*aBufferViewName);
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 @@ -103,7 +103,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 @@ -82,7 +82,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
69 changes: 43 additions & 26 deletions src/RWGltf/RWGltf_TriangulationReader.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -520,10 +520,13 @@ bool RWGltf_TriangulationReader::readBuffer (const Handle(RWGltf_GltfLatePrimiti
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");
Message::SendWarning(TCollection_AsciiString("Buffer '") + aName
+ "' skipped unsupported primitive array");
return true;
}

Expand All @@ -545,50 +548,64 @@ bool RWGltf_TriangulationReader::readBuffer (const Handle(RWGltf_GltfLatePrimiti
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)
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.");
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)");
return false;
Expand All @@ -599,7 +616,7 @@ bool RWGltf_TriangulationReader::readBuffer (const Handle(RWGltf_GltfLatePrimiti
Message::SendTrace (TCollection_AsciiString() + aNbDegenerate
+ " 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 @@ -108,7 +108,7 @@ protected:

protected:

Handle(Poly_Triangulation) myTriangulation;
Handle(RWMesh_TriangulationSource) myTriangulation;

};

Expand Down
34 changes: 34 additions & 0 deletions src/RWMesh/RWMesh_TriangulationReader.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -147,3 +147,37 @@ bool RWMesh_TriangulationReader::finalizeLoading (const Handle(RWMesh_Triangulat
}
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 @@ -269,6 +269,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
Expand Down
Loading

0 comments on commit ef27677

Please sign in to comment.