Some cleanup of particle path drawing logic:
* Path drawing now works for non hair particles. * Should fix the following bugs too: [#21316] Hair weight drawing is wrong [#21923] Consistent Crash When Rendering Particle Scene. [#21950] Path rendering option for particles causes crash
This commit is contained in:
@@ -705,12 +705,6 @@ class PARTICLE_PT_render(ParticleButtonsPanel):
|
||||
sub = split.column()
|
||||
sub.prop(part, "velocity_length")
|
||||
elif part.ren_as == 'PATH':
|
||||
|
||||
if part.type != 'HAIR' and part.physics_type != 'KEYED' and (psys.point_cache.baked is False):
|
||||
box = layout.box()
|
||||
box.label(text="Baked or keyed particles needed for correct rendering.")
|
||||
return
|
||||
|
||||
sub.prop(part, "render_strand")
|
||||
subsub = sub.column()
|
||||
subsub.active = (part.render_strand is False)
|
||||
@@ -862,11 +856,6 @@ class PARTICLE_PT_draw(ParticleButtonsPanel):
|
||||
|
||||
path = (part.ren_as == 'PATH' and part.draw_as == 'RENDER') or part.draw_as == 'PATH'
|
||||
|
||||
if path and part.type != 'HAIR' and part.physics_type != 'KEYED' and psys.point_cache.baked is False:
|
||||
box = layout.box()
|
||||
box.label(text="Baked or keyed particles needed for correct drawing.")
|
||||
return
|
||||
|
||||
row = layout.row()
|
||||
row.prop(part, "display", slider=True)
|
||||
if part.draw_as != 'RENDER' or part.ren_as == 'HALO':
|
||||
|
@@ -255,9 +255,11 @@ void psys_threads_free(ParticleThread *threads);
|
||||
void psys_make_billboard(ParticleBillboardData *bb, float xvec[3], float yvec[3], float zvec[3], float center[3]);
|
||||
|
||||
/* particle_system.c */
|
||||
void psys_update_path_cache(struct ParticleSimulationData *sim, float cfra);
|
||||
struct ParticleSystem *psys_get_target_system(struct Object *ob, struct ParticleTarget *pt);
|
||||
void psys_count_keyed_targets(struct ParticleSimulationData *sim);
|
||||
void psys_update_particle_tree(struct ParticleSystem *psys, float cfra);
|
||||
void psys_update_children(struct ParticleSimulationData *sim);
|
||||
|
||||
void psys_make_temp_pointcache(struct Object *ob, struct ParticleSystem *psys);
|
||||
void psys_get_pointcache_start_end(struct Scene *scene, ParticleSystem *psys, int *sfra, int *efra);
|
||||
|
@@ -433,7 +433,7 @@ void free_keyed_keys(ParticleSystem *psys)
|
||||
}
|
||||
}
|
||||
}
|
||||
static void free_child_path_cache(ParticleSystem *psys)
|
||||
void psys_free_child_path_cache(ParticleSystem *psys)
|
||||
{
|
||||
psys_free_path_cache_buffers(psys->childcache, &psys->childcachebufs);
|
||||
psys->childcache = NULL;
|
||||
@@ -451,7 +451,7 @@ void psys_free_path_cache(ParticleSystem *psys, PTCacheEdit *edit)
|
||||
psys->pathcache= NULL;
|
||||
psys->totcached= 0;
|
||||
|
||||
free_child_path_cache(psys);
|
||||
psys_free_child_path_cache(psys);
|
||||
}
|
||||
}
|
||||
void psys_free_children(ParticleSystem *psys)
|
||||
@@ -462,7 +462,7 @@ void psys_free_children(ParticleSystem *psys)
|
||||
psys->totchild=0;
|
||||
}
|
||||
|
||||
free_child_path_cache(psys);
|
||||
psys_free_child_path_cache(psys);
|
||||
}
|
||||
void psys_free_particles(ParticleSystem *psys)
|
||||
{
|
||||
@@ -1037,6 +1037,7 @@ typedef struct ParticleInterpolationData {
|
||||
ParticleKey *kkey[2];
|
||||
|
||||
PointCache *cache;
|
||||
PTCacheMem *pm;
|
||||
|
||||
PTCacheEditPoint *epoint;
|
||||
PTCacheEditKey *ekey[2];
|
||||
@@ -1045,31 +1046,74 @@ typedef struct ParticleInterpolationData {
|
||||
int bspline;
|
||||
} ParticleInterpolationData;
|
||||
/* Assumes pointcache->mem_cache exists, so for disk cached particles call psys_make_temp_pointcache() before use */
|
||||
static void get_pointcache_keys_for_time(Object *ob, PointCache *cache, int index, float t, ParticleKey *key1, ParticleKey *key2)
|
||||
/* It uses ParticleInterpolationData->pm to store the current memory cache frame so it's thread safe. */
|
||||
static void get_pointcache_keys_for_time(Object *ob, PointCache *cache, PTCacheMem **cur, int index, float t, ParticleKey *key1, ParticleKey *key2)
|
||||
{
|
||||
static PTCacheMem *pm = NULL; /* not thread safe */
|
||||
static PTCacheMem *pm = NULL;
|
||||
|
||||
if(index < 0) { /* initialize */
|
||||
pm = cache->mem_cache.first;
|
||||
*cur = cache->mem_cache.first;
|
||||
|
||||
if(pm)
|
||||
pm = pm->next;
|
||||
if(*cur)
|
||||
*cur = (*cur)->next;
|
||||
}
|
||||
else {
|
||||
if(pm) {
|
||||
while(pm && pm->next && (float)pm->frame < t)
|
||||
pm = pm->next;
|
||||
if(*cur) {
|
||||
while(*cur && (*cur)->next && (float)(*cur)->frame < t)
|
||||
*cur = (*cur)->next;
|
||||
|
||||
pm = *cur;
|
||||
|
||||
BKE_ptcache_make_particle_key(key2, pm->index_array ? pm->index_array[index] - 1 : index, pm->data, (float)pm->frame);
|
||||
BKE_ptcache_make_particle_key(key1, pm->prev->index_array ? pm->prev->index_array[index] - 1 : index, pm->prev->data, (float)pm->prev->frame);
|
||||
if(pm->prev->index_array && pm->prev->index_array[index] == 0)
|
||||
copy_particle_key(key1, key2, 1);
|
||||
else
|
||||
BKE_ptcache_make_particle_key(key1, pm->prev->index_array ? pm->prev->index_array[index] - 1 : index, pm->prev->data, (float)pm->prev->frame);
|
||||
}
|
||||
else if(cache->mem_cache.first) {
|
||||
PTCacheMem *pm2 = cache->mem_cache.first;
|
||||
BKE_ptcache_make_particle_key(key2, pm2->index_array ? pm2->index_array[index] - 1 : index, pm2->data, (float)pm2->frame);
|
||||
pm = cache->mem_cache.first;
|
||||
BKE_ptcache_make_particle_key(key2, pm->index_array ? pm->index_array[index] - 1 : index, pm->data, (float)pm->frame);
|
||||
copy_particle_key(key1, key2, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
static int get_pointcache_times_for_particle(PointCache *cache, int index, float *start, float *end)
|
||||
{
|
||||
PTCacheMem *pm;
|
||||
int ret = 0;
|
||||
|
||||
for(pm=cache->mem_cache.first; pm; pm=pm->next) {
|
||||
if(pm->index_array) {
|
||||
if(pm->index_array[index]) {
|
||||
*start = pm->frame;
|
||||
ret++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
*start = pm->frame;
|
||||
ret++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for(pm=cache->mem_cache.last; pm; pm=pm->prev) {
|
||||
if(pm->index_array) {
|
||||
if(pm->index_array[index]) {
|
||||
*end = pm->frame;
|
||||
ret++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
*end = pm->frame;
|
||||
ret++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return ret == 2;
|
||||
}
|
||||
static void init_particle_interpolation(Object *ob, ParticleSystem *psys, ParticleData *pa, ParticleInterpolationData *pind)
|
||||
{
|
||||
|
||||
@@ -1091,10 +1135,15 @@ static void init_particle_interpolation(Object *ob, ParticleSystem *psys, Partic
|
||||
pind->dietime = (key + pa->totkey - 1)->time;
|
||||
}
|
||||
else if(pind->cache) {
|
||||
get_pointcache_keys_for_time(ob, pind->cache, -1, 0.0f, NULL, NULL);
|
||||
|
||||
float start, end;
|
||||
get_pointcache_keys_for_time(ob, pind->cache, &pind->pm, -1, 0.0f, NULL, NULL);
|
||||
pind->birthtime = pa ? pa->time : pind->cache->startframe;
|
||||
pind->dietime = pa ? pa->dietime : pind->cache->endframe;
|
||||
|
||||
if(get_pointcache_times_for_particle(pind->cache, pa - psys->particles, &start, &end)) {
|
||||
pind->birthtime = MAX2(pind->birthtime, start);
|
||||
pind->dietime = MIN2(pind->dietime, end);
|
||||
}
|
||||
}
|
||||
else {
|
||||
HairKey *key = pa->hair;
|
||||
@@ -1224,7 +1273,7 @@ static void do_particle_interpolation(ParticleSystem *psys, int p, ParticleData
|
||||
memcpy(keys + 2, pind->kkey[1], sizeof(ParticleKey));
|
||||
}
|
||||
else if(pind->cache) {
|
||||
get_pointcache_keys_for_time(NULL, pind->cache, p, real_t, keys+1, keys+2);
|
||||
get_pointcache_keys_for_time(NULL, pind->cache, &pind->pm, p, real_t, keys+1, keys+2);
|
||||
}
|
||||
else {
|
||||
hair_to_particle(keys + 1, pind->hkey[0]);
|
||||
@@ -2672,7 +2721,7 @@ void psys_cache_child_paths(ParticleSimulationData *sim, float cfra, int editupd
|
||||
}
|
||||
else {
|
||||
/* clear out old and create new empty path cache */
|
||||
free_child_path_cache(sim->psys);
|
||||
psys_free_child_path_cache(sim->psys);
|
||||
sim->psys->childcache= psys_alloc_path_cache_buffers(&sim->psys->childcachebufs, totchild, ctx->steps+1);
|
||||
sim->psys->totchildcache = totchild;
|
||||
}
|
||||
@@ -2743,7 +2792,7 @@ void psys_cache_paths(ParticleSimulationData *sim, float cfra)
|
||||
int keyed, baked;
|
||||
|
||||
/* we don't have anything valid to create paths from so let's quit here */
|
||||
if((psys->flag & PSYS_HAIR_DONE || psys->flag & PSYS_KEYED || psys->pointcache->flag & PTCACHE_BAKED)==0)
|
||||
if((psys->flag & PSYS_HAIR_DONE || psys->flag & PSYS_KEYED || psys->pointcache)==0)
|
||||
return;
|
||||
|
||||
if(psys_in_edit_mode(sim->scene, psys))
|
||||
@@ -2753,7 +2802,7 @@ void psys_cache_paths(ParticleSimulationData *sim, float cfra)
|
||||
BLI_srandom(psys->seed);
|
||||
|
||||
keyed = psys->flag & PSYS_KEYED;
|
||||
baked = !hair_dm && psys->pointcache->flag & PTCACHE_BAKED;
|
||||
baked = !hair_dm && psys->pointcache->mem_cache.first;
|
||||
|
||||
/* clear out old and create new empty path cache */
|
||||
psys_free_path_cache(psys, psys->edit);
|
||||
@@ -3148,7 +3197,7 @@ void psys_cache_edit_paths(Scene *scene, Object *ob, PTCacheEdit *edit, float cf
|
||||
|
||||
edit->totcached = totpart;
|
||||
|
||||
if(psys && psys->part->type == PART_HAIR) {
|
||||
if(psys) {
|
||||
ParticleSimulationData sim = {scene, ob, psys, psys_get_modifier(ob, psys), NULL};
|
||||
psys_cache_child_paths(&sim, cfra, 1);
|
||||
}
|
||||
|
@@ -3075,66 +3075,18 @@ static void deflect_particle(ParticleSimulationData *sim, int p, float dfra, flo
|
||||
/* Hair */
|
||||
/************************************************/
|
||||
/* check if path cache or children need updating and do it if needed */
|
||||
static void psys_update_path_cache(ParticleSimulationData *sim, float cfra)
|
||||
void psys_update_path_cache(ParticleSimulationData *sim, float cfra)
|
||||
{
|
||||
ParticleSystem *psys = sim->psys;
|
||||
ParticleSettings *part = psys->part;
|
||||
ParticleEditSettings *pset = &sim->scene->toolsettings->particle;
|
||||
int distr=0, alloc=0, skip=0;
|
||||
|
||||
if((psys->part->childtype && psys->totchild != get_psys_tot_child(sim->scene, psys)) || psys->recalc&PSYS_RECALC_RESET)
|
||||
alloc=1;
|
||||
|
||||
if(alloc || psys->recalc&PSYS_RECALC_CHILD || (psys->vgroup[PSYS_VG_DENSITY] && (sim->ob && sim->ob->mode & OB_MODE_WEIGHT_PAINT)))
|
||||
distr=1;
|
||||
|
||||
if(distr){
|
||||
if(alloc)
|
||||
realloc_particles(sim, sim->psys->totpart);
|
||||
|
||||
if(get_psys_tot_child(sim->scene, psys)) {
|
||||
/* don't generate children while computing the hair keys */
|
||||
if(!(psys->part->type == PART_HAIR) || (psys->flag & PSYS_HAIR_DONE)) {
|
||||
distribute_particles(sim, PART_FROM_CHILD);
|
||||
|
||||
if(part->from!=PART_FROM_PARTICLE && part->childtype==PART_CHILD_FACES && part->parents!=0.0)
|
||||
psys_find_parents(sim);
|
||||
}
|
||||
}
|
||||
else
|
||||
psys_free_children(psys);
|
||||
}
|
||||
|
||||
if((part->type==PART_HAIR || psys->flag&PSYS_KEYED || psys->pointcache->flag & PTCACHE_BAKED)==0)
|
||||
skip = 1; /* only hair, keyed and baked stuff can have paths */
|
||||
else if(part->ren_as != PART_DRAW_PATH && !(part->type==PART_HAIR && ELEM(part->ren_as, PART_DRAW_OB, PART_DRAW_GR)))
|
||||
skip = 1; /* particle visualization must be set as path */
|
||||
else if(!psys->renderdata) {
|
||||
if(part->draw_as != PART_DRAW_REND)
|
||||
skip = 1; /* draw visualization */
|
||||
else if(psys->pointcache->flag & PTCACHE_BAKING)
|
||||
skip = 1; /* no need to cache paths while baking dynamics */
|
||||
else if(psys_in_edit_mode(sim->scene, psys)) {
|
||||
if((pset->flag & PE_DRAW_PART)==0)
|
||||
skip = 1;
|
||||
else if(part->childtype==0 && (psys->flag & PSYS_HAIR_DYNAMICS && psys->pointcache->flag & PTCACHE_BAKED)==0)
|
||||
skip = 1; /* in edit mode paths are needed for child particles and dynamic hair */
|
||||
}
|
||||
}
|
||||
|
||||
if(!skip) {
|
||||
|
||||
/* only hair, keyed and baked stuff can have paths */
|
||||
if(part->type==PART_HAIR || psys->flag&PSYS_KEYED || psys->pointcache->mem_cache.first) {
|
||||
psys_cache_paths(sim, cfra);
|
||||
|
||||
/* for render, child particle paths are computed on the fly */
|
||||
if(part->childtype) {
|
||||
if(!psys->totchild)
|
||||
skip = 1;
|
||||
else if((psys->part->type == PART_HAIR && psys->flag & PSYS_HAIR_DONE)==0)
|
||||
skip = 1;
|
||||
|
||||
if(!skip)
|
||||
psys_cache_child_paths(sim, cfra, 0);
|
||||
}
|
||||
if(part->childtype && psys->totchild)
|
||||
psys_cache_child_paths(sim, cfra, 0);
|
||||
}
|
||||
else if(psys->pathcache)
|
||||
psys_free_path_cache(psys, NULL);
|
||||
@@ -3249,6 +3201,8 @@ static void do_hair_dynamics(ParticleSimulationData *sim)
|
||||
psys->hair_out_dm = clothModifier_do(psys->clmd, sim->scene, sim->ob, dm, 0, 0);
|
||||
|
||||
psys->clmd->sim_parms->effector_weights = NULL;
|
||||
|
||||
psys_free_path_cache(psys, NULL);
|
||||
}
|
||||
static void hair_step(ParticleSimulationData *sim, float cfra)
|
||||
{
|
||||
@@ -3278,10 +3232,6 @@ static void hair_step(ParticleSimulationData *sim, float cfra)
|
||||
if(psys->part->type==PART_HAIR && psys->flag & PSYS_HAIR_DYNAMICS)
|
||||
do_hair_dynamics(sim);
|
||||
|
||||
psys_update_effectors(sim);
|
||||
|
||||
psys_update_path_cache(sim, cfra);
|
||||
|
||||
psys->flag |= PSYS_HAIR_UPDATED;
|
||||
}
|
||||
|
||||
@@ -3500,14 +3450,19 @@ static void dynamics_step(ParticleSimulationData *sim, float cfra)
|
||||
}
|
||||
|
||||
free_collider_cache(&sim->colliders);
|
||||
|
||||
if(psys->pathcache)
|
||||
psys_free_path_cache(psys, NULL);
|
||||
}
|
||||
static void update_children(ParticleSimulationData *sim)
|
||||
void psys_update_children(ParticleSimulationData *sim)
|
||||
{
|
||||
if((sim->psys->part->type == PART_HAIR) && (sim->psys->flag & PSYS_HAIR_DONE)==0)
|
||||
/* don't generate children while growing hair - waste of time */
|
||||
psys_free_children(sim->psys);
|
||||
else if(sim->psys->part->childtype && sim->psys->totchild != get_psys_tot_child(sim->scene, sim->psys))
|
||||
distribute_particles(sim, PART_FROM_CHILD);
|
||||
else if(sim->psys->part->childtype) {
|
||||
if(sim->psys->totchild != get_psys_tot_child(sim->scene, sim->psys))
|
||||
distribute_particles(sim, PART_FROM_CHILD);
|
||||
}
|
||||
else
|
||||
psys_free_children(sim->psys);
|
||||
}
|
||||
@@ -3762,8 +3717,6 @@ static void system_step(ParticleSimulationData *sim, float cfra)
|
||||
|
||||
if(ELEM(cache_result, PTCACHE_READ_EXACT, PTCACHE_READ_INTERPOLATED)) {
|
||||
cached_step(sim, cfra);
|
||||
update_children(sim);
|
||||
psys_update_path_cache(sim, cfra);
|
||||
|
||||
BKE_ptcache_validate(cache, framenr);
|
||||
|
||||
@@ -3827,9 +3780,6 @@ static void system_step(ParticleSimulationData *sim, float cfra)
|
||||
BKE_ptcache_write_cache(use_cache, framenr);
|
||||
}
|
||||
|
||||
if(init)
|
||||
update_children(sim);
|
||||
|
||||
/* cleanup */
|
||||
if(psys->lattice){
|
||||
end_latt_deform(psys->lattice);
|
||||
@@ -3992,6 +3942,13 @@ void particle_system_update(Scene *scene, Object *ob, ParticleSystem *psys)
|
||||
/* execute drivers only, as animation has already been done */
|
||||
BKE_animsys_evaluate_animdata(&part->id, part->adt, cfra, ADT_RECALC_DRIVERS);
|
||||
|
||||
/* TODO: only free child paths in case of PSYS_RECALC_CHILD */
|
||||
if(psys->recalc & PSYS_RECALC)
|
||||
psys_free_path_cache(psys, NULL);
|
||||
|
||||
if(psys->recalc & PSYS_RECALC_CHILD)
|
||||
psys_free_children(psys);
|
||||
|
||||
if(psys->recalc & PSYS_RECALC_TYPE)
|
||||
psys_changed_type(&sim);
|
||||
else if(psys->recalc & PSYS_RECALC_PHYS)
|
||||
@@ -4048,7 +4005,6 @@ void particle_system_update(Scene *scene, Object *ob, ParticleSystem *psys)
|
||||
if(part->phystype == PART_PHYS_KEYED) {
|
||||
psys_count_keyed_targets(&sim);
|
||||
set_keyed_keys(&sim);
|
||||
psys_update_path_cache(&sim,(int)cfra);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@@ -160,7 +160,7 @@ void PE_free_ptcache_edit(PTCacheEdit *edit)
|
||||
edit->emitter_field= 0;
|
||||
}
|
||||
|
||||
psys_free_path_cache(NULL, edit);
|
||||
psys_free_path_cache(edit->psys, edit);
|
||||
|
||||
MEM_freeN(edit);
|
||||
}
|
||||
|
@@ -3466,12 +3466,11 @@ static void draw_new_particle_system(Scene *scene, View3D *v3d, RegionView3D *rv
|
||||
select=1;
|
||||
}
|
||||
|
||||
psys_update_children(&sim);
|
||||
|
||||
psys->flag|=PSYS_DRAWING;
|
||||
|
||||
if(part->type==PART_HAIR && !psys->childcache)
|
||||
totchild=0;
|
||||
else
|
||||
totchild=psys->totchild*part->disp/100;
|
||||
totchild=psys->totchild*part->disp/100;
|
||||
|
||||
ma= give_current_material(ob,part->omat);
|
||||
|
||||
@@ -3506,11 +3505,18 @@ static void draw_new_particle_system(Scene *scene, View3D *v3d, RegionView3D *rv
|
||||
|
||||
totpart=psys->totpart;
|
||||
|
||||
//if(part->flag&PART_GLOB_TIME)
|
||||
cfra=bsystem_time(scene, 0, (float)CFRA, 0.0f);
|
||||
|
||||
if(draw_as==PART_DRAW_PATH && psys->pathcache==NULL && psys->childcache==NULL)
|
||||
draw_as=PART_DRAW_DOT;
|
||||
if(draw_as==PART_DRAW_PATH) {
|
||||
if(psys->pathcache==NULL && psys->childcache==NULL)
|
||||
psys_update_path_cache(&sim, cfra);
|
||||
|
||||
/* can't create pathcache for some reason*/
|
||||
if(psys->pathcache==NULL && psys->childcache==NULL)
|
||||
draw_as=PART_DRAW_DOT;
|
||||
else if(psys->childcache==NULL)
|
||||
totchild = 0;
|
||||
}
|
||||
|
||||
/* 3. */
|
||||
switch(draw_as){
|
||||
@@ -3862,7 +3868,7 @@ static void draw_new_particle_system(Scene *scene, View3D *v3d, RegionView3D *rv
|
||||
UI_ThemeColor(TH_WIRE);
|
||||
}*/
|
||||
|
||||
if(totchild && (part->draw&PART_DRAW_PARENT)==0)
|
||||
if(totchild && ((part->draw&PART_DRAW_PARENT)==0 || psys_in_edit_mode(scene, psys)))
|
||||
totpart=0;
|
||||
else if(psys->pathcache==NULL)
|
||||
totpart=0;
|
||||
|
@@ -1532,8 +1532,6 @@ static int render_new_particle_system(Render *re, ObjectRen *obr, ParticleSystem
|
||||
/* 1. check that everything is ok & updated */
|
||||
if(psys==NULL)
|
||||
return 0;
|
||||
|
||||
totchild=psys->totchild;
|
||||
|
||||
part=psys->part;
|
||||
pars=psys->particles;
|
||||
@@ -1554,6 +1552,8 @@ static int render_new_particle_system(Render *re, ObjectRen *obr, ParticleSystem
|
||||
if(part->phystype==PART_PHYS_KEYED)
|
||||
psys_count_keyed_targets(&sim);
|
||||
|
||||
psys_update_children(&sim);
|
||||
totchild=psys->totchild;
|
||||
|
||||
if(G.rendering == 0) { /* preview render */
|
||||
totchild = (int)((float)totchild * (float)part->disp / 100.0f);
|
||||
@@ -1657,6 +1657,9 @@ static int render_new_particle_system(Render *re, ObjectRen *obr, ParticleSystem
|
||||
transpose_m3(nmat);
|
||||
|
||||
/* 2.6 setup strand rendering */
|
||||
if(part->ren_as == PART_DRAW_PATH && psys->pathcache==NULL)
|
||||
psys_update_path_cache(&sim, cfra);
|
||||
|
||||
if(part->ren_as == PART_DRAW_PATH && psys->pathcache){
|
||||
path_nbr=(int)pow(2.0,(double) part->ren_step);
|
||||
|
||||
|
Reference in New Issue
Block a user