diff --git a/include/renderer.h b/include/renderer.h index c393083..651be90 100644 --- a/include/renderer.h +++ b/include/renderer.h @@ -16,94 +16,93 @@ namespace engine { // A uniform struct that holds data of type T template struct UniformDescriptor { - const gfx::DescriptorSetLayout* layout; - const gfx::DescriptorSet* set; - struct UniformBufferData { - T data; - } uniform_buffer_data; - gfx::UniformBuffer* uniform_buffer; + const gfx::DescriptorSetLayout* layout; + const gfx::DescriptorSet* set; + struct UniformBufferData { + T data; + } uniform_buffer_data; + gfx::UniformBuffer* uniform_buffer; +}; + +struct Line { + glm::vec3 pos1; + glm::vec3 pos2; }; class Renderer : private ApplicationComponent { - public: - Renderer(Application& app, gfx::GraphicsSettings settings); + public: + Renderer(Application& app, gfx::GraphicsSettings settings); - ~Renderer(); + ~Renderer(); - void PreRender(bool window_is_resized, glm::mat4 camera_transform); + void PreRender(bool window_is_resized, glm::mat4 camera_transform); - // staticList can be nullptr to render nothing - void Render(const RenderList* static_list, const RenderList* dynamic_list); + // staticList can be nullptr to render nothing + void Render(const RenderList* static_list, const RenderList* dynamic_list, const std::vector& debug_lines); - // getters + // getters - GFXDevice* GetDevice() { return device_.get(); } + GFXDevice* GetDevice() { return device_.get(); } - const gfx::DescriptorSetLayout* GetGlobalSetLayout() { - return global_uniform.layout; - } + const gfx::DescriptorSetLayout* GetGlobalSetLayout() { return global_uniform.layout; } - const gfx::DescriptorSetLayout* GetFrameSetLayout() { - return frame_uniform.layout; - } + const gfx::DescriptorSetLayout* GetFrameSetLayout() { return frame_uniform.layout; } - const gfx::DescriptorSetLayout* GetMaterialSetLayout() { - return material_set_layout; - } + const gfx::DescriptorSetLayout* GetMaterialSetLayout() { return material_set_layout; } - std::unordered_map samplers; + std::unordered_map samplers; - private: - std::unique_ptr device_; + private: + std::unique_ptr device_; - struct CameraSettings { - float vertical_fov_radians = glm::radians(70.0f); - float clip_near = 0.5f; - float clip_far = 1000.0f; - } camera_settings_; + struct CameraSettings { + float vertical_fov_radians = glm::radians(70.0f); + float clip_near = 0.5f; + float clip_far = 1000.0f; + } camera_settings_; - // ALL vertex shaders must begin with: - /* - layout(set = 0, binding = 0) uniform GlobalSetUniformBuffer { - mat4 proj; - } globalSetUniformBuffer; + // ALL vertex shaders must begin with: + /* + layout(set = 0, binding = 0) uniform GlobalSetUniformBuffer { + mat4 proj; + } globalSetUniformBuffer; - layout(set = 1, binding = 0) uniform FrameSetUniformBuffer { - mat4 view; - } frameSetUniformBuffer; + layout(set = 1, binding = 0) uniform FrameSetUniformBuffer { + mat4 view; + } frameSetUniformBuffer; - layout( push_constant ) uniform Constants { - mat4 model; - } constants; - */ - // ALL fragment shaders must begin with: - /* - layout(set = 2, binding = 0) uniform sampler2D materialSetAlbedoSampler; - layout(set = 2, binding = 1) uniform sampler2D materialSetNormalSampler; - layout(set = 2, binding = 2) uniform sampler2D materialSetOcclusionSampler; - layout(set = 2, binding = 3) uniform sampler2D materialSetMetallicRoughnessSampler; - */ + layout( push_constant ) uniform Constants { + mat4 model; + } constants; + */ + // ALL fragment shaders must begin with: + /* + layout(set = 2, binding = 0) uniform sampler2D materialSetAlbedoSampler; + layout(set = 2, binding = 1) uniform sampler2D materialSetNormalSampler; + layout(set = 2, binding = 2) uniform sampler2D materialSetOcclusionSampler; + layout(set = 2, binding = 3) uniform sampler2D materialSetMetallicRoughnessSampler; + */ - // in vertex shader - UniformDescriptor global_uniform; // rarely updates; set 0 - UniformDescriptor frame_uniform; // updates once per frame; set 1 - // in fragment shader - const gfx::DescriptorSetLayout* material_set_layout; // set 2; set bound per material + // in vertex shader + UniformDescriptor global_uniform; // rarely updates; set 0 + UniformDescriptor frame_uniform; // updates once per frame; set 1 + // in fragment shader + const gfx::DescriptorSetLayout* material_set_layout; // set 2; set bound per material - float viewport_aspect_ratio_ = 1.0f; + float viewport_aspect_ratio_ = 1.0f; - const gfx::Pipeline* last_bound_pipeline_ = nullptr; + const gfx::Pipeline* last_bound_pipeline_ = nullptr; - struct DebugRenderingThings { - const gfx::Pipeline* pipeline = nullptr; - // have a simple vertex buffer with 2 points that draws a line - const gfx::Buffer* vertex_buffer = nullptr; - // shader will take 2 clip space xyzw coords as push constants to define the line - } debug_rendering_things_{}; + struct DebugRenderingThings { + const gfx::Pipeline* pipeline = nullptr; + // have a simple vertex buffer with 2 points that draws a line + const gfx::Buffer* vertex_buffer = nullptr; + // shader will take 2 clip space xyzw coords as push constants to define the line + } debug_rendering_things_{}; - void DrawRenderList(gfx::DrawBuffer* draw_buffer, const RenderList& render_list); + void DrawRenderList(gfx::DrawBuffer* draw_buffer, const RenderList& render_list); }; -} // namespace engine +} // namespace engine #endif diff --git a/include/scene.h b/include/scene.h index 88f707e..a7d288b 100644 --- a/include/scene.h +++ b/include/scene.h @@ -12,140 +12,167 @@ #include "ecs.h" #include "event_system.h" +#include "components/transform.h" namespace engine { class Application; class Scene { - public: - Scene(Application* app); - Scene(const Scene&) = delete; - Scene& operator=(const Scene&) = delete; - ~Scene(); + public: + Scene(Application* app); + Scene(const Scene&) = delete; + Scene& operator=(const Scene&) = delete; + ~Scene(); - void Update(float ts); + void Update(float ts); - Application* app() { return app_; } + Application* app() { return app_; } - EventSystem* event_system() { return event_system_.get(); } + EventSystem* event_system() { return event_system_.get(); } - /* ecs stuff */ + /* ecs stuff */ - Entity CreateEntity(const std::string& tag, Entity parent = 0, - const glm::vec3& pos = glm::vec3{0.0f, 0.0f, 0.0f}, - const glm::quat& rot = glm::quat{1.0f, 0.0f, 0.0f, 0.0f}, - const glm::vec3& scl = glm::vec3{1.0f, 1.0f, 1.0f}); + Entity CreateEntity(const std::string& tag, Entity parent = 0, const glm::vec3& pos = glm::vec3{0.0f, 0.0f, 0.0f}, + const glm::quat& rot = glm::quat{1.0f, 0.0f, 0.0f, 0.0f}, const glm::vec3& scl = glm::vec3{1.0f, 1.0f, 1.0f}); - Entity GetEntity(const std::string& tag, Entity parent = 0); + Entity GetEntity(const std::string& tag, Entity parent = 0); - size_t GetComponentSignaturePosition(size_t hash); + size_t GetComponentSignaturePosition(size_t hash); - template - void RegisterComponent() { - size_t hash = typeid(T).hash_code(); - assert(component_arrays_.contains(hash) == false && - "Registering component type more than once."); - component_arrays_.emplace(hash, std::make_unique>()); + template + void RegisterComponent() + { + size_t hash = typeid(T).hash_code(); + assert(component_arrays_.contains(hash) == false && "Registering component type more than once."); + component_arrays_.emplace(hash, std::make_unique>()); - size_t signature_position = next_signature_position_; - ++next_signature_position_; - assert(signature_position < kMaxComponents && - "Registering too many components!"); - assert(component_signature_positions_.contains(hash) == false); - component_signature_positions_.emplace(hash, signature_position); - } - - template - T* GetComponent(Entity entity) { - auto array = GetComponentArray(); - return array->GetData(entity); - } - - template - T* AddComponent(Entity entity, const T& comp = T{}) { - size_t hash = typeid(T).hash_code(); - - auto array = GetComponentArray(); - array->InsertData(entity, comp); // errors if entity already exists in array - - // set the component bit for this entity - size_t signature_position = component_signature_positions_.at(hash); - auto& signature_ref = signatures_.at(entity); - signature_ref.set(signature_position); - - for (auto& [system_hash, system] : ecs_systems_) { - if (system->entities_.contains(entity)) continue; - if ((system->signature_ & signature_ref) == system->signature_) { - system->entities_.insert(entity); - system->OnComponentInsert(entity); - } + size_t signature_position = next_signature_position_; + ++next_signature_position_; + assert(signature_position < kMaxComponents && "Registering too many components!"); + assert(component_signature_positions_.contains(hash) == false); + component_signature_positions_.emplace(hash, signature_position); } - return array->GetData(entity); - } + template + T* GetComponent(Entity entity) + { + // check if component exists on entity: + size_t hash = typeid(T).hash_code(); + size_t signature_position = component_signature_positions_.at(hash); + const auto& entity_signature = signatures_.at(entity); + if (entity_signature.test(signature_position) == false) { + return nullptr; + } - template - void RegisterSystem() { - size_t hash = typeid(T).hash_code(); - ecs_systems_.emplace_back(hash, std::make_unique(this)); - } - - template - T* GetSystem() { - size_t hash = typeid(T).hash_code(); - System* found_system = nullptr; - for (auto& [system_hash, system] : ecs_systems_) { - if (hash == system_hash) found_system = system.get(); + auto array = GetComponentArray(); + return array->GetData(entity); } - if (found_system == nullptr) { - throw std::runtime_error("Unable to find system"); + + // because GetComponent(entity); } - T* casted_ptr = dynamic_cast(found_system); - if (casted_ptr == nullptr) { - throw std::runtime_error("Failed to cast system pointer!"); + + glm::vec3& GetPosition(Entity entity) { + return GetTransform(entity)->position; } - return casted_ptr; - } - private: - Application* const app_; - - public: - Entity next_entity_id_ = 1; // 0 is not a valid entity - private: - uint64_t framecount_ = 0; - - /* ecs stuff */ - - size_t next_signature_position_ = 0; - // maps component hashes to signature positions - std::unordered_map component_signature_positions_{}; - // maps entity ids to their signatures - std::unordered_map> signatures_{}; - // maps component hashes to their arrays - std::unordered_map> - component_arrays_{}; - - // hashes and associated systems - std::vector>> ecs_systems_{}; - - template - ComponentArray* GetComponentArray() { - size_t hash = typeid(T).hash_code(); - auto it = component_arrays_.find(hash); - if (it == component_arrays_.end()) { - throw std::runtime_error("Cannot find component array."); + glm::quat& GetRotation(Entity entity) { + return GetTransform(entity)->rotation; } - auto ptr = it->second.get(); - auto casted_ptr = dynamic_cast*>(ptr); - assert(casted_ptr != nullptr); - return casted_ptr; - } - std::unique_ptr event_system_{}; + glm::vec3& GetScale(Entity entity) { + return GetTransform(entity)->scale; + } + + template + T* AddComponent(Entity entity, const T& comp = T{}) + { + size_t hash = typeid(T).hash_code(); + + auto array = GetComponentArray(); + array->InsertData(entity, comp); // errors if entity already exists in array + + // set the component bit for this entity + size_t signature_position = component_signature_positions_.at(hash); + auto& signature_ref = signatures_.at(entity); + signature_ref.set(signature_position); + + for (auto& [system_hash, system] : ecs_systems_) { + if (system->entities_.contains(entity)) continue; + if ((system->signature_ & signature_ref) == system->signature_) { + system->entities_.insert(entity); + system->OnComponentInsert(entity); + } + } + + return array->GetData(entity); + } + + template + void RegisterSystem() + { + size_t hash = typeid(T).hash_code(); + ecs_systems_.emplace_back(hash, std::make_unique(this)); + } + + template + T* GetSystem() + { + size_t hash = typeid(T).hash_code(); + System* found_system = nullptr; + for (auto& [system_hash, system] : ecs_systems_) { + if (hash == system_hash) found_system = system.get(); + } + if (found_system == nullptr) { + throw std::runtime_error("Unable to find system"); + } + T* casted_ptr = dynamic_cast(found_system); + if (casted_ptr == nullptr) { + throw std::runtime_error("Failed to cast system pointer!"); + } + return casted_ptr; + } + + private: + Application* const app_; + + public: + Entity next_entity_id_ = 1; // 0 is not a valid entity + private: + uint64_t framecount_ = 0; + + /* ecs stuff */ + + size_t next_signature_position_ = 0; + // maps component hashes to signature positions + std::unordered_map component_signature_positions_{}; + // maps entity ids to their signatures + std::unordered_map> signatures_{}; + // maps component hashes to their arrays + std::unordered_map> component_arrays_{}; + + // hashes and associated systems + std::vector>> ecs_systems_{}; + + template + ComponentArray* GetComponentArray() + { + size_t hash = typeid(T).hash_code(); + auto it = component_arrays_.find(hash); + if (it == component_arrays_.end()) { + throw std::runtime_error("Cannot find component array."); + } + auto ptr = it->second.get(); + auto casted_ptr = dynamic_cast*>(ptr); + assert(casted_ptr != nullptr); + return casted_ptr; + } + + std::unique_ptr event_system_{}; }; -} // namespace engine +} // namespace engine #endif diff --git a/include/systems/collisions.h b/include/systems/collisions.h index d3f8b38..57040cd 100644 --- a/include/systems/collisions.h +++ b/include/systems/collisions.h @@ -33,7 +33,7 @@ class CollisionSystem : public System { Raycast GetRaycast(Ray ray); - private: + public: // one node of the BVH struct BiTreeNode { enum class Type : uint8_t { BoundingVolume, Entity, Empty }; @@ -53,11 +53,12 @@ class CollisionSystem : public System { PrimitiveInfo(const AABB& aabb, Entity entity_idx); }; + std::vector bvh_{}; + + private: size_t colliders_size_last_update_ = 0; size_t colliders_size_now_ = 0; - std::vector bvh_{}; - bool RaycastTreeNode(const Ray& ray, const BiTreeNode& node, glm::vec3& location, float& t, Entity& object_index); static int BuildNode(std::vector& prims, std::vector& tree_nodes); diff --git a/res/engine/shaders/fancy.frag b/res/engine/shaders/fancy.frag index bae23cc..7a0bf07 100644 --- a/res/engine/shaders/fancy.frag +++ b/res/engine/shaders/fancy.frag @@ -33,7 +33,7 @@ void main() { const float roughness = metallic_roughness.b; const float roughness_2 = roughness * roughness; - const vec3 light_colour = vec3(1.0, 1.0, 1.0) * 2.4; + const vec3 light_colour = vec3(1.0, 1.0, 1.0) * 5.0; const vec3 emission = vec3(0.0, 0.0, 0.0); const float ao = texture(materialSetOcclusionSampler, fragUV).r; @@ -42,8 +42,8 @@ void main() { const vec3 N = GetNormal(); const vec3 V = normalize(fragViewPosTangentSpace - fragPosTangentSpace); - //const vec3 L = normalize(fragLightPosTangentSpace - fragPosTangentSpace); - const vec3 L = normalize(vec3(5.0, 0.0, 3.0)); + const vec3 L = normalize(fragLightPosTangentSpace - fragPosTangentSpace); + //const vec3 L = normalize(vec3(5.0, 0.0, 3.0)); const vec3 H = normalize(V + L); //const vec3 dielectric_brdf = FresnelMix(); diff --git a/src/application.cpp b/src/application.cpp index 8bde9aa..20672e0 100644 --- a/src/application.cpp +++ b/src/application.cpp @@ -25,6 +25,7 @@ #include "resources/texture.h" #include "systems/mesh_render_system.h" #include "components/transform.h" +#include "components/collider.h" #include "scene.h" #include "scene_manager.h" #include "window.h" @@ -34,6 +35,7 @@ #include #define WIN_MAX_PATH 260 #endif +#include static struct ImGuiThings { ImGuiContext* context; @@ -114,8 +116,8 @@ Application::Application(const char* appName, const char* appVersion, gfx::Graph shaderSettings.cull_backface = true; shaderSettings.write_z = true; shaderSettings.render_order = 0; - auto fancyShader = std::make_unique(renderer(), GetResourcePath("engine/shaders/fancy.vert"), - GetResourcePath("engine/shaders/fancy.frag"), shaderSettings); + auto fancyShader = + std::make_unique(renderer(), GetResourcePath("engine/shaders/fancy.vert"), GetResourcePath("engine/shaders/fancy.frag"), shaderSettings); GetResourceManager()->AddPersistent("builtin.fancy", std::move(fancyShader)); } { @@ -129,14 +131,14 @@ Application::Application(const char* appName, const char* appVersion, gfx::Graph shaderSettings.cull_backface = true; shaderSettings.write_z = false; shaderSettings.render_order = 1; - auto skyboxShader = std::make_unique(renderer(), GetResourcePath("engine/shaders/skybox.vert"), - GetResourcePath("engine/shaders/skybox.frag"), shaderSettings); + auto skyboxShader = + std::make_unique(renderer(), GetResourcePath("engine/shaders/skybox.vert"), GetResourcePath("engine/shaders/skybox.frag"), shaderSettings); GetResourceManager()->AddPersistent("builtin.skybox", std::move(skyboxShader)); } /* default textures */ { - const uint8_t pixel[4] = { 255, 255, 255, 255 }; + const uint8_t pixel[4] = {255, 255, 255, 255}; gfx::SamplerInfo samplerInfo{}; samplerInfo.minify = gfx::Filter::kNearest; samplerInfo.magnify = gfx::Filter::kNearest; @@ -146,7 +148,7 @@ Application::Application(const char* appName, const char* appVersion, gfx::Graph GetResourceManager()->AddPersistent("builtin.white", std::move(whiteTexture)); } { - const uint8_t pixel[4] = { 0, 0, 0, 255 }; + const uint8_t pixel[4] = {0, 0, 0, 255}; gfx::SamplerInfo samplerInfo{}; samplerInfo.minify = gfx::Filter::kNearest; samplerInfo.magnify = gfx::Filter::kNearest; @@ -156,7 +158,7 @@ Application::Application(const char* appName, const char* appVersion, gfx::Graph GetResourceManager()->AddPersistent("builtin.black", std::move(blackTexture)); } { - const uint8_t pixel[4] = { 127, 127, 255, 255 }; + const uint8_t pixel[4] = {127, 127, 255, 255}; gfx::SamplerInfo samplerInfo{}; samplerInfo.minify = gfx::Filter::kNearest; samplerInfo.magnify = gfx::Filter::kNearest; @@ -166,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, 0, 127, 255}; gfx::SamplerInfo samplerInfo{}; samplerInfo.minify = gfx::Filter::kNearest; samplerInfo.magnify = gfx::Filter::kNearest; @@ -268,7 +270,7 @@ void Application::GameLoop() int depth = find_depth(i, 0); for (int j = 0; j < depth; ++j) tabs += std::string{" "}; ImGui::Text("%s%s", tabs.c_str(), t->tag.c_str()); - //ImGui::Text("%.1f %.1f %.1f", t->position.x, t->position.y, t->position.z); + // ImGui::Text("%.1f %.1f %.1f", t->position.x, t->position.y, t->position.z); } } else { @@ -283,6 +285,7 @@ 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) { camera_transform = scene->GetComponent(scene->GetEntity("camera"))->world_matrix; auto mesh_render_system = scene->GetSystem(); @@ -290,7 +293,7 @@ void Application::GameLoop() dynamic_list = mesh_render_system->GetDynamicRenderList(); } renderer_->PreRender(window()->GetWindowResized(), camera_transform); - renderer_->Render(static_list, dynamic_list); + renderer_->Render(static_list, dynamic_list, debug_lines); /* poll events */ window_->GetInputAndEvents(); diff --git a/src/renderer.cpp b/src/renderer.cpp index 227e7fa..ab6acf5 100644 --- a/src/renderer.cpp +++ b/src/renderer.cpp @@ -61,8 +61,8 @@ Renderer::Renderer(Application& app, gfx::GraphicsSettings settings) : Applicati debug_pipeline_info.vertex_format = debug_vertex_format; debug_pipeline_info.alpha_blending = false; debug_pipeline_info.backface_culling = false; // probably ignored for line rendering - debug_pipeline_info.write_z = false; // lines don't need the depth buffer - //debug_pipeline_info.descriptor_set_layouts = empty; + debug_pipeline_info.write_z = false; // lines don't need the depth buffer + // debug_pipeline_info.descriptor_set_layouts = empty; debug_pipeline_info.line_primitives = true; debug_rendering_things_.pipeline = device_->CreatePipeline(debug_pipeline_info); @@ -104,7 +104,7 @@ void Renderer::PreRender(bool window_is_resized, glm::mat4 camera_transform) device_->WriteUniformBuffer(frame_uniform.uniform_buffer, 0, sizeof(frame_uniform.uniform_buffer_data), &frame_uniform.uniform_buffer_data); } -void Renderer::Render(const RenderList* static_list, const RenderList* dynamic_list) +void Renderer::Render(const RenderList* static_list, const RenderList* dynamic_list, const std::vector& debug_lines) { last_bound_pipeline_ = nullptr; @@ -124,8 +124,20 @@ void Renderer::Render(const RenderList* static_list, const RenderList* dynamic_l // draw debug shit here device_->CmdBindPipeline(draw_buffer, debug_rendering_things_.pipeline); glm::vec4 debug_positions[2] = {}; - debug_positions[0] = global_uniform.uniform_buffer_data.data * frame_uniform.uniform_buffer_data.data * glm::vec4{ 0.0f, 0.0f, 0.0f, 1.0f }; - debug_positions[1] = global_uniform.uniform_buffer_data.data * frame_uniform.uniform_buffer_data.data * glm::vec4{ 0.0f, 0.0f, 1.0f, 1.0f }; + 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); + 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); + 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); device_->CmdDraw(draw_buffer, 2, 1, 0, 0); diff --git a/src/systems/collisions.cpp b/src/systems/collisions.cpp index b3351c7..74192f9 100644 --- a/src/systems/collisions.cpp +++ b/src/systems/collisions.cpp @@ -78,13 +78,37 @@ void CollisionSystem::OnUpdate(float ts) transformed_box.max = t->world_matrix * glm::vec4(c->aabb.max, 1.0f); // if a mesh is not rotated a multiple of 90 degrees, the box will not fit the mesh // TODO fix it - prims.emplace_back(c->aabb, entity); + + // correct min and max + AABB box{}; + box.min.x = fminf(transformed_box.min.x, transformed_box.max.x); + box.min.y = fminf(transformed_box.min.y, transformed_box.max.y); + box.min.z = fminf(transformed_box.min.z, transformed_box.max.z); + box.max.x = fmaxf(transformed_box.min.x, transformed_box.max.x); + box.max.y = fmaxf(transformed_box.min.y, transformed_box.max.y); + box.max.z = fmaxf(transformed_box.min.z, transformed_box.max.z); + + prims.emplace_back(box, entity); } bvh_.clear(); bvh_.reserve(entities_.size() * 3 / 2); // from testing, bvh is usually 20% larger than number of objects BuildNode(prims, bvh_); + // check AABB mins and maxes are the correct order + for (const auto& node : bvh_) { + if (node.type1 != BiTreeNode::Type::Empty) { + if (node.box1.max.x < node.box1.min.x) abort(); + if (node.box1.max.y < node.box1.min.y) abort(); + if (node.box1.max.z < node.box1.min.z) abort(); + } + if (node.type2 != BiTreeNode::Type::Empty) { + if (node.box2.max.x < node.box2.min.x) abort(); + if (node.box2.max.y < node.box2.min.y) abort(); + if (node.box2.max.z < node.box2.min.z) abort(); + } + } + LOG_INFO("BUILT BVH!"); colliders_size_last_update_ = colliders_size_now_; @@ -94,10 +118,13 @@ void CollisionSystem::OnUpdate(float ts) Raycast CollisionSystem::GetRaycast(Ray ray) { Raycast res{}; + res.hit = false; ray.direction = glm::normalize(ray.direction); - res.hit = RaycastTreeNode(ray, bvh_.back(), res.location, res.distance, res.hit_entity); + if (!bvh_.empty()) { + res.hit = RaycastTreeNode(ray, bvh_.back(), res.location, res.distance, res.hit_entity); + } return res; } @@ -109,20 +136,41 @@ CollisionSystem::PrimitiveInfo::PrimitiveInfo(const AABB& aabb, Entity entity_id int CollisionSystem::BuildNode(std::vector& prims, std::vector& tree_nodes) { - if (prims.size() == 0) throw std::runtime_error("Cannot build BVH with no primitives!"); + if (prims.size() == 0) abort(); std::array, 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{}; @@ -132,7 +180,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++) { @@ -142,91 +190,79 @@ 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) { @@ -235,7 +271,7 @@ int CollisionSystem::BuildNode(std::vector& prims, std::vector& prims, std::vectorapp()->input_manager()->GetButton("sprint")) speed *= 10.0f; float dx = scene_->app()->input_manager()->GetAxis("movex"); - float dy = (-scene_->app()->input_manager()->GetAxis("movey")); + float dy = scene_->app()->input_manager()->GetAxis("movey"); // calculate new pitch and yaw @@ -55,15 +55,35 @@ void CameraControllerSystem::OnUpdate(float ts) // 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::rotateX(glm::vec3{0.0f, 0.0f, dy}, c->pitch), 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); h_vel *= speed; t->position += h_vel * dt; + // 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; + + // 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 + + ray.direction = c-> + const engine::Raycast fall_raycast = scene_->GetSystem()->GetRaycast(fall_ray); + if (fall_raycast.hit && fall_raycast.distance < player_height + (-c->fall_vel * dt)) { + t->position.z += -(fall_raycast.distance - player_height); + c->fall_vel = 0.0f; + } + else { + t->position.z += c->fall_vel * dt; + } + constexpr float kMaxDistanceFromOrigin = 10000.0f; if (glm::length(t->position) > kMaxDistanceFromOrigin) { - t->position = {0.0f, 5.0f, 0.0f}; + t->position = {0.0f, 0.0f, 10.0f}; } /* ROTATION STUFF */ @@ -90,13 +110,14 @@ void CameraControllerSystem::OnUpdate(float ts) /* user interface inputs */ if (scene_->app()->window()->GetKeyPress(engine::inputs::Key::K_P)) { - std::string pos_string{"x: " + std::to_string(t->world_matrix[3][0]) + " y: " + std::to_string(t->world_matrix[3][1]) + " z: " + std::to_string(t->world_matrix[3][2])}; + std::string pos_string{"x: " + std::to_string(t->world_matrix[3][0]) + " y: " + std::to_string(t->world_matrix[3][1]) + + " z: " + std::to_string(t->world_matrix[3][2])}; LOG_INFO("position {}", pos_string); LOG_INFO("rotation w: {} x: {} y: {} z: {}", t->rotation.w, t->rotation.x, t->rotation.y, t->rotation.z); } if (scene_->app()->window()->GetKeyPress(engine::inputs::Key::K_R)) { - t->position = {0.0f, 5.0f, 0.0f}; + t->position = {0.0f, 0.0f, 10.0f}; } if (scene_->app()->input_manager()->GetButtonPress("fullscreen")) { @@ -113,11 +134,12 @@ void CameraControllerSystem::OnUpdate(float ts) if (scene_->app()->window()->GetButtonPress(engine::inputs::MouseButton::M_LEFT)) { engine::Ray ray{}; - ray.origin.x = t->world_matrix[3][0]; - ray.origin.y = t->world_matrix[3][1]; - ray.origin.z = t->world_matrix[3][2]; - ray.direction = glm::vec3{ 0.0f, 0.0f, -1.0f }; + 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); - LOG_INFO("Raycast success? {}", cast.hit ? "YES" : "NO"); + if (cast.hit) { + LOG_INFO("Raycast hit {}", scene_->GetComponent(cast.hit_entity)->tag); + LOG_INFO("Distance: {} m", cast.distance); + } } } \ No newline at end of file diff --git a/test/src/camera_controller.hpp b/test/src/camera_controller.hpp index 83ed69e..a68da9b 100644 --- a/test/src/camera_controller.hpp +++ b/test/src/camera_controller.hpp @@ -11,6 +11,7 @@ struct CameraControllerComponent { static constexpr float kCameraSensitivity = 0.007f; float yaw = 0.0f; float pitch = glm::half_pi(); + glm::vec3 vel{ 0.0f, 0.0f, 0.0f }; }; class CameraControllerSystem diff --git a/test/src/game.cpp b/test/src/game.cpp index fec985e..d3f67d5 100644 --- a/test/src/game.cpp +++ b/test/src/game.cpp @@ -103,16 +103,19 @@ void PlayGame(GameSettings settings) floor_renderable->material->SetMetallicRoughnessTexture(floor_mr); floor_renderable->material->SetOcclusionTexture(app.GetResource("builtin.white")); floor_renderable->visible = true ; + auto floor_col = main_scene->AddComponent(floor); + floor_col->aabb.min = glm::vec3{0.0f, 0.0f, 0.0f}; + floor_col->aabb.max = glm::vec3{100.0f, 100.0f, 0.1f }; - - engine::Entity normal_map_test = engine::util::LoadGLTF(*main_scene, app.GetResourcePath("models/normalmaptest.glb")); - main_scene->GetComponent(normal_map_test)->position += glm::vec3{-10.0f, 0.0f, 1.0f}; + engine::Entity redcube = engine::util::LoadGLTF(*main_scene, app.GetResourcePath("models/redcube.glb")); engine::Entity monke = engine::util::LoadGLTF(*main_scene, app.GetResourcePath("models/monke.glb")); + main_scene->GetComponent(monke)->position.y += 10.0f; - engine::Entity bottle = engine::util::LoadGLTF(*main_scene, app.GetResourcePath("models/bottle.glb")); - main_scene->GetComponent(bottle)->scale *= 50.0f; - main_scene->GetComponent(bottle)->position.z += 50.0f;; + //engine::Entity bottle = engine::util::LoadGLTF(*main_scene, app.GetResourcePath("models/bottle.glb")); + //main_scene->GetComponent(bottle)->scale *= 10.0f; + //main_scene->GetComponent(bottle)->position.x += 25.0f; + //main_scene->GetComponent(bottle)->position.z += 5.0f; /* skybox */ engine::Entity skybox = main_scene->CreateEntity("skybox");