working
This commit is contained in:
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
BIN
build/CMakeFiles/ModernEngine.dir/src/Engine.cpp.o
Normal file
BIN
build/CMakeFiles/ModernEngine.dir/src/Engine.cpp.o
Normal file
Binary file not shown.
@@ -1142,6 +1142,9 @@ CMakeFiles/ModernEngine.dir/src/Engine.cpp.o: \
|
|||||||
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/c++/v1/__memory/assume_aligned.h \
|
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/c++/v1/__memory/assume_aligned.h \
|
||||||
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/c++/v1/__memory/ranges_uninitialized_algorithms.h \
|
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/c++/v1/__memory/ranges_uninitialized_algorithms.h \
|
||||||
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/c++/v1/__memory/uses_allocator_construction.h \
|
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/c++/v1/__memory/uses_allocator_construction.h \
|
||||||
|
/opt/homebrew/include/glm/gtc/matrix_access.hpp \
|
||||||
|
/opt/homebrew/include/glm/gtc/../detail/setup.hpp \
|
||||||
|
/opt/homebrew/include/glm/gtc/matrix_access.inl \
|
||||||
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/c++/v1/iostream \
|
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/c++/v1/iostream \
|
||||||
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/c++/v1/ostream \
|
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/c++/v1/ostream \
|
||||||
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/c++/v1/__ostream/print.h \
|
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/c++/v1/__ostream/print.h \
|
||||||
|
|||||||
BIN
build/CMakeFiles/ModernEngine.dir/src/LevelEditor.cpp.o
Normal file
BIN
build/CMakeFiles/ModernEngine.dir/src/LevelEditor.cpp.o
Normal file
Binary file not shown.
@@ -975,6 +975,9 @@ CMakeFiles/ModernEngine.dir/src/LevelEditor.cpp.o: \
|
|||||||
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/c++/v1/__stop_token/stop_callback.h \
|
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/c++/v1/__stop_token/stop_callback.h \
|
||||||
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/c++/v1/future \
|
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/c++/v1/future \
|
||||||
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/c++/v1/__exception/exception_ptr.h \
|
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/c++/v1/__exception/exception_ptr.h \
|
||||||
|
/opt/homebrew/include/glm/gtc/matrix_access.hpp \
|
||||||
|
/opt/homebrew/include/glm/gtc/../detail/setup.hpp \
|
||||||
|
/opt/homebrew/include/glm/gtc/matrix_access.inl \
|
||||||
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/c++/v1/iostream \
|
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/c++/v1/iostream \
|
||||||
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/c++/v1/ostream \
|
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/c++/v1/ostream \
|
||||||
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/c++/v1/__ostream/print.h \
|
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/c++/v1/__ostream/print.h \
|
||||||
|
|||||||
@@ -976,6 +976,9 @@ CMakeFiles/ModernEngine.dir/src/OBJModelPrimitive.cpp.o: \
|
|||||||
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/c++/v1/__stop_token/stop_callback.h \
|
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/c++/v1/__stop_token/stop_callback.h \
|
||||||
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/c++/v1/future \
|
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/c++/v1/future \
|
||||||
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/c++/v1/__exception/exception_ptr.h \
|
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/c++/v1/__exception/exception_ptr.h \
|
||||||
|
/opt/homebrew/include/glm/gtc/matrix_access.hpp \
|
||||||
|
/opt/homebrew/include/glm/gtc/../detail/setup.hpp \
|
||||||
|
/opt/homebrew/include/glm/gtc/matrix_access.inl \
|
||||||
/Users/will/Documents/zoomengine/src/OBJLoader.h \
|
/Users/will/Documents/zoomengine/src/OBJLoader.h \
|
||||||
/Users/will/Documents/zoomengine/src/SimpleTextureLoader.h \
|
/Users/will/Documents/zoomengine/src/SimpleTextureLoader.h \
|
||||||
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/c++/v1/iostream \
|
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/c++/v1/iostream \
|
||||||
|
|||||||
BIN
build/CMakeFiles/ModernEngine.dir/src/main.cpp.o
Normal file
BIN
build/CMakeFiles/ModernEngine.dir/src/main.cpp.o
Normal file
Binary file not shown.
@@ -975,6 +975,9 @@ CMakeFiles/ModernEngine.dir/src/main.cpp.o: \
|
|||||||
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/c++/v1/__stop_token/stop_callback.h \
|
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/c++/v1/__stop_token/stop_callback.h \
|
||||||
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/c++/v1/future \
|
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/c++/v1/future \
|
||||||
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/c++/v1/__exception/exception_ptr.h \
|
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/c++/v1/__exception/exception_ptr.h \
|
||||||
|
/opt/homebrew/include/glm/gtc/matrix_access.hpp \
|
||||||
|
/opt/homebrew/include/glm/gtc/../detail/setup.hpp \
|
||||||
|
/opt/homebrew/include/glm/gtc/matrix_access.inl \
|
||||||
/Users/will/Documents/zoomengine/src/LevelEditor.cpp \
|
/Users/will/Documents/zoomengine/src/LevelEditor.cpp \
|
||||||
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/c++/v1/iostream \
|
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/c++/v1/iostream \
|
||||||
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/c++/v1/ostream \
|
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/c++/v1/ostream \
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
empty
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
empty
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
empty
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
empty
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
empty
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
empty
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
empty
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
empty
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
empty
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
empty
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
empty
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
empty
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
empty
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
empty
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
empty
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
16
|
|
||||||
BIN
build/ModernEngine
Executable file
BIN
build/ModernEngine
Executable file
Binary file not shown.
@@ -522,6 +522,46 @@ Engine::Engine(int w, int h, const char* title)
|
|||||||
InitOverlay();
|
InitOverlay();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// -------------------------------------------------------------
|
||||||
|
// CullSphere — Gribb/Hartmann frustum plane extraction
|
||||||
|
//
|
||||||
|
// Extracts 6 frustum planes directly from the view-projection
|
||||||
|
// matrix by combining its rows. [web:72][web:76]
|
||||||
|
// Returns TRUE if the sphere is fully OUTSIDE any plane
|
||||||
|
// (i.e. should be culled — skip rendering).
|
||||||
|
// -------------------------------------------------------------
|
||||||
|
bool Engine::CullSphere(const glm::mat4& vp,
|
||||||
|
const glm::vec3& center,
|
||||||
|
float radius)
|
||||||
|
{
|
||||||
|
// GLM stores matrices column-major, so row(vp, n) extracts row n.
|
||||||
|
// Each frustum plane = combination of row 3 ± row n. [web:75][web:76]
|
||||||
|
const glm::vec4 row0 = glm::row(vp, 0);
|
||||||
|
const glm::vec4 row1 = glm::row(vp, 1);
|
||||||
|
const glm::vec4 row2 = glm::row(vp, 2);
|
||||||
|
const glm::vec4 row3 = glm::row(vp, 3);
|
||||||
|
|
||||||
|
const glm::vec4 planes[6] = {
|
||||||
|
row3 + row0, // Left
|
||||||
|
row3 - row0, // Right
|
||||||
|
row3 + row1, // Bottom
|
||||||
|
row3 - row1, // Top
|
||||||
|
row3 + row2, // Near
|
||||||
|
row3 - row2, // Far
|
||||||
|
};
|
||||||
|
|
||||||
|
const glm::vec4 pt(center, 1.0f);
|
||||||
|
|
||||||
|
for (const auto& plane : planes) {
|
||||||
|
// Signed distance from sphere centre to plane.
|
||||||
|
// If distance < -radius, sphere is fully on the outside. [web:73]
|
||||||
|
if (glm::dot(plane, pt) < -radius) {
|
||||||
|
return true; // Cull — outside this plane
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false; // Inside or intersecting all 6 planes — keep
|
||||||
|
}
|
||||||
|
|
||||||
// Culling subset method
|
// Culling subset method
|
||||||
Engine::CullingResult Engine::CullObjectsSubset(
|
Engine::CullingResult Engine::CullObjectsSubset(
|
||||||
const std::vector<GameObject*>& objects,
|
const std::vector<GameObject*>& objects,
|
||||||
@@ -1003,9 +1043,17 @@ void Engine::RenderSceneInstanced() {
|
|||||||
|
|
||||||
// MULTITHREADED CULLING SECTION
|
// MULTITHREADED CULLING SECTION
|
||||||
glm::mat4 vp = proj * view;
|
glm::mat4 vp = proj * view;
|
||||||
|
|
||||||
|
std::vector<GameObject*> raw_objects;
|
||||||
|
raw_objects.reserve(game_objects_.size());
|
||||||
|
for (const auto& sp : game_objects_)
|
||||||
|
raw_objects.push_back(sp.get());
|
||||||
|
|
||||||
|
if (raw_objects.empty()) return;
|
||||||
|
|
||||||
// Determine number of threads and chunk size
|
// Determine number of threads and chunk size
|
||||||
size_t numThreads = std::min(threadPool_.getNumThreads(), game_objects_.size());
|
size_t numThreads = std::min(threadPool_.size(), game_objects_.size());
|
||||||
|
numThreads = std::max(numThreads, static_cast<size_t>(1));
|
||||||
size_t objectsPerThread = (game_objects_.size() + numThreads - 1) / numThreads;
|
size_t objectsPerThread = (game_objects_.size() + numThreads - 1) / numThreads;
|
||||||
|
|
||||||
// Launch culling tasks
|
// Launch culling tasks
|
||||||
@@ -1018,9 +1066,11 @@ void Engine::RenderSceneInstanced() {
|
|||||||
|
|
||||||
if (startIdx >= game_objects_.size()) break;
|
if (startIdx >= game_objects_.size()) break;
|
||||||
|
|
||||||
futures.push_back(threadPool_.enqueue([this, startIdx, endIdx, vp]() {
|
futures.push_back(threadPool_.enqueue(
|
||||||
return CullObjectsSubset(game_objects_, startIdx, endIdx, vp);
|
[this, &raw_objects, startIdx, endIdx, vp]() -> CullingResult {
|
||||||
}));
|
return CullObjectsSubset(raw_objects, startIdx, endIdx, vp);
|
||||||
|
}
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Collect results
|
// Collect results
|
||||||
|
|||||||
@@ -6,7 +6,8 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include "ThreadPool.h"
|
#include "ThreadPool.h"
|
||||||
|
#include <glm/gtc/matrix_access.hpp>
|
||||||
|
|
||||||
|
|
||||||
// Forward declarations
|
// Forward declarations
|
||||||
@@ -80,6 +81,10 @@ private:
|
|||||||
std::unordered_map<Primitive*, std::vector<glm::mat4>>& outBatches
|
std::unordered_map<Primitive*, std::vector<glm::mat4>>& outBatches
|
||||||
);
|
);
|
||||||
|
|
||||||
|
static bool CullSphere(const glm::mat4& vp,
|
||||||
|
const glm::vec3& center,
|
||||||
|
float radius);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -177,4 +182,4 @@ private:
|
|||||||
glm::vec3 position_;
|
glm::vec3 position_;
|
||||||
glm::vec3 rotation_;
|
glm::vec3 rotation_;
|
||||||
glm::vec3 scale_{1.0f};
|
glm::vec3 scale_{1.0f};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,53 +1,65 @@
|
|||||||
// LevelEditor.cpp
|
// LevelEditor.cpp — FIXED
|
||||||
|
|
||||||
#include "Engine.h"
|
#include "Engine.h"
|
||||||
#include <GLFW/glfw3.h>
|
#include <GLFW/glfw3.h>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
// ---------------------------------------------
|
|
||||||
// LevelEditor: free camera, object placement, light sliders, stats
|
|
||||||
// ---------------------------------------------
|
|
||||||
class LevelEditor : public Engine {
|
class LevelEditor : public Engine {
|
||||||
private:
|
private:
|
||||||
std::vector<std::shared_ptr<GameObject>> objects_;
|
std::vector<std::shared_ptr<GameObject>> objects_;
|
||||||
std::shared_ptr<Primitive> monkeyPrim_;
|
std::shared_ptr<Primitive> monkeyPrim_;
|
||||||
std::shared_ptr<GameObject> selected_;
|
std::shared_ptr<GameObject> selected_;
|
||||||
|
|
||||||
|
// FIX: Button stores its own pressed state so DrawButton can be
|
||||||
|
// called for multiple buttons without a shared static variable
|
||||||
struct Button {
|
struct Button {
|
||||||
float x, y, w, h;
|
float x, y, w, h;
|
||||||
bool pressed;
|
bool pressed = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Free-cam state
|
||||||
|
bool firstMouse_ = true;
|
||||||
|
double lastX_ = 0.0, lastY_ = 0.0;
|
||||||
|
float yaw_ = -90.0f, pitch_ = 0.0f;
|
||||||
|
float camSpeed_ = 10.0f, mouseSens_ = 0.1f;
|
||||||
|
|
||||||
// Free cam state
|
// Light sliders
|
||||||
bool firstMouse_ = true;
|
float lightR = 1.0f, lightG = 1.0f, lightB = 1.0f, lightI = 1.0f;
|
||||||
double lastX_, lastY_;
|
bool dragR = false, dragG = false, dragB = false, dragI = false;
|
||||||
float yaw_ = -90.0f, pitch_ = 0.0f;
|
|
||||||
float camSpeed_ = 10.0f, mouseSens_ = 0.1f;
|
|
||||||
|
|
||||||
// Light control state
|
Button btnOrtho{ 560.0f, 150.0f, 80.0f, 20.0f, false };
|
||||||
float lightR = 1, lightG = 1, lightB = 1, lightI = 1;
|
bool useOrtho_ = false;
|
||||||
bool dragR = false, dragG = false, dragB = false, dragI = false;
|
|
||||||
|
|
||||||
|
// FIX: per-key previous-state for edge detection (one-shot placement)
|
||||||
|
bool prevKey1_ = false, prevKey2_ = false, prevKey3_ = false,
|
||||||
|
prevKey4_ = false, prevKey5_ = false;
|
||||||
|
|
||||||
Button btnOrtho { 560, 150, 80, 20, false };
|
// ---------------------------------------------------------------
|
||||||
bool useOrtho_ = false;
|
// Input helpers
|
||||||
|
// ---------------------------------------------------------------
|
||||||
// Helpers
|
|
||||||
bool KeyDown(int k) const { return glfwGetKey(window_, k) == GLFW_PRESS; }
|
bool KeyDown(int k) const { return glfwGetKey(window_, k) == GLFW_PRESS; }
|
||||||
|
|
||||||
|
// Returns true only on the frame the key transitions low→high [web:61]
|
||||||
|
bool KeyJustPressed(int k, bool& prev) const {
|
||||||
|
bool cur = KeyDown(k);
|
||||||
|
bool just = cur && !prev;
|
||||||
|
prev = cur;
|
||||||
|
return just;
|
||||||
|
}
|
||||||
|
|
||||||
void ProcessMouse(double xpos, double ypos) {
|
void ProcessMouse(double xpos, double ypos) {
|
||||||
if (firstMouse_) {
|
if (firstMouse_) {
|
||||||
lastX_ = xpos; lastY_ = ypos;
|
lastX_ = xpos; lastY_ = ypos;
|
||||||
firstMouse_ = false;
|
firstMouse_ = false;
|
||||||
|
return; // FIX: skip first frame to avoid a jump
|
||||||
}
|
}
|
||||||
float xoff = (xpos - lastX_) * mouseSens_;
|
float xoff = static_cast<float>(xpos - lastX_) * mouseSens_;
|
||||||
float yoff = (lastY_ - ypos) * mouseSens_;
|
float yoff = static_cast<float>(lastY_ - ypos) * mouseSens_;
|
||||||
lastX_ = xpos; lastY_ = ypos;
|
lastX_ = xpos; lastY_ = ypos;
|
||||||
yaw_ += xoff;
|
yaw_ += xoff;
|
||||||
pitch_ = glm::clamp(pitch_ + yoff, -89.0f, 89.0f);
|
pitch_ = glm::clamp(pitch_ + yoff, -89.0f, 89.0f);
|
||||||
glm::vec3 front;
|
glm::vec3 front;
|
||||||
front.x = cos(glm::radians(yaw_)) * cos(glm::radians(pitch_));
|
front.x = cos(glm::radians(yaw_)) * cos(glm::radians(pitch_));
|
||||||
@@ -56,266 +68,268 @@ private:
|
|||||||
camera_target_ = camera_position_ + glm::normalize(front);
|
camera_target_ = camera_position_ + glm::normalize(front);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Place primitive at camera target
|
// FIX: overload — explicit position for use before camera is set up
|
||||||
void Place(std::shared_ptr<Primitive> prim) {
|
void Place(std::shared_ptr<Primitive> prim, const glm::vec3& pos) {
|
||||||
glm::vec3 pos = camera_target_;
|
|
||||||
auto go = CreateGameObject(prim, pos);
|
auto go = CreateGameObject(prim, pos);
|
||||||
objects_.push_back(go);
|
objects_.push_back(go);
|
||||||
AddGameObject(go);
|
AddGameObject(go);
|
||||||
}
|
}
|
||||||
|
void Place(std::shared_ptr<Primitive> prim) { Place(prim, camera_target_); }
|
||||||
|
|
||||||
// Raycast pick uses bounding spheres
|
|
||||||
std::shared_ptr<GameObject> Pick(double mx, double my) {
|
std::shared_ptr<GameObject> Pick(double mx, double my) {
|
||||||
int w,h; glfwGetWindowSize(window_,&w,&h);
|
int w, h; glfwGetWindowSize(window_, &w, &h);
|
||||||
float x = (2.0f*mx)/w -1.0f, y=1.0f-(2.0f*my)/h;
|
float nx = (2.0f * mx) / w - 1.0f;
|
||||||
glm::vec4 ray_clip{x,y,-1,1};
|
float ny = 1.0f - (2.0f * my) / h;
|
||||||
|
glm::vec4 ray_clip{ nx, ny, -1.0f, 1.0f };
|
||||||
// Create projection and view matrices locally
|
|
||||||
glm::mat4 proj = glm::perspective(glm::radians(60.0f), float(w)/h, 0.1f, 1000.0f);
|
glm::mat4 proj = glm::perspective(glm::radians(60.0f), float(w)/h, 0.1f, 1000.0f);
|
||||||
glm::mat4 view = glm::lookAt(camera_position_, camera_target_, camera_up_);
|
glm::mat4 view = glm::lookAt(camera_position_, camera_target_, camera_up_);
|
||||||
|
glm::vec4 ray_eye = glm::inverse(proj) * ray_clip;
|
||||||
glm::vec4 ray_eye = glm::inverse(proj)*ray_clip; ray_eye.z=-1; ray_eye.w=0;
|
ray_eye.z = -1.0f; ray_eye.w = 0.0f;
|
||||||
glm::vec3 dir = glm::normalize(glm::vec3(glm::inverse(view)*ray_eye));
|
glm::vec3 dir = glm::normalize(glm::vec3(glm::inverse(view) * ray_eye));
|
||||||
|
|
||||||
std::shared_ptr<GameObject> best; float bd=FLT_MAX;
|
std::shared_ptr<GameObject> best; float bd = FLT_MAX;
|
||||||
for(auto& obj:objects_){
|
for (auto& obj : objects_) {
|
||||||
glm::vec3 oc = camera_position_ - obj->GetPosition();
|
glm::vec3 oc = camera_position_ - obj->GetPosition();
|
||||||
float b = glm::dot(oc,dir);
|
float b = glm::dot(oc, dir);
|
||||||
float c = glm::dot(oc,oc) - obj->GetBoundingRadius()*obj->GetBoundingRadius();
|
float c = glm::dot(oc, oc)
|
||||||
float disc = b*b - c;
|
- obj->GetBoundingRadius() * obj->GetBoundingRadius();
|
||||||
if(disc<0) continue;
|
float disc = b * b - c;
|
||||||
float t = -b - sqrt(disc);
|
if (disc < 0.0f) continue;
|
||||||
if(t>0 && t<bd){ bd=t; best=obj; }
|
float t = -b - std::sqrt(disc);
|
||||||
|
if (t > 0.0f && t < bd) { bd = t; best = obj; }
|
||||||
}
|
}
|
||||||
return best;
|
return best;
|
||||||
}
|
}
|
||||||
|
|
||||||
void DrawChar(char c, float x, float y, float s) {
|
// ---------------------------------------------------------------
|
||||||
static const std::vector<std::vector<std::pair<float,float>>> strokes = {
|
// 2D drawing — unified vertex type + helper to avoid repeating
|
||||||
{{0,0},{1,0},{1,1},{0,1},{0,0}},{{0.5f,0},{0.5f,1}},
|
// glBufferData / glVertexAttribPointer for every draw call
|
||||||
{{0,1},{1,1},{1,0.5f},{0,0.5f},{0,0},{1,0}},
|
// ---------------------------------------------------------------
|
||||||
{{0,1},{1,1},{1,0},{0,0},{1,0.5f}},
|
struct V2D { float x, y, r, g, b; };
|
||||||
{{0,1},{0,0.5f},{1,0.5f},{1,1},{1,0}},
|
|
||||||
{{1,1},{0,1},{0,0.5f},{1,0.5f},{1,0},{0,0}},
|
// FIX: single upload-and-draw helper used everywhere — the original
|
||||||
{{1,1},{0,1},{0,0},{1,0},{1,0.5f},{0,0.5f}},
|
// code was missing glVertexAttribPointer calls before several
|
||||||
{{0,1},{1,1},{0.5f,0}},
|
// glDrawArrays calls (e.g. the button fill triangles)
|
||||||
{{0,0},{0,1},{1,1},{1,0},{0,0},{0,0.5f},{1,0.5f}},
|
void UploadAndDraw(const std::vector<V2D>& v, GLenum mode) {
|
||||||
{{1,0},{1,1},{0,1},{0,0.5f},{1,0.5f}}
|
if (v.empty()) return;
|
||||||
};
|
glBufferData(GL_ARRAY_BUFFER,
|
||||||
std::vector<float> v;
|
static_cast<GLsizeiptr>(v.size() * sizeof(V2D)),
|
||||||
if(c>='0'&&c<='9'){
|
v.data(), GL_DYNAMIC_DRAW);
|
||||||
for(auto&p:strokes[c-'0']){
|
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE,
|
||||||
v.push_back(x+p.first*s);
|
sizeof(V2D), reinterpret_cast<void*>(0));
|
||||||
v.push_back(y+p.second*s);
|
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE,
|
||||||
v.push_back(1);v.push_back(1);v.push_back(1);
|
sizeof(V2D), reinterpret_cast<void*>(2 * sizeof(float)));
|
||||||
|
glDrawArrays(mode, 0, static_cast<GLsizei>(v.size()));
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
// Segment-display font — digits AND letters
|
||||||
|
// FIX: original DrawChar only handled '0'–'9'; calling it with
|
||||||
|
// 'R','G','B','I' (and button labels) produced no output.
|
||||||
|
// Merged into one function covering A–Z and 0–9.
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
void DrawChar(char c, float x, float y, float s,
|
||||||
|
float r = 1.0f, float g = 1.0f, float b = 1.0f)
|
||||||
|
{
|
||||||
|
using P = std::pair<float,float>;
|
||||||
|
std::vector<P> pts;
|
||||||
|
|
||||||
|
if (c >= '0' && c <= '9') {
|
||||||
|
static const std::vector<std::vector<P>> D = {
|
||||||
|
{{0,0},{1,0},{1,1},{0,1},{0,0}},
|
||||||
|
{{0.5f,0},{0.5f,1}},
|
||||||
|
{{0,1},{1,1},{1,0.5f},{0,0.5f},{0,0},{1,0}},
|
||||||
|
{{0,1},{1,1},{1,0},{0,0},{1,0.5f}},
|
||||||
|
{{0,1},{0,0.5f},{1,0.5f},{1,1},{1,0}},
|
||||||
|
{{1,1},{0,1},{0,0.5f},{1,0.5f},{1,0},{0,0}},
|
||||||
|
{{1,1},{0,1},{0,0},{1,0},{1,0.5f},{0,0.5f}},
|
||||||
|
{{0,1},{1,1},{0.5f,0}},
|
||||||
|
{{0,0},{0,1},{1,1},{1,0},{0,0},{0,0.5f},{1,0.5f}},
|
||||||
|
{{1,0},{1,1},{0,1},{0,0.5f},{1,0.5f}}
|
||||||
|
};
|
||||||
|
pts = D[c - '0'];
|
||||||
|
} else {
|
||||||
|
switch (c) {
|
||||||
|
case 'A': pts={{0,0},{0.5f,1},{1,0},{0.25f,0.5f},{0.75f,0.5f}}; break;
|
||||||
|
case 'B': pts={{0,0},{0,1},{0.7f,1},{0.7f,0.5f},{0,0.5f},{0.7f,0.5f},{0.7f,0},{0,0}}; break;
|
||||||
|
case 'C': pts={{1,1},{0,1},{0,0},{1,0}}; break;
|
||||||
|
case 'D': pts={{0,0},{0,1},{0.7f,0.8f},{0.7f,0.2f},{0,0}}; break;
|
||||||
|
case 'E': pts={{1,1},{0,1},{0,0.5f},{0.7f,0.5f},{0,0.5f},{0,0},{1,0}}; break;
|
||||||
|
case 'F': pts={{0,0},{0,1},{1,1},{0,1},{0,0.5f},{0.7f,0.5f}}; break;
|
||||||
|
case 'G': pts={{1,1},{0,1},{0,0},{1,0},{1,0.5f},{0.5f,0.5f}}; break;
|
||||||
|
case 'H': pts={{0,0},{0,1},{0,0.5f},{1,0.5f},{1,1},{1,0}}; break;
|
||||||
|
case 'I': pts={{0,1},{1,1},{0.5f,1},{0.5f,0},{0,0},{1,0}}; break;
|
||||||
|
case 'J': pts={{0,1},{1,1},{1,0},{0,0},{0,0.3f}}; break;
|
||||||
|
case 'K': pts={{0,0},{0,1},{0,0.5f},{1,1},{0,0.5f},{1,0}}; break;
|
||||||
|
case 'L': pts={{0,1},{0,0},{1,0}}; break;
|
||||||
|
case 'M': pts={{0,0},{0,1},{0.5f,0.5f},{1,1},{1,0}}; break;
|
||||||
|
case 'N': pts={{0,0},{0,1},{1,0},{1,1}}; break;
|
||||||
|
case 'O': pts={{0,0},{0,1},{1,1},{1,0},{0,0}}; break;
|
||||||
|
case 'P': pts={{0,0},{0,1},{1,1},{1,0.5f},{0,0.5f}}; break;
|
||||||
|
case 'Q': pts={{1,0},{0,0},{0,1},{1,1},{1,0},{0.6f,0.2f}}; break;
|
||||||
|
case 'R': pts={{0,0},{0,1},{1,1},{1,0.5f},{0,0.5f},{1,0}}; break;
|
||||||
|
case 'S': pts={{1,1},{0,1},{0,0.5f},{1,0.5f},{1,0},{0,0}}; break;
|
||||||
|
case 'T': pts={{0,1},{1,1},{0.5f,1},{0.5f,0}}; break;
|
||||||
|
case 'U': pts={{0,1},{0,0},{1,0},{1,1}}; break;
|
||||||
|
case 'V': pts={{0,1},{0.5f,0},{1,1}}; break;
|
||||||
|
case 'W': pts={{0,1},{0.25f,0},{0.5f,0.5f},{0.75f,0},{1,1}}; break;
|
||||||
|
case 'X': pts={{0,0},{1,1},{0.5f,0.5f},{0,1},{1,0}}; break;
|
||||||
|
case 'Y': pts={{0,1},{0.5f,0.5f},{1,1},{0.5f,0.5f},{0.5f,0}}; break;
|
||||||
|
case 'Z': pts={{0,1},{1,1},{0,0},{1,0}}; break;
|
||||||
|
default: return; // skip spaces / unknown
|
||||||
}
|
}
|
||||||
glBufferData(GL_ARRAY_BUFFER,v.size()*sizeof(float),v.data(),GL_DYNAMIC_DRAW);
|
|
||||||
glVertexAttribPointer(0,2,GL_FLOAT,GL_FALSE,5*sizeof(float),0);
|
|
||||||
glVertexAttribPointer(1,3,GL_FLOAT,GL_FALSE,5*sizeof(float),(void*)(2*sizeof(float)));
|
|
||||||
glDrawArrays(GL_LINE_STRIP,0,(GLsizei)(v.size()/5));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<V2D> v;
|
||||||
|
v.reserve(pts.size());
|
||||||
|
for (auto& p : pts)
|
||||||
|
v.push_back({ x + p.first * s, y + p.second * s, r, g, b });
|
||||||
|
UploadAndDraw(v, GL_LINE_STRIP);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw a letter R,G,B,I
|
// Draw a string left-to-right with fixed character advance
|
||||||
void DrawLetter(char c, float x, float y, float s) {
|
void DrawString(const char* text, float x, float y, float charSize,
|
||||||
struct V{float x,y,r,g,b;};
|
float r = 1.0f, float g = 1.0f, float b = 1.0f)
|
||||||
std::vector<V> L;
|
{
|
||||||
if(c=='R'){
|
const float advance = charSize * 0.75f + 2.0f;
|
||||||
L={{x,y,1,1,1},{x,y+s,1,1,1},{x,y+s,1,1,1},{x+s*0.7f,y+s,1,1,1},
|
for (int i = 0; text[i] != '\0'; ++i)
|
||||||
{x+s*0.7f,y+s,1,1,1},{x+s*0.7f,y+s*0.5f,1,1,1},{x+s*0.7f,y+s*0.5f,1,1,1},
|
if (text[i] != ' ')
|
||||||
{x,y+s*0.5f,1,1,1},{x,y+s*0.5f,1,1,1},{x+s*0.7f,y,1,1,1}};
|
DrawChar(text[i], x + i * advance, y, charSize * 0.7f, r, g, b);
|
||||||
} else if(c=='G'){
|
|
||||||
L={{x+s,y+s*0.3f,1,1,1},{x,y+s*0.3f,1,1,1},{x,y+s*0.3f,1,1,1},
|
|
||||||
{x,y+s*0.7f,1,1,1},{x,y+s*0.7f,1,1,1},{x+s,y+s*0.7f,1,1,1},
|
|
||||||
{x+s,y+s*0.7f,1,1,1},{x+s,y+s*0.5f,1,1,1},{x+s,y+s*0.5f,1,1,1},
|
|
||||||
{x+s*0.5f,y+s*0.5f,1,1,1}};
|
|
||||||
} else if(c=='B'){
|
|
||||||
L={{x,y,1,1,1},{x,y+s,1,1,1},{x,y+s,1,1,1},{x+s*0.7f,y+s,1,1,1},
|
|
||||||
{x+s*0.7f,y+s,1,1,1},{x+s*0.7f,y+s*0.5f,1,1,1},{x+s*0.7f,y+s*0.5f,1,1,1},
|
|
||||||
{x,y+s*0.5f,1,1,1},{x,y+s*0.5f,1,1,1},{x+s*0.7f,y,1,1,1},
|
|
||||||
{x+s*0.7f,y,1,1,1},{x,y,1,1,1}};
|
|
||||||
} else if(c=='I'){
|
|
||||||
L={{x,y+s,1,1,1},{x+s,y+s,1,1,1},{x+s*0.5f,y+s,1,1,1},
|
|
||||||
{x+s*0.5f,y,1,1,1},{x,y,1,1,1},{x+s,y,1,1,1}};
|
|
||||||
}
|
|
||||||
if(!L.empty()){
|
|
||||||
glBufferData(GL_ARRAY_BUFFER,L.size()*sizeof(V),L.data(),GL_DYNAMIC_DRAW);
|
|
||||||
glVertexAttribPointer(0,2,GL_FLOAT,GL_FALSE,sizeof(V),(void*)0);
|
|
||||||
glVertexAttribPointer(1,3,GL_FLOAT,GL_FALSE,sizeof(V),(void*)(2*sizeof(float)));
|
|
||||||
glDrawArrays(GL_LINES,0,(GLsizei)L.size());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
float MeasureTextWidth(const char* text, float size) {
|
float MeasureTextWidth(const char* text, float size) {
|
||||||
return strlen(text) * size * 0.6f;
|
return strlen(text) * (size * 0.7f + 2.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw a production-ready gradient button
|
// FIX: takes Button& (not const) so pressed state is stored per-button,
|
||||||
bool DrawButton(const Button &btn, const char* label,
|
// replacing the broken shared static bool in the original
|
||||||
double mx, double my, bool mb) {
|
bool DrawButton(Button& btn, const char* label,
|
||||||
struct V { float x,y,r,g,b; };
|
double mx, double my, bool mb)
|
||||||
|
{
|
||||||
// Determine states
|
|
||||||
bool hovered = mx >= btn.x && mx <= btn.x + btn.w
|
bool hovered = mx >= btn.x && mx <= btn.x + btn.w
|
||||||
&& my >= btn.y && my <= btn.y + btn.h;
|
&& my >= btn.y && my <= btn.y + btn.h;
|
||||||
static bool pressedPrev = false;
|
bool prevPressed = btn.pressed;
|
||||||
bool pressed = mb && hovered;
|
btn.pressed = mb && hovered;
|
||||||
bool clicked = pressed && !pressedPrev;
|
bool clicked = btn.pressed && !prevPressed;
|
||||||
pressedPrev = pressed;
|
|
||||||
|
|
||||||
// Colors
|
glm::vec3 top = hovered ? glm::vec3(0.3f,0.5f,0.8f)
|
||||||
glm::vec3 topColor = hovered ? glm::vec3(0.3f,0.5f,0.8f) : glm::vec3(0.2f,0.4f,0.7f);
|
: glm::vec3(0.2f,0.4f,0.7f);
|
||||||
glm::vec3 bottomColor = hovered ? glm::vec3(0.1f,0.3f,0.6f) : glm::vec3(0.0f,0.2f,0.5f);
|
glm::vec3 bot = hovered ? glm::vec3(0.1f,0.3f,0.6f)
|
||||||
if (pressed) {
|
: glm::vec3(0.0f,0.2f,0.5f);
|
||||||
topColor *= 0.8f;
|
if (btn.pressed) { top *= 0.8f; bot *= 0.8f; }
|
||||||
bottomColor *= 0.8f;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Vertex data: two triangles forming the quad with gradient
|
// FIX: UploadAndDraw now sets glVertexAttribPointer — the original
|
||||||
V verts[6] = {
|
// triangle draw was missing those calls entirely
|
||||||
// first triangle
|
UploadAndDraw({
|
||||||
{btn.x, btn.y, bottomColor.r, bottomColor.g, bottomColor.b},
|
{btn.x, btn.y, bot.r, bot.g, bot.b},
|
||||||
{btn.x + btn.w, btn.y, bottomColor.r, bottomColor.g, bottomColor.b},
|
{btn.x + btn.w, btn.y, bot.r, bot.g, bot.b},
|
||||||
{btn.x + btn.w, btn.y + btn.h, topColor.r, topColor.g, topColor.b },
|
{btn.x + btn.w, btn.y + btn.h, top.r, top.g, top.b},
|
||||||
// second triangle
|
{btn.x, btn.y, bot.r, bot.g, bot.b},
|
||||||
{btn.x, btn.y, bottomColor.r, bottomColor.g, bottomColor.b},
|
{btn.x + btn.w, btn.y + btn.h, top.r, top.g, top.b},
|
||||||
{btn.x + btn.w, btn.y + btn.h, topColor.r, topColor.g, topColor.b },
|
{btn.x, btn.y + btn.h, top.r, top.g, top.b},
|
||||||
{btn.x, btn.y + btn.h, topColor.r, topColor.g, topColor.b }
|
}, GL_TRIANGLES);
|
||||||
};
|
|
||||||
|
|
||||||
glBufferData(GL_ARRAY_BUFFER, sizeof(verts), verts, GL_DYNAMIC_DRAW);
|
UploadAndDraw({
|
||||||
glDrawArrays(GL_TRIANGLES, 0, 6);
|
{btn.x, btn.y, 0,0,0},
|
||||||
|
{btn.x + btn.w, btn.y, 0,0,0},
|
||||||
|
{btn.x + btn.w, btn.y + btn.h, 0,0,0},
|
||||||
|
{btn.x, btn.y + btn.h, 0,0,0},
|
||||||
|
{btn.x, btn.y, 0,0,0},
|
||||||
|
}, GL_LINE_STRIP);
|
||||||
|
|
||||||
// Draw border
|
// Centred label using DrawString (supports full A–Z)
|
||||||
struct V2 { float x,y,r,g,b; };
|
float tw = MeasureTextWidth(label, 10.0f);
|
||||||
V2 border[5] = {
|
float tx = btn.x + (btn.w - tw) * 0.5f;
|
||||||
{btn.x, btn.y, 0,0,0},
|
float ty = btn.y + (btn.h - 10.0f) * 0.5f;
|
||||||
{btn.x + btn.w, btn.y, 0,0,0},
|
DrawString(label, tx, ty, 10.0f);
|
||||||
{btn.x + btn.w, btn.y + btn.h, 0,0,0},
|
|
||||||
{btn.x, btn.y + btn.h, 0,0,0},
|
|
||||||
{btn.x, btn.y, 0,0,0}
|
|
||||||
};
|
|
||||||
glBufferData(GL_ARRAY_BUFFER, sizeof(border), border, GL_DYNAMIC_DRAW);
|
|
||||||
glVertexAttribPointer(0,2,GL_FLOAT,GL_FALSE,sizeof(V2),(void*)0);
|
|
||||||
glVertexAttribPointer(1,3,GL_FLOAT,GL_FALSE,sizeof(V2),(void*)(2*sizeof(float)));
|
|
||||||
glDrawArrays(GL_LINE_STRIP,0,5);
|
|
||||||
|
|
||||||
// Draw label centered
|
|
||||||
float textWidth = MeasureTextWidth(label, 12.0f);
|
|
||||||
float tx = btn.x + (btn.w - textWidth) * 0.5f;
|
|
||||||
float ty = btn.y + (btn.h - 12.0f) * 0.5f;
|
|
||||||
DrawChar(label[0], tx, ty, 12); // loop over each char if full string support needed
|
|
||||||
|
|
||||||
return clicked;
|
return clicked;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void DrawSlider(float x, float y, float w, float h,
|
||||||
|
float& val, bool& drag,
|
||||||
|
double mx, double my, bool mb)
|
||||||
|
{
|
||||||
|
if (mb && mx >= x && mx <= x+w && my >= y && my <= y+h) drag = true;
|
||||||
|
if (!mb) drag = false;
|
||||||
|
if (drag) val = glm::clamp(float((mx - x) / w), 0.0f, 1.0f);
|
||||||
|
|
||||||
// Draw slider at (x,y), width w height h controlling value 0–1
|
// Track background
|
||||||
void DrawSlider(float x,float y,float w,float h,
|
UploadAndDraw({
|
||||||
float &val,bool &drag,
|
{x, y, 0.2f,0.2f,0.2f}, {x+w, y, 0.2f,0.2f,0.2f},
|
||||||
double mx,double my,bool mb){
|
{x+w, y+h, 0.2f,0.2f,0.2f}, {x, y+h, 0.2f,0.2f,0.2f}
|
||||||
// handle drag
|
}, GL_TRIANGLE_FAN);
|
||||||
if(mb && mx>=x && mx<=x+w && my>=y && my<=y+h) drag=true;
|
|
||||||
if(!mb) drag=false;
|
|
||||||
if(drag) val = glm::clamp(float((mx-x)/w),0.0f,1.0f);
|
|
||||||
|
|
||||||
struct V{float x,y,r,g,b;};
|
// Filled portion
|
||||||
// track bg
|
float fw = w * val;
|
||||||
std::vector<V> quad = {
|
UploadAndDraw({
|
||||||
{x,y,0.2f,0.2f,0.2f},{x+w,y,0.2f,0.2f,0.2f},
|
{x, y, 0.8f,0.4f,0.4f}, {x+fw, y, 0.8f,0.4f,0.4f},
|
||||||
{x+w,y+h,0.2f,0.2f,0.2f},{x,y+h,0.2f,0.2f,0.2f}
|
{x+fw, y+h, 0.8f,0.4f,0.4f}, {x, y+h, 0.8f,0.4f,0.4f}
|
||||||
};
|
}, GL_TRIANGLE_FAN);
|
||||||
glBufferData(GL_ARRAY_BUFFER,quad.size()*sizeof(V),quad.data(),GL_DYNAMIC_DRAW);
|
|
||||||
glVertexAttribPointer(0,2,GL_FLOAT,GL_FALSE,sizeof(V),(void*)0);
|
|
||||||
glVertexAttribPointer(1,3,GL_FLOAT,GL_FALSE,sizeof(V),(void*)(2*sizeof(float)));
|
|
||||||
glDrawArrays(GL_TRIANGLE_FAN,0,4);
|
|
||||||
|
|
||||||
// filled
|
// Knob
|
||||||
float fw = w*val;
|
float kx = x + fw - 2.0f, ky = y - 2.0f, ks = h + 4.0f;
|
||||||
quad = {
|
UploadAndDraw({
|
||||||
{x,y,0.8f,0.4f,0.4f},{x+fw,y,0.8f,0.4f,0.4f},
|
{kx, ky, 1,1,1}, {kx+ks, ky, 1,1,1},
|
||||||
{x+fw,y+h,0.8f,0.4f,0.4f},{x,y+h,0.8f,0.4f,0.4f}
|
{kx+ks, ky+ks, 1,1,1}, {kx, ky+ks, 1,1,1}
|
||||||
};
|
}, GL_TRIANGLE_FAN);
|
||||||
glBufferData(GL_ARRAY_BUFFER,quad.size()*sizeof(V),quad.data(),GL_DYNAMIC_DRAW);
|
|
||||||
glVertexAttribPointer(0,2,GL_FLOAT,GL_FALSE,sizeof(V),(void*)0);
|
|
||||||
glVertexAttribPointer(1,3,GL_FLOAT,GL_FALSE,sizeof(V),(void*)(2*sizeof(float)));
|
|
||||||
glDrawArrays(GL_TRIANGLE_FAN,0,4);
|
|
||||||
|
|
||||||
// knob
|
|
||||||
float kx=x+fw-2, ky=y-2, ks=h+4;
|
|
||||||
quad = {
|
|
||||||
{kx,ky,1.0f,1.0f,1.0f},{kx+ks,ky,1.0f,1.0f,1.0f},
|
|
||||||
{kx+ks,ky+ks,1.0f,1.0f,1.0f},{kx,ky+ks,1.0f,1.0f,1.0f}
|
|
||||||
};
|
|
||||||
glBufferData(GL_ARRAY_BUFFER,quad.size()*sizeof(V),quad.data(),GL_DYNAMIC_DRAW);
|
|
||||||
glVertexAttribPointer(0,2,GL_FLOAT,GL_FALSE,sizeof(V),(void*)0);
|
|
||||||
glVertexAttribPointer(1,3,GL_FLOAT,GL_FALSE,sizeof(V),(void*)(2*sizeof(float)));
|
|
||||||
glDrawArrays(GL_TRIANGLE_FAN,0,4);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
LevelEditor() : Engine(3840,2160,"Level Editor").enable_vsync = true {}
|
// FIX 1 — constructor: enable_vsync set in body, not on base ctor call
|
||||||
|
LevelEditor() : Engine(3840, 2160, "Level Editor") {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
bool OnInitialize() override {
|
bool OnInitialize() override {
|
||||||
// load monkey
|
try { monkeyPrim_ = CreateOBJModel("monkey.obj"); }
|
||||||
try {
|
catch (...) { monkeyPrim_ = CreateCube(glm::vec3(1.0f, 0.5f, 0.2f)); }
|
||||||
monkeyPrim_ = CreateOBJModel("monkey.obj");
|
|
||||||
}
|
|
||||||
catch(...) {
|
|
||||||
monkeyPrim_ = CreateCube(glm::vec3(1.0f, 0.5f, 0.2f));
|
|
||||||
}
|
|
||||||
|
|
||||||
// initial grid - fix glm::vec3 constructor
|
// FIX: use explicit-position overload — camera_target_ is not yet
|
||||||
Place(CreateGrid(20, 1.0f, glm::vec3(0.4f, 0.4f, 0.4f)));
|
// set at this point, so passing it would place the grid at (0,0,0)
|
||||||
|
// only by accident if Engine zero-inits it. Be explicit.
|
||||||
|
Place(CreateGrid(20, 1.0f, glm::vec3(0.4f)), glm::vec3(0.0f));
|
||||||
|
|
||||||
// camera
|
camera_position_ = glm::vec3(0.0f, 20.0f, 20.0f);
|
||||||
camera_position_ = glm::vec3(0, 20, 20);
|
camera_target_ = camera_position_
|
||||||
glm::vec3 dir = glm::normalize(glm::vec3(0,0,0) - camera_position_);
|
+ glm::normalize(glm::vec3(0.0f) - camera_position_);
|
||||||
camera_target_ = camera_position_ + dir;
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void OnUpdate(float dt) override {
|
void OnUpdate(float dt) override {
|
||||||
// free flight WASD QE
|
|
||||||
glm::vec3 front = glm::normalize(camera_target_ - camera_position_);
|
glm::vec3 front = glm::normalize(camera_target_ - camera_position_);
|
||||||
glm::vec3 right = glm::normalize(glm::cross(front, camera_up_));
|
glm::vec3 right = glm::normalize(glm::cross(front, camera_up_));
|
||||||
if(KeyDown(GLFW_KEY_W)) camera_position_ += front * camSpeed_ * dt;
|
|
||||||
if(KeyDown(GLFW_KEY_S)) camera_position_ -= front * camSpeed_ * dt;
|
if (KeyDown(GLFW_KEY_W)) camera_position_ += front * camSpeed_ * dt;
|
||||||
if(KeyDown(GLFW_KEY_A)) camera_position_ -= right * camSpeed_ * dt;
|
if (KeyDown(GLFW_KEY_S)) camera_position_ -= front * camSpeed_ * dt;
|
||||||
if(KeyDown(GLFW_KEY_D)) camera_position_ += right * camSpeed_ * dt;
|
if (KeyDown(GLFW_KEY_A)) camera_position_ -= right * camSpeed_ * dt;
|
||||||
if(KeyDown(GLFW_KEY_Q)) camera_position_ -= camera_up_ * camSpeed_ * dt;
|
if (KeyDown(GLFW_KEY_D)) camera_position_ += right * camSpeed_ * dt;
|
||||||
if(KeyDown(GLFW_KEY_E)) camera_position_ += camera_up_ * camSpeed_ * dt;
|
if (KeyDown(GLFW_KEY_Q)) camera_position_ -= camera_up_ * camSpeed_ * dt;
|
||||||
|
if (KeyDown(GLFW_KEY_E)) camera_position_ += camera_up_ * camSpeed_ * dt;
|
||||||
camera_target_ = camera_position_ + front;
|
camera_target_ = camera_position_ + front;
|
||||||
|
|
||||||
// mouse look on RMB
|
double mx, my; glfwGetCursorPos(window_, &mx, &my);
|
||||||
double mx,my; glfwGetCursorPos(window_,&mx,&my);
|
if (glfwGetMouseButton(window_, GLFW_MOUSE_BUTTON_RIGHT) == GLFW_PRESS)
|
||||||
if(glfwGetMouseButton(window_,GLFW_MOUSE_BUTTON_RIGHT)==GLFW_PRESS)
|
ProcessMouse(mx, my);
|
||||||
ProcessMouse(mx,my);
|
else
|
||||||
else firstMouse_=true;
|
firstMouse_ = true;
|
||||||
|
|
||||||
// placement keys - fix glm::vec3 constructors
|
// FIX: KeyJustPressed — one object per key-press, not one per frame [web:61]
|
||||||
if(KeyDown(GLFW_KEY_1)) Place(CreateCube(glm::vec3(0.8f, 0.2f, 0.2f)));
|
if (KeyJustPressed(GLFW_KEY_1, prevKey1_)) Place(CreateCube(glm::vec3(0.8f,0.2f,0.2f)));
|
||||||
if(KeyDown(GLFW_KEY_2)) Place(CreateSphere(16, glm::vec3(0.2f, 0.8f, 0.2f)));
|
if (KeyJustPressed(GLFW_KEY_2, prevKey2_)) Place(CreateSphere(16, glm::vec3(0.2f,0.8f,0.2f)));
|
||||||
if(KeyDown(GLFW_KEY_3)) Place(CreatePlane(5, 5, glm::vec3(0.2f, 0.2f, 0.8f)));
|
if (KeyJustPressed(GLFW_KEY_3, prevKey3_)) Place(CreatePlane(5.0f, 5.0f, glm::vec3(0.2f,0.2f,0.8f)));
|
||||||
if(KeyDown(GLFW_KEY_4)) Place(CreateGrid(5, 1.0f, glm::vec3(0.5f, 0.5f, 0.5f)));
|
if (KeyJustPressed(GLFW_KEY_4, prevKey4_)) Place(CreateGrid(5, 1.0f, glm::vec3(0.5f)));
|
||||||
if(KeyDown(GLFW_KEY_5)) Place(monkeyPrim_);
|
if (KeyJustPressed(GLFW_KEY_5, prevKey5_)) Place(monkeyPrim_);
|
||||||
|
|
||||||
// picking
|
if (glfwGetMouseButton(window_, GLFW_MOUSE_BUTTON_LEFT) == GLFW_PRESS)
|
||||||
if(glfwGetMouseButton(window_,GLFW_MOUSE_BUTTON_LEFT)==GLFW_PRESS)
|
selected_ = Pick(mx, my);
|
||||||
selected_ = Pick(mx,my);
|
|
||||||
|
|
||||||
// move selected with arrows
|
if (selected_) {
|
||||||
if(selected_){
|
|
||||||
glm::vec3 p = selected_->GetPosition();
|
glm::vec3 p = selected_->GetPosition();
|
||||||
if(KeyDown(GLFW_KEY_UP)) p.z -= dt*5;
|
if (KeyDown(GLFW_KEY_UP)) p.z -= dt * 5.0f;
|
||||||
if(KeyDown(GLFW_KEY_DOWN)) p.z += dt*5;
|
if (KeyDown(GLFW_KEY_DOWN)) p.z += dt * 5.0f;
|
||||||
if(KeyDown(GLFW_KEY_LEFT)) p.x -= dt*5;
|
if (KeyDown(GLFW_KEY_LEFT)) p.x -= dt * 5.0f;
|
||||||
if(KeyDown(GLFW_KEY_RIGHT)) p.x += dt*5;
|
if (KeyDown(GLFW_KEY_RIGHT)) p.x += dt * 5.0f;
|
||||||
selected_->SetPosition(p);
|
selected_->SetPosition(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -323,70 +337,64 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
void OnRender() override {
|
void OnRender() override {
|
||||||
|
|
||||||
glUseProgram(shader_program_);
|
glUseProgram(shader_program_);
|
||||||
int w = width_, h = height_;
|
|
||||||
glm::mat4 proj = useOrtho_
|
glm::mat4 proj = useOrtho_
|
||||||
? glm::ortho(-30.0f*float(w)/h, 30.0f*float(w)/h, -30.0f, 30.0f, 0.1f, 1000.0f)
|
? glm::ortho(-30.0f * float(width_)/height_, 30.0f * float(width_)/height_,
|
||||||
: glm::perspective(glm::radians(60.0f), float(w)/h, 0.1f, 1000.0f);
|
-30.0f, 30.0f, 0.1f, 1000.0f)
|
||||||
glUniformMatrix4fv(glGetUniformLocation(shader_program_, "uProjection"), 1, GL_FALSE, &proj[0][0]);
|
: glm::perspective(glm::radians(60.0f),
|
||||||
|
float(width_) / float(height_), 0.1f, 1000.0f);
|
||||||
|
glUniformMatrix4fv(glGetUniformLocation(shader_program_, "uProjection"),
|
||||||
|
1, GL_FALSE, &proj[0][0]);
|
||||||
|
|
||||||
|
|
||||||
// 1) 3D scene + overlay graphs
|
|
||||||
Engine::OnRender();
|
Engine::OnRender();
|
||||||
|
RenderOverlay();
|
||||||
|
|
||||||
// 2) custom 2D UI: sliders + stats
|
// ── 2D overlay ───────────────────────────────────────────────
|
||||||
RenderOverlay(); // draws graphs & text
|
|
||||||
|
|
||||||
// setup 2D
|
|
||||||
glDisable(GL_DEPTH_TEST);
|
glDisable(GL_DEPTH_TEST);
|
||||||
glEnable(GL_BLEND);
|
glEnable(GL_BLEND);
|
||||||
glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||||
|
|
||||||
glUseProgram(overlay_program);
|
glUseProgram(overlay_program);
|
||||||
GLint loc = glGetUniformLocation(overlay_program,"uOrtho");
|
glm::mat4 ortho2D = glm::ortho(0.0f, float(width_), 0.0f, float(height_));
|
||||||
glm::mat4 ortho = glm::ortho(0.0f,float(width_),0.0f,float(height_));
|
glUniformMatrix4fv(glGetUniformLocation(overlay_program, "uOrtho"),
|
||||||
glUniformMatrix4fv(loc,1,GL_FALSE,&ortho[0][0]);
|
1, GL_FALSE, &ortho2D[0][0]);
|
||||||
glBindVertexArray(overlay_vao);
|
glBindVertexArray(overlay_vao);
|
||||||
glBindBuffer(GL_ARRAY_BUFFER, overlay_vbo);
|
glBindBuffer(GL_ARRAY_BUFFER, overlay_vbo);
|
||||||
|
|
||||||
// mouse state
|
double mx, my; glfwGetCursorPos(window_, &mx, &my);
|
||||||
double mx,my; glfwGetCursorPos(window_,&mx,&my);
|
my = height_ - my; // flip Y to match OpenGL bottom-left origin
|
||||||
my = height_ - my; // flip Y coordinate for OpenGL
|
bool mb = glfwGetMouseButton(window_, GLFW_MOUSE_BUTTON_LEFT) == GLFW_PRESS;
|
||||||
bool mb = glfwGetMouseButton(window_,GLFW_MOUSE_BUTTON_LEFT)==GLFW_PRESS;
|
|
||||||
|
|
||||||
// draw sliders and labels
|
// Sliders
|
||||||
DrawSlider(50,50,200,10, lightR, dragR, mx,my, mb);
|
DrawSlider(50, 50, 200, 10, lightR, dragR, mx, my, mb);
|
||||||
DrawSlider(50,70,200,10, lightG, dragG, mx,my, mb);
|
DrawSlider(50, 70, 200, 10, lightG, dragG, mx, my, mb);
|
||||||
DrawSlider(50,90,200,10, lightB, dragB, mx,my, mb);
|
DrawSlider(50, 90, 200, 10, lightB, dragB, mx, my, mb);
|
||||||
DrawSlider(50,110,200,10,lightI, dragI, mx,my, mb);
|
DrawSlider(50, 110, 200, 10, lightI, dragI, mx, my, mb);
|
||||||
|
|
||||||
if (DrawButton(btnOrtho, useOrtho_ ? "Ortho On" : "Persp On", mx, my, mb)) {
|
// FIX: DrawButton now takes Button& — state is stored per button
|
||||||
|
// FIX: labels use uppercase A–Z which DrawChar now supports
|
||||||
|
if (DrawButton(btnOrtho, useOrtho_ ? "ORTHO" : "PERSP", mx, my, mb))
|
||||||
useOrtho_ = !useOrtho_;
|
useOrtho_ = !useOrtho_;
|
||||||
}
|
|
||||||
|
|
||||||
|
// FIX: was DrawChar('R',...) etc. — DrawChar now handles letters;
|
||||||
|
// coloured to match channel (red/green/blue/white)
|
||||||
|
DrawChar('R', 30, 52, 10, 1.0f, 0.3f, 0.3f);
|
||||||
|
DrawChar('G', 30, 72, 10, 0.3f, 1.0f, 0.3f);
|
||||||
|
DrawChar('B', 30, 92, 10, 0.3f, 0.5f, 1.0f);
|
||||||
|
DrawChar('I', 30, 112, 10, 1.0f, 1.0f, 1.0f);
|
||||||
|
|
||||||
|
// Push updated light values to 3D shader
|
||||||
// draw labels
|
|
||||||
DrawChar('R',30,50,12);
|
|
||||||
DrawChar('G',30,70,12);
|
|
||||||
DrawChar('B',30,90,12);
|
|
||||||
DrawChar('I',30,110,12);
|
|
||||||
|
|
||||||
// update 3D shader light
|
|
||||||
glUseProgram(shader_program_);
|
glUseProgram(shader_program_);
|
||||||
glUniform3f(glGetUniformLocation(shader_program_,"uLightColor"),lightR,lightG,lightB);
|
glUniform3f(glGetUniformLocation(shader_program_, "uLightColor"),
|
||||||
glUniform1f(glGetUniformLocation(shader_program_,"uAmbientStrength"),0.1f*lightI);
|
lightR, lightG, lightB);
|
||||||
|
glUniform1f(glGetUniformLocation(shader_program_, "uAmbientStrength"),
|
||||||
|
0.1f * lightI);
|
||||||
|
|
||||||
// restore
|
|
||||||
glBindVertexArray(0);
|
glBindVertexArray(0);
|
||||||
glUseProgram(0);
|
glUseProgram(0);
|
||||||
glDisable(GL_BLEND);
|
glDisable(GL_BLEND);
|
||||||
glEnable(GL_DEPTH_TEST);
|
glEnable(GL_DEPTH_TEST);
|
||||||
}
|
}
|
||||||
|
|
||||||
void OnShutdown() override {
|
void OnShutdown() override { Engine::OnShutdown(); }
|
||||||
Engine::OnShutdown();
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -8,11 +8,11 @@
|
|||||||
#include <future>
|
#include <future>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <type_traits> // for std::invoke_result_t
|
#include <stdexcept>
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
class ThreadPool {
|
class ThreadPool {
|
||||||
public:
|
public:
|
||||||
// Construct a thread pool with the given number of threads (defaults to hardware concurrency)
|
|
||||||
explicit ThreadPool(size_t numThreads = std::thread::hardware_concurrency())
|
explicit ThreadPool(size_t numThreads = std::thread::hardware_concurrency())
|
||||||
: stopFlag(false)
|
: stopFlag(false)
|
||||||
{
|
{
|
||||||
@@ -36,21 +36,29 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Destructor shuts down the pool and joins all threads
|
~ThreadPool() { shutdown(); }
|
||||||
~ThreadPool() {
|
|
||||||
shutdown();
|
// Non-copyable, non-movable
|
||||||
}
|
ThreadPool(const ThreadPool&) = delete;
|
||||||
|
ThreadPool& operator=(const ThreadPool&) = delete;
|
||||||
|
|
||||||
// Enqueue a task into the thread pool. Returns a future for the result.
|
|
||||||
template <class F, class... Args>
|
template <class F, class... Args>
|
||||||
auto enqueue(F&& f, Args&&... args)
|
auto enqueue(F&& f, Args&&... args)
|
||||||
-> std::future<std::invoke_result_t<F, Args...>>
|
// FIX: use decay_t so invoke_result matches what is actually stored —
|
||||||
|
// std::bind produced a wrapper whose result type differed from F's
|
||||||
|
-> std::future<std::invoke_result_t<std::decay_t<F>, std::decay_t<Args>...>>
|
||||||
{
|
{
|
||||||
using return_type = std::invoke_result_t<F, Args...>;
|
using return_type =
|
||||||
|
std::invoke_result_t<std::decay_t<F>, std::decay_t<Args>...>;
|
||||||
|
|
||||||
// Wrap the function and its arguments into a packaged_task
|
// FIX: replace std::bind with tuple + std::apply — bind wrappers are
|
||||||
|
// not transparently invocable and break invoke_result deduction
|
||||||
auto taskPtr = std::make_shared<std::packaged_task<return_type()>>(
|
auto taskPtr = std::make_shared<std::packaged_task<return_type()>>(
|
||||||
std::bind(std::forward<F>(f), std::forward<Args>(args)...)
|
[func = std::forward<F>(f),
|
||||||
|
args = std::make_tuple(std::forward<Args>(args)...)]() mutable
|
||||||
|
-> return_type {
|
||||||
|
return std::apply(func, args);
|
||||||
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
std::future<return_type> result = taskPtr->get_future();
|
std::future<return_type> result = taskPtr->get_future();
|
||||||
@@ -58,30 +66,31 @@ public:
|
|||||||
std::unique_lock<std::mutex> lock(queueMutex);
|
std::unique_lock<std::mutex> lock(queueMutex);
|
||||||
if (stopFlag.load())
|
if (stopFlag.load())
|
||||||
throw std::runtime_error("enqueue on stopped ThreadPool");
|
throw std::runtime_error("enqueue on stopped ThreadPool");
|
||||||
|
|
||||||
tasks.emplace([taskPtr]() { (*taskPtr)(); });
|
tasks.emplace([taskPtr]() { (*taskPtr)(); });
|
||||||
}
|
}
|
||||||
condition.notify_one();
|
condition.notify_one();
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Signal all threads to shut down and join them
|
// FIX: added size() — Engine.cpp called getNumThreads() which didn't exist
|
||||||
|
size_t size() const { return workers.size(); }
|
||||||
|
|
||||||
void shutdown() {
|
void shutdown() {
|
||||||
{
|
{
|
||||||
std::unique_lock<std::mutex> lock(queueMutex);
|
std::unique_lock<std::mutex> lock(queueMutex);
|
||||||
stopFlag.store(true);
|
stopFlag.store(true);
|
||||||
}
|
}
|
||||||
condition.notify_all();
|
condition.notify_all();
|
||||||
for (std::thread &worker : workers) {
|
for (std::thread& worker : workers)
|
||||||
if (worker.joinable())
|
if (worker.joinable())
|
||||||
worker.join();
|
worker.join();
|
||||||
}
|
workers.clear(); // safe to call shutdown() more than once
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::vector<std::thread> workers; // worker threads
|
std::vector<std::thread> workers;
|
||||||
std::queue<std::function<void()>> tasks; // task queue
|
std::queue<std::function<void()>> tasks;
|
||||||
std::mutex queueMutex; // protects tasks
|
std::mutex queueMutex;
|
||||||
std::condition_variable condition; // signals availability
|
std::condition_variable condition;
|
||||||
std::atomic<bool> stopFlag; // indicates shutdown
|
std::atomic<bool> stopFlag;
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user