From 4743d7d58a697ef65f016fbe9a901683f546fccf Mon Sep 17 00:00:00 2001 From: cinnaboot Date: Sun, 13 Jan 2019 15:48:46 -0500 Subject: [PATCH] testing some raycasting algorithms --- TODO.md | 1 + src/camera.cpp | 67 +++++++++++++++++++++++++++++++++++++++++++++++++ src/camera.h | 8 ++++++ src/gooey.cpp | 7 +++--- src/hexgame.cpp | 67 ++++++++++++++++++++++--------------------------- src/hexgrid.cpp | 63 +++++++++++++++++++++++++++++++++++----------- src/hexgrid.h | 13 +++++++++- src/util.h | 7 ++++++ 8 files changed, 177 insertions(+), 56 deletions(-) diff --git a/TODO.md b/TODO.md index 4f4429b..1ee6ed1 100644 --- a/TODO.md +++ b/TODO.md @@ -35,6 +35,7 @@ - use a storage pool for assimp meshes allowing reuse across entities - also cache textures from loaded meshes for re-use - replace remaining calls to malloc/free with safe(r) function in util.h +- remove v2i/v3f... etc from util.h and either use glm everywhere, or write a smaller linear math.h ## LATER TODO: - add initial opengl constant for INT_MAX, and checks in render_group functions diff --git a/src/camera.cpp b/src/camera.cpp index 667d9dd..97187fe 100644 --- a/src/camera.cpp +++ b/src/camera.cpp @@ -12,6 +12,12 @@ #define NEAR_CLIP_PLANE 20.f +// forward declarations +inline glm::vec3 convertv3f(v3f v); + + +// interface + void cameraInitPerspective(camera& cam, glm::vec3 position, glm::vec3 target, glm::vec3 world_up) { @@ -65,6 +71,59 @@ cameraUnproject(camera& cam, int x, int y, int vp_width, int vp_height) return v; } +v3f +cameraCreateRay(camera& cam, v2i vp_coords, v2i vp_dims) +{ +#if 0 + float x = 2.f * vp_coords.x / vp_dims.x - 1.f; + float y = 2.f * vp_coords.y / vp_dims.y - 1.f; + glm::vec4 ray_start_ndc = glm::vec4(x, y, -1.f, 1.f); + glm::vec4 ray_end_ndc = glm::vec4(x, y, 0, 1.f); + + glm::mat4 M = glm::inverse(cam.projection * cam.view); + glm::vec4 ray_start_world = M * ray_start_ndc; + glm::vec4 ray_end_world = M * ray_end_ndc; + ray_start_world /= ray_start_world.w; + ray_end_world /= ray_end_world.w; + + glm::vec3 ray_origin = glm::vec3(ray_start_world); + glm::vec3 ray_dir = glm::normalize(glm::vec3(ray_end_world - ray_start_world)); + return v3f(ray_dir.x, ray_dir.y, ray_dir.z); +#else + // http://antongerdelan.net/opengl/raycasting.html + float x = 2.f * vp_coords.x / vp_dims.x - 1.f; + float y = 2.f * vp_coords.y / vp_dims.y - 1.f; + glm::vec4 ray_clip = glm::vec4(x, y, -1.f, 1.f); + glm::vec4 ray_eye = glm::inverse(cam.projection) * ray_clip; + ray_eye = glm::vec4(ray_eye.x, ray_eye.y, -1.f, 0); // NOTE: reset as ray + glm::vec4 ray_world = glm::normalize(glm::inverse(cam.view) * ray_eye); + return v3f(ray_world.x, ray_world.y, ray_world.z); +#endif +} + +bool +cameraIntersectPlane(camera& cam, v3f ray, v3f plane_origin, v3f plane_normal, v3f& intersection) +{ + glm::vec3 c_o = cam.position; + glm::vec3 r = convertv3f(ray); + //glm::vec3 r = cam.forward; + glm::vec3 p_o = convertv3f(plane_origin); + glm::vec3 p_n = convertv3f(plane_normal); + float divisor = glm::dot(r, p_n); + + if (divisor <= 0.000001f && divisor >= -0.000001f) // NOTE: ray and plane are co-planar + return false; +#if 1 + float distance = glm::dot((p_o - c_o), p_n) / divisor; + glm::vec3 xsect = c_o + (r * distance); +#else + float distance = glm::dot(n, (p_o - c_o)) / glm::dot(n, r); + glm::vec3 xsect = c_o + r * distance; +#endif + intersection = v3f(xsect.x, xsect.y, xsect.z); + return true; +} + void cameraMove(camera& cam, bool up, bool left, bool down, bool right, bool forward, bool backward) { @@ -143,3 +202,11 @@ cameraRoll(camera& cam, bool CW, bool CCW) cam.view *= m; cam.MVP = cam.projection * cam.view * cam.model; } + +// internal + +inline glm::vec3 +convertv3f(v3f v) +{ + return glm::vec3(v.x, v.y, v.z); +} diff --git a/src/camera.h b/src/camera.h index 143194a..b545645 100644 --- a/src/camera.h +++ b/src/camera.h @@ -31,7 +31,15 @@ enum projection_type v2f cameraUnproject(camera& cam, int x, int y, int vp_width, int vp_height); + +v3f cameraCreateRay(camera& cam, v2i vp_coords, v2i vp_dims); + +bool cameraIntersectPlane(camera& cam, v3f ray, v3f plane_origin, v3f plane_normal, v3f& intersection); + void cameraInitPerspective(camera& cam, glm::vec3 position, glm::vec3 target, glm::vec3 world_up); + void cameraMove(camera& cam, bool up, bool left, bool down, bool right, bool forward, bool backward); + void cameraRotate(camera& cam, int32 xrel, int32 yrel); + void cameraRoll(camera& cam, bool CW, bool CCW); diff --git a/src/gooey.cpp b/src/gooey.cpp index 355356a..7f3b965 100644 --- a/src/gooey.cpp +++ b/src/gooey.cpp @@ -147,6 +147,10 @@ renderHexgridWindow(hexgrid& grid, ImGuiWindowFlags window_flags) grid.draw_mode = LINE; if (ImGui::RadioButton("Cone Fill", (grid.draw_mode == CONE_FILL))) grid.draw_mode = CONE_FILL; + if (ImGui::RadioButton("Add Hexes", (grid.draw_mode == ADD_HEXES))) + grid.draw_mode = ADD_HEXES; + if (ImGui::RadioButton("Remove Hexes", (grid.draw_mode == REMOVE_HEXES))) + grid.draw_mode = REMOVE_HEXES; ImGui::Separator(); ImGui::Text("is_selecting"); @@ -166,9 +170,6 @@ renderHexgridWindow(hexgrid& grid, ImGuiWindowFlags window_flags) } ImGui::Separator(); - ImGui::Button("Add Hex"); - ImGui::SameLine(); ImGui::Button("Remove Hex"); - ImGui::TextWrapped("TODO: need to add more state to hexgame.cpp for adding/removing hexes, and modifying entities"); ImGui::End(); } diff --git a/src/hexgame.cpp b/src/hexgame.cpp index 5115a44..0fa9111 100644 --- a/src/hexgame.cpp +++ b/src/hexgame.cpp @@ -90,6 +90,10 @@ init() // TODO: I think this is necessary because we're using calloc to initialize gamestate // which contains the hashtable. g_game_state->grid.hex_map = std::unordered_map(); + // NOTE: testing hex_add + g_game_state->grid.draw_mode = ADD_HEXES; + // TODO: maybe add this as an option to scene json? + g_game_state->grid.normal = v3f(0.f, 0.f, 1.f); // init global render state g_render_state = UTIL_ALLOC(1, render_state); @@ -170,37 +174,41 @@ startDrawHelper(int32 x, int32 y) void handleMouseDown(SDL_MouseButtonEvent &e) { + game_state* gs = g_game_state; + render_state* rs = g_render_state; v2i coords = mapMouseToViewport(e.x, e.y); + v2i dims = rs->viewport_dims; - switch (g_game_state->grid.draw_mode) - { + switch (gs->grid.draw_mode) { case FILL: startDrawHelper(coords.x, coords.y); break; - case LINE: startDrawHelper(coords.x, coords.y); break; - case CONE_FILL: startDrawHelper(coords.x, coords.y); break; - case PATHFINDING: break; - + case ADD_HEXES: { + v3f ray = cameraCreateRay(rs->cam, coords, dims); + v3f xsect; + if (cameraIntersectPlane(rs->cam, ray, gs->grid.position, gs->grid.normal, xsect)) + hgAddHex(gs->grid, xsect); + break; + } case NONE: - // fall through default: - { - v2i dims = g_render_state->viewport_dims; - v2f v = cameraUnproject(g_render_state->cam, coords.x, coords.y, dims.x, dims.y); - hex_info *hex = hgGetSingleHex(g_game_state->grid, v.x, v.y); + v2f v = cameraUnproject(rs->cam, coords.x, coords.y, dims.x, dims.y); + hex_info *hex = hgGetSingleHex(gs->grid, v.x, v.y); + if (hex) { hex->selected = !hex->selected; - g_game_state->grid.start_hex = g_game_state->grid.current_hex = hex; + gs->grid.start_hex = gs->grid.current_hex = hex; } - }break; + + break; } } @@ -211,25 +219,19 @@ handleMouseMove(SDL_MouseMotionEvent &e) v2i dims = g_render_state->viewport_dims; v2f v = cameraUnproject(g_render_state->cam, coords.x, coords.y, dims.x, dims.y); - switch (g_game_state->grid.draw_mode) - { + switch (g_game_state->grid.draw_mode) { case FILL: hgUpdateHexFill(g_game_state->grid, v.x, v.y); break; - case LINE: hgUpdateHexLineDraw(g_game_state->grid, v.x, v.y); break; - case CONE_FILL: hgUpdateHexConeFill(g_game_state->grid, v.x, v.y, CONE_ANGLE, g_polygon_select_vertices); break; - case PATHFINDING: break; - case NONE: - // fall through default: break; } @@ -241,22 +243,19 @@ processSDLEvents() SDL_Event e; bool run = true; - while (SDL_PollEvent(&e)) - { + while (SDL_PollEvent(&e)) { // let gooey have event // TODO: need to check for both io.WantCaptureKeyboard and io.WantCaptureMouse // to fix bug with 'ESC' not passing through while in imgui bool gooey_wants = gooProcessEvent(e); game_state* gs = g_game_state; - switch (e.type) - { + switch (e.type) { case SDL_QUIT: run = false; break; case SDL_KEYDOWN: - switch (e.key.keysym.sym) - { + switch (e.key.keysym.sym) { case SDLK_ESCAPE: run = false; break; case SDLK_e: gs->is_moveforward = true; break; case SDLK_s: gs->is_moveleft = true; break; @@ -269,8 +268,7 @@ processSDLEvents() } break; case SDL_KEYUP: - switch (e.key.keysym.sym) - { + switch (e.key.keysym.sym) { case SDLK_e: gs->is_moveforward = false; break; case SDLK_s: gs->is_moveleft = false; break; case SDLK_d: gs->is_movebackward = false; break; @@ -282,22 +280,17 @@ processSDLEvents() } break; case SDL_MOUSEBUTTONDOWN: - if (!gooey_wants) - { - if (e.button.button == SDL_BUTTON_LEFT) - { + if (!gooey_wants) { + if (e.button.button == SDL_BUTTON_LEFT) { gs->grid.is_selecting = true; handleMouseDown(e.button); - } - else if (e.button.button == SDL_BUTTON_RIGHT) - { + } else if (e.button.button == SDL_BUTTON_RIGHT) { gs->is_camera_rotate = true; } } break; case SDL_MOUSEBUTTONUP: - if (!gooey_wants) - { + if (!gooey_wants) { if (e.button.button == SDL_BUTTON_LEFT) gs->grid.is_selecting = false; else if (e.button.button == SDL_BUTTON_RIGHT) diff --git a/src/hexgrid.cpp b/src/hexgrid.cpp index 12a60d6..3e3c132 100644 --- a/src/hexgrid.cpp +++ b/src/hexgrid.cpp @@ -5,7 +5,9 @@ // forward declarations -bool createHexagonGrid(hexgrid& hg); +hex_info createHexInfo(hexgrid& hg, int q, int r); +void createHexagonGrid(hexgrid& hg); + // interface @@ -13,8 +15,7 @@ bool hgCreateHexes(hexgrid& hg) { if (hg.gridT == HEXAGON) { - if (!createHexagonGrid(hg)) - return false; + createHexagonGrid(hg); } else { LOG(ERROR) << "Unhandled grid type\n"; return false; @@ -23,6 +24,32 @@ hgCreateHexes(hexgrid& hg) return true; } +// TODO: testing add/remove hex interface + +bool +hgAddHex(hexgrid& hg, v3f hex_coords) +{ + hex_info* hxi = hgGetSingleHex(hg, hex_coords.x, hex_coords.y); + if (hxi) { + hxi->selected = true; + } else { + Hex h = hex_round(pixel_to_hex(hg.hexlib_layout, Point(hex_coords.x, hex_coords.y))); + hex_info new_hex = createHexInfo(hg, h.q, h.r); + // TODO: need to reseize the GL buffers for filled hexes & hex lines here + //hg.hex_map.insert({h, new_hex}); + } + + return false; +} + +bool +hgRemoveHex(hexgrid& hg, v3i hex_coords) +{ + return false; +} + +// ------------------- + hex_info* hgGetSingleHex(hexgrid& hg, real32 x, real32 y) { @@ -157,7 +184,23 @@ hgUpdateUVBuffer(hexgrid& hg, float* uv_buffer, uint buf_len) // internal -bool +hex_info +createHexInfo(hexgrid& hg, int q, int r) +{ + hex_info hxi; + hxi.hexID = (int32) hg.hex_map.size(); + hxi.selected = false; + hxi.hex.q = q; hxi.hex.r = r; hxi.hex.s = -q-r; + Point p = hex_to_pixel(hg.hexlib_layout, hxi.hex); + hxi.XPos = p.x; + hxi.YPos = p.y; + hxi.vertices = polygon_corners(hg.hexlib_layout, hxi.hex); + hxi.vertices.shrink_to_fit(); + + return hxi; +} + +void createHexagonGrid(hexgrid& hg) { int hr = hg.hex_radius; @@ -168,15 +211,7 @@ createHexagonGrid(hexgrid& hg) int r2 = std::min(hr, -q + hr); for (int r = r1; r <= r2; r++) { - hex_info hxi; - hxi.hexID = (int32) hg.hex_map.size(); - hxi.selected = false; - hxi.hex.q = q; hxi.hex.r = r; hxi.hex.s = -q-r; - Point p = hex_to_pixel(hg.hexlib_layout, hxi.hex); - hxi.XPos = p.x; - hxi.YPos = p.y; - hxi.vertices = polygon_corners(hg.hexlib_layout, hxi.hex); - hxi.vertices.shrink_to_fit(); + hex_info hxi = createHexInfo(hg, q, r); // NOTE: testing sparse grid if ((q > -2 && q < 2) && (r > -2 && r < 2)) { @@ -187,6 +222,4 @@ createHexagonGrid(hexgrid& hg) ///////////// } } - - return true; } diff --git a/src/hexgrid.h b/src/hexgrid.h index 9a08a89..3100bcd 100644 --- a/src/hexgrid.h +++ b/src/hexgrid.h @@ -16,7 +16,9 @@ enum HexDrawMode FILL, LINE, CONE_FILL, - PATHFINDING + PATHFINDING, + ADD_HEXES, + REMOVE_HEXES }; // TODO: implement more hexgrid types @@ -55,6 +57,7 @@ struct hexgrid Layout hexlib_layout; Orientation hexlib_orientation; v3f position; + v3f normal; uint hex_size; uint hex_radius; @@ -72,6 +75,14 @@ struct hexgrid bool hgCreateHexes(hexgrid& hg); +// TODO: testing add/remove hex interface + +bool hgAddHex(hexgrid& hg, v3f hex_coords); + +bool hgRemoveHex(hexgrid& hg, v3i hex_coords); + +// ------------------- + hex_info* hgGetSingleHex(hexgrid& hg, real32 x, real32 y); void hgResetHexes(hexgrid& hg); diff --git a/src/util.h b/src/util.h index 62b2b35..6432408 100644 --- a/src/util.h +++ b/src/util.h @@ -41,6 +41,13 @@ struct v3f real64 z; }; +struct v3i +{ + int32 x; + int32 y; + int32 z; +}; + struct v4i { int32 x0;