diff --git a/Makefile b/Makefile index feb918d7..9d56e32c 100644 --- a/Makefile +++ b/Makefile @@ -142,6 +142,7 @@ cppcheck: $(shell cppcheck --version | grep -qP '2\.(1[1-8]|1\d{2,}|[2-9]\d+)|[3-9]+\.'; echo $$?)) cppcheck \ --enable=all \ + --inline-suppr \ --suppress=missingIncludeSystem --suppress=unusedFunction --suppress=unmatchedSuppression \ --library=opengl --library=posix --library=tinyxml2 \ $$([ $(CPPCHECK_EXHAUSTIVE_SUPPORT) -eq 0 ] && echo "--check-level=exhaustive") \ diff --git a/include/engine/camera/Camera.hpp b/include/engine/camera/Camera.hpp index 8129b064..e8880676 100644 --- a/include/engine/camera/Camera.hpp +++ b/include/engine/camera/Camera.hpp @@ -14,10 +14,13 @@ #pragma once +#include #include #include #include +#include "engine/render/BoundingSphere.hpp" + namespace engine::camera { enum class MovementDirection { @@ -39,22 +42,36 @@ class Camera { glm::vec3 lookAt; glm::vec3 up; float fov; - float nearPlane; - float farPlane; + float near; + float far; + float aspectRatio; + + glm::mat4 cameraMatrix; + std::array viewFrustum; public: - Camera(const glm::vec3 &position, - const glm::vec3 &lookAt, - const glm::vec3 &up, - float fov, - float nearPlane, - float farPlane); + Camera(const glm::vec3 &_position, + const glm::vec3 &_lookAt, + const glm::vec3 &_up, + float _fov, + float _near, + float _far); - virtual void move(MovementDirection direction, float deltaTime) = 0; virtual void setPosition(const glm::vec3 &pos); + void setWindowSize(int width, int height); + virtual void move(MovementDirection direction, float deltaTime) = 0; + + const glm::vec3 &getPosition() const; + const glm::mat4 &getCameraMatrix() const; + + bool isInFrustum(const render::BoundingSphere &sphere) const; + +protected: + void updateCameraMatrix(); + void updateViewFrustum(); - glm::vec3 getPosition() const; - glm::mat4 getCameraMatrix(float aspectRatio) const; +private: + static glm::vec4 planeEquation(const glm::vec3 &p1, const glm::vec3 &p2, const glm::vec3 &p3); }; } diff --git a/include/engine/render/BoundingSphere.hpp b/include/engine/render/BoundingSphere.hpp new file mode 100644 index 00000000..7572ed0c --- /dev/null +++ b/include/engine/render/BoundingSphere.hpp @@ -0,0 +1,48 @@ +/// Copyright 2025 Ana Oliveira, Humberto Gomes, Mariana Rocha, Sara Lopes +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. + +#pragma once + +#include +#include + +#include "engine/render/BoundingSphere.hpp" +#include "engine/render/RenderPipeline.hpp" +#include "utils/Vertex.hpp" + +namespace engine::render { + +class Model; // Importing model would lead to a recursive inclusion + +class BoundingSphere { +private: + glm::vec4 center; + float radius; + + static Model *sphereModel; + static bool initializingSphereModel; + +public: + BoundingSphere(); + BoundingSphere(const glm::vec4 &_center, float _radius); + explicit BoundingSphere(const std::vector &vertices); + BoundingSphere(const BoundingSphere &sphere, const glm::mat4 &transform); + + glm::vec4 getCenter() const; + float getRadius() const; + + void draw(const RenderPipeline &pipeline, const glm::mat4 &cameraMatrix) const; +}; + +} diff --git a/include/engine/render/Model.hpp b/include/engine/render/Model.hpp index 122346bb..86f0b1cc 100644 --- a/include/engine/render/Model.hpp +++ b/include/engine/render/Model.hpp @@ -17,6 +17,7 @@ #include #include +#include "engine/render/BoundingSphere.hpp" #include "utils/Vertex.hpp" #include "utils/WavefrontOBJ.hpp" @@ -26,6 +27,7 @@ class Model { private: GLuint vao, vbo, ibo; unsigned int vertexCount; + BoundingSphere boundingSphere; public: Model(const std::vector &vertices, const std::vector &indices); @@ -34,10 +36,13 @@ class Model { Model(Model &&) = delete; ~Model(); + const BoundingSphere &getBoundingSphere() const; + void draw() const; private: explicit Model(const std::pair, std::vector> &vertices); + void calculateBoundingSphere(); }; } diff --git a/include/engine/scene/Entity.hpp b/include/engine/scene/Entity.hpp index 44837441..9173c4cd 100644 --- a/include/engine/scene/Entity.hpp +++ b/include/engine/scene/Entity.hpp @@ -29,6 +29,7 @@ namespace engine::scene { class Entity { private: std::shared_ptr model; + render::BoundingSphere boundingSphere; public: Entity(const tinyxml2::XMLElement *modelElement, @@ -37,6 +38,9 @@ class Entity { Entity(const Entity &entity) = delete; Entity(Entity &&entity) = delete; + void updateBoundingSphere(const glm::mat4 &worldTransform); + const render::BoundingSphere &getBoundingSphere() const; + void draw(const render::RenderPipeline &pipeline, const glm::mat4 &transform) const; }; diff --git a/include/engine/scene/Group.hpp b/include/engine/scene/Group.hpp index 89431c1a..cf4e403d 100644 --- a/include/engine/scene/Group.hpp +++ b/include/engine/scene/Group.hpp @@ -22,6 +22,8 @@ #include #include +#include "engine/camera/Camera.hpp" +#include "engine/render/BoundingSphere.hpp" #include "engine/render/RenderPipeline.hpp" #include "engine/scene/Entity.hpp" #include "engine/scene/TRSTransform.hpp" @@ -32,6 +34,7 @@ class Group { private: std::vector> entities; std::vector> groups; + render::BoundingSphere boundingSphere; TRSTransform transform; public: @@ -41,7 +44,13 @@ class Group { Group(const Group &entity) = delete; Group(Group &&entity) = delete; - void draw(const render::RenderPipeline &pipeline, const glm::mat4 &_transform) const; + int getEntityCount() const; + void updateBoundingSphere(const glm::mat4 &worldTransform); + + int draw(const render::RenderPipeline &pipeline, + const camera::Camera &camera, + const glm::mat4 &_transform, + bool drawBoundingSpheres) const; }; } diff --git a/include/engine/scene/Scene.hpp b/include/engine/scene/Scene.hpp index 9e36dbbc..10e4d7e9 100644 --- a/include/engine/scene/Scene.hpp +++ b/include/engine/scene/Scene.hpp @@ -41,10 +41,11 @@ class Scene { int getWindowWidth() const; int getWindowHeight() const; + int getEntityCount() const; void setWindowSize(int width, int height); camera::Camera &getCamera(); - void draw(const render::RenderPipeline &pipeline) const; + int draw(const render::RenderPipeline &pipeline, bool drawBoundingSpheres) const; }; } diff --git a/include/engine/window/SceneWindow.hpp b/include/engine/window/SceneWindow.hpp index 8fd57e54..45290958 100644 --- a/include/engine/window/SceneWindow.hpp +++ b/include/engine/window/SceneWindow.hpp @@ -29,7 +29,7 @@ class SceneWindow : public Window { render::RenderPipeline pipeline; scene::Scene scene; render::Axis xAxis, yAxis, zAxis; - UI _UI; + UI ui; public: explicit SceneWindow(const std::string &sceneFile); diff --git a/include/engine/window/UI.hpp b/include/engine/window/UI.hpp index a7310745..0417b2a4 100644 --- a/include/engine/window/UI.hpp +++ b/include/engine/window/UI.hpp @@ -23,16 +23,17 @@ namespace engine::window { class UI { private: camera::Camera &camera; - bool showAxes = true; + bool showAxes, showBoundingSpheres; + int entityCount; public: - UI(Window &window, camera::Camera &_camera); - void render(); + UI(Window &window, camera::Camera &_camera, int _entityCount); ~UI(); - void setShowAxes(bool value); + void render(int renderedEntities); bool isShowAxesEnabled() const; + bool isShowBoundingSpheresEnabled() const; }; } diff --git a/src/engine/camera/Camera.cpp b/src/engine/camera/Camera.cpp index 4a88a9a1..070b8cf4 100644 --- a/src/engine/camera/Camera.cpp +++ b/src/engine/camera/Camera.cpp @@ -12,31 +12,106 @@ /// See the License for the specific language governing permissions and /// limitations under the License. +#include + #include "engine/camera/Camera.hpp" namespace engine::camera { -Camera::Camera(const glm::vec3 &pos, - const glm::vec3 &target, - const glm::vec3 &upDir, - float fovAngle, - float near, - float far) : - position(pos), lookAt(target), up(upDir), fov(fovAngle), nearPlane(near), farPlane(far) {} +Camera::Camera(const glm::vec3 &_position, + const glm::vec3 &_lookAt, + const glm::vec3 &_up, + float _fov, + float _near, + float _far) : + position(_position), + lookAt(_lookAt), + up(_up), + fov(glm::radians(_fov)), + near(_near), + far(_far), + aspectRatio(1.0f) { + + this->updateCameraMatrix(); + this->updateViewFrustum(); +} void Camera::setPosition(const glm::vec3 &pos) { this->position = pos; + + this->updateCameraMatrix(); + this->updateViewFrustum(); } -glm::mat4 Camera::getCameraMatrix(float aspectRatio) const { - glm::mat4 view = glm::lookAt(this->position, this->lookAt, this->up); - glm::mat4 projection = - glm::perspective(glm::radians(this->fov), aspectRatio, this->nearPlane, this->farPlane); - return projection * view; +void Camera::setWindowSize(int width, int height) { + this->aspectRatio = static_cast(width) / height; + + this->updateCameraMatrix(); + this->updateViewFrustum(); } -glm::vec3 Camera::getPosition() const { +const glm::vec3 &Camera::getPosition() const { return this->position; } +const glm::mat4 &Camera::getCameraMatrix() const { + return this->cameraMatrix; +} + +void Camera::updateCameraMatrix() { + const glm::mat4 view = glm::lookAt(this->position, this->lookAt, this->up); + const glm::mat4 projection = + glm::perspective(this->fov, this->aspectRatio, this->near, this->far); + + this->cameraMatrix = projection * view; +} + +bool Camera::isInFrustum(const render::BoundingSphere &sphere) const { + return std::none_of(this->viewFrustum.cbegin(), + this->viewFrustum.cend(), + [sphere](const glm::vec4 &plane) { + const float distance = + glm::dot(glm::vec3(plane), glm::vec3(sphere.getCenter())) + plane.w; + return distance < -sphere.getRadius(); + }); +} + +void Camera::updateViewFrustum() { + const float halfNearHeight = tanf(this->fov / 2) * this->near; + const float halfNearWidth = halfNearHeight * this->aspectRatio; + + const float halfFarHeight = tanf(this->fov / 2) * this->far; + const float halfFarWidth = halfFarHeight * this->aspectRatio; + + const glm::vec3 d = glm::normalize(this->lookAt - this->position); + const glm::vec3 right = glm::normalize(glm::cross(d, this->up)); + const glm::vec3 realUp = glm::normalize(glm::cross(right, d)); + + const glm::vec3 farCenter = this->position + d * this->far; + const glm::vec3 nearCenter = this->position + d * this->near; + + const glm::vec3 nearTopLeft = nearCenter + realUp * halfNearHeight - right * halfNearWidth; + const glm::vec3 nearTopRight = nearCenter + realUp * halfNearHeight + right * halfNearWidth; + const glm::vec3 nearBottomLeft = nearCenter - realUp * halfNearHeight - right * halfNearWidth; + const glm::vec3 nearBottomRight = nearCenter - realUp * halfNearHeight + right * halfNearWidth; + + const glm::vec3 farTopLeft = farCenter + realUp * halfFarHeight - right * halfFarWidth; + const glm::vec3 farTopRight = farCenter + realUp * halfFarHeight + right * halfFarWidth; + const glm::vec3 farBottomLeft = farCenter - realUp * halfFarHeight - right * halfFarWidth; + const glm::vec3 farBottomRight = farCenter - realUp * halfFarHeight + right * halfFarWidth; + + this->viewFrustum[0] = Camera::planeEquation(nearTopLeft, nearTopRight, nearBottomLeft); + this->viewFrustum[1] = Camera::planeEquation(farTopLeft, farBottomLeft, farTopRight); + this->viewFrustum[2] = Camera::planeEquation(nearTopLeft, nearBottomLeft, farTopLeft); + this->viewFrustum[3] = Camera::planeEquation(nearTopRight, farTopRight, nearBottomRight); + this->viewFrustum[4] = Camera::planeEquation(nearTopLeft, farTopLeft, nearTopRight); + this->viewFrustum[5] = Camera::planeEquation(farBottomRight, farBottomLeft, nearBottomLeft); +} + +glm::vec4 Camera::planeEquation(const glm::vec3 &p1, const glm::vec3 &p2, const glm::vec3 &p3) { + const glm::vec3 normal = glm::normalize(glm::cross(p2 - p1, p3 - p1)); + const float constantFactor = glm::dot(-normal, p1); + return glm::vec4(normal, constantFactor); +} + } diff --git a/src/engine/camera/FreeCamera.cpp b/src/engine/camera/FreeCamera.cpp index b4425856..caaa7211 100644 --- a/src/engine/camera/FreeCamera.cpp +++ b/src/engine/camera/FreeCamera.cpp @@ -72,28 +72,30 @@ void FreeCamera::move(MovementDirection direction, float deltaTime) { case MovementDirection::LookLeft: yaw -= rotationSpeed; updateCameraVectors(); - return; + break; case MovementDirection::LookRight: yaw += rotationSpeed; updateCameraVectors(); - return; + break; case MovementDirection::LookUp: pitch += rotationSpeed; if (pitch > glm::radians(89.0f)) pitch = glm::radians(89.0f); updateCameraVectors(); - return; + break; case MovementDirection::LookDown: pitch -= rotationSpeed; if (pitch < glm::radians(-89.0f)) pitch = glm::radians(-89.0f); updateCameraVectors(); - return; + break; default: break; } lookAt = position + front; + this->updateCameraMatrix(); + this->updateViewFrustum(); } void FreeCamera::setPosition(const glm::vec3 &newPosition) { diff --git a/src/engine/camera/OrbitalCamera.cpp b/src/engine/camera/OrbitalCamera.cpp index 65e03785..64bf7b93 100644 --- a/src/engine/camera/OrbitalCamera.cpp +++ b/src/engine/camera/OrbitalCamera.cpp @@ -26,6 +26,7 @@ OrbitalCamera::OrbitalCamera(const glm::vec3 &_position, float _near, float _far) : Camera(_position, _lookAt, _up, _fov, _near, _far) { + const glm::vec3 delta = _position - this->lookAt; radius = glm::length(delta); @@ -75,7 +76,10 @@ void OrbitalCamera::move(MovementDirection dir, float delta) { azimuth = glm::mod(azimuth, glm::two_pi()); polar = glm::clamp(polar, 0.01f, glm::pi() - 0.01f); radius = glm::clamp(radius, 0.5f, 100.0f); - updatePosition(); + + this->updatePosition(); + this->updateCameraMatrix(); + this->updateViewFrustum(); } void OrbitalCamera::setPosition(const glm::vec3 &newPosition) { diff --git a/src/engine/render/BoundingSphere.cpp b/src/engine/render/BoundingSphere.cpp new file mode 100644 index 00000000..b39a0dc2 --- /dev/null +++ b/src/engine/render/BoundingSphere.cpp @@ -0,0 +1,94 @@ +/// Copyright 2025 Ana Oliveira, Humberto Gomes, Mariana Rocha, Sara Lopes +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. + +#include +#include +#include +#include + +#include "engine/render/BoundingSphere.hpp" +#include "engine/render/Model.hpp" +#include "generator/figures/Sphere.hpp" + +namespace engine::render { + +BoundingSphere::BoundingSphere() : center(0.0f, 0.0f, 0.0f, 1.0f), radius(0.0f) { + if (!BoundingSphere::sphereModel && !BoundingSphere::initializingSphereModel) { + BoundingSphere::initializingSphereModel = true; + + generator::figures::Sphere sphere(1.0f, 16, 16); + // Free this only with the destruction of the OpenGL context + BoundingSphere::sphereModel = new Model(sphere); + } +} + +BoundingSphere::BoundingSphere(const glm::vec4 &_center, float _radius) : BoundingSphere() { + this->center = _center; + this->radius = _radius; +} + +BoundingSphere::BoundingSphere(const std::vector &vertices) : BoundingSphere() { + // Calculate center of mass + this->center = + std::transform_reduce(std::execution::par, + vertices.cbegin(), + vertices.cend(), + glm::vec4(0.0f), + std::plus<>(), + [](const utils::Vertex &vertex) { return vertex.position; }) / + static_cast(vertices.size()); + + // Calculate radius + this->radius = std::transform_reduce( + std::execution::par, + vertices.cbegin(), + vertices.cend(), + -1.0, + [](float d1, float d2) { return std::max(d1, d2); }, + [this](const utils::Vertex &vertex) { + return glm::distance(this->center, vertex.position); + }); +} + +BoundingSphere::BoundingSphere(const BoundingSphere &sphere, const glm::mat4 &transform) { + // https://math.stackexchange.com/questions/237369 + const float scalex = glm::length(transform[0]); + const float scaley = glm::length(transform[1]); + const float scalez = glm::length(transform[2]); + + this->radius = sphere.radius * std::max(scalex, std::max(scaley, scalez)); + this->center = transform * sphere.center; +} + +glm::vec4 BoundingSphere::getCenter() const { + return this->center; +} + +float BoundingSphere::getRadius() const { + return this->radius; +} + +void BoundingSphere::draw(const RenderPipeline &pipeline, const glm::mat4 &cameraMatrix) const { + const glm::vec3 translationVector(this->center); + const glm::vec3 scaleVector(this->radius); + pipeline.setMatrix(cameraMatrix * glm::translate(translationVector) * glm::scale(scaleVector)); + + pipeline.setColor(glm::vec4(1.0f, 0.0f, 0.0f, 1.0f)); + BoundingSphere::sphereModel->draw(); +} + +Model *BoundingSphere::sphereModel = nullptr; +bool BoundingSphere::initializingSphereModel = false; + +} diff --git a/src/engine/render/Model.cpp b/src/engine/render/Model.cpp index 20703997..1965c8a7 100644 --- a/src/engine/render/Model.cpp +++ b/src/engine/render/Model.cpp @@ -16,7 +16,8 @@ namespace engine::render { -Model::Model(const std::vector &vertices, const std::vector &indices) { +Model::Model(const std::vector &vertices, const std::vector &indices) : + boundingSphere(vertices) { glGenVertexArrays(1, &this->vao); glBindVertexArray(this->vao); @@ -51,6 +52,10 @@ Model::~Model() { glDeleteVertexArrays(1, &this->vao); } +const BoundingSphere &Model::getBoundingSphere() const { + return this->boundingSphere; +} + void Model::draw() const { glBindVertexArray(this->vao); glDrawElements(GL_TRIANGLES, this->vertexCount, GL_UNSIGNED_INT, nullptr); diff --git a/src/engine/scene/Entity.cpp b/src/engine/scene/Entity.cpp index 8cbe1493..bcdf8f80 100644 --- a/src/engine/scene/Entity.cpp +++ b/src/engine/scene/Entity.cpp @@ -36,6 +36,14 @@ Entity::Entity(const tinyxml2::XMLElement *modelElement, } } +void Entity::updateBoundingSphere(const glm::mat4 &worldTransform) { + this->boundingSphere = render::BoundingSphere(this->model->getBoundingSphere(), worldTransform); +} + +const render::BoundingSphere &Entity::getBoundingSphere() const { + return this->boundingSphere; +} + void Entity::draw(const render::RenderPipeline &pipeline, const glm::mat4 &transform) const { pipeline.setColor(glm::vec4(1.0f)); pipeline.setMatrix(transform); diff --git a/src/engine/scene/Group.cpp b/src/engine/scene/Group.cpp index 416b782e..b1a022f2 100644 --- a/src/engine/scene/Group.cpp +++ b/src/engine/scene/Group.cpp @@ -12,6 +12,8 @@ /// See the License for the specific language governing permissions and /// limitations under the License. +#include +#include #include #include "engine/scene/Group.hpp" @@ -52,16 +54,103 @@ Group::Group(const tinyxml2::XMLElement *groupElement, } } -void Group::draw(const render::RenderPipeline &pipeline, const glm::mat4 &_transform) const { +int Group::getEntityCount() const { + return std::transform_reduce( + std::execution::par, + this->groups.cbegin(), + this->groups.cend(), + this->entities.size(), + std::plus<>(), + [](const std::unique_ptr &group) { return group->getEntityCount(); }); +} + +void Group::updateBoundingSphere(const glm::mat4 &worldTransform) { + const glm::mat4 subTransform = worldTransform * this->transform.getMatrix(); + + // Calculate center of group (approximation for objects around the same size) + glm::vec4 groupCenter(0.0f); + + groupCenter += std::transform_reduce(std::execution::par, + this->entities.cbegin(), + this->entities.cend(), + glm::vec4(0.0f), + std::plus<>(), + [subTransform](const std::unique_ptr &entity) { + entity->updateBoundingSphere(subTransform); + return entity->getBoundingSphere().getCenter(); + }); + + groupCenter += std::transform_reduce(std::execution::par, + this->groups.cbegin(), + this->groups.cend(), + glm::vec4(0.0f), + std::plus<>(), + [subTransform](const std::unique_ptr &group) { + group->updateBoundingSphere(subTransform); + return group->boundingSphere.getCenter(); + }); + + groupCenter /= this->entities.size() + this->groups.size(); + + const float entitiesRadius = std::transform_reduce( + std::execution::par, + this->entities.cbegin(), + this->entities.cend(), + 0.0f, + [](float d1, float d2) { return std::max(d1, d2); }, + [groupCenter](const std::unique_ptr &entity) { + const render::BoundingSphere &entitySphere = entity->getBoundingSphere(); + return glm::distance(entitySphere.getCenter(), groupCenter) + entitySphere.getRadius(); + }); + + const float groupsRadius = std::transform_reduce( + std::execution::par, + this->groups.cbegin(), + this->groups.cend(), + 0.0f, + [](float d1, float d2) { return std::max(d1, d2); }, + [groupCenter](const std::unique_ptr &group) { + const render::BoundingSphere &groupSphere = group->boundingSphere; + return glm::distance(groupSphere.getCenter(), groupCenter) + groupSphere.getRadius(); + }); + + const float radius = std::max(entitiesRadius, groupsRadius); + this->boundingSphere = render::BoundingSphere(groupCenter, radius); +} + +int Group::draw(const render::RenderPipeline &pipeline, + const camera::Camera &camera, + const glm::mat4 &_transform, + bool drawBoundingSpheres) const { + const glm::mat4 subTransform = _transform * this->transform.getMatrix(); + const glm::mat4 &cameraMatrix = camera.getCameraMatrix(); + int renderedEntities = 0; + + if (!camera.isInFrustum(this->boundingSphere)) + return renderedEntities; for (const std::unique_ptr &entity : this->entities) { - entity->draw(pipeline, subTransform); + const render::BoundingSphere entityBoundingSphere = entity->getBoundingSphere(); + + if (camera.isInFrustum(entityBoundingSphere)) { + entity->draw(pipeline, subTransform); + renderedEntities++; // cppcheck-suppress useStlAlgorithm + + if (drawBoundingSpheres) + entityBoundingSphere.draw(pipeline, cameraMatrix); + } } for (const std::unique_ptr &group : this->groups) { - group->draw(pipeline, subTransform); + // cppcheck-suppress useStlAlgorithm + renderedEntities += group->draw(pipeline, camera, subTransform, drawBoundingSpheres); } + + if (drawBoundingSpheres) + this->boundingSphere.draw(pipeline, cameraMatrix); + + return renderedEntities; } } diff --git a/src/engine/scene/Scene.cpp b/src/engine/scene/Scene.cpp index 3f5ab48c..e1f88698 100644 --- a/src/engine/scene/Scene.cpp +++ b/src/engine/scene/Scene.cpp @@ -12,9 +12,11 @@ /// See the License for the specific language governing permissions and /// limitations under the License. -#include "engine/scene/Scene.hpp" +#include +#include #include "engine/camera/CameraFactory.hpp" +#include "engine/scene/Scene.hpp" #include "utils/XMLUtils.hpp" namespace engine::scene { @@ -60,25 +62,40 @@ int Scene::getWindowHeight() const { return this->windowHeight; } +int Scene::getEntityCount() const { + return std::transform_reduce( + std::execution::par, + this->groups.cbegin(), + this->groups.cend(), + 0, + std::plus<>(), + [](const std::unique_ptr &group) { return group->getEntityCount(); }); +} + void Scene::setWindowSize(int width, int height) { this->windowWidth = width; this->windowHeight = height; + this->camera->setWindowSize(width, height); } camera::Camera &Scene::getCamera() { return *camera; } -void Scene::draw(const render::RenderPipeline &pipeline) const { - const float aspectRatio = static_cast(this->windowWidth) / this->windowHeight; - const glm::mat4 cameraMatrix = this->camera->getCameraMatrix(aspectRatio); +int Scene::draw(const render::RenderPipeline &pipeline, bool drawBoundingSpheres) const { + const glm::mat4 &cameraMatrix = this->camera->getCameraMatrix(); + + int entityCount = 0; for (const std::unique_ptr &group : this->groups) { - group->draw(pipeline, cameraMatrix); + group->updateBoundingSphere(glm::mat4(1.0f)); + entityCount += group->draw(pipeline, *this->camera, cameraMatrix, drawBoundingSpheres); } // Reset camera after transforms pipeline.setMatrix(cameraMatrix); + + return entityCount; } } diff --git a/src/engine/window/SceneWindow.cpp b/src/engine/window/SceneWindow.cpp index 5f1eca36..9f3f6665 100644 --- a/src/engine/window/SceneWindow.cpp +++ b/src/engine/window/SceneWindow.cpp @@ -32,7 +32,7 @@ SceneWindow::SceneWindow(const std::string &sceneFile) : xAxis(glm::vec3(1.0f, 0.0f, 0.0f)), yAxis(glm::vec3(0.0f, 1.0f, 0.0f)), zAxis(glm::vec3(0.0f, 0.0f, 1.0f)), - _UI(*this, scene.getCamera()) { + ui(*this, scene.getCamera(), scene.getEntityCount()) { this->resize(scene.getWindowWidth(), scene.getWindowHeight()); @@ -93,10 +93,11 @@ void SceneWindow::onRender() { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glClearColor(0.f, 0.f, 0.f, 1.f); - this->_UI.render(); - this->scene.draw(this->pipeline); + int renderedEntities = + this->scene.draw(this->pipeline, this->ui.isShowBoundingSpheresEnabled()); + this->ui.render(renderedEntities); - if (this->_UI.isShowAxesEnabled()) { + if (this->ui.isShowAxesEnabled()) { this->xAxis.draw(this->pipeline); this->yAxis.draw(this->pipeline); this->zAxis.draw(this->pipeline); diff --git a/src/engine/window/UI.cpp b/src/engine/window/UI.cpp index 69949874..dac95b11 100644 --- a/src/engine/window/UI.cpp +++ b/src/engine/window/UI.cpp @@ -15,7 +15,6 @@ #include "engine/window/UI.hpp" #include #include -#include #include #include @@ -25,7 +24,9 @@ namespace engine::window { -UI::UI(Window &window, camera::Camera &_camera) : camera(_camera) { +UI::UI(Window &window, camera::Camera &_camera, int _entityCount) : + camera(_camera), showAxes(true), showBoundingSpheres(false), entityCount(_entityCount) { + IMGUI_CHECKVERSION(); ImGui::CreateContext(); ImGui::StyleColorsDark(); @@ -34,7 +35,13 @@ UI::UI(Window &window, camera::Camera &_camera) : camera(_camera) { ImGui_ImplOpenGL3_Init("#version 460 core"); } -void UI::render() { +UI::~UI() { + ImGui_ImplOpenGL3_Shutdown(); + ImGui_ImplGlfw_Shutdown(); + ImGui::DestroyContext(); +} + +void UI::render(int renderedEntities) { ImGui_ImplOpenGL3_NewFrame(); ImGui_ImplGlfw_NewFrame(); ImGui::NewFrame(); @@ -50,6 +57,10 @@ void UI::render() { } ImGui::Text("FPS: %d", fps); + std::string entityText = std::to_string(renderedEntities) + " / " + + std::to_string(this->entityCount) + " entities rendered"; + ImGui::Text(entityText.c_str()); + ImGui::Separator(); ImGui::Text("Render Options"); @@ -67,7 +78,8 @@ void UI::render() { } } - if (ImGui::Checkbox("Show Axes", &showAxes)) {} + if (ImGui::Checkbox("Show Axes", &this->showAxes)) {} + if (ImGui::Checkbox("Show Bounding Spheres", &this->showBoundingSpheres)) {} ImGui::Separator(); ImGui::Text("Camera Options"); @@ -83,18 +95,12 @@ void UI::render() { ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); } -UI::~UI() { - ImGui_ImplOpenGL3_Shutdown(); - ImGui_ImplGlfw_Shutdown(); - ImGui::DestroyContext(); -} - -void UI::setShowAxes(bool value) { - this->showAxes = value; -} - bool UI::isShowAxesEnabled() const { return this->showAxes; } +bool UI::isShowBoundingSpheresEnabled() const { + return this->showBoundingSpheres; +} + }