2025-01-05 13:17:26 +00:00
# include <GL/glew.h> // Include GLEW before <SDL2/SDL.h>?
2025-01-05 01:30:18 +00:00
# include "sdl.hpp"
2025-01-11 17:48:48 +00:00
# include "VertexBufferLayout.h"
2025-01-07 23:34:46 +00:00
# include "colormod.h"
2025-01-08 22:04:07 +00:00
# include <SDL2/SDL_video.h>
2025-01-06 18:32:22 +00:00
# include <alloca.h>
2025-01-06 20:01:22 +00:00
# include <cstdio>
2025-01-05 01:30:18 +00:00
# include <iostream>
2025-01-06 20:01:22 +00:00
# include <string>
# include <fstream>
2025-01-06 18:32:22 +00:00
//#include <iterator>
# include <ostream>
# include <string>
2025-01-06 20:01:22 +00:00
# include <sstream>
2025-01-07 23:34:46 +00:00
2025-01-11 18:56:38 +00:00
# include "Renderer.h"
# include "VertexBuffer.h"
# include "VertexArray.h"
# include "IndexBuffer.h"
2025-01-07 23:34:46 +00:00
# if defined(_MSC_VER) // Microsoft Visual C++
# include <intrin.h>
# define DEBUG_BREAK() __debugbreak()
# elif defined(__i386__) || defined(__x86_64__)
// Use inline assembly for x86/x86_64
# define DEBUG_BREAK() __asm__ volatile("int3")
# else
// Fallback on non-x86 platforms
# include <signal.h>
# define DEBUG_BREAK() raise(SIGTRAP)
# endif
// ASSERT macro that shows file, line, and the failed expression
# define ASSERT(x) \
do { \
if ( ! ( x ) ) { \
std : : cerr < < " Assertion Failed: " < < # x < < ' \n ' \
< < " File: " < < __FILE__ < < ' \n ' \
< < " Line: " < < __LINE__ < < std : : endl ; \
DEBUG_BREAK ( ) ; \
} \
} while ( false )
# define GLCall(x) GLClearError();\
x ; \
ASSERT ( GLLogCall ( ) )
2025-01-06 20:01:22 +00:00
struct ShaderProgramSource {
std : : string VertexSource , FragmentSource ;
} ;
2025-01-05 01:30:18 +00:00
SdlWindow : : SdlWindow ( const char * title , int width , int height )
: m_window ( nullptr ) ,
m_renderer ( nullptr ) ,
m_isRunning ( false ) ,
m_isFullscreen ( false ) ,
m_width ( width ) ,
2025-01-05 02:27:36 +00:00
m_height ( height ) ,
2025-01-05 13:17:26 +00:00
m_glContext ( nullptr ) ,
m_windowedWidth ( width ) ,
2025-01-08 22:04:07 +00:00
m_windowedHeight ( height ) ,
2025-01-11 17:48:48 +00:00
r ( 0.5f ) ,
2025-01-08 22:04:07 +00:00
location ( ) ,
2025-01-11 17:48:48 +00:00
increment ( 0.05f ) ,
2025-01-11 18:56:38 +00:00
ib ( nullptr , 6 )
2025-01-05 01:30:18 +00:00
{
2025-01-05 02:27:36 +00:00
// 1. Set attributes
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 ) ;
2025-01-05 01:30:18 +00:00
2025-01-05 02:41:40 +00:00
// 2. Create the window with OpenGL flag
m_window = SDL_CreateWindow ( title ,
SDL_WINDOWPOS_CENTERED ,
SDL_WINDOWPOS_CENTERED ,
width ,
height ,
2025-01-05 02:52:38 +00:00
SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE ) ;
2025-01-05 02:41:40 +00:00
2025-01-05 01:30:18 +00:00
if ( ! m_window ) {
std : : cerr < < " Failed to create window: " < < SDL_GetError ( ) < < std : : endl ;
return ;
}
2025-01-05 02:41:40 +00:00
// 3. Create OpenGL context
m_glContext = SDL_GL_CreateContext ( m_window ) ;
if ( ! m_glContext ) {
std : : cerr < < " Failed to create GL context: " < < SDL_GetError ( ) < < std : : endl ;
return ;
}
// 4. Optionally init GLEW (if not on macOS core profile)
# ifndef __APPLE__
glewExperimental = GL_TRUE ;
2025-01-05 13:17:26 +00:00
GLenum glewErr = glewInit ( ) ;
2025-01-05 02:41:40 +00:00
if ( glewInit ( ) ! = GLEW_OK ) {
2025-01-05 13:17:26 +00:00
std : : cerr < < " Failed to init GLEW " < < glewGetErrorString ( glewErr ) < < std : : endl ;
2025-01-05 02:41:40 +00:00
return ;
2025-01-05 01:30:18 +00:00
}
2025-01-05 13:17:26 +00:00
glGetError ( ) ;
2025-01-05 02:41:40 +00:00
# endif
2025-01-05 01:30:18 +00:00
2025-01-05 02:41:40 +00:00
// 5. Set vsync (optional)
2025-01-08 22:04:07 +00:00
SDL_GL_SetSwapInterval ( 1 ) ; //Vsync
2025-01-11 17:48:48 +00:00
SdlWindow : : GLClearError ( ) ;
2025-01-05 02:41:40 +00:00
// 6. Mark as running
m_isRunning = true ;
2025-01-05 13:51:33 +00:00
2025-01-06 20:30:18 +00:00
float positions [ ] = { // vertex for a triangle
2025-01-05 14:12:28 +00:00
//x,y
2025-01-06 20:30:18 +00:00
- 0.5f , - 0.5f , // 0
0.5f , - 0.5f , // 1
0.5f , 0.5f , //2
- 0.5f , 0.5f // 3
2025-01-05 13:51:33 +00:00
} ;
2025-01-06 20:30:18 +00:00
unsigned int indices [ ] = {
0 , 1 , 2 ,
2 , 3 , 0
} ;
2025-01-11 17:48:48 +00:00
2025-01-11 18:56:38 +00:00
//unsigned int vao; //vertext array object
//GLCall(glGenVertexArrays(1, &vao));
//GLCall(glBindVertexArray(vao));
2025-01-11 17:48:48 +00:00
VertexArray va ;
VertexBuffer vb ( positions , 4 * 2 * sizeof ( float ) ) ;
2025-01-11 18:56:38 +00:00
ib = IndexBuffer ( indices , 6 ) ;
2025-01-11 17:48:48 +00:00
VertexBufferLayout layout ;
layout . Push < float > ( 2 ) ;
2025-01-11 18:56:38 +00:00
2025-01-11 17:48:48 +00:00
va . AddBuffer ( vb , layout ) ;
2025-01-06 20:01:22 +00:00
ShaderProgramSource source = parseShader ( " res/shaders/Basic.shader " ) ;
2025-01-11 18:56:38 +00:00
std : : cout < < " VERTEX " < < std : : endl < < source . VertexSource < < std : : endl ;
std : : cout < < " FRAGMENT " < < std : : endl < < source . FragmentSource < < std : : endl ;
2025-01-11 17:48:48 +00:00
unsigned int m_ShaderID = createShader ( source . VertexSource , source . FragmentSource ) ;
GLCall ( glUseProgram ( m_ShaderID ) ) ;
2025-01-08 22:04:07 +00:00
2025-01-11 18:56:38 +00:00
GLCall ( unsigned int location = glGetUniformLocation ( m_ShaderID , " u_Color " ) ) ;
2025-01-08 22:04:07 +00:00
ASSERT ( location ! = - 1 ) ; // -1 is an error
2025-01-11 18:56:38 +00:00
2025-01-08 22:04:07 +00:00
GLCall ( glUniform4f ( location , 0.8f , 0.3f , 0.8f , 1.0f ) ) ;
2025-01-11 17:48:48 +00:00
GLCall ( glBindVertexArray ( 0 ) ) ;
GLCall ( glUseProgram ( 0 ) ) ;
GLCall ( glBindBuffer ( GL_ARRAY_BUFFER , 0 ) ) ;
GLCall ( glBindBuffer ( GL_ELEMENT_ARRAY_BUFFER , 0 ) ) ;
2025-01-05 14:12:28 +00:00
2025-01-05 01:30:18 +00:00
}
SdlWindow : : ~ SdlWindow ( ) {
2025-01-05 02:41:40 +00:00
// If using SDL Renderer, destroy it. But if you’ re purely using OpenGL, you might remove it.
2025-01-05 01:30:18 +00:00
if ( m_renderer ) {
2025-01-05 02:41:40 +00:00
SDL_DestroyRenderer ( m_renderer ) ;
m_renderer = nullptr ;
2025-01-05 01:30:18 +00:00
}
2025-01-05 02:41:40 +00:00
// Delete the OpenGL context
if ( m_glContext ) {
SDL_GL_DeleteContext ( m_glContext ) ;
m_glContext = nullptr ;
}
2025-01-05 01:30:18 +00:00
if ( m_window ) {
2025-01-05 02:41:40 +00:00
SDL_DestroyWindow ( m_window ) ;
m_window = nullptr ;
2025-01-05 01:30:18 +00:00
}
2025-01-11 17:48:48 +00:00
if ( shader ) {
glDeleteProgram ( shader ) ;
shader = 0 ;
}
2025-01-05 02:41:40 +00:00
2025-01-05 01:30:18 +00:00
SDL_Quit ( ) ;
}
2025-01-05 02:41:40 +00:00
2025-01-05 01:30:18 +00:00
void SdlWindow : : run ( ) {
while ( m_isRunning ) {
processEvents ( ) ;
update ( ) ;
render ( ) ;
}
2025-01-07 23:34:46 +00:00
GLCall ( glDeleteProgram ( shader ) ) ;
2025-01-05 01:30:18 +00:00
}
void SdlWindow : : processEvents ( ) {
SDL_Event event ;
while ( SDL_PollEvent ( & event ) ) {
if ( event . type = = SDL_QUIT ) {
m_isRunning = false ;
}
// Handle other events (keyboard, mouse, etc.) here if needed
else if ( event . type = = SDL_KEYDOWN ) {
// Check if the key pressed is Enter/Return
if ( event . key . keysym . sym = = SDLK_RETURN ) {
std : : cout < < " input detected " < < std : : endl ;
}
if ( event . key . keysym . sym = = SDLK_SPACE ) {
std : : cout < < " Full screen togled! " < < std : : endl ;
setFullscreen ( ! m_isFullscreen ) ;
}
if ( event . key . keysym . sym = = SDLK_ESCAPE ) {
std : : cout < < " Bye! " < < std : : endl ;
m_isRunning = false ; //exit application
}
}
2025-01-05 02:52:38 +00:00
else if ( event . type = = SDL_WINDOWEVENT ) {
if ( event . window . event = = SDL_WINDOWEVENT_RESIZED ) {
// SDL gives the new width/height in event.window.data1/data2
int newWidth = event . window . data1 ;
int newHeight = event . window . data2 ;
// Update your internal width/height (if you keep track)
m_width = newWidth ;
m_height = newHeight ;
2025-01-05 13:17:26 +00:00
// If we are in windowed mode, update the "windowed" size.
if ( ! m_isFullscreen ) {
m_windowedWidth = newWidth ;
m_windowedHeight = newHeight ;
}
2025-01-05 02:52:38 +00:00
// Update the OpenGL viewport
2025-01-07 23:34:46 +00:00
GLCall ( glViewport ( 0 , 0 , newWidth , newHeight ) ) ;
2025-01-05 02:52:38 +00:00
// (Optional) If you have a projection matrix, update it here as well
// e.g., recalc the aspect ratio for a perspective projection
}
}
2025-01-05 01:30:18 +00:00
}
}
void SdlWindow : : update ( ) {
// Update game/application logic here
2025-01-08 22:04:07 +00:00
if ( r > 1.0f ) {
increment = - 0.05f ;
} else if ( r < 0.0f ) {
increment = 0.05f ;
}
r + = increment ;
2025-01-05 01:30:18 +00:00
}
void SdlWindow : : render ( ) {
2025-01-05 02:41:40 +00:00
// Use GL calls instead of SDL’ s renderer
2025-01-08 22:04:07 +00:00
GLCall ( glClearColor ( 0.0f , 0.0f , 0.0f , 1.0f ) ) ; //background
2025-01-11 17:48:48 +00:00
//
2025-01-07 23:34:46 +00:00
GLCall ( glClear ( GL_COLOR_BUFFER_BIT ) ) ;
2025-01-11 17:48:48 +00:00
GLCall ( glUseProgram ( shader ) ) ;
GLCall ( glUniform4f ( location , r , 0.3f , 0.8f , 1.0f ) ) ;
2025-01-05 01:30:18 +00:00
2025-01-11 17:48:48 +00:00
va . Bind ( ) ;
ib . Bind ( ) ;
2025-01-05 02:41:40 +00:00
// TODO: Draw with OpenGL here (shaders, triangles, etc.)
2025-01-07 23:34:46 +00:00
//glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, nullptr);
GLCall ( glDrawElements ( GL_TRIANGLES , 6 , GL_UNSIGNED_INT , nullptr ) ) ; //macro assert for debugging
2025-01-08 22:04:07 +00:00
2025-01-05 02:41:40 +00:00
// Swap buffers
SDL_GL_SwapWindow ( m_window ) ;
2025-01-05 01:30:18 +00:00
}
void SdlWindow : : setFullscreen ( bool fullscreen ) {
if ( m_window ) {
m_isFullscreen = fullscreen ;
if ( m_isFullscreen ) {
2025-01-05 13:17:26 +00:00
// Going TO fullscreen:
// (Optional) store the current size again, in case it changed
m_windowedWidth = m_width ;
m_windowedHeight = m_height ;
2025-01-05 01:30:18 +00:00
SDL_SetWindowFullscreen ( m_window , SDL_WINDOW_FULLSCREEN_DESKTOP ) ;
} else {
2025-01-05 13:17:26 +00:00
// Returning FROM fullscreen:
2025-01-05 01:30:18 +00:00
SDL_SetWindowFullscreen ( m_window , 0 ) ; // return to windowed
2025-01-05 13:17:26 +00:00
// Now restore the window’ s old size
SDL_SetWindowSize ( m_window , m_windowedWidth , m_windowedHeight ) ;
// Update m_width, m_height so they reflect the new (restored) size
m_width = m_windowedWidth ;
m_height = m_windowedHeight ;
2025-01-05 01:30:18 +00:00
}
}
}
2025-01-05 13:17:26 +00:00
2025-01-06 18:32:22 +00:00
unsigned int SdlWindow : : compileShader ( unsigned int type , const std : : string & source ) {
2025-01-07 23:34:46 +00:00
GLCall ( unsigned int id = glCreateShader ( type ) ) ;
2025-01-06 18:32:22 +00:00
const char * src = source . c_str ( ) ; // <--- this string needs to exist when compiling/running
2025-01-07 23:34:46 +00:00
GLCall ( glShaderSource ( id , 1 , & src , nullptr ) ) ;
GLCall ( glCompileShader ( id ) ) ;
2025-01-06 18:32:22 +00:00
//TODO: error handling
int result ;
2025-01-07 23:34:46 +00:00
GLCall ( glGetShaderiv ( id , GL_COMPILE_STATUS , & result ) ) ;
2025-01-06 18:32:22 +00:00
if ( result = = GL_FALSE ) {
int length ;
2025-01-07 23:34:46 +00:00
GLCall ( glGetShaderiv ( id , GL_INFO_LOG_LENGTH , & length ) ) ;
2025-01-06 18:32:22 +00:00
char * message = ( char * ) alloca ( length * sizeof ( char ) ) ; //do i need to deallocate this??
2025-01-07 23:34:46 +00:00
GLCall ( glGetShaderInfoLog ( id , length , & length , message ) ) ;
2025-01-06 18:38:21 +00:00
std : : cout < < " Failed to compile " < < ( type = = GL_VERTEX_SHADER ? " vertex " : " fragment " ) < < " shader " < < std : : endl ; // print out what type of shader it is
2025-01-06 18:32:22 +00:00
std : : cout < < message < < std : : endl ;
2025-01-07 23:34:46 +00:00
GLCall ( glDeleteShader ( id ) ) ;
2025-01-06 18:32:22 +00:00
return 0 ;
}
//
return id ;
}
unsigned int SdlWindow : : createShader ( const std : : string & vertexShader , const std : : string & fragmentShader ) {
2025-01-07 23:34:46 +00:00
GLCall ( unsigned int program = glCreateProgram ( ) ) ;
2025-01-06 18:32:22 +00:00
unsigned int vs = compileShader ( GL_VERTEX_SHADER , vertexShader ) ;
unsigned int fs = compileShader ( GL_FRAGMENT_SHADER , fragmentShader ) ;
2025-01-07 23:34:46 +00:00
GLCall ( glAttachShader ( program , vs ) ) ;
GLCall ( glAttachShader ( program , fs ) ) ;
2025-01-06 18:32:22 +00:00
2025-01-07 23:34:46 +00:00
GLCall ( glLinkProgram ( program ) ) ;
GLCall ( glValidateProgram ( program ) ) ;
2025-01-06 18:32:22 +00:00
2025-01-07 23:34:46 +00:00
GLCall ( glDeleteShader ( vs ) ) ;
GLCall ( glDeleteShader ( fs ) ) ;
2025-01-06 18:32:22 +00:00
return program ;
}
2025-01-06 20:01:22 +00:00
SdlWindow : : ShaderProgramSource SdlWindow : : parseShader ( const std : : string & filepath ) {
//can be changed to c standard which is a bit faster
std : : ifstream stream ( filepath ) ;
if ( ! stream . is_open ( ) ) {
std : : cerr < < " parseShader ERROR: Could not open file " < < filepath < < std : : endl ;
// Return empty (or handle however you prefer)
return { " " , " " } ;
}
enum class ShaderType {
NONE = - 1 , VERTEX = 0 , FRAGMENT = 1
} ;
std : : string line ;
std : : stringstream ss [ 2 ] ; //vertex - fragment
ShaderType type = ShaderType : : NONE ;
while ( getline ( stream , line ) ) {
if ( line . find ( " #shader " ) ! = std : : string : : npos ) {
if ( line . find ( " vertex " ) ! = std : : string : : npos ) {
type = ShaderType : : VERTEX ;
}
else if ( line . find ( " fragment " ) ! = std : : string : : npos ) {
type = ShaderType : : FRAGMENT ;
}
}
else {
ss [ ( int ) type ] < < line < < ' \n ' ;
}
}
return { ss [ 0 ] . str ( ) , ss [ 1 ] . str ( ) } ;
}
2025-01-07 23:34:46 +00:00
void SdlWindow : : GLClearError ( ) {
while ( glGetError ( ) ! = GL_NO_ERROR ) ;
}
bool SdlWindow : : GLLogCall ( ) {
Color : : Modifier red ( Color : : FG_RED ) ;
Color : : Modifier def ( Color : : FG_DEFAULT ) ;
while ( GLenum error = glGetError ( ) ) {
std : : cout < < red < < " [OpenGL Error] ( " < < error < < " ) " < < def < < std : : endl ; //if error, it will return a number, this needs to be converted to hex to then look up that value inn GL docs
return false ;
}
return true ;
}