Forked from
tome / Tales of MajEyal
11476 commits behind the upstream repository.
-
dg authored
git-svn-id: http://svn.net-core.org/repos/t-engine4@3628 51575b47-30f0-44d4-a5cc-537603b46e54
dg authoredgit-svn-id: http://svn.net-core.org/repos/t-engine4@3628 51575b47-30f0-44d4-a5cc-537603b46e54
particles.c 24.63 KiB
/*
TE4 - T-Engine 4
Copyright (C) 2009, 2010, 2011 Nicolas Casalini
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Nicolas Casalini "DarkGod"
darkgod@te4.org
*/
#include "display.h"
#include "lua.h"
#include "lauxlib.h"
#include "lualib.h"
#include "core_lua.h"
#include "auxiliar.h"
#include "types.h"
#include "particles.h"
#include "script.h"
#include <math.h>
#include "SFMT.h"
#include "tSDL.h"
#include "physfs.h"
#include "physfsrwops.h"
#define rng(x, y) (x + rand_div(1 + y - x))
#define PARTICLE_ETERNAL 999999
#define PARTICLES_PER_ARRAY 1000
int MAX_THREADS = 1;
extern int nb_cpus;
static particle_thread *threads = NULL;
static int textures_ref = LUA_NOREF;
static int nb_threads = 0;
static int cur_thread = 0;
void thread_add(particles_type *ps);
static void getinitfield(lua_State *L, const char *key, int *min, int *max)
{
lua_pushstring(L, key);
lua_gettable(L, -2);
lua_pushnumber(L, 1);
lua_gettable(L, -2);
*min = (int)lua_tonumber(L, -1);
lua_pop(L, 1);
lua_pushnumber(L, 2);
lua_gettable(L, -2);
*max = (int)lua_tonumber(L, -1);
lua_pop(L, 1);
// printf("%s :: %d %d\n", key, (int)*min, (int)*max);
lua_pop(L, 1);
}
static void getparticulefield(lua_State *L, const char *k, float *v)
{
lua_pushstring(L, k);
lua_gettable(L, -2);
*v = (float)lua_tonumber(L, -1);
// printf("emit %s :: %f\n", k, *v);
lua_pop(L, 1);
}
// Runs into main thread
static int particles_new(lua_State *L)
{
const char *name_def = luaL_checkstring(L, 1);
const char *args = luaL_checkstring(L, 2);
// float zoom = luaL_checknumber(L, 3);
int density = luaL_checknumber(L, 4);
particles_type *ps = (particles_type*)lua_newuserdata(L, sizeof(particles_type));
auxiliar_setclass(L, "core{particles}", -1);
ps->lock = SDL_CreateMutex();
ps->name_def = strdup(name_def);
ps->args = strdup(args);
ps->density = density;
ps->alive = TRUE;
ps->i_want_to_die = FALSE;
ps->l = NULL;
ps->texcoords = NULL;
ps->vertices = NULL;
ps->colors = NULL;
ps->particles = NULL;
ps->init = FALSE;
thread_add(ps);
return 1;
}
// Runs into main thread
static int particles_free(lua_State *L)
{
particles_type *ps = (particles_type*)auxiliar_checkclass(L, "core{particles}", 1);
plist *l = ps->l;
// printf("Deleting particle from main lua state %x :: %x\n", (int)ps->l, (int)ps);
if (l && l->pt) SDL_mutexP(l->pt->lock);
ps->alive = FALSE;
if (l) l->ps = NULL;
ps->l = NULL;
SDL_DestroyMutex(ps->lock);
if (ps->texcoords) { free(ps->texcoords); ps->texcoords = NULL; }
if (ps->vertices) { free(ps->vertices); ps->vertices = NULL; }
if (ps->colors) { free(ps->colors); ps->colors = NULL; }
if (ps->particles) { free(ps->particles); ps->particles = NULL; }
if (l && l->pt) SDL_mutexV(l->pt->lock);
lua_pushnumber(L, 1);
return 1;
}
// Runs into main thread
static int particles_is_alive(lua_State *L)
{
particles_type *ps = (particles_type*)auxiliar_checkclass(L, "core{particles}", 1);
lua_pushboolean(L, ps->alive);
return 1;
}
// Runs into main thread
static int particles_die(lua_State *L)
{
particles_type *ps = (particles_type*)auxiliar_checkclass(L, "core{particles}", 1);
ps->i_want_to_die = TRUE;
return 1;
}
// Load a texture for the particle system; runs in the main thread
extern void make_texture_for_surface(SDL_Surface *s, int *fw, int *fh);
extern void copy_surface_to_texture(SDL_Surface *s);
GLuint particle_load_texture(const char *file)
{
char path[512];
char *base = "/data/gfx/";
strcpy(path, base);
strcpy(path + strlen(base), file);
strcpy(path + strlen(base) + strlen(file), ".png");
SDL_Surface *s = IMG_Load_RW(PHYSFSRWOPS_openRead(path), TRUE);
if (!s) return 0;
GLuint t = 0;
glGenTextures(1, &t);
tglBindTexture(GL_TEXTURE_2D, t);
int fw, fh;
make_texture_for_surface(s, &fw, &fh);
copy_surface_to_texture(s);
SDL_FreeSurface(s);
printf("Loaded texture for particles: %s => %d\n", path, t);
return t;
}
// Runs into main thread
static int particles_to_screen(lua_State *L)
{
particles_type *ps = (particles_type*)auxiliar_checkclass(L, "core{particles}", 1);
int x = luaL_checknumber(L, 2);
int y = luaL_checknumber(L, 3);
bool show = lua_toboolean(L, 4);
float zoom = lua_isnumber(L, 5) ? lua_tonumber(L, 5) : 1;
if (!show || !ps->init) return 0;
GLfloat *vertices = ps->vertices;
GLfloat *colors = ps->colors;
GLshort *texcoords = ps->texcoords;
SDL_mutexP(ps->lock);
// Load the texture the first time
if (ps->texture_name)
{
lua_rawgeti(L, LUA_REGISTRYINDEX, textures_ref);
lua_pushstring(L, ps->texture_name);
lua_gettable(L, -2);
if (lua_isnumber(L, -1)) // Use an existing texture
{
ps->texture = lua_tonumber(L, -1);
lua_pop(L, 1);
}
else // Load a new texture
{
lua_pop(L, 1);
ps->texture = particle_load_texture(ps->texture_name);
lua_pushstring(L, ps->texture_name);
lua_pushnumber(L, ps->texture);
lua_settable(L, -3);
free((char*)ps->texture_name);
ps->texture_name = NULL;
}
lua_pop(L, 1);
}
glBindTexture(GL_TEXTURE_2D, ps->texture);
glTexCoordPointer(2, GL_SHORT, 0, texcoords);
glColorPointer(4, GL_FLOAT, 0, colors);
glVertexPointer(2, GL_FLOAT, 0, vertices);
glTranslatef(x, y, 0);
glPushMatrix();
glScalef(ps->zoom * zoom, ps->zoom * zoom, ps->zoom * zoom);
glRotatef(ps->rotate, 0, 0, 1);
int remaining = ps->batch_nb;
while (remaining >= PARTICLES_PER_ARRAY)
{
glDrawArrays(GL_QUADS, remaining - PARTICLES_PER_ARRAY, PARTICLES_PER_ARRAY);
remaining -= PARTICLES_PER_ARRAY;
}
if (remaining) glDrawArrays(GL_QUADS, 0, remaining);
SDL_mutexV(ps->lock);
glRotatef(-ps->rotate, 0, 0, 1);
glPopMatrix();
glTranslatef(-x, -y, 0);
return 0;
}
// Runs into particles thread
static void particles_update(lua_State *L, particles_type *ps, bool last)
{
int w = 0;
bool alive = FALSE;
float zoom = 1;
int vert_idx = 0, col_idx = 0;
int i, j;
if (!ps->init) return;
if (last) SDL_mutexP(ps->lock);
GLfloat *vertices = ps->vertices;
GLfloat *colors = ps->colors;
GLshort *texcoords = ps->texcoords;
ps->rotate += ps->rotate_v;
for (w = 0; w < ps->nb; w++)
{
particle_type *p = &ps->particles[w];
if (p->life > 0)
{
alive = TRUE;
if (p->life != PARTICLE_ETERNAL) p->life--;
p->ox = p->x;
p->oy = p->y;
p->x += p->xv;
p->y += p->yv;
if (p->vel)
{
p->x += cos(p->dir) * p->vel;
p->y += sin(p->dir) * p->vel;
}
p->dir += p->dirv;
p->vel += p->velv;
p->r += p->rv;
p->g += p->gv;
p->b += p->bv;
p->a += p->av;
p->size += p->sizev;
p->xv += p->xa;
p->yv += p->ya;
p->dirv += p->dira;
p->velv += p->vela;
p->rv += p->ra;
p->gv += p->ga;
p->bv += p->ba;
p->av += p->aa;
p->sizev += p->sizea;
if (last)
{
if (!p->trail)
{
i = p->x * zoom - p->size / 2;
j = p->y * zoom - p->size / 2;
vertices[vert_idx] = i; vertices[vert_idx+1] = j;
vertices[vert_idx+2] = p->size + i; vertices[vert_idx+3] = j;
vertices[vert_idx+4] = p->size + i; vertices[vert_idx+5] = p->size + j;
vertices[vert_idx+6] = i; vertices[vert_idx+7] = p->size + j;
}
else
{
if ((p->ox <= p->x) && (p->oy <= p->y))
{
vertices[vert_idx+0] = 0 + p->ox * zoom; vertices[vert_idx+1] = 0 + p->oy * zoom;
vertices[vert_idx+2] = p->size + p->x * zoom; vertices[vert_idx+3] = 0 + p->y * zoom;
vertices[vert_idx+4] = p->size + p->x * zoom; vertices[vert_idx+5] = p->size + p->y * zoom;
vertices[vert_idx+6] = 0 + p->x * zoom; vertices[vert_idx+7] = p->size + p->y * zoom;
}
else if ((p->ox <= p->x) && (p->oy > p->y))
{
vertices[vert_idx+0] = 0 + p->x * zoom; vertices[vert_idx+1] = 0 + p->y * zoom;
vertices[vert_idx+2] = p->size + p->x * zoom; vertices[vert_idx+3] = 0 + p->y * zoom;
vertices[vert_idx+4] = p->size + p->x * zoom; vertices[vert_idx+5] = p->size + p->y * zoom;
vertices[vert_idx+6] = 0 + p->ox * zoom; vertices[vert_idx+7] = p->size + p->oy * zoom;
}
else if ((p->ox > p->x) && (p->oy <= p->y))
{
vertices[vert_idx+0] = 0 + p->x * zoom; vertices[vert_idx+1] = 0 + p->y * zoom;
vertices[vert_idx+2] = p->size + p->ox * zoom; vertices[vert_idx+3] = 0 + p->oy * zoom;
vertices[vert_idx+4] = p->size + p->x * zoom; vertices[vert_idx+5] = p->size + p->y * zoom;
vertices[vert_idx+6] = 0 + p->x * zoom; vertices[vert_idx+7] = p->size + p->y * zoom;
}
else if ((p->ox > p->x) && (p->oy > p->y))
{
vertices[vert_idx+0] = 0 + p->x * zoom; vertices[vert_idx+1] = 0 + p->y * zoom;
vertices[vert_idx+2] = p->size + p->x * zoom; vertices[vert_idx+3] = 0 + p->y * zoom;
vertices[vert_idx+4] = p->size + p->ox * zoom; vertices[vert_idx+5] = p->size + p->oy * zoom;
vertices[vert_idx+6] = 0 + p->x * zoom; vertices[vert_idx+7] = p->size + p->y * zoom;
}
}
/* Setup texture coords */
texcoords[vert_idx] = 0; texcoords[vert_idx+1] = 0;
texcoords[vert_idx+2] = 1; texcoords[vert_idx+3] = 0;
texcoords[vert_idx+4] = 1; texcoords[vert_idx+5] = 1;
texcoords[vert_idx+6] = 0; texcoords[vert_idx+7] = 1;
/* Setup color */
colors[col_idx] = p->r; colors[col_idx+1] = p->g; colors[col_idx+2] = p->b; colors[col_idx+3] = p->a;
colors[col_idx+4] = p->r; colors[col_idx+5] = p->g; colors[col_idx+6] = p->b; colors[col_idx+7] = p->a;
colors[col_idx+8] = p->r; colors[col_idx+9] = p->g; colors[col_idx+10] = p->b; colors[col_idx+11] = p->a;
colors[col_idx+12] = p->r; colors[col_idx+13] = p->g; colors[col_idx+14] = p->b; colors[col_idx+15] = p->a;
vert_idx += 8;
col_idx += 16;
}
}
}
if (last)
{
ps->batch_nb = vert_idx / 2;
ps->alive = alive || ps->no_stop;
SDL_mutexV(ps->lock);
}
}
// Runs into particles thread
static int particles_emit(lua_State *L)
{
plist *l = (plist*)lua_touserdata(L, lua_upvalueindex(1)); // The first upvalue, store in the closure, is the particle's plist
particles_type *ps = l->ps;
if (!ps || !ps->init) return 0;
int nb = luaL_checknumber(L, 2);
if (!nb) return 0;
// printf("Emitting %d particles out of %d for system %x\n", nb, ps->nb, (int)ps);
nb = (nb * ps->density) / 100;
if (!nb) nb = 1;
int i;
for (i = 0; i < ps->nb; i++)
{
particle_type *p = &ps->particles[i];
if (!p->life)
{
if (l->generator_ref == LUA_NOREF)
{
p->life = rng(ps->life_min, ps->life_max);
p->size = rng(ps->size_min, ps->size_max);
p->sizev = rng(ps->sizev_min, ps->sizev_max);
p->sizea = rng(ps->sizea_min, ps->sizea_max);
p->x = p->y = 0;
float angle = rng(ps->angle_min, ps->angle_max) * M_PI / 180;
float v = rng(ps->anglev_min, ps->anglev_max) / ps->base;
float a = rng(ps->anglea_min, ps->anglea_max) / ps->base;
p->xa = cos(angle) * a;
p->ya = sin(angle) * a;
p->xv = cos(angle) * v;
p->yv = sin(angle) * v;
p->dir = 0;
p->dirv = 0;
p->dira = 0;
p->vel = 0;
p->velv = 0;
p->vela = 0;
p->r = rng(ps->r_min, ps->r_max) / 255.0f;
p->g = rng(ps->g_min, ps->g_max) / 255.0f;
p->b = rng(ps->b_min, ps->b_max) / 255.0f;
p->a = rng(ps->a_min, ps->a_max) / 255.0f;
p->rv = rng(ps->rv_min, ps->rv_max) / ps->base;
p->gv = rng(ps->gv_min, ps->gv_max) / ps->base;
p->bv = rng(ps->bv_min, ps->bv_max) / ps->base;
p->av = rng(ps->av_min, ps->av_max) / ps->base;
p->ra = rng(ps->ra_min, ps->ra_max) / ps->base;
p->ga = rng(ps->ga_min, ps->ga_max) / ps->base;
p->ba = rng(ps->ba_min, ps->ba_max) / ps->base;
p->aa = rng(ps->aa_min, ps->aa_max) / ps->base;
p->trail = FALSE;
}
else
{
lua_rawgeti(L, LUA_REGISTRYINDEX, l->generator_ref);
if (lua_pcall(L, 0, 1, 0))
{
printf("Particle emitter error %x (%d): %s\n", (int)l, l->generator_ref, lua_tostring(L, -1));
lua_pop(L, 1);
}
if (!lua_isnil(L, -1))
{
float life;
float trail;
getparticulefield(L, "trail", &trail); p->trail = trail;
getparticulefield(L, "life", &life); p->life = life;
getparticulefield(L, "size", &(p->size));
getparticulefield(L, "sizev", &(p->sizev));
getparticulefield(L, "sizea", &(p->sizea));
getparticulefield(L, "x", &(p->x));
getparticulefield(L, "xv", &(p->xv));
getparticulefield(L, "xa", &(p->xa));
getparticulefield(L, "y", &(p->y));
getparticulefield(L, "yv", &(p->yv));
getparticulefield(L, "ya", &(p->ya));
getparticulefield(L, "dir", &(p->dir));
getparticulefield(L, "dirv", &(p->dirv));
getparticulefield(L, "dira", &(p->dira));
getparticulefield(L, "vel", &(p->vel));
getparticulefield(L, "velv", &(p->velv));
getparticulefield(L, "vela", &(p->vela));
getparticulefield(L, "r", &(p->r));
getparticulefield(L, "rv", &(p->rv));
getparticulefield(L, "ra", &(p->ra));
getparticulefield(L, "g", &(p->g));
getparticulefield(L, "gv", &(p->gv));
getparticulefield(L, "ga", &(p->ga));
getparticulefield(L, "b", &(p->b));
getparticulefield(L, "bv", &(p->bv));
getparticulefield(L, "ba", &(p->ba));
getparticulefield(L, "a", &(p->a));
getparticulefield(L, "av", &(p->av));
getparticulefield(L, "aa", &(p->aa));
}
lua_pop(L, 1);
}
p->ox = p->x;
p->oy = p->y;
nb--;
if (!nb) break;
}
}
return 0;
}
static const struct luaL_reg particleslib[] =
{
{"newEmitter", particles_new},
{NULL, NULL},
};
static const struct luaL_reg particles_reg[] =
{
{"__gc", particles_free},
{"toScreen", particles_to_screen},
{"isAlive", particles_is_alive},
{"die", particles_die},
{NULL, NULL},
};
int luaopen_particles(lua_State *L)
{
auxiliar_newclass(L, "core{particles}", particles_reg);
luaL_openlib(L, "core.particles", particleslib, 0);
lua_pushstring(L, "ETERNAL");
lua_pushnumber(L, PARTICLE_ETERNAL);
lua_settable(L, -3);
lua_pop(L, 1);
// Make a table to store all textures
lua_newtable(L);
textures_ref = luaL_ref(L, LUA_REGISTRYINDEX);
return 1;
}
/*********************************************************
** Multithread particle code
*********************************************************/
// Runs on particles thread
void thread_particle_run(particle_thread *pt, plist *l)
{
lua_State *L = pt->L;
particles_type *ps = l->ps;
if (!ps || !ps->l || !ps->init || !ps->alive || ps->i_want_to_die) return;
// Update
lua_rawgeti(L, LUA_REGISTRYINDEX, l->updator_ref);
lua_rawgeti(L, LUA_REGISTRYINDEX, l->emit_ref);
if (lua_pcall(L, 1, 0, 0))
{
printf("L(%x) Particle updater error %x (%d, %d): %s\n", (int)L, (int)l, l->updator_ref, l->emit_ref, lua_tostring(L, -1));
// ps->i_want_to_die = TRUE;
lua_pop(L, 1);
}
particles_update(L, ps, TRUE);
}
// Runs on particles thread
extern int docall (lua_State *L, int narg, int nret);
void thread_particle_init(particle_thread *pt, plist *l)
{
lua_State *L = pt->L;
particles_type *ps = l->ps;
int tile_w, tile_h;
int base_size = 0;
// Load the particle definition
// Returns: generator_fct:1, update_fct:2, max:3, gl:4, no_stop:5
if (!luaL_loadfile(L, ps->name_def))
{
// Make a new table to serve as environment for the function
lua_newtable(L);
if (!luaL_loadstring(L, ps->args))
{
lua_pushvalue(L, -2); // Copy the evn table
lua_setfenv(L, -2); // Set it as the function env
if (lua_pcall(L, 0, 0, 0))
{
printf("Particle args init error %x (%s): %s\n", (int)l, ps->args, lua_tostring(L, -1));
lua_pop(L, 1);
}
}
else
{
lua_pop(L, 1);
printf("Loading particle arguments failed: %s\n", ps->args);
}
// Copy tile_w and tile_h for compatibility with old code
lua_pushstring(L, "engine");
lua_newtable(L);
lua_pushstring(L, "Map");
lua_newtable(L);
lua_pushstring(L, "tile_w");
lua_pushstring(L, "tile_w");
lua_gettable(L, -7);
tile_w = lua_tonumber(L, -1);
lua_settable(L, -3);
lua_pushstring(L, "tile_h");
lua_pushstring(L, "tile_h");
lua_gettable(L, -7);
tile_h = lua_tonumber(L, -1);
lua_settable(L, -3);
lua_settable(L, -3);
lua_settable(L, -3);
// The metatable which references the global space
lua_newtable(L);
lua_pushstring(L, "__index");
lua_pushvalue(L, LUA_GLOBALSINDEX);
lua_settable(L, -3);
// Set the environment metatable
lua_setmetatable(L, -2);
// Set the environment
lua_pushvalue(L, -1);
lua_setfenv(L, -3);
lua_insert(L, -2);
// Call the method
if (lua_pcall(L, 0, 5, 0))
{
printf("Particle run error %x (%s): %s\n", (int)l, ps->args, lua_tostring(L, -1));
lua_pop(L, 1);
}
// Check base size
lua_pushstring(L, "base_size");
lua_gettable(L, -7);
base_size = lua_tonumber(L, -1);
lua_pop(L, 1);
lua_remove(L, -6);
}
else { lua_pop(L, 1); return; }
int nb = lua_isnumber(L, 3) ? lua_tonumber(L, 3) : 1000;
nb = (nb * ps->density) / 100;
if (!nb) nb = 1;
ps->nb = nb;
ps->no_stop = lua_toboolean(L, 5);
ps->zoom = 1;
if (base_size)
{
ps->zoom = (((float)tile_w + (float)tile_h) / 2) / (float)base_size;
}
int batch = nb;
ps->batch_nb = 0;
ps->vertices = calloc(2*4*batch, sizeof(GLfloat)); // 2 coords, 4 vertices per particles
ps->colors = calloc(4*4*batch, sizeof(GLfloat)); // 4 color data, 4 vertices per particles
ps->texcoords = calloc(2*4*batch, sizeof(GLshort));
ps->particles = calloc(nb, sizeof(particle_type));
if (lua_isstring(L, 4))
ps->texture_name = strdup(lua_tostring(L, 4));
else
ps->texture_name = strdup("particle");
// Locate the updator
lua_pushvalue(L, 2);
l->updator_ref = luaL_ref(L, LUA_REGISTRYINDEX);
// Grab all parameters
lua_pushvalue(L, 1);
lua_pushstring(L, "system_rotation");
lua_gettable(L, -2);
ps->rotate = lua_tonumber(L, -1); lua_pop(L, 1);
lua_pushstring(L, "system_rotationv");
lua_gettable(L, -2);
ps->rotate_v = lua_tonumber(L, -1); lua_pop(L, 1);
lua_pushstring(L, "generator");
lua_gettable(L, -2);
if (lua_isnil(L, -1))
{
lua_pop(L, 1);
l->generator_ref = LUA_NOREF;
}
else
l->generator_ref = luaL_ref(L, LUA_REGISTRYINDEX);
if (l->generator_ref == LUA_NOREF)
{
lua_pushstring(L, "base");
lua_gettable(L, -2);
ps->base = (float)lua_tonumber(L, -1);
lua_pop(L, 1);
getinitfield(L, "life", &(ps->life_min), &(ps->life_max));
getinitfield(L, "angle", &(ps->angle_min), &(ps->angle_max));
getinitfield(L, "anglev", &(ps->anglev_min), &(ps->anglev_max));
getinitfield(L, "anglea", &(ps->anglea_min), &(ps->anglea_max));
getinitfield(L, "size", &(ps->size_min), &(ps->size_max));
getinitfield(L, "sizev", &(ps->sizev_min), &(ps->sizev_max));
getinitfield(L, "sizea", &(ps->sizea_min), &(ps->sizea_max));
getinitfield(L, "r", &(ps->r_min), &(ps->r_max));
getinitfield(L, "rv", &(ps->rv_min), &(ps->rv_max));
getinitfield(L, "ra", &(ps->ra_min), &(ps->ra_max));
getinitfield(L, "g", &(ps->g_min), &(ps->g_max));
getinitfield(L, "gv", &(ps->gv_min), &(ps->gv_max));
getinitfield(L, "ga", &(ps->ga_min), &(ps->ga_max));
getinitfield(L, "b", &(ps->b_min), &(ps->b_max));
getinitfield(L, "bv", &(ps->bv_min), &(ps->bv_max));
getinitfield(L, "ba", &(ps->ba_min), &(ps->ba_max));
getinitfield(L, "a", &(ps->a_min), &(ps->a_max));
getinitfield(L, "av", &(ps->av_min), &(ps->av_max));
getinitfield(L, "aa", &(ps->aa_min), &(ps->aa_max));
// printf("Particle emiter using default generator\n");
}
else
{
// printf("Particle emiter using custom generator\n");
}
lua_pop(L, 1);
// Pop all returns
lua_pop(L, 5);
// Push a special emitter
lua_newtable(L);
lua_pushstring(L, "ps");
lua_newtable(L);
lua_pushstring(L, "emit");
lua_pushlightuserdata(L, l);
lua_pushcclosure(L, particles_emit, 1);
lua_settable(L, -3);
lua_settable(L, -3);
l->emit_ref = luaL_ref(L, LUA_REGISTRYINDEX);
free((char*)ps->name_def);
free((char*)ps->args);
ps->name_def = ps->args = NULL;
ps->init = TRUE;
}
void thread_particle_die(particle_thread *pt, plist *l)
{
lua_State *L = pt->L;
particles_type *ps = l->ps;
// printf("Deleting particle from list %x :: %x\n", (int)l, (int)ps);
if (l->emit_ref != LUA_NOREF) luaL_unref(L, LUA_REGISTRYINDEX, l->emit_ref);
if (l->updator_ref != LUA_NOREF) luaL_unref(L, LUA_REGISTRYINDEX, l->updator_ref);
if (l->generator_ref != LUA_NOREF) luaL_unref(L, LUA_REGISTRYINDEX, l->generator_ref);
l->emit_ref = LUA_NOREF;
l->updator_ref = LUA_NOREF;
l->generator_ref = LUA_NOREF;
if (ps)
{
if (ps->texcoords) { free(ps->texcoords); ps->texcoords = NULL; }
if (ps->vertices) { free(ps->vertices); ps->vertices = NULL; }
if (ps->colors) { free(ps->colors); ps->colors = NULL; }
if (ps->particles) { free(ps->particles); ps->particles = NULL; }
ps->init = FALSE;
ps->alive = FALSE;
}
}
// Runs on particles thread
int thread_particles(void *data)
{
particle_thread *pt = (particle_thread*)data;
lua_State *L = lua_open(); /* create state */
luaL_openlibs(L); /* open libraries */
luaopen_core(L);
luaopen_particles(L);
pt->L = L;
// Override "print" if requested
if (no_debug)
{
lua_pushcfunction(L, noprint);
lua_setglobal(L, "print");
}
// And run the lua engine pre init scripts
if (!luaL_loadfile(L, "/loader/pre-init.lua")) docall(L, 0, 0);
else lua_pop(L, 1);
plist *prev;
plist *l;
while (pt->running)
{
// Wait for a keyframe
// printf("Runing particle thread %d (waiting for sem)\n", pt->id);
SDL_SemWait(pt->keyframes);
// printf("Runing particle thread %d (waiting for mutex; running(%d))\n", pt->id, pt->running);
if (!pt->running) break;
SDL_mutexP(pt->lock);
int nb = 0;
l = pt->list;
prev = NULL;
while (l)
{
if (l->ps && l->ps->alive && !l->ps->i_want_to_die)
{
if (l->ps->init) thread_particle_run(pt, l);
else thread_particle_init(pt, l);
prev = l;
l = l->next;
}
else
{
thread_particle_die(pt, l);
// Remove dead ones
if (!prev) pt->list = l->next;
else prev->next = l->next;
l = l->next;
}
nb++;
}
// printf("Particles thread %d has %d systems\n", pt->id, nb);
SDL_mutexV(pt->lock);
}
printf("Cleaning up particle thread %d\n", pt->id);
// Cleanup
SDL_mutexP(pt->lock);
l = pt->list;
while (l)
{
thread_particle_die(pt, l);
l = l->next;
}
SDL_mutexV(pt->lock);
lua_close(L);
SDL_DestroySemaphore(pt->keyframes);
SDL_DestroyMutex(pt->lock);
printf("Cleaned up particle thread %d\n", pt->id);
return(0);
}
// Runs on main thread
// Signals all particles threads that some new keyframes have arrived
void thread_particle_new_keyframes(int nb_keyframes)
{
int i, j;
for (i = 0; i < MAX_THREADS; i++)
{
for (j = 0; j < nb_keyframes; j++) SDL_SemPost(threads[i].keyframes);
}
}
// Runs on main thread
void thread_add(particles_type *ps)
{
particle_thread *pt = &threads[cur_thread];
// Insert it in the head of the list
SDL_mutexP(pt->lock);
plist *l = malloc(sizeof(plist));
l->pt = pt;
l->ps = ps;
l->next = pt->list;
pt->list = l;
ps->l = l;
SDL_mutexV(pt->lock);
// printf("New particles registered on thread %d: %s\n", cur_thread, ps->name_def);
cur_thread++;
if (cur_thread >= MAX_THREADS) cur_thread = 0;
}
// Runs on main thread
void free_particles_thread()
{
if (!threads) return;
int i;
for (i = 0; i < MAX_THREADS; i++)
{
int status;
int sem_res;
particle_thread *pt = &threads[i];
printf("Destroying particle thread %d (waiting for mutex)\n", i);
SDL_mutexP(pt->lock);
pt->running = FALSE;
SDL_mutexV(pt->lock);
printf("Destroying particle thread %d\n", i);
sem_res = SDL_SemPost(pt->keyframes);
if (sem_res) printf("Error while waiting for particle thread to die: %s\n", SDL_GetError());
printf("Destroying particle thread %d (waiting for thread %x)\n", i, (int)pt->thread);
SDL_WaitThread(pt->thread, &status);
printf("Destroyed particle thread %d (%d)\n", i, status);
}
nb_threads = 0;
free(threads);
threads = NULL;
}
// Runs on main thread
void create_particles_thread()
{
int i;
// Previous ones
if (threads)
{
free_particles_thread();
}
MAX_THREADS = nb_cpus - 1;
MAX_THREADS = (MAX_THREADS < 1) ? 1 : MAX_THREADS;
threads = calloc(MAX_THREADS, sizeof(particle_thread));
cur_thread = 0;
for (i = 0; i < MAX_THREADS; i++)
{
SDL_Thread *thread;
particle_thread *pt = &threads[i];
pt->id = nb_threads++;
pt->list = NULL;
pt->lock = SDL_CreateMutex();
pt->keyframes = SDL_CreateSemaphore(0);
pt->running = TRUE;
thread = SDL_CreateThread(thread_particles, pt);
if (thread == NULL) {
printf("Unable to create particle thread: %s\n", SDL_GetError());
continue;
}
pt->thread = thread;
printf("Creating particles thread %d\n", pt->id);
}
}