diff --git a/engine/include/cubos/engine/gizmos/gizmos.hpp b/engine/include/cubos/engine/gizmos/gizmos.hpp index a0b971b38..c941d51f9 100644 --- a/engine/include/cubos/engine/gizmos/gizmos.hpp +++ b/engine/include/cubos/engine/gizmos/gizmos.hpp @@ -39,8 +39,8 @@ namespace cubos::engine /// @param from One of the ends of the line to be drawn. /// @param to The other end of the line to be drawn. /// @param lifespan How long the line will be on screen for, in seconds. Defaults to 0, which means a single - /// @param space Space to draw the gizmo in /// frame. + /// @param space Space to draw the gizmo in. void drawLine(const std::string& id, glm::vec3 from, glm::vec3 to, float lifespan = 0.0F, Space space = Space::World); @@ -50,17 +50,30 @@ namespace cubos::engine /// @param oppositeCorner The opposite corner of the box to be drawn. /// @param lifespan How long the line will be on screen for, in seconds. Defaults to 0, which means a single /// frame. - /// @param space Space to draw the gizmo in + /// @param space Space to draw the gizmo in. void drawBox(const std::string& id, glm::vec3 corner, glm::vec3 oppositeCorner, float lifespan = 0.0F, Space space = Space::World); + /// @brief Draws a cut cone gizmo. + /// @param id Identifier of the gizmo. + /// @param firstBaseCenter Center of one of the bases. + /// @param firstBaseRadius Radius of one of the bases. + /// @param secondBaseCenter Center of the second base. + /// @param secondBaseRadius Radius of the second base. + /// @param lifespan How long the line will be on screen for, in seconds. Defaults to 0, which means a single + /// frame. + /// @param space Space to draw the gizmo in. + void drawCutCone(const std::string& id, glm::vec3 firstBaseCenter, float firstBaseRadius, + glm::vec3 secondBaseCenter, float secondBaseRadius, float lifespan = 0.0F, + Space space = Space::World); + /// @brief Draws a wireframe box gizmo. /// @param id Identifier of the gizmo. /// @param corner One of the corners of the box to be drawn. /// @param oppositeCorner The opposite corner of the box to be drawn. /// @param lifespan How long the line will be on screen for, in seconds. Defaults to 0, which means a single /// frame. - /// @param space Space to draw the gizmo in + /// @param space Space to draw the gizmo in. void drawWireBox(const std::string& id, glm::vec3 corner, glm::vec3 oppositeCorner, float lifespan = 0.0F, Space space = Space::World); @@ -82,9 +95,9 @@ namespace cubos::engine bool decreaseLifespan(float delta); protected: - const std::string& mId; ///< Gizmo identifier - glm::vec3 mColor; ///< Color of the gizmo - float mLifespan; ///< Time in seconds the gizmo has left to live + const std::string& mId; ///< Gizmo identifier. + glm::vec3 mColor; ///< Color of the gizmo. + float mLifespan; ///< Time in seconds the gizmo has left to live. }; std::vector> worldGizmos; ///< Queued gizmos to be drawn in world space. @@ -92,7 +105,12 @@ namespace cubos::engine std::vector> screenGizmos; ///< Queued gizmos to be drawn in screen space. private: - glm::vec3 mColor; ///< Currently set color + /// @brief Adds a gizmo into the corresponding vector. + /// @param gizmo Gizmo to be added. + /// @param space Space in which the gizmo will be drawn. + void push(const std::shared_ptr& gizmo, const Space& space); + + glm::vec3 mColor; ///< Currently set color. }; } // namespace cubos::engine diff --git a/engine/samples/gizmos/main.cpp b/engine/samples/gizmos/main.cpp index baebeade5..6ca8fceac 100644 --- a/engine/samples/gizmos/main.cpp +++ b/engine/samples/gizmos/main.cpp @@ -18,29 +18,30 @@ static void settingsSystem(Write settings) static void setCameraSystem(Write camera, Commands cmds) { - camera->entities[0] = cmds.create() - .add(Camera{.fovY = 60.0F, .zNear = 0.1F, .zFar = 100.0F}) - .add(LocalToWorld{}) - .add(Position{{5.0F, 5.0F, -10.0F}}) - .add(Rotation{glm::quatLookAt(glm::vec3{0.0F, 0.0F, 1.0F}, glm::vec3{0.0F, 1.0F, 0.0F})}) - .entity(); + camera->entities[0] = + cmds.create() + .add(Camera{.fovY = 60.0F, .zNear = 0.1F, .zFar = 100.0F}) + .add(LocalToWorld{}) + .add(Position{{5.0F, 5.0F, 0.0F}}) + .add(Rotation{glm::quatLookAt(glm::vec3{-1.0F, -1.0F, 0.0F}, glm::vec3{0.0F, 1.0F, 0.0F})}) + .entity(); camera->entities[1] = cmds.create() .add(Camera{.fovY = 60.0F, .zNear = 0.1F, .zFar = 100.0F}) .add(LocalToWorld{}) - .add(Position{{-5.0F, 5.0F, -10.0F}}) - .add(Rotation{glm::quatLookAt(glm::vec3{0.0F, 0.0F, 1.0F}, glm::vec3{0.0F, 1.0F, 0.0F})}) + .add(Position{{-25.0F, 25.0F, 0.0F}}) + .add(Rotation{glm::quatLookAt(glm::vec3{1.0F, -1.0F, 0.0F}, glm::vec3{0.0F, 1.0F, 0.0F})}) .entity(); camera->entities[2] = cmds.create() .add(Camera{.fovY = 60.0F, .zNear = 0.1F, .zFar = 100.0F}) .add(LocalToWorld{}) - .add(Position{{-5.0F, -5.0F, -10.0F}}) - .add(Rotation{glm::quatLookAt(glm::vec3{0.5F, 0.5F, 0.5F}, glm::vec3{0.0F, 1.0F, 0.0F})}) + .add(Position{{-25.0F, -25.0F, 0.0F}}) + .add(Rotation{glm::quatLookAt(glm::vec3{1.0F, 1.0F, 0.0F}, glm::vec3{0.0F, 1.0F, 0.0F})}) .entity(); camera->entities[3] = cmds.create() .add(Camera{.fovY = 60.0F, .zNear = 0.1F, .zFar = 100.0F}) .add(LocalToWorld{}) - .add(Position{{5.0F, -5.0F, -10.0F}}) - .add(Rotation{glm::quatLookAt(glm::vec3{0.5F, 0.5F, 0.5F}, glm::vec3{0.0F, 1.0F, 0.0F})}) + .add(Position{{25.0F, -25.0F, 0.0F}}) + .add(Rotation{glm::quatLookAt(glm::vec3{-1.0F, 1.0F, 0.0F}, glm::vec3{0.0F, 1.0F, 0.0F})}) .entity(); } @@ -62,6 +63,11 @@ static void drawSystem(Write gizmos) gizmos->color({0.5F, 1, 1}); gizmos->drawWireBox("test box", {0.5F, 0.5F, 0.5F}, {-1, -1, -1}, 0, Gizmos::Space::World); /// [Box] + + ///[Cut Cone] + gizmos->color({0.5F, 0.3F, 1}); + gizmos->drawCutCone("test cut cone", {0.7F, 0.7F, 0.7F}, 5.0F, {-3, -3, -3}, 3.0F, 0, Gizmos::Space::World); + ///[Cut Cone] } /// [System] diff --git a/engine/samples/gizmos/page.md b/engine/samples/gizmos/page.md index 727d9abee..e420c12ff 100644 --- a/engine/samples/gizmos/page.md +++ b/engine/samples/gizmos/page.md @@ -24,10 +24,14 @@ Let's now add a wireboxe. We want to know were exactly is the centre of each cam @snippet gizmos/main.cpp WireBox -Finally, let's add a box. This box will be in World space, as it's the last space left to cover. It will be drawn by any camera that is looking at it, much like if it was an object in the world. +Let's add a box. This box will be in World space, as it's the last space left to cover. It will be drawn by any camera that is looking at it, much like if it was an object in the world. @snippet gizmos/main.cpp Box +Finally let's add a cut cone. A cut cone is cylinder with faces that can have different radiuses. If you set one of the bases to have a radius of 0, you'll have a simple cone. If you set them both to have the same radius, you'll have a cylinder. Our cut cone will have different radiuses: + +@snippet gizmos/main.cpp Cut Cone + The whole system looks like this: @snippet gizmos/main.cpp System diff --git a/engine/src/cubos/engine/gizmos/gizmos.cpp b/engine/src/cubos/engine/gizmos/gizmos.cpp index 6dd4a2955..460b7f312 100644 --- a/engine/src/cubos/engine/gizmos/gizmos.cpp +++ b/engine/src/cubos/engine/gizmos/gizmos.cpp @@ -7,6 +7,7 @@ #include "renderer.hpp" #include "types/box.hpp" +#include "types/cut_cone.hpp" #include "types/line.hpp" using cubos::engine::DeltaTime; @@ -33,9 +34,8 @@ void Gizmos::color(const glm::vec3& color) mColor = color; } -void Gizmos::drawLine(const std::string& id, glm::vec3 from, glm::vec3 to, float lifespan, Space space) +void Gizmos::push(const std::shared_ptr& gizmo, const Space& space) { - auto gizmo = std::make_shared(id, from, to, mColor, lifespan); if (space == Space::World) { worldGizmos.push_back(gizmo); @@ -50,21 +50,14 @@ void Gizmos::drawLine(const std::string& id, glm::vec3 from, glm::vec3 to, float } } +void Gizmos::drawLine(const std::string& id, glm::vec3 from, glm::vec3 to, float lifespan, Space space) +{ + push(std::make_shared(id, from, to, mColor, lifespan), space); +} + void Gizmos::drawBox(const std::string& id, glm::vec3 corner, glm::vec3 oppositeCorner, float lifespan, Space space) { - auto gizmo = std::make_shared(id, corner, oppositeCorner, mColor, lifespan); - if (space == Space::World) - { - worldGizmos.push_back(gizmo); - } - else if (space == Space::View) - { - viewGizmos.push_back(gizmo); - } - else if (space == Space::Screen) - { - screenGizmos.push_back(gizmo); - } + push(std::make_shared(id, corner, oppositeCorner, mColor, lifespan), space); } void Gizmos::drawWireBox(const std::string& id, glm::vec3 corner, glm::vec3 oppositeCorner, float lifespan, Space space) @@ -93,3 +86,11 @@ void Gizmos::drawWireBox(const std::string& id, glm::vec3 corner, glm::vec3 oppo glm::vec3{oppositeCorner[0], corner[1], oppositeCorner[2]}, lifespan, space); drawLine(id, glm::vec3{oppositeCorner[0], oppositeCorner[1], corner[2]}, oppositeCorner, lifespan, space); } + +void Gizmos::drawCutCone(const std::string& id, glm::vec3 firstBaseCenter, float firstBaseRadius, + glm::vec3 secondBaseCenter, float secondBaseRadius, float lifespan, Space space) +{ + push(std::make_shared(id, firstBaseCenter, firstBaseRadius, secondBaseCenter, secondBaseRadius, + mColor, lifespan), + space); +} diff --git a/engine/src/cubos/engine/gizmos/renderer.cpp b/engine/src/cubos/engine/gizmos/renderer.cpp index ad0df9541..feaef0084 100644 --- a/engine/src/cubos/engine/gizmos/renderer.cpp +++ b/engine/src/cubos/engine/gizmos/renderer.cpp @@ -38,6 +38,7 @@ void GizmosRenderer::init(RenderDevice* currentRenderDevice) pipeline = renderDevice->createShaderPipeline(vs, ps); initLinePrimitive(); initBoxPrimitive(); + initCutConePrimitive(); } void GizmosRenderer::initLinePrimitive() @@ -90,3 +91,62 @@ void GizmosRenderer::initBoxPrimitive() 5, 7, 6, 5, 4, 7}; boxPrimitive.ib = renderDevice->createIndexBuffer(sizeof(indices), indices, IndexFormat::UInt, Usage::Static); } + +void GizmosRenderer::initCutConePrimitive() +{ + cutConePrimitive.vaDesc.elementCount = 1; + cutConePrimitive.vaDesc.elements[0].name = "position"; + cutConePrimitive.vaDesc.elements[0].type = Type::Float; + cutConePrimitive.vaDesc.elements[0].size = 3; + cutConePrimitive.vaDesc.elements[0].buffer.index = 0; + cutConePrimitive.vaDesc.elements[0].buffer.offset = 0; + cutConePrimitive.vaDesc.elements[0].buffer.stride = 3 * sizeof(float); + cutConePrimitive.vaDesc.shaderPipeline = pipeline; + + float verts[CutConeVertsPerBase * 6]; + + renderDevice->setShaderPipeline(pipeline); + cutConePrimitive.vb = renderDevice->createVertexBuffer(sizeof(verts), verts, Usage::Dynamic); + cutConePrimitive.vaDesc.buffers[0] = cutConePrimitive.vb; + cutConePrimitive.va = renderDevice->createVertexArray(cutConePrimitive.vaDesc); + + unsigned int indices[12 * CutConeVertsPerBase - 12]; + + int iIndex = 0; + // Sides + for (unsigned int i = 0; i < CutConeVertsPerBase - 1; i += 1) + { + indices[iIndex++] = i; + indices[iIndex++] = i + 1; + indices[iIndex++] = i + CutConeVertsPerBase; + indices[iIndex++] = i + 1; + indices[iIndex++] = i + CutConeVertsPerBase + 1; + indices[iIndex++] = i + CutConeVertsPerBase; + } + + // Last Side Faces + indices[iIndex++] = CutConeVertsPerBase - 1; + indices[iIndex++] = CutConeVertsPerBase; + indices[iIndex++] = (CutConeVertsPerBase * 2) - 1; + indices[iIndex++] = 0; + indices[iIndex++] = CutConeVertsPerBase; + indices[iIndex++] = CutConeVertsPerBase - 1; + + // Base A + for (unsigned int i = 0; i < CutConeVertsPerBase - 2; i += 1) + { + indices[iIndex++] = 0; + indices[iIndex++] = i + 1; + indices[iIndex++] = i + 2; + } + + // Base B + for (unsigned int i = CutConeVertsPerBase; i < (2 * CutConeVertsPerBase) - 2; i += 1) + { + indices[iIndex++] = CutConeVertsPerBase; + indices[iIndex++] = i + 1; + indices[iIndex++] = i + 2; + } + + cutConePrimitive.ib = renderDevice->createIndexBuffer(sizeof(indices), indices, IndexFormat::UInt, Usage::Static); +} diff --git a/engine/src/cubos/engine/gizmos/renderer.hpp b/engine/src/cubos/engine/gizmos/renderer.hpp index 6f8d3dc97..2a5ff7c5f 100644 --- a/engine/src/cubos/engine/gizmos/renderer.hpp +++ b/engine/src/cubos/engine/gizmos/renderer.hpp @@ -8,10 +8,12 @@ namespace cubos::engine class GizmosRenderer final { public: - cubos::core::gl::ShaderPipeline pipeline; ///< Shader pipeline to be used when drawing gizmos - cubos::core::gl::RenderDevice* renderDevice; ///< Active render device + static constexpr int CutConeVertsPerBase = 16; ///< Number of vertexes in each face of a cut cone gizmo. - /// @brief Set of buffers and structs that hold te information needed to draw a specific type of mesh + cubos::core::gl::ShaderPipeline pipeline; ///< Shader pipeline to be used when drawing gizmos. + cubos::core::gl::RenderDevice* renderDevice; ///< Active render device. + + /// @brief Set of buffers and structs that hold te information needed to draw a specific type of mesh. struct Primitive { cubos::core::gl::VertexBuffer vb; @@ -20,15 +22,17 @@ namespace cubos::engine cubos::core::gl::VertexArrayDesc vaDesc; }; - Primitive linePrimitive; ///< GL line information - Primitive boxPrimitive; ///< GL box information + Primitive linePrimitive; ///< GL line information. + Primitive boxPrimitive; ///< GL box information. + Primitive cutConePrimitive; ///< GL cut cone information. - /// @brief Sets up the GizmosRenderer to be used - /// @param renderDevice the current RenderDevice being used + /// @brief Sets up the render device to be used. + /// @param renderDevice the current Render device being used. void init(cubos::core::gl::RenderDevice* currentRenderDevice); private: void initLinePrimitive(); void initBoxPrimitive(); + void initCutConePrimitive(); }; } // namespace cubos::engine diff --git a/engine/src/cubos/engine/gizmos/types/box.hpp b/engine/src/cubos/engine/gizmos/types/box.hpp index 9ddaa13d8..079506258 100644 --- a/engine/src/cubos/engine/gizmos/types/box.hpp +++ b/engine/src/cubos/engine/gizmos/types/box.hpp @@ -8,19 +8,19 @@ namespace cubos::engine { - /// @brief A gizmo that is a filled box + /// @brief A gizmo that is a filled box. class BoxGizmo : public cubos::engine::Gizmos::Gizmo { glm::vec3 mPointA; glm::vec3 mPointB; public: - /// @brief BoxGizmo constructor - /// @param id identifier of the gizmo - /// @param corner point at one of the corners of the box - /// @param oppositeCorner point at the opposite corner of the box - /// @param color color for the gizmo to be drawn in - /// @param lifespan time the gizmo will remain on screen, in seconds + /// @brief Constructs. + /// @param id Identifier of the gizmo. + /// @param corner Point at one of the corners of the box. + /// @param oppositeCorner Point at the opposite corner of the box. + /// @param color Color for the gizmo to be drawn in. + /// @param lifespan Time the gizmo will remain on screen, in seconds. BoxGizmo(const std::string& id, glm::vec3 corner, glm::vec3 oppositeCorner, const glm::vec3& color, float lifespan) : cubos::engine::Gizmos::Gizmo(id, color, lifespan) @@ -29,8 +29,9 @@ namespace cubos::engine { } - /// @brief draws the gizmo to screen - /// @param renderer the GizmosRenderer in use + /// @brief Draws the gizmo to the screen. + /// @param renderer GizmosRenderer in use. + /// @param mvp Matrix containing projection and viewpoint transformations. void draw(cubos::engine::GizmosRenderer& renderer, const glm::mat<4, 4, float, glm::packed_highp>& mvp) override { auto* verts = static_cast(renderer.boxPrimitive.vb->map()); diff --git a/engine/src/cubos/engine/gizmos/types/cut_cone.hpp b/engine/src/cubos/engine/gizmos/types/cut_cone.hpp new file mode 100644 index 000000000..50652fef7 --- /dev/null +++ b/engine/src/cubos/engine/gizmos/types/cut_cone.hpp @@ -0,0 +1,85 @@ +#pragma once + +#include +#include + +#include + +#include "../renderer.hpp" + +namespace cubos::engine +{ + /// @brief A gizmo that is a cylinder with bases that can be different. + class CutConeGizmo : public Gizmos::Gizmo + { + glm::vec3 mPointA; + float mRadiusA; + glm::vec3 mPointB; + float mRadiusB; + + public: + /// @brief Constructs. + /// @param id Identifier of the gizmo. + /// @param firstBaseCenter Center of one of the bases. + /// @param firstBaseRadius Radius of one of the bases. + /// @param secondBaseCenter Center of the second base. + /// @param secondBaseRadius Radius of the second base. + /// @param color Color for the gizmo to be drawn in. + /// @param lifespan Time the gizmo will remain on screen, in seconds. + CutConeGizmo(const std::string& id, glm::vec3 firstBaseCenter, float firstBaseRadius, + glm::vec3 secondBaseCenter, float secondBaseRadius, const glm::vec3& color, float lifespan) + : Gizmos::Gizmo(id, color, lifespan) + , mPointA(firstBaseCenter) + , mRadiusA(firstBaseRadius) + , mPointB(secondBaseCenter) + , mRadiusB(secondBaseRadius) + { + } + + /// @brief Draws the gizmo to screen. + /// @param renderer GizmosRenderer in use. + /// @param mvp Matrix containing projection and viewpoint transformations. + void draw(GizmosRenderer& renderer, const glm::mat<4, 4, float, glm::packed_highp>& mvp) override + { + auto* verts = static_cast(renderer.cutConePrimitive.vb->map()); + + glm::vec3 n = glm::normalize(mPointB - mPointA); + glm::vec3 p = {n[1], -n[0], n[2]}; + + glm::vec3 pA = p * mRadiusA; + + for (int i = 0; i < GizmosRenderer::CutConeVertsPerBase; i++) + { + glm::vec3 vert = + glm::rotate(pA, (float)i * glm::radians(360.0F / (float)GizmosRenderer::CutConeVertsPerBase), n) + + mPointA; + verts[i] = {vert[0], vert[1], vert[2]}; + } + + glm::vec3 pB = p * mRadiusB; + + for (int i = GizmosRenderer::CutConeVertsPerBase; i < 2 * GizmosRenderer::CutConeVertsPerBase; i++) + { + glm::vec3 vert = + glm::rotate(pB, (float)i * glm::radians(360.0F / (float)GizmosRenderer::CutConeVertsPerBase), n) + + mPointB; + verts[i] = {vert[0], vert[1], vert[2]}; + } + + renderer.cutConePrimitive.vb->unmap(); + + renderer.renderDevice->setVertexArray(renderer.cutConePrimitive.va); + renderer.renderDevice->setIndexBuffer(renderer.cutConePrimitive.ib); + + auto mvpBuffer = + renderer.renderDevice->createConstantBuffer(sizeof(glm::mat4), &mvp, cubos::core::gl::Usage::Static); + renderer.pipeline->getBindingPoint("MVP")->bind(mvpBuffer); + + renderer.pipeline->getBindingPoint("objColor")->setConstant(mColor); + renderer.renderDevice->setRasterState( + renderer.renderDevice->createRasterState(cubos::core::gl::RasterStateDesc{})); + renderer.renderDevice->drawTrianglesIndexed(0, + 12 * cubos::engine::GizmosRenderer::CutConeVertsPerBase - 12); + } + }; +} // namespace cubos::engine diff --git a/engine/src/cubos/engine/gizmos/types/line.hpp b/engine/src/cubos/engine/gizmos/types/line.hpp index 24afde22d..67f3b5456 100644 --- a/engine/src/cubos/engine/gizmos/types/line.hpp +++ b/engine/src/cubos/engine/gizmos/types/line.hpp @@ -8,19 +8,19 @@ namespace cubos::engine { - /// @brief A gizmo that is a line connecting two points + /// @brief A gizmo that is a line connecting two points. class LineGizmo : public cubos::engine::Gizmos::Gizmo { glm::vec3 mPointA; glm::vec3 mPointB; public: - /// @brief LineGizmo constructor - /// @param id identifier of the gizmo - /// @param from point at one of the ends of the line - /// @param to point at the other end of the line - /// @param color color for the gizmo to be drawn in - /// @param lifespan time the gizmo will remain on screen, in seconds + /// @brief Constructs. + /// @param id Identifier of the gizmo. + /// @param from Point at one of the ends of the line. + /// @param to Point at the other end of the line. + /// @param color Color for the gizmo to be drawn in. + /// @param lifespan Time the gizmo will remain on screen, in seconds. LineGizmo(const std::string& id, glm::vec3 from, glm::vec3 to, const glm::vec3& color, float lifespan) : cubos::engine::Gizmos::Gizmo(id, color, lifespan) , mPointA(from) @@ -28,8 +28,9 @@ namespace cubos::engine { } - /// @brief draws the gizmo to screen - /// @param renderer the GizmosRenderer in use + /// @brief Draws the gizmo to the screen. + /// @param renderer GizmosRenderer in use. + /// @param mvp Matrix containing projection and viewpoint transformations. void draw(cubos::engine::GizmosRenderer& renderer, const glm::mat<4, 4, float, glm::packed_highp>& mvp) override { auto* verts = static_cast(renderer.linePrimitive.vb->map());