diff --git a/TODO b/TODO.txt similarity index 100% rename from TODO rename to TODO.txt diff --git a/cloc b/cloc deleted file mode 100644 index d9b8137..0000000 --- a/cloc +++ /dev/null @@ -1,71 +0,0 @@ -include/application.h -include/components/collider.h -include/components/custom.h -include/components/mesh_renderable.h -include/components/transform.h -include/components/ui_renderable.h -include/ecs.h -include/engine_api.h -include/event_system.h -include/gfx.h -include/gfx_device.h -include/input_manager.h -include/inputs/keyboard.h -include/inputs/mouse.h -include/log.h -include/logger.h -include/renderer.h -include/resource_manager.h -include/resources/font.h -include/resources/material.h -include/resources/mesh.h -include/resources/shader.h -include/resources/texture.h -include/scene.h -include/scene_manager.h -include/systems/collisions.h -include/systems/custom_behaviour.h -include/systems/mesh_render_system.h -include/systems/transform.h -include/systems/ui_render_system.h -include/util.h -include/util/files.h -include/util/gltf_loader.h -include/util/model_loader.h -include/window.h -src/application.cpp -src/ecs.cpp -src/gfx_device_vulkan.cpp -src/input_manager.cpp -src/renderer.cpp -src/resources/font.cpp -src/resources/material.cpp -src/resources/mesh.cpp -src/resources/shader.cpp -src/resources/texture.cpp -src/scene.cpp -src/scene_manager.cpp -src/systems/collisions.cpp -src/systems/custom_behaviour.cpp -src/systems/mesh_render_system.cpp -src/systems/transform.cpp -src/systems/ui_render_system.cpp -src/util/files.cpp -src/util/gltf_loader.cpp -src/util/model_loader.cpp -src/vulkan/device.cpp -src/vulkan/device.h -src/vulkan/gpu_allocator.cpp -src/vulkan/gpu_allocator.h -src/vulkan/instance.cpp -src/vulkan/instance.h -src/vulkan/swapchain.cpp -src/vulkan/swapchain.h -src/window.cpp -test/src/camera_controller.cpp -test/src/camera_controller.hpp -test/src/game.cpp -test/src/game.hpp -test/src/main.cpp -test/src/meshgen.cpp -test/src/meshgen.hpp diff --git a/include/application.h b/include/application.h index efdcc00..bffde9b 100644 --- a/include/application.h +++ b/include/application.h @@ -66,6 +66,8 @@ class Application { std::string GetResourcePath(const std::string relative_path) const { return (resources_path_ / relative_path).string(); } + std::vector debug_lines{}; + private: std::unique_ptr window_; std::unique_ptr input_manager_; diff --git a/include/renderer.h b/include/renderer.h index 651be90..6b3945c 100644 --- a/include/renderer.h +++ b/include/renderer.h @@ -27,6 +27,7 @@ struct UniformDescriptor { struct Line { glm::vec3 pos1; glm::vec3 pos2; + glm::vec3 color; }; class Renderer : private ApplicationComponent { diff --git a/include/systems/collisions.h b/include/systems/collisions.h index 57040cd..7c76ea8 100644 --- a/include/systems/collisions.h +++ b/include/systems/collisions.h @@ -18,11 +18,14 @@ struct Ray { struct Raycast { glm::vec3 location; - Entity hit_entity; + glm::vec3 normal; + Entity hit_entity; // broken float distance; bool hit; }; +enum class AABBSide { Left, Right, Bottom, Top, Front, Back }; + class CollisionSystem : public System { public: CollisionSystem(Scene* scene); @@ -59,7 +62,14 @@ class CollisionSystem : public System { size_t colliders_size_last_update_ = 0; size_t colliders_size_now_ = 0; - bool RaycastTreeNode(const Ray& ray, const BiTreeNode& node, glm::vec3& location, float& t, Entity& object_index); + struct RaycastTreeNodeResult { + glm::vec3 location; + Entity object_index; + AABBSide side; + float t; + bool hit; // if this is false, all other values are undefined + }; + RaycastTreeNodeResult RaycastTreeNode(const Ray& ray, const BiTreeNode& node); static int BuildNode(std::vector& prims, std::vector& tree_nodes); }; diff --git a/res/engine/shaders/debug.frag b/res/engine/shaders/debug.frag index 7dd43fe..2b10c24 100644 --- a/res/engine/shaders/debug.frag +++ b/res/engine/shaders/debug.frag @@ -1,7 +1,9 @@ #version 450 +layout(location = 0) in vec3 inColor; + layout(location = 0) out vec4 outColor; void main() { - outColor = vec4(1.0); + outColor = vec4(inColor, 1.0); } \ No newline at end of file diff --git a/res/engine/shaders/debug.vert b/res/engine/shaders/debug.vert index f2e5b6a..8503945 100644 --- a/res/engine/shaders/debug.vert +++ b/res/engine/shaders/debug.vert @@ -2,9 +2,13 @@ layout( push_constant ) uniform Constants { vec4 positions[2]; + vec3 color; } constants; +layout(location = 0) out vec3 color; + void main() { + color = constants.color; gl_Position = constants.positions[gl_VertexIndex]; gl_Position.y *= -1.0; } diff --git a/res/engine/shaders/fancy.frag b/res/engine/shaders/fancy.frag index 7d5f27d..2583306 100644 --- a/res/engine/shaders/fancy.frag +++ b/res/engine/shaders/fancy.frag @@ -29,8 +29,8 @@ float GGXDist(float alpha_2, float N_dot_H) { void main() { const vec3 metallic_roughness = vec3(texture(materialSetMetallicRoughnessSampler, fragUV)); - const float metallic = metallic_roughness.g; - const float roughness = metallic_roughness.b; + const float metallic = metallic_roughness.b; + const float roughness = metallic_roughness.g; // roughness of zero is completely black? const float roughness_2 = roughness * roughness; const vec3 light_colour = vec3(1.0, 1.0, 1.0) * 2.4; diff --git a/res/engine/shaders/fancy.vert b/res/engine/shaders/fancy.vert index ec61072..d1c79d1 100644 --- a/res/engine/shaders/fancy.vert +++ b/res/engine/shaders/fancy.vert @@ -34,7 +34,7 @@ void main() { fragUV = inUV; fragPosTangentSpace = worldToTangentSpace * vec3(worldPosition); fragViewPosTangentSpace = worldToTangentSpace * vec3(inverse(frameSetUniformBuffer.view) * vec4(0.0, 0.0, 0.0, 1.0)); - fragLightPosTangentSpace = worldToTangentSpace * vec3(10000.0, 0000.0, 59000.0); + fragLightPosTangentSpace = worldToTangentSpace * vec3(10000.0, 0000.0, 20000.0); gl_Position.y *= -1.0; } diff --git a/src/application.cpp b/src/application.cpp index 20672e0..970292e 100644 --- a/src/application.cpp +++ b/src/application.cpp @@ -168,7 +168,7 @@ Application::Application(const char* appName, const char* appVersion, gfx::Graph GetResourceManager()->AddPersistent("builtin.normal", std::move(normalTexture)); } { - const uint8_t pixel[4] = {255, 0, 127, 255}; + const uint8_t pixel[4] = {255, 127, 0, 255}; // AO, roughness, metallic gfx::SamplerInfo samplerInfo{}; samplerInfo.minify = gfx::Filter::kNearest; samplerInfo.magnify = gfx::Filter::kNearest; @@ -208,7 +208,8 @@ void Application::GameLoop() struct DebugMenuState { bool menu_active = false; - bool show_info_window = true; + bool show_aabbs = false; + bool show_info_window = false; } debug_menu_state; // single-threaded game loop @@ -245,6 +246,7 @@ void Application::GameLoop() if (ImGui::Begin("debugMenu", 0)) { ImGui::Text("Test!"); ImGui::Text("FPS: %.3f", std::roundf(avg_fps)); + ImGui::Checkbox("Show AABBs?", &debug_menu_state.show_aabbs); } ImGui::End(); } @@ -285,8 +287,73 @@ void Application::GameLoop() const RenderList* static_list = nullptr; const RenderList* dynamic_list = nullptr; glm::mat4 camera_transform{1.0f}; - std::vector debug_lines{}; if (scene) { + if (debug_menu_state.show_aabbs) { + if (CollisionSystem* colsys = scene->GetSystem()) { + for (const auto& node : colsys->bvh_) { + if (node.type1 == CollisionSystem::BiTreeNode::Type::Entity) { + const glm::vec3 col = + (node.type1 == CollisionSystem::BiTreeNode::Type::BoundingVolume) ? glm::vec3{ 1.0f, 0.0f, 0.0f } : glm::vec3{ 0.0f, 1.0f, 0.0f }; + Line line1{ glm::vec3{node.box1.min.x, node.box1.min.y, node.box1.min.z}, glm::vec3{node.box1.max.x, node.box1.min.y, node.box1.min.z}, col }; + debug_lines.push_back(line1); + Line line2{ glm::vec3{node.box1.min.x, node.box1.min.y, node.box1.min.z}, glm::vec3{node.box1.min.x, node.box1.max.y, node.box1.min.z}, col }; + debug_lines.push_back(line2); + Line line3{ glm::vec3{node.box1.max.x, node.box1.max.y, node.box1.min.z}, glm::vec3{node.box1.max.x, node.box1.min.y, node.box1.min.z}, col }; + debug_lines.push_back(line3); + Line line4{ glm::vec3{node.box1.max.x, node.box1.max.y, node.box1.min.z}, glm::vec3{node.box1.min.x, node.box1.max.y, node.box1.min.z}, col }; + debug_lines.push_back(line4); + + Line line5{ glm::vec3{node.box1.min.x, node.box1.min.y, node.box1.min.z}, glm::vec3{node.box1.min.x, node.box1.min.y, node.box1.max.z}, col }; + debug_lines.push_back(line5); + Line line6{ glm::vec3{node.box1.min.x, node.box1.max.y, node.box1.min.z}, glm::vec3{node.box1.min.x, node.box1.max.y, node.box1.max.z}, col }; + debug_lines.push_back(line6); + Line line7{ glm::vec3{node.box1.max.x, node.box1.min.y, node.box1.min.z}, glm::vec3{node.box1.max.x, node.box1.min.y, node.box1.max.z}, col }; + debug_lines.push_back(line7); + Line line8{ glm::vec3{node.box1.max.x, node.box1.max.y, node.box1.min.z}, glm::vec3{node.box1.max.x, node.box1.max.y, node.box1.max.z}, col }; + debug_lines.push_back(line8); + + Line line9{ glm::vec3{node.box1.min.x, node.box1.min.y, node.box1.max.z}, glm::vec3{node.box1.max.x, node.box1.min.y, node.box1.max.z}, col }; + debug_lines.push_back(line9); + Line line10{ glm::vec3{node.box1.min.x, node.box1.min.y, node.box1.max.z}, glm::vec3{node.box1.min.x, node.box1.max.y, node.box1.max.z}, col }; + debug_lines.push_back(line10); + Line line11{ glm::vec3{node.box1.max.x, node.box1.max.y, node.box1.max.z}, glm::vec3{node.box1.max.x, node.box1.min.y, node.box1.max.z}, col }; + debug_lines.push_back(line11); + Line line12{ glm::vec3{node.box1.max.x, node.box1.max.y, node.box1.max.z}, glm::vec3{node.box1.min.x, node.box1.max.y, node.box1.max.z}, col }; + debug_lines.push_back(line12); + } + if (node.type2 == CollisionSystem::BiTreeNode::Type::Entity) { + const glm::vec3 col = + (node.type2 == CollisionSystem::BiTreeNode::Type::BoundingVolume) ? glm::vec3{ 1.0f, 0.0f, 0.0f } : glm::vec3{ 0.0f, 1.0f, 0.0f }; + Line line1{ glm::vec3{node.box2.min.x, node.box2.min.y, node.box2.min.z}, glm::vec3{node.box2.max.x, node.box2.min.y, node.box2.min.z}, col }; + debug_lines.push_back(line1); + Line line2{ glm::vec3{node.box2.min.x, node.box2.min.y, node.box2.min.z}, glm::vec3{node.box2.min.x, node.box2.max.y, node.box2.min.z}, col }; + debug_lines.push_back(line2); + Line line3{ glm::vec3{node.box2.max.x, node.box2.max.y, node.box2.min.z}, glm::vec3{node.box2.max.x, node.box2.min.y, node.box2.min.z}, col }; + debug_lines.push_back(line3); + Line line4{ glm::vec3{node.box2.max.x, node.box2.max.y, node.box2.min.z}, glm::vec3{node.box2.min.x, node.box2.max.y, node.box2.min.z}, col }; + debug_lines.push_back(line4); + + Line line5{ glm::vec3{node.box2.min.x, node.box2.min.y, node.box2.min.z}, glm::vec3{node.box2.min.x, node.box2.min.y, node.box2.max.z}, col }; + debug_lines.push_back(line5); + Line line6{ glm::vec3{node.box2.min.x, node.box2.max.y, node.box2.min.z}, glm::vec3{node.box2.min.x, node.box2.max.y, node.box2.max.z}, col }; + debug_lines.push_back(line6); + Line line7{ glm::vec3{node.box2.max.x, node.box2.min.y, node.box2.min.z}, glm::vec3{node.box2.max.x, node.box2.min.y, node.box2.max.z}, col }; + debug_lines.push_back(line7); + Line line8{ glm::vec3{node.box2.max.x, node.box2.max.y, node.box2.min.z}, glm::vec3{node.box2.max.x, node.box2.max.y, node.box2.max.z}, col }; + debug_lines.push_back(line8); + + Line line9{ glm::vec3{node.box2.min.x, node.box2.min.y, node.box2.max.z}, glm::vec3{node.box2.max.x, node.box2.min.y, node.box2.max.z}, col }; + debug_lines.push_back(line9); + Line line10{ glm::vec3{node.box2.min.x, node.box2.min.y, node.box2.max.z}, glm::vec3{node.box2.min.x, node.box2.max.y, node.box2.max.z}, col }; + debug_lines.push_back(line10); + Line line11{ glm::vec3{node.box2.max.x, node.box2.max.y, node.box2.max.z}, glm::vec3{node.box2.max.x, node.box2.min.y, node.box2.max.z}, col }; + debug_lines.push_back(line11); + Line line12{ glm::vec3{node.box2.max.x, node.box2.max.y, node.box2.max.z}, glm::vec3{node.box2.min.x, node.box2.max.y, node.box2.max.z}, col }; + debug_lines.push_back(line12); + } + } + } + } camera_transform = scene->GetComponent(scene->GetEntity("camera"))->world_matrix; auto mesh_render_system = scene->GetSystem(); static_list = mesh_render_system->GetStaticRenderList(); @@ -294,6 +361,7 @@ void Application::GameLoop() } renderer_->PreRender(window()->GetWindowResized(), camera_transform); renderer_->Render(static_list, dynamic_list, debug_lines); + debug_lines.clear(); // gets remade every frame :0 /* poll events */ window_->GetInputAndEvents(); diff --git a/src/renderer.cpp b/src/renderer.cpp index ab6acf5..996b8c1 100644 --- a/src/renderer.cpp +++ b/src/renderer.cpp @@ -121,24 +121,32 @@ void Renderer::Render(const RenderList* static_list, const RenderList* dynamic_l } } + struct DebugPush { + glm::vec4 pos1; + glm::vec4 pos2; + glm::vec3 color; + }; + // draw debug shit here device_->CmdBindPipeline(draw_buffer, debug_rendering_things_.pipeline); - glm::vec4 debug_positions[2] = {}; + DebugPush push{}; for (const Line& l : debug_lines) { - debug_positions[0] = global_uniform.uniform_buffer_data.data * frame_uniform.uniform_buffer_data.data * glm::vec4(l.pos1, 1.0f); - debug_positions[1] = global_uniform.uniform_buffer_data.data * frame_uniform.uniform_buffer_data.data * glm::vec4(l.pos2, 1.0f); - device_->CmdPushConstants(draw_buffer, debug_rendering_things_.pipeline, 0, sizeof(glm::vec4) * 2, debug_positions); + push.pos1 = global_uniform.uniform_buffer_data.data * frame_uniform.uniform_buffer_data.data * glm::vec4(l.pos1, 1.0f); + push.pos2 = global_uniform.uniform_buffer_data.data * frame_uniform.uniform_buffer_data.data * glm::vec4(l.pos2, 1.0f); + push.color = l.color; + device_->CmdPushConstants(draw_buffer, debug_rendering_things_.pipeline, 0, sizeof(DebugPush), &push); device_->CmdDraw(draw_buffer, 2, 1, 0, 0); } // also make a lil crosshair - debug_positions[0] = glm::vec4(-0.05f, 0.0f, 0.0f, 1.0f); - debug_positions[1] = glm::vec4(0.05f, 0.0f, 0.0f, 1.0f); - device_->CmdPushConstants(draw_buffer, debug_rendering_things_.pipeline, 0, sizeof(glm::vec4) * 2, debug_positions); + push.color = glm::vec3{ 1.0f, 1.0f, 1.0f }; + push.pos1 = glm::vec4(-0.05f, 0.0f, 0.0f, 1.0f); + push.pos2 = glm::vec4(0.05f, 0.0f, 0.0f, 1.0f); + device_->CmdPushConstants(draw_buffer, debug_rendering_things_.pipeline, 0, sizeof(DebugPush), &push); device_->CmdDraw(draw_buffer, 2, 1, 0, 0); - debug_positions[0] = glm::vec4(0.0f, -0.05f, 0.0f, 1.0f); - debug_positions[1] = glm::vec4(0.0f, 0.05f, 0.0f, 1.0f); - device_->CmdPushConstants(draw_buffer, debug_rendering_things_.pipeline, 0, sizeof(glm::vec4) * 2, debug_positions); + push.pos1 = glm::vec4(0.0f, -0.05f, 0.0f, 1.0f); + push.pos2 = glm::vec4(0.0f, 0.05f, 0.0f, 1.0f); + device_->CmdPushConstants(draw_buffer, debug_rendering_things_.pipeline, 0, sizeof(DebugPush), &push); device_->CmdDraw(draw_buffer, 2, 1, 0, 0); device_->CmdRenderImguiDrawData(draw_buffer, ImGui::GetDrawData()); diff --git a/src/systems/collisions.cpp b/src/systems/collisions.cpp index 74192f9..0d8eea2 100644 --- a/src/systems/collisions.cpp +++ b/src/systems/collisions.cpp @@ -29,7 +29,8 @@ static float GetBoxArea(const AABB& box) } // returns true on hit and tmin -static std::pair RayBoxIntersection(const Ray& ray, const AABB& box, float t) +// also modifies 'side' reference +static std::pair RayBoxIntersection(const Ray& ray, const AABB& box, float t, AABBSide& side) { // Thank you https://tavianator.com/cgit/dimension.git/tree/libdimension/bvh/bvh.c glm::vec3 n_inv{1.0f / ray.direction.x, 1.0f / ray.direction.y, 1.0f / ray.direction.z}; @@ -52,6 +53,20 @@ static std::pair RayBoxIntersection(const Ray& ray, const AABB& box tmin = fmaxf(tmin, fminf(tz1, tz2)); tmax = fminf(tmax, fmaxf(tz1, tz2)); + side = AABBSide::Left; + if (tmin == tx2) + side = AABBSide::Right; + else if (tmin == tx1) + side = AABBSide::Left; + else if (tmin == ty2) + side = AABBSide::Back; + else if (tmin == ty1) + side = AABBSide::Front; + else if (tmin == tz2) + side = AABBSide::Top; + else if (tmin == tz1) + side = AABBSide::Bottom; + return std::make_pair((tmax >= fmaxf(0.0, tmin) && tmin < t), tmin); } @@ -117,13 +132,40 @@ void CollisionSystem::OnUpdate(float ts) Raycast CollisionSystem::GetRaycast(Ray ray) { + ray.direction = glm::normalize(ray.direction); + Raycast res{}; res.hit = false; - ray.direction = glm::normalize(ray.direction); - if (!bvh_.empty()) { - res.hit = RaycastTreeNode(ray, bvh_.back(), res.location, res.distance, res.hit_entity); + const RaycastTreeNodeResult tree_node_cast_res = RaycastTreeNode(ray, bvh_.back()); + if (tree_node_cast_res.hit) { + res.hit = true; + res.distance = tree_node_cast_res.t; + res.location = tree_node_cast_res.location; + res.hit_entity = tree_node_cast_res.object_index; + // find normal + switch (tree_node_cast_res.side) { + case AABBSide::Left: + res.normal = glm::vec3{-1.0, 0.0f, 0.0f}; + break; + case AABBSide::Right: + res.normal = glm::vec3{1.0, 0.0f, 0.0f}; + break; + case AABBSide::Bottom: + res.normal = glm::vec3{0.0, 0.0f, -1.0f}; + break; + case AABBSide::Top: + res.normal = glm::vec3{0.0, 0.0f, 1.0f}; + break; + case AABBSide::Front: + res.normal = glm::vec3{0.0, -1.0f, 0.0f}; + break; + case AABBSide::Back: + res.normal = glm::vec3{0.0, 1.0f, 0.0f}; + break; + } + } } return res; @@ -139,38 +181,17 @@ int CollisionSystem::BuildNode(std::vector& prims, std::vector, 3> centroid_tests{ - [](const PrimitiveInfo& p1, const PrimitiveInfo& p2) -> bool { - return (p1.centroid.x < p2.centroid.x); - }, - [](const PrimitiveInfo& p1, const PrimitiveInfo& p2) -> bool { - return (p1.centroid.y < p2.centroid.y); - }, - [](const PrimitiveInfo& p1, const PrimitiveInfo& p2) -> bool { - return (p1.centroid.z < p2.centroid.z); - } - }; + [](const PrimitiveInfo& p1, const PrimitiveInfo& p2) -> bool { return (p1.centroid.x < p2.centroid.x); }, + [](const PrimitiveInfo& p1, const PrimitiveInfo& p2) -> bool { return (p1.centroid.y < p2.centroid.y); }, + [](const PrimitiveInfo& p1, const PrimitiveInfo& p2) -> bool { return (p1.centroid.z < p2.centroid.z); }}; std::array, 3> box_min_tests{ - [](const PrimitiveInfo& p1, const PrimitiveInfo& p2) -> bool { - return (p1.box.min.x < p2.box.min.x); - }, - [](const PrimitiveInfo& p1, const PrimitiveInfo& p2) -> bool { - return (p1.box.min.y < p2.box.min.y); - }, - [](const PrimitiveInfo& p1, const PrimitiveInfo& p2) -> bool { - return (p1.box.min.z < p2.box.min.z); - } - }; + [](const PrimitiveInfo& p1, const PrimitiveInfo& p2) -> bool { return (p1.box.min.x < p2.box.min.x); }, + [](const PrimitiveInfo& p1, const PrimitiveInfo& p2) -> bool { return (p1.box.min.y < p2.box.min.y); }, + [](const PrimitiveInfo& p1, const PrimitiveInfo& p2) -> bool { return (p1.box.min.z < p2.box.min.z); }}; std::array, 3> box_max_tests{ - [](const PrimitiveInfo& p1, const PrimitiveInfo& p2) -> bool { - return (p1.box.max.x < p2.box.max.x); - }, - [](const PrimitiveInfo& p1, const PrimitiveInfo& p2) -> bool { - return (p1.box.max.y < p2.box.max.y); - }, - [](const PrimitiveInfo& p1, const PrimitiveInfo& p2) -> bool { - return (p1.box.max.z < p2.box.max.z); - } - }; + [](const PrimitiveInfo& p1, const PrimitiveInfo& p2) -> bool { return (p1.box.max.x < p2.box.max.x); }, + [](const PrimitiveInfo& p1, const PrimitiveInfo& p2) -> bool { return (p1.box.max.y < p2.box.max.y); }, + [](const PrimitiveInfo& p1, const PrimitiveInfo& p2) -> bool { return (p1.box.max.z < p2.box.max.z); }}; BiTreeNode node{}; @@ -180,7 +201,7 @@ int CollisionSystem::BuildNode(std::vector& prims, std::vector().infinity(); // surface area heuristic + float sah = std::numeric_limits().infinity(); // surface area heuristic // try along each axis for (int axis = 0; axis < 3; axis++) { @@ -190,79 +211,91 @@ int CollisionSystem::BuildNode(std::vector& prims, std::vector(&(std::min_element(prims.begin(), prims.begin() + i + 1, box_min_tests[axis])->box.min))[axis]; - float box1_main_max = reinterpret_cast(&(std::max_element(prims.begin(), prims.begin() + i + 1, box_max_tests[axis])->box.max))[axis]; - float box2_main_min = reinterpret_cast(&(std::min_element(prims.begin() + i + 1, prims.end(), box_min_tests[axis])->box.min))[axis]; - float box2_main_max = reinterpret_cast(&(std::max_element(prims.begin() + i + 1, prims.end(), box_max_tests[axis])->box.max))[axis]; + float box1_main_min = + reinterpret_cast(&(std::min_element(prims.begin(), prims.begin() + i + 1, box_min_tests[axis])->box.min))[axis]; + float box1_main_max = + reinterpret_cast(&(std::max_element(prims.begin(), prims.begin() + i + 1, box_max_tests[axis])->box.max))[axis]; + float box2_main_min = + reinterpret_cast(&(std::min_element(prims.begin() + i + 1, prims.end(), box_min_tests[axis])->box.min))[axis]; + float box2_main_max = + reinterpret_cast(&(std::max_element(prims.begin() + i + 1, prims.end(), box_max_tests[axis])->box.max))[axis]; - float box1_min1 = reinterpret_cast(&(std::min_element(prims.begin(), prims.begin() + i + 1, box_min_tests[other_axis1])->box.min))[other_axis1]; - float box1_max1 = reinterpret_cast(&(std::max_element(prims.begin(), prims.begin() + i + 1, box_max_tests[other_axis1])->box.max))[other_axis1]; - float box1_min2 = reinterpret_cast(&(std::min_element(prims.begin(), prims.begin() + i + 1, box_min_tests[other_axis2])->box.min))[other_axis2]; - float box1_max2 = reinterpret_cast(&(std::max_element(prims.begin(), prims.begin() + i + 1, box_max_tests[other_axis2])->box.max))[other_axis2]; + float box1_min1 = + reinterpret_cast(&(std::min_element(prims.begin(), prims.begin() + i + 1, box_min_tests[other_axis1])->box.min))[other_axis1]; + float box1_max1 = + reinterpret_cast(&(std::max_element(prims.begin(), prims.begin() + i + 1, box_max_tests[other_axis1])->box.max))[other_axis1]; + float box1_min2 = + reinterpret_cast(&(std::min_element(prims.begin(), prims.begin() + i + 1, box_min_tests[other_axis2])->box.min))[other_axis2]; + float box1_max2 = + reinterpret_cast(&(std::max_element(prims.begin(), prims.begin() + i + 1, box_max_tests[other_axis2])->box.max))[other_axis2]; - float box2_min1 = reinterpret_cast(&(std::min_element(prims.begin() + i + 1, prims.end(), box_min_tests[other_axis1])->box.min))[other_axis1]; - float box2_max1 = reinterpret_cast(&(std::max_element(prims.begin() + i + 1, prims.end(), box_max_tests[other_axis1])->box.max))[other_axis1]; - float box2_min2 = reinterpret_cast(&(std::min_element(prims.begin() + i + 1, prims.end(), box_min_tests[other_axis2])->box.min))[other_axis2]; - float box2_max2 = reinterpret_cast(&(std::max_element(prims.begin() + i + 1, prims.end(), box_max_tests[other_axis2])->box.max))[other_axis2]; + float box2_min1 = + reinterpret_cast(&(std::min_element(prims.begin() + i + 1, prims.end(), box_min_tests[other_axis1])->box.min))[other_axis1]; + float box2_max1 = + reinterpret_cast(&(std::max_element(prims.begin() + i + 1, prims.end(), box_max_tests[other_axis1])->box.max))[other_axis1]; + float box2_min2 = + reinterpret_cast(&(std::min_element(prims.begin() + i + 1, prims.end(), box_min_tests[other_axis2])->box.min))[other_axis2]; + float box2_max2 = + reinterpret_cast(&(std::max_element(prims.begin() + i + 1, prims.end(), box_max_tests[other_axis2])->box.max))[other_axis2]; AABB box1{}, box2{}; switch (axis) { - case 0: - // x - box1.min.x = box1_main_min; - box1.min.y = box1_min1; - box1.min.z = box1_min2; - box1.max.x = box1_main_max; - box1.max.y = box1_max1; - box1.max.z = box1_max2; - box2.min.x = box2_main_min; - box2.min.y = box2_min1; - box2.min.z = box2_min2; - box2.max.x = box2_main_max; - box2.max.y = box2_max1; - box2.max.z = box2_max2; - break; - case 1: - // y - box1.min.x = box1_min2; - box1.min.y = box1_main_min; - box1.min.z = box1_min1; - box1.max.x = box1_max2; - box1.max.y = box1_main_max; - box1.max.z = box1_max1; - box2.min.x = box2_min2; - box2.min.y = box2_main_min; - box2.min.z = box2_min1; - box2.max.x = box2_max2; - box2.max.y = box2_main_max; - box2.max.z = box2_max1; - break; - case 2: - // z - box1.min.x = box1_min1; - box1.min.y = box1_min2; - box1.min.z = box1_main_min; - box1.max.x = box1_max1; - box1.max.y = box1_max2; - box1.max.z = box1_main_max; - box2.min.x = box2_min1; - box2.min.y = box2_min2; - box2.min.z = box2_main_min; - box2.max.x = box2_max1; - box2.max.y = box2_max2; - box2.max.z = box2_main_max; - break; + case 0: + // x + box1.min.x = box1_main_min; + box1.min.y = box1_min1; + box1.min.z = box1_min2; + box1.max.x = box1_main_max; + box1.max.y = box1_max1; + box1.max.z = box1_max2; + box2.min.x = box2_main_min; + box2.min.y = box2_min1; + box2.min.z = box2_min2; + box2.max.x = box2_main_max; + box2.max.y = box2_max1; + box2.max.z = box2_max2; + break; + case 1: + // y + box1.min.x = box1_min2; + box1.min.y = box1_main_min; + box1.min.z = box1_min1; + box1.max.x = box1_max2; + box1.max.y = box1_main_max; + box1.max.z = box1_max1; + box2.min.x = box2_min2; + box2.min.y = box2_main_min; + box2.min.z = box2_min1; + box2.max.x = box2_max2; + box2.max.y = box2_main_max; + box2.max.z = box2_max1; + break; + case 2: + // z + box1.min.x = box1_min1; + box1.min.y = box1_min2; + box1.min.z = box1_main_min; + box1.max.x = box1_max1; + box1.max.y = box1_max2; + box1.max.z = box1_main_max; + box2.min.x = box2_min1; + box2.min.y = box2_min2; + box2.min.z = box2_main_min; + box2.max.x = box2_max1; + box2.max.y = box2_max2; + box2.max.z = box2_main_max; + break; } const float combined_surface_area = (GetBoxArea(box1) * (i + 1)) + (GetBoxArea(box2) * (prims.size() - (i + 1))); if (combined_surface_area < sah) { @@ -271,7 +304,7 @@ int CollisionSystem::BuildNode(std::vector& prims, std::vector& prims, std::vector& prims, std::vector::infinity(); float t2 = std::numeric_limits::infinity(); + AABBSide side1; + AABBSide side2; + if (node.type1 != Type::Empty) { - auto [is_hit, t] = RayBoxIntersection(ray, node.box1, T); + auto [is_hit, t] = RayBoxIntersection(ray, node.box1, T, side1); is_hit1 = is_hit; t1 = t; } if (node.type2 != Type::Empty) { - auto [is_hit, t] = RayBoxIntersection(ray, node.box2, T); + auto [is_hit, t] = RayBoxIntersection(ray, node.box2, T, side2); is_hit2 = is_hit; t2 = t; } // possible outcomes: - // neither hit - if (!is_hit1 && !is_hit2) return false; - - // when both hit - if (is_hit1 && is_hit2) { + if (!is_hit1 && !is_hit2) { + res.hit = false; + return res; + } + else if (is_hit1 && is_hit2) { // if 1 is a BV and 2 a gameobject, see if gameobject is in front if (node.type1 == Type::BoundingVolume && node.type2 == Type::Entity) { if (t2 < t1) { - location = (ray.direction * t2) + ray.origin; - t = t2; - object_index = node.index2; - return true; + res.location = (ray.direction * t2) + ray.origin; + res.t = t2; + res.object_index = node.index2; + res.side = side2; + res.hit = true; + return res; } else - return RaycastTreeNode(ray, bvh_.at(node.index1), location, t, object_index); + return RaycastTreeNode(ray, bvh_.at(node.index1)); } // if 1 is a gameobject and 2 a BV, see if gameobject is in front if (node.type1 == Type::Entity && node.type2 == Type::BoundingVolume) { if (t1 < t2) { - location = (ray.direction * t1) + ray.origin; - t = t1; - object_index = node.index1; - return true; + res.location = (ray.direction * t1) + ray.origin; + res.t = t1; + res.object_index = node.index1; + res.side = side1; + res.hit = true; + return res; } else - return RaycastTreeNode(ray, bvh_.at(node.index2), location, t, object_index); + return RaycastTreeNode(ray, bvh_.at(node.index2)); } // if 1 is a BV and 2 is a BV if (node.type1 == Type::BoundingVolume && node.type2 == Type::BoundingVolume) { - float node1_t{}; - glm::vec3 location1{}; - Entity object_index1{}; - bool node1_intersects = RaycastTreeNode(ray, bvh_.at(node.index1), location1, node1_t, object_index1); - float node2_t{}; - glm::vec3 location2{}; - Entity object_index2; - bool node2_intersects = RaycastTreeNode(ray, bvh_.at(node.index2), location2, node2_t, object_index2); - if (node1_intersects && node2_intersects) { - if (node1_t < node2_t) { - location = location1; - t = node1_t; - object_index = object_index1; - return true; + auto node1_intersection = RaycastTreeNode(ray, bvh_.at(node.index1)); + auto node2_intersection = RaycastTreeNode(ray, bvh_.at(node.index2)); + if (node1_intersection.hit && node2_intersection.hit) { + if (node1_intersection.t < node2_intersection.t) { + res.location = node1_intersection.location; + res.t = node1_intersection.t; + res.object_index = node1_intersection.object_index; + res.side = node1_intersection.side; + res.hit = true; + return res; } else { - location = location2; - t = node2_t; - object_index = object_index1; - return true; + res.location = node2_intersection.location; + res.t = node2_intersection.t; + res.object_index = node2_intersection.object_index; + res.side = node2_intersection.side; + res.hit = true; + return res; } } - else if (node1_intersects) { - t = node1_t; - location = location1; - object_index = object_index1; - return true; + else if (node1_intersection.hit) { + res.location = node1_intersection.location; + res.t = node1_intersection.t; + res.object_index = node1_intersection.object_index; + res.side = node1_intersection.side; + res.hit = true; + return res; } - else if (node2_intersects) { - t = node2_t; - location = location2; - object_index = object_index2; - return true; + else if (node2_intersection.hit) { + res.location = node2_intersection.location; + res.t = node2_intersection.t; + res.object_index = node2_intersection.object_index; + res.side = node2_intersection.side; + res.hit = true; + return res; } else { - return false; + res.hit = false; + return res; } } // if 1 is a gameobject and 2 is a gameobject if (node.type1 == Type::Entity && node.type2 == Type::Entity) { if (t1 < t2) { - location = (ray.direction * t1) + ray.origin; - t = t1; - object_index = node.index1; + res.location = (ray.direction * t1) + ray.origin; + res.t = t1; + res.object_index = node.index1; + res.side = side1; + res.hit = true; + return res; } else { - location = (ray.direction * t2) + ray.origin; - t = t2; - object_index = node.index2; + res.location = (ray.direction * t2) + ray.origin; + res.t = t2; + res.object_index = node.index2; + res.side = side2; + res.hit = true; + return res; } - return true; } } - - // only 1 hits - if (is_hit1) { + else if (is_hit1) { switch (node.type1) { case Type::BoundingVolume: - return RaycastTreeNode(ray, bvh_.at(node.index1), location, t, object_index); + return RaycastTreeNode(ray, bvh_.at(node.index1)); case Type::Entity: - location = (ray.direction * t1) + ray.origin; - t = t1; - object_index = node.index1; - return true; + res.location = (ray.direction * t1) + ray.origin; + res.t = t1; + res.object_index = node.index1; + res.side = side1; + res.hit = true; + return res; + } + } + else if (is_hit2) { + switch (node.type2) { + case Type::BoundingVolume: + return RaycastTreeNode(ray, bvh_.at(node.index2)); + case Type::Entity: + res.location = (ray.direction * t2) + ray.origin; + res.t = t2; + res.object_index = node.index2; + res.side = side2; + res.hit = true; + return res; } } - // only 2 hits - if (is_hit2) { - switch (node.type2) { - case Type::BoundingVolume: - return RaycastTreeNode(ray, bvh_.at(node.index2), location, t, object_index); - case Type::Entity: - location = (ray.direction * t2) + ray.origin; - t = t2; - object_index = node.index2; - return true; - } - } + throw std::runtime_error("RaycastTreeNode() hit end of function. This should not be possible"); } } // namespace engine diff --git a/src/util/gltf_loader.cpp b/src/util/gltf_loader.cpp index e7093f6..4d2023d 100644 --- a/src/util/gltf_loader.cpp +++ b/src/util/gltf_loader.cpp @@ -209,28 +209,23 @@ engine::Entity LoadGLTF(Scene& scene, const std::string& path, bool isStatic) for (const tg::Material& material : model.materials) { if (material.alphaMode != "OPAQUE") { LOG_WARN("Material {} contains alphaMode {} which isn't supported yet", material.name, material.alphaMode); - LOG_WARN("Material will be opaque"); } if (material.doubleSided == true) { LOG_WARN("Material {} specifies double-sided mesh rendering which isn't supported yet", material.name); - LOG_WARN("Material will be single-sided."); } if (material.emissiveTexture.index != -1 || material.emissiveFactor[0] != 0.0 || material.emissiveFactor[1] != 0.0 || material.emissiveFactor[2] != 0.0) { LOG_WARN("Material {} contains an emissive texture or non-zero emissive factor. Emission is currently unsupported.", material.name); - LOG_WARN("Material will be created without emission."); } const auto& baseColorFactor4 = material.pbrMetallicRoughness.baseColorFactor; if (baseColorFactor4[0] != 1.0 || baseColorFactor4[1] != 1.0 || baseColorFactor4[2] != 1.0 || baseColorFactor4[3] != 1.0) { if (material.pbrMetallicRoughness.baseColorTexture.index != -1) { LOG_WARN("Material {} contains a base color multiplier which isn't supported yet.", material.name); - LOG_WARN("The material's base color texture will be used as-is."); } } if (material.pbrMetallicRoughness.metallicFactor != 1.0 || material.pbrMetallicRoughness.roughnessFactor != 1.0) { if (material.pbrMetallicRoughness.metallicRoughnessTexture.index != -1) { - LOG_WARN("Material {} contains a metallic and/or roughness multiplier which isn't supported yet.", material.name); - LOG_WARN("The material's metallic-roughness texture will be used as-is."); + LOG_WARN("Material {} contains a metallic and/or roughness multiplier which isn't supported yet.", material.name); } } @@ -244,7 +239,6 @@ engine::Entity LoadGLTF(Scene& scene, const std::string& path, bool isStatic) } else { LOG_WARN("Material {} base color texture specifies a UV channel other than zero which is unsupported.", material.name); - LOG_WARN("Material will be created with a white base color"); } } else if (baseColorFactor4[0] != 1.0 || baseColorFactor4[1] != 1.0 || baseColorFactor4[2] != 1.0 || baseColorFactor4[3] != 1.0) { @@ -271,12 +265,11 @@ engine::Entity LoadGLTF(Scene& scene, const std::string& path, bool isStatic) } else { LOG_WARN("Material {} metallic roughness texture specifies a UV channel other than zero which is unsupported.", material.name); - LOG_WARN("Material will be created with a default metallic roughness"); } } else { LOG_INFO("Creating a metallic-roughness texture..."); - const std::vector mr_values{1.0f, material.pbrMetallicRoughness.metallicFactor, material.pbrMetallicRoughness.roughnessFactor, 1.0f}; + const std::vector mr_values{1.0f, material.pbrMetallicRoughness.roughnessFactor, material.pbrMetallicRoughness.metallicFactor, 1.0f}; Color mr(mr_values); if (metal_rough_textures.contains(mr) == false) { const uint8_t pixel[4] = {mr.r, mr.g, mr.b, mr.a}; @@ -299,7 +292,6 @@ engine::Entity LoadGLTF(Scene& scene, const std::string& path, bool isStatic) } else { LOG_WARN("Material {} occlusion texture specifies a UV channel other than zero which is unsupported.", material.name); - LOG_WARN("Material will be created with no ambient occlusion"); } } @@ -311,7 +303,6 @@ engine::Entity LoadGLTF(Scene& scene, const std::string& path, bool isStatic) } else { LOG_WARN("Material {} normal texture specifies a UV channel other than zero which is unsupported.", material.name); - LOG_WARN("Material will be created with no normal map"); } } } diff --git a/test/res/models/MY_AXES.glb b/test/res/models/MY_AXES.glb new file mode 100644 index 0000000..0d1006e Binary files /dev/null and b/test/res/models/MY_AXES.glb differ diff --git a/test/res/models/stairs.glb b/test/res/models/stairs.glb new file mode 100644 index 0000000..941e725 Binary files /dev/null and b/test/res/models/stairs.glb differ diff --git a/test/src/camera_controller.cpp b/test/src/camera_controller.cpp index 6878478..7931a3f 100644 --- a/test/src/camera_controller.cpp +++ b/test/src/camera_controller.cpp @@ -34,60 +34,102 @@ void CameraControllerSystem::OnUpdate(float ts) const float dt = ts; - float dx = scene_->app()->input_manager()->GetAxis("movex"); - float dy = scene_->app()->input_manager()->GetAxis("movey"); + float dx = scene_->app()->input_manager()->GetAxis("movex") * CameraControllerComponent::kSpeedStrafe; + float dy = scene_->app()->input_manager()->GetAxis("movey") * CameraControllerComponent::kSpeedForwardBack; // calculate new pitch and yaw - constexpr float kMaxPitch = glm::pi(); - constexpr float kMinPitch = 0.0f; - - float d_pitch = scene_->app()->input_manager()->GetAxis("looky") * -1.0f * c->kCameraSensitivity; + float d_pitch = scene_->app()->input_manager()->GetAxis("looky") * -1.0f * CameraControllerComponent::kCameraSensitivity; c->pitch += d_pitch; - if (c->pitch <= kMinPitch || c->pitch >= kMaxPitch) { + if (c->pitch <= CameraControllerComponent::kMinPitch || c->pitch >= CameraControllerComponent::kMaxPitch) { c->pitch -= d_pitch; } - c->yaw += scene_->app()->input_manager()->GetAxis("lookx") * -1.0f * c->kCameraSensitivity; + c->yaw += scene_->app()->input_manager()->GetAxis("lookx") * -1.0f * CameraControllerComponent::kCameraSensitivity; // update position relative to camera direction in xz plane const glm::vec3 d2x_rotated = glm::rotateZ(glm::vec3{dx, 0.0f, 0.0f}, c->yaw); const glm::vec3 d2y_rotated = glm::rotateZ(glm::vec3{0.0f, dy, 0.0f}, c->yaw); glm::vec3 h_vel = (d2x_rotated + d2y_rotated); c->vel.x = h_vel.x; - c->vel.y = h_vel.y; - // keep vel.z as gravity can increase it every frame + c->vel.y = h_vel.y; + // keep vel.z as gravity can increase it every frame // gravity stuff here: - constexpr float g = -9.81f; // constant velocity gravity??? - constexpr float player_height = 71.0f * 25.4f / 1000.0f; - c->vel.z += g * dt; + c->vel.z += CameraControllerComponent::kGravAccel * dt; - if (scene_->app()->input_manager()->GetButtonPress("jump")) { - c->vel.z += 4.4f; // m/s - } - - // update position with velocity: - - // check for collision during next frame and push back and remove velocity in the normal of the collision direction if so - engine::Ray ray{}; - ray.origin = t->position; - ray.origin.z -= player_height; // check for collision from the player's feet - - const glm::vec3 dX = c->vel * dt; // where player will end up with no collision - ray.direction = glm::normalize(dX); - const engine::Raycast raycast = scene_->GetSystem()->GetRaycast(ray); - if (raycast.hit && raycast.distance < glm::length(dX)) { - // will collide - t->position -= raycast.distance * ray.direction; // push out of collision zone - c->vel.z = 0.0f; // remove velocity normal to collision surface + // jumping + if (scene_->app()->input_manager()->GetButtonPress("jump") && c->grounded) { + c->vel.z += 4.4f; // m/s } - - t->position += c->vel * dt; - constexpr float kMaxDistanceFromOrigin = 10000.0f; + // update position with velocity: - if (glm::length(t->position) > kMaxDistanceFromOrigin) { - t->position = {0.0f, 0.0f, 10.0f}; + // check horizontal collisions first as otherwise the player may be teleported above a wall instead of colliding against it + if (c->vel.x != 0.0f || c->vel.y != 0.0f) { // just in case, to avoid a ray with direction = (0,0,0) + engine::Ray horiz_ray{}; + horiz_ray.origin = t->position; // set origin to 'MaxStairHeight' units above player's feet + horiz_ray.origin.z += CameraControllerComponent::kMaxStairHeight - CameraControllerComponent::kPlayerHeight; + horiz_ray.direction.x = c->vel.x; + horiz_ray.direction.y = c->vel.y; // this is normalized by GetRayCast() + horiz_ray.direction.z = 0.0f; + const engine::Raycast horiz_raycast = scene_->GetSystem()->GetRaycast(horiz_ray); + if (horiz_raycast.hit) { + const glm::vec2 norm_xy = glm::normalize(glm::vec2{horiz_raycast.normal.x, horiz_raycast.normal.y}) * -1.0f; // make it point towards object + const glm::vec2 vel_xy = glm::vec2{ c->vel.x, c->vel.y }; + // find the extent of the player's velocity in the direction of the wall's normal vector + const glm::vec2 partial_vel = norm_xy * glm::dot(norm_xy, vel_xy); + const glm::vec2 partial_dX = partial_vel * dt; + if (glm::length(partial_dX) > horiz_raycast.distance - CameraControllerComponent::kPlayerCollisionRadius) { + // player will collide with wall + // push player out of collision zone + const glm::vec2 push_vector = glm::normalize(vel_xy) * fmaxf(CameraControllerComponent::kPlayerCollisionRadius, horiz_raycast.distance); + t->position.x = horiz_raycast.location.x - push_vector.x; + t->position.y = horiz_raycast.location.y - push_vector.y; + c->vel.x -= partial_vel.x; + c->vel.y -= partial_vel.y; + } + } + } + + // check falling collisions + if (c->vel.z < 0.0f) { // if falling + engine::Ray fall_ray{}; + fall_ray.origin = t->position; + fall_ray.origin.z += CameraControllerComponent::kMaxStairHeight - CameraControllerComponent::kPlayerHeight; + fall_ray.direction = {0.0f, 0.0f, -1.0f}; // down + const engine::Raycast fall_raycast = scene_->GetSystem()->GetRaycast(fall_ray); + if (fall_raycast.hit) { // there is ground below player + // find how far the player will move (downwards) if velocity is applied without collision + const float mag_dz = fabsf(c->vel.z * dt); + // check if the player will be less than 'height' units above the collided ground + if (mag_dz > fall_raycast.distance - CameraControllerComponent::kMaxStairHeight) { + LOG_INFO("HIT"); + // push player up to ground level and set as grounded + t->position.z = fall_raycast.location.z + CameraControllerComponent::kPlayerHeight; + c->vel.z = 0.0f; + c->grounded = true; + } + else { // there is ground below player, however they are not grounded + c->grounded = false; + } + } + else { // there is no ground below player (THEY ARE FALLING INTO THE VOID) + c->grounded = false; + } + } + else if (c->vel.z > 0.0f) { + c->grounded = false; // they are jumping + } + + if (c->was_grounded != c->grounded) { + LOG_INFO("GROUNDED? {}", c->grounded); + c->was_grounded = c->grounded; + } + + t->position += c->vel * dt; + + if (glm::length(t->position) > CameraControllerComponent::kMaxDistanceFromOrigin) { + t->position = {0.0f, 0.0f, 100.0f}; } /* ROTATION STUFF */ @@ -122,6 +164,9 @@ void CameraControllerSystem::OnUpdate(float ts) if (scene_->app()->window()->GetKeyPress(engine::inputs::Key::K_R)) { t->position = {0.0f, 0.0f, 10.0f}; + c->vel = {0.0f, 0.0f, 0.0f}; + c->pitch = glm::half_pi(); + c->yaw = 0.0f; } if (scene_->app()->input_manager()->GetButtonPress("fullscreen")) { @@ -136,14 +181,38 @@ void CameraControllerSystem::OnUpdate(float ts) scene_->app()->scene_manager()->SetActiveScene(next_scene_); } - if (scene_->app()->window()->GetButtonPress(engine::inputs::MouseButton::M_LEFT)) { + static std::vector perm_lines{}; + + if (scene_->app()->window()->GetButton(engine::inputs::MouseButton::M_LEFT)) { engine::Ray ray{}; ray.origin = t->position; ray.direction = glm::vec3(glm::mat4_cast(t->rotation) * glm::vec4{0.0f, 0.0f, -1.0f, 1.0f}); engine::Raycast cast = scene_->GetSystem()->GetRaycast(ray); + if (cast.hit) { - LOG_INFO("Raycast hit {}", scene_->GetComponent(cast.hit_entity)->tag); LOG_INFO("Distance: {} m", cast.distance); + LOG_INFO("Location: {} {} {}", cast.location.x, cast.location.y, cast.location.z); + LOG_INFO("Normal: {} {} {}", cast.normal.x, cast.normal.y, cast.normal.z); + LOG_INFO("Ray direction: {} {} {}", ray.direction.x, ray.direction.y, ray.direction.z); + LOG_INFO("Hit Entity: {}", scene_->GetComponent(cast.hit_entity)->tag); + perm_lines.emplace_back(ray.origin, cast.location, glm::vec3{0.0f, 0.0f, 1.0f}); } } + if (scene_->app()->window()->GetButtonPress(engine::inputs::MouseButton::M_RIGHT)) { + engine::Ray horiz_ray{}; + horiz_ray.origin = t->position; // set origin to 'MaxStairHeight' units above player's feet + horiz_ray.origin.z += CameraControllerComponent::kMaxStairHeight - CameraControllerComponent::kPlayerHeight; + horiz_ray.direction.x = c->vel.x; + horiz_ray.direction.y = c->vel.y; // this is normalized by GetRayCast() + horiz_ray.direction.z = 0.0f; + const engine::Raycast cast = scene_->GetSystem()->GetRaycast(horiz_ray); + if (cast.hit) { + LOG_INFO("Distance: {} m", cast.distance); + LOG_INFO("Location: {} {} {}", cast.location.x, cast.location.y, cast.location.z); + LOG_INFO("Normal: {} {} {}", cast.normal.x, cast.normal.y, cast.normal.z); + perm_lines.emplace_back(horiz_ray.origin, cast.location, glm::vec3{ 0.0f, 0.0f, 1.0f }); + } + } + + scene_->app()->debug_lines.insert(scene_->app()->debug_lines.end(), perm_lines.begin(), perm_lines.end()); } diff --git a/test/src/camera_controller.hpp b/test/src/camera_controller.hpp index a68da9b..80eefb3 100644 --- a/test/src/camera_controller.hpp +++ b/test/src/camera_controller.hpp @@ -7,11 +7,21 @@ #include "ecs.h" struct CameraControllerComponent { - static constexpr float kWalkSpeed = 4.0f; + static constexpr float kSpeedForwardBack = 4.0f; + static constexpr float kSpeedStrafe = 4.0f; static constexpr float kCameraSensitivity = 0.007f; + static constexpr float kMaxDistanceFromOrigin = 10000.0f; + static constexpr float kGravAccel = -9.81f; + static constexpr float kPlayerHeight = 71.0f * 25.4f / 1000.0f; + static constexpr float kMaxStairHeight = 0.2f; + static constexpr float kPlayerCollisionRadius = 0.2f; + static constexpr float kMaxPitch = glm::pi(); + static constexpr float kMinPitch = 0.0f; float yaw = 0.0f; float pitch = glm::half_pi(); glm::vec3 vel{ 0.0f, 0.0f, 0.0f }; + bool grounded = false; + bool was_grounded = false; }; class CameraControllerSystem diff --git a/test/src/game.cpp b/test/src/game.cpp index 30304c8..a820224 100644 --- a/test/src/game.cpp +++ b/test/src/game.cpp @@ -127,18 +127,20 @@ void PlayGame(GameSettings settings) skybox_renderable->material->SetAlbedoTexture(app.GetResource("builtin.black")); engine::Entity helmet = engine::util::LoadGLTF(*main_scene, app.GetResourcePath("models/DamagedHelmet.glb")); - main_scene->GetPosition(helmet) += glm::vec3{20.0f, 10.0f, 5.0f}; + main_scene->GetPosition(helmet) += glm::vec3{5.0f, 5.0f, 1.0f}; + main_scene->GetScale(helmet) *= 3.0f; engine::Entity toycar = engine::util::LoadGLTF(*main_scene, app.GetResourcePath("models/ToyCar.glb")); main_scene->GetScale(toycar) *= 100.0f; - auto car_spin = main_scene->AddComponent(toycar); - car_spin->onInit = []() -> void {}; - car_spin->onUpdate = [&](float dt) -> void { - static float yaw = 0.0f; - yaw += dt; - main_scene->GetRotation(toycar) = glm::angleAxis(yaw, glm::vec3{0.0f, 0.0f, 1.0f}); - main_scene->GetRotation(toycar) *= glm::angleAxis(glm::half_pi(), glm::vec3{1.0f, 0.0f, 0.0f}); - }; + main_scene->GetPosition(toycar).z += 1.8f; + + engine::Entity stairs = engine::util::LoadGLTF(*main_scene, app.GetResourcePath("models/stairs.glb")); + main_scene->GetPosition(stairs) += glm::vec3{-8.0f, -5.0f, 0.1f}; + main_scene->GetRotation(stairs) = glm::angleAxis(glm::half_pi(), glm::vec3{ 0.0f, 0.0f, 1.0f }); + main_scene->GetRotation(stairs) *= glm::angleAxis(glm::half_pi(), glm::vec3{ 1.0f, 0.0f, 0.0f }); + + engine::Entity axes = engine::util::LoadGLTF(*main_scene, app.GetResourcePath("models/MY_AXES.glb")); + main_scene->GetPosition(axes) += glm::vec3{-40.0f, -40.0f, 1.0f}; } start_scene->GetSystem()->next_scene_ = main_scene; diff --git a/testfile.txt b/testfile.txt deleted file mode 100644 index 0527e6b..0000000 --- a/testfile.txt +++ /dev/null @@ -1 +0,0 @@ -This is a test