mirror of https://github.com/bjornbytes/lovr.git
Morganization;
This commit is contained in:
parent
56bbf1cf56
commit
f57810cd7a
|
@ -57,7 +57,7 @@ int l_lovrMeshDrawInstanced(lua_State* L) {
|
|||
int instances = luaL_checkinteger(L, 2);
|
||||
float transform[16];
|
||||
luax_readtransform(L, 3, transform, 1);
|
||||
lovrGraphicsDraw(&(DrawCommand) {
|
||||
lovrGraphicsDraw(&(DrawOptions) {
|
||||
.transform = transform,
|
||||
.mesh = mesh,
|
||||
.material = lovrMeshGetMaterial(mesh),
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "graphics/opengl/opengl.h"
|
||||
#include "graphics/opengl.h"
|
||||
|
||||
static float* lovrFontAlignLine(float* x, float* lineEnd, float width, HorizontalAlign halign) {
|
||||
while(x < lineEnd) {
|
||||
|
|
|
@ -11,22 +11,12 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
typedef struct {
|
||||
Layer layer;
|
||||
mat4 transform;
|
||||
Shader* shader;
|
||||
Material* material;
|
||||
Mesh* mesh;
|
||||
Pipeline pipeline;
|
||||
int instances;
|
||||
} GpuDrawCommand;
|
||||
|
||||
typedef void (*gpuProc)(void);
|
||||
|
||||
void gpuInit(bool srgb, gpuProc (*getProcAddress)(const char*));
|
||||
void gpuDestroy();
|
||||
void gpuClear(Canvas** canvas, int canvasCount, Color* color, float* depth, int* stencil);
|
||||
void gpuDraw(GpuDrawCommand* command);
|
||||
void gpuDraw(DrawCommand* command);
|
||||
void gpuPresent();
|
||||
|
||||
// Ephemeral
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#include "math/mat4.h"
|
||||
#include "math/vec3.h"
|
||||
#include "util.h"
|
||||
#include "lib/glfw.h"
|
||||
#include "lib/stb/stb_image.h"
|
||||
#define _USE_MATH_DEFINES
|
||||
#include <stdlib.h>
|
||||
|
@ -15,17 +16,14 @@ static GraphicsState state;
|
|||
|
||||
static void onCloseWindow(GLFWwindow* window) {
|
||||
if (window == state.window) {
|
||||
EventType type = EVENT_QUIT;
|
||||
EventData data = { .quit = { false, 0 } };
|
||||
Event event = { .type = type, .data = data };
|
||||
lovrEventPush(event);
|
||||
lovrEventPush((Event) { .type = EVENT_QUIT, .data = { .quit = { false, 0 } } });
|
||||
}
|
||||
}
|
||||
|
||||
// Base
|
||||
|
||||
void lovrGraphicsInit() {
|
||||
//
|
||||
// This page intentionally left blank
|
||||
}
|
||||
|
||||
void lovrGraphicsDestroy() {
|
||||
|
@ -33,12 +31,12 @@ void lovrGraphicsDestroy() {
|
|||
lovrGraphicsSetShader(NULL);
|
||||
lovrGraphicsSetFont(NULL);
|
||||
lovrGraphicsSetCanvas(NULL, 0);
|
||||
for (int i = 0; i < DEFAULT_SHADER_COUNT; i++) {
|
||||
for (int i = 0; i < MAX_DEFAULT_SHADERS; i++) {
|
||||
lovrRelease(state.defaultShaders[i]);
|
||||
}
|
||||
lovrRelease(state.defaultMaterial);
|
||||
lovrRelease(state.defaultFont);
|
||||
lovrRelease(state.mesh);
|
||||
lovrRelease(state.defaultMesh);
|
||||
gpuDestroy();
|
||||
memset(&state, 0, sizeof(GraphicsState));
|
||||
}
|
||||
|
@ -103,11 +101,32 @@ void lovrGraphicsCreateWindow(int w, int h, bool fullscreen, int msaa, const cha
|
|||
vertexFormatAppend(&format, "lovrPosition", ATTR_FLOAT, 3);
|
||||
vertexFormatAppend(&format, "lovrNormal", ATTR_FLOAT, 3);
|
||||
vertexFormatAppend(&format, "lovrTexCoord", ATTR_FLOAT, 2);
|
||||
state.mesh = lovrMeshCreate(64, format, MESH_TRIANGLES, MESH_STREAM);
|
||||
state.defaultMesh = lovrMeshCreate(64, format, MESH_TRIANGLES, MESH_STREAM);
|
||||
lovrGraphicsReset();
|
||||
state.initialized = true;
|
||||
}
|
||||
|
||||
void lovrGraphicsSetCamera(Camera* camera, bool clear) {
|
||||
if (!camera) {
|
||||
int width, height;
|
||||
lovrGraphicsGetDimensions(&width, &height);
|
||||
state.camera.canvas = NULL;
|
||||
state.camera.viewport[0] = 0;
|
||||
state.camera.viewport[1] = 0;
|
||||
state.camera.viewport[2] = width;
|
||||
state.camera.viewport[3] = height;
|
||||
mat4_identity(state.camera.viewMatrix);
|
||||
mat4_perspective(state.camera.projection, .01f, 100.f, 67 * M_PI / 180., (float) width / height);
|
||||
} else {
|
||||
state.camera = *camera;
|
||||
}
|
||||
|
||||
if (clear) {
|
||||
Color backgroundColor = lovrGraphicsGetBackgroundColor();
|
||||
gpuClear(&state.camera.canvas, 1, &backgroundColor, &(float) { 1. }, &(int) { 0 });
|
||||
}
|
||||
}
|
||||
|
||||
void lovrGraphicsGetDimensions(int* width, int* height) {
|
||||
glfwGetFramebufferSize(state.window, width, height);
|
||||
}
|
||||
|
@ -115,17 +134,13 @@ void lovrGraphicsGetDimensions(int* width, int* height) {
|
|||
// State
|
||||
|
||||
void lovrGraphicsReset() {
|
||||
int width, height;
|
||||
lovrGraphicsGetDimensions(&width, &height);
|
||||
state.transform = 0;
|
||||
state.layer = 0;
|
||||
memcpy(state.layers[state.layer].viewport, (int[]) { 0, 0, width, height }, 4 * sizeof(uint32_t));
|
||||
mat4_perspective(state.layers[state.layer].projection, .01f, 100.f, 67 * M_PI / 180., (float) width / height);
|
||||
mat4_identity(state.layers[state.layer].view);
|
||||
lovrGraphicsSetBackgroundColor((Color) { 0, 0, 0, 1. });
|
||||
state.pipeline = 0;
|
||||
lovrGraphicsSetCamera(NULL, false);
|
||||
lovrGraphicsSetBackgroundColor((Color) { 0, 0, 0, 1 });
|
||||
lovrGraphicsSetBlendMode(BLEND_ALPHA, BLEND_ALPHA_MULTIPLY);
|
||||
lovrGraphicsSetCanvas(NULL, 0);
|
||||
lovrGraphicsSetColor((Color) { 1., 1., 1., 1. });
|
||||
lovrGraphicsSetColor((Color) { 1, 1, 1, 1 });
|
||||
lovrGraphicsSetCullingEnabled(false);
|
||||
lovrGraphicsSetDefaultFilter((TextureFilter) { .mode = FILTER_TRILINEAR });
|
||||
lovrGraphicsSetDepthTest(COMPARE_LEQUAL, true);
|
||||
|
@ -139,6 +154,15 @@ void lovrGraphicsReset() {
|
|||
lovrGraphicsOrigin();
|
||||
}
|
||||
|
||||
void lovrGraphicsPushPipeline() {
|
||||
lovrAssert(++state.pipeline < MAX_PIPELINES, "Unbalanced pipeline stack (more pushes than pops?)");
|
||||
memcpy(&state.pipelines[state.pipeline], &state.pipelines[state.pipeline - 1], sizeof(Pipeline));
|
||||
}
|
||||
|
||||
void lovrGraphicsPopPipeline() {
|
||||
lovrAssert(--state.pipeline >= 0, "Unbalanced pipeline stack (more pops than pushes?)");
|
||||
}
|
||||
|
||||
Color lovrGraphicsGetBackgroundColor() {
|
||||
return state.pipelines[state.pipeline].backgroundColor;
|
||||
}
|
||||
|
@ -158,21 +182,21 @@ void lovrGraphicsSetBlendMode(BlendMode mode, BlendAlphaMode alphaMode) {
|
|||
}
|
||||
|
||||
void lovrGraphicsGetCanvas(Canvas** canvas, int* count) {
|
||||
Layer layer = state.layers[state.layer];
|
||||
if (layer.user) {
|
||||
*count = layer.canvasCount;
|
||||
memcpy(canvas, layer.canvas, layer.canvasCount * sizeof(Canvas*));
|
||||
} else {
|
||||
*count = 0;
|
||||
}
|
||||
*count = state.pipelines[state.pipeline].canvasCount;
|
||||
memcpy(canvas, state.pipelines[state.pipeline].canvas, *count);
|
||||
}
|
||||
|
||||
void lovrGraphicsSetCanvas(Canvas** canvas, int count) {
|
||||
if (state.layers[state.layer].user) {
|
||||
lovrGraphicsPopLayer();
|
||||
for (int i = 0; i < count; i++) {
|
||||
lovrRetain(canvas[i]);
|
||||
}
|
||||
|
||||
lovrGraphicsPushLayer(canvas, count, true);
|
||||
for (int i = 0; i < state.pipelines[state.pipeline].canvasCount; i++) {
|
||||
lovrRelease(state.pipelines[state.pipeline].canvas[i]);
|
||||
}
|
||||
|
||||
memcpy(state.pipelines[state.pipeline].canvas, canvas, count * sizeof(Canvas*));
|
||||
state.pipelines[state.pipeline].canvasCount = count;
|
||||
}
|
||||
|
||||
Color lovrGraphicsGetColor() {
|
||||
|
@ -258,11 +282,9 @@ Shader* lovrGraphicsGetShader() {
|
|||
}
|
||||
|
||||
void lovrGraphicsSetShader(Shader* shader) {
|
||||
if (shader != state.pipelines[state.pipeline].shader) {
|
||||
lovrRetain(shader);
|
||||
lovrRelease(state.pipelines[state.pipeline].shader);
|
||||
state.pipelines[state.pipeline].shader = shader;
|
||||
}
|
||||
lovrRetain(shader);
|
||||
lovrRelease(state.pipelines[state.pipeline].shader);
|
||||
state.pipelines[state.pipeline].shader = shader;
|
||||
}
|
||||
|
||||
void lovrGraphicsGetStencilTest(CompareMode* mode, int* value) {
|
||||
|
@ -296,17 +318,12 @@ void lovrGraphicsSetWireframe(bool wireframe) {
|
|||
// Transforms
|
||||
|
||||
void lovrGraphicsPush() {
|
||||
if (++state.transform >= MAX_TRANSFORMS) {
|
||||
lovrThrow("Unbalanced matrix stack (more pushes than pops?)");
|
||||
}
|
||||
|
||||
memcpy(state.transforms[state.transform], state.transforms[state.transform - 1], 2 * 16 * sizeof(float));
|
||||
lovrAssert(++state.transform < MAX_TRANSFORMS, "Unbalanced matrix stack (more pushes than pops?)");
|
||||
mat4_init(state.transforms[state.transform], state.transforms[state.transform - 1]);
|
||||
}
|
||||
|
||||
void lovrGraphicsPop() {
|
||||
if (--state.transform < 0) {
|
||||
lovrThrow("Unbalanced matrix stack (more pops than pushes?)");
|
||||
}
|
||||
lovrAssert(--state.transform >= 0, "Unbalanced matrix stack (more pops than pushes?)");
|
||||
}
|
||||
|
||||
void lovrGraphicsOrigin() {
|
||||
|
@ -332,28 +349,27 @@ void lovrGraphicsMatrixTransform(mat4 transform) {
|
|||
// Drawing
|
||||
|
||||
VertexPointer lovrGraphicsGetVertexPointer(uint32_t count) {
|
||||
lovrMeshResize(state.mesh, count);
|
||||
return lovrMeshMapVertices(state.mesh, 0, count, false, true);
|
||||
lovrMeshResize(state.defaultMesh, count);
|
||||
return lovrMeshMapVertices(state.defaultMesh, 0, count, false, true);
|
||||
}
|
||||
|
||||
void lovrGraphicsClear(Color* color, float* depth, int* stencil) {
|
||||
Layer layer = state.layers[state.layer];
|
||||
gpuClear(layer.canvas, layer.canvasCount, color, depth, stencil);
|
||||
Pipeline* pipeline = &state.pipelines[state.pipeline];
|
||||
if (pipeline->canvasCount > 0) {
|
||||
gpuClear(pipeline->canvas, pipeline->canvasCount, color, depth, stencil);
|
||||
} else {
|
||||
gpuClear(&state.camera.canvas, 1, color, depth, stencil);
|
||||
}
|
||||
}
|
||||
|
||||
void lovrGraphicsDraw(DrawCommand* draw) {
|
||||
if (draw->transform) {
|
||||
lovrGraphicsPush();
|
||||
lovrGraphicsMatrixTransform(draw->transform);
|
||||
}
|
||||
|
||||
void lovrGraphicsDraw(DrawOptions* draw) {
|
||||
Shader* shader = state.pipelines[state.pipeline].shader ? state.pipelines[state.pipeline].shader : state.defaultShaders[draw->shader];
|
||||
if (!shader) shader = state.defaultShaders[draw->shader] = lovrShaderCreateDefault(draw->shader);
|
||||
|
||||
Mesh* mesh = draw->mesh;
|
||||
if (!mesh) {
|
||||
int drawCount = draw->range.count ? draw->range.count : (draw->index.count ? draw->index.count : draw->vertex.count);
|
||||
mesh = state.mesh;
|
||||
mesh = state.defaultMesh;
|
||||
lovrMeshSetDrawMode(mesh, draw->mode);
|
||||
lovrMeshSetDrawRange(mesh, draw->range.start, drawCount);
|
||||
if (draw->vertex.count) {
|
||||
|
@ -370,41 +386,43 @@ void lovrGraphicsDraw(DrawCommand* draw) {
|
|||
|
||||
Material* material = draw->material;
|
||||
if (!material) {
|
||||
material = state.defaultMaterial;
|
||||
|
||||
if (!material) {
|
||||
material = state.defaultMaterial = lovrMaterialCreate(true);
|
||||
if (!state.defaultMaterial) {
|
||||
state.defaultMaterial = lovrMaterialCreate(true);
|
||||
}
|
||||
|
||||
material = state.defaultMaterial;
|
||||
|
||||
for (int i = 0; i < MAX_MATERIAL_TEXTURES; i++) {
|
||||
lovrMaterialSetTexture(material, i, draw->textures[i]);
|
||||
}
|
||||
}
|
||||
|
||||
gpuDraw(&(GpuDrawCommand) {
|
||||
.layer = state.layers[state.layer],
|
||||
DrawCommand command = {
|
||||
.mesh = mesh,
|
||||
.shader = shader,
|
||||
.material = material,
|
||||
.transform = state.transforms[state.transform],
|
||||
.mesh = mesh,
|
||||
.camera = state.camera,
|
||||
.pipeline = state.pipelines[state.pipeline],
|
||||
.instances = draw->instances
|
||||
});
|
||||
};
|
||||
|
||||
mat4_init(command.transform, state.transforms[state.transform]);
|
||||
if (draw->transform) {
|
||||
lovrGraphicsPop();
|
||||
mat4_multiply(command.transform, draw->transform);
|
||||
}
|
||||
|
||||
gpuDraw(&command);
|
||||
}
|
||||
|
||||
void lovrGraphicsPoints(uint32_t count) {
|
||||
lovrGraphicsDraw(&(DrawCommand) {
|
||||
lovrGraphicsDraw(&(DrawOptions) {
|
||||
.mode = MESH_POINTS,
|
||||
.range = { 0, count }
|
||||
});
|
||||
}
|
||||
|
||||
void lovrGraphicsLine(uint32_t count) {
|
||||
lovrGraphicsDraw(&(DrawCommand) {
|
||||
lovrGraphicsDraw(&(DrawOptions) {
|
||||
.mode = MESH_LINE_STRIP,
|
||||
.range = { 0, count }
|
||||
});
|
||||
|
@ -412,7 +430,7 @@ void lovrGraphicsLine(uint32_t count) {
|
|||
|
||||
void lovrGraphicsTriangle(DrawMode mode, Material* material, float points[9]) {
|
||||
if (mode == DRAW_MODE_LINE) {
|
||||
lovrGraphicsDraw(&(DrawCommand) {
|
||||
lovrGraphicsDraw(&(DrawOptions) {
|
||||
.material = material,
|
||||
.mode = MESH_LINE_LOOP,
|
||||
.vertex.count = 3,
|
||||
|
@ -425,7 +443,7 @@ void lovrGraphicsTriangle(DrawMode mode, Material* material, float points[9]) {
|
|||
} else {
|
||||
float normal[3];
|
||||
vec3_cross(vec3_init(normal, &points[0]), &points[3]);
|
||||
lovrGraphicsDraw(&(DrawCommand) {
|
||||
lovrGraphicsDraw(&(DrawOptions) {
|
||||
.material = material,
|
||||
.mode = MESH_TRIANGLES,
|
||||
.vertex.count = 3,
|
||||
|
@ -440,7 +458,7 @@ void lovrGraphicsTriangle(DrawMode mode, Material* material, float points[9]) {
|
|||
|
||||
void lovrGraphicsPlane(DrawMode mode, Material* material, mat4 transform) {
|
||||
if (mode == DRAW_MODE_LINE) {
|
||||
lovrGraphicsDraw(&(DrawCommand) {
|
||||
lovrGraphicsDraw(&(DrawOptions) {
|
||||
.transform = transform,
|
||||
.material = material,
|
||||
.mode = MESH_LINE_LOOP,
|
||||
|
@ -453,7 +471,7 @@ void lovrGraphicsPlane(DrawMode mode, Material* material, mat4 transform) {
|
|||
}
|
||||
});
|
||||
} else if (mode == DRAW_MODE_FILL) {
|
||||
lovrGraphicsDraw(&(DrawCommand) {
|
||||
lovrGraphicsDraw(&(DrawOptions) {
|
||||
.transform = transform,
|
||||
.material = material,
|
||||
.mode = MESH_TRIANGLE_STRIP,
|
||||
|
@ -470,7 +488,7 @@ void lovrGraphicsPlane(DrawMode mode, Material* material, mat4 transform) {
|
|||
|
||||
void lovrGraphicsBox(DrawMode mode, Material* material, mat4 transform) {
|
||||
if (mode == DRAW_MODE_LINE) {
|
||||
lovrGraphicsDraw(&(DrawCommand) {
|
||||
lovrGraphicsDraw(&(DrawOptions) {
|
||||
.transform = transform,
|
||||
.material = material,
|
||||
.mode = MESH_LINES,
|
||||
|
@ -496,7 +514,7 @@ void lovrGraphicsBox(DrawMode mode, Material* material, mat4 transform) {
|
|||
}
|
||||
});
|
||||
} else {
|
||||
lovrGraphicsDraw(&(DrawCommand) {
|
||||
lovrGraphicsDraw(&(DrawOptions) {
|
||||
.transform = transform,
|
||||
.material = material,
|
||||
.mode = MESH_TRIANGLE_STRIP,
|
||||
|
@ -555,7 +573,7 @@ void lovrGraphicsArc(DrawMode mode, ArcMode arcMode, Material* material, mat4 tr
|
|||
bool hasCenterPoint = arcMode == ARC_MODE_PIE && fabsf(theta1 - theta2) < 2 * M_PI;
|
||||
uint32_t count = segments + 1 + hasCenterPoint;
|
||||
VertexPointer vertices = lovrGraphicsGetVertexPointer(count);
|
||||
lovrMeshWriteIndices(state.mesh, 0, 0);
|
||||
lovrMeshWriteIndices(state.defaultMesh, 0, 0);
|
||||
|
||||
if (hasCenterPoint) {
|
||||
memcpy(vertices.floats, (float[]) { 0, 0, 0, 0, 0, 1, .5, .5 }, 8 * sizeof(float));
|
||||
|
@ -573,7 +591,7 @@ void lovrGraphicsArc(DrawMode mode, ArcMode arcMode, Material* material, mat4 tr
|
|||
theta += angleShift;
|
||||
}
|
||||
|
||||
lovrGraphicsDraw(&(DrawCommand) {
|
||||
lovrGraphicsDraw(&(DrawOptions) {
|
||||
.transform = transform,
|
||||
.material = material,
|
||||
.mode = mode == DRAW_MODE_LINE ? (arcMode == ARC_MODE_OPEN ? MESH_LINE_STRIP : MESH_LINE_LOOP) : MESH_TRIANGLE_FAN,
|
||||
|
@ -595,7 +613,7 @@ void lovrGraphicsCylinder(Material* material, float x1, float y1, float z1, floa
|
|||
uint32_t indexCount = 3 * segments * ((capped && r1) + (capped && r2) + 2);
|
||||
|
||||
VertexPointer vertices = lovrGraphicsGetVertexPointer(vertexCount);
|
||||
IndexPointer indices = lovrMeshWriteIndices(state.mesh, indexCount, sizeof(uint32_t));
|
||||
IndexPointer indices = lovrMeshWriteIndices(state.defaultMesh, indexCount, sizeof(uint32_t));
|
||||
float* baseVertex = vertices.floats;
|
||||
|
||||
vec3_init(p, n);
|
||||
|
@ -675,7 +693,7 @@ void lovrGraphicsCylinder(Material* material, float x1, float y1, float z1, floa
|
|||
#undef PUSH_CYLINDER_VERTEX
|
||||
#undef PUSH_CYLINDER_TRIANGLE
|
||||
|
||||
lovrGraphicsDraw(&(DrawCommand) {
|
||||
lovrGraphicsDraw(&(DrawOptions) {
|
||||
.material = material,
|
||||
.mode = MESH_TRIANGLES,
|
||||
.range = { 0, indexCount }
|
||||
|
@ -684,7 +702,7 @@ void lovrGraphicsCylinder(Material* material, float x1, float y1, float z1, floa
|
|||
|
||||
void lovrGraphicsSphere(Material* material, mat4 transform, int segments) {
|
||||
VertexPointer vertices = lovrGraphicsGetVertexPointer((segments + 1) * (segments + 1));
|
||||
IndexPointer indices = lovrMeshWriteIndices(state.mesh, segments * segments * 6, sizeof(uint32_t));
|
||||
IndexPointer indices = lovrMeshWriteIndices(state.defaultMesh, segments * segments * 6, sizeof(uint32_t));
|
||||
|
||||
for (int i = 0; i <= segments; i++) {
|
||||
float v = i / (float) segments;
|
||||
|
@ -710,7 +728,7 @@ void lovrGraphicsSphere(Material* material, mat4 transform, int segments) {
|
|||
}
|
||||
}
|
||||
|
||||
lovrGraphicsDraw(&(DrawCommand) {
|
||||
lovrGraphicsDraw(&(DrawOptions) {
|
||||
.transform = transform,
|
||||
.material = material,
|
||||
.mode = MESH_TRIANGLES,
|
||||
|
@ -723,7 +741,7 @@ void lovrGraphicsSkybox(Texture* texture, float angle, float ax, float ay, float
|
|||
lovrAssert(type == TEXTURE_CUBE || type == TEXTURE_2D, "Only 2D and cube textures can be used as skyboxes");
|
||||
lovrGraphicsPushPipeline();
|
||||
lovrGraphicsSetWinding(WINDING_COUNTERCLOCKWISE);
|
||||
lovrGraphicsDraw(&(DrawCommand) {
|
||||
lovrGraphicsDraw(&(DrawOptions) {
|
||||
.shader = type == TEXTURE_CUBE ? SHADER_CUBE : SHADER_PANO,
|
||||
.textures[TEXTURE_DIFFUSE] = texture,
|
||||
.textures[TEXTURE_ENVIRONMENT_MAP] = texture,
|
||||
|
@ -747,7 +765,7 @@ void lovrGraphicsPrint(const char* str, mat4 transform, float wrap, HorizontalAl
|
|||
uint32_t maxVertices = strlen(str) * 6;
|
||||
VertexPointer vertexPointer = lovrGraphicsGetVertexPointer(maxVertices);
|
||||
lovrFontRender(font, str, wrap, halign, valign, vertexPointer, &offsety, &vertexCount);
|
||||
lovrMeshWriteIndices(state.mesh, 0, 0);
|
||||
lovrMeshWriteIndices(state.defaultMesh, 0, 0);
|
||||
|
||||
lovrGraphicsPush();
|
||||
lovrGraphicsMatrixTransform(transform);
|
||||
|
@ -755,7 +773,7 @@ void lovrGraphicsPrint(const char* str, mat4 transform, float wrap, HorizontalAl
|
|||
lovrGraphicsTranslate(0, offsety, 0);
|
||||
lovrGraphicsPushPipeline();
|
||||
state.pipelines[state.pipeline].depthWrite = false;
|
||||
lovrGraphicsDraw(&(DrawCommand) {
|
||||
lovrGraphicsDraw(&(DrawOptions) {
|
||||
.shader = SHADER_FONT,
|
||||
.textures[TEXTURE_DIFFUSE] = font->texture,
|
||||
.mode = MESH_TRIANGLES,
|
||||
|
@ -768,7 +786,7 @@ void lovrGraphicsPrint(const char* str, mat4 transform, float wrap, HorizontalAl
|
|||
void lovrGraphicsFill(Texture* texture) {
|
||||
lovrGraphicsPushPipeline();
|
||||
lovrGraphicsSetDepthTest(COMPARE_NONE, false);
|
||||
lovrGraphicsDraw(&(DrawCommand) {
|
||||
lovrGraphicsDraw(&(DrawOptions) {
|
||||
.shader = SHADER_FILL,
|
||||
.textures[TEXTURE_DIFFUSE] = texture,
|
||||
.mode = MESH_TRIANGLE_STRIP,
|
||||
|
@ -782,62 +800,3 @@ void lovrGraphicsFill(Texture* texture) {
|
|||
});
|
||||
lovrGraphicsPopPipeline();
|
||||
}
|
||||
|
||||
// Internal
|
||||
void lovrGraphicsPushLayer(Canvas** canvas, int count, bool user) {
|
||||
lovrAssert(count <= MAX_CANVASES, "Attempt to set %d canvases (the maximum is %d)", count, MAX_CANVASES);
|
||||
lovrAssert(++state.layer < MAX_LAYERS, "Layer overflow");
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
lovrRetain(canvas[i]);
|
||||
}
|
||||
|
||||
Layer* prevLayer = &state.layers[state.layer - 1];
|
||||
for (int i = 0; i < prevLayer->canvasCount; i++) {
|
||||
lovrRelease(prevLayer->canvas[i]);
|
||||
}
|
||||
|
||||
Layer* layer = &state.layers[state.layer];
|
||||
memcpy(layer, prevLayer, sizeof(Layer));
|
||||
layer->canvasCount = count;
|
||||
layer->user = user;
|
||||
|
||||
if (count > 0) {
|
||||
memcpy(layer->canvas, canvas, count * sizeof(Canvas*));
|
||||
}
|
||||
}
|
||||
|
||||
void lovrGraphicsPopLayer() {
|
||||
Layer* layer = &state.layers[state.layer];
|
||||
if (layer->canvasCount > 0) {
|
||||
lovrCanvasResolve(layer->canvas[0]);
|
||||
}
|
||||
|
||||
lovrAssert(--state.layer >= 0, "Layer underflow");
|
||||
}
|
||||
|
||||
void lovrGraphicsPushPipeline() {
|
||||
if (++state.pipeline >= MAX_PIPELINES) {
|
||||
lovrThrow("Unbalanced pipeline stack (more pushes than pops?)");
|
||||
}
|
||||
|
||||
memcpy(&state.pipelines[state.pipeline], &state.pipelines[state.pipeline - 1], sizeof(Pipeline));
|
||||
}
|
||||
|
||||
void lovrGraphicsPopPipeline() {
|
||||
if (--state.pipeline < 0) {
|
||||
lovrThrow("Unbalanced pipeline stack (more pops than pushes?)");
|
||||
}
|
||||
}
|
||||
|
||||
void lovrGraphicsSetCamera(mat4 projection, mat4 view) {
|
||||
mat4_set(state.layers[state.layer].projection, projection);
|
||||
mat4_set(state.layers[state.layer].view, view);
|
||||
}
|
||||
|
||||
void lovrGraphicsSetViewport(uint32_t x, uint32_t y, uint32_t width, uint32_t height) {
|
||||
state.layers[state.layer].viewport[0] = x;
|
||||
state.layers[state.layer].viewport[1] = y;
|
||||
state.layers[state.layer].viewport[2] = width;
|
||||
state.layers[state.layer].viewport[3] = height;
|
||||
}
|
||||
|
|
|
@ -6,25 +6,21 @@
|
|||
#include "graphics/texture.h"
|
||||
#include "math/math.h"
|
||||
#include "util.h"
|
||||
#include "lib/glfw.h"
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#pragma once
|
||||
|
||||
#define MAX_TRANSFORMS 60
|
||||
#define INTERNAL_TRANSFORMS 4
|
||||
#define MAX_LAYERS 4
|
||||
#define MAX_PIPELINES 14
|
||||
#define INTERNAL_PIPELINES 2
|
||||
#define DEFAULT_SHADER_COUNT 5
|
||||
#define MAX_TEXTURES 16
|
||||
#define MAX_TRANSFORMS 64
|
||||
#define MAX_PIPELINES 16
|
||||
|
||||
typedef void (*StencilCallback)(void* userdata);
|
||||
|
||||
typedef struct {
|
||||
int shaderSwitches;
|
||||
int drawCalls;
|
||||
} GraphicsStats;
|
||||
typedef enum {
|
||||
ARC_MODE_PIE,
|
||||
ARC_MODE_OPEN,
|
||||
ARC_MODE_CLOSED
|
||||
} ArcMode;
|
||||
|
||||
typedef enum {
|
||||
BLEND_ALPHA,
|
||||
|
@ -42,22 +38,6 @@ typedef enum {
|
|||
BLEND_PREMULTIPLIED
|
||||
} BlendAlphaMode;
|
||||
|
||||
typedef enum {
|
||||
DRAW_MODE_FILL,
|
||||
DRAW_MODE_LINE
|
||||
} DrawMode;
|
||||
|
||||
typedef enum {
|
||||
ARC_MODE_PIE,
|
||||
ARC_MODE_OPEN,
|
||||
ARC_MODE_CLOSED
|
||||
} ArcMode;
|
||||
|
||||
typedef enum {
|
||||
WINDING_CLOCKWISE,
|
||||
WINDING_COUNTERCLOCKWISE
|
||||
} Winding;
|
||||
|
||||
typedef enum {
|
||||
COMPARE_NONE,
|
||||
COMPARE_EQUAL,
|
||||
|
@ -68,6 +48,11 @@ typedef enum {
|
|||
COMPARE_GEQUAL
|
||||
} CompareMode;
|
||||
|
||||
typedef enum {
|
||||
DRAW_MODE_FILL,
|
||||
DRAW_MODE_LINE
|
||||
} DrawMode;
|
||||
|
||||
typedef enum {
|
||||
STENCIL_REPLACE,
|
||||
STENCIL_INCREMENT,
|
||||
|
@ -77,6 +62,11 @@ typedef enum {
|
|||
STENCIL_INVERT
|
||||
} StencilAction;
|
||||
|
||||
typedef enum {
|
||||
WINDING_CLOCKWISE,
|
||||
WINDING_COUNTERCLOCKWISE
|
||||
} Winding;
|
||||
|
||||
typedef struct {
|
||||
bool initialized;
|
||||
float pointSizes[2];
|
||||
|
@ -85,10 +75,24 @@ typedef struct {
|
|||
float textureAnisotropy;
|
||||
} GraphicsLimits;
|
||||
|
||||
typedef struct {
|
||||
int shaderSwitches;
|
||||
int drawCalls;
|
||||
} GraphicsStats;
|
||||
|
||||
typedef struct {
|
||||
Canvas* canvas;
|
||||
uint32_t viewport[4];
|
||||
mat4 viewMatrix;
|
||||
mat4 projection;
|
||||
} Camera;
|
||||
|
||||
typedef struct {
|
||||
Color backgroundColor;
|
||||
BlendMode blendMode;
|
||||
BlendAlphaMode blendAlphaMode;
|
||||
Canvas* canvas[MAX_CANVASES];
|
||||
int canvasCount;
|
||||
Color color;
|
||||
bool culling;
|
||||
CompareMode depthTest;
|
||||
|
@ -104,19 +108,6 @@ typedef struct {
|
|||
} Pipeline;
|
||||
|
||||
typedef struct {
|
||||
float projection[16];
|
||||
float view[16];
|
||||
uint32_t viewport[4];
|
||||
Canvas* canvas[MAX_CANVASES];
|
||||
int canvasCount;
|
||||
bool user;
|
||||
} Layer;
|
||||
|
||||
typedef struct {
|
||||
mat4 transform;
|
||||
DefaultShader shader;
|
||||
Material* material;
|
||||
Texture* textures[MAX_MATERIAL_TEXTURES];
|
||||
Mesh* mesh;
|
||||
MeshDrawMode mode;
|
||||
struct {
|
||||
|
@ -131,23 +122,36 @@ typedef struct {
|
|||
int start;
|
||||
int count;
|
||||
} range;
|
||||
DefaultShader shader;
|
||||
Material* material;
|
||||
Texture* textures[MAX_MATERIAL_TEXTURES];
|
||||
mat4 transform;
|
||||
int instances;
|
||||
} DrawOptions;
|
||||
|
||||
typedef struct {
|
||||
Mesh* mesh;
|
||||
Shader* shader;
|
||||
Material* material;
|
||||
Camera camera;
|
||||
float transform[16];
|
||||
Pipeline pipeline;
|
||||
int instances;
|
||||
} DrawCommand;
|
||||
|
||||
typedef struct {
|
||||
bool initialized;
|
||||
GLFWwindow* window;
|
||||
Shader* defaultShaders[DEFAULT_SHADER_COUNT];
|
||||
bool gammaCorrect;
|
||||
void* window;
|
||||
Camera camera;
|
||||
Shader* defaultShaders[MAX_DEFAULT_SHADERS];
|
||||
Material* defaultMaterial;
|
||||
Font* defaultFont;
|
||||
Mesh* defaultMesh;
|
||||
TextureFilter defaultFilter;
|
||||
bool gammaCorrect;
|
||||
Mesh* mesh;
|
||||
float transforms[MAX_TRANSFORMS + INTERNAL_TRANSFORMS][16];
|
||||
float transforms[MAX_TRANSFORMS][16];
|
||||
int transform;
|
||||
Layer layers[MAX_LAYERS];
|
||||
int layer;
|
||||
Pipeline pipelines[MAX_PIPELINES + INTERNAL_PIPELINES];
|
||||
Pipeline pipelines[MAX_PIPELINES];
|
||||
int pipeline;
|
||||
} GraphicsState;
|
||||
|
||||
|
@ -157,11 +161,14 @@ void lovrGraphicsDestroy();
|
|||
void lovrGraphicsPresent();
|
||||
void lovrGraphicsCreateWindow(int w, int h, bool fullscreen, int msaa, const char* title, const char* icon);
|
||||
void lovrGraphicsGetDimensions(int* width, int* height);
|
||||
void lovrGraphicsSetCamera(Camera* camera, bool clear);
|
||||
GraphicsLimits lovrGraphicsGetLimits();
|
||||
GraphicsStats lovrGraphicsGetStats();
|
||||
|
||||
// State
|
||||
void lovrGraphicsReset();
|
||||
void lovrGraphicsPushPipeline();
|
||||
void lovrGraphicsPopPipeline();
|
||||
Color lovrGraphicsGetBackgroundColor();
|
||||
void lovrGraphicsSetBackgroundColor(Color color);
|
||||
void lovrGraphicsGetBlendMode(BlendMode* mode, BlendAlphaMode* alphaMode);
|
||||
|
@ -205,7 +212,7 @@ void lovrGraphicsMatrixTransform(mat4 transform);
|
|||
// Drawing
|
||||
VertexPointer lovrGraphicsGetVertexPointer(uint32_t capacity);
|
||||
void lovrGraphicsClear(Color* color, float* depth, int* stencil);
|
||||
void lovrGraphicsDraw(DrawCommand* draw);
|
||||
void lovrGraphicsDraw(DrawOptions* draw);
|
||||
void lovrGraphicsPoints(uint32_t count);
|
||||
void lovrGraphicsLine(uint32_t count);
|
||||
void lovrGraphicsTriangle(DrawMode mode, Material* material, float points[9]);
|
||||
|
@ -219,11 +226,3 @@ void lovrGraphicsSkybox(Texture* texture, float angle, float ax, float ay, float
|
|||
void lovrGraphicsPrint(const char* str, mat4 transform, float wrap, HorizontalAlign halign, VerticalAlign valign);
|
||||
void lovrGraphicsStencil(StencilAction action, int replaceValue, StencilCallback callback, void* userdata);
|
||||
void lovrGraphicsFill(Texture* texture);
|
||||
|
||||
// Internal
|
||||
void lovrGraphicsPushLayer(Canvas** canvas, int count, bool user);
|
||||
void lovrGraphicsPopLayer();
|
||||
void lovrGraphicsPushPipeline();
|
||||
void lovrGraphicsPopPipeline();
|
||||
void lovrGraphicsSetCamera(mat4 projection, mat4 view);
|
||||
void lovrGraphicsSetViewport(uint32_t x, uint32_t y, uint32_t width, uint32_t height);
|
||||
|
|
|
@ -40,7 +40,7 @@ static void renderNode(Model* model, int nodeIndex, int instances) {
|
|||
|
||||
lovrMeshSetDrawRange(model->mesh, primitive->drawStart, primitive->drawCount);
|
||||
lovrMeshSetPose(model->mesh, (float*) model->pose);
|
||||
lovrGraphicsDraw(&(DrawCommand) {
|
||||
lovrGraphicsDraw(&(DrawOptions) {
|
||||
.transform = model->nodeTransforms[nodeIndex],
|
||||
.mesh = model->mesh,
|
||||
.material = lovrMeshGetMaterial(model->mesh),
|
||||
|
|
|
@ -17,8 +17,8 @@ typedef struct {
|
|||
Material* material;
|
||||
Animator* animator;
|
||||
Mesh* mesh;
|
||||
float pose[MAX_BONES][16];
|
||||
float (*nodeTransforms)[16];
|
||||
mat4 pose[MAX_BONES];
|
||||
mat4* nodeTransforms;
|
||||
float aabb[6];
|
||||
bool aabbDirty;
|
||||
} Model;
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,8 @@
|
|||
#pragma once
|
||||
|
||||
#if EMSCRIPTEN
|
||||
#include <GLES3/gl3.h>
|
||||
#include <GLES2/gl2ext.h>
|
||||
#else
|
||||
#include "lib/glad/glad.h"
|
||||
#endif
|
|
@ -1,153 +0,0 @@
|
|||
#include "graphics/opengl/opengl.h"
|
||||
#include "graphics/graphics.h"
|
||||
#include "graphics/gpu.h"
|
||||
|
||||
bool lovrCanvasSupportsFormat(TextureFormat format) {
|
||||
switch (format) {
|
||||
case FORMAT_RGB:
|
||||
case FORMAT_RGBA:
|
||||
case FORMAT_RGBA16F:
|
||||
case FORMAT_RGBA32F:
|
||||
case FORMAT_RG11B10F:
|
||||
return true;
|
||||
case FORMAT_DXT1:
|
||||
case FORMAT_DXT3:
|
||||
case FORMAT_DXT5:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Canvas* lovrCanvasCreate(int width, int height, TextureFormat format, CanvasFlags flags) {
|
||||
TextureData* textureData = lovrTextureDataGetEmpty(width, height, format);
|
||||
Texture* texture = lovrTextureCreate(TEXTURE_2D, &textureData, 1, true, flags.mipmaps);
|
||||
if (!texture) return NULL;
|
||||
|
||||
Canvas* canvas = lovrAlloc(sizeof(Canvas), lovrCanvasDestroy);
|
||||
canvas->texture = *texture;
|
||||
canvas->flags = flags;
|
||||
|
||||
// Framebuffer
|
||||
glGenFramebuffers(1, &canvas->framebuffer);
|
||||
gpuBindFramebuffer(canvas->framebuffer);
|
||||
|
||||
// Color attachment
|
||||
if (flags.msaa > 0) {
|
||||
GLenum internalFormat = lovrConvertTextureFormatInternal(format, lovrGraphicsIsGammaCorrect());
|
||||
glGenRenderbuffers(1, &canvas->msaaTexture);
|
||||
glBindRenderbuffer(GL_RENDERBUFFER, canvas->msaaTexture);
|
||||
glRenderbufferStorageMultisample(GL_RENDERBUFFER, flags.msaa, internalFormat, width, height);
|
||||
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, canvas->msaaTexture);
|
||||
} else {
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, canvas->texture.id, 0);
|
||||
}
|
||||
|
||||
// Depth/Stencil
|
||||
if (flags.depth || flags.stencil) {
|
||||
GLenum depthStencilFormat = flags.stencil ? GL_DEPTH24_STENCIL8 : GL_DEPTH_COMPONENT24;
|
||||
glGenRenderbuffers(1, &canvas->depthStencilBuffer);
|
||||
glBindRenderbuffer(GL_RENDERBUFFER, canvas->depthStencilBuffer);
|
||||
if (flags.msaa > 0) {
|
||||
glRenderbufferStorageMultisample(GL_RENDERBUFFER, flags.msaa, depthStencilFormat, width, height);
|
||||
} else {
|
||||
glRenderbufferStorage(GL_RENDERBUFFER, depthStencilFormat, width, height);
|
||||
}
|
||||
|
||||
if (flags.depth) {
|
||||
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, canvas->depthStencilBuffer);
|
||||
}
|
||||
|
||||
if (flags.stencil) {
|
||||
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, canvas->depthStencilBuffer);
|
||||
}
|
||||
}
|
||||
|
||||
// Resolve framebuffer
|
||||
if (flags.msaa > 0) {
|
||||
glGenFramebuffers(1, &canvas->resolveFramebuffer);
|
||||
gpuBindFramebuffer(canvas->resolveFramebuffer);
|
||||
glBindTexture(GL_TEXTURE_2D, canvas->texture.id);
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, canvas->texture.id, 0);
|
||||
gpuBindFramebuffer(canvas->framebuffer);
|
||||
}
|
||||
|
||||
lovrAssert(glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE, "Error creating Canvas");
|
||||
lovrGraphicsClear(&(Color) { 0, 0, 0, 0 }, &(float) { 1. }, &(int) { 0 });
|
||||
gpuBindFramebuffer(0);
|
||||
|
||||
return canvas;
|
||||
}
|
||||
|
||||
void lovrCanvasDestroy(void* ref) {
|
||||
Canvas* canvas = ref;
|
||||
glDeleteFramebuffers(1, &canvas->framebuffer);
|
||||
if (canvas->resolveFramebuffer) {
|
||||
glDeleteFramebuffers(1, &canvas->resolveFramebuffer);
|
||||
}
|
||||
if (canvas->depthStencilBuffer) {
|
||||
glDeleteRenderbuffers(1, &canvas->depthStencilBuffer);
|
||||
}
|
||||
if (canvas->msaaTexture) {
|
||||
glDeleteTextures(1, &canvas->msaaTexture);
|
||||
}
|
||||
lovrTextureDestroy(ref);
|
||||
}
|
||||
|
||||
uint32_t lovrCanvasGetId(Canvas* canvas) {
|
||||
return canvas->framebuffer;
|
||||
}
|
||||
|
||||
void lovrCanvasBind(Canvas** canvases, int canvasCount) {
|
||||
if (canvasCount == 0) {
|
||||
gpuBindFramebuffer(0);
|
||||
return;
|
||||
}
|
||||
|
||||
gpuBindFramebuffer(canvases[0]->texture.id);
|
||||
if (memcmp(canvases, canvases[0]->attachments, MAX_CANVASES * sizeof(Canvas*))) {
|
||||
memcpy(canvases[0]->attachments, canvases, MAX_CANVASES * sizeof(Canvas*));
|
||||
|
||||
GLenum buffers[MAX_CANVASES];
|
||||
for (int i = 0; i < canvasCount; i++) {
|
||||
buffers[i] = GL_COLOR_ATTACHMENT0 + i;
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, buffers[i], GL_TEXTURE_2D, lovrTextureGetId((Texture*) canvases[i]), 0);
|
||||
}
|
||||
glDrawBuffers(canvasCount, buffers);
|
||||
|
||||
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
|
||||
lovrAssert(status != GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS, "All multicanvas canvases must have the same dimensions");
|
||||
lovrAssert(status == GL_FRAMEBUFFER_COMPLETE, "Unable to bind framebuffer");
|
||||
}
|
||||
}
|
||||
|
||||
void lovrCanvasResolve(Canvas* canvas) {
|
||||
if (canvas->flags.msaa > 0) {
|
||||
int width = canvas->texture.width;
|
||||
int height = canvas->texture.height;
|
||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, canvas->framebuffer);
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, canvas->resolveFramebuffer);
|
||||
glBlitFramebuffer(0, 0, width, height, 0, 0, width, height, GL_COLOR_BUFFER_BIT, GL_LINEAR);
|
||||
}
|
||||
|
||||
if (canvas->flags.mipmaps) {
|
||||
gpuBindTexture(&canvas->texture, 0);
|
||||
glGenerateMipmap(canvas->texture.glType);
|
||||
}
|
||||
}
|
||||
|
||||
TextureFormat lovrCanvasGetFormat(Canvas* canvas) {
|
||||
return canvas->texture.slices[0]->format;
|
||||
}
|
||||
|
||||
int lovrCanvasGetMSAA(Canvas* canvas) {
|
||||
return canvas->flags.msaa;
|
||||
}
|
||||
|
||||
TextureData* lovrCanvasNewTextureData(Canvas* canvas) {
|
||||
TextureData* textureData = lovrTextureDataGetBlank(canvas->texture.width, canvas->texture.height, 0, FORMAT_RGBA);
|
||||
if (!textureData) return NULL;
|
||||
|
||||
gpuBindFramebuffer(canvas->framebuffer);
|
||||
glReadPixels(0, 0, canvas->texture.width, canvas->texture.height, GL_RGBA, GL_UNSIGNED_BYTE, textureData->blob.data);
|
||||
|
||||
return textureData;
|
||||
}
|
|
@ -1,479 +0,0 @@
|
|||
#include "graphics/gpu.h"
|
||||
#include "graphics/opengl/opengl.h"
|
||||
#include "resources/shaders.h"
|
||||
#include "data/modelData.h"
|
||||
#include "math/mat4.h"
|
||||
#include <string.h>
|
||||
|
||||
#define MAX_TEXTURES 16
|
||||
|
||||
static struct {
|
||||
Texture* defaultTexture;
|
||||
BlendMode blendMode;
|
||||
BlendAlphaMode blendAlphaMode;
|
||||
bool culling;
|
||||
bool depthEnabled;
|
||||
CompareMode depthTest;
|
||||
bool depthWrite;
|
||||
float lineWidth;
|
||||
bool stencilEnabled;
|
||||
CompareMode stencilMode;
|
||||
int stencilValue;
|
||||
bool stencilWriting;
|
||||
Winding winding;
|
||||
bool wireframe;
|
||||
uint32_t framebuffer;
|
||||
uint32_t indexBuffer;
|
||||
uint32_t program;
|
||||
Texture* textures[MAX_TEXTURES];
|
||||
uint32_t vertexArray;
|
||||
uint32_t vertexBuffer;
|
||||
uint32_t viewport[4];
|
||||
bool srgb;
|
||||
GraphicsLimits limits;
|
||||
GraphicsStats stats;
|
||||
} state;
|
||||
|
||||
static void gammaCorrectColor(Color* color) {
|
||||
if (state.srgb) {
|
||||
color->r = lovrMathGammaToLinear(color->r);
|
||||
color->g = lovrMathGammaToLinear(color->g);
|
||||
color->b = lovrMathGammaToLinear(color->b);
|
||||
}
|
||||
}
|
||||
|
||||
static GLenum convertCompareMode(CompareMode mode) {
|
||||
switch (mode) {
|
||||
case COMPARE_NONE: return GL_ALWAYS;
|
||||
case COMPARE_EQUAL: return GL_EQUAL;
|
||||
case COMPARE_NEQUAL: return GL_NOTEQUAL;
|
||||
case COMPARE_LESS: return GL_LESS;
|
||||
case COMPARE_LEQUAL: return GL_LEQUAL;
|
||||
case COMPARE_GREATER: return GL_GREATER;
|
||||
case COMPARE_GEQUAL: return GL_GEQUAL;
|
||||
}
|
||||
}
|
||||
|
||||
void gpuInit(bool srgb, gpuProc (*getProcAddress)(const char*)) {
|
||||
#ifndef EMSCRIPTEN
|
||||
gladLoadGLLoader((GLADloadproc) getProcAddress);
|
||||
glEnable(GL_LINE_SMOOTH);
|
||||
glEnable(GL_PROGRAM_POINT_SIZE);
|
||||
if (srgb) {
|
||||
glEnable(GL_FRAMEBUFFER_SRGB);
|
||||
} else {
|
||||
glDisable(GL_FRAMEBUFFER_SRGB);
|
||||
}
|
||||
#endif
|
||||
glEnable(GL_BLEND);
|
||||
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
||||
state.srgb = srgb;
|
||||
state.blendMode = -1;
|
||||
state.blendAlphaMode = -1;
|
||||
state.culling = -1;
|
||||
state.depthEnabled = -1;
|
||||
state.depthTest = -1;
|
||||
state.depthWrite = -1;
|
||||
state.lineWidth = -1;
|
||||
state.stencilEnabled = -1;
|
||||
state.stencilMode = -1;
|
||||
state.stencilValue = -1;
|
||||
state.stencilWriting = false;
|
||||
state.winding = -1;
|
||||
state.wireframe = -1;
|
||||
}
|
||||
|
||||
void gpuDestroy() {
|
||||
lovrRelease(state.defaultTexture);
|
||||
for (int i = 0; i < MAX_TEXTURES; i++) {
|
||||
lovrRelease(state.textures[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void gpuClear(Canvas** canvas, int canvasCount, Color* color, float* depth, int* stencil) {
|
||||
gpuBindFramebuffer(canvasCount > 0 ? lovrCanvasGetId(canvas[0]) : 0);
|
||||
|
||||
if (color) {
|
||||
gammaCorrectColor(color);
|
||||
float c[4] = { color->r, color->g, color->b, color->a };
|
||||
glClearBufferfv(GL_COLOR, 0, c);
|
||||
for (int i = 1; i < canvasCount; i++) {
|
||||
glClearBufferfv(GL_COLOR, i, c);
|
||||
}
|
||||
}
|
||||
|
||||
if (depth) {
|
||||
glClearBufferfv(GL_DEPTH, 0, depth);
|
||||
}
|
||||
|
||||
if (stencil) {
|
||||
glClearBufferiv(GL_STENCIL, 0, stencil);
|
||||
}
|
||||
}
|
||||
|
||||
void lovrGraphicsStencil(StencilAction action, int replaceValue, StencilCallback callback, void* userdata) {
|
||||
state.depthWrite = false;
|
||||
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
|
||||
|
||||
if (!state.stencilEnabled) {
|
||||
state.stencilEnabled = true;
|
||||
glEnable(GL_STENCIL_TEST);
|
||||
}
|
||||
|
||||
GLenum glAction;
|
||||
switch (action) {
|
||||
case STENCIL_REPLACE: glAction = GL_REPLACE; break;
|
||||
case STENCIL_INCREMENT: glAction = GL_INCR; break;
|
||||
case STENCIL_DECREMENT: glAction = GL_DECR; break;
|
||||
case STENCIL_INCREMENT_WRAP: glAction = GL_INCR_WRAP; break;
|
||||
case STENCIL_DECREMENT_WRAP: glAction = GL_DECR_WRAP; break;
|
||||
case STENCIL_INVERT: glAction = GL_INVERT; break;
|
||||
}
|
||||
|
||||
glStencilFunc(GL_ALWAYS, replaceValue, 0xff);
|
||||
glStencilOp(GL_KEEP, GL_KEEP, glAction);
|
||||
|
||||
state.stencilWriting = true;
|
||||
callback(userdata);
|
||||
state.stencilWriting = false;
|
||||
|
||||
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
|
||||
state.stencilMode = ~0; // Dirty
|
||||
}
|
||||
|
||||
void gpuDraw(GpuDrawCommand* command) {
|
||||
Mesh* mesh = command->mesh;
|
||||
Material* material = command->material;
|
||||
Shader* shader = command->shader;
|
||||
Pipeline* pipeline = &command->pipeline;
|
||||
int instances = command->instances;
|
||||
|
||||
// Blend mode
|
||||
if (state.blendMode != pipeline->blendMode || state.blendAlphaMode != pipeline->blendAlphaMode) {
|
||||
state.blendMode = pipeline->blendMode;
|
||||
state.blendAlphaMode = pipeline->blendAlphaMode;
|
||||
|
||||
GLenum srcRGB = state.blendMode == BLEND_MULTIPLY ? GL_DST_COLOR : GL_ONE;
|
||||
if (srcRGB == GL_ONE && state.blendAlphaMode == BLEND_ALPHA_MULTIPLY) {
|
||||
srcRGB = GL_SRC_ALPHA;
|
||||
}
|
||||
|
||||
switch (state.blendMode) {
|
||||
case BLEND_ALPHA:
|
||||
glBlendEquation(GL_FUNC_ADD);
|
||||
glBlendFuncSeparate(srcRGB, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
|
||||
break;
|
||||
|
||||
case BLEND_ADD:
|
||||
glBlendEquation(GL_FUNC_ADD);
|
||||
glBlendFuncSeparate(srcRGB, GL_ONE, GL_ZERO, GL_ONE);
|
||||
break;
|
||||
|
||||
case BLEND_SUBTRACT:
|
||||
glBlendEquation(GL_FUNC_REVERSE_SUBTRACT);
|
||||
glBlendFuncSeparate(srcRGB, GL_ONE, GL_ZERO, GL_ONE);
|
||||
break;
|
||||
|
||||
case BLEND_MULTIPLY:
|
||||
glBlendEquation(GL_FUNC_ADD);
|
||||
glBlendFuncSeparate(srcRGB, GL_ZERO, GL_DST_COLOR, GL_ZERO);
|
||||
break;
|
||||
|
||||
case BLEND_LIGHTEN:
|
||||
glBlendEquation(GL_MAX);
|
||||
glBlendFuncSeparate(srcRGB, GL_ZERO, GL_ONE, GL_ZERO);
|
||||
break;
|
||||
|
||||
case BLEND_DARKEN:
|
||||
glBlendEquation(GL_MIN);
|
||||
glBlendFuncSeparate(srcRGB, GL_ZERO, GL_ONE, GL_ZERO);
|
||||
break;
|
||||
|
||||
case BLEND_SCREEN:
|
||||
glBlendEquation(GL_FUNC_ADD);
|
||||
glBlendFuncSeparate(srcRGB, GL_ONE_MINUS_SRC_COLOR, GL_ONE, GL_ONE_MINUS_SRC_COLOR);
|
||||
break;
|
||||
|
||||
case BLEND_REPLACE:
|
||||
glBlendEquation(GL_FUNC_ADD);
|
||||
glBlendFuncSeparate(srcRGB, GL_ZERO, GL_ONE, GL_ZERO);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Culling
|
||||
if (state.culling != pipeline->culling) {
|
||||
state.culling = pipeline->culling;
|
||||
if (state.culling) {
|
||||
glEnable(GL_CULL_FACE);
|
||||
} else {
|
||||
glDisable(GL_CULL_FACE);
|
||||
}
|
||||
}
|
||||
|
||||
// Depth test
|
||||
if (state.depthTest != pipeline->depthTest) {
|
||||
state.depthTest = pipeline->depthTest;
|
||||
if (state.depthTest != COMPARE_NONE) {
|
||||
if (!state.depthEnabled) {
|
||||
state.depthEnabled = true;
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
}
|
||||
glDepthFunc(convertCompareMode(state.depthTest));
|
||||
} else if (state.depthEnabled) {
|
||||
state.depthEnabled = false;
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
}
|
||||
}
|
||||
|
||||
// Depth write
|
||||
if (state.depthWrite != pipeline->depthWrite) {
|
||||
state.depthWrite = pipeline->depthWrite;
|
||||
glDepthMask(state.depthWrite);
|
||||
}
|
||||
|
||||
// Line width
|
||||
if (state.lineWidth != pipeline->lineWidth) {
|
||||
state.lineWidth = state.lineWidth;
|
||||
glLineWidth(state.lineWidth);
|
||||
}
|
||||
|
||||
// Stencil mode
|
||||
if (!state.stencilWriting && (state.stencilMode != pipeline->stencilMode || state.stencilValue != pipeline->stencilValue)) {
|
||||
state.stencilMode = pipeline->stencilMode;
|
||||
state.stencilValue = pipeline->stencilValue;
|
||||
if (state.stencilMode != COMPARE_NONE) {
|
||||
if (!state.stencilEnabled) {
|
||||
state.stencilEnabled = true;
|
||||
glEnable(GL_STENCIL_TEST);
|
||||
}
|
||||
|
||||
GLenum glMode = GL_ALWAYS;
|
||||
switch (state.stencilMode) {
|
||||
case COMPARE_EQUAL: glMode = GL_EQUAL; break;
|
||||
case COMPARE_NEQUAL: glMode = GL_NOTEQUAL; break;
|
||||
case COMPARE_LESS: glMode = GL_GREATER; break;
|
||||
case COMPARE_LEQUAL: glMode = GL_GEQUAL; break;
|
||||
case COMPARE_GREATER: glMode = GL_LESS; break;
|
||||
case COMPARE_GEQUAL: glMode = GL_LEQUAL; break;
|
||||
default: break;
|
||||
}
|
||||
|
||||
glStencilFunc(glMode, state.stencilValue, 0xff);
|
||||
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
|
||||
} else if (state.stencilEnabled) {
|
||||
state.stencilEnabled = false;
|
||||
glDisable(GL_STENCIL_TEST);
|
||||
}
|
||||
}
|
||||
|
||||
// Winding
|
||||
if (state.winding != pipeline->winding) {
|
||||
state.winding = pipeline->winding;
|
||||
glFrontFace(state.winding == WINDING_CLOCKWISE ? GL_CW : GL_CCW);
|
||||
}
|
||||
|
||||
// Wireframe
|
||||
if (state.wireframe != pipeline->wireframe) {
|
||||
state.wireframe = pipeline->wireframe;
|
||||
#ifndef EMSCRIPTEN
|
||||
glPolygonMode(GL_FRONT_AND_BACK, state.wireframe ? GL_LINE : GL_FILL);
|
||||
#endif
|
||||
}
|
||||
|
||||
// Transform
|
||||
lovrShaderSetMatrix(shader, "lovrProjection", command->layer.projection, 16);
|
||||
lovrShaderSetMatrix(shader, "lovrView", command->layer.view, 16);
|
||||
lovrShaderSetMatrix(shader, "lovrModel", command->transform, 16);
|
||||
|
||||
float modelView[16];
|
||||
mat4_multiply(mat4_set(modelView, command->layer.view), command->transform);
|
||||
lovrShaderSetMatrix(shader, "lovrTransform", modelView, 16);
|
||||
|
||||
if (lovrShaderHasUniform(shader, "lovrNormalMatrix")) {
|
||||
if (mat4_invert(modelView)) {
|
||||
mat4_transpose(modelView);
|
||||
} else {
|
||||
mat4_identity(modelView);
|
||||
}
|
||||
|
||||
float normalMatrix[9] = {
|
||||
modelView[0], modelView[1], modelView[2],
|
||||
modelView[4], modelView[5], modelView[6],
|
||||
modelView[8], modelView[9], modelView[10],
|
||||
};
|
||||
|
||||
lovrShaderSetMatrix(shader, "lovrNormalMatrix", normalMatrix, 9);
|
||||
}
|
||||
|
||||
// Pose
|
||||
float* pose = lovrMeshGetPose(mesh);
|
||||
if (pose) {
|
||||
lovrShaderSetMatrix(shader, "lovrPose", pose, MAX_BONES * 16);
|
||||
} else {
|
||||
float identity[16];
|
||||
mat4_identity(identity);
|
||||
lovrShaderSetMatrix(shader, "lovrPose", identity, 16);
|
||||
}
|
||||
|
||||
// Point size
|
||||
lovrShaderSetFloat(shader, "lovrPointSize", &pipeline->pointSize, 1);
|
||||
|
||||
// Color
|
||||
Color color = pipeline->color;
|
||||
gammaCorrectColor(&color);
|
||||
float data[4] = { color.r, color.g, color.b, color.a };
|
||||
lovrShaderSetFloat(shader, "lovrColor", data, 4);
|
||||
|
||||
// Material
|
||||
for (int i = 0; i < MAX_MATERIAL_SCALARS; i++) {
|
||||
float value = lovrMaterialGetScalar(material, i);
|
||||
lovrShaderSetFloat(shader, lovrShaderScalarUniforms[i], &value, 1);
|
||||
}
|
||||
|
||||
for (int i = 0; i < MAX_MATERIAL_COLORS; i++) {
|
||||
Color color = lovrMaterialGetColor(material, i);
|
||||
gammaCorrectColor(&color);
|
||||
float data[4] = { color.r, color.g, color.b, color.a };
|
||||
lovrShaderSetFloat(shader, lovrShaderColorUniforms[i], data, 4);
|
||||
}
|
||||
|
||||
for (int i = 0; i < MAX_MATERIAL_TEXTURES; i++) {
|
||||
Texture* texture = lovrMaterialGetTexture(material, i);
|
||||
lovrShaderSetTexture(shader, lovrShaderTextureUniforms[i], &texture, 1);
|
||||
}
|
||||
|
||||
// Layer
|
||||
lovrCanvasBind(command->layer.canvas, command->layer.canvasCount);
|
||||
gpuSetViewport(command->layer.viewport);
|
||||
|
||||
// Shader
|
||||
gpuUseProgram(lovrShaderGetProgram(shader));
|
||||
lovrShaderBind(shader);
|
||||
|
||||
// Attributes
|
||||
lovrMeshBind(mesh, shader);
|
||||
|
||||
// Draw (TODEW)
|
||||
uint32_t rangeStart, rangeCount;
|
||||
lovrMeshGetDrawRange(mesh, &rangeStart, &rangeCount);
|
||||
uint32_t indexCount;
|
||||
size_t indexSize;
|
||||
lovrMeshReadIndices(mesh, &indexCount, &indexSize);
|
||||
GLenum glDrawMode = lovrConvertMeshDrawMode(lovrMeshGetDrawMode(mesh));
|
||||
if (indexCount > 0) {
|
||||
size_t count = rangeCount ? rangeCount : indexCount;
|
||||
GLenum indexType = indexSize == sizeof(uint16_t) ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT;
|
||||
size_t offset = rangeStart * indexSize;
|
||||
if (instances > 1) {
|
||||
glDrawElementsInstanced(glDrawMode, count, indexType, (GLvoid*) offset, instances);
|
||||
} else {
|
||||
glDrawElements(glDrawMode, count, indexType, (GLvoid*) offset);
|
||||
}
|
||||
} else {
|
||||
size_t count = rangeCount ? rangeCount : lovrMeshGetVertexCount(mesh);
|
||||
if (instances > 1) {
|
||||
glDrawArraysInstanced(glDrawMode, rangeStart, count, instances);
|
||||
} else {
|
||||
glDrawArrays(glDrawMode, rangeStart, count);
|
||||
}
|
||||
}
|
||||
|
||||
state.stats.drawCalls++;
|
||||
}
|
||||
|
||||
void gpuPresent() {
|
||||
memset(&state.stats, 0, sizeof(state.stats));
|
||||
}
|
||||
|
||||
GraphicsLimits lovrGraphicsGetLimits() {
|
||||
if (!state.limits.initialized) {
|
||||
#ifdef EMSCRIPTEN
|
||||
glGetFloatv(GL_ALIASED_POINT_SIZE_RANGE, state.limits.pointSizes);
|
||||
#else
|
||||
glGetFloatv(GL_POINT_SIZE_RANGE, state.limits.pointSizes);
|
||||
#endif
|
||||
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &state.limits.textureSize);
|
||||
glGetIntegerv(GL_MAX_SAMPLES, &state.limits.textureMSAA);
|
||||
glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &state.limits.textureAnisotropy);
|
||||
state.limits.initialized = 1;
|
||||
}
|
||||
|
||||
return state.limits;
|
||||
}
|
||||
|
||||
GraphicsStats lovrGraphicsGetStats() {
|
||||
return state.stats;
|
||||
}
|
||||
|
||||
// Ephemeral
|
||||
|
||||
void gpuBindFramebuffer(uint32_t framebuffer) {
|
||||
if (state.framebuffer != framebuffer) {
|
||||
state.framebuffer = framebuffer;
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
|
||||
}
|
||||
}
|
||||
|
||||
void gpuBindIndexBuffer(uint32_t indexBuffer) {
|
||||
if (state.indexBuffer != indexBuffer) {
|
||||
state.indexBuffer = indexBuffer;
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer);
|
||||
}
|
||||
}
|
||||
|
||||
void gpuBindTexture(Texture* texture, int slot) {
|
||||
lovrAssert(slot >= 0 && slot < MAX_TEXTURES, "Invalid texture slot %d", slot);
|
||||
|
||||
if (!texture) {
|
||||
if (!state.defaultTexture) {
|
||||
TextureData* textureData = lovrTextureDataGetBlank(1, 1, 0xff, FORMAT_RGBA);
|
||||
state.defaultTexture = lovrTextureCreate(TEXTURE_2D, &textureData, 1, true, false);
|
||||
lovrRelease(textureData);
|
||||
}
|
||||
|
||||
texture = state.defaultTexture;
|
||||
}
|
||||
|
||||
if (texture != state.textures[slot]) {
|
||||
lovrRetain(texture);
|
||||
lovrRelease(state.textures[slot]);
|
||||
state.textures[slot] = texture;
|
||||
glActiveTexture(GL_TEXTURE0 + slot);
|
||||
glBindTexture(texture->glType, lovrTextureGetId(texture));
|
||||
}
|
||||
}
|
||||
|
||||
void gpuBindVertexArray(uint32_t vertexArray) {
|
||||
if (state.vertexArray != vertexArray) {
|
||||
state.vertexArray = vertexArray;
|
||||
glBindVertexArray(vertexArray);
|
||||
}
|
||||
}
|
||||
|
||||
void gpuBindVertexBuffer(uint32_t vertexBuffer) {
|
||||
if (state.vertexBuffer != vertexBuffer) {
|
||||
state.vertexBuffer = vertexBuffer;
|
||||
glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
|
||||
}
|
||||
}
|
||||
|
||||
Texture* gpuGetTexture(int slot) {
|
||||
lovrAssert(slot >= 0 && slot < MAX_TEXTURES, "Invalid texture slot %d", slot);
|
||||
return state.textures[slot];
|
||||
}
|
||||
|
||||
void gpuSetViewport(uint32_t viewport[4]) {
|
||||
if (memcmp(state.viewport, viewport, 4 * sizeof(uint32_t))) {
|
||||
memcpy(state.viewport, viewport, 4 * sizeof(uint32_t));
|
||||
glViewport(viewport[0], viewport[1], viewport[2], viewport[3]);
|
||||
}
|
||||
}
|
||||
|
||||
void gpuUseProgram(uint32_t program) {
|
||||
if (state.program != program) {
|
||||
state.program = program;
|
||||
glUseProgram(program);
|
||||
state.stats.shaderSwitches++;
|
||||
}
|
||||
}
|
|
@ -1,305 +0,0 @@
|
|||
#include "graphics/opengl/opengl.h"
|
||||
#include "graphics/gpu.h"
|
||||
#include "math/math.h"
|
||||
#include <limits.h>
|
||||
|
||||
GLenum lovrConvertMeshUsage(MeshUsage usage) {
|
||||
switch (usage) {
|
||||
case MESH_STATIC: return GL_STATIC_DRAW;
|
||||
case MESH_DYNAMIC: return GL_DYNAMIC_DRAW;
|
||||
case MESH_STREAM: return GL_STREAM_DRAW;
|
||||
}
|
||||
}
|
||||
|
||||
GLenum lovrConvertMeshDrawMode(MeshDrawMode mode) {
|
||||
switch (mode) {
|
||||
case MESH_POINTS: return GL_POINTS;
|
||||
case MESH_LINES: return GL_LINES;
|
||||
case MESH_LINE_STRIP: return GL_LINE_STRIP;
|
||||
case MESH_LINE_LOOP: return GL_LINE_LOOP;
|
||||
case MESH_TRIANGLE_STRIP: return GL_TRIANGLE_STRIP;
|
||||
case MESH_TRIANGLES: return GL_TRIANGLES;
|
||||
case MESH_TRIANGLE_FAN: return GL_TRIANGLE_FAN;
|
||||
}
|
||||
}
|
||||
|
||||
Mesh* lovrMeshCreate(uint32_t count, VertexFormat format, MeshDrawMode drawMode, MeshUsage usage) {
|
||||
Mesh* mesh = lovrAlloc(sizeof(Mesh), lovrMeshDestroy);
|
||||
if (!mesh) return NULL;
|
||||
|
||||
mesh->count = count;
|
||||
mesh->format = format;
|
||||
mesh->drawMode = drawMode;
|
||||
mesh->usage = lovrConvertMeshUsage(usage);
|
||||
|
||||
glGenBuffers(1, &mesh->vbo);
|
||||
glGenBuffers(1, &mesh->ibo);
|
||||
gpuBindVertexBuffer(mesh->vbo);
|
||||
glBufferData(GL_ARRAY_BUFFER, count * format.stride, NULL, mesh->usage);
|
||||
glGenVertexArrays(1, &mesh->vao);
|
||||
|
||||
map_init(&mesh->attachments);
|
||||
for (int i = 0; i < format.count; i++) {
|
||||
map_set(&mesh->attachments, format.attributes[i].name, ((MeshAttachment) { mesh, i, 0, true }));
|
||||
}
|
||||
|
||||
mesh->data.raw = calloc(count, format.stride);
|
||||
|
||||
return mesh;
|
||||
}
|
||||
|
||||
void lovrMeshDestroy(void* ref) {
|
||||
Mesh* mesh = ref;
|
||||
lovrRelease(mesh->material);
|
||||
free(mesh->data.raw);
|
||||
free(mesh->indices.raw);
|
||||
glDeleteBuffers(1, &mesh->vbo);
|
||||
glDeleteBuffers(1, &mesh->ibo);
|
||||
glDeleteVertexArrays(1, &mesh->vao);
|
||||
const char* key;
|
||||
map_iter_t iter = map_iter(&mesh->attachments);
|
||||
while ((key = map_next(&mesh->attachments, &iter)) != NULL) {
|
||||
MeshAttachment* attachment = map_get(&mesh->attachments, key);
|
||||
if (attachment->mesh != mesh) {
|
||||
lovrRelease(attachment->mesh);
|
||||
}
|
||||
}
|
||||
map_deinit(&mesh->attachments);
|
||||
free(mesh);
|
||||
}
|
||||
|
||||
void lovrMeshAttachAttribute(Mesh* mesh, Mesh* other, const char* name, int divisor) {
|
||||
MeshAttachment* otherAttachment = map_get(&other->attachments, name);
|
||||
lovrAssert(!mesh->isAttachment, "Attempted to attach to a mesh which is an attachment itself");
|
||||
lovrAssert(otherAttachment, "No attribute named '%s' exists", name);
|
||||
lovrAssert(!map_get(&mesh->attachments, name), "Mesh already has an attribute named '%s'", name);
|
||||
lovrAssert(divisor >= 0, "Divisor can't be negative");
|
||||
|
||||
MeshAttachment attachment = { other, otherAttachment->attributeIndex, divisor, true };
|
||||
map_set(&mesh->attachments, name, attachment);
|
||||
other->isAttachment = true;
|
||||
lovrRetain(other);
|
||||
}
|
||||
|
||||
void lovrMeshDetachAttribute(Mesh* mesh, const char* name) {
|
||||
MeshAttachment* attachment = map_get(&mesh->attachments, name);
|
||||
lovrAssert(attachment, "No attached attribute '%s' was found", name);
|
||||
lovrAssert(attachment->mesh != mesh, "Attribute '%s' was not attached from another Mesh", name);
|
||||
lovrRelease(attachment->mesh);
|
||||
map_remove(&mesh->attachments, name);
|
||||
}
|
||||
|
||||
void lovrMeshBind(Mesh* mesh, Shader* shader) {
|
||||
const char* key;
|
||||
map_iter_t iter = map_iter(&mesh->attachments);
|
||||
|
||||
MeshAttachment layout[MAX_ATTACHMENTS];
|
||||
memset(layout, 0, MAX_ATTACHMENTS * sizeof(MeshAttachment));
|
||||
|
||||
gpuBindVertexArray(mesh->vao);
|
||||
lovrMeshUnmapVertices(mesh);
|
||||
lovrMeshUnmapIndices(mesh);
|
||||
if (mesh->indexCount > 0) {
|
||||
gpuBindIndexBuffer(mesh->ibo);
|
||||
}
|
||||
|
||||
while ((key = map_next(&mesh->attachments, &iter)) != NULL) {
|
||||
int location = lovrShaderGetAttributeId(shader, key);
|
||||
|
||||
if (location >= 0) {
|
||||
MeshAttachment* attachment = map_get(&mesh->attachments, key);
|
||||
layout[location] = *attachment;
|
||||
lovrMeshUnmapVertices(attachment->mesh);
|
||||
lovrMeshUnmapIndices(attachment->mesh);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < MAX_ATTACHMENTS; i++) {
|
||||
MeshAttachment previous = mesh->layout[i];
|
||||
MeshAttachment current = layout[i];
|
||||
|
||||
if (!memcmp(&previous, ¤t, sizeof(MeshAttachment))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (previous.enabled != current.enabled) {
|
||||
if (current.enabled) {
|
||||
glEnableVertexAttribArray(i);
|
||||
} else {
|
||||
glDisableVertexAttribArray(i);
|
||||
mesh->layout[i] = current;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (previous.divisor != current.divisor) {
|
||||
glVertexAttribDivisor(i, current.divisor);
|
||||
}
|
||||
|
||||
if (previous.mesh != current.mesh || previous.attributeIndex != current.attributeIndex) {
|
||||
gpuBindVertexBuffer(current.mesh->vbo);
|
||||
VertexFormat* format = ¤t.mesh->format;
|
||||
Attribute attribute = format->attributes[current.attributeIndex];
|
||||
switch (attribute.type) {
|
||||
case ATTR_FLOAT:
|
||||
glVertexAttribPointer(i, attribute.count, GL_FLOAT, GL_TRUE, format->stride, (void*) attribute.offset);
|
||||
break;
|
||||
|
||||
case ATTR_BYTE:
|
||||
glVertexAttribPointer(i, attribute.count, GL_UNSIGNED_BYTE, GL_TRUE, format->stride, (void*) attribute.offset);
|
||||
break;
|
||||
|
||||
case ATTR_INT:
|
||||
glVertexAttribIPointer(i, attribute.count, GL_UNSIGNED_INT, format->stride, (void*) attribute.offset);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
mesh->layout[i] = current;
|
||||
}
|
||||
}
|
||||
|
||||
VertexFormat* lovrMeshGetVertexFormat(Mesh* mesh) {
|
||||
return &mesh->format;
|
||||
}
|
||||
|
||||
MeshDrawMode lovrMeshGetDrawMode(Mesh* mesh) {
|
||||
return mesh->drawMode;
|
||||
}
|
||||
|
||||
void lovrMeshSetDrawMode(Mesh* mesh, MeshDrawMode drawMode) {
|
||||
mesh->drawMode = drawMode;
|
||||
}
|
||||
|
||||
int lovrMeshGetVertexCount(Mesh* mesh) {
|
||||
return mesh->count;
|
||||
}
|
||||
|
||||
bool lovrMeshIsAttributeEnabled(Mesh* mesh, const char* name) {
|
||||
MeshAttachment* attachment = map_get(&mesh->attachments, name);
|
||||
lovrAssert(attachment, "Mesh does not have an attribute named '%s'", name);
|
||||
return attachment->enabled;
|
||||
}
|
||||
|
||||
void lovrMeshSetAttributeEnabled(Mesh* mesh, const char* name, bool enable) {
|
||||
MeshAttachment* attachment = map_get(&mesh->attachments, name);
|
||||
lovrAssert(attachment, "Mesh does not have an attribute named '%s'", name);
|
||||
attachment->enabled = enable;
|
||||
}
|
||||
|
||||
void lovrMeshGetDrawRange(Mesh* mesh, uint32_t* start, uint32_t* count) {
|
||||
*start = mesh->rangeStart;
|
||||
*count = mesh->rangeCount;
|
||||
}
|
||||
|
||||
void lovrMeshSetDrawRange(Mesh* mesh, uint32_t start, uint32_t count) {
|
||||
uint32_t limit = mesh->indexCount > 0 ? mesh->indexCount : mesh->count;
|
||||
lovrAssert(start + count <= limit, "Invalid mesh draw range [%d, %d]", start + 1, start + count + 1);
|
||||
mesh->rangeStart = start;
|
||||
mesh->rangeCount = count;
|
||||
}
|
||||
|
||||
Material* lovrMeshGetMaterial(Mesh* mesh) {
|
||||
return mesh->material;
|
||||
}
|
||||
|
||||
void lovrMeshSetMaterial(Mesh* mesh, Material* material) {
|
||||
if (mesh->material != material) {
|
||||
lovrRetain(material);
|
||||
lovrRelease(mesh->material);
|
||||
mesh->material = material;
|
||||
}
|
||||
}
|
||||
|
||||
float* lovrMeshGetPose(Mesh* mesh) {
|
||||
return mesh->pose;
|
||||
}
|
||||
|
||||
void lovrMeshSetPose(Mesh* mesh, float* pose) {
|
||||
mesh->pose = pose;
|
||||
}
|
||||
|
||||
VertexPointer lovrMeshMapVertices(Mesh* mesh, uint32_t start, uint32_t count, bool read, bool write) {
|
||||
if (write) {
|
||||
mesh->dirtyStart = MIN(mesh->dirtyStart, start);
|
||||
mesh->dirtyEnd = MAX(mesh->dirtyEnd, start + count);
|
||||
}
|
||||
|
||||
return (VertexPointer) { .bytes = mesh->data.bytes + start * mesh->format.stride };
|
||||
}
|
||||
|
||||
void lovrMeshUnmapVertices(Mesh* mesh) {
|
||||
if (mesh->dirtyEnd == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
size_t stride = mesh->format.stride;
|
||||
gpuBindVertexBuffer(mesh->vbo);
|
||||
if (mesh->usage == MESH_STREAM) {
|
||||
glBufferData(GL_ARRAY_BUFFER, mesh->count * stride, mesh->data.bytes, mesh->usage);
|
||||
} else {
|
||||
size_t offset = mesh->dirtyStart * stride;
|
||||
size_t count = (mesh->dirtyEnd - mesh->dirtyStart) * stride;
|
||||
glBufferSubData(GL_ARRAY_BUFFER, offset, count, mesh->data.bytes + offset);
|
||||
}
|
||||
|
||||
mesh->dirtyStart = INT_MAX;
|
||||
mesh->dirtyEnd = 0;
|
||||
}
|
||||
|
||||
IndexPointer lovrMeshReadIndices(Mesh* mesh, uint32_t* count, size_t* size) {
|
||||
*size = mesh->indexSize;
|
||||
*count = mesh->indexCount;
|
||||
|
||||
if (mesh->indexCount == 0) {
|
||||
return (IndexPointer) { .raw = NULL };
|
||||
} else if (mesh->mappedIndices) {
|
||||
lovrMeshUnmapIndices(mesh);
|
||||
}
|
||||
|
||||
return mesh->indices;
|
||||
}
|
||||
|
||||
IndexPointer lovrMeshWriteIndices(Mesh* mesh, uint32_t count, size_t size) {
|
||||
if (mesh->mappedIndices) {
|
||||
lovrMeshUnmapIndices(mesh);
|
||||
}
|
||||
|
||||
mesh->indexSize = size;
|
||||
mesh->indexCount = count;
|
||||
|
||||
if (count == 0) {
|
||||
return (IndexPointer) { .raw = NULL };
|
||||
}
|
||||
|
||||
gpuBindVertexArray(mesh->vao);
|
||||
gpuBindIndexBuffer(mesh->ibo);
|
||||
mesh->mappedIndices = true;
|
||||
|
||||
if (mesh->indexCapacity < size * count) {
|
||||
mesh->indexCapacity = nextPo2(size * count);
|
||||
mesh->indices.raw = realloc(mesh->indices.raw, mesh->indexCapacity);
|
||||
glBufferData(GL_ELEMENT_ARRAY_BUFFER, mesh->indexCapacity, NULL, mesh->usage);
|
||||
}
|
||||
|
||||
return mesh->indices;
|
||||
}
|
||||
|
||||
void lovrMeshUnmapIndices(Mesh* mesh) {
|
||||
if (!mesh->mappedIndices) {
|
||||
return;
|
||||
}
|
||||
|
||||
mesh->mappedIndices = false;
|
||||
gpuBindIndexBuffer(mesh->ibo);
|
||||
glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, mesh->indexCount * mesh->indexSize, mesh->indices.raw);
|
||||
}
|
||||
|
||||
void lovrMeshResize(Mesh* mesh, uint32_t count) {
|
||||
if (mesh->count < count) {
|
||||
mesh->count = nextPo2(count);
|
||||
gpuBindVertexBuffer(mesh->vbo);
|
||||
mesh->data.raw = realloc(mesh->data.raw, count * mesh->format.stride);
|
||||
glBufferData(GL_ARRAY_BUFFER, count * mesh->format.stride, mesh->data.raw, mesh->usage);
|
||||
}
|
||||
}
|
|
@ -1,122 +0,0 @@
|
|||
#include "graphics/shader.h"
|
||||
#include "graphics/texture.h"
|
||||
#include "graphics/canvas.h"
|
||||
#include "graphics/mesh.h"
|
||||
#include "util.h"
|
||||
#include "lib/map/map.h"
|
||||
#include <stdint.h>
|
||||
|
||||
#pragma once
|
||||
|
||||
#if EMSCRIPTEN
|
||||
#include <GLES3/gl3.h>
|
||||
#include <GLES2/gl2ext.h>
|
||||
#else
|
||||
#include "lib/glad/glad.h"
|
||||
#endif
|
||||
|
||||
#define LOVR_SHADER_POSITION 0
|
||||
#define LOVR_SHADER_NORMAL 1
|
||||
#define LOVR_SHADER_TEX_COORD 2
|
||||
#define LOVR_SHADER_VERTEX_COLOR 3
|
||||
#define LOVR_SHADER_TANGENT 4
|
||||
#define LOVR_SHADER_BONES 5
|
||||
#define LOVR_SHADER_BONE_WEIGHTS 6
|
||||
#define LOVR_MAX_UNIFORM_LENGTH 256
|
||||
#define LOVR_MAX_ATTRIBUTE_LENGTH 256
|
||||
|
||||
typedef struct {
|
||||
GLchar name[LOVR_MAX_UNIFORM_LENGTH];
|
||||
GLenum glType;
|
||||
int index;
|
||||
int location;
|
||||
int count;
|
||||
int components;
|
||||
size_t size;
|
||||
UniformType type;
|
||||
union {
|
||||
void* data;
|
||||
int* ints;
|
||||
float* floats;
|
||||
Texture** textures;
|
||||
} value;
|
||||
int baseTextureSlot;
|
||||
bool dirty;
|
||||
} Uniform;
|
||||
|
||||
typedef map_t(Uniform) map_uniform_t;
|
||||
|
||||
struct Shader {
|
||||
Ref ref;
|
||||
uint32_t program;
|
||||
map_uniform_t uniforms;
|
||||
map_int_t attributes;
|
||||
};
|
||||
|
||||
struct Texture {
|
||||
Ref ref;
|
||||
TextureType type;
|
||||
GLenum glType;
|
||||
TextureData** slices;
|
||||
int width;
|
||||
int height;
|
||||
int depth;
|
||||
GLuint id;
|
||||
TextureFilter filter;
|
||||
TextureWrap wrap;
|
||||
bool srgb;
|
||||
bool mipmaps;
|
||||
bool allocated;
|
||||
};
|
||||
|
||||
struct Canvas {
|
||||
Texture texture;
|
||||
GLuint framebuffer;
|
||||
GLuint resolveFramebuffer;
|
||||
GLuint depthStencilBuffer;
|
||||
GLuint msaaTexture;
|
||||
CanvasFlags flags;
|
||||
Canvas** attachments[MAX_CANVASES];
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
Mesh* mesh;
|
||||
int attributeIndex;
|
||||
int divisor;
|
||||
bool enabled;
|
||||
} MeshAttachment;
|
||||
|
||||
typedef map_t(MeshAttachment) map_attachment_t;
|
||||
|
||||
struct Mesh {
|
||||
Ref ref;
|
||||
uint32_t count;
|
||||
VertexFormat format;
|
||||
MeshDrawMode drawMode;
|
||||
GLenum usage;
|
||||
VertexPointer data;
|
||||
IndexPointer indices;
|
||||
uint32_t indexCount;
|
||||
size_t indexSize;
|
||||
size_t indexCapacity;
|
||||
bool mappedIndices;
|
||||
uint32_t dirtyStart;
|
||||
uint32_t dirtyEnd;
|
||||
uint32_t rangeStart;
|
||||
uint32_t rangeCount;
|
||||
GLuint vao;
|
||||
GLuint vbo;
|
||||
GLuint ibo;
|
||||
Material* material;
|
||||
float* pose;
|
||||
map_attachment_t attachments;
|
||||
MeshAttachment layout[MAX_ATTACHMENTS];
|
||||
bool isAttachment;
|
||||
};
|
||||
|
||||
GLenum lovrConvertWrapMode(WrapMode mode);
|
||||
GLenum lovrConvertTextureFormat(TextureFormat format);
|
||||
GLenum lovrConvertTextureFormatInternal(TextureFormat format, bool srgb);
|
||||
GLenum lovrConvertMeshUsage(MeshUsage usage);
|
||||
GLenum lovrConvertMeshDrawMode(MeshDrawMode mode);
|
||||
bool lovrIsTextureFormatCompressed(TextureFormat format);
|
|
@ -1,391 +0,0 @@
|
|||
#include "graphics/opengl/opengl.h"
|
||||
#include "graphics/graphics.h"
|
||||
#include "graphics/gpu.h"
|
||||
#include "resources/shaders.h"
|
||||
#include "math/math.h"
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
static UniformType getUniformType(GLenum type, const char* debug) {
|
||||
switch (type) {
|
||||
case GL_FLOAT:
|
||||
case GL_FLOAT_VEC2:
|
||||
case GL_FLOAT_VEC3:
|
||||
case GL_FLOAT_VEC4:
|
||||
return UNIFORM_FLOAT;
|
||||
|
||||
case GL_INT:
|
||||
case GL_INT_VEC2:
|
||||
case GL_INT_VEC3:
|
||||
case GL_INT_VEC4:
|
||||
return UNIFORM_INT;
|
||||
|
||||
case GL_FLOAT_MAT2:
|
||||
case GL_FLOAT_MAT3:
|
||||
case GL_FLOAT_MAT4:
|
||||
return UNIFORM_MATRIX;
|
||||
|
||||
case GL_SAMPLER_2D:
|
||||
case GL_SAMPLER_3D:
|
||||
case GL_SAMPLER_CUBE:
|
||||
case GL_SAMPLER_2D_ARRAY:
|
||||
return UNIFORM_SAMPLER;
|
||||
|
||||
default:
|
||||
lovrThrow("Unsupported type for uniform '%s'", debug);
|
||||
return UNIFORM_FLOAT;
|
||||
}
|
||||
}
|
||||
|
||||
static int getUniformComponents(GLenum type) {
|
||||
switch (type) {
|
||||
case GL_FLOAT:
|
||||
case GL_INT:
|
||||
case GL_SAMPLER_2D:
|
||||
case GL_SAMPLER_3D:
|
||||
case GL_SAMPLER_CUBE:
|
||||
case GL_SAMPLER_2D_ARRAY:
|
||||
return 1;
|
||||
|
||||
case GL_FLOAT_VEC2:
|
||||
case GL_INT_VEC2:
|
||||
case GL_FLOAT_MAT2:
|
||||
return 2;
|
||||
|
||||
case GL_FLOAT_VEC3:
|
||||
case GL_INT_VEC3:
|
||||
case GL_FLOAT_MAT3:
|
||||
return 3;
|
||||
|
||||
case GL_FLOAT_VEC4:
|
||||
case GL_INT_VEC4:
|
||||
case GL_FLOAT_MAT4:
|
||||
return 4;
|
||||
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
static GLuint compileShader(GLenum type, const char* source) {
|
||||
GLuint shader = glCreateShader(type);
|
||||
|
||||
glShaderSource(shader, 1, (const GLchar**) &source, NULL);
|
||||
glCompileShader(shader);
|
||||
|
||||
int isShaderCompiled;
|
||||
glGetShaderiv(shader, GL_COMPILE_STATUS, &isShaderCompiled);
|
||||
if (!isShaderCompiled) {
|
||||
int logLength;
|
||||
glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &logLength);
|
||||
|
||||
char* log = malloc(logLength);
|
||||
glGetShaderInfoLog(shader, logLength, &logLength, log);
|
||||
lovrThrow("Could not compile shader %s", log);
|
||||
}
|
||||
|
||||
return shader;
|
||||
}
|
||||
|
||||
static GLuint linkShaders(GLuint vertexShader, GLuint fragmentShader) {
|
||||
GLuint program = glCreateProgram();
|
||||
|
||||
if (vertexShader) {
|
||||
glAttachShader(program, vertexShader);
|
||||
}
|
||||
|
||||
if (fragmentShader) {
|
||||
glAttachShader(program, fragmentShader);
|
||||
}
|
||||
|
||||
glBindAttribLocation(program, LOVR_SHADER_POSITION, "lovrPosition");
|
||||
glBindAttribLocation(program, LOVR_SHADER_NORMAL, "lovrNormal");
|
||||
glBindAttribLocation(program, LOVR_SHADER_TEX_COORD, "lovrTexCoord");
|
||||
glBindAttribLocation(program, LOVR_SHADER_VERTEX_COLOR, "lovrVertexColor");
|
||||
glBindAttribLocation(program, LOVR_SHADER_TANGENT, "lovrTangent");
|
||||
glBindAttribLocation(program, LOVR_SHADER_BONES, "lovrBones");
|
||||
glBindAttribLocation(program, LOVR_SHADER_BONE_WEIGHTS, "lovrBoneWeights");
|
||||
glLinkProgram(program);
|
||||
|
||||
int isLinked;
|
||||
glGetProgramiv(program, GL_LINK_STATUS, &isLinked);
|
||||
if (!isLinked) {
|
||||
int logLength;
|
||||
glGetProgramiv(program, GL_INFO_LOG_LENGTH, &logLength);
|
||||
|
||||
char* log = malloc(logLength);
|
||||
glGetProgramInfoLog(program, logLength, &logLength, log);
|
||||
lovrThrow("Could not link shader %s", log);
|
||||
}
|
||||
|
||||
glDetachShader(program, vertexShader);
|
||||
glDeleteShader(vertexShader);
|
||||
glDetachShader(program, fragmentShader);
|
||||
glDeleteShader(fragmentShader);
|
||||
|
||||
return program;
|
||||
}
|
||||
|
||||
Shader* lovrShaderCreate(const char* vertexSource, const char* fragmentSource) {
|
||||
Shader* shader = lovrAlloc(sizeof(Shader), lovrShaderDestroy);
|
||||
if (!shader) return NULL;
|
||||
|
||||
char source[8192];
|
||||
|
||||
// Vertex
|
||||
vertexSource = vertexSource == NULL ? lovrDefaultVertexShader : vertexSource;
|
||||
snprintf(source, sizeof(source), "%s%s\n%s", lovrShaderVertexPrefix, vertexSource, lovrShaderVertexSuffix);
|
||||
GLuint vertexShader = compileShader(GL_VERTEX_SHADER, source);
|
||||
|
||||
// Fragment
|
||||
fragmentSource = fragmentSource == NULL ? lovrDefaultFragmentShader : fragmentSource;
|
||||
snprintf(source, sizeof(source), "%s%s\n%s", lovrShaderFragmentPrefix, fragmentSource, lovrShaderFragmentSuffix);
|
||||
GLuint fragmentShader = compileShader(GL_FRAGMENT_SHADER, source);
|
||||
|
||||
// Link
|
||||
uint32_t program = linkShaders(vertexShader, fragmentShader);
|
||||
shader->program = program;
|
||||
|
||||
gpuUseProgram(program);
|
||||
glVertexAttrib4fv(LOVR_SHADER_VERTEX_COLOR, (float[4]) { 1., 1., 1., 1. });
|
||||
glVertexAttribI4iv(LOVR_SHADER_BONES, (int[4]) { 0., 0., 0., 0. });
|
||||
glVertexAttrib4fv(LOVR_SHADER_BONE_WEIGHTS, (float[4]) { 1., 0., 0., 0. });
|
||||
|
||||
// Uniform introspection
|
||||
int32_t uniformCount;
|
||||
int textureSlot = 0;
|
||||
map_init(&shader->uniforms);
|
||||
glGetProgramiv(program, GL_ACTIVE_UNIFORMS, &uniformCount);
|
||||
for (int i = 0; i < uniformCount; i++) {
|
||||
Uniform uniform;
|
||||
glGetActiveUniform(program, i, LOVR_MAX_UNIFORM_LENGTH, NULL, &uniform.count, &uniform.glType, uniform.name);
|
||||
|
||||
char* subscript = strchr(uniform.name, '[');
|
||||
if (subscript) {
|
||||
*subscript = '\0';
|
||||
}
|
||||
|
||||
uniform.index = i;
|
||||
uniform.location = glGetUniformLocation(program, uniform.name);
|
||||
uniform.type = getUniformType(uniform.glType, uniform.name);
|
||||
uniform.components = getUniformComponents(uniform.glType);
|
||||
uniform.baseTextureSlot = (uniform.type == UNIFORM_SAMPLER) ? textureSlot : -1;
|
||||
|
||||
if (uniform.location == -1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (uniform.type) {
|
||||
case UNIFORM_FLOAT:
|
||||
uniform.size = uniform.components * uniform.count * sizeof(float);
|
||||
uniform.value.data = calloc(1, uniform.size);
|
||||
break;
|
||||
|
||||
case UNIFORM_INT:
|
||||
uniform.size = uniform.components * uniform.count * sizeof(int);
|
||||
uniform.value.data = calloc(1, uniform.size);
|
||||
break;
|
||||
|
||||
case UNIFORM_MATRIX:
|
||||
uniform.size = uniform.components * uniform.components * uniform.count * sizeof(int);
|
||||
uniform.value.data = calloc(1, uniform.size);
|
||||
break;
|
||||
|
||||
case UNIFORM_SAMPLER:
|
||||
uniform.size = uniform.components * uniform.count * MAX(sizeof(Texture*), sizeof(int));
|
||||
uniform.value.data = calloc(1, uniform.size);
|
||||
|
||||
// Use the value for ints to bind texture slots, but use the value for textures afterwards.
|
||||
for (int i = 0; i < uniform.count; i++) {
|
||||
uniform.value.ints[i] = uniform.baseTextureSlot + i;
|
||||
}
|
||||
glUniform1iv(uniform.location, uniform.count, uniform.value.ints);
|
||||
break;
|
||||
}
|
||||
|
||||
size_t offset = 0;
|
||||
for (int j = 0; j < uniform.count; j++) {
|
||||
int location = uniform.location;
|
||||
|
||||
if (uniform.count > 1) {
|
||||
char name[LOVR_MAX_UNIFORM_LENGTH];
|
||||
snprintf(name, LOVR_MAX_UNIFORM_LENGTH, "%s[%d]", uniform.name, j);
|
||||
location = glGetUniformLocation(program, name);
|
||||
}
|
||||
|
||||
switch (uniform.type) {
|
||||
case UNIFORM_FLOAT:
|
||||
glGetUniformfv(program, location, &uniform.value.floats[offset]);
|
||||
offset += uniform.components;
|
||||
break;
|
||||
|
||||
case UNIFORM_INT:
|
||||
glGetUniformiv(program, location, &uniform.value.ints[offset]);
|
||||
offset += uniform.components;
|
||||
break;
|
||||
|
||||
case UNIFORM_MATRIX:
|
||||
glGetUniformfv(program, location, &uniform.value.floats[offset]);
|
||||
offset += uniform.components * uniform.components;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
map_set(&shader->uniforms, uniform.name, uniform);
|
||||
textureSlot += (uniform.type == UNIFORM_SAMPLER) ? uniform.count : 0;
|
||||
}
|
||||
|
||||
// Attribute cache
|
||||
int32_t attributeCount;
|
||||
glGetProgramiv(program, GL_ACTIVE_ATTRIBUTES, &attributeCount);
|
||||
map_init(&shader->attributes);
|
||||
for (int i = 0; i < attributeCount; i++) {
|
||||
char name[LOVR_MAX_ATTRIBUTE_LENGTH];
|
||||
GLint size;
|
||||
GLenum type;
|
||||
glGetActiveAttrib(program, i, LOVR_MAX_ATTRIBUTE_LENGTH, NULL, &size, &type, name);
|
||||
map_set(&shader->attributes, name, glGetAttribLocation(program, name));
|
||||
}
|
||||
|
||||
return shader;
|
||||
}
|
||||
|
||||
Shader* lovrShaderCreateDefault(DefaultShader type) {
|
||||
switch (type) {
|
||||
case SHADER_DEFAULT: return lovrShaderCreate(NULL, NULL);
|
||||
case SHADER_CUBE: return lovrShaderCreate(lovrCubeVertexShader, lovrCubeFragmentShader); break;
|
||||
case SHADER_PANO: return lovrShaderCreate(lovrCubeVertexShader, lovrPanoFragmentShader); break;
|
||||
case SHADER_FONT: return lovrShaderCreate(NULL, lovrFontFragmentShader);
|
||||
case SHADER_FILL: return lovrShaderCreate(lovrFillVertexShader, NULL);
|
||||
default: lovrThrow("Unknown default shader type");
|
||||
}
|
||||
}
|
||||
|
||||
void lovrShaderDestroy(void* ref) {
|
||||
Shader* shader = ref;
|
||||
glDeleteProgram(shader->program);
|
||||
map_deinit(&shader->uniforms);
|
||||
map_deinit(&shader->attributes);
|
||||
free(shader);
|
||||
}
|
||||
|
||||
uint32_t lovrShaderGetProgram(Shader* shader) {
|
||||
return shader->program;
|
||||
}
|
||||
|
||||
void lovrShaderBind(Shader* shader) {
|
||||
map_iter_t iter = map_iter(&shader->uniforms);
|
||||
const char* key;
|
||||
while ((key = map_next(&shader->uniforms, &iter)) != NULL) {
|
||||
Uniform* uniform = map_get(&shader->uniforms, key);
|
||||
|
||||
if (uniform->type != UNIFORM_SAMPLER && !uniform->dirty) {
|
||||
continue;
|
||||
}
|
||||
|
||||
uniform->dirty = false;
|
||||
int count = uniform->count;
|
||||
void* data = uniform->value.data;
|
||||
|
||||
switch (uniform->type) {
|
||||
case UNIFORM_FLOAT:
|
||||
switch (uniform->components) {
|
||||
case 1: glUniform1fv(uniform->location, count, data); break;
|
||||
case 2: glUniform2fv(uniform->location, count, data); break;
|
||||
case 3: glUniform3fv(uniform->location, count, data); break;
|
||||
case 4: glUniform4fv(uniform->location, count, data); break;
|
||||
}
|
||||
break;
|
||||
|
||||
case UNIFORM_INT:
|
||||
switch (uniform->components) {
|
||||
case 1: glUniform1iv(uniform->location, count, data); break;
|
||||
case 2: glUniform2iv(uniform->location, count, data); break;
|
||||
case 3: glUniform3iv(uniform->location, count, data); break;
|
||||
case 4: glUniform4iv(uniform->location, count, data); break;
|
||||
}
|
||||
break;
|
||||
|
||||
case UNIFORM_MATRIX:
|
||||
switch (uniform->components) {
|
||||
case 2: glUniformMatrix2fv(uniform->location, count, GL_FALSE, data); break;
|
||||
case 3: glUniformMatrix3fv(uniform->location, count, GL_FALSE, data); break;
|
||||
case 4: glUniformMatrix4fv(uniform->location, count, GL_FALSE, data); break;
|
||||
}
|
||||
break;
|
||||
|
||||
case UNIFORM_SAMPLER:
|
||||
for (int i = 0; i < count; i++) {
|
||||
GLenum uniformTextureType;
|
||||
switch (uniform->glType) {
|
||||
case GL_SAMPLER_2D: uniformTextureType = GL_TEXTURE_2D; break;
|
||||
case GL_SAMPLER_3D: uniformTextureType = GL_TEXTURE_3D; break;
|
||||
case GL_SAMPLER_CUBE: uniformTextureType = GL_TEXTURE_CUBE_MAP; break;
|
||||
case GL_SAMPLER_2D_ARRAY: uniformTextureType = GL_TEXTURE_2D_ARRAY; break;
|
||||
}
|
||||
gpuBindTexture(uniform->value.textures[i], uniform->baseTextureSlot + i);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int lovrShaderGetAttributeId(Shader* shader, const char* name) {
|
||||
int* id = map_get(&shader->attributes, name);
|
||||
return id ? *id : -1;
|
||||
}
|
||||
|
||||
bool lovrShaderHasUniform(Shader* shader, const char* name) {
|
||||
return map_get(&shader->uniforms, name) != NULL;
|
||||
}
|
||||
|
||||
bool lovrShaderGetUniform(Shader* shader, const char* name, int* count, int* components, size_t* size, UniformType* type) {
|
||||
Uniform* uniform = map_get(&shader->uniforms, name);
|
||||
if (!uniform) {
|
||||
return false;
|
||||
}
|
||||
|
||||
*count = uniform->count;
|
||||
*components = uniform->components;
|
||||
*size = uniform->size;
|
||||
*type = uniform->type;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void lovrShaderSetUniform(Shader* shader, const char* name, UniformType type, void* data, int count, size_t size, const char* debug) {
|
||||
Uniform* uniform = map_get(&shader->uniforms, name);
|
||||
if (!uniform) {
|
||||
return;
|
||||
}
|
||||
|
||||
const char* plural = (uniform->size / size) > 1 ? "s" : "";
|
||||
lovrAssert(uniform->type == type, "Unable to send %ss to uniform %s", debug, uniform->name);
|
||||
lovrAssert(count * size <= uniform->size, "Expected at most %d %s%s for uniform %s, got %d", uniform->size / size, debug, plural, uniform->name, count);
|
||||
|
||||
if (!uniform->dirty && !memcmp(uniform->value.data, data, count * size)) {
|
||||
return;
|
||||
}
|
||||
|
||||
memcpy(uniform->value.data, data, count * size);
|
||||
uniform->dirty = true;
|
||||
}
|
||||
|
||||
void lovrShaderSetFloat(Shader* shader, const char* name, float* data, int count) {
|
||||
lovrShaderSetUniform(shader, name, UNIFORM_FLOAT, data, count, sizeof(float), "float");
|
||||
}
|
||||
|
||||
void lovrShaderSetInt(Shader* shader, const char* name, int* data, int count) {
|
||||
lovrShaderSetUniform(shader, name, UNIFORM_INT, data, count, sizeof(int), "int");
|
||||
}
|
||||
|
||||
void lovrShaderSetMatrix(Shader* shader, const char* name, float* data, int count) {
|
||||
lovrShaderSetUniform(shader, name, UNIFORM_MATRIX, data, count, sizeof(float), "float");
|
||||
}
|
||||
|
||||
void lovrShaderSetTexture(Shader* shader, const char* name, Texture** data, int count) {
|
||||
lovrShaderSetUniform(shader, name, UNIFORM_SAMPLER, data, count, sizeof(Texture*), "texture");
|
||||
}
|
|
@ -1,271 +0,0 @@
|
|||
#include "graphics/opengl/opengl.h"
|
||||
#include "graphics/graphics.h"
|
||||
#include "graphics/gpu.h"
|
||||
#include "lib/vec/vec.h"
|
||||
#include "math/math.h"
|
||||
#include <math.h>
|
||||
|
||||
GLenum lovrConvertWrapMode(WrapMode mode) {
|
||||
switch (mode) {
|
||||
case WRAP_CLAMP: return GL_CLAMP_TO_EDGE;
|
||||
case WRAP_REPEAT: return GL_REPEAT;
|
||||
case WRAP_MIRRORED_REPEAT: return GL_MIRRORED_REPEAT;
|
||||
}
|
||||
}
|
||||
|
||||
GLenum lovrConvertTextureFormat(TextureFormat format) {
|
||||
switch (format) {
|
||||
case FORMAT_RGB: return GL_RGB;
|
||||
case FORMAT_RGBA: return GL_RGBA;
|
||||
case FORMAT_RGBA16F: return GL_RGBA;
|
||||
case FORMAT_RGBA32F: return GL_RGBA;
|
||||
case FORMAT_RG11B10F: return GL_RGB;
|
||||
case FORMAT_DXT1: return GL_COMPRESSED_RGB_S3TC_DXT1_EXT;
|
||||
case FORMAT_DXT3: return GL_COMPRESSED_RGBA_S3TC_DXT3_EXT;
|
||||
case FORMAT_DXT5: return GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
|
||||
}
|
||||
}
|
||||
|
||||
GLenum lovrConvertTextureFormatInternal(TextureFormat format, bool srgb) {
|
||||
switch (format) {
|
||||
case FORMAT_RGB: return srgb ? GL_SRGB8 : GL_RGB8;
|
||||
case FORMAT_RGBA: return srgb ? GL_SRGB8_ALPHA8 : GL_RGBA8;
|
||||
case FORMAT_RGBA16F: return GL_RGBA16F;
|
||||
case FORMAT_RGBA32F: return GL_RGBA32F;
|
||||
case FORMAT_RG11B10F: return GL_R11F_G11F_B10F;
|
||||
case FORMAT_DXT1: return srgb ? GL_COMPRESSED_SRGB_S3TC_DXT1_EXT : GL_COMPRESSED_RGB_S3TC_DXT1_EXT;
|
||||
case FORMAT_DXT3: return srgb ? GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT : GL_COMPRESSED_RGBA_S3TC_DXT3_EXT;
|
||||
case FORMAT_DXT5: return srgb ? GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT : GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
|
||||
}
|
||||
}
|
||||
|
||||
bool lovrIsTextureFormatCompressed(TextureFormat format) {
|
||||
switch (format) {
|
||||
case FORMAT_DXT1:
|
||||
case FORMAT_DXT3:
|
||||
case FORMAT_DXT5:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static void lovrTextureAllocate(Texture* texture, TextureData* textureData) {
|
||||
texture->allocated = true;
|
||||
texture->width = textureData->width;
|
||||
texture->height = textureData->height;
|
||||
|
||||
if (lovrIsTextureFormatCompressed(textureData->format)) {
|
||||
return;
|
||||
}
|
||||
|
||||
int w = textureData->width;
|
||||
int h = textureData->height;
|
||||
int mipmapCount = log2(MAX(w, h)) + 1;
|
||||
bool srgb = lovrGraphicsIsGammaCorrect() && texture->srgb;
|
||||
GLenum glFormat = lovrConvertTextureFormat(textureData->format);
|
||||
GLenum internalFormat = lovrConvertTextureFormatInternal(textureData->format, srgb);
|
||||
#ifndef EMSCRIPTEN
|
||||
if (GLAD_GL_ARB_texture_storage) {
|
||||
#endif
|
||||
if (texture->type == TEXTURE_ARRAY) {
|
||||
glTexStorage3D(texture->glType, mipmapCount, internalFormat, w, h, texture->depth);
|
||||
} else {
|
||||
glTexStorage2D(texture->glType, mipmapCount, internalFormat, w, h);
|
||||
}
|
||||
#ifndef EMSCRIPTEN
|
||||
} else {
|
||||
for (int i = 0; i < mipmapCount; i++) {
|
||||
switch (texture->type) {
|
||||
case TEXTURE_2D:
|
||||
glTexImage2D(texture->glType, i, internalFormat, w, h, 0, glFormat, GL_UNSIGNED_BYTE, NULL);
|
||||
break;
|
||||
|
||||
case TEXTURE_CUBE:
|
||||
for (int face = 0; face < 6; face++) {
|
||||
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, i, internalFormat, w, h, 0, glFormat, GL_UNSIGNED_BYTE, NULL);
|
||||
}
|
||||
break;
|
||||
|
||||
case TEXTURE_ARRAY:
|
||||
case TEXTURE_VOLUME:
|
||||
glTexImage3D(texture->glType, i, internalFormat, w, h, texture->depth, 0, glFormat, GL_UNSIGNED_BYTE, NULL);
|
||||
break;
|
||||
}
|
||||
w = MAX(w >> 1, 1);
|
||||
h = MAX(h >> 1, 1);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
Texture* lovrTextureCreate(TextureType type, TextureData** slices, int depth, bool srgb, bool mipmaps) {
|
||||
Texture* texture = lovrAlloc(sizeof(Texture), lovrTextureDestroy);
|
||||
if (!texture) return NULL;
|
||||
|
||||
texture->type = type;
|
||||
switch (type) {
|
||||
case TEXTURE_2D: texture->glType = GL_TEXTURE_2D; break;
|
||||
case TEXTURE_ARRAY: texture->glType = GL_TEXTURE_2D_ARRAY; break;
|
||||
case TEXTURE_CUBE: texture->glType = GL_TEXTURE_CUBE_MAP; break;
|
||||
case TEXTURE_VOLUME: texture->glType = GL_TEXTURE_3D; break;
|
||||
}
|
||||
|
||||
texture->slices = calloc(depth, sizeof(TextureData**));
|
||||
texture->depth = depth;
|
||||
texture->srgb = srgb;
|
||||
texture->mipmaps = mipmaps;
|
||||
|
||||
WrapMode wrap = type == TEXTURE_CUBE ? WRAP_CLAMP : WRAP_REPEAT;
|
||||
glGenTextures(1, &texture->id);
|
||||
gpuBindTexture(texture, 0);
|
||||
lovrTextureSetFilter(texture, lovrGraphicsGetDefaultFilter());
|
||||
lovrTextureSetWrap(texture, (TextureWrap) { .s = wrap, .t = wrap, .r = wrap });
|
||||
|
||||
lovrAssert(type != TEXTURE_CUBE || depth == 6, "6 images are required for a cube texture\n");
|
||||
lovrAssert(type != TEXTURE_2D || depth == 1, "2D textures can only contain a single image");
|
||||
|
||||
if (slices) {
|
||||
for (int i = 0; i < depth; i++) {
|
||||
lovrTextureReplacePixels(texture, slices[i], i);
|
||||
}
|
||||
}
|
||||
|
||||
return texture;
|
||||
}
|
||||
|
||||
void lovrTextureDestroy(void* ref) {
|
||||
Texture* texture = ref;
|
||||
for (int i = 0; i < texture->depth; i++) {
|
||||
lovrRelease(texture->slices[i]);
|
||||
}
|
||||
glDeleteTextures(1, &texture->id);
|
||||
free(texture->slices);
|
||||
free(texture);
|
||||
}
|
||||
|
||||
GLuint lovrTextureGetId(Texture* texture) {
|
||||
return texture->id;
|
||||
}
|
||||
|
||||
int lovrTextureGetWidth(Texture* texture) {
|
||||
return texture->width;
|
||||
}
|
||||
|
||||
int lovrTextureGetHeight(Texture* texture) {
|
||||
return texture->height;
|
||||
}
|
||||
|
||||
int lovrTextureGetDepth(Texture* texture) {
|
||||
return texture->depth;
|
||||
}
|
||||
|
||||
TextureType lovrTextureGetType(Texture* texture) {
|
||||
return texture->type;
|
||||
}
|
||||
|
||||
void lovrTextureReplacePixels(Texture* texture, TextureData* textureData, int slice) {
|
||||
lovrRetain(textureData);
|
||||
lovrRelease(texture->slices[slice]);
|
||||
texture->slices[slice] = textureData;
|
||||
|
||||
if (!texture->allocated) {
|
||||
lovrAssert(texture->type != TEXTURE_CUBE || textureData->width == textureData->height, "Cubemap images must be square");
|
||||
lovrTextureAllocate(texture, textureData);
|
||||
} else {
|
||||
lovrAssert(textureData->width == texture->width && textureData->height == texture->height, "All texture slices must have the same dimensions");
|
||||
}
|
||||
|
||||
if (!textureData->blob.data) {
|
||||
return;
|
||||
}
|
||||
|
||||
GLenum glFormat = lovrConvertTextureFormat(textureData->format);
|
||||
GLenum glInternalFormat = lovrConvertTextureFormatInternal(textureData->format, texture->srgb);
|
||||
GLenum binding = (texture->type == TEXTURE_CUBE) ? GL_TEXTURE_CUBE_MAP_POSITIVE_X + slice : texture->glType;
|
||||
|
||||
if (lovrIsTextureFormatCompressed(textureData->format)) {
|
||||
Mipmap m; int i;
|
||||
vec_foreach(&textureData->mipmaps, m, i) {
|
||||
switch (texture->type) {
|
||||
case TEXTURE_2D:
|
||||
case TEXTURE_CUBE:
|
||||
glCompressedTexImage2D(binding, i, glInternalFormat, m.width, m.height, 0, m.size, m.data);
|
||||
break;
|
||||
case TEXTURE_ARRAY:
|
||||
case TEXTURE_VOLUME:
|
||||
glCompressedTexSubImage3D(binding, i, 0, 0, slice, m.width, m.height, 1, glInternalFormat, m.size, m.data);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
switch (texture->type) {
|
||||
case TEXTURE_2D:
|
||||
case TEXTURE_CUBE:
|
||||
glTexSubImage2D(binding, 0, 0, 0, textureData->width, textureData->height, glFormat, GL_UNSIGNED_BYTE, textureData->blob.data);
|
||||
break;
|
||||
case TEXTURE_ARRAY:
|
||||
case TEXTURE_VOLUME:
|
||||
glTexSubImage3D(binding, 0, 0, 0, slice, textureData->width, textureData->height, 1, glFormat, GL_UNSIGNED_BYTE, textureData->blob.data);
|
||||
break;
|
||||
}
|
||||
|
||||
if (texture->mipmaps) {
|
||||
glGenerateMipmap(texture->glType);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TextureFilter lovrTextureGetFilter(Texture* texture) {
|
||||
return texture->filter;
|
||||
}
|
||||
|
||||
void lovrTextureSetFilter(Texture* texture, TextureFilter filter) {
|
||||
float anisotropy = filter.mode == FILTER_ANISOTROPIC ? MAX(filter.anisotropy, 1.) : 1.;
|
||||
gpuBindTexture(texture, 0);
|
||||
texture->filter = filter;
|
||||
|
||||
switch (filter.mode) {
|
||||
case FILTER_NEAREST:
|
||||
glTexParameteri(texture->glType, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
glTexParameteri(texture->glType, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
break;
|
||||
|
||||
case FILTER_BILINEAR:
|
||||
if (texture->mipmaps) {
|
||||
glTexParameteri(texture->glType, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
|
||||
glTexParameteri(texture->glType, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
} else {
|
||||
glTexParameteri(texture->glType, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameteri(texture->glType, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
}
|
||||
break;
|
||||
|
||||
case FILTER_TRILINEAR:
|
||||
case FILTER_ANISOTROPIC:
|
||||
if (texture->mipmaps) {
|
||||
glTexParameteri(texture->glType, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
|
||||
glTexParameteri(texture->glType, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
} else {
|
||||
glTexParameteri(texture->glType, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameteri(texture->glType, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
glTexParameteri(texture->glType, GL_TEXTURE_MAX_ANISOTROPY_EXT, anisotropy);
|
||||
}
|
||||
|
||||
TextureWrap lovrTextureGetWrap(Texture* texture) {
|
||||
return texture->wrap;
|
||||
}
|
||||
|
||||
void lovrTextureSetWrap(Texture* texture, TextureWrap wrap) {
|
||||
texture->wrap = wrap;
|
||||
gpuBindTexture(texture, 0);
|
||||
glTexParameteri(texture->glType, GL_TEXTURE_WRAP_S, lovrConvertWrapMode(wrap.s));
|
||||
glTexParameteri(texture->glType, GL_TEXTURE_WRAP_T, lovrConvertWrapMode(wrap.t));
|
||||
if (texture->type == TEXTURE_CUBE || texture->type == TEXTURE_VOLUME) {
|
||||
glTexParameteri(texture->glType, GL_TEXTURE_WRAP_R, lovrConvertWrapMode(wrap.r));
|
||||
}
|
||||
}
|
|
@ -15,7 +15,8 @@ typedef enum {
|
|||
SHADER_CUBE,
|
||||
SHADER_PANO,
|
||||
SHADER_FONT,
|
||||
SHADER_FILL
|
||||
SHADER_FILL,
|
||||
MAX_DEFAULT_SHADERS
|
||||
} DefaultShader;
|
||||
|
||||
typedef struct Shader Shader;
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
#include "math/mat4.h"
|
||||
#include "math/vec3.h"
|
||||
#include "math/quat.h"
|
||||
#include "lib/glfw.h"
|
||||
#define _USE_MATH_DEFINES
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
|
@ -286,21 +287,23 @@ static void fakeRenderTo(void (*callback)(void*), void* userdata) {
|
|||
float projection[16];
|
||||
mat4_perspective(projection, state.clipNear, state.clipFar, 67 * M_PI / 180., (float) width / height);
|
||||
|
||||
float view[16];
|
||||
mat4_identity(view);
|
||||
mat4_translate(view, 0, state.offset, 0);
|
||||
mat4_multiply(view, state.transform);
|
||||
mat4_invert(view);
|
||||
float viewMatrix[16];
|
||||
mat4_identity(viewMatrix);
|
||||
mat4_translate(viewMatrix, 0, state.offset, 0);
|
||||
mat4_multiply(viewMatrix, state.transform);
|
||||
mat4_invert(viewMatrix);
|
||||
|
||||
Color backgroundColor = lovrGraphicsGetBackgroundColor();
|
||||
lovrGraphicsPushLayer(NULL, 0, false);
|
||||
lovrGraphicsClear(&backgroundColor, &(float) { 1. }, &(int) { 0 });
|
||||
lovrGraphicsSetCamera(projection, view);
|
||||
lovrGraphicsSetViewport(0, 0, width, height);
|
||||
callback(userdata);
|
||||
lovrGraphicsSetViewport(width, 0, width, height);
|
||||
callback(userdata);
|
||||
lovrGraphicsPopLayer();
|
||||
for (int eye = 0; eye < 2; eye++) {
|
||||
lovrGraphicsSetCamera(&(Camera) {
|
||||
.viewport = { width * eye, 0, width, height },
|
||||
.viewMatrix = viewMatrix,
|
||||
.projection = projection
|
||||
}, eye == 0);
|
||||
|
||||
callback(userdata);
|
||||
}
|
||||
|
||||
lovrGraphicsSetCamera(NULL, false);
|
||||
}
|
||||
|
||||
static void fakeUpdate(float dt) {
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
#pragma pack(pop)
|
||||
#endif
|
||||
|
||||
#include "graphics/opengl/opengl.h"
|
||||
#include "graphics/opengl.h"
|
||||
|
||||
// From openvr_capi.h
|
||||
extern intptr_t VR_InitInternal(EVRInitError *peError, EVRApplicationType eType);
|
||||
|
@ -673,26 +673,23 @@ static void openvrRenderTo(void (*callback)(void*), void* userdata) {
|
|||
state.compositor->WaitGetPoses(state.renderPoses, 16, NULL, 0);
|
||||
mat4_fromMat34(head, state.renderPoses[state.headsetIndex].mDeviceToAbsoluteTracking.m);
|
||||
|
||||
Color backgroundColor = lovrGraphicsGetBackgroundColor();
|
||||
lovrGraphicsPushLayer(&state.canvas, 1, false);
|
||||
lovrGraphicsClear(&backgroundColor, &(float) { 1. }, NULL);
|
||||
|
||||
for (HeadsetEye i = EYE_LEFT; i <= EYE_RIGHT; i++) {
|
||||
|
||||
// Camera
|
||||
EVREye vrEye = (i == EYE_LEFT) ? EVREye_Eye_Left : EVREye_Eye_Right;
|
||||
for (int i = 0; i < 2; i++) {
|
||||
EVREye vrEye = (i == 0) ? EVREye_Eye_Left : EVREye_Eye_Right;
|
||||
mat4_fromMat44(projection, state.system->GetProjectionMatrix(vrEye, state.clipNear, state.clipFar).m);
|
||||
mat4_identity(view);
|
||||
mat4_translate(view, 0, state.offset, 0);
|
||||
mat4_multiply(view, head);
|
||||
mat4_multiply(view, mat4_fromMat34(eye, state.system->GetEyeToHeadTransform(vrEye).m));
|
||||
mat4_invert(view);
|
||||
lovrGraphicsSetCamera(projection, view);
|
||||
lovrGraphicsSetViewport(state.renderWidth * i, 0, state.renderWidth, state.renderHeight);
|
||||
|
||||
// Render
|
||||
lovrGraphicsSetCamera(&(Camera) {
|
||||
.canvas = state.canvas,
|
||||
.viewport = { state.renderWidth * i, 0, state.renderWidth, state.renderHeight },
|
||||
.viewMatrix = view,
|
||||
.projection = projection
|
||||
}, i == 0);
|
||||
|
||||
callback(userdata);
|
||||
lovrCanvasResolve(state.canvas);
|
||||
}
|
||||
|
||||
// Submit
|
||||
|
@ -707,19 +704,15 @@ static void openvrRenderTo(void (*callback)(void*), void* userdata) {
|
|||
state.compositor->Submit(EVREye_Eye_Right, &eyeTexture, &right, EVRSubmitFlags_Submit_Default);
|
||||
glBindTexture(GL_TEXTURE_2D, lovrTextureGetId(oldTexture));
|
||||
|
||||
lovrGraphicsPopLayer();
|
||||
lovrGraphicsSetCamera(NULL, false);
|
||||
state.isRendering = false;
|
||||
|
||||
if (state.isMirrored) {
|
||||
Color oldColor = lovrGraphicsGetColor();
|
||||
lovrGraphicsPushPipeline();
|
||||
lovrGraphicsSetColor((Color) { 1, 1, 1, 1 });
|
||||
Shader* lastShader = lovrGraphicsGetShader();
|
||||
lovrRetain(lastShader);
|
||||
lovrGraphicsSetShader(NULL);
|
||||
lovrGraphicsFill((Texture*) state.canvas);
|
||||
lovrGraphicsSetShader(lastShader);
|
||||
lovrRelease(lastShader);
|
||||
lovrGraphicsSetColor(oldColor);
|
||||
lovrGraphicsPopPipeline();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -89,15 +89,18 @@ static void onMountChanged(bool mounted) {
|
|||
static void onFrame(float* leftView, float* rightView, float* leftProjection, float* rightProjection, void* userdata) {
|
||||
int width, height;
|
||||
webvrGetDisplayDimensions(&width, &height);
|
||||
lovrGraphicsPushLayer(NULL, 0, false);
|
||||
lovrGraphicsClear(true, true, true, lovrGraphicsGetBackgroundColor(), 1., 0);
|
||||
lovrGraphicsSetCamera(leftProjection, leftView);
|
||||
lovrGraphicsSetViewport(0, 0, width, height);
|
||||
state.renderCallback(userdata);
|
||||
lovrGraphicsSetCamera(rightProjection, rightView);
|
||||
lovrGraphicsSetViewport(width, 0, width, height);
|
||||
state.renderCallback(userdata);
|
||||
lovrGraphicsPopLayer();
|
||||
mat4 views[2] = { leftView, rightView };
|
||||
mat4 projections[2] = { leftProjection, rightProjection };
|
||||
for (int eye = 0; eye < 2; eye++) {
|
||||
lovrGraphicsSetCamera(&(Camera) {
|
||||
.viewport = { width * eye, 0, width, height },
|
||||
.viewMatrix = views[eye],
|
||||
.projection = projections[eye]
|
||||
}, eye == 0);
|
||||
|
||||
state.renderCallback(userdata);
|
||||
}
|
||||
lovrGraphicsSetCamera(NULL, false);
|
||||
}
|
||||
|
||||
static bool webvrDriverInit(float offset) {
|
||||
|
|
Loading…
Reference in New Issue