diff --git a/imgui_sdl_simple_example/imgui_sdl_simple_example/imgui_sdl_simple_example.vcxproj b/imgui_sdl_simple_example/imgui_sdl_simple_example/imgui_sdl_simple_example.vcxproj index b4102e4..d710420 100644 --- a/imgui_sdl_simple_example/imgui_sdl_simple_example/imgui_sdl_simple_example.vcxproj +++ b/imgui_sdl_simple_example/imgui_sdl_simple_example/imgui_sdl_simple_example.vcxproj @@ -107,6 +107,7 @@ _DEBUG;_CONSOLE;%(PreprocessorDefinitions) true ..\..\sdl2_simple_example\sdl2_simple_example\ + stdcpp20 Console diff --git a/sdl2_simple_example/sdl2_simple_example/Config.cpp b/sdl2_simple_example/sdl2_simple_example/Config.cpp new file mode 100644 index 0000000..b605b48 --- /dev/null +++ b/sdl2_simple_example/sdl2_simple_example/Config.cpp @@ -0,0 +1,103 @@ +#include "Config.h" + + +Config::Config(MyWindow* window) : _myWindow(window) {} +Config::~Config() {} + +void Config::draw(SDL_Window* _window, Renderer renderer, float fps) { + ImGui::Begin("Config"); + ImGui::Text("Configuracion general del sistema"); + + // Sección de resolución de pantalla + ImGui::Text("Resolución de pantalla:"); + + struct ResolutionItem { + ivec2 resolution; + std::string name; + }; + + std::vector resolutions = { + { {800, 600}, "800 x 600" }, + { {1024, 768}, "1024 x 768" }, + { {1280, 720}, "1280 x 720" }, + { {1360, 768}, "1360 x 768" }, + { {1366, 768}, "1366 x 768" }, + { {1440, 900}, "1440 x 900" }, + { {1600, 900}, "1600 x 900" }, + { {1920, 1080}, "1920 x 1080" } + }; + + static int selectedResolutionIndex = 0; + if (ImGui::Combo("Resolucion", &selectedResolutionIndex, [](void* data, int idx, const char** out_text) { + const std::vector* resolutions = static_cast*>(data); + *out_text = resolutions->at(idx).name.c_str(); // Return resolution name + return true; + }, (void*)&resolutions, resolutions.size())) { + selectedResolution = resolutions[selectedResolutionIndex].resolution; + + // Update window size + SDL_SetWindowSize(_window, selectedResolution.x, selectedResolution.y); + + // Ensure we apply the new projection and viewport + if (!isFullscreen) { + renderer.applyProjectionAndViewport(selectedResolution); // Aplica la resolución seleccionada + } + } + + // Sección de pantalla completa + if (ImGui::Checkbox("Pantalla completa", &isFullscreen)) { + if (isFullscreen) { + SDL_SetWindowFullscreen(_window, SDL_WINDOW_FULLSCREEN_DESKTOP); + _myWindow->logMessage("Pantalla completa activada."); + } + else { + SDL_SetWindowFullscreen(_window, 0); // 0 means windowed mode + _myWindow->logMessage("Pantalla completa desactivada."); + } + + // Get the current window size dynamically after changing fullscreen mode + int windowWidth, windowHeight; + SDL_GetWindowSize(_window, &windowWidth, &windowHeight); + + // Apply the new window size for projection and viewport + renderer.applyProjectionAndViewport(ivec2(windowWidth, windowHeight)); + } + + ImGui::Text("Grafico FPS"); + static float values[90] = {}; + static int values_offset = 0; + values[values_offset] = fps; // Ejemplo fijo de FPS + values_offset = (values_offset + 1) % 90; + char fpsText[16]; + sprintf_s(fpsText, "%d fps", static_cast(fps)); + ImGui::PlotLines("FPS", values, IM_ARRAYSIZE(values), values_offset, fpsText, 0.0f, 100.0f, ImVec2(0, 80)); + + ImGui::Text("Consumo de Memoria: "); + try { + MemoryInfo memInfo = MemoryUsage::getMemoryInfo(); + ImGui::Separator(); + ImGui::Text("Consumo de Memoria:"); + ImGui::Text("Memoria Total: %llu MB", memInfo.totalMemory); + ImGui::Text("Memoria Libre: %llu MB", memInfo.freeMemory); + ImGui::Text("Memoria Usada: %llu MB", memInfo.usedMemory); + static float totalMemoryValues[90]; + static float freeMemoryValues[90]; + static float usedMemoryValues[90]; + static int memValuesOffset = 0; + + totalMemoryValues[memValuesOffset] = memInfo.totalMemory; + freeMemoryValues[memValuesOffset] = memInfo.freeMemory; + usedMemoryValues[memValuesOffset] = memInfo.usedMemory; + memValuesOffset = (values_offset + 1) % 90; + + ImGui::PlotLines("TotalMem", totalMemoryValues, IM_ARRAYSIZE(totalMemoryValues), memValuesOffset, "TotalMem", 0.0f, 100.0f, ImVec2(0, 80)); + ImGui::PlotLines("freeMem", freeMemoryValues, IM_ARRAYSIZE(freeMemoryValues), memValuesOffset, "FreeMem", 0.0f, 100.0f, ImVec2(0, 80)); + ImGui::PlotLines("UsedMem", usedMemoryValues, IM_ARRAYSIZE(usedMemoryValues), memValuesOffset, "UsedMem", 0.0f, 100.0f, ImVec2(0, 80)); + } + catch (const std::exception& e) { + ImGui::Text("Error obteniendo memoria: %s", e.what()); + } + ImGui::Text("Deteccion de maquinaria i versions de programario:"); + ImGui::Text("SDL, OpenGL, DevIL"); + ImGui::End(); +} \ No newline at end of file diff --git a/sdl2_simple_example/sdl2_simple_example/Config.h b/sdl2_simple_example/sdl2_simple_example/Config.h new file mode 100644 index 0000000..f7d3d01 --- /dev/null +++ b/sdl2_simple_example/sdl2_simple_example/Config.h @@ -0,0 +1,25 @@ +#pragma once +#ifndef CONFIG_H +#define CONFIG_H + +#include +#include +#include +#include "MyWindow.h" +#include "MemoryUsage.h" + + +class Config { +public: + Config(MyWindow* window); + ~Config(); + + void draw(SDL_Window* _window, Renderer renderer, float fps); +private: + MyWindow* _myWindow; + glm::ivec2 selectedResolution; + bool isFullscreen = false; +}; + + +#endif // CONFIG_H \ No newline at end of file diff --git a/sdl2_simple_example/sdl2_simple_example/Consola.cpp b/sdl2_simple_example/sdl2_simple_example/Consola.cpp new file mode 100644 index 0000000..ff96704 --- /dev/null +++ b/sdl2_simple_example/sdl2_simple_example/Consola.cpp @@ -0,0 +1,12 @@ +#include "Consola.h" + +Consola::Consola(){} +Consola::~Consola(){} + +void Consola::draw(std::vector logMessage) { + ImGui::Begin("Consola"); + for (const auto& msg : logMessage) { + ImGui::Text("%s", msg.c_str()); + } + ImGui::End(); +} \ No newline at end of file diff --git a/sdl2_simple_example/sdl2_simple_example/Consola.h b/sdl2_simple_example/sdl2_simple_example/Consola.h new file mode 100644 index 0000000..73f490c --- /dev/null +++ b/sdl2_simple_example/sdl2_simple_example/Consola.h @@ -0,0 +1,19 @@ +#pragma once +#ifndef CONSOLA_H +#define CONSOLA_H + +#include +#include +#include + +class Consola { +public: + Consola(); + ~Consola(); + + static void draw(std::vector logMessage); +private: + +}; + +#endif // CONSOLA_H \ No newline at end of file diff --git a/sdl2_simple_example/sdl2_simple_example/CustomFormatUtils.h b/sdl2_simple_example/sdl2_simple_example/CustomFormatUtils.h new file mode 100644 index 0000000..1c4c78a --- /dev/null +++ b/sdl2_simple_example/sdl2_simple_example/CustomFormatUtils.h @@ -0,0 +1,42 @@ +#pragma once +#include +#include +#include + +void saveCustomFormat(const std::string& path, const std::vector& vertices, const std::vector& uvs, const std::vector& indices) { + std::ofstream file(path, std::ios::binary); + + size_t vertexCount = vertices.size(); + size_t uvCount = uvs.size(); + size_t indexCount = indices.size(); + + file.write(reinterpret_cast(&vertexCount), sizeof(size_t)); + file.write(reinterpret_cast(vertices.data()), vertices.size() * sizeof(float)); + + file.write(reinterpret_cast(&uvCount), sizeof(size_t)); + file.write(reinterpret_cast(uvs.data()), uvs.size() * sizeof(float)); + + file.write(reinterpret_cast(&indexCount), sizeof(size_t)); + file.write(reinterpret_cast(indices.data()), indices.size() * sizeof(unsigned int)); + + file.close(); +} + +void loadCustomFormat(const std::string& path, std::vector& vertices, std::vector& uvs, std::vector& indices) { + std::ifstream file(path, std::ios::binary); + + size_t vertexCount, uvCount, indexCount; + file.read(reinterpret_cast(&vertexCount), sizeof(size_t)); + vertices.resize(vertexCount); + file.read(reinterpret_cast(vertices.data()), vertexCount * sizeof(float)); + + file.read(reinterpret_cast(&uvCount), sizeof(size_t)); + uvs.resize(uvCount); + file.read(reinterpret_cast(uvs.data()), uvCount * sizeof(float)); + + file.read(reinterpret_cast(&indexCount), sizeof(size_t)); + indices.resize(indexCount); + file.read(reinterpret_cast(indices.data()), indexCount * sizeof(unsigned int)); + + file.close(); +} \ No newline at end of file diff --git a/sdl2_simple_example/sdl2_simple_example/Escena.cpp b/sdl2_simple_example/sdl2_simple_example/Escena.cpp new file mode 100644 index 0000000..4bbccc4 --- /dev/null +++ b/sdl2_simple_example/sdl2_simple_example/Escena.cpp @@ -0,0 +1,24 @@ +#include "Escena.h" + +extern Renderer renderer; + +Escena::Escena(){} +Escena::~Escena(){} + +void Escena::draw(GLuint textureColorbuffer) { + ImGui::Begin("Scene"); + if (textureColorbuffer) { + + ImGui::Image((void*)(intptr_t)textureColorbuffer, ImVec2(renderer._WINDOW_SIZE.x, renderer._WINDOW_SIZE.y)); + + if (ImGui::BeginDragDropTarget()) { + if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload("FILE_PATH")) { + const char* filePath = (const char*)payload->Data; + + handleFileDrop(filePath); // Procesar archivo arrastrado + } + ImGui::EndDragDropTarget(); + } + } + ImGui::End(); +} diff --git a/sdl2_simple_example/sdl2_simple_example/Escena.h b/sdl2_simple_example/sdl2_simple_example/Escena.h new file mode 100644 index 0000000..0f4fd72 --- /dev/null +++ b/sdl2_simple_example/sdl2_simple_example/Escena.h @@ -0,0 +1,18 @@ +#pragma once +#ifndef ESCENA_H +#define ESCENA_H + +#include +#include "MyWindow.h" + + +class Escena { +public: + Escena(); + ~Escena(); + static void draw(GLuint texturecolorbuffer); +private: + +}; + +#endif // ESCENA_H \ No newline at end of file diff --git a/sdl2_simple_example/sdl2_simple_example/FileExplorer.cpp b/sdl2_simple_example/sdl2_simple_example/FileExplorer.cpp new file mode 100644 index 0000000..022bbd9 --- /dev/null +++ b/sdl2_simple_example/sdl2_simple_example/FileExplorer.cpp @@ -0,0 +1,111 @@ +#include "FileExplorer.h" + +Explorer::Explorer(){} +Explorer::~Explorer(){} + + +void Explorer::UpdateDirectoryContents() { + try { + directoryContents.clear(); + + if (fs::exists(currentDirectory) && fs::is_directory(currentDirectory)) { + for (const auto& entry : fs::directory_iterator(currentDirectory)) { + directoryContents.push_back(entry.path().string()); + } + // Asegurarnos de que el índice seleccionado sea válido + if (selectedFileIndex >= directoryContents.size()) { + selectedFileIndex = -1; + } + } + } + catch (const fs::filesystem_error& e) { + std::cerr << "Error listing directory: " << e.what() << std::endl; + selectedFileIndex = -1; + } +} +void Explorer::deleteSelectedFile() { + if (selectedFileIndex < 0 || selectedFileIndex >= directoryContents.size()) { + return; + } + + try { + std::string filePath = directoryContents[selectedFileIndex]; + if (!fs::is_directory(filePath) && fs::exists(filePath)) { + if (fs::remove(filePath)) { + selectedFileIndex = -1; + UpdateDirectoryContents(); + } + } + } + catch (const fs::filesystem_error& e) { + std::cerr << "Error deleting file: " << e.what() << std::endl; + } +} +void Explorer::draw() { + ImGui::Begin("File Explorer"); + + // Mostrar directorio actual + ImGui::Text("Current Directory: %s", currentDirectory.c_str()); + + // Botón para subir un nivel + if (ImGui::Button(".. (Go Up)")) { + try { + fs::path parent = fs::path(currentDirectory).parent_path(); + if (fs::exists(parent)) { + currentDirectory = parent.string(); + selectedFileIndex = -1; // Reset selection when changing directory + UpdateDirectoryContents(); + } + } + catch (const fs::filesystem_error& e) { + std::cerr << "Error accessing parent directory: " << e.what() << std::endl; + } + } + + // Lista de archivos y directorios + ImGui::BeginChild("Files", ImVec2(0, -30), true); + for (size_t i = 0; i < directoryContents.size(); ++i) { + try { + fs::path entry(directoryContents[i]); + if (!fs::exists(entry)) continue; // Skip if file no longer exists + + std::string displayName = FileSystemUtils::getFileName(entry.string()); + bool isSelected = (i == selectedFileIndex); + + if (fs::is_directory(entry)) { + // Directorios como botones + if (ImGui::Button(displayName.c_str())) { + currentDirectory = entry.string(); + selectedFileIndex = -1; + UpdateDirectoryContents(); + } + } + else { + // Archivos como seleccionables + if (ImGui::Selectable(displayName.c_str(), isSelected)) { + selectedFileIndex = i; + } + + // Drag & Drop solo para archivos + if (ImGui::BeginDragDropSource(ImGuiDragDropFlags_None)) { + const char* filePath = entry.string().c_str(); + ImGui::SetDragDropPayload("FILE_PATH", filePath, strlen(filePath) + 1); + ImGui::Text("Dragging: %s", displayName.c_str()); + ImGui::EndDragDropSource(); + } + } + } + catch (const fs::filesystem_error& e) { + std::cerr << "Error processing file: " << e.what() << std::endl; + } + } + ImGui::EndChild(); + + // Manejar borrado con tecla Delete + if (ImGui::IsKeyPressed(ImGuiKey_Delete) && selectedFileIndex >= 0) { + deleteSelectedFile(); + } + + ImGui::End(); +} + diff --git a/sdl2_simple_example/sdl2_simple_example/FileExplorer.h b/sdl2_simple_example/sdl2_simple_example/FileExplorer.h new file mode 100644 index 0000000..46dcc92 --- /dev/null +++ b/sdl2_simple_example/sdl2_simple_example/FileExplorer.h @@ -0,0 +1,40 @@ +#pragma once +#ifndef FILEEXPLORER_H +#define FILEEXPLORER_H + +#include +#include "MyWindow.h" +#include "FilesystemUtils.h" +#include +#include + +class Explorer { +public: + //MyWindow* window + Explorer(); + ~Explorer(); + + void draw(); + void UpdateDirectoryContents(); + + + int getSelectedFileIndex() const { return selectedFileIndex; } + std::string getSelectedFilePath() const { + return selectedFileIndex >= 0 && selectedFileIndex < directoryContents.size() + ? directoryContents[selectedFileIndex] + : ""; + } +private: + //MyWindow* _myWindow; + glm::ivec2 selectedResolution; + bool isFullscreen = false; + + std::string currentDirectory = "Library/Meshes"; + std::vector directoryContents; + int selectedFileIndex = -1; + + void deleteSelectedFile(); +}; + + +#endif // FILEEXPLORER_H \ No newline at end of file diff --git a/sdl2_simple_example/sdl2_simple_example/FilesystemUtils.h b/sdl2_simple_example/sdl2_simple_example/FilesystemUtils.h new file mode 100644 index 0000000..56a9cef --- /dev/null +++ b/sdl2_simple_example/sdl2_simple_example/FilesystemUtils.h @@ -0,0 +1,69 @@ +#pragma once + +#include +#include + +namespace fs = std::filesystem; + +class FileSystemUtils { +public: + // Crear directorios necesarios + static void GenerateRequiredDirectories() { + const std::vector dirs = { + "Assets", + "Library/Meshes", + "Library/Materials", + "Library/Models", + "Library/Textures" + }; + + for (const auto& dir : dirs) { + try { + if (!fs::exists(dir)) { + fs::create_directories(dir); + } + } + catch (const fs::filesystem_error& e) { + std::cerr << "Error al crear directorio " << dir << ": " << e.what() << std::endl; + } + } + } + + // Obtener nombre del archivo a partir de su ruta + static std::string getFileName(const std::string& filePath) { + try { + return fs::path(filePath).filename().string(); + } + catch (const fs::filesystem_error& e) { + std::cerr << "Error al obtener el nombre del archivo: " << e.what() << std::endl; + return ""; + } + } + + // Listar todos los archivos y carpetas dentro de un directorio + static std::vector ListDirectory(const std::string& directoryPath) { + std::vector entries; + try { + if (fs::exists(directoryPath) && fs::is_directory(directoryPath)) { + for (const auto& entry : fs::directory_iterator(directoryPath)) { + entries.push_back(entry.path().generic_string()); + } + } + } + catch (const fs::filesystem_error& e) { + std::cerr << "Error al listar el directorio " << directoryPath << ": " << e.what() << std::endl; + } + return entries; + } + + // Verificar si un archivo existe + static bool FileExists(const std::string& filePath) { + try { + return fs::exists(filePath); + } + catch (const fs::filesystem_error& e) { + std::cerr << "Error al verificar existencia de archivo: " << e.what() << std::endl; + return false; + } + } +}; diff --git a/sdl2_simple_example/sdl2_simple_example/GameObject.cpp b/sdl2_simple_example/sdl2_simple_example/GameObject.cpp new file mode 100644 index 0000000..fae217b --- /dev/null +++ b/sdl2_simple_example/sdl2_simple_example/GameObject.cpp @@ -0,0 +1,152 @@ +#include "GameObject.h" +#include +#include +#include + +GameObject::GameObject(Mesh* mesh, TextureData* texture) + : mesh(mesh), texture(texture), parent(nullptr) {} + +GameObject::~GameObject() { + // Desvinculamos todos los hijos antes de destruir el objeto + for (auto* child : children) { + child->parent = nullptr; + } + + // Si tenemos un padre, nos removemos de su lista de hijos + if (parent) { + parent->removeChild(this); + } + delete mesh; + if (texture) delete texture; +} + +void GameObject::setTexture(TextureData* newTexture) { + texture = newTexture; +} + + +void GameObject::setTransform(const Transform& newTransform) { + transform = newTransform; +} +bool GameObject::isChildOf(GameObject* potentialParent) const { + if (!potentialParent) return false; + + GameObject* current = parent; + while (current) { + if (current == potentialParent) return true; + current = current->getParent(); + } + return false; +} +void GameObject::setParent(GameObject* newParent) { + // Si ya teníamos un padre, nos removemos de su lista de hijos + if (parent) { + parent->removeChild(this); + } + + parent = newParent; + + // Nos agregamos a la lista de hijos del nuevo padre + if (parent) { + parent->addChild(this); + } +} + +void GameObject::addChild(GameObject* child) { + if (child && std::find(children.begin(), children.end(), child) == children.end()) { + children.push_back(child); + if (child->parent != this) { + child->setParent(this); + } + } +} + +void GameObject::removeChild(GameObject* child) { + auto it = std::find(children.begin(), children.end(), child); + if (it != children.end()) { + children.erase(it); + if (child->parent == this) { + child->parent = nullptr; + } + } +} + +Transform GameObject::getGlobalTransform() const { + glm::mat4 localMatrix = glm::mat4(1.0f); + localMatrix = glm::translate(localMatrix, glm::vec3(transform.position.x, transform.position.y, transform.position.z)); + localMatrix = glm::rotate(localMatrix, glm::radians(transform.rotation.x), glm::vec3(1.0f, 0.0f, 0.0f)); + localMatrix = glm::rotate(localMatrix, glm::radians(transform.rotation.y), glm::vec3(0.0f, 1.0f, 0.0f)); + localMatrix = glm::rotate(localMatrix, glm::radians(transform.rotation.z), glm::vec3(0.0f, 0.0f, 1.0f)); + localMatrix = glm::scale(localMatrix, glm::vec3(transform.scale.x, transform.scale.y, transform.scale.z)); + + // Multiply with parent's matrix if exists + if (parent) { + glm::mat4 parentMatrix = glm::mat4(1.0f); + Transform parentTransform = parent->getGlobalTransform(); + parentMatrix = glm::translate(parentMatrix, glm::vec3(parentTransform.position.x, parentTransform.position.y, parentTransform.position.z)); + parentMatrix = glm::rotate(parentMatrix, glm::radians(parentTransform.rotation.x), glm::vec3(1.0f, 0.0f, 0.0f)); + parentMatrix = glm::rotate(parentMatrix, glm::radians(parentTransform.rotation.y), glm::vec3(0.0f, 1.0f, 0.0f)); + parentMatrix = glm::rotate(parentMatrix, glm::radians(parentTransform.rotation.z), glm::vec3(0.0f, 0.0f, 1.0f)); + parentMatrix = glm::scale(parentMatrix, glm::vec3(parentTransform.scale.x, parentTransform.scale.y, parentTransform.scale.z)); + + localMatrix = parentMatrix * localMatrix; + } + + Transform globalTransform; + + // Extract position + glm::vec3 position = glm::vec3(localMatrix[3]); + globalTransform.position.x = position.x; + globalTransform.position.y = position.y; + globalTransform.position.z = position.z; + + // Extract rotation + glm::vec3 rotation; + rotation.x = glm::degrees(atan2(localMatrix[1][2], localMatrix[2][2])); + rotation.y = glm::degrees(asin(-localMatrix[0][2])); + rotation.z = glm::degrees(atan2(localMatrix[0][1], localMatrix[0][0])); + globalTransform.rotation.x = rotation.x; + globalTransform.rotation.y = rotation.y; + globalTransform.rotation.z = rotation.z; + + // Extract scale + globalTransform.scale.x = glm::length(glm::vec3(localMatrix[0])); + globalTransform.scale.y = glm::length(glm::vec3(localMatrix[1])); + globalTransform.scale.z = glm::length(glm::vec3(localMatrix[2])); + + return globalTransform; +} + +void GameObject::draw() const { + glPushMatrix(); // Save the current matrix + + Transform globalTransform = getGlobalTransform(); + // Apply transformations based on the GameObject's transform + glTranslatef(globalTransform.position.x, globalTransform.position.y, globalTransform.position.z); + glRotatef(globalTransform.rotation.x, 1.0f, 0.0f, 0.0f); + glRotatef(globalTransform.rotation.y, 0.0f, 1.0f, 0.0f); + glRotatef(globalTransform.rotation.z, 0.0f, 0.0f, 1.0f); + glScalef(globalTransform.scale.x, globalTransform.scale.y, globalTransform.scale.z); + + // Enable textures if there is a texture bound + if (texture) { + glEnable(GL_TEXTURE_2D); + texture->bind(); // Bind the object's texture + } + else { + glDisable(GL_TEXTURE_2D); // Disable if no texture + } + + if (mesh) mesh->render(); // Render the mesh + + // Unbind and disable texture after rendering to prevent interference with other objects + if (texture) { + texture->unbind(); + glDisable(GL_TEXTURE_2D); + } + // Dibujar todos los hijos + for (const auto* child : children) { + child->draw(); + } + glPopMatrix(); // Restore the previous matrix +} \ No newline at end of file diff --git a/sdl2_simple_example/sdl2_simple_example/GameObject.h b/sdl2_simple_example/sdl2_simple_example/GameObject.h new file mode 100644 index 0000000..e0af0fb --- /dev/null +++ b/sdl2_simple_example/sdl2_simple_example/GameObject.h @@ -0,0 +1,32 @@ +#pragma once +#include "Mesh.h" +#include "Texture.h" +#include "Transform.h" +#include + +class GameObject { +public: + GameObject(Mesh* mesh, TextureData* texture); + ~GameObject(); + + void draw() const; + void setTexture(TextureData* texture); + void setParent(GameObject* newParent); + void addChild(GameObject* child); + void removeChild(GameObject* child); + + Transform getGlobalTransform() const; + void setTransform(const Transform& newTransform); + bool isChildOf(GameObject* potentialParent) const; + + TextureData* getTexture() const { return texture; } + Mesh* getMesh() const { return mesh; } + GameObject* getParent() const { return parent; } + const std::vector& getChildren() const { return children; } + + Mesh* mesh; + TextureData* texture; + Transform transform; + GameObject* parent; + std::vector children; +}; \ No newline at end of file diff --git a/sdl2_simple_example/sdl2_simple_example/Importer.cpp b/sdl2_simple_example/sdl2_simple_example/Importer.cpp new file mode 100644 index 0000000..00eb440 --- /dev/null +++ b/sdl2_simple_example/sdl2_simple_example/Importer.cpp @@ -0,0 +1,182 @@ +#include "Importer.h" +#include "Texture.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using vec3 = glm::dvec3; + +float modelScale = 1.0f; // Global scaling factor for the model +vec3 modelCenter(0.0f); // Model center for positioning + +Importer::Importer() { + ilInit(); + iluInit(); + ilutRenderer(ILUT_OPENGL); +} + +Importer::~Importer() { + if (textureID) { + glDeleteTextures(1, &textureID); + } +} + +void Importer::setWindow(MyWindow* window) { + _window = window; +} + +void centerModel(const aiScene* scene) { + if (scene->mNumMeshes == 0) return; + + aiVector3D minimo(FLT_MAX, FLT_MAX, FLT_MAX); + aiVector3D maximo(-FLT_MAX, -FLT_MAX, -FLT_MAX); + + for (unsigned int i = 0; i < scene->mNumMeshes; i++) { + for (unsigned int v = 0; v < scene->mMeshes[i]->mNumVertices; v++) { + aiVector3D vertex = scene->mMeshes[i]->mVertices[v]; + minimo.x = min(minimo.x, vertex.x); + minimo.y = min(minimo.y, vertex.y); + minimo.z = min(minimo.z, vertex.z); + maximo.x = max(maximo.x, vertex.x); + maximo.y = max(maximo.y, vertex.y); + maximo.z = max(maximo.z, vertex.z); + } + } + + modelCenter = vec3((minimo.x + maximo.x) / 2, (minimo.y + maximo.y) / 2, (minimo.z + maximo.z) / 2); + modelScale = 2.0f / glm::length(vec3(maximo.x - minimo.x, maximo.y - minimo.y, maximo.z - minimo.z)); +} + +// Dentro de la clase Importer, agrega un método para crear una textura checker +GLuint Importer::GenerateCheckerTexture() { + const int width = 64; + const int height = 64; + std::vector checkerImage(width * height * 4); + + // Generar el patrón checker + for (int y = 0; y < height; ++y) { + for (int x = 0; x < width; ++x) { + int c = ((x / 8) % 2) == ((y / 8) % 2) ? 255 : 0; + checkerImage[(y * width + x) * 4 + 0] = (GLubyte)c; + checkerImage[(y * width + x) * 4 + 1] = (GLubyte)c; + checkerImage[(y * width + x) * 4 + 2] = (GLubyte)c; + checkerImage[(y * width + x) * 4 + 3] = 255; // Alpha + } + } + + static GLuint checkerTexture = 2; + glBindTexture(GL_TEXTURE_2D, checkerTexture); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, checkerImage.data()); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + + return checkerTexture; +} + +bool Importer::loadFBX(const std::string& filePath) { + _window->logMessage("Attempting to load FBX file: " + filePath); // Log de carga + + Assimp::Importer importer; + const aiScene* scene = importer.ReadFile(filePath, aiProcess_Triangulate | aiProcess_GenUVCoords | aiProcess_CalcTangentSpace); + + if (!scene) { + _window->logMessage("Error loading FBX: " + std::string(importer.GetErrorString())); + return false; + } + + _window->logMessage("Successfully loaded FBX file: " + filePath); + _window->logMessage("Total meshes in scene: " + std::to_string(scene->mNumMeshes)); + + // Centrado y escalado del modelo + centerModel(scene); + + // Limpiar los contenedores para evitar que se mezclen datos de diferentes cargas + vertices.clear(); + uvs.clear(); + indices.clear(); + + // Procesar cada malla en el archivo FBX + for (unsigned int i = 0; i < scene->mNumMeshes; i++) { + aiMesh* mesh = scene->mMeshes[i]; + _window->logMessage("Processing mesh: " + std::to_string(i) + " with " + + std::to_string(mesh->mNumVertices) + " vertices and " + + std::to_string(mesh->mNumFaces) + " faces."); + + unsigned int baseVertex = vertices.size() / 3; // Offset para los índices de cada malla + + // Añadir vértices y coordenadas UV de la malla actual + for (unsigned int v = 0; v < mesh->mNumVertices; v++) { + aiVector3D vertex = mesh->mVertices[v]; + vertices.push_back(vertex.x); + vertices.push_back(vertex.y); + vertices.push_back(vertex.z); + + if (mesh->HasTextureCoords(0)) { + aiVector3D texCoord = mesh->mTextureCoords[0][v]; + uvs.push_back(texCoord.x); + uvs.push_back(1.0f - texCoord.y); + } + else { + uvs.push_back(0.0f); + uvs.push_back(0.0f); + } + } + + // Añadir índices de la malla actual + for (unsigned int f = 0; f < mesh->mNumFaces; f++) { + aiFace face = mesh->mFaces[f]; + for (unsigned int j = 0; j < face.mNumIndices; j++) { + indices.push_back(baseVertex + face.mIndices[j]); // Ajuste para índice base + } + } + } + + return true; +} + +TextureData* Importer::loadTexture(const std::string& texturePath) { + ILuint imageID; + ilGenImages(1, &imageID); + ilBindImage(imageID); + + if (!ilLoadImage((const wchar_t*)texturePath.c_str())) { // Cambia a ilLoadImage((const wchar_t*)texturePath.c_str()) en Windows si necesitas soporte para rutas UTF-16 + ilDeleteImages(1, &imageID); + std::cerr << "Failed to load texture from: " << texturePath << std::endl; + return 0; // Retorna 0 si falla la carga + } + + // Convertir la imagen a formato RGBA para OpenGL + ilConvertImage(IL_RGBA, IL_UNSIGNED_BYTE); + + // Obtener dimensiones + int width = ilGetInteger(IL_IMAGE_WIDTH); + int height = ilGetInteger(IL_IMAGE_HEIGHT); + + GLuint textureID; + glGenTextures(1, &textureID); + glBindTexture(GL_TEXTURE_2D, textureID); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, ilGetData()); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + + ilDeleteImages(1, &imageID); // Limpia la imagen DevIL + + // Retorna la textura cargada con ID, path, ancho y alto + TextureData* texture = new TextureData(textureID, texturePath, width, height); + return texture; +} + +// Método para obtener la textura cargada o checker +GLuint Importer::getTextureID() const { + return textureID; +} \ No newline at end of file diff --git a/sdl2_simple_example/sdl2_simple_example/Importer.h b/sdl2_simple_example/sdl2_simple_example/Importer.h new file mode 100644 index 0000000..ae249d0 --- /dev/null +++ b/sdl2_simple_example/sdl2_simple_example/Importer.h @@ -0,0 +1,34 @@ +#pragma once + +#include +#include +#include +#include "glm/glm.hpp" +#include "MyWindow.h" +#include "Texture.h" + +class Importer { +public: + Importer(); + ~Importer(); + + bool loadFBX(const std::string& filePath); + TextureData* loadTexture(const std::string& texturePath); + const std::vector& getVertices() const { return vertices; } + const std::vector& getUVs() const { return uvs; } + const std::vector& getIndices() const { return indices; } + GLuint getTextureID() { return textureID; } + + GLuint GenerateCheckerTexture(); + GLuint getTextureID() const; + + void setWindow(MyWindow* window); + +private: + std::vector vertices; + std::vector uvs; + std::vector indices; + GLuint textureID = 0; + + MyWindow* _window = nullptr; +}; \ No newline at end of file diff --git a/sdl2_simple_example/sdl2_simple_example/Inspector.cpp b/sdl2_simple_example/sdl2_simple_example/Inspector.cpp new file mode 100644 index 0000000..e401541 --- /dev/null +++ b/sdl2_simple_example/sdl2_simple_example/Inspector.cpp @@ -0,0 +1,112 @@ +#include "Inspector.h" + +extern Scene scene; + +Inspector::Inspector() {} +Inspector::~Inspector() {} + +static TextureData* originalTexture = nullptr; + +void Inspector::draw() { + ImGui::Begin("Inspector"); + + if (scene.selectedGameObjectIndex >= 0 && scene.selectedGameObjectIndex < scene.gameObjects.size()) { + GameObject* selectedObject = scene.gameObjects[scene.selectedGameObjectIndex]; + if (!selectedObject) { + ImGui::End(); + return; + } + + ImGui::Text("Texture"); + + // Safe texture handling + if (selectedObject->texture) { + if (selectedObject->texture->id != 2 && !originalTexture) { + originalTexture = selectedObject->texture; + } + + if (ImGui::Button("Off")) { + if (!chekerOn && scene.checkerTextureID != 0) { + selectedObject->setTexture(new TextureData(scene.checkerTextureID)); + chekerOn = true; + } + } + ImGui::SameLine(); + if (ImGui::Button("On")) { + if (chekerOn && originalTexture) { + selectedObject->setTexture(originalTexture); + chekerOn = false; + } + } + + // Display texture information + GLuint textureID = selectedObject->texture->getTextureID(); + ImGui::Text("Texture ID: %u", textureID); + + if (selectedObject->texture->getHeight() && selectedObject->texture->getWidth()) { + GLuint GetHeight = selectedObject->texture->getHeight(); + GLuint GetWidth = selectedObject->texture->getWidth(); + ImGui::Text("Texture Shape: %u x %u", GetHeight, GetWidth); + } + else { + ImGui::Text("Texture Shape: None"); + } + ImGui::Text("Texture path: %s", selectedObject->texture->getPath().c_str()); + } + else { + ImGui::Text("No texture assigned"); + } + + // Transform controls + float position[3] = { selectedObject->transform.position.x, selectedObject->transform.position.y, selectedObject->transform.position.z }; + if (ImGui::InputFloat3("Posicion", position)) { + selectedObject->transform.position = { position[0], position[1], position[2] }; + } + + float rotation[3] = { selectedObject->transform.rotation.x, selectedObject->transform.rotation.y, selectedObject->transform.rotation.z }; + if (ImGui::InputFloat3("Rotacion", rotation)) { + selectedObject->transform.rotation = { rotation[0], rotation[1], rotation[2] }; + } + + float scale[3] = { selectedObject->transform.scale.x, selectedObject->transform.scale.y, selectedObject->transform.scale.z }; + if (ImGui::InputFloat3("Escala", scale)) { + selectedObject->transform.scale = { scale[0], scale[1], scale[2] }; + } + + // Mesh information + if (Mesh* mesh = selectedObject->getMesh()) { + const std::vector& vertices = mesh->getVertices(); + const std::vector& uvs = mesh->getUVs(); + const std::vector& indices = mesh->getIndices(); + + ImGui::Text("Informacion de la Malla:"); + ImGui::Text("Vertices: %zu", vertices.size()); + ImGui::Text("UVs: %zu", uvs.size()); + ImGui::Text("Indices: %zu", indices.size()); + + ImGui::Separator(); + ImGui::Text("Vista previa de vertices:"); + for (size_t i = 0; i < std::min(vertices.size(), 9); i += 3) { + ImGui::Text("(%f, %f, %f)", vertices[i], vertices[i + 1], vertices[i + 2]); + } + + ImGui::Text("Vista previa de UVs:"); + for (size_t i = 0; i < std::min(uvs.size(), 6); i += 2) { + ImGui::Text("(%f, %f)", uvs[i], uvs[i + 1]); + } + + ImGui::Text("Vista previa de indices:"); + for (size_t i = 0; i < std::min(indices.size(), 9); i += 3) { + ImGui::Text("(%u, %u, %u)", indices[i], indices[i + 1], indices[i + 2]); + } + } + else { + ImGui::Text("Malla: Ninguna"); + } + } + else { + ImGui::Text("Seleccione un GameObject de la jerarquia para ver sus propiedades."); + } + + ImGui::End(); +} \ No newline at end of file diff --git a/sdl2_simple_example/sdl2_simple_example/Inspector.h b/sdl2_simple_example/sdl2_simple_example/Inspector.h new file mode 100644 index 0000000..e6c9c54 --- /dev/null +++ b/sdl2_simple_example/sdl2_simple_example/Inspector.h @@ -0,0 +1,19 @@ +#pragma once +#ifndef INSPECTOR_H +#define INSPECTOR_H + +#include "MyWindow.h" +#include "imgui.h" + +class Inspector { +public: + Inspector(); + ~Inspector(); + void draw(); +private: + bool chekerOn = false; + bool saved = false; + +}; + +#endif // INSPECTOR_H \ No newline at end of file diff --git a/sdl2_simple_example/sdl2_simple_example/Jerarquia.cpp b/sdl2_simple_example/sdl2_simple_example/Jerarquia.cpp new file mode 100644 index 0000000..043b6e0 --- /dev/null +++ b/sdl2_simple_example/sdl2_simple_example/Jerarquia.cpp @@ -0,0 +1,106 @@ +#include "Jerarquia.h" + +extern Scene scene; + +Jerarquia::Jerarquia() {} +Jerarquia::~Jerarquia() {} + +void Jerarquia::drawGameObjectNode(GameObject* obj, const std::string& name, int index) { + if (!obj) return; + + ImGuiTreeNodeFlags flags = ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick; + + if (scene.selectedGameObjectIndex == index) { + flags |= ImGuiTreeNodeFlags_Selected; + } + + if (obj->getChildren().empty()) { + flags |= ImGuiTreeNodeFlags_Leaf; + } + + bool nodeOpen = ImGui::TreeNodeEx((name + "###" + std::to_string(index)).c_str(), flags); + + if (ImGui::IsItemClicked()) { + scene.selectedGameObjectIndex = index; + } + + // Handle delete with Delete key + if (scene.selectedGameObjectIndex == index && ImGui::IsKeyPressed(ImGuiKey_Delete)) { + scene.deleteGameObjectHierarchy(obj); + if (nodeOpen) ImGui::TreePop(); + return; + } + + if (ImGui::BeginDragDropSource(ImGuiDragDropFlags_None)) { + int draggedIndex = index; + ImGui::SetDragDropPayload("GAMEOBJECT_DND", &draggedIndex, sizeof(int)); + ImGui::Text("Arrastrando: %s", name.c_str()); + ImGui::EndDragDropSource(); + } + + if (ImGui::BeginDragDropTarget()) { + if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload("GAMEOBJECT_DND")) { + int droppedIndex = *(const int*)payload->Data; + if (droppedIndex != index) { + if (scene.setParentChild(droppedIndex, index)) { + printf("Objeto %s ahora es hijo de %s\n", + scene.gameObjectNames[droppedIndex].c_str(), + scene.gameObjectNames[index].c_str()); + } + } + } + ImGui::EndDragDropTarget(); + } + + if (nodeOpen) { + auto& children = obj->getChildren(); + for (GameObject* child : children) { + auto it = std::find(scene.gameObjects.begin(), scene.gameObjects.end(), child); + if (it != scene.gameObjects.end()) { + int childIndex = std::distance(scene.gameObjects.begin(), it); + drawGameObjectNode(child, scene.gameObjectNames[childIndex], childIndex); + } + } + ImGui::TreePop(); + } +} + +void Jerarquia::draw() { + ImGui::Begin("Jerarquía"); + + // Add "Create Empty" button + if (ImGui::Button("Create Empty")) { + GameObject* emptyObj = new GameObject(nullptr, nullptr); + scene.gameObjects.push_back(emptyObj); + scene.gameObjectNames.push_back("Empty Object " + std::to_string(scene.gameObjects.size())); + } + + ImGui::Text("Lista de GameObjects:"); + + ImGui::BeginChild("GameObjectList", ImVec2(0, -30), true); + + for (size_t i = 0; i < scene.gameObjects.size(); ++i) { + GameObject* obj = scene.gameObjects[i]; + if (!obj->getParent()) { + drawGameObjectNode(obj, scene.gameObjectNames[i], i); + } + } + ImGui::EndChild(); + + ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(0.15f, 0.15f, 0.15f, 1.0f)); + ImGui::BeginChild("DropZone", ImVec2(0, 25), true); + ImGui::Text("Desvincular padre"); + + if (ImGui::BeginDragDropTarget()) { + if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload("GAMEOBJECT_DND")) { + int droppedIndex = *(const int*)payload->Data; + scene.removeFromParent(droppedIndex); + } + ImGui::EndDragDropTarget(); + } + + ImGui::EndChild(); + ImGui::PopStyleColor(); + + ImGui::End(); +} \ No newline at end of file diff --git a/sdl2_simple_example/sdl2_simple_example/Jerarquia.h b/sdl2_simple_example/sdl2_simple_example/Jerarquia.h new file mode 100644 index 0000000..4bfb01f --- /dev/null +++ b/sdl2_simple_example/sdl2_simple_example/Jerarquia.h @@ -0,0 +1,17 @@ +#pragma once +#ifndef JERARQUIA_H +#define JERARQUIA_H + +#include +#include "MyWindow.h" +#include "Scene.h" +class Jerarquia { +public: + Jerarquia(); + ~Jerarquia(); + static void draw(); +private: + static void drawGameObjectNode(GameObject* obj, const std::string& name, int index); +}; + +#endif // JERARQUIA_H diff --git a/sdl2_simple_example/sdl2_simple_example/Library/Meshes/BakerHouse.fbx b/sdl2_simple_example/sdl2_simple_example/Library/Meshes/BakerHouse.fbx new file mode 100644 index 0000000..b6e6721 Binary files /dev/null and b/sdl2_simple_example/sdl2_simple_example/Library/Meshes/BakerHouse.fbx differ diff --git a/sdl2_simple_example/sdl2_simple_example/Library/Meshes/cube.fbx b/sdl2_simple_example/sdl2_simple_example/Library/Meshes/cube.fbx new file mode 100644 index 0000000..3de70b0 Binary files /dev/null and b/sdl2_simple_example/sdl2_simple_example/Library/Meshes/cube.fbx differ diff --git a/sdl2_simple_example/sdl2_simple_example/Library/Models/model_data.bin b/sdl2_simple_example/sdl2_simple_example/Library/Models/model_data.bin new file mode 100644 index 0000000..ae89332 Binary files /dev/null and b/sdl2_simple_example/sdl2_simple_example/Library/Models/model_data.bin differ diff --git a/sdl2_simple_example/sdl2_simple_example/Library/Textures/Baker_house.png b/sdl2_simple_example/sdl2_simple_example/Library/Textures/Baker_house.png new file mode 100644 index 0000000..415732b Binary files /dev/null and b/sdl2_simple_example/sdl2_simple_example/Library/Textures/Baker_house.png differ diff --git a/sdl2_simple_example/sdl2_simple_example/MemoryUsage.h b/sdl2_simple_example/sdl2_simple_example/MemoryUsage.h new file mode 100644 index 0000000..399719d --- /dev/null +++ b/sdl2_simple_example/sdl2_simple_example/MemoryUsage.h @@ -0,0 +1,25 @@ +#pragma once +#include +#include + +struct MemoryInfo { + unsigned long long totalMemory; + unsigned long long freeMemory; + unsigned long long usedMemory; +}; + +class MemoryUsage { +public: + static MemoryInfo getMemoryInfo() { + MEMORYSTATUSEX memInfo; + memInfo.dwLength = sizeof(MEMORYSTATUSEX); + + if (GlobalMemoryStatusEx(&memInfo)) { + MemoryInfo info; + info.totalMemory = memInfo.ullTotalPhys / (1024 * 1024); // Convertir a MB + info.freeMemory = memInfo.ullAvailPhys / (1024 * 1024); // Convertir a MB + info.usedMemory = info.totalMemory - info.freeMemory; // Calcular memoria usada + return info; + } + } +}; \ No newline at end of file diff --git a/sdl2_simple_example/sdl2_simple_example/Mesh.cpp b/sdl2_simple_example/sdl2_simple_example/Mesh.cpp new file mode 100644 index 0000000..d1662ec --- /dev/null +++ b/sdl2_simple_example/sdl2_simple_example/Mesh.cpp @@ -0,0 +1,61 @@ +#include "Mesh.h" + +Mesh::Mesh(const std::vector& vertices, const std::vector& uvs, const std::vector& indices) + : vertices(vertices), uvs(uvs), indices(indices) { + setupMesh(); +} + +Mesh::~Mesh() { + glDeleteVertexArrays(1, &vao); + glDeleteBuffers(1, &vboVertices); + glDeleteBuffers(1, &vboUVs); + glDeleteBuffers(1, &ebo); +} + +void Mesh::setupMesh() { + glGenVertexArrays(1, &vao); + glGenBuffers(1, &vboVertices); + glGenBuffers(1, &vboUVs); + glGenBuffers(1, &ebo); + + glBindVertexArray(vao); + + glBindBuffer(GL_ARRAY_BUFFER, vboVertices); + glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(float), vertices.data(), GL_STATIC_DRAW); + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0); + glEnableVertexAttribArray(0); + + glBindBuffer(GL_ARRAY_BUFFER, vboUVs); + glBufferData(GL_ARRAY_BUFFER, uvs.size() * sizeof(float), uvs.data(), GL_STATIC_DRAW); + glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), (void*)0); + glEnableVertexAttribArray(1); + + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(unsigned int), indices.data(), GL_STATIC_DRAW); + + glBindVertexArray(0); +} + +void Mesh::render() const { + glBindVertexArray(vao); + + glEnableClientState(GL_VERTEX_ARRAY); + glBindBuffer(GL_ARRAY_BUFFER, vboVertices); + glVertexPointer(3, GL_FLOAT, 0, nullptr); + + if (!uvs.empty()) { + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + glBindBuffer(GL_ARRAY_BUFFER, vboUVs); + glTexCoordPointer(2, GL_FLOAT, 0, nullptr); + } + + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo); + glDrawElements(GL_TRIANGLES, indices.size(), GL_UNSIGNED_INT, nullptr); + + glDisableClientState(GL_VERTEX_ARRAY); + if (!uvs.empty()) { + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + } + + glBindVertexArray(0); +} \ No newline at end of file diff --git a/sdl2_simple_example/sdl2_simple_example/Mesh.h b/sdl2_simple_example/sdl2_simple_example/Mesh.h new file mode 100644 index 0000000..03c8d65 --- /dev/null +++ b/sdl2_simple_example/sdl2_simple_example/Mesh.h @@ -0,0 +1,22 @@ +#pragma once +#include +#include + +class Mesh { +public: + std::vector vertices; + std::vector uvs; + std::vector indices; + GLuint vao, vboVertices, vboUVs, ebo; + + Mesh(const std::vector& vertices, const std::vector& uvs, const std::vector& indices); + ~Mesh(); + + void setupMesh(); + void render() const; + + // Getter methods for mesh information + const std::vector& getVertices() const { return vertices; } + const std::vector& getUVs() const { return uvs; } + const std::vector& getIndices() const { return indices; } +}; \ No newline at end of file diff --git a/sdl2_simple_example/sdl2_simple_example/MyWindow.cpp b/sdl2_simple_example/sdl2_simple_example/MyWindow.cpp index 917af88..a45d9ac 100644 --- a/sdl2_simple_example/sdl2_simple_example/MyWindow.cpp +++ b/sdl2_simple_example/sdl2_simple_example/MyWindow.cpp @@ -1,16 +1,84 @@ -#include -#include -#include -#include + #include "MyWindow.h" +#include + +#include "imgui.h" +#include "imgui_impl_sdl2.h" +#include "imgui_impl_opengl3.h" +#include + +#include "Consola.h" +#include "Config.h" +#include "Jerarquia.h" +#include "Escena.h" +#include "Inspector.h" +#include "FileExplorer.h" + using namespace std; +extern Scene scene; +Renderer renderer; +ImGuiIO* g_io = nullptr; +Inspector inspector; +Explorer explorer; + +float x = Renderer::eyeX; +float y = Renderer::eyeY; +float z = Renderer::eyeZ; +bool chekerOn = false; + +MyWindow::MyWindow(const char* title, unsigned short width, unsigned short height) : fps(0.0f), frameCount(0), lastTime(SDL_GetTicks()) { + SDL_Init(SDL_INIT_VIDEO); // Mueve esta l�nea al principio del constructor + open(title, width, height); + + ImGui::CreateContext(); + + g_io = &ImGui::GetIO(); + g_io->ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable keyboard + g_io->ConfigFlags |= ImGuiConfigFlags_DockingEnable; // Enable docking + g_io->ConfigFlags |= ImGuiConfigFlags_ViewportsEnable; // Enable multiple viewports -MyWindow::MyWindow(const char* title, unsigned short width, unsigned short height){ - open(title, width, height); + ImGui_ImplSDL2_InitForOpenGL(_window, _ctx); + ImGui_ImplOpenGL3_Init("#version 130"); + + // Tema de colores a un estilo lila oscuro cercano al negro + ImGuiStyle& style = ImGui::GetStyle(); + ImVec4* colors = style.Colors; + + colors[ImGuiCol_WindowBg] = ImVec4(0.08f, 0.02f, 0.1f, 0.9f); // Fondo de la ventana + colors[ImGuiCol_TitleBg] = ImVec4(0.18f, 0.05f, 0.2f, 1.0f); // Título de la ventana (sin focus) + colors[ImGuiCol_TitleBgActive] = ImVec4(0.22f, 0.05f, 0.25f, 1.0f); // Título de la ventana (con focus) + colors[ImGuiCol_Border] = ImVec4(0.2f, 0.1f, 0.25f, 1.0f); // Bordes + colors[ImGuiCol_FrameBg] = ImVec4(0.12f, 0.02f, 0.15f, 1.0f); // Fondo de widgets como InputFloat3 + colors[ImGuiCol_FrameBgHovered] = ImVec4(0.2f, 0.1f, 0.25f, 1.0f); // Fondo de widgets cuando están en hover + colors[ImGuiCol_FrameBgActive] = ImVec4(0.25f, 0.1f, 0.3f, 1.0f); // Fondo de widgets cuando están activos + colors[ImGuiCol_Button] = ImVec4(0.15f, 0.05f, 0.2f, 1.0f); // Botones + colors[ImGuiCol_ButtonHovered] = ImVec4(0.2f, 0.1f, 0.25f, 1.0f); // Botones cuando están en hover + colors[ImGuiCol_ButtonActive] = ImVec4(0.25f, 0.1f, 0.3f, 1.0f); // Botones cuando están activos + colors[ImGuiCol_Header] = ImVec4(0.2f, 0.1f, 0.25f, 1.0f); // Cabecera de listas desplegables + colors[ImGuiCol_HeaderHovered] = ImVec4(0.25f, 0.1f, 0.3f, 1.0f); // Cabecera de listas desplegables en hover + colors[ImGuiCol_HeaderActive] = ImVec4(0.3f, 0.15f, 0.35f, 1.0f); // Cabecera de listas desplegables activas + + SDL_EventState(SDL_DROPFILE, SDL_ENABLE); } MyWindow::~MyWindow() { + // Limpieza de ImGui + ImGui_ImplSDL2_Shutdown(); + ImGui_ImplOpenGL3_Shutdown(); + ImGui::DestroyContext(); + + // Liberar recursos OpenGL + glDeleteFramebuffers(1, &framebuffer); + glDeleteTextures(1, &textureColorbuffer); + glDeleteRenderbuffers(1, &rbo); + + // Cerrar ventana y SDL close(); + SDL_Quit(); +} + +void MyWindow::logMessage(const std::string& message) { + logMessages.push_back(message); } void MyWindow::open(const char* title, unsigned short width, unsigned short height) { @@ -20,13 +88,18 @@ void MyWindow::open(const char* title, unsigned short width, unsigned short heig SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0); - _window = SDL_CreateWindow( title, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, width, height, SDL_WINDOW_OPENGL); - if (!_window) throw exception(SDL_GetError()); + _window = SDL_CreateWindow(title, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, width, height, SDL_WINDOW_OPENGL); + if (!_window) { + logMessage("Error al crear ventana SDL."); // Mensaje en caso de error + throw exception(SDL_GetError()); + } _ctx = SDL_GL_CreateContext(_window); - if (!_ctx) throw exception(SDL_GetError()); - if (SDL_GL_MakeCurrent(_window, _ctx) != 0) throw exception(SDL_GetError()); - if (SDL_GL_SetSwapInterval(1) != 0) throw exception(SDL_GetError()); + if (!_ctx) { + logMessage("Error al crear contexto OpenGL."); // Mensaje en caso de error + throw exception(SDL_GetError()); + } + logMessage("Ventana y contexto creados con exito."); } void MyWindow::close() { @@ -36,7 +109,174 @@ void MyWindow::close() { _ctx = nullptr; SDL_DestroyWindow(static_cast(_window)); - _window = nullptr; + _window = nullptr; + + logMessage("Ventana cerrada."); +} + +void MyWindow::setupFramebuffer() { + glGenFramebuffers(1, &framebuffer); + glBindFramebuffer(GL_FRAMEBUFFER, framebuffer); + + glGenTextures(1, &textureColorbuffer); + glBindTexture(GL_TEXTURE_2D, textureColorbuffer); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, renderer._WINDOW_SIZE.x, renderer._WINDOW_SIZE.y, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textureColorbuffer, 0); + + glGenRenderbuffers(1, &rbo); + glBindRenderbuffer(GL_RENDERBUFFER, rbo); + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, renderer._WINDOW_SIZE.x, renderer._WINDOW_SIZE.y); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, rbo); + + if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { + logMessage("Framebuffer no está completo."); + } + + glBindFramebuffer(GL_FRAMEBUFFER, 0); +} + +void MyWindow::renderToFramebuffer() { + glBindFramebuffer(GL_FRAMEBUFFER, framebuffer); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { + logMessage("Framebuffer no está completo."); + return; + } + + renderer.setupView(cameraDistance, cameraAngleX, cameraAngleY, panX, panY); + + renderer.deployGrid(10.0f); // Draw grid first + + scene.drawScene(); + + glBindFramebuffer(GL_FRAMEBUFFER, 0); +} + +void MyWindow::draw() { + Config config(this); + + Uint32 currentTime = SDL_GetTicks(); + frameCount++; + + // Calcular FPS cada segundo + if (currentTime - lastTime >= 1000) { + fps = frameCount / ((currentTime - lastTime) / 1000.0f); + frameCount = 0; + lastTime = currentTime; + } + ImGui_ImplOpenGL3_NewFrame(); + ImGui_ImplSDL2_NewFrame(); + ImGui::NewFrame(); + + // Barra de men� principal + if (ImGui::BeginMainMenuBar()) { + if (ImGui::BeginMenu("Menu")) { + if (ImGui::MenuItem("Salir")) { + SDL_Event quit_event; + quit_event.type = SDL_QUIT; + SDL_PushEvent(&quit_event); + } + if (ImGui::MenuItem("GitHub")) { + SDL_OpenURL("https://github.com/didacpema/Oyuki_Engine.git"); + // Aqu� podr�amos abrir el enlace de GitHub + } + if (ImGui::MenuItem("Sobre el motor")) { + ImGui::OpenPopup("AboutPopup"); + ImGui::Text("Este motor grafico es propiedad de los estudiantes de la UPC"); + } + ImGui::EndMenu(); + } + + if (ImGui::BeginMenu("Editor")) { + if (ImGui::MenuItem("Consola", NULL, isConsolaOn, true)) + { + isConsolaOn = !isConsolaOn; + } + if (ImGui::MenuItem("Config", NULL, isConfigOn, true)) + { + isConfigOn = !isConfigOn; + } + if (ImGui::MenuItem("Jerarquia", NULL, isJerarquiaOn, true)) + { + isJerarquiaOn = !isJerarquiaOn; + } + if (ImGui::MenuItem("Inspector", NULL, isInspectorOn, true)) + { + isInspectorOn = !isInspectorOn; + } + ImGui::EndMenu(); + } + + if (ImGui::BeginMenu("GameObject")) { + if (ImGui::MenuItem("Cube")) { + scene.createCube("Library/Meshes/cube.fbx"); + } + if (ImGui::MenuItem("Sphere")) { + scene.createSphere("Sphere"); + } + ImGui::EndMenu(); + } + ImGui::EndMainMenuBar(); + } + //COMENTA ESTA LINEA PARA SER FELIZ + ImGui::DockSpaceOverViewport(ImGui::GetMainViewport()->ID); + + if (isConsolaOn) + { + Consola::draw(logMessages); + } + + if (isConfigOn) + { + config.draw(_window, renderer, fps); + } + + if (isJerarquiaOn) { + Jerarquia::draw(); + } + + // Si el usuario pulsa 'F', movemos la cámara al objeto seleccionado + if (ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_F)) && scene.selectedGameObjectIndex != -1) { + GameObject* selectedObject = scene.gameObjects[scene.selectedGameObjectIndex]; + panX = selectedObject->transform.position.x; + panY = selectedObject->transform.position.y; + cameraDistance = glm::distance(glm::vec3(x, y, z), selectedObject->transform.position); // Ajuste de distancia + logMessage("Cámara ajustada al objeto seleccionado."); + } + + if (isSceneOn) { + renderToFramebuffer(); // Renderiza la escena al framebuffer + Escena::draw(textureColorbuffer); + } + + if (isInspectorOn) + { + inspector.draw(); + } + + + // Popup "About" + if (ImGui::BeginPopup("AboutPopup")) { + ImGui::Text("Informacion sobre el motor:"); + ImGui::Text("Version: 1.0"); + ImGui::Text("Desarrollado con SDL, OpenGL, ImGui, Assimp, DevIL"); + ImGui::EndPopup(); + } + + explorer.draw(); + + ImGui::Render(); + ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); + + if (g_io->ConfigFlags & ImGuiConfigFlags_ViewportsEnable) { + ImGui::UpdatePlatformWindows(); + ImGui::RenderPlatformWindowsDefault(); + SDL_GL_MakeCurrent(_window, _ctx); + } + } void MyWindow::swapBuffers() const { @@ -44,12 +284,88 @@ void MyWindow::swapBuffers() const { } bool MyWindow::processEvents(IEventProcessor* event_processor) { - SDL_Event e; - while (SDL_PollEvent(&e)) { - if (event_processor) event_processor->processEvent(e); - switch (e.type) { - case SDL_QUIT: close(); return false; - } + SDL_Event e; + static int lastMouseX, lastMouseY; + + while (SDL_PollEvent(&e)) { + // Enviar el evento a ImGui primero + ImGui_ImplSDL2_ProcessEvent(&e); + + if (event_processor) { + event_processor->processEvent(e); // Procesar eventos personalizados + } + + switch (e.type) + { + case SDL_DROPFILE: { + char* droppedFile = e.drop.file; + logMessage("Archivo arrastrado: " + string(droppedFile)); + handleFileDrop(droppedFile); // Tu función para manejar archivos + SDL_free(droppedFile); + break; + } + case SDL_QUIT: + logMessage("Evento de salida recibido."); + close(); + return false; + case SDL_MOUSEMOTION: + if (e.motion.state & SDL_BUTTON_RMASK) { + if (SDL_GetModState() & KMOD_ALT) { + cameraAngleY += e.motion.xrel * 0.005f; + cameraAngleX -= e.motion.yrel * 0.005f; + } + } + break; + case SDL_KEYDOWN: + // Movimiento de cámara existente + if (e.key.keysym.sym == SDLK_w) { + if (e.key.keysym.mod & KMOD_SHIFT) + panY += camSpeed * 2; + else + panY += camSpeed; + } + if (e.key.keysym.sym == SDLK_s) { + if (e.key.keysym.mod & KMOD_SHIFT) + panY -= camSpeed * 2; + else + panY -= camSpeed; + } + if (e.key.keysym.sym == SDLK_a) { + if (e.key.keysym.mod & KMOD_SHIFT) + panX -= camSpeed * 2; + else + panX -= camSpeed; + } + if (e.key.keysym.sym == SDLK_d) { + if (e.key.keysym.mod & KMOD_SHIFT) + panX += camSpeed * 2; + else + panX += camSpeed; + } + + // Nueva funcionalidad para enfocar el objeto seleccionado con tecla 'F' + if (e.key.keysym.sym == SDLK_f) { + // Verifica que haya un objeto seleccionado + if (scene.selectedGameObjectIndex >= 0 && scene.selectedGameObjectIndex < scene.gameObjects.size()) { + GameObject* selectedObject = scene.gameObjects[scene.selectedGameObjectIndex]; + + // Cambia el pivote de la cámara a la posición del objeto seleccionado + panX = selectedObject->transform.position.x; + panY = selectedObject->transform.position.y; + cameraDistance = glm::distance(glm::vec3(x, y, z), selectedObject->transform.position); // Ajuste de distancia + + logMessage("Cámara centrada en el objeto seleccionado."); + } + else { + logMessage("No hay objeto seleccionado."); + } + } + break; + case SDL_MOUSEWHEEL: + cameraDistance += e.wheel.y > 0 ? -0.5f : 0.5f; + cameraDistance = max(1.0f, cameraDistance); + break; + } } return true; } diff --git a/sdl2_simple_example/sdl2_simple_example/MyWindow.h b/sdl2_simple_example/sdl2_simple_example/MyWindow.h index 6d0c010..d174ea5 100644 --- a/sdl2_simple_example/sdl2_simple_example/MyWindow.h +++ b/sdl2_simple_example/sdl2_simple_example/MyWindow.h @@ -1,41 +1,77 @@ #pragma once + +#include "Renderer.h" +#include "Scene.h" + +#include +#include #include #include +#include + +#include +#include + class IEventProcessor { public: - virtual void processEvent(const SDL_Event& event) = 0; + virtual void processEvent(const SDL_Event& event) = 0; }; class MyWindow { + SDL_Window* _window = nullptr; + void* _ctx = nullptr; - SDL_Window* _window = nullptr; - void* _ctx = nullptr; - - unsigned short _width = 800; - unsigned short _height = 600; + unsigned short _width = 800; + unsigned short _height = 600; public: - int width() const { return _width; } - int height() const { return _height; } - double aspect() const { return static_cast(_width) / _height; } + int width() const { return _width; } + int height() const { return _height; } + double aspect() const { return static_cast(_width) / _height; } - auto* windowPtr() const { return _window; } - auto* contextPtr() const { return _ctx; } + float cameraDistance = 5.0f; // Distancia inicial de la cámara (zoom) + float cameraAngleX = 0.0f; // Ángulo de rotación en el eje X + float cameraAngleY = 0.0f; // Ángulo de rotación en el eje Y + float panX = 0.0f, panY = 0.0f; // Desplazamiento de la cámara (pan) + bool isSceneOn = true; + bool isConsolaOn = true; + bool isConfigOn = true; + bool isJerarquiaOn = true; + bool isInspectorOn = true; + float camSpeed = 0.1; + //FPS + float fps = 0.0f; + int frameCount = 0; + Uint32 lastTime = 0; + SDL_Window* windowPtr() const { return _window; } + void* contextPtr() const { return _ctx; } - MyWindow(const char* title, unsigned short width, unsigned short height); - MyWindow(MyWindow&&) noexcept = delete; - MyWindow(const MyWindow&) = delete; - MyWindow& operator=(const MyWindow&) = delete; - ~MyWindow(); + MyWindow(const char* title, unsigned short width, unsigned short height); + MyWindow(MyWindow&&) noexcept = delete; + MyWindow(const MyWindow&) = delete; + MyWindow& operator=(const MyWindow&) = delete; + ~MyWindow(); - void open(const char* title, unsigned short width, unsigned short height); - void close(); - bool isOpen() const { return _window; } + void open(const char* title, unsigned short width, unsigned short height); + void close(); + bool isOpen() const { return _window; } - bool processEvents(IEventProcessor* event_processor = nullptr); - void swapBuffers() const; + bool processEvents(IEventProcessor* event_processor = nullptr); + void draw(); + void swapBuffers() const; + + void setupFramebuffer(); + void renderToFramebuffer(); + + GLuint getFramebufferTexture() const { return textureColorbuffer; } + + void logMessage(const std::string& message); // Método para agregar mensajes al LOG + std::vector logMessages; // Vector de mensajes de LOG + + GLuint framebuffer, textureColorbuffer, rbo; +}; -}; \ No newline at end of file +extern void handleFileDrop(const char* filePath); \ No newline at end of file diff --git a/sdl2_simple_example/sdl2_simple_example/Renderer.cpp b/sdl2_simple_example/sdl2_simple_example/Renderer.cpp new file mode 100644 index 0000000..f4aecce --- /dev/null +++ b/sdl2_simple_example/sdl2_simple_example/Renderer.cpp @@ -0,0 +1,74 @@ +#include "Renderer.h" +#include +#include +#include + +// Define the static member variable +ivec2 Renderer::_WINDOW_SIZE; + +float Renderer::eyeX = 0.0f; // Asignar un valor inicial +float Renderer::eyeY = 0.0f; // Asignar un valor inicial +float Renderer::eyeZ = 0.0f; // Asignar un valor inicial + +void Renderer::initOpenGL(ivec2 WINDOW_SIZE) { + glewInit(); + if (!GLEW_VERSION_3_0) throw std::exception("OpenGL 3.0 API is not available."); + + glEnable(GL_DEPTH_TEST); + + glDepthFunc(GL_LEQUAL); // Set depth function to less or equal + glClearDepth(1.0f); // Clear depth buffer with maximum depth value + + glClearColor(0.0f, 0.0f, 0.0f, 1.0f); + + // Store the window size + Renderer::_WINDOW_SIZE = WINDOW_SIZE; +} + +void Renderer::deployGrid(float spacing) { + glDisable(GL_TEXTURE_2D); // Disable texture for the grid lines + float gridRange = 1000.0f; + + glColor3f(1.0f, 1.0f, 1.0f); // Set grid color + glBegin(GL_LINES); + + for (float i = -gridRange; i <= gridRange; i += spacing) { + glVertex3f(i, 0, -gridRange); + glVertex3f(i, 0, gridRange); + } + + for (float i = -gridRange; i <= gridRange; i += spacing) { + glVertex3f(-gridRange, 0, i); + glVertex3f(gridRange, 0, i); + } + + glEnd(); +} + +void Renderer::setupProjection(float fov, float aspectRatio, float nearPlane, float farPlane) { + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + + float windowAspectRatio = static_cast(Renderer::_WINDOW_SIZE.x) / Renderer::_WINDOW_SIZE.y; + // Use the stored window size for the aspect ratio + gluPerspective(fov, static_cast(Renderer::_WINDOW_SIZE.x) / Renderer::_WINDOW_SIZE.y, nearPlane, farPlane); +} + +void Renderer::setupView(float cameraDistance, float cameraAngleX, float cameraAngleY, float panX, float panY) { + glClearDepth(1.0); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + + // Actualiza las variables miembro con los valores calculados + eyeX = cameraDistance * sin(cameraAngleY) * cos(cameraAngleX) + panX; + eyeY = cameraDistance * sin(cameraAngleX) + panY; + eyeZ = cameraDistance * cos(cameraAngleY) * cos(cameraAngleX); + + gluLookAt(eyeX, eyeY, eyeZ, panX, panY, 0.0, 0.0, -1.0, 0.0); +} + +void Renderer::applyProjectionAndViewport(ivec2 selectedResolution) { + glViewport(0, 0, selectedResolution.x, selectedResolution.y); // Actualiza el viewport + // Asegúrate de usar selectedResolution para calcular el aspecto + setupProjection(45.0f, static_cast(selectedResolution.x) / selectedResolution.y, 0.1f, 1000.0f); +} \ No newline at end of file diff --git a/sdl2_simple_example/sdl2_simple_example/Renderer.h b/sdl2_simple_example/sdl2_simple_example/Renderer.h new file mode 100644 index 0000000..7decd14 --- /dev/null +++ b/sdl2_simple_example/sdl2_simple_example/Renderer.h @@ -0,0 +1,21 @@ +#pragma once +#include +#include +#include "MyWindow.h" +#include + +using ivec2 = glm::ivec2; + +class Renderer { +public: + static void initOpenGL(ivec2 WINDOW_SIZE); + static void setupProjection(float fov, float aspectRatio, float nearPlane, float farPlane); + static void setupView(float cameraDistance, float cameraAngleX, float cameraAngleY, float panX, float panY); + static void deployGrid(float spacing); + void applyProjectionAndViewport(ivec2 selectedResolution); + + // Nuevas variables miembro + static float eyeX, eyeY, eyeZ; + + static ivec2 _WINDOW_SIZE; +}; \ No newline at end of file diff --git a/sdl2_simple_example/sdl2_simple_example/Scene.cpp b/sdl2_simple_example/sdl2_simple_example/Scene.cpp new file mode 100644 index 0000000..41ff887 --- /dev/null +++ b/sdl2_simple_example/sdl2_simple_example/Scene.cpp @@ -0,0 +1,184 @@ +#include "Scene.h" +#include "Importer.h" +#include "FilesystemUtils.h" +#include + +extern Importer importer; + +Scene::Scene() {} + +Scene::~Scene() { + // Solo eliminamos los objetos raíz, ya que eliminarán recursivamente a sus hijos + for (auto it = gameObjects.begin(); it != gameObjects.end();) { + GameObject* obj = *it; + if (!obj->getParent()) { + deleteGameObjectHierarchy(obj); + it = gameObjects.erase(it); + } + else { + ++it; + } + } + gameObjects.clear(); + gameObjectNames.clear(); +} +void Scene::deleteGameObjectHierarchy(GameObject* obj) { + if (!obj) return; + + // Primero eliminamos los hijos recursivamente + auto children = obj->getChildren(); // Hacemos una copia porque se modificará durante la eliminación + for (auto* child : children) { + deleteGameObjectHierarchy(child); + } + + // Eliminamos este objeto de la lista de gameObjects y gameObjectNames + auto it = std::find(gameObjects.begin(), gameObjects.end(), obj); + if (it != gameObjects.end()) { + int index = std::distance(gameObjects.begin(), it); + gameObjects.erase(it); + gameObjectNames.erase(gameObjectNames.begin() + index); + } + + delete obj; +} +void Scene::loadModelData(const std::vector& vertices, const std::vector& uvs, const std::vector& indices, const std::string& name, const Transform& transform) { + Mesh* mesh = new Mesh(vertices, uvs, indices); + GameObject* gameObject = new GameObject(mesh, nullptr); + + gameObject->setTransform(transform); + + gameObjects.push_back(gameObject); + gameObjectNames.push_back(name); +} + +void Scene::setTexture(TextureData* newTexture) { + if (!gameObjects.empty()) { + GameObject* lastObject = gameObjects.back(); + if (lastObject->getTexture() != nullptr) { + delete lastObject->getTexture(); + } + lastObject->setTexture(newTexture); + } +} + +void Scene::setCheckerTexture(GLuint checkerTextureID) { + // Creamos una textura temporal con el checkerTextureID, un path vacío y tamaño predeterminado (0,0) + TextureData* checkerTexture = new TextureData(checkerTextureID, "checker_texture_path", 0, 0); + + // Asignamos esta textura a través de setTexture + setTexture(checkerTexture); +} + +bool Scene::setParentChild(int childIndex, int parentIndex) { + if (!isValidParenting(childIndex, parentIndex)) { + return false; + } + + GameObject* child = gameObjects[childIndex]; + GameObject* newParent = gameObjects[parentIndex]; + + child->setParent(newParent); + return true; +} +void Scene::removeFromParent(int childIndex) { + if (childIndex >= 0 && childIndex < gameObjects.size()) { + GameObject* child = gameObjects[childIndex]; + GameObject* parent = child->getParent(); + if (parent) { + parent->removeChild(child); + child->setParent(nullptr); + } + } +} +bool Scene::isValidParenting(int childIndex, int parentIndex) const { + if (childIndex < 0 || childIndex >= gameObjects.size() || + parentIndex < 0 || parentIndex >= gameObjects.size() || + childIndex == parentIndex) { + return false; + } + + GameObject* child = gameObjects[childIndex]; + GameObject* parent = gameObjects[parentIndex]; + + // Verificar que el padre no sea hijo del hijo (evitar ciclos) + return !parent->isChildOf(child); +} +void Scene::drawScene() { + // Solo dibujamos los objetos raíz, ellos dibujarán a sus hijos + for (auto* obj : gameObjects) { + if (!obj->getParent()) { // Si no tiene padre, es un objeto raíz + if (obj->getTexture() == nullptr && checkerTextureID != 2) { + TextureData* checkerTexture = new TextureData(checkerTextureID, "checker_texture_path", 0, 0); + obj->setTexture(checkerTexture); + } + obj->draw(); + } + } +} + +void Scene::createCube(const char* filePath) { + + std::string fileName = "Cube"; + + std::string path(filePath); + std::string extension = path.substr(path.find_last_of('.') + 1); + + if (importer.loadFBX(filePath)) { + loadModelData(importer.getVertices(), importer.getUVs(), importer.getIndices(), fileName); + + setCheckerTexture(checkerTextureID); + } +} + +void Scene::createSphere(const std::string& name, const Transform& transform) { + + const int sectors = 36; // Divide la esfera en 36 partes (puedes cambiar este número para más resolución) + const int stacks = 18; // Divide la esfera en 18 partes (puedes cambiar este número) + + // Vértices y UVs para la esfera + std::vector vertices; + std::vector uvs; + std::vector indices; + + // Generar la malla esférica (esto se hace con un algoritmo para crear una esfera) + for (int i = 0; i <= stacks; ++i) { + float stackStep = angle / stacks; + float stackAngle = angle / 2 - i * stackStep; + + for (int j = 0; j <= sectors; ++j) { + float sectorAngle = j * (2 * angle / sectors); + + // Coordenadas esféricas convertidas a cartesianas + float x = cos(stackAngle) * cos(sectorAngle); + float y = cos(stackAngle) * sin(sectorAngle); + float z = sin(stackAngle); + + // Añadir vértices + vertices.push_back(x); + vertices.push_back(y); + vertices.push_back(z); + + // Generar coordenadas UV + uvs.push_back((float)j / sectors); + uvs.push_back((float)i / stacks); + } + } + + // Generar índices para el dibujo de las caras + for (int i = 0; i < stacks; ++i) { + for (int j = 0; j < sectors; ++j) { + int first = (i * (sectors + 1)) + j; + int second = first + sectors + 1; + + indices.push_back(first); + indices.push_back(second); + indices.push_back(first + 1); + + indices.push_back(second); + indices.push_back(second + 1); + indices.push_back(first + 1); + } + } + loadModelData(vertices, uvs, indices, name, transform); + setCheckerTexture(checkerTextureID); +} \ No newline at end of file diff --git a/sdl2_simple_example/sdl2_simple_example/Scene.h b/sdl2_simple_example/sdl2_simple_example/Scene.h new file mode 100644 index 0000000..7723b25 --- /dev/null +++ b/sdl2_simple_example/sdl2_simple_example/Scene.h @@ -0,0 +1,35 @@ +#pragma once +#include +#include +#include "GameObject.h" +#include +#include + + +class Scene { +public: + Scene(); + ~Scene(); + void loadModelData(const std::vector& vertices, const std::vector& uvs, const std::vector& indices, const std::string& name, const Transform& transform = Transform()); + void createSphere(const std::string& name, const Transform& transform = Transform()); + void createCube(const char* filePath); + void setTexture(TextureData* newTexture); + void setCheckerTexture(GLuint checkerTextureID); + void drawScene(); + + // Nuevos métodos para manejar la jerarquía + bool setParentChild(int childIndex, int parentIndex); + void removeFromParent(int childIndex); + bool isValidParenting(int childIndex, int parentIndex) const; + + std::vector gameObjects; + std::vector gameObjectNames; + + // Índice del objeto seleccionado (-1 si no hay ninguno) + int selectedGameObjectIndex = -1; + float angle = 3.14159265359f; + GLuint checkerTextureID; + + void deleteGameObjectHierarchy(GameObject* obj); + +}; \ No newline at end of file diff --git a/sdl2_simple_example/sdl2_simple_example/Texture.cpp b/sdl2_simple_example/sdl2_simple_example/Texture.cpp new file mode 100644 index 0000000..768e55c --- /dev/null +++ b/sdl2_simple_example/sdl2_simple_example/Texture.cpp @@ -0,0 +1,9 @@ +#include "Texture.h" + +void TextureData::bind() const { + glBindTexture(GL_TEXTURE_2D, id); +} + +void TextureData::unbind() const { + glBindTexture(GL_TEXTURE_2D, 0); +} \ No newline at end of file diff --git a/sdl2_simple_example/sdl2_simple_example/Texture.h b/sdl2_simple_example/sdl2_simple_example/Texture.h new file mode 100644 index 0000000..de4e896 --- /dev/null +++ b/sdl2_simple_example/sdl2_simple_example/Texture.h @@ -0,0 +1,22 @@ +#pragma once +#include +#include + +class TextureData { +public: + GLuint id; + std::string path; + int width; + int height; + + TextureData(GLuint textureId = 0, const std::string& filePath = "", int texWidth = 0, int texHeight = 0) + : id(textureId), path(filePath), width(texWidth), height(texHeight) {} + + void bind() const; + void unbind() const; + + GLuint getTextureID() const { return id; } + std::string getPath() const { return path; } + int getWidth() const { return width; } + int getHeight() const { return height; } +}; \ No newline at end of file diff --git a/sdl2_simple_example/sdl2_simple_example/Transform.h b/sdl2_simple_example/sdl2_simple_example/Transform.h new file mode 100644 index 0000000..71630d1 --- /dev/null +++ b/sdl2_simple_example/sdl2_simple_example/Transform.h @@ -0,0 +1,11 @@ +#pragma once +#include + +class Transform { +public: + glm::vec3 position; + glm::vec3 rotation; + glm::vec3 scale; + + Transform() : position(0.0f), rotation(0.0f), scale(1.0f) {} +}; \ No newline at end of file diff --git a/sdl2_simple_example/sdl2_simple_example/imgui.ini b/sdl2_simple_example/sdl2_simple_example/imgui.ini new file mode 100644 index 0000000..24ba21c --- /dev/null +++ b/sdl2_simple_example/sdl2_simple_example/imgui.ini @@ -0,0 +1,115 @@ +[Window][Debug##Default] +Pos=60,60 +Size=400,400 +Collapsed=0 + +[Window][Consola] +ViewportPos=730,513 +ViewportId=0xAA02EECC +Size=636,241 +Collapsed=0 +DockId=0x00000009,1 + +[Window][Configuració] +Pos=37,333 +Size=325,273 +Collapsed=0 + +[Window][Jerarquia] +Pos=0,394 +Size=186,113 +Collapsed=0 + +[Window][Inspector] +Pos=590,19 +Size=210,581 +Collapsed=0 +DockId=0x00000008,0 + +[Window][Console] +Pos=28,107 +Size=488,472 +Collapsed=0 + +[Window][Config] +ViewportPos=730,513 +ViewportId=0xAA02EECC +Size=636,241 +Collapsed=0 +DockId=0x00000009,0 + +[Window][Hierarchy] +Pos=176,287 +Size=283,65 +Collapsed=0 + +[Window][WindowOverViewport_11111111] +Pos=0,19 +Size=800,581 +Collapsed=0 + +[Window][Dockable Window 1] +Pos=602,19 +Size=198,581 +Collapsed=0 +DockId=0x00000004,0 + +[Window][Dockable Window 2] +Pos=0,19 +Size=397,581 +Collapsed=0 +DockId=0x00000001,0 + +[Window][Editor Docking Window] +Pos=60,60 +Size=32,39 +Collapsed=0 + +[Window][DockSpace] +Size=800,600 +Collapsed=0 + +[Window][Scene View] +Pos=60,60 +Size=32,35 +Collapsed=0 + +[Window][Jerarquía] +Pos=0,19 +Size=209,581 +Collapsed=0 +DockId=0x00000006,0 + +[Window][Scene] +Pos=164,19 +Size=424,581 +Collapsed=0 +DockId=0x00000005,0 + +[Window][File Explorer] +ViewportPos=730,513 +ViewportId=0xAA02EECC +Size=636,241 +Collapsed=0 +DockId=0x00000009,2 + +[Window][Jerarquía] +Pos=0,19 +Size=162,581 +Collapsed=0 +DockId=0x0000000B,0 + +[Docking][Data] +DockNode ID=0x00000009 Pos=730,513 Size=636,241 Selected=0xCAC6E4C7 +DockSpace ID=0x11111111 Window=0xA87D555D Pos=560,259 Size=800,581 Split=X + DockNode ID=0x0000000B Parent=0x11111111 SizeRef=162,581 Selected=0x06C9255F + DockNode ID=0x0000000C Parent=0x11111111 SizeRef=636,581 Split=X + DockNode ID=0x00000006 Parent=0x0000000C SizeRef=209,581 Selected=0xADB1772B + DockNode ID=0x0000000A Parent=0x0000000C SizeRef=589,581 Split=X + DockNode ID=0x00000003 Parent=0x0000000A SizeRef=600,581 Split=X + DockNode ID=0x00000001 Parent=0x00000003 SizeRef=397,581 Selected=0x29384C3A + DockNode ID=0x00000002 Parent=0x00000003 SizeRef=0,0 Split=X Selected=0x83E87391 + DockNode ID=0x00000005 Parent=0x00000002 SizeRef=424,559 CentralNode=1 Selected=0xE192E354 + DockNode ID=0x00000008 Parent=0x00000002 SizeRef=210,559 Selected=0xE7039252 + DockNode ID=0x00000004 Parent=0x0000000A SizeRef=198,581 Selected=0x6E9836EA + diff --git a/sdl2_simple_example/sdl2_simple_example/main.cpp b/sdl2_simple_example/sdl2_simple_example/main.cpp index ff32d4b..968a0ab 100644 --- a/sdl2_simple_example/sdl2_simple_example/main.cpp +++ b/sdl2_simple_example/sdl2_simple_example/main.cpp @@ -1,54 +1,112 @@ + + +#define SDL_MAIN_HANDLED + #include #include #include #include #include #include "MyWindow.h" -using namespace std; +#include "Importer.h" +#include "customFormatUtils.h" +#include "FilesystemUtils.h" +#include +#include +#include +#include "FileExplorer.h" +using namespace std; using hrclock = chrono::high_resolution_clock; -using u8vec4 = glm::u8vec4; using ivec2 = glm::ivec2; -using vec3 = glm::dvec3; -static const ivec2 WINDOW_SIZE(512, 512); +static const ivec2 WINDOW_SIZE(800, 600); static const unsigned int FPS = 60; static const auto FRAME_DT = 1.0s / FPS; -static void init_openGL() { - glewInit(); - if (!GLEW_VERSION_3_0) throw exception("OpenGL 3.0 API is not available."); - glEnable(GL_DEPTH_TEST); - glClearColor(0.5, 0.5, 0.5, 1.0); -} +Importer importer; +Scene scene; +MyWindow myWindow("SDL2 Simple Example", WINDOW_SIZE.x, WINDOW_SIZE.y); +extern Explorer explorer; -static void draw_triangle(const u8vec4& color, const vec3& center, double size) { - glColor4ub(color.r, color.g, color.b, color.a); - glBegin(GL_TRIANGLES); - glVertex3d(center.x, center.y + size, center.z); - glVertex3d(center.x - size, center.y - size, center.z); - glVertex3d(center.x + size, center.y - size, center.z); - glEnd(); -} +void handleFileDrop(const char* filePath) { + std::string fileName = FileSystemUtils::getFileName(filePath); + + // Procesar extensiones + size_t dotPos = fileName.find_last_of("."); + if (dotPos != std::string::npos) { + fileName = fileName.substr(0, dotPos); + } + std::string path(filePath); + std::string extension = path.substr(path.find_last_of('.') + 1); + + if (extension == "fbx") { + if (importer.loadFBX(filePath)) { + scene.loadModelData(importer.getVertices(), importer.getUVs(), importer.getIndices(), fileName); -static void display_func() { - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - draw_triangle(u8vec4(255, 0, 0, 255), vec3(0.0, 0.0, 0.0), 0.5); + // Configurar textura checker si no está asignada + + scene.checkerTextureID = importer.GenerateCheckerTexture(); + + scene.setCheckerTexture(scene.checkerTextureID); + } + } + else if (extension == "png" || extension == "jpg") { + TextureData* texture = importer.loadTexture(filePath); + if (texture != nullptr) { + scene.setTexture(texture); + } + } + + // Actualizar el contenido del directorio en la interfaz + explorer.UpdateDirectoryContents(); } int main(int argc, char** argv) { - MyWindow window("SDL2 Simple Example", WINDOW_SIZE.x, WINDOW_SIZE.y); - init_openGL(); + FileSystemUtils::GenerateRequiredDirectories(); + + explorer.UpdateDirectoryContents(); // Cargar contenido inicial + importer.setWindow(&myWindow); + + myWindow.logMessage("Initializing SDL..."); + if (SDL_Init(SDL_INIT_VIDEO) != 0) { + myWindow.logMessage("Error initializing SDL: " + std::string(SDL_GetError())); + return -1; + } + myWindow.logMessage("SDL initialized successfully."); + + myWindow.logMessage("Initializing DevIL..."); + ilInit(); + iluInit(); + ilutRenderer(ILUT_OPENGL); + myWindow.logMessage("DevIL initialized successfully."); + + myWindow.logMessage("Initializing OpenGL context..."); + Renderer::initOpenGL(WINDOW_SIZE); + Renderer::setupProjection(45.0f, 1.0f, 0.1f, 1000.0f); + myWindow.logMessage("OpenGL context initialized."); + myWindow.setupFramebuffer(); + myWindow.renderToFramebuffer(); + + SDL_EventState(SDL_DROPFILE, SDL_ENABLE); + + handleFileDrop("Library/Meshes/BakerHouse.fbx"); + handleFileDrop("Library/Textures/Baker_house.png"); + + while (myWindow.processEvents() && myWindow.isOpen()) { + auto start = hrclock::now(); + + + + myWindow.draw(); + myWindow.swapBuffers(); - while(window.processEvents() && window.isOpen()) { - const auto t0 = hrclock::now(); - display_func(); - window.swapBuffers(); - const auto t1 = hrclock::now(); - const auto dt = t1 - t0; - if(dttrue _DEBUG;_CONSOLE;%(PreprocessorDefinitions) true + stdcpp17 Console @@ -127,11 +128,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/sdl2_simple_example/sdl2_simple_example/sdl2_simple_example.vcxproj.filters b/sdl2_simple_example/sdl2_simple_example/sdl2_simple_example.vcxproj.filters index 04a7e88..da064ea 100644 --- a/sdl2_simple_example/sdl2_simple_example/sdl2_simple_example.vcxproj.filters +++ b/sdl2_simple_example/sdl2_simple_example/sdl2_simple_example.vcxproj.filters @@ -13,6 +13,9 @@ {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + {15a2393b-0452-4c45-af67-e7d5dde42e75} + @@ -21,10 +24,94 @@ Source Files + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Windows + + + Windows + + + Windows + + + Windows + + + Windows + + + Source Files + + + Windows + Header Files + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Windows + + + Windows + + + Windows + + + Windows + + + Windows + + + Windows + \ No newline at end of file diff --git a/vcpkg.json b/vcpkg.json index a4d6f3d..61feba4 100644 --- a/vcpkg.json +++ b/vcpkg.json @@ -1,12 +1,4 @@ { "$schema": "https://raw.githubusercontent.com/microsoft/vcpkg-tool/main/docs/vcpkg.schema.json", - "dependencies": [ - "glm", - "glew", - "sdl2", - { - "name": "imgui", - "features": [ "sdl2-binding", "opengl3-binding", "docking-experimental" ] - } - ] + "dependencies": ["glm", "glew", "sdl2","assimp","devil", {"name": "imgui", "features": [ "sdl2-binding", "opengl3-binding", "docking-experimental"]}] } \ No newline at end of file