wip: lmap

main
Dominik Madarász 2023-12-01 12:01:03 +01:00
parent 32d4aa8d02
commit 084f549ba1
10 changed files with 505 additions and 76 deletions

View File

@ -1257,6 +1257,9 @@ ffi.cdef([[
//lcpp INF [0000] vec3: macro name but used as C declaration in:API vec3 pose(bool forward, float curframe, int minframe, int maxframe, bool loop, float *opt_retframe);
//lcpp INF [0000] vec3: macro name but used as C declaration in:STATIC vec3 pose(bool forward, float curframe, int minframe, int maxframe, bool loop, float *opt_retframe);
//lcpp INF [0000] vec3: macro name but used as C declaration in: vec3 pose(bool forward, float curframe, int minframe, int maxframe, bool loop, float *opt_retframe);
//lcpp INF [0000] vec3: macro name but used as C declaration in:API lightmap_t lightmap(int hmsize , float near, float far, vec3 color , int passes , float threshold , float distmod );
//lcpp INF [0000] vec3: macro name but used as C declaration in:STATIC lightmap_t lightmap(int hmsize , float near, float far, vec3 color , int passes , float threshold , float distmod );
//lcpp INF [0000] vec3: macro name but used as C declaration in: lightmap_t lightmap(int hmsize , float near, float far, vec3 color , int passes , float threshold , float distmod );
//lcpp INF [0000] vec3: macro name but used as C declaration in:API void skybox_sh_add_light(skybox_t *sky, vec3 light, vec3 dir, float strength);
//lcpp INF [0000] vec3: macro name but used as C declaration in:API void skybox_sh_add_light(skybox_t *sky, vec3 light, vec3 dir, float strength);
//lcpp INF [0000] vec3: macro name but used as C declaration in:STATIC void skybox_sh_add_light(skybox_t *sky, vec3 light, vec3 dir, float strength);
@ -2954,6 +2957,7 @@ unsigned num_textures;
handle *textures;
char **texture_names;
material_t* materials;
texture_t *lightmap;
unsigned num_meshes;
unsigned num_triangles;
unsigned num_joints;
@ -2999,7 +3003,16 @@ anim_t* anims;
anims_t animations(const char *pathfile, int flags);
typedef struct lightmap_t {
struct lm_context *ctx;
bool ready;
texture_t lightmap;
model_t** models;
} lightmap_t;
lightmap_t lightmap(int hmsize , float near, float far, vec3 color , int passes , float threshold , float distmod );
void lightmap_setup(lightmap_t *lm, int w, int h);
void lightmap_addmodel(lightmap_t *lm, model_t *m);
void lightmap_bake(lightmap_t *lm, int bounces, void (*drawscene)(lightmap_t *lm, float *view, float *proj, void *userdata), void *userdata);
void lightmap_clear(lightmap_t *lm);
void lightmap_destroy(lightmap_t *lm);
typedef struct skybox_t {
handle program;
mesh_t geometry;
@ -3324,7 +3337,7 @@ unsigned play;
bool paused;
struct atlas_t *a;
} sprite_t;
enum { OBJTYPE_sprite_t = 10 }; typedef struct { unsigned static_assert_on_L__LINE__ : !!(10 <= 255); } static_assert_on_Lconcat(_L,3945)___COUNTER__; typedef struct { unsigned static_assert_on_L__LINE__ : !!(sizeof(sprite_t)); } static_assert_on_Lconcat(_L,3945)___COUNTER__;;
enum { OBJTYPE_sprite_t = 10 }; typedef struct { unsigned static_assert_on_L__LINE__ : !!(10 <= 255); } static_assert_on_Lconcat(_L,3954)___COUNTER__; typedef struct { unsigned static_assert_on_L__LINE__ : !!(sizeof(sprite_t)); } static_assert_on_Lconcat(_L,3954)___COUNTER__;;
void sprite_ctor(sprite_t *s);
void sprite_dtor(sprite_t *s);
void sprite_tick(sprite_t *s);

View File

@ -1,7 +1,7 @@
#include "v4k.h"
#define LIGHTMAPPER_IMPLEMENTATION
// #define LM_DEBUG_INTERPOLATION
//#define LM_DEBUG_INTERPOLATION
#include "3rd_lightmapper.h"
#define scene_t scene_t2

View File

@ -152,7 +152,8 @@ void main() {
// rimlight
#ifdef RIM
{vec3 n = normalize(mat3(M) * v_normal); // convert normal to view space
{
vec3 n = normalize(mat3(M) * v_normal); // convert normal to view space
vec3 p = (M * vec4(v_position,1.0)).xyz; // convert position to view space
vec3 v = vec3(0,-1,0);
if (!u_rimambient) {

View File

@ -49,6 +49,7 @@ uniform sampler2DArray blend_shapes; // @todo: implement me
in vec3 att_position; // @todo: reorder ass2iqe to emit p3 n3 u2 t3 b3 c4B i4 w4 instead
in vec2 att_texcoord;
in vec2 att_texcoord2;
in vec3 att_normal;
in vec4 att_tangent; // vec3 + bi sign
in mat4 att_instanced_matrix; // for instanced rendering

View File

@ -15930,9 +15930,6 @@ API void script_call(const char *lua_function);
API bool script_tests();
// -----------------------------------------------------------------------------
// script framework
enum {
SCRIPT_LUA = 1,
SCRIPT_DEBUGGER = 2,
@ -17514,6 +17511,7 @@ typedef struct model_t {
handle *textures;
char **texture_names;
array(material_t) materials;
texture_t *lightmap;
unsigned num_meshes;
unsigned num_triangles;
@ -17524,7 +17522,7 @@ typedef struct model_t {
float curframe;
mat44 pivot;
int stride; // usually 60 bytes (12*4+4*3) for a p3 u2 n3 t4 i4B w4B c4B vertex stream
int stride; // usually 68 bytes for a p3 u2 u2 n3 t4 i4B w4B c4B vertex stream
void *verts;
int num_verts;
handle vao, ibo, vbo, vao_instanced;
@ -17573,11 +17571,22 @@ API anims_t animations(const char *pathfile, int flags);
// -----------------------------------------------------------------------------
// lightmapping utils
// @fixme: support xatlas uv packing
typedef struct lightmap_t {
struct lm_context *ctx; // private
bool ready;
texture_t lightmap; //@fixme: do we need it per-model?
array(model_t*) models;
} lightmap_t;
API lightmap_t lightmap(int hmsize /*64*/, float near, float far, vec3 color /*1,1,1 for AO*/, int passes /*2*/, float threshold /*0.01f*/, float distmod /*0.0f*/);
API void lightmap_setup(lightmap_t *lm, int w, int h);
API void lightmap_addmodel(lightmap_t *lm, model_t *m);
API void lightmap_bake(lightmap_t *lm, int bounces, void (*drawscene)(lightmap_t *lm, float *view, float *proj, void *userdata), void *userdata);
API void lightmap_clear(lightmap_t *lm);
API void lightmap_destroy(lightmap_t *lm);
// -----------------------------------------------------------------------------
// skyboxes
@ -369313,6 +369322,7 @@ struct iqmbounds {
typedef struct iqm_vertex {
GLfloat position[3];
GLfloat texcoord[2];
GLfloat texcoord2[2];
GLfloat normal[3];
GLfloat tangent[4];
GLubyte blendindexes[4];
@ -369472,26 +369482,28 @@ void model_set_state(model_t m) {
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(iqm_vertex), (GLvoid*)offsetof(iqm_vertex, position) );
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(iqm_vertex), (GLvoid*)offsetof(iqm_vertex, texcoord) );
glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, sizeof(iqm_vertex), (GLvoid*)offsetof(iqm_vertex, normal) );
glVertexAttribPointer(3, 4, GL_FLOAT, GL_FALSE, sizeof(iqm_vertex), (GLvoid*)offsetof(iqm_vertex, tangent) );
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(iqm_vertex), (GLvoid*)offsetof(iqm_vertex, texcoord2) );
glVertexAttribPointer(3, 3, GL_FLOAT, GL_FALSE, sizeof(iqm_vertex), (GLvoid*)offsetof(iqm_vertex, normal) );
glVertexAttribPointer(4, 4, GL_FLOAT, GL_FALSE, sizeof(iqm_vertex), (GLvoid*)offsetof(iqm_vertex, tangent) );
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
glEnableVertexAttribArray(2);
glEnableVertexAttribArray(3);
glEnableVertexAttribArray(4);
// vertex color
glVertexAttribPointer(11, 4, GL_UNSIGNED_BYTE, GL_FALSE, sizeof(iqm_vertex), (GLvoid*)offsetof(iqm_vertex,color) );
glEnableVertexAttribArray(11);
glVertexAttribPointer(12, 4, GL_UNSIGNED_BYTE, GL_FALSE, sizeof(iqm_vertex), (GLvoid*)offsetof(iqm_vertex,color) );
glEnableVertexAttribArray(12);
// animation
if(numframes > 0) {
glVertexAttribPointer( 8, 4, GL_UNSIGNED_BYTE, GL_FALSE, sizeof(iqm_vertex), (GLvoid*)offsetof(iqm_vertex,blendindexes) );
glVertexAttribPointer( 9, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(iqm_vertex), (GLvoid*)offsetof(iqm_vertex,blendweights) );
glVertexAttribPointer(10, 1, GL_FLOAT, GL_FALSE, sizeof(iqm_vertex), (GLvoid*)offsetof(iqm_vertex, blendvertexindex) );
glEnableVertexAttribArray(8);
glVertexAttribPointer( 9, 4, GL_UNSIGNED_BYTE, GL_FALSE, sizeof(iqm_vertex), (GLvoid*)offsetof(iqm_vertex,blendindexes) );
glVertexAttribPointer( 10, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(iqm_vertex), (GLvoid*)offsetof(iqm_vertex,blendweights) );
glVertexAttribPointer(11, 1, GL_FLOAT, GL_FALSE, sizeof(iqm_vertex), (GLvoid*)offsetof(iqm_vertex, blendvertexindex) );
glEnableVertexAttribArray(9);
glEnableVertexAttribArray(10);
glEnableVertexAttribArray(11);
}
// mat4 attribute; for instanced rendering
@ -369503,20 +369515,20 @@ void model_set_state(model_t m) {
glBindBuffer(GL_ARRAY_BUFFER, m.vao_instanced);
glBufferData(GL_ARRAY_BUFFER, m.num_instances * mat4_size, m.instanced_matrices, GL_STATIC_DRAW);
glVertexAttribPointer(4, 4, GL_FLOAT, GL_FALSE, 4 * vec4_size, (GLvoid*)(((char*)NULL)+(0 * vec4_size)));
glVertexAttribPointer(5, 4, GL_FLOAT, GL_FALSE, 4 * vec4_size, (GLvoid*)(((char*)NULL)+(1 * vec4_size)));
glVertexAttribPointer(6, 4, GL_FLOAT, GL_FALSE, 4 * vec4_size, (GLvoid*)(((char*)NULL)+(2 * vec4_size)));
glVertexAttribPointer(7, 4, GL_FLOAT, GL_FALSE, 4 * vec4_size, (GLvoid*)(((char*)NULL)+(3 * vec4_size)));
glVertexAttribPointer(5, 4, GL_FLOAT, GL_FALSE, 4 * vec4_size, (GLvoid*)(((char*)NULL)+(0 * vec4_size)));
glVertexAttribPointer(6, 4, GL_FLOAT, GL_FALSE, 4 * vec4_size, (GLvoid*)(((char*)NULL)+(1 * vec4_size)));
glVertexAttribPointer(7, 4, GL_FLOAT, GL_FALSE, 4 * vec4_size, (GLvoid*)(((char*)NULL)+(2 * vec4_size)));
glVertexAttribPointer(8, 4, GL_FLOAT, GL_FALSE, 4 * vec4_size, (GLvoid*)(((char*)NULL)+(3 * vec4_size)));
glEnableVertexAttribArray(4);
glEnableVertexAttribArray(5);
glEnableVertexAttribArray(6);
glEnableVertexAttribArray(7);
glEnableVertexAttribArray(8);
glVertexAttribDivisor(4, 1);
glVertexAttribDivisor(5, 1);
glVertexAttribDivisor(6, 1);
glVertexAttribDivisor(7, 1);
glVertexAttribDivisor(8, 1);
}
// 7 bitangent? into texcoord.z?
@ -369598,7 +369610,10 @@ bool model_load_meshes(iqm_t *q, const struct iqmheader *hdr, model_t *m) {
if(inposition) memcpy(v->position, &inposition[i*3], sizeof(v->position));
if(innormal) memcpy(v->normal, &innormal[i*3], sizeof(v->normal));
if(intangent) memcpy(v->tangent, &intangent[i*4], sizeof(v->tangent));
if(intexcoord) memcpy(v->texcoord, &intexcoord[i*2], sizeof(v->texcoord));
if(intexcoord) {
memcpy(v->texcoord, &intexcoord[i*2], sizeof(v->texcoord));
memcpy(v->texcoord2, &intexcoord[i*2], sizeof(v->texcoord2)); // populate UV1 with the same value, used by lightmapper
}
if(inblendindex8) memcpy(v->blendindexes, &inblendindex8[i*4], sizeof(v->blendindexes));
if(inblendweight8) memcpy(v->blendweights, &inblendweight8[i*4], sizeof(v->blendweights));
if(inblendindexi) {
@ -369868,8 +369883,8 @@ model_t model_from_mem(const void *mem, int len, int flags) {
// static int shaderprog = -1;
// if( shaderprog < 0 ) {
const char *symbols[] = { "{{include-shadowmap}}", vfs_read("shaders/fs_0_0_shadowmap_lit.glsl") }; // #define RIM
int shaderprog = shader(strlerp(1,symbols,vfs_read("shaders/vs_323444143_16_332_model.glsl")), strlerp(1,symbols,vfs_read("shaders/fs_32_4_model.glsl")), //fs,
"att_position,att_texcoord,att_normal,att_tangent,att_instanced_matrix,,,,att_indexes,att_weights,att_vertexindex,att_color,att_bitangent","fragColor",
int shaderprog = shader(strlerp(1,symbols,vfs_read("shaders/vs_3223444143_16_332_model.glsl")), strlerp(1,symbols,vfs_read("shaders/fs_32_4_model.glsl")), //fs,
"att_position,att_texcoord,att_texcoord2,att_normal,att_tangent,att_instanced_matrix,,,,att_indexes,att_weights,att_vertexindex,att_color,att_bitangent","fragColor",
va("SHADING_PHONG,%s", (flags&MODEL_RIMLIGHT)?"RIM":""));
// }
// ASSERT(shaderprog > 0);
@ -370259,6 +370274,129 @@ anims_t animations(const char *pathfile, int flags) {
}
return a;
}
// -----------------------------------------------------------------------------
// lightmapping utils
// @fixme: support xatlas uv packing, add UV1 coords to vertex model specs
lightmap_t lightmap(int hmsize, float cnear, float cfar, vec3 color, int passes, float threshold, float distmod) {
lightmap_t lm = {0};
lm.ctx = lmCreate(hmsize, cnear, cfar, color.x, color.y, color.z, passes, threshold, distmod);
if (!lm.ctx) {
PANIC("Error: Could not initialize lightmapper.\n");
return lm;
}
return lm;
}
static
void lightmap_destroytexture(lightmap_t *lm) {
texture_destroy(&lm->lightmap);
for (int i = 0; i < array_count(lm->models); i++) {
lm->models[i]->lightmap = NULL;
}
}
void lightmap_destroy(lightmap_t *lm) {
lmDestroy(lm->ctx);
lightmap_destroytexture(lm);
//
}
void lightmap_setup(lightmap_t *lm, int w, int h) {
if (lm->ready) {
lightmap_destroytexture(lm);
}
lm->ready=1;
lm->lightmap = texture_create(w, h, 4, 0, TEXTURE_LINEAR);
glBindTexture(GL_TEXTURE_2D, lm->lightmap.id);
unsigned char emissive[] = { 0, 0, 0, 255 };
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, emissive);
glBindTexture(GL_TEXTURE_2D, 0);
}
void lightmap_addmodel(lightmap_t *lm, model_t *m) {
array_push(lm->models, m);
}
void lightmap_bake(lightmap_t *lm, int bounces, void (*drawscene)(lightmap_t *lm, float *view, float *proj, void *userdata), void *userdata) {
ASSERT(lm->ready);
// @fixme: use xatlas to UV pack all models, update their UV1 and upload them to GPU.
// @fixme: combine all verts data together and push to lmSetGeometry
// int w = lm->lightmap->w, h = lm->lightmap->h;
// float *data = CALLOC(w * h * 4, sizeof(float));
// memset(data, 0, w*h*4);
// for (int b = 0; b < bounces; b++) {
// lmSetTargetLightmap(lm->ctx, data, w, h, 4);
// lmSetGeometry(lm->ctx, NULL,
// LM_FLOAT, (unsigned char*)scene->vertices + offsetof(vertex_t, p), sizeof(vertex_t),
// LM_NONE , NULL , 0 , // no interpolated normals in this example
// LM_FLOAT, (unsigned char*)scene->vertices + offsetof(vertex_t, t), sizeof(vertex_t),
// scene->indexCount, LM_UNSIGNED_SHORT, scene->indices);
// glDisable(GL_BLEND);
// int vp[4];
// float view[16], projection[16];
// double lastUpdateTime = 0.0;
// while (lmBegin(ctx, vp, view, projection))
// {
// // render to lightmapper framebuffer
// glViewport(vp[0], vp[1], vp[2], vp[3]);
// drawScene(scene, view, projection);
// // display progress every second (printf is expensive)
// double time = time_ms() / 1000.0;
// if (time - lastUpdateTime > 1.0)
// {
// lastUpdateTime = time;
// printf("\r%6.2f%%", lmProgress(ctx) * 100.0f);
// fflush(stdout);
// }
// lmEnd(ctx);
// // window_swap();
// }
// printf("\rFinished baking %d triangles.\n", scene->indexCount / 3);
// }
// lmDestroy(ctx);
// // postprocess texture
// float *temp = CALLOC(w * h * 4, sizeof(float));
// for (int i = 0; i < 16; i++)
// {
// lmImageDilate(data, temp, w, h, 4);
// lmImageDilate(temp, data, w, h, 4);
// }
// lmImageSmooth(data, temp, w, h, 4);
// lmImageDilate(temp, data, w, h, 4);
// lmImagePower(data, w, h, 4, 1.0f / 2.2f, 0x7); // gamma correct color channels
// FREE(temp);
// // save result to a file
// if (lmImageSaveTGAf("result.tga", data, w, h, 4, 1.0f))
// printf("Saved result.tga\n");
// // upload result
// glBindTexture(GL_TEXTURE_2D, scene->lightmap);
// glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_FLOAT, data);
// FREE(data);
}
void lightmap_clear(lightmap_t *lm) {
ASSERT(lm->ready);
glBindTexture(GL_TEXTURE_2D, lm->lightmap.id);
unsigned char emissive[] = { 0, 0, 0, 255 };
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, emissive);
glBindTexture(GL_TEXTURE_2D, 0);
}
#line 0
#line 1 "v4k_renderdd.c"

View File

@ -23,9 +23,6 @@ API void script_call(const char *lua_function);
API bool script_tests();
// -----------------------------------------------------------------------------
// script framework
enum {
SCRIPT_LUA = 1,
SCRIPT_DEBUGGER = 2,

View File

@ -2766,6 +2766,7 @@ struct iqmbounds {
typedef struct iqm_vertex {
GLfloat position[3];
GLfloat texcoord[2];
GLfloat texcoord2[2];
GLfloat normal[3];
GLfloat tangent[4];
GLubyte blendindexes[4];
@ -2925,26 +2926,28 @@ void model_set_state(model_t m) {
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(iqm_vertex), (GLvoid*)offsetof(iqm_vertex, position) );
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(iqm_vertex), (GLvoid*)offsetof(iqm_vertex, texcoord) );
glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, sizeof(iqm_vertex), (GLvoid*)offsetof(iqm_vertex, normal) );
glVertexAttribPointer(3, 4, GL_FLOAT, GL_FALSE, sizeof(iqm_vertex), (GLvoid*)offsetof(iqm_vertex, tangent) );
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(iqm_vertex), (GLvoid*)offsetof(iqm_vertex, texcoord2) );
glVertexAttribPointer(3, 3, GL_FLOAT, GL_FALSE, sizeof(iqm_vertex), (GLvoid*)offsetof(iqm_vertex, normal) );
glVertexAttribPointer(4, 4, GL_FLOAT, GL_FALSE, sizeof(iqm_vertex), (GLvoid*)offsetof(iqm_vertex, tangent) );
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
glEnableVertexAttribArray(2);
glEnableVertexAttribArray(3);
glEnableVertexAttribArray(4);
// vertex color
glVertexAttribPointer(11, 4, GL_UNSIGNED_BYTE, GL_FALSE, sizeof(iqm_vertex), (GLvoid*)offsetof(iqm_vertex,color) );
glEnableVertexAttribArray(11);
glVertexAttribPointer(12, 4, GL_UNSIGNED_BYTE, GL_FALSE, sizeof(iqm_vertex), (GLvoid*)offsetof(iqm_vertex,color) );
glEnableVertexAttribArray(12);
// animation
if(numframes > 0) {
glVertexAttribPointer( 8, 4, GL_UNSIGNED_BYTE, GL_FALSE, sizeof(iqm_vertex), (GLvoid*)offsetof(iqm_vertex,blendindexes) );
glVertexAttribPointer( 9, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(iqm_vertex), (GLvoid*)offsetof(iqm_vertex,blendweights) );
glVertexAttribPointer(10, 1, GL_FLOAT, GL_FALSE, sizeof(iqm_vertex), (GLvoid*)offsetof(iqm_vertex, blendvertexindex) );
glEnableVertexAttribArray(8);
glVertexAttribPointer( 9, 4, GL_UNSIGNED_BYTE, GL_FALSE, sizeof(iqm_vertex), (GLvoid*)offsetof(iqm_vertex,blendindexes) );
glVertexAttribPointer( 10, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(iqm_vertex), (GLvoid*)offsetof(iqm_vertex,blendweights) );
glVertexAttribPointer(11, 1, GL_FLOAT, GL_FALSE, sizeof(iqm_vertex), (GLvoid*)offsetof(iqm_vertex, blendvertexindex) );
glEnableVertexAttribArray(9);
glEnableVertexAttribArray(10);
glEnableVertexAttribArray(11);
}
// mat4 attribute; for instanced rendering
@ -2956,20 +2959,20 @@ void model_set_state(model_t m) {
glBindBuffer(GL_ARRAY_BUFFER, m.vao_instanced);
glBufferData(GL_ARRAY_BUFFER, m.num_instances * mat4_size, m.instanced_matrices, GL_STATIC_DRAW);
glVertexAttribPointer(4, 4, GL_FLOAT, GL_FALSE, 4 * vec4_size, (GLvoid*)(((char*)NULL)+(0 * vec4_size)));
glVertexAttribPointer(5, 4, GL_FLOAT, GL_FALSE, 4 * vec4_size, (GLvoid*)(((char*)NULL)+(1 * vec4_size)));
glVertexAttribPointer(6, 4, GL_FLOAT, GL_FALSE, 4 * vec4_size, (GLvoid*)(((char*)NULL)+(2 * vec4_size)));
glVertexAttribPointer(7, 4, GL_FLOAT, GL_FALSE, 4 * vec4_size, (GLvoid*)(((char*)NULL)+(3 * vec4_size)));
glVertexAttribPointer(5, 4, GL_FLOAT, GL_FALSE, 4 * vec4_size, (GLvoid*)(((char*)NULL)+(0 * vec4_size)));
glVertexAttribPointer(6, 4, GL_FLOAT, GL_FALSE, 4 * vec4_size, (GLvoid*)(((char*)NULL)+(1 * vec4_size)));
glVertexAttribPointer(7, 4, GL_FLOAT, GL_FALSE, 4 * vec4_size, (GLvoid*)(((char*)NULL)+(2 * vec4_size)));
glVertexAttribPointer(8, 4, GL_FLOAT, GL_FALSE, 4 * vec4_size, (GLvoid*)(((char*)NULL)+(3 * vec4_size)));
glEnableVertexAttribArray(4);
glEnableVertexAttribArray(5);
glEnableVertexAttribArray(6);
glEnableVertexAttribArray(7);
glEnableVertexAttribArray(8);
glVertexAttribDivisor(4, 1);
glVertexAttribDivisor(5, 1);
glVertexAttribDivisor(6, 1);
glVertexAttribDivisor(7, 1);
glVertexAttribDivisor(8, 1);
}
// 7 bitangent? into texcoord.z?
@ -3051,7 +3054,10 @@ bool model_load_meshes(iqm_t *q, const struct iqmheader *hdr, model_t *m) {
if(inposition) memcpy(v->position, &inposition[i*3], sizeof(v->position));
if(innormal) memcpy(v->normal, &innormal[i*3], sizeof(v->normal));
if(intangent) memcpy(v->tangent, &intangent[i*4], sizeof(v->tangent));
if(intexcoord) memcpy(v->texcoord, &intexcoord[i*2], sizeof(v->texcoord));
if(intexcoord) {
memcpy(v->texcoord, &intexcoord[i*2], sizeof(v->texcoord));
memcpy(v->texcoord2, &intexcoord[i*2], sizeof(v->texcoord2)); // populate UV1 with the same value, used by lightmapper
}
if(inblendindex8) memcpy(v->blendindexes, &inblendindex8[i*4], sizeof(v->blendindexes));
if(inblendweight8) memcpy(v->blendweights, &inblendweight8[i*4], sizeof(v->blendweights));
if(inblendindexi) {
@ -3321,8 +3327,8 @@ model_t model_from_mem(const void *mem, int len, int flags) {
// static int shaderprog = -1;
// if( shaderprog < 0 ) {
const char *symbols[] = { "{{include-shadowmap}}", vfs_read("shaders/fs_0_0_shadowmap_lit.glsl") }; // #define RIM
int shaderprog = shader(strlerp(1,symbols,vfs_read("shaders/vs_323444143_16_332_model.glsl")), strlerp(1,symbols,vfs_read("shaders/fs_32_4_model.glsl")), //fs,
"att_position,att_texcoord,att_normal,att_tangent,att_instanced_matrix,,,,att_indexes,att_weights,att_vertexindex,att_color,att_bitangent","fragColor",
int shaderprog = shader(strlerp(1,symbols,vfs_read("shaders/vs_3223444143_16_332_model.glsl")), strlerp(1,symbols,vfs_read("shaders/fs_32_4_model.glsl")), //fs,
"att_position,att_texcoord,att_texcoord2,att_normal,att_tangent,att_instanced_matrix,,,,att_indexes,att_weights,att_vertexindex,att_color,att_bitangent","fragColor",
va("SHADING_PHONG,%s", (flags&MODEL_RIMLIGHT)?"RIM":""));
// }
// ASSERT(shaderprog > 0);
@ -3712,3 +3718,126 @@ anims_t animations(const char *pathfile, int flags) {
}
return a;
}
// -----------------------------------------------------------------------------
// lightmapping utils
// @fixme: support xatlas uv packing, add UV1 coords to vertex model specs
lightmap_t lightmap(int hmsize, float cnear, float cfar, vec3 color, int passes, float threshold, float distmod) {
lightmap_t lm = {0};
lm.ctx = lmCreate(hmsize, cnear, cfar, color.x, color.y, color.z, passes, threshold, distmod);
if (!lm.ctx) {
PANIC("Error: Could not initialize lightmapper.\n");
return lm;
}
return lm;
}
static
void lightmap_destroytexture(lightmap_t *lm) {
texture_destroy(&lm->lightmap);
for (int i = 0; i < array_count(lm->models); i++) {
lm->models[i]->lightmap = NULL;
}
}
void lightmap_destroy(lightmap_t *lm) {
lmDestroy(lm->ctx);
lightmap_destroytexture(lm);
//
}
void lightmap_setup(lightmap_t *lm, int w, int h) {
if (lm->ready) {
lightmap_destroytexture(lm);
}
lm->ready=1;
lm->lightmap = texture_create(w, h, 4, 0, TEXTURE_LINEAR);
glBindTexture(GL_TEXTURE_2D, lm->lightmap.id);
unsigned char emissive[] = { 0, 0, 0, 255 };
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, emissive);
glBindTexture(GL_TEXTURE_2D, 0);
}
void lightmap_addmodel(lightmap_t *lm, model_t *m) {
array_push(lm->models, m);
}
void lightmap_bake(lightmap_t *lm, int bounces, void (*drawscene)(lightmap_t *lm, float *view, float *proj, void *userdata), void *userdata) {
ASSERT(lm->ready);
// @fixme: use xatlas to UV pack all models, update their UV1 and upload them to GPU.
// @fixme: combine all verts data together and push to lmSetGeometry
// int w = lm->lightmap->w, h = lm->lightmap->h;
// float *data = CALLOC(w * h * 4, sizeof(float));
// memset(data, 0, w*h*4);
// for (int b = 0; b < bounces; b++) {
// lmSetTargetLightmap(lm->ctx, data, w, h, 4);
// lmSetGeometry(lm->ctx, NULL,
// LM_FLOAT, (unsigned char*)scene->vertices + offsetof(vertex_t, p), sizeof(vertex_t),
// LM_NONE , NULL , 0 , // no interpolated normals in this example
// LM_FLOAT, (unsigned char*)scene->vertices + offsetof(vertex_t, t), sizeof(vertex_t),
// scene->indexCount, LM_UNSIGNED_SHORT, scene->indices);
// glDisable(GL_BLEND);
// int vp[4];
// float view[16], projection[16];
// double lastUpdateTime = 0.0;
// while (lmBegin(ctx, vp, view, projection))
// {
// // render to lightmapper framebuffer
// glViewport(vp[0], vp[1], vp[2], vp[3]);
// drawScene(scene, view, projection);
// // display progress every second (printf is expensive)
// double time = time_ms() / 1000.0;
// if (time - lastUpdateTime > 1.0)
// {
// lastUpdateTime = time;
// printf("\r%6.2f%%", lmProgress(ctx) * 100.0f);
// fflush(stdout);
// }
// lmEnd(ctx);
// // window_swap();
// }
// printf("\rFinished baking %d triangles.\n", scene->indexCount / 3);
// }
// lmDestroy(ctx);
// // postprocess texture
// float *temp = CALLOC(w * h * 4, sizeof(float));
// for (int i = 0; i < 16; i++)
// {
// lmImageDilate(data, temp, w, h, 4);
// lmImageDilate(temp, data, w, h, 4);
// }
// lmImageSmooth(data, temp, w, h, 4);
// lmImageDilate(temp, data, w, h, 4);
// lmImagePower(data, w, h, 4, 1.0f / 2.2f, 0x7); // gamma correct color channels
// FREE(temp);
// // save result to a file
// if (lmImageSaveTGAf("result.tga", data, w, h, 4, 1.0f))
// printf("Saved result.tga\n");
// // upload result
// glBindTexture(GL_TEXTURE_2D, scene->lightmap);
// glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_FLOAT, data);
// FREE(data);
}
void lightmap_clear(lightmap_t *lm) {
ASSERT(lm->ready);
glBindTexture(GL_TEXTURE_2D, lm->lightmap.id);
unsigned char emissive[] = { 0, 0, 0, 255 };
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, emissive);
glBindTexture(GL_TEXTURE_2D, 0);
}

View File

@ -520,6 +520,7 @@ typedef struct model_t {
handle *textures;
char **texture_names;
array(material_t) materials;
texture_t *lightmap;
unsigned num_meshes;
unsigned num_triangles;
@ -530,7 +531,7 @@ typedef struct model_t {
float curframe;
mat44 pivot;
int stride; // usually 60 bytes (12*4+4*3) for a p3 u2 n3 t4 i4B w4B c4B vertex stream
int stride; // usually 68 bytes for a p3 u2 u2 n3 t4 i4B w4B c4B vertex stream
void *verts;
int num_verts;
handle vao, ibo, vbo, vao_instanced;
@ -579,11 +580,22 @@ API anims_t animations(const char *pathfile, int flags);
// -----------------------------------------------------------------------------
// lightmapping utils
// @fixme: support xatlas uv packing
typedef struct lightmap_t {
struct lm_context *ctx; // private
bool ready;
texture_t lightmap; //@fixme: do we need it per-model?
array(model_t*) models;
} lightmap_t;
API lightmap_t lightmap(int hmsize /*64*/, float near, float far, vec3 color /*1,1,1 for AO*/, int passes /*2*/, float threshold /*0.01f*/, float distmod /*0.0f*/);
API void lightmap_setup(lightmap_t *lm, int w, int h);
API void lightmap_addmodel(lightmap_t *lm, model_t *m);
API void lightmap_bake(lightmap_t *lm, int bounces, void (*drawscene)(lightmap_t *lm, float *view, float *proj, void *userdata), void *userdata);
API void lightmap_clear(lightmap_t *lm);
API void lightmap_destroy(lightmap_t *lm);
// -----------------------------------------------------------------------------
// skyboxes

View File

@ -19682,6 +19682,7 @@ struct iqmbounds {
typedef struct iqm_vertex {
GLfloat position[3];
GLfloat texcoord[2];
GLfloat texcoord2[2];
GLfloat normal[3];
GLfloat tangent[4];
GLubyte blendindexes[4];
@ -19841,26 +19842,28 @@ void model_set_state(model_t m) {
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(iqm_vertex), (GLvoid*)offsetof(iqm_vertex, position) );
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(iqm_vertex), (GLvoid*)offsetof(iqm_vertex, texcoord) );
glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, sizeof(iqm_vertex), (GLvoid*)offsetof(iqm_vertex, normal) );
glVertexAttribPointer(3, 4, GL_FLOAT, GL_FALSE, sizeof(iqm_vertex), (GLvoid*)offsetof(iqm_vertex, tangent) );
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(iqm_vertex), (GLvoid*)offsetof(iqm_vertex, texcoord2) );
glVertexAttribPointer(3, 3, GL_FLOAT, GL_FALSE, sizeof(iqm_vertex), (GLvoid*)offsetof(iqm_vertex, normal) );
glVertexAttribPointer(4, 4, GL_FLOAT, GL_FALSE, sizeof(iqm_vertex), (GLvoid*)offsetof(iqm_vertex, tangent) );
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
glEnableVertexAttribArray(2);
glEnableVertexAttribArray(3);
glEnableVertexAttribArray(4);
// vertex color
glVertexAttribPointer(11, 4, GL_UNSIGNED_BYTE, GL_FALSE, sizeof(iqm_vertex), (GLvoid*)offsetof(iqm_vertex,color) );
glEnableVertexAttribArray(11);
glVertexAttribPointer(12, 4, GL_UNSIGNED_BYTE, GL_FALSE, sizeof(iqm_vertex), (GLvoid*)offsetof(iqm_vertex,color) );
glEnableVertexAttribArray(12);
// animation
if(numframes > 0) {
glVertexAttribPointer( 8, 4, GL_UNSIGNED_BYTE, GL_FALSE, sizeof(iqm_vertex), (GLvoid*)offsetof(iqm_vertex,blendindexes) );
glVertexAttribPointer( 9, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(iqm_vertex), (GLvoid*)offsetof(iqm_vertex,blendweights) );
glVertexAttribPointer(10, 1, GL_FLOAT, GL_FALSE, sizeof(iqm_vertex), (GLvoid*)offsetof(iqm_vertex, blendvertexindex) );
glEnableVertexAttribArray(8);
glVertexAttribPointer( 9, 4, GL_UNSIGNED_BYTE, GL_FALSE, sizeof(iqm_vertex), (GLvoid*)offsetof(iqm_vertex,blendindexes) );
glVertexAttribPointer( 10, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(iqm_vertex), (GLvoid*)offsetof(iqm_vertex,blendweights) );
glVertexAttribPointer(11, 1, GL_FLOAT, GL_FALSE, sizeof(iqm_vertex), (GLvoid*)offsetof(iqm_vertex, blendvertexindex) );
glEnableVertexAttribArray(9);
glEnableVertexAttribArray(10);
glEnableVertexAttribArray(11);
}
// mat4 attribute; for instanced rendering
@ -19872,20 +19875,20 @@ void model_set_state(model_t m) {
glBindBuffer(GL_ARRAY_BUFFER, m.vao_instanced);
glBufferData(GL_ARRAY_BUFFER, m.num_instances * mat4_size, m.instanced_matrices, GL_STATIC_DRAW);
glVertexAttribPointer(4, 4, GL_FLOAT, GL_FALSE, 4 * vec4_size, (GLvoid*)(((char*)NULL)+(0 * vec4_size)));
glVertexAttribPointer(5, 4, GL_FLOAT, GL_FALSE, 4 * vec4_size, (GLvoid*)(((char*)NULL)+(1 * vec4_size)));
glVertexAttribPointer(6, 4, GL_FLOAT, GL_FALSE, 4 * vec4_size, (GLvoid*)(((char*)NULL)+(2 * vec4_size)));
glVertexAttribPointer(7, 4, GL_FLOAT, GL_FALSE, 4 * vec4_size, (GLvoid*)(((char*)NULL)+(3 * vec4_size)));
glVertexAttribPointer(5, 4, GL_FLOAT, GL_FALSE, 4 * vec4_size, (GLvoid*)(((char*)NULL)+(0 * vec4_size)));
glVertexAttribPointer(6, 4, GL_FLOAT, GL_FALSE, 4 * vec4_size, (GLvoid*)(((char*)NULL)+(1 * vec4_size)));
glVertexAttribPointer(7, 4, GL_FLOAT, GL_FALSE, 4 * vec4_size, (GLvoid*)(((char*)NULL)+(2 * vec4_size)));
glVertexAttribPointer(8, 4, GL_FLOAT, GL_FALSE, 4 * vec4_size, (GLvoid*)(((char*)NULL)+(3 * vec4_size)));
glEnableVertexAttribArray(4);
glEnableVertexAttribArray(5);
glEnableVertexAttribArray(6);
glEnableVertexAttribArray(7);
glEnableVertexAttribArray(8);
glVertexAttribDivisor(4, 1);
glVertexAttribDivisor(5, 1);
glVertexAttribDivisor(6, 1);
glVertexAttribDivisor(7, 1);
glVertexAttribDivisor(8, 1);
}
// 7 bitangent? into texcoord.z?
@ -19967,7 +19970,10 @@ bool model_load_meshes(iqm_t *q, const struct iqmheader *hdr, model_t *m) {
if(inposition) memcpy(v->position, &inposition[i*3], sizeof(v->position));
if(innormal) memcpy(v->normal, &innormal[i*3], sizeof(v->normal));
if(intangent) memcpy(v->tangent, &intangent[i*4], sizeof(v->tangent));
if(intexcoord) memcpy(v->texcoord, &intexcoord[i*2], sizeof(v->texcoord));
if(intexcoord) {
memcpy(v->texcoord, &intexcoord[i*2], sizeof(v->texcoord));
memcpy(v->texcoord2, &intexcoord[i*2], sizeof(v->texcoord2)); // populate UV1 with the same value, used by lightmapper
}
if(inblendindex8) memcpy(v->blendindexes, &inblendindex8[i*4], sizeof(v->blendindexes));
if(inblendweight8) memcpy(v->blendweights, &inblendweight8[i*4], sizeof(v->blendweights));
if(inblendindexi) {
@ -20237,8 +20243,8 @@ model_t model_from_mem(const void *mem, int len, int flags) {
// static int shaderprog = -1;
// if( shaderprog < 0 ) {
const char *symbols[] = { "{{include-shadowmap}}", vfs_read("shaders/fs_0_0_shadowmap_lit.glsl") }; // #define RIM
int shaderprog = shader(strlerp(1,symbols,vfs_read("shaders/vs_323444143_16_332_model.glsl")), strlerp(1,symbols,vfs_read("shaders/fs_32_4_model.glsl")), //fs,
"att_position,att_texcoord,att_normal,att_tangent,att_instanced_matrix,,,,att_indexes,att_weights,att_vertexindex,att_color,att_bitangent","fragColor",
int shaderprog = shader(strlerp(1,symbols,vfs_read("shaders/vs_3223444143_16_332_model.glsl")), strlerp(1,symbols,vfs_read("shaders/fs_32_4_model.glsl")), //fs,
"att_position,att_texcoord,att_texcoord2,att_normal,att_tangent,att_instanced_matrix,,,,att_indexes,att_weights,att_vertexindex,att_color,att_bitangent","fragColor",
va("SHADING_PHONG,%s", (flags&MODEL_RIMLIGHT)?"RIM":""));
// }
// ASSERT(shaderprog > 0);
@ -20628,6 +20634,129 @@ anims_t animations(const char *pathfile, int flags) {
}
return a;
}
// -----------------------------------------------------------------------------
// lightmapping utils
// @fixme: support xatlas uv packing, add UV1 coords to vertex model specs
lightmap_t lightmap(int hmsize, float cnear, float cfar, vec3 color, int passes, float threshold, float distmod) {
lightmap_t lm = {0};
lm.ctx = lmCreate(hmsize, cnear, cfar, color.x, color.y, color.z, passes, threshold, distmod);
if (!lm.ctx) {
PANIC("Error: Could not initialize lightmapper.\n");
return lm;
}
return lm;
}
static
void lightmap_destroytexture(lightmap_t *lm) {
texture_destroy(&lm->lightmap);
for (int i = 0; i < array_count(lm->models); i++) {
lm->models[i]->lightmap = NULL;
}
}
void lightmap_destroy(lightmap_t *lm) {
lmDestroy(lm->ctx);
lightmap_destroytexture(lm);
//
}
void lightmap_setup(lightmap_t *lm, int w, int h) {
if (lm->ready) {
lightmap_destroytexture(lm);
}
lm->ready=1;
lm->lightmap = texture_create(w, h, 4, 0, TEXTURE_LINEAR);
glBindTexture(GL_TEXTURE_2D, lm->lightmap.id);
unsigned char emissive[] = { 0, 0, 0, 255 };
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, emissive);
glBindTexture(GL_TEXTURE_2D, 0);
}
void lightmap_addmodel(lightmap_t *lm, model_t *m) {
array_push(lm->models, m);
}
void lightmap_bake(lightmap_t *lm, int bounces, void (*drawscene)(lightmap_t *lm, float *view, float *proj, void *userdata), void *userdata) {
ASSERT(lm->ready);
// @fixme: use xatlas to UV pack all models, update their UV1 and upload them to GPU.
// @fixme: combine all verts data together and push to lmSetGeometry
// int w = lm->lightmap->w, h = lm->lightmap->h;
// float *data = CALLOC(w * h * 4, sizeof(float));
// memset(data, 0, w*h*4);
// for (int b = 0; b < bounces; b++) {
// lmSetTargetLightmap(lm->ctx, data, w, h, 4);
// lmSetGeometry(lm->ctx, NULL,
// LM_FLOAT, (unsigned char*)scene->vertices + offsetof(vertex_t, p), sizeof(vertex_t),
// LM_NONE , NULL , 0 , // no interpolated normals in this example
// LM_FLOAT, (unsigned char*)scene->vertices + offsetof(vertex_t, t), sizeof(vertex_t),
// scene->indexCount, LM_UNSIGNED_SHORT, scene->indices);
// glDisable(GL_BLEND);
// int vp[4];
// float view[16], projection[16];
// double lastUpdateTime = 0.0;
// while (lmBegin(ctx, vp, view, projection))
// {
// // render to lightmapper framebuffer
// glViewport(vp[0], vp[1], vp[2], vp[3]);
// drawScene(scene, view, projection);
// // display progress every second (printf is expensive)
// double time = time_ms() / 1000.0;
// if (time - lastUpdateTime > 1.0)
// {
// lastUpdateTime = time;
// printf("\r%6.2f%%", lmProgress(ctx) * 100.0f);
// fflush(stdout);
// }
// lmEnd(ctx);
// // window_swap();
// }
// printf("\rFinished baking %d triangles.\n", scene->indexCount / 3);
// }
// lmDestroy(ctx);
// // postprocess texture
// float *temp = CALLOC(w * h * 4, sizeof(float));
// for (int i = 0; i < 16; i++)
// {
// lmImageDilate(data, temp, w, h, 4);
// lmImageDilate(temp, data, w, h, 4);
// }
// lmImageSmooth(data, temp, w, h, 4);
// lmImageDilate(temp, data, w, h, 4);
// lmImagePower(data, w, h, 4, 1.0f / 2.2f, 0x7); // gamma correct color channels
// FREE(temp);
// // save result to a file
// if (lmImageSaveTGAf("result.tga", data, w, h, 4, 1.0f))
// printf("Saved result.tga\n");
// // upload result
// glBindTexture(GL_TEXTURE_2D, scene->lightmap);
// glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_FLOAT, data);
// FREE(data);
}
void lightmap_clear(lightmap_t *lm) {
ASSERT(lm->ready);
glBindTexture(GL_TEXTURE_2D, lm->lightmap.id);
unsigned char emissive[] = { 0, 0, 0, 255 };
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, emissive);
glBindTexture(GL_TEXTURE_2D, 0);
}
#line 0
#line 1 "v4k_renderdd.c"

View File

@ -1997,9 +1997,6 @@ API void script_call(const char *lua_function);
API bool script_tests();
// -----------------------------------------------------------------------------
// script framework
enum {
SCRIPT_LUA = 1,
SCRIPT_DEBUGGER = 2,
@ -3581,6 +3578,7 @@ typedef struct model_t {
handle *textures;
char **texture_names;
array(material_t) materials;
texture_t *lightmap;
unsigned num_meshes;
unsigned num_triangles;
@ -3591,7 +3589,7 @@ typedef struct model_t {
float curframe;
mat44 pivot;
int stride; // usually 60 bytes (12*4+4*3) for a p3 u2 n3 t4 i4B w4B c4B vertex stream
int stride; // usually 68 bytes for a p3 u2 u2 n3 t4 i4B w4B c4B vertex stream
void *verts;
int num_verts;
handle vao, ibo, vbo, vao_instanced;
@ -3640,11 +3638,22 @@ API anims_t animations(const char *pathfile, int flags);
// -----------------------------------------------------------------------------
// lightmapping utils
// @fixme: support xatlas uv packing
typedef struct lightmap_t {
struct lm_context *ctx; // private
bool ready;
texture_t lightmap; //@fixme: do we need it per-model?
array(model_t*) models;
} lightmap_t;
API lightmap_t lightmap(int hmsize /*64*/, float near, float far, vec3 color /*1,1,1 for AO*/, int passes /*2*/, float threshold /*0.01f*/, float distmod /*0.0f*/);
API void lightmap_setup(lightmap_t *lm, int w, int h);
API void lightmap_addmodel(lightmap_t *lm, model_t *m);
API void lightmap_bake(lightmap_t *lm, int bounces, void (*drawscene)(lightmap_t *lm, float *view, float *proj, void *userdata), void *userdata);
API void lightmap_clear(lightmap_t *lm);
API void lightmap_destroy(lightmap_t *lm);
// -----------------------------------------------------------------------------
// skyboxes