mirror of https://github.com/bjornbytes/lovr.git
Generalize Shader to support more types of uniforms;
This commit is contained in:
parent
b82ed7fd56
commit
54533351bb
|
@ -3,163 +3,134 @@
|
|||
#include "graphics/shader.h"
|
||||
#include "math/transform.h"
|
||||
|
||||
struct TempData {
|
||||
void* data;
|
||||
size_t size;
|
||||
};
|
||||
|
||||
static struct TempData tempData;
|
||||
|
||||
int l_lovrShaderSend(lua_State* L) {
|
||||
Shader* shader = luax_checktype(L, 1, Shader);
|
||||
const char* name = luaL_checkstring(L, 2);
|
||||
lua_settop(L, 3);
|
||||
|
||||
int id = lovrShaderGetUniformId(shader, name);
|
||||
if (id == -1) {
|
||||
Uniform* uniform = lovrShaderGetUniform(shader, name);
|
||||
|
||||
if (!uniform) {
|
||||
return luaL_error(L, "Unknown shader variable '%s'", name);
|
||||
}
|
||||
|
||||
GLenum type;
|
||||
int size;
|
||||
lovrShaderGetUniformType(shader, name, &type, &size);
|
||||
lovrGraphicsBindProgram(shader->id);
|
||||
float data[16];
|
||||
int n;
|
||||
vec_float_t values;
|
||||
vec_init(&values);
|
||||
|
||||
switch (type) {
|
||||
case GL_SAMPLER_2D:
|
||||
case GL_SAMPLER_CUBE:
|
||||
case GL_INT:
|
||||
lovrShaderSendInt(shader, id, luaL_checkinteger(L, 3));
|
||||
break;
|
||||
|
||||
case GL_FLOAT:
|
||||
lovrShaderSendFloat(shader, id, luaL_checknumber(L, 3));
|
||||
break;
|
||||
|
||||
case GL_FLOAT_VEC2:
|
||||
luaL_checktype(L, 3, LUA_TTABLE);
|
||||
lua_rawgeti(L, 3, 1);
|
||||
if (!lua_istable(L, -1)) {
|
||||
lua_newtable(L);
|
||||
lua_pushvalue(L, 3);
|
||||
lua_rawseti(L, -2, 1);
|
||||
} else {
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
|
||||
n = lua_objlen(L, -1);
|
||||
if (n < size) {
|
||||
return luaL_error(L, "Expected %d vec3s, got %d", size, n);
|
||||
}
|
||||
|
||||
for (int i = 0; i < size; i++) {
|
||||
lua_rawgeti(L, -1, i + 1);
|
||||
for (int j = 0; j < 2; j++) {
|
||||
lua_rawgeti(L, -1, j + 1);
|
||||
vec_push(&values, lua_tonumber(L, -1));
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
|
||||
lovrShaderSendFloatVec2(shader, id, size, values.data);
|
||||
break;
|
||||
|
||||
case GL_FLOAT_VEC3:
|
||||
luaL_checktype(L, 3, LUA_TTABLE);
|
||||
lua_rawgeti(L, 3, 1);
|
||||
if (!lua_istable(L, -1)) {
|
||||
lua_newtable(L);
|
||||
lua_pushvalue(L, 3);
|
||||
lua_rawseti(L, -2, 1);
|
||||
} else {
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
|
||||
n = lua_objlen(L, -1);
|
||||
if (n < size) {
|
||||
return luaL_error(L, "Expected %d vec3s, got %d", size, n);
|
||||
}
|
||||
|
||||
for (int i = 0; i < size; i++) {
|
||||
lua_rawgeti(L, -1, i + 1);
|
||||
for (int j = 0; j < 3; j++) {
|
||||
lua_rawgeti(L, -1, j + 1);
|
||||
vec_push(&values, lua_tonumber(L, -1));
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
|
||||
lovrShaderSendFloatVec3(shader, id, size, values.data);
|
||||
break;
|
||||
|
||||
case GL_FLOAT_VEC4:
|
||||
luaL_checktype(L, 3, LUA_TTABLE);
|
||||
lua_rawgeti(L, 3, 1);
|
||||
if (!lua_istable(L, -1)) {
|
||||
lua_newtable(L);
|
||||
lua_pushvalue(L, 3);
|
||||
lua_rawseti(L, -2, 1);
|
||||
} else {
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
|
||||
n = lua_objlen(L, -1);
|
||||
if (n < size) {
|
||||
return luaL_error(L, "Expected %d vec3s, got %d", size, n);
|
||||
}
|
||||
|
||||
for (int i = 0; i < size; i++) {
|
||||
lua_rawgeti(L, -1, i + 1);
|
||||
for (int j = 0; j < 4; j++) {
|
||||
lua_rawgeti(L, -1, j + 1);
|
||||
vec_push(&values, lua_tonumber(L, -1));
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
|
||||
lovrShaderSendFloatVec4(shader, id, size, values.data);
|
||||
break;
|
||||
|
||||
case GL_FLOAT_MAT2:
|
||||
luaL_checktype(L, 3, LUA_TTABLE);
|
||||
for (int i = 0; i < 4; i++) {
|
||||
lua_rawgeti(L, 3, i + 1);
|
||||
data[i] = lua_tonumber(L, -1);
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
lovrShaderSendFloatMat2(shader, id, data);
|
||||
break;
|
||||
|
||||
case GL_FLOAT_MAT3:
|
||||
luaL_checktype(L, 3, LUA_TTABLE);
|
||||
for (int i = 0; i < 9; i++) {
|
||||
lua_rawgeti(L, 3, i + 1);
|
||||
data[i] = lua_tonumber(L, -1);
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
lovrShaderSendFloatMat3(shader, id, data);
|
||||
break;
|
||||
|
||||
case GL_FLOAT_MAT4:
|
||||
if (lua_isuserdata(L, 3)) {
|
||||
Transform* transform = luax_checktype(L, 3, Transform);
|
||||
memcpy(data, transform->matrix, 16 * sizeof(float));
|
||||
} else {
|
||||
luaL_checktype(L, 3, LUA_TTABLE);
|
||||
for (int i = 0; i < 16; i++) {
|
||||
lua_rawgeti(L, 3, i + 1);
|
||||
data[i] = lua_tonumber(L, -1);
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
}
|
||||
lovrShaderSendFloatMat4(shader, id, data);
|
||||
break;
|
||||
|
||||
default:
|
||||
return luaL_error(L, "Unknown uniform type %d", type);
|
||||
if (!tempData.data) {
|
||||
tempData.data = malloc(uniform->size);
|
||||
} else if (tempData.size < uniform->size) {
|
||||
tempData.size = uniform->size;
|
||||
tempData.data = realloc(tempData.data, tempData.size);
|
||||
}
|
||||
|
||||
vec_deinit(&values);
|
||||
int* ints = (int*) tempData.data;
|
||||
float* floats = (float*) tempData.data;
|
||||
Texture** textures = (Texture**) tempData.data;
|
||||
|
||||
int n = 1;
|
||||
int components = uniform->components;
|
||||
|
||||
if (components > 1) {
|
||||
luaL_checktype(L, 3, LUA_TTABLE);
|
||||
lua_rawgeti(L, 3, 1);
|
||||
if (!lua_istable(L, -1)) {
|
||||
lua_newtable(L);
|
||||
lua_pushvalue(L, 3);
|
||||
lua_rawseti(L, -2, 1);
|
||||
} else {
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
|
||||
n = lua_objlen(L, -1);
|
||||
if (n != uniform->count) {
|
||||
const char* elements = uniform->count == 1 ? "element" : "elements";
|
||||
return luaL_error(L, "Expected %d %s for array '%s', got %d", uniform->count, elements, uniform->name, n);
|
||||
}
|
||||
}
|
||||
|
||||
switch (uniform->type) {
|
||||
case UNIFORM_FLOAT:
|
||||
if (components == 1) {
|
||||
floats[0] = luaL_checkinteger(L, 3);
|
||||
} else {
|
||||
luaL_checktype(L, 3, LUA_TTABLE);
|
||||
for (int i = 0; i < n; i++) {
|
||||
lua_rawgeti(L, -1, i + 1);
|
||||
if (lua_type(L, -1) != LUA_TTABLE || (int) lua_objlen(L, -1) != components) {
|
||||
return luaL_error(L, "Expected %d components for uniform '%s' #%d, got %d", components, uniform->name, lua_objlen(L, -1));
|
||||
}
|
||||
for (int j = 0; j < components; j++) {
|
||||
lua_rawgeti(L, -1, j + 1);
|
||||
floats[i * components + j] = luaL_checknumber(L, -1);
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
}
|
||||
lovrShaderSetFloat(shader, name, floats, n);
|
||||
break;
|
||||
|
||||
case UNIFORM_INT:
|
||||
if (components == 1) {
|
||||
ints[0] = luaL_checkinteger(L, 3);
|
||||
} else {
|
||||
luaL_checktype(L, 3, LUA_TTABLE);
|
||||
for (int i = 0; i < n; i++) {
|
||||
lua_rawgeti(L, -1, i + 1);
|
||||
if (lua_type(L, -1) != LUA_TTABLE || (int) lua_objlen(L, -1) != components) {
|
||||
return luaL_error(L, "Expected %d components for uniform '%s' #%d, got %d", components, uniform->name, lua_objlen(L, -1));
|
||||
}
|
||||
for (int j = 0; j < components; j++) {
|
||||
lua_rawgeti(L, -1, j + 1);
|
||||
ints[i * components + j] = luaL_checkinteger(L, -1);
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
}
|
||||
lovrShaderSetInt(shader, name, ints, n);
|
||||
break;
|
||||
|
||||
case UNIFORM_MATRIX:
|
||||
if (components == 4 && lua_isuserdata(L, 3)) {
|
||||
Transform* transform = luax_checktype(L, 3, Transform);
|
||||
memcpy(floats, transform->matrix, 16 * sizeof(float));
|
||||
} else {
|
||||
luaL_checktype(L, 3, LUA_TTABLE);
|
||||
for (int i = 0; i < n; i++) {
|
||||
lua_rawgeti(L, -1, i + 1);
|
||||
for (int j = 0; j < components * components; j++) {
|
||||
lua_rawgeti(L, -1, j + 1);
|
||||
floats[i * components * components + j] = luaL_checknumber(L, -1);
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
}
|
||||
lovrShaderSetMatrix(shader, name, floats, n);
|
||||
break;
|
||||
|
||||
case UNIFORM_SAMPLER:
|
||||
if (components == 1) {
|
||||
textures[0] = luax_checktype(L, 3, Texture);
|
||||
} else {
|
||||
luaL_checktype(L, 3, LUA_TTABLE);
|
||||
for (int i = 0; i < n; i++) {
|
||||
lua_rawgeti(L, -1, i + 1);
|
||||
textures[i] = luax_checktype(L, -1, Texture);
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
}
|
||||
lovrShaderSetTexture(shader, name, textures, n);
|
||||
break;
|
||||
|
||||
default: break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -87,8 +87,36 @@ void lovrGraphicsPrepare() {
|
|||
mat4 model = state.transforms[state.transform][MATRIX_MODEL];
|
||||
mat4 view = state.transforms[state.transform][MATRIX_VIEW];
|
||||
mat4 projection = state.canvases[state.canvas].projection;
|
||||
lovrShaderSetMatrix(shader, "lovrModel", model, 16);
|
||||
lovrShaderSetMatrix(shader, "lovrView", view, 16);
|
||||
lovrShaderSetMatrix(shader, "lovrProjection", projection, 16);
|
||||
|
||||
float transform[16];
|
||||
mat4_multiply(mat4_set(transform, view), model);
|
||||
lovrShaderSetMatrix(shader, "lovrTransform", transform, 16);
|
||||
|
||||
if (lovrShaderGetUniform(shader, "lovrNormalMatrix")) {
|
||||
if (mat4_invert(transform)) {
|
||||
mat4_transpose(transform);
|
||||
} else {
|
||||
mat4_identity(transform);
|
||||
}
|
||||
|
||||
float normalMatrix[9] = {
|
||||
transform[0], transform[1], transform[2],
|
||||
transform[4], transform[5], transform[6],
|
||||
transform[8], transform[9], transform[10]
|
||||
};
|
||||
|
||||
lovrShaderSetMatrix(shader, "lovrNormalMatrix", normalMatrix, 9);
|
||||
}
|
||||
|
||||
// Color
|
||||
float color[4] = { state.color.r / 255., state.color.g / 255., state.color.b / 255., state.color.a / 255. };
|
||||
lovrShaderSetFloat(shader, "lovrColor", color, 4);
|
||||
|
||||
lovrGraphicsBindProgram(shader->id);
|
||||
lovrShaderBind(shader, model, view, projection, state.color, 0);
|
||||
lovrShaderBind(shader);
|
||||
}
|
||||
|
||||
void lovrGraphicsCreateWindow(int w, int h, int fullscreen, int msaa, const char* title, const char* icon) {
|
||||
|
|
|
@ -5,6 +5,63 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.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_CUBE:
|
||||
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_CUBE:
|
||||
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);
|
||||
|
||||
|
@ -79,46 +136,103 @@ Shader* lovrShaderCreate(const char* vertexSource, const char* fragmentSource) {
|
|||
|
||||
// Link
|
||||
GLuint id = linkShaders(vertexShader, fragmentShader);
|
||||
shader->id = id;
|
||||
|
||||
// Compute information about uniforms
|
||||
lovrGraphicsBindProgram(id);
|
||||
|
||||
// Uniform introspection
|
||||
GLint uniformCount;
|
||||
int textureSlot = 0;
|
||||
GLsizei bufferSize = LOVR_MAX_UNIFORM_LENGTH / sizeof(GLchar);
|
||||
map_init(&shader->uniforms);
|
||||
glGetProgramiv(id, GL_ACTIVE_UNIFORMS, &uniformCount);
|
||||
for (int i = 0; i < uniformCount; i++) {
|
||||
Uniform uniform;
|
||||
glGetActiveUniform(id, i, bufferSize, NULL, &uniform.count, &uniform.type, uniform.name);
|
||||
glGetActiveUniform(id, i, bufferSize, NULL, &uniform.count, &uniform.glType, uniform.name);
|
||||
|
||||
char* subscript = strchr(uniform.name, '[');
|
||||
if (subscript) {
|
||||
*subscript = '\0';
|
||||
}
|
||||
uniform.location = glGetUniformLocation(id, uniform.name);
|
||||
|
||||
uniform.index = i;
|
||||
uniform.location = glGetUniformLocation(id, uniform.name);
|
||||
uniform.type = getUniformType(uniform.glType, uniform.name);
|
||||
uniform.components = getUniformComponents(uniform.glType);
|
||||
uniform.baseTextureSlot = (uniform.type == UNIFORM_SAMPLER) ? textureSlot : -1;
|
||||
|
||||
switch (uniform.type) {
|
||||
case UNIFORM_FLOAT:
|
||||
uniform.size = uniform.components * uniform.count * sizeof(float);
|
||||
uniform.value.data = malloc(uniform.size);
|
||||
break;
|
||||
|
||||
case UNIFORM_INT:
|
||||
uniform.size = uniform.components * uniform.count * sizeof(int);
|
||||
uniform.value.data = malloc(uniform.size);
|
||||
break;
|
||||
|
||||
case UNIFORM_MATRIX:
|
||||
uniform.size = uniform.components * uniform.components * uniform.count * sizeof(int);
|
||||
uniform.value.data = malloc(uniform.size);
|
||||
break;
|
||||
|
||||
case UNIFORM_SAMPLER:
|
||||
uniform.size = uniform.components * uniform.count * MAX(sizeof(Texture*), sizeof(int));
|
||||
uniform.value.data = malloc(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;
|
||||
}
|
||||
|
||||
memset(uniform.value.data, 0, uniform.size);
|
||||
|
||||
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(id, name);
|
||||
}
|
||||
|
||||
switch (uniform.type) {
|
||||
case UNIFORM_FLOAT:
|
||||
glGetUniformfv(id, location, &uniform.value.floats[offset]);
|
||||
offset += uniform.components;
|
||||
break;
|
||||
|
||||
case UNIFORM_INT:
|
||||
glGetUniformiv(id, location, &uniform.value.ints[offset]);
|
||||
offset += uniform.components;
|
||||
break;
|
||||
|
||||
case UNIFORM_MATRIX:
|
||||
glGetUniformfv(id, 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;
|
||||
}
|
||||
|
||||
// Initial state
|
||||
shader->id = id;
|
||||
mat4_identity(shader->model);
|
||||
mat4_identity(shader->view);
|
||||
mat4_identity(shader->projection);
|
||||
shader->color = (Color) { 0, 0, 0, 0 };
|
||||
|
||||
// Send initial uniform values to shader
|
||||
lovrGraphicsBindProgram(id);
|
||||
lovrShaderBind(shader, shader->model, shader->view, shader->projection, shader->color, 1);
|
||||
|
||||
return shader;
|
||||
}
|
||||
|
||||
Shader* lovrShaderCreateDefault(DefaultShader type) {
|
||||
switch (type) {
|
||||
case SHADER_DEFAULT: return lovrShaderCreate(NULL, NULL);
|
||||
case SHADER_SKYBOX: {
|
||||
Shader* shader = lovrShaderCreate(lovrSkyboxVertexShader, lovrSkyboxFragmentShader);
|
||||
lovrShaderSendInt(shader, lovrShaderGetUniformId(shader, "cube"), 1);
|
||||
return shader;
|
||||
}
|
||||
case SHADER_SKYBOX: return lovrShaderCreate(lovrSkyboxVertexShader, lovrSkyboxFragmentShader); break;
|
||||
case SHADER_FONT: return lovrShaderCreate(NULL, lovrFontFragmentShader);
|
||||
case SHADER_FULLSCREEN: return lovrShaderCreate(lovrNoopVertexShader, NULL);
|
||||
default: lovrThrow("Unknown default shader type");
|
||||
|
@ -132,64 +246,54 @@ void lovrShaderDestroy(const Ref* ref) {
|
|||
free(shader);
|
||||
}
|
||||
|
||||
void lovrShaderBind(Shader* shader, mat4 model, mat4 view, mat4 projection, Color color, int force) {
|
||||
int dirtyModel = force || memcmp(shader->model, model, 16 * sizeof(float));
|
||||
int dirtyView = force || memcmp(shader->view, view, 16 * sizeof(float));
|
||||
int dirtyTransform = dirtyModel || dirtyView;
|
||||
int dirtyProjection = force || memcmp(shader->projection, projection, 16 * sizeof(float));
|
||||
int dirtyColor = force || memcmp(&shader->color, &color, sizeof(Color));
|
||||
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 (dirtyModel) {
|
||||
int uniformId = lovrShaderGetUniformId(shader, "lovrModel");
|
||||
lovrShaderSendFloatMat4(shader, uniformId, model);
|
||||
memcpy(shader->model, model, 16 * sizeof(float));
|
||||
}
|
||||
|
||||
if (dirtyView) {
|
||||
int uniformId = lovrShaderGetUniformId(shader, "lovrView");
|
||||
lovrShaderSendFloatMat4(shader, uniformId, view);
|
||||
memcpy(shader->view, view, 16 * sizeof(float));
|
||||
}
|
||||
|
||||
if (dirtyTransform) {
|
||||
float matrix[16];
|
||||
int uniformId = lovrShaderGetUniformId(shader, "lovrTransform");
|
||||
|
||||
mat4_multiply(mat4_set(matrix, view), model);
|
||||
|
||||
if (uniformId > -1) {
|
||||
lovrShaderSendFloatMat4(shader, uniformId, matrix);
|
||||
if (uniform->type != UNIFORM_SAMPLER && !uniform->dirty) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (mat4_invert(matrix)) {
|
||||
mat4_transpose(matrix);
|
||||
} else {
|
||||
mat4_identity(matrix);
|
||||
uniform->dirty = 0;
|
||||
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++) {
|
||||
TextureType type = uniform->glType == GL_SAMPLER_2D ? TEXTURE_2D : TEXTURE_CUBE;
|
||||
lovrGraphicsBindTexture(uniform->value.textures[i], type, uniform->baseTextureSlot + i);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
float normalMatrix[9] = {
|
||||
matrix[0], matrix[1], matrix[2],
|
||||
matrix[4], matrix[5], matrix[6],
|
||||
matrix[8], matrix[9], matrix[10]
|
||||
};
|
||||
|
||||
uniformId = lovrShaderGetUniformId(shader, "lovrNormalMatrix");
|
||||
if (uniformId > -1) {
|
||||
lovrShaderSendFloatMat3(shader, uniformId, normalMatrix);
|
||||
}
|
||||
}
|
||||
|
||||
if (dirtyProjection) {
|
||||
int uniformId = lovrShaderGetUniformId(shader, "lovrProjection");
|
||||
lovrShaderSendFloatMat4(shader, uniformId, projection);
|
||||
memcpy(shader->projection, projection, 16 * sizeof(float));
|
||||
}
|
||||
|
||||
if (dirtyColor) {
|
||||
int uniformId = lovrShaderGetUniformId(shader, "lovrColor");
|
||||
float c[4] = { color.r / 255., color.g / 255., color.b / 255., color.a / 255. };
|
||||
lovrShaderSendFloatVec4(shader, uniformId, 1, c);
|
||||
shader->color = color;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -201,51 +305,39 @@ int lovrShaderGetAttributeId(Shader* shader, const char* name) {
|
|||
return glGetAttribLocation(shader->id, name);
|
||||
}
|
||||
|
||||
int lovrShaderGetUniformId(Shader* shader, const char* name) {
|
||||
Uniform* uniform = map_get(&shader->uniforms, name);
|
||||
return uniform ? uniform->location : -1;
|
||||
Uniform* lovrShaderGetUniform(Shader* shader, const char* name) {
|
||||
return map_get(&shader->uniforms, name);
|
||||
}
|
||||
|
||||
int lovrShaderGetUniformType(Shader* shader, const char* name, GLenum* type, int* count) {
|
||||
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 1;
|
||||
return;
|
||||
}
|
||||
|
||||
*type = uniform->type;
|
||||
*count = uniform->count;
|
||||
return 0;
|
||||
lovrAssert(uniform->type == type, "Unable to send %ss to uniform %s", debug, uniform->name);
|
||||
lovrAssert(count * size == uniform->size, "Expected %d %ss for uniform %s, got %d", uniform->count, debug, uniform->name, count);
|
||||
|
||||
if (!uniform->dirty && !memcmp(uniform->value.data, data, count * size)) {
|
||||
return;
|
||||
}
|
||||
|
||||
memcpy(uniform->value.data, data, count * size);
|
||||
uniform->dirty = 1;
|
||||
}
|
||||
|
||||
void lovrShaderSendInt(Shader* shader, int id, int value) {
|
||||
glUniform1i(id, value);
|
||||
void lovrShaderSetFloat(Shader* shader, const char* name, float* data, int count) {
|
||||
lovrShaderSetUniform(shader, name, UNIFORM_FLOAT, data, count, sizeof(float), "float");
|
||||
}
|
||||
|
||||
void lovrShaderSendFloat(Shader* shader, int id, float value) {
|
||||
glUniform1f(id, value);
|
||||
void lovrShaderSetInt(Shader* shader, const char* name, int* data, int count) {
|
||||
lovrShaderSetUniform(shader, name, UNIFORM_INT, data, count, sizeof(int), "int");
|
||||
}
|
||||
|
||||
void lovrShaderSendFloatVec2(Shader* shader, int id, int count, float* vector) {
|
||||
glUniform2fv(id, count, vector);
|
||||
void lovrShaderSetMatrix(Shader* shader, const char* name, float* data, int count) {
|
||||
lovrShaderSetUniform(shader, name, UNIFORM_MATRIX, data, count, sizeof(float), "float");
|
||||
}
|
||||
|
||||
void lovrShaderSendFloatVec3(Shader* shader, int id, int count, float* vector) {
|
||||
glUniform3fv(id, count, vector);
|
||||
}
|
||||
|
||||
void lovrShaderSendFloatVec4(Shader* shader, int id, int count, float* vector) {
|
||||
glUniform4fv(id, count, vector);
|
||||
}
|
||||
|
||||
void lovrShaderSendFloatMat2(Shader* shader, int id, float* matrix) {
|
||||
glUniformMatrix2fv(id, 1, GL_FALSE, matrix);
|
||||
}
|
||||
|
||||
void lovrShaderSendFloatMat3(Shader* shader, int id, float* matrix) {
|
||||
glUniformMatrix3fv(id, 1, GL_FALSE, matrix);
|
||||
}
|
||||
|
||||
void lovrShaderSendFloatMat4(Shader* shader, int id, float* matrix) {
|
||||
glUniformMatrix4fv(id, 1, GL_FALSE, matrix);
|
||||
void lovrShaderSetTexture(Shader* shader, const char* name, Texture** data, int count) {
|
||||
lovrShaderSetUniform(shader, name, UNIFORM_SAMPLER, data, count, sizeof(Texture*), "texture");
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
#include "graphics/texture.h"
|
||||
#include "math/math.h"
|
||||
#include "lib/map/map.h"
|
||||
#include "lib/glfw.h"
|
||||
|
@ -10,6 +11,20 @@
|
|||
#define LOVR_SHADER_TEX_COORD 2
|
||||
#define LOVR_MAX_UNIFORM_LENGTH 256
|
||||
|
||||
typedef enum {
|
||||
UNIFORM_FLOAT,
|
||||
UNIFORM_MATRIX,
|
||||
UNIFORM_INT,
|
||||
UNIFORM_SAMPLER
|
||||
} UniformType;
|
||||
|
||||
typedef union {
|
||||
void* data;
|
||||
int* ints;
|
||||
float* floats;
|
||||
Texture** textures;
|
||||
} UniformValue;
|
||||
|
||||
typedef enum {
|
||||
SHADER_DEFAULT,
|
||||
SHADER_SKYBOX,
|
||||
|
@ -19,10 +34,16 @@ typedef enum {
|
|||
|
||||
typedef struct {
|
||||
GLchar name[LOVR_MAX_UNIFORM_LENGTH];
|
||||
GLenum glType;
|
||||
int index;
|
||||
int location;
|
||||
GLenum type;
|
||||
int count;
|
||||
int components;
|
||||
size_t size;
|
||||
UniformType type;
|
||||
UniformValue value;
|
||||
int baseTextureSlot;
|
||||
int dirty;
|
||||
} Uniform;
|
||||
|
||||
typedef map_t(Uniform) map_uniform_t;
|
||||
|
@ -40,15 +61,10 @@ typedef struct {
|
|||
Shader* lovrShaderCreate(const char* vertexSource, const char* fragmentSource);
|
||||
Shader* lovrShaderCreateDefault(DefaultShader type);
|
||||
void lovrShaderDestroy(const Ref* ref);
|
||||
void lovrShaderBind(Shader* shader, mat4 model, mat4 view, mat4 projection, Color color, int force);
|
||||
void lovrShaderBind(Shader* shader);
|
||||
int lovrShaderGetAttributeId(Shader* shader, const char* name);
|
||||
int lovrShaderGetUniformId(Shader* shader, const char* name);
|
||||
int lovrShaderGetUniformType(Shader* shader, const char* name, GLenum* type, int* count);
|
||||
void lovrShaderSendInt(Shader* shader, int id, int value);
|
||||
void lovrShaderSendFloat(Shader* shader, int id, float value);
|
||||
void lovrShaderSendFloatVec2(Shader* shader, int id, int count, float* vector);
|
||||
void lovrShaderSendFloatVec3(Shader* shader, int id, int count,float* vector);
|
||||
void lovrShaderSendFloatVec4(Shader* shader, int id, int count, float* vector);
|
||||
void lovrShaderSendFloatMat2(Shader* shader, int id, float* matrix);
|
||||
void lovrShaderSendFloatMat3(Shader* shader, int id, float* matrix);
|
||||
void lovrShaderSendFloatMat4(Shader* shader, int id, float* matrix);
|
||||
Uniform* lovrShaderGetUniform(Shader* shader, const char* name);
|
||||
void lovrShaderSetFloat(Shader* shader, const char* name, float* data, int count);
|
||||
void lovrShaderSetInt(Shader* shader, const char* name, int* data, int count);
|
||||
void lovrShaderSetMatrix(Shader* shader, const char* name, float* data, int count);
|
||||
void lovrShaderSetTexture(Shader* shader, const char* name, Texture** data, int count);
|
||||
|
|
Loading…
Reference in New Issue