From 3a2ca49b6d5dffd6064cc035243c32b98b40efff Mon Sep 17 00:00:00 2001 From: ichesnok Date: Tue, 29 Mar 2022 17:02:02 +0300 Subject: [PATCH] 0032867: Data Exchange - Implement Draco compression for writing glTF Draco compression added in RWGltf_CafWriter class. --- adm/cmake/draco.cmake | 94 +++++- src/RWGltf/FILES | 1 + src/RWGltf/RWGltf_CafWriter.cxx | 447 +++++++++++++++++++++++--- src/RWGltf/RWGltf_CafWriter.hxx | 37 ++- src/RWGltf/RWGltf_DracoParameters.hxx | 41 +++ src/XSDRAWSTLVRML/XSDRAWSTLVRML.cxx | 81 ++++- tests/de_mesh/gltf_write/010 | 16 + tests/de_mesh/gltf_write/Diamond | 16 + tests/de_mesh/gltf_write/as1draco | 18 ++ tests/de_mesh/gltf_write/bearing | 16 + tests/de_mesh/gltf_write/bull | 16 + tests/de_mesh/gltf_write/screw | 18 ++ tests/de_mesh/gltf_write/soapbox | 17 + tests/de_mesh/gltf_write/test | 20 ++ 14 files changed, 770 insertions(+), 68 deletions(-) create mode 100644 src/RWGltf/RWGltf_DracoParameters.hxx create mode 100644 tests/de_mesh/gltf_write/010 create mode 100644 tests/de_mesh/gltf_write/Diamond create mode 100644 tests/de_mesh/gltf_write/as1draco create mode 100644 tests/de_mesh/gltf_write/bearing create mode 100644 tests/de_mesh/gltf_write/bull create mode 100644 tests/de_mesh/gltf_write/screw create mode 100644 tests/de_mesh/gltf_write/soapbox create mode 100644 tests/de_mesh/gltf_write/test diff --git a/adm/cmake/draco.cmake b/adm/cmake/draco.cmake index 53cf90a954..755391fa52 100644 --- a/adm/cmake/draco.cmake +++ b/adm/cmake/draco.cmake @@ -1,4 +1,96 @@ # Draco - a library for a lossy vertex data compression, used as extension to glTF format. # https://github.com/google/draco -THIRDPARTY_PRODUCT("DRACO" "draco/compression/decode.h" "CSF_Draco" "") +OCCT_INCLUDE_CMAKE_FILE ("adm/cmake/occt_macros") + +if (NOT DEFINED INSTALL_DRACO) + set (INSTALL_DRACO OFF CACHE BOOL "${INSTALL_DRACO_DESCR}") +endif() + +if (NOT DEFINED 3RDPARTY_DRACO_DIR) + set (3RDPARTY_DRACO_DIR "" CACHE PATH "The directory containing Draco") +endif() + +if (NOT DEFINED 3RDPARTY_DRACO_INCLUDE_DIR) + set (3RDPARTY_DRACO_INCLUDE_DIR "" CACHE PATH "The directory containing headers of the Draco") +endif() + +if (NOT DEFINED 3RDPARTY_DRACO_LIBRARY OR NOT 3RDPARTY_DRACO_LIBRARY_DIR OR NOT EXISTS "${3RDPARTY_DRACO_LIBRARY_DIR}") + set (3RDPARTY_DRACO_LIBRARY "" CACHE FILEPATH "Draco library" FORCE) +endif() + +if (NOT DEFINED 3RDPARTY_DRACO_LIBRARY_DIR) + set (3RDPARTY_DRACO_LIBRARY_DIR "" CACHE PATH "The directory containing Draco library") +endif() + +if (3RDPARTY_DIR AND EXISTS "${3RDPARTY_DIR}") + if (NOT 3RDPARTY_DRACO_DIR OR NOT EXISTS "${3RDPARTY_DRACO_DIR}") + FIND_PRODUCT_DIR("${3RDPARTY_DIR}" draco DRACO_DIR_NAME) + if (DRACO_DIR_NAME) + set (3RDPARTY_DRACO_DIR "${3RDPARTY_DIR}/${DRACO_DIR_NAME}" CACHE PATH "The directory containing Draco" FORCE) + endif() + endif() +endif() + +if (3RDPARTY_DRACO_DIR AND EXISTS "${3RDPARTY_DRACO_DIR}") + set (DRACO_INCLUDE_PATH "${3RDPARTY_DRACO_DIR}/include") + set (DRACO_LIBRARY_PATH "${3RDPARTY_DRACO_DIR}/lib") +endif() + +if (NOT 3RDPARTY_DRACO_INCLUDE_DIR) + if (DRACO_INCLUDE_PATH AND EXISTS "${DRACO_INCLUDE_PATH}") + set (3RDPARTY_DRACO_INCLUDE_DIR "${DRACO_INCLUDE_PATH}" CACHE FILEPATH "The directory containing headers of DRACO" FORCE) + endif() +endif() + +if (NOT 3RDPARTY_DRACO_LIBRARY_DIR) + if (DRACO_LIBRARY_PATH AND EXISTS "${DRACO_LIBRARY_PATH}") + set (3RDPARTY_DRACO_LIBRARY_DIR "${DRACO_LIBRARY_PATH}" CACHE FILEPATH "The directory containing DRACO library" FORCE) + endif() +endif() + +if (3RDPARTY_DRACO_INCLUDE_DIR AND EXISTS "${3RDPARTY_DRACO_INCLUDE_DIR}") + list (APPEND 3RDPARTY_INCLUDE_DIRS "${3RDPARTY_DRACO_INCLUDE_DIR}") +else() + list (APPEND 3RDPARTY_NOT_INCLUDED 3RDPARTY_DRACO_INCLUDE_DIR) +endif() + +if (3RDPARTY_DRACO_DIR AND EXISTS "${3RDPARTY_DRACO_DIR}") + if (NOT 3RDPARTY_DRACO_LIBRARY OR NOT EXISTS "${3RDPARTY_DRACO_LIBRARY}") + set (CMAKE_FIND_LIBRARY_SUFFIXES .lib .so .dylib .a) + set (3RDPARTY_DRACO_LIBRARY "3RDPARTY_DRACO_LIBRARY-NOTFOUND" CACHE FILEPATH "The path to Draco library" FORCE) + + find_library (3RDPARTY_DRACO_LIBRARY NAMES ${CSF_Draco} + PATHS "${3RDPARTY_DRACO_LIBRARY_DIR}" + PATH_SUFFIXES lib + CMAKE_FIND_ROOT_PATH_BOTH + NO_DEFAULT_PATH) + if (3RDPARTY_DRACO_LIBRARY AND EXISTS "${3RDPARTY_DRACO_LIBRARY}") + get_filename_component (3RDPARTY_DRACO_LIBRARY_DIR "${3RDPARTY_DRACO_LIBRARY}" PATH) + set (3RDPARTY_DRACO_LIBRARY_DIR "${3RDPARTY_DRACO_LIBRARY_DIR}" CACHE FILEPATH "The directory containing Draco library" FORCE) + endif() + endif() +endif() + +if (3RDPARTY_DRACO_LIBRARY_DIR AND EXISTS "${3RDPARTY_DRACO_LIBRARY_DIR}") + list (APPEND 3RDPARTY_LIBRARY_DIRS "${3RDPARTY_DRACO_LIBRARY_DIR}") +else() + list (APPEND 3RDPARTY_NO_LIBS 3RDPARTY_DRACO_LIBRARY_DIR) +endif() + +if (INSTALL_DRACO) + get_filename_component(3RDPARTY_DRACO_LIBRARY_REALPATH ${3RDPARTY_DRACO_LIBRARY} REALPATH) + if (SINGLE_GENERATOR) + install (FILES ${3RDPARTY_DRACO_LIBRARY_REALPATH} DESTINATION "${INSTALL_DIR_LIB}") + else() + install (FILES ${3RDPARTY_DRACO_LIBRARY_REALPATH} + CONFIGURATIONS Release + DESTINATION "${INSTALL_DIR_LIB}") + install (FILES ${3RDPARTY_DRACO_LIBRARY_REALPATH} + CONFIGURATIONS RelWithDebInfo + DESTINATION "${INSTALL_DIR_LIB}i") + install (FILES ${3RDPARTY_DRACO_LIBRARY_REALPATH} + CONFIGURATIONS Debug + DESTINATION "${INSTALL_DIR_LIB}d") + endif() +endif() diff --git a/src/RWGltf/FILES b/src/RWGltf/FILES index 5d580a2052..fc24654adf 100644 --- a/src/RWGltf/FILES +++ b/src/RWGltf/FILES @@ -4,6 +4,7 @@ RWGltf_CafWriter.cxx RWGltf_CafWriter.hxx RWGltf_ConfigurationNode.cxx RWGltf_ConfigurationNode.hxx +RWGltf_DracoParameters.hxx RWGltf_GltfAccessor.hxx RWGltf_GltfAccessorCompType.hxx RWGltf_GltfAccessorLayout.hxx diff --git a/src/RWGltf/RWGltf_CafWriter.cxx b/src/RWGltf/RWGltf_CafWriter.cxx index 5e0c8cf0ba..836d15b86e 100644 --- a/src/RWGltf/RWGltf_CafWriter.cxx +++ b/src/RWGltf/RWGltf_CafWriter.cxx @@ -22,7 +22,7 @@ #include #include #include -#include +#include #include #include #include @@ -44,6 +44,10 @@ #include #endif +#ifdef HAVE_DRACO + #include +#endif + IMPLEMENT_STANDARD_RTTIEXT(RWGltf_CafWriter, Standard_Transient) namespace @@ -84,6 +88,84 @@ 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& 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& 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& 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& 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 } //================================================================ @@ -150,7 +232,8 @@ Standard_Boolean RWGltf_CafWriter::toSkipFaceMesh (const RWMesh_FaceIterator& th 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& theMesh) const { if (theGltfFace.NodePos.Id == RWGltf_GltfAccessor::INVALID_ID) { @@ -161,8 +244,11 @@ void RWGltf_CafWriter::saveNodes (RWGltf_GltfFace& theGltfFace, } 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(); @@ -172,7 +258,14 @@ void RWGltf_CafWriter::saveNodes (RWGltf_GltfFace& theGltfFace, 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); + } } } @@ -183,7 +276,8 @@ void RWGltf_CafWriter::saveNodes (RWGltf_GltfFace& theGltfFace, void RWGltf_CafWriter::saveNormals (RWGltf_GltfFace& theGltfFace, std::ostream& theBinFile, RWMesh_FaceIterator& theFaceIter, - Standard_Integer& theAccessorNb) const + Standard_Integer& theAccessorNb, + const std::shared_ptr& theMesh) const { if (!theFaceIter.HasNormals()) { @@ -199,8 +293,11 @@ void RWGltf_CafWriter::saveNormals (RWGltf_GltfFace& theGltfFace, } 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(); @@ -210,7 +307,14 @@ void RWGltf_CafWriter::saveNormals (RWGltf_GltfFace& theGltfFace, 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); + } } } @@ -221,7 +325,8 @@ void RWGltf_CafWriter::saveNormals (RWGltf_GltfFace& theGltfFace, 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& theMesh) const { if (!theFaceIter.HasTexCoords()) { @@ -253,8 +358,11 @@ void RWGltf_CafWriter::saveTextCoords (RWGltf_GltfFace& theGltfFace, } 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(); @@ -263,7 +371,14 @@ void RWGltf_CafWriter::saveTextCoords (RWGltf_GltfFace& theGltfFace, { 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()); + } } } @@ -274,7 +389,8 @@ void RWGltf_CafWriter::saveTextCoords (RWGltf_GltfFace& theGltfFace, void RWGltf_CafWriter::saveIndices (RWGltf_GltfFace& theGltfFace, std::ostream& theBinFile, const RWMesh_FaceIterator& theFaceIter, - Standard_Integer& theAccessorNb) + Standard_Integer& theAccessorNb, + const std::shared_ptr& theMesh) { if (theGltfFace.Indices.Id == RWGltf_GltfAccessor::INVALID_ID) { @@ -287,11 +403,14 @@ void RWGltf_CafWriter::saveIndices (RWGltf_GltfFace& theGltfFace, } 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(); @@ -306,13 +425,20 @@ void RWGltf_CafWriter::saveIndices (RWGltf_GltfFace& theGltfFace, 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)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)aTri(1), (uint16_t)aTri(2), (uint16_t)aTri(3))); + } + else + { + writeTriangle32(theBinFile, Graphic3d_Vec3i(aTri(1), aTri(2), aTri(3))); + } } } } @@ -373,6 +499,14 @@ bool RWGltf_CafWriter::writeBinData (const Handle(TDocStd_Document)& theDocument 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; @@ -396,6 +530,8 @@ bool RWGltf_CafWriter::writeBinData (const Handle(TDocStd_Document)& theDocument myBuffViewInd.ByteLength = 0; myBuffViewInd.Target = RWGltf_GltfBufferViewTarget_ELEMENT_ARRAY_BUFFER; + myBuffViewsDraco.clear(); + myBinDataMap.Clear(); myBinDataLen64 = 0; @@ -508,6 +644,7 @@ bool RWGltf_CafWriter::writeBinData (const Handle(TDocStd_Document)& theDocument } } + std::vector> aMeshes; Standard_Integer aNbAccessors = 0; NCollection_Map aWrittenFaces; NCollection_DataMap aWrittenPrimData; @@ -526,6 +663,7 @@ bool RWGltf_CafWriter::writeBinData (const Handle(TDocStd_Document)& theDocument 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(); @@ -533,6 +671,23 @@ bool RWGltf_CafWriter::writeBinData (const Handle(TDocStd_Document)& theDocument { continue; } + + std::shared_ptr 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())); + aMeshPtr = aMeshes.back(); + } + } + #endif for (RWGltf_GltfFaceList::Iterator aGltfFaceIter (*aGltfFaceList); aGltfFaceIter.More() && aPSentryBin.More(); aGltfFaceIter.Next()) { @@ -579,22 +734,22 @@ bool RWGltf_CafWriter::writeBinData (const Handle(TDocStd_Document)& theDocument 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: @@ -611,16 +766,22 @@ bool RWGltf_CafWriter::writeBinData (const Handle(TDocStd_Document)& theDocument } // 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; @@ -629,6 +790,72 @@ bool RWGltf_CafWriter::writeBinData (const Handle(TDocStd_Document)& theDocument 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& 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) { @@ -1020,10 +1247,13 @@ void RWGltf_CafWriter::writePositions (const RWGltf_GltfFace& theGltfFace) } 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"); @@ -1068,10 +1298,13 @@ void RWGltf_CafWriter::writeNormals (const RWGltf_GltfFace& theGltfFace) } 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"); @@ -1116,10 +1349,13 @@ void RWGltf_CafWriter::writeTextCoords (const RWGltf_GltfFace& theGltfFace) } 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"); @@ -1164,10 +1400,13 @@ void RWGltf_CafWriter::writeIndices (const RWGltf_GltfFace& theGltfFace) } 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"); @@ -1309,6 +1548,24 @@ void RWGltf_CafWriter::writeBufferViews (const Standard_Integer theBinDataBuffer 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); @@ -1352,7 +1609,28 @@ void RWGltf_CafWriter::writeBuffers() // ======================================================================= 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 } // ======================================================================= @@ -1425,6 +1703,7 @@ void RWGltf_CafWriter::writeMaterials (const RWGltf_GltfSceneNodeMap& theSceneNo // ======================================================================= void RWGltf_CafWriter::writePrimArray (const RWGltf_GltfFace& theGltfFace, const TCollection_AsciiString& theName, + const int theDracoBufInd, bool& theToStartPrims) { #ifdef HAVE_RAPIDJSON @@ -1471,12 +1750,48 @@ void RWGltf_CafWriter::writePrimArray (const RWGltf_GltfFace& theGltfFace, } 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 } @@ -1492,6 +1807,8 @@ void RWGltf_CafWriter::writeMeshes (const RWGltf_GltfSceneNodeMap& theSceneNodeM myWriter->Key (RWGltf_GltfRootElementName (RWGltf_GltfRootElement_Meshes)); myWriter->StartArray(); + int aDracoBufInd = 0; + NCollection_IndexedDataMap aDracoBufIndMap; NCollection_Map aWrittenFaces; for (RWGltf_GltfSceneNodeMap::Iterator aSceneNodeIter (theSceneNodeMap); aSceneNodeIter.More(); aSceneNodeIter.Next()) { @@ -1522,7 +1839,23 @@ void RWGltf_CafWriter::writeMeshes (const RWGltf_GltfSceneNodeMap& theSceneNodeM 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 @@ -1542,7 +1875,23 @@ void RWGltf_CafWriter::writeMeshes (const RWGltf_GltfSceneNodeMap& theSceneNodeM } 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; + } } } diff --git a/src/RWGltf/RWGltf_CafWriter.hxx b/src/RWGltf/RWGltf_CafWriter.hxx index 6e8b976733..be1d8a344d 100644 --- a/src/RWGltf/RWGltf_CafWriter.hxx +++ b/src/RWGltf/RWGltf_CafWriter.hxx @@ -18,12 +18,14 @@ #include #include #include +#include #include #include #include #include #include #include +#include #include @@ -40,6 +42,15 @@ class RWGltf_CafWriter : public Standard_Transient DEFINE_STANDARD_RTTIEXT(RWGltf_CafWriter, Standard_Transient) public: + //! Mesh + struct Mesh + { + std::vector NodesVec; //!< vector for mesh nodes + std::vector NormalsVec; //!< vector for mesh normals + std::vector TexCoordsVec; //!< vector for mesh texture UV coordinates + std::vector 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) @@ -114,6 +125,12 @@ public: //! 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 @@ -186,40 +203,48 @@ protected: //! @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& 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& 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& 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& theMesh); protected: @@ -280,9 +305,11 @@ 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. @@ -369,6 +396,8 @@ protected: ShapeToGltfFaceMap myBinDataMap; //!< map for TopoDS_Face to glTF face (merging duplicates) int64_t myBinDataLen64; //!< length of binary file + std::vector myBuffViewsDraco; //!< vector of buffers view with compression data + RWGltf_DracoParameters myDracoParameters; //!< Draco parameters }; #endif // _RWGltf_CafWriter_HeaderFiler diff --git a/src/RWGltf/RWGltf_DracoParameters.hxx b/src/RWGltf/RWGltf_DracoParameters.hxx new file mode 100644 index 0000000000..7e3f48349a --- /dev/null +++ b/src/RWGltf/RWGltf_DracoParameters.hxx @@ -0,0 +1,41 @@ +// Copyright (c) 2022 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. + +#ifndef _RWGltf_DracoParameters_HeaderFile +#define _RWGltf_DracoParameters_HeaderFile + +//! Draco compression parameters +struct RWGltf_DracoParameters +{ + RWGltf_DracoParameters() + : DracoCompression (false), + CompressionLevel (7), + QuantizePositionBits (14), + QuantizeNormalBits (10), + QuantizeTexcoordBits (12), + QuantizeColorBits (8), + QuantizeGenericBits (12), + UnifiedQuantization (false) + {} + + bool DracoCompression; //!< flag to use Draco compression (FALSE by default). If it is TRUE, compression is used + int CompressionLevel; //!< Draco compression level [0-10] (7 by default) + int QuantizePositionBits; //!< quantization bits for position attribute (14 by default) + int QuantizeNormalBits; //!< quantization bits for normal attribute (10 by default) + int QuantizeTexcoordBits; //!< quantization bits for texture coordinate attribute (12 by default) + int QuantizeColorBits; //!< quantization bits for color attributes (8 by default) + int QuantizeGenericBits; //!< quantization bits for skinning and custom attributes (12 by default) + bool UnifiedQuantization; //!< quantize positions of all primitives using the same quantization grid (FALSE by default) +}; + +#endif diff --git a/src/XSDRAWSTLVRML/XSDRAWSTLVRML.cxx b/src/XSDRAWSTLVRML/XSDRAWSTLVRML.cxx index 5abe0d7b87..7976a23ecb 100644 --- a/src/XSDRAWSTLVRML/XSDRAWSTLVRML.cxx +++ b/src/XSDRAWSTLVRML/XSDRAWSTLVRML.cxx @@ -44,6 +44,7 @@ #include #include #include +#include #include #include #include @@ -384,6 +385,7 @@ static Standard_Integer WriteGltf (Draw_Interpretor& theDI, 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]); @@ -516,6 +518,44 @@ static Standard_Integer WriteGltf (Draw_Interpretor& theDI, { 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] << "'"; @@ -547,6 +587,7 @@ static Standard_Integer WriteGltf (Draw_Interpretor& theDI, 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()); @@ -2361,20 +2402,32 @@ void XSDRAWSTLVRML::InitCommands (Draw_Interpretor& theCommands) __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", diff --git a/tests/de_mesh/gltf_write/010 b/tests/de_mesh/gltf_write/010 new file mode 100644 index 0000000000..b9b5d1be71 --- /dev/null +++ b/tests/de_mesh/gltf_write/010 @@ -0,0 +1,16 @@ +puts "========" +puts "0032867: Data Exchange - Implement Draco compression for writing glTF" +puts "Test case exporting model into glb (binary glTF) file." +puts "========" + +Close D0 -silent +ReadGltf D0 [locate_data_file bug32867_010.glb] + +set aGltfFile1 "${imagedir}/${casename}_tmp1.glb" +set aGltfFile2 "${imagedir}/${casename}_tmp2.glb" + +WriteGltf D0 "$aGltfFile1" -draco on -mergefaces +WriteGltf D0 "$aGltfFile2" -draco on + +ReadGltf D1 "$aGltfFile1" +ReadGltf D "$aGltfFile2" diff --git a/tests/de_mesh/gltf_write/Diamond b/tests/de_mesh/gltf_write/Diamond new file mode 100644 index 0000000000..f020c335eb --- /dev/null +++ b/tests/de_mesh/gltf_write/Diamond @@ -0,0 +1,16 @@ +puts "========" +puts "0032867: Data Exchange - Implement Draco compression for writing glTF" +puts "Test case exporting model into glb (binary glTF) file." +puts "========" + +Close D0 -silent +ReadGltf D0 [locate_data_file bug32867_Diamond.glb] + +set aGltfFile1 "${imagedir}/${casename}_tmp1.glb" +set aGltfFile2 "${imagedir}/${casename}_tmp2.glb" + +WriteGltf D0 "$aGltfFile1" -draco on +WriteGltf D0 "$aGltfFile2" -draco on -mergefaces + +ReadGltf D1 "$aGltfFile1" +ReadGltf D "$aGltfFile2" diff --git a/tests/de_mesh/gltf_write/as1draco b/tests/de_mesh/gltf_write/as1draco new file mode 100644 index 0000000000..f07a25a3c9 --- /dev/null +++ b/tests/de_mesh/gltf_write/as1draco @@ -0,0 +1,18 @@ +puts "========" +puts "0032867: Data Exchange - Implement Draco compression for writing glTF" +puts "Test case exporting model into glb (binary glTF) file." +puts "========" + +Close D0 -silent +ReadStep D0 [locate_data_file as1-oc-214-mat.stp] +XGetOneShape ss D0 +incmesh ss 1.0 + +set aGltfFile1 "${imagedir}/${casename}_tmp1.glb" +set aGltfFile2 "${imagedir}/${casename}_tmp2.glb" + +WriteGltf D0 "$aGltfFile1" -draco on +WriteGltf D0 "$aGltfFile2" -draco on -mergefaces + +ReadGltf D1 "$aGltfFile1" +ReadGltf D "$aGltfFile2" diff --git a/tests/de_mesh/gltf_write/bearing b/tests/de_mesh/gltf_write/bearing new file mode 100644 index 0000000000..46d8ae6d15 --- /dev/null +++ b/tests/de_mesh/gltf_write/bearing @@ -0,0 +1,16 @@ +puts "========" +puts "0032867: Data Exchange - Implement Draco compression for writing glTF" +puts "Test case exporting model into glb (binary glTF) file." +puts "========" + +restore [locate_data_file bearing.brep] b +incmesh b 0.1 + +set aGltfFile1 "${imagedir}/${casename}_tmp1.glb" +set aGltfFile2 "${imagedir}/${casename}_tmp2.glb" + +WriteGltf b "$aGltfFile1" -draco on +WriteGltf b "$aGltfFile2" -draco on -mergefaces + +ReadGltf D0 "$aGltfFile1" +ReadGltf D "$aGltfFile2" diff --git a/tests/de_mesh/gltf_write/bull b/tests/de_mesh/gltf_write/bull new file mode 100644 index 0000000000..5d7de77f3c --- /dev/null +++ b/tests/de_mesh/gltf_write/bull @@ -0,0 +1,16 @@ +puts "========" +puts "0032867: Data Exchange - Implement Draco compression for writing glTF" +puts "Test case exporting model into glb (binary glTF) file." +puts "========" + +Close D0 -silent +ReadGltf D0 [locate_data_file bug32867_bull.glb] + +set aGltfFile1 "${imagedir}/${casename}_tmp1.glb" +set aGltfFile2 "${imagedir}/${casename}_tmp2.glb" + +WriteGltf D0 "$aGltfFile1" -draco on +WriteGltf D0 "$aGltfFile2" -draco on -mergefaces + +ReadGltf D1 "$aGltfFile1" +ReadGltf D "$aGltfFile2" diff --git a/tests/de_mesh/gltf_write/screw b/tests/de_mesh/gltf_write/screw new file mode 100644 index 0000000000..fa7bee51c8 --- /dev/null +++ b/tests/de_mesh/gltf_write/screw @@ -0,0 +1,18 @@ +puts "========" +puts "0032867: Data Exchange - Implement Draco compression for writing glTF" +puts "Test case exporting model into glb (binary glTF) file." +puts "========" + +Close D0 -silent +ReadStep D0 [locate_data_file screw.step] +XGetOneShape ss D0 +incmesh ss 1.0 + +set aGltfFile1 "${imagedir}/${casename}_tmp1.glb" +set aGltfFile2 "${imagedir}/${casename}_tmp2.glb" + +WriteGltf D0 "$aGltfFile1" -draco on -mergefaces +WriteGltf D0 "$aGltfFile2" -draco on + +ReadGltf D1 "$aGltfFile1" +ReadGltf D "$aGltfFile2" \ No newline at end of file diff --git a/tests/de_mesh/gltf_write/soapbox b/tests/de_mesh/gltf_write/soapbox new file mode 100644 index 0000000000..2ec1901886 --- /dev/null +++ b/tests/de_mesh/gltf_write/soapbox @@ -0,0 +1,17 @@ +puts "========" +puts "0032867: Data Exchange - Implement Draco compression for writing glTF" +puts "Test case exporting model into glb (binary glTF) file." +puts "========" + +ReadStep D0 [locate_data_file ec_soapbox-A.stp] +XGetOneShape ss D0 +incmesh ss 1.0 + +set aGltfFile1 "${imagedir}/${casename}_tmp1.glb" +set aGltfFile2 "${imagedir}/${casename}_tmp2.glb" + +WriteGltf D0 "$aGltfFile1" -draco on +WriteGltf D0 "$aGltfFile2" -draco on -mergefaces + +ReadGltf D1 "$aGltfFile1" +ReadGltf D "$aGltfFile2" \ No newline at end of file diff --git a/tests/de_mesh/gltf_write/test b/tests/de_mesh/gltf_write/test new file mode 100644 index 0000000000..708cfe088e --- /dev/null +++ b/tests/de_mesh/gltf_write/test @@ -0,0 +1,20 @@ +puts "========" +puts "0032867: Data Exchange - Implement Draco compression for writing glTF" +puts "Test case exporting model into glb (binary glTF) file." +puts "========" + +Close D0 -silent +ReadGltf D0 [locate_data_file bug32867_test.glb] + +set aGltfFile1 "${imagedir}/${casename}_tmp1.glb" +set aGltfFile2 "${imagedir}/${casename}_tmp2.glb" + +WriteGltf D0 "$aGltfFile1" -draco on +WriteGltf D0 "$aGltfFile2" -draco on -mergefaces + +ReadGltf D1 "$aGltfFile1" +XGetOneShape s1 D1 +checktrinfo s1 -tri 9366 +ReadGltf D "$aGltfFile2" +XGetOneShape s2 D +checktrinfo s2 -tri 9366