From 8bffec003910cfcbd2dad89939c9d1630bfff312 Mon Sep 17 00:00:00 2001 From: wmayer Date: Thu, 3 Sep 2020 11:07:09 +0200 Subject: [PATCH] Gui: add function to export a VRMLGroup into X3D format --- src/Gui/SoFCDB.cpp | 231 +++++++++++++++++++++++++++++++++++++++++---- src/Gui/SoFCDB.h | 13 ++- 2 files changed, 225 insertions(+), 19 deletions(-) diff --git a/src/Gui/SoFCDB.cpp b/src/Gui/SoFCDB.cpp index 47ca778680..471aee3416 100644 --- a/src/Gui/SoFCDB.cpp +++ b/src/Gui/SoFCDB.cpp @@ -34,8 +34,13 @@ # include #endif +#include +#include +#include + #include #include +#include #include #include "SoFCDB.h" @@ -317,6 +322,7 @@ void Gui::SoFCDB::writeToVRML(SoNode* node, std::string& buffer) SoToVRML2Action tovrml2; tovrml2.apply(noSwitches); SoVRMLGroup* vrmlRoot = tovrml2.getVRML2SceneGraph(); + vrmlRoot->setInstancePrefix(SbString("o")); vrmlRoot->ref(); buffer = SoFCDB::writeNodesToString(vrmlRoot); @@ -366,7 +372,7 @@ bool Gui::SoFCDB::writeToVRML(SoNode* node, const char* filename, bool binary) bool Gui::SoFCDB::writeToX3D(SoNode* node, const char* filename, bool binary) { std::string buffer; - writeToX3D(node, buffer); + writeToX3D(node, false, buffer); Base::FileInfo fi(filename); if (binary) { @@ -392,8 +398,9 @@ bool Gui::SoFCDB::writeToX3D(SoNode* node, const char* filename, bool binary) return false; } -bool Gui::SoFCDB::writeToX3D(SoNode* node, std::string& buffer) +bool Gui::SoFCDB::writeToX3D(SoNode* node, bool exportViewpoints, std::string& buffer) { +#if 0 writeToVRML(node, buffer); if (buffer.empty()) return false; @@ -419,22 +426,24 @@ bool Gui::SoFCDB::writeToX3D(SoNode* node, std::string& buffer) x3d.replace('\t', " "); - // compute a sensible view point - SoGetBoundingBoxAction bboxAction(SbViewportRegion(1280, 1024)); - bboxAction.apply(node); - SbBox3f bbox = bboxAction.getBoundingBox(); - SbSphere bs; - bs.circumscribe(bbox); - const SbVec3f& cnt = bs.getCenter(); - float dist = bs.getRadius(); + if (exportViewpoints) { + // compute a sensible view point + SoGetBoundingBoxAction bboxAction(SbViewportRegion(1280, 1024)); + bboxAction.apply(node); + SbBox3f bbox = bboxAction.getBoundingBox(); + SbSphere bs; + bs.circumscribe(bbox); + const SbVec3f& cnt = bs.getCenter(); + float dist = bs.getRadius(); - QString vp = QString::fromLatin1(" \n") - .arg(cnt[0]).arg(cnt[1]).arg(cnt[2]).arg(cnt[2] + 2.0f * dist); - int index = x3d.indexOf("\n"); - if (index >= 0) { - x3d.insert(index + 8, vp); + QString vp = QString::fromLatin1(" \n") + .arg(cnt[0]).arg(cnt[1]).arg(cnt[2]).arg(cnt[2] + 2.0f * dist); + int index = x3d.indexOf("\n"); + if (index >= 0) { + x3d.insert(index + 8, vp); + } } buffer = x3d.data(); @@ -443,12 +452,186 @@ bool Gui::SoFCDB::writeToX3D(SoNode* node, std::string& buffer) } return false; +#else + SoNode* noSwitches = replaceSwitchesInSceneGraph(node); + noSwitches->ref(); + SoVRMLAction vrml2; + vrml2.setOverrideMode(true); + vrml2.apply(noSwitches); + SoToVRML2Action tovrml2; + tovrml2.apply(noSwitches); + SoVRMLGroup* vrmlRoot = tovrml2.getVRML2SceneGraph(); + + vrmlRoot->setInstancePrefix(SbString("o")); + vrmlRoot->ref(); + + std::stringstream out; + writeX3D(vrmlRoot, exportViewpoints, out); + buffer = out.str(); + + vrmlRoot->unref(); // release the memory as soon as possible + + // restore old settings + vrml2.setOverrideMode(false); + vrml2.apply(noSwitches); + noSwitches->unref(); + + return true; +#endif +} + +void Gui::SoFCDB::writeX3DFields(SoNode* node, std::map& nodeMap, + bool isRoot, int& numDEF, int spaces, std::ostream& out) +{ + // remove the VRML prefix from the type name + std::string type(node->getTypeId().getName().getString()); + type = type.substr(4); + + out << Base::blanks(spaces) << "<" << type; + if (node->getRefCount() > 1 && !isRoot) { + SbName name = node->getName(); + std::stringstream str; + if (name.getLength() == 0) + str << "o" << numDEF++; + else + str << name.getString(); + + nodeMap[node] = str.str(); + out << " DEF=\"" << str.str() << "\""; + } + + const SoFieldData* fielddata = node->getFieldData(); + if (fielddata) { + int numFieldNodes = 0; + + // process non-SoSFNode and non-SoMFNode fields + for (int i=0; igetNumFields(); i++) { + SoField* field = fielddata->getField(node, i); + if (!field->isDefault()) { + if (!field->isOfType(SoSFNode::getClassTypeId()) && + !field->isOfType(SoMFNode::getClassTypeId())) { + SbString value; + field->get(value); + QByteArray ba(value.getString(), value.getLength()); + ba.replace('\n', ' '); + if (field->isOfType(SoMField::getClassTypeId())) { + ba.replace('[', ' '); + ba.replace(']', ' '); + QList ary = ba.split(','); + for (auto& it : ary) { + it = it.simplified(); + } + + ba = ary.join(", "); + } + + out << '\n' << Base::blanks(spaces+2) << fielddata->getFieldName(i).getString() << "=\"" << ba.data() << "\" "; + } + else { + numFieldNodes++; + } + } + } + + if (numFieldNodes > 0) { + out << ">\n"; + } + else { + out << "/>\n"; + } + + // process SoSFNode or SoMFNode fields + for (int i=0; igetNumFields(); i++) { + SoField* field = fielddata->getField(node, i); + if (!field->isDefault()) { + if (field->isOfType(SoSFNode::getClassTypeId())) { + SoSFNode* sfNode = static_cast(field); + writeX3DChild(sfNode->getValue(), nodeMap, numDEF, spaces+2, out); + } + else if (field->isOfType(SoMFNode::getClassTypeId())) { + SoMFNode* mfNode = static_cast(field); + for (int j=0; jgetNum(); j++) { + writeX3DChild(mfNode->getNode(j), nodeMap, numDEF, spaces+2, out); + } + } + } + } + + if (numFieldNodes > 0) { + out << Base::blanks(spaces) << "\n"; + } + } +} + +void Gui::SoFCDB::writeX3DChild(SoNode* node, std::map& nodeMap, + int& numDEF, int spaces, std::ostream& out) +{ + // check if the node is already used + auto mapIt = nodeMap.find(node); + if (mapIt == nodeMap.end()) { + writeX3DFields(node, nodeMap, false, numDEF, spaces, out); + } + else { + // remove the VRML prefix from the type name + std::string sftype(node->getTypeId().getName().getString()); + sftype = sftype.substr(4); + out << Base::blanks(spaces) << "<" << sftype << " USE=\"" << mapIt->second << "\" />\n"; + } +} + +void Gui::SoFCDB::writeX3D(SoVRMLGroup* node, bool exportViewpoints, std::ostream& out) +{ + out << "\n"; + out << "\n"; + out << "\n"; + out << " \n" + " \n" + " \n" + " \n" + " \n"; + + std::map nodeMap; + out << "\n"; + + // compute a sensible view point + SoGetBoundingBoxAction bboxAction(SbViewportRegion(1280, 1024)); + bboxAction.apply(node); + SbBox3f bbox = bboxAction.getBoundingBox(); + SbSphere bs; + bs.circumscribe(bbox); + const SbVec3f& cnt = bs.getCenter(); + float dist = 2.0f * bs.getRadius(); + + if (exportViewpoints) { + auto viewpoint = [&out](const char* text, const SbVec3f& cnt, + const SbVec3f& pos, const SbVec3f& axis, float angle) { + out << " " + << "\n"; + }; + + viewpoint("Front", cnt, SbVec3f(cnt[0], cnt[1] - dist, cnt[2]), SbVec3f(1.0f, 0.0f, 0.0f), 1.5707964f); + viewpoint("Back", cnt, SbVec3f(cnt[0], cnt[1] + dist, cnt[2]), SbVec3f(0.0f, 0.707106f, 0.707106f), 3.141592f); + viewpoint("Right", cnt, SbVec3f(cnt[0] + dist, cnt[1], cnt[2]), SbVec3f(0.577350f, 0.577350f, 0.577350f), 2.094395f); + viewpoint("Left", cnt, SbVec3f(cnt[0] - dist, cnt[1], cnt[2]), SbVec3f(-0.577350f, 0.577350f, 0.577350f), 4.188790f); + viewpoint("Top", cnt, SbVec3f(cnt[0], cnt[1], cnt[2] + dist), SbVec3f(0.0f, 0.0f, 1.0f), 0.0f); + viewpoint("Bottom", cnt, SbVec3f(cnt[0], cnt[1], cnt[2] - dist), SbVec3f(1.0f, 0.0f, 0.0f), 3.141592f); + } + + int numDEF = 0; + writeX3DFields(node, nodeMap, true, numDEF, 2, out); + out << "\n"; + out << "\n"; } bool Gui::SoFCDB::writeToX3DOM(SoNode* node, std::string& buffer) { std::string x3d; - if (!writeToX3D(node, x3d)) + if (!writeToX3D(node, true, x3d)) return false; // remove the first two lines from the x3d output as this duplicates @@ -466,6 +649,18 @@ bool Gui::SoFCDB::writeToX3DOM(SoNode* node, std::string& buffer) << " \n" << " \n"; out << x3d; + + auto onclick = [&out](const char* text) { + out << " \n"; + }; + + onclick("Front"); + onclick("Back"); + onclick("Right"); + onclick("Left"); + onclick("Top"); + onclick("Bottom"); + out << "\n"; buffer = out.str(); diff --git a/src/Gui/SoFCDB.h b/src/Gui/SoFCDB.h index adcaca29e4..afaf6fe72a 100644 --- a/src/Gui/SoFCDB.h +++ b/src/Gui/SoFCDB.h @@ -24,11 +24,15 @@ #ifndef GUI_SOFCDB_H #define GUI_SOFCDB_H +#include #include +#include #include class SoNode; class SoGroup; +class SoVRMLGroup; + namespace Gui { /** * The FreeCAD database class to initialize all our own Inventor nodes. @@ -46,7 +50,7 @@ public: static bool writeToVRML(SoNode* node, const char* filename, bool binary); static void writeToVRML(SoNode* node, std::string& buffer); static bool writeToX3D(SoNode* node, const char* filename, bool binary); - static bool writeToX3D(SoNode* node, std::string& buffer); + static bool writeToX3D(SoNode* node, bool exportViewpoints, std::string& buffer); static bool writeToX3DOM(SoNode* node, std::string& buffer); // Write to Inventor, VRML, X3D or XHTML (based on X3DOM) file static bool writeToFile(SoNode* node, const char* filename, bool binary); @@ -54,6 +58,13 @@ public: * on why this is needed. */ static SoGroup* getStorage(); + +private: + static void writeX3D(SoVRMLGroup* node, bool exportViewpoints, std::ostream& out); + static void writeX3DChild(SoNode* node, std::map& nodeMap, + int& numDEF, int spaces, std::ostream& out); + static void writeX3DFields(SoNode* node, std::map& nodeMap, + bool isRoot, int& numDEF, int spaces, std::ostream& out); }; } // namespace Gui