diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..86f5a2c --- /dev/null +++ b/.clang-format @@ -0,0 +1,56 @@ +Language: Cpp +BasedOnStyle: LLVM + +AccessModifierOffset: -8 +AlignAfterOpenBracket: Align +AlignConsecutiveAssignments: Consecutive +AlignConsecutiveDeclarations: Consecutive +AlignConsecutiveMacros: Consecutive +AlignEscapedNewlines: Left +AlignOperands: DontAlign +AlignTrailingComments: Always +AllowShortBlocksOnASingleLine: Never +AllowShortEnumsOnASingleLine: false +AllowShortFunctionsOnASingleLine: None +AllowShortLambdasOnASingleLine: None +BraceWrapping: + AfterCaseLabel: false + AfterClass: false + AfterControlStatement: false + AfterEnum: false + AfterFunction: true + AfterNamespace: false + AfterObjCDeclaration: false + AfterStruct: false + AfterUnion: false + AfterExternBlock: false + BeforeCatch: false + BeforeElse: false + BeforeLambdaBody: false + BeforeWhile: false + IndentBraces: false +BreakBeforeBinaryOperators: NonAssignment +BreakBeforeBraces: Custom +BreakInheritanceList: AfterColon +BreakBeforeTernaryOperators: true +BreakConstructorInitializers: BeforeComma +BreakStringLiterals: false +ColumnLimit: 80 +Cpp11BracedListStyle: true +EmptyLineBeforeAccessModifier: Always +IndentAccessModifiers: false +IndentCaseBlocks: false +IndentCaseLabels: false +IndentGotoLabels: false +IndentPPDirectives: None +IndentWidth: 8 +InsertNewlineAtEOF: true +InsertTrailingCommas: None +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: None +PackConstructorInitializers: NextLineOnly +PointerAlignment: Left +SortIncludes: Never +SpaceBeforeCaseColon: false +TabWidth: 8 +UseTab: Always diff --git a/.clang-tidy b/.clang-tidy new file mode 100644 index 0000000..8b148ce --- /dev/null +++ b/.clang-tidy @@ -0,0 +1,64 @@ +--- +# Linux-style naming conventions for C++ projects +Checks: > + readability-identifier-naming, + readability-braces-around-statements, + readability-function-size, + modernize-use-nullptr, + modernize-use-override, + performance-*, + -modernize-use-trailing-return-type + +CheckOptions: + # Classes and structs use CamelCase (C++ convention) + - { key: readability-identifier-naming.ClassCase, value: CamelCase } + - { key: readability-identifier-naming.StructCase, value: CamelCase } + - { key: readability-identifier-naming.EnumCase, value: CamelCase } + - { key: readability-identifier-naming.UnionCase, value: CamelCase } + + # Functions and methods use snake_case (Linux style) + - { key: readability-identifier-naming.FunctionCase, value: lower_case } + - { key: readability-identifier-naming.MethodCase, value: lower_case } + + # Member variables: snake_case with trailing underscore + - { key: readability-identifier-naming.PrivateMemberCase, value: lower_case } + - { key: readability-identifier-naming.PrivateMemberSuffix, value: '_' } + - { key: readability-identifier-naming.ProtectedMemberCase, value: lower_case } + - { key: readability-identifier-naming.ProtectedMemberSuffix, value: '_' } + - { key: readability-identifier-naming.PublicMemberCase, value: lower_case } + + # Parameters and local variables: snake_case + - { key: readability-identifier-naming.ParameterCase, value: lower_case } + - { key: readability-identifier-naming.VariableCase, value: lower_case } + - { key: readability-identifier-naming.LocalVariableCase, value: lower_case } + + # Constants and macros: UPPER_CASE + - { key: readability-identifier-naming.ConstantCase, value: UPPER_CASE } + - { key: readability-identifier-naming.ConstexprVariableCase, value: UPPER_CASE } + - { key: readability-identifier-naming.MacroDefinitionCase, value: UPPER_CASE } + - { key: readability-identifier-naming.EnumConstantCase, value: UPPER_CASE } + + # Global variables: snake_case with g_ prefix (optional, can remove prefix) + - { key: readability-identifier-naming.GlobalVariableCase, value: lower_case } + - { key: readability-identifier-naming.GlobalVariablePrefix, value: 'g_' } + + # Namespaces: snake_case + - { key: readability-identifier-naming.NamespaceCase, value: lower_case } + + # Template parameters: CamelCase + - { key: readability-identifier-naming.TypeTemplateParameterCase, value: CamelCase } + - { key: readability-identifier-naming.ValueTemplateParameterCase, value: lower_case } + + # Typedef and type aliases: snake_case_t suffix (Linux style) + - { key: readability-identifier-naming.TypedefCase, value: lower_case } + - { key: readability-identifier-naming.TypedefSuffix, value: '_t' } + - { key: readability-identifier-naming.TypeAliasCase, value: lower_case } + - { key: readability-identifier-naming.TypeAliasSuffix, value: '_t' } + + # Function size limits + - { key: readability-function-size.LineThreshold, value: 100 } + - { key: readability-function-size.StatementThreshold, value: 50 } + +WarningsAsErrors: '' +HeaderFilterRegex: 'src/.*\.h$' +FormatStyle: file diff --git a/src/camera.cpp b/src/camera.cpp new file mode 100644 index 0000000..093b04e --- /dev/null +++ b/src/camera.cpp @@ -0,0 +1,41 @@ +#include "camera.h" +#include "state.h" +#include "../lib/glew/GL/glew.h" +#include "../lib/glm/gtc/matrix_transform.hpp" + +Camera::Camera() + : projection_(glm::mat4(1.0f)) + , viewport_(0, 0, 800, 600) + , clearColor_(0.0f, 0.0f, 0.0f) +{ +} + +void Camera::prepare() +{ + // Set projection matrix + State::projectionMatrix = projection_; + + // Calculate view matrix + // For a camera, we need the inverse transformation: + // View = inverse(Translation * Rotation) + // Which is: Rotation^-1 * Translation^-1 + glm::mat4 view = glm::mat4(1.0f); + + // Inverse rotation (rotate in opposite direction) + view = glm::rotate(view, -rotation_.z, glm::vec3(0.0f, 0.0f, 1.0f)); + view = glm::rotate(view, -rotation_.y, glm::vec3(0.0f, 1.0f, 0.0f)); + view = glm::rotate(view, -rotation_.x, glm::vec3(1.0f, 0.0f, 0.0f)); + + // Inverse translation (translate in opposite direction) + view = glm::translate(view, -position_); + + State::viewMatrix = view; + + // Set viewport + glViewport(viewport_.x, viewport_.y, viewport_.z, viewport_.w); + glScissor(viewport_.x, viewport_.y, viewport_.z, viewport_.w); + + // Set clear color and clear buffers + glClearColor(clearColor_.r, clearColor_.g, clearColor_.b, 1.0f); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); +} diff --git a/src/camera.h b/src/camera.h new file mode 100644 index 0000000..61f2fe5 --- /dev/null +++ b/src/camera.h @@ -0,0 +1,46 @@ +#ifndef CAMERA_H_ +#define CAMERA_H_ + +#include "entity.h" +#include "../lib/glm/glm.hpp" + +class Camera : public Entity { +public: + Camera(); + + const glm::mat4& getProjection() const + { + return projection_; + } + void setProjection(const glm::mat4& proj) + { + projection_ = proj; + } + + const glm::ivec4& getViewport() const + { + return viewport_; + } + void setViewport(const glm::ivec4& vp) + { + viewport_ = vp; + } + + const glm::vec3& getClearColor() const + { + return clearColor_; + } + void setClearColor(const glm::vec3& color) + { + clearColor_ = color; + } + + void prepare(); + +private: + glm::mat4 projection_; + glm::ivec4 viewport_; + glm::vec3 clearColor_; +}; + +#endif // CAMERA_H_ diff --git a/src/entity.cpp b/src/entity.cpp new file mode 100644 index 0000000..b489cee --- /dev/null +++ b/src/entity.cpp @@ -0,0 +1,13 @@ +#include "entity.h" + +Entity::Entity() + : position_(0.0f, 0.0f, 0.0f) + , rotation_(0.0f, 0.0f, 0.0f) + , scale_(1.0f, 1.0f, 1.0f) +{ +} + +void Entity::move(const glm::vec3& vec) +{ + position_ += vec; +} diff --git a/src/entity.h b/src/entity.h new file mode 100644 index 0000000..59063d5 --- /dev/null +++ b/src/entity.h @@ -0,0 +1,55 @@ +#ifndef ENTITY_H_ +#define ENTITY_H_ + +#include "../lib/glm/glm.hpp" + +class Entity { +public: + Entity(); + virtual ~Entity() + { + } + + const glm::vec3& getPosition() const + { + return position_; + } + void setPosition(const glm::vec3& pos) + { + position_ = pos; + } + + const glm::vec3& getRotation() const + { + return rotation_; + } + void setRotation(const glm::vec3& rot) + { + rotation_ = rot; + } + + const glm::vec3& getScale() const + { + return scale_; + } + void setScale(const glm::vec3& scale) + { + scale_ = scale; + } + + void move(const glm::vec3& vec); + + virtual void update(float deltaTime) + { + } + virtual void draw() + { + } + +protected: + glm::vec3 position_; + glm::vec3 rotation_; + glm::vec3 scale_; +}; + +#endif // ENTITY_H_ diff --git a/src/main.cpp b/src/main.cpp index 3fe5037..80f4cf8 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -2,9 +2,9 @@ #pragma comment(linker, "/SUBSYSTEM:windows /ENTRY:mainCRTStartup") #endif*/ - #include #include +#include #include "../lib/glew/GL/glew.h" #include "../lib/glfw/glfw3.h" @@ -14,11 +14,18 @@ #include "vertex.h" #include "shader.h" #include "buffer.h" +#include "state.h" +#include "mesh.h" +#include "entity.h" +#include "model.h" +#include "camera.h" +#include "world.h" -#define SCREEN_WIDTH 800 +#define SCREEN_WIDTH 800 #define SCREEN_HEIGHT 600 -int main() { +int main() +{ // Initialize OpenGL if (!glfwInit()) { std::cerr << "Failed to initialize glfw\n"; @@ -27,12 +34,13 @@ int main() { glfwWindowHint(GLFW_RESIZABLE, false); glfwWindowHint(GLFW_SAMPLES, 8); - - //glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); - //glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2); - //glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); - GLFWwindow* win = glfwCreateWindow(SCREEN_WIDTH, SCREEN_HEIGHT, "Daniel Poveda", nullptr, nullptr); + // glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); + // glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2); + // glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); + + GLFWwindow* win = glfwCreateWindow(SCREEN_WIDTH, SCREEN_HEIGHT, + "Daniel Poveda", nullptr, nullptr); if (win == nullptr) { std::cerr << "Failed to create opengl window\n"; glfwTerminate(); @@ -43,68 +51,87 @@ int main() { glEnable(GL_DEPTH_TEST); glEnable(GL_SCISSOR_TEST); - std::cout << "OpenGL initialized, version: " << glGetString(GL_VERSION) << "\n"; + std::cout << "OpenGL initialized, version: " << glGetString(GL_VERSION) + << "\n"; // Initialize GLEW glewExperimental = GL_TRUE; - GLenum err = glewInit(); + GLenum err = glewInit(); if (err != GLEW_OK) { - std::cerr << "Failed to initialize GLEW: " << glewGetErrorString(err) << "\n"; + std::cerr << "Failed to initialize GLEW: " + << glewGetErrorString(err) << "\n"; glfwTerminate(); return 1; } - // Logic - std::vector vertices = { - {{0.0f, 0.5f, 0.0f}, {0.0f, 1.0f, 0.0f}}, - {{-0.5f, -0.5f, 0.0f}, {0.0f, 0.0f, 1.0f}}, - {{0.5f, -0.5f, 0.0f}, {1.0f, 0.0f, 0.0f}} - }; - std::vector indices = { 0, 1, 2 }; - - Buffer buffer(vertices, indices); - Shader shader("data/vertex.glsl", "data/fragment.glsl"); - if (std::strlen(shader.getError()) > 0) { - std::cerr << "Failed to initialize shaders: " << shader.getError() << "\n"; + // Initialize default shader + State::defaultShader = + std::make_shared("data/vertex.glsl", "data/fragment.glsl"); + if (std::strlen(State::defaultShader->getError()) > 0) { + std::cerr << "Failed to initialize shaders: " + << State::defaultShader->getError() << "\n"; } - glm::mat4 projection = glm::perspective(glm::radians(45.0f), static_cast(SCREEN_WIDTH) / SCREEN_HEIGHT, 0.1f, 100.0f); - //glm::mat4 view = glm::translate(glm::mat4(1.0f), glm::vec3(0, 0, 6)); - glm::mat4 view = glm::lookAt(glm::vec3(0, 0, 6), glm::vec3(0, 0, 0), glm::vec3(0, 1, 0)); + // Create world + World world; - // main loop - double angle = 0.0; - double lastTime = glfwGetTime(); - while (!glfwWindowShouldClose(win) && !glfwGetKey(win, GLFW_KEY_ESCAPE)) { - // Delta - double delta_time = glfwGetTime() - lastTime; - lastTime = glfwGetTime(); + // Create camera + auto camera = std::make_shared(); + camera->setPosition(glm::vec3(0.0f, 0.0f, 6.0f)); + camera->setProjection(glm::perspective( + glm::radians(45.0f), + static_cast(SCREEN_WIDTH) / SCREEN_HEIGHT, 0.1f, 100.0f)); + camera->setViewport(glm::ivec4(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT)); + camera->setClearColor(glm::vec3(0.1f, 0.1f, 0.1f)); + world.addEntity(camera); - // Reset window - int screenWidth, screenHeight; - glfwGetWindowSize(win, &screenWidth, &screenHeight); - glViewport(0, 0, screenWidth, screenHeight); + // Create triangle mesh + std::vector vertices = { + {{0.0f, 0.5f, 0.0f}, {0.0f, 1.0f, 0.0f}}, + {{-0.5f, -0.5f, 0.0f}, {0.0f, 0.0f, 1.0f}}, + {{0.5f, -0.5f, 0.0f}, {1.0f, 0.0f, 0.0f}}}; + std::vector indices = {0, 1, 2}; - glClearColor(0.1f, 0.1f, 0.1f, 1.0f); - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + auto buffer = std::make_shared(vertices, indices); + auto mesh = std::make_shared(); + mesh->addBuffer(buffer); - shader.Use(); - - // Logic - angle += 32.0 * delta_time; - - for (int row = 0; row < 3; ++row) { - for (int col = 0; col < 3; ++col) { - glm::mat4 model = glm::translate(glm::mat4(1.0), glm::vec3(-3.0f + static_cast(col) * 3.0f, 0.0f, static_cast(-row) * 3.0f)); - model = glm::rotate(model, glm::radians(static_cast(angle)), glm::vec3(0, 1, 0)); - - glm::mat4 mvp = projection * view * model; - - Shader::setMat4(shader.getLocation("mvp"), mvp); - - buffer.Draw(shader); - } + // Create 9 models in a 3x3 grid + std::vector> models; + for (int row = 0; row < 3; ++row) { + for (int col = 0; col < 3; ++col) { + auto model = std::make_shared(mesh); + model->setPosition( + glm::vec3(-3.0f + static_cast(col) * 3.0f, + 0.0f, static_cast(-row) * 3.0f)); + models.push_back(model); + world.addEntity(model); } + } + + // Main loop + double angle = 0.0; + double lastTime = glfwGetTime(); + while (!glfwWindowShouldClose(win) + && !glfwGetKey(win, GLFW_KEY_ESCAPE)) { + // Delta time + double currentTime = glfwGetTime(); + double deltaTime = currentTime - lastTime; + lastTime = currentTime; + + // Update angle + angle += 32.0 * deltaTime; + + // Update rotation for all models + for (auto& model : models) { + model->setRotation(glm::vec3( + 0.0f, glm::radians(static_cast(angle)), + 0.0f)); + } + + // Update and draw world + world.update(static_cast(deltaTime)); + world.draw(); // Refresh screen glfwSwapBuffers(win); @@ -115,4 +142,4 @@ int main() { glfwTerminate(); return 0; -} \ No newline at end of file +} diff --git a/src/mesh.cpp b/src/mesh.cpp new file mode 100644 index 0000000..d0c0e7e --- /dev/null +++ b/src/mesh.cpp @@ -0,0 +1,33 @@ +#include "mesh.h" +#include "buffer.h" +#include "shader.h" +#include "state.h" + +void Mesh::addBuffer(const std::shared_ptr& buffer, + const std::shared_ptr& shader) +{ + buffers_.push_back(buffer); + shaders_.push_back(shader); +} + +void Mesh::draw() +{ + // Calculate MVP matrix + glm::mat4 mvp = + State::projectionMatrix * State::viewMatrix * State::modelMatrix; + + // Draw each buffer with its shader + for (size_t i = 0; i < buffers_.size(); ++i) { + // Use buffer's shader if available, otherwise use default + // shader + std::shared_ptr shader = + shaders_[i] ? shaders_[i] : State::defaultShader; + + if (shader) { + shader->Use(); + int mvpLoc = shader->getLocation("mvp"); + Shader::setMat4(mvpLoc, mvp); + buffers_[i]->Draw(*shader); + } + } +} diff --git a/src/mesh.h b/src/mesh.h new file mode 100644 index 0000000..0efd1ec --- /dev/null +++ b/src/mesh.h @@ -0,0 +1,37 @@ +#ifndef MESH_H_ +#define MESH_H_ + +#include +#include + +class Buffer; +class Shader; + +class Mesh { +public: + Mesh() = default; + + void addBuffer(const std::shared_ptr& buffer, + const std::shared_ptr& shader = nullptr); + + size_t getNumBuffers() const + { + return buffers_.size(); + } + const std::shared_ptr& getBuffer(size_t index) const + { + return buffers_[index]; + } + std::shared_ptr& getBuffer(size_t index) + { + return buffers_[index]; + } + + void draw(); + +private: + std::vector> buffers_; + std::vector> shaders_; +}; + +#endif // MESH_H_ diff --git a/src/model.cpp b/src/model.cpp new file mode 100644 index 0000000..e21d907 --- /dev/null +++ b/src/model.cpp @@ -0,0 +1,35 @@ +#include "model.h" +#include "mesh.h" +#include "state.h" +#include "../lib/glm/gtc/matrix_transform.hpp" + +Model::Model(const std::shared_ptr& mesh) + : mesh_(mesh) +{ +} + +void Model::draw() +{ + if (!mesh_) + return; + + // Build model matrix: Translation * Rotation * Scale + glm::mat4 model = glm::mat4(1.0f); + + // Translation + model = glm::translate(model, position_); + + // Rotation (applying X, Y, Z rotations) + model = glm::rotate(model, rotation_.x, glm::vec3(1.0f, 0.0f, 0.0f)); + model = glm::rotate(model, rotation_.y, glm::vec3(0.0f, 1.0f, 0.0f)); + model = glm::rotate(model, rotation_.z, glm::vec3(0.0f, 0.0f, 1.0f)); + + // Scale + model = glm::scale(model, scale_); + + // Set the model matrix in State + State::modelMatrix = model; + + // Draw the mesh + mesh_->draw(); +} diff --git a/src/model.h b/src/model.h new file mode 100644 index 0000000..c748095 --- /dev/null +++ b/src/model.h @@ -0,0 +1,19 @@ +#ifndef MODEL_H_ +#define MODEL_H_ + +#include "entity.h" +#include + +class Mesh; + +class Model : public Entity { +public: + Model(const std::shared_ptr& mesh); + + virtual void draw() override; + +private: + std::shared_ptr mesh_; +}; + +#endif // MODEL_H_ diff --git a/src/state.cpp b/src/state.cpp new file mode 100644 index 0000000..e38facc --- /dev/null +++ b/src/state.cpp @@ -0,0 +1,6 @@ +#include "state.h" + +std::shared_ptr State::defaultShader = nullptr; +glm::mat4 State::projectionMatrix = glm::mat4(1.0f); +glm::mat4 State::viewMatrix = glm::mat4(1.0f); +glm::mat4 State::modelMatrix = glm::mat4(1.0f); diff --git a/src/state.h b/src/state.h new file mode 100644 index 0000000..a6811a2 --- /dev/null +++ b/src/state.h @@ -0,0 +1,17 @@ +#ifndef STATE_H_ +#define STATE_H_ + +#include +#include "../lib/glm/glm.hpp" + +class Shader; + +class State { +public: + static std::shared_ptr defaultShader; + static glm::mat4 projectionMatrix; + static glm::mat4 viewMatrix; + static glm::mat4 modelMatrix; +}; + +#endif // STATE_H_ diff --git a/src/world.cpp b/src/world.cpp new file mode 100644 index 0000000..11ba6ab --- /dev/null +++ b/src/world.cpp @@ -0,0 +1,58 @@ +#include "world.h" +#include "entity.h" +#include "camera.h" +#include + +void World::addEntity(const std::shared_ptr& entity) +{ + entities_.push_back(entity); + + // Check if entity is a camera + std::shared_ptr camera = + std::dynamic_pointer_cast(entity); + if (camera) { + cameras_.push_back(camera); + } +} + +void World::removeEntity(const std::shared_ptr& entity) +{ + // Remove from entities list + auto it = std::find(entities_.begin(), entities_.end(), entity); + if (it != entities_.end()) { + entities_.erase(it); + } + + // Check if entity is a camera and remove from cameras list + std::shared_ptr camera = + std::dynamic_pointer_cast(entity); + if (camera) { + auto camIt = + std::find(cameras_.begin(), cameras_.end(), camera); + if (camIt != cameras_.end()) { + cameras_.erase(camIt); + } + } +} + +void World::update(float deltaTime) +{ + for (auto& entity : entities_) { + entity->update(deltaTime); + } +} + +void World::draw() +{ + // Draw for each camera + for (auto& camera : cameras_) { + // Prepare the camera (sets viewport, projection, view, clears + // screen) + camera->prepare(); + + // Draw all entities + for (auto& entity : entities_) { + entity->draw(); + } + } +} diff --git a/src/world.h b/src/world.h new file mode 100644 index 0000000..85e82ba --- /dev/null +++ b/src/world.h @@ -0,0 +1,38 @@ +#ifndef WORLD_H_ +#define WORLD_H_ + +#include +#include + +class Entity; +class Camera; + +class World { +public: + World() = default; + + void addEntity(const std::shared_ptr& entity); + void removeEntity(const std::shared_ptr& entity); + + size_t getNumEntities() const + { + return entities_.size(); + } + const std::shared_ptr& getEntity(size_t index) const + { + return entities_[index]; + } + std::shared_ptr& getEntity(size_t index) + { + return entities_[index]; + } + + void update(float deltaTime); + void draw(); + +private: + std::vector> entities_; + std::vector> cameras_; +}; + +#endif // WORLD_H_ diff --git a/ugine3d.vcxproj b/ugine3d.vcxproj index 151122b..c2a6669 100644 --- a/ugine3d.vcxproj +++ b/ugine3d.vcxproj @@ -160,15 +160,27 @@ + + + + + + + + + + + + diff --git a/ugine3d.vcxproj.filters b/ugine3d.vcxproj.filters index d2a7047..cf0192f 100644 --- a/ugine3d.vcxproj.filters +++ b/ugine3d.vcxproj.filters @@ -42,6 +42,24 @@ Source Files + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + @@ -59,6 +77,24 @@ Source Files + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files +