From 71ff1adf6155c053c5e3282f118a7e1eff530f71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Zaj=C4=85c?= Date: Sun, 31 Dec 2023 02:23:31 +0000 Subject: [PATCH] refactor: added lepus::engine::objects::Mesh and lepus::gfx::GLMesh (#37) * refactor: added early Mesh and GLMesh class * chore: fixed missing header on linux * refactor: replaced single global VBO/IBO in ApiGL with GLMesh * chore: removed unnecessary header include * chore: fixed header path * chore: fixed incorrect type qualifiers * refactor: added GLMesh copy/move constructors * docs: added comments * chore: fixed typo in CMakeLists * docs: update comment * fix: fixed wrong index count in GraphicsApiGL::Draw() * fix: fixed extra copy --- CMakeLists.txt | 17 +- src/lepus/engine/Objects/Mesh.h | 298 +++++++++++++++ src/lepus/gfx/GraphicsEngine/Apis/ApiGL.h | 9 +- .../gfx/GraphicsEngine/Apis/ApiGL/ApiGL.cpp | 41 +- .../GraphicsEngine/Apis/ApiGL/Types/GLMesh.h | 135 +++++++ .../Apis/ApiGL/Types/GLMesh/GLMesh.cpp | 29 ++ src/lepus/utility/Primitives.h | 360 +++++++++--------- src/lepus/utility/Primitives/Shared.cpp | 5 + 8 files changed, 693 insertions(+), 201 deletions(-) create mode 100644 src/lepus/engine/Objects/Mesh.h create mode 100644 src/lepus/gfx/GraphicsEngine/Apis/ApiGL/Types/GLMesh.h create mode 100644 src/lepus/gfx/GraphicsEngine/Apis/ApiGL/Types/GLMesh/GLMesh.cpp create mode 100644 src/lepus/utility/Primitives/Shared.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index e3ef01a..62f8c68 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -70,9 +70,9 @@ include_directories(${CMAKE_SOURCE_DIR}/3rdparty/imgui) add_executable(LepusDemo src/examples/demo/DemoApp.h src/examples/demo/main.cpp) # LepusUtility -file(GLOB_RECURSE LEPUSUTILITY_HEADERS ${LEPUS_SRC_DIR}/utility/*.h ${LEPUS_SRC_DIR}/utility/**/*.h) -add_custom_target(LepusUtility SOURCES ${LEPUSUTILITY_HEADERS}) -source_group(TREE ${LEPUS_SRC_DIR}/utility FILES ${LEPUSUTILITY_HEADERS}) +# file(GLOB_RECURSE LEPUSUTILITY_HEADERS ${LEPUS_SRC_DIR}/utility/*.h ${LEPUS_SRC_DIR}/utility/**/*.h) +# add_custom_target(LepusUtility SOURCES ${LEPUSUTILITY_HEADERS}) +# source_group(TREE ${LEPUS_SRC_DIR}/utility FILES ${LEPUSUTILITY_HEADERS}) # LepusGfx shaders file(GLOB LEPUS3D_SHADERS_SRC ${CMAKE_SOURCE_DIR}/Content/GLSL/*.frag ${CMAKE_SOURCE_DIR}/Content/GLSL/*.vert) @@ -90,6 +90,11 @@ file(GLOB_RECURSE LepusEngine_SRC ${LEPUS_SRC_DIR}/engine/*.h ${LEPUS_SRC_DIR}/e add_library(LepusEngine ${LepusEngine_SRC}) source_group(TREE ${LEPUS_SRC_DIR}/engine FILES ${LepusEngine_SRC}) +# LepusUtility sources +file(GLOB_RECURSE LepusUtility_SRC ${LEPUS_SRC_DIR}/utility/*.h ${LEPUS_SRC_DIR}/utility/*.cpp ${LEPUS_SRC_DIR}/utility/**/*.h ${LEPUS_SRC_DIR}/utility/**/*.cpp) +add_library(LepusUtility ${LepusUtility_SRC}) +source_group(TREE ${LEPUS_SRC_DIR}/utility FILES ${LepusUtility_SRC}) + # LepusSystem sources file(GLOB_RECURSE LepusSystem_SRC ${LEPUS_SRC_DIR}/system/*.h ${LEPUS_SRC_DIR}/system/*.cpp ${LEPUS_SRC_DIR}/system/**/*.h ${LEPUS_SRC_DIR}/system/**/*.cpp) add_library(LepusSystem ${LepusSystem_SRC}) @@ -155,8 +160,8 @@ if(OpenGL::GL) elseif(OpenGL::OpenGL) set(GL_LIBRARY OpenGL::OpenGL) endif(OpenGL::GL) -target_link_libraries(LepusGfx PRIVATE GL3W glfw ${GL_LIBRARY} DearImgui LepusEngine LepusSystem) -target_link_libraries(LepusDemo PRIVATE DearImgui LepusGfx LepusEngine) +target_link_libraries(LepusGfx PRIVATE GL3W glfw ${GL_LIBRARY} DearImgui LepusEngine LepusUtility LepusSystem) +target_link_libraries(LepusDemo PRIVATE DearImgui LepusGfx LepusUtility LepusEngine) # Copy content (models, GLSL, etc.) file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/Content) @@ -230,4 +235,4 @@ else() foreach(Target IN LISTS LepusTargets) target_compile_options(${Target} PRIVATE -Wall -Wextra -Wpedantic) endforeach() -endif() \ No newline at end of file +endif() diff --git a/src/lepus/engine/Objects/Mesh.h b/src/lepus/engine/Objects/Mesh.h new file mode 100644 index 0000000..1595afd --- /dev/null +++ b/src/lepus/engine/Objects/Mesh.h @@ -0,0 +1,298 @@ +#ifndef LEPUS_ENGINE_OBJECTS_MESH +#define LEPUS_ENGINE_OBJECTS_MESH + +#include +#include +#include + +namespace lepus +{ + namespace engine + { + /// @brief Different vertex formats supported by the engine. + enum MeshVertexFormat + { + /// @brief (Default) 3 positional components (XYZ) for each vertex + VVV, + Invalid + }; + + namespace objects + { + /// @brief Base class for Mesh data in the engine. + /// + /// Can be specialised for different parts of the engine (e.g.rendering, physics) by inheriting and adding API-specific logic as needed. + class Mesh + { + private: + /// @brief The vertex format used by the mesh data. + MeshVertexFormat m_Format = MeshVertexFormat::Invalid; + + /// @brief Vertex data + void* m_Vertices = nullptr; + size_t m_szVertices = 0; + + /// @brief Index data (optional, but common enough to be included in the base class for simplicity's sake) + uint32_t* m_Indices = nullptr; + size_t m_IndexCount = 0; + + /// @brief Does this Mesh own the vertex/index data? If true, this Mesh will delete the data when Dispose is called. + bool m_OwnData = false; + + /// @brief Is the Mesh indexed? + bool m_IsIndexed = false; + + protected: + inline void CopyInternal(const Mesh& other) + { + m_Format = other.m_Format; + m_Vertices = other.m_Vertices; + m_szVertices = other.m_szVertices; + m_IndexCount = other.m_IndexCount; + m_Indices = other.m_Indices; + m_IsIndexed = other.m_IsIndexed; + + // We're not making a copy of the data, so we don't own it. + // TODO: Add some kind of reference counting via a resource manager, so if we copy an existing mesh, its data doesn't get disposed until its copies are. + m_OwnData = false; + } + + /// @brief Always called after constructing a new Mesh instance (excluding move/copy ctors). + inline virtual void InitInternal() + { + + } + + inline void MoveInternal(Mesh& other) + { + m_Format = other.m_Format; + + m_Vertices = other.m_Vertices; + m_szVertices = other.m_szVertices; + + m_Indices = other.m_Indices; + m_IndexCount = other.m_IndexCount; + + m_IsIndexed = other.m_IsIndexed; + m_OwnData = other.m_OwnData; + + other.m_Format = MeshVertexFormat::Invalid; + other.m_IndexCount = 0; + other.m_szVertices = 0; + other.m_Indices = nullptr; + other.m_Vertices = nullptr; + other.m_OwnData = false; + other.m_IsIndexed = false; + } + +#define LEPUS_MESH_CONSTRUCTOR(MeshClass) \ + public:\ + inline MeshClass(lepus::engine::MeshVertexFormat format = lepus::engine::MeshVertexFormat::VVV) {Init(format);} \ + inline MeshClass(const lepus::utility::Primitive& primitive, bool copy = false) {Init((void*)primitive.GetVertices(), primitive.VertexBufferSize(), lepus::engine::MeshVertexFormat::VVV, (uint32_t*)primitive.GetIndices(), primitive.IndexCount(), copy);} \ + inline MeshClass(void* vertices, size_t szVertices, lepus::engine::MeshVertexFormat format = lepus::engine::MeshVertexFormat::VVV, uint32_t* indices = nullptr, size_t indexCount = 0, bool copy = false) {Init(vertices, szVertices, format, indices, indexCount, copy);} \ + inline virtual ~MeshClass() {Dispose();} + + LEPUS_MESH_CONSTRUCTOR(Mesh); + + public: + + /// @brief Copy constructor + /// @param other Mesh object to copy + Mesh(const Mesh& other) + { + CopyInternal(other); + } + + /// @brief Copy assignment + /// @param other Mesh object to copy + /// @return + Mesh& operator=(const Mesh& other) + { + CopyInternal(other); + return *this; + } + + /// @brief Move constructor + /// @param other Rvalue to initialise the Mesh object with. + Mesh(Mesh&& other) + { + m_Format = MeshVertexFormat::Invalid; + m_Vertices = nullptr; + m_szVertices = 0; + m_OwnData = false; + m_Indices = nullptr; + m_IndexCount = 0; + m_IsIndexed = false; + + *this = std::move(other); + } + + /// @brief Move assignment + /// @param other Mesh rvalue to assign + /// @return The current object with vertices, indices and any metadata from the rvalue. + Mesh& operator=(Mesh&& other) + { + // Prevent self-assignment. + if (this != &other) + { + // Make sure our data is disposed first if necessary so memory doesn't leak. + Dispose(); + + MoveInternal(other); + } + + return *this; + } + + /// @brief Default constructor. Initialises an empty mesh with just a vertex format and no vertices. + /// @param format Vertex format to use. + inline void Init(MeshVertexFormat format = MeshVertexFormat::VVV) + { + m_Format = format; + m_OwnData = false; + m_IsIndexed = false; + m_szVertices = 0; + m_IndexCount = 0; + m_Vertices = nullptr; + m_Indices = nullptr; + + InitInternal(); + } + + /// @brief Initialises the Mesh with geometry data from a built-in primitive. + /// @param primitive A built-in primitive instance from LepusUtility. + /// @param copy Should the data be copied? + inline void Init(const lepus::utility::Primitive& primitive, bool copy = false) + { + Init((void*)primitive.GetVertices(), primitive.VertexBufferSize(), MeshVertexFormat::VVV, (uint32_t*)primitive.GetIndices(), primitive.IndexCount(), copy); + } + + /// @brief Initialises the Mesh with provided geometry data. + /// @param vertices Vertex data + /// @param szVertices Size of the vertex data (in bytes) + /// @param format Vertex data format + /// @param indices Index data. Optional - pass nullptr for non-indexed. + /// @param indexCount Number of indices. + /// @param copy Should the data be copied? + inline void Init(void* vertices, size_t szVertices, MeshVertexFormat format = MeshVertexFormat::VVV, uint32_t* indices = nullptr, size_t indexCount = 0, bool copy = false) + { + m_Format = format; + m_IsIndexed = indexCount > 0 && indices != nullptr; + m_szVertices = szVertices; + + if (copy) + { + m_Vertices = malloc(szVertices); + memcpy(m_Vertices, vertices, szVertices); + + if (m_IsIndexed) + { + m_IndexCount = indexCount; + m_Indices = new uint32_t[indexCount]; + memcpy(m_Indices, indices, indexCount * sizeof(uint32_t)); + } + + m_OwnData = true; + } + else + { + m_Vertices = vertices; + + if (m_IsIndexed) + { + m_IndexCount = indexCount; + m_Indices = indices; + } + + m_OwnData = false; + } + + InitInternal(); + } + + /// @brief Gets the vertex format used by this Mesh. + /// @return The MeshVertexFormat this Mesh was initialised with. + inline MeshVertexFormat GetFormat() const { return m_Format; } + + /// @brief Gets the pointer to vertex data used by this Mesh. + /// @return A constant pointer to the vertex data. + const void* GetVertices() const + { + return m_Vertices; + } + + /// @brief Gets the pointer to index data used by this Mesh. + /// @return A constant pointer to the index data. + const uint32_t* GetIndices() const + { + return m_Indices; + } + + /// @brief Gets the size of a single vertex based on the vertex format used by this Mesh. + /// @return An unsigned 8-bit integer defining a single vertex size. + uint8_t GetSingleVertexSize() const + { + switch (m_Format) + { + case MeshVertexFormat::VVV: + return sizeof(float) * 3; + case MeshVertexFormat::Invalid: + default: + return 0; + } + } + + /// @brief Gets the number of vertices in this Mesh. + /// @return An unsigned 64-bit integer defining the number of vertices (TODO: that's a maximum of 18,446,744,073,709,551,615 vertices... do we really need 64-bit for that?) + size_t inline VertexCount() const + { + return m_szVertices / GetSingleVertexSize(); + } + + /// @brief Gets the size of the vertex data. + /// @return An unsigned 64-bit integer defining the size of the vertex data (in bytes). + size_t inline VertexBufferSize() const + { + return m_szVertices; + } + + /// @brief Gets the size of the index data. + /// @return An unsigned 64-bit integer defining the size of the index data (in bytes). + size_t inline IndexBufferSize() const + { + return m_IndexCount * sizeof(uint32_t); + } + + /// @brief Gets the number of indices in this Mesh. + /// @return An unsigned 64-bit integer defining the number of indices that make up this Mesh (TODO: again, isn't 64 bits overkill?) + size_t inline IndexCount() const + { + return m_IndexCount; + } + + /// @brief Releases any resources and data held by this Mesh as needed. + inline virtual void Dispose() + { + if (m_OwnData) + { + if (m_Vertices) + { + free(m_Vertices); + m_Vertices = nullptr; + m_szVertices = 0; + } + + if (m_Indices) + { + delete[] m_Indices; + m_Indices = nullptr; + m_IndexCount = 0; + } + } + } + }; + } + } +} + +#endif \ No newline at end of file diff --git a/src/lepus/gfx/GraphicsEngine/Apis/ApiGL.h b/src/lepus/gfx/GraphicsEngine/Apis/ApiGL.h index 1fd01fb..334cb54 100644 --- a/src/lepus/gfx/GraphicsEngine/Apis/ApiGL.h +++ b/src/lepus/gfx/GraphicsEngine/Apis/ApiGL.h @@ -9,6 +9,7 @@ #include #include "ApiGL/Bindings.h" +#include "ApiGL/Types/GLMesh.h" namespace lepus { @@ -64,16 +65,17 @@ namespace lepus { friend class GraphicsApiGLOptions; private: + static const uint8_t _meshCount = 2; struct { /// @brief Handle to the vertex array objects. GLuint vao; /// @brief Handle to the global VBO. - GLuint vbo; + GLuint vbo[_meshCount] = { 0, 0 }; /// @brief Handle to the global IBO. - GLuint ibo; + GLuint ibo[_meshCount] = { 0, 0 }; /// @brief List with all uniforms used by the API. // TODO: Change to array - might get better cache/locality to improve access times. @@ -86,8 +88,7 @@ namespace lepus GLuint m_Programs[GraphicsApiGLOptions::ProgramCount]; - // TODO: separate - LepusUtility::Primitive m_CubeGeometry = LepusUtility::Primitives::Cube(); + GLMesh m_Meshes[_meshCount]; private: void SetupVertexArrays(); diff --git a/src/lepus/gfx/GraphicsEngine/Apis/ApiGL/ApiGL.cpp b/src/lepus/gfx/GraphicsEngine/Apis/ApiGL/ApiGL.cpp index 1aa3a60..0ae519d 100644 --- a/src/lepus/gfx/GraphicsEngine/Apis/ApiGL/ApiGL.cpp +++ b/src/lepus/gfx/GraphicsEngine/Apis/ApiGL/ApiGL.cpp @@ -1,5 +1,4 @@ #include "../ApiGL.h" -#include using namespace lepus::gfx; @@ -19,19 +18,16 @@ void GraphicsApiGL::SetupBuffers() { glBindVertexArray(m_Pipeline.vao); - // Create a global VBO and upload triangle data to it. - glCreateBuffers(1, &m_Pipeline.vbo); - //const GLfloat vertices[] = { -0.5f, -0.5f, 0.f, 0.5f, -0.5f, 0.f, 0.f, 0.5f, 0.f }; - glBindBuffer(GL_ARRAY_BUFFER, m_Pipeline.vbo); - glEnableVertexAttribArray(0); - glVertexAttribPointer(0, 3, GL_FLOAT, GL_TRUE, 0, 0); - glBufferData(GL_ARRAY_BUFFER, (GLsizeiptr)m_CubeGeometry.VertexBufferSize(), m_CubeGeometry.GetVertices(), GL_STATIC_DRAW); - - // Create a global IBO and upload triangle index data to it. - glCreateBuffers(1, &m_Pipeline.ibo); - //const GLuint indices[] = { 0, 1, 2 }; - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_Pipeline.ibo); - glBufferData(GL_ELEMENT_ARRAY_BUFFER, (GLsizeiptr)m_CubeGeometry.IndexBufferSize(), m_CubeGeometry.GetIndices(), GL_STATIC_DRAW); + // Creating a mesh from built-in primitive geometry. + m_Meshes[0] = GLMesh(lepus::utility::Primitives::Cube()); + m_Pipeline.vbo[0] = m_Meshes[0].GetVBO(); + m_Pipeline.ibo[0] = m_Meshes[0].GetIBO(); + + // Creating a mesh by copying the first mesh. + // Vertex & index data is shared with the first mesh, but uploaded to a separate pair of OpenGL buffers. + m_Meshes[1] = GLMesh(m_Meshes[0]); + m_Pipeline.vbo[1] = m_Meshes[1].GetVBO(); + m_Pipeline.ibo[1] = m_Meshes[1].GetIBO(); } void GraphicsApiGL::SetupShaders() @@ -56,14 +52,14 @@ void GraphicsApiGL::SetupShaders() void GraphicsApiGL::SetupUniforms() { // Proj matrix - auto* proj = new lepus::gfx::GLMatrixUniformBinding(glGetUniformLocation(m_Programs[0], "PROJ")); + auto* proj = new lepus::gfx::GLMatrixUniformBinding(glGetUniformLocation(m_Programs[0], LEPUS_GFX_UNIFORMS_GLOBAL_PROJECTION_MATRIX)); m_Pipeline.uniforms.push_front((lepus::gfx::GLUniformBinding*)(proj)); - m_Pipeline.uniformMap.insert_or_assign("PROJ", reinterpret_cast*>(proj)); + m_Pipeline.uniformMap.insert_or_assign(LEPUS_GFX_UNIFORMS_GLOBAL_PROJECTION_MATRIX, reinterpret_cast*>(proj)); // View matrix - auto* view = new lepus::gfx::GLMatrixUniformBinding(glGetUniformLocation(m_Programs[0], "VIEW")); + auto* view = new lepus::gfx::GLMatrixUniformBinding(glGetUniformLocation(m_Programs[0], LEPUS_GFX_UNIFORMS_GLOBAL_VIEW_MATRIX)); m_Pipeline.uniforms.push_front((lepus::gfx::GLUniformBinding*)(view)); - m_Pipeline.uniformMap.insert_or_assign("VIEW", reinterpret_cast*>(view)); + m_Pipeline.uniformMap.insert_or_assign(LEPUS_GFX_UNIFORMS_GLOBAL_VIEW_MATRIX, reinterpret_cast*>(view)); } void GraphicsApiGL::CreatePipeline() @@ -108,10 +104,13 @@ void GraphicsApiGL::Draw() glUseProgram(m_Programs[0]); glBindVertexArray(m_Pipeline.vao); - glBindBuffer(GL_ARRAY_BUFFER, m_Pipeline.vbo); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_Pipeline.ibo); + for (uint8_t meshIndex = 0; meshIndex < _meshCount; meshIndex++) + { + glBindBuffer(GL_ARRAY_BUFFER, m_Pipeline.vbo[meshIndex]); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_Pipeline.ibo[meshIndex]); - glDrawElements(GL_TRIANGLES, (GLsizei)m_CubeGeometry.IndexCount(), GL_UNSIGNED_INT, 0); + glDrawElements(GL_TRIANGLES, (GLsizei)m_Meshes[meshIndex].IndexCount(), GL_UNSIGNED_INT, 0); + } } void GraphicsApiGL::ClearFrameBuffer(float r, float g, float b) diff --git a/src/lepus/gfx/GraphicsEngine/Apis/ApiGL/Types/GLMesh.h b/src/lepus/gfx/GraphicsEngine/Apis/ApiGL/Types/GLMesh.h new file mode 100644 index 0000000..b82954e --- /dev/null +++ b/src/lepus/gfx/GraphicsEngine/Apis/ApiGL/Types/GLMesh.h @@ -0,0 +1,135 @@ +#ifndef LEPUS_GFX_GL_MESH +#define LEPUS_GFX_GL_MESH + +#include +#include + +namespace lepus +{ + namespace gfx + { + /// @brief A Mesh specialisation to create and manage OpenGL resources for mesh data as needed. + class GLMesh : public lepus::engine::objects::Mesh + { + private: + GLuint m_VBO; + GLuint m_IBO; + + bool m_HasVBO; + bool m_HasIBO; + + /// @brief Creates a Vertex Buffer Object for the mesh data. + void _CreateVBO(); + /// @brief Creates an Index Buffer Object for the mesh data. + void _CreateIBO(); + + protected: + inline void InitInternal() override + { + m_VBO = 0; + m_IBO = 0; + m_HasIBO = false; + m_HasVBO = false; + + // Avoid creating a VBO or IBO if the size is 0 - this is likely because the mesh is being created on the stack + // with the default constructor before a VAO was bound. This will only result in a crash. + if (VertexBufferSize() > 0) + { + _CreateVBO(); + } + + if (IndexCount() > 0) + { + _CreateIBO(); + } + } + + inline void CopyInternal(const GLMesh& other) + { + Mesh::CopyInternal(other); + InitInternal(); + } + + public: + LEPUS_MESH_CONSTRUCTOR(GLMesh); + + /// @brief Copy constructor + /// @param other GLMesh object to copy. + GLMesh(const GLMesh& other) + { + CopyInternal(other); + } + + /// @brief Move constructor. Moves all base Mesh data (including vertices and indices) as well as OpenGL buffer names. + /// @param other GLMesh rvalue to move data from. + GLMesh(GLMesh&& other) + { + // Calls the move-assignment operator as the logic is the same. + *this = std::move(other); + } + + /// @brief Copy-assignment operator + /// @param other GLMesh object to copy. + /// @return A reference to a GLMesh object with data internally copied from the assigned object. + GLMesh& operator=(const GLMesh& other) + { + CopyInternal(other); + return *this; + } + + /// @brief Move-assignment operator + /// @param other GLMesh rvalue to move data from. + /// @return A reference to a GLMesh object with vertex & index data as well as OpenGL buffers moved from the assigned object. + GLMesh& operator=(GLMesh&& other) + { + if (this != &other) + { + Dispose(); + + MoveInternal(other); + + m_IBO = other.m_IBO; + m_VBO = other.m_VBO; + m_HasIBO = other.m_HasIBO; + m_HasVBO = other.m_HasVBO; + + other.m_HasIBO = false; + other.m_HasVBO = false; + other.m_IBO = 0; + other.m_VBO = 0; + } + + + return *this; + } + + /// @brief Gets the Vertex Buffer Object used for this Mesh. + /// @return An OpenGL Vertex Buffer Object name that can be used with glBindBuffer. + inline GLuint GetVBO() const { return m_VBO; } + + /// @brief Gets the Index Buffer Object used for this Mesh. + /// @return An OpenGL Index Buffer Object name that can be used with glBindBuffer. + inline GLuint GetIBO() const { return m_IBO; } + + /// @brief Specialised Dispose() that, in addition to calling base Dispose(), releases OpenGL Vertex and Index Buffer Objects as needed. + inline void Dispose() override + { + Mesh::Dispose(); + + if (m_HasVBO) + { + glDeleteBuffers(1, &m_VBO); + m_HasVBO = false; + } + + if (m_HasIBO) + { + glDeleteBuffers(1, &m_IBO); + m_HasIBO = false; + } + } + }; + } +} + +#endif \ No newline at end of file diff --git a/src/lepus/gfx/GraphicsEngine/Apis/ApiGL/Types/GLMesh/GLMesh.cpp b/src/lepus/gfx/GraphicsEngine/Apis/ApiGL/Types/GLMesh/GLMesh.cpp new file mode 100644 index 0000000..3fe635a --- /dev/null +++ b/src/lepus/gfx/GraphicsEngine/Apis/ApiGL/Types/GLMesh/GLMesh.cpp @@ -0,0 +1,29 @@ +#include "../GLMesh.h" +using namespace lepus::gfx; + +void GLMesh::_CreateVBO() +{ + if (GetFormat() != lepus::engine::MeshVertexFormat::Invalid) + { + glBindBuffer(GL_ARRAY_BUFFER, 0); + glCreateBuffers(1, &m_VBO); + glBindBuffer(GL_ARRAY_BUFFER, m_VBO); + glEnableVertexAttribArray(0); + glVertexAttribPointer(0, 3, GL_FLOAT, GL_TRUE, 0, 0); + glBufferData(GL_ARRAY_BUFFER, (GLsizeiptr)VertexBufferSize(), GetVertices(), GL_STATIC_DRAW); + glBindBuffer(GL_ARRAY_BUFFER, 0); + + m_HasVBO = true; + } +} + +void GLMesh::_CreateIBO() +{ + // Create an IBO and upload index data to it. + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + glCreateBuffers(1, &m_IBO); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_IBO); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, (GLsizeiptr)IndexBufferSize(), GetIndices(), GL_STATIC_DRAW); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + m_HasIBO = true; +} \ No newline at end of file diff --git a/src/lepus/utility/Primitives.h b/src/lepus/utility/Primitives.h index ab0a0c6..b409eee 100644 --- a/src/lepus/utility/Primitives.h +++ b/src/lepus/utility/Primitives.h @@ -3,202 +3,222 @@ #include #include +#include -namespace LepusUtility +namespace lepus { - class Primitive + namespace utility { - private: - float* m_Verts; - size_t m_VertCount; - uint32_t* m_Indices; - size_t m_IndexCount; - - public: - /// @brief Number of floats for each vertex. Currently, we only store position (XYZ). - static const unsigned char NbComponents = 3; - - // Move constructor - Primitive(Primitive&& other) + class Primitive { - Primitive(); + private: + float* m_Verts; + size_t m_VertCount; + uint32_t* m_Indices; + size_t m_IndexCount; + + public: + /// @brief Number of floats for each vertex. Currently, we only store position (XYZ). + static const unsigned char NbComponents = 3; + + // Move constructor + Primitive(Primitive&& other) + { + Primitive(); - m_Verts = other.m_Verts; - m_VertCount = other.m_VertCount; - m_Indices = other.m_Indices; - m_IndexCount = other.m_IndexCount; + m_Verts = other.m_Verts; + m_VertCount = other.m_VertCount; + m_Indices = other.m_Indices; + m_IndexCount = other.m_IndexCount; - // Prevent the moved resource from altering the pointers. - other.m_Verts = nullptr; - other.m_Indices = nullptr; - } + // Prevent the moved resource from altering the pointers. + other.m_Verts = nullptr; + other.m_Indices = nullptr; + } - Primitive& operator=(Primitive&& other) - { - if (this != &other) + Primitive& operator=(Primitive&& other) { - if (m_Verts) - { - delete[] m_Verts; - } - - if (m_Indices) + if (this != &other) { - delete[] m_Indices; + if (m_Verts) + { + delete[] m_Verts; + } + + if (m_Indices) + { + delete[] m_Indices; + } + + m_Verts = other.m_Verts; + m_Indices = other.m_Indices; + m_VertCount = other.m_VertCount; + m_IndexCount = other.m_IndexCount; } - m_Verts = other.m_Verts; - m_Indices = other.m_Indices; - m_VertCount = other.m_VertCount; - m_IndexCount = other.m_IndexCount; + return *this; } - return *this; - } + /// @brief Creates a new primitive with the provided vertices. + /// @param vertexPositions Array of tightly packed XYZ positions for each vertex. + /// @param nbVertexPositions Number of vertex positions - do not confuse with length of vertexPositions. + Primitive(const float* const vertexPositions, size_t nbVertexPositions, const uint32_t* const indices, size_t nbIndices) + { + m_Verts = nullptr; + m_VertCount = 0; + m_Indices = nullptr; + m_IndexCount = 0; - /// @brief Creates a new primitive with the provided vertices. - /// @param vertexPositions Array of tightly packed XYZ positions for each vertex. - /// @param nbVertexPositions Number of vertex positions - do not confuse with length of vertexPositions. - Primitive(const float* const vertexPositions, size_t nbVertexPositions, const uint32_t* const indices, size_t nbIndices) - { - m_Verts = nullptr; - m_VertCount = 0; - m_Indices = nullptr; - m_IndexCount = 0; + Init(vertexPositions, nbVertexPositions, indices, nbIndices); + } - Init(vertexPositions, nbVertexPositions, indices, nbIndices); - } + Primitive() + { + m_Verts = nullptr; + m_VertCount = 0; + m_Indices = nullptr; + m_IndexCount = 0; + } - Primitive() - { - m_Verts = nullptr; - m_VertCount = 0; - m_Indices = nullptr; - m_IndexCount = 0; - } - - /// @brief Initialises the primitive with the provided vertices. - /// @param vertexPositions Array of tightly packed XYZ positions for each vertex. - /// @param nbVertexPositions Number of vertex positions - do not confuse with length of vertexPositions. - void Init(const float* const vertexPositions, size_t nbVertexPositions, const uint32_t* const indices, size_t nbIndices) - { - assert(m_Verts == nullptr); + /// @brief Initialises the primitive with the provided vertices. + /// @param vertexPositions Array of tightly packed XYZ positions for each vertex. + /// @param nbVertexPositions Number of vertex positions - do not confuse with length of vertexPositions. + void Init(const float* const vertexPositions, size_t nbVertexPositions, const uint32_t* const indices, size_t nbIndices) + { + assert(m_Verts == nullptr); - m_Verts = new float[nbVertexPositions * NbComponents]; - m_VertCount = nbVertexPositions; - memcpy(m_Verts, vertexPositions, nbVertexPositions * NbComponents * sizeof(float)); + m_Verts = new float[nbVertexPositions * NbComponents]; + m_VertCount = nbVertexPositions; + memcpy(m_Verts, vertexPositions, nbVertexPositions * NbComponents * sizeof(float)); - m_Indices = new uint32_t[nbIndices]; - m_IndexCount = nbIndices; - memcpy(m_Indices, indices, nbIndices * sizeof(uint32_t)); - } + m_Indices = new uint32_t[nbIndices]; + m_IndexCount = nbIndices; + memcpy(m_Indices, indices, nbIndices * sizeof(uint32_t)); + } - const float* GetVertices() - { - return m_Verts; - } + const float* GetVertices() const + { + return m_Verts; + } - const uint32_t* GetIndices() - { - return m_Indices; - } + const uint32_t* GetIndices() const + { + return m_Indices; + } - const size_t inline VertexCount() - { - return m_VertCount; - } + size_t inline VertexCount() const + { + return m_VertCount; + } - constexpr size_t const inline VertexBufferSize() - { - return m_VertCount * sizeof(float) * NbComponents; - } + size_t inline VertexBufferSize() const + { + return m_VertCount * sizeof(float) * NbComponents; + } - constexpr size_t const inline IndexBufferSize() - { - return m_IndexCount * sizeof(uint32_t); - } + size_t inline IndexBufferSize() const + { + return m_IndexCount * sizeof(uint32_t); + } - const size_t inline IndexCount() - { - return m_IndexCount; - } - }; + size_t inline IndexCount() const + { + return m_IndexCount; + } + }; - class Primitives - { - public: - static const inline Primitive Cube() + class Primitives { - // Assuming negative left and negative up - const float cubeVertices[] = { - // Front: - -0.5f, 0.5f, -0.5f, // top-left (0) - 0.5f, 0.5f, -0.5f, // top-right (1) - -0.5f, -0.5f, -0.5f, // bottom-left (2) - 0.5f, -0.5f, -0.5f, // bottom-right (3) - - // Left: - -0.5f, 0.5f, 0.5f, // top-left (4) - // use front top-left (0) as top-right - -0.5f, -0.5f, 0.5f, // bottom-left (5) - // use front bottom-left (2) as bottom-right - - // Back: - 0.5f, 0.5f, 0.5f, // top-left (6) - // reuse left top-left (4) as top-right - 0.5f, -0.5f, 0.5f, // bottom-left (7) - // use left bottom-left (5) as bottom-right - - // Top: - // use left top-left (4) as top-left - // use back top-left (6) as top-right - // use left top-right (0) as bottom-left - // use front top-right (1) as bottom-right - - // Right: - // use front top-right (1) as top-left - // use back top-left (6) as top-right - // use front bottom-right (3) as bottom-left - // use back bottom-left (7) as bottom-right - - // Bottom: - // use front bottom-left (2) as top-left - // use front bottom-right (3) as top-right - // use back bottom-right (5) as bottom-left - // use back bottom-left (7) as bottom-right - }; - - const uint32_t indices[] = { - // Front: - 1, 3, 0, - 3, 2, 0, - - - // Left: - 0, 2, 4, - 2, 5, 4, - - - // Back: - 5, 6, 4, - 5, 7, 6, - - // Top: - 1, 4, 6, - 1, 0, 4, - - // Right: - 7, 1, 6, - 7, 3, 1, - - // Bottom: - 7, 2, 3, - 7, 5, 2 - }; - - return Primitive(cubeVertices, sizeof(cubeVertices) / sizeof(float) / 3, indices, sizeof(indices) / sizeof(uint32_t)); - } - }; + private: + static Primitives _shared; + public: + + /// @brief Gets the global Primitives instance. + /// @return A reference to the global Primitives object containing shared instances of Primitive objects. + static Primitives& Shared() { return _shared; }; + +#define LEPUS_UTILITY_PRIMITIVE_SHARED(PrimitiveName) \ +private:\ +Primitive m_ ## PrimitiveName ## Instance = Create ## PrimitiveName(); \ +public:\ +static const inline Primitive& PrimitiveName() {return Shared().m_ ## PrimitiveName ## Instance;} + + public: + static const inline Primitive CreateCube() + { + // Assuming negative left and negative up + const float cubeVertices[] = { + // Front: + -0.5f, 0.5f, -0.5f, // top-left (0) + 0.5f, 0.5f, -0.5f, // top-right (1) + -0.5f, -0.5f, -0.5f, // bottom-left (2) + 0.5f, -0.5f, -0.5f, // bottom-right (3) + + // Left: + -0.5f, 0.5f, 0.5f, // top-left (4) + // use front top-left (0) as top-right + -0.5f, -0.5f, 0.5f, // bottom-left (5) + // use front bottom-left (2) as bottom-right + + // Back: + 0.5f, 0.5f, 0.5f, // top-left (6) + // reuse left top-left (4) as top-right + 0.5f, -0.5f, 0.5f, // bottom-left (7) + // use left bottom-left (5) as bottom-right + + // Top: + // use left top-left (4) as top-left + // use back top-left (6) as top-right + // use left top-right (0) as bottom-left + // use front top-right (1) as bottom-right + + // Right: + // use front top-right (1) as top-left + // use back top-left (6) as top-right + // use front bottom-right (3) as bottom-left + // use back bottom-left (7) as bottom-right + + // Bottom: + // use front bottom-left (2) as top-left + // use front bottom-right (3) as top-right + // use back bottom-right (5) as bottom-left + // use back bottom-left (7) as bottom-right + }; + + const uint32_t indices[] = { + // Front: + 1, 3, 0, + 3, 2, 0, + + + // Left: + 0, 2, 4, + 2, 5, 4, + + + // Back: + 5, 6, 4, + 5, 7, 6, + + // Top: + 1, 4, 6, + 1, 0, 4, + + // Right: + 7, 1, 6, + 7, 3, 1, + + // Bottom: + 7, 2, 3, + 7, 5, 2 + }; + + return Primitive(cubeVertices, sizeof(cubeVertices) / sizeof(float) / 3, indices, sizeof(indices) / sizeof(uint32_t)); + } + + LEPUS_UTILITY_PRIMITIVE_SHARED(Cube); + }; + } } #endif \ No newline at end of file diff --git a/src/lepus/utility/Primitives/Shared.cpp b/src/lepus/utility/Primitives/Shared.cpp new file mode 100644 index 0000000..66d749f --- /dev/null +++ b/src/lepus/utility/Primitives/Shared.cpp @@ -0,0 +1,5 @@ +#include "../Primitives.h" + +using namespace lepus::utility; + +Primitives Primitives::_shared = Primitives(); \ No newline at end of file