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-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>
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 ) ,
m_windowedHeight ( height )
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)
SDL_GL_SetSwapInterval ( 1 ) ;
// 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-05 14:12:28 +00:00
//vertex buffer
2025-01-05 13:51:33 +00:00
unsigned int buffer ;
glGenBuffers ( 1 , & buffer ) ;
glBindBuffer ( GL_ARRAY_BUFFER , buffer ) ; //select buffer called 'buffer'
2025-01-06 20:30:18 +00:00
glBufferData ( GL_ARRAY_BUFFER , 6 * 2 * sizeof ( float ) , positions , GL_STATIC_DRAW ) ; // assigne buffer size, static as we use many times, but does not change
2025-01-05 14:12:28 +00:00
//vertext attributes / layout
glEnableVertexAttribArray ( 0 ) ;
glVertexAttribPointer ( 0 , 2 , GL_FLOAT , GL_FALSE , sizeof ( float ) * 2 , 0 ) ;
2025-01-06 20:30:18 +00:00
//indext beffer
unsigned int ibo ; //indext buffer object
glGenBuffers ( 1 , & ibo ) ;
glBindBuffer ( GL_ELEMENT_ARRAY_BUFFER , ibo ) ; //select buffer called 'buffer'
glBufferData ( GL_ELEMENT_ARRAY_BUFFER , 6 * sizeof ( unsigned int ) , indices , GL_STATIC_DRAW ) ; // assigne buffer size, static as we use many times, but does not change
2025-01-05 14:12:28 +00:00
2025-01-06 20:01:22 +00:00
ShaderProgramSource source = parseShader ( " res/shaders/Basic.shader " ) ;
unsigned int shader = createShader ( source . VertexSource , source . FragmentSource ) ;
2025-01-06 18:32:22 +00:00
glUseProgram ( shader ) ;
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-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-06 18:38:21 +00:00
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
glViewport ( 0 , 0 , newWidth , newHeight ) ;
// (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
}
void SdlWindow : : render ( ) {
2025-01-05 02:41:40 +00:00
// Use GL calls instead of SDL’ s renderer
glClearColor ( 0.0f , 0.0f , 0.0f , 1.0f ) ;
glClear ( GL_COLOR_BUFFER_BIT ) ;
2025-01-05 01:30:18 +00:00
2025-01-05 02:41:40 +00:00
// TODO: Draw with OpenGL here (shaders, triangles, etc.)
2025-01-06 20:30:18 +00:00
glDrawElements ( GL_TRIANGLES , 6 , GL_UNSIGNED_INT , nullptr ) ;
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 ) {
unsigned int id = glCreateShader ( type ) ;
const char * src = source . c_str ( ) ; // <--- this string needs to exist when compiling/running
glShaderSource ( id , 1 , & src , nullptr ) ;
glCompileShader ( id ) ;
//TODO: error handling
int result ;
glGetShaderiv ( id , GL_COMPILE_STATUS , & result ) ;
if ( result = = GL_FALSE ) {
int length ;
glGetShaderiv ( id , GL_INFO_LOG_LENGTH , & length ) ;
char * message = ( char * ) alloca ( length * sizeof ( char ) ) ; //do i need to deallocate this??
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 ;
glDeleteShader ( id ) ;
return 0 ;
}
//
return id ;
}
unsigned int SdlWindow : : createShader ( const std : : string & vertexShader , const std : : string & fragmentShader ) {
unsigned int program = glCreateProgram ( ) ;
unsigned int vs = compileShader ( GL_VERTEX_SHADER , vertexShader ) ;
unsigned int fs = compileShader ( GL_FRAGMENT_SHADER , fragmentShader ) ;
glAttachShader ( program , vs ) ;
glAttachShader ( program , fs ) ;
glLinkProgram ( program ) ;
glValidateProgram ( program ) ;
glDeleteShader ( vs ) ;
glDeleteShader ( fs ) ;
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 ( ) } ;
}