#include <OSD_FileSystem.hxx>
#include <OSD_File.hxx>
#include <OSD_Path.hxx>
-#include <Poly_Triangulation.hxx>
+#include <OSD_Timer.hxx>
#include <RWGltf_GltfAccessorLayout.hxx>
#include <RWGltf_GltfArrayType.hxx>
#include <RWGltf_GltfMaterialMap.hxx>
#include <RWGltf_GltfOStreamWriter.hxx>
#endif
+#ifdef HAVE_DRACO
+ #include <draco/compression/encode.h>
+#endif
+
IMPLEMENT_STANDARD_RTTIEXT(RWGltf_CafWriter, Standard_Transient)
namespace
{
theStream.write ((const char* )theTri.GetData(), sizeof(theTri));
}
+
+#ifdef HAVE_DRACO
+ //! Write nodes to Draco mesh
+ static void writeNodesToDracoMesh (draco::Mesh& theMesh,
+ const std::vector<Graphic3d_Vec3>& theNodes)
+ {
+ if (theNodes.empty())
+ {
+ return;
+ }
+
+ draco::PointAttribute anAttr;
+ anAttr.Init (draco::GeometryAttribute::POSITION, 3, draco::DT_FLOAT32, false, sizeof(float) * 3);
+ const int anId = theMesh.AddAttribute (anAttr, true, uint32_t(theNodes.size()));
+ draco::PointAttribute* aPtr = theMesh.attribute (anId);
+ draco::PointIndex anIndex(0);
+ for (size_t aNodeInd = 0; aNodeInd < theNodes.size(); ++aNodeInd, ++anIndex)
+ {
+ aPtr->SetAttributeValue (aPtr->mapped_index(anIndex), theNodes[aNodeInd].GetData());
+ }
+ }
+
+ //! Write normals to Draco mesh
+ static void writeNormalsToDracoMesh (draco::Mesh& theMesh,
+ const std::vector<Graphic3d_Vec3>& theNormals)
+ {
+ if (theNormals.empty())
+ {
+ return;
+ }
+
+ draco::PointAttribute anAttr;
+ anAttr.Init (draco::GeometryAttribute::NORMAL, 3, draco::DT_FLOAT32, false, sizeof(float) * 3);
+ const int anId = theMesh.AddAttribute (anAttr, true, uint32_t(theNormals.size()));
+ draco::PointAttribute* aPtr = theMesh.attribute (anId);
+ draco::PointIndex anIndex(0);
+ for (size_t aNormInd = 0; aNormInd < theNormals.size(); ++aNormInd, ++anIndex)
+ {
+ aPtr->SetAttributeValue (aPtr->mapped_index(anIndex), theNormals[aNormInd].GetData());
+ }
+ }
+
+ //! Write texture UV coordinates to Draco mesh
+ static void writeTexCoordsToDracoMesh (draco::Mesh& theMesh,
+ const std::vector<Graphic3d_Vec2>& theTexCoord)
+ {
+ if (theTexCoord.empty())
+ {
+ return;
+ }
+
+ draco::PointAttribute anAttr;
+ anAttr.Init (draco::GeometryAttribute::TEX_COORD, 2, draco::DT_FLOAT32, false, sizeof(float) * 2);
+ const int anId = theMesh.AddAttribute (anAttr, true, uint32_t(theTexCoord.size()));
+ draco::PointAttribute* aPtr = theMesh.attribute (anId);
+ draco::PointIndex anIndex(0);
+ for (size_t aTexInd = 0; aTexInd < theTexCoord.size(); ++aTexInd, ++anIndex)
+ {
+ aPtr->SetAttributeValue (aPtr->mapped_index(anIndex), theTexCoord[aTexInd].GetData());
+ }
+ }
+
+ //! Write indices to Draco mesh
+ static void writeIndicesToDracoMesh (draco::Mesh& theMesh,
+ const std::vector<Poly_Triangle>& theIndices)
+ {
+ draco::Mesh::Face aFace;
+ int anIndex = 0;
+ for (size_t anInd = 0; anInd < theIndices.size(); ++anInd, ++anIndex)
+ {
+ const Poly_Triangle& anElem = theIndices[anInd];
+ aFace[0] = anElem.Value(1);
+ aFace[1] = anElem.Value(2);
+ aFace[2] = anElem.Value(3);
+ theMesh.SetFace (draco::FaceIndex (anIndex), aFace);
+ }
+ }
+#endif
}
//================================================================
void RWGltf_CafWriter::saveNodes (RWGltf_GltfFace& theGltfFace,
std::ostream& theBinFile,
const RWMesh_FaceIterator& theFaceIter,
- Standard_Integer& theAccessorNb) const
+ Standard_Integer& theAccessorNb,
+ const std::shared_ptr<RWGltf_CafWriter::Mesh>& theMesh) const
{
if (theGltfFace.NodePos.Id == RWGltf_GltfAccessor::INVALID_ID)
{
}
else
{
- const int64_t aPos = theGltfFace.NodePos.ByteOffset + myBuffViewPos.ByteOffset + theGltfFace.NodePos.Count * sizeof(Graphic3d_Vec3);
- Standard_ASSERT_RAISE (aPos == (int64_t )theBinFile.tellp(), "wrong offset");
+ if (theMesh.get() == nullptr)
+ {
+ const int64_t aPos = theGltfFace.NodePos.ByteOffset + myBuffViewPos.ByteOffset + theGltfFace.NodePos.Count * sizeof(Graphic3d_Vec3);
+ Standard_ASSERT_RAISE(aPos == (int64_t)theBinFile.tellp(), "wrong offset");
+ }
}
theGltfFace.NodePos.Count += theFaceIter.NbNodes();
gp_XYZ aNode = theFaceIter.NodeTransformed (aNodeIter).XYZ();
myCSTrsf.TransformPosition (aNode);
theGltfFace.NodePos.BndBox.Add (Graphic3d_Vec3d(aNode.X(), aNode.Y(), aNode.Z()));
- writeVec3 (theBinFile, aNode);
+ if (theMesh.get() != nullptr)
+ {
+ theMesh->NodesVec.push_back(Graphic3d_Vec3(float(aNode.X()), float(aNode.Y()), float(aNode.Z())));
+ }
+ else
+ {
+ writeVec3(theBinFile, aNode);
+ }
}
}
void RWGltf_CafWriter::saveNormals (RWGltf_GltfFace& theGltfFace,
std::ostream& theBinFile,
RWMesh_FaceIterator& theFaceIter,
- Standard_Integer& theAccessorNb) const
+ Standard_Integer& theAccessorNb,
+ const std::shared_ptr<RWGltf_CafWriter::Mesh>& theMesh) const
{
if (!theFaceIter.HasNormals())
{
}
else
{
- const int64_t aPos = theGltfFace.NodeNorm.ByteOffset + myBuffViewNorm.ByteOffset + theGltfFace.NodeNorm.Count * sizeof(Graphic3d_Vec3);
- Standard_ASSERT_RAISE (aPos == (int64_t )theBinFile.tellp(), "wrong offset");
+ if (theMesh.get() == nullptr)
+ {
+ const int64_t aPos = theGltfFace.NodeNorm.ByteOffset + myBuffViewNorm.ByteOffset + theGltfFace.NodeNorm.Count * sizeof(Graphic3d_Vec3);
+ Standard_ASSERT_RAISE(aPos == (int64_t)theBinFile.tellp(), "wrong offset");
+ }
}
theGltfFace.NodeNorm.Count += theFaceIter.NbNodes();
const gp_Dir aNormal = theFaceIter.NormalTransformed (aNodeIter);
Graphic3d_Vec3 aVecNormal ((float )aNormal.X(), (float )aNormal.Y(), (float )aNormal.Z());
myCSTrsf.TransformNormal (aVecNormal);
- writeVec3 (theBinFile, aVecNormal);
+ if (theMesh.get() != nullptr)
+ {
+ theMesh->NormalsVec.push_back(aVecNormal);
+ }
+ else
+ {
+ writeVec3(theBinFile, aVecNormal);
+ }
}
}
void RWGltf_CafWriter::saveTextCoords (RWGltf_GltfFace& theGltfFace,
std::ostream& theBinFile,
const RWMesh_FaceIterator& theFaceIter,
- Standard_Integer& theAccessorNb) const
+ Standard_Integer& theAccessorNb,
+ const std::shared_ptr<RWGltf_CafWriter::Mesh>& theMesh) const
{
if (!theFaceIter.HasTexCoords())
{
}
else
{
- const int64_t aPos = theGltfFace.NodeUV.ByteOffset + myBuffViewTextCoord.ByteOffset + theGltfFace.NodeUV.Count * sizeof(Graphic3d_Vec2);
- Standard_ASSERT_RAISE (aPos == (int64_t )theBinFile.tellp(), "wrong offset");
+ if (theMesh.get() == nullptr)
+ {
+ const int64_t aPos = theGltfFace.NodeUV.ByteOffset + myBuffViewTextCoord.ByteOffset + theGltfFace.NodeUV.Count * sizeof(Graphic3d_Vec2);
+ Standard_ASSERT_RAISE(aPos == (int64_t)theBinFile.tellp(), "wrong offset");
+ }
}
theGltfFace.NodeUV.Count += theFaceIter.NbNodes();
{
gp_Pnt2d aTexCoord = theFaceIter.NodeTexCoord (aNodeIter);
aTexCoord.SetY (1.0 - aTexCoord.Y());
- writeVec2 (theBinFile, aTexCoord.XY());
+ if (theMesh.get() != nullptr)
+ {
+ theMesh->TexCoordsVec.push_back(Graphic3d_Vec2((float)aTexCoord.X(), (float)aTexCoord.Y()));
+ }
+ else
+ {
+ writeVec2(theBinFile, aTexCoord.XY());
+ }
}
}
void RWGltf_CafWriter::saveIndices (RWGltf_GltfFace& theGltfFace,
std::ostream& theBinFile,
const RWMesh_FaceIterator& theFaceIter,
- Standard_Integer& theAccessorNb)
+ Standard_Integer& theAccessorNb,
+ const std::shared_ptr<RWGltf_CafWriter::Mesh>& theMesh)
{
if (theGltfFace.Indices.Id == RWGltf_GltfAccessor::INVALID_ID)
{
}
else
{
- const int64_t aRefPos = (int64_t )theBinFile.tellp();
- const int64_t aPos = theGltfFace.Indices.ByteOffset
- + myBuffViewInd.ByteOffset
- + theGltfFace.Indices.Count * (theGltfFace.Indices.ComponentType == RWGltf_GltfAccessorCompType_UInt32 ? sizeof(uint32_t) : sizeof(uint16_t));
- Standard_ASSERT_RAISE (aPos == aRefPos, "wrong offset");
+ if (theMesh.get() == nullptr)
+ {
+ const int64_t aRefPos = (int64_t )theBinFile.tellp();
+ const int64_t aPos = theGltfFace.Indices.ByteOffset
+ + myBuffViewInd.ByteOffset
+ + theGltfFace.Indices.Count * (theGltfFace.Indices.ComponentType == RWGltf_GltfAccessorCompType_UInt32 ? sizeof(uint32_t) : sizeof(uint16_t));
+ Standard_ASSERT_RAISE (aPos == aRefPos, "wrong offset");
+ }
}
const Standard_Integer aNodeFirst = theGltfFace.NbIndexedNodes - theFaceIter.ElemLower();
aTri(1) += aNodeFirst;
aTri(2) += aNodeFirst;
aTri(3) += aNodeFirst;
- if (theGltfFace.Indices.ComponentType == RWGltf_GltfAccessorCompType_UInt16)
+ if (theMesh.get() != nullptr)
{
- writeTriangle16 (theBinFile, NCollection_Vec3<uint16_t>((uint16_t)aTri(1), (uint16_t)aTri(2), (uint16_t)aTri(3)));
+ theMesh->IndicesVec.push_back(aTri);
}
else
{
- writeTriangle32 (theBinFile, Graphic3d_Vec3i (aTri(1), aTri(2), aTri(3)));
+ if (theGltfFace.Indices.ComponentType == RWGltf_GltfAccessorCompType_UInt16)
+ {
+ writeTriangle16(theBinFile, NCollection_Vec3<uint16_t>((uint16_t)aTri(1), (uint16_t)aTri(2), (uint16_t)aTri(3)));
+ }
+ else
+ {
+ writeTriangle32(theBinFile, Graphic3d_Vec3i(aTri(1), aTri(2), aTri(3)));
+ }
}
}
}
const TColStd_MapOfAsciiString* theLabelFilter,
const Message_ProgressRange& theProgress)
{
+#ifndef HAVE_DRACO
+ if (myDracoParameters.DracoCompression)
+ {
+ Message::SendFail ("Error: cannot use Draco compression, Draco library missing.");
+ return false;
+ }
+#endif
+
myBuffViewPos.Id = RWGltf_GltfAccessor::INVALID_ID;
myBuffViewPos.ByteOffset = 0;
myBuffViewPos.ByteLength = 0;
myBuffViewInd.ByteLength = 0;
myBuffViewInd.Target = RWGltf_GltfBufferViewTarget_ELEMENT_ARRAY_BUFFER;
+ myBuffViewsDraco.clear();
+
myBinDataMap.Clear();
myBinDataLen64 = 0;
}
}
+ std::vector<std::shared_ptr<RWGltf_CafWriter::Mesh>> aMeshes;
Standard_Integer aNbAccessors = 0;
NCollection_Map<Handle(RWGltf_GltfFaceList)> aWrittenFaces;
NCollection_DataMap<TopoDS_Shape, Handle(RWGltf_GltfFace), TopTools_ShapeMapHasher> aWrittenPrimData;
aBuffView->ByteOffset = aBinFile->tellp();
aWrittenFaces.Clear (false);
aWrittenPrimData.Clear (false);
+ size_t aMeshIndex = 0;
for (ShapeToGltfFaceMap::Iterator aBinDataIter (myBinDataMap); aBinDataIter.More() && aPSentryBin.More(); aBinDataIter.Next())
{
const Handle(RWGltf_GltfFaceList)& aGltfFaceList = aBinDataIter.Value();
{
continue;
}
+
+ std::shared_ptr<RWGltf_CafWriter::Mesh> aMeshPtr;
+ ++aMeshIndex;
+ #ifdef HAVE_DRACO
+ if (myDracoParameters.DracoCompression)
+ {
+ if (aMeshIndex <= aMeshes.size())
+ {
+ aMeshPtr = aMeshes.at(aMeshIndex - 1);
+ }
+ else
+ {
+ aMeshes.push_back(std::make_shared<RWGltf_CafWriter::Mesh>(RWGltf_CafWriter::Mesh()));
+ aMeshPtr = aMeshes.back();
+ }
+ }
+ #endif
for (RWGltf_GltfFaceList::Iterator aGltfFaceIter (*aGltfFaceList); aGltfFaceIter.More() && aPSentryBin.More(); aGltfFaceIter.Next())
{
case RWGltf_GltfArrayType_Position:
{
aGltfFace->NbIndexedNodes = 0; // reset to zero before RWGltf_GltfArrayType_Indices step
- saveNodes (*aGltfFace, *aBinFile, aFaceIter, aNbAccessors);
+ saveNodes (*aGltfFace, *aBinFile, aFaceIter, aNbAccessors, aMeshPtr);
break;
}
case RWGltf_GltfArrayType_Normal:
{
- saveNormals (*aGltfFace, *aBinFile, aFaceIter, aNbAccessors);
+ saveNormals (*aGltfFace, *aBinFile, aFaceIter, aNbAccessors, aMeshPtr);
break;
}
case RWGltf_GltfArrayType_TCoord0:
{
- saveTextCoords (*aGltfFace, *aBinFile, aFaceIter, aNbAccessors);
+ saveTextCoords (*aGltfFace, *aBinFile, aFaceIter, aNbAccessors, aMeshPtr);
break;
}
case RWGltf_GltfArrayType_Indices:
{
- saveIndices (*aGltfFace, *aBinFile, aFaceIter, aNbAccessors);
+ saveIndices (*aGltfFace, *aBinFile, aFaceIter, aNbAccessors, aMeshPtr);
break;
}
default:
}
// add alignment by 4 bytes (might happen on RWGltf_GltfAccessorCompType_UInt16 indices)
- int64_t aContentLen64 = (int64_t)aBinFile->tellp();
- while (aContentLen64 % 4 != 0)
+ if (!myDracoParameters.DracoCompression)
{
- aBinFile->write (" ", 1);
- ++aContentLen64;
+ int64_t aContentLen64 = (int64_t)aBinFile->tellp();
+ while (aContentLen64 % 4 != 0)
+ {
+ aBinFile->write(" ", 1);
+ ++aContentLen64;
+ }
}
}
}
- aBuffView->ByteLength = (int64_t )aBinFile->tellp() - aBuffView->ByteOffset;
+ if (!myDracoParameters.DracoCompression)
+ {
+ aBuffView->ByteLength = (int64_t)aBinFile->tellp() - aBuffView->ByteOffset;
+ }
if (!aPSentryBin.More())
{
return false;
aPSentryBin.Next();
}
+ if (myDracoParameters.DracoCompression)
+ {
+#ifdef HAVE_DRACO
+ OSD_Timer aDracoTimer;
+ aDracoTimer.Start();
+ int aBuffId = 0;
+ draco::Encoder aDracoEncoder;
+ aDracoEncoder.SetAttributeQuantization (draco::GeometryAttribute::POSITION, myDracoParameters.QuantizePositionBits);
+ aDracoEncoder.SetAttributeQuantization (draco::GeometryAttribute::NORMAL, myDracoParameters.QuantizeNormalBits);
+ aDracoEncoder.SetAttributeQuantization (draco::GeometryAttribute::TEX_COORD, myDracoParameters.QuantizeTexcoordBits);
+ aDracoEncoder.SetAttributeQuantization (draco::GeometryAttribute::COLOR, myDracoParameters.QuantizeColorBits);
+ aDracoEncoder.SetAttributeQuantization (draco::GeometryAttribute::GENERIC, myDracoParameters.QuantizeGenericBits);
+ aDracoEncoder.SetSpeedOptions (myDracoParameters.CompressionLevel, myDracoParameters.CompressionLevel);
+ for (size_t aMeshInd = 0; aMeshInd != aMeshes.size(); ++aMeshInd)
+ {
+ const std::shared_ptr<RWGltf_CafWriter::Mesh>& aCurrentMesh = aMeshes[aMeshInd];
+ if (aCurrentMesh->NodesVec.empty())
+ {
+ continue;
+ }
+
+ draco::Mesh aDracoMesh;
+ writeNodesToDracoMesh (aDracoMesh, aCurrentMesh->NodesVec);
+ if (!aCurrentMesh->NormalsVec.empty())
+ {
+ writeNormalsToDracoMesh (aDracoMesh, aCurrentMesh->NormalsVec);
+ }
+ if (!aCurrentMesh->TexCoordsVec.empty())
+ {
+ writeTexCoordsToDracoMesh (aDracoMesh, aCurrentMesh->TexCoordsVec);
+ }
+ writeIndicesToDracoMesh (aDracoMesh, aCurrentMesh->IndicesVec);
+
+ draco::EncoderBuffer anEncoderBuff;
+ draco::Status aStatus = aDracoEncoder.EncodeMeshToBuffer (aDracoMesh, &anEncoderBuff);
+ if (!aStatus.ok())
+ {
+ Message::SendFail (TCollection_AsciiString("Error: mesh cannot be encoded in draco buffer."));
+ return false;
+ }
+
+ RWGltf_GltfBufferView aBuffViewDraco;
+ aBuffViewDraco.Id = aBuffId++;
+ aBuffViewDraco.ByteOffset = aBinFile->tellp();
+ aBinFile->write (anEncoderBuff.data(), std::streamsize(anEncoderBuff.size()));
+ if (!aBinFile->good())
+ {
+ Message::SendFail (TCollection_AsciiString("File '") + myBinFileNameFull + "' cannot be written");
+ return false;
+ }
+
+ int64_t aLength = (int64_t)aBinFile->tellp();
+ while (aLength % 4 != 0)
+ {
+ aBinFile->write(" ", 1);
+ ++aLength;
+ }
+
+ aBuffViewDraco.ByteLength = aLength - aBuffViewDraco.ByteOffset;
+ myBuffViewsDraco.push_back (aBuffViewDraco);
+ }
+ aDracoTimer.Stop();
+ Message::SendInfo (TCollection_AsciiString("Draco compression time: ") + aDracoTimer.ElapsedTime() + " s");
+#endif
+ }
+
if (myIsBinary
&& myToEmbedTexturesInGlb)
{
}
myWriter->StartObject();
- myWriter->Key ("bufferView");
- myWriter->Int (myBuffViewPos.Id);
- myWriter->Key ("byteOffset");
- myWriter->Int64 (theGltfFace.NodePos.ByteOffset);
+ if (!myDracoParameters.DracoCompression)
+ {
+ myWriter->Key ("bufferView");
+ myWriter->Int (myBuffViewPos.Id);
+ myWriter->Key ("byteOffset");
+ myWriter->Int64 (theGltfFace.NodePos.ByteOffset);
+ }
myWriter->Key ("componentType");
myWriter->Int (theGltfFace.NodePos.ComponentType);
myWriter->Key ("count");
}
myWriter->StartObject();
- myWriter->Key ("bufferView");
- myWriter->Int (myBuffViewNorm.Id);
- myWriter->Key ("byteOffset");
- myWriter->Int64 (theGltfFace.NodeNorm.ByteOffset);
+ if (!myDracoParameters.DracoCompression)
+ {
+ myWriter->Key ("bufferView");
+ myWriter->Int (myBuffViewNorm.Id);
+ myWriter->Key ("byteOffset");
+ myWriter->Int64 (theGltfFace.NodeNorm.ByteOffset);
+ }
myWriter->Key ("componentType");
myWriter->Int (theGltfFace.NodeNorm.ComponentType);
myWriter->Key ("count");
}
myWriter->StartObject();
- myWriter->Key ("bufferView");
- myWriter->Int (myBuffViewTextCoord.Id);
- myWriter->Key ("byteOffset");
- myWriter->Int64 (theGltfFace.NodeUV.ByteOffset);
+ if (!myDracoParameters.DracoCompression)
+ {
+ myWriter->Key ("bufferView");
+ myWriter->Int (myBuffViewTextCoord.Id);
+ myWriter->Key ("byteOffset");
+ myWriter->Int64 (theGltfFace.NodeUV.ByteOffset);
+ }
myWriter->Key ("componentType");
myWriter->Int (theGltfFace.NodeUV.ComponentType);
myWriter->Key ("count");
}
myWriter->StartObject();
- myWriter->Key ("bufferView");
- myWriter->Int (myBuffViewInd.Id);
- myWriter->Key ("byteOffset");
- myWriter->Int64 (theGltfFace.Indices.ByteOffset);
+ if (!myDracoParameters.DracoCompression)
+ {
+ myWriter->Key("bufferView");
+ myWriter->Int(myBuffViewInd.Id);
+ myWriter->Key("byteOffset");
+ myWriter->Int64(theGltfFace.Indices.ByteOffset);
+ }
myWriter->Key ("componentType");
myWriter->Int (theGltfFace.Indices.ComponentType);
myWriter->Key ("count");
myWriter->Int (myBuffViewInd.Target);
myWriter->EndObject();
}
+ if (myDracoParameters.DracoCompression)
+ {
+ for (size_t aBufInd = 0; aBufInd != myBuffViewsDraco.size(); ++aBufInd)
+ {
+ if (myBuffViewsDraco[aBufInd].Id != RWGltf_GltfAccessor::INVALID_ID)
+ {
+ aBuffViewId++;
+ myWriter->StartObject();
+ myWriter->Key("buffer");
+ myWriter->Int(theBinDataBufferId);
+ myWriter->Key("byteLength");
+ myWriter->Int64(myBuffViewsDraco[aBufInd].ByteLength);
+ myWriter->Key("byteOffset");
+ myWriter->Int64(myBuffViewsDraco[aBufInd].ByteOffset);
+ myWriter->EndObject();
+ }
+ }
+ }
myMaterialMap->FlushGlbBufferViews (myWriter.get(), theBinDataBufferId, aBuffViewId);
// =======================================================================
void RWGltf_CafWriter::writeExtensions()
{
+#ifdef HAVE_RAPIDJSON
Standard_ProgramError_Raise_if (myWriter.get() == NULL, "Internal error: RWGltf_CafWriter::writeExtensions()");
+
+ if (myDracoParameters.DracoCompression)
+ {
+ myWriter->Key(RWGltf_GltfRootElementName(RWGltf_GltfRootElement_ExtensionsUsed));
+
+ myWriter->StartArray();
+ {
+ myWriter->Key("KHR_draco_mesh_compression");
+ }
+ myWriter->EndArray();
+
+ myWriter->Key(RWGltf_GltfRootElementName(RWGltf_GltfRootElement_ExtensionsRequired));
+
+ myWriter->StartArray();
+ {
+ myWriter->Key("KHR_draco_mesh_compression");
+ }
+ myWriter->EndArray();
+ }
+#endif
}
// =======================================================================
// =======================================================================
void RWGltf_CafWriter::writePrimArray (const RWGltf_GltfFace& theGltfFace,
const TCollection_AsciiString& theName,
+ const int theDracoBufInd,
bool& theToStartPrims)
{
#ifdef HAVE_RAPIDJSON
}
myWriter->Key ("mode");
myWriter->Int (RWGltf_GltfPrimitiveMode_Triangles);
+
+ if (myDracoParameters.DracoCompression)
+ {
+ myWriter->Key("extensions");
+ myWriter->StartObject();
+ {
+ myWriter->Key("KHR_draco_mesh_compression");
+ myWriter->StartObject();
+ myWriter->Key("bufferView");
+ myWriter->Int(myBuffViewsDraco[theDracoBufInd].Id);
+ myWriter->Key("attributes");
+ myWriter->StartObject();
+ {
+ int anAttrInd = 0;
+ if (theGltfFace.NodePos.Id != RWGltf_GltfAccessor::INVALID_ID)
+ {
+ myWriter->Key("POSITION");
+ myWriter->Int(anAttrInd++);
+ }
+ if (theGltfFace.NodeNorm.Id != RWGltf_GltfAccessor::INVALID_ID)
+ {
+ myWriter->Key("NORMAL");
+ myWriter->Int(anAttrInd++);
+ }
+ if (theGltfFace.NodeUV.Id != RWGltf_GltfAccessor::INVALID_ID)
+ {
+ myWriter->Key("TEXCOORD_0");
+ myWriter->Int(anAttrInd++);
+ }
+ }
+ myWriter->EndObject();
+ myWriter->EndObject();
+ }
+ myWriter->EndObject();
+ }
}
myWriter->EndObject();
#else
(void )theGltfFace;
(void )theName;
(void )theToStartPrims;
+ (void )theDracoBufInd;
#endif
}
myWriter->Key (RWGltf_GltfRootElementName (RWGltf_GltfRootElement_Meshes));
myWriter->StartArray();
+ int aDracoBufInd = 0;
+ NCollection_IndexedDataMap<int, int> aDracoBufIndMap;
NCollection_Map<Handle(RWGltf_GltfFaceList)> aWrittenFaces;
for (RWGltf_GltfSceneNodeMap::Iterator aSceneNodeIter (theSceneNodeMap); aSceneNodeIter.More(); aSceneNodeIter.Next())
{
for (RWGltf_GltfFaceList::Iterator aFaceGroupIter (*aGltfFaceList); aFaceGroupIter.More(); aFaceGroupIter.Next())
{
const Handle(RWGltf_GltfFace)& aGltfFace = aFaceGroupIter.Value();
- writePrimArray (*aGltfFace, aNodeName, toStartPrims);
+ const int aPrevSize = aDracoBufIndMap.Size();
+ const int aTempDracoBufInd = aDracoBufInd;
+ if (myDracoParameters.DracoCompression
+ && !aDracoBufIndMap.FindFromKey (aGltfFace->NodePos.Id, aDracoBufInd))
+ {
+ aDracoBufIndMap.Add (aGltfFace->NodePos.Id, aDracoBufInd);
+ }
+
+ writePrimArray (*aGltfFace, aNodeName, aDracoBufInd, toStartPrims);
+ if (aTempDracoBufInd != aDracoBufInd)
+ {
+ aDracoBufInd = aTempDracoBufInd;
+ }
+ if (!myDracoParameters.DracoCompression || aDracoBufIndMap.Size() > aPrevSize)
+ {
+ ++aDracoBufInd;
+ }
}
}
else
}
const Handle(RWGltf_GltfFace)& aGltfFace = aGltfFaceList->First();
- writePrimArray (*aGltfFace, aNodeName, toStartPrims);
+ const int aPrevSize = aDracoBufIndMap.Size();
+ const int aTempDracoBufInd = aDracoBufInd;
+ if (myDracoParameters.DracoCompression
+ && !aDracoBufIndMap.FindFromKey(aGltfFace->NodePos.Id, aDracoBufInd))
+ {
+ aDracoBufIndMap.Add(aGltfFace->NodePos.Id, aDracoBufInd);
+ }
+
+ writePrimArray (*aGltfFace, aNodeName, aDracoBufInd, toStartPrims);
+ if (aTempDracoBufInd != aDracoBufInd)
+ {
+ aDracoBufInd = aTempDracoBufInd;
+ }
+ if (!myDracoParameters.DracoCompression || aDracoBufIndMap.Size() > aPrevSize)
+ {
+ ++aDracoBufInd;
+ }
}
}
#include <TColStd_MapOfAsciiString.hxx>
#include <TDF_LabelSequence.hxx>
#include <TopTools_ShapeMapHasher.hxx>
+#include <RWGltf_DracoParameters.hxx>
#include <RWGltf_GltfBufferView.hxx>
#include <RWGltf_GltfFace.hxx>
#include <RWGltf_WriterTrsfFormat.hxx>
#include <RWMesh_CoordinateSystemConverter.hxx>
#include <RWMesh_NameFormat.hxx>
#include <XCAFPrs_Style.hxx>
+#include <Poly_Triangle.hxx>
#include <memory>
DEFINE_STANDARD_RTTIEXT(RWGltf_CafWriter, Standard_Transient)
public:
+ //! Mesh
+ struct Mesh
+ {
+ std::vector<Graphic3d_Vec3> NodesVec; //!< vector for mesh nodes
+ std::vector<Graphic3d_Vec3> NormalsVec; //!< vector for mesh normals
+ std::vector<Graphic3d_Vec2> TexCoordsVec; //!< vector for mesh texture UV coordinates
+ std::vector<Poly_Triangle> IndicesVec; //!< vector for mesh indices
+ };
+
//! Main constructor.
//! @param theFile [in] path to output glTF file
//! @param theIsBinary [in] flag to write into binary glTF format (.glb)
//! May reduce binary data size thanks to smaller triangle indexes.
void SetSplitIndices16 (bool theToSplit) { myToSplitIndices16 = theToSplit; }
+ //! Return Draco parameters
+ const RWGltf_DracoParameters& CompressionParameters() const { return myDracoParameters; }
+
+ //! Set Draco parameters
+ void SetCompressionParameters(const RWGltf_DracoParameters& theDracoParameters) { myDracoParameters = theDracoParameters; }
+
//! Write glTF file and associated binary file.
//! Triangulation data should be precomputed within shapes!
//! @param theDocument [in] input document
//! @param theBinFile [out] output file to write into
//! @param theFaceIter [in] current face to write
//! @param theAccessorNb [in] [out] last accessor index
+ //! @param theMesh [in] [out] mesh
Standard_EXPORT virtual void saveNodes (RWGltf_GltfFace& theGltfFace,
std::ostream& theBinFile,
const RWMesh_FaceIterator& theFaceIter,
- Standard_Integer& theAccessorNb) const;
+ Standard_Integer& theAccessorNb,
+ const std::shared_ptr<RWGltf_CafWriter::Mesh>& theMesh) const;
//! Write mesh normals into binary file.
//! @param theGltfFace [out] glTF face definition
//! @param theBinFile [out] output file to write into
//! @param theFaceIter [in] current face to write
//! @param theAccessorNb [in] [out] last accessor index
+ //! @param theMesh [in] [out] mesh
Standard_EXPORT virtual void saveNormals (RWGltf_GltfFace& theGltfFace,
std::ostream& theBinFile,
RWMesh_FaceIterator& theFaceIter,
- Standard_Integer& theAccessorNb) const;
+ Standard_Integer& theAccessorNb,
+ const std::shared_ptr<RWGltf_CafWriter::Mesh>& theMesh) const;
//! Write mesh texture UV coordinates into binary file.
//! @param theGltfFace [out] glTF face definition
//! @param theBinFile [out] output file to write into
//! @param theFaceIter [in] current face to write
//! @param theAccessorNb [in] [out] last accessor index
+ //! @param theMesh [in] [out] mesh
Standard_EXPORT virtual void saveTextCoords (RWGltf_GltfFace& theGltfFace,
std::ostream& theBinFile,
const RWMesh_FaceIterator& theFaceIter,
- Standard_Integer& theAccessorNb) const;
+ Standard_Integer& theAccessorNb,
+ const std::shared_ptr<RWGltf_CafWriter::Mesh>& theMesh) const;
//! Write mesh indexes into binary file.
//! @param theGltfFace [out] glTF face definition
//! @param theBinFile [out] output file to write into
//! @param theFaceIter [in] current face to write
//! @param theAccessorNb [in] [out] last accessor index
+ //! @param theMesh [in] [out] mesh
Standard_EXPORT virtual void saveIndices (RWGltf_GltfFace& theGltfFace,
std::ostream& theBinFile,
const RWMesh_FaceIterator& theFaceIter,
- Standard_Integer& theAccessorNb);
+ Standard_Integer& theAccessorNb,
+ const std::shared_ptr<RWGltf_CafWriter::Mesh>& theMesh);
protected:
//! Write a primitive array to RWGltf_GltfRootElement_Meshes section.
//! @param[in] theGltfFace face to write
//! @param[in] theName primitive array name
+ //! @param[in] theDracoBufInd draco buffer index
//! @param[in,out] theToStartPrims flag indicating that primitive array has been started
Standard_EXPORT virtual void writePrimArray (const RWGltf_GltfFace& theGltfFace,
const TCollection_AsciiString& theName,
+ const int theDracoBufInd,
bool& theToStartPrims);
//! Write RWGltf_GltfRootElement_Nodes section.
ShapeToGltfFaceMap myBinDataMap; //!< map for TopoDS_Face to glTF face (merging duplicates)
int64_t myBinDataLen64; //!< length of binary file
+ std::vector<RWGltf_GltfBufferView> myBuffViewsDraco; //!< vector of buffers view with compression data
+ RWGltf_DracoParameters myDracoParameters; //!< Draco parameters
};
#endif // _RWGltf_CafWriter_HeaderFiler
#include <Quantity_Color.hxx>
#include <Quantity_HArray1OfColor.hxx>
#include <Quantity_NameOfColor.hxx>
+#include <RWGltf_DracoParameters.hxx>
#include <RWGltf_CafReader.hxx>
#include <RWGltf_CafWriter.hxx>
#include <RWMesh_FaceIterator.hxx>
bool toMergeFaces = false, toSplitIndices16 = false;
RWMesh_NameFormat aNodeNameFormat = RWMesh_NameFormat_InstanceOrProduct;
RWMesh_NameFormat aMeshNameFormat = RWMesh_NameFormat_Product;
+ RWGltf_DracoParameters aDracoParameters;
for (Standard_Integer anArgIter = 1; anArgIter < theNbArgs; ++anArgIter)
{
TCollection_AsciiString anArgCase (theArgVec[anArgIter]);
{
toEmbedTexturesInGlb = false;
}
+ else if (anArgCase == "-draco")
+ {
+ aDracoParameters.DracoCompression = Draw::ParseOnOffIterator(theNbArgs, theArgVec, anArgIter);
+ }
+ else if (anArgCase == "-compressionlevel" && (anArgIter + 1) < theNbArgs
+ && Draw::ParseInteger(theArgVec[anArgIter + 1], aDracoParameters.CompressionLevel))
+ {
+ ++anArgIter;
+ }
+ else if (anArgCase == "-quantizepositionbits" && (anArgIter + 1) < theNbArgs
+ && Draw::ParseInteger(theArgVec[anArgIter + 1], aDracoParameters.QuantizePositionBits))
+ {
+ ++anArgIter;
+ }
+ else if (anArgCase == "-quantizenormalbits" && (anArgIter + 1) < theNbArgs
+ && Draw::ParseInteger(theArgVec[anArgIter + 1], aDracoParameters.QuantizeNormalBits))
+ {
+ ++anArgIter;
+ }
+ else if (anArgCase == "-quantizetexcoordbits" && (anArgIter + 1) < theNbArgs
+ && Draw::ParseInteger(theArgVec[anArgIter + 1], aDracoParameters.QuantizeTexcoordBits))
+ {
+ ++anArgIter;
+ }
+ else if (anArgCase == "-quantizecolorbits" && (anArgIter + 1) < theNbArgs
+ && Draw::ParseInteger(theArgVec[anArgIter + 1], aDracoParameters.QuantizeColorBits))
+ {
+ ++anArgIter;
+ }
+ else if (anArgCase == "-quantizegenericbits" && (anArgIter + 1) < theNbArgs
+ && Draw::ParseInteger(theArgVec[anArgIter + 1], aDracoParameters.QuantizeGenericBits))
+ {
+ ++anArgIter;
+ }
+ else if (anArgCase == "-unifiedquantization")
+ {
+ aDracoParameters.UnifiedQuantization = Draw::ParseOnOffIterator(theNbArgs, theArgVec, anArgIter);
+ }
else
{
Message::SendFail() << "Syntax error at '" << theArgVec[anArgIter] << "'";
aWriter.SetToEmbedTexturesInGlb (toEmbedTexturesInGlb);
aWriter.SetMergeFaces (toMergeFaces);
aWriter.SetSplitIndices16 (toSplitIndices16);
+ aWriter.SetCompressionParameters(aDracoParameters);
aWriter.ChangeCoordinateSystemConverter().SetInputLengthUnit (aScaleFactorM);
aWriter.ChangeCoordinateSystemConverter().SetInputCoordinateSystem (aSystemCoordSys);
aWriter.Perform (aDoc, aFileInfo, aProgress->Start());
__FILE__, ReadGltf, g);
theCommands.Add ("WriteGltf",
"WriteGltf Doc file [-trsfFormat {compact|TRS|mat4}]=compact"
- "\n\t\t: [-systemCoordSys {Zup|Yup}]=Zup"
- "\n\t\t: [-comments Text] [-author Name]"
- "\n\t\t: [-forceUVExport]=0 [-texturesSeparate]=0 [-mergeFaces]=0 [-splitIndices16]=0"
- "\n\t\t: [-nodeNameFormat {empty|product|instance|instOrProd|prodOrInst|prodAndInst|verbose}]=instOrProd"
- "\n\t\t: [-meshNameFormat {empty|product|instance|instOrProd|prodOrInst|prodAndInst|verbose}]=product"
- "\n\t\t: Write XDE document into glTF file."
- "\n\t\t: -trsfFormat preferred transformation format"
- "\n\t\t: -systemCoordSys system coordinate system; Zup when not specified"
- "\n\t\t: -mergeFaces merge Faces within the same Mesh"
- "\n\t\t: -splitIndices16 split Faces to keep 16-bit indices when -mergeFaces is enabled"
- "\n\t\t: -forceUVExport always export UV coordinates"
- "\n\t\t: -texturesSeparate write textures to separate files"
- "\n\t\t: -nodeNameFormat name format for Nodes"
- "\n\t\t: -meshNameFormat name format for Meshes",
+ "\n\t\t: [-systemCoordSys {Zup|Yup}]=Zup"
+ "\n\t\t: [-comments Text] [-author Name]"
+ "\n\t\t: [-forceUVExport]=0 [-texturesSeparate]=0 [-mergeFaces]=0 [-splitIndices16]=0"
+ "\n\t\t: [-nodeNameFormat {empty|product|instance|instOrProd|prodOrInst|prodAndInst|verbose}]=instOrProd"
+ "\n\t\t: [-meshNameFormat {empty|product|instance|instOrProd|prodOrInst|prodAndInst|verbose}]=product"
+ "\n\t\t: [-draco]=0 [-compressionLevel {0-10}]=7 [-quantizePositionBits Value]=14 [-quantizeNormalBits Value]=10"
+ "\n\t\t: [-quantizeTexcoordBits Value]=12 [-quantizeColorBits Value]=8 [-quantizeGenericBits Value]=12"
+ "\n\t\t: [-unifiedQuantization]=0"
+ "\n\t\t: Write XDE document into glTF file."
+ "\n\t\t: -trsfFormat preferred transformation format"
+ "\n\t\t: -systemCoordSys system coordinate system; Zup when not specified"
+ "\n\t\t: -mergeFaces merge Faces within the same Mesh"
+ "\n\t\t: -splitIndices16 split Faces to keep 16-bit indices when -mergeFaces is enabled"
+ "\n\t\t: -forceUVExport always export UV coordinates"
+ "\n\t\t: -texturesSeparate write textures to separate files"
+ "\n\t\t: -nodeNameFormat name format for Nodes"
+ "\n\t\t: -meshNameFormat name format for Meshes"
+ "\n\t\t: -draco use Draco compression 3D geometric meshes"
+ "\n\t\t: -compressionLevel draco compression level [0-10] (by default 7), a value of 0 will apply sequential encoding and preserve face order"
+ "\n\t\t: -quantizePositionBits quantization bits for position attribute when using Draco compression (by default 14)"
+ "\n\t\t: -quantizeNormalBits quantization bits for normal attribute when using Draco compression (by default 10)"
+ "\n\t\t: -quantizeTexcoordBits quantization bits for texture coordinate attribute when using Draco compression (by default 12)"
+ "\n\t\t: -quantizeColorBits quantization bits for color attribute when using Draco compression (by default 8)"
+ "\n\t\t: -quantizeGenericBits quantization bits for skinning attribute (joint indices and joint weights)"
+ "\n and custom attributes when using Draco compression (by default 12)"
+ "\n\t\t: -unifiedQuantization quantization is applied on each primitive separately if this option is false",
__FILE__, WriteGltf, g);
theCommands.Add ("writegltf",
"writegltf shape file",