Index: fog.c
===================================================================
--- fog.c	(revision 394)
+++ fog.c	(revision 395)
@@ -28,88 +28,98 @@
 glFogfv(GL_FOG_COLOR,raydium_background_color);
 }
 
-void raydium_fog_mode_set(GLuint mode)
+void raydium_fog_mode(GLuint mode)
 {
-raydium_fog_mode=mode;
+raydium_fog_mode_value=mode;
 }
 
-int raydium_fog_mode_get(void)
+void raydium_fog_density(GLfloat density)
 {
-return raydium_fog_mode;
+raydium_fog_density_value=density;
 }
 
-void raydium_fog_density_set(GLfloat density)
+void raydium_fog_near(GLfloat fnear)
 {
-raydium_fog_density=density;
+raydium_fog_near_value=fnear;
 }
 
-float raydium_fog_density_get(void)
+void raydium_fog_far(GLfloat ffar)
 {
-return raydium_fog_density;
+raydium_fog_far_value=ffar;
 }
 
-void raydium_fog_near_set(GLfloat fnear)
+void raydium_fog_apply(void)
 {
-raydium_fog_near=fnear;
+
+if(raydium_fog_enabled_tag)
+{
+    glEnable(GL_FOG);
+    glFogi(GL_FOG_MODE,raydium_fog_mode_value);
+    raydium_fog_color_update();
+    glFogf(GL_FOG_DENSITY, raydium_fog_density_value);
+    glHint(GL_FOG_HINT, GL_FASTEST);
+
+    if(raydium_fog_far_value==0)
+	{	
+	raydium_fog_far_value=raydium_projection_far;
+	raydium_fog_near_value=raydium_projection_far/4.f;
+	}		
+
+    glFogf(GL_FOG_START,raydium_fog_near_value);
+    glFogf(GL_FOG_END,raydium_fog_far_value);		
+    }
+    else
+    {
+    glDisable(GL_FOG);
+    }
 }
 
-float raydium_fog_near_get(void)
+
+void raydium_fog_wait(void)
 {
-return raydium_fog_near;
+glDisable(GL_FOG);
 }
-void raydium_fog_far_set(GLfloat ffar)
+
+void raydium_fog_init(void)
 {
-raydium_fog_far=ffar;
+raydium_fog_far_value=0;
+raydium_fog_near_value=0;
+raydium_fog_density_value=0;
+raydium_fog_mode_value=RAYDIUM_FOG_MODE_LINEAR;
+raydium_fog_volumetric_enabled_tag=0;
+
+switch(RENDER_VOLUMETRIC_FOG_AXIS)
+    {
+    case 0:
+	raydium_fog_volumetric_array=raydium_vertex_x;
+	break;
+    case 1:
+	raydium_fog_volumetric_array=raydium_vertex_y;
+	break;
+    case 2:
+	raydium_fog_volumetric_array=raydium_vertex_z;
+	break;	
+    default:
+	raydium_log("fog: FAILED: RENDER_VOLUMETRIC_FOG_AXIS is broken !");
+	exit(100);
+    }
+
+raydium_fog_enable();
+raydium_log("fog: OK");
 }
 
-float raydium_fog_far_get(void)
+void raydium_fog_volumetric_support(void)
 {
-return raydium_fog_far;
+raydium_fog_volumetric_enabled_tag=1;
 }
-void raydium_fog_apply(void)
+
+void raydium_fog_volumetric_enable(void)
 {
-	char info[255];
-	if(raydium_fog_enabled_tag)
-	{
-		glEnable(GL_FOG);
-		glFogi(GL_FOG_MODE,raydium_fog_mode);
-		glFogfv(GL_FOG_COLOR,raydium_background_color);
-		glFogf(GL_FOG_DENSITY, raydium_fog_density);
-		glHint(GL_FOG_HINT, GL_FASTEST);
-		if(raydium_fog_far==0)
-		{	
-			raydium_fog_far=raydium_projection_far;
-			raydium_fog_near= raydium_projection_far/4;
-		}		
-		glFogf(GL_FOG_START,raydium_fog_near);
-		glFogf(GL_FOG_END,raydium_fog_far);		
-		glFogi(GL_FOG_MODE,raydium_fog_mode);
-		switch(raydium_fog_mode)
-		{
-			case GL_LINEAR:
-			strcpy(info,"linear");
-			break;
-			case GL_EXP:
-			strcpy(info,"exp");
-			break;
-			case GL_EXP2:
-			strcpy(info,"exp2");
-			break;
-			default:
-			strcpy(info,"error");
-			break;
-		}	
-//		raydium_log("Fog activated. Mode: %s; Density: %f; near: %f, far: %f.",info, raydium_fog_density,raydium_fog_near,raydium_fog_far );	
-	}
-	else
-	{
-		glDisable(GL_FOG);
-//		raydium_log("Fog deactivated.");
-	}
+glFogi(GL_FOG_COORDINATE_SOURCE_EXT, GL_FOG_COORDINATE_EXT);
 }
 
-void raydium_fog_wait(void)
+void raydium_fog_volumetric_disable(void)
 {
-	glDisable(GL_FOG);
-//	raydium_log("Fog stopped.");
+glFogi(GL_FOG_COORDINATE_SOURCE_EXT, GL_FRAGMENT_DEPTH_EXT);
 }
+
e)
            }
        // cache texture
        raydium_texture_current_set_name(val_s);
        }
    }
fclose(fp);
}


void raydium_particle_generator_load_internal(int generator,FILE *fp, char *filename)
{
int ret;
char var[RAYDIUM_MAX_NAME_LEN];
char val_s[RAYDIUM_MAX_NAME_LEN];
GLfloat val_f[4];
int size;
char done;

while( (ret=raydium_parser_read(var,val_s,val_f,&size,fp))!=RAYDIUM_PARSER_TYPE_EOF)
    {
    done=0;

    if(!strcasecmp(var,"include"))
        {
        FILE *sub;
        char dir[RAYDIUM_MAX_NAME_LEN];

        if(ret!=RAYDIUM_PARSER_TYPE_STRING)
            {
            raydium_log("particle: parser: include: wrong type");
            continue;
            }
        raydium_file_dirname(dir,filename);
        strcat(dir,val_s);
        strcpy(val_s,dir);
        sub=raydium_file_fopen(val_s,"rt"); // idem
        if(!sub)
         {
         raydium_log("particle: ERROR: %s cannot open %s particle subfile",filename,val_s);
         continue;
         }
        raydium_particle_generator_load_internal(generator,sub,val_s);
        fclose(sub);
        done=1;
        }

    if(!strcasecmp(var,"position"))
        {
        if(ret!=RAYDIUM_PARSER_TYPE_FLOAT || size!=3)
            {
            raydium_log("particle: parser: position: wrong type");
            continue;
            }
        memcpy(raydium_particle_generators[generator].position,val_f,sizeof(GLfloat)*3);
        done=1;
        }

    if(!strcasecmp(var,"position_random"))
        {
        if(ret!=RAYDIUM_PARSER_TYPE_FLOAT || size!=3)
            {
            raydium_log("particle: parser: position_random: wrong type");
            continue;
            }
        memcpy(raydium_particle_generators[generator].position_random,val_f,sizeof(GLfloat)*3);
        done=1;
        }

    if(!strcasecmp(var,"ttl_generator"))
        {
        if(ret!=RAYDIUM_PARSER_TYPE_FLOAT || size!=1)
            {
            raydium_log("particle: parser: ttl_generator: wrong type");
            continue;
            }
        raydium_particle_generators[generator].ttl_generator=val_f[0];
        done=1;
        }

    if(!strcasecmp(var,"ttl_particles"))
        {
        if(ret!=RAYDIUM_PARSER_TYPE_FLOAT || size!=1)
            {
            raydium_log("particle: parser: ttl_particles: wrong type");
            continue;
            }
        raydium_particle_generators[generator].ttl_particles=val_f[0];
        done=1;
        }

    if(!strcasecmp(var,"ttl_particles_random"))
        {
        if(ret!=RAYDIUM_PARSER_TYPE_FLOAT || size!=1)
            {
            raydium_log("particle: parser: ttl_particles_random: wrong type");
            continue;
            }
        raydium_particle_generators[generator].ttl_particles_random=val_f[0];
        done=1;
        }

    if(!strcasecmp(var,"particles_per_second"))
        {
        if(ret!=RAYDIUM_PARSER_TYPE_FLOAT || size!=1)
            {
            raydium_log("particle: parser: particles_per_second: wrong type");
            continue;
            }
        raydium_particle_generators[generator].particles_per_second=val_f[0];
        done=1;
        }

    if(!strcasecmp(var,"texture"))
        {
        if(ret!=RAYDIUM_PARSER_TYPE_STRING)
            {
            raydium_log("particle: parser: texture: wrong type");
            continue;
            }
        raydium_particle_generators[generator].texture=
            raydium_texture_find_by_name(val_s);
        done=1;
        }


    if(!strcasecmp(var,"size"))
        {
        if(ret!=RAYDIUM_PARSER_TYPE_FLOAT || size!=1)
            {
            raydium_log("particle: parser: size: wrong type");
            continue;
            }
        raydium_particle_generators[generator].size=val_f[0];
        done=1;
        }

    if(!strcasecmp(var,"size_inc_per_sec"))
        {
        if(ret!=RAYDIUM_PARSER_TYPE_FLOAT || size!=1)
            {
            raydium_log("particle: parser: size_inc_per_sec: wrong type");
            continue;
            }
        raydium_particle_generators[generator].size_inc_per_sec=val_f[0];
        done=1;
        }

    if(!strcasecmp(var,"size_random"))
        {
        if(ret!=RAYDIUM_PARSER_TYPE_FLOAT || size!=1)
            {
            raydium_log("particle: parser: size_random: wrong type");
            continue;
            }
        raydium_particle_generators[generator].size_random=val_f[0];
        done=1;
        }

    if(!strcasecmp(var,"size_limit"))
        {
        if(ret!=RAYDIUM_PARSER_TYPE_FLOAT || size!=1)
            {
            raydium_log("particle: parser: size_limit: wrong type");
            continue;
            }
        raydium_particle_generators[generator].size_limit=val_f[0];
        done=1;
        }

    if(!strcasecmp(var,"gravity"))
        {
        if(ret!=RAYDIUM_PARSER_TYPE_FLOAT || size!=3)
            {
            raydium_log("particle: parser: gravity: wrong type");
            continue;
            }
        memcpy(raydium_particle_generators[generator].gravity,val_f,sizeof(GLfloat)*3);
        done=1;
        }

    if(!strcasecmp(var,"vector"))
        {
        if(ret!=RAYDIUM_PARSER_TYPE_FLOAT || size!=3)
            {
            raydium_log("particle: parser: vector: wrong type");
            continue;
            }
        memcpy(raydium_particle_generators[generator].vector,val_f,sizeof(GLfloat)*3);
        done=1;
        }

    if(!strcasecmp(var,"vector_random"))
        {
        if(ret!=RAYDIUM_PARSER_TYPE_FLOAT || size!=3)
            {
            raydium_log("particle: parser: vector_random: wrong type");
            continue;
            }
        memcpy(raydium_particle_generators[generator].vector_random,val_f,sizeof(GLfloat)*3);
        done=1;
        }

    if(!strcasecmp(var,"vector_sphere_angles"))
        {
        if(ret!=RAYDIUM_PARSER_TYPE_FLOAT || size!=3)
            {
            raydium_log("particle: parser: vector_sphere_angles: wrong type");
            continue;
            }
        memcpy(raydium_particle_generators[generator].vector_sphere_angles,val_f,sizeof(GLfloat)*3);
        done=1;
        }

    if(!strcasecmp(var,"vector_sphere_angles_random"))
        {
        if(ret!=RAYDIUM_PARSER_TYPE_FLOAT || size!=3)
            {
            raydium_log("particle: parser: vector_sphere_angles_random: wrong type");
            continue;
            }
        memcpy(raydium_particle_generators[generator].vector_sphere_angles_random,val_f,sizeof(GLfloat)*3);
        done=1;
        }

    if(!strcasecmp(var,"vector_sphere_force"))
        {
        if(ret!=RAYDIUM_PARSER_TYPE_FLOAT || size!=1)
            {
            raydium_log("particle: parser: vector_sphere_force: wrong type");
            continue;
            }
        raydium_particle_generators[generator].vector_sphere_force=val_f[0];
        done=1;
        }

    if(!strcasecmp(var,"vector_sphere_force_random"))
        {
        if(ret!=RAYDIUM_PARSER_TYPE_FLOAT || size!=1)
            {
            raydium_log("particle: parser: vector_sphere_force_random: wrong type");
            continue;
            }
        raydium_particle_generators[generator].vector_sphere_force_random=val_f[0];
        done=1;
        }

    if(!strcasecmp(var,"rotation_speed"))
        {
        if(ret!=RAYDIUM_PARSER_TYPE_FLOAT || size!=1)
            {
            raydium_log("particle: parser: rotation_speed: wrong type");
            continue;
            }
        raydium_particle_generators[generator].rotation_speed=val_f[0];
        done=1;
        }

    if(!strcasecmp(var,"rotation_random"))
        {
        if(ret!=RAYDIUM_PARSER_TYPE_FLOAT || size!=1)
            {
            raydium_log("particle: parser: rotation_random: wrong type");
            continue;
            }
        raydium_particle_generators[generator].rotation_random=val_f[0];
        done=1;
        }

    if(!strcasecmp(var,"color_start"))
        {
        if(ret!=RAYDIUM_PARSER_TYPE_FLOAT || size!=4)
            {
            raydium_log("particle: parser: color_start: wrong type");
            continue;
            }
        memcpy(raydium_particle_generators[generator].color_start,val_f,sizeof(GLfloat)*4);
        done=1;
        }

    if(!strcasecmp(var,"color_start_random"))
        {
        if(ret!=RAYDIUM_PARSER_TYPE_FLOAT || size!=4)
            {
            raydium_log("particle: parser: color_start_random: wrong type");
            continue;
            }
        memcpy(raydium_particle_generators[generator].color_start_random,val_f,sizeof(GLfloat)*4);
        done=1;
        }

    if(!strcasecmp(var,"color_end"))
        {
        if(ret!=RAYDIUM_PARSER_TYPE_FLOAT || size!=4)
            {
            raydium_log("particle: parser: color_end: wrong type");
            continue;
            }
        memcpy(raydium_particle_generators[generator].color_end,val_f,sizeof(GLfloat)*4);
        done=1;
        }

    if(!strcasecmp(var,"color_end_random"))
        {
        if(ret!=RAYDIUM_PARSER_TYPE_FLOAT || size!=4)
            {
            raydium_log("particle: parser: color_end_random: wrong type");
            continue;
            }
        memcpy(raydium_particle_generators[generator].color_end_random,val_f,sizeof(GLfloat)*4);
        done=1;
        }

    if(!strcasecmp(var,"visibility"))
        {
        if(ret!=RAYDIUM_PARSER_TYPE_FLOAT || size!=1)
            {
            raydium_log("particle: parser: visibility: wrong type");
            continue;
            }
        raydium_particle_generators[generator].visibility=val_f[0];
        done=1;
        }

    if(!done)
        raydium_log("particle: parser: invalid or unsupported option '%s' (%s)",var,filename);
    }
}

int raydium_particle_generator_load(char *filename,char *name)
{
FILE *fp;
int generator;
int i;


if(raydium_particle_generator_find(name)>=0)
    {
    raydium_log("particle: Cannot load \"%s\": '%s' already exists",filename,name);
    return -1;
    }

fp=raydium_file_fopen(filename,"rt"); // rb ? must test under win32
if(!fp)
 {
 raydium_log("particle: ERROR: Cannot open %s particle file",filename);
 return -1;
 }

for(i=0;i<RAYDIUM_MAX_GENERATORS;i++)
    if(raydium_particle_generators[i].state==0)
        break;

if(i==RAYDIUM_MAX_GENERATORS)
    {
    raydium_log("particle: no more available slots !");
    fclose(fp);
    return -1;
    }
generator=i;

raydium_particle_generators[generator].state=1;
raydium_particle_generators[generator].enabled=1;
strcpy(raydium_particle_generators[generator].name,name);
raydium_particle_generators[generator].position[0]=0;
raydium_particle_generators[generator].position[1]=0;
raydium_particle_generators[generator].position[2]=0;
raydium_particle_generators[generator].position_random[0]=0;
raydium_particle_generators[generator].position_random[1]=0;
raydium_particle_generators[generator].position_random[2]=0;
raydium_particle_generators[generator].position_user[0]=0;
raydium_particle_generators[generator].position_user[1]=0;
raydium_particle_generators[generator].position_user[2]=0;
raydium_particle_generators[generator].ttl_generator=1;
raydium_particle_generators[generator].ttl_particles=1;
raydium_particle_generators[generator].particles_per_second=100;
raydium_particle_generators[generator].texture=0;
raydium_particle_generators[generator].size=1;
raydium_particle_generators[generator].size_inc_per_sec=0;
raydium_particle_generators[generator].size_random=0;
raydium_particle_generators[generator].size_limit=-1;
raydium_particle_generators[generator].gravity[0]=0;
raydium_particle_generators[generator].gravity[1]=0;
raydium_particle_generators[generator].gravity[2]=0;
raydium_particle_generators[generator].vector[0]=0;
raydium_particle_generators[generator].vector[1]=0;
raydium_particle_generators[generator].vector[2]=0;
raydium_particle_generators[generator].vector_random[0]=0;
raydium_particle_generators[generator].vector_random[1]=0;
raydium_particle_generators[generator].vector_random[2]=0;
raydium_particle_generators[generator].vector_sphere_angles[0]=0;
raydium_particle_generators[generator].vector_sphere_angles[1]=0;
raydium_particle_generators[generator].vector_sphere_angles[2]=0;
raydium_particle_generators[generator].vector_sphere_angles_random[0]=0;
raydium_particle_generators[generator].vector_sphere_angles_random[1]=0;
raydium_particle_generators[generator].vector_sphere_angles_random[2]=0;
raydium_particle_generators[generator].vector_sphere_force=0;
raydium_particle_generators[generator].vector_sphere_force_random=0;
// velocity_limit
raydium_particle_generators[generator].rotation_speed=45;
raydium_particle_generators[generator].rotation_random=0;
raydium_particle_generators[generator].color_start[0]=1;
raydium_particle_generators[generator].color_start[1]=1;
raydium_particle_generators[generator].color_start[2]=1;
raydium_particle_generators[generator].color_start[3]=1;
raydium_particle_generators[generator].color_start_random[0]=0;
raydium_particle_generators[generator].color_start_random[1]=0;
raydium_particle_generators[generator].color_start_random[2]=0;
raydium_particle_generators[generator].color_start_random[3]=0;
raydium_particle_generators[generator].color_end[0]=1;
raydium_particle_generators[generator].color_end[1]=1;
raydium_particle_generators[generator].color_end[2]=1;
raydium_particle_generators[generator].color_end[3]=1;
raydium_particle_generators[generator].color_end_random[0]=0;
raydium_particle_generators[generator].color_end_random[1]=0;
raydium_particle_generators[generator].color_end_random[2]=0;
raydium_particle_generators[generator].color_end_random[3]=0;
raydium_particle_generators[generator].visibility=1;
raydium_particle_generators[generator].OnDeleteParticle=NULL;

// transform
raydium_particle_generator_load_internal(generator,fp,filename);

fclose(fp);
return generator;
}


void raydium_particle_generator_update(int g,GLfloat step)
{
int i,j,p,to_create;
raydium_particle_Generator *gen;
raydium_particle_Particle *part;

/*
if(!raydium_particle_generators[g].state)
    {
    raydium_log("particle: Cannot update generator: invalid index");
    return;
    }
*/

gen=&raydium_particle_generators[g];

// This +1 is bad, must use a "float" counter on generator
to_create=(step*gen->particles_per_second)+1;
//raydium_log("will create %i particles for %s (%i)",to_create,gen->name,g);

if(!gen->enabled) to_create=0;

for(i=0;i<to_create;i++)
    {
    p=raydium_particle_find_free();
    if(p<0)
        {
        //raydium_log("particle: No more particle slots !");
        break;
        }

    raydium_particle_particles[p]=malloc(sizeof(raydium_particle_Particle));
    if(!raydium_particle_particles[p])
        {
        raydium_log("particle: ERROR: malloc failed !");
        return;
        }


    part=raydium_particle_particles[p];

    part->ttl_init=raydium_random_f(gen->ttl_particles-gen->ttl_particles_random,gen->ttl_particles+gen->ttl_particles_random);
    part->ttl=part->ttl_init;
    part->texture=gen->texture;

    memcpy(part->position,gen->position,sizeof(GLfloat)*3);
    for(j=0;j<3;j++)
        part->position[j]+=raydium_random_f(-gen->position_random[j],gen->position_random[j]);

    for(j=0;j<3;j++)
        part->position[j]+=gen->position_user[j];

    part->size=raydium_random_f(gen->size-gen->size_random,gen->size+gen->size_random);
    part->size_inc_per_sec=gen->size_inc_per_sec;
    part->size_limit=gen->size_limit;

    memcpy(part->gravity,gen->gravity,sizeof(GLfloat)*3);


    if(gen->vector_sphere_force==0 && gen->vector_sphere_force_random==0)
    {
     // ortho
     memcpy(part->vel,gen->vector,sizeof(GLfloat)*3);
     for(j=0;j<3;j++)
        part->vel[j]+=raydium_random_f(-gen->vector_random[j],gen->vector_random[j]);
    }
    else
    {
    // spherical
    GLfloat def_angles[3]={0,0,1};
    GLfloat angles[3];
    GLfloat force;

    memcpy(angles,gen->vector_sphere_angles,sizeof(GLfloat)*3);
    for(j=0;j<3;j++)
        angles[j]+=raydium_random_f(-gen->vector_sphere_angles_random[j],gen->vector_sphere_angles_random[j]);


    force=gen->vector_sphere_force
         +raydium_random_f(-gen->vector_sphere_force_random,
                            gen->vector_sphere_force_random);
    for(j=0;j<3;j++)
        def_angles[j]*=force;

    raydium_math_rotate(def_angles,angles[0],angles[1],angles[2],part->vel);
    }

    memcpy(part->color_start,gen->color_start,sizeof(GLfloat)*4);
    for(j=0;j<4;j++)
        part->color_start[j]+=raydium_random_f(-gen->color_start_random[j],gen->color_start_random[j]);

    memcpy(part->color_end,gen->color_end,sizeof(GLfloat)*4);
    for(j=0;j<4;j++)
        part->color_end[j]+=raydium_random_f(-gen->color_end_random[j],gen->color_end_random[j]);

    part->rotation_speed=raydium_random_f(gen->rotation_speed-gen->rotation_random,
                                          gen->rotation_speed+gen->rotation_random);

    part->visibility=gen->visibility;
    part->OnDelete=gen->OnDeleteParticle;
    }


if(gen->ttl_generator==0)
    return; // infinite generator

gen->ttl_generator-=step;
if(gen->ttl_generator<=0)
    {
    // we've a OnDelete callback for particles and not for
    // generators ... 'must code it.
    raydium_particle_generator_delete(gen->id);
    }
}

void raydium_particle_update(int part, GLfloat step)
{
int i;
GLfloat age,age_factor;
raydium_particle_Particle *p;

p=raydium_particle_particles[part];
// ttl
if(p->ttl!=0) // if not an infinite particle
{
 p->ttl-=step;
 if(p->ttl<=0) // "timeout" ...
    {
    void (*f)(raydium_particle_Particle *);

    f=p->OnDelete;
    if(f) f(p);

    free(p);
    raydium_particle_particles[part]=NULL;
    return;
    }
}

age=(p->ttl_init-p->ttl);
if(p->ttl_init)
    age_factor=age/p->ttl_init;
else
    age_factor=0;

// pos
for(i=0;i<3;i++)
    p->position[i]+=(p->vel[i]*step);

// vel
for(i=0;i<3;i++)
    p->vel[i]+=(p->gravity[i]*step);

// size
p->size+=(p->size_inc_per_sec*step);
if(p->size<0) p->size=0;
if(p->size>p->size_limit && p->size_limit>0) p->size=p->size_limit;

// color
for(i=0;i<4;i++)
    p->current_color[i]=(p->color_end[i]-p->color_start[i])*age_factor+p->color_start[i];

// rotation
p->current_rotation=age*p->rotation_speed;

}

void raydium_particle_callback(void)
{
int i;

//raydium_profile_start();

for(i=0;i<RAYDIUM_MAX_GENERATORS;i++)
    if(raydium_particle_generators[i].state)
        raydium_particle_generator_update(i,raydium_frame_time*raydium_particle_time_factor);

for(i=0;i<RAYDIUM_MAX_PARTICLES;i++)
    if(raydium_particle_particles[i])
        raydium_particle_update(i,raydium_frame_time*raydium_particle_time_factor);

//raydium_profile_end("particles updating");
}


int raydium_particle_state_dump(char *filename)
{
FILE *fp;
int i;
int cpt=0;
raydium_particle_Particle *p;

fp=raydium_file_fopen(filename,"wt");
if(!fp)
    {
    raydium_log("particle: ERROR: cannot create '%s' filename",filename);
    return 0;
    }

fprintf(fp,"0\n");

for(i=0;i<RAYDIUM_MAX_PARTICLES;i++)
    if(raydium_particle_particles[i])
        {
        cpt++;
        p=raydium_particle_particles[i];
        fprintf(fp,"%f %f %f %f %f %f %f %f %f %s\n",
                p->position[0],
                p->position[1],
                p->position[2],
                p->size,
                p->current_color[0],
                p->current_color[1],
                p->current_color[2],
                p->current_color[3],
                p->visibility,
                raydium_texture_name[p->texture]);
        }
fclose(fp);
raydium_log("particle: %i particle(s) dumped",cpt);
return 1;
}

int raydium_particle_state_restore(char *filename)
{
FILE *fp;
int p,visu,cpt=0;
raydium_particle_Particle *part;
GLfloat pos[3],color[4],size,visibility;
char texture[RAYDIUM_MAX_NAME_LEN];

fp=raydium_file_fopen(filename,"rt");
if(!fp)
    {
    raydium_log("particle: ERROR: cannot read from file '%s'",filename);
    return 0;
    }
fscanf(fp,"%i\n",&visu);

if(visu!=0)
    {
    raydium_log("particle: ERROR: '%s' file must be 'version 0'",filename);
    return 0;
    }


while( fscanf(fp,"%f %f %f %f %f %f %f %f %f %s\n",
    &pos[0],
    &pos[1],
    &pos[2],
    &size,
    &color[0],
    &color[1],
    &color[2],
    &color[3],
    &visibility,
    texture)!=EOF )
    {
    cpt++;
    p=raydium_particle_find_free();
    if(p<0)
        {
        raydium_log("particle: No more particle slots !");
        return -1;
        }

    raydium_particle_particles[p]=malloc(sizeof(raydium_particle_Particle));
    if(!raydium_particle_particles[p])
        {
        raydium_log("particle: ERROR: malloc failed !");
        return 0;
        }


    part=raydium_particle_particles[p];

    part->ttl_init=0;
    part->ttl=part->ttl_init;
    part->texture=raydium_texture_find_by_name(texture);

    memcpy(part->position,pos,sizeof(GLfloat)*3);

    part->size=size;
    part->size_inc_per_sec=0;
    part->size_limit=size+1;

    part->gravity[0]=part->gravity[1]=part->gravity[2]=0;

    part->vel[0]=part->vel[1]=part->vel[2]=0;

    memcpy(part->color_start,   color,sizeof(GLfloat)*4);
    memcpy(part->color_end,     color,sizeof(GLfloat)*4);
    memcpy(part->current_color, color,sizeof(GLfloat)*4);

    part->rotation_speed=0;
    part->visibility=visibility;
    part->OnDelete=NULL;
    }
fclose(fp);
raydium_log("particle: %i infinite particle(s) created",cpt);
return 1;
}


void raydium_particle_draw(raydium_particle_Particle *p,GLfloat ux, GLfloat uy, GLfloat uz, GLfloat rx, GLfloat ry, GLfloat rz)
{
GLfloat TSIZE;

if(!raydium_random_proba(p->visibility))
    return;

// need to order drawing by texture id
raydium_rendering_internal_prepare_texture_render(raydium_texture_current_set(p->texture));
glColor4fv(p->current_color);
TSIZE=p->size;

ux*=TSIZE/2;
uy*=TSIZE/2;
uz*=TSIZE/2;

rx*=TSIZE/2;
ry*=TSIZE/2;
rz*=TSIZE/2;

// p->rotation

  glBegin(GL_QUADS); // berk... but i'll switch to triangles one day ;)
      glTexCoord2f(0.0f, 0.0f);glVertex3f(p->position[0] + (ux - rx),p->position[1] + (uy - ry),p->position[2] + (uz - rz));
      glTexCoord2f(0.0f, 1.0f);glVertex3f(p->position[0] + (rx + ux),p->position[1] + (ry + uy),p->position[2] + (rz + uz));
      glTexCoord2f(1.0f, 1.0f);glVertex3f(p->position[0] + (rx - ux),p->position[1] + (ry - uy),p->position[2] + (rz - uz));
      glTexCoord2f(1.0f, 0.0f);glVertex3f(p->position[0] + (-rx - ux),p->position[1] + (-ry - uy),p->position[2] + (-rz - uz));
  glEnd();
}


void raydium_particle_scale_all(GLfloat scale)
{
raydium_particle_scale_factor=scale;
}

void raydium_particle_draw_all(void)
{
GLuint i;
GLuint texsave;
char light;
GLfloat modmat[16];
GLfloat ux;
GLfloat uy;
GLfloat uz;
GLfloat rx;
GLfloat ry;
GLfloat rz;

//raydium_profile_start();

texsave=raydium_texture_current_main;
light=raydium_light_enabled_tag;
raydium_light_disable();
if (raydium_camera_pushed) raydium_camera_replace(); // is it really our job to do it here ?
//raydium_rendering_internal_restore_render_state();

glGetFloatv(GL_MODELVIEW_MATRIX,modmat);
ux=modmat[0]*raydium_particle_scale_factor;
uy=modmat[4]*raydium_particle_scale_factor;
uz=modmat[8]*raydium_particle_scale_factor;
rx=modmat[1]*raydium_particle_scale_factor;
ry=modmat[5]*raydium_particle_scale_factor;
rz=modmat[9]*raydium_particle_scale_factor;

glDepthMask(GL_FALSE);

for(i=0;i<RAYDIUM_MAX_PARTICLES;i++)
    if(raydium_particle_particles[i])
        raydium_particle_draw(raydium_particle_particles[i],ux,uy,uz,rx,ry,rz);

glDepthMask(GL_TRUE);
if(light) raydium_light_enable();

raydium_texture_current_set(texsave);
//raydium_rendering_internal_prepare_texture_render(texsave);
//raydium_profile_end("particles drawing");
}


void raydium_particle_generator_move(int gen, GLfloat *pos)
{
if(!raydium_particle_generator_isvalid(gen))
    {
    raydium_log("particle: cannot move generator: invalid name or index");
    return;
    }
memcpy(raydium_particle_generators[gen].position_user,pos,sizeof(GLfloat)*3);
}

void raydium_particle_generator_move_name(char *gen, GLfloat *pos)
{
raydium_particle_generator_move(raydium_particle_generator_find(gen),pos);
}

void raydium_particle_generator_move_name_3f(char *gen, GLfloat x, GLfloat y, GLfloat z)
{
GLfloat tmp[3];
tmp[0]=x;
tmp[1]=y;
tmp[2]=z;
raydium_particle_generator_move_name(gen,tmp);
}

void raydium_particle_generator_particles_OnDelete(int gen, void *OnDelete)
{
if(!raydium_particle_generator_isvalid(gen))
    {
    raydium_log("particle: cannot set OnDelete: invalid name or index");
    return;
    }
raydium_particle_generators[gen].OnDeleteParticle=OnDelete;
}

void raydium_particle_generator_particles_OnDelete_name(char *gen, void *OnDelete)
{
raydium_particle_generator_particles_OnDelete(raydium_particle_generator_find(gen),OnDelete);
}