Strand Render Simplification
authorBrecht Van Lommel <brechtvanlommel@pandora.be>
Thu, 20 Dec 2007 16:35:27 +0000 (16:35 +0000)
committerBrecht Van Lommel <brechtvanlommel@pandora.be>
Thu, 20 Dec 2007 16:35:27 +0000 (16:35 +0000)
============================

- 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.

14 files changed:
source/blender/blenkernel/BKE_particle.h
source/blender/blenkernel/intern/particle.c
source/blender/blenkernel/intern/particle_system.c
source/blender/blenloader/intern/readfile.c
source/blender/makesdna/DNA_material_types.h
source/blender/makesdna/DNA_particle_types.h
source/blender/render/intern/include/render_types.h
source/blender/render/intern/include/renderdatabase.h
source/blender/render/intern/include/strand.h
source/blender/render/intern/source/convertblender.c
source/blender/render/intern/source/renderdatabase.c
source/blender/render/intern/source/strand.c
source/blender/src/buttons_object.c
source/blender/src/buttons_shading.c

index 1c69fe613c77676155cbc2306d64b94ae7065c6e..a1ac97406e161659ea54d6976375e9c8f56fca38 100644 (file)
@@ -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);
index 85ac809bf700a77b2448a04971e6dfd3b1de8230..32f1795747a858b785dd585eac2fd03c1d88d16b 100644 (file)
@@ -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; a<tot; a++) {
+               b= origindex[ctx->index[a]];
+               if(b != -1)
+                       elems[b].totchild++;
+       }
+
+       /* compute areas and centers of original faces */
+       for(mf=mface, a=0; a<totface; a++, mf++) {
+               b= origindex[a];
+
+               if(b != -1) {
+                       VECCOPY(co1, mvert[mf->v1].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<totorigface; a++)
+               if(facetotvert[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; a<totorigface; a++, elem++) {
+               area = psys_render_projected_area(ctx->psys, 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; a<tot; a++) {
+               b= origindex[ctx->index[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; a<totorigface; a++, elem++)
+               elem->curchild= 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;
 }
 
 
index af1db36b648a20b75ce60c46e8bc8e84b3e41a67..a8ccd12874eceff51650cd3bc64d60b05d2251ae 100644 (file)
@@ -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; p<totpart; p+=thread->tot, cpa+=thread->tot) {
-                       psys_thread_distribute_particle(thread, NULL, cpa, p);
-                       rng_skip(thread->rng, 5*(thread->tot-1));
+               for(p=0; p<totpart; p++, cpa++) {
+                       if(thread->ctx->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; i<child_nbr; i++){
                                for(p=0; p<psys->totpart; 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;i<tot; i++)
                sum[i+1]= sum[i]+weight[i]*totweight;
-
+       
        if(part->flag&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; i<totthread; i++) {
                threads[i].rng= rng_new(seed);
                threads[i].tot= totthread;
@@ -1323,6 +1346,7 @@ void psys_threads_free(ParticleThread *threads)
        if(ctx->jitoff) 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);
index cf71d9ab41e930d80f421d03ddbfd94011909384..8bbcc7016c14b2a072133deb870f7372909ac633 100644 (file)
@@ -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 */
index 22585e59c7bd3d5a05bcd2e1049f47d8baa94153..f0b06585f27b12a5416f1d7c08a2b2187aec48e4 100644 (file)
@@ -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 */
index a00d7d484d934f6b50351dc26ad45b1a7707fa5b..942c837df97ba2786a9b0a1816cf78daa09011ac 100644 (file)
@@ -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
index 4a731878ffb051a5b042fbd3381195d601b1b1d6..7fbbd0f5abc8d4990d1f730f5bde0b66508baaac 100644 (file)
@@ -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;
index 0346f2d64138ca194e668975ec30eefedf98fed7..c919a54008e385d86b0eccb81a31f1e9d66dcc10 100644 (file)
@@ -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);
index 7f37317d4d524b7afed0bbb8de92552d773fed27..34a147c193399fd07483c84514e883491b1666c3 100644 (file)
@@ -68,6 +68,9 @@ typedef struct StrandPoint {
        /* screen space */
        float hoco[4];
        float x, y;
+
+       /* simplification */
+       float alpha;
 } StrandPoint;
 
 typedef struct StrandSegment {
index de3bd6079a427c0bfb53b514490dfdb6eddb81e3..eb5bb3f2f713cc41308abbb061447bac0dc318af 100644 (file)
@@ -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;
                                        }
                                        
index 3906b1fc0019a5edcb205b3a5e349c878bd5283f..79c87252fc2354c861863684e8c0f1f74e3c16d8 100644 (file)
 #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);
index 42c6d559f6545f9e85634ad5068cfa927cc6cc56..4f0e9764a431c0e0e88f690aca1ce75f13452bc8 100644 (file)
@@ -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)
index 3c2969e77bfe057c732b2d41af2d862d6eef8917..e5bb4dd8a104fa4e82edcd5b1891492940efb1a2 100644 (file)
@@ -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);
                }
index dafd156524f15584476898c36abb1c822ddb9e23..ad1afcc06087c437202259dd531a1e72ca5392f2 100644 (file)
@@ -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);