diff --git a/CMakeLists.txt b/CMakeLists.txt index 6f74609..e1e4cb9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -186,6 +186,7 @@ set(SOURCE_FILES ${FUNGT_BASE_DIR}/Geometries/square.cpp ${FUNGT_BASE_DIR}/Geometries/sphere.cpp ${FUNGT_BASE_DIR}/Geometries/box.cpp + ${FUNGT_BASE_DIR}/Geometries/torus.cpp ${FUNGT_BASE_DIR}/Model/model.cpp ${FUNGT_BASE_DIR}/Helpers/helpers.cpp ${FUNGT_BASE_DIR}/Animation/animation.cpp diff --git a/Geometries/primitives.cpp b/Geometries/primitives.cpp index 6b49325..d559525 100644 --- a/Geometries/primitives.cpp +++ b/Geometries/primitives.cpp @@ -119,4 +119,215 @@ void Primitive::InitGraphics() if (this->getNumOfIndices() > 0) { m_vi.unbind(); } + buildTopology(); } +void Primitive::clearSelection() +{ + // Clear all vertex selection flags + std::fill(m_vertexSelected.begin(), m_vertexSelected.end(), false); + + // Clear all face selection flags + for (auto& face : m_faces) { + face.selected = false; + } +} + +void Primitive::selectVertex(uint32_t idx, bool additive) +{ + if (idx >= m_vertex.size()) { + std::cerr << "selectVertex: index " << idx << " out of range" << std::endl; + return; + } + + if (!additive) { + clearSelection(); + } + + m_vertexSelected[idx] = true; +} + +void Primitive::selectFace(uint32_t idx, bool additive) +{ + if (idx >= m_faces.size()) { + std::cerr << "selectFace: index " << idx << " out of range" << std::endl; + return; + } + + if (!additive) { + clearSelection(); + } + + m_faces[idx].selected = true; +} +void Primitive::updateGPUBuffers() +{ + if (m_vertex.empty()) { + return; + } + + // Bind VBO and re-upload vertex data + m_vb.bind(); + m_vb.bufferData(this->getVertices(), this->sizeOfVertices()); + m_vb.unbind(); + + // Bind EBO and re-upload index data (if we have indices) + if (!m_index.empty()) { + m_vi.bind(); + m_vi.indexData(this->getIndices(), this->sizeOfIndices()); + m_vi.unbind(); + } +} +void Primitive::recalculateNormals() +{ + if (m_vertex.empty() || m_index.empty()) { + return; + } + + // Zero out all vertex normals + for (auto& v : m_vertex) { + v.normal = glm::vec3(0.0f); + } + + // Accumulate face normals into vertices (area-weighted) + for (size_t i = 0; i < m_index.size(); i += 3) { + uint32_t i0 = m_index[i]; + uint32_t i1 = m_index[i + 1]; + uint32_t i2 = m_index[i + 2]; + + glm::vec3 v0 = m_vertex[i0].position; + glm::vec3 v1 = m_vertex[i1].position; + glm::vec3 v2 = m_vertex[i2].position; + + // Face normal (cross product gives area-weighted normal) + glm::vec3 edge1 = v1 - v0; + glm::vec3 edge2 = v2 - v0; + glm::vec3 faceNormal = glm::cross(edge1, edge2); + + // Accumulate into vertex normals (don't normalize yet) + m_vertex[i0].normal += faceNormal; + m_vertex[i1].normal += faceNormal; + m_vertex[i2].normal += faceNormal; + } + + // Normalize all vertex normals + for (auto& v : m_vertex) { + if (glm::length(v.normal) > 0.0001f) { + v.normal = glm::normalize(v.normal); + } + } +} +void Primitive::buildTopology() +{ + // Clear existing topology + m_halfEdges.clear(); + m_faces.clear(); + m_vertexSelected.clear(); + + if (m_index.empty() || m_vertex.empty()) { + m_topologyDirty = false; + return; + } + + // Initialize vertex selection flags + m_vertexSelected.resize(m_vertex.size(), false); + + // Count triangles + size_t numTriangles = m_index.size() / 3; + m_faces.resize(numTriangles); + + // Allocate space for half-edges (3 per triangle) + m_halfEdges.resize(numTriangles * 3); + + // Build half-edges from triangles + for (size_t faceIdx = 0; faceIdx < numTriangles; ++faceIdx) { + uint32_t i0 = m_index[faceIdx * 3 + 0]; + uint32_t i1 = m_index[faceIdx * 3 + 1]; + uint32_t i2 = m_index[faceIdx * 3 + 2]; + + // Three half-edges for this triangle + uint32_t he0 = faceIdx * 3 + 0; + uint32_t he1 = faceIdx * 3 + 1; + uint32_t he2 = faceIdx * 3 + 2; + + // Half-edge 0: i0 -> i1 + m_halfEdges[he0].vertex = i1; + m_halfEdges[he0].next = he1; + m_halfEdges[he0].face = faceIdx; + m_halfEdges[he0].twin = UINT32_MAX; // Will find twins next + + // Half-edge 1: i1 -> i2 + m_halfEdges[he1].vertex = i2; + m_halfEdges[he1].next = he2; + m_halfEdges[he1].face = faceIdx; + m_halfEdges[he1].twin = UINT32_MAX; + + // Half-edge 2: i2 -> i0 + m_halfEdges[he2].vertex = i0; + m_halfEdges[he2].next = he0; + m_halfEdges[he2].face = faceIdx; + m_halfEdges[he2].twin = UINT32_MAX; + + // Store one half-edge reference in the face + m_faces[faceIdx].halfEdge = he0; + m_faces[faceIdx].selected = false; + } + + // Find twin half-edges + // For each half-edge, search for its opposite + for (size_t i = 0; i < m_halfEdges.size(); ++i) { + if (m_halfEdges[i].twin != UINT32_MAX) { + continue; // Already has a twin + } + + // This half-edge goes: vertexStart -> vertexEnd + // Find the triangle index and position within triangle for edge i + uint32_t faceIdx = i / 3; + uint32_t edgeInFace = i % 3; + + // Get the starting vertex of this half-edge + uint32_t vertexStart; + if (edgeInFace == 0) { + vertexStart = m_index[faceIdx * 3 + 0]; + } + else if (edgeInFace == 1) { + vertexStart = m_index[faceIdx * 3 + 1]; + } + else { + vertexStart = m_index[faceIdx * 3 + 2]; + } + + uint32_t vertexEnd = m_halfEdges[i].vertex; + + // Search for twin: an edge going vertexEnd -> vertexStart + for (size_t j = i + 1; j < m_halfEdges.size(); ++j) { + uint32_t otherFaceIdx = j / 3; + uint32_t otherEdgeInFace = j % 3; + + uint32_t otherVertexStart; + if (otherEdgeInFace == 0) { + otherVertexStart = m_index[otherFaceIdx * 3 + 0]; + } + else if (otherEdgeInFace == 1) { + otherVertexStart = m_index[otherFaceIdx * 3 + 1]; + } + else { + otherVertexStart = m_index[otherFaceIdx * 3 + 2]; + } + + uint32_t otherVertexEnd = m_halfEdges[j].vertex; + + // Check if this is the twin + if (vertexStart == otherVertexEnd && vertexEnd == otherVertexStart) { + m_halfEdges[i].twin = j; + m_halfEdges[j].twin = i; + break; + } + } + } + + m_topologyDirty = false; + + std::cout << "Topology built: " << m_vertex.size() << " vertices, " + << m_faces.size() << " faces, " + << m_halfEdges.size() << " half-edges" << std::endl; +} \ No newline at end of file diff --git a/Geometries/primitives.hpp b/Geometries/primitives.hpp index 4d2afdf..e37c2f3 100644 --- a/Geometries/primitives.hpp +++ b/Geometries/primitives.hpp @@ -23,6 +23,25 @@ class Primitive { std::vector m_vertex; std::vector m_index; + //Topology data for editing + struct HalfEdge { + uint32_t vertex; // Which vertex this edge points TO + uint32_t twin; // Opposite half-edge (UINT32_MAX if boundary) + uint32_t next; // Next half-edge in the same face + uint32_t face; // Which face this edge belongs to + }; + + struct EditFace { + uint32_t halfEdge; // One (any) half-edge of this face + bool selected; // Is this face selected? + }; + + std::vector m_halfEdges; + std::vector m_faces; + std::vector m_vertexSelected; // Per-vertex selection flags + + bool m_topologyDirty; // Does topology need rebuilding? + public: VertexArrayObject m_vao; VertexBuffer m_vb; @@ -49,7 +68,24 @@ class Primitive { const std::vector& getIndices() const; // Geometry-specific virtuals virtual void setData() = 0; - + // NEW: Topology management + void buildTopology(); + bool isTopologyBuilt() const { return !m_topologyDirty; } + + // Selection + void clearSelection(); + void selectVertex(uint32_t idx, bool additive = false); + void selectFace(uint32_t idx, bool additive = false); + const std::vector& getVertexSelection() const { return m_vertexSelected; } + const std::vector& getFaces() const { return m_faces; } + //Drawing wireframe and vertices for edit mode: all primitives will use the same method, so we can implement it here in the base class + void drawWireframe(); + void drawVertices(); + // GPU sync + void updateGPUBuffers(); + + // Geometry operations + void recalculateNormals(); // Graphics initialization void setTexture(const std::string &pathToTexture); void InitGraphics(); diff --git a/Geometries/pyramid.cpp b/Geometries/pyramid.cpp index 16a2b77..f1d1063 100644 --- a/Geometries/pyramid.cpp +++ b/Geometries/pyramid.cpp @@ -48,3 +48,43 @@ void Pyramid::setData() this->set(vertices,nOfvertices); } +void Primitive::drawWireframe() +{ + if (m_vertex.empty()) return; + + // Save current polygon mode + GLint polygonMode[2]; + glGetIntegerv(GL_POLYGON_MODE, polygonMode); + + // Draw as wireframe + glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); + glLineWidth(2.0f); + + m_vao.bind(); + + if (!m_index.empty()) { + glDrawElements(GL_TRIANGLES, m_index.size(), GL_UNSIGNED_INT, 0); + } + else { + glDrawArrays(GL_TRIANGLES, 0, m_vertex.size()); + } + + m_vao.unbind(); + + // Restore polygon mode + glPolygonMode(GL_FRONT_AND_BACK, polygonMode[0]); + glLineWidth(1.0f); +} + +void Primitive::drawVertices() +{ + if (m_vertex.empty()) return; + + glPointSize(8.0f); + + m_vao.bind(); + glDrawArrays(GL_POINTS, 0, m_vertex.size()); + m_vao.unbind(); + + glPointSize(1.0f); +} \ No newline at end of file diff --git a/Geometries/torus.cpp b/Geometries/torus.cpp new file mode 100644 index 0000000..383680e --- /dev/null +++ b/Geometries/torus.cpp @@ -0,0 +1,125 @@ +#include "torus.hpp" +#include + +Torus::Torus(float majorRadius, float minorRadius, int majorSegments, int minorSegments) + : Primitive(), + m_majorRadius(majorRadius), + m_minorRadius(minorRadius), + m_majorSegments(majorSegments), + m_minorSegments(minorSegments) +{ + std::cout << "Torus constructor" << std::endl; +} + +Torus::~Torus() +{ + std::cout << "Torus destructor" << std::endl; +} + +void Torus::setData() +{ + std::vector vertices; + std::vector indices; + + const float PI = 3.14159265359f; + + // Generate vertices + for (int i = 0; i <= m_majorSegments; ++i) { + float u = (float)i / (float)m_majorSegments * 2.0f * PI; + float cosU = cos(u); + float sinU = sin(u); + + for (int j = 0; j <= m_minorSegments; ++j) { + float v = (float)j / (float)m_minorSegments * 2.0f * PI; + float cosV = cos(v); + float sinV = sin(v); + + // Position + glm::vec3 pos; + pos.x = (m_majorRadius + m_minorRadius * cosV) * cosU; + pos.y = (m_majorRadius + m_minorRadius * cosV) * sinU; + pos.z = m_minorRadius * sinV; + + // Normal (pointing outward from tube surface) + glm::vec3 normal; + normal.x = cosV * cosU; + normal.y = cosV * sinU; + normal.z = sinV; + normal = glm::normalize(normal); + + // Texture coordinates + glm::vec2 texcoord; + texcoord.x = (float)i / (float)m_majorSegments; + texcoord.y = (float)j / (float)m_minorSegments; + + PrimitiveVertex vertex; + vertex.position = pos; + vertex.normal = normal; + vertex.texcoord = texcoord; + + vertices.push_back(vertex); + } + } + + // Generate indices (quads made of two triangles) + for (int i = 0; i < m_majorSegments; ++i) { + for (int j = 0; j < m_minorSegments; ++j) { + int a = i * (m_minorSegments + 1) + j; + int b = a + m_minorSegments + 1; + int c = a + 1; + int d = b + 1; + + // First triangle + indices.push_back(a); + indices.push_back(b); + indices.push_back(c); + + // Second triangle + indices.push_back(c); + indices.push_back(b); + indices.push_back(d); + } + } + + // Store in base class vectors + PrimitiveVertex* vertArray = vertices.data(); + GLuint* indArray = indices.data(); + this->set(vertArray, vertices.size(), indArray, indices.size()); + + std::cout << "Torus generated: " << vertices.size() << " vertices, " + << indices.size() / 3 << " triangles" << std::endl; +} + +void Torus::draw() +{ + texture.active(); + texture.bind(); + m_vao.bind(); + glDrawElements(GL_TRIANGLES, this->getNumOfIndices(), GL_UNSIGNED_INT, 0); + m_vao.unbind(); +} + +void Torus::InstancedDraw(Shader& shader, int instanceCount) +{ + texture.active(); + texture.bind(); + m_vao.bind(); + glDrawElementsInstanced(GL_TRIANGLES, this->getNumOfIndices(), GL_UNSIGNED_INT, 0, instanceCount); + m_vao.unbind(); +} + +void Torus::setRadii(float major, float minor) +{ + m_majorRadius = major; + m_minorRadius = minor; + setData(); // Regenerate geometry + InitGraphics(); // Re-upload to GPU +} + +void Torus::setSegments(int major, int minor) +{ + m_majorSegments = major; + m_minorSegments = minor; + setData(); // Regenerate geometry + InitGraphics(); // Re-upload to GPU +} \ No newline at end of file diff --git a/Geometries/torus.hpp b/Geometries/torus.hpp new file mode 100644 index 0000000..524e8c8 --- /dev/null +++ b/Geometries/torus.hpp @@ -0,0 +1,29 @@ +#if !defined(_TORUS_HPP_) +#define _TORUS_HPP_ + +#include "primitives.hpp" + +class Torus : public Primitive { +private: + float m_majorRadius; // Distance from center to tube center + float m_minorRadius; // Tube thickness + int m_majorSegments; // Divisions around major circle + int m_minorSegments; // Divisions around tube + +public: + Torus(float majorRadius = 1.0f, + float minorRadius = 0.3f, + int majorSegments = 48, + int minorSegments = 24); + ~Torus(); + + void draw() override; + void setData() override; + void InstancedDraw(Shader& shader, int instanceCount) override; + + // Parameter access (if you want to adjust after creation) + void setRadii(float major, float minor); + void setSegments(int major, int minor); +}; + +#endif // _TORUS_HPP_ \ No newline at end of file diff --git a/PBR/Space/space.cpp b/PBR/Space/space.cpp index 67b8f8a..25a91a7 100644 --- a/PBR/Space/space.cpp +++ b/PBR/Space/space.cpp @@ -345,11 +345,11 @@ void Space::LoadGeometryToRender(const SimpleGeometry& geometry) { tri.uvs[2][0] = v2.texcoord.x; tri.uvs[2][1] = v2.texcoord.y; - tri.material.baseColor[0] = material.baseColor.x; - tri.material.baseColor[1] = material.baseColor.y; - tri.material.baseColor[2] = material.baseColor.z; - tri.material.roughness = material.roughness; - tri.material.metallic = material.metallic; + tri.material.baseColor[0] = material.m_diffLigth.x; + tri.material.baseColor[1] = material.m_diffLigth.y; + tri.material.baseColor[2] = material.m_diffLigth.z; + tri.material.roughness = 0.7; + tri.material.metallic = 0.05; tri.material.baseColorTexIdx = baseColorTexId; m_triangles.push_back(tri); diff --git a/Renderable/renderable.hpp b/Renderable/renderable.hpp index 0905303..c211732 100644 --- a/Renderable/renderable.hpp +++ b/Renderable/renderable.hpp @@ -7,6 +7,10 @@ class Renderable{ //Abstract class + protected: + bool m_isEditMode = false; // NEW: Edit mode flag + bool m_isEditable = false; // Can this renderable be edited? + public: @@ -42,6 +46,11 @@ class Renderable{ //Abstract class virtual std::weak_ptr getRigidBody() const { return std::weak_ptr(); } + + virtual void setEditMode(bool enabled) { m_isEditMode = enabled; } + virtual bool isEditMode() const { return m_isEditMode; } + virtual bool isEditable() const { return m_isEditable; } + virtual ~Renderable() = default; }; diff --git a/Samples/creating_model/CMakeLists.txt b/Samples/creating_model/CMakeLists.txt new file mode 100644 index 0000000..e1e4cb9 --- /dev/null +++ b/Samples/creating_model/CMakeLists.txt @@ -0,0 +1,400 @@ +cmake_minimum_required(VERSION 3.15) +project(FunGT) + +# ════════════════════════════════════════════════════════════════════════════ +# GPU BACKEND OPTIONS +# ════════════════════════════════════════════════════════════════════════════ +option(FUNGT_USE_CUDA "Enable CUDA backend for NVIDIA GPUs" ON) +option(FUNGT_USE_SYCL "Enable SYCL backend for Intel GPUs" ON) + +# Allow FUNGT_BASE_DIR to be set externally (command line, environment, or cache) +# Priority: 1. Cache variable, 2. Environment variable, 3. Default fallback +if(NOT DEFINED FUNGT_BASE_DIR) + # Check if it's set as an environment variable + if(DEFINED ENV{FUNGT_BASE_DIR}) + set(FUNGT_BASE_DIR $ENV{FUNGT_BASE_DIR} CACHE PATH "FunGT base directory") + message(STATUS "Using FUNGT_BASE_DIR from environment: ${FUNGT_BASE_DIR}") + else() + # Use a default fallback (you must specify this via -DFUNGT_BASE_DIR or environment) + message(FATAL_ERROR "FUNGT_BASE_DIR not specified. Please set it via:\n" + " cmake -DFUNGT_BASE_DIR=/path/to/FunGT ..\n" + " or export FUNGT_BASE_DIR=/path/to/FunGT") + endif() +else() + message(STATUS "Using pre-defined FUNGT_BASE_DIR: ${FUNGT_BASE_DIR}") +endif() + +# Validate that the base directory exists and contains expected subdirectories +if(NOT EXISTS "${FUNGT_BASE_DIR}") + message(FATAL_ERROR "FUNGT_BASE_DIR does not exist: ${FUNGT_BASE_DIR}") +endif() + +if(NOT EXISTS "${FUNGT_BASE_DIR}/VertexGL" OR NOT EXISTS "${FUNGT_BASE_DIR}/Shaders") + message(FATAL_ERROR "FUNGT_BASE_DIR does not appear to be a valid FunGT directory: ${FUNGT_BASE_DIR}") +endif() + +# Set the C++ standard +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +# ════════════════════════════════════════════════════════════════════════════ +# PBR RENDERER LIBRARIES - PREBUILT FROM PBR/main/build +# ════════════════════════════════════════════════════════════════════════════ +set(PBR_LIB_DIR ${FUNGT_BASE_DIR}/PBR/main/build) + +if(UNIX) + # Set the build type to Release + set(CMAKE_BUILD_TYPE Release) + set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/release/linux) + + # ALWAYS use SYCL compiler (required for funlib) + set(CMAKE_CXX_COMPILER /home/juanchuletas/Documents/Development/sycl_workspace/llvm/build/bin/clang++) + + # Add the prebuilt ImGui library + add_library(imgui STATIC IMPORTED) + set_target_properties(imgui PROPERTIES + IMPORTED_LOCATION "${FUNGT_BASE_DIR}/vendor/imgui/lib/libimgui.a" + INTERFACE_INCLUDE_DIRECTORIES "${FUNGT_BASE_DIR}/vendor/imgui" + ) + + # Add funlib + set(FUNLIB_DIR ${FUNGT_BASE_DIR}/vendor/funlib) + add_library(funlib STATIC IMPORTED GLOBAL) + set_target_properties(funlib PROPERTIES + IMPORTED_LOCATION ${FUNLIB_DIR}/lib/libfunlib.a + INTERFACE_INCLUDE_DIRECTORIES ${FUNLIB_DIR}/include + ) + # PBR Core (always required) + add_library(pbr_core STATIC IMPORTED) + set_target_properties(pbr_core PROPERTIES + IMPORTED_LOCATION "${PBR_LIB_DIR}/libpbr_core.a" + ) + + # PBR CUDA renderer (if enabled) + if(FUNGT_USE_CUDA) + add_library(pbr_cuda STATIC IMPORTED) + set_target_properties(pbr_cuda PROPERTIES + IMPORTED_LOCATION "${PBR_LIB_DIR}/libpbr_cuda.a" + ) + set(PBR_CUDA_LIB pbr_cuda) + message(STATUS "PBR CUDA renderer library: ${PBR_LIB_DIR}/libpbr_cuda.a") + endif() + + # PBR SYCL renderer (if enabled) + if(FUNGT_USE_SYCL) + add_library(pbr_sycl STATIC IMPORTED) + set_target_properties(pbr_sycl PROPERTIES + IMPORTED_LOCATION "${PBR_LIB_DIR}/libpbr_sycl.a" + ) + set(PBR_SYCL_LIB pbr_sycl) + message(STATUS "PBR SYCL renderer library: ${PBR_LIB_DIR}/libpbr_sycl.a") + endif() +endif() + +if (WIN32) + # Add the macro definition + add_definitions(-DGLM_ENABLE_EXPERIMENTAL) + # Set vcpkg toolchain file + set(CMAKE_TOOLCHAIN_FILE "C:/Users/juang/Documents/Development/vcpkg/scripts/buildsystems/vcpkg.cmake" CACHE STRING "Vcpkg toolchain file") + # Set CMake prefix path for vcpkg packages + set(CMAKE_PREFIX_PATH "C:/Users/juang/Documents/Development/vcpkg/installed/x64-windows") + set(GLFW_BUILD_STATIC ON) + message(STATUS "Working Dir: " ${CMAKE_CURRENT_SOURCE_DIR}) +endif() + +# Include directories +include_directories( + ${FUNGT_BASE_DIR} + ${FUNGT_BASE_DIR}/GT + ${FUNGT_BASE_DIR}/VertexGL + ${FUNGT_BASE_DIR}/Shaders + ${FUNGT_BASE_DIR}/funGT + ${FUNGT_BASE_DIR}/AnimatedModel + ${FUNGT_BASE_DIR}/Textures + ${FUNGT_BASE_DIR}/vendor/stb_image + ${FUNGT_BASE_DIR}/Imgui_Setup + ${FUNGT_BASE_DIR}/Material + ${FUNGT_BASE_DIR}/Mesh + ${FUNGT_BASE_DIR}/Camera + ${FUNGT_BASE_DIR}/Geometries + ${FUNGT_BASE_DIR}/Model + ${FUNGT_BASE_DIR}/Helpers + ${FUNGT_BASE_DIR}/Animation + ${FUNGT_BASE_DIR}/Bone + ${FUNGT_BASE_DIR}/Matrix + ${FUNGT_BASE_DIR}/SceneManager + ${FUNGT_BASE_DIR}/CubeMap + ${FUNGT_BASE_DIR}/ParticleSimulation + ${FUNGT_BASE_DIR}/Random + ${FUNGT_BASE_DIR}/Path_Manager + ${FUNGT_BASE_DIR}/InfoWindow + ${FUNGT_BASE_DIR}/GUI + ${FUNGT_BASE_DIR}/GUI/physics + ${FUNGT_BASE_DIR}/SimpleModel + ${FUNGT_BASE_DIR}/SimpleGeometry + ${FUNGT_BASE_DIR}/Physics/CollisionManager + ${FUNGT_BASE_DIR}/Physics/Contact + ${FUNGT_BASE_DIR}/Physics/RigidBody + ${FUNGT_BASE_DIR}/Physics/Shapes + ${FUNGT_BASE_DIR}/Physics/Integrators + ${FUNGT_BASE_DIR}/Physics/ContactHelpers + ${FUNGT_BASE_DIR}/Physics/PhysicsWorld + ${FUNGT_BASE_DIR}/Physics/Clothing + ${FUNGT_BASE_DIR}/Quaternion + ${FUNGT_BASE_DIR}/Vector + ${FUNGT_BASE_DIR}/ViewPort + ${FUNGT_BASE_DIR}/Renders + ${FUNGT_BASE_DIR}/Platform/OpenGL + ${FUNGT_BASE_DIR}/InfoDevice + ${FUNGT_BASE_DIR}/PBR/PBRCamera + ${FUNGT_BASE_DIR}/PBR/Ray + ${FUNGT_BASE_DIR}/PBR/HitData + ${FUNGT_BASE_DIR}/PBR/Intersection + ${FUNGT_BASE_DIR}/PBR/Light + ${FUNGT_BASE_DIR}/PBR/Space + ${FUNGT_BASE_DIR}/PBR/Render/include + ${FUNGT_BASE_DIR}/PBR/Render/brdf + ${FUNGT_BASE_DIR}/PBR/Render/shared + ${FUNGT_BASE_DIR}/PBR/TextureManager + ${FUNGT_BASE_DIR}/PBR/BVH + ${FUNGT_BASE_DIR}/Triangle +) + +# ════════════════════════════════════════════════════════════════════════════ +# COMMON SOURCE FILES (compiled with SYCL compiler) +# ════════════════════════════════════════════════════════════════════════════ +set(SOURCE_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/main.cpp + ${FUNGT_BASE_DIR}/GT/graphicsTool.cpp + ${FUNGT_BASE_DIR}/VertexGL/vertexArrayObjects.cpp + ${FUNGT_BASE_DIR}/VertexGL/vertexBuffers.cpp + ${FUNGT_BASE_DIR}/VertexGL/vertexIndices.cpp + ${FUNGT_BASE_DIR}/Shaders/shader.cpp + ${FUNGT_BASE_DIR}/funGT/fungt.cpp + ${FUNGT_BASE_DIR}/AnimatedModel/animated_model.cpp + ${FUNGT_BASE_DIR}/Textures/textures.cpp + ${FUNGT_BASE_DIR}/vendor/stb_image/stb_image.cpp + ${FUNGT_BASE_DIR}/Imgui_Setup/imgui_setup.cpp + ${FUNGT_BASE_DIR}/Material/material.cpp + ${FUNGT_BASE_DIR}/Mesh/mesh.cpp + ${FUNGT_BASE_DIR}/Camera/camera.cpp + ${FUNGT_BASE_DIR}/Geometries/cube.cpp + ${FUNGT_BASE_DIR}/Geometries/plane.cpp + ${FUNGT_BASE_DIR}/Geometries/primitives.cpp + ${FUNGT_BASE_DIR}/Geometries/pyramid.cpp + ${FUNGT_BASE_DIR}/Geometries/shape.cpp + ${FUNGT_BASE_DIR}/Geometries/inf_grid.cpp + ${FUNGT_BASE_DIR}/Geometries/square.cpp + ${FUNGT_BASE_DIR}/Geometries/sphere.cpp + ${FUNGT_BASE_DIR}/Geometries/box.cpp + ${FUNGT_BASE_DIR}/Geometries/torus.cpp + ${FUNGT_BASE_DIR}/Model/model.cpp + ${FUNGT_BASE_DIR}/Helpers/helpers.cpp + ${FUNGT_BASE_DIR}/Animation/animation.cpp + ${FUNGT_BASE_DIR}/Bone/bone.cpp + ${FUNGT_BASE_DIR}/Matrix/matrix4x4f.cpp + ${FUNGT_BASE_DIR}/Matrix/matrix3x3f.cpp + ${FUNGT_BASE_DIR}/SceneManager/scene_manager.cpp + ${FUNGT_BASE_DIR}/CubeMap/cube_map.cpp + ${FUNGT_BASE_DIR}/ParticleSimulation/particle_simulation.cpp + ${FUNGT_BASE_DIR}/Random/random.cpp + ${FUNGT_BASE_DIR}/Path_Manager/path_manager.cpp + ${FUNGT_BASE_DIR}/InfoWindow/infowindow.cpp + ${FUNGT_BASE_DIR}/GUI/gui.cpp + ${FUNGT_BASE_DIR}/GUI/physics/simulation_controller_window.cpp + ${FUNGT_BASE_DIR}/GUI/physics/debug_rigidbody_renderer.cpp + ${FUNGT_BASE_DIR}/SimpleModel/simple_model.cpp + ${FUNGT_BASE_DIR}/SimpleGeometry/simple_geometry.cpp + ${FUNGT_BASE_DIR}/Physics/Collisions/simple_collision.cpp + ${FUNGT_BASE_DIR}/Physics/Collisions/manifold_collision.cpp + ${FUNGT_BASE_DIR}/Physics/CollisionManager/collision_manager.cpp + ${FUNGT_BASE_DIR}/Physics/Collider/collider.cpp + ${FUNGT_BASE_DIR}/Physics/Clothing/clothing.cpp + ${FUNGT_BASE_DIR}/ViewPort/viewport.cpp + ${FUNGT_BASE_DIR}/Renders/framebuffer.cpp + ${FUNGT_BASE_DIR}/Renders/display_graphics.cpp + ${FUNGT_BASE_DIR}/Platform/OpenGL/OGLframeBuffer.cpp + ${FUNGT_BASE_DIR}/InfoDevice/gpu_device_info.cpp + ${FUNGT_BASE_DIR}/PBR/Intersection/intersection.cpp + ${FUNGT_BASE_DIR}/PBR/Space/space.cpp + ${FUNGT_BASE_DIR}/PBR/BVH/bvh_builder.cpp +) + +# ════════════════════════════════════════════════════════════════════════════ +# GPU DEVICE DETECTION BACKENDS +# ════════════════════════════════════════════════════════════════════════════ + +# CUDA backend (compiled with g++, NOT SYCL compiler) +if(FUNGT_USE_CUDA) + find_package(CUDAToolkit REQUIRED) + + if(CUDAToolkit_FOUND) + message(STATUS "CUDA Toolkit found: ${CUDAToolkit_VERSION}") + + # Create separate library for CUDA backend + add_library(cuda_backend STATIC + ${FUNGT_BASE_DIR}/InfoDevice/cuda_backend.cpp + ) + + # CRITICAL: Compile with standard g++, NOT SYCL compiler + set_source_files_properties( + ${FUNGT_BASE_DIR}/InfoDevice/cuda_backend.cpp + PROPERTIES + COMPILE_FLAGS "-std=c++17" + ) + + target_include_directories(cuda_backend PRIVATE + ${CUDAToolkit_INCLUDE_DIRS} + ${FUNGT_BASE_DIR}/InfoDevice + ) + + target_link_libraries(cuda_backend PRIVATE + CUDA::cudart + ) + + set(CUDA_BACKEND_LIB cuda_backend) + add_compile_definitions(FUNGT_USE_CUDA) + message(STATUS "CUDA device detection enabled (compiled with g++)") + enable_language(CUDA) + else() + message(FATAL_ERROR "CUDA requested but CUDA Toolkit not found!") + endif() +else() + message(STATUS "CUDA backend disabled") +endif() + +# SYCL backend (compiled with SYCL compiler) +if(FUNGT_USE_SYCL) + list(APPEND SOURCE_FILES ${FUNGT_BASE_DIR}/InfoDevice/sycl_backend.cpp) + message(STATUS "SYCL device detection enabled (compiled with SYCL compiler)") +else() + message(STATUS "SYCL backend disabled") +endif() + +# ════════════════════════════════════════════════════════════════════════════ +# CREATE EXECUTABLE +# ════════════════════════════════════════════════════════════════════════════ +add_executable(FunGT ${SOURCE_FILES}) +if(FUNGT_USE_CUDA) + set_target_properties(FunGT PROPERTIES + CUDA_RESOLVE_DEVICE_SYMBOLS ON + CUDA_SEPARABLE_COMPILATION ON + ) +endif() +# ════════════════════════════════════════════════════════════════════════════ +# PLATFORM-SPECIFIC CONFIGURATIONS +# ════════════════════════════════════════════════════════════════════════════ +if (WIN32) + # Use vcpkg packages on Windows + find_package(OpenGL REQUIRED) + find_package(GLEW REQUIRED) + find_package(glfw3 REQUIRED) + find_package(assimp REQUIRED) + find_package(glm REQUIRED) + + target_link_libraries(FunGT + PRIVATE + OpenGL::GL + glfw + GLEW::GLEW + assimp::assimp + glm::glm + ) + +elseif (UNIX) + # ════════════════════════════════════════════════════════════════════════ + # LINUX-SPECIFIC SETTINGS + # ════════════════════════════════════════════════════════════════════════ + message(STATUS "Configuring for Linux") + + # Find required packages + find_package(OpenCL REQUIRED) + if (OpenCL_FOUND) + message(STATUS "OpenCL found") + else() + message(FATAL_ERROR "OpenCL library not found!") + endif() + + find_package(OpenGL REQUIRED) + if (OpenGL_FOUND) + message(STATUS "OpenGL found") + else() + message(FATAL_ERROR "OpenGL library not found!") + endif() + + find_package(glfw3 REQUIRED) + if (glfw3_FOUND) + message(STATUS "GLFW found") + else() + message(FATAL_ERROR "GLFW library not found!") + endif() + + find_package(GLEW REQUIRED) + if (GLEW_FOUND) + message(STATUS "GLEW found") + else() + message(FATAL_ERROR "GLEW library not found!") + endif() + + find_package(assimp REQUIRED) + if (assimp_FOUND) + message(STATUS "Assimp found") + else() + message(FATAL_ERROR "Assimp library not found!") + endif() + + find_package(glm CONFIG REQUIRED) + if(glm_FOUND) + message(STATUS "glm found") + else() + message(FATAL_ERROR "glm not found!") + endif() + + # Link libraries + target_include_directories(FunGT PRIVATE ${OpenCL_INCLUDE_DIRS}) + target_link_libraries(FunGT + PRIVATE + OpenGL::GL + glfw + GLEW::GLEW + assimp::assimp + glm::glm + dl + funlib + imgui + ${OpenCL_LIBRARIES} + ${CUDA_BACKEND_LIB} + pbr_core + ${PBR_CUDA_LIB} + ${PBR_SYCL_LIB} + $<$:CUDA::cudart_static> # CHANGE TO cudart_static + $<$:CUDA::cuda_driver> + ) + + # ════════════════════════════════════════════════════════════════════════ + # SYCL compilation flags (always applied because funlib requires it) + # ════════════════════════════════════════════════════════════════════════ + target_compile_options(FunGT PRIVATE -fsycl) + target_link_options(FunGT PRIVATE -fsycl) + + if(FUNGT_USE_SYCL) + target_compile_definitions(FunGT PRIVATE FUNGT_USE_SYCL) + endif() + +endif() + +# ════════════════════════════════════════════════════════════════════════════ +# BUILD SUMMARY +# ════════════════════════════════════════════════════════════════════════════ +message(STATUS "") +message(STATUS "════════════════════════════════════════") +message(STATUS " FunGT Build Configuration") +message(STATUS "════════════════════════════════════════") +message(STATUS " Main Compiler: ${CMAKE_CXX_COMPILER}") +message(STATUS " CUDA Backend: ${FUNGT_USE_CUDA}") +message(STATUS " SYCL Backend: ${FUNGT_USE_SYCL}") +message(STATUS " Build Type: ${CMAKE_BUILD_TYPE}") +if(FUNGT_USE_CUDA) + message(STATUS " CUDA Compiled: Separately with g++") +endif() +message(STATUS "════════════════════════════════════════") +message(STATUS "") \ No newline at end of file diff --git a/Samples/creating_model/main.cpp b/Samples/creating_model/main.cpp new file mode 100644 index 0000000..66380f1 --- /dev/null +++ b/Samples/creating_model/main.cpp @@ -0,0 +1,38 @@ +#include "funGT/fungt.hpp" +const unsigned int SCREEN_WIDTH = 2100; +const unsigned int SCREEN_HEIGHT = 1200; + + + +int main (){ + //std::string path = findProjectRoot(); + //Path to your shaders and models: + ModelPaths model_ball; + //model.path = getAssetPath("Animations/monster_dancing/monster_dancing.dae"); + model_ball.path = getAssetPath("Obj/LuxoLamp/Luxo.obj"); + + //Creates a FunGT Scene to display + FunGTScene myGame = FunGT::createScene(SCREEN_WIDTH, SCREEN_HEIGHT); + //Background color, use 255.f for pure white, + myGame->setBackgroundColor(); + + myGame->initGL(); + //Gets an instance of the SceneManager class to render objects + FunGTSceneManager scene_manager = myGame->getSceneManager(); + // Creates an animation object + FunGTSGeom donut = SimpleGeometry::create(Geometry::Torus); + donut->load(); + + + myGame->set([&]() { // Sets up all the scenes in your game + // Adds the renderable objects to the SceneManager + // Adds the renderable objects to the SceneManager + scene_manager->addRenderableObj(donut); + }); + myGame->render([&](){ + + }); + + return 0; + +} \ No newline at end of file diff --git a/SimpleGeometry/simple_geometry.cpp b/SimpleGeometry/simple_geometry.cpp index 474437e..0ce9979 100644 --- a/SimpleGeometry/simple_geometry.cpp +++ b/SimpleGeometry/simple_geometry.cpp @@ -15,9 +15,26 @@ void SimpleGeometry::setPrimitive(std::shared_ptr primitive) { void SimpleGeometry::setMaterial(const glm::vec3& baseColor, float roughness, float metallic) { - m_material.baseColor = baseColor; - m_material.roughness = roughness; - m_material.metallic = metallic; + // Base color maps to diffuse + m_material.m_diffLigth = baseColor; + + // Ambient is darker version of base color + //m_material.m_ambientLight = baseColor * 0.3f; + + // Roughness maps to shininess inversely + // roughness: 0 = shiny, 1 = rough + // shininess: high = shiny, low = rough + // Formula: shininess = (1 - roughness)^2 * 128 + // float shininess = std::pow(1.0f - roughness, 2.0f) * 128.0f; + // m_material.m_shininess = std::max(1.0f, shininess); + + // // Specular strength based on roughness + // // Rough surfaces have weak specular highlights + // float specStrength = 1.0f - roughness; + // m_material.m_specLight = glm::vec3(specStrength * 0.5f); + + // // No emission by default + // m_material.m_emission = 0.0f; } void SimpleGeometry::setShaderPaths(const std::string &vs_path, const std::string &fs_path) { @@ -34,6 +51,7 @@ void SimpleGeometry::load(const std::string &pathToTexture) { m_Shader.create(m_vs_path, m_fs_path); if(!pathToTexture.empty()){ m_primitive->setTexture(pathToTexture); + m_isTexturized = true; } m_primitive->InitGraphics(); @@ -61,7 +79,10 @@ void SimpleGeometry::scale(float s) { } void SimpleGeometry::draw() { + if (m_primitive) { + m_Shader.setUniform1i("hasTexture", m_isTexturized == true ? 1 : 0); + m_material.sendToShader(m_Shader); m_primitive->draw(); } } @@ -128,6 +149,19 @@ std::shared_ptr SimpleGeometry::create(Geometry geomType) { simpleGeom->m_vs_path = getAssetPath("shaders/primitive_vs.glsl"); simpleGeom->m_fs_path = getAssetPath("shaders/primitive_fs.glsl"); break; + } + case Geometry::Torus:{ + simpleGeom->setPrimitive(std::make_shared(0.8f, 0.3f, 48, 24)); + simpleGeom->m_vs_path = getAssetPath("shaders/primitive_vs.glsl"); + simpleGeom->m_fs_path = getAssetPath("shaders/primitive_fs.glsl"); + + // Donut: warm brown, somewhat rough, non-metallic + simpleGeom->setMaterial( + glm::vec3(0.82f, 0.61f, 0.42f), // baseColor (donut brown) + 0.7f, // roughness (matte finish) + 0.0f // metallic (food is never metal) + ); + break; } default: throw std::runtime_error("Unknown geometry type"); diff --git a/SimpleGeometry/simple_geometry.hpp b/SimpleGeometry/simple_geometry.hpp index 293940d..435c1c3 100644 --- a/SimpleGeometry/simple_geometry.hpp +++ b/SimpleGeometry/simple_geometry.hpp @@ -6,8 +6,10 @@ #include "Geometries/sphere.hpp" #include "Geometries/box.hpp" #include "Geometries/plane.hpp" +#include "Geometries/torus.hpp" #include "Renderable/renderable.hpp" #include "Path_Manager/path_manager.hpp" +#include "Material/material.hpp" #include "Shaders/shader.hpp" #include @@ -17,19 +19,14 @@ enum class Geometry { Cube, Sphere, Box, - Plane + Plane, + Torus // Add more geometry types as needed }; class SimpleGeometry : public Renderable { private: - struct GeometryMaterial { - int baseColorTexIdx = -1; - glm::vec3 baseColor = glm::vec3(0.8f); - float roughness = 0.5f; - float metallic = 0.0f; - }; - GeometryMaterial m_material; + Material m_material; std::shared_ptr m_primitive; Shader m_Shader; @@ -63,7 +60,7 @@ class SimpleGeometry : public Renderable { // Getters for PBR path tracer std::shared_ptr getPrimitive() const { return m_primitive; } - const GeometryMaterial& getMaterial() const { return m_material; } + const Material& getMaterial() const { return m_material; } // Override methods from Renderable void draw() override; Shader& getShader() override; diff --git a/funGT/fungt.cpp b/funGT/fungt.cpp index c29a389..db8b103 100644 --- a/funGT/fungt.cpp +++ b/funGT/fungt.cpp @@ -31,7 +31,23 @@ void FunGT::processKeyBoardInput() // m_camera.move(deltaTime,2); // if (glfwGetKey(m_Window, GLFW_KEY_D) == GLFW_PRESS) // m_camera.move(deltaTime,3); - + static bool tabPressed = false; + if (glfwGetKey(m_Window, GLFW_KEY_TAB) == GLFW_PRESS) { + if (!tabPressed) { + m_editMode = !m_editMode; + + // Toggle edit mode on ALL renderables + for (auto& node : m_sceneManager->getRenderable()) { + node->setEditMode(m_editMode); + } + + std::cout << "Edit Mode: " << (m_editMode ? "ON" : "OFF") << std::endl; + tabPressed = true; + } + } + else { + tabPressed = false; + } } void FunGT::processMouseInput(double xpos, double ypos) { diff --git a/funGT/fungt.hpp b/funGT/fungt.hpp index a13b398..a15b65b 100644 --- a/funGT/fungt.hpp +++ b/funGT/fungt.hpp @@ -58,6 +58,12 @@ class FunGT : public GraphicsTool{ spCollisionManager m_collisionManager; std::shared_ptr m_physicsWorld; + + //Editing meshes: + + bool m_editMode = false; + Primitive* m_activePrimitive = nullptr; // Which mesh are we editing + public: FunGT(int _width, int _height); ~FunGT(); @@ -85,7 +91,6 @@ class FunGT : public GraphicsTool{ if(m_physicsWorld) { m_collisionManager = m_physicsWorld->getCollisionManager(); } - //m_collisionManager = m_physicsWorld->getCollisionManager(); } std::shared_ptr getPhysicsWorld() { if(!m_physicsWorld) { @@ -94,6 +99,12 @@ class FunGT : public GraphicsTool{ } return m_physicsWorld; } + //Mesh editing functions + bool isEditMode() const { return m_editMode; } + void setEditMode(bool enabled) { m_editMode = enabled; } + void setActivePrimitive(Primitive* prim) { m_activePrimitive = prim; } + Primitive* getActivePrimitive() { return m_activePrimitive; } + protected: // Override virtual methods from GraphicsTool void onMouseMove(double xpos, double ypos) override; diff --git a/shaders/primitive_fs.glsl b/shaders/primitive_fs.glsl index be9bd9e..bd19446 100644 --- a/shaders/primitive_fs.glsl +++ b/shaders/primitive_fs.glsl @@ -1,5 +1,13 @@ #version 440 core +struct Material { + vec3 ambient; + vec3 diffuse; + vec3 specular; + float shininess; + float emission; +}; + in vec3 FragPos; in vec3 Normal; in vec2 TexCoord; @@ -9,9 +17,17 @@ out vec4 FragColor; uniform sampler2D texture_diffuse; uniform vec3 lightPos = vec3(5.0, 5.0, 5.0); uniform vec3 viewPos = vec3(0.0, 2.0, 5.0); - +uniform bool hasTexture; +uniform Material material; void main() { - vec3 color = texture(texture_diffuse, TexCoord).rgb; + vec3 color; + if(hasTexture){ + color = texture(texture_diffuse, TexCoord).rgb; + } + else{ + color = material.diffuse; + } + // Ambient vec3 ambient = 0.3 * color;