lovr/src/graphics/graphics.c

546 lines
14 KiB
C
Raw Normal View History

2016-09-27 07:24:28 +00:00
#define _USE_MATH_DEFINES
2016-07-07 07:04:24 +00:00
#include "graphics.h"
2016-08-08 01:32:37 +00:00
#include "../glfw.h"
#include "../util.h"
2016-07-16 05:39:17 +00:00
#include <stdlib.h>
2016-09-27 07:24:28 +00:00
#include <math.h>
2016-07-07 07:04:24 +00:00
#include <assimp/cimport.h>
#include <assimp/scene.h>
#include <assimp/postprocess.h>
2016-09-14 00:02:23 +00:00
2016-09-27 06:48:09 +00:00
static GraphicsState state;
2016-07-07 07:04:24 +00:00
2016-08-10 06:28:17 +00:00
void lovrGraphicsInit() {
2016-09-27 06:48:09 +00:00
vec_init(&state.transforms);
state.projection = mat4_init();
2016-10-04 04:54:27 +00:00
state.defaultShader = lovrShaderCreate(lovrDefaultVertexShader, lovrDefaultFragmentShader);
2016-10-24 22:02:23 +00:00
state.skyboxShader = lovrShaderCreate(lovrSkyboxVertexShader, lovrSkyboxFragmentShader);
2016-09-29 07:00:02 +00:00
glGenBuffers(1, &state.shapeBuffer);
2016-09-30 06:18:51 +00:00
glGenBuffers(1, &state.shapeIndexBuffer);
2016-09-29 07:00:02 +00:00
glGenVertexArrays(1, &state.shapeArray);
vec_init(&state.shapeData);
2016-09-30 06:18:51 +00:00
vec_init(&state.shapeIndices);
2016-09-28 03:20:08 +00:00
lovrGraphicsReset();
}
void lovrGraphicsDestroy() {
vec_deinit(&state.transforms);
mat4_deinit(state.projection);
lovrShaderDestroy(state.defaultShader);
2016-10-24 22:02:23 +00:00
lovrShaderDestroy(state.skyboxShader);
glDeleteBuffers(1, &state.shapeBuffer);
glDeleteBuffers(1, &state.shapeIndexBuffer);
glDeleteVertexArrays(1, &state.shapeArray);
vec_deinit(&state.shapeData);
vec_deinit(&state.shapeIndices);
}
2016-09-28 03:20:08 +00:00
void lovrGraphicsReset() {
int i;
mat4 matrix;
vec_foreach(&state.transforms, matrix, i) {
mat4_deinit(matrix);
}
vec_clear(&state.transforms);
vec_push(&state.transforms, mat4_init());
2016-09-29 03:11:58 +00:00
lovrGraphicsSetProjection(.1f, 100.f, 67 * M_PI / 180); // TODO customize via lovr.conf
2016-09-28 02:56:36 +00:00
lovrGraphicsSetShader(state.defaultShader);
2016-09-29 03:11:58 +00:00
lovrGraphicsSetBackgroundColor(0, 0, 0, 0);
lovrGraphicsSetColor(255, 255, 255, 255);
2016-09-28 04:32:57 +00:00
lovrGraphicsSetColorMask(1, 1, 1, 1);
2016-09-29 04:47:36 +00:00
lovrGraphicsSetScissorEnabled(0);
2016-10-01 20:48:31 +00:00
lovrGraphicsSetLineWidth(1);
2016-11-13 01:38:49 +00:00
lovrGraphicsSetPointSize(1);
2016-10-03 19:02:49 +00:00
lovrGraphicsSetCullingEnabled(0);
lovrGraphicsSetPolygonWinding(POLYGON_WINDING_COUNTERCLOCKWISE);
2016-08-10 06:28:17 +00:00
}
2016-07-07 07:04:24 +00:00
2016-09-17 03:11:11 +00:00
void lovrGraphicsClear(int color, int depth) {
int bits = 0;
if (color) {
bits |= GL_COLOR_BUFFER_BIT;
}
if (depth) {
bits |= GL_DEPTH_BUFFER_BIT;
}
glClear(bits);
2016-07-07 07:04:24 +00:00
}
2016-08-10 06:28:17 +00:00
void lovrGraphicsPresent() {
2016-07-07 07:04:24 +00:00
glfwSwapBuffers(window);
}
2016-09-24 03:58:56 +00:00
void lovrGraphicsPrepare() {
Shader* shader = lovrGraphicsGetShader();
lovrShaderBind(shader, vec_last(&state.transforms), state.projection, state.color, 0);
2016-09-24 03:58:56 +00:00
}
2016-09-28 04:37:46 +00:00
void lovrGraphicsGetBackgroundColor(float* r, float* g, float* b, float* a) {
2016-07-28 02:48:59 +00:00
GLfloat clearColor[4];
glGetFloatv(GL_COLOR_CLEAR_VALUE, clearColor);
2016-08-10 06:28:17 +00:00
*r = clearColor[0];
*g = clearColor[1];
*b = clearColor[2];
*a = clearColor[3];
2016-07-28 02:48:59 +00:00
}
2016-09-28 04:37:46 +00:00
void lovrGraphicsSetBackgroundColor(float r, float g, float b, float a) {
2016-08-08 20:51:22 +00:00
glClearColor(r / 255, g / 255, b / 255, a / 255);
2016-07-28 02:48:59 +00:00
}
2016-09-29 03:11:58 +00:00
void lovrGraphicsGetColor(unsigned char* r, unsigned char* g, unsigned char* b, unsigned char* a) {
*r = LOVR_COLOR_R(state.color);
*g = LOVR_COLOR_G(state.color);
*b = LOVR_COLOR_B(state.color);
*a = LOVR_COLOR_A(state.color);
}
void lovrGraphicsSetColor(unsigned char r, unsigned char g, unsigned char b, unsigned char a) {
state.color = LOVR_COLOR(r, g, b, a);
}
2016-10-03 18:41:31 +00:00
void lovrGraphicsGetColorMask(char* r, char* g, char* b, char* a) {
char mask = state.colorMask;
2016-09-28 04:32:57 +00:00
*r = mask & 0x1;
*g = mask & 0x2;
*b = mask & 0x4;
*a = mask & 0x8;
}
2016-10-03 18:41:31 +00:00
void lovrGraphicsSetColorMask(char r, char g, char b, char a) {
2016-09-28 04:32:57 +00:00
state.colorMask = ((r & 1) << 0) | ((g & 1) << 1) | ((b & 1) << 2) | ((a & 1) << 3);
glColorMask(r, g, b, a);
}
2016-09-29 04:47:36 +00:00
char lovrGraphicsIsScissorEnabled() {
return state.isScissorEnabled;
}
void lovrGraphicsSetScissorEnabled(char isEnabled) {
state.isScissorEnabled = isEnabled;
if (isEnabled) {
glEnable(GL_SCISSOR_TEST);
} else {
glDisable(GL_SCISSOR_TEST);
}
}
void lovrGraphicsGetScissor(int* x, int* y, int* width, int* height) {
*x = state.scissor.x;
*y = state.scissor.x;
*width = state.scissor.width;
*height = state.scissor.height;
}
void lovrGraphicsSetScissor(int x, int y, int width, int height) {
int windowWidth, windowHeight;
2016-09-29 05:09:57 +00:00
glfwGetFramebufferSize(window, &windowWidth, &windowHeight);
2016-09-29 04:47:36 +00:00
state.scissor.x = x;
state.scissor.x = y;
state.scissor.width = width;
state.scissor.height = height;
glScissor(x, windowHeight - y, width, height);
}
2016-09-14 00:02:23 +00:00
Shader* lovrGraphicsGetShader() {
2016-09-27 06:48:09 +00:00
return state.activeShader;
2016-09-14 00:02:23 +00:00
}
2016-08-10 06:28:17 +00:00
void lovrGraphicsSetShader(Shader* shader) {
2016-09-28 02:56:36 +00:00
if (!shader) {
shader = state.defaultShader;
}
2016-09-27 06:48:09 +00:00
state.activeShader = shader;
2016-07-16 02:17:27 +00:00
}
2016-09-27 06:48:09 +00:00
void lovrGraphicsSetProjection(float near, float far, float fov) {
int width, height;
glfwGetWindowSize(window, &width, &height);
mat4_setProjection(state.projection, near, far, fov, (float) width / height);
2016-09-24 05:11:56 +00:00
}
2016-09-27 07:24:28 +00:00
void lovrGraphicsSetProjectionRaw(mat4 projection) {
memcpy(state.projection, projection, 16 * sizeof(float));
}
2016-10-01 20:48:31 +00:00
float lovrGraphicsGetLineWidth() {
return state.lineWidth;
}
void lovrGraphicsSetLineWidth(float width) {
state.lineWidth = width;
glLineWidth(width);
}
2016-11-13 01:38:49 +00:00
float lovrGraphicsGetPointSize() {
return state.pointSize;
}
void lovrGraphicsSetPointSize(float size) {
state.pointSize = size;
glPointSize(size);
}
2016-10-03 19:02:49 +00:00
char lovrGraphicsIsCullingEnabled() {
return state.isCullingEnabled;
}
void lovrGraphicsSetCullingEnabled(char isEnabled) {
state.isCullingEnabled = isEnabled;
if (isEnabled) {
glEnable(GL_CULL_FACE);
} else {
glDisable(GL_CULL_FACE);
}
}
PolygonWinding lovrGraphicsGetPolygonWinding() {
return state.polygonWinding;
}
void lovrGraphicsSetPolygonWinding(PolygonWinding winding) {
state.polygonWinding = winding;
glFrontFace(winding);
}
2016-09-21 07:55:53 +00:00
int lovrGraphicsPush() {
2016-09-27 06:48:09 +00:00
vec_mat4_t* transforms = &state.transforms;
2016-09-21 22:26:05 +00:00
if (transforms->length >= 64) { return 1; }
vec_push(transforms, mat4_copy(vec_last(transforms)));
2016-09-21 07:55:53 +00:00
return 0;
}
int lovrGraphicsPop() {
2016-09-27 06:48:09 +00:00
vec_mat4_t* transforms = &state.transforms;
2016-09-21 22:26:05 +00:00
if (transforms->length <= 1) { return 1; }
2016-09-21 07:55:53 +00:00
mat4_deinit(vec_pop(transforms));
return 0;
}
2016-09-21 22:26:05 +00:00
void lovrGraphicsOrigin() {
2016-09-27 06:48:09 +00:00
vec_mat4_t* transforms = &state.transforms;
2016-09-21 22:26:05 +00:00
mat4_setIdentity(vec_last(transforms));
}
void lovrGraphicsTranslate(float x, float y, float z) {
2016-09-27 06:48:09 +00:00
mat4_translate(vec_last(&state.transforms), x, y, z);
}
void lovrGraphicsRotate(float w, float x, float y, float z) {
2016-09-27 06:48:09 +00:00
mat4_rotate(vec_last(&state.transforms), w, x, y, z);
}
void lovrGraphicsScale(float x, float y, float z) {
2016-09-27 06:48:09 +00:00
mat4_scale(vec_last(&state.transforms), x, y, z);
}
2016-11-02 03:48:04 +00:00
void lovrGraphicsTransform(float tx, float ty, float tz, float sx, float sy, float sz, float angle, float ax, float ay, float az) {
2016-11-08 06:03:13 +00:00
// Normalize rotation vector
float len = sqrtf(ax * ax + ay * ay + az * az);
if (len != 1 && len != 0) {
len = 1 / len;
ax *= len;
ay *= len;
az *= len;
}
// Convert angle-axis to quaternion
2016-11-02 03:48:04 +00:00
float cos2 = cos(angle / 2.f);
float sin2 = sin(angle / 2.f);
float qw = cos2;
float qx = sin2 * ax;
float qy = sin2 * ay;
float qz = sin2 * az;
2016-11-08 06:03:13 +00:00
// M *= T * S * R
2016-11-02 03:48:04 +00:00
float transform[16];
mat4_setTranslation(transform, tx, ty, tz);
mat4_scale(transform, sx, sy, sz);
mat4_rotate(transform, qw, qx, qy, qz);
lovrGraphicsMatrixTransform(transform);
}
void lovrGraphicsMatrixTransform(mat4 transform) {
2016-09-29 07:21:38 +00:00
mat4_multiply(vec_last(&state.transforms), transform);
}
int lovrGraphicsGetWidth() {
int width;
glfwGetFramebufferSize(window, &width, NULL);
return width;
}
int lovrGraphicsGetHeight() {
int height;
glfwGetFramebufferSize(window, NULL, &height);
return height;
}
2016-10-24 02:01:27 +00:00
void lovrGraphicsSetShapeData(float* data, int dataCount, unsigned int* indices, int indicesCount) {
vec_clear(&state.shapeIndices);
vec_clear(&state.shapeData);
if (data) {
vec_pusharr(&state.shapeData, data, dataCount);
}
if (indices) {
vec_pusharr(&state.shapeIndices, indices, indicesCount);
}
}
2016-10-07 06:34:35 +00:00
void lovrGraphicsDrawLinedShape(GLenum mode) {
vec_float_t* vertices = &state.shapeData;
vec_uint_t* indices = &state.shapeIndices;
lovrGraphicsPrepare();
2016-09-29 07:00:02 +00:00
glBindVertexArray(state.shapeArray);
glBindBuffer(GL_ARRAY_BUFFER, state.shapeBuffer);
2016-10-07 06:34:35 +00:00
glBufferData(GL_ARRAY_BUFFER, vertices->length * sizeof(float), vertices->data, GL_STREAM_DRAW);
glEnableVertexAttribArray(0);
2016-09-29 07:00:02 +00:00
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, NULL);
2016-09-30 06:18:51 +00:00
2016-10-07 06:34:35 +00:00
if (indices->length > 0) {
2016-09-30 06:18:51 +00:00
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, state.shapeIndexBuffer);
2016-10-07 06:34:35 +00:00
glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices->length * sizeof(unsigned int), indices->data, GL_STREAM_DRAW);
glDrawElements(mode, indices->length, GL_UNSIGNED_INT, NULL);
} else {
glDrawArrays(mode, 0, vertices->length / 3);
2016-09-30 06:18:51 +00:00
}
2016-09-29 07:00:02 +00:00
}
2016-10-07 06:34:35 +00:00
void lovrGraphicsDrawFilledShape() {
vec_float_t* vertices = &state.shapeData;
int stride = 6;
2016-10-08 01:30:37 +00:00
int strideBytes = stride * sizeof(float);
2016-10-07 06:34:35 +00:00
2016-09-29 07:00:02 +00:00
lovrGraphicsPrepare();
glBindVertexArray(state.shapeArray);
2016-10-07 06:34:35 +00:00
glBindBuffer(GL_ARRAY_BUFFER, state.shapeBuffer);
glBufferData(GL_ARRAY_BUFFER, vertices->length * sizeof(float), vertices->data, GL_STREAM_DRAW);
2016-10-08 01:30:37 +00:00
glEnableVertexAttribArray(LOVR_SHADER_POSITION);
glVertexAttribPointer(LOVR_SHADER_POSITION, 3, GL_FLOAT, GL_FALSE, strideBytes, (void*) 0);
glEnableVertexAttribArray(LOVR_SHADER_NORMAL);
glVertexAttribPointer(LOVR_SHADER_NORMAL, 3, GL_FLOAT, GL_FALSE, strideBytes, (void*) (3 * sizeof(float)));
2016-10-24 02:01:27 +00:00
glDrawArrays(GL_TRIANGLE_STRIP, 0, vertices->length / stride);
2016-10-07 06:34:35 +00:00
glBindVertexArray(0);
2016-09-30 02:39:25 +00:00
}
2016-11-08 07:16:33 +00:00
void lovrGraphicsPoints(float* points, int count) {
lovrGraphicsSetShapeData(points, count, NULL, 0);
lovrGraphicsDrawLinedShape(GL_POINTS);
}
2016-09-30 02:39:25 +00:00
void lovrGraphicsLine(float* points, int count) {
2016-10-24 02:01:27 +00:00
lovrGraphicsSetShapeData(points, count, NULL, 0);
2016-10-07 06:34:35 +00:00
lovrGraphicsDrawLinedShape(GL_LINE_STRIP);
2016-09-29 07:00:02 +00:00
}
2016-10-04 03:56:45 +00:00
void lovrGraphicsPlane(DrawMode mode, float x, float y, float z, float size, float nx, float ny, float nz) {
// Normalize the normal vector
float len = sqrt(nx * nx + ny * ny + nz + nz);
if (len != 1) {
len = 1 / len;
nx *= len;
ny *= len;
nz *= len;
}
// Vector perpendicular to the normal vector and the vector of the default geometry (cross product)
float cx = -ny;
float cy = nx;
float cz = 0.f;
// Angle between normal vector and the normal vector of the default geometry (dot product)
float theta = acos(nz);
lovrGraphicsPush();
2016-11-02 03:48:04 +00:00
lovrGraphicsTransform(x, y, z, size, size, size, theta, cx, cy, cz);
2016-10-04 03:56:45 +00:00
if (mode == DRAW_MODE_LINE) {
2016-10-07 06:34:35 +00:00
float points[] = {
-.5, .5, 0,
.5, .5, 0,
.5, -.5, 0,
-.5, -.5, 0
};
2016-10-24 02:01:27 +00:00
lovrGraphicsSetShapeData(points, 12, NULL, 0);
2016-10-07 06:34:35 +00:00
lovrGraphicsDrawLinedShape(GL_LINE_LOOP);
2016-10-04 03:56:45 +00:00
} else if (mode == DRAW_MODE_FILL) {
2016-10-07 06:34:35 +00:00
float data[] = {
-.5, .5, 0, 0, 0, -1,
-.5, -.5, 0, 0, 0, -1,
.5, .5, 0, 0, 0, -1,
.5, -.5, 0, 0, 0, -1
};
2016-10-24 02:01:27 +00:00
lovrGraphicsSetShapeData(data, 24, NULL, 0);
2016-10-07 06:34:35 +00:00
lovrGraphicsDrawFilledShape();
2016-10-04 03:56:45 +00:00
}
lovrGraphicsPop();
}
2016-10-01 20:39:38 +00:00
void lovrGraphicsCube(DrawMode mode, float x, float y, float z, float size, float angle, float axisX, float axisY, float axisZ) {
2016-10-01 19:53:15 +00:00
lovrGraphicsPush();
2016-11-02 03:48:04 +00:00
lovrGraphicsTransform(x, y, z, size, size, size, angle, axisX, axisY, axisZ);
2016-10-01 19:10:38 +00:00
2016-10-01 19:53:15 +00:00
if (mode == DRAW_MODE_LINE) {
2016-10-07 06:34:35 +00:00
float points[] = {
// Front
-.5, .5, -.5,
.5, .5, -.5,
.5, -.5, -.5,
-.5, -.5, -.5,
// Back
-.5, .5, .5,
.5, .5, .5,
.5, -.5, .5,
-.5, -.5, .5
};
2016-10-01 19:10:38 +00:00
unsigned int indices[] = {
2016-10-01 19:53:15 +00:00
0, 1, 1, 2, 2, 3, 3, 0, // Front
4, 5, 5, 6, 6, 7, 7, 4, // Back
2016-10-01 19:10:38 +00:00
0, 4, 1, 5, 2, 6, 3, 7 // Connections
};
2016-10-24 02:01:27 +00:00
lovrGraphicsSetShapeData(points, 24, indices, 24);
2016-10-07 06:34:35 +00:00
lovrGraphicsDrawLinedShape(GL_LINES);
2016-10-01 19:10:38 +00:00
} else {
2016-10-07 06:34:35 +00:00
float data[] = {
// Front
-.5, -.5, -.5, 0, 0, -1,
.5, -.5, -.5, 0, 0, -1,
-.5, .5, -.5, 0, 0, -1,
.5, .5, -.5, 0, 0, -1,
// Right
.5, .5, -.5, 1, 0, 0,
.5, -.5, -.5, 1, 0, 0,
.5, .5, .5, 1, 0, 0,
.5, -.5, .5, 1, 0, 0,
// Back
.5, -.5, .5, 0, 0, 1,
-.5, -.5, .5, 0, 0, 1,
.5, .5, .5, 0, 0, 1,
-.5, .5, .5, 0, 0, 1,
// Left
-.5, .5, .5, -1, 0, 0,
-.5, -.5, .5, -1, 0, 0,
-.5, .5, -.5, -1, 0, 0,
-.5, -.5, -.5, -1, 0, 0,
// Bottom
-.5, -.5, -.5, 0, -1, 0,
-.5, -.5, .5, 0, -1, 0,
.5, -.5, -.5, 0, -1, 0,
.5, -.5, .5, 0, -1, 0,
// Adjust
.5, -.5, .5, 0, 1, 0,
-.5, .5, -.5, 0, 1, 0,
// Top
-.5, .5, -.5, 0, 1, 0,
.5, .5, -.5, 0, 1, 0,
-.5, .5, .5, 0, 1, 0,
.5, .5, .5, 0, 1, 0
2016-10-01 19:53:15 +00:00
};
2016-10-24 02:01:27 +00:00
lovrGraphicsSetShapeData(data, 156, NULL, 0);
2016-10-07 06:34:35 +00:00
lovrGraphicsDrawFilledShape();
2016-10-01 19:10:38 +00:00
}
2016-10-01 19:53:15 +00:00
lovrGraphicsPop();
2016-09-30 06:18:51 +00:00
}
2016-10-24 22:02:23 +00:00
void lovrGraphicsSkybox(Skybox* skybox, float angle, float ax, float ay, float az) {
if (!skybox) {
return;
}
Shader* lastShader = lovrGraphicsGetShader();
lovrGraphicsSetShader(state.skyboxShader);
float cos2 = cos(angle / 2);
float sin2 = sin(angle / 2);
float rw = cos2;
float rx = sin2 * ax;
float ry = sin2 * ay;
float rz = sin2 * az;
lovrGraphicsPrepare();
lovrGraphicsPush();
lovrGraphicsOrigin();
lovrGraphicsRotate(rw, rx, ry, rz);
float cube[] = {
// Front
1.f, -1.f, -1.f, 0, 0, 0,
1.f, 1.f, -1.f, 0, 0, 0,
-1.f, -1.f, -1.f, 0, 0, 0,
-1.f, 1.f, -1.f, 0, 0, 0,
// Left
-1.f, 1.f, -1.f, 0, 0, 0,
-1.f, 1.f, 1.f, 0, 0, 0,
-1.f, -1.f, -1.f, 0, 0, 0,
-1.f, -1.f, 1.f, 0, 0, 0,
// Back
-1.f, -1.f, 1.f, 0, 0, 0,
1.f, -1.f, 1.f, 0, 0, 0,
-1.f, 1.f, 1.f, 0, 0, 0,
1.f, 1.f, 1.f, 0, 0, 0,
// Right
1.f, 1.f, 1.f, 0, 0, 0,
1.f, -1.f, 1.f, 0, 0, 0,
1.f, 1.f, -1.f, 0, 0, 0,
1.f, -1.f, -1.f, 0, 0, 0,
// Bottom
1.f, -1.f, -1.f, 0, 0, 0,
1.f, -1.f, 1.f, 0, 0, 0,
-1.f, -1.f, -1.f, 0, 0, 0,
-1.f, -1.f, 1.f, 0, 0, 0,
// Adjust
-1.f, -1.f, 1.f, 0, 0, 0,
-1.f, 1.f, -1.f, 0, 0, 0,
// Top
-1.f, 1.f, -1.f, 0, 0, 0,
-1.f, 1.f, 1.f, 0, 0, 0,
1.f, 1.f, -1.f, 0, 0, 0,
1.f, 1.f, 1.f, 0, 0, 0
};
glDepthMask(GL_FALSE);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_CUBE_MAP, skybox->texture);
lovrGraphicsSetShapeData(cube, 156, NULL, 0);
lovrGraphicsDrawFilledShape();
glBindTexture(GL_TEXTURE_CUBE_MAP, 0);
glDepthMask(GL_TRUE);
lovrGraphicsSetShader(lastShader);
lovrGraphicsPop();
}