From 640018bfa7878a79e1b3c220ddd488d66464dc70 Mon Sep 17 00:00:00 2001 From: wmayer Date: Wed, 8 Nov 2023 23:20:57 +0100 Subject: [PATCH] JtReader: allow to open JT files using TKJT --- src/Mod/JtReader/App/AppJtReaderPy.cpp | 48 ++++- src/Mod/JtReader/App/CMakeLists.txt | 17 ++ src/Mod/JtReader/App/TKJtReader.cpp | 270 +++++++++++++++++++++++++ src/Mod/JtReader/App/TKJtReader.h | 76 +++++++ 4 files changed, 401 insertions(+), 10 deletions(-) create mode 100644 src/Mod/JtReader/App/TKJtReader.cpp create mode 100644 src/Mod/JtReader/App/TKJtReader.h diff --git a/src/Mod/JtReader/App/AppJtReaderPy.cpp b/src/Mod/JtReader/App/AppJtReaderPy.cpp index ddeb0ec6cc..d4b369ede9 100644 --- a/src/Mod/JtReader/App/AppJtReaderPy.cpp +++ b/src/Mod/JtReader/App/AppJtReaderPy.cpp @@ -24,6 +24,7 @@ #include #include +#include #include #include #include @@ -34,6 +35,7 @@ #include #include "TestJtReader.h" +#include "TKJtReader.h" using std::vector; @@ -64,9 +66,10 @@ public: } private: + // NOLINTBEGIN Py::Object read(const Py::Tuple& args) { - char* Name; + char* Name {}; if (!PyArg_ParseTuple(args.ptr(), "et", "utf-8", &Name)) { throw Py::Exception(); } @@ -77,7 +80,7 @@ private: } Py::Object open(const Py::Tuple& args) { - char* Name; + char* Name {}; if (!PyArg_ParseTuple(args.ptr(), "et", "utf-8", &Name)) { throw Py::Exception(); } @@ -87,11 +90,24 @@ private: // Base::Console().Log("Open in Mesh with %s",Name); - Base::FileInfo file(EncodedName.c_str()); + Base::FileInfo file(EncodedName); if (file.hasExtension("jt")) { TestJtReader reader; - reader.setFile(EncodedName.c_str()); + reader.setFile(EncodedName); reader.read(); + +#ifdef JTREADER_HAVE_TKJT + JtReaderNS::TKJtReader jtReader; + jtReader.open(EncodedName); + + App::Document* doc = App::GetApplication().newDocument(); + std::string objname = file.fileNamePure(); + auto iv = dynamic_cast( + doc->addObject("App::InventorObject", objname.c_str())); + iv->Buffer.setValue(jtReader.getOutput()); + iv->purgeTouched(); +#endif + return Py::None(); } @@ -99,8 +115,8 @@ private: } Py::Object importer(const Py::Tuple& args) { - char* Name; - const char* DocName; + char* Name {}; + const char* DocName {}; if (!PyArg_ParseTuple(args.ptr(), "ets", "utf-8", &Name, &DocName)) { throw Py::Exception(); } @@ -112,21 +128,33 @@ private: if (file.hasExtension("jt")) { // add Import feature - App::Document* pcDoc = App::GetApplication().getDocument(DocName); - if (!pcDoc) { - pcDoc = App::GetApplication().newDocument(DocName); + App::Document* doc = App::GetApplication().getDocument(DocName); + if (!doc) { + doc = App::GetApplication().newDocument(DocName); } +#ifdef JTREADER_HAVE_TKJT + JtReaderNS::TKJtReader jtReader; + jtReader.open(EncodedName); + + std::string objname = file.fileNamePure(); + auto iv = dynamic_cast( + doc->addObject("App::InventorObject", objname.c_str())); + iv->Buffer.setValue(jtReader.getOutput()); + iv->purgeTouched(); +#endif + return Py::None(); } throw Py::RuntimeError("unknown file ending"); } + // NOLINTEND }; PyObject* initModule() { - return Base::Interpreter().addModule(new Module); + return Base::Interpreter().addModule(new Module); // NOLINT } } // namespace JtReaderNS diff --git a/src/Mod/JtReader/App/CMakeLists.txt b/src/Mod/JtReader/App/CMakeLists.txt index 5286ef896c..2425040cfd 100644 --- a/src/Mod/JtReader/App/CMakeLists.txt +++ b/src/Mod/JtReader/App/CMakeLists.txt @@ -1,3 +1,6 @@ +set(JTREADER_TKJT_INCLUDE_DIRS CACHE PATH "Include directory of TKJT headers") +set(JTREADER_TKJT_LIBRARIES CACHE FILEPATH "File path to TKJT library") + include_directories( ${CMAKE_BINARY_DIR} ${CMAKE_CURRENT_BINARY_DIR} @@ -12,6 +15,7 @@ link_directories(${OCC_LIBRARY_DIR}) set(JtReader_LIBS Mesh + ${OCC_LIBRARIES} #${OCC_OCAF_LIBRARIES} #${OCC_OCAF_DEBUG_LIBRARIES} ) @@ -27,6 +31,19 @@ SET(JtReader_SRCS FcLodHandler.cpp ) +if (EXISTS "${JTREADER_TKJT_INCLUDE_DIRS}/JtData_Object.hxx") + add_definitions(-DJTREADER_HAVE_TKJT) + include_directories(${JTREADER_TKJT_INCLUDE_DIRS}) + list (APPEND JtReader_LIBS + ${JTREADER_TKJT_LIBRARIES} + ) + + list (APPEND JtReader_SRCS + TKJtReader.cpp + TKJtReader.h + ) +endif () + set (JtReader_Scripts ../Init.py ) diff --git a/src/Mod/JtReader/App/TKJtReader.cpp b/src/Mod/JtReader/App/TKJtReader.cpp new file mode 100644 index 0000000000..66b5d696fc --- /dev/null +++ b/src/Mod/JtReader/App/TKJtReader.cpp @@ -0,0 +1,270 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later + +/*************************************************************************** + * Copyright (c) 2023 Werner Mayer * + * * + * This file is part of FreeCAD. * + * * + * FreeCAD is free software: you can redistribute it and/or modify it * + * under the terms of the GNU Lesser General Public License as * + * published by the Free Software Foundation, either version 2.1 of the * + * License, or (at your option) any later version. * + * * + * FreeCAD is distributed in the hope that it will be useful, but * + * WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with FreeCAD. If not, see * + * . * + * * + **************************************************************************/ + +#include "PreCompiled.h" +#include + +#include "TKJtReader.h" +#include +#include + +using namespace JtReaderNS; + +namespace +{ +// clang-format off +const Handle(Standard_Type) Type_JtAttribute_GeometricTransform = STANDARD_TYPE(JtAttribute_GeometricTransform); +const Handle(Standard_Type) Type_JtAttribute_Material = STANDARD_TYPE(JtAttribute_Material); +const Handle(Standard_Type) Type_JtNode_Partition = STANDARD_TYPE(JtNode_Partition); +const Handle(Standard_Type) Type_JtNode_Group = STANDARD_TYPE(JtNode_Group); +const Handle(Standard_Type) Type_JtNode_Instance = STANDARD_TYPE(JtNode_Instance); +const Handle(Standard_Type) Type_JtNode_RangeLOD = STANDARD_TYPE(JtNode_RangeLOD); +const Handle(Standard_Type) Type_JtNode_Shape_Base = STANDARD_TYPE(JtNode_Shape_Base); +const Handle(Standard_Type) Type_JtNode_Shape_Vertex = STANDARD_TYPE(JtNode_Shape_Vertex); +const Handle(Standard_Type) Type_JtNode_Shape_TriStripSet = STANDARD_TYPE(JtNode_Shape_TriStripSet); +// clang-format on +} // namespace + +TKJtReader::TKJtReader() + : builder {result} +{} + +void TKJtReader::clear() +{ + result.str(std::string()); + result.clear(); +} + +void TKJtReader::open(const std::string& filename) +{ + clear(); + + Base::FileInfo file(filename); + jtDir = TCollection_ExtendedString(TCollection_AsciiString((file.dirPath() + '/').c_str())); + + TCollection_ExtendedString aFileName(filename.c_str()); + Handle(JtData_Model) aModel = new JtData_Model(aFileName); + + if (!aModel.IsNull()) { + Handle(JtNode_Partition) aNode = aModel->Init(); + if (!aNode.IsNull()) { + rootNode = aNode; + + builder.addHeader(); + builder.beginSeparator(); + Base::ShapeHintsItem shapeHints; + shapeHints.setShapeType(Base::ShapeType::Type::UnknownShapeType); + shapeHints.setVertexOrdering(Base::VertexOrdering::Ordering::CounterClockwise); + builder.addNode(shapeHints); + traverseGraph(aNode); + builder.endSeparator(); + rootNode.Nullify(); + } + } +} + +std::string TKJtReader::getOutput() const +{ + return result.str(); +} + +void TKJtReader::readMaterialAttribute(const Handle(JtAttribute_Material) & aMaterial) +{ + const Jt_F32* ambient = aMaterial->AmbientColor(); + const Jt_F32* diffuse = aMaterial->DiffuseColor(); + const Jt_F32* specular = aMaterial->SpecularColor(); + // const Jt_F32* emissive = aMaterial->EmissionColor(); + Jt_F32 shininess = aMaterial->Shininess() / 100.0F; + + // NOLINTBEGIN + Base::MaterialItem item; + item.setAmbientColor({Base::ColorRGB(ambient[0], ambient[1], ambient[2])}); + item.setDiffuseColor({Base::ColorRGB(diffuse[0], diffuse[1], diffuse[2])}); + item.setSpecularColor({Base::ColorRGB(specular[0], specular[1], specular[2])}); + // Ignore the emissive color as there are sometimes some weird values + // item.setEmissiveColor({Base::ColorRGB(emissive[0], emissive[1], emissive[2])}); + item.setShininess({shininess}); + // NOLINTEND + + builder.addNode(item); +} + +void TKJtReader::readTransformAttribute(const Handle(JtAttribute_GeometricTransform) & aTransform) +{ + gp_Trsf trsf; + aTransform->GetTrsf(trsf); + gp_Mat mat = trsf.VectorialPart(); + gp_XYZ xyz = trsf.TranslationPart(); + Base::Matrix4D mtrx; + // NOLINTBEGIN + mtrx[0][0] = mat(1, 1); + mtrx[0][1] = mat(1, 2); + mtrx[0][2] = mat(1, 3); + + mtrx[1][0] = mat(2, 1); + mtrx[1][1] = mat(2, 2); + mtrx[1][2] = mat(2, 3); + + mtrx[2][0] = mat(3, 1); + mtrx[2][1] = mat(3, 2); + mtrx[2][2] = mat(3, 3); + + // set pos vector + mtrx[0][3] = xyz.X(); + mtrx[1][3] = xyz.Y(); + mtrx[2][3] = xyz.Z(); + // NOLINTEND + + transformations.push_back(mtrx); +} + +void TKJtReader::readShapeVertex(const Handle(JtNode_Shape_Vertex) & aShape) +{ + const JtData_Object::VectorOfLateLoads& aLateLoaded = aShape->LateLoads(); + for (std::size_t index = 0; index < aLateLoaded.Count(); ++index) { + Handle(JtData_Object) anObject = aLateLoaded[index]->DefferedObject(); + if (anObject.IsNull()) { + aLateLoaded[index]->Load(); + anObject = aLateLoaded[index]->DefferedObject(); + } + if (!anObject.IsNull()) { + Handle(JtElement_ShapeLOD_TriStripSet) aLOD = + Handle(JtElement_ShapeLOD_TriStripSet)::DownCast(anObject); + if (!aLOD.IsNull()) { + getTriangleStripSet(aLOD); + } + } + } +} + +void TKJtReader::getTriangleStripSet(const Handle(JtElement_ShapeLOD_TriStripSet) & aLOD) +{ + const int pointsPerFace = 3; + std::vector points; + std::vector faces; + const JtElement_ShapeLOD_Vertex::VertexData& vertices = aLOD->Vertices(); + const JtElement_ShapeLOD_Vertex::IndicesVec& indices = aLOD->Indices(); + float* data = vertices.Data(); + // NOLINTBEGIN + for (int index = 0; index < indices.Count(); index += 3) { + int coordIndex = indices[index] * 3; + points.emplace_back(data[coordIndex], data[coordIndex + 1], data[coordIndex + 2]); + coordIndex = indices[index + 1] * 3; + points.emplace_back(data[coordIndex], data[coordIndex + 1], data[coordIndex + 2]); + coordIndex = indices[index + 2] * 3; + points.emplace_back(data[coordIndex], data[coordIndex + 1], data[coordIndex + 2]); + faces.push_back(pointsPerFace); + } + // NOLINTEND + builder.addNode(Base::Coordinate3Item {points}); + builder.addNode(Base::FaceSetItem {faces}); +} + +void TKJtReader::readPartition(const Handle(JtNode_Partition) & aPart) +{ + if (rootNode != aPart) { + TCollection_ExtendedString aFullPath = jtDir + aPart->FileName(); + TCollection_AsciiString aFileName(aFullPath); + Handle(JtData_Model) aModel = new JtData_Model(aFullPath); + Handle(JtNode_Partition) aNode = aModel->Init(); + if (!aNode.IsNull()) { + builder.addNode(Base::InfoItem(std::string(aFileName.ToCString()))); + traverseGraph(aNode); + } + } +} + +void TKJtReader::readGroup(const Handle(JtNode_Group) & aGroup) +{ + if (!transformations.empty()) { + builder.addNode(Base::TransformItem {transformations.back()}); + transformations.pop_back(); + } + const auto& aChildren = aGroup->Children(); + for (std::size_t aChildIdx = 0; aChildIdx < aChildren.Count(); ++aChildIdx) { + Handle(JtNode_Base) aChild = Handle(JtNode_Base)::DownCast(aChildren[aChildIdx]); + traverseGraph(aChild); + } +} + +void TKJtReader::readRangeLOD(const Handle(JtNode_RangeLOD) & aRangeLOD) +{ + readGroup(aRangeLOD); +} + +void TKJtReader::readInstance(const Handle(JtNode_Instance) & anInstance) +{ + Handle(JtNode_Base) aBase = Handle(JtNode_Base)::DownCast(anInstance->Object()); + if (!aBase.IsNull()) { + traverseGraph(aBase); + } +} + +void TKJtReader::readAttributes(const JtData_Object::VectorOfObjects& attr) +{ + for (std::size_t aAttrIdx = 0; aAttrIdx < attr.Count(); ++aAttrIdx) { + Handle(JtData_Object) anAttr = attr[aAttrIdx]; + if (anAttr->IsKind(Type_JtAttribute_GeometricTransform)) { + Handle(JtAttribute_GeometricTransform) aTransform = + Handle(JtAttribute_GeometricTransform)::DownCast(anAttr); + readTransformAttribute(aTransform); + } + else if (anAttr->IsKind(Type_JtAttribute_Material)) { + Handle(JtAttribute_Material) aMaterial = Handle(JtAttribute_Material)::DownCast(anAttr); + readMaterialAttribute(aMaterial); + } + } +} + +void TKJtReader::traverseGraph(const Handle(JtNode_Base) & aNode) +{ + readAttributes(aNode->Attributes()); + + if (aNode->IsKind(Type_JtNode_Partition)) { + Handle(JtNode_Partition) aPart = Handle(JtNode_Partition)::DownCast(aNode); + builder.beginSeparator(); + readPartition(aPart); + readGroup(aPart); + builder.endSeparator(); + } + else if (aNode->IsKind(Type_JtNode_RangeLOD)) { + Handle(JtNode_RangeLOD) aRangeLOD = Handle(JtNode_RangeLOD)::DownCast(aNode); + builder.beginSeparator(); + readRangeLOD(aRangeLOD); + builder.endSeparator(); + } + else if (aNode->IsKind(Type_JtNode_Group)) { + Handle(JtNode_Group) aGroup = Handle(JtNode_Group)::DownCast(aNode); + builder.beginSeparator(); + readGroup(aGroup); + builder.endSeparator(); + } + else if (aNode->IsKind(Type_JtNode_Instance)) { + Handle(JtNode_Instance) anInstance = Handle(JtNode_Instance)::DownCast(aNode); + readInstance(anInstance); + } + else if (aNode->IsKind(Type_JtNode_Shape_Vertex)) { + Handle(JtNode_Shape_Vertex) aShape = Handle(JtNode_Shape_Vertex)::DownCast(aNode); + readShapeVertex(aShape); + } +} diff --git a/src/Mod/JtReader/App/TKJtReader.h b/src/Mod/JtReader/App/TKJtReader.h new file mode 100644 index 0000000000..eb69006d95 --- /dev/null +++ b/src/Mod/JtReader/App/TKJtReader.h @@ -0,0 +1,76 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later + +/*************************************************************************** + * Copyright (c) 2023 Werner Mayer * + * * + * This file is part of FreeCAD. * + * * + * FreeCAD is free software: you can redistribute it and/or modify it * + * under the terms of the GNU Lesser General Public License as * + * published by the Free Software Foundation, either version 2.1 of the * + * License, or (at your option) any later version. * + * * + * FreeCAD is distributed in the hope that it will be useful, but * + * WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with FreeCAD. If not, see * + * . * + * * + **************************************************************************/ + +#ifndef JT_READER_TKJTREADER_H +#define JT_READER_TKJTREADER_H + +#ifdef JTREADER_HAVE_TKJT +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace JtReaderNS +{ + +class TKJtReader +{ +public: + TKJtReader(); + void open(const std::string& filename); + void clear(); + std::string getOutput() const; + +private: + void traverseGraph(const Handle(JtNode_Base) & aNode); + void readMaterialAttribute(const Handle(JtAttribute_Material) & aMaterial); + void readTransformAttribute(const Handle(JtAttribute_GeometricTransform) & aTransform); + void readShapeVertex(const Handle(JtNode_Shape_Vertex) & aShape); + void readPartition(const Handle(JtNode_Partition) & aPart); + void readGroup(const Handle(JtNode_Group) & aGroup); + void readRangeLOD(const Handle(JtNode_RangeLOD) & aRangeLOD); + void readInstance(const Handle(JtNode_Instance) & anInstance); + void readAttributes(const JtData_Object::VectorOfObjects& attr); + void getTriangleStripSet(const Handle(JtElement_ShapeLOD_TriStripSet) & aLOD); + +private: + TCollection_ExtendedString jtDir; + Handle(JtNode_Partition) rootNode; + std::stringstream result; + Base::InventorBuilder builder; + std::list transformations; +}; + +}; // namespace JtReaderNS + +#endif // JTREADER_HAVE_TKJT + +#endif // JT_READER_TKJTREADER_H