I am currently creating a lightweight program to view and manipulate 3D objects similar to the program Blender or 3DS max.
I am using C++, GLFW, and OpenGL. While I plan to have it be cross platform, I am currently only working on a computer running Debian 9.0 (stretch) with the XFCE desktop environment.
Currently the callback glfwSetDropCallback never seems to get called, no mater what I try. I have set break points and asserts but nothing. All other callbacks that I have tried work just fine. I have included my GLFW cpp from my project. If something else is needed feel free to ask.
Also as a last note I am just using GLFW because everything else seems too heavy for my needs. Also using X11 directly appears like a lot of work for not much gain, especially since I would need a different solution for each OS I target. In the end I just want to be able to drag and drop items onto my programs window and then have my code do something with the path(s).
#include "GLFWindow.h"
#include <cassert>
#include <iostream>
// Variables for callback.
static bool keys[1024];
static std::vector<std::string> dragAndDrop;
static Camera* _camera;
static GLdouble* _lastX;
static GLdouble* _lastY;
GLFWindow::GLFWindow()
:m_window(nullptr), m_lastX(0.0), m_lastY(0.0)
{
glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, GLFW_VERSION_MAJOR);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, GLFW_VERSION_MINOR);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
glfwWindowHint(GLFW_RESIZABLE, GL_FALSE);
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_FALSE); // For OSX
glfwWindowHint(GLFW_FOCUSED, GL_TRUE);
std::cout << glfwGetVersionString() << std::endl;
m_window = glfwCreateWindow(1920, 1080, "Model Viewer", /*glfwGetPrimaryMonitor()*/nullptr, nullptr);
if(m_window == nullptr)
{
std::cout << "Failed to create GLFW window" << std::endl;
glfwTerminate();
assert(m_window != nullptr);
}
glfwMakeContextCurrent(m_window);
glfwSetWindowPos(m_window, 1920 - 1920/2, 1080 - 1080/2);
glfwSetKeyCallback(m_window,
[](GLFWwindow* window, int key, int, int action, int)
{
if(key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)
glfwSetWindowShouldClose(window, GL_TRUE);
if(action == GLFW_PRESS)
keys[key] = true;
else if(action == GLFW_RELEASE)
keys[key] = false;
});
glfwSetInputMode(m_window, GLFW_CURSOR, GLFW_CURSOR_HIDDEN);
glfwSetCursorEnterCallback(m_window,
[](GLFWwindow* window, int entered)
{
if(entered)
{
double xPos, yPos;
glfwGetCursorPos(window, &xPos, &yPos);
*_lastX = xPos;
*_lastY = yPos;
}
});
glfwSetDropCallback(m_window,
[](GLFWwindow*, int count, const char** paths)
{
std::cout << "Drag and Drop count - " << count << std::endl;
for(unsigned int i = 0; i < count; ++i)
{
std::cout << std::string(paths[i]) << std::endl;
dragAndDrop.push_back(std::string(paths[i]));
}
});
}
GLFWindow::~GLFWindow()
{
glfwTerminate();
}
GLint GLFWindow::GetWidth()
{
GLint width = 0;
glfwGetFramebufferSize(m_window, &width, nullptr);
return width;
}
GLint GLFWindow::GetHeight()
{
GLint height = 0;
glfwGetFramebufferSize(m_window, nullptr, &height);
return height;
}
std::string GLFWindow::GetDragAndDrop()
{
if(dragAndDrop.empty())
return "END";
std::string item = dragAndDrop[dragAndDrop.size()-1];
dragAndDrop.pop_back();
if(item.find(".obj") == std::string::npos && item.find(".fbx") == std::string::npos
&& item.find(".OBJ") == std::string::npos && item.find(".FBX") == std::string::npos)
return "INVALID";
return item;
}
bool GLFWindow::ShouldClose()
{
return glfwWindowShouldClose(m_window);
}
void GLFWindow::AddCamera(Camera* camera)
{
m_camera = camera;
_camera = m_camera;
_lastX = &m_lastX;
_lastY = &m_lastY;
glfwSetScrollCallback(m_window,
[](GLFWwindow*, double, double yoffset)
{
_camera->ProcessMouseScroll(yoffset);
});
glfwSetCursorPosCallback(m_window,
[](GLFWwindow*, double xPos, double yPos)
{
GLfloat xOffset = xPos - *_lastX;
GLfloat yOffset = *_lastY - yPos;
*_lastX = xPos;
*_lastY = yPos;
_camera->ProcessMouseMovement(xOffset, yOffset);
});
}
void GLFWindow::PollEvents(const GLfloat deltaTime)
{
glfwPollEvents();
if(keys[GLFW_KEY_W])
m_camera->ProcessKeyboard(0, deltaTime);
if(keys[GLFW_KEY_S])
m_camera->ProcessKeyboard(1, deltaTime);
if(keys[GLFW_KEY_A])
m_camera->ProcessKeyboard(2, deltaTime);
if(keys[GLFW_KEY_D])
m_camera->ProcessKeyboard(3, deltaTime);
if(keys[GLFW_KEY_Q])
m_camera->ProcessKeyboard(4, deltaTime);
if(keys[GLFW_KEY_E])
m_camera->ProcessKeyboard(5, deltaTime);
}
GLfloat GLFWindow::GetElapsedTime()
{
return glfwGetTime();
}
void GLFWindow::SwapBuffers()
{
glfwSwapBuffers(m_window);
}
Thanks you for your time,
Brandon
Edit:
I added some X11 code in to test some things.
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xos.h>
Display *display =XOpenDisplay((char *)0);
if(XInternAtom(display, "XdndAware", False))
std::cout << "Supported" << std::endl;
This code always returns Supported. I now believe it is a bug with GLFW.
Final Edit:
This was a bug with GLFW.
Bug is due to Thunar providing text/uri-list while GLFW assumed UTF8_STRING was supported. Fix incoming.
If anyone else has this issue here is the fix.
https://github.com/glfw/glfw/commit/6a65341e14bc7745c52fe86fe53be08dbd682dd9
Edit: This was indeed a bug and has been resolved.
First of all make sure that you're using the newest stable GLFW version (today that is 3.2.1).
The most obvious reason could be that you never call GLFWindow::PollEvents() thus in turn never call glfwPollEvents(). However I'm doubting this is the cause, as you never mention any other input problems.
To verify if it really is the callback never getting called, try this minimal example:
glfwInit();
// You might need some of the window hints
GLFWwindow *window = glfwCreateWindow(600, 400, "Test glfwSetDropCallback", NULL, NULL);
glfwSetDropCallback(window, [](GLFWwindow*, int count, const char **paths)
{
std::cout << "Drag and Drop count - " << count << std::endl;
for (unsigned int i = 0; i < count; ++i)
std::cout << "- " << paths[i] << std::endl;
});
while (!glfwWindowShouldClose(window))
{
glfwSwapBuffers(window);
glfwPollEvents();
}
glfwDestroyWindow(window);
glfwTerminate();
I just tested the above code on Windows and it worked. So if that code doesn't work. Then it might be a X11 specific bug and should be notified on GLFW's issue tracker.
If it still doesn't work. Then before deeming it a bug, you should test if your display even supports file dropping. So while this is X11 specific. You'd only be using it to verify support.
Note that GLFW doesn't complain if dropping files isn't supported.
It's a long time since I've touched X11. But if I recall correctly, checking support for dropping files would be something like this:
Display *display = ...
if (XInternAtom(display, "XdndAware", False))
std::cout << "Supported" << std::endl;
Related
I want to implement KeyJustDown function which checks, if given key was just pressed down. Key was just pressed down, if it is down in the current state and was up in the previous state.
int isKeyJustDown(int key) {
return !inputState.keysPrev[key] && inputState.keysCurr[key];
}
At the beginning of the frame I also update the input state like so:
struct InputState {
char keysPrev[256];
char keysCurr[256];
} inputState;
void updateInput() {
memcpy(inputState.keysPrev, inputState.keysCurr, 256);
glfwPollEvents();
}
This works fine with glfw3 application on desktop. Callbacks are dispatched when glfwPollEvents function is called. However this does not apply when using emscripten.
I have two questions. When exactly are callbacks dispatched when using emscripten (do they happen as soon as event occurs or is there a certain point in the loop it happens)? And how can I achieve the same behaviour as on desktop when using emscripten?
Here is the full example:
#include <GLFW/glfw3.h>
#include <memory.h>
#include <stdio.h>
#ifdef __EMSCRIPTEN__
#include <emscripten.h>
#endif
GLFWwindow *window;
struct InputState {
char keysPrev[256];
char keysCurr[256];
} inputState;
void updateInput() {
memcpy(inputState.keysPrev, inputState.keysCurr, 256);
glfwPollEvents();
}
int isKeyJustDown(int key) {
return !inputState.keysPrev[key] && inputState.keysCurr[key];
}
void update() {
updateInput();
if (isKeyJustDown(GLFW_KEY_E)) {
printf("Key E\n");
}
glfwSwapBuffers(window);
}
void keyCallback(GLFWwindow *window, int key, int scancode, int action, int mods) {
if (key < 0) return;
inputState.keysCurr[key] = action != GLFW_RELEASE;
}
int main() {
if (!glfwInit()) return 1;
window = glfwCreateWindow(1280, 720, "Input test", NULL, NULL);
if (!window) return 1;
glfwSetKeyCallback(window, keyCallback);
#ifdef __EMSCRIPTEN__
emscripten_set_main_loop(update, 0, 1);
#else
while (!glfwWindowShouldClose(window)) {
update();
}
glfwTerminate();
#endif
return 0;
}
It looks like glfwPollInput does nothing in the emscripten implementation. The actual event callbacks can happen at any time. To solve my problem I moved updateInput function after checking for input:
void update() {
if (isKeyJustDown(GLFW_KEY_E)) {
printf("Key E\n");
}
glfwSwapBuffers(window);
updateInput();
}
Relevant links:
https://github.com/raysan5/raylib/pull/2380
https://github.com/raysan5/raylib/issues/2379
First time asking a question on here so if it's not layed out correctly or something is missing I'll try to get everything else up.
I'm trying to learn OpenGl because I'm interested in developing games of my own and I would rather create my own engine than one already out there.
I'm using GLEW also and I know it inits because the code doesn't output an Error
I spend alittle while googling the error code and the OpenGL but none of the problems really match what I'm getting.
I also tried the GLEW_Experimental = true but that did not change anything.
Code: main
#include "src/graphics/window.h"
int main()
{
using namespace FutureGamingEngine;
using namespace graphics;
Window window("Test", 1080, 720);
glClearColor(0, 255, 0, 1.0f); //Red, Green, Blue, No Idea
std::cout << glGetString(GL_VERSION) << std::endl;
std::cout << glGetString(GL_RENDERER) << std::endl;
std::cout << glGetString(GL_RENDER) << std::endl;
while (!window.closed())
{
window.clear();
glBegin(GL_QUADS);
glVertex2f(-0.5f, -0.5f);
glVertex2f(-0.5f, 0.5f);
glVertex2f(0.5f, 0.5f);
glVertex2f(0.5f,-0.5f);
glEnd();
window.update();
}
//system("PAUSE");
return 0;
}
Window:
#include "window.h"
namespace FutureGamingEngine
{
namespace graphics
{
void windowResize(GLFWwindow *window, int width, int height);
Window::Window(const char *title, int width, int height)
{
c_title = title;
c_height = height;
c_width = width;
if (!init())
{
glfwTerminate();
}
}
Window::~Window()
{
glfwTerminate();
}
bool Window::init()
{
glewExperimental = true;
if (!glfwInit())
{
std::cout << "Error Code: 0" << std::endl;
return false;
}
//Create a windowed mode and it's OpenGl Content
c_window = glfwCreateWindow(c_width, c_height, c_title, NULL, NULL);
//If we fail to make the window Terminate OpenGL
if (!c_window)
{
glfwTerminate();
return false;
}
//std::cout << "Open GL: " << glGetString(GL_VERSION) << std::endl;
glfwMakeContextCurrent(c_window);
glfwSetWindowSizeCallback(c_window, windowResize);
if (glewInit() != GLEW_OK)
{
std::cout << "Error Code: 1" << std::endl;
return false;
}
return true;
}
bool Window::closed() const
{
return glfwWindowShouldClose(c_window);
}
void Window::update()
{
//poll for and process events
glfwPollEvents();
glfwGetFramebufferSize(c_window, &c_width, &c_height);
//swap front and backbuffers
glfwSwapBuffers(c_window);
}
void Window::clear() const
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
}
void windowResize(GLFWwindow *window, int width, int height)
{
glViewport(0, 0, width, height);
}
}
}
The Header for window:
#pragma once
#define GLEW_STATIC
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include <iostream>
namespace FutureGamingEngine
{
namespace graphics
{
class Window
{
private:
const char *c_title;
int c_width, c_height;
GLFWwindow *c_window;
bool c_closed;
public:
Window(const char *a_name, int a_width, int a_height);
~Window();
bool closed() const;
void update();
void clear() const;
inline int getHeight() const { return c_height; };
inline int getWidth() const { return c_width; };
private:
bool init();
};
}
}
I'm expecting it to tell me the Render Version. What I'm actually getting is Error Produced on std::cout << glGetString(GL_RENDER) << std::endl; :
Exception thrown at 0x7C50F6E0 (ucrtbased.dll) in FutureGamingEngine-Core.exe: 0xC0000005: Access violation reading location 0x00000000.
(https://i.imgur.com/uKu3hxX.png)
and the doesn't get to rendering the Triangle like it did prior to me asking for Gl_render
GL_RENDERER is valid enum, GL_RENDER is not. See OpenGL doc
Thank you Ripi2! I apperently did not look hard enough :D
For some reason my Window::callback is being called even after the mouse has left the window. I am unable to find a solution or even something that could help. Is it possible that GLFW updated how the mouse cursor callback operates? I wonder if it is an order of invocation problem?
Window
Window::Window(std::string title, int32_t width, int32_t height) {
// TODO: add support for monitor and share for GLFW
m_window = std::unique_ptr<GLFWwindow, GLFWdeleter>(glfwCreateWindow(width, height, title.c_str(), nullptr, nullptr));
glfwMakeContextCurrent(m_window.get());
glfwSetWindowUserPointer(m_window.get(), this);
glfwSetCursorPosCallback(m_window.get(), Window::callback);
}
void Window::mouse_callback(double xpos, double ypos) {
std::cout << "x: " << xpos << " y: " << ypos << std::endl;
}
void Window::callback(GLFWwindow* window, double xpos, double ypos)
{
auto win = static_cast<Window*>(glfwGetWindowUserPointer(window));
win->mouse_callback(xpos, ypos);
}
Engine
void startup() const
{
if (glfwInit() == 0)
{
LOG(kError, "GLFW init failed!");
exit(-1);
}
}
void Engine::run() {
if (m_main_window_registered)
{
glewExperimental = static_cast<GLboolean>(true);
if (glewInit() != GLEW_OK)
{
std::cout << "Failed to initialize glew" << std::endl;
return;
}
}
while(glfwWindowShouldClose(m_main_window->window()) == 0) {
glClear(GL_COLOR_BUFFER_BIT);
glfwSwapBuffers(m_main_window->window());
glfwPollEvents();
}
}
main.cpp
int main()
{
g_engine.startup();
glfwWindowHint(GLFW_SAMPLES, 4);
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
auto window = std::make_unique<Window>("Hello World!", 640, 480);
//window->make_current();
g_engine.registerWindow(std::move(window));
g_engine.run();
glfwTerminate();
return 0;
}
I have figured out what the problem (or better put) issue is. On Windows the callback performs as expected where once the mouse leaves the windows area the callback stops firing. For OSX the window never loses focus and therefore the cursor callback is always being called. To fix the issue you simply have to test the coordinates to ensure that the mouse is in fact inside the window.
I'm creating a GLFW window, and I would like the bottom left corner to be the origin. For some reason, the top left corner is the origin.
I should also mention that I'm using OSX.
Here's my window class:
#include "window.h"
namespace graphics {
void window_size_callback(GLFWwindow* window, int width, int height);
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode);
void mouse_button_callback(GLFWwindow* window, int button, int action, int mode);
void cursor_position_callback(GLFWwindow* window, double mouse_x, double mouse_y);
Window::Window(const char* title, int width, int height) {
m_title = title;
m_width = width;
m_height = height;
if (!init())
glfwTerminate();
}
Window::~Window() {
glfwTerminate();
}
bool Window::init() {
if (!glfwInit()) {
std::cout << "GLFW failed to initialize!" << std::endl;
return false;
}
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);
m_window = glfwCreateWindow(m_width, m_height, m_title, NULL, NULL);
if (!m_window) {
glfwTerminate();
std::cout << "GLFW failed to create a window!" << std::endl;
return false;
}
glfwMakeContextCurrent(m_window);
glfwSetWindowUserPointer(m_window, this);
glfwSetWindowSizeCallback(m_window, window_size_callback);
glfwSetKeyCallback(m_window, key_callback);
glfwSetMouseButtonCallback(m_window, mouse_button_callback);
glfwSetCursorPosCallback(m_window, cursor_position_callback);
if (glewInit() != GLEW_OK) {
std::cout << "GLEW failed to initialize!" << std::endl;
return false;
}
std::cout << "OpenGL Version: " << glGetString(GL_VERSION) << std::endl;
std::cout << "OpenGL Shading Language Version: " << glGetString(GL_SHADING_LANGUAGE_VERSION) << std::endl;
return true;
}
void Window::update() {
glfwPollEvents();
glfwSwapBuffers(m_window);
}
void Window::clear() const {
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
}
bool Window::isClosed() const {
return glfwWindowShouldClose(m_window);
}
bool Window::isKeyPressed(unsigned int keycode) const {
if (keycode >= MAX_KEYS)
return false;
return m_keys[keycode];
}
bool Window::isMouseButtonPressed(unsigned int button) const {
if (button >= MAX_BUTTONS)
return false;
return m_mouse_buttons[button];
}
void Window::getMousePosition(double& x, double& y) const {
x = m_mouse_x;
y = m_mouse_y;
}
void window_size_callback(GLFWwindow* window, int width, int height) {
Window* win = (Window*)glfwGetWindowUserPointer(window);
win->m_width = width;
win->m_height = height;
}
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode) {
Window* win = (Window*)glfwGetWindowUserPointer(window);
win->m_keys[key] = action != GLFW_RELEASE;
}
void mouse_button_callback(GLFWwindow* window, int button, int action, int mode) {
Window* win = (Window*)glfwGetWindowUserPointer(window);
win->m_mouse_buttons[button] = action != GLFW_RELEASE;
}
void cursor_position_callback(GLFWwindow* window, double mouse_x, double mouse_y) {
Window* win = (Window*)glfwGetWindowUserPointer(window);
win->m_mouse_x = mouse_x;
win->m_mouse_y = mouse_y;
}
}
http://www.opengl-tutorial.org/beginners-tutorials/tutorial-2-the-first-triangle/
But here is a better way to visualize this : use the Right Hand Rule
X is your thumb
Y is your index
Z is your middle finger. If you put
your thumb to the right and your index to the sky, it will point to
your back, too. Having the Z in this direction is weird, so why is it
so ? Short answer : because 100 years of Right Hand Rule Math will
give you lots of useful tools. The only downside is an unintuitive Z.
This is something you can’t change, it’s built in your graphics card.
So (-1,-1) is the bottom left corner of your screen. (1,-1) is the
bottom right, and (0,1) is the middle top. So this triangle should
take most of the screen.
I am using OpenGL and GLSL. I am following a tutorial, this one to be exact: Exact video of tutorial that I am having problem with but he does not use xcode, which is what I am using, and it has lead to this issue.
I'll start this out with my system information, and all the information I think is relevant enough to share that'll help with the problem. I will then tell you what the problem is, along with everything else I've learned that I think is relevant and helpful. After that, I'll include a copy of all the code I used, so you can look at it for errors and the real problem and such.
System Information:
iMac (21.5 inch, mid-2011)
OS X Yosesemite (V 10.10.1)
Processor: 2.5 GHz Intel Core i5
Memory: 4 GB 1333 MHz DDR3
Graphics:
OpenGL version: 4.1 ATI-1.28.29
GLSL version: 4.10
Vendor: ATI Technologies Inc.
Renderer: AMD Radeon HD 6750M OpenGL Engine
Xcode v 6.1.1
My problem:
The problem is that Shader is failing to compile.
fragment-shader.txt:
#version 130
out vec3 color;
void main() {
color = vec3(1.0, 0.0, 0.0);
}
vertex-shader.txt
#version 130
in vec2 vertexPosition;
void main() {
//Set the x,y position on the screen
gl_Position.xy = vertexPosition;
//the z position is zero since we are in 2D
gl_Position.z = 0.0;
//Indicate that the coordinates are normalized
gl_Position.w = 1.0;
}
Both of these fail to compile. The compilation call will be in GLSLProgram.cpp, more specifically void GLSLProgram::compileShader.
I do check for errors, and the error message I recieved was: "ERROR: 0:1: '' : version '130' is not supported"
The typical causes of this error I discovered is an outdated graphics card. I know that this is not the case, However, I checked anyways, just during runtime. I check my OpenGl version and glsl version, and I get the following result:
OpenGL version: 2.1 ATI-1.28.29
GLSL version: 1.20
Vendor: ATI Technologies Inc.
Renderer: AMD Radeon HD 6750M OpenGL Engine
It is reading my memory card, but the OpenGL and GLSL version are wrong. I have no idea why. And I have been unable to figure out why or how to fix it myself through the internet, and that is why I am here.
I downloaded a random crossplatform program that also uses GLSL, and it runs fine. I'm not really familiar with openGL, so I wasn't able to look at the code and figure out what the problem was. All I can do is give my best educated guess. I think it is a preprocessor error. I might be using the incorrect preprocessor, or an outdated version, or I don't know. That's the problem. The code that worked however, printed out the correct the OpenGL version and GLSL version during runtime. So I do know something is causing me not to use the most up to date version. I dont know if that is the only problem, but I do know that is one of the problems.
The code I mentioned in previous paragraph is from this tutorial, and access to the code is in a link in the first paragraph of that tutorial
Code
I'm on only going to include the cpp files. Forward declarations are in the header file, but I dont see the need to include them. If you do in fact need them, ask, and I will post them.
main.cpp
#include "MainGame.h"
#include "Sprite.h"
#include <iostream>
int main( int argc, char** argv) {
MainGame mainGame;
mainGame.run();
return 0;
}
MainGame.cpp (Ties everything together)
#include "MainGame.h"
#include <iostream>
#include <string>
MainGame::MainGame(){
_window = nullptr;
_screenHeight = 768;
_screenWidth = 1024;
_gameState = PLAY;
}
MainGame::~MainGame(){
}
void MainGame::run(){
initSystems();
_sprite.init(-1.0f, -1.0f, 1.0f, 1.0f);
gameLoop();
}
void MainGame::initSystems(){
SDL_Init(SDL_INIT_EVERYTHING);
_window = SDL_CreateWindow("Game Engine", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, _screenWidth, _screenHeight, SDL_WINDOW_OPENGL);
if (_window == nullptr) {
fatalError("SDL Window could not be created. :(");
}
SDL_GLContext glContext = SDL_GL_CreateContext(_window);
if (glContext == nullptr) {
fatalError("SDL_GL context could not be created. :(");
}
std::cout << "OpenGL version: " << glGetString(GL_VERSION) << std::endl;
std::cout << "GLSL version: " << glGetString(GL_SHADING_LANGUAGE_VERSION) << std::endl;
std::cout << "Vendor: " << glGetString(GL_VENDOR) << std::endl;
std::cout << "Renderer: " << glGetString(GL_RENDERER) << std::endl;
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
glClearColor(0.0f, 0.0f, 1.0f, 1.0);
initShaders();
}
void MainGame::initShaders(){
_colorProgram.compileShaders("/Users/user/filepath.../vertex-shader.txt", "/Users/user/filepath.../fragment-shader.txt");
_colorProgram.addAttribute("vertexPosition");
_colorProgram.linkShaders();
}
void MainGame::gameLoop(){
while (_gameState != GameState::EXIT) {
processInput();
drawGame();
}
}
void MainGame::processInput(){
SDL_Event evnt;
while (SDL_PollEvent(&evnt))
switch (evnt.type) {
case SDL_QUIT:
_gameState = EXIT;
break;
case SDL_MOUSEMOTION:
std::cout << evnt.motion.x << " " << evnt.motion.y << std::endl;
break;
}
}
void MainGame::drawGame(){
glClearDepth(1.0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
_colorProgram.use();
_sprite.draw();
_colorProgram.unuse();
SDL_GL_SwapWindow(_window);
}
GLSLProgram.cpp (Handles most GLSL related things)
#include "GLSLProgram.h"
#include "Errors.h"
#include <fstream>
#include <vector>
#include <iostream>
GLSLProgram::GLSLProgram() : _numAttributes(0), _programID(0), _vertexShaderID(0), _fragmentShaderID(0)
{
}
GLSLProgram::~GLSLProgram(){
}
void GLSLProgram::compileShaders(const std::string& vertexShaderFilePath, const std::string& fragmentShaderFilePath){
_vertexShaderID = glCreateShader(GL_VERTEX_SHADER);
if (_vertexShaderID == 0){
fatalError("Vertex Shader failed to be created");
}
_fragmentShaderID = glCreateShader(GL_FRAGMENT_SHADER);
if (_fragmentShaderID == 0){
fatalError("Fragment Shader failed to be created");
}
compileShader(vertexShaderFilePath, _vertexShaderID);
compileShader(fragmentShaderFilePath, _fragmentShaderID);
}
void GLSLProgram::addAttribute(const std::string& attributeName){
glBindAttribLocation(_programID, _numAttributes++, attributeName.c_str());
}
void GLSLProgram::linkShaders(){
//Vertex and fragment shaders are successfully compiled.
//Now time to link them together into a program.
//Get a program object.
_programID = glCreateProgram();
//Attach our shaders to our program
glAttachShader(_programID, _vertexShaderID);
glAttachShader(_programID, _fragmentShaderID);
//Link our program
glLinkProgram(_programID);
//Note the different functions here: glGetProgram* instead of glGetShader*.
GLint isLinked = 0;
glGetProgramiv(_programID, GL_LINK_STATUS, (int *)&isLinked);
if(isLinked == GL_FALSE)
{
GLint maxLength = 0;
glGetProgramiv(_programID, GL_INFO_LOG_LENGTH, &maxLength);
//The maxLength includes the NULL character
std::vector<char> errorLog(maxLength);
glGetProgramInfoLog(_programID, maxLength, &maxLength, &errorLog[0]);
//We don't need the program anymore.
glDeleteProgram(_programID);
//Don't leak shaders either.
glDeleteShader(_vertexShaderID);
glDeleteShader(_fragmentShaderID);
std::printf("%s/n", &(errorLog[0]));
fatalError("Shaders failed to link"); //In this simple program, we'll just leave
}
//Always detach shaders after a successful link.
glDetachShader(_programID, _vertexShaderID);
glDetachShader(_programID, _fragmentShaderID);
glDeleteShader(_vertexShaderID);
glDeleteShader(_fragmentShaderID);
}
void GLSLProgram::use(){
glUseProgram(_programID);
for (int i = 0; i < _numAttributes; i++) {
glEnableVertexAttribArray(i);
}
}
void GLSLProgram::unuse(){
glUseProgram(0);
for (int i = 0; i < _numAttributes; i++) {
glDisableVertexAttribArray(i);
}
}
void GLSLProgram::compileShader(const std::string& filePath, GLuint id){
std::ifstream shaderFile(filePath);
if (shaderFile.fail()){
perror(filePath.c_str());
fatalError("Failed to open " + filePath);
}
std::string fileContents ="";
std::string line;
while (std::getline(shaderFile, line)) {
fileContents += line + "\n";
}
shaderFile.close();
const char* contentsPtr = fileContents.c_str();
glShaderSource(id, 1, &contentsPtr, nullptr);
glCompileShader(id);
GLint success = 0;
glGetShaderiv(id, GL_COMPILE_STATUS, &success);
if(success == GL_FALSE)
{
GLint maxLength = 0;
glGetShaderiv(id, GL_INFO_LOG_LENGTH, &maxLength);
// The maxLength includes the NULL character
std::vector<char> errorLog(maxLength);
glGetShaderInfoLog(id, maxLength, &maxLength, &errorLog[0]);
// Provide the infolog in whatever manor you deem best.
// Exit with failure.
glDeleteShader(id); // Don't leak the shader.
std::printf("%s\n", &(errorLog[0]));
fatalError("Shader " + filePath + " failed to compile");
}
// Shader compilation is successful.
}
Sprite.cpp (Draws a simple triangle)
#include "Sprite.h"
Sprite::Sprite(){
_vboID = 0;
}
Sprite::~Sprite(){
if (_vboID != 0){
glDeleteBuffers(1, &_vboID);
}
}
void Sprite::init(float x, float y, float width, float height){
_x = x;
_y = y;
_width = width;
_height = height;
if (_vboID == 0){
glGenBuffers(1, &_vboID);
}
float vertexData[12];
//first triangle
vertexData[0] = x + width;
vertexData[1] = y + height;
vertexData[2] = x;
vertexData[3] = y + height;
vertexData[4] = x;
vertexData[5] = y;
//second triangle
vertexData[6] = x + width;
vertexData[7] = y + height;
vertexData[8] = x;
vertexData[9] = y;
vertexData[10] = x + width;
vertexData[11] = y;
glBindBuffer(GL_ARRAY_BUFFER, _vboID);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertexData), vertexData, GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
void Sprite::draw(){
glBindBuffer(GL_ARRAY_BUFFER, _vboID);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, 0);
glDrawArrays(GL_TRIANGLES, 0, 6);
glDisableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
error.cpp (quits in case of error)
#include "Errors.h"
#include <iostream>
#include <SDL2/SDL.h>
void fatalError(std::string errorString) {
std::cout << errorString << std::endl;
std::cout << "Enter any key to quit...";
int tmp;
std::cin >> tmp;
SDL_Quit();
}
Additionally, this code does work, on Windows at least. Almost all of it was taken from the youtube link above, just with slight modifications to work with Xcode. There is a link to that code in the description of the youtube vid.
You need to call SDL_GL_SetAttribute before creating the window to set the minimal OpenGL version you expect.
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);