GLUT apparently doesn't do double buffers by default, (at least sometimes) so we need to explicitly set it in init.
240 lines
6.4 KiB
C++
240 lines
6.4 KiB
C++
#include <GL/glew.h>
|
|
#include <GL/freeglut.h>
|
|
|
|
#include <glm/mat4x4.hpp>
|
|
#include <glm/ext/matrix_transform.hpp>
|
|
#include <glm/gtc/type_ptr.hpp>
|
|
|
|
#define STB_IMAGE_IMPLEMENTATION
|
|
#include <stb_image.h>
|
|
|
|
#include <webp/decode.h>
|
|
|
|
#include <fmt/core.h>
|
|
|
|
#include <iostream>
|
|
#include <chrono>
|
|
|
|
#include <graphics.hpp>
|
|
#include <modelpart.hpp>
|
|
#include <model.hpp>
|
|
#include <paths.hpp>
|
|
#include <config.hpp>
|
|
#include <cv.hpp>
|
|
#include <args.hpp>
|
|
#include <error.hpp>
|
|
|
|
GLuint shader; //standard shader program used for all elements
|
|
GLuint transUniform; //location of the "transMatrix" transformation matrix uniform in the shader
|
|
|
|
float windowAspectRatio;
|
|
|
|
int frameCount;
|
|
std::chrono::high_resolution_clock::time_point secondStart;
|
|
|
|
//parts of the model (see modelpart.hpp)
|
|
Model* model;
|
|
|
|
void display () {
|
|
glClear(GL_COLOR_BUFFER_BIT);
|
|
windowAspectRatio = glutGet(GLUT_WINDOW_WIDTH) / (float)glutGet(GLUT_WINDOW_HEIGHT);
|
|
|
|
model->draw();
|
|
|
|
glutSwapBuffers();
|
|
}
|
|
|
|
void initBuffers (GLuint* vaoNum) {
|
|
//TODO: put quad stuff in header file or something
|
|
GLfloat quad[] = {
|
|
//vertex UV/texcoord
|
|
0.5f, 0.5f, 1.0f, 0.0f,
|
|
0.5f, -0.5f, 1.0f, 1.0f,
|
|
-0.5f, -0.5f, 0.0f, 1.0f,
|
|
-0.5f, 0.5f, 0.0f, 0.0f,
|
|
};
|
|
|
|
GLuint quadElements[] = {
|
|
0, 1, 2,
|
|
2, 3, 0
|
|
};
|
|
|
|
glGenVertexArrays(1, vaoNum);
|
|
glBindVertexArray(*vaoNum);
|
|
|
|
GLuint vbo; //vertex buffer
|
|
glGenBuffers(1, &vbo);
|
|
glBindBuffer(GL_ARRAY_BUFFER, vbo);
|
|
//change to GL_DYNAMIC_DRAW when using deformable meshes
|
|
glBufferData(GL_ARRAY_BUFFER, sizeof(quad), quad, GL_STATIC_DRAW);
|
|
|
|
GLuint ebo; //element buffer
|
|
glGenBuffers(1, &ebo);
|
|
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
|
|
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(quadElements), quadElements, GL_STATIC_DRAW);
|
|
|
|
//tell OpenGL what to put in the input vars
|
|
GLuint posAttr = glGetAttribLocation(shader, "position");
|
|
glVertexAttribPointer(posAttr, 2, GL_FLOAT, GL_FALSE, 4*sizeof(GLfloat), 0);
|
|
glEnableVertexAttribArray(posAttr);
|
|
GLuint uvAttr = glGetAttribLocation(shader, "texcoord");
|
|
glVertexAttribPointer(uvAttr, 2, GL_FLOAT, GL_FALSE, 4*sizeof(GLfloat), (void*)(2*sizeof(GLfloat)));
|
|
glEnableVertexAttribArray(uvAttr);
|
|
}
|
|
|
|
void fpsCounterReset() {
|
|
glutSetWindowTitle(fmt::format("{0} - {1} @ {2} FPS", PROJECT_NAME, model->getName(), frameCount).c_str());
|
|
|
|
secondStart = std::chrono::high_resolution_clock::now();
|
|
frameCount = 0;
|
|
}
|
|
|
|
void initGraphics () {
|
|
int argc = 1;
|
|
char *argv[1] = {(char*)"fc2d"};
|
|
|
|
glutInit(&argc, argv);
|
|
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB);
|
|
glutInitWindowSize(512, 512);
|
|
glutCreateWindow(PROJECT_NAME);
|
|
|
|
glewExperimental = GL_TRUE;
|
|
glewInit();
|
|
|
|
initShader();
|
|
|
|
model = new Model(resolvePath(("models/"+optData.model+".fma").c_str()).c_str());
|
|
|
|
fpsCounterReset();
|
|
|
|
//enable blending for alpha textures
|
|
glEnable(GL_BLEND);
|
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
|
|
|
//set background color
|
|
glClearColor(0.5f, 0.5f, 0.5f, 1.0f);
|
|
|
|
glutDisplayFunc(display);
|
|
|
|
|
|
std::cout << "graphics init complete" << std::endl;
|
|
}
|
|
|
|
void initTexture (GLuint* texNum, unsigned char* buffer, size_t bufferLength) {
|
|
glGenTextures(1, texNum);
|
|
glBindTexture(GL_TEXTURE_2D, *texNum);
|
|
|
|
int x, y, channels;
|
|
GLubyte* pixels;
|
|
int webp = WebPGetInfo(buffer, bufferLength, &x, &y);
|
|
if (webp) {
|
|
pixels = WebPDecodeRGBA(buffer, bufferLength, &x, &y);
|
|
} else {
|
|
//try stb_image (png, jpg, gif, etc)
|
|
pixels = stbi_load_from_memory(buffer, bufferLength, &x, &y, &channels, 4);
|
|
if (!pixels) {
|
|
showError("Corrupt or unsupported texture format!", "Could not load texture", false);
|
|
|
|
GLubyte defaultPixels[] =
|
|
{255, 0, 255, 255, 0, 0, 0, 255,
|
|
0, 0, 0, 255, 255, 0, 255, 255 };
|
|
|
|
pixels = defaultPixels;
|
|
x = 2;
|
|
y = 2;
|
|
}
|
|
}
|
|
|
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, x, y, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
|
}
|
|
|
|
void initShader() {
|
|
const char* vsSrc =
|
|
"#version 120\n"
|
|
"attribute vec2 position;"
|
|
"attribute vec2 texcoord;"
|
|
"varying vec2 uv;"
|
|
"uniform mat4 transMatrix;"
|
|
"void main () {"
|
|
"gl_Position = transMatrix * vec4(position, 0.0, 1.0);"
|
|
"uv = texcoord;"
|
|
"}";
|
|
|
|
const char* fsSrc =
|
|
"#version 120\n"
|
|
"varying vec2 uv;"
|
|
"uniform sampler2D tex;"
|
|
"void main () {"
|
|
"gl_FragColor = texture2D(tex, uv);"
|
|
"}";
|
|
|
|
//compile vert shader
|
|
GLuint vs = glCreateShader(GL_VERTEX_SHADER);
|
|
glShaderSource(vs, 1, &vsSrc, NULL);
|
|
glCompileShader(vs);
|
|
GLint status;
|
|
glGetShaderiv(vs, GL_COMPILE_STATUS, &status);
|
|
if (status != GL_TRUE) { std::cout << "vertex shader borked" << std::endl;
|
|
printShaderCompileLog(vs); }
|
|
|
|
//compile frag shader
|
|
GLuint fs = glCreateShader(GL_FRAGMENT_SHADER);
|
|
glShaderSource(fs, 1, &fsSrc, NULL);
|
|
glCompileShader(fs);
|
|
glGetShaderiv(fs, GL_COMPILE_STATUS, &status);
|
|
if (status != GL_TRUE) { std::cout << "fragment shader borked" << std::endl;
|
|
printShaderCompileLog(vs); }
|
|
|
|
//link shaders into a shader program
|
|
shader = glCreateProgram();
|
|
glAttachShader(shader, vs);
|
|
glAttachShader(shader, fs);
|
|
//glBindFragDataLocation(shader, 0, "color");
|
|
glLinkProgram(shader);
|
|
glUseProgram(shader);
|
|
|
|
//set an identity ("do nothing") transformation matrix as default
|
|
transUniform = glGetUniformLocation(shader, "transMatrix");
|
|
glUniformMatrix4fv(transUniform, 1, GL_FALSE, glm::value_ptr(glm::mat4(1.0f)));
|
|
}
|
|
|
|
void graphicsFrame () {
|
|
glutMainLoopEvent();
|
|
|
|
auto timeSinceSecondStart = std::chrono::high_resolution_clock::now() - secondStart;
|
|
frameCount++;
|
|
long usSinceSecondStart = std::chrono::duration_cast<std::chrono::microseconds>(timeSinceSecondStart).count();
|
|
|
|
if (usSinceSecondStart >= 1000000) {
|
|
fpsCounterReset();
|
|
}
|
|
}
|
|
|
|
void printShaderCompileLog(GLuint shader) {
|
|
char logBuffer[1024];
|
|
glGetShaderInfoLog(shader, 1024, NULL, logBuffer);
|
|
std::cout << "Compile log for shader " << shader << std::endl;
|
|
std::cout << logBuffer << std::endl;
|
|
}
|
|
|
|
void updateModel(struct FaceData faceData) {
|
|
/*
|
|
//calculate transforms
|
|
parts[0].setTransform(headPos, rotation, scale);
|
|
parts[1].setTransform(facePos, rotation, scale);
|
|
parts[2].setTransform(facePos, rotation, scale);
|
|
|
|
//set mouth texture to open or closed
|
|
parts[2].selectTexture(mouthOpen ? 1 : 0);
|
|
*/
|
|
model->updateTransforms(faceData);
|
|
|
|
//tell FreeGLUT to schedule a screen update
|
|
glutPostRedisplay();
|
|
}
|