I'm trying to use modern OpenGL with wxGLCanvas.
To learn OpenGL I'm following this tutorial: https://www.youtube.com/watch?v=x0H--CL ... S2&index=5.
To start I'm only trying to write a simple triangle on the screen.
Header:
Code: Select all
#include <GL/glew.h>
#include <wx/glcanvas.h>
class Viewer3D : public wxGLCanvas{
private:
struct ShaderSource {
std::string vertex;
std::string fragment;
bool isOk() const noexcept{
return not (vertex.empty() and fragment.empty());
}
};
ShaderSource loadShader(const std::string &path);
unsigned int compileShader(unsigned int type, const std::string &src);
unsigned int compileGLProgram(const ShaderSource &src);
public:
std::unique_ptr<wxGLContextAttrs> ctxAttr;
std::unique_ptr<wxGLContext> ctx;
Viewer3D(
wxWindow *parent, wxWindowID id, wxGLAttributes attrList,
const wxPoint &pos =wxDefaultPosition,
const wxSize &size = wxDefaultSize, long style = 0,
const wxString &name = wxGLCanvasName,
const wxPalette &palette = wxNullPalette
);
void onPaint(wxPaintEvent &e);
virtual ~Viewer3D(); // NOLINT(modernize-use-override)
};
Code: Select all
#include <wx/wx.h>
#include <fstream>
#include <sstream>
#include <array>
#include "../hdr/Viewer3D.hpp"
Viewer3D::ShaderSource Viewer3D::loadShader(const std::string &path) {
enum ShaderType {
VERTEX=0,
FRAGMENT
}mode;
using namespace std;
ifstream input(path);
array<stringstream,2> shaders;
string line;
if (not input.good()){
wxLogError(strerror(errno));
return {};
}
// initialize mode
if ((not getline(input, line)) or line.find("//#shader") == string::npos)
return {};
if (line.find("vertex") != string::npos)
mode = ShaderType::VERTEX;
else if(line.find("fragment"))
mode = ShaderType::FRAGMENT;
else
return {};
while (getline(input, line)){
if (line.find("//#shader") != string::npos){
if (line.find("vertex") != string::npos){
// load vertex shader
mode = ShaderType::VERTEX;
} else if(line.find("fragment")){
// load fragment shader
mode = ShaderType::FRAGMENT;
} else {
// error
return {};
}
} else {
shaders[mode] << line << '\n';
}
}
return ShaderSource{
shaders[ShaderType::VERTEX].str(),
shaders[ShaderType::FRAGMENT].str()
};
}
unsigned int Viewer3D::compileShader(unsigned int type, const std::string &src){
SetCurrent(*ctx);
auto id = glCreateShader(type);
const char *srcCStr = src.c_str();
glShaderSource(id, 1, &srcCStr, nullptr);
glCompileShader(id);
// error handling
int status;
glGetShaderiv(id, GL_COMPILE_STATUS, &status);
if (status == GL_FALSE){
int length;
std::string log;
glGetShaderiv(id, GL_INFO_LOG_LENGTH, &length);
log.reserve(static_cast<unsigned long>(length));
glGetShaderInfoLog(id, length, &length, &log[0]);
std::puts(log.c_str());
return 0;
}
return id;
}
unsigned int Viewer3D::compileGLProgram(const Viewer3D::ShaderSource &src) {
SetCurrent(*ctx);
if(not src.isOk()){
wxLogError("invalid ShaderSource value");
return 0;
}
unsigned int programId = glCreateProgram();
auto vertexId = compileShader(GL_VERTEX_SHADER, src.vertex);
auto fragmentId = compileShader(GL_FRAGMENT_SHADER, src.fragment);
glAttachShader(programId, vertexId);
glAttachShader(programId, fragmentId);
glLinkProgram(programId);
glValidateProgram(programId);
// error handling
int status;
glGetProgramiv(programId, GL_VALIDATE_STATUS, &status);
if (status == GL_FALSE){
int length;
std::string log;
glGetProgramiv(programId, GL_INFO_LOG_LENGTH, &length);
log.reserve(static_cast<unsigned long>(length));
glGetProgramInfoLog(programId, length, &length, &log[0]);
std::puts(log.c_str());
return 0;
}
glDetachShader(programId, vertexId);
glDetachShader(programId, fragmentId);
glDeleteShader(vertexId);
glDeleteShader(fragmentId);
return programId;
}
Viewer3D::Viewer3D(
wxWindow *parent, wxWindowID id, wxGLAttributes attrList,
const wxPoint &pos, const wxSize &size,
long style, const wxString &name, const wxPalette &palette
) : wxGLCanvas(parent, attrList, id, pos, size, style, name, palette),
ctxAttr(new wxGLContextAttrs)
{
ctxAttr->CoreProfile().OGLVersion(4,3).EndList();
ctx = std::make_unique<wxGLContext>(this, nullptr, ctxAttr.get());
if (not ctx->IsOK()){
wxLogFatalError("Cannot initialize wxGLContext");
return;
}
SetCurrent(*ctx);
// initializing glew
GLenum res = glewInit();
if (res != GLEW_OK) {
wxLogFatalError("Error: '%s'\n", glewGetErrorString(res));
}
std::cout << (glGetString(GL_VERSION)) << std::endl;
Bind(wxEVT_PAINT, &Viewer3D::onPaint, this);
}
void Viewer3D::onPaint(wxPaintEvent &e) {
SetCurrent(*ctx);
glClearColor(0.f, 0.f, 0.f, 1.f);
float positions[] = {
0.0f, 0.0f,
-0.5f, -0.5f,
0.5f, -0.5f
};
unsigned int buff;
glGenBuffers(1, &buff);
glBindBuffer(GL_ARRAY_BUFFER, buff);
glBufferData(GL_ARRAY_BUFFER, 6*sizeof(float), positions, GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 2*sizeof(float), 0);
auto shdrSrc = loadShader(
"/Users/noemurr/git/NavTools/code/gui/3D-world-viewer/shaders"
"/basicShader.glsl"
);
auto programID = compileGLProgram(shdrSrc);
glUseProgram(programID);
glDrawArrays(GL_TRIANGLES, 0, 3);
glFlush();
SwapBuffers();
}
Viewer3D::~Viewer3D() = default;
I've already read the pyramid example of opengl but it is too complex and I can't understand what I do wrong.