]> OCCT Git - occt.git/commitdiff
GLTF Import - Metadata support #184 CR0_dkulikov
authordkulikov <dkulikov@opencascade.com>
Mon, 9 Dec 2024 17:06:33 +0000 (17:06 +0000)
committerdkulikov <dkulikov@opencascade.com>
Tue, 10 Dec 2024 17:05:38 +0000 (17:05 +0000)
Limited support of metadata import is added to RWGltf_GltfJsonParser.
Following Json data types are currently supported: int32, double, string,
array of int32, array of double, array of strings, Json object.
Notable unsupported types are: binary data, array of Json objects.
Metadata is processed for nodes and meshes.
Tests "gltf_export" are updated with import testing and renamed to "gltf".

Method RWGltf_GltfJsonParser::gltfParseSceneNode() is slightly
refactored: parsing of transformations and transformation matrices is
moved into separate functions.

25 files changed:
src/RWGltf/RWGltf_GltfJsonParser.cxx
src/RWGltf/RWGltf_GltfJsonParser.hxx
tests/metadata/gltf/A1 [new file with mode: 0644]
tests/metadata/gltf/A2 [new file with mode: 0644]
tests/metadata/gltf/A3 [new file with mode: 0644]
tests/metadata/gltf/A4 [new file with mode: 0644]
tests/metadata/gltf/A5 [new file with mode: 0644]
tests/metadata/gltf/A6 [new file with mode: 0644]
tests/metadata/gltf/A7 [new file with mode: 0644]
tests/metadata/gltf/A8 [new file with mode: 0644]
tests/metadata/gltf/A9 [new file with mode: 0644]
tests/metadata/gltf/begin [new file with mode: 0644]
tests/metadata/gltf/end [new file with mode: 0644]
tests/metadata/gltf_export/A1 [deleted file]
tests/metadata/gltf_export/A2 [deleted file]
tests/metadata/gltf_export/A3 [deleted file]
tests/metadata/gltf_export/A4 [deleted file]
tests/metadata/gltf_export/A5 [deleted file]
tests/metadata/gltf_export/A6 [deleted file]
tests/metadata/gltf_export/A7 [deleted file]
tests/metadata/gltf_export/A8 [deleted file]
tests/metadata/gltf_export/A9 [deleted file]
tests/metadata/gltf_export/begin [deleted file]
tests/metadata/gltf_export/end [deleted file]
tests/metadata/grids.list

index d6387a2e98fdafbed6f51d0e8a889218bcef89d6..e69e2f0a034ed362e052584c8797949b3c3651ec 100644 (file)
@@ -25,6 +25,7 @@
 #include <OSD_ThreadPool.hxx>
 #include <Precision.hxx>
 #include <FSD_Base64.hxx>
+#include <TDataStd_NamedData.hxx>
 #include <TopExp_Explorer.hxx>
 #include <TopoDS.hxx>
 #include <TopoDS_Iterator.hxx>
@@ -52,6 +53,265 @@ namespace
   private:
     Handle(NCollection_Buffer) myBaseBuffer;
   };
+
+  //! Helper class to parse "extras" section of glTF node.
+  //! In order to use, provide the ID and the "extras" value of the node
+  //! in the constructor, and then call Parse().
+  //! Alternatively, just call ParseExtras() static function.
+  class RWGltf_ExtrasParser
+  {
+  public:
+    //! Constructor. Initializes parser.
+    //! @param theParentID ID of the Json object that contains this "extras" value. Used only for printing messages.
+    //! @param theExtrasValue "extras" value to parse.
+    RWGltf_ExtrasParser(const TCollection_AsciiString& theParentID,
+                        const RWGltf_JsonValue& theExtrasValue);
+
+    //! Parses the "extras" value provided in the constructor.
+    //! @return Container with parsed data. May be nullptr if failed to parse.
+    Handle(TDataStd_NamedData) Parse();
+
+    //! Parses provided "extras" value.
+    //! @param theParentID ID of the Json object that contains this "extras" value. Used only for printing messages.
+    //! @param theExtrasValue "extras" value to parse. May be nullptr, in which case function will return nullptr.
+    //! @return Container with parsed data. May be nullptr if failed to parse.
+    static Handle(TDataStd_NamedData) ParseExtras(const TCollection_AsciiString& theParentID,
+                                                  const RWGltf_JsonValue* theExtrasValue);
+
+  private:
+    //! Parse value as the Json object. Serves as the entry point to parse "extras".
+    //! Can also parse any object inside it.
+    //! @param theValue Value to parse.
+    //! @param theValueName Name of the value. For "extras" object should be empty.
+    //! @return true if object was processed, false otherwise. Note that true doesn't mean that
+    //!         object was successfully parsed and stored, it could be if unsupported type in
+    //!         which case warning is issued. true only indicates that no further processing required.
+    bool parseObject(const RWGltf_JsonValue& theValue, const std::string& theValueName = {});
+
+    //! Parse value as the integer.
+    //! @param theValue Value to parse.
+    //! @param theValueName Name of the value.
+    //! @return true if object was processed, false otherwise. Note that true doesn't mean that
+    //!         object was successfully parsed and stored, it could be if unsupported type in
+    //!         which case warning is issued. true only indicates that no further processing required.
+    bool parseNumber(const RWGltf_JsonValue& theValue, const std::string& theValueName);
+
+    //! Parse value as the string.
+    //! @param theValue Value to parse.
+    //! @param theValueName Name of the value.
+    //! @return true if object was processed, false otherwise. Note that true doesn't mean that
+    //!         object was successfully parsed and stored, it could be if unsupported type in
+    //!         which case warning is issued. true only indicates that no further processing required.
+    bool parseString(const RWGltf_JsonValue& theValue, const std::string& theValueName);
+
+    //! Parse value as the array.
+    //! Currently only arrays of following types are supported: int, double, string.
+    //! IMPORTANT: Array of Json objects is NOT supported.
+    //! @param theValue Value to parse.
+    //! @param theValueName Name of the value.
+    //! @return true if object was processed, false otherwise. Note that true doesn't mean that
+    //!         object was successfully parsed and stored, it could be if unsupported type in
+    //!         which case warning is issued. true only indicates that no further processing required.
+    bool parseArray(const RWGltf_JsonValue& theValue, const std::string& theValueName);
+
+    //! Returns result container for internal usage. Is container in not initialized yet,
+    //! this function will initialize it, so it is guaranteed to be valid.
+    Handle(TDataStd_NamedData)& getResult();
+
+  private:
+    const TCollection_AsciiString& myParentID;    //!< ID of the Json object that contains "extras" value. For printing messages.
+    const RWGltf_JsonValue&        myExtrasValue; //!< "extras" value to parse.
+    Handle(TDataStd_NamedData)     myResult;      //!< Result of parsing.
+  };
+
+  // =======================================================================
+  // function : RWGltf_ExtrasParser
+  // purpose  :
+  // =======================================================================
+  RWGltf_ExtrasParser::RWGltf_ExtrasParser(const TCollection_AsciiString& theParentID,
+                                           const RWGltf_JsonValue& theExtrasValue)
+    : myParentID(theParentID),
+      myExtrasValue(theExtrasValue),
+      myResult(nullptr)
+  {}
+
+  // =======================================================================
+  // function : parseValue
+  // purpose  :
+  // =======================================================================
+  Handle(TDataStd_NamedData) RWGltf_ExtrasParser::Parse()
+  {
+    parseObject(myExtrasValue);
+    // Intentionally returning myResult instead of getResult(). If parseObject() parsed data
+    // successfully, it will be initialized already. Otherwise, we should return nullptr.
+    return myResult;
+  }
+
+  // =======================================================================
+  // function : parseValue
+  // purpose  :
+  // =======================================================================
+  Handle(TDataStd_NamedData) RWGltf_ExtrasParser::ParseExtras(const TCollection_AsciiString& theParentID,
+                                                              const RWGltf_JsonValue* theExtrasValue)
+  {
+    if (!theExtrasValue)
+    {
+      return nullptr;
+    }
+
+    RWGltf_ExtrasParser anExtrasParser(theParentID, *theExtrasValue);
+    return anExtrasParser.Parse();
+  }
+
+  // =======================================================================
+  // function : parseValue
+  // purpose  :
+  // =======================================================================
+  bool RWGltf_ExtrasParser::parseObject(const RWGltf_JsonValue& theValue, const std::string& theValueName)
+  {
+    if (!theValue.IsObject())
+    {
+      return false;
+    }
+
+    bool anIsAnyValProcessed = false;
+    for (auto& anItem : theValue.GetObject())
+    {
+      std::string aCurrentValName = theValueName.empty() ? std::string(anItem.name.GetString())
+                                                         : theValueName + "." + anItem.name.GetString();
+      const RWGltf_JsonValue& aCurrentValue = anItem.value;
+
+      const bool anIsCurrentValProcessed = parseNumber(aCurrentValue, aCurrentValName)
+                                        || parseString(aCurrentValue, aCurrentValName)
+                                        || parseArray(aCurrentValue, aCurrentValName)
+                                        || parseObject(aCurrentValue, aCurrentValName);
+      anIsAnyValProcessed |= anIsCurrentValProcessed;
+    }
+
+    return anIsAnyValProcessed;
+  }
+
+  // =======================================================================
+  // function : parseNumber
+  // purpose  :
+  // =======================================================================
+  bool RWGltf_ExtrasParser::parseNumber(const RWGltf_JsonValue& theValue, const std::string& theValueName)
+  {
+    if (theValue.IsNumber() && !theValue.IsInt() && !theValue.IsDouble())
+    {
+      Message::SendWarning() << "Warning: Extras owner \"" << myParentID << "\", Value \"" << theValueName << "\" - "
+                             << "Unsupported integer type. It will be skipped.";
+      return true;
+    }
+
+    if (theValue.IsInt())
+    {
+      getResult()->SetInteger(theValueName.c_str(), theValue.GetInt());
+      return true;
+    }
+
+    if (theValue.IsDouble())
+    {
+      getResult()->SetReal(theValueName.c_str(), theValue.GetDouble());
+      return true;
+    }
+
+    return false;
+  }
+
+  // =======================================================================
+  // function : parseString
+  // purpose  :
+  // =======================================================================
+  bool RWGltf_ExtrasParser::parseString(const RWGltf_JsonValue& theValue, const std::string& theValueName)
+  {
+    if (theValue.IsString())
+    {
+      getResult()->SetString(theValueName.c_str(), theValue.GetString());
+      return true;
+      // Note: maybe in a future we should detect and parse binary data?
+      // Currently TDataStd_NamedData doesn't support array of bytes, so we can
+      // only try to process it as a string.
+    }
+
+    return false;
+  }
+
+  // =======================================================================
+  // function : parseArray
+  // purpose  :
+  // =======================================================================
+  bool RWGltf_ExtrasParser::parseArray(const RWGltf_JsonValue& theValue, const std::string& theValueName)
+  {
+    if (!theValue.IsArray())
+    {
+      return false;
+    }
+
+    if (theValue.Size() == 0)
+    {
+      // Processing empty array first.
+      Message::SendInfo() << "Extras owner \"" << myParentID << "\", Value \"" << theValueName << "\" - "
+                          << "Empty array is detected. Storing as empty string.";
+      getResult()->SetString(theValueName.c_str(), "");
+      return true;
+    }
+
+    if (theValue[0].IsInt())
+    {
+      // Array of integers is supported, storing as normal.
+      Handle(TColStd_HArray1OfInteger) anArray = new TColStd_HArray1OfInteger(0, theValue.Size());
+      for (size_t anIndex = 0; anIndex < theValue.Size(); ++anIndex)
+      {
+        anArray->SetValue(static_cast<Standard_Integer>(anIndex), theValue[0].GetInt());
+      }
+      getResult()->SetArrayOfIntegers(theValueName.c_str(), anArray);
+      return true;
+    }
+    else if (theValue[0].IsDouble())
+    {
+      // Array of double is supported, storing as normal.
+      Handle(TColStd_HArray1OfReal) anArray = new TColStd_HArray1OfReal(0, theValue.Size());
+      for (size_t anIndex = 0; anIndex < theValue.Size(); ++anIndex)
+      {
+        anArray->SetValue(static_cast<Standard_Integer>(anIndex), theValue[0].GetDouble());
+      }
+      getResult()->SetArrayOfReals(theValueName.c_str(), anArray);
+      return true;
+    }
+    else if (theValue[0].IsString())
+    {
+      // Storing array of strings as string with separator.
+      Message::SendInfo() << "Extras owner \"" << myParentID << "\", Value \"" << theValueName << "\" - "
+                          << "Array of strings is detected. Storing as string with separators.";
+      std::string anArrayString;
+      const std::string aSeparator = ";";
+      for (size_t i = 0; i < theValue.Size(); ++i)
+      {
+        anArrayString = anArrayString + aSeparator + theValue[0].GetString();
+      }
+      getResult()->SetString(theValueName.c_str(), anArrayString.c_str());
+      return true;
+    }
+
+    // Unsupported type of array. Print waring and return.
+    Message::SendWarning() << "Warning: Extras owner \"" << myParentID << "\", Value \"" << theValueName << "\" - "
+                           << "Array of unsupported type is detected. It will be skipped.";
+    return true;
+  }
+
+  // =======================================================================
+  // function : parseArray
+  // purpose  :
+  // =======================================================================
+  Handle(TDataStd_NamedData)& RWGltf_ExtrasParser::getResult()
+  {
+    if (myResult.IsNull())
+    {
+      myResult = new TDataStd_NamedData;
+    }
+    return myResult;
+  }
 }
 
 //! Find member of the object in a safe way.
