1
0
mirror of https://git.dev.opencascade.org/repos/occt.git synced 2025-05-16 10:54:53 +03:00

Data Exchange, Gltf Reader - Implement non-uniform scaling in Gltf Import #503

Apply non-uniform scale factors directly to the triangulation during Gltf import.
Add flag for turning on/off (on by default) this behavior.
OCP-1948
This commit is contained in:
ikochetkova 2025-04-24 14:20:36 +01:00 committed by GitHub
parent 3ee97f70d0
commit 7cd39973a4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 429 additions and 22 deletions

View File

@ -100,6 +100,8 @@ bool DEGLTF_ConfigurationNode::Load(const Handle(DE_ConfigurationContext)& theRe
theResource->BooleanVal("read.print.debug.message", theResource->BooleanVal("read.print.debug.message",
InternalParameters.ReadPrintDebugMessages, InternalParameters.ReadPrintDebugMessages,
aScope); aScope);
InternalParameters.ReadApplyScale =
theResource->BooleanVal("read.apply.scale", InternalParameters.ReadApplyScale, aScope);
InternalParameters.WriteComment = InternalParameters.WriteComment =
theResource->StringVal("write.comment", InternalParameters.WriteComment, aScope); theResource->StringVal("write.comment", InternalParameters.WriteComment, aScope);
@ -260,6 +262,13 @@ TCollection_AsciiString DEGLTF_ConfigurationNode::Save() const
aScope + "read.print.debug.message :\t " + InternalParameters.ReadPrintDebugMessages + "\n"; aScope + "read.print.debug.message :\t " + InternalParameters.ReadPrintDebugMessages + "\n";
aResult += "!\n"; aResult += "!\n";
aResult += "!\n";
aResult +=
"!Flag to apply non-uniform transformation directly to the triangulation (modify nodes)\n";
aResult += "!Default value: 1(true). Available values: 0(false), 1(true)\n";
aResult += aScope + "read.apply.scale :\t " + InternalParameters.ReadApplyScale + "\n";
aResult += "!\n";
aResult += "!\n"; aResult += "!\n";
aResult += "!Write parameters:\n"; aResult += "!Write parameters:\n";
aResult += "!\n"; aResult += "!\n";

View File

@ -101,6 +101,7 @@ public:
bool ReadSkipLateDataLoading = false; //!< Flag to skip triangulation loading bool ReadSkipLateDataLoading = false; //!< Flag to skip triangulation loading
bool ReadKeepLateData = true;//!< Flag to keep information about deferred storage to load/unload triangulation later bool ReadKeepLateData = true;//!< Flag to keep information about deferred storage to load/unload triangulation later
bool ReadPrintDebugMessages = false; //!< Flag to print additional debug information bool ReadPrintDebugMessages = false; //!< Flag to print additional debug information
bool ReadApplyScale = true; //!< Flag to apply non-uniform scale factor to the triangulations (modify nodes coordinates)
// Writing // Writing
TCollection_AsciiString WriteComment; //!< Export special comment TCollection_AsciiString WriteComment; //!< Export special comment
TCollection_AsciiString WriteAuthor; //!< Author of exported file name TCollection_AsciiString WriteAuthor; //!< Author of exported file name

View File

@ -41,6 +41,7 @@ static void SetReaderParameters(RWGltf_CafReader& theReade
theReader.SetToSkipLateDataLoading(theNode->InternalParameters.ReadSkipLateDataLoading); theReader.SetToSkipLateDataLoading(theNode->InternalParameters.ReadSkipLateDataLoading);
theReader.SetToKeepLateData(theNode->InternalParameters.ReadKeepLateData); theReader.SetToKeepLateData(theNode->InternalParameters.ReadKeepLateData);
theReader.SetToPrintDebugMessages(theNode->InternalParameters.ReadPrintDebugMessages); theReader.SetToPrintDebugMessages(theNode->InternalParameters.ReadPrintDebugMessages);
theReader.SetToApplyScale(theNode->InternalParameters.ReadApplyScale);
} }
} // namespace } // namespace

View File

@ -26,6 +26,11 @@
#include <OSD_FileSystem.hxx> #include <OSD_FileSystem.hxx>
#include <OSD_ThreadPool.hxx> #include <OSD_ThreadPool.hxx>
#include <RWGltf_GltfLatePrimitiveArray.hxx> #include <RWGltf_GltfLatePrimitiveArray.hxx>
#include <TDocStd_Document.hxx>
#include <TopoDS.hxx>
#include <XCAFDoc_DocumentTool.hxx>
#include <XCAFDoc_ShapeMapTool.hxx>
#include <XCAFDoc_ShapeTool.hxx>
IMPLEMENT_STANDARD_RTTIEXT(RWGltf_CafReader, RWMesh_CafReader) IMPLEMENT_STANDARD_RTTIEXT(RWGltf_CafReader, RWMesh_CafReader)
@ -170,7 +175,8 @@ RWGltf_CafReader::RWGltf_CafReader()
myIsDoublePrecision(false), myIsDoublePrecision(false),
myToSkipLateDataLoading(false), myToSkipLateDataLoading(false),
myToKeepLateData(true), myToKeepLateData(true),
myToPrintDebugMessages(false) myToPrintDebugMessages(false),
myToApplyScale(true)
{ {
myCoordSysConverter.SetInputLengthUnit(1.0); // glTF defines model in meters myCoordSysConverter.SetInputLengthUnit(1.0); // glTF defines model in meters
myCoordSysConverter.SetInputCoordinateSystem(RWMesh_CoordinateSystem_glTF); myCoordSysConverter.SetInputCoordinateSystem(RWMesh_CoordinateSystem_glTF);
@ -299,6 +305,9 @@ Standard_Boolean RWGltf_CafReader::performMesh(std::istream& th
{ {
aDoc.SetBinaryFormat(aBinBodyOffset, aBinBodyLen); aDoc.SetBinaryFormat(aBinBodyOffset, aBinBodyLen);
} }
myShapeScaleMap = new NCollection_DataMap<TopoDS_Shape, gp_XYZ, TopTools_ShapeMapHasher>();
aDoc.SetToApplyScale(myToApplyScale);
aDoc.SetScaleMap(*myShapeScaleMap);
#ifdef HAVE_RAPIDJSON #ifdef HAVE_RAPIDJSON
rapidjson::ParseResult aRes; rapidjson::ParseResult aRes;
@ -418,3 +427,263 @@ void RWGltf_CafReader::updateLateDataReader(
} }
} }
} }
//=================================================================================================
void RWGltf_CafReader::fillDocument()
{
if (!myToFillDoc || myXdeDoc.IsNull() || myRootShapes.IsEmpty())
{
return;
}
// set units
Standard_Real aLengthUnit = 1.;
if (!XCAFDoc_DocumentTool::GetLengthUnit(myXdeDoc, aLengthUnit))
{
XCAFDoc_DocumentTool::SetLengthUnit(myXdeDoc, SystemLengthUnit());
}
else if (aLengthUnit != SystemLengthUnit())
{
Message::SendWarning("Warning: Length unit of document not equal to the system length unit");
}
const Standard_Boolean wasAutoNaming = XCAFDoc_ShapeTool::AutoNaming();
XCAFDoc_ShapeTool::SetAutoNaming(Standard_False);
const TCollection_AsciiString aRootName; // = generateRootName (theFile);
CafDocumentTools aTools;
aTools.ShapeTool = XCAFDoc_DocumentTool::ShapeTool(myXdeDoc->Main());
aTools.ColorTool = XCAFDoc_DocumentTool::ColorTool(myXdeDoc->Main());
aTools.VisMaterialTool = XCAFDoc_DocumentTool::VisMaterialTool(myXdeDoc->Main());
for (TopTools_SequenceOfShape::Iterator aRootIter(myRootShapes); aRootIter.More();
aRootIter.Next())
{
addShapeIntoDoc(aTools, aRootIter.Value(), TDF_Label(), aRootName);
}
XCAFDoc_DocumentTool::ShapeTool(myXdeDoc->Main())->UpdateAssemblies();
XCAFDoc_ShapeTool::SetAutoNaming(wasAutoNaming);
}
//=================================================================================================
Standard_Boolean RWGltf_CafReader::addShapeIntoDoc(CafDocumentTools& theTools,
const TopoDS_Shape& theShape,
const TDF_Label& theLabel,
const TCollection_AsciiString& theParentName,
const Standard_Boolean theHasScale,
const gp_XYZ& theScale)
{
if (theShape.IsNull() || myXdeDoc.IsNull())
{
return Standard_False;
}
const TopAbs_ShapeEnum aShapeType = theShape.ShapeType();
TopoDS_Shape aShapeToAdd = theShape;
const TopoDS_Shape aShapeNoLoc = theShape.Located(TopLoc_Location());
Standard_Boolean toMakeAssembly = Standard_False;
bool isShapeScaled = myShapeScaleMap->IsBound(theShape);
gp_XYZ aCurScale;
if (theHasScale)
{
// update translation part
gp_Trsf aTrsf = theShape.Location().Transformation();
gp_XYZ aTranslation = aTrsf.TranslationPart();
aTranslation.SetX(aTranslation.X() * theScale.X());
aTranslation.SetY(aTranslation.Y() * theScale.Y());
aTranslation.SetZ(aTranslation.Z() * theScale.Z());
aTrsf.SetTranslationPart(aTranslation);
aShapeToAdd.Location(TopLoc_Location(aTrsf));
}
if (isShapeScaled && aShapeType == TopAbs_FACE)
{
// Scale triangulation
aCurScale = myShapeScaleMap->Find(theShape);
TopLoc_Location aLoc;
TopoDS_Face aFace = TopoDS::Face(aShapeToAdd);
myShapeScaleMap->UnBind(theShape);
const Handle(Poly_Triangulation)& aPolyTri = BRep_Tool::Triangulation(aFace, aLoc);
if (!aPolyTri.IsNull())
{
for (int aNodeIdx = 1; aNodeIdx <= aPolyTri->NbNodes(); ++aNodeIdx)
{
gp_Pnt aNode = aPolyTri->Node(aNodeIdx);
aNode.SetX(aNode.X() * aCurScale.X());
aNode.SetY(aNode.Y() * aCurScale.Y());
aNode.SetZ(aNode.Z() * aCurScale.Z());
aPolyTri->SetNode(aNodeIdx, aNode);
}
}
}
if (theShape.ShapeType() == TopAbs_COMPOUND)
{
RWMesh_NodeAttributes aSubFaceAttribs;
for (TopoDS_Iterator aSubShapeIter(theShape, Standard_True, Standard_False);
!toMakeAssembly && aSubShapeIter.More();
aSubShapeIter.Next())
{
if (aSubShapeIter.Value().ShapeType() != TopAbs_FACE)
{
toMakeAssembly = Standard_True;
break;
}
const TopoDS_Face& aFace = TopoDS::Face(aSubShapeIter.Value());
toMakeAssembly =
toMakeAssembly
|| (myAttribMap.Find(aFace, aSubFaceAttribs) && !aSubFaceAttribs.Name.IsEmpty());
}
if (toMakeAssembly)
{
// create an empty Compound to add as assembly, so that we can add children one-by-one via
// AddComponent()
TopoDS_Compound aCompound;
BRep_Builder aBuilder;
aBuilder.MakeCompound(aCompound);
aCompound.Location(theShape.Location(), Standard_False);
aShapeToAdd = aCompound;
}
}
TDF_Label aNewLabel, anOldLabel;
if (theLabel.IsNull())
{
// add new shape
aNewLabel = theTools.ShapeTool->AddShape(aShapeToAdd, toMakeAssembly);
}
else if (theTools.ShapeTool->IsAssembly(theLabel))
{
// add shape as component
if (theTools.ComponentMap.Find(aShapeNoLoc, anOldLabel))
{
aNewLabel = theTools.ShapeTool->AddComponent(theLabel, anOldLabel, theShape.Location());
}
else
{
aNewLabel = theTools.ShapeTool->AddComponent(theLabel, aShapeToAdd, toMakeAssembly);
TDF_Label aRefLabel = aNewLabel;
theTools.ShapeTool->GetReferredShape(aNewLabel, aRefLabel);
if (!aRefLabel.IsNull())
{
theTools.ComponentMap.Bind(aShapeNoLoc, aRefLabel);
}
}
}
else
{
// add shape as sub-shape
aNewLabel = theTools.ShapeTool->AddSubShape(theLabel, theShape);
if (!aNewLabel.IsNull())
{
Handle(XCAFDoc_ShapeMapTool) aShapeMapTool = XCAFDoc_ShapeMapTool::Set(aNewLabel);
aShapeMapTool->SetShape(theShape);
}
}
if (aNewLabel.IsNull())
{
return Standard_False;
}
if (toMakeAssembly)
{
TDF_Label aRefLabel;
theTools.ShapeTool->GetReferredShape(aNewLabel, aRefLabel);
if (!aRefLabel.IsNull())
{
theTools.OriginalShapeMap.Bind(theShape, aRefLabel);
}
}
// if new label is a reference get referred shape
TDF_Label aNewRefLabel = aNewLabel;
theTools.ShapeTool->GetReferredShape(aNewLabel, aNewRefLabel);
RWMesh_NodeAttributes aRefShapeAttribs;
myAttribMap.Find(aShapeNoLoc, aRefShapeAttribs);
bool hasProductName = false;
if (aNewLabel != aNewRefLabel)
{
// put attributes to the Instance (overrides Product attributes)
RWMesh_NodeAttributes aShapeAttribs;
if (!theShape.Location().IsIdentity() && myAttribMap.Find(theShape, aShapeAttribs))
{
if (!aShapeAttribs.Style.IsEqual(aRefShapeAttribs.Style))
{
setShapeStyle(theTools, aNewLabel, aShapeAttribs.Style);
}
if (aShapeAttribs.NamedData != aRefShapeAttribs.NamedData)
{
setShapeNamedData(theTools, aNewLabel, aShapeAttribs.NamedData);
}
setShapeName(aNewLabel, aShapeType, aShapeAttribs.Name, theLabel, theParentName);
if (aRefShapeAttribs.Name.IsEmpty() && !aShapeAttribs.Name.IsEmpty())
{
// it is not nice having unnamed Product, so copy name from first Instance (probably the
// only one)
hasProductName = true;
setShapeName(aNewRefLabel, aShapeType, aShapeAttribs.Name, theLabel, theParentName);
}
else if (aShapeAttribs.Name.IsEmpty() && !aRefShapeAttribs.Name.IsEmpty())
{
// copy name from Product
setShapeName(aNewLabel, aShapeType, aRefShapeAttribs.Name, theLabel, theParentName);
}
}
else
{
// copy name from Product
setShapeName(aNewLabel, aShapeType, aRefShapeAttribs.Name, theLabel, theParentName);
}
}
if (!anOldLabel.IsNull())
{
// already defined in the document
return Standard_True;
}
// put attributes to the Product (shared across Instances)
if (!hasProductName)
{
setShapeName(aNewRefLabel, aShapeType, aRefShapeAttribs.Name, theLabel, theParentName);
}
setShapeStyle(theTools, aNewRefLabel, aRefShapeAttribs.Style);
setShapeNamedData(theTools, aNewRefLabel, aRefShapeAttribs.NamedData);
if (theTools.ShapeTool->IsAssembly(aNewRefLabel))
{
bool aHasScale = theHasScale | isShapeScaled;
gp_XYZ aScale = theScale;
if (isShapeScaled)
{
aScale.SetX(aCurScale.X() * theScale.X());
aScale.SetY(aCurScale.Y() * theScale.Y());
aScale.SetZ(aCurScale.Z() * theScale.Z());
}
// store sub-shapes (iterator is set to not inherit Location of parent object)
TCollection_AsciiString aDummyName;
for (TopoDS_Iterator aSubShapeIter(theShape, Standard_True, Standard_False);
aSubShapeIter.More();
aSubShapeIter.Next())
{
addShapeIntoDoc(theTools, aSubShapeIter.Value(), aNewRefLabel, aDummyName, aHasScale, aScale);
}
}
else
{
// store a plain list of sub-shapes in case if they have custom attributes (usually per-face
// color)
for (TopoDS_Iterator aSubShapeIter(theShape, Standard_True, Standard_False);
aSubShapeIter.More();
aSubShapeIter.Next())
{
addSubShapeIntoDoc(theTools, aSubShapeIter.Value(), aNewRefLabel);
}
}
return Standard_True;
}

