2023-11-30 13:33:49 +00:00
# include "v4k.h"
# define LIGHTMAPPER_IMPLEMENTATION
2023-12-01 11:01:03 +00:00
//#define LM_DEBUG_INTERPOLATION
2023-11-30 13:33:49 +00:00
# include "3rd_lightmapper.h"
# define scene_t scene_t2
typedef struct {
float p [ 3 ] ;
float t [ 2 ] ;
} vertex_t ;
typedef struct
{
GLuint program ;
GLint u_lightmap ;
GLint u_projection ;
GLint u_view ;
GLuint lightmap ;
int w , h ;
GLuint vao , vbo , ibo ;
vertex_t * vertices ;
unsigned short * indices ;
unsigned int vertexCount , indexCount ;
} scene_t ;
static int initScene ( scene_t * scene ) ;
static void drawScene ( scene_t * scene , float * view , float * projection ) ;
static void destroyScene ( scene_t * scene ) ;
static int bake ( scene_t * scene )
{
lm_context * ctx = lmCreate (
64 , // hemisphere resolution (power of two, max=512)
0.001f , 100.0f , // zNear, zFar of hemisphere cameras
1.0f , 1.0f , 1.0f , // background color (white for ambient occlusion)
2 , 0.01f , // lightmap interpolation threshold (small differences are interpolated rather than sampled)
// check debug_interpolation.tga for an overview of sampled (red) vs interpolated (green) pixels.
0.0f ) ; // modifier for camera-to-surface distance for hemisphere rendering.
// tweak this to trade-off between interpolated normals quality and other artifacts (see declaration).
if ( ! ctx )
{
fprintf ( stderr , " Error: Could not initialize lightmapper. \n " ) ;
return 0 ;
}
int w = scene - > w , h = scene - > h ;
float * data = CALLOC ( w * h * 4 , sizeof ( float ) ) ;
for ( int b = 0 ; b < 1 ; b + + ) {
memset ( data , 0 , w * h * 4 ) ;
lmSetTargetLightmap ( ctx , data , w , h , 4 ) ;
lmSetGeometry ( ctx , NULL , // no transformation in this example
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 ( " \r Finished 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 ) ;
return 1 ;
}
static void fpsCameraViewMatrix ( float * view ) ;
static void mainLoop ( scene_t * scene )
{
glViewport ( 0 , 0 , window_width ( ) , window_height ( ) ) ;
// camera for glfw window
float view [ 16 ] , projection [ 16 ] ;
fpsCameraViewMatrix ( view ) ;
perspective44 ( projection , 45.0f , window_aspect ( ) , 0.01f , 100.0f ) ;
// draw to screen with a blueish sky
glClearColor ( 0.6f , 0.8f , 1.0f , 1.0f ) ;
glClear ( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ) ;
drawScene ( scene , view , projection ) ;
}
int main ( )
{
window_create ( 0.5 , WINDOW_VSYNC_DISABLED ) ;
scene_t scene = { 0 } ;
if ( ! initScene ( & scene ) )
{
fprintf ( stderr , " Could not initialize scene. \n " ) ;
return EXIT_FAILURE ;
}
window_title ( " AO Baking demo " ) ;
while ( window_swap ( ) )
{
mainLoop ( & scene ) ;
if ( ui_panel ( " Lightmapper " , PANEL_OPEN ) ) {
ui_label2 ( " Freecam " , " Mouse + W/A/S/D/E/Q keys " ) ;
ui_label ( " Warning " ICON_MD_WARNING " @This will take a few seconds and bake a lightmap illuminated by: The mesh itself (initially black) + A white sky (1.0f, 1.0f, 1.0f) " ) ;
if ( ui_button ( " Bake 1 light bounce " ) ) {
bake ( & scene ) ;
}
ui_panel_end ( ) ;
}
}
destroyScene ( & scene ) ;
}
// helpers ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
static int loadSimpleObjFile ( const char * filename , vertex_t * * vertices , unsigned int * vertexCount , unsigned short * * indices , unsigned int * indexCount ) ;
static int initScene ( scene_t * scene )
{
// load mesh
if ( ! loadSimpleObjFile ( " demos/art/meshes/gazebo.obj " , & scene - > vertices , & scene - > vertexCount , & scene - > indices , & scene - > indexCount ) )
{
fprintf ( stderr , " Error loading obj file \n " ) ;
return 0 ;
}
glGenVertexArrays ( 1 , & scene - > vao ) ;
glBindVertexArray ( scene - > vao ) ;
glGenBuffers ( 1 , & scene - > vbo ) ;
glBindBuffer ( GL_ARRAY_BUFFER , scene - > vbo ) ;
glBufferData ( GL_ARRAY_BUFFER , scene - > vertexCount * sizeof ( vertex_t ) , scene - > vertices , GL_STATIC_DRAW ) ;
glGenBuffers ( 1 , & scene - > ibo ) ;
glBindBuffer ( GL_ELEMENT_ARRAY_BUFFER , scene - > ibo ) ;
glBufferData ( GL_ELEMENT_ARRAY_BUFFER , scene - > indexCount * sizeof ( unsigned short ) , scene - > indices , GL_STATIC_DRAW ) ;
glEnableVertexAttribArray ( 0 ) ;
glVertexAttribPointer ( 0 , 3 , GL_FLOAT , GL_FALSE , sizeof ( vertex_t ) , ( void * ) offsetof ( vertex_t , p ) ) ;
glEnableVertexAttribArray ( 1 ) ;
glVertexAttribPointer ( 1 , 2 , GL_FLOAT , GL_FALSE , sizeof ( vertex_t ) , ( void * ) offsetof ( vertex_t , t ) ) ;
// create lightmap texture
scene - > w = 654 ;
scene - > h = 654 ;
glGenTextures ( 1 , & scene - > lightmap ) ;
glBindTexture ( GL_TEXTURE_2D , scene - > lightmap ) ;
glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_MIN_FILTER , GL_LINEAR ) ;
glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_MAG_FILTER , GL_LINEAR ) ;
glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_WRAP_S , GL_CLAMP_TO_EDGE ) ;
glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_WRAP_T , GL_CLAMP_TO_EDGE ) ;
unsigned char emissive [ ] = { 0 , 0 , 0 , 255 } ;
glTexImage2D ( GL_TEXTURE_2D , 0 , GL_RGBA , 1 , 1 , 0 , GL_RGBA , GL_UNSIGNED_BYTE , emissive ) ;
// load shader
const char * vp =
" in vec3 a_position; \n "
" in vec2 a_texcoord; \n "
" uniform mat4 u_view; \n "
" uniform mat4 u_projection; \n "
" out vec2 v_texcoord; \n "
" void main() \n "
" { \n "
" gl_Position = u_projection * (u_view * vec4(a_position, 1.0)); \n "
" v_texcoord = a_texcoord; \n "
" } \n " ;
const char * fp =
" in vec2 v_texcoord; \n "
" uniform sampler2D u_lightmap; \n "
" out vec4 o_color; \n "
" void main() \n "
" { \n "
" o_color = vec4(texture(u_lightmap, v_texcoord).rgb, gl_FrontFacing ? 1.0 : 0.0); \n "
" } \n " ;
scene - > program = shader ( vp , fp , " a_position,a_texcoord " , " o_color " , NULL ) ;
if ( ! scene - > program )
{
fprintf ( stderr , " Error loading shader \n " ) ;
return 0 ;
}
scene - > u_view = glGetUniformLocation ( scene - > program , " u_view " ) ;
scene - > u_projection = glGetUniformLocation ( scene - > program , " u_projection " ) ;
scene - > u_lightmap = glGetUniformLocation ( scene - > program , " u_lightmap " ) ;
return 1 ;
}
static void drawScene ( scene_t * scene , float * view , float * projection )
{
glEnable ( GL_DEPTH_TEST ) ;
glUseProgram ( scene - > program ) ;
glUniform1i ( scene - > u_lightmap , 0 ) ;
glUniformMatrix4fv ( scene - > u_projection , 1 , GL_FALSE , projection ) ;
glUniformMatrix4fv ( scene - > u_view , 1 , GL_FALSE , view ) ;
glBindTexture ( GL_TEXTURE_2D , scene - > lightmap ) ;
glBindVertexArray ( scene - > vao ) ;
glDrawElements ( GL_TRIANGLES , scene - > indexCount , GL_UNSIGNED_SHORT , 0 ) ;
}
static void destroyScene ( scene_t * scene )
{
FREE ( scene - > vertices ) ;
FREE ( scene - > indices ) ;
glDeleteVertexArrays ( 1 , & scene - > vao ) ;
glDeleteBuffers ( 1 , & scene - > vbo ) ;
glDeleteBuffers ( 1 , & scene - > ibo ) ;
glDeleteTextures ( 1 , & scene - > lightmap ) ;
glDeleteProgram ( scene - > program ) ;
}
static int loadSimpleObjFile ( const char * filename , vertex_t * * vertices , unsigned int * vertexCount , unsigned short * * indices , unsigned int * indexCount )
{
FILE * file = fopen ( filename , " rt " ) ;
if ( ! file )
return 0 ;
char line [ 1024 ] ;
// first pass
unsigned int np = 0 , nn = 0 , nt = 0 , nf = 0 ;
while ( ! feof ( file ) )
{
fgets ( line , 1024 , file ) ;
if ( line [ 0 ] = = ' # ' ) continue ;
if ( line [ 0 ] = = ' v ' )
{
if ( line [ 1 ] = = ' ' ) { np + + ; continue ; }
if ( line [ 1 ] = = ' n ' ) { nn + + ; continue ; }
if ( line [ 1 ] = = ' t ' ) { nt + + ; continue ; }
assert ( ! " unknown vertex attribute " ) ;
}
if ( line [ 0 ] = = ' f ' ) { nf + + ; continue ; }
assert ( ! " unknown identifier " ) ;
}
assert ( np & & np = = nn & & np = = nt & & nf ) ; // only supports obj files without separately indexed vertex attributes
// allocate memory
* vertexCount = np ;
* vertices = CALLOC ( np , sizeof ( vertex_t ) ) ;
* indexCount = nf * 3 ;
* indices = CALLOC ( nf * 3 , sizeof ( unsigned short ) ) ;
// second pass
fseek ( file , 0 , SEEK_SET ) ;
unsigned int cp = 0 , cn = 0 , ct = 0 , cf = 0 ;
while ( ! feof ( file ) )
{
fgets ( line , 1024 , file ) ;
if ( line [ 0 ] = = ' # ' ) continue ;
if ( line [ 0 ] = = ' v ' )
{
if ( line [ 1 ] = = ' ' ) { float * p = ( * vertices ) [ cp + + ] . p ; char * e1 , * e2 ; p [ 0 ] = ( float ) strtod ( line + 2 , & e1 ) ; p [ 1 ] = ( float ) strtod ( e1 , & e2 ) ; p [ 2 ] = ( float ) strtod ( e2 , 0 ) ; continue ; }
if ( line [ 1 ] = = ' n ' ) { /*float *n = (*vertices)[cn++].n; char *e1, *e2; n[0] = (float)strtod(line + 3, &e1); n[1] = (float)strtod(e1, &e2); n[2] = (float)strtod(e2, 0);*/ continue ; } // no normals needed
if ( line [ 1 ] = = ' t ' ) { float * t = ( * vertices ) [ ct + + ] . t ; char * e1 ; t [ 0 ] = ( float ) strtod ( line + 3 , & e1 ) ; t [ 1 ] = ( float ) strtod ( e1 , 0 ) ; continue ; }
assert ( ! " unknown vertex attribute " ) ;
}
if ( line [ 0 ] = = ' f ' )
{
unsigned short * tri = ( * indices ) + cf ;
cf + = 3 ;
char * e1 , * e2 , * e3 = line + 1 ;
for ( int i = 0 ; i < 3 ; i + + )
{
unsigned long pi = strtoul ( e3 + 1 , & e1 , 10 ) ;
assert ( e1 [ 0 ] = = ' / ' ) ;
unsigned long ti = strtoul ( e1 + 1 , & e2 , 10 ) ;
assert ( e2 [ 0 ] = = ' / ' ) ;
unsigned long ni = strtoul ( e2 + 1 , & e3 , 10 ) ;
assert ( pi = = ti & & pi = = ni ) ;
tri [ i ] = ( unsigned short ) ( pi - 1 ) ;
}
continue ;
}
assert ( ! " unknown identifier " ) ;
}
fclose ( file ) ;
return 1 ;
}
static void fpsCameraViewMatrix ( float * view )
{
// initial camera config
static float position [ ] = { 0.0f , 0.3f , 1.5f } ;
static float rotation [ ] = { 0.0f , 0.0f } ;
// mouse look
static double lastMouse [ ] = { 0.0 , 0.0 } ;
double mouse_coord [ 2 ] ;
mouse_coord [ 0 ] = input ( MOUSE_X ) ;
mouse_coord [ 1 ] = input ( MOUSE_Y ) ;
if ( input ( MOUSE_L ) )
{
rotation [ 0 ] + = ( float ) ( mouse_coord [ 1 ] - lastMouse [ 1 ] ) * - 0.2f ;
rotation [ 1 ] + = ( float ) ( mouse_coord [ 0 ] - lastMouse [ 0 ] ) * - 0.2f ;
}
lastMouse [ 0 ] = mouse_coord [ 0 ] ;
lastMouse [ 1 ] = mouse_coord [ 1 ] ;
float rotationY [ 16 ] , rotationX [ 16 ] , rotationYX [ 16 ] ;
rotation44 ( rotationX , rotation [ 0 ] , 1.0f , 0.0f , 0.0f ) ;
rotation44 ( rotationY , rotation [ 1 ] , 0.0f , 1.0f , 0.0f ) ;
multiply44x2 ( rotationYX , rotationY , rotationX ) ;
// keyboard movement (WSADEQ)
float speed = input ( KEY_SHIFT ) ? 0.1f : 0.01f ;
vec3 movement = { 0 } ;
if ( input ( KEY_W ) ) movement . z - = speed ;
if ( input ( KEY_S ) ) movement . z + = speed ;
if ( input ( KEY_A ) ) movement . x - = speed ;
if ( input ( KEY_D ) ) movement . x + = speed ;
if ( input ( KEY_E ) ) movement . y - = speed ;
if ( input ( KEY_Q ) ) movement . y + = speed ;
vec3 worldMovement = transform344 ( rotationYX , movement ) ;
position [ 0 ] + = worldMovement . x ;
position [ 1 ] + = worldMovement . y ;
position [ 2 ] + = worldMovement . z ;
// construct view matrix
float inverseRotation [ 16 ] , inverseTranslation [ 16 ] ;
transpose44 ( inverseRotation , rotationYX ) ;
translation44 ( inverseTranslation , - position [ 0 ] , - position [ 1 ] , - position [ 2 ] ) ;
multiply44x2 ( view , inverseRotation , inverseTranslation ) ; // = inverse(translation(position) * rotationYX);
}
#if 0
# ######################################################################
76702 17.93 % rendered hemicubes integrated to lightmap texels .
179388 41.94 % interpolated lightmap texels .
171626 40.13 % wasted lightmap texels .
29.95 % of used texels were rendered .
# ######################################################################
Finished baking 731 triangles .
Saved result . tga
vs
# ######################################################################
124 0.05 % rendered hemicubes integrated to lightmap texels .
201 0.08 % interpolated lightmap texels .
261947 99.88 % wasted lightmap texels .
38.15 % of used texels were rendered .
# ######################################################################
Finished baking 731 triangles .
Saved result . tga
# endif