@@ -167,11 +427,175 @@ void RWGltf_GltfJsonParser::GltfElementMap::Init (const TCollection_AsciiString&
 // purpose  :
 // =======================================================================
 void RWGltf_GltfJsonParser::reportGltfSyntaxProblem (const TCollection_AsciiString& theMsg,
-                                                    Message_Gravity theGravity)
+                                                    Message_Gravity theGravity) const
 {
   Message::Send (myErrorPrefix + theMsg, theGravity);
 }
 
+// =======================================================================
+// function : parseTransformationMatrix
+// purpose  :
+// =======================================================================
+bool RWGltf_GltfJsonParser::parseTransformationMatrix(const TCollection_AsciiString& theSceneNodeId,
+                                                      const RWGltf_JsonValue& theMatrixVal,
+                                                      TopLoc_Location& theResult) const
+{
+  if (!theMatrixVal.IsArray() || theMatrixVal.Size() != 16)
+  {
+    reportGltfError("Scene node '" + theSceneNodeId + "' defines invalid transformation matrix array.");
+    return false;
+  }
+
+  Graphic3d_Mat4d aMat4;
+  for (int aColIter = 0; aColIter < 4; ++aColIter)
+  {
+    for (int aRowIter = 0; aRowIter < 4; ++aRowIter)
+    {
+      const RWGltf_JsonValue& aGenVal = theMatrixVal[aColIter * 4 + aRowIter];
+      if (!aGenVal.IsNumber())
+      {
+        reportGltfError("Scene node '" + theSceneNodeId + "' defines invalid transformation matrix.");
+        return false;
+      }
+      aMat4.SetValue(aRowIter, aColIter, aGenVal.GetDouble());
+    }
+  }
+
+  if (!aMat4.IsIdentity())
+  {
+    gp_Trsf aTrsf;
+    aTrsf.SetValues(aMat4.GetValue(0, 0), aMat4.GetValue(0, 1), aMat4.GetValue(0, 2), aMat4.GetValue(0, 3),
+                    aMat4.GetValue(1, 0), aMat4.GetValue(1, 1), aMat4.GetValue(1, 2), aMat4.GetValue(1, 3),
+                    aMat4.GetValue(2, 0), aMat4.GetValue(2, 1), aMat4.GetValue(2, 2), aMat4.GetValue(2, 3));
+    myCSTrsf.TransformTransformation(aTrsf);
+    if (aTrsf.Form() != gp_Identity)
+    {
+      theResult = TopLoc_Location(aTrsf);
+    }
+  }
+
+  return true;
+}
+
+// =======================================================================
+// function : RWGltf_GltfJsonParser
+// purpose  :
+// =======================================================================
+bool RWGltf_GltfJsonParser::parseTransformationComponents(const TCollection_AsciiString& theSceneNodeId,
+                                                          const RWGltf_JsonValue* theRotationVal,
+                                                          const RWGltf_JsonValue* theScaleVal,
+                                                          const RWGltf_JsonValue* theTranslationVal,
+                                                          TopLoc_Location& theResult) const
+{
+  gp_Trsf aTrsf;
+  if (theRotationVal != NULL)
+  {
+    if (!theRotationVal->IsArray() || theRotationVal->Size() != 4)
+    {
+      reportGltfError("Scene node '" + theSceneNodeId + "' defines invalid rotation quaternion.");
+      return false;
+    }
+
+    Graphic3d_Vec4d aRotVec4;
+    for (int aCompIter = 0; aCompIter < 4; ++aCompIter)
+    {
+      const RWGltf_JsonValue& aGenVal = (*theRotationVal)[aCompIter];
+      if (!aGenVal.IsNumber())
+      {
+        reportGltfError("Scene node '" + theSceneNodeId + "' defines invalid rotation.");
+        return false;
+      }
+      aRotVec4[aCompIter] = aGenVal.GetDouble();
+    }
+    const gp_Quaternion aQuaternion(aRotVec4.x(), aRotVec4.y(), aRotVec4.z(), aRotVec4.w());
+    if (Abs(aQuaternion.X()) > gp::Resolution()
+        || Abs(aQuaternion.Y()) > gp::Resolution()
+        || Abs(aQuaternion.Z()) > gp::Resolution()
+        || Abs(aQuaternion.W() - 1.0) > gp::Resolution())
+    {
+      aTrsf.SetRotation(aQuaternion);
+    }
+  }
+
+  if (theTranslationVal != NULL)
+  {
+    if (!theTranslationVal->IsArray() || theTranslationVal->Size() != 3)
+    {
+      reportGltfError("Scene node '" + theSceneNodeId + "' defines invalid translation vector.");
+      return false;
+    }
+
+    gp_XYZ aTransVec;
+    for (int aCompIter = 0; aCompIter < 3; ++aCompIter)
+    {
+      const RWGltf_JsonValue& aGenVal = (*theTranslationVal)[aCompIter];
+      if (!aGenVal.IsNumber())
+      {
+        reportGltfError("Scene node '" + theSceneNodeId + "' defines invalid translation.");
+        return false;
+      }
+      aTransVec.SetCoord(aCompIter + 1, aGenVal.GetDouble());
+    }
+    aTrsf.SetTranslationPart(aTransVec);
+  }
+
+  if (theScaleVal != NULL)
+  {
+    Graphic3d_Vec3d aScaleVec;
+    if (!theScaleVal->IsArray() || theScaleVal->Size() != 3)
+    {
+      reportGltfError("Scene node '" + theSceneNodeId + "' defines invalid scale vector.");
+      return false;
+    }
+    for (int aCompIter = 0; aCompIter < 3; ++aCompIter)
+    {
+      const RWGltf_JsonValue& aGenVal = (*theScaleVal)[aCompIter];
+      if (!aGenVal.IsNumber())
+      {
+        reportGltfError("Scene node '" + theSceneNodeId + "' defines invalid scale.");
+        return false;
+      }
+      aScaleVec[aCompIter] = aGenVal.GetDouble();
+      if (Abs(aScaleVec[aCompIter]) <= gp::Resolution())
+      {
+        reportGltfError("Scene node '" + theSceneNodeId + "' defines invalid scale.");
+        return false;
+      }
+    }
+
+    if (Abs(aScaleVec.x() - aScaleVec.y()) > Precision::Confusion()
+        || Abs(aScaleVec.y() - aScaleVec.z()) > Precision::Confusion()
+        || Abs(aScaleVec.x() - aScaleVec.z()) > Precision::Confusion())
+    {
+      Graphic3d_Mat4d aScaleMat;
+      aScaleMat.SetDiagonal(aScaleVec);
+
+      Graphic3d_Mat4d aMat4;
+      aTrsf.GetMat4(aMat4);
+
+      aMat4 = aMat4 * aScaleMat;
+      aTrsf = gp_Trsf();
+      aTrsf.SetValues(aMat4.GetValue(0, 0), aMat4.GetValue(0, 1), aMat4.GetValue(0, 2), aMat4.GetValue(0, 3),
+                      aMat4.GetValue(1, 0), aMat4.GetValue(1, 1), aMat4.GetValue(1, 2), aMat4.GetValue(1, 3),
+                      aMat4.GetValue(2, 0), aMat4.GetValue(2, 1), aMat4.GetValue(2, 2), aMat4.GetValue(2, 3));
+
+      Message::SendWarning(TCollection_AsciiString("glTF reader, scene node '")
+                           + theSceneNodeId + "' defines unsupported scaling " + aScaleVec.x() + " " + aScaleVec.y() + " " + aScaleVec.z());
+    }
+    else if (Abs(aScaleVec.x() - 1.0) > Precision::Confusion())
+    {
+      aTrsf.SetScaleFactor(aScaleVec.x());
+    }
+  }
+
+  myCSTrsf.TransformTransformation(aTrsf);
+  if (aTrsf.Form() != gp_Identity)
+  {
+    theResult = TopLoc_Location(aTrsf);
+  }
+  return true;
+}
+
 // =======================================================================
 // function : RWGltf_GltfJsonParser
 // purpose  :
@@ -1123,6 +1547,11 @@ bool RWGltf_GltfJsonParser::gltfParseSceneNode (TopoDS_Shape& theNodeShape,
                                                 const RWGltf_JsonValue& theSceneNode,
                                                 const Message_ProgressRange& theProgress)
 {
+  if (findNodeShape(theNodeShape, theSceneNodeId))
+  {
+    return true;
+  }
+
   const RWGltf_JsonValue* aName         = findObjectMember (theSceneNode, "name");
   //const RWGltf_JsonValue* aJointName    = findObjectMember (theSceneNode, "jointName");
   const RWGltf_JsonValue* aChildren     = findObjectMember (theSceneNode, "children");
@@ -1133,180 +1562,44 @@ bool RWGltf_GltfJsonParser::gltfParseSceneNode (TopoDS_Shape& theNodeShape,
   const RWGltf_JsonValue* aTrsfRotVal   = findObjectMember (theSceneNode, "rotation");
   const RWGltf_JsonValue* aTrsfScaleVal = findObjectMember (theSceneNode, "scale");
   const RWGltf_JsonValue* aTrsfTransVal = findObjectMember (theSceneNode, "translation");
-  if (findNodeShape (theNodeShape, theSceneNodeId))
-  {
-    return true;
-  }
+  const RWGltf_JsonValue* anExtrasVal   = findObjectMember (theSceneNode, "extras");
 
   TopLoc_Location aNodeLoc;
-  const bool hasTrs = aTrsfRotVal   != NULL
-                   || aTrsfScaleVal != NULL
-                   || aTrsfTransVal != NULL;
-  if (aTrsfMatVal != NULL)
+  const bool aHasTransformComponents = aTrsfRotVal   != NULL
+                                    || aTrsfScaleVal != NULL
+                                    || aTrsfTransVal != NULL;
+  const bool aHasTransformMatrix     = aTrsfMatVal != NULL;
+  if (aHasTransformComponents && aHasTransformMatrix)
   {
-    if (hasTrs)
-    {
-      reportGltfError ("Scene node '" + theSceneNodeId + "' defines ambiguous transformation.");
-      return false;
-    }
-    else if (!aTrsfMatVal->IsArray()
-           || aTrsfMatVal->Size() != 16)
+    reportGltfError("Scene node '" + theSceneNodeId + "' defines ambiguous transformation.");
+    return false;
+  }
+  else if (aHasTransformMatrix)
+  {
+    if (!parseTransformationMatrix(theSceneNodeId, *aTrsfMatVal, aNodeLoc))
     {
-      reportGltfError ("Scene node '" + theSceneNodeId + "' defines invalid transformation matrix array.");
       return false;
     }
-
-    Graphic3d_Mat4d aMat4;
-    for (int aColIter = 0; aColIter < 4; ++aColIter)
-    {
-      for (int aRowIter = 0; aRowIter < 4; ++aRowIter)
-      {
-        const RWGltf_JsonValue& aGenVal = (*aTrsfMatVal)[aColIter * 4 + aRowIter];
-        if (!aGenVal.IsNumber())
-        {
-          reportGltfError ("Scene node '" + theSceneNodeId + "' defines invalid transformation matrix.");
-          return false;
-        }
-        aMat4.SetValue (aRowIter, aColIter, aGenVal.GetDouble());
-      }
-    }
-
-    if (!aMat4.IsIdentity())
-    {
-      gp_Trsf aTrsf;
-      aTrsf.SetValues (aMat4.GetValue (0, 0), aMat4.GetValue (0, 1), aMat4.GetValue (0, 2), aMat4.GetValue (0, 3),
-                       aMat4.GetValue (1, 0), aMat4.GetValue (1, 1), aMat4.GetValue (1, 2), aMat4.GetValue (1, 3),
-                       aMat4.GetValue (2, 0), aMat4.GetValue (2, 1), aMat4.GetValue (2, 2), aMat4.GetValue (2, 3));
-      myCSTrsf.TransformTransformation (aTrsf);
-      if (aTrsf.Form() != gp_Identity)
-      {
-        aNodeLoc = TopLoc_Location (aTrsf);
-      }
-    }
   }
-  else if (hasTrs)
+  else if (aHasTransformComponents)
   {
-    gp_Trsf aTrsf;
-    if (aTrsfRotVal != NULL)
+    if (!parseTransformationComponents(theSceneNodeId, aTrsfRotVal, aTrsfScaleVal, aTrsfTransVal, aNodeLoc))
     {
-      if (!aTrsfRotVal->IsArray()
-        || aTrsfRotVal->Size() != 4)
-      {
-        reportGltfError ("Scene node '" + theSceneNodeId + "' defines invalid rotation quaternion.");
-        return false;
-      }
-
-      Graphic3d_Vec4d aRotVec4;
-      for (int aCompIter = 0; aCompIter < 4; ++aCompIter)
-      {
-        const RWGltf_JsonValue& aGenVal = (*aTrsfRotVal)[aCompIter];
-        if (!aGenVal.IsNumber())
-        {
-          reportGltfError ("Scene node '" + theSceneNodeId + "' defines invalid rotation.");
-          return false;
-        }
-        aRotVec4[aCompIter] = aGenVal.GetDouble();
-      }
-      const gp_Quaternion aQuaternion (aRotVec4.x(), aRotVec4.y(), aRotVec4.z(), aRotVec4.w());
-      if (Abs (aQuaternion.X())       > gp::Resolution()
-       || Abs (aQuaternion.Y())       > gp::Resolution()
-       || Abs (aQuaternion.Z())       > gp::Resolution()
-       || Abs (aQuaternion.W() - 1.0) > gp::Resolution())
-      {
-        aTrsf.SetRotation (aQuaternion);
-      }
-    }
-
-    if (aTrsfTransVal != NULL)
-    {
-      if (!aTrsfTransVal->IsArray()
-        || aTrsfTransVal->Size() != 3)
-      {
-        reportGltfError ("Scene node '" + theSceneNodeId + "' defines invalid translation vector.");
-        return false;
-      }
-
-      gp_XYZ aTransVec;
-      for (int aCompIter = 0; aCompIter < 3; ++aCompIter)
-      {
-        const RWGltf_JsonValue& aGenVal = (*aTrsfTransVal)[aCompIter];
-        if (!aGenVal.IsNumber())
-        {
-          reportGltfError ("Scene node '" + theSceneNodeId + "' defines invalid translation.");
-          return false;
-        }
-        aTransVec.SetCoord (aCompIter + 1, aGenVal.GetDouble());
-      }
-      aTrsf.SetTranslationPart (aTransVec);
-    }
-
-    if (aTrsfScaleVal != NULL)
-    {
-      Graphic3d_Vec3d aScaleVec;
-      if (!aTrsfScaleVal->IsArray()
-        || aTrsfScaleVal->Size() != 3)
-      {
-        reportGltfError ("Scene node '" + theSceneNodeId + "' defines invalid scale vector.");
-        return false;
-      }
-      for (int aCompIter = 0; aCompIter < 3; ++aCompIter)
-      {
-        const RWGltf_JsonValue& aGenVal = (*aTrsfScaleVal)[aCompIter];
-        if (!aGenVal.IsNumber())
-        {
-          reportGltfError ("Scene node '" + theSceneNodeId + "' defines invalid scale.");
-          return false;
-        }
-        aScaleVec[aCompIter] = aGenVal.GetDouble();
-        if (Abs (aScaleVec[aCompIter]) <= gp::Resolution())
-        {
-          reportGltfError ("Scene node '" + theSceneNodeId + "' defines invalid scale.");
-          return false;
-        }
-      }
-
-      if (Abs (aScaleVec.x() - aScaleVec.y()) > Precision::Confusion()
-       || Abs (aScaleVec.y() - aScaleVec.z()) > Precision::Confusion()
-       || Abs (aScaleVec.x() - aScaleVec.z()) > Precision::Confusion())
-      {
-        Graphic3d_Mat4d aScaleMat;
-        aScaleMat.SetDiagonal (aScaleVec);
-
-        Graphic3d_Mat4d aMat4;
-        aTrsf.GetMat4 (aMat4);
-
-        aMat4 = aMat4 * aScaleMat;
-        aTrsf = gp_Trsf();
-        aTrsf.SetValues (aMat4.GetValue (0, 0), aMat4.GetValue (0, 1), aMat4.GetValue (0, 2), aMat4.GetValue (0, 3),
-                         aMat4.GetValue (1, 0), aMat4.GetValue (1, 1), aMat4.GetValue (1, 2), aMat4.GetValue (1, 3),
-                         aMat4.GetValue (2, 0), aMat4.GetValue (2, 1), aMat4.GetValue (2, 2), aMat4.GetValue (2, 3));
-
-        Message::SendWarning (TCollection_AsciiString ("glTF reader, scene node '")
-                            + theSceneNodeId + "' defines unsupported scaling " + aScaleVec.x() + " " + aScaleVec.y() + " " + aScaleVec.z());
-      }
-      else if (Abs (aScaleVec.x() - 1.0) > Precision::Confusion())
-      {
-        aTrsf.SetScaleFactor (aScaleVec.x());
-      }
-    }
-
-    myCSTrsf.TransformTransformation (aTrsf);
-    if (aTrsf.Form() != gp_Identity)
-    {
-      aNodeLoc = TopLoc_Location (aTrsf);
+      return false;
     }
   }
 
+  const Handle(TDataStd_NamedData) anExtras = RWGltf_ExtrasParser::ParseExtras(theSceneNodeId, anExtrasVal);
+
   BRep_Builder aBuilder;
   TopoDS_Compound aNodeShape;
   aBuilder.MakeCompound (aNodeShape);
   TopTools_SequenceOfShape aChildShapes;
   int aNbSubShapes = 0;
-  if (aChildren != NULL
-  && !gltfParseSceneNodes (aChildShapes, *aChildren, theProgress))
+  if (aChildren != NULL && !gltfParseSceneNodes (aChildShapes, *aChildren, theProgress))
   {
     theNodeShape = aNodeShape;
-    bindNodeShape (theNodeShape, aNodeLoc, theSceneNodeId, aName);
+    bindNodeShape (theNodeShape, aNodeLoc, theSceneNodeId, aName, anExtras);
     return false;
   }
   for (TopTools_SequenceOfShape::Iterator aChildShapeIter (aChildShapes); aChildShapeIter.More(); aChildShapeIter.Next())
@@ -1315,8 +1608,7 @@ bool RWGltf_GltfJsonParser::gltfParseSceneNode (TopoDS_Shape& theNodeShape,
     ++aNbSubShapes;
   }
 
-  if (aMeshes_1 != NULL
-   && aMeshes_1->IsArray())
+  if (aMeshes_1 != NULL && aMeshes_1->IsArray())
   {
     // glTF 1.0
     for (rapidjson::Value::ConstValueIterator aMeshIter = aMeshes_1->Begin();
@@ -1326,7 +1618,7 @@ bool RWGltf_GltfJsonParser::gltfParseSceneNode (TopoDS_Shape& theNodeShape,
       if (aMesh == NULL)
       {
         theNodeShape = aNodeShape;
-        bindNodeShape (theNodeShape, aNodeLoc, theSceneNodeId, aName);
+        bindNodeShape (theNodeShape, aNodeLoc, theSceneNodeId, aName, anExtras);
         reportGltfError ("Scene node '" + theSceneNodeId + "' refers to non-existing mesh.");
         return false;
       }
@@ -1335,7 +1627,7 @@ bool RWGltf_GltfJsonParser::gltfParseSceneNode (TopoDS_Shape& theNodeShape,
       if (!gltfParseMesh (aMeshShape, getKeyString (*aMeshIter), *aMesh))
       {
         theNodeShape = aNodeShape;
-        bindNodeShape (theNodeShape, aNodeLoc, theSceneNodeId, aName);
+        bindNodeShape (theNodeShape, aNodeLoc, theSceneNodeId, aName, anExtras);
         return false;
       }
       if (!aMeshShape.IsNull())
@@ -1345,6 +1637,7 @@ bool RWGltf_GltfJsonParser::gltfParseSceneNode (TopoDS_Shape& theNodeShape,
       }
     }
   }
+
   if (aMesh_2 != NULL)
   {
     // glTF 2.0
@@ -1352,7 +1645,7 @@ bool RWGltf_GltfJsonParser::gltfParseSceneNode (TopoDS_Shape& theNodeShape,
     if (aMesh == NULL)
     {
       theNodeShape = aNodeShape;
-      bindNodeShape (theNodeShape, aNodeLoc, theSceneNodeId, aName);
+      bindNodeShape (theNodeShape, aNodeLoc, theSceneNodeId, aName, anExtras);
       reportGltfError ("Scene node '" + theSceneNodeId + "' refers to non-existing mesh.");
       return false;
     }
@@ -1361,7 +1654,7 @@ bool RWGltf_GltfJsonParser::gltfParseSceneNode (TopoDS_Shape& theNodeShape,
     if (!gltfParseMesh (aMeshShape, getKeyString (*aMesh_2), *aMesh))
     {
       theNodeShape = aNodeShape;
-      bindNodeShape (theNodeShape, aNodeLoc, theSceneNodeId, aName);
+      bindNodeShape (theNodeShape, aNodeLoc, theSceneNodeId, aName, anExtras);
       return false;
     }
     if (!aMeshShape.IsNull())
@@ -1371,8 +1664,7 @@ bool RWGltf_GltfJsonParser::gltfParseSceneNode (TopoDS_Shape& theNodeShape,
     }
   }
 
-  if (aChildShapes.IsEmpty()
-   && aNbSubShapes == 1)
+  if (aChildShapes.IsEmpty() && aNbSubShapes == 1)
   {
     theNodeShape = TopoDS_Iterator (aNodeShape).Value();
   }
@@ -1380,7 +1672,7 @@ bool RWGltf_GltfJsonParser::gltfParseSceneNode (TopoDS_Shape& theNodeShape,
   {
     theNodeShape = aNodeShape;
   }
-  bindNodeShape (theNodeShape, aNodeLoc, theSceneNodeId, aName);
+  bindNodeShape (theNodeShape, aNodeLoc, theSceneNodeId, aName, anExtras);
   return true;
 }
 
@@ -1392,8 +1684,9 @@ bool RWGltf_GltfJsonParser::gltfParseMesh (TopoDS_Shape& theMeshShape,
                                            const TCollection_AsciiString& theMeshId,
                                            const RWGltf_JsonValue& theMesh)
 {
-  const RWGltf_JsonValue* aName  = findObjectMember (theMesh, "name");
-  const RWGltf_JsonValue* aPrims = findObjectMember (theMesh, "primitives");
+  const RWGltf_JsonValue* aName       = findObjectMember (theMesh, "name");
+  const RWGltf_JsonValue* aPrims      = findObjectMember (theMesh, "primitives");
+  const RWGltf_JsonValue* anExtrasVal = findObjectMember(theMesh, "extras");
   if (aPrims == NULL
   || !aPrims->IsArray())
   {
@@ -1439,7 +1732,9 @@ bool RWGltf_GltfJsonParser::gltfParseMesh (TopoDS_Shape& theMeshShape,
   {
     theMeshShape = aMeshShape;
   }
-  bindMeshShape (theMeshShape, theMeshId, aName);
+
+  const Handle(TDataStd_NamedData) anExtras = RWGltf_ExtrasParser::ParseExtras(theMeshId, anExtrasVal);
+  bindMeshShape (theMeshShape, theMeshId, aName, anExtras);
   return true;
 }
 
@@ -1977,7 +2272,8 @@ void RWGltf_GltfJsonParser::bindNamedShape (TopoDS_Shape& theShape,
                                             ShapeMapGroup theGroup,
                                             const TopLoc_Location& theLoc,
                                             const TCollection_AsciiString& theId,
-                                            const RWGltf_JsonValue* theUserName)
+                                            const RWGltf_JsonValue* theUserName,
+                                            const Handle(TDataStd_NamedData)& theExtras)
 {
   if (theShape.IsNull())
   {
@@ -1998,8 +2294,7 @@ void RWGltf_GltfJsonParser::bindNamedShape (TopoDS_Shape& theShape,
   }
 
   TCollection_AsciiString aUserName;
-  if (theUserName != NULL
-   && theUserName->IsString())
+  if (theUserName != NULL && theUserName->IsString())
   {
     aUserName = theUserName->GetString();
   }
@@ -2012,6 +2307,7 @@ void RWGltf_GltfJsonParser::bindNamedShape (TopoDS_Shape& theShape,
   {
     RWMesh_NodeAttributes aShapeAttribs;
     aShapeAttribs.Name = aUserName;
+    aShapeAttribs.NamedData = theExtras;
     if (myIsGltf1)
     {
       aShapeAttribs.RawName = theId;
index 0d26a027178e3e2ef592e556c291f3037af7c966..19748ad5ed377b0f841f9b851a0eaf28b599a64f 100644 (file)
@@ -297,17 +297,19 @@ protected:
   void bindNodeShape (TopoDS_Shape& theShape,
                       const TopLoc_Location& theLoc,
                       const TCollection_AsciiString& theNodeId,
-                      const RWGltf_JsonValue* theUserName)
+                      const RWGltf_JsonValue* theUserName,
+                      const Handle(TDataStd_NamedData)& theExtras)
   {
-    bindNamedShape (theShape, ShapeMapGroup_Nodes, theLoc, theNodeId, theUserName);
+    bindNamedShape (theShape, ShapeMapGroup_Nodes, theLoc, theNodeId, theUserName, theExtras);
   }
 
   //! Bind name attribute.
   void bindMeshShape (TopoDS_Shape& theShape,
                       const TCollection_AsciiString& theMeshId,
-                      const RWGltf_JsonValue* theUserName)
+                      const RWGltf_JsonValue* theUserName,
+                      const Handle(TDataStd_NamedData)& theExtras)
   {
-    bindNamedShape (theShape, ShapeMapGroup_Meshes, TopLoc_Location(), theMeshId, theUserName);
+    bindNamedShape (theShape, ShapeMapGroup_Meshes, TopLoc_Location(), theMeshId, theUserName, theExtras);
   }
 
   //! Find named shape.
@@ -329,7 +331,8 @@ protected:
                                        ShapeMapGroup theGroup,
                                        const TopLoc_Location& theLoc,
                                        const TCollection_AsciiString& theId,
-                                       const RWGltf_JsonValue* theUserName);
+                                       const RWGltf_JsonValue* theUserName,
+                                       const Handle(TDataStd_NamedData)& theExtras);
 
   //! Find named shape.
   bool findNamedShape (TopoDS_Shape& theShape,
@@ -405,12 +408,38 @@ protected:
   };
 #endif
 protected:
-
   //! Print message about invalid glTF syntax.
-  void reportGltfSyntaxProblem (const TCollection_AsciiString& theMsg, Message_Gravity theGravity);
+  void reportGltfSyntaxProblem (const TCollection_AsciiString& theMsg, Message_Gravity theGravity) const;
+
+private:
+  //! Parse transformation matrix of the node.
+  //! @param theSceneNodeId Name of the node. Used only for printing messages.
+  //! @param theMatrixVal Json value containing transformation matrix.
+  //! @param theResult TopLoc_Location object where result of parsing will be written.
+  //! @param If true - parsing was successful, transformation is written into @p theResult.
+  //!        If true - failed to parse, @p theResult is unchanged.
+  bool parseTransformationMatrix(const TCollection_AsciiString& theSceneNodeId,
+                                 const RWGltf_JsonValue& theMatrixVal,
+                                 TopLoc_Location& theResult) const;
+
+  //! Parse transformation components of the node.
+  //! @param theSceneNodeId Name of the node. Used only for printing messages.
+  //! @param theRotationVal Json value containing rotation component of transformation.
+  //!        May be null in which case it is ignored.
+  //! @param theScaleVal Json value containing scale component of transformation.
+  //!        May be null in which case it is ignored.
+  //! @param theTranslationVal Json value containing translation component of transformation.
+  //!        May be null in which case it is ignored.
+  //! @param theResult TopLoc_Location object where result of parsing will be written.
+  //! @param If true - parsing was successful, transformation is written into @p theResult.
+  //!        If true - failed to parse, @p theResult is unchanged.
+  bool parseTransformationComponents(const TCollection_AsciiString& theSceneNodeId,
+                                     const RWGltf_JsonValue* theRotationVal,
+                                     const RWGltf_JsonValue* theScaleVal,
+                                     const RWGltf_JsonValue* theTranslationVal,
+                                     TopLoc_Location& theResult) const;
 
 protected:
-
   TopTools_SequenceOfShape*        myRootShapes;    //!< sequence of result root shapes
   RWMesh_NodeAttributeMap*         myAttribMap;     //!< shape attributes
   NCollection_IndexedMap<TCollection_AsciiString>*
diff --git a/tests/metadata/gltf/A1 b/tests/metadata/gltf/A1
new file mode 100644 (file)
index 0000000..efd60d4
--- /dev/null
@@ -0,0 +1,29 @@
+# !!!! This file is generated automatically, do not edit manually! See end script
+set filename bug28345_30338.stp
+set ref_size 5896
+set check_metadata 1
+set ref_metadata {Property for [0:1:1:1]:
+H_CIP : 55.545955351400004
+Property for [0:1:1:1:1]:
+H : 45
+E : 55
+B : 16
+I : 15
+A : 3
+D : 3
+C : 140
+F : 0.29999999999999999
+DESCRIPTION : 
+MODELED_BY : 
+Property for [0:1:1:1:2]:
+H : 45
+E : 55
+B : 16
+I : 15
+A : 3
+D : 3
+C : 140
+F : 0.29999999999999999
+DESCRIPTION : 
+MODELED_BY : 
+}
diff --git a/tests/metadata/gltf/A2 b/tests/metadata/gltf/A2
new file mode 100644 (file)
index 0000000..e07d965
--- /dev/null
@@ -0,0 +1,26 @@
+# !!!! This file is generated automatically, do not edit manually! See end script
+set filename bug28389_CONFIDENTIAL_SHEET_METAL_F3D.stp
+set ref_size 86278
+set check_metadata 1
+set ref_metadata {Property for [0:1:1:1]:
+yCenterOfGravity : 0.1148447698
+Ixz : 9.3210000000000004e-07
+Izx : 9.3210000000000004e-07
+OriginY : 0
+Surface : 0.34595390710000001
+Volume : 0.0001375456
+Iyz : -1.2030000000000001e-07
+zCenterOfGravity : -0.056064514900000001
+Ixy : 2.044e-07
+Iyy : 3.6385e-06
+xCenterOfGravity : -0.12673526900000001
+Density : 1000
+Izz : 3.3558999999999999e-06
+Ixx : 1.7740000000000001e-06
+Izy : -1.2030000000000001e-07
+Mass : 0.13754561600000001
+Iyx : 2.044e-07
+OriginX : 0
+OriginZ : 0
+JoggleFormula : 
+}
diff --git a/tests/metadata/gltf/A3 b/tests/metadata/gltf/A3
new file mode 100644 (file)
index 0000000..d805b6e
--- /dev/null
@@ -0,0 +1,23 @@
+# !!!! This file is generated automatically, do not edit manually! See end script
+set filename bug28444_nist_ftc_06_asme1_ct5240_rd.stp
+set ref_size 85383
+set check_metadata 1
+set ref_metadata {Property for [0:1:1:1]:
+yCenterOfGravity : 0.0289950044
+Ixz : 0
+Izx : 0
+Surface : 0.28317040780000002
+Volume : 0.0033238733999999999
+Iyz : -1.3068999999999999e-06
+zCenterOfGravity : -0.10963042420000001
+Ixy : 0
+Iyy : 4.46342e-05
+xCenterOfGravity : -0
+Density : 1000
+Izz : 2.63853e-05
+Length : 0
+Ixx : 2.16819e-05
+Izy : -1.3068999999999999e-06
+Mass : 3.3238733752999998
+Iyx : 0
+}
diff --git a/tests/metadata/gltf/A4 b/tests/metadata/gltf/A4
new file mode 100644 (file)
index 0000000..a7f8462
--- /dev/null
@@ -0,0 +1,4 @@
+# !!!! This file is generated automatically, do not edit manually! See end script
+set filename bug29525_rev_part_neu_01.prt_converted_from_datakit.stp
+set ref_size 80996
+set check_metadata 0
diff --git a/tests/metadata/gltf/A5 b/tests/metadata/gltf/A5
new file mode 100644 (file)
index 0000000..cb88744
--- /dev/null
@@ -0,0 +1,18 @@
+# !!!! This file is generated automatically, do not edit manually! See end script
+set filename bug29633_nist_ctc_05_asme1_ap242-1.stp
+set ref_size 69902
+set check_metadata 1
+set ref_metadata {Property for [0:1:1:1]:
+FILESIZE : 1495040
+GDT_STANDARD : 5302
+MaterialMultipleAssigned : FALSE
+ATTR_VERSION : 18.3.001
+FILESAVETIME : Tue Dec 09 03:47:24 2014
+Part Number : NIST PMI CTC 05 ASME1
+Revision : D
+CAD_SOURCE : ug
+MTIME : 1418096844
+MaterialMissingAssignments : TRUE
+FILENAME : nist_ctc_05_asme1.prt
+Description : NIST PMI test model downloaded from http://go.usa.gov/mGVm
+}
diff --git a/tests/metadata/gltf/A6 b/tests/metadata/gltf/A6
new file mode 100644 (file)
index 0000000..fafd4bd
--- /dev/null
@@ -0,0 +1,41 @@
+# !!!! This file is generated automatically, do not edit manually! See end script
+set filename bug29803.stp
+set ref_size 17032
+set check_metadata 1
+set ref_metadata {Property for [0:1:1:1]:
+OUT_MASS : 50.813477444850157
+RELIEF_DIA : 21.005800000000001
+HELIX_LENGTH : 0
+OUT_OAL : 78.049999999992593
+HELIX_START : 0
+OUT_SHANK_LEN : 27.2499999999926
+RCA_SIZE : 0
+SHANK_UNDER : 0
+OUT_REF_LEN : 50.799999999999997
+BODY_LENGTH : 48.514000000000003
+THEO_BLADE_DIA : 11.074400000000001
+BODY_DIA : 0
+DRILL_DEPTH : 47.625
+SHANK_SIZE : 16
+FLUTE_LENGTH : 42.468800000000002
+OUT_SHANK_DIA : 15.999999999999501
+PRIORITY : 0
+OUT_DRILL_DEPTH : 44.754800000000003
+SCREW_HOLE_SKEW : 1.1000000000000001
+SHANK_DIAMETER : 15.999999999999501
+DESCRIPTION : T-A HOLDER
+SS_FLANGE : NO
+MODELED_BY : LSD
+STANDARD_BODY_DIA : Y
+DEEP_HOLE_WEBSITE : 
+ITEM_NUM : HOLDER
+LENGTH : STUB
+FINISH : BLACK OXIDE
+NOTES : 
+SHANK_IM : M
+FLUTE : STRAIGHT
+SHANK : ER
+MATERIAL : STEEL
+SERIES : Y
+DEEP_HOLE_NOTES : 
+}
diff --git a/tests/metadata/gltf/A7 b/tests/metadata/gltf/A7
new file mode 100644 (file)
index 0000000..e45618c
--- /dev/null
@@ -0,0 +1,236 @@
+# !!!! This file is generated automatically, do not edit manually! See end script
+set filename sp7_04-do-242.stp
+set ref_size 224779
+set check_metadata 1
+set ref_metadata {Property for [0:1:1:1]:
+PRO_MP_ALT_COGX : - >
+DESCRIPTION ACCESS : Full
+PRO_MP_TRF_21 DESCRIPTION : NULL
+PRO_MP_IXY DESCRIPTION : NULL
+PRO_MP_VOLUME ACCESS : Locked
+PRO_MP_TRF_23 DESIGNATED : NO
+PRO_MP_COGY DESIGNATED : NO
+PRO_MP_COGY ACCESS : Locked
+PRO_MP_ALT_IYY ACCESS : Full
+PRO_MP_ALT_MASS SOURCE : Alternate Mass Prop
+PRO_MP_ALT_COGY DESCRIPTION : NULL
+PRO_MP_ALT_INERTIA_ORIGIN DESIGNATED : NO
+PRO_MP_ALT_INERTIA_ORIGIN : PRO_MP_ALT_CSYS
+PRO_MP_TRF_31 DESCRIPTION : NULL
+PRO_MP_AREA SOURCE : Mass Properties
+DESCRIPTION DESCRIPTION : NULL
+PRO_MP_ALT_IXY DESIGNATED : NO
+PRO_MP_ALT_VOLUME DESCRIPTION : NULL
+PRO_MP_TRF_13 DESCRIPTION : NULL
+PRO_MP_MASS DESIGNATED : NO
+PRO_MP_COGY : - >
+PRO_MP_AREA DESCRIPTION : NULL
+PRO_MP_DENSITY DESIGNATED : NO
+PRO_MP_ALT_IZZ ACCESS : Full
+PRO_MP_IXX DESCRIPTION : NULL
+PRO_MP_TRF_32 DESIGNATED : NO
+PRO_MP_IYZ DESIGNATED : NO
+PRO_MP_COGY SOURCE : Mass Properties
+PRO_MP_IZZ DESIGNATED : NO
+PRO_MP_TRF_42 SOURCE : Mass Properties
+PRO_MP_ALT_AREA DESIGNATED : NO
+PRO_MP_TRF_12 DESIGNATED : NO
+PRO_MP_ALT_AREA SOURCE : Alternate Mass Prop
+PRO_MP_ALT_INERTIA_ORIGIN DESCRIPTION : NULL
+PRO_MP_CSYS DESCRIPTION : NULL
+PRO_MP_TRF_12 SOURCE : Mass Properties
+PRO_MP_TRF_31 DESIGNATED : NO
+PRO_MP_IYZ : - >
+PRO_MP_TRF_33 : - >
+PRO_MP_ALT_COGY ACCESS : Full
+REVISION SOURCE : User-Defined
+PRO_MP_ALT_COGZ : - >
+PRO_MP_TRF_41 ACCESS : Locked
+PRO_MP_TRF_23 DESCRIPTION : NULL
+PRO_MP_ALT_COGZ DESIGNATED : NO
+PRO_MP_TRF_33 ACCESS : Locked
+PRO_MP_MASS DESCRIPTION : NULL
+PRO_MP_ALT_IZZ : - >
+PRO_MP_TRF_32 : - >
+DESCRIPTION : NIST PMI test model downloaded from http://go.usa.gov/mGVm
+PRO_MP_TRF_22 SOURCE : Mass Properties
+PRO_MP_ALT_INERTIA_ORIGIN ACCESS : Full
+PRO_MP_ALT_VOLUME : - >
+PRO_MP_TRF_41 SOURCE : Mass Properties
+PRO_MP_ALT_CSYS : DEFAULT
+MP_DENSITY : - >
+PRO_MP_IZZ SOURCE : Mass Properties
+PRO_MP_IYY DESCRIPTION : NULL
+PRO_MP_MASS SOURCE : Mass Properties
+PRO_MP_ALT_MASS : - >
+PRO_MP_ALT_VOLUME SOURCE : Alternate Mass Prop
+PRO_MP_ALT_IYY DESCRIPTION : NULL
+DESCRIPTION SOURCE : User-Defined
+PRO_MP_TRF_23 SOURCE : Mass Properties
+PRO_MP_ALT_IYZ : - >
+PRO_MP_MASS : - >
+PRO_MP_DENSITY ACCESS : Locked
+PRO_MP_DENSITY SOURCE : Mass Properties
+PRO_MP_ALT_COGZ DESCRIPTION : NULL
+PRO_MP_ALT_IXZ : - >
+PRO_MP_ALT_IZZ DESCRIPTION : NULL
+PRO_MP_ALT_IYY SOURCE : Alternate Mass Prop
+PRO_MP_IYZ ACCESS : Locked
+PRO_MP_ALT_IXZ DESIGNATED : NO
+PRO_MP_IXY ACCESS : Locked
+PRO_MP_TRF_13 ACCESS : Locked
+PRO_MP_DENSITY DESCRIPTION : NULL
+PRO_MP_AREA ACCESS : Locked
+PRO_MP_TRF_31 : - >
+PRO_MP_IYZ DESCRIPTION : NULL
+PRO_MP_IYY SOURCE : Mass Properties
+PRO_MP_COGX ACCESS : Locked
+PRO_MP_COGZ : - >
+PRO_MP_IYY DESIGNATED : NO
+PRO_MP_TRF_33 DESCRIPTION : NULL
+PRO_MP_ALT_IZZ SOURCE : Alternate Mass Prop
+PRO_MP_IXX : - >
+PRO_MP_TRF_11 ACCESS : Locked
+PRO_MP_TRF_11 DESIGNATED : NO
+PRO_MP_CSYS SOURCE : Mass Properties
+PRO_MP_ALT_COGY SOURCE : Alternate Mass Prop
+PRO_MP_INERTIA_ORIGIN ACCESS : Locked
+PRO_MP_TRF_21 : - >
+PART_NUMBER DESCRIPTION : NULL
+MP_DENSITY SOURCE : Alternate Mass Prop
+PRO_MP_ALT_IYZ ACCESS : Full
+PRO_MP_COGX DESIGNATED : NO
+PRO_MP_TRF_41 : - >
+PRO_MP_TRF_11 SOURCE : Mass Properties
+PRO_MP_TRF_32 DESCRIPTION : NULL
+PRO_MP_ALT_IXX DESCRIPTION : NULL
+PRO_MP_IXX SOURCE : Mass Properties
+PRO_MP_ALT_CSYS DESCRIPTION : NULL
+PRO_MP_TRF_13 : - >
+PRO_MP_IYY : - >
+PRO_MP_TRF_21 SOURCE : Mass Properties
+PRO_MP_SOURCE DESCRIPTION : NULL
+PRO_MP_TRF_32 SOURCE : Mass Properties
+PRO_MP_IZZ ACCESS : Locked
+PRO_MP_TRF_42 DESCRIPTION : NULL
+PRO_MP_ALT_COGX DESIGNATED : NO
+PRO_MP_TRF_13 SOURCE : Mass Properties
+PRO_MP_ALT_IXX ACCESS : Full
+PRO_MP_ALT_AREA : - >
+PRO_MP_ALT_COGX SOURCE : Alternate Mass Prop
+PRO_MP_AREA DESIGNATED : NO
+PRO_MP_IXY SOURCE : Mass Properties
+PRO_MP_SOURCE DESIGNATED : NO
+PRO_MP_TRF_22 ACCESS : Locked
+PRO_MP_VOLUME SOURCE : Mass Properties
+MP_DENSITY DESIGNATED : NO
+PRO_MP_IZZ DESCRIPTION : NULL
+PRO_MP_IXX ACCESS : Locked
+PRO_MP_TRF_31 ACCESS : Locked
+PRO_MP_AREA : - >
+PRO_MP_CSYS : - >
+PRO_MP_ALT_COGX ACCESS : Full
+PRO_MP_ALT_IYZ DESIGNATED : NO
+PRO_MP_TRF_42 DESIGNATED : NO
+PRO_MP_ALT_COGY : - >
+PRO_MP_IXZ DESIGNATED : NO
+PRO_MP_CSYS DESIGNATED : NO
+PRO_MP_IZZ : - >
+PRO_MP_TRF_12 DESCRIPTION : NULL
+PRO_MP_ALT_IXZ ACCESS : Full
+PRO_MP_INERTIA_ORIGIN : - >
+PRO_MP_IXY DESIGNATED : NO
+PRO_MP_TRF_33 DESIGNATED : NO
+PRO_MP_ALT_COGY DESIGNATED : NO
+PRO_MP_ALT_AREA DESCRIPTION : NULL
+PRO_MP_IXZ DESCRIPTION : NULL
+PRO_MP_INERTIA_ORIGIN DESCRIPTION : NULL
+PRO_MP_ALT_IYY DESIGNATED : NO
+PRO_MP_IYY ACCESS : Locked
+PRO_MP_COGZ ACCESS : Locked
+PRO_MP_SOURCE : GEOMETRY
+PRO_MP_COGX DESCRIPTION : NULL
+PRO_MP_ALT_IYZ DESCRIPTION : NULL
+PRO_MP_IXZ SOURCE : Mass Properties
+PRO_MP_TRF_23 ACCESS : Locked
+PRO_MP_ALT_IXY : - >
+PRO_MP_ALT_IYZ SOURCE : Alternate Mass Prop
+PRO_MP_TRF_42 ACCESS : Locked
+PRO_MP_ALT_INERTIA_ORIGIN SOURCE : Alternate Mass Prop
+REVISION : D
+PRO_MP_ALT_IXY ACCESS : Full
+DESCRIPTION DESIGNATED : YES
+PRO_MP_TRF_22 DESCRIPTION : NULL
+PRO_MP_TRF_12 ACCESS : Locked
+PRO_MP_SOURCE ACCESS : Full
+REVISION DESIGNATED : YES
+PRO_MP_IXZ ACCESS : Locked
+PRO_MP_TRF_43 DESCRIPTION : NULL
+PRO_MP_COGY DESCRIPTION : NULL
+PRO_MP_INERTIA_ORIGIN DESIGNATED : NO
+PRO_MP_TRF_12 : - >
+REVISION DESCRIPTION : NULL
+PRO_MP_ALT_IXY SOURCE : Alternate Mass Prop
+PRO_MP_TRF_11 DESCRIPTION : NULL
+PRO_MP_ALT_MASS DESIGNATED : NO
+PRO_MP_TRF_11 : - >
+PRO_MP_TRF_43 SOURCE : Mass Properties
+PART_NUMBER ACCESS : Full
+PRO_MP_VOLUME DESCRIPTION : NULL
+PRO_MP_ALT_IXY DESCRIPTION : NULL
+PRO_MP_COGZ DESCRIPTION : NULL
+PRO_MP_COGX : - >
+PRO_MP_SOURCE SOURCE : Alternate Mass Prop
+PRO_MP_ALT_IXX : - >
+PRO_MP_TRF_22 DESIGNATED : NO
+PRO_MP_TRF_42 : - >
+PRO_MP_INERTIA_ORIGIN SOURCE : Mass Properties
+PRO_MP_COGZ DESIGNATED : NO
+PRO_MP_TRF_31 SOURCE : Mass Properties
+PART_NUMBER DESIGNATED : YES
+PRO_MP_COGX SOURCE : Mass Properties
+PRO_MP_TRF_23 : - >
+PRO_MP_IXX DESIGNATED : NO
+PRO_MP_ALT_CSYS ACCESS : Full
+PRO_MP_CSYS ACCESS : Locked
+PRO_MP_TRF_22 : - >
+PRO_MP_TRF_33 SOURCE : Mass Properties
+PRO_MP_COGZ SOURCE : Mass Properties
+PRO_MP_VOLUME DESIGNATED : NO
+PRO_MP_ALT_IXZ DESCRIPTION : NULL
+PART_NUMBER SOURCE : User-Defined
+PRO_MP_TRF_32 ACCESS : Locked
+PRO_MP_IYZ SOURCE : Mass Properties
+PRO_MP_TRF_43 DESIGNATED : NO
+PRO_MP_ALT_VOLUME ACCESS : Full
+PRO_MP_ALT_COGZ SOURCE : Alternate Mass Prop
+PRO_MP_TRF_21 DESIGNATED : NO
+MP_DENSITY DESCRIPTION : NULL
+PRO_MP_TRF_41 DESIGNATED : NO
+REVISION ACCESS : Full
+PRO_MP_ALT_COGX DESCRIPTION : NULL
+PRO_MP_ALT_IZZ DESIGNATED : NO
+PRO_MP_DENSITY : 0.000000
+PRO_MP_IXY : - >
+PRO_MP_ALT_MASS ACCESS : Full
+PRO_MP_ALT_CSYS DESIGNATED : NO
+PRO_MP_TRF_43 : - >
+PRO_MP_ALT_IXZ SOURCE : Alternate Mass Prop
+PRO_MP_IXZ : - >
+PRO_MP_MASS ACCESS : Locked
+PRO_MP_ALT_IXX SOURCE : Alternate Mass Prop
+PRO_MP_ALT_COGZ ACCESS : Full
+PRO_MP_VOLUME : - >
+PRO_MP_ALT_IXX DESIGNATED : NO
+MP_DENSITY ACCESS : Full
+PRO_MP_TRF_21 ACCESS : Locked
+PRO_MP_ALT_IYY : - >
+PRO_MP_TRF_41 DESCRIPTION : NULL
+PRO_MP_ALT_MASS DESCRIPTION : NULL
+PRO_MP_TRF_13 DESIGNATED : NO
+PRO_MP_ALT_CSYS SOURCE : Alternate Mass Prop
+PRO_MP_ALT_VOLUME DESIGNATED : NO
+PART_NUMBER : NIST PMI CTC 04 ASME1
+PRO_MP_TRF_43 ACCESS : Locked
+PRO_MP_ALT_AREA ACCESS : Full
+}
diff --git a/tests/metadata/gltf/A8 b/tests/metadata/gltf/A8
new file mode 100644 (file)
index 0000000..cdb51b5
--- /dev/null
@@ -0,0 +1,68 @@
+# !!!! This file is generated automatically, do not edit manually! See end script
+set filename bug32087_part.stp
+set ref_size 15789
+set check_metadata 1
+set ref_metadata {Property for [0:1:1:1]:
+SETUP_PART : 0
+MODEL_3D_REVISION : 1
+MATERIAL_DENSITY : 7850
+WEIGHT_PROTOTYPE : 0
+WEIGHT_FINAL : 0
+WEIGHT_CALCULATED : 0.0070751592515700002
+SUPPLIER_NAME : 
+SEMI_FINISHED_PRODUCT : 
+REFERENCE : 
+REFERENCE_DESIGNATION++ : 
+MODEL_3D_APPROVED_BY : 
+MODEL_3D_CAD_SYSTEM : 
+PART_NAME : 
+MODEL_3D_CHECKED_BY : 
+OWNER : 
+COPYRIGHT : 
+WORK_ORDER_NUMBER : 
+PART_NUMBER : 
+ID_NUMBER_MATERIAL : 
+SPARE_WEARING_PART : 
+GENERAL_TOLERANCES_DRILL_HOLE : 
+TREATMENT : 
+MODEL_3D_CREATED_BY : 
+UNIT_SYSTEM : 
+MODEL_3D_DATE_OF_ISSUE : 
+WELD_TOLERANCES : 
+PROJECT : 
+LANGUAGE : 
+MODEL_3D_CREATED_BY_DEPARTMENT : 
+MODEL_3D_RELEASED_STATUS : 
+TECHNICAL_SPECIFICATION : 
+SUPPLIER_NUMBER : 
+SURFACE_PROTECTION : 
+EDGE_CONDITION : 
+GENERAL_TOLERANCES : 
+EDGE_CONDITION_INNER_EDGE : 
+ORDER_NUMBER : 
+GENERAL_TOLERANCES_FIT_DRILL_HOLE : 
+REFERENCE_DESIGNATION= : 
+SURFACE_ROUGHNESS : 
+REFERENCE_DESIGNATION- : 
+TOLERANCING_PRINCIPLE : 
+TECHNICAL_DIRECTIVE : 
+STOCK_NUMBER : 
+MODEL_3D_APPROVED_BY_DEPARTMENT : 
+PART_REVISION_LEVEL : 
+EDGE_CONDITION_OUTER_EDGE : 
+ARTICLE_NUMBER : 
+MATERIAL : 
+REFERENCE_DESIGNATION== : 
+SIMPLIFIED_DRAWING_REVISION : 
+MODEL_3D_REVISION_LEVEL : 
+MODEL_3D_RELEASED_BY : 
+MODEL_3D_REPLACED_BY : 
+REFERENCE_DESIGNATION+ : 
+MODEL_3D_ID_NUMBER : 
+PART_NOTE : 
+WELD_PREPARATION : 
+MODEL_3D_RELEASED_DATE : 
+MODEL_3D_REPLACES : 
+PAINT_SURFACE : 
+PART_SOURCE : 
+}
diff --git a/tests/metadata/gltf/A9 b/tests/metadata/gltf/A9
new file mode 100644 (file)
index 0000000..b99ad90
--- /dev/null
@@ -0,0 +1,9 @@
+# !!!! This file is generated automatically, do not edit manually! See end script
+set filename nist_ftc_08_asme1_ap242-2.stp
+set ref_size 118200
+set check_metadata 1
+set ref_metadata {Property for [0:1:1:1]:
+Revision : C
+PartNumber : NIST PMI FTC 08 ASME1
+DescriptionRef : NIST PMI test model downloaded from http://go.usa.gov/mGVm
+}
diff --git a/tests/metadata/gltf/begin b/tests/metadata/gltf/begin
new file mode 100644 (file)
index 0000000..56cb94b
--- /dev/null
@@ -0,0 +1 @@
+pload MODELING
diff --git a/tests/metadata/gltf/end b/tests/metadata/gltf/end
new file mode 100644 (file)
index 0000000..0e571bc
--- /dev/null
@@ -0,0 +1,76 @@
+# Set flag dump_file to 1 in order to regenerate script files with actual data
+# used as reference. In this mode all tests intentionally report failure. 
+set dump_file 0
+
+# Read original file
+if { [string length $filename] > 1} {
+  set path_file [locate_data_file $filename]
+  if { [catch { ReadFile aDocExport $path_file } catch_result] } {
+    set err_msg "Error: file was not read - exception "
+    puts $err_msg
+  }
+}
+
+# mesh the shape before Gltf writing
+XGetOneShape a aDocExport
+incmesh a 0.1
+
+# write file
+WriteGltf aDocExport $imagedir/${casename}_D_First.gltf
+set aSize [file size $imagedir/${casename}_D_First.gltf]
+
+# Import created Gltf file and get its metadata.
+ReadGltf aDocImport $imagedir/${casename}_D_First.gltf
+set aMetaDataBase [ XGetProperties aDocImport ]
+set aMetaData [format $aMetaDataBase]
+
+
+if { $dump_file == 1 } { 
+    set fd_stream  [open $dirname/$groupname/$gridname/$casename w] 
+    puts $fd_stream "# !!!! This file is generated automatically, do not edit manually! See end script"
+    puts $fd_stream "set filename $filename"
+    puts $fd_stream "set ref_size $aSize"
+    puts $fd_stream "set check_metadata $check_metadata"
+    if {$check_metadata == 1} {
+      puts $fd_stream "set ref_metadata \{$aMetaData\}"
+    }
+    close $fd_stream
+    puts "Error : Running in regeneration mode, comparison was not performed!"
+} else {
+    if {$aSize != $ref_size} {
+        puts "Error: Wrong file size $aSize instead of $ref_size"
+    }
+    
+    if {$check_metadata == 1} {
+      # Compare metadata in files.
+      set aMetaDataLines [split ${aMetaData} "\n"]
+      set aRefMetaDataLines [split ${ref_metadata} "\n"]
+      set aMetaDataLinesCount [llength $aMetaDataLines]
+      set aRefMetaDataLinesCount [llength $aRefMetaDataLines]
+      set aMinLineCount [expr min($aMetaDataLinesCount, $aRefMetaDataLinesCount)]
+      for {set aLineIndex 0} {$aLineIndex < $aMinLineCount} {incr aLineIndex} {
+        set aCurrentMetaDataLine [lindex $aMetaDataLines $aLineIndex]
+        set aCurrentRefMetaDataLine [lindex $aRefMetaDataLines $aLineIndex]
+        if {$aCurrentMetaDataLine != $aCurrentRefMetaDataLine} {
+          puts "Error: Incorrect metadata at line $aLineIndex"
+          puts "Expected: \"$aCurrentRefMetaDataLine\""
+          puts "Actual:   \"$aCurrentMetaDataLine\""
+          break
+        }
+      }
+      # It is faster to check this condition first, before string-by-string comparison,
+      # however string-by-string comparison would be more informative in case of error.
+      if {$aMetaDataLinesCount != $aRefMetaDataLinesCount} {
+           puts "Error: Line count is metadata doesn't match the expected value."
+          puts "Expected: $aRefMetaDataLinesCount"
+          puts "Actual:   $aMetaDataLinesCount"
+      }
+    }
+}
+
+# finalize scenario
+Close aDocImport
+Close aDocExport
+file delete $imagedir/${casename}_D_First.gltf
+file delete $imagedir/${casename}_D_First.bin
+puts "TEST COMPLETED"
diff --git a/tests/metadata/gltf_export/A1 b/tests/metadata/gltf_export/A1
deleted file mode 100644 (file)
index 836d0e9..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-# !!!! This file is generated automatically, do not edit manually! See end script
-set filename bug28345_30338.stp
-set ref_size 5896
diff --git a/tests/metadata/gltf_export/A2 b/tests/metadata/gltf_export/A2
deleted file mode 100644 (file)
index 734de94..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-# !!!! This file is generated automatically, do not edit manually! See end script
-set filename bug28389_CONFIDENTIAL_SHEET_METAL_F3D.stp
-set ref_size 86278
diff --git a/tests/metadata/gltf_export/A3 b/tests/metadata/gltf_export/A3
deleted file mode 100644 (file)
index b1020fd..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-# !!!! This file is generated automatically, do not edit manually! See end script
-set filename bug28444_nist_ftc_06_asme1_ct5240_rd.stp
-set ref_size 85383
diff --git a/tests/metadata/gltf_export/A4 b/tests/metadata/gltf_export/A4
deleted file mode 100644 (file)
index 6cdc3c7..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-# !!!! This file is generated automatically, do not edit manually! See end script
-set filename bug29525_rev_part_neu_01.prt_converted_from_datakit.stp
-set ref_size 80996
diff --git a/tests/metadata/gltf_export/A5 b/tests/metadata/gltf_export/A5
deleted file mode 100644 (file)
index 3e132d8..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-# !!!! This file is generated automatically, do not edit manually! See end script
-set filename bug29633_nist_ctc_05_asme1_ap242-1.stp
-set ref_size 69902
diff --git a/tests/metadata/gltf_export/A6 b/tests/metadata/gltf_export/A6
deleted file mode 100644 (file)
index db247e7..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-# !!!! This file is generated automatically, do not edit manually! See end script
-set filename bug29803.stp
-set ref_size 17032
diff --git a/tests/metadata/gltf_export/A7 b/tests/metadata/gltf_export/A7
deleted file mode 100644 (file)
index 3bb42fa..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-# !!!! This file is generated automatically, do not edit manually! See end script
-set filename sp7_04-do-242.stp
-set ref_size 224779
diff --git a/tests/metadata/gltf_export/A8 b/tests/metadata/gltf_export/A8
deleted file mode 100644 (file)
index 8c2edc7..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-# !!!! This file is generated automatically, do not edit manually! See end script
-set filename bug32087_part.stp
-set ref_size 15789
diff --git a/tests/metadata/gltf_export/A9 b/tests/metadata/gltf_export/A9
deleted file mode 100644 (file)
index 2428cb4..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-# !!!! This file is generated automatically, do not edit manually! See end script
-set filename nist_ftc_08_asme1_ap242-2.stp
-set ref_size 118200
diff --git a/tests/metadata/gltf_export/begin b/tests/metadata/gltf_export/begin
deleted file mode 100644 (file)
index 56cb94b..0000000
+++ /dev/null
@@ -1 +0,0 @@
-pload MODELING
diff --git a/tests/metadata/gltf_export/end b/tests/metadata/gltf_export/end
deleted file mode 100644 (file)
index 8e4881b..0000000
+++ /dev/null
@@ -1,39 +0,0 @@
-# Set flag dump_file to 1 in order to regenerate script files with actual data
-# used as reference. In this mode all tests intentionally report failure. 
-set dump_file 0
-
-# Read original file
-if { [string length $filename] > 1} {
-  set path_file [locate_data_file $filename]
-  if { [catch { ReadFile D $path_file } catch_result] } {
-    set err_msg "Error: file was not read - exception "
-    puts $err_msg
-  }
-}
-
-# mesh the shape before Gltf writing
-XGetOneShape a D
-incmesh a 0.1
-
-# write file
-WriteGltf D $imagedir/${casename}_D_First.gltf
-set aSize [file size $imagedir/${casename}_D_First.gltf]
-
-if { $dump_file == 1 } { 
-    set fd_stream  [open $dirname/$groupname/$gridname/$casename w] 
-    puts $fd_stream "# !!!! This file is generated automatically, do not edit manually! See end script"
-    puts $fd_stream "set filename $filename"
-    puts $fd_stream "set ref_size $aSize"
-    close $fd_stream
-    puts "Error : Running in regeneration mode, comparison was not performed!"
-} else {
-    if {$aSize != $ref_size} {
-        puts "Error: Wrong file size $aSize instead of $ref_size"
-    }
-}
-
-# finalize scenario
-Close D
-file delete $imagedir/${casename}_D_First.gltf
-file delete $imagedir/${casename}_D_First.bin
-puts "TEST COMPLETED"
index 3dc7702c6b8b3a107cbc634781779178f00cf6b6..5a589c2a0dedb089509212e0bb515d20c98749e5 100644 (file)
@@ -1,2 +1,2 @@
 001 step
-002 gltf_export
+002 gltf