From 4ec94c97879aafef15f7663135745e4ba61e62cf Mon Sep 17 00:00:00 2001 From: Adrian Kummerlaender Date: Mon, 17 May 2021 00:15:33 +0200 Subject: Extract first public LiterateLB version --- tangle/util/camera.h | 86 ++++++++++++++++++++++++++++ tangle/util/colormap.h | 38 ++++++++++++ tangle/util/noise.h | 39 +++++++++++++ tangle/util/render_window.h | 95 ++++++++++++++++++++++++++++++ tangle/util/texture.h | 25 ++++++++ tangle/util/timer.h | 19 ++++++ tangle/util/volumetric_example.h | 121 +++++++++++++++++++++++++++++++++++++++ 7 files changed, 423 insertions(+) create mode 100644 tangle/util/camera.h create mode 100644 tangle/util/colormap.h create mode 100644 tangle/util/noise.h create mode 100644 tangle/util/render_window.h create mode 100644 tangle/util/texture.h create mode 100644 tangle/util/timer.h create mode 100644 tangle/util/volumetric_example.h (limited to 'tangle/util') diff --git a/tangle/util/camera.h b/tangle/util/camera.h new file mode 100644 index 0000000..0a793d3 --- /dev/null +++ b/tangle/util/camera.h @@ -0,0 +1,86 @@ +#include +#include "SFML/Window/Event.hpp" + +class Camera { +private: + float _distance; + float _phi; + float _psi; + float3 _target; + float3 _eye; + float3 _direction; + bool _dragging; + bool _moving; + float2 _lastMouse; + +public: + Camera(float3 target, float distance, float phi, float psi): + _distance(distance), + _phi(phi), + _psi(psi), + _target(target), + _dragging(false), + _moving(false) { + update(); + } + + void update() { + _eye = _target + make_float3(_distance*sin(_psi)*cos(_phi), _distance*sin(_psi)*sin(_phi), _distance*cos(_psi)); + _direction = normalize(_target - _eye); + } + + void handle(sf::Event& event) { + switch (event.type) { + case sf::Event::MouseWheelMoved: + _distance -= event.mouseWheel.delta * 10; + break; + case sf::Event::MouseButtonPressed: + if (event.mouseButton.button == sf::Mouse::Left) { + _dragging = true; + _lastMouse = make_float2(event.mouseButton.x, event.mouseButton.y); + } else if (event.mouseButton.button == sf::Mouse::Right) { + _moving = true; + _lastMouse = make_float2(event.mouseButton.x, event.mouseButton.y); + } + break; + case sf::Event::MouseButtonReleased: + if (event.mouseButton.button == sf::Mouse::Left) { + _dragging = false; + } else if (event.mouseButton.button == sf::Mouse::Right) { + _moving = false; + } + break; + case sf::Event::MouseMoved: + if (_dragging) { + float2 mouse = make_float2(event.mouseMove.x, event.mouseMove.y); + float2 delta = mouse - _lastMouse; + _lastMouse = mouse; + _phi += 0.4*delta.x * 2*M_PI/360; + if (delta.y > 0 && _psi <= M_PI-2*M_PI/60) { + _psi += 0.4*delta.y * M_PI/180; + } else if (delta.y < 0 && _psi >= 2*M_PI/60) { + _psi += 0.4*delta.y * M_PI/180; + } + } + if (_moving) { + float2 mouse = make_float2(event.mouseMove.x, event.mouseMove.y); + float2 delta = mouse - _lastMouse; + _lastMouse = mouse; + float3 forward = normalize(_target - _eye); + float3 right = normalize(cross(make_float3(0.f, 0.f, -1.f), forward)); + float3 up = cross(right, forward); + _target += 0.4*right*delta.x - 0.4*up*delta.y; + } + break; + } + update(); + } + + float3 getDirection() const { + return _direction; + } + + float3 getEyePosition() const { + return _eye; + } +}; diff --git a/tangle/util/colormap.h b/tangle/util/colormap.h new file mode 100644 index 0000000..c01cea8 --- /dev/null +++ b/tangle/util/colormap.h @@ -0,0 +1,38 @@ +#pragma once +#include "assets.h" +#include "texture.h" + +#include +#include +#include + +struct ColorPalette { + const assets::File* current; + sf::Texture texture; + + ColorPalette(cudaSurfaceObject_t& palette) { + current = &assets::palette::files[5]; + texture.loadFromMemory(current->data, current->size); + palette = bindTextureToCuda(texture); + } + + void interact(); +}; + +void ColorPalette::interact() { + if (ImGui::BeginCombo("Color palette", current->name.c_str())) { + for (unsigned i=0; i < assets::palette::file_count; ++i) { + bool is_selected = (current == &assets::palette::files[i]); + if (ImGui::Selectable(assets::palette::files[i].name.c_str(), is_selected)) { + current = &assets::palette::files[i]; + texture.loadFromMemory(current->data, current->size); + break; + } + if (is_selected) { + ImGui::SetItemDefaultFocus(); + } + } + ImGui::EndCombo(); + } + ImGui::Image(texture, sf::Vector2f(400.,40.)); +} diff --git a/tangle/util/noise.h b/tangle/util/noise.h new file mode 100644 index 0000000..22ad548 --- /dev/null +++ b/tangle/util/noise.h @@ -0,0 +1,39 @@ +#pragma once +#include "assets.h" +#include "texture.h" + +#include +#include +#include + +struct NoiseSource { + const assets::File* current; + sf::Texture texture; + + NoiseSource(cudaSurfaceObject_t& noise) { + current = &assets::noise::files[0]; + texture.loadFromMemory(current->data, current->size); + noise = bindTextureToCuda(texture); + } + + void interact(); +}; + +void NoiseSource::interact() { + ImGui::Image(texture, sf::Vector2f(32,20)); + ImGui::SameLine(); + if (ImGui::BeginCombo("Noise", current->name.c_str())) { + for (unsigned i=0; i < assets::noise::file_count; ++i) { + bool is_selected = (current == &assets::noise::files[i]); + if (ImGui::Selectable(assets::noise::files[i].name.c_str(), is_selected)) { + current = &assets::noise::files[i]; + texture.loadFromMemory(current->data, current->size); + break; + } + if (is_selected) { + ImGui::SetItemDefaultFocus(); + } + } + ImGui::EndCombo(); + } +} diff --git a/tangle/util/render_window.h b/tangle/util/render_window.h new file mode 100644 index 0000000..4392150 --- /dev/null +++ b/tangle/util/render_window.h @@ -0,0 +1,95 @@ +#pragma once + +#include +#include + +#include +#include + +#include "texture.h" +#include "assets.h" + +class RenderWindow { +private: +sf::RenderWindow _window; + +sf::Sprite _render_sprite; +sf::Texture _render_texture; +cudaSurfaceObject_t _render_surface; +sf::Rect _render_texture_view; + +sf::Shader _blur_shader; +bool _blur = false; + +sf::Clock _ui_delta_clock; + +public: +RenderWindow(std::string name): + _window(sf::VideoMode(800, 600), name) { + _render_texture.create(sf::VideoMode::getDesktopMode().width, sf::VideoMode::getDesktopMode().height); + _render_surface = bindTextureToCuda(_render_texture); + _render_sprite.setTexture(_render_texture); + _render_texture_view = sf::Rect(0,0,_window.getSize().x,_window.getSize().y); + _render_sprite.setTextureRect(_render_texture_view); + _window.setView(sf::View(sf::Vector2f(_render_texture_view.width/2, _render_texture_view.height/2), + sf::Vector2f(_window.getSize().x, _window.getSize().y))); + _window.setVerticalSyncEnabled(true); + _blur_shader.loadFromMemory(std::string(reinterpret_cast(assets::shader::file_blur_frag)), sf::Shader::Fragment); + _blur_shader.setUniform("texture", sf::Shader::CurrentTexture); + ImGui::SFML::Init(_window); + ImGuiIO& io = ImGui::GetIO(); + io.MouseDrawCursor = true; +}; + +bool isOpen() const { + return _window.isOpen(); +} + +void setBlur(bool state) { + _blur = state; +} + +template +void draw(UI ui, MOUSE mouse); + +cudaSurfaceObject_t getRenderSurface() { + return _render_surface; +} + +sf::Rect getRenderView() { + return _render_texture_view; +} + +}; + +template +void RenderWindow::draw(UI ui, MOUSE mouse) { + sf::Event event; + while (_window.pollEvent(event)) { + ImGui::SFML::ProcessEvent(event); + if (event.type == sf::Event::Closed) { + _window.close(); + } + if (event.type == sf::Event::Resized) { + _render_texture_view = sf::Rect(0,0,event.size.width,event.size.height); + _render_sprite.setTextureRect(_render_texture_view); + sf::View view(sf::Vector2f(_render_texture_view.width/2, _render_texture_view.height/2), + sf::Vector2f(event.size.width, event.size.height)); + _window.setView(view); + } + if (!ImGui::GetIO().WantCaptureMouse) { + mouse(event); + } + } + + ImGui::SFML::Update(_window, _ui_delta_clock.restart()); + ui(); + _window.clear(); + if (_blur) { + _window.draw(_render_sprite, &_blur_shader); + } else { + _window.draw(_render_sprite); + } + ImGui::SFML::Render(_window); + _window.display(); +} diff --git a/tangle/util/texture.h b/tangle/util/texture.h new file mode 100644 index 0000000..f2e0440 --- /dev/null +++ b/tangle/util/texture.h @@ -0,0 +1,25 @@ +#pragma once + +#include +#include +#include +#include + +cudaSurfaceObject_t bindTextureToCuda(sf::Texture& texture) { + GLuint gl_tex_handle = texture.getNativeHandle(); + cudaGraphicsResource* cuda_tex_handle; + cudaArray* buffer; + + cudaGraphicsGLRegisterImage(&cuda_tex_handle, gl_tex_handle, GL_TEXTURE_2D, cudaGraphicsRegisterFlagsNone); + cudaGraphicsMapResources(1, &cuda_tex_handle, 0); + cudaGraphicsSubResourceGetMappedArray(&buffer, cuda_tex_handle, 0, 0); + + cudaResourceDesc resDesc; + resDesc.resType = cudaResourceTypeArray; + + resDesc.res.array.array = buffer; + cudaSurfaceObject_t cudaSurfaceObject = 0; + cudaCreateSurfaceObject(&cudaSurfaceObject, &resDesc); + + return cudaSurfaceObject; +} diff --git a/tangle/util/timer.h b/tangle/util/timer.h new file mode 100644 index 0000000..fd5d832 --- /dev/null +++ b/tangle/util/timer.h @@ -0,0 +1,19 @@ +#pragma once +#include + +namespace timer { + +std::chrono::time_point now() { + return std::chrono::steady_clock::now(); +} + +double secondsSince( + std::chrono::time_point& pit) { + return std::chrono::duration_cast>(now() - pit).count(); +} + +double mlups(std::size_t nCells, std::size_t nSteps, std::chrono::time_point& start) { + return nCells * nSteps / (secondsSince(start) * 1e6); +} + +} diff --git a/tangle/util/volumetric_example.h b/tangle/util/volumetric_example.h new file mode 100644 index 0000000..cc6a24e --- /dev/null +++ b/tangle/util/volumetric_example.h @@ -0,0 +1,121 @@ +#pragma once +#include + +#include "camera.h" +#include "texture.h" +#include "colormap.h" +#include "noise.h" +#include "render_window.h" +#include "../sampler/sampler.h" + +class VolumetricExample : public RenderWindow { +private: +std::vector> _sampler; +Sampler* _current = nullptr; + +Camera _camera; +VolumetricRenderConfig _config; +ColorPalette _palette; +NoiseSource _noise; + +int _steps_per_second = 100; +int _samples_per_second = 30; + +public: +VolumetricExample(descriptor::CuboidD<3> cuboid): + RenderWindow("LiterateLB"), + _camera(make_float3(cuboid.nX/2,cuboid.nY/2,cuboid.nZ/2), cuboid.nX, M_PI/2, M_PI/2), + _config(cuboid), + _palette(_config.palette), + _noise(_config.noise) +{ + _config.canvas = this->getRenderSurface(); + this->setBlur(_config.apply_blur); +} + +template class SAMPLER, typename... ARGS> +void add(ARGS&&... args) { + _sampler.emplace_back(new SAMPLER(std::forward(args)...)); + _current = _sampler.back().get(); +} + +template +void run(TIMESTEP step) { + sf::Clock last_sample; + sf::Clock last_frame; + std::size_t iStep = 0; + volatile bool simulate = true; + + sf::Thread simulation([&]() { + while (this->isOpen()) { + if (last_sample.getElapsedTime().asSeconds() > 1.0 / _samples_per_second) { + _current->sample(); + cudaStreamSynchronize(cudaStreamPerThread); + last_sample.restart(); + if (simulate) { + for (unsigned i=0; i < (1.0 / _samples_per_second) * _steps_per_second; ++i) { + step(iStep++); + } + } + } + } + }); + simulation.launch(); + + while (this->isOpen()) { + this->draw( + [&](){ + ImGui::Begin("Simulation", 0, ImGuiWindowFlags_AlwaysAutoResize); + if (ImGui::BeginCombo("Source", _current->getName().c_str())) { + for (auto& option : _sampler) { + if (ImGui::Selectable(option->getName().c_str(), _current == option.get())) { + _current = option.get(); + } + } + ImGui::EndCombo(); + } + _current->interact(); + ImGui::SliderInt("Timestep/s", &_steps_per_second, 1, 2000); + ImGui::SliderInt("Samples/s", &_samples_per_second, 1, 60); + if (simulate) { + simulate = !ImGui::Button("Pause"); + } else { + simulate = ImGui::Button("Continue"); + } + ImGui::End(); + ImGui::Begin("Render", 0, ImGuiWindowFlags_AlwaysAutoResize); + ImGui::SliderFloat("Brightness", &_config.brightness, 0.1f, 2.f); + ImGui::SliderFloat("Delta", &_config.delta, 0.05f, 2.f); + ImGui::SliderFloat("Transparency", &_config.transparency, 0.001f, 1.f); + _palette.interact(); + if (ImGui::CollapsingHeader("Details")) { + ImGui::Checkbox("Align slices to view", &_config.align_slices_to_view); + ImGui::SameLine(); + ImGui::Checkbox("Jitter", &_config.apply_noise); + ImGui::SameLine(); + ImGui::Checkbox("Blur", &_config.apply_blur); + this->setBlur(_config.apply_blur); + if (_config.apply_noise) { + _noise.interact(); + } + } + ImGui::End(); + }, + [&](sf::Event& event) { + _camera.handle(event); + _config.eye_pos = _camera.getEyePosition(); + _config.eye_dir = _camera.getDirection(); + _config.canvas_size = make_uint2(this->getRenderView().width, this->getRenderView().height); + } + ); + if (last_frame.getElapsedTime().asSeconds() > 1.0 / _samples_per_second) { + _current->render(_config); + cudaStreamSynchronize(cudaStreamPerThread); + last_frame.restart(); + } + } + + simulation.wait(); +} + +}; -- cgit v1.2.3