naev 0.12.5
nlua_shader.c
Go to the documentation of this file.
1/*
2 * See Licensing and Copyright notice in naev.h
3 */
10#include <lauxlib.h>
12
13#include "nlua_shader.h"
14
15#include "array.h"
16#include "nlua_tex.h"
17#include "nluadef.h"
18#include "render.h"
19
20/* Shader metatable methods. */
21static int shaderL_gc( lua_State *L );
22static int shaderL_eq( lua_State *L );
23static int shaderL_new( lua_State *L );
24static int shaderL_send( lua_State *L );
25static int shaderL_sendRaw( lua_State *L );
26static int shaderL_hasUniform( lua_State *L );
27static int shaderL_addPostProcess( lua_State *L );
28static int shaderL_rmPostProcess( lua_State *L );
29
30static const luaL_Reg shaderL_methods[] = {
31 { "__gc", shaderL_gc },
32 { "__eq", shaderL_eq },
33 { "new", shaderL_new },
34 { "send", shaderL_send },
35 { "sendRaw", shaderL_sendRaw },
36 { "hasUniform", shaderL_hasUniform },
37 { "addPPShader", shaderL_addPostProcess },
38 { "rmPPShader", shaderL_rmPostProcess },
39 { 0, 0 } };
40
41/* Useful stuff. */
42static int shader_compareUniform( const void *a, const void *b );
43static int shader_searchUniform( const void *id, const void *u );
44static LuaUniform_t *shader_getUniform( const LuaShader_t *ls,
45 const char *name );
46static int shaderL_sendHelper( lua_State *L, int ignore_missing );
47
54int nlua_loadShader( nlua_env env )
55{
56 nlua_register( env, SHADER_METATABLE, shaderL_methods, 1 );
57 return 0;
58}
59
72LuaShader_t *lua_toshader( lua_State *L, int ind )
73{
74 return (LuaShader_t *)lua_touserdata( L, ind );
75}
76
83LuaShader_t *luaL_checkshader( lua_State *L, int ind )
84{
85 if ( lua_isshader( L, ind ) )
86 return lua_toshader( L, ind );
87 luaL_typerror( L, ind, SHADER_METATABLE );
88 return NULL;
89}
90
97LuaShader_t *lua_pushshader( lua_State *L, LuaShader_t shader )
98{
99 LuaShader_t *c = (LuaShader_t *)lua_newuserdata( L, sizeof( LuaShader_t ) );
100 *c = shader;
101 luaL_getmetatable( L, SHADER_METATABLE );
102 lua_setmetatable( L, -2 );
103 return c;
104}
105
112int lua_isshader( lua_State *L, int ind )
113{
114 int ret;
115
116 if ( lua_getmetatable( L, ind ) == 0 )
117 return 0;
118 lua_getfield( L, LUA_REGISTRYINDEX, SHADER_METATABLE );
119
120 ret = 0;
121 if ( lua_rawequal( L, -1, -2 ) ) /* does it have the correct mt? */
122 ret = 1;
123
124 lua_pop( L, 2 ); /* remove both metatables */
125 return ret;
126}
127
134static int shaderL_gc( lua_State *L )
135{
136 LuaShader_t *shader = luaL_checkshader( L, 1 );
137 if ( shader->pp_id > 0 )
138 render_postprocessRm( shader->pp_id );
139 glDeleteProgram( shader->program );
140 array_free( shader->tex );
141 free( shader->uniforms );
142 if ( gl_checkErr() )
143 NLUA_ERROR( L, _( "OpenGL Error!" ) );
144 return 0;
145}
146
155static int shaderL_eq( lua_State *L )
156{
157 const LuaShader_t *f1, *f2;
158 f1 = luaL_checkshader( L, 1 );
159 f2 = luaL_checkshader( L, 2 );
160 lua_pushboolean( L, ( memcmp( f1, f2, sizeof( LuaShader_t ) ) == 0 ) );
161 return 1;
162}
163
164/*
165 * For qsort.
166 */
167static int shader_compareUniform( const void *a, const void *b )
168{
169 const LuaUniform_t *u1, *u2;
170 u1 = (const LuaUniform_t *)a;
171 u2 = (const LuaUniform_t *)b;
172 return strcmp( u1->name, u2->name );
173}
174
175static int shader_searchUniform( const void *id, const void *u )
176{
177 return strcmp( (const char *)id, ( (LuaUniform_t *)u )->name );
178}
179
180static LuaUniform_t *shader_getUniform( const LuaShader_t *ls,
181 const char *name )
182{
183 return bsearch( name, ls->uniforms, ls->nuniforms, sizeof( LuaUniform_t ),
184 shader_searchUniform );
185}
186
195static int shaderL_new( lua_State *L )
196{
197 LuaShader_t shader;
198 const char *pixelcode, *vertexcode;
199 GLint ntex;
200 GLsizei length;
201
202 /* Get arguments. */
203 pixelcode = luaL_checkstring( L, 1 );
204 vertexcode = luaL_checkstring( L, 2 );
205
206 /* Initialize. */
207 memset( &shader, 0, sizeof( shader ) );
208
209 /* Do from string. */
210 shader.program = gl_program_vert_frag_string(
211 vertexcode, strlen( vertexcode ), pixelcode, strlen( pixelcode ) );
212 if ( shader.program == 0 )
213 return NLUA_ERROR( L, _( "shader failed to compile!" ) );
214
215 /* Set up defaults. */
216#define ATTRIB( name ) \
217 shader.name = glGetAttribLocation( shader.program, #name )
218#define UNIFORM( name ) \
219 shader.name = glGetUniformLocation( shader.program, #name )
220 UNIFORM( ViewSpaceFromLocal );
221 UNIFORM( ClipSpaceFromView );
222 UNIFORM( ClipSpaceFromLocal );
223 UNIFORM( ViewNormalFromLocal );
224 UNIFORM( MainTex );
225 UNIFORM( ConstantColour );
226 UNIFORM( love_ScreenSize );
227 ATTRIB( VertexPosition );
228 ATTRIB( VertexTexCoord );
229 ATTRIB( VertexColour );
230#undef ATTRIB
231#undef UNIFORM
232
233 /* Do other uniforms. */
234 glGetProgramiv( shader.program, GL_ACTIVE_UNIFORMS, &shader.nuniforms );
235 shader.uniforms = calloc( shader.nuniforms, sizeof( LuaUniform_t ) );
236 ntex = 0;
237 for ( GLint i = 0; i < shader.nuniforms; i++ ) {
238 LuaUniform_t *u = &shader.uniforms[i];
239 glGetActiveUniform( shader.program, (GLuint)i, SHADER_NAME_MAXLEN,
240 &length, &u->size, &u->type, u->name );
241 u->id = glGetUniformLocation( shader.program, u->name );
242 u->tex = -1;
243
244 /* Textures need special care. */
245 if ( ( u->type == GL_SAMPLER_2D ) &&
246 ( strcmp( u->name, "MainTex" ) != 0 ) ) {
247 if ( shader.tex == NULL )
248 shader.tex = array_create( LuaTexture_t );
249 LuaTexture_t *t = &array_grow( &shader.tex );
250 ntex++;
251 t->active = GL_TEXTURE0 + ntex;
252 t->texid = 0;
253 t->uniform = u->id;
254 t->value = ntex;
255 u->tex = ntex - 1;
256 }
257 }
258 qsort( shader.uniforms, shader.nuniforms, sizeof( LuaUniform_t ),
259 shader_compareUniform );
260
261 if ( gl_checkErr() )
262 NLUA_ERROR( L, _( "OpenGL Error!" ) );
263
264 lua_pushshader( L, shader );
265 return 1;
266}
267
271void shader_parseUniformArgsFloat( GLfloat values[4], lua_State *L, int idx,
272 int n )
273{
274 if ( lua_istable( L, idx ) ) {
275 for ( int j = 0; j < n; j++ ) {
276 lua_pushnumber( L, j + 1 );
277 lua_gettable( L, idx );
278 values[j] = luaL_checknumber( L, -1 );
279 }
280 lua_pop( L, n );
281 } else {
282 for ( int j = 0; j < n; j++ )
283 values[j] = luaL_checknumber( L, idx + j );
284 }
285}
286
290void shader_parseUniformArgsInt( GLint values[4], lua_State *L, int idx, int n )
291{
292 if ( lua_istable( L, idx ) ) {
293 for ( int j = 0; j < n; j++ ) {
294 lua_pushnumber( L, j + 1 );
295 lua_gettable( L, idx );
296 values[j] = luaL_checkint( L, -1 );
297 }
298 lua_pop( L, n );
299 } else {
300 for ( int j = 0; j < n; j++ )
301 values[j] = luaL_checkint( L, idx + j );
302 }
303}
304
313static int shaderL_send( lua_State *L )
314{
315 return shaderL_sendHelper( L, 0 );
316}
317
326static int shaderL_sendRaw( lua_State *L )
327{
328 return shaderL_sendHelper( L, 1 );
329}
330
334static int shaderL_sendHelper( lua_State *L, int ignore_missing )
335{
336 const LuaShader_t *ls;
337 LuaUniform_t *u;
338 const char *name;
339 int idx;
340 GLfloat values[4];
341 GLint ivalues[4];
342 const glTexture *tex;
343
344 ls = luaL_checkshader( L, 1 );
345 name = luaL_checkstring( L, 2 );
346
347 u = shader_getUniform( ls, name );
348 if ( u == NULL ) {
349 if ( ignore_missing )
350 return 0;
351 return NLUA_ERROR( L, _( "Shader does not have uniform '%s'!" ), name );
352 }
353
354 /* With OpenGL 4.1 or ARB_separate_shader_objects, there
355 * is no need to set the program first. */
356 glUseProgram( ls->program );
357 idx = 3;
358 switch ( u->type ) {
359 case GL_FLOAT:
360 shader_parseUniformArgsFloat( values, L, idx, 1 );
361 glUniform1f( u->id, values[0] );
362 break;
363 case GL_FLOAT_VEC2:
364 shader_parseUniformArgsFloat( values, L, idx, 2 );
365 glUniform2f( u->id, values[0], values[1] );
366 break;
367 case GL_FLOAT_VEC3:
368 shader_parseUniformArgsFloat( values, L, idx, 3 );
369 glUniform3f( u->id, values[0], values[1], values[2] );
370 break;
371 case GL_FLOAT_VEC4:
372 shader_parseUniformArgsFloat( values, L, idx, 4 );
373 glUniform4f( u->id, values[0], values[1], values[2], values[3] );
374 break;
375
376 case GL_INT:
377 shader_parseUniformArgsInt( ivalues, L, idx, 1 );
378 glUniform1i( u->id, ivalues[0] );
379 break;
380 case GL_INT_VEC2:
381 shader_parseUniformArgsInt( ivalues, L, idx, 2 );
382 glUniform2i( u->id, ivalues[0], ivalues[1] );
383 break;
384 case GL_INT_VEC3:
385 shader_parseUniformArgsInt( ivalues, L, idx, 3 );
386 glUniform3i( u->id, ivalues[0], ivalues[1], ivalues[2] );
387 break;
388 case GL_INT_VEC4:
389 shader_parseUniformArgsInt( ivalues, L, idx, 4 );
390 glUniform4i( u->id, ivalues[0], ivalues[1], ivalues[2], ivalues[3] );
391 break;
392
393 case GL_SAMPLER_2D:
394 tex = luaL_checktex( L, idx );
395 ls->tex[u->tex].texid = tex->texture;
396 break;
397
398 default:
399 NLUA_WARN( L,
400 _( "Unsupported shader uniform type '%d' for uniform '%s'. "
401 "Ignoring." ),
402 u->type, u->name );
403 }
404 glUseProgram( 0 );
405
406 if ( gl_checkErr() )
407 NLUA_ERROR( L, _( "OpenGL Error!" ) );
408
409 return 0;
410}
411
420static int shaderL_hasUniform( lua_State *L )
421{
422 /* Parameters. */
423 const LuaShader_t *ls = luaL_checkshader( L, 1 );
424 const char *name = luaL_checkstring( L, 2 );
425
426 /* Search. */
427 lua_pushboolean( L, shader_getUniform( ls, name ) != NULL );
428 return 1;
429}
430
441static int shaderL_addPostProcess( lua_State *L )
442{
443 LuaShader_t *ls = luaL_checkshader( L, 1 );
444 const char *str = luaL_optstring( L, 2, "final" );
445 int priority = luaL_optinteger( L, 3, 0 );
446 int layer = PP_LAYER_FINAL;
447
448 if ( strcmp( str, "final" ) == 0 )
449 layer = PP_LAYER_FINAL;
450 else if ( strcmp( str, "game" ) == 0 )
451 layer = PP_LAYER_GAME;
452 else if ( strcmp( str, "gui" ) == 0 )
453 layer = PP_LAYER_GUI;
454 else if ( strcmp( str, "core" ) == 0 )
455 layer = PP_LAYER_CORE;
456 else
457 return NLUA_ERROR( L,
458 _( "Layer was '%s', but must be one of 'final', "
459 "'game', 'gui', or 'core'." ),
460 str );
461
462 if ( ls->pp_id == 0 )
463 ls->pp_id = render_postprocessAdd( ls, layer, priority, 0 );
464 lua_pushboolean( L, ls->pp_id > 0 );
465 return 1;
466}
467
475static int shaderL_rmPostProcess( lua_State *L )
476{
477 LuaShader_t *ls = luaL_checkshader( L, 1 );
478 lua_pushboolean( L, render_postprocessRm( ls->pp_id ) );
479 ls->pp_id = 0;
480 return 1;
481}
Provides macros to work with dynamic arrays.
#define array_free(ptr_array)
Frees memory allocated and sets array to NULL.
Definition array.h:170
#define array_grow(ptr_array)
Increases the number of elements by one and returns the last element.
Definition array.h:122
#define array_create(basic_type)
Creates a new dynamic array of ‘basic_type’.
Definition array.h:93
LuaShader_t * lua_pushshader(lua_State *L, LuaShader_t shader)
Pushes a shader on the stack.
Definition nlua_shader.c:97
void shader_parseUniformArgsFloat(GLfloat values[4], lua_State *L, int idx, int n)
Helper to parse up float vector (or arguments).
static int shaderL_send(lua_State *L)
Allows setting values of uniforms for a shader. Errors out if the uniform is unknown or unused (as in...
static int shaderL_sendRaw(lua_State *L)
Allows setting values of uniforms for a shader, while ignoring unknown (or unused) uniforms.
void shader_parseUniformArgsInt(GLint values[4], lua_State *L, int idx, int n)
Helper to parse up integer vector (or arguments).
static const luaL_Reg shaderL_methods[]
Definition nlua_shader.c:30
static int shaderL_eq(lua_State *L)
Compares two shaders to see if they are the same.
static int shaderL_new(lua_State *L)
Creates a new shader.
static int shaderL_hasUniform(lua_State *L)
Checks to see if a shader has a uniform.
static int shaderL_addPostProcess(lua_State *L)
Sets a shader as a post-processing shader.
LuaShader_t * lua_toshader(lua_State *L, int ind)
Lua bindings to interact with shaders.
Definition nlua_shader.c:72
int nlua_loadShader(nlua_env env)
Loads the shader library.
Definition nlua_shader.c:54
LuaShader_t * luaL_checkshader(lua_State *L, int ind)
Gets shader at index or raises error if there is no shader at index.
Definition nlua_shader.c:83
static int shaderL_rmPostProcess(lua_State *L)
Removes a shader as a post-processing shader.
static int shaderL_sendHelper(lua_State *L, int ignore_missing)
Helper to set the uniform while handling unknown/inactive uniforms.
int lua_isshader(lua_State *L, int ind)
Checks to see if ind is a shader.
static int shaderL_gc(lua_State *L)
Frees a shader.
glTexture * luaL_checktex(lua_State *L, int ind)
Gets texture at index or raises error if there is no texture at index.
Definition nlua_tex.c:97
static const double c[]
Definition rng.c:256
unsigned int pp_id
Definition nlua_shader.h:53
Abstraction for rendering sprite sheets.
Definition opengl_tex.h:43
GLuint texture
Definition opengl_tex.h:59