2806 lines
74 KiB
C
2806 lines
74 KiB
C
#include"k3.h"
|
|
|
|
#include"gl.h"
|
|
|
|
#include<stdlib.h>
|
|
#include<string.h>
|
|
#include<stdio.h>
|
|
#include<stdarg.h>
|
|
#include<limits.h>
|
|
#include<assert.h>
|
|
|
|
#include<cglm/vec2.h>
|
|
#include<cglm/frustum.h>
|
|
#include<cglm/cam.h>
|
|
|
|
#include"ssort.h"
|
|
|
|
#define GL_FROM_K3TEX(k3t) ((k3t) ? (k3t)->tex : 0)
|
|
#define GL_FROM_K3MARCHER(k3m) ((GLuint) (uintptr_t) (k3m))
|
|
#define GL_FROM_K3ARBVP(k3m) ((GLuint) (uintptr_t) (k3m))
|
|
#define GL_FROM_K3ARBFP(k3m) ((GLuint) (uintptr_t) (k3m))
|
|
#define GL_FROM_K3GLSL(k3m) ((GLuint) (uintptr_t) (k3m))
|
|
|
|
#define RAST_DIFF 0
|
|
#define RAST_NORM 1
|
|
#define RAST_DISP 2
|
|
#define RAST_LEVELS 3
|
|
|
|
static int k3CPUSkinning = 0;
|
|
|
|
int k3IsCore = 0;
|
|
|
|
static struct k3GLSLP *basicBlitProgram;
|
|
static struct k3GLSLP *basicCubemapProgram;
|
|
|
|
struct k3Tex {
|
|
GLuint tex;
|
|
int cubemap;
|
|
uint32_t szX;
|
|
uint32_t szY;
|
|
uint32_t szZ;
|
|
};
|
|
|
|
struct k3Storage {
|
|
int16_t ref;
|
|
void(*free)(struct k3Storage*);
|
|
};
|
|
|
|
struct k3StorageBasic {
|
|
struct k3Storage;
|
|
GLuint gl;
|
|
};
|
|
|
|
void k3StorageRef(struct k3Storage *s) {
|
|
s->ref++;
|
|
}
|
|
|
|
void k3StorageUnref(struct k3Storage *s) {
|
|
if(--s->ref == 0) {
|
|
s->free(s);
|
|
free(s);
|
|
}
|
|
}
|
|
|
|
static void k3StorageBasicFree(struct k3Storage *s) {
|
|
glDeleteBuffersARB(1, &((struct k3StorageBasic*) s)->gl);
|
|
}
|
|
struct k3StorageBasic *k3StorageBasic() {
|
|
struct k3StorageBasic *ret = calloc(1, sizeof(*ret));
|
|
ret->ref = 1;
|
|
ret->free = k3StorageBasicFree;
|
|
glGenBuffersARB(1, &ret->gl);
|
|
return ret;
|
|
}
|
|
|
|
struct k3Offscreen {
|
|
GLuint fbo;
|
|
struct k3Tex *diffuse;
|
|
struct k3Tex *depth;
|
|
};
|
|
|
|
struct k3Mdl {
|
|
struct {
|
|
vec3 *pos;
|
|
uint8_t *boneids;
|
|
uint16_t *boneweights;
|
|
} cpuSkinning;
|
|
|
|
size_t verts;
|
|
struct k3StorageBasic *vstore;
|
|
struct k3StorageBasic *estore;
|
|
|
|
uint16_t meshCount;
|
|
struct k3Mesh *meshes;
|
|
|
|
size_t boneCount;
|
|
mat4 *invBind;
|
|
uint8_t *boneParents;
|
|
|
|
uint16_t animCount;
|
|
struct k3Animation **anims;
|
|
|
|
vec3 aabb[2];
|
|
|
|
int offV, offN, offC, offU, offB, offT;
|
|
|
|
const char *debugname;
|
|
};
|
|
|
|
static uint16_t MainWidth, MainHeight;
|
|
void k3Resize(uint16_t width, uint16_t height) {
|
|
MainWidth = width;
|
|
MainHeight = height;
|
|
}
|
|
|
|
static float Time;
|
|
void k3SetTime(float t) {
|
|
Time = t;
|
|
}
|
|
|
|
static void update_aabb(struct k3Mdl *mdl, vec3 *pos) {
|
|
glm_vec3_copy(pos[0], mdl->aabb[0]);
|
|
glm_vec3_copy(pos[0], mdl->aabb[1]);
|
|
for(size_t i = 0; i < mdl->verts; i++) {
|
|
glm_vec3_minv(pos[i], mdl->aabb[0], mdl->aabb[0]);
|
|
glm_vec3_maxv(pos[i], mdl->aabb[1], mdl->aabb[1]);
|
|
}
|
|
}
|
|
|
|
static int8_t *generate_tangents(size_t verts, size_t indices, uint16_t *inds, vec3 *pos, int8_t *nrm, vec2 *uvs) {
|
|
vec4 *tans1 = _mm_malloc(verts * sizeof(*tans1), 16);
|
|
vec4 *tans2 = _mm_malloc(verts * sizeof(*tans2), 16);
|
|
|
|
memset(tans1, 0, sizeof(*tans1) * verts);
|
|
memset(tans2, 0, sizeof(*tans2) * verts);
|
|
|
|
bool warningBadTangents = 0;
|
|
|
|
for(size_t i = 0; i < indices; i += 3) {
|
|
vec3 p1, p2, p3;
|
|
glm_vec3_copy(pos[inds[i + 0]], p1);
|
|
glm_vec3_copy(pos[inds[i + 1]], p2);
|
|
glm_vec3_copy(pos[inds[i + 2]], p3);
|
|
|
|
vec2 w1, w2, w3;
|
|
glm_vec2_copy(uvs[inds[i + 0]], w1);
|
|
glm_vec2_copy(uvs[inds[i + 1]], w2);
|
|
glm_vec2_copy(uvs[inds[i + 2]], w3);
|
|
|
|
float x1 = p2[0] - p1[0];
|
|
float x2 = p3[0] - p1[0];
|
|
|
|
float y1 = p2[1] - p1[1];
|
|
float y2 = p3[1] - p1[1];
|
|
|
|
float z1 = p2[2] - p1[2];
|
|
float z2 = p3[2] - p1[2];
|
|
|
|
float s1 = w2[0] - w1[0];
|
|
float s2 = w3[0] - w1[0];
|
|
|
|
float t1 = w2[1] - w1[1];
|
|
float t2 = w3[1] - w1[1];
|
|
|
|
if(s1 * t2 - s2 * t1 == 0 && !warningBadTangents) {
|
|
warningBadTangents = true;
|
|
k3Log(k3_WARN, "Bad tangents. Is mesh properly UV-mapped?");
|
|
}
|
|
|
|
float r = 1.f / (s1 * t2 - s2 * t1);
|
|
|
|
vec3 sdir = {(t2 * x1 - t1 * x2) * r, (t2 * y1 - t1 * y2) * r, (t2 * z1 - t1 * z2) * r};
|
|
vec3 tdir = {(s1 * x2 - s2 * x1) * r, (s1 * y2 - s2 * y1) * r, (s1 * z2 - s2 * z1) * r};
|
|
|
|
glm_vec3_add(tans1[inds[i + 0]], sdir, tans1[inds[i + 0]]);
|
|
glm_vec3_add(tans1[inds[i + 1]], sdir, tans1[inds[i + 1]]);
|
|
glm_vec3_add(tans1[inds[i + 2]], sdir, tans1[inds[i + 2]]);
|
|
|
|
glm_vec3_add(tans2[inds[i + 0]], tdir, tans2[inds[i + 0]]);
|
|
glm_vec3_add(tans2[inds[i + 1]], tdir, tans2[inds[i + 1]]);
|
|
glm_vec3_add(tans2[inds[i + 2]], tdir, tans2[inds[i + 2]]);
|
|
}
|
|
|
|
int8_t *ret = malloc(verts * 3);
|
|
memset(ret, 0, verts * 3);
|
|
|
|
for(size_t i = 0; i < verts; i++) {
|
|
vec3 n = {nrm[i * 3 + 0], nrm[i * 3 + 1], nrm[i * 3 + 2]};
|
|
glm_vec3_normalize(n);
|
|
glm_vec3_scale(n, glm_vec3_dot(n, tans1[i]), n);
|
|
|
|
vec3 t;
|
|
glm_vec3_sub(tans1[i], n, t);
|
|
glm_vec3_normalize(t);
|
|
|
|
ret[i * 3 + 0] = t[0] * 127;
|
|
ret[i * 3 + 1] = t[1] * 127;
|
|
ret[i * 3 + 2] = t[2] * 127;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
struct k3Mdl *k3MdlCreate(size_t verts, size_t indices, size_t boneCount, vec3 *pos, uint8_t *nrm, float *uvs, uint8_t *cols, uint8_t *boneids, uint16_t *boneweights, uint16_t *inds, mat4 *invBind, uint8_t *boneParents) {
|
|
struct k3Mdl *ret = calloc(1, sizeof(*ret));
|
|
ret->verts = verts;
|
|
ret->meshCount = 0;
|
|
ret->meshes = NULL;
|
|
ret->boneCount = boneCount;
|
|
ret->invBind = invBind;
|
|
ret->boneParents = boneParents;
|
|
ret->animCount = 0;
|
|
ret->anims = NULL;
|
|
|
|
if(k3CPUSkinning && pos && boneids && boneweights) {
|
|
ret->cpuSkinning.pos = malloc(sizeof(*ret->cpuSkinning.pos) * verts);
|
|
memcpy(ret->cpuSkinning.pos, pos, sizeof(*ret->cpuSkinning.pos) * verts);
|
|
|
|
ret->cpuSkinning.boneids = malloc(sizeof(*ret->cpuSkinning.boneids) * verts * 4);
|
|
memcpy(ret->cpuSkinning.boneids, boneids, sizeof(*ret->cpuSkinning.boneids) * verts * 4);
|
|
|
|
ret->cpuSkinning.boneweights = malloc(sizeof(*ret->cpuSkinning.boneweights) * verts * 4);
|
|
memcpy(ret->cpuSkinning.boneweights, boneweights, sizeof(*ret->cpuSkinning.boneweights) * verts * 4);
|
|
}
|
|
|
|
ret->vstore = k3StorageBasic();
|
|
ret->estore = k3StorageBasic();
|
|
|
|
glBindBufferARB(GL_ARRAY_BUFFER_ARB, ret->vstore->gl);
|
|
|
|
size_t size = verts * (12 + 3 + 8 + 3);
|
|
if(cols) {
|
|
size += verts * 4;
|
|
}
|
|
if(boneCount) {
|
|
size += verts * (4 + 8);
|
|
}
|
|
|
|
glBufferDataARB(GL_ARRAY_BUFFER_ARB, size, NULL, GL_STREAM_DRAW_ARB);
|
|
|
|
int o = 0;
|
|
|
|
ret->offV = o;
|
|
glBufferSubDataARB(GL_ARRAY_BUFFER_ARB, o, 12 * verts, pos);
|
|
o += 12 * verts;
|
|
|
|
if(nrm) {
|
|
ret->offN = o;
|
|
glBufferSubDataARB(GL_ARRAY_BUFFER_ARB, o, 3 * verts, nrm);
|
|
o += 3 * verts;
|
|
} else ret->offN = -1;
|
|
|
|
if(uvs) {
|
|
ret->offU = o;
|
|
glBufferSubDataARB(GL_ARRAY_BUFFER_ARB, o, 8 * verts, uvs);
|
|
o += 8 * verts;
|
|
} else ret->offU = -1;
|
|
|
|
if(nrm && uvs) {
|
|
ret->offT = o;
|
|
|
|
int8_t *tans = generate_tangents(verts, indices, inds, pos, nrm, uvs);
|
|
glBufferSubDataARB(GL_ARRAY_BUFFER_ARB, o, 3 * verts, tans);
|
|
free(tans);
|
|
|
|
o += 3 * verts;
|
|
} else ret->offT = -1;
|
|
|
|
if(cols) {
|
|
ret->offC = o;
|
|
|
|
glBufferSubDataARB(GL_ARRAY_BUFFER_ARB, o, 4 * verts, cols);
|
|
o += 4 * verts;
|
|
} else ret->offC = -1;
|
|
|
|
if(boneCount) {
|
|
ret->offB = o;
|
|
|
|
glBufferSubDataARB(GL_ARRAY_BUFFER_ARB, o, 4 * verts, boneids);
|
|
o += 4 * verts;
|
|
|
|
glBufferSubDataARB(GL_ARRAY_BUFFER_ARB, o, 8 * verts, boneweights);
|
|
o += 8 * verts;
|
|
} else ret->offB = -1;
|
|
|
|
glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, ret->estore->gl);
|
|
glBufferDataARB(GL_ELEMENT_ARRAY_BUFFER_ARB, indices * sizeof(uint16_t), inds, GL_STATIC_DRAW_ARB);
|
|
|
|
update_aabb(ret, pos);
|
|
|
|
ret->debugname = strdup("Unnamed");
|
|
|
|
return ret;
|
|
}
|
|
|
|
void k3MdlUpdatePos(struct k3Mdl *mdl, vec3 *pos) {
|
|
if(mdl->offV == -1) {
|
|
k3Log(k3_ERR, "Missing vertex position attribute. Cannot update.");
|
|
return;
|
|
}
|
|
|
|
update_aabb(mdl, pos);
|
|
|
|
glBindBufferARB(GL_ARRAY_BUFFER_ARB, mdl->vstore->gl);
|
|
glBufferSubDataARB(GL_ARRAY_BUFFER_ARB, mdl->offV, 12 * mdl->verts, pos);
|
|
}
|
|
|
|
void k3MdlUpdateNrm(struct k3Mdl *mdl, uint8_t *nrm) {
|
|
if(mdl->offN == -1) {
|
|
k3Log(k3_ERR, "Missing vertex normal attribute. Cannot update.");
|
|
return;
|
|
}
|
|
|
|
glBindBufferARB(GL_ARRAY_BUFFER_ARB, mdl->vstore->gl);
|
|
glBufferSubDataARB(GL_ARRAY_BUFFER_ARB, mdl->offN, 3 * mdl->verts, nrm);
|
|
}
|
|
|
|
void k3MdlAddMesh(struct k3Mdl *mdl, struct k3Mat *mat, uint16_t idxStart, uint16_t idxNumber) {
|
|
mdl->meshes = realloc(mdl->meshes, sizeof(*mdl->meshes) * (mdl->meshCount + 1));
|
|
struct k3Mesh *mehs = &mdl->meshes[mdl->meshCount++];
|
|
mehs->idxStart = idxStart;
|
|
mehs->idxNumber = idxNumber;
|
|
memcpy(&mehs->mat, mat, sizeof(*mat));
|
|
}
|
|
|
|
struct k3Mesh *k3MdlGetMeshes(struct k3Mdl *mdl, size_t *count) {
|
|
*count = mdl->meshCount;
|
|
return mdl->meshes;
|
|
}
|
|
|
|
void k3MdlAddAnim(struct k3Mdl *mdl, struct k3Animation *anim) {
|
|
mdl->anims = realloc(mdl->anims, sizeof(*mdl->anims) * (mdl->animCount + 1));
|
|
mdl->anims[mdl->animCount++] = anim;
|
|
}
|
|
|
|
struct k3Animation *k3MdlGetAnim(struct k3Mdl *mdl, uint16_t id) {
|
|
for(size_t i = 0; i < mdl->animCount; i++) {
|
|
if(mdl->anims[i]->id == id) {
|
|
return mdl->anims[i];
|
|
}
|
|
}
|
|
}
|
|
|
|
size_t k3MdlGetBoneCount(struct k3Mdl *mdl) {
|
|
return mdl->boneCount;
|
|
}
|
|
|
|
struct k3Mdl *k3MdlCopySubs(struct k3Mdl *src) {
|
|
struct k3Mdl *dst = calloc(1, sizeof(*dst));
|
|
|
|
dst->cpuSkinning.pos = src->cpuSkinning.pos;
|
|
dst->cpuSkinning.boneids = src->cpuSkinning.boneids;
|
|
dst->cpuSkinning.boneweights = src->cpuSkinning.boneweights;
|
|
|
|
dst->verts = src->verts;
|
|
|
|
dst->vstore = src->vstore;
|
|
dst->estore = src->estore;
|
|
k3StorageRef((void*) dst->vstore);
|
|
k3StorageRef((void*) dst->estore);
|
|
|
|
dst->meshCount = src->meshCount;
|
|
dst->meshes = malloc(sizeof(*dst->meshes) * dst->meshCount);
|
|
memcpy(dst->meshes, src->meshes, sizeof(*dst->meshes) * dst->meshCount);
|
|
|
|
dst->boneCount = src->boneCount;
|
|
dst->invBind = malloc(sizeof(*dst->invBind) * dst->boneCount);
|
|
memcpy(dst->invBind, src->invBind, sizeof(dst->invBind) * dst->boneCount);
|
|
dst->boneParents = malloc(sizeof(*dst->boneParents) * dst->boneCount);
|
|
memcpy(dst->boneParents, src->boneParents, sizeof(*dst->boneParents) * dst->boneCount);
|
|
|
|
dst->animCount = src->animCount;
|
|
dst->anims = malloc(sizeof(*dst->anims) * dst->animCount);
|
|
memcpy(dst->anims, src->anims, sizeof(*dst->anims) * dst->animCount);
|
|
|
|
memcpy(dst->aabb, src->aabb, sizeof(dst->aabb));
|
|
|
|
dst->offB = src->offB;
|
|
dst->offC = src->offC;
|
|
dst->offN = src->offN;
|
|
dst->offT = src->offT;
|
|
dst->offU = src->offU;
|
|
dst->offV = src->offV;
|
|
|
|
dst->debugname = strdup(src->debugname);
|
|
|
|
return dst;
|
|
}
|
|
|
|
void k3MdlSetDebugName(struct k3Mdl *mdl, const char *name) {
|
|
free(mdl->debugname);
|
|
mdl->debugname = strdup(name);
|
|
|
|
k3Log(k3_DEBUG, "Model name: %s", name);
|
|
}
|
|
|
|
static void anim_update(struct k3Animator *anim) {
|
|
float frame = anim->time * anim->base->fps;
|
|
|
|
size_t f0 = floorf(frame);
|
|
size_t f1 = (f0 + 1);
|
|
|
|
float alpha = frame - f0;
|
|
|
|
f0 %= anim->base->frameCount;
|
|
f1 %= anim->base->frameCount;
|
|
|
|
for(size_t b = 0; b < anim->base->boneCount; b++) {
|
|
vec4 t0, t1;
|
|
glm_vec4_copy(anim->base->frames[anim->base->boneCount * f0 + b].translation, t0);
|
|
glm_vec4_copy(anim->base->frames[anim->base->boneCount * f1 + b].translation, t1);
|
|
|
|
versor r0, r1;
|
|
glm_quat_copy(anim->base->frames[anim->base->boneCount * f0 + b].rotation, r0);
|
|
glm_quat_copy(anim->base->frames[anim->base->boneCount * f1 + b].rotation, r1);
|
|
|
|
vec4 t;
|
|
glm_vec4_lerp(t0, t1, alpha, t);
|
|
|
|
versor r;
|
|
glm_quat_slerp(r0, r1, alpha, r);
|
|
|
|
mat4 m = GLM_MAT4_IDENTITY_INIT;
|
|
glm_quat_mat4(r, m);
|
|
glm_vec4_copy(t, m[3]);
|
|
|
|
uint8_t parent = anim->base->boneParents[b];
|
|
if(parent != 0xFF) {
|
|
glm_mat4_mul(anim->inter[parent], m, anim->inter[b]);
|
|
} else {
|
|
glm_mat4_copy(m, anim->inter[b]);
|
|
}
|
|
}
|
|
|
|
for(size_t b = 0; b < anim->base->boneCount; b++) {
|
|
glm_mat4_mul(anim->inter[b], anim->base->invBind[b], anim->inter[b]);
|
|
}
|
|
}
|
|
void k3AnimatorSet(struct k3Animator *anim, float time) {
|
|
if(!anim->inter) anim->inter = _mm_malloc(sizeof(mat4) * anim->base->boneCount * anim->base->frameCount, 16);
|
|
|
|
anim->time = time;
|
|
|
|
anim_update(anim);
|
|
}
|
|
void k3AnimatorStep(struct k3Animator *anim, float dt) {
|
|
k3AnimatorSet(anim, anim->time + dt);
|
|
}
|
|
|
|
|
|
struct k3Tex *k3TexCreate(enum k3TexType type) {
|
|
struct k3Tex *ret = calloc(1, sizeof(*ret));
|
|
|
|
if(type == k3_CUBEMAP) {
|
|
ret->cubemap = 1;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
void k3TexUpdate(struct k3Tex *tex, enum k3TexType type, int index, uint16_t width, uint16_t height, void *data) {
|
|
GLenum target = tex->cubemap ? GL_TEXTURE_CUBE_MAP : GL_TEXTURE_2D;
|
|
|
|
/*
|
|
The Intel 4500MHD driver causes broken textures upon calling glTexImage2D a second time to resize them.
|
|
Specifically, the textures appear to be read as if all zeroes.
|
|
And they return to normal once you come back to the original size!?!
|
|
|
|
For this reason, each k3TexUpdate call should destroy the texture name, and create a new one.
|
|
*/
|
|
if(tex->tex) {
|
|
// Assume cubemaps don't get reuploaded because I'm lazy..
|
|
if(!tex->cubemap) {
|
|
glDeleteTextures(1, &tex->tex);
|
|
tex->tex = 0;
|
|
}
|
|
} else {
|
|
k3Log(k3_INFO, "Init type %i tex (%u,%u,%u)", type, width, height, 0);
|
|
}
|
|
|
|
GLint internalFmt, externalFmt, intype;
|
|
int linearFilter, mipmap;
|
|
|
|
switch(type) {
|
|
case k3_RAWCOLOR:
|
|
internalFmt = GLAD_GL_ARB_texture_float ? GL_RGBA32F_ARB : GL_RGB10_A2;
|
|
externalFmt = GL_RGBA;
|
|
intype = GL_UNSIGNED_BYTE;
|
|
linearFilter = 1;
|
|
mipmap = 1;
|
|
break;
|
|
case k3_NORMAL:
|
|
internalFmt = GL_RGBA;
|
|
externalFmt = GL_RGBA;
|
|
intype = GL_UNSIGNED_BYTE;
|
|
linearFilter = 1;
|
|
mipmap = 1;
|
|
break;
|
|
case k3_DIFFUSE:
|
|
case k3_EMISSION:
|
|
internalFmt = GLAD_GL_EXT_texture_compression_s3tc
|
|
? (GLAD_GL_EXT_texture_sRGB ? GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT : GL_COMPRESSED_RGBA_S3TC_DXT5_EXT)
|
|
: (GLAD_GL_EXT_texture_sRGB ? GL_SRGB8_ALPHA8_EXT : GL_RGBA8);
|
|
externalFmt = GL_RGBA;
|
|
intype = GL_UNSIGNED_BYTE;
|
|
linearFilter = 1;
|
|
mipmap = 1;
|
|
break;
|
|
case k3_DISPLACEMENT:
|
|
case k3_ROUGHNESS:
|
|
internalFmt = !k3IsCore ? GL_LUMINANCE8 : GL_RED;
|
|
externalFmt = GL_RED;
|
|
intype = GL_UNSIGNED_BYTE;
|
|
linearFilter = 1;
|
|
mipmap = 1;
|
|
break;
|
|
case k3_ALPHA:
|
|
internalFmt = !k3IsCore ? GL_ALPHA8 : GL_RED;
|
|
externalFmt = !k3IsCore ? GL_ALPHA : GL_RED;
|
|
intype = GL_UNSIGNED_BYTE;
|
|
linearFilter = 1;
|
|
mipmap = 1;
|
|
break;
|
|
case k3_DEPTH:
|
|
internalFmt = GL_DEPTH_COMPONENT24;
|
|
externalFmt = GL_DEPTH_COMPONENT;
|
|
intype = GL_UNSIGNED_INT;
|
|
if(!k3IsCore) glTexParameteri(target, GL_DEPTH_TEXTURE_MODE, GL_LUMINANCE);
|
|
linearFilter = 1;
|
|
mipmap = 0;
|
|
break;
|
|
default:
|
|
abort();
|
|
break;
|
|
}
|
|
|
|
if(!tex->tex) {
|
|
glGenTextures(1, &tex->tex);
|
|
glBindTexture(target, tex->tex);
|
|
|
|
if(tex->cubemap) {
|
|
glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
|
glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
|
glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
|
glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
|
} else {
|
|
if(!k3IsCore && !mipmap) {
|
|
glTexParameteri(target, GL_GENERATE_MIPMAP, GL_FALSE);
|
|
} else {
|
|
glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
|
}
|
|
glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
|
}
|
|
|
|
if(type == k3_DEPTH) {
|
|
glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, (const float[]) {1, 1, 1, 1});
|
|
glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
|
|
glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
|
|
}
|
|
} else {
|
|
glBindTexture(target, tex->tex);
|
|
}
|
|
|
|
glTexParameteri(target, GL_TEXTURE_MIN_FILTER, linearFilter ? GL_LINEAR : GL_NEAREST);
|
|
glTexParameteri(target, GL_TEXTURE_MAG_FILTER, linearFilter ? GL_LINEAR : GL_NEAREST);
|
|
|
|
if(k3IsCore && type == k3_ALPHA) {
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_R, GL_ONE);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_G, GL_ONE);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_B, GL_ONE);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_A, GL_RED);
|
|
}
|
|
|
|
if(tex->cubemap) {
|
|
target = GL_TEXTURE_CUBE_MAP_POSITIVE_X + index;
|
|
} else if(index) {
|
|
k3Log(k3_WARN, "Non-zero index passed for non-cubemap texture");
|
|
}
|
|
|
|
glTexImage2D(target, 0, internalFmt, width, height, 0, externalFmt, intype, data);
|
|
|
|
if(!tex->cubemap && k3IsCore && mipmap) {
|
|
glGenerateMipmap(target);
|
|
}
|
|
|
|
tex->szX = width;
|
|
tex->szY = height;
|
|
tex->szZ = 0;
|
|
}
|
|
uint32_t k3TexSzX(struct k3Tex *this) {
|
|
return this->szX;
|
|
}
|
|
uint32_t k3TexSzY(struct k3Tex *this) {
|
|
return this->szY;
|
|
}
|
|
uint32_t k3TexSzZ(struct k3Tex *this) {
|
|
return this->szZ;
|
|
}
|
|
|
|
static size_t LightCountOld;
|
|
static size_t LightCount;
|
|
static struct k3Light *Lights;
|
|
void k3SetLights(size_t count, struct k3Light *lightsNew) {
|
|
LightCount = count;
|
|
Lights = lightsNew;
|
|
}
|
|
|
|
struct k3Light *k3GetLights(size_t *a) {
|
|
*a = LightCount;
|
|
return Lights;
|
|
}
|
|
|
|
void k3Clear() {
|
|
glDepthMask(GL_TRUE);
|
|
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
|
|
|
|
glClearColor(0.2, 0.2, 0.2, 1);
|
|
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
|
}
|
|
|
|
uint8_t k3GraphicalReduction;
|
|
|
|
struct Renderable {
|
|
mat4 modelmat;
|
|
struct k3Mdl *mdl;
|
|
struct k3Mesh *mesh;
|
|
struct k3AnimationBone *bones;
|
|
GLhandleARB glsl;
|
|
GLuint arbvp;
|
|
GLuint arbfp;
|
|
} __attribute__((aligned(16)));
|
|
static struct Renderable *renderQueue = NULL;
|
|
static size_t renderQueueSize, renderQueueCapacity = 0;
|
|
|
|
static mat4 CamMat;
|
|
static mat4 ProjMat;
|
|
|
|
struct LightShadow {
|
|
mat4 vp;
|
|
vec4 atlasSegment;
|
|
#ifdef k3_IRREGULAR_SHADOWS
|
|
GLuint multimapEls; // buffer
|
|
GLuint multimapHeads; // image
|
|
#endif
|
|
} __attribute__((aligned(16)));
|
|
static size_t LightShadowsCount, LightShadowsCapacity;
|
|
static struct LightShadow *LightShadows;
|
|
static struct k3Offscreen *ShadowAtlas;
|
|
|
|
static bool LightShadowIrregularMode = false;
|
|
static GLuint IrregPixelsInShadow;
|
|
|
|
static intmax_t rblecompar1(const void *a, const void *b) {
|
|
int i = ((const struct Renderable*) a)->mesh->mat.passes[0].transparent - ((const struct Renderable*) b)->mesh->mat.passes[0].transparent;
|
|
|
|
if(i == 0) {
|
|
float dist2A = glm_vec3_distance2(((const struct Renderable*) a)->modelmat[3], CamMat[3]);
|
|
float dist2B = glm_vec3_distance2(((const struct Renderable*) b)->modelmat[3], CamMat[3]);
|
|
i = copysignf(1, dist2A - dist2B);
|
|
if(((const struct Renderable*) a)->mesh->mat.passes[0].transparent) {
|
|
i = -i;
|
|
}
|
|
}
|
|
|
|
if(i == 0) {
|
|
i = ((const struct Renderable*) a)->glsl - ((const struct Renderable*) b)->glsl;
|
|
}
|
|
|
|
if(i == 0) {
|
|
i = ((const struct Renderable*) a)->arbfp - ((const struct Renderable*) b)->arbfp;
|
|
}
|
|
|
|
if(i == 0) {
|
|
i = ((const struct Renderable*) a)->arbvp - ((const struct Renderable*) b)->arbvp;
|
|
}
|
|
|
|
//if(i == 0) {
|
|
// i = (uintptr_t) ((const struct Renderable*) a)->mesh->mat - (uintptr_t) ((const struct Renderable*) b)->mesh->mat;
|
|
//}
|
|
|
|
return i;
|
|
}
|
|
static void queuesort() {
|
|
ssort(renderQueue, renderQueueSize, sizeof(struct Renderable), rblecompar1);
|
|
}
|
|
|
|
void k3Batch(struct k3Mdl *mdl, mat4 modelmat, struct k3AnimationBone *bones) {
|
|
for(size_t mesh = 0; mesh < mdl->meshCount; mesh++) {
|
|
if(renderQueueSize == renderQueueCapacity) {
|
|
struct Renderable *new = _mm_malloc(sizeof(*renderQueue) * (renderQueueCapacity + 64), 16);
|
|
if(renderQueue) {
|
|
memcpy(new, renderQueue, sizeof(*renderQueue) * renderQueueCapacity);
|
|
_mm_free(renderQueue);
|
|
}
|
|
renderQueue = new;
|
|
renderQueueCapacity += 64;
|
|
}
|
|
struct Renderable *r = &renderQueue[renderQueueSize++];
|
|
r->mdl = mdl;
|
|
r->mesh = &mdl->meshes[mesh];
|
|
glm_mat4_copy(modelmat, r->modelmat);
|
|
r->bones = bones;
|
|
r->glsl = GL_FROM_K3GLSL(r->mesh->mat.passes[0].glsl.hp);
|
|
r->arbvp = GL_FROM_K3ARBVP(r->mesh->mat.passes[0].arbvp.vp);
|
|
r->arbfp = GL_FROM_K3ARBFP(r->mesh->mat.passes[0].arbfp.fp);
|
|
}
|
|
}
|
|
|
|
static void setup_ff_projection(mat4 proj) {
|
|
if(!k3IsCore) {
|
|
glMatrixMode(GL_PROJECTION);
|
|
glLoadIdentity();
|
|
glLoadMatrixf((float*) proj);
|
|
}
|
|
}
|
|
|
|
static void setup_core_projection(GLuint prog, mat4 proj) {
|
|
if(k3IsCore) {
|
|
GLint u = glGetUniformLocation(prog, "u_projection");
|
|
|
|
if(u != -1) {
|
|
glUniformMatrix4fv(u, 1, GL_FALSE, (float*) proj);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void setup_ff_lights(mat4 view, int lightStart, int lightCount) {
|
|
// Some drivers crash on glEnable(GL_LIGHTING) *AND* glDisable(GL_LIGHTING) for some reason
|
|
// Just don't do it unless fixed-function is explicitly on
|
|
|
|
if(!k3IsCore) {
|
|
|
|
static int lightCountOld = 0;
|
|
if(lightCountOld != lightCount) {
|
|
if(lightCount > lightCountOld) {
|
|
for(int i = lightCountOld; i < lightCount; i++) {
|
|
glEnable(GL_LIGHT0 + i);
|
|
}
|
|
} else {
|
|
for(int i = lightCount; i < lightCountOld; i++) {
|
|
glDisable(GL_LIGHT0 + i);
|
|
}
|
|
}
|
|
|
|
lightCountOld = lightCount;
|
|
}
|
|
|
|
glMatrixMode(GL_MODELVIEW);
|
|
glLoadMatrixf((float*) view); // Lighting in eye-space
|
|
if(LightCount)
|
|
glEnable(GL_LIGHTING);
|
|
else
|
|
glDisable(GL_LIGHTING);
|
|
|
|
for(int i = 0; i < lightCount; i++) {
|
|
glLightfv(GL_LIGHT0 + i, GL_POSITION, Lights[lightStart + i].dir.direction);
|
|
glLightfv(GL_LIGHT0 + i, GL_DIFFUSE, Lights[lightStart + i].color);
|
|
|
|
// Fixed-function does not support a light cutoff radius, so approximate, assuming 0.01 is black
|
|
glLightf(GL_LIGHT0 + i, GL_CONSTANT_ATTENUATION, 1);
|
|
glLightf(GL_LIGHT0 + i, GL_LINEAR_ATTENUATION, 0);
|
|
glLightf(GL_LIGHT0 + i, GL_QUADRATIC_ATTENUATION, 1.f / (Lights[lightStart + i].radius * Lights[lightStart + i].radius * 0.01));
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
static void setup_arbprog_globals() {
|
|
if(GLAD_GL_ARB_fragment_program) {
|
|
glProgramEnvParameter4fARB(GL_FRAGMENT_PROGRAM_ARB, 0, Time, 0, 0, 0);
|
|
}
|
|
|
|
if(GLAD_GL_ARB_vertex_program) {
|
|
glProgramEnvParameter4fARB(GL_VERTEX_PROGRAM_ARB, 0, Time, 0, 0, 0);
|
|
}
|
|
}
|
|
|
|
static void setup_glsl_globals(GLuint bound, mat4 view) {
|
|
if(!k3IsCore) {
|
|
glUniform1fARB(glGetUniformLocationARB(bound, "u_time"), Time);
|
|
glUniform3fARB(glGetUniformLocationARB(bound, "u_cam"), CamMat[3][0], CamMat[3][1], CamMat[3][2]);
|
|
glUniformMatrix4fvARB(glGetUniformLocationARB(bound, "u_view"), 1, GL_FALSE, (float*) view);
|
|
} else {
|
|
glUniform1f(glGetUniformLocationARB(bound, "u_time"), Time);
|
|
glUniform3f(glGetUniformLocationARB(bound, "u_cam"), CamMat[3][0], CamMat[3][1], CamMat[3][2]);
|
|
glUniformMatrix4fv(glGetUniformLocationARB(bound, "u_view"), 1, GL_FALSE, (float*) view);
|
|
}
|
|
}
|
|
|
|
static void setup_glsl_mat_uniforms(GLhandleARB bound, struct k3Mat *mat, int pass) {
|
|
for(int u = 0; u < mat->passes[pass].glsl.uCount; u++) {
|
|
if(mat->passes[pass].glsl.u[u].type == k3_MAT_UNIFORM_I1) {
|
|
GLuint id = glGetUniformLocationARB(bound, mat->passes[pass].glsl.u[u].name);
|
|
|
|
if(!k3IsCore) {
|
|
glUniform1iARB(id, mat->passes[pass].glsl.u[u].i1);
|
|
} else {
|
|
glUniform1i(id, mat->passes[pass].glsl.u[u].i1);
|
|
}
|
|
} else if(mat->passes[pass].glsl.u[u].type == k3_MAT_UNIFORM_F1) {
|
|
GLuint id = glGetUniformLocationARB(bound, mat->passes[pass].glsl.u[u].name);
|
|
|
|
if(!k3IsCore) {
|
|
glUniform1fARB(id, mat->passes[pass].glsl.u[u].f1);
|
|
} else {
|
|
glUniform1f(id, mat->passes[pass].glsl.u[u].f1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void setup_glsl_model_uniforms(GLuint bound, float *modelmat) {
|
|
mat4 invmodel;
|
|
glm_mat4_inv(modelmat, invmodel);
|
|
|
|
if(!k3IsCore) {
|
|
glUniformMatrix4fvARB(glGetUniformLocationARB(bound, "u_model"), 1, GL_FALSE, (float*) modelmat);
|
|
glUniformMatrix4fvARB(glGetUniformLocationARB(bound, "u_imodel"), 1, GL_FALSE, (float*) invmodel);
|
|
} else {
|
|
glUniformMatrix4fv(glGetUniformLocationARB(bound, "u_model"), 1, GL_FALSE, (float*) modelmat);
|
|
glUniformMatrix4fv(glGetUniformLocationARB(bound, "u_imodel"), 1, GL_FALSE, (float*) invmodel);
|
|
}
|
|
}
|
|
|
|
static void setup_glsl_lighting_uniforms(GLuint bound, int lightsStart, int lightsCount) {
|
|
if(lightsCount > 4) {
|
|
lightsCount = 4;
|
|
k3Log(k3_ERR, "Max 4 lights per pass");
|
|
}
|
|
|
|
if(lightsStart >= LightCount) {
|
|
lightsStart = 0;
|
|
lightsCount = 0;
|
|
}
|
|
|
|
if(lightsStart + lightsCount > LightCount) {
|
|
lightsCount = LightCount - lightsStart;
|
|
}
|
|
|
|
vec4 settings1[4];
|
|
vec4 settings2[4];
|
|
|
|
vec4 colors[4];
|
|
memset(colors, 0, sizeof(colors));
|
|
|
|
for(int i = 0; i < lightsCount; i++) {
|
|
struct k3Light *l = &Lights[i + lightsStart];
|
|
|
|
if(l->type == k3_DIRECTIONAL) {
|
|
glm_vec4_copy(l->dir.direction, settings1[i]);
|
|
} else if(l->type == k3_SPOT) {
|
|
glm_vec4_copy(l->spot.position, settings1[i]);
|
|
|
|
float inclination = acosf(l->spot.direction[1]);
|
|
float azimuth = atan2f(l->spot.direction[2], l->spot.direction[0]);
|
|
|
|
settings2[i][0] = inclination;
|
|
settings2[i][1] = azimuth;
|
|
settings2[i][2] = cosf(l->spot.angle);
|
|
settings2[i][3] = l->radius;
|
|
} else if(l->type == k3_HALF_OMNI) {
|
|
glm_vec4_copy(l->omni.position, settings1[i]);
|
|
settings1[i][3] = 2;
|
|
|
|
settings2[i][3] = l->radius;
|
|
} else if(l->type == k3_OMNI) {
|
|
glm_vec4_copy(l->omni.position, settings1[i]);
|
|
settings1[i][3] = 3;
|
|
|
|
settings2[i][3] = l->radius;
|
|
}
|
|
|
|
memcpy(colors[i], Lights[lightsStart + i].color, sizeof(vec4));
|
|
}
|
|
|
|
vec4 ambient = {0, 0, 0, 0};
|
|
|
|
if(lightsStart == 0) {
|
|
glm_vec4_copy((vec4) {0.018, 0.018, 0.040, 0}, ambient);
|
|
}
|
|
|
|
if(!k3IsCore) {
|
|
glUniform4fvARB(glGetUniformLocationARB(bound, "u_BaseLightSettings1"), 4, (float*) settings1);
|
|
glUniform4fvARB(glGetUniformLocationARB(bound, "u_BaseLightSettings2"), 4, (float*) settings2);
|
|
glUniform4fvARB(glGetUniformLocationARB(bound, "u_BaseLightColors"), 4, (float*) colors);
|
|
glUniform4fvARB(glGetUniformLocationARB(bound, "u_AmbientLight"), 1, (float*) ambient);
|
|
} else {
|
|
glUniform4fv(glGetUniformLocationARB(bound, "u_BaseLightSettings1"), 4, (float*) settings1);
|
|
glUniform4fv(glGetUniformLocationARB(bound, "u_BaseLightSettings2"), 4, (float*) settings2);
|
|
glUniform4fv(glGetUniformLocationARB(bound, "u_BaseLightColors"), 4, (float*) colors);
|
|
glUniform4fv(glGetUniformLocationARB(bound, "u_AmbientLight"), 1, (float*) ambient);
|
|
}
|
|
}
|
|
|
|
static void setup_glsl_shadow_uniforms(GLuint bound, int atlasUnit, int lightsStart, int lightsCount) {
|
|
if(lightsCount > 4) {
|
|
lightsCount = 4;
|
|
k3Log(k3_ERR, "Max 4 lights per pass");
|
|
}
|
|
|
|
if(!LightShadows) return;
|
|
|
|
if(LightShadowIrregularMode) {
|
|
assert(k3IsCore);
|
|
|
|
glUniform1i(glGetUniformLocation(bound, "u_pixelsinshadow"), 0);
|
|
} else {
|
|
mat4 m[4];
|
|
memset(m, 0, sizeof(m));
|
|
|
|
vec4 seg[4];
|
|
|
|
for(int i = 0; i < lightsCount; i++) {
|
|
glm_mat4_copy(LightShadows[i + lightsStart].vp, m[i]);
|
|
glm_vec4_copy(LightShadows[i + lightsStart].atlasSegment, seg[i]);
|
|
}
|
|
|
|
// Make it look outside the atlas completely
|
|
for(int i = lightsCount; i < 4; i++) {
|
|
seg[i][0] = -1;
|
|
seg[i][1] = -1;
|
|
seg[i][2] = -1;
|
|
seg[i][3] = -1;
|
|
}
|
|
|
|
if(!k3IsCore) {
|
|
glUniformMatrix4fvARB(glGetUniformLocationARB(bound, "u_shadows0vp"), 4, GL_FALSE, (float*) m);
|
|
glUniform4fvARB(glGetUniformLocationARB(bound, "u_shadows0seg"), 4, (float*) seg);
|
|
|
|
glUniform1iARB(glGetUniformLocationARB(bound, "u_shadows0atlas"), atlasUnit);
|
|
} else {
|
|
glUniformMatrix4fv(glGetUniformLocationARB(bound, "u_shadows0vp"), 4, GL_FALSE, (float*) m);
|
|
glUniform4fv(glGetUniformLocationARB(bound, "u_shadows0seg"), 4, (float*) seg);
|
|
|
|
glUniform1i(glGetUniformLocationARB(bound, "u_shadows0atlas"), atlasUnit);
|
|
}
|
|
}
|
|
}
|
|
|
|
static int bind_mat_textures(struct k3Mat *mat, int pass) {
|
|
int i;
|
|
if(GLAD_GL_EXT_direct_state_access) {
|
|
for(i = 0; i < k3_MAX_GLSL_UNITS; i++) {
|
|
glBindMultiTextureEXT(GL_TEXTURE0 + i, GL_TEXTURE_2D, GL_FROM_K3TEX(mat->passes[pass].units[i]));
|
|
}
|
|
} else {
|
|
for(i = 0; i < k3_MAX_GLSL_UNITS; i++) {
|
|
glActiveTexture(GL_TEXTURE0 + i);
|
|
glBindTexture(GL_TEXTURE_2D, GL_FROM_K3TEX(mat->passes[pass].units[i]));
|
|
}
|
|
}
|
|
return i;
|
|
}
|
|
|
|
static int bind_shadow_texture(int textureUnit) {
|
|
if(LightShadowIrregularMode) {
|
|
if(glBindImageTexture) {
|
|
glBindImageTexture(0, IrregPixelsInShadow, 0, GL_FALSE, 0, GL_READ_WRITE_ARB, GL_R32UI);
|
|
} else {
|
|
glBindImageTextureEXT(0, IrregPixelsInShadow, 0, GL_FALSE, 0, GL_READ_WRITE_ARB, GL_R32UI);
|
|
}
|
|
return textureUnit;
|
|
} else {
|
|
if(GLAD_GL_EXT_direct_state_access) {
|
|
glBindMultiTextureEXT(GL_TEXTURE0 + textureUnit, GL_TEXTURE_2D, GL_FROM_K3TEX(ShadowAtlas->depth));
|
|
} else {
|
|
glActiveTexture(GL_TEXTURE0 + textureUnit);
|
|
glBindTexture(GL_TEXTURE_2D, GL_FROM_K3TEX(ShadowAtlas->depth));
|
|
}
|
|
return ++textureUnit;
|
|
}
|
|
}
|
|
|
|
static void enable_glsl_bones(GLuint bound, struct k3Mdl *mdl, struct k3AnimationBone *bones) {
|
|
if(mdl->boneCount) {
|
|
GLint a0;
|
|
GLint a1;
|
|
|
|
if(!k3IsCore) {
|
|
a0 = glGetAttribLocationARB(bound, "a_boneids");
|
|
a1 = glGetAttribLocationARB(bound, "a_boneweights");
|
|
} else {
|
|
a0 = glGetAttribLocation(bound, "a_boneids");
|
|
a1 = glGetAttribLocation(bound, "a_boneweights");
|
|
}
|
|
|
|
if((a0 == -1) != (a1 == -1)) {
|
|
k3Log(k3_ERR, "a_boneids and a_boneweights must be both null or non-null");
|
|
}
|
|
|
|
if(a0 != -1) {
|
|
if(!k3IsCore) {
|
|
glEnableVertexAttribArrayARB(a0);
|
|
glEnableVertexAttribArrayARB(a1);
|
|
glVertexAttribPointerARB(a0, 4, GL_UNSIGNED_BYTE, GL_FALSE, 0, (void*) (mdl->offB + 0 * mdl->verts));
|
|
glVertexAttribPointerARB(a1, 4, GL_UNSIGNED_SHORT, GL_TRUE, 0, (void*) (mdl->offB + 4 * mdl->verts));
|
|
} else {
|
|
glEnableVertexAttribArray(a0);
|
|
glEnableVertexAttribArray(a1);
|
|
glVertexAttribPointer(a0, 4, GL_UNSIGNED_BYTE, GL_FALSE, 0, (void*) (mdl->offB + 0 * mdl->verts));
|
|
glVertexAttribPointer(a1, 4, GL_UNSIGNED_SHORT, GL_TRUE, 0, (void*) (mdl->offB + 4 * mdl->verts));
|
|
}
|
|
}
|
|
|
|
if(bones) {
|
|
if(!k3IsCore) {
|
|
glUniform4fvARB(glGetUniformLocationARB(bound, "u_bonedata"), 2 * mdl->boneCount, (float*) bones);
|
|
} else {
|
|
glUniform4fv(glGetUniformLocationARB(bound, "u_bonedata"), 2 * mdl->boneCount, (float*) bones);
|
|
}
|
|
} else {
|
|
vec4 data[48] = {};
|
|
for(int i = 0; i < 48; i++) {
|
|
data[i][3] = 1;
|
|
}
|
|
|
|
if(!k3IsCore) {
|
|
glUniform4fvARB(glGetUniformLocationARB(bound, "u_bonedata"), 48, (float*) data);
|
|
} else {
|
|
glUniform4fv(glGetUniformLocationARB(bound, "u_bonedata"), 48, (float*) data);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void disable_glsl_bones(struct k3Mdl *mdl, GLuint bound) {
|
|
if(mdl->boneCount) {
|
|
GLint a0;
|
|
GLint a1;
|
|
|
|
a0 = glGetAttribLocationARB(bound, "a_boneids");
|
|
a1 = glGetAttribLocationARB(bound, "a_boneweights");
|
|
|
|
if(a0 != -1) {
|
|
if(!k3IsCore) {
|
|
glDisableVertexAttribArrayARB(a0);
|
|
glDisableVertexAttribArrayARB(a1);
|
|
} else {
|
|
glDisableVertexAttribArray(a0);
|
|
glDisableVertexAttribArray(a1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void enable_glsl_tangents(GLuint bound, struct k3Mdl *mdl) {
|
|
if(mdl->offT == -1) {
|
|
return;
|
|
}
|
|
|
|
GLint a;
|
|
|
|
if(!k3IsCore) {
|
|
a = glGetAttribLocationARB(bound, "a_tangent");
|
|
} else {
|
|
a = glGetAttribLocation(bound, "a_tangent");
|
|
}
|
|
|
|
if(a == -1) {
|
|
return;
|
|
}
|
|
|
|
if(!k3IsCore) {
|
|
glEnableVertexAttribArrayARB(a);
|
|
glVertexAttribPointerARB(a, 3, GL_BYTE, GL_TRUE, 0, (void*) mdl->offT);
|
|
} else {
|
|
glEnableVertexAttribArray(a);
|
|
glVertexAttribPointer(a, 3, GL_BYTE, GL_TRUE, 0, (void*) mdl->offT);
|
|
}
|
|
}
|
|
|
|
static void disable_glsl_tangents(GLuint bound, struct k3Mdl *mdl) {
|
|
if(mdl->offT == -1) {
|
|
return;
|
|
}
|
|
|
|
GLint a;
|
|
|
|
if(!k3IsCore) {
|
|
a = glGetAttribLocationARB(bound, "a_tangent");
|
|
} else {
|
|
a = glGetAttribLocation(bound, "a_tangent");
|
|
}
|
|
|
|
if(a == -1) {
|
|
return;
|
|
}
|
|
|
|
if(!k3IsCore) {
|
|
glDisableVertexAttribArrayARB(a);
|
|
} else {
|
|
glDisableVertexAttribArray(a);
|
|
}
|
|
}
|
|
|
|
static void enable_vertex_buffers(struct k3Mdl *mdl, GLuint prog) {
|
|
if(!k3IsCore) {
|
|
glEnableClientState(GL_VERTEX_ARRAY);
|
|
glEnableClientState(GL_NORMAL_ARRAY);
|
|
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
|
|
|
|
glVertexPointer(3, GL_FLOAT, 0, (void*) (uintptr_t) mdl->offV);
|
|
glNormalPointer(GL_BYTE, 0, (void*) (uintptr_t) mdl->offN);
|
|
glTexCoordPointer(2, GL_FLOAT, 0, (void*) (uintptr_t) mdl->offU);
|
|
|
|
if(mdl->offC != -1) {
|
|
glEnableClientState(GL_COLOR_ARRAY);
|
|
glColorPointer(4, GL_UNSIGNED_BYTE, 0, (void*) (uintptr_t) mdl->offC);
|
|
} else {
|
|
glColor4f(1, 1, 1, 1);
|
|
}
|
|
} else if(prog) {
|
|
GLint aPos = glGetAttribLocation(prog, "a_pos");
|
|
GLint aNormal = glGetAttribLocation(prog, "a_normal");
|
|
GLint aUv = glGetAttribLocation(prog, "a_uv");
|
|
GLint aColor = glGetAttribLocation(prog, "a_color");
|
|
|
|
if(aPos != -1) {
|
|
glEnableVertexAttribArray(aPos);
|
|
glVertexAttribPointer(aPos, 3, GL_FLOAT, GL_FALSE, 0, (void*) (uintptr_t) mdl->offV);
|
|
}
|
|
|
|
if(aNormal != -1) {
|
|
glEnableVertexAttribArray(aNormal);
|
|
glVertexAttribPointer(aNormal, 3, GL_BYTE, GL_TRUE, 0, (void*) (uintptr_t) mdl->offN);
|
|
}
|
|
|
|
if(aUv != -1) {
|
|
glEnableVertexAttribArray(aUv);
|
|
glVertexAttribPointer(aUv, 2, GL_FLOAT, GL_FALSE, 0, (void*) (uintptr_t) mdl->offU);
|
|
}
|
|
|
|
if(mdl->offC != -1 && aColor != -1) {
|
|
glEnableVertexAttribArray(aColor);
|
|
glVertexAttribPointer(aColor, 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, (void*) (uintptr_t) mdl->offC);
|
|
} else if(aColor != -1) {
|
|
glVertexAttrib4f(aColor, 1, 1, 1, 1);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void disable_vertex_buffers(struct k3Mdl *mdl, GLuint prog) {
|
|
if(!k3IsCore) {
|
|
glDisableClientState(GL_VERTEX_ARRAY);
|
|
glDisableClientState(GL_NORMAL_ARRAY);
|
|
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
|
|
|
|
if(mdl->offC != -1) {
|
|
glDisableClientState(GL_COLOR_ARRAY);
|
|
}
|
|
} else if(prog) {
|
|
GLint aPos = glGetAttribLocation(prog, "a_pos");
|
|
GLint aNormal = glGetAttribLocation(prog, "a_normal");
|
|
GLint aUv = glGetAttribLocation(prog, "a_uv");
|
|
GLint aColor = glGetAttribLocation(prog, "a_color");
|
|
|
|
if(aPos != -1) {
|
|
glDisableVertexAttribArray(aPos);
|
|
}
|
|
|
|
if(aNormal != -1) {
|
|
glDisableVertexAttribArray(aNormal);
|
|
}
|
|
|
|
if(aUv != -1) {
|
|
glDisableVertexAttribArray(aUv);
|
|
}
|
|
|
|
if(mdl->offC != -1 && aColor != -1) {
|
|
glDisableVertexAttribArray(aColor);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void push_aabb(struct k3Mat *mat, int pass, struct k3Mdl *mdl) {
|
|
float aabb = mat->passes[pass].aabb;
|
|
|
|
vec3 verts[] = {
|
|
-aabb + mdl->aabb[0][0], -aabb + mdl->aabb[0][1], -aabb + mdl->aabb[0][2],
|
|
-aabb + mdl->aabb[0][0], +aabb + mdl->aabb[1][1], -aabb + mdl->aabb[0][2],
|
|
+aabb + mdl->aabb[1][0], -aabb + mdl->aabb[0][1], -aabb + mdl->aabb[0][2],
|
|
|
|
-aabb + mdl->aabb[0][0], +aabb + mdl->aabb[1][1], -aabb + mdl->aabb[0][2],
|
|
+aabb + mdl->aabb[1][0], +aabb + mdl->aabb[1][1], -aabb + mdl->aabb[0][2],
|
|
+aabb + mdl->aabb[1][0], -aabb + mdl->aabb[0][1], -aabb + mdl->aabb[0][2],
|
|
|
|
-aabb + mdl->aabb[0][0], -aabb + mdl->aabb[0][1], +aabb + mdl->aabb[1][2],
|
|
+aabb + mdl->aabb[1][0], -aabb + mdl->aabb[0][1], +aabb + mdl->aabb[1][2],
|
|
-aabb + mdl->aabb[0][0], +aabb + mdl->aabb[1][1], +aabb + mdl->aabb[1][2],
|
|
|
|
+aabb + mdl->aabb[1][0], -aabb + mdl->aabb[0][1], +aabb + mdl->aabb[1][2],
|
|
+aabb + mdl->aabb[1][0], +aabb + mdl->aabb[1][1], +aabb + mdl->aabb[1][2],
|
|
-aabb + mdl->aabb[0][0], +aabb + mdl->aabb[1][1], +aabb + mdl->aabb[1][2],
|
|
|
|
-aabb + mdl->aabb[0][0], -aabb + mdl->aabb[0][1], +aabb + mdl->aabb[1][2],
|
|
-aabb + mdl->aabb[0][0], +aabb + mdl->aabb[1][1], +aabb + mdl->aabb[1][2],
|
|
-aabb + mdl->aabb[0][0], -aabb + mdl->aabb[0][1], -aabb + mdl->aabb[0][2],
|
|
|
|
-aabb + mdl->aabb[0][0], +aabb + mdl->aabb[1][1], +aabb + mdl->aabb[1][2],
|
|
-aabb + mdl->aabb[0][0], +aabb + mdl->aabb[1][1], -aabb + mdl->aabb[0][2],
|
|
-aabb + mdl->aabb[0][0], -aabb + mdl->aabb[0][1], -aabb + mdl->aabb[0][2],
|
|
|
|
+aabb + mdl->aabb[1][0], -aabb + mdl->aabb[0][1], +aabb + mdl->aabb[1][2],
|
|
+aabb + mdl->aabb[1][0], -aabb + mdl->aabb[0][1], -aabb + mdl->aabb[0][2],
|
|
+aabb + mdl->aabb[1][0], +aabb + mdl->aabb[1][1], +aabb + mdl->aabb[1][2],
|
|
|
|
+aabb + mdl->aabb[1][0], -aabb + mdl->aabb[0][1], -aabb + mdl->aabb[0][2],
|
|
+aabb + mdl->aabb[1][0], +aabb + mdl->aabb[1][1], -aabb + mdl->aabb[0][2],
|
|
+aabb + mdl->aabb[1][0], +aabb + mdl->aabb[1][1], +aabb + mdl->aabb[1][2],
|
|
|
|
-aabb + mdl->aabb[0][0], +aabb + mdl->aabb[1][1], +aabb + mdl->aabb[1][2],
|
|
+aabb + mdl->aabb[1][0], +aabb + mdl->aabb[1][1], +aabb + mdl->aabb[1][2],
|
|
-aabb + mdl->aabb[0][0], +aabb + mdl->aabb[1][1], -aabb + mdl->aabb[0][2],
|
|
|
|
+aabb + mdl->aabb[1][0], +aabb + mdl->aabb[1][1], +aabb + mdl->aabb[1][2],
|
|
+aabb + mdl->aabb[1][0], +aabb + mdl->aabb[1][1], -aabb + mdl->aabb[0][2],
|
|
-aabb + mdl->aabb[0][0], +aabb + mdl->aabb[1][1], -aabb + mdl->aabb[0][2],
|
|
|
|
-aabb + mdl->aabb[0][0], -aabb + mdl->aabb[0][1], +aabb + mdl->aabb[1][2],
|
|
-aabb + mdl->aabb[0][0], -aabb + mdl->aabb[0][1], -aabb + mdl->aabb[0][2],
|
|
+aabb + mdl->aabb[1][0], -aabb + mdl->aabb[0][1], +aabb + mdl->aabb[1][2],
|
|
|
|
-aabb + mdl->aabb[0][0], -aabb + mdl->aabb[0][1], -aabb + mdl->aabb[0][2],
|
|
+aabb + mdl->aabb[1][0], -aabb + mdl->aabb[0][1], -aabb + mdl->aabb[0][2],
|
|
+aabb + mdl->aabb[1][0], -aabb + mdl->aabb[0][1], +aabb + mdl->aabb[1][2],
|
|
};
|
|
|
|
if(!k3IsCore) {
|
|
glBegin(GL_TRIANGLES);
|
|
for(int i = 0; i < sizeof(verts) / sizeof(*verts); i++) {
|
|
glVertex3f(verts[i][0], verts[i][1], verts[i][2]);
|
|
}
|
|
glEnd();
|
|
} else {
|
|
static GLuint vbo;
|
|
if(!vbo) {
|
|
glGenBuffers(1, &vbo);
|
|
|
|
glBindBuffer(GL_ARRAY_BUFFER, vbo);
|
|
glBufferData(GL_ARRAY_BUFFER, sizeof(verts), verts, GL_DYNAMIC_DRAW);
|
|
} else {
|
|
glBindBuffer(GL_ARRAY_BUFFER, vbo);
|
|
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(verts), verts);
|
|
}
|
|
|
|
GLint aPos = glGetAttribLocation(GL_FROM_K3GLSL(mat->passes[0].glsl.hp), "a_pos");
|
|
|
|
glEnableVertexAttribArray(aPos);
|
|
glVertexAttribPointer(aPos, 3, GL_FLOAT, GL_FALSE, 0, (void*) 0);
|
|
|
|
glDrawArrays(GL_TRIANGLES, 0, sizeof(verts) / sizeof(*verts));
|
|
|
|
glDisableVertexAttribArray(aPos);
|
|
}
|
|
}
|
|
|
|
static void apply_cpu_skinning(struct k3Mdl *mdl, struct k3AnimationBone *bones) {
|
|
if(mdl->boneCount == 0) {
|
|
return;
|
|
}
|
|
|
|
vec3 *boned = alloca(mdl->verts * sizeof(*boned));
|
|
|
|
for(size_t i = 0; i < mdl->verts; i++) {
|
|
glm_vec3_zero(boned[i]);
|
|
|
|
for(size_t boner = 0; boner < 4; boner++) {
|
|
int bone = mdl->cpuSkinning.boneids[i * 4 + boner];
|
|
|
|
float weight = mdl->cpuSkinning.boneweights[i * 4 + boner] / 65535.f;
|
|
|
|
vec3 a;
|
|
glm_vec3_scale((float*) bones[bone].rotation, 2 * glm_vec3_dot(mdl->cpuSkinning.pos[i], (float*) bones[bone].rotation), a);
|
|
|
|
vec3 b;
|
|
glm_vec3_scale(mdl->cpuSkinning.pos[i], bones[bone].rotation[3] * bones[bone].rotation[3] - glm_vec3_dot((float*) bones[bone].rotation, (float*) bones[bone].rotation), b);
|
|
|
|
vec3 c;
|
|
glm_vec3_cross((float*) bones[bone].rotation, mdl->cpuSkinning.pos[i], c);
|
|
glm_vec3_scale(c, 2 * bones[bone].rotation[3], c);
|
|
|
|
vec3 v;
|
|
|
|
glm_vec3_add(a, b, v);
|
|
glm_vec3_add(v, c, v);
|
|
|
|
glm_vec3_add(v, (float*) bones[bone].translation, v);
|
|
|
|
glm_vec3_muladds(v, weight, boned[i]);
|
|
}
|
|
}
|
|
|
|
glBufferSubDataARB(GL_ARRAY_BUFFER_ARB, mdl->offV, mdl->verts * sizeof(*boned), boned);
|
|
}
|
|
|
|
static void forward_subpass(mat4 view, int transparent, int lightsStart, int lightsCount, size_t rbleStart, size_t rbleEnd) {
|
|
setup_ff_lights(view, lightsStart, lightsCount);
|
|
|
|
GLhandleARB lastGLSL = -1;
|
|
GLuint lastVP = 0, lastFP = 0;
|
|
struct k3Mat *lastMaterial = NULL;
|
|
int lastAdditive = -1;
|
|
|
|
for(size_t rble = rbleStart; rble < rbleEnd; rble++) {
|
|
struct k3Mdl *mdl = renderQueue[rble].mdl;
|
|
struct k3Mesh *mesh = renderQueue[rble].mesh;
|
|
float *modelmat = (float*) renderQueue[rble].modelmat;
|
|
struct k3AnimationBone *bones = renderQueue[rble].bones;
|
|
GLhandleARB glsl = renderQueue[rble].glsl;
|
|
GLuint arbvp = renderQueue[rble].arbvp;
|
|
GLuint arbfp = renderQueue[rble].arbfp;
|
|
|
|
struct k3Mat *mat = &mesh->mat;
|
|
|
|
if(mat->passes[0].additive && lightsStart != 0) {
|
|
// Additive materials aren't affected by lighting. Do not draw more than once.
|
|
continue;
|
|
}
|
|
|
|
if(mat->passes[0].transparent != transparent) {
|
|
continue;
|
|
}
|
|
|
|
if(lastAdditive != mat->passes[0].additive) {
|
|
if(mat->passes[0].additive) {
|
|
glDepthMask(GL_FALSE);
|
|
glBlendFuncSeparate(GL_ONE, GL_ONE, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
|
} else {
|
|
glDepthMask(GL_TRUE);
|
|
|
|
if(transparent) {
|
|
if(lightsStart == 0) {
|
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
|
} else {
|
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE);
|
|
}
|
|
} else {
|
|
if(lightsStart == 0) {
|
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
|
} else {
|
|
glBlendFunc(GL_ONE, GL_ONE);
|
|
}
|
|
}
|
|
}
|
|
|
|
lastAdditive = mat->passes[0].additive;
|
|
}
|
|
|
|
if(glsl) {
|
|
if(lastGLSL != glsl) {
|
|
if(k3IsCore)
|
|
glUseProgram(glsl);
|
|
else
|
|
glUseProgramObjectARB(glsl);
|
|
|
|
lastGLSL = glsl;
|
|
|
|
setup_glsl_globals(glsl, view);
|
|
}
|
|
|
|
setup_core_projection(glsl, ProjMat);
|
|
|
|
setup_glsl_mat_uniforms(glsl, mat, 0);
|
|
setup_glsl_shadow_uniforms(glsl, mat->passes[0].unitsUsed, lightsStart, lightsCount);
|
|
setup_glsl_lighting_uniforms(glsl, lightsStart, lightsCount);
|
|
|
|
setup_glsl_model_uniforms(glsl, modelmat);
|
|
|
|
if(mat != lastMaterial) {
|
|
bind_mat_textures(mat, 0);
|
|
bind_shadow_texture(mat->passes[0].unitsUsed);
|
|
}
|
|
} else if(!k3IsCore) {
|
|
if(lastGLSL && GLAD_GL_ARB_shading_language_100) {
|
|
glUseProgramObjectARB(0);
|
|
}
|
|
lastGLSL = 0;
|
|
|
|
if(lastVP != arbvp) {
|
|
if(lastVP && !arbvp) {
|
|
glDisable(GL_VERTEX_PROGRAM_ARB);
|
|
} else if(!lastVP && arbvp) {
|
|
glEnable(GL_VERTEX_PROGRAM_ARB);
|
|
}
|
|
|
|
if(arbvp) {
|
|
glBindProgramARB(GL_VERTEX_PROGRAM_ARB, arbvp);
|
|
}
|
|
|
|
lastVP = arbvp;
|
|
}
|
|
|
|
if(lastFP != arbfp) {
|
|
if(lastFP && !arbfp) {
|
|
glDisable(GL_FRAGMENT_PROGRAM_ARB);
|
|
} else if(!lastFP && arbfp) {
|
|
glEnable(GL_FRAGMENT_PROGRAM_ARB);
|
|
}
|
|
|
|
if(arbfp) {
|
|
glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, arbfp);
|
|
|
|
if(mat != lastMaterial) {
|
|
bind_mat_textures(mat, 0);
|
|
}
|
|
}
|
|
|
|
lastFP = arbfp;
|
|
}
|
|
|
|
if(!k3IsCore && !arbfp) {
|
|
glActiveTexture(GL_TEXTURE0);
|
|
if(mat->passes[0].units[0]) {
|
|
glEnable(GL_TEXTURE_2D);
|
|
glBindTexture(GL_TEXTURE_2D, GL_FROM_K3TEX(mat->passes[0].units[0]));
|
|
glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, (float[]) {1, 1, 1, 1});
|
|
} else {
|
|
glDisable(GL_TEXTURE_2D);
|
|
glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat->primitive.diffuse);
|
|
}
|
|
glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, mat->primitive.specular);
|
|
glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, mat->primitive.emission);
|
|
glMateriali(GL_FRONT_AND_BACK, GL_SHININESS, mat->primitive.shininess * 128);
|
|
}
|
|
}
|
|
|
|
lastMaterial = mat;
|
|
|
|
if(mat->passes[0].nocull) {
|
|
glDisable(GL_CULL_FACE);
|
|
} else {
|
|
glEnable(GL_CULL_FACE);
|
|
}
|
|
if(!k3IsCore) {
|
|
if(mat->passes[0].alphatest) {
|
|
glEnable(GL_ALPHA_TEST);
|
|
glAlphaFunc(GL_GREATER, 0.9f);
|
|
} else {
|
|
glDisable(GL_ALPHA_TEST);
|
|
}
|
|
}
|
|
|
|
if(!k3IsCore) {
|
|
glMatrixMode(GL_MODELVIEW);
|
|
mat4 modelview;
|
|
glm_mat4_mul(view, modelmat, modelview);
|
|
glLoadMatrixf((float*) modelview);
|
|
}
|
|
|
|
glBindBufferARB(GL_ARRAY_BUFFER_ARB, mdl->vstore->gl);
|
|
glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, mdl->estore->gl);
|
|
|
|
if(glsl) {
|
|
enable_glsl_bones(glsl, mdl, bones);
|
|
enable_glsl_tangents(glsl, mdl);
|
|
}
|
|
|
|
if(k3CPUSkinning) {
|
|
apply_cpu_skinning(mdl, bones);
|
|
}
|
|
|
|
if(isnanf(mat->passes[0].aabb)) {
|
|
enable_vertex_buffers(mdl, glsl);
|
|
|
|
glDrawElements(GL_TRIANGLES, mesh->idxNumber, GL_UNSIGNED_SHORT, (void*) (mesh->idxStart * 2));
|
|
|
|
disable_vertex_buffers(mdl, glsl);
|
|
} else {
|
|
push_aabb(mat, 0, mdl);
|
|
}
|
|
|
|
if(glsl) {
|
|
disable_glsl_tangents(glsl, mdl);
|
|
disable_glsl_bones(mdl, glsl);
|
|
}
|
|
}
|
|
}
|
|
|
|
void k3PassForward(mat4 projection, mat4 cam) {
|
|
glm_mat4_copy(cam, CamMat);
|
|
glm_mat4_copy(projection, ProjMat);
|
|
queuesort();
|
|
|
|
mat4 view;
|
|
glm_mat4_inv_fast(cam, view);
|
|
|
|
setup_ff_projection(projection);
|
|
|
|
setup_arbprog_globals();
|
|
|
|
glEnable(GL_DEPTH_TEST);
|
|
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
|
|
glDepthMask(GL_TRUE);
|
|
|
|
glDepthFunc(GL_LEQUAL);
|
|
if(GLAD_GL_EXT_framebuffer_sRGB) {
|
|
glEnable(GL_FRAMEBUFFER_SRGB_EXT);
|
|
}
|
|
glEnable(GL_BLEND);
|
|
glEnable(GL_MULTISAMPLE);
|
|
|
|
glEnable(GL_CULL_FACE);
|
|
glFrontFace(GL_CCW);
|
|
|
|
if(!k3IsCore) {
|
|
glEnable(GL_NORMALIZE);
|
|
}
|
|
|
|
int l = 0, k = LightCount;
|
|
while(1) {
|
|
forward_subpass(view, 0, l, k > 4 ? 4 : k, 0, renderQueueSize);
|
|
|
|
l += 4;
|
|
k -= 4;
|
|
|
|
if(k <= 0) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Horribly inefficient.
|
|
for(size_t rble = 0; rble < renderQueueSize; rble++) {
|
|
l = 0, k = LightCount;
|
|
while(1) {
|
|
forward_subpass(view, 1, l, k > 4 ? 4 : k, rble, rble + 1);
|
|
|
|
l += 4;
|
|
k -= 4;
|
|
|
|
if(k <= 0) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void k3PassDepthOnly(mat4 projection, mat4 cam, int clear, int cull) {
|
|
glm_mat4_copy(cam, CamMat);
|
|
glm_mat4_copy(projection, ProjMat);
|
|
queuesort();
|
|
|
|
mat4 view;
|
|
glm_mat4_inv_fast(cam, view);
|
|
|
|
setup_ff_projection(projection);
|
|
|
|
setup_arbprog_globals();
|
|
|
|
glEnable(GL_DEPTH_TEST);
|
|
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
|
|
glDepthMask(GL_TRUE);
|
|
glDepthFunc(GL_LESS);
|
|
|
|
if(clear) {
|
|
glClear(GL_DEPTH_BUFFER_BIT);
|
|
}
|
|
|
|
if(GLAD_GL_ARB_fragment_program)
|
|
glDisable(GL_FRAGMENT_PROGRAM_ARB);
|
|
if(GLAD_GL_ARB_vertex_program)
|
|
glDisable(GL_VERTEX_PROGRAM_ARB);
|
|
|
|
glFrontFace(GL_CCW);
|
|
if(cull) {
|
|
glEnable(GL_CULL_FACE);
|
|
} else {
|
|
glDisable(GL_CULL_FACE);
|
|
}
|
|
|
|
GLuint lastVP = 0;
|
|
GLhandleARB lastGLSL = -1;
|
|
|
|
for(size_t rble = 0; rble < renderQueueSize; rble++) {
|
|
struct k3Mdl *mdl = renderQueue[rble].mdl;
|
|
struct k3Mesh *mesh = renderQueue[rble].mesh;
|
|
float *modelmat = (float*) renderQueue[rble].modelmat;
|
|
struct k3Mat *mat = &mesh->mat;
|
|
struct k3AnimationBone *bones = renderQueue[rble].bones;
|
|
|
|
if(mat->passes[0].additive || mat->passes[0].transparent) {
|
|
continue;
|
|
}
|
|
|
|
GLhandleARB glsl = renderQueue[rble].glsl;
|
|
GLuint arbvp = renderQueue[rble].arbvp;
|
|
|
|
if(glsl) {
|
|
if(lastGLSL != glsl) {
|
|
if(k3IsCore)
|
|
glUseProgram(glsl);
|
|
else
|
|
glUseProgramObjectARB(glsl);
|
|
|
|
lastGLSL = glsl;
|
|
|
|
setup_glsl_globals(glsl, view);
|
|
}
|
|
|
|
setup_core_projection(glsl, projection);
|
|
|
|
setup_glsl_mat_uniforms(glsl, mat, 0);
|
|
|
|
setup_glsl_model_uniforms(glsl, modelmat);
|
|
} else if(!k3IsCore) {
|
|
if(lastGLSL && GLAD_GL_ARB_shading_language_100) {
|
|
if(k3IsCore)
|
|
glUseProgram(0);
|
|
else
|
|
glUseProgramObjectARB(0);
|
|
}
|
|
lastGLSL = 0;
|
|
|
|
if(arbvp != lastVP) {
|
|
if(arbvp && !lastVP) {
|
|
glEnable(GL_VERTEX_PROGRAM_ARB);
|
|
} else if(!arbvp && lastVP) {
|
|
glDisable(GL_VERTEX_PROGRAM_ARB);
|
|
}
|
|
|
|
if(arbvp) {
|
|
glBindProgramARB(GL_VERTEX_PROGRAM_ARB, arbvp);
|
|
}
|
|
|
|
lastVP = arbvp;
|
|
}
|
|
}
|
|
|
|
if(!k3IsCore) {
|
|
glMatrixMode(GL_MODELVIEW);
|
|
mat4 modelview;
|
|
glm_mat4_mul(view, modelmat, modelview);
|
|
glLoadMatrixf((float*) modelview);
|
|
}
|
|
|
|
glBindBufferARB(GL_ARRAY_BUFFER_ARB, mdl->vstore->gl);
|
|
glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, mdl->estore->gl);
|
|
|
|
if(glsl) {
|
|
enable_glsl_bones(glsl, mdl, bones);
|
|
}
|
|
|
|
enable_vertex_buffers(mdl, glsl);
|
|
|
|
if(k3CPUSkinning) {
|
|
apply_cpu_skinning(mdl, bones);
|
|
}
|
|
|
|
if(isnanf(mat->passes[0].aabb)) {
|
|
enable_vertex_buffers(mdl, glsl);
|
|
|
|
glDrawElements(GL_TRIANGLES, mesh->idxNumber, GL_UNSIGNED_SHORT, (void*) (mesh->idxStart * 2));
|
|
|
|
disable_vertex_buffers(mdl, glsl);
|
|
} else {
|
|
push_aabb(mat, 0, mdl);
|
|
}
|
|
|
|
if(glsl) {
|
|
disable_glsl_bones(mdl, glsl);
|
|
}
|
|
}
|
|
|
|
glFrontFace(GL_CCW);
|
|
}
|
|
|
|
static size_t compute_light_views(mat4 **_projs, mat4 **_cams, char **_isCube) {
|
|
mat4 *projs = _mm_malloc(sizeof(*projs) * LightCount * 6, 16);
|
|
mat4 *cams = _mm_malloc(sizeof(*cams) * LightCount * 6, 16);
|
|
char *isCube = _mm_malloc(sizeof(*isCube) * LightCount * 6, 1);
|
|
|
|
size_t s = 0;
|
|
|
|
for(int i = 0; i < LightCount; i++) {
|
|
isCube[s] = 0;
|
|
|
|
struct k3Light *l = &Lights[i];
|
|
|
|
/*if(!l->castShadow) {
|
|
continue;
|
|
}*/
|
|
|
|
if(l->type == k3_DIRECTIONAL) {
|
|
glm_ortho(-60, 60, -60, 60, -60, 60, projs[s]);
|
|
|
|
mat4 invmainproj;
|
|
glm_mat4_inv_fast(ProjMat, invmainproj);
|
|
|
|
mat4 frustummat;
|
|
glm_mat4_mul(CamMat, invmainproj, frustummat);
|
|
|
|
vec4 corners[8];
|
|
glm_frustum_corners(frustummat, corners);
|
|
|
|
vec4 viewcenter;
|
|
glm_frustum_center(corners, viewcenter);
|
|
|
|
mat4 view;
|
|
glm_look_anyup(viewcenter, l->dir.direction, view);
|
|
|
|
glm_mat4_inv_fast(view, cams[s]);
|
|
|
|
s++;
|
|
} else if(l->type == k3_SPOT || l->type == k3_HALF_OMNI) {
|
|
glm_perspective(l->spot.angle, 1, 0.1, l->radius, projs[s]);
|
|
|
|
mat4 view;
|
|
glm_look_anyup(l->spot.position, l->spot.direction, view);
|
|
|
|
glm_mat4_inv_fast(view, cams[s]);
|
|
|
|
s++;
|
|
} else if(l->type == k3_OMNI) {
|
|
static const vec3 dirs[] = {{0, 0, -1}, {0, 0, 1}, {-1, 0, 0}, {1, 0, 0}, {0, -1, 0}, {0, 1, 0}};
|
|
static const vec3 ups[] = {{0, 1, 0}, {0, 1, 0}, {0, 1, 0}, {0, 1, 0}, {0, 0, 1}, {0, 0, -1}};
|
|
|
|
for(int d = 0; d < 6; d++) {
|
|
glm_perspective(glm_rad(120), 1, 0.1, l->radius, projs[s]);
|
|
|
|
mat4 view;
|
|
glm_look(l->omni.position, dirs[d], ups[d], view);
|
|
|
|
glm_mat4_inv_fast(view, cams[s]);
|
|
|
|
isCube[s] = d != 0;
|
|
|
|
s++;
|
|
}
|
|
}
|
|
}
|
|
|
|
*_projs = projs;
|
|
*_cams = cams;
|
|
*_isCube = isCube;
|
|
|
|
return s;
|
|
}
|
|
|
|
#ifdef k3_IRREGULAR_SHADOWS
|
|
static void pass_irregular(int passnum, mat4 mainproj, mat4 maincam, mat4 lightproj, mat4 lightcam, int lightshadow, int layer) {
|
|
glm_mat4_copy(passnum == 1 ? maincam : lightcam, CamMat);
|
|
glm_mat4_copy(passnum == 1 ? mainproj : lightproj, ProjMat);
|
|
queuesort();
|
|
|
|
mat4 mainview;
|
|
glm_mat4_inv_fast(maincam, mainview);
|
|
|
|
mat4 lightview;
|
|
glm_mat4_inv_fast(lightcam, lightview);
|
|
|
|
mat4 lightvp;
|
|
glm_mat4_mul(lightproj, lightview, lightvp);
|
|
|
|
setup_ff_projection(passnum == 1 ? mainproj : lightproj);
|
|
|
|
setup_arbprog_globals();
|
|
|
|
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
|
|
|
|
// For debugging
|
|
if(layer == 4) {
|
|
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
|
|
}
|
|
|
|
glEnable(GL_DEPTH_TEST);
|
|
|
|
if(passnum == 1) {
|
|
glDepthMask(GL_FALSE);
|
|
glDepthFunc(GL_EQUAL);
|
|
glEnable(GL_CULL_FACE);
|
|
} else {
|
|
glDepthMask(GL_FALSE);
|
|
glDepthFunc(GL_LEQUAL);
|
|
glDisable(GL_CULL_FACE);
|
|
}
|
|
|
|
glFrontFace(GL_CCW);
|
|
|
|
if(GLAD_GL_ARB_fragment_program)
|
|
glDisable(GL_FRAGMENT_PROGRAM_ARB);
|
|
if(GLAD_GL_ARB_vertex_program)
|
|
glDisable(GL_VERTEX_PROGRAM_ARB);
|
|
|
|
GLuint lastVP = 0;
|
|
GLhandleARB lastGLSL = -1;
|
|
|
|
for(size_t rble = 0; rble < renderQueueSize; rble++) {
|
|
struct k3Mdl *mdl = renderQueue[rble].mdl;
|
|
struct k3Mesh *mesh = renderQueue[rble].mesh;
|
|
float *modelmat = (float*) renderQueue[rble].modelmat;
|
|
struct k3Mat *mat = &mesh->mat;
|
|
struct k3AnimationBone *bones = renderQueue[rble].bones;
|
|
|
|
if(mat->passes[0].additive || mat->passes[0].transparent) {
|
|
continue;
|
|
}
|
|
|
|
GLhandleARB glsl;
|
|
|
|
if(passnum == 1) {
|
|
glsl = GL_FROM_K3GLSL(mat->passes[0].glsl.hpIrreg1);
|
|
} else if(passnum == 2) {
|
|
glsl = GL_FROM_K3GLSL(mat->passes[0].glsl.hpIrreg2);
|
|
}
|
|
|
|
if(glsl) {
|
|
if(lastGLSL != glsl) {
|
|
if(k3IsCore)
|
|
glUseProgram(glsl);
|
|
else
|
|
glUseProgramObjectARB(glsl);
|
|
|
|
lastGLSL = glsl;
|
|
|
|
setup_glsl_globals(glsl, passnum == 1 ? mainview : lightview);
|
|
}
|
|
|
|
setup_core_projection(glsl, passnum == 1 ? mainproj : lightproj);
|
|
|
|
setup_glsl_mat_uniforms(glsl, mat, 0);
|
|
|
|
glBindBufferBaseEXT(GL_SHADER_STORAGE_BUFFER, 0, LightShadows[lightshadow].multimapEls);
|
|
|
|
if(passnum == 2) {
|
|
glUniform1iARB(glGetUniformLocationARB(glsl, "u_lightnum"), lightshadow);
|
|
}
|
|
|
|
if(glBindImageTexture) {
|
|
glBindImageTexture(0, LightShadows[lightshadow].multimapHeads, 0, GL_FALSE, layer, GL_READ_WRITE_ARB, GL_R32UI);
|
|
glBindImageTexture(1, IrregPixelsInShadow, 0, GL_FALSE, 0, GL_READ_WRITE_ARB, GL_R32UI);
|
|
} else {
|
|
glBindImageTextureEXT(0, LightShadows[lightshadow].multimapHeads, 0, GL_FALSE, layer, GL_READ_WRITE_ARB, GL_R32UI);
|
|
glBindImageTextureEXT(1, IrregPixelsInShadow, 0, GL_FALSE, 0, GL_READ_WRITE_ARB, GL_R32UI);
|
|
}
|
|
|
|
glUniformMatrix4fvARB(glGetUniformLocationARB(glsl, "u_lightvp"), 1, GL_FALSE, (float*) lightvp);
|
|
|
|
setup_glsl_model_uniforms(glsl, modelmat);
|
|
} else {
|
|
k3Log(k3_WARN, "Missing irregular shadow pass %i shader", passnum);
|
|
continue;
|
|
}
|
|
|
|
if(!k3IsCore) {
|
|
glMatrixMode(GL_MODELVIEW);
|
|
mat4 modelview;
|
|
glm_mat4_mul(passnum == 1 ? mainview : lightview, modelmat, modelview);
|
|
glLoadMatrixf((float*) modelview);
|
|
}
|
|
|
|
glBindBufferARB(GL_ARRAY_BUFFER_ARB, mdl->vstore->gl);
|
|
glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, mdl->estore->gl);
|
|
|
|
if(glsl) {
|
|
enable_glsl_bones(glsl, mdl, bones);
|
|
}
|
|
|
|
enable_vertex_buffers(mdl, glsl);
|
|
|
|
if(k3CPUSkinning) {
|
|
apply_cpu_skinning(mdl, bones);
|
|
}
|
|
|
|
if(isnanf(mat->passes[0].aabb)) {
|
|
enable_vertex_buffers(mdl, glsl);
|
|
|
|
glDrawElements(GL_TRIANGLES, mesh->idxNumber, GL_UNSIGNED_SHORT, (void*) (mesh->idxStart * 2));
|
|
|
|
disable_vertex_buffers(mdl, glsl);
|
|
} else {
|
|
push_aabb(mat, 0, mdl);
|
|
}
|
|
|
|
if(glsl) {
|
|
disable_glsl_bones(mdl, glsl);
|
|
}
|
|
}
|
|
}
|
|
|
|
#define MULTIMAP_HEADS_SIZE 64
|
|
|
|
void k3PassIrregular(struct k3Offscreen *mainview, mat4 mainproj, mat4 maincam) {
|
|
static size_t cachedMainViewResolution = 0;
|
|
|
|
size_t mainViewWidth = mainview ? k3TexSzX(mainview->diffuse) : MainWidth;
|
|
size_t mainViewHeight = mainview ? k3TexSzY(mainview->diffuse) : MainHeight;
|
|
size_t mainViewResolution = mainViewWidth * mainViewHeight;
|
|
|
|
if(!IrregPixelsInShadow || cachedMainViewResolution != mainViewResolution) {
|
|
if(!IrregPixelsInShadow) {
|
|
glGenTextures(1, &IrregPixelsInShadow);
|
|
}
|
|
|
|
glBindTexture(GL_TEXTURE_2D, IrregPixelsInShadow);
|
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_R32UI, mainViewWidth, mainViewHeight, 0, GL_RED_INTEGER_EXT, GL_UNSIGNED_INT, NULL);
|
|
}
|
|
|
|
mat4 *projs;
|
|
mat4 *cams;
|
|
char *isCube;
|
|
|
|
size_t count = compute_light_views(&projs, &cams, &isCube);
|
|
|
|
LightShadowsCount = count;
|
|
if(LightShadowsCount > LightShadowsCapacity) {
|
|
_mm_free(LightShadows);
|
|
|
|
LightShadows = _mm_malloc(sizeof(*LightShadows) * LightShadowsCount, 16);
|
|
memset(LightShadows, 0, sizeof(*LightShadows) * LightShadowsCount);
|
|
LightShadowsCapacity = LightShadowsCount;
|
|
}
|
|
|
|
LightShadowIrregularMode = true;
|
|
|
|
int ls = -1, layer = 0;
|
|
|
|
// TODO: document :)
|
|
for(int lv = 0; lv < count; lv++) {
|
|
if(isCube[lv]) {
|
|
layer++;
|
|
} else {
|
|
layer = 0;
|
|
ls++;
|
|
}
|
|
|
|
if(!isCube[lv]) {
|
|
if(!LightShadows[ls].multimapEls || cachedMainViewResolution != mainViewResolution) {
|
|
if(!LightShadows[ls].multimapEls) {
|
|
glGenBuffersARB(1, &LightShadows[ls].multimapEls);
|
|
}
|
|
glBindBufferARB(GL_SHADER_STORAGE_BUFFER, LightShadows[ls].multimapEls);
|
|
glBufferDataARB(GL_SHADER_STORAGE_BUFFER, 4 + 24 * (1500000), NULL, GL_STREAM_DRAW_ARB);
|
|
}
|
|
|
|
if(!LightShadows[ls].multimapHeads) {
|
|
glGenTextures(1, &LightShadows[ls].multimapHeads);
|
|
glBindTexture(GL_TEXTURE_2D_ARRAY_EXT, LightShadows[ls].multimapHeads);
|
|
glTexImage3D(GL_TEXTURE_2D_ARRAY_EXT, 0, GL_R32UI, MULTIMAP_HEADS_SIZE, MULTIMAP_HEADS_SIZE, 6, 0, GL_RED_INTEGER_EXT, GL_UNSIGNED_INT, NULL);
|
|
}
|
|
|
|
// Set multimap heads to all -1, to signify end of linked lists
|
|
glBindTexture(GL_TEXTURE_2D_ARRAY_EXT, LightShadows[ls].multimapHeads);
|
|
void *allones = malloc(MULTIMAP_HEADS_SIZE * MULTIMAP_HEADS_SIZE * sizeof(GLuint) * 6);
|
|
memset(allones, 0xFF, sizeof(GLuint) * MULTIMAP_HEADS_SIZE * MULTIMAP_HEADS_SIZE * 6);
|
|
glTexSubImage3D(GL_TEXTURE_2D_ARRAY_EXT, 0, 0, 0, 0, MULTIMAP_HEADS_SIZE, MULTIMAP_HEADS_SIZE, 6, GL_RED_INTEGER_EXT, GL_UNSIGNED_INT, allones);
|
|
free(allones);
|
|
|
|
// Set next free index to 0
|
|
glBindBufferARB(GL_SHADER_STORAGE_BUFFER, LightShadows[ls].multimapEls);
|
|
glBufferSubDataARB(GL_SHADER_STORAGE_BUFFER, 0, sizeof(GLuint), &(GLuint) {0});
|
|
}
|
|
|
|
pass_irregular(1, mainproj, maincam, projs[lv], cams[lv], ls, layer);
|
|
}
|
|
|
|
cachedMainViewResolution = mainViewResolution;
|
|
|
|
glMemoryBarrier(GL_ALL_BARRIER_BITS);
|
|
|
|
glViewport(0, 0, MULTIMAP_HEADS_SIZE, MULTIMAP_HEADS_SIZE);
|
|
|
|
if(GLAD_GL_INTEL_conservative_rasterization) {
|
|
glEnable(GL_CONSERVATIVE_RASTERIZATION_INTEL);
|
|
} else if(GLAD_GL_NV_conservative_raster) {
|
|
glEnable(GL_CONSERVATIVE_RASTERIZATION_NV);
|
|
}
|
|
|
|
ls = -1, layer = 0;
|
|
for(int lv = 0; lv < count; lv++) {
|
|
if(isCube[lv]) {
|
|
layer++;
|
|
} else {
|
|
layer = 0;
|
|
ls++;
|
|
}
|
|
|
|
pass_irregular(2, mainproj, maincam, projs[lv], cams[lv], ls, layer);
|
|
}
|
|
|
|
_mm_free(projs);
|
|
_mm_free(cams);
|
|
_mm_free(isCube);
|
|
|
|
if(GLAD_GL_INTEL_conservative_rasterization) {
|
|
glDisable(GL_CONSERVATIVE_RASTERIZATION_INTEL);
|
|
} else if(GLAD_GL_NV_conservative_raster) {
|
|
glDisable(GL_CONSERVATIVE_RASTERIZATION_NV);
|
|
}
|
|
|
|
glViewport(0, 0, mainViewWidth, mainViewHeight);
|
|
|
|
glMemoryBarrier(GL_ALL_BARRIER_BITS);
|
|
}
|
|
#else
|
|
void k3PassIrregular(struct k3Offscreen *mainview, mat4 mainproj, mat4 maincam) {
|
|
k3Log(k3_WARN, "k3_IRREGULAR_SHADOWS is off");
|
|
}
|
|
#endif
|
|
|
|
// Constructs shadowmap atlas, saves `offscr` for own use
|
|
void k3PassShadowmap(mat4 projection, mat4 cam, struct k3Offscreen *offscr) {
|
|
glm_mat4_copy(projection, ProjMat);
|
|
glm_mat4_copy(cam, CamMat);
|
|
|
|
mat4 *projs;
|
|
mat4 *cams;
|
|
char *isCube;
|
|
|
|
size_t count = compute_light_views(&projs, &cams, &isCube);
|
|
|
|
ShadowAtlas = offscr;
|
|
|
|
LightShadowIrregularMode = false;
|
|
|
|
LightShadowsCount = LightCount;
|
|
if(LightShadowsCount > LightShadowsCapacity) {
|
|
_mm_free(LightShadows);
|
|
|
|
LightShadows = _mm_malloc(sizeof(*LightShadows) * LightShadowsCount, 16);
|
|
memset(LightShadows, 0, sizeof(*LightShadows) * LightShadowsCount);
|
|
LightShadowsCapacity = LightShadowsCount;
|
|
}
|
|
|
|
if(count == 0) {
|
|
return;
|
|
}
|
|
|
|
if(k3TexSzX(offscr->depth) != k3TexSzY(offscr->depth)) {
|
|
k3Log(k3_ERR, "Square texture expected for shadow map atlas");
|
|
return;
|
|
}
|
|
|
|
int cellsPerDimension = 0;
|
|
|
|
if(count == 1) {
|
|
cellsPerDimension = 1;
|
|
} else {
|
|
int cellsTotalLog = 1;
|
|
while((1 << cellsTotalLog) < count) {
|
|
cellsTotalLog++;
|
|
}
|
|
cellsPerDimension = 1 << ((cellsTotalLog + 1) / 2);
|
|
}
|
|
|
|
uint16_t sz = k3TexSzX(offscr->depth);
|
|
uint16_t cellSz = sz / cellsPerDimension;
|
|
|
|
size_t s = 0;
|
|
|
|
k3BeginOffscreen(offscr);
|
|
for(size_t i = 0; i < count; i++) {
|
|
int cellX = i % cellsPerDimension;
|
|
int cellY = i / cellsPerDimension;
|
|
|
|
int vp[] = {cellX * cellSz, cellY * cellSz, cellSz, cellSz};
|
|
|
|
mat4 view = GLM_MAT4_IDENTITY_INIT;
|
|
glm_mat4_inv(cams[i], view);
|
|
|
|
if(!isCube[i]) {
|
|
glm_mat4_mul(projs[i], view, LightShadows[s].vp);
|
|
glm_vec4_copy((vec4) {i, (float) cellSz / sz, 0, 0}, LightShadows[s].atlasSegment);
|
|
s++;
|
|
}
|
|
|
|
glViewport(vp[0], vp[1], vp[2], vp[3]);
|
|
k3PassDepthOnly(projs[i], cams[i], i == 0, false);
|
|
}
|
|
k3EndOffscreen(offscr);
|
|
|
|
_mm_free(projs);
|
|
_mm_free(cams);
|
|
_mm_free(isCube);
|
|
}
|
|
|
|
void k3BatchClear() {
|
|
renderQueueSize = 0;
|
|
}
|
|
|
|
int k3CubemapTraditional(struct k3Tex *tex, mat4 proj, mat4 cam) {
|
|
if(!tex->cubemap) {
|
|
k3Log(k3_ERR, "k3CubemapTraditional accepts only cubemaps");
|
|
return 0;
|
|
}
|
|
|
|
glDepthMask(GL_FALSE);
|
|
|
|
glActiveTexture(GL_TEXTURE0);
|
|
|
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
|
|
|
if(!k3IsCore) {
|
|
mat4 mv;
|
|
glm_mat4_inv_fast(cam, mv);
|
|
mv[3][0] = mv[3][1] = mv[3][2] = 0;
|
|
|
|
glMatrixMode(GL_PROJECTION);
|
|
glLoadMatrixf((float*) proj);
|
|
|
|
glMatrixMode(GL_MODELVIEW);
|
|
glLoadMatrixf((float*) mv);
|
|
|
|
if(!GLAD_GL_ARB_shading_language_100) {
|
|
glDisable(GL_LIGHTING);
|
|
}
|
|
|
|
glDisable(GL_CULL_FACE);
|
|
|
|
if(GLAD_GL_ARB_shading_language_100) {
|
|
glUseProgramObjectARB(0);
|
|
}
|
|
if(GLAD_GL_ARB_vertex_program) {
|
|
glDisable(GL_VERTEX_PROGRAM_ARB);
|
|
}
|
|
if(GLAD_GL_ARB_fragment_program) {
|
|
glDisable(GL_FRAGMENT_PROGRAM_ARB);
|
|
}
|
|
|
|
glEnable(GL_TEXTURE_CUBE_MAP);
|
|
//glEnable(0x884F);
|
|
|
|
glBegin(GL_QUADS);
|
|
glColor3f(1, 1, 1);
|
|
|
|
for(int z = -1; z <= 1; z += 2) {
|
|
glTexCoord3f(-1, -1, z);
|
|
glVertex3f(-1, -1, z);
|
|
glTexCoord3f(+1, -1, z);
|
|
glVertex3f(+1, -1, z);
|
|
glTexCoord3f(+1, +1, z);
|
|
glVertex3f(+1, +1, z);
|
|
glTexCoord3f(-1, +1, z);
|
|
glVertex3f(-1, +1, z);
|
|
}
|
|
for(int x = -1; x <= 1; x += 2) {
|
|
glTexCoord3f(x, -1, -1);
|
|
glVertex3f(x, -1, -1);
|
|
glTexCoord3f(x, +1, -1);
|
|
glVertex3f(x, +1, -1);
|
|
glTexCoord3f(x, +1, +1);
|
|
glVertex3f(x, +1, +1);
|
|
glTexCoord3f(x, -1, +1);
|
|
glVertex3f(x, -1, +1);
|
|
}
|
|
for(int y = -1; y <= 1; y += 2) {
|
|
glTexCoord3f(-1, y, -1);
|
|
glVertex3f(-1, y, -1);
|
|
glTexCoord3f(+1, y, -1);
|
|
glVertex3f(+1, y, -1);
|
|
glTexCoord3f(+1, y, +1);
|
|
glVertex3f(+1, y, +1);
|
|
glTexCoord3f(-1, y, +1);
|
|
glVertex3f(-1, y, +1);
|
|
}
|
|
glEnd();
|
|
|
|
glDisable(GL_TEXTURE_CUBE_MAP);
|
|
} else {
|
|
GLuint prog = GL_FROM_K3GLSL(basicCubemapProgram);
|
|
|
|
glUseProgram(prog);
|
|
|
|
mat4 cam0;
|
|
glm_mat4_copy(cam, cam0);
|
|
cam0[3][0] = 0;
|
|
cam0[3][1] = 0;
|
|
cam0[3][2] = 0;
|
|
|
|
mat4 pinv;
|
|
glm_mat4_inv_fast(proj, pinv);
|
|
|
|
mat4 vpinv;
|
|
glm_mat4_mul(cam0, pinv, vpinv);
|
|
|
|
glUniformMatrix4fv(glGetUniformLocation(prog, "u_vpinv"), 1, GL_FALSE, (float*) vpinv);
|
|
|
|
glDrawArrays(GL_TRIANGLES, 0, 3);
|
|
|
|
// For some reason this is necessary?!?!?!
|
|
// PLEASE SOMEONE FIGURE OUT
|
|
glUseProgram(0);
|
|
}
|
|
|
|
glDepthMask(GL_TRUE);
|
|
|
|
return 1;
|
|
}
|
|
|
|
void GlCallback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *message, const void *userParam) {
|
|
// Some messages end in newlines; remove.
|
|
if(message[length - 1] == '\n') {
|
|
length--;
|
|
}
|
|
|
|
// A history is employed to prevent spammings in the debug callback.
|
|
|
|
#define HISTORY_SIZE 8
|
|
static char *history[HISTORY_SIZE];
|
|
static int historyNext = 0;
|
|
|
|
int newish = 1;
|
|
|
|
for(int i = 0; i < HISTORY_SIZE; i++) {
|
|
if(history[i] && !strncmp(history[i], message, length)) {
|
|
newish = 0;
|
|
}
|
|
}
|
|
|
|
if(newish) {
|
|
enum k3LogLevel lvl;
|
|
|
|
switch(severity) {
|
|
case GL_DEBUG_SEVERITY_HIGH_ARB:
|
|
lvl = k3_ERR;
|
|
break;
|
|
case GL_DEBUG_SEVERITY_MEDIUM_ARB:
|
|
lvl = k3_WARN;
|
|
break;
|
|
default:
|
|
lvl = k3_INFO;
|
|
break;
|
|
}
|
|
|
|
k3Log(lvl, "%.*s", length, message);
|
|
|
|
history[historyNext] = realloc(history[historyNext], length + 1);
|
|
strncpy(history[historyNext], message, length);
|
|
history[historyNext][length] = 0;
|
|
|
|
historyNext = (historyNext + 1) % HISTORY_SIZE;
|
|
}
|
|
}
|
|
|
|
void k3Init() {
|
|
if(GLAD_GL_ARB_debug_output) {
|
|
glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS_ARB);
|
|
glDebugMessageCallbackARB(GlCallback, NULL);
|
|
}
|
|
|
|
GLuint i;
|
|
glGetIntegerv(GL_CONTEXT_PROFILE_MASK, &i);
|
|
|
|
if(i & GL_CONTEXT_CORE_PROFILE_BIT) {
|
|
k3IsCore = 1;
|
|
} else {
|
|
k3IsCore = 0;
|
|
}
|
|
|
|
if(k3IsCore) {
|
|
GLuint lol;
|
|
glGenVertexArrays(1, &lol);
|
|
glBindVertexArray(lol);
|
|
|
|
basicBlitProgram = k3ProgramGLSL(k3ShaderGLSLV(
|
|
"#version 330\n"
|
|
"uniform vec2 u_sz;"
|
|
"const vec4 positions[3] = vec4[3] (vec4(-1, -1, 0, 1), vec4(3, -1, 0, 1), vec4(-1, 3, 0, 1));\n"
|
|
"out vec2 v_uv;\n"
|
|
"void main() {\n"
|
|
" v_uv = positions[gl_VertexID].xy * (1.0 + 1.0 / u_sz) * 0.5 + 0.5;\n"
|
|
" gl_Position = positions[gl_VertexID];\n"
|
|
"}\n",
|
|
NULL), k3ShaderGLSLF(
|
|
"#version 330\n"
|
|
"uniform sampler2D u_tex;\n"
|
|
"in vec2 v_uv;\n"
|
|
"out vec4 fragcol;\n"
|
|
"void main() {\n"
|
|
" fragcol = texture2D(u_tex, v_uv);\n"
|
|
"}\n",
|
|
NULL), NULL);
|
|
|
|
basicCubemapProgram = k3ProgramGLSL(k3ShaderGLSLV(
|
|
"#version 330\n"
|
|
"const vec4 positions[3] = vec4[3] (vec4(-1, -1, 0, 1), vec4(3, -1, 0, 1), vec4(-1, 3, 0, 1));\n"
|
|
"uniform mat4 u_vpinv;\n"
|
|
"out vec3 v_uv;\n"
|
|
"void main() {\n"
|
|
" v_uv = (u_vpinv * vec4(positions[gl_VertexID].xyz, 1)).xyz;\n"
|
|
" gl_Position = positions[gl_VertexID];\n"
|
|
"}\n"
|
|
, NULL), k3ShaderGLSLF(
|
|
"#version 330\n"
|
|
"uniform samplerCube u_tex;\n"
|
|
"in vec3 v_uv;\n"
|
|
"out vec4 fragcolor;\n"
|
|
"void main() {\n"
|
|
" fragcolor = textureCube(u_tex, v_uv);\n"
|
|
"}\n"
|
|
, NULL), NULL);
|
|
}
|
|
}
|
|
|
|
static size_t preprocess(char *src, const char*(*ldr)(const char *fn), const char ***strs, GLuint **sizes) {
|
|
size_t len = 1;
|
|
*strs = calloc(len, sizeof(**strs));
|
|
*sizes = calloc(len, sizeof(**sizes));
|
|
|
|
(*strs)[0] = src;
|
|
(*sizes)[0] = strlen(src);
|
|
|
|
for(size_t i = 0; i < len; i++) {
|
|
char *include = strstr((*strs)[i], "#include ");
|
|
|
|
if(include && (include == (*strs)[i] || include[-1] == '\n')) {
|
|
|
|
char *end = strchr(include, '\n');
|
|
|
|
*end = 0;
|
|
|
|
if(!ldr) {
|
|
|
|
k3Log(k3_ERR, "Cannot include %s without a loader.", include + 9);
|
|
|
|
} else {
|
|
|
|
len += 2;
|
|
*strs = realloc(*strs, sizeof(**strs) * (len));
|
|
*sizes = realloc(*sizes, sizeof(**sizes) * (len));
|
|
|
|
memmove(*strs + i + 3, *strs + i + 1, sizeof(**strs) * (len - i - 3));
|
|
memmove(*sizes + i + 3, *sizes + i + 1, sizeof(**sizes) * (len - i - 3));
|
|
|
|
(*sizes)[i] = include - (*strs)[i];
|
|
|
|
(*strs)[i + 1] = ldr(include + 9);
|
|
(*sizes)[i + 1] = strlen((*strs)[i + 1]);
|
|
|
|
(*strs)[i + 2] = end + 1;
|
|
(*sizes)[i + 2] = strlen((*strs)[i + 2]);
|
|
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
return len;
|
|
}
|
|
|
|
struct k3GLSLV *k3ShaderGLSLV(const char *src_, const char*(*ldr)(const char *fn)) {
|
|
GLhandleARB p;
|
|
|
|
char *src = strdup(src_);
|
|
|
|
const char **strs;
|
|
GLuint *sizes;
|
|
size_t listsz = preprocess(src, ldr, &strs, &sizes);
|
|
|
|
if(!k3IsCore) {
|
|
p = glCreateShaderObjectARB(GL_VERTEX_SHADER_ARB);
|
|
|
|
glShaderSourceARB(p, listsz, strs, sizes);
|
|
|
|
glCompileShaderARB(p);
|
|
|
|
int i;
|
|
glGetObjectParameterivARB(p, GL_OBJECT_COMPILE_STATUS_ARB, &i);
|
|
if(i == GL_FALSE) {
|
|
char buf[256];
|
|
GLsizei len;
|
|
glGetInfoLogARB(p, sizeof(buf), &len, buf);
|
|
|
|
k3Log(k3_ERR, "Failed to compile GLSL vertex shader: %.*s", len, buf);
|
|
}
|
|
} else {
|
|
p = glCreateShader(GL_VERTEX_SHADER);
|
|
|
|
glShaderSource(p, listsz, strs, sizes);
|
|
|
|
glCompileShader(p);
|
|
|
|
int i;
|
|
glGetShaderiv(p, GL_COMPILE_STATUS, &i);
|
|
if(i == GL_FALSE) {
|
|
char buf[256];
|
|
GLsizei len;
|
|
glGetShaderInfoLog(p, sizeof(buf), &len, buf);
|
|
|
|
k3Log(k3_ERR, "Failed to compile GLSL vertex shader: %.*s", len, buf);
|
|
}
|
|
}
|
|
|
|
free(strs);
|
|
free(sizes);
|
|
free(src);
|
|
|
|
return (struct k3GLSLV*) (uintptr_t) p;
|
|
}
|
|
|
|
struct k3GLSLF *k3ShaderGLSLF(const char *src_, const char*(*ldr)(const char *fn)) {
|
|
GLhandleARB p;
|
|
|
|
char *src = strdup(src_);
|
|
|
|
const char **strs;
|
|
GLuint *sizes;
|
|
size_t listsz = preprocess(src, ldr, &strs, &sizes);
|
|
|
|
if(!k3IsCore) {
|
|
p = glCreateShaderObjectARB(GL_FRAGMENT_SHADER_ARB);
|
|
|
|
glShaderSourceARB(p, listsz, strs, sizes);
|
|
|
|
glCompileShaderARB(p);
|
|
|
|
int i;
|
|
glGetObjectParameterivARB(p, GL_OBJECT_COMPILE_STATUS_ARB, &i);
|
|
if(i == GL_FALSE) {
|
|
char buf[256];
|
|
GLsizei len;
|
|
glGetInfoLogARB(p, sizeof(buf), &len, buf);
|
|
|
|
k3Log(k3_ERR, "Failed to compile GLSL fragment shader: %.*s", len, buf);
|
|
}
|
|
} else {
|
|
p = glCreateShader(GL_FRAGMENT_SHADER);
|
|
|
|
glShaderSource(p, listsz, strs, sizes);
|
|
|
|
glCompileShader(p);
|
|
|
|
int i;
|
|
glGetShaderiv(p, GL_COMPILE_STATUS, &i);
|
|
if(i == GL_FALSE) {
|
|
char buf[256];
|
|
GLsizei len;
|
|
glGetShaderInfoLog(p, sizeof(buf), &len, buf);
|
|
|
|
k3Log(k3_ERR, "Failed to compile GLSL fragment shader: %.*s", len, buf);
|
|
}
|
|
}
|
|
|
|
free(strs);
|
|
free(sizes);
|
|
free(src);
|
|
|
|
return (struct k3GLSLF*) (uintptr_t) p;
|
|
}
|
|
|
|
struct k3GLSLG *k3ShaderGLSLG(const char *src_, const char*(*ldr)(const char *fn)) {
|
|
GLhandleARB p = glCreateShaderObjectARB(GL_GEOMETRY_SHADER_EXT);
|
|
|
|
char *src = strdup(src_);
|
|
|
|
const char **strs;
|
|
GLuint *sizes;
|
|
size_t listsz = preprocess(src, ldr, &strs, &sizes);
|
|
|
|
glShaderSourceARB(p, listsz, strs, sizes);
|
|
|
|
glCompileShaderARB(p);
|
|
|
|
int i;
|
|
glGetObjectParameterivARB(p, GL_OBJECT_COMPILE_STATUS_ARB, &i);
|
|
if(i == GL_FALSE) {
|
|
char buf[256];
|
|
GLsizei len;
|
|
glGetInfoLogARB(p, sizeof(buf), &len, buf);
|
|
|
|
k3Log(k3_ERR, "Failed to compile GLSL geometry shader: %.*s", len, buf);
|
|
}
|
|
|
|
free(strs);
|
|
free(sizes);
|
|
free(src);
|
|
|
|
return (struct k3GLSLG*) (uintptr_t) p;
|
|
}
|
|
|
|
struct k3GLSLP *k3ProgramGLSL(struct k3GLSLV *vs, struct k3GLSLF *fs, struct k3GLSLG *gs) {
|
|
GLhandleARB prog;
|
|
|
|
if(!k3IsCore) {
|
|
prog = glCreateProgramObjectARB();
|
|
|
|
glAttachObjectARB(prog, (uintptr_t) vs);
|
|
glAttachObjectARB(prog, (uintptr_t) fs);
|
|
|
|
if(gs) glAttachObjectARB(prog, (uintptr_t) gs);
|
|
|
|
glLinkProgramARB(prog);
|
|
|
|
int i;
|
|
glGetObjectParameterivARB(prog, GL_OBJECT_LINK_STATUS_ARB, &i);
|
|
if(i == GL_FALSE) {
|
|
char buf[256];
|
|
GLsizei len;
|
|
glGetInfoLogARB(prog, sizeof(buf), &len, buf);
|
|
|
|
k3Log(k3_ERR, "Failed to link GLSL program: %.*s", len, buf);
|
|
}
|
|
|
|
GLint maxLength;
|
|
glGetObjectParameterivARB(prog, GL_OBJECT_ACTIVE_UNIFORM_MAX_LENGTH_ARB, &maxLength);
|
|
|
|
char *name = alloca(maxLength + 1);
|
|
memset(name, 0, maxLength + 1);
|
|
|
|
GLint uniformCount;
|
|
glGetObjectParameterivARB(prog, GL_OBJECT_ACTIVE_UNIFORMS_ARB, &uniformCount);
|
|
|
|
for(i = 0; i < uniformCount; i++) {
|
|
int size;
|
|
int type;
|
|
glGetActiveUniformARB(prog, i, maxLength, NULL, &size, &type, name);
|
|
|
|
k3Log(k3_DEBUG, "%i %s", size, name);
|
|
}
|
|
} else {
|
|
prog = glCreateProgram();
|
|
|
|
glAttachShader(prog, (uintptr_t) vs);
|
|
glAttachShader(prog, (uintptr_t) fs);
|
|
|
|
if(gs) glAttachShader(prog, (uintptr_t) gs);
|
|
|
|
glLinkProgram(prog);
|
|
|
|
int i;
|
|
glGetProgramiv(prog, GL_LINK_STATUS, &i);
|
|
if(i == GL_FALSE) {
|
|
char buf[256];
|
|
GLsizei len;
|
|
glGetProgramInfoLog(prog, sizeof(buf), &len, buf);
|
|
|
|
k3Log(k3_ERR, "Failed to link GLSL program: %.*s", len, buf);
|
|
}
|
|
|
|
GLint maxLength;
|
|
glGetProgramiv(prog, GL_ACTIVE_UNIFORM_MAX_LENGTH, &maxLength);
|
|
|
|
char *name = alloca(maxLength + 1);
|
|
memset(name, 0, maxLength + 1);
|
|
|
|
GLint uniformCount;
|
|
glGetProgramiv(prog, GL_ACTIVE_UNIFORMS, &uniformCount);
|
|
|
|
for(i = 0; i < uniformCount; i++) {
|
|
int size;
|
|
int type;
|
|
glGetActiveUniform(prog, i, maxLength, NULL, &size, &type, name);
|
|
|
|
k3Log(k3_DEBUG, "%i %s", size, name);
|
|
}
|
|
}
|
|
|
|
return (struct k3GLSLP*) (uintptr_t) prog;
|
|
}
|
|
|
|
uint16_t k3ProgramGetUId(struct k3GLSLP *p, const char *key) {
|
|
return glGetUniformLocationARB(GL_FROM_K3GLSL(p), key);
|
|
}
|
|
|
|
struct k3ARBVP *k3ProgramARBVP(const char *src) {
|
|
if(!GLAD_GL_ARB_vertex_program) {
|
|
return NULL;
|
|
}
|
|
|
|
GLuint p;
|
|
glGenProgramsARB(1, &p);
|
|
glBindProgramARB(GL_VERTEX_PROGRAM_ARB, p);
|
|
glGetError();
|
|
glProgramStringARB(GL_VERTEX_PROGRAM_ARB, GL_PROGRAM_FORMAT_ASCII_ARB, strlen(src), src);
|
|
if(glGetError() == GL_INVALID_OPERATION) {
|
|
puts(glGetString(GL_PROGRAM_ERROR_STRING_ARB));
|
|
return NULL;
|
|
}
|
|
|
|
/*printf("VP\n");
|
|
|
|
GLint i;
|
|
glGetProgramivARB(GL_VERTEX_PROGRAM_ARB, GL_PROGRAM_INSTRUCTIONS_ARB, &i);
|
|
printf("\tTotal instructions: %i\n", i);
|
|
glGetProgramivARB(GL_VERTEX_PROGRAM_ARB, GL_PROGRAM_ALU_INSTRUCTIONS_ARB, &i);
|
|
printf("\tTotal ALU instructions: %i\n", i);
|
|
glGetProgramivARB(GL_VERTEX_PROGRAM_ARB, GL_PROGRAM_TEX_INSTRUCTIONS_ARB, &i);
|
|
printf("\tTotal TEX instructions: %i\n", i);
|
|
glGetProgramivARB(GL_VERTEX_PROGRAM_ARB, GL_PROGRAM_TEX_INDIRECTIONS_ARB, &i);
|
|
printf("\tTotal TEX indirections: %i\n", i);
|
|
glGetProgramivARB(GL_VERTEX_PROGRAM_ARB, GL_PROGRAM_TEMPORARIES_ARB, &i);
|
|
printf("\tTotal temporaries: %i\n", i);
|
|
glGetProgramivARB(GL_VERTEX_PROGRAM_ARB, GL_PROGRAM_PARAMETERS_ARB, &i);
|
|
printf("\tTotal parameters: %i\n", i);
|
|
glGetProgramivARB(GL_VERTEX_PROGRAM_ARB, GL_PROGRAM_ATTRIBS_ARB, &i);
|
|
printf("\tTotal attribs: %i\n", i);
|
|
glGetProgramivARB(GL_VERTEX_PROGRAM_ARB, GL_PROGRAM_ADDRESS_REGISTERS_ARB, &i);
|
|
printf("\tTotal addresses: %i\n", i);*/
|
|
|
|
return (struct k3ARBVP*) (uintptr_t) p;
|
|
}
|
|
|
|
struct k3ARBFP *k3ProgramARBFP(const char *src) {
|
|
if(!GLAD_GL_ARB_fragment_program) {
|
|
return NULL;
|
|
}
|
|
|
|
GLuint p;
|
|
glGenProgramsARB(1, &p);
|
|
glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, p);
|
|
glGetError();
|
|
glProgramStringARB(GL_FRAGMENT_PROGRAM_ARB, GL_PROGRAM_FORMAT_ASCII_ARB, strlen(src), src);
|
|
if(glGetError() == GL_INVALID_OPERATION) {
|
|
puts(glGetString(GL_PROGRAM_ERROR_STRING_ARB));
|
|
return NULL;
|
|
}
|
|
|
|
/*printf("FP\n");
|
|
|
|
GLint i;
|
|
glGetProgramivARB(GL_FRAGMENT_PROGRAM_ARB, GL_PROGRAM_INSTRUCTIONS_ARB, &i);
|
|
printf("\tTotal instructions: %i\n", i);
|
|
glGetProgramivARB(GL_FRAGMENT_PROGRAM_ARB, GL_PROGRAM_ALU_INSTRUCTIONS_ARB, &i);
|
|
printf("\tTotal ALU instructions: %i\n", i);
|
|
glGetProgramivARB(GL_FRAGMENT_PROGRAM_ARB, GL_PROGRAM_TEX_INSTRUCTIONS_ARB, &i);
|
|
printf("\tTotal TEX instructions: %i\n", i);
|
|
glGetProgramivARB(GL_FRAGMENT_PROGRAM_ARB, GL_PROGRAM_TEX_INDIRECTIONS_ARB, &i);
|
|
printf("\tTotal TEX indirections: %i\n", i);
|
|
glGetProgramivARB(GL_FRAGMENT_PROGRAM_ARB, GL_PROGRAM_TEMPORARIES_ARB, &i);
|
|
printf("\tTotal temporaries: %i\n", i);
|
|
glGetProgramivARB(GL_FRAGMENT_PROGRAM_ARB, GL_PROGRAM_PARAMETERS_ARB, &i);
|
|
printf("\tTotal parameters: %i\n", i);
|
|
glGetProgramivARB(GL_FRAGMENT_PROGRAM_ARB, GL_PROGRAM_ATTRIBS_ARB, &i);
|
|
printf("\tTotal attribs: %i\n", i);*/
|
|
|
|
return (struct k3ARBFP*) (uintptr_t) p;
|
|
}
|
|
|
|
struct k3Offscreen *k3OffscreenCreate(struct k3Tex *diffuse, struct k3Tex *depth) {
|
|
k3Log(k3_INFO, "Init %sFBO", !diffuse && depth ? "depth-only " : "");
|
|
|
|
GLuint fbo = 0;
|
|
if(GLAD_GL_EXT_framebuffer_object) {
|
|
glGenFramebuffersEXT(1, &fbo);
|
|
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo);
|
|
|
|
if(diffuse) {
|
|
glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, GL_FROM_K3TEX(diffuse), 0);
|
|
} else {
|
|
glDrawBuffer(GL_NONE);
|
|
glReadBuffer(GL_NONE);
|
|
}
|
|
|
|
if(depth) {
|
|
glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_2D, GL_FROM_K3TEX(depth), 0);
|
|
}
|
|
|
|
if(glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT) != GL_FRAMEBUFFER_COMPLETE_EXT) {
|
|
k3Log(k3_WARN, "Framebuffer incomplete");
|
|
}
|
|
|
|
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
|
|
glDrawBuffer(GL_BACK); // XXX: this should not be necessary
|
|
} else {
|
|
k3Log(k3_ERR, "Non-FBO offscreens not implemented");
|
|
return NULL;
|
|
}
|
|
|
|
struct k3Offscreen *ret = malloc(sizeof(*ret));
|
|
ret->fbo = fbo;
|
|
ret->diffuse = diffuse;
|
|
ret->depth = depth;
|
|
return ret;
|
|
}
|
|
|
|
void k3BeginOffscreen(struct k3Offscreen *offscr) {
|
|
if(GLAD_GL_EXT_framebuffer_object) {
|
|
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, offscr->fbo);
|
|
|
|
struct k3Tex *t = offscr->diffuse ? offscr->diffuse : offscr->depth;
|
|
glViewport(0, 0, k3TexSzX(t), k3TexSzY(t));
|
|
} else {
|
|
k3Log(k3_ERR, "Non-FBO offscreens not implemented");
|
|
}
|
|
}
|
|
|
|
void k3EndOffscreen(struct k3Offscreen *offscr) {
|
|
if(GLAD_GL_EXT_framebuffer_object) {
|
|
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
|
|
glViewport(0, 0, MainWidth, MainHeight);
|
|
} else {
|
|
k3Log(k3_ERR, "Non-FBO offscreens not implemented");
|
|
}
|
|
}
|
|
|
|
void k3BlitToScreen(struct k3Offscreen *offscr, int additive) {
|
|
k3BlitToScreenEffect(offscr, additive, k3_NONE, NULL, NULL);
|
|
}
|
|
|
|
void k3BlitToScreenEffect(struct k3Offscreen *offscr, int additive, int effect, void *program, void *params) {
|
|
if(GLAD_GL_EXT_framebuffer_object) {
|
|
glActiveTexture(GL_TEXTURE0);
|
|
|
|
glBindTexture(GL_TEXTURE_2D, GL_FROM_K3TEX(offscr->diffuse));
|
|
|
|
if(additive) {
|
|
glEnable(GL_BLEND);
|
|
glBlendFunc(GL_ONE, GL_ONE);
|
|
} else {
|
|
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
|
glDisable(GL_BLEND);
|
|
}
|
|
|
|
if(!k3IsCore) {
|
|
glDisable(GL_LIGHTING);
|
|
glDisable(GL_NORMALIZE);
|
|
|
|
glMatrixMode(GL_MODELVIEW);
|
|
glLoadIdentity();
|
|
glMatrixMode(GL_PROJECTION);
|
|
glLoadIdentity();
|
|
|
|
glEnable(GL_TEXTURE_2D);
|
|
}
|
|
|
|
glDisable(GL_DEPTH_TEST);
|
|
|
|
if(GLAD_GL_ARB_vertex_program) {
|
|
glDisable(GL_VERTEX_PROGRAM_ARB);
|
|
}
|
|
|
|
if(GLAD_GL_ARB_fragment_program) {
|
|
if(effect != k3_ARBFRAG) {
|
|
glDisable(GL_FRAGMENT_PROGRAM_ARB);
|
|
} else {
|
|
glEnable(GL_FRAGMENT_PROGRAM_ARB);
|
|
glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, effect == k3_ARBFRAG ? GL_FROM_K3ARBFP(program) : 0);
|
|
}
|
|
}
|
|
|
|
if(k3IsCore) {
|
|
if(effect == k3_NONE) {
|
|
glUseProgram(GL_FROM_K3GLSL(basicBlitProgram));
|
|
} else {
|
|
assert(effect == k3_GLSL);
|
|
|
|
glUseProgram(GL_FROM_K3GLSL(program));
|
|
}
|
|
|
|
GLint uSz = glGetUniformLocation(GL_FROM_K3GLSL(basicBlitProgram), "u_sz");
|
|
glUniform2f(uSz, k3TexSzX(offscr->diffuse), k3TexSzY(offscr->diffuse));
|
|
|
|
glDrawArrays(GL_TRIANGLES, 0, 3);
|
|
} else {
|
|
if(GLAD_GL_ARB_shader_objects) {
|
|
glUseProgramObjectARB(effect == k3_GLSL ? GL_FROM_K3GLSL(program) : 0);
|
|
}
|
|
|
|
float bleedW = 1.0f / k3TexSzX(offscr->diffuse);
|
|
float bleedH = 1.0f / k3TexSzY(offscr->diffuse);
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
|
|
|
glBegin(GL_QUADS);
|
|
glColor4f(1, 1, 1, 1);
|
|
glTexCoord2f(bleedW, bleedH);
|
|
glVertex2f(-1, -1);
|
|
glTexCoord2f(1 - bleedW, bleedH);
|
|
glVertex2f(1, -1);
|
|
glTexCoord2f(1 - bleedW, 1 - bleedH);
|
|
glVertex2f(1, 1);
|
|
glTexCoord2f(bleedW, 1 - bleedH);
|
|
glVertex2f(-1, 1);
|
|
glEnd();
|
|
}
|
|
} else {
|
|
k3Log(k3_ERR, "Non-FBO offscreens not implemented");
|
|
}
|
|
}
|
|
|
|
void k3OffscreenDestroy(struct k3Offscreen *offscr) {
|
|
|
|
}
|
|
|
|
static k3LogCallback CallbackFunc;
|
|
void k3SetLogCallback(k3LogCallback fun) {
|
|
CallbackFunc = fun;
|
|
}
|
|
void k3Log(enum k3LogLevel level, const char *format, ...) {
|
|
static char *buf = NULL;
|
|
static size_t bufLen = 0;
|
|
|
|
va_list vl;
|
|
va_start(vl, format);
|
|
|
|
va_list vlc;
|
|
va_copy(vlc, vl);
|
|
size_t len = vsnprintf(NULL, 0, format, vlc) + 1;
|
|
if(len > bufLen) {
|
|
buf = realloc(buf, len);
|
|
}
|
|
va_end(vlc);
|
|
|
|
vsnprintf(buf, len, format, vl);
|
|
|
|
va_end(vl);
|
|
|
|
CallbackFunc(level, buf, len);
|
|
}
|
|
|
|
uint16_t k3TexSzMax() {
|
|
GLint i;
|
|
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &i);
|
|
|
|
return i;
|
|
}
|