403 lines
13 KiB
C++
403 lines
13 KiB
C++
#include <cstdio>
|
|
#include <cmath>
|
|
#include <string>
|
|
#include <unordered_map>
|
|
#include <SDL2/SDL.h>
|
|
#include <SDL2/SDL_image.h>
|
|
#include <SDL2/SDL_ttf.h>
|
|
|
|
#include <glbinding/gl/gl.h>
|
|
#include <glbinding/Binding.h>
|
|
#include <glbinding/callbacks.h>
|
|
|
|
#define GLM_FORCE_RADIANS
|
|
#include <glm/glm.hpp>
|
|
#include <glm/gtc/matrix_transform.hpp>
|
|
#include <glm/gtc/type_ptr.hpp>
|
|
#include <glm/gtx/transform.hpp>
|
|
#include <glm/gtc/packing.hpp>
|
|
|
|
#include "common.hh"
|
|
#include "shaders.hh"
|
|
#include "texture.hh"
|
|
#include "VBOManager.hh"
|
|
#include "objectParser.hh"
|
|
#include "Object.hh"
|
|
#include "font.hh"
|
|
#include "Overlay.hh"
|
|
#include "TextWidget.hh"
|
|
#include "LinearLayout.hh"
|
|
|
|
using namespace gl;
|
|
|
|
void glAfterCallback(glbinding::FunctionCall const& fc)
|
|
{
|
|
glbinding::setCallbackMask(glbinding::CallbackMask::None);
|
|
GLenum error = glGetError();
|
|
glbinding::setCallbackMask(glbinding::CallbackMask::After);
|
|
if (error != GL_NO_ERROR)
|
|
throw GLException(error);
|
|
}
|
|
|
|
int main(int argc, char *argv[])
|
|
{
|
|
SDL_Window *window;
|
|
int retcode = 0;
|
|
|
|
glbinding::Binding::initialize();
|
|
|
|
if (SDL_Init(SDL_INIT_VIDEO) < 0) {
|
|
std::printf("Could not init SDL: %s\n", SDL_GetError());
|
|
return 1;
|
|
}
|
|
if (IMG_Init(IMG_INIT_PNG | IMG_INIT_JPG) == 0) {
|
|
std::printf("Could not init SDL_image: %s\n", SDL_GetError());
|
|
SDL_Quit();
|
|
return 1;
|
|
}
|
|
|
|
if(TTF_Init() == -1) {
|
|
printf("Could not init SDL_ttf: %s\n", TTF_GetError());
|
|
IMG_Quit();
|
|
SDL_Quit();
|
|
return 1;
|
|
}
|
|
|
|
SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
|
|
SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
|
|
SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
|
|
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 16);
|
|
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
|
|
|
|
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
|
|
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
|
|
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);
|
|
|
|
window = SDL_CreateWindow("SDL2 Test",
|
|
SDL_WINDOWPOS_UNDEFINED,
|
|
SDL_WINDOWPOS_UNDEFINED,
|
|
1680,
|
|
1050,
|
|
/*SDL_WINDOW_FULLSCREEN_DESKTOP |*/ SDL_WINDOW_OPENGL);
|
|
|
|
if (!window) {
|
|
std::printf("Could not create window: %s\n", SDL_GetError());
|
|
TTF_Quit();
|
|
IMG_Quit();
|
|
SDL_Quit();
|
|
return 1;
|
|
}
|
|
|
|
SDL_GLContext glcontext = SDL_GL_CreateContext(window);
|
|
if (!glcontext) {
|
|
std::printf("Could not create GL context: %s\n", SDL_GetError());
|
|
SDL_DestroyWindow(window);
|
|
TTF_Quit();
|
|
IMG_Quit();
|
|
SDL_Quit();
|
|
return 1;
|
|
}
|
|
|
|
// Clear GL errors
|
|
while(glGetError() != GL_NO_ERROR);
|
|
|
|
try {
|
|
glbinding::setCallbackMask(glbinding::CallbackMask::After);
|
|
glbinding::setAfterCallback(glAfterCallback);
|
|
|
|
{
|
|
int depth, stencil, aa, major, minor;
|
|
SDL_GL_GetAttribute(SDL_GL_DEPTH_SIZE, &depth);
|
|
SDL_GL_GetAttribute(SDL_GL_STENCIL_SIZE, &stencil);
|
|
SDL_GL_GetAttribute(SDL_GL_MULTISAMPLESAMPLES, &aa);
|
|
SDL_GL_GetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, &major);
|
|
SDL_GL_GetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, &minor);
|
|
|
|
std::printf("Depth: %d, Stencil: %d, AA: %d, GL %d.%d\n",
|
|
depth, stencil, aa, major, minor);
|
|
}
|
|
|
|
int width, height;
|
|
SDL_GetWindowSize(window, &width, &height);
|
|
glm::mat4 proj = glm::perspectiveFov(75.0f, static_cast<float>(width), static_cast<float>(height),
|
|
0.1f, 100.0f);
|
|
glm::mat4 view = glm::lookAt(glm::vec3(2.0f, 2.0f, 10),
|
|
glm::vec3(0.0f, 0.0f, 0),
|
|
glm::vec3(0, 1, 0));
|
|
|
|
Font font{"DejaVuSans.ttf", 12};
|
|
|
|
// sf::Font font;
|
|
// if (!font.loadFromFile("DejaVuSans.ttf")) {
|
|
// std::printf("Error loading font\n");
|
|
// return 1;
|
|
// }
|
|
|
|
// sf::Text fpsText{"0", font, 12};
|
|
// fpsText.setPosition({200, 200});
|
|
|
|
// window.setFramerateLimit(60);
|
|
|
|
// sf::Clock clock;
|
|
// sf::Time last = clock.getElapsedTime();
|
|
auto last = SDL_GetTicks();
|
|
|
|
Texture2D cubeTex("textures/Wood_Box_Texture.jpg");
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, static_cast<int>(GL_LINEAR_MIPMAP_LINEAR));
|
|
if (SDL_GL_ExtensionSupported("GL_EXT_texture_filter_anisotropic"))
|
|
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 4.0f);
|
|
else
|
|
std::printf("Warning: extension GL_EXT_texture_filter_anisotropic not supported\n");
|
|
Texture2D redTex("textures/red.png");
|
|
Texture2D whiteTex("textures/white.png");
|
|
|
|
const unsigned lights = 2;
|
|
const unsigned shadowMapSize = 512;
|
|
glm::mat4 shadowProj = glm::perspectiveFov(90.0f, static_cast<float>(shadowMapSize),
|
|
static_cast<float>(shadowMapSize), 1.0f, 128.0f);
|
|
//glm::mat4 shadowProj = glm::ortho(-10.0f, 10.0f, -10.0f, 10.0f, 1.0f, 128.0f);
|
|
std::vector<TextureCubeMap> shadowMaps;
|
|
for(unsigned i = 0;i < lights;++i) {
|
|
shadowMaps.emplace_back(shadowMapSize);
|
|
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_COMPARE_FUNC, static_cast<GLint>(GL_LESS));
|
|
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_COMPARE_MODE, static_cast<GLint>(GL_COMPARE_REF_TO_TEXTURE));
|
|
}
|
|
|
|
const glm::vec3 lightPos[lights] = {glm::vec3(2.0, -2.0, 10.0),
|
|
glm::vec3(2.0, 2.0, 10.0)};
|
|
// const glm::vec3 lightColor[lights] = {glm::vec3(1.0, 0.9, 0.8),
|
|
// glm::vec3(0.0, 1.0, 0.0)};
|
|
// const float lightIntensity[lights] = {75.0f, 25.0f};
|
|
|
|
glm::mat4 ovlProj = glm::ortho(0.0f, static_cast<float>(width), static_cast<float>(height), 0.0f);
|
|
|
|
Framebuffer shadowFB;
|
|
|
|
VertexShader vs{fileToString("shaders/textured.vs")};
|
|
FragmentShader fs{fileToString("shaders/textured.fs")};
|
|
Program prog{vs, fs};
|
|
|
|
VertexShader shadowVs{fileToString("shaders/shadow.vs")};
|
|
FragmentShader shadowFs{fileToString("shaders/shadow.fs")};
|
|
Program shadowProg{shadowVs, shadowFs};
|
|
|
|
VertexShader ovlVs{fileToString("shaders/overlay.vs")};
|
|
FragmentShader ovlFs{fileToString("shaders/overlay.fs")};
|
|
Program ovlProg{ovlVs, ovlFs};
|
|
|
|
prog.use();
|
|
glUniformMatrix4fv(prog.getUniformLocation("projection_matrix"), 1, GL_FALSE,
|
|
glm::value_ptr(proj));
|
|
glUniformMatrix4fv(prog.getUniformLocation("view_matrix"), 1, GL_FALSE,
|
|
glm::value_ptr(view));
|
|
glUniform1i(prog.getUniformLocation("texBase"), 0);
|
|
|
|
const GLint shadowMapTUs[] = {1, 2};
|
|
glUniform1iv(prog.getUniformLocation("texShadowMaps"), 2, shadowMapTUs);
|
|
|
|
shadowProg.use();
|
|
glUniformMatrix4fv(shadowProg.getUniformLocation("projection_matrix"), 1, GL_FALSE,
|
|
glm::value_ptr(shadowProj));
|
|
|
|
ovlProg.use();
|
|
glUniformMatrix4fv(ovlProg.getUniformLocation("projection_matrix"), 1, GL_FALSE,
|
|
glm::value_ptr(ovlProj));
|
|
glUniform1i(ovlProg.getUniformLocation("texBase"), 0);
|
|
|
|
Object box("objects/woodbox.obj");
|
|
Object pyramid("objects/pyramid.obj");
|
|
Object plane("objects/plane.obj");
|
|
|
|
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
|
|
|
|
bool close = false;
|
|
|
|
unsigned fpsTime = 0, fpsCount = 0;
|
|
TextWidget fpsText{font, "0 FPS"};
|
|
fpsText.setBackgroundColor(SDL_Color{0, 255, 0, 128});
|
|
fpsText.setPadding(8, 8, 8, 8);
|
|
|
|
auto laber = make_unique<TextWidget>(font, "Dies ist ein längerer\nText, der umgebrochen werden sollte. Bla laber schwafl, laber bullshit bingo",
|
|
200, WRAP_CONTENT);
|
|
laber->setBackgroundColor(SDL_Color{255, 0, 0, 128});
|
|
laber->setPadding(4, 0, 4, 0);
|
|
|
|
SDLSurfaceUPtr buttonImg(IMG_Load("textures/button_100x30.png"));
|
|
|
|
if (!buttonImg)
|
|
throw SDLException();
|
|
|
|
auto button = make_unique<TextWidget>(font, "Do stuff!", 100, 30);
|
|
button->setPadding(6, 6, 6, 6);
|
|
button->setBackground(buttonImg.get());
|
|
button->setForegroundColor(SDL_Color{0, 0, 0, 255});
|
|
button->setAlignment(ALIGN_CENTER, ALIGN_CENTER);
|
|
|
|
auto layout = make_unique<LinearLayout>();
|
|
layout->setBackgroundColor(SDL_Color{0, 255, 255, 128});
|
|
layout->addChild(std::move(laber));
|
|
layout->addChild(std::move(button));
|
|
layout->setChildPadding(4);
|
|
layout->setPadding(4, 4, 4, 4);
|
|
layout->setGLRenderPos(200, 200);
|
|
|
|
while (!close) {
|
|
SDL_Event event;
|
|
while (SDL_PollEvent(&event)) {
|
|
switch(event.type) {
|
|
case SDL_KEYDOWN:
|
|
close = true;
|
|
break;
|
|
case SDL_WINDOWEVENT:
|
|
if (event.window.event == SDL_WINDOWEVENT_CLOSE)
|
|
close = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Draw shadow maps
|
|
GLint origVP[4];
|
|
glGetIntegerv(GL_VIEWPORT, origVP);
|
|
glViewport(0, 0, shadowMapSize, shadowMapSize);
|
|
shadowProg.use();
|
|
shadowFB.bind();
|
|
glPolygonOffset(1.1f, 4.0f);
|
|
glEnable(GL_POLYGON_OFFSET_FILL);
|
|
glEnable(GL_POLYGON_OFFSET_LINE);
|
|
glEnable(GL_POLYGON_OFFSET_POINT);
|
|
for (unsigned light = 0;light < lights;++light) {
|
|
for (unsigned i = 0;i < 6;++i) {
|
|
GLenum const face[6] = {GL_TEXTURE_CUBE_MAP_POSITIVE_X, GL_TEXTURE_CUBE_MAP_NEGATIVE_X,
|
|
GL_TEXTURE_CUBE_MAP_POSITIVE_Y, GL_TEXTURE_CUBE_MAP_NEGATIVE_Y,
|
|
GL_TEXTURE_CUBE_MAP_POSITIVE_Z, GL_TEXTURE_CUBE_MAP_NEGATIVE_Z};
|
|
|
|
const glm::vec3 dir[6] = {glm::vec3(1.0f, 0.0f, 0.0f),
|
|
glm::vec3(-1.0f, 0.0f, 0.0f),
|
|
glm::vec3(0.0f, 1.0f, 0.0f),
|
|
glm::vec3(0.0f, -1.0f, 0.0f),
|
|
glm::vec3(0.0f, 0.0f, 1.0f),
|
|
glm::vec3(0.0f, 0.0f, -1.0f)};
|
|
const glm::vec3 up[6] = {glm::vec3(0.0f, 1.0f, 0.0f),
|
|
glm::vec3(0.0f, -1.0f, 0.0f),
|
|
glm::vec3(1.0f, 0.0f, 0.0f),
|
|
glm::vec3(-1.0f, 0.0f, 0.0f),
|
|
glm::vec3(0.0f, 1.0f, 0.0f),
|
|
glm::vec3(0.0f, -1.0f, 0.0f)};
|
|
|
|
shadowMaps[light].bind();
|
|
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
|
|
face[i], shadowMaps[light].getID(), 0);
|
|
glClear(GL_DEPTH_BUFFER_BIT);
|
|
glEnable(GL_DEPTH_TEST);
|
|
|
|
glm::mat4 view = glm::lookAt(lightPos[light],
|
|
lightPos[light]+dir[i],
|
|
up[i]);
|
|
glUniformMatrix4fv(prog.getUniformLocation("view_matrix"), 1, GL_FALSE,
|
|
glm::value_ptr(view));
|
|
|
|
glm::mat4 model = glm::translate(glm::vec3(0.5f, 0.5f, -0.5f)) *
|
|
glm::rotate(SDL_GetTicks()*0.001f, glm::vec3(1.0f, 0.0f, 0.0f)) *
|
|
glm::translate(glm::vec3(-0.5f, -0.5f, 0.5f));
|
|
glUniformMatrix4fv(shadowProg.getUniformLocation("model_matrix"), 1, GL_FALSE,
|
|
glm::value_ptr(model));
|
|
box.draw();
|
|
model = glm::translate(glm::vec3(2.0f, -2.5f, 0.0f)) *
|
|
glm::rotate(0.35f, glm::vec3(0.0f, 1.0f, 0.0f));
|
|
glUniformMatrix4fv(shadowProg.getUniformLocation("model_matrix"), 1, GL_FALSE,
|
|
glm::value_ptr(model));
|
|
plane.draw();
|
|
model = glm::translate(glm::vec3(-2.0f, 0.0f, 0.0f));
|
|
glUniformMatrix4fv(shadowProg.getUniformLocation("model_matrix"), 1, GL_FALSE,
|
|
glm::value_ptr(model));
|
|
pyramid.draw();
|
|
}
|
|
}
|
|
|
|
GLsync fence = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, UnusedMask());
|
|
|
|
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
|
|
glViewport(origVP[0], origVP[1], origVP[2], origVP[3]);
|
|
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
|
glEnable(GL_DEPTH_TEST);
|
|
glEnable(GL_CULL_FACE);
|
|
glDisable(GL_POLYGON_OFFSET_FILL);
|
|
glDisable(GL_POLYGON_OFFSET_LINE);
|
|
glDisable(GL_POLYGON_OFFSET_POINT);
|
|
|
|
glActiveTexture(GL_TEXTURE2);
|
|
shadowMaps[1].bind();
|
|
glActiveTexture(GL_TEXTURE1);
|
|
shadowMaps[0].bind();
|
|
glActiveTexture(GL_TEXTURE0);
|
|
|
|
glm::mat4 model = glm::translate(glm::vec3(0.5f, 0.5f, -0.5f)) *
|
|
glm::rotate(SDL_GetTicks()*0.001f, glm::vec3(1.0f, 0.0f, 0.0f)) *
|
|
glm::translate(glm::vec3(-0.5f, -0.5f, 0.5f));
|
|
cubeTex.bind();
|
|
prog.use();
|
|
|
|
glUniformMatrix4fv(prog.getUniformLocation("model_matrix"), 1, GL_FALSE,
|
|
glm::value_ptr(model));
|
|
|
|
// Shadow maps must be rendered before real drawing can begin
|
|
glClientWaitSync(fence, SyncObjectMask(), 0xffffffffu);
|
|
glDeleteSync(fence);
|
|
|
|
|
|
box.draw();
|
|
|
|
whiteTex.bind();
|
|
model = glm::translate(glm::vec3(2.0f, -2.5f, 0.0f)) *
|
|
glm::rotate(0.35f, glm::vec3(0.0f, 1.0f, 0.0f));
|
|
glUniformMatrix4fv(prog.getUniformLocation("model_matrix"), 1, GL_FALSE,
|
|
glm::value_ptr(model));
|
|
plane.draw();
|
|
|
|
redTex.bind();
|
|
model = glm::translate(glm::vec3(-2.0f, 0.0f, 0.0f));
|
|
glUniformMatrix4fv(prog.getUniformLocation("model_matrix"), 1, GL_FALSE,
|
|
glm::value_ptr(model));
|
|
pyramid.draw();
|
|
|
|
auto now = SDL_GetTicks();
|
|
auto elapsed = now - last;
|
|
last = now;
|
|
if (fpsTime+elapsed > 1000) {
|
|
fpsText.setText(std::to_string(fpsCount + 1) + " FPS");
|
|
|
|
fpsCount = 0;
|
|
fpsTime = 0;
|
|
} else {
|
|
++fpsCount;
|
|
fpsTime += elapsed;
|
|
}
|
|
|
|
glDisable(GL_DEPTH_TEST);
|
|
glEnable(GL_BLEND);
|
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
|
ovlProg.use();
|
|
|
|
fpsText.renderToGL();
|
|
layout->renderToGL();
|
|
glDisable(GL_BLEND);
|
|
|
|
SDL_GL_SwapWindow(window);
|
|
}
|
|
}catch(Exception &ex) {
|
|
std::printf("%s\n", ex.toString().c_str());
|
|
retcode = 1;
|
|
}
|
|
|
|
SDL_GL_DeleteContext(glcontext);
|
|
|
|
SDL_DestroyWindow(window);
|
|
|
|
// Clean up
|
|
TTF_Quit();
|
|
IMG_Quit();
|
|
SDL_Quit();
|
|
|
|
return retcode;
|
|
}
|