From: Daniel Karbach Date: Tue, 8 Mar 2016 16:46:48 +0000 (+0100) Subject: mouse cursor mockup X-Git-Url: https://git.localhorst.tv/?a=commitdiff_plain;h=2e761a60d1717297b7e308ef1b66e2a81319fdff;p=tacos.git mouse cursor mockup with a fake intersection test --- diff --git a/assets b/assets index c690bc6..5fa2c5b 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit c690bc6e1a92fae19922e48b58c4874e330b490a +Subproject commit 5fa2c5b1e2c9b3a7411f05a6e512992f8f551125 diff --git a/src/graphics/camera.cpp b/src/graphics/camera.cpp index d9b8e84..f637852 100644 --- a/src/graphics/camera.cpp +++ b/src/graphics/camera.cpp @@ -25,7 +25,7 @@ void Camera::Move(const glm::vec3 &delta) noexcept { void Camera::Rotate(const glm::vec2 &delta) noexcept { orientation += delta; - orientation.x = glm::clamp(orientation.x, -1.55f, 1.55f); + orientation.x = glm::clamp(orientation.x, 0.0f, 1.55f); while (orientation.y > 3.14159265358979323844f) { // π orientation.y -= 6.28318530717958647688f; // 2π } diff --git a/src/physics/ray.hpp b/src/physics/ray.hpp new file mode 100644 index 0000000..355af50 --- /dev/null +++ b/src/physics/ray.hpp @@ -0,0 +1,18 @@ +#ifndef TACOS_PHYSICS_RAY_HPP_ +#define TACOS_PHYSICS_RAY_HPP_ + +#include + + +namespace tacos { + +struct Ray { + + glm::vec3 origin; + glm::vec3 direction; + +}; + +} + +#endif diff --git a/src/tacos.cpp b/src/tacos.cpp index 0e47e6e..e477ab4 100644 --- a/src/tacos.cpp +++ b/src/tacos.cpp @@ -6,13 +6,16 @@ #include "graphics/shader.hpp" #include "graphics/viewport.hpp" #include "graphics/window.hpp" +#include "physics/ray.hpp" #include "rand/SimplexNoise.hpp" +#include "world/Cursor.hpp" #include "world/Floor.hpp" #include #include #include #include +#include using namespace tacos; @@ -41,6 +44,14 @@ int main(int argc, char *argv[]) { GLint mv_location = floor_program.UniformLocation("MV"); GLint mvp_location = floor_program.UniformLocation("MVP"); + Shader cursor_vert(asset_loader.LoadVertexShader("cursor")); + Shader cursor_frag(asset_loader.LoadFragmentShader("cursor")); + Program cursor_program; + cursor_program.Attach(cursor_vert); + cursor_program.Attach(cursor_frag); + cursor_program.Link(); + GLint vp_location = cursor_program.UniformLocation("VP"); + Floor floor(64, 50); SimplexNoise noise(0); for (int z = 0; z < floor.Depth(); ++z) { @@ -50,14 +61,29 @@ int main(int argc, char *argv[]) { } floor.GenerateVertices(); + Cursor cursor; + Camera camera; camera.Warp(glm::vec3(20.0f, 0.0f, 20.0f)); glm::mat4 M; glm::mat4 V; + glm::mat4 VP; glm::mat4 MV; glm::mat4 MVP; + // cursor stuff + glm::mat4 inverse_VP; + glm::vec2 screen_mouse; + { + int x, y; + SDL_GetMouseState(&x, &y); + screen_mouse.x = x; + screen_mouse.y = y; + } + Ray world_mouse; + glm::vec3 pointer; + constexpr float cam_speed = 15.0f; // units per second constexpr float cam_rot_speed = 2.0f; // radians per second @@ -68,8 +94,6 @@ int main(int argc, char *argv[]) { glm::vec2 cam_rot_ccw(0.0f); glm::vec2 cam_rot_cw(0.0f); - floor_program.Use(); - bool running = true; Uint32 last = SDL_GetTicks(); SDL_Event event; @@ -167,6 +191,10 @@ int main(int argc, char *argv[]) { break; } break; + case SDL_MOUSEMOTION: + screen_mouse.x = event.motion.x; + screen_mouse.y = event.motion.y; + break; case SDL_QUIT: running = false; break; @@ -186,9 +214,28 @@ int main(int argc, char *argv[]) { camera.BackOff((cam_far - cam_near) * cam_speed * dt); camera.Rotate((cam_rot_ccw - cam_rot_cw) * cam_rot_speed * dt); - // render V = camera.View(); + VP = viewport.Perspective() * V; + { // mouse + inverse_VP = glm::inverse(VP); + glm::vec2 clip_mouse((screen_mouse / glm::vec2(viewport.Width(), viewport.Height()) - 0.5f) * 2.0f); + // viewport space has origin in lower left, but sdl gives coordinates with orgin in upper left, + // so Y is inverted here (since it maps from -1 to 1 simply by negating) + glm::vec4 ray_begin(inverse_VP * glm::vec4(clip_mouse.x, -clip_mouse.y, -1.0f, 1.0f)); + glm::vec4 ray_end(inverse_VP * glm::vec4(clip_mouse.x, -clip_mouse.y, 0.0f, 1.0f)); + world_mouse.origin = glm::vec3(ray_begin) / ray_begin.w; + world_mouse.direction = glm::normalize((glm::vec3(ray_end) / ray_end.w) - world_mouse.origin); + } + + if (floor.Intersection(world_mouse, pointer)) { + cursor.FloorTile(floor, int(pointer.x), int(pointer.z)); + } else { + cursor.Hide(); + } + + // render glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + floor_program.Use(); for (int z = 0; z < floor.VAODepth(); ++z) { for (int x = 0; x < floor.VAOWidth(); ++x) { M = glm::translate(glm::mat4(1.0f), floor.VAOPosition(x, z)); @@ -199,6 +246,11 @@ int main(int argc, char *argv[]) { floor.DrawVAO(x, z); } } + if (cursor.Visible()) { + cursor_program.Use(); + cursor_program.Uniform(vp_location, VP); + cursor.Draw(); + } window.Flip(); last = now; diff --git a/src/world/Cursor.hpp b/src/world/Cursor.hpp new file mode 100644 index 0000000..828e411 --- /dev/null +++ b/src/world/Cursor.hpp @@ -0,0 +1,51 @@ +#ifndef TACOS_WORLD_CURSOR_HPP_ +#define TACOS_WORLD_CURSOR_HPP_ + +#include +#include + + +namespace tacos { + +class Floor; + +class Cursor { + +public: + Cursor(); + ~Cursor(); + + Cursor(const Cursor &) = delete; + Cursor &operator =(const Cursor &) = delete; + + /// hide cursor + void Hide() noexcept; + /// set top left corner of cursor to focus given floor tile + void FloorTile(const Floor &, int tile_x, int tile_z); + + bool Visible() const noexcept { return mode != HIDDEN; } + void Draw() const noexcept; + +private: + GLuint vao; + GLuint buffers[2]; + + // side length in vertices (make sure it's between 2 and 8 inclusive) + int size; + // distance between cursor and ground + float offset; + + enum Mode { + HIDDEN, + FLOOR, + } mode; + + struct Attributes { + glm::vec3 position; + }; + +}; + +} + +#endif diff --git a/src/world/Floor.hpp b/src/world/Floor.hpp index a7046b5..0ecd6ed 100644 --- a/src/world/Floor.hpp +++ b/src/world/Floor.hpp @@ -1,6 +1,8 @@ #ifndef TACOS_WORLD_FLOOR_HPP_ #define TACOS_WORLD_FLOOR_HPP_ +#include "../physics/ray.hpp" + #include #include #include @@ -45,6 +47,9 @@ public: void GenerateVertices(); + /// check if ray intersects floor, write point of intersection to point + bool Intersection(const Ray &ray, glm::vec3 &point); + private: void SetupVAO(int which, GLuint element_buffer, int vertex_count) noexcept; void FillElementBuffer(GLuint which, int tile_width, int tile_depth); diff --git a/src/world/world.cpp b/src/world/world.cpp index eba12ea..25723ba 100644 --- a/src/world/world.cpp +++ b/src/world/world.cpp @@ -1,8 +1,78 @@ +#include "Cursor.hpp" #include "Floor.hpp" namespace tacos { +Cursor::Cursor() +: vao(0) +, buffers{0} +, size(3) +, offset(0.1f) +, mode(HIDDEN) { + glGenVertexArrays(1, &vao); + glGenBuffers(2, buffers); + + glBindVertexArray(vao); + glBindBuffer(GL_ARRAY_BUFFER, buffers[0]); + glEnableVertexAttribArray(0); + glVertexAttribPointer(0, 3, GL_FLOAT, 0, sizeof(Attributes), reinterpret_cast(offsetof(Attributes, position))); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffers[1]); + glBindVertexArray(0); +} + +Cursor::~Cursor() { + glDeleteBuffers(2, buffers); + glDeleteVertexArrays(1, &vao); +} + +void Cursor::Hide() noexcept { + mode = HIDDEN; +} + +void Cursor::FloorTile(const Floor &floor, int tile_x, int tile_z) { + // TODO: only update if changed + mode = FLOOR; + + int x_begin = glm::clamp(tile_x, 0, floor.Width() - size); + int x_end = x_begin + size; + int z_begin = glm::clamp(tile_z, 0, floor.Depth() - size); + int z_end = z_begin + size; + + glBindVertexArray(vao); + glBindBuffer(GL_ARRAY_BUFFER, buffers[0]); + glBufferData(GL_ARRAY_BUFFER, size * size * sizeof(Attributes), nullptr, GL_DYNAMIC_DRAW); + Attributes *attrib = reinterpret_cast(glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY)); + for (int z = z_begin, index = 0; z < z_end; ++z) { + for (int x = x_begin; x < x_end; ++x, ++index) { + attrib[index].position = glm::vec3(x, floor.GetElevation(x, z) + offset, z); + } + } + glUnmapBuffer(GL_ARRAY_BUFFER); + + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffers[1]); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, (size - 1) * (size - 1) * 6, nullptr, GL_DYNAMIC_DRAW); + unsigned char *element = reinterpret_cast(glMapBuffer(GL_ELEMENT_ARRAY_BUFFER, GL_WRITE_ONLY)); + for (int z = 0, index = 0; z < size - 1; ++z) { + for (int x = 0; x < size - 1; ++x, ++index) { + element[index * 6 + 0] = (z + 0) * size + (x + 0); + element[index * 6 + 1] = (z + 0) * size + (x + 1); + element[index * 6 + 2] = (z + 1) * size + (x + 0); + element[index * 6 + 3] = (z + 0) * size + (x + 1); + element[index * 6 + 4] = (z + 1) * size + (x + 1); + element[index * 6 + 5] = (z + 1) * size + (x + 0); + } + } + glUnmapBuffer(GL_ELEMENT_ARRAY_BUFFER); + glBindVertexArray(0); +} + +void Cursor::Draw() const noexcept { + glBindVertexArray(vao); + glDrawElements(GL_TRIANGLES, (size - 1) * (size - 1) * 6, GL_UNSIGNED_BYTE, nullptr); +} + + constexpr int Floor::VAO_DIVISOR; Floor::Floor(int w, int d) @@ -140,4 +210,21 @@ void Floor::DrawVAO(int vao_x, int vao_z) const noexcept { glDrawElements(GL_TRIANGLES, NumTiles(vao_x, vao_z) * 6, GL_UNSIGNED_SHORT, nullptr); } +bool Floor::Intersection(const Ray &ray, glm::vec3 &point) { + // TODO: this tests for Y=0 plane intersection, change to respect heightmap + if (std::abs(ray.direction.y) < std::numeric_limits::epsilon()) { + // ray parallel to plane + return false; + } + float factor = ray.origin.y / ray.direction.y; + if (factor > 0.0f) { + // intersection "behind" the ray + return false; + } + point.x = ray.origin.x - (ray.direction.x * factor); + point.y = 0.0f; + point.z = ray.origin.z - (ray.direction.z * factor); + return point.x >= 0.0f && point.x <= float(width) && point.z >= 0.0f && point.z <= float(depth); +} + }