diff --git a/src/modules/voxelrender/RawVolumeRenderer.h b/src/modules/voxelrender/RawVolumeRenderer.h index 0b05e110db..f2d7cf82d3 100644 --- a/src/modules/voxelrender/RawVolumeRenderer.h +++ b/src/modules/voxelrender/RawVolumeRenderer.h @@ -55,6 +55,7 @@ struct RenderContext : public core::NonCopyable { bool onlyModels = false; // render the built-in normals bool renderNormals = false; + voxel::Region sliceRegion = voxel::Region::InvalidRegion; bool init(const glm::ivec2 &size); void shutdown(); diff --git a/src/modules/voxelrender/SceneGraphRenderer.cpp b/src/modules/voxelrender/SceneGraphRenderer.cpp index ada8761e09..6aaca18fcc 100644 --- a/src/modules/voxelrender/SceneGraphRenderer.cpp +++ b/src/modules/voxelrender/SceneGraphRenderer.cpp @@ -94,20 +94,6 @@ video::Camera toCamera(const glm::ivec2 &size, const scenegraph::SceneGraphNodeC return camera; } -static inline int getVolumeId(int nodeId) { - // TODO: using the node id here is not good as they are increasing when you modify the scene graph - return nodeId; -} - -static inline int getVolumeId(const scenegraph::SceneGraphNode &node) { - return getVolumeId(node.id()); -} - -static inline int getNodeId(int volumeIdx) { - // TODO: using the node id here is not good as they are increasing when you modify the scene graph - return volumeIdx; -} - SceneGraphRenderer::SceneGraphRenderer() { } @@ -127,7 +113,12 @@ void SceneGraphRenderer::update(const voxel::MeshStatePtr &meshState) { } void SceneGraphRenderer::scheduleRegionExtraction(const voxel::MeshStatePtr &meshState, scenegraph::SceneGraphNode &node, const voxel::Region ®ion) { - _volumeRenderer.scheduleRegionExtraction(meshState, getVolumeId(node), region); + const int idx = getVolumeId(node); + if (_sliceVolume && _sliceVolume.get() == meshState->volume(idx)) { + _sliceVolumeDirty = true; + return; + } + _volumeRenderer.scheduleRegionExtraction(meshState, idx, region); } void SceneGraphRenderer::setAmbientColor(const glm::vec3 &color) { @@ -207,10 +198,35 @@ void SceneGraphRenderer::prepare(const voxel::MeshStatePtr &meshState, const Ren continue; } const voxel::RawVolume *v = meshState->volume(id); - _volumeRenderer.setVolume(meshState, id, node, true); - const voxel::Region ®ion = node.region(); - if (v != node.volume()) { - _volumeRenderer.scheduleRegionExtraction(meshState, id, region); + const voxel::RawVolume *nodeVolume = sceneGraph.resolveVolume(node); + + bool sliceView = false; + voxel::Region region; + if (node.id() == activeNodeId) { + if (renderContext.sliceRegion.isValid()) { + sliceView = true; + if (_sliceVolumeDirty || !_sliceVolume || _sliceVolume->region() != renderContext.sliceRegion) { + _sliceVolume = core::make_shared(nodeVolume, renderContext.sliceRegion); + // either node or slice volume (nodes get their volume managed - and here we have a smart pointer) + (void)_volumeRenderer.setVolume(meshState, id, _sliceVolume.get(), &node.palette(), &node.normalPalette(), !_sliceVolumeDirty); + _volumeRenderer.scheduleRegionExtraction(meshState, id, _sliceVolume->region()); + _sliceVolumeDirty = false; + + region = _sliceVolume->region(); + v = _sliceVolume.get(); + } + } else { + _sliceVolume = nullptr; + _sliceVolumeDirty = false; + } + } + + if (!sliceView) { + _volumeRenderer.setVolume(meshState, id, node, true); + region = node.region(); + if (v != nodeVolume) { + _volumeRenderer.scheduleRegionExtraction(meshState, id, region); + } } if (renderContext.renderMode == RenderMode::Scene) { const scenegraph::FrameTransform &transform = sceneGraph.transformForFrame(node, frame); @@ -240,7 +256,7 @@ void SceneGraphRenderer::prepare(const voxel::MeshStatePtr &meshState, const Ren hideNode = true; } } else { - hideNode = id != activeNodeId; + hideNode = node.id() != activeNodeId; } } else { hideNode = !node.visible(); @@ -248,7 +264,7 @@ void SceneGraphRenderer::prepare(const voxel::MeshStatePtr &meshState, const Ren meshState->hide(id, hideNode); if (grayInactive) { - meshState->gray(id, id != activeNodeId); + meshState->gray(id, node.id() != activeNodeId); } else { meshState->gray(id, false); } @@ -283,7 +299,7 @@ void SceneGraphRenderer::prepare(const voxel::MeshStatePtr &meshState, const Ren hideNode = true; } } else { - hideNode = id != activeNodeId; + hideNode = node.id() != activeNodeId; } } else { hideNode = !node.visible(); @@ -291,7 +307,7 @@ void SceneGraphRenderer::prepare(const voxel::MeshStatePtr &meshState, const Ren meshState->hide(id, hideNode); if (grayInactive) { - meshState->gray(id, id != activeNodeId); + meshState->gray(id, node.id() != activeNodeId); } else { meshState->gray(id, false); } diff --git a/src/modules/voxelrender/SceneGraphRenderer.h b/src/modules/voxelrender/SceneGraphRenderer.h index bb8e974c62..dfeb1e9982 100644 --- a/src/modules/voxelrender/SceneGraphRenderer.h +++ b/src/modules/voxelrender/SceneGraphRenderer.h @@ -6,10 +6,12 @@ #include "RawVolumeRenderer.h" #include "app/I18N.h" +#include "core/SharedPtr.h" #include "core/collection/DynamicArray.h" #include "render/CameraFrustum.h" #include "scenegraph/SceneGraphNode.h" #include "video/Camera.h" +#include "voxel/RawVolume.h" /** * Basic voxel rendering @@ -41,7 +43,8 @@ class SceneGraphRenderer : public core::NonCopyable { render::CameraFrustum _cameraRenderer; core::DynamicArray _cameras; void prepare(const voxel::MeshStatePtr &meshState, const RenderContext &renderContext); - + core::SharedPtr _sliceVolume; + bool _sliceVolumeDirty = false; public: SceneGraphRenderer(); void construct(); @@ -69,6 +72,20 @@ class SceneGraphRenderer : public core::NonCopyable { void render(const voxel::MeshStatePtr &meshState, RenderContext &renderContext, const video::Camera &camera, bool shadow = true, bool waitPending = false); void clear(const voxel::MeshStatePtr &meshState); + + static inline int getVolumeId(int nodeId) { + // TODO: using the node id here is not good as they are increasing when you modify the scene graph + return nodeId; + } + + static inline int getVolumeId(const scenegraph::SceneGraphNode &node) { + return getVolumeId(node.id()); + } + + static inline int getNodeId(int volumeIdx) { + // TODO: using the node id here is not good as they are increasing when you modify the scene graph + return volumeIdx; + } }; } // namespace voxelrender diff --git a/src/tools/voxedit/modules/voxedit-ui/Viewport.cpp b/src/tools/voxedit/modules/voxedit-ui/Viewport.cpp index 3a21683b5e..686038000a 100644 --- a/src/tools/voxedit/modules/voxedit-ui/Viewport.cpp +++ b/src/tools/voxedit/modules/voxedit-ui/Viewport.cpp @@ -5,6 +5,7 @@ #include "Viewport.h" #include "Gizmo.h" #include "DragAndDropPayload.h" +#include "imgui.h" #include "scenegraph/SceneGraphAnimation.h" #include "scenegraph/SceneGraphKeyFrame.h" #include "ui/IconsLucide.h" @@ -229,14 +230,57 @@ void Viewport::renderCursor() { renderCursorDetails(); } +bool Viewport::renderSlicer(const glm::ivec2 &contentSize) { + auto &sceneGraph = _sceneMgr->sceneGraph(); + const int activeNode = sceneGraph.activeNode(); + bool changed = false; + if (const scenegraph::SceneGraphNode *node = _sceneMgr->sceneGraphModelNode(activeNode)) { + glm::ivec3 mins = _renderContext.sliceRegion.getLowerCorner(); + const voxel::Region &nodeRegion = sceneGraph.resolveRegion(*node); + bool sliceActive = _renderContext.sliceRegion.isValid(); + if (ImGui::Checkbox("##sliceactive", &sliceActive)) { + if (!sliceActive) { + _renderContext.sliceRegion = voxel::Region::InvalidRegion; + } else { + glm::ivec3 nodeMaxs = nodeRegion.getUpperCorner(); + glm::ivec3 nodeMins = nodeRegion.getLowerCorner(); + nodeMaxs.y = nodeMaxs.y; + _renderContext.sliceRegion.setLowerCorner(nodeMins); + _renderContext.sliceRegion.setUpperCorner(nodeMaxs); + } + changed = true; + } + if (ImGui::IsItemHovered()) { + _viewportUIElementHovered = true; + } + if (sliceActive && ImGui::VSliderInt("##slicepos", {ImGui::Size(3.0f), (float)contentSize.y}, &mins.y, + nodeRegion.getLowerY(), nodeRegion.getUpperY())) { + glm::ivec3 nodeMaxs = nodeRegion.getUpperCorner(); + glm::ivec3 nodeMins = nodeRegion.getLowerCorner(); + nodeMaxs.y = mins.y; + nodeMins.y = mins.y; + _renderContext.sliceRegion.setLowerCorner(nodeMins); + _renderContext.sliceRegion.setUpperCorner(nodeMaxs); + changed = true; + } + if (ImGui::IsItemHovered()) { + _viewportUIElementHovered = true; + } + } + return changed; +} + void Viewport::renderViewport() { core_trace_scoped(Viewport); glm::ivec2 contentSize = ImGui::GetContentRegionAvail(); - const float headerSize = ImGui::GetCursorPosY(); + ImVec2 cursorPos = ImGui::GetCursorPos(); + const float headerSize = cursorPos.y; if (setupFrameBuffer(contentSize)) { _camera.update(_app->deltaFrameSeconds()); renderToFrameBuffer(); + renderSlicer(contentSize); + ImGui::SetCursorPos(cursorPos); renderViewportImage(contentSize); const bool modifiedRegion = renderGizmo(camera(), headerSize, contentSize); @@ -452,6 +496,7 @@ void Viewport::update(command::CommandExecutionListener *listener) { core_trace_scoped(ViewportPanel); _camera.setFarPlane(_viewDistance->floatVal()); + _viewportUIElementHovered = false; _hovered = false; _visible = false; _cameraManipulated = false; diff --git a/src/tools/voxedit/modules/voxedit-ui/Viewport.h b/src/tools/voxedit/modules/voxedit-ui/Viewport.h index 94a9cd6ad3..3046a7cb61 100644 --- a/src/tools/voxedit/modules/voxedit-ui/Viewport.h +++ b/src/tools/voxedit/modules/voxedit-ui/Viewport.h @@ -34,6 +34,7 @@ class Viewport : public ui::Panel { bool _hovered = false; // is this viewport instance visible at all? bool _visible = false; + bool _viewportUIElementHovered = false; /** * while we are still modifying the transform or shifting the volume we don't want to * flood the memento states - thus we lock the memento handler and track this here. @@ -132,6 +133,7 @@ class Viewport : public ui::Panel { void dragAndDrop(float headerSize); void renderCursor(); void renderCursorDetails() const; + bool renderSlicer(const glm::ivec2 &contentSize); void renderViewport(); void toggleVideoRecording(); void menuBarPolygonModeOptions(); @@ -189,7 +191,7 @@ inline int Viewport::id() const { } inline bool Viewport::isHovered() const { - return _hovered && !_cameraManipulated; + return _hovered && !_cameraManipulated && !_viewportUIElementHovered; } inline bool Viewport::isVisible() const { diff --git a/src/tools/voxedit/modules/voxedit-util/ISceneRenderer.h b/src/tools/voxedit/modules/voxedit-util/ISceneRenderer.h index f79a801fd3..5862c74b8e 100644 --- a/src/tools/voxedit/modules/voxedit-util/ISceneRenderer.h +++ b/src/tools/voxedit/modules/voxedit-util/ISceneRenderer.h @@ -6,6 +6,8 @@ #include "core/IComponent.h" #include "math/Axis.h" +#include "scenegraph/SceneGraphNode.h" +#include "voxel/RawVolume.h" #include "voxel/Region.h" #include "voxelrender/RawVolumeRenderer.h" @@ -44,6 +46,9 @@ class ISceneRenderer : public core::IComponent { } virtual void renderScene(voxelrender::RenderContext &renderContext, const video::Camera &camera) { } + virtual const voxel::RawVolume *volumeForNode(const scenegraph::SceneGraphNode &node) { + return node.volume(); + } }; using SceneRendererPtr = core::SharedPtr; diff --git a/src/tools/voxedit/modules/voxedit-util/SceneManager.cpp b/src/tools/voxedit/modules/voxedit-util/SceneManager.cpp index 979b29724f..0537ce6185 100644 --- a/src/tools/voxedit/modules/voxedit-util/SceneManager.cpp +++ b/src/tools/voxedit/modules/voxedit-util/SceneManager.cpp @@ -2793,7 +2793,7 @@ bool SceneManager::mouseRayTrace(bool force) { if (node == nullptr) { return false; } - const voxel::RawVolume* v = node->volume(); + const voxel::RawVolume* v = _sceneRenderer->volumeForNode(*node); if (v == nullptr) { return false; } diff --git a/src/tools/voxedit/modules/voxedit-util/SceneRenderer.cpp b/src/tools/voxedit/modules/voxedit-util/SceneRenderer.cpp index 59a1d8e099..1ae07458f6 100644 --- a/src/tools/voxedit/modules/voxedit-util/SceneRenderer.cpp +++ b/src/tools/voxedit/modules/voxedit-util/SceneRenderer.cpp @@ -14,6 +14,8 @@ #include "voxedit-util/AxisUtil.h" #include "voxedit-util/Config.h" #include "voxel/RawVolume.h" +#include "voxelrender/RawVolumeRenderer.h" +#include "voxelrender/SceneGraphRenderer.h" namespace voxedit { @@ -21,7 +23,7 @@ SceneRenderer::SceneRenderer() : _meshState(core::make_shared( } void SceneRenderer::construct() { - _volumeRenderer.construct(); + _sceneGraphRenderer.construct(); _meshState->construct(); } @@ -41,7 +43,7 @@ bool SceneRenderer::init() { Log::error("Failed to initialize the mesh state"); return false; } - if (!_volumeRenderer.init(_meshState->hasNormals())) { + if (!_sceneGraphRenderer.init(_meshState->hasNormals())) { Log::error("Failed to initialize the volume renderer"); return false; } @@ -61,11 +63,11 @@ bool SceneRenderer::init() { } void SceneRenderer::clear() { - _volumeRenderer.clear(_meshState); + _sceneGraphRenderer.clear(_meshState); } void SceneRenderer::shutdown() { - _volumeRenderer.shutdown(); + _sceneGraphRenderer.shutdown(); // don't free the volumes here, they belong to the scene graph (void)_meshState->shutdown(); @@ -128,7 +130,7 @@ bool SceneRenderer::extractVolume(const scenegraph::SceneGraph &sceneGraph) { for (size_t i = 0; i < n; ++i) { const voxel::Region ®ion = _extractRegions[i].region; if (scenegraph::SceneGraphNode *node = sceneGraphModelNode(sceneGraph, _extractRegions[i].nodeId)) { - _volumeRenderer.scheduleRegionExtraction(_meshState, *node, region); + _sceneGraphRenderer.scheduleRegionExtraction(_meshState, *node, region); Log::debug("Extract node %i", _extractRegions[i].nodeId); voxel::logRegion("Extraction", region); } @@ -272,12 +274,21 @@ void SceneRenderer::updateBoneMesh(bool sceneMode, const scenegraph::SceneGraph _shapeRenderer.createOrUpdate(_boneMeshIndex, _shapeBuilder); } +const voxel::RawVolume *SceneRenderer::volumeForNode(const scenegraph::SceneGraphNode &node) { + int idx = voxelrender::SceneGraphRenderer::getVolumeId(node); + const voxel::RawVolume *v = _meshState->volume(idx); + if (v == nullptr) { + v = node.volume(); + } + return v; +} + bool SceneRenderer::isVisible(int nodeId, bool hideEmpty) const { - return _volumeRenderer.isVisible(_meshState, nodeId, hideEmpty); + return _sceneGraphRenderer.isVisible(_meshState, nodeId, hideEmpty); } void SceneRenderer::removeNode(int nodeId) { - _volumeRenderer.nodeRemove(_meshState, nodeId); + _sceneGraphRenderer.nodeRemove(_meshState, nodeId); } void SceneRenderer::update() { @@ -287,10 +298,10 @@ void SceneRenderer::update() { _gridRenderer.setColor(style::color(style::ColorGridBorder)); glm::vec3 val; _ambientColor->vec3Val(&val[0]); - _volumeRenderer.setAmbientColor(val); + _sceneGraphRenderer.setAmbientColor(val); _diffuseColor->vec3Val(&val[0]); - _volumeRenderer.setDiffuseColor(val); - _volumeRenderer.update(_meshState); + _sceneGraphRenderer.setDiffuseColor(val); + _sceneGraphRenderer.update(_meshState); } void SceneRenderer::renderScene(voxelrender::RenderContext &renderContext, const video::Camera &camera) { @@ -306,7 +317,7 @@ void SceneRenderer::renderScene(voxelrender::RenderContext &renderContext, const video::ScopedState depthTest(video::State::DepthTest, true); updateAABBMesh(renderContext.renderMode == voxelrender::RenderMode::Scene, *renderContext.sceneGraph, renderContext.frame); updateBoneMesh(renderContext.renderMode == voxelrender::RenderMode::Scene, *renderContext.sceneGraph, renderContext.frame); - _volumeRenderer.render(_meshState, renderContext, camera, _renderShadow->boolVal(), false); + _sceneGraphRenderer.render(_meshState, renderContext, camera, _renderShadow->boolVal(), false); extractVolume(*renderContext.sceneGraph); } diff --git a/src/tools/voxedit/modules/voxedit-util/SceneRenderer.h b/src/tools/voxedit/modules/voxedit-util/SceneRenderer.h index 584184584d..52545dea06 100644 --- a/src/tools/voxedit/modules/voxedit-util/SceneRenderer.h +++ b/src/tools/voxedit/modules/voxedit-util/SceneRenderer.h @@ -19,7 +19,7 @@ namespace voxedit { class SceneRenderer : public ISceneRenderer { private: voxel::MeshStatePtr _meshState; - voxelrender::SceneGraphRenderer _volumeRenderer; + voxelrender::SceneGraphRenderer _sceneGraphRenderer; render::GridRenderer _gridRenderer; video::ShapeBuilder _shapeBuilder; render::ShapeRenderer _shapeRenderer; @@ -77,6 +77,7 @@ class SceneRenderer : public ISceneRenderer { bool isVisible(int nodeId, bool hideEmpty = true) const override; void renderUI(voxelrender::RenderContext &renderContext, const video::Camera &camera) override; void renderScene(voxelrender::RenderContext &renderContext, const video::Camera &camera) override; + const voxel::RawVolume *volumeForNode(const scenegraph::SceneGraphNode &node) override; }; } // namespace voxedit