View File

@ -45,6 +45,10 @@ public:
//! main (default) scene will be loaded. //! main (default) scene will be loaded.
bool ToLoadAllScenes() const { return myToLoadAllScenes; } bool ToLoadAllScenes() const { return myToLoadAllScenes; }
//! Return TRUE if non-uniform scaling should be applied directly to the triangulation.
//! FALSE if the average scale should be applied to the transformation matrix.
bool ToApplyScale() const { return myToApplyScale; }
//! Set flag to flag to load all scenes in the document, FALSE by default which means only main //! Set flag to flag to load all scenes in the document, FALSE by default which means only main
//! (default) scene will be loaded. //! (default) scene will be loaded.
void SetLoadAllScenes(bool theToLoadAll) { myToLoadAllScenes = theToLoadAll; } void SetLoadAllScenes(bool theToLoadAll) { myToLoadAllScenes = theToLoadAll; }
@ -67,6 +71,10 @@ public:
//! Sets flag to skip data loading. //! Sets flag to skip data loading.
void SetToSkipLateDataLoading(bool theToSkip) { myToSkipLateDataLoading = theToSkip; } void SetToSkipLateDataLoading(bool theToSkip) { myToSkipLateDataLoading = theToSkip; }
//! Set flag to apply non-uniform scaling directly to the triangulation (modify nodes).
//! TRUE by default. In case of FALSE the average scale is applied to the transformation matrix.
void SetToApplyScale(bool theToApplyScale) { myToApplyScale = theToApplyScale; }
//! Returns TRUE if data should be loaded into itself without its transferring to new structure. //! Returns TRUE if data should be loaded into itself without its transferring to new structure.
//! It allows to keep information about deferred storage to load/unload this data later. //! It allows to keep information about deferred storage to load/unload this data later.
//! TRUE by default. //! TRUE by default.
@ -92,6 +100,17 @@ protected:
const Standard_Boolean theToProbe) const Standard_Boolean theToProbe)
Standard_OVERRIDE; Standard_OVERRIDE;
//! Fill document with new root shapes.
Standard_EXPORT virtual void fillDocument() Standard_OVERRIDE;
//! Append new shape into the document (recursively).
Standard_EXPORT Standard_Boolean addShapeIntoDoc(CafDocumentTools& theTools,
const TopoDS_Shape& theShape,
const TDF_Label& theLabel,
const TCollection_AsciiString& theParentName,
const Standard_Boolean theHasScale = false,
const gp_XYZ& theScale = gp_XYZ(0., 0., 0.));
//! Create primitive array reader context. //! Create primitive array reader context.
//! Can be overridden by sub-class to read triangulation into application-specific data structures //! Can be overridden by sub-class to read triangulation into application-specific data structures
//! instead of Poly_Triangulation. Default implementation creates RWGltf_TriangulationReader. //! instead of Poly_Triangulation. Default implementation creates RWGltf_TriangulationReader.
@ -123,6 +142,9 @@ protected:
Standard_Boolean myToKeepLateData; //!< flag to keep information about deferred storage to load/unload triangulation later Standard_Boolean myToKeepLateData; //!< flag to keep information about deferred storage to load/unload triangulation later
// clang-format on // clang-format on
Standard_Boolean myToPrintDebugMessages; //!< flag to print additional debug information Standard_Boolean myToPrintDebugMessages; //!< flag to print additional debug information
Standard_Boolean myToApplyScale; //!< flag to apply non-uniform scaling
NCollection_DataMap<TopoDS_Shape, gp_XYZ, TopTools_ShapeMapHasher>*
myShapeScaleMap; //!< map of shapes with non-uniform scalings
}; };
#endif // _RWGltf_CafReader_HeaderFile #endif // _RWGltf_CafReader_HeaderFile

View File

@ -15,6 +15,7 @@
#include "RWGltf_GltfJsonParser.hxx" #include "RWGltf_GltfJsonParser.hxx"
#include <BRep_Builder.hxx> #include <BRep_Builder.hxx>
#include <BRepBuilderAPI_Copy.hxx>
#include <FSD_Base64.hxx> #include <FSD_Base64.hxx>
#include <Message.hxx> #include <Message.hxx>
#include <Message_Messenger.hxx> #include <Message_Messenger.hxx>
@ -504,8 +505,11 @@ bool RWGltf_GltfJsonParser::parseTransformationComponents(
const RWGltf_JsonValue* theRotationVal, const RWGltf_JsonValue* theRotationVal,
const RWGltf_JsonValue* theScaleVal, const RWGltf_JsonValue* theScaleVal,
const RWGltf_JsonValue* theTranslationVal, const RWGltf_JsonValue* theTranslationVal,
TopLoc_Location& theResult) const TopLoc_Location& theResult,
bool& theHasScale,
gp_XYZ& theScale) const
{ {
theHasScale = false;
gp_Trsf aTrsf; gp_Trsf aTrsf;
if (theRotationVal != NULL) if (theRotationVal != NULL)
{ {
@ -584,13 +588,16 @@ bool RWGltf_GltfJsonParser::parseTransformationComponents(
|| Abs(aScaleVec.y() - aScaleVec.z()) > Precision::Confusion() || Abs(aScaleVec.y() - aScaleVec.z()) > Precision::Confusion()
|| Abs(aScaleVec.x() - aScaleVec.z()) > Precision::Confusion()) || Abs(aScaleVec.x() - aScaleVec.z()) > Precision::Confusion())
{ {
Graphic3d_Mat4d aScaleMat;
aScaleMat.SetDiagonal(aScaleVec);
Graphic3d_Mat4d aMat4; Graphic3d_Mat4d aMat4;
aTrsf.GetMat4(aMat4); aTrsf.GetMat4(aMat4);
if (!myToApplyScale)
{
Graphic3d_Mat4d aScaleMat;
aScaleMat.SetDiagonal(aScaleVec);
aMat4 = aMat4 * aScaleMat; aMat4 = aMat4 * aScaleMat;
}
aTrsf = gp_Trsf(); aTrsf = gp_Trsf();
aTrsf.SetValues(aMat4.GetValue(0, 0), aTrsf.SetValues(aMat4.GetValue(0, 0),
aMat4.GetValue(0, 1), aMat4.GetValue(0, 1),
@ -605,9 +612,18 @@ bool RWGltf_GltfJsonParser::parseTransformationComponents(
aMat4.GetValue(2, 2), aMat4.GetValue(2, 2),
aMat4.GetValue(2, 3)); aMat4.GetValue(2, 3));
Message::SendWarning(TCollection_AsciiString("glTF reader, scene node '") + theSceneNodeId TCollection_AsciiString aWarnMessage = TCollection_AsciiString("glTF reader, scene node '")
+ "' defines unsupported scaling " + aScaleVec.x() + " " + aScaleVec.y() + theSceneNodeId + "' defines unsupported scaling "
+ " " + aScaleVec.z()); + aScaleVec.x() + " " + aScaleVec.y() + " "
+ aScaleVec.z();
if (myToApplyScale)
{
aWarnMessage += TCollection_AsciiString(". It was applied to the triangulation directly");
theHasScale = true;
theScale = gp_XYZ(aScaleVec.x(), aScaleVec.y(), aScaleVec.z());
}
Message::SendWarning(aWarnMessage);
} }
else if (Abs(aScaleVec.x() - 1.0) > Precision::Confusion()) else if (Abs(aScaleVec.x() - 1.0) > Precision::Confusion())
{ {
@ -638,6 +654,7 @@ RWGltf_GltfJsonParser::RWGltf_GltfJsonParser(TopTools_SequenceOfShape& theRootSh
: myRootShapes(&theRootShapes), : myRootShapes(&theRootShapes),
myAttribMap(NULL), myAttribMap(NULL),
myExternalFiles(NULL), myExternalFiles(NULL),
myShapeScaleMap(NULL),
myMetadata(NULL), myMetadata(NULL),
myBinBodyOffset(0), myBinBodyOffset(0),
myBinBodyLen(0), myBinBodyLen(0),
@ -647,7 +664,8 @@ RWGltf_GltfJsonParser::RWGltf_GltfJsonParser(TopTools_SequenceOfShape& theRootSh
myToLoadAllScenes(false), myToLoadAllScenes(false),
myUseMeshNameAsFallback(true), myUseMeshNameAsFallback(true),
myToProbeHeader(false), myToProbeHeader(false),
myToReadAssetExtras(true) myToReadAssetExtras(true),
myToApplyScale(true)
{ {
myCSTrsf.SetInputLengthUnit(1.0); // meters myCSTrsf.SetInputLengthUnit(1.0); // meters
myCSTrsf.SetInputCoordinateSystem(RWMesh_CoordinateSystem_glTF); myCSTrsf.SetInputCoordinateSystem(RWMesh_CoordinateSystem_glTF);
@ -1567,6 +1585,8 @@ bool RWGltf_GltfJsonParser::gltfParseSceneNode(TopoDS_Shape& th
const RWGltf_JsonValue* anExtrasVal = findObjectMember(theSceneNode, "extras"); const RWGltf_JsonValue* anExtrasVal = findObjectMember(theSceneNode, "extras");
TopLoc_Location aNodeLoc; TopLoc_Location aNodeLoc;
bool aHasScale = false;
gp_XYZ aScale;
const bool aHasTransformComponents = const bool aHasTransformComponents =
aTrsfRotVal != NULL || aTrsfScaleVal != NULL || aTrsfTransVal != NULL; aTrsfRotVal != NULL || aTrsfScaleVal != NULL || aTrsfTransVal != NULL;
const bool aHasTransformMatrix = aTrsfMatVal != NULL; const bool aHasTransformMatrix = aTrsfMatVal != NULL;
@ -1588,7 +1608,9 @@ bool RWGltf_GltfJsonParser::gltfParseSceneNode(TopoDS_Shape& th
aTrsfRotVal, aTrsfRotVal,
aTrsfScaleVal, aTrsfScaleVal,
aTrsfTransVal, aTrsfTransVal,
aNodeLoc)) aNodeLoc,
aHasScale,
aScale))
{ {
return false; return false;
} }
@ -1605,7 +1627,7 @@ bool RWGltf_GltfJsonParser::gltfParseSceneNode(TopoDS_Shape& th
if (aChildren != NULL && !gltfParseSceneNodes(aChildShapes, *aChildren, theProgress)) if (aChildren != NULL && !gltfParseSceneNodes(aChildShapes, *aChildren, theProgress))
{ {
theNodeShape = aNodeShape; theNodeShape = aNodeShape;
bindNodeShape(theNodeShape, aNodeLoc, theSceneNodeId, aName, anExtras); bindNodeShape(theNodeShape, aNodeLoc, aHasScale, aScale, theSceneNodeId, aName, anExtras);
return false; return false;
} }
for (TopTools_SequenceOfShape::Iterator aChildShapeIter(aChildShapes); aChildShapeIter.More(); for (TopTools_SequenceOfShape::Iterator aChildShapeIter(aChildShapes); aChildShapeIter.More();
@ -1627,7 +1649,7 @@ bool RWGltf_GltfJsonParser::gltfParseSceneNode(TopoDS_Shape& th
if (aMesh == NULL) if (aMesh == NULL)
{ {
theNodeShape = aNodeShape; theNodeShape = aNodeShape;
bindNodeShape(theNodeShape, aNodeLoc, theSceneNodeId, aName, anExtras); bindNodeShape(theNodeShape, aNodeLoc, aHasScale, aScale, theSceneNodeId, aName, anExtras);
reportGltfError("Scene node '" + theSceneNodeId + "' refers to non-existing mesh."); reportGltfError("Scene node '" + theSceneNodeId + "' refers to non-existing mesh.");
return false; return false;
} }
@ -1636,7 +1658,7 @@ bool RWGltf_GltfJsonParser::gltfParseSceneNode(TopoDS_Shape& th
if (!gltfParseMesh(aMeshShape, getKeyString(*aMeshIter), *aMesh)) if (!gltfParseMesh(aMeshShape, getKeyString(*aMeshIter), *aMesh))
{ {
theNodeShape = aNodeShape; theNodeShape = aNodeShape;
bindNodeShape(theNodeShape, aNodeLoc, theSceneNodeId, aName, anExtras); bindNodeShape(theNodeShape, aNodeLoc, aHasScale, aScale, theSceneNodeId, aName, anExtras);
return false; return false;
} }
if (!aMeshShape.IsNull()) if (!aMeshShape.IsNull())
@ -1654,7 +1676,7 @@ bool RWGltf_GltfJsonParser::gltfParseSceneNode(TopoDS_Shape& th
if (aMesh == NULL) if (aMesh == NULL)
{ {
theNodeShape = aNodeShape; theNodeShape = aNodeShape;
bindNodeShape(theNodeShape, aNodeLoc, theSceneNodeId, aName, anExtras); bindNodeShape(theNodeShape, aNodeLoc, aHasScale, aScale, theSceneNodeId, aName, anExtras);
reportGltfError("Scene node '" + theSceneNodeId + "' refers to non-existing mesh."); reportGltfError("Scene node '" + theSceneNodeId + "' refers to non-existing mesh.");
return false; return false;
} }
@ -1663,13 +1685,15 @@ bool RWGltf_GltfJsonParser::gltfParseSceneNode(TopoDS_Shape& th
if (!gltfParseMesh(aMeshShape, getKeyString(*aMesh_2), *aMesh)) if (!gltfParseMesh(aMeshShape, getKeyString(*aMesh_2), *aMesh))
{ {
theNodeShape = aNodeShape; theNodeShape = aNodeShape;
bindNodeShape(theNodeShape, aNodeLoc, theSceneNodeId, aName, anExtras); bindNodeShape(theNodeShape, aNodeLoc, aHasScale, aScale, theSceneNodeId, aName, anExtras);
return false; return false;
} }
if (!aMeshShape.IsNull()) if (!aMeshShape.IsNull())
{ {
aBuilder.Add(aNodeShape, aMeshShape); aBuilder.Add(aNodeShape, aMeshShape);
++aNbSubShapes; ++aNbSubShapes;
if (aHasScale)
myShapeScaleMap->Bind(aMeshShape, aScale);
} }
} }
@ -1681,7 +1705,7 @@ bool RWGltf_GltfJsonParser::gltfParseSceneNode(TopoDS_Shape& th
{ {
theNodeShape = aNodeShape; theNodeShape = aNodeShape;
} }
bindNodeShape(theNodeShape, aNodeLoc, theSceneNodeId, aName, anExtras); bindNodeShape(theNodeShape, aNodeLoc, aHasScale, aScale, theSceneNodeId, aName, anExtras);
return true; return true;
} }
@ -2337,10 +2361,11 @@ bool RWGltf_GltfJsonParser::gltfParseBuffer(
} }
//================================================================================================= //=================================================================================================
void RWGltf_GltfJsonParser::bindNamedShape(TopoDS_Shape& theShape, void RWGltf_GltfJsonParser::bindNamedShape(TopoDS_Shape& theShape,
ShapeMapGroup theGroup, ShapeMapGroup theGroup,
const TopLoc_Location& theLoc, const TopLoc_Location& theLoc,
const bool theHasScale,
const gp_XYZ& theScale,
const TCollection_AsciiString& theId, const TCollection_AsciiString& theId,
const RWGltf_JsonValue* theUserName, const RWGltf_JsonValue* theUserName,
const Handle(TDataStd_NamedData)& theExtras) const Handle(TDataStd_NamedData)& theExtras)
@ -2350,6 +2375,19 @@ void RWGltf_GltfJsonParser::bindNamedShape(TopoDS_Shape& the
return; return;
} }
if (theHasScale && myShapeScaleMap->IsBound(theShape))
{
// Check scaling values
gp_XYZ aScale = myShapeScaleMap->Find(theShape);
if ((aScale - theScale).Modulus() > Precision::Confusion())
{
// Create a shape copy to avoid problems with different scaling
BRepBuilderAPI_Copy aCopy;
aCopy.Perform(theShape, Standard_True, Standard_True);
theShape = aCopy.Shape();
}
}
TopoDS_Shape aShape = theShape; TopoDS_Shape aShape = theShape;
if (!theLoc.IsIdentity()) if (!theLoc.IsIdentity())
{ {
@ -2453,6 +2491,11 @@ void RWGltf_GltfJsonParser::bindNamedShape(TopoDS_Shape& the
} }
myAttribMap->Bind(theShape, aShapeAttribs); myAttribMap->Bind(theShape, aShapeAttribs);
} }
if (theHasScale)
{
myShapeScaleMap->Bind(theShape, theScale);
}
myShapeMap[theGroup].Bind(theId, theShape); myShapeMap[theGroup].Bind(theId, theShape);
} }

View File

@ -81,6 +81,12 @@ public:
//! Set map for storing node attributes. //! Set map for storing node attributes.
void SetAttributeMap(RWMesh_NodeAttributeMap& theAttribMap) { myAttribMap = &theAttribMap; } void SetAttributeMap(RWMesh_NodeAttributeMap& theAttribMap) { myAttribMap = &theAttribMap; }
//! Set map for storing non-uniform scalings.
void SetScaleMap(NCollection_DataMap<TopoDS_Shape, gp_XYZ, TopTools_ShapeMapHasher>& theScaleMap)
{
myShapeScaleMap = &theScaleMap;
}
//! Set list for storing external files. //! Set list for storing external files.
void SetExternalFiles(NCollection_IndexedMap<TCollection_AsciiString>& theExternalFiles) void SetExternalFiles(NCollection_IndexedMap<TCollection_AsciiString>& theExternalFiles)
{ {
@ -120,6 +126,10 @@ public:
//! Set flag to use Mesh name in case if Node name is empty, TRUE by default. //! Set flag to use Mesh name in case if Node name is empty, TRUE by default.
void SetMeshNameAsFallback(bool theToFallback) { myUseMeshNameAsFallback = theToFallback; } void SetMeshNameAsFallback(bool theToFallback) { myUseMeshNameAsFallback = theToFallback; }
//! Set flag to apply non-uniform scaling directly to the triangulation (modify nodes).
//! TRUE by default. In case of FALSE the average scale is applied to the transformation matrix.
void SetToApplyScale(bool theToApplyScale) { myToApplyScale = theToApplyScale; }
//! Parse glTF document. //! Parse glTF document.
Standard_EXPORT bool Parse(const Message_ProgressRange& theProgress); Standard_EXPORT bool Parse(const Message_ProgressRange& theProgress);
@ -289,11 +299,20 @@ protected:
//! Bind name attribute. //! Bind name attribute.
void bindNodeShape(TopoDS_Shape& theShape, void bindNodeShape(TopoDS_Shape& theShape,
const TopLoc_Location& theLoc, const TopLoc_Location& theLoc,
const bool theHasScale,
const gp_XYZ& theScale,
const TCollection_AsciiString& theNodeId, const TCollection_AsciiString& theNodeId,
const RWGltf_JsonValue* theUserName, const RWGltf_JsonValue* theUserName,
const Handle(TDataStd_NamedData)& theExtras) const Handle(TDataStd_NamedData)& theExtras)
{ {
bindNamedShape(theShape, ShapeMapGroup_Nodes, theLoc, theNodeId, theUserName, theExtras); bindNamedShape(theShape,
ShapeMapGroup_Nodes,
theLoc,
theHasScale,
theScale,
theNodeId,
theUserName,
theExtras);
} }
//! Bind name attribute. //! Bind name attribute.
@ -305,6 +324,8 @@ protected:
bindNamedShape(theShape, bindNamedShape(theShape,
ShapeMapGroup_Meshes, ShapeMapGroup_Meshes,
TopLoc_Location(), TopLoc_Location(),
false,
gp_XYZ(),
theMeshId, theMeshId,
theUserName, theUserName,
theExtras); theExtras);
@ -326,6 +347,8 @@ protected:
Standard_EXPORT void bindNamedShape(TopoDS_Shape& theShape, Standard_EXPORT void bindNamedShape(TopoDS_Shape& theShape,
ShapeMapGroup theGroup, ShapeMapGroup theGroup,
const TopLoc_Location& theLoc, const TopLoc_Location& theLoc,
const bool theHasScale,
const gp_XYZ& theScale,
const TCollection_AsciiString& theId, const TCollection_AsciiString& theId,
const RWGltf_JsonValue* theUserName, const RWGltf_JsonValue* theUserName,
const Handle(TDataStd_NamedData)& theExtras); const Handle(TDataStd_NamedData)& theExtras);
@ -417,13 +440,18 @@ private:
//! @param theTranslationVal Json value containing translation component of transformation. //! @param theTranslationVal Json value containing translation component of transformation.
//! May be null in which case it is ignored. //! May be null in which case it is ignored.
//! @param theResult TopLoc_Location object where result of parsing will be written. //! @param theResult TopLoc_Location object where result of parsing will be written.
//! @param theHasScale The flag indicates if scale component was found in the transformation.
//! @param theScale Found scale vector. Only valid if @p theHasScale is true.
//! Otherwise, it is undefined.
//! @param If true - parsing was successful, transformation is written into @p theResult. //! @param If true - parsing was successful, transformation is written into @p theResult.
//! If true - failed to parse, @p theResult is unchanged. //! If true - failed to parse, @p theResult is unchanged.
bool parseTransformationComponents(const TCollection_AsciiString& theSceneNodeId, bool parseTransformationComponents(const TCollection_AsciiString& theSceneNodeId,
const RWGltf_JsonValue* theRotationVal, const RWGltf_JsonValue* theRotationVal,
const RWGltf_JsonValue* theScaleVal, const RWGltf_JsonValue* theScaleVal,
const RWGltf_JsonValue* theTranslationVal, const RWGltf_JsonValue* theTranslationVal,
TopLoc_Location& theResult) const; TopLoc_Location& theResult,
bool& theHasScale,
gp_XYZ& theScale) const;
//! Fill lines and points data not deferred. //! Fill lines and points data not deferred.
//! @param theMeshData source glTF triangulation //! @param theMeshData source glTF triangulation
@ -440,6 +468,8 @@ protected:
RWMesh_NodeAttributeMap* myAttribMap; //!< shape attributes RWMesh_NodeAttributeMap* myAttribMap; //!< shape attributes
NCollection_IndexedMap<TCollection_AsciiString>* NCollection_IndexedMap<TCollection_AsciiString>*
myExternalFiles; //!< list of external file references myExternalFiles; //!< list of external file references
NCollection_DataMap<TopoDS_Shape, gp_XYZ, TopTools_ShapeMapHasher>*
myShapeScaleMap; //!< map of shapes with non-uniform scalings
// clang-format off // clang-format off
RWMesh_CoordinateSystemConverter myCSTrsf; //!< transformation from glTF to OCCT coordinate system RWMesh_CoordinateSystemConverter myCSTrsf; //!< transformation from glTF to OCCT coordinate system
// clang-format on // clang-format on
@ -470,6 +500,7 @@ protected:
bool myUseMeshNameAsFallback; //!< flag to use Mesh name in case if Node name is empty, TRUE by default bool myUseMeshNameAsFallback; //!< flag to use Mesh name in case if Node name is empty, TRUE by default
bool myToProbeHeader; //!< flag to probe header without full reading, FALSE by default bool myToProbeHeader; //!< flag to probe header without full reading, FALSE by default
bool myToReadAssetExtras; //!< flag to translate asset.extras into metadata, TRUE by default bool myToReadAssetExtras; //!< flag to translate asset.extras into metadata, TRUE by default
bool myToApplyScale; //!< flag to apply non-uniform scaling
// clang-format on // clang-format on
#ifdef HAVE_RAPIDJSON #ifdef HAVE_RAPIDJSON

View File

@ -119,6 +119,7 @@ static Standard_Integer ReadGltf(Draw_Interpretor& theDI,
Standard_Boolean toPrintDebugInfo = Standard_False; Standard_Boolean toPrintDebugInfo = Standard_False;
Standard_Boolean toLoadAllScenes = Standard_False; Standard_Boolean toLoadAllScenes = Standard_False;
Standard_Boolean toPrintAssetInfo = Standard_False; Standard_Boolean toPrintAssetInfo = Standard_False;
Standard_Boolean toApplyScale = Standard_True;
Standard_Boolean isNoDoc = (TCollection_AsciiString(theArgVec[0]) == "readgltf"); Standard_Boolean isNoDoc = (TCollection_AsciiString(theArgVec[0]) == "readgltf");
for (Standard_Integer anArgIter = 1; anArgIter < theNbArgs; ++anArgIter) for (Standard_Integer anArgIter = 1; anArgIter < theNbArgs; ++anArgIter)
{ {
@ -167,6 +168,10 @@ static Standard_Integer ReadGltf(Draw_Interpretor& theDI,
{ {
toPrintAssetInfo = Draw::ParseOnOffIterator(theNbArgs, theArgVec, anArgIter); toPrintAssetInfo = Draw::ParseOnOffIterator(theNbArgs, theArgVec, anArgIter);
} }
else if (anArgCase == "-applyscale")
{
toApplyScale = Draw::ParseOnOffIterator(theNbArgs, theArgVec, anArgIter);
}
else if (aDestName.IsEmpty()) else if (aDestName.IsEmpty())
{ {
aDestName = theArgVec[anArgIter]; aDestName = theArgVec[anArgIter];
@ -229,6 +234,7 @@ static Standard_Integer ReadGltf(Draw_Interpretor& theDI,
aReader.SetToKeepLateData(toKeepLateData); aReader.SetToKeepLateData(toKeepLateData);
aReader.SetToPrintDebugMessages(toPrintDebugInfo); aReader.SetToPrintDebugMessages(toPrintDebugInfo);
aReader.SetLoadAllScenes(toLoadAllScenes); aReader.SetLoadAllScenes(toLoadAllScenes);
aReader.SetToApplyScale(toApplyScale);
if (aDestName.IsEmpty()) if (aDestName.IsEmpty())
{ {
aReader.ProbeHeader(aFilePath); aReader.ProbeHeader(aFilePath);
@ -523,7 +529,10 @@ void XSDRAWGLTF::Factory(Draw_Interpretor& theDI)
"\n\t\t: -allScenes load all scenes defined in the document instead of default one " "\n\t\t: -allScenes load all scenes defined in the document instead of default one "
"(false by default)" "(false by default)"
"\n\t\t: -toPrintDebugInfo print additional debug information during data reading" "\n\t\t: -toPrintDebugInfo print additional debug information during data reading"
"\n\t\t: -assetInfo print asset information", "\n\t\t: -assetInfo print asset information"
"\n\t\t: -applyScale apply non-uniform scaling directly to the triangulation (modify "
"\n\t\t: nodes) (true by default). In case of false the average scale is "
"\n\t\t: applied to the transformation matrix.",
__FILE__, __FILE__,
ReadGltf, ReadGltf,
aGroup); aGroup);

View File

@ -0,0 +1,21 @@
puts "========"
puts "OCP-1948: Implement non-uniform scaling in Gltf Import"
puts "========"
Close D -silent
ReadGltf D [locate_data_file bug_ocp1948_PSU_Cartoning_subunit__right-01.01.01.03-CART-03_green_bottom.glb]
XGetOneShape s D
checknbshapes s -face 87 -compound 21
checktrinfo s -tri 16473 -nod 15835
# check center of gravity
set REF_X 18300.5
set REF_Y -9484
set REF_Z 129.844
set tol 1e-4
set pos [vprops s]
if {([expr abs($REF_X - [lindex $pos 9])] > $tol) ||
([expr abs($REF_Y - [lindex $pos 12])] > $tol) ||
([expr abs($REF_Z - [lindex $pos 15])] > $tol)} {
puts "Error: wrong position of the imported model."
}

View File

@ -128,6 +128,7 @@ provider.GLTF.OCC.read.use.mesh.name.as.fallback : 1
provider.GLTF.OCC.read.skip.late.data.loading : 0 provider.GLTF.OCC.read.skip.late.data.loading : 0
provider.GLTF.OCC.read.keep.late.data : 1 provider.GLTF.OCC.read.keep.late.data : 1
provider.GLTF.OCC.read.print.debug.message : 0 provider.GLTF.OCC.read.print.debug.message : 0
provider.GLTF.OCC.read.apply.scale : 1
provider.GLTF.OCC.write.comment : provider.GLTF.OCC.write.comment :
provider.GLTF.OCC.write.author : provider.GLTF.OCC.write.author :
provider.GLTF.OCC.write.trsf.format : 0 provider.GLTF.OCC.write.trsf.format : 0