3D viewport, milestone 1
This commit is contained in:
parent
38156dd570
commit
e277663ba0
27 changed files with 7389 additions and 8 deletions
113
src/platform/GLContext.h
Normal file
113
src/platform/GLContext.h
Normal file
|
|
@ -0,0 +1,113 @@
|
|||
// GLContext.h - OpenGL context abstraction for McRogueFace 3D
|
||||
// Provides uniform GL access across SFML and SDL2 backends
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../3d/Math3D.h"
|
||||
|
||||
namespace mcrf {
|
||||
namespace gl {
|
||||
|
||||
// =============================================================================
|
||||
// Initialization
|
||||
// =============================================================================
|
||||
|
||||
// Initialize OpenGL function pointers if needed (GLAD for desktop SFML)
|
||||
// Returns true if GL is ready to use
|
||||
bool initGL();
|
||||
|
||||
// Check if GL is initialized and ready
|
||||
bool isGLReady();
|
||||
|
||||
// =============================================================================
|
||||
// Framebuffer Object (FBO) Management
|
||||
// =============================================================================
|
||||
|
||||
// Create a framebuffer with color texture and optional depth renderbuffer
|
||||
// Returns FBO id, sets colorTex to the color attachment texture
|
||||
// depthRB is optional - pass nullptr if depth buffer not needed
|
||||
unsigned int createFramebuffer(int width, int height, unsigned int* colorTex, unsigned int* depthRB = nullptr);
|
||||
|
||||
// Bind a framebuffer for rendering
|
||||
void bindFramebuffer(unsigned int fbo);
|
||||
|
||||
// Bind the default framebuffer (screen)
|
||||
void bindDefaultFramebuffer();
|
||||
|
||||
// Delete a framebuffer and its attachments
|
||||
void deleteFramebuffer(unsigned int fbo, unsigned int colorTex, unsigned int depthRB);
|
||||
|
||||
// =============================================================================
|
||||
// Shader Compilation
|
||||
// =============================================================================
|
||||
|
||||
// Compile a vertex or fragment shader from source
|
||||
// type should be GL_VERTEX_SHADER or GL_FRAGMENT_SHADER
|
||||
unsigned int compileShader(unsigned int type, const char* source);
|
||||
|
||||
// Link vertex and fragment shaders into a program
|
||||
// Returns program id, or 0 on failure
|
||||
unsigned int linkProgram(unsigned int vertShader, unsigned int fragShader);
|
||||
|
||||
// Delete a shader program
|
||||
void deleteProgram(unsigned int program);
|
||||
|
||||
// =============================================================================
|
||||
// GL State Management (for mixing with SFML rendering)
|
||||
// =============================================================================
|
||||
|
||||
// Save current OpenGL state before custom 3D rendering
|
||||
// This includes blend mode, depth test, culling, bound textures, etc.
|
||||
void pushState();
|
||||
|
||||
// Restore OpenGL state after custom 3D rendering
|
||||
void popState();
|
||||
|
||||
// =============================================================================
|
||||
// 3D Rendering State Setup
|
||||
// =============================================================================
|
||||
|
||||
// Set up GL state for 3D rendering (depth test, culling, etc.)
|
||||
void setup3DState();
|
||||
|
||||
// Restore GL state for 2D rendering (disable depth, etc.)
|
||||
void restore2DState();
|
||||
|
||||
// =============================================================================
|
||||
// Depth Buffer Operations
|
||||
// =============================================================================
|
||||
|
||||
// Enable/disable depth testing
|
||||
void setDepthTest(bool enable);
|
||||
|
||||
// Enable/disable depth writing
|
||||
void setDepthWrite(bool enable);
|
||||
|
||||
// Set depth test function (GL_LESS, GL_LEQUAL, etc.)
|
||||
void setDepthFunc(unsigned int func);
|
||||
|
||||
// Clear the depth buffer
|
||||
void clearDepth();
|
||||
|
||||
// =============================================================================
|
||||
// Face Culling
|
||||
// =============================================================================
|
||||
|
||||
// Enable/disable face culling
|
||||
void setCulling(bool enable);
|
||||
|
||||
// Set which face to cull (GL_BACK, GL_FRONT, GL_FRONT_AND_BACK)
|
||||
void setCullFace(unsigned int face);
|
||||
|
||||
// =============================================================================
|
||||
// Utility
|
||||
// =============================================================================
|
||||
|
||||
// Get last GL error as string (for debugging)
|
||||
const char* getErrorString();
|
||||
|
||||
// Check for GL errors and log them
|
||||
bool checkError(const char* operation);
|
||||
|
||||
} // namespace gl
|
||||
} // namespace mcrf
|
||||
60
src/platform/GLContext_Headless.cpp
Normal file
60
src/platform/GLContext_Headless.cpp
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
// GLContext_Headless.cpp - Headless backend for OpenGL context abstraction
|
||||
// Returns failure for all operations since there's no GPU
|
||||
|
||||
#ifdef MCRF_HEADLESS
|
||||
|
||||
#include "GLContext.h"
|
||||
|
||||
namespace mcrf {
|
||||
namespace gl {
|
||||
|
||||
bool initGL() {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool isGLReady() {
|
||||
return false;
|
||||
}
|
||||
|
||||
unsigned int createFramebuffer(int width, int height, unsigned int* colorTex, unsigned int* depthRB) {
|
||||
if (colorTex) *colorTex = 0;
|
||||
if (depthRB) *depthRB = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void bindFramebuffer(unsigned int fbo) {}
|
||||
void bindDefaultFramebuffer() {}
|
||||
void deleteFramebuffer(unsigned int fbo, unsigned int colorTex, unsigned int depthRB) {}
|
||||
|
||||
unsigned int compileShader(unsigned int type, const char* source) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned int linkProgram(unsigned int vertShader, unsigned int fragShader) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void deleteProgram(unsigned int program) {}
|
||||
void pushState() {}
|
||||
void popState() {}
|
||||
void setup3DState() {}
|
||||
void restore2DState() {}
|
||||
void setDepthTest(bool enable) {}
|
||||
void setDepthWrite(bool enable) {}
|
||||
void setDepthFunc(unsigned int func) {}
|
||||
void clearDepth() {}
|
||||
void setCulling(bool enable) {}
|
||||
void setCullFace(unsigned int face) {}
|
||||
|
||||
const char* getErrorString() {
|
||||
return "Headless mode - no GL context";
|
||||
}
|
||||
|
||||
bool checkError(const char* operation) {
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace gl
|
||||
} // namespace mcrf
|
||||
|
||||
#endif // MCRF_HEADLESS
|
||||
305
src/platform/GLContext_SDL2.cpp
Normal file
305
src/platform/GLContext_SDL2.cpp
Normal file
|
|
@ -0,0 +1,305 @@
|
|||
// GLContext_SDL2.cpp - SDL2 backend for OpenGL context abstraction
|
||||
// Leverages existing SDL2Renderer infrastructure
|
||||
|
||||
#ifdef MCRF_SDL2
|
||||
|
||||
#include "GLContext.h"
|
||||
#include "SDL2Renderer.h"
|
||||
|
||||
#ifdef __EMSCRIPTEN__
|
||||
#include <GLES2/gl2.h>
|
||||
#else
|
||||
#include <GL/gl.h>
|
||||
#include <GL/glext.h>
|
||||
#endif
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace mcrf {
|
||||
namespace gl {
|
||||
|
||||
// =============================================================================
|
||||
// State tracking structures (same as SFML version)
|
||||
// =============================================================================
|
||||
|
||||
struct GLState {
|
||||
GLboolean depthTest;
|
||||
GLboolean depthWrite;
|
||||
GLenum depthFunc;
|
||||
GLboolean cullFace;
|
||||
GLenum cullMode;
|
||||
GLboolean blend;
|
||||
GLenum blendSrc;
|
||||
GLenum blendDst;
|
||||
GLint viewport[4];
|
||||
GLint boundFBO;
|
||||
GLint boundProgram;
|
||||
GLint boundTexture;
|
||||
};
|
||||
|
||||
static std::vector<GLState> stateStack;
|
||||
|
||||
// =============================================================================
|
||||
// Initialization
|
||||
// =============================================================================
|
||||
|
||||
bool initGL() {
|
||||
// SDL2Renderer handles GL initialization
|
||||
auto& renderer = sf::SDL2Renderer::getInstance();
|
||||
return renderer.isGLInitialized();
|
||||
}
|
||||
|
||||
bool isGLReady() {
|
||||
auto& renderer = sf::SDL2Renderer::getInstance();
|
||||
return renderer.isGLInitialized();
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// FBO Management
|
||||
// =============================================================================
|
||||
|
||||
unsigned int createFramebuffer(int width, int height, unsigned int* colorTex, unsigned int* depthRB) {
|
||||
GLuint fbo, tex, depth = 0;
|
||||
|
||||
// Create FBO
|
||||
glGenFramebuffers(1, &fbo);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
|
||||
|
||||
// Create color texture
|
||||
glGenTextures(1, &tex);
|
||||
glBindTexture(GL_TEXTURE_2D, tex);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tex, 0);
|
||||
|
||||
// Create depth renderbuffer if requested
|
||||
// Note: GLES2 uses GL_DEPTH_COMPONENT16 instead of GL_DEPTH_COMPONENT24
|
||||
if (depthRB) {
|
||||
glGenRenderbuffers(1, &depth);
|
||||
glBindRenderbuffer(GL_RENDERBUFFER, depth);
|
||||
#ifdef __EMSCRIPTEN__
|
||||
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, width, height);
|
||||
#else
|
||||
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, width, height);
|
||||
#endif
|
||||
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depth);
|
||||
*depthRB = depth;
|
||||
}
|
||||
|
||||
// Check completeness
|
||||
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
if (depth) glDeleteRenderbuffers(1, &depth);
|
||||
glDeleteTextures(1, &tex);
|
||||
glDeleteFramebuffers(1, &fbo);
|
||||
return 0;
|
||||
}
|
||||
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
*colorTex = tex;
|
||||
return fbo;
|
||||
}
|
||||
|
||||
void bindFramebuffer(unsigned int fbo) {
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
|
||||
}
|
||||
|
||||
void bindDefaultFramebuffer() {
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
}
|
||||
|
||||
void deleteFramebuffer(unsigned int fbo, unsigned int colorTex, unsigned int depthRB) {
|
||||
if (depthRB) {
|
||||
GLuint rb = depthRB;
|
||||
glDeleteRenderbuffers(1, &rb);
|
||||
}
|
||||
if (colorTex) {
|
||||
GLuint tex = colorTex;
|
||||
glDeleteTextures(1, &tex);
|
||||
}
|
||||
if (fbo) {
|
||||
GLuint f = fbo;
|
||||
glDeleteFramebuffers(1, &f);
|
||||
}
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// Shader Compilation
|
||||
// =============================================================================
|
||||
|
||||
unsigned int compileShader(unsigned int type, const char* source) {
|
||||
GLuint shader = glCreateShader(type);
|
||||
glShaderSource(shader, 1, &source, nullptr);
|
||||
glCompileShader(shader);
|
||||
|
||||
GLint success;
|
||||
glGetShaderiv(shader, GL_COMPILE_STATUS, &success);
|
||||
if (!success) {
|
||||
GLchar infoLog[512];
|
||||
glGetShaderInfoLog(shader, 512, nullptr, infoLog);
|
||||
// TODO: Log error
|
||||
glDeleteShader(shader);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return shader;
|
||||
}
|
||||
|
||||
unsigned int linkProgram(unsigned int vertShader, unsigned int fragShader) {
|
||||
GLuint program = glCreateProgram();
|
||||
glAttachShader(program, vertShader);
|
||||
glAttachShader(program, fragShader);
|
||||
glLinkProgram(program);
|
||||
|
||||
GLint success;
|
||||
glGetProgramiv(program, GL_LINK_STATUS, &success);
|
||||
if (!success) {
|
||||
GLchar infoLog[512];
|
||||
glGetProgramInfoLog(program, 512, nullptr, infoLog);
|
||||
// TODO: Log error
|
||||
glDeleteProgram(program);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return program;
|
||||
}
|
||||
|
||||
void deleteProgram(unsigned int program) {
|
||||
glDeleteProgram(program);
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// State Management
|
||||
// =============================================================================
|
||||
|
||||
void pushState() {
|
||||
GLState state;
|
||||
|
||||
state.depthTest = glIsEnabled(GL_DEPTH_TEST);
|
||||
glGetBooleanv(GL_DEPTH_WRITEMASK, &state.depthWrite);
|
||||
glGetIntegerv(GL_DEPTH_FUNC, (GLint*)&state.depthFunc);
|
||||
|
||||
state.cullFace = glIsEnabled(GL_CULL_FACE);
|
||||
glGetIntegerv(GL_CULL_FACE_MODE, (GLint*)&state.cullMode);
|
||||
|
||||
state.blend = glIsEnabled(GL_BLEND);
|
||||
glGetIntegerv(GL_BLEND_SRC_ALPHA, (GLint*)&state.blendSrc);
|
||||
glGetIntegerv(GL_BLEND_DST_ALPHA, (GLint*)&state.blendDst);
|
||||
|
||||
glGetIntegerv(GL_VIEWPORT, state.viewport);
|
||||
glGetIntegerv(GL_FRAMEBUFFER_BINDING, &state.boundFBO);
|
||||
glGetIntegerv(GL_CURRENT_PROGRAM, &state.boundProgram);
|
||||
glGetIntegerv(GL_TEXTURE_BINDING_2D, &state.boundTexture);
|
||||
|
||||
stateStack.push_back(state);
|
||||
}
|
||||
|
||||
void popState() {
|
||||
if (stateStack.empty()) return;
|
||||
|
||||
GLState& state = stateStack.back();
|
||||
|
||||
if (state.depthTest) glEnable(GL_DEPTH_TEST);
|
||||
else glDisable(GL_DEPTH_TEST);
|
||||
glDepthMask(state.depthWrite);
|
||||
glDepthFunc(state.depthFunc);
|
||||
|
||||
if (state.cullFace) glEnable(GL_CULL_FACE);
|
||||
else glDisable(GL_CULL_FACE);
|
||||
glCullFace(state.cullMode);
|
||||
|
||||
if (state.blend) glEnable(GL_BLEND);
|
||||
else glDisable(GL_BLEND);
|
||||
glBlendFunc(state.blendSrc, state.blendDst);
|
||||
|
||||
glViewport(state.viewport[0], state.viewport[1], state.viewport[2], state.viewport[3]);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, state.boundFBO);
|
||||
glUseProgram(state.boundProgram);
|
||||
glBindTexture(GL_TEXTURE_2D, state.boundTexture);
|
||||
|
||||
stateStack.pop_back();
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// 3D State Setup
|
||||
// =============================================================================
|
||||
|
||||
void setup3DState() {
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
glDepthMask(GL_TRUE);
|
||||
glDepthFunc(GL_LESS);
|
||||
glEnable(GL_CULL_FACE);
|
||||
glCullFace(GL_BACK);
|
||||
}
|
||||
|
||||
void restore2DState() {
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
glDisable(GL_CULL_FACE);
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// Depth Operations
|
||||
// =============================================================================
|
||||
|
||||
void setDepthTest(bool enable) {
|
||||
if (enable) glEnable(GL_DEPTH_TEST);
|
||||
else glDisable(GL_DEPTH_TEST);
|
||||
}
|
||||
|
||||
void setDepthWrite(bool enable) {
|
||||
glDepthMask(enable ? GL_TRUE : GL_FALSE);
|
||||
}
|
||||
|
||||
void setDepthFunc(unsigned int func) {
|
||||
glDepthFunc(func);
|
||||
}
|
||||
|
||||
void clearDepth() {
|
||||
glClear(GL_DEPTH_BUFFER_BIT);
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// Culling
|
||||
// =============================================================================
|
||||
|
||||
void setCulling(bool enable) {
|
||||
if (enable) glEnable(GL_CULL_FACE);
|
||||
else glDisable(GL_CULL_FACE);
|
||||
}
|
||||
|
||||
void setCullFace(unsigned int face) {
|
||||
glCullFace(face);
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// Error Handling
|
||||
// =============================================================================
|
||||
|
||||
const char* getErrorString() {
|
||||
GLenum err = glGetError();
|
||||
switch (err) {
|
||||
case GL_NO_ERROR: return nullptr;
|
||||
case GL_INVALID_ENUM: return "GL_INVALID_ENUM";
|
||||
case GL_INVALID_VALUE: return "GL_INVALID_VALUE";
|
||||
case GL_INVALID_OPERATION: return "GL_INVALID_OPERATION";
|
||||
case GL_OUT_OF_MEMORY: return "GL_OUT_OF_MEMORY";
|
||||
default: return "Unknown GL error";
|
||||
}
|
||||
}
|
||||
|
||||
bool checkError(const char* operation) {
|
||||
const char* err = getErrorString();
|
||||
if (err) {
|
||||
// TODO: Log error with operation name
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace gl
|
||||
} // namespace mcrf
|
||||
|
||||
#endif // MCRF_SDL2
|
||||
339
src/platform/GLContext_SFML.cpp
Normal file
339
src/platform/GLContext_SFML.cpp
Normal file
|
|
@ -0,0 +1,339 @@
|
|||
// GLContext_SFML.cpp - SFML backend for OpenGL context abstraction
|
||||
// Uses GLAD for GL function loading
|
||||
|
||||
#ifndef MCRF_SDL2
|
||||
#ifndef MCRF_HEADLESS
|
||||
|
||||
#include "GLContext.h"
|
||||
#include <glad/glad.h>
|
||||
#include <SFML/OpenGL.hpp>
|
||||
#include <vector>
|
||||
#include <cstdio>
|
||||
|
||||
namespace mcrf {
|
||||
namespace gl {
|
||||
|
||||
// =============================================================================
|
||||
// State tracking
|
||||
// =============================================================================
|
||||
|
||||
static bool s_gladInitialized = false;
|
||||
|
||||
struct GLState {
|
||||
GLboolean depthTest;
|
||||
GLboolean depthWrite;
|
||||
GLenum depthFunc;
|
||||
GLboolean cullFace;
|
||||
GLenum cullMode;
|
||||
GLboolean blend;
|
||||
GLenum blendSrc;
|
||||
GLenum blendDst;
|
||||
GLint viewport[4];
|
||||
GLint boundFBO;
|
||||
GLint boundProgram;
|
||||
GLint boundTexture;
|
||||
};
|
||||
|
||||
static std::vector<GLState> stateStack;
|
||||
|
||||
// =============================================================================
|
||||
// Initialization
|
||||
// =============================================================================
|
||||
|
||||
bool initGL() {
|
||||
if (s_gladInitialized) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Load GL function pointers via GLAD
|
||||
// Note: SFML must have created an OpenGL context before this is called
|
||||
if (!gladLoadGL()) {
|
||||
fprintf(stderr, "GLContext_SFML: Failed to initialize GLAD\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
s_gladInitialized = true;
|
||||
printf("GLContext_SFML: GLAD initialized - OpenGL %d.%d\n", GLVersion.major, GLVersion.minor);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool isGLReady() {
|
||||
return s_gladInitialized;
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// FBO Management
|
||||
// =============================================================================
|
||||
|
||||
unsigned int createFramebuffer(int width, int height, unsigned int* colorTex, unsigned int* depthRB) {
|
||||
if (!s_gladInitialized) return 0;
|
||||
|
||||
GLuint fbo, tex, depth = 0;
|
||||
|
||||
// Create FBO
|
||||
glGenFramebuffers(1, &fbo);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
|
||||
|
||||
// Create color texture
|
||||
glGenTextures(1, &tex);
|
||||
glBindTexture(GL_TEXTURE_2D, tex);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tex, 0);
|
||||
|
||||
// Create depth renderbuffer if requested
|
||||
if (depthRB) {
|
||||
glGenRenderbuffers(1, &depth);
|
||||
glBindRenderbuffer(GL_RENDERBUFFER, depth);
|
||||
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, width, height);
|
||||
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depth);
|
||||
*depthRB = depth;
|
||||
}
|
||||
|
||||
// Check completeness
|
||||
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
|
||||
if (status != GL_FRAMEBUFFER_COMPLETE) {
|
||||
fprintf(stderr, "GLContext_SFML: Framebuffer incomplete: 0x%x\n", status);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
if (depth) glDeleteRenderbuffers(1, &depth);
|
||||
glDeleteTextures(1, &tex);
|
||||
glDeleteFramebuffers(1, &fbo);
|
||||
return 0;
|
||||
}
|
||||
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
*colorTex = tex;
|
||||
return fbo;
|
||||
}
|
||||
|
||||
void bindFramebuffer(unsigned int fbo) {
|
||||
if (s_gladInitialized) {
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
|
||||
}
|
||||
}
|
||||
|
||||
void bindDefaultFramebuffer() {
|
||||
if (s_gladInitialized) {
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void deleteFramebuffer(unsigned int fbo, unsigned int colorTex, unsigned int depthRB) {
|
||||
if (!s_gladInitialized) return;
|
||||
|
||||
if (depthRB) {
|
||||
GLuint rb = depthRB;
|
||||
glDeleteRenderbuffers(1, &rb);
|
||||
}
|
||||
if (colorTex) {
|
||||
GLuint tex = colorTex;
|
||||
glDeleteTextures(1, &tex);
|
||||
}
|
||||
if (fbo) {
|
||||
GLuint f = fbo;
|
||||
glDeleteFramebuffers(1, &f);
|
||||
}
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// Shader Compilation
|
||||
// =============================================================================
|
||||
|
||||
unsigned int compileShader(unsigned int type, const char* source) {
|
||||
if (!s_gladInitialized) return 0;
|
||||
|
||||
GLuint shader = glCreateShader(type);
|
||||
glShaderSource(shader, 1, &source, nullptr);
|
||||
glCompileShader(shader);
|
||||
|
||||
GLint success;
|
||||
glGetShaderiv(shader, GL_COMPILE_STATUS, &success);
|
||||
if (!success) {
|
||||
GLchar infoLog[512];
|
||||
glGetShaderInfoLog(shader, 512, nullptr, infoLog);
|
||||
fprintf(stderr, "GLContext_SFML: Shader compilation failed:\n%s\n", infoLog);
|
||||
glDeleteShader(shader);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return shader;
|
||||
}
|
||||
|
||||
unsigned int linkProgram(unsigned int vertShader, unsigned int fragShader) {
|
||||
if (!s_gladInitialized) return 0;
|
||||
|
||||
GLuint program = glCreateProgram();
|
||||
glAttachShader(program, vertShader);
|
||||
glAttachShader(program, fragShader);
|
||||
glLinkProgram(program);
|
||||
|
||||
GLint success;
|
||||
glGetProgramiv(program, GL_LINK_STATUS, &success);
|
||||
if (!success) {
|
||||
GLchar infoLog[512];
|
||||
glGetProgramInfoLog(program, 512, nullptr, infoLog);
|
||||
fprintf(stderr, "GLContext_SFML: Program linking failed:\n%s\n", infoLog);
|
||||
glDeleteProgram(program);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return program;
|
||||
}
|
||||
|
||||
void deleteProgram(unsigned int program) {
|
||||
if (s_gladInitialized && program) {
|
||||
glDeleteProgram(program);
|
||||
}
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// State Management
|
||||
// =============================================================================
|
||||
|
||||
void pushState() {
|
||||
if (!s_gladInitialized) return;
|
||||
|
||||
GLState state;
|
||||
|
||||
state.depthTest = glIsEnabled(GL_DEPTH_TEST);
|
||||
glGetBooleanv(GL_DEPTH_WRITEMASK, &state.depthWrite);
|
||||
glGetIntegerv(GL_DEPTH_FUNC, (GLint*)&state.depthFunc);
|
||||
|
||||
state.cullFace = glIsEnabled(GL_CULL_FACE);
|
||||
glGetIntegerv(GL_CULL_FACE_MODE, (GLint*)&state.cullMode);
|
||||
|
||||
state.blend = glIsEnabled(GL_BLEND);
|
||||
glGetIntegerv(GL_BLEND_SRC_ALPHA, (GLint*)&state.blendSrc);
|
||||
glGetIntegerv(GL_BLEND_DST_ALPHA, (GLint*)&state.blendDst);
|
||||
|
||||
glGetIntegerv(GL_VIEWPORT, state.viewport);
|
||||
glGetIntegerv(GL_FRAMEBUFFER_BINDING, &state.boundFBO);
|
||||
glGetIntegerv(GL_CURRENT_PROGRAM, &state.boundProgram);
|
||||
glGetIntegerv(GL_TEXTURE_BINDING_2D, &state.boundTexture);
|
||||
|
||||
stateStack.push_back(state);
|
||||
}
|
||||
|
||||
void popState() {
|
||||
if (!s_gladInitialized || stateStack.empty()) return;
|
||||
|
||||
GLState& state = stateStack.back();
|
||||
|
||||
if (state.depthTest) glEnable(GL_DEPTH_TEST);
|
||||
else glDisable(GL_DEPTH_TEST);
|
||||
glDepthMask(state.depthWrite);
|
||||
glDepthFunc(state.depthFunc);
|
||||
|
||||
if (state.cullFace) glEnable(GL_CULL_FACE);
|
||||
else glDisable(GL_CULL_FACE);
|
||||
glCullFace(state.cullMode);
|
||||
|
||||
if (state.blend) glEnable(GL_BLEND);
|
||||
else glDisable(GL_BLEND);
|
||||
glBlendFunc(state.blendSrc, state.blendDst);
|
||||
|
||||
glViewport(state.viewport[0], state.viewport[1], state.viewport[2], state.viewport[3]);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, state.boundFBO);
|
||||
glUseProgram(state.boundProgram);
|
||||
glBindTexture(GL_TEXTURE_2D, state.boundTexture);
|
||||
|
||||
stateStack.pop_back();
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// 3D State Setup
|
||||
// =============================================================================
|
||||
|
||||
void setup3DState() {
|
||||
if (!s_gladInitialized) return;
|
||||
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
glDepthMask(GL_TRUE);
|
||||
glDepthFunc(GL_LESS);
|
||||
glEnable(GL_CULL_FACE);
|
||||
glCullFace(GL_BACK);
|
||||
}
|
||||
|
||||
void restore2DState() {
|
||||
if (!s_gladInitialized) return;
|
||||
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
glDisable(GL_CULL_FACE);
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// Depth Operations
|
||||
// =============================================================================
|
||||
|
||||
void setDepthTest(bool enable) {
|
||||
if (!s_gladInitialized) return;
|
||||
if (enable) glEnable(GL_DEPTH_TEST);
|
||||
else glDisable(GL_DEPTH_TEST);
|
||||
}
|
||||
|
||||
void setDepthWrite(bool enable) {
|
||||
if (!s_gladInitialized) return;
|
||||
glDepthMask(enable ? GL_TRUE : GL_FALSE);
|
||||
}
|
||||
|
||||
void setDepthFunc(unsigned int func) {
|
||||
if (!s_gladInitialized) return;
|
||||
glDepthFunc(func);
|
||||
}
|
||||
|
||||
void clearDepth() {
|
||||
if (!s_gladInitialized) return;
|
||||
glClear(GL_DEPTH_BUFFER_BIT);
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// Culling
|
||||
// =============================================================================
|
||||
|
||||
void setCulling(bool enable) {
|
||||
if (!s_gladInitialized) return;
|
||||
if (enable) glEnable(GL_CULL_FACE);
|
||||
else glDisable(GL_CULL_FACE);
|
||||
}
|
||||
|
||||
void setCullFace(unsigned int face) {
|
||||
if (!s_gladInitialized) return;
|
||||
glCullFace(face);
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// Error Handling
|
||||
// =============================================================================
|
||||
|
||||
const char* getErrorString() {
|
||||
if (!s_gladInitialized) return "GLAD not initialized";
|
||||
|
||||
GLenum err = glGetError();
|
||||
switch (err) {
|
||||
case GL_NO_ERROR: return nullptr;
|
||||
case GL_INVALID_ENUM: return "GL_INVALID_ENUM";
|
||||
case GL_INVALID_VALUE: return "GL_INVALID_VALUE";
|
||||
case GL_INVALID_OPERATION: return "GL_INVALID_OPERATION";
|
||||
case GL_OUT_OF_MEMORY: return "GL_OUT_OF_MEMORY";
|
||||
case GL_INVALID_FRAMEBUFFER_OPERATION: return "GL_INVALID_FRAMEBUFFER_OPERATION";
|
||||
default: return "Unknown GL error";
|
||||
}
|
||||
}
|
||||
|
||||
bool checkError(const char* operation) {
|
||||
const char* err = getErrorString();
|
||||
if (err) {
|
||||
fprintf(stderr, "GLContext_SFML: GL error after %s: %s\n", operation, err);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace gl
|
||||
} // namespace mcrf
|
||||
|
||||
#endif // MCRF_HEADLESS
|
||||
#endif // MCRF_SDL2
|
||||
Loading…
Add table
Add a link
Reference in a new issue