Put non-opengl-specific stuff in their own files;

This commit is contained in:
bjorn 2018-12-24 21:08:06 -08:00 committed by Bjorn Swenson
parent fbcee9ac9c
commit c87645e8ef
11 changed files with 488 additions and 471 deletions

View File

@ -423,11 +423,15 @@ if(LOVR_ENABLE_GRAPHICS)
add_definitions(-DLOVR_ENABLE_GRAPHICS)
target_sources(lovr PRIVATE
src/graphics/animator.c
src/graphics/canvas.c
src/graphics/font.c
src/graphics/graphics.c
src/graphics/material.c
src/graphics/mesh.c
src/graphics/model.c
src/graphics/opengl.c
src/graphics/shader.c
src/graphics/texture.c
src/api/graphics.c
src/api/types/animator.c
src/api/types/canvas.c

9
src/graphics/buffer.c Normal file
View File

@ -0,0 +1,9 @@
#include "graphics/buffer.h"
size_t lovrBufferGetSize(Buffer* buffer) {
return buffer->size;
}
BufferUsage lovrBufferGetUsage(Buffer* buffer) {
return buffer->usage;
}

View File

@ -1,5 +1,7 @@
#include "graphics/opengl.h"
#include "util.h"
#include <stdlib.h>
#include <stdbool.h>
#pragma once

58
src/graphics/canvas.c Normal file
View File

@ -0,0 +1,58 @@
#include "graphics/canvas.h"
const Attachment* lovrCanvasGetAttachments(Canvas* canvas, int* count) {
if (count) *count = canvas->attachmentCount;
return canvas->attachments;
}
void lovrCanvasSetAttachments(Canvas* canvas, Attachment* attachments, int count) {
lovrAssert(count > 0, "A Canvas must have at least one attached Texture");
lovrAssert(count <= MAX_CANVAS_ATTACHMENTS, "Only %d textures can be attached to a Canvas, got %d\n", MAX_CANVAS_ATTACHMENTS, count);
if (!canvas->needsAttach && count == canvas->attachmentCount && !memcmp(canvas->attachments, attachments, count * sizeof(Attachment))) {
return;
}
for (int i = 0; i < count; i++) {
Texture* texture = attachments[i].texture;
int width = lovrTextureGetWidth(texture, attachments[i].level);
int height = lovrTextureGetHeight(texture, attachments[i].level);
bool hasDepth = canvas->flags.depth.enabled;
lovrAssert(!hasDepth || width == canvas->width, "Texture width of %d does not match Canvas width (%d)", width, canvas->width);
lovrAssert(!hasDepth || height == canvas->height, "Texture height of %d does not match Canvas height (%d)", height, canvas->height);
lovrAssert(texture->msaa == canvas->flags.msaa, "Texture MSAA does not match Canvas MSAA");
lovrRetain(texture);
}
for (int i = 0; i < canvas->attachmentCount; i++) {
lovrRelease(canvas->attachments[i].texture);
}
memcpy(canvas->attachments, attachments, count * sizeof(Attachment));
canvas->attachmentCount = count;
canvas->needsAttach = true;
}
bool lovrCanvasIsDirty(Canvas* canvas) {
return canvas->needsAttach;
}
bool lovrCanvasIsStereo(Canvas* canvas) {
return canvas->flags.stereo;
}
int lovrCanvasGetWidth(Canvas* canvas) {
return canvas->width;
}
int lovrCanvasGetHeight(Canvas* canvas) {
return canvas->height;
}
int lovrCanvasGetMSAA(Canvas* canvas) {
return canvas->flags.msaa;
}
Texture* lovrCanvasGetDepthTexture(Canvas* canvas) {
return canvas->depth.texture;
}

View File

@ -2,6 +2,7 @@
#include "data/rasterizer.h"
#include "event/event.h"
#include "filesystem/filesystem.h"
#include "math/math.h"
#include "util.h"
#include "lib/math.h"
#include "lib/stb/stb_image.h"
@ -12,6 +13,14 @@
static GraphicsState state;
static void gammaCorrectColor(Color* color) {
if (state.gammaCorrect) {
color->r = lovrMathGammaToLinear(color->r);
color->g = lovrMathGammaToLinear(color->g);
color->b = lovrMathGammaToLinear(color->b);
}
}
static void onCloseWindow() {
lovrEventPush((Event) { .type = EVENT_QUIT, .data.quit = { false, 0 } });
}
@ -410,6 +419,7 @@ uint16_t* lovrGraphicsGetIndexPointer(uint32_t count) {
}
void lovrGraphicsClear(Color* color, float* depth, int* stencil) {
if (color) gammaCorrectColor(color);
lovrGpuClear(state.canvas ? state.canvas : state.camera.canvas, color, depth, stencil);
}

View File

@ -4,7 +4,7 @@
#include "graphics/mesh.h"
#include "graphics/shader.h"
#include "graphics/texture.h"
#include "math/math.h"
#include "lib/math.h"
#include "util.h"
#include "platform.h"
#include <stdbool.h>

126
src/graphics/mesh.c Normal file
View File

@ -0,0 +1,126 @@
#include "graphics/mesh.h"
void lovrMeshAttachAttribute(Mesh* mesh, const char* name, MeshAttribute* attribute) {
lovrAssert(!map_get(&mesh->attributes, name), "Mesh already has an attribute named '%s'", name);
lovrAssert(attribute->divisor >= 0, "Divisor can't be negative");
map_set(&mesh->attributes, name, *attribute);
lovrRetain(attribute->buffer);
mesh->dirty = true;
}
void lovrMeshDetachAttribute(Mesh* mesh, const char* name) {
MeshAttribute* attribute = map_get(&mesh->attributes, name);
lovrAssert(attribute, "No attached attribute '%s' was found", name);
lovrAssert(attribute->buffer != mesh->vbo, "Attribute '%s' was not attached from another Mesh", name);
lovrRelease(attribute->buffer);
map_remove(&mesh->attributes, name);
mesh->dirty = true;
}
MeshAttribute* lovrMeshGetAttribute(Mesh* mesh, const char* name) {
return map_get(&mesh->attributes, name);
}
bool lovrMeshIsDirty(Mesh* mesh) {
return mesh->dirty;
}
VertexFormat* lovrMeshGetVertexFormat(Mesh* mesh) {
return &mesh->format;
}
bool lovrMeshIsReadable(Mesh* mesh) {
return mesh->readable;
}
DrawMode lovrMeshGetDrawMode(Mesh* mesh) {
return mesh->mode;
}
void lovrMeshSetDrawMode(Mesh* mesh, DrawMode mode) {
if (mesh->mode != mode) {
mesh->mode = mode;
mesh->dirty = true;
}
}
int lovrMeshGetVertexCount(Mesh* mesh) {
return mesh->count;
}
bool lovrMeshIsAttributeEnabled(Mesh* mesh, const char* name) {
MeshAttribute* attribute = map_get(&mesh->attributes, name);
lovrAssert(attribute, "Mesh does not have an attribute named '%s'", name);
return attribute->enabled;
}
void lovrMeshSetAttributeEnabled(Mesh* mesh, const char* name, bool enable) {
MeshAttribute* attribute = map_get(&mesh->attributes, name);
lovrAssert(attribute, "Mesh does not have an attribute named '%s'", name);
mesh->dirty = attribute->enabled != enable;
attribute->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);
if (mesh->rangeStart != start || mesh->rangeCount != count) {
mesh->rangeStart = start;
mesh->rangeCount = count;
mesh->dirty = true;
}
}
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;
mesh->dirty = true;
}
}
void* lovrMeshMapVertices(Mesh* mesh, size_t offset) {
return lovrBufferMap(mesh->vbo, offset);
}
void lovrMeshFlushVertices(Mesh* mesh, size_t offset, size_t size) {
lovrBufferFlush(mesh->vbo, offset, size);
}
void* lovrMeshMapIndices(Mesh* mesh, uint32_t count, size_t indexSize, size_t offset) {
mesh->indexSize = indexSize;
mesh->indexCount = count;
mesh->dirty = true;
if (count == 0) {
return NULL;
}
if (mesh->indexCapacity < indexSize * count) {
mesh->indexCapacity = nextPo2(indexSize * count);
lovrRelease(mesh->ibo);
mesh->ibo = lovrBufferCreate(mesh->indexCapacity, NULL, mesh->usage, mesh->readable);
}
return lovrBufferMap(mesh->ibo, offset);
}
void lovrMeshFlushIndices(Mesh* mesh) {
if (mesh->indexCount > 0) {
lovrBufferFlush(mesh->ibo, 0, mesh->indexCount * mesh->indexSize);
}
}
void* lovrMeshReadIndices(Mesh* mesh, uint32_t* count, size_t* indexSize) {
return *count = mesh->indexCount, *indexSize = mesh->indexSize, lovrBufferMap(mesh->ibo, 0);
}

View File

@ -74,14 +74,6 @@ static struct {
// Helper functions
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;
@ -285,58 +277,6 @@ static TextureType getUniformTextureType(GLenum type) {
}
}
static size_t getUniformTypeLength(const Uniform* uniform) {
size_t size = 0;
if (uniform->count > 1) {
size += 2 + floor(log10(uniform->count)) + 1; // "[count]"
}
switch (uniform->type) {
case UNIFORM_MATRIX: size += 4; break;
case UNIFORM_FLOAT: size += uniform->components == 1 ? 5 : 4; break;
case UNIFORM_INT: size += uniform->components == 1 ? 3 : 5; break;
default: break;
}
return size;
}
static const char* getUniformTypeName(const Uniform* uniform) {
switch (uniform->type) {
case UNIFORM_FLOAT:
switch (uniform->components) {
case 1: return "float";
case 2: return "vec2";
case 3: return "vec3";
case 4: return "vec4";
}
break;
case UNIFORM_INT:
switch (uniform->components) {
case 1: return "int";
case 2: return "ivec2";
case 3: return "ivec3";
case 4: return "ivec4";
}
break;
case UNIFORM_MATRIX:
switch (uniform->components) {
case 2: return "mat2";
case 3: return "mat3";
case 4: return "mat4";
}
break;
default: break;
}
lovrThrow("Unreachable");
return "";
}
// TODO really ought to have TextureType-specific default textures
static Texture* lovrGpuGetDefaultTexture() {
if (!state.defaultTexture) {
@ -759,7 +699,6 @@ void lovrGpuClear(Canvas* canvas, Color* color, float* depth, int* stencil) {
lovrCanvasBind(canvas, true);
if (color) {
gammaCorrectColor(color);
int count = canvas ? canvas->attachmentCount : 1;
for (int i = 0; i < count; i++) {
glClearBufferfv(GL_COLOR, i, (float[]) { color->r, color->g, color->b, color->a });
@ -1081,34 +1020,6 @@ void lovrTextureReplacePixels(Texture* texture, TextureData* textureData, int x,
}
}
int lovrTextureGetWidth(Texture* texture, int mipmap) {
return MAX(texture->width >> mipmap, 1);
}
int lovrTextureGetHeight(Texture* texture, int mipmap) {
return MAX(texture->height >> mipmap, 1);
}
int lovrTextureGetDepth(Texture* texture, int mipmap) {
return texture->type == TEXTURE_VOLUME ? MAX(texture->depth >> mipmap, 1) : texture->depth;
}
int lovrTextureGetMipmapCount(Texture* texture) {
return texture->mipmapCount;
}
TextureType lovrTextureGetType(Texture* texture) {
return texture->type;
}
TextureFormat lovrTextureGetFormat(Texture* texture) {
return texture->format;
}
TextureFilter lovrTextureGetFilter(Texture* texture) {
return texture->filter;
}
void lovrTextureSetFilter(Texture* texture, TextureFilter filter) {
float anisotropy = filter.mode == FILTER_ANISOTROPIC ? MAX(filter.anisotropy, 1.) : 1.;
lovrGpuBindTexture(texture, 0);
@ -1145,10 +1056,6 @@ void lovrTextureSetFilter(Texture* texture, TextureFilter filter) {
glTexParameteri(texture->target, GL_TEXTURE_MAX_ANISOTROPY_EXT, anisotropy);
}
TextureWrap lovrTextureGetWrap(Texture* texture) {
return texture->wrap;
}
void lovrTextureSetWrap(Texture* texture, TextureWrap wrap) {
texture->wrap = wrap;
lovrGpuBindTexture(texture, 0);
@ -1217,39 +1124,6 @@ void lovrCanvasDestroy(void* ref) {
lovrRelease(canvas->depth.texture);
}
const Attachment* lovrCanvasGetAttachments(Canvas* canvas, int* count) {
if (count) *count = canvas->attachmentCount;
return canvas->attachments;
}
void lovrCanvasSetAttachments(Canvas* canvas, Attachment* attachments, int count) {
lovrAssert(count > 0, "A Canvas must have at least one attached Texture");
lovrAssert(count <= MAX_CANVAS_ATTACHMENTS, "Only %d textures can be attached to a Canvas, got %d\n", MAX_CANVAS_ATTACHMENTS, count);
if (!canvas->needsAttach && count == canvas->attachmentCount && !memcmp(canvas->attachments, attachments, count * sizeof(Attachment))) {
return;
}
for (int i = 0; i < count; i++) {
Texture* texture = attachments[i].texture;
int width = lovrTextureGetWidth(texture, attachments[i].level);
int height = lovrTextureGetHeight(texture, attachments[i].level);
bool hasDepth = canvas->flags.depth.enabled;
lovrAssert(!hasDepth || width == canvas->width, "Texture width of %d does not match Canvas width (%d)", width, canvas->width);
lovrAssert(!hasDepth || height == canvas->height, "Texture height of %d does not match Canvas height (%d)", height, canvas->height);
lovrAssert(texture->msaa == canvas->flags.msaa, "Texture MSAA does not match Canvas MSAA");
lovrRetain(texture);
}
for (int i = 0; i < canvas->attachmentCount; i++) {
lovrRelease(canvas->attachments[i].texture);
}
memcpy(canvas->attachments, attachments, count * sizeof(Attachment));
canvas->attachmentCount = count;
canvas->needsAttach = true;
}
void lovrCanvasBind(Canvas* canvas, bool willDraw) {
if (canvas) {
lovrGpuBindFramebuffer(canvas->framebuffer);
@ -1311,10 +1185,6 @@ void lovrCanvasBind(Canvas* canvas, bool willDraw) {
canvas->needsAttach = false;
}
bool lovrCanvasIsDirty(Canvas* canvas) {
return canvas->needsAttach;
}
void lovrCanvasResolve(Canvas* canvas) {
if (!canvas->needsResolve) {
return;
@ -1357,26 +1227,6 @@ void lovrCanvasResolve(Canvas* canvas) {
canvas->needsResolve = false;
}
bool lovrCanvasIsStereo(Canvas* canvas) {
return canvas->flags.stereo;
}
int lovrCanvasGetWidth(Canvas* canvas) {
return canvas->width;
}
int lovrCanvasGetHeight(Canvas* canvas) {
return canvas->height;
}
int lovrCanvasGetMSAA(Canvas* canvas) {
return canvas->flags.msaa;
}
Texture* lovrCanvasGetDepthTexture(Canvas* canvas) {
return canvas->depth.texture;
}
TextureData* lovrCanvasNewTextureData(Canvas* canvas, int index) {
lovrCanvasBind(canvas, false);
@ -1441,14 +1291,6 @@ void lovrBufferDestroy(void* ref) {
#endif
}
size_t lovrBufferGetSize(Buffer* buffer) {
return buffer->size;
}
BufferUsage lovrBufferGetUsage(Buffer* buffer) {
return buffer->usage;
}
void* lovrBufferMap(Buffer* buffer, size_t offset) {
return (uint8_t*) buffer->data + offset;
}
@ -1701,23 +1543,13 @@ static void lovrShaderSetupUniforms(Shader* shader) {
}
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;
case UNIFORM_FLOAT: glGetUniformfv(program, location, &uniform.value.floats[offset]); break;
case UNIFORM_INT: glGetUniformiv(program, location, &uniform.value.ints[offset]); break;
case UNIFORM_MATRIX: glGetUniformfv(program, location, &uniform.value.floats[offset]); break;
default: break;
}
offset += uniform.components * (uniform.type == UNIFORM_MATRIX ? uniform.components : 1);
}
map_set(&shader->uniformMap, uniform.name, shader->uniforms.length);
@ -1773,6 +1605,7 @@ Shader* lovrShaderInitGraphics(Shader* shader, const char* vertexSource, const c
shader->program = program;
shader->type = SHADER_GRAPHICS;
// Generic attributes
lovrGpuUseProgram(program);
glVertexAttrib4fv(LOVR_SHADER_VERTEX_COLOR, (float[4]) { 1., 1., 1., 1. });
glVertexAttribI4iv(LOVR_SHADER_BONES, (int[4]) { 0., 0., 0., 0. });
@ -1786,9 +1619,7 @@ Shader* lovrShaderInitGraphics(Shader* shader, const char* vertexSource, const c
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);
glGetActiveAttrib(program, i, LOVR_MAX_ATTRIBUTE_LENGTH, NULL, NULL, NULL, name);
map_set(&shader->attributes, name, glGetAttribLocation(program, name));
}
@ -1814,17 +1645,6 @@ Shader* lovrShaderInitCompute(Shader* shader, const char* source) {
return shader;
}
Shader* lovrShaderInitDefault(Shader* shader, DefaultShader type) {
switch (type) {
case SHADER_DEFAULT: return lovrShaderInitGraphics(shader, NULL, NULL);
case SHADER_CUBE: return lovrShaderInitGraphics(shader, lovrCubeVertexShader, lovrCubeFragmentShader);
case SHADER_PANO: return lovrShaderInitGraphics(shader, lovrCubeVertexShader, lovrPanoFragmentShader);
case SHADER_FONT: return lovrShaderInitGraphics(shader, NULL, lovrFontFragmentShader);
case SHADER_FILL: return lovrShaderInitGraphics(shader, lovrFillVertexShader, NULL);
default: lovrThrow("Unknown default shader type"); return NULL;
}
}
void lovrShaderDestroy(void* ref) {
Shader* shader = ref;
glDeleteProgram(shader->program);
@ -1845,10 +1665,6 @@ void lovrShaderDestroy(void* ref) {
map_deinit(&shader->blockMap);
}
ShaderType lovrShaderGetType(Shader* shader) {
return shader->type;
}
void lovrShaderBind(Shader* shader) {
UniformBlock* block;
Uniform* uniform;
@ -1979,104 +1795,6 @@ void lovrShaderBind(Shader* shader) {
shader->dirty = false;
}
bool lovrShaderIsDirty(Shader* shader) {
return shader->dirty;
}
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->uniformMap, name) != NULL;
}
const Uniform* lovrShaderGetUniform(Shader* shader, const char* name) {
int* index = map_get(&shader->uniformMap, name);
if (!index) {
return false;
}
return &shader->uniforms.data[*index];
}
static void lovrShaderSetUniform(Shader* shader, const char* name, UniformType type, void* data, int start, int count, int size, const char* debug) {
int* index = map_get(&shader->uniformMap, name);
if (!index) {
return;
}
Uniform* uniform = &shader->uniforms.data[*index];
const char* plural = (uniform->size / size) > 1 ? "s" : "";
lovrAssert(uniform->type == type, "Unable to send %ss to uniform %s", debug, name);
lovrAssert((start + count) * size <= uniform->size, "Too many %s%s for uniform %s, maximum is %d", debug, plural, name, uniform->size / size);
void* dest = uniform->value.bytes + start * size;
if (!uniform->dirty && !memcmp(dest, data, count * size)) {
return;
}
memcpy(dest, data, count * size);
uniform->dirty = true;
shader->dirty = true;
}
void lovrShaderSetFloats(Shader* shader, const char* name, float* data, int start, int count) {
lovrShaderSetUniform(shader, name, UNIFORM_FLOAT, data, start, count, sizeof(float), "float");
}
void lovrShaderSetInts(Shader* shader, const char* name, int* data, int start, int count) {
lovrShaderSetUniform(shader, name, UNIFORM_INT, data, start, count, sizeof(int), "int");
}
void lovrShaderSetMatrices(Shader* shader, const char* name, float* data, int start, int count) {
lovrShaderSetUniform(shader, name, UNIFORM_MATRIX, data, start, count, sizeof(float), "float");
}
void lovrShaderSetTextures(Shader* shader, const char* name, Texture** data, int start, int count) {
lovrShaderSetUniform(shader, name, UNIFORM_SAMPLER, data, start, count, sizeof(Texture*), "texture");
}
void lovrShaderSetImages(Shader* shader, const char* name, Image* data, int start, int count) {
lovrShaderSetUniform(shader, name, UNIFORM_IMAGE, data, start, count, sizeof(Image), "image");
}
void lovrShaderSetColor(Shader* shader, const char* name, Color color) {
gammaCorrectColor(&color);
lovrShaderSetUniform(shader, name, UNIFORM_FLOAT, (float*) &color, 0, 4, sizeof(float), "float");
}
void lovrShaderSetBlock(Shader* shader, const char* name, ShaderBlock* source, UniformAccess access) {
int* id = map_get(&shader->blockMap, name);
lovrAssert(id, "No shader block named '%s'", name);
int type = *id & 1;
int index = *id >> 1;
UniformBlock* block = &shader->blocks[type].data[index];
block->access = access;
if (source != block->source) {
if (source) {
lovrAssert(block->uniforms.length == source->uniforms.length, "ShaderBlock must have same number of uniforms as block definition in Shader");
for (int i = 0; i < block->uniforms.length; i++) {
const Uniform* u = &block->uniforms.data[i];
const Uniform* v = &source->uniforms.data[i];
lovrAssert(u->type == v->type, "Shader is not compatible with ShaderBlock, check type of variable '%s'", v->name);
lovrAssert(u->offset == v->offset, "Shader is not compatible with ShaderBlock, check order of variable '%s'", v->name);
// This check is disabled due to observed driver bugs with std140 layouts
// lovrAssert(u->size == v->size, "Shader is not compatible with ShaderBlock, check count of variable '%s'", v->name);
}
}
lovrRetain(source);
lovrRelease(block->source);
block->source = source;
shader->dirty = true;
}
}
// ShaderBlock
ShaderBlock* lovrShaderBlockInit(ShaderBlock* block, vec_uniform_t* uniforms, BlockType type, BufferUsage usage) {
@ -2126,62 +1844,6 @@ void lovrShaderBlockDestroy(void* ref) {
map_deinit(&block->uniformMap);
}
BlockType lovrShaderBlockGetType(ShaderBlock* block) {
return block->type;
}
// TODO use sds!
char* lovrShaderBlockGetShaderCode(ShaderBlock* block, const char* blockName, size_t* length) {
// Calculate
size_t size = 0;
size_t tab = 2;
size += 15; // "layout(std140) "
size += block->type == BLOCK_UNIFORM ? 7 : 6; // "uniform" || "buffer"
size += 1; // " "
size += strlen(blockName);
size += 3; // " {\n"
for (int i = 0; i < block->uniforms.length; i++) {
size += tab;
size += getUniformTypeLength(&block->uniforms.data[i]);
size += 1; // " "
size += strlen(block->uniforms.data[i].name);
size += 2; // ";\n"
}
size += 3; // "};\n"
// Allocate
char* code = malloc(size + 1);
// Concatenate
char* s = code;
s += sprintf(s, "layout(std140) %s %s {\n", block->type == BLOCK_UNIFORM ? "uniform" : "buffer", blockName);
for (int i = 0; i < block->uniforms.length; i++) {
const Uniform* uniform = &block->uniforms.data[i];
if (uniform->count > 1) {
s += sprintf(s, " %s %s[%d];\n", getUniformTypeName(uniform), uniform->name, uniform->count);
} else {
s += sprintf(s, " %s %s;\n", getUniformTypeName(uniform), uniform->name);
}
}
s += sprintf(s, "};\n");
*s = '\0';
*length = size;
return code;
}
const Uniform* lovrShaderBlockGetUniform(ShaderBlock* block, const char* name) {
int* index = map_get(&block->uniformMap, name);
if (!index) return NULL;
return &block->uniforms.data[*index];
}
Buffer* lovrShaderBlockGetBuffer(ShaderBlock* block) {
return block->buffer;
}
// Mesh
Mesh* lovrMeshInit(Mesh* mesh, uint32_t count, VertexFormat format, DrawMode mode, BufferUsage usage, bool readable) {
@ -2225,27 +1887,6 @@ void lovrMeshDestroy(void* ref) {
lovrRelease(mesh->ibo);
}
void lovrMeshAttachAttribute(Mesh* mesh, const char* name, MeshAttribute* attribute) {
lovrAssert(!map_get(&mesh->attributes, name), "Mesh already has an attribute named '%s'", name);
lovrAssert(attribute->divisor >= 0, "Divisor can't be negative");
map_set(&mesh->attributes, name, *attribute);
lovrRetain(attribute->buffer);
mesh->dirty = true;
}
void lovrMeshDetachAttribute(Mesh* mesh, const char* name) {
MeshAttribute* attribute = map_get(&mesh->attributes, name);
lovrAssert(attribute, "No attached attribute '%s' was found", name);
lovrAssert(attribute->buffer != mesh->vbo, "Attribute '%s' was not attached from another Mesh", name);
lovrRelease(attribute->buffer);
map_remove(&mesh->attributes, name);
mesh->dirty = true;
}
MeshAttribute* lovrMeshGetAttribute(Mesh* mesh, const char* name) {
return map_get(&mesh->attributes, name);
}
void lovrMeshBind(Mesh* mesh, Shader* shader, int divisorMultiplier) {
const char* key;
map_iter_t iter = map_iter(&mesh->attachments);
@ -2323,10 +1964,6 @@ void lovrMeshBind(Mesh* mesh, Shader* shader, int divisorMultiplier) {
mesh->dirty = false;
}
bool lovrMeshIsDirty(Mesh* mesh) {
return mesh->dirty;
}
void lovrMeshDraw(Mesh* mesh, int instances) {
GLenum glDrawMode = convertDrawMode(mesh->mode);
@ -2350,103 +1987,3 @@ void lovrMeshDraw(Mesh* mesh, int instances) {
state.stats.drawCalls++;
}
VertexFormat* lovrMeshGetVertexFormat(Mesh* mesh) {
return &mesh->format;
}
bool lovrMeshIsReadable(Mesh* mesh) {
return mesh->readable;
}
DrawMode lovrMeshGetDrawMode(Mesh* mesh) {
return mesh->mode;
}
void lovrMeshSetDrawMode(Mesh* mesh, DrawMode mode) {
if (mesh->mode != mode) {
mesh->mode = mode;
mesh->dirty = true;
}
}
int lovrMeshGetVertexCount(Mesh* mesh) {
return mesh->count;
}
bool lovrMeshIsAttributeEnabled(Mesh* mesh, const char* name) {
MeshAttribute* attribute = map_get(&mesh->attributes, name);
lovrAssert(attribute, "Mesh does not have an attribute named '%s'", name);
return attribute->enabled;
}
void lovrMeshSetAttributeEnabled(Mesh* mesh, const char* name, bool enable) {
MeshAttribute* attribute = map_get(&mesh->attributes, name);
lovrAssert(attribute, "Mesh does not have an attribute named '%s'", name);
mesh->dirty = attribute->enabled != enable;
attribute->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);
if (mesh->rangeStart != start || mesh->rangeCount != count) {
mesh->rangeStart = start;
mesh->rangeCount = count;
mesh->dirty = true;
}
}
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;
mesh->dirty = true;
}
}
void* lovrMeshMapVertices(Mesh* mesh, size_t offset) {
return lovrBufferMap(mesh->vbo, offset);
}
void lovrMeshFlushVertices(Mesh* mesh, size_t offset, size_t size) {
lovrBufferFlush(mesh->vbo, offset, size);
}
void* lovrMeshMapIndices(Mesh* mesh, uint32_t count, size_t indexSize, size_t offset) {
mesh->indexSize = indexSize;
mesh->indexCount = count;
mesh->dirty = true;
if (count == 0) {
return NULL;
}
if (mesh->indexCapacity < indexSize * count) {
mesh->indexCapacity = nextPo2(indexSize * count);
lovrRelease(mesh->ibo);
mesh->ibo = lovrBufferCreate(mesh->indexCapacity, NULL, mesh->usage, mesh->readable);
}
return lovrBufferMap(mesh->ibo, offset);
}
void lovrMeshFlushIndices(Mesh* mesh) {
if (mesh->indexCount > 0) {
lovrBufferFlush(mesh->ibo, 0, mesh->indexCount * mesh->indexSize);
}
}
void* lovrMeshReadIndices(Mesh* mesh, uint32_t* count, size_t* indexSize) {
return *count = mesh->indexCount, *indexSize = mesh->indexSize, lovrBufferMap(mesh->ibo, 0);
}

234
src/graphics/shader.c Normal file
View File

@ -0,0 +1,234 @@
#include "graphics/shader.h"
#include "graphics/graphics.h"
#include "math/math.h"
#include "resources/shaders.h"
#include <math.h>
static size_t getUniformTypeLength(const Uniform* uniform) {
size_t size = 0;
if (uniform->count > 1) {
size += 2 + floor(log10(uniform->count)) + 1; // "[count]"
}
switch (uniform->type) {
case UNIFORM_MATRIX: size += 4; break;
case UNIFORM_FLOAT: size += uniform->components == 1 ? 5 : 4; break;
case UNIFORM_INT: size += uniform->components == 1 ? 3 : 5; break;
default: break;
}
return size;
}
static const char* getUniformTypeName(const Uniform* uniform) {
switch (uniform->type) {
case UNIFORM_FLOAT:
switch (uniform->components) {
case 1: return "float";
case 2: return "vec2";
case 3: return "vec3";
case 4: return "vec4";
}
break;
case UNIFORM_INT:
switch (uniform->components) {
case 1: return "int";
case 2: return "ivec2";
case 3: return "ivec3";
case 4: return "ivec4";
}
break;
case UNIFORM_MATRIX:
switch (uniform->components) {
case 2: return "mat2";
case 3: return "mat3";
case 4: return "mat4";
}
break;
default: break;
}
lovrThrow("Unreachable");
return "";
}
Shader* lovrShaderInitDefault(Shader* shader, DefaultShader type) {
switch (type) {
case SHADER_DEFAULT: return lovrShaderInitGraphics(shader, NULL, NULL);
case SHADER_CUBE: return lovrShaderInitGraphics(shader, lovrCubeVertexShader, lovrCubeFragmentShader);
case SHADER_PANO: return lovrShaderInitGraphics(shader, lovrCubeVertexShader, lovrPanoFragmentShader);
case SHADER_FONT: return lovrShaderInitGraphics(shader, NULL, lovrFontFragmentShader);
case SHADER_FILL: return lovrShaderInitGraphics(shader, lovrFillVertexShader, NULL);
default: lovrThrow("Unknown default shader type"); return NULL;
}
}
ShaderType lovrShaderGetType(Shader* shader) {
return shader->type;
}
bool lovrShaderIsDirty(Shader* shader) {
return shader->dirty;
}
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->uniformMap, name) != NULL;
}
const Uniform* lovrShaderGetUniform(Shader* shader, const char* name) {
int* index = map_get(&shader->uniformMap, name);
if (!index) {
return false;
}
return &shader->uniforms.data[*index];
}
static void lovrShaderSetUniform(Shader* shader, const char* name, UniformType type, void* data, int start, int count, int size, const char* debug) {
int* index = map_get(&shader->uniformMap, name);
if (!index) {
return;
}
Uniform* uniform = &shader->uniforms.data[*index];
const char* plural = (uniform->size / size) > 1 ? "s" : "";
lovrAssert(uniform->type == type, "Unable to send %ss to uniform %s", debug, name);
lovrAssert((start + count) * size <= uniform->size, "Too many %s%s for uniform %s, maximum is %d", debug, plural, name, uniform->size / size);
void* dest = uniform->value.bytes + start * size;
if (!uniform->dirty && !memcmp(dest, data, count * size)) {
return;
}
memcpy(dest, data, count * size);
uniform->dirty = true;
shader->dirty = true;
}
void lovrShaderSetFloats(Shader* shader, const char* name, float* data, int start, int count) {
lovrShaderSetUniform(shader, name, UNIFORM_FLOAT, data, start, count, sizeof(float), "float");
}
void lovrShaderSetInts(Shader* shader, const char* name, int* data, int start, int count) {
lovrShaderSetUniform(shader, name, UNIFORM_INT, data, start, count, sizeof(int), "int");
}
void lovrShaderSetMatrices(Shader* shader, const char* name, float* data, int start, int count) {
lovrShaderSetUniform(shader, name, UNIFORM_MATRIX, data, start, count, sizeof(float), "float");
}
void lovrShaderSetTextures(Shader* shader, const char* name, Texture** data, int start, int count) {
lovrShaderSetUniform(shader, name, UNIFORM_SAMPLER, data, start, count, sizeof(Texture*), "texture");
}
void lovrShaderSetImages(Shader* shader, const char* name, Image* data, int start, int count) {
lovrShaderSetUniform(shader, name, UNIFORM_IMAGE, data, start, count, sizeof(Image), "image");
}
void lovrShaderSetColor(Shader* shader, const char* name, Color color) {
if (lovrGraphicsIsGammaCorrect()) {
color.r = lovrMathGammaToLinear(color.r);
color.g = lovrMathGammaToLinear(color.g);
color.b = lovrMathGammaToLinear(color.b);
}
lovrShaderSetUniform(shader, name, UNIFORM_FLOAT, (float*) &color, 0, 4, sizeof(float), "float");
}
void lovrShaderSetBlock(Shader* shader, const char* name, ShaderBlock* source, UniformAccess access) {
int* id = map_get(&shader->blockMap, name);
lovrAssert(id, "No shader block named '%s'", name);
int type = *id & 1;
int index = *id >> 1;
UniformBlock* block = &shader->blocks[type].data[index];
block->access = access;
if (source != block->source) {
if (source) {
lovrAssert(block->uniforms.length == source->uniforms.length, "ShaderBlock must have same number of uniforms as block definition in Shader");
for (int i = 0; i < block->uniforms.length; i++) {
const Uniform* u = &block->uniforms.data[i];
const Uniform* v = &source->uniforms.data[i];
lovrAssert(u->type == v->type, "Shader is not compatible with ShaderBlock, check type of variable '%s'", v->name);
lovrAssert(u->offset == v->offset, "Shader is not compatible with ShaderBlock, check order of variable '%s'", v->name);
// This check is disabled due to observed driver bugs with std140 layouts
// lovrAssert(u->size == v->size, "Shader is not compatible with ShaderBlock, check count of variable '%s'", v->name);
}
}
lovrRetain(source);
lovrRelease(block->source);
block->source = source;
shader->dirty = true;
}
}
// ShaderBlock
BlockType lovrShaderBlockGetType(ShaderBlock* block) {
return block->type;
}
// TODO use sds!
char* lovrShaderBlockGetShaderCode(ShaderBlock* block, const char* blockName, size_t* length) {
// Calculate
size_t size = 0;
size_t tab = 2;
size += 15; // "layout(std140) "
size += block->type == BLOCK_UNIFORM ? 7 : 6; // "uniform" || "buffer"
size += 1; // " "
size += strlen(blockName);
size += 3; // " {\n"
for (int i = 0; i < block->uniforms.length; i++) {
size += tab;
size += getUniformTypeLength(&block->uniforms.data[i]);
size += 1; // " "
size += strlen(block->uniforms.data[i].name);
size += 2; // ";\n"
}
size += 3; // "};\n"
// Allocate
char* code = malloc(size + 1);
// Concatenate
char* s = code;
s += sprintf(s, "layout(std140) %s %s {\n", block->type == BLOCK_UNIFORM ? "uniform" : "buffer", blockName);
for (int i = 0; i < block->uniforms.length; i++) {
const Uniform* uniform = &block->uniforms.data[i];
if (uniform->count > 1) {
s += sprintf(s, " %s %s[%d];\n", getUniformTypeName(uniform), uniform->name, uniform->count);
} else {
s += sprintf(s, " %s %s;\n", getUniformTypeName(uniform), uniform->name);
}
}
s += sprintf(s, "};\n");
*s = '\0';
*length = size;
return code;
}
const Uniform* lovrShaderBlockGetUniform(ShaderBlock* block, const char* name) {
int* index = map_get(&block->uniformMap, name);
if (!index) return NULL;
return &block->uniforms.data[*index];
}
Buffer* lovrShaderBlockGetBuffer(ShaderBlock* block) {
return block->buffer;
}

View File

@ -104,6 +104,8 @@ typedef struct {
GPU_SHADER_FIELDS
} Shader;
// Shader
Shader* lovrShaderInitGraphics(Shader* shader, const char* vertexSource, const char* fragmentSource);
Shader* lovrShaderInitCompute(Shader* shader, const char* source);
Shader* lovrShaderInitDefault(Shader* shader, DefaultShader type);
@ -125,6 +127,8 @@ void lovrShaderSetImages(Shader* shader, const char* name, Image* data, int star
void lovrShaderSetColor(Shader* shader, const char* name, Color color);
void lovrShaderSetBlock(Shader* shader, const char* name, ShaderBlock* block, UniformAccess access);
// ShaderBlock
ShaderBlock* lovrShaderBlockInit(ShaderBlock* block, vec_uniform_t* uniforms, BlockType type, BufferUsage usage);
#define lovrShaderBlockCreate(...) lovrShaderBlockInit(lovrAlloc(ShaderBlock), __VA_ARGS__)
void lovrShaderBlockDestroy(void* ref);

33
src/graphics/texture.c Normal file
View File

@ -0,0 +1,33 @@
#include "graphics/texture.h"
int lovrTextureGetWidth(Texture* texture, int mipmap) {
return MAX(texture->width >> mipmap, 1);
}
int lovrTextureGetHeight(Texture* texture, int mipmap) {
return MAX(texture->height >> mipmap, 1);
}
int lovrTextureGetDepth(Texture* texture, int mipmap) {
return texture->type == TEXTURE_VOLUME ? MAX(texture->depth >> mipmap, 1) : texture->depth;
}
int lovrTextureGetMipmapCount(Texture* texture) {
return texture->mipmapCount;
}
TextureType lovrTextureGetType(Texture* texture) {
return texture->type;
}
TextureFormat lovrTextureGetFormat(Texture* texture) {
return texture->format;
}
TextureFilter lovrTextureGetFilter(Texture* texture) {
return texture->filter;
}
TextureWrap lovrTextureGetWrap(Texture* texture) {
return texture->wrap;
}