Fix for [#26372] Objects as PS Hair displays and renders differently

* Grid distribution isn't really suited for hair, so this is now disabled.
* Setting a jittered distribution with particles/face = 1 now creates particles on the center of faces.
* Quite a bit of cleanup of the whole particle distribution code.
This commit is contained in:
Janne Karhu 2011-03-09 14:18:33 +00:00
parent 3e8c838a99
commit f89fc824aa
7 changed files with 310 additions and 404 deletions

View File

@ -242,7 +242,7 @@ void psys_get_particle_on_path(struct ParticleSimulationData *sim, int pa_num, s
int psys_get_particle_state(struct ParticleSimulationData *sim, int p, struct ParticleKey *state, int always);
/* for anim.c */
void psys_get_dupli_texture(struct Object *ob, struct ParticleSettings *part, struct ParticleSystemModifierData *psmd, struct ParticleData *pa, struct ChildParticle *cpa, float *uv, float *orco);
void psys_get_dupli_texture(struct ParticleSystem *psys, struct ParticleSettings *part, struct ParticleSystemModifierData *psmd, struct ParticleData *pa, struct ChildParticle *cpa, float *uv, float *orco);
void psys_get_dupli_path_transform(struct ParticleSimulationData *sim, struct ParticleData *pa, struct ChildParticle *cpa, struct ParticleCacheKey *cache, float mat[][4], float *scale);
ParticleThread *psys_threads_create(struct ParticleSimulationData *sim);

View File

@ -1346,8 +1346,6 @@ static void new_particle_duplilist(ListBase *lb, ID *id, Scene *scene, Object *p
/* for groups, pick the object based on settings */
if(part->draw&PART_DRAW_RAND_GR)
b= BLI_rand() % totgroup;
else if(part->from==PART_FROM_PARTICLE)
b= pa_num % totgroup;
else
b= a % totgroup;
@ -1402,7 +1400,7 @@ static void new_particle_duplilist(ListBase *lb, ID *id, Scene *scene, Object *p
dob= new_dupli_object(lb, go->ob, mat, par->lay, counter, OB_DUPLIPARTS, animated);
copy_m4_m4(dob->omat, obcopylist[b].obmat);
if(G.rendering)
psys_get_dupli_texture(par, part, sim.psmd, pa, cpa, dob->uv, dob->orco);
psys_get_dupli_texture(psys, part, sim.psmd, pa, cpa, dob->uv, dob->orco);
}
}
else {
@ -1434,7 +1432,7 @@ static void new_particle_duplilist(ListBase *lb, ID *id, Scene *scene, Object *p
dob= new_dupli_object(lb, ob, mat, ob->lay, counter, GS(id->name) == ID_GR ? OB_DUPLIGROUP : OB_DUPLIPARTS, animated);
copy_m4_m4(dob->omat, oldobmat);
if(G.rendering)
psys_get_dupli_texture(par, part, sim.psmd, pa, cpa, dob->uv, dob->orco);
psys_get_dupli_texture(psys, part, sim.psmd, pa, cpa, dob->uv, dob->orco);
}
}

View File

@ -2407,7 +2407,7 @@ static int psys_threads_init_path(ParticleThread *threads, Scene *scene, float c
steps = (int)pow(2.0, (double)pset->draw_step);
}
if(totchild && part->from!=PART_FROM_PARTICLE && part->childtype==PART_CHILD_FACES){
if(totchild && part->childtype==PART_CHILD_FACES){
totparent=(int)(totchild*part->parents*0.3);
if(G.rendering && part->child_nbr && part->ren_child_nbr)
@ -2449,16 +2449,14 @@ static int psys_threads_init_path(ParticleThread *threads, Scene *scene, float c
psys->lattice = psys_get_lattice(&ctx->sim);
/* cache all relevant vertex groups if they exist */
if(part->from!=PART_FROM_PARTICLE){
ctx->vg_length = psys_cache_vgroup(ctx->dm,psys,PSYS_VG_LENGTH);
ctx->vg_clump = psys_cache_vgroup(ctx->dm,psys,PSYS_VG_CLUMP);
ctx->vg_kink = psys_cache_vgroup(ctx->dm,psys,PSYS_VG_KINK);
ctx->vg_rough1 = psys_cache_vgroup(ctx->dm,psys,PSYS_VG_ROUGH1);
ctx->vg_rough2 = psys_cache_vgroup(ctx->dm,psys,PSYS_VG_ROUGH2);
ctx->vg_roughe = psys_cache_vgroup(ctx->dm,psys,PSYS_VG_ROUGHE);
if(psys->part->flag & PART_CHILD_EFFECT)
ctx->vg_effector = psys_cache_vgroup(ctx->dm,psys,PSYS_VG_EFFECTOR);
}
ctx->vg_length = psys_cache_vgroup(ctx->dm,psys,PSYS_VG_LENGTH);
ctx->vg_clump = psys_cache_vgroup(ctx->dm,psys,PSYS_VG_CLUMP);
ctx->vg_kink = psys_cache_vgroup(ctx->dm,psys,PSYS_VG_KINK);
ctx->vg_rough1 = psys_cache_vgroup(ctx->dm,psys,PSYS_VG_ROUGH1);
ctx->vg_rough2 = psys_cache_vgroup(ctx->dm,psys,PSYS_VG_ROUGH2);
ctx->vg_roughe = psys_cache_vgroup(ctx->dm,psys,PSYS_VG_ROUGHE);
if(psys->part->flag & PART_CHILD_EFFECT)
ctx->vg_effector = psys_cache_vgroup(ctx->dm,psys,PSYS_VG_EFFECTOR);
/* set correct ipo timing */
#if 0 // XXX old animation system
@ -2912,8 +2910,8 @@ void psys_cache_paths(ParticleSimulationData *sim, float cfra)
if(ma && (psys->part->draw & PART_DRAW_MAT_COL))
VECCOPY(col, &ma->r)
if(psys->part->from!=PART_FROM_PARTICLE && !(psys->flag & PSYS_GLOBAL_HAIR)) {
if(!(psys->part->flag & PART_CHILD_EFFECT))
if((psys->flag & PSYS_GLOBAL_HAIR)==0) {
if((psys->part->flag & PART_CHILD_EFFECT)==0)
vg_effector = psys_cache_vgroup(psmd->dm, psys, PSYS_VG_EFFECTOR);
if(!psys->totchild)
@ -4042,7 +4040,7 @@ void psys_get_particle_on_path(ParticleSimulationData *sim, int p, ParticleKey *
if(state->time < 0.0f)
t = psys_get_child_time(psys, cpa, -state->time, NULL, NULL);
if(totchild && part->from!=PART_FROM_PARTICLE && part->childtype==PART_CHILD_FACES){
if(totchild && part->childtype==PART_CHILD_FACES){
/* part->parents could still be 0 so we can't test with totparent */
between=1;
}
@ -4192,7 +4190,7 @@ int psys_get_particle_state(ParticleSimulationData *sim, int p, ParticleKey *sta
if(!psys->totchild)
return 0;
if(part->from != PART_FROM_PARTICLE && part->childtype == PART_CHILD_FACES){
if(part->childtype == PART_CHILD_FACES){
if(!(psys->flag & PSYS_KEYED))
return 0;
@ -4314,13 +4312,15 @@ int psys_get_particle_state(ParticleSimulationData *sim, int p, ParticleKey *sta
}
}
void psys_get_dupli_texture(Object *UNUSED(ob), ParticleSettings *part, ParticleSystemModifierData *psmd, ParticleData *pa, ChildParticle *cpa, float *uv, float *orco)
void psys_get_dupli_texture(ParticleSystem *psys, ParticleSettings *part, ParticleSystemModifierData *psmd, ParticleData *pa, ChildParticle *cpa, float *uv, float *orco)
{
MFace *mface;
MTFace *mtface;
float loc[3];
int num;
uv[0] = uv[1] = 0.f;
if(cpa) {
if(part->childtype == PART_CHILD_FACES) {
mtface= CustomData_get_layer(&psmd->dm->faceData, CD_MTFACE);
@ -4329,43 +4329,36 @@ void psys_get_dupli_texture(Object *UNUSED(ob), ParticleSettings *part, Particle
mtface += cpa->num;
psys_interpolate_uvs(mtface, mface->v4, cpa->fuv, uv);
}
else
uv[0]= uv[1]= 0.0f;
psys_particle_on_emitter(psmd,PART_FROM_FACE,cpa->num,DMCACHE_ISCHILD,cpa->fuv,cpa->foffset,loc,0,0,0,orco,0);
return;
}
else
uv[0]= uv[1]= 0.0f;
psys_particle_on_emitter(psmd,
(part->childtype == PART_CHILD_FACES)? PART_FROM_FACE: PART_FROM_PARTICLE,
cpa->num,DMCACHE_ISCHILD,cpa->fuv,cpa->foffset,loc,0,0,0,orco,0);
}
else {
if(part->from == PART_FROM_FACE) {
mtface= CustomData_get_layer(&psmd->dm->faceData, CD_MTFACE);
num= pa->num_dmcache;
if(num == DMCACHE_NOTFOUND)
num= pa->num;
if (num >= psmd->dm->getNumFaces(psmd->dm)) {
/* happens when simplify is enabled
* gives invalid coords but would crash otherwise */
num= DMCACHE_NOTFOUND;
}
if(mtface && num != DMCACHE_NOTFOUND) {
mface= psmd->dm->getFaceData(psmd->dm, num, CD_MFACE);
mtface += num;
psys_interpolate_uvs(mtface, mface->v4, pa->fuv, uv);
}
else
uv[0]= uv[1]= 0.0f;
else {
pa = psys->particles + cpa->pa[0];
}
else
uv[0]= uv[1]= 0.0f;
psys_particle_on_emitter(psmd,part->from,pa->num,pa->num_dmcache,pa->fuv,pa->foffset,loc,0,0,0,orco,0);
}
if(part->from == PART_FROM_FACE) {
mtface= CustomData_get_layer(&psmd->dm->faceData, CD_MTFACE);
num= pa->num_dmcache;
if(num == DMCACHE_NOTFOUND)
num= pa->num;
if (num >= psmd->dm->getNumFaces(psmd->dm)) {
/* happens when simplify is enabled
* gives invalid coords but would crash otherwise */
num= DMCACHE_NOTFOUND;
}
if(mtface && num != DMCACHE_NOTFOUND) {
mface= psmd->dm->getFaceData(psmd->dm, num, CD_MFACE);
mtface += num;
psys_interpolate_uvs(mtface, mface->v4, pa->fuv, uv);
}
}
psys_particle_on_emitter(psmd,part->from,pa->num,pa->num_dmcache,pa->fuv,pa->foffset,loc,0,0,0,orco,0);
}
void psys_get_dupli_path_transform(ParticleSimulationData *sim, ParticleData *pa, ChildParticle *cpa, ParticleCacheKey *cache, float mat[][4], float *scale)
@ -4380,7 +4373,7 @@ void psys_get_dupli_path_transform(ParticleSimulationData *sim, ParticleData *pa
len= normalize_v3(vec);
if(psys->part->rotmode) {
if(!pa)
if(pa == NULL)
pa= psys->particles+cpa->pa[0];
vec_to_quat( q,xvec, ob->trackflag, ob->upflag);
@ -4391,12 +4384,13 @@ void psys_get_dupli_path_transform(ParticleSimulationData *sim, ParticleData *pa
mul_m4_m4m4(mat, obrotmat, qmat);
}
else {
if(pa == NULL && psys->part->childflat != PART_CHILD_FACES)
pa = psys->particles + cpa->pa[0];
if(pa)
psys_particle_on_emitter(psmd,sim->psys->part->from,pa->num,pa->num_dmcache,pa->fuv,pa->foffset,loc,nor,0,0,0,0);
else
psys_particle_on_emitter(psmd,
(psys->part->childtype == PART_CHILD_FACES)? PART_FROM_FACE: PART_FROM_PARTICLE,
cpa->num,DMCACHE_ISCHILD,cpa->fuv,cpa->foffset,loc,nor,0,0,0,0);
psys_particle_on_emitter(psmd,PART_FROM_FACE,cpa->num,DMCACHE_ISCHILD,cpa->fuv,cpa->foffset,loc,nor,0,0,0,0);
copy_m3_m4(nmat, ob->imat);
transpose_m3(nmat);

View File

@ -329,6 +329,10 @@ static void alloc_child_particles(ParticleSystem *psys, int tot)
}
}
/************************************************/
/* Distribution */
/************************************************/
void psys_calc_dmcache(Object *ob, DerivedMesh *dm, ParticleSystem *psys)
{
/* use for building derived mesh mapping info:
@ -405,7 +409,35 @@ void psys_calc_dmcache(Object *ob, DerivedMesh *dm, ParticleSystem *psys)
}
}
static void distribute_particles_in_grid(DerivedMesh *dm, ParticleSystem *psys)
static void distribute_simple_children(Scene *scene, Object *ob, DerivedMesh *finaldm, ParticleSystem *psys)
{
ChildParticle *cpa = psys->child;
int i, p;
int child_nbr= get_psys_child_number(scene, psys);
int totpart= get_psys_tot_child(scene, psys);
alloc_child_particles(psys, totpart);
for(i=0; i<child_nbr; i++){
for(p=0; p<psys->totpart; p++,cpa++){
float length=2.0;
cpa->parent=p;
/* create even spherical distribution inside unit sphere */
while(length>=1.0f){
cpa->fuv[0]=2.0f*BLI_frand()-1.0f;
cpa->fuv[1]=2.0f*BLI_frand()-1.0f;
cpa->fuv[2]=2.0f*BLI_frand()-1.0f;
length=len_v3(cpa->fuv);
}
cpa->num=-1;
}
}
/* dmcache must be updated for parent particles if children from faces is used */
psys_calc_dmcache(ob, finaldm, psys);
}
static void distribute_grid(DerivedMesh *dm, ParticleSystem *psys)
{
ParticleData *pa=NULL;
float min[3], max[3], delta[3], d;
@ -686,7 +718,7 @@ static void psys_uv_to_w(float u, float v, int quad, float *w)
}
/* Find the index in "sum" array before "value" is crossed. */
static int binary_search_distribution(float *sum, int n, float value)
static int distribute_binary_search(float *sum, int n, float value)
{
int mid, low=0, high=n;
@ -716,13 +748,11 @@ static int binary_search_distribution(float *sum, int n, float value)
/* note: this function must be thread safe, for from == PART_FROM_CHILD */
#define ONLY_WORKING_WITH_PA_VERTS 0
static void psys_thread_distribute_particle(ParticleThread *thread, ParticleData *pa, ChildParticle *cpa, int p)
static void distribute_threads_exec(ParticleThread *thread, ParticleData *pa, ChildParticle *cpa, int p)
{
ParticleThreadContext *ctx= thread->ctx;
Object *ob= ctx->sim.ob;
DerivedMesh *dm= ctx->dm;
ParticleData *tpa;
/* ParticleSettings *part= ctx->sim.psys->part; */
float *v1, *v2, *v3, *v4, nor[3], orco1[3], co1[3], co2[3], nor1[3];
float cur_d, min_d, randu, randv;
int from= ctx->from;
@ -760,9 +790,17 @@ static void psys_thread_distribute_particle(ParticleThread *thread, ParticleData
switch(distr){
case PART_DISTR_JIT:
ctx->jitoff[i] = fmod(ctx->jitoff[i],(float)ctx->jitlevel);
psys_uv_to_w(ctx->jit[2*(int)ctx->jitoff[i]], ctx->jit[2*(int)ctx->jitoff[i]+1], mface->v4, pa->fuv);
ctx->jitoff[i]++;
if(ctx->jitlevel == 1) {
if(mface->v4)
psys_uv_to_w(0.5f, 0.5f, mface->v4, pa->fuv);
else
psys_uv_to_w(0.33333f, 0.33333f, mface->v4, pa->fuv);
}
else {
ctx->jitoff[i] = fmod(ctx->jitoff[i],(float)ctx->jitlevel);
psys_uv_to_w(ctx->jit[2*(int)ctx->jitoff[i]], ctx->jit[2*(int)ctx->jitoff[i]+1], mface->v4, pa->fuv);
ctx->jitoff[i]++;
}
break;
case PART_DISTR_RAND:
randu= rng_getFloat(thread->rng);
@ -828,15 +866,6 @@ static void psys_thread_distribute_particle(ParticleThread *thread, ParticleData
}
}
}
else if(from == PART_FROM_PARTICLE) {
tpa=ctx->tpars+ctx->index[p];
pa->num=ctx->index[p];
pa->fuv[0]=tpa->fuv[0];
pa->fuv[1]=tpa->fuv[1];
/* abusing foffset a little for timing in near reaction */
pa->foffset=ctx->weight[ctx->index[p]];
ctx->weight[ctx->index[p]]+=ctx->maxweight;
}
else if(from == PART_FROM_CHILD) {
MFace *mf;
@ -870,7 +899,6 @@ static void psys_thread_distribute_particle(ParticleThread *thread, ParticleData
maxd=ptn[maxw-1].dist;
mind=ptn[0].dist;
/*dd=maxd-mind;*/ /*UNUSED*/
/* the weights here could be done better */
for(w=0; w<maxw; w++){
@ -906,7 +934,7 @@ static void psys_thread_distribute_particle(ParticleThread *thread, ParticleData
rng_skip(thread->rng, rng_skip_tot);
}
static void *exec_distribution(void *data)
static void *distribute_threads_exec_cb(void *data)
{
ParticleThread *thread= (ParticleThread*)data;
ParticleSystem *psys= thread->ctx->sim.psys;
@ -923,7 +951,7 @@ static void *exec_distribution(void *data)
rng_skip(thread->rng, PSYS_RND_DIST_SKIP * thread->ctx->skip[p]);
if((p+thread->num) % thread->tot == 0)
psys_thread_distribute_particle(thread, NULL, cpa, p);
distribute_threads_exec(thread, NULL, cpa, p);
else /* thread skip */
rng_skip(thread->rng, PSYS_RND_DIST_SKIP);
}
@ -932,7 +960,7 @@ static void *exec_distribution(void *data)
totpart= psys->totpart;
pa= psys->particles + thread->num;
for(p=thread->num; p<totpart; p+=thread->tot, pa+=thread->tot)
psys_thread_distribute_particle(thread, pa, NULL, p);
distribute_threads_exec(thread, pa, NULL, p);
}
return 0;
@ -940,7 +968,7 @@ static void *exec_distribution(void *data)
/* not thread safe, but qsort doesn't take userdata argument */
static int *COMPARE_ORIG_INDEX = NULL;
static int compare_orig_index(const void *p1, const void *p2)
static int distribute_compare_orig_index(const void *p1, const void *p2)
{
int index1 = COMPARE_ORIG_INDEX[*(const int*)p1];
int index2 = COMPARE_ORIG_INDEX[*(const int*)p2];
@ -961,44 +989,54 @@ static int compare_orig_index(const void *p1, const void *p2)
return 1;
}
/* creates a distribution of coordinates on a DerivedMesh */
/* */
/* 1. lets check from what we are emitting */
/* 2. now we know that we have something to emit from so */
/* let's calculate some weights */
/* 2.1 from even distribution */
/* 2.2 and from vertex groups */
/* 3. next we determine the indexes of emitting thing that */
/* the particles will have */
/* 4. let's do jitter if we need it */
/* 5. now we're ready to set the indexes & distributions to */
/* the particles */
/* 6. and we're done! */
static void distribute_invalid(Scene *scene, ParticleSystem *psys, int from)
{
if(from == PART_FROM_CHILD) {
ChildParticle *cpa;
int p, totchild = get_psys_tot_child(scene, psys);
if(psys->child && totchild) {
for(p=0,cpa=psys->child; p<totchild; p++,cpa++){
cpa->fuv[0]=cpa->fuv[1]=cpa->fuv[2]=cpa->fuv[3]= 0.0;
cpa->foffset= 0.0f;
cpa->parent=0;
cpa->pa[0]=cpa->pa[1]=cpa->pa[2]=cpa->pa[3]=0;
cpa->num= -1;
}
}
}
else {
PARTICLE_P;
LOOP_PARTICLES {
pa->fuv[0]=pa->fuv[1]=pa->fuv[2]= pa->fuv[3]= 0.0;
pa->foffset= 0.0f;
pa->num= -1;
}
}
}
/* Creates a distribution of coordinates on a DerivedMesh */
/* This is to denote functionality that does not yet work with mesh - only derived mesh */
static int psys_threads_init_distribution(ParticleThread *threads, Scene *scene, DerivedMesh *finaldm, int from)
static int distribute_threads_init_data(ParticleThread *threads, Scene *scene, DerivedMesh *finaldm, int from)
{
ParticleThreadContext *ctx= threads[0].ctx;
Object *ob= ctx->sim.ob;
ParticleSystem *psys= ctx->sim.psys;
Object *tob;
ParticleData *pa=0, *tpars= 0;
ParticleSettings *part;
ParticleSystem *tpsys;
ParticleSeam *seams= 0;
ChildParticle *cpa=0;
KDTree *tree=0;
DerivedMesh *dm= NULL;
float *jit= NULL;
int i, seed, p=0, totthread= threads[0].tot;
int /*no_distr=0,*/ cfrom=0;
int tot=0, totpart, *index=0, children=0, totseam=0;
//int *vertpart=0;
int cfrom=0;
int totelem=0, totpart, *particle_element=0, children=0, totseam=0;
int jitlevel= 1, distr;
float *weight=0,*sum=0,*jitoff=0;
float cur, maxweight=0.0, tweight, totweight, co[3], nor[3], orco[3], ornor[3];
float *element_weight=NULL,*element_sum=NULL,*jitter_offset=NULL, *vweight=NULL;
float cur, maxweight=0.0, tweight, totweight, inv_totweight, co[3], nor[3], orco[3], ornor[3];
if(ob==0 || psys==0 || psys->part==0)
if(ELEM3(NULL, ob, psys, psys->part))
return 0;
part=psys->part;
@ -1012,81 +1050,63 @@ static int psys_threads_init_distribution(ParticleThread *threads, Scene *scene,
return 0;
}
BLI_srandom(31415926 + psys->seed);
if(from==PART_FROM_CHILD){
distr=PART_DISTR_RAND;
BLI_srandom(31415926 + psys->seed + psys->child_seed);
if(part->from!=PART_FROM_PARTICLE && part->childtype==PART_CHILD_FACES){
dm= finaldm;
children=1;
tree=BLI_kdtree_new(totpart);
for(p=0,pa=psys->particles; p<totpart; p++,pa++){
psys_particle_on_dm(dm,part->from,pa->num,pa->num_dmcache,pa->fuv,pa->foffset,co,nor,0,0,orco,ornor);
transform_mesh_orco_verts((Mesh*)ob->data, &orco, 1, 1);
BLI_kdtree_insert(tree, p, orco, ornor);
}
BLI_kdtree_balance(tree);
totpart=get_psys_tot_child(scene, psys);
cfrom=from=PART_FROM_FACE;
}
else{
/* no need to figure out distribution */
int child_nbr= get_psys_child_number(scene, psys);
totpart= get_psys_tot_child(scene, psys);
alloc_child_particles(psys, totpart);
cpa=psys->child;
for(i=0; i<child_nbr; i++){
for(p=0; p<psys->totpart; p++,cpa++){
float length=2.0;
cpa->parent=p;
/* create even spherical distribution inside unit sphere */
while(length>=1.0f){
cpa->fuv[0]=2.0f*BLI_frand()-1.0f;
cpa->fuv[1]=2.0f*BLI_frand()-1.0f;
cpa->fuv[2]=2.0f*BLI_frand()-1.0f;
length=len_v3(cpa->fuv);
}
cpa->num=-1;
}
}
/* dmcache must be updated for parent particles if children from faces is used */
psys_calc_dmcache(ob, finaldm, psys);
/* First handle special cases */
if(from == PART_FROM_CHILD) {
/* Simple children */
if(part->childtype != PART_CHILD_FACES) {
BLI_srandom(31415926 + psys->seed + psys->child_seed);
distribute_simple_children(scene, ob, finaldm, psys);
return 0;
}
}
else{
dm= CDDM_from_mesh((Mesh*)ob->data, ob);
/* special handling of grid distribution */
else {
/* Grid distribution */
if(part->distr==PART_DISTR_GRID && from != PART_FROM_VERT){
distribute_particles_in_grid(dm,psys);
BLI_srandom(31415926 + psys->seed);
dm= CDDM_from_mesh((Mesh*)ob->data, ob);
distribute_grid(dm,psys);
dm->release(dm);
return 0;
}
}
/* Create trees and original coordinates if needed */
if(from == PART_FROM_CHILD) {
distr=PART_DISTR_RAND;
BLI_srandom(31415926 + psys->seed + psys->child_seed);
dm= finaldm;
children=1;
totpart = get_psys_tot_child(scene, psys);
cfrom = from = PART_FROM_FACE;
tree=BLI_kdtree_new(totpart);
for(p=0,pa=psys->particles; p<totpart; p++,pa++){
psys_particle_on_dm(dm,part->from,pa->num,pa->num_dmcache,pa->fuv,pa->foffset,co,nor,0,0,orco,ornor);
transform_mesh_orco_verts((Mesh*)ob->data, &orco, 1, 1);
BLI_kdtree_insert(tree, p, orco, ornor);
}
BLI_kdtree_balance(tree);
}
else {
distr = part->distr;
BLI_srandom(31415926 + psys->seed);
dm= CDDM_from_mesh((Mesh*)ob->data, ob);
/* we need orco for consistent distributions */
DM_add_vert_layer(dm, CD_ORCO, CD_ASSIGN, get_mesh_orco_verts(ob));
distr=part->distr;
if(from==PART_FROM_VERT){
if(from == PART_FROM_VERT) {
MVert *mv= dm->getVertDataArray(dm, CD_MVERT);
float (*orcodata)[3]= dm->getVertDataArray(dm, CD_ORCO);
int totvert = dm->getNumVerts(dm);
tree=BLI_kdtree_new(totvert);
for(p=0; p<totvert; p++){
for(p=0; p<totvert; p++) {
if(orcodata) {
VECCOPY(co,orcodata[p])
transform_mesh_orco_verts((Mesh*)ob->data, &co, 1, 1);
@ -1100,73 +1120,33 @@ static int psys_threads_init_distribution(ParticleThread *threads, Scene *scene,
}
}
/* 1. */
switch(from){
case PART_FROM_VERT:
tot = dm->getNumVerts(dm);
break;
case PART_FROM_VOLUME:
case PART_FROM_FACE:
tot = dm->getNumFaces(dm);
break;
case PART_FROM_PARTICLE:
if(psys->target_ob)
tob=psys->target_ob;
else
tob=ob;
/* Get total number of emission elements and allocate needed arrays */
totelem = (from == PART_FROM_VERT) ? dm->getNumVerts(dm) : dm->getNumFaces(dm);
if((tpsys=BLI_findlink(&tob->particlesystem,psys->target_psys-1))){
tpars=tpsys->particles;
tot=tpsys->totpart;
}
break;
}
if(totelem == 0){
distribute_invalid(scene, psys, children ? PART_FROM_CHILD : 0);
if(tot==0){
/*no_distr=1;*/ /*UNUSED*/
if(children){
if(G.f & G_DEBUG)
fprintf(stderr,"Particle child distribution error: Nothing to emit from!\n");
if(psys->child) {
for(p=0,cpa=psys->child; p<totpart; p++,cpa++){
cpa->fuv[0]=cpa->fuv[1]=cpa->fuv[2]=cpa->fuv[3]= 0.0;
cpa->foffset= 0.0f;
cpa->parent=0;
cpa->pa[0]=cpa->pa[1]=cpa->pa[2]=cpa->pa[3]=0;
cpa->num= -1;
}
}
}
else {
if(G.f & G_DEBUG)
fprintf(stderr,"Particle distribution error: Nothing to emit from!\n");
for(p=0,pa=psys->particles; p<totpart; p++,pa++){
pa->fuv[0]=pa->fuv[1]=pa->fuv[2]= pa->fuv[3]= 0.0;
pa->foffset= 0.0f;
pa->num= -1;
}
}
if(G.f & G_DEBUG)
fprintf(stderr,"Particle distribution error: Nothing to emit from!\n");
if(dm != finaldm) dm->release(dm);
return 0;
}
/* 2. */
element_weight = MEM_callocN(sizeof(float)*totelem, "particle_distribution_weights");
particle_element= MEM_callocN(sizeof(int)*totpart, "particle_distribution_indexes");
element_sum = MEM_callocN(sizeof(float)*(totelem+1), "particle_distribution_sum");
jitter_offset = MEM_callocN(sizeof(float)*totelem, "particle_distribution_jitoff");
weight=MEM_callocN(sizeof(float)*tot, "particle_distribution_weights");
index=MEM_callocN(sizeof(int)*totpart, "particle_distribution_indexes");
sum=MEM_callocN(sizeof(float)*(tot+1), "particle_distribution_sum");
jitoff=MEM_callocN(sizeof(float)*tot, "particle_distribution_jitoff");
/* 2.1 */
if((part->flag&PART_EDISTR || children) && ELEM(from,PART_FROM_PARTICLE,PART_FROM_VERT)==0){
/* Calculate weights from face areas */
if((part->flag&PART_EDISTR || children) && from != PART_FROM_VERT){
MVert *v1, *v2, *v3, *v4;
float totarea=0.0, co1[3], co2[3], co3[3], co4[3];
float totarea=0.f, co1[3], co2[3], co3[3], co4[3];
float (*orcodata)[3];
orcodata= dm->getVertDataArray(dm, CD_ORCO);
for(i=0; i<tot; i++){
for(i=0; i<totelem; i++){
MFace *mf=dm->getFaceData(dm,i,CD_MFACE);
if(orcodata) {
@ -1176,6 +1156,10 @@ static int psys_threads_init_distribution(ParticleThread *threads, Scene *scene,
transform_mesh_orco_verts((Mesh*)ob->data, &co1, 1, 1);
transform_mesh_orco_verts((Mesh*)ob->data, &co2, 1, 1);
transform_mesh_orco_verts((Mesh*)ob->data, &co3, 1, 1);
if(mf->v4) {
VECCOPY(co4, orcodata[mf->v4]);
transform_mesh_orco_verts((Mesh*)ob->data, &co4, 1, 1);
}
}
else {
v1= (MVert*)dm->getVertData(dm,mf->v1,CD_MVERT);
@ -1184,156 +1168,133 @@ static int psys_threads_init_distribution(ParticleThread *threads, Scene *scene,
VECCOPY(co1, v1->co);
VECCOPY(co2, v2->co);
VECCOPY(co3, v3->co);
}
if (mf->v4){
if(orcodata) {
VECCOPY(co4, orcodata[mf->v4]);
transform_mesh_orco_verts((Mesh*)ob->data, &co4, 1, 1);
}
else {
if(mf->v4) {
v4= (MVert*)dm->getVertData(dm,mf->v4,CD_MVERT);
VECCOPY(co4, v4->co);
}
cur= area_quad_v3(co1, co2, co3, co4);
}
else
cur= area_tri_v3(co1, co2, co3);
if(cur>maxweight)
maxweight=cur;
weight[i]= cur;
totarea+=cur;
cur = mf->v4 ? area_quad_v3(co1, co2, co3, co4) : area_tri_v3(co1, co2, co3);
if(cur > maxweight)
maxweight = cur;
element_weight[i] = cur;
totarea += cur;
}
for(i=0; i<tot; i++)
weight[i] /= totarea;
for(i=0; i<totelem; i++)
element_weight[i] /= totarea;
maxweight /= totarea;
}
else if(from==PART_FROM_PARTICLE){
float val=(float)tot/(float)totpart;
for(i=0; i<tot; i++)
weight[i]=val;
maxweight=val;
}
else{
float min=1.0f/(float)(MIN2(tot,totpart));
for(i=0; i<tot; i++)
weight[i]=min;
float min=1.0f/(float)(MIN2(totelem,totpart));
for(i=0; i<totelem; i++)
element_weight[i]=min;
maxweight=min;
}
/* 2.2 */
if(ELEM3(from,PART_FROM_VERT,PART_FROM_FACE,PART_FROM_VOLUME)){
float *vweight= psys_cache_vgroup(dm,psys,PSYS_VG_DENSITY);
/* Calculate weights from vgroup */
vweight = psys_cache_vgroup(dm,psys,PSYS_VG_DENSITY);
if(vweight){
if(from==PART_FROM_VERT) {
for(i=0;i<tot; i++)
weight[i]*=vweight[i];
}
else { /* PART_FROM_FACE / PART_FROM_VOLUME */
for(i=0;i<tot; i++){
MFace *mf=dm->getFaceData(dm,i,CD_MFACE);
tweight = vweight[mf->v1] + vweight[mf->v2] + vweight[mf->v3];
if(mf->v4) {
tweight += vweight[mf->v4];
tweight /= 4.0;
}
else {
tweight /= 3.0;
}
weight[i]*=tweight;
}
}
MEM_freeN(vweight);
if(vweight){
if(from==PART_FROM_VERT) {
for(i=0;i<totelem; i++)
element_weight[i]*=vweight[i];
}
else { /* PART_FROM_FACE / PART_FROM_VOLUME */
for(i=0;i<totelem; i++){
MFace *mf=dm->getFaceData(dm,i,CD_MFACE);
tweight = vweight[mf->v1] + vweight[mf->v2] + vweight[mf->v3];
if(mf->v4) {
tweight += vweight[mf->v4];
tweight /= 4.0;
}
else {
tweight /= 3.0;
}
element_weight[i]*=tweight;
}
}
MEM_freeN(vweight);
}
/* 3. */
/* Calculate total weight of all elements */
totweight= 0.0f;
for(i=0;i<tot; i++)
totweight += weight[i];
for(i=0;i<totelem; i++)
totweight += element_weight[i];
if(totweight > 0.0f)
totweight= 1.0f/totweight;
inv_totweight = (totweight > 0.f ? 1.f/totweight : 0.f);
sum[0]= 0.0f;
for(i=0;i<tot; i++)
sum[i+1]= sum[i]+weight[i]*totweight;
/* Calculate cumulative weights */
element_sum[0]= 0.0f;
for(i=0; i<totelem; i++)
element_sum[i+1]= element_sum[i] + element_weight[i] * inv_totweight;
/* Finally assign elements to particles */
if((part->flag&PART_TRAND) || (part->simplify_flag&PART_SIMPLIFY_ENABLE)) {
float pos;
for(p=0; p<totpart; p++) {
/* In theory sys[tot] should be 1.0, but due to float errors this is not necessarily always true, so scale pos accordingly. */
pos= BLI_frand() * sum[tot];
index[p]= binary_search_distribution(sum, tot, pos);
index[p]= MIN2(tot-1, index[p]);
jitoff[index[p]]= pos;
/* In theory element_sum[totelem] should be 1.0, but due to float errors this is not necessarily always true, so scale pos accordingly. */
pos= BLI_frand() * element_sum[totelem];
particle_element[p]= distribute_binary_search(element_sum, totelem, pos);
particle_element[p]= MIN2(totelem-1, particle_element[p]);
jitter_offset[particle_element[p]]= pos;
}
}
else {
double step, pos;
step= (totpart <= 1)? 0.5: 1.0/(totpart-1);
pos= 1e-16f; /* tiny offset to avoid zero weight face */
step= (totpart < 2) ? 0.5 : 1.0/(double)totpart;
pos= 1e-16; /* tiny offset to avoid zero weight face */
i= 0;
for(p=0; p<totpart; p++, pos+=step) {
while((i < tot) && (pos > sum[i+1]))
while((i < totelem) && (pos > element_sum[i+1]))
i++;
index[p]= MIN2(tot-1, i);
particle_element[p]= MIN2(totelem-1, i);
/* avoid zero weight face */
if(p == totpart-1 && weight[index[p]] == 0.0f)
index[p]= index[p-1];
if(p == totpart-1 && element_weight[particle_element[p]] == 0.0f)
particle_element[p]= particle_element[p-1];
jitoff[index[p]]= pos;
jitter_offset[particle_element[p]]= pos;
}
}
MEM_freeN(sum);
MEM_freeN(element_sum);
/* for hair, sort by origindex, allows optimizations in rendering */
/* however with virtual parents the children need to be in random order */
/* For hair, sort by origindex (allows optimizations in rendering), */
/* however with virtual parents the children need to be in random order. */
if(part->type == PART_HAIR && !(part->childtype==PART_CHILD_FACES && part->parents!=0.0)) {
if(from != PART_FROM_PARTICLE) {
COMPARE_ORIG_INDEX = NULL;
if(from == PART_FROM_VERT) {
if(dm->numVertData)
COMPARE_ORIG_INDEX= dm->getVertDataArray(dm, CD_ORIGINDEX);
}
else {
if(dm->numFaceData)
COMPARE_ORIG_INDEX= dm->getFaceDataArray(dm, CD_ORIGINDEX);
}
if(COMPARE_ORIG_INDEX) {
qsort(particle_element, totpart, sizeof(int), distribute_compare_orig_index);
COMPARE_ORIG_INDEX = NULL;
if(from == PART_FROM_VERT) {
if(dm->numVertData)
COMPARE_ORIG_INDEX= dm->getVertDataArray(dm, CD_ORIGINDEX);
}
else {
if(dm->numFaceData)
COMPARE_ORIG_INDEX= dm->getFaceDataArray(dm, CD_ORIGINDEX);
}
if(COMPARE_ORIG_INDEX) {
qsort(index, totpart, sizeof(int), compare_orig_index);
COMPARE_ORIG_INDEX = NULL;
}
}
}
/* weights are no longer used except for FROM_PARTICLE, which needs them zeroed for indexing */
if(from==PART_FROM_PARTICLE){
for(i=0; i<tot; i++)
weight[i]=0.0f;
}
/* 4. */
/* Create jittering if needed */
if(distr==PART_DISTR_JIT && ELEM(from,PART_FROM_FACE,PART_FROM_VOLUME)) {
jitlevel= part->userjit;
if(jitlevel == 0) {
jitlevel= totpart/tot;
jitlevel= totpart/totelem;
if(part->flag & PART_EDISTR) jitlevel*= 2; /* looks better in general, not very scietific */
if(jitlevel<3) jitlevel= 3;
}
@ -1350,16 +1311,16 @@ static int psys_threads_init_distribution(ParticleThread *threads, Scene *scene,
BLI_array_randomize(jit, 2*sizeof(float), jitlevel, psys->seed); /* for custom jit or even distribution */
}
/* 5. */
/* Setup things for threaded distribution */
ctx->tree= tree;
ctx->seams= seams;
ctx->totseam= totseam;
ctx->sim.psys= psys;
ctx->index= index;
ctx->index= particle_element;
ctx->jit= jit;
ctx->jitlevel= jitlevel;
ctx->jitoff= jitoff;
ctx->weight= weight;
ctx->jitoff= jitter_offset;
ctx->weight= element_weight;
ctx->maxweight= maxweight;
ctx->from= (children)? PART_FROM_CHILD: from;
ctx->cfrom= cfrom;
@ -1394,14 +1355,14 @@ static void distribute_particles_on_dm(ParticleSimulationData *sim, int from)
pthreads= psys_threads_create(sim);
if(!psys_threads_init_distribution(pthreads, sim->scene, finaldm, from)) {
if(!distribute_threads_init_data(pthreads, sim->scene, finaldm, from)) {
psys_threads_free(pthreads);
return;
}
totthread= pthreads[0].tot;
if(totthread > 1) {
BLI_init_threads(&threads, exec_distribution, totthread);
BLI_init_threads(&threads, distribute_threads_exec_cb, totthread);
for(i=0; i<totthread; i++)
BLI_insert_thread(&threads, &pthreads[i]);
@ -1409,7 +1370,7 @@ static void distribute_particles_on_dm(ParticleSimulationData *sim, int from)
BLI_end_threads(&threads);
}
else
exec_distribution(&pthreads[0]);
distribute_threads_exec_cb(&pthreads[0]);
psys_calc_dmcache(sim->ob, finaldm, sim->psys);
@ -1423,17 +1384,11 @@ static void distribute_particles_on_dm(ParticleSimulationData *sim, int from)
/* ready for future use, to emit particles without geometry */
static void distribute_particles_on_shape(ParticleSimulationData *sim, int UNUSED(from))
{
ParticleSystem *psys = sim->psys;
PARTICLE_P;
distribute_invalid(sim->scene, sim->psys, 0);
fprintf(stderr,"Shape emission not yet possible!\n");
LOOP_PARTICLES {
pa->fuv[0]=pa->fuv[1]=pa->fuv[2]=pa->fuv[3]= 0.0;
pa->foffset= 0.0f;
pa->num= -1;
}
}
static void distribute_particles(ParticleSimulationData *sim, int from)
{
PARTICLE_PSMD;
@ -1449,16 +1404,9 @@ static void distribute_particles(ParticleSimulationData *sim, int from)
distribute_particles_on_shape(sim, from);
if(distr_error){
ParticleSystem *psys = sim->psys;
PARTICLE_P;
distribute_invalid(sim->scene, sim->psys, from);
fprintf(stderr,"Particle distribution error!\n");
LOOP_PARTICLES {
pa->fuv[0]=pa->fuv[1]=pa->fuv[2]=pa->fuv[3]= 0.0;
pa->foffset= 0.0f;
pa->num= -1;
}
}
}
@ -1547,7 +1495,7 @@ void initialize_particle(ParticleSimulationData *sim, ParticleData *pa, int p)
pa->flag &= ~PARS_UNEXIST;
if(part->from != PART_FROM_PARTICLE && part->type != PART_FLUID) {
if(part->type != PART_FLUID) {
psys_get_texture(sim, pa, &ptex, PAMAP_INIT, 0.f);
if(ptex.exist < PSYS_FRAND(p+125))
@ -1628,34 +1576,6 @@ void reset_particle(ParticleSimulationData *sim, ParticleData *pa, float dtime,
int p = pa - psys->particles;
part=psys->part;
#if 0 /* deprecated code */
if(part->from==PART_FROM_PARTICLE){
float speed;
ParticleSimulationData tsim= {0};
tsim.scene= sim->scene;
tsim.ob= psys->target_ob ? psys->target_ob : ob;
tsim.psys = BLI_findlink(&tsim.ob->particlesystem, sim->psys->target_psys-1);
state.time = pa->time;
if(pa->num == -1)
memset(&state, 0, sizeof(state));
else
psys_get_particle_state(&tsim, pa->num, &state, 1);
psys_get_from_key(&state, loc, nor, rot, 0);
mul_qt_v3(rot, vtan);
mul_qt_v3(rot, utan);
speed= normalize_v3_v3(p_vel, state.vel);
mul_v3_fl(p_vel, dot_v3v3(r_vel, p_vel));
VECSUB(p_vel, r_vel, p_vel);
normalize_v3(p_vel);
mul_v3_fl(p_vel, speed);
VECCOPY(pa->fuv, loc); /* abusing pa->fuv (not used for "from particle") for storing emit location */
}
else{
#endif
/* get precise emitter matrix if particle is born */
if(part->type!=PART_HAIR && pa->time < cfra && pa->time >= sim->psys->cfra) {
/* we have to force RECALC_ANIM here since where_is_objec_time only does drivers */
@ -3156,7 +3076,7 @@ static void psys_update_path_cache(ParticleSimulationData *sim, float cfra)
if(!(psys->part->type == PART_HAIR) || (psys->flag & PSYS_HAIR_DONE)) {
distribute_particles(sim, PART_FROM_CHILD);
if(part->from!=PART_FROM_PARTICLE && part->childtype==PART_CHILD_FACES && part->parents!=0.0)
if(part->childtype==PART_CHILD_FACES && part->parents!=0.0)
psys_find_parents(sim);
}
}
@ -3923,13 +3843,6 @@ static void psys_changed_type(ParticleSimulationData *sim)
BKE_ptcache_id_from_particles(&pid, sim->ob, sim->psys);
if(part->from == PART_FROM_PARTICLE) {
//if(part->type != PART_REACTOR)
part->from = PART_FROM_FACE;
if(part->distr == PART_DISTR_GRID && part->from != PART_FROM_VERT)
part->distr = PART_DISTR_JIT;
}
if(part->phystype != PART_PHYS_KEYED)
sim->psys->flag &= ~PSYS_KEYED;
@ -3937,6 +3850,9 @@ static void psys_changed_type(ParticleSimulationData *sim)
if(ELEM4(part->ren_as, PART_DRAW_NOT, PART_DRAW_PATH, PART_DRAW_OB, PART_DRAW_GR)==0)
part->ren_as = PART_DRAW_PATH;
if(part->distr == PART_DISTR_GRID)
part->distr = PART_DISTR_JIT;
if(ELEM3(part->draw_as, PART_DRAW_NOT, PART_DRAW_REND, PART_DRAW_PATH)==0)
part->draw_as = PART_DRAW_REND;

View File

@ -339,7 +339,7 @@ typedef struct ParticleSystem{ /* note, make sure all (runtime) are NULL's in
#define PART_FROM_VERT 0
#define PART_FROM_FACE 1
#define PART_FROM_VOLUME 2
#define PART_FROM_PARTICLE 3
/* #define PART_FROM_PARTICLE 3 deprecated! */
#define PART_FROM_CHILD 4
/* part->distr */

View File

@ -60,7 +60,19 @@ EnumPropertyItem part_reactor_from_items[] = {
{PART_FROM_VERT, "VERT", 0, "Verts", ""},
{PART_FROM_FACE, "FACE", 0, "Faces", ""},
{PART_FROM_VOLUME, "VOLUME", 0, "Volume", ""},
{PART_FROM_PARTICLE, "PARTICLE", 0, "Particle", ""},
{0, NULL, 0, NULL, NULL}
};
EnumPropertyItem part_dist_items[] = {
{PART_DISTR_JIT, "JIT", 0, "Jittered", ""},
{PART_DISTR_RAND, "RAND", 0, "Random", ""},
{PART_DISTR_GRID, "GRID", 0, "Grid", ""},
{0, NULL, 0, NULL, NULL}
};
EnumPropertyItem part_hair_dist_items[] = {
{PART_DISTR_JIT, "JIT", 0, "Jittered", ""},
{PART_DISTR_RAND, "RAND", 0, "Random", ""},
{0, NULL, 0, NULL, NULL}
};
@ -665,6 +677,16 @@ static EnumPropertyItem *rna_Particle_from_itemf(bContext *C, PointerRNA *ptr, i
return part_from_items;
}
static EnumPropertyItem *rna_Particle_dist_itemf(bContext *C, PointerRNA *ptr, int *free)
{
ParticleSettings *part = ptr->id.data;
if(part->type==PART_HAIR)
return part_hair_dist_items;
else
return part_dist_items;
}
static EnumPropertyItem *rna_Particle_draw_as_itemf(bContext *C, PointerRNA *ptr, int *free)
{
ParticleSettings *part = ptr->id.data;
@ -1361,13 +1383,6 @@ static void rna_def_particle_settings(BlenderRNA *brna)
{0, NULL, 0, NULL, NULL}
};
static EnumPropertyItem dist_items[] = {
{PART_DISTR_JIT, "JIT", 0, "Jittered", ""},
{PART_DISTR_RAND, "RAND", 0, "Random", ""},
{PART_DISTR_GRID, "GRID", 0, "Grid", ""},
{0, NULL, 0, NULL, NULL}
};
static EnumPropertyItem phys_type_items[] = {
{PART_PHYS_NO, "NO", 0, "No", ""},
{PART_PHYS_NEWTON, "NEWTON", 0, "Newtonian", ""},
@ -1609,7 +1624,9 @@ static void rna_def_particle_settings(BlenderRNA *brna)
prop= RNA_def_property(srna, "distribution", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, NULL, "distr");
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
RNA_def_property_enum_items(prop, dist_items);
RNA_def_property_enum_items(prop, part_dist_items);
RNA_def_property_enum_items(prop, part_draw_as_items);
RNA_def_property_enum_funcs(prop, NULL, NULL, "rna_Particle_dist_itemf");
RNA_def_property_ui_text(prop, "Distribution", "How to distribute particles on selected element");
RNA_def_property_update(prop, 0, "rna_Particle_reset");

View File

@ -1713,25 +1713,6 @@ static int render_new_particle_system(Render *re, ObjectRen *obr, ParticleSystem
bb.totnum = totpart+totchild;
bb.uv_split = part->bb_uv_split;
}
#if 0 // XXX old animation system
/* 2.3 setup time */
if(part->flag&PART_ABS_TIME && part->ipo) {
calc_ipo(part->ipo, cfra);
execute_ipo((ID *)part, part->ipo);
}
if(part->flag & PART_GLOB_TIME)
#endif // XXX old animation system
///* 2.4 setup reactors */
// if(part->type == PART_REACTOR){
// psys_get_reactor_target(ob, psys, &tob, &tpsys);
// if(tpsys && (part->from==PART_FROM_PARTICLE || part->phystype==PART_PHYS_NO)){
// psmd = psys_get_modifier(tob,tpsys);
// tpart = tpsys->part;
// }
// }
/* 2.5 setup matrices */
mul_m4_m4m4(mat, ob->obmat, re->viewmat);
@ -1828,7 +1809,7 @@ static int render_new_particle_system(Render *re, ObjectRen *obr, ParticleSystem
hasize = ma->hasize;
/* get orco */
if(tpsys && (part->from==PART_FROM_PARTICLE || part->phystype==PART_PHYS_NO)){
if(tpsys && part->phystype==PART_PHYS_NO){
tpa=tpsys->particles+pa->num;
psys_particle_on_emitter(psmd,tpart->from,tpa->num,pa->num_dmcache,tpa->fuv,tpa->foffset,co,nor,0,0,sd.orco,0);
}
@ -1892,7 +1873,7 @@ static int render_new_particle_system(Render *re, ObjectRen *obr, ParticleSystem
}
/* get uvco & mcol */
if(part->from!=PART_FROM_PARTICLE && part->childtype==PART_CHILD_FACES) {
if(part->childtype==PART_CHILD_FACES) {
get_particle_uvco_mcol(PART_FROM_FACE, psmd->dm, cpa->fuv, cpa->num, &sd);
}
else {