From 0f2b2e3c60173e85e137df71392d867576e65ac1 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Thu, 20 Dec 2007 16:35:27 +0000 Subject: Strand Render Simplification ============================ - Strand render now has options to remove child strands as the object's faces becomes smaller, in the Simplification particle panel. - "Reference Size" is the approximate size of the object on screen, after which simplification starts. - "Rate" is how fast strands are removed. - "Transition" is the percentage of strands being faded out as they are removed. - Another "Viewport" option removes strands on faces that are outside of the viewport. "Rate" again controls how fast these are removed. - Strand render in Blender Units now has an adjustable minimum width. Below this minimum width, strands start fading out instead of getting smaller. --- source/blender/blenkernel/BKE_particle.h | 10 +- source/blender/blenkernel/intern/particle.c | 308 ++++++++++++++++++++- source/blender/blenkernel/intern/particle_system.c | 103 ++++--- source/blender/blenloader/intern/readfile.c | 16 +- source/blender/makesdna/DNA_material_types.h | 1 + source/blender/makesdna/DNA_particle_types.h | 9 + .../blender/render/intern/include/render_types.h | 2 +- .../blender/render/intern/include/renderdatabase.h | 2 + source/blender/render/intern/include/strand.h | 3 + .../blender/render/intern/source/convertblender.c | 45 +-- .../blender/render/intern/source/renderdatabase.c | 18 ++ source/blender/render/intern/source/strand.c | 49 +++- source/blender/src/buttons_object.c | 44 ++- source/blender/src/buttons_shading.c | 34 ++- 14 files changed, 545 insertions(+), 99 deletions(-) (limited to 'source/blender') diff --git a/source/blender/blenkernel/BKE_particle.h b/source/blender/blenkernel/BKE_particle.h index 1c69fe613c7..a1ac97406e1 100644 --- a/source/blender/blenkernel/BKE_particle.h +++ b/source/blender/blenkernel/BKE_particle.h @@ -112,8 +112,8 @@ typedef struct ParticleSeam{ typedef struct ParticleCacheKey{ float co[3]; float vel[3]; - float rot[4]; float col[3]; + float rot[4]; int steps; } ParticleCacheKey; @@ -165,7 +165,7 @@ typedef struct ParticleThreadContext { float *jit, *jitoff, *weight; float maxweight; - int *index, jitlevel; + int *index, *skip, jitlevel; int from, cfrom, distr; @@ -214,8 +214,10 @@ void free_hair(struct ParticleSystem *psys); void free_keyed_keys(struct ParticleSystem *psys); void psys_free(struct Object * ob, struct ParticleSystem * psys); -void psys_particles_to_render_backup(struct Object *ob, struct ParticleSystem *psys); -void psys_render_backup_to_particles(struct Object *ob, struct ParticleSystem *psys); +void psys_render_set(struct Object *ob, struct ParticleSystem *psys, float viewmat[][4], float winmat[][4], int winx, int winy); +void psys_render_restore(struct Object *ob, struct ParticleSystem *psys); +int psys_render_simplify_distribution(struct ParticleThreadContext *ctx, int tot); +int psys_render_simplify_params(struct ParticleSystem *psys, struct ChildParticle *cpa, float *params); void clear_particles_from_cache(struct Object *ob, struct ParticleSystem *psys, int cfra); //void psys_remove_from_particle_list(struct Object *ob, short nbr, struct ParticleSystem *psys); diff --git a/source/blender/blenkernel/intern/particle.c b/source/blender/blenkernel/intern/particle.c index 85ac809bf70..32f1795747a 100644 --- a/source/blender/blenkernel/intern/particle.c +++ b/source/blender/blenkernel/intern/particle.c @@ -295,7 +295,6 @@ void free_keyed_keys(ParticleSystem *psys) } void free_child_path_cache(ParticleSystem *psys) { - if(psys->childcache){ if(psys->childcache[0]) MEM_freeN(psys->childcache[0]); @@ -363,28 +362,98 @@ void psys_free(Object *ob, ParticleSystem * psys) } } -/* these two functions move away particle data and bring it back after +/* these functions move away particle data and bring it back after * rendering, to make different render settings possible without * removing the previous data. this should be solved properly once */ -typedef struct ParticleRenderDataup { +typedef struct ParticleRenderElem { + int curchild, totchild; + float lambda, t, scalemin, scalemax; +} ParticleRenderElem; + +typedef struct ParticleRenderData { ChildParticle *child; ParticleCacheKey **pathcache; ParticleCacheKey **childcache; int totchild, totcached, totchildcache; DerivedMesh *dm; int totdmvert, totdmedge, totdmface; -} ParticleRenderDataup; -void psys_particles_to_render_backup(Object *ob, ParticleSystem *psys) + float mat[4][4]; + float viewmat[4][4], winmat[4][4]; + int winx, winy; + + int dosimplify; + ParticleRenderElem *elems; + int *origindex; +} ParticleRenderData; + +static float psys_render_viewport_falloff(double rate, float dist, float width) +{ + return pow(rate, dist/width); +} + +static float psys_render_projected_area(ParticleSystem *psys, float *center, float area, double vprate, float *viewport) +{ + ParticleRenderData *data= psys->renderdata; + float co[3], view[3], ortho1[3], ortho2[2], w, dx, dy, radius; + + /* transform to view space */ + VECCOPY(co, center); + co[3]= 1.0f; + Mat4MulVec4fl(data->viewmat, co); + + /* compute two vectors orthogonal to view vector */ + VECCOPY(view, co); + Normalize(view); + VecOrthoBasisf(view, ortho1, ortho2); + + /* compute on screen minification */ + w= co[2]*data->winmat[2][3] + data->winmat[3][3]; + dx= data->winx*ortho2[0]*data->winmat[0][0]; + dy= data->winy*ortho2[1]*data->winmat[1][1]; + w= sqrt(dx*dx + dy*dy)/w; + + /* w squared because we are working with area */ + area= area*w*w; + + /* viewport of the screen test */ + + /* project point on screen */ + Mat4MulVec4fl(data->winmat, co); + if(co[3] != 0.0f) { + co[0]= 0.5f*data->winx*(1.0f + co[0]/co[3]); + co[1]= 0.5f*data->winy*(1.0f + co[1]/co[3]); + } + + /* screen space radius */ + radius= sqrt(area/M_PI); + + /* make smaller using fallof once over screen edge */ + *viewport= 1.0f; + + if(co[0]+radius < 0.0f) + *viewport *= psys_render_viewport_falloff(vprate, -(co[0]+radius), data->winx); + else if(co[0]-radius > data->winx) + *viewport *= psys_render_viewport_falloff(vprate, (co[0]-radius) - data->winx, data->winx); + + if(co[1]+radius < 0.0f) + *viewport *= psys_render_viewport_falloff(vprate, -(co[1]+radius), data->winy); + else if(co[1]-radius > data->winy) + *viewport *= psys_render_viewport_falloff(vprate, (co[1]-radius) - data->winy, data->winy); + + return area; +} + +void psys_render_set(Object *ob, ParticleSystem *psys, float viewmat[][4], float winmat[][4], int winx, int winy) { - ParticleRenderDataup *data; + ParticleRenderData*data; ParticleSystemModifierData *psmd= psys_get_modifier(ob, psys); if(!G.rendering) return; - data= MEM_callocN(sizeof(ParticleRenderDataup), "ParticleRenderDataup"); + data= MEM_callocN(sizeof(ParticleRenderData), "ParticleRenderData"); data->child= psys->child; data->totchild= psys->totchild; @@ -404,17 +473,26 @@ void psys_particles_to_render_backup(Object *ob, ParticleSystem *psys) psys->childcache= NULL; psys->totchild= psys->totcached= psys->totchildcache= 0; + Mat4CpyMat4(data->winmat, winmat); + Mat4MulMat4(data->viewmat, ob->obmat, viewmat); + Mat4MulMat4(data->mat, data->viewmat, winmat); + data->winx= winx; + data->winy= winy; + psys->renderdata= data; } -void psys_render_backup_to_particles(Object *ob, ParticleSystem *psys) +void psys_render_restore(Object *ob, ParticleSystem *psys) { - ParticleRenderDataup *data; + ParticleRenderData*data; ParticleSystemModifierData *psmd= psys_get_modifier(ob, psys); data= psys->renderdata; if(!data) return; + + if(data->elems) + MEM_freeN(data->elems); if(psmd->dm) { psmd->dm->needsFree= 1; @@ -428,7 +506,7 @@ void psys_render_backup_to_particles(Object *ob, ParticleSystem *psys) psys->child= 0; psys->totchild= 0; } - + psys->child= data->child; psys->totchild= data->totchild; psys->pathcache= data->pathcache; @@ -449,6 +527,211 @@ void psys_render_backup_to_particles(Object *ob, ParticleSystem *psys) psys->renderdata= NULL; } +int psys_render_simplify_distribution(ParticleThreadContext *ctx, int tot) +{ + DerivedMesh *dm= ctx->dm; + Mesh *me= (Mesh*)(ctx->ob->data); + MFace *mf, *mface; + MVert *mvert; + ParticleRenderData *data; + ParticleRenderElem *elems, *elem; + ParticleSettings *part= ctx->psys->part; + float *facearea, (*facecenter)[3], size[3], fac, powrate; + float co1[3], co2[3], co3[3], co4[3], lambda, arearatio, t, area, viewport; + double vprate; + int *origindex, *facetotvert; + int a, b, totorigface, totface, newtot, skipped; + + if(part->draw_as!=PART_DRAW_PATH || !(part->draw & PART_DRAW_REN_STRAND)) + return tot; + if(!ctx->psys->renderdata || !(part->simplify_flag & PART_SIMPLIFY_ENABLE)) + return tot; + + mvert= dm->getVertArray(dm); + mface= dm->getFaceArray(dm); + origindex= dm->getFaceDataArray(dm, CD_ORIGINDEX); + totface= dm->getNumFaces(dm); + totorigface= me->totface; + + if(totface == 0 || totorigface == 0 || origindex == NULL) + return tot; + + facearea= MEM_callocN(sizeof(float)*totorigface, "SimplifyFaceArea"); + facecenter= MEM_callocN(sizeof(float[3])*totorigface, "SimplifyFaceCenter"); + facetotvert= MEM_callocN(sizeof(int)*totorigface, "SimplifyFaceArea"); + elems= MEM_callocN(sizeof(ParticleRenderElem)*totorigface, "SimplifyFaceElem"); + + data= ctx->psys->renderdata; + data->dosimplify= 1; + data->elems= elems; + data->origindex= origindex; + + /* compute number of children per original face */ + for(a=0; aindex[a]]; + if(b != -1) + elems[b].totchild++; + } + + /* compute areas and centers of original faces */ + for(mf=mface, a=0; av1].co); + VECCOPY(co2, mvert[mf->v2].co); + VECCOPY(co3, mvert[mf->v3].co); + + VECADD(facecenter[b], facecenter[b], co1); + VECADD(facecenter[b], facecenter[b], co2); + VECADD(facecenter[b], facecenter[b], co3); + + if(mf->v4) { + VECCOPY(co4, mvert[mf->v4].co); + VECADD(facecenter[b], facecenter[b], co4); + facearea[b] += AreaQ3Dfl(co1, co2, co3, co4); + facetotvert[b] += 4; + } + else { + facearea[b] += AreaT3Dfl(co1, co2, co3); + facetotvert[b] += 3; + } + } + } + + for(a=0; a 0) + VecMulf(facecenter[a], 1.0f/facetotvert[a]); + + /* for conversion from BU area / pixel area to reference screen size */ + mesh_get_texspace(me, 0, 0, size); + fac= ((size[0] + size[1] + size[2])/3.0f)/part->simplify_refsize; + fac= fac*fac; + + powrate= log(0.5f)/log(part->simplify_rate*0.5f); + if(part->simplify_flag & PART_SIMPLIFY_VIEWPORT) + vprate= pow(1.0 - part->simplify_viewport, 5.0); + else + vprate= 1.0; + + /* set simplification parameters per original face */ + for(a=0, elem=elems; apsys, facecenter[a], facearea[a], vprate, &viewport); + arearatio= fac*area/facearea[a]; + + if(arearatio < 1.0f || viewport < 1.0f) { + /* lambda is percentage of elements to keep */ + lambda= (arearatio < 1.0f)? pow(arearatio, powrate): 1.0f; + lambda *= viewport; + + /* compute transition region */ + t= part->simplify_transition; + elem->t= (lambda-t < 0.0f)? lambda: (lambda+t > 1.0f)? 1.0f-lambda: t; + + /* scale at end and beginning of the transition region */ + elem->scalemax= (lambda+t < 1.0f)? 1.0f/lambda: 1.0f/(1.0f - elem->t*elem->t/t); + elem->scalemin= (lambda+t < 1.0f)? 0.0f: elem->scalemax*(1.0f-elem->t/t); + + /* extend lambda to include transition */ + lambda= lambda + elem->t; + if(lambda > 1.0f) + lambda= 1.0f; + } + else { + lambda= arearatio; + + elem->scalemax= 1.0f; //sqrt(lambda); + elem->scalemin= 1.0f; //sqrt(lambda); + } + + elem->lambda= lambda; + elem->scalemin= sqrt(elem->scalemin); + elem->scalemax= sqrt(elem->scalemax); + elem->curchild= 0; + } + + MEM_freeN(facearea); + MEM_freeN(facecenter); + MEM_freeN(facetotvert); + + /* move indices and set random number skipping */ + ctx->skip= MEM_callocN(sizeof(int)*tot, "SimplificationSkip"); + + skipped= 0; + for(a=0, newtot=0; aindex[a]]; + if(b != -1) { + if(elems[b].curchild++ < ceil(elems[b].lambda*elems[b].totchild)) { + ctx->index[newtot]= ctx->index[a]; + ctx->skip[newtot]= skipped; + skipped= 0; + newtot++; + } + else skipped++; + } + else skipped++; + } + + for(a=0, elem=elems; acurchild= 0; + + return newtot; +} + +int psys_render_simplify_params(ParticleSystem *psys, ChildParticle *cpa, float *params) +{ + ParticleRenderData *data; + ParticleRenderElem *elem; + float x, w, scale, alpha, lambda, t, scalemin, scalemax; + int b; + + if(!(psys->renderdata && (psys->part->simplify_flag & PART_SIMPLIFY_ENABLE))) + return 0; + + data= psys->renderdata; + if(!data->dosimplify) + return 0; + + b= data->origindex[cpa->num]; + if(b == -1) + return 0; + + elem= &data->elems[b]; + + lambda= elem->lambda; + t= elem->t; + scalemin= elem->scalemin; + scalemax= elem->scalemax; + + if(lambda >= 1.0f) { + scale= scalemin; + alpha= 1.0f; + } + else { + x= (elem->curchild+0.5f)/elem->totchild; + if(x < lambda-t) { + scale= scalemax; + alpha= 1.0f; + } + else if(x >= lambda+t) { + scale= scalemin; + alpha= 0.0f; + } + else { + w= (lambda+t - x)/(2.0f*t); + scale= scalemin + (scalemax - scalemin)*w; + alpha= w; + } + } + + params[0]= scale; + params[1]= alpha; + + elem->curchild++; + + return 1; +} + /************************************************/ /* Interpolated Particles */ /************************************************/ @@ -2550,6 +2833,11 @@ static void default_particle_settings(ParticleSettings *part) } part->ipo = NULL; + + part->simplify_refsize= 1920; + part->simplify_rate= 1.0f; + part->simplify_transition= 0.1f; + part->simplify_viewport= 0.8; } diff --git a/source/blender/blenkernel/intern/particle_system.c b/source/blender/blenkernel/intern/particle_system.c index af1db36b648..a8ccd12874e 100644 --- a/source/blender/blenkernel/intern/particle_system.c +++ b/source/blender/blenkernel/intern/particle_system.c @@ -105,13 +105,13 @@ static int get_current_display_percentage(ParticleSystem *psys) return psys->part->disp; } -static void alloc_particles(ParticleSystem *psys, int new_totpart) +static void alloc_particles(Object *ob, ParticleSystem *psys, int new_totpart) { ParticleData *newpars = 0, *pa; - int i, child_nbr, totpart, totsaved = 0; + int i, totpart, totsaved = 0; - if(new_totpart<0){ - if(psys->part->distr==PART_DISTR_GRID){ + if(new_totpart<0) { + if(psys->part->distr==PART_DISTR_GRID) { totpart= psys->part->grid_res; totpart*=totpart*totpart; } @@ -123,7 +123,7 @@ static void alloc_particles(ParticleSystem *psys, int new_totpart) if(totpart) newpars= MEM_callocN(totpart*sizeof(ParticleData), "particles"); - if(psys->particles){ + if(psys->particles) { totsaved=MIN2(psys->totpart,totpart); /*save old pars*/ if(totsaved) @@ -136,16 +136,7 @@ static void alloc_particles(ParticleSystem *psys, int new_totpart) } psys->particles=newpars; - child_nbr= (psys->renderdata)? psys->part->ren_child_nbr: psys->part->child_nbr; - if(child_nbr && psys->part->childtype){ - if(psys->child) - MEM_freeN(psys->child); - psys->child = NULL; - if(totpart) - psys->child= MEM_callocN(totpart*child_nbr*sizeof(ChildParticle), "child_particles"); - psys->totchild=totpart*child_nbr; - } - else if(psys->child){ + if(psys->child) { MEM_freeN(psys->child); psys->child=0; psys->totchild=0; @@ -154,6 +145,32 @@ static void alloc_particles(ParticleSystem *psys, int new_totpart) psys->totpart=totpart; } +static int get_alloc_child_particles_tot(ParticleSystem *psys) +{ + int child_nbr; + + if(!psys->part->childtype) + return 0; + + child_nbr= (psys->renderdata)? psys->part->ren_child_nbr: psys->part->child_nbr; + return psys->totpart*child_nbr; +} + +static void alloc_child_particles(ParticleSystem *psys, int tot) +{ + if(psys->child){ + MEM_freeN(psys->child); + psys->child=0; + psys->totchild=0; + } + + if(psys->part->childtype) { + psys->totchild= tot; + if(psys->totchild) + psys->child= MEM_callocN(psys->totchild*sizeof(ChildParticle), "child_particles"); + } +} + /* only run this if from == PART_FROM_FACE */ void psys_calc_dmfaces(Object *ob, DerivedMesh *dm, ParticleSystem *psys) { @@ -607,7 +624,7 @@ void psys_thread_distribute_particle(ParticleThread *thread, ParticleData *pa, C } mf= dm->getFaceData(dm, ctx->index[p], CD_MFACE); - + //switch(distr){ // case PART_DISTR_JIT: // i=index[p]; @@ -741,12 +758,16 @@ void *exec_distribution(void *data) if(thread->ctx->from == PART_FROM_CHILD) { totpart= psys->totchild; - cpa= psys->child + thread->num; + cpa= psys->child; - rng_skip(thread->rng, 5*thread->num); - for(p=thread->num; ptot, cpa+=thread->tot) { - psys_thread_distribute_particle(thread, NULL, cpa, p); - rng_skip(thread->rng, 5*(thread->tot-1)); + for(p=0; pctx->skip) /* simplification skip */ + rng_skip(thread->rng, 5*thread->ctx->skip[p]); + + if((p+thread->num) % thread->tot == 0) + psys_thread_distribute_particle(thread, NULL, cpa, p); + else /* thread skip */ + rng_skip(thread->rng, 5); } } else { @@ -757,7 +778,7 @@ void *exec_distribution(void *data) } return 0; -} +} /* creates a distribution of coordinates on a DerivedMesh */ /* */ @@ -813,7 +834,6 @@ int psys_threads_init_distribution(ParticleThread *threads, DerivedMesh *finaldm if(from==PART_FROM_CHILD){ distr=PART_DISTR_RAND; - cpa=psys->child; if(part->from!=PART_FROM_PARTICLE && part->childtype==PART_CHILD_FACES){ dm= finaldm; children=1; @@ -828,7 +848,7 @@ int psys_threads_init_distribution(ParticleThread *threads, DerivedMesh *finaldm BLI_kdtree_balance(tree); - totpart=psys->totchild; + totpart=get_alloc_child_particles_tot(psys); cfrom=from=PART_FROM_FACE; if(part->flag&PART_CHILD_SEAMS){ @@ -879,6 +899,8 @@ int psys_threads_init_distribution(ParticleThread *threads, DerivedMesh *finaldm /* no need to figure out distribution */ int child_nbr= (psys->renderdata)? part->ren_child_nbr: part->child_nbr; + alloc_child_particles(psys, 1.0f); + cpa=psys->child; for(i=0; itotpart; p++,cpa++){ float length=2.0; @@ -1100,7 +1122,7 @@ int psys_threads_init_distribution(ParticleThread *threads, DerivedMesh *finaldm sum[0]= 0.0f; for(i=0;iflag&PART_TRAND){ float pos; @@ -1156,9 +1178,6 @@ int psys_threads_init_distribution(ParticleThread *threads, DerivedMesh *finaldm } /* 5. */ - if(children) - from=PART_FROM_CHILD; - ctx->tree= tree; ctx->seams= seams; ctx->totseam= totseam; @@ -1169,17 +1188,21 @@ int psys_threads_init_distribution(ParticleThread *threads, DerivedMesh *finaldm ctx->jitoff= jitoff; ctx->weight= weight; ctx->maxweight= maxweight; - ctx->from= from; + ctx->from= (children)? PART_FROM_CHILD: from; ctx->cfrom= cfrom; ctx->distr= distr; ctx->dm= dm; ctx->tpars= tpars; - seed= 31415926 + ctx->psys->seed; + if(children) { + totpart= psys_render_simplify_distribution(ctx, totpart); + alloc_child_particles(psys, totpart); + } - if(from!=PART_FROM_CHILD || psys->totchild < 10000) + if(!children || psys->totchild < 10000) totthread= 1; + seed= 31415926 + ctx->psys->seed; for(i=0; ijitoff) MEM_freeN(ctx->jitoff); if(ctx->weight) MEM_freeN(ctx->weight); if(ctx->index) MEM_freeN(ctx->index); + if(ctx->skip) MEM_freeN(ctx->skip); if(ctx->seams) MEM_freeN(ctx->seams); //if(ctx->vertpart) MEM_freeN(ctx->vertpart); BLI_kdtree_free(ctx->tree); @@ -4188,13 +4212,16 @@ static void psys_update_path_cache(Object *ob, ParticleSystemModifierData *psmd, if(distr){ if(alloc) - alloc_particles(psys,psys->totpart); + alloc_particles(ob,psys,psys->totpart); - if(psys->totchild && part->childtype){ - distribute_particles(ob,psys,PART_FROM_CHILD); + if(get_alloc_child_particles_tot(psys)) { + /* don't generate children while computing the hair keys */ + if(!(psys->part->type == PART_HAIR) || (psys->flag & PSYS_HAIR_DONE)) { + distribute_particles(ob,psys,PART_FROM_CHILD); - if(part->from!=PART_FROM_PARTICLE && part->childtype==PART_CHILD_FACES && part->parents!=0.0) - psys_find_parents(ob,psmd,psys); + if(part->from!=PART_FROM_PARTICLE && part->childtype==PART_CHILD_FACES && part->parents!=0.0) + psys_find_parents(ob,psmd,psys); + } } } @@ -4413,11 +4440,11 @@ static void system_step(Object *ob, ParticleSystem *psys, ParticleSystemModifier if(init) { if(distr) { if(alloc) - alloc_particles(psys, totpart); + alloc_particles(ob, psys, totpart); distribute_particles(ob, psys, part->from); - if(psys->totchild && part->childtype) + if(get_alloc_child_particles_tot(psys)) distribute_particles(ob, psys, PART_FROM_CHILD); } initialize_all_particles(ob, psys, psmd); diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c index cf71d9ab41e..8bbcc7016c1 100644 --- a/source/blender/blenloader/intern/readfile.c +++ b/source/blender/blenloader/intern/readfile.c @@ -6826,7 +6826,7 @@ static void do_versions(FileData *fd, Library *lib, Main *main) } for(ma=main->mat.first; ma; ma= ma->id.next) { - if (ma->samp_gloss_mir == 0) { + if(ma->samp_gloss_mir == 0) { ma->gloss_mir = ma->gloss_tra= 1.0; ma->aniso_gloss_mir = 1.0; ma->samp_gloss_mir = ma->samp_gloss_tra= 18; @@ -6834,11 +6834,23 @@ static void do_versions(FileData *fd, Library *lib, Main *main) ma->dist_mir = 0.0; ma->fadeto_mir = MA_RAYMIR_FADETOSKY; } + + if(ma->strand_min == 0.0f) + ma->strand_min= 1.0f; } - for(part=main->particle.first; part; part=part->id.next) + for(part=main->particle.first; part; part=part->id.next) { if(part->ren_child_nbr==0) part->ren_child_nbr= part->child_nbr; + + if(part->simplify_refsize==0) { + part->simplify_refsize= 1920; + part->simplify_rate= 1.0f; + part->simplify_transition= 0.1f; + part->simplify_viewport= 0.8f; + } + } + if (main->versionfile < 245 || main->subversionfile < 12) { /* initialize skeleton generation toolsettings */ diff --git a/source/blender/makesdna/DNA_material_types.h b/source/blender/makesdna/DNA_material_types.h index 22585e59c7b..f0b06585f27 100644 --- a/source/blender/makesdna/DNA_material_types.h +++ b/source/blender/makesdna/DNA_material_types.h @@ -85,6 +85,7 @@ typedef struct Material { short flarec, starc, linec, ringc; float hasize, flaresize, subsize, flareboost; float strand_sta, strand_end, strand_ease, strand_surfnor; + float strand_min, strand_pad; char strand_uvname[32]; float sbias; /* shadow bias */ diff --git a/source/blender/makesdna/DNA_particle_types.h b/source/blender/makesdna/DNA_particle_types.h index a00d7d484d9..942c837df97 100644 --- a/source/blender/makesdna/DNA_particle_types.h +++ b/source/blender/makesdna/DNA_particle_types.h @@ -121,6 +121,11 @@ typedef struct ParticleSettings { short bb_align, bb_uv_split, bb_anim, bb_split_offset; float bb_tilt, bb_rand_tilt, bb_offset[2]; + /* simplification */ + short simplify_flag, simplify_refsize; + float simplify_rate, simplify_transition; + float simplify_viewport; + /* general values */ float sta, end, lifetime, randlife; float timetweak, jitfac, keyed_time; @@ -307,6 +312,10 @@ typedef struct ParticleSystem{ #define PART_DRAW_WHOLE_GR (1<<14) #define PART_DRAW_REN_STRAND (1<<15) +/* part->simplify_flag */ +#define PART_SIMPLIFY_ENABLE 1 +#define PART_SIMPLIFY_VIEWPORT 2 + /* part->bb_align */ #define PART_BB_X 0 #define PART_BB_Y 1 diff --git a/source/blender/render/intern/include/render_types.h b/source/blender/render/intern/include/render_types.h index 4a731878ffb..7fbbd0f5abc 100644 --- a/source/blender/render/intern/include/render_types.h +++ b/source/blender/render/intern/include/render_types.h @@ -343,7 +343,7 @@ typedef struct StrandBuffer { unsigned int lay; int overrideuv; int flag, maxdepth; - float adaptcos; + float adaptcos, minwidth; float winmat[4][4]; int winx, winy; diff --git a/source/blender/render/intern/include/renderdatabase.h b/source/blender/render/intern/include/renderdatabase.h index 0346f2d6413..c919a54008e 100644 --- a/source/blender/render/intern/include/renderdatabase.h +++ b/source/blender/render/intern/include/renderdatabase.h @@ -69,6 +69,7 @@ typedef struct StrandTableNode { struct StrandRen *strand; float *winspeed; float *surfnor; + float *simplify; struct MCol *mcol; float *uv; int totuv, totmcol; @@ -112,6 +113,7 @@ int RE_vlakren_get_normal(struct Render *re, struct ObjectInstanceRen *obi, stru float *RE_strandren_get_surfnor(struct ObjectRen *obr, struct StrandRen *strand, int verify); float *RE_strandren_get_uv(struct ObjectRen *obr, struct StrandRen *strand, int n, char **name, int verify); struct MCol *RE_strandren_get_mcol(struct ObjectRen *obr, struct StrandRen *strand, int n, char **name, int verify); +float *RE_strandren_get_simplify(struct ObjectRen *obr, struct StrandRen *strand, int verify); float *RE_strandren_get_winspeed(struct ObjectInstanceRen *obi, struct StrandRen *strand, int verify); struct VertRen *RE_vertren_copy(struct ObjectRen *obr, struct VertRen *ver); diff --git a/source/blender/render/intern/include/strand.h b/source/blender/render/intern/include/strand.h index 7f37317d4d5..34a147c1933 100644 --- a/source/blender/render/intern/include/strand.h +++ b/source/blender/render/intern/include/strand.h @@ -68,6 +68,9 @@ typedef struct StrandPoint { /* screen space */ float hoco[4]; float x, y; + + /* simplification */ + float alpha; } StrandPoint; typedef struct StrandSegment { diff --git a/source/blender/render/intern/source/convertblender.c b/source/blender/render/intern/source/convertblender.c index de3bd6079a4..eb5bb3f2f71 100644 --- a/source/blender/render/intern/source/convertblender.c +++ b/source/blender/render/intern/source/convertblender.c @@ -983,7 +983,7 @@ static void static_particle_strand(Render *re, ObjectRen *obr, Material *ma, flo { static VertRen *v1= NULL, *v2= NULL; VlakRen *vlr; - float nor[3], cross[3], w, dx, dy, width; + float nor[3], cross[3], crosslen, w, dx, dy, width; static float anor[3], avec[3]; int flag, i; static int second=0; @@ -992,14 +992,11 @@ static void static_particle_strand(Render *re, ObjectRen *obr, Material *ma, flo Normalize(nor); // nor needed as tangent Crossf(cross, vec, nor); - if(ma->mode&MA_STR_B_UNITS) - Normalize(cross); - /* turn cross in pixelsize */ w= vec[2]*re->winmat[2][3] + re->winmat[3][3]; - dx= re->winx*cross[0]*re->winmat[0][0]/w; - dy= re->winy*cross[1]*re->winmat[1][1]/w; - w= sqrt(dx*dx + dy*dy); + dx= re->winx*cross[0]*re->winmat[0][0]; + dy= re->winy*cross[1]*re->winmat[1][1]; + w= sqrt(dx*dx + dy*dy)/w; if(w!=0.0f) { float fac; @@ -1013,12 +1010,16 @@ static void static_particle_strand(Render *re, ObjectRen *obr, Material *ma, flo width= ((1.0f-fac)*ma->strand_sta + (fac)*ma->strand_end); - /* use actual Blender units for strand width and fall back to min 1px */ + /* use actual Blender units for strand width and fall back to minimum width */ if(ma->mode & MA_STR_B_UNITS){ - if(width < 1.0f/w) - width= 1.0f/w; + crosslen= VecLength(cross); + w= 2.0f*crosslen*ma->strand_min/w; + + if(width < w) + width= w; + /*cross is the radius of the strand so we want it to be half of full width */ - VecMulf(cross,0.5); + VecMulf(cross,0.5/crosslen); } else width/=w; @@ -1496,7 +1497,8 @@ static int render_new_particle_system(Render *re, ObjectRen *obr, ParticleSystem float *orco=0,*surfnor=0,*uvco=0, strandlen=0.0f, curlen=0.0f; float hasize, pa_size, pa_time, r_tilt, cfra=bsystem_time(ob,(float)CFRA,0.0); float loc_tex[3], size_tex[3], adapt_angle=0.0, adapt_pix=0.0, random; - int i, a, k, max_k=0, totpart, totuv=0, override_uv=-1; + float simplify[2]; + int i, a, k, max_k=0, totpart, totuv=0, override_uv=-1, dosimplify = 0; int path_possible=0, keys_possible=0, baked_keys=0, totchild=psys->totchild; int seed, path_nbr=0, path=0, orco1=0, adapt=0, uv[3]={0,0,0}; char **uv_name=0; @@ -1639,9 +1641,10 @@ static int render_new_particle_system(Render *re, ObjectRen *obr, ParticleSystem Mat4CpyMat4(strandbuf->winmat, re->winmat); strandbuf->winx= re->winx; strandbuf->winy= re->winy; - strandbuf->maxdepth= 2; /* TODO */ + strandbuf->maxdepth= 2; strandbuf->adaptcos= cos((float)part->adapt_angle*(float)(M_PI/180.0)); strandbuf->overrideuv= override_uv; + strandbuf->minwidth= ma->strand_min; if(part->flag & PART_HAIR_BSPLINE) strandbuf->flag |= R_STRAND_BSPLINE; @@ -1720,7 +1723,7 @@ static int render_new_particle_system(Render *re, ObjectRen *obr, ParticleSystem if(totchild && (part->draw&PART_DRAW_PARENT)==0) continue; } - else{ + else { ChildParticle *cpa= psys->child+a-totpart; pa_time=psys_get_child_time(psys, cpa, cfra); @@ -1778,6 +1781,8 @@ static int render_new_particle_system(Render *re, ObjectRen *obr, ParticleSystem } } + dosimplify= psys_render_simplify_params(psys, cpa, simplify); + if(path_nbr) { cache = psys->childcache[a-totpart]; max_k = (int)cache->steps; @@ -1805,6 +1810,12 @@ static int render_new_particle_system(Render *re, ObjectRen *obr, ParticleSystem strand->vert= svert; VECCOPY(strand->orco, orco); + if(dosimplify) { + float *ssimplify= RE_strandren_get_simplify(obr, strand, 1); + ssimplify[0]= simplify[0]; + ssimplify[1]= simplify[1]; + } + if(surfnor) { float *snor= RE_strandren_get_surfnor(obr, strand, 1); VECCOPY(snor, surfnor); @@ -3886,7 +3897,7 @@ static void add_render_object(Render *re, Object *ob, Object *par, int index, in show_emitter= 0; for(psys=ob->particlesystem.first; psys; psys=psys->next) { show_emitter += psys->part->draw & PART_DRAW_EMITTER; - psys_particles_to_render_backup(ob, psys); + psys_render_set(ob, psys, re->viewmat, re->winmat, re->winx, re->winy); } /* if no psys has "show emitter" selected don't render emitter */ @@ -3910,7 +3921,7 @@ static void add_render_object(Render *re, Object *ob, Object *par, int index, in for(psys=ob->particlesystem.first; psys; psys=psys->next, psysindex++) { obr= RE_addRenderObject(re, ob, par, index, psysindex); init_render_object_data(re, obr, only_verts); - psys_render_backup_to_particles(ob, psys); + psys_render_restore(ob, psys); /* only add instance for objects that have not been used for dupli */ if(!(ob->transflag & OB_RENDER_DUPLI)) @@ -4666,7 +4677,7 @@ void RE_Database_FromScene_Vectors(Render *re, Scene *sce) } else { /* check if both have same amounts of vertices */ if(obi->totvector!=oldobi->totvector) { - printf("Warning: object %s has different amount of vertices on other frame\n", obi->ob->id.name+2); + printf("Warning: object %s has different amount of vertices or strands on other frame\n", obi->ob->id.name+2); continue; } diff --git a/source/blender/render/intern/source/renderdatabase.c b/source/blender/render/intern/source/renderdatabase.c index 3906b1fc001..79c87252fc2 100644 --- a/source/blender/render/intern/source/renderdatabase.c +++ b/source/blender/render/intern/source/renderdatabase.c @@ -104,6 +104,7 @@ #define RE_MCOL_ELEMS 4 #define RE_UV_ELEMS 2 #define RE_SURFNOR_ELEMS 3 +#define RE_SIMPLIFY_ELEMS 2 float *RE_vertren_get_sticky(ObjectRen *obr, VertRen *ver, int verify) { @@ -590,6 +591,21 @@ MCol *RE_strandren_get_mcol(ObjectRen *obr, StrandRen *strand, int n, char **nam return node->mcol + index*RE_MCOL_ELEMS; } +float *RE_strandren_get_simplify(struct ObjectRen *obr, struct StrandRen *strand, int verify) +{ + float *simplify; + int nr= strand->index>>8; + + simplify= obr->strandnodes[nr].simplify; + if(simplify==NULL) { + if(verify) + simplify= obr->strandnodes[nr].simplify= MEM_callocN(256*RE_SIMPLIFY_ELEMS*sizeof(float), "simplify table"); + else + return NULL; + } + return simplify + (strand->index & 255)*RE_SIMPLIFY_ELEMS; +} + /* winspeed is exception, it is stored per instance */ float *RE_strandren_get_winspeed(ObjectInstanceRen *obi, StrandRen *strand, int verify) { @@ -743,6 +759,8 @@ void free_renderdata_strandnodes(StrandTableNode *strandnodes) MEM_freeN(strandnodes[a].winspeed); if(strandnodes[a].surfnor) MEM_freeN(strandnodes[a].surfnor); + if(strandnodes[a].simplify) + MEM_freeN(strandnodes[a].simplify); } MEM_freeN(strandnodes); diff --git a/source/blender/render/intern/source/strand.c b/source/blender/render/intern/source/strand.c index 42c6d559f65..4f0e9764a43 100644 --- a/source/blender/render/intern/source/strand.c +++ b/source/blender/render/intern/source/strand.c @@ -321,7 +321,8 @@ void strand_eval_point(StrandSegment *sseg, StrandPoint *spoint) { Material *ma; StrandBuffer *strandbuf; - float p[4][3], data[4], cross[3], w, dx, dy, t; + float *simplify; + float p[4][3], data[4], cross[3], crosslen, w, dx, dy, t; int type; strandbuf= sseg->buffer; @@ -378,25 +379,34 @@ void strand_eval_point(StrandSegment *sseg, StrandPoint *spoint) Normalize(spoint->nor); spoint->width= strand_eval_width(ma, spoint->strandco); + + /* simplification */ + simplify= RE_strandren_get_simplify(strandbuf->obr, sseg->strand, 0); + spoint->alpha= (simplify)? simplify[1]: 1.0f; /* outer points */ Crossf(cross, spoint->co, spoint->tan); - if(strandbuf->flag & R_STRAND_B_UNITS) - Normalize(cross); - w= spoint->co[2]*strandbuf->winmat[2][3] + strandbuf->winmat[3][3]; - dx= strandbuf->winx*cross[0]*strandbuf->winmat[0][0]/w; - dy= strandbuf->winy*cross[1]*strandbuf->winmat[1][1]/w; - w= sqrt(dx*dx + dy*dy); + dx= strandbuf->winx*cross[0]*strandbuf->winmat[0][0]; + dy= strandbuf->winy*cross[1]*strandbuf->winmat[1][1]; + w= sqrt(dx*dx + dy*dy)/w; if(w > 0.0f) { if(strandbuf->flag & R_STRAND_B_UNITS) { - w= 1.0f/w; + crosslen= VecLength(cross); + w= 2.0f*crosslen*strandbuf->minwidth/w; - if(spoint->width < w) + if(spoint->width < w) { + spoint->alpha= spoint->width/w; spoint->width= w; - VecMulf(cross, spoint->width*0.5f); + } + + if(simplify) + /* squared because we only change width, not length */ + spoint->width *= simplify[0]*simplify[0]; + + VecMulf(cross, spoint->width*0.5f/crosslen); } else VecMulf(cross, spoint->width/w); @@ -528,7 +538,7 @@ static void strand_project_point(float winmat[][4], float winx, float winy, Stra } #include "BLI_rand.h" -void strand_shade_point(Render *re, ShadeSample *ssamp, StrandSegment *sseg, StrandPoint *spoint); +static void strand_shade_point(Render *re, ShadeSample *ssamp, StrandSegment *sseg, StrandPoint *spoint); static void strand_shade_get(StrandPart *spart, int lookup, ShadeSample *ssamp, StrandPoint *spoint, StrandVert *svert, StrandSegment *sseg) { @@ -653,7 +663,7 @@ static int strand_test_clip(float winmat[][4], ZSpan *zspan, float *bounds, floa return clipflag; } -void strand_shade_point(Render *re, ShadeSample *ssamp, StrandSegment *sseg, StrandPoint *spoint) +static void strand_shade_point(Render *re, ShadeSample *ssamp, StrandSegment *sseg, StrandPoint *spoint) { ShadeInput *shi= ssamp->shi; ShadeResult *shr= ssamp->shr; @@ -685,6 +695,21 @@ void strand_shade_point(Render *re, ShadeSample *ssamp, StrandSegment *sseg, Str shade_samples_do_AO(ssamp); shade_input_do_shade(shi, shr); + /* apply simplification */ + if(spoint->alpha < 1.0f) { + shr->combined[0] *= spoint->alpha; + shr->combined[1] *= spoint->alpha; + shr->combined[2] *= spoint->alpha; + shr->combined[3] *= spoint->alpha; + + shr->col[0] *= spoint->alpha; + shr->col[1] *= spoint->alpha; + shr->col[2] *= spoint->alpha; + shr->col[3] *= spoint->alpha; + + shr->alpha *= spoint->alpha; + } + /* include lamphalos for strand, since halo layer was added already */ if(re->flag & R_LAMPHALO) if(shi->layflag & SCE_LAY_HALO) diff --git a/source/blender/src/buttons_object.c b/source/blender/src/buttons_object.c index 3c2969e77bf..e5bb4dd8a10 100644 --- a/source/blender/src/buttons_object.c +++ b/source/blender/src/buttons_object.c @@ -3610,8 +3610,8 @@ static void object_panel_particle_children(Object *ob) if(part==NULL) return; block= uiNewBlock(&curarea->uiblocks, "object_panel_particle_child", UI_EMBOSS, UI_HELV, curarea->win); - uiNewPanelTabbed("Extras", "Particle"); if(uiNewPanel(curarea, block, "Children", "Particle", 1300, 0, 318, 204)==0) return; + uiNewPanelTabbed("Extras", "Particle"); uiDefButS(block, MENU, B_PART_ALLOC_CHILD, "Children from:%t|Faces%x2|Particles%x1|None%x0", butx,buty,butw,buth, &part->childtype, 14.0, 0.0, 0, 0, "Create child particles"); @@ -3957,6 +3957,47 @@ static void object_panel_particle_visual(Object *ob) } uiBlockEndAlign(block); } +static void object_panel_particle_simplification(Object *ob) +{ + uiBlock *block; + ParticleSystem *psys=psys_get_current(ob); + ParticleSettings *part; + short butx=0, buty=160, butw=150, buth=20; + + if (psys==NULL) return; + part=psys->part; + if(part==NULL) return; + + if(part->draw_as!=PART_DRAW_PATH || !(part->draw & PART_DRAW_REN_STRAND)) + return; + if(part->childtype!=PART_CHILD_FACES) + return; + + block= uiNewBlock(&curarea->uiblocks, "object_panel_particle_simplification", UI_EMBOSS, UI_HELV, curarea->win); + uiNewPanelTabbed("Visualization", "Particle"); + if(uiNewPanel(curarea, block, "Simplification", "Particle", 640, 0, 318, 204)==0) return; + + uiBlockBeginAlign(block); + uiDefButBitS(block, TOG, PART_SIMPLIFY_ENABLE, B_PART_REDRAW, "Child Simplification", butx,buty-=buth,butw,buth, &part->simplify_flag, 0, 0, 0, 0, "Remove child strands as the object becomes smaller on the screen"); + uiBlockEndAlign(block); + if(part->simplify_flag & PART_SIMPLIFY_ENABLE) { + buty -= 10; + + uiBlockBeginAlign(block); + uiDefButS(block, NUM, B_NOP, "Reference Size:", butx,(buty-=buth),butw,buth, &part->simplify_refsize, 1.0, 32768.0, 0, 0, "Reference size size in pixels, after which simplification begins"); + uiDefButF(block, NUM, B_NOP, "Rate:", butx,(buty-=buth),butw,buth, &part->simplify_rate, 0.0, 1.0, 0, 0, "Speed of simplification"); + uiDefButF(block, NUM, B_NOP, "Transition:", butx,(buty-=buth),butw,buth, &part->simplify_transition, 0.0, 1.0, 0, 0, "Transition period for fading out strands"); + uiBlockEndAlign(block); + + buty -= 10; + + uiBlockBeginAlign(block); + uiDefButBitS(block, TOG, PART_SIMPLIFY_VIEWPORT, B_PART_REDRAW, "Viewport", butx,buty-=buth,butw,buth, &part->simplify_flag, 0, 0, 0, 0, "Remove child strands as the object goes outside the viewport"); + uiDefButF(block, NUM, B_NOP, "Rate:", butx,(buty-=buth),butw,buth, &part->simplify_viewport, 0.0, 0.999, 0, 0, "Speed of simplification"); + uiBlockEndAlign(block); + } + uiBlockEndAlign(block); +} static void boidrule_moveDown(void *part_v, void *rule_v) { ParticleSettings *part = part_v; @@ -4700,6 +4741,7 @@ void particle_panels() if(psys){ object_panel_particle_physics(ob); object_panel_particle_visual(ob); + object_panel_particle_simplification(ob); object_panel_particle_extra(ob); object_panel_particle_children(ob); } diff --git a/source/blender/src/buttons_shading.c b/source/blender/src/buttons_shading.c index dafd156524f..ad1afcc0608 100644 --- a/source/blender/src/buttons_shading.c +++ b/source/blender/src/buttons_shading.c @@ -3852,31 +3852,37 @@ static uiBlock *strand_menu(void *mat_v) { Material *ma= mat_v; uiBlock *block; + int buth=20, butw=230, butx=10, buty=160; block= uiNewBlock(&curarea->uiblocks, "strand menu", UI_EMBOSS, UI_HELV, curarea->win); - + + if(ma->mode & MA_STR_B_UNITS) + buty += buth; + /* use this for a fake extra empy space around the buttons */ - uiDefBut(block, LABEL, 0, "", 0, 0, 250, 170, NULL, 0, 0, 0, 0, ""); + uiDefBut(block, LABEL, 0, "", 0, 0, butw+20, buty+10, NULL, 0, 0, 0, 0, ""); + /* event return 0, to prevent menu to close */ uiBlockBeginAlign(block); - /* event return 0, to prevent menu to close */ - - uiDefButBitI(block, TOG, MA_TANGENT_STR, 0, "Use Tangent Shading", 10,140,230,20, &(ma->mode), 0, 0, 0, 0, "Uses direction of strands as normal for tangent-shading"); - uiDefButBitI(block, TOG, MA_STR_SURFDIFF, 0, "Surface Diffuse", 10,120,115,20, &(ma->mode), 0, 0, 0, 0, "Make diffuse shading more similar to shading the surface"); - uiDefButF(block, NUM, 0, "Dist", 125,120,115,20, &ma->strand_surfnor, 0.0f, 10.0f, 2, 0, "Distance in Blender units over which to blend in the surface normal"); + uiDefButBitI(block, TOG, MA_TANGENT_STR, 0, "Use Tangent Shading", butx,buty-=buth,butw,buth, &(ma->mode), 0, 0, 0, 0, "Uses direction of strands as normal for tangent-shading"); + uiDefButBitI(block, TOG, MA_STR_SURFDIFF, 0, "Surface Diffuse", butx,buty-=buth,butw/2,buth, &(ma->mode), 0, 0, 0, 0, "Make diffuse shading more similar to shading the surface"); + uiDefButF(block, NUM, 0, "Dist", butx+butw/2,buty,butw/2,buth, &ma->strand_surfnor, 0.0f, 10.0f, 2, 0, "Distance in Blender units over which to blend in the surface normal"); + + buty -= 5; uiBlockBeginAlign(block); - uiDefButBitI(block, TOG, MA_STR_B_UNITS, 0, "Use Blender Units", 10,95,230,20, &(ma->mode), 0, 0, 0, 0, "Use actual Blender units for widths instead of pixels"); + uiDefButBitI(block, TOG, MA_STR_B_UNITS, 0, "Use Blender Units", butx,buty-=buth,butw,buth, &(ma->mode), 0, 0, 0, 0, "Use actual Blender units for widths instead of pixels"); if(ma->mode & MA_STR_B_UNITS){ - uiDefButF(block, NUMSLI, 0, "Start ", 10, 75, 230,20, &ma->strand_sta, 0.0001, 2.0, 2, 0, "Start size of strands in Blender units"); - uiDefButF(block, NUMSLI, 0, "End ", 10, 55, 230,20, &ma->strand_end, 0.0001, 1.0, 2, 0, "End size of strands in Blender units"); + uiDefButF(block, NUMSLI, 0, "Start ", butx,buty-=buth, butw,buth, &ma->strand_sta, 0.0001, 2.0, 2, 0, "Start size of strands in Blender units"); + uiDefButF(block, NUMSLI, 0, "End ", butx,buty-=buth, butw,buth, &ma->strand_end, 0.0001, 1.0, 2, 0, "End size of strands in Blender units"); + uiDefButF(block, NUMSLI, 0, "Minimum ", butx,buty-=buth, butw,buth, &ma->strand_min, 0.001, 10.0, 0, 0, "Minimum size of strands in pixels"); } else{ - uiDefButF(block, NUMSLI, 0, "Start ", 10, 75, 230,20, &ma->strand_sta, 0.25, 20.0, 2, 0, "Start size of strands in pixels"); - uiDefButF(block, NUMSLI, 0, "End ", 10, 55, 230,20, &ma->strand_end, 0.25, 10.0, 2, 0, "End size of strands in pixels"); + uiDefButF(block, NUMSLI, 0, "Start ", butx,buty-=buth, butw,buth, &ma->strand_sta, 0.25, 20.0, 2, 0, "Start size of strands in pixels"); + uiDefButF(block, NUMSLI, 0, "End ", butx,buty-=buth, butw,buth, &ma->strand_end, 0.25, 10.0, 2, 0, "End size of strands in pixels"); } - uiDefButF(block, NUMSLI, 0, "Shape ", 10, 35, 230,20, &ma->strand_ease, -0.9, 0.9, 2, 0, "Shape of strands, positive value makes it rounder, negative makes it spiky"); - uiDefBut(block, TEX, B_MATPRV, "UV:", 10,10,230,20, ma->strand_uvname, 0, 31, 0, 0, "Set name of UV layer to override"); + uiDefButF(block, NUMSLI, 0, "Shape ", butx,buty-=buth, butw,buth, &ma->strand_ease, -0.9, 0.9, 2, 0, "Shape of strands, positive value makes it rounder, negative makes it spiky"); + uiDefBut(block, TEX, B_MATPRV, "UV:", butx,buty-=buth,butw,buth, ma->strand_uvname, 0, 31, 0, 0, "Set name of UV layer to override"); uiBlockSetDirection(block, UI_TOP); BIF_preview_changed(ID_MA); -- cgit v1.2.3