Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions 40_PathTracer/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ set(NBL_INCLUDE_SERACH_DIRECTORIES
"${NBL_EXT_MITSUBA_LOADER_INCLUDE_DIRS}"
"${CMAKE_CURRENT_SOURCE_DIR}/include"
"${CMAKE_CURRENT_SOURCE_DIR}/src"
"${NBL_ROOT_PATH}/3rdparty/portable-file-dialogs"
)
set(NBL_LIBRARIES
"${NBL_EXT_MITSUBA_LOADER_LIB}"
Expand All @@ -12,12 +13,29 @@ set(NBL_LIBRARIES
"${NBL_EXT_IMGUI_UI_LIB}"
)
set(NBL_EXAMPLE_SOURCES
"${CMAKE_CURRENT_SOURCE_DIR}/include/common.hpp"
"${CMAKE_CURRENT_SOURCE_DIR}/include/io/CSceneLoader.h"
"${CMAKE_CURRENT_SOURCE_DIR}/include/gui/CSceneWindow.h"
"${CMAKE_CURRENT_SOURCE_DIR}/include/gui/CSessionWindow.h"
"${CMAKE_CURRENT_SOURCE_DIR}/include/gui/CUIManager.h"
"${CMAKE_CURRENT_SOURCE_DIR}/include/renderer/SAASequence.h"
"${CMAKE_CURRENT_SOURCE_DIR}/include/renderer/CScene.h"
"${CMAKE_CURRENT_SOURCE_DIR}/include/renderer/CRenderer.h"
"${CMAKE_CURRENT_SOURCE_DIR}/include/renderer/CSession.h"
"${CMAKE_CURRENT_SOURCE_DIR}/include/renderer/resolve/CBasicRWMCResolver.h"
"${CMAKE_CURRENT_SOURCE_DIR}/include/renderer/resolve/IResolver.h"
"${CMAKE_CURRENT_SOURCE_DIR}/include/renderer/present/CWindowPresenter.h"
"${CMAKE_CURRENT_SOURCE_DIR}/include/renderer/present/IPresenter.h"

"${CMAKE_CURRENT_SOURCE_DIR}/src/io/CSceneLoader.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/src/renderer/CSession.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/src/renderer/CScene.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/src/renderer/CRenderer.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/src/renderer/resolve/CBasicRWMCResolver.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/src/renderer/present/CWindowPresenter.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/src/gui/CUIManager.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/src/gui/CSceneWindow.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/src/gui/CSessionWindow.cpp"
)
nbl_create_executable_project("${NBL_EXAMPLE_SOURCES}" "" "${NBL_INCLUDE_SERACH_DIRECTORIES}" "${NBL_LIBRARIES}")

Expand Down
73 changes: 73 additions & 0 deletions 40_PathTracer/include/gui/CSceneWindow.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
// Copyright (C) 2025-2026 - DevSH Graphics Programming Sp. z O.O.
// This file is part of the "Nabla Engine".
// For conditions of distribution and use, see copyright notice in nabla.h
#ifndef _NBL_THIS_EXAMPLE_C_SCENE_WINDOW_H_INCLUDED_
#define _NBL_THIS_EXAMPLE_C_SCENE_WINDOW_H_INCLUDED_

#include "imgui.h"

#include <functional>
#include <string>
#include <vector>

namespace nbl::this_example
{
// Forward declarations
class CScene;
class CSession;
}

namespace nbl::this_example::gui
{

class CSceneWindow final
{
public:
// Callbacks for user actions - main app handles the actual logic
struct SCallbacks
{
std::function<void(size_t sensorIndex)> onSensorSelected = nullptr;
std::function<void(const std::string& path)> onLoadRequested = nullptr;
std::function<void()> onReloadRequested = nullptr;
};
void setCallbacks(const SCallbacks& callbacks) { m_callbacks = callbacks; }

CSceneWindow() = default;
~CSceneWindow() = default;

// Set the scene to display (can be null)
void setScene(const CScene* scene) { m_scene = scene; }

// Get current scene path for display
void setScenePath(const std::string& path) { m_scenePath = path; }

// Main draw call - renders the window
// forceReposition: if true, window will reposition to default location
void draw(bool forceReposition = false);

// Current selection state
int getSelectedSensorIndex() const { return m_selectedSensorIndex; }
void setSelectedSensorIndex(int idx) { m_selectedSensorIndex = idx; }

// Window visibility
bool isOpen() const { return m_isOpen; }
void setOpen(bool open) { m_isOpen = open; }

private:
const CScene* m_scene = nullptr;
std::string m_scenePath = "";
int m_selectedSensorIndex = -1;
bool m_isOpen = true;
SCallbacks m_callbacks;

// Section drawing helpers
void drawLoadSection();
void drawSensorsSection();
void drawGlobalsSection();
void drawEmittersSection();
void drawEditorSection();
};

} // namespace nbl::this_example::gui

#endif // _NBL_THIS_EXAMPLE_C_SCENE_WINDOW_H_INCLUDED_
114 changes: 114 additions & 0 deletions 40_PathTracer/include/gui/CSessionWindow.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
// Copyright (C) 2025-2026 - DevSH Graphics Programming Sp. z O.O.
// This file is part of the "Nabla Engine".
// For conditions of distribution and use, see copyright notice in nabla.h
#ifndef _NBL_THIS_EXAMPLE_C_SESSION_WINDOW_H_INCLUDED_
#define _NBL_THIS_EXAMPLE_C_SESSION_WINDOW_H_INCLUDED_

#include "imgui.h"
#include "renderer/shaders/pathtrace/push_constants.hlsl"
#include <functional>
#include <string>
#include <array>
#include <renderer/CSession.h>

namespace nbl::this_example
{
class CSession;
}

namespace nbl::this_example::gui
{

class CSessionWindow final
{
public:
// Buffer types that can be displayed
enum class BufferType : uint32_t
{
Beauty = 0,
Albedo,
Normal,
Motion,
Mask,
RWMCCascades,
SampleCount,
Count
};

// Callbacks to main app
struct SCallbacks
{
// Requires session recreation
std::function<void(CSession::RenderMode mode, CSession* session)> onRenderModeChanged = nullptr;
std::function<void(uint16_t width, uint16_t height)> onResolutionChanged = nullptr;

// Requires reset()
std::function<void(const SSensorDynamics& dynamics, CSession* session)> onMutablesChanged = nullptr;

// Immediate update()
std::function<void(const SSensorDynamics& dynamics, CSession* session)> onDynamicsChanged = nullptr;

// Buffer view change
std::function<void(int bufferIndex)> onBufferSelected = nullptr;
};
void setCallbacks(const SCallbacks& callbacks) { m_callbacks = callbacks; }

CSessionWindow() = default;
~CSessionWindow() = default;

// Set active session to display/control
void setSession(CSession* session);

// Set texture IDs for buffer thumbnails (called by CUIManager)
void setBufferTextureIDs(const std::array<uint32_t, static_cast<size_t>(BufferType::Count)>& textureIDs);

// forceReposition: if true, window will reposition to default location
void draw(bool forceReposition = false);

bool isOpen() const { return m_isOpen; }
void setOpen(bool open) { m_isOpen = open; }

// Get currently selected buffer
BufferType getSelectedBuffer() const { return static_cast<BufferType>(m_state.selectedBufferIndex); }

private:
CSession* m_session = nullptr;
bool m_isOpen = true;
SCallbacks m_callbacks;

// Texture IDs for buffer thumbnails
std::array<uint32_t, static_cast<size_t>(BufferType::Count)> m_bufferTextureIDs = {};

// Local state for UI controls
struct SState
{
// Mode
CSession::RenderMode renderMode = CSession::RenderMode::Beauty;

// Dynamics
float cropOffsetX = 0.0f;
float cropOffsetY = 0.0f;
float tMax = 10000.0f;

// Mutables
int cropWidth = 1920;
int cropHeight = 1080;
float nearClip = 0.1f;
float farClip = 10000.0f;

// View
int selectedBufferIndex = 0;
} m_state;

// Copies to check for changes
SSensorDynamics m_cachedDynamics;

void drawRenderModeSection();
void drawDynamicsSection();
void drawMutablesSection();
void drawOutputBufferSection();
};

} // namespace nbl::this_example::gui

#endif // _NBL_THIS_EXAMPLE_C_SESSION_WINDOW_H_INCLUDED_
142 changes: 142 additions & 0 deletions 40_PathTracer/include/gui/CUIManager.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
// Copyright (C) 2025-2026 - DevSH Graphics Programming Sp. z O.O.
// This file is part of the "Nabla Engine".
// For conditions of distribution and use, see copyright notice in nabla.h
#ifndef _NBL_THIS_EXAMPLE_C_UI_MANAGER_H_INCLUDED_
#define _NBL_THIS_EXAMPLE_C_UI_MANAGER_H_INCLUDED_

#include "nbl/ext/ImGui/ImGui.h"
#include "nbl/video/alloc/SubAllocatedDescriptorSet.h"
#include "gui/CSceneWindow.h"
#include "gui/CSessionWindow.h"
#include "renderer/shaders/pathtrace/push_constants.hlsl"

#include <functional>

namespace nbl::this_example
{
// Forward declarations
class CScene;
class CSession;
class CWindowPresenter;
}

namespace nbl::this_example::gui
{

class CUIManager final : public core::IReferenceCounted
{
public:
static constexpr uint32_t MaxUITextureCount = 16u;
static constexpr uint32_t TexturesBindingIndex = 0u;

// Texture indices for session buffers (after font atlas at index 0)
enum class SessionTextureIndex : uint32_t
{
Beauty = 0,
Albedo,
Normal,
Motion,
Mask,
RWMCCascades,
SampleCount,
Count
};

struct SCreationParams
{
core::smart_refctd_ptr<asset::IAssetManager> assetManager;
core::smart_refctd_ptr<video::IUtilities> utilities;
video::IQueue* transferQueue = nullptr;
system::logger_opt_smart_ptr logger = nullptr;
};

struct SCachedParams : SCreationParams
{
};

CUIManager(SCachedParams&& params) : m_params(std::move(params)) {}

struct SInitParams
{
video::IGPURenderpass* renderpass = nullptr;
std::function<void(size_t sensorIndex)> onSensorSelected = nullptr;
std::function<void(const std::string& path)> onLoadSceneRequested = nullptr;
std::function<void()> onReloadSceneRequested = nullptr;

// Session Callbacks
std::function<void(CSession::RenderMode mode, CSession* session)> onRenderModeChanged = nullptr;
std::function<void(uint16_t width, uint16_t height)> onResolutionChanged = nullptr;
std::function<void(const SSensorDynamics& dynamics, CSession* session)> onMutablesChanged = nullptr;
std::function<void(const SSensorDynamics& dynamics, CSession* session)> onDynamicsChanged = nullptr;
std::function<void(int bufferIndex)> onBufferSelected = nullptr;

};

static core::smart_refctd_ptr<CUIManager> create(SCreationParams&& params);

// Initialize GPU resources
bool init(const SInitParams& params);

// Cleanup (call before destruction)
void deinit();

// Set current scene for the scene window
void setScene(const CScene* scene, const std::string& scenePath = "");

// Set current active session for session window and bind its textures
void setSession(CSession* session, video::ISemaphore* semaphore = nullptr, uint64_t semaphoreValue = 0);

// Update ImGui state with input events
void update(const nbl::ext::imgui::UI::SUpdateParameters& params);

// Draw all UI windows - called between beginRenderpass() and endRenderpassAndPresent()
void drawWindows();
bool render(video::IGPUCommandBuffer* cmdbuf, video::ISemaphore::SWaitInfo waitInfo = {});

nbl::ext::imgui::UI* getImGuiManager() { return m_imguiManager.get(); }
video::IGPUDescriptorSet* getDescriptorSet() { return m_subAllocDS ? m_subAllocDS->getDescriptorSet() : nullptr; }

CSceneWindow& getSceneWindow() { return m_sceneWindow; }
CSessionWindow& getSessionWindow() { return m_sessionWindow; }

// Reset window positions (call when viewport/resolution changes)
void resetWindowPositions() { m_needsRepositionWindows = true; }

private:
// Bind session textures to descriptor set
void bindSessionTextures(CSession* session);
// Unbind session textures (deallocate indices)
void unbindSessionTextures(video::ISemaphore* semaphore, uint64_t semaphoreValue);

SCachedParams m_params;

// ImGui extension manager
core::smart_refctd_ptr<nbl::ext::imgui::UI> m_imguiManager;

// SubAllocated descriptor set for dynamic texture management
core::smart_refctd_ptr<video::SubAllocatedDescriptorSet> m_subAllocDS;

// Allocated texture indices for session buffers
std::array<video::SubAllocatedDescriptorSet::value_type, static_cast<size_t>(SessionTextureIndex::Count)> m_sessionTextureIndices;

// Samplers
struct
{
core::smart_refctd_ptr<video::IGPUSampler> gui;
core::smart_refctd_ptr<video::IGPUSampler> user;
} m_samplers;

// Current session (for tracking when it changes)
CSession* m_currentSession = nullptr;

// UI Windows
CSceneWindow m_sceneWindow;
CSessionWindow m_sessionWindow;

bool m_initialized = false;
bool m_needsRepositionWindows = true; // Start true to position on first frame
};

} // namespace nbl::this_example::gui

#endif // _NBL_THIS_EXAMPLE_C_UI_MANAGER_H_INCLUDED_
4 changes: 3 additions & 1 deletion 40_PathTracer/include/renderer/present/CWindowPresenter.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// This file is part of the "Nabla Engine".
// For conditions of distribution and use, see copyright notice in nabla.h
#ifndef _NBL_THIS_EXAMPLE_C_WINDOW_PRESENTER_H_INCLUDED_
#define _NBL_THIS_EXAMPLE_C_BASIC_RWMC_RESOLVER_H_INCLUDED_
#define _NBL_THIS_EXAMPLE_C_WINDOW_PRESENTER_H_INCLUDED_


#include "nbl/ext/FullScreenTriangle/FullScreenTriangle.h"
Expand Down Expand Up @@ -49,6 +49,8 @@ class CWindowPresenter : public IPresenter

//
inline const video::IGPURenderpass* getRenderpass() const {return getSwapchainResources()->getRenderpass();}
inline video::IGPURenderpass* getRenderpass() {return getSwapchainResources()->getRenderpass();}
inline ui::IWindow* getWindow() {return m_construction.window;}

//
bool irrecoverable() const {return m_construction.surface->irrecoverable() || !m_construction.surface->isWindowOpen();}
Expand Down
3 changes: 3 additions & 0 deletions 40_PathTracer/include/renderer/present/IPresenter.h
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,9 @@ class IPresenter : public core::IReferenceCounted, public core::InterfaceUnmovab
inline video::IQueue* getQueue() const {return m_queue;}
//
inline video::ILogicalDevice* getDevice() const {return const_cast<video::ILogicalDevice*>(m_semaphore->getOriginDevice());}

inline const video::ISemaphore* getSemaphore() const { return m_semaphore.get(); }
inline uint64_t getPresentCount() const { return m_presentCount; }

//
virtual bool irrecoverable() const {return false;}
Expand Down
Loading