Skip to content

Instantly share code, notes, and snippets.

@Kinane-dev
Last active August 3, 2025 00:32
Show Gist options
  • Save Kinane-dev/79be40843a2f60bf74b10aa4ed1f395a to your computer and use it in GitHub Desktop.
Save Kinane-dev/79be40843a2f60bf74b10aa4ed1f395a to your computer and use it in GitHub Desktop.
cmake_minimum_required(VERSION 3.16)
project(SpicyGamesEngine)
# Configuration du standard C++
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# Définir les chemins des bibliothèques selon votre structure
set(LIBS_DIR "${CMAKE_CURRENT_SOURCE_DIR}/libs")
set(EXTERN_DIR "${CMAKE_CURRENT_SOURCE_DIR}/extern")
set(ENGINE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/engine")
# Chemins des bibliothèques
set(SDL2_DIR "${LIBS_DIR}/SDL2")
set(ASSIMP_DIR "${LIBS_DIR}/assimp")
set(GLM_DIR "${LIBS_DIR}/glm")
set(IMGUI_DIR "${LIBS_DIR}/imgui")
set(GLAD_DIR "${LIBS_DIR}/glad")
set(IMGUIZMO_DIR "${LIBS_DIR}/ImGuizmo")
set(STB_DIR "${LIBS_DIR}/stb")
set(LUA_DIR "${EXTERN_DIR}/lua")
# Rechercher OpenGL
find_package(OpenGL REQUIRED)
# Configuration SDL2
set(SDL2_INCLUDE_DIRS "${SDL2_DIR}/include")
if(WIN32)
set(SDL2_LIBRARIES "${SDL2_DIR}/lib/x64/SDL2.lib;${SDL2_DIR}/lib/x64/SDL2main.lib")
set(SDL2_DLLS "${SDL2_DIR}/lib/x64/SDL2.dll")
else()
find_package(PkgConfig REQUIRED)
pkg_check_modules(SDL2 REQUIRED sdl2)
endif()
# Configuration Assimp
set(ASSIMP_INCLUDE_DIRS "${ASSIMP_DIR}/include")
if(WIN32)
# Chercher la bibliothèque Assimp dans les répertoires possibles
find_library(ASSIMP_LIBRARY
NAMES assimp assimp-vc143-mt assimp-vc142-mt assimp-vc141-mt
PATHS "${ASSIMP_DIR}/lib" "${ASSIMP_DIR}/bin/x64"
NO_DEFAULT_PATH
)
if(NOT ASSIMP_LIBRARY)
message(FATAL_ERROR "Assimp library not found in ${ASSIMP_DIR}")
endif()
set(ASSIMP_LIBRARIES ${ASSIMP_LIBRARY})
# Chercher la DLL Assimp
find_file(ASSIMP_DLL
NAMES assimp.dll assimp-vc143-mt.dll assimp-vc142-mt.dll assimp-vc141-mt.dll
PATHS "${ASSIMP_DIR}/bin" "${ASSIMP_DIR}/bin/x64"
NO_DEFAULT_PATH
)
else()
find_package(assimp REQUIRED)
set(ASSIMP_LIBRARIES assimp)
endif()
# Configuration GLM (header-only)
set(GLM_INCLUDE_DIRS "${GLM_DIR}")
# Configuration Lua
set(LUA_INCLUDE_DIRS "${LUA_DIR}/include")
if(WIN32)
find_library(LUA_LIBRARY
NAMES lua lua54 lua53 lua52
PATHS "${LUA_DIR}/lib"
NO_DEFAULT_PATH
)
if(LUA_LIBRARY)
set(LUA_LIBRARIES ${LUA_LIBRARY})
set(LUA_FOUND TRUE)
else()
set(LUA_FOUND FALSE)
message(WARNING "Lua library not found, building without Lua support")
endif()
else()
find_package(Lua)
endif()
# Sources GLAD
set(GLAD_SOURCES
"${GLAD_DIR}/src/glad.c"
)
# Sources ImGui
set(IMGUI_SOURCES
"${IMGUI_DIR}/imgui.cpp"
"${IMGUI_DIR}/imgui_demo.cpp"
"${IMGUI_DIR}/imgui_draw.cpp"
"${IMGUI_DIR}/imgui_tables.cpp"
"${IMGUI_DIR}/imgui_widgets.cpp"
"${IMGUI_DIR}/backends/imgui_impl_sdl2.cpp"
"${IMGUI_DIR}/backends/imgui_impl_opengl3.cpp"
)
# Sources ImGuizmo
set(IMGUIZMO_SOURCES
"${IMGUIZMO_DIR}/ImGuizmo.cpp"
)
# Collecter tous les fichiers source du moteur
file(GLOB_RECURSE ENGINE_SOURCES
"${ENGINE_DIR}/src/*.cpp"
"${ENGINE_DIR}/src/*.c"
)
# Vérifier que main.cpp existe
if(NOT EXISTS "${ENGINE_DIR}/src/main.cpp")
message(FATAL_ERROR "main.cpp not found in ${ENGINE_DIR}/src/")
endif()
# Créer l'exécutable
add_executable(${PROJECT_NAME}
${ENGINE_SOURCES}
${GLAD_SOURCES}
${IMGUI_SOURCES}
${IMGUIZMO_SOURCES}
)
# Répertoires d'inclusion
target_include_directories(${PROJECT_NAME} PRIVATE
# Répertoires du moteur
"${ENGINE_DIR}/src"
"${ENGINE_DIR}/src/core"
"${ENGINE_DIR}/src/graphics"
# Bibliothèques externes
${SDL2_INCLUDE_DIRS}
${ASSIMP_INCLUDE_DIRS}
${GLM_INCLUDE_DIRS}
"${GLAD_DIR}/include"
"${IMGUI_DIR}"
"${IMGUI_DIR}/backends"
"${IMGUIZMO_DIR}"
"${STB_DIR}"
)
# Configuration Lua conditionnelle
if(LUA_FOUND)
target_include_directories(${PROJECT_NAME} PRIVATE ${LUA_INCLUDE_DIRS})
target_link_libraries(${PROJECT_NAME} ${LUA_LIBRARIES})
target_compile_definitions(${PROJECT_NAME} PRIVATE LUA_ENABLED)
message(STATUS "Lua support enabled")
else()
target_compile_definitions(${PROJECT_NAME} PRIVATE NO_LUA)
message(STATUS "Building without Lua support")
endif()
# Lier les bibliothèques
target_link_libraries(${PROJECT_NAME}
${SDL2_LIBRARIES}
${ASSIMP_LIBRARIES}
${OPENGL_LIBRARIES}
)
# Configuration spécifique à Windows
if(WIN32)
# Définir les macros Windows
target_compile_definitions(${PROJECT_NAME} PRIVATE
_WIN32
NOMINMAX
WIN32_LEAN_AND_MEAN
)
# Copier les DLL nécessaires
if(EXISTS "${SDL2_DLLS}")
add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"${SDL2_DLLS}"
$<TARGET_FILE_DIR:${PROJECT_NAME}>
COMMENT "Copying SDL2.dll"
)
endif()
if(ASSIMP_DLL AND EXISTS "${ASSIMP_DLL}")
add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"${ASSIMP_DLL}"
$<TARGET_FILE_DIR:${PROJECT_NAME}>
COMMENT "Copying Assimp DLL"
)
endif()
# Configuration du sous-système
set_target_properties(${PROJECT_NAME} PROPERTIES
WIN32_EXECUTABLE TRUE
LINK_FLAGS "/SUBSYSTEM:CONSOLE"
)
# Bibliothèques système Windows
target_link_libraries(${PROJECT_NAME}
kernel32
user32
gdi32
winspool
shell32
ole32
oleaut32
uuid
comdlg32
advapi32
)
endif()
# Configuration Debug/Release
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
target_compile_definitions(${PROJECT_NAME} PRIVATE
DEBUG
_DEBUG
)
if(MSVC)
target_compile_options(${PROJECT_NAME} PRIVATE /W4 /WX-)
else()
target_compile_options(${PROJECT_NAME} PRIVATE -Wall -Wextra -g)
endif()
else()
target_compile_definitions(${PROJECT_NAME} PRIVATE
NDEBUG
RELEASE
)
if(MSVC)
target_compile_options(${PROJECT_NAME} PRIVATE /O2)
else()
target_compile_options(${PROJECT_NAME} PRIVATE -O3)
endif()
endif()
# Support pour les threads
find_package(Threads REQUIRED)
target_link_libraries(${PROJECT_NAME} Threads::Threads)
# Créer un dossier pour les assets dans le répertoire de sortie
add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E make_directory
$<TARGET_FILE_DIR:${PROJECT_NAME}>/assets
COMMENT "Creating assets directory"
)
# Copier les assets s'ils existent
if(EXISTS "${ENGINE_DIR}/assets")
add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_directory
"${ENGINE_DIR}/assets"
$<TARGET_FILE_DIR:${PROJECT_NAME}>/assets
COMMENT "Copying assets"
)
endif()
# Définir le répertoire de travail pour Visual Studio
if(MSVC)
set_target_properties(${PROJECT_NAME} PROPERTIES
VS_DEBUGGER_WORKING_DIRECTORY $<TARGET_FILE_DIR:${PROJECT_NAME}>
)
endif()
# Messages informatifs
message(STATUS "")
message(STATUS "=== SpicyGamesEngine Configuration ===")
message(STATUS "Build Type: ${CMAKE_BUILD_TYPE}")
message(STATUS "C++ Standard: ${CMAKE_CXX_STANDARD}")
message(STATUS "")
message(STATUS "Engine Sources: ${ENGINE_DIR}/src")
message(STATUS "Assets: ${ENGINE_DIR}/assets")
message(STATUS "")
message(STATUS "Libraries:")
message(STATUS " SDL2: ${SDL2_INCLUDE_DIRS}")
message(STATUS " Assimp: ${ASSIMP_INCLUDE_DIRS}")
message(STATUS " GLM: ${GLM_INCLUDE_DIRS}")
message(STATUS " GLAD: ${GLAD_DIR}")
message(STATUS " ImGui: ${IMGUI_DIR}")
message(STATUS " ImGuizmo: ${IMGUIZMO_DIR}")
message(STATUS " STB: ${STB_DIR}")
if(LUA_FOUND)
message(STATUS " Lua: ${LUA_INCLUDE_DIRS} ✓")
else()
message(STATUS " Lua: Not found ✗")
endif()
message(STATUS "")
message(STATUS "Build commands:")
message(STATUS " cd build")A
message(STATUS " cmake ..")
message(STATUS " cmake --build . --config Release")
message(STATUS "==========================================")
message(STATUS "")
[CMake] G├®n├®ration...
-- Selecting Windows SDK version 10.0.26100.0 to target Windows 10.0.22631.
CMake Warning at CMakeLists.txt:77 (message):
Lua library not found, building without Lua support
-- Building without Lua support
--
-- === SpicyGamesEngine Configuration ===
-- Build Type:
-- C++ Standard: 17
--
-- Engine Sources: C:/Users/sanab/Music/SpicyGamesEngine/engine/src
-- Assets: C:/Users/sanab/Music/SpicyGamesEngine/engine/assets
--
-- Libraries:
-- SDL2: C:/Users/sanab/Music/SpicyGamesEngine/libs/SDL2/include
-- Assimp: C:/Users/sanab/Music/SpicyGamesEngine/libs/assimp/include
-- GLM: C:/Users/sanab/Music/SpicyGamesEngine/libs/glm
-- GLAD: C:/Users/sanab/Music/SpicyGamesEngine/libs/glad
-- ImGui: C:/Users/sanab/Music/SpicyGamesEngine/libs/imgui
-- ImGuizmo: C:/Users/sanab/Music/SpicyGamesEngine/libs/ImGuizmo
-- STB: C:/Users/sanab/Music/SpicyGamesEngine/libs/stb
-- Lua: Not found ✗
--
-- Build commands:
-- cd build
-- cmake ..
-- cmake --build . --config Release
-- ==========================================
--
-- Configuring done (0.8s)
-- Generating done (0.4s)
-- Build files have been written to: C:/Users/sanab/Music/SpicyGamesEngine/build
[CMake] Compilation...
Version MSBuild 17.14.14+a129329f1 pour .NET Framework
main.cpp
C:\Users\sanab\Music\SpicyGamesEngine\engine\src\main.cpp(67,10): error C2143: erreur de syntaxe : absence de ';' avant
'*' [C:\Users\sanab\Music\SpicyGamesEngine\build\SpicyGamesEngine.vcxproj]
C:\Users\sanab\Music\SpicyGamesEngine\engine\src\main.cpp(67,1): error C4430: spécificateur de type manquant - int est
pris en compte par défaut. Remarque : C++ ne prend pas en charge int par défaut [C:\Users\sanab\Music\SpicyGamesEngine\
build\SpicyGamesEngine.vcxproj]
C:\Users\sanab\Music\SpicyGamesEngine\engine\src\main.cpp(67,12): error C4430: spécificateur de type manquant - int est
pris en compte par défaut. Remarque : C++ ne prend pas en charge int par défaut [C:\Users\sanab\Music\SpicyGamesEngine
\build\SpicyGamesEngine.vcxproj]
C:\Users\sanab\Music\SpicyGamesEngine\engine\src\main.cpp(293,36): error C2143: erreur de syntaxe : absence de '}' avan
t '(' [C:\Users\sanab\Music\SpicyGamesEngine\build\SpicyGamesEngine.vcxproj]
C:\Users\sanab\Music\SpicyGamesEngine\engine\src\main.cpp(293,36): error C2059: erreur de syntaxe : 'constante' [C:\Use
rs\sanab\Music\SpicyGamesEngine\build\SpicyGamesEngine.vcxproj]
C:\Users\sanab\Music\SpicyGamesEngine\engine\src\main.cpp(293,51): error C2143: erreur de syntaxe : absence de ';' avan
t '}' [C:\Users\sanab\Music\SpicyGamesEngine\build\SpicyGamesEngine.vcxproj]
C:\Users\sanab\Music\SpicyGamesEngine\engine\src\main.cpp(293,51): error C2238: jetons inattendus avant ';' [C:\Users\s
anab\Music\SpicyGamesEngine\build\SpicyGamesEngine.vcxproj]
C:\Users\sanab\Music\SpicyGamesEngine\engine\src\main.cpp(296,1): error C2059: erreur de syntaxe : '}' [C:\Users\sanab\
Music\SpicyGamesEngine\build\SpicyGamesEngine.vcxproj]
C:\Users\sanab\Music\SpicyGamesEngine\engine\src\main.cpp(296,1): error C2143: erreur de syntaxe : absence de ';' avant
'}' [C:\Users\sanab\Music\SpicyGamesEngine\build\SpicyGamesEngine.vcxproj]
C:\Users\sanab\Music\SpicyGamesEngine\engine\src\main.cpp(656,23): error C2065: 'ImGuiConfigFlags_DockingEnable' : iden
tificateur non déclaré [C:\Users\sanab\Music\SpicyGamesEngine\build\SpicyGamesEngine.vcxproj]
C:\Users\sanab\Music\SpicyGamesEngine\engine\src\main.cpp(657,23): error C2065: 'ImGuiConfigFlags_ViewportsEnable' : id
entificateur non déclaré [C:\Users\sanab\Music\SpicyGamesEngine\build\SpicyGamesEngine.vcxproj]
C:\Users\sanab\Music\SpicyGamesEngine\engine\src\main.cpp(661,26): error C2065: 'ImGuiConfigFlags_ViewportsEnable' : id
entificateur non déclaré [C:\Users\sanab\Music\SpicyGamesEngine\build\SpicyGamesEngine.vcxproj]
C:\Users\sanab\Music\SpicyGamesEngine\engine\src\main.cpp(733,9): error C2065: 'ImGuiDockNodeFlags' : identificateur no
n déclaré [C:\Users\sanab\Music\SpicyGamesEngine\build\SpicyGamesEngine.vcxproj]
C:\Users\sanab\Music\SpicyGamesEngine\engine\src\main.cpp(733,28): error C2146: erreur de syntaxe : absence de ';' avan
t l'identificateur 'dockspace_flags' [C:\Users\sanab\Music\SpicyGamesEngine\build\SpicyGamesEngine.vcxproj]
C:\Users\sanab\Music\SpicyGamesEngine\engine\src\main.cpp(733,28): error C2065: 'dockspace_flags' : identificateur non
déclaré [C:\Users\sanab\Music\SpicyGamesEngine\build\SpicyGamesEngine.vcxproj]
C:\Users\sanab\Music\SpicyGamesEngine\engine\src\main.cpp(733,46): error C2065: 'ImGuiDockNodeFlags_None' : identificat
eur non déclaré [C:\Users\sanab\Music\SpicyGamesEngine\build\SpicyGamesEngine.vcxproj]
C:\Users\sanab\Music\SpicyGamesEngine\engine\src\main.cpp(734,68): error C2065: 'ImGuiWindowFlags_NoDocking' : identifi
cateur non déclaré [C:\Users\sanab\Music\SpicyGamesEngine\build\SpicyGamesEngine.vcxproj]
C:\Users\sanab\Music\SpicyGamesEngine\engine\src\main.cpp(739,16): error C2039: 'SetNextWindowViewport' n'est pas membr
e de 'ImGui' [C:\Users\sanab\Music\SpicyGamesEngine\build\SpicyGamesEngine.vcxproj]
C:\Users\sanab\Music\SpicyGamesEngine\libs\imgui\imgui.h(3970,11):
voir la déclaration de 'ImGui'
C:\Users\sanab\Music\SpicyGamesEngine\engine\src\main.cpp(739,16): error C3861: 'SetNextWindowViewport' : identificateu
r introuvable [C:\Users\sanab\Music\SpicyGamesEngine\build\SpicyGamesEngine.vcxproj]
C:\Users\sanab\Music\SpicyGamesEngine\engine\src\main.cpp(745,13): error C2065: 'dockspace_flags' : identificateur non
déclaré [C:\Users\sanab\Music\SpicyGamesEngine\build\SpicyGamesEngine.vcxproj]
C:\Users\sanab\Music\SpicyGamesEngine\engine\src\main.cpp(745,31): error C2065: 'ImGuiDockNodeFlags_PassthruCentralNode
' : identificateur non déclaré [C:\Users\sanab\Music\SpicyGamesEngine\build\SpicyGamesEngine.vcxproj]
C:\Users\sanab\Music\SpicyGamesEngine\engine\src\main.cpp(755,30): error C2065: 'ImGuiConfigFlags_DockingEnable' : iden
tificateur non déclaré [C:\Users\sanab\Music\SpicyGamesEngine\build\SpicyGamesEngine.vcxproj]
C:\Users\sanab\Music\SpicyGamesEngine\engine\src\main.cpp(757,20): error C2039: 'DockSpace' n'est pas membre de 'ImGui'
[C:\Users\sanab\Music\SpicyGamesEngine\build\SpicyGamesEngine.vcxproj]
C:\Users\sanab\Music\SpicyGamesEngine\libs\imgui\imgui.h(3970,11):
voir la déclaration de 'ImGui'
C:\Users\sanab\Music\SpicyGamesEngine\engine\src\main.cpp(757,64): error C2065: 'dockspace_flags' : identificateur non
déclaré [C:\Users\sanab\Music\SpicyGamesEngine\build\SpicyGamesEngine.vcxproj]
C:\Users\sanab\Music\SpicyGamesEngine\engine\src\main.cpp(757,20): error C3861: 'DockSpace' : identificateur introuvabl
e [C:\Users\sanab\Music\SpicyGamesEngine\build\SpicyGamesEngine.vcxproj]
C:\Users\sanab\Music\SpicyGamesEngine\engine\src\main.cpp(791,30): error C2065: 'ImGuiConfigFlags_ViewportsEnable' : id
entificateur non déclaré [C:\Users\sanab\Music\SpicyGamesEngine\build\SpicyGamesEngine.vcxproj]
C:\Users\sanab\Music\SpicyGamesEngine\engine\src\main.cpp(794,20): error C2039: 'UpdatePlatformWindows' n'est pas membr
e de 'ImGui' [C:\Users\sanab\Music\SpicyGamesEngine\build\SpicyGamesEngine.vcxproj]
C:\Users\sanab\Music\SpicyGamesEngine\libs\imgui\imgui.h(3970,11):
voir la déclaration de 'ImGui'
C:\Users\sanab\Music\SpicyGamesEngine\engine\src\main.cpp(794,20): error C3861: 'UpdatePlatformWindows' : identificateu
r introuvable [C:\Users\sanab\Music\SpicyGamesEngine\build\SpicyGamesEngine.vcxproj]
C:\Users\sanab\Music\SpicyGamesEngine\engine\src\main.cpp(795,20): error C2039: 'RenderPlatformWindowsDefault' n'est pa
s membre de 'ImGui' [C:\Users\sanab\Music\SpicyGamesEngine\build\SpicyGamesEngine.vcxproj]
C:\Users\sanab\Music\SpicyGamesEngine\libs\imgui\imgui.h(3970,11):
voir la déclaration de 'ImGui'
C:\Users\sanab\Music\SpicyGamesEngine\engine\src\main.cpp(795,20): error C3861: 'RenderPlatformWindowsDefault' : identi
ficateur introuvable [C:\Users\sanab\Music\SpicyGamesEngine\build\SpicyGamesEngine.vcxproj]
C:\Users\sanab\Music\SpicyGamesEngine\engine\src\main.cpp(808,9): error C3861: 'lua_close' : identificateur introuvable
[C:\Users\sanab\Music\SpicyGamesEngine\build\SpicyGamesEngine.vcxproj]
C:\Users\sanab\Music\SpicyGamesEngine\engine\src\main.cpp(1734,2): error C4430: spécificateur de type manquant - int es
t pris en compte par défaut. Remarque : C++ ne prend pas en charge int par défaut [C:\Users\sanab\Music\SpicyGamesEngin
e\build\SpicyGamesEngine.vcxproj]
C:\Users\sanab\Music\SpicyGamesEngine\engine\src\main.cpp(1734,11): error C2059: erreur de syntaxe : ':' [C:\Users\sana
b\Music\SpicyGamesEngine\build\SpicyGamesEngine.vcxproj]
C:\Users\sanab\Music\SpicyGamesEngine\engine\src\main.cpp(1735,21): error C2059: erreur de syntaxe : 'if' [C:\Users\san
ab\Music\SpicyGamesEngine\build\SpicyGamesEngine.vcxproj]
C:\Users\sanab\Music\SpicyGamesEngine\engine\src\main.cpp(1735,50): error C2143: erreur de syntaxe : absence de ';' ava
nt '{' [C:\Users\sanab\Music\SpicyGamesEngine\build\SpicyGamesEngine.vcxproj]
C:\Users\sanab\Music\SpicyGamesEngine\engine\src\main.cpp(1735,50): error C2447: '{' : en-tête de fonction manquant (li
ste formelle à l'ancien format ?) [C:\Users\sanab\Music\SpicyGamesEngine\build\SpicyGamesEngine.vcxproj]
C:\Users\sanab\Music\SpicyGamesEngine\engine\src\main.cpp(1741,21): error C2059: erreur de syntaxe : 'break' [C:\Users\
sanab\Music\SpicyGamesEngine\build\SpicyGamesEngine.vcxproj]
C:\Users\sanab\Music\SpicyGamesEngine\engine\src\main.cpp(1742,17): error C2059: erreur de syntaxe : 'case' [C:\Users\s
anab\Music\SpicyGamesEngine\build\SpicyGamesEngine.vcxproj]
Ô£à Build termin├®.
L'ex├®cutable est situ├® dans: build\Release\SpicyGamesEngine.exe
Appuyez sur une touche pour continuer...
#include <iostream>
#include <string>
#include <vector>
#include <memory>
#include <map>
#include <filesystem>
#include <chrono>
#include <fstream>
#include <sstream>
#include <set>
#include <algorithm>
#include <utility>
#include <cstring>
#include <cstdint>
// File dialog for Windows
#ifdef _WIN32
#include <windows.h>
#include <commdlg.h>
#endif
#include <SDL.h>
// Fix APIENTRY redefinition
#ifdef _WIN32
#undef APIENTRY
#endif
#include <glad/glad.h>
#include <imgui.h>
#include <imgui_impl_sdl2.h>
#include <imgui_impl_opengl3.h>
#include <ImGuizmo.h>
#include <assimp/version.h>
#include <assimp/Importer.hpp>
#include <assimp/scene.h>
#include <assimp/postprocess.h>
#ifndef NO_LUA
#include <lua.hpp>
#endif
#include <glm/glm.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <glm/gtx/matrix_decompose.hpp>
#include <glm/gtx/transform.hpp>
#include <glm/gtc/matrix_transform.hpp>
#define STB_IMAGE_IMPLEMENTATION
#include <stb_image.h>
// Définir M_PI si pas défini
#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif
// Forward declarations
struct Mesh;
struct SceneObject;
struct Camera;
struct InputState;
struct PerformanceStats;
// Global variables
SDL_Window* g_window = nullptr;
SDL_GLContext g_glContext = nullptr;
lua_State* g_luaState = nullptr;
// OpenGL variables
GLuint g_shaderProgram = 0;
GLuint g_skyboxShader = 0;
GLuint g_skyboxVAO = 0;
GLuint g_skyboxVBO = 0;
// Textures
std::map<std::string, GLuint> g_textures;
GLuint g_skyboxTexture = 0;
// Project settings
struct ProjectSettings {
std::string projectName = "New Project";
std::string projectPath = "";
std::string sceneName = "Main Scene";
bool autoSave = true;
int autoSaveInterval = 300; // seconds
} g_projectSettings;
// Mesh data structure
struct Vertex {
glm::vec3 position;
glm::vec2 texCoords;
glm::vec3 normal;
};
struct Mesh {
std::vector<Vertex> vertices;
std::vector<unsigned int> indices;
GLuint VAO = 0, VBO = 0, EBO = 0;
std::string textureName;
void setupMesh() {
glGenVertexArrays(1, &VAO);
glGenBuffers(1, &VBO);
glGenBuffers(1, &EBO);
glBindVertexArray(VAO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(Vertex), &vertices[0], GL_STATIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(unsigned int), &indices[0], GL_STATIC_DRAW);
// Positions
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)0);
// Texture coordinates
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, texCoords));
// Normals
glEnableVertexAttribArray(2);
glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, normal));
glBindVertexArray(0);
}
void draw() {
glBindVertexArray(VAO);
glDrawElements(GL_TRIANGLES, static_cast<GLsizei>(indices.size()), GL_UNSIGNED_INT, 0);
glBindVertexArray(0);
}
~Mesh() {
if (VAO) glDeleteVertexArrays(1, &VAO);
if (VBO) glDeleteBuffers(1, &VBO);
if (EBO) glDeleteBuffers(1, &EBO);
}
};
// Camera
struct Camera {
glm::vec3 position = glm::vec3(5.0f, 5.0f, 5.0f);
glm::vec3 target = glm::vec3(0.0f, 0.0f, 0.0f);
glm::vec3 up = glm::vec3(0.0f, 1.0f, 0.0f);
glm::vec3 front = glm::vec3(0.0f, 0.0f, -1.0f);
glm::vec3 right = glm::vec3(1.0f, 0.0f, 0.0f);
float yaw = -90.0f;
float pitch = 0.0f;
float fov = 45.0f;
float nearPlane = 0.1f;
float farPlane = 100.0f;
float speed = 5.0f;
float sensitivity = 0.1f;
void updateVectors() {
glm::vec3 direction;
direction.x = cos(glm::radians(yaw)) * cos(glm::radians(pitch));
direction.y = sin(glm::radians(pitch));
direction.z = sin(glm::radians(yaw)) * cos(glm::radians(pitch));
front = glm::normalize(direction);
right = glm::normalize(glm::cross(front, glm::vec3(0.0f, 1.0f, 0.0f)));
up = glm::normalize(glm::cross(right, front));
target = position + front;
}
glm::mat4 getViewMatrix() {
return glm::lookAt(position, position + front, up);
}
glm::mat4 getProjectionMatrix(float aspectRatio) {
return glm::perspective(glm::radians(fov), aspectRatio, nearPlane, farPlane);
}
};
// Input state
struct InputState {
bool keys[SDL_NUM_SCANCODES] = {false};
bool mouseButtons[8] = {false};
int mouseX = 0, mouseY = 0;
int mouseDeltaX = 0, mouseDeltaY = 0;
bool firstMouse = true;
int lastMouseX = 0, lastMouseY = 0;
bool rightMousePressed = false;
};
enum class ObjectType {
CUBE,
SPHERE,
PLANE,
CYLINDER,
LIGHT,
MODEL,
EMPTY
};
struct SceneObject {
std::string name;
glm::mat4 transform = glm::mat4(1.0f);
ObjectType type = ObjectType::CUBE;
glm::vec3 color = glm::vec3(0.7f, 0.7f, 1.0f);
bool visible = true;
bool selected = false;
std::string textureName = "";
std::string modelPath = "";
glm::vec3 scale = glm::vec3(1.0f);
glm::vec3 rotation = glm::vec3(0.0f);
glm::vec3 position = glm::vec3(0.0f);
// Light properties
float lightIntensity = 1.0f;
float lightRange = 10.0f;
// Material properties
float roughness = 0.5f;
float metallic = 0.0f;
float emission = 0.0f;
std::vector<std::unique_ptr<Mesh>> meshes;
void updateTransform() {
glm::mat4 T = glm::translate(glm::mat4(1.0f), position);
glm::mat4 R = glm::rotate(glm::mat4(1.0f), glm::radians(rotation.x), glm::vec3(1, 0, 0));
R = glm::rotate(R, glm::radians(rotation.y), glm::vec3(0, 1, 0));
R = glm::rotate(R, glm::radians(rotation.z), glm::vec3(0, 0, 1));
glm::mat4 S = glm::scale(glm::mat4(1.0f), scale);
transform = T * R * S;
}
void decomposeTransform() {
glm::vec3 skew;
glm::vec4 perspective;
glm::quat orient;
glm::decompose(transform, scale, orient, position, skew, perspective);
rotation = glm::degrees(glm::eulerAngles(orient));
}
};
// Global scene objects and variables
std::vector<std::unique_ptr<SceneObject>> g_sceneObjects;
int g_objectCounter = 0;
int g_selectedIndex = -1;
Camera g_camera;
InputState g_input;
// Default meshes
std::unique_ptr<Mesh> g_defaultCubeMesh;
std::unique_ptr<Mesh> g_defaultSphereMesh;
std::unique_ptr<Mesh> g_defaultPlaneMesh;
std::unique_ptr<Mesh> g_defaultCylinderMesh;
// Gizmo settings
ImGuizmo::OPERATION g_gizmoOperation = ImGuizmo::TRANSLATE;
ImGuizmo::MODE g_gizmoMode = ImGuizmo::LOCAL;
bool g_useSnap = false;
float g_snapValues[3] = { 1.0f, 15.0f, 0.1f }; // translate, rotation, scale
// UI State
bool g_showSceneHierarchy = true;
bool g_showProperties = true;
bool g_showViewport = true;
bool g_showAssetBrowser = true;
bool g_showConsole = true;
bool g_showTextureManager = true;
bool g_showSettings = false;
bool g_showStats = true;
bool g_showDemoWindow = false;
// Console
std::vector<std::string> g_consoleLog;
std::string g_consoleInput;
// Skybox colors
glm::vec3 g_skyboxTopColor = glm::vec3(0.5f, 0.7f, 1.0f);
glm::vec3 g_skyboxBottomColor = glm::vec3(0.8f, 0.9f, 1.0f);
// Framebuffer for viewport
GLuint g_framebuffer = 0;
GLuint g_colorTexture = 0;
GLuint g_depthTexture = 0;
int g_viewportWidth = 800;
int g_viewportHeight = 600;
// Performance tracking
struct PerformanceStats {
float frameTime = 0.0f;
float fps = 0.0f;
int drawCalls = 0;
int vertices = 0;
int triangles = 0;
} g_stats;
// Undo/Redo system
struct UndoAction {
enum Type { TRANSFORM, CREATE, DELETE, MODIFY } type;
int objectIndex;
std::string data;
};
std::vector<UndoAction> g_undoStack;
std::vector<UndoAction> g_redoStack;
const size_t MAX_UNDO_STACK = 50;
// Global variables for settings
bool g_wireframe = false;
bool g_vsync = true;
float g_gamma = 2.2f;
bool g_gizmoBounds = false;
// Function prototypes
void LogToConsole(const std::string& message);
std::string OpenFileDialog(const char* filter);
std::string SaveFileDialog(const char* filter);
GLuint LoadTexture(const std::string& path);
GLuint CompileShader(GLenum type, const char* source);
void CreateDefaultCubeMesh();
void CreateSphereMesh();
void CreatePlaneMesh();
void CreateCylinderMesh();
void CreateFramebuffer(int width, int height);
void InitOpenGL();
void InitLua();
void ProcessInput(float deltaTime);
void CreateObject(ObjectType type);
void ImportModel();
void ImportTexture();
bool LoadModel(const std::string& path, SceneObject* obj);
void RenderScene();
void RenderSkybox(const glm::mat4& view, const glm::mat4& projection);
Mesh* GetMeshForObjectType(ObjectType type);
std::unique_ptr<Mesh> ProcessMesh(aiMesh* mesh, const aiScene* scene);
void ProcessNode(aiNode* node, const aiScene* scene, std::vector<std::unique_ptr<Mesh>>& meshes);
void RenderMenuBar();
void RenderSceneHierarchy();
void RenderProperties();
void RenderAssetBrowser();
void RenderTextureManager();
void RenderConsole();
void RenderSettings();
void RenderStats();
void RenderViewport();
// Function to log messages to console
void LogToConsole(const std::string& message) {
g_consoleLog.push_back(message);
if (g_consoleLog.size() > 1000) {
g_consoleLog.erase(g_consoleLog.begin());
}
std::cout << "[LOG] " << message << std::endl;
}
// File dialog functions
#ifdef _WIN32
std::string OpenFileDialog(const char* filter) {
OPENFILENAMEA ofn;
char szFile[260] = {0};
ZeroMemory(&ofn, sizeof(ofn));
ofn.lStructSize = sizeof(ofn);
ofn.hwndOwner = NULL;
ofn.lpstrFile = szFile;
ofn.nMaxFile = sizeof(szFile);
ofn.lpstrFilter = filter;
ofn.nFilterIndex = 1;
ofn.lpstrFileTitle = NULL;
ofn.nMaxFileTitle = 0;
ofn.lpstrInitialDir = NULL;
ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;
if (GetOpenFileNameA(&ofn)) {
return std::string(szFile);
}
return "";
}
std::string SaveFileDialog(const char* filter) {
OPENFILENAMEA ofn;
char szFile[260] = {0};
ZeroMemory(&ofn, sizeof(ofn));
ofn.lStructSize = sizeof(ofn);
ofn.hwndOwner = NULL;
ofn.lpstrFile = szFile;
ofn.nMaxFile = sizeof(szFile);
ofn.lpstrFilter = filter;
ofn.nFilterIndex = 1;
ofn.lpstrFileTitle = NULL;
ofn.nMaxFileTitle = 0;
ofn.lpstrInitialDir = NULL;
ofn.Flags = OFN_PATHMUSTEXIST | OFN_OVERWRITEPROMPT;
if (GetSaveFileNameA(&ofn)) {
return std::string(szFile);
}
return "";
}
#else
std::string OpenFileDialog(const char* filter) {
LogToConsole("File dialog not implemented for this platform");
return "";
}
std::string SaveFileDialog(const char* filter) {
LogToConsole("Save dialog not implemented for this platform");
return "";
}
#endif
// Texture loading function
GLuint LoadTexture(const std::string& path) {
GLuint textureID;
glGenTextures(1, &textureID);
int width, height, nrChannels;
stbi_set_flip_vertically_on_load(true);
unsigned char* data = stbi_load(path.c_str(), &width, &height, &nrChannels, 0);
if (data) {
GLenum format = GL_RGB;
if (nrChannels == 1) format = GL_RED;
else if (nrChannels == 3) format = GL_RGB;
else if (nrChannels == 4) format = GL_RGBA;
glBindTexture(GL_TEXTURE_2D, textureID);
glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, GL_UNSIGNED_BYTE, data);
glGenerateMipmap(GL_TEXTURE_2D);
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_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
stbi_image_free(data);
LogToConsole("Texture chargee: " + path + " (" + std::to_string(width) + "x" + std::to_string(height) + ")");
} else {
LogToConsole("Erreur chargement texture: " + path);
glDeleteTextures(1, &textureID);
return 0;
}
return textureID;
}
// Shader sources
const char* vertexShaderSource = R"(
#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec2 aTexCoord;
layout (location = 2) in vec3 aNormal;
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
uniform bool isWireframe;
out vec2 TexCoord;
out vec3 Normal;
out vec3 FragPos;
void main()
{
FragPos = vec3(model * vec4(aPos, 1.0));
Normal = mat3(transpose(inverse(model))) * aNormal;
TexCoord = aTexCoord;
gl_Position = projection * view * vec4(FragPos, 1.0);
}
)";
const char* fragmentShaderSource = R"(
#version 330 core
out vec4 FragColor;
in vec2 TexCoord;
in vec3 Normal;
in vec3 FragPos;
uniform vec3 objectColor;
uniform bool isSelected;
uniform bool isWireframe;
uniform bool hasTexture;
uniform sampler2D texture1;
uniform vec3 lightPos;
uniform vec3 lightColor;
uniform vec3 viewPos;
uniform float roughness;
uniform float metallic;
uniform float emission;
void main()
{
if (isWireframe) {
FragColor = vec4(1.0, 0.5, 0.0, 1.0);
return;
}
vec3 color = objectColor;
if (hasTexture) {
vec3 texColor = texture(texture1, TexCoord).rgb;
color = color * texColor;
}
// Simple PBR-like lighting
vec3 norm = normalize(Normal);
vec3 lightDir = normalize(lightPos - FragPos);
vec3 viewDir = normalize(viewPos - FragPos);
vec3 halfwayDir = normalize(lightDir + viewDir);
// Diffuse
float diff = max(dot(norm, lightDir), 0.0);
vec3 diffuse = diff * lightColor * (1.0 - metallic);
// Specular
float spec = pow(max(dot(norm, halfwayDir), 0.0), (1.0 - roughness) * 256.0);
vec3 specular = spec * lightColor * mix(vec3(0.04), color, metallic);
vec3 ambient = 0.1 * color;
vec3 result = ambient + diffuse * color + specular;
// Add emission
result += color * emission;
if (isSelected) {
result = mix(result, vec3(1.0, 0.5, 0.0), 0.3);
}
FragColor = vec4(result, 1.0);
}
)";
const char* skyboxVertexShader = R"(
#version 330 core
layout (location = 0) in vec3 aPos;
uniform mat4 view;
uniform mat4 projection;
out vec3 WorldPos;
void main()
{
WorldPos = aPos;
mat4 rotView = mat4(mat3(view));
vec4 clipPos = projection * rotView * vec4(aPos, 1.0);
gl_Position = clipPos.xyww;
}
)";
const char* skyboxFragmentShader = R"(
#version 330 core
out vec4 FragColor;
in vec3 WorldPos;
uniform vec3 topColor;
uniform vec3 bottomColor;
void main()
{
float t = (normalize(WorldPos).y + 1.0) * 0.5;
vec3 color = mix(bottomColor, topColor, t);
FragColor = vec4(color, 1.0);
}
)";
// Shader compilation function
GLuint CompileShader(GLenum type, const char* source) {
GLuint shader = glCreateShader(type);
glShaderSource(shader, 1, &source, NULL);
glCompileShader(shader);
GLint success;
glGetShaderiv(shader, GL_COMPILE_STATUS, &success);
if (!success) {
char infoLog[512];
glGetShaderInfoLog(shader, 512, NULL, infoLog);
LogToConsole("Erreur compilation shader: " + std::string(infoLog));
}
return shader;
}
// Mesh creation functions (reste du code...)
void CreateDefaultCubeMesh() {
g_defaultCubeMesh = std::make_unique<Mesh>();
g_defaultCubeMesh->vertices = {
// Front face
{{-0.5f, -0.5f, 0.5f}, {0.0f, 0.0f}, {0.0f, 0.0f, 1.0f}},
{{ 0.5f, -0.5f, 0.5f}, {1.0f, 0.0f}, {0.0f, 0.0f, 1.0f}},
{{ 0.5f, 0.5f, 0.5f}, {1.0f, 1.0f}, {0.0f, 0.0f, 1.0f}},
{{-0.5f, 0.5f, 0.5f}, {0.0f, 1.0f}, {0.0f, 0.0f, 1.0f}},
// Back face
{{-0.5f, -0.5f, -0.5f}, {1.0f, 0.0f}, {0.0f, 0.0f, -1.0f}},
{{ 0.5f, -0.5f, -0.5f}, {0.0f, 0.0f}, {0.0f, 0.0f, -1.0f}},
{{ 0.5f, 0.5f, -0.5f}, {0.0f, 1.0f}, {0.0f, 0.0f, -1.0f}},
{{-0.5f, 0.5f, -0.5f}, {1.0f, 1.0f}, {0.0f, 0.0f, -1.0f}}
};
g_defaultCubeMesh->indices = {
0, 1, 2, 2, 3, 0, // Front
4, 5, 6, 6, 7, 4, // Back
3, 2, 6, 6, 7, 3, // Top
0, 1, 5, 5, 4, 0, // Bottom
0, 3, 7, 7, 4, 0, // Left
1, 2, 6, 6, 5, 1 // Right
};
g_defaultCubeMesh->setupMesh();
}
// Ajoutez ici les autres fonctions manquantes...
// Note: Le fichier original semble tronqué, je fournis la structure de base corrigée
int main(int argc, char* argv[]) {
// Initialization
if (SDL_Init(SDL_INIT_VIDEO) < 0) {
std::cerr << "SDL initialization failed: " << SDL_GetError() << std::endl;
return -1;
}
// OpenGL context
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
// Create window
g_window = SDL_CreateWindow("SpicyGamesEngine",
SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
1280, 720,
SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE);
if (!g_window) {
std::cerr << "Window creation failed: " << SDL_GetError() << std::endl;
SDL_Quit();
return -1;
}
g_glContext = SDL_GL_CreateContext(g_window);
if (!g_glContext) {
std::cerr << "OpenGL context creation failed: " << SDL_GetError() << std::endl;
SDL_DestroyWindow(g_window);
SDL_Quit();
return -1;
}
if (!gladLoadGLLoader((GLADloadproc)SDL_GL_GetProcAddress)) {
std::cerr << "Failed to initialize OpenGL context" << std::endl;
return -1;
}
// Enable VSync
SDL_GL_SetSwapInterval(g_vsync ? 1 : 0);
// Initialize ImGui
IMGUI_CHECKVERSION();
ImGui::CreateContext();
ImGuiIO& io = ImGui::GetIO();
io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;
io.ConfigFlags |= ImGuiConfigFlags_DockingEnable;
io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable;
ImGui::StyleColorsDark();
ImGuiStyle& style = ImGui::GetStyle();
if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) {
style.WindowRounding = 0.0f;
style.Colors[ImGuiCol_WindowBg].w = 1.0f;
}
ImGui_ImplSDL2_InitForOpenGL(g_window, g_glContext);
ImGui_ImplOpenGL3_Init("#version 330");
// Initialize engine systems
InitOpenGL();
InitLua();
LogToConsole("SpicyGamesEngine demarré avec succès!");
// Main loop
bool running = true;
auto lastTime = std::chrono::high_resolution_clock::now();
while (running) {
auto currentTime = std::chrono::high_resolution_clock::now();
float deltaTime = std::chrono::duration<float>(currentTime - lastTime).count();
lastTime = currentTime;
g_stats.frameTime = deltaTime;
g_stats.fps = 1.0f / deltaTime;
// Events
SDL_Event event;
while (SDL_PollEvent(&event)) {
ImGui_ImplSDL2_ProcessEvent(&event);
switch (event.type) {
case SDL_QUIT:
running = false;
break;
case SDL_KEYDOWN:
if (event.key.keysym.scancode < SDL_NUM_SCANCODES) {
g_input.keys[event.key.keysym.scancode] = true;
}
break;
case SDL_KEYUP:
if (event.key.keysym.scancode < SDL_NUM_SCANCODES) {
g_input.keys[event.key.keysym.scancode] = false;
}
break;
case SDL_MOUSEBUTTONUP:
if (event.button.button < 8) {
g_input.mouseButtons[event.button.button] = false;
if (event.button.button == SDL_BUTTON_RIGHT) {
g_input.rightMousePressed = false;
}
}
break;
case SDL_MOUSEMOTION:
g_input.mouseX = event.motion.x;
g_input.mouseY = event.motion.y;
g_input.mouseDeltaX = event.motion.xrel;
g_input.mouseDeltaY = event.motion.yrel;
break;
}
}
// Process input
ProcessInput(deltaTime);
// Start ImGui frame
ImGui_ImplOpenGL3_NewFrame();
ImGui_ImplSDL2_NewFrame();
ImGui::NewFrame();
ImGuizmo::BeginFrame();
// Create dockspace
ImGuiDockNodeFlags dockspace_flags = ImGuiDockNodeFlags_None;
ImGuiWindowFlags window_flags = ImGuiWindowFlags_MenuBar | ImGuiWindowFlags_NoDocking;
const ImGuiViewport* viewport = ImGui::GetMainViewport();
ImGui::SetNextWindowPos(viewport->WorkPos);
ImGui::SetNextWindowSize(viewport->WorkSize);
ImGui::SetNextWindowViewport(viewport->ID);
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f);
ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f);
window_flags |= ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove;
window_flags |= ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoNavFocus;
if (dockspace_flags & ImGuiDockNodeFlags_PassthruCentralNode)
window_flags |= ImGuiWindowFlags_NoBackground;
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f));
ImGui::Begin("DockSpace Demo", nullptr, window_flags);
ImGui::PopStyleVar();
ImGui::PopStyleVar(2);
// Submit the DockSpace
ImGuiIO& io = ImGui::GetIO();
if (io.ConfigFlags & ImGuiConfigFlags_DockingEnable) {
ImGuiID dockspace_id = ImGui::GetID("MyDockSpace");
ImGui::DockSpace(dockspace_id, ImVec2(0.0f, 0.0f), dockspace_flags);
}
// Render menu bar
RenderMenuBar();
ImGui::End();
// Render UI panels
RenderSceneHierarchy();
RenderProperties();
RenderAssetBrowser();
RenderTextureManager();
RenderConsole();
RenderSettings();
RenderStats();
RenderViewport();
if (g_showDemoWindow) {
ImGui::ShowDemoWindow(&g_showDemoWindow);
}
// Render
int displayW, displayH;
SDL_GetWindowSize(g_window, &displayW, &displayH);
glViewport(0, 0, displayW, displayH);
glClearColor(0.45f, 0.55f, 0.60f, 1.00f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// Render ImGui
ImGui::Render();
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
// Update and Render additional Platform Windows
if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) {
SDL_Window* backup_current_window = SDL_GL_GetCurrentWindow();
SDL_GLContext backup_current_context = SDL_GL_GetCurrentContext();
ImGui::UpdatePlatformWindows();
ImGui::RenderPlatformWindowsDefault();
SDL_GL_MakeCurrent(backup_current_window, backup_current_context);
}
SDL_GL_SwapWindow(g_window);
}
// Cleanup
ImGui_ImplOpenGL3_Shutdown();
ImGui_ImplSDL2_Shutdown();
ImGui::DestroyContext();
if (g_luaState) {
lua_close(g_luaState);
}
SDL_GL_DeleteContext(g_glContext);
SDL_DestroyWindow(g_window);
SDL_Quit();
return 0;
}
// Implémentation des fonctions manquantes...
void CreateSphereMesh() {
g_defaultSphereMesh = std::make_unique<Mesh>();
const int segments = 32;
const int rings = 16;
// Generate vertices
for (int ring = 0; ring <= rings; ring++) {
float phi = static_cast<float>(M_PI * ring / rings);
for (int segment = 0; segment <= segments; segment++) {
float theta = static_cast<float>(2.0 * M_PI * segment / segments);
Vertex vertex;
vertex.position.x = sin(phi) * cos(theta);
vertex.position.y = cos(phi);
vertex.position.z = sin(phi) * sin(theta);
vertex.normal = vertex.position;
vertex.texCoords.x = static_cast<float>(segment) / segments;
vertex.texCoords.y = static_cast<float>(ring) / rings;
g_defaultSphereMesh->vertices.push_back(vertex);
}
}
// Generate indices
for (int ring = 0; ring < rings; ring++) {
for (int segment = 0; segment < segments; segment++) {
int current = ring * (segments + 1) + segment;
int next = current + segments + 1;
g_defaultSphereMesh->indices.push_back(current);
g_defaultSphereMesh->indices.push_back(next);
g_defaultSphereMesh->indices.push_back(current + 1);
g_defaultSphereMesh->indices.push_back(current + 1);
g_defaultSphereMesh->indices.push_back(next);
g_defaultSphereMesh->indices.push_back(next + 1);
}
}
g_defaultSphereMesh->setupMesh();
}
void CreatePlaneMesh() {
g_defaultPlaneMesh = std::make_unique<Mesh>();
g_defaultPlaneMesh->vertices = {
{{-0.5f, 0.0f, -0.5f}, {0.0f, 0.0f}, {0.0f, 1.0f, 0.0f}},
{{ 0.5f, 0.0f, -0.5f}, {1.0f, 0.0f}, {0.0f, 1.0f, 0.0f}},
{{ 0.5f, 0.0f, 0.5f}, {1.0f, 1.0f}, {0.0f, 1.0f, 0.0f}},
{{-0.5f, 0.0f, 0.5f}, {0.0f, 1.0f}, {0.0f, 1.0f, 0.0f}},
};
g_defaultPlaneMesh->indices = {
0, 1, 2, 2, 3, 0
};
g_defaultPlaneMesh->setupMesh();
}
void CreateCylinderMesh() {
g_defaultCylinderMesh = std::make_unique<Mesh>();
const int segments = 32;
const float height = 1.0f;
const float radius = 0.5f;
// Top and bottom centers
g_defaultCylinderMesh->vertices.push_back({{0.0f, height/2, 0.0f}, {0.5f, 0.5f}, {0.0f, 1.0f, 0.0f}});
g_defaultCylinderMesh->vertices.push_back({{0.0f, -height/2, 0.0f}, {0.5f, 0.5f}, {0.0f, -1.0f, 0.0f}});
// Side vertices
for (int i = 0; i <= segments; i++) {
float theta = static_cast<float>(2.0 * M_PI * i / segments);
float x = radius * cos(theta);
float z = radius * sin(theta);
float u = static_cast<float>(i) / segments;
// Top ring
g_defaultCylinderMesh->vertices.push_back({{x, height/2, z}, {u, 1.0f}, {x, 0.0f, z}});
// Bottom ring
g_defaultCylinderMesh->vertices.push_back({{x, -height/2, z}, {u, 0.0f}, {x, 0.0f, z}});
}
// Generate indices (caps and sides)
for (int i = 0; i < segments; i++) {
// Top cap
g_defaultCylinderMesh->indices.push_back(0);
g_defaultCylinderMesh->indices.push_back(2 + i * 2);
g_defaultCylinderMesh->indices.push_back(2 + ((i + 1) % segments) * 2);
// Bottom cap
g_defaultCylinderMesh->indices.push_back(1);
g_defaultCylinderMesh->indices.push_back(3 + ((i + 1) % segments) * 2);
g_defaultCylinderMesh->indices.push_back(3 + i * 2);
// Side faces
int current = 2 + i * 2;
int next = 2 + ((i + 1) % segments) * 2;
g_defaultCylinderMesh->indices.push_back(current);
g_defaultCylinderMesh->indices.push_back(current + 1);
g_defaultCylinderMesh->indices.push_back(next);
g_defaultCylinderMesh->indices.push_back(next);
g_defaultCylinderMesh->indices.push_back(current + 1);
g_defaultCylinderMesh->indices.push_back(next + 1);
}
g_defaultCylinderMesh->setupMesh();
}
void CreateFramebuffer(int width, int height) {
// Delete existing framebuffer if it exists
if (g_framebuffer != 0) {
glDeleteFramebuffers(1, &g_framebuffer);
glDeleteTextures(1, &g_colorTexture);
glDeleteTextures(1, &g_depthTexture);
}
g_viewportWidth = width;
g_viewportHeight = height;
// Create framebuffer
glGenFramebuffers(1, &g_framebuffer);
glBindFramebuffer(GL_FRAMEBUFFER, g_framebuffer);
// Create color texture
glGenTextures(1, &g_colorTexture);
glBindTexture(GL_TEXTURE_2D, g_colorTexture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 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, g_colorTexture, 0);
// Create depth texture
glGenTextures(1, &g_depthTexture);
glBindTexture(GL_TEXTURE_2D, g_depthTexture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, width, height, 0, GL_DEPTH_COMPONENT, GL_FLOAT, 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_DEPTH_ATTACHMENT, GL_TEXTURE_2D, g_depthTexture, 0);
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
LogToConsole("Erreur: Framebuffer incomplet!");
}
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
void InitOpenGL() {
// Compilation des shaders principaux
GLuint vertexShader = CompileShader(GL_VERTEX_SHADER, vertexShaderSource);
GLuint fragmentShader = CompileShader(GL_FRAGMENT_SHADER, fragmentShaderSource);
g_shaderProgram = glCreateProgram();
glAttachShader(g_shaderProgram, vertexShader);
glAttachShader(g_shaderProgram, fragmentShader);
glLinkProgram(g_shaderProgram);
GLint success;
glGetProgramiv(g_shaderProgram, GL_LINK_STATUS, &success);
if (!success) {
char infoLog[512];
glGetProgramInfoLog(g_shaderProgram, 512, NULL, infoLog);
LogToConsole("Erreur link programme: " + std::string(infoLog));
}
glDeleteShader(vertexShader);
glDeleteShader(fragmentShader);
// Compilation des shaders skybox
GLuint skyVertexShader = CompileShader(GL_VERTEX_SHADER, skyboxVertexShader);
GLuint skyFragmentShader = CompileShader(GL_FRAGMENT_SHADER, skyboxFragmentShader);
g_skyboxShader = glCreateProgram();
glAttachShader(g_skyboxShader, skyVertexShader);
glAttachShader(g_skyboxShader, skyFragmentShader);
glLinkProgram(g_skyboxShader);
glGetProgramiv(g_skyboxShader, GL_LINK_STATUS, &success);
if (!success) {
char infoLog[512];
glGetProgramInfoLog(g_skyboxShader, 512, NULL, infoLog);
LogToConsole("Erreur link programme skybox: " + std::string(infoLog));
}
glDeleteShader(skyVertexShader);
glDeleteShader(skyFragmentShader);
// Skybox setup
float skyboxVertices[] = {
-1.0f, 1.0f, -1.0f,
-1.0f, -1.0f, -1.0f,
1.0f, -1.0f, -1.0f,
1.0f, -1.0f, -1.0f,
1.0f, 1.0f, -1.0f,
-1.0f, 1.0f, -1.0f,
-1.0f, -1.0f, 1.0f,
-1.0f, -1.0f, -1.0f,
-1.0f, 1.0f, -1.0f,
-1.0f, 1.0f, -1.0f,
-1.0f, 1.0f, 1.0f,
-1.0f, -1.0f, 1.0f,
1.0f, -1.0f, -1.0f,
1.0f, -1.0f, 1.0f,
1.0f, 1.0f, 1.0f,
1.0f, 1.0f, 1.0f,
1.0f, 1.0f, -1.0f,
1.0f, -1.0f, -1.0f,
-1.0f, -1.0f, 1.0f,
-1.0f, 1.0f, 1.0f,
1.0f, 1.0f, 1.0f,
1.0f, 1.0f, 1.0f,
1.0f, -1.0f, 1.0f,
-1.0f, -1.0f, 1.0f,
-1.0f, 1.0f, -1.0f,
1.0f, 1.0f, -1.0f,
1.0f, 1.0f, 1.0f,
1.0f, 1.0f, 1.0f,
-1.0f, 1.0f, 1.0f,
-1.0f, 1.0f, -1.0f,
-1.0f, -1.0f, -1.0f,
-1.0f, -1.0f, 1.0f,
1.0f, -1.0f, -1.0f,
1.0f, -1.0f, -1.0f,
-1.0f, -1.0f, 1.0f,
1.0f, -1.0f, 1.0f
};
glGenVertexArrays(1, &g_skyboxVAO);
glGenBuffers(1, &g_skyboxVBO);
glBindVertexArray(g_skyboxVAO);
glBindBuffer(GL_ARRAY_BUFFER, g_skyboxVBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(skyboxVertices), &skyboxVertices, GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
glBindVertexArray(0);
// Create default meshes
CreateDefaultCubeMesh();
CreateSphereMesh();
CreatePlaneMesh();
CreateCylinderMesh();
// Create framebuffer
CreateFramebuffer(g_viewportWidth, g_viewportHeight);
// Enable depth testing
glEnable(GL_DEPTH_TEST);
glEnable(GL_CULL_FACE);
glCullFace(GL_BACK);
LogToConsole("OpenGL initialise avec succes");
}
void InitLua() {
#ifndef NO_LUA
g_luaState = luaL_newstate();
luaL_openlibs(g_luaState);
// Register print function
lua_pushcfunction(g_luaState, [](lua_State* L) -> int {
const char* msg = luaL_checkstring(L, 1);
LogToConsole("Lua: " + std::string(msg));
return 0;
});
lua_setglobal(g_luaState, "cpp_print");
LogToConsole("Lua initialise avec succes");
#else
LogToConsole("Lua desactive");
g_luaState = nullptr;
#endif
}
void ProcessInput(float deltaTime) {
float velocity = g_camera.speed * deltaTime;
if (g_input.keys[SDL_SCANCODE_W] || g_input.keys[SDL_SCANCODE_Z]) {
g_camera.position += g_camera.front * velocity;
}
if (g_input.keys[SDL_SCANCODE_S]) {
g_camera.position -= g_camera.front * velocity;
}
if (g_input.keys[SDL_SCANCODE_A] || g_input.keys[SDL_SCANCODE_Q]) {
g_camera.position -= g_camera.right * velocity;
}
if (g_input.keys[SDL_SCANCODE_D]) {
g_camera.position += g_camera.right * velocity;
}
if (g_input.keys[SDL_SCANCODE_SPACE]) {
g_camera.position += g_camera.up * velocity;
}
if (g_input.keys[SDL_SCANCODE_LSHIFT]) {
g_camera.position -= g_camera.up * velocity;
}
// Gizmo shortcuts
if (g_input.keys[SDL_SCANCODE_G]) g_gizmoOperation = ImGuizmo::TRANSLATE;
if (g_input.keys[SDL_SCANCODE_R]) g_gizmoOperation = ImGuizmo::ROTATE;
if (g_input.keys[SDL_SCANCODE_T]) g_gizmoOperation = ImGuizmo::SCALE;
// Delete selected object
if (g_input.keys[SDL_SCANCODE_DELETE] && g_selectedIndex >= 0) {
g_sceneObjects.erase(g_sceneObjects.begin() + g_selectedIndex);
g_selectedIndex = -1;
LogToConsole("Objet supprime");
}
if (g_input.rightMousePressed) {
if (g_input.firstMouse) {
g_input.lastMouseX = g_input.mouseX;
g_input.lastMouseY = g_input.mouseY;
g_input.firstMouse = false;
}
float xoffset = g_input.mouseX - g_input.lastMouseX;
float yoffset = g_input.lastMouseY - g_input.mouseY;
g_input.lastMouseX = g_input.mouseX;
g_input.lastMouseY = g_input.mouseY;
xoffset *= g_camera.sensitivity;
yoffset *= g_camera.sensitivity;
g_camera.yaw += xoffset;
g_camera.pitch += yoffset;
if (g_camera.pitch > 89.0f) g_camera.pitch = 89.0f;
if (g_camera.pitch < -89.0f) g_camera.pitch = -89.0f;
g_camera.updateVectors();
} else {
g_input.firstMouse = true;
}
}
// Ajoutez les autres fonctions UI et de rendu ici...
// (Le code est très long, je peux continuer avec les fonctions spécifiques si nécessaire)
void CreateObject(ObjectType type) {
auto obj = std::make_unique<SceneObject>();
switch (type) {
case ObjectType::CUBE:
obj->name = "Cube_" + std::to_string(g_objectCounter++);
obj->color = glm::vec3(0.7f, 0.7f, 1.0f);
break;
case ObjectType::SPHERE:
obj->name = "Sphere_" + std::to_string(g_objectCounter++);
obj->color = glm::vec3(1.0f, 0.7f, 0.7f);
break;
case ObjectType::PLANE:
obj->name = "Plane_" + std::to_string(g_objectCounter++);
obj->color = glm::vec3(0.7f, 1.0f, 0.7f);
obj->scale = glm::vec3(5.0f, 0.1f, 5.0f);
break;
case ObjectType::CYLINDER:
obj->name = "Cylinder_" + std::to_string(g_objectCounter++);
obj->color = glm::vec3(0.7f, 1.0f, 1.0f);
break;
case ObjectType::LIGHT:
obj->name = "Light_" + std::to_string(g_objectCounter++);
obj->color = glm::vec3(1.0f, 1.0f, 0.7f);
obj->scale = glm::vec3(0.2f, 0.2f, 0.2f);
obj->lightIntensity = 1.0f;
obj->lightRange = 10.0f;
obj->emission = 2.0f;
break;
case ObjectType::MODEL:
obj->name = "Model_" + std::to_string(g_objectCounter++);
obj->color = glm::vec3(0.8f, 0.8f, 0.8f);
break;
case ObjectType::EMPTY:
obj->name = "Empty_" + std::to_string(g_objectCounter++);
obj->color = glm::vec3(0.5f, 0.5f, 0.5f);
obj->scale = glm::vec3(0.1f, 0.1f, 0.1f);
break;
}
obj->type = type;
obj->updateTransform();
g_sceneObjects.push_back(std::move(obj));
LogToConsole("Objet cree: " + g_sceneObjects.back()->name);
}
void ImportTexture() {
std::string path = OpenFileDialog("Image Files\0*.png;*.jpg;*.jpeg;*.bmp;*.tga\0All Files\0*.*\0");
if (!path.empty()) {
GLuint textureID = LoadTexture(path);
if (textureID != 0) {
std::filesystem::path filePath(path);
std::string textureName = filePath.stem().string();
int counter = 1;
std::string originalName = textureName;
while (g_textures.find(textureName) != g_textures.end()) {
textureName = originalName + "_" + std::to_string(counter++);
}
g_textures[textureName] = textureID;
LogToConsole("Texture importee: " + textureName);
}
}
}
// Stub implementations pour les fonctions UI manquantes
void RenderMenuBar() {
if (ImGui::BeginMenuBar()) {
if (ImGui::BeginMenu("File")) {
if (ImGui::MenuItem("New Scene", "Ctrl+N")) {
g_sceneObjects.clear();
g_selectedIndex = -1;
g_objectCounter = 0;
LogToConsole("Nouvelle scene creee");
}
ImGui::EndMenu();
}
if (ImGui::BeginMenu("Create")) {
if (ImGui::MenuItem("Cube")) CreateObject(ObjectType::CUBE);
if (ImGui::MenuItem("Sphere")) CreateObject(ObjectType::SPHERE);
if (ImGui::MenuItem("Plane")) CreateObject(ObjectType::PLANE);
if (ImGui::MenuItem("Cylinder")) CreateObject(ObjectType::CYLINDER);
ImGui::Separator();
if (ImGui::MenuItem("Light")) CreateObject(ObjectType::LIGHT);
if (ImGui::MenuItem("Empty")) CreateObject(ObjectType::EMPTY);
ImGui::EndMenu();
}
ImGui::EndMenuBar();
}
}
void RenderSceneHierarchy() {
if (!g_showSceneHierarchy) return;
ImGui::Begin("Scene Hierarchy", &g_showSceneHierarchy);
// Quick create buttons
if (ImGui::Button("Cube")) CreateObject(ObjectType::CUBE);
ImGui::SameLine();
if (ImGui::Button("Sphere")) CreateObject(ObjectType::SPHERE);
ImGui::Separator();
for (size_t i = 0; i < g_sceneObjects.size(); ++i) {
auto& obj = g_sceneObjects[i];
ImGuiTreeNodeFlags flags = ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_NoTreePushOnOpen;
if (g_selectedIndex == static_cast<int>(i)) {
flags |= ImGuiTreeNodeFlags_Selected;
}
ImGui::TreeNodeEx((void*)(intptr_t)i, flags, "%s", obj->name.c_str());
if (ImGui::IsItemClicked()) {
g_selectedIndex = static_cast<int>(i);
obj->selected = true;
for (size_t j = 0; j < g_sceneObjects.size(); ++j) {
if (j != i) g_sceneObjects[j]->selected = false;
}
}
}
if (g_sceneObjects.empty()) {
ImGui::TextColored(ImVec4(0.5f, 0.5f, 0.5f, 1.0f), "Scene vide");
}
ImGui::End();
}
void RenderProperties() {
if (!g_showProperties) return;
ImGui::Begin("Properties", &g_showProperties);
if (g_selectedIndex >= 0 && g_selectedIndex < static_cast<int>(g_sceneObjects.size())) {
auto& obj = g_sceneObjects[g_selectedIndex];
// Transform
if (ImGui::CollapsingHeader("Transform", ImGuiTreeNodeFlags_DefaultOpen)) {
bool transformChanged = false;
transformChanged |= ImGui::DragFloat3("Position", glm::value_ptr(obj->position), 0.1f);
transformChanged |= ImGui::DragFloat3("Rotation", glm::value_ptr(obj->rotation), 1.0f);
transformChanged |= ImGui::DragFloat3("Scale", glm::value_ptr(obj->scale), 0.01f, 0.001f, 10.0f);
if (transformChanged) {
obj->updateTransform();
}
}
// Material
if (ImGui::CollapsingHeader("Material")) {
ImGui::ColorEdit3("Color", glm::value_ptr(obj->color));
ImGui::SliderFloat("Roughness", &obj->roughness, 0.0f, 1.0f);
ImGui::SliderFloat("Metallic", &obj->metallic, 0.0f, 1.0f);
ImGui::SliderFloat("Emission", &obj->emission, 0.0f, 5.0f);
}
} else {
ImGui::TextColored(ImVec4(0.5f, 0.5f, 0.5f, 1.0f), "Aucun objet selectionne");
}
ImGui::End();
}
void RenderAssetBrowser() {
if (!g_showAssetBrowser) return;
ImGui::Begin("Asset Browser", &g_showAssetBrowser);
if (ImGui::Button("Import Texture...")) {
ImportTexture();
}
ImGui::Separator();
if (ImGui::CollapsingHeader("Textures", ImGuiTreeNodeFlags_DefaultOpen)) {
if (g_textures.empty()) {
ImGui::TextColored(ImVec4(0.5f, 0.5f, 0.5f, 1.0f), "No textures loaded");
} else {
for (const auto& tex : g_textures) {
ImGui::Text("%s", tex.first.c_str());
}
}
}
ImGui::End();
}
void RenderTextureManager() {
if (!g_showTextureManager) return;
ImGui::Begin("Texture Manager", &g_showTextureManager);
if (ImGui::Button("Import Texture...")) {
ImportTexture();
}
ImGui::Separator();
ImGui::Text("Textures disponibles (%zu):", g_textures.size());
for (const auto& tex : g_textures) {
ImGui::Text("%s", tex.first.c_str());
}
if (g_textures.empty()) {
ImGui::TextColored(ImVec4(0.5f, 0.5f, 0.5f, 1.0f), "Aucune texture chargee");
}
ImGui::End();
}
void RenderConsole() {
if (!g_showConsole) return;
ImGui::Begin("Console", &g_showConsole);
if (ImGui::Button("Clear")) {
g_consoleLog.clear();
}
ImGui::Separator();
// Console output
ImGui::BeginChild("ScrollingRegion", ImVec2(0, -35), false, ImGuiWindowFlags_HorizontalScrollbar);
for (const auto& line : g_consoleLog) {
ImVec4 color = ImVec4(1.0f, 1.0f, 1.0f, 1.0f);
if (line.find("Erreur") != std::string::npos) {
color = ImVec4(1.0f, 0.4f, 0.4f, 1.0f);
} else if (line.find("Lua:") != std::string::npos) {
color = ImVec4(0.4f, 1.0f, 0.4f, 1.0f);
}
ImGui::TextColored(color, "%s", line.c_str());
}
ImGui::EndChild();
// Console input
ImGui::Separator();
static char inputBuffer[256] = "";
if (ImGui::InputText("##ConsoleInput", inputBuffer, sizeof(inputBuffer), ImGuiInputTextFlags_EnterReturnsTrue)) {
std::string command = std::string(inputBuffer);
if (!command.empty()) {
LogToConsole("> " + command);
if (command == "clear") {
g_consoleLog.clear();
} else if (command == "help") {
LogToConsole("Available commands: clear, help, stats");
} else if (command == "stats") {
LogToConsole("Objects: " + std::to_string(g_sceneObjects.size()));
LogToConsole("Textures: " + std::to_string(g_textures.size()));
}
memset(inputBuffer, 0, sizeof(inputBuffer));
}
}
ImGui::End();
}
void RenderSettings() {
if (!g_showSettings) return;
ImGui::Begin("Settings", &g_showSettings);
if (ImGui::CollapsingHeader("Graphics")) {
ImGui::Checkbox("VSync", &g_vsync);
ImGui::Checkbox("Wireframe", &g_wireframe);
ImGui::SliderFloat("Gamma", &g_gamma, 0.1f, 3.0f);
}
if (ImGui::CollapsingHeader("Camera")) {
ImGui::SliderFloat("Speed", &g_camera.speed, 1.0f, 20.0f);
ImGui::SliderFloat("Sensitivity", &g_camera.sensitivity, 0.01f, 1.0f);
ImGui::SliderFloat("FOV", &g_camera.fov, 30.0f, 120.0f);
}
ImGui::End();
}
void RenderStats() {
if (!g_showStats) return;
ImGui::Begin("Statistics", &g_showStats);
ImGui::Text("Performance");
ImGui::Separator();
ImGui::Text("Frame Time: %.3f ms", g_stats.frameTime * 1000.0f);
ImGui::Text("FPS: %.1f", g_stats.fps);
ImGui::Text("Draw Calls: %d", g_stats.drawCalls);
ImGui::Text("Vertices: %d", g_stats.vertices);
ImGui::Text("Triangles: %d", g_stats.triangles);
ImGui::Separator();
ImGui::Text("Scene");
ImGui::Text("Objects: %zu", g_sceneObjects.size());
ImGui::Text("Textures: %zu", g_textures.size());
ImGui::End();
}
void RenderViewport() {
if (!g_showViewport) return;
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f));
ImGui::Begin("Viewport", &g_showViewport);
ImVec2 viewportSize = ImGui::GetContentRegionAvail();
// Resize framebuffer if needed
if (viewportSize.x != g_viewportWidth || viewportSize.y != g_viewportHeight) {
if (viewportSize.x > 0 && viewportSize.y > 0) {
CreateFramebuffer(static_cast<int>(viewportSize.x), static_cast<int>(viewportSize.y));
}
}
// Render scene to framebuffer
glBindFramebuffer(GL_FRAMEBUFFER, g_framebuffer);
glViewport(0, 0, g_viewportWidth, g_viewportHeight);
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// Render scene
RenderScene();
glBindFramebuffer(GL_FRAMEBUFFER, 0);
// Display framebuffer texture in ImGui
ImGui::Image((void*)(intptr_t)g_colorTexture, viewportSize, ImVec2(0, 1), ImVec2(1, 0));
ImGui::End();
ImGui::PopStyleVar();
}
void RenderSkybox(const glm::mat4& view, const glm::mat4& projection) {
glDepthFunc(GL_LEQUAL);
glUseProgram(g_skyboxShader);
GLint viewLoc = glGetUniformLocation(g_skyboxShader, "view");
GLint projectionLoc = glGetUniformLocation(g_skyboxShader, "projection");
GLint topColorLoc = glGetUniformLocation(g_skyboxShader, "topColor");
GLint bottomColorLoc = glGetUniformLocation(g_skyboxShader, "bottomColor");
glUniformMatrix4fv(viewLoc, 1, GL_FALSE, glm::value_ptr(view));
glUniformMatrix4fv(projectionLoc, 1, GL_FALSE, glm::value_ptr(projection));
glUniform3fv(topColorLoc, 1, glm::value_ptr(g_skyboxTopColor));
glUniform3fv(bottomColorLoc, 1, glm::value_ptr(g_skyboxBottomColor));
glBindVertexArray(g_skyboxVAO);
glDrawArrays(GL_TRIANGLES, 0, 36);
glBindVertexArray(0);
glDepthFunc(GL_LESS);
}
Mesh* GetMeshForObjectType(ObjectType type) {
switch (type) {
case ObjectType::CUBE:
default:
return g_defaultCubeMesh.get();
case ObjectType::SPHERE:
return g_defaultSphereMesh.get();
case ObjectType::PLANE:
return g_defaultPlaneMesh.get();
case ObjectType::CYLINDER:
return g_defaultCylinderMesh.get();
case ObjectType::LIGHT:
return g_defaultCubeMesh.get();
case ObjectType::EMPTY:
return g_defaultCubeMesh.get();
}
}
void RenderScene() {
// Clear stats
g_stats.drawCalls = 0;
g_stats.vertices = 0;
g_stats.triangles = 0;
glEnable(GL_DEPTH_TEST);
glEnable(GL_CULL_FACE);
// Calculate matrices
float aspectRatio = (float)g_viewportWidth / (float)g_viewportHeight;
glm::mat4 view = g_camera.getViewMatrix();
glm::mat4 projection = g_camera.getProjectionMatrix(aspectRatio);
// Render skybox first
RenderSkybox(view, projection);
// Use main shader
glUseProgram(g_shaderProgram);
// Set common uniforms
GLint viewLoc = glGetUniformLocation(g_shaderProgram, "view");
GLint projectionLoc = glGetUniformLocation(g_shaderProgram, "projection");
GLint lightPosLoc = glGetUniformLocation(g_shaderProgram, "lightPos");
GLint lightColorLoc = glGetUniformLocation(g_shaderProgram, "lightColor");
GLint viewPosLoc = glGetUniformLocation(g_shaderProgram, "viewPos");
glUniformMatrix4fv(viewLoc, 1, GL_FALSE, glm::value_ptr(view));
glUniformMatrix4fv(projectionLoc, 1, GL_FALSE, glm::value_ptr(projection));
glUniform3fv(viewPosLoc, 1, glm::value_ptr(g_camera.position));
// Find first light for basic lighting
glm::vec3 lightPos = glm::vec3(5.0f, 5.0f, 5.0f);
glm::vec3 lightColor = glm::vec3(1.0f, 1.0f, 1.0f);
for (const auto& obj : g_sceneObjects) {
if (obj->type == ObjectType::LIGHT && obj->visible) {
lightPos = obj->position;
lightColor = obj->color * obj->lightIntensity;
break;
}
}
glUniform3fv(lightPosLoc, 1, glm::value_ptr(lightPos));
glUniform3fv(lightColorLoc, 1, glm::value_ptr(lightColor));
// Render all objects
for (const auto& obj : g_sceneObjects) {
if (!obj->visible) continue;
// Set object-specific uniforms
GLint modelLoc = glGetUniformLocation(g_shaderProgram, "model");
GLint colorLoc = glGetUniformLocation(g_shaderProgram, "objectColor");
GLint selectedLoc = glGetUniformLocation(g_shaderProgram, "isSelected");
GLint wireframeLoc = glGetUniformLocation(g_shaderProgram, "isWireframe");
GLint hasTextureLoc = glGetUniformLocation(g_shaderProgram, "hasTexture");
GLint roughnessLoc = glGetUniformLocation(g_shaderProgram, "roughness");
GLint metallicLoc = glGetUniformLocation(g_shaderProgram, "metallic");
GLint emissionLoc = glGetUniformLocation(g_shaderProgram, "emission");
glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(obj->transform));
glUniform3fv(colorLoc, 1, glm::value_ptr(obj->color));
glUniform1i(selectedLoc, obj->selected ? 1 : 0);
glUniform1i(wireframeLoc, g_wireframe ? 1 : 0);
glUniform1f(roughnessLoc, obj->roughness);
glUniform1f(metallicLoc, obj->metallic);
glUniform1f(emissionLoc, obj->emission);
// Bind texture if available
bool hasTexture = false;
if (!obj->textureName.empty() && g_textures.find(obj->textureName) != g_textures.end()) {
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, g_textures[obj->textureName]);
glUniform1i(glGetUniformLocation(g_shaderProgram, "texture1"), 0);
hasTexture = true;
}
glUniform1i(hasTextureLoc, hasTexture ? 1 : 0);
// Set wireframe mode
if (g_wireframe) {
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
} else {
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
}
// Render appropriate mesh
if (obj->type == ObjectType::MODEL && !obj->meshes.empty()) {
for (const auto& mesh : obj->meshes) {
mesh->draw();
g_stats.drawCalls++;
g_stats.vertices += static_cast<int>(mesh->vertices.size());
g_stats.triangles += static_cast<int>(mesh->indices.size()) / 3;
}
} else {
Mesh* mesh = GetMeshForObjectType(obj->type);
if (mesh) {
mesh->draw();
g_stats.drawCalls++;
g_stats.vertices += static_cast<int>(mesh->vertices.size());
g_stats.triangles += static_cast<int>(mesh->indices.size()) / 3;
}
}
}
// Reset polygon mode
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
}
// Assimp model loading functions
std::unique_ptr<Mesh> ProcessMesh(aiMesh* mesh, const aiScene* scene) {
auto newMesh = std::make_unique<Mesh>();
for (unsigned int i = 0; i < mesh->mNumVertices; i++) {
Vertex vertex;
vertex.position.x = mesh->mVertices[i].x;
vertex.position.y = mesh->mVertices[i].y;
vertex.position.z = mesh->mVertices[i].z;
if (mesh->HasNormals()) {
vertex.normal.x = mesh->mNormals[i].x;
vertex.normal.y = mesh->mNormals[i].y;
vertex.normal.z = mesh->mNormals[i].z;
} else {
vertex.normal = glm::vec3(0.0f, 1.0f, 0.0f);
}
if (mesh->mTextureCoords[0]) {
vertex.texCoords.x = mesh->mTextureCoords[0][i].x;
vertex.texCoords.y = mesh->mTextureCoords[0][i].y;
} else {
vertex.texCoords = glm::vec2(0.0f, 0.0f);
}
newMesh->vertices.push_back(vertex);
}
for (unsigned int i = 0; i < mesh->mNumFaces; i++) {
aiFace face = mesh->mFaces[i];
for (unsigned int j = 0; j < face.mNumIndices; j++) {
newMesh->indices.push_back(face.mIndices[j]);
}
}
newMesh->setupMesh();
return newMesh;
}
void ProcessNode(aiNode* node, const aiScene* scene, std::vector<std::unique_ptr<Mesh>>& meshes) {
for (unsigned int i = 0; i < node->mNumMeshes; i++) {
aiMesh* mesh = scene->mMeshes[node->mMeshes[i]];
meshes.push_back(ProcessMesh(mesh, scene));
}
for (unsigned int i = 0; i < node->mNumChildren; i++) {
ProcessNode(node->mChildren[i], scene, meshes);
}
}
bool LoadModel(const std::string& path, SceneObject* obj) {
Assimp::Importer importer;
const aiScene* scene = importer.ReadFile(path,
aiProcess_Triangulate |
aiProcess_FlipUVs |
aiProcess_CalcTangentSpace |
aiProcess_GenNormals);
if (!scene || scene->mFlags & AI_SCENE_FLAGS_INCOMPLETE || !scene->mRootNode) {
LogToConsole("Erreur Assimp: " + std::string(importer.GetErrorString()));
return false;
}
obj->meshes.clear();
ProcessNode(scene->mRootNode, scene, obj->meshes);
LogToConsole("Modele charge: " + path + " (" + std::to_string(obj->meshes.size()) + " meshes)");
return true;
}
void ImportModel() {
std::string path = OpenFileDialog("3D Models\0*.obj;*.fbx;*.dae;*.3ds;*.blend;*.ply\0All Files\0*.*\0");
if (!path.empty()) {
CreateObject(ObjectType::MODEL);
auto& obj = g_sceneObjects.back();
obj->modelPath = path;
std::filesystem::path filePath(path);
obj->name = filePath.stem().string() + "_" + std::to_string(g_objectCounter - 1);
if (LoadModel(path, obj.get())) {
LogToConsole("Modele importe avec succes: " + path);
} else {
LogToConsole("Echec import modele: " + path);
g_sceneObjects.pop_back();
g_objectCounter--;
}
}
}UTTONDOWN:
if (event.button.button < 8) {
g_input.mouseButtons[event.button.button] = true;
if (event.button.button == SDL_BUTTON_RIGHT) {
g_input.rightMousePressed = true;
}
}
break;
case SDL_MOUSEB
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment