Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Texture rendering #209

Open
wants to merge 16 commits into
base: RayTrace
Choose a base branch
from
29 changes: 28 additions & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,31 @@ Before pull request please run clang-tidy on your code (on windows you can use c

**T.2 - Naming** Try to be nice to the template user. Refrein from using `class T` as universal name for everything. Do you expect enum class? Name it! `template<class Enum>`.

**T.3 - SFINAE** Be nice to the user. Rather than using `std::enable_if<>::type` use C++17 less boiler plate `std::enable_if_t<>`. And every time you can use `static_asser` instead as you can explain reason to not allow instantiation of the template with given arguments.
**T.3 - SFINAE** Be nice to the user. Rather than using `std::enable_if<>::type` use C++17 less boiler plate `std::enable_if_t<>`. And every time you can use `static_assert` instead as you can explain reason to not allow instantiation of the template with given arguments.

###### Classes

**C.1 Declaration order** - First declare `public` part of the class and place constructors and destructors as first memeber functions. Than `protected` part and `private` as last part.
Reason: Bear in mind you want to comunicate usage of the class first, than implementation details. Programmer who will come to your header will most likely will be looking for usage of the class, not the implementation details such as member variables.

**C2. Definition** - The source file should first state reflection of the class if present. Followd by static variables definition. The first member functions defined should be constructors and after them destructor. The order of member functions is not defined.

**C2.1 Destructor definition** - Put destructor definition in source file even for defaulted destructors. This helps header separation.

**C2.2 Separators** - Each member function definition should be separated by separator. This helps readablitiy.

```
//=================================================================================
```

**C.3 Initialize member variables** - Uninitialized variables leads to undefined behaviour. Initialize all member variables in the constructor.

**C.3.1 Unify initialization** - Either init all member variables in initialization list or as inline initialization in header. Do not combine those as it makes **C.2** rule harder to follow.

###### Testing

This project is not fully tested. But unit test proven to be useful to keep at least basic invariants intact even during biggest changes. Rule of thumb I am trying to follow is when I encounter bug I write a unit test to fixate the invariant by it. I am trying to implement unit tests at least for the basic building block of this engine.

**TEST.1 Test structure** - Place tests to separate projects following naming convention of engine projects. Tests should be placed to source files named same way as engine sources. This could be broken by moving of files in the engine, but helps to keep track of test placemennt. Tests are executed during CI and fail of the test will block Pull requests.

**TEST.2 Private functions testing** - When testing private functions of the class use fixture that is set as `friend class` of the tested class.
142 changes: 142 additions & 0 deletions Editor/Editor/Editors/Image/Tools/BrickGenerator.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
#include <EditorStdafx.h>

#include <Editor/Editors/Image/Tools/BrickGenerator.h>

#include <GUI/ReflectionGUI.h>

#include <random>
#include <rttr/registration>

// clang-format off
RTTR_REGISTRATION
{
using namespace GLEngine::Editor;
using namespace Utils::Reflection;

rttr::registration::class_<C_BrickGenerator>("C_BrickGenerator")
.property("Height", &C_BrickGenerator::m_RowHeight)(
rttr::policy::prop::bind_as_ptr,
RegisterMetaclass<MetaGUI::SliderInt>(),
RegisterMetamember<UI::SliderInt::Name>("Row height:"),
RegisterMetamember<UI::SliderInt::Min>(1),
RegisterMetamember<UI::SliderInt::Max>(100))
.property("BrickWidth", &C_BrickGenerator::m_BrickWidth)(
rttr::policy::prop::bind_as_ptr,
RegisterMetaclass<MetaGUI::SliderInt>(),
RegisterMetamember<UI::SliderInt::Name>("Row width:"),
RegisterMetamember<UI::SliderInt::Min>(1),
RegisterMetamember<UI::SliderInt::Max>(100))
.property("BrickOffset", &C_BrickGenerator::m_BrickOffset)(
rttr::policy::prop::bind_as_ptr,
RegisterMetaclass<MetaGUI::SliderInt>(),
RegisterMetamember<UI::SliderInt::Name>("Brick offset:"),
RegisterMetamember<UI::SliderInt::Min>(1),
RegisterMetamember<UI::SliderInt::Max>(100))
#pragma region Randomness
.property("RowHeightDeviation", &C_BrickGenerator::m_RowHeightDeviation)(
rttr::policy::prop::bind_as_ptr,
RegisterMetaclass<MetaGUI::SliderInt>(),
RegisterMetamember<MetaGUIInfo::CollapsableGroup>("Randomness"),
RegisterMetamember<UI::SliderInt::Name>("Row height deviation:"),
RegisterMetamember<UI::SliderInt::Min>(1),
RegisterMetamember<UI::SliderInt::Max>(100))
.property("BrickWidthDeviation", &C_BrickGenerator::m_BrickWidthDeviation)(
rttr::policy::prop::bind_as_ptr,
RegisterMetaclass<MetaGUI::SliderInt>(),
RegisterMetamember<MetaGUIInfo::CollapsableGroup>("Randomness"),
RegisterMetamember<UI::SliderInt::Name>("Brick width deviation:"),
RegisterMetamember<UI::SliderInt::Min>(1),
RegisterMetamember<UI::SliderInt::Max>(100))
.property("MortarWidthDeviation", &C_BrickGenerator::m_MortarWidthDeviation)(
rttr::policy::prop::bind_as_ptr,
RegisterMetaclass<MetaGUI::SliderInt>(),
RegisterMetamember<MetaGUIInfo::CollapsableGroup>("Randomness"),
RegisterMetamember<UI::SliderInt::Name>("Mortar width deviation:"),
RegisterMetamember<UI::SliderInt::Min>(1),
RegisterMetamember<UI::SliderInt::Max>(100))
.property("MortarHeightDeviation", &C_BrickGenerator::m_MortarHeightDeviation)(
rttr::policy::prop::bind_as_ptr,
RegisterMetaclass<MetaGUI::SliderInt>(),
RegisterMetamember<MetaGUIInfo::CollapsableGroup>("Randomness"),
RegisterMetamember<UI::SliderInt::Name>("Mortar height deviation:"),
RegisterMetamember<UI::SliderInt::Min>(1),
RegisterMetamember<UI::SliderInt::Max>(100))
#pragma endregion
.property("MortarThickness", &C_BrickGenerator::m_MortarThickness)(
rttr::policy::prop::bind_as_ptr,
RegisterMetaclass<MetaGUI::SliderInt>(),
RegisterMetamember<UI::SliderInt::Name>("Mortar thickness:"),
RegisterMetamember<UI::SliderInt::Min>(1),
RegisterMetamember<UI::SliderInt::Max>(100))
.property("BrickColour", &C_BrickGenerator::m_BrickColour)(
rttr::policy::prop::bind_as_ptr,
RegisterMetaclass<MetaGUI::Colour>(),
RegisterMetamember<UI::Colour::Name>("Brick colour:"))
.property("MortarColour", &C_BrickGenerator::m_MortarColour)(
rttr::policy::prop::bind_as_ptr,
RegisterMetaclass<MetaGUI::Colour>(),
RegisterMetamember<UI::Colour::Name>("Mortar colour:")
);
}
// clang-format on

namespace GLEngine::Editor {
//=================================================================================
C_BrickGenerator::C_BrickGenerator(Renderer::C_TextureView view)
: C_ImageEditorTool(view)
, m_RowHeight(10)
, m_BrickWidth(30)
, m_MortarThickness(2)
, m_BrickOffset(20)
, m_RowHeightDeviation(0)
, m_BrickWidthDeviation(0)
, m_MortarWidthDeviation(0)
, m_MortarHeightDeviation(0)
, m_BrickColour(Colours::T_Colour{227.f / 255.f, 3.f / 255.f, 12.f / 255.f})
, m_MortarColour(Colours::T_Colour{93.f / 255.f, 93.f / 255.f, 93.f / 255.f})
{
}

//=================================================================================
void C_BrickGenerator::Apply()
{
std::random_device rd; // a seed source for the random number engine
std::mt19937 gen(rd()); // mersenne_twister_engine seeded with rd()
std::uniform_int_distribution<> rowHeightGen(-m_RowHeightDeviation / 2, m_RowHeightDeviation / 2);
std::uniform_int_distribution<> brickWidthGen(-m_BrickWidthDeviation / 2, m_BrickWidthDeviation / 2);
std::uniform_int_distribution<> mortarWidthGen(-m_MortarWidthDeviation / 2, m_MortarWidthDeviation / 2);
std::uniform_int_distribution<> mortarHeightGen(-m_MortarHeightDeviation / 2, m_MortarHeightDeviation / 2);

m_View.ClearColor({m_MortarColour, 1.f});
unsigned int mortarTop = 0;
int totalOffset = 0;
int row = 0;
while (mortarTop < m_View.GetDimensions().y)
{
int mortarLeft = totalOffset;
int rowHeight = m_RowHeight + rowHeightGen(gen);
while (mortarLeft < static_cast<int>(m_View.GetDimensions().x))
{
int brickWidth = m_BrickWidth + brickWidthGen(gen);
int leftMortarThickness = m_MortarThickness + mortarWidthGen(gen);
int brickEnd = mortarLeft + leftMortarThickness + brickWidth - 1;
if (brickEnd >= 0)
{
for (int i = 0; i < rowHeight; ++i)
{
m_View.FillLineSpan(m_BrickColour, mortarTop + m_MortarThickness + i, std::max(0, mortarLeft + leftMortarThickness),
mortarLeft + leftMortarThickness + brickWidth - 1);
}
}
mortarLeft += leftMortarThickness + brickWidth;
}
mortarTop += m_MortarThickness + rowHeight + mortarHeightGen(gen);
totalOffset -= m_BrickOffset;
if (totalOffset <= -2 * (m_BrickWidth + m_MortarThickness))
totalOffset += (m_BrickWidth + m_MortarThickness);

row++;
}
}

} // namespace GLEngine::Editor
35 changes: 35 additions & 0 deletions Editor/Editor/Editors/Image/Tools/BrickGenerator.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#pragma once

#include <Editor/Editors/ImageEditorTool.h>

#include <Renderer/Colours.h>
#include <Renderer/Textures/TextureView.h>

#include <rttr/registration.h>
#include <rttr/registration_friend.h>

namespace GLEngine::Editor {
class C_BrickGenerator : public C_ImageEditorTool {
public:
C_BrickGenerator(Renderer::C_TextureView view);
RTTR_ENABLE(C_ImageEditorTool);
RTTR_REGISTRATION_FRIEND;

void Apply() override;

private:
int m_RowHeight;
int m_BrickWidth;
int m_MortarThickness;
int m_BrickOffset;

// randomness
int m_RowHeightDeviation;
int m_BrickWidthDeviation;
int m_MortarWidthDeviation;
int m_MortarHeightDeviation;

Colours::T_Colour m_BrickColour;
Colours::T_Colour m_MortarColour;
};
} // namespace GLEngine::Editor
182 changes: 182 additions & 0 deletions Editor/Editor/Editors/ImageEditor.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
#include <EditorStdafx.h>

#include <Editor/Editors/Image/Tools/BrickGenerator.h>
#include <Editor/Editors/ImageEditor.h>
#include <Editor/Editors/ImageEditorTool.h>

#include <Renderer/Colours.h>
#include <Renderer/IDevice.h>
#include <Renderer/IRenderer.h>
#include <Renderer/Render/CPURasterizer.h>
#include <Renderer/Textures/TextureView.h>

#include <GUI/FileDialogWindow.h>
#include <GUI/GUIManager.h>
#include <GUI/Menu/Menu.h>
#include <GUI/Menu/MenuItem.h>
#include <GUI/ReflectionGUI.h>

#include <Core/Application.h>

#include <Utils/HighResolutionTimer.h>

#include <imgui_internal.h>

namespace GLEngine::Editor {

const static glm::vec2 s_ImageDrawArea{800, 800};
const static glm::uvec2 s_BackgroundDim{2, 2};

//=================================================================================
C_ImageEditor::C_ImageEditor(GUID guid, GUI::C_GUIManager& guiMGR)
: GUI::C_Window(guid, "Image editor")
, m_Storage(1024, 1024, 4)
, m_GUIImage(nullptr)
, m_FileMenu("File")
, m_Tools("Tools")
, m_DeviceImage(nullptr)
, m_Background(nullptr)
{
auto& device = Core::C_Application::Get().GetActiveRenderer().GetDevice();
const Renderer::TextureDescriptor desc{.name = "Editor image",
.width = 1024,
.height = 1024,
.type = Renderer::E_TextureType::TEXTURE_2D,
.format = Renderer::E_TextureFormat::RGBA32f,
.m_bStreamable = false};
const Renderer::TextureDescriptor descTransparent{.name = "Transparent background",
.width = s_BackgroundDim.x,
.height = s_BackgroundDim.y,
.type = Renderer::E_TextureType::TEXTURE_2D,
.format = Renderer::E_TextureFormat::RGB32f,
.m_bStreamable = false};

m_DeviceImage = device.CreateTextureHandle(desc);
m_Background = device.CreateTextureHandle(descTransparent);
device.AllocateTexture(*m_DeviceImage.get());
device.AllocateTexture(*m_Background.get());
m_Storage.SetAll(glm::vec4(Colours::white, 0.f));
{
Renderer::C_TextureViewStorageCPU<float> storage(s_BackgroundDim.x, s_BackgroundDim.y, 3);
storage.SetAll(glm::vec4(Colours::white, 0.f));
Renderer::C_TextureView view(&storage);

view.Set(glm::ivec2{0, 0}, glm::vec3{Colours::gray});
view.Set(glm::ivec2{1, 1}, glm::vec3{Colours::gray});

m_Background->SetTexData2D(0, &storage);
m_Background->SetWrap(Renderer::E_WrapFunction::Repeat, Renderer::E_WrapFunction::Repeat);
m_Background->SetFilter(Renderer::E_TextureFilter::Nearest, Renderer::E_TextureFilter::Nearest);
}

AddMenu(m_FileMenu);
AddMenu(m_Tools);

m_Tools.AddMenuItem(guiMGR.CreateMenuItem<GUI::Menu::C_MenuItem>("Histogram", std::bind(&C_ImageEditor::ToggleHistogram, this)));
m_Tools.AddMenuItem(guiMGR.CreateMenuItem<GUI::Menu::C_MenuItem>("Brick", [&]() { m_ActiveTool = std::make_unique<C_BrickGenerator>(Renderer::C_TextureView(&m_Storage)); }));
m_Tools.AddMenuItem(guiMGR.CreateMenuItem<GUI::Menu::C_MenuItem>("Blur", [&]() { m_ActiveTool = std::make_unique<C_GaussianBlur>(Renderer::C_TextureView(&m_Storage)); }));
std::reference_wrapper<GUI::Menu::C_MenuItem> createMenuItem = guiMGR.CreateMenuItem<GUI::Menu::C_MenuItem>("Save as...", [&]() {
const auto textureSelectorGUID = NextGUID();
auto* textureSelectWindow = new GUI::C_FileDialogWindow(
".bmp,.hdr,.ppm", "Save image as...",
[&, textureSelectorGUID](const std::filesystem::path& texture) {
// TODO save here

guiMGR.DestroyWindow(textureSelectorGUID);
},
textureSelectorGUID, "./Images");
guiMGR.AddCustomWindow(textureSelectWindow);
textureSelectWindow->SetVisible();
});
m_FileMenu.AddMenuItem(createMenuItem);

m_GUIImage = new GUI::C_ImageViewer(*m_DeviceImage.get());
m_GUIImage->SetSize({800, 800});
m_DeviceImage->SetTexData2D(0, &m_Storage);

std::thread([&]() {
Utils::HighResolutionTimer timer;
timer.reset();
Renderer::C_TextureView view(&m_Storage);
view.EnableBlending();
int x1 = 100;
int x2 = 900;
int y1 = 100;
int y2 = 900;

glm::uvec2 center{512, 512};
Renderer::C_CPURasterizer rasterizer(view);
rasterizer.DrawCircle(Colours::red, center, 200);
rasterizer.DrawCircle(Colours::red, center, 400);
// rasterizer.FloodFill(Colours::blue, {212, 512});
// rasterizer.FloodFill(Colours::green, {512, 512});
// for (int i = 0; i < 36; ++i)
//{
// glm::ivec2 dir{std::cos(glm::radians(i * 10.f)) * 400, std::sin(glm::radians(i * 10.f)) * 400};
// rasterizer.DrawLine(Colours::red, center, glm::ivec2{center} + dir, true);
//}
m_bDone = true;
m_bModified = true;
CORE_LOG(E_Level::Info, E_Context::Render, "Line render time {}", float(timer.getElapsedTimeFromLastQueryMilliseconds()) / 1000.f);
}).detach();
}

//=================================================================================
C_ImageEditor::~C_ImageEditor()
{
delete m_GUIImage;
auto& device = Core::C_Application::Get().GetActiveRenderer().GetDevice();
device.DestroyTexture(*m_DeviceImage.get());
}

//=================================================================================
void C_ImageEditor::Update()
{
static int numUpdates = 0;
if (!m_bFinish)
{
++numUpdates;
if (m_bDone)
{
CORE_LOG(E_Level::Info, E_Context::Render, "Updates: {}", numUpdates);
}
m_DeviceImage->SetTexData2D(0, &m_Storage); // possibly miss last update
m_bFinish = m_bDone;
}
}

//=================================================================================
void C_ImageEditor::DrawComponents() const
{
const ImVec2 canvas_p0 = ImGui::GetCursorPos();
ImGui::Image((void*)(intptr_t)(m_Background->GetGPUHandle()), {s_ImageDrawArea.x, s_ImageDrawArea.y}, {0, 0},
{s_ImageDrawArea.x / (s_BackgroundDim.x * 5), s_ImageDrawArea.y / (s_BackgroundDim.y * 5)});
ImGui::SetCursorPos(canvas_p0);
m_GUIImage->Draw();
::ImGui::SameLine();
if (m_ActiveTool)
{
::ImGui::BeginChild("ImageTools");
rttr::instance obj(m_ActiveTool.get());
if (GUI::DrawAllPropertyGUI(obj).empty() == false)
{

}
if (ImGui::Button("Apply")) {
m_ActiveTool->Apply();
m_bFinish = false; // hack
}
::ImGui::EndChild();
}
}

void C_ImageEditor::ToggleHistogram()
{
CORE_LOG(E_Level::Error, E_Context::Core, "Histogram.");
}

void C_ImageEditor::SetupToolPreview()
{
}

} // namespace GLEngine::Editor
Loading
Loading