From 8f62c8f38791f09fa1d58b41cb9a161b6701405b Mon Sep 17 00:00:00 2001 From: IlyaShurupov Date: Wed, 19 Jun 2024 22:17:18 +0300 Subject: [PATCH] new widgets --- 3DEditor/private/Editor.cpp | 18 +++- 3DEditor/public/Editor.hpp | 6 ++ 3DEditor/public/EditorWidget.hpp | 108 ++++++++++++++++++++---- Graphics/private/EventHandler.cpp | 7 +- Graphics/private/GraphicApplication.cpp | 11 +++ Graphics/public/EventHandler.hpp | 5 ++ Graphics/public/Window.hpp | 2 +- Math/public/Camera.hpp | 9 +- Math/public/Rect.hpp | 4 + RasterRender/private/Render.cpp | 7 ++ Widgets/examples/SimpleGUI.cpp | 14 ++- Widgets/examples/SimpleGUI.hpp | 45 ++++++++++ Widgets/public/ButtonWidget.hpp | 4 + Widgets/public/CollapsableMenu.hpp | 91 ++++++++++++++++++++ Widgets/public/FloatingWidget.hpp | 46 ++++++++++ Widgets/public/LabelWidget.hpp | 6 +- Widgets/public/ScrollableWidget.hpp | 31 ++++--- Widgets/public/Widgets.hpp | 2 + 18 files changed, 373 insertions(+), 43 deletions(-) create mode 100644 Widgets/examples/SimpleGUI.hpp create mode 100644 Widgets/public/CollapsableMenu.hpp create mode 100644 Widgets/public/FloatingWidget.hpp diff --git a/3DEditor/private/Editor.cpp b/3DEditor/private/Editor.cpp index 5fc2931..e8838cf 100644 --- a/3DEditor/private/Editor.cpp +++ b/3DEditor/private/Editor.cpp @@ -58,7 +58,7 @@ void Editor::loadDefaults() { void Editor::setViewportSize(const Vec2F& size) { // TODO remove - mScene.mCamera.rotate(0.01f, 0.0); + // mScene.mCamera.rotate(0.01f, 0.0); mScene.mRenderSettings.size = size; @@ -100,3 +100,19 @@ void Editor::denoisePathRenderBuffers() { void Editor::setRenderType(Editor::RenderType type) { mRenderType = type; } + +void Editor::navigationOrbit(const Vec2F& delta) { + mScene.mCamera.rotate(delta.x, delta.y); +} + +void Editor::navigationPan(const Vec2F& pos, const Vec2F& prevPos) { + mScene.mCamera.move(pos, prevPos); +} + +void Editor::navigationZoom(halnf factor) { + mScene.mCamera.zoom(factor); +} + +void Editor::navigationReset() { + mScene.mCamera.lookAtPoint({ 0, 0, 0 }, { 1, 5, 1 }, { 0, 0, 1 }); +} \ No newline at end of file diff --git a/3DEditor/public/Editor.hpp b/3DEditor/public/Editor.hpp index 9e4d40b..ee16122 100644 --- a/3DEditor/public/Editor.hpp +++ b/3DEditor/public/Editor.hpp @@ -23,6 +23,12 @@ namespace tp { void setRenderType(RenderType type); void denoisePathRenderBuffers(); + void navigationOrbit(const Vec2F& delta); + void navigationPan(const Vec2F& pos, const Vec2F& prevPos); + void navigationZoom(halnf factor); + void navigationReset(); + + private: void sendBuffersToGPU(); void denoise(); diff --git a/3DEditor/public/EditorWidget.hpp b/3DEditor/public/EditorWidget.hpp index 13eed21..93a43a4 100644 --- a/3DEditor/public/EditorWidget.hpp +++ b/3DEditor/public/EditorWidget.hpp @@ -45,13 +45,36 @@ namespace tp { this->mChildWidgets.pushBack(&mSplitView); this->mChildWidgets.pushBack(&mSettingsWidget); - mRenderPathTracer.mLabel.mLabel = "Render with Path Tracer"; - mRenderRaster.mLabel.mLabel = "Render with Raster"; - mDenoiseButton.mLabel.mLabel = "Denoise (IntelOpenImage)"; + // Render + { + mRenderPathTracer.setLabel("Render with Path Tracer"); + mRenderRaster.setLabel("Render with Raster"); + mRenderDeNoise.setLabel("Denoise (IntelOpenImage)"); - mSettingsWidget.addWidget(&mRenderPathTracer); - mSettingsWidget.addWidget(&mRenderRaster); - mSettingsWidget.addWidget(&mDenoiseButton); + mRenderMenu.addWidgetToMenu(&mRenderPathTracer); + mRenderMenu.addWidgetToMenu(&mRenderRaster); + mRenderMenu.addWidgetToMenu(&mRenderDeNoise); + + mRenderMenu.setLabel("Render"); + } + + // Navigation + { + mNavigationPan.setLabel("Pan"); + mNavigationOrbit.setLabel("Orbit"); + mNavigationZoom.setLabel("Zoom"); + mNavigationReset.setLabel("Reset"); + + mNavigationMenu.addWidgetToMenu(&mNavigationPan); + mNavigationMenu.addWidgetToMenu(&mNavigationOrbit); + mNavigationMenu.addWidgetToMenu(&mNavigationZoom); + mNavigationMenu.addWidgetToMenu(&mNavigationReset); + + mNavigationMenu.setLabel("Navigation"); + } + + mSettingsWidget.addWidget(&mRenderMenu); + mSettingsWidget.addWidget(&mNavigationMenu); } void eventProcess(const Events& events) override { @@ -60,17 +83,60 @@ namespace tp { mViewport.setArea(mSplitView.getFirst()); mSettingsWidget.setArea(mSplitView.getSecond()); - if (mRenderPathTracer.isFired()) { - mEditor->renderPathFrame(); - mEditor->setRenderType(Editor::RenderType::PATH_TRACER); + // render settings + { + if (mRenderPathTracer.isFired()) { + mEditor->renderPathFrame(); + mEditor->setRenderType(Editor::RenderType::PATH_TRACER); + } + + if (mRenderRaster.isFired()) { + mEditor->setRenderType(Editor::RenderType::RASTER); + } + + if (mRenderDeNoise.isFired()) { + mEditor->denoisePathRenderBuffers(); + } } - if (mRenderRaster.isFired()) { - mEditor->setRenderType(Editor::RenderType::RASTER); - } + // navigation + { + if (mNavigationOrbit.isFired()) { + mNavigationType = ORBIT; + } - if (mDenoiseButton.isFired()) { - mEditor->denoisePathRenderBuffers(); + if (mNavigationPan.isFired()) { + mNavigationType = PAN; + } + + if (mNavigationZoom.isFired()) { + mNavigationType = ZOOM; + } + + if (mNavigationReset.isFired()) { + mEditor->navigationReset(); + } + + const auto& activeArea = mViewport.mArea; + if (this->isHolding() && activeArea.isInside(events.getPointer())) { + switch (mNavigationType) { + case ORBIT: + mEditor->navigationOrbit(events.getPointerDelta() / activeArea.size * 3); + break; + + case PAN: + { + auto pointer = (((events.getPointer() - activeArea.pos) / activeArea.size) - 0.5f) * 2; + auto prevPointer = (((events.getPointerPrev() - activeArea.pos) / activeArea.size) - 0.5f) * 2; + mEditor->navigationPan(prevPointer, pointer); + } + break; + + case ZOOM: + mEditor->navigationZoom(1 + (events.getPointerDelta().y / activeArea.size.y)); + break; + } + } } } @@ -89,12 +155,22 @@ namespace tp { SplitView mSplitView; ViewportWidget mViewport; + ScrollableWindow mSettingsWidget; // Controls - ScrollableWindow mSettingsWidget; + CollapsableMenu mRenderMenu; ButtonWidget mRenderPathTracer; ButtonWidget mRenderRaster; - ButtonWidget mDenoiseButton; + ButtonWidget mRenderDeNoise; + + // Navigation + enum NavigationType { ORBIT, PAN, ZOOM } mNavigationType = ORBIT; + + CollapsableMenu mNavigationMenu; + ButtonWidget mNavigationPan; + ButtonWidget mNavigationOrbit; + ButtonWidget mNavigationZoom; + ButtonWidget mNavigationReset; RGBA mBaseColor; }; diff --git a/Graphics/private/EventHandler.cpp b/Graphics/private/EventHandler.cpp index 3d5cdd8..f1f5c10 100644 --- a/Graphics/private/EventHandler.cpp +++ b/Graphics/private/EventHandler.cpp @@ -43,6 +43,7 @@ void EventHandler::processEvent() { switch (eventData.type) { case InputEvent::Type::MOUSE_POS: { + mPointerPrev = mPointer; mPointer = eventData.mouseEvent; mEventQueue.popFront(); break; @@ -77,6 +78,8 @@ void EventHandler::processEvent() { const Vec2F& EventHandler::getPointer() const { return mPointer; } +const Vec2F& EventHandler::getPointerPrev() const { return mPointerPrev; } + bool EventHandler::isPressed(InputID id) const { return mInputStates[(int) id].mCurrentState == InputState::State::PRESSED; } @@ -92,4 +95,6 @@ bool EventHandler::isDown(InputID id) const { mInputStates[(int) id].mCurrentState == InputState::State::HOLD; } -halnf EventHandler::getScrollY() const { return 0; } \ No newline at end of file +halnf EventHandler::getScrollY() const { return 0; } + +Vec2F EventHandler::getPointerDelta() const { return mPointer - mPointerPrev; } diff --git a/Graphics/private/GraphicApplication.cpp b/Graphics/private/GraphicApplication.cpp index 3bfd811..270c94d 100644 --- a/Graphics/private/GraphicApplication.cpp +++ b/Graphics/private/GraphicApplication.cpp @@ -23,6 +23,17 @@ void Application::run() { bool redrawNeeded = false; + // proc first frame by default + { + mWindow->processEvents(); + processFrame(eventHandler); + + mGraphics->drawBegin(); + drawFrame(mGraphics->getCanvas()); + mGraphics->drawEnd(); + mWindow->draw(); + } + while (!mWindow->shouldClose()) { if (mProcTimer.isTimeout()) { diff --git a/Graphics/public/EventHandler.hpp b/Graphics/public/EventHandler.hpp index 5e29884..802a427 100644 --- a/Graphics/public/EventHandler.hpp +++ b/Graphics/public/EventHandler.hpp @@ -70,6 +70,9 @@ namespace tp { void processEvent(); [[nodiscard]] const Vec2F& getPointer() const; + [[nodiscard]] const Vec2F& getPointerPrev() const; + [[nodiscard]] Vec2F getPointerDelta() const; + [[nodiscard]] bool isPressed(InputID id) const; [[nodiscard]] bool isReleased(InputID id) const; [[nodiscard]] bool isDown(InputID id) const; @@ -85,6 +88,8 @@ namespace tp { // input states Vec2F mPointer; + Vec2F mPointerPrev; + halnf mPointerPressure = 0; InputState mInputStates[(int) InputID::LAST_KEY_CODE]{}; diff --git a/Graphics/public/Window.hpp b/Graphics/public/Window.hpp index 3399739..15c9680 100644 --- a/Graphics/public/Window.hpp +++ b/Graphics/public/Window.hpp @@ -12,7 +12,7 @@ namespace tp { ~Window(); public: - static Window* createWindow(Vec2F size = { 1000.f, 900.f }, const char* title = "Window"); + static Window* createWindow(Vec2F size = { 500.f, 500.f }, const char* title = "Window"); static void destroyWindow(Window* window); public: diff --git a/Math/public/Camera.hpp b/Math/public/Camera.hpp index e4d4adc..2b2ba45 100644 --- a/Math/public/Camera.hpp +++ b/Math/public/Camera.hpp @@ -30,9 +30,16 @@ namespace tp { public: void lookAtPoint(const Vec3F& aTarget, const Vec3F& aPos, Vec3F aUp); - void rotate(halnf anglex, halnf angleY); + + // -1 -1 is top left 1 1 is bottom right void move(Vec2F aPos, Vec2F aPrevPos); + + // keeps z axis above your head + void rotate(halnf anglex, halnf angleY); + + // -1 -1 is top left 1 1 is bottom right void zoom(halnf ratio); + void offset_target(halnf val); public: diff --git a/Math/public/Rect.hpp b/Math/public/Rect.hpp index 8d90b91..c1045fb 100644 --- a/Math/public/Rect.hpp +++ b/Math/public/Rect.hpp @@ -159,6 +159,10 @@ namespace tp { size = p3 - pos; } + Rect shrink(Type val) const { + return { pos + val, size - val * 2 }; + } + // if only one point isInside bool clampOutside(Vec2& v1, Vec2& v2) { bool const in1 = isInside(v1); diff --git a/RasterRender/private/Render.cpp b/RasterRender/private/Render.cpp index 4c3429e..27d0f89 100644 --- a/RasterRender/private/Render.cpp +++ b/RasterRender/private/Render.cpp @@ -34,6 +34,9 @@ void RasterRender::render(const Scene& geometry, const Vec2& size) { glEnable(GL_DEPTH_TEST); + // glPolygonMode(GL_FRONT, GL_LINE); + // glPolygonMode(GL_BACK, GL_LINE); + for (auto object : geometry.mObjects) { static auto origin = (GLint) mDefaultShader.getu("Origin"); @@ -52,6 +55,10 @@ void RasterRender::render(const Scene& geometry, const Vec2& size) { mDefaultShader.unbind(); + + // glPolygonMode(GL_FRONT, GL_FILL); + // glPolygonMode(GL_BACK, GL_FILL); + mRenderBuffer.endDraw(); } diff --git a/Widgets/examples/SimpleGUI.cpp b/Widgets/examples/SimpleGUI.cpp index 81f6f40..2e6a49c 100644 --- a/Widgets/examples/SimpleGUI.cpp +++ b/Widgets/examples/SimpleGUI.cpp @@ -1,16 +1,15 @@ -#include "ChatGUI.hpp" +#include "SimpleGUI.hpp" #include "GraphicApplication.hpp" + using namespace tp; class SimpleGUI : public Application { public: SimpleGUI() { - mGui.addWidget(&mButton); - mGui.addWidget(&mLabel); - mGui.addWidget(&mSlider); + } void processFrame(EventHandler* eventHandler) override { @@ -22,17 +21,14 @@ public: } void drawFrame(Canvas* canvas) override { - canvas->rect(mGui.mArea, { 0, 0, 0, 1 }); + canvas->rect(mGui.mArea, { 0.1f, 0.1f, 0.1f, 1.f }); mGui.drawWrapper(*canvas); } private: WidgetManager mWidgetManager; - ScrollableWindow mGui; - ButtonWidget mButton; - LabelWidget mLabel; - NamedSliderWidget mSlider; + SimpleWidget2 mGui; }; int main() { diff --git a/Widgets/examples/SimpleGUI.hpp b/Widgets/examples/SimpleGUI.hpp new file mode 100644 index 0000000..5ced83e --- /dev/null +++ b/Widgets/examples/SimpleGUI.hpp @@ -0,0 +1,45 @@ +#include "Widgets.hpp" + +namespace tp { + + template + class SimpleWidget : public ScrollableWindow { + public: + SimpleWidget() { + this->addWidget(&mLabel); + this->addWidget(&mButton); + this->addWidget(&mSlider); + this->addWidget(&mCollapsableMenu); + + mCollapsableMenu.addWidgetToMenu(&mInMenuButton1); + mCollapsableMenu.addWidgetToMenu(&mInMenuButton2); + + mInMenuButton1.mLabel.mLabel = "Button1"; + mInMenuButton2.mLabel.mLabel = "Button2"; + + this->mArea.size = { 500, 500 }; + } + + private: + CollapsableMenu mCollapsableMenu; + ButtonWidget mInMenuButton1; + ButtonWidget mInMenuButton2; + + ButtonWidget mButton; + LabelWidget mLabel; + NamedSliderWidget mSlider; + }; + + template + class SimpleWidget2 : public Widget { + public: + SimpleWidget2() { + this->mChildWidgets.pushBack(&mFloating); + mFloating.addWidgetToMenu(&mWidget); + } + + private: + FloatingWidget mFloating; + SimpleWidget mWidget; + }; +} \ No newline at end of file diff --git a/Widgets/public/ButtonWidget.hpp b/Widgets/public/ButtonWidget.hpp index 9d64a6d..9281709 100644 --- a/Widgets/public/ButtonWidget.hpp +++ b/Widgets/public/ButtonWidget.hpp @@ -32,6 +32,10 @@ namespace tp { } } + void setLabel(const std::string& string) { + mLabel.mLabel = string; + } + public: void eventUpdateConfiguration(WidgetManager& wm) override { wm.setActiveId("Button"); diff --git a/Widgets/public/CollapsableMenu.hpp b/Widgets/public/CollapsableMenu.hpp new file mode 100644 index 0000000..3ba76f5 --- /dev/null +++ b/Widgets/public/CollapsableMenu.hpp @@ -0,0 +1,91 @@ +#pragma once + +#include "ScrollableWidget.hpp" +#include "ButtonWidget.hpp" + +namespace tp { + + template + class CollapsableMenu : public Widget { + public: + CollapsableMenu() { + this->mChildWidgets.pushBack(&mHeader); + this->mChildWidgets.pushBack(&mBody); + } + + void eventProcess(const Events&) override { + if (mHeader.isReleased()) { + mCollapsed = !mCollapsed; + } + + mHeader.setArea(getHeaderRect()); + + mBody.mEnable = !mCollapsed; + if (mCollapsed) { + this->mArea.size.y = headerHeight; + } else { + this->mArea.size.y = headerHeight + getBodyRect().size.y + mPadding * 2; + mBody.setArea(getBodyRect()); + } + } + + void eventDraw(Canvas& canvas) override { + canvas.rect(this->mArea, mBorderColor, rounding); + canvas.rect(this->mArea.shrink(mBorderSize), mMenuColor, rounding); + } + + public: + void addWidgetToMenu(Widget* widget) { + mBody.addWidget(widget); + } + + void setLabel(const std::string& string) { + mHeader.mLabel = string; + } + + private: + RectF getHeaderRect() { + RectF out = { this->mArea.pos, { this->mArea.size.x, headerHeight } }; + return out.shrink(mPadding); + } + + RectF getBodyRect() { + RectF out = { { this->mArea.pos.x, this->mArea.pos.y + headerHeight }, + { this->mArea.size.x, mBody.getContentSize() } + }; + + if (mBody.getContentSize()) { + out = out.shrink(mPadding); + out.size.y += mPadding * 2 + 1; + } + + return out; + } + + public: + void eventUpdateConfiguration(WidgetManager& wm) override { + wm.setActiveId("CollapsableMenu"); + + headerHeight = wm.getNumber("HeaderHeight", 35); + mMenuColor = wm.getColor("MenuColor", RGBA(0, 0, 0, 1.f)); + rounding = wm.getNumber("Rounding", "Rounding"); + mBorderColor = wm.getColor("BorderColor", RGBA(0.16, 0.16, 0.16, 1.f)); + mBorderSize = wm.getNumber("BorderSize", 2.f); + mPadding = wm.getNumber("Padding", "Padding"); + } + + protected: + ScrollableWindow mBody; + LabelWidget mHeader; + + RGBA mMenuColor; + RGBA mBorderColor; + + halnf headerHeight = 30; + halnf rounding = 0; + halnf mBorderSize = 0; + halnf mPadding = 0; + + bool mCollapsed = true; + }; +} \ No newline at end of file diff --git a/Widgets/public/FloatingWidget.hpp b/Widgets/public/FloatingWidget.hpp new file mode 100644 index 0000000..5c7065f --- /dev/null +++ b/Widgets/public/FloatingWidget.hpp @@ -0,0 +1,46 @@ +#pragma once + +#include "CollapsableMenu.hpp" + +namespace tp { + template + class FloatingWidget : public CollapsableMenu { + public: + FloatingWidget() { + this->mArea = { 0, 0, 300, 300 }; + } + + virtual void procWrapper(const Events& events, const RectF& parentArea) override { + if (!this->mEnable) return; + + this->checkVisibility(events, parentArea); + + if (!this->mVisible) return; + + this->checkFocus(events); + + this->checkClicked(events); + + mParentArea = parentArea; + this->eventProcess(events); + + for (auto child : this->mChildWidgets) { + child->procWrapper(events, this->getArea()); + } + } + + void eventProcess(const Events& events) override { + CollapsableMenu::eventProcess(events); + + if (this->mHeader.isHolding()) { + this->mArea.pos += events.getPointerDelta(); + if (!this->mArea.isEnclosedIn(mParentArea)) { + this->mArea.pos -= events.getPointerDelta(); + } + } + } + + RectF mParentArea; + + }; +} \ No newline at end of file diff --git a/Widgets/public/LabelWidget.hpp b/Widgets/public/LabelWidget.hpp index 91322b3..fd3630d 100644 --- a/Widgets/public/LabelWidget.hpp +++ b/Widgets/public/LabelWidget.hpp @@ -6,10 +6,12 @@ namespace tp { template class LabelWidget : public Widget { public: - LabelWidget() = default; + LabelWidget() { + this->mArea = { 0, 0, 100, 30 }; + } void eventDraw(Canvas& canvas) override { - canvas.text(mLabel.c_str(), this->mArea, fontSize, Canvas::CC, padding, fontColor); + canvas.text(mLabel.c_str(), this->mArea, fontSize, Canvas::LC, padding, fontColor); } public: diff --git a/Widgets/public/ScrollableWidget.hpp b/Widgets/public/ScrollableWidget.hpp index a7d71fc..31a7351 100644 --- a/Widgets/public/ScrollableWidget.hpp +++ b/Widgets/public/ScrollableWidget.hpp @@ -131,7 +131,7 @@ namespace tp { this->mChildWidgets.pushBack(&mContentWidget); } - ~ScrollableWindow() = default; + virtual ~ScrollableWindow() = default; // takes whole area void eventProcess(const Events& events) override { @@ -154,7 +154,7 @@ namespace tp { for (auto widget : content) { widget->setArea({ mContentWidget.mArea.x + padding, - widget->mArea.y, + mContentWidget.mArea.y + widget->mArea.y, mScroller.getViewport().z - padding * 2, widget->mArea.w }); } @@ -169,23 +169,30 @@ namespace tp { mPadding = wm.getNumber("Padding", "Padding"); } + [[nodiscard]] halnf getContentSize() const { + return mContentSize; + } + private: void updateContents(List*>& contentWidgets) { - if (contentWidgets.size()) { - const halnf offset = contentWidgets.first()->mArea.y + mPadding; + if (!contentWidgets.size()) { + return; + } - halnf start = 0; - for (auto widget : contentWidgets) { - widget->mArea.y = start; - start += widget->mArea.w + mPadding; - } + const halnf offset = contentWidgets.first()->mArea.y + mPadding; - for (auto widget : contentWidgets) { - widget->mArea.y += offset; - } + halnf start = 0; + for (auto widget : contentWidgets) { + widget->mArea.y = start; + start += widget->mArea.w + mPadding; + } + + for (auto widget : contentWidgets) { + widget->mArea.y += offset; } } + // ready content size void updateContentSize(List*>& contentWidgets) { mContentSize = 0; if (contentWidgets.size()) { diff --git a/Widgets/public/Widgets.hpp b/Widgets/public/Widgets.hpp index 95d35b8..7fa4b09 100644 --- a/Widgets/public/Widgets.hpp +++ b/Widgets/public/Widgets.hpp @@ -6,4 +6,6 @@ #include "SplitViewWidget.hpp" #include "TextInputWidget.hpp" #include "SliderWidget.hpp" +#include "CollapsableMenu.hpp" +#include "FloatingWidget.hpp" #include "Animations.hpp" \ No newline at end of file