From 70798e1c63fd8c72bacbfa05bdeb992b2533fa89 Mon Sep 17 00:00:00 2001 From: tabgab Date: Fri, 3 Jul 2026 14:52:29 +0300 Subject: [PATCH 1/4] visualizer: adopt backend-neutral cScene3DNode (OSG+VSG coexistence) Companion to the omnetpp-dev change that makes cScene3DNode a renderer-neutral handle rather than an alias of osg::Node. Wrap OSG scene roots with omnetpp::createScene3DNode() when calling cOsgCanvas::setScene(), and recover the typed root via getOsgRoot()/getVsgRoot() instead of casting cScene3DNode directly to a scene-graph type. Updated both visualizer trees: - OSG: SceneOsgVisualizer, SceneOsgEarthVisualizer, SceneOsgVisualizerBase, OsgScene, and GeographicCoordinateSystem (osgEarth map node lookup) - VSG: SceneVsgVisualizer, VsgScene Lets INET build and run against an OMNeT++ that contains both 3-D backends, with the renderer selected per simulation. Co-Authored-By: Claude Opus 4.8 --- .../common/geometry/common/GeographicCoordinateSystem.cc | 3 ++- src/inet/visualizer/osg/base/SceneOsgVisualizerBase.cc | 4 +++- src/inet/visualizer/osg/scene/SceneOsgEarthVisualizer.cc | 3 ++- src/inet/visualizer/osg/scene/SceneOsgVisualizer.cc | 4 +++- src/inet/visualizer/osg/util/OsgScene.cc | 6 ++++-- src/inet/visualizer/vsg/scene/SceneVsgVisualizer.cc | 2 +- src/inet/visualizer/vsg/util/VsgScene.cc | 2 +- 7 files changed, 16 insertions(+), 8 deletions(-) diff --git a/src/inet/common/geometry/common/GeographicCoordinateSystem.cc b/src/inet/common/geometry/common/GeographicCoordinateSystem.cc index a251d12ea38..8233f763735 100644 --- a/src/inet/common/geometry/common/GeographicCoordinateSystem.cc +++ b/src/inet/common/geometry/common/GeographicCoordinateSystem.cc @@ -10,6 +10,7 @@ #if defined(WITH_OSGEARTH) && defined(INET_WITH_VISUALIZATIONOSG) #include #include +#include "qtenv/osg/osgscenehandle.h" // omnetpp::getOsgRoot() #endif namespace inet { @@ -49,7 +50,7 @@ Define_Module(OsgGeographicCoordinateSystem); void OsgGeographicCoordinateSystem::initialize(int stage) { if (stage == INITSTAGE_LOCAL) { - auto mapScene = getParentModule()->getOsgCanvas()->getScene(); + auto mapScene = omnetpp::getOsgRoot(getParentModule()->getOsgCanvas()->getScene()); mapNode = osgEarth::MapNode::findMapNode(mapScene); if (mapNode == nullptr) throw cRuntimeError("Count not find map node in the scene"); diff --git a/src/inet/visualizer/osg/base/SceneOsgVisualizerBase.cc b/src/inet/visualizer/osg/base/SceneOsgVisualizerBase.cc index e0567c18289..8be1bb28d94 100644 --- a/src/inet/visualizer/osg/base/SceneOsgVisualizerBase.cc +++ b/src/inet/visualizer/osg/base/SceneOsgVisualizerBase.cc @@ -12,6 +12,8 @@ #include #include +#include "qtenv/osg/osgscenehandle.h" // omnetpp::createScene3DNode(osg::Node*) + #include "inet/common/ModuleAccess.h" #include "inet/visualizer/osg/scene/NetworkNodeOsgVisualizer.h" #include "inet/visualizer/osg/util/OsgScene.h" @@ -28,7 +30,7 @@ void SceneOsgVisualizerBase::initializeScene() throw cRuntimeError("OSG canvas scene at '%s' has been already initialized", visualizationTargetModule->getFullPath().c_str()); else { auto topLevelScene = new inet::osg::TopLevelScene(); - osgCanvas->setScene(topLevelScene); + osgCanvas->setScene(omnetpp::createScene3DNode(topLevelScene)); const char *clearColor = par("clearColor"); if (*clearColor != '\0') osgCanvas->setClearColor(cFigure::Color(clearColor)); diff --git a/src/inet/visualizer/osg/scene/SceneOsgEarthVisualizer.cc b/src/inet/visualizer/osg/scene/SceneOsgEarthVisualizer.cc index 0bf764846c8..3c84bed9b5e 100644 --- a/src/inet/visualizer/osg/scene/SceneOsgEarthVisualizer.cc +++ b/src/inet/visualizer/osg/scene/SceneOsgEarthVisualizer.cc @@ -19,6 +19,7 @@ #include #include #include +#include "qtenv/osg/osgscenehandle.h" // omnetpp::getOsgRoot() #endif // ifdef WITH_OSGEARTH namespace inet { @@ -62,7 +63,7 @@ void SceneOsgEarthVisualizer::initializeScene() throw cRuntimeError("Could not read earth map file '%s'", mapFileString.c_str()); auto osgCanvas = visualizationTargetModule->getOsgCanvas(); osgCanvas->setViewerStyle(cOsgCanvas::STYLE_EARTH); - auto topLevelScene = check_and_cast(osgCanvas->getScene()); + auto topLevelScene = check_and_cast(omnetpp::getOsgRoot(osgCanvas->getScene())); topLevelScene->addChild(mapScene); mapNode = MapNode::findMapNode(mapScene); if (mapNode == nullptr) diff --git a/src/inet/visualizer/osg/scene/SceneOsgVisualizer.cc b/src/inet/visualizer/osg/scene/SceneOsgVisualizer.cc index 89f03d7aec6..c7788dac5d1 100644 --- a/src/inet/visualizer/osg/scene/SceneOsgVisualizer.cc +++ b/src/inet/visualizer/osg/scene/SceneOsgVisualizer.cc @@ -10,6 +10,8 @@ #include #include +#include "qtenv/osg/osgscenehandle.h" // omnetpp::getOsgRoot() + #include "inet/common/ModuleAccess.h" #include "inet/visualizer/osg/util/OsgScene.h" #include "inet/visualizer/osg/util/OsgUtils.h" @@ -39,7 +41,7 @@ void SceneOsgVisualizer::initialize(int stage) void SceneOsgVisualizer::initializeScene() { SceneOsgVisualizerBase::initializeScene(); - auto topLevelScene = check_and_cast(visualizationTargetModule->getOsgCanvas()->getScene()); + auto topLevelScene = check_and_cast(omnetpp::getOsgRoot(visualizationTargetModule->getOsgCanvas()->getScene())); topLevelScene->addChild(new inet::osg::SimulationScene()); } diff --git a/src/inet/visualizer/osg/util/OsgScene.cc b/src/inet/visualizer/osg/util/OsgScene.cc index c267057fb73..1b4bb1e23e7 100644 --- a/src/inet/visualizer/osg/util/OsgScene.cc +++ b/src/inet/visualizer/osg/util/OsgScene.cc @@ -7,6 +7,8 @@ #include "inet/visualizer/osg/util/OsgScene.h" +#include "qtenv/osg/osgscenehandle.h" // omnetpp::createScene3DNode(osg::Node*), getOsgRoot() + namespace inet { namespace osg { @@ -35,7 +37,7 @@ SimulationScene *TopLevelScene::getSimulationScene() SimulationScene *TopLevelScene::getSimulationScene(cModule *module) { auto osgCanvas = module->getOsgCanvas(); - auto topLevelScene = dynamic_cast(osgCanvas->getScene()); + auto topLevelScene = dynamic_cast(omnetpp::getOsgRoot(osgCanvas->getScene())); if (topLevelScene != nullptr) return topLevelScene->getSimulationScene(); else { @@ -43,7 +45,7 @@ SimulationScene *TopLevelScene::getSimulationScene(cModule *module) topLevelScene = new TopLevelScene(); topLevelScene->addChild(simulationScene); // NOTE: these are the default values when there's no SceneOsgVisualizer - osgCanvas->setScene(topLevelScene); + osgCanvas->setScene(omnetpp::createScene3DNode(topLevelScene)); osgCanvas->setClearColor(cFigure::Color("#FFFFFF")); osgCanvas->setZNear(0.1); osgCanvas->setZFar(100000); diff --git a/src/inet/visualizer/vsg/scene/SceneVsgVisualizer.cc b/src/inet/visualizer/vsg/scene/SceneVsgVisualizer.cc index f1dc4d3eaa0..f1391e52f90 100644 --- a/src/inet/visualizer/vsg/scene/SceneVsgVisualizer.cc +++ b/src/inet/visualizer/vsg/scene/SceneVsgVisualizer.cc @@ -39,7 +39,7 @@ void SceneVsgVisualizer::initializeScene() { SceneVsgVisualizerBase::initializeScene(); auto sceneNode = visualizationTargetModule->getOsgCanvas()->getScene(); - auto topLevelScene = sceneNode != nullptr ? dynamic_cast(sceneNode->getRoot().get()) : nullptr; + auto topLevelScene = dynamic_cast(omnetpp::getVsgRoot(sceneNode).get()); if (topLevelScene == nullptr) throw cRuntimeError("Cannot find the VSG top level scene"); topLevelScene->addChild(inet::vsg::SimulationScene::create()); diff --git a/src/inet/visualizer/vsg/util/VsgScene.cc b/src/inet/visualizer/vsg/util/VsgScene.cc index 91378cceaea..8dd23db92f3 100644 --- a/src/inet/visualizer/vsg/util/VsgScene.cc +++ b/src/inet/visualizer/vsg/util/VsgScene.cc @@ -31,7 +31,7 @@ SimulationScene *TopLevelScene::getSimulationScene(cModule *module) // Under WITH_VSG the cOsgCanvas scene is an opaque omnetpp::cScene3DNode that // wraps the VSG scene-root group; recover our TopLevelScene from its root. auto sceneNode = osgCanvas->getScene(); - ::vsg::ref_ptr root = sceneNode != nullptr ? sceneNode->getRoot() : ::vsg::ref_ptr(); + ::vsg::ref_ptr root = omnetpp::getVsgRoot(sceneNode); // null / wrong-backend safe auto topLevelScene = root ? dynamic_cast(root.get()) : nullptr; if (topLevelScene != nullptr) return topLevelScene->getSimulationScene(); From ac141dd081d3238ded28bea3582f8bd545e19e05 Mon Sep 17 00:00:00 2001 From: tabgab Date: Fri, 3 Jul 2026 16:24:42 +0300 Subject: [PATCH 2/4] =?UTF-8?q?docs:=20vsg=20README=20=E2=80=94=20OSG=20an?= =?UTF-8?q?d=20VSG=20can=20coexist;=20how=20to=20build=20with=20both?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Corrects the "VisualizationOsg and VisualizationVsg are mutually exclusive" note (true only before the backend-neutral cScene3DNode change) and documents that an OMNeT++ configured with both WITH_OSG=yes and WITH_VSG=yes contains both 3D backends, selected per simulation. Co-Authored-By: Claude Opus 4.8 --- src/inet/visualizer/vsg/README.md | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/src/inet/visualizer/vsg/README.md b/src/inet/visualizer/vsg/README.md index d2e15d85043..b1f11fc7c8a 100644 --- a/src/inet/visualizer/vsg/README.md +++ b/src/inet/visualizer/vsg/README.md @@ -387,16 +387,23 @@ switching to the 3D view, and pressing Run. ## Building & running - Requires a VSG-enabled OMNeT++ build (in this environment: the - `omnetpp-dev` checkout, configured/built with VSG support, - i.e. `WITH_VSG=yes`). -- INET side: the `VisualizationVsg` feature (`.oppfeatures`, id - `VisualizationVsg`) must be enabled; it compiles with - `-DINET_WITH_VISUALIZATIONVSG`, requires the `PhysicalEnvironment` and - `VisualizationCommon` features, and is **mutually exclusive** with - `VisualizationOsg` — an OMNeT++ build has either OSG or VSG support, never - both, so the two visualizer features cannot be enabled simultaneously. + `omnetpp-dev` checkout, configured with `WITH_VSG=yes`). +- **OSG and VSG can now coexist in one build.** Since OMNeT++'s `cScene3DNode` + became a backend-neutral handle (omnetpp-dev `topic/VSG`, the + "backend-neutral `cScene3DNode`" change), a single OMNeT++ build can contain + **both** 3D backends and choose one **per simulation**. To get this, + configure OMNeT++ with **both** `WITH_OSG=yes` *and* `WITH_VSG=yes`: the + Qtenv loader then builds and loads both `liboppqtenv-osg` and + `liboppqtenv-vsg` and selects the matching one per `cOsgCanvas` (via + `cScene3DNode::getBackendType()`). Building with only one backend still works. +- INET side: enable the `VisualizationVsg` feature (`.oppfeatures`; compiles + with `-DINET_WITH_VISUALIZATIONVSG`; requires `PhysicalEnvironment` and + `VisualizationCommon`). `VisualizationOsg` **may be enabled at the same + time** — a scene renders on whichever backend its visualizer creates. (An + earlier revision of this doc said the two were mutually exclusive; that was + true only before the backend-neutral `cScene3DNode` change.) - Build with `make MODE=release` (from the INET root, after - `opp_featuretool` / configure has enabled `VisualizationVsg`). + `opp_featuretool` / configure has enabled the desired feature(s)). - Run an example with `inet -u Qtenv` from its directory (e.g. `examples/visualizer/vsgsignalwave3d`), then switch to the 3D view in the Qtenv toolbar and press Run. From f3bd13cbe0eabafabe29c871ac3021c2694588a4 Mon Sep 17 00:00:00 2001 From: tabgab Date: Fri, 3 Jul 2026 16:44:56 +0300 Subject: [PATCH 3/4] .oppfeatures: VisualizationVsg no longer mutually exclusive with OSG The feature description said VisualizationOsg and VisualizationVsg are mutually exclusive; that is no longer true after the backend-neutral cScene3DNode change. There is no `conflicts` between them, so both can be enabled together in an OMNeT++ built with WITH_OSG + WITH_VSG, each scene rendering on the backend its visualizer creates. (VisualizationVsg is still initiallyEnabled=false, so enable it explicitly for coexistence.) Co-Authored-By: Claude Opus 4.8 --- .oppfeatures | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/.oppfeatures b/.oppfeatures index b28a9f38bb9..d1f040b2189 100644 --- a/.oppfeatures +++ b/.oppfeatures @@ -1759,10 +1759,12 @@ name = "Visualization VSG (3D)" description = "Provides network-level 3D visualization features for physical layer, data link layer and network layer communications, and more. - This is the VulkanSceneGraph-based replacement for the OpenSceneGraph + This is the VulkanSceneGraph-based counterpart to the OpenSceneGraph (VisualizationOsg) 3D visualizer; it requires OMNeT++ built with VSG - support (WITH_VSG=yes). VisualizationOsg and VisualizationVsg are - mutually exclusive (OMNeT++ is built with either OSG or VSG, not both)." + support (WITH_VSG=yes). Since OMNeT++'s cScene3DNode became + backend-neutral, VisualizationOsg and VisualizationVsg may be enabled + together (in an OMNeT++ built with both WITH_OSG and WITH_VSG); each + scene renders on the backend its visualizer creates." initiallyEnabled = "false" requires = "PhysicalEnvironment VisualizationCommon" recommended = "" From 6d3244880e300ce1ad7713708340123433a5a536 Mon Sep 17 00:00:00 2001 From: tabgab Date: Fri, 3 Jul 2026 17:13:05 +0300 Subject: [PATCH 4/4] =?UTF-8?q?visualizer:=20fix=20OSG-only=20build=20?= =?UTF-8?q?=E2=80=94=20add=20-I$(OMNETPP=5FROOT)/src=20for=20osgscenehandl?= =?UTF-8?q?e.h?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The five OSG source files adopted in this PR #include "qtenv/osg/osgscenehandle.h", which lives under $(OMNETPP_ROOT)/src. That include path was only added by the VisualizationVsg makefrag block (line 81), so an OSG-only build (VisualizationOsg enabled, VisualizationVsg disabled — the common existing configuration) could not find the header and failed to compile. Add -I$(OMNETPP_ROOT)/src to the VisualizationOsg block, mirroring the VSG block. Co-Authored-By: Claude Opus 4.8 --- src/makefrag | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/makefrag b/src/makefrag index b84ee35e483..d691d2f1eae 100644 --- a/src/makefrag +++ b/src/makefrag @@ -60,6 +60,11 @@ endif WITH_VISUALIZATIONOSG := $(shell (cd .. && $(FEATURETOOL) -q isenabled VisualizationOsg && echo enabled) ) ifeq ($(WITH_VISUALIZATIONOSG), enabled) ifeq ($(WITH_OSG), yes) + # -I$(OMNETPP_ROOT)/src gives access to the plugin's qtenv/osg/osgscenehandle.h + # (createScene3DNode / getOsgRoot / cScene3DNode), needed by the OSG scene + # visualizers now that cScene3DNode is backend-neutral. Mirrors the + # VisualizationVsg block below; required for OSG-only builds (VSG disabled). + CFLAGS += -I$(OMNETPP_ROOT)/src OMNETPP_LIBS += -losg -losgText -losgDB -losgGA -losgViewer -losgUtil -lOpenThreads # TODO: use $(OSG_LIBS) from Makefile.inc? Does that include -losgText? Why not? ifeq ($(WITH_OSGEARTH), yes) OMNETPP_LIBS += -losgEarth -losgEarthUtil # TODO: use $(OSGEARTH_LIBS) from Makefile.inc? -lgeos_c might also be needed. Is that included? Should it be?