Compare commits

...

8 commits

Author SHA1 Message Date
IlyaShurupov
2de6bc82b7 fix bug in update manager 2024-11-05 21:28:59 +03:00
IlyaShurupov
b5e490a0f8 focus locking stack 2024-11-05 20:32:59 +03:00
IlyaShurupov
1bbce78a2e fix focus locking bug 2024-11-05 19:36:45 +03:00
IlyaShurupov
c82ad3c150 Debug Tools and Scrollable widget 2024-11-05 19:36:20 +03:00
IlyaShurupov
402a4fdebd fix focus locking 2024-11-05 19:34:28 +03:00
IlyaShurupov
fdaab48158 3D Editor treaks 2024-11-05 12:26:48 +03:00
IlyaShurupov
60a15fad8b Raster render updates 2024-11-04 22:54:10 +03:00
IlyaShurupov
e636394952 Tunes 2024-11-04 21:54:18 +03:00
47 changed files with 429 additions and 182 deletions

View file

@ -19,5 +19,6 @@ file(GLOB APP_SOURCES "./applications/*.cpp")
add_executable(3DEditorApp ${APP_SOURCES}) add_executable(3DEditorApp ${APP_SOURCES})
target_link_libraries(3DEditorApp ${PROJECT_NAME}) target_link_libraries(3DEditorApp ${PROJECT_NAME})
file(COPY "rsc" DESTINATION "${CMAKE_BINARY_DIR}/${PROJECT_NAME}/") file(COPY "../RasterRender/rsc" DESTINATION "${CMAKE_BINARY_DIR}/${PROJECT_NAME}/")
file(COPY "rsc/Font.ttf" DESTINATION "${CMAKE_BINARY_DIR}/${PROJECT_NAME}/") file(COPY "../Graphics/rsc" DESTINATION "${CMAKE_BINARY_DIR}/${PROJECT_NAME}/")
file(COPY "../3DScene/rsc" DESTINATION "${CMAKE_BINARY_DIR}/${PROJECT_NAME}/")

View file

@ -24,7 +24,10 @@ Editor::Editor() {
void Editor::renderViewport() { void Editor::renderViewport() {
switch (mRenderType) { switch (mRenderType) {
case RenderType::RASTER: case RenderType::RASTER:
mRasterRenderer.render(mScene, mScene.mRenderSettings.size); mRasterRenderer.beginRender(mScene, mScene.mRenderSettings.size);
mRasterRenderer.renderDefault(mScene);
if (mActive) mRasterRenderer.renderOutline(mScene.mCamera, *mActive);
mRasterRenderer.endRender();
break; break;
case RenderType::PATH_TRACER: case RenderType::PATH_TRACER:
@ -54,6 +57,7 @@ uint4 Editor::getViewportTexID() {
void Editor::loadDefaults() { void Editor::loadDefaults() {
mScene.load("rsc/scene/script.lua"); mScene.load("rsc/scene/script.lua");
mResetCamera = mScene.mCamera;
} }
void Editor::setViewportSize(const Vec2F& size) { void Editor::setViewportSize(const Vec2F& size) {
@ -73,6 +77,7 @@ Editor::~Editor() {
} }
void Editor::renderPathFrame() { void Editor::renderPathFrame() {
mScene.updateCache();
mPathRenderer.render(mScene, mPathTracerBuffers, mScene.mRenderSettings); mPathRenderer.render(mScene, mPathTracerBuffers, mScene.mRenderSettings);
sendBuffersToGPU(); sendBuffersToGPU();
} }
@ -116,5 +121,20 @@ void Editor::navigationZoom(halnf factor) {
} }
void Editor::navigationReset() { void Editor::navigationReset() {
mScene.mCamera.lookAtPoint({ 0, 0, 0 }, { 1, 5, 1 }, { 0, 0, 1 }); mScene.mCamera = mResetCamera;
// mScene.mCamera.lookAtPoint({ 0, 0, 0 }, { 1, 5, 1 }, { 0, 0, 1 });
} }
void Editor::selectObject(const Vec2F& screenPos) {
mScene.updateCache();
auto dir = (mScene.mCamera.project(screenPos) - mScene.mCamera.getPos()).normalize();
Ray ray = Ray( dir, mScene.mCamera.getPos() );
auto rayCastData = mScene.castRay(ray, 1000);
mActive = rayCastData.obj;
}
Object* Editor::getActiveObject() { return mActive; }
Scene* Editor::getScene() { return &mScene; }

View file

@ -28,6 +28,10 @@ namespace tp {
void navigationZoom(halnf factor); void navigationZoom(halnf factor);
void navigationReset(); void navigationReset();
void selectObject(const Vec2F& screenPos);
Object* getActiveObject();
Scene* getScene();
private: private:
void sendBuffersToGPU(); void sendBuffersToGPU();
@ -36,6 +40,8 @@ namespace tp {
private: private:
Scene mScene; Scene mScene;
Camera mResetCamera;
RenderType mRenderType = RenderType::RASTER; RenderType mRenderType = RenderType::RASTER;
RasterRender mRasterRenderer; RasterRender mRasterRenderer;
@ -43,5 +49,7 @@ namespace tp {
RayTracer::OutputBuffers mPathTracerBuffers; RayTracer::OutputBuffers mPathTracerBuffers;
uint4 mPathRenderTexture = 0; uint4 mPathRenderTexture = 0;
Object* mActive = nullptr;
}; };
} }

View file

@ -66,11 +66,13 @@ namespace tp {
mNavigationOrbit.setText("Orbit"); mNavigationOrbit.setText("Orbit");
mNavigationZoom.setText("Zoom"); mNavigationZoom.setText("Zoom");
mNavigationReset.setText("Reset"); mNavigationReset.setText("Reset");
mNavigationSelect.setText("Select");
mNavigationMenu.addToMenu(&mNavigationPan); mNavigationMenu.addToMenu(&mNavigationPan);
mNavigationMenu.addToMenu(&mNavigationOrbit); mNavigationMenu.addToMenu(&mNavigationOrbit);
mNavigationMenu.addToMenu(&mNavigationZoom); mNavigationMenu.addToMenu(&mNavigationZoom);
mNavigationMenu.addToMenu(&mNavigationReset); mNavigationMenu.addToMenu(&mNavigationReset);
mNavigationMenu.addToMenu(&mNavigationSelect);
mNavigationMenu.setText("Navigation"); mNavigationMenu.setText("Navigation");
} }
@ -85,30 +87,39 @@ namespace tp {
mNavigationOrbit.setAction( [this]() { mNavigationType = ORBIT; }); mNavigationOrbit.setAction( [this]() { mNavigationType = ORBIT; });
mNavigationPan.setAction( [this]() { mNavigationType = PAN; }); mNavigationPan.setAction( [this]() { mNavigationType = PAN; });
mNavigationZoom.setAction( [this](){ mNavigationType = ZOOM; }); mNavigationZoom.setAction( [this](){ mNavigationType = ZOOM; });
mNavigationSelect.setAction( [this](){ mNavigationType = SELECT; });
mNavigationReset.setAction( [this]() { mEditor->navigationReset(); }); mNavigationReset.setAction( [this]() { mEditor->navigationReset(); });
} }
void process(const EventHandler& events) override { void process(const EventHandler& events) override {
DockWidget::process(events); DockWidget::process(events);
auto pointer = events.getPointer(); if (auto obj = mEditor->getActiveObject()) { // dummy rotate active object
auto pointerPrev = events.getPointerPrev(); auto rotator = obj->mTopology.Basis.rotatorDir({1, 1, 1}, 0.1);
obj->mTopology.Basis = rotator * obj->mTopology.Basis;
}
mEditor->getScene()->updateCache();
const auto& activeArea = mViewport.getArea(); const auto& activeArea = mViewport.getArea();
auto pointer = events.getPointer();
auto pointerPrev = events.getPointerPrev();
auto pointerRelative = (((pointer - activeArea.pos) / activeArea.size) - 0.5f) * 2;
if (activeArea.isInside(pointer) && events.isDown(InputID::MOUSE1)) { if (activeArea.isInside(pointer) && events.isDown(InputID::MOUSE1)) {
switch (mNavigationType) { switch (mNavigationType) {
case ORBIT: mEditor->navigationOrbit(events.getPointerDelta() / activeArea.size * 3); break; case ORBIT: mEditor->navigationOrbit(events.getPointerDelta() / activeArea.size * 3); break;
case PAN: case PAN:
{ {
auto pointerRelative = (((pointer - activeArea.pos) / activeArea.size) - 0.5f) * 2;
auto prevPointerRelative = (((pointerPrev - activeArea.pos) / activeArea.size) - 0.5f) * 2; auto prevPointerRelative = (((pointerPrev - activeArea.pos) / activeArea.size) - 0.5f) * 2;
mEditor->navigationPan(prevPointerRelative, pointerRelative); mEditor->navigationPan(prevPointerRelative, pointerRelative);
} }
break; break;
case ZOOM: mEditor->navigationZoom(1 + (events.getPointerDelta().y / activeArea.size.y)); break; case ZOOM: mEditor->navigationZoom(1 + (events.getPointerDelta().y / activeArea.size.y)); break;
case SELECT: mEditor->selectObject(pointerRelative * -1); break;
} }
} }
} }
@ -129,13 +140,14 @@ namespace tp {
ButtonWidget mRenderDeNoise; ButtonWidget mRenderDeNoise;
// Navigation // Navigation
enum NavigationType { ORBIT, PAN, ZOOM } mNavigationType = ORBIT; enum NavigationType { SELECT, ORBIT, PAN, ZOOM } mNavigationType = ORBIT;
FloatingMenu mNavigationMenu; FloatingMenu mNavigationMenu;
ButtonWidget mNavigationPan; ButtonWidget mNavigationPan;
ButtonWidget mNavigationOrbit; ButtonWidget mNavigationOrbit;
ButtonWidget mNavigationZoom; ButtonWidget mNavigationZoom;
ButtonWidget mNavigationReset; ButtonWidget mNavigationReset;
ButtonWidget mNavigationSelect;
RGBA mBaseColor; RGBA mBaseColor;
}; };

View file

@ -1,11 +0,0 @@
#version 330 core
layout(location = 0) in vec3 Point;
uniform vec4 Origin;
uniform mat4 Basis;
uniform mat4 Camera;
void main() {
gl_Position = Camera * vec4(Point.xyz, 1.0);
}

View file

@ -7,6 +7,61 @@ extern "C" {
} }
#include <filesystem> #include <filesystem>
#include <utility>
tp::Vec3F tp::Scene::getVec3(lua_State* state, const char* name) {
int stackSize = lua_gettop(state);
// Access the "pos" field and validate it
lua_getfield(state, -1, name);
if (!lua_istable(state, -1) || lua_rawlen(state, -1) != 3) {
throw IOError(std::string("Invalid vec3 named ") + name);
}
// Read the values from the table
float pos[3];
for (int i = 0; i < 3; i++) {
lua_rawgeti(state, -1, i + 1);
if (lua_isnumber(state, -1)) {
pos[i] = lua_tonumber(state, -1);
} else {
throw IOError("vec3 values must be numbers");
}
lua_pop(state, 1); // pop number
}
lua_pop(state, 1); // pop vec3
ASSERT(stackSize == lua_gettop(state))
return { pos[0], pos[1], pos[2] };
}
tp::Vec2F tp::Scene::getVec2(lua_State* state, const char* name) {
int stackSize = lua_gettop(state);
// Access the "pos" field and validate it
lua_getfield(state, -1, name);
if (!lua_istable(state, -1) || lua_rawlen(state, -1) != 2) {
throw IOError(std::string("Invalid vec2 named ") + name);
}
// Read the values from the table
float pos[2];
for (int i = 0; i < 2; i++) {
lua_rawgeti(state, -1, i + 1);
if (lua_isnumber(state, -1)) {
pos[i] = lua_tonumber(state, -1);
} else {
throw IOError("vec3 values must be numbers");
}
lua_pop(state, 1); // pop number
}
lua_pop(state, 1); // pop vec3
ASSERT(stackSize == lua_gettop(state))
return { pos[0], pos[1] };
}
// Function to read a Lua table representing RenderSettings // Function to read a Lua table representing RenderSettings
int readRenderSettings(lua_State* L, tp::RenderSettings& settings) { int readRenderSettings(lua_State* L, tp::RenderSettings& settings) {
@ -53,23 +108,8 @@ int readRenderSettings(lua_State* L, tp::RenderSettings& settings) {
} }
// Function to read a Lua table representing a light // Function to read a Lua table representing a light
int readLight(lua_State* L, tp::PointLight* light) { int tp::Scene::readLight(lua_State* L, tp::PointLight* light) {
lua_getfield(L, -1, "pos"); // Get the "pos" field from the light table light->pos = getVec3(L, "pos");
if (!lua_istable(L, -1)) {
printf("Light is missing the 'pos' table.\n");
return 0; // Error
}
for (int i = 0; i < 3; i++) {
lua_rawgeti(L, -1, i + 1); // Index is 1-based in Lua
if (!lua_isnumber(L, -1)) {
printf("Light 'pos' field is not a number at index %d.\n", i);
lua_pop(L, 2); // Pop both the number and the 'pos' table
return 0; // Error
}
light->pos[i] = lua_tonumber(L, -1);
lua_pop(L, 1); // Pop the number
}
lua_pop(L, 1); // Pop the 'pos' table
lua_getfield(L, -1, "intensity"); // Get the "intensity" field from the light table lua_getfield(L, -1, "intensity"); // Get the "intensity" field from the light table
if (!lua_isnumber(L, -1)) { if (!lua_isnumber(L, -1)) {
@ -121,8 +161,7 @@ bool tp::Scene::loadLuaFormat(const std::string& scenePath) {
} }
// --- camera // --- camera
{
// Access Camera table
lua_getglobal(L, "Camera"); lua_getglobal(L, "Camera");
if (!lua_istable(L, -1)) { if (!lua_istable(L, -1)) {
printf("Camera is not a table.\n"); printf("Camera is not a table.\n");
@ -130,56 +169,20 @@ bool tp::Scene::loadLuaFormat(const std::string& scenePath) {
return false; return false;
} }
// Verify you are inside the "Camera" table Vec3F camPos = getVec3(L, "pos");
int cameraTableIndex = lua_gettop(L); // Get the index of the "Camera" table Vec3F camTarget = getVec3(L, "target");
Vec3F camUp = getVec3(L, "up");
Vec2F camSize = getVec2(L, "size");
// Access the "pos" field and validate it mRenderSettings.size = camSize;
lua_getfield(L, cameraTableIndex, "pos");
if (!lua_istable(L, -1) || lua_rawlen(L, -1) != 3) {
printf("Invalid 'pos' field in Camera table.\n");
lua_close(L);
return false;
}
// Read the values from the table mCamera.lookAtPoint(camTarget, camPos, camUp);
float pos[3];
for (int i = 0; i < 3; i++) {
lua_rawgeti(L, -1, i + 1);
if (lua_isnumber(L, -1)) {
pos[i] = lua_tonumber(L, -1);
} else {
printf("Invalid 'pos' field value at index %d.\n", i + 1);
lua_close(L);
return false;
}
lua_pop(L, 1);
}
// Access the "size_x" field and validate it
lua_getfield(L, cameraTableIndex, "size_x");
if (!lua_isnumber(L, -1)) {
printf("Invalid or missing 'size_x' field in Camera table.\n");
lua_close(L);
return false;
}
int size_x = lua_tointeger(L, -1);
lua_pop(L, 1); // Pop the 'size_x' value from the stack
// Access the "size_y" field and validate it
lua_getfield(L, cameraTableIndex, "size_y");
if (!lua_isnumber(L, -1)) {
printf("Invalid or missing 'size_y' field in Camera table.\n");
lua_close(L);
return false;
}
int size_y = lua_tointeger(L, -1);
mRenderSettings.size = { (tp::halnf) size_x, (tp::halnf) size_y };
mCamera.lookAtPoint({ 0, 0, 0 }, { pos[0], pos[1], pos[2] }, { 0, 0, 1 });
mCamera.setFOV(3.14 / 4); mCamera.setFOV(3.14 / 4);
mCamera.setFar(100); mCamera.setFar(100);
mCamera.setRatio((tp::halnf) size_y / (tp::halnf) size_x); mCamera.setRatio((tp::halnf) camSize.y / (tp::halnf) camSize.x);
lua_pop(L, 1);
}
// ---------- LIGHTS // ---------- LIGHTS
{ {

View file

@ -49,3 +49,38 @@ bool tp::Scene::loadOBJFormat(const std::string& objetsPath) {
return mObjects.size(); return mObjects.size();
} }
const tp::RayCastData& tp::Scene::castRay(const Ray& ray, alnf farVal) {
auto& out = mRayCastData;
out.hit = false;
out.obj = nullptr;
farVal *= farVal;
for (auto obj : mObjects) {
for (auto trig : obj->mCache.TrigCaches) {
if (trig->castRay(ray)) {
auto dist = (TrigCache::getHitPos() - ray.pos).length2();
if (farVal > dist && dist > EPSILON) {
out.trig = &trig.data();
out.hitPos = TrigCache::getHitPos();
out.obj = &obj.data();
out.hit = true;
farVal = dist;
}
}
}
}
return mRayCastData;
}
void tp::Scene::updateCache() {
for (auto obj : mObjects) {
obj->mCache.updateCache();
}
}

View file

@ -2,5 +2,13 @@
#include "Scene.hpp" #include "Scene.hpp"
bool tp::Scene::load(const std::string& scenePath) { bool tp::Scene::load(const std::string& scenePath) {
return loadLuaFormat(scenePath); try {
auto res = loadLuaFormat(scenePath);
if (!res) throw IOError("Failed loading lua script");
} catch (const IOError& err) {
printf("Failed loading scene : %s\n", err.description.c_str());
return false;
}
return true;
} }

View file

@ -6,6 +6,8 @@
#include <string> #include <string>
struct lua_State;
namespace tp { namespace tp {
struct RenderSettings { struct RenderSettings {
uhalni depth = 2; uhalni depth = 2;
@ -42,7 +44,22 @@ namespace tp {
halnf intensity = 1.f; halnf intensity = 1.f;
}; };
struct RayCastData {
Object* obj = nullptr;
TrigCache* trig = nullptr;
Vec3F hitPos = { 0, 0, 0 };
bool hit = false;
bool inv = false;
};
class Scene { class Scene {
struct IOError : public std::exception {
explicit IOError(std::string in) :
description(std::move(in)) {}
std::string description;
};
public: public:
Scene() = default; Scene() = default;
@ -51,11 +68,23 @@ namespace tp {
bool loadLuaFormat(const std::string& scenePath); bool loadLuaFormat(const std::string& scenePath);
bool loadOBJFormat(const std::string& objectsPath); bool loadOBJFormat(const std::string& objectsPath);
const RayCastData& castRay(const Ray& ray, alnf farVal);
void updateCache();
private:
Vec3F getVec3(lua_State* state, const char* name);
Vec2F getVec2(lua_State* state, const char* name);
int readLight(lua_State* L, tp::PointLight* light);
public: public:
Buffer<Object> mObjects; Buffer<Object> mObjects;
Buffer<PointLight> mLights; Buffer<PointLight> mLights;
Camera mCamera; Camera mCamera;
RenderSettings mRenderSettings; RenderSettings mRenderSettings;
RayCastData mRayCastData;
}; };
} }

View file

@ -2,9 +2,10 @@
Meshes = "meshes.obj" Meshes = "meshes.obj"
Camera = { Camera = {
pos = { 0.5, 4.5, 0.2 }, pos = { 0, 5, 0 },
size_x = 600, target = { 0, 0, 0 },
size_y = 800, up = { 0, 0, 1 },
size = { 600, 800 },
} }
Lights = { Lights = {

View file

@ -19,5 +19,5 @@ target_link_libraries(Example${PROJECT_NAME} ${PROJECT_NAME} ${BINDINGS_LIBS})
target_include_directories(Example${PROJECT_NAME} PRIVATE ${BINDINGS_INCLUDE}) target_include_directories(Example${PROJECT_NAME} PRIVATE ${BINDINGS_INCLUDE})
file(COPY "examples/Font.ttf" DESTINATION "${CMAKE_BINARY_DIR}/${PROJECT_NAME}/") file(COPY "../Graphics/rsc" DESTINATION "${CMAKE_BINARY_DIR}/${PROJECT_NAME}/")

Binary file not shown.

View file

@ -27,7 +27,7 @@ Canvas::Canvas(Window* window) {
mContext->vg = nvgCreateGL3(NVG_ANTIALIAS | NVG_STENCIL_STROKES); mContext->vg = nvgCreateGL3(NVG_ANTIALIAS | NVG_STENCIL_STROKES);
if (nvgCreateFont(mContext->vg, "default", "Font.ttf") == -1) { if (nvgCreateFont(mContext->vg, "default", "rsc/Font.ttf") == -1) {
ASSERT(!"Cant create NVG font") ASSERT(!"Cant create NVG font")
} }
} }

View file

@ -36,7 +36,7 @@ DebugGUI::DebugGUI(Window* window) {
ImGui_ImplOpenGL3_Init("#version 330"); ImGui_ImplOpenGL3_Init("#version 330");
ImGuiIO& io = ImGui::GetIO(); ImGuiIO& io = ImGui::GetIO();
io.Fonts->AddFontFromFileTTF("Font.ttf", 20.f); io.Fonts->AddFontFromFileTTF("rsc/Font.ttf", 20.f);
io.ConfigInputTrickleEventQueue = false; io.ConfigInputTrickleEventQueue = false;

View file

@ -87,6 +87,7 @@ namespace tp {
[[nodiscard]] halnf getPointerPressure() const; [[nodiscard]] halnf getPointerPressure() const;
void setEnableKeyEvents(bool); void setEnableKeyEvents(bool);
[[nodiscard]] bool isKeyEventsEnabled() const { return mEnableKeyEvents; }
private: private:
void processEventUnguarded(); void processEventUnguarded();

View file

@ -88,7 +88,7 @@ void Camera::lookAtPoint(const Vec3F& aTarget, const Vec3F& aPos, Vec3F aUp) {
} }
mPos = aPos; mPos = aPos;
mTarget = aTarget; mTarget = aTarget;
Vec3F f = (mPos - mTarget).normalize(); Vec3F f = (mTarget - mPos).normalize();
mUp = f * (aUp.normalize() * f); mUp = f * (aUp.normalize() * f);
} }
@ -117,16 +117,20 @@ void Camera::rotate(halnf angleX, halnf angleY) {
Vec3F wup(0, 0, 1); Vec3F wup(0, 0, 1);
mPos -= mTarget; mPos -= mTarget;
mat3f rotZ = mat3f::rotatorDir(wup, angleX); Mat3F rotZ = Mat3F::rotatorDir(wup, angleX);
mPos = rotZ * mPos; mPos = rotZ * mPos;
mUp = rotZ * mUp; mUp = rotZ * mUp;
Vec3F f = mPos.unitV(); Vec3F f = mPos.unitV();
Vec3F s = mUp * f; Vec3F s = mUp * f;
mPos = mat3f::rotatorDir(s, -angleY) * mPos; mPos = Mat3F::rotatorDir(s, -angleY) * mPos;
mPos += mTarget; mPos += mTarget;
lookAtPoint(mTarget, mPos, mUp); lookAtPoint(mTarget, mPos, mUp);
} }
void Camera::setPos(Vec3F pos) {
mPos = pos;
}

View file

@ -18,6 +18,7 @@ namespace tp {
void setRatio(halnf ratio); void setRatio(halnf ratio);
void setFOV(halnf fov); void setFOV(halnf fov);
void setFar(halnf far); void setFar(halnf far);
void setPos(Vec3F pos);
[[nodiscard]] const Vec3F& getPos() const; [[nodiscard]] const Vec3F& getPos() const;
[[nodiscard]] const Vec3F& getTarget() const; [[nodiscard]] const Vec3F& getTarget() const;

View file

@ -465,7 +465,7 @@ namespace tp {
} }
// Matrix Properties // Matrix Properties
MVec transform(const MVec& in) const { return MVec(i.x * in.x + i.y * in.y, j.x * in.y + j.y * in.x); } MVec transform(const MVec& in) const { return MVec(i.x * in.x + i.y * in.y, j.x * in.x + j.y * in.y); }
Mat transform(const Mat& in) const { Mat transform(const Mat& in) const {
Mat out; Mat out;
@ -495,8 +495,8 @@ namespace tp {
}; };
template <typename Type> template <typename Type>
using mat3 = Mat<Type, 3, 3>; using Mat3 = Mat<Type, 3, 3>;
using mat3f = mat3<halnf>; using Mat3F = Mat3<halnf>;
template <typename Type> template <typename Type>
class Mat<Type, 3, 3> { class Mat<Type, 3, 3> {
@ -647,22 +647,22 @@ namespace tp {
Mat inv() { return cofactors() /= det(); } Mat inv() { return cofactors() /= det(); }
Mat rotatorX(alnf angle) { Mat rotatorX(alnf angle) const {
alnf cosA = (alnf) cos(angle); alnf cosA = (alnf) cos(angle);
alnf sinA = (alnf) sin(angle); alnf sinA = (alnf) sin(angle);
return { { 1, 0, 0 }, { 0, cosA, -sinA }, { 0, sinA, cosA } }; return { { 1, 0, 0 }, { 0, cosA, -sinA }, { 0, sinA, cosA } };
} }
Mat rotatorY(alnf angle) { Mat rotatorY(alnf angle) const {
alnf cosA = (alnf) cos(angle); alnf cosA = (alnf) cos(angle);
alnf sinA = (alnf) sin(angle); alnf sinA = (alnf) sin(angle);
return { { cosA, 0, sinA }, { 0, 1, 0 }, { -sinA, 0, cosA } }; return { { cosA, 0, sinA }, { 0, 1, 0 }, { -sinA, 0, cosA } };
} }
Mat rotatorZ(alnf angle) { static Mat rotatorZ(alnf angle) {
alnf cosA = (alnf) cos(angle); Type cosA = (Type) cos(angle);
alnf sinA = (alnf) sin(angle); Type sinA = (Type) sin(angle);
return { { cosA, -sinA, 0 }, { sinA, cosA, 0 }, { 0, 0, 1 } }; return { vec{ cosA, -sinA, 0 }, vec{ sinA, cosA, 0 }, vec{ 0, 0, 1 } };
} }
static Mat rotatorDir(vec dir, alnf angle) { static Mat rotatorDir(vec dir, alnf angle) {
@ -707,4 +707,14 @@ namespace tp {
Type det() { return Type(); } Type det() { return Type(); }
}; };
template<typename Type>
Mat<Type, 4, 4> toMat4(const Mat<Type, 3, 3>& in) {
Mat<Type, 4, 4> out;
out[0] = { in[0][0], in[0][1], in[0][2], 0 };
out[1] = { in[1][0], in[1][1], in[1][2], 0 };
out[2] = { in[2][0], in[2][1], in[2][2], 0 };
out[3] = { Type(0), Type(0), Type(0), Type(1) };
return out;
}
} }

View file

@ -30,7 +30,7 @@ namespace tp {
struct Topology { struct Topology {
Vec3F Origin = { 0, 0, 0 }; Vec3F Origin = { 0, 0, 0 };
mat3f Basis = { { 1, 0, 0 }, { 0, 1, 0 }, { 0, 0, 1 } }; Mat3F Basis = { { 1, 0, 0 }, { 0, 1, 0 }, { 0, 0, 1 } };
Buffer<Vec3F> Points; Buffer<Vec3F> Points;
Buffer<Vec3F> Normals; Buffer<Vec3F> Normals;

View file

@ -110,6 +110,13 @@ namespace tp {
return *this; return *this;
} }
Vec& operator=(Type val) {
for (ualni i = 0; i < tSize; i++) {
get(i) = val;
}
return *this;
}
void assign(Type val) { void assign(Type val) {
for (ualni i = 0; i < tSize; i++) { for (ualni i = 0; i < tSize; i++) {
get(i) = val; get(i) = val;
@ -576,4 +583,14 @@ namespace tp {
using Vec4F = Vec4<halnf>; using Vec4F = Vec4<halnf>;
using Vec4I = Vec4<halni>; using Vec4I = Vec4<halni>;
template <typename Type>
Vec<Type, 4> toVec4(const Vec<Type, 3>& in) {
Vec<Type, 4> out;
out[0] = in[0];
out[1] = in[1];
out[2] = in[2];
out[3] = 0;
return out;
}
} }

View file

@ -102,7 +102,7 @@ void RenderBuffer::setViewport(const RectF& viewport) {
void RenderBuffer::clear() { void RenderBuffer::clear() {
AssertGL(glClearColor(mClearCol.r, mClearCol.g, mClearCol.b, mClearCol.a)); AssertGL(glClearColor(mClearCol.r, mClearCol.g, mClearCol.b, mClearCol.a));
AssertGL(glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)); AssertGL(glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT));
} }
void RenderBuffer::endDraw() { void RenderBuffer::endDraw() {

View file

@ -3,26 +3,42 @@
using namespace tp; using namespace tp;
RasterRender::RasterRender() : RasterRender::RasterRender() {
mRenderBuffer({ 100, 100 })
{
mDefaultShader.load("rsc/shaders/default.vert", nullptr, "rsc/shaders/default.frag", true); mDefaultShader.load("rsc/shaders/default.vert", nullptr, "rsc/shaders/default.frag", true);
mSolidShader.load("rsc/shaders/default.vert", nullptr, "rsc/shaders/solid.frag", true);
} }
RasterRender::~RasterRender() {} RasterRender::~RasterRender() {}
void RasterRender::resize(const Vec2<tp::ualni>& size) {
auto sizeF = Vec2F(size.x, size.y);
mRenderBuffer.resize(sizeF);
mTempBuffer.resize(sizeF);
}
uint4 RasterRender::getRenderBufferID() { return mRenderBuffer.texId(); } uint4 RasterRender::getRenderBufferID() { return mRenderBuffer.texId(); }
Vec2F RasterRender::getBufferSize() { return mRenderBuffer.getSize(); } Vec2F RasterRender::getBufferSize() { return mRenderBuffer.getSize(); }
void RasterRender::render(const Scene& geometry, const Vec2<ualni>& size) { void RasterRender::bindCameraShaderAttributes(const Mat4F& cameraMat) {
static auto camera = (GLint) mDefaultShader.getu("Camera");
glUniformMatrix4fv(camera, 1, true, &cameraMat[0][0]);
}
for (auto object : geometry.mObjects) { void RasterRender::bindObjectShaderAttributes(const Object& object) {
if (!object->mGUPBuffers) { static auto origin = (GLint) mDefaultShader.getu("Origin");
object->mGUPBuffers = new ObjectBuffers(&object.data()); static auto basis = (GLint) mDefaultShader.getu("Basis");
}
}
Mat4F basisMat = toMat4(object.mTopology.Basis);
Vec4F originPoint = toVec4(object.mTopology.Origin);
basisMat = basisMat.transpose();
glUniform4fv(origin, 1, &originPoint[0]);
glUniformMatrix4fv(basis, 1, false, &basisMat[0][0]);
}
void RasterRender::renderDefault(const Scene& geometry) {
mRenderBuffer.mClearCol = { 0.0f, 0.0f, 0.0f, 0.f }; mRenderBuffer.mClearCol = { 0.0f, 0.0f, 0.0f, 0.f };
mRenderBuffer.beginDraw(); mRenderBuffer.beginDraw();
@ -37,24 +53,21 @@ void RasterRender::render(const Scene& geometry, const Vec2<ualni>& size) {
// glPolygonMode(GL_FRONT, GL_LINE); // glPolygonMode(GL_FRONT, GL_LINE);
// glPolygonMode(GL_BACK, GL_LINE); // glPolygonMode(GL_BACK, GL_LINE);
// auto rotator = Mat3F::rotatorZ(alnf(0.001));
bindCameraShaderAttributes(cameraMat);
for (auto object : geometry.mObjects) { for (auto object : geometry.mObjects) {
// object->mTopology.Origin += 0.01;
// object->mTopology.Basis = rotator * object->mTopology.Basis;
static auto origin = (GLint) mDefaultShader.getu("Origin"); bindObjectShaderAttributes(object.data());
static auto basis = (GLint) mDefaultShader.getu("Basis");
static auto camera = (GLint) mDefaultShader.getu("Camera");
Mat4F basisMat;
Vec4F originPoint;
glUniform4fv(origin, 1, &originPoint[0]);
glUniformMatrix4fv(basis, 1, false, &basisMat[0][0]);
glUniformMatrix4fv(camera, 1, true, &cameraMat[0][0]);
object->mGUPBuffers->drawCall(); object->mGUPBuffers->drawCall();
} }
mDefaultShader.unbind(); mDefaultShader.unbind();
glDisable(GL_DEPTH_TEST);
// glPolygonMode(GL_FRONT, GL_FILL); // glPolygonMode(GL_FRONT, GL_FILL);
// glPolygonMode(GL_BACK, GL_FILL); // glPolygonMode(GL_BACK, GL_FILL);
@ -62,4 +75,37 @@ void RasterRender::render(const Scene& geometry, const Vec2<ualni>& size) {
mRenderBuffer.endDraw(); mRenderBuffer.endDraw();
} }
void RasterRender::renderOutline(const Camera& camera, const Object& object) {
mRenderBuffer.beginDraw();
// glEnable(GL_STENCIL_TEST);
mSolidShader.bind();
bindCameraShaderAttributes(camera.calculateTransformationMatrix());
bindObjectShaderAttributes(object);
object.mGUPBuffers->drawCall();
mSolidShader.unbind();
// glDisable(GL_STENCIL_TEST);
mRenderBuffer.endDraw();
}
void RasterRender::beginRender(const Scene& geometry, const Vec2<tp::ualni>& size) {
resize(size);
for (auto object : geometry.mObjects) {
if (!object->mGUPBuffers) {
object->mGUPBuffers = new ObjectBuffers(&object.data());
}
}
}
void RasterRender::endRender() {
// pass
}
RenderBuffer* RasterRender::getRenderBuffer() { return &mRenderBuffer; } RenderBuffer* RasterRender::getRenderBuffer() { return &mRenderBuffer; }

View file

@ -7,7 +7,7 @@
namespace tp { namespace tp {
class RenderBuffer { class RenderBuffer {
public: public:
explicit RenderBuffer(const Vec2F& size); explicit RenderBuffer(const Vec2F& size = { 10, 10 });
RenderBuffer(const Vec2F& size, tp::uint1 samples); RenderBuffer(const Vec2F& size, tp::uint1 samples);
~RenderBuffer(); ~RenderBuffer();

View file

@ -12,13 +12,27 @@ namespace tp {
RasterRender(); RasterRender();
~RasterRender(); ~RasterRender();
void render(const Scene& geometry, const Vec2<ualni>& size); void beginRender(const Scene& geometry, const Vec2<ualni>& size);
void endRender();
void renderDefault(const Scene& geometry);
void renderOutline(const Camera& camera, const Object& object);
void resize(const Vec2<ualni>& size);
uint4 getRenderBufferID(); uint4 getRenderBufferID();
RenderBuffer* getRenderBuffer(); RenderBuffer* getRenderBuffer();
Vec2F getBufferSize(); Vec2F getBufferSize();
private:
void bindObjectShaderAttributes(const Object& object);
void bindCameraShaderAttributes(const Mat4F& cameraMat);
private: private:
RenderBuffer mRenderBuffer; RenderBuffer mRenderBuffer;
RenderBuffer mTempBuffer;
RenderShader mDefaultShader; RenderShader mDefaultShader;
RenderShader mSolidShader;
}; };
} }

View file

@ -0,0 +1,13 @@
#version 330 core
layout(location = 0) in vec3 Point;
uniform vec4 Origin;
uniform mat4 Basis;
uniform mat4 Camera;
void main() {
// vec4 transformed = vec4(Point.xyz, 1.0);
vec4 transformed = (Basis * vec4(Point.xyz, 1.0)) + Origin;
gl_Position = Camera * transformed;
}

View file

@ -0,0 +1,7 @@
#version 330 core
out vec4 FragColor;
void main() {
FragColor = vec4(1, 0, 0, 1.f);
}

View file

@ -43,6 +43,8 @@ normal = n1 * barycentric.x + n2 * barycentric.y + n3 * barycentric.z;
using namespace tp; using namespace tp;
// TODO : de-duplicate in Scene?
void RayTracer::castRay(const Ray& ray, RayCastData& out, alnf farVal) { void RayTracer::castRay(const Ray& ray, RayCastData& out, alnf farVal) {
out.hit = false; out.hit = false;
out.obj = nullptr; out.obj = nullptr;

View file

@ -30,14 +30,6 @@ namespace tp {
void render(const Scene& scene, OutputBuffers& out, const RenderSettings& settings); void render(const Scene& scene, OutputBuffers& out, const RenderSettings& settings);
private: private:
struct RayCastData {
const Object* obj = nullptr;
TrigCache* trig = nullptr;
Vec3F hitPos = { 0, 0, 0 };
bool hit = false;
bool inv = false;
};
struct LightData { struct LightData {
halnf intensity = 0; halnf intensity = 0;
}; };

View file

@ -19,4 +19,4 @@ add_executable(Sketch3DApp ./applications/Entry.cpp)
target_link_libraries(Sketch3DApp ${PROJECT_NAME}) target_link_libraries(Sketch3DApp ${PROJECT_NAME})
file(COPY "rsc" DESTINATION "${CMAKE_BINARY_DIR}/${PROJECT_NAME}/") file(COPY "rsc" DESTINATION "${CMAKE_BINARY_DIR}/${PROJECT_NAME}/")
file(COPY "applications/Font.ttf" DESTINATION "${CMAKE_BINARY_DIR}/${PROJECT_NAME}/") file(COPY "../Graphics/rsc" DESTINATION "${CMAKE_BINARY_DIR}/${PROJECT_NAME}/")

View file

@ -11,7 +11,7 @@ public:
Sketch3DApplication() { Sketch3DApplication() {
setRoot(&mGui); setRoot(&mGui);
mGui.createRenderWidget(mGraphics->getCanvas(), { 1920, 1080 }); mGui.createRenderWidget(mGraphics->getCanvas(), { 2560, 1440 });
mGui.setProject(&mSketch); mGui.setProject(&mSketch);
} }

Binary file not shown.

View file

@ -280,7 +280,7 @@ void PencilBrush::draw(Renderer* render, const Camera* camera) {
} }
PencilBrush::~PencilBrush() { PencilBrush::~PencilBrush() {
if (mStroke) delete mStroke; delete mStroke;
} }
void PencilBrush::finish(Project* proj) { void PencilBrush::finish(Project* proj) {

View file

@ -14,7 +14,7 @@ using namespace tp;
class Example : public WidgetApplication { class Example : public WidgetApplication {
public: public:
Example() { Example() {
exampleColorPicker(); exampleNestedMenus();
} }
void exampleAll() { void exampleAll() {
@ -64,6 +64,7 @@ public:
static ButtonWidget buttons[10]; static ButtonWidget buttons[10];
widget.setDirection(false); widget.setDirection(false);
// widget.setDirection(false);
setRoot(&widget); setRoot(&widget);

View file

@ -58,7 +58,9 @@ void RootWidget::processFrame(EventHandler* events, const RectF& screenArea) {
updateAreaCache(&mRoot, false); updateAreaCache(&mRoot, false);
// trigger some widgets by moise pointer // trigger some widgets by moise pointer
mUpdateManager.handleFocusChanges(&mRoot, *events); auto prevFocus = mUpdateManager.getFocusWidget();
auto newFocus = mUpdateManager.findFocusWidget(&mRoot, *events);
mUpdateManager.handleFocusChanges(newFocus, prevFocus);
// check triggered widgets for removal // check triggered widgets for removal
mUpdateManager.clean(); mUpdateManager.clean();
@ -148,8 +150,10 @@ void RootWidget::openPopup(Widget* widget) {
} }
void RootWidget::closePopup(Widget* widget) { void RootWidget::closePopup(Widget* widget) {
if (mUpdateManager.canRemoveWidget(widget)) {
mPopups.removeChild(widget); mPopups.removeChild(widget);
mUpdateManager.freeFocus(widget); mUpdateManager.removeWidget(widget);
}
} }
void RootWidget::lockFocus(Widget* widget) { mUpdateManager.lockFocus(widget); } void RootWidget::lockFocus(Widget* widget) { mUpdateManager.lockFocus(widget); }

View file

@ -113,14 +113,17 @@ void DebugManager::recursiveDraw(Canvas& canvas, Widget* active, const Vec2F& po
canvas.text((active->mDebug.id + ":" + std::to_string(depthOrder)).c_str(), area, 22, Canvas::Align::LC, 2, color); canvas.text((active->mDebug.id + ":" + std::to_string(depthOrder)).c_str(), area, 22, Canvas::Align::LC, 2, color);
} }
if (mRootWidget->mUpdateManager.mFocusLockStack.find(active)) {
canvas.frame(area, { 0, 1, 0, 0.5f });
}
if (active->mFlags.get(Widget::IN_FOCUS)) { if (active->mFlags.get(Widget::IN_FOCUS)) {
if (active->isUpdate()) { if (active->isUpdate()) {
canvas.circle(pos + active->mDebug.pLocal, 5, active->mDebug.col); canvas.circle(pos + active->mDebug.pLocal, 5, active->mDebug.col);
canvas.circle(active->mDebug.pGlobal, 15, active->mDebug.col); canvas.circle(active->mDebug.pGlobal, 15, active->mDebug.col);
} }
RGBA color = { 1, 0, 0, 0.3f }; canvas.debugCross(area, { 0, 1, 0, 1 });
canvas.debugCross(area, color);
} }
int orderIdx = 0; int orderIdx = 0;

View file

@ -36,7 +36,7 @@ void LayoutManager::findDependencies(Widget* root) {
if (!widget->isUpdate()) continue; if (!widget->isUpdate()) continue;
for (auto child : widget->mChildren) { for (auto child : widget->mChildren) {
// if (!child->isUpdate()) continue; if (!child->isUpdate()) continue;
mDepGraph.insert({ child, {} }); mDepGraph.insert({ child, {} });

View file

@ -69,35 +69,36 @@ void UpdateManager::getWidgetPath(Widget* widget, std::vector<Widget*>& out) {
} }
} }
void UpdateManager::handleFocusChanges(Widget* root, EventHandler& events) { Widget* UpdateManager::findFocusWidget(Widget* root, EventHandler& events) {
auto prevFocus = mInFocusWidget;
events.setCursorOrigin({ 0, 0 }); events.setCursorOrigin({ 0, 0 });
mInFocusWidget = nullptr; mInFocusWidget = nullptr;
findMouseFocusWidget(root, &mInFocusWidget, events.getPointer());
findFocusWidget(root, &mInFocusWidget, events.getPointer()); if (mFocusLockStack.size()) {
if (mFocusLockWidget) {
bool hasLockedWidget = false; bool hasLockedWidget = false;
for (auto iter = mInFocusWidget; iter; iter = iter->mParent) { for (auto iter = mInFocusWidget; iter; iter = iter->mParent) {
if (iter == mFocusLockWidget) { if (iter == mFocusLockStack.last()) {
hasLockedWidget = true; hasLockedWidget = true;
break; break;
} }
} }
if (!hasLockedWidget) { if (!hasLockedWidget) {
mInFocusWidget = mFocusLockWidget; mInFocusWidget = mFocusLockStack.last();
} }
} }
return mInFocusWidget;
}
void UpdateManager::handleFocusChanges(Widget* active, Widget* prevActive) {
// if (mInFocusWidget == prevFocus) return; // if (mInFocusWidget == prevFocus) return;
if (mInFocusWidget) scheduleUpdate(mInFocusWidget, "focus entered"); if (active) scheduleUpdate(active, "focus entered");
if (!mInFocusWidget && !prevFocus) return; if (!active && !prevActive) return;
std::vector<Widget*> path2; std::vector<Widget*> path2;
getWidgetPath(mInFocusWidget, path2); getWidgetPath(active, path2);
size_t propLen2 = path2.size(); size_t propLen2 = path2.size();
for (auto i = 0; i < path2.size(); i++) { for (auto i = 0; i < path2.size(); i++) {
if (!path2[i]->propagateEventsToChildren()) { if (!path2[i]->propagateEventsToChildren()) {
@ -107,7 +108,7 @@ void UpdateManager::handleFocusChanges(Widget* root, EventHandler& events) {
} }
std::vector<Widget*> path1; std::vector<Widget*> path1;
getWidgetPath(prevFocus, path1); getWidgetPath(prevActive, path1);
size_t propLen1 = path1.size(); size_t propLen1 = path1.size();
for (auto i = 0; i < path1.size(); i++) { for (auto i = 0; i < path1.size(); i++) {
if (!path1[i]->propagateEventsToChildren()) { if (!path1[i]->propagateEventsToChildren()) {
@ -135,7 +136,7 @@ void UpdateManager::handleFocusChanges(Widget* root, EventHandler& events) {
} }
} }
void UpdateManager::findFocusWidget(Widget* iter, Widget** focus, const Vec2F& pointer) { void UpdateManager::findMouseFocusWidget(Widget* iter, Widget** focus, const Vec2F& pointer) {
if (!iter->mArea.getTargetRect().isInside(pointer) || !iter->mFlags.get(Widget::ENABLED)) return; if (!iter->mArea.getTargetRect().isInside(pointer) || !iter->mFlags.get(Widget::ENABLED)) return;
if (iter->processesEvents()) { if (iter->processesEvents()) {
@ -143,7 +144,7 @@ void UpdateManager::findFocusWidget(Widget* iter, Widget** focus, const Vec2F& p
} }
for (auto child = iter->mDepthOrder.lastNode(); child; child = child->prev) { for (auto child = iter->mDepthOrder.lastNode(); child; child = child->prev) {
findFocusWidget(child->data, focus, pointer - iter->mArea.getTargetRect().pos); findMouseFocusWidget(child->data, focus, pointer - iter->mArea.getTargetRect().pos);
} }
} }
@ -159,8 +160,10 @@ void UpdateManager::processActiveTree(Widget* iter, EventHandler& events, Vec2F
procWidget(iter, events, false); procWidget(iter, events, false);
} }
for (auto child : iter->mDepthOrder) { for (auto child = iter->mDepthOrder.firstNode(); child;) {
processActiveTree(child.data(), events, current); auto next = child->next; // any child may be removed at runtime
processActiveTree(child->data, events, current);
child = next;
} }
} }
@ -211,10 +214,24 @@ void UpdateManager::procWidget(Widget* widget, EventHandler& events, bool withEv
} }
void UpdateManager::lockFocus(tp::Widget* widget) { void UpdateManager::lockFocus(tp::Widget* widget) {
mFocusLockWidget = widget; DEBUG_ASSERT(mFocusLockStack.find(widget) == nullptr)
// TODO : check that widget is a child of top of the stack
mFocusLockStack.pushBack(widget);
} }
void UpdateManager::freeFocus(tp::Widget* widget) { void UpdateManager::freeFocus(tp::Widget* widget) {
// DEBUG_ASSERT(mFocusLockWidget == widget) DEBUG_ASSERT(mFocusLockStack.last() == widget)
mFocusLockWidget = nullptr; mFocusLockStack.popBack();
}
bool UpdateManager::canRemoveWidget(Widget* widget) const {
if (auto node = mFocusLockStack.find(widget)) {
if (node->next) return false;
}
return true;
}
void UpdateManager::removeWidget(Widget* widget) {
freeFocus(widget);
} }

View file

@ -86,7 +86,7 @@ void DockWidget::process(const EventHandler& events) {
} }
} else if (events.isReleased(InputID::MOUSE1)) { } else if (events.isReleased(InputID::MOUSE1)) {
layout()->endResize(); layout()->endResize();
freeFocus(); if (layout()->isResizing()) freeFocus();
} }
if (layout()->isResizing()) { if (layout()->isResizing()) {

View file

@ -7,9 +7,10 @@ using namespace tp;
void ScrollableBarWidget::process(const EventHandler& events) { void ScrollableBarWidget::process(const EventHandler& events) {
// all content is visible no need to process anything // all content is visible no need to process anything
if (mSizeFactor >= 1) { if (mSizeFactor >= 1) {
if (mScrolling) freeFocus();
mScrolling = false; mScrolling = false;
mPosFactor = mSizeFactor / 2.f; mPosFactor = mSizeFactor / 2.f;
freeFocus();
return; return;
} }

View file

@ -31,6 +31,8 @@ namespace tp {
using DFSAction = std::function<void(Widget*)>; using DFSAction = std::function<void(Widget*)>;
protected:
enum Flags : int1 { enum Flags : int1 {
ENABLED = 0, ENABLED = 0,
NEEDS_UPDATE, NEEDS_UPDATE,

View file

@ -87,7 +87,7 @@ namespace tp {
bool mDebug = false; bool mDebug = false;
bool mDebugStopProcessing = false; bool mDebugStopProcessing = false;
bool mDebugRedrawAlways = false; bool mDebugRedrawAlways = false;
bool mDetailed = false; bool mDetailed = true;
std::set<Widget*> mProcBreakpoints; std::set<Widget*> mProcBreakpoints;
std::set<Widget*> mLayBreakpoints; std::set<Widget*> mLayBreakpoints;

View file

@ -25,20 +25,26 @@ namespace tp {
void clean(); void clean();
void updateTreeToProcess(Widget* root); void updateTreeToProcess(Widget* root);
void handleFocusChanges(Widget* root, EventHandler& events); void handleFocusChanges(Widget* active, Widget* prevActive);
Widget* findFocusWidget(Widget* root, EventHandler& events);
void findFocusWidget(Widget* iter, Widget** focus, const Vec2F& pointer); void findMouseFocusWidget(Widget* iter, Widget** focus, const Vec2F& pointer);
static void getWidgetPath(Widget* widget, std::vector<Widget*>& out); static void getWidgetPath(Widget* widget, std::vector<Widget*>& out);
void processActiveTree(Widget* iter, EventHandler& events, Vec2F pos); void processActiveTree(Widget* iter, EventHandler& events, Vec2F pos);
void processFocusItems(EventHandler& events); void processFocusItems(EventHandler& events);
Widget* getFocusWidget() { return mInFocusWidget; }
bool canRemoveWidget(Widget* widget) const;
void removeWidget(Widget* widget);
private: private:
static void procWidget(Widget* widget, EventHandler& events, bool withEvents = false); static void procWidget(Widget* widget, EventHandler& events, bool withEvents = false);
private: private:
std::map<Widget*, bool> mTriggeredWidgets; std::map<Widget*, bool> mTriggeredWidgets;
Widget* mInFocusWidget = nullptr; Widget* mInFocusWidget = nullptr;
Widget* mFocusLockWidget = nullptr; List<Widget*> mFocusLockStack;
private: private:
int mDebugWidgetsToProcess = 0; int mDebugWidgetsToProcess = 0;