diff options
Diffstat (limited to 'source/blender/blenkernel/intern')
70 files changed, 26606 insertions, 10090 deletions
diff --git a/source/blender/blenkernel/intern/BME_Customdata.c b/source/blender/blenkernel/intern/BME_Customdata.c new file mode 100644 index 00000000000..1fc8a4071dc --- /dev/null +++ b/source/blender/blenkernel/intern/BME_Customdata.c @@ -0,0 +1,199 @@ +/** + * BME_customdata.c jan 2007 + * + * Custom Data functions for Bmesh + * + * $Id: BKE_bmesh.h,v 1.00 2007/01/17 17:42:01 Briggs Exp $ + * + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. The Blender + * Foundation also sells licenses for use in proprietary software under + * the Blender License. See http://www.blender.org/BL/ for information + * about this. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * The Original Code is Copyright (C) 2004 Blender Foundation. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): Geoffrey Bantle, Brecht Van Lommel, Ben Batt + * + * ***** END GPL LICENSE BLOCK ***** + */ + + +#include "BKE_bmesh.h" +#include "BKE_bmeshCustomData.h" +#include "bmesh_private.h" +#include <string.h> +#include "MEM_guardedalloc.h" +#include "BLI_mempool.h" + +/********************* Layer type information **********************/ +typedef struct BME_LayerTypeInfo { + int size; + char *defaultname; + void (*copy)(const void *source, void *dest, int count); + void (*free)(void *data, int count, int size); + void (*interp)(void **sources, float *weights, float *sub_weights, int count, void *dest); + void (*set_default)(void *data, int count); +} BME_LayerTypeInfo; +const BME_LayerTypeInfo BMELAYERTYPEINFO[BME_CD_NUMTYPES] = { + {sizeof(BME_facetex), "TexFace", NULL, NULL, NULL, NULL}, + {sizeof(BME_looptex), "UV", NULL, NULL, NULL, NULL}, + {sizeof(BME_loopcol), "VCol", NULL, NULL, NULL, NULL}, + {sizeof(BME_DeformVert), "Group", NULL, NULL, NULL, NULL} +}; +static const BME_LayerTypeInfo *BME_layerType_getInfo(int type) +{ + if(type < 0 || type >= CD_NUMTYPES) return NULL; + + return &BMELAYERTYPEINFO[type]; +} +void BME_CD_Create(BME_CustomData *data, BME_CustomDataInit *init, int initalloc) +{ + int i, j, offset=0; + const BME_LayerTypeInfo *info; + + /*initialize data members*/ + data->layers = NULL; + data->pool = NULL; + data->totlayer = 0; + data->totsize = 0; + + /*first count how many layers to alloc*/ + for(i=0; i < BME_CD_NUMTYPES; i++){ + info = BME_layerType_getInfo(i); + data->totlayer += init->layout[i]; + data->totsize += (init->layout[i] * info->size); + } + /*alloc our layers*/ + if(data->totlayer){ + /*alloc memory*/ + data->layers = MEM_callocN(sizeof(BME_CustomDataLayer)*data->totlayer, "BMesh Custom Data Layers"); + data->pool = BLI_mempool_create(data->totsize, initalloc, initalloc); + /*initialize layer data*/ + for(i=0; i < BME_CD_NUMTYPES; i++){ + if(init->layout[i]){ + info = BME_layerType_getInfo(i); + for(j=0; j < init->layout[i]; j++){ + if(j==0) data->layers[j+i].active = init->active[i]; + data->layers[j+i].type = i; + data->layers[j+i].offset = offset; + strcpy(data->layers[j+i].name, &(init->nametemplate[j+i])); + offset += info->size; + } + } + } + } +} + +void BME_CD_Free(BME_CustomData *data) +{ + if(data->pool) BLI_mempool_destroy(data->pool); +} + +/*Block level ops*/ +void BME_CD_free_block(BME_CustomData *data, void **block) +{ + const BME_LayerTypeInfo *typeInfo; + int i; + + if(!*block) return; + for(i = 0; i < data->totlayer; ++i) { + typeInfo = BME_layerType_getInfo(data->layers[i].type); + if(typeInfo->free) { + int offset = data->layers[i].offset; + typeInfo->free((char*)*block + offset, 1, typeInfo->size); + } + } + BLI_mempool_free(data->pool, *block); + *block = NULL; +} + + +static void BME_CD_alloc_block(BME_CustomData *data, void **block) +{ + + if (*block) BME_CD_free_block(data, block); //if we copy layers that have their own free functions like deformverts + + if (data->totsize > 0) + *block = BLI_mempool_alloc(data->pool); + else + *block = NULL; +} + +void BME_CD_copy_data(const BME_CustomData *source, BME_CustomData *dest, + void *src_block, void **dest_block) +{ + const BME_LayerTypeInfo *typeInfo; + int dest_i, src_i; + + if (!*dest_block) /*for addXXXlist functions!*/ + BME_CD_alloc_block(dest, dest_block); + + /* copies a layer at a time */ + dest_i = 0; + for(src_i = 0; src_i < source->totlayer; ++src_i) { + + /* find the first dest layer with type >= the source type + * (this should work because layers are ordered by type) + */ + while(dest_i < dest->totlayer + && dest->layers[dest_i].type < source->layers[src_i].type) + ++dest_i; + + /* if there are no more dest layers, we're done */ + if(dest_i >= dest->totlayer) return; + + /* if we found a matching layer, copy the data */ + if(dest->layers[dest_i].type == source->layers[src_i].type && + strcmp(dest->layers[dest_i].name, source->layers[src_i].name) == 0) { + char *src_data = (char*)src_block + source->layers[src_i].offset; + char *dest_data = (char*)*dest_block + dest->layers[dest_i].offset; + + typeInfo = BME_layerType_getInfo(source->layers[src_i].type); + + if(typeInfo->copy) + typeInfo->copy(src_data, dest_data, 1); + else + memcpy(dest_data, src_data, typeInfo->size); + + /* if there are multiple source & dest layers of the same type, + * we don't want to copy all source layers to the same dest, so + * increment dest_i + */ + ++dest_i; + } + } +} +void BME_CD_set_default(BME_CustomData *data, void **block) +{ + const BME_LayerTypeInfo *typeInfo; + int i; + + if (!*block) + BME_CD_alloc_block(data, block); //for addXXXlist functions... + + for(i = 0; i < data->totlayer; ++i) { + int offset = data->layers[i].offset; + + typeInfo = BME_layerType_getInfo(data->layers[i].type); + + if(typeInfo->set_default) + typeInfo->set_default((char*)*block + offset, 1); + } +} diff --git a/source/blender/blenkernel/intern/BME_conversions.c b/source/blender/blenkernel/intern/BME_conversions.c new file mode 100644 index 00000000000..2129042bdc2 --- /dev/null +++ b/source/blender/blenkernel/intern/BME_conversions.c @@ -0,0 +1,646 @@ +/** + * BME_mesh.c jan 2007 + * + * BMesh mesh level functions. + * + * $Id: BME_eulers.c,v 1.00 2007/01/17 17:42:01 Briggs Exp $ + * + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * about this. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * The Original Code is Copyright (C) 2007 Blender Foundation. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): Geoffrey Bantle, Levi Schooley. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#include "MEM_guardedalloc.h" +#include "BKE_customdata.h" + +#include "DNA_listBase.h" +#include "DNA_meshdata_types.h" +#include "DNA_object_types.h" +#include "DNA_scene_types.h" + +#include "BKE_utildefines.h" +#include "BKE_mesh.h" +#include "BKE_bmesh.h" +#include "BKE_global.h" +#include "BKE_DerivedMesh.h" +#include "BKE_cdderivedmesh.h" + +#include "BLI_blenlib.h" +#include "BLI_editVert.h" +#include "BLI_edgehash.h" +//XXX #include "BIF_editmesh.h" +//XXX #include "editmesh.h" +#include "bmesh_private.h" + +//XXX #include "BSE_edit.h" + +/*merge these functions*/ +static void BME_DMcorners_to_loops(BME_Mesh *bm, CustomData *facedata, int index, BME_Poly *f, int numCol, int numTex){ + int i, j; + BME_Loop *l; + MTFace *texface; + MTexPoly *texpoly; + MCol *mcol; + MLoopCol *mloopcol; + MLoopUV *mloopuv; + + for(i=0; i< numTex; i++){ + texface = CustomData_get_layer_n(facedata, CD_MTFACE, i); + texpoly = CustomData_bmesh_get_n(&bm->pdata, f->data, CD_MTEXPOLY, i); + + texpoly->tpage = texface[index].tpage; + texpoly->flag = texface[index].flag; + texpoly->transp = texface[index].transp; + texpoly->mode = texface[index].mode; + texpoly->tile = texface[index].tile; + texpoly->unwrap = texface[index].unwrap; + + j = 0; + l = f->loopbase; + do{ + mloopuv = CustomData_bmesh_get_n(&bm->ldata, l->data, CD_MLOOPUV, i); + mloopuv->uv[0] = texface[index].uv[j][0]; + mloopuv->uv[1] = texface[index].uv[j][1]; + j++; + l = l->next; + }while(l!=f->loopbase); + } + + for(i=0; i < numCol; i++){ + mcol = CustomData_get_layer_n(facedata, CD_MCOL, i); + j = 0; + l = f->loopbase; + do{ + mloopcol = CustomData_bmesh_get_n(&bm->ldata, l->data, CD_MLOOPCOL, i); + mloopcol->r = mcol[(index*4)+j].r; + mloopcol->g = mcol[(index*4)+j].g; + mloopcol->b = mcol[(index*4)+j].b; + mloopcol->a = mcol[(index*4)+j].a; + j++; + l = l->next; + }while(l!=f->loopbase); + } +} + +static void BME_DMloops_to_corners(BME_Mesh *bm, CustomData *facedata, int index, BME_Poly *f,int numCol, int numTex){ + int i, j; + BME_Loop *l; + MTFace *texface; + MTexPoly *texpoly; + MCol *mcol; + MLoopCol *mloopcol; + MLoopUV *mloopuv; + + for(i=0; i < numTex; i++){ + texface = CustomData_get_layer_n(facedata, CD_MTFACE, i); + texpoly = CustomData_bmesh_get_n(&bm->pdata, f->data, CD_MTEXPOLY, i); + + texface[index].tpage = texpoly->tpage; + texface[index].flag = texpoly->flag; + texface[index].transp = texpoly->transp; + texface[index].mode = texpoly->mode; + texface[index].tile = texpoly->tile; + texface[index].unwrap = texpoly->unwrap; + + j = 0; + l = f->loopbase; + do{ + mloopuv = CustomData_bmesh_get_n(&bm->ldata, l->data, CD_MLOOPUV, i); + texface[index].uv[j][0] = mloopuv->uv[0]; + texface[index].uv[j][1] = mloopuv->uv[1]; + j++; + l = l->next; + }while(l!=f->loopbase); + + } + for(i=0; i < numCol; i++){ + mcol = CustomData_get_layer_n(facedata,CD_MCOL, i); + j = 0; + l = f->loopbase; + do{ + mloopcol = CustomData_bmesh_get_n(&bm->ldata, l->data, CD_MLOOPCOL, i); + mcol[(index*4) + j].r = mloopcol->r; + mcol[(index*4) + j].g = mloopcol->g; + mcol[(index*4) + j].b = mloopcol->b; + mcol[(index*4) + j].a = mloopcol->a; + j++; + l = l->next; + }while(l!=f->loopbase); + } +} + + +static void BME_corners_to_loops(BME_Mesh *bm, CustomData *facedata, void *face_block, BME_Poly *f,int numCol, int numTex){ + int i, j; + BME_Loop *l; + MTFace *texface; + MTexPoly *texpoly; + MCol *mcol; + MLoopCol *mloopcol; + MLoopUV *mloopuv; + + for(i=0; i < numTex; i++){ + texface = CustomData_em_get_n(facedata, face_block, CD_MTFACE, i); + texpoly = CustomData_bmesh_get_n(&bm->pdata, f->data, CD_MTEXPOLY, i); + + texpoly->tpage = texface->tpage; + texpoly->flag = texface->flag; + texpoly->transp = texface->transp; + texpoly->mode = texface->mode; + texpoly->tile = texface->tile; + texpoly->unwrap = texface->unwrap; + + j = 0; + l = f->loopbase; + do{ + mloopuv = CustomData_bmesh_get_n(&bm->ldata, l->data, CD_MLOOPUV, i); + mloopuv->uv[0] = texface->uv[j][0]; + mloopuv->uv[1] = texface->uv[j][1]; + j++; + l = l->next; + }while(l!=f->loopbase); + + } + for(i=0; i < numCol; i++){ + mcol = CustomData_em_get_n(facedata, face_block, CD_MCOL, i); + j = 0; + l = f->loopbase; + do{ + mloopcol = CustomData_bmesh_get_n(&bm->ldata, l->data, CD_MLOOPCOL, i); + mloopcol->r = mcol[j].r; + mloopcol->g = mcol[j].g; + mloopcol->b = mcol[j].b; + mloopcol->a = mcol[j].a; + j++; + l = l->next; + }while(l!=f->loopbase); + } +} + +static void BME_loops_to_corners(BME_Mesh *bm, CustomData *facedata, void *face_block, BME_Poly *f,int numCol, int numTex){ + int i, j; + BME_Loop *l; + MTFace *texface; + MTexPoly *texpoly; + MCol *mcol; + MLoopCol *mloopcol; + MLoopUV *mloopuv; + + for(i=0; i < numTex; i++){ + texface = CustomData_em_get_n(facedata, face_block, CD_MTFACE, i); + texpoly = CustomData_bmesh_get_n(&bm->pdata, f->data, CD_MTEXPOLY, i); + + texface->tpage = texpoly->tpage; + texface->flag = texpoly->flag; + texface->transp = texpoly->transp; + texface->mode = texpoly->mode; + texface->tile = texpoly->tile; + texface->unwrap = texpoly->unwrap; + + j = 0; + l = f->loopbase; + do{ + mloopuv = CustomData_bmesh_get_n(&bm->ldata, l->data, CD_MLOOPUV, i); + texface->uv[j][0] = mloopuv->uv[0]; + texface->uv[j][1] = mloopuv->uv[1]; + j++; + l = l->next; + }while(l!=f->loopbase); + + } + for(i=0; i < numCol; i++){ + mcol = CustomData_em_get_n(facedata, face_block, CD_MCOL, i); + j = 0; + l = f->loopbase; + do{ + mloopcol = CustomData_bmesh_get_n(&bm->ldata, l->data, CD_MLOOPCOL, i); + mcol[j].r = mloopcol->r; + mcol[j].g = mloopcol->g; + mcol[j].b = mloopcol->b; + mcol[j].a = mloopcol->a; + j++; + l = l->next; + }while(l!=f->loopbase); + } +} +/*move the EditMesh conversion functions to editmesh_tools.c*/ +BME_Mesh *BME_editmesh_to_bmesh(EditMesh *em) { + BME_Mesh *bm; + int allocsize[4] = {512,512,2048,512}, numTex, numCol; + BME_Vert *v1, *v2; + BME_Edge *e, *edar[4]; + BME_Poly *f; + + EditVert *eve; + EditEdge *eed; + EditFace *efa; + + int len; + bm = BME_make_mesh(allocsize); + + /*copy custom data layout*/ + CustomData_copy(&em->vdata, &bm->vdata, CD_MASK_BMESH, CD_CALLOC, 0); + CustomData_copy(&em->edata, &bm->edata, CD_MASK_BMESH, CD_CALLOC, 0); + CustomData_copy(&em->fdata, &bm->pdata, CD_MASK_BMESH, CD_CALLOC, 0); + + /*copy face corner data*/ + CustomData_to_bmeshpoly(&em->fdata, &bm->pdata, &bm->ldata); + /*initialize memory pools*/ + CustomData_bmesh_init_pool(&bm->vdata, allocsize[0]); + CustomData_bmesh_init_pool(&bm->edata, allocsize[1]); + CustomData_bmesh_init_pool(&bm->ldata, allocsize[2]); + CustomData_bmesh_init_pool(&bm->pdata, allocsize[3]); + /*needed later*/ + numTex = CustomData_number_of_layers(&bm->pdata, CD_MTEXPOLY); + numCol = CustomData_number_of_layers(&bm->ldata, CD_MLOOPCOL); + + BME_model_begin(bm); + /*add verts*/ + eve= em->verts.first; + while(eve) { + v1 = BME_MV(bm,eve->co); + VECCOPY(v1->no,eve->no); + v1->flag = eve->f; + v1->h = eve->h; + v1->bweight = eve->bweight; + /*Copy Custom Data*/ + CustomData_bmesh_copy_data(&em->vdata, &bm->vdata, eve->data, &v1->data); + eve->tmp.v = (EditVert*)v1; + eve = eve->next; + } + + /*add edges*/ + eed= em->edges.first; + while(eed) { + v1 = (BME_Vert*)eed->v1->tmp.v; + v2 = (BME_Vert*)eed->v2->tmp.v; + e = BME_ME(bm, v1, v2); + e->crease = eed->crease; + e->bweight = eed->bweight; + e->flag = eed->f & SELECT; + if(eed->sharp) e->flag |= ME_SHARP; + if(eed->seam) e->flag |= ME_SEAM; + //XXX if(eed->h & EM_FGON) e->flag |= ME_FGON; + if(eed->h & 1) e->flag |= ME_HIDE; + eed->tmp.e = (EditEdge*)e; + CustomData_bmesh_copy_data(&em->edata, &bm->edata, eed->data, &e->data); + eed = eed->next; + } + /*add faces.*/ + efa= em->faces.first; + while(efa) { + if(efa->v4) len = 4; + else len = 3; + + edar[0] = (BME_Edge*)efa->e1->tmp.e; + edar[1] = (BME_Edge*)efa->e2->tmp.e; + edar[2] = (BME_Edge*)efa->e3->tmp.e; + if(len == 4){ + edar[3] = (BME_Edge*)efa->e4->tmp.e; + } + + /*find v1 and v2*/ + v1 = (BME_Vert*)efa->v1->tmp.v; + v2 = (BME_Vert*)efa->v2->tmp.v; + + f = BME_MF(bm,v1,v2,edar,len); + f->mat_nr = efa->mat_nr; + f->flag = efa->flag; + if(efa->h) { + f->flag |= ME_HIDE; + f->flag &= ~ME_FACE_SEL; + } + else { + if(efa->f & 1) f->flag |= ME_FACE_SEL; + else f->flag &= ~ME_FACE_SEL; + } + CustomData_bmesh_copy_data(&em->fdata, &bm->pdata, efa->data, &f->data); + BME_corners_to_loops(bm, &em->fdata, efa->data, f,numCol,numTex); + efa = efa->next; + } + BME_model_end(bm); + return bm; +} +/* adds the geometry in the bmesh to G.editMesh (does not free G.editMesh) + * if td != NULL, the transdata will be mapped to the EditVert's co */ +EditMesh *BME_bmesh_to_editmesh(BME_Mesh *bm, BME_TransData_Head *td) { + BME_Vert *v1; + BME_Edge *e; + BME_Poly *f; + + BME_TransData *vtd; + + EditMesh *em; + EditVert *eve1, *eve2, *eve3, *eve4, **evlist; + EditEdge *eed; + EditFace *efa; + + int totvert, len, i, numTex, numCol; + + em = G.editMesh; + + if (em == NULL) return NULL; + + + CustomData_copy(&bm->vdata, &em->vdata, CD_MASK_BMESH, CD_CALLOC, 0); + CustomData_copy(&bm->edata, &em->edata, CD_MASK_BMESH, CD_CALLOC, 0); + CustomData_copy(&bm->pdata, &em->fdata, CD_MASK_BMESH, CD_CALLOC, 0); + CustomData_from_bmeshpoly(&em->fdata, &bm->pdata, &bm->ldata,0); + numTex = CustomData_number_of_layers(&bm->pdata, CD_MTEXPOLY); + numCol = CustomData_number_of_layers(&bm->ldata, CD_MLOOPCOL); + + + /* convert to EditMesh */ + /* make editverts */ + totvert = BLI_countlist(&(bm->verts)); + evlist= (EditVert **)MEM_mallocN(totvert*sizeof(void *),"evlist"); + for (i=0,v1=bm->verts.first;v1;v1=v1->next,i++) { + v1->tflag1 = i; + eve1 = NULL; //XXX addvertlist(v1->co,NULL); + if (td && (vtd = BME_get_transdata(td,v1))) { + vtd->loc = eve1->co; + } + eve1->keyindex = i; + evlist[i]= eve1; + eve1->f = (unsigned char)v1->flag; + eve1->h = (unsigned char)v1->h; + eve1->bweight = v1->bweight; + CustomData_em_copy_data(&bm->vdata, &em->vdata, v1->data, &eve1->data); + } + + /* make edges */ + for (e=bm->edges.first;e;e=e->next) { + if(0) { //XXX if(!(findedgelist(evlist[e->v1->tflag1], evlist[e->v2->tflag1]))){ + eed= NULL; //XXX addedgelist(evlist[e->v1->tflag1], evlist[e->v2->tflag1], NULL); + eed->crease = e->crease; + eed->bweight = e->bweight; + if(e->flag & ME_SEAM) eed->seam = 1; + if(e->flag & ME_SHARP) eed->sharp = 1; + if(e->flag & SELECT) eed->f |= SELECT; + //XXX if(e->flag & ME_FGON) eed->h= EM_FGON; // 2 different defines! + if(e->flag & ME_HIDE) eed->h |= 1; + if(G.scene->selectmode==SCE_SELECT_EDGE) + ; //XXX EM_select_edge(eed, eed->f & SELECT); + + CustomData_em_copy_data(&bm->edata, &em->edata, e->data, &eed->data); + } + } + + /* make faces */ + for (f=bm->polys.first;f;f=f->next) { + len = BME_cycle_length(f->loopbase); + if (len==3 || len==4) { + eve1= evlist[f->loopbase->v->tflag1]; + eve2= evlist[f->loopbase->next->v->tflag1]; + eve3= evlist[f->loopbase->next->next->v->tflag1]; + if (len == 4) { + eve4= evlist[f->loopbase->prev->v->tflag1]; + } + else { + eve4= NULL; + } + + efa = NULL; //XXX addfacelist(eve1, eve2, eve3, eve4, NULL, NULL); + efa->mat_nr = (unsigned char)f->mat_nr; + efa->flag= f->flag & ~ME_HIDE; + if(f->flag & ME_FACE_SEL) { + efa->f |= SELECT; + } + if(f->flag & ME_HIDE) efa->h= 1; + if((G.f & G_FACESELECT) && (efa->f & SELECT)) + ; //XXX EM_select_face(efa, 1); /* flush down */ + CustomData_em_copy_data(&bm->pdata, &em->fdata, f->data, &efa->data); + BME_loops_to_corners(bm, &em->fdata, efa->data, f,numCol,numTex); + } + } + + MEM_freeN(evlist); + + //XXX countall(); + + return em; +} + +/* Adds the geometry found in dm to bm + */ +BME_Mesh *BME_derivedmesh_to_bmesh(DerivedMesh *dm) +{ + + BME_Mesh *bm; + int allocsize[4] = {512,512,2048,512}; + MVert *mvert, *mv; + MEdge *medge, *me; + MFace *mface, *mf; + int totface,totedge,totvert,i,len, numTex, numCol; + BME_Vert *v1=NULL,*v2=NULL, **vert_array; + BME_Edge *e=NULL; + BME_Poly *f=NULL; + + EdgeHash *edge_hash = BLI_edgehash_new(); + + bm = BME_make_mesh(allocsize); + /*copy custom data layout*/ + CustomData_copy(&dm->vertData, &bm->vdata, CD_MASK_BMESH, CD_CALLOC, 0); + CustomData_copy(&dm->edgeData, &bm->edata, CD_MASK_BMESH, CD_CALLOC, 0); + CustomData_copy(&dm->faceData, &bm->pdata, CD_MASK_BMESH, CD_CALLOC, 0); + + /*copy face corner data*/ + CustomData_to_bmeshpoly(&dm->faceData, &bm->pdata, &bm->ldata); + /*initialize memory pools*/ + CustomData_bmesh_init_pool(&bm->vdata, allocsize[0]); + CustomData_bmesh_init_pool(&bm->edata, allocsize[1]); + CustomData_bmesh_init_pool(&bm->ldata, allocsize[2]); + CustomData_bmesh_init_pool(&bm->pdata, allocsize[3]); + /*needed later*/ + numTex = CustomData_number_of_layers(&bm->pdata, CD_MTEXPOLY); + numCol = CustomData_number_of_layers(&bm->ldata, CD_MLOOPCOL); + + totvert = dm->getNumVerts(dm); + totedge = dm->getNumEdges(dm); + totface = dm->getNumFaces(dm); + mvert = dm->getVertArray(dm); + medge = dm->getEdgeArray(dm); + mface = dm->getFaceArray(dm); + + vert_array = MEM_mallocN(sizeof(*vert_array)*totvert,"BME_derivedmesh_to_bmesh BME_Vert* array"); + + BME_model_begin(bm); + /*add verts*/ + for(i=0,mv = mvert; i < totvert;i++,mv++){ + v1 = BME_MV(bm,mv->co); + vert_array[i] = v1; + v1->flag = mv->flag; + v1->bweight = mv->bweight/255.0f; + CustomData_to_bmesh_block(&dm->vertData, &bm->vdata, i, &v1->data); + } + /*add edges*/ + for(i=0,me = medge; i < totedge;i++,me++){ + v1 = vert_array[me->v1]; + v2 = vert_array[me->v2]; + e = BME_ME(bm, v1, v2); + e->crease = me->crease/255.0f; + e->bweight = me->bweight/255.0f; + e->flag = (unsigned char)me->flag; + BLI_edgehash_insert(edge_hash,me->v1,me->v2,e); + CustomData_to_bmesh_block(&dm->edgeData, &bm->edata, i, &e->data); + } + /*add faces.*/ + for(i=0,mf = mface; i < totface;i++,mf++){ + BME_Edge *edar[4]; + if(mf->v4) len = 4; + else len = 3; + + edar[0] = BLI_edgehash_lookup(edge_hash,mf->v1,mf->v2); + edar[1] = BLI_edgehash_lookup(edge_hash,mf->v2,mf->v3); + if(len == 4){ + edar[2] = BLI_edgehash_lookup(edge_hash,mf->v3,mf->v4); + edar[3] = BLI_edgehash_lookup(edge_hash,mf->v4,mf->v1); + } + else + edar[2] = BLI_edgehash_lookup(edge_hash,mf->v3,mf->v1); + + /*find v1 and v2*/ + v1 = vert_array[mf->v1]; + v2 = vert_array[mf->v2]; + + f = BME_MF(bm,v1,v2,edar,len); + f->mat_nr = mf->mat_nr; + f->flag = mf->flag; + CustomData_to_bmesh_block(&dm->faceData,&bm->pdata,i,&f->data); + BME_DMcorners_to_loops(bm, &dm->faceData,i,f, numCol,numTex); + } + + BME_model_end(bm); + BLI_edgehash_free(edge_hash, NULL); + MEM_freeN(vert_array); + return bm; +} + +DerivedMesh *BME_bmesh_to_derivedmesh(BME_Mesh *bm, DerivedMesh *dm) +{ + MFace *mface, *mf; + MEdge *medge, *me; + MVert *mvert, *mv; + int totface,totedge,totvert,i,bmeshok,len, numTex, numCol; + + BME_Vert *v1=NULL; + BME_Edge *e=NULL, *oe=NULL; + BME_Poly *f=NULL; + + DerivedMesh *result; + EdgeHash *edge_hash = BLI_edgehash_new(); + + totvert = BLI_countlist(&(bm->verts)); + totedge = 0; + + /*we cannot have double edges in a derived mesh!*/ + for(i=0, v1=bm->verts.first; v1; v1=v1->next, i++) v1->tflag1 = i; + for(e=bm->edges.first; e; e=e->next){ + oe = BLI_edgehash_lookup(edge_hash,e->v1->tflag1, e->v2->tflag1); + if(!oe){ + totedge++; + BLI_edgehash_insert(edge_hash,e->v1->tflag1,e->v2->tflag1,e); + e->tflag2 = 1; + } + else{ + e->tflag2 = 0; + } + } + + /*count quads and tris*/ + totface = 0; + bmeshok = 1; + for(f=bm->polys.first;f;f=f->next){ + len = BME_cycle_length(f->loopbase); + if(len == 3 || len == 4) totface++; + } + + /*convert back to mesh*/ + result = CDDM_from_template(dm,totvert,totedge,totface); + CustomData_merge(&bm->vdata, &result->vertData, CD_MASK_BMESH, CD_CALLOC, totvert); + CustomData_merge(&bm->edata, &result->edgeData, CD_MASK_BMESH, CD_CALLOC, totedge); + CustomData_merge(&bm->pdata, &result->faceData, CD_MASK_BMESH, CD_CALLOC, totface); + CustomData_from_bmeshpoly(&result->faceData, &bm->pdata, &bm->ldata,totface); + numTex = CustomData_number_of_layers(&bm->pdata, CD_MTEXPOLY); + numCol = CustomData_number_of_layers(&bm->ldata, CD_MLOOPCOL); + + + /*Make Verts*/ + mvert = CDDM_get_verts(result); + for(i=0,v1=bm->verts.first,mv=mvert;v1;v1=v1->next,i++,mv++){ + VECCOPY(mv->co,v1->co); + mv->flag = (unsigned char)v1->flag; + mv->bweight = (char)(255.0*v1->bweight); + CustomData_from_bmesh_block(&bm->vdata, &result->vertData, &v1->data, i); + } + medge = CDDM_get_edges(result); + i=0; + for(e=bm->edges.first,me=medge;e;e=e->next){ + if(e->tflag2){ + if(e->v1->tflag1 < e->v2->tflag1){ + me->v1 = e->v1->tflag1; + me->v2 = e->v2->tflag1; + } + else{ + me->v1 = e->v2->tflag1; + me->v2 = e->v1->tflag1; + } + + me->crease = (char)(255.0*e->crease); + me->bweight = (char)(255.0*e->bweight); + me->flag = e->flag; + CustomData_from_bmesh_block(&bm->edata, &result->edgeData, &e->data, i); + me++; + i++; + } + } + if(totface){ + mface = CDDM_get_faces(result); + /*make faces*/ + for(i=0,f=bm->polys.first;f;f=f->next){ + mf = &mface[i]; + len = BME_cycle_length(f->loopbase); + if(len==3 || len==4){ + mf->v1 = f->loopbase->v->tflag1; + mf->v2 = f->loopbase->next->v->tflag1; + mf->v3 = f->loopbase->next->next->v->tflag1; + if(len == 4){ + mf->v4 = f->loopbase->prev->v->tflag1; + } + /* test and rotate indexes if necessary so that verts 3 and 4 aren't index 0 */ + if(mf->v3 == 0 || (len == 4 && mf->v4 == 0)){ + test_index_face(mf, NULL, i, len); + } + mf->mat_nr = (unsigned char)f->mat_nr; + mf->flag = (unsigned char)f->flag; + CustomData_from_bmesh_block(&bm->pdata, &result->faceData, &f->data, i); + BME_DMloops_to_corners(bm, &result->faceData, i, f,numCol,numTex); + i++; + } + } + } + BLI_edgehash_free(edge_hash, NULL); + return result; +} diff --git a/source/blender/blenkernel/intern/BME_eulers.c b/source/blender/blenkernel/intern/BME_eulers.c new file mode 100644 index 00000000000..801e0b8bdec --- /dev/null +++ b/source/blender/blenkernel/intern/BME_eulers.c @@ -0,0 +1,977 @@ +/** + * BME_eulers.c jan 2007 + * + * BMesh Euler construction API. + * + * $Id: BME_eulers.c,v 1.00 2007/01/17 17:42:01 Briggs Exp $ + * + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * about this. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * The Original Code is Copyright (C) 2004 Blender Foundation. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): Geoffrey Bantle. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#include "MEM_guardedalloc.h" + +#include "DNA_listBase.h" +#include "DNA_meshdata_types.h" +#include "DNA_mesh_types.h" + +#include "BKE_utildefines.h" +#include "BKE_customdata.h" +#include "BKE_bmesh.h" + +#include "BLI_blenlib.h" +#include "bmesh_private.h" +#include "BLI_ghash.h" + +/********************************************************* + * "Euler API" * + * * + * * + * Primitive construction operators for mesh tools. * + * * + **********************************************************/ + + +/* + The functions in this file represent the 'primitive' or 'atomic' operators that + mesh tools use to manipulate the topology of the structure.* The purpose of these + functions is to provide a trusted set of operators to manipulate the mesh topology + and which can also be combined together like building blocks to create more + sophisticated tools. It needs to be stressed that NO manipulation of an existing + mesh structure should be done outside of these functions. + + In the BMesh system, each euler is named by an ancronym which describes what it actually does. + Furthermore each Euler has a logical inverse. An important design criteria of all Eulers is that + through a Euler's logical inverse you can 'undo' an operation. (Special note should + be taken of BME_loop_reverse, which is its own inverse). + + BME_MF/KF: Make Face and Kill Face + BME_ME/KE: Make Edge and Kill Edge + BME_MV/KV: Make Vert and Kill Vert + BME_SEMV/JEKV: Split Edge, Make Vert and Join Edge, Kill Vert + BME_SFME/JFKE: Split Face, Make Edge and Join Face, Kill Edge + BME_loop_reverse: Reverse a Polygon's loop cycle. (used for flip normals for one) + + Using a combination of these eleven eulers any non-manifold modelling operation can be achieved. + Each Euler operator has a detailed explanation of what is does in the comments preceding its + code. + + *The term "Euler Operator" is actually a misnomer when referring to a non-manifold + data structure. Its use is in keeping with the convention established by others. + + TODO: + -Finish inserting 'strict' validation in all Eulers +*/ + +void *BME_exit(char *s) { + if (s) printf("%s\n",s); + return NULL; +} + +#define RETCLEAR(bm) {bm->rval->v = bm->rval->e = bm->rval->f = bm->rva->l = NULL;} +/*MAKE Eulers*/ + +/** + * BME_MV + * + * MAKE VERT EULER: + * + * Makes a single loose vertex. + * + * Returns - + * A BME_Vert pointer. + */ + +BME_Vert *BME_MV(BME_Mesh *bm, float *vec){ + BME_Vert *v = BME_addvertlist(bm, NULL); + VECCOPY(v->co,vec); + return v; +} + +/** + * BME_ME + * + * MAKE EDGE EULER: + * + * Makes a single wire edge between two vertices. + * If the caller does not want there to be duplicate + * edges between the vertices, it is up to them to check + * for this condition beforehand. + * + * Returns - + * A BME_Edge pointer. + */ + +BME_Edge *BME_ME(BME_Mesh *bm, BME_Vert *v1, BME_Vert *v2){ + BME_Edge *e=NULL; + BME_CycleNode *d1=NULL, *d2=NULL; + int valance1=0, valance2=0, edok; + + /*edge must be between two distinct vertices...*/ + if(v1 == v2) return NULL; + + #ifndef BME_FASTEULER + /*count valance of v1*/ + if(v1->edge){ + d1 = BME_disk_getpointer(v1->edge,v1); + if(d1) valance1 = BME_cycle_length(d1); + else BME_error(); + } + if(v2->edge){ + d2 = BME_disk_getpointer(v2->edge,v2); + if(d2) valance2 = BME_cycle_length(d2); + else BME_error(); + } + #endif + + /*go ahead and add*/ + e = BME_addedgelist(bm, v1, v2, NULL); + BME_disk_append_edge(e, e->v1); + BME_disk_append_edge(e, e->v2); + + #ifndef BME_FASTEULER + /*verify disk cycle lengths*/ + d1 = BME_disk_getpointer(e, e->v1); + edok = BME_cycle_validate(valance1+1, d1); + if(!edok) BME_error(); + d2 = BME_disk_getpointer(e, e->v2); + edok = BME_cycle_validate(valance2+1, d2); + if(!edok) BME_error(); + + /*verify that edge actually made it into the cycle*/ + edok = BME_disk_hasedge(v1, e); + if(!edok) BME_error(); + edok = BME_disk_hasedge(v2, e); + if(!edok) BME_error(); + #endif + return e; +} + + + +/** + * BME_MF + * + * MAKE FACE EULER: + * Takes a list of edge pointers which form a closed loop and makes a face + * from them. The first edge in elist is considered to be the start of the + * polygon, and v1 and v2 are its vertices and determine the winding of the face + * Other than the first edge, no other assumptions are made about the order of edges + * in the elist array. To verify that it is a single closed loop and derive the correct + * order a simple series of verifications is done and all elements are visited. + * + * Returns - + * A BME_Poly pointer + */ + +#define MF_CANDIDATE 1 +#define MF_VISITED 2 +#define MF_TAKEN 4 + +BME_Poly *BME_MF(BME_Mesh *bm, BME_Vert *v1, BME_Vert *v2, BME_Edge **elist, int len) +{ + BME_Poly *f = NULL; + BME_Edge *curedge; + BME_Vert *curvert, *tv, **vlist; + int i, j, done, cont, edok; + + if(len < 2) return NULL; + + /*make sure that v1 and v2 are in elist[0]*/ + if(BME_verts_in_edge(v1,v2,elist[0]) == 0) return NULL; + + /*clear euler flags*/ + for(i=0;i<len;i++) elist[i]->eflag1=elist[i]->eflag2 = 0; + for(i=0;i<len;i++){ + elist[i]->eflag1 |= MF_CANDIDATE; + + /*if elist[i] has a loop, count its radial length*/ + if(elist[i]->loop) elist[i]->eflag2 = BME_cycle_length(&(elist[i]->loop->radial)); + else elist[i]->eflag2 = 0; + } + + /* For each vertex in each edge, it must have exactly two MF_CANDIDATE edges attached to it + Note that this does not gauruntee that face is a single closed loop. At best it gauruntees + that elist contains a finite number of seperate closed loops. + */ + for(i=0; i<len; i++){ + edok = BME_disk_count_edgeflag(elist[i]->v1, MF_CANDIDATE, 0); + if(edok != 2) return NULL; + edok = BME_disk_count_edgeflag(elist[i]->v2, MF_CANDIDATE, 0); + if(edok != 2) return NULL; + } + + /*set start edge, start vert and target vert for our loop traversal*/ + curedge = elist[0]; + tv = v1; + curvert = v2; + + if(bm->vtarlen < len){ + MEM_freeN(bm->vtar); + bm->vtar = MEM_callocN(sizeof(BME_Vert *)* len, "BMesh Vert pointer array"); + bm->vtarlen = len; + } + /*insert tv into vlist since its the first vertex in face*/ + i=0; + vlist=bm->vtar; + vlist[i] = tv; + + /* Basic procedure: Starting with curv we find the edge in it's disk cycle which hasn't + been visited yet. When we do, we put curv in a linked list and find the next MF_CANDIDATE + edge, loop until we find TV. We know TV is reachable because of test we did earlier. + */ + done=0; + while(!done){ + /*add curvert to vlist*/ + /*insert some error cheking here for overflows*/ + i++; + vlist[i] = curvert; + + /*mark curedge as visited*/ + curedge->eflag1 |= MF_VISITED; + + /*find next edge and vert*/ + curedge = BME_disk_next_edgeflag(curedge, curvert, MF_CANDIDATE, 0); + curvert = BME_edge_getothervert(curedge, curvert); + if(curvert == tv){ + curedge->eflag1 |= MF_VISITED; + done=1; + } + } + + /* Verify that all edges have been visited It's possible that we did reach tv + from sv, but that several unconnected loops were passed in via elist. + */ + cont=1; + for(i=0; i<len; i++){ + if((elist[i]->eflag1 & MF_VISITED) == 0) cont = 0; + } + + /*if we get this far, its ok to allocate the face and add the loops*/ + if(cont){ + BME_Loop *l; + BME_Edge *e; + f = BME_addpolylist(bm, NULL); + f->len = len; + for(i=0;i<len;i++){ + curvert = vlist[i]; + l = BME_create_loop(bm,curvert,NULL,f,NULL); + if(!(f->loopbase)) f->loopbase = l; + BME_cycle_append(f->loopbase, l); + } + + /*take care of edge pointers and radial cycle*/ + for(i=0, l = f->loopbase; i<len; i++, l=l->next){ + e = NULL; + if(l == f->loopbase) e = elist[0]; /*first edge*/ + + else{/*search elist for others*/ + for(j=1; j<len; j++){ + edok = BME_verts_in_edge(l->v, l->next->v, elist[j]); + if(edok){ + e = elist[j]; + break; + } + } + } + l->e = e; /*set pointer*/ + BME_radial_append(e, l); /*append into radial*/ + } + + f->len = len; + + /*Validation Loop cycle*/ + edok = BME_cycle_validate(len, f->loopbase); + if(!edok) BME_error(); + for(i=0, l = f->loopbase; i<len; i++, l=l->next){ + /*validate loop vert pointers*/ + edok = BME_verts_in_edge(l->v, l->next->v, l->e); + if(!edok) BME_error(); + /*validate the radial cycle of each edge*/ + edok = BME_cycle_length(&(l->radial)); + if(edok != (l->e->eflag2 + 1)) BME_error(); + } + } + return f; +} + +/* KILL Eulers */ + +/** + * BME_KV + * + * KILL VERT EULER: + * + * Kills a single loose vertex. + * + * Returns - + * 1 for success, 0 for failure. + */ + +int BME_KV(BME_Mesh *bm, BME_Vert *v){ + if(v->edge == NULL){ + BLI_remlink(&(bm->verts), v); + BME_free_vert(bm,v); + return 1; + } + return 0; +} + +/** + * BME_KE + * + * KILL EDGE EULER: + * + * Kills a wire edge. + * + * Returns - + * 1 for success, 0 for failure. + */ + +int BME_KE(BME_Mesh *bm, BME_Edge *e){ + int edok; + + /*Make sure that no faces!*/ + if(e->loop == NULL){ + BME_disk_remove_edge(e, e->v1); + BME_disk_remove_edge(e, e->v2); + + /*verify that edge out of disk*/ + edok = BME_disk_hasedge(e->v1, e); + if(edok) BME_error(); + edok = BME_disk_hasedge(e->v2, e); + if(edok) BME_error(); + + /*remove and deallocate*/ + BLI_remlink(&(bm->edges), e); + BME_free_edge(bm, e); + return 1; + } + return 0; +} + +/** + * BME_KF + * + * KILL FACE EULER: + * + * The logical inverse of BME_MF. + * Kills a face and removes each of its loops from the radial that it belongs to. + * + * Returns - + * 1 for success, 0 for failure. +*/ + +int BME_KF(BME_Mesh *bm, BME_Poly *bply){ + BME_Loop *newbase,*oldbase, *curloop; + int i,len=0; + + /*add validation to make sure that radial cycle is cleaned up ok*/ + /*deal with radial cycle first*/ + len = BME_cycle_length(bply->loopbase); + for(i=0, curloop=bply->loopbase; i < len; i++, curloop = curloop->next) + BME_radial_remove_loop(curloop, curloop->e); + + /*now deallocate the editloops*/ + for(i=0; i < len; i++){ + newbase = bply->loopbase->next; + oldbase = bply->loopbase; + BME_cycle_remove(oldbase, oldbase); + BME_free_loop(bm, oldbase); + bply->loopbase = newbase; + } + + BLI_remlink(&(bm->polys), bply); + BME_free_poly(bm, bply); + return 1; +} + +/*SPLIT Eulers*/ + +/** + * BME_SEMV + * + * SPLIT EDGE MAKE VERT: + * Takes a given edge and splits it into two, creating a new vert. + * + * + * Before: OV---------TV + * After: OV----NV---TV + * + * Returns - + * BME_Vert pointer. + * +*/ + +BME_Vert *BME_SEMV(BME_Mesh *bm, BME_Vert *tv, BME_Edge *e, BME_Edge **re){ + BME_Vert *nv, *ov; + BME_CycleNode *diskbase; + BME_Edge *ne; + int i, edok, valance1=0, valance2=0; + + if(BME_vert_in_edge(e,tv) == 0) return NULL; + ov = BME_edge_getothervert(e,tv); + //v2 = tv; + + /*count valance of v1*/ + diskbase = BME_disk_getpointer(e, ov); + valance1 = BME_cycle_length(diskbase); + /*count valance of v2*/ + diskbase = BME_disk_getpointer(e, tv); + valance2 = BME_cycle_length(diskbase); + + nv = BME_addvertlist(bm, tv); + ne = BME_addedgelist(bm, nv, tv, e); + + //e->v2 = nv; + /*remove e from v2's disk cycle*/ + BME_disk_remove_edge(e, tv); + /*swap out tv for nv in e*/ + BME_edge_swapverts(e, tv, nv); + /*add e to nv's disk cycle*/ + BME_disk_append_edge(e, nv); + /*add ne to nv's disk cycle*/ + BME_disk_append_edge(ne, nv); + /*add ne to tv's disk cycle*/ + BME_disk_append_edge(ne, tv); + /*verify disk cycles*/ + diskbase = BME_disk_getpointer(ov->edge,ov); + edok = BME_cycle_validate(valance1, diskbase); + if(!edok) BME_error(); + diskbase = BME_disk_getpointer(tv->edge,tv); + edok = BME_cycle_validate(valance2, diskbase); + if(!edok) BME_error(); + diskbase = BME_disk_getpointer(nv->edge,nv); + edok = BME_cycle_validate(2, diskbase); + if(!edok) BME_error(); + + /*Split the radial cycle if present*/ + if(e->loop){ + BME_Loop *nl,*l; + BME_CycleNode *radEBase=NULL, *radNEBase=NULL; + int radlen = BME_cycle_length(&(e->loop->radial)); + /*Take the next loop. Remove it from radial. Split it. Append to appropriate radials.*/ + while(e->loop){ + l=e->loop; + l->f->len++; + BME_radial_remove_loop(l,e); + + nl = BME_create_loop(bm,NULL,NULL,l->f,l); + nl->prev = l; + nl->next = l->next; + nl->prev->next = nl; + nl->next->prev = nl; + nl->v = nv; + + /*assign the correct edge to the correct loop*/ + if(BME_verts_in_edge(nl->v, nl->next->v, e)){ + nl->e = e; + l->e = ne; + + /*append l into ne's rad cycle*/ + if(!radNEBase){ + radNEBase = &(l->radial); + radNEBase->next = NULL; + radNEBase->prev = NULL; + } + + if(!radEBase){ + radEBase = &(nl->radial); + radEBase->next = NULL; + radEBase->prev = NULL; + } + + BME_cycle_append(radEBase,&(nl->radial)); + BME_cycle_append(radNEBase,&(l->radial)); + + } + else if(BME_verts_in_edge(nl->v,nl->next->v,ne)){ + nl->e = ne; + l->e = e; + + if(!radNEBase){ + radNEBase = &(nl->radial); + radNEBase->next = NULL; + radNEBase->prev = NULL; + } + if(!radEBase){ + radEBase = &(l->radial); + radEBase->next = NULL; + radEBase->prev = NULL; + } + BME_cycle_append(radEBase,&(l->radial)); + BME_cycle_append(radNEBase,&(nl->radial)); + } + + } + + e->loop = radEBase->data; + ne->loop = radNEBase->data; + + /*verify length of radial cycle*/ + edok = BME_cycle_validate(radlen,&(e->loop->radial)); + if(!edok) BME_error(); + edok = BME_cycle_validate(radlen,&(ne->loop->radial)); + if(!edok) BME_error(); + + /*verify loop->v and loop->next->v pointers for e*/ + for(i=0,l=e->loop; i < radlen; i++, l = l->radial.next->data){ + if(!(l->e == e)) BME_error(); + if(!(l->radial.data == l)) BME_error(); + if(l->prev->e != ne && l->next->e != ne) BME_error(); + edok = BME_verts_in_edge(l->v, l->next->v, e); + if(!edok) BME_error(); + if(l->v == l->next->v) BME_error(); + if(l->e == l->next->e) BME_error(); + /*verify loop cycle for kloop->f*/ + edok = BME_cycle_validate(l->f->len, l->f->loopbase); + if(!edok) BME_error(); + } + /*verify loop->v and loop->next->v pointers for ne*/ + for(i=0,l=ne->loop; i < radlen; i++, l = l->radial.next->data){ + if(!(l->e == ne)) BME_error(); + if(!(l->radial.data == l)) BME_error(); + if(l->prev->e != e && l->next->e != e) BME_error(); + edok = BME_verts_in_edge(l->v, l->next->v, ne); + if(!edok) BME_error(); + if(l->v == l->next->v) BME_error(); + if(l->e == l->next->e) BME_error(); + /*verify loop cycle for kloop->f. Redundant*/ + edok = BME_cycle_validate(l->f->len, l->f->loopbase); + if(!edok) BME_error(); + } + } + + if(re) *re = ne; + return nv; +} + +/** + * BME_SFME + * + * SPLIT FACE MAKE EDGE: + * + * Takes as input two vertices in a single face. An edge is created which divides the original face + * into two distinct regions. One of the regions is assigned to the original face and it is closed off. + * The second region has a new face assigned to it. + * + * Examples: + * + * Before: After: + * ---------- ---------- + * | | | | + * | | | f1 | + * v1 f1 v2 v1======v2 + * | | | f2 | + * | | | | + * ---------- ---------- + * + * Note that the input vertices can be part of the same edge. This will result in a two edged face. + * This is desirable for advanced construction tools and particularly essential for edge bevel. Because + * of this it is up to the caller to decide what to do with the extra edge. + * + * Returns - + * A BME_Poly pointer + */ +BME_Poly *BME_SFME(BME_Mesh *bm, BME_Poly *f, BME_Vert *v1, BME_Vert *v2, BME_Loop **rl){ + + BME_Poly *f2; + BME_Loop *v1loop = NULL, *v2loop = NULL, *curloop, *f1loop=NULL, *f2loop=NULL; + BME_Edge *e; + int i, len, f1len, f2len; + + + /*verify that v1 and v2 are in face.*/ + len = BME_cycle_length(f->loopbase); + for(i = 0, curloop = f->loopbase; i < len; i++, curloop = curloop->next){ + if(curloop->v == v1) v1loop = curloop; + else if(curloop->v == v2) v2loop = curloop; + } + + if(!v1loop || !v2loop) return NULL; + + /*allocate new edge between v1 and v2*/ + e = BME_addedgelist(bm, v1, v2,NULL); + BME_disk_append_edge(e, v1); + BME_disk_append_edge(e, v2); + + f2 = BME_addpolylist(bm,f); + f1loop = BME_create_loop(bm,v2,e,f,v2loop); + f2loop = BME_create_loop(bm,v1,e,f2,v1loop); + + f1loop->prev = v2loop->prev; + f2loop->prev = v1loop->prev; + v2loop->prev->next = f1loop; + v1loop->prev->next = f2loop; + + f1loop->next = v1loop; + f2loop->next = v2loop; + v1loop->prev = f1loop; + v2loop->prev = f2loop; + + f2->loopbase = f2loop; + f->loopbase = f1loop; + + /*validate both loops*/ + /*I dont know how many loops are supposed to be in each face at this point! FIXME!*/ + + /*go through all of f2's loops and make sure they point to it properly.*/ + f2len = BME_cycle_length(f2->loopbase); + for(i=0, curloop = f2->loopbase; i < f2len; i++, curloop = curloop->next) curloop->f = f2; + + /*link up the new loops into the new edges radial*/ + BME_radial_append(e, f1loop); + BME_radial_append(e, f2loop); + + + f2->len = f2len; + + f1len = BME_cycle_length(f->loopbase); + f->len = f1len; + + if(rl) *rl = f2loop; + return f2; +} + + +/** + * BME_JEKV + * + * JOIN EDGE KILL VERT: + * Takes a an edge and pointer to one of its vertices and collapses + * the edge on that vertex. + * + * Before: OE KE + * ------- ------- + * | || | + * OV KV TV + * + * + * After: OE + * --------------- + * | | + * OV TV + * + * + * Restrictions: + * KV is a vertex that must have a valance of exactly two. Furthermore + * both edges in KV's disk cycle (OE and KE) must be unique (no double + * edges). + * + * It should also be noted that this euler has the possibility of creating + * faces with just 2 edges. It is up to the caller to decide what to do with + * these faces. + * + * Returns - + * 1 for success, 0 for failure. + */ +int BME_JEKV(BME_Mesh *bm, BME_Edge *ke, BME_Vert *kv) +{ + BME_Edge *oe; + BME_Vert *ov, *tv; + BME_CycleNode *diskbase; + BME_Loop *killoop,*nextl; + int len,radlen=0, halt = 0, i, valance1, valance2,edok; + + if(BME_vert_in_edge(ke,kv) == 0) return 0; + diskbase = BME_disk_getpointer(kv->edge, kv); + len = BME_cycle_length(diskbase); + + if(len == 2){ + oe = BME_disk_nextedge(ke, kv); + tv = BME_edge_getothervert(ke, kv); + ov = BME_edge_getothervert(oe, kv); + halt = BME_verts_in_edge(kv, tv, oe); //check for double edges + + if(halt) return 0; + else{ + + /*For verification later, count valance of ov and tv*/ + diskbase = BME_disk_getpointer(ov->edge, ov); + valance1 = BME_cycle_length(diskbase); + diskbase = BME_disk_getpointer(tv->edge, tv); + valance2 = BME_cycle_length(diskbase); + + /*remove oe from kv's disk cycle*/ + BME_disk_remove_edge(oe,kv); + /*relink oe->kv to be oe->tv*/ + BME_edge_swapverts(oe, kv, tv); + /*append oe to tv's disk cycle*/ + BME_disk_append_edge(oe, tv); + /*remove ke from tv's disk cycle*/ + BME_disk_remove_edge(ke, tv); + + + + /*deal with radial cycle of ke*/ + if(ke->loop){ + /*first step, fix the neighboring loops of all loops in ke's radial cycle*/ + radlen = BME_cycle_length(&(ke->loop->radial)); + for(i=0,killoop = ke->loop; i<radlen; i++, killoop = BME_radial_nextloop(killoop)){ + /*relink loops and fix vertex pointer*/ + killoop->next->prev = killoop->prev; + killoop->prev->next = killoop->next; + if(killoop->next->v == kv) killoop->next->v = tv; + + /*fix len attribute of face*/ + killoop->f->len--; + if(killoop->f->loopbase == killoop) killoop->f->loopbase = killoop->next; + } + /*second step, remove all the hanging loops attached to ke*/ + killoop = ke->loop; + radlen = BME_cycle_length(&(ke->loop->radial)); + /*make sure we have enough room in bm->lpar*/ + if(bm->lparlen < radlen){ + MEM_freeN(bm->lpar); + bm->lpar = MEM_callocN(sizeof(BME_Loop *)* radlen, "BMesh Loop pointer array"); + bm->lparlen = bm->lparlen * radlen; + } + /*this should be wrapped into a bme_free_radial function to be used by BME_KF as well...*/ + i=0; + while(i<radlen){ + bm->lpar[i] = killoop; + killoop = killoop->radial.next->data; + i++; + } + i=0; + while(i<radlen){ + BME_free_loop(bm,bm->lpar[i]); + i++; + } + /*Validate radial cycle of oe*/ + edok = BME_cycle_validate(radlen,&(oe->loop->radial)); + + } + + + /*Validate disk cycles*/ + diskbase = BME_disk_getpointer(ov->edge,ov); + edok = BME_cycle_validate(valance1, diskbase); + if(!edok) BME_error(); + diskbase = BME_disk_getpointer(tv->edge,tv); + edok = BME_cycle_validate(valance2, diskbase); + if(!edok) BME_error(); + + /*Validate loop cycle of all faces attached to oe*/ + for(i=0,nextl = oe->loop; i<radlen; i++, nextl = BME_radial_nextloop(nextl)){ + edok = BME_cycle_validate(nextl->f->len,nextl->f->loopbase); + if(!edok) BME_error(); + } + /*deallocate edge*/ + BLI_remlink(&(bm->edges), ke); + BME_free_edge(bm, ke); + /*deallocate vertex*/ + BLI_remlink(&(bm->verts), kv); + BME_free_vert(bm, kv); + return 1; + } + } + return 0; +} + + +/** + * BME_loop_reverse + * + * FLIP FACE EULER + * + * Changes the winding order of a face from CW to CCW or vice versa. + * This euler is a bit peculiar in compairson to others as it is its + * own inverse. + * + * TODO: reinsert validation code. + * + * Returns - + * 1 for success, 0 for failure. + */ + +int BME_loop_reverse(BME_Mesh *bm, BME_Poly *f){ + BME_Loop *l = f->loopbase, *curloop, *oldprev, *oldnext; + int i, j, edok, len = 0; + + len = BME_cycle_length(l); + if(bm->edarlen < len){ + MEM_freeN(bm->edar); + bm->edar = MEM_callocN(sizeof(BME_Edge *)* len, "BMesh Edge pointer array"); + bm->edarlen = len; + } + + for(i=0, curloop = l; i< len; i++, curloop=curloop->next){ + curloop->e->eflag1 = 0; + curloop->e->eflag2 = BME_cycle_length(&curloop->radial); + BME_radial_remove_loop(curloop, curloop->e); + /*in case of border edges we HAVE to zero out curloop->radial Next/Prev*/ + curloop->radial.next = curloop->radial.prev = NULL; + bm->edar[i] = curloop->e; + } + + /*actually reverse the loop. This belongs in BME_cycle_reverse!*/ + for(i=0, curloop = l; i < len; i++){ + oldnext = curloop->next; + oldprev = curloop->prev; + curloop->next = oldprev; + curloop->prev = oldnext; + curloop = oldnext; + } + + if(len == 2){ //two edged face + //do some verification here! + l->e = bm->edar[1]; + l->next->e = bm->edar[0]; + } + else{ + for(i=0, curloop = l; i < len; i++, curloop = curloop->next){ + edok = 0; + for(j=0; j < len; j++){ + edok = BME_verts_in_edge(curloop->v, curloop->next->v, bm->edar[j]); + if(edok){ + curloop->e = bm->edar[j]; + break; + } + } + } + } + /*rebuild radial*/ + for(i=0, curloop = l; i < len; i++, curloop = curloop->next) BME_radial_append(curloop->e, curloop); + + /*validate radial*/ + for(i=0, curloop = l; i < len; i++, curloop = curloop->next){ + edok = BME_cycle_validate(curloop->e->eflag2, &(curloop->radial)); + if(!edok){ + BME_error(); + } + } + return 1; +} + +/** + * BME_JFKE + * + * JOIN FACE KILL EDGE: + * + * Takes two faces joined by a single 2-manifold edge and fuses them togather. + * The edge shared by the faces must not be connected to any other edges which have + * Both faces in its radial cycle + * + * Examples: + * + * A B + * ---------- ---------- + * | | | | + * | f1 | | f1 | + * v1========v2 = Ok! v1==V2==v3 == Wrong! + * | f2 | | f2 | + * | | | | + * ---------- ---------- + * + * In the example A, faces f1 and f2 are joined by a single edge, and the euler can safely be used. + * In example B however, f1 and f2 are joined by multiple edges and will produce an error. The caller + * in this case should call BME_JEKV on the extra edges before attempting to fuse f1 and f2. + * + * Also note that the order of arguments decides whether or not certain per-face attributes are present + * in the resultant face. For instance vertex winding, material index, smooth flags, ect are inherited + * from f1, not f2. + * + * Returns - + * A BME_Poly pointer +*/ + +BME_Poly *BME_JFKE(BME_Mesh *bm, BME_Poly *f1, BME_Poly *f2, BME_Edge *e) +{ + + BME_Loop *curloop, *f1loop=NULL, *f2loop=NULL; + int loopok = 0, newlen = 0,i, f1len=0, f2len=0, radlen=0, edok; + + if(f1 == f2) return NULL; //can't join a face to itself + /*verify that e is in both f1 and f2*/ + f1len = BME_cycle_length(f1->loopbase); + f2len = BME_cycle_length(f2->loopbase); + for(i=0, curloop = f1->loopbase; i < f1len; i++, curloop = curloop->next){ + if(curloop->e == e){ + f1loop = curloop; + break; + } + } + for(i=0, curloop = f2->loopbase; i < f2len; i++, curloop = curloop->next){ + if(curloop->e==e){ + f2loop = curloop; + break; + } + } + if(!(f1loop && f2loop)) return NULL; + + /*validate that edge is 2-manifold edge*/ + radlen = BME_cycle_length(&(f1loop->radial)); + if(radlen != 2) return NULL; + + /*validate direction of f2's loop cycle is compatible.*/ + if(f1loop->v == f2loop->v) return NULL; + + /* + Finally validate that for each face, each vertex has another edge in its disk cycle that is + not e, and not shared. + */ + if(BME_radial_find_face(f1loop->next->e,f2)) return NULL; + if(BME_radial_find_face(f1loop->prev->e,f2)) return NULL; + if(BME_radial_find_face(f2loop->next->e,f1)) return NULL; + if(BME_radial_find_face(f2loop->prev->e,f1)) return NULL; + + /*join the two loops*/ + f1loop->prev->next = f2loop->next; + f2loop->next->prev = f1loop->prev; + + f1loop->next->prev = f2loop->prev; + f2loop->prev->next = f1loop->next; + + /*if f1loop was baseloop, give f1loop->next the base.*/ + if(f1->loopbase == f1loop) f1->loopbase = f1loop->next; + + /*validate the new loop*/ + loopok = BME_cycle_validate((f1len+f2len)-2, f1->loopbase); + if(!loopok) BME_error(); + + /*make sure each loop points to the proper face*/ + newlen = BME_cycle_length(f1->loopbase); + for(i = 0, curloop = f1->loopbase; i < newlen; i++, curloop = curloop->next) curloop->f = f1; + + f1->len = newlen; + + edok = BME_cycle_validate(f1->len, f1->loopbase); + if(!edok) BME_error(); + + /*remove edge from the disk cycle of its two vertices.*/ + BME_disk_remove_edge(f1loop->e, f1loop->e->v1); + BME_disk_remove_edge(f1loop->e, f1loop->e->v2); + + /*deallocate edge and its two loops as well as f2*/ + BLI_remlink(&(bm->edges), f1loop->e); + BLI_remlink(&(bm->polys), f2); + BME_free_edge(bm, f1loop->e); + BME_free_loop(bm, f1loop); + BME_free_loop(bm, f2loop); + BME_free_poly(bm, f2); + return f1; +} diff --git a/source/blender/blenkernel/intern/BME_mesh.c b/source/blender/blenkernel/intern/BME_mesh.c new file mode 100644 index 00000000000..ad46a7c1eb7 --- /dev/null +++ b/source/blender/blenkernel/intern/BME_mesh.c @@ -0,0 +1,285 @@ +/** + * BME_mesh.c jan 2007 + * + * BMesh mesh level functions. + * + * $Id: BME_eulers.c,v 1.00 2007/01/17 17:42:01 Briggs Exp $ + * + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * about this. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * The Original Code is Copyright (C) 2007 Blender Foundation. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): Geoffrey Bantle. + * + * ***** END GPL LICENSE BLOCK ***** + */ + + +#include "MEM_guardedalloc.h" +#include "DNA_listBase.h" +#include "BLI_blenlib.h" +#include "BKE_utildefines.h" +#include "BKE_bmesh.h" +#include "bmesh_private.h" + + +/* + * BME MAKE MESH + * + * Allocates a new BME_Mesh structure. + * Returns - + * Pointer to a Bmesh + * +*/ + +BME_Mesh *BME_make_mesh(int allocsize[4]) +{ + /*allocate the structure*/ + BME_Mesh *bm = MEM_callocN(sizeof(BME_Mesh),"BMesh"); + /*allocate the memory pools for the mesh elements*/ + bm->vpool = BLI_mempool_create(sizeof(BME_Vert), allocsize[0], allocsize[0]); + bm->epool = BLI_mempool_create(sizeof(BME_Edge), allocsize[1], allocsize[1]); + bm->lpool = BLI_mempool_create(sizeof(BME_Loop), allocsize[2], allocsize[2]); + bm->ppool = BLI_mempool_create(sizeof(BME_Poly), allocsize[3], allocsize[3]); + return bm; +} +/* + * BME FREE MESH + * + * Frees a BME_Mesh structure. +*/ + +void BME_free_mesh(BME_Mesh *bm) +{ + BME_Vert *v; + BME_Edge *e; + BME_Loop *l; + BME_Poly *f; + + for(v=bm->verts.first; v; v=v->next) CustomData_bmesh_free_block(&bm->vdata, &v->data); + for(e=bm->edges.first; e; e=e->next) CustomData_bmesh_free_block(&bm->edata, &e->data); + for(f=bm->polys.first; f; f=f->next){ + CustomData_bmesh_free_block(&bm->pdata, &f->data); + l = f->loopbase; + do{ + CustomData_bmesh_free_block(&bm->ldata, &l->data); + l = l->next; + }while(l!=f->loopbase); + } + + /*Free custom data pools, This should probably go in CustomData_free?*/ + if(bm->vdata.totlayer) BLI_mempool_destroy(bm->vdata.pool); + if(bm->edata.totlayer) BLI_mempool_destroy(bm->edata.pool); + if(bm->ldata.totlayer) BLI_mempool_destroy(bm->ldata.pool); + if(bm->pdata.totlayer) BLI_mempool_destroy(bm->pdata.pool); + + /*free custom data*/ + CustomData_free(&bm->vdata,0); + CustomData_free(&bm->edata,0); + CustomData_free(&bm->ldata,0); + CustomData_free(&bm->pdata,0); + + /*destroy element pools*/ + BLI_mempool_destroy(bm->vpool); + BLI_mempool_destroy(bm->epool); + BLI_mempool_destroy(bm->ppool); + BLI_mempool_destroy(bm->lpool); + + MEM_freeN(bm); +} + +/* + * BME MODEL BEGIN AND END + * + * These two functions represent the 'point of entry' for tools. Every BMesh tool + * must begin with a call to BME_model_end() and finish with a call to BME_model_end(). + * No modification of mesh data is allowed except in between these two calls. + * + * The purpose of these calls is allow for housekeeping tasks to be performed, + * such as allocating/freeing scratch arrays or performing debug validation of + * the mesh structure. + * + * Returns - + * Nothing + * +*/ + +int BME_model_begin(BME_Mesh *bm){ + /*Initialize some scratch pointer arrays used by eulers*/ + bm->vtar = MEM_callocN(sizeof(BME_Vert *) * 1024, "BMesh scratch vert array"); + bm->edar = MEM_callocN(sizeof(BME_Edge *) * 1024, "BMesh scratch edge array"); + bm->lpar = MEM_callocN(sizeof(BME_Loop *) * 1024, "BMesh scratch loop array"); + bm->plar = MEM_callocN(sizeof(BME_Poly *) * 1024, "BMesh scratch poly array"); + + bm->vtarlen = bm->edarlen = bm->lparlen = bm->plarlen = 1024; + + return 1; +} + +void BME_model_end(BME_Mesh *bm){ + int meshok, totvert, totedge, totpoly; + + totvert = BLI_countlist(&(bm->verts)); + totedge = BLI_countlist(&(bm->edges)); + totpoly = BLI_countlist(&(bm->polys)); + + if(bm->vtar) MEM_freeN(bm->vtar); + if(bm->edar) MEM_freeN(bm->edar); + if(bm->lpar) MEM_freeN(bm->lpar); + if(bm->plar) MEM_freeN(bm->plar); + + bm->vtar = NULL; + bm->edar = NULL; + bm->lpar = NULL; + bm->plar = NULL; + bm->vtarlen = bm->edarlen = bm->lparlen = bm->plarlen = 0; + + + if(bm->totvert!=totvert || bm->totedge!=totedge || bm->totpoly!=totpoly) + BME_error(); + + meshok = BME_validate_mesh(bm, 1); + if(!meshok){ + BME_error(); + } +} + +/* + * BME VALIDATE MESH + * + * There are several levels of validation for meshes. At the + * Euler level, some basic validation is done to local topology. + * To catch more subtle problems however, BME_validate_mesh() is + * called by BME_model_end() whenever a tool is done executing. + * The purpose of this function is to insure that during the course + * of tool execution that nothing has been done to invalidate the + * structure, and if it has, provide a way of reporting that so that + * we can restore the proper structure from a backup. Since a full mesh + * validation would be too expensive, this is presented as a compromise. + * + * TODO + * + * -Make this only part of debug builds + */ + +#define VHALT(halt) {BME_error(); if(halt) return 0;} + +int BME_validate_mesh(struct BME_Mesh *bm, int halt) +{ + BME_Vert *v; + BME_Edge *e; + BME_Poly *f; + BME_Loop *l; + BME_CycleNode *diskbase; + int i, ok; + + /*Simple edge verification*/ + for(e=bm->edges.first; e; e=e->next){ + if(e->v1 == e->v2) VHALT(halt); + /*validate e->d1.data and e->d2.data*/ + if(e->d1.data != e || e->d2.data != e) VHALT(halt); + /*validate e->loop->e*/ + if(e->loop){ + if(e->loop->e != e) VHALT(halt); + } + } + + /*calculate disk cycle lengths*/ + for(v=bm->verts.first; v; v=v->next) v->tflag1 = v->tflag2 = 0; + for(e=bm->edges.first; e; e=e->next){ + e->v1->tflag1++; + e->v2->tflag1++; + } + /*Validate vertices and disk cycle*/ + for(v=bm->verts.first; v; v=v->next){ + /*validate v->edge pointer*/ + if(v->tflag1){ + if(v->edge){ + ok = BME_vert_in_edge(v->edge,v); + if(!ok) VHALT(halt); + /*validate length of disk cycle*/ + diskbase = BME_disk_getpointer(v->edge, v); + ok = BME_cycle_validate(v->tflag1, diskbase); + if(!ok) VHALT(halt); + /*validate that each edge in disk cycle contains V*/ + for(i=0, e=v->edge; i < v->tflag1; i++, e = BME_disk_nextedge(e,v)){ + ok = BME_vert_in_edge(e, v); + if(!ok) VHALT(halt); + } + } + else VHALT(halt); + } + } + /*validate edges*/ + for(e=bm->edges.first; e; e=e->next){ + /*seperate these into BME_disk_hasedge (takes pointer to edge)*/ + /*search v1 disk cycle for edge*/ + ok = BME_disk_hasedge(e->v1,e); + if(!ok) VHALT(halt); + /*search v2 disk cycle for edge*/ + ok = BME_disk_hasedge(e->v2,e); + if(!ok) VHALT(halt); + } + + for(e=bm->edges.first; e; e=e->next) e->tflag2 = 0; //store incident faces + /*Validate the loop cycle integrity.*/ + for(f=bm->polys.first; f; f=f->next){ + ok = BME_cycle_length(f->loopbase); + if(ok > 1){ + f->tflag1 = ok; + } + else VHALT(halt); + for(i=0, l=f->loopbase; i < f->tflag1; i++, l=l->next){ + /*verify loop->v pointers*/ + ok = BME_verts_in_edge(l->v, l->next->v, l->e); + if(!ok) VHALT(halt); + /*verify radial node data pointer*/ + if(l->radial.data != l) VHALT(halt); + /*validate l->e->loop poitner*/ + if(l->e->loop == NULL) VHALT(halt); + /*validate l->f pointer*/ + if(l->f != f) VHALT(halt); + /*see if l->e->loop is actually in radial cycle*/ + + l->e->tflag2++; + } + } + + /*validate length of radial cycle*/ + for(e=bm->edges.first; e; e=e->next){ + if(e->loop){ + ok = BME_cycle_validate(e->tflag2,&(e->loop->radial)); + if(!ok) VHALT(halt); + } + } + + /*validate that EIDs are within range... if not indicates corrupted mem*/ + + /*if we get this far, pretty safe to return 1*/ + return 1; +} + +/* Currently just a convient place for a breakpoint. + Probably should take an error string +*/ +void BME_error(void){ + printf("BME modelling error!"); +} diff --git a/source/blender/blenkernel/intern/BME_structure.c b/source/blender/blenkernel/intern/BME_structure.c new file mode 100644 index 00000000000..ca27f5efd10 --- /dev/null +++ b/source/blender/blenkernel/intern/BME_structure.c @@ -0,0 +1,627 @@ +/** + * BME_structure.c jan 2007 + * + * Low level routines for manipulating the BMesh structure. + * + * $Id: BME_structure.c,v 1.00 2007/01/17 17:42:01 Briggs Exp $ + * + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * about this. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * The Original Code is Copyright (C) 2007 Blender Foundation. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): Geoffrey Bantle. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#include <limits.h> +#include "MEM_guardedalloc.h" + +#include "DNA_listBase.h" +#include "BKE_utildefines.h" +#include "BKE_bmesh.h" +#include "BLI_blenlib.h" +#include "BLI_linklist.h" +#include "BLI_ghash.h" +/** + * MISC utility functions. + * + */ + +int BME_vert_in_edge(BME_Edge *e, BME_Vert *v){ + if(e->v1 == v || e->v2 == v) return 1; + return 0; +} +int BME_verts_in_edge(BME_Vert *v1, BME_Vert *v2, BME_Edge *e){ + if(e->v1 == v1 && e->v2 == v2) return 1; + else if(e->v1 == v2 && e->v2 == v1) return 1; + return 0; +} + +BME_Vert *BME_edge_getothervert(BME_Edge *e, BME_Vert *v){ + if(e->v1 == v) return e->v2; + else if(e->v2 == v) return e->v1; + return NULL; +} + +int BME_edge_swapverts(BME_Edge *e, BME_Vert *orig, BME_Vert *new){ + if(e->v1 == orig){ + e->v1 = new; + e->d1.next = NULL; + e->d1.prev = NULL; + return 1; + } + else if(e->v2 == orig){ + e->v2 = new; + e->d2.next = NULL; + e->d2.prev = NULL; + return 1; + } + return 0; +} + +/** + * ALLOCATION/DEALLOCATION FUNCTIONS + */ + +BME_Vert *BME_addvertlist(BME_Mesh *bm, BME_Vert *example){ + BME_Vert *v=NULL; + v = BLI_mempool_alloc(bm->vpool); + v->next = v->prev = NULL; + v->EID = bm->nextv; + v->co[0] = v->co[1] = v->co[2] = 0.0f; + v->no[0] = v->no[1] = v->no[2] = 0.0f; + v->edge = NULL; + v->data = NULL; + v->eflag1 = v->eflag2 = v->tflag1 = v->tflag2 = 0; + v->flag = v->h = 0; + v->bweight = 0.0f; + BLI_addtail(&(bm->verts), v); + bm->nextv++; + bm->totvert++; + + if(example){ + VECCOPY(v->co,example->co); + CustomData_bmesh_copy_data(&bm->vdata, &bm->vdata, example->data, &v->data); + } + else + CustomData_bmesh_set_default(&bm->vdata, &v->data); + + return v; +} +BME_Edge *BME_addedgelist(BME_Mesh *bm, BME_Vert *v1, BME_Vert *v2, BME_Edge *example){ + BME_Edge *e=NULL; + e = BLI_mempool_alloc(bm->epool); + e->next = e->prev = NULL; + e->EID = bm->nexte; + e->v1 = v1; + e->v2 = v2; + e->d1.next = e->d1.prev = e->d2.next = e->d2.prev = NULL; + e->d1.data = e; + e->d2.data = e; + e->loop = NULL; + e->data = NULL; + e->eflag1 = e->eflag2 = e->tflag1 = e->tflag2 = 0; + e->flag = e->h = 0; + e->crease = e->bweight = 0.0f; + bm->nexte++; + bm->totedge++; + BLI_addtail(&(bm->edges), e); + + if(example) + CustomData_bmesh_copy_data(&bm->edata, &bm->edata, example->data, &e->data); + else + CustomData_bmesh_set_default(&bm->edata, &e->data); + + + return e; +} +BME_Loop *BME_create_loop(BME_Mesh *bm, BME_Vert *v, BME_Edge *e, BME_Poly *f, BME_Loop *example){ + BME_Loop *l=NULL; + l = BLI_mempool_alloc(bm->lpool); + l->next = l->prev = NULL; + l->EID = bm->nextl; + l->radial.next = l->radial.prev = NULL; + l->radial.data = l; + l->v = v; + l->e = e; + l->f = f; + l->data = NULL; + l->eflag1 = l->eflag2 = l->tflag1 = l->tflag2 = 0; + l->flag = l->h = 0; //stupid waste! + bm->nextl++; + bm->totloop++; + + if(example) + CustomData_bmesh_copy_data(&bm->ldata, &bm->ldata, example->data, &l->data); + else + CustomData_bmesh_set_default(&bm->ldata, &l->data); + + return l; +} + +BME_Poly *BME_addpolylist(BME_Mesh *bm, BME_Poly *example){ + BME_Poly *f = NULL; + f = BLI_mempool_alloc(bm->ppool); + f->next = f->prev = NULL; + f->EID = bm->nextp; + f->loopbase = NULL; + f->len = 0; + f->data = NULL; + f->eflag1 = f->eflag2 = f->tflag1 = f->tflag2 = 0; + f->flag = f->h = f->mat_nr; + BLI_addtail(&(bm->polys),f); + bm->nextp++; + bm->totpoly++; + + if(example) + CustomData_bmesh_copy_data(&bm->pdata, &bm->pdata, example->data, &f->data); + else + CustomData_bmesh_set_default(&bm->pdata, &f->data); + + + return f; +} + +/* free functions dont do much *yet*. When per-vertex, per-edge and per-face/faceloop + data is added though these will be needed. +*/ +void BME_free_vert(BME_Mesh *bm, BME_Vert *v){ + bm->totvert--; + CustomData_bmesh_free_block(&bm->vdata, &v->data); + BLI_mempool_free(bm->vpool, v); +} +void BME_free_edge(BME_Mesh *bm, BME_Edge *e){ + bm->totedge--; + CustomData_bmesh_free_block(&bm->edata, &e->data); + BLI_mempool_free(bm->epool, e); +} +void BME_free_poly(BME_Mesh *bm, BME_Poly *f){ + bm->totpoly--; + CustomData_bmesh_free_block(&bm->pdata, &f->data); + BLI_mempool_free(bm->ppool, f); +} +void BME_free_loop(BME_Mesh *bm, BME_Loop *l){ + bm->totloop--; + CustomData_bmesh_free_block(&bm->ldata, &l->data); + BLI_mempool_free(bm->lpool, l); +} +/** + * BMESH CYCLES + * + * Cycles are circular doubly linked lists that form the basis of adjacency + * information in the BME modeller. Full adjacency relations can be derived + * from examining these cycles very quickly. Although each cycle is a double + * circular linked list, each one is considered to have a 'base' or 'head', + * and care must be taken by Euler code when modifying the contents of a cycle. + * + * The contents of this file are split into two parts. First there are the + * BME_cycle family of functions which are generic circular double linked list + * procedures. The second part contains higher level procedures for supporting + * modification of specific cycle types. + * + * The three cycles explicitly stored in the BMesh data structure are as follows: + * + * 1: The Disk Cycle - A circle of edges around a vertex + * Base: vertex->edge pointer. + * + * This cycle is the most complicated in terms of its structure. Each BME_Edge contains + * two BME_CycleNode structures to keep track of that edge's membership in the disk cycle + * of each of its vertices. However for any given vertex it may be the first in some edges + * in its disk cycle and the second for others. The BME_disk_XXX family of functions contain + * some nice utilities for navigating disk cycles in a way that hides this detail from the + * tool writer. + * + * Note that the disk cycle is completley independant from face data. One advantage of this + * is that wire edges are fully integrated into the topology database. Another is that the + * the disk cycle has no problems dealing with non-manifold conditions involving faces. + * + * Functions relating to this cycle: + * + * BME_disk_append_edge + * BME_disk_remove_edge + * BME_disk_nextedge + * BME_disk_getpointer + * + * 2: The Radial Cycle - A circle of face edges (BME_Loop) around an edge + * Base: edge->loop->radial structure. + * + * The radial cycle is similar to the radial cycle in the radial edge data structure.* + * Unlike the radial edge however, the radial cycle does not require a large amount of memory + * to store non-manifold conditions since BMesh does not keep track of region/shell + * information. + * + * Functions relating to this cycle: + * + * BME_radial_append + * BME_radial_remove_loop + * BME_radial_nextloop + * BME_radial_find_face + * + * + * 3: The Loop Cycle - A circle of face edges around a polygon. + * Base: polygon->loopbase. + * + * The loop cycle keeps track of a faces vertices and edges. It should be noted that the + * direction of a loop cycle is either CW or CCW depending on the face normal, and is + * not oriented to the faces editedges. + * + * Functions relating to this cycle: + * + * BME_cycle_XXX family of functions. + * + * + * Note that the order of elements in all cycles except the loop cycle is undefined. This + * leads to slightly increased seek time for deriving some adjacency relations, however the + * advantage is that no intrinsic properties of the data structures are dependant upon the + * cycle order and all non-manifold conditions are represented trivially. + * +*/ + + +void BME_cycle_append(void *h, void *nt) +{ + BME_CycleNode *oldtail, *head, *newtail; + + head = (BME_CycleNode*)h; + newtail = (BME_CycleNode*)nt; + + if(head->next == NULL){ + head->next = newtail; + head->prev = newtail; + newtail->next = head; + newtail->prev = head; + } + else{ + oldtail = head->prev; + oldtail->next = newtail; + newtail->next = head; + newtail->prev = oldtail; + head->prev = newtail; + + } +} + +/** + * BME_cycle_length + * + * Count the nodes in a cycle. + * + * Returns - + * Integer + */ + +int BME_cycle_length(void *h){ + + int len = 0; + BME_CycleNode *head, *curnode; + head = (BME_CycleNode*)h; + + if(head){ + len = 1; + for(curnode = head->next; curnode != head; curnode=curnode->next){ + if(len == INT_MAX){ //check for infinite loop/corrupted cycle + return -1; + } + len++; + } + } + return len; +} + + +/** + * BME_cycle_remove + * + * Removes a node from a cycle. + * + * Returns - + * 1 for success, 0 for failure. + */ + +int BME_cycle_remove(void *h, void *remn) +{ + int i, len; + BME_CycleNode *head, *remnode, *curnode; + + head = (BME_CycleNode*)h; + remnode = (BME_CycleNode*)remn; + len = BME_cycle_length(h); + + if(len == 1 && head == remnode){ + head->next = NULL; + head->prev = NULL; + return 1; + } + else{ + for(i=0, curnode = head; i < len; curnode = curnode->next){ + if(curnode == remnode){ + remnode->prev->next = remnode->next; + remnode->next->prev = remnode->prev; + /*zero out remnode pointers, important!*/ + //remnode->next = NULL; + //remnode->prev = NULL; + return 1; + + } + } + } + return 0; +} + +/** + * BME_cycle_validate + * + * Validates a cycle. Takes as an argument the expected length of the cycle and + * a pointer to the cycle head or base. + * + * + * Returns - + * 1 for success, 0 for failure. + */ + +int BME_cycle_validate(int len, void *h){ + int i; + BME_CycleNode *curnode, *head; + head = (BME_CycleNode*)h; + + /*forward validation*/ + for(i = 0, curnode = head; i < len; i++, curnode = curnode->next); + if(curnode != head) return 0; + + /*reverse validation*/ + for(i = 0, curnode = head; i < len; i++, curnode = curnode->prev); + if(curnode != head) return 0; + + return 1; +} + +/*Begin Disk Cycle routines*/ + +/** + * BME_disk_nextedge + * + * Find the next edge in a disk cycle + * + * Returns - + * Pointer to the next edge in the disk cycle for the vertex v. + */ + +BME_Edge *BME_disk_nextedge(BME_Edge *e, BME_Vert *v) +{ + if(BME_vert_in_edge(e, v)){ + if(e->v1 == v) return e->d1.next->data; + else if(e->v2 == v) return e->d2.next->data; + } + return NULL; +} + +/** + * BME_disk_getpointer + * + * Given an edge and one of its vertices, find the apporpriate CycleNode + * + * Returns - + * Pointer to BME_CycleNode. + */ +BME_CycleNode *BME_disk_getpointer(BME_Edge *e, BME_Vert *v){ + /*returns pointer to the cycle node for the appropriate vertex in this disk*/ + if(e->v1 == v) return &(e->d1); + else if (e->v2 == v) return &(e->d2); + return NULL; +} + +/** + * BME_disk_append_edge + * + * Appends edge to the end of a vertex disk cycle. + * + * Returns - + * 1 for success, 0 for failure + */ + +int BME_disk_append_edge(BME_Edge *e, BME_Vert *v) +{ + + BME_CycleNode *base, *tail; + + if(BME_vert_in_edge(e, v) == 0) return 0; /*check to make sure v is in e*/ + + /*check for loose vert first*/ + if(v->edge == NULL){ + v->edge = e; + base = tail = BME_disk_getpointer(e, v); + BME_cycle_append(base, tail); /*circular reference is ok!*/ + return 1; + } + + /*insert e at the end of disk cycle and make it the new v->edge*/ + base = BME_disk_getpointer(v->edge, v); + tail = BME_disk_getpointer(e, v); + BME_cycle_append(base, tail); + return 1; +} + +/** + * BME_disk_remove_edge + * + * Removes an edge from a disk cycle. If the edge to be removed is + * at the base of the cycle, the next edge becomes the new base. + * + * + * Returns - + * Nothing + */ + +void BME_disk_remove_edge(BME_Edge *e, BME_Vert *v) +{ + BME_CycleNode *base, *remnode; + BME_Edge *newbase; + int len; + + base = BME_disk_getpointer(v->edge, v); + remnode = BME_disk_getpointer(e, v); + + /*first deal with v->edge pointer...*/ + len = BME_cycle_length(base); + if(len == 1) newbase = NULL; + else if(v->edge == e) newbase = base->next-> data; + else newbase = v->edge; + + /*remove and rebase*/ + BME_cycle_remove(base, remnode); + v->edge = newbase; +} + +/** + * BME_disk_next_edgeflag + * + * Searches the disk cycle of v, starting with e, for the + * next edge that has either eflag or tflag. + * + * BME_Edge pointer. + */ + +BME_Edge *BME_disk_next_edgeflag(BME_Edge *e, BME_Vert *v, int eflag, int tflag){ + + BME_CycleNode *diskbase; + BME_Edge *curedge; + int len, ok; + + if(eflag && tflag) return NULL; + + ok = BME_vert_in_edge(e,v); + if(ok){ + diskbase = BME_disk_getpointer(e, v); + len = BME_cycle_length(diskbase); + curedge = BME_disk_nextedge(e,v); + while(curedge != e){ + if(tflag){ + if(curedge->tflag1 == tflag) return curedge; + } + else if(eflag){ + if(curedge->eflag1 == eflag) return curedge; + } + curedge = BME_disk_nextedge(curedge, v); + } + } + return NULL; +} + +/** + * BME_disk_count_edgeflag + * + * Counts number of edges in this verts disk cycle which have + * either eflag or tflag (but not both!) + * + * Returns - + * Integer. + */ + +int BME_disk_count_edgeflag(BME_Vert *v, int eflag, int tflag){ + BME_CycleNode *diskbase; + BME_Edge *curedge; + int i, len=0, count=0; + + if(v->edge){ + if(eflag && tflag) return 0; /*tflag and eflag are reserved for different functions!*/ + diskbase = BME_disk_getpointer(v->edge, v); + len = BME_cycle_length(diskbase); + + for(i = 0, curedge=v->edge; i<len; i++){ + if(tflag){ + if(curedge->tflag1 == tflag) count++; + } + else if(eflag){ + if(curedge->eflag1 == eflag) count++; + } + curedge = BME_disk_nextedge(curedge, v); + } + } + return count; +} + +int BME_disk_hasedge(BME_Vert *v, BME_Edge *e){ + BME_CycleNode *diskbase; + BME_Edge *curedge; + int i, len=0; + + if(v->edge){ + diskbase = BME_disk_getpointer(v->edge,v); + len = BME_cycle_length(diskbase); + + for(i = 0, curedge=v->edge; i<len; i++){ + if(curedge == e) return 1; + else curedge=BME_disk_nextedge(curedge, v); + } + } + return 0; +} +/*end disk cycle routines*/ + +BME_Loop *BME_radial_nextloop(BME_Loop *l){ + return (BME_Loop*)(l->radial.next->data); +} + +void BME_radial_append(BME_Edge *e, BME_Loop *l){ + if(e->loop == NULL) e->loop = l; + BME_cycle_append(&(e->loop->radial), &(l->radial)); +} + +void BME_radial_remove_loop(BME_Loop *l, BME_Edge *e) +{ + BME_Loop *newbase; + int len; + + /*deal with edge->loop pointer*/ + len = BME_cycle_length(&(e->loop->radial)); + if(len == 1) newbase = NULL; + else if(e->loop == l) newbase = e->loop->radial.next->data; + else newbase = e->loop; + + /*remove and rebase*/ + BME_cycle_remove(&(e->loop->radial), &(l->radial)); + e->loop = newbase; +} + +int BME_radial_find_face(BME_Edge *e,BME_Poly *f) +{ + + BME_Loop *curloop; + int i, len; + + len = BME_cycle_length(&(e->loop->radial)); + for(i = 0, curloop = e->loop; i < len; i++, curloop = curloop->radial.next->data){ + if(curloop->f == f) return 1; + } + return 0; +} + +struct BME_Loop *BME_loop_find_loop(struct BME_Poly *f, struct BME_Vert *v) { + BME_Loop *l; + int i, len; + + len = BME_cycle_length(f->loopbase); + for (i = 0, l=f->loopbase; i < len; i++, l=l->next) { + if (l->v == v) return l; + } + return NULL; +} diff --git a/source/blender/blenkernel/intern/BME_tools.c b/source/blender/blenkernel/intern/BME_tools.c new file mode 100644 index 00000000000..8aaa7aca023 --- /dev/null +++ b/source/blender/blenkernel/intern/BME_tools.c @@ -0,0 +1,1323 @@ +/** + * BME_tools.c jan 2007 + * + * Functions for changing the topology of a mesh. + * + * $Id: BME_eulers.c,v 1.00 2007/01/17 17:42:01 Briggs Exp $ + * + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * The Original Code is Copyright (C) 2004 Blender Foundation. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): Geoffrey Bantle and Levi Schooley. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#include <math.h> + +#include "MEM_guardedalloc.h" + +#include "DNA_listBase.h" +#include "DNA_meshdata_types.h" +#include "DNA_mesh_types.h" +#include "DNA_object_types.h" + +#include "BKE_utildefines.h" +#include "BKE_bmesh.h" +#include "BLI_arithb.h" +#include "BLI_blenlib.h" + +/*split this all into a seperate bevel.c file in src*/ + +/* ------- Bevel code starts here -------- */ + +BME_TransData_Head *BME_init_transdata(int bufsize) { + BME_TransData_Head *td; + + td = MEM_callocN(sizeof(BME_TransData_Head), "BMesh transdata header"); + td->gh = BLI_ghash_new(BLI_ghashutil_ptrhash,BLI_ghashutil_ptrcmp); + td->ma = BLI_memarena_new(bufsize); + BLI_memarena_use_calloc(td->ma); + + return td; +} + +void BME_free_transdata(BME_TransData_Head *td) { + BLI_ghash_free(td->gh,NULL,NULL); + BLI_memarena_free(td->ma); + MEM_freeN(td); +} + +BME_TransData *BME_assign_transdata(BME_TransData_Head *td, BME_Mesh *bm, BME_Vert *v, + float *co, float *org, float *vec, float *loc, + float factor, float weight, float maxfactor, float *max) { + BME_TransData *vtd; + int is_new = 0; + + if (v == NULL) return NULL; + + if ((vtd = BLI_ghash_lookup(td->gh, v)) == NULL && bm != NULL) { + vtd = BLI_memarena_alloc(td->ma, sizeof(*vtd)); + BLI_ghash_insert(td->gh, v, vtd); + td->len++; + is_new = 1; + } + + vtd->bm = bm; + vtd->v = v; + if (co != NULL) VECCOPY(vtd->co,co); + if (org == NULL && is_new) { VECCOPY(vtd->org,v->co); } /* default */ + else if (org != NULL) VECCOPY(vtd->org,org); + if (vec != NULL) { + VECCOPY(vtd->vec,vec); + Normalize(vtd->vec); + } + vtd->loc = loc; + + vtd->factor = factor; + vtd->weight = weight; + vtd->maxfactor = maxfactor; + vtd->max = max; + + return vtd; +} + +BME_TransData *BME_get_transdata(BME_TransData_Head *td, BME_Vert *v) { + BME_TransData *vtd; + vtd = BLI_ghash_lookup(td->gh, v); + return vtd; +} + +/* a hack (?) to use the transdata memarena to allocate floats for use with the max limits */ +float *BME_new_transdata_float(BME_TransData_Head *td) { + return BLI_memarena_alloc(td->ma, sizeof(float)); +} + +static int BME_is_nonmanifold_vert(BME_Mesh *bm, BME_Vert *v) { + BME_Edge *e, *oe; + BME_Loop *l; + int len, count, flag; + + if (v->edge == NULL) { + /* loose vert */ + return 1; + } + + /* count edges while looking for non-manifold edges */ + oe = v->edge; + for (len=0,e=v->edge; e != oe || (e == oe && len == 0); len++,e=BME_disk_nextedge(e,v)) { + if (e->loop == NULL) { + /* loose edge */ + return 1; + } + + if (BME_cycle_length(&(e->loop->radial)) > 2) { + /* edge shared by more than two faces */ + return 1; + } + } + + count = 1; + flag = 1; + e = NULL; + oe = v->edge; + l = oe->loop; + while(e != oe) { + if (l->v == v) l = l->prev; + else l = l->next; + e = l->e; + count++; /* count the edges */ + + if (flag && l->radial.next->data == l) { + /* we've hit the edge of an open mesh, reset once */ + flag = 0; + count = 1; + oe = e; + e = NULL; + l = oe->loop; + } + else if (l->radial.next->data == l) { + /* break the loop */ + e = oe; + } + else { + l = l->radial.next->data; + } + } + + if (count < len) { + /* vert shared by multiple regions */ + return 1; + } + + return 0; +} + +/* a wrapper for BME_JFKE that [for now just] checks to + * make sure loop directions are compatible */ +static BME_Poly *BME_JFKE_safe(BME_Mesh *bm, BME_Poly *f1, BME_Poly *f2, BME_Edge *e) { + BME_Loop *l1, *l2; + + l1 = e->loop; + l2 = l1->radial.next->data; + if (l1->v == l2->v) { + BME_loop_reverse(bm, f2); + } + + return BME_JFKE(bm, f1, f2, e); +} + +/* a wrapper for BME_SFME that transfers element flags */ +static BME_Poly *BME_split_face(BME_Mesh *bm, BME_Poly *f, BME_Vert *v1, BME_Vert *v2, BME_Loop **nl, BME_Edge *example) { + BME_Poly *nf; + nf = BME_SFME(bm,f,v1,v2,nl); + nf->flag = f->flag; + /* if the edge was selected, select this face, too */ + if (example->flag & SELECT) f->flag |= ME_FACE_SEL; + nf->h = f->h; + nf->mat_nr = f->mat_nr; + if (nl && example) { + (*nl)->e->flag = example->flag; + (*nl)->e->h = example->h; + (*nl)->e->crease = example->crease; + (*nl)->e->bweight = example->bweight; + } + + return nf; +} + + +static void BME_data_interp_from_verts(BME_Mesh *bm, BME_Vert *v1, BME_Vert *v2, BME_Vert *v, float fac) +{ + void *src[2]; + float w[2]; + if (v1->data && v2->data) { + src[0]= v1->data; + src[1]= v2->data; + w[0] = 1.0f-fac; + w[1] = fac; + CustomData_bmesh_interp(&bm->vdata, src, w, NULL, 2, v->data); + } +} + + +static void BME_data_facevert_edgesplit(BME_Mesh *bm, BME_Vert *v1, BME_Vert *v2, BME_Vert *v, BME_Edge *e1, float fac){ + void *src[2]; + float w[2]; + BME_Loop *l=NULL, *v1loop = NULL, *vloop = NULL, *v2loop = NULL; + + w[0] = 1.0f - fac; + w[1] = fac; + + if(!e1->loop) return; + l = e1->loop; + do{ + if(l->v == v1){ + v1loop = l; + vloop = v1loop->next; + v2loop = vloop->next; + }else if(l->v == v){ + v1loop = l->next; + vloop = l; + v2loop = l->prev; + + } + + src[0] = v1loop->data; + src[1] = v2loop->data; + + CustomData_bmesh_interp(&bm->ldata, src,w, NULL, 2, vloop->data); + l = l->radial.next->data; + }while(l!=e1->loop); +} + + +/* a wrapper for BME_SEMV that transfers element flags */ /*add custom data interpolation in here!*/ +static BME_Vert *BME_split_edge(BME_Mesh *bm, BME_Vert *v, BME_Edge *e, BME_Edge **ne, float percent) { + BME_Vert *nv, *v2; + float len; + + v2 = BME_edge_getothervert(e,v); + nv = BME_SEMV(bm,v,e,ne); + if (nv == NULL) return NULL; + VECSUB(nv->co,v2->co,v->co); + len = VecLength(nv->co); + VECADDFAC(nv->co,v->co,nv->co,len*percent); + nv->flag = v->flag; + nv->bweight = v->bweight; + if (ne) { + (*ne)->flag = e->flag; + (*ne)->h = e->h; + (*ne)->crease = e->crease; + (*ne)->bweight = e->bweight; + } + /*v->nv->v2*/ + BME_data_facevert_edgesplit(bm,v2, v, nv, e, 0.75); + return nv; +} + +static void BME_collapse_vert(BME_Mesh *bm, BME_Edge *ke, BME_Vert *kv, float fac){ + void *src[2]; + float w[2]; + BME_Loop *l=NULL, *kvloop=NULL, *tvloop=NULL; + BME_Vert *tv = BME_edge_getothervert(ke,kv); + + w[0] = 1.0f - fac; + w[1] = fac; + + if(ke->loop){ + l = ke->loop; + do{ + if(l->v == tv && l->next->v == kv){ + tvloop = l; + kvloop = l->next; + + src[0] = kvloop->data; + src[1] = tvloop->data; + CustomData_bmesh_interp(&bm->ldata, src,w, NULL, 2, kvloop->data); + } + l=l->radial.next->data; + }while(l!=ke->loop); + } + BME_JEKV(bm,ke,kv); +} + + + +static int BME_bevel_is_split_vert(BME_Loop *l) { + /* look for verts that have already been added to the edge when + * beveling other polys; this can be determined by testing the + * vert and the edges around it for originality + */ + if ((l->v->tflag1 & BME_BEVEL_ORIG)==0 + && (l->e->tflag1 & BME_BEVEL_ORIG) + && (l->prev->e->tflag1 & BME_BEVEL_ORIG)) + { + return 1; + } + return 0; +} + +/* get a vector, vec, that points from v1->co to wherever makes sense to + * the bevel operation as a whole based on the relationship between v1 and v2 + * (won't necessarily be a vec from v1->co to v2->co, though it probably will be); + * the return value is -1 for failure, 0 if we used vert co's, and 1 if we used transform origins */ +static int BME_bevel_get_vec(float *vec, BME_Vert *v1, BME_Vert *v2, BME_TransData_Head *td) { + BME_TransData *vtd1, *vtd2; + + vtd1 = BME_get_transdata(td,v1); + vtd2 = BME_get_transdata(td,v2); + if (!vtd1 || !vtd2) { + //printf("BME_bevel_get_vec() got called without proper BME_TransData\n"); + return -1; + } + + /* compare the transform origins to see if we can use the vert co's; + * if they belong to different origins, then we will use the origins to determine + * the vector */ + if (VecCompare(vtd1->org,vtd2->org,0.000001f)) { + VECSUB(vec,v2->co,v1->co); + if (VecLength(vec) < 0.000001f) { + VecMulf(vec,0); + } + return 0; + } + else { + VECSUB(vec,vtd2->org,vtd1->org); + if (VecLength(vec) < 0.000001f) { + VecMulf(vec,0); + } + return 1; + } +} + +/* "Projects" a vector perpendicular to vec2 against vec1, such that + * the projected vec1 + vec2 has a min distance of 1 from the "edge" defined by vec2. + * note: the direction, is_forward, is used in conjunction with up_vec to determine + * whether this is a convex or concave corner. If it is a concave corner, it will + * be projected "backwards." If vec1 is before vec2, is_forward should be 0 (we are projecting backwards). + * vec1 is the vector to project onto (expected to be normalized) + * vec2 is the direction of projection (pointing away from vec1) + * up_vec is used for orientation (expected to be normalized) + * returns the length of the projected vector that lies along vec1 */ +static float BME_bevel_project_vec(float *vec1, float *vec2, float *up_vec, int is_forward, BME_TransData_Head *td) { + float factor, vec3[3], tmp[3],c1,c2; + + Crossf(tmp,vec1,vec2); + Normalize(tmp); + factor = Inpf(up_vec,tmp); + if ((factor > 0 && is_forward) || (factor < 0 && !is_forward)) { + Crossf(vec3,vec2,tmp); /* hmm, maybe up_vec should be used instead of tmp */ + } + else { + Crossf(vec3,tmp,vec2); /* hmm, maybe up_vec should be used instead of tmp */ + } + Normalize(vec3); + c1 = Inpf(vec3,vec1); + c2 = Inpf(vec1,vec1); + if (fabs(c1) < 0.000001f || fabs(c2) < 0.000001f) { + factor = 0.0f; + } + else { + factor = c2/c1; + } + + return factor; +} + +/* BME_bevel_split_edge() is the main math work-house; its responsibilities are: + * using the vert and the loop passed, get or make the split vert, set its coordinates + * and transform properties, and set the max limits. + * Finally, return the split vert. */ +static BME_Vert *BME_bevel_split_edge(BME_Mesh *bm, BME_Vert *v, BME_Vert *v1, BME_Loop *l, float *up_vec, float value, BME_TransData_Head *td) { + BME_TransData *vtd, *vtd1, *vtd2; + BME_Vert *sv, *v2, *v3, *ov; + BME_Loop *lv1, *lv2; + BME_Edge *ne, *e1, *e2; + float maxfactor, scale, len, dis, vec1[3], vec2[3], t_up_vec[3]; + int is_edge, forward, is_split_vert; + + if (l == NULL) { + /* what you call operator overloading in C :) + * I wanted to use the same function for both wire edges and poly loops + * so... here we walk around edges to find the needed verts */ + forward = 1; + is_split_vert = 0; + if (v->edge == NULL) { + //printf("We can't split a loose vert's edge!\n"); + return NULL; + } + e1 = v->edge; /* we just use the first two edges */ + e2 = BME_disk_nextedge(v->edge, v); + if (e1 == e2) { + //printf("You need at least two edges to use BME_bevel_split_edge()\n"); + return NULL; + } + v2 = BME_edge_getothervert(e1, v); + v3 = BME_edge_getothervert(e2, v); + if (v1 != v2 && v1 != v3) { + //printf("Error: more than 2 edges in v's disk cycle, or v1 does not share an edge with v\n"); + return NULL; + } + if (v1 == v2) { + v2 = v3; + } + else { + e1 = e2; + } + ov = BME_edge_getothervert(e1,v); + sv = BME_split_edge(bm,v,e1,&ne,0); + //BME_data_interp_from_verts(bm, v, ov, sv, 0.25); /*this is technically wrong...*/ + //BME_data_interp_from_faceverts(bm, v, ov, sv, 0.25); + //BME_data_interp_from_faceverts(bm, ov, v, sv, 0.25); + BME_assign_transdata(td, bm, sv, sv->co, sv->co, NULL, sv->co, 0, -1, -1, NULL); /* quick default */ + sv->tflag1 |= BME_BEVEL_BEVEL; + ne->tflag1 = BME_BEVEL_ORIG; /* mark edge as original, even though it isn't */ + BME_bevel_get_vec(vec1,v1,v,td); + BME_bevel_get_vec(vec2,v2,v,td); + Crossf(t_up_vec,vec1,vec2); + Normalize(t_up_vec); + up_vec = t_up_vec; + } + else { + /* establish loop direction */ + if (l->v == v) { + forward = 1; + lv1 = l->next; + lv2 = l->prev; + v1 = l->next->v; + v2 = l->prev->v; + } + else if (l->next->v == v) { + forward = 0; + lv1 = l; + lv2 = l->next->next; + v1 = l->v; + v2 = l->next->next->v; + } + else { + //printf("ERROR: BME_bevel_split_edge() - v must be adjacent to l\n"); + return NULL; + } + + if (BME_bevel_is_split_vert(lv1)) { + is_split_vert = 1; + sv = v1; + if (forward) v1 = l->next->next->v; + else v1 = l->prev->v; + } + else { + is_split_vert = 0; + ov = BME_edge_getothervert(l->e,v); + sv = BME_split_edge(bm,v,l->e,&ne,0); + //BME_data_interp_from_verts(bm, v, ov, sv, 0.25); /*this is technically wrong...*/ + //BME_data_interp_from_faceverts(bm, v, ov, sv, 0.25); + //BME_data_interp_from_faceverts(bm, ov, v, sv, 0.25); + BME_assign_transdata(td, bm, sv, sv->co, sv->co, NULL, sv->co, 0, -1, -1, NULL); /* quick default */ + sv->tflag1 |= BME_BEVEL_BEVEL; + ne->tflag1 = BME_BEVEL_ORIG; /* mark edge as original, even though it isn't */ + } + + if (BME_bevel_is_split_vert(lv2)) { + if (forward) v2 = lv2->prev->v; + else v2 = lv2->next->v; + } + } + + is_edge = BME_bevel_get_vec(vec1,v,v1,td); /* get the vector we will be projecting onto */ + BME_bevel_get_vec(vec2,v,v2,td); /* get the vector we will be projecting parallel to */ + len = VecLength(vec1); + Normalize(vec1); + + vtd = BME_get_transdata(td, sv); + vtd1 = BME_get_transdata(td, v); + vtd2 = BME_get_transdata(td,v1); + + if (vtd1->loc == NULL) { + /* this is a vert with data only for calculating initial weights */ + if (vtd1->weight < 0) { + vtd1->weight = 0; + } + scale = vtd1->weight/vtd1->factor; + if (!vtd1->max) { + vtd1->max = BME_new_transdata_float(td); + *vtd1->max = -1; + } + } + else { + scale = vtd1->weight; + } + vtd->max = vtd1->max; + + if (is_edge && vtd1->loc != NULL) { + maxfactor = vtd1->maxfactor; + } + else { + maxfactor = scale*BME_bevel_project_vec(vec1,vec2,up_vec,forward,td); + if (vtd->maxfactor > 0 && vtd->maxfactor < maxfactor) { + maxfactor = vtd->maxfactor; + } + } + + dis = (v1->tflag1 & BME_BEVEL_ORIG)? len/3 : len/2; + if (is_edge || dis > maxfactor*value) { + dis = maxfactor*value; + } + VECADDFAC(sv->co,v->co,vec1,dis); + VECSUB(vec1,sv->co,vtd1->org); + dis = VecLength(vec1); + Normalize(vec1); + BME_assign_transdata(td, bm, sv, vtd1->org, vtd1->org, vec1, sv->co, dis, scale, maxfactor, vtd->max); + + return sv; +} + +static float BME_bevel_set_max(BME_Vert *v1, BME_Vert *v2, float value, BME_TransData_Head *td) { + BME_TransData *vtd1, *vtd2; + float max, fac1, fac2, vec1[3], vec2[3], vec3[3]; + + BME_bevel_get_vec(vec1,v1,v2,td); + vtd1 = BME_get_transdata(td,v1); + vtd2 = BME_get_transdata(td,v2); + + if (vtd1->loc == NULL) { + fac1 = 0; + } + else { + VECCOPY(vec2,vtd1->vec); + VecMulf(vec2,vtd1->factor); + if (Inpf(vec1, vec1)) { + Projf(vec2,vec2,vec1); + fac1 = VecLength(vec2)/value; + } + else { + fac1 = 0; + } + } + + if (vtd2->loc == NULL) { + fac2 = 0; + } + else { + VECCOPY(vec3,vtd2->vec); + VecMulf(vec3,vtd2->factor); + if (Inpf(vec1, vec1)) { + Projf(vec2,vec3,vec1); + fac2 = VecLength(vec2)/value; + } + else { + fac2 = 0; + } + } + + if (fac1 || fac2) { + max = VecLength(vec1)/(fac1 + fac2); + if (vtd1->max && (*vtd1->max < 0 || max < *vtd1->max)) { + *vtd1->max = max; + } + if (vtd2->max && (*vtd2->max < 0 || max < *vtd2->max)) { + *vtd2->max = max; + } + } + else { + max = -1; + } + + return max; +} + +static BME_Vert *BME_bevel_wire(BME_Mesh *bm, BME_Vert *v, float value, int res, int options, BME_TransData_Head *td) { + BME_Vert *ov1, *ov2, *v1, *v2; + + ov1 = BME_edge_getothervert(v->edge, v); + ov2 = BME_edge_getothervert(BME_disk_nextedge(v->edge, v), v); + + /* split the edges */ + v1 = BME_bevel_split_edge(bm,v,ov1,NULL,NULL,value,td); + v1->tflag1 |= BME_BEVEL_NONMAN; + v2 = BME_bevel_split_edge(bm,v,ov2,NULL,NULL,value,td); + v2->tflag1 |= BME_BEVEL_NONMAN; + + if (value > 0.5) { + BME_bevel_set_max(v1,ov1,value,td); + BME_bevel_set_max(v2,ov2,value,td); + } + + /* remove the original vert */ + if (res) { + BME_JEKV(bm,v->edge,v); + } + + return v1; +} + +static BME_Loop *BME_bevel_edge(BME_Mesh *bm, BME_Loop *l, float value, int options, float *up_vec, BME_TransData_Head *td) { + BME_Vert *v1, *v2, *kv; + BME_Loop *kl=NULL, *nl; + BME_Edge *e; + BME_Poly *f; + + f = l->f; + e = l->e; + + if ((l->e->tflag1 & BME_BEVEL_BEVEL) == 0 + && ((l->v->tflag1 & BME_BEVEL_BEVEL) || (l->next->v->tflag1 & BME_BEVEL_BEVEL))) + { /* sanity check */ + return l; + } + + /* checks and operations for prev edge */ + /* first, check to see if this edge was inset previously */ + if ((l->prev->e->tflag1 & BME_BEVEL_ORIG) == 0 + && (l->v->tflag1 & BME_BEVEL_NONMAN) == 0) { + kl = l->prev->radial.next->data; + if (kl->v == l->v) kl = kl->prev; + else kl = kl->next; + kv = l->v; + } + else { + kv = NULL; + } + /* get/make the first vert to be used in SFME */ + if (l->v->tflag1 & BME_BEVEL_NONMAN){ + v1 = l->v; + } + else { /* we'll need to split the previous edge */ + v1 = BME_bevel_split_edge(bm,l->v,NULL,l->prev,up_vec,value,td); + } + /* if we need to clean up geometry... */ + if (kv) { + l = l->next; + if (kl->v == kv) { + BME_split_face(bm,kl->f,kl->prev->v,kl->next->v,&nl,kl->prev->e); + BME_JFKE(bm,((BME_Loop*)kl->prev->radial.next->data)->f,kl->f,kl->prev->e); + BME_collapse_vert(bm, kl->e, kv, 1.0); + //BME_JEKV(bm,kl->e,kv); + + } + else { + BME_split_face(bm,kl->f,kl->next->next->v,kl->v,&nl,kl->next->e); + BME_JFKE(bm,((BME_Loop*)kl->next->radial.next->data)->f,kl->f,kl->next->e); + BME_collapse_vert(bm, kl->e, kv, 1.0); + //BME_JEKV(bm,kl->e,kv); + } + l = l->prev; + } + + /* checks and operations for the next edge */ + /* first, check to see if this edge was inset previously */ + if ((l->next->e->tflag1 & BME_BEVEL_ORIG) == 0 + && (l->next->v->tflag1 & BME_BEVEL_NONMAN) == 0) { + kl = l->next->radial.next->data; + if (kl->v == l->next->v) kl = kl->prev; + else kl = kl->next; + kv = l->next->v; + } + else { + kv = NULL; + } + /* get/make the second vert to be used in SFME */ + if (l->next->v->tflag1 & BME_BEVEL_NONMAN) { + v2 = l->next->v; + } + else { /* we'll need to split the next edge */ + v2 = BME_bevel_split_edge(bm,l->next->v,NULL,l->next,up_vec,value,td); + } + /* if we need to clean up geometry... */ + if (kv) { + if (kl->v == kv) { + BME_split_face(bm,kl->f,kl->prev->v,kl->next->v,&nl,kl->prev->e); + BME_JFKE(bm,((BME_Loop*)kl->prev->radial.next->data)->f,kl->f,kl->prev->e); + BME_collapse_vert(bm, kl->e, kv, 1.0); + //BME_JEKV(bm,kl->e,kv); + } + else { + BME_split_face(bm,kl->f,kl->next->next->v,kl->v,&nl,kl->next->e); + BME_JFKE(bm,((BME_Loop*)kl->next->radial.next->data)->f,kl->f,kl->next->e); + BME_collapse_vert(bm, kl->e, kv, 1.0); + //BME_JEKV(bm,kl->e,kv); + } + } + + if ((v1->tflag1 & BME_BEVEL_NONMAN)==0 || (v2->tflag1 & BME_BEVEL_NONMAN)==0) { + BME_split_face(bm,f,v2,v1,&l,e); + l->e->tflag1 = BME_BEVEL_BEVEL; + l = l->radial.next->data; + } + + if (l->f != f){ + //printf("Whoops! You got something out of order in BME_bevel_edge()!\n"); + } + + return l; +} + +static BME_Loop *BME_bevel_vert(BME_Mesh *bm, BME_Loop *l, float value, int options, float *up_vec, BME_TransData_Head *td) { + BME_Vert *v1, *v2; + BME_Poly *f; + + /* get/make the first vert to be used in SFME */ + /* may need to split the previous edge */ + v1 = BME_bevel_split_edge(bm,l->v,NULL,l->prev,up_vec,value,td); + + /* get/make the second vert to be used in SFME */ + /* may need to split this edge (so move l) */ + l = l->prev; + v2 = BME_bevel_split_edge(bm,l->next->v,NULL,l->next,up_vec,value,td); + l = l->next->next; + + /* "cut off" this corner */ + f = BME_split_face(bm,l->f,v2,v1,NULL,l->e); + + return l; +} + +/** + * BME_bevel_poly + * + * Polygon inset tool: + * + * Insets a polygon/face based on the tflag1's of its vertices + * and edges. Used by the bevel tool only, for now. + * The parameter "value" is the distance to inset (should be negative). + * The parameter "options" is not currently used. + * + * Returns - + * A BME_Poly pointer to the resulting inner face. +*/ +static BME_Poly *BME_bevel_poly(BME_Mesh *bm, BME_Poly *f, float value, int options, BME_TransData_Head *td) { + BME_Loop *l, *ol; + BME_TransData *vtd1, *vtd2; + float up_vec[3], vec1[3], vec2[3], vec3[3], fac1, fac2, max = -1; + int len, i; + + up_vec[0] = 0.0f; + up_vec[1] = 0.0f; + up_vec[2] = 0.0f; + /* find a good normal for this face (there's better ways, I'm sure) */ + ol = f->loopbase; + l = ol->next; + for (i=0,ol=f->loopbase,l=ol->next; l->next!=ol; l=l->next) { + BME_bevel_get_vec(vec1,l->next->v,ol->v,td); + BME_bevel_get_vec(vec2,l->v,ol->v,td); + Crossf(vec3,vec2,vec1); + VECADD(up_vec,up_vec,vec3); + i++; + } + VecMulf(up_vec,1.0f/i); + Normalize(up_vec); + + for (i=0,len=f->len; i<len; i++,l=l->next) { + if ((l->e->tflag1 & BME_BEVEL_BEVEL) && (l->e->tflag1 & BME_BEVEL_ORIG)) { + max = 1.0f; + l = BME_bevel_edge(bm, l, value, options, up_vec, td); + } + else if ((l->v->tflag1 & BME_BEVEL_BEVEL) && (l->v->tflag1 & BME_BEVEL_ORIG) && (l->prev->e->tflag1 & BME_BEVEL_BEVEL) == 0) { + max = 1.0f; + l = BME_bevel_vert(bm, l, value, options, up_vec, td); + } + } + + /* max pass */ + if (value > 0.5 && max > 0) { + max = -1; + for (i=0,len=f->len; i<len; i++,l=l->next) { + if ((l->e->tflag1 & BME_BEVEL_BEVEL) || (l->e->tflag1 & BME_BEVEL_ORIG)) { + BME_bevel_get_vec(vec1,l->v,l->next->v,td); + vtd1 = BME_get_transdata(td,l->v); + vtd2 = BME_get_transdata(td,l->next->v); + if (vtd1->loc == NULL) { + fac1 = 0; + } + else { + VECCOPY(vec2,vtd1->vec); + VecMulf(vec2,vtd1->factor); + if (Inpf(vec1, vec1)) { + Projf(vec2,vec2,vec1); + fac1 = VecLength(vec2)/value; + } + else { + fac1 = 0; + } + } + if (vtd2->loc == NULL) { + fac2 = 0; + } + else { + VECCOPY(vec3,vtd2->vec); + VecMulf(vec3,vtd2->factor); + if (Inpf(vec1, vec1)) { + Projf(vec2,vec3,vec1); + fac2 = VecLength(vec2)/value; + } + else { + fac2 = 0; + } + } + if (fac1 || fac2) { + max = VecLength(vec1)/(fac1 + fac2); + if (vtd1->max && (*vtd1->max < 0 || max < *vtd1->max)) { + *vtd1->max = max; + } + if (vtd2->max && (*vtd2->max < 0 || max < *vtd2->max)) { + *vtd2->max = max; + } + } + } + } + } + + return l->f; +} + +static void BME_bevel_add_vweight(BME_TransData_Head *td, BME_Mesh *bm, BME_Vert *v, float weight, float factor, int options) { + BME_TransData *vtd; + + if (v->tflag1 & BME_BEVEL_NONMAN) return; + v->tflag1 |= BME_BEVEL_BEVEL; + if ( (vtd = BME_get_transdata(td, v)) ) { + if (options & BME_BEVEL_EMIN) { + vtd->factor = 1.0; + if (vtd->weight < 0 || weight < vtd->weight) { + vtd->weight = weight; + } + } + else if (options & BME_BEVEL_EMAX) { + vtd->factor = 1.0; + if (weight > vtd->weight) { + vtd->weight = weight; + } + } + else if (vtd->weight < 0) { + vtd->factor = factor; + vtd->weight = weight; + } + else { + vtd->factor += factor; /* increment number of edges with weights (will be averaged) */ + vtd->weight += weight; /* accumulate all the weights */ + } + } + else { + /* we'll use vtd->loc == NULL to mark that this vert is not moving */ + vtd = BME_assign_transdata(td, bm, v, v->co, NULL, NULL, NULL, factor, weight, -1, NULL); + } +} + +static float BME_bevel_get_angle(BME_Mesh *bm, BME_Edge *e, BME_Vert *v) { + BME_Vert *v1, *v2; + BME_Loop *l1, *l2; + float vec1[3], vec2[3], vec3[3], vec4[3]; + + l1 = e->loop; + l2 = e->loop->radial.next->data; + if (l1->v == v) { + v1 = l1->prev->v; + v2 = l1->next->v; + } + else { + v1 = l1->next->next->v; + v2 = l1->v; + } + VECSUB(vec1,v1->co,v->co); + VECSUB(vec2,v2->co,v->co); + Crossf(vec3,vec1,vec2); + + l1 = l2; + if (l1->v == v) { + v1 = l1->prev->v; + v2 = l1->next->v; + } + else { + v1 = l1->next->next->v; + v2 = l1->v; + } + VECSUB(vec1,v1->co,v->co); + VECSUB(vec2,v2->co,v->co); + Crossf(vec4,vec2,vec1); + + Normalize(vec3); + Normalize(vec4); + + return Inpf(vec3,vec4); +} +static int BME_face_sharededges(BME_Poly *f1, BME_Poly *f2){ + BME_Loop *l; + int count = 0; + + l = f1->loopbase; + do{ + if(BME_radial_find_face(l->e,f2)) count++; + l = l->next; + }while(l != f1->loopbase); + + return count; +} +/** + * BME_bevel_initialize + * + * Prepare the mesh for beveling: + * + * Sets the tflag1's of the mesh elements based on the options passed. + * + * Returns - + * A BME_Mesh pointer to the BMesh passed as a parameter. +*/ +static BME_Mesh *BME_bevel_initialize(BME_Mesh *bm, int options, int defgrp_index, float angle, BME_TransData_Head *td) { + BME_Vert *v; + BME_Edge *e; + BME_Poly *f; + BME_TransData *vtd; + MDeformVert *dvert; + MDeformWeight *dw; + int len; + float weight, threshold; + + /* vert pass */ + for (v=bm->verts.first; v; v=v->next) { + dvert = NULL; + dw = NULL; + v->tflag1 = BME_BEVEL_ORIG; + /* originally coded, a vertex gets tagged with BME_BEVEL_BEVEL in this pass if + * the vert is manifold (or is shared by only two edges - wire bevel) + * BME_BEVEL_SELECT is passed and the vert has v->flag&SELECT or + * BME_BEVEL_VWEIGHT is passed, and the vert has a defgrp and weight + * BME_BEVEL_ANGLE is not passed + * BME_BEVEL_EWEIGHT is not passed + */ + /* originally coded, a vertex gets tagged with BME_BEVEL_NONMAN in this pass if + * the vert is loose, shared by multiple regions, or is shared by wire edges + * note: verts belonging to edges of open meshes are not tagged with BME_BEVEL_NONMAN + */ + /* originally coded, a vertex gets a transform weight set in this pass if + * BME_BEVEL_VWEIGHT is passed, and the vert has a defgrp and weight + */ + + /* get disk cycle length */ + if (v->edge == NULL) { + len = 0; + } + else { + len = BME_cycle_length(BME_disk_getpointer(v->edge,v)); + /* we'll assign a default transform data to every vert (except the loose ones) */ + vtd = BME_assign_transdata(td, bm, v, v->co, v->co, NULL, NULL, 0, -1, -1, NULL); + } + + /* check for non-manifold vert */ + if (BME_is_nonmanifold_vert(bm,v)) { + v->tflag1 |= BME_BEVEL_NONMAN; + } + + /* BME_BEVEL_BEVEL tests */ + if ((v->tflag1 & BME_BEVEL_NONMAN) == 0 || len == 2) { /* either manifold vert, or wire vert */ + if (((options & BME_BEVEL_SELECT) && (v->flag & SELECT)) + || ((options & BME_BEVEL_WEIGHT) && (options & BME_BEVEL_VERT)) /* use weights for verts */ + || ((options & BME_BEVEL_ANGLE) == 0 + && (options & BME_BEVEL_SELECT) == 0 + && (options & BME_BEVEL_WEIGHT) == 0)) + { + if (options & BME_BEVEL_WEIGHT) { + /* do vert weight stuff */ + //~ dvert = CustomData_em_get(&bm->vdata,v->data,CD_MDEFORMVERT); + //~ if (!dvert) continue; + //~ for (i = 0; i < dvert->totweight; ++i) { + //~ if(dvert->dw[i].def_nr == defgrp_index) { + //~ dw = &dvert->dw[i]; + //~ break; + //~ } + //~ } + //~ if (!dw || dw->weight == 0.0) continue; + if (v->bweight == 0.0) continue; + vtd = BME_assign_transdata(td, bm, v, v->co, v->co, NULL, NULL, 1.0, v->bweight, -1, NULL); + v->tflag1 |= BME_BEVEL_BEVEL; + } + else { + vtd = BME_assign_transdata(td, bm, v, v->co, v->co, NULL, NULL, 1.0, 1.0, -1, NULL); + v->tflag1 |= BME_BEVEL_BEVEL; + } + } + } + } + + /* edge pass */ + threshold = (float)cos((angle + 0.00001) * M_PI / 180.0); + for (e=bm->edges.first; e; e=e->next) { + e->tflag1 = BME_BEVEL_ORIG; + weight = 0.0; + /* originally coded, an edge gets tagged with BME_BEVEL_BEVEL in this pass if + * BME_BEVEL_VERT is not set + * the edge is manifold (shared by exactly two faces) + * BME_BEVEL_SELECT is passed and the edge has e->flag&SELECT or + * BME_BEVEL_EWEIGHT is passed, and the edge has the crease set or + * BME_BEVEL_ANGLE is passed, and the edge is sharp enough + * BME_BEVEL_VWEIGHT is passed, and both verts are set for bevel + */ + /* originally coded, a vertex gets tagged with BME_BEVEL_BEVEL in this pass if + * the vert belongs to the edge + * the vert is not tagged with BME_BEVEL_NONMAN + * the edge is eligible for bevel (even if BME_BEVEL_VERT is set, or the edge is shared by less than 2 faces) + */ + /* originally coded, a vertex gets a transform weight set in this pass if + * the vert belongs to the edge + * the edge has a weight + */ + /* note: edge weights are cumulative at the verts, + * i.e. the vert's weight is the average of the weights of its weighted edges + */ + + if (e->loop == NULL) { + len = 0; + e->v1->tflag1 |= BME_BEVEL_NONMAN; + e->v2->tflag1 |= BME_BEVEL_NONMAN; + } + else { + len = BME_cycle_length(&(e->loop->radial)); + } + + if (len > 2) { + /* non-manifold edge of the worst kind */ + continue; + } + + if ((options & BME_BEVEL_SELECT) && (e->flag & SELECT)) { + weight = 1.0; + /* stupid editmode doesn't always flush selections, or something */ + e->v1->flag |= SELECT; + e->v2->flag |= SELECT; + } + else if ((options & BME_BEVEL_WEIGHT) && (options & BME_BEVEL_VERT) == 0) { + weight = e->bweight; + } + else if (options & BME_BEVEL_ANGLE) { + if ((e->v1->tflag1 & BME_BEVEL_NONMAN) == 0 && BME_bevel_get_angle(bm,e,e->v1) < threshold) { + e->tflag1 |= BME_BEVEL_BEVEL; + e->v1->tflag1 |= BME_BEVEL_BEVEL; + BME_bevel_add_vweight(td, bm, e->v1, 1.0, 1.0, options); + } + else { + BME_bevel_add_vweight(td, bm, e->v1, 0.0, 1.0, options); + } + if ((e->v2->tflag1 & BME_BEVEL_NONMAN) == 0 && BME_bevel_get_angle(bm,e,e->v2) < threshold) { + e->tflag1 |= BME_BEVEL_BEVEL; + e->v2->tflag1 |= BME_BEVEL_BEVEL; + BME_bevel_add_vweight(td, bm, e->v2, 1.0, 1.0, options); + } + else { + BME_bevel_add_vweight(td, bm, e->v2, 0.0, 1.0, options); + } + } + //~ else if ((options & BME_BEVEL_VWEIGHT) && (options & BME_BEVEL_VERT) == 0) { + //~ if ((e->v1->tflag1 & BME_BEVEL_BEVEL) && (e->v2->tflag1 & BME_BEVEL_BEVEL)) { + //~ e->tflag1 |= BME_BEVEL_BEVEL; + //~ } + //~ } + else if ((options & BME_BEVEL_SELECT) == 0 + && (options & BME_BEVEL_VERT) == 0) + { + weight = 1.0; + } + + if (weight > 0.0) { + e->tflag1 |= BME_BEVEL_BEVEL; + BME_bevel_add_vweight(td, bm, e->v1, weight, 1.0, options); + BME_bevel_add_vweight(td, bm, e->v2, weight, 1.0, options); + } + + if (len != 2 || options & BME_BEVEL_VERT) { + e->tflag1 &= ~BME_BEVEL_BEVEL; + } + } + + /* face pass */ + for (f=bm->polys.first; f; f=f->next) f->tflag1 = BME_BEVEL_ORIG; + + /*clean up edges with 2 faces that share more than one edge*/ + for (e=bm->edges.first; e; e=e->next){ + if(e->tflag1 & BME_BEVEL_BEVEL){ + int count = 0; + count = BME_face_sharededges(e->loop->f, ((BME_Loop*)e->loop->radial.next->data)->f); + if(count > 1){ + e->tflag1 &= ~BME_BEVEL_BEVEL; + } + } + } + + return bm; +} + +/* tags all elements as originals */ +static BME_Mesh *BME_bevel_reinitialize(BME_Mesh *bm) { + BME_Vert *v; + BME_Edge *e; + BME_Poly *f; + + for (v = bm->verts.first; v; v=v->next) { + v->tflag1 |= BME_BEVEL_ORIG; + } + + for (e=bm->edges.first; e; e=e->next) { + e->tflag1 |= BME_BEVEL_ORIG; + } + + for (f=bm->polys.first; f; f=f->next) { + f->tflag1 |= BME_BEVEL_ORIG; + } + + return bm; +} + +/** + * BME_bevel_mesh + * + * Mesh beveling tool: + * + * Bevels an entire mesh. It currently uses the tflag1's of + * its vertices and edges to track topological changes. + * The parameter "value" is the distance to inset (should be negative). + * The parameter "options" is not currently used. + * + * Returns - + * A BME_Mesh pointer to the BMesh passed as a parameter. +*/ + +static void bmesh_dissolve_disk(BME_Mesh *bm, BME_Vert *v){ + BME_Poly *f; + BME_Edge *e; + int done, len; + + if(v->edge){ + done = 0; + while(!done){ + done = 1; + e = v->edge; /*loop the edge looking for a edge to dissolve*/ + do{ + f = NULL; + len = BME_cycle_length(&(e->loop->radial)); + if(len == 2){ + f = BME_JFKE_safe(bm,e->loop->f, ((BME_Loop*)(e->loop->radial.next->data))->f, e); + } + if(f){ + done = 0; + break; + } + e = BME_disk_nextedge(e,v); + }while(e != v->edge); + } + BME_collapse_vert(bm, v->edge, v, 1.0); + //BME_JEKV(bm,v->edge,v); + } +} +static BME_Mesh *BME_bevel_mesh(BME_Mesh *bm, float value, int res, int options, int defgrp_index, BME_TransData_Head *td) { + BME_Vert *v, *nv; + BME_Edge *e, *oe; + BME_Loop *l, *l2; + BME_Poly *f; + unsigned int i, len; + + for (f=bm->polys.first; f; f=f->next) { + if(f->tflag1 & BME_BEVEL_ORIG) { + BME_bevel_poly(bm,f,value,options,td); + } + } + + /* here we will loop through all the verts to clean up the left over geometry */ + /* crazy idea. when res == 0, don't remove the original geometry */ + for (v = bm->verts.first; v; /* we may kill v, so increment in-loop */) { + nv = v->next; + if ((v->tflag1 & BME_BEVEL_NONMAN) && (v->tflag1 & BME_BEVEL_BEVEL) && (v->tflag1 & BME_BEVEL_ORIG)) { + v = BME_bevel_wire(bm, v, value, res, options, td); + } + else if (res && ((v->tflag1 & BME_BEVEL_BEVEL) && (v->tflag1 & BME_BEVEL_ORIG))) { + int count = 0; + /* first, make sure we're not sitting on an edge to be removed */ + oe = v->edge; + e = BME_disk_nextedge(oe,v); + while ((e->tflag1 & BME_BEVEL_BEVEL) && (e->tflag1 & BME_BEVEL_ORIG)) { + e = BME_disk_nextedge(e,v); + if (e == oe) { + //printf("Something's wrong! We can't remove every edge here!\n"); + break; + } + } + /* look for original edges, and remove them */ + oe = e; + while ( (e = BME_disk_next_edgeflag(oe, v, 0, BME_BEVEL_ORIG | BME_BEVEL_BEVEL)) ) { + count++; + /* join the faces (we'll split them later) */ + f = BME_JFKE_safe(bm,e->loop->f,((BME_Loop*)e->loop->radial.next->data)->f,e); + if (!f){ + //printf("Non-manifold geometry not getting tagged right?\n"); + } + } + + /*need to do double check *before* you bevel to make sure that manifold edges are for two faces that share only *one* edge to make sure it doesnt hang here!*/ + + + /* all original edges marked to be beveled have been removed; + * now we need to link up the edges for this "corner" */ + len = BME_cycle_length(BME_disk_getpointer(v->edge, v)); + for (i=0,e=v->edge; i < len; i++,e=BME_disk_nextedge(e,v)) { + l = e->loop; + l2 = l->radial.next->data; + if (l->v != v) l = l->next; + if (l2->v != v) l2 = l2->next; + /* look for faces that have had the original edges removed via JFKE */ + if (l->f->len > 3) { + BME_split_face(bm,l->f,l->next->v,l->prev->v,&l,l->e); /* clip this corner off */ + if (len > 2) { + l->e->tflag1 |= BME_BEVEL_BEVEL; + } + } + if (l2->f->len > 3) { + BME_split_face(bm,l2->f,l2->next->v,l2->prev->v,&l,l2->e); /* clip this corner off */ + if (len > 2) { + l->e->tflag1 |= BME_BEVEL_BEVEL; + } + } + } + bmesh_dissolve_disk(bm, v); + } + v = nv; + } + + return bm; +} + +static BME_Mesh *BME_tesselate(BME_Mesh *bm) { + BME_Loop *l, *nextloop; + BME_Poly *f; + + for (f=bm->polys.first; f; f=f->next) { + l = f->loopbase; + while (l->f->len > 4) { + nextloop = l->next->next->next; + /* make a quad */ + BME_split_face(bm,l->f,l->v,nextloop->v,NULL,l->e); + l = nextloop; + } + } + return bm; +} + + +/*Main bevel function: + Should be only one exported + +*/ + +/* options that can be passed: + * BME_BEVEL_VWEIGHT <---- v, Look at vertex weights; use defgrp_index if option is present + * BME_BEVEL_SELECT <---- v,e, check selection for verts and edges + * BME_BEVEL_ANGLE <---- v,e, don't bevel-tag verts - tag verts per edge + * BME_BEVEL_VERT <---- e, don't tag edges + * BME_BEVEL_EWEIGHT <---- e, use crease flag for now + * BME_BEVEL_PERCENT <---- Will need to think about this one; will probably need to incorporate into actual bevel routine + * BME_BEVEL_RADIUS <---- Will need to think about this one; will probably need to incorporate into actual bevel routine + * All weights/limits are stored per-vertex + */ +BME_Mesh *BME_bevel(BME_Mesh *bm, float value, int res, int options, int defgrp_index, float angle, BME_TransData_Head **rtd) { + BME_Vert *v; + BME_TransData_Head *td; + BME_TransData *vtd; + int i; + float fac=1, d; + + td = BME_init_transdata(BLI_MEMARENA_STD_BUFSIZE); + + BME_bevel_initialize(bm, options, defgrp_index, angle, td); + + /* recursion math courtesy of Martin Poirier (theeth) */ + for (i=0; i<res-1; i++) { + if (i==0) fac += 1.0f/3.0f; else fac += 1.0f/(3 * i * 2.0f); + } + d = 1.0f/fac; + /* crazy idea. if res == 0, don't remove original geometry */ + for (i=0; i<res || (res==0 && i==0); i++) { + if (i != 0) BME_bevel_reinitialize(bm); + BME_model_begin(bm); + BME_bevel_mesh(bm,d,res,options,defgrp_index,td); + BME_model_end(bm); + if (i==0) d /= 3; else d /= 2; + } + + BME_tesselate(bm); + + if (rtd) { + *rtd = td; + return bm; + } + + /* transform pass */ + for (v = bm->verts.first; v; v=v->next) { + if ( (vtd = BME_get_transdata(td, v)) ) { + if (vtd->max && (*vtd->max > 0 && value > *vtd->max)) { + d = *vtd->max; + } + else { + d = value; + } + VECADDFAC(v->co,vtd->org,vtd->vec,vtd->factor*d); + } + v->tflag1 = 0; + } + + BME_free_transdata(td); + return bm; +} diff --git a/source/blender/blenkernel/intern/CCGSubSurf.c b/source/blender/blenkernel/intern/CCGSubSurf.c index 4e00e29029c..cee032f364e 100644 --- a/source/blender/blenkernel/intern/CCGSubSurf.c +++ b/source/blender/blenkernel/intern/CCGSubSurf.c @@ -7,6 +7,8 @@ #include "CCGSubSurf.h" +#include "BLO_sys_types.h" // for intptr_t support + /***/ typedef unsigned char byte; @@ -35,7 +37,7 @@ typedef struct _EHash { #define EHASH_alloc(eh, nb) ((eh)->allocatorIFC.alloc((eh)->allocator, nb)) #define EHASH_free(eh, ptr) ((eh)->allocatorIFC.free((eh)->allocator, ptr)) -#define EHASH_hash(eh, item) (((unsigned long) (item))%((unsigned int) (eh)->curSize)) +#define EHASH_hash(eh, item) (((uintptr_t) (item))%((unsigned int) (eh)->curSize)) static EHash *_ehash_new(int estimatedNumEntries, CCGAllocatorIFC *allocatorIFC, CCGAllocatorHDL allocator) { EHash *eh = allocatorIFC->alloc(allocator, sizeof(*eh)); @@ -199,21 +201,21 @@ static CCGAllocatorIFC *_getStandardAllocatorIFC(void) { static int VertDataEqual(float *a, float *b) { return a[0]==b[0] && a[1]==b[1] && a[2]==b[2]; } -#define VertDataZero(av) { float *a = (float*) av; a[0] = a[1] = a[2] = 0.0f; } -#define VertDataCopy(av, bv) { float *a = (float*) av, *b = (float*) bv; a[0] =b[0]; a[1] =b[1]; a[2] =b[2]; } -#define VertDataAdd(av, bv) { float *a = (float*) av, *b = (float*) bv; a[0]+=b[0]; a[1]+=b[1]; a[2]+=b[2]; } -#define VertDataSub(av, bv) { float *a = (float*) av, *b = (float*) bv; a[0]-=b[0]; a[1]-=b[1]; a[2]-=b[2]; } -#define VertDataMulN(av, n) { float *a = (float*) av; a[0]*=n; a[1]*=n; a[2]*=n; } +#define VertDataZero(av) { float *_a = (float*) av; _a[0] = _a[1] = _a[2] = 0.0f; } +#define VertDataCopy(av, bv) { float *_a = (float*) av, *_b = (float*) bv; _a[0] =_b[0]; _a[1] =_b[1]; _a[2] =_b[2]; } +#define VertDataAdd(av, bv) { float *_a = (float*) av, *_b = (float*) bv; _a[0]+=_b[0]; _a[1]+=_b[1]; _a[2]+=_b[2]; } +#define VertDataSub(av, bv) { float *_a = (float*) av, *_b = (float*) bv; _a[0]-=_b[0]; _a[1]-=_b[1]; _a[2]-=_b[2]; } +#define VertDataMulN(av, n) { float *_a = (float*) av; _a[0]*=n; _a[1]*=n; _a[2]*=n; } #define VertDataAvg4(tv, av, bv, cv, dv) \ { \ - float *t = (float*) tv, *a = (float*) av, *b = (float*) bv, *c = (float*) cv, *d = (float*) dv; \ - t[0] = (a[0]+b[0]+c[0]+d[0])*.25; \ - t[1] = (a[1]+b[1]+c[1]+d[1])*.25; \ - t[2] = (a[2]+b[2]+c[2]+d[2])*.25; \ + float *_t = (float*) tv, *_a = (float*) av, *_b = (float*) bv, *_c = (float*) cv, *_d = (float*) dv; \ + _t[0] = (_a[0]+_b[0]+_c[0]+_d[0])*.25; \ + _t[1] = (_a[1]+_b[1]+_c[1]+_d[1])*.25; \ + _t[2] = (_a[2]+_b[2]+_c[2]+_d[2])*.25; \ } -#define NormZero(av) { float *a = (float*) av; a[0] = a[1] = a[2] = 0.0f; } -#define NormCopy(av, bv) { float *a = (float*) av, *b = (float*) bv; a[0] =b[0]; a[1] =b[1]; a[2] =b[2]; } -#define NormAdd(av, bv) { float *a = (float*) av, *b = (float*) bv; a[0]+=b[0]; a[1]+=b[1]; a[2]+=b[2]; } +#define NormZero(av) { float *_a = (float*) av; _a[0] = _a[1] = _a[2] = 0.0f; } +#define NormCopy(av, bv) { float *_a = (float*) av, *_b = (float*) bv; _a[0] =_b[0]; _a[1] =_b[1]; _a[2] =_b[2]; } +#define NormAdd(av, bv) { float *_a = (float*) av, *_b = (float*) bv; _a[0]+=_b[0]; _a[1]+=_b[1]; _a[2]+=_b[2]; } static int _edge_isBoundary(CCGEdge *e); @@ -328,7 +330,7 @@ struct _CCGSubSurf { /***/ -static CCGVert *_vert_new(CCGVertHDL vHDL, int levels, int dataSize, CCGSubSurf *ss) { +static CCGVert *_vert_new(CCGVertHDL vHDL, CCGSubSurf *ss) { CCGVert *v = CCGSUBSURF_alloc(ss, sizeof(CCGVert) + ss->meshIFC.vertDataSize * (ss->subdivLevels+1) + ss->meshIFC.vertUserSize); byte *userData; @@ -344,7 +346,7 @@ static CCGVert *_vert_new(CCGVertHDL vHDL, int levels, int dataSize, CCGSubSurf return v; } -static void _vert_remEdge(CCGVert *v, CCGEdge *e, CCGSubSurf *ss) { +static void _vert_remEdge(CCGVert *v, CCGEdge *e) { int i; for (i=0; i<v->numEdges; i++) { if (v->edges[i]==e) { @@ -353,7 +355,7 @@ static void _vert_remEdge(CCGVert *v, CCGEdge *e, CCGSubSurf *ss) { } } } -static void _vert_remFace(CCGVert *v, CCGFace *f, CCGSubSurf *ss) { +static void _vert_remFace(CCGVert *v, CCGFace *f) { int i; for (i=0; i<v->numFaces; i++) { if (v->faces[i]==f) { @@ -401,13 +403,13 @@ static void _vert_free(CCGVert *v, CCGSubSurf *ss) { CCGSUBSURF_free(ss, v); } -static int VERT_seam(CCGVert *v, CCGSubSurf *ss) { +static int VERT_seam(CCGVert *v) { return ((v->flags & Vert_eSeam) != 0); } /***/ -static CCGEdge *_edge_new(CCGEdgeHDL eHDL, CCGVert *v0, CCGVert *v1, float crease, int levels, int dataSize, CCGSubSurf *ss) { +static CCGEdge *_edge_new(CCGEdgeHDL eHDL, CCGVert *v0, CCGVert *v1, float crease, CCGSubSurf *ss) { CCGEdge *e = CCGSUBSURF_alloc(ss, sizeof(CCGEdge) + ss->meshIFC.vertDataSize *((ss->subdivLevels+1) + (1<<(ss->subdivLevels+1))-1) + ss->meshIFC.edgeUserSize); byte *userData; @@ -427,7 +429,7 @@ static CCGEdge *_edge_new(CCGEdgeHDL eHDL, CCGVert *v0, CCGVert *v1, float creas return e; } -static void _edge_remFace(CCGEdge *e, CCGFace *f, CCGSubSurf *ss) { +static void _edge_remFace(CCGEdge *e, CCGFace *f) { int i; for (i=0; i<e->numFaces; i++) { if (e->faces[i]==f) { @@ -476,8 +478,8 @@ static void _edge_free(CCGEdge *e, CCGSubSurf *ss) { CCGSUBSURF_free(ss, e); } static void _edge_unlinkMarkAndFree(CCGEdge *e, CCGSubSurf *ss) { - _vert_remEdge(e->v0, e, ss); - _vert_remEdge(e->v1, e, ss); + _vert_remEdge(e->v0, e); + _vert_remEdge(e->v1, e); e->v0->flags |= Vert_eEffected; e->v1->flags |= Vert_eEffected; _edge_free(e, ss); @@ -494,7 +496,7 @@ static float EDGE_getSharpness(CCGEdge *e, int lvl) { return e->crease - lvl; } -static CCGFace *_face_new(CCGFaceHDL fHDL, CCGVert **verts, CCGEdge **edges, int numVerts, int levels, int dataSize, CCGSubSurf *ss) { +static CCGFace *_face_new(CCGFaceHDL fHDL, CCGVert **verts, CCGEdge **edges, int numVerts, CCGSubSurf *ss) { int maxGridSize = 1 + (1<<(ss->subdivLevels-1)); CCGFace *f = CCGSUBSURF_alloc(ss, sizeof(CCGFace) + sizeof(CCGVert*)*numVerts + sizeof(CCGEdge*)*numVerts + ss->meshIFC.vertDataSize *(1 + numVerts*maxGridSize + numVerts*maxGridSize*maxGridSize) + ss->meshIFC.faceUserSize); byte *userData; @@ -608,8 +610,8 @@ static void _face_free(CCGFace *f, CCGSubSurf *ss) { static void _face_unlinkMarkAndFree(CCGFace *f, CCGSubSurf *ss) { int j; for (j=0; j<f->numVerts; j++) { - _vert_remFace(FACE_getVerts(f)[j], f, ss); - _edge_remFace(FACE_getEdges(f)[j], f, ss); + _vert_remFace(FACE_getVerts(f)[j], f); + _edge_remFace(FACE_getEdges(f)[j], f); FACE_getVerts(f)[j]->flags |= Vert_eEffected; } _face_free(f, ss); @@ -882,7 +884,7 @@ CCGError ccgSubSurf_syncVert(CCGSubSurf *ss, CCGVertHDL vHDL, void *vertData, in if (ss->syncState==eSyncState_Partial) { v = _ehash_lookupWithPrev(ss->vMap, vHDL, &prevp); if (!v) { - v = _vert_new(vHDL, ss->subdivLevels, ss->meshIFC.vertDataSize, ss); + v = _vert_new(vHDL, ss); VertDataCopy(_vert_getCo(v,0,ss->meshIFC.vertDataSize), vertData); _ehash_insert(ss->vMap, (EHEntry*) v); v->flags = Vert_eEffected|seamflag; @@ -911,7 +913,7 @@ CCGError ccgSubSurf_syncVert(CCGSubSurf *ss, CCGVertHDL vHDL, void *vertData, in v = _ehash_lookupWithPrev(ss->oldVMap, vHDL, &prevp); if (!v) { - v = _vert_new(vHDL, ss->subdivLevels, ss->meshIFC.vertDataSize, ss); + v = _vert_new(vHDL, ss); VertDataCopy(_vert_getCo(v,0,ss->meshIFC.vertDataSize), vertData); _ehash_insert(ss->vMap, (EHEntry*) v); v->flags = Vert_eEffected|seamflag; @@ -941,7 +943,7 @@ CCGError ccgSubSurf_syncEdge(CCGSubSurf *ss, CCGEdgeHDL eHDL, CCGVertHDL e_vHDL0 CCGVert *v0 = _ehash_lookup(ss->vMap, e_vHDL0); CCGVert *v1 = _ehash_lookup(ss->vMap, e_vHDL1); - eNew = _edge_new(eHDL, v0, v1, crease, ss->subdivLevels, ss->meshIFC.vertDataSize, ss); + eNew = _edge_new(eHDL, v0, v1, crease, ss); if (e) { *prevp = eNew; @@ -966,7 +968,7 @@ CCGError ccgSubSurf_syncEdge(CCGSubSurf *ss, CCGEdgeHDL eHDL, CCGVertHDL e_vHDL0 if (!e || e->v0->vHDL!=e_vHDL0 || e->v1->vHDL!=e_vHDL1|| e->crease!=crease) { CCGVert *v0 = _ehash_lookup(ss->vMap, e_vHDL0); CCGVert *v1 = _ehash_lookup(ss->vMap, e_vHDL1); - e = _edge_new(eHDL, v0, v1, crease, ss->subdivLevels, ss->meshIFC.vertDataSize, ss); + e = _edge_new(eHDL, v0, v1, crease, ss); _ehash_insert(ss->eMap, (EHEntry*) e); e->v0->flags |= Vert_eEffected; e->v1->flags |= Vert_eEffected; @@ -1015,7 +1017,7 @@ CCGError ccgSubSurf_syncFace(CCGSubSurf *ss, CCGFaceHDL fHDL, int numVerts, CCGV } if (!f || topologyChanged) { - fNew = _face_new(fHDL, ss->tempVerts, ss->tempEdges, numVerts, ss->subdivLevels, ss->meshIFC.vertDataSize, ss); + fNew = _face_new(fHDL, ss->tempVerts, ss->tempEdges, numVerts, ss); if (f) { ss->numGrids += numVerts - f->numVerts; @@ -1052,7 +1054,7 @@ CCGError ccgSubSurf_syncFace(CCGSubSurf *ss, CCGFaceHDL fHDL, int numVerts, CCGV if (!ss->tempEdges[k]) { if (ss->allowEdgeCreation) { - CCGEdge *e = ss->tempEdges[k] = _edge_new((CCGEdgeHDL) -1, ss->tempVerts[k], ss->tempVerts[(k+1)%numVerts], ss->defaultCreaseValue, ss->subdivLevels, ss->meshIFC.vertDataSize, ss); + CCGEdge *e = ss->tempEdges[k] = _edge_new((CCGEdgeHDL) -1, ss->tempVerts[k], ss->tempVerts[(k+1)%numVerts], ss->defaultCreaseValue, ss); _ehash_insert(ss->eMap, (EHEntry*) e); e->v0->flags |= Vert_eEffected; e->v1->flags |= Vert_eEffected; @@ -1073,7 +1075,7 @@ CCGError ccgSubSurf_syncFace(CCGSubSurf *ss, CCGFaceHDL fHDL, int numVerts, CCGV } if (!f || topologyChanged) { - f = _face_new(fHDL, ss->tempVerts, ss->tempEdges, numVerts, ss->subdivLevels, ss->meshIFC.vertDataSize, ss); + f = _face_new(fHDL, ss->tempVerts, ss->tempEdges, numVerts, ss); _ehash_insert(ss->fMap, (EHEntry*) f); ss->numGrids += numVerts; @@ -1226,7 +1228,7 @@ static void ccgSubSurf__sync(CCGSubSurf *ss) { void *nCo = VERT_getCo(v, nextLvl); int sharpCount = 0, allSharp = 1; float avgSharpness = 0.0; - int seam = VERT_seam(v, ss), seamEdges = 0; + int seam = VERT_seam(v), seamEdges = 0; for (i=0; i<v->numEdges; i++) { CCGEdge *e = v->edges[i]; @@ -1243,9 +1245,11 @@ static void ccgSubSurf__sync(CCGSubSurf *ss) { } } - avgSharpness /= sharpCount; - if (avgSharpness>1.0) { - avgSharpness = 1.0; + if(sharpCount) { + avgSharpness /= sharpCount; + if (avgSharpness>1.0) { + avgSharpness = 1.0; + } } if (seam && seamEdges < 2) @@ -1293,7 +1297,7 @@ static void ccgSubSurf__sync(CCGSubSurf *ss) { VertDataMulN(nCo, 1.0f/numEdges); } - if ((sharpCount>1 && v->numFaces) || seam) { + if (sharpCount>1 || seam) { VertDataZero(q); if (seam) { @@ -1524,7 +1528,7 @@ static void ccgSubSurf__sync(CCGSubSurf *ss) { void *nCo = VERT_getCo(v, nextLvl); int sharpCount = 0, allSharp = 1; float avgSharpness = 0.0; - int seam = VERT_seam(v, ss), seamEdges = 0; + int seam = VERT_seam(v), seamEdges = 0; for (i=0; i<v->numEdges; i++) { CCGEdge *e = v->edges[i]; @@ -1541,9 +1545,11 @@ static void ccgSubSurf__sync(CCGSubSurf *ss) { } } - avgSharpness /= sharpCount; - if (avgSharpness>1.0) { - avgSharpness = 1.0; + if(sharpCount) { + avgSharpness /= sharpCount; + if (avgSharpness>1.0) { + avgSharpness = 1.0; + } } if (seam && seamEdges < 2) @@ -2069,7 +2075,7 @@ int ccgSubSurf_getGridLevelSize(CCGSubSurf *ss, int level) { /* Vert accessors */ -CCGVertHDL ccgSubSurf_getVertVertHandle(CCGSubSurf *ss, CCGVert *v) { +CCGVertHDL ccgSubSurf_getVertVertHandle(CCGVert *v) { return v->vHDL; } int ccgSubSurf_getVertAge(CCGSubSurf *ss, CCGVert *v) { @@ -2083,20 +2089,20 @@ int ccgSubSurf_getVertAge(CCGSubSurf *ss, CCGVert *v) { void *ccgSubSurf_getVertUserData(CCGSubSurf *ss, CCGVert *v) { return VERT_getLevelData(v) + ss->meshIFC.vertDataSize*(ss->subdivLevels+1); } -int ccgSubSurf_getVertNumFaces(CCGSubSurf *ss, CCGVert *v) { +int ccgSubSurf_getVertNumFaces(CCGVert *v) { return v->numFaces; } -CCGFace *ccgSubSurf_getVertFace(CCGSubSurf *ss, CCGVert *v, int index) { +CCGFace *ccgSubSurf_getVertFace(CCGVert *v, int index) { if (index<0 || index>=v->numFaces) { return NULL; } else { return v->faces[index]; } } -int ccgSubSurf_getVertNumEdges(CCGSubSurf *ss, CCGVert *v) { +int ccgSubSurf_getVertNumEdges(CCGVert *v) { return v->numEdges; } -CCGEdge *ccgSubSurf_getVertEdge(CCGSubSurf *ss, CCGVert *v, int index) { +CCGEdge *ccgSubSurf_getVertEdge(CCGVert *v, int index) { if (index<0 || index>=v->numEdges) { return NULL; } else { @@ -2116,7 +2122,7 @@ void *ccgSubSurf_getVertLevelData(CCGSubSurf *ss, CCGVert *v, int level) { /* Edge accessors */ -CCGEdgeHDL ccgSubSurf_getEdgeEdgeHandle(CCGSubSurf *ss, CCGEdge *e) { +CCGEdgeHDL ccgSubSurf_getEdgeEdgeHandle(CCGEdge *e) { return e->eHDL; } int ccgSubSurf_getEdgeAge(CCGSubSurf *ss, CCGEdge *e) { @@ -2130,20 +2136,20 @@ int ccgSubSurf_getEdgeAge(CCGSubSurf *ss, CCGEdge *e) { void *ccgSubSurf_getEdgeUserData(CCGSubSurf *ss, CCGEdge *e) { return EDGE_getLevelData(e) + ss->meshIFC.vertDataSize *((ss->subdivLevels+1) + (1<<(ss->subdivLevels+1))-1); } -int ccgSubSurf_getEdgeNumFaces(CCGSubSurf *ss, CCGEdge *e) { +int ccgSubSurf_getEdgeNumFaces(CCGEdge *e) { return e->numFaces; } -CCGFace *ccgSubSurf_getEdgeFace(CCGSubSurf *ss, CCGEdge *e, int index) { +CCGFace *ccgSubSurf_getEdgeFace(CCGEdge *e, int index) { if (index<0 || index>=e->numFaces) { return NULL; } else { return e->faces[index]; } } -CCGVert *ccgSubSurf_getEdgeVert0(CCGSubSurf *ss, CCGEdge *e) { +CCGVert *ccgSubSurf_getEdgeVert0(CCGEdge *e) { return e->v0; } -CCGVert *ccgSubSurf_getEdgeVert1(CCGSubSurf *ss, CCGEdge *e) { +CCGVert *ccgSubSurf_getEdgeVert1(CCGEdge *e) { return e->v1; } void *ccgSubSurf_getEdgeDataArray(CCGSubSurf *ss, CCGEdge *e) { @@ -2159,7 +2165,7 @@ void *ccgSubSurf_getEdgeLevelData(CCGSubSurf *ss, CCGEdge *e, int x, int level) return _edge_getCo(e, level, x, ss->meshIFC.vertDataSize); } } -float ccgSubSurf_getEdgeCrease(CCGSubSurf *ss, CCGEdge *e) { +float ccgSubSurf_getEdgeCrease(CCGEdge *e) { return e->crease; } @@ -2180,7 +2186,7 @@ void *ccgSubSurf_getFaceUserData(CCGSubSurf *ss, CCGFace *f) { int maxGridSize = 1 + (1<<(ss->subdivLevels-1)); return FACE_getCenterData(f) + ss->meshIFC.vertDataSize *(1 + f->numVerts*maxGridSize + f->numVerts*maxGridSize*maxGridSize); } -int ccgSubSurf_getFaceNumVerts(CCGSubSurf *ss, CCGFace *f) { +int ccgSubSurf_getFaceNumVerts(CCGFace *f) { return f->numVerts; } CCGVert *ccgSubSurf_getFaceVert(CCGSubSurf *ss, CCGFace *f, int index) { @@ -2197,7 +2203,7 @@ CCGEdge *ccgSubSurf_getFaceEdge(CCGSubSurf *ss, CCGFace *f, int index) { return FACE_getEdges(f)[index]; } } -int ccgSubSurf_getFaceEdgeIndex(CCGSubSurf *ss, CCGFace *f, CCGEdge *e) { +int ccgSubSurf_getFaceEdgeIndex(CCGFace *f, CCGEdge *e) { int i; for (i=0; i<f->numVerts; i++) @@ -2206,7 +2212,7 @@ int ccgSubSurf_getFaceEdgeIndex(CCGSubSurf *ss, CCGFace *f, CCGEdge *e) { return -1; } -void *ccgSubSurf_getFaceCenterData(CCGSubSurf *ss, CCGFace *f) { +void *ccgSubSurf_getFaceCenterData(CCGFace *f) { return FACE_getCenterData(f); } void *ccgSubSurf_getFaceGridEdgeDataArray(CCGSubSurf *ss, CCGFace *f, int gridIndex) { diff --git a/source/blender/blenkernel/intern/CCGSubSurf.h b/source/blender/blenkernel/intern/CCGSubSurf.h index 91f3ffab43b..fbd0aecc0a5 100644 --- a/source/blender/blenkernel/intern/CCGSubSurf.h +++ b/source/blender/blenkernel/intern/CCGSubSurf.h @@ -82,11 +82,11 @@ int ccgSubSurf_getGridSize (CCGSubSurf *ss); int ccgSubSurf_getGridLevelSize (CCGSubSurf *ss, int level); CCGVert* ccgSubSurf_getVert (CCGSubSurf *ss, CCGVertHDL v); -CCGVertHDL ccgSubSurf_getVertVertHandle (CCGSubSurf *ss, CCGVert *v); -int ccgSubSurf_getVertNumFaces (CCGSubSurf *ss, CCGVert *v); -CCGFace* ccgSubSurf_getVertFace (CCGSubSurf *ss, CCGVert *v, int index); -int ccgSubSurf_getVertNumEdges (CCGSubSurf *ss, CCGVert *v); -CCGEdge* ccgSubSurf_getVertEdge (CCGSubSurf *ss, CCGVert *v, int index); +CCGVertHDL ccgSubSurf_getVertVertHandle (CCGVert *v); +int ccgSubSurf_getVertNumFaces (CCGVert *v); +CCGFace* ccgSubSurf_getVertFace (CCGVert *v, int index); +int ccgSubSurf_getVertNumEdges (CCGVert *v); +CCGEdge* ccgSubSurf_getVertEdge (CCGVert *v, int index); int ccgSubSurf_getVertAge (CCGSubSurf *ss, CCGVert *v); void* ccgSubSurf_getVertUserData (CCGSubSurf *ss, CCGVert *v); @@ -94,12 +94,12 @@ void* ccgSubSurf_getVertData (CCGSubSurf *ss, CCGVert *v); void* ccgSubSurf_getVertLevelData (CCGSubSurf *ss, CCGVert *v, int level); CCGEdge* ccgSubSurf_getEdge (CCGSubSurf *ss, CCGEdgeHDL e); -CCGEdgeHDL ccgSubSurf_getEdgeEdgeHandle (CCGSubSurf *ss, CCGEdge *e); -int ccgSubSurf_getEdgeNumFaces (CCGSubSurf *ss, CCGEdge *e); -CCGFace* ccgSubSurf_getEdgeFace (CCGSubSurf *ss, CCGEdge *e, int index); -CCGVert* ccgSubSurf_getEdgeVert0 (CCGSubSurf *ss, CCGEdge *e); -CCGVert* ccgSubSurf_getEdgeVert1 (CCGSubSurf *ss, CCGEdge *e); -float ccgSubSurf_getEdgeCrease (CCGSubSurf *ss, CCGEdge *e); +CCGEdgeHDL ccgSubSurf_getEdgeEdgeHandle (CCGEdge *e); +int ccgSubSurf_getEdgeNumFaces (CCGEdge *e); +CCGFace* ccgSubSurf_getEdgeFace (CCGEdge *e, int index); +CCGVert* ccgSubSurf_getEdgeVert0 (CCGEdge *e); +CCGVert* ccgSubSurf_getEdgeVert1 (CCGEdge *e); +float ccgSubSurf_getEdgeCrease (CCGEdge *e); int ccgSubSurf_getEdgeAge (CCGSubSurf *ss, CCGEdge *e); void* ccgSubSurf_getEdgeUserData (CCGSubSurf *ss, CCGEdge *e); @@ -109,14 +109,14 @@ void* ccgSubSurf_getEdgeLevelData (CCGSubSurf *ss, CCGEdge *e, int x, int lev CCGFace* ccgSubSurf_getFace (CCGSubSurf *ss, CCGFaceHDL f); CCGFaceHDL ccgSubSurf_getFaceFaceHandle (CCGSubSurf *ss, CCGFace *f); -int ccgSubSurf_getFaceNumVerts (CCGSubSurf *ss, CCGFace *f); +int ccgSubSurf_getFaceNumVerts (CCGFace *f); CCGVert* ccgSubSurf_getFaceVert (CCGSubSurf *ss, CCGFace *f, int index); CCGEdge* ccgSubSurf_getFaceEdge (CCGSubSurf *ss, CCGFace *f, int index); -int ccgSubSurf_getFaceEdgeIndex (CCGSubSurf *ss, CCGFace *f, CCGEdge *e); +int ccgSubSurf_getFaceEdgeIndex (CCGFace *f, CCGEdge *e); int ccgSubSurf_getFaceAge (CCGSubSurf *ss, CCGFace *f); void* ccgSubSurf_getFaceUserData (CCGSubSurf *ss, CCGFace *f); -void* ccgSubSurf_getFaceCenterData (CCGSubSurf *ss, CCGFace *f); +void* ccgSubSurf_getFaceCenterData (CCGFace *f); void* ccgSubSurf_getFaceGridEdgeDataArray (CCGSubSurf *ss, CCGFace *f, int gridIndex); void* ccgSubSurf_getFaceGridEdgeData (CCGSubSurf *ss, CCGFace *f, int gridIndex, int x); void* ccgSubSurf_getFaceGridDataArray (CCGSubSurf *ss, CCGFace *f, int gridIndex); diff --git a/source/blender/blenkernel/intern/DerivedMesh.c b/source/blender/blenkernel/intern/DerivedMesh.c index 473c64f58f2..aab39a099bf 100644 --- a/source/blender/blenkernel/intern/DerivedMesh.c +++ b/source/blender/blenkernel/intern/DerivedMesh.c @@ -33,8 +33,6 @@ #include <config.h> #endif -#include <zlib.h> - #include "PIL_time.h" #include "MEM_guardedalloc.h" @@ -59,6 +57,7 @@ #include "BLI_edgehash.h" #include "BLI_editVert.h" #include "BLI_linklist.h" +#include "BLI_memarena.h" #include "BKE_cdderivedmesh.h" #include "BKE_customdata.h" @@ -66,17 +65,21 @@ #include "BKE_deform.h" #include "BKE_displist.h" #include "BKE_effect.h" +#include "BKE_fluidsim.h" #include "BKE_global.h" #include "BKE_key.h" #include "BKE_material.h" #include "BKE_modifier.h" #include "BKE_mesh.h" +#include "BKE_multires.h" #include "BKE_object.h" #include "BKE_subsurf.h" #include "BKE_texture.h" #include "BKE_utildefines.h" #include "BKE_particle.h" +#include "BLO_sys_types.h" // for intptr_t support + #ifdef WITH_VERSE #include "BKE_verse.h" #endif @@ -85,16 +88,15 @@ #include "BIF_glutil.h" //XXX #include "multires.h" - -// headers for fluidsim bobj meshes -#include <stdlib.h> -#include "LBM_fluidsim.h" -#include "elbeem.h" +// +#include "GPU_draw.h" +#include "GPU_extensions.h" +#include "GPU_material.h" /////////////////////////////////// /////////////////////////////////// -MVert *dm_getVertArray(DerivedMesh *dm) +static MVert *dm_getVertArray(DerivedMesh *dm) { MVert *mvert = CustomData_get_layer(&dm->vertData, CD_MVERT); @@ -108,7 +110,7 @@ MVert *dm_getVertArray(DerivedMesh *dm) return mvert; } -MEdge *dm_getEdgeArray(DerivedMesh *dm) +static MEdge *dm_getEdgeArray(DerivedMesh *dm) { MEdge *medge = CustomData_get_layer(&dm->edgeData, CD_MEDGE); @@ -122,7 +124,7 @@ MEdge *dm_getEdgeArray(DerivedMesh *dm) return medge; } -MFace *dm_getFaceArray(DerivedMesh *dm) +static MFace *dm_getFaceArray(DerivedMesh *dm) { MFace *mface = CustomData_get_layer(&dm->faceData, CD_MFACE); @@ -136,7 +138,7 @@ MFace *dm_getFaceArray(DerivedMesh *dm) return mface; } -MVert *dm_dupVertArray(DerivedMesh *dm) +static MVert *dm_dupVertArray(DerivedMesh *dm) { MVert *tmp = MEM_callocN(sizeof(*tmp) * dm->getNumVerts(dm), "dm_dupVertArray tmp"); @@ -146,7 +148,7 @@ MVert *dm_dupVertArray(DerivedMesh *dm) return tmp; } -MEdge *dm_dupEdgeArray(DerivedMesh *dm) +static MEdge *dm_dupEdgeArray(DerivedMesh *dm) { MEdge *tmp = MEM_callocN(sizeof(*tmp) * dm->getNumEdges(dm), "dm_dupEdgeArray tmp"); @@ -156,7 +158,7 @@ MEdge *dm_dupEdgeArray(DerivedMesh *dm) return tmp; } -MFace *dm_dupFaceArray(DerivedMesh *dm) +static MFace *dm_dupFaceArray(DerivedMesh *dm) { MFace *tmp = MEM_callocN(sizeof(*tmp) * dm->getNumFaces(dm), "dm_dupFaceArray tmp"); @@ -187,10 +189,6 @@ void DM_init_funcs(DerivedMesh *dm) void DM_init(DerivedMesh *dm, int numVerts, int numEdges, int numFaces) { - CustomData_add_layer(&dm->vertData, CD_ORIGINDEX, CD_CALLOC, NULL, numVerts); - CustomData_add_layer(&dm->edgeData, CD_ORIGINDEX, CD_CALLOC, NULL, numEdges); - CustomData_add_layer(&dm->faceData, CD_ORIGINDEX, CD_CALLOC, NULL, numFaces); - dm->numVertData = numVerts; dm->numEdgeData = numEdges; dm->numFaceData = numFaces; @@ -413,39 +411,20 @@ void DM_swap_face_data(DerivedMesh *dm, int index, int *corner_indices) CustomData_swap(&dm->faceData, index, corner_indices); } +/// + static DerivedMesh *getMeshDerivedMesh(Mesh *me, Object *ob, float (*vertCos)[3]) { DerivedMesh *dm = CDDM_from_mesh(me, ob); - int i, dofluidsim; - - dofluidsim = ((ob->fluidsimFlag & OB_FLUIDSIM_ENABLE) && - (ob->fluidsimSettings->type & OB_FLUIDSIM_DOMAIN)&& - (ob->fluidsimSettings->meshSurface) && - (1) && (!give_parteff(ob)) && // doesnt work together with particle systems! - (me->totvert == ((Mesh *)(ob->fluidsimSettings->meshSurface))->totvert)); - - if (vertCos && !dofluidsim) + + if(!dm) + return NULL; + + if (vertCos) CDDM_apply_vert_coords(dm, vertCos); CDDM_calc_normals(dm); - /* apply fluidsim normals */ - if (dofluidsim) { - // use normals from readBobjgz - // TODO? check for modifiers!? - MVert *fsvert = ob->fluidsimSettings->meshSurfNormals; - short (*normals)[3] = MEM_mallocN(sizeof(short)*3*me->totvert, "fluidsim nor"); - - for (i=0; i<me->totvert; i++) { - VECCOPY(normals[i], fsvert[i].no); - //mv->no[0]= 30000; mv->no[1]= mv->no[2]= 0; // DEBUG fixed test normals - } - - CDDM_apply_vert_normals(dm, normals); - - MEM_freeN(normals); - } - return dm; } @@ -484,7 +463,7 @@ static void emDM_foreachMappedEdge(DerivedMesh *dm, void (*func)(void *userData, EditVert *eve; for (i=0,eve=emdm->em->verts.first; eve; eve= eve->next) - eve->tmp.l = (long) i++; + eve->tmp.l = (intptr_t) i++; for(i=0,eed= emdm->em->edges.first; eed; i++,eed= eed->next) func(userData, i, emdm->vertexCos[(int) eed->v1->tmp.l], emdm->vertexCos[(int) eed->v2->tmp.l]); } else { @@ -502,7 +481,7 @@ static void emDM_drawMappedEdges(DerivedMesh *dm, int (*setDrawOptions)(void *us EditVert *eve; for (i=0,eve=emdm->em->verts.first; eve; eve= eve->next) - eve->tmp.l = (long) i++; + eve->tmp.l = (intptr_t) i++; glBegin(GL_LINES); for(i=0,eed= emdm->em->edges.first; eed; i++,eed= eed->next) { @@ -537,7 +516,7 @@ static void emDM_drawMappedEdgesInterp(DerivedMesh *dm, int (*setDrawOptions)(vo EditVert *eve; for (i=0,eve=emdm->em->verts.first; eve; eve= eve->next) - eve->tmp.l = (long) i++; + eve->tmp.l = (intptr_t) i++; glBegin(GL_LINES); for (i=0,eed= emdm->em->edges.first; eed; i++,eed= eed->next) { @@ -624,7 +603,7 @@ static void emDM_foreachMappedFaceCenter(DerivedMesh *dm, void (*func)(void *use if (emdm->vertexCos) { for (i=0,eve=emdm->em->verts.first; eve; eve= eve->next) - eve->tmp.l = (long) i++; + eve->tmp.l = (intptr_t) i++; } for(i=0,efa= emdm->em->faces.first; efa; i++,efa= efa->next) { @@ -634,7 +613,6 @@ static void emDM_foreachMappedFaceCenter(DerivedMesh *dm, void (*func)(void *use } static void emDM_drawMappedFaces(DerivedMesh *dm, int (*setDrawOptions)(void *userData, int index, int *drawSmooth_r), void *userData, int useColors) { - GLubyte act_face_stipple[32*32/8] = DM_FACE_STIPPLE; EditMeshDerivedMesh *emdm= (EditMeshDerivedMesh*) dm; EditFace *efa; int i, draw; @@ -643,7 +621,7 @@ static void emDM_drawMappedFaces(DerivedMesh *dm, int (*setDrawOptions)(void *us EditVert *eve; for (i=0,eve=emdm->em->verts.first; eve; eve= eve->next) - eve->tmp.l = (long) i++; + eve->tmp.l = (intptr_t) i++; for (i=0,efa= emdm->em->faces.first; efa; i++,efa= efa->next) { int drawSmooth = (efa->flag & ME_SMOOTH); @@ -651,7 +629,7 @@ static void emDM_drawMappedFaces(DerivedMesh *dm, int (*setDrawOptions)(void *us if(draw) { if (draw==2) { /* enabled with stipple */ glEnable(GL_POLYGON_STIPPLE); - glPolygonStipple(act_face_stipple); + glPolygonStipple(0); //XXX stipple_quarttone); } glShadeModel(drawSmooth?GL_SMOOTH:GL_FLAT); @@ -688,7 +666,7 @@ static void emDM_drawMappedFaces(DerivedMesh *dm, int (*setDrawOptions)(void *us if(draw) { if (draw==2) { /* enabled with stipple */ glEnable(GL_POLYGON_STIPPLE); - glPolygonStipple(act_face_stipple); + glPolygonStipple(0); //XXX stipple_quarttone); } glShadeModel(drawSmooth?GL_SMOOTH:GL_FLAT); @@ -732,11 +710,14 @@ static void emDM_drawFacesTex_common(DerivedMesh *dm, EditFace *efa; int i; + /* always use smooth shading even for flat faces, else vertex colors wont interpolate */ + glShadeModel(GL_SMOOTH); + if (vertexCos) { EditVert *eve; for (i=0,eve=em->verts.first; eve; eve= eve->next) - eve->tmp.l = (long) i++; + eve->tmp.l = (intptr_t) i++; for (i=0,efa= em->faces.first; efa; i++,efa= efa->next) { MTFace *tf= CustomData_em_get(&em->fdata, efa->data, CD_MTFACE); @@ -753,11 +734,16 @@ static void emDM_drawFacesTex_common(DerivedMesh *dm, flag= 1; if(flag != 0) { /* flag 0 == the face is hidden or invisible */ - if (flag==1 && mcol) - cp= (unsigned char*)mcol; - - glShadeModel(drawSmooth?GL_SMOOTH:GL_FLAT); - + + /* we always want smooth here since otherwise vertex colors dont interpolate */ + if (mcol) { + if (flag==1) { + cp= (unsigned char*)mcol; + } + } else { + glShadeModel(drawSmooth?GL_SMOOTH:GL_FLAT); + } + glBegin(efa->v4?GL_QUADS:GL_TRIANGLES); if (!drawSmooth) { glNormal3fv(emdm->faceNos[i]); @@ -821,10 +807,14 @@ static void emDM_drawFacesTex_common(DerivedMesh *dm, flag= 1; if(flag != 0) { /* flag 0 == the face is hidden or invisible */ - if (flag==1 && mcol) - cp= (unsigned char*)mcol; - - glShadeModel(drawSmooth?GL_SMOOTH:GL_FLAT); + /* we always want smooth here since otherwise vertex colors dont interpolate */ + if (mcol) { + if (flag==1) { + cp= (unsigned char*)mcol; + } + } else { + glShadeModel(drawSmooth?GL_SMOOTH:GL_FLAT); + } glBegin(efa->v4?GL_QUADS:GL_TRIANGLES); if (!drawSmooth) { @@ -886,6 +876,162 @@ static void emDM_drawMappedFacesTex(DerivedMesh *dm, int (*setDrawOptions)(void emDM_drawFacesTex_common(dm, NULL, setDrawOptions, userData); } +static void emDM_drawMappedFacesGLSL(DerivedMesh *dm, + int (*setMaterial)(int, void *attribs), + int (*setDrawOptions)(void *userData, int index), void *userData) +{ + EditMeshDerivedMesh *emdm= (EditMeshDerivedMesh*) dm; + EditMesh *em= emdm->em; + float (*vertexCos)[3]= emdm->vertexCos; + float (*vertexNos)[3]= emdm->vertexNos; + EditVert *eve; + EditFace *efa; + DMVertexAttribs attribs; + GPUVertexAttribs gattribs; + MTFace *tf; + int transp, new_transp, orig_transp, tfoffset; + int i, b, matnr, new_matnr, dodraw, layer; + + dodraw = 0; + matnr = -1; + + transp = GPU_get_material_blend_mode(); + orig_transp = transp; + layer = CustomData_get_layer_index(&em->fdata, CD_MTFACE); + tfoffset = (layer == -1)? -1: em->fdata.layers[layer].offset; + + memset(&attribs, 0, sizeof(attribs)); + + /* always use smooth shading even for flat faces, else vertex colors wont interpolate */ + glShadeModel(GL_SMOOTH); + + for (i=0,eve=em->verts.first; eve; eve= eve->next) + eve->tmp.l = (long) i++; + +#define PASSATTRIB(efa, eve, vert) { \ + if(attribs.totorco) { \ + float *orco = attribs.orco.array[eve->tmp.l]; \ + glVertexAttrib3fvARB(attribs.orco.glIndex, orco); \ + } \ + for(b = 0; b < attribs.tottface; b++) { \ + MTFace *_tf = (MTFace*)((char*)efa->data + attribs.tface[b].emOffset); \ + glVertexAttrib2fvARB(attribs.tface[b].glIndex, _tf->uv[vert]); \ + } \ + for(b = 0; b < attribs.totmcol; b++) { \ + MCol *cp = (MCol*)((char*)efa->data + attribs.mcol[b].emOffset); \ + GLubyte col[4]; \ + col[0]= cp->b; col[1]= cp->g; col[2]= cp->r; col[3]= cp->a; \ + glVertexAttrib4ubvARB(attribs.mcol[b].glIndex, col); \ + } \ + if(attribs.tottang) { \ + float *tang = attribs.tang.array[i*4 + vert]; \ + glVertexAttrib3fvARB(attribs.tang.glIndex, tang); \ + } \ +} + + for (i=0,efa= em->faces.first; efa; i++,efa= efa->next) { + int drawSmooth= (efa->flag & ME_SMOOTH); + + if(setDrawOptions && !setDrawOptions(userData, i)) + continue; + + new_matnr = efa->mat_nr + 1; + if(new_matnr != matnr) { + dodraw = setMaterial(matnr = new_matnr, &gattribs); + if(dodraw) + DM_vertex_attributes_from_gpu(dm, &gattribs, &attribs); + } + + if(tfoffset != -1) { + tf = (MTFace*)((char*)efa->data)+tfoffset; + new_transp = tf->transp; + + if(new_transp != transp) { + if(new_transp == GPU_BLEND_SOLID && orig_transp != GPU_BLEND_SOLID) + GPU_set_material_blend_mode(orig_transp); + else + GPU_set_material_blend_mode(new_transp); + transp = new_transp; + } + } + + if(dodraw) { + glBegin(efa->v4?GL_QUADS:GL_TRIANGLES); + if (!drawSmooth) { + if(vertexCos) glNormal3fv(emdm->faceNos[i]); + else glNormal3fv(efa->n); + + PASSATTRIB(efa, efa->v1, 0); + if(vertexCos) glVertex3fv(vertexCos[(int) efa->v1->tmp.l]); + else glVertex3fv(efa->v1->co); + + PASSATTRIB(efa, efa->v2, 1); + if(vertexCos) glVertex3fv(vertexCos[(int) efa->v2->tmp.l]); + else glVertex3fv(efa->v2->co); + + PASSATTRIB(efa, efa->v3, 2); + if(vertexCos) glVertex3fv(vertexCos[(int) efa->v3->tmp.l]); + else glVertex3fv(efa->v3->co); + + if(efa->v4) { + PASSATTRIB(efa, efa->v4, 3); + if(vertexCos) glVertex3fv(vertexCos[(int) efa->v4->tmp.l]); + else glVertex3fv(efa->v4->co); + } + } else { + PASSATTRIB(efa, efa->v1, 0); + if(vertexCos) { + glNormal3fv(vertexNos[(int) efa->v1->tmp.l]); + glVertex3fv(vertexCos[(int) efa->v1->tmp.l]); + } + else { + glNormal3fv(efa->v1->no); + glVertex3fv(efa->v1->co); + } + + PASSATTRIB(efa, efa->v2, 1); + if(vertexCos) { + glNormal3fv(vertexNos[(int) efa->v2->tmp.l]); + glVertex3fv(vertexCos[(int) efa->v2->tmp.l]); + } + else { + glNormal3fv(efa->v2->no); + glVertex3fv(efa->v2->co); + } + + PASSATTRIB(efa, efa->v3, 2); + if(vertexCos) { + glNormal3fv(vertexNos[(int) efa->v3->tmp.l]); + glVertex3fv(vertexCos[(int) efa->v3->tmp.l]); + } + else { + glNormal3fv(efa->v3->no); + glVertex3fv(efa->v3->co); + } + + if(efa->v4) { + PASSATTRIB(efa, efa->v4, 3); + if(vertexCos) { + glNormal3fv(vertexNos[(int) efa->v4->tmp.l]); + glVertex3fv(vertexCos[(int) efa->v4->tmp.l]); + } + else { + glNormal3fv(efa->v4->no); + glVertex3fv(efa->v4->co); + } + } + } + glEnd(); + } + } +} + +static void emDM_drawFacesGLSL(DerivedMesh *dm, + int (*setMaterial)(int, void *attribs)) +{ + dm->drawMappedFacesGLSL(dm, setMaterial, NULL, NULL); +} + static void emDM_getMinMax(DerivedMesh *dm, float min_r[3], float max_r[3]) { EditMeshDerivedMesh *emdm= (EditMeshDerivedMesh*) dm; @@ -925,7 +1071,7 @@ static int emDM_getNumFaces(DerivedMesh *dm) return BLI_countlist(&emdm->em->faces); } -void emDM_getVert(DerivedMesh *dm, int index, MVert *vert_r) +static void emDM_getVert(DerivedMesh *dm, int index, MVert *vert_r) { EditVert *ev = ((EditMeshDerivedMesh *)dm)->em->verts.first; int i; @@ -940,9 +1086,10 @@ void emDM_getVert(DerivedMesh *dm, int index, MVert *vert_r) /* TODO what to do with vert_r->flag and vert_r->mat_nr? */ vert_r->mat_nr = 0; + vert_r->bweight = (unsigned char) (ev->bweight*255.0f); } -void emDM_getEdge(DerivedMesh *dm, int index, MEdge *edge_r) +static void emDM_getEdge(DerivedMesh *dm, int index, MEdge *edge_r) { EditMesh *em = ((EditMeshDerivedMesh *)dm)->em; EditEdge *ee = em->edges.first; @@ -952,6 +1099,7 @@ void emDM_getEdge(DerivedMesh *dm, int index, MEdge *edge_r) for(i = 0; i < index; ++i) ee = ee->next; edge_r->crease = (unsigned char) (ee->crease*255.0f); + edge_r->bweight = (unsigned char) (ee->bweight*255.0f); /* TODO what to do with edge_r->flag? */ edge_r->flag = ME_EDGEDRAW|ME_EDGERENDER; if (ee->seam) edge_r->flag |= ME_SEAM; @@ -976,7 +1124,7 @@ void emDM_getEdge(DerivedMesh *dm, int index, MEdge *edge_r) } } -void emDM_getFace(DerivedMesh *dm, int index, MFace *face_r) +static void emDM_getFace(DerivedMesh *dm, int index, MFace *face_r) { EditMesh *em = ((EditMeshDerivedMesh *)dm)->em; EditFace *ef = em->faces.first; @@ -1018,7 +1166,7 @@ void emDM_getFace(DerivedMesh *dm, int index, MFace *face_r) test_index_face(face_r, NULL, 0, ef->v4?4:3); } -void emDM_copyVertArray(DerivedMesh *dm, MVert *vert_r) +static void emDM_copyVertArray(DerivedMesh *dm, MVert *vert_r) { EditVert *ev = ((EditMeshDerivedMesh *)dm)->em->verts.first; @@ -1032,10 +1180,11 @@ void emDM_copyVertArray(DerivedMesh *dm, MVert *vert_r) /* TODO what to do with vert_r->flag and vert_r->mat_nr? */ vert_r->mat_nr = 0; vert_r->flag = 0; + vert_r->bweight = (unsigned char) (ev->bweight*255.0f); } } -void emDM_copyEdgeArray(DerivedMesh *dm, MEdge *edge_r) +static void emDM_copyEdgeArray(DerivedMesh *dm, MEdge *edge_r) { EditMesh *em = ((EditMeshDerivedMesh *)dm)->em; EditEdge *ee = em->edges.first; @@ -1044,10 +1193,11 @@ void emDM_copyEdgeArray(DerivedMesh *dm, MEdge *edge_r) /* store vertex indices in tmp union */ for(ev = em->verts.first, i = 0; ev; ev = ev->next, ++i) - ev->tmp.l = (long) i++; + ev->tmp.l = (intptr_t) i; for( ; ee; ee = ee->next, ++edge_r) { edge_r->crease = (unsigned char) (ee->crease*255.0f); + edge_r->bweight = (unsigned char) (ee->bweight*255.0f); /* TODO what to do with edge_r->flag? */ edge_r->flag = ME_EDGEDRAW|ME_EDGERENDER; if (ee->seam) edge_r->flag |= ME_SEAM; @@ -1062,7 +1212,7 @@ void emDM_copyEdgeArray(DerivedMesh *dm, MEdge *edge_r) } } -void emDM_copyFaceArray(DerivedMesh *dm, MFace *face_r) +static void emDM_copyFaceArray(DerivedMesh *dm, MFace *face_r) { EditMesh *em = ((EditMeshDerivedMesh *)dm)->em; EditFace *ef = em->faces.first; @@ -1071,7 +1221,7 @@ void emDM_copyFaceArray(DerivedMesh *dm, MFace *face_r) /* store vertexes indices in tmp union */ for(ev = em->verts.first, i = 0; ev; ev = ev->next, ++i) - ev->tmp.l = (long) i; + ev->tmp.l = (intptr_t) i; for( ; ef; ef = ef->next, ++face_r) { face_r->mat_nr = ef->mat_nr; @@ -1087,6 +1237,43 @@ void emDM_copyFaceArray(DerivedMesh *dm, MFace *face_r) } } +static void *emDM_getFaceDataArray(DerivedMesh *dm, int type) +{ + EditMeshDerivedMesh *emdm= (EditMeshDerivedMesh*) dm; + EditMesh *em= emdm->em; + EditFace *efa; + char *data, *emdata; + void *datalayer; + int index, offset, size; + + datalayer = DM_get_face_data_layer(dm, type); + if(datalayer) + return datalayer; + + /* layers are store per face for editmesh, we convert to a temporary + * data layer array in the derivedmesh when these are requested */ + if(type == CD_MTFACE || type == CD_MCOL) { + index = CustomData_get_layer_index(&em->fdata, type); + + if(index != -1) { + offset = em->fdata.layers[index].offset; + size = CustomData_sizeof(type); + + DM_add_face_layer(dm, type, CD_CALLOC, NULL); + index = CustomData_get_layer_index(&dm->faceData, type); + dm->faceData.layers[index].flag |= CD_FLAG_TEMPORARY; + + data = datalayer = DM_get_face_data_layer(dm, type); + for(efa=em->faces.first; efa; efa=efa->next, data+=size) { + emdata = CustomData_em_get(&em->fdata, efa->data, type); + memcpy(data, emdata, size); + } + } + } + + return datalayer; +} + static void emDM_release(DerivedMesh *dm) { EditMeshDerivedMesh *emdm= (EditMeshDerivedMesh*) dm; @@ -1122,6 +1309,7 @@ static DerivedMesh *getEditMeshDerivedMesh(EditMesh *em, Object *ob, emdm->dm.copyVertArray = emDM_copyVertArray; emdm->dm.copyEdgeArray = emDM_copyEdgeArray; emdm->dm.copyFaceArray = emDM_copyFaceArray; + emdm->dm.getFaceDataArray = emDM_getFaceDataArray; emdm->dm.foreachMappedVert = emDM_foreachMappedVert; emdm->dm.foreachMappedEdge = emDM_foreachMappedEdge; @@ -1132,7 +1320,9 @@ static DerivedMesh *getEditMeshDerivedMesh(EditMesh *em, Object *ob, emdm->dm.drawMappedEdgesInterp = emDM_drawMappedEdgesInterp; emdm->dm.drawMappedFaces = emDM_drawMappedFaces; emdm->dm.drawMappedFacesTex = emDM_drawMappedFacesTex; + emdm->dm.drawMappedFacesGLSL = emDM_drawMappedFacesGLSL; emdm->dm.drawFacesTex = emDM_drawFacesTex; + emdm->dm.drawFacesGLSL = emDM_drawFacesGLSL; emdm->dm.drawUVEdges = emDM_drawUVEdges; emdm->dm.release = emDM_release; @@ -1158,7 +1348,7 @@ static DerivedMesh *getEditMeshDerivedMesh(EditMesh *em, Object *ob, int i; for (i=0,eve=em->verts.first; eve; eve= eve->next) - eve->tmp.l = (long) i++; + eve->tmp.l = (intptr_t) i++; emdm->vertexNos = MEM_callocN(sizeof(*emdm->vertexNos)*i, "emdm_vno"); emdm->faceNos = MEM_mallocN(sizeof(*emdm->faceNos)*totface, "emdm_vno"); @@ -1316,6 +1506,7 @@ void vDM_getEdge(DerivedMesh *dm, int index, MEdge *edge_r) /* not supported yet */ edge_r->flag = 0; edge_r->crease = 0; + edge_r->bweight = 0; break; } } @@ -1422,6 +1613,7 @@ void vDM_copyEdgeArray(DerivedMesh *dm, MEdge *edge_r) /* not supported yet */ edge_r->flag = 0; edge_r->crease = 0; + edge_r->bweight = 0; } } } @@ -1560,7 +1752,7 @@ static void vDM_drawUVEdges(DerivedMesh *dm) } /* draw all VerseFaces */ -static void vDM_drawFacesSolid(DerivedMesh *dm, int (*setMaterial)(int)) +static void vDM_drawFacesSolid(DerivedMesh *dm, int (*setMaterial)(int, void *attribs)) { VDerivedMesh *vdm = (VDerivedMesh*)dm; struct VerseFace *vface; @@ -1660,6 +1852,8 @@ static void vDM_drawMappedFacesTex( int (*setDrawParams)(void *userData, int index), void *userData) { + /* not supported yet */ + vDM_drawFacesTex(dm, NULL); } /**/ @@ -1801,19 +1995,23 @@ CustomDataMask get_viewedit_datamask() ScrArea *sa; /* check if we need tfaces & mcols due to face select or texture paint */ - if(FACESEL_PAINT_TEST || G.f & G_TEXTUREPAINT) { + if(FACESEL_PAINT_TEST || G.f & G_TEXTUREPAINT) mask |= CD_MASK_MTFACE | CD_MASK_MCOL; - } else { - /* check if we need tfaces & mcols due to view mode */ - for(sa = G.curscreen->areabase.first; sa; sa = sa->next) { - if(sa->spacetype == SPACE_VIEW3D) { - View3D *view = sa->spacedata.first; - if(view->drawtype == OB_SHADED) { - /* this includes normals for mesh_create_shadedColors */ - mask |= CD_MASK_MTFACE | CD_MASK_MCOL | CD_MASK_NORMAL | CD_MASK_ORCO; - } - if((view->drawtype == OB_TEXTURE) || ((view->drawtype == OB_SOLID) && (view->flag2 & V3D_SOLID_TEX))) { - mask |= CD_MASK_MTFACE | CD_MASK_MCOL; + + /* check if we need tfaces & mcols due to view mode */ + for(sa = G.curscreen->areabase.first; sa; sa = sa->next) { + if(sa->spacetype == SPACE_VIEW3D) { + View3D *view = sa->spacedata.first; + if(view->drawtype == OB_SHADED) { + /* this includes normals for mesh_create_shadedColors */ + mask |= CD_MASK_MTFACE | CD_MASK_MCOL | CD_MASK_NORMAL | CD_MASK_ORCO; + } + if((view->drawtype == OB_TEXTURE) || ((view->drawtype == OB_SOLID) && (view->flag2 & V3D_SOLID_TEX))) { + mask |= CD_MASK_MTFACE | CD_MASK_MCOL; + + if((G.fileflags & G_FILE_GAME_MAT) && + (G.fileflags & G_FILE_GAME_MAT_GLSL)) { + mask |= CD_MASK_ORCO; } } } @@ -1826,13 +2024,41 @@ CustomDataMask get_viewedit_datamask() return mask; } -static DerivedMesh *create_orco_dm(Object *ob, Mesh *me) +static float *get_editmesh_orco_verts(EditMesh *em) +{ + EditVert *eve; + float *orco; + int a, totvert; + + /* these may not really be the orco's, but it's only for preview. + * could be solver better once, but isn't simple */ + + totvert= 0; + for(eve=em->verts.first; eve; eve=eve->next) + totvert++; + + orco = MEM_mallocN(sizeof(float)*3*totvert, "EditMesh Orco"); + + for(a=0, eve=em->verts.first; eve; eve=eve->next, a+=3) + VECCOPY(orco+a, eve->co); + + return orco; +} + +static DerivedMesh *create_orco_dm(Object *ob, Mesh *me, EditMesh *em) { DerivedMesh *dm; float (*orco)[3]; - dm= CDDM_from_mesh(me, ob); - orco= (float(*)[3])get_mesh_orco_verts(ob); + if(em) { + dm= CDDM_from_editmesh(em, me); + orco= (float(*)[3])get_editmesh_orco_verts(em); + } + else { + dm= CDDM_from_mesh(me, ob); + orco= (float(*)[3])get_mesh_orco_verts(ob); + } + CDDM_apply_vert_coords(dm, orco); CDDM_calc_normals(dm); MEM_freeN(orco); @@ -1840,7 +2066,7 @@ static DerivedMesh *create_orco_dm(Object *ob, Mesh *me) return dm; } -static void add_orco_dm(Object *ob, DerivedMesh *dm, DerivedMesh *orcodm) +static void add_orco_dm(Object *ob, EditMesh *em, DerivedMesh *dm, DerivedMesh *orcodm) { float (*orco)[3], (*layerorco)[3]; int totvert; @@ -1855,8 +2081,10 @@ static void add_orco_dm(Object *ob, DerivedMesh *dm, DerivedMesh *orcodm) else dm->getVertCos(dm, orco); } - else - orco= (float(*)[3])get_mesh_orco_verts(ob); + else { + if(em) orco= (float(*)[3])get_editmesh_orco_verts(em); + else orco= (float(*)[3])get_mesh_orco_verts(ob); + } transform_mesh_orco_verts(ob->data, orco, totvert, 0); @@ -1871,7 +2099,7 @@ static void add_orco_dm(Object *ob, DerivedMesh *dm, DerivedMesh *orcodm) static void mesh_calc_modifiers(Object *ob, float (*inputVertexCos)[3], DerivedMesh **deform_r, DerivedMesh **final_r, int useRenderParams, int useDeform, - int needMapping, CustomDataMask dataMask) + int needMapping, CustomDataMask dataMask, int index) { Mesh *me = ob->data; ModifierData *firstmd, *md; @@ -1880,7 +2108,6 @@ static void mesh_calc_modifiers(Object *ob, float (*inputVertexCos)[3], float (*deformedVerts)[3] = NULL; DerivedMesh *dm, *orcodm, *finaldm; int numVerts = me->totvert; - int fluidsimMeshUsed = 0; int required_mode; md = firstmd = modifiers_getVirtualModifierList(ob); @@ -1896,22 +2123,6 @@ static void mesh_calc_modifiers(Object *ob, float (*inputVertexCos)[3], if(deform_r) *deform_r = NULL; *final_r = NULL; - /* replace original mesh by fluidsim surface mesh for fluidsim - * domain objects - */ - if((G.obedit!=ob) && !needMapping) { - if((ob->fluidsimFlag & OB_FLUIDSIM_ENABLE) && - (1) && (!give_parteff(ob)) ) { // doesnt work together with particle systems! - if(ob->fluidsimSettings->type & OB_FLUIDSIM_DOMAIN) { - loadFluidsimMesh(ob,useRenderParams); - fluidsimMeshUsed = 1; - /* might have changed... */ - me = ob->data; - numVerts = me->totvert; - } - } - } - if(useRenderParams) required_mode = eModifierMode_Render; else required_mode = eModifierMode_Realtime; @@ -1920,7 +2131,7 @@ static void mesh_calc_modifiers(Object *ob, float (*inputVertexCos)[3], deformedVerts = mesh_getVertexCos(me, &numVerts); /* Apply all leading deforming modifiers */ - for(; md; md = md->next, curr = curr->next) { + for(;md; md = md->next, curr = curr->next) { ModifierTypeInfo *mti = modifierType_getInfo(md->type); if((md->mode & required_mode) != required_mode) continue; @@ -1934,6 +2145,10 @@ static void mesh_calc_modifiers(Object *ob, float (*inputVertexCos)[3], } else { break; } + + /* grab modifiers until index i */ + if((index >= 0) && (modifiers_indexInObject(ob, md) >= index)) + break; } /* Result of all leading deforming modifiers is cached for @@ -1959,18 +2174,11 @@ static void mesh_calc_modifiers(Object *ob, float (*inputVertexCos)[3], #endif } } else { - if(!fluidsimMeshUsed) { - /* default behaviour for meshes */ - if(inputVertexCos) - deformedVerts = inputVertexCos; - else - deformedVerts = mesh_getRefKeyCos(me, &numVerts); - } else { - /* the fluid sim mesh might have more vertices than the original - * one, so inputVertexCos shouldnt be used - */ - deformedVerts = mesh_getVertexCos(me, &numVerts); - } + /* default behaviour for meshes */ + if(inputVertexCos) + deformedVerts = inputVertexCos; + else + deformedVerts = mesh_getRefKeyCos(me, &numVerts); } @@ -1987,14 +2195,13 @@ static void mesh_calc_modifiers(Object *ob, float (*inputVertexCos)[3], if(me->vnode) dm = derivedmesh_from_versemesh(me->vnode, deformedVerts); #endif - for(; md; md = md->next, curr = curr->next) { + for(;md; md = md->next, curr = curr->next) { ModifierTypeInfo *mti = modifierType_getInfo(md->type); if((md->mode & required_mode) != required_mode) continue; if(mti->type == eModifierTypeType_OnlyDeform && !useDeform) continue; if((mti->flags & eModifierTypeFlag_RequiresOriginalData) && dm) { - modifier_setError(md, "Internal error, modifier requires " - "original data (bad stack position)."); + modifier_setError(md, "Modifier requires original data, bad stack position."); continue; } if(mti->isDisabled && mti->isDisabled(md)) continue; @@ -2004,7 +2211,7 @@ static void mesh_calc_modifiers(Object *ob, float (*inputVertexCos)[3], if(dm && mti->requiredDataMask) { mask = mti->requiredDataMask(md); if(mask & CD_MASK_ORCO) - add_orco_dm(ob, dm, orcodm); + add_orco_dm(ob, NULL, dm, orcodm); } /* How to apply modifier depends on (a) what we already have as @@ -2057,7 +2264,7 @@ static void mesh_calc_modifiers(Object *ob, float (*inputVertexCos)[3], mask= (CustomDataMask)curr->link; if(mask & CD_MASK_ORCO) { if(!orcodm) - orcodm= create_orco_dm(ob, me); + orcodm= create_orco_dm(ob, me, NULL); mask &= ~CD_MASK_ORCO; DM_set_only_copy(orcodm, mask); @@ -2094,6 +2301,10 @@ static void mesh_calc_modifiers(Object *ob, float (*inputVertexCos)[3], } } } + + /* grab modifiers until index i */ + if((index >= 0) && (modifiers_indexInObject(ob, md) >= index)) + break; } for(md=firstmd; md; md=md->next) @@ -2133,8 +2344,12 @@ static void mesh_calc_modifiers(Object *ob, float (*inputVertexCos)[3], } /* add an orco layer if needed */ - if(dataMask & CD_MASK_ORCO) - add_orco_dm(ob, finaldm, orcodm); + if(dataMask & CD_MASK_ORCO) { + add_orco_dm(ob, NULL, finaldm, orcodm); + + if(deform_r && *deform_r) + add_orco_dm(ob, NULL, *deform_r, NULL); + } *final_r = finaldm; @@ -2145,9 +2360,6 @@ static void mesh_calc_modifiers(Object *ob, float (*inputVertexCos)[3], MEM_freeN(deformedVerts); BLI_linklist_free(datamasks, NULL); - - /* restore mesh in any case */ - if(fluidsimMeshUsed) ob->data = ob->fluidsimSettings->orgMesh; } static float (*editmesh_getVertexCos(EditMesh *em, int *numVerts_r))[3] @@ -2171,8 +2383,7 @@ static int editmesh_modifier_is_enabled(ModifierData *md, DerivedMesh *dm) if((md->mode & required_mode) != required_mode) return 0; if((mti->flags & eModifierTypeFlag_RequiresOriginalData) && dm) { - modifier_setError(md, "Internal error, modifier requires" - "original data (bad stack position)."); + modifier_setError(md, "Modifier requires original data, bad stack position."); return 0; } if(mti->isDisabled && mti->isDisabled(md)) return 0; @@ -2190,7 +2401,8 @@ static void editmesh_calc_modifiers(DerivedMesh **cage_r, EditMesh *em = G.editMesh; ModifierData *md; float (*deformedVerts)[3] = NULL; - DerivedMesh *dm; + CustomDataMask mask; + DerivedMesh *dm, *orcodm = NULL; int i, numVerts = 0, cageIndex = modifiers_getCageIndex(ob, NULL); LinkNode *datamasks, *curr; @@ -2215,6 +2427,13 @@ static void editmesh_calc_modifiers(DerivedMesh **cage_r, if(!editmesh_modifier_is_enabled(md, dm)) continue; + /* add an orco layer if needed by this modifier */ + if(dm && mti->requiredDataMask) { + mask = mti->requiredDataMask(md); + if(mask & CD_MASK_ORCO) + add_orco_dm(ob, em, dm, orcodm); + } + /* How to apply modifier depends on (a) what we already have as * a result of previous modifiers (could be a DerivedMesh or just * deformed vertices) and (b) what type the modifier is. @@ -2265,6 +2484,23 @@ static void editmesh_calc_modifiers(DerivedMesh **cage_r, } } + /* create an orco derivedmesh in parallel */ + mask= (CustomDataMask)curr->link; + if(mask & CD_MASK_ORCO) { + if(!orcodm) + orcodm= create_orco_dm(ob, ob->data, em); + + mask &= ~CD_MASK_ORCO; + DM_set_only_copy(orcodm, mask); + ndm = mti->applyModifierEM(md, ob, em, orcodm); + + if(ndm) { + /* if the modifier returned a new dm, release the old one */ + if(orcodm && orcodm != ndm) orcodm->release(orcodm); + orcodm = ndm; + } + } + /* set the DerivedMesh to only copy needed data */ DM_set_only_copy(dm, (CustomDataMask)curr->link); @@ -2323,6 +2559,13 @@ static void editmesh_calc_modifiers(DerivedMesh **cage_r, deformedVerts = NULL; } + /* add an orco layer if needed */ + if(dataMask & CD_MASK_ORCO) + add_orco_dm(ob, em, *final_r, orcodm); + + if(orcodm) + orcodm->release(orcodm); + if(deformedVerts) MEM_freeN(deformedVerts); } @@ -2460,21 +2703,26 @@ static void mesh_build_data(Object *ob, CustomDataMask dataMask) if( (G.f & G_WEIGHTPAINT) && ob==obact ) { MCol *wpcol = (MCol*)calc_weightpaint_colors(ob); int layernum = CustomData_number_of_layers(&me->fdata, CD_MCOL); + int prevactive = CustomData_get_active_layer(&me->fdata, CD_MCOL); + int prevrender = CustomData_get_render_layer(&me->fdata, CD_MCOL); /* ugly hack here, we temporarily add a new active mcol layer with weightpaint colors in it, that is then duplicated in CDDM_from_mesh */ CustomData_add_layer(&me->fdata, CD_MCOL, CD_ASSIGN, wpcol, me->totface); CustomData_set_layer_active(&me->fdata, CD_MCOL, layernum); + CustomData_set_layer_render(&me->fdata, CD_MCOL, layernum); mesh_calc_modifiers(ob, NULL, &ob->derivedDeform, &ob->derivedFinal, 0, 1, - needMapping, dataMask); + needMapping, dataMask, -1); CustomData_free_layer_active(&me->fdata, CD_MCOL, me->totface); + CustomData_set_layer_active(&me->fdata, CD_MCOL, prevactive); + CustomData_set_layer_render(&me->fdata, CD_MCOL, prevrender); } else { mesh_calc_modifiers(ob, NULL, &ob->derivedDeform, - &ob->derivedFinal, 0, 1, - needMapping, dataMask); + &ob->derivedFinal, G.rendering, 1, + needMapping, dataMask, -1); } INIT_MINMAX(min, max); @@ -2532,14 +2780,7 @@ void makeDerivedMesh(Object *ob, CustomDataMask dataMask) if (ob==G.obedit) { editmesh_build_data(dataMask); } else { - PartEff *paf= give_parteff(ob); - mesh_build_data(ob, dataMask); - - if(paf) { - if((paf->flag & PAF_STATIC) || (ob->recalc & OB_RECALC_TIME)==0) - build_particle_system(ob); - } } } @@ -2572,7 +2813,7 @@ float *multires_render_pin(Object *ob, Mesh *me, int *orig_lvl) { float *vert_copy= NULL; - if(me->mr) { + if(me->mr && !(me->mr->flag & MULTIRES_NO_RENDER)) { MultiresLevel *lvl= NULL; int i; @@ -2598,16 +2839,22 @@ float *multires_render_pin(Object *ob, Mesh *me, int *orig_lvl) void multires_render_final(Object *ob, Mesh *me, DerivedMesh **dm, float *vert_copy, const int orig_lvl, CustomDataMask dataMask) { - if(me->mr) { + if(me->mr && !(me->mr->flag & MULTIRES_NO_RENDER)) { if((*dm)->getNumVerts(*dm) == me->totvert && (*dm)->getNumFaces(*dm) == me->totface) { //XXX MultiresLevel *lvl= multires_level_n(me->mr, BLI_countlist(&me->mr->levels)); DerivedMesh *old= NULL; + MVert *vertdup= NULL; int i; - (*dm)->copyVertArray(*dm, me->mvert); + /* Copy the verts into the mesh */ + vertdup= (*dm)->dupVertArray(*dm); (*dm)->release(*dm); - + for(i=0; i<me->totvert; ++i) + me->mvert[i]= vertdup[i]; + /* Free vertdup after use*/ + MEM_freeN(vertdup); + /* Go to the render level */ me->mr->newlvl= me->mr->renderlvl; //XXX multires_set_level(ob, me, 1); (*dm)= getMeshDerivedMesh(me, ob, NULL); @@ -2618,7 +2865,7 @@ void multires_render_final(Object *ob, Mesh *me, DerivedMesh **dm, float *vert_c old->release(old); if(dataMask & CD_MASK_ORCO) - add_orco_dm(ob, *dm, NULL); + add_orco_dm(ob, NULL, *dm, NULL); /* Restore the original verts */ me->mr->newlvl= BLI_countlist(&me->mr->levels); @@ -2646,7 +2893,21 @@ DerivedMesh *mesh_create_derived_render(Object *ob, CustomDataMask dataMask) int orig_lvl= 0; vert_copy= multires_render_pin(ob, me, &orig_lvl); - mesh_calc_modifiers(ob, NULL, NULL, &final, 1, 1, 0, dataMask); + mesh_calc_modifiers(ob, NULL, NULL, &final, 1, 1, 0, dataMask, -1); + multires_render_final(ob, me, &final, vert_copy, orig_lvl, dataMask); + + return final; +} + +DerivedMesh *mesh_create_derived_index_render(Object *ob, CustomDataMask dataMask, int index) +{ + DerivedMesh *final; + Mesh *me= get_mesh(ob); + float *vert_copy= NULL; + int orig_lvl= 0; + + vert_copy= multires_render_pin(ob, me, &orig_lvl); + mesh_calc_modifiers(ob, NULL, NULL, &final, 1, 1, 0, dataMask, index); multires_render_final(ob, me, &final, vert_copy, orig_lvl, dataMask); return final; @@ -2656,7 +2917,7 @@ DerivedMesh *mesh_create_derived_view(Object *ob, CustomDataMask dataMask) { DerivedMesh *final; - mesh_calc_modifiers(ob, NULL, NULL, &final, 0, 1, 0, dataMask); + mesh_calc_modifiers(ob, NULL, NULL, &final, 0, 1, 0, dataMask, -1); return final; } @@ -2666,7 +2927,7 @@ DerivedMesh *mesh_create_derived_no_deform(Object *ob, float (*vertCos)[3], { DerivedMesh *final; - mesh_calc_modifiers(ob, vertCos, NULL, &final, 0, 0, 0, dataMask); + mesh_calc_modifiers(ob, vertCos, NULL, &final, 0, 0, 0, dataMask, -1); return final; } @@ -2681,7 +2942,7 @@ DerivedMesh *mesh_create_derived_no_deform_render(Object *ob, int orig_lvl= 0; vert_copy= multires_render_pin(ob, me, &orig_lvl); - mesh_calc_modifiers(ob, vertCos, NULL, &final, 1, 0, 0, dataMask); + mesh_calc_modifiers(ob, vertCos, NULL, &final, 1, 0, 0, dataMask, -1); multires_render_final(ob, me, &final, vert_copy, orig_lvl, dataMask); return final; @@ -2833,580 +3094,211 @@ int editmesh_get_first_deform_matrices(float (**deformmats)[3][3], float (**defo return numleft; } -/* ************************* fluidsim bobj file handling **************************** */ - -#ifndef DISABLE_ELBEEM - -#ifdef WIN32 -#ifndef snprintf -#define snprintf _snprintf -#endif -#endif +/* ******************* GLSL ******************** */ -/* write .bobj.gz file for a mesh object */ -void writeBobjgz(char *filename, struct Object *ob, int useGlobalCoords, int append, float time) +void DM_add_tangent_layer(DerivedMesh *dm) { - char debugStrBuffer[256]; - int wri,i,j,totvert,totface; - float wrf; - gzFile gzf; - DerivedMesh *dm; - float vec[3]; - float rotmat[3][3]; - MVert *mvert; - MFace *mface; - //if(append)return; // DEBUG - - if(!ob->data || (ob->type!=OB_MESH)) { - snprintf(debugStrBuffer,256,"Writing GZ_BOBJ Invalid object %s ...\n", ob->id.name); - elbeemDebugOut(debugStrBuffer); - return; - } - if((ob->size[0]<0.0) || (ob->size[0]<0.0) || (ob->size[0]<0.0) ) { - snprintf(debugStrBuffer,256,"\nfluidSim::writeBobjgz:: Warning object %s has negative scaling - check triangle ordering...?\n\n", ob->id.name); - elbeemDebugOut(debugStrBuffer); - } + /* mesh vars */ + MTFace *mtface, *tf; + MFace *mface, *mf; + MVert *mvert, *v1, *v2, *v3, *v4; + MemArena *arena= NULL; + VertexTangent **vtangents= NULL; + float (*orco)[3]= NULL, (*tangent)[3]; + float *uv1, *uv2, *uv3, *uv4, *vtang; + float fno[3], tang[3], uv[4][2]; + int i, j, len, mf_vi[4], totvert, totface; - snprintf(debugStrBuffer,256,"Writing GZ_BOBJ '%s' ... ",filename); elbeemDebugOut(debugStrBuffer); - if(append) gzf = gzopen(filename, "a+b9"); - else gzf = gzopen(filename, "wb9"); - if (!gzf) { - snprintf(debugStrBuffer,256,"writeBobjgz::error - Unable to open file for writing '%s'\n", filename); - elbeemDebugOut(debugStrBuffer); + if(CustomData_get_layer_index(&dm->faceData, CD_TANGENT) != -1) return; - } - dm = mesh_create_derived_render(ob, CD_MASK_BAREMESH); - //dm = mesh_create_derived_no_deform(ob,NULL); + /* check we have all the needed layers */ + totvert= dm->getNumVerts(dm); + totface= dm->getNumFaces(dm); - mvert = dm->getVertArray(dm); - mface = dm->getFaceArray(dm); - totvert = dm->getNumVerts(dm); - totface = dm->getNumFaces(dm); + mvert= dm->getVertArray(dm); + mface= dm->getFaceArray(dm); + mtface= dm->getFaceDataArray(dm, CD_MTFACE); - // write time value for appended anim mesh - if(append) { - gzwrite(gzf, &time, sizeof(time)); + if(!mtface) { + orco= dm->getVertDataArray(dm, CD_ORCO); + if(!orco) + return; } - - // continue with verts/norms - if(sizeof(wri)!=4) { snprintf(debugStrBuffer,256,"Writing GZ_BOBJ, Invalid int size %d...\n", wri); elbeemDebugOut(debugStrBuffer); return; } // paranoia check - wri = dm->getNumVerts(dm); - mvert = dm->getVertArray(dm); - gzwrite(gzf, &wri, sizeof(wri)); - for(i=0; i<wri;i++) { - VECCOPY(vec, mvert[i].co); - if(useGlobalCoords) { Mat4MulVecfl(ob->obmat, vec); } - for(j=0; j<3; j++) { - wrf = vec[j]; - gzwrite(gzf, &wrf, sizeof( wrf )); + + /* create tangent layer */ + DM_add_face_layer(dm, CD_TANGENT, CD_CALLOC, NULL); + tangent= DM_get_face_data_layer(dm, CD_TANGENT); + + /* allocate some space */ + arena= BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE); + BLI_memarena_use_calloc(arena); + vtangents= MEM_callocN(sizeof(VertexTangent*)*totvert, "VertexTangent"); + + /* sum tangents at connected vertices */ + for(i=0, tf=mtface, mf=mface; i < totface; mf++, tf++, i++) { + v1= &mvert[mf->v1]; + v2= &mvert[mf->v2]; + v3= &mvert[mf->v3]; + + if (mf->v4) { + v4= &mvert[mf->v4]; + CalcNormFloat4(v4->co, v3->co, v2->co, v1->co, fno); } - } - - // should be the same as Vertices.size - wri = totvert; - gzwrite(gzf, &wri, sizeof(wri)); - EulToMat3(ob->rot, rotmat); - for(i=0; i<wri;i++) { - VECCOPY(vec, mvert[i].no); - Normalize(vec); - if(useGlobalCoords) { Mat3MulVecfl(rotmat, vec); } - for(j=0; j<3; j++) { - wrf = vec[j]; - gzwrite(gzf, &wrf, sizeof( wrf )); + else { + v4= NULL; + CalcNormFloat(v3->co, v2->co, v1->co, fno); } - } - - // append only writes verts&norms - if(!append) { - //float side1[3],side2[3],norm1[3],norm2[3]; - //float inpf; - - // compute no. of triangles - wri = 0; - for(i=0; i<totface; i++) { - wri++; - if(mface[i].v4) { wri++; } + + if(mtface) { + uv1= tf->uv[0]; + uv2= tf->uv[1]; + uv3= tf->uv[2]; + uv4= tf->uv[3]; } - gzwrite(gzf, &wri, sizeof(wri)); - for(i=0; i<totface; i++) { - - int face[4]; - face[0] = mface[i].v1; - face[1] = mface[i].v2; - face[2] = mface[i].v3; - face[3] = mface[i].v4; - //snprintf(debugStrBuffer,256,"F %s %d = %d,%d,%d,%d \n",ob->id.name, i, face[0],face[1],face[2],face[3] ); elbeemDebugOut(debugStrBuffer); - //VecSubf(side1, mvert[face[1]].co,mvert[face[0]].co); - //VecSubf(side2, mvert[face[2]].co,mvert[face[0]].co); - //Crossf(norm1,side1,side2); - gzwrite(gzf, &(face[0]), sizeof( face[0] )); - gzwrite(gzf, &(face[1]), sizeof( face[1] )); - gzwrite(gzf, &(face[2]), sizeof( face[2] )); - if(face[3]) { - //VecSubf(side1, mvert[face[2]].co,mvert[face[0]].co); - //VecSubf(side2, mvert[face[3]].co,mvert[face[0]].co); - //Crossf(norm2,side1,side2); - //inpf = Inpf(norm1,norm2); - //if(inpf>0.) { - gzwrite(gzf, &(face[0]), sizeof( face[0] )); - gzwrite(gzf, &(face[2]), sizeof( face[2] )); - gzwrite(gzf, &(face[3]), sizeof( face[3] )); - //} else { - //gzwrite(gzf, &(face[0]), sizeof( face[0] )); - //gzwrite(gzf, &(face[3]), sizeof( face[3] )); - //gzwrite(gzf, &(face[2]), sizeof( face[2] )); - //} - } // quad + else { + uv1= uv[0]; uv2= uv[1]; uv3= uv[2]; uv4= uv[3]; + spheremap(orco[mf->v1][0], orco[mf->v1][1], orco[mf->v1][2], &uv[0][0], &uv[0][1]); + spheremap(orco[mf->v2][0], orco[mf->v2][1], orco[mf->v2][2], &uv[1][0], &uv[1][1]); + spheremap(orco[mf->v3][0], orco[mf->v3][1], orco[mf->v3][2], &uv[2][0], &uv[2][1]); + if(v4) + spheremap(orco[mf->v4][0], orco[mf->v4][1], orco[mf->v4][2], &uv[3][0], &uv[3][1]); } - } - - snprintf(debugStrBuffer,256,"Done. #Vertices: %d, #Triangles: %d\n", totvert, totface ); - elbeemDebugOut(debugStrBuffer); - - gzclose( gzf ); - dm->release(dm); -} - -void initElbeemMesh(struct Object *ob, - int *numVertices, float **vertices, - int *numTriangles, int **triangles, - int useGlobalCoords) -{ - DerivedMesh *dm = NULL; - MVert *mvert; - MFace *mface; - int countTris=0, i, totvert, totface; - float *verts; - int *tris; - - dm = mesh_create_derived_render(ob, CD_MASK_BAREMESH); - //dm = mesh_create_derived_no_deform(ob,NULL); - - mvert = dm->getVertArray(dm); - mface = dm->getFaceArray(dm); - totvert = dm->getNumVerts(dm); - totface = dm->getNumFaces(dm); - - *numVertices = totvert; - verts = MEM_callocN( totvert*3*sizeof(float), "elbeemmesh_vertices"); - for(i=0; i<totvert; i++) { - VECCOPY( &verts[i*3], mvert[i].co); - if(useGlobalCoords) { Mat4MulVecfl(ob->obmat, &verts[i*3]); } - } - *vertices = verts; - - for(i=0; i<totface; i++) { - countTris++; - if(mface[i].v4) { countTris++; } - } - *numTriangles = countTris; - tris = MEM_callocN( countTris*3*sizeof(int), "elbeemmesh_triangles"); - countTris = 0; - for(i=0; i<totface; i++) { - int face[4]; - face[0] = mface[i].v1; - face[1] = mface[i].v2; - face[2] = mface[i].v3; - face[3] = mface[i].v4; - - tris[countTris*3+0] = face[0]; - tris[countTris*3+1] = face[1]; - tris[countTris*3+2] = face[2]; - countTris++; - if(face[3]) { - tris[countTris*3+0] = face[0]; - tris[countTris*3+1] = face[2]; - tris[countTris*3+2] = face[3]; - countTris++; + + tangent_from_uv(uv1, uv2, uv3, v1->co, v2->co, v3->co, fno, tang); + sum_or_add_vertex_tangent(arena, &vtangents[mf->v1], tang, uv1); + sum_or_add_vertex_tangent(arena, &vtangents[mf->v2], tang, uv2); + sum_or_add_vertex_tangent(arena, &vtangents[mf->v3], tang, uv3); + + if(mf->v4) { + v4= &mvert[mf->v4]; + + tangent_from_uv(uv1, uv3, uv4, v1->co, v3->co, v4->co, fno, tang); + sum_or_add_vertex_tangent(arena, &vtangents[mf->v1], tang, uv1); + sum_or_add_vertex_tangent(arena, &vtangents[mf->v3], tang, uv3); + sum_or_add_vertex_tangent(arena, &vtangents[mf->v4], tang, uv4); } } - *triangles = tris; - - dm->release(dm); -} - -/* read .bobj.gz file into a fluidsimDerivedMesh struct */ -Mesh* readBobjgz(char *filename, Mesh *orgmesh, float* bbstart, float *bbsize) //, fluidsimDerivedMesh *fsdm) -{ - int wri,i,j; - char debugStrBuffer[256]; - float wrf; - Mesh *newmesh; - const int debugBobjRead = 1; - // init data from old mesh (materials,flags) - MFace *origMFace = &((MFace*) orgmesh->mface)[0]; - int mat_nr = -1; - int flag = -1; - MFace *fsface = NULL; - int gotBytes; - gzFile gzf; - - if(!orgmesh) return NULL; - if(!origMFace) return NULL; - mat_nr = origMFace->mat_nr; - flag = origMFace->flag; - - // similar to copy_mesh - newmesh = MEM_dupallocN(orgmesh); - newmesh->mat= orgmesh->mat; - - newmesh->mvert= NULL; - newmesh->medge= NULL; - newmesh->mface= NULL; - newmesh->mtface= NULL; - - newmesh->dvert = NULL; - - newmesh->mcol= NULL; - newmesh->msticky= NULL; - newmesh->texcomesh= NULL; - memset(&newmesh->vdata, 0, sizeof(newmesh->vdata)); - memset(&newmesh->edata, 0, sizeof(newmesh->edata)); - memset(&newmesh->fdata, 0, sizeof(newmesh->fdata)); - - newmesh->key= NULL; - newmesh->totface = 0; - newmesh->totvert = 0; - newmesh->totedge = 0; - newmesh->medge = NULL; - - - snprintf(debugStrBuffer,256,"Reading '%s' GZ_BOBJ... ",filename); elbeemDebugOut(debugStrBuffer); - gzf = gzopen(filename, "rb"); - // gzf = fopen(filename, "rb"); - // debug: fread(b,c,1,a) = gzread(a,b,c) - if (!gzf) { - //snprintf(debugStrBuffer,256,"readBobjgz::error - Unable to open file for reading '%s'\n", filename); // DEBUG - MEM_freeN(newmesh); - return NULL; - } - - //if(sizeof(wri)!=4) { snprintf(debugStrBuffer,256,"Reading GZ_BOBJ, Invalid int size %d...\n", wri); return NULL; } // paranoia check - gotBytes = gzread(gzf, &wri, sizeof(wri)); - newmesh->totvert = wri; - newmesh->mvert = CustomData_add_layer(&newmesh->vdata, CD_MVERT, CD_CALLOC, NULL, newmesh->totvert); - if(debugBobjRead){ snprintf(debugStrBuffer,256,"#vertices %d ", newmesh->totvert); elbeemDebugOut(debugStrBuffer); } //DEBUG - for(i=0; i<newmesh->totvert;i++) { - //if(debugBobjRead) snprintf(debugStrBuffer,256,"V %d = ",i); - for(j=0; j<3; j++) { - gotBytes = gzread(gzf, &wrf, sizeof( wrf )); - newmesh->mvert[i].co[j] = wrf; - //if(debugBobjRead) snprintf(debugStrBuffer,256,"%25.20f ", wrf); + + /* write tangent to layer */ + for(i=0, tf=mtface, mf=mface; i < totface; mf++, tf++, i++, tangent+=4) { + len= (mf->v4)? 4 : 3; + + if(mtface) { + uv1= tf->uv[0]; + uv2= tf->uv[1]; + uv3= tf->uv[2]; + uv4= tf->uv[3]; } - //if(debugBobjRead) snprintf(debugStrBuffer,256,"\n"); - } - - // should be the same as Vertices.size - gotBytes = gzread(gzf, &wri, sizeof(wri)); - if(wri != newmesh->totvert) { - // complain #vertices has to be equal to #normals, reset&abort - CustomData_free_layer_active(&newmesh->vdata, CD_MVERT, newmesh->totvert); - MEM_freeN(newmesh); - snprintf(debugStrBuffer,256,"Reading GZ_BOBJ, #normals=%d, #vertices=%d, aborting...\n", wri,newmesh->totvert ); - return NULL; - } - for(i=0; i<newmesh->totvert;i++) { - for(j=0; j<3; j++) { - gotBytes = gzread(gzf, &wrf, sizeof( wrf )); - newmesh->mvert[i].no[j] = (short)(wrf*32767.0f); - //newmesh->mvert[i].no[j] = 0.5; // DEBUG tst + else { + uv1= uv[0]; uv2= uv[1]; uv3= uv[2]; uv4= uv[3]; + spheremap(orco[mf->v1][0], orco[mf->v1][1], orco[mf->v1][2], &uv[0][0], &uv[0][1]); + spheremap(orco[mf->v2][0], orco[mf->v2][1], orco[mf->v2][2], &uv[1][0], &uv[1][1]); + spheremap(orco[mf->v3][0], orco[mf->v3][1], orco[mf->v3][2], &uv[2][0], &uv[2][1]); + if(len==4) + spheremap(orco[mf->v4][0], orco[mf->v4][1], orco[mf->v4][2], &uv[3][0], &uv[3][1]); } - //fprintf(stderr," DEBDPCN nm%d, %d = %d,%d,%d \n", - //(int)(newmesh->mvert), i, newmesh->mvert[i].no[0], newmesh->mvert[i].no[1], newmesh->mvert[i].no[2]); - } - //fprintf(stderr," DPCN 0 = %d,%d,%d \n", newmesh->mvert[0].no[0], newmesh->mvert[0].no[1], newmesh->mvert[0].no[2]); + + mf_vi[0]= mf->v1; + mf_vi[1]= mf->v2; + mf_vi[2]= mf->v3; + mf_vi[3]= mf->v4; + + for(j=0; j<len; j++) { + vtang= find_vertex_tangent(vtangents[mf_vi[j]], mtface ? tf->uv[j] : uv[j]); - - /* compute no. of triangles */ - gotBytes = gzread(gzf, &wri, sizeof(wri)); - newmesh->totface = wri; - newmesh->mface = CustomData_add_layer(&newmesh->fdata, CD_MFACE, CD_CALLOC, NULL, newmesh->totface); - if(debugBobjRead){ snprintf(debugStrBuffer,256,"#faces %d ", newmesh->totface); elbeemDebugOut(debugStrBuffer); } //DEBUG - fsface = newmesh->mface; - for(i=0; i<newmesh->totface; i++) { - int face[4]; - - gotBytes = gzread(gzf, &(face[0]), sizeof( face[0] )); - gotBytes = gzread(gzf, &(face[1]), sizeof( face[1] )); - gotBytes = gzread(gzf, &(face[2]), sizeof( face[2] )); - face[3] = 0; - - fsface[i].v1 = face[0]; - fsface[i].v2 = face[1]; - fsface[i].v3 = face[2]; - fsface[i].v4 = face[3]; - } - - // correct triangles with v3==0 for blender, cycle verts - for(i=0; i<newmesh->totface; i++) { - if(!fsface[i].v3) { - int temp = fsface[i].v1; - fsface[i].v1 = fsface[i].v2; - fsface[i].v2 = fsface[i].v3; - fsface[i].v3 = temp; + VECCOPY(tangent[j], vtang); + Normalize(tangent[j]); } } - gzclose( gzf ); - for(i=0;i<newmesh->totface;i++) { - fsface[i].mat_nr = mat_nr; - fsface[i].flag = flag; - fsface[i].edcode = ME_V1V2 | ME_V2V3 | ME_V3V1; - //snprintf(debugStrBuffer,256,"%d : %d,%d,%d\n", i,fsface[i].mat_nr, fsface[i].flag, fsface[i].edcode ); - } - - snprintf(debugStrBuffer,256," (%d,%d) done\n", newmesh->totvert,newmesh->totface); elbeemDebugOut(debugStrBuffer); //DEBUG - return newmesh; -} - -/* read zipped fluidsim velocities into the co's of the fluidsimsettings normals struct */ -void readVelgz(char *filename, Object *srcob) -{ - char debugStrBuffer[256]; - int wri, i, j; - float wrf; - gzFile gzf; - MVert *vverts = srcob->fluidsimSettings->meshSurfNormals; - int len = strlen(filename); - Mesh *mesh = srcob->data; - // mesh and vverts have to be valid from loading... - - // clean up in any case - for(i=0; i<mesh->totvert;i++) { - for(j=0; j<3; j++) { - vverts[i].co[j] = 0.; - } - } - if(srcob->fluidsimSettings->domainNovecgen>0) return; - - if(len<7) { - //printf("readVelgz Eror: invalid filename '%s'\n",filename); // DEBUG - return; - } - - // .bobj.gz , correct filename - // 87654321 - filename[len-6] = 'v'; - filename[len-5] = 'e'; - filename[len-4] = 'l'; - - snprintf(debugStrBuffer,256,"Reading '%s' GZ_VEL... ",filename); elbeemDebugOut(debugStrBuffer); - gzf = gzopen(filename, "rb"); - if (!gzf) { - //printf("readVelgz Eror: unable to open file '%s'\n",filename); // DEBUG - return; - } - - gzread(gzf, &wri, sizeof( wri )); - if(wri != mesh->totvert) { - //printf("readVelgz Eror: invalid no. of velocities %d vs. %d aborting.\n" ,wri ,mesh->totvert ); // DEBUG - return; - } - - for(i=0; i<mesh->totvert;i++) { - for(j=0; j<3; j++) { - gzread(gzf, &wrf, sizeof( wrf )); - vverts[i].co[j] = wrf; - } - //if(i<20) fprintf(stderr, "GZ_VELload %d = %f,%f,%f \n",i,vverts[i].co[0],vverts[i].co[1],vverts[i].co[2]); // DEBUG - } - - gzclose(gzf); + BLI_memarena_free(arena); + MEM_freeN(vtangents); } - -/* ***************************** fluidsim derived mesh ***************************** */ - -/* check which file to load, and replace old mesh of the object with it */ -/* this replacement is undone at the end of mesh_calc_modifiers */ -void loadFluidsimMesh(Object *srcob, int useRenderParams) +void DM_vertex_attributes_from_gpu(DerivedMesh *dm, GPUVertexAttribs *gattribs, DMVertexAttribs *attribs) { - Mesh *mesh = NULL; - float *bbStart = NULL, *bbSize = NULL; - float lastBB[3]; - int displaymode = 0; - int curFrame = G.scene->r.cfra - 1 /*G.scene->r.sfra*/; /* start with 0 at start frame */ - char targetDir[FILE_MAXFILE+FILE_MAXDIR], targetFile[FILE_MAXFILE+FILE_MAXDIR]; - char debugStrBuffer[256]; - //snprintf(debugStrBuffer,256,"loadFluidsimMesh call (obid '%s', rp %d)\n", srcob->id.name, useRenderParams); // debug + CustomData *vdata, *fdata, *tfdata = NULL; + int a, b, layer; - if((!srcob)||(!srcob->fluidsimSettings)) { - snprintf(debugStrBuffer,256,"DEBUG - Invalid loadFluidsimMesh call, rp %d, dm %d)\n", useRenderParams, displaymode); // debug - elbeemDebugOut(debugStrBuffer); // debug - return; - } - // make sure the original mesh data pointer is stored - if(!srcob->fluidsimSettings->orgMesh) { - srcob->fluidsimSettings->orgMesh = srcob->data; - } + /* From the layers requested by the GLSL shader, figure out which ones are + * actually available for this derivedmesh, and retrieve the pointers */ - // free old mesh, if there is one (todo, check if it's still valid?) - if(srcob->fluidsimSettings->meshSurface) { - Mesh *freeFsMesh = srcob->fluidsimSettings->meshSurface; + memset(attribs, 0, sizeof(DMVertexAttribs)); - // similar to free_mesh(...) , but no things like unlink... - CustomData_free(&freeFsMesh->vdata, freeFsMesh->totvert); - CustomData_free(&freeFsMesh->edata, freeFsMesh->totedge); - CustomData_free(&freeFsMesh->fdata, freeFsMesh->totface); - MEM_freeN(freeFsMesh); - - if(srcob->data == srcob->fluidsimSettings->meshSurface) - srcob->data = srcob->fluidsimSettings->orgMesh; - srcob->fluidsimSettings->meshSurface = NULL; - - if(srcob->fluidsimSettings->meshSurfNormals) MEM_freeN(srcob->fluidsimSettings->meshSurfNormals); - srcob->fluidsimSettings->meshSurfNormals = NULL; - } - - // init bounding box - bbStart = srcob->fluidsimSettings->bbStart; - bbSize = srcob->fluidsimSettings->bbSize; - lastBB[0] = bbSize[0]; // TEST - lastBB[1] = bbSize[1]; - lastBB[2] = bbSize[2]; - fluidsimGetAxisAlignedBB(srcob->fluidsimSettings->orgMesh, srcob->obmat, bbStart, bbSize, &srcob->fluidsimSettings->meshBB); - // check free fsmesh... TODO - - if(!useRenderParams) { - displaymode = srcob->fluidsimSettings->guiDisplayMode; - } else { - displaymode = srcob->fluidsimSettings->renderDisplayMode; - } - - snprintf(debugStrBuffer,256,"loadFluidsimMesh call (obid '%s', rp %d, dm %d), curFra=%d, sFra=%d #=%d \n", - srcob->id.name, useRenderParams, displaymode, G.scene->r.cfra, G.scene->r.sfra, curFrame ); // debug - elbeemDebugOut(debugStrBuffer); // debug - - strncpy(targetDir, srcob->fluidsimSettings->surfdataPath, FILE_MAXDIR); - // use preview or final mesh? - if(displaymode==1) { - // just display original object - srcob->data = srcob->fluidsimSettings->orgMesh; - return; - } else if(displaymode==2) { - strcat(targetDir,"fluidsurface_preview_#"); - } else { // 3 - strcat(targetDir,"fluidsurface_final_#"); - } - BLI_convertstringcode(targetDir, G.sce, curFrame); // fixed #frame-no - strcpy(targetFile,targetDir); - strcat(targetFile, ".bobj.gz"); + vdata = &dm->vertData; + fdata = &dm->faceData; - snprintf(debugStrBuffer,256,"loadFluidsimMesh call (obid '%s', rp %d, dm %d) '%s' \n", srcob->id.name, useRenderParams, displaymode, targetFile); // debug - elbeemDebugOut(debugStrBuffer); // debug + /* ugly hack, editmesh derivedmesh doesn't copy face data, this way we + * can use offsets instead */ + if(dm->release == emDM_release) + tfdata = &((EditMeshDerivedMesh*)dm)->em->fdata; + else + tfdata = fdata; + + /* add a tangent layer if necessary */ + for(b = 0; b < gattribs->totlayer; b++) + if(gattribs->layer[b].type == CD_TANGENT) + if(CustomData_get_layer_index(fdata, CD_TANGENT) == -1) + DM_add_tangent_layer(dm); + + for(b = 0; b < gattribs->totlayer; b++) { + if(gattribs->layer[b].type == CD_MTFACE) { + /* uv coordinates */ + if(gattribs->layer[b].name[0]) + layer = CustomData_get_named_layer_index(tfdata, CD_MTFACE, + gattribs->layer[b].name); + else + layer = CustomData_get_active_layer_index(tfdata, CD_MTFACE); - if(displaymode!=2) { // dont add bounding box for final - mesh = readBobjgz(targetFile, srcob->fluidsimSettings->orgMesh ,NULL,NULL); - } else { - mesh = readBobjgz(targetFile, srcob->fluidsimSettings->orgMesh, bbSize,bbSize ); - } - if(!mesh) { - // switch, abort background rendering when fluidsim mesh is missing - const char *strEnvName2 = "BLENDER_ELBEEMBOBJABORT"; // from blendercall.cpp - if(G.background==1) { - if(getenv(strEnvName2)) { - int elevel = atoi(getenv(strEnvName2)); - if(elevel>0) { - printf("Env. var %s set, fluid sim mesh '%s' not found, aborting render...\n",strEnvName2, targetFile); - exit(1); - } + if(layer != -1) { + a = attribs->tottface++; + + attribs->tface[a].array = tfdata->layers[layer].data; + attribs->tface[a].emOffset = tfdata->layers[layer].offset; + attribs->tface[a].glIndex = gattribs->layer[b].glindex; } } - - // display org. object upon failure - srcob->data = srcob->fluidsimSettings->orgMesh; - return; - } + else if(gattribs->layer[b].type == CD_MCOL) { + /* vertex colors */ + if(gattribs->layer[b].name[0]) + layer = CustomData_get_named_layer_index(tfdata, CD_MCOL, + gattribs->layer[b].name); + else + layer = CustomData_get_active_layer_index(tfdata, CD_MCOL); - if((mesh)&&(mesh->totvert>0)) { - make_edges(mesh, 0); // 0 = make all edges draw - } - srcob->fluidsimSettings->meshSurface = mesh; - srcob->data = mesh; - srcob->fluidsimSettings->meshSurfNormals = MEM_dupallocN(mesh->mvert); + if(layer != -1) { + a = attribs->totmcol++; - // load vertex velocities, if they exist... - // TODO? use generate flag as loading flag as well? - // warning, needs original .bobj.gz mesh loading filename - if(displaymode==3) { - readVelgz(targetFile, srcob); - } else { - // no data for preview, only clear... - int i,j; - for(i=0; i<mesh->totvert;i++) { for(j=0; j<3; j++) { srcob->fluidsimSettings->meshSurfNormals[i].co[j] = 0.; }} - } + attribs->mcol[a].array = tfdata->layers[layer].data; + attribs->mcol[a].emOffset = tfdata->layers[layer].offset; + attribs->mcol[a].glIndex = gattribs->layer[b].glindex; + } + } + else if(gattribs->layer[b].type == CD_TANGENT) { + /* tangents */ + layer = CustomData_get_layer_index(fdata, CD_TANGENT); - //fprintf(stderr,"LOADFLM DEBXHCH fs=%d 3:%d,%d,%d \n", (int)mesh, ((Mesh *)(srcob->fluidsimSettings->meshSurface))->mvert[3].no[0], ((Mesh *)(srcob->fluidsimSettings->meshSurface))->mvert[3].no[1], ((Mesh *)(srcob->fluidsimSettings->meshSurface))->mvert[3].no[2]); - return; -} + if(layer != -1) { + attribs->tottang = 1; -/* helper function */ -/* init axis aligned BB for mesh object */ -void fluidsimGetAxisAlignedBB(struct Mesh *mesh, float obmat[][4], - /*RET*/ float start[3], /*RET*/ float size[3], /*RET*/ struct Mesh **bbmesh ) -{ - float bbsx=0.0, bbsy=0.0, bbsz=0.0; - float bbex=1.0, bbey=1.0, bbez=1.0; - int i; - float vec[3]; - - VECCOPY(vec, mesh->mvert[0].co); - Mat4MulVecfl(obmat, vec); - bbsx = vec[0]; bbsy = vec[1]; bbsz = vec[2]; - bbex = vec[0]; bbey = vec[1]; bbez = vec[2]; - - for(i=1; i<mesh->totvert;i++) { - VECCOPY(vec, mesh->mvert[i].co); - Mat4MulVecfl(obmat, vec); - - if(vec[0] < bbsx){ bbsx= vec[0]; } - if(vec[1] < bbsy){ bbsy= vec[1]; } - if(vec[2] < bbsz){ bbsz= vec[2]; } - if(vec[0] > bbex){ bbex= vec[0]; } - if(vec[1] > bbey){ bbey= vec[1]; } - if(vec[2] > bbez){ bbez= vec[2]; } - } - - // return values... - if(start) { - start[0] = bbsx; - start[1] = bbsy; - start[2] = bbsz; - } - if(size) { - size[0] = bbex-bbsx; - size[1] = bbey-bbsy; - size[2] = bbez-bbsz; - } - - // init bounding box mesh? - if(bbmesh) { - int i,j; - Mesh *newmesh = NULL; - if(!(*bbmesh)) { newmesh = MEM_callocN(sizeof(Mesh), "fluidsimGetAxisAlignedBB_meshbb"); } - else { newmesh = *bbmesh; } - - newmesh->totvert = 8; - if(!newmesh->mvert) - newmesh->mvert = CustomData_add_layer(&newmesh->vdata, CD_MVERT, CD_CALLOC, NULL, newmesh->totvert); - for(i=0; i<8; i++) { - for(j=0; j<3; j++) newmesh->mvert[i].co[j] = start[j]; + attribs->tang.array = fdata->layers[layer].data; + attribs->tang.emOffset = fdata->layers[layer].offset; + attribs->tang.glIndex = gattribs->layer[b].glindex; + } } + else if(gattribs->layer[b].type == CD_ORCO) { + /* original coordinates */ + layer = CustomData_get_layer_index(vdata, CD_ORCO); - newmesh->totface = 6; - if(!newmesh->mface) - newmesh->mface = CustomData_add_layer(&newmesh->fdata, CD_MFACE, CD_CALLOC, NULL, newmesh->totface); + if(layer != -1) { + attribs->totorco = 1; - *bbmesh = newmesh; + attribs->orco.array = vdata->layers[layer].data; + attribs->orco.emOffset = vdata->layers[layer].offset; + attribs->orco.glIndex = gattribs->layer[b].glindex; + } + } } } -#else // DISABLE_ELBEEM - -/* dummy for mesh_calc_modifiers */ -void loadFluidsimMesh(Object *srcob, int useRenderParams) { -} - -#endif // DISABLE_ELBEEM - diff --git a/source/blender/blenkernel/intern/Makefile b/source/blender/blenkernel/intern/Makefile index 94aa3db3f9e..4cfc8f91efe 100644 --- a/source/blender/blenkernel/intern/Makefile +++ b/source/blender/blenkernel/intern/Makefile @@ -36,6 +36,7 @@ include nan_compile.mk CFLAGS += $(LEVEL_1_C_WARNINGS) # OpenGL and Python +CPPFLAGS += -I$(NAN_GLEW)/include CPPFLAGS += -I$(OPENGL_HEADERS) CPPFLAGS += -I$(NAN_PYTHON)/include/python$(NAN_PYTHON_VERSION) @@ -66,6 +67,7 @@ CPPFLAGS += $(NAN_SDLCFLAGS) CPPFLAGS += -I$(NAN_IKSOLVER)/include CPPFLAGS += -I$(NAN_DECIMATION)/include CPPFLAGS += -I$(NAN_ELBEEM)/include +CPPFLAGS += -I$(NAN_OPENNL)/include # path to zlib CPPFLAGS += -I$(NAN_ZLIB)/include @@ -73,9 +75,15 @@ CPPFLAGS += -I$(NAN_ZLIB)/include #path to nodes CPPFLAGS += -I../../nodes +#path to gpu +CPPFLAGS += -I../../gpu + # path to our own external headerfiles CPPFLAGS += -I.. +# path to bullet2, for cloth +CPPFLAGS += -I../../../../extern/bullet2/src + ifeq ($(WITH_FREETYPE2), true) CPPFLAGS += -DWITH_FREETYPE2 CPPFLAGS += -I$(NAN_FREETYPE)/include diff --git a/source/blender/blenkernel/intern/action.c b/source/blender/blenkernel/intern/action.c index 3011cc2f99b..1c720cea21f 100644 --- a/source/blender/blenkernel/intern/action.c +++ b/source/blender/blenkernel/intern/action.c @@ -62,6 +62,7 @@ #include "BLI_arithb.h" #include "BLI_blenlib.h" +#include "BLI_ghash.h" //XXX #include "nla.h" @@ -174,24 +175,51 @@ void free_action (bAction *act) if (act->chanbase.first) BLI_freelistN(&act->chanbase); + + /* Free groups */ + if (act->groups.first) + BLI_freelistN(&act->groups); + + /* Free pose-references (aka local markers) */ + if (act->markers.first) + BLI_freelistN(&act->markers); } bAction *copy_action (bAction *src) { bAction *dst = NULL; bActionChannel *dchan, *schan; + bActionGroup *dgrp, *sgrp; if (!src) return NULL; dst= copy_libblock(src); + BLI_duplicatelist(&(dst->chanbase), &(src->chanbase)); + BLI_duplicatelist(&(dst->groups), &(src->groups)); + BLI_duplicatelist(&(dst->markers), &(src->markers)); - for (dchan=dst->chanbase.first, schan=src->chanbase.first; dchan; dchan=dchan->next, schan=schan->next){ + for (dchan=dst->chanbase.first, schan=src->chanbase.first; dchan; dchan=dchan->next, schan=schan->next) { + for (dgrp=dst->groups.first, sgrp=src->groups.first; dgrp && sgrp; dgrp=dgrp->next, sgrp=sgrp->next) { + if (dchan->grp == sgrp) { + dchan->grp= dgrp; + + if (dgrp->channels.first == schan) + dgrp->channels.first= dchan; + if (dgrp->channels.last == schan) + dgrp->channels.last= dchan; + + break; + } + } + dchan->ipo = copy_ipo(dchan->ipo); copy_constraint_channels(&dchan->constraintChannels, &schan->constraintChannels); } + dst->id.flag |= LIB_FAKEUSER; dst->id.us++; + return dst; } @@ -263,6 +291,12 @@ void copy_pose(bPose **dst, bPose *src, int copycon) return; } + if (*dst==src) { + printf("copy_pose source and target are the same\n"); + *dst=NULL; + return; + } + outPose= MEM_callocN(sizeof(bPose), "pose"); BLI_duplicatelist (&outPose->chanbase, &src->chanbase); @@ -282,13 +316,82 @@ void free_pose_channels(bPose *pose) { bPoseChannel *pchan; - if (pose->chanbase.first){ + if (pose->chanbase.first) { for (pchan = pose->chanbase.first; pchan; pchan=pchan->next){ - if(pchan->path) + if (pchan->path) MEM_freeN(pchan->path); free_constraints(&pchan->constraints); } - BLI_freelistN (&pose->chanbase); + BLI_freelistN(&pose->chanbase); + } +} + +void free_pose(bPose *pose) +{ + if (pose) { + /* free pose-channels */ + free_pose_channels(pose); + + /* free pose-groups */ + if (pose->agroups.first) + BLI_freelistN(&pose->agroups); + + /* free pose */ + MEM_freeN(pose); + } +} + +void game_copy_pose(bPose **dst, bPose *src) +{ + bPose *out; + bPoseChannel *pchan, *outpchan; + GHash *ghash; + + /* the game engine copies the current armature pose and then swaps + * the object pose pointer. this makes it possible to change poses + * without affecting the original blender data. */ + + if (!src) { + *dst=NULL; + return; + } + else if (*dst==src) { + printf("copy_pose source and target are the same\n"); + *dst=NULL; + return; + } + + out= MEM_dupallocN(src); + out->agroups.first= out->agroups.last= NULL; + BLI_duplicatelist(&out->chanbase, &src->chanbase); + + /* remap pointers */ + ghash= BLI_ghash_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp); + + pchan= src->chanbase.first; + outpchan= out->chanbase.first; + for (; pchan; pchan=pchan->next, outpchan=outpchan->next) + BLI_ghash_insert(ghash, pchan, outpchan); + + for (pchan=out->chanbase.first; pchan; pchan=pchan->next) { + pchan->parent= BLI_ghash_lookup(ghash, pchan->parent); + pchan->child= BLI_ghash_lookup(ghash, pchan->child); + pchan->path= NULL; + } + + BLI_ghash_free(ghash, NULL, NULL); + + *dst=out; +} + +void game_free_pose(bPose *pose) +{ + if (pose) { + /* we don't free constraints, those are owned by the original pose */ + if(pose->chanbase.first) + BLI_freelistN(&pose->chanbase); + + MEM_freeN(pose); } } @@ -299,16 +402,20 @@ static void copy_pose_channel_data(bPoseChannel *pchan, const bPoseChannel *chan VECCOPY(pchan->loc, chan->loc); VECCOPY(pchan->size, chan->size); QUATCOPY(pchan->quat, chan->quat); + Mat4CpyMat4(pchan->chan_mat, (float(*)[4])chan->chan_mat); + Mat4CpyMat4(pchan->pose_mat, (float(*)[4])chan->pose_mat); pchan->flag= chan->flag; con= chan->constraints.first; - for(pcon= pchan->constraints.first; pcon; pcon= pcon->next) { + for(pcon= pchan->constraints.first; pcon; pcon= pcon->next, con= con->next) { pcon->enforce= con->enforce; pcon->headtail= con->headtail; } } -/* checks for IK constraint, can do more constraints flags later */ +/* checks for IK constraint, and also for Follow-Path constraint. + * can do more constraints flags later + */ /* pose should be entirely OK */ void update_pose_constraint_flags(bPose *pose) { @@ -316,13 +423,15 @@ void update_pose_constraint_flags(bPose *pose) bConstraint *con; /* clear */ - for (pchan = pose->chanbase.first; pchan; pchan=pchan->next) { + for (pchan= pose->chanbase.first; pchan; pchan= pchan->next) { pchan->constflag= 0; } + pose->flag &= ~POSE_CONSTRAINTS_TIMEDEPEND; + /* detect */ - for (pchan = pose->chanbase.first; pchan; pchan=pchan->next) { - for(con= pchan->constraints.first; con; con= con->next) { - if(con->type==CONSTRAINT_TYPE_KINEMATIC) { + for (pchan= pose->chanbase.first; pchan; pchan=pchan->next) { + for (con= pchan->constraints.first; con; con= con->next) { + if (con->type==CONSTRAINT_TYPE_KINEMATIC) { bKinematicConstraint *data = (bKinematicConstraint*)con->data; pchan->constflag |= PCHAN_HAS_IK; @@ -345,7 +454,20 @@ void update_pose_constraint_flags(bPose *pose) } } } - else pchan->constflag |= PCHAN_HAS_CONST; + else if (con->type == CONSTRAINT_TYPE_FOLLOWPATH) { + bFollowPathConstraint *data= (bFollowPathConstraint *)con->data; + + /* for drawing constraint colors when color set allows this */ + pchan->constflag |= PCHAN_HAS_CONST; + + /* if we have a valid target, make sure that this will get updated on frame-change + * (needed for when there is no anim-data for this pose) + */ + if ((data->tar) && (data->tar->type==OB_CURVE)) + pose->flag |= POSE_CONSTRAINTS_TIMEDEPEND; + } + else + pchan->constflag |= PCHAN_HAS_CONST; } } } @@ -385,7 +507,7 @@ bActionChannel *get_action_channel(bAction *act, const char *name) if (!act || !name) return NULL; - for (chan = act->chanbase.first; chan; chan=chan->next){ + for (chan = act->chanbase.first; chan; chan=chan->next) { if (!strcmp (chan->name, name)) return chan; } @@ -393,18 +515,16 @@ bActionChannel *get_action_channel(bAction *act, const char *name) return NULL; } -/* returns existing channel, or adds new one. In latter case it doesnt activate it, context is required for that*/ +/* returns existing channel, or adds new one. In latter case it doesnt activate it, context is required for that */ bActionChannel *verify_action_channel(bAction *act, const char *name) { bActionChannel *chan; chan= get_action_channel(act, name); - if(chan==NULL) { - if (!chan) { - chan = MEM_callocN (sizeof(bActionChannel), "actionChannel"); - strncpy (chan->name, name, 31); - BLI_addtail (&act->chanbase, chan); - } + if (chan == NULL) { + chan = MEM_callocN (sizeof(bActionChannel), "actionChannel"); + strncpy(chan->name, name, 31); + BLI_addtail(&act->chanbase, chan); } return chan; } @@ -432,8 +552,11 @@ static float get_actionstrip_frame(bActionStrip *strip, float cframe, int invert { float length, actlength, repeat, scale; + if (strip->repeat == 0.0f) strip->repeat = 1.0f; repeat = (strip->flag & ACTSTRIP_USESTRIDE) ? (1.0f) : (strip->repeat); - scale = abs(strip->scale); /* scale must be positive (for now) */ + + if (strip->scale == 0.0f) strip->scale= 1.0f; + scale = fabs(strip->scale); /* scale must be positive (for now) */ actlength = strip->actend-strip->actstart; if (actlength == 0.0f) actlength = 1.0f; @@ -680,6 +803,11 @@ void extract_pose_from_pose(bPose *pose, const bPose *src) const bPoseChannel *schan; bPoseChannel *pchan= pose->chanbase.first; + if (pose==src) { + printf("extract_pose_from_pose source and target are the same\n"); + return; + } + for (schan=src->chanbase.first; schan; schan=schan->next, pchan= pchan->next) { copy_pose_channel_data(pchan, schan); } @@ -757,6 +885,12 @@ void copy_pose_result(bPose *to, bPose *from) return; } + if (to==from) { + printf("copy_pose_result source and target are the same\n"); + return; + } + + for(pchanfrom= from->chanbase.first; pchanfrom; pchanfrom= pchanfrom->next) { pchanto= get_pose_channel(to, pchanfrom->name); if(pchanto) { @@ -783,7 +917,7 @@ typedef struct NlaIpoChannel { int type; } NlaIpoChannel; -static void extract_ipochannels_from_action(ListBase *lb, ID *id, bAction *act, char *name, float ctime) +void extract_ipochannels_from_action(ListBase *lb, ID *id, bAction *act, const char *name, float ctime) { bActionChannel *achan= get_action_channel(act, name); IpoCurve *icu; @@ -876,15 +1010,18 @@ static void blend_ipochannels(ListBase *dst, ListBase *src, float srcweight, int } } -static void execute_ipochannels(ListBase *lb) +int execute_ipochannels(ListBase *lb) { NlaIpoChannel *nic; + int count = 0; for(nic= lb->first; nic; nic= nic->next) { if(nic->poin) { write_ipo_poin(nic->poin, nic->type, nic->val); + count++; } } + return count; } /* nla timing */ @@ -901,11 +1038,7 @@ static float nla_time(float cfra, float unit) /* global time */ cfra*= G.scene->r.framelen; - - - /* decide later... */ -// if(no_speed_curve==0) if(ob && ob->ipo) cfra= calc_ipo_time(ob->ipo, cfra); - + return cfra; } @@ -915,7 +1048,7 @@ static float nla_time(float cfra, float unit) static float stridechannel_frame(Object *ob, float sizecorr, bActionStrip *strip, Path *path, float pathdist, float *stride_offset) { bAction *act= strip->act; - char *name= strip->stridechannel; + const char *name= strip->stridechannel; bActionChannel *achan= get_action_channel(act, name); int stride_axis= strip->stride_axis; @@ -1083,12 +1216,13 @@ void what_does_obaction (Object *ob, bAction *act, float cframe) workob.constraints.first = ob->constraints.first; workob.constraints.last = ob->constraints.last; - strcpy(workob.parsubstr, ob->parsubstr); + strcpy(workob.parsubstr, ob->parsubstr); + strcpy(workob.id.name, ob->id.name); /* extract_ipochannels_from_action needs id's! */ workob.action= act; - extract_ipochannels_from_action(&tchanbase, &ob->id, act, "Object", bsystem_time(&workob, cframe, 0.0)); + extract_ipochannels_from_action(&tchanbase, &workob.id, act, "Object", bsystem_time(&workob, cframe, 0.0)); if (tchanbase.first) { execute_ipochannels(&tchanbase); @@ -1308,10 +1442,8 @@ static void do_nla(Object *ob, int blocktype) } /* free */ - if (tpose){ - free_pose_channels(tpose); - MEM_freeN(tpose); - } + if (tpose) + free_pose(tpose); if(chanbase.first) BLI_freelistN(&chanbase); } diff --git a/source/blender/blenkernel/intern/anim.c b/source/blender/blenkernel/intern/anim.c index 08648aa6e10..aeddabf8c1b 100644 --- a/source/blender/blenkernel/intern/anim.c +++ b/source/blender/blenkernel/intern/anim.c @@ -44,6 +44,7 @@ #include "DNA_key_types.h" #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" +#include "DNA_modifier_types.h" #include "DNA_object_types.h" #include "DNA_particle_types.h" #include "DNA_scene_types.h" @@ -51,6 +52,7 @@ #include "DNA_vfont_types.h" #include "BKE_anim.h" +#include "BKE_curve.h" #include "BKE_DerivedMesh.h" #include "BKE_displist.h" #include "BKE_effect.h" @@ -61,6 +63,7 @@ #include "BKE_key.h" #include "BKE_lattice.h" #include "BKE_main.h" +#include "BKE_mesh.h" #include "BKE_object.h" #include "BKE_particle.h" #include "BKE_utildefines.h" @@ -69,6 +72,8 @@ #include <config.h> #endif +static void object_duplilist_recursive(ID *id, Object *ob, ListBase *duplilist, float par_space_mat[][4], int level, int animated); + void free_path(Path *path) { if(path->data) MEM_freeN(path->data); @@ -112,7 +117,7 @@ void calc_curvepath(Object *ob) path->len= tot+1; /* exception: vector handle paths and polygon paths should be subdivided at least a factor resolu */ - if(path->len<nu->resolu*nu->pntsu) path->len= nu->resolu*nu->pntsu; + if(path->len<nu->resolu*SEGMENTSU(nu)) path->len= nu->resolu*SEGMENTSU(nu); dist= (float *)MEM_mallocN((tot+1)*4, "calcpathdist"); @@ -275,7 +280,7 @@ int where_on_path(Object *ob, float ctime, float *vec, float *dir) /* returns OK /* ****************** DUPLICATOR ************** */ -static DupliObject *new_dupli_object(ListBase *lb, Object *ob, float mat[][4], int lay, int index) +static DupliObject *new_dupli_object(ListBase *lb, Object *ob, float mat[][4], int lay, int index, int type, int animated) { DupliObject *dob= MEM_callocN(sizeof(DupliObject), "dupliobject"); @@ -285,50 +290,66 @@ static DupliObject *new_dupli_object(ListBase *lb, Object *ob, float mat[][4], i Mat4CpyMat4(dob->omat, ob->obmat); dob->origlay= ob->lay; dob->index= index; + dob->type= type; + dob->animated= (type == OB_DUPLIGROUP) && animated; ob->lay= lay; return dob; } -static void group_duplilist(ListBase *lb, Object *ob, int level) +static void group_duplilist(ListBase *lb, Object *ob, int level, int animated) { DupliObject *dob; Group *group; GroupObject *go; - float mat[4][4]; + float mat[4][4], tmat[4][4]; if(ob->dup_group==NULL) return; group= ob->dup_group; /* simple preventing of too deep nested groups */ - if(level>4) return; + if(level>MAX_DUPLI_RECUR) return; /* handles animated groups, and */ /* we need to check update for objects that are not in scene... */ group_handle_recalc_and_update(ob, group); + animated= animated || group_is_animated(ob, group); for(go= group->gobject.first; go; go= go->next) { /* note, if you check on layer here, render goes wrong... it still deforms verts and uses parent imat */ if(go->ob!=ob) { - Mat4MulMat4(mat, go->ob->obmat, ob->obmat); - dob= new_dupli_object(lb, go->ob, mat, ob->lay, 0); + + /* Group Dupli Offset, should apply after everything else */ + if (group->dupli_ofs[0] || group->dupli_ofs[1] || group->dupli_ofs[2]) { + Mat4CpyMat4(tmat, go->ob->obmat); + VecSubf(tmat[3], tmat[3], group->dupli_ofs); + Mat4MulMat4(mat, tmat, ob->obmat); + } else { + Mat4MulMat4(mat, go->ob->obmat, ob->obmat); + } + + dob= new_dupli_object(lb, go->ob, mat, ob->lay, 0, OB_DUPLIGROUP, animated); dob->no_draw= (dob->origlay & group->layer)==0; - if(go->ob->dup_group && (go->ob->transflag & OB_DUPLIGROUP)) { + if(go->ob->transflag & OB_DUPLI) { Mat4CpyMat4(dob->ob->obmat, dob->mat); - group_duplilist(lb, go->ob, level+1); + object_duplilist_recursive((ID *)group, go->ob, lb, ob->obmat, level+1, animated); Mat4CpyMat4(dob->ob->obmat, dob->omat); } } } } -static void frames_duplilist(ListBase *lb, Object *ob) +static void frames_duplilist(ListBase *lb, Object *ob, int level, int animated) { extern int enable_cu_speed; /* object.c */ Object copyob; + DupliObject *dob; int cfrao, ok; + /* simple preventing of too deep nested groups */ + if(level>MAX_DUPLI_RECUR) return; + cfrao= G.scene->r.cfra; if(ob->parent==NULL && ob->track==NULL && ob->ipo==NULL && ob->constraints.first==NULL) return; @@ -347,7 +368,8 @@ static void frames_duplilist(ListBase *lb, Object *ob) if(ok) { do_ob_ipo(ob); where_is_object_time(ob, (float)G.scene->r.cfra); - new_dupli_object(lb, ob, ob->obmat, ob->lay, G.scene->r.cfra); + dob= new_dupli_object(lb, ob, ob->obmat, ob->lay, G.scene->r.cfra, OB_DUPLIFRAMES, animated); + Mat4CpyMat4(dob->omat, copyob.obmat); } } @@ -357,22 +379,28 @@ static void frames_duplilist(ListBase *lb, Object *ob) } struct vertexDupliData { + ID *id; /* scene or group, for recursive loops */ + int level; + int animated; ListBase *lb; float pmat[4][4]; + float obmat[4][4]; /* Only used for dupliverts inside dupligroups, where the ob->obmat is modified */ Object *ob, *par; + float (*orco)[3]; }; static void vertex_dupli__mapFunc(void *userData, int index, float *co, float *no_f, short *no_s) { + DupliObject *dob; struct vertexDupliData *vdd= userData; - float vec[3], *q2, mat[3][3], tmat[4][4], obmat[4][4]; + float vec[3], q2[4], mat[3][3], tmat[4][4], obmat[4][4]; VECCOPY(vec, co); Mat4MulVecfl(vdd->pmat, vec); VecSubf(vec, vec, vdd->pmat[3]); - VecAddf(vec, vec, vdd->ob->obmat[3]); + VecAddf(vec, vec, vdd->obmat[3]); - Mat4CpyMat4(obmat, vdd->ob->obmat); + Mat4CpyMat4(obmat, vdd->obmat); VECCOPY(obmat[3], vec); if(vdd->par->transflag & OB_DUPLIROT) { @@ -383,44 +411,98 @@ static void vertex_dupli__mapFunc(void *userData, int index, float *co, float *n vec[0]= -no_s[0]; vec[1]= -no_s[1]; vec[2]= -no_s[2]; } - q2= vectoquat(vec, vdd->ob->trackflag, vdd->ob->upflag); + vectoquat(vec, vdd->ob->trackflag, vdd->ob->upflag, q2); QuatToMat3(q2, mat); Mat4CpyMat4(tmat, obmat); Mat4MulMat43(obmat, tmat, mat); } - new_dupli_object(vdd->lb, vdd->ob, obmat, vdd->par->lay, index); + dob= new_dupli_object(vdd->lb, vdd->ob, obmat, vdd->par->lay, index, OB_DUPLIVERTS, vdd->animated); + if(vdd->orco) + VECCOPY(dob->orco, vdd->orco[index]); + + if(vdd->ob->transflag & OB_DUPLI) { + float tmpmat[4][4]; + Mat4CpyMat4(tmpmat, vdd->ob->obmat); + Mat4CpyMat4(vdd->ob->obmat, obmat); /* pretend we are really this mat */ + object_duplilist_recursive((ID *)vdd->id, vdd->ob, vdd->lb, obmat, vdd->level+1, vdd->animated); + Mat4CpyMat4(vdd->ob->obmat, tmpmat); + } } -static void vertex_duplilist(ListBase *lb, Scene *sce, Object *par) +static void vertex_duplilist(ListBase *lb, ID *id, Object *par, float par_space_mat[][4], int level, int animated) { - Object *ob; - Base *base; + Object *ob, *ob_iter; + Mesh *me; + Base *base = NULL; float vec[3], no[3], pmat[4][4]; - int lay, totvert, a; + int lay, totvert, a, oblay; DerivedMesh *dm; + struct vertexDupliData vdd; + Scene *sce = NULL; + Group *group = NULL; + GroupObject * go = NULL; Mat4CpyMat4(pmat, par->obmat); - lay= G.scene->lay; - + /* simple preventing of too deep nested groups */ + if(level>MAX_DUPLI_RECUR) return; + if(par==G.obedit) dm= editmesh_get_derived_cage(CD_MASK_BAREMESH); else - dm = mesh_get_derived_deform(par, CD_MASK_BAREMESH); + dm= mesh_get_derived_deform(par, CD_MASK_BAREMESH); + + if(G.rendering) { + me= par->data; + vdd.orco= (float(*)[3])get_mesh_orco_verts(par); + transform_mesh_orco_verts(me, vdd.orco, me->totvert, 0); + } + else + vdd.orco= NULL; totvert = dm->getNumVerts(dm); - base= sce->base.first; - while(base) { - - if(base->object->type>0 && (lay & base->lay) && G.obedit!=base->object) { - ob= base->object->parent; + /* having to loop on scene OR group objects is NOT FUN */ + if (GS(id->name) == ID_SCE) { + sce = (Scene *)id; + lay= sce->lay; + base= sce->base.first; + } else { + group = (Group *)id; + lay= group->layer; + go = group->gobject.first; + } + + /* Start looping on Scene OR Group objects */ + while (base || go) { + if (sce) { + ob_iter= base->object; + oblay = base->lay; + } else { + ob_iter= go->ob; + oblay = ob_iter->lay; + } + + if (lay & oblay && G.obedit!=ob_iter) { + ob=ob_iter->parent; while(ob) { if(ob==par) { - struct vertexDupliData vdd; + ob = ob_iter; + /* End Scene/Group object loop, below is generic */ - ob= base->object; + + /* par_space_mat - only used for groups so we can modify the space dupli's are in + when par_space_mat is NULL ob->obmat can be used instead of ob__obmat + */ + if(par_space_mat) + Mat4MulMat4(vdd.obmat, ob->obmat, par_space_mat); + else + Mat4CpyMat4(vdd.obmat, ob->obmat); + + vdd.id= id; + vdd.level= level; + vdd.animated= animated; vdd.lb= lb; vdd.ob= ob; vdd.par= par; @@ -446,26 +528,37 @@ static void vertex_duplilist(ListBase *lb, Scene *sce, Object *par) ob= ob->parent; } } - base= base->next; + if (sce) base= base->next; /* scene loop */ + else go= go->next; /* group loop */ } + if(vdd.orco) + MEM_freeN(vdd.orco); dm->release(dm); } -static void face_duplilist(ListBase *lb, Scene *sce, Object *par) +static void face_duplilist(ListBase *lb, ID *id, Object *par, float par_space_mat[][4], int level, int animated) { - Object *ob; - Base *base; + Object *ob, *ob_iter; + Base *base = NULL; + DupliObject *dob; DerivedMesh *dm; + Mesh *me; + MTFace *mtface; MFace *mface; MVert *mvert; - float pmat[4][4], imat[3][3]; - int lay, totface, a; - - Mat4CpyMat4(pmat, par->obmat); + float pmat[4][4], imat[3][3], (*orco)[3] = NULL, w; + int lay, oblay, totface, a; + Scene *sce = NULL; + Group *group = NULL; + GroupObject *go = NULL; + float ob__obmat[4][4]; /* needed for groups where the object matrix needs to be modified */ - lay= G.scene->lay; + /* simple preventing of too deep nested groups */ + if(level>MAX_DUPLI_RECUR) return; + Mat4CpyMat4(pmat, par->obmat); + if(par==G.obedit) { int totvert; dm= editmesh_get_derived_cage(CD_MASK_BAREMESH); @@ -484,26 +577,69 @@ static void face_duplilist(ListBase *lb, Scene *sce, Object *par) mface= dm->getFaceArray(dm); mvert= dm->getVertArray(dm); } + + if(G.rendering) { + me= (Mesh*)par->data; + + orco= (float(*)[3])get_mesh_orco_verts(par); + transform_mesh_orco_verts(me, orco, me->totvert, 0); + mtface= me->mtface; + } + else { + orco= NULL; + mtface= NULL; + } + /* having to loop on scene OR group objects is NOT FUN */ + if (GS(id->name) == ID_SCE) { + sce = (Scene *)id; + lay= sce->lay; + base= sce->base.first; + } else { + group = (Group *)id; + lay= group->layer; + go = group->gobject.first; + } - for(base= sce->base.first; base; base= base->next) { + /* Start looping on Scene OR Group objects */ + while (base || go) { + if (sce) { + ob_iter= base->object; + oblay = base->lay; + } else { + ob_iter= go->ob; + oblay = ob_iter->lay; + } - if(base->object->type>0 && (lay & base->lay) && G.obedit!=base->object) { - ob= base->object->parent; + if (lay & oblay && G.obedit!=ob_iter) { + ob=ob_iter->parent; while(ob) { if(ob==par) { + ob = ob_iter; + /* End Scene/Group object loop, below is generic */ - ob= base->object; - Mat3CpyMat4(imat, ob->parentinv); + /* par_space_mat - only used for groups so we can modify the space dupli's are in + when par_space_mat is NULL ob->obmat can be used instead of ob__obmat + */ + if(par_space_mat) + Mat4MulMat4(ob__obmat, ob->obmat, par_space_mat); + else + Mat4CpyMat4(ob__obmat, ob->obmat); + Mat3CpyMat4(imat, ob->parentinv); + /* mballs have a different dupli handling */ if(ob->type!=OB_MBALL) ob->flag |= OB_DONE; /* doesnt render */ for(a=0; a<totface; a++) { - float *v1= mvert[ mface[a].v1 ].co; - float *v2= mvert[ mface[a].v2 ].co; - float *v3= mvert[ mface[a].v3 ].co; - float *v4= mface[a].v4?mvert[ mface[a].v4 ].co:NULL; + int mv1 = mface[a].v1; + int mv2 = mface[a].v2; + int mv3 = mface[a].v3; + int mv4 = mface[a].v4; + float *v1= mvert[mv1].co; + float *v2= mvert[mv2].co; + float *v3= mvert[mv3].co; + float *v4= (mv4)? mvert[mv4].co: NULL; float cent[3], quat[4], mat[3][3], mat3[3][3], tmat[4][4], obmat[4][4]; /* translation */ @@ -514,9 +650,10 @@ static void face_duplilist(ListBase *lb, Scene *sce, Object *par) Mat4MulVecfl(pmat, cent); VecSubf(cent, cent, pmat[3]); - VecAddf(cent, cent, ob->obmat[3]); + VecAddf(cent, cent, ob__obmat[3]); + + Mat4CpyMat4(obmat, ob__obmat); - Mat4CpyMat4(obmat, ob->obmat); VECCOPY(obmat[3], cent); /* rotation */ @@ -536,8 +673,40 @@ static void face_duplilist(ListBase *lb, Scene *sce, Object *par) Mat4CpyMat4(tmat, obmat); Mat4MulMat43(obmat, tmat, mat); - new_dupli_object(lb, ob, obmat, lay, a); - + dob= new_dupli_object(lb, ob, obmat, lay, a, OB_DUPLIFACES, animated); + if(G.rendering) { + w= (mv4)? 0.25f: 1.0f/3.0f; + + if(orco) { + VECADDFAC(dob->orco, dob->orco, orco[mv1], w); + VECADDFAC(dob->orco, dob->orco, orco[mv2], w); + VECADDFAC(dob->orco, dob->orco, orco[mv3], w); + if(mv4) + VECADDFAC(dob->orco, dob->orco, orco[mv4], w); + } + + if(mtface) { + dob->uv[0] += w*mtface[a].uv[0][0]; + dob->uv[1] += w*mtface[a].uv[0][1]; + dob->uv[0] += w*mtface[a].uv[1][0]; + dob->uv[1] += w*mtface[a].uv[1][1]; + dob->uv[0] += w*mtface[a].uv[2][0]; + dob->uv[1] += w*mtface[a].uv[2][1]; + + if(mv4) { + dob->uv[0] += w*mtface[a].uv[3][0]; + dob->uv[1] += w*mtface[a].uv[3][1]; + } + } + } + + if(ob->transflag & OB_DUPLI) { + float tmpmat[4][4]; + Mat4CpyMat4(tmpmat, ob->obmat); + Mat4CpyMat4(ob->obmat, obmat); /* pretend we are really this mat */ + object_duplilist_recursive((ID *)id, ob, lb, ob->obmat, level+1, animated); + Mat4CpyMat4(ob->obmat, tmpmat); + } } break; @@ -545,34 +714,51 @@ static void face_duplilist(ListBase *lb, Scene *sce, Object *par) ob= ob->parent; } } + if (sce) base= base->next; /* scene loop */ + else go= go->next; /* group loop */ } if(par==G.obedit) { MEM_freeN(mface); MEM_freeN(mvert); } + + if(orco) + MEM_freeN(orco); dm->release(dm); } -static void new_particle_duplilist(ListBase *lb, Scene *sce, Object *par, ParticleSystem *psys) +static void new_particle_duplilist(ListBase *lb, ID *id, Object *par, float par_space_mat[][4], ParticleSystem *psys, int level, int animated) { GroupObject *go; - Object *ob, **oblist=0; + Object *ob=0, **oblist=0, obcopy, *obcopylist=0; + DupliObject *dob; ParticleSettings *part; ParticleData *pa; + ChildParticle *cpa=0; ParticleKey state; - float ctime, pa_time; - float tmat[4][4], mat[3][3], obrotmat[3][3], parotmat[3][3], size=0.0; - float xvec[3] = {-1.0, 0.0, 0.0}, *q; - int lay, a, k, step_nbr = 0, counter; + ParticleCacheKey *cache; + ParticleSystemModifierData *psmd; + float ctime, pa_time, scale = 1.0f; + float tmat[4][4], mat[4][4], pamat[4][4], size=0.0; + float (*obmat)[4], (*oldobmat)[4]; + int lay, a, b, k, step_nbr = 0, counter, hair = 0; int totpart, totchild, totgroup=0, pa_num; if(psys==0) return; + /* simple preventing of too deep nested groups */ + if(level>MAX_DUPLI_RECUR) return; + part=psys->part; + psmd= psys_get_modifier(par, psys); + + if(part==0) + return; - if(part==0) return; + if(!psys_check_enabled(par, psys)) + return; ctime = bsystem_time(par, (float)G.scene->r.cfra, 0.0); @@ -580,7 +766,7 @@ static void new_particle_duplilist(ListBase *lb, Scene *sce, Object *par, Partic totchild = psys->totchild; BLI_srandom(31415926 + psys->seed); - + lay= G.scene->lay; if((part->draw_as == PART_DRAW_OB && part->dup_ob) || (part->draw_as == PART_DRAW_GR && part->dup_group && part->dup_group->gobject.first)) { @@ -590,103 +776,171 @@ static void new_particle_duplilist(ListBase *lb, Scene *sce, Object *par, Partic else step_nbr = 0; + /* if we have a hair particle system, use the path cache */ + if(part->type == PART_HAIR) { + if(psys->flag & PSYS_HAIR_DONE) + hair= (totchild == 0 || psys->childcache) && psys->pathcache; + if(!hair) + return; + + /* we use cache, update totchild according to cached data */ + totchild = psys->totchildcache; + totpart = psys->totcached; + } + psys->lattice = psys_get_lattice(par, psys); + /* gather list of objects or single object */ if(part->draw_as==PART_DRAW_GR) { group_handle_recalc_and_update(par, part->dup_group); - go= part->dup_group->gobject.first; - while(go) { - go=go->next; + for(go=part->dup_group->gobject.first; go; go=go->next) totgroup++; - } - oblist= MEM_callocN(totgroup*sizeof(Object *), "dupgroup object list"); - go= part->dup_group->gobject.first; - for(a=0; a<totgroup; a++, go=go->next) - oblist[a]=go->ob; + /* we also copy the actual objects to restore afterwards, since + * where_is_object_time will change the object which breaks transform */ + oblist = MEM_callocN(totgroup*sizeof(Object *), "dupgroup object list"); + obcopylist = MEM_callocN(totgroup*sizeof(Object), "dupgroup copy list"); + + go = part->dup_group->gobject.first; + for(a=0; a<totgroup; a++, go=go->next) { + oblist[a] = go->ob; + obcopylist[a] = *go->ob; + } + } + else { + ob = part->dup_ob; + obcopy = *ob; } if(totchild==0 || part->draw & PART_DRAW_PARENT) - a=0; + a = 0; else - a=totpart; + a = totpart; for(pa=psys->particles,counter=0; a<totpart+totchild; a++,pa++,counter++) { if(a<totpart) { - if(pa->flag & (PARS_UNEXIST+PARS_NO_DISP)) continue; - - pa_num=pa->num; + /* handle parent particle */ + if(pa->flag & (PARS_UNEXIST+PARS_NO_DISP)) + continue; - pa_time=pa->time; - - size=pa->size; + pa_num = pa->num; + pa_time = pa->time; + size = pa->size; } else { - /* TODO: figure these two out */ - pa_num = a; - pa_time = psys->particles[psys->child[a - totpart].parent].time; + /* handle child particle */ + cpa = &psys->child[a - totpart]; - size=psys_get_child_size(psys, &psys->child[a - totpart], ctime, 0); + pa_num = a; + pa_time = psys->particles[cpa->parent].time; + size = psys_get_child_size(psys, cpa, ctime, 0); } if(part->draw_as==PART_DRAW_GR) { + /* for groups, pick the object based on settings */ if(part->draw&PART_DRAW_RAND_GR) - ob = oblist[BLI_rand() % totgroup]; + b= BLI_rand() % totgroup; else if(part->from==PART_FROM_PARTICLE) - ob = oblist[pa_num % totgroup]; + b= pa_num % totgroup; else - ob = oblist[a % totgroup]; + b= a % totgroup; + + ob = oblist[b]; + obmat = oblist[b]->obmat; + oldobmat = obcopylist[b].obmat; + } + else { + obmat= ob->obmat; + oldobmat= obcopy.obmat; } - else - ob = part->dup_ob; for(k=0; k<=step_nbr; k++, counter++) { - if(step_nbr) { + if(hair) { + /* hair we handle separate and compute transform based on hair keys */ + if(a < totpart) { + cache = psys->pathcache[a]; + psys_get_dupli_path_transform(par, psys, psmd, pa, 0, cache, pamat, &scale); + } + else { + cache = psys->childcache[a-totpart]; + psys_get_dupli_path_transform(par, psys, psmd, 0, cpa, cache, pamat, &scale); + } + + VECCOPY(pamat[3], cache->co); + pamat[3][3]= 1.0f; + + } + else if(step_nbr) { + /* other keys */ state.time = (float)k / (float)step_nbr; psys_get_particle_on_path(par, psys, a, &state, 0); + + QuatToMat4(state.rot, pamat); + VECCOPY(pamat[3], state.co); + pamat[3][3]= 1.0f; } else { + /* first key */ state.time = -1.0; if(psys_get_particle_state(par, psys, a, &state, 0) == 0) continue; - } - QuatToMat3(state.rot, parotmat); + QuatToMat4(state.rot, pamat); + VECCOPY(pamat[3], state.co); + pamat[3][3]= 1.0f; + } if(part->draw_as==PART_DRAW_GR && psys->part->draw & PART_DRAW_WHOLE_GR) { - for(go= part->dup_group->gobject.first; go; go= go->next) { - - Mat4CpyMat4(tmat, go->ob->obmat); - Mat4MulMat43(tmat, go->ob->obmat, parotmat); - Mat4MulFloat3((float *)tmat, size); - - VECADD(tmat[3], go->ob->obmat[3], state.co); + for(go= part->dup_group->gobject.first, b=0; go; go= go->next, b++) { + Mat4MulMat4(tmat, oblist[b]->obmat, pamat); + Mat4MulFloat3((float *)tmat, size*scale); + if(par_space_mat) + Mat4MulMat4(mat, tmat, par_space_mat); + else + Mat4CpyMat4(mat, tmat); - new_dupli_object(lb, go->ob, tmat, par->lay, counter); + dob= new_dupli_object(lb, go->ob, mat, par->lay, counter, OB_DUPLIPARTS, animated); + Mat4CpyMat4(dob->omat, obcopylist[b].obmat); + if(G.rendering) + psys_get_dupli_texture(par, part, psmd, pa, cpa, dob->uv, dob->orco); } } else { /* to give ipos in object correct offset */ where_is_object_time(ob, ctime-pa_time); - q = vectoquat(xvec, ob->trackflag, ob->upflag); - QuatToMat3(q, obrotmat); - - Mat3MulMat3(mat, parotmat, obrotmat); - Mat4CpyMat4(tmat, ob->obmat); - Mat4MulMat43(tmat, ob->obmat, mat); - Mat4MulFloat3((float *)tmat, size); + Mat4CpyMat4(mat, pamat); - VECCOPY(tmat[3], state.co); + Mat4MulMat4(tmat, obmat, mat); + Mat4MulFloat3((float *)tmat, size*scale); + if(par_space_mat) + Mat4MulMat4(mat, tmat, par_space_mat); + else + Mat4CpyMat4(mat, tmat); - new_dupli_object(lb, ob, tmat, par->lay, counter); + dob= new_dupli_object(lb, ob, mat, par->lay, counter, OB_DUPLIPARTS, animated); + Mat4CpyMat4(dob->omat, oldobmat); + if(G.rendering) + psys_get_dupli_texture(par, part, psmd, pa, cpa, dob->uv, dob->orco); } } } + + /* restore objects since they were changed in where_is_object_time */ + if(part->draw_as==PART_DRAW_GR) { + for(a=0; a<totgroup; a++) + *(oblist[a])= obcopylist[a]; + } + else + *ob= obcopy; } + + /* clean up */ if(oblist) MEM_freeN(oblist); + if(obcopylist) + MEM_freeN(obcopylist); if(psys->lattice) { end_latt_deform(); @@ -717,7 +971,7 @@ static Object *find_family_object(Object **obar, char *family, char ch) } -static void font_duplilist(ListBase *lb, Object *par) +static void font_duplilist(ListBase *lb, Object *par, int level, int animated) { Object *ob, *obar[256]; Curve *cu; @@ -725,6 +979,9 @@ static void font_duplilist(ListBase *lb, Object *par) float vec[3], obmat[4][4], pmat[4][4], fsize, xof, yof; int slen, a; + /* simple preventing of too deep nested groups */ + if(level>MAX_DUPLI_RECUR) return; + Mat4CpyMat4(pmat, par->obmat); /* in par the family name is stored, use this to find the other objects */ @@ -755,54 +1012,73 @@ static void font_duplilist(ListBase *lb, Object *par) Mat4CpyMat4(obmat, par->obmat); VECCOPY(obmat[3], vec); - new_dupli_object(lb, ob, obmat, par->lay, a); + new_dupli_object(lb, ob, obmat, par->lay, a, OB_DUPLIVERTS, animated); } - } MEM_freeN(chartransdata); } /* ***************************** */ +static void object_duplilist_recursive(ID *id, Object *ob, ListBase *duplilist, float par_space_mat[][4], int level, int animated) +{ + if((ob->transflag & OB_DUPLI)==0) + return; + + /* Should the dupli's be generated for this object? - Respect restrict flags */ + if (G.rendering) { + if (ob->restrictflag & OB_RESTRICT_RENDER) { + return; + } + } else { + if (ob->restrictflag & OB_RESTRICT_VIEW) { + return; + } + } -/* note; group dupli's already set transform matrix. see note in group_duplilist() */ -ListBase *object_duplilist(Scene *sce, Object *ob) -{ - ListBase *duplilist= MEM_mallocN(sizeof(ListBase), "duplilist"); - duplilist->first= duplilist->last= NULL; - - if(ob->transflag & OB_DUPLI) { - if(ob->transflag & OB_DUPLIPARTS) { - ParticleSystem *psys = ob->particlesystem.first; - for(; psys; psys=psys->next) - new_particle_duplilist(duplilist, sce, ob, psys); + if(ob->transflag & OB_DUPLIPARTS) { + ParticleSystem *psys = ob->particlesystem.first; + for(; psys; psys=psys->next) + new_particle_duplilist(duplilist, id, ob, par_space_mat, psys, level+1, animated); + } + else if(ob->transflag & OB_DUPLIVERTS) { + if(ob->type==OB_MESH) { + vertex_duplilist(duplilist, id, ob, par_space_mat, level+1, animated); } - else if(ob->transflag & OB_DUPLIVERTS) { - if(ob->type==OB_MESH) { - vertex_duplilist(duplilist, sce, ob); - } - else if(ob->type==OB_FONT) { - font_duplilist(duplilist, ob); + else if(ob->type==OB_FONT) { + if (GS(id->name)==ID_SCE) { /* TODO - support dupligroups */ + font_duplilist(duplilist, ob, level+1, animated); } } - else if(ob->transflag & OB_DUPLIFACES) { - if(ob->type==OB_MESH) - face_duplilist(duplilist, sce, ob); + } + else if(ob->transflag & OB_DUPLIFACES) { + if(ob->type==OB_MESH) + face_duplilist(duplilist, id, ob, par_space_mat, level+1, animated); + } + else if(ob->transflag & OB_DUPLIFRAMES) { + if (GS(id->name)==ID_SCE) { /* TODO - support dupligroups */ + frames_duplilist(duplilist, ob, level+1, animated); } - else if(ob->transflag & OB_DUPLIFRAMES) - frames_duplilist(duplilist, ob); - else if(ob->transflag & OB_DUPLIGROUP) { - DupliObject *dob; - - group_duplilist(duplilist, ob, 0); /* now recursive */ + } else if(ob->transflag & OB_DUPLIGROUP) { + DupliObject *dob; + + group_duplilist(duplilist, ob, level+1, animated); /* now recursive */ - /* make copy already, because in group dupli's deform displists can be made, requiring parent matrices */ + if (level==0) { for(dob= duplilist->first; dob; dob= dob->next) - Mat4CpyMat4(dob->ob->obmat, dob->mat); + if(dob->type == OB_DUPLIGROUP) + Mat4CpyMat4(dob->ob->obmat, dob->mat); } - } - +} + +/* Returns a list of DupliObject + * note; group dupli's already set transform matrix. see note in group_duplilist() */ +ListBase *object_duplilist(Scene *sce, Object *ob) +{ + ListBase *duplilist= MEM_mallocN(sizeof(ListBase), "duplilist"); + duplilist->first= duplilist->last= NULL; + object_duplilist_recursive((ID *)sce, ob, duplilist, NULL, 0, 0); return duplilist; } diff --git a/source/blender/blenkernel/intern/armature.c b/source/blender/blenkernel/intern/armature.c index 4d50c268397..8bb694f45e8 100644 --- a/source/blender/blenkernel/intern/armature.c +++ b/source/blender/blenkernel/intern/armature.c @@ -30,6 +30,8 @@ #include <math.h> #include <string.h> #include <stdio.h> +#include <float.h> + #include "MEM_guardedalloc.h" //XXX #include "nla.h" @@ -354,6 +356,117 @@ void bone_flip_name (char *name, int strip_number) sprintf (name, "%s%s%s%s", prefix, replace, suffix, number); } +/* Finds the best possible extension to the name on a particular axis. (For renaming, check for unique names afterwards) + * This assumes that bone names are at most 32 chars long! + * strip_number: removes number extensions (TODO: not used) + * axis: the axis to name on + * head/tail: the head/tail co-ordinate of the bone on the specified axis + */ +void bone_autoside_name (char *name, int strip_number, short axis, float head, float tail) +{ + int len; + char basename[32]={""}; + char extension[5]={""}; + + len= strlen(name); + if (len == 0) return; + strcpy(basename, name); + + /* Figure out extension to append: + * - The extension to append is based upon the axis that we are working on. + * - If head happens to be on 0, then we must consider the tail position as well to decide + * which side the bone is on + * -> If tail is 0, then it's bone is considered to be on axis, so no extension should be added + * -> Otherwise, extension is added from perspective of object based on which side tail goes to + * - If head is non-zero, extension is added from perspective of object based on side head is on + */ + if (axis == 2) { + /* z-axis - vertical (top/bottom) */ + if (IS_EQ(head, 0)) { + if (tail < 0) + strcpy(extension, "Bot"); + else if (tail > 0) + strcpy(extension, "Top"); + } + else { + if (head < 0) + strcpy(extension, "Bot"); + else + strcpy(extension, "Top"); + } + } + else if (axis == 1) { + /* y-axis - depth (front/back) */ + if (IS_EQ(head, 0)) { + if (tail < 0) + strcpy(extension, "Fr"); + else if (tail > 0) + strcpy(extension, "Bk"); + } + else { + if (head < 0) + strcpy(extension, "Fr"); + else + strcpy(extension, "Bk"); + } + } + else { + /* x-axis - horizontal (left/right) */ + if (IS_EQ(head, 0)) { + if (tail < 0) + strcpy(extension, "R"); + else if (tail > 0) + strcpy(extension, "L"); + } + else { + if (head < 0) + strcpy(extension, "R"); + else if (head > 0) + strcpy(extension, "L"); + } + } + + /* Simple name truncation + * - truncate if there is an extension and it wouldn't be able to fit + * - otherwise, just append to end + */ + if (extension[0]) { + int change = 1; + + while (change) { /* remove extensions */ + change = 0; + if (len > 2 && basename[len-2]=='.') { + if (basename[len-1]=='L' || basename[len-1] == 'R' ) { /* L R */ + basename[len-2] = '\0'; + len-=2; + change= 1; + } + } else if (len > 3 && basename[len-3]=='.') { + if ( (basename[len-2]=='F' && basename[len-1] == 'r') || /* Fr */ + (basename[len-2]=='B' && basename[len-1] == 'k') /* Bk */ + ) { + basename[len-3] = '\0'; + len-=3; + change= 1; + } + } else if (len > 4 && basename[len-4]=='.') { + if ( (basename[len-3]=='T' && basename[len-2]=='o' && basename[len-1] == 'p') || /* Top */ + (basename[len-3]=='B' && basename[len-2]=='o' && basename[len-1] == 't') /* Bot */ + ) { + basename[len-4] = '\0'; + len-=4; + change= 1; + } + } + } + + if ((32 - len) < strlen(extension) + 1) { /* add 1 for the '.' */ + strncpy(name, basename, len-strlen(extension)); + } + } + + sprintf(name, "%s.%s", basename, extension); +} /* ************* B-Bone support ******************* */ @@ -1305,18 +1418,27 @@ static void pose_proxy_synchronize(Object *ob, Object *from, int layer_protected bPoseChannel *pchan, *pchanp, pchanw; bConstraint *con; - if(frompose==NULL) return; + if (frompose==NULL) return; /* exception, armature local layer should be proxied too */ - if(pose->proxy_layer) + if (pose->proxy_layer) ((bArmature *)ob->data)->layer= pose->proxy_layer; /* clear all transformation values from library */ rest_pose(frompose); - pchan= pose->chanbase.first; - for(; pchan; pchan= pchan->next) { - if(pchan->bone->layer & layer_protected) { + /* copy over all of the proxy's bone groups */ + /* TODO for later - implement 'local' bone groups as for constraints + * Note: this isn't trivial, as bones reference groups by index not by pointer, + * so syncing things correctly needs careful attention + */ + BLI_freelistN(&pose->agroups); + BLI_duplicatelist(&pose->agroups, &frompose->agroups); + pose->active_group= frompose->active_group; + + for (pchan= pose->chanbase.first; pchan; pchan= pchan->next) { + if (pchan->bone->layer & layer_protected) { + ListBase proxylocal_constraints = {NULL, NULL}; pchanp= get_pose_channel(frompose, pchan->name); /* copy posechannel to temp, but restore important pointers */ @@ -1327,9 +1449,16 @@ static void pose_proxy_synchronize(Object *ob, Object *from, int layer_protected pchanw.child= pchan->child; pchanw.path= NULL; - /* constraints, set target ob pointer to own object */ + /* constraints - proxy constraints are flushed... local ones are added after + * 1. extract constraints not from proxy (CONSTRAINT_PROXY_LOCAL) from pchan's constraints + * 2. copy proxy-pchan's constraints on-to new + * 3. add extracted local constraints back on top + */ + extract_proxylocal_constraints(&proxylocal_constraints, &pchan->constraints); copy_constraints(&pchanw.constraints, &pchanp->constraints); + addlisttolist(&pchanw.constraints, &proxylocal_constraints); + /* constraints - set target ob pointer to own object */ for (con= pchanw.constraints.first; con; con= con->next) { bConstraintTypeInfo *cti= constraint_get_typeinfo(con); ListBase targets = {NULL, NULL}; @@ -1444,21 +1573,16 @@ static void initialize_posetree(struct Object *ob, bPoseChannel *pchan_tip) /* find IK constraint, and validate it */ for(con= pchan_tip->constraints.first; con; con= con->next) { - if(con->type==CONSTRAINT_TYPE_KINEMATIC) break; + if(con->type==CONSTRAINT_TYPE_KINEMATIC) { + data=(bKinematicConstraint*)con->data; + if (data->flag & CONSTRAINT_IK_AUTO) break; + if (data->tar==NULL) continue; + if (data->tar->type==OB_ARMATURE && data->subtarget[0]==0) continue; + if ((con->flag & CONSTRAINT_DISABLE)==0 && (con->enforce!=0.0)) break; + } } if(con==NULL) return; - data=(bKinematicConstraint*)con->data; - - /* two types of targets */ - if(data->flag & CONSTRAINT_IK_AUTO); - else { - if(con->flag & CONSTRAINT_DISABLE) return; /* checked in editconstraint.c */ - if(con->enforce == 0.0f) return; - if(data->tar==NULL) return; - if(data->tar->type==OB_ARMATURE && data->subtarget[0]==0) return; - } - /* exclude tip from chain? */ if(!(data->flag & CONSTRAINT_IK_TIP)) pchan_tip= pchan_tip->parent; @@ -1561,7 +1685,7 @@ static void initialize_posetree(struct Object *ob, bPoseChannel *pchan_tip) were executed & assigned. Now as last we do an IK pass */ static void execute_posetree(Object *ob, PoseTree *tree) { - float R_parmat[3][3]; + float R_parmat[3][3], identity[3][3]; float iR_parmat[3][3]; float R_bonemat[3][3]; float goalrot[3][3], goalpos[3]; @@ -1570,7 +1694,8 @@ static void execute_posetree(Object *ob, PoseTree *tree) float irest_basis[3][3], full_basis[3][3]; float end_pose[4][4], world_pose[4][4]; float length, basis[3][3], rest_basis[3][3], start[3], *ikstretch=NULL; - int a, flag, hasstretch=0; + float resultinf=0.0f; + int a, flag, hasstretch=0, resultblend=0; bPoseChannel *pchan; IK_Segment *seg, *parent, **iktree, *iktarget; IK_Solver *solver; @@ -1580,13 +1705,13 @@ static void execute_posetree(Object *ob, PoseTree *tree) if (tree->totchannel == 0) return; - + iktree= MEM_mallocN(sizeof(void*)*tree->totchannel, "ik tree"); for(a=0; a<tree->totchannel; a++) { pchan= tree->pchan[a]; bone= pchan->bone; - + /* set DoF flag */ flag= 0; if(!(pchan->ikflag & BONE_IK_NO_XDOF) && !(pchan->ikflag & BONE_IK_NO_XDOF_TEMP)) @@ -1595,32 +1720,32 @@ static void execute_posetree(Object *ob, PoseTree *tree) flag |= IK_YDOF; if(!(pchan->ikflag & BONE_IK_NO_ZDOF) && !(pchan->ikflag & BONE_IK_NO_ZDOF_TEMP)) flag |= IK_ZDOF; - + if(tree->stretch && (pchan->ikstretch > 0.0)) { flag |= IK_TRANS_YDOF; hasstretch = 1; } - + seg= iktree[a]= IK_CreateSegment(flag); - + /* find parent */ if(a == 0) parent= NULL; else parent= iktree[tree->parent[a]]; - + IK_SetParent(seg, parent); - + /* get the matrix that transforms from prevbone into this bone */ Mat3CpyMat4(R_bonemat, pchan->pose_mat); - + /* gather transformations for this IK segment */ - + if (pchan->parent) Mat3CpyMat4(R_parmat, pchan->parent->pose_mat); else Mat3One(R_parmat); - + /* bone offset */ if (pchan->parent && (a > 0)) VecSubf(start, pchan->pose_head, pchan->parent->pose_tail); @@ -1630,40 +1755,40 @@ static void execute_posetree(Object *ob, PoseTree *tree) /* change length based on bone size */ length= bone->length*VecLength(R_bonemat[1]); - + /* compute rest basis and its inverse */ Mat3CpyMat3(rest_basis, bone->bone_mat); Mat3CpyMat3(irest_basis, bone->bone_mat); Mat3Transp(irest_basis); - + /* compute basis with rest_basis removed */ Mat3Inv(iR_parmat, R_parmat); Mat3MulMat3(full_basis, iR_parmat, R_bonemat); Mat3MulMat3(basis, irest_basis, full_basis); - + /* basis must be pure rotation */ Mat3Ortho(basis); - + /* transform offset into local bone space */ Mat3Ortho(iR_parmat); Mat3MulVecfl(iR_parmat, start); - + IK_SetTransform(seg, start, rest_basis, basis, length); - + if (pchan->ikflag & BONE_IK_XLIMIT) IK_SetLimit(seg, IK_X, pchan->limitmin[0], pchan->limitmax[0]); if (pchan->ikflag & BONE_IK_YLIMIT) IK_SetLimit(seg, IK_Y, pchan->limitmin[1], pchan->limitmax[1]); if (pchan->ikflag & BONE_IK_ZLIMIT) IK_SetLimit(seg, IK_Z, pchan->limitmin[2], pchan->limitmax[2]); - + IK_SetStiffness(seg, IK_X, pchan->stiffness[0]); IK_SetStiffness(seg, IK_Y, pchan->stiffness[1]); IK_SetStiffness(seg, IK_Z, pchan->stiffness[2]); - + if(tree->stretch && (pchan->ikstretch > 0.0)) { float ikstretch = pchan->ikstretch*pchan->ikstretch; - IK_SetStiffness(seg, IK_TRANS_Y, MIN2(1.0-ikstretch, 0.999)); + IK_SetStiffness(seg, IK_TRANS_Y, MIN2(1.0-ikstretch, 0.99)); IK_SetLimit(seg, IK_TRANS_Y, 0.001, 1e10); } } @@ -1689,7 +1814,7 @@ static void execute_posetree(Object *ob, PoseTree *tree) for (target=tree->targets.first; target; target=target->next) { float polepos[3]; int poleconstrain= 0; - + data= (bKinematicConstraint*)target->con->data; /* 1.0=ctime, we pass on object for auto-ik (owner-type here is object, even though @@ -1706,7 +1831,7 @@ static void execute_posetree(Object *ob, PoseTree *tree) /* same for pole vector target */ if(data->poletar) { get_constraint_target_matrix(target->con, 1, CONSTRAINT_OBTYPE_OBJECT, ob, rootmat, 1.0); - + if(data->flag & CONSTRAINT_IK_SETANGLE) { /* don't solve IK when we are setting the pole angle */ break; @@ -1716,6 +1841,12 @@ static void execute_posetree(Object *ob, PoseTree *tree) VECCOPY(polepos, goal[3]); poleconstrain= 1; + /* for pole targets, we blend the result of the ik solver + * instead of the target position, otherwise we can't get + * a smooth transition */ + resultblend= 1; + resultinf= target->con->enforce; + if(data->flag & CONSTRAINT_IK_GETANGLE) { poleangledata= data; data->flag &= ~CONSTRAINT_IK_GETANGLE; @@ -1724,7 +1855,7 @@ static void execute_posetree(Object *ob, PoseTree *tree) } /* do we need blending? */ - if (target->con->enforce!=1.0) { + if (!resultblend && target->con->enforce!=1.0) { float q1[4], q2[4], q[4]; float fac= target->con->enforce; float mfac= 1.0-fac; @@ -1774,36 +1905,41 @@ static void execute_posetree(Object *ob, PoseTree *tree) tree->basis_change= MEM_mallocN(sizeof(float[3][3])*tree->totchannel, "ik basis change"); if(hasstretch) ikstretch= MEM_mallocN(sizeof(float)*tree->totchannel, "ik stretch"); - + for(a=0; a<tree->totchannel; a++) { IK_GetBasisChange(iktree[a], tree->basis_change[a]); - + if(hasstretch) { /* have to compensate for scaling received from parent */ float parentstretch, stretch; - + pchan= tree->pchan[a]; parentstretch= (tree->parent[a] >= 0)? ikstretch[tree->parent[a]]: 1.0; - + if(tree->stretch && (pchan->ikstretch > 0.0)) { float trans[3], length; - + IK_GetTranslationChange(iktree[a], trans); length= pchan->bone->length*VecLength(pchan->pose_mat[1]); - + ikstretch[a]= (length == 0.0)? 1.0: (trans[1]+length)/length; } else ikstretch[a] = 1.0; - + stretch= (parentstretch == 0.0)? 1.0: ikstretch[a]/parentstretch; - + VecMulf(tree->basis_change[a][0], stretch); VecMulf(tree->basis_change[a][1], stretch); VecMulf(tree->basis_change[a][2], stretch); - } + if(resultblend && resultinf!=1.0f) { + Mat3One(identity); + Mat3BlendMat3(tree->basis_change[a], identity, + tree->basis_change[a], resultinf); + } + IK_FreeSegment(iktree[a]); } @@ -2072,8 +2208,10 @@ static void where_is_pose_bone(Object *ob, bPoseChannel *pchan, float ctime) } else { Mat4MulMat4(pchan->pose_mat, pchan->chan_mat, bone->arm_mat); - /* only rootbones get the cyclic offset */ - VecAddf(pchan->pose_mat[3], pchan->pose_mat[3], ob->pose->cyclic_offset); + + /* only rootbones get the cyclic offset (unless user doesn't want that) */ + if ((bone->flag & BONE_NO_CYCLICOFFSET) == 0) + VecAddf(pchan->pose_mat[3], pchan->pose_mat[3], ob->pose->cyclic_offset); } /* do NLA strip modifiers - i.e. curve follow */ diff --git a/source/blender/blenkernel/intern/blender.c b/source/blender/blenkernel/intern/blender.c index 109fe4b2d74..b63d63c925c 100644 --- a/source/blender/blenkernel/intern/blender.c +++ b/source/blender/blenkernel/intern/blender.c @@ -30,11 +30,15 @@ * ***** END GPL LICENSE BLOCK ***** */ -#ifndef WIN32 - #include <unistd.h> // for read close - #include <sys/param.h> // for MAXPATHLEN +#ifndef _WIN32 + #include <unistd.h> // for read close + #include <sys/param.h> // for MAXPATHLEN #else - #include <io.h> // for open close read + #include <io.h> // for open close read + #define open _open + #define read _read + #define close _close + #define write _write #endif #include <stdlib.h> @@ -61,6 +65,7 @@ #include "IMB_imbuf_types.h" #include "IMB_imbuf.h" +#include "BKE_action.h" #include "BKE_blender.h" #include "BKE_curve.h" #include "BKE_depsgraph.h" @@ -282,7 +287,6 @@ static void clean_paths(Main *main) } BLI_clean(scene->r.backbuf); BLI_clean(scene->r.pic); - BLI_clean(scene->r.ftype); scene= scene->id.next; } @@ -313,6 +317,7 @@ static void setup_app_data(bContext *C, BlendFileData *bfd, char *filename) extern void lib_link_screen_restore(Main *, Scene *); SWAP(ListBase, G.main->screen, bfd->main->screen); + SWAP(ListBase, G.main->script, bfd->main->script); /* we re-use current screen */ curscreen= C->screen; @@ -366,7 +371,8 @@ static void setup_app_data(bContext *C, BlendFileData *bfd, char *filename) /* special cases, override loaded flags: */ if (G.f & G_DEBUG) bfd->globalf |= G_DEBUG; else bfd->globalf &= ~G_DEBUG; - if (!(G.f & G_DOSCRIPTLINKS)) bfd->globalf &= ~G_DOSCRIPTLINKS; + + if ((U.flag & USER_DONT_DOSCRIPTLINKS)) bfd->globalf &= ~G_DOSCRIPTLINKS; G.f= bfd->globalf; @@ -377,6 +383,9 @@ static void setup_app_data(bContext *C, BlendFileData *bfd, char *filename) /* baseflags, groups, make depsgraph, etc */ set_scene_bg(C->scene); + /* clear BONE_UNKEYED flags, these are not valid anymore for proxies */ + framechange_poses_clear_unkeyed(); + /* last stage of do_versions actually, that sets recalc flags for recalc poses */ for(ob= G.main->object.first; ob; ob= ob->id.next) { if(ob->type==OB_ARMATURE) @@ -393,7 +402,7 @@ static void setup_app_data(bContext *C, BlendFileData *bfd, char *filename) if (G.sce != filename) /* these are the same at times, should never copy to the same location */ strcpy(G.sce, filename); - strcpy(G.main->name, filename); /* is guaranteed current file */ + BLI_strncpy(G.main->name, filename, FILE_MAX); /* is guaranteed current file */ MEM_freeN(bfd); } @@ -445,8 +454,8 @@ int BKE_read_file_from_memory(bContext *C, char* filebuf, int filelength, void * BlendFileData *bfd; bfd= BLO_read_from_memory(filebuf, filelength, &bre); - if (bfd) { - setup_app_data(C, bfd, "<memory>"); + if (bfd) { + setup_app_data(C, bfd, "<memory2>"); } else { // XXX error("Loading failed: %s", BLO_bre_as_string(bre)); } @@ -462,7 +471,7 @@ int BKE_read_file_from_memfile(bContext *C, MemFile *memfile) bfd= BLO_read_from_memfile(G.sce, memfile, &bre); if (bfd) { - setup_app_data(C, bfd, "<memory>"); + setup_app_data(C, bfd, "<memory1>"); } else { // XXX error("Loading failed: %s", BLO_bre_as_string(bre)); } @@ -481,6 +490,7 @@ typedef struct UndoElem { char str[FILE_MAXDIR+FILE_MAXFILE]; char name[MAXUNDONAME]; MemFile memfile; + uintptr_t undosize; } UndoElem; static ListBase undobase={NULL, NULL}; @@ -511,6 +521,7 @@ static int read_undosave(bContext *C, UndoElem *uel) /* name can be a dynamic string */ void BKE_write_undo(bContext *C, char *name) { + uintptr_t maxmem, totmem, memused; int nr, success; UndoElem *uel; @@ -560,7 +571,7 @@ void BKE_write_undo(bContext *C, char *name) counter= counter % U.undosteps; sprintf(numstr, "%d.blend", counter); - BLI_make_file_string("/", tstr, U.tempdir, numstr); + BLI_make_file_string("/", tstr, btempdir, numstr); success= BLO_write_file(C, tstr, G.fileflags, &err); @@ -572,12 +583,41 @@ void BKE_write_undo(bContext *C, char *name) if(curundo->prev) prevfile= &(curundo->prev->memfile); + memused= MEM_get_memory_in_use(); success= BLO_write_file_mem(C, prevfile, &curundo->memfile, G.fileflags, &err); - + curundo->undosize= MEM_get_memory_in_use() - memused; + } + + if(U.undomemory != 0) { + /* limit to maximum memory (afterwards, we can't know in advance) */ + totmem= 0; + maxmem= ((uintptr_t)U.undomemory)*1024*1024; + + /* keep at least two (original + other) */ + uel= undobase.last; + while(uel && uel->prev) { + totmem+= uel->undosize; + if(totmem>maxmem) break; + uel= uel->prev; + } + + if(uel) { + if(uel->prev && uel->prev->prev) + uel= uel->prev; + + while(undobase.first!=uel) { + UndoElem *first= undobase.first; + BLI_remlink(&undobase, first); + /* the merge is because of compression */ + BLO_merge_memfile(&first->memfile, &first->next->memfile); + MEM_freeN(first); + } + } } } -/* 1= an undo, -1 is a redo. we have to make sure 'curundo' remains at current situation */ +/* 1= an undo, -1 is a redo. we have to make sure 'curundo' remains at current situation + * Note, ALWAYS call sound_initialize_sounds after BKE_undo_step() */ void BKE_undo_step(bContext *C, int step) { @@ -638,14 +678,14 @@ char *BKE_undo_menu_string(void) UndoElem *uel; DynStr *ds= BLI_dynstr_new(); char *menu; - + BLI_dynstr_append(ds, "Global Undo History %t"); for(uel= undobase.first; uel; uel= uel->next) { BLI_dynstr_append(ds, "|"); BLI_dynstr_append(ds, uel->name); } - + menu= BLI_dynstr_get_cstring(ds); BLI_dynstr_free(ds); @@ -671,11 +711,11 @@ void BKE_undo_save_quit(void) /* no undo state to save */ if(undobase.first==undobase.last) return; - BLI_make_file_string("/", str, U.tempdir, "quit.blend"); + BLI_make_file_string("/", str, btempdir, "quit.blend"); file = open(str,O_BINARY+O_WRONLY+O_CREAT+O_TRUNC, 0666); if(file == -1) { - printf("Unable to save %s\n", str); + //XXX error("Unable to save %s, check you have permissions", str); return; } @@ -687,7 +727,7 @@ void BKE_undo_save_quit(void) close(file); - if(chunk) printf("Unable to save %s\n", str); + if(chunk) ; //XXX error("Unable to save %s, internal error", str); else printf("Saved session recovery to %s\n", str); } diff --git a/source/blender/blenkernel/intern/bmesh_private.h b/source/blender/blenkernel/intern/bmesh_private.h new file mode 100644 index 00000000000..f34ef0090f3 --- /dev/null +++ b/source/blender/blenkernel/intern/bmesh_private.h @@ -0,0 +1,70 @@ +/** + * BME_private.h jan 2007 + * + * low level, 'private' function prototypes for bmesh kernel. + * + * $Id: BKE_bmesh.h,v 1.00 2007/01/17 17:42:01 Briggs Exp $ + * + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. The Blender + * Foundation also sells licenses for use in proprietary software under + * the Blender License. See http://www.blender.org/BL/ for information + * about this. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * The Original Code is Copyright (C) 2004 Blender Foundation. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): Geoffrey Bantle. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#ifndef BMESH_PRIVATE +#define BMESH_PRIVATE + +#include "BKE_bmesh.h" + +/*ALLOCATION/DEALLOCATION*/ +struct BME_Vert *BME_addvertlist(struct BME_Mesh *bm, struct BME_Vert *example); +struct BME_Edge *BME_addedgelist(struct BME_Mesh *bm, struct BME_Vert *v1, struct BME_Vert *v2, struct BME_Edge *example); +struct BME_Poly *BME_addpolylist(struct BME_Mesh *bm, struct BME_Poly *example); +struct BME_Loop *BME_create_loop(struct BME_Mesh *bm, struct BME_Vert *v, struct BME_Edge *e, struct BME_Poly *f, struct BME_Loop *example); + +void BME_free_vert(struct BME_Mesh *bm, struct BME_Vert *v); +void BME_free_edge(struct BME_Mesh *bm, struct BME_Edge *e); +void BME_free_poly(struct BME_Mesh *bm, struct BME_Poly *f); +void BME_free_loop(struct BME_Mesh *bm, struct BME_Loop *l); + +/*DOUBLE CIRCULAR LINKED LIST FUNCTIONS*/ +void BME_cycle_append(void *h, void *nt); +int BME_cycle_remove(void *h, void *remn); +int BME_cycle_validate(int len, void *h); +/*DISK CYCLE MANAGMENT*/ +int BME_disk_append_edge(struct BME_Edge *e, struct BME_Vert *v); +void BME_disk_remove_edge(struct BME_Edge *e, struct BME_Vert *v); +/*RADIAL CYCLE MANAGMENT*/ +void BME_radial_append(struct BME_Edge *e, struct BME_Loop *l); +void BME_radial_remove_loop(struct BME_Loop *l, struct BME_Edge *e); + +/*MISC FUNCTIONS*/ +int BME_edge_swapverts(struct BME_Edge *e, struct BME_Vert *orig, struct BME_Vert *new); /*relink edge*/ +int BME_disk_hasedge(struct BME_Vert *v, struct BME_Edge *e); + +/*Error reporting. Shouldnt be called by tools ever.*/ +void BME_error(void); +#endif diff --git a/source/blender/blenkernel/intern/brush.c b/source/blender/blenkernel/intern/brush.c index f1b12869c0f..1c53af97dbb 100644 --- a/source/blender/blenkernel/intern/brush.c +++ b/source/blender/blenkernel/intern/brush.c @@ -70,7 +70,8 @@ Brush *add_brush(char *name) brush->clone.alpha= 0.5; /* enable fake user by default */ - brush_toggle_fake_user(brush); + brush->id.flag |= LIB_FAKEUSER; + brush_toggled_fake_user(brush); return brush; } @@ -92,8 +93,10 @@ Brush *copy_brush(Brush *brush) } /* enable fake user by default */ - if (!(brushn->id.flag & LIB_FAKEUSER)) - brush_toggle_fake_user(brushn); + if (!(brushn->id.flag & LIB_FAKEUSER)) { + brushn->id.flag |= LIB_FAKEUSER; + brush_toggled_fake_user(brushn); + } return brushn; } @@ -145,8 +148,10 @@ void make_local_brush(Brush *brush) new_id(0, (ID *)brush, 0); /* enable fake user by default */ - if (!(brush->id.flag & LIB_FAKEUSER)) - brush_toggle_fake_user(brush); + if (!(brush->id.flag & LIB_FAKEUSER)) { + brush->id.flag |= LIB_FAKEUSER; + brush_toggled_fake_user(brush); + } } else if(local && lib) { brushn= copy_brush(brush); @@ -200,16 +205,14 @@ int brush_delete(Brush **current_brush) return 0; } -void brush_toggle_fake_user(Brush *brush) +void brush_toggled_fake_user(Brush *brush) { ID *id= (ID*)brush; if(id) { if(id->flag & LIB_FAKEUSER) { - id->flag -= LIB_FAKEUSER; - id->us--; - } else { - id->flag |= LIB_FAKEUSER; id_us_plus(id); + } else { + id->us--; } } } @@ -356,7 +359,6 @@ void brush_sample_tex(Brush *brush, float *xy, float *rgba) rgba[0]= rgba[1]= rgba[2]= rgba[3]= 1.0f; } -#define FTOCHAR(val) val<=0.0f?0: (val>=1.0f?255: (char)(255.0f*val)) void brush_imbuf_new(Brush *brush, short flt, short texfall, int size, ImBuf **outbuf) { diff --git a/source/blender/blenkernel/intern/bullet.c b/source/blender/blenkernel/intern/bullet.c new file mode 100644 index 00000000000..b389f8c0536 --- /dev/null +++ b/source/blender/blenkernel/intern/bullet.c @@ -0,0 +1,96 @@ +/* + * + * $Id$ + * + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * The Original Code is Copyright (C) Blender Foundation + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): none yet. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#include "MEM_guardedalloc.h" + +/* types */ +#include "DNA_object_force.h" /* here is the softbody struct */ + +#include "BKE_bullet.h" + + +/* ************ Object level, exported functions *************** */ + +/* allocates and initializes general main data */ +BulletSoftBody *bsbNew(void) +{ + BulletSoftBody *bsb; + + bsb= MEM_callocN(sizeof(BulletSoftBody), "bulletsoftbody"); + + bsb->flag = OB_BSB_BENDING_CONSTRAINTS | OB_BSB_SHAPE_MATCHING | OB_BSB_AERO_VPOINT; + bsb->linStiff = 0.5f; + bsb->angStiff = 1.0f; + bsb->volume = 1.0f; + + + bsb->viterations = 0; + bsb->piterations = 2; + bsb->diterations = 0; + bsb->citerations = 4; + + bsb->kSRHR_CL = 0.1f; + bsb->kSKHR_CL = 1.f; + bsb->kSSHR_CL = 0.5f; + bsb->kSR_SPLT_CL = 0.5f; + + bsb->kSK_SPLT_CL = 0.5f; + bsb->kSS_SPLT_CL = 0.5f; + bsb->kVCF = 1; + bsb->kDP = 0; + + bsb->kDG = 0; + bsb->kLF = 0; + bsb->kPR = 0; + bsb->kVC = 0; + + bsb->kDF = 0.2f; + bsb->kMT = 0.05; + bsb->kCHR = 1.0f; + bsb->kKHR = 0.1f; + + bsb->kSHR = 1.0f; + bsb->kAHR = 0.7f; + + bsb->collisionflags = 0; + //bsb->collisionflags = OB_BSB_COL_CL_RS + OB_BSB_COL_CL_SS; + bsb->numclusteriterations = 64; + + return bsb; +} + +/* frees all */ +void bsbFree(BulletSoftBody *bsb) +{ + /* no internal data yet */ + MEM_freeN(bsb); +} + + diff --git a/source/blender/blenkernel/intern/bvhutils.c b/source/blender/blenkernel/intern/bvhutils.c new file mode 100644 index 00000000000..ae449843d2a --- /dev/null +++ b/source/blender/blenkernel/intern/bvhutils.c @@ -0,0 +1,577 @@ +/** + * + * $Id$ + * + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * The Original Code is Copyright (C) Blender Foundation. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): André Pinto. + * + * ***** END GPL LICENSE BLOCK ***** + */ +#include <stdio.h> +#include <string.h> +#include <math.h> + +#include "BKE_bvhutils.h" + +#include "DNA_object_types.h" +#include "DNA_modifier_types.h" +#include "DNA_meshdata_types.h" + +#include "BKE_DerivedMesh.h" +#include "BKE_utildefines.h" +#include "BKE_deform.h" +#include "BKE_cdderivedmesh.h" +#include "BKE_displist.h" +#include "BKE_global.h" + +#include "BLI_arithb.h" + +/* Math stuff for ray casting on mesh faces and for nearest surface */ + +static float ray_tri_intersection(const BVHTreeRay *ray, const float m_dist, const float *v0, const float *v1, const float *v2) +{ + float dist; + + if(RayIntersectsTriangle((float*)ray->origin, (float*)ray->direction, (float*)v0, (float*)v1, (float*)v2, &dist, NULL)) + return dist; + + return FLT_MAX; +} + +static float sphereray_tri_intersection(const BVHTreeRay *ray, float radius, const float m_dist, const float *v0, const float *v1, const float *v2) +{ + + float idist; + float p1[3]; + float plane_normal[3], hit_point[3]; + + CalcNormFloat((float*)v0, (float*)v1, (float*)v2, plane_normal); + + VECADDFAC( p1, ray->origin, ray->direction, m_dist); + if(SweepingSphereIntersectsTriangleUV((float*)ray->origin, p1, radius, (float*)v0, (float*)v1, (float*)v2, &idist, hit_point)) + { + return idist * m_dist; + } + + return FLT_MAX; +} + + +/* + * Function adapted from David Eberly's distance tools (LGPL) + * http://www.geometrictools.com/LibFoundation/Distance/Distance.html + */ +static float nearest_point_in_tri_surface(const float *v0,const float *v1,const float *v2,const float *p, int *v, int *e, float *nearest ) +{ + float diff[3]; + float e0[3]; + float e1[3]; + float A00; + float A01; + float A11; + float B0; + float B1; + float C; + float Det; + float S; + float T; + float sqrDist; + int lv = -1, le = -1; + + VECSUB(diff, v0, p); + VECSUB(e0, v1, v0); + VECSUB(e1, v2, v0); + + A00 = INPR ( e0, e0 ); + A01 = INPR( e0, e1 ); + A11 = INPR ( e1, e1 ); + B0 = INPR( diff, e0 ); + B1 = INPR( diff, e1 ); + C = INPR( diff, diff ); + Det = fabs( A00 * A11 - A01 * A01 ); + S = A01 * B1 - A11 * B0; + T = A01 * B0 - A00 * B1; + + if ( S + T <= Det ) + { + if ( S < 0.0f ) + { + if ( T < 0.0f ) // Region 4 + { + if ( B0 < 0.0f ) + { + T = 0.0f; + if ( -B0 >= A00 ) + { + S = (float)1.0; + sqrDist = A00 + 2.0f * B0 + C; + lv = 1; + } + else + { + if(fabs(A00) > FLT_EPSILON) + S = -B0/A00; + else + S = 0.0f; + sqrDist = B0 * S + C; + le = 0; + } + } + else + { + S = 0.0f; + if ( B1 >= 0.0f ) + { + T = 0.0f; + sqrDist = C; + lv = 0; + } + else if ( -B1 >= A11 ) + { + T = 1.0f; + sqrDist = A11 + 2.0f * B1 + C; + lv = 2; + } + else + { + if(fabs(A11) > FLT_EPSILON) + T = -B1 / A11; + else + T = 0.0f; + sqrDist = B1 * T + C; + le = 1; + } + } + } + else // Region 3 + { + S = 0.0f; + if ( B1 >= 0.0f ) + { + T = 0.0f; + sqrDist = C; + lv = 0; + } + else if ( -B1 >= A11 ) + { + T = 1.0f; + sqrDist = A11 + 2.0f * B1 + C; + lv = 2; + } + else + { + if(fabs(A11) > FLT_EPSILON) + T = -B1 / A11; + else + T = 0.0; + sqrDist = B1 * T + C; + le = 1; + } + } + } + else if ( T < 0.0f ) // Region 5 + { + T = 0.0f; + if ( B0 >= 0.0f ) + { + S = 0.0f; + sqrDist = C; + lv = 0; + } + else if ( -B0 >= A00 ) + { + S = 1.0f; + sqrDist = A00 + 2.0f * B0 + C; + lv = 1; + } + else + { + if(fabs(A00) > FLT_EPSILON) + S = -B0 / A00; + else + S = 0.0f; + sqrDist = B0 * S + C; + le = 0; + } + } + else // Region 0 + { + // Minimum at interior lv + float invDet; + if(fabs(Det) > FLT_EPSILON) + invDet = 1.0f / Det; + else + invDet = 0.0f; + S *= invDet; + T *= invDet; + sqrDist = S * ( A00 * S + A01 * T + 2.0f * B0) + + T * ( A01 * S + A11 * T + 2.0f * B1 ) + C; + } + } + else + { + float tmp0, tmp1, numer, denom; + + if ( S < 0.0f ) // Region 2 + { + tmp0 = A01 + B0; + tmp1 = A11 + B1; + if ( tmp1 > tmp0 ) + { + numer = tmp1 - tmp0; + denom = A00 - 2.0f * A01 + A11; + if ( numer >= denom ) + { + S = 1.0f; + T = 0.0f; + sqrDist = A00 + 2.0f * B0 + C; + lv = 1; + } + else + { + if(fabs(denom) > FLT_EPSILON) + S = numer / denom; + else + S = 0.0f; + T = 1.0f - S; + sqrDist = S * ( A00 * S + A01 * T + 2.0f * B0 ) + + T * ( A01 * S + A11 * T + 2.0f * B1 ) + C; + le = 2; + } + } + else + { + S = 0.0f; + if ( tmp1 <= 0.0f ) + { + T = 1.0f; + sqrDist = A11 + 2.0f * B1 + C; + lv = 2; + } + else if ( B1 >= 0.0f ) + { + T = 0.0f; + sqrDist = C; + lv = 0; + } + else + { + if(fabs(A11) > FLT_EPSILON) + T = -B1 / A11; + else + T = 0.0f; + sqrDist = B1 * T + C; + le = 1; + } + } + } + else if ( T < 0.0f ) // Region 6 + { + tmp0 = A01 + B1; + tmp1 = A00 + B0; + if ( tmp1 > tmp0 ) + { + numer = tmp1 - tmp0; + denom = A00 - 2.0f * A01 + A11; + if ( numer >= denom ) + { + T = 1.0f; + S = 0.0f; + sqrDist = A11 + 2.0f * B1 + C; + lv = 2; + } + else + { + if(fabs(denom) > FLT_EPSILON) + T = numer / denom; + else + T = 0.0f; + S = 1.0f - T; + sqrDist = S * ( A00 * S + A01 * T + 2.0f * B0 ) + + T * ( A01 * S + A11 * T + 2.0f * B1 ) + C; + le = 2; + } + } + else + { + T = 0.0f; + if ( tmp1 <= 0.0f ) + { + S = 1.0f; + sqrDist = A00 + 2.0f * B0 + C; + lv = 1; + } + else if ( B0 >= 0.0f ) + { + S = 0.0f; + sqrDist = C; + lv = 0; + } + else + { + if(fabs(A00) > FLT_EPSILON) + S = -B0 / A00; + else + S = 0.0f; + sqrDist = B0 * S + C; + le = 0; + } + } + } + else // Region 1 + { + numer = A11 + B1 - A01 - B0; + if ( numer <= 0.0f ) + { + S = 0.0f; + T = 1.0f; + sqrDist = A11 + 2.0f * B1 + C; + lv = 2; + } + else + { + denom = A00 - 2.0f * A01 + A11; + if ( numer >= denom ) + { + S = 1.0f; + T = 0.0f; + sqrDist = A00 + 2.0f * B0 + C; + lv = 1; + } + else + { + if(fabs(denom) > FLT_EPSILON) + S = numer / denom; + else + S = 0.0f; + T = 1.0f - S; + sqrDist = S * ( A00 * S + A01 * T + 2.0f * B0 ) + + T * ( A01 * S + A11 * T + 2.0f * B1 ) + C; + le = 2; + } + } + } + } + + // Account for numerical round-off error + if ( sqrDist < FLT_EPSILON ) + sqrDist = 0.0f; + + { + float w[3], x[3], y[3], z[3]; + VECCOPY(w, v0); + VECCOPY(x, e0); + VecMulf(x, S); + VECCOPY(y, e1); + VecMulf(y, T); + VECADD(z, w, x); + VECADD(z, z, y); + //VECSUB(d, p, z); + VECCOPY(nearest, z); + // d = p - ( v0 + S * e0 + T * e1 ); + } + *v = lv; + *e = le; + + return sqrDist; +} + + +/* + * BVH from meshs callbacks + */ + +// Callback to bvh tree nearest point. The tree must bust have been built using bvhtree_from_mesh_faces. +// userdata must be a BVHMeshCallbackUserdata built from the same mesh as the tree. +static void mesh_faces_nearest_point(void *userdata, int index, const float *co, BVHTreeNearest *nearest) +{ + const BVHTreeFromMesh *data = (BVHTreeFromMesh*) userdata; + MVert *vert = data->vert; + MFace *face = data->face + index; + + float *t0, *t1, *t2, *t3; + t0 = vert[ face->v1 ].co; + t1 = vert[ face->v2 ].co; + t2 = vert[ face->v3 ].co; + t3 = face->v4 ? vert[ face->v4].co : NULL; + + + do + { + float nearest_tmp[3], dist; + int vertex, edge; + + dist = nearest_point_in_tri_surface(t0, t1, t2, co, &vertex, &edge, nearest_tmp); + if(dist < nearest->dist) + { + nearest->index = index; + nearest->dist = dist; + VECCOPY(nearest->co, nearest_tmp); + CalcNormFloat(t0, t1, t2, nearest->no); + } + + t1 = t2; + t2 = t3; + t3 = NULL; + + } while(t2); +} + +// Callback to bvh tree raycast. The tree must bust have been built using bvhtree_from_mesh_faces. +// userdata must be a BVHMeshCallbackUserdata built from the same mesh as the tree. +static void mesh_faces_spherecast(void *userdata, int index, const BVHTreeRay *ray, BVHTreeRayHit *hit) +{ + const BVHTreeFromMesh *data = (BVHTreeFromMesh*) userdata; + MVert *vert = data->vert; + MFace *face = data->face + index; + + float *t0, *t1, *t2, *t3; + t0 = vert[ face->v1 ].co; + t1 = vert[ face->v2 ].co; + t2 = vert[ face->v3 ].co; + t3 = face->v4 ? vert[ face->v4].co : NULL; + + + do + { + float dist; + if(data->sphere_radius == 0.0f) + dist = ray_tri_intersection(ray, hit->dist, t0, t1, t2); + else + dist = sphereray_tri_intersection(ray, data->sphere_radius, hit->dist, t0, t1, t2); + + if(dist >= 0 && dist < hit->dist) + { + hit->index = index; + hit->dist = dist; + VECADDFAC(hit->co, ray->origin, ray->direction, dist); + + CalcNormFloat(t0, t1, t2, hit->no); + } + + t1 = t2; + t2 = t3; + t3 = NULL; + + } while(t2); +} + +/* + * BVH builders + */ +// Builds a bvh tree.. where nodes are the vertexs of the given mesh +void bvhtree_from_mesh_verts(BVHTreeFromMesh *data, DerivedMesh *mesh, float epsilon, int tree_type, int axis) +{ + int i; + int numVerts= mesh->getNumVerts(mesh); + MVert *vert = mesh->getVertDataArray(mesh, CD_MVERT); + BVHTree *tree = NULL; + + memset(data, 0, sizeof(*data)); + + if(vert == NULL) + { + printf("bvhtree cant be build: cant get a vertex array"); + return; + } + + tree = BLI_bvhtree_new(numVerts, epsilon, tree_type, axis); + if(tree != NULL) + { + for(i = 0; i < numVerts; i++) + BLI_bvhtree_insert(tree, i, vert[i].co, 1); + + BLI_bvhtree_balance(tree); + + data->tree = tree; + + //a NULL nearest callback works fine + //remeber the min distance to point is the same as the min distance to BV of point + data->nearest_callback = NULL; + data->raycast_callback = NULL; + + data->mesh = mesh; + data->vert = mesh->getVertDataArray(mesh, CD_MVERT); + data->face = mesh->getFaceDataArray(mesh, CD_MFACE); + + data->sphere_radius = epsilon; + } +} + +// Builds a bvh tree.. where nodes are the faces of the given mesh. +void bvhtree_from_mesh_faces(BVHTreeFromMesh *data, DerivedMesh *mesh, float epsilon, int tree_type, int axis) +{ + int i; + int numFaces= mesh->getNumFaces(mesh); + MVert *vert = mesh->getVertDataArray(mesh, CD_MVERT); + MFace *face = mesh->getFaceDataArray(mesh, CD_MFACE); + BVHTree *tree = NULL; + + memset(data, 0, sizeof(*data)); + + if(vert == NULL && face == NULL) + { + printf("bvhtree cant be build: cant get a vertex/face array"); + return; + } + + /* Create a bvh-tree of the given target */ + tree = BLI_bvhtree_new(numFaces, epsilon, tree_type, axis); + if(tree != NULL) + { + for(i = 0; i < numFaces; i++) + { + float co[4][3]; + VECCOPY(co[0], vert[ face[i].v1 ].co); + VECCOPY(co[1], vert[ face[i].v2 ].co); + VECCOPY(co[2], vert[ face[i].v3 ].co); + if(face[i].v4) + VECCOPY(co[3], vert[ face[i].v4 ].co); + + BLI_bvhtree_insert(tree, i, co[0], face[i].v4 ? 4 : 3); + } + BLI_bvhtree_balance(tree); + + data->tree = tree; + data->nearest_callback = mesh_faces_nearest_point; + data->raycast_callback = mesh_faces_spherecast; + + data->mesh = mesh; + data->vert = mesh->getVertDataArray(mesh, CD_MVERT); + data->face = mesh->getFaceDataArray(mesh, CD_MFACE); + + data->sphere_radius = epsilon; + } +} + +// Frees data allocated by a call to bvhtree_from_mesh_*. +void free_bvhtree_from_mesh(struct BVHTreeFromMesh *data) +{ + if(data->tree) + { + BLI_bvhtree_free(data->tree); + memset( data, 0, sizeof(data) ); + } +} + + diff --git a/source/blender/blenkernel/intern/cdderivedmesh.c b/source/blender/blenkernel/intern/cdderivedmesh.c index 6a856307916..2c1b5ced614 100644 --- a/source/blender/blenkernel/intern/cdderivedmesh.c +++ b/source/blender/blenkernel/intern/cdderivedmesh.c @@ -52,11 +52,16 @@ #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" +#include "DNA_object_fluidsim.h" #include "DNA_object_types.h" #include "DNA_scene_types.h" #include "MEM_guardedalloc.h" +#include "GPU_draw.h" +#include "GPU_extensions.h" +#include "GPU_material.h" + #include <string.h> #include <limits.h> @@ -116,7 +121,7 @@ static void cdDM_copyEdgeArray(DerivedMesh *dm, MEdge *edge_r) memcpy(edge_r, cddm->medge, sizeof(*edge_r) * dm->numEdgeData); } -void cdDM_copyFaceArray(DerivedMesh *dm, MFace *face_r) +static void cdDM_copyFaceArray(DerivedMesh *dm, MFace *face_r) { CDDerivedMesh *cddm = (CDDerivedMesh *)dm; memcpy(face_r, cddm->mface, sizeof(*face_r) * dm->numFaceData); @@ -242,7 +247,7 @@ static void cdDM_drawLooseEdges(DerivedMesh *dm) glEnd(); } -static void cdDM_drawFacesSolid(DerivedMesh *dm, int (*setMaterial)(int)) +static void cdDM_drawFacesSolid(DerivedMesh *dm, int (*setMaterial)(int, void *attribs)) { CDDerivedMesh *cddm = (CDDerivedMesh*) dm; MVert *mvert = cddm->mvert; @@ -270,7 +275,7 @@ static void cdDM_drawFacesSolid(DerivedMesh *dm, int (*setMaterial)(int)) || new_shademodel != shademodel) { glEnd(); - drawCurrentMat = setMaterial(matnr = new_matnr); + drawCurrentMat = setMaterial(matnr = new_matnr, NULL); glShadeModel(shademodel = new_shademodel); glBegin(glmode = new_glmode); @@ -555,6 +560,134 @@ static void cdDM_drawMappedFacesTex(DerivedMesh *dm, int (*setDrawOptions)(void cdDM_drawFacesTex_common(dm, NULL, setDrawOptions, userData); } +static void cdDM_drawMappedFacesGLSL(DerivedMesh *dm, int (*setMaterial)(int, void *attribs), int (*setDrawOptions)(void *userData, int index), void *userData) +{ + CDDerivedMesh *cddm = (CDDerivedMesh*) dm; + GPUVertexAttribs gattribs; + DMVertexAttribs attribs; + MVert *mvert = cddm->mvert; + MFace *mface = cddm->mface; + MTFace *tf = dm->getFaceDataArray(dm, CD_MTFACE); + float (*nors)[3] = dm->getFaceDataArray(dm, CD_NORMAL); + int a, b, dodraw, smoothnormal, matnr, new_matnr; + int transp, new_transp, orig_transp; + int orig, *index = dm->getFaceDataArray(dm, CD_ORIGINDEX); + + matnr = -1; + smoothnormal = 0; + dodraw = 0; + transp = GPU_get_material_blend_mode(); + orig_transp = transp; + + memset(&attribs, 0, sizeof(attribs)); + + glShadeModel(GL_SMOOTH); + glBegin(GL_QUADS); + + for(a = 0; a < dm->numFaceData; a++, mface++) { + new_matnr = mface->mat_nr + 1; + + if(new_matnr != matnr) { + glEnd(); + + dodraw = setMaterial(matnr = new_matnr, &gattribs); + if(dodraw) + DM_vertex_attributes_from_gpu(dm, &gattribs, &attribs); + + glBegin(GL_QUADS); + } + + if(!dodraw) { + continue; + } + else if(setDrawOptions) { + orig = index[a]; + + if(orig == ORIGINDEX_NONE) + continue; + else if(!setDrawOptions(userData, orig)) + continue; + } + + if(tf) { + new_transp = tf[a].transp; + + if(new_transp != transp) { + glEnd(); + + if(new_transp == GPU_BLEND_SOLID && orig_transp != GPU_BLEND_SOLID) + GPU_set_material_blend_mode(orig_transp); + else + GPU_set_material_blend_mode(new_transp); + transp = new_transp; + + glBegin(GL_QUADS); + } + } + + smoothnormal = (mface->flag & ME_SMOOTH); + + if(!smoothnormal) { + if(nors) { + glNormal3fv(nors[a]); + } + else { + /* TODO ideally a normal layer should always be available */ + float nor[3]; + if(mface->v4) { + CalcNormFloat4(mvert[mface->v1].co, mvert[mface->v2].co, + mvert[mface->v3].co, mvert[mface->v4].co, + nor); + } else { + CalcNormFloat(mvert[mface->v1].co, mvert[mface->v2].co, + mvert[mface->v3].co, nor); + } + glNormal3fv(nor); + } + } + +#define PASSVERT(index, vert) { \ + if(attribs.totorco) \ + glVertexAttrib3fvARB(attribs.orco.glIndex, attribs.orco.array[index]); \ + for(b = 0; b < attribs.tottface; b++) { \ + MTFace *tf = &attribs.tface[b].array[a]; \ + glVertexAttrib2fvARB(attribs.tface[b].glIndex, tf->uv[vert]); \ + } \ + for(b = 0; b < attribs.totmcol; b++) { \ + MCol *cp = &attribs.mcol[b].array[a*4 + vert]; \ + GLubyte col[4]; \ + col[0]= cp->b; col[1]= cp->g; col[2]= cp->r; col[3]= cp->a; \ + glVertexAttrib4ubvARB(attribs.mcol[b].glIndex, col); \ + } \ + if(attribs.tottang) { \ + float *tang = attribs.tang.array[a*4 + vert]; \ + glVertexAttrib3fvARB(attribs.tang.glIndex, tang); \ + } \ + if(smoothnormal) \ + glNormal3sv(mvert[index].no); \ + glVertex3fv(mvert[index].co); \ +} + + PASSVERT(mface->v1, 0); + PASSVERT(mface->v2, 1); + PASSVERT(mface->v3, 2); + if(mface->v4) + PASSVERT(mface->v4, 3) + else + PASSVERT(mface->v3, 2) + +#undef PASSVERT + } + glEnd(); + + glShadeModel(GL_FLAT); +} + +static void cdDM_drawFacesGLSL(DerivedMesh *dm, int (*setMaterial)(int, void *attribs)) +{ + dm->drawMappedFacesGLSL(dm, setMaterial, NULL, NULL); +} + static void cdDM_drawMappedEdges(DerivedMesh *dm, int (*setDrawOptions)(void *userData, int index), void *userData) { CDDerivedMesh *cddm = (CDDerivedMesh*) dm; @@ -712,8 +845,10 @@ static CDDerivedMesh *cdDM_create(const char *desc) dm->drawFacesSolid = cdDM_drawFacesSolid; dm->drawFacesColored = cdDM_drawFacesColored; dm->drawFacesTex = cdDM_drawFacesTex; + dm->drawFacesGLSL = cdDM_drawFacesGLSL; dm->drawMappedFaces = cdDM_drawMappedFaces; dm->drawMappedFacesTex = cdDM_drawMappedFacesTex; + dm->drawMappedFacesGLSL = cdDM_drawMappedFacesGLSL; dm->foreachMappedVert = cdDM_foreachMappedVert; dm->foreachMappedEdge = cdDM_foreachMappedEdge; @@ -731,6 +866,10 @@ DerivedMesh *CDDM_new(int numVerts, int numEdges, int numFaces) DM_init(dm, numVerts, numEdges, numFaces); + CustomData_add_layer(&dm->vertData, CD_ORIGINDEX, CD_CALLOC, NULL, numVerts); + CustomData_add_layer(&dm->edgeData, CD_ORIGINDEX, CD_CALLOC, NULL, numEdges); + CustomData_add_layer(&dm->faceData, CD_ORIGINDEX, CD_CALLOC, NULL, numFaces); + CustomData_add_layer(&dm->vertData, CD_MVERT, CD_CALLOC, NULL, numVerts); CustomData_add_layer(&dm->edgeData, CD_MEDGE, CD_CALLOC, NULL, numEdges); CustomData_add_layer(&dm->faceData, CD_MFACE, CD_CALLOC, NULL, numFaces); @@ -746,18 +885,26 @@ DerivedMesh *CDDM_from_mesh(Mesh *mesh, Object *ob) { CDDerivedMesh *cddm = cdDM_create("CDDM_from_mesh dm"); DerivedMesh *dm = &cddm->dm; - int i, *index; + int i, *index, alloctype; - /* this does a referenced copy, the only new layers being ORIGINDEX */ + /* this does a referenced copy, the only new layers being ORIGINDEX, + * with an exception for fluidsim */ DM_init(dm, mesh->totvert, mesh->totedge, mesh->totface); + + CustomData_add_layer(&dm->vertData, CD_ORIGINDEX, CD_CALLOC, NULL, mesh->totvert); + CustomData_add_layer(&dm->edgeData, CD_ORIGINDEX, CD_CALLOC, NULL, mesh->totedge); + CustomData_add_layer(&dm->faceData, CD_ORIGINDEX, CD_CALLOC, NULL, mesh->totface); + dm->deformedOnly = 1; - CustomData_merge(&mesh->vdata, &dm->vertData, CD_MASK_MESH, CD_REFERENCE, + alloctype= CD_REFERENCE; + + CustomData_merge(&mesh->vdata, &dm->vertData, CD_MASK_MESH, alloctype, mesh->totvert); - CustomData_merge(&mesh->edata, &dm->edgeData, CD_MASK_MESH, CD_REFERENCE, + CustomData_merge(&mesh->edata, &dm->edgeData, CD_MASK_MESH, alloctype, mesh->totedge); - CustomData_merge(&mesh->fdata, &dm->faceData, CD_MASK_MESH, CD_REFERENCE, + CustomData_merge(&mesh->fdata, &dm->faceData, CD_MASK_MESH, alloctype, mesh->totface); cddm->mvert = CustomData_get_layer(&dm->vertData, CD_MVERT); @@ -833,6 +980,7 @@ DerivedMesh *CDDM_from_editmesh(EditMesh *em, Mesh *me) mv->no[0] = eve->no[0] * 32767.0; mv->no[1] = eve->no[1] * 32767.0; mv->no[2] = eve->no[2] * 32767.0; + mv->bweight = (unsigned char) (eve->bweight * 255.0f); mv->mat_nr = 0; mv->flag = 0; @@ -850,6 +998,7 @@ DerivedMesh *CDDM_from_editmesh(EditMesh *em, Mesh *me) med->v1 = eed->v1->tmp.l; med->v2 = eed->v2->tmp.l; med->crease = (unsigned char) (eed->crease * 255.0f); + med->bweight = (unsigned char) (eed->bweight * 255.0f); med->flag = ME_EDGEDRAW|ME_EDGERENDER; if(eed->seam) med->flag |= ME_SEAM; diff --git a/source/blender/blenkernel/intern/cloth.c b/source/blender/blenkernel/intern/cloth.c new file mode 100644 index 00000000000..dbc94571cad --- /dev/null +++ b/source/blender/blenkernel/intern/cloth.c @@ -0,0 +1,1290 @@ +/* cloth.c +* +* +* ***** BEGIN GPL LICENSE BLOCK ***** +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License +* as published by the Free Software Foundation; either version 2 +* of the License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +* +* The Original Code is Copyright (C) Blender Foundation +* All rights reserved. +* +* Contributor(s): Daniel Genrich +* +* ***** END GPL LICENSE BLOCK ***** +*/ + +#include "MEM_guardedalloc.h" + +#include "BKE_cloth.h" + +#include "DNA_cloth_types.h" +#include "DNA_mesh_types.h" +#include "DNA_object_force.h" +#include "DNA_scene_types.h" + +#include "BKE_deform.h" +#include "BKE_DerivedMesh.h" +#include "BKE_cdderivedmesh.h" +#include "BKE_effect.h" +#include "BKE_global.h" +#include "BKE_object.h" +#include "BKE_modifier.h" +#include "BKE_utildefines.h" + +#include "BKE_pointcache.h" + +#include "BLI_kdopbvh.h" + +#ifdef _WIN32 +void tstart ( void ) +{} +void tend ( void ) +{ +} +double tval() +{ + return 0; +} +#else +#include <sys/time.h> + static struct timeval _tstart, _tend; + static struct timezone tz; + void tstart ( void ) +{ + gettimeofday ( &_tstart, &tz ); +} +void tend ( void ) +{ + gettimeofday ( &_tend,&tz ); +} +double tval() +{ + double t1, t2; + t1 = ( double ) _tstart.tv_sec + ( double ) _tstart.tv_usec/ ( 1000*1000 ); + t2 = ( double ) _tend.tv_sec + ( double ) _tend.tv_usec/ ( 1000*1000 ); + return t2-t1; +} +#endif + +/* Our available solvers. */ +// 255 is the magic reserved number, so NEVER try to put 255 solvers in here! +// 254 = MAX! +static CM_SOLVER_DEF solvers [] = +{ + { "Implicit", CM_IMPLICIT, implicit_init, implicit_solver, implicit_free }, + // { "Implicit C++", CM_IMPLICITCPP, implicitcpp_init, implicitcpp_solver, implicitcpp_free }, +}; + +/* ********** cloth engine ******* */ +/* Prototypes for internal functions. +*/ +static void cloth_to_object (Object *ob, ClothModifierData *clmd, DerivedMesh *dm); +static void cloth_from_mesh ( Object *ob, ClothModifierData *clmd, DerivedMesh *dm ); +static int cloth_from_object(Object *ob, ClothModifierData *clmd, DerivedMesh *dm, float framenr, int first); +int cloth_build_springs ( ClothModifierData *clmd, DerivedMesh *dm ); +static void cloth_apply_vgroup ( ClothModifierData *clmd, DerivedMesh *dm ); + + +/****************************************************************************** +* +* External interface called by modifier.c clothModifier functions. +* +******************************************************************************/ +/** + * cloth_init - creates a new cloth simulation. + * + * 1. create object + * 2. fill object with standard values or with the GUI settings if given + */ +void cloth_init ( ClothModifierData *clmd ) +{ + /* Initialize our new data structure to reasonable values. */ + clmd->sim_parms->gravity [0] = 0.0; + clmd->sim_parms->gravity [1] = 0.0; + clmd->sim_parms->gravity [2] = -9.81; + clmd->sim_parms->structural = 15.0; + clmd->sim_parms->shear = 15.0; + clmd->sim_parms->bending = 0.5; + clmd->sim_parms->Cdis = 5.0; + clmd->sim_parms->Cvi = 1.0; + clmd->sim_parms->mass = 0.3f; + clmd->sim_parms->stepsPerFrame = 5; + clmd->sim_parms->flags = 0; + clmd->sim_parms->solver_type = 0; + clmd->sim_parms->preroll = 0; + clmd->sim_parms->maxspringlen = 10; + clmd->sim_parms->vgroup_mass = 0; + clmd->sim_parms->avg_spring_len = 0.0; + clmd->sim_parms->presets = 2; /* cotton as start setting */ + clmd->sim_parms->timescale = 1.0f; /* speed factor, describes how fast cloth moves */ + + clmd->coll_parms->self_friction = 5.0; + clmd->coll_parms->friction = 5.0; + clmd->coll_parms->loop_count = 2; + clmd->coll_parms->epsilon = 0.015f; + clmd->coll_parms->flags = CLOTH_COLLSETTINGS_FLAG_ENABLED; + clmd->coll_parms->collision_list = NULL; + clmd->coll_parms->self_loop_count = 1.0; + clmd->coll_parms->selfepsilon = 0.75; + + /* These defaults are copied from softbody.c's + * softbody_calc_forces() function. + */ + clmd->sim_parms->eff_force_scale = 1000.0; + clmd->sim_parms->eff_wind_scale = 250.0; + + // also from softbodies + clmd->sim_parms->maxgoal = 1.0f; + clmd->sim_parms->mingoal = 0.0f; + clmd->sim_parms->defgoal = 0.0f; + clmd->sim_parms->goalspring = 1.0f; + clmd->sim_parms->goalfrict = 0.0f; +} + +BVHTree *bvhselftree_build_from_cloth (ClothModifierData *clmd, float epsilon) +{ + unsigned int i; + BVHTree *bvhtree; + Cloth *cloth = clmd->clothObject; + ClothVertex *verts; + MFace *mfaces; + float co[12]; + + if(!clmd) + return NULL; + + cloth = clmd->clothObject; + + if(!cloth) + return NULL; + + verts = cloth->verts; + mfaces = cloth->mfaces; + + // in the moment, return zero if no faces there + if(!cloth->numverts) + return NULL; + + // create quadtree with k=26 + bvhtree = BLI_bvhtree_new(cloth->numverts, epsilon, 4, 6); + + // fill tree + for(i = 0; i < cloth->numverts; i++, verts++) + { + VECCOPY(&co[0*3], verts->xold); + + BLI_bvhtree_insert(bvhtree, i, co, 1); + } + + // balance tree + BLI_bvhtree_balance(bvhtree); + + return bvhtree; +} + +BVHTree *bvhtree_build_from_cloth (ClothModifierData *clmd, float epsilon) +{ + unsigned int i; + BVHTree *bvhtree; + Cloth *cloth = clmd->clothObject; + ClothVertex *verts; + MFace *mfaces; + float co[12]; + + if(!clmd) + return NULL; + + cloth = clmd->clothObject; + + if(!cloth) + return NULL; + + verts = cloth->verts; + mfaces = cloth->mfaces; + + // in the moment, return zero if no faces there + if(!cloth->numfaces) + return NULL; + + // create quadtree with k=26 + bvhtree = BLI_bvhtree_new(cloth->numfaces, epsilon, 4, 26); + + // fill tree + for(i = 0; i < cloth->numfaces; i++, mfaces++) + { + VECCOPY(&co[0*3], verts[mfaces->v1].xold); + VECCOPY(&co[1*3], verts[mfaces->v2].xold); + VECCOPY(&co[2*3], verts[mfaces->v3].xold); + + if(mfaces->v4) + VECCOPY(&co[3*3], verts[mfaces->v4].xold); + + BLI_bvhtree_insert(bvhtree, i, co, (mfaces->v4 ? 4 : 3)); + } + + // balance tree + BLI_bvhtree_balance(bvhtree); + + return bvhtree; +} + +void bvhtree_update_from_cloth(ClothModifierData *clmd, int moving) +{ + unsigned int i = 0; + Cloth *cloth = clmd->clothObject; + BVHTree *bvhtree = cloth->bvhtree; + ClothVertex *verts = cloth->verts; + MFace *mfaces; + float co[12], co_moving[12]; + int ret = 0; + + if(!bvhtree) + return; + + mfaces = cloth->mfaces; + + // update vertex position in bvh tree + if(verts && mfaces) + { + for(i = 0; i < cloth->numfaces; i++, mfaces++) + { + VECCOPY(&co[0*3], verts[mfaces->v1].txold); + VECCOPY(&co[1*3], verts[mfaces->v2].txold); + VECCOPY(&co[2*3], verts[mfaces->v3].txold); + + if(mfaces->v4) + VECCOPY(&co[3*3], verts[mfaces->v4].txold); + + // copy new locations into array + if(moving) + { + // update moving positions + VECCOPY(&co_moving[0*3], verts[mfaces->v1].tx); + VECCOPY(&co_moving[1*3], verts[mfaces->v2].tx); + VECCOPY(&co_moving[2*3], verts[mfaces->v3].tx); + + if(mfaces->v4) + VECCOPY(&co_moving[3*3], verts[mfaces->v4].tx); + + ret = BLI_bvhtree_update_node(bvhtree, i, co, co_moving, (mfaces->v4 ? 4 : 3)); + } + else + { + ret = BLI_bvhtree_update_node(bvhtree, i, co, NULL, (mfaces->v4 ? 4 : 3)); + } + + // check if tree is already full + if(!ret) + break; + } + + BLI_bvhtree_update_tree(bvhtree); + } +} + +void bvhselftree_update_from_cloth(ClothModifierData *clmd, int moving) +{ + unsigned int i = 0; + Cloth *cloth = clmd->clothObject; + BVHTree *bvhtree = cloth->bvhselftree; + ClothVertex *verts = cloth->verts; + MFace *mfaces; + float co[12], co_moving[12]; + int ret = 0; + + if(!bvhtree) + return; + + mfaces = cloth->mfaces; + + // update vertex position in bvh tree + if(verts && mfaces) + { + for(i = 0; i < cloth->numverts; i++, verts++) + { + VECCOPY(&co[0*3], verts->txold); + + // copy new locations into array + if(moving) + { + // update moving positions + VECCOPY(&co_moving[0*3], verts->tx); + + ret = BLI_bvhtree_update_node(bvhtree, i, co, co_moving, 1); + } + else + { + ret = BLI_bvhtree_update_node(bvhtree, i, co, NULL, 1); + } + + // check if tree is already full + if(!ret) + break; + } + + BLI_bvhtree_update_tree(bvhtree); + } +} + +int modifiers_indexInObject(Object *ob, ModifierData *md_seek); + +int cloth_read_cache(Object *ob, ClothModifierData *clmd, float framenr) +{ + PTCacheID pid; + PTCacheFile *pf; + Cloth *cloth = clmd->clothObject; + unsigned int a, ret = 1; + + if(!cloth) + return 0; + + BKE_ptcache_id_from_cloth(&pid, ob, clmd); + pf = BKE_ptcache_file_open(&pid, PTCACHE_FILE_READ, framenr); + if(pf) { + for(a = 0; a < cloth->numverts; a++) { + if(!BKE_ptcache_file_read_floats(pf, cloth->verts[a].x, 3)) { + ret = 0; + break; + } + if(!BKE_ptcache_file_read_floats(pf, cloth->verts[a].xconst, 3)) { + ret = 0; + break; + } + if(!BKE_ptcache_file_read_floats(pf, cloth->verts[a].v, 3)) { + ret = 0; + break; + } + } + + BKE_ptcache_file_close(pf); + } + else + ret = 0; + + return ret; +} + +void cloth_clear_cache(Object *ob, ClothModifierData *clmd, float framenr) +{ + PTCacheID pid; + + BKE_ptcache_id_from_cloth(&pid, ob, clmd); + + // don't do anything as long as we're in editmode! + if(pid.cache->flag & PTCACHE_BAKE_EDIT_ACTIVE) + return; + + BKE_ptcache_id_clear(&pid, PTCACHE_CLEAR_AFTER, framenr); +} + +void cloth_write_cache(Object *ob, ClothModifierData *clmd, float framenr) +{ + Cloth *cloth = clmd->clothObject; + PTCacheID pid; + PTCacheFile *pf; + unsigned int a; + + if(!cloth) + return; + + BKE_ptcache_id_from_cloth(&pid, ob, clmd); + pf = BKE_ptcache_file_open(&pid, PTCACHE_FILE_WRITE, framenr); + if(!pf) + return; + + for(a = 0; a < cloth->numverts; a++) { + BKE_ptcache_file_write_floats(pf, cloth->verts[a].x, 3); + BKE_ptcache_file_write_floats(pf, cloth->verts[a].xconst, 3); + BKE_ptcache_file_write_floats(pf, cloth->verts[a].v, 3); + } + + BKE_ptcache_file_close(pf); +} + +static int do_init_cloth(Object *ob, ClothModifierData *clmd, DerivedMesh *result, int framenr) +{ + PointCache *cache; + + cache= clmd->point_cache; + + /* initialize simulation data if it didn't exist already */ + if(clmd->clothObject == NULL) { + if(!cloth_from_object(ob, clmd, result, framenr, 1)) { + cache->flag &= ~PTCACHE_SIMULATION_VALID; + cache->simframe= 0; + return 0; + } + + if(clmd->clothObject == NULL) { + cache->flag &= ~PTCACHE_SIMULATION_VALID; + cache->simframe= 0; + return 0; + } + + implicit_set_positions(clmd); + } + + return 1; +} + +static int do_step_cloth(Object *ob, ClothModifierData *clmd, DerivedMesh *result, int framenr) +{ + ClothVertex *verts = NULL; + Cloth *cloth; + ListBase *effectors = NULL; + MVert *mvert; + int i, ret = 0; + + /* simulate 1 frame forward */ + cloth = clmd->clothObject; + verts = cloth->verts; + mvert = result->getVertArray(result); + + /* force any pinned verts to their constrained location. */ + for(i = 0; i < clmd->clothObject->numverts; i++, verts++) { + /* save the previous position. */ + VECCOPY(verts->xold, verts->xconst); + VECCOPY(verts->txold, verts->x); + + /* Get the current position. */ + VECCOPY(verts->xconst, mvert[i].co); + Mat4MulVecfl(ob->obmat, verts->xconst); + } + + tstart(); + + /* call the solver. */ + if(solvers [clmd->sim_parms->solver_type].solver) + ret = solvers[clmd->sim_parms->solver_type].solver(ob, framenr, clmd, effectors); + + tend(); + + // printf ( "%f\n", ( float ) tval() ); + + return ret; +} + +/************************************************ + * clothModifier_do - main simulation function +************************************************/ +DerivedMesh *clothModifier_do(ClothModifierData *clmd, Object *ob, DerivedMesh *dm, int useRenderParams, int isFinalCalc) +{ + DerivedMesh *result; + PointCache *cache; + PTCacheID pid; + float timescale; + int framedelta, framenr, startframe, endframe; + + framenr= (int)G.scene->r.cfra; + cache= clmd->point_cache; + result = CDDM_copy(dm); + + BKE_ptcache_id_from_cloth(&pid, ob, clmd); + BKE_ptcache_id_time(&pid, framenr, &startframe, &endframe, ×cale); + clmd->sim_parms->timescale= timescale; + + if(!result) { + cache->flag &= ~PTCACHE_SIMULATION_VALID; + cache->simframe= 0; + return dm; + } + + /* verify we still have the same number of vertices, if not do nothing. + * note that this should only happen if the number of vertices changes + * during an animation due to a preceding modifier, this should not + * happen because of object changes! */ + if(clmd->clothObject) { + if(result->getNumVerts(result) != clmd->clothObject->numverts) { + cache->flag &= ~PTCACHE_SIMULATION_VALID; + cache->simframe= 0; + return result; + } + } + + // unused in the moment, calculated seperately in implicit.c + clmd->sim_parms->dt = clmd->sim_parms->timescale / clmd->sim_parms->stepsPerFrame; + + /* handle continuous simulation with the play button */ + if(BKE_ptcache_get_continue_physics()) { + cache->flag &= ~PTCACHE_SIMULATION_VALID; + cache->simframe= 0; + + /* do simulation */ + if(!do_init_cloth(ob, clmd, result, framenr)) + return result; + + do_step_cloth(ob, clmd, result, framenr); + cloth_to_object(ob, clmd, result); + + return result; + } + + /* simulation is only active during a specific period */ + if(framenr < startframe) { + cache->flag &= ~PTCACHE_SIMULATION_VALID; + cache->simframe= 0; + return result; + } + else if(framenr > endframe) { + framenr= endframe; + } + + if(cache->flag & PTCACHE_SIMULATION_VALID) + framedelta= framenr - cache->simframe; + else + framedelta= -1; + + /* initialize simulation data if it didn't exist already */ + if(!do_init_cloth(ob, clmd, result, framenr)) + return result; + + /* try to read from cache */ + if(cloth_read_cache(ob, clmd, framenr)) { + cache->flag |= PTCACHE_SIMULATION_VALID; + cache->simframe= framenr; + + implicit_set_positions(clmd); + cloth_to_object (ob, clmd, result); + + return result; + } + else if(ob->id.lib || (cache->flag & PTCACHE_BAKED)) { + /* if baked and nothing in cache, do nothing */ + cache->flag &= ~PTCACHE_SIMULATION_VALID; + cache->simframe= 0; + return result; + } + + if(framenr == startframe) { + cache->flag |= PTCACHE_SIMULATION_VALID; + cache->simframe= framenr; + + /* don't write cache on first frame, but on second frame write + * cache for frame 1 and 2 */ + } + else if(framedelta == 1) { + /* if on second frame, write cache for first frame */ + if(framenr == startframe+1) + cloth_write_cache(ob, clmd, startframe); + + /* do simulation */ + cache->flag |= PTCACHE_SIMULATION_VALID; + cache->simframe= framenr; + + if(!do_step_cloth(ob, clmd, result, framenr)) { + cache->flag &= ~PTCACHE_SIMULATION_VALID; + cache->simframe= 0; + } + else + cloth_write_cache(ob, clmd, framenr); + + cloth_to_object (ob, clmd, result); + } + else { + cache->flag &= ~PTCACHE_SIMULATION_VALID; + cache->simframe= 0; + } + + return result; +} + +/* frees all */ +void cloth_free_modifier ( Object *ob, ClothModifierData *clmd ) +{ + Cloth *cloth = NULL; + + if ( !clmd ) + return; + + cloth = clmd->clothObject; + + + if ( cloth ) + { + // If our solver provides a free function, call it + if ( solvers [clmd->sim_parms->solver_type].free ) + { + solvers [clmd->sim_parms->solver_type].free ( clmd ); + } + + // Free the verts. + if ( cloth->verts != NULL ) + MEM_freeN ( cloth->verts ); + + cloth->verts = NULL; + cloth->numverts = 0; + + // Free the springs. + if ( cloth->springs != NULL ) + { + LinkNode *search = cloth->springs; + while(search) + { + ClothSpring *spring = search->link; + + MEM_freeN ( spring ); + search = search->next; + } + BLI_linklist_free(cloth->springs, NULL); + + cloth->springs = NULL; + } + + cloth->springs = NULL; + cloth->numsprings = 0; + + // free BVH collision tree + if ( cloth->bvhtree ) + BLI_bvhtree_free ( cloth->bvhtree ); + + if ( cloth->bvhselftree ) + BLI_bvhtree_free ( cloth->bvhselftree ); + + // we save our faces for collision objects + if ( cloth->mfaces ) + MEM_freeN ( cloth->mfaces ); + + if(cloth->edgehash) + BLI_edgehash_free ( cloth->edgehash, NULL ); + + + /* + if(clmd->clothObject->facemarks) + MEM_freeN(clmd->clothObject->facemarks); + */ + MEM_freeN ( cloth ); + clmd->clothObject = NULL; + } +} + +/* frees all */ +void cloth_free_modifier_extern ( ClothModifierData *clmd ) +{ + Cloth *cloth = NULL; + if(G.rt > 0) + printf("cloth_free_modifier_extern\n"); + + if ( !clmd ) + return; + + cloth = clmd->clothObject; + + if ( cloth ) + { + if(G.rt > 0) + printf("cloth_free_modifier_extern in\n"); + + // If our solver provides a free function, call it + if ( solvers [clmd->sim_parms->solver_type].free ) + { + solvers [clmd->sim_parms->solver_type].free ( clmd ); + } + + // Free the verts. + if ( cloth->verts != NULL ) + MEM_freeN ( cloth->verts ); + + cloth->verts = NULL; + cloth->numverts = 0; + + // Free the springs. + if ( cloth->springs != NULL ) + { + LinkNode *search = cloth->springs; + while(search) + { + ClothSpring *spring = search->link; + + MEM_freeN ( spring ); + search = search->next; + } + BLI_linklist_free(cloth->springs, NULL); + + cloth->springs = NULL; + } + + cloth->springs = NULL; + cloth->numsprings = 0; + + // free BVH collision tree + if ( cloth->bvhtree ) + BLI_bvhtree_free ( cloth->bvhtree ); + + if ( cloth->bvhselftree ) + BLI_bvhtree_free ( cloth->bvhselftree ); + + // we save our faces for collision objects + if ( cloth->mfaces ) + MEM_freeN ( cloth->mfaces ); + + if(cloth->edgehash) + BLI_edgehash_free ( cloth->edgehash, NULL ); + + + /* + if(clmd->clothObject->facemarks) + MEM_freeN(clmd->clothObject->facemarks); + */ + MEM_freeN ( cloth ); + clmd->clothObject = NULL; + } +} + +/****************************************************************************** +* +* Internal functions. +* +******************************************************************************/ + +/** + * cloth_to_object - copies the deformed vertices to the object. + * + **/ +static void cloth_to_object (Object *ob, ClothModifierData *clmd, DerivedMesh *dm) +{ + unsigned int i = 0; + MVert *mvert = NULL; + unsigned int numverts; + Cloth *cloth = clmd->clothObject; + + if (clmd->clothObject) { + /* inverse matrix is not uptodate... */ + Mat4Invert (ob->imat, ob->obmat); + + mvert = CDDM_get_verts(dm); + numverts = dm->getNumVerts(dm); + + for (i = 0; i < numverts; i++) + { + VECCOPY (mvert[i].co, cloth->verts[i].x); + Mat4MulVecfl (ob->imat, mvert[i].co); /* cloth is in global coords */ + } + } +} + + +/** + * cloth_apply_vgroup - applies a vertex group as specified by type + * + **/ +/* can be optimized to do all groups in one loop */ +static void cloth_apply_vgroup ( ClothModifierData *clmd, DerivedMesh *dm ) +{ + int i = 0; + int j = 0; + MDeformVert *dvert = NULL; + Cloth *clothObj = NULL; + int numverts = dm->getNumVerts ( dm ); + float goalfac = 0; + ClothVertex *verts = NULL; + + clothObj = clmd->clothObject; + + if ( !dm ) + return; + + numverts = dm->getNumVerts ( dm ); + + verts = clothObj->verts; + + if (((clmd->sim_parms->flags & CLOTH_SIMSETTINGS_FLAG_SCALING ) || + (clmd->sim_parms->flags & CLOTH_SIMSETTINGS_FLAG_GOAL )) && + ((clmd->sim_parms->vgroup_mass>0) || + (clmd->sim_parms->vgroup_struct>0)|| + (clmd->sim_parms->vgroup_bend>0))) + { + for ( i = 0; i < numverts; i++, verts++ ) + { + dvert = dm->getVertData ( dm, i, CD_MDEFORMVERT ); + if ( dvert ) + { + for ( j = 0; j < dvert->totweight; j++ ) + { + if (( dvert->dw[j].def_nr == (clmd->sim_parms->vgroup_mass-1)) && (clmd->sim_parms->flags & CLOTH_SIMSETTINGS_FLAG_GOAL )) + { + verts->goal = dvert->dw [j].weight; + goalfac= 1.0f; + + /* + // Kicking goal factor to simplify things...who uses that anyway? + // ABS ( clmd->sim_parms->maxgoal - clmd->sim_parms->mingoal ); + */ + + verts->goal = ( float ) pow ( verts->goal , 4.0f ); + if ( verts->goal >=SOFTGOALSNAP ) + { + verts->flags |= CLOTH_VERT_FLAG_PINNED; + } + } + + if (clmd->sim_parms->flags & CLOTH_SIMSETTINGS_FLAG_SCALING ) + { + if( dvert->dw[j].def_nr == (clmd->sim_parms->vgroup_struct-1)) + { + verts->struct_stiff = dvert->dw [j].weight; + verts->shear_stiff = dvert->dw [j].weight; + } + + if( dvert->dw[j].def_nr == (clmd->sim_parms->vgroup_bend-1)) + { + verts->bend_stiff = dvert->dw [j].weight; + } + } + /* + // for later + if( dvert->dw[j].def_nr == (clmd->sim_parms->vgroup_weight-1)) + { + verts->mass = dvert->dw [j].weight; + } + */ + } + } + } + } +} + +static int cloth_from_object(Object *ob, ClothModifierData *clmd, DerivedMesh *dm, float framenr, int first) +{ + int i = 0; + MVert *mvert = NULL; + ClothVertex *verts = NULL; + float tnull[3] = {0,0,0}; + Cloth *cloth = NULL; + float maxdist = 0; + + // If we have a clothObject, free it. + if ( clmd->clothObject != NULL ) + { + cloth_free_modifier ( ob, clmd ); + if(G.rt > 0) + printf("cloth_free_modifier cloth_from_object\n"); + } + + // Allocate a new cloth object. + clmd->clothObject = MEM_callocN ( sizeof ( Cloth ), "cloth" ); + if ( clmd->clothObject ) + { + clmd->clothObject->old_solver_type = 255; + // clmd->clothObject->old_collision_type = 255; + cloth = clmd->clothObject; + clmd->clothObject->edgehash = NULL; + } + else if ( !clmd->clothObject ) + { + modifier_setError ( & ( clmd->modifier ), "Out of memory on allocating clmd->clothObject." ); + return 0; + } + + // mesh input objects need DerivedMesh + if ( !dm ) + return 0; + + cloth_from_mesh ( ob, clmd, dm ); + + // create springs + clmd->clothObject->springs = NULL; + clmd->clothObject->numsprings = -1; + + mvert = dm->getVertArray ( dm ); + verts = clmd->clothObject->verts; + + // set initial values + for ( i = 0; i < dm->getNumVerts(dm); i++, verts++ ) + { + if(first) + { + VECCOPY ( verts->x, mvert[i].co ); + Mat4MulVecfl ( ob->obmat, verts->x ); + } + + /* no GUI interface yet */ + verts->mass = clmd->sim_parms->mass; + verts->impulse_count = 0; + + if ( clmd->sim_parms->flags & CLOTH_SIMSETTINGS_FLAG_GOAL ) + verts->goal= clmd->sim_parms->defgoal; + else + verts->goal= 0.0f; + + verts->flags = 0; + VECCOPY ( verts->xold, verts->x ); + VECCOPY ( verts->xconst, verts->x ); + VECCOPY ( verts->txold, verts->x ); + VECCOPY ( verts->tx, verts->x ); + VecMulf ( verts->v, 0.0f ); + + verts->impulse_count = 0; + VECCOPY ( verts->impulse, tnull ); + } + + // apply / set vertex groups + // has to be happen before springs are build! + cloth_apply_vgroup (clmd, dm); + + if ( !cloth_build_springs ( clmd, dm ) ) + { + cloth_free_modifier ( ob, clmd ); + modifier_setError ( & ( clmd->modifier ), "Can't build springs." ); + printf("cloth_free_modifier cloth_build_springs\n"); + return 0; + } + + for ( i = 0; i < dm->getNumVerts(dm); i++) + { + if((!(cloth->verts[i].flags & CLOTH_VERT_FLAG_PINNED)) && (cloth->verts[i].goal > ALMOST_ZERO)) + { + cloth_add_spring (clmd, i, i, 0.0, CLOTH_SPRING_TYPE_GOAL); + } + } + + // init our solver + if ( solvers [clmd->sim_parms->solver_type].init ) { + solvers [clmd->sim_parms->solver_type].init ( ob, clmd ); + } + + if(!first) + implicit_set_positions(clmd); + + clmd->clothObject->bvhtree = bvhtree_build_from_cloth ( clmd, clmd->coll_parms->epsilon ); + + for(i = 0; i < dm->getNumVerts(dm); i++) + { + maxdist = MAX2(maxdist, clmd->coll_parms->selfepsilon* ( cloth->verts[i].avg_spring_len*2.0)); + } + + clmd->clothObject->bvhselftree = bvhselftree_build_from_cloth ( clmd, maxdist ); + + return 1; +} + +static void cloth_from_mesh ( Object *ob, ClothModifierData *clmd, DerivedMesh *dm ) +{ + unsigned int numverts = dm->getNumVerts ( dm ); + unsigned int numfaces = dm->getNumFaces ( dm ); + MFace *mface = CDDM_get_faces(dm); + unsigned int i = 0; + + /* Allocate our vertices. */ + clmd->clothObject->numverts = numverts; + clmd->clothObject->verts = MEM_callocN ( sizeof ( ClothVertex ) * clmd->clothObject->numverts, "clothVertex" ); + if ( clmd->clothObject->verts == NULL ) + { + cloth_free_modifier ( ob, clmd ); + modifier_setError ( & ( clmd->modifier ), "Out of memory on allocating clmd->clothObject->verts." ); + printf("cloth_free_modifier clmd->clothObject->verts\n"); + return; + } + + // save face information + clmd->clothObject->numfaces = numfaces; + clmd->clothObject->mfaces = MEM_callocN ( sizeof ( MFace ) * clmd->clothObject->numfaces, "clothMFaces" ); + if ( clmd->clothObject->mfaces == NULL ) + { + cloth_free_modifier ( ob, clmd ); + modifier_setError ( & ( clmd->modifier ), "Out of memory on allocating clmd->clothObject->mfaces." ); + printf("cloth_free_modifier clmd->clothObject->mfaces\n"); + return; + } + for ( i = 0; i < numfaces; i++ ) + memcpy ( &clmd->clothObject->mfaces[i], &mface[i], sizeof ( MFace ) ); + + /* Free the springs since they can't be correct if the vertices + * changed. + */ + if ( clmd->clothObject->springs != NULL ) + MEM_freeN ( clmd->clothObject->springs ); + +} + +/*************************************************************************************** +* SPRING NETWORK BUILDING IMPLEMENTATION BEGIN +***************************************************************************************/ + +// be carefull: implicit solver has to be resettet when using this one! +// --> only for implicit handling of this spring! +int cloth_add_spring ( ClothModifierData *clmd, unsigned int indexA, unsigned int indexB, float restlength, int spring_type) +{ + Cloth *cloth = clmd->clothObject; + ClothSpring *spring = NULL; + + if(cloth) + { + // TODO: look if this spring is already there + + spring = ( ClothSpring * ) MEM_callocN ( sizeof ( ClothSpring ), "cloth spring" ); + + if(!spring) + return 0; + + spring->ij = indexA; + spring->kl = indexB; + spring->restlen = restlength; + spring->type = spring_type; + spring->flags = 0; + spring->stiffness = 0; + + cloth->numsprings++; + + BLI_linklist_prepend ( &cloth->springs, spring ); + + return 1; + } + return 0; +} + +void cloth_free_errorsprings(Cloth *cloth, EdgeHash *edgehash, LinkNode **edgelist) +{ + unsigned int i = 0; + + if ( cloth->springs != NULL ) + { + LinkNode *search = cloth->springs; + while(search) + { + ClothSpring *spring = search->link; + + MEM_freeN ( spring ); + search = search->next; + } + BLI_linklist_free(cloth->springs, NULL); + + cloth->springs = NULL; + } + + if(edgelist) + { + for ( i = 0; i < cloth->numverts; i++ ) + { + BLI_linklist_free ( edgelist[i],NULL ); + } + + MEM_freeN ( edgelist ); + } + + if(cloth->edgehash) + BLI_edgehash_free ( cloth->edgehash, NULL ); +} + +int cloth_build_springs ( ClothModifierData *clmd, DerivedMesh *dm ) +{ + Cloth *cloth = clmd->clothObject; + ClothSpring *spring = NULL, *tspring = NULL, *tspring2 = NULL; + unsigned int struct_springs = 0, shear_springs=0, bend_springs = 0; + int i = 0; + int numverts = dm->getNumVerts ( dm ); + int numedges = dm->getNumEdges ( dm ); + int numfaces = dm->getNumFaces ( dm ); + MEdge *medge = CDDM_get_edges ( dm ); + MFace *mface = CDDM_get_faces ( dm ); + int index2 = 0; // our second vertex index + LinkNode **edgelist = NULL; + EdgeHash *edgehash = NULL; + LinkNode *search = NULL, *search2 = NULL; + float temp[3]; + + // error handling + if ( numedges==0 ) + return 0; + + cloth->springs = NULL; + + edgelist = MEM_callocN ( sizeof ( LinkNode * ) * numverts, "cloth_edgelist_alloc" ); + + if(!edgelist) + return 0; + + for ( i = 0; i < numverts; i++ ) + { + edgelist[i] = NULL; + } + + if ( cloth->springs ) + MEM_freeN ( cloth->springs ); + + // create spring network hash + edgehash = BLI_edgehash_new(); + + // structural springs + for ( i = 0; i < numedges; i++ ) + { + spring = ( ClothSpring * ) MEM_callocN ( sizeof ( ClothSpring ), "cloth spring" ); + + if ( spring ) + { + spring->ij = MIN2(medge[i].v1, medge[i].v2); + spring->kl = MAX2(medge[i].v2, medge[i].v1); + VECSUB ( temp, cloth->verts[spring->kl].x, cloth->verts[spring->ij].x ); + spring->restlen = sqrt ( INPR ( temp, temp ) ); + clmd->sim_parms->avg_spring_len += spring->restlen; + cloth->verts[spring->ij].avg_spring_len += spring->restlen; + cloth->verts[spring->kl].avg_spring_len += spring->restlen; + cloth->verts[spring->ij].spring_count++; + cloth->verts[spring->kl].spring_count++; + spring->type = CLOTH_SPRING_TYPE_STRUCTURAL; + spring->flags = 0; + spring->stiffness = (cloth->verts[spring->kl].struct_stiff + cloth->verts[spring->ij].struct_stiff) / 2.0; + struct_springs++; + + BLI_linklist_prepend ( &cloth->springs, spring ); + } + else + { + cloth_free_errorsprings(cloth, edgehash, edgelist); + return 0; + } + } + + if(struct_springs > 0) + clmd->sim_parms->avg_spring_len /= struct_springs; + + for(i = 0; i < numverts; i++) + { + cloth->verts[i].avg_spring_len = cloth->verts[i].avg_spring_len * 0.49 / ((float)cloth->verts[i].spring_count); + } + + // shear springs + for ( i = 0; i < numfaces; i++ ) + { + // triangle faces already have shear springs due to structural geometry + if ( !mface[i].v4 ) + continue; + + spring = ( ClothSpring *) MEM_callocN ( sizeof ( ClothSpring ), "cloth spring" ); + + if(!spring) + { + cloth_free_errorsprings(cloth, edgehash, edgelist); + return 0; + } + + spring->ij = MIN2(mface[i].v1, mface[i].v3); + spring->kl = MAX2(mface[i].v3, mface[i].v1); + VECSUB ( temp, cloth->verts[spring->kl].x, cloth->verts[spring->ij].x ); + spring->restlen = sqrt ( INPR ( temp, temp ) ); + spring->type = CLOTH_SPRING_TYPE_SHEAR; + spring->stiffness = (cloth->verts[spring->kl].shear_stiff + cloth->verts[spring->ij].shear_stiff) / 2.0; + + BLI_linklist_append ( &edgelist[spring->ij], spring ); + BLI_linklist_append ( &edgelist[spring->kl], spring ); + shear_springs++; + + BLI_linklist_prepend ( &cloth->springs, spring ); + + + // if ( mface[i].v4 ) --> Quad face + spring = ( ClothSpring * ) MEM_callocN ( sizeof ( ClothSpring ), "cloth spring" ); + + if(!spring) + { + cloth_free_errorsprings(cloth, edgehash, edgelist); + return 0; + } + + spring->ij = MIN2(mface[i].v2, mface[i].v4); + spring->kl = MAX2(mface[i].v4, mface[i].v2); + VECSUB ( temp, cloth->verts[spring->kl].x, cloth->verts[spring->ij].x ); + spring->restlen = sqrt ( INPR ( temp, temp ) ); + spring->type = CLOTH_SPRING_TYPE_SHEAR; + spring->stiffness = (cloth->verts[spring->kl].shear_stiff + cloth->verts[spring->ij].shear_stiff) / 2.0; + + BLI_linklist_append ( &edgelist[spring->ij], spring ); + BLI_linklist_append ( &edgelist[spring->kl], spring ); + shear_springs++; + + BLI_linklist_prepend ( &cloth->springs, spring ); + } + + // bending springs + search2 = cloth->springs; + for ( i = struct_springs; i < struct_springs+shear_springs; i++ ) + { + if ( !search2 ) + break; + + tspring2 = search2->link; + search = edgelist[tspring2->kl]; + while ( search ) + { + tspring = search->link; + index2 = ( ( tspring->ij==tspring2->kl ) ? ( tspring->kl ) : ( tspring->ij ) ); + + // check for existing spring + // check also if startpoint is equal to endpoint + if ( !BLI_edgehash_haskey ( edgehash, MIN2(tspring2->ij, index2), MAX2(tspring2->ij, index2) ) + && ( index2!=tspring2->ij ) ) + { + spring = ( ClothSpring * ) MEM_callocN ( sizeof ( ClothSpring ), "cloth spring" ); + + if(!spring) + { + cloth_free_errorsprings(cloth, edgehash, edgelist); + return 0; + } + + spring->ij = MIN2(tspring2->ij, index2); + spring->kl = MAX2(tspring2->ij, index2); + VECSUB ( temp, cloth->verts[spring->kl].x, cloth->verts[spring->ij].x ); + spring->restlen = sqrt ( INPR ( temp, temp ) ); + spring->type = CLOTH_SPRING_TYPE_BENDING; + spring->stiffness = (cloth->verts[spring->kl].bend_stiff + cloth->verts[spring->ij].bend_stiff) / 2.0; + BLI_edgehash_insert ( edgehash, spring->ij, spring->kl, NULL ); + bend_springs++; + + BLI_linklist_prepend ( &cloth->springs, spring ); + } + search = search->next; + } + search2 = search2->next; + } + + /* insert other near springs in edgehash AFTER bending springs are calculated (for selfcolls) */ + for ( i = 0; i < numedges; i++ ) // struct springs + BLI_edgehash_insert ( edgehash, MIN2(medge[i].v1, medge[i].v2), MAX2(medge[i].v2, medge[i].v1), NULL ); + + for ( i = 0; i < numfaces; i++ ) // edge springs + { + if(mface[i].v4) + { + BLI_edgehash_insert ( edgehash, MIN2(mface[i].v1, mface[i].v3), MAX2(mface[i].v3, mface[i].v1), NULL ); + + BLI_edgehash_insert ( edgehash, MIN2(mface[i].v2, mface[i].v4), MAX2(mface[i].v2, mface[i].v4), NULL ); + } + } + + + cloth->numsprings = struct_springs + shear_springs + bend_springs; + + if ( edgelist ) + { + for ( i = 0; i < numverts; i++ ) + { + BLI_linklist_free ( edgelist[i],NULL ); + } + + MEM_freeN ( edgelist ); + } + + cloth->edgehash = edgehash; + + if(G.rt>0) + printf("avg_len: %f\n",clmd->sim_parms->avg_spring_len); + + return 1; + +} /* cloth_build_springs */ +/*************************************************************************************** +* SPRING NETWORK BUILDING IMPLEMENTATION END +***************************************************************************************/ + diff --git a/source/blender/blenkernel/intern/collision.c b/source/blender/blenkernel/intern/collision.c new file mode 100644 index 00000000000..9db3dda94eb --- /dev/null +++ b/source/blender/blenkernel/intern/collision.c @@ -0,0 +1,1677 @@ +/* collision.c +* +* +* ***** BEGIN GPL LICENSE BLOCK ***** +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License +* as published by the Free Software Foundation; either version 2 +* of the License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +* +* The Original Code is Copyright (C) Blender Foundation +* All rights reserved. +* +* The Original Code is: all of this file. +* +* Contributor(s): none yet. +* +* ***** END GPL LICENSE BLOCK ***** +*/ + +#include "MEM_guardedalloc.h" + +#include "BKE_cloth.h" + +#include "DNA_cloth_types.h" +#include "DNA_group_types.h" +#include "DNA_mesh_types.h" +#include "DNA_object_types.h" +#include "DNA_object_force.h" +#include "DNA_scene_types.h" + +#include "BKE_DerivedMesh.h" +#include "BKE_global.h" +#include "BKE_mesh.h" +#include "BKE_object.h" +#include "BKE_modifier.h" +#include "BKE_utildefines.h" +#include "BKE_DerivedMesh.h" + +#include "Bullet-C-Api.h" + +#include "BLI_kdopbvh.h" +#include "BKE_collision.h" + + +/*********************************** +Collision modifier code start +***********************************/ + +/* step is limited from 0 (frame start position) to 1 (frame end position) */ +void collision_move_object ( CollisionModifierData *collmd, float step, float prevstep ) +{ + float tv[3] = {0,0,0}; + unsigned int i = 0; + + for ( i = 0; i < collmd->numverts; i++ ) + { + VECSUB ( tv, collmd->xnew[i].co, collmd->x[i].co ); + VECADDS ( collmd->current_x[i].co, collmd->x[i].co, tv, prevstep ); + VECADDS ( collmd->current_xnew[i].co, collmd->x[i].co, tv, step ); + VECSUB ( collmd->current_v[i].co, collmd->current_xnew[i].co, collmd->current_x[i].co ); + } + bvhtree_update_from_mvert ( collmd->bvhtree, collmd->mfaces, collmd->numfaces, collmd->current_x, collmd->current_xnew, collmd->numverts, 1 ); +} + +BVHTree *bvhtree_build_from_mvert ( MFace *mfaces, unsigned int numfaces, MVert *x, unsigned int numverts, float epsilon ) +{ + BVHTree *tree; + float co[12]; + int i; + MFace *tface = mfaces; + + tree = BLI_bvhtree_new ( numfaces*2, epsilon, 4, 26 ); + + // fill tree + for ( i = 0; i < numfaces; i++, tface++ ) + { + VECCOPY ( &co[0*3], x[tface->v1].co ); + VECCOPY ( &co[1*3], x[tface->v2].co ); + VECCOPY ( &co[2*3], x[tface->v3].co ); + if ( tface->v4 ) + VECCOPY ( &co[3*3], x[tface->v4].co ); + + BLI_bvhtree_insert ( tree, i, co, ( mfaces->v4 ? 4 : 3 ) ); + } + + // balance tree + BLI_bvhtree_balance ( tree ); + + return tree; +} + +void bvhtree_update_from_mvert ( BVHTree * bvhtree, MFace *faces, int numfaces, MVert *x, MVert *xnew, int numverts, int moving ) +{ + int i; + MFace *mfaces = faces; + float co[12], co_moving[12]; + int ret = 0; + + if ( !bvhtree ) + return; + + if ( x ) + { + for ( i = 0; i < numfaces; i++, mfaces++ ) + { + VECCOPY ( &co[0*3], x[mfaces->v1].co ); + VECCOPY ( &co[1*3], x[mfaces->v2].co ); + VECCOPY ( &co[2*3], x[mfaces->v3].co ); + if ( mfaces->v4 ) + VECCOPY ( &co[3*3], x[mfaces->v4].co ); + + // copy new locations into array + if ( moving && xnew ) + { + // update moving positions + VECCOPY ( &co_moving[0*3], xnew[mfaces->v1].co ); + VECCOPY ( &co_moving[1*3], xnew[mfaces->v2].co ); + VECCOPY ( &co_moving[2*3], xnew[mfaces->v3].co ); + if ( mfaces->v4 ) + VECCOPY ( &co_moving[3*3], xnew[mfaces->v4].co ); + + ret = BLI_bvhtree_update_node ( bvhtree, i, co, co_moving, ( mfaces->v4 ? 4 : 3 ) ); + } + else + { + ret = BLI_bvhtree_update_node ( bvhtree, i, co, NULL, ( mfaces->v4 ? 4 : 3 ) ); + } + + // check if tree is already full + if ( !ret ) + break; + } + + BLI_bvhtree_update_tree ( bvhtree ); + } +} + +/*********************************** +Collision modifier code end +***********************************/ + +/** +* gsl_poly_solve_cubic - +* +* copied from SOLVE_CUBIC.C --> GSL +*/ + +#define mySWAP(a,b) do { double tmp = b ; b = a ; a = tmp ; } while(0) + +int +gsl_poly_solve_cubic (double a, double b, double c, + double *x0, double *x1, double *x2) +{ + double q = (a * a - 3 * b); + double r = (2 * a * a * a - 9 * a * b + 27 * c); + + double Q = q / 9; + double R = r / 54; + + double Q3 = Q * Q * Q; + double R2 = R * R; + + double CR2 = 729 * r * r; + double CQ3 = 2916 * q * q * q; + + if (R == 0 && Q == 0) + { + *x0 = - a / 3 ; + *x1 = - a / 3 ; + *x2 = - a / 3 ; + return 3 ; + } + else if (CR2 == CQ3) + { + /* this test is actually R2 == Q3, written in a form suitable + for exact computation with integers */ + + /* Due to finite precision some double roots may be missed, and + considered to be a pair of complex roots z = x +/- epsilon i + close to the real axis. */ + + double sqrtQ = sqrt (Q); + + if (R > 0) + { + *x0 = -2 * sqrtQ - a / 3; + *x1 = sqrtQ - a / 3; + *x2 = sqrtQ - a / 3; + } + else + { + *x0 = - sqrtQ - a / 3; + *x1 = - sqrtQ - a / 3; + *x2 = 2 * sqrtQ - a / 3; + } + return 3 ; + } + else if (CR2 < CQ3) /* equivalent to R2 < Q3 */ + { + double sqrtQ = sqrt (Q); + double sqrtQ3 = sqrtQ * sqrtQ * sqrtQ; + double theta = acos (R / sqrtQ3); + double norm = -2 * sqrtQ; + *x0 = norm * cos (theta / 3) - a / 3; + *x1 = norm * cos ((theta + 2.0 * M_PI) / 3) - a / 3; + *x2 = norm * cos ((theta - 2.0 * M_PI) / 3) - a / 3; + + /* Sort *x0, *x1, *x2 into increasing order */ + + if (*x0 > *x1) + mySWAP(*x0, *x1) ; + + if (*x1 > *x2) + { + mySWAP(*x1, *x2) ; + + if (*x0 > *x1) + mySWAP(*x0, *x1) ; + } + + return 3; + } + else + { + double sgnR = (R >= 0 ? 1 : -1); + double A = -sgnR * pow (fabs (R) + sqrt (R2 - Q3), 1.0/3.0); + double B = Q / A ; + *x0 = A + B - a / 3; + return 1; + } +} + + + +/** +* gsl_poly_solve_quadratic +* +* copied from GSL +*/ +int +gsl_poly_solve_quadratic (double a, double b, double c, + double *x0, double *x1) +{ + double disc = b * b - 4 * a * c; + + if (a == 0) /* Handle linear case */ + { + if (b == 0) + { + return 0; + } + else + { + *x0 = -c / b; + return 1; + }; + } + + if (disc > 0) + { + if (b == 0) + { + double r = fabs (0.5 * sqrt (disc) / a); + *x0 = -r; + *x1 = r; + } + else + { + double sgnb = (b > 0 ? 1 : -1); + double temp = -0.5 * (b + sgnb * sqrt (disc)); + double r1 = temp / a ; + double r2 = c / temp ; + + if (r1 < r2) + { + *x0 = r1 ; + *x1 = r2 ; + } + else + { + *x0 = r2 ; + *x1 = r1 ; + } + } + return 2; + } + else if (disc == 0) + { + *x0 = -0.5 * b / a ; + *x1 = -0.5 * b / a ; + return 2 ; + } + else + { + return 0; + } +} + + + + +/* +* See Bridson et al. "Robust Treatment of Collision, Contact and Friction for Cloth Animation" +* page 4, left column +*/ +int cloth_get_collision_time ( double a[3], double b[3], double c[3], double d[3], double e[3], double f[3], double solution[3] ) +{ + int num_sols = 0; + + // x^0 - checked + double g = a[0] * c[1] * e[2] - a[0] * c[2] * e[1] + + a[1] * c[2] * e[0] - a[1] * c[0] * e[2] + + a[2] * c[0] * e[1] - a[2] * c[1] * e[0]; + + // x^1 + double h = -b[2] * c[1] * e[0] + b[1] * c[2] * e[0] - a[2] * d[1] * e[0] + + a[1] * d[2] * e[0] + b[2] * c[0] * e[1] - b[0] * c[2] * e[1] + + a[2] * d[0] * e[1] - a[0] * d[2] * e[1] - b[1] * c[0] * e[2] + + b[0] * c[1] * e[2] - a[1] * d[0] * e[2] + a[0] * d[1] * e[2] - + a[2] * c[1] * f[0] + a[1] * c[2] * f[0] + a[2] * c[0] * f[1] - + a[0] * c[2] * f[1] - a[1] * c[0] * f[2] + a[0] * c[1] * f[2]; + + // x^2 + double i = -b[2] * d[1] * e[0] + b[1] * d[2] * e[0] + + b[2] * d[0] * e[1] - b[0] * d[2] * e[1] - + b[1] * d[0] * e[2] + b[0] * d[1] * e[2] - + b[2] * c[1] * f[0] + b[1] * c[2] * f[0] - + a[2] * d[1] * f[0] + a[1] * d[2] * f[0] + + b[2] * c[0] * f[1] - b[0] * c[2] * f[1] + + a[2] * d[0] * f[1] - a[0] * d[2] * f[1] - + b[1] * c[0] * f[2] + b[0] * c[1] * f[2] - + a[1] * d[0] * f[2] + a[0] * d[1] * f[2]; + + // x^3 - checked + double j = -b[2] * d[1] * f[0] + b[1] * d[2] * f[0] + + b[2] * d[0] * f[1] - b[0] * d[2] * f[1] - + b[1] * d[0] * f[2] + b[0] * d[1] * f[2]; + + /* + printf("r1: %lf\n", a[0] * c[1] * e[2] - a[0] * c[2] * e[1]); + printf("r2: %lf\n", a[1] * c[2] * e[0] - a[1] * c[0] * e[2]); + printf("r3: %lf\n", a[2] * c[0] * e[1] - a[2] * c[1] * e[0]); + + printf("x1 x: %f, y: %f, z: %f\n", a[0], a[1], a[2]); + printf("x2 x: %f, y: %f, z: %f\n", c[0], c[1], c[2]); + printf("x3 x: %f, y: %f, z: %f\n", e[0], e[1], e[2]); + + printf("v1 x: %f, y: %f, z: %f\n", b[0], b[1], b[2]); + printf("v2 x: %f, y: %f, z: %f\n", d[0], d[1], d[2]); + printf("v3 x: %f, y: %f, z: %f\n", f[0], f[1], f[2]); + + printf("t^3: %lf, t^2: %lf, t^1: %lf, t^0: %lf\n", j, i, h, g); + +*/ + // Solve cubic equation to determine times t1, t2, t3, when the collision will occur. + if ( ABS ( j ) > DBL_EPSILON ) + { + i /= j; + h /= j; + g /= j; + num_sols = gsl_poly_solve_cubic ( i, h, g, &solution[0], &solution[1], &solution[2] ); + } + else + { + num_sols = gsl_poly_solve_quadratic ( i, h, g, &solution[0], &solution[1] ); + solution[2] = -1.0; + } + + // printf("num_sols: %d, sol1: %lf, sol2: %lf, sol3: %lf\n", num_sols, solution[0], solution[1], solution[2]); + + // Discard negative solutions + if ( ( num_sols >= 1 ) && ( solution[0] < DBL_EPSILON ) ) + { + --num_sols; + solution[0] = solution[num_sols]; + } + if ( ( num_sols >= 2 ) && ( solution[1] < DBL_EPSILON ) ) + { + --num_sols; + solution[1] = solution[num_sols]; + } + if ( ( num_sols == 3 ) && ( solution[2] < DBL_EPSILON ) ) + { + --num_sols; + } + + // Sort + if ( num_sols == 2 ) + { + if ( solution[0] > solution[1] ) + { + double tmp = solution[0]; + solution[0] = solution[1]; + solution[1] = tmp; + } + } + else if ( num_sols == 3 ) + { + + // Bubblesort + if ( solution[0] > solution[1] ) + { + double tmp = solution[0]; solution[0] = solution[1]; solution[1] = tmp; + } + if ( solution[1] > solution[2] ) + { + double tmp = solution[1]; solution[1] = solution[2]; solution[2] = tmp; + } + if ( solution[0] > solution[1] ) + { + double tmp = solution[0]; solution[0] = solution[1]; solution[1] = tmp; + } + } + + return num_sols; +} + + +// w3 is not perfect +void collision_compute_barycentric ( float pv[3], float p1[3], float p2[3], float p3[3], float *w1, float *w2, float *w3 ) +{ + double tempV1[3], tempV2[3], tempV4[3]; + double a,b,c,d,e,f; + + VECSUB ( tempV1, p1, p3 ); + VECSUB ( tempV2, p2, p3 ); + VECSUB ( tempV4, pv, p3 ); + + a = INPR ( tempV1, tempV1 ); + b = INPR ( tempV1, tempV2 ); + c = INPR ( tempV2, tempV2 ); + e = INPR ( tempV1, tempV4 ); + f = INPR ( tempV2, tempV4 ); + + d = ( a * c - b * b ); + + if ( ABS ( d ) < ALMOST_ZERO ) + { + *w1 = *w2 = *w3 = 1.0 / 3.0; + return; + } + + w1[0] = ( float ) ( ( e * c - b * f ) / d ); + + if ( w1[0] < 0 ) + w1[0] = 0; + + w2[0] = ( float ) ( ( f - b * ( double ) w1[0] ) / c ); + + if ( w2[0] < 0 ) + w2[0] = 0; + + w3[0] = 1.0f - w1[0] - w2[0]; +} + +DO_INLINE void collision_interpolateOnTriangle ( float to[3], float v1[3], float v2[3], float v3[3], double w1, double w2, double w3 ) +{ + to[0] = to[1] = to[2] = 0; + VECADDMUL ( to, v1, w1 ); + VECADDMUL ( to, v2, w2 ); + VECADDMUL ( to, v3, w3 ); +} + + +int cloth_collision_response_static ( ClothModifierData *clmd, CollisionModifierData *collmd, CollPair *collpair, CollPair *collision_end ) +{ + int result = 0; + Cloth *cloth1; + float w1, w2, w3, u1, u2, u3; + float v1[3], v2[3], relativeVelocity[3]; + float magrelVel; + float epsilon2 = BLI_bvhtree_getepsilon ( collmd->bvhtree ); + + cloth1 = clmd->clothObject; + + for ( ; collpair != collision_end; collpair++ ) + { + // only handle static collisions here + if ( collpair->flag & COLLISION_IN_FUTURE ) + continue; + + // compute barycentric coordinates for both collision points + collision_compute_barycentric ( collpair->pa, + cloth1->verts[collpair->ap1].txold, + cloth1->verts[collpair->ap2].txold, + cloth1->verts[collpair->ap3].txold, + &w1, &w2, &w3 ); + + // was: txold + collision_compute_barycentric ( collpair->pb, + collmd->current_x[collpair->bp1].co, + collmd->current_x[collpair->bp2].co, + collmd->current_x[collpair->bp3].co, + &u1, &u2, &u3 ); + + // Calculate relative "velocity". + collision_interpolateOnTriangle ( v1, cloth1->verts[collpair->ap1].tv, cloth1->verts[collpair->ap2].tv, cloth1->verts[collpair->ap3].tv, w1, w2, w3 ); + + collision_interpolateOnTriangle ( v2, collmd->current_v[collpair->bp1].co, collmd->current_v[collpair->bp2].co, collmd->current_v[collpair->bp3].co, u1, u2, u3 ); + + VECSUB ( relativeVelocity, v2, v1 ); + + // Calculate the normal component of the relative velocity (actually only the magnitude - the direction is stored in 'normal'). + magrelVel = INPR ( relativeVelocity, collpair->normal ); + + // printf("magrelVel: %f\n", magrelVel); + + // Calculate masses of points. + // TODO + + // If v_n_mag < 0 the edges are approaching each other. + if ( magrelVel > ALMOST_ZERO ) + { + // Calculate Impulse magnitude to stop all motion in normal direction. + float magtangent = 0, repulse = 0, d = 0; + double impulse = 0.0; + float vrel_t_pre[3]; + float temp[3]; + + // calculate tangential velocity + VECCOPY ( temp, collpair->normal ); + VecMulf ( temp, magrelVel ); + VECSUB ( vrel_t_pre, relativeVelocity, temp ); + + // Decrease in magnitude of relative tangential velocity due to coulomb friction + // in original formula "magrelVel" should be the "change of relative velocity in normal direction" + magtangent = MIN2 ( clmd->coll_parms->friction * 0.01 * magrelVel,sqrt ( INPR ( vrel_t_pre,vrel_t_pre ) ) ); + + // Apply friction impulse. + if ( magtangent > ALMOST_ZERO ) + { + Normalize ( vrel_t_pre ); + + impulse = magtangent / ( 1.0 + w1*w1 + w2*w2 + w3*w3 ); // 2.0 * + VECADDMUL ( cloth1->verts[collpair->ap1].impulse, vrel_t_pre, w1 * impulse ); + VECADDMUL ( cloth1->verts[collpair->ap2].impulse, vrel_t_pre, w2 * impulse ); + VECADDMUL ( cloth1->verts[collpair->ap3].impulse, vrel_t_pre, w3 * impulse ); + } + + // Apply velocity stopping impulse + // I_c = m * v_N / 2.0 + // no 2.0 * magrelVel normally, but looks nicer DG + impulse = magrelVel / ( 1.0 + w1*w1 + w2*w2 + w3*w3 ); + + VECADDMUL ( cloth1->verts[collpair->ap1].impulse, collpair->normal, w1 * impulse ); + cloth1->verts[collpair->ap1].impulse_count++; + + VECADDMUL ( cloth1->verts[collpair->ap2].impulse, collpair->normal, w2 * impulse ); + cloth1->verts[collpair->ap2].impulse_count++; + + VECADDMUL ( cloth1->verts[collpair->ap3].impulse, collpair->normal, w3 * impulse ); + cloth1->verts[collpair->ap3].impulse_count++; + + // Apply repulse impulse if distance too short + // I_r = -min(dt*kd, m(0,1d/dt - v_n)) + d = clmd->coll_parms->epsilon*8.0/9.0 + epsilon2*8.0/9.0 - collpair->distance; + if ( ( magrelVel < 0.1*d*clmd->sim_parms->stepsPerFrame ) && ( d > ALMOST_ZERO ) ) + { + repulse = MIN2 ( d*1.0/clmd->sim_parms->stepsPerFrame, 0.1*d*clmd->sim_parms->stepsPerFrame - magrelVel ); + + // stay on the safe side and clamp repulse + if ( impulse > ALMOST_ZERO ) + repulse = MIN2 ( repulse, 5.0*impulse ); + repulse = MAX2 ( impulse, repulse ); + + impulse = repulse / ( 1.0 + w1*w1 + w2*w2 + w3*w3 ); // original 2.0 / 0.25 + VECADDMUL ( cloth1->verts[collpair->ap1].impulse, collpair->normal, impulse ); + VECADDMUL ( cloth1->verts[collpair->ap2].impulse, collpair->normal, impulse ); + VECADDMUL ( cloth1->verts[collpair->ap3].impulse, collpair->normal, impulse ); + } + + result = 1; + } + } + return result; +} + +//Determines collisions on overlap, collisions are writen to collpair[i] and collision+number_collision_found is returned +CollPair* cloth_collision ( ModifierData *md1, ModifierData *md2, BVHTreeOverlap *overlap, CollPair *collpair ) +{ + ClothModifierData *clmd = ( ClothModifierData * ) md1; + CollisionModifierData *collmd = ( CollisionModifierData * ) md2; + MFace *face1=NULL, *face2 = NULL; + ClothVertex *verts1 = clmd->clothObject->verts; + double distance = 0; + float epsilon1 = clmd->coll_parms->epsilon; + float epsilon2 = BLI_bvhtree_getepsilon ( collmd->bvhtree ); + int i; + + face1 = & ( clmd->clothObject->mfaces[overlap->indexA] ); + face2 = & ( collmd->mfaces[overlap->indexB] ); + + // check all 4 possible collisions + for ( i = 0; i < 4; i++ ) + { + if ( i == 0 ) + { + // fill faceA + collpair->ap1 = face1->v1; + collpair->ap2 = face1->v2; + collpair->ap3 = face1->v3; + + // fill faceB + collpair->bp1 = face2->v1; + collpair->bp2 = face2->v2; + collpair->bp3 = face2->v3; + } + else if ( i == 1 ) + { + if ( face1->v4 ) + { + // fill faceA + collpair->ap1 = face1->v1; + collpair->ap2 = face1->v4; + collpair->ap3 = face1->v3; + + // fill faceB + collpair->bp1 = face2->v1; + collpair->bp2 = face2->v2; + collpair->bp3 = face2->v3; + } + else + i++; + } + if ( i == 2 ) + { + if ( face2->v4 ) + { + // fill faceA + collpair->ap1 = face1->v1; + collpair->ap2 = face1->v2; + collpair->ap3 = face1->v3; + + // fill faceB + collpair->bp1 = face2->v1; + collpair->bp2 = face2->v4; + collpair->bp3 = face2->v3; + } + else + break; + } + else if ( i == 3 ) + { + if ( face1->v4 && face2->v4 ) + { + // fill faceA + collpair->ap1 = face1->v1; + collpair->ap2 = face1->v4; + collpair->ap3 = face1->v3; + + // fill faceB + collpair->bp1 = face2->v1; + collpair->bp2 = face2->v4; + collpair->bp3 = face2->v3; + } + else + break; + } + +#ifdef WITH_BULLET + // calc distance + normal + distance = plNearestPoints ( + verts1[collpair->ap1].txold, verts1[collpair->ap2].txold, verts1[collpair->ap3].txold, collmd->current_x[collpair->bp1].co, collmd->current_x[collpair->bp2].co, collmd->current_x[collpair->bp3].co, collpair->pa,collpair->pb,collpair->vector ); +#else + // just be sure that we don't add anything + distance = 2.0 * ( epsilon1 + epsilon2 + ALMOST_ZERO ); +#endif + + if ( distance <= ( epsilon1 + epsilon2 + ALMOST_ZERO ) ) + { + VECCOPY ( collpair->normal, collpair->vector ); + Normalize ( collpair->normal ); + + collpair->distance = distance; + collpair->flag = 0; + collpair++; + }/* + else + { + float w1, w2, w3, u1, u2, u3; + float v1[3], v2[3], relativeVelocity[3]; + + // calc relative velocity + + // compute barycentric coordinates for both collision points + collision_compute_barycentric ( collpair->pa, + verts1[collpair->ap1].txold, + verts1[collpair->ap2].txold, + verts1[collpair->ap3].txold, + &w1, &w2, &w3 ); + + // was: txold + collision_compute_barycentric ( collpair->pb, + collmd->current_x[collpair->bp1].co, + collmd->current_x[collpair->bp2].co, + collmd->current_x[collpair->bp3].co, + &u1, &u2, &u3 ); + + // Calculate relative "velocity". + collision_interpolateOnTriangle ( v1, verts1[collpair->ap1].tv, verts1[collpair->ap2].tv, verts1[collpair->ap3].tv, w1, w2, w3 ); + + collision_interpolateOnTriangle ( v2, collmd->current_v[collpair->bp1].co, collmd->current_v[collpair->bp2].co, collmd->current_v[collpair->bp3].co, u1, u2, u3 ); + + VECSUB ( relativeVelocity, v2, v1 ); + + if(sqrt(INPR(relativeVelocity, relativeVelocity)) >= distance) + { + // check for collision in the future + collpair->flag |= COLLISION_IN_FUTURE; + collpair++; + } + }*/ + } + return collpair; +} + +int cloth_collision_response_moving( ClothModifierData *clmd, CollisionModifierData *collmd, CollPair *collpair, CollPair *collision_end ) +{ + int result = 0; + Cloth *cloth1; + float w1, w2, w3, u1, u2, u3; + float v1[3], v2[3], relativeVelocity[3]; + float magrelVel; + + cloth1 = clmd->clothObject; + + for ( ; collpair != collision_end; collpair++ ) + { + // compute barycentric coordinates for both collision points + collision_compute_barycentric ( collpair->pa, + cloth1->verts[collpair->ap1].txold, + cloth1->verts[collpair->ap2].txold, + cloth1->verts[collpair->ap3].txold, + &w1, &w2, &w3 ); + + // was: txold + collision_compute_barycentric ( collpair->pb, + collmd->current_x[collpair->bp1].co, + collmd->current_x[collpair->bp2].co, + collmd->current_x[collpair->bp3].co, + &u1, &u2, &u3 ); + + // Calculate relative "velocity". + collision_interpolateOnTriangle ( v1, cloth1->verts[collpair->ap1].tv, cloth1->verts[collpair->ap2].tv, cloth1->verts[collpair->ap3].tv, w1, w2, w3 ); + + collision_interpolateOnTriangle ( v2, collmd->current_v[collpair->bp1].co, collmd->current_v[collpair->bp2].co, collmd->current_v[collpair->bp3].co, u1, u2, u3 ); + + VECSUB ( relativeVelocity, v2, v1 ); + + // Calculate the normal component of the relative velocity (actually only the magnitude - the direction is stored in 'normal'). + magrelVel = INPR ( relativeVelocity, collpair->normal ); + + // printf("magrelVel: %f\n", magrelVel); + + // Calculate masses of points. + // TODO + + // If v_n_mag < 0 the edges are approaching each other. + if ( magrelVel > ALMOST_ZERO ) + { + // Calculate Impulse magnitude to stop all motion in normal direction. + float magtangent = 0; + double impulse = 0.0; + float vrel_t_pre[3]; + float temp[3]; + + // calculate tangential velocity + VECCOPY ( temp, collpair->normal ); + VecMulf ( temp, magrelVel ); + VECSUB ( vrel_t_pre, relativeVelocity, temp ); + + // Decrease in magnitude of relative tangential velocity due to coulomb friction + // in original formula "magrelVel" should be the "change of relative velocity in normal direction" + magtangent = MIN2 ( clmd->coll_parms->friction * 0.01 * magrelVel,sqrt ( INPR ( vrel_t_pre,vrel_t_pre ) ) ); + + // Apply friction impulse. + if ( magtangent > ALMOST_ZERO ) + { + Normalize ( vrel_t_pre ); + + impulse = 2.0 * magtangent / ( 1.0 + w1*w1 + w2*w2 + w3*w3 ); + VECADDMUL ( cloth1->verts[collpair->ap1].impulse, vrel_t_pre, w1 * impulse ); + VECADDMUL ( cloth1->verts[collpair->ap2].impulse, vrel_t_pre, w2 * impulse ); + VECADDMUL ( cloth1->verts[collpair->ap3].impulse, vrel_t_pre, w3 * impulse ); + } + + // Apply velocity stopping impulse + // I_c = m * v_N / 2.0 + // no 2.0 * magrelVel normally, but looks nicer DG + impulse = magrelVel / ( 1.0 + w1*w1 + w2*w2 + w3*w3 ); + + VECADDMUL ( cloth1->verts[collpair->ap1].impulse, collpair->normal, w1 * impulse ); + cloth1->verts[collpair->ap1].impulse_count++; + + VECADDMUL ( cloth1->verts[collpair->ap2].impulse, collpair->normal, w2 * impulse ); + cloth1->verts[collpair->ap2].impulse_count++; + + VECADDMUL ( cloth1->verts[collpair->ap3].impulse, collpair->normal, w3 * impulse ); + cloth1->verts[collpair->ap3].impulse_count++; + + // Apply repulse impulse if distance too short + // I_r = -min(dt*kd, m(0,1d/dt - v_n)) + /* + d = clmd->coll_parms->epsilon*8.0/9.0 + epsilon2*8.0/9.0 - collpair->distance; + if ( ( magrelVel < 0.1*d*clmd->sim_parms->stepsPerFrame ) && ( d > ALMOST_ZERO ) ) + { + repulse = MIN2 ( d*1.0/clmd->sim_parms->stepsPerFrame, 0.1*d*clmd->sim_parms->stepsPerFrame - magrelVel ); + + // stay on the safe side and clamp repulse + if ( impulse > ALMOST_ZERO ) + repulse = MIN2 ( repulse, 5.0*impulse ); + repulse = MAX2 ( impulse, repulse ); + + impulse = repulse / ( 1.0 + w1*w1 + w2*w2 + w3*w3 ); // original 2.0 / 0.25 + VECADDMUL ( cloth1->verts[collpair->ap1].impulse, collpair->normal, impulse ); + VECADDMUL ( cloth1->verts[collpair->ap2].impulse, collpair->normal, impulse ); + VECADDMUL ( cloth1->verts[collpair->ap3].impulse, collpair->normal, impulse ); + } + */ + result = 1; + } + } + return result; +} + +static float projectPointOntoLine(float *p, float *a, float *b) +{ + float ba[3], pa[3]; + VECSUB(ba, b, a); + VECSUB(pa, p, a); + return INPR(pa, ba) / INPR(ba, ba); +} + +static void calculateEENormal(float *np1, float *np2, float *np3, float *np4,float *out_normal) +{ + float line1[3], line2[3]; + float length; + + VECSUB(line1, np2, np1); + VECSUB(line2, np3, np1); + + // printf("l1: %f, l1: %f, l2: %f, l2: %f\n", line1[0], line1[1], line2[0], line2[1]); + + Crossf(out_normal, line1, line2); + + + + length = Normalize(out_normal); + if (length <= FLT_EPSILON) + { // lines are collinear + VECSUB(out_normal, np2, np1); + Normalize(out_normal); + } +} + +static void findClosestPointsEE(float *x1, float *x2, float *x3, float *x4, float *w1, float *w2) +{ + float temp[3], temp2[3]; + + double a, b, c, e, f; + + VECSUB(temp, x2, x1); + a = INPR(temp, temp); + + VECSUB(temp2, x4, x3); + b = -INPR(temp, temp2); + + c = INPR(temp2, temp2); + + VECSUB(temp2, x3, x1); + e = INPR(temp, temp2); + + VECSUB(temp, x4, x3); + f = -INPR(temp, temp2); + + *w1 = (e * c - b * f) / (a * c - b * b); + *w2 = (f - b * *w1) / c; + +} + +// calculates the distance of 2 edges +float edgedge_distance(float np11[3], float np12[3], float np21[3], float np22[3], float *out_a1, float *out_a2, float *out_normal) +{ + float line1[3], line2[3], cross[3]; + float length; + float temp[3], temp2[3]; + float dist_a1, dist_a2; + + VECSUB(line1, np12, np11); + VECSUB(line2, np22, np21); + + Crossf(cross, line1, line2); + length = INPR(cross, cross); + + if (length < FLT_EPSILON) + { + *out_a2 = projectPointOntoLine(np11, np21, np22); + if ((*out_a2 >= -FLT_EPSILON) && (*out_a2 <= 1.0 + FLT_EPSILON)) + { + *out_a1 = 0; + calculateEENormal(np11, np12, np21, np22, out_normal); + VECSUB(temp, np22, np21); + VecMulf(temp, *out_a2); + VECADD(temp2, temp, np21); + VECADD(temp2, temp2, np11); + return INPR(temp2, temp2); + } + + CLAMP(*out_a2, 0.0, 1.0); + if (*out_a2 > .5) + { // == 1.0 + *out_a1 = projectPointOntoLine(np22, np11, np12); + if ((*out_a1 >= -FLT_EPSILON) && (*out_a1 <= 1.0 + FLT_EPSILON)) + { + calculateEENormal(np11, np12, np21, np22, out_normal); + + // return (np22 - (np11 + (np12 - np11) * out_a1)).lengthSquared(); + VECSUB(temp, np12, np11); + VecMulf(temp, *out_a1); + VECADD(temp2, temp, np11); + VECSUB(temp2, np22, temp2); + return INPR(temp2, temp2); + } + } + else + { // == 0.0 + *out_a1 = projectPointOntoLine(np21, np11, np12); + if ((*out_a1 >= -FLT_EPSILON) && (*out_a1 <= 1.0 + FLT_EPSILON)) + { + calculateEENormal(np11, np11, np21, np22, out_normal); + + // return (np21 - (np11 + (np12 - np11) * out_a1)).lengthSquared(); + VECSUB(temp, np12, np11); + VecMulf(temp, *out_a1); + VECADD(temp2, temp, np11); + VECSUB(temp2, np21, temp2); + return INPR(temp2, temp2); + } + } + + CLAMP(*out_a1, 0.0, 1.0); + calculateEENormal(np11, np12, np21, np22, out_normal); + if(*out_a1 > .5) + { + if(*out_a2 > .5) + { + VECSUB(temp, np12, np22); + } + else + { + VECSUB(temp, np12, np21); + } + } + else + { + if(*out_a2 > .5) + { + VECSUB(temp, np11, np22); + } + else + { + VECSUB(temp, np11, np21); + } + } + + return INPR(temp, temp); + } + else + { + + // If the lines aren't parallel (but coplanar) they have to intersect + + findClosestPointsEE(np11, np12, np21, np22, out_a1, out_a2); + + // If both points are on the finite edges, we're done. + if (*out_a1 >= 0.0 && *out_a1 <= 1.0 && *out_a2 >= 0.0 && *out_a2 <= 1.0) + { + float p1[3], p2[3]; + + // p1= np11 + (np12 - np11) * out_a1; + VECSUB(temp, np12, np11); + VecMulf(temp, *out_a1); + VECADD(p1, np11, temp); + + // p2 = np21 + (np22 - np21) * out_a2; + VECSUB(temp, np22, np21); + VecMulf(temp, *out_a2); + VECADD(p2, np21, temp); + + calculateEENormal(np11, np12, np21, np22, out_normal); + VECSUB(temp, p1, p2); + return INPR(temp, temp); + } + + + /* + * Clamp both points to the finite edges. + * The one that moves most during clamping is one part of the solution. + */ + dist_a1 = *out_a1; + CLAMP(dist_a1, 0.0, 1.0); + dist_a2 = *out_a2; + CLAMP(dist_a2, 0.0, 1.0); + + // Now project the "most clamped" point on the other line. + if (dist_a1 > dist_a2) + { + /* keep out_a1 */ + float p1[3]; + + // p1 = np11 + (np12 - np11) * out_a1; + VECSUB(temp, np12, np11); + VecMulf(temp, *out_a1); + VECADD(p1, np11, temp); + + *out_a2 = projectPointOntoLine(p1, np21, np22); + CLAMP(*out_a2, 0.0, 1.0); + + calculateEENormal(np11, np12, np21, np22, out_normal); + + // return (p1 - (np21 + (np22 - np21) * out_a2)).lengthSquared(); + VECSUB(temp, np22, np21); + VecMulf(temp, *out_a2); + VECADD(temp, temp, np21); + VECSUB(temp, p1, temp); + return INPR(temp, temp); + } + else + { + /* keep out_a2 */ + float p2[3]; + + // p2 = np21 + (np22 - np21) * out_a2; + VECSUB(temp, np22, np21); + VecMulf(temp, *out_a2); + VECADD(p2, np21, temp); + + *out_a1 = projectPointOntoLine(p2, np11, np12); + CLAMP(*out_a1, 0.0, 1.0); + + calculateEENormal(np11, np12, np21, np22, out_normal); + + // return ((np11 + (np12 - np11) * out_a1) - p2).lengthSquared(); + VECSUB(temp, np12, np11); + VecMulf(temp, *out_a1); + VECADD(temp, temp, np11); + VECSUB(temp, temp, p2); + return INPR(temp, temp); + } + } + + printf("Error in edgedge_distance: end of function\n"); + return 0; +} + +int cloth_collision_moving_edges ( ClothModifierData *clmd, CollisionModifierData *collmd, CollPair *collpair ) +{ + EdgeCollPair edgecollpair; + Cloth *cloth1=NULL; + ClothVertex *verts1=NULL; + unsigned int i = 0, k = 0; + int numsolutions = 0; + double x1[3], v1[3], x2[3], v2[3], x3[3], v3[3]; + double solution[3], solution2[3]; + MVert *verts2 = collmd->current_x; // old x + MVert *velocity2 = collmd->current_v; // velocity + float distance = 0; + float triA[3][3], triB[3][3]; + int result = 0; + + cloth1 = clmd->clothObject; + verts1 = cloth1->verts; + + for(i = 0; i < 9; i++) + { + // 9 edge - edge possibilities + + if(i == 0) // cloth edge: 1-2; coll edge: 1-2 + { + edgecollpair.p11 = collpair->ap1; + edgecollpair.p12 = collpair->ap2; + + edgecollpair.p21 = collpair->bp1; + edgecollpair.p22 = collpair->bp2; + } + else if(i == 1) // cloth edge: 1-2; coll edge: 2-3 + { + edgecollpair.p11 = collpair->ap1; + edgecollpair.p12 = collpair->ap2; + + edgecollpair.p21 = collpair->bp2; + edgecollpair.p22 = collpair->bp3; + } + else if(i == 2) // cloth edge: 1-2; coll edge: 1-3 + { + edgecollpair.p11 = collpair->ap1; + edgecollpair.p12 = collpair->ap2; + + edgecollpair.p21 = collpair->bp1; + edgecollpair.p22 = collpair->bp3; + } + else if(i == 3) // cloth edge: 2-3; coll edge: 1-2 + { + edgecollpair.p11 = collpair->ap2; + edgecollpair.p12 = collpair->ap3; + + edgecollpair.p21 = collpair->bp1; + edgecollpair.p22 = collpair->bp2; + } + else if(i == 4) // cloth edge: 2-3; coll edge: 2-3 + { + edgecollpair.p11 = collpair->ap2; + edgecollpair.p12 = collpair->ap3; + + edgecollpair.p21 = collpair->bp2; + edgecollpair.p22 = collpair->bp3; + } + else if(i == 5) // cloth edge: 2-3; coll edge: 1-3 + { + edgecollpair.p11 = collpair->ap2; + edgecollpair.p12 = collpair->ap3; + + edgecollpair.p21 = collpair->bp1; + edgecollpair.p22 = collpair->bp3; + } + else if(i ==6) // cloth edge: 1-3; coll edge: 1-2 + { + edgecollpair.p11 = collpair->ap1; + edgecollpair.p12 = collpair->ap3; + + edgecollpair.p21 = collpair->bp1; + edgecollpair.p22 = collpair->bp2; + } + else if(i ==7) // cloth edge: 1-3; coll edge: 2-3 + { + edgecollpair.p11 = collpair->ap1; + edgecollpair.p12 = collpair->ap3; + + edgecollpair.p21 = collpair->bp2; + edgecollpair.p22 = collpair->bp3; + } + else if(i == 8) // cloth edge: 1-3; coll edge: 1-3 + { + edgecollpair.p11 = collpair->ap1; + edgecollpair.p12 = collpair->ap3; + + edgecollpair.p21 = collpair->bp1; + edgecollpair.p22 = collpair->bp3; + } + /* + if((edgecollpair.p11 == 3) && (edgecollpair.p12 == 16)) + printf("Ahier!\n"); + if((edgecollpair.p11 == 16) && (edgecollpair.p12 == 3)) + printf("Ahier!\n"); + */ + + // if ( !cloth_are_edges_adjacent ( clmd, collmd, &edgecollpair ) ) + { + // always put coll points in p21/p22 + VECSUB ( x1, verts1[edgecollpair.p12].txold, verts1[edgecollpair.p11].txold ); + VECSUB ( v1, verts1[edgecollpair.p12].tv, verts1[edgecollpair.p11].tv ); + + VECSUB ( x2, verts2[edgecollpair.p21].co, verts1[edgecollpair.p11].txold ); + VECSUB ( v2, velocity2[edgecollpair.p21].co, verts1[edgecollpair.p11].tv ); + + VECSUB ( x3, verts2[edgecollpair.p22].co, verts1[edgecollpair.p11].txold ); + VECSUB ( v3, velocity2[edgecollpair.p22].co, verts1[edgecollpair.p11].tv ); + + numsolutions = cloth_get_collision_time ( x1, v1, x2, v2, x3, v3, solution ); + + if((edgecollpair.p11 == 3 && edgecollpair.p12==16)|| (edgecollpair.p11==16 && edgecollpair.p12==3)) + { + if(edgecollpair.p21==6 || edgecollpair.p22 == 6) + { + printf("dist: %f, sol[k]: %lf, sol2[k]: %lf\n", distance, solution[k], solution2[k]); + printf("a1: %f, a2: %f, b1: %f, b2: %f\n", x1[0], x2[0], x3[0], v1[0]); + printf("b21: %d, b22: %d\n", edgecollpair.p21, edgecollpair.p22); + } + } + + for ( k = 0; k < numsolutions; k++ ) + { + // printf("sol %d: %lf\n", k, solution[k]); + if ( ( solution[k] >= ALMOST_ZERO ) && ( solution[k] <= 1.0 ) && ( solution[k] > ALMOST_ZERO)) + { + float a,b; + float out_normal[3]; + float distance; + float impulse = 0; + float I_mag; + + // move verts + VECADDS(triA[0], verts1[edgecollpair.p11].txold, verts1[edgecollpair.p11].tv, solution[k]); + VECADDS(triA[1], verts1[edgecollpair.p12].txold, verts1[edgecollpair.p12].tv, solution[k]); + + VECADDS(triB[0], collmd->current_x[edgecollpair.p21].co, collmd->current_v[edgecollpair.p21].co, solution[k]); + VECADDS(triB[1], collmd->current_x[edgecollpair.p22].co, collmd->current_v[edgecollpair.p22].co, solution[k]); + + // TODO: check for collisions + distance = edgedge_distance(triA[0], triA[1], triB[0], triB[1], &a, &b, out_normal); + + if ((distance <= clmd->coll_parms->epsilon + BLI_bvhtree_getepsilon ( collmd->bvhtree ) + ALMOST_ZERO) && (INPR(out_normal, out_normal) > 0)) + { + float vrel_1_to_2[3], temp[3], temp2[3], out_normalVelocity; + float desiredVn; + + VECCOPY(vrel_1_to_2, verts1[edgecollpair.p11].tv); + VecMulf(vrel_1_to_2, 1.0 - a); + VECCOPY(temp, verts1[edgecollpair.p12].tv); + VecMulf(temp, a); + + VECADD(vrel_1_to_2, vrel_1_to_2, temp); + + VECCOPY(temp, verts1[edgecollpair.p21].tv); + VecMulf(temp, 1.0 - b); + VECCOPY(temp2, verts1[edgecollpair.p22].tv); + VecMulf(temp2, b); + VECADD(temp, temp, temp2); + + VECSUB(vrel_1_to_2, vrel_1_to_2, temp); + + out_normalVelocity = INPR(vrel_1_to_2, out_normal); +/* + // this correction results in wrong normals sometimes? + if(out_normalVelocity < 0.0) + { + out_normalVelocity*= -1.0; + VecMulf(out_normal, -1.0); + } +*/ + /* Inelastic repulsion impulse. */ + + // Calculate which normal velocity we need. + desiredVn = (out_normalVelocity * (float)solution[k] - (.1 * (clmd->coll_parms->epsilon + BLI_bvhtree_getepsilon ( collmd->bvhtree )) - sqrt(distance)) - ALMOST_ZERO); + + // Now calculate what impulse we need to reach that velocity. + I_mag = (out_normalVelocity - desiredVn) / 2.0; // / (1/m1 + 1/m2); + + // Finally apply that impulse. + impulse = (2.0 * -I_mag) / (a*a + (1.0-a)*(1.0-a) + b*b + (1.0-b)*(1.0-b)); + + VECADDMUL ( verts1[edgecollpair.p11].impulse, out_normal, (1.0-a) * impulse ); + verts1[edgecollpair.p11].impulse_count++; + + VECADDMUL ( verts1[edgecollpair.p12].impulse, out_normal, a * impulse ); + verts1[edgecollpair.p12].impulse_count++; + + // return true; + result = 1; + break; + } + else + { + // missing from collision.hpp + } + // mintime = MIN2(mintime, (float)solution[k]); + + break; + } + } + } + } + return result; +} + +int cloth_collision_moving ( ClothModifierData *clmd, CollisionModifierData *collmd, CollPair *collpair, CollPair *collision_end ) +{ + Cloth *cloth1; + cloth1 = clmd->clothObject; + + for ( ; collpair != collision_end; collpair++ ) + { + // only handle moving collisions here + if (!( collpair->flag & COLLISION_IN_FUTURE )) + continue; + + cloth_collision_moving_edges ( clmd, collmd, collpair); + // cloth_collision_moving_tris ( clmd, collmd, collpair); + } + + return 1; +} + + +// return all collision objects in scene +// collision object will exclude self +CollisionModifierData **get_collisionobjects(Object *self, int *numcollobj) +{ + Base *base=NULL; + CollisionModifierData **objs = NULL; + Object *coll_ob = NULL; + CollisionModifierData *collmd = NULL; + int numobj = 0, maxobj = 100; + + objs = MEM_callocN(sizeof(CollisionModifierData *)*maxobj, "CollisionObjectsArray"); + // check all collision objects + for ( base = G.scene->base.first; base; base = base->next ) + { + /*Only proceed for mesh object in same layer */ + if(!(base->object->type==OB_MESH && (base->lay & self->lay))) + continue; + + coll_ob = base->object; + + if(coll_ob == self) + continue; + + if(coll_ob->pd && coll_ob->pd->deflect) + { + collmd = ( CollisionModifierData * ) modifiers_findByType ( coll_ob, eModifierType_Collision ); + } + else + collmd = NULL; + + if ( collmd ) + { + if(numobj >= maxobj) + { + // realloc + int oldmax = maxobj; + CollisionModifierData **tmp; + maxobj *= 2; + tmp = MEM_callocN(sizeof(CollisionModifierData *)*maxobj, "CollisionObjectsArray"); + memcpy(tmp, objs, sizeof(CollisionModifierData *)*oldmax); + MEM_freeN(objs); + objs = tmp; + + } + + objs[numobj] = collmd; + numobj++; + } + else + { + if ( coll_ob->dup_group ) + { + GroupObject *go; + Group *group = coll_ob->dup_group; + + for ( go= group->gobject.first; go; go= go->next ) + { + coll_ob = go->ob; + collmd = NULL; + + if(coll_ob == self) + continue; + + if(coll_ob->pd && coll_ob->pd->deflect) + { + collmd = ( CollisionModifierData * ) modifiers_findByType ( coll_ob, eModifierType_Collision ); + } + else + collmd = NULL; + + if ( !collmd ) + continue; + + if( !collmd->bvhtree) + continue; + + if(numobj >= maxobj) + { + // realloc + int oldmax = maxobj; + CollisionModifierData **tmp; + maxobj *= 2; + tmp = MEM_callocN(sizeof(CollisionModifierData *)*maxobj, "CollisionObjectsArray"); + memcpy(tmp, objs, sizeof(CollisionModifierData *)*oldmax); + MEM_freeN(objs); + objs = tmp; + } + + objs[numobj] = collmd; + numobj++; + } + } + } + } + *numcollobj = numobj; + return objs; +} + +void cloth_bvh_objcollisions_nearcheck ( ClothModifierData * clmd, CollisionModifierData *collmd, CollPair **collisions, CollPair **collisions_index, int numresult, BVHTreeOverlap *overlap) +{ + int i; + + *collisions = ( CollPair* ) MEM_mallocN ( sizeof ( CollPair ) * numresult * 4, "collision array" ); //*4 since cloth_collision_static can return more than 1 collision + *collisions_index = *collisions; + + for ( i = 0; i < numresult; i++ ) + { + *collisions_index = cloth_collision ( ( ModifierData * ) clmd, ( ModifierData * ) collmd, overlap+i, *collisions_index ); + } +} + +int cloth_bvh_objcollisions_resolve ( ClothModifierData * clmd, CollisionModifierData *collmd, CollPair *collisions, CollPair *collisions_index) +{ + Cloth *cloth = clmd->clothObject; + int i=0, j = 0, numfaces = 0, numverts = 0; + ClothVertex *verts = NULL; + int ret = 0; + int result = 0; + float tnull[3] = {0,0,0}; + + numfaces = clmd->clothObject->numfaces; + numverts = clmd->clothObject->numverts; + + verts = cloth->verts; + + // process all collisions (calculate impulses, TODO: also repulses if distance too short) + result = 1; + for ( j = 0; j < 5; j++ ) // 5 is just a value that ensures convergence + { + result = 0; + + if ( collmd->bvhtree ) + { + result += cloth_collision_response_static ( clmd, collmd, collisions, collisions_index ); + + // apply impulses in parallel + if ( result ) + { + for ( i = 0; i < numverts; i++ ) + { + // calculate "velocities" (just xnew = xold + v; no dt in v) + if ( verts[i].impulse_count ) + { + VECADDMUL ( verts[i].tv, verts[i].impulse, 1.0f / verts[i].impulse_count ); + VECCOPY ( verts[i].impulse, tnull ); + verts[i].impulse_count = 0; + + ret++; + } + } + } + } + } + return ret; +} + +// cloth - object collisions +int cloth_bvh_objcollision ( Object *ob, ClothModifierData * clmd, float step, float dt ) +{ + Cloth *cloth=NULL; + BVHTree *cloth_bvh=NULL; + int i=0, numfaces = 0, numverts = 0, k, l, j; + int rounds = 0; // result counts applied collisions; ic is for debug output; + ClothVertex *verts = NULL; + int ret = 0, ret2 = 0; + CollisionModifierData **collobjs = NULL; + int numcollobj = 0; + + if ( ( clmd->sim_parms->flags & CLOTH_SIMSETTINGS_FLAG_COLLOBJ ) || ! ( ( ( Cloth * ) clmd->clothObject )->bvhtree ) ) + { + return 0; + } + + cloth = clmd->clothObject; + verts = cloth->verts; + cloth_bvh = ( BVHTree * ) cloth->bvhtree; + numfaces = clmd->clothObject->numfaces; + numverts = clmd->clothObject->numverts; + + //////////////////////////////////////////////////////////// + // static collisions + //////////////////////////////////////////////////////////// + + // update cloth bvh + bvhtree_update_from_cloth ( clmd, 1 ); // 0 means STATIC, 1 means MOVING (see later in this function) + bvhselftree_update_from_cloth ( clmd, 0 ); // 0 means STATIC, 1 means MOVING (see later in this function) + + collobjs = get_collisionobjects(ob, &numcollobj); + + if(!collobjs) + return 0; + + do + { + CollPair **collisions, **collisions_index; + + ret2 = 0; + + collisions = MEM_callocN(sizeof(CollPair *) *numcollobj , "CollPair"); + collisions_index = MEM_callocN(sizeof(CollPair *) *numcollobj , "CollPair"); + + // check all collision objects + for(i = 0; i < numcollobj; i++) + { + CollisionModifierData *collmd = collobjs[i]; + BVHTreeOverlap *overlap = NULL; + int result = 0; + + if(!collmd->bvhtree) + continue; + + /* move object to position (step) in time */ + collision_move_object ( collmd, step + dt, step ); + + /* search for overlapping collision pairs */ + overlap = BLI_bvhtree_overlap ( cloth_bvh, collmd->bvhtree, &result ); + + // go to next object if no overlap is there + if(!result || !overlap) + { + if ( overlap ) + MEM_freeN ( overlap ); + continue; + } + + /* check if collisions really happen (costly near check) */ + cloth_bvh_objcollisions_nearcheck ( clmd, collmd, &collisions[i], &collisions_index[i], result, overlap); + + // resolve nearby collisions + ret += cloth_bvh_objcollisions_resolve ( clmd, collmd, collisions[i], collisions_index[i]); + ret2 += ret; + + if ( overlap ) + MEM_freeN ( overlap ); + } + rounds++; + + for(i = 0; i < numcollobj; i++) + { + if ( collisions[i] ) MEM_freeN ( collisions[i] ); + } + + MEM_freeN(collisions); + MEM_freeN(collisions_index); + + //////////////////////////////////////////////////////////// + // update positions + // this is needed for bvh_calc_DOP_hull_moving() [kdop.c] + //////////////////////////////////////////////////////////// + + // verts come from clmd + for ( i = 0; i < numverts; i++ ) + { + if ( clmd->sim_parms->flags & CLOTH_SIMSETTINGS_FLAG_GOAL ) + { + if ( verts [i].flags & CLOTH_VERT_FLAG_PINNED ) + { + continue; + } + } + + VECADD ( verts[i].tx, verts[i].txold, verts[i].tv ); + } + //////////////////////////////////////////////////////////// + + + //////////////////////////////////////////////////////////// + // Test on *simple* selfcollisions + //////////////////////////////////////////////////////////// + if ( clmd->coll_parms->flags & CLOTH_COLLSETTINGS_FLAG_SELF ) + { + for(l = 0; l < clmd->coll_parms->self_loop_count; l++) + { + // TODO: add coll quality rounds again + BVHTreeOverlap *overlap = NULL; + int result = 0; + + // collisions = 1; + verts = cloth->verts; // needed for openMP + + numfaces = clmd->clothObject->numfaces; + numverts = clmd->clothObject->numverts; + + verts = cloth->verts; + + if ( cloth->bvhselftree ) + { + // search for overlapping collision pairs + overlap = BLI_bvhtree_overlap ( cloth->bvhselftree, cloth->bvhselftree, &result ); + + // #pragma omp parallel for private(k, i, j) schedule(static) + for ( k = 0; k < result; k++ ) + { + float temp[3]; + float length = 0; + float mindistance; + + i = overlap[k].indexA; + j = overlap[k].indexB; + + mindistance = clmd->coll_parms->selfepsilon* ( cloth->verts[i].avg_spring_len + cloth->verts[j].avg_spring_len ); + + if ( clmd->sim_parms->flags & CLOTH_SIMSETTINGS_FLAG_GOAL ) + { + if ( ( cloth->verts [i].flags & CLOTH_VERT_FLAG_PINNED ) + && ( cloth->verts [j].flags & CLOTH_VERT_FLAG_PINNED ) ) + { + continue; + } + } + + VECSUB ( temp, verts[i].tx, verts[j].tx ); + + if ( ( ABS ( temp[0] ) > mindistance ) || ( ABS ( temp[1] ) > mindistance ) || ( ABS ( temp[2] ) > mindistance ) ) continue; + + // check for adjacent points (i must be smaller j) + if ( BLI_edgehash_haskey ( cloth->edgehash, MIN2(i, j), MAX2(i, j) ) ) + { + continue; + } + + length = Normalize ( temp ); + + if ( length < mindistance ) + { + float correction = mindistance - length; + + if ( cloth->verts [i].flags & CLOTH_VERT_FLAG_PINNED ) + { + VecMulf ( temp, -correction ); + VECADD ( verts[j].tx, verts[j].tx, temp ); + } + else if ( cloth->verts [j].flags & CLOTH_VERT_FLAG_PINNED ) + { + VecMulf ( temp, correction ); + VECADD ( verts[i].tx, verts[i].tx, temp ); + } + else + { + VecMulf ( temp, -correction*0.5 ); + VECADD ( verts[j].tx, verts[j].tx, temp ); + + VECSUB ( verts[i].tx, verts[i].tx, temp ); + } + ret = 1; + ret2 += ret; + } + else + { + // check for approximated time collisions + } + } + + if ( overlap ) + MEM_freeN ( overlap ); + + } + } + //////////////////////////////////////////////////////////// + + //////////////////////////////////////////////////////////// + // SELFCOLLISIONS: update velocities + //////////////////////////////////////////////////////////// + if ( ret2 ) + { + for ( i = 0; i < cloth->numverts; i++ ) + { + if ( ! ( verts [i].flags & CLOTH_VERT_FLAG_PINNED ) ) + { + VECSUB ( verts[i].tv, verts[i].tx, verts[i].txold ); + } + } + } + //////////////////////////////////////////////////////////// + } + } + while ( ret2 && ( clmd->coll_parms->loop_count>rounds ) ); + + if(collobjs) + MEM_freeN(collobjs); + + return MIN2 ( ret, 1 ); +} diff --git a/source/blender/blenkernel/intern/colortools.c b/source/blender/blenkernel/intern/colortools.c index 83b014cdd63..1bc34aea9a1 100644 --- a/source/blender/blenkernel/intern/colortools.c +++ b/source/blender/blenkernel/intern/colortools.c @@ -167,18 +167,30 @@ void curvemap_remove(CurveMap *cuma, int flag) void curvemap_insert(CurveMap *cuma, float x, float y) { CurveMapPoint *cmp= MEM_callocN((cuma->totpoint+1)*sizeof(CurveMapPoint), "curve points"); - int a; - - memcpy(cmp, cuma->curve, (cuma->totpoint)*sizeof(CurveMapPoint)); + int a, b, foundloc= 0; + + /* insert fragments of the old one and the new point to the new curve */ + cuma->totpoint++; + for(a=0, b=0; a<cuma->totpoint; a++) { + if((x < cuma->curve[a].x) && !foundloc) { + cmp[a].x= x; + cmp[a].y= y; + cmp[a].flag= CUMA_SELECT; + foundloc= 1; + } + else { + cmp[a].x= cuma->curve[b].x; + cmp[a].y= cuma->curve[b].y; + cmp[a].flag= cuma->curve[b].flag; + cmp[a].flag &= ~CUMA_SELECT; /* make sure old points don't remain selected */ + cmp[a].shorty= cuma->curve[b].shorty; + b++; + } + } + + /* free old curve and replace it with new one */ MEM_freeN(cuma->curve); cuma->curve= cmp; - - cuma->curve[cuma->totpoint].x= x; - cuma->curve[cuma->totpoint].y= y; - cuma->curve[cuma->totpoint].flag = CUMA_SELECT; - for(a=0; a<cuma->totpoint; a++, cmp++) - cmp->flag= 0; - cuma->totpoint++; } void curvemap_reset(CurveMap *cuma, rctf *clipr) @@ -580,7 +592,8 @@ float curvemap_evaluateF(CurveMap *cuma, float value) fi= (value-cuma->mintable)*cuma->range; i= (int)fi; - if(fi<0.0f || fi>cuma->range) + /* fi is table float index and should check against table range i.e. [0.0 CM_TABLE] */ + if(fi<0.0f || fi>CM_TABLE) return curvemap_calc_extend(cuma, value, &cuma->table[0].x, &cuma->table[CM_TABLE].x); else { if(i<0) return cuma->table[0].y; @@ -637,7 +650,6 @@ void curvemapping_evaluate_premulRGBF(CurveMapping *cumap, float *vecout, const vecout[2]= curvemap_evaluateF(cumap->cm+2, fac); } -#define FTOCHAR(val) val<=0.0f?0: (val>=1.0f?255: (char)(255.0f*val)) void curvemapping_do_ibuf(CurveMapping *cumap, ImBuf *ibuf) { @@ -717,3 +729,24 @@ void curvemapping_initialize(CurveMapping *cumap) curvemap_make_table(cumap->cm+a, &cumap->clipr); } } + +void curvemapping_table_RGBA(CurveMapping *cumap, float **array, int *size) +{ + int a; + + *size = CM_TABLE+1; + *array = MEM_callocN(sizeof(float)*(*size)*4, "CurveMapping"); + curvemapping_initialize(cumap); + + for(a=0; a<*size; a++) { + if(cumap->cm[0].table) + (*array)[a*4+0]= cumap->cm[0].table[a].y; + if(cumap->cm[1].table) + (*array)[a*4+1]= cumap->cm[1].table[a].y; + if(cumap->cm[2].table) + (*array)[a*4+2]= cumap->cm[2].table[a].y; + if(cumap->cm[3].table) + (*array)[a*4+3]= cumap->cm[3].table[a].y; + } +} + diff --git a/source/blender/blenkernel/intern/constraint.c b/source/blender/blenkernel/intern/constraint.c index 3ed4d134145..b8bfb002075 100644 --- a/source/blender/blenkernel/intern/constraint.c +++ b/source/blender/blenkernel/intern/constraint.c @@ -28,8 +28,10 @@ */ #include <stdio.h> +#include <stddef.h> #include <string.h> #include <math.h> +#include <float.h> #include "MEM_guardedalloc.h" //XXX #include "nla.h" @@ -63,8 +65,9 @@ #include "BKE_library.h" #include "BKE_idprop.h" - +#ifndef DISABLE_PYTHON #include "BPY_extern.h" +#endif #ifdef HAVE_CONFIG_H #include <config.h> @@ -78,10 +81,10 @@ /* ******************* Constraint Channels ********************** */ /* Constraint Channels exist in one of two places: * - Under Action Channels in an Action (act->chanbase->achan->constraintChannels) - * - Under Object without object-level action yet (ob->constraintChannels) + * - Under Object without Object-level Action yet (ob->constraintChannels) * - * The main purpose that constraint channels serve is to act as a link - * between an IPO-block which + * The main purpose that Constraint Channels serve is to act as a link + * between an IPO-block (which provides values to interpolate between for some settings) */ /* ------------ Data Management ----------- */ @@ -137,7 +140,7 @@ bConstraintChannel *get_constraint_channel (ListBase *list, const char name[]) { bConstraintChannel *chan; - if(list) { + if (list) { for (chan = list->first; chan; chan=chan->next) { if (!strcmp(name, chan->name)) { return chan; @@ -177,11 +180,11 @@ void do_constraint_channels (ListBase *conbase, ListBase *chanbase, float ctime, for (con=conbase->first; con; con=con->next) { Ipo *ipo= NULL; - if(con->flag & CONSTRAINT_OWN_IPO) + if (con->flag & CONSTRAINT_OWN_IPO) ipo= con->ipo; else { bConstraintChannel *chan = get_constraint_channel(chanbase, con->name); - if(chan) ipo= chan->ipo; + if (chan) ipo= chan->ipo; } if (ipo) { @@ -200,7 +203,24 @@ void do_constraint_channels (ListBase *conbase, ListBase *chanbase, float ctime, break; case CO_HEADTAIL: { - con->headtail = icu->curval; + /* we need to check types of constraints that can get this here, as user + * may have created an IPO-curve for this from IPO-editor but for a constraint + * that cannot support this + */ + switch (con->type) { + /* supported constraints go here... */ + case CONSTRAINT_TYPE_LOCLIKE: + case CONSTRAINT_TYPE_TRACKTO: + case CONSTRAINT_TYPE_MINMAX: + case CONSTRAINT_TYPE_STRETCHTO: + case CONSTRAINT_TYPE_DISTLIMIT: + con->headtail = icu->curval; + break; + + default: + /* not supported */ + break; + } } break; } @@ -221,55 +241,7 @@ void do_constraint_channels (ListBase *conbase, ListBase *chanbase, float ctime, /* Find the first available, non-duplicate name for a given constraint */ void unique_constraint_name (bConstraint *con, ListBase *list) { - bConstraint *curcon; - char tempname[64]; - int number = 1, exists = 0; - char *dot; - - /* See if we are given an empty string */ - if (con->name[0] == '\0') { - /* give it default name first */ - strcpy(con->name, "Const"); - } - - /* See if we even need to do this */ - if (list == NULL) - return; - - for (curcon = list->first; curcon; curcon=curcon->next) { - if (curcon != con) { - if (!strcmp(curcon->name, con->name)) { - exists = 1; - break; - } - } - } - - if (exists == 0) - return; - - /* Strip off the suffix */ - dot = strchr(con->name, '.'); - if (dot) - *dot=0; - - for (number = 1; number <= 999; number++) { - sprintf(tempname, "%s.%03d", con->name, number); - - exists = 0; - for (curcon=list->first; curcon; curcon=curcon->next) { - if (con != curcon) { - if (strcmp(curcon->name, tempname)==0) { - exists = 1; - break; - } - } - } - if (exists == 0) { - strcpy(con->name, tempname); - return; - } - } + BLI_uniquename(list, con, "Const", offsetof(bConstraint, name), 32); } /* ----------------- Evaluation Loop Preparation --------------- */ @@ -337,7 +309,7 @@ void constraints_clear_evalob (bConstraintOb *cob) /* calculate delta of constraints evaluation */ Mat4Invert(imat, cob->startmat); - Mat4MulMat4(delta, cob->matrix, imat); + Mat4MulMat4(delta, imat, cob->matrix); /* copy matrices back to source */ switch (cob->type) { @@ -395,19 +367,12 @@ void constraint_mat_convertspace (Object *ob, bPoseChannel *pchan, float mat[][4 case CONSTRAINT_SPACE_WORLD: /* ---------- FROM WORLDSPACE ---------- */ { /* world to pose */ - if (to==CONSTRAINT_SPACE_POSE || to==CONSTRAINT_SPACE_LOCAL || to==CONSTRAINT_SPACE_PARLOCAL) { - Mat4Invert(imat, ob->obmat); - Mat4CpyMat4(tempmat, mat); - Mat4MulMat4(mat, tempmat, imat); - } + Mat4Invert(imat, ob->obmat); + Mat4CpyMat4(tempmat, mat); + Mat4MulMat4(mat, tempmat, imat); - /* pose to local */ - if (to == CONSTRAINT_SPACE_LOCAL) { - /* call self with slightly different values */ - constraint_mat_convertspace(ob, pchan, mat, CONSTRAINT_SPACE_POSE, to); - } - /* pose to local + parent */ - else if (to == CONSTRAINT_SPACE_PARLOCAL) { + /* use pose-space as stepping stone for other spaces... */ + if (ELEM(to, CONSTRAINT_SPACE_LOCAL, CONSTRAINT_SPACE_PARLOCAL)) { /* call self with slightly different values */ constraint_mat_convertspace(ob, pchan, mat, CONSTRAINT_SPACE_POSE, to); } @@ -473,69 +438,66 @@ void constraint_mat_convertspace (Object *ob, bPoseChannel *pchan, float mat[][4 break; case CONSTRAINT_SPACE_LOCAL: /* ------------ FROM LOCALSPACE --------- */ { - /* local to pose */ - if (to==CONSTRAINT_SPACE_POSE || to==CONSTRAINT_SPACE_WORLD) { - /* do inverse procedure that was done for pose to local */ - if (pchan->bone) { - /* we need the posespace_matrix = local_matrix + (parent_posespace_matrix + restpos) */ - if (pchan->parent) { - float offs_bone[4][4]; - - /* construct offs_bone the same way it is done in armature.c */ - Mat4CpyMat3(offs_bone, pchan->bone->bone_mat); - VECCOPY(offs_bone[3], pchan->bone->head); - offs_bone[3][1]+= pchan->bone->parent->length; + /* local to pose - do inverse procedure that was done for pose to local */ + if (pchan->bone) { + /* we need the posespace_matrix = local_matrix + (parent_posespace_matrix + restpos) */ + if (pchan->parent) { + float offs_bone[4][4]; + + /* construct offs_bone the same way it is done in armature.c */ + Mat4CpyMat3(offs_bone, pchan->bone->bone_mat); + VECCOPY(offs_bone[3], pchan->bone->head); + offs_bone[3][1]+= pchan->bone->parent->length; + + if (pchan->bone->flag & BONE_HINGE) { + /* pose_mat = par_pose-space_location * chan_mat */ + float tmat[4][4]; - if (pchan->bone->flag & BONE_HINGE) { - /* pose_mat = par_pose-space_location * chan_mat */ - float tmat[4][4]; - - /* the rotation of the parent restposition */ - Mat4CpyMat4(tmat, pchan->bone->parent->arm_mat); - - /* the location of actual parent transform */ - VECCOPY(tmat[3], offs_bone[3]); - offs_bone[3][0]= offs_bone[3][1]= offs_bone[3][2]= 0.0f; - Mat4MulVecfl(pchan->parent->pose_mat, tmat[3]); - - Mat4MulMat4(diff_mat, offs_bone, tmat); - Mat4CpyMat4(tempmat, mat); - Mat4MulMat4(mat, tempmat, diff_mat); - } - else { - /* pose_mat = par_pose_mat * bone_mat * chan_mat */ - Mat4MulMat4(diff_mat, offs_bone, pchan->parent->pose_mat); - Mat4CpyMat4(tempmat, mat); - Mat4MulMat4(mat, tempmat, diff_mat); - } + /* the rotation of the parent restposition */ + Mat4CpyMat4(tmat, pchan->bone->parent->arm_mat); + + /* the location of actual parent transform */ + VECCOPY(tmat[3], offs_bone[3]); + offs_bone[3][0]= offs_bone[3][1]= offs_bone[3][2]= 0.0f; + Mat4MulVecfl(pchan->parent->pose_mat, tmat[3]); + + Mat4MulMat4(diff_mat, offs_bone, tmat); + Mat4CpyMat4(tempmat, mat); + Mat4MulMat4(mat, tempmat, diff_mat); } else { - Mat4CpyMat4(diff_mat, pchan->bone->arm_mat); - + /* pose_mat = par_pose_mat * bone_mat * chan_mat */ + Mat4MulMat4(diff_mat, offs_bone, pchan->parent->pose_mat); Mat4CpyMat4(tempmat, mat); Mat4MulMat4(mat, tempmat, diff_mat); } } + else { + Mat4CpyMat4(diff_mat, pchan->bone->arm_mat); + + Mat4CpyMat4(tempmat, mat); + Mat4MulMat4(mat, tempmat, diff_mat); + } } - /* local to world */ - if (to == CONSTRAINT_SPACE_WORLD) { + + /* use pose-space as stepping stone for other spaces */ + if (ELEM(to, CONSTRAINT_SPACE_WORLD, CONSTRAINT_SPACE_PARLOCAL)) { /* call self with slightly different values */ constraint_mat_convertspace(ob, pchan, mat, CONSTRAINT_SPACE_POSE, to); - } + } } break; case CONSTRAINT_SPACE_PARLOCAL: /* -------------- FROM LOCAL WITH PARENT ---------- */ { - /* local to pose */ - if (to==CONSTRAINT_SPACE_POSE || to==CONSTRAINT_SPACE_WORLD) { - if (pchan->bone) { - Mat4CpyMat4(diff_mat, pchan->bone->arm_mat); - Mat4CpyMat4(tempmat, mat); - Mat4MulMat4(mat, diff_mat, tempmat); - } + /* local + parent to pose */ + if (pchan->bone) { + Mat4CpyMat4(diff_mat, pchan->bone->arm_mat); + Mat4CpyMat4(tempmat, mat); + Mat4MulMat4(mat, diff_mat, tempmat); } - /* local to world */ - if (to == CONSTRAINT_SPACE_WORLD) { + + /* use pose-space as stepping stone for other spaces */ + if (ELEM(to, CONSTRAINT_SPACE_WORLD, CONSTRAINT_SPACE_LOCAL)) { /* call self with slightly different values */ constraint_mat_convertspace(ob, pchan, mat, CONSTRAINT_SPACE_POSE, to); } @@ -586,8 +548,8 @@ static void contarget_get_mesh_mat (Object *ob, char *substring, float mat[][4]) if (dgroup < 0) return; /* get DerivedMesh */ - if (G.obedit && G.editMesh) { - /* we are in editmode, so get a special derived mesh */ + if ((G.obedit == ob) && (G.editMesh)) { + /* target is in editmode, so get a special derived mesh */ dm = CDDM_from_editmesh(G.editMesh, ob->data); } else { @@ -880,7 +842,7 @@ static void default_get_tarmat (bConstraint *con, bConstraintOb *cob, bConstrain if (nocopy == 0) { \ datatar= ct->tar; \ strcpy(datasubtarget, ct->subtarget); \ - con->tarspace= ct->space; \ + con->tarspace= (char)ct->space; \ } \ \ BLI_freelinkN(list, ct); \ @@ -900,7 +862,7 @@ static void default_get_tarmat (bConstraint *con, bConstraintOb *cob, bConstrain bConstraintTarget *ctn = ct->next; \ if (nocopy == 0) { \ datatar= ct->tar; \ - con->tarspace= ct->space; \ + con->tarspace= (char)ct->space; \ } \ \ BLI_freelinkN(list, ct); \ @@ -920,7 +882,7 @@ static void childof_new_data (void *cdata) Mat4One(data->invmat); } -static void childof_get_tars (bConstraint *con, ListBase *list) +static int childof_get_tars (bConstraint *con, ListBase *list) { if (con && list) { bChildOfConstraint *data= con->data; @@ -928,7 +890,11 @@ static void childof_get_tars (bConstraint *con, ListBase *list) /* standard target-getting macro for single-target constraints */ SINGLETARGET_GET_TARS(con, data->tar, data->subtarget, ct, list) + + return 1; } + + return 0; } static void childof_flush_tars (bConstraint *con, ListBase *list, short nocopy) @@ -1018,7 +984,7 @@ static void trackto_new_data (void *cdata) data->reserved2 = UP_Z; } -static void trackto_get_tars (bConstraint *con, ListBase *list) +static int trackto_get_tars (bConstraint *con, ListBase *list) { if (con && list) { bTrackToConstraint *data= con->data; @@ -1026,7 +992,11 @@ static void trackto_get_tars (bConstraint *con, ListBase *list) /* standard target-getting macro for single-target constraints */ SINGLETARGET_GET_TARS(con, data->tar, data->subtarget, ct, list) + + return 1; } + + return 0; } static void trackto_flush_tars (bConstraint *con, ListBase *list, short nocopy) @@ -1124,9 +1094,9 @@ static void vectomat (float *vec, float *target_up, short axis, short upflag, sh /* identity matrix - don't do anything if the two axes are the same */ else { m[0][0]= m[1][1]= m[2][2]= 1.0; - m[0][1]= m[0][2]= m[0][3]= 0.0; - m[1][0]= m[1][2]= m[1][3]= 0.0; - m[2][0]= m[2][1]= m[2][3]= 0.0; + m[0][1]= m[0][2]= 0.0; + m[1][0]= m[1][2]= 0.0; + m[2][0]= m[2][1]= 0.0; } } @@ -1195,16 +1165,20 @@ static void kinematic_new_data (void *cdata) data->flag= CONSTRAINT_IK_TIP|CONSTRAINT_IK_STRETCH|CONSTRAINT_IK_POS; } -static void kinematic_get_tars (bConstraint *con, ListBase *list) +static int kinematic_get_tars (bConstraint *con, ListBase *list) { if (con && list) { bKinematicConstraint *data= con->data; bConstraintTarget *ct; - /* standard target-getting macro for single-target constraints */ + /* standard target-getting macro for single-target constraints is used twice here */ SINGLETARGET_GET_TARS(con, data->tar, data->subtarget, ct, list) SINGLETARGET_GET_TARS(con, data->poletar, data->polesubtarget, ct, list) + + return 2; } + + return 0; } static void kinematic_flush_tars (bConstraint *con, ListBase *list, short nocopy) @@ -1273,7 +1247,7 @@ static void followpath_new_data (void *cdata) data->followflag = 0; } -static void followpath_get_tars (bConstraint *con, ListBase *list) +static int followpath_get_tars (bConstraint *con, ListBase *list) { if (con && list) { bFollowPathConstraint *data= con->data; @@ -1281,7 +1255,11 @@ static void followpath_get_tars (bConstraint *con, ListBase *list) /* standard target-getting macro for single-target constraints without subtargets */ SINGLETARGETNS_GET_TARS(con, data->tar, ct, list) + + return 1; } + + return 0; } static void followpath_flush_tars (bConstraint *con, ListBase *list, short nocopy) @@ -1301,7 +1279,7 @@ static void followpath_get_tarmat (bConstraint *con, bConstraintOb *cob, bConstr if (VALID_CONS_TARGET(ct)) { Curve *cu= ct->tar->data; - float q[4], vec[4], dir[3], *quat, x1; + float q[4], vec[4], dir[3], quat[4], x1; float totmat[4][4]; float curvetime; @@ -1326,7 +1304,7 @@ static void followpath_get_tarmat (bConstraint *con, bConstraintOb *cob, bConstr if ( where_on_path(ct->tar, curvetime, vec, dir) ) { if (data->followflag) { - quat= vectoquat(dir, (short) data->trackflag, (short) data->upflag); + vectoquat(dir, (short) data->trackflag, (short) data->upflag, quat); Normalize(dir); q[0]= (float)cos(0.5*vec[3]); @@ -1456,9 +1434,9 @@ static void rotlimit_evaluate (bConstraint *con, bConstraintOb *cob, ListBase *t Mat4ToEul(cob->matrix, eul); /* eulers: radians to degrees! */ - eul[0] = (eul[0] / M_PI * 180); - eul[1] = (eul[1] / M_PI * 180); - eul[2] = (eul[2] / M_PI * 180); + eul[0] = (float)(eul[0] / M_PI * 180); + eul[1] = (float)(eul[1] / M_PI * 180); + eul[2] = (float)(eul[2] / M_PI * 180); /* limiting of euler values... */ if (data->flag & LIMIT_XROT) { @@ -1484,9 +1462,9 @@ static void rotlimit_evaluate (bConstraint *con, bConstraintOb *cob, ListBase *t } /* eulers: degrees to radians ! */ - eul[0] = (eul[0] / 180 * M_PI); - eul[1] = (eul[1] / 180 * M_PI); - eul[2] = (eul[2] / 180 * M_PI); + eul[0] = (float)(eul[0] / 180 * M_PI); + eul[1] = (float)(eul[1] / 180 * M_PI); + eul[2] = (float)(eul[2] / 180 * M_PI); LocEulSizeToMat4(cob->matrix, loc, eul, size); } @@ -1574,7 +1552,7 @@ static void loclike_new_data (void *cdata) data->flag = LOCLIKE_X|LOCLIKE_Y|LOCLIKE_Z; } -static void loclike_get_tars (bConstraint *con, ListBase *list) +static int loclike_get_tars (bConstraint *con, ListBase *list) { if (con && list) { bLocateLikeConstraint *data= con->data; @@ -1582,7 +1560,11 @@ static void loclike_get_tars (bConstraint *con, ListBase *list) /* standard target-getting macro for single-target constraints */ SINGLETARGET_GET_TARS(con, data->tar, data->subtarget, ct, list) + + return 1; } + + return 0; } static void loclike_flush_tars (bConstraint *con, ListBase *list, short nocopy) @@ -1652,7 +1634,7 @@ static void rotlike_new_data (void *cdata) data->flag = ROTLIKE_X|ROTLIKE_Y|ROTLIKE_Z; } -static void rotlike_get_tars (bConstraint *con, ListBase *list) +static int rotlike_get_tars (bConstraint *con, ListBase *list) { if (con && list) { bRotateLikeConstraint *data= con->data; @@ -1660,7 +1642,11 @@ static void rotlike_get_tars (bConstraint *con, ListBase *list) /* standard target-getting macro for single-target constraints */ SINGLETARGET_GET_TARS(con, data->tar, data->subtarget, ct, list) + + return 1; } + + return 0; } static void rotlike_flush_tars (bConstraint *con, ListBase *list, short nocopy) @@ -1749,7 +1735,7 @@ static void sizelike_new_data (void *cdata) data->flag = SIZELIKE_X|SIZELIKE_Y|SIZELIKE_Z; } -static void sizelike_get_tars (bConstraint *con, ListBase *list) +static int sizelike_get_tars (bConstraint *con, ListBase *list) { if (con && list) { bSizeLikeConstraint *data= con->data; @@ -1757,7 +1743,11 @@ static void sizelike_get_tars (bConstraint *con, ListBase *list) /* standard target-getting macro for single-target constraints */ SINGLETARGET_GET_TARS(con, data->tar, data->subtarget, ct, list) + + return 1; } + + return 0; } static void sizelike_flush_tars (bConstraint *con, ListBase *list, short nocopy) @@ -1824,6 +1814,7 @@ static bConstraintTypeInfo CTI_SIZELIKE = { sizelike_evaluate /* evaluate */ }; + /* ----------- Python Constraint -------------- */ static void pycon_free (bConstraint *con) @@ -1863,14 +1854,18 @@ static void pycon_new_data (void *cdata) data->prop->type = IDP_GROUP; } -static void pycon_get_tars (bConstraint *con, ListBase *list) +static int pycon_get_tars (bConstraint *con, ListBase *list) { if (con && list) { bPythonConstraint *data= con->data; list->first = data->targets.first; list->last = data->targets.last; + + return data->tarnum; } + + return 0; } /* Whether this approach is maintained remains to be seen (aligorith) */ @@ -1892,7 +1887,12 @@ static void pycon_get_tarmat (bConstraint *con, bConstraintOb *cob, bConstraintT * this matrix if it needs to do so */ constraint_target_to_mat4(ct->tar, ct->subtarget, ct->matrix, CONSTRAINT_SPACE_WORLD, ct->space, con->headtail); - BPY_pyconstraint_target(data, ct); + + /* only execute target calculation if allowed */ +#ifndef DISABLE_PYTHON + if (G.f & G_DOSCRIPTLINKS) + BPY_pyconstraint_target(data, ct); +#endif } else if (ct) Mat4One(ct->matrix); @@ -1900,8 +1900,14 @@ static void pycon_get_tarmat (bConstraint *con, bConstraintOb *cob, bConstraintT static void pycon_evaluate (bConstraint *con, bConstraintOb *cob, ListBase *targets) { +#ifdef DISABLE_PYTHON + return; +#else bPythonConstraint *data= con->data; + /* only evaluate in python if we're allowed to do so */ + if ((G.f & G_DOSCRIPTLINKS)==0) return; + /* currently removed, until I this can be re-implemented for multiple targets */ #if 0 /* Firstly, run the 'driver' function which has direct access to the objects involved @@ -1913,6 +1919,7 @@ static void pycon_evaluate (bConstraint *con, bConstraintOb *cob, ListBase *targ /* Now, run the actual 'constraint' function, which should only access the matrices */ BPY_pyconstraint_eval(data, cob, targets); +#endif /* DISABLE_PYTHON */ } static bConstraintTypeInfo CTI_PYTHON = { @@ -1946,7 +1953,7 @@ static void actcon_new_data (void *cdata) data->type = 20; } -static void actcon_get_tars (bConstraint *con, ListBase *list) +static int actcon_get_tars (bConstraint *con, ListBase *list) { if (con && list) { bActionConstraint *data= con->data; @@ -1954,7 +1961,11 @@ static void actcon_get_tars (bConstraint *con, ListBase *list) /* standard target-getting macro for single-target constraints */ SINGLETARGET_GET_TARS(con, data->tar, data->subtarget, ct, list) + + return 1; } + + return 0; } static void actcon_flush_tars (bConstraint *con, ListBase *list, short nocopy) @@ -2031,8 +2042,7 @@ static void actcon_get_tarmat (bConstraint *con, bConstraintOb *cob, bConstraint Mat4CpyMat4(ct->matrix, tchan->chan_mat); /* Clean up */ - free_pose_channels(pose); - MEM_freeN(pose); + free_pose(pose); } else if (cob->type == CONSTRAINT_OBTYPE_OBJECT) { /* evaluate using workob */ @@ -2086,7 +2096,7 @@ static void locktrack_new_data (void *cdata) data->lockflag = LOCK_Z; } -static void locktrack_get_tars (bConstraint *con, ListBase *list) +static int locktrack_get_tars (bConstraint *con, ListBase *list) { if (con && list) { bLockTrackConstraint *data= con->data; @@ -2094,7 +2104,11 @@ static void locktrack_get_tars (bConstraint *con, ListBase *list) /* the following macro is used for all standard single-target constraints */ SINGLETARGET_GET_TARS(con, data->tar, data->subtarget, ct, list) + + return 1; } + + return 0; } static void locktrack_flush_tars (bConstraint *con, ListBase *list, short nocopy) @@ -2425,6 +2439,122 @@ static bConstraintTypeInfo CTI_LOCKTRACK = { locktrack_evaluate /* evaluate */ }; +/* ---------- Limit Distance Constraint ----------- */ + +static void distlimit_new_data (void *cdata) +{ + bDistLimitConstraint *data= (bDistLimitConstraint *)cdata; + + data->dist= 0.0; +} + +static int distlimit_get_tars (bConstraint *con, ListBase *list) +{ + if (con && list) { + bDistLimitConstraint *data= con->data; + bConstraintTarget *ct; + + /* standard target-getting macro for single-target constraints */ + SINGLETARGET_GET_TARS(con, data->tar, data->subtarget, ct, list) + + return 1; + } + + return 0; +} + +static void distlimit_flush_tars (bConstraint *con, ListBase *list, short nocopy) +{ + if (con && list) { + bDistLimitConstraint *data= con->data; + bConstraintTarget *ct= list->first; + + /* the following macro is used for all standard single-target constraints */ + SINGLETARGET_FLUSH_TARS(con, data->tar, data->subtarget, ct, list, nocopy) + } +} + +static void distlimit_evaluate (bConstraint *con, bConstraintOb *cob, ListBase *targets) +{ + bDistLimitConstraint *data= con->data; + bConstraintTarget *ct= targets->first; + + /* only evaluate if there is a target */ + if (VALID_CONS_TARGET(ct)) { + float dvec[3], dist=0.0f, sfac=1.0f; + short clamp_surf= 0; + + /* calculate our current distance from the target */ + dist= VecLenf(cob->matrix[3], ct->matrix[3]); + + /* set distance (flag is only set when user demands it) */ + if (data->dist == 0) + data->dist= dist; + + /* check if we're which way to clamp from, and calculate interpolation factor (if needed) */ + if (data->mode == LIMITDIST_OUTSIDE) { + /* if inside, then move to surface */ + if (dist <= data->dist) { + clamp_surf= 1; + sfac= data->dist / dist; + } + /* if soft-distance is enabled, start fading once owner is dist+softdist from the target */ + else if (data->flag & LIMITDIST_USESOFT) { + if (dist <= (data->dist + data->soft)) { + + } + } + } + else if (data->mode == LIMITDIST_INSIDE) { + /* if outside, then move to surface */ + if (dist >= data->dist) { + clamp_surf= 1; + sfac= data->dist / dist; + } + /* if soft-distance is enabled, start fading once owner is dist-soft from the target */ + else if (data->flag & LIMITDIST_USESOFT) { + // FIXME: there's a problem with "jumping" when this kicks in + if (dist >= (data->dist - data->soft)) { + sfac = (float)( data->soft*(1.0 - exp(-(dist - data->dist)/data->soft)) + data->dist ); + sfac /= dist; + + clamp_surf= 1; + } + } + } + else { + if (IS_EQ(dist, data->dist)==0) { + clamp_surf= 1; + sfac= data->dist / dist; + } + } + + /* clamp to 'surface' (i.e. move owner so that dist == data->dist) */ + if (clamp_surf) { + /* simply interpolate along line formed by target -> owner */ + VecLerpf(dvec, ct->matrix[3], cob->matrix[3], sfac); + + /* copy new vector onto owner */ + VECCOPY(cob->matrix[3], dvec); + } + } +} + +static bConstraintTypeInfo CTI_DISTLIMIT = { + CONSTRAINT_TYPE_DISTLIMIT, /* type */ + sizeof(bDistLimitConstraint), /* size */ + "Limit Distance", /* name */ + "bDistLimitConstraint", /* struct name */ + NULL, /* free data */ + NULL, /* relink data */ + NULL, /* copy data */ + distlimit_new_data, /* new data */ + distlimit_get_tars, /* get constraint targets */ + distlimit_flush_tars, /* flush constraint targets */ + default_get_tarmat, /* get a target matrix */ + distlimit_evaluate /* evaluate */ +}; + /* ---------- Stretch To ------------ */ static void stretchto_new_data (void *cdata) @@ -2437,7 +2567,7 @@ static void stretchto_new_data (void *cdata) data->bulge = 1.0; } -static void stretchto_get_tars (bConstraint *con, ListBase *list) +static int stretchto_get_tars (bConstraint *con, ListBase *list) { if (con && list) { bStretchToConstraint *data= con->data; @@ -2445,7 +2575,11 @@ static void stretchto_get_tars (bConstraint *con, ListBase *list) /* standard target-getting macro for single-target constraints */ SINGLETARGET_GET_TARS(con, data->tar, data->subtarget, ct, list) + + return 1; } + + return 0; } static void stretchto_flush_tars (bConstraint *con, ListBase *list, short nocopy) @@ -2613,7 +2747,7 @@ static void minmax_new_data (void *cdata) data->flag = 0; } -static void minmax_get_tars (bConstraint *con, ListBase *list) +static int minmax_get_tars (bConstraint *con, ListBase *list) { if (con && list) { bMinMaxConstraint *data= con->data; @@ -2621,7 +2755,11 @@ static void minmax_get_tars (bConstraint *con, ListBase *list) /* standard target-getting macro for single-target constraints */ SINGLETARGET_GET_TARS(con, data->tar, data->subtarget, ct, list) + + return 1; } + + return 0; } static void minmax_flush_tars (bConstraint *con, ListBase *list, short nocopy) @@ -2743,7 +2881,7 @@ static void rbj_new_data (void *cdata) data->type=1; } -static void rbj_get_tars (bConstraint *con, ListBase *list) +static int rbj_get_tars (bConstraint *con, ListBase *list) { if (con && list) { bRigidBodyJointConstraint *data= con->data; @@ -2751,7 +2889,11 @@ static void rbj_get_tars (bConstraint *con, ListBase *list) /* standard target-getting macro for single-target constraints without subtargets */ SINGLETARGETNS_GET_TARS(con, data->tar, ct, list) + + return 1; } + + return 0; } static void rbj_flush_tars (bConstraint *con, ListBase *list, short nocopy) @@ -2782,7 +2924,7 @@ static bConstraintTypeInfo CTI_RIGIDBODYJOINT = { /* -------- Clamp To ---------- */ -static void clampto_get_tars (bConstraint *con, ListBase *list) +static int clampto_get_tars (bConstraint *con, ListBase *list) { if (con && list) { bClampToConstraint *data= con->data; @@ -2790,7 +2932,11 @@ static void clampto_get_tars (bConstraint *con, ListBase *list) /* standard target-getting macro for single-target constraints without subtargets */ SINGLETARGETNS_GET_TARS(con, data->tar, ct, list) + + return 1; } + + return 0; } static void clampto_flush_tars (bConstraint *con, ListBase *list, short nocopy) @@ -2955,7 +3101,7 @@ static void transform_new_data (void *cdata) data->map[2]= 2; } -static void transform_get_tars (bConstraint *con, ListBase *list) +static int transform_get_tars (bConstraint *con, ListBase *list) { if (con && list) { bTransformConstraint *data= con->data; @@ -2963,7 +3109,11 @@ static void transform_get_tars (bConstraint *con, ListBase *list) /* standard target-getting macro for single-target constraints */ SINGLETARGET_GET_TARS(con, data->tar, data->subtarget, ct, list) + + return 1; } + + return 0; } static void transform_flush_tars (bConstraint *con, ListBase *list, short nocopy) @@ -2990,11 +3140,13 @@ static void transform_evaluate (bConstraint *con, bConstraintOb *cob, ListBase * /* obtain target effect */ switch (data->from) { - case 2: /* scale */ + case 2: /* scale */ Mat4ToSize(ct->matrix, dvec); break; - case 1: /* rotation */ + case 1: /* rotation (convert to degrees first) */ Mat4ToEul(ct->matrix, dvec); + for (i=0; i<3; i++) + dvec[i] = (float)(dvec[i] / M_PI * 180); break; default: /* location */ VecCopyf(dvec, ct->matrix[3]); @@ -3004,7 +3156,7 @@ static void transform_evaluate (bConstraint *con, bConstraintOb *cob, ListBase * /* extract components of owner's matrix */ VECCOPY(loc, cob->matrix[3]); Mat4ToEul(cob->matrix, eul); - Mat4ToSize(cob->matrix, size); + Mat4ToSize(cob->matrix, size); /* determine where in range current transforms lie */ if (data->expo) { @@ -3026,17 +3178,6 @@ static void transform_evaluate (bConstraint *con, bConstraintOb *cob, ListBase * } } - /* convert radian<->degree */ - if (data->from==1 && data->to==0) { - /* from radians to degrees */ - for (i=0; i<3; i++) - sval[i] = sval[i] / M_PI * 180; - } - else if (data->from==0 && data->to==1) { - /* from degrees to radians */ - for (i=0; i<3; i++) - sval[i] = sval[i] / 180 * M_PI; - } /* apply transforms */ switch (data->to) { @@ -3048,11 +3189,14 @@ static void transform_evaluate (bConstraint *con, bConstraintOb *cob, ListBase * for (i=0; i<3; i++) { float tmin, tmax; - /* convert destination min/max ranges from degrees to radians */ - tmin= data->to_min[i] / M_PI * 180; - tmax= data->to_max[i] / M_PI * 180; + tmin= data->to_min[i]; + tmax= data->to_max[i]; + /* all values here should be in degrees */ eul[i]= tmin + (sval[data->map[i]] * (tmax - tmin)); + + /* now convert final value back to radians */ + eul[i] = (float)(eul[i] / 180 * M_PI); } break; default: /* location */ @@ -3110,12 +3254,12 @@ static void constraints_init_typeinfo () { constraintsTypeInfo[11]= &CTI_PYTHON; /* Python/Script Constraint */ constraintsTypeInfo[12]= &CTI_ACTION; /* Action Constraint */ constraintsTypeInfo[13]= &CTI_LOCKTRACK; /* Locked-Track Constraint */ - constraintsTypeInfo[14]= NULL; /* 'Distance Limit' Constraint */ + constraintsTypeInfo[14]= &CTI_DISTLIMIT; /* Limit Distance Constraint */ constraintsTypeInfo[15]= &CTI_STRETCHTO; /* StretchTo Constaint */ constraintsTypeInfo[16]= &CTI_MINMAX; /* Floor Constraint */ constraintsTypeInfo[17]= &CTI_RIGIDBODYJOINT; /* RigidBody Constraint */ constraintsTypeInfo[18]= &CTI_CLAMPTO; /* ClampTo Constraint */ - constraintsTypeInfo[19]= &CTI_TRANSFORM; /* Transformation Constraint */ + constraintsTypeInfo[19]= &CTI_TRANSFORM; /* Transformation Constraint */ } /* This function should be used for getting the appropriate type-info when only @@ -3241,6 +3385,46 @@ void copy_constraints (ListBase *dst, ListBase *src) } } +/* -------- Constraints and Proxies ------- */ + +/* Rescue all constraints tagged as being CONSTRAINT_PROXY_LOCAL (i.e. added to bone that's proxy-synced in this file) */ +void extract_proxylocal_constraints (ListBase *dst, ListBase *src) +{ + bConstraint *con, *next; + + /* for each tagged constraint, remove from src and move to dst */ + for (con= src->first; con; con= next) { + next= con->next; + + /* check if tagged */ + if (con->flag & CONSTRAINT_PROXY_LOCAL) { + BLI_remlink(src, con); + BLI_addtail(dst, con); + } + } +} + +/* Returns if the owner of the constraint is proxy-protected */ +short proxylocked_constraints_owner (Object *ob, bPoseChannel *pchan) +{ + /* Currently, constraints can only be on object or bone level */ + if (ob && ob->proxy) { + if (ob->pose && pchan) { + bArmature *arm= ob->data; + + /* On bone-level, check if bone is on proxy-protected layer */ + if ((pchan->bone) && (pchan->bone->layer & arm->layer_protected)) + return 1; + } + else { + /* FIXME: constraints on object-level are not handled well yet */ + return 1; + } + } + + return 0; +} + /* -------- Target-Matrix Stuff ------- */ /* This function is a relic from the prior implementations of the constraints system, when all diff --git a/source/blender/blenkernel/intern/curve.c b/source/blender/blenkernel/intern/curve.c index 90e65b629b4..1a671dfe771 100644 --- a/source/blender/blenkernel/intern/curve.c +++ b/source/blender/blenkernel/intern/curve.c @@ -247,7 +247,7 @@ void tex_space_curve(Curve *cu) { DispList *dl; BoundBox *bb; - float *data, min[3], max[3], loc[3], size[3]; + float *fp, min[3], max[3], loc[3], size[3]; int tot, doit= 0; if(cu->bb==NULL) cu->bb= MEM_callocN(sizeof(BoundBox), "boundbox"); @@ -262,10 +262,10 @@ void tex_space_curve(Curve *cu) else tot= dl->nr*dl->parts; if(tot) doit= 1; - data= dl->verts; + fp= dl->verts; while(tot--) { - DO_MINMAX(data, min, max); - data+= 3; + DO_MINMAX(fp, min, max); + fp += 3; } dl= dl->next; } @@ -348,9 +348,9 @@ void freeNurb(Nurb *nu) if(nu->bp) MEM_freeN(nu->bp); nu->bp= 0; if(nu->knotsu) MEM_freeN(nu->knotsu); - nu->knotsu= 0; + nu->knotsu= NULL; if(nu->knotsv) MEM_freeN(nu->knotsv); - nu->knotsv= 0; + nu->knotsv= NULL; /* if(nu->trim.first) freeNurblist(&(nu->trim)); */ MEM_freeN(nu); @@ -393,7 +393,7 @@ Nurb *duplicateNurb(Nurb *nu) (BPoint*)MEM_mallocN((len)* sizeof(BPoint),"duplicateNurb3"); memcpy(newnu->bp, nu->bp, len*sizeof(BPoint)); - newnu->knotsu=newnu->knotsv= 0; + newnu->knotsu= newnu->knotsv= NULL; if(nu->knotsu) { len= KNOTSU(nu); @@ -506,6 +506,7 @@ static void calcknots(float *knots, short aantal, short order, short type) } } else if(type==2) { + /* Warning, the order MUST be 2 or 4, if this is not enforced, the displist will be corrupt */ if(order==4) { k= 0.34; for(a=0;a<t;a++) { @@ -520,6 +521,9 @@ static void calcknots(float *knots, short aantal, short order, short type) knots[a]= (float)floor(k); } } + else { + printf("bez nurb curve order is not 3 or 4, should never happen\n"); + } } } @@ -529,7 +533,8 @@ static void makecyclicknots(float *knots, short pnts, short order) int a, b, order2, c; if(knots==0) return; - order2=order-1; + + order2=order-1; /* do first long rows (order -1), remove identical knots at endpoints */ if(order>2) { @@ -549,26 +554,35 @@ static void makecyclicknots(float *knots, short pnts, short order) } -void makeknots(Nurb *nu, short uv, short type) /* 0: uniform, 1: endpoints, 2: bezier */ +/* type - 0: uniform, 1: endpoints, 2: bezier, note, cyclic nurbs are always uniform */ +void makeknots(Nurb *nu, short uv, short type) { if( (nu->type & 7)==CU_NURBS ) { - if(uv & 1) { + if(uv == 1) { if(nu->knotsu) MEM_freeN(nu->knotsu); - if(nu->pntsu>1) { + if(check_valid_nurb_u(nu)) { nu->knotsu= MEM_callocN(4+sizeof(float)*KNOTSU(nu), "makeknots"); - calcknots(nu->knotsu, nu->pntsu, nu->orderu, type); - if(nu->flagu & 1) makecyclicknots(nu->knotsu, nu->pntsu, nu->orderu); + if(nu->flagu & CU_CYCLIC) { + calcknots(nu->knotsu, nu->pntsu, nu->orderu, 0); /* cyclic should be uniform */ + makecyclicknots(nu->knotsu, nu->pntsu, nu->orderu); + } else { + calcknots(nu->knotsu, nu->pntsu, nu->orderu, type); + } } - else nu->knotsu= 0; - } - if(uv & 2) { + else nu->knotsu= NULL; + + } else if(uv == 2) { if(nu->knotsv) MEM_freeN(nu->knotsv); - if(nu->pntsv>1) { + if(check_valid_nurb_v(nu)) { nu->knotsv= MEM_callocN(4+sizeof(float)*KNOTSV(nu), "makeknots"); - calcknots(nu->knotsv, nu->pntsv, nu->orderv, type); - if(nu->flagv & 1) makecyclicknots(nu->knotsv, nu->pntsv, nu->orderv); + if(nu->flagv & CU_CYCLIC) { + calcknots(nu->knotsv, nu->pntsv, nu->orderv, 0); /* cyclic should be uniform */ + makecyclicknots(nu->knotsv, nu->pntsv, nu->orderv); + } else { + calcknots(nu->knotsv, nu->pntsv, nu->orderv, type); + } } - else nu->knotsv= 0; + else nu->knotsv= NULL; } } } @@ -636,31 +650,31 @@ static void basisNurb(float t, short order, short pnts, float *knots, float *bas } -void makeNurbfaces(Nurb *nu, float *data, int rowstride) -/* data has to be 3*4*resolu*resolv in size, and zero-ed */ +void makeNurbfaces(Nurb *nu, float *coord_array, int rowstride) +/* coord_array has to be 3*4*resolu*resolv in size, and zero-ed */ { BPoint *bp; float *basisu, *basis, *basisv, *sum, *fp, *in; float u, v, ustart, uend, ustep, vstart, vend, vstep, sumdiv; int i, j, iofs, jofs, cycl, len, resolu, resolv; int istart, iend, jsta, jen, *jstart, *jend, ratcomp; - - if(nu->knotsu==0 || nu->knotsv==0) return; + + int totu = nu->pntsu*nu->resolu, totv = nu->pntsv*nu->resolv; + + if(nu->knotsu==NULL || nu->knotsv==NULL) return; if(nu->orderu>nu->pntsu) return; if(nu->orderv>nu->pntsv) return; - if(data==0) return; - + if(coord_array==NULL) return; + /* allocate and initialize */ - len= nu->pntsu*nu->pntsv; + len = totu * totv; if(len==0) return; sum= (float *)MEM_callocN(sizeof(float)*len, "makeNurbfaces1"); - - resolu= nu->resolu; - resolv= nu->resolv; - len= resolu*resolv; + + len= totu*totv; if(len==0) { MEM_freeN(sum); return; @@ -676,46 +690,50 @@ void makeNurbfaces(Nurb *nu, float *data, int rowstride) } bp++; } - + fp= nu->knotsu; ustart= fp[nu->orderu-1]; - if(nu->flagu & 1) uend= fp[nu->pntsu+nu->orderu-1]; + if(nu->flagu & CU_CYCLIC) uend= fp[nu->pntsu+nu->orderu-1]; else uend= fp[nu->pntsu]; - ustep= (uend-ustart)/(resolu-1+(nu->flagu & 1)); + ustep= (uend-ustart)/((nu->flagu & CU_CYCLIC) ? totu : totu - 1); + basisu= (float *)MEM_mallocN(sizeof(float)*KNOTSU(nu), "makeNurbfaces3"); fp= nu->knotsv; vstart= fp[nu->orderv-1]; - if(nu->flagv & 1) vend= fp[nu->pntsv+nu->orderv-1]; + if(nu->flagv & CU_CYCLIC) vend= fp[nu->pntsv+nu->orderv-1]; else vend= fp[nu->pntsv]; - vstep= (vend-vstart)/(resolv-1+(nu->flagv & 1)); + vstep= (vend-vstart)/((nu->flagv & CU_CYCLIC) ? totv : totv - 1); + len= KNOTSV(nu); - basisv= (float *)MEM_mallocN(sizeof(float)*len*resolv, "makeNurbfaces3"); - jstart= (int *)MEM_mallocN(sizeof(float)*resolv, "makeNurbfaces4"); - jend= (int *)MEM_mallocN(sizeof(float)*resolv, "makeNurbfaces5"); + basisv= (float *)MEM_mallocN(sizeof(float)*len*totv, "makeNurbfaces3"); + jstart= (int *)MEM_mallocN(sizeof(float)*totv, "makeNurbfaces4"); + jend= (int *)MEM_mallocN(sizeof(float)*totv, "makeNurbfaces5"); /* precalculation of basisv and jstart,jend */ - if(nu->flagv & 1) cycl= nu->orderv-1; + if(nu->flagv & CU_CYCLIC) cycl= nu->orderv-1; else cycl= 0; v= vstart; basis= basisv; + resolv= totv; while(resolv--) { basisNurb(v, nu->orderv, (short)(nu->pntsv+cycl), nu->knotsv, basis, jstart+resolv, jend+resolv); basis+= KNOTSV(nu); v+= vstep; } - if(nu->flagu & 1) cycl= nu->orderu-1; + if(nu->flagu & CU_CYCLIC) cycl= nu->orderu-1; else cycl= 0; - in= data; + in= coord_array; u= ustart; + resolu= totu; while(resolu--) { basisNurb(u, nu->orderu, (short)(nu->pntsu+cycl), nu->knotsu, basisu, &istart, &iend); basis= basisv; - resolv= nu->resolv; + resolv= totv; while(resolv--) { jsta= jstart[resolv]; @@ -784,7 +802,7 @@ void makeNurbfaces(Nurb *nu, float *data, int rowstride) basis+= KNOTSV(nu); } u+= ustep; - if (rowstride!=0) in = (float*) (((unsigned char*) in) + (rowstride - 3*nu->resolv*sizeof(*in))); + if (rowstride!=0) in = (float*) (((unsigned char*) in) + (rowstride - 3*totv*sizeof(*in))); } /* free */ @@ -795,24 +813,28 @@ void makeNurbfaces(Nurb *nu, float *data, int rowstride) MEM_freeN(jend); } -void makeNurbcurve(Nurb *nu, float *data, int resolu, int dim) -/* data has to be dim*4*pntsu*resolu in size and zero-ed */ +void makeNurbcurve(Nurb *nu, float *coord_array, float *tilt_array, float *radius_array, int resolu) +/* coord_array has to be 3*4*pntsu*resolu in size and zero-ed + * tilt_array and radius_array will be written to if valid */ { BPoint *bp; float u, ustart, uend, ustep, sumdiv; - float *basisu, *sum, *fp, *in; + float *basisu, *sum, *fp; + float *coord_fp= coord_array, *tilt_fp= tilt_array, *radius_fp= radius_array; int i, len, istart, iend, cycl; - if(nu->knotsu==0) return; + if(nu->knotsu==NULL) return; if(nu->orderu>nu->pntsu) return; - if(data==0) return; + if(coord_array==0) return; /* allocate and initialize */ len= nu->pntsu; if(len==0) return; sum= (float *)MEM_callocN(sizeof(float)*len, "makeNurbcurve1"); - - resolu*= nu->pntsu; + + resolu= (resolu*SEGMENTSU(nu)); + if((nu->flagu & CU_CYCLIC)==0) resolu++; + if(resolu==0) { MEM_freeN(sum); return; @@ -820,15 +842,15 @@ void makeNurbcurve(Nurb *nu, float *data, int resolu, int dim) fp= nu->knotsu; ustart= fp[nu->orderu-1]; - if(nu->flagu & 1) uend= fp[nu->pntsu+nu->orderu-1]; + if(nu->flagu & CU_CYCLIC) uend= fp[nu->pntsu+nu->orderu-1]; else uend= fp[nu->pntsu]; - ustep= (uend-ustart)/(resolu-1+(nu->flagu & 1)); + ustep= (uend-ustart)/(resolu - ((nu->flagu & CU_CYCLIC) ? 0 : 1)); + basisu= (float *)MEM_mallocN(sizeof(float)*KNOTSU(nu), "makeNurbcurve3"); - if(nu->flagu & 1) cycl= nu->orderu-1; + if(nu->flagu & CU_CYCLIC) cycl= nu->orderu-1; else cycl= 0; - in= data; u= ustart; while(resolu--) { @@ -863,17 +885,24 @@ void makeNurbcurve(Nurb *nu, float *data, int resolu, int dim) if(*fp!=0.0) { - in[0]+= (*fp) * bp->vec[0]; - in[1]+= (*fp) * bp->vec[1]; - if(dim>=3) { - in[2]+= (*fp) * bp->vec[2]; - if(dim==4) in[3]+= (*fp) * bp->alfa; - } + coord_fp[0]+= (*fp) * bp->vec[0]; + coord_fp[1]+= (*fp) * bp->vec[1]; + coord_fp[2]+= (*fp) * bp->vec[2]; + + if (tilt_fp) + (*tilt_fp) += (*fp) * bp->alfa; + + if (radius_fp) + (*radius_fp) += (*fp) * bp->radius; + } } - in+= dim; - + coord_fp+= 3; + + if (tilt_fp) tilt_fp++; + if (radius_fp) radius_fp++; + u+= ustep; } @@ -918,7 +947,7 @@ float *make_orco_surf(Object *ob) Nurb *nu; int a, b, tot=0; int sizeu, sizev; - float *data, *orco; + float *fp, *coord_array; /* first calculate the size of the datablock */ nu= cu->nurb.first; @@ -932,8 +961,8 @@ float *make_orco_surf(Object *ob) See also convertblender.c: init_render_surf() */ - sizeu = nu->resolu; - sizev = nu->resolv; + sizeu = nu->pntsu*nu->resolu; + sizev = nu->pntsv*nu->resolv; if (nu->flagu & CU_CYCLIC) sizeu++; if (nu->flagv & CU_CYCLIC) sizev++; if(nu->pntsv>1) tot+= sizeu * sizev; @@ -941,13 +970,13 @@ float *make_orco_surf(Object *ob) nu= nu->next; } /* makeNurbfaces wants zeros */ - data= orco= MEM_callocN(3*sizeof(float)*tot, "make_orco"); + fp= coord_array= MEM_callocN(3*sizeof(float)*tot, "make_orco"); nu= cu->nurb.first; while(nu) { if(nu->pntsv>1) { - sizeu = nu->resolu; - sizev = nu->resolv; + sizeu = nu->pntsu*nu->resolu; + sizev = nu->pntsv*nu->resolv; if (nu->flagu & CU_CYCLIC) sizeu++; if (nu->flagv & CU_CYCLIC) sizev++; @@ -955,20 +984,20 @@ float *make_orco_surf(Object *ob) for(b=0; b< sizeu; b++) { for(a=0; a< sizev; a++) { - if(sizev <2) data[0]= 0.0f; - else data[0]= -1.0f + 2.0f*((float)a)/(sizev - 1); + if(sizev <2) fp[0]= 0.0f; + else fp[0]= -1.0f + 2.0f*((float)a)/(sizev - 1); - if(sizeu <2) data[1]= 0.0f; - else data[1]= -1.0f + 2.0f*((float)b)/(sizeu - 1); + if(sizeu <2) fp[1]= 0.0f; + else fp[1]= -1.0f + 2.0f*((float)b)/(sizeu - 1); - data[2]= 0.0; + fp[2]= 0.0; - data+= 3; + fp+= 3; } } } else { - float *_tdata= MEM_callocN(nu->resolu*nu->resolv*3*sizeof(float), "temp data"); + float *_tdata= MEM_callocN((nu->pntsu*nu->resolu) * (nu->pntsv*nu->resolv) *3*sizeof(float), "temp data"); float *tdata= _tdata; makeNurbfaces(nu, tdata, 0); @@ -983,12 +1012,12 @@ float *make_orco_surf(Object *ob) if (a==sizev-1 && (nu->flagv & CU_CYCLIC)) use_a= 0; - tdata = _tdata + 3 * (use_b * nu->resolv + use_a); + tdata = _tdata + 3 * (use_b * (nu->pntsv*nu->resolv) + use_a); - data[0]= (tdata[0]-cu->loc[0])/cu->size[0]; - data[1]= (tdata[1]-cu->loc[1])/cu->size[1]; - data[2]= (tdata[2]-cu->loc[2])/cu->size[2]; - data+= 3; + fp[0]= (tdata[0]-cu->loc[0])/cu->size[0]; + fp[1]= (tdata[1]-cu->loc[1])/cu->size[1]; + fp[2]= (tdata[2]-cu->loc[2])/cu->size[2]; + fp+= 3; } } @@ -998,7 +1027,7 @@ float *make_orco_surf(Object *ob) nu= nu->next; } - return orco; + return coord_array; } @@ -1010,7 +1039,7 @@ float *make_orco_curve(Object *ob) Curve *cu = ob->data; DispList *dl; int u, v, numVerts; - float *fp, *orco; + float *fp, *coord_array; int remakeDisp = 0; if (!(cu->flag&CU_UV_ORCO) && cu->key && cu->key->refkey) { @@ -1038,7 +1067,7 @@ float *make_orco_curve(Object *ob) } } - fp= orco= MEM_mallocN(3*sizeof(float)*numVerts, "cu_orco"); + fp= coord_array= MEM_mallocN(3*sizeof(float)*numVerts, "cu_orco"); for (dl=cu->disp.first; dl; dl=dl->next) { if (dl->type==DL_INDEX3) { for (u=0; u<dl->nr; u++, fp+=3) { @@ -1071,9 +1100,12 @@ float *make_orco_curve(Object *ob) fp[1]= 2.0f*v/(dl->nr-1) - 1.0f; fp[2]= 0.0; } else { + float *vert; int realv= v % dl->nr; - - VECCOPY(fp, &dl->verts[(dl->nr*u + realv)*3]); + int realu= u % dl->parts; + + vert= dl->verts + 3*(dl->nr*realu + realv); + VECCOPY(fp, vert); fp[0]= (fp[0]-cu->loc[0])/cu->size[0]; fp[1]= (fp[1]-cu->loc[1])/cu->size[1]; @@ -1088,7 +1120,7 @@ float *make_orco_curve(Object *ob) makeDispListCurveTypes(ob, 0); } - return orco; + return coord_array; } @@ -1415,7 +1447,7 @@ static void calc_bevel_sin_cos(float x1, float y1, float x2, float y2, float *si } -static void alfa_bezpart(BezTriple *prevbezt, BezTriple *bezt, Nurb *nu, float *data_a, int resolu) +static void alfa_bezpart(BezTriple *prevbezt, BezTriple *bezt, Nurb *nu, float *tilt_array, float *radius_array, int resolu) { BezTriple *pprev, *next, *last; float fac, dfac, t[4]; @@ -1425,14 +1457,14 @@ static void alfa_bezpart(BezTriple *prevbezt, BezTriple *bezt, Nurb *nu, float * /* returns a point */ if(prevbezt==nu->bezt) { - if(nu->flagu & 1) pprev= last; + if(nu->flagu & CU_CYCLIC) pprev= last; else pprev= prevbezt; } else pprev= prevbezt-1; /* next point */ if(bezt==last) { - if(nu->flagu & 1) next= nu->bezt; + if(nu->flagu & CU_CYCLIC) next= nu->bezt; else next= bezt; } else next= bezt+1; @@ -1441,10 +1473,30 @@ static void alfa_bezpart(BezTriple *prevbezt, BezTriple *bezt, Nurb *nu, float * dfac= 1.0f/(float)resolu; for(a=0; a<resolu; a++, fac+= dfac) { + if (tilt_array) { + if (nu->tilt_interp==3) { /* May as well support for tilt also 2.47 ease interp */ + tilt_array[a] = prevbezt->alfa + (bezt->alfa - prevbezt->alfa)*(3.0f*fac*fac - 2.0f*fac*fac*fac); + } else { + set_four_ipo(fac, t, nu->tilt_interp); + tilt_array[a]= t[0]*pprev->alfa + t[1]*prevbezt->alfa + t[2]*bezt->alfa + t[3]*next->alfa; + } + } - set_four_ipo(fac, t, nu->tilt_interp); - - data_a[a]= t[0]*pprev->alfa + t[1]*prevbezt->alfa + t[2]*bezt->alfa + t[3]*next->alfa; + if (radius_array) { + if (nu->radius_interp==3) { + /* Support 2.47 ease interp + * Note! - this only takes the 2 points into account, + * giving much more localized results to changes in radius, sometimes you want that */ + radius_array[a] = prevbezt->radius + (bezt->radius - prevbezt->radius)*(3.0f*fac*fac - 2.0f*fac*fac*fac); + } else { + + /* reuse interpolation from tilt if we can */ + if (tilt_array==NULL || nu->tilt_interp != nu->radius_interp) { + set_four_ipo(fac, t, nu->radius_interp); + } + radius_array[a]= t[0]*pprev->radius + t[1]*prevbezt->radius + t[2]*bezt->radius + t[3]*next->radius; + } + } } } @@ -1462,13 +1514,19 @@ void makeBevelList(Object *ob) BPoint *bp; BevList *bl, *blnew, *blnext; BevPoint *bevp, *bevp2, *bevp1 = NULL, *bevp0; - float *data, *data_a, *v1, *v2, min, inp, x1, x2, y1, y2, vec[3]; + float min, inp, x1, x2, y1, y2, vec[3]; + float *coord_array, *tilt_array=NULL, *radius_array=NULL, *coord_fp, *tilt_fp=NULL, *radius_fp=NULL; + float *v1, *v2; struct bevelsort *sortdata, *sd, *sd1; - int a, b, len, nr, poly, resolu; - + int a, b, nr, poly, resolu, len=0; + int do_tilt, do_radius; + /* this function needs an object, because of tflag and upflag */ cu= ob->data; + /* do we need to calculate the radius for each point? */ + /* do_radius = (cu->bevobj || cu->taperobj || (cu->flag & CU_FRONT) || (cu->flag & CU_BACK)) ? 0 : 1; */ + /* STEP 1: MAKE POLYS */ BLI_freelistN(&(cu->bev)); @@ -1476,8 +1534,15 @@ void makeBevelList(Object *ob) else nu= cu->nurb.first; while(nu) { - if(nu->pntsu<=1) { - bl= MEM_callocN(sizeof(BevList)+1*sizeof(BevPoint), "makeBevelList"); + + /* check if we will calculate tilt data */ + do_tilt = ((nu->type & CU_2D) && (cu->flag & CU_3D)==0) ? 0 : 1; + do_radius = (do_tilt || cu->bevobj) ? 1 : 0; /* normal display uses the radius, better just to calculate them */ + + /* check we are a single point? also check we are not a surface and that the orderu is sane, + * enforced in the UI but can go wrong possibly */ + if(!check_valid_nurb_u(nu)) { + bl= MEM_callocN(sizeof(BevList)+1*sizeof(BevPoint), "makeBevelList1"); BLI_addtail(&(cu->bev), bl); bl->nr= 0; } else { @@ -1487,11 +1552,11 @@ void makeBevelList(Object *ob) resolu= nu->resolu; if((nu->type & 7)==CU_POLY) { - - bl= MEM_callocN(sizeof(BevList)+len*sizeof(BevPoint), "makeBevelList"); + len= nu->pntsu; + bl= MEM_callocN(sizeof(BevList)+len*sizeof(BevPoint), "makeBevelList2"); BLI_addtail(&(cu->bev), bl); - if(nu->flagu & 1) bl->poly= 0; + if(nu->flagu & CU_CYCLIC) bl->poly= 0; else bl->poly= -1; bl->nr= len; bl->flag= 0; @@ -1503,24 +1568,25 @@ void makeBevelList(Object *ob) bevp->y= bp->vec[1]; bevp->z= bp->vec[2]; bevp->alfa= bp->alfa; - bevp->f1= 1; + bevp->radius= bp->radius; + bevp->f1= SELECT; bevp++; bp++; } } else if((nu->type & 7)==CU_BEZIER) { - len= resolu*(nu->pntsu+ (nu->flagu & 1) -1)+1; /* in case last point is not cyclic */ - bl= MEM_callocN(sizeof(BevList)+len*sizeof(BevPoint), "makeBevelList"); + len= resolu*(nu->pntsu+ (nu->flagu & CU_CYCLIC) -1)+1; /* in case last point is not cyclic */ + bl= MEM_callocN(sizeof(BevList)+len*sizeof(BevPoint), "makeBevelBPoints"); BLI_addtail(&(cu->bev), bl); - if(nu->flagu & 1) bl->poly= 0; + if(nu->flagu & CU_CYCLIC) bl->poly= 0; else bl->poly= -1; bevp= (BevPoint *)(bl+1); a= nu->pntsu-1; bezt= nu->bezt; - if(nu->flagu & 1) { + if(nu->flagu & CU_CYCLIC) { a++; prevbezt= nu->bezt+(nu->pntsu-1); } @@ -1529,8 +1595,13 @@ void makeBevelList(Object *ob) bezt++; } - data= MEM_mallocN(3*sizeof(float)*(resolu+1), "makeBevelList2"); - data_a= MEM_callocN(sizeof(float)*(resolu+1), "data_a"); + coord_array= coord_fp= MEM_mallocN(3*sizeof(float)*(resolu+1), "makeBevelCoords"); + + if(do_tilt) + tilt_array= tilt_fp= MEM_callocN(sizeof(float)*(resolu+1), "makeBevelTilt"); + + if (do_radius) + radius_array= radius_fp= MEM_callocN(sizeof(float)*(resolu+1), "nakeBevelRadius"); while(a--) { if(prevbezt->h2==HD_VECT && bezt->h1==HD_VECT) { @@ -1539,6 +1610,7 @@ void makeBevelList(Object *ob) bevp->y= prevbezt->vec[1][1]; bevp->z= prevbezt->vec[1][2]; bevp->alfa= prevbezt->alfa; + bevp->radius= prevbezt->radius; bevp->f1= SELECT; bevp->f2= 0; bevp++; @@ -1550,38 +1622,44 @@ void makeBevelList(Object *ob) v2= bezt->vec[0]; /* always do all three, to prevent data hanging around */ - forward_diff_bezier(v1[0], v1[3], v2[0], v2[3], data, resolu, 3); - forward_diff_bezier(v1[1], v1[4], v2[1], v2[4], data+1, resolu, 3); - forward_diff_bezier(v1[2], v1[5], v2[2], v2[5], data+2, resolu, 3); - - if((nu->type & CU_2D)==0) { - if(cu->flag & CU_3D) { - alfa_bezpart(prevbezt, bezt, nu, data_a, resolu); - } - } + forward_diff_bezier(v1[0], v1[3], v2[0], v2[3], coord_array, resolu, 3); + forward_diff_bezier(v1[1], v1[4], v2[1], v2[4], coord_array+1, resolu, 3); + forward_diff_bezier(v1[2], v1[5], v2[2], v2[5], coord_array+2, resolu, 3); + if (do_tilt || do_radius) + alfa_bezpart(prevbezt, bezt, nu, tilt_array, radius_array, resolu); /* indicate with handlecodes double points */ if(prevbezt->h1==prevbezt->h2) { - if(prevbezt->h1==0 || prevbezt->h1==HD_VECT) bevp->f1= 1; + if(prevbezt->h1==0 || prevbezt->h1==HD_VECT) bevp->f1= SELECT; } else { - if(prevbezt->h1==0 || prevbezt->h1==HD_VECT) bevp->f1= 1; - else if(prevbezt->h2==0 || prevbezt->h2==HD_VECT) bevp->f1= 1; + if(prevbezt->h1==0 || prevbezt->h1==HD_VECT) bevp->f1= SELECT; + else if(prevbezt->h2==0 || prevbezt->h2==HD_VECT) bevp->f1= SELECT; } - v1= data; - v2= data_a; nr= resolu; + coord_fp = coord_array; + tilt_fp = tilt_array; + radius_fp = radius_array; + while(nr--) { - bevp->x= v1[0]; - bevp->y= v1[1]; - bevp->z= v1[2]; - bevp->alfa= v2[0]; + bevp->x= coord_fp[0]; + bevp->y= coord_fp[1]; + bevp->z= coord_fp[2]; + coord_fp+=3; + + if (do_tilt) { + bevp->alfa= *tilt_fp; + tilt_fp++; + } + + if (do_radius) { + bevp->radius= *radius_fp; + radius_fp++; + } bevp++; - v1+=3; - v2++; } bl->nr+= resolu; @@ -1590,44 +1668,67 @@ void makeBevelList(Object *ob) bezt++; } - MEM_freeN(data); - MEM_freeN(data_a); + MEM_freeN(coord_array); + if (do_tilt) MEM_freeN(tilt_array); + if (do_radius) MEM_freeN(radius_array); + coord_array = tilt_array = radius_array = NULL; - if((nu->flagu & 1)==0) { /* not cyclic: endpoint */ + if((nu->flagu & CU_CYCLIC)==0) { /* not cyclic: endpoint */ bevp->x= prevbezt->vec[1][0]; bevp->y= prevbezt->vec[1][1]; bevp->z= prevbezt->vec[1][2]; bevp->alfa= prevbezt->alfa; + bevp->radius= prevbezt->radius; bl->nr++; } - } else if((nu->type & 7)==CU_NURBS) { if(nu->pntsv==1) { - len= resolu*nu->pntsu; - bl= MEM_mallocN(sizeof(BevList)+len*sizeof(BevPoint), "makeBevelList3"); + len= (resolu*SEGMENTSU(nu)); + if((nu->flagu & CU_CYCLIC)==0) len++; + + bl= MEM_callocN(sizeof(BevList)+len*sizeof(BevPoint), "makeBevelList3"); BLI_addtail(&(cu->bev), bl); bl->nr= len; bl->flag= 0; - if(nu->flagu & 1) bl->poly= 0; + if(nu->flagu & CU_CYCLIC) bl->poly= 0; else bl->poly= -1; bevp= (BevPoint *)(bl+1); - data= MEM_callocN(4*sizeof(float)*len, "makeBevelList4"); /* has to be zero-ed */ - makeNurbcurve(nu, data, resolu, 4); + coord_array= coord_fp= MEM_callocN(3*sizeof(float)*len, "makeBevelCoords"); /* has to be zero-ed */ + + if(do_tilt) + tilt_array= tilt_fp= MEM_callocN(sizeof(float)*len, "makeBevelTilt"); + + if (do_radius) + radius_array= radius_fp= MEM_callocN(sizeof(float)*len, "nakeBevelRadius"); + + makeNurbcurve(nu, coord_array, tilt_array, radius_array, resolu); - v1= data; while(len--) { - bevp->x= v1[0]; - bevp->y= v1[1]; - bevp->z= v1[2]; - bevp->alfa= v1[3]; + bevp->x= coord_fp[0]; + bevp->y= coord_fp[1]; + bevp->z= coord_fp[2]; + coord_fp+=3; + + if (do_tilt) { + bevp->alfa= *tilt_fp; + tilt_fp++; + } + + if (do_radius) { + bevp->radius= *radius_fp; + radius_fp++; + } + bevp->f1= bevp->f2= 0; bevp++; - v1+=4; } - MEM_freeN(data); + MEM_freeN(coord_array); + if (do_tilt) MEM_freeN(tilt_array); + if (do_radius) MEM_freeN(radius_array); + coord_array = tilt_array = radius_array = NULL; } } } @@ -1662,7 +1763,7 @@ void makeBevelList(Object *ob) blnext= bl->next; if(bl->nr && bl->flag) { nr= bl->nr- bl->flag+1; /* +1 because vectorbezier sets flag too */ - blnew= MEM_mallocN(sizeof(BevList)+nr*sizeof(BevPoint), "makeBevelList"); + blnew= MEM_mallocN(sizeof(BevList)+nr*sizeof(BevPoint), "makeBevelList4"); memcpy(blnew, bl, sizeof(BevList)); blnew->nr= 0; BLI_remlink(&(cu->bev), bl); @@ -1785,13 +1886,13 @@ void makeBevelList(Object *ob) bevp2->cosa= bevp1->cosa; if(cu->flag & CU_3D) { /* 3D */ - float *quat, q[4]; + float quat[4], q[4]; vec[0]= bevp1->x - bevp2->x; vec[1]= bevp1->y - bevp2->y; vec[2]= bevp1->z - bevp2->z; - quat= vectoquat(vec, 5, 1); + vectoquat(vec, 5, 1, quat); Normalize(vec); q[0]= (float)cos(0.5*bevp1->alfa); @@ -1817,7 +1918,7 @@ void makeBevelList(Object *ob) while(nr--) { if(cu->flag & CU_3D) { /* 3D */ - float *quat, q[4]; + float quat[4], q[4]; vec[0]= bevp2->x - bevp0->x; vec[1]= bevp2->y - bevp0->y; @@ -1825,7 +1926,7 @@ void makeBevelList(Object *ob) Normalize(vec); - quat= vectoquat(vec, 5, 1); + vectoquat(vec, 5, 1, quat); q[0]= (float)cos(0.5*bevp1->alfa); x1= (float)sin(0.5*bevp1->alfa); @@ -1870,134 +1971,6 @@ void makeBevelList(Object *ob) } } -/* calculates a bevel width (radius) for a particular subdivided curve part, - * based on the radius value of the surrounding CVs */ -float calc_curve_subdiv_radius(Curve *cu, Nurb *nu, int cursubdiv) -{ - BezTriple *bezt, *beztfirst, *beztlast, *beztnext, *beztprev; - BPoint *bp, *bpfirst, *bplast; - int resolu; - float prevrad=0.0, nextrad=0.0, rad=0.0, ratio=0.0; - int vectseg=0, subdivs=0; - - if((nu==NULL) || (nu->pntsu<=1)) return 1.0; - bezt= nu->bezt; - bp = nu->bp; - - if(G.rendering && cu->resolu_ren!=0) resolu= cu->resolu_ren; - else resolu= nu->resolu; - - if(((nu->type & 7)==CU_BEZIER) && (bezt != NULL)) { - beztfirst = nu->bezt; - beztlast = nu->bezt + (nu->pntsu - 1); - - /* loop through the CVs to end up with a pointer to the CV before the subdiv in question, and a ratio - * of how far that subdiv is between this CV and the next */ - while(bezt<=beztlast) { - beztnext = bezt+1; - beztprev = bezt-1; - vectseg=0; - - if (subdivs==cursubdiv) { - ratio= 0.0; - break; - } - - /* check to see if we're looking at a vector segment (no subdivisions) */ - if (nu->flagu & CU_CYCLIC) { - if (bezt == beztfirst) { - if ((beztlast->h2==HD_VECT) && (bezt->h1==HD_VECT)) vectseg = 1; - } else { - if ((beztprev->h2==HD_VECT) && (bezt->h1==HD_VECT)) vectseg = 1; - } - } else if ((bezt->h2==HD_VECT) && (beztnext->h1==HD_VECT)) vectseg = 1; - - - if (vectseg==0) { - /* if it's NOT a vector segment, check to see if the subdiv falls within the segment */ - subdivs += resolu; - - if (cursubdiv < subdivs) { - ratio = 1.0 - ((subdivs - cursubdiv)/(float)resolu); - break; - } - } else { - /* must be a vector segment.. loop again! */ - subdivs += 1; - } - - bezt++; - } - - /* Now we have a nice bezt pointer to the CV that we want. But cyclic messes it up, so must correct for that.. - * (cyclic goes last-> first -> first+1 -> first+2 -> ...) */ - if (nu->flagu & CU_CYCLIC) { - if (bezt == beztfirst) bezt = beztlast; - else bezt--; - } - - /* find the radii at the bounding CVs and interpolate between them based on ratio */ - rad = prevrad = bezt->radius; - - if ((bezt == beztlast) && (nu->flagu & CU_CYCLIC)) { /* loop around */ - bezt= beztfirst; - } else if (bezt != beztlast) { - bezt++; - } - nextrad = bezt->radius; - - } - else if( ( ((nu->type & 7)==CU_NURBS) || ((nu->type & 7)==CU_POLY)) && (bp != NULL)) { - /* follows similar algo as for bezt above */ - bpfirst = nu->bp; - bplast = nu->bp + (nu->pntsu - 1); - - if ((nu->type & 7)==CU_POLY) resolu=1; - - while(bp<=bplast) { - if (subdivs==cursubdiv) { - ratio= 0.0; - break; - } - - subdivs += resolu; - - if (cursubdiv < subdivs) { - ratio = 1.0 - ((subdivs - cursubdiv)/(float)resolu); - break; - } - - bp++; - } - - if ( ((nu->type & 7)==CU_NURBS) && (nu->flagu & CU_CYCLIC)) { - if (bp == bplast) bp = bpfirst; - else bp++; - } - - rad = prevrad = bp->radius; - - if ((bp == bplast) && (nu->flagu & CU_CYCLIC)) { /* loop around */ - bp= bpfirst; - } else if (bp != bplast) { - bp++; - } - nextrad = bp->radius; - - } - - - if (nextrad != prevrad) { - /* smooth interpolation */ - rad = prevrad + (nextrad-prevrad)*(3.0f*ratio*ratio - 2.0f*ratio*ratio*ratio); - } - - if (rad > 0.0) - return rad; - else - return 1.0; -} - /* ****************** HANDLES ************** */ /* @@ -2204,7 +2177,7 @@ void calchandlesNurb(Nurb *nu) /* first, if needed, set handle flags */ a= nu->pntsu; bezt= nu->bezt; - if(nu->flagu & 1) prev= bezt+(a-1); + if(nu->flagu & CU_CYCLIC) prev= bezt+(a-1); else prev= 0; next= bezt+1; @@ -2212,7 +2185,7 @@ void calchandlesNurb(Nurb *nu) /* first, if needed, set handle flags */ calchandleNurb(bezt, prev, next, 0); prev= bezt; if(a==1) { - if(nu->flagu & 1) next= nu->bezt; + if(nu->flagu & CU_CYCLIC) next= nu->bezt; else next= 0; } else next++; @@ -2366,9 +2339,9 @@ void sethandlesNurb(short code) bezt= nu->bezt; a= nu->pntsu; while(a--) { - if(bezt->f1 || bezt->f3) { - if(bezt->f1) bezt->h1= code; - if(bezt->f3) bezt->h2= code; + if((bezt->f1 & SELECT) || (bezt->f3 & SELECT)) { + if(bezt->f1 & SELECT) bezt->h1= code; + if(bezt->f3 & SELECT) bezt->h2= code; if(bezt->h1!=bezt->h2) { if ELEM(bezt->h1, HD_ALIGN, HD_AUTO) bezt->h1= HD_FREE; if ELEM(bezt->h2, HD_ALIGN, HD_AUTO) bezt->h2= HD_FREE; @@ -2396,8 +2369,8 @@ void sethandlesNurb(short code) bezt= nu->bezt; a= nu->pntsu; while(a--) { - if(bezt->f1 && bezt->h1) ok= 1; - if(bezt->f3 && bezt->h2) ok= 1; + if((bezt->f1 & SELECT) && bezt->h1) ok= 1; + if((bezt->f3 & SELECT) && bezt->h2) ok= 1; if(ok) break; bezt++; } @@ -2413,8 +2386,8 @@ void sethandlesNurb(short code) bezt= nu->bezt; a= nu->pntsu; while(a--) { - if(bezt->f1) bezt->h1= ok; - if(bezt->f3 ) bezt->h2= ok; + if(bezt->f1 & SELECT) bezt->h1= ok; + if(bezt->f3 & SELECT) bezt->h2= ok; bezt++; } @@ -2603,3 +2576,63 @@ void curve_applyVertexCos(Curve *cu, ListBase *lb, float (*vertexCos)[3]) } } } + +int check_valid_nurb_u( struct Nurb *nu ) +{ + if (nu==NULL) return 0; + if (nu->pntsu <= 1) return 0; + if ((nu->type & 7)!=CU_NURBS) return 1; /* not a nurb, lets assume its valid */ + + if (nu->pntsu < nu->orderu) return 0; + if (((nu->flag & CU_CYCLIC)==0) && ((nu->flagu>>1) & 2)) { /* Bezier U Endpoints */ + if (nu->orderu==4) { + if (nu->pntsu < 5) return 0; /* bezier with 4 orderu needs 5 points */ + } else if (nu->orderu != 3) return 0; /* order must be 3 or 4 */ + } + return 1; +} +int check_valid_nurb_v( struct Nurb *nu) +{ + if (nu==NULL) return 0; + if (nu->pntsv <= 1) return 0; + if ((nu->type & 7)!=CU_NURBS) return 1; /* not a nurb, lets assume its valid */ + + if (nu->pntsv < nu->orderv) return 0; + if (((nu->flag & CU_CYCLIC)==0) && ((nu->flagv>>1) & 2)) { /* Bezier V Endpoints */ + if (nu->orderv==4) { + if (nu->pntsv < 5) return 0; /* bezier with 4 orderu needs 5 points */ + } else if (nu->orderv != 3) return 0; /* order must be 3 or 4 */ + } + return 1; +} + +int clamp_nurb_order_u( struct Nurb *nu ) +{ + int change = 0; + if(nu->pntsu<nu->orderu) { + nu->orderu= nu->pntsu; + change= 1; + } + if(((nu->flag & CU_CYCLIC)==0) && (nu->flagu>>1)&2) { + CLAMP(nu->orderu, 3,4); + change= 1; + } + return change; +} + +int clamp_nurb_order_v( struct Nurb *nu) +{ + int change = 0; + if(nu->pntsv<nu->orderv) { + nu->orderv= nu->pntsv; + change= 1; + } + if(((nu->flag & CU_CYCLIC)==0) && (nu->flagv>>1)&2) { + CLAMP(nu->orderv, 3,4); + change= 1; + } + return change; +} + + + diff --git a/source/blender/blenkernel/intern/customdata.c b/source/blender/blenkernel/intern/customdata.c index d8ce311b5ca..e93266c85f3 100644 --- a/source/blender/blenkernel/intern/customdata.c +++ b/source/blender/blenkernel/intern/customdata.c @@ -36,6 +36,7 @@ #include "BLI_blenlib.h" #include "BLI_linklist.h" +#include "BLI_mempool.h" #include "DNA_customdata_types.h" #include "DNA_listBase.h" @@ -97,11 +98,16 @@ static void layerCopy_mdeformvert(const void *source, void *dest, for(i = 0; i < count; ++i) { MDeformVert *dvert = (MDeformVert *)((char *)dest + i * size); - MDeformWeight *dw = MEM_callocN(dvert->totweight * sizeof(*dw), - "layerCopy_mdeformvert dw"); - memcpy(dw, dvert->dw, dvert->totweight * sizeof(*dw)); - dvert->dw = dw; + if(dvert->totweight) { + MDeformWeight *dw = MEM_callocN(dvert->totweight * sizeof(*dw), + "layerCopy_mdeformvert dw"); + + memcpy(dw, dvert->dw, dvert->totweight * sizeof(*dw)); + dvert->dw = dw; + } + else + dvert->dw = NULL; } } @@ -259,14 +265,34 @@ static void layerSwap_tface(void *data, int *corner_indices) { MTFace *tf = data; float uv[4][2]; + static const short pin_flags[4] = + { TF_PIN1, TF_PIN2, TF_PIN3, TF_PIN4 }; + static const char sel_flags[4] = + { TF_SEL1, TF_SEL2, TF_SEL3, TF_SEL4 }; + short unwrap = tf->unwrap & ~(TF_PIN1 | TF_PIN2 | TF_PIN3 | TF_PIN4); + char flag = tf->flag & ~(TF_SEL1 | TF_SEL2 | TF_SEL3 | TF_SEL4); int j; for(j = 0; j < 4; ++j) { - uv[j][0] = tf->uv[corner_indices[j]][0]; - uv[j][1] = tf->uv[corner_indices[j]][1]; + int source_index = corner_indices[j]; + + uv[j][0] = tf->uv[source_index][0]; + uv[j][1] = tf->uv[source_index][1]; + + // swap pinning flags around + if(tf->unwrap & pin_flags[source_index]) { + unwrap |= pin_flags[j]; + } + + // swap selection flags around + if(tf->flag & sel_flags[source_index]) { + flag |= sel_flags[j]; + } } memcpy(tf->uv, uv, sizeof(tf->uv)); + tf->unwrap = unwrap; + tf->flag = flag; } static void layerDefault_tface(void *data, int count) @@ -354,8 +380,80 @@ static void layerDefault_origspace_face(void *data, int count) } /* --------- */ +static void layerDefault_mloopcol(void *data, int count) +{ + static MLoopCol default_mloopcol = {255,255,255,255}; + MLoopCol *mlcol = (MLoopCol*)data; + int i; + for(i = 0; i < count; i++) + mlcol[i] = default_mloopcol; + +} + +static void layerInterp_mloopcol(void **sources, float *weights, + float *sub_weights, int count, void *dest) +{ + MLoopCol *mc = dest; + int i; + float *sub_weight; + struct { + float a; + float r; + float g; + float b; + } col; + col.a = col.r = col.g = col.b = 0; + sub_weight = sub_weights; + for(i = 0; i < count; ++i){ + float weight = weights ? weights[i] : 1; + MLoopCol *src = sources[i]; + if(sub_weights){ + col.a += src->a * (*sub_weight) * weight; + col.r += src->r * (*sub_weight) * weight; + col.g += src->g * (*sub_weight) * weight; + col.b += src->b * (*sub_weight) * weight; + sub_weight++; + } else { + col.a += src->a * weight; + col.r += src->r * weight; + col.g += src->g * weight; + col.b += src->b * weight; + } + } + mc->a = (int)col.a; + mc->r = (int)col.r; + mc->g = (int)col.g; + mc->b = (int)col.b; +} +static void layerInterp_mloopuv(void **sources, float *weights, + float *sub_weights, int count, void *dest) +{ + MLoopUV *mluv = dest; + int i; + float *sub_weight; + struct { + float u; + float v; + }uv; + uv.u = uv.v = 0.0; + sub_weight = sub_weights; + for(i = 0; i < count; ++i){ + float weight = weights ? weights[i] : 1; + MLoopUV *src = sources[i]; + if(sub_weights){ + uv.u += src->uv[0] * (*sub_weight) * weight; + uv.v += src->uv[1] * (*sub_weight) * weight; + sub_weight++; + } else { + uv.u += src->uv[0] * weight; + uv.v += src->uv[1] * weight; + } + } + mluv->uv[0] = uv.u; + mluv->uv[1] = uv.v; +} static void layerInterp_mcol(void **sources, float *weights, float *sub_weights, int count, void *dest) @@ -427,6 +525,8 @@ static void layerDefault_mcol(void *data, int count) mcol[i] = default_mcol; } + + const LayerTypeInfo LAYERTYPEINFO[CD_NUMTYPES] = { {sizeof(MVert), "MVert", 1, NULL, NULL, NULL, NULL, NULL, NULL}, {sizeof(MSticky), "MSticky", 1, NULL, NULL, NULL, layerInterp_msticky, NULL, @@ -449,13 +549,17 @@ const LayerTypeInfo LAYERTYPEINFO[CD_NUMTYPES] = { {sizeof(MStringProperty), "MStringProperty",1,"String",NULL,NULL,NULL,NULL}, {sizeof(OrigSpaceFace), "OrigSpaceFace", 1, "UVTex", layerCopy_origspace_face, NULL, layerInterp_origspace_face, layerSwap_origspace_face, layerDefault_origspace_face}, - {sizeof(float)*3, "", 0, NULL, NULL, NULL, NULL, NULL, NULL} + {sizeof(float)*3, "", 0, NULL, NULL, NULL, NULL, NULL, NULL}, + {sizeof(MTexPoly), "MTexPoly", 1, "Face Texture", NULL, NULL, NULL, NULL, NULL}, + {sizeof(MLoopUV), "MLoopUV", 1, "UV coord", NULL, NULL, layerInterp_mloopuv, NULL, NULL}, + {sizeof(MLoopCol), "MLoopCol", 1, "Col", NULL, NULL, layerInterp_mloopcol, NULL, layerDefault_mloopcol}, + {sizeof(float)*3*4, "", 0, NULL, NULL, NULL, NULL, NULL, NULL} }; const char *LAYERTYPENAMES[CD_NUMTYPES] = { "CDMVert", "CDMSticky", "CDMDeformVert", "CDMEdge", "CDMFace", "CDMTFace", "CDMCol", "CDOrigIndex", "CDNormal", "CDFlags","CDMFloatProperty", - "CDMIntProperty","CDMStringProperty", "CDOrigSpace", "CDOrco"}; + "CDMIntProperty","CDMStringProperty", "CDOrigSpace", "CDOrco", "CDMTexPoly", "CDMLoopUV", "CDMloopCol", "CDTangent"}; const CustomDataMask CD_MASK_BAREMESH = CD_MASK_MVERT | CD_MASK_MEDGE | CD_MASK_MFACE; @@ -469,7 +573,13 @@ const CustomDataMask CD_MASK_EDITMESH = const CustomDataMask CD_MASK_DERIVEDMESH = CD_MASK_MSTICKY | CD_MASK_MDEFORMVERT | CD_MASK_MTFACE | CD_MASK_MCOL | CD_MASK_ORIGINDEX | CD_MASK_PROP_FLT | CD_MASK_PROP_INT | - CD_MASK_PROP_STR | CD_MASK_ORIGSPACE | CD_MASK_ORCO; + CD_MASK_PROP_STR | CD_MASK_ORIGSPACE | CD_MASK_ORCO | CD_MASK_TANGENT; +const CustomDataMask CD_MASK_BMESH = + CD_MASK_MSTICKY | CD_MASK_MDEFORMVERT | CD_MASK_PROP_FLT | CD_MASK_PROP_INT | CD_MASK_PROP_STR; +const CustomDataMask CD_MASK_FACECORNERS = + CD_MASK_MTFACE | CD_MASK_MCOL | CD_MASK_MTEXPOLY | CD_MASK_MLOOPUV | + CD_MASK_MLOOPCOL; + static const LayerTypeInfo *layerType_getInfo(int type) { @@ -816,12 +926,9 @@ void *CustomData_add_layer_named(CustomData *data, int type, int alloctype, int CustomData_free_layer(CustomData *data, int type, int totelem, int index) { int i; - CustomDataLayer *layer; if (index < 0) return 0; - layer = &data->layers[index]; - customData_free_layer__internal(&data->layers[index], totelem); for (i=index+1; i < data->totlayer; ++i) @@ -1236,7 +1343,8 @@ void CustomData_em_copy_data(const CustomData *source, CustomData *dest, if(dest_i >= dest->totlayer) return; /* if we found a matching layer, copy the data */ - if(dest->layers[dest_i].type == source->layers[src_i].type) { + if(dest->layers[dest_i].type == source->layers[src_i].type && + strcmp(dest->layers[dest_i].name, source->layers[src_i].name) == 0) { char *src_data = (char*)src_block + source->layers[src_i].offset; char *dest_data = (char*)*dest_block + dest->layers[dest_i].offset; @@ -1272,7 +1380,7 @@ void *CustomData_em_get_n(const CustomData *data, void *block, int type, int n) int layer_index; /* get the layer index of the first layer of type */ - layer_index = CustomData_get_active_layer_index(data, type); + layer_index = CustomData_get_layer_index(data, type); if(layer_index < 0) return NULL; return (char *)block + data->layers[layer_index+n].offset; @@ -1443,6 +1551,302 @@ void CustomData_from_em_block(const CustomData *source, CustomData *dest, } +/*Bmesh functions*/ +/*needed to convert to/from different face reps*/ +void CustomData_to_bmeshpoly(CustomData *fdata, CustomData *pdata, CustomData *ldata) +{ + int i; + for(i=0; i < fdata->totlayer; i++){ + if(fdata->layers[i].type == CD_MTFACE){ + CustomData_add_layer(pdata, CD_MTEXPOLY, CD_CALLOC, &(fdata->layers[i].name), 0); + CustomData_add_layer(ldata, CD_MLOOPUV, CD_CALLOC, &(fdata->layers[i].name), 0); + } + else if(fdata->layers[i].type == CD_MCOL) + CustomData_add_layer(ldata, CD_MLOOPCOL, CD_CALLOC, &(fdata->layers[i].name), 0); + } +} +void CustomData_from_bmeshpoly(CustomData *fdata, CustomData *pdata, CustomData *ldata, int total){ + int i; + for(i=0; i < pdata->totlayer; i++){ + if(pdata->layers[i].type == CD_MTEXPOLY) + CustomData_add_layer(fdata, CD_MTFACE, CD_CALLOC, &(pdata->layers[i].name), total); + } + for(i=0; i < ldata->totlayer; i++){ + if(ldata->layers[i].type == CD_MLOOPCOL) + CustomData_add_layer(fdata, CD_MCOL, CD_CALLOC, &(ldata->layers[i].name), total); + } +} + + +void CustomData_bmesh_init_pool(CustomData *data, int allocsize){ + if(data->totlayer)data->pool = BLI_mempool_create(data->totsize, allocsize, allocsize); +} + +void CustomData_bmesh_free_block(CustomData *data, void **block) +{ + const LayerTypeInfo *typeInfo; + int i; + + if(!*block) return; + for(i = 0; i < data->totlayer; ++i) { + if(!(data->layers[i].flag & CD_FLAG_NOFREE)) { + typeInfo = layerType_getInfo(data->layers[i].type); + + if(typeInfo->free) { + int offset = data->layers[i].offset; + typeInfo->free((char*)*block + offset, 1, typeInfo->size); + } + } + } + + BLI_mempool_free(data->pool, *block); + *block = NULL; +} + +static void CustomData_bmesh_alloc_block(CustomData *data, void **block) +{ + + if (*block) + CustomData_bmesh_free_block(data, block); + + if (data->totsize > 0) + *block = BLI_mempool_calloc(data->pool); + else + *block = NULL; +} + +void CustomData_bmesh_copy_data(const CustomData *source, CustomData *dest, + void *src_block, void **dest_block) +{ + const LayerTypeInfo *typeInfo; + int dest_i, src_i; + + if (!*dest_block) + CustomData_bmesh_alloc_block(dest, dest_block); + + /* copies a layer at a time */ + dest_i = 0; + for(src_i = 0; src_i < source->totlayer; ++src_i) { + + /* find the first dest layer with type >= the source type + * (this should work because layers are ordered by type) + */ + while(dest_i < dest->totlayer + && dest->layers[dest_i].type < source->layers[src_i].type) + ++dest_i; + + /* if there are no more dest layers, we're done */ + if(dest_i >= dest->totlayer) return; + + /* if we found a matching layer, copy the data */ + if(dest->layers[dest_i].type == source->layers[src_i].type && + strcmp(dest->layers[dest_i].name, source->layers[src_i].name) == 0) { + char *src_data = (char*)src_block + source->layers[src_i].offset; + char *dest_data = (char*)*dest_block + dest->layers[dest_i].offset; + + typeInfo = layerType_getInfo(source->layers[src_i].type); + + if(typeInfo->copy) + typeInfo->copy(src_data, dest_data, 1); + else + memcpy(dest_data, src_data, typeInfo->size); + + /* if there are multiple source & dest layers of the same type, + * we don't want to copy all source layers to the same dest, so + * increment dest_i + */ + ++dest_i; + } + } +} + +/*Bmesh Custom Data Functions. Should replace editmesh ones with these as well, due to more effecient memory alloc*/ +void *CustomData_bmesh_get(const CustomData *data, void *block, int type) +{ + int layer_index; + + /* get the layer index of the first layer of type */ + layer_index = CustomData_get_active_layer_index(data, type); + if(layer_index < 0) return NULL; + + return (char *)block + data->layers[layer_index].offset; +} + +void *CustomData_bmesh_get_n(const CustomData *data, void *block, int type, int n) +{ + int layer_index; + + /* get the layer index of the first layer of type */ + layer_index = CustomData_get_layer_index(data, type); + if(layer_index < 0) return NULL; + + return (char *)block + data->layers[layer_index+n].offset; +} + +void CustomData_bmesh_set(const CustomData *data, void *block, int type, void *source) +{ + void *dest = CustomData_bmesh_get(data, block, type); + const LayerTypeInfo *typeInfo = layerType_getInfo(type); + + if(!dest) return; + + if(typeInfo->copy) + typeInfo->copy(source, dest, 1); + else + memcpy(dest, source, typeInfo->size); +} + +void CustomData_bmesh_set_n(CustomData *data, void *block, int type, int n, void *source) +{ + void *dest = CustomData_bmesh_get_n(data, block, type, n); + const LayerTypeInfo *typeInfo = layerType_getInfo(type); + + if(!dest) return; + + if(typeInfo->copy) + typeInfo->copy(source, dest, 1); + else + memcpy(dest, source, typeInfo->size); +} + +void CustomData_bmesh_interp(CustomData *data, void **src_blocks, float *weights, + float *sub_weights, int count, void *dest_block) +{ + int i, j; + void *source_buf[SOURCE_BUF_SIZE]; + void **sources = source_buf; + + /* slow fallback in case we're interpolating a ridiculous number of + * elements + */ + if(count > SOURCE_BUF_SIZE) + sources = MEM_callocN(sizeof(*sources) * count, + "CustomData_interp sources"); + + /* interpolates a layer at a time */ + for(i = 0; i < data->totlayer; ++i) { + CustomDataLayer *layer = &data->layers[i]; + const LayerTypeInfo *typeInfo = layerType_getInfo(layer->type); + if(typeInfo->interp) { + for(j = 0; j < count; ++j) + sources[j] = (char *)src_blocks[j] + layer->offset; + + typeInfo->interp(sources, weights, sub_weights, count, + (char *)dest_block + layer->offset); + } + } + + if(count > SOURCE_BUF_SIZE) MEM_freeN(sources); +} + +void CustomData_bmesh_set_default(CustomData *data, void **block) +{ + const LayerTypeInfo *typeInfo; + int i; + + if (!*block) + CustomData_bmesh_alloc_block(data, block); + + for(i = 0; i < data->totlayer; ++i) { + int offset = data->layers[i].offset; + + typeInfo = layerType_getInfo(data->layers[i].type); + + if(typeInfo->set_default) + typeInfo->set_default((char*)*block + offset, 1); + } +} + +void CustomData_to_bmesh_block(const CustomData *source, CustomData *dest, + int src_index, void **dest_block) +{ + const LayerTypeInfo *typeInfo; + int dest_i, src_i, src_offset; + + if (!*dest_block) + CustomData_bmesh_alloc_block(dest, dest_block); + + /* copies a layer at a time */ + dest_i = 0; + for(src_i = 0; src_i < source->totlayer; ++src_i) { + + /* find the first dest layer with type >= the source type + * (this should work because layers are ordered by type) + */ + while(dest_i < dest->totlayer + && dest->layers[dest_i].type < source->layers[src_i].type) + ++dest_i; + + /* if there are no more dest layers, we're done */ + if(dest_i >= dest->totlayer) return; + + /* if we found a matching layer, copy the data */ + if(dest->layers[dest_i].type == source->layers[src_i].type) { + int offset = dest->layers[dest_i].offset; + char *src_data = source->layers[src_i].data; + char *dest_data = (char*)*dest_block + offset; + + typeInfo = layerType_getInfo(dest->layers[dest_i].type); + src_offset = src_index * typeInfo->size; + + if(typeInfo->copy) + typeInfo->copy(src_data + src_offset, dest_data, 1); + else + memcpy(dest_data, src_data + src_offset, typeInfo->size); + + /* if there are multiple source & dest layers of the same type, + * we don't want to copy all source layers to the same dest, so + * increment dest_i + */ + ++dest_i; + } + } +} + +void CustomData_from_bmesh_block(const CustomData *source, CustomData *dest, + void *src_block, int dest_index) +{ + const LayerTypeInfo *typeInfo; + int dest_i, src_i, dest_offset; + + /* copies a layer at a time */ + dest_i = 0; + for(src_i = 0; src_i < source->totlayer; ++src_i) { + + /* find the first dest layer with type >= the source type + * (this should work because layers are ordered by type) + */ + while(dest_i < dest->totlayer + && dest->layers[dest_i].type < source->layers[src_i].type) + ++dest_i; + + /* if there are no more dest layers, we're done */ + if(dest_i >= dest->totlayer) return; + + /* if we found a matching layer, copy the data */ + if(dest->layers[dest_i].type == source->layers[src_i].type) { + int offset = source->layers[src_i].offset; + char *src_data = (char*)src_block + offset; + char *dest_data = dest->layers[dest_i].data; + + typeInfo = layerType_getInfo(dest->layers[dest_i].type); + dest_offset = dest_index * typeInfo->size; + + if(typeInfo->copy) + typeInfo->copy(src_data, dest_data + dest_offset, 1); + else + memcpy(dest_data + dest_offset, src_data, typeInfo->size); + + /* if there are multiple source & dest layers of the same type, + * we don't want to copy all source layers to the same dest, so + * increment dest_i + */ + ++dest_i; + } + } + +} + void CustomData_file_write_info(int type, char **structname, int *structnum) { const LayerTypeInfo *typeInfo = layerType_getInfo(type); diff --git a/source/blender/blenkernel/intern/deform.c b/source/blender/blenkernel/intern/deform.c index ab53571b62d..47736865273 100644 --- a/source/blender/blenkernel/intern/deform.c +++ b/source/blender/blenkernel/intern/deform.c @@ -220,3 +220,28 @@ void unique_vertexgroup_name (bDeformGroup *dg, Object *ob) } } } + +float deformvert_get_weight(const struct MDeformVert *dvert, int group_num) +{ + if(dvert) + { + const MDeformWeight *dw = dvert->dw; + int i; + + for(i=dvert->totweight; i>0; i--, dw++) + if(dw->def_nr == group_num) + return dw->weight; + } + + /* Not found */ + return 0.0; +} + +float vertexgroup_get_vertex_weight(const struct MDeformVert *dvert, int index, int group_num) +{ + if(group_num == -1 || dvert == NULL) + return 1.0; + + return deformvert_get_weight(dvert+index, group_num); +} + diff --git a/source/blender/blenkernel/intern/depsgraph.c b/source/blender/blenkernel/intern/depsgraph.c index 0ce318eb57f..f5deda9c3c9 100644 --- a/source/blender/blenkernel/intern/depsgraph.c +++ b/source/blender/blenkernel/intern/depsgraph.c @@ -46,6 +46,7 @@ #include "DNA_effect_types.h" #include "DNA_group_types.h" #include "DNA_lattice_types.h" +#include "DNA_lamp_types.h" #include "DNA_key_types.h" #include "DNA_mesh_types.h" #include "DNA_modifier_types.h" @@ -71,12 +72,15 @@ #include "BKE_modifier.h" #include "BKE_object.h" #include "BKE_particle.h" +#include "BKE_pointcache.h" #include "BKE_utildefines.h" #include "BKE_scene.h" #include "MEM_guardedalloc.h" +#ifndef DISABLE_PYTHON #include "BPY_extern.h" +#endif #include "depsgraph_private.h" @@ -310,6 +314,7 @@ static void dag_add_driver_relation(Ipo *ipo, DagForest *dag, DagNode *node, int if ((icu->driver->flag & IPO_DRIVER_FLAG_INVALID) || (icu->driver->name[0] == '\0')) continue; /* empty or invalid expression */ +#ifndef DISABLE_PYTHON else { /* now we need refs to all objects mentioned in this * pydriver expression, to call 'dag_add_relation' @@ -322,22 +327,41 @@ static void dag_add_driver_relation(Ipo *ipo, DagForest *dag, DagNode *node, int ob = *oba; node1 = dag_get_node(dag, ob); if (ob->type == OB_ARMATURE) - dag_add_relation(dag, node1, node, isdata?DAG_RL_DATA_DATA:DAG_RL_DATA_OB); + dag_add_relation(dag, node1, node, isdata?DAG_RL_DATA_DATA:DAG_RL_DATA_OB, "Python Ipo Driver"); else - dag_add_relation(dag, node1, node, isdata?DAG_RL_OB_DATA:DAG_RL_OB_OB); + dag_add_relation(dag, node1, node, isdata?DAG_RL_OB_DATA:DAG_RL_OB_OB, "Python Ipo Driver"); oba++; } MEM_freeN(obarray); } } +#endif /* DISABLE_PYTHON */ } else if (icu->driver->ob) { node1 = dag_get_node(dag, icu->driver->ob); if(icu->driver->blocktype==ID_AR) - dag_add_relation(dag, node1, node, isdata?DAG_RL_DATA_DATA:DAG_RL_DATA_OB); + dag_add_relation(dag, node1, node, isdata?DAG_RL_DATA_DATA:DAG_RL_DATA_OB, "Ipo Driver"); else - dag_add_relation(dag, node1, node, isdata?DAG_RL_OB_DATA:DAG_RL_OB_OB); + dag_add_relation(dag, node1, node, isdata?DAG_RL_OB_DATA:DAG_RL_OB_OB, "Ipo Driver"); + } + } + } +} + +static void dag_add_collision_field_relation(DagForest *dag, Object *ob, DagNode *node) +{ + Base *base; + DagNode *node2; + + // would be nice to have a list of colliders here + // so for now walk all objects in scene check 'same layer rule' + for(base = G.scene->base.first; base; base= base->next) { + if((base->lay & ob->lay) && base->object->pd) { + Object *ob1= base->object; + if((ob1->pd->deflect || ob1->pd->forcefield) && (ob1 != ob)) { + node2 = dag_get_node(dag, ob1); + dag_add_relation(dag, node2, node, DAG_RL_DATA_DATA|DAG_RL_OB_DATA, "Field Collision"); } } } @@ -358,7 +382,7 @@ static void build_dag_object(DagForest *dag, DagNode *scenenode, Object *ob, int if ((ob->data) && (mask&DAG_RL_DATA)) { node2 = dag_get_node(dag,ob->data); - dag_add_relation(dag,node,node2,DAG_RL_DATA); + dag_add_relation(dag,node,node2,DAG_RL_DATA, "Object-Data Relation"); node2->first_ancestor = ob; node2->ancestor_count += 1; } @@ -383,11 +407,11 @@ static void build_dag_object(DagForest *dag, DagNode *scenenode, Object *ob, int node3 = dag_get_node(dag, ct->tar); if (ct->subtarget[0]) - dag_add_relation(dag,node3,node, DAG_RL_OB_DATA|DAG_RL_DATA_DATA); + dag_add_relation(dag,node3,node, DAG_RL_OB_DATA|DAG_RL_DATA_DATA, cti->name); else if(ELEM(con->type, CONSTRAINT_TYPE_FOLLOWPATH, CONSTRAINT_TYPE_CLAMPTO)) - dag_add_relation(dag,node3,node, DAG_RL_DATA_DATA|DAG_RL_OB_DATA); + dag_add_relation(dag,node3,node, DAG_RL_DATA_DATA|DAG_RL_OB_DATA, cti->name); else - dag_add_relation(dag,node3,node, DAG_RL_OB_DATA); + dag_add_relation(dag,node3,node, DAG_RL_OB_DATA, cti->name); } } @@ -435,7 +459,7 @@ static void build_dag_object(DagForest *dag, DagNode *scenenode, Object *ob, int for(amod= strip->modifiers.first; amod; amod= amod->next) { if(amod->ob) { node2 = dag_get_node(dag, amod->ob); - dag_add_relation(dag, node2, node, DAG_RL_DATA_DATA|DAG_RL_OB_DATA); + dag_add_relation(dag, node2, node, DAG_RL_DATA_DATA|DAG_RL_OB_DATA, "NLA Strip Modifier"); } } } @@ -455,46 +479,55 @@ static void build_dag_object(DagForest *dag, DagNode *scenenode, Object *ob, int switch(ob->partype) { case PARSKEL: - dag_add_relation(dag,node2,node,DAG_RL_DATA_DATA|DAG_RL_OB_OB); + dag_add_relation(dag,node2,node,DAG_RL_DATA_DATA|DAG_RL_OB_OB, "Parent"); break; case PARVERT1: case PARVERT3: case PARBONE: - dag_add_relation(dag,node2,node,DAG_RL_DATA_OB|DAG_RL_OB_OB); + dag_add_relation(dag,node2,node,DAG_RL_DATA_OB|DAG_RL_OB_OB, "Vertex Parent"); break; default: if(ob->parent->type==OB_LATTICE) - dag_add_relation(dag,node2,node,DAG_RL_DATA_DATA|DAG_RL_OB_OB); + dag_add_relation(dag,node2,node,DAG_RL_DATA_DATA|DAG_RL_OB_OB, "Lattice Parent"); else if(ob->parent->type==OB_CURVE) { Curve *cu= ob->parent->data; if(cu->flag & CU_PATH) - dag_add_relation(dag,node2,node,DAG_RL_DATA_OB|DAG_RL_OB_OB); + dag_add_relation(dag,node2,node,DAG_RL_DATA_OB|DAG_RL_OB_OB, "Curve Parent"); else - dag_add_relation(dag,node2,node,DAG_RL_OB_OB); + dag_add_relation(dag,node2,node,DAG_RL_OB_OB, "Curve Parent"); } - else - dag_add_relation(dag,node2,node,DAG_RL_OB_OB); + else + dag_add_relation(dag,node2,node,DAG_RL_OB_OB, "Parent"); } /* exception case: parent is duplivert */ if(ob->type==OB_MBALL && (ob->parent->transflag & OB_DUPLIVERTS)) { - dag_add_relation(dag, node2, node, DAG_RL_DATA_DATA|DAG_RL_OB_OB); + dag_add_relation(dag, node2, node, DAG_RL_DATA_DATA|DAG_RL_OB_OB, "Duplivert"); } addtoroot = 0; } if (ob->track) { node2 = dag_get_node(dag,ob->track); - dag_add_relation(dag,node2,node,DAG_RL_OB_OB); + dag_add_relation(dag,node2,node,DAG_RL_OB_OB, "Track To"); addtoroot = 0; } if (ob->proxy) { node2 = dag_get_node(dag, ob->proxy); - dag_add_relation(dag, node, node2, DAG_RL_DATA_DATA|DAG_RL_OB_OB); + dag_add_relation(dag, node, node2, DAG_RL_DATA_DATA|DAG_RL_OB_OB, "Proxy"); /* inverted relation, so addtoroot shouldn't be set to zero */ } if (ob->type==OB_CAMERA) { Camera *cam = (Camera *)ob->data; + if (cam->ipo) { + dag_add_driver_relation(cam->ipo, dag, node, 1); + } if (cam->dof_ob) { node2 = dag_get_node(dag, cam->dof_ob); - dag_add_relation(dag,node2,node,DAG_RL_OB_OB); + dag_add_relation(dag,node2,node,DAG_RL_OB_OB, "Camera DoF"); + } + } + if (ob->type==OB_LAMP) { + Lamp *la = (Lamp *)ob->data; + if (la->ipo) { + dag_add_driver_relation(la->ipo, dag, node, 1); } } if (ob->transflag & OB_DUPLI) { @@ -504,46 +537,33 @@ static void build_dag_object(DagForest *dag, DagNode *scenenode, Object *ob, int if(go->ob) { node2 = dag_get_node(dag, go->ob); /* node2 changes node1, this keeps animations updated in groups?? not logical? */ - dag_add_relation(dag, node2, node, DAG_RL_OB_OB); + dag_add_relation(dag, node2, node, DAG_RL_OB_OB, "Dupligroup"); } } } } /* softbody collision */ - if((ob->type==OB_MESH) || (ob->type==OB_CURVE) || (ob->type==OB_LATTICE)) { - Base *base; - if(modifiers_isSoftbodyEnabled(ob)){ - // would be nice to have a list of colliders here - // so for now walk all objects in scene check 'same layer rule' - for(base = G.scene->base.first; base; base= base->next) { - if( (base->lay & ob->lay) && base->object->pd) { - Object *ob1= base->object; - if((ob1->pd->deflect) && (ob1 != ob)) { - node2 = dag_get_node(dag, ob1); - dag_add_relation(dag, node2, node, DAG_RL_DATA_DATA|DAG_RL_OB_DATA); - } - } - } - } - } + if((ob->type==OB_MESH) || (ob->type==OB_CURVE) || (ob->type==OB_LATTICE)) + if(modifiers_isSoftbodyEnabled(ob) || modifiers_isClothEnabled(ob)) + dag_add_collision_field_relation(dag, ob, node); if (ob->type==OB_MBALL) { Object *mom= find_basis_mball(ob); if(mom!=ob) { node2 = dag_get_node(dag, mom); - dag_add_relation(dag,node,node2,DAG_RL_DATA_DATA|DAG_RL_OB_DATA); // mom depends on children! + dag_add_relation(dag,node,node2,DAG_RL_DATA_DATA|DAG_RL_OB_DATA, "Metaball"); // mom depends on children! } } else if (ob->type==OB_CURVE) { Curve *cu= ob->data; if(cu->bevobj) { node2 = dag_get_node(dag, cu->bevobj); - dag_add_relation(dag,node2,node,DAG_RL_DATA_DATA|DAG_RL_OB_DATA); + dag_add_relation(dag,node2,node,DAG_RL_DATA_DATA|DAG_RL_OB_DATA, "Curve Bevel"); } if(cu->taperobj) { node2 = dag_get_node(dag, cu->taperobj); - dag_add_relation(dag,node2,node,DAG_RL_DATA_DATA|DAG_RL_OB_DATA); + dag_add_relation(dag,node2,node,DAG_RL_DATA_DATA|DAG_RL_OB_DATA, "Curve Taper"); } if(cu->ipo) dag_add_driver_relation(cu->ipo, dag, node, 1); @@ -553,52 +573,41 @@ static void build_dag_object(DagForest *dag, DagNode *scenenode, Object *ob, int Curve *cu= ob->data; if(cu->textoncurve) { node2 = dag_get_node(dag, cu->textoncurve); - dag_add_relation(dag,node2,node,DAG_RL_DATA_DATA|DAG_RL_OB_DATA); - } - } - else if(ob->type==OB_MESH) { - PartEff *paf= give_parteff(ob); - if(paf) { - ListBase *listb; - pEffectorCache *ec; - - /* ob location depends on itself */ - if((paf->flag & PAF_STATIC)==0) - dag_add_relation(dag, node, node, DAG_RL_OB_DATA); - - listb= pdInitEffectors(ob, paf->group); /* note, makes copy... */ - if(listb) { - for(ec= listb->first; ec; ec= ec->next) { - Object *ob1= ec->ob; - PartDeflect *pd= ob1->pd; - - if(pd->forcefield) { - node2 = dag_get_node(dag, ob1); - if(pd->forcefield==PFIELD_GUIDE) - dag_add_relation(dag, node2, node, DAG_RL_DATA_DATA|DAG_RL_OB_DATA); - else - dag_add_relation(dag, node2, node, DAG_RL_OB_DATA); - } - } - - pdEndEffectors(listb); /* restores copy... */ - } + dag_add_relation(dag,node2,node,DAG_RL_DATA_DATA|DAG_RL_OB_DATA, "Texture On Curve"); } } - + psys= ob->particlesystem.first; if(psys) { ParticleEffectorCache *nec; + GroupObject *go; for(; psys; psys=psys->next) { ParticleSettings *part= psys->part; - - dag_add_relation(dag, node, node, DAG_RL_OB_DATA); + + dag_add_relation(dag, node, node, DAG_RL_OB_DATA, "Particle-Object Relation"); + + if(psys->flag & PSYS_DISABLED || psys->flag & PSYS_DELETE) + continue; if(part->phystype==PART_PHYS_KEYED && psys->keyed_ob && BLI_findlink(&psys->keyed_ob->particlesystem,psys->keyed_psys-1)) { node2 = dag_get_node(dag, psys->keyed_ob); - dag_add_relation(dag, node2, node, DAG_RL_DATA_DATA); + dag_add_relation(dag, node2, node, DAG_RL_DATA_DATA, "Particle Keyed Physics"); + } + + if(part->draw_as == PART_DRAW_OB && part->dup_ob) { + node2 = dag_get_node(dag, part->dup_ob); + dag_add_relation(dag, node, node2, DAG_RL_OB_OB, "Particle Object Visualisation"); + if(part->dup_ob->type == OB_MBALL) + dag_add_relation(dag, node, node2, DAG_RL_DATA_DATA, "Particle Object Visualisation"); + } + + if(part->draw_as == PART_DRAW_GR && part->dup_group) { + for(go=part->dup_group->gobject.first; go; go=go->next) { + node2 = dag_get_node(dag, go->ob); + dag_add_relation(dag, node, node2, DAG_RL_OB_OB, "Particle Group Visualisation"); + } } if(psys->effectors.first) @@ -612,22 +621,22 @@ static void build_dag_object(DagForest *dag, DagNode *scenenode, Object *ob, int if(nec->type & PSYS_EC_EFFECTOR) { node2 = dag_get_node(dag, ob1); if(ob1->pd->forcefield==PFIELD_GUIDE) - dag_add_relation(dag, node2, node, DAG_RL_DATA_DATA|DAG_RL_OB_DATA); + dag_add_relation(dag, node2, node, DAG_RL_DATA_DATA|DAG_RL_OB_DATA, "Particle Field"); else - dag_add_relation(dag, node2, node, DAG_RL_OB_DATA); + dag_add_relation(dag, node2, node, DAG_RL_OB_DATA, "Particle Field"); } else if(nec->type & PSYS_EC_DEFLECT) { node2 = dag_get_node(dag, ob1); - dag_add_relation(dag, node2, node, DAG_RL_DATA_DATA|DAG_RL_OB_DATA); + dag_add_relation(dag, node2, node, DAG_RL_DATA_DATA|DAG_RL_OB_DATA, "Particle Collision"); } else if(nec->type & PSYS_EC_PARTICLE) { node2 = dag_get_node(dag, ob1); - dag_add_relation(dag, node2, node, DAG_RL_DATA_DATA); + dag_add_relation(dag, node2, node, DAG_RL_DATA_DATA, "Particle Field"); } if(nec->type & PSYS_EC_REACTOR) { node2 = dag_get_node(dag, ob1); - dag_add_relation(dag, node, node2, DAG_RL_DATA_DATA); + dag_add_relation(dag, node, node2, DAG_RL_DATA_DATA, "Particle Reactor"); } } } @@ -652,12 +661,12 @@ static void build_dag_object(DagForest *dag, DagNode *scenenode, Object *ob, int node2 = dag_get_node(dag, obt); if (ELEM(con->type, CONSTRAINT_TYPE_FOLLOWPATH, CONSTRAINT_TYPE_CLAMPTO)) - dag_add_relation(dag, node2, node, DAG_RL_DATA_OB|DAG_RL_OB_OB); + dag_add_relation(dag, node2, node, DAG_RL_DATA_OB|DAG_RL_OB_OB, cti->name); else { if (ELEM3(obt->type, OB_ARMATURE, OB_MESH, OB_LATTICE) && (ct->subtarget[0])) - dag_add_relation(dag, node2, node, DAG_RL_DATA_OB|DAG_RL_OB_OB); + dag_add_relation(dag, node2, node, DAG_RL_DATA_OB|DAG_RL_OB_OB, cti->name); else - dag_add_relation(dag, node2, node, DAG_RL_OB_OB); + dag_add_relation(dag, node2, node, DAG_RL_OB_OB, cti->name); } addtoroot = 0; } @@ -668,7 +677,7 @@ static void build_dag_object(DagForest *dag, DagNode *scenenode, Object *ob, int } if (addtoroot == 1 ) - dag_add_relation(dag,scenenode,node,DAG_RL_SCENE); + dag_add_relation(dag,scenenode,node,DAG_RL_SCENE, "Scene Relation"); } struct DagForest *build_dag(struct Scene *sce, short mask) @@ -859,12 +868,12 @@ DagNode * dag_get_sub_node (DagForest *forest,void * fob) return node; } -void dag_add_relation(DagForest *forest, DagNode *fob1, DagNode *fob2, short rel) +static void dag_add_parent_relation(DagForest *forest, DagNode *fob1, DagNode *fob2, short rel, char *name) { - DagAdjList *itA = fob1->child; + DagAdjList *itA = fob2->parent; while (itA) { /* search if relation exist already */ - if (itA->node == fob2) { + if (itA->node == fob1) { itA->type |= rel; itA->count += 1; return; @@ -873,19 +882,23 @@ void dag_add_relation(DagForest *forest, DagNode *fob1, DagNode *fob2, short rel } /* create new relation and insert at head. MALLOC alert! */ itA = MEM_mallocN(sizeof(DagAdjList),"DAG adj list"); - itA->node = fob2; + itA->node = fob1; itA->type = rel; itA->count = 1; - itA->next = fob1->child; - fob1->child = itA; + itA->next = fob2->parent; + itA->name = name; + fob2->parent = itA; } -static void dag_add_parent_relation(DagForest *forest, DagNode *fob1, DagNode *fob2, short rel) +void dag_add_relation(DagForest *forest, DagNode *fob1, DagNode *fob2, short rel, char *name) { - DagAdjList *itA = fob2->parent; + DagAdjList *itA = fob1->child; + /* parent relation is for cycle checking */ + dag_add_parent_relation(forest, fob1, fob2, rel, name); + while (itA) { /* search if relation exist already */ - if (itA->node == fob1) { + if (itA->node == fob2) { itA->type |= rel; itA->count += 1; return; @@ -894,13 +907,127 @@ static void dag_add_parent_relation(DagForest *forest, DagNode *fob1, DagNode *f } /* create new relation and insert at head. MALLOC alert! */ itA = MEM_mallocN(sizeof(DagAdjList),"DAG adj list"); - itA->node = fob1; + itA->node = fob2; itA->type = rel; itA->count = 1; - itA->next = fob2->parent; - fob2->parent = itA; + itA->next = fob1->child; + itA->name = name; + fob1->child = itA; } +static char *dag_node_name(DagNode *node) +{ + if(node->ob == NULL) + return "null"; + else if(ugly_hack_sorry) + return ((ID*)(node->ob))->name+2; + else + return ((bPoseChannel*)(node->ob))->name; +} + +#if 0 +static void dag_node_print_dependencies(DagNode *node) +{ + DagAdjList *itA; + + printf("%s depends on:\n", dag_node_name(node)); + + for(itA= node->parent; itA; itA= itA->next) + printf(" %s through %s\n", dag_node_name(itA->node), itA->name); + printf("\n"); +} +#endif + +static int dag_node_print_dependency_recurs(DagNode *node, DagNode *endnode) +{ + DagAdjList *itA; + + if(node->color == DAG_BLACK) + return 0; + + node->color= DAG_BLACK; + + if(node == endnode) + return 1; + + for(itA= node->parent; itA; itA= itA->next) { + if(dag_node_print_dependency_recurs(itA->node, endnode)) { + printf(" %s depends on %s through %s.\n", dag_node_name(node), dag_node_name(itA->node), itA->name); + return 1; + } + } + + return 0; +} + +static void dag_node_print_dependency_cycle(DagForest *dag, DagNode *startnode, DagNode *endnode, char *name) +{ + DagNode *node; + + for(node = dag->DagNode.first; node; node= node->next) + node->color= DAG_WHITE; + + printf(" %s depends on %s through %s.\n", dag_node_name(endnode), dag_node_name(startnode), name); + dag_node_print_dependency_recurs(startnode, endnode); + printf("\n"); +} + +static int dag_node_recurs_level(DagNode *node, int level) +{ + DagAdjList *itA; + int newlevel; + + node->color= DAG_BLACK; /* done */ + newlevel= ++level; + + for(itA= node->parent; itA; itA= itA->next) { + if(itA->node->color==DAG_WHITE) { + itA->node->ancestor_count= dag_node_recurs_level(itA->node, level); + newlevel= MAX2(newlevel, level+itA->node->ancestor_count); + } + else + newlevel= MAX2(newlevel, level+itA->node->ancestor_count); + } + + return newlevel; +} + +static void dag_check_cycle(DagForest *dag) +{ + DagNode *node; + DagAdjList *itA; + + /* tag nodes unchecked */ + for(node = dag->DagNode.first; node; node= node->next) + node->color= DAG_WHITE; + + for(node = dag->DagNode.first; node; node= node->next) { + if(node->color==DAG_WHITE) { + node->ancestor_count= dag_node_recurs_level(node, 0); + } + } + + /* check relations, and print errors */ + for(node = dag->DagNode.first; node; node= node->next) { + for(itA= node->parent; itA; itA= itA->next) { + if(itA->node->ancestor_count > node->ancestor_count) { + if(node->ob && itA->node->ob) { + printf("Dependency cycle detected:\n"); + dag_node_print_dependency_cycle(dag, itA->node, node, itA->name); + } + } + } + } + + /* parent relations are only needed for cycle checking, so free now */ + for(node = dag->DagNode.first; node; node= node->next) { + while (node->parent) { + itA = node->parent->next; + MEM_freeN(node->parent); + node->parent = itA; + } + } +} /* * MainDAG is the DAG of all objects in current scene @@ -1539,6 +1666,8 @@ void DAG_scene_sort(struct Scene *sce) build_dag(sce, DAG_RL_ALL_BUT_DATA); + dag_check_cycle(sce->theDag); + nqueue = queue_create(DAGQUEUEALLOC); for(node = sce->theDag->DagNode.first; node; node= node->next) { @@ -1630,11 +1759,12 @@ static void flush_update_node(DagNode *node, unsigned int layer, int curtime) ob= node->ob; if(ob && (ob->recalc & OB_RECALC)) { all_layer= ob->lay; + /* got an object node that changes, now check relations */ for(itA = node->child; itA; itA= itA->next) { all_layer |= itA->lay; /* the relationship is visible */ - if(itA->lay & layer) { + if((itA->lay & layer) || (itA->node->ob == G.obedit)) { if(itA->node->type==ID_OB) { obc= itA->node->ob; oldflag= obc->recalc; @@ -1665,7 +1795,7 @@ static void flush_update_node(DagNode *node, unsigned int layer, int curtime) } } /* even nicer, we can clear recalc flags... */ - if((all_layer & layer)==0) { + if((all_layer & layer)==0 && (ob != G.obedit)) { /* but existing displaylists or derivedmesh should be freed */ if(ob->recalc & OB_RECALC_DATA) object_free_display(ob); @@ -1679,7 +1809,7 @@ static void flush_update_node(DagNode *node, unsigned int layer, int curtime) /* could merge this in with loop above...? (ton) */ for(itA = node->child; itA; itA= itA->next) { /* the relationship is visible */ - if(itA->lay & layer) { + if((itA->lay & layer) || (itA->node->ob == G.obedit)) { if(itA->node->type==ID_OB) { obc= itA->node->ob; /* child moves */ @@ -1703,18 +1833,24 @@ static void flush_update_node(DagNode *node, unsigned int layer, int curtime) } /* node was checked to have lasttime != curtime , and is of type ID_OB */ -static unsigned int flush_layer_node(DagNode *node, int curtime) +static unsigned int flush_layer_node(Scene *sce, DagNode *node, int curtime) { + Base *base; DagAdjList *itA; node->lasttime= curtime; - node->lay= ((Object *)node->ob)->lay; + node->lay= 0; + for(base= sce->base.first; base; base= base->next) { + if(node->ob == base->object) { + node->lay= ((Object *)node->ob)->lay; + break; + } + } for(itA = node->child; itA; itA= itA->next) { if(itA->node->type==ID_OB) { if(itA->node->lasttime!=curtime) { - itA->lay= flush_layer_node(itA->node, curtime); // lay is only set once for each relation - //printf("layer %d for relation %s to %s\n", itA->lay, ((Object *)node->ob)->id.name, ((Object *)itA->node->ob)->id.name); + itA->lay= flush_layer_node(sce, itA->node, curtime); // lay is only set once for each relation } else itA->lay= itA->node->lay; @@ -1725,11 +1861,38 @@ static unsigned int flush_layer_node(DagNode *node, int curtime) return node->lay; } +/* node was checked to have lasttime != curtime , and is of type ID_OB */ +static void flush_pointcache_reset(DagNode *node, int curtime, int reset) +{ + DagAdjList *itA; + Object *ob; + + node->lasttime= curtime; + + for(itA = node->child; itA; itA= itA->next) { + if(itA->node->type==ID_OB) { + if(itA->node->lasttime!=curtime) { + ob= (Object*)(node->ob); + + if(reset || (ob->recalc & OB_RECALC)) { + if(BKE_ptcache_object_reset(ob, PTCACHE_RESET_DEPSGRAPH)) + ob->recalc |= OB_RECALC_DATA; + + flush_pointcache_reset(itA->node, curtime, 1); + } + else + flush_pointcache_reset(itA->node, curtime, 0); + } + } + } +} + /* flushes all recalc flags in objects down the dependency tree */ -void DAG_scene_flush_update(Scene *sce, unsigned int lay) +void DAG_scene_flush_update(Scene *sce, unsigned int lay, int time) { DagNode *firstnode; DagAdjList *itA; + Object *ob; int lasttime; if(sce->theDag==NULL) { @@ -1738,21 +1901,43 @@ void DAG_scene_flush_update(Scene *sce, unsigned int lay) } firstnode= sce->theDag->DagNode.first; // always scene node + + for(itA = firstnode->child; itA; itA= itA->next) + itA->lay= 0; /* first we flush the layer flags */ sce->theDag->time++; // so we know which nodes were accessed lasttime= sce->theDag->time; - for(itA = firstnode->child; itA; itA= itA->next) { + + for(itA = firstnode->child; itA; itA= itA->next) if(itA->node->lasttime!=lasttime && itA->node->type==ID_OB) - flush_layer_node(itA->node, lasttime); - } + flush_layer_node(sce, itA->node, lasttime); /* then we use the relationships + layer info to flush update events */ sce->theDag->time++; // so we know which nodes were accessed lasttime= sce->theDag->time; - for(itA = firstnode->child; itA; itA= itA->next) { - if(itA->node->lasttime!=lasttime && itA->node->type==ID_OB) + for(itA = firstnode->child; itA; itA= itA->next) + if(itA->node->lasttime!=lasttime && itA->node->type==ID_OB) flush_update_node(itA->node, lay, lasttime); + + /* if update is not due to time change, do pointcache clears */ + if(!time) { + sce->theDag->time++; // so we know which nodes were accessed + lasttime= sce->theDag->time; + for(itA = firstnode->child; itA; itA= itA->next) { + if(itA->node->lasttime!=lasttime && itA->node->type==ID_OB) { + ob= (Object*)(itA->node->ob); + + if(ob->recalc & OB_RECALC) { + if(BKE_ptcache_object_reset(ob, PTCACHE_RESET_DEPSGRAPH)) + ob->recalc |= OB_RECALC_DATA; + + flush_pointcache_reset(itA->node, lasttime, 1); + } + else + flush_pointcache_reset(itA->node, lasttime, 0); + } + } } } @@ -1807,8 +1992,10 @@ static void dag_object_time_update_flags(Object *ob) } } } - else if(ob->scriptlink.totscript) ob->recalc |= OB_RECALC_OB; - else if(ob->parent) { + + if(ob->scriptlink.totscript) ob->recalc |= OB_RECALC_OB; + + if(ob->parent) { /* motion path or bone child */ if(ob->parent->type==OB_CURVE || ob->parent->type==OB_ARMATURE) ob->recalc |= OB_RECALC_OB; } @@ -1829,9 +2016,11 @@ static void dag_object_time_update_flags(Object *ob) } } } - else if(modifiers_isSoftbodyEnabled(ob)) ob->recalc |= OB_RECALC_DATA; - else if(object_modifiers_use_time(ob)) ob->recalc |= OB_RECALC_DATA; - else { + + if(object_modifiers_use_time(ob)) ob->recalc |= OB_RECALC_DATA; + if((ob->pose) && (ob->pose->flag & POSE_CONSTRAINTS_TIMEDEPEND)) ob->recalc |= OB_RECALC_DATA; + + { Mesh *me; Curve *cu; Lattice *lt; @@ -1845,21 +2034,6 @@ static void dag_object_time_update_flags(Object *ob) ob->shapeflag &= ~OB_SHAPE_TEMPLOCK; } } - else if(ob->effect.first) { - Effect *eff= ob->effect.first; - PartEff *paf= give_parteff(ob); - - if(eff->type==EFF_WAVE) - ob->recalc |= OB_RECALC_DATA; - else if(paf && paf->keys==NULL) - ob->recalc |= OB_RECALC_DATA; - } - if((ob->fluidsimFlag & OB_FLUIDSIM_ENABLE) && (ob->fluidsimSettings)) { - // fluidsimSettings might not be initialized during load... - if(ob->fluidsimSettings->type & (OB_FLUIDSIM_DOMAIN|OB_FLUIDSIM_PARTICLE)) { - ob->recalc |= OB_RECALC_DATA; // NT FSPARTICLE - } - } if(ob->particlesystem.first) ob->recalc |= OB_RECALC_DATA; break; @@ -1937,7 +2111,7 @@ void DAG_scene_update_flags(Scene *scene, unsigned int lay) } for(sce= scene; sce; sce= sce->set) - DAG_scene_flush_update(sce, lay); + DAG_scene_flush_update(sce, lay, 1); /* test: set time flag, to disable baked systems to update */ for(SETLOOPER(scene, base)) { @@ -1987,7 +2161,9 @@ void DAG_object_flush_update(Scene *sce, Object *ob, short flag) { if(ob==NULL || sce->theDag==NULL) return; + ob->recalc |= flag; + BKE_ptcache_object_reset(ob, PTCACHE_RESET_DEPSGRAPH); /* all users of this ob->data should be checked */ /* BUT! displists for curves are still only on cu */ @@ -2000,8 +2176,9 @@ void DAG_object_flush_update(Scene *sce, Object *ob, short flag) else { Object *obt; for (obt=G.main->object.first; obt; obt= obt->id.next) { - if (obt->data==ob->data) { + if (obt != ob && obt->data==ob->data) { obt->recalc |= OB_RECALC_DATA; + BKE_ptcache_object_reset(obt, PTCACHE_RESET_DEPSGRAPH); } } } @@ -2010,9 +2187,9 @@ void DAG_object_flush_update(Scene *sce, Object *ob, short flag) } if(G.curscreen) - DAG_scene_flush_update(sce, dag_screen_view3d_layers()); + DAG_scene_flush_update(sce, dag_screen_view3d_layers(), 0); else - DAG_scene_flush_update(sce, sce->lay); + DAG_scene_flush_update(sce, sce->lay, 0); } /* recursively descends tree, each node only checked once */ @@ -2060,8 +2237,8 @@ void DAG_object_update_flags(Scene *sce, Object *ob, unsigned int lay) /* object not in scene? then handle group exception. needs to be dagged once too */ if(node==NULL) { - Group *group= find_group(ob); - if(group) { + Group *group= NULL; + while( (group = find_group(ob, group)) ) { GroupObject *go; /* primitive; tag all... this call helps building groups for particles */ for(go= group->gobject.first; go; go= go->next) @@ -2094,51 +2271,6 @@ void DAG_object_update_flags(Scene *sce, Object *ob, unsigned int lay) /* ******************* DAG FOR ARMATURE POSE ***************** */ -static int node_recurs_level(DagNode *node, int level) -{ - DagAdjList *itA; - - node->color= DAG_BLACK; /* done */ - level++; - - for(itA= node->parent; itA; itA= itA->next) { - if(itA->node->color==DAG_WHITE) - itA->node->ancestor_count= node_recurs_level(itA->node, level); - } - - return level; -} - -static void pose_check_cycle(DagForest *dag) -{ - DagNode *node; - DagAdjList *itA; - - /* tag nodes unchecked */ - for(node = dag->DagNode.first; node; node= node->next) - node->color= DAG_WHITE; - - for(node = dag->DagNode.first; node; node= node->next) { - if(node->color==DAG_WHITE) { - node->ancestor_count= node_recurs_level(node, 0); - } - } - - /* check relations, and print errors */ - for(node = dag->DagNode.first; node; node= node->next) { - for(itA= node->parent; itA; itA= itA->next) { - if(itA->node->ancestor_count > node->ancestor_count) { - bPoseChannel *pchan= (bPoseChannel *)node->ob; - bPoseChannel *parchan= (bPoseChannel *)itA->node->ob; - - if(pchan && parchan) - if(pchan->parent!=parchan) - printf("Cycle in %s to %s\n", pchan->name, parchan->name); - } - } - } -} - /* we assume its an armature with pose */ void DAG_pose_sort(Object *ob) { @@ -2167,8 +2299,7 @@ void DAG_pose_sort(Object *ob) if(pchan->parent) { node2 = dag_get_node(dag, pchan->parent); - dag_add_relation(dag, node2, node, 0); - dag_add_parent_relation(dag, node2, node, 0); + dag_add_relation(dag, node2, node, 0, "Parent Relation"); addtoroot = 0; } for (con = pchan->constraints.first; con; con=con->next) { @@ -2179,12 +2310,14 @@ void DAG_pose_sort(Object *ob) if(con->ipo) { IpoCurve *icu; for(icu= con->ipo->curve.first; icu; icu= icu->next) { - if(icu->driver && icu->driver->ob==ob) { + /* icu->driver->ob should actually point to ob->proxy if it + * is a proxy, but since it wasn't set correct it older + * files comparing with ob->proxy makes it work for those */ + if(icu->driver && (icu->driver->ob==ob || icu->driver->ob==ob->proxy)) { bPoseChannel *target= get_pose_channel(ob->pose, icu->driver->name); if(target) { node2 = dag_get_node(dag, target); - dag_add_relation(dag, node2, node, 0); - dag_add_parent_relation(dag, node2, node, 0); + dag_add_relation(dag, node2, node, 0, "Ipo Driver"); /* uncommented this line, results in dependencies * not being added properly for this constraint, @@ -2203,9 +2336,8 @@ void DAG_pose_sort(Object *ob) bPoseChannel *target= get_pose_channel(ob->pose, ct->subtarget); if (target) { node2= dag_get_node(dag, target); - dag_add_relation(dag, node2, node, 0); - dag_add_parent_relation(dag, node2, node, 0); - + dag_add_relation(dag, node2, node, 0, "IK Constraint"); + if (con->type==CONSTRAINT_TYPE_KINEMATIC) { bKinematicConstraint *data = (bKinematicConstraint *)con->data; bPoseChannel *parchan; @@ -2220,8 +2352,7 @@ void DAG_pose_sort(Object *ob) /* Walk to the chain's root */ while (parchan) { node3= dag_get_node(dag, parchan); - dag_add_relation(dag, node2, node3, 0); - dag_add_parent_relation(dag, node2, node3, 0); + dag_add_relation(dag, node2, node3, 0, "IK Constraint"); segcount++; if (segcount==data->rootbone || segcount>255) break; // 255 is weak @@ -2237,12 +2368,11 @@ void DAG_pose_sort(Object *ob) } } if (addtoroot == 1 ) { - dag_add_relation(dag, rootnode, node, 0); - dag_add_parent_relation(dag, rootnode, node, 0); + dag_add_relation(dag, rootnode, node, 0, "Root Bone Relation"); } } - pose_check_cycle(dag); + dag_check_cycle(dag); /* now we try to sort... */ tempbase.first= tempbase.last= NULL; diff --git a/source/blender/blenkernel/intern/displist.c b/source/blender/blenkernel/intern/displist.c index 6071ce1c871..44aa439ee3e 100644 --- a/source/blender/blenkernel/intern/displist.c +++ b/source/blender/blenkernel/intern/displist.c @@ -87,6 +87,8 @@ #include "RE_pipeline.h" #include "RE_shader_ext.h" +#include "BLO_sys_types.h" // for intptr_t support + static void boundbox_displist(Object *ob); @@ -203,8 +205,9 @@ void addnormalsDispList(Object *ob, ListBase *lb) ndata= dl->nors; for(a=0; a<dl->parts; a++) { - - DL_SURFINDEX(dl->flag & DL_CYCL_U, dl->flag & DL_CYCL_V, dl->nr, dl->parts); + + if (surfindex_displist(dl, a, &b, &p1, &p2, &p3, &p4)==0) + break; v1= vdata+ 3*p1; n1= ndata+ 3*p1; @@ -268,6 +271,33 @@ void count_displist(ListBase *lb, int *totvert, int *totface) } } +int surfindex_displist(DispList *dl, int a, int *b, int *p1, int *p2, int *p3, int *p4) +{ + if((dl->flag & DL_CYCL_V)==0 && a==(dl->parts)-1) { + return 0; + } + + if(dl->flag & DL_CYCL_U) { + (*p1)= dl->nr*a; + (*p2)= (*p1)+ dl->nr-1; + (*p3)= (*p1)+ dl->nr; + (*p4)= (*p2)+ dl->nr; + (*b)= 0; + } else { + (*p2)= dl->nr*a; + (*p1)= (*p2)+1; + (*p4)= (*p2)+ dl->nr; + (*p3)= (*p1)+ dl->nr; + (*b)= 1; + } + + if( (dl->flag & DL_CYCL_V) && a==dl->parts-1) { \ + (*p3)-= dl->nr*dl->parts; \ + (*p4)-= dl->nr*dl->parts; \ + } + + return 1; +} /* ***************************** shade displist. note colors now are in rgb(a) order ******************** */ @@ -758,7 +788,10 @@ void reshadeall_displist(void) for(base= G.scene->base.first; base; base= base->next) { ob= base->object; - freedisplist(&ob->disp); + + if(ELEM5(ob->type, OB_MESH, OB_CURVE, OB_SURF, OB_FONT, OB_MBALL)) + freedisplist(&ob->disp); + if(base->lay & G.scene->lay) { /* Metaballs have standard displist at the Object */ if(ob->type==OB_MBALL) shadeDispList(base); @@ -786,7 +819,7 @@ static void curve_to_displist(Curve *cu, ListBase *nubase, ListBase *dispbase) else resolu= nu->resolu; - if(nu->pntsu<2); + if(!check_valid_nurb_u(nu)); else if((nu->type & 7)==CU_BEZIER) { /* count */ @@ -819,7 +852,7 @@ static void curve_to_displist(Curve *cu, ListBase *nubase, ListBase *dispbase) data= dl->verts; - if(nu->flagu & 1) { + if(nu->flagu & CU_CYCLIC) { dl->type= DL_POLY; a= nu->pntsu; } @@ -856,19 +889,22 @@ static void curve_to_displist(Curve *cu, ListBase *nubase, ListBase *dispbase) } } else if((nu->type & 7)==CU_NURBS) { - len= nu->pntsu*resolu; + len= (resolu*SEGMENTSU(nu)); + if((nu->flagu & CU_CYCLIC)==0) len++; + dl= MEM_callocN(sizeof(DispList), "makeDispListsurf"); dl->verts= MEM_callocN(len*3*sizeof(float), "dlverts"); BLI_addtail(dispbase, dl); dl->parts= 1; + dl->nr= len; dl->col= nu->mat_nr; dl->charidx = nu->charidx; data= dl->verts; - if(nu->flagu & 1) dl->type= DL_POLY; + if(nu->flagu & CU_CYCLIC) dl->type= DL_POLY; else dl->type= DL_SEGM; - makeNurbcurve(nu, data, resolu, 3); + makeNurbcurve(nu, data, NULL, NULL, resolu); } else if((nu->type & 7)==CU_POLY) { len= nu->pntsu; @@ -881,7 +917,7 @@ static void curve_to_displist(Curve *cu, ListBase *nubase, ListBase *dispbase) dl->charidx = nu->charidx; data= dl->verts; - if(nu->flagu & 1) dl->type= DL_POLY; + if(nu->flagu & CU_CYCLIC) dl->type= DL_POLY; else dl->type= DL_SEGM; a= len; @@ -989,9 +1025,9 @@ void filldisplist(ListBase *dispbase, ListBase *to) efa= fillfacebase.first; index= dlnew->index; while(efa) { - index[0]= (long)efa->v1->tmp.l; - index[1]= (long)efa->v2->tmp.l; - index[2]= (long)efa->v3->tmp.l; + index[0]= (intptr_t)efa->v1->tmp.l; + index[1]= (intptr_t)efa->v2->tmp.l; + index[2]= (intptr_t)efa->v3->tmp.l; index+= 3; efa= efa->next; @@ -1174,7 +1210,7 @@ static ModifierData *curve_get_tesselate_point(Object *ob, int forRender, int ed if ((md->mode & required_mode) != required_mode) continue; if (mti->isDisabled && mti->isDisabled(md)) continue; - if (md->type==eModifierType_Hook || md->type==eModifierType_Softbody) { + if (ELEM3(md->type, eModifierType_Hook, eModifierType_Softbody, eModifierType_MeshDeform)) { preTesselatePoint = md; } } @@ -1231,7 +1267,7 @@ void curve_calc_modifiers_pre(Object *ob, ListBase *nurb, int forRender, float ( *numVerts_r = numVerts; } -void curve_calc_modifiers_post(Object *ob, ListBase *nurb, ListBase *dispbase, int forRender, float (*originalVerts)[3], float (*deformedVerts)[3]) +static void curve_calc_modifiers_post(Object *ob, ListBase *nurb, ListBase *dispbase, int forRender, float (*originalVerts)[3], float (*deformedVerts)[3]) { int editmode = (!forRender && ob==G.obedit); ModifierData *md = modifiers_getVirtualModifierList(ob); @@ -1250,13 +1286,40 @@ void curve_calc_modifiers_post(Object *ob, ListBase *nurb, ListBase *dispbase, i for (; md; md=md->next) { ModifierTypeInfo *mti = modifierType_getInfo(md->type); - + if ((md->mode & required_mode) != required_mode) continue; if (mti->isDisabled && mti->isDisabled(md)) continue; if (mti->type!=eModifierTypeType_OnlyDeform && mti->type!=eModifierTypeType_DeformOrConstruct) continue; - for (dl=dispbase->first; dl; dl=dl->next) { - mti->deformVerts(md, ob, NULL, (float(*)[3]) dl->verts, (dl->type==DL_INDEX3)?dl->nr:dl->parts*dl->nr); + /* need to put all verts in 1 block for curve deform */ + if(md->type==eModifierType_Curve) { + float *allverts, *fp; + int totvert= 0; + + for (dl=dispbase->first; dl; dl=dl->next) + totvert+= (dl->type==DL_INDEX3)?dl->nr:dl->parts*dl->nr; + + fp= allverts= MEM_mallocN(totvert*sizeof(float)*3, "temp vert"); + for (dl=dispbase->first; dl; dl=dl->next) { + int offs= 3 * ((dl->type==DL_INDEX3)?dl->nr:dl->parts*dl->nr); + memcpy(fp, dl->verts, sizeof(float) * offs); + fp+= offs; + } + + mti->deformVerts(md, ob, NULL, (float(*)[3]) allverts, totvert); + + fp= allverts; + for (dl=dispbase->first; dl; dl=dl->next) { + int offs= 3 * ((dl->type==DL_INDEX3)?dl->nr:dl->parts*dl->nr); + memcpy(dl->verts, fp, sizeof(float) * offs); + fp+= offs; + } + MEM_freeN(allverts); + } + else { + for (dl=dispbase->first; dl; dl=dl->next) { + mti->deformVerts(md, ob, NULL, (float(*)[3]) dl->verts, (dl->type==DL_INDEX3)?dl->nr:dl->parts*dl->nr); + } } } @@ -1278,7 +1341,8 @@ static void displist_surf_indices(DispList *dl) for(a=0; a<dl->parts; a++) { - DL_SURFINDEX(dl->flag & DL_CYCL_U, dl->flag & DL_CYCL_V, dl->nr, dl->parts); + if (surfindex_displist(dl, a, &b, &p1, &p2, &p3, &p4)==0) + break; for(; b<dl->nr; b++, index+=4) { index[0]= p1; @@ -1333,13 +1397,13 @@ void makeDispListSurf(Object *ob, ListBase *dispbase, int forRender) dl->rt= nu->flag; data= dl->verts; - if(nu->flagu & 1) dl->type= DL_POLY; + if(nu->flagu & CU_CYCLIC) dl->type= DL_POLY; else dl->type= DL_SEGM; - makeNurbcurve(nu, data, nu->resolu, 3); + makeNurbcurve(nu, data, NULL, NULL, nu->resolu); } else { - len= nu->resolu*nu->resolv; + len= (nu->pntsu*nu->resolu) * (nu->pntsv*nu->resolv); dl= MEM_callocN(sizeof(DispList), "makeDispListsurf"); dl->verts= MEM_callocN(len*3*sizeof(float), "dlverts"); @@ -1351,9 +1415,9 @@ void makeDispListSurf(Object *ob, ListBase *dispbase, int forRender) data= dl->verts; dl->type= DL_SURF; - - dl->parts= nu->resolu; /* in reverse, because makeNurbfaces works that way */ - dl->nr= nu->resolv; + + dl->parts= (nu->pntsu*nu->resolu); /* in reverse, because makeNurbfaces works that way */ + dl->nr= (nu->pntsv*nu->resolv); if(nu->flagv & CU_CYCLIC) dl->flag|= DL_CYCL_U; /* reverse too! */ if(nu->flagu & CU_CYCLIC) dl->flag|= DL_CYCL_V; @@ -1483,7 +1547,7 @@ void makeDispListCurveTypes(Object *ob, int forOrco) float fac=1.0; if (cu->taperobj==NULL) { if ( (cu->bevobj!=NULL) || !((cu->flag & CU_FRONT) || (cu->flag & CU_BACK)) ) - fac = calc_curve_subdiv_radius(cu, nu, a); + fac = bevp->radius; } else { fac = calc_taper(cu->taperobj, a, bl->nr); } diff --git a/source/blender/blenkernel/intern/effect.c b/source/blender/blenkernel/intern/effect.c index 881e67eef8a..66e8a039dda 100644 --- a/source/blender/blenkernel/intern/effect.c +++ b/source/blender/blenkernel/intern/effect.c @@ -58,6 +58,7 @@ #include "BKE_anim.h" /* needed for where_on_path */ #include "BKE_armature.h" #include "BKE_blender.h" +#include "BKE_collision.h" #include "BKE_constraint.h" #include "BKE_deform.h" #include "BKE_depsgraph.h" @@ -98,43 +99,19 @@ typedef struct VeNoCo { float co[3], no[3]; } VeNoCo; -Effect *add_effect(int type) +/* ***************** PARTICLES ***************** */ + +/* deprecated, only keep this for readfile.c */ +PartEff *give_parteff(Object *ob) { - Effect *eff=0; PartEff *paf; - int a; - switch(type) { - case EFF_PARTICLE: - paf= MEM_callocN(sizeof(PartEff), "neweff"); - eff= (Effect *)paf; - - paf->sta= 1.0; - paf->end= 100.0; - paf->lifetime= 50.0; - for(a=0; a<PAF_MAXMULT; a++) { - paf->life[a]= 50.0; - paf->child[a]= 4; - paf->mat[a]= 1; - } - - paf->totpart= 1000; - paf->totkey= 8; - paf->staticstep= 5; - paf->defvec[2]= 1.0f; - paf->nabla= 0.05f; - paf->disp = 100; - paf->speedtex = 8; - paf->omat = 1; - paf->flag= PAF_FACE; - - break; + paf= ob->effect.first; + while(paf) { + if(paf->type==EFF_PARTICLE) return paf; + paf= paf->next; } - - eff->type= eff->buttype= type; - eff->flag |= SELECT; - - return eff; + return 0; } void free_effect(Effect *eff) @@ -161,172 +138,6 @@ void free_effects(ListBase *lb) } } -Effect *copy_effect(Effect *eff) -{ - Effect *effn; - - effn= MEM_dupallocN(eff); - if(effn->type==EFF_PARTICLE) ((PartEff *)effn)->keys= 0; - - return effn; -} - -void copy_act_effect(Object *ob) -{ - /* return a copy of the active effect */ - Effect *effn, *eff; - - eff= ob->effect.first; - while(eff) { - if(eff->flag & SELECT) { - - effn= copy_effect(eff); - BLI_addtail(&ob->effect, effn); - - eff->flag &= ~SELECT; - return; - - } - eff= eff->next; - } - - /* when it comes here: add new effect */ - eff= add_effect(EFF_PARTICLE); - BLI_addtail(&ob->effect, eff); - -} - -void copy_effects(ListBase *lbn, ListBase *lb) -{ - Effect *eff, *effn; - - lbn->first= lbn->last= 0; - - eff= lb->first; - while(eff) { - effn= copy_effect(eff); - BLI_addtail(lbn, effn); - - eff= eff->next; - } - -} - -void deselectall_eff(Object *ob) -{ - Effect *eff= ob->effect.first; - - while(eff) { - eff->flag &= ~SELECT; - eff= eff->next; - } -} - -/* ***************** PARTICLES ***************** */ - -static Particle *new_particle(PartEff *paf) -{ - static Particle *pa; - static int cur; - - /* we agree: when paf->keys==0: alloc */ - if(paf->keys==NULL) { - pa= paf->keys= MEM_callocN( paf->totkey*paf->totpart*sizeof(Particle), "particlekeys" ); - cur= 0; - } - else { - if(cur && cur<paf->totpart) pa+=paf->totkey; - cur++; - } - return pa; -} - -PartEff *give_parteff(Object *ob) -{ - PartEff *paf; - - paf= ob->effect.first; - while(paf) { - if(paf->type==EFF_PARTICLE) return paf; - paf= paf->next; - } - return 0; -} - -void where_is_particle(PartEff *paf, Particle *pa, float ctime, float *vec) -{ - Particle *p[4]; - float dt, t[4]; - int a; - - if(paf->totkey==1 || ctime < pa->time) { - VECCOPY(vec, pa->co); - return; - } - - /* first find the first particlekey */ - a= (int)((paf->totkey-1)*(ctime-pa->time)/pa->lifetime); - if(a>=paf->totkey) a= paf->totkey-1; - else if(a<0) a= 0; - - pa+= a; - - if(a>0) p[0]= pa-1; else p[0]= pa; - p[1]= pa; - - if(a+1<paf->totkey) p[2]= pa+1; else p[2]= pa; - if(a+2<paf->totkey) p[3]= pa+2; else p[3]= p[2]; - - if(p[1]==p[2] || p[2]->time == p[1]->time) dt= 0.0; - else dt= (ctime-p[1]->time)/(p[2]->time - p[1]->time); - - if(paf->flag & PAF_BSPLINE) set_four_ipo(dt, t, KEY_BSPLINE); - else set_four_ipo(dt, t, KEY_CARDINAL); - - vec[0]= t[0]*p[0]->co[0] + t[1]*p[1]->co[0] + t[2]*p[2]->co[0] + t[3]*p[3]->co[0]; - vec[1]= t[0]*p[0]->co[1] + t[1]*p[1]->co[1] + t[2]*p[2]->co[1] + t[3]*p[3]->co[1]; - vec[2]= t[0]*p[0]->co[2] + t[1]*p[1]->co[2] + t[2]*p[2]->co[2] + t[3]*p[3]->co[2]; - -} - -static void particle_tex(MTex *mtex, PartEff *paf, float *co, float *no) -{ - float tin, tr, tg, tb, ta; - float old; - - externtex(mtex, co, &tin, &tr, &tg, &tb, &ta); - - if(paf->texmap==PAF_TEXINT) { - tin*= paf->texfac; - no[0]+= tin*paf->defvec[0]; - no[1]+= tin*paf->defvec[1]; - no[2]+= tin*paf->defvec[2]; - } - else if(paf->texmap==PAF_TEXRGB) { - no[0]+= (tr-0.5f)*paf->texfac; - no[1]+= (tg-0.5f)*paf->texfac; - no[2]+= (tb-0.5f)*paf->texfac; - } - else { /* PAF_TEXGRAD */ - - old= tin; - co[0]+= paf->nabla; - externtex(mtex, co, &tin, &tr, &tg, &tb, &ta); - no[0]+= (old-tin)*paf->texfac; - - co[0]-= paf->nabla; - co[1]+= paf->nabla; - externtex(mtex, co, &tin, &tr, &tg, &tb, &ta); - no[1]+= (old-tin)*paf->texfac; - - co[1]-= paf->nabla; - co[2]+= paf->nabla; - externtex(mtex, co, &tin, &tr, &tg, &tb, &ta); - no[2]+= (old-tin)*paf->texfac; - - } -} - /* -------------------------- Effectors ------------------ */ static void add_to_effectorcache(ListBase *lb, Object *ob, Object *obsrc) @@ -349,6 +160,13 @@ static void add_to_effectorcache(ListBase *lb, Object *ob, Object *obsrc) } } else if(pd->forcefield) { + + if(pd->forcefield == PFIELD_WIND) + { + pd->rng = rng_new(1); + rng_srandom(pd->rng, (unsigned int)(ceil(PIL_check_seconds_timer()))); // use better seed + } + ec= MEM_callocN(sizeof(pEffectorCache), "effector cache"); ec->ob= ob; BLI_addtail(lb, ec); @@ -397,1713 +215,354 @@ void pdEndEffectors(ListBase *lb) pEffectorCache *ec; /* restore full copy */ for(ec= lb->first; ec; ec= ec->next) + { + if(ec->ob->pd && (ec->ob->pd->forcefield == PFIELD_WIND)) + rng_free(ec->ob->pd->rng); + *(ec->ob)= ec->obcopy; + } BLI_freelistN(lb); } } -/* local for this c file, only for guides now */ -static void precalc_effectors(Object *ob, PartEff *paf, Particle *pa, ListBase *lb) -{ - pEffectorCache *ec; - - for(ec= lb->first; ec; ec= ec->next) { - PartDeflect *pd= ec->ob->pd; - - ec->oldspeed[0]= ec->oldspeed[1]= ec->oldspeed[2]= 0.0f; - - if(pd->forcefield==PFIELD_GUIDE && ec->ob->type==OB_CURVE) { - float vec[4], dir[3]; - - if(!(paf->flag & PAF_STATIC)) - where_is_object_time(ec->ob, pa->time); - - /* scale corrects speed vector to curve size */ - if(paf->totkey>1) ec->scale= (paf->totkey-1)/pa->lifetime; - else ec->scale= 1.0f; - - /* time_scale is for random life */ - if(pa->lifetime>paf->lifetime) - ec->time_scale= paf->lifetime/pa->lifetime; - else - ec->time_scale= pa->lifetime/paf->lifetime; - - /* distance of first path point to particle origin */ - where_on_path(ec->ob, 0.0f, vec, dir); - VECCOPY(ec->oldloc, vec); /* store local coord for differences */ - Mat4MulVecfl(ec->ob->obmat, vec); - - /* for static we need to move to global space */ - if(paf->flag & PAF_STATIC) { - VECCOPY(dir, pa->co); - Mat4MulVecfl(ob->obmat, dir); - ec->guide_dist= VecLenf(vec, dir); - } - else - ec->guide_dist= VecLenf(vec, pa->co); - } - } -} +/************************************************/ +/* Effectors */ +/************************************************/ -/* -------- pdDoEffectors() -------- - generic force/speed system, now used for particles and softbodies - lb = listbase with objects that take part in effecting - opco = global coord, as input - force = force accumulator - speed = actual current speed which can be altered - cur_time = "external" time in frames, is constant for static particles - loc_time = "local" time in frames, range <0-1> for the lifetime of particle - par_layer = layer the caller is in - flags = only used for softbody wind now - guide = old speed of particle +// triangle - ray callback function +static void eff_tri_ray_hit(void *userdata, int index, const BVHTreeRay *ray, BVHTreeRayHit *hit) +{ + // whenever we hit a bounding box, we don't check further + hit->dist = -1; + hit->index = 1; +} -*/ -void pdDoEffectors(ListBase *lb, float *opco, float *force, float *speed, float cur_time, float loc_time, unsigned int flags) +// get visibility of a wind ray +static float eff_calc_visibility(Object *ob, float *co, float *dir) { -/* - Modifies the force on a particle according to its - relation with the effector object - Different kind of effectors include: - Forcefields: Gravity-like attractor - (force power is related to the inverse of distance to the power of a falloff value) - Vortex fields: swirling effectors - (particles rotate around Z-axis of the object. otherwise, same relation as) - (Forcefields, but this is not done through a force/acceleration) - Guide: particles on a path - (particles are guided along a curve bezier or old nurbs) - (is independent of other effectors) -*/ - Object *ob; - pEffectorCache *ec; - PartDeflect *pd; - float vect_to_vert[3]; - float f_force, force_vec[3]; - float *obloc; - float distance, force_val, ffall_val; - float guidecollect[3], guidedist= 0.0f; - int cur_frame; + CollisionModifierData **collobjs = NULL; + int numcollobj = 0, i; + float norm[3], len = 0.0; + float visibility = 1.0; - guidecollect[0]= guidecollect[1]= guidecollect[2]=0.0f; - - /* Cycle through collected objects, get total of (1/(gravity_strength * dist^gravity_power)) */ - /* Check for min distance here? (yes would be cool to add that, ton) */ + collobjs = get_collisionobjects(ob, &numcollobj); - for(ec = lb->first; ec; ec= ec->next) { - /* object effectors were fully checked to be OK to evaluate! */ - ob= ec->ob; - pd= ob->pd; - - /* Get IPO force strength and fall off values here */ - if (has_ipo_code(ob->ipo, OB_PD_FSTR)) - force_val = IPO_GetFloatValue(ob->ipo, OB_PD_FSTR, cur_time); - else - force_val = pd->f_strength; + if(!collobjs) + return 0; + + VECCOPY(norm, dir); + VecMulf(norm, -1.0); + len = Normalize(norm); + + // check all collision objects + for(i = 0; i < numcollobj; i++) + { + CollisionModifierData *collmd = collobjs[i]; - if (has_ipo_code(ob->ipo, OB_PD_FFALL)) - ffall_val = IPO_GetFloatValue(ob->ipo, OB_PD_FFALL, cur_time); - else - ffall_val = pd->f_power; + if(collmd->bvhtree) + { + BVHTreeRayHit hit; - /* Need to set r.cfra for paths (investigate, ton) (uses ob->ctime now, ton) */ - if(ob->ctime!=cur_time) { - cur_frame = G.scene->r.cfra; - G.scene->r.cfra = (int)cur_time; - where_is_object_time(ob, cur_time); - G.scene->r.cfra = cur_frame; - } - - /* use center of object for distance calculus */ - obloc= ob->obmat[3]; - VECSUB(vect_to_vert, obloc, opco); - distance = VecLength(vect_to_vert); + hit.index = -1; + hit.dist = len + FLT_EPSILON; - if((pd->flag & PFIELD_USEMAX) && distance>pd->maxdist && pd->forcefield != PFIELD_GUIDE) - ; /* don't do anything */ - else if(pd->forcefield == PFIELD_WIND) { - VECCOPY(force_vec, ob->obmat[2]); - - /* wind works harder perpendicular to normal, would be nice for softbody later (ton) */ - - /* Limit minimum distance to vertex so that */ - /* the force is not too big */ - if (distance < 0.001) distance = 0.001f; - f_force = (force_val)*(1/(1000 * (float)pow((double)distance, (double)ffall_val))); - /* this option for softbody only */ - if(flags && PE_WIND_AS_SPEED){ - speed[0] -= (force_vec[0] * f_force ); - speed[1] -= (force_vec[1] * f_force ); - speed[2] -= (force_vec[2] * f_force ); - } - else{ - force[0] += force_vec[0]*f_force; - force[1] += force_vec[1]*f_force; - force[2] += force_vec[2]*f_force; - } - } - else if(pd->forcefield == PFIELD_FORCE) { - - /* only use center of object */ - obloc= ob->obmat[3]; - - /* Now calculate the gravitational force */ - VECSUB(vect_to_vert, obloc, opco); - distance = VecLength(vect_to_vert); - - /* Limit minimum distance to vertex so that */ - /* the force is not too big */ - if (distance < 0.001) distance = 0.001f; - f_force = (force_val)*(1.0/(1000.0 * (float)pow((double)distance, (double)ffall_val))); - force[0] += (vect_to_vert[0] * f_force ); - force[1] += (vect_to_vert[1] * f_force ); - force[2] += (vect_to_vert[2] * f_force ); - } - else if(pd->forcefield == PFIELD_VORTEX) { - float vortexvec[3]; - - /* only use center of object */ - obloc= ob->obmat[3]; - - /* Now calculate the vortex force */ - VECSUB(vect_to_vert, obloc, opco); - distance = VecLength(vect_to_vert); - - Crossf(force_vec, ob->obmat[2], vect_to_vert); - Normalize(force_vec); - - /* Limit minimum distance to vertex so that */ - /* the force is not too big */ - if (distance < 0.001) distance = 0.001f; - f_force = (force_val)*(1.0/(100.0 * (float)pow((double)distance, (double)ffall_val))); - vortexvec[0]= -(force_vec[0] * f_force ); - vortexvec[1]= -(force_vec[1] * f_force ); - vortexvec[2]= -(force_vec[2] * f_force ); - - /* this option for softbody only */ - if(flags &&PE_WIND_AS_SPEED) { - speed[0]+= vortexvec[0]; - speed[1]+= vortexvec[1]; - speed[2]+= vortexvec[2]; - } - else { - /* since vortex alters the speed, we have to correct for the previous vortex result */ - speed[0]+= vortexvec[0] - ec->oldspeed[0]; - speed[1]+= vortexvec[1] - ec->oldspeed[1]; - speed[2]+= vortexvec[2] - ec->oldspeed[2]; + // check if the way is blocked + if(BLI_bvhtree_ray_cast(collmd->bvhtree, co, norm, 0.0f, &hit, eff_tri_ray_hit, NULL)>=0) + { + // visibility is only between 0 and 1, calculated from 1-absorption + visibility *= MAX2(0.0, MIN2(1.0, (1.0-((float)collmd->absorption)*0.01))); - VECCOPY(ec->oldspeed, vortexvec); + if(visibility <= 0.0f) + break; } } - else if(pd->forcefield == PFIELD_GUIDE) { - float guidevec[4], guidedir[3]; - float mindist= force_val; /* force_val is actually mindist in the UI */ - - distance= ec->guide_dist; - - /* WARNING: bails out with continue here */ - if((pd->flag & PFIELD_USEMAX) && distance>pd->maxdist) continue; - - /* calculate contribution factor for this guide */ - if(distance<=mindist) f_force= 1.0f; - else if(pd->flag & PFIELD_USEMAX) { - if(distance>pd->maxdist || mindist>=pd->maxdist) f_force= 0.0f; - else { - f_force= 1.0f - (distance-mindist)/(pd->maxdist - mindist); - if(ffall_val!=0.0f) - f_force = (float)pow(f_force, ffall_val+1.0); - } - } - else { - f_force= 1.0f/(1.0f + distance-mindist); - if(ffall_val!=0.0f) - f_force = (float)pow(f_force, ffall_val+1.0); - } - - /* now derive path point from loc_time */ - if(pd->flag & PFIELD_GUIDE_PATH_ADD) - where_on_path(ob, f_force*loc_time*ec->time_scale, guidevec, guidedir); - else - where_on_path(ob, loc_time*ec->time_scale, guidevec, guidedir); - - VECSUB(guidedir, guidevec, ec->oldloc); - VECCOPY(ec->oldloc, guidevec); - - Mat4Mul3Vecfl(ob->obmat, guidedir); - VecMulf(guidedir, ec->scale); /* correction for lifetime and speed */ - - /* we subtract the speed we gave it previous step */ - VECCOPY(guidevec, guidedir); - VECSUB(guidedir, guidedir, ec->oldspeed); - VECCOPY(ec->oldspeed, guidevec); - - /* if it fully contributes, we stop */ - if(f_force==1.0) { - VECCOPY(guidecollect, guidedir); - guidedist= 1.0f; - break; - } - else if(guidedist<1.0f) { - VecMulf(guidedir, f_force); - VECADD(guidecollect, guidecollect, guidedir); - guidedist += f_force; - } - } - } - - /* all guides are accumulated here */ - if(guidedist!=0.0f) { - if(guidedist!=1.0f) VecMulf(guidecollect, 1.0f/guidedist); - VECADD(speed, speed, guidecollect); } -} - -static void cache_object_vertices(Object *ob) -{ - Mesh *me; - MVert *mvert; - float *fp; - int a; - me= ob->data; - if(me->totvert==0) return; - - fp= ob->sumohandle= MEM_mallocN(3*sizeof(float)*me->totvert, "cache particles"); - mvert= me->mvert; - a= me->totvert; - while(a--) { - VECCOPY(fp, mvert->co); - Mat4MulVecfl(ob->obmat, fp); - mvert++; - fp+= 3; - } + MEM_freeN(collobjs); + + return visibility; } -static int pdDoDeflection(RNG *rng, float opco[3], float npco[3], float opno[3], - float npno[3], float life, float force[3], int def_depth, - float cur_time, unsigned int par_layer, int *last_object, - int *last_face, int *same_face) +// noise function for wind e.g. +static float wind_func(struct RNG *rng, float strength) { - /* Particle deflection code */ - /* The code is in two sections: the first part checks whether a particle has */ - /* intersected a face of a deflector mesh, given its old and new co-ords, opco and npco */ - /* and which face it hit first */ - /* The second part calculates the new co-ordinates given that collision and updates */ - /* the new co-ordinates accordingly */ - Base *base; - Object *ob, *deflection_object = NULL; - Mesh *def_mesh; - MFace *mface, *deflection_face = NULL; - float *v1, *v2, *v3, *v4, *vcache=NULL; - float nv1[3], nv2[3], nv3[3], nv4[3], edge1[3], edge2[3]; - float dv1[3] = {0}, dv2[3] = {0}, dv3[3] = {0}; - float vect_to_int[3], refl_vel[3]; - float d_intersect_co[3], d_intersect_vect[3], d_nvect[3], d_i_co_above[3]; - float forcec[3]; - float k_point3, dist_to_plane; - float first_dist, ref_plane_mag; - float dk_plane=0, dk_point1=0; - float icalctop, icalcbot, n_mag; - float mag_iv, x_m,y_m,z_m; - float damping, perm_thresh; - float perm_val, rdamp_val; - int a, deflected=0, deflected_now=0; - float t,t2, min_t; - float mat[3][3], obloc[3] = {0}; - int cur_frame; - float time_before, time_after; - float force_mag_norm; - int d_object=0, d_face=0, ds_object=0, ds_face=0; - - first_dist = 200000; - min_t = 200000; - - /* The first part of the code, finding the first intersected face*/ - base= G.scene->base.first; - while (base) { - /*Only proceed for mesh object in same layer */ - if(base->object->type==OB_MESH && (base->lay & par_layer)) { - ob= base->object; - /* only with deflecting set */ - if(ob->pd && ob->pd->deflect) { - def_mesh= ob->data; - - d_object = d_object + 1; - - d_face = d_face + 1; - mface= def_mesh->mface; - a = def_mesh->totface; - - - if(ob->parent==NULL && ob->ipo==NULL) { // static - if(ob->sumohandle==NULL) cache_object_vertices(ob); - vcache= ob->sumohandle; - } - else { - /*Find out where the object is at this time*/ - cur_frame = G.scene->r.cfra; - G.scene->r.cfra = (int)cur_time; - where_is_object_time(ob, cur_time); - G.scene->r.cfra = cur_frame; - - /*Pass the values from ob->obmat to mat*/ - /*and the location values to obloc */ - Mat3CpyMat4(mat,ob->obmat); - obloc[0] = ob->obmat[3][0]; - obloc[1] = ob->obmat[3][1]; - obloc[2] = ob->obmat[3][2]; - vcache= NULL; - - } - - while (a--) { - - if(vcache) { - v1= vcache+ 3*(mface->v1); - VECCOPY(nv1, v1); - v1= vcache+ 3*(mface->v2); - VECCOPY(nv2, v1); - v1= vcache+ 3*(mface->v3); - VECCOPY(nv3, v1); - v1= vcache+ 3*(mface->v4); - VECCOPY(nv4, v1); - } - else { - /* Calculate the global co-ordinates of the vertices*/ - v1= (def_mesh->mvert+(mface->v1))->co; - v2= (def_mesh->mvert+(mface->v2))->co; - v3= (def_mesh->mvert+(mface->v3))->co; - v4= (def_mesh->mvert+(mface->v4))->co; + int random = (rng_getInt(rng)+1) % 65535; // max 2357 + float force = rng_getFloat(rng) + 1.0f; + float ret; + float sign = 0; - VECCOPY(nv1, v1); - VECCOPY(nv2, v2); - VECCOPY(nv3, v3); - VECCOPY(nv4, v4); + sign = (random > 32000.0) ? 1.0: -1.0; // dividing by 2 is not giving equal sign distribution - /*Apply the objects deformation matrix*/ - Mat3MulVecfl(mat, nv1); - Mat3MulVecfl(mat, nv2); - Mat3MulVecfl(mat, nv3); - Mat3MulVecfl(mat, nv4); + ret = sign*((float)random / force)*strength/65535.0f; - VECADD(nv1, nv1, obloc); - VECADD(nv2, nv2, obloc); - VECADD(nv3, nv3, obloc); - VECADD(nv4, nv4, obloc); - } - - deflected_now = 0; - - - -// t= 0.5; // this is labda of line, can use it optimize quad intersection -// sorry but no .. see below (BM) - if( LineIntersectsTriangle(opco, npco, nv1, nv2, nv3, &t, NULL) ) { - if (t < min_t) { - deflected = 1; - deflected_now = 1; - } - } -// else if (mface->v4 && (t>=0.0 && t<=1.0)) { -// no, you can't skip testing the other triangle -// it might give a smaller t on (close to) the edge .. this is numerics not esoteric maths :) -// note: the 2 triangles don't need to share a plane ! (BM) - if (mface->v4) { - if( LineIntersectsTriangle(opco, npco, nv1, nv3, nv4, &t2, NULL) ) { - if (t2 < min_t) { - deflected = 1; - deflected_now = 2; - } - } - } - - if ((deflected_now > 0) && ((t < min_t) ||(t2 < min_t))) { - min_t = t; - ds_object = d_object; - ds_face = d_face; - deflection_object = ob; - deflection_face = mface; - if (deflected_now==1) { - min_t = t; - VECCOPY(dv1, nv1); - VECCOPY(dv2, nv2); - VECCOPY(dv3, nv3); - } - else { - min_t = t2; - VECCOPY(dv1, nv1); - VECCOPY(dv2, nv3); - VECCOPY(dv3, nv4); - } - } - mface++; - } - } - } - base = base->next; - } - - - /* Here's the point to do the permeability calculation */ - /* Set deflected to 0 if a random number is below the value */ - /* Get the permeability IPO here*/ - if (deflected) { - - if (has_ipo_code(deflection_object->ipo, OB_PD_PERM)) - perm_val = IPO_GetFloatValue(deflection_object->ipo, OB_PD_PERM, cur_time); - else - perm_val = deflection_object->pd->pdef_perm; - - perm_thresh = rng_getFloat(rng) - perm_val; - if (perm_thresh < 0 ) { - deflected = 0; - } - } - - /* Now for the second part of the deflection code - work out the new speed */ - /* and position of the particle if a collision occurred */ - if (deflected) { - VECSUB(edge1, dv1, dv2); - VECSUB(edge2, dv3, dv2); - Crossf(d_nvect, edge2, edge1); - n_mag = Normalize(d_nvect); - dk_plane = INPR(d_nvect, nv1); - dk_point1 = INPR(d_nvect,opco); - - VECSUB(d_intersect_vect, npco, opco); - - d_intersect_co[0] = opco[0] + (min_t * (npco[0] - opco[0])); - d_intersect_co[1] = opco[1] + (min_t * (npco[1] - opco[1])); - d_intersect_co[2] = opco[2] + (min_t * (npco[2] - opco[2])); - - d_i_co_above[0] = (d_intersect_co[0] + (0.001f * d_nvect[0])); - d_i_co_above[1] = (d_intersect_co[1] + (0.001f * d_nvect[1])); - d_i_co_above[2] = (d_intersect_co[2] + (0.001f * d_nvect[2])); - mag_iv = Normalize(d_intersect_vect); - VECCOPY(npco, d_intersect_co); - - VECSUB(vect_to_int, opco, d_intersect_co); - first_dist = Normalize(vect_to_int); - - /* Work out the lengths of time before and after collision*/ - time_before = (life*(first_dist / (mag_iv))); - time_after = (life*((mag_iv - first_dist) / (mag_iv))); - - /* We have to recalculate what the speed would have been at the */ - /* point of collision, not the key frame time */ - npno[0]= opno[0] + time_before*force[0]; - npno[1]= opno[1] + time_before*force[1]; - npno[2]= opno[2] + time_before*force[2]; - - - /* Reflect the speed vector in the face */ - x_m = (2 * npno[0] * d_nvect[0]); - y_m = (2 * npno[1] * d_nvect[1]); - z_m = (2 * npno[2] * d_nvect[2]); - refl_vel[0] = npno[0] - (d_nvect[0] * (x_m + y_m + z_m)); - refl_vel[1] = npno[1] - (d_nvect[1] * (x_m + y_m + z_m)); - refl_vel[2] = npno[2] - (d_nvect[2] * (x_m + y_m + z_m)); - - /*A random variation in the damping factor........ */ - /*Get the IPO values for damping here*/ - - if (has_ipo_code(deflection_object->ipo, OB_PD_SDAMP)) - damping = IPO_GetFloatValue(deflection_object->ipo, OB_PD_SDAMP, cur_time); - else - damping = deflection_object->pd->pdef_damp; - - if (has_ipo_code(deflection_object->ipo, OB_PD_RDAMP)) - rdamp_val = IPO_GetFloatValue(deflection_object->ipo, OB_PD_RDAMP, cur_time); - else - rdamp_val = deflection_object->pd->pdef_rdamp; - - damping = damping + ((1.0f - damping) * rng_getFloat(rng) *rdamp_val); - damping = damping * damping; - ref_plane_mag = INPR(refl_vel,d_nvect); - - if (damping > 0.999) damping = 0.999f; - - /* Now add in the damping force - only damp in the direction of */ - /* the faces normal vector */ - npno[0] = (refl_vel[0] - (d_nvect[0] * ref_plane_mag * damping)); - npno[1] = (refl_vel[1] - (d_nvect[1] * ref_plane_mag * damping)); - npno[2] = (refl_vel[2] - (d_nvect[2] * ref_plane_mag * damping)); - - /* Now reset opno */ - VECCOPY(opno,npno); - VECCOPY(forcec, force); - - /* If the particle has bounced more than four times on the same */ - /* face within this cycle (depth > 4, same face > 4 ) */ - /* Then set the force to be only that component of the force */ - /* in the same direction as the face normal */ - /* i.e. subtract the component of the force in the direction */ - /* of the face normal from the actual force */ - if ((ds_object == *last_object) && (ds_face == *last_face)) { - /* Increment same_face */ - *same_face = *same_face + 1; - if ((*same_face > 3) && (def_depth > 3)) { - force_mag_norm = INPR(forcec, d_nvect); - forcec[0] = forcec[0] - (d_nvect[0] * force_mag_norm); - forcec[1] = forcec[1] - (d_nvect[1] * force_mag_norm); - forcec[2] = forcec[2] - (d_nvect[2] * force_mag_norm); - } - } - else *same_face = 1; - - *last_object = ds_object; - *last_face = ds_face; - - /* We have the particles speed at the point of collision */ - /* Now we want the particles speed at the current key frame */ - - npno[0]= npno[0] + time_after*forcec[0]; - npno[1]= npno[1] + time_after*forcec[1]; - npno[2]= npno[2] + time_after*forcec[2]; - - /* Now we have to recalculate pa->co for the remainder*/ - /* of the time since the intersect*/ - npco[0]= npco[0] + time_after*npno[0]; - npco[1]= npco[1] + time_after*npno[1]; - npco[2]= npco[2] + time_after*npno[2]; - - /* And set the old co-ordinates back to the point just above the intersection */ - VECCOPY(opco, d_i_co_above); - - /* Finally update the time */ - life = time_after; - cur_time += time_before; - - /* The particle may have fallen through the face again by now!!*/ - /* So check if the particle has changed sides of the plane compared*/ - /* the co-ordinates at the last keyframe*/ - /* But only do this as a last resort, if we've got to the end of the */ - /* number of collisions allowed */ - if (def_depth==9) { - k_point3 = INPR(d_nvect,npco); - if (((dk_plane > k_point3) && (dk_plane < dk_point1))||((dk_plane < k_point3) && (dk_plane > dk_point1))) { - - /* Yup, the pesky particle may have fallen through a hole!!! */ - /* So we'll cheat a bit and move the particle along the normal vector */ - /* until it's just the other side of the plane */ - icalctop = (dk_plane - d_nvect[0]*npco[0] - d_nvect[1]*npco[1] - d_nvect[2]*npco[2]); - icalcbot = (d_nvect[0]*d_nvect[0] + d_nvect[1]*d_nvect[1] + d_nvect[2]*d_nvect[2]); - dist_to_plane = icalctop / icalcbot; - - /* Now just increase the distance a little to place */ - /* the point the other side of the plane */ - dist_to_plane *= 1.1f; - npco[0]= npco[0] + (dist_to_plane * d_nvect[0]); - npco[1]= npco[1] + (dist_to_plane * d_nvect[1]); - npco[2]= npco[2] + (dist_to_plane * d_nvect[2]); - - } - } - } - return deflected; + return ret; } -/* - rng= random number generator - ob = object that spawns the particles - depth = for fireworks - nr = index nr of current particle - paf = the particle system - part = current particle - force = force vector - deform = flag to indicate lattice deform - */ -static void make_particle_keys(RNG *rng, Object *ob, int depth, int nr, PartEff *paf, Particle *part, float *force, int deform, MTex *mtex, ListBase *effectorbase) + +static float falloff_func(float fac, int usemin, float mindist, int usemax, float maxdist, float power) { - Particle *pa, *opa = NULL; - float damp, deltalife, life; - float cur_time, maxspeed= paf->maxlen/(float)paf->totkey; - float opco[3], opno[3], npco[3], npno[3], new_force[3], new_speed[3]; - int b, rt1, rt2, deflected, deflection, finish_defs, def_count; - int last_ob, last_fc, same_fc; - - damp= 1.0f-paf->damp; - pa= part; - - /* start speed: random */ - if(paf->randfac!=0.0) { - pa->no[0]+= paf->randfac*(rng_getFloat(rng) - 0.5f); - pa->no[1]+= paf->randfac*(rng_getFloat(rng) - 0.5f); - pa->no[2]+= paf->randfac*(rng_getFloat(rng) - 0.5f); - } + if(!usemin) + mindist= 0.0f; - /* start speed: texture */ - if(mtex && paf->texfac!=0.0) { - particle_tex(mtex, paf, pa->co, pa->no); + if(fac < mindist) { + return 1.0f; } + else if(usemax) { + if(fac>maxdist || (maxdist-mindist)<=0.0f) + return 0.0f; - /* effectors here? */ - if(effectorbase) - precalc_effectors(ob, paf, pa, effectorbase); - - if(paf->totkey>1) deltalife= pa->lifetime/(paf->totkey-1); - else deltalife= pa->lifetime; - - /* longer lifetime results in longer distance covered */ - VecMulf(pa->no, deltalife); - - opa= pa; - pa++; - - for(b=1; b<paf->totkey; b++) { - - /* new time */ - pa->time= opa->time+deltalife; - cur_time = pa->time; - - /* set initial variables */ - VECCOPY(opco, opa->co); - VECCOPY(new_force, force); - VECCOPY(new_speed, opa->no); - VecMulf(new_speed, 1.0f/deltalife); - //new_speed[0] = new_speed[1] = new_speed[2] = 0.0f; - - /* handle differences between static (local coords, fixed frame) and dynamic */ - if(effectorbase) { - float loc_time= ((float)b)/(float)(paf->totkey-1); - - if(paf->flag & PAF_STATIC) { - float opco1[3], new_force1[3]; - - /* move co and force to global coords */ - VECCOPY(opco1, opco); - Mat4MulVecfl(ob->obmat, opco1); - VECCOPY(new_force1, new_force); - Mat4Mul3Vecfl(ob->obmat, new_force1); - Mat4Mul3Vecfl(ob->obmat, new_speed); - - cur_time = G.scene->r.cfra; - - /* force fields */ - pdDoEffectors(effectorbase, opco1, new_force1, new_speed, cur_time, loc_time, 0); - - /* move co, force and newspeed back to local */ - VECCOPY(opco, opco1); - Mat4MulVecfl(ob->imat, opco); - VECCOPY(new_force, new_force1); - Mat4Mul3Vecfl(ob->imat, new_force); - Mat4Mul3Vecfl(ob->imat, new_speed); - } - else { - /* force fields */ - pdDoEffectors(effectorbase, opco, new_force, new_speed, cur_time, loc_time, 0); - } - } - - /* new speed */ - pa->no[0]= deltalife * (new_speed[0] + new_force[0]); - pa->no[1]= deltalife * (new_speed[1] + new_force[1]); - pa->no[2]= deltalife * (new_speed[2] + new_force[2]); - - /* speed limitor */ - if((paf->flag & PAF_STATIC) && maxspeed!=0.0f) { - float len= VecLength(pa->no); - if(len > maxspeed) - VecMulf(pa->no, maxspeed/len); - } - - /* new location */ - pa->co[0]= opa->co[0] + pa->no[0]; - pa->co[1]= opa->co[1] + pa->no[1]; - pa->co[2]= opa->co[2] + pa->no[2]; - - /* Particle deflection code */ - if((paf->flag & PAF_STATIC)==0) { - deflection = 0; - finish_defs = 1; - def_count = 0; - - VECCOPY(opno, opa->no); - VECCOPY(npco, pa->co); - VECCOPY(npno, pa->no); - - life = deltalife; - cur_time -= deltalife; - - last_ob = -1; - last_fc = -1; - same_fc = 0; - - /* First call the particle deflection check for the particle moving */ - /* between the old co-ordinates and the new co-ordinates */ - /* If a deflection occurs, call the code again, this time between the */ - /* intersection point and the updated new co-ordinates */ - /* Bail out if we've done the calculation 10 times - this seems ok */ - /* for most scenes I've tested */ - while (finish_defs) { - deflected = pdDoDeflection(rng, opco, npco, opno, npno, life, new_force, - def_count, cur_time, ob->lay, - &last_ob, &last_fc, &same_fc); - if (deflected) { - def_count = def_count + 1; - deflection = 1; - if (def_count==10) finish_defs = 0; - } - else { - finish_defs = 0; - } - } - - /* Only update the particle positions and speed if we had a deflection */ - if (deflection) { - pa->co[0] = npco[0]; - pa->co[1] = npco[1]; - pa->co[2] = npco[2]; - pa->no[0] = npno[0]; - pa->no[1] = npno[1]; - pa->no[2] = npno[2]; - } - } - - /* speed: texture */ - if(mtex && paf->texfac!=0.0) { - particle_tex(mtex, paf, pa->co, pa->no); - } - if(damp!=1.0) { - pa->no[0]*= damp; - pa->no[1]*= damp; - pa->no[2]*= damp; - } - - opa= pa; - pa++; - /* opa is used later on too! */ + fac= (fac-mindist)/(maxdist-mindist); + return 1.0f - (float)pow((double)fac, (double)power); } + else + return pow((double)1.0f+fac-mindist, (double)-power); +} - if(deform) { - /* deform all keys */ - pa= part; - b= paf->totkey; - while(b--) { - calc_latt_deform(pa->co, 1.0f); - pa++; - } - } - - /* the big multiplication */ - if(depth<PAF_MAXMULT && paf->mult[depth]!=0.0) { - - /* new 'child' emerges from an average 'mult' part from - the particles */ - damp = (float)nr; - rt1= (int)(damp*paf->mult[depth]); - rt2= (int)((damp+1.0)*paf->mult[depth]); - if(rt1!=rt2) { - - for(b=0; b<paf->child[depth]; b++) { - pa= new_particle(paf); - *pa= *opa; - pa->lifetime= paf->life[depth]; - if(paf->randlife!=0.0) { - pa->lifetime*= 1.0f + paf->randlife*(rng_getFloat(rng) - 0.5f); - } - pa->mat_nr= paf->mat[depth]; +static float falloff_func_dist(PartDeflect *pd, float fac) +{ + return falloff_func(fac, pd->flag&PFIELD_USEMIN, pd->mindist, pd->flag&PFIELD_USEMAX, pd->maxdist, pd->f_power); +} - make_particle_keys(rng, ob, depth+1, b, paf, pa, force, deform, mtex, effectorbase); - } - } - } +static float falloff_func_rad(PartDeflect *pd, float fac) +{ + return falloff_func(fac, pd->flag&PFIELD_USEMINR, pd->minrad, pd->flag&PFIELD_USEMAXR, pd->maxrad, pd->f_power_r); } -static void init_mv_jit(float *jit, int num, int seed2) +float effector_falloff(PartDeflect *pd, float *eff_velocity, float *vec_to_part) { - RNG *rng; - float *jit2, x, rad1, rad2, rad3; - int i, num2; + float eff_dir[3], temp[3]; + float falloff=1.0, fac, r_fac; + + if(pd->forcefield==PFIELD_LENNARDJ) + return falloff; /* Lennard-Jones field has it's own falloff built in */ + + VecCopyf(eff_dir,eff_velocity); + Normalize(eff_dir); + + if(pd->flag & PFIELD_POSZ && Inpf(eff_dir,vec_to_part)<0.0f) + falloff=0.0f; + else switch(pd->falloff){ + case PFIELD_FALL_SPHERE: + fac=VecLength(vec_to_part); + falloff= falloff_func_dist(pd, fac); + break; + + case PFIELD_FALL_TUBE: + fac=Inpf(vec_to_part,eff_dir); + falloff= falloff_func_dist(pd, ABS(fac)); + if(falloff == 0.0f) + break; - if(num==0) return; + VECADDFAC(temp,vec_to_part,eff_dir,-fac); + r_fac=VecLength(temp); + falloff*= falloff_func_rad(pd, r_fac); + break; + case PFIELD_FALL_CONE: + fac=Inpf(vec_to_part,eff_dir); + falloff= falloff_func_dist(pd, ABS(fac)); + if(falloff == 0.0f) + break; - rad1= (float)(1.0/sqrt((float)num)); - rad2= (float)(1.0/((float)num)); - rad3= (float)sqrt((float)num)/((float)num); + r_fac=saacos(fac/VecLength(vec_to_part))*180.0f/(float)M_PI; + falloff*= falloff_func_rad(pd, r_fac); - rng = rng_new(31415926 + num + seed2); - x= 0; - num2 = 2 * num; - for(i=0; i<num2; i+=2) { - - jit[i]= x + rad1*(0.5f - rng_getFloat(rng)); - jit[i+1]= i/(2.0f*num) + rad1*(0.5f - rng_getFloat(rng)); - - jit[i]-= (float)floor(jit[i]); - jit[i+1]-= (float)floor(jit[i+1]); - - x+= rad3; - x -= (float)floor(x); + break; } - jit2= MEM_mallocN(12 + 2*sizeof(float)*num, "initjit"); - - for (i=0 ; i<4 ; i++) { - BLI_jitterate1(jit, jit2, num, rad1); - BLI_jitterate1(jit, jit2, num, rad1); - BLI_jitterate2(jit, jit2, num, rad2); - } - MEM_freeN(jit2); - rng_free(rng); + return falloff; } -#define JIT_RAND 32 - -/* for a position within a face, tot is total amount of faces */ -static void give_mesh_particle_coord(PartEff *paf, VeNoCo *noco, MFace *mface, int partnr, int subnr, float *co, float *no) +void do_physical_effector(Object *ob, float *opco, short type, float force_val, float distance, float falloff, float size, float damp, float *eff_velocity, float *vec_to_part, float *velocity, float *field, int planar, struct RNG *rng, float noise_factor, float charge, float pa_size) { - static float *jit= NULL; - static float *trands= NULL; - static int jitlevel= 1; - float *v1, *v2, *v3, *v4; - float u, v; - float *n1, *n2, *n3, *n4; - - /* free signal */ - if(paf==NULL) { - if(jit) MEM_freeN(jit); - jit= NULL; - if(trands) MEM_freeN(trands); - trands= NULL; + float mag_vec[3]={0,0,0}; + float temp[3], temp2[3]; + float eff_vel[3]; + float noise = 0, visibility; + + // calculate visibility + visibility = eff_calc_visibility(ob, opco, vec_to_part); + if(visibility <= 0.0) return; - } - - /* first time initialize jitter or trand, partnr then is total amount of particles, subnr total amount of faces */ - if(trands==NULL && jit==NULL) { - RNG *rng = rng_new(31415926 + paf->seed); - int i, tot; - - if(paf->flag & PAF_TRAND) - tot= partnr; - else - tot= JIT_RAND; /* arbitrary... allows JIT_RAND times more particles in a face for jittered distro */ - - trands= MEM_callocN(2+2*tot*sizeof(float), "trands"); - for(i=0; i<tot; i++) { - trands[2*i]= rng_getFloat(rng); - trands[2*i+1]= rng_getFloat(rng); - } - rng_free(rng); + falloff *= visibility; - if((paf->flag & PAF_TRAND)==0) { - jitlevel= paf->userjit; + VecCopyf(eff_vel,eff_velocity); + Normalize(eff_vel); + + switch(type){ + case PFIELD_WIND: + VECCOPY(mag_vec,eff_vel); - if(jitlevel == 0) { - jitlevel= partnr/subnr; - if(paf->flag & PAF_EDISTR) jitlevel*= 2; /* looks better in general, not very scietific */ - if(jitlevel<3) jitlevel= 3; - if(jitlevel>100) jitlevel= 100; - } + // add wind noise here, only if we have wind + if((noise_factor > 0.0f) && (force_val > FLT_EPSILON)) + noise = wind_func(rng, noise_factor); - jit= MEM_callocN(2+ jitlevel*2*sizeof(float), "jit"); - init_mv_jit(jit, jitlevel, paf->seed); - BLI_array_randomize(jit, 2*sizeof(float), jitlevel, paf->seed); /* for custom jit or even distribution */ - } - return; - } - - if(paf->flag & PAF_TRAND) { - u= trands[2*partnr]; - v= trands[2*partnr+1]; - } - else { - /* jittered distribution gets fixed random offset */ - if(subnr>=jitlevel) { - int jitrand= (subnr/jitlevel) % JIT_RAND; - - subnr %= jitlevel; - u= jit[2*subnr] + trands[2*jitrand]; - v= jit[2*subnr+1] + trands[2*jitrand+1]; - if(u > 1.0f) u-= 1.0f; - if(v > 1.0f) v-= 1.0f; - } - else { - u= jit[2*subnr]; - v= jit[2*subnr+1]; - } - } - - v1= (noco+(mface->v1))->co; - v2= (noco+(mface->v2))->co; - v3= (noco+(mface->v3))->co; - n1= (noco+(mface->v1))->no; - n2= (noco+(mface->v2))->no; - n3= (noco+(mface->v3))->no; - - if(mface->v4) { - float uv= u*v; - float muv= (1.0f-u)*(v); - float umv= (u)*(1.0f-v); - float mumv= (1.0f-u)*(1.0f-v); - - v4= (noco+(mface->v4))->co; - n4= (noco+(mface->v4))->no; - - co[0]= mumv*v1[0] + muv*v2[0] + uv*v3[0] + umv*v4[0]; - co[1]= mumv*v1[1] + muv*v2[1] + uv*v3[1] + umv*v4[1]; - co[2]= mumv*v1[2] + muv*v2[2] + uv*v3[2] + umv*v4[2]; + VecMulf(mag_vec,(force_val+noise)*falloff); + VecAddf(field,field,mag_vec); + break; - no[0]= mumv*n1[0] + muv*n2[0] + uv*n3[0] + umv*n4[0]; - no[1]= mumv*n1[1] + muv*n2[1] + uv*n3[1] + umv*n4[1]; - no[2]= mumv*n1[2] + muv*n2[2] + uv*n3[2] + umv*n4[2]; - } - else { - /* mirror triangle uv coordinates when on other side */ - if(u + v > 1.0f) { - u= 1.0f-u; - v= 1.0f-v; - } - co[0]= v1[0] + u*(v3[0]-v1[0]) + v*(v2[0]-v1[0]); - co[1]= v1[1] + u*(v3[1]-v1[1]) + v*(v2[1]-v1[1]); - co[2]= v1[2] + u*(v3[2]-v1[2]) + v*(v2[2]-v1[2]); - - no[0]= n1[0] + u*(n3[0]-n1[0]) + v*(n2[0]-n1[0]); - no[1]= n1[1] + u*(n3[1]-n1[1]) + v*(n2[1]-n1[1]); - no[2]= n1[2] + u*(n3[2]-n1[2]) + v*(n2[2]-n1[2]); - } -} + case PFIELD_FORCE: + if(planar) + Projf(mag_vec,vec_to_part,eff_vel); + else + VecCopyf(mag_vec,vec_to_part); + Normalize(mag_vec); -/* Gets a MDeformVert's weight in group (0 if not in group) */ -/* note; this call could be in mesh.c or deform.c, but OK... it's in armature.c too! (ton) */ -static float vert_weight(MDeformVert *dvert, int group) -{ - MDeformWeight *dw; - int i; - - if(dvert) { - dw= dvert->dw; - for(i= dvert->totweight; i>0; i--, dw++) { - if(dw->def_nr == group) return dw->weight; - if(i==1) break; /*otherwise dw will point to somewhere it shouldn't*/ - } - } - return 0.0; -} + VecMulf(mag_vec,force_val*falloff); + VecAddf(field,field,mag_vec); + break; -/* Gets a faces average weight in a group, helper for below, face and weights are always set */ -static float face_weight(MFace *face, float *weights) -{ - float tweight; - - tweight = weights[face->v1] + weights[face->v2] + weights[face->v3]; - - if(face->v4) { - tweight += weights[face->v4]; - tweight /= 4.0; - } - else { - tweight /= 3.0; - } + case PFIELD_VORTEX: + Crossf(mag_vec,eff_vel,vec_to_part); - return tweight; -} + Normalize(mag_vec); -/* helper function for build_particle_system() */ -static void make_weight_tables(PartEff *paf, Mesh *me, int totpart, VeNoCo *vertlist, int totvert, MFace *facelist, int totface, float **vweights, float **fweights) -{ - MFace *mface; - float *foweights=NULL, *voweights=NULL; - float totvweight=0.0f, totfweight=0.0f; - int a; - - if((paf->flag & PAF_FACE)==0) totface= 0; - - /* collect emitting vertices & faces if vert groups used */ - if(paf->vertgroup && me->dvert) { - - /* allocate weights array for all vertices, also for lookup of faces later on. note it's a malloc */ - *vweights= voweights= MEM_mallocN( totvert*sizeof(float), "pafvoweights" ); - totvweight= 0.0f; - for(a=0; a<totvert; a++) { - voweights[a]= vert_weight(me->dvert+a, paf->vertgroup-1); - totvweight+= voweights[a]; - } - - if(totface) { - /* allocate weights array for faces, note it's a malloc */ - *fweights= foweights= MEM_mallocN(totface*sizeof(float), "paffoweights" ); - for(a=0, mface=facelist; a<totface; a++, mface++) { - foweights[a] = face_weight(mface, voweights); - } - } - } - - /* make weights for faces or for even area distribution */ - if(totface && (paf->flag & PAF_EDISTR)) { - float maxfarea= 0.0f, curfarea; - - /* two cases for area distro, second case we already have group weights */ - if(foweights==NULL) { - /* allocate weights array for faces, note it's a malloc */ - *fweights= foweights= MEM_mallocN(totface*sizeof(float), "paffoweights" ); - - for(a=0, mface=facelist; a<totface; a++, mface++) { - if (mface->v4) - curfarea= AreaQ3Dfl(vertlist[mface->v1].co, vertlist[mface->v2].co, vertlist[mface->v3].co, vertlist[mface->v4].co); - else - curfarea= AreaT3Dfl(vertlist[mface->v1].co, vertlist[mface->v2].co, vertlist[mface->v3].co); - if(curfarea>maxfarea) - maxfarea = curfarea; - foweights[a]= curfarea; - } - } - else { - for(a=0, mface=facelist; a<totface; a++, mface++) { - if(foweights[a]!=0.0f) { - if (mface->v4) - curfarea= AreaQ3Dfl(vertlist[mface->v1].co, vertlist[mface->v2].co, vertlist[mface->v3].co, vertlist[mface->v4].co); - else - curfarea= AreaT3Dfl(vertlist[mface->v1].co, vertlist[mface->v2].co, vertlist[mface->v3].co); - if(curfarea>maxfarea) - maxfarea = curfarea; - foweights[a]*= curfarea; - } - } - } - - /* normalize weights for max face area, calculate tot */ - if(maxfarea!=0.0f) { - maxfarea= 1.0f/maxfarea; - for(a=0; a< totface; a++) { - if(foweights[a]!=0.0) { - foweights[a] *= maxfarea; - totfweight+= foweights[a]; - } - } - } - } - else if(foweights) { - /* only add totfweight value */ - for(a=0; a< totface; a++) { - if(foweights[a]!=0.0) { - totfweight+= foweights[a]; - } - } - } - - /* if weight arrays, we turn these arrays into the amount of particles */ - if(totvert && voweights) { - float mult= (float)totpart/totvweight; - - for(a=0; a< totvert; a++) { - if(voweights[a]!=0.0) - voweights[a] *= mult; - } - } - - if(totface && foweights) { - float mult= (float)totpart/totfweight; - - for(a=0; a< totface; a++) { - if(foweights[a]!=0.0) - foweights[a] *= mult; - } - } -} + VecMulf(mag_vec,force_val*distance*falloff); + VecAddf(field,field,mag_vec); -/* helper function for build_particle_system() */ -static void make_length_tables(PartEff *paf, Mesh *me, int totvert, MFace *facelist, int totface, float **vlengths, float **flengths) -{ - MFace *mface; - float *folengths=NULL, *volengths=NULL; - int a; - - if((paf->flag & PAF_FACE)==0) totface= 0; - - /* collect emitting vertices & faces if vert groups used */ - if(paf->vertgroup_v && me->dvert) { - - /* allocate lengths array for all vertices, also for lookup of faces later on. note it's a malloc */ - *vlengths= volengths= MEM_mallocN( totvert*sizeof(float), "pafvolengths" ); - for(a=0; a<totvert; a++) { - volengths[a]= vert_weight(me->dvert+a, paf->vertgroup_v-1); - } - - if(totface) { - /* allocate lengths array for faces, note it's a malloc */ - *flengths= folengths= MEM_mallocN(totface*sizeof(float), "paffolengths" ); - for(a=0, mface=facelist; a<totface; a++, mface++) { - folengths[a] = face_weight(mface, volengths); - } - } - } -} + break; + case PFIELD_MAGNET: + if(planar) + VecCopyf(temp,eff_vel); + else + /* magnetic field of a moving charge */ + Crossf(temp,eff_vel,vec_to_part); + Normalize(temp); -/* for paf start to end, store all matrices for objects */ -typedef struct pMatrixCache { - float obmat[4][4]; - float imat[3][3]; -} pMatrixCache; + Crossf(temp2,velocity,temp); + VecAddf(mag_vec,mag_vec,temp2); + VecMulf(mag_vec,force_val*falloff); + VecAddf(field,field,mag_vec); + break; + case PFIELD_HARMONIC: + if(planar) + Projf(mag_vec,vec_to_part,eff_vel); + else + VecCopyf(mag_vec,vec_to_part); -/* WARN: this function stores data in ob->id.idnew! */ -/* error: this function changes ob->recalc of other objects... */ -static pMatrixCache *cache_object_matrices(Object *ob, int start, int end) -{ - pMatrixCache *mcache, *mc; - Group *group= NULL; - Object *obcopy; - Base *base; - float framelenold, cfrao, sfo; - - /* object can be linked in group... stupid exception */ - if(NULL==object_in_scene(ob, G.scene)) - group= find_group(ob); - - mcache= mc= MEM_mallocN( (end-start+1)*sizeof(pMatrixCache), "ob matrix cache"); - - framelenold= G.scene->r.framelen; - G.scene->r.framelen= 1.0f; - cfrao= G.scene->r.cfra; - sfo= ob->sf; - ob->sf= 0.0f; - - /* clear storage, copy recalc tag (bad loop) */ - for(obcopy= G.main->object.first; obcopy; obcopy= obcopy->id.next) { - obcopy->id.newid= NULL; - obcopy->recalco= obcopy->recalc; - obcopy->recalc= 0; - } - - /* all objects get tagged recalc that influence this object (does group too) */ - /* note that recalco has the real recalc tags, set by callers of this function */ - ob->recalc |= OB_RECALC_OB; /* make sure a recalc gets flushed */ - DAG_object_update_flags(G.scene, ob, -1); - - for(G.scene->r.cfra= start; G.scene->r.cfra<=end; G.scene->r.cfra++, mc++) { - - if(group) { - GroupObject *go; - - for(go= group->gobject.first; go; go= go->next) { - if(go->ob->recalc) { - where_is_object(go->ob); - - do_ob_key(go->ob); - if(go->ob->type==OB_ARMATURE) { - do_all_pose_actions(go->ob); // only does this object actions - where_is_pose(go->ob); - } - } - } - } - else { - for(base= G.scene->base.first; base; base= base->next) { - if(base->object->recalc) { - if(base->object->id.newid==NULL) - base->object->id.newid= MEM_dupallocN(base->object); - - where_is_object(base->object); - - do_ob_key(base->object); - if(base->object->type==OB_ARMATURE) { - do_all_pose_actions(base->object); // only does this object actions - where_is_pose(base->object); - } - } - } - } - Mat4CpyMat4(mc->obmat, ob->obmat); - Mat4Invert(ob->imat, ob->obmat); - Mat3CpyMat4(mc->imat, ob->imat); - Mat3Transp(mc->imat); - } - - /* restore */ - G.scene->r.cfra= cfrao; - G.scene->r.framelen= framelenold; - ob->sf= sfo; - - if(group) { - GroupObject *go; - - for(go= group->gobject.first; go; go= go->next) { - if(go->ob->recalc) { - where_is_object(go->ob); - - do_ob_key(go->ob); - if(go->ob->type==OB_ARMATURE) { - do_all_pose_actions(go->ob); // only does this object actions - where_is_pose(go->ob); - } - } - } - } - else { - for(base= G.scene->base.first; base; base= base->next) { - if(base->object->recalc) { - - if(base->object->id.newid) { - obcopy= (Object *)base->object->id.newid; - *(base->object) = *(obcopy); - MEM_freeN(obcopy); - base->object->id.newid= NULL; - } - - do_ob_key(base->object); - if(base->object->type==OB_ARMATURE) { - do_all_pose_actions(base->object); // only does this object actions - where_is_pose(base->object); - } - } - } - } - - /* copy recalc tag (bad loop) */ - for(obcopy= G.main->object.first; obcopy; obcopy= obcopy->id.next) - obcopy->recalc= obcopy->recalco; - - return mcache; -} + Normalize(mag_vec); -/* for fluidsim win32 debug messages */ -#if defined(WIN32) && (!(defined snprintf)) -#define snprintf _snprintf -#endif + VecMulf(mag_vec,force_val*falloff); + VecSubf(field,field,mag_vec); -/* main particle building function - one day particles should become dynamic (realtime) with the current method as a 'bake' (ton) */ -void build_particle_system(Object *ob) -{ - RNG *rng; - PartEff *paf; - Particle *pa; - Mesh *me; - Base *base; - MTex *mtexmove=0, *mtextime=0; - Material *ma; - MFace *facelist= NULL; - pMatrixCache *mcache=NULL, *mcnow, *mcprev; - ListBase *effectorbase; - VeNoCo *vertexcosnos; - double startseconds= PIL_check_seconds_timer(); - float ftime, dtime, force[3], vec[3], fac, co[3], no[3]; - float *voweights= NULL, *foweights= NULL, maxw=1.0f; - float *volengths= NULL, *folengths= NULL; - int deform=0, a, totpart, paf_sta, paf_end; - int waitcursor_set= 0, totvert, totface, curface, curvert; -#ifndef DISABLE_ELBEEM - int readMask, activeParts, fileParts; -#endif - - /* return conditions */ - if(ob->type!=OB_MESH) return; - me= ob->data; + VecCopyf(mag_vec,velocity); + /* 1.9 is an experimental value to get critical damping at damp=1.0 */ + VecMulf(mag_vec,damp*1.9f*(float)sqrt(force_val)); + VecSubf(field,field,mag_vec); + break; + case PFIELD_CHARGE: + if(planar) + Projf(mag_vec,vec_to_part,eff_vel); + else + VecCopyf(mag_vec,vec_to_part); - paf= give_parteff(ob); - if(paf==NULL) return; - - if(G.rendering==0 && paf->disp==0) return; - - if(paf->keys) MEM_freeN(paf->keys); /* free as early as possible, for returns */ - paf->keys= NULL; - - //printf("build particles\n"); - - /* fluid sim particle import handling, actual loading of particles from file */ - #ifndef DISABLE_ELBEEM - if( (1) && (ob->fluidsimFlag & OB_FLUIDSIM_ENABLE) && // broken, disabled for now! - (ob->fluidsimSettings) && - (ob->fluidsimSettings->type == OB_FLUIDSIM_PARTICLE)) { - char *suffix = "fluidsurface_particles_#"; - char *suffix2 = ".gz"; - char filename[256]; - char debugStrBuffer[256]; - int curFrame = G.scene->r.cfra -1; // warning - sync with derived mesh fsmesh loading - int j, numFileParts; - gzFile gzf; - float vel[3]; - - if(ob==G.obedit) { // off... - paf->totpart = 0; // 1 or 0? - return; - } + Normalize(mag_vec); - // ok, start loading - strcpy(filename, ob->fluidsimSettings->surfdataPath); - strcat(filename, suffix); - BLI_convertstringcode(filename, G.sce, curFrame); // fixed #frame-no - strcat(filename, suffix2); - - gzf = gzopen(filename, "rb"); - if (!gzf) { - snprintf(debugStrBuffer,256,"readFsPartData::error - Unable to open file for reading '%s' \n", filename); - //elbeemDebugOut(debugStrBuffer); - paf->totpart = 0; - return; - } + VecMulf(mag_vec,charge*force_val*falloff); + VecAddf(field,field,mag_vec); + break; + case PFIELD_LENNARDJ: + { + float fac; - gzread(gzf, &totpart, sizeof(totpart)); - numFileParts = totpart; - totpart = (G.rendering)?totpart:(paf->disp*totpart)/100; - paf->totpart= totpart; - paf->totkey= 1; - /* initialize particles */ - new_particle(paf); - ftime = 0.0; // unused... - - // set up reading mask - readMask = ob->fluidsimSettings->typeFlags; - activeParts=0; - fileParts=0; - - for(a=0; a<totpart; a++) { - int ptype=0; - short shsize=0; - float convertSize=0.0; - gzread(gzf, &ptype, sizeof( ptype )); - if(ptype&readMask) { - activeParts++; - pa= new_particle(paf); - pa->time= ftime; - pa->lifetime= ftime + 10000.; // add large number to make sure they are displayed, G.scene->r.efra +1.0; - pa->co[0] = 0.0; - pa->co[1] = - pa->co[2] = 1.0*(float)a / (float)totpart; - pa->no[0] = pa->no[1] = pa->no[2] = 0.0; - pa->mat_nr= paf->omat; - gzread(gzf, &convertSize, sizeof( float )); - // convert range of 1.0-10.0 to shorts 1000-10000) - shsize = (short)(convertSize*1000.0); - pa->rt = shsize; - - for(j=0; j<3; j++) { - float wrf; - gzread(gzf, &wrf, sizeof( wrf )); - pa->co[j] = wrf; - //fprintf(stderr,"Rj%d ",j); - } - for(j=0; j<3; j++) { - float wrf; - gzread(gzf, &wrf, sizeof( wrf )); - vel[j] = wrf; - } - //if(a<25) fprintf(stderr,"FSPARTICLE debug set %s , a%d = %f,%f,%f , life=%f \n", filename, a, pa->co[0],pa->co[1],pa->co[2], pa->lifetime ); - } else { - // skip... - for(j=0; j<2*3+1; j++) { - float wrf; gzread(gzf, &wrf, sizeof( wrf )); - } + if(planar) { + Projf(mag_vec,vec_to_part,eff_vel); + distance = VecLength(mag_vec); } - fileParts++; - } - gzclose( gzf ); - - totpart = paf->totpart = activeParts; - snprintf(debugStrBuffer,256,"readFsPartData::done - particles:%d, active:%d, file:%d, mask:%d \n", paf->totpart,activeParts,fileParts,readMask); - elbeemDebugOut(debugStrBuffer); - return; - } // fluid sim particles done - #endif // DISABLE_ELBEEM - - if(paf->end < paf->sta) return; - - if( (paf->flag & PAF_OFACE) && (paf->flag & PAF_FACE)==0) return; - - if(me->totvert==0) return; - - if(ob==G.obedit) return; - totpart= (G.rendering)?paf->totpart:(paf->disp*paf->totpart)/100; - if(totpart==0) return; - - /* No returns after this line! */ - - /* material */ - ma= give_current_material(ob, paf->omat); - if(ma) { - if(paf->speedtex) - mtexmove= ma->mtex[paf->speedtex-1]; - mtextime= ma->mtex[paf->timetex-1]; - } + else + VecCopyf(mag_vec,vec_to_part); - disable_speed_curve(1); /* check this... */ + /* at this distance the field is 60 times weaker than maximum */ + if(distance > 2.22 * (size+pa_size)) + break; - /* initialize particles */ - new_particle(paf); + fac = pow((size+pa_size)/distance,6.0); + + fac = - fac * (1.0 - fac) / distance; - /* reset deflector cache, sumohandle is free, but its still sorta abuse... (ton) */ - for(base= G.scene->base.first; base; base= base->next) - base->object->sumohandle= NULL; + /* limit the repulsive term drastically to avoid huge forces */ + fac = ((fac>2.0) ? 2.0 : fac); - /* all object positions from start to end */ - paf_sta= (int)floor(paf->sta); - paf_end= (int)ceil(paf->end); - if((paf->flag & PAF_STATIC)==0) - mcache= cache_object_matrices(ob, paf_sta, paf_end); - - /* mult generations? */ - for(a=0; a<PAF_MAXMULT; a++) { - if(paf->mult[a]!=0.0) { - /* interesting formula! this way after 'x' generations the total is paf->totpart */ - totpart= (int)(totpart / (1.0+paf->mult[a]*paf->child[a])); + /* 0.003715 is the fac value at 2.22 times (size+pa_size), + substracted to avoid discontinuity at the border + */ + VecMulf(mag_vec, force_val * (fac-0.0037315)); + VecAddf(field,field,mag_vec); + break; } - else break; } +} - /* for static particles, calculate system on current frame (? ton) */ - if(ma) do_mat_ipo(ma); - - /* matrix invert for static too */ - Mat4Invert(ob->imat, ob->obmat); - Mat4CpyMat4(paf->imat, ob->imat); /* used for duplicators */ - - /* new random generator */ - rng = rng_new(paf->seed); - - /* otherwise it goes way too fast */ - force[0]= paf->force[0]*0.05f; - force[1]= paf->force[1]*0.05f; - force[2]= paf->force[2]*0.05f; - - if( paf->flag & PAF_STATIC ) deform= 0; - else { - Object *parlatt= modifiers_isDeformedByLattice(ob); - if(parlatt) { - deform= 1; - init_latt_deform(parlatt, 0); - } - } - - /* get the effectors */ - effectorbase= pdInitEffectors(ob, paf->group); - - /* init geometry, return is 6 x float * me->totvert in size */ - vertexcosnos= (VeNoCo *)mesh_get_mapped_verts_nors(ob); - facelist= me->mface; - totvert= me->totvert; - totface= me->totface; - - /* if vertexweights or even distribution, it makes weight tables, also checks where it emits from */ - make_weight_tables(paf, me, totpart, vertexcosnos, totvert, facelist, totface, &voweights, &foweights); - - /* vertexweights can define lengths too */ - make_length_tables(paf, me, totvert, facelist, totface, &volengths, &folengths); - - /* now define where to emit from, if there are face weights we skip vertices */ - if(paf->flag & PAF_OFACE) totvert= 0; - if((paf->flag & PAF_FACE)==0) totface= 0; - if(foweights) totvert= 0; - - /* initialize give_mesh_particle_coord */ - if(totface) - give_mesh_particle_coord(paf, vertexcosnos, facelist, totpart, totface, NULL, NULL); - - /* correction for face timing when using weighted average */ - if(totface && foweights) { - maxw= (paf->end-paf->sta)/foweights[0]; - } - else if(totvert && voweights) { - maxw= (paf->end-paf->sta)/voweights[0]; - } - - /* for loop below */ - if (paf->flag & PAF_STATIC) { - ftime = G.scene->r.cfra; - dtime= 0.0f; - } else { - ftime= paf->sta; - dtime= (paf->end - paf->sta)/(float)totpart; - } - - curface= curvert= 0; - for(a=0; a<totpart; a++, ftime+=dtime) { - - /* we set waitcursor only when a half second expired, particles now are realtime updated */ - if(waitcursor_set==0 && (a % 256)==255) { - double seconds= PIL_check_seconds_timer(); - if(seconds - startseconds > 0.5) { - //XXX waitcursor(1); - waitcursor_set= 1; - } - } - - pa= new_particle(paf); - pa->time= ftime; +/* -------- pdDoEffectors() -------- + generic force/speed system, now used for particles and softbodies + lb = listbase with objects that take part in effecting + opco = global coord, as input + force = force accumulator + speed = actual current speed which can be altered + cur_time = "external" time in frames, is constant for static particles + loc_time = "local" time in frames, range <0-1> for the lifetime of particle + par_layer = layer the caller is in + flags = only used for softbody wind now + guide = old speed of particle - /* get coordinates from faces, only when vertices set to zero */ - if(totvert==0 && totface) { - int curjit; - - /* use weight table, we have to do faces in order to be able to use jitter table... */ - if(foweights) { - - if(foweights[curface] < 1.0f) { - float remainder= 0.0f; - - while(remainder + foweights[curface] < 1.0f && curface<totface-1) { - remainder += foweights[curface]; - curface++; - } - /* if this is the last face, the foweights[] can be zero, so we don't add a particle extra */ - if(curface!=totface-1) - foweights[curface] += remainder; - - maxw= (paf->end-paf->sta)/foweights[curface]; - } +*/ +void pdDoEffectors(ListBase *lb, float *opco, float *force, float *speed, float cur_time, float loc_time, unsigned int flags) +{ +/* + Modifies the force on a particle according to its + relation with the effector object + Different kind of effectors include: + Forcefields: Gravity-like attractor + (force power is related to the inverse of distance to the power of a falloff value) + Vortex fields: swirling effectors + (particles rotate around Z-axis of the object. otherwise, same relation as) + (Forcefields, but this is not done through a force/acceleration) + Guide: particles on a path + (particles are guided along a curve bezier or old nurbs) + (is independent of other effectors) +*/ + Object *ob; + pEffectorCache *ec; + PartDeflect *pd; + + float distance, vec_to_part[3]; + float falloff; - if(foweights[curface]==0.0f) - break; /* WARN skips here out of particle generating */ - else { - if(foweights[curface] >= 1.0f) /* note the >= here, this because of the < 1.0f above, it otherwise will stick to 1 face forever */ - foweights[curface] -= 1.0f; - - curjit= (int) foweights[curface]; - give_mesh_particle_coord(paf, vertexcosnos, facelist+curface, a, curjit, co, no); - - /* time correction to make particles appear evenly, maxw does interframe (0-1) */ - pa->time= paf->sta + maxw*foweights[curface]; - } - } - else { - curface= a % totface; - curjit= a/totface; - give_mesh_particle_coord(paf, vertexcosnos, facelist+curface, a, curjit, co, no); - } - } - /* get coordinates from vertices */ - if(totvert) { - /* use weight table */ - if(voweights) { - - if(voweights[curvert] < 1.0f) { - float remainder= 0.0f; - - while(remainder + voweights[curvert] < 1.0f && curvert<totvert-1) { - remainder += voweights[curvert]; - curvert++; - } - voweights[curvert] += remainder; - maxw= (paf->end-paf->sta)/voweights[curvert]; - } - - if(voweights[curvert]==0.0f) - break; /* WARN skips here out of particle generating */ - else { - if(voweights[curvert] > 1.0f) - voweights[curvert] -= 1.0f; - - /* time correction to make particles appear evenly */ - pa->time= paf->sta + maxw*voweights[curvert]; - } - } - else { - curvert= a % totvert; - if(a >= totvert && totface) - totvert= 0; - } + /* Cycle through collected objects, get total of (1/(gravity_strength * dist^gravity_power)) */ + /* Check for min distance here? (yes would be cool to add that, ton) */ + + for(ec = lb->first; ec; ec= ec->next) { + /* object effectors were fully checked to be OK to evaluate! */ + ob= ec->ob; + pd= ob->pd; - VECCOPY(co, vertexcosnos[curvert].co); - VECCOPY(no, vertexcosnos[curvert].no); - } - - VECCOPY(pa->co, co); - - /* dynamic options */ - if((paf->flag & PAF_STATIC)==0) { - int cur; + /* Get IPO force strength and fall off values here */ + where_is_object_time(ob,cur_time); - /* particle retiming with texture */ - if(mtextime && (paf->flag2 & PAF_TEXTIME)) { - float tin, tr, tg, tb, ta, orco[3]; - - /* calculate normalized orco */ - orco[0] = (co[0]-me->loc[0])/me->size[0]; - orco[1] = (co[1]-me->loc[1])/me->size[1]; - orco[2] = (co[2]-me->loc[2])/me->size[2]; - externtex(mtextime, orco, &tin, &tr, &tg, &tb, &ta); - - if(paf->flag2neg & PAF_TEXTIME) - pa->time = paf->sta + (paf->end - paf->sta)*tin; - else - pa->time = paf->sta + (paf->end - paf->sta)*(1.0f-tin); - } + /* use center of object for distance calculus */ + VecSubf(vec_to_part, opco, ob->obmat[3]); + distance = VecLength(vec_to_part); - /* set ob at correct time, we use cached matrices */ - cur= (int)floor(pa->time) + 1 ; /* + 1 has a reason: (obmat/prevobmat) otherwise comet-tails start too late */ - - if(cur <= paf_end) mcnow= mcache + cur - paf_sta; - else mcnow= mcache + paf_end - paf_sta; - - if(cur > paf_sta) mcprev= mcnow-1; - else mcprev= mcache; - - /* move to global space */ - Mat4MulVecfl(mcnow->obmat, pa->co); + falloff=effector_falloff(pd,ob->obmat[2],vec_to_part); - VECCOPY(vec, co); - Mat4MulVecfl(mcprev->obmat, vec); - - /* first start speed: object */ - VECSUB(pa->no, pa->co, vec); - - VecMulf(pa->no, paf->obfac); - - /* calculate the correct inter-frame */ - fac= (pa->time- (float)floor(pa->time)); - pa->co[0]= fac*pa->co[0] + (1.0f-fac)*vec[0]; - pa->co[1]= fac*pa->co[1] + (1.0f-fac)*vec[1]; - pa->co[2]= fac*pa->co[2] + (1.0f-fac)*vec[2]; - - /* start speed: normal */ - if(paf->normfac!=0.0) { - /* imat is transpose ! */ - VECCOPY(vec, no); - Mat3MulVecfl(mcnow->imat, vec); - - Normalize(vec); - VecMulf(vec, paf->normfac); - VECADD(pa->no, pa->no, vec); - } - } + if(falloff<=0.0f) + ; /* don't do anything */ else { - if(paf->normfac!=0.0) { - VECCOPY(pa->no, no); - Normalize(pa->no); - VecMulf(pa->no, paf->normfac); - } - } - - pa->lifetime= paf->lifetime; - if(paf->randlife!=0.0) { - pa->lifetime*= 1.0f + paf->randlife*(rng_getFloat(rng) - 0.5f); - } - pa->mat_nr= paf->omat; - - if(folengths) - pa->lifetime*= folengths[curface]; - - make_particle_keys(rng, ob, 0, a, paf, pa, force, deform, mtexmove, effectorbase); - } - - /* free stuff */ - give_mesh_particle_coord(NULL, NULL, NULL, 0, 0, NULL, NULL); - MEM_freeN(vertexcosnos); - if(voweights) MEM_freeN(voweights); - if(foweights) MEM_freeN(foweights); - if(volengths) MEM_freeN(volengths); - if(folengths) MEM_freeN(folengths); - if(mcache) MEM_freeN(mcache); - rng_free(rng); - - if(deform) end_latt_deform(); - - if(effectorbase) - pdEndEffectors(effectorbase); - - /* reset deflector cache */ - for(base= G.scene->base.first; base; base= base->next) { - if(base->object->sumohandle) { + float field[3]={0,0,0}, tmp[3]; + VECCOPY(field, force); + do_physical_effector(ob, opco, pd->forcefield,pd->f_strength,distance, + falloff,pd->f_dist,pd->f_damp,ob->obmat[2],vec_to_part, + speed,force,pd->flag&PFIELD_PLANAR, pd->rng, pd->f_noise, 0.0f, 0.0f); - MEM_freeN(base->object->sumohandle); - base->object->sumohandle= NULL; + // for softbody backward compatibility + if(flags & PE_WIND_AS_SPEED){ + VECSUB(tmp, force, field); + VECSUB(speed, speed, tmp); + } } } - - disable_speed_curve(0); - - //XXX if(waitcursor_set) waitcursor(0); } - diff --git a/source/blender/blenkernel/intern/exotic.c b/source/blender/blenkernel/intern/exotic.c index 7a138f9ac68..28ed74aa9db 100644 --- a/source/blender/blenkernel/intern/exotic.c +++ b/source/blender/blenkernel/intern/exotic.c @@ -76,10 +76,14 @@ #include <fcntl.h> #include <string.h> -#ifndef WIN32 +#ifndef _WIN32 #include <unistd.h> #else #include <io.h> +#define open _open +#define read _read +#define close _close +#define write _write #endif #include "MEM_guardedalloc.h" @@ -116,7 +120,9 @@ #include "BKE_curve.h" #include "BKE_customdata.h" +#ifndef DISABLE_PYTHON #include "BPY_extern.h" +#endif #include "zlib.h" @@ -869,7 +875,7 @@ static void read_videoscape_lamp(char *str) Object *ob; Lamp *la; FILE *fp; - float vec[3], *q1; + float vec[3], q1[4]; int tot, val; char s[50]; @@ -897,7 +903,7 @@ static void read_videoscape_lamp(char *str) fscanf(fp, "%f %f %f\n", ob->loc, ob->loc+1, ob->loc+2); val= fscanf(fp, "%f %f %f\n", vec, vec+1, vec+2); - q1= vectoquat(vec, 5, 2); + vectoquat(vec, 5, 2, q1); QuatToEul(q1, ob->rot); if(val<=0) break; @@ -2091,8 +2097,10 @@ static void displist_to_mesh(DispList *dlfirst) } for(a=0; a<dl->parts; a++) { - - DL_SURFINDEX(dl->flag & DL_CYCL_U, dl->flag & DL_CYCL_V, dl->nr, dl->parts); + + if (surfindex_displist(dl, a, &b, &p1, &p2, &p3, &p4)==0) + break; + p1+= startve; p2+= startve; p3+= startve; @@ -2419,6 +2427,7 @@ int BKE_read_exotic(char *name) read_stl_mesh_binary(name); retval = 1; } +#ifndef DISABLE_PYTHON // TODO: this should not be in the kernel... else { // unknown format, call Python importloader if (BPY_call_importloader(name)) { @@ -2428,6 +2437,7 @@ int BKE_read_exotic(char *name) } } +#endif /* DISABLE_PYTHON */ //XXX waitcursor(0); } } @@ -2626,17 +2636,17 @@ static void write_videoscape_mesh(Object *ob, char *str) if(evl->v4==0) { fprintf(fp, "3 %ld %ld %ld 0x%x\n", - evl->v1->tmp.l, - evl->v2->tmp.l, - evl->v3->tmp.l, + (long int) evl->v1->tmp.l, + (long int) evl->v2->tmp.l, + (long int) evl->v3->tmp.l, kleur[evl->mat_nr]); } else { fprintf(fp, "4 %ld %ld %ld %ld 0x%x\n", - evl->v1->tmp.l, - evl->v2->tmp.l, - evl->v3->tmp.l, - evl->v4->tmp.l, + (long int) evl->v1->tmp.l, + (long int) evl->v2->tmp.l, + (long int) evl->v3->tmp.l, + (long int) evl->v4->tmp.l, kleur[evl->mat_nr]); } evl= evl->next; diff --git a/source/blender/blenkernel/intern/fluidsim.c b/source/blender/blenkernel/intern/fluidsim.c new file mode 100644 index 00000000000..29c4e0f2fb5 --- /dev/null +++ b/source/blender/blenkernel/intern/fluidsim.c @@ -0,0 +1,660 @@ +/** + * fluidsim.c + * + * + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * The Original Code is Copyright (C) Blender Foundation + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): none yet. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#include "MEM_guardedalloc.h" + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_object_force.h" // for pointcache +#include "DNA_particle_types.h" +#include "DNA_scene_types.h" // N_T + +#include "BLI_arithb.h" +#include "BLI_blenlib.h" + +#include "BKE_cdderivedmesh.h" +#include "BKE_customdata.h" +#include "BKE_DerivedMesh.h" +#include "BKE_fluidsim.h" +#include "BKE_global.h" +#include "BKE_modifier.h" +#include "BKE_mesh.h" +#include "BKE_pointcache.h" +#include "BKE_utildefines.h" + +// headers for fluidsim bobj meshes +#include <stdlib.h> +#include "LBM_fluidsim.h" +#include "elbeem.h" +#include <zlib.h> +#include <string.h> +#include <stdio.h> + +/* ************************* fluidsim bobj file handling **************************** */ + +// ----------------------------------------- +// forward decleration +// ----------------------------------------- + +// ----------------------------------------- + +void fluidsim_init(FluidsimModifierData *fluidmd) +{ +#ifndef DISABLE_ELBEEM + if(fluidmd) + { + FluidsimSettings *fss = MEM_callocN(sizeof(FluidsimSettings), "fluidsimsettings"); + + fluidmd->fss = fss; + + if(!fss) + return; + + fss->type = OB_FSBND_NOSLIP; + fss->show_advancedoptions = 0; + + fss->resolutionxyz = 50; + fss->previewresxyz = 25; + fss->realsize = 0.03; + fss->guiDisplayMode = 2; // preview + fss->renderDisplayMode = 3; // render + + fss->viscosityMode = 2; // default to water + fss->viscosityValue = 1.0; + fss->viscosityExponent = 6; + + // dg TODO: change this to [] + fss->gravx = 0.0; + fss->gravy = 0.0; + fss->gravz = -9.81; + fss->animStart = 0.0; + fss->animEnd = 0.30; + fss->gstar = 0.005; // used as normgstar + fss->maxRefine = -1; + // maxRefine is set according to resolutionxyz during bake + + // fluid/inflow settings + // fss->iniVel --> automatically set to 0 + + /* elubie: changed this to default to the same dir as the render output + to prevent saving to C:\ on Windows */ + BLI_strncpy(fss->surfdataPath, btempdir, FILE_MAX); + + // first init of bounding box + // no bounding box needed + + // todo - reuse default init from elbeem! + fss->typeFlags = 0; + fss->domainNovecgen = 0; + fss->volumeInitType = 1; // volume + fss->partSlipValue = 0.0; + + fss->generateTracers = 0; + fss->generateParticles = 0.0; + fss->surfaceSmoothing = 1.0; + fss->surfaceSubdivs = 1.0; + fss->particleInfSize = 0.0; + fss->particleInfAlpha = 0.0; + + // init fluid control settings + fss->attractforceStrength = 0.2; + fss->attractforceRadius = 0.75; + fss->velocityforceStrength = 0.2; + fss->velocityforceRadius = 0.75; + fss->cpsTimeStart = fss->animStart; + fss->cpsTimeEnd = fss->animEnd; + fss->cpsQuality = 10.0; // 1.0 / 10.0 => means 0.1 width + + /* + BAD TODO: this is done in buttons_object.c in the moment + Mesh *mesh = ob->data; + // calculate bounding box + fluid_get_bb(mesh->mvert, mesh->totvert, ob->obmat, fss->bbStart, fss->bbSize); + */ + + // (ab)used to store velocities + fss->meshSurfNormals = NULL; + + fss->lastgoodframe = -1; + + fss->flag = 0; + + } +#endif + return; +} + +void fluidsim_free(FluidsimModifierData *fluidmd) +{ +#ifndef DISABLE_ELBEEM + if(fluidmd) + { + if(fluidmd->fss->meshSurfNormals) + { + MEM_freeN(fluidmd->fss->meshSurfNormals); + fluidmd->fss->meshSurfNormals = NULL; + } + MEM_freeN(fluidmd->fss); + } +#endif + return; +} + +DerivedMesh *fluidsimModifier_do(FluidsimModifierData *fluidmd, Object *ob, DerivedMesh *dm, int useRenderParams, int isFinalCalc) +{ +#ifndef DISABLE_ELBEEM + DerivedMesh *result = NULL; + int framenr; + FluidsimSettings *fss = NULL; + + framenr= (int)G.scene->r.cfra; + + // only handle fluidsim domains + if(fluidmd && fluidmd->fss && (fluidmd->fss->type != OB_FLUIDSIM_DOMAIN)) + return dm; + + // sanity check + if(!fluidmd || (fluidmd && !fluidmd->fss)) + return dm; + + fss = fluidmd->fss; + + // timescale not supported yet + // clmd->sim_parms->timescale= timescale; + + // support reversing of baked fluid frames here + if((fss->flag & OB_FLUIDSIM_REVERSE) && (fss->lastgoodframe >= 0)) + { + framenr = fss->lastgoodframe - framenr + 1; + CLAMP(framenr, 1, fss->lastgoodframe); + } + + /* try to read from cache */ + if(((fss->lastgoodframe >= framenr) || (fss->lastgoodframe < 0)) && (result = fluidsim_read_cache(ob, dm, fluidmd, framenr, useRenderParams))) + { + // fss->lastgoodframe = framenr; // set also in src/fluidsim.c + return result; + } + else + { + // display last known good frame + if(fss->lastgoodframe >= 0) + { + if((result = fluidsim_read_cache(ob, dm, fluidmd, fss->lastgoodframe, useRenderParams))) + { + return result; + } + + // it was supposed to be a valid frame but it isn't! + fss->lastgoodframe = framenr - 1; + + + // this could be likely the case when you load an old fluidsim + if((result = fluidsim_read_cache(ob, dm, fluidmd, fss->lastgoodframe, useRenderParams))) + { + return result; + } + } + + result = CDDM_copy(dm); + + if(result) + { + return result; + } + } + + return dm; +#else + return NULL; +#endif +} + +#ifndef DISABLE_ELBEEM +/* read .bobj.gz file into a fluidsimDerivedMesh struct */ +static DerivedMesh *fluidsim_read_obj(char *filename) +{ + int wri,i,j; + float wrf; + int gotBytes; + gzFile gzf; + int numverts = 0, numfaces = 0; + DerivedMesh *dm = NULL; + MFace *mface; + MVert *mvert; + short *normals; + + // ------------------------------------------------ + // get numverts + numfaces first + // ------------------------------------------------ + gzf = gzopen(filename, "rb"); + if (!gzf) + { + return NULL; + } + + // read numverts + gotBytes = gzread(gzf, &wri, sizeof(wri)); + numverts = wri; + + // skip verts + for(i=0; i<numverts*3; i++) + { + gotBytes = gzread(gzf, &wrf, sizeof( wrf )); + } + + // read number of normals + gotBytes = gzread(gzf, &wri, sizeof(wri)); + + // skip normals + for(i=0; i<numverts*3; i++) + { + gotBytes = gzread(gzf, &wrf, sizeof( wrf )); + } + + /* get no. of triangles */ + gotBytes = gzread(gzf, &wri, sizeof(wri)); + numfaces = wri; + + gzclose( gzf ); + // ------------------------------------------------ + + if(!numfaces || !numverts) + return NULL; + + gzf = gzopen(filename, "rb"); + if (!gzf) + { + return NULL; + } + + dm = CDDM_new(numverts, 0, numfaces); + + if(!dm) + { + gzclose( gzf ); + return NULL; + } + + // read numverts + gotBytes = gzread(gzf, &wri, sizeof(wri)); + + // read vertex position from file + mvert = CDDM_get_verts(dm); + for(i=0; i<numverts; i++) + { + MVert *mv = &mvert[i]; + + for(j=0; j<3; j++) + { + gotBytes = gzread(gzf, &wrf, sizeof( wrf )); + mv->co[j] = wrf; + } + } + + // should be the same as numverts + gotBytes = gzread(gzf, &wri, sizeof(wri)); + if(wri != numverts) + { + if(dm) + dm->release(dm); + gzclose( gzf ); + return NULL; + } + + normals = MEM_callocN(sizeof(short) * numverts * 3, "fluid_tmp_normals" ); + if(!normals) + { + if(dm) + dm->release(dm); + gzclose( gzf ); + return NULL; + } + + // read normals from file (but don't save them yet) + for(i=0; i<numverts*3; i++) + { + gotBytes = gzread(gzf, &wrf, sizeof( wrf )); + normals[i] = (short)(wrf*32767.0f); + } + + /* read no. of triangles */ + gotBytes = gzread(gzf, &wri, sizeof(wri)); + + if(wri!=numfaces) + printf("Fluidsim: error in reading data from file.\n"); + + // read triangles from file + mface = CDDM_get_faces(dm); + for(i=0; i<numfaces; i++) + { + int face[4]; + MFace *mf = &mface[i]; + + gotBytes = gzread(gzf, &(face[0]), sizeof( face[0] )); + gotBytes = gzread(gzf, &(face[1]), sizeof( face[1] )); + gotBytes = gzread(gzf, &(face[2]), sizeof( face[2] )); + face[3] = 0; + + // check if 3rd vertex has index 0 (not allowed in blender) + if(face[2]) + { + mf->v1 = face[0]; + mf->v2 = face[1]; + mf->v3 = face[2]; + } + else + { + mf->v1 = face[1]; + mf->v2 = face[2]; + mf->v3 = face[0]; + } + mf->v4 = face[3]; + + test_index_face(mf, NULL, 0, 3); + } + + gzclose( gzf ); + + CDDM_calc_edges(dm); + + CDDM_apply_vert_normals(dm, (short (*)[3])normals); + MEM_freeN(normals); + + // CDDM_calc_normals(result); + + return dm; +} + +DerivedMesh *fluidsim_read_cache(Object *ob, DerivedMesh *orgdm, FluidsimModifierData *fluidmd, int framenr, int useRenderParams) +{ + int displaymode = 0; + int curFrame = framenr - 1 /*G.scene->r.sfra*/; /* start with 0 at start frame */ + char targetDir[FILE_MAXFILE+FILE_MAXDIR], targetFile[FILE_MAXFILE+FILE_MAXDIR]; + FluidsimSettings *fss = fluidmd->fss; + DerivedMesh *dm = NULL; + MFace *mface; + int numfaces; + int mat_nr, flag, i; + + if(!useRenderParams) { + displaymode = fss->guiDisplayMode; + } else { + displaymode = fss->renderDisplayMode; + } + + strncpy(targetDir, fss->surfdataPath, FILE_MAXDIR); + + // use preview or final mesh? + if(displaymode==1) + { + // just display original object + return NULL; + } + else if(displaymode==2) + { + strcat(targetDir,"fluidsurface_preview_####"); + } + else + { // 3 + strcat(targetDir,"fluidsurface_final_####"); + } + + BLI_convertstringcode(targetDir, G.sce); + BLI_convertstringframe(targetDir, curFrame); // fixed #frame-no + + strcpy(targetFile,targetDir); + strcat(targetFile, ".bobj.gz"); + + dm = fluidsim_read_obj(targetFile); + + if(!dm) + { + // switch, abort background rendering when fluidsim mesh is missing + const char *strEnvName2 = "BLENDER_ELBEEMBOBJABORT"; // from blendercall.cpp + + if(G.background==1) { + if(getenv(strEnvName2)) { + int elevel = atoi(getenv(strEnvName2)); + if(elevel>0) { + printf("Env. var %s set, fluid sim mesh '%s' not found, aborting render...\n",strEnvName2, targetFile); + exit(1); + } + } + } + + // display org. object upon failure which is in dm + return NULL; + } + + // assign material + flags to new dm + mface = orgdm->getFaceArray(orgdm); + mat_nr = mface[0].mat_nr; + flag = mface[0].flag; + + mface = dm->getFaceArray(dm); + numfaces = dm->getNumFaces(dm); + for(i=0; i<numfaces; i++) + { + mface[i].mat_nr = mat_nr; + mface[i].flag = flag; + } + + // load vertex velocities, if they exist... + // TODO? use generate flag as loading flag as well? + // warning, needs original .bobj.gz mesh loading filename + if(displaymode==3) + { + fluidsim_read_vel_cache(fluidmd, dm, targetFile); + } + else + { + if(fss->meshSurfNormals) + MEM_freeN(fss->meshSurfNormals); + + fss->meshSurfNormals = NULL; + } + + return dm; +} + + +/* read zipped fluidsim velocities into the co's of the fluidsimsettings normals struct */ +void fluidsim_read_vel_cache(FluidsimModifierData *fluidmd, DerivedMesh *dm, char *filename) +{ + int wri, i, j; + float wrf; + gzFile gzf; + FluidsimSettings *fss = fluidmd->fss; + int len = strlen(filename); + int totvert = dm->getNumVerts(dm); + float *velarray = NULL; + + // mesh and vverts have to be valid from loading... + + if(fss->meshSurfNormals) + MEM_freeN(fss->meshSurfNormals); + + if(len<7) + { + return; + } + + if(fss->domainNovecgen>0) return; + + // abusing pointer to hold an array of 3d-velocities + fss->meshSurfNormals = MEM_callocN(sizeof(float)*3*dm->getNumVerts(dm), "Fluidsim_velocities"); + // abusing pointer to hold an INT + fss->meshSurface = SET_INT_IN_POINTER(totvert); + + velarray = (float *)fss->meshSurfNormals; + + // .bobj.gz , correct filename + // 87654321 + filename[len-6] = 'v'; + filename[len-5] = 'e'; + filename[len-4] = 'l'; + + gzf = gzopen(filename, "rb"); + if (!gzf) + { + MEM_freeN(fss->meshSurfNormals); + fss->meshSurfNormals = NULL; + return; + } + + gzread(gzf, &wri, sizeof( wri )); + if(wri != totvert) + { + MEM_freeN(fss->meshSurfNormals); + fss->meshSurfNormals = NULL; + return; + } + + for(i=0; i<totvert;i++) + { + for(j=0; j<3; j++) + { + gzread(gzf, &wrf, sizeof( wrf )); + velarray[3*i + j] = wrf; + } + } + + gzclose(gzf); +} + +void fluid_get_bb(MVert *mvert, int totvert, float obmat[][4], + /*RET*/ float start[3], /*RET*/ float size[3] ) +{ + float bbsx=0.0, bbsy=0.0, bbsz=0.0; + float bbex=1.0, bbey=1.0, bbez=1.0; + int i; + float vec[3]; + + VECCOPY(vec, mvert[0].co); + Mat4MulVecfl(obmat, vec); + bbsx = vec[0]; bbsy = vec[1]; bbsz = vec[2]; + bbex = vec[0]; bbey = vec[1]; bbez = vec[2]; + + for(i = 1; i < totvert; i++) { + VECCOPY(vec, mvert[i].co); + Mat4MulVecfl(obmat, vec); + + if(vec[0] < bbsx){ bbsx= vec[0]; } + if(vec[1] < bbsy){ bbsy= vec[1]; } + if(vec[2] < bbsz){ bbsz= vec[2]; } + if(vec[0] > bbex){ bbex= vec[0]; } + if(vec[1] > bbey){ bbey= vec[1]; } + if(vec[2] > bbez){ bbez= vec[2]; } + } + + // return values... + if(start) { + start[0] = bbsx; + start[1] = bbsy; + start[2] = bbsz; + } + if(size) { + size[0] = bbex-bbsx; + size[1] = bbey-bbsy; + size[2] = bbez-bbsz; + } +} + +//------------------------------------------------------------------------------- +// old interface +//------------------------------------------------------------------------------- + + + +//------------------------------------------------------------------------------- +// file handling +//------------------------------------------------------------------------------- + +void initElbeemMesh(struct Object *ob, + int *numVertices, float **vertices, + int *numTriangles, int **triangles, + int useGlobalCoords, int modifierIndex) +{ + DerivedMesh *dm = NULL; + MVert *mvert; + MFace *mface; + int countTris=0, i, totvert, totface; + float *verts; + int *tris; + + dm = mesh_create_derived_index_render(ob, CD_MASK_BAREMESH, modifierIndex); + //dm = mesh_create_derived_no_deform(ob,NULL); + + mvert = dm->getVertArray(dm); + mface = dm->getFaceArray(dm); + totvert = dm->getNumVerts(dm); + totface = dm->getNumFaces(dm); + + *numVertices = totvert; + verts = MEM_callocN( totvert*3*sizeof(float), "elbeemmesh_vertices"); + for(i=0; i<totvert; i++) { + VECCOPY( &verts[i*3], mvert[i].co); + if(useGlobalCoords) { Mat4MulVecfl(ob->obmat, &verts[i*3]); } + } + *vertices = verts; + + for(i=0; i<totface; i++) { + countTris++; + if(mface[i].v4) { countTris++; } + } + *numTriangles = countTris; + tris = MEM_callocN( countTris*3*sizeof(int), "elbeemmesh_triangles"); + countTris = 0; + for(i=0; i<totface; i++) { + int face[4]; + face[0] = mface[i].v1; + face[1] = mface[i].v2; + face[2] = mface[i].v3; + face[3] = mface[i].v4; + + tris[countTris*3+0] = face[0]; + tris[countTris*3+1] = face[1]; + tris[countTris*3+2] = face[2]; + countTris++; + if(face[3]) { + tris[countTris*3+0] = face[0]; + tris[countTris*3+1] = face[2]; + tris[countTris*3+2] = face[3]; + countTris++; + } + } + *triangles = tris; + + dm->release(dm); +} + +#endif // DISABLE_ELBEEM + diff --git a/source/blender/blenkernel/intern/font.c b/source/blender/blenkernel/intern/font.c index fb58c076c95..a8a97742c24 100644 --- a/source/blender/blenkernel/intern/font.c +++ b/source/blender/blenkernel/intern/font.c @@ -160,41 +160,61 @@ utf8slen(char *src) return size; } -int utf8towchar_(wchar_t *w, char *c) + +/* Converts Unicode to wchar + +According to RFC 3629 "UTF-8, a transformation format of ISO 10646" +(http://tools.ietf.org/html/rfc3629), the valid UTF-8 encoding are: + + Char. number range | UTF-8 octet sequence + (hexadecimal) | (binary) + --------------------+--------------------------------------------- + 0000 0000-0000 007F | 0xxxxxxx + 0000 0080-0000 07FF | 110xxxxx 10xxxxxx + 0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx + 0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx + +If the encoding incidated by the first character is incorrect (because the +1 to 3 following characters do not match 10xxxxxx), the output is a '?' and +only a single input character is consumed. + +*/ + +int utf8towchar(wchar_t *w, char *c) { int len=0; + if(w==NULL || c==NULL) return(0); - //printf("%s\n",c); - while(*c) - { - if(*c & 0x80) - { - if(*c & 0x40) - { - if(*c & 0x20) - { - if(*c & 0x10) - { - *w=(c[0] & 0x0f)<<18 | (c[1]&0x1f)<<12 | (c[2]&0x3f)<<6 | (c[3]&0x7f); - c++; - } - else - *w=(c[0] & 0x1f)<<12 | (c[1]&0x3f)<<6 | (c[2]&0x7f); - c++; - } - else - *w=(((c[0] &0x3f)<<6) | (c[1]&0x7f)); + + while(*c) { + if ((*c & 0xe0) == 0xc0) { + if((c[1] & 0x80) && (c[1] & 0x40) == 0x00) { + *w=((c[0] &0x1f)<<6) | (c[1]&0x3f); c++; + } else { + *w = '?'; } - else - *w=(c[0] & 0x7f); + } else if ((*c & 0xf0) == 0xe0) { + if((c[1] & c[2] & 0x80) && ((c[1] | c[2]) & 0x40) == 0x00) { + *w=((c[0] & 0x0f)<<12) | ((c[1]&0x3f)<<6) | (c[2]&0x3f); + c += 2; + } else { + *w = '?'; } - else - *w=(c[0] & 0x7f); - c++; - w++; - len++; - } + } else if ((*c & 0xf8) == 0xf0) { + if((c[1] & c[2] & c[3] & 0x80) && ((c[1] | c[2] | c[3]) & 0x40) == 0x00) { + *w=((c[0] & 0x07)<<18) | ((c[1]&0x1f)<<12) | ((c[2]&0x3f)<<6) | (c[3]&0x3f); + c += 3; + } else { + *w = '?'; + } + } else + *w=(c[0] & 0x7f); + + c++; + w++; + len++; + } return len; } @@ -443,7 +463,7 @@ static void build_underline(Curve *cu, float x1, float y1, float x2, float y2, i if (nu2 == NULL) return; nu2->resolu= cu->resolu; nu2->bezt = NULL; - nu2->knotsu = nu2->knotsv = 0; + nu2->knotsu = nu2->knotsv = NULL; nu2->flag= 0; nu2->charidx = charidx+1000; if (mat_nr > 0) nu2->mat_nr= mat_nr-1; @@ -532,7 +552,7 @@ static void buildchar(Curve *cu, unsigned long character, CharInfo *info, float memcpy(nu2, nu1, sizeof(struct Nurb)); nu2->resolu= cu->resolu; nu2->bp = 0; - nu2->knotsu = nu2->knotsv = 0; + nu2->knotsu = nu2->knotsv = NULL; nu2->flag= CU_SMOOTH; nu2->charidx = charidx; if (info->mat_nr) { @@ -664,7 +684,7 @@ struct chartrans *text_to_curve(Object *ob, int mode) utf8len = utf8slen(cu->str); tmp = mem = MEM_callocN(((utf8len + 1) * sizeof(wchar_t)), "convertedmem"); - utf8towchar_(mem, cu->str); + utf8towchar(mem, cu->str); // Count the wchar_t string length slen = wcslen(mem); diff --git a/source/blender/blenkernel/intern/group.c b/source/blender/blenkernel/intern/group.c index 201d93220f9..0d2f86bb151 100644 --- a/source/blender/blenkernel/intern/group.c +++ b/source/blender/blenkernel/intern/group.c @@ -41,6 +41,7 @@ #include "DNA_object_types.h" #include "DNA_nla_types.h" #include "DNA_scene_types.h" +#include "DNA_particle_types.h" #include "BLI_blenlib.h" @@ -77,14 +78,37 @@ void unlink_group(Group *group) { Material *ma; Object *ob; + Scene *sce; + SceneRenderLayer *srl; + ParticleSystem *psys; for(ma= G.main->mat.first; ma; ma= ma->id.next) { if(ma->group==group) ma->group= NULL; } + for(ma= G.main->mat.first; ma; ma= ma->id.next) { + if(ma->group==group) + ma->group= NULL; + } + for (sce= G.main->scene.first; sce; sce= sce->id.next) { + Base *base= sce->base.first; + + /* ensure objects are not in this group */ + for(; base; base= base->next) { + if(rem_from_group(group, base->object) && find_group(base->object, NULL)==NULL) { + base->object->flag &= ~OB_FROMGROUP; + base->flag &= ~OB_FROMGROUP; + } + } + + for(srl= sce->r.layers.first; srl; srl= srl->next) { + if (srl->light_override==group) + srl->light_override= NULL; + } + } + for(ob= G.main->object.first; ob; ob= ob->id.next) { bActionStrip *strip; - PartEff *paf; if(ob->dup_group==group) { ob->dup_group= NULL; @@ -95,13 +119,17 @@ void unlink_group(Group *group) strip->object= NULL; } } - for(paf= ob->effect.first; paf; paf= paf->next) { - if(paf->type==EFF_PARTICLE) { - if(paf->group) - paf->group= NULL; - } + + for(psys=ob->particlesystem.first; psys; psys=psys->next){ + if(psys->part->dup_group==group) + psys->part->dup_group= NULL; + if(psys->part->eff_group==group) + psys->part->eff_group= NULL; } } + + /* group stays in library, but no members */ + free_group(group); group->id.us= 0; } @@ -134,11 +162,11 @@ void add_to_group(Group *group, Object *ob) } /* also used for ob==NULL */ -void rem_from_group(Group *group, Object *ob) +int rem_from_group(Group *group, Object *ob) { GroupObject *go, *gon; - - if(group==NULL) return; + int removed = 0; + if(group==NULL) return 0; go= group->gobject.first; while(go) { @@ -146,9 +174,12 @@ void rem_from_group(Group *group, Object *ob) if(go->ob==ob) { BLI_remlink(&group->gobject, go); free_group_object(go); + removed = 1; + /* should break here since an object being in a group twice cant happen? */ } go= gon; } + return removed; } int object_in_group(Object *ob, Group *group) @@ -164,9 +195,12 @@ int object_in_group(Object *ob, Group *group) return 0; } -Group *find_group(Object *ob) +Group *find_group(Object *ob, Group *group) { - Group *group= G.main->group.first; + if (group) + group= group->id.next; + else + group= G.main->group.first; while(group) { if(object_in_group(ob, group)) @@ -188,6 +222,20 @@ void group_tag_recalc(Group *group) } } +int group_is_animated(Object *parent, Group *group) +{ + GroupObject *go; + + if(give_timeoffset(parent) != 0.0f || parent->nlastrips.first) + return 1; + + for(go= group->gobject.first; go; go= go->next) + if(go->ob && go->ob->proxy) + return 1; + + return 0; +} + /* only replaces object strips or action when parent nla instructs it */ /* keep checking nla.c though, in case internal structure of strip changes */ static void group_replaces_nla(Object *parent, Object *target, char mode) @@ -228,7 +276,6 @@ static void group_replaces_nla(Object *parent, Object *target, char mode) } } - /* puts all group members in local timing system, after this call you can draw everything, leaves tags in objects to signal it needs further updating */ @@ -238,12 +285,12 @@ void group_handle_recalc_and_update(Object *parent, Group *group) GroupObject *go; /* if animated group... */ - if(parent->sf != 0.0f || parent->nlastrips.first) { + if(give_timeoffset(parent) != 0.0f || parent->nlastrips.first) { int cfrao; /* switch to local time */ cfrao= G.scene->r.cfra; - G.scene->r.cfra -= (int)parent->sf; + G.scene->r.cfra -= (int)give_timeoffset(parent); /* we need a DAG per group... */ for(go= group->gobject.first; go; go= go->next) { diff --git a/source/blender/blenkernel/intern/icons.c b/source/blender/blenkernel/intern/icons.c index f144d2badd1..b9e3c593ddf 100644 --- a/source/blender/blenkernel/intern/icons.c +++ b/source/blender/blenkernel/intern/icons.c @@ -48,6 +48,9 @@ #include "BLI_ghash.h" #include "BKE_icons.h" +#include "BKE_utildefines.h" + +#include "BLO_sys_types.h" // for intptr_t support #define GS(a) (*((short *)(a))) @@ -87,7 +90,7 @@ static int get_next_free_id() return gNextIconId++; /* now we try to find the smallest icon id not stored in the gIcons hash */ - while (BLI_ghash_lookup(gIcons, (void *)startId) && startId>=gFirstIconId) + while (BLI_ghash_lookup(gIcons, SET_INT_IN_POINTER(startId)) && startId>=gFirstIconId) startId++; /* if we found a suitable one that isnt used yet, return it */ @@ -216,7 +219,7 @@ void BKE_icon_changed(int id) if (!id) return; - icon = BLI_ghash_lookup(gIcons, (void *)id); + icon = BLI_ghash_lookup(gIcons, SET_INT_IN_POINTER(id)); if (icon) { @@ -258,7 +261,7 @@ int BKE_icon_getid(struct ID* id) new_icon->drawinfo = 0; new_icon->drawinfo_free = 0; - BLI_ghash_insert(gIcons, (void *)id->icon_id, new_icon); + BLI_ghash_insert(gIcons, SET_INT_IN_POINTER(id->icon_id), new_icon); return id->icon_id; } @@ -267,7 +270,7 @@ Icon* BKE_icon_get(int icon_id) { Icon* icon = 0; - icon = BLI_ghash_lookup(gIcons, (void*)icon_id); + icon = BLI_ghash_lookup(gIcons, SET_INT_IN_POINTER(icon_id)); if (!icon) { printf("BKE_icon_get: Internal error, no icon for icon ID: %d\n", icon_id); @@ -281,7 +284,7 @@ void BKE_icon_set(int icon_id, struct Icon* icon) { Icon* old_icon = 0; - old_icon = BLI_ghash_lookup(gIcons, (void*)icon_id); + old_icon = BLI_ghash_lookup(gIcons, SET_INT_IN_POINTER(icon_id)); if (old_icon) { @@ -289,7 +292,7 @@ void BKE_icon_set(int icon_id, struct Icon* icon) return; } - BLI_ghash_insert(gIcons, (void *)icon_id, icon); + BLI_ghash_insert(gIcons, SET_INT_IN_POINTER(icon_id), icon); } void BKE_icon_delete(struct ID* id) @@ -297,6 +300,6 @@ void BKE_icon_delete(struct ID* id) if (!id->icon_id) return; /* no icon defined for library object */ - BLI_ghash_remove(gIcons, (void*)id->icon_id, 0, icon_free); + BLI_ghash_remove(gIcons, SET_INT_IN_POINTER(id->icon_id), 0, icon_free); id->icon_id = 0; } diff --git a/source/blender/blenkernel/intern/idprop.c b/source/blender/blenkernel/intern/idprop.c index eb633e92cc0..542a1dff651 100644 --- a/source/blender/blenkernel/intern/idprop.c +++ b/source/blender/blenkernel/intern/idprop.c @@ -54,7 +54,8 @@ static char idp_size_table[] = { sizeof(float)*16, /*Matrix type, deprecated*/ 0, /*arrays don't have a fixed size*/ sizeof(ListBase), /*Group type*/ - sizeof(void*) + sizeof(void*), + sizeof(double) }; @@ -110,6 +111,7 @@ void IDP_ResizeArray(IDProperty *prop, int newlen) newp->type = prop->type; newp->flag = prop->flag; newp->data.val = prop->data.val; + newp->data.val2 = prop->data.val2; return newp; } @@ -209,7 +211,8 @@ void IDP_UnlinkID(IDProperty *prop) IDProperty *IDP_CopyGroup(IDProperty *prop) { IDProperty *newp = idp_generic_copy(prop), *link; - + newp->len = prop->len; + for (link=prop->data.group.first; link; link=link->next) { BLI_addtail(&newp->data.group, IDP_CopyProperty(link)); } @@ -217,6 +220,10 @@ IDProperty *IDP_CopyGroup(IDProperty *prop) return newp; } +/* + replaces a property with the same name in a group, or adds + it if the propery doesn't exist. +*/ void IDP_ReplaceInGroup(IDProperty *group, IDProperty *prop) { IDProperty *loop; @@ -224,10 +231,10 @@ void IDP_ReplaceInGroup(IDProperty *group, IDProperty *prop) if (BSTR_EQ(loop->name, prop->name)) { if (loop->next) BLI_insertlinkbefore(&group->data.group, loop->next, prop); else BLI_addtail(&group->data.group, prop); + BLI_remlink(&group->data.group, loop); IDP_FreeProperty(loop); - MEM_freeN(loop); - group->len++; + MEM_freeN(loop); return; } } @@ -259,7 +266,7 @@ int IDP_InsertToGroup(IDProperty *group, IDProperty *previous, IDProperty *pnew) } group->len++; - + BLI_insertlink(&group->data.group, previous, pnew); return 1; } @@ -314,7 +321,7 @@ void IDP_FreeIterBeforeEnd(void *vself) This is because all ID Property freeing functions free only direct data (not the ID Property struct itself), but for Groups the child properties *are* considered direct data.*/ -void IDP_FreeGroup(IDProperty *prop) +static void IDP_FreeGroup(IDProperty *prop) { IDProperty *loop, *next; for (loop=prop->data.group.first; loop; loop=next) @@ -345,6 +352,10 @@ IDProperty *IDP_GetProperties(ID *id, int create_if_needed) if (create_if_needed) { id->properties = MEM_callocN(sizeof(IDProperty), "IDProperty"); id->properties->type = IDP_GROUP; + /* dont overwite the data's name and type + * some functions might need this if they + * dont have a real ID, should be named elsewhere - Campbell */ + /* strcpy(id->name, "top_level_group");*/ } return id->properties; } @@ -363,10 +374,14 @@ IDProperty *IDP_New(int type, IDPropertyTemplate val, char *name) prop = MEM_callocN(sizeof(IDProperty), "IDProperty float"); *(float*)&prop->data.val = val.f; break; + case IDP_DOUBLE: + prop = MEM_callocN(sizeof(IDProperty), "IDProperty float"); + *(double*)&prop->data.val = val.d; + break; case IDP_ARRAY: { - /*for now, we only support float and int arrays*/ - if (val.array.type == IDP_FLOAT || val.array.type == IDP_INT) { + /*for now, we only support float and int and double arrays*/ + if (val.array.type == IDP_FLOAT || val.array.type == IDP_INT || val.array.type == IDP_DOUBLE) { prop = MEM_callocN(sizeof(IDProperty), "IDProperty array"); prop->len = prop->totallen = val.array.len; prop->subtype = val.array.type; @@ -409,10 +424,14 @@ IDProperty *IDP_New(int type, IDPropertyTemplate val, char *name) prop->type = type; strncpy(prop->name, name, MAX_IDPROP_NAME); + + /*security null byte*/ + prop->name[MAX_IDPROP_NAME-1] = 0; + return prop; } -/*NOTE: this will free all child properties of list arrays and groups! +/*NOTE: this will free all child properties including list arrays and groups! Also, note that this does NOT unlink anything! Plus it doesn't free the actual IDProperty struct either.*/ void IDP_FreeProperty(IDProperty *prop) @@ -430,7 +449,8 @@ void IDP_FreeProperty(IDProperty *prop) } } -/*Unlinks any IDProperty<->ID linkage that might be going on.*/ +/*Unlinks any IDProperty<->ID linkage that might be going on. + note: currently unused.*/ void IDP_UnlinkProperty(IDProperty *prop) { switch (prop->type) { diff --git a/source/blender/blenkernel/intern/image.c b/source/blender/blenkernel/intern/image.c index a79433fcfa1..de1747e514c 100644 --- a/source/blender/blenkernel/intern/image.c +++ b/source/blender/blenkernel/intern/image.c @@ -53,6 +53,7 @@ #include "DNA_packedFile_types.h" #include "DNA_scene_types.h" #include "DNA_camera_types.h" +#include "DNA_sequence_types.h" #include "DNA_texture_types.h" #include "DNA_sequence_types.h" #include "DNA_userdef_types.h" @@ -81,6 +82,11 @@ /* for stamp drawing to an image */ #include "BMF_Api.h" +#include "GPU_extensions.h" +#include "GPU_draw.h" + +#include "BLO_sys_types.h" // for intptr_t support + /* max int, to indicate we don't store sequences in ibuf */ #define IMA_NO_INDEX 0x7FEFEFEF @@ -93,32 +99,50 @@ /* used by sequencer and image premul option - IMA_DO_PREMUL */ void converttopremul(struct ImBuf *ibuf) { - int x, y, val; - char *cp; + int x, y; if(ibuf==0) return; - if(ibuf->depth==24) { /* put alpha at 255 */ - - cp= (char *)(ibuf->rect); - for(y=0; y<ibuf->y; y++) { - for(x=0; x<ibuf->x; x++, cp+=4) { - cp[3]= 255; + if (ibuf->rect) { + int val; + char *cp; + if(ibuf->depth==24) { /* put alpha at 255 */ + cp= (char *)(ibuf->rect); + for(y=0; y<ibuf->y; y++) { + for(x=0; x<ibuf->x; x++, cp+=4) { + cp[3]= 255; + } + } + } else { + cp= (char *)(ibuf->rect); + for(y=0; y<ibuf->y; y++) { + for(x=0; x<ibuf->x; x++, cp+=4) { + val= cp[3]; + cp[0]= (cp[0]*val)>>8; + cp[1]= (cp[1]*val)>>8; + cp[2]= (cp[2]*val)>>8; + } } } - return; } - - cp= (char *)(ibuf->rect); - for(y=0; y<ibuf->y; y++) { - for(x=0; x<ibuf->x; x++, cp+=4) { - if(cp[3]==0) { - cp[0]= cp[1]= cp[2]= 0; + if (ibuf->rect_float) { + float val; + float *cp; + if(ibuf->depth==24) { /* put alpha at 1.0 */ + cp= ibuf->rect_float;; + for(y=0; y<ibuf->y; y++) { + for(x=0; x<ibuf->x; x++, cp+=4) { + cp[3]= 1.0; + } } - else if(cp[3]!=255) { - val= cp[3]; - cp[0]= (cp[0]*val)>>8; - cp[1]= (cp[1]*val)>>8; - cp[2]= (cp[2]*val)>>8; + } else { + cp= ibuf->rect_float; + for(y=0; y<ibuf->y; y++) { + for(x=0; x<ibuf->x; x++, cp+=4) { + val= cp[3]; + cp[0]= cp[0]*val; + cp[1]= cp[1]*val; + cp[2]= cp[2]*val; + } } } } @@ -215,7 +239,7 @@ static void image_free_buffers(Image *ima) ima->rr= NULL; } - //XXX free_realtime_image(ima); + GPU_free_image(ima); ima->ok= IMA_OK; } @@ -223,7 +247,6 @@ static void image_free_buffers(Image *ima) /* called by library too, do not free ima itself */ void free_image(Image *ima) { - image_free_buffers(ima); if (ima->packedfile) { freePackedFile(ima->packedfile); @@ -234,7 +257,6 @@ void free_image(Image *ima) if (ima->preview) { BKE_previewimg_free(&ima->preview); } - } /* only image block itself */ @@ -260,15 +282,19 @@ static Image *image_alloc(const char *name, short source, short type) /* get the ibuf from an image cache, local use here only */ static ImBuf *image_get_ibuf(Image *ima, int index, int frame) { + /* this function is intended to be thread safe. with IMA_NO_INDEX this + * should be OK, but when iterating over the list this is more tricky + * */ if(index==IMA_NO_INDEX) return ima->ibufs.first; else { ImBuf *ibuf; - + index= IMA_MAKE_INDEX(frame, index); for(ibuf= ima->ibufs.first; ibuf; ibuf= ibuf->next) if(ibuf->index==index) return ibuf; + return NULL; } } @@ -296,19 +322,16 @@ static void image_assign_ibuf(Image *ima, ImBuf *ibuf, int index, int frame) for(link= ima->ibufs.first; link; link= link->next) if(link->index>=index) break; - /* now we don't want copies? */ - if(link && ibuf->index==link->index) { - ImBuf *prev= ibuf->prev; - image_remove_ibuf(ima, link); - link= prev; - } - + + ibuf->index= index; + /* this function accepts link==NULL */ BLI_insertlinkbefore(&ima->ibufs, link, ibuf); - - ibuf->index= index; + + /* now we don't want copies? */ + if(link && ibuf->index==link->index) + image_remove_ibuf(ima, link); } - } /* checks if image was already loaded, then returns same image */ @@ -329,7 +352,8 @@ Image *BKE_add_image_file(const char *name) } BLI_strncpy(str, name, sizeof(str)); - BLI_convertstringcode(str, G.sce, G.scene->r.cfra); + BLI_convertstringcode(str, G.sce); + BLI_convertstringframe(str, G.scene->r.cfra); /* TODO - should this realy be here? */ /* exists? */ file= open(str, O_BINARY|O_RDONLY); @@ -340,7 +364,8 @@ Image *BKE_add_image_file(const char *name) for(ima= G.main->image.first; ima; ima= ima->id.next) { if(ima->source!=IMA_SRC_VIEWER && ima->source!=IMA_SRC_GENERATED) { BLI_strncpy(strtest, ima->name, sizeof(ima->name)); - BLI_convertstringcode(strtest, G.sce, G.scene->r.cfra); + BLI_convertstringcode(strtest, G.sce); + BLI_convertstringframe(strtest, G.scene->r.cfra); /* TODO - should this be here? */ if( strcmp(strtest, str)==0 ) { if(ima->anim==NULL || ima->id.us==0) { @@ -373,20 +398,27 @@ Image *BKE_add_image_file(const char *name) return ima; } -static ImBuf *add_ibuf_size(int width, int height, char *name, short uvtestgrid, float color[4]) +static ImBuf *add_ibuf_size(int width, int height, char *name, int floatbuf, short uvtestgrid, float color[4]) { ImBuf *ibuf; float h=0.0, hoffs=0.0, hue=0.0, s=0.9, v=0.9, r, g, b; - unsigned char *rect; + unsigned char *rect= NULL; + float *rect_float= NULL; int x, y; int checkerwidth=21, dark=1; - ibuf= IMB_allocImBuf(width, height, 24, IB_rect, 0); + if (floatbuf) { + ibuf= IMB_allocImBuf(width, height, 24, IB_rectfloat, 0); + rect_float= (float*)ibuf->rect_float; + } + else { + ibuf= IMB_allocImBuf(width, height, 24, IB_rect, 0); + rect= (unsigned char*)ibuf->rect; + } + strcpy(ibuf->name, "Untitled"); ibuf->userflags |= IB_BITMAPDIRTY; - rect= (unsigned char*)ibuf->rect; - if (uvtestgrid) { /* these two passes could be combined into one, but it's more readable and * easy to tweak like this, speed isn't really that much of an issue in this situation... */ @@ -395,26 +427,40 @@ static ImBuf *add_ibuf_size(int width, int height, char *name, short uvtestgrid, for(y=0; y<ibuf->y; y++) { dark = pow(-1, floor(y / checkerwidth)); - for(x=0; x<ibuf->x; x++, rect+=4) { + for(x=0; x<ibuf->x; x++) { if (x % checkerwidth == 0) dark *= -1; - if (dark > 0) { - rect[0] = rect[1] = rect[2] = 64; - rect[3] = 255; - } else { - rect[0] = rect[1] = rect[2] = 150; - rect[3] = 255; + if (floatbuf) { + if (dark > 0) { + rect_float[0] = rect_float[1] = rect_float[2] = 0.25; + rect_float[3] = 1.0; + } else { + rect_float[0] = rect_float[1] = rect_float[2] = 0.58; + rect_float[3] = 1.0; + } + rect_float+=4; + } + else { + if (dark > 0) { + rect[0] = rect[1] = rect[2] = 64; + rect[3] = 255; + } else { + rect[0] = rect[1] = rect[2] = 150; + rect[3] = 255; + } + rect += 4; } } } /* 2nd pass, colored + */ - rect= (unsigned char*)ibuf->rect; + if (floatbuf) rect_float= (float*)ibuf->rect_float; + else rect= (unsigned char*)ibuf->rect; for(y=0; y<ibuf->y; y++) { hoffs = 0.125 * floor(y / checkerwidth); - for(x=0; x<ibuf->x; x++, rect+=4) { + for(x=0; x<ibuf->x; x++) { h = 0.125 * floor(x / checkerwidth); if ((fabs((x % checkerwidth) - (checkerwidth / 2)) < 4) && @@ -426,22 +472,44 @@ static ImBuf *add_ibuf_size(int width, int height, char *name, short uvtestgrid, hue = fmod(fabs(h-hoffs), 1.0); hsv_to_rgb(hue, s, v, &r, &g, &b); - rect[0]= (char)(r * 255.0); - rect[1]= (char)(g * 255.0); - rect[2]= (char)(b * 255.0); - rect[3]= 255; + if (floatbuf) { + rect_float[0]= r; + rect_float[1]= g; + rect_float[2]= b; + rect_float[3]= 1.0; + } + else { + rect[0]= (char)(r * 255.0); + rect[1]= (char)(g * 255.0); + rect[2]= (char)(b * 255.0); + rect[3]= 255; + } } } - + + if (floatbuf) + rect_float+=4; + else + rect+=4; } } } else { /* blank image */ for(y=0; y<ibuf->y; y++) { - for(x=0; x<ibuf->x; x++, rect+=4) { - rect[0]= (char)(color[0] * 255.0); - rect[1]= (char)(color[1] * 255.0); - rect[2]= (char)(color[2] * 255.0); - rect[3]= (char)(color[3] * 255.0); + for(x=0; x<ibuf->x; x++) { + if (floatbuf) { + rect_float[0]= color[0]; + rect_float[1]= color[1]; + rect_float[2]= color[2]; + rect_float[3]= color[3]; + rect_float+=4; + } + else { + rect[0]= (char)(color[0] * 255.0); + rect[1]= (char)(color[1] * 255.0); + rect[2]= (char)(color[2] * 255.0); + rect[3]= (char)(color[3] * 255.0); + rect+=4; + } } } } @@ -449,7 +517,7 @@ static ImBuf *add_ibuf_size(int width, int height, char *name, short uvtestgrid, } /* adds new image block, creates ImBuf and initializes color */ -Image *BKE_add_image_size(int width, int height, char *name, short uvtestgrid, float color[4]) +Image *BKE_add_image_size(int width, int height, char *name, int floatbuf, short uvtestgrid, float color[4]) { Image *ima; @@ -464,7 +532,7 @@ Image *BKE_add_image_size(int width, int height, char *name, short uvtestgrid, f ima->gen_y= height; ima->gen_type= uvtestgrid; - ibuf= add_ibuf_size(width, height, name, uvtestgrid, color); + ibuf= add_ibuf_size(width, height, name, floatbuf, uvtestgrid, color); image_assign_ibuf(ima, ibuf, IMA_NO_INDEX, 0); ima->ok= IMA_OK_LOADED; @@ -515,7 +583,7 @@ void tag_image_time(Image *ima) ima->lastused = (int)PIL_check_seconds_timer(); } -void tag_all_images_time() +static void tag_all_images_time() { Image *ima; int ctime = (int)PIL_check_seconds_timer(); @@ -551,7 +619,7 @@ void free_old_images() This gives textures a "second chance" to be used before dying. */ if(ima->bindcode || ima->repbind) { - //XXX free_realtime_image(ima); + GPU_free_image(ima); ima->lastused = ctime; } /* Otherwise, just kill the buffers */ @@ -563,6 +631,47 @@ void free_old_images() } } +static uintptr_t image_mem_size(Image *ima) +{ + ImBuf *ibuf, *ibufm; + int level; + uintptr_t size = 0; + + size= 0; + for(ibuf= ima->ibufs.first; ibuf; ibuf= ibuf->next) { + if(ibuf->rect) size += MEM_allocN_len(ibuf->rect); + else if(ibuf->rect_float) size += MEM_allocN_len(ibuf->rect_float); + + for(level=0; level<IB_MIPMAP_LEVELS; level++) { + ibufm= ibuf->mipmap[level]; + if(ibufm) { + if(ibufm->rect) size += MEM_allocN_len(ibufm->rect); + else if(ibufm->rect_float) size += MEM_allocN_len(ibufm->rect_float); + } + } + } + + return size; +} + +void BKE_image_print_memlist(void) +{ + Image *ima; + uintptr_t size, totsize= 0; + + for(ima= G.main->image.first; ima; ima= ima->id.next) + totsize += image_mem_size(ima); + + printf("\ntotal image memory len: %.3lf MB\n", (double)totsize/(double)(1024*1024)); + + for(ima= G.main->image.first; ima; ima= ima->id.next) { + size= image_mem_size(ima); + + if(size) + printf("%s len: %.3f MB\n", ima->id.name+2, (double)size/(double)(1024*1024)); + } +} + void BKE_image_free_all_textures(void) { Tex *tex; @@ -740,17 +849,13 @@ void BKE_add_image_extension(char *string, int imtype) if(!BLI_testextensie(string, ".tga")) extension= ".tga"; } - else if(ELEM5(imtype, R_MOVIE, R_AVICODEC, R_AVIRAW, R_AVIJPEG, R_JPEG90)) { - if(!( BLI_testextensie(string, ".jpg") || BLI_testextensie(string, ".jpeg"))) - extension= ".jpg"; - } else if(imtype==R_BMP) { if(!BLI_testextensie(string, ".bmp")) extension= ".bmp"; } else if(G.have_libtiff && (imtype==R_TIFF)) { - if(!BLI_testextensie(string, ".tif")) - extension= ".tif"; + if(!BLI_testextensie(string, ".tif") && + !BLI_testextensie(string, ".tiff")) extension= ".tif"; } #ifdef WITH_OPENEXR else if( ELEM(imtype, R_OPENEXR, R_MULTILAYER)) { @@ -766,10 +871,14 @@ void BKE_add_image_extension(char *string, int imtype) if (!BLI_testextensie(string, ".dpx")) extension= ".dpx"; } - else { /* targa default */ + else if(imtype==R_TARGA) { if(!BLI_testextensie(string, ".tga")) extension= ".tga"; } + else { // R_MOVIE, R_AVICODEC, R_AVIRAW, R_AVIJPEG, R_JPEG90, R_QUICKTIME etc + if(!( BLI_testextensie(string, ".jpg") || BLI_testextensie(string, ".jpeg"))) + extension= ".jpg"; + } strcat(string, extension); } @@ -799,8 +908,13 @@ static void stampdata(StampData *stamp_data, int do_prefix) #endif /* WIN32 */ if (G.scene->r.stamp & R_STAMP_FILENAME) { - if (do_prefix) sprintf(stamp_data->file, "File %s", G.sce); - else sprintf(stamp_data->file, "%s", G.sce); + if (G.relbase_valid) { + if (do_prefix) sprintf(stamp_data->file, "File %s", G.sce); + else sprintf(stamp_data->file, "%s", G.sce); + } else { + if (do_prefix) strcpy(stamp_data->file, "File <untitled>"); + else strcpy(stamp_data->file, "<untitled>"); + } stamp_data->note[0] = '\0'; } else { stamp_data->file[0] = '\0'; @@ -877,8 +991,11 @@ static void stampdata(StampData *stamp_data, int do_prefix) } if (G.scene->r.stamp & R_STAMP_CAMERA) { - if (do_prefix) sprintf(stamp_data->camera, "Camera %s", ((Camera *) G.scene->camera)->id.name+2); - else sprintf(stamp_data->camera, "%s", ((Camera *) G.scene->camera)->id.name+2); + if (G.scene->camera) strcpy(text, ((Camera *) G.scene->camera)->id.name+2); + else strcpy(text, "<none>"); + + if (do_prefix) sprintf(stamp_data->camera, "Camera %s", text); + else sprintf(stamp_data->camera, "%s", text); } else { stamp_data->camera[0] = '\0'; } @@ -903,7 +1020,7 @@ static void stampdata(StampData *stamp_data, int do_prefix) } } -void BKE_stamp_buf(unsigned char *rect, float *rectf, int width, int height) +void BKE_stamp_buf(unsigned char *rect, float *rectf, int width, int height, int channels) { struct StampData stamp_data; @@ -951,7 +1068,7 @@ void BKE_stamp_buf(unsigned char *rect, float *rectf, int width, int height) /* Top left corner */ text_width = BMF_GetStringWidth(font, stamp_data.file); buf_rectfill_area(rect, rectf, width, height, G.scene->r.bg_stamp, x-1, y-1, x+text_width+text_pad+1, y+font_height+1); - BMF_DrawStringBuf(font, stamp_data.file, x+(text_pad/2), y, G.scene->r.fg_stamp, rect, rectf, width, height); + BMF_DrawStringBuf(font, stamp_data.file, x+(text_pad/2), y, G.scene->r.fg_stamp, rect, rectf, width, height, channels); y -= font_height+2; /* Top and bottom 1 pix padding each */ } @@ -959,7 +1076,7 @@ void BKE_stamp_buf(unsigned char *rect, float *rectf, int width, int height) if (stamp_data.note[0]) { text_width = BMF_GetStringWidth(font, stamp_data.note); buf_rectfill_area(rect, rectf, width, height, G.scene->r.bg_stamp, x-1, y-1, x+text_width+text_pad+1, y+font_height+1); - BMF_DrawStringBuf(font, stamp_data.note, x+(text_pad/2), y, G.scene->r.fg_stamp, rect, rectf, width, height); + BMF_DrawStringBuf(font, stamp_data.note, x+(text_pad/2), y, G.scene->r.fg_stamp, rect, rectf, width, height, channels); y -= font_height+2; /* Top and bottom 1 pix padding each */ } @@ -967,7 +1084,7 @@ void BKE_stamp_buf(unsigned char *rect, float *rectf, int width, int height) if (stamp_data.date[0]) { text_width = BMF_GetStringWidth(font, stamp_data.date); buf_rectfill_area(rect, rectf, width, height, G.scene->r.bg_stamp, x-1, y-1, x+text_width+text_pad+1, y+font_height+1); - BMF_DrawStringBuf(font, stamp_data.date, x+(text_pad/2), y, G.scene->r.fg_stamp, rect, rectf, width, height); + BMF_DrawStringBuf(font, stamp_data.date, x+(text_pad/2), y, G.scene->r.fg_stamp, rect, rectf, width, height, channels); } /* Bottom left corner, leaving space for timing */ @@ -976,7 +1093,7 @@ void BKE_stamp_buf(unsigned char *rect, float *rectf, int width, int height) y = font_height+2+1; /* 2 for padding in TIME|FRAME fields below and 1 for padding in this one */ text_width = BMF_GetStringWidth(font, stamp_data.marker); buf_rectfill_area(rect, rectf, width, height, G.scene->r.bg_stamp, x-1, y-1, x+text_width+text_pad+1, y+font_height+1); - BMF_DrawStringBuf(font, stamp_data.marker, x+(text_pad/2), y, G.scene->r.fg_stamp, rect, rectf, width, height); + BMF_DrawStringBuf(font, stamp_data.marker, x+(text_pad/2), y, G.scene->r.fg_stamp, rect, rectf, width, height, channels); } /* Left bottom corner */ @@ -985,7 +1102,7 @@ void BKE_stamp_buf(unsigned char *rect, float *rectf, int width, int height) y = 1; text_width = BMF_GetStringWidth(font, stamp_data.time); buf_rectfill_area(rect, rectf, width, height, G.scene->r.bg_stamp, x-1, y-1, x+text_width+text_pad+1, y+font_height+1); - BMF_DrawStringBuf(font, stamp_data.time, x+(text_pad/2), y, G.scene->r.fg_stamp, rect, rectf, width, height); + BMF_DrawStringBuf(font, stamp_data.time, x+(text_pad/2), y, G.scene->r.fg_stamp, rect, rectf, width, height, channels); x += text_width+text_pad+2; /* Both sides have 1 pix additional padding each */ } @@ -995,7 +1112,7 @@ void BKE_stamp_buf(unsigned char *rect, float *rectf, int width, int height) if (!stamp_data.time[0]) x = 1; y = 1; buf_rectfill_area(rect, rectf, width, height, G.scene->r.bg_stamp, x-1, y-1, x+text_width+text_pad+1, y+font_height+1); - BMF_DrawStringBuf(font, stamp_data.frame, x+(text_pad/2), y, G.scene->r.fg_stamp, rect, rectf, width, height); + BMF_DrawStringBuf(font, stamp_data.frame, x+(text_pad/2), y, G.scene->r.fg_stamp, rect, rectf, width, height, channels); } if (stamp_data.camera[0]) { @@ -1004,7 +1121,7 @@ void BKE_stamp_buf(unsigned char *rect, float *rectf, int width, int height) x = (width/2) - (BMF_GetStringWidth(font, stamp_data.camera)/2); y = 1; buf_rectfill_area(rect, rectf, width, height, G.scene->r.bg_stamp, x-1, y-1, x+text_width+text_pad+1, y+font_height+1); - BMF_DrawStringBuf(font, stamp_data.camera, x+(text_pad/2), y, G.scene->r.fg_stamp, rect, rectf, width, height); + BMF_DrawStringBuf(font, stamp_data.camera, x+(text_pad/2), y, G.scene->r.fg_stamp, rect, rectf, width, height, channels); } if (stamp_data.scene[0]) { @@ -1013,7 +1130,7 @@ void BKE_stamp_buf(unsigned char *rect, float *rectf, int width, int height) x = width - (text_width+1+text_pad); y = 1; buf_rectfill_area(rect, rectf, width, height, G.scene->r.bg_stamp, x-1, y-1, x+text_width+text_pad+1, y+font_height+1); - BMF_DrawStringBuf(font, stamp_data.scene, x+(text_pad/2), y, G.scene->r.fg_stamp, rect, rectf, width, height); + BMF_DrawStringBuf(font, stamp_data.scene, x+(text_pad/2), y, G.scene->r.fg_stamp, rect, rectf, width, height, channels); } if (stamp_data.strip[0]) { @@ -1022,7 +1139,7 @@ void BKE_stamp_buf(unsigned char *rect, float *rectf, int width, int height) x = width - (text_width+1+text_pad); y = height - font_height - 1; buf_rectfill_area(rect, rectf, width, height, G.scene->r.bg_stamp, x-1, y-1, x+text_width+text_pad+1, y+font_height+1); - BMF_DrawStringBuf(font, stamp_data.strip, x+(text_pad/2), y, G.scene->r.fg_stamp, rect, rectf, width, height); + BMF_DrawStringBuf(font, stamp_data.strip, x+(text_pad/2), y, G.scene->r.fg_stamp, rect, rectf, width, height, channels); } } @@ -1070,6 +1187,9 @@ int BKE_write_ibuf(ImBuf *ibuf, char *name, int imtype, int subimtype, int quali } else if ((G.have_libtiff) && (imtype==R_TIFF)) { ibuf->ftype= TIF; + + if(subimtype & R_TIFF_16BIT) + ibuf->ftype |= TIF_16BIT; } #ifdef WITH_OPENEXR else if (imtype==R_OPENEXR || imtype==R_MULTILAYER) { @@ -1121,23 +1241,16 @@ int BKE_write_ibuf(ImBuf *ibuf, char *name, int imtype, int subimtype, int quali void BKE_makepicstring(char *string, char *base, int frame, int imtype) { - short i, len, digits= 4; /* digits in G.scene? */ - char num[10]; - if (string==NULL) return; BLI_strncpy(string, base, FILE_MAX - 10); /* weak assumption */ - BLI_convertstringcode(string, G.sce, frame); - - len= strlen(string); - - i= digits - sprintf(num, "%d", frame); - for(; i>0; i--){ - string[len]= '0'; - len++; - } - string[len]= 0; - strcat(string, num); + + /* if we dont have any #'s to insert numbers into, use 4 numbers by default */ + if (strchr(string, '#')==NULL) + strcat(string, "####"); /* 4 numbers */ + + BLI_convertstringcode(string, G.sce); + BLI_convertstringframe(string, frame); if(G.scene->r.scemode & R_EXTENSION) BKE_add_image_extension(string, imtype); @@ -1373,9 +1486,11 @@ static ImBuf *image_load_sequence_file(Image *ima, ImageUser *iuser, int frame) BLI_strncpy(name, ima->name, sizeof(name)); if(ima->id.lib) - BLI_convertstringcode(name, ima->id.lib->filename, frame); + BLI_convertstringcode(name, ima->id.lib->filename); else - BLI_convertstringcode(name, G.sce, frame); + BLI_convertstringcode(name, G.sce); + + BLI_convertstringframe(name, frame); /* TODO - should this be here? */ /* read ibuf */ ibuf = IMB_loadiffname(name, IB_rect|IB_multilayer); @@ -1391,13 +1506,17 @@ static ImBuf *image_load_sequence_file(Image *ima, ImageUser *iuser, int frame) ibuf= NULL; } else { - image_assign_ibuf(ima, ibuf, 0, frame); image_initialize_after_load(ima, ibuf); + image_assign_ibuf(ima, ibuf, 0, frame); } #else - image_assign_ibuf(ima, ibuf, 0, frame); image_initialize_after_load(ima, ibuf); + image_assign_ibuf(ima, ibuf, 0, frame); #endif + + if(ima->flag & IMA_DO_PREMUL) + converttopremul(ibuf); + } else ima->ok= 0; @@ -1432,8 +1551,9 @@ static ImBuf *image_load_sequence_multilayer(Image *ima, ImageUser *iuser, int f // if(oldrr) printf("freed previous result %p\n", oldrr); if(oldrr) RE_FreeRenderResult(oldrr); } - else + else { ima->rr= oldrr; + } } if(ima->rr) { @@ -1448,8 +1568,8 @@ static ImBuf *image_load_sequence_multilayer(Image *ima, ImageUser *iuser, int f ibuf->mall= IB_rectfloat; ibuf->channels= rpass->channels; - image_assign_ibuf(ima, ibuf, iuser->multi_index, frame); image_initialize_after_load(ima, ibuf); + image_assign_ibuf(ima, ibuf, iuser?iuser->multi_index:0, frame); } // else printf("pass not found\n"); @@ -1475,9 +1595,9 @@ static ImBuf *image_load_movie_file(Image *ima, ImageUser *iuser, int frame) BLI_strncpy(str, ima->name, FILE_MAX); if(ima->id.lib) - BLI_convertstringcode(str, ima->id.lib->filename, 0); + BLI_convertstringcode(str, ima->id.lib->filename); else - BLI_convertstringcode(str, G.sce, 0); + BLI_convertstringcode(str, G.sce); ima->anim = openanim(str, IB_cmap | IB_rect); @@ -1495,8 +1615,8 @@ static ImBuf *image_load_movie_file(Image *ima, ImageUser *iuser, int frame) ibuf = IMB_anim_absolute(ima->anim, fra); if(ibuf) { - image_assign_ibuf(ima, ibuf, 0, frame); image_initialize_after_load(ima, ibuf); + image_assign_ibuf(ima, ibuf, 0, frame); } else ima->ok= 0; @@ -1515,6 +1635,7 @@ static ImBuf *image_load_image_file(Image *ima, ImageUser *iuser, int cfra) { struct ImBuf *ibuf; char str[FILE_MAX]; + int assign = 0; /* always ensure clean ima */ image_free_buffers(ima); @@ -1528,9 +1649,11 @@ static ImBuf *image_load_image_file(Image *ima, ImageUser *iuser, int cfra) /* get the right string */ BLI_strncpy(str, ima->name, sizeof(str)); if(ima->id.lib) - BLI_convertstringcode(str, ima->id.lib->filename, cfra); + BLI_convertstringcode(str, ima->id.lib->filename); else - BLI_convertstringcode(str, G.sce, cfra); + BLI_convertstringcode(str, G.sce); + + BLI_convertstringframe(str, cfra); /* read ibuf */ ibuf = IMB_loadiffname(str, IB_rect|IB_multilayer|IB_imginfo); @@ -1545,8 +1668,8 @@ static ImBuf *image_load_image_file(Image *ima, ImageUser *iuser, int cfra) ibuf= NULL; } else { - image_assign_ibuf(ima, ibuf, IMA_NO_INDEX, 0); image_initialize_after_load(ima, ibuf); + assign= 1; /* check if the image is a font image... */ detectBitmapFont(ibuf); @@ -1562,6 +1685,9 @@ static ImBuf *image_load_image_file(Image *ima, ImageUser *iuser, int cfra) else ima->ok= 0; + if(assign) + image_assign_ibuf(ima, ibuf, IMA_NO_INDEX, 0); + if(iuser) iuser->ok= ima->ok; @@ -1585,12 +1711,13 @@ static ImBuf *image_get_ibuf_multilayer(Image *ima, ImageUser *iuser) if(rpass) { ibuf= IMB_allocImBuf(ima->rr->rectx, ima->rr->recty, 32, 0, 0); - image_assign_ibuf(ima, ibuf, iuser?iuser->multi_index:IMA_NO_INDEX, 0); image_initialize_after_load(ima, ibuf); ibuf->rect_float= rpass->rect; ibuf->flags |= IB_rectfloat; ibuf->channels= rpass->channels; + + image_assign_ibuf(ima, ibuf, iuser?iuser->multi_index:IMA_NO_INDEX, 0); } } @@ -1609,31 +1736,38 @@ static ImBuf *image_get_render_result(Image *ima, ImageUser *iuser) { RenderResult *rr= RE_GetResult(RE_GetRender(G.scene->id.name)); - if(rr && iuser) { + if(rr) { RenderResult rres; float *rectf; unsigned int *rect; - int channels= 4, layer= iuser->layer; + float dither; + int channels, layer, pass; + + channels= 4; + layer= (iuser)? iuser->layer: 0; + pass= (iuser)? iuser->pass: 0; /* this gives active layer, composite or seqence result */ RE_GetResultImage(RE_GetRender(G.scene->id.name), &rres); rect= (unsigned int *)rres.rect32; rectf= rres.rectf; - + dither= G.scene->r.dither_intensity; + /* get compo/seq result by default */ if(rr->rectf && layer==0); else if(rr->layers.first) { - RenderLayer *rl= BLI_findlink(&rr->layers, iuser->layer-(rr->rectf?1:0)); + RenderLayer *rl= BLI_findlink(&rr->layers, layer-(rr->rectf?1:0)); if(rl) { /* there's no combined pass, is in renderlayer itself */ - if(iuser->pass==0) { + if(pass==0) { rectf= rl->rectf; } else { - RenderPass *rpass= BLI_findlink(&rl->passes, iuser->pass-1); + RenderPass *rpass= BLI_findlink(&rl->passes, pass-1); if(rpass) { channels= rpass->channels; rectf= rpass->rect; + dither= 0.0f; /* don't dither passes */ } } } @@ -1658,6 +1792,9 @@ static ImBuf *image_get_render_result(Image *ima, ImageUser *iuser) ibuf->rect_float= rectf; ibuf->flags |= IB_rectfloat; ibuf->channels= channels; + ibuf->zbuf_float= rres.rectz; + ibuf->flags |= IB_zbuffloat; + ibuf->dither= dither; ima->ok= IMA_OK_LOADED; return ibuf; @@ -1667,118 +1804,171 @@ static ImBuf *image_get_render_result(Image *ima, ImageUser *iuser) return NULL; } -/* Checks optional ImageUser and verifies/creates ImBuf. */ -/* returns ibuf */ -ImBuf *BKE_image_get_ibuf(Image *ima, ImageUser *iuser) +static ImBuf *image_get_ibuf_threadsafe(Image *ima, ImageUser *iuser, int *frame_r, int *index_r) { - ImBuf *ibuf= NULL; - float color[] = {0, 0, 0, 1}; + ImBuf *ibuf = NULL; + int frame = 0, index = 0; - /* quick reject tests */ - if(ima==NULL) - return NULL; - if(iuser) { - if(iuser->ok==0) - return NULL; - } - else if(ima->ok==0) - return NULL; - - BLI_lock_thread(LOCK_IMAGE); - - /* handle image source and types */ + /* see if we already have an appropriate ibuf, with image source and type */ if(ima->source==IMA_SRC_MOVIE) { - /* source is from single file, use flipbook to store ibuf */ - int frame= iuser?iuser->framenr:ima->lastframe; - + frame= iuser?iuser->framenr:ima->lastframe; ibuf= image_get_ibuf(ima, 0, frame); - if(ibuf==NULL) - ibuf= image_load_movie_file(ima, iuser, frame); } else if(ima->source==IMA_SRC_SEQUENCE) { - if(ima->type==IMA_TYPE_IMAGE) { - /* regular files, ibufs in flipbook, allows saving */ - int frame= iuser?iuser->framenr:ima->lastframe; - + frame= iuser?iuser->framenr:ima->lastframe; ibuf= image_get_ibuf(ima, 0, frame); - if(ibuf==NULL) - ibuf= image_load_sequence_file(ima, iuser, frame); - else - BLI_strncpy(ima->name, ibuf->name, sizeof(ima->name)); } - /* no else; on load the ima type can change */ - if(ima->type==IMA_TYPE_MULTILAYER) { - /* only 1 layer/pass stored in imbufs, no exrhandle anim storage, no saving */ - int frame= iuser?iuser->framenr:ima->lastframe; - int index= iuser?iuser->multi_index:IMA_NO_INDEX; - + else if(ima->type==IMA_TYPE_MULTILAYER) { + frame= iuser?iuser->framenr:ima->lastframe; + index= iuser?iuser->multi_index:IMA_NO_INDEX; ibuf= image_get_ibuf(ima, index, frame); - if(G.rt) printf("seq multi fra %d id %d ibuf %p %s\n", frame, index, ibuf, ima->id.name); - if(ibuf==NULL) - ibuf= image_load_sequence_multilayer(ima, iuser, frame); - else - BLI_strncpy(ima->name, ibuf->name, sizeof(ima->name)); } - } else if(ima->source==IMA_SRC_FILE) { - - if(ima->type==IMA_TYPE_IMAGE) { + if(ima->type==IMA_TYPE_IMAGE) ibuf= image_get_ibuf(ima, IMA_NO_INDEX, 0); - if(ibuf==NULL) - ibuf= image_load_image_file(ima, iuser, G.scene->r.cfra); /* cfra only for '#', this global is OK */ - } - /* no else; on load the ima type can change */ - if(ima->type==IMA_TYPE_MULTILAYER) { - /* keeps render result, stores ibufs in listbase, allows saving */ + else if(ima->type==IMA_TYPE_MULTILAYER) ibuf= image_get_ibuf(ima, iuser?iuser->multi_index:IMA_NO_INDEX, 0); - if(ibuf==NULL) - ibuf= image_get_ibuf_multilayer(ima, iuser); - } - } else if(ima->source == IMA_SRC_GENERATED) { - /* generated is: ibuf is allocated dynamically */ ibuf= image_get_ibuf(ima, IMA_NO_INDEX, 0); - - if(ibuf==NULL) { - if(ima->type==IMA_TYPE_VERSE) { - /* todo */ - } - else { /* always fall back to IMA_TYPE_UV_TEST */ - /* UV testgrid or black or solid etc */ - if(ima->gen_x==0) ima->gen_x= 256; - if(ima->gen_y==0) ima->gen_y= 256; - ibuf= add_ibuf_size(ima->gen_x, ima->gen_y, ima->name, ima->gen_type, color); - image_assign_ibuf(ima, ibuf, IMA_NO_INDEX, 0); - ima->ok= IMA_OK_LOADED; - } - } } else if(ima->source == IMA_SRC_VIEWER) { if(ima->type==IMA_TYPE_R_RESULT) { - /* always verify entirely */ - ibuf= image_get_render_result(ima, iuser); + /* always verify entirely, not that this shouldn't happen + * during render anyway */ } else if(ima->type==IMA_TYPE_COMPOSITE) { - int frame= iuser?iuser->framenr:0; - - /* Composite Viewer, all handled in compositor */ + frame= iuser?iuser->framenr:0; ibuf= image_get_ibuf(ima, 0, frame); - if(ibuf==NULL) { - /* fake ibuf, will be filled in compositor */ - ibuf= IMB_allocImBuf(256, 256, 32, IB_rect, 0); - image_assign_ibuf(ima, ibuf, 0, frame); + } + } + + *frame_r = frame; + *index_r = index; + + return ibuf; +} + +/* Checks optional ImageUser and verifies/creates ImBuf. */ +/* returns ibuf */ +ImBuf *BKE_image_get_ibuf(Image *ima, ImageUser *iuser) +{ + ImBuf *ibuf= NULL; + float color[] = {0, 0, 0, 1}; + int frame= 0, index= 0; + + /* This function is intended to be thread-safe. It postpones the mutex lock + * until it needs to load the image, if the image is already there it + * should just get the pointer and return. The reason is that a lot of mutex + * locks appears to be very slow on certain multicore macs, causing a render + * with image textures to actually slow down as more threads are used. + * + * Note that all the image loading functions should also make sure they do + * things in a threadsafe way for image_get_ibuf_threadsafe to work correct. + * That means, the last two steps must be, 1) add the ibuf to the list and + * 2) set ima/iuser->ok to 0 to IMA_OK_LOADED */ + + /* quick reject tests */ + if(ima==NULL) + return NULL; + if(iuser) { + if(iuser->ok==0) + return NULL; + } + else if(ima->ok==0) + return NULL; + + /* try to get the ibuf without locking */ + ibuf= image_get_ibuf_threadsafe(ima, iuser, &frame, &index); + + if(ibuf == NULL) { + /* couldn't get ibuf and image is not ok, so let's lock and try to + * load the image */ + BLI_lock_thread(LOCK_IMAGE); + + /* need to check ok flag and loading ibuf again, because the situation + * might have changed in the meantime */ + if(iuser) { + if(iuser->ok==0) { + BLI_unlock_thread(LOCK_IMAGE); + return NULL; } } + else if(ima->ok==0) { + BLI_unlock_thread(LOCK_IMAGE); + return NULL; + } + + ibuf= image_get_ibuf_threadsafe(ima, iuser, &frame, &index); + + if(ibuf == NULL) { + /* we are sure we have to load the ibuf, using source and type */ + if(ima->source==IMA_SRC_MOVIE) { + /* source is from single file, use flipbook to store ibuf */ + ibuf= image_load_movie_file(ima, iuser, frame); + } + else if(ima->source==IMA_SRC_SEQUENCE) { + if(ima->type==IMA_TYPE_IMAGE) { + /* regular files, ibufs in flipbook, allows saving */ + ibuf= image_load_sequence_file(ima, iuser, frame); + } + /* no else; on load the ima type can change */ + if(ima->type==IMA_TYPE_MULTILAYER) { + /* only 1 layer/pass stored in imbufs, no exrhandle anim storage, no saving */ + ibuf= image_load_sequence_multilayer(ima, iuser, frame); + } + + if(ibuf) + BLI_strncpy(ima->name, ibuf->name, sizeof(ima->name)); + } + else if(ima->source==IMA_SRC_FILE) { + + if(ima->type==IMA_TYPE_IMAGE) + ibuf= image_load_image_file(ima, iuser, G.scene->r.cfra); /* cfra only for '#', this global is OK */ + /* no else; on load the ima type can change */ + if(ima->type==IMA_TYPE_MULTILAYER) + /* keeps render result, stores ibufs in listbase, allows saving */ + ibuf= image_get_ibuf_multilayer(ima, iuser); + + } + else if(ima->source == IMA_SRC_GENERATED) { + /* generated is: ibuf is allocated dynamically */ + if(ima->type==IMA_TYPE_VERSE) { + /* todo */ + } + else { /* always fall back to IMA_TYPE_UV_TEST */ + /* UV testgrid or black or solid etc */ + if(ima->gen_x==0) ima->gen_x= 256; + if(ima->gen_y==0) ima->gen_y= 256; + ibuf= add_ibuf_size(ima->gen_x, ima->gen_y, ima->name, 0, ima->gen_type, color); + image_assign_ibuf(ima, ibuf, IMA_NO_INDEX, 0); + ima->ok= IMA_OK_LOADED; + } + } + else if(ima->source == IMA_SRC_VIEWER) { + if(ima->type==IMA_TYPE_R_RESULT) { + /* always verify entirely */ + ibuf= image_get_render_result(ima, iuser); + } + else if(ima->type==IMA_TYPE_COMPOSITE) { + /* Composite Viewer, all handled in compositor */ + /* fake ibuf, will be filled in compositor */ + ibuf= IMB_allocImBuf(256, 256, 32, IB_rect, 0); + image_assign_ibuf(ima, ibuf, 0, frame); + } + } + } + + BLI_unlock_thread(LOCK_IMAGE); } + /* we assuming that if it is not rendering, it's also not multithreaded + * (a somewhat weak assumption) */ if(G.rendering==0) tag_image_time(ima); - BLI_unlock_thread(LOCK_IMAGE); - return ibuf; } diff --git a/source/blender/blenkernel/intern/implicit.c b/source/blender/blenkernel/intern/implicit.c new file mode 100644 index 00000000000..93e35a4db06 --- /dev/null +++ b/source/blender/blenkernel/intern/implicit.c @@ -0,0 +1,1709 @@ +/* implicit.c +* +* +* ***** BEGIN GPL LICENSE BLOCK ***** +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License +* as published by the Free Software Foundation; either version 2 +* of the License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +* +* The Original Code is Copyright (C) Blender Foundation +* All rights reserved. +* +* The Original Code is: all of this file. +* +* Contributor(s): none yet. +* +* ***** END GPL LICENSE BLOCK ***** +*/ + +#include "MEM_guardedalloc.h" + +#include "BKE_cloth.h" + +#include "DNA_cloth_types.h" +#include "DNA_scene_types.h" + +#include "BKE_effect.h" +#include "BKE_global.h" +#include "BKE_cloth.h" +#include "BKE_utildefines.h" + +#ifdef _WIN32 +#include <windows.h> +static LARGE_INTEGER _itstart, _itend; +static LARGE_INTEGER ifreq; +void itstart(void) +{ + static int first = 1; + if(first) { + QueryPerformanceFrequency(&ifreq); + first = 0; + } + QueryPerformanceCounter(&_itstart); +} +void itend(void) +{ + QueryPerformanceCounter(&_itend); +} +double itval() +{ + return ((double)_itend.QuadPart - + (double)_itstart.QuadPart)/((double)ifreq.QuadPart); +} +#else +#include <sys/time.h> +// intrinsics need better compile flag checking +// #include <xmmintrin.h> +// #include <pmmintrin.h> +// #include <pthread.h> + + static struct timeval _itstart, _itend; + static struct timezone itz; + void itstart(void) +{ + gettimeofday(&_itstart, &itz); +} +void itend(void) +{ + gettimeofday(&_itend,&itz); +} +double itval() +{ + double t1, t2; + t1 = (double)_itstart.tv_sec + (double)_itstart.tv_usec/(1000*1000); + t2 = (double)_itend.tv_sec + (double)_itend.tv_usec/(1000*1000); + return t2-t1; +} +#endif + +static float I[3][3] = {{1,0,0},{0,1,0},{0,0,1}}; +static float ZERO[3][3] = {{0,0,0}, {0,0,0}, {0,0,0}}; + +/* +#define C99 +#ifdef C99 +#defineDO_INLINE inline +#else +#defineDO_INLINE static +#endif +*/ +struct Cloth; + +////////////////////////////////////////// +/* fast vector / matrix library, enhancements are welcome :) -dg */ +///////////////////////////////////////// + +/* DEFINITIONS */ +typedef float lfVector[3]; +typedef struct fmatrix3x3 { + float m[3][3]; /* 3x3 matrix */ + unsigned int c,r; /* column and row number */ + int pinned; /* is this vertex allowed to move? */ + float n1,n2,n3; /* three normal vectors for collision constrains */ + unsigned int vcount; /* vertex count */ + unsigned int scount; /* spring count */ +} fmatrix3x3; + +/////////////////////////// +// float[3] vector +/////////////////////////// +/* simple vector code */ +/* STATUS: verified */ +DO_INLINE void mul_fvector_S(float to[3], float from[3], float scalar) +{ + to[0] = from[0] * scalar; + to[1] = from[1] * scalar; + to[2] = from[2] * scalar; +} +/* simple cross product */ +/* STATUS: verified */ +DO_INLINE void cross_fvector(float to[3], float vectorA[3], float vectorB[3]) +{ + to[0] = vectorA[1] * vectorB[2] - vectorA[2] * vectorB[1]; + to[1] = vectorA[2] * vectorB[0] - vectorA[0] * vectorB[2]; + to[2] = vectorA[0] * vectorB[1] - vectorA[1] * vectorB[0]; +} +/* simple v^T * v product ("outer product") */ +/* STATUS: HAS TO BE verified (*should* work) */ +DO_INLINE void mul_fvectorT_fvector(float to[3][3], float vectorA[3], float vectorB[3]) +{ + mul_fvector_S(to[0], vectorB, vectorA[0]); + mul_fvector_S(to[1], vectorB, vectorA[1]); + mul_fvector_S(to[2], vectorB, vectorA[2]); +} +/* simple v^T * v product with scalar ("outer product") */ +/* STATUS: HAS TO BE verified (*should* work) */ +DO_INLINE void mul_fvectorT_fvectorS(float to[3][3], float vectorA[3], float vectorB[3], float aS) +{ + mul_fvectorT_fvector(to, vectorA, vectorB); + + mul_fvector_S(to[0], to[0], aS); + mul_fvector_S(to[1], to[1], aS); + mul_fvector_S(to[2], to[2], aS); +} + + +/* printf vector[3] on console: for debug output */ +void print_fvector(float m3[3]) +{ + printf("%f\n%f\n%f\n\n",m3[0],m3[1],m3[2]); +} + +/////////////////////////// +// long float vector float (*)[3] +/////////////////////////// +/* print long vector on console: for debug output */ +DO_INLINE void print_lfvector(float (*fLongVector)[3], unsigned int verts) +{ + unsigned int i = 0; + for(i = 0; i < verts; i++) + { + print_fvector(fLongVector[i]); + } +} +/* create long vector */ +DO_INLINE lfVector *create_lfvector(unsigned int verts) +{ + // TODO: check if memory allocation was successfull */ + return (lfVector *)MEM_callocN (verts * sizeof(lfVector), "cloth_implicit_alloc_vector"); + // return (lfVector *)cloth_aligned_malloc(&MEMORY_BASE, verts * sizeof(lfVector)); +} +/* delete long vector */ +DO_INLINE void del_lfvector(float (*fLongVector)[3]) +{ + if (fLongVector != NULL) + { + MEM_freeN (fLongVector); + // cloth_aligned_free(&MEMORY_BASE, fLongVector); + } +} +/* copy long vector */ +DO_INLINE void cp_lfvector(float (*to)[3], float (*from)[3], unsigned int verts) +{ + memcpy(to, from, verts * sizeof(lfVector)); +} +/* init long vector with float[3] */ +DO_INLINE void init_lfvector(float (*fLongVector)[3], float vector[3], unsigned int verts) +{ + unsigned int i = 0; + for(i = 0; i < verts; i++) + { + VECCOPY(fLongVector[i], vector); + } +} +/* zero long vector with float[3] */ +DO_INLINE void zero_lfvector(float (*to)[3], unsigned int verts) +{ + memset(to, 0.0f, verts * sizeof(lfVector)); +} +/* multiply long vector with scalar*/ +DO_INLINE void mul_lfvectorS(float (*to)[3], float (*fLongVector)[3], float scalar, unsigned int verts) +{ + unsigned int i = 0; + + for(i = 0; i < verts; i++) + { + mul_fvector_S(to[i], fLongVector[i], scalar); + } +} +/* multiply long vector with scalar*/ +/* A -= B * float */ +DO_INLINE void submul_lfvectorS(float (*to)[3], float (*fLongVector)[3], float scalar, unsigned int verts) +{ + unsigned int i = 0; + for(i = 0; i < verts; i++) + { + VECSUBMUL(to[i], fLongVector[i], scalar); + } +} +/* dot product for big vector */ +DO_INLINE float dot_lfvector(float (*fLongVectorA)[3], float (*fLongVectorB)[3], unsigned int verts) +{ + long i = 0; + float temp = 0.0; +// schedule(guided, 2) +#pragma omp parallel for reduction(+: temp) + for(i = 0; i < (long)verts; i++) + { + temp += INPR(fLongVectorA[i], fLongVectorB[i]); + } + return temp; +} +/* A = B + C --> for big vector */ +DO_INLINE void add_lfvector_lfvector(float (*to)[3], float (*fLongVectorA)[3], float (*fLongVectorB)[3], unsigned int verts) +{ + unsigned int i = 0; + + for(i = 0; i < verts; i++) + { + VECADD(to[i], fLongVectorA[i], fLongVectorB[i]); + } + +} +/* A = B + C * float --> for big vector */ +DO_INLINE void add_lfvector_lfvectorS(float (*to)[3], float (*fLongVectorA)[3], float (*fLongVectorB)[3], float bS, unsigned int verts) +{ + unsigned int i = 0; + + for(i = 0; i < verts; i++) + { + VECADDS(to[i], fLongVectorA[i], fLongVectorB[i], bS); + + } +} +/* A = B * float + C * float --> for big vector */ +DO_INLINE void add_lfvectorS_lfvectorS(float (*to)[3], float (*fLongVectorA)[3], float aS, float (*fLongVectorB)[3], float bS, unsigned int verts) +{ + unsigned int i = 0; + + for(i = 0; i < verts; i++) + { + VECADDSS(to[i], fLongVectorA[i], aS, fLongVectorB[i], bS); + } +} +/* A = B - C * float --> for big vector */ +DO_INLINE void sub_lfvector_lfvectorS(float (*to)[3], float (*fLongVectorA)[3], float (*fLongVectorB)[3], float bS, unsigned int verts) +{ + unsigned int i = 0; + for(i = 0; i < verts; i++) + { + VECSUBS(to[i], fLongVectorA[i], fLongVectorB[i], bS); + } + +} +/* A = B - C --> for big vector */ +DO_INLINE void sub_lfvector_lfvector(float (*to)[3], float (*fLongVectorA)[3], float (*fLongVectorB)[3], unsigned int verts) +{ + unsigned int i = 0; + + for(i = 0; i < verts; i++) + { + VECSUB(to[i], fLongVectorA[i], fLongVectorB[i]); + } + +} +/////////////////////////// +// 3x3 matrix +/////////////////////////// +/* printf 3x3 matrix on console: for debug output */ +void print_fmatrix(float m3[3][3]) +{ + printf("%f\t%f\t%f\n",m3[0][0],m3[0][1],m3[0][2]); + printf("%f\t%f\t%f\n",m3[1][0],m3[1][1],m3[1][2]); + printf("%f\t%f\t%f\n\n",m3[2][0],m3[2][1],m3[2][2]); +} + +/* copy 3x3 matrix */ +DO_INLINE void cp_fmatrix(float to[3][3], float from[3][3]) +{ + // memcpy(to, from, sizeof (float) * 9); + VECCOPY(to[0], from[0]); + VECCOPY(to[1], from[1]); + VECCOPY(to[2], from[2]); +} + +/* copy 3x3 matrix */ +DO_INLINE void initdiag_fmatrixS(float to[3][3], float aS) +{ + cp_fmatrix(to, ZERO); + + to[0][0] = aS; + to[1][1] = aS; + to[2][2] = aS; +} + +/* calculate determinant of 3x3 matrix */ +DO_INLINE float det_fmatrix(float m[3][3]) +{ + return m[0][0]*m[1][1]*m[2][2] + m[1][0]*m[2][1]*m[0][2] + m[0][1]*m[1][2]*m[2][0] + -m[0][0]*m[1][2]*m[2][1] - m[0][1]*m[1][0]*m[2][2] - m[2][0]*m[1][1]*m[0][2]; +} + +DO_INLINE void inverse_fmatrix(float to[3][3], float from[3][3]) +{ + unsigned int i, j; + float d; + + if((d=det_fmatrix(from))==0) + { + printf("can't build inverse"); + exit(0); + } + for(i=0;i<3;i++) + { + for(j=0;j<3;j++) + { + int i1=(i+1)%3; + int i2=(i+2)%3; + int j1=(j+1)%3; + int j2=(j+2)%3; + // reverse indexs i&j to take transpose + to[j][i] = (from[i1][j1]*from[i2][j2]-from[i1][j2]*from[i2][j1])/d; + /* + if(i==j) + to[i][j] = 1.0f / from[i][j]; + else + to[i][j] = 0; + */ + } + } + +} + +/* 3x3 matrix multiplied by a scalar */ +/* STATUS: verified */ +DO_INLINE void mul_fmatrix_S(float matrix[3][3], float scalar) +{ + mul_fvector_S(matrix[0], matrix[0],scalar); + mul_fvector_S(matrix[1], matrix[1],scalar); + mul_fvector_S(matrix[2], matrix[2],scalar); +} + +/* a vector multiplied by a 3x3 matrix */ +/* STATUS: verified */ +DO_INLINE void mul_fvector_fmatrix(float *to, float *from, float matrix[3][3]) +{ + to[0] = matrix[0][0]*from[0] + matrix[1][0]*from[1] + matrix[2][0]*from[2]; + to[1] = matrix[0][1]*from[0] + matrix[1][1]*from[1] + matrix[2][1]*from[2]; + to[2] = matrix[0][2]*from[0] + matrix[1][2]*from[1] + matrix[2][2]*from[2]; +} + +/* 3x3 matrix multiplied by a vector */ +/* STATUS: verified */ +DO_INLINE void mul_fmatrix_fvector(float *to, float matrix[3][3], float *from) +{ + to[0] = INPR(matrix[0],from); + to[1] = INPR(matrix[1],from); + to[2] = INPR(matrix[2],from); +} +/* 3x3 matrix multiplied by a 3x3 matrix */ +/* STATUS: verified */ +DO_INLINE void mul_fmatrix_fmatrix(float to[3][3], float matrixA[3][3], float matrixB[3][3]) +{ + mul_fvector_fmatrix(to[0], matrixA[0],matrixB); + mul_fvector_fmatrix(to[1], matrixA[1],matrixB); + mul_fvector_fmatrix(to[2], matrixA[2],matrixB); +} +/* 3x3 matrix addition with 3x3 matrix */ +DO_INLINE void add_fmatrix_fmatrix(float to[3][3], float matrixA[3][3], float matrixB[3][3]) +{ + VECADD(to[0], matrixA[0], matrixB[0]); + VECADD(to[1], matrixA[1], matrixB[1]); + VECADD(to[2], matrixA[2], matrixB[2]); +} +/* 3x3 matrix add-addition with 3x3 matrix */ +DO_INLINE void addadd_fmatrix_fmatrix(float to[3][3], float matrixA[3][3], float matrixB[3][3]) +{ + VECADDADD(to[0], matrixA[0], matrixB[0]); + VECADDADD(to[1], matrixA[1], matrixB[1]); + VECADDADD(to[2], matrixA[2], matrixB[2]); +} +/* 3x3 matrix sub-addition with 3x3 matrix */ +DO_INLINE void addsub_fmatrixS_fmatrixS(float to[3][3], float matrixA[3][3], float aS, float matrixB[3][3], float bS) +{ + VECADDSUBSS(to[0], matrixA[0], aS, matrixB[0], bS); + VECADDSUBSS(to[1], matrixA[1], aS, matrixB[1], bS); + VECADDSUBSS(to[2], matrixA[2], aS, matrixB[2], bS); +} +/* A -= B + C (3x3 matrix sub-addition with 3x3 matrix) */ +DO_INLINE void subadd_fmatrix_fmatrix(float to[3][3], float matrixA[3][3], float matrixB[3][3]) +{ + VECSUBADD(to[0], matrixA[0], matrixB[0]); + VECSUBADD(to[1], matrixA[1], matrixB[1]); + VECSUBADD(to[2], matrixA[2], matrixB[2]); +} +/* A -= B*x + C*y (3x3 matrix sub-addition with 3x3 matrix) */ +DO_INLINE void subadd_fmatrixS_fmatrixS(float to[3][3], float matrixA[3][3], float aS, float matrixB[3][3], float bS) +{ + VECSUBADDSS(to[0], matrixA[0], aS, matrixB[0], bS); + VECSUBADDSS(to[1], matrixA[1], aS, matrixB[1], bS); + VECSUBADDSS(to[2], matrixA[2], aS, matrixB[2], bS); +} +/* A = B - C (3x3 matrix subtraction with 3x3 matrix) */ +DO_INLINE void sub_fmatrix_fmatrix(float to[3][3], float matrixA[3][3], float matrixB[3][3]) +{ + VECSUB(to[0], matrixA[0], matrixB[0]); + VECSUB(to[1], matrixA[1], matrixB[1]); + VECSUB(to[2], matrixA[2], matrixB[2]); +} +/* A += B - C (3x3 matrix add-subtraction with 3x3 matrix) */ +DO_INLINE void addsub_fmatrix_fmatrix(float to[3][3], float matrixA[3][3], float matrixB[3][3]) +{ + VECADDSUB(to[0], matrixA[0], matrixB[0]); + VECADDSUB(to[1], matrixA[1], matrixB[1]); + VECADDSUB(to[2], matrixA[2], matrixB[2]); +} +///////////////////////////////////////////////////////////////// +// special functions +///////////////////////////////////////////////////////////////// +/* a vector multiplied and added to/by a 3x3 matrix */ +DO_INLINE void muladd_fvector_fmatrix(float to[3], float from[3], float matrix[3][3]) +{ + to[0] += matrix[0][0]*from[0] + matrix[1][0]*from[1] + matrix[2][0]*from[2]; + to[1] += matrix[0][1]*from[0] + matrix[1][1]*from[1] + matrix[2][1]*from[2]; + to[2] += matrix[0][2]*from[0] + matrix[1][2]*from[1] + matrix[2][2]*from[2]; +} +/* 3x3 matrix multiplied and added to/by a 3x3 matrix and added to another 3x3 matrix */ +DO_INLINE void muladd_fmatrix_fmatrix(float to[3][3], float matrixA[3][3], float matrixB[3][3]) +{ + muladd_fvector_fmatrix(to[0], matrixA[0],matrixB); + muladd_fvector_fmatrix(to[1], matrixA[1],matrixB); + muladd_fvector_fmatrix(to[2], matrixA[2],matrixB); +} +/* a vector multiplied and sub'd to/by a 3x3 matrix */ +DO_INLINE void mulsub_fvector_fmatrix(float to[3], float from[3], float matrix[3][3]) +{ + to[0] -= matrix[0][0]*from[0] + matrix[1][0]*from[1] + matrix[2][0]*from[2]; + to[1] -= matrix[0][1]*from[0] + matrix[1][1]*from[1] + matrix[2][1]*from[2]; + to[2] -= matrix[0][2]*from[0] + matrix[1][2]*from[1] + matrix[2][2]*from[2]; +} +/* 3x3 matrix multiplied and sub'd to/by a 3x3 matrix and added to another 3x3 matrix */ +DO_INLINE void mulsub_fmatrix_fmatrix(float to[3][3], float matrixA[3][3], float matrixB[3][3]) +{ + mulsub_fvector_fmatrix(to[0], matrixA[0],matrixB); + mulsub_fvector_fmatrix(to[1], matrixA[1],matrixB); + mulsub_fvector_fmatrix(to[2], matrixA[2],matrixB); +} +/* 3x3 matrix multiplied+added by a vector */ +/* STATUS: verified */ +DO_INLINE void muladd_fmatrix_fvector(float to[3], float matrix[3][3], float from[3]) +{ + to[0] += INPR(matrix[0],from); + to[1] += INPR(matrix[1],from); + to[2] += INPR(matrix[2],from); +} +/* 3x3 matrix multiplied+sub'ed by a vector */ +DO_INLINE void mulsub_fmatrix_fvector(float to[3], float matrix[3][3], float from[3]) +{ + to[0] -= INPR(matrix[0],from); + to[1] -= INPR(matrix[1],from); + to[2] -= INPR(matrix[2],from); +} +///////////////////////////////////////////////////////////////// + +/////////////////////////// +// SPARSE SYMMETRIC big matrix with 3x3 matrix entries +/////////////////////////// +/* printf a big matrix on console: for debug output */ +void print_bfmatrix(fmatrix3x3 *m3) +{ + unsigned int i = 0; + + for(i = 0; i < m3[0].vcount + m3[0].scount; i++) + { + print_fmatrix(m3[i].m); + } +} +/* create big matrix */ +DO_INLINE fmatrix3x3 *create_bfmatrix(unsigned int verts, unsigned int springs) +{ + // TODO: check if memory allocation was successfull */ + fmatrix3x3 *temp = (fmatrix3x3 *)MEM_callocN (sizeof (fmatrix3x3) * (verts + springs), "cloth_implicit_alloc_matrix"); + temp[0].vcount = verts; + temp[0].scount = springs; + return temp; +} +/* delete big matrix */ +DO_INLINE void del_bfmatrix(fmatrix3x3 *matrix) +{ + if (matrix != NULL) + { + MEM_freeN (matrix); + } +} + +/* copy big matrix */ +DO_INLINE void cp_bfmatrix(fmatrix3x3 *to, fmatrix3x3 *from) +{ + // TODO bounds checking + memcpy(to, from, sizeof(fmatrix3x3) * (from[0].vcount+from[0].scount) ); +} + +/* init big matrix */ +// slow in parallel +DO_INLINE void init_bfmatrix(fmatrix3x3 *matrix, float m3[3][3]) +{ + unsigned int i; + + for(i = 0; i < matrix[0].vcount+matrix[0].scount; i++) + { + cp_fmatrix(matrix[i].m, m3); + } +} + +/* init the diagonal of big matrix */ +// slow in parallel +DO_INLINE void initdiag_bfmatrix(fmatrix3x3 *matrix, float m3[3][3]) +{ + unsigned int i,j; + float tmatrix[3][3] = {{0,0,0},{0,0,0},{0,0,0}}; + + for(i = 0; i < matrix[0].vcount; i++) + { + cp_fmatrix(matrix[i].m, m3); + } + for(j = matrix[0].vcount; j < matrix[0].vcount+matrix[0].scount; j++) + { + cp_fmatrix(matrix[j].m, tmatrix); + } +} + +/* multiply big matrix with scalar*/ +DO_INLINE void mul_bfmatrix_S(fmatrix3x3 *matrix, float scalar) +{ + unsigned int i = 0; + for(i = 0; i < matrix[0].vcount+matrix[0].scount; i++) + { + mul_fmatrix_S(matrix[i].m, scalar); + } +} + +/* SPARSE SYMMETRIC multiply big matrix with long vector*/ +/* STATUS: verified */ +DO_INLINE void mul_bfmatrix_lfvector( float (*to)[3], fmatrix3x3 *from, lfVector *fLongVector) +{ + unsigned int i = 0; + lfVector *temp = create_lfvector(from[0].vcount); + + zero_lfvector(to, from[0].vcount); + +#pragma omp parallel sections private(i) + { +#pragma omp section + { + for(i = from[0].vcount; i < from[0].vcount+from[0].scount; i++) + { + muladd_fmatrix_fvector(to[from[i].c], from[i].m, fLongVector[from[i].r]); + } + } +#pragma omp section + { + for(i = 0; i < from[0].vcount+from[0].scount; i++) + { + muladd_fmatrix_fvector(temp[from[i].r], from[i].m, fLongVector[from[i].c]); + } + } + } + add_lfvector_lfvector(to, to, temp, from[0].vcount); + + del_lfvector(temp); + + +} + +/* SPARSE SYMMETRIC multiply big matrix with long vector (for diagonal preconditioner) */ +/* STATUS: verified */ +DO_INLINE void mul_prevfmatrix_lfvector( float (*to)[3], fmatrix3x3 *from, lfVector *fLongVector) +{ + unsigned int i = 0; + + for(i = 0; i < from[0].vcount; i++) + { + mul_fmatrix_fvector(to[from[i].r], from[i].m, fLongVector[from[i].c]); + } +} + +/* SPARSE SYMMETRIC add big matrix with big matrix: A = B + C*/ +DO_INLINE void add_bfmatrix_bfmatrix( fmatrix3x3 *to, fmatrix3x3 *from, fmatrix3x3 *matrix) +{ + unsigned int i = 0; + + /* process diagonal elements */ + for(i = 0; i < matrix[0].vcount+matrix[0].scount; i++) + { + add_fmatrix_fmatrix(to[i].m, from[i].m, matrix[i].m); + } + +} +/* SPARSE SYMMETRIC add big matrix with big matrix: A += B + C */ +DO_INLINE void addadd_bfmatrix_bfmatrix( fmatrix3x3 *to, fmatrix3x3 *from, fmatrix3x3 *matrix) +{ + unsigned int i = 0; + + /* process diagonal elements */ + for(i = 0; i < matrix[0].vcount+matrix[0].scount; i++) + { + addadd_fmatrix_fmatrix(to[i].m, from[i].m, matrix[i].m); + } + +} +/* SPARSE SYMMETRIC subadd big matrix with big matrix: A -= B + C */ +DO_INLINE void subadd_bfmatrix_bfmatrix( fmatrix3x3 *to, fmatrix3x3 *from, fmatrix3x3 *matrix) +{ + unsigned int i = 0; + + /* process diagonal elements */ + for(i = 0; i < matrix[0].vcount+matrix[0].scount; i++) + { + subadd_fmatrix_fmatrix(to[i].m, from[i].m, matrix[i].m); + } + +} +/* A = B - C (SPARSE SYMMETRIC sub big matrix with big matrix) */ +DO_INLINE void sub_bfmatrix_bfmatrix( fmatrix3x3 *to, fmatrix3x3 *from, fmatrix3x3 *matrix) +{ + unsigned int i = 0; + + /* process diagonal elements */ + for(i = 0; i < matrix[0].vcount+matrix[0].scount; i++) + { + sub_fmatrix_fmatrix(to[i].m, from[i].m, matrix[i].m); + } + +} +/* SPARSE SYMMETRIC sub big matrix with big matrix S (special constraint matrix with limited entries) */ +DO_INLINE void sub_bfmatrix_Smatrix( fmatrix3x3 *to, fmatrix3x3 *from, fmatrix3x3 *matrix) +{ + unsigned int i = 0; + + /* process diagonal elements */ + for(i = 0; i < matrix[0].vcount; i++) + { + sub_fmatrix_fmatrix(to[matrix[i].c].m, from[matrix[i].c].m, matrix[i].m); + } + +} +/* A += B - C (SPARSE SYMMETRIC addsub big matrix with big matrix) */ +DO_INLINE void addsub_bfmatrix_bfmatrix( fmatrix3x3 *to, fmatrix3x3 *from, fmatrix3x3 *matrix) +{ + unsigned int i = 0; + + /* process diagonal elements */ + for(i = 0; i < matrix[0].vcount+matrix[0].scount; i++) + { + addsub_fmatrix_fmatrix(to[i].m, from[i].m, matrix[i].m); + } + +} +/* SPARSE SYMMETRIC sub big matrix with big matrix*/ +/* A -= B * float + C * float --> for big matrix */ +/* VERIFIED */ +DO_INLINE void subadd_bfmatrixS_bfmatrixS( fmatrix3x3 *to, fmatrix3x3 *from, float aS, fmatrix3x3 *matrix, float bS) +{ + unsigned int i = 0; + + /* process diagonal elements */ + for(i = 0; i < matrix[0].vcount+matrix[0].scount; i++) + { + subadd_fmatrixS_fmatrixS(to[i].m, from[i].m, aS, matrix[i].m, bS); + } + +} + +/////////////////////////////////////////////////////////////////// +// simulator start +/////////////////////////////////////////////////////////////////// +typedef struct Implicit_Data +{ + lfVector *X, *V, *Xnew, *Vnew, *olddV, *F, *B, *dV, *z; + fmatrix3x3 *A, *dFdV, *dFdX, *S, *P, *Pinv, *bigI, *M; +} Implicit_Data; + +int implicit_init (Object *ob, ClothModifierData *clmd) +{ + unsigned int i = 0; + unsigned int pinned = 0; + Cloth *cloth = NULL; + ClothVertex *verts = NULL; + ClothSpring *spring = NULL; + Implicit_Data *id = NULL; + LinkNode *search = NULL; + + if(G.rt > 0) + printf("implicit_init\n"); + + // init memory guard + // MEMORY_BASE.first = MEMORY_BASE.last = NULL; + + cloth = (Cloth *)clmd->clothObject; + verts = cloth->verts; + + // create implicit base + id = (Implicit_Data *)MEM_callocN (sizeof(Implicit_Data), "implicit vecmat"); + cloth->implicit = id; + + /* process diagonal elements */ + id->A = create_bfmatrix(cloth->numverts, cloth->numsprings); + id->dFdV = create_bfmatrix(cloth->numverts, cloth->numsprings); + id->dFdX = create_bfmatrix(cloth->numverts, cloth->numsprings); + id->S = create_bfmatrix(cloth->numverts, 0); + id->Pinv = create_bfmatrix(cloth->numverts, cloth->numsprings); + id->P = create_bfmatrix(cloth->numverts, cloth->numsprings); + id->bigI = create_bfmatrix(cloth->numverts, cloth->numsprings); // TODO 0 springs + id->M = create_bfmatrix(cloth->numverts, cloth->numsprings); + id->X = create_lfvector(cloth->numverts); + id->Xnew = create_lfvector(cloth->numverts); + id->V = create_lfvector(cloth->numverts); + id->Vnew = create_lfvector(cloth->numverts); + id->olddV = create_lfvector(cloth->numverts); + zero_lfvector(id->olddV, cloth->numverts); + id->F = create_lfvector(cloth->numverts); + id->B = create_lfvector(cloth->numverts); + id->dV = create_lfvector(cloth->numverts); + id->z = create_lfvector(cloth->numverts); + + for(i=0;i<cloth->numverts;i++) + { + id->A[i].r = id->A[i].c = id->dFdV[i].r = id->dFdV[i].c = id->dFdX[i].r = id->dFdX[i].c = id->P[i].c = id->P[i].r = id->Pinv[i].c = id->Pinv[i].r = id->bigI[i].c = id->bigI[i].r = id->M[i].r = id->M[i].c = i; + + if(verts [i].flags & CLOTH_VERT_FLAG_PINNED) + { + id->S[pinned].pinned = 1; + id->S[pinned].c = id->S[pinned].r = i; + pinned++; + } + + initdiag_fmatrixS(id->M[i].m, verts[i].mass); + } + + // S is special and needs specific vcount and scount + id->S[0].vcount = pinned; id->S[0].scount = 0; + + // init springs + search = cloth->springs; + for(i=0;i<cloth->numsprings;i++) + { + spring = search->link; + + // dFdV_start[i].r = big_I[i].r = big_zero[i].r = + id->A[i+cloth->numverts].r = id->dFdV[i+cloth->numverts].r = id->dFdX[i+cloth->numverts].r = + id->P[i+cloth->numverts].r = id->Pinv[i+cloth->numverts].r = id->bigI[i+cloth->numverts].r = id->M[i+cloth->numverts].r = spring->ij; + + // dFdV_start[i].c = big_I[i].c = big_zero[i].c = + id->A[i+cloth->numverts].c = id->dFdV[i+cloth->numverts].c = id->dFdX[i+cloth->numverts].c = + id->P[i+cloth->numverts].c = id->Pinv[i+cloth->numverts].c = id->bigI[i+cloth->numverts].c = id->M[i+cloth->numverts].c = spring->kl; + + spring->matrix_index = i + cloth->numverts; + + search = search->next; + } + + initdiag_bfmatrix(id->bigI, I); + + for(i = 0; i < cloth->numverts; i++) + { + VECCOPY(id->X[i], verts[i].x); + } + + return 1; +} +int implicit_free (ClothModifierData *clmd) +{ + Implicit_Data *id; + Cloth *cloth; + cloth = (Cloth *)clmd->clothObject; + + if(cloth) + { + id = cloth->implicit; + + if(id) + { + del_bfmatrix(id->A); + del_bfmatrix(id->dFdV); + del_bfmatrix(id->dFdX); + del_bfmatrix(id->S); + del_bfmatrix(id->P); + del_bfmatrix(id->Pinv); + del_bfmatrix(id->bigI); + del_bfmatrix(id->M); + + del_lfvector(id->X); + del_lfvector(id->Xnew); + del_lfvector(id->V); + del_lfvector(id->Vnew); + del_lfvector(id->olddV); + del_lfvector(id->F); + del_lfvector(id->B); + del_lfvector(id->dV); + del_lfvector(id->z); + + MEM_freeN(id); + } + } + + return 1; +} + +DO_INLINE float fb(float length, float L) +{ + float x = length/L; + return (-11.541f*pow(x,4)+34.193f*pow(x,3)-39.083f*pow(x,2)+23.116f*x-9.713f); +} + +DO_INLINE float fbderiv(float length, float L) +{ + float x = length/L; + + return (-46.164f*pow(x,3)+102.579f*pow(x,2)-78.166f*x+23.116f); +} + +DO_INLINE float fbstar(float length, float L, float kb, float cb) +{ + float tempfb = kb * fb(length, L); + + float fbstar = cb * (length - L); + + if(tempfb < fbstar) + return fbstar; + else + return tempfb; +} + +// function to calculae bending spring force (taken from Choi & Co) +DO_INLINE float fbstar_jacobi(float length, float L, float kb, float cb) +{ + float tempfb = kb * fb(length, L); + float fbstar = cb * (length - L); + + if(tempfb < fbstar) + { + return cb; + } + else + { + return kb * fbderiv(length, L); + } +} + +DO_INLINE void filter(lfVector *V, fmatrix3x3 *S) +{ + unsigned int i=0; + + for(i=0;i<S[0].vcount;i++) + { + mul_fvector_fmatrix(V[S[i].r], V[S[i].r], S[i].m); + } +} + +int cg_filtered(lfVector *ldV, fmatrix3x3 *lA, lfVector *lB, lfVector *z, fmatrix3x3 *S) +{ + // Solves for unknown X in equation AX=B + unsigned int conjgrad_loopcount=0, conjgrad_looplimit=100; + float conjgrad_epsilon=0.0001f, conjgrad_lasterror=0; + lfVector *q, *d, *tmp, *r; + float s, starget, a, s_prev; + unsigned int numverts = lA[0].vcount; + q = create_lfvector(numverts); + d = create_lfvector(numverts); + tmp = create_lfvector(numverts); + r = create_lfvector(numverts); + + // zero_lfvector(ldV, CLOTHPARTICLES); + filter(ldV, S); + + add_lfvector_lfvector(ldV, ldV, z, numverts); + + // r = B - Mul(tmp,A,X); // just use B if X known to be zero + cp_lfvector(r, lB, numverts); + mul_bfmatrix_lfvector(tmp, lA, ldV); + sub_lfvector_lfvector(r, r, tmp, numverts); + + filter(r,S); + + cp_lfvector(d, r, numverts); + + s = dot_lfvector(r, r, numverts); + starget = s * sqrt(conjgrad_epsilon); + + while((s>starget && conjgrad_loopcount < conjgrad_looplimit)) + { + // Mul(q,A,d); // q = A*d; + mul_bfmatrix_lfvector(q, lA, d); + + filter(q,S); + + a = s/dot_lfvector(d, q, numverts); + + // X = X + d*a; + add_lfvector_lfvectorS(ldV, ldV, d, a, numverts); + + // r = r - q*a; + sub_lfvector_lfvectorS(r, r, q, a, numverts); + + s_prev = s; + s = dot_lfvector(r, r, numverts); + + //d = r+d*(s/s_prev); + add_lfvector_lfvectorS(d, r, d, (s/s_prev), numverts); + + filter(d,S); + + conjgrad_loopcount++; + } + conjgrad_lasterror = s; + + del_lfvector(q); + del_lfvector(d); + del_lfvector(tmp); + del_lfvector(r); + // printf("W/O conjgrad_loopcount: %d\n", conjgrad_loopcount); + + return conjgrad_loopcount<conjgrad_looplimit; // true means we reached desired accuracy in given time - ie stable +} + +// block diagonalizer +DO_INLINE void BuildPPinv(fmatrix3x3 *lA, fmatrix3x3 *P, fmatrix3x3 *Pinv) +{ + unsigned int i = 0; + + // Take only the diagonal blocks of A +// #pragma omp parallel for private(i) + for(i = 0; i<lA[0].vcount; i++) + { + // block diagonalizer + cp_fmatrix(P[i].m, lA[i].m); + inverse_fmatrix(Pinv[i].m, P[i].m); + + } +} +/* +// version 1.3 +int cg_filtered_pre(lfVector *dv, fmatrix3x3 *lA, lfVector *lB, lfVector *z, fmatrix3x3 *S, fmatrix3x3 *P, fmatrix3x3 *Pinv) +{ + unsigned int numverts = lA[0].vcount, iterations = 0, conjgrad_looplimit=100; + float delta0 = 0, deltaNew = 0, deltaOld = 0, alpha = 0; + float conjgrad_epsilon=0.0001; // 0.2 is dt for steps=5 + lfVector *r = create_lfvector(numverts); + lfVector *p = create_lfvector(numverts); + lfVector *s = create_lfvector(numverts); + lfVector *h = create_lfvector(numverts); + + BuildPPinv(lA, P, Pinv); + + filter(dv, S); + add_lfvector_lfvector(dv, dv, z, numverts); + + mul_bfmatrix_lfvector(r, lA, dv); + sub_lfvector_lfvector(r, lB, r, numverts); + filter(r, S); + + mul_prevfmatrix_lfvector(p, Pinv, r); + filter(p, S); + + deltaNew = dot_lfvector(r, p, numverts); + + delta0 = deltaNew * sqrt(conjgrad_epsilon); + + // itstart(); + + while ((deltaNew > delta0) && (iterations < conjgrad_looplimit)) + { + iterations++; + + mul_bfmatrix_lfvector(s, lA, p); + filter(s, S); + + alpha = deltaNew / dot_lfvector(p, s, numverts); + + add_lfvector_lfvectorS(dv, dv, p, alpha, numverts); + + add_lfvector_lfvectorS(r, r, s, -alpha, numverts); + + mul_prevfmatrix_lfvector(h, Pinv, r); + filter(h, S); + + deltaOld = deltaNew; + + deltaNew = dot_lfvector(r, h, numverts); + + add_lfvector_lfvectorS(p, h, p, deltaNew / deltaOld, numverts); + + filter(p, S); + + } + + // itend(); + // printf("cg_filtered_pre time: %f\n", (float)itval()); + + del_lfvector(h); + del_lfvector(s); + del_lfvector(p); + del_lfvector(r); + + printf("iterations: %d\n", iterations); + + return iterations<conjgrad_looplimit; +} +*/ +// version 1.4 +int cg_filtered_pre(lfVector *dv, fmatrix3x3 *lA, lfVector *lB, lfVector *z, fmatrix3x3 *S, fmatrix3x3 *P, fmatrix3x3 *Pinv, fmatrix3x3 *bigI) +{ + unsigned int numverts = lA[0].vcount, iterations = 0, conjgrad_looplimit=100; + float delta0 = 0, deltaNew = 0, deltaOld = 0, alpha = 0, tol = 0; + lfVector *r = create_lfvector(numverts); + lfVector *p = create_lfvector(numverts); + lfVector *s = create_lfvector(numverts); + lfVector *h = create_lfvector(numverts); + lfVector *bhat = create_lfvector(numverts); + lfVector *btemp = create_lfvector(numverts); + + BuildPPinv(lA, P, Pinv); + + initdiag_bfmatrix(bigI, I); + sub_bfmatrix_Smatrix(bigI, bigI, S); + + // x = Sx_0+(I-S)z + filter(dv, S); + add_lfvector_lfvector(dv, dv, z, numverts); + + // b_hat = S(b-A(I-S)z) + mul_bfmatrix_lfvector(r, lA, z); + mul_bfmatrix_lfvector(bhat, bigI, r); + sub_lfvector_lfvector(bhat, lB, bhat, numverts); + + // r = S(b-Ax) + mul_bfmatrix_lfvector(r, lA, dv); + sub_lfvector_lfvector(r, lB, r, numverts); + filter(r, S); + + // p = SP^-1r + mul_prevfmatrix_lfvector(p, Pinv, r); + filter(p, S); + + // delta0 = bhat^TP^-1bhat + mul_prevfmatrix_lfvector(btemp, Pinv, bhat); + delta0 = dot_lfvector(bhat, btemp, numverts); + + // deltaNew = r^TP + deltaNew = dot_lfvector(r, p, numverts); + + /* + filter(dv, S); + add_lfvector_lfvector(dv, dv, z, numverts); + + mul_bfmatrix_lfvector(r, lA, dv); + sub_lfvector_lfvector(r, lB, r, numverts); + filter(r, S); + + mul_prevfmatrix_lfvector(p, Pinv, r); + filter(p, S); + + deltaNew = dot_lfvector(r, p, numverts); + + delta0 = deltaNew * sqrt(conjgrad_epsilon); + */ + + // itstart(); + + tol = (0.01*0.2); + + while ((deltaNew > delta0*tol*tol) && (iterations < conjgrad_looplimit)) + { + iterations++; + + mul_bfmatrix_lfvector(s, lA, p); + filter(s, S); + + alpha = deltaNew / dot_lfvector(p, s, numverts); + + add_lfvector_lfvectorS(dv, dv, p, alpha, numverts); + + add_lfvector_lfvectorS(r, r, s, -alpha, numverts); + + mul_prevfmatrix_lfvector(h, Pinv, r); + filter(h, S); + + deltaOld = deltaNew; + + deltaNew = dot_lfvector(r, h, numverts); + + add_lfvector_lfvectorS(p, h, p, deltaNew / deltaOld, numverts); + + filter(p, S); + + } + + // itend(); + // printf("cg_filtered_pre time: %f\n", (float)itval()); + + del_lfvector(btemp); + del_lfvector(bhat); + del_lfvector(h); + del_lfvector(s); + del_lfvector(p); + del_lfvector(r); + + // printf("iterations: %d\n", iterations); + + return iterations<conjgrad_looplimit; +} + +// outer product is NOT cross product!!! +DO_INLINE void dfdx_spring_type1(float to[3][3], float extent[3], float length, float L, float dot, float k) +{ + // dir is unit length direction, rest is spring's restlength, k is spring constant. + // return (outerprod(dir,dir)*k + (I - outerprod(dir,dir))*(k - ((k*L)/length))); + float temp[3][3]; + float temp1 = k*(1.0 - (L/length)); + + mul_fvectorT_fvectorS(temp, extent, extent, 1.0 / dot); + sub_fmatrix_fmatrix(to, I, temp); + mul_fmatrix_S(to, temp1); + + mul_fvectorT_fvectorS(temp, extent, extent, k/ dot); + add_fmatrix_fmatrix(to, to, temp); + + /* + mul_fvectorT_fvector(temp, dir, dir); + sub_fmatrix_fmatrix(to, I, temp); + mul_fmatrix_S(to, k* (1.0f-(L/length))); + mul_fmatrix_S(temp, k); + add_fmatrix_fmatrix(to, temp, to); + */ +} + +DO_INLINE void dfdx_spring_type2(float to[3][3], float dir[3], float length, float L, float k, float cb) +{ + // return outerprod(dir,dir)*fbstar_jacobi(length, L, k, cb); + mul_fvectorT_fvectorS(to, dir, dir, fbstar_jacobi(length, L, k, cb)); +} + +DO_INLINE void dfdv_damp(float to[3][3], float dir[3], float damping) +{ + // derivative of force wrt velocity. + mul_fvectorT_fvectorS(to, dir, dir, damping); + +} + +DO_INLINE void dfdx_spring(float to[3][3], float dir[3],float length,float L,float k) +{ + // dir is unit length direction, rest is spring's restlength, k is spring constant. + //return ( (I-outerprod(dir,dir))*Min(1.0f,rest/length) - I) * -k; + mul_fvectorT_fvector(to, dir, dir); + sub_fmatrix_fmatrix(to, I, to); + mul_fmatrix_S(to, (((L/length)> 1.0f) ? (1.0f): (L/length))); + sub_fmatrix_fmatrix(to, to, I); + mul_fmatrix_S(to, -k); +} + +// unused atm +DO_INLINE void dfdx_damp(float to[3][3], float dir[3],float length,const float vel[3],float rest,float damping) +{ + // inner spring damping vel is the relative velocity of the endpoints. + // return (I-outerprod(dir,dir)) * (-damping * -(dot(dir,vel)/Max(length,rest))); + mul_fvectorT_fvector(to, dir, dir); + sub_fmatrix_fmatrix(to, I, to); + mul_fmatrix_S(to, (-damping * -(INPR(dir,vel)/MAX2(length,rest)))); + +} + +DO_INLINE void cloth_calc_spring_force(ClothModifierData *clmd, ClothSpring *s, lfVector *lF, lfVector *X, lfVector *V, fmatrix3x3 *dFdV, fmatrix3x3 *dFdX, float time) +{ + Cloth *cloth = clmd->clothObject; + ClothVertex *verts = cloth->verts; + float extent[3]; + float length = 0, dot = 0; + float dir[3] = {0,0,0}; + float vel[3]; + float k = 0.0f; + float L = s->restlen; + float cb = clmd->sim_parms->structural; + + float nullf[3] = {0,0,0}; + float stretch_force[3] = {0,0,0}; + float bending_force[3] = {0,0,0}; + float damping_force[3] = {0,0,0}; + float nulldfdx[3][3]={ {0,0,0}, {0,0,0}, {0,0,0}}; + + float scaling = 0.0; + + VECCOPY(s->f, nullf); + cp_fmatrix(s->dfdx, nulldfdx); + cp_fmatrix(s->dfdv, nulldfdx); + + // calculate elonglation + VECSUB(extent, X[s->kl], X[s->ij]); + VECSUB(vel, V[s->kl], V[s->ij]); + dot = INPR(extent, extent); + length = sqrt(dot); + + s->flags &= ~CLOTH_SPRING_FLAG_NEEDED; + + if(length > ALMOST_ZERO) + { + /* + if(length>L) + { + if((clmd->sim_parms->flags & CSIMSETT_FLAG_TEARING_ENABLED) + && ((((length-L)*100.0f/L) > clmd->sim_parms->maxspringlen))) // cut spring! + { + s->flags |= CSPRING_FLAG_DEACTIVATE; + return; + } + } + */ + mul_fvector_S(dir, extent, 1.0f/length); + } + else + { + mul_fvector_S(dir, extent, 0.0f); + } + + // calculate force of structural + shear springs + if((s->type & CLOTH_SPRING_TYPE_STRUCTURAL) || (s->type & CLOTH_SPRING_TYPE_SHEAR)) + { + if(length > L) // only on elonglation + { + s->flags |= CLOTH_SPRING_FLAG_NEEDED; + + k = clmd->sim_parms->structural; + + scaling = k + s->stiffness * ABS(clmd->sim_parms->max_struct-k); + + k = scaling / (clmd->sim_parms->avg_spring_len + FLT_EPSILON); + + // TODO: verify, half verified (couldn't see error) + mul_fvector_S(stretch_force, dir, k*(length-L)); + + VECADD(s->f, s->f, stretch_force); + + // Ascher & Boxman, p.21: Damping only during elonglation + // something wrong with it... + mul_fvector_S(damping_force, dir, clmd->sim_parms->Cdis * INPR(vel,dir)); + VECADD(s->f, s->f, damping_force); + + /* VERIFIED */ + dfdx_spring(s->dfdx, dir, length, L, k); + + /* VERIFIED */ + dfdv_damp(s->dfdv, dir, clmd->sim_parms->Cdis); + + } + } + else if(s->type & CLOTH_SPRING_TYPE_GOAL) + { + float tvect[3]; + + s->flags |= CLOTH_SPRING_FLAG_NEEDED; + + // current_position = xold + t * (newposition - xold) + VECSUB(tvect, verts[s->ij].xconst, verts[s->ij].xold); + mul_fvector_S(tvect, tvect, time); + VECADD(tvect, tvect, verts[s->ij].xold); + + VECSUB(extent, X[s->ij], tvect); + + dot = INPR(extent, extent); + length = sqrt(dot); + + k = clmd->sim_parms->goalspring; + + scaling = k + s->stiffness * ABS(clmd->sim_parms->max_struct-k); + + k = verts [s->ij].goal * scaling / (clmd->sim_parms->avg_spring_len + FLT_EPSILON); + + VECADDS(s->f, s->f, extent, -k); + + mul_fvector_S(damping_force, dir, clmd->sim_parms->goalfrict * 0.01 * INPR(vel,dir)); + VECADD(s->f, s->f, damping_force); + + // HERE IS THE PROBLEM!!!! + // dfdx_spring(s->dfdx, dir, length, 0.0, k); + // dfdv_damp(s->dfdv, dir, MIN2(1.0, (clmd->sim_parms->goalfrict/100.0))); + } + else // calculate force of bending springs + { + if(length < L) + { + s->flags |= CLOTH_SPRING_FLAG_NEEDED; + + k = clmd->sim_parms->bending; + + scaling = k + s->stiffness * ABS(clmd->sim_parms->max_bend-k); + cb = k = scaling / (20.0*(clmd->sim_parms->avg_spring_len + FLT_EPSILON)); + + mul_fvector_S(bending_force, dir, fbstar(length, L, k, cb)); + VECADD(s->f, s->f, bending_force); + + dfdx_spring_type2(s->dfdx, dir, length,L, k, cb); + } + } +} + +DO_INLINE void cloth_apply_spring_force(ClothModifierData *clmd, ClothSpring *s, lfVector *lF, lfVector *X, lfVector *V, fmatrix3x3 *dFdV, fmatrix3x3 *dFdX) +{ + if(s->flags & CLOTH_SPRING_FLAG_NEEDED) + { + if(!(s->type & CLOTH_SPRING_TYPE_BENDING)) + { + sub_fmatrix_fmatrix(dFdV[s->ij].m, dFdV[s->ij].m, s->dfdv); + sub_fmatrix_fmatrix(dFdV[s->kl].m, dFdV[s->kl].m, s->dfdv); + add_fmatrix_fmatrix(dFdV[s->matrix_index].m, dFdV[s->matrix_index].m, s->dfdv); + } + + VECADD(lF[s->ij], lF[s->ij], s->f); + + if(!(s->type & CLOTH_SPRING_TYPE_GOAL)) + VECSUB(lF[s->kl], lF[s->kl], s->f); + + sub_fmatrix_fmatrix(dFdX[s->kl].m, dFdX[s->kl].m, s->dfdx); + sub_fmatrix_fmatrix(dFdX[s->ij].m, dFdX[s->ij].m, s->dfdx); + add_fmatrix_fmatrix(dFdX[s->matrix_index].m, dFdX[s->matrix_index].m, s->dfdx); + } +} + + +static void CalcFloat( float *v1, float *v2, float *v3, float *n) +{ + float n1[3],n2[3]; + + n1[0]= v1[0]-v2[0]; + n2[0]= v2[0]-v3[0]; + n1[1]= v1[1]-v2[1]; + n2[1]= v2[1]-v3[1]; + n1[2]= v1[2]-v2[2]; + n2[2]= v2[2]-v3[2]; + n[0]= n1[1]*n2[2]-n1[2]*n2[1]; + n[1]= n1[2]*n2[0]-n1[0]*n2[2]; + n[2]= n1[0]*n2[1]-n1[1]*n2[0]; +} + +static void CalcFloat4( float *v1, float *v2, float *v3, float *v4, float *n) +{ + /* real cross! */ + float n1[3],n2[3]; + + n1[0]= v1[0]-v3[0]; + n1[1]= v1[1]-v3[1]; + n1[2]= v1[2]-v3[2]; + + n2[0]= v2[0]-v4[0]; + n2[1]= v2[1]-v4[1]; + n2[2]= v2[2]-v4[2]; + + n[0]= n1[1]*n2[2]-n1[2]*n2[1]; + n[1]= n1[2]*n2[0]-n1[0]*n2[2]; + n[2]= n1[0]*n2[1]-n1[1]*n2[0]; +} + +float calculateVertexWindForce(float wind[3], float vertexnormal[3]) +{ + return (INPR(wind, vertexnormal)); +} + +void cloth_calc_force(ClothModifierData *clmd, lfVector *lF, lfVector *lX, lfVector *lV, fmatrix3x3 *dFdV, fmatrix3x3 *dFdX, ListBase *effectors, float time, fmatrix3x3 *M) +{ + /* Collect forces and derivatives: F,dFdX,dFdV */ + Cloth *cloth = clmd->clothObject; + int i = 0; + float spring_air = clmd->sim_parms->Cvi * 0.01f; /* viscosity of air scaled in percent */ + float gravity[3]; + float tm2[3][3] = {{-spring_air,0,0}, {0,-spring_air,0},{0,0,-spring_air}}; + MFace *mfaces = cloth->mfaces; + unsigned int numverts = cloth->numverts; + LinkNode *search = cloth->springs; + lfVector *winvec; + + VECCOPY(gravity, clmd->sim_parms->gravity); + mul_fvector_S(gravity, gravity, 0.001f); /* scale gravity force */ + + /* set dFdX jacobi matrix to zero */ + init_bfmatrix(dFdX, ZERO); + /* set dFdX jacobi matrix diagonal entries to -spring_air */ + initdiag_bfmatrix(dFdV, tm2); + + init_lfvector(lF, gravity, numverts); + + /* multiply lF with mass matrix + // force = mass * acceleration (in this case: gravity) + */ + for(i = 0; i < numverts; i++) + { + float temp[3]; + VECCOPY(temp, lF[i]); + mul_fmatrix_fvector(lF[i], M[i].m, temp); + } + + submul_lfvectorS(lF, lV, spring_air, numverts); + + /* handle external forces like wind */ + if(effectors) + { + // 0 = force, 1 = normalized force + winvec = create_lfvector(cloth->numverts); + + if(!winvec) + printf("winvec: out of memory in implicit.c\n"); + + // precalculate wind forces + for(i = 0; i < cloth->numverts; i++) + { + float speed[3] = {0.0f, 0.0f,0.0f}; + + pdDoEffectors(effectors, lX[i], winvec[i], speed, (float)G.scene->r.cfra, 0.0f, 0); + } + + for(i = 0; i < cloth->numfaces; i++) + { + float trinormal[3]={0,0,0}; // normalized triangle normal + float triunnormal[3]={0,0,0}; // not-normalized-triangle normal + float tmp[3]={0,0,0}; + float factor = (mfaces[i].v4) ? 0.25 : 1.0 / 3.0; + factor *= 0.02; + + // calculate face normal + if(mfaces[i].v4) + CalcFloat4(lX[mfaces[i].v1],lX[mfaces[i].v2],lX[mfaces[i].v3],lX[mfaces[i].v4],triunnormal); + else + CalcFloat(lX[mfaces[i].v1],lX[mfaces[i].v2],lX[mfaces[i].v3],triunnormal); + + VECCOPY(trinormal, triunnormal); + Normalize(trinormal); + + // add wind from v1 + VECCOPY(tmp, trinormal); + VecMulf(tmp, calculateVertexWindForce(winvec[mfaces[i].v1], triunnormal)); + VECADDS(lF[mfaces[i].v1], lF[mfaces[i].v1], tmp, factor); + + // add wind from v2 + VECCOPY(tmp, trinormal); + VecMulf(tmp, calculateVertexWindForce(winvec[mfaces[i].v2], triunnormal)); + VECADDS(lF[mfaces[i].v2], lF[mfaces[i].v2], tmp, factor); + + // add wind from v3 + VECCOPY(tmp, trinormal); + VecMulf(tmp, calculateVertexWindForce(winvec[mfaces[i].v3], triunnormal)); + VECADDS(lF[mfaces[i].v3], lF[mfaces[i].v3], tmp, factor); + + // add wind from v4 + if(mfaces[i].v4) + { + VECCOPY(tmp, trinormal); + VecMulf(tmp, calculateVertexWindForce(winvec[mfaces[i].v4], triunnormal)); + VECADDS(lF[mfaces[i].v4], lF[mfaces[i].v4], tmp, factor); + } + } + del_lfvector(winvec); + } + + // calculate spring forces + search = cloth->springs; + while(search) + { + // only handle active springs + // if(((clmd->sim_parms->flags & CSIMSETT_FLAG_TEARING_ENABLED) && !(springs[i].flags & CSPRING_FLAG_DEACTIVATE))|| !(clmd->sim_parms->flags & CSIMSETT_FLAG_TEARING_ENABLED)){} + cloth_calc_spring_force(clmd, search->link, lF, lX, lV, dFdV, dFdX, time); + + search = search->next; + } + + // apply spring forces + search = cloth->springs; + while(search) + { + // only handle active springs + // if(((clmd->sim_parms->flags & CSIMSETT_FLAG_TEARING_ENABLED) && !(springs[i].flags & CSPRING_FLAG_DEACTIVATE))|| !(clmd->sim_parms->flags & CSIMSETT_FLAG_TEARING_ENABLED)) + cloth_apply_spring_force(clmd, search->link, lF, lX, lV, dFdV, dFdX); + search = search->next; + } + // printf("\n"); +} + +void simulate_implicit_euler(lfVector *Vnew, lfVector *lX, lfVector *lV, lfVector *lF, fmatrix3x3 *dFdV, fmatrix3x3 *dFdX, float dt, fmatrix3x3 *A, lfVector *B, lfVector *dV, fmatrix3x3 *S, lfVector *z, lfVector *olddV, fmatrix3x3 *P, fmatrix3x3 *Pinv, fmatrix3x3 *M, fmatrix3x3 *bigI) +{ + unsigned int numverts = dFdV[0].vcount; + + lfVector *dFdXmV = create_lfvector(numverts); + zero_lfvector(dV, numverts); + + cp_bfmatrix(A, M); + + subadd_bfmatrixS_bfmatrixS(A, dFdV, dt, dFdX, (dt*dt)); + + mul_bfmatrix_lfvector(dFdXmV, dFdX, lV); + + add_lfvectorS_lfvectorS(B, lF, dt, dFdXmV, (dt*dt), numverts); + + itstart(); + + cg_filtered(dV, A, B, z, S); /* conjugate gradient algorithm to solve Ax=b */ + // cg_filtered_pre(dV, A, B, z, S, P, Pinv, bigI); + + itend(); + // printf("cg_filtered calc time: %f\n", (float)itval()); + + cp_lfvector(olddV, dV, numverts); + + // advance velocities + add_lfvector_lfvector(Vnew, lV, dV, numverts); + + + del_lfvector(dFdXmV); +} + +int implicit_solver (Object *ob, float frame, ClothModifierData *clmd, ListBase *effectors) +{ + unsigned int i=0; + float step=0.0f, tf=clmd->sim_parms->timescale; + Cloth *cloth = clmd->clothObject; + ClothVertex *verts = cloth->verts; + unsigned int numverts = cloth->numverts; + float dt = clmd->sim_parms->timescale / clmd->sim_parms->stepsPerFrame; + Implicit_Data *id = cloth->implicit; + int result = 0; + + if(clmd->sim_parms->flags & CLOTH_SIMSETTINGS_FLAG_GOAL) /* do goal stuff */ + { + for(i = 0; i < numverts; i++) + { + // update velocities with constrained velocities from pinned verts + if(verts [i].flags & CLOTH_VERT_FLAG_PINNED) + { + VECSUB(id->V[i], verts[i].xconst, verts[i].xold); + // VecMulf(id->V[i], clmd->sim_parms->stepsPerFrame); + } + } + } + + while(step < tf) + { + // calculate forces + effectors= pdInitEffectors(ob,NULL); + cloth_calc_force(clmd, id->F, id->X, id->V, id->dFdV, id->dFdX, effectors, step, id->M); + if(effectors) pdEndEffectors(effectors); + + // calculate new velocity + simulate_implicit_euler(id->Vnew, id->X, id->V, id->F, id->dFdV, id->dFdX, dt, id->A, id->B, id->dV, id->S, id->z, id->olddV, id->P, id->Pinv, id->M, id->bigI); + + // advance positions + add_lfvector_lfvectorS(id->Xnew, id->X, id->Vnew, dt, numverts); + + /* move pinned verts to correct position */ + for(i = 0; i < numverts; i++) + { + if(clmd->sim_parms->flags & CLOTH_SIMSETTINGS_FLAG_GOAL) + { + if(verts [i].flags & CLOTH_VERT_FLAG_PINNED) + { + float tvect[3] = {.0,.0,.0}; + VECSUB(tvect, verts[i].xconst, verts[i].xold); + mul_fvector_S(tvect, tvect, step+dt); + VECADD(tvect, tvect, verts[i].xold); + VECCOPY(id->Xnew[i], tvect); + } + } + + VECCOPY(verts[i].txold, id->X[i]); + } + + if(clmd->coll_parms->flags & CLOTH_COLLSETTINGS_FLAG_ENABLED) + { + // collisions + // itstart(); + + // update verts to current positions + for(i = 0; i < numverts; i++) + { + VECCOPY(verts[i].tx, id->Xnew[i]); + + VECSUB(verts[i].tv, verts[i].tx, verts[i].txold); + VECCOPY(verts[i].v, verts[i].tv); + } + + // call collision function + // TODO: check if "step" or "step+dt" is correct - dg + result = cloth_bvh_objcollision(ob, clmd, step, dt); + + // correct velocity again, just to be sure we had to change it due to adaptive collisions + for(i = 0; i < numverts; i++) + { + VECSUB(verts[i].tv, verts[i].tx, id->X[i]); + } + + // copy corrected positions back to simulation + for(i = 0; i < numverts; i++) + { + if(result) + { + + if((clmd->sim_parms->flags & CLOTH_SIMSETTINGS_FLAG_GOAL) && (verts [i].flags & CLOTH_VERT_FLAG_PINNED)) + continue; + + VECCOPY(id->Xnew[i], verts[i].tx); + VECCOPY(id->Vnew[i], verts[i].tv); + VecMulf(id->Vnew[i], clmd->sim_parms->stepsPerFrame); + } + } + + // X = Xnew; + cp_lfvector(id->X, id->Xnew, numverts); + + // if there were collisions, advance the velocity from v_n+1/2 to v_n+1 + + if(result) + { + // V = Vnew; + cp_lfvector(id->V, id->Vnew, numverts); + + // calculate + effectors= pdInitEffectors(ob,NULL); + cloth_calc_force(clmd, id->F, id->X, id->V, id->dFdV, id->dFdX, effectors, step+dt, id->M); + if(effectors) pdEndEffectors(effectors); + + simulate_implicit_euler(id->Vnew, id->X, id->V, id->F, id->dFdV, id->dFdX, dt / 2.0f, id->A, id->B, id->dV, id->S, id->z, id->olddV, id->P, id->Pinv, id->M, id->bigI); + } + + } + else + { + // X = Xnew; + cp_lfvector(id->X, id->Xnew, numverts); + } + + // itend(); + // printf("collision time: %f\n", (float)itval()); + + // V = Vnew; + cp_lfvector(id->V, id->Vnew, numverts); + + step += dt; + + } + + for(i = 0; i < numverts; i++) + { + if((clmd->sim_parms->flags & CLOTH_SIMSETTINGS_FLAG_GOAL) && (verts [i].flags & CLOTH_VERT_FLAG_PINNED)) + { + VECCOPY(verts[i].txold, verts[i].xconst); // TODO: test --> should be .x + VECCOPY(verts[i].x, verts[i].xconst); + VECCOPY(verts[i].v, id->V[i]); + } + else + { + VECCOPY(verts[i].txold, id->X[i]); + VECCOPY(verts[i].x, id->X[i]); + VECCOPY(verts[i].v, id->V[i]); + } + } + + return 1; +} + +void implicit_set_positions (ClothModifierData *clmd) +{ + Cloth *cloth = clmd->clothObject; + ClothVertex *verts = cloth->verts; + unsigned int numverts = cloth->numverts, i; + Implicit_Data *id = cloth->implicit; + + for(i = 0; i < numverts; i++) + { + VECCOPY(id->X[i], verts[i].x); + VECCOPY(id->V[i], verts[i].v); + } + if(G.rt > 0) + printf("implicit_set_positions\n"); +} + diff --git a/source/blender/blenkernel/intern/ipo.c b/source/blender/blenkernel/intern/ipo.c index 78e8f2c5ee0..8407c66d584 100644 --- a/source/blender/blenkernel/intern/ipo.c +++ b/source/blender/blenkernel/intern/ipo.c @@ -23,7 +23,7 @@ * * The Original Code is: all of this file. * - * Contributor(s): none yet. + * Contributor(s): 2008, Joshua Leung (IPO System cleanup) * * ***** END GPL LICENSE BLOCK ***** */ @@ -72,15 +72,23 @@ #include "BKE_main.h" #include "BKE_mesh.h" #include "BKE_object.h" + +#ifndef DISABLE_PYTHON #include "BPY_extern.h" /* for BPY_pydriver_eval() */ +#endif #define SMALL -1.0e-10 +/* ***************************** Adrcode Blocktype Defines ********************************* */ + /* This array concept was meant to make sure that defines such as OB_LOC_X don't have to be enumerated, also for backward compatibility, future changes, and to enable it all can be accessed with a for-next loop. + + This should whole adrcode system should eventually be replaced by a proper Data API */ + int co_ar[CO_TOTIPO]= { CO_ENFORCE, CO_HEADTAIL }; @@ -150,9 +158,9 @@ int wo_ar[WO_TOTIPO]= { }; int la_ar[LA_TOTIPO]= { - LA_ENERGY, LA_COL_R, LA_COL_G, LA_COL_B, + LA_ENERGY, LA_COL_R, LA_COL_G, LA_COL_B, LA_DIST, LA_SPOTSI, LA_SPOTBL, - LA_QUAD1, LA_QUAD2, LA_HALOINT, + LA_QUAD1, LA_QUAD2, LA_HALOINT, MA_MAP1+MAP_OFS_X, MA_MAP1+MAP_OFS_Y, MA_MAP1+MAP_OFS_Z, MA_MAP1+MAP_SIZE_X, MA_MAP1+MAP_SIZE_Y, MA_MAP1+MAP_SIZE_Z, @@ -174,460 +182,617 @@ int fluidsim_ar[FLUIDSIM_TOTIPO]= { FLUIDSIM_VISC, FLUIDSIM_TIME, FLUIDSIM_GRAV_X , FLUIDSIM_GRAV_Y , FLUIDSIM_GRAV_Z , FLUIDSIM_VEL_X , FLUIDSIM_VEL_Y , FLUIDSIM_VEL_Z , - FLUIDSIM_ACTIVE + FLUIDSIM_ACTIVE, + FLUIDSIM_ATTR_FORCE_STR, FLUIDSIM_ATTR_FORCE_RADIUS, + FLUIDSIM_VEL_FORCE_STR, FLUIDSIM_VEL_FORCE_RADIUS, }; int part_ar[PART_TOTIPO]= { PART_EMIT_FREQ, PART_EMIT_LIFE, PART_EMIT_VEL, PART_EMIT_AVE, PART_EMIT_SIZE, PART_AVE, PART_SIZE, PART_DRAG, PART_BROWN, PART_DAMP, PART_LENGTH, PART_CLUMP, PART_GRAV_X, PART_GRAV_Y, PART_GRAV_Z, PART_KINK_AMP, PART_KINK_FREQ, PART_KINK_SHAPE, - PART_BB_TILT + PART_BB_TILT, PART_PD_FSTR, PART_PD_FFALL, PART_PD_FMAXD, PART_PD2_FSTR, PART_PD2_FFALL, PART_PD2_FMAXD }; +/* ************************** Data-Level Functions ************************* */ + +/* ---------------------- Freeing --------------------------- */ -float frame_to_float(int cfra) /* see also bsystem_time in object.c */ +/* frees the ipo curve itself too */ +void free_ipo_curve (IpoCurve *icu) { - extern float bluroffs; /* bad stuff borrowed from object.c */ - extern float fieldoffs; - float ctime; + if (icu == NULL) + return; - ctime= (float)cfra; - ctime+= bluroffs+fieldoffs; - ctime*= G.scene->r.framelen; + if (icu->bezt) + MEM_freeN(icu->bezt); + if (icu->driver) + MEM_freeN(icu->driver); - return ctime; -} - -/* includes ipo curve itself */ -void free_ipo_curve(IpoCurve *icu) -{ - if(icu->bezt) MEM_freeN(icu->bezt); - if(icu->bp) MEM_freeN(icu->bp); - if(icu->driver) MEM_freeN(icu->driver); MEM_freeN(icu); } /* do not free ipo itself */ -void free_ipo(Ipo *ipo) +void free_ipo (Ipo *ipo) { - IpoCurve *icu; + IpoCurve *icu, *icn; + + if (ipo == NULL) + return; - while( (icu= ipo->curve.first) ) { + for (icu= ipo->curve.first; icu; icu= icn) { + icn= icu->next; + + /* must remove the link before freeing, as the curve is freed too */ BLI_remlink(&ipo->curve, icu); free_ipo_curve(icu); } } +/* ---------------------- Init --------------------------- */ + /* on adding new ipos, or for empty views */ -void ipo_default_v2d_cur(int blocktype, rctf *cur) +void ipo_default_v2d_cur (int blocktype, rctf *cur) { - if(blocktype==ID_CA) { - cur->xmin= G.scene->r.sfra; - cur->xmax= G.scene->r.efra; - cur->ymin= 0.0; - cur->ymax= 100.0; - } - else if ELEM5(blocktype, ID_MA, ID_CU, ID_WO, ID_LA, ID_CO) { - cur->xmin= (float)G.scene->r.sfra-0.1; - cur->xmax= G.scene->r.efra; - cur->ymin= (float)-0.1; - cur->ymax= (float)+1.1; - } - else if(blocktype==ID_TE) { - cur->xmin= (float)G.scene->r.sfra-0.1; - cur->xmax= G.scene->r.efra; - cur->ymin= (float)-0.1; - cur->ymax= (float)+1.1; - } - else if(blocktype==ID_SEQ) { - cur->xmin= -5.0; - cur->xmax= 105.0; - cur->ymin= (float)-0.1; - cur->ymax= (float)+1.1; - } - else if(blocktype==ID_KE) { - cur->xmin= (float)G.scene->r.sfra-0.1; - cur->xmax= G.scene->r.efra; - cur->ymin= (float)-0.1; - cur->ymax= (float)+2.1; - } - else { /* ID_OB and everything else */ - cur->xmin= G.scene->r.sfra; - cur->xmax= G.scene->r.efra; - cur->ymin= -5.0; - cur->ymax= +5.0; + switch (blocktype) { + case ID_CA: + cur->xmin= (float)G.scene->r.sfra; + cur->xmax= (float)G.scene->r.efra; + cur->ymin= 0.0f; + cur->ymax= 100.0f; + break; + + case ID_MA: case ID_WO: case ID_LA: + case ID_CU: case ID_CO: + cur->xmin= (float)(G.scene->r.sfra - 0.1f); + cur->xmax= (float)G.scene->r.efra; + cur->ymin= (float)-0.1f; + cur->ymax= (float)+1.1f; + break; + + case ID_TE: + cur->xmin= (float)(G.scene->r.sfra - 0.1f); + cur->xmax= (float)G.scene->r.efra; + cur->ymin= (float)-0.1f; + cur->ymax= (float)+1.1f; + break; + + case ID_SEQ: + cur->xmin= -5.0f; + cur->xmax= 105.0f; + cur->ymin= (float)-0.1f; + cur->ymax= (float)+1.1f; + break; + + case ID_KE: + cur->xmin= (float)(G.scene->r.sfra - 0.1f); + cur->xmax= (float)G.scene->r.efra; + cur->ymin= (float)-0.1f; + cur->ymax= (float)+2.1f; + break; + + default: /* ID_OB and everything else */ + cur->xmin= (float)G.scene->r.sfra; + cur->xmax= (float)G.scene->r.efra; + cur->ymin= -5.0f; + cur->ymax= +5.0f; + break; } } - -Ipo *add_ipo(char *name, int idcode) +/* create a new IPO block (allocates the block) */ +Ipo *add_ipo (char name[], int blocktype) { Ipo *ipo; ipo= alloc_libblock(&G.main->ipo, ID_IP, name); - ipo->blocktype= idcode; - ipo_default_v2d_cur(idcode, &ipo->cur); + ipo->blocktype= blocktype; + ipo_default_v2d_cur(blocktype, &ipo->cur); return ipo; } -Ipo *copy_ipo(Ipo *ipo) +/* ---------------------- Copy --------------------------- */ + +/* duplicate an IPO block and all its data */ +Ipo *copy_ipo (Ipo *src) { - Ipo *ipon; + Ipo *dst; IpoCurve *icu; - if(ipo==NULL) return 0; - - ipon= copy_libblock(ipo); + if (src == NULL) + return NULL; - BLI_duplicatelist(&(ipon->curve), &(ipo->curve)); + dst= copy_libblock(src); + BLI_duplicatelist(&dst->curve, &src->curve); - for(icu= ipo->curve.first; icu; icu= icu->next) { + for (icu= src->curve.first; icu; icu= icu->next) { icu->bezt= MEM_dupallocN(icu->bezt); - if(icu->driver) icu->driver= MEM_dupallocN(icu->driver); + + if (icu->driver) + icu->driver= MEM_dupallocN(icu->driver); } - return ipon; + return dst; } -/* uses id->newid to match pointers with other copied data */ -void ipo_idnew(Ipo *ipo) +/* ---------------------- Relink --------------------------- */ + +/* uses id->newid to match pointers with other copied data + * - called after single-user or other such + */ +void ipo_idnew (Ipo *ipo) { - if(ipo) { + if (ipo) { IpoCurve *icu; - for(icu= ipo->curve.first; icu; icu= icu->next) { - if(icu->driver) { + for (icu= ipo->curve.first; icu; icu= icu->next) { + if (icu->driver) ID_NEW(icu->driver->ob); - } } } } -void make_local_obipo(Ipo *ipo) +/* --------------------- Find + Check ----------------------- */ + +/* find the IPO-curve within a given IPO-block with the adrcode of interest */ +IpoCurve *find_ipocurve (Ipo *ipo, int adrcode) +{ + if (ipo) { + IpoCurve *icu; + + for (icu= ipo->curve.first; icu; icu= icu->next) { + if (icu->adrcode == adrcode) + return icu; + } + } + return NULL; +} + +/* return whether the given IPO block has a IPO-curve with the given adrcode */ +short has_ipo_code(Ipo *ipo, int adrcode) +{ + /* return success of faliure from trying to find such an IPO-curve */ + return (find_ipocurve(ipo, adrcode) != NULL); +} + +/* ---------------------- Make Local --------------------------- */ + + +/* make the given IPO local (for Objects) + * - only lib users: do nothing + * - only local users: set flag + * - mixed: make copy + */ +void make_local_obipo (Ipo *src) { Object *ob; - Ipo *ipon; + Ipo *dst; int local=0, lib=0; - /* - only lib users: do nothing - * - only local users: set flag - * - mixed: make copy - */ - - ob= G.main->object.first; - while(ob) { - if(ob->ipo==ipo) { - if(ob->id.lib) lib= 1; + /* check if only local and/or lib */ + for (ob= G.main->object.first; ob; ob= ob->id.next) { + if (ob->ipo == src) { + if (ob->id.lib) lib= 1; else local= 1; } - ob= ob->id.next; } - if(local && lib==0) { - ipo->id.lib= 0; - ipo->id.flag= LIB_LOCAL; - new_id(0, (ID *)ipo, 0); + /* only local - set flag */ + if (local && lib==0) { + src->id.lib= 0; + src->id.flag= LIB_LOCAL; + new_id(0, (ID *)src, 0); } - else if(local && lib) { - ipon= copy_ipo(ipo); - ipon->id.us= 0; + /* mixed: make copy */ + else if (local && lib) { + dst= copy_ipo(src); + dst->id.us= 0; - ob= G.main->object.first; - while(ob) { - if(ob->ipo==ipo) { - - if(ob->id.lib==NULL) { - ob->ipo= ipon; - ipon->id.us++; - ipo->id.us--; + for (ob= G.main->object.first; ob; ob= ob->id.next) { + if (ob->ipo == src) { + if (ob->id.lib == NULL) { + ob->ipo= dst; + dst->id.us++; + src->id.us--; } } - ob= ob->id.next; } } } -void make_local_matipo(Ipo *ipo) +/* make the given IPO local (for Materials) + * - only lib users: do nothing + * - only local users: set flag + * - mixed: make copy + */ +void make_local_matipo (Ipo *src) { Material *ma; - Ipo *ipon; + Ipo *dst; int local=0, lib=0; - - /* - only lib users: do nothing - * - only local users: set flag - * - mixed: make copy - */ - ma= G.main->mat.first; - while(ma) { - if(ma->ipo==ipo) { - if(ma->id.lib) lib= 1; + /* check if only local and/or lib */ + for (ma= G.main->mat.first; ma; ma= ma->id.next) { + if (ma->ipo == src) { + if (ma->id.lib) lib= 1; else local= 1; } - ma= ma->id.next; } - if(local && lib==0) { - ipo->id.lib= 0; - ipo->id.flag= LIB_LOCAL; - new_id(0, (ID *)ipo, 0); + /* only local - set flag */ + if (local && lib==0) { + src->id.lib= 0; + src->id.flag= LIB_LOCAL; + new_id(0, (ID *)src, 0); } - else if(local && lib) { - ipon= copy_ipo(ipo); - ipon->id.us= 0; + /* mixed: make copy */ + else if (local && lib) { + dst= copy_ipo(src); + dst->id.us= 0; - ma= G.main->mat.first; - while(ma) { - if(ma->ipo==ipo) { - - if(ma->id.lib==NULL) { - ma->ipo= ipon; - ipon->id.us++; - ipo->id.us--; + for (ma= G.main->mat.first; ma; ma= ma->id.next) { + if (ma->ipo == src) { + if (ma->id.lib == NULL) { + ma->ipo= dst; + dst->id.us++; + src->id.us--; } } - ma= ma->id.next; } } } -void make_local_keyipo(Ipo *ipo) +/* make the given IPO local (for ShapeKeys) + * - only lib users: do nothing + * - only local users: set flag + * - mixed: make copy + */ +void make_local_keyipo (Ipo *src) { Key *key; - Ipo *ipon; + Ipo *dst; int local=0, lib=0; - - /* - only lib users: do nothing - * - only local users: set flag - * - mixed: make copy - */ - key= G.main->key.first; - while(key) { - if(key->ipo==ipo) { - if(key->id.lib) lib= 1; + /* check if only local and/or lib */ + for (key= G.main->key.first; key; key= key->id.next) { + if (key->ipo == src) { + if (key->id.lib) lib= 1; else local= 1; } - key= key->id.next; } - if(local && lib==0) { - ipo->id.lib= 0; - ipo->id.flag= LIB_LOCAL; - new_id(0, (ID *)ipo, 0); + /* only local - set flag */ + if (local && lib==0) { + src->id.lib= 0; + src->id.flag= LIB_LOCAL; + new_id(0, (ID *)src, 0); } - else if(local && lib) { - ipon= copy_ipo(ipo); - ipon->id.us= 0; + /* mixed: make copy */ + else if (local && lib) { + dst= copy_ipo(src); + dst->id.us= 0; - key= G.main->key.first; - while(key) { - if(key->ipo==ipo) { - - if(key->id.lib==NULL) { - key->ipo= ipon; - ipon->id.us++; - ipo->id.us--; + for (key= G.main->key.first; key; key= key->id.next) { + if (key->ipo == src) { + if (key->id.lib == NULL) { + key->ipo= dst; + dst->id.us++; + src->id.us--; } } - key= key->id.next; } } } -void make_local_ipo(Ipo *ipo) +/* generic call to make IPO's local */ +void make_local_ipo (Ipo *ipo) { - - if(ipo->id.lib==NULL) return; - if(ipo->id.us==1) { + /* can't touch lib-linked data */ + if (ipo->id.lib == NULL) + return; + + /* with only one user, just set local flag */ + if (ipo->id.us == 1) { ipo->id.lib= 0; ipo->id.flag= LIB_LOCAL; new_id(0, (ID *)ipo, 0); return; } - if(ipo->blocktype==ID_OB) make_local_obipo(ipo); - else if(ipo->blocktype==ID_MA) make_local_matipo(ipo); - else if(ipo->blocktype==ID_KE) make_local_keyipo(ipo); - + /* when more than 1 user, can only make local for certain blocktypes */ + switch (ipo->blocktype) { + case ID_OB: + make_local_obipo(ipo); + break; + case ID_MA: + make_local_matipo(ipo); + break; + case ID_KE: + make_local_keyipo(ipo); + break; + } } -IpoCurve *find_ipocurve(Ipo *ipo, int adrcode) +/* ***************************** Keyframe Column Tools ********************************* */ + +/* add a BezTriple to a column */ +void add_to_cfra_elem(ListBase *lb, BezTriple *bezt) { - if(ipo) { - IpoCurve *icu; - for(icu= ipo->curve.first; icu; icu= icu->next) { - if(icu->adrcode==adrcode) return icu; + CfraElem *ce, *cen; + + for (ce= lb->first; ce; ce= ce->next) { + /* double key? */ + if (ce->cfra == bezt->vec[1][0]) { + if (bezt->f2 & SELECT) ce->sel= bezt->f2; + return; } + /* should key be inserted before this column? */ + else if (ce->cfra > bezt->vec[1][0]) break; } - return NULL; + + /* create a new column */ + cen= MEM_callocN(sizeof(CfraElem), "add_to_cfra_elem"); + if (ce) BLI_insertlinkbefore(lb, ce, cen); + else BLI_addtail(lb, cen); + + cen->cfra= bezt->vec[1][0]; + cen->sel= bezt->f2; } -void calchandles_ipocurve(IpoCurve *icu) +/* make a list of keyframe 'columns' in an IPO block */ +void make_cfra_list (Ipo *ipo, ListBase *elems) { - BezTriple *bezt, *prev, *next; + IpoCurve *icu; + BezTriple *bezt; int a; + + for (icu= ipo->curve.first; icu; icu= icu->next) { + if (icu->flag & IPO_VISIBLE) { + /* ... removed old checks for adrcode types from here ... + * - (was this used for IpoKeys in the past?) + */ + + bezt= icu->bezt; + if (bezt) { + for (a=0; a < icu->totvert; a++, bezt++) { + add_to_cfra_elem(elems, bezt); + } + } + } + } +} + +/* ***************************** Timing Stuff ********************************* */ - a= icu->totvert; +/* This (evil) function is needed to cope with two legacy Blender rendering features + * mblur (motion blur that renders 'subframes' and blurs them together), and fields + * rendering. Thus, the use of ugly globals from object.c + */ +// BAD... EVIL... JUJU...!!!! +float frame_to_float (int cfra) /* see also bsystem_time in object.c */ +{ + extern float bluroffs; /* bad stuff borrowed from object.c */ + extern float fieldoffs; + float ctime; - /* IPO_CONST doesn't have handles */ - if(a<2 || icu->ipo==IPO_CONST) return; + ctime= (float)cfra; + ctime+= bluroffs+fieldoffs; + ctime*= G.scene->r.framelen; - bezt= icu->bezt; - prev= 0; - next= bezt+1; + return ctime; +} - while(a--) { +/* ***************************** IPO Curve Sanity ********************************* */ +/* The functions here are used in various parts of Blender, usually after some editing + * of keyframe data has occurred. They ensure that keyframe data is properly ordered and + * that the handles are correctly + */ - if(bezt->vec[0][0]>bezt->vec[1][0]) bezt->vec[0][0]= bezt->vec[1][0]; - if(bezt->vec[2][0]<bezt->vec[1][0]) bezt->vec[2][0]= bezt->vec[1][0]; +/* This function recalculates the handles of an IPO-Curve + * If the BezTriples have been rearranged, sort them first before using this. + */ +void calchandles_ipocurve (IpoCurve *icu) +{ + BezTriple *bezt, *prev, *next; + int a= icu->totvert; - if(icu->flag & IPO_AUTO_HORIZ) + /* Error checking: + * - need at least two points + * - need bezier keys + * - only bezier-interpolation has handles (for now) + */ + if (ELEM(NULL, icu, icu->bezt) || (a < 2) || ELEM(icu->ipo, IPO_CONST, IPO_LIN)) + return; + + /* get initial pointers */ + bezt= icu->bezt; + prev= NULL; + next= (bezt + 1); + + /* loop over all beztriples, adjusting handles */ + while (a--) { + /* clamp timing of handles to be on either side of beztriple */ + if (bezt->vec[0][0] > bezt->vec[1][0]) bezt->vec[0][0]= bezt->vec[1][0]; + if (bezt->vec[2][0] < bezt->vec[1][0]) bezt->vec[2][0]= bezt->vec[1][0]; + + /* calculate autohandles */ + if (icu->flag & IPO_AUTO_HORIZ) calchandleNurb(bezt, prev, next, 2); /* 2==special autohandle && keep extrema horizontal */ else calchandleNurb(bezt, prev, next, 1); /* 1==special autohandle */ - - prev= bezt; - if(a==1) { - next= 0; - } - else next++; - + /* for automatic ease in and out */ - if(bezt->h1==HD_AUTO && bezt->h2==HD_AUTO) { - if(a==0 || a==icu->totvert-1) { - if(icu->extrap==IPO_HORIZ) { + if ((bezt->h1==HD_AUTO) && (bezt->h2==HD_AUTO)) { + /* only do this on first or last beztriple */ + if ((a==0) || (a==icu->totvert-1)) { + /* set both handles to have same horizontal value as keyframe */ + if (icu->extrap==IPO_HORIZ) { bezt->vec[0][1]= bezt->vec[2][1]= bezt->vec[1][1]; } } } + /* advance pointers for next iteration */ + prev= bezt; + if (a == 1) next= NULL; + else next++; bezt++; } } -void testhandles_ipocurve(IpoCurve *icu) +/* Use when IPO-Curve with handles has changed + * It treats all BezTriples with the following rules: + * - PHASE 1: do types have to be altered? + * -> Auto handles: become aligned when selection status is NOT(000 || 111) + * -> Vector handles: become 'nothing' when (one half selected AND other not) + * - PHASE 2: recalculate handles +*/ +void testhandles_ipocurve (IpoCurve *icu) { - /* use when something has changed with handles. - it treats all BezTriples with the following rules: - PHASE 1: do types have to be altered? - Auto handles: become aligned when selection status is NOT(000 || 111) - Vector handles: become 'nothing' when (one half selected AND other not) - PHASE 2: recalculate handles - */ - BezTriple *bezt; - int flag, a; + BezTriple *bezt; + int a; - bezt= icu->bezt; - if(bezt==NULL) return; + /* only beztriples have handles (bpoints don't though) */ + if (ELEM(NULL, icu, icu->bezt)) + return; - a= icu->totvert; - while(a--) { - flag= 0; - if(bezt->f1 & SELECT) flag++; - if(bezt->f2 & SELECT) flag += 2; - if(bezt->f3 & SELECT) flag += 4; - - if( !(flag==0 || flag==7) ) { - if(bezt->h1==HD_AUTO) { /* auto */ + /* loop over beztriples */ + for (a=0, bezt=icu->bezt; a < icu->totvert; a++, bezt++) { + short flag= 0; + + /* flag is initialised as selection status + * of beztriple control-points (labelled 0,1,2) + */ + if (bezt->f1 & SELECT) flag |= (1<<0); // == 1 + if (bezt->f2 & SELECT) flag |= (1<<1); // == 2 + if (bezt->f3 & SELECT) flag |= (1<<2); // == 4 + + /* one or two handles selected only */ + if (ELEM(flag, 0, 7)==0) { + /* auto handles become aligned */ + if (bezt->h1==HD_AUTO) bezt->h1= HD_ALIGN; - } - if(bezt->h2==HD_AUTO) { /* auto */ + if(bezt->h2==HD_AUTO) bezt->h2= HD_ALIGN; + + /* vector handles become 'free' when only one half selected */ + if(bezt->h1==HD_VECT) { + /* only left half (1 or 2 or 1+2) */ + if (flag < 4) + bezt->h1= 0; } - - if(bezt->h1==HD_VECT) { /* vector */ - if(flag < 4) bezt->h1= 0; - } - if(bezt->h2==HD_VECT) { /* vector */ - if( flag > 3) bezt->h2= 0; + if(bezt->h2==HD_VECT) { + /* only right half (4 or 2+4) */ + if (flag > 3) + bezt->h2= 0; } } - bezt++; } + /* recalculate handles */ calchandles_ipocurve(icu); } - +/* This function sorts BezTriples so that they are arranged in chronological order, + * as tools working on IPO-Curves expect that the BezTriples are in order. + */ void sort_time_ipocurve(IpoCurve *icu) { - BezTriple *bezt; - int a, ok= 1; + short ok= 1; - while(ok) { + /* keep adjusting order of beztriples until nothing moves (bubble-sort) */ + while (ok) { ok= 0; - - if(icu->bezt) { - bezt= icu->bezt; - a= icu->totvert; - while(a--) { - if(a>0) { - if( bezt->vec[1][0] > (bezt+1)->vec[1][0]) { + + /* currently, will only be needed when there are beztriples */ + if (icu->bezt) { + BezTriple *bezt; + int a; + + /* loop over ALL points to adjust position in array and recalculate handles */ + for (a=0, bezt=icu->bezt; a < icu->totvert; a++, bezt++) { + /* check if thee's a next beztriple which we could try to swap with current */ + if (a < (icu->totvert-1)) { + /* swap if one is after the other (and indicate that order has changed) */ + if (bezt->vec[1][0] > (bezt+1)->vec[1][0]) { SWAP(BezTriple, *bezt, *(bezt+1)); ok= 1; } + + /* if either one of both of the points exceeds crosses over the keyframe time... */ + if ( (bezt->vec[0][0] > bezt->vec[1][0]) && (bezt->vec[2][0] < bezt->vec[1][0]) ) { + /* swap handles if they have switched sides for some reason */ + SWAP(float, bezt->vec[0][0], bezt->vec[2][0]); + SWAP(float, bezt->vec[0][1], bezt->vec[2][1]); + } + else { + /* clamp handles */ + if (bezt->vec[0][0] > bezt->vec[1][0]) + bezt->vec[0][0]= bezt->vec[1][0]; + if (bezt->vec[2][0] < bezt->vec[1][0]) + bezt->vec[2][0]= bezt->vec[1][0]; + } } - if(bezt->vec[0][0]>bezt->vec[1][0] && bezt->vec[2][0]<bezt->vec[1][0]) { - SWAP(float, bezt->vec[0][0], bezt->vec[2][0]); - SWAP(float, bezt->vec[0][1], bezt->vec[2][1]); - } - else { - if(bezt->vec[0][0]>bezt->vec[1][0]) bezt->vec[0][0]= bezt->vec[1][0]; - if(bezt->vec[2][0]<bezt->vec[1][0]) bezt->vec[2][0]= bezt->vec[1][0]; - } - bezt++; } } - else { - - } } } -int test_time_ipocurve(IpoCurve *icu) +/* This function tests if any BezTriples are out of order, thus requiring a sort */ +int test_time_ipocurve (IpoCurve *icu) { - BezTriple *bezt; int a; - if(icu->bezt) { - bezt= icu->bezt; - a= icu->totvert-1; - while(a--) { - if( bezt->vec[1][0] > (bezt+1)->vec[1][0]) { - return 1; - } - bezt++; - } - } - else { + /* currently, only need to test beztriples */ + if (icu->bezt) { + BezTriple *bezt; + /* loop through all beztriples, stopping when one exceeds the one after it */ + for (a=0, bezt= icu->bezt; a < (icu->totvert - 1); a++, bezt++) { + if (bezt->vec[1][0] > (bezt+1)->vec[1][0]) + return 1; + } } - + + /* none need any swapping */ return 0; } -void correct_bezpart(float *v1, float *v2, float *v3, float *v4) +/* --------- */ + +/* The total length of the handles is not allowed to be more + * than the horizontal distance between (v1-v4). + * This is to prevent curve loops. +*/ +void correct_bezpart (float *v1, float *v2, float *v3, float *v4) { - /* the total length of the handles is not allowed to be more - * than the horizontal distance between (v1-v4) - * this to prevent curve loops - */ float h1[2], h2[2], len1, len2, len, fac; + /* calculate handle deltas */ h1[0]= v1[0]-v2[0]; h1[1]= v1[1]-v2[1]; h2[0]= v4[0]-v3[0]; h2[1]= v4[1]-v3[1]; + /* calculate distances: + * - len = span of time between keyframes + * - len1 = length of handle of start key + * - len2 = length of handle of end key + */ len= v4[0]- v1[0]; len1= (float)fabs(h1[0]); len2= (float)fabs(h2[0]); - if(len1+len2==0.0) return; - if(len1+len2 > len) { + /* if the handles have no length, no need to do any corrections */ + if ((len1+len2) == 0.0) + return; + + /* the two handles cross over each other, so force them + * apart using the proportion they overlap + */ + if ((len1+len2) > len) { fac= len/(len1+len2); v2[0]= (v1[0]-fac*h1[0]); @@ -635,57 +800,86 @@ void correct_bezpart(float *v1, float *v2, float *v3, float *v4) v3[0]= (v4[0]-fac*h2[0]); v3[1]= (v4[1]-fac*h2[1]); - } } -/* *********************** ARITH *********************** */ +#if 0 // TODO: enable when we have per-segment interpolation +/* This function sets the interpolation mode for an entire Ipo-Curve. + * It is primarily used for patching old files, but is also used in the interface + * to make sure that all segments of the curve use the same interpolation. + */ +void set_interpolation_ipocurve (IpoCurve *icu, short ipo) +{ + BezTriple *bezt; + int a; + + /* validate arguments */ + if (icu == NULL) return; + if (ELEM3(ipo, IPO_CONST, IPO_LIN, IPO_BEZ)==0) return; -int findzero(float x, float q0, float q1, float q2, float q3, float *o) + /* set interpolation mode for whole curve */ + icu->ipo= ipo; + + /* set interpolation mode of all beztriples */ + for (a=0, bezt=icu->bezt; a<icu->totvert; a++, bezt++) + bezt->ipo= ipo; +} +#endif // TODO: enable when we have per-segment interpolation + +/* ***************************** Curve Calculations ********************************* */ + +/* find root/zero */ +int findzero (float x, float q0, float q1, float q2, float q3, float *o) { double c0, c1, c2, c3, a, b, c, p, q, d, t, phi; int nr= 0; - c0= q0-x; - c1= 3*(q1-q0); - c2= 3*(q0-2*q1+q2); - c3= q3-q0+3*(q1-q2); + c0= q0 - x; + c1= 3 * (q1 - q0); + c2= 3 * (q0 - 2*q1 + q2); + c3= q3 - q0 + 3 * (q1 - q2); - if(c3!=0.0) { + if (c3 != 0.0) { a= c2/c3; b= c1/c3; c= c0/c3; a= a/3; - - p= b/3-a*a; - q= (2*a*a*a-a*b+c)/2; - d= q*q+p*p*p; - - if(d>0.0) { + + p= b/3 - a*a; + q= (2*a*a*a - a*b + c) / 2; + d= q*q + p*p*p; + + if (d > 0.0) { t= sqrt(d); - o[0]= (float)(Sqrt3d(-q+t)+Sqrt3d(-q-t)-a); - if(o[0]>= SMALL && o[0]<=1.000001) return 1; + o[0]= (float)(Sqrt3d(-q+t) + Sqrt3d(-q-t) - a); + + if ((o[0] >= SMALL) && (o[0] <= 1.000001)) return 1; else return 0; } - else if(d==0.0) { + else if (d == 0.0) { t= Sqrt3d(-q); - o[0]= (float)(2*t-a); - if(o[0]>=SMALL && o[0]<=1.000001) nr++; + o[0]= (float)(2*t - a); + + if ((o[0] >= SMALL) && (o[0] <= 1.000001)) nr++; o[nr]= (float)(-t-a); - if(o[nr]>=SMALL && o[nr]<=1.000001) return nr+1; + + if ((o[nr] >= SMALL) && (o[nr] <= 1.000001)) return nr+1; else return nr; } else { - phi= acos(-q/sqrt(-(p*p*p))); + phi= acos(-q / sqrt(-(p*p*p))); t= sqrt(-p); p= cos(phi/3); - q= sqrt(3-3*p*p); - o[0]= (float)(2*t*p-a); - if(o[0]>=SMALL && o[0]<=1.000001) nr++; - o[nr]= (float)(-t*(p+q)-a); - if(o[nr]>=SMALL && o[nr]<=1.000001) nr++; - o[nr]= (float)(-t*(p-q)-a); - if(o[nr]>=SMALL && o[nr]<=1.000001) return nr+1; + q= sqrt(3 - 3*p*p); + o[0]= (float)(2*t*p - a); + + if ((o[0] >= SMALL) && (o[0] <= 1.000001)) nr++; + o[nr]= (float)(-t * (p + q) - a); + + if ((o[nr] >= SMALL) && (o[nr] <= 1.000001)) nr++; + o[nr]= (float)(-t * (p - q) - a); + + if ((o[nr] >= SMALL) && (o[nr] <= 1.000001)) return nr+1; else return nr; } } @@ -694,83 +888,97 @@ int findzero(float x, float q0, float q1, float q2, float q3, float *o) b=c1; c=c0; - if(a!=0.0) { - p=b*b-4*a*c; - if(p>0) { + if (a != 0.0) { + // discriminant + p= b*b - 4*a*c; + + if (p > 0) { p= sqrt(p); - o[0]= (float)((-b-p)/(2*a)); - if(o[0]>=SMALL && o[0]<=1.000001) nr++; + o[0]= (float)((-b-p) / (2 * a)); + + if ((o[0] >= SMALL) && (o[0] <= 1.000001)) nr++; o[nr]= (float)((-b+p)/(2*a)); - if(o[nr]>=SMALL && o[nr]<=1.000001) return nr+1; + + if ((o[nr] >= SMALL) && (o[nr] <= 1.000001)) return nr+1; else return nr; } - else if(p==0) { - o[0]= (float)(-b/(2*a)); - if(o[0]>=SMALL && o[0]<=1.000001) return 1; + else if (p == 0) { + o[0]= (float)(-b / (2 * a)); + if ((o[0] >= SMALL) && (o[0] <= 1.000001)) return 1; else return 0; } } - else if(b!=0.0) { + else if (b != 0.0) { o[0]= (float)(-c/b); - if(o[0]>=SMALL && o[0]<=1.000001) return 1; + + if ((o[0] >= SMALL) && (o[0] <= 1.000001)) return 1; else return 0; } - else if(c==0.0) { + else if (c == 0.0) { o[0]= 0.0; return 1; } + return 0; } } -void berekeny(float f1, float f2, float f3, float f4, float *o, int b) +void berekeny (float f1, float f2, float f3, float f4, float *o, int b) { float t, c0, c1, c2, c3; int a; c0= f1; - c1= 3.0f*(f2 - f1); - c2= 3.0f*(f1 - 2.0f*f2 + f3); - c3= f4 - f1 + 3.0f*(f2-f3); + c1= 3.0f * (f2 - f1); + c2= 3.0f * (f1 - 2.0f*f2 + f3); + c3= f4 - f1 + 3.0f * (f2 - f3); - for(a=0; a<b; a++) { + for (a=0; a < b; a++) { t= o[a]; - o[a]= c0+t*c1+t*t*c2+t*t*t*c3; + o[a]= c0 + t*c1 + t*t*c2 + t*t*t*c3; } } -void berekenx(float *f, float *o, int b) +void berekenx (float *f, float *o, int b) { float t, c0, c1, c2, c3; int a; c0= f[0]; - c1= 3*(f[3]-f[0]); - c2= 3*(f[0]-2*f[3]+f[6]); - c3= f[9]-f[0]+3*(f[3]-f[6]); - for(a=0; a<b; a++) { + c1= 3 * (f[3] - f[0]); + c2= 3 * (f[0] - 2*f[3] + f[6]); + c3= f[9] - f[0] + 3 * (f[3] - f[6]); + + for (a=0; a < b; a++) { t= o[a]; - o[a]= c0+t*c1+t*t*c2+t*t*t*c3; + o[a]= c0 + t*c1 + t*t*c2 + t*t*t*c3; } } -/* we need the local transform = current transform - (parent transform + bone transform) */ -/* (local transform is on action channel level) */ -static void posechannel_get_local_transform(bPoseChannel *pchan, float *loc, float *eul, float *size) +/* ***************************** IPO - Calculations ********************************* */ + +/* ---------------------- Curve Evaluation --------------------------- */ + +/* helper function for evaluating drivers: + * - we need the local transform = current transform - (parent transform + bone transform) + * - (local transform is on action channel level) + */ +static void posechannel_get_local_transform (bPoseChannel *pchan, float loc[], float eul[], float size[]) { - float diff_mat[4][4]; float parmat[4][4], offs_bone[4][4], imat[4][4]; + float diff_mat[4][4]; + /* get first the parent + bone transform in parmat */ if (pchan->parent) { - /* get first the parent + bone transform in parmat */ - /* bone transform itself */ Mat4CpyMat3(offs_bone, pchan->bone->bone_mat); + /* The bone's root offset (is in the parent's coordinate system) */ VECCOPY(offs_bone[3], pchan->bone->head); + /* Get the length translation of parent (length along y axis) */ offs_bone[3][1]+= pchan->parent->bone->length; - + Mat4MulSerie(parmat, pchan->parent->pose_mat, offs_bone, NULL, NULL, NULL, NULL, NULL, NULL); /* invert it */ @@ -779,64 +987,83 @@ static void posechannel_get_local_transform(bPoseChannel *pchan, float *loc, flo else { Mat4CpyMat3(offs_bone, pchan->bone->bone_mat); VECCOPY(offs_bone[3], pchan->bone->head); - + /* invert it */ Mat4Invert(imat, offs_bone); - } /* difference: current transform - (parent transform + bone transform) */ Mat4MulMat4(diff_mat, pchan->pose_mat, imat); - if(loc) + /* extract relevant components */ + if (loc) VECCOPY(loc, diff_mat[3]); - if(eul) + if (eul) Mat4ToEul(diff_mat, eul); - if(size) + if (size) Mat4ToSize(diff_mat, size); - } -/* has to return a float value */ -static float eval_driver(IpoDriver *driver, float ipotime) +/* evaluate an IPO-driver to get a 'time' value to use instead of "ipotime" + * - "ipotime" is the frame at which IPO-curve is being evaluated + * - has to return a float value + */ +static float eval_driver (IpoDriver *driver, float ipotime) { - - if(driver->type == IPO_DRIVER_TYPE_PYTHON) { +#ifndef DISABLE_PYTHON + /* currently, drivers are either PyDrivers (evaluating a PyExpression, or Object/Pose-Channel transforms) */ + if (driver->type == IPO_DRIVER_TYPE_PYTHON) { /* check for empty or invalid expression */ - if ((driver->name[0] == '\0') || - (driver->flag & IPO_DRIVER_FLAG_INVALID)) + if ( (driver->name[0] == '\0') || + (driver->flag & IPO_DRIVER_FLAG_INVALID) ) + { return 0.0f; - /* this evals the expression and returns its result: - * (on errors it reports, then returns 0.0f) */ + } + + /* this evaluates the expression using Python,and returns its result: + * - on errors it reports, then returns 0.0f + */ return BPY_pydriver_eval(driver); } - else { - Object *ob= driver->ob; + else +#endif /* DISABLE_PYTHON */ + { - if(ob==NULL) return 0.0f; - if(ob->proxy_from) + Object *ob= driver->ob; + + /* must have an object to evaluate */ + if (ob == NULL) + return 0.0f; + + /* if a proxy, use the proxy source*/ + if (ob->proxy_from) ob= ob->proxy_from; - if(driver->blocktype==ID_OB) { - /* depsgraph failure; ob ipos are calculated in where_is_object, this might get called too late */ - if(ob->ipo && ob->ctime!=ipotime) { + /* use given object as driver */ + if (driver->blocktype == ID_OB) { + /* depsgraph failure: ob ipos are calculated in where_is_object, this might get called too late */ + if ((ob->ipo) && (ob->ctime != ipotime)) { + /* calculate the value of relevant channel on the Object, but do not write the value + * calculated on to the Object but onto "ipotime" instead + */ calc_ipo_spec(ob->ipo, driver->adrcode, &ipotime); return ipotime; } - switch(driver->adrcode) { + /* return the value of the relevant channel */ + switch (driver->adrcode) { case OB_LOC_X: return ob->loc[0]; case OB_LOC_Y: return ob->loc[1]; case OB_LOC_Z: return ob->loc[2]; - case OB_ROT_X: - return ob->rot[0]/(M_PI_2/9.0); - case OB_ROT_Y: - return ob->rot[1]/(M_PI_2/9.0); - case OB_ROT_Z: - return ob->rot[2]/(M_PI_2/9.0); + case OB_ROT_X: /* hack: euler rotations are divided by 10 deg to fit on same axes as other channels */ + return (float)( ob->rot[0]/(M_PI_2/9.0) ); + case OB_ROT_Y: /* hack: euler rotations are divided by 10 deg to fit on same axes as other channels */ + return (float)( ob->rot[1]/(M_PI_2/9.0) ); + case OB_ROT_Z: /* hack: euler rotations are divided by 10 deg to fit on same axes as other channels */ + return (float)( ob->rot[2]/(M_PI_2/9.0) ); case OB_SIZE_X: return ob->size[0]; case OB_SIZE_Y: @@ -845,14 +1072,20 @@ static float eval_driver(IpoDriver *driver, float ipotime) return ob->size[2]; } } + + /* use given pose-channel as driver */ else { /* ID_AR */ bPoseChannel *pchan= get_pose_channel(ob->pose, driver->name); - if(pchan && pchan->bone) { - - /* rotation difference is not a simple driver (i.e. value drives value), but the angle between 2 bones is driving stuff... which is useful */ - if(driver->adrcode==OB_ROT_DIFF) { + + /* must have at least 1 bone to use */ + if (pchan && pchan->bone) { + /* rotation difference is not a simple driver (i.e. value drives value), but the angle between 2 bones is driving stuff... + * - the name of the second pchan is also stored in driver->name, but packed after the other one by DRIVER_NAME_OFFS chars + */ + if (driver->adrcode == OB_ROT_DIFF) { bPoseChannel *pchan2= get_pose_channel(ob->pose, driver->name+DRIVER_NAME_OFFS); - if(pchan2 && pchan2->bone) { + + if (pchan2 && pchan2->bone) { float q1[4], q2[4], quat[4], angle; Mat4ToQuat(pchan->pose_mat, q1); @@ -863,27 +1096,33 @@ static float eval_driver(IpoDriver *driver, float ipotime) angle = 2.0f * (saacos(quat[0])); angle= ABS(angle); - return angle>M_PI?2.0f*M_PI-angle:angle; + return (angle > M_PI) ? (float)((2.0f * M_PI) - angle) : (float)(angle); } } + + /* standard driver */ else { float loc[3], eul[3], size[3]; + /* retrieve local transforms to return + * - we use eulers here NOT quats, so that Objects can be driven by bones easily + * also, this way is more understandable for users + */ posechannel_get_local_transform(pchan, loc, eul, size); - - switch(driver->adrcode) { + + switch (driver->adrcode) { case OB_LOC_X: return loc[0]; case OB_LOC_Y: return loc[1]; case OB_LOC_Z: return loc[2]; - case OB_ROT_X: - return eul[0]/(M_PI_2/9.0); - case OB_ROT_Y: - return eul[1]/(M_PI_2/9.0); - case OB_ROT_Z: - return eul[2]/(M_PI_2/9.0); + case OB_ROT_X: /* hack: euler rotations are divided by 10 deg to fit on same axes as other channels */ + return (float)( eul[0]/(M_PI_2/9.0) ); + case OB_ROT_Y: /* hack: euler rotations are divided by 10 deg to fit on same axes as other channels */ + return (float)( eul[1]/(M_PI_2/9.0) ); + case OB_ROT_Z: /* hack: euler rotations are divided by 10 deg to fit on same axes as other channels */ + return (float)( eul[2]/(M_PI_2/9.0) ); case OB_SIZE_X: return size[0]; case OB_SIZE_Y: @@ -895,227 +1134,722 @@ static float eval_driver(IpoDriver *driver, float ipotime) } } } + + /* return 0.0f, as couldn't find relevant data to use */ return 0.0f; } -float eval_icu(IpoCurve *icu, float ipotime) +/* evaluate and return the value of the given IPO-curve at the specified frame ("evaltime") */ +float eval_icu(IpoCurve *icu, float evaltime) { - BezTriple *bezt, *prevbezt; - float v1[2], v2[2], v3[2], v4[2], opl[32], dx, fac; - float cycdx, cycdy, ofs, cycyofs, cvalue = 0.0; - int a, b; + float cvalue = 0.0f; - cycyofs= 0.0; - - if(icu->driver) { + /* if there is a driver, evaluate it to find value to use as "evaltime" + * - this value will also be returned as the value of the 'curve', if there are no keyframes + */ + if (icu->driver) { /* ipotime now serves as input for the curve */ - ipotime= cvalue= eval_driver(icu->driver, ipotime); + evaltime= cvalue= eval_driver(icu->driver, evaltime); } - if(icu->bezt) { + + /* there are keyframes (in the form of BezTriples) which can be interpolated between */ + if (icu->bezt) { + /* get pointers */ + BezTriple *bezt, *prevbezt, *lastbezt; + float v1[2], v2[2], v3[2], v4[2], opl[32], dx, fac; + float cycdx, cycdy, ofs, cycyofs= 0.0; + int a, b; + + /* get pointers */ + a= icu->totvert-1; prevbezt= icu->bezt; bezt= prevbezt+1; - a= icu->totvert-1; + lastbezt= prevbezt + a; - /* cyclic? */ - if(icu->extrap & IPO_CYCL) { - ofs= icu->bezt->vec[1][0]; - cycdx= (icu->bezt+icu->totvert-1)->vec[1][0] - ofs; - cycdy= (icu->bezt+icu->totvert-1)->vec[1][1] - icu->bezt->vec[1][1]; - if(cycdx!=0.0) { - - if(icu->extrap & IPO_DIR) { - cycyofs= (float)floor((ipotime-ofs)/cycdx); - cycyofs*= cycdy; + /* extrapolation mode is 'cyclic' - find relative place within a cycle */ + if (icu->extrap & IPO_CYCL) { + /* ofs is start frame of cycle */ + ofs= prevbezt->vec[1][0]; + + /* calculate period and amplitude (total height) of a cycle */ + cycdx= lastbezt->vec[1][0] - prevbezt->vec[1][0]; + cycdy= lastbezt->vec[1][1] - prevbezt->vec[1][1]; + + /* cycle occurs over some period of time (cycdx should be positive all the time) */ + if (cycdx) { + /* check if 'cyclic extrapolation', and thus calculate y-offset for this cycle + * - IPO_CYCLX = (IPO_CYCL + IPO_DIR) + */ + if (icu->extrap & IPO_DIR) { + cycyofs = (float)floor((evaltime - ofs) / cycdx); + cycyofs *= cycdy; } - - ipotime= (float)(fmod(ipotime-ofs, cycdx)+ofs); - if(ipotime<ofs) ipotime+= cycdx; + + /* calculate where in the cycle we are (overwrite evaltime to reflect this) */ + evaltime= (float)(fmod(evaltime-ofs, cycdx) + ofs); + if (evaltime < ofs) evaltime += cycdx; } } - /* endpoints? */ - - if(prevbezt->vec[1][0]>=ipotime) { - if( (icu->extrap & IPO_DIR) && icu->ipo!=IPO_CONST) { - dx= prevbezt->vec[1][0]-ipotime; - fac= prevbezt->vec[1][0]-prevbezt->vec[0][0]; - if(fac!=0.0) { - fac= (prevbezt->vec[1][1]-prevbezt->vec[0][1])/fac; - cvalue= prevbezt->vec[1][1]-fac*dx; + /* evaluation time at or past endpoints? */ + // TODO: for per-bezt interpolation, replace all icu->ipo with (bezt)->ipo + if (prevbezt->vec[1][0] >= evaltime) { + /* before or on first keyframe */ + if ((icu->extrap & IPO_DIR) && (icu->ipo != IPO_CONST)) { + /* linear or bezier interpolation */ + if (icu->ipo==IPO_LIN) { + /* Use the next center point instead of our own handle for + * linear interpolated extrapolate + */ + if (icu->totvert == 1) + cvalue= prevbezt->vec[1][1]; + else { + bezt = prevbezt+1; + dx= prevbezt->vec[1][0] - evaltime; + fac= bezt->vec[1][0] - prevbezt->vec[1][0]; + + /* prevent division by zero */ + if (fac) { + fac= (bezt->vec[1][1] - prevbezt->vec[1][1]) / fac; + cvalue= prevbezt->vec[1][1] - (fac * dx); + } + else + cvalue= prevbezt->vec[1][1]; + } + } + else { + /* Use the first handle (earlier) of first BezTriple to calculate the + * gradient and thus the value of the curve at evaltime + */ + dx= prevbezt->vec[1][0] - evaltime; + fac= prevbezt->vec[1][0] - prevbezt->vec[0][0]; + + /* prevent division by zero */ + if (fac) { + fac= (prevbezt->vec[1][1] - prevbezt->vec[0][1]) / fac; + cvalue= prevbezt->vec[1][1] - (fac * dx); + } + else + cvalue= prevbezt->vec[1][1]; } - else cvalue= prevbezt->vec[1][1]; } - else cvalue= prevbezt->vec[1][1]; - - cvalue+= cycyofs; + else { + /* constant (IPO_HORIZ) extrapolation or constant interpolation, + * so just extend first keyframe's value + */ + cvalue= prevbezt->vec[1][1]; + } } - else if( (prevbezt+a)->vec[1][0]<=ipotime) { - if( (icu->extrap & IPO_DIR) && icu->ipo!=IPO_CONST) { - prevbezt+= a; - dx= ipotime-prevbezt->vec[1][0]; - fac= prevbezt->vec[2][0]-prevbezt->vec[1][0]; - - if(fac!=0) { - fac= (prevbezt->vec[2][1]-prevbezt->vec[1][1])/fac; - cvalue= prevbezt->vec[1][1]+fac*dx; + else if (lastbezt->vec[1][0] <= evaltime) { + /* after or on last keyframe */ + if( (icu->extrap & IPO_DIR) && (icu->ipo != IPO_CONST)) { + /* linear or bezier interpolation */ + if (icu->ipo==IPO_LIN) { + /* Use the next center point instead of our own handle for + * linear interpolated extrapolate + */ + if (icu->totvert == 1) + cvalue= lastbezt->vec[1][1]; + else { + prevbezt = lastbezt - 1; + dx= evaltime - lastbezt->vec[1][0]; + fac= lastbezt->vec[1][0] - prevbezt->vec[1][0]; + + /* prevent division by zero */ + if (fac) { + fac= (lastbezt->vec[1][1] - prevbezt->vec[1][1]) / fac; + cvalue= lastbezt->vec[1][1] + (fac * dx); + } + else + cvalue= lastbezt->vec[1][1]; + } + } + else { + /* Use the gradient of the second handle (later) of last BezTriple to calculate the + * gradient and thus the value of the curve at evaltime + */ + dx= evaltime - lastbezt->vec[1][0]; + fac= lastbezt->vec[2][0] - lastbezt->vec[1][0]; + + /* prevent division by zero */ + if (fac) { + fac= (lastbezt->vec[2][1] - lastbezt->vec[1][1]) / fac; + cvalue= lastbezt->vec[1][1] + (fac * dx); + } + else + cvalue= lastbezt->vec[1][1]; } - else cvalue= prevbezt->vec[1][1]; } - else cvalue= (prevbezt+a)->vec[1][1]; - - cvalue+= cycyofs; + else { + /* constant (IPO_HORIZ) extrapolation or constant interpolation, + * so just extend last keyframe's value + */ + cvalue= lastbezt->vec[1][1]; + } } else { - while(a--) { - if(prevbezt->vec[1][0]<=ipotime && bezt->vec[1][0]>=ipotime) { - if(icu->ipo==IPO_CONST) { - cvalue= prevbezt->vec[1][1]+cycyofs; + /* evaltime occurs somewhere in the middle of the curve */ + // TODO: chould be optimised by using a binary search instead??? + for (a=0; prevbezt && bezt && (a < icu->totvert-1); a++, prevbezt=bezt, bezt++) { + /* evaltime occurs within the interval defined by these two keyframes */ + if ((prevbezt->vec[1][0] <= evaltime) && (bezt->vec[1][0] >= evaltime)) { + /* value depends on interpolation mode */ + if (icu->ipo == IPO_CONST) { + /* constant (evaltime not relevant, so no interpolation needed) */ + cvalue= prevbezt->vec[1][1]; } - else if(icu->ipo==IPO_LIN) { - fac= bezt->vec[1][0]-prevbezt->vec[1][0]; - if(fac==0) cvalue= cycyofs+prevbezt->vec[1][1]; - else { - fac= (ipotime-prevbezt->vec[1][0])/fac; - cvalue= cycyofs+prevbezt->vec[1][1]+ fac*(bezt->vec[1][1]-prevbezt->vec[1][1]); + else if (icu->ipo == IPO_LIN) { + /* linear - interpolate between values of the two keyframes */ + fac= bezt->vec[1][0] - prevbezt->vec[1][0]; + + /* prevent division by zero */ + if (fac) { + fac= (evaltime - prevbezt->vec[1][0]) / fac; + cvalue= prevbezt->vec[1][1] + (fac * (bezt->vec[1][1] - prevbezt->vec[1][1])); } + else + cvalue= prevbezt->vec[1][1]; } else { + /* bezier interpolation */ + /* v1,v2 are the first keyframe and its 2nd handle */ v1[0]= prevbezt->vec[1][0]; v1[1]= prevbezt->vec[1][1]; v2[0]= prevbezt->vec[2][0]; v2[1]= prevbezt->vec[2][1]; - + /* v3,v4 are the last keyframe's 1st handle + the last keyframe */ v3[0]= bezt->vec[0][0]; v3[1]= bezt->vec[0][1]; v4[0]= bezt->vec[1][0]; v4[1]= bezt->vec[1][1]; - + + /* adjust handles so that they don't overlap (forming a loop) */ correct_bezpart(v1, v2, v3, v4); - b= findzero(ipotime, v1[0], v2[0], v3[0], v4[0], opl); - if(b) { + /* try to get a value for this position - if failure, try another set of points */ + b= findzero(evaltime, v1[0], v2[0], v3[0], v4[0], opl); + if (b) { berekeny(v1[1], v2[1], v3[1], v4[1], opl, 1); - cvalue= opl[0]+cycyofs; + cvalue= opl[0]; break; } } } - prevbezt= bezt; - bezt++; } } + + /* apply y-offset (for 'cyclic extrapolation') to calculated value */ + cvalue+= cycyofs; } - - if(icu->ymin < icu->ymax) { - if(cvalue < icu->ymin) cvalue= icu->ymin; - else if(cvalue > icu->ymax) cvalue= icu->ymax; + + /* clamp evaluated value to lie within allowable value range for this channel */ + if (icu->ymin < icu->ymax) { + CLAMP(cvalue, icu->ymin, icu->ymax); } + /* return evaluated value */ return cvalue; } -void calc_icu(IpoCurve *icu, float ctime) +/* ------------------- IPO-Block/Curve Calculation - General API ----------------------- */ + +/* calculate the value of the given IPO-curve at the current frame, and set its curval */ +void calc_icu (IpoCurve *icu, float ctime) { + /* calculate and set curval (evaluates driver too) */ icu->curval= eval_icu(icu, ctime); } -float calc_ipo_time(Ipo *ipo, float ctime) +/* calculate for the current frame, all IPO-curves in IPO-block that can be evaluated + * - icu->curval is set for all IPO-curves which are evaluated! + */ +void calc_ipo (Ipo *ipo, float ctime) { + IpoCurve *icu; + + /* if there is no IPO block to evaluate, or whole block is "muted" */ + if (ipo == NULL) return; + if (ipo->muteipo) return; + + /* loop over all curves */ + for (icu= ipo->curve.first; icu; icu= icu->next) { + /* only evaluated curve if allowed to: + * - Muted channels should not be evaluated as they shouldn't have any effect + * --> user explictly turned them off! + * - Drivers should be evaluated at all updates + * --> TODO Note: drivers should be separated from standard channels + * - IPO_LOCK is not set, as it is set by some internal mechanisms to prevent + * IPO-curve from overwriting data (currently only used for IPO-Record). + */ + if ((icu->driver) || (icu->flag & IPO_LOCK)==0) { + if ((icu->flag & IPO_MUTE)==0) + calc_icu(icu, ctime); + } + } +} - if(ipo && ipo->blocktype==ID_OB) { - IpoCurve *icu= ipo->curve.first; +/* ------------------- IPO-Block/Curve Calculation - Special Hacks ----------------------- */ - while(icu) { - if (icu->adrcode==OB_TIME) { - calc_icu(icu, ctime); - return 10.0f*icu->curval; - } - icu= icu->next; - } +/* Calculate and return the value of the 'Time' Ipo-Curve from an Object, + * OR return the current time if not found + * - used in object.c -> bsystem_time() + */ +float calc_ipo_time (Ipo *ipo, float ctime) +{ + /* only Time IPO from Object IPO-blocks are relevant */ + if ((ipo) && (ipo->blocktype == ID_OB)) { + IpoCurve *icu= find_ipocurve(ipo, OB_TIME); + + /* only calculate (and set icu->curval) for time curve */ + if (icu) { + calc_icu(icu, ctime); + return (10.0f * icu->curval); + } } + /* no appropriate time-curve found */ return ctime; } -void calc_ipo(Ipo *ipo, float ctime) +/* Evaluate the specified channel in the given IPO block on the specified frame (ctime), + * writing the value into that channel's icu->curval, but ALSO dumping it in ctime. + * - Returns success and modifies ctime! + */ +short calc_ipo_spec (Ipo *ipo, int adrcode, float *ctime) +{ + IpoCurve *icu= find_ipocurve(ipo, adrcode); + + /* only evaluate if found */ + if (icu) { + /* only calculate if allowed to (not locked and not muted) + * - drivers not taken into account, because this may be called when calculating a driver + */ + if ((icu->flag & (IPO_LOCK|IPO_MUTE))==0) + calc_icu(icu, *ctime); + + /* value resulting from calculations is written into ctime! */ + *ctime= icu->curval; + return 1; + } + + /* couldn't evaluate */ + return 0; +} + +/* ***************************** IPO - DataAPI ********************************* */ + +/* --------------------- Flush/Execute IPO Values ----------------------------- */ + +/* Flush IpoCurve->curvals to the data they affect (defined by ID) + * - not for Actions or Constraints! (those have their own special handling) + */ +void execute_ipo (ID *id, Ipo *ipo) { IpoCurve *icu; + void *poin; + int type; - if(ipo==NULL) return; - if(ipo->muteipo) return; + /* don't do anything without an IPO block */ + if (ipo == NULL) + return; - for(icu= ipo->curve.first; icu; icu= icu->next) { - if(icu->driver || (icu->flag & IPO_LOCK)==0) { - if((icu->flag & IPO_MUTE)==0) - calc_icu(icu, ctime); + /* loop over IPO Curves, getting pointer to var to affect, and write into that pointer */ + for (icu= ipo->curve.first; icu; icu= icu->next) { + poin= get_ipo_poin(id, icu, &type); + if (poin) write_ipo_poin(poin, type, icu->curval); + } +} + +/* Flush Action-Channel IPO data to Pose Channel */ +void execute_action_ipo (bActionChannel *achan, bPoseChannel *pchan) +{ + /* only do this if there's an Action Channel and Pose Channel to use */ + if (achan && achan->ipo && pchan) { + IpoCurve *icu; + + /* loop over IPO-curves, getting a pointer to pchan var to write to + * - assume for now that only 'float' channels will ever get written into + */ + for (icu= achan->ipo->curve.first; icu; icu= icu->next) { + void *poin= get_pchan_ipo_poin(pchan, icu->adrcode); + if (poin) write_ipo_poin(poin, IPO_FLOAT, icu->curval); } } } -/* ************************************** */ -/* DO THE IPO! */ -/* ************************************** */ -void write_ipo_poin(void *poin, int type, float val) +/* --------------------- Force Calculation + Flush IPO Values ----------------------------- */ + +/* Calculate values for given IPO block, then flush to all of block's users + * - for general usage + */ +void do_ipo (Ipo *ipo) +{ + if (ipo) { + float ctime= frame_to_float(G.scene->r.cfra); + + /* calculate values, then flush to all users of this IPO block */ + calc_ipo(ipo, ctime); + do_ipo_nocalc(ipo); + } +} + +/* Calculate values for given Material's IPO block, then flush to given Material only */ +void do_mat_ipo (Material *ma) { + float ctime; + + if (ELEM(NULL, ma, ma->ipo)) + return; + + ctime= frame_to_float(G.scene->r.cfra); + /* if(ob->ipoflag & OB_OFFS_OB) ctime-= ob->sf; */ + + /* calculate values for current time, then flush values to given material only */ + calc_ipo(ma->ipo, ctime); + execute_ipo((ID *)ma, ma->ipo); +} - switch(type) { - case IPO_FLOAT: - *( (float *)poin)= val; - break; - case IPO_FLOAT_DEGR: - *( (float *)poin)= (float)(val*M_PI_2/9.0); - break; - case IPO_INT: - case IPO_INT_BIT: - case IPO_LONG: - *( (int *)poin)= (int)val; - break; - case IPO_SHORT: - case IPO_SHORT_BIT: - *( (short *)poin)= (short)val; - break; - case IPO_CHAR: - case IPO_CHAR_BIT: - *( (char *)poin)= (char)val; - break; +/* Calculate values for given Object's IPO block, then flush to given Object only + * - there's also some funky stuff that looks like it's for scene layers + */ +void do_ob_ipo (Object *ob) +{ + float ctime; + unsigned int lay; + + if (ob->ipo == NULL) + return; + + /* do not set ob->ctime here: for example when parent in invisible layer */ + ctime= bsystem_time(ob, (float) G.scene->r.cfra, 0.0); + + /* calculate values of */ + calc_ipo(ob->ipo, ctime); + + /* Patch: remember localview */ + lay= ob->lay & 0xFF000000; + + /* flush IPO values to this object only */ + execute_ipo((ID *)ob, ob->ipo); + + /* hack: for layer animation??? - is this what this is? (Aligorith, 28Sep2008) */ + ob->lay |= lay; + if ((ob->id.name[2]=='S') && (ob->id.name[3]=='C') && (ob->id.name[4]=='E')) { + if (strcmp(G.scene->id.name+2, ob->id.name+6)==0) { + G.scene->lay= ob->lay; + //XXX copy_view3d_lock(0); + /* no redraw here! creates too many calls */ + } } } -float read_ipo_poin(void *poin, int type) +/* Only execute those IPO-Curves with drivers, on the current frame, for the given Object + * - TODO: Drivers should really be made separate from standard anim channels + */ +void do_ob_ipodrivers (Object *ob, Ipo *ipo, float ctime) { - float val = 0.0; + IpoCurve *icu; + void *poin; + int type; - switch(type) { - case IPO_FLOAT: - val= *( (float *)poin); + for (icu= ipo->curve.first; icu; icu= icu->next) { + if (icu->driver) { + icu->curval= eval_icu(icu, ctime); + + poin= get_ipo_poin((ID *)ob, icu, &type); + if (poin) write_ipo_poin(poin, type, icu->curval); + } + } +} + +/* Special variation to calculate IPO values for Sequence + perform other stuff */ +void do_seq_ipo (Sequence *seq, int cfra) +{ + float ctime, div; + + /* seq_ipo has an exception: calc both fields immediately */ + if (seq->ipo) { + if ((seq->flag & SEQ_IPO_FRAME_LOCKED) != 0) { + ctime = frame_to_float(cfra); + div = 1.0; + } + else { + ctime= frame_to_float(cfra - seq->startdisp); + div= (seq->enddisp - seq->startdisp) / 100.0f; + if (div == 0.0) return; + } + + /* 2nd field */ + calc_ipo(seq->ipo, (ctime+0.5f)/div); + execute_ipo((ID *)seq, seq->ipo); + seq->facf1= seq->facf0; + + /* 1st field */ + calc_ipo(seq->ipo, ctime/div); + execute_ipo((ID *)seq, seq->ipo); + } + else + seq->facf1= seq->facf0= 1.0f; +} + +/* --------- */ + + +/* exception: it does calc for objects... + * now find out why this routine was used anyway! + */ +void do_ipo_nocalc (Ipo *ipo) +{ + Object *ob; + Material *ma; + Tex *tex; + World *wo; + Lamp *la; + Camera *ca; + bSound *snd; + + if (ipo == NULL) + return; + + /* only flush IPO values (without calculating first/again) on + * to the datablocks that use the given IPO block + */ + switch (ipo->blocktype) { + case ID_OB: + for (ob= G.main->object.first; ob; ob= ob->id.next) { + if (ob->ipo == ipo) do_ob_ipo(ob); + } break; - case IPO_FLOAT_DEGR: - val= *( (float *)poin); - val = (float)(val/(M_PI_2/9.0)); + case ID_MA: + for (ma= G.main->mat.first; ma; ma= ma->id.next) { + if (ma->ipo == ipo) execute_ipo((ID *)ma, ipo); + } break; - case IPO_INT: - case IPO_INT_BIT: - case IPO_LONG: - val= (float)(*( (int *)poin)); + case ID_TE: + for (tex= G.main->tex.first; tex; tex= tex->id.next) { + if (tex->ipo == ipo) execute_ipo((ID *)tex, ipo); + } break; - case IPO_SHORT: - case IPO_SHORT_BIT: - val= *( (short *)poin); + case ID_WO: + for (wo= G.main->world.first; wo; wo= wo->id.next) { + if (wo->ipo == ipo) execute_ipo((ID *)wo, ipo); + } break; - case IPO_CHAR: - case IPO_CHAR_BIT: - val= *( (char *)poin); + case ID_LA: + for (la= G.main->lamp.first; la; la= la->id.next) { + if (la->ipo == ipo) execute_ipo((ID *)la, ipo); + } + break; + case ID_CA: + for (ca= G.main->camera.first; ca; ca= ca->id.next) { + if (ca->ipo == ipo) execute_ipo((ID *)ca, ipo); + } + break; + case ID_SO: + for (snd= G.main->sound.first; snd; snd= snd->id.next) { + if (snd->ipo == ipo) execute_ipo((ID *)snd, ipo); + } break; } - return val; } -static void *give_tex_poin(Tex *tex, int adrcode, int *type ) +/* Executes IPO's for whole database on frame change, in a specified order, + * with datablocks being calculated in alphabetical order + * - called on scene_update_for_newframe() only + */ +void do_all_data_ipos () { - void *poin=0; + Material *ma; + Tex *tex; + World *wo; + Ipo *ipo; + Lamp *la; + Key *key; + Camera *ca; + bSound *snd; + Sequence *seq; + Editing *ed; + Base *base; + float ctime; + + ctime= frame_to_float(G.scene->r.cfra); + + /* this exception cannot be depgraphed yet... what todo with objects in other layers?... */ + for (base= G.scene->base.first; base; base= base->next) { + Object *ob= base->object; + + /* only update layer when an ipo */ + if (has_ipo_code(ob->ipo, OB_LAY)) { + do_ob_ipo(ob); + base->lay= ob->lay; + } + } + + /* layers for the set...*/ + if (G.scene->set) { + for (base= G.scene->set->base.first; base; base= base->next) { + Object *ob= base->object; + + if (has_ipo_code(ob->ipo, OB_LAY)) { + do_ob_ipo(ob); + base->lay= ob->lay; + } + } + } + + /* Calculate all IPO blocks in use, execept those for Objects */ + for (ipo= G.main->ipo.first; ipo; ipo= ipo->id.next) { + if ((ipo->id.us) && (ipo->blocktype != ID_OB)) { + calc_ipo(ipo, ctime); + } + } - switch(adrcode) { + /* Texture Blocks */ + for (tex= G.main->tex.first; tex; tex= tex->id.next) { + if (tex->ipo) execute_ipo((ID *)tex, tex->ipo); + } + + /* Material Blocks */ + for (ma= G.main->mat.first; ma; ma= ma->id.next) { + if (ma->ipo) execute_ipo((ID *)ma, ma->ipo); + } + + /* World Blocks */ + for (wo= G.main->world.first; wo; wo= wo->id.next) { + if (wo->ipo) execute_ipo((ID *)wo, wo->ipo); + } + + /* ShapeKey Blocks */ + for (key= G.main->key.first; key; key= key->id.next) { + if (key->ipo) execute_ipo((ID *)key, key->ipo); + } + + /* Lamp Blocks */ + for (la= G.main->lamp.first; la; la= la->id.next) { + if (la->ipo) execute_ipo((ID *)la, la->ipo); + } + + /* Camera Blocks */ + for (ca= G.main->camera.first; ca; ca= ca->id.next) { + if (ca->ipo) execute_ipo((ID *)ca, ca->ipo); + } + + /* Sound Blocks (Old + Unused) */ + for (snd= G.main->sound.first; snd; snd= snd->id.next) { + if (snd->ipo) execute_ipo((ID *)snd, snd->ipo); + } + + /* Sequencer: process FAC Ipos used as volume envelopes */ + ed= G.scene->ed; + if (ed) { + for (seq= ed->seqbasep->first; seq; seq= seq->next) { + if ( ((seq->type == SEQ_RAM_SOUND) || (seq->type == SEQ_HD_SOUND)) && + (seq->startdisp <= G.scene->r.cfra+2) && + (seq->enddisp>G.scene->r.cfra) && + (seq->ipo) ) + { + do_seq_ipo(seq, G.scene->r.cfra); + } + } + } +} + + +/* --------------------- Assorted ----------------------------- */ + +/* clear delta-transforms on all Objects which use the given IPO block */ +void clear_delta_obipo(Ipo *ipo) +{ + Object *ob; + + /* only search if there's an IPO */ + if (ipo == NULL) + return; + + /* search through all objects in database */ + for (ob= G.main->object.first; ob; ob= ob->id.next) { + /* can only update if not a library */ + if (ob->id.lib == NULL) { + if (ob->ipo == ipo) { + memset(&ob->dloc, 0, 12); + memset(&ob->drot, 0, 12); + memset(&ob->dsize, 0, 12); + } + } + } +} + +/* ***************************** IPO - DataAPI ********************************* */ + +// !!!!!!!!!!!!!!!!!!!!!!!!!!!! FIXME - BAD CRUFT WARNING !!!!!!!!!!!!!!!!!!!!!!! + +/* These functions here should be replaced eventually by the Data API, as this is + * inflexible duplication... + */ + +/* --------------------- Get Pointer API ----------------------------- */ + +/* get pointer to pose-channel's channel, but set appropriate flags first */ +void *get_pchan_ipo_poin (bPoseChannel *pchan, int adrcode) +{ + void *poin= NULL; + + switch (adrcode) { + case AC_QUAT_W: + poin= &(pchan->quat[0]); + pchan->flag |= POSE_ROT; + break; + case AC_QUAT_X: + poin= &(pchan->quat[1]); + pchan->flag |= POSE_ROT; + break; + case AC_QUAT_Y: + poin= &(pchan->quat[2]); + pchan->flag |= POSE_ROT; + break; + case AC_QUAT_Z: + poin= &(pchan->quat[3]); + pchan->flag |= POSE_ROT; + break; + + case AC_LOC_X: + poin= &(pchan->loc[0]); + pchan->flag |= POSE_LOC; + break; + case AC_LOC_Y: + poin= &(pchan->loc[1]); + pchan->flag |= POSE_LOC; + break; + case AC_LOC_Z: + poin= &(pchan->loc[2]); + pchan->flag |= POSE_LOC; + break; + + case AC_SIZE_X: + poin= &(pchan->size[0]); + pchan->flag |= POSE_SIZE; + break; + case AC_SIZE_Y: + poin= &(pchan->size[1]); + pchan->flag |= POSE_SIZE; + break; + case AC_SIZE_Z: + poin= &(pchan->size[2]); + pchan->flag |= POSE_SIZE; + break; + } + + /* return pointer */ + return poin; +} + +/* get texture channel */ +static void *give_tex_poin (Tex *tex, int adrcode, int *type ) +{ + void *poin= NULL; + + switch (adrcode) { case TE_NSIZE: poin= &(tex->noisesize); break; case TE_TURB: @@ -1168,17 +1902,18 @@ static void *give_tex_poin(Tex *tex, int adrcode, int *type ) poin= &(tex->bright); break; case TE_CONTRA: poin= &(tex->contrast); break; - } + /* return pointer */ return poin; } -void *give_mtex_poin(MTex *mtex, int adrcode ) +/* get texture-slot/mapping channel */ +void *give_mtex_poin (MTex *mtex, int adrcode) { - void *poin=0; - - switch(adrcode) { + void *poin= NULL; + + switch (adrcode) { case MAP_OFS_X: poin= &(mtex->ofs[0]); break; case MAP_OFS_Y: @@ -1209,6 +1944,7 @@ void *give_mtex_poin(MTex *mtex, int adrcode ) poin= &(mtex->dispfac); break; } + /* return pointer */ return poin; } @@ -1225,1280 +1961,923 @@ void *give_mtex_poin(MTex *mtex, int adrcode ) /* from misc_util: flip the bytes from x */ /* #define GS(x) (((unsigned char *)(x))[0] << 8 | ((unsigned char *)(x))[1]) */ -void *get_ipo_poin(ID *id, IpoCurve *icu, int *type) + +/* general function to get pointer to source/destination data */ +void *get_ipo_poin (ID *id, IpoCurve *icu, int *type) { void *poin= NULL; - Object *ob; - Material *ma; - MTex *mtex; - Tex *tex; - Lamp *la; - Sequence *seq; - World *wo; - ParticleSettings *part; + MTex *mtex= NULL; + /* most channels will have float data, but those with other types will override this */ *type= IPO_FLOAT; - if( GS(id->name)==ID_OB) { - - ob= (Object *)id; - - switch(icu->adrcode) { - case OB_LOC_X: - poin= &(ob->loc[0]); break; - case OB_LOC_Y: - poin= &(ob->loc[1]); break; - case OB_LOC_Z: - poin= &(ob->loc[2]); break; - case OB_DLOC_X: - poin= &(ob->dloc[0]); break; - case OB_DLOC_Y: - poin= &(ob->dloc[1]); break; - case OB_DLOC_Z: - poin= &(ob->dloc[2]); break; - - case OB_ROT_X: - poin= &(ob->rot[0]); *type= IPO_FLOAT_DEGR; break; - case OB_ROT_Y: - poin= &(ob->rot[1]); *type= IPO_FLOAT_DEGR; break; - case OB_ROT_Z: - poin= &(ob->rot[2]); *type= IPO_FLOAT_DEGR; break; - case OB_DROT_X: - poin= &(ob->drot[0]); *type= IPO_FLOAT_DEGR; break; - case OB_DROT_Y: - poin= &(ob->drot[1]); *type= IPO_FLOAT_DEGR; break; - case OB_DROT_Z: - poin= &(ob->drot[2]); *type= IPO_FLOAT_DEGR; break; + /* data is divided into 'blocktypes' based on ID-codes */ + switch (GS(id->name)) { + case ID_OB: /* object channels ----------------------------- */ + { + Object *ob= (Object *)id; - case OB_SIZE_X: - poin= &(ob->size[0]); break; - case OB_SIZE_Y: - poin= &(ob->size[1]); break; - case OB_SIZE_Z: - poin= &(ob->size[2]); break; - case OB_DSIZE_X: - poin= &(ob->dsize[0]); break; - case OB_DSIZE_Y: - poin= &(ob->dsize[1]); break; - case OB_DSIZE_Z: - poin= &(ob->dsize[2]); break; - - case OB_LAY: - poin= &(ob->lay); *type= IPO_INT_BIT; break; + switch (icu->adrcode) { + case OB_LOC_X: + poin= &(ob->loc[0]); break; + case OB_LOC_Y: + poin= &(ob->loc[1]); break; + case OB_LOC_Z: + poin= &(ob->loc[2]); break; + case OB_DLOC_X: + poin= &(ob->dloc[0]); break; + case OB_DLOC_Y: + poin= &(ob->dloc[1]); break; + case OB_DLOC_Z: + poin= &(ob->dloc[2]); break; - case OB_COL_R: - poin= &(ob->col[0]); - break; - case OB_COL_G: - poin= &(ob->col[1]); - break; - case OB_COL_B: - poin= &(ob->col[2]); - break; - case OB_COL_A: - poin= &(ob->col[3]); - break; - case OB_PD_FSTR: - if(ob->pd) poin= &(ob->pd->f_strength); - break; - case OB_PD_FFALL: - if(ob->pd) poin= &(ob->pd->f_power); - break; - case OB_PD_SDAMP: - if(ob->pd) poin= &(ob->pd->pdef_damp); - break; - case OB_PD_RDAMP: - if(ob->pd) poin= &(ob->pd->pdef_rdamp); - break; - case OB_PD_PERM: - if(ob->pd) poin= &(ob->pd->pdef_perm); - break; - case OB_PD_FMAXD: - if(ob->pd) poin= &(ob->pd->maxdist); - break; - } - } - else if( GS(id->name)==ID_MA) { - - ma= (Material *)id; - - switch(icu->adrcode) { - case MA_COL_R: - poin= &(ma->r); break; - case MA_COL_G: - poin= &(ma->g); break; - case MA_COL_B: - poin= &(ma->b); break; - case MA_SPEC_R: - poin= &(ma->specr); break; - case MA_SPEC_G: - poin= &(ma->specg); break; - case MA_SPEC_B: - poin= &(ma->specb); break; - case MA_MIR_R: - poin= &(ma->mirr); break; - case MA_MIR_G: - poin= &(ma->mirg); break; - case MA_MIR_B: - poin= &(ma->mirb); break; - case MA_REF: - poin= &(ma->ref); break; - case MA_ALPHA: - poin= &(ma->alpha); break; - case MA_EMIT: - poin= &(ma->emit); break; - case MA_AMB: - poin= &(ma->amb); break; - case MA_SPEC: - poin= &(ma->spec); break; - case MA_HARD: - poin= &(ma->har); *type= IPO_SHORT; break; - case MA_SPTR: - poin= &(ma->spectra); break; - case MA_IOR: - poin= &(ma->ang); break; - case MA_MODE: - poin= &(ma->mode); *type= IPO_INT_BIT; break; - case MA_HASIZE: - poin= &(ma->hasize); break; - case MA_TRANSLU: - poin= &(ma->translucency); break; - case MA_RAYM: - poin= &(ma->ray_mirror); break; - case MA_FRESMIR: - poin= &(ma->fresnel_mir); break; - case MA_FRESMIRI: - poin= &(ma->fresnel_mir_i); break; - case MA_FRESTRA: - poin= &(ma->fresnel_tra); break; - case MA_FRESTRAI: - poin= &(ma->fresnel_tra_i); break; - case MA_ADD: - poin= &(ma->add); break; - } - - if(poin==NULL) { - mtex= 0; - if(icu->adrcode & MA_MAP1) mtex= ma->mtex[0]; - else if(icu->adrcode & MA_MAP2) mtex= ma->mtex[1]; - else if(icu->adrcode & MA_MAP3) mtex= ma->mtex[2]; - else if(icu->adrcode & MA_MAP4) mtex= ma->mtex[3]; - else if(icu->adrcode & MA_MAP5) mtex= ma->mtex[4]; - else if(icu->adrcode & MA_MAP6) mtex= ma->mtex[5]; - else if(icu->adrcode & MA_MAP7) mtex= ma->mtex[6]; - else if(icu->adrcode & MA_MAP8) mtex= ma->mtex[7]; - else if(icu->adrcode & MA_MAP9) mtex= ma->mtex[8]; - else if(icu->adrcode & MA_MAP10) mtex= ma->mtex[9]; + case OB_ROT_X: + poin= &(ob->rot[0]); *type= IPO_FLOAT_DEGR; break; + case OB_ROT_Y: + poin= &(ob->rot[1]); *type= IPO_FLOAT_DEGR; break; + case OB_ROT_Z: + poin= &(ob->rot[2]); *type= IPO_FLOAT_DEGR; break; + case OB_DROT_X: + poin= &(ob->drot[0]); *type= IPO_FLOAT_DEGR; break; + case OB_DROT_Y: + poin= &(ob->drot[1]); *type= IPO_FLOAT_DEGR; break; + case OB_DROT_Z: + poin= &(ob->drot[2]); *type= IPO_FLOAT_DEGR; break; + + case OB_SIZE_X: + poin= &(ob->size[0]); break; + case OB_SIZE_Y: + poin= &(ob->size[1]); break; + case OB_SIZE_Z: + poin= &(ob->size[2]); break; + case OB_DSIZE_X: + poin= &(ob->dsize[0]); break; + case OB_DSIZE_Y: + poin= &(ob->dsize[1]); break; + case OB_DSIZE_Z: + poin= &(ob->dsize[2]); break; - if(mtex) { - poin= give_mtex_poin(mtex, icu->adrcode & (MA_MAP1-1) ); - } - } - } - else if( GS(id->name)==ID_TE) { - tex= (Tex *)id; - - if(tex) poin= give_tex_poin(tex, icu->adrcode, type); - } - else if( GS(id->name)==ID_SEQ) { - seq= (Sequence *)id; - - switch(icu->adrcode) { - case SEQ_FAC1: - poin= &(seq->facf0); break; - } - } - else if( GS(id->name)==ID_CU) { - - poin= &(icu->curval); - - } - else if( GS(id->name)==ID_KE) { - KeyBlock *kb= ((Key *)id)->block.first; - - for(; kb; kb= kb->next) - if(kb->adrcode==icu->adrcode) + case OB_LAY: + poin= &(ob->lay); *type= IPO_INT_BIT; break; + + case OB_COL_R: + poin= &(ob->col[0]); break; + case OB_COL_G: + poin= &(ob->col[1]); break; + case OB_COL_B: + poin= &(ob->col[2]); break; + case OB_COL_A: + poin= &(ob->col[3]); break; + + case OB_PD_FSTR: + if (ob->pd) poin= &(ob->pd->f_strength); + break; + case OB_PD_FFALL: + if (ob->pd) poin= &(ob->pd->f_power); + break; + case OB_PD_SDAMP: + if (ob->pd) poin= &(ob->pd->pdef_damp); + break; + case OB_PD_RDAMP: + if (ob->pd) poin= &(ob->pd->pdef_rdamp); + break; + case OB_PD_PERM: + if (ob->pd) poin= &(ob->pd->pdef_perm); + break; + case OB_PD_FMAXD: + if (ob->pd) poin= &(ob->pd->maxdist); break; - if(kb) - poin= &(kb->curval); - - } - else if(GS(id->name)==ID_WO) { - - wo= (World *)id; - - switch(icu->adrcode) { - case WO_HOR_R: - poin= &(wo->horr); break; - case WO_HOR_G: - poin= &(wo->horg); break; - case WO_HOR_B: - poin= &(wo->horb); break; - case WO_ZEN_R: - poin= &(wo->zenr); break; - case WO_ZEN_G: - poin= &(wo->zeng); break; - case WO_ZEN_B: - poin= &(wo->zenb); break; - - case WO_EXPOS: - poin= &(wo->exposure); break; - - case WO_MISI: - poin= &(wo->misi); break; - case WO_MISTDI: - poin= &(wo->mistdist); break; - case WO_MISTSTA: - poin= &(wo->miststa); break; - case WO_MISTHI: - poin= &(wo->misthi); break; - - case WO_STAR_R: - poin= &(wo->starr); break; - case WO_STAR_G: - poin= &(wo->starg); break; - case WO_STAR_B: - poin= &(wo->starb); break; - - case WO_STARDIST: - poin= &(wo->stardist); break; - case WO_STARSIZE: - poin= &(wo->starsize); break; - } - - if(poin==NULL) { - mtex= 0; - if(icu->adrcode & MA_MAP1) mtex= wo->mtex[0]; - else if(icu->adrcode & MA_MAP2) mtex= wo->mtex[1]; - else if(icu->adrcode & MA_MAP3) mtex= wo->mtex[2]; - else if(icu->adrcode & MA_MAP4) mtex= wo->mtex[3]; - else if(icu->adrcode & MA_MAP5) mtex= wo->mtex[4]; - else if(icu->adrcode & MA_MAP6) mtex= wo->mtex[5]; - else if(icu->adrcode & MA_MAP7) mtex= wo->mtex[6]; - else if(icu->adrcode & MA_MAP8) mtex= wo->mtex[7]; - else if(icu->adrcode & MA_MAP9) mtex= wo->mtex[8]; - else if(icu->adrcode & MA_MAP10) mtex= wo->mtex[9]; - - if(mtex) { - poin= give_mtex_poin(mtex, icu->adrcode & (MA_MAP1-1) ); - } - } - } - else if( GS(id->name)==ID_LA) { - - la= (Lamp *)id; - - switch(icu->adrcode) { - case LA_ENERGY: - poin= &(la->energy); break; - case LA_COL_R: - poin= &(la->r); break; - case LA_COL_G: - poin= &(la->g); break; - case LA_COL_B: - poin= &(la->b); break; - case LA_DIST: - poin= &(la->dist); break; - case LA_SPOTSI: - poin= &(la->spotsize); break; - case LA_SPOTBL: - poin= &(la->spotblend); break; - case LA_QUAD1: - poin= &(la->att1); break; - case LA_QUAD2: - poin= &(la->att2); break; - case LA_HALOINT: - poin= &(la->haint); break; - } - - if(poin==NULL) { - mtex= 0; - if(icu->adrcode & MA_MAP1) mtex= la->mtex[0]; - else if(icu->adrcode & MA_MAP2) mtex= la->mtex[1]; - else if(icu->adrcode & MA_MAP3) mtex= la->mtex[2]; - else if(icu->adrcode & MA_MAP4) mtex= la->mtex[3]; - else if(icu->adrcode & MA_MAP5) mtex= la->mtex[4]; - else if(icu->adrcode & MA_MAP6) mtex= la->mtex[5]; - else if(icu->adrcode & MA_MAP7) mtex= la->mtex[6]; - else if(icu->adrcode & MA_MAP8) mtex= la->mtex[7]; - else if(icu->adrcode & MA_MAP9) mtex= la->mtex[8]; - else if(icu->adrcode & MA_MAP10) mtex= la->mtex[9]; - - if(mtex) { - poin= give_mtex_poin(mtex, icu->adrcode & (MA_MAP1-1) ); } } - } - else if(GS(id->name)==ID_CA) { - Camera *ca= (Camera *)id; - - /* yafray: aperture & focal distance params */ - switch(icu->adrcode) { - case CAM_LENS: - if(ca->type==CAM_ORTHO) - poin= &(ca->ortho_scale); - else - poin= &(ca->lens); break; - case CAM_STA: - poin= &(ca->clipsta); break; - case CAM_END: - poin= &(ca->clipend); break; - case CAM_YF_APERT: - poin= &(ca->YF_aperture); break; - case CAM_YF_FDIST: - poin= &(ca->YF_dofdist); break; - case CAM_SHIFT_X: - poin= &(ca->shiftx); break; - case CAM_SHIFT_Y: - poin= &(ca->shifty); break; - } - } - else if(GS(id->name)==ID_SO) { - bSound *snd= (bSound *)id; - - switch(icu->adrcode) { - case SND_VOLUME: - poin= &(snd->volume); break; - case SND_PITCH: - poin= &(snd->pitch); break; - case SND_PANNING: - poin= &(snd->panning); break; - case SND_ATTEN: - poin= &(snd->attenuation); break; - } - } - else if( GS(id->name)==ID_PA) { - - part= (ParticleSettings *)id; - - switch(icu->adrcode) { - case PART_EMIT_FREQ: - case PART_EMIT_LIFE: - case PART_EMIT_VEL: - case PART_EMIT_AVE: - case PART_EMIT_SIZE: - poin= NULL; break; - case PART_CLUMP: - poin= &(part->clumpfac); break; - case PART_AVE: - poin= &(part->avefac); break; - case PART_SIZE: - poin= &(part->size); break; - case PART_DRAG: - poin= &(part->dragfac); break; - case PART_BROWN: - poin= &(part->brownfac); break; - case PART_DAMP: - poin= &(part->dampfac); break; - case PART_LENGTH: - poin= &(part->length); break; - case PART_GRAV_X: - poin= &(part->acc[0]); break; - case PART_GRAV_Y: - poin= &(part->acc[1]); break; - case PART_GRAV_Z: - poin= &(part->acc[2]); break; - case PART_KINK_AMP: - poin= &(part->kink_amp); break; - case PART_KINK_FREQ: - poin= &(part->kink_freq); break; - case PART_KINK_SHAPE: - poin= &(part->kink_shape); break; - case PART_BB_TILT: - poin= &(part->bb_tilt); break; - } - } - - return poin; -} - -void set_icu_vars(IpoCurve *icu) -{ - /* defaults. 0.0 for y-extents makes these ignored */ - icu->ymin= icu->ymax= 0.0; - icu->ipo= IPO_BEZ; - - if(icu->blocktype==ID_OB) { - - if(icu->adrcode==OB_LAY) { - icu->ipo= IPO_CONST; - icu->vartype= IPO_BITS; - } - - } - else if(icu->blocktype==ID_MA) { - - if(icu->adrcode < MA_MAP1) { - switch(icu->adrcode) { - case MA_HASIZE: - icu->ymax= 10000.0; break; - case MA_HARD: - icu->ymax= 511.0; break; + case ID_MA: /* material channels ----------------------------- */ + { + Material *ma= (Material *)id; + + switch (icu->adrcode) { + case MA_COL_R: + poin= &(ma->r); break; + case MA_COL_G: + poin= &(ma->g); break; + case MA_COL_B: + poin= &(ma->b); break; + case MA_SPEC_R: + poin= &(ma->specr); break; + case MA_SPEC_G: + poin= &(ma->specg); break; + case MA_SPEC_B: + poin= &(ma->specb); break; + case MA_MIR_R: + poin= &(ma->mirr); break; + case MA_MIR_G: + poin= &(ma->mirg); break; + case MA_MIR_B: + poin= &(ma->mirb); break; + case MA_REF: + poin= &(ma->ref); break; + case MA_ALPHA: + poin= &(ma->alpha); break; + case MA_EMIT: + poin= &(ma->emit); break; + case MA_AMB: + poin= &(ma->amb); break; case MA_SPEC: - icu->ymax= 2.0; break; + poin= &(ma->spec); break; + case MA_HARD: + poin= &(ma->har); *type= IPO_SHORT; break; + case MA_SPTR: + poin= &(ma->spectra); break; + case MA_IOR: + poin= &(ma->ang); break; case MA_MODE: - icu->ipo= IPO_CONST; - icu->vartype= IPO_BITS; break; - case MA_RAYM: - icu->ymax= 1.0; break; + poin= &(ma->mode); *type= IPO_INT_BIT; break; // evil... dumping bitflags directly to user! + case MA_HASIZE: + poin= &(ma->hasize); break; case MA_TRANSLU: - icu->ymax= 1.0; break; - case MA_IOR: - icu->ymin= 1.0; - icu->ymax= 3.0; break; + poin= &(ma->translucency); break; + case MA_RAYM: + poin= &(ma->ray_mirror); break; case MA_FRESMIR: - icu->ymax= 5.0; break; + poin= &(ma->fresnel_mir); break; case MA_FRESMIRI: - icu->ymin= 1.0; - icu->ymax= 5.0; break; + poin= &(ma->fresnel_mir_i); break; case MA_FRESTRA: - icu->ymax= 5.0; break; + poin= &(ma->fresnel_tra); break; case MA_FRESTRAI: - icu->ymin= 1.0; - icu->ymax= 5.0; break; + poin= &(ma->fresnel_tra_i); break; case MA_ADD: - icu->ymax= 1.0; break; - default: - icu->ymax= 1.0; break; + poin= &(ma->add); break; + } + + if (poin == NULL) { + if (icu->adrcode & MA_MAP1) mtex= ma->mtex[0]; + else if (icu->adrcode & MA_MAP2) mtex= ma->mtex[1]; + else if (icu->adrcode & MA_MAP3) mtex= ma->mtex[2]; + else if (icu->adrcode & MA_MAP4) mtex= ma->mtex[3]; + else if (icu->adrcode & MA_MAP5) mtex= ma->mtex[4]; + else if (icu->adrcode & MA_MAP6) mtex= ma->mtex[5]; + else if (icu->adrcode & MA_MAP7) mtex= ma->mtex[6]; + else if (icu->adrcode & MA_MAP8) mtex= ma->mtex[7]; + else if (icu->adrcode & MA_MAP9) mtex= ma->mtex[8]; + else if (icu->adrcode & MA_MAP10) mtex= ma->mtex[9]; + else if (icu->adrcode & MA_MAP12) mtex= ma->mtex[11]; + else if (icu->adrcode & MA_MAP11) mtex= ma->mtex[10]; + else if (icu->adrcode & MA_MAP13) mtex= ma->mtex[12]; + else if (icu->adrcode & MA_MAP14) mtex= ma->mtex[13]; + else if (icu->adrcode & MA_MAP15) mtex= ma->mtex[14]; + else if (icu->adrcode & MA_MAP16) mtex= ma->mtex[15]; + else if (icu->adrcode & MA_MAP17) mtex= ma->mtex[16]; + else if (icu->adrcode & MA_MAP18) mtex= ma->mtex[17]; + + if (mtex) + poin= give_mtex_poin(mtex, (icu->adrcode & (MA_MAP1-1))); } } - else { - switch(icu->adrcode & (MA_MAP1-1)) { - case MAP_OFS_X: - case MAP_OFS_Y: - case MAP_OFS_Z: - case MAP_SIZE_X: - case MAP_SIZE_Y: - case MAP_SIZE_Z: - icu->ymax= 1000.0; - icu->ymin= -1000.0; + break; + case ID_TE: /* texture channels ----------------------------- */ + { + Tex *tex= (Tex *)id; - break; - case MAP_R: - case MAP_G: - case MAP_B: - case MAP_DVAR: - case MAP_COLF: - case MAP_VARF: - case MAP_DISP: - icu->ymax= 1.0; - break; - case MAP_NORF: - icu->ymax= 25.0; - break; + if (tex) + poin= give_tex_poin(tex, icu->adrcode, type); + } + break; + case ID_SEQ: /* sequence channels ----------------------------- */ + { + Sequence *seq= (Sequence *)id; + + switch (icu->adrcode) { + case SEQ_FAC1: + poin= &(seq->facf0); break; } } - } - else if(icu->blocktype==ID_TE) { - switch(icu->adrcode & (MA_MAP1-1)) { - case TE_NSIZE: - icu->ymin= 0.0001; - icu->ymax= 2.0; break; - case TE_NDEPTH: - icu->vartype= IPO_SHORT; - icu->ipo= IPO_CONST; - icu->ymax= 6.0; break; - case TE_NTYPE: - icu->vartype= IPO_SHORT; - icu->ipo= IPO_CONST; - icu->ymax= 1.0; break; - case TE_TURB: - icu->ymax= 200.0; break; - case TE_VNW1: - case TE_VNW2: - case TE_VNW3: - case TE_VNW4: - icu->ymax= 2.0; - icu->ymin= -2.0; break; - case TE_VNMEXP: - icu->ymax= 10.0; - icu->ymin= 0.01; break; - case TE_VN_DISTM: - icu->vartype= IPO_SHORT; - icu->ipo= IPO_CONST; - icu->ymax= 6.0; break; - case TE_VN_COLT: - icu->vartype= IPO_SHORT; - icu->ipo= IPO_CONST; - icu->ymax= 3.0; break; - case TE_ISCA: - icu->ymax= 10.0; - icu->ymin= 0.01; break; - case TE_DISTA: - icu->ymax= 10.0; break; - case TE_MG_TYP: - icu->vartype= IPO_SHORT; - icu->ipo= IPO_CONST; - icu->ymax= 6.0; break; - case TE_MGH: - icu->ymin= 0.0001; - icu->ymax= 2.0; break; - case TE_MG_LAC: - case TE_MG_OFF: - case TE_MG_GAIN: - icu->ymax= 6.0; break; - case TE_MG_OCT: - icu->ymax= 8.0; break; - case TE_N_BAS1: - case TE_N_BAS2: - icu->vartype= IPO_SHORT; - icu->ipo= IPO_CONST; - icu->ymax= 8.0; break; - case TE_COL_R: - icu->ymax= 0.0; break; - case TE_COL_G: - icu->ymax= 2.0; break; - case TE_COL_B: - icu->ymax= 2.0; break; - case TE_BRIGHT: - icu->ymax= 2.0; break; - case TE_CONTRA: - icu->ymax= 5.0; break; - + break; + case ID_CU: /* curve channels ----------------------------- */ + { + poin= &(icu->curval); } - } - else if(icu->blocktype==ID_SEQ) { - - icu->ymax= 1.0; - - } - else if(icu->blocktype==ID_CU) { - - icu->ymax= 1.0; - - } - else if(icu->blocktype==ID_WO) { - - if(icu->adrcode < MA_MAP1) { - switch(icu->adrcode) { + break; + case ID_KE: /* shapekey channels ----------------------------- */ + { + Key *key= (Key *)id; + KeyBlock *kb; + + for(kb= key->block.first; kb; kb= kb->next) { + if (kb->adrcode == icu->adrcode) + break; + } + + if (kb) + poin= &(kb->curval); + } + break; + case ID_WO: /* world channels ----------------------------- */ + { + World *wo= (World *)id; + + switch (icu->adrcode) { + case WO_HOR_R: + poin= &(wo->horr); break; + case WO_HOR_G: + poin= &(wo->horg); break; + case WO_HOR_B: + poin= &(wo->horb); break; + case WO_ZEN_R: + poin= &(wo->zenr); break; + case WO_ZEN_G: + poin= &(wo->zeng); break; + case WO_ZEN_B: + poin= &(wo->zenb); break; + case WO_EXPOS: - icu->ymax= 5.0; break; + poin= &(wo->exposure); break; + + case WO_MISI: + poin= &(wo->misi); break; case WO_MISTDI: + poin= &(wo->mistdist); break; case WO_MISTSTA: + poin= &(wo->miststa); break; case WO_MISTHI: + poin= &(wo->misthi); break; + + case WO_STAR_R: + poin= &(wo->starr); break; + case WO_STAR_G: + poin= &(wo->starg); break; + case WO_STAR_B: + poin= &(wo->starb); break; + case WO_STARDIST: + poin= &(wo->stardist); break; case WO_STARSIZE: - break; - - default: - icu->ymax= 1.0; - break; + poin= &(wo->starsize); break; } - } - else { - switch(icu->adrcode & (MA_MAP1-1)) { - case MAP_OFS_X: - case MAP_OFS_Y: - case MAP_OFS_Z: - case MAP_SIZE_X: - case MAP_SIZE_Y: - case MAP_SIZE_Z: - icu->ymax= 100.0; - icu->ymin= -100.0; - break; - case MAP_R: - case MAP_G: - case MAP_B: - case MAP_DVAR: - case MAP_COLF: - case MAP_NORF: - case MAP_VARF: - case MAP_DISP: - icu->ymax= 1.0; + if (poin == NULL) { + if (icu->adrcode & MA_MAP1) mtex= wo->mtex[0]; + else if (icu->adrcode & MA_MAP2) mtex= wo->mtex[1]; + else if (icu->adrcode & MA_MAP3) mtex= wo->mtex[2]; + else if (icu->adrcode & MA_MAP4) mtex= wo->mtex[3]; + else if (icu->adrcode & MA_MAP5) mtex= wo->mtex[4]; + else if (icu->adrcode & MA_MAP6) mtex= wo->mtex[5]; + else if (icu->adrcode & MA_MAP7) mtex= wo->mtex[6]; + else if (icu->adrcode & MA_MAP8) mtex= wo->mtex[7]; + else if (icu->adrcode & MA_MAP9) mtex= wo->mtex[8]; + else if (icu->adrcode & MA_MAP10) mtex= wo->mtex[9]; + else if (icu->adrcode & MA_MAP11) mtex= wo->mtex[10]; + else if (icu->adrcode & MA_MAP12) mtex= wo->mtex[11]; + else if (icu->adrcode & MA_MAP13) mtex= wo->mtex[12]; + else if (icu->adrcode & MA_MAP14) mtex= wo->mtex[13]; + else if (icu->adrcode & MA_MAP15) mtex= wo->mtex[14]; + else if (icu->adrcode & MA_MAP16) mtex= wo->mtex[15]; + else if (icu->adrcode & MA_MAP17) mtex= wo->mtex[16]; + else if (icu->adrcode & MA_MAP18) mtex= wo->mtex[17]; + + if (mtex) + poin= give_mtex_poin(mtex, (icu->adrcode & (MA_MAP1-1))); } } - } - else if(icu->blocktype==ID_LA) { - if(icu->adrcode < MA_MAP1) { - switch(icu->adrcode) { + break; + case ID_LA: /* lamp channels ----------------------------- */ + { + Lamp *la= (Lamp *)id; + + switch (icu->adrcode) { case LA_ENERGY: - case LA_DIST: - break; - + poin= &(la->energy); break; case LA_COL_R: + poin= &(la->r); break; case LA_COL_G: + poin= &(la->g); break; case LA_COL_B: + poin= &(la->b); break; + case LA_DIST: + poin= &(la->dist); break; + case LA_SPOTSI: + poin= &(la->spotsize); break; case LA_SPOTBL: + poin= &(la->spotblend); break; case LA_QUAD1: + poin= &(la->att1); break; case LA_QUAD2: - icu->ymax= 1.0; break; - case LA_SPOTSI: - icu->ymax= 180.0; break; + poin= &(la->att2); break; case LA_HALOINT: - icu->ymax= 5.0; break; + poin= &(la->haint); break; + } + + if (poin == NULL) { + if (icu->adrcode & MA_MAP1) mtex= la->mtex[0]; + else if (icu->adrcode & MA_MAP2) mtex= la->mtex[1]; + else if (icu->adrcode & MA_MAP3) mtex= la->mtex[2]; + else if (icu->adrcode & MA_MAP4) mtex= la->mtex[3]; + else if (icu->adrcode & MA_MAP5) mtex= la->mtex[4]; + else if (icu->adrcode & MA_MAP6) mtex= la->mtex[5]; + else if (icu->adrcode & MA_MAP7) mtex= la->mtex[6]; + else if (icu->adrcode & MA_MAP8) mtex= la->mtex[7]; + else if (icu->adrcode & MA_MAP9) mtex= la->mtex[8]; + else if (icu->adrcode & MA_MAP10) mtex= la->mtex[9]; + else if (icu->adrcode & MA_MAP11) mtex= la->mtex[10]; + else if (icu->adrcode & MA_MAP12) mtex= la->mtex[11]; + else if (icu->adrcode & MA_MAP13) mtex= la->mtex[12]; + else if (icu->adrcode & MA_MAP14) mtex= la->mtex[13]; + else if (icu->adrcode & MA_MAP15) mtex= la->mtex[14]; + else if (icu->adrcode & MA_MAP16) mtex= la->mtex[15]; + else if (icu->adrcode & MA_MAP17) mtex= la->mtex[16]; + else if (icu->adrcode & MA_MAP18) mtex= la->mtex[17]; + + if (mtex) + poin= give_mtex_poin(mtex, (icu->adrcode & (MA_MAP1-1))); } } - else { - switch(icu->adrcode & (MA_MAP1-1)) { - case MAP_OFS_X: - case MAP_OFS_Y: - case MAP_OFS_Z: - case MAP_SIZE_X: - case MAP_SIZE_Y: - case MAP_SIZE_Z: - icu->ymax= 100.0; - icu->ymin= -100.0; + break; + case ID_CA: /* camera channels ----------------------------- */ + { + Camera *ca= (Camera *)id; + + switch (icu->adrcode) { + case CAM_LENS: + if (ca->type == CAM_ORTHO) + poin= &(ca->ortho_scale); + else + poin= &(ca->lens); break; - case MAP_R: - case MAP_G: - case MAP_B: - case MAP_DVAR: - case MAP_COLF: - case MAP_NORF: - case MAP_VARF: - case MAP_DISP: - icu->ymax= 1.0; + case CAM_STA: + poin= &(ca->clipsta); break; + case CAM_END: + poin= &(ca->clipend); break; + + case CAM_YF_APERT: + poin= &(ca->YF_aperture); break; + case CAM_YF_FDIST: + poin= &(ca->YF_dofdist); break; + + case CAM_SHIFT_X: + poin= &(ca->shiftx); break; + case CAM_SHIFT_Y: + poin= &(ca->shifty); break; } } - } - else if(icu->blocktype==ID_CA) { - - /* yafray: aperture & focal distance params */ - switch(icu->adrcode) { - case CAM_LENS: - icu->ymin= 5.0; - icu->ymax= 1000.0; - break; - case CAM_STA: - icu->ymin= 0.001f; - break; - case CAM_END: - icu->ymin= 0.1f; - break; - case CAM_YF_APERT: - icu->ymin = 0.0; - icu->ymax = 2.0; - break; - case CAM_YF_FDIST: - icu->ymin = 0.0; - icu->ymax = 5000.0; break; + case ID_SO: /* sound channels ----------------------------- */ + { + bSound *snd= (bSound *)id; - case CAM_SHIFT_X: - case CAM_SHIFT_Y: - icu->ymin= -2.0f; - icu->ymax= 2.0f; - break; + switch (icu->adrcode) { + case SND_VOLUME: + poin= &(snd->volume); break; + case SND_PITCH: + poin= &(snd->pitch); break; + case SND_PANNING: + poin= &(snd->panning); break; + case SND_ATTEN: + poin= &(snd->attenuation); break; + } } - } - else if(icu->blocktype==ID_SO) { - - switch(icu->adrcode) { - case SND_VOLUME: - icu->ymin= 0.0; - icu->ymax= 1.0; - break; - case SND_PITCH: - icu->ymin= -12.0; - icu->ymin= 12.0; - break; - case SND_PANNING: - icu->ymin= 0.0; - icu->ymax= 1.0; - break; - case SND_ATTEN: - icu->ymin= 0.0; - icu->ymin= 1.0; break; + case ID_PA: /* particle channels ----------------------------- */ + { + ParticleSettings *part= (ParticleSettings *)id; + + switch (icu->adrcode) { + case PART_EMIT_FREQ: + case PART_EMIT_LIFE: + case PART_EMIT_VEL: + case PART_EMIT_AVE: + case PART_EMIT_SIZE: + poin= NULL; + break; + + case PART_CLUMP: + poin= &(part->clumpfac); break; + case PART_AVE: + poin= &(part->avefac); break; + case PART_SIZE: + poin= &(part->size); break; + case PART_DRAG: + poin= &(part->dragfac); break; + case PART_BROWN: + poin= &(part->brownfac); break; + case PART_DAMP: + poin= &(part->dampfac); break; + case PART_LENGTH: + poin= &(part->length); break; + case PART_GRAV_X: + poin= &(part->acc[0]); break; + case PART_GRAV_Y: + poin= &(part->acc[1]); break; + case PART_GRAV_Z: + poin= &(part->acc[2]); break; + case PART_KINK_AMP: + poin= &(part->kink_amp); break; + case PART_KINK_FREQ: + poin= &(part->kink_freq); break; + case PART_KINK_SHAPE: + poin= &(part->kink_shape); break; + case PART_BB_TILT: + poin= &(part->bb_tilt); break; + + case PART_PD_FSTR: + if (part->pd) poin= &(part->pd->f_strength); + break; + case PART_PD_FFALL: + if (part->pd) poin= &(part->pd->f_power); + break; + case PART_PD_FMAXD: + if (part->pd) poin= &(part->pd->maxdist); + break; + case PART_PD2_FSTR: + if (part->pd2) poin= &(part->pd2->f_strength); + break; + case PART_PD2_FFALL: + if (part->pd2) poin= &(part->pd2->f_power); + break; + case PART_PD2_FMAXD: + if (part->pd2) poin= &(part->pd2->maxdist); + break; + } } - } - else if(icu->blocktype==ID_PA){ - - switch(icu->adrcode) { - case PART_EMIT_LIFE: - case PART_SIZE: - case PART_KINK_FREQ: - case PART_EMIT_VEL: - case PART_EMIT_AVE: - case PART_EMIT_SIZE: - icu->ymin= 0.0; break; - case PART_CLUMP: - case PART_DRAG: - case PART_DAMP: - case PART_LENGTH: - icu->ymin= 0.0; - icu->ymax= 1.0; - break; - case PART_KINK_SHAPE: - icu->ymin= -0.999; - icu->ymax= 0.999; - } - } - else if(icu->blocktype==ID_CO) { - icu->ymin= 0.0; - icu->ymax= 1.0f; } - - /* by default, slider limits will be icu->ymin and icu->ymax */ - icu->slide_min= icu->ymin; - icu->slide_max= icu->ymax; -} -/* not for actions or constraints! */ -void execute_ipo(ID *id, Ipo *ipo) -{ - IpoCurve *icu; - void *poin; - int type; - - if(ipo==NULL) return; - - for(icu= ipo->curve.first; icu; icu= icu->next) { - poin= get_ipo_poin(id, icu, &type); - if(poin) write_ipo_poin(poin, type, icu->curval); - } + /* return pointer */ + return poin; } -void *get_pchan_ipo_poin(bPoseChannel *pchan, int adrcode) +/* --------------------- IPO-Curve Limits ----------------------------- */ + +/* set limits for IPO-curve + * Note: must be synced with UI and PyAPI + */ +void set_icu_vars (IpoCurve *icu) { - void *poin= NULL; + /* defaults. 0.0 for y-extents makes these ignored */ + icu->ymin= icu->ymax= 0.0; + icu->ipo= IPO_BEZ; - switch (adrcode) { - case AC_QUAT_W: - poin= &(pchan->quat[0]); - pchan->flag |= POSE_ROT; + switch (icu->blocktype) { + case ID_OB: /* object channels ----------------------------- */ + { + if (icu->adrcode == OB_LAY) { + icu->ipo= IPO_CONST; + icu->vartype= IPO_BITS; + } + } break; - case AC_QUAT_X: - poin= &(pchan->quat[1]); - pchan->flag |= POSE_ROT; + case ID_MA: /* material channels ----------------------------- */ + { + if (icu->adrcode < MA_MAP1) { + switch (icu->adrcode) { + case MA_HASIZE: + icu->ymax= 10000.0; break; + case MA_HARD: + icu->ymax= 511.0; break; + case MA_SPEC: + icu->ymax= 2.0; break; + case MA_MODE: + icu->ipo= IPO_CONST; + icu->vartype= IPO_BITS; break; + case MA_RAYM: + icu->ymax= 1.0; break; + case MA_TRANSLU: + icu->ymax= 1.0; break; + case MA_IOR: + icu->ymin= 1.0; + icu->ymax= 3.0; break; + case MA_FRESMIR: + icu->ymax= 5.0; break; + case MA_FRESMIRI: + icu->ymin= 1.0; + icu->ymax= 5.0; break; + case MA_FRESTRA: + icu->ymax= 5.0; break; + case MA_FRESTRAI: + icu->ymin= 1.0; + icu->ymax= 5.0; break; + case MA_ADD: + icu->ymax= 1.0; break; + case MA_EMIT: + icu->ymax= 2.0; break; + default: + icu->ymax= 1.0; break; + } + } + else { + switch (icu->adrcode & (MA_MAP1-1)) { + case MAP_OFS_X: + case MAP_OFS_Y: + case MAP_OFS_Z: + case MAP_SIZE_X: + case MAP_SIZE_Y: + case MAP_SIZE_Z: + icu->ymax= 1000.0; + icu->ymin= -1000.0; + break; + case MAP_R: + case MAP_G: + case MAP_B: + case MAP_DVAR: + case MAP_COLF: + case MAP_VARF: + case MAP_DISP: + icu->ymax= 1.0; + break; + case MAP_NORF: + icu->ymax= 25.0; + break; + } + } + } break; - case AC_QUAT_Y: - poin= &(pchan->quat[2]); - pchan->flag |= POSE_ROT; + case ID_TE: /* texture channels ----------------------------- */ + { + switch (icu->adrcode & (MA_MAP1-1)) { + case TE_NSIZE: + icu->ymin= 0.0001f; + icu->ymax= 2.0f; + break; + case TE_NDEPTH: + icu->vartype= IPO_SHORT; + icu->ipo= IPO_CONST; + icu->ymax= 6.0f; + break; + case TE_NTYPE: + icu->vartype= IPO_SHORT; + icu->ipo= IPO_CONST; + icu->ymax= 1.0f; + break; + case TE_TURB: + icu->ymax= 200.0f; + break; + case TE_VNW1: + case TE_VNW2: + case TE_VNW3: + case TE_VNW4: + icu->ymax= 2.0f; + icu->ymin= -2.0f; + break; + case TE_VNMEXP: + icu->ymax= 10.0f; + icu->ymin= 0.01f; + break; + case TE_VN_DISTM: + icu->vartype= IPO_SHORT; + icu->ipo= IPO_CONST; + icu->ymax= 6.0f; + break; + case TE_VN_COLT: + icu->vartype= IPO_SHORT; + icu->ipo= IPO_CONST; + icu->ymax= 3.0f; + break; + case TE_ISCA: + icu->ymax= 10.0f; + icu->ymin= 0.01f; + break; + case TE_DISTA: + icu->ymax= 10.0f; + break; + case TE_MG_TYP: + icu->vartype= IPO_SHORT; + icu->ipo= IPO_CONST; + icu->ymax= 6.0f; + break; + case TE_MGH: + icu->ymin= 0.0001f; + icu->ymax= 2.0f; + break; + case TE_MG_LAC: + case TE_MG_OFF: + case TE_MG_GAIN: + icu->ymax= 6.0f; break; + case TE_MG_OCT: + icu->ymax= 8.0f; break; + case TE_N_BAS1: + case TE_N_BAS2: + icu->vartype= IPO_SHORT; + icu->ipo= IPO_CONST; + icu->ymax= 8.0f; + break; + case TE_COL_R: + icu->ymax= 0.0f; break; + case TE_COL_G: + icu->ymax= 2.0f; break; + case TE_COL_B: + icu->ymax= 2.0f; break; + case TE_BRIGHT: + icu->ymax= 2.0f; break; + case TE_CONTRA: + icu->ymax= 5.0f; break; + } + } break; - case AC_QUAT_Z: - poin= &(pchan->quat[3]); - pchan->flag |= POSE_ROT; + case ID_SEQ: /* sequence channels ----------------------------- */ + { + icu->ymax= 1.0f; + } break; - case AC_LOC_X: - poin= &(pchan->loc[0]); - pchan->flag |= POSE_LOC; + case ID_CU: /* curve channels ----------------------------- */ + { + icu->ymax= 1.0f; + } break; - case AC_LOC_Y: - poin= &(pchan->loc[1]); - pchan->flag |= POSE_LOC; + case ID_WO: /* world channels ----------------------------- */ + { + if (icu->adrcode < MA_MAP1) { + switch (icu->adrcode) { + case WO_EXPOS: + icu->ymax= 5.0f; break; + + case WO_MISTDI: + case WO_MISTSTA: + case WO_MISTHI: + case WO_STARDIST: + case WO_STARSIZE: + break; + + default: + icu->ymax= 1.0f; + break; + } + } + else { + switch (icu->adrcode & (MA_MAP1-1)) { + case MAP_OFS_X: + case MAP_OFS_Y: + case MAP_OFS_Z: + case MAP_SIZE_X: + case MAP_SIZE_Y: + case MAP_SIZE_Z: + icu->ymax= 100.0f; + icu->ymin= -100.0f; + break; + case MAP_R: + case MAP_G: + case MAP_B: + case MAP_DVAR: + case MAP_COLF: + case MAP_NORF: + case MAP_VARF: + case MAP_DISP: + icu->ymax= 1.0f; + } + } + } break; - case AC_LOC_Z: - poin= &(pchan->loc[2]); - pchan->flag |= POSE_LOC; - break; - case AC_SIZE_X: - poin= &(pchan->size[0]); - pchan->flag |= POSE_SIZE; + case ID_LA: /* lamp channels ----------------------------- */ + { + if (icu->adrcode < MA_MAP1) { + switch (icu->adrcode) { + case LA_ENERGY: + case LA_DIST: + break; + + case LA_COL_R: + case LA_COL_G: + case LA_COL_B: + case LA_SPOTBL: + case LA_QUAD1: + case LA_QUAD2: + icu->ymax= 1.0f; break; + + case LA_SPOTSI: + icu->ymax= 180.0f; break; + + case LA_HALOINT: + icu->ymax= 5.0f; break; + } + } + else { + switch (icu->adrcode & (MA_MAP1-1)) { + case MAP_OFS_X: + case MAP_OFS_Y: + case MAP_OFS_Z: + case MAP_SIZE_X: + case MAP_SIZE_Y: + case MAP_SIZE_Z: + icu->ymax= 100.0f; + icu->ymin= -100.0f; + break; + case MAP_R: + case MAP_G: + case MAP_B: + case MAP_DVAR: + case MAP_COLF: + case MAP_NORF: + case MAP_VARF: + case MAP_DISP: + icu->ymax= 1.0f; + } + } + } break; - case AC_SIZE_Y: - poin= &(pchan->size[1]); - pchan->flag |= POSE_SIZE; + case ID_CA: /* camera channels ----------------------------- */ + { + switch (icu->adrcode) { + case CAM_LENS: + icu->ymin= 1.0f; + icu->ymax= 1000.0f; + break; + case CAM_STA: + icu->ymin= 0.001f; + break; + case CAM_END: + icu->ymin= 0.1f; + break; + + case CAM_YF_APERT: + icu->ymin = 0.0f; + icu->ymax = 2.0f; + break; + case CAM_YF_FDIST: + icu->ymin = 0.0f; + icu->ymax = 5000.0f; + break; + + case CAM_SHIFT_X: + case CAM_SHIFT_Y: + icu->ymin= -2.0f; + icu->ymax= 2.0f; + break; + } + } break; - case AC_SIZE_Z: - poin= &(pchan->size[2]); - pchan->flag |= POSE_SIZE; + case ID_SO: /* sound channels ----------------------------- */ + { + switch (icu->adrcode) { + case SND_VOLUME: + icu->ymin= 0.0f; + icu->ymax= 1.0f; + break; + case SND_PITCH: + icu->ymin= -12.0f; + icu->ymin= 12.0f; + break; + case SND_PANNING: + icu->ymin= 0.0f; + icu->ymax= 1.0f; + break; + case SND_ATTEN: + icu->ymin= 0.0f; + icu->ymin= 1.0f; + break; + } + } break; - } - return poin; -} - -void execute_action_ipo(bActionChannel *achan, bPoseChannel *pchan) -{ - - if(achan && achan->ipo) { - IpoCurve *icu; - for(icu= achan->ipo->curve.first; icu; icu= icu->next) { - void *poin= get_pchan_ipo_poin(pchan, icu->adrcode); - if(poin) { - write_ipo_poin(poin, IPO_FLOAT, icu->curval); - //printf("execute_action_ipo wrote_ipo_poin: %f\n", icu->curval); - //printf("%s has poin %p value %f\n", achan->name, poin, icu->curval); + case ID_PA: /* particle channels ----------------------------- */ + { + switch (icu->adrcode) { + case PART_EMIT_LIFE: + case PART_SIZE: + case PART_KINK_FREQ: + case PART_EMIT_VEL: + case PART_EMIT_AVE: + case PART_EMIT_SIZE: + icu->ymin= 0.0f; + break; + case PART_CLUMP: + icu->ymin= -1.0f; + icu->ymax= 1.0f; + break; + case PART_DRAG: + case PART_DAMP: + case PART_LENGTH: + icu->ymin= 0.0f; + icu->ymax= 1.0f; + break; + case PART_KINK_SHAPE: + icu->ymin= -0.999f; + icu->ymax= 0.999f; + break; } } + break; + case ID_CO: /* constraint channels ----------------------------- */ + { + icu->ymin= 0.0f; + icu->ymax= 1.0f; + } + break; } + + /* by default, slider limits will be icu->ymin and icu->ymax */ + icu->slide_min= icu->ymin; + icu->slide_max= icu->ymax; } -/* exception: it does calc for objects... - * now find out why this routine was used anyway! - */ -void do_ipo_nocalc(Ipo *ipo) +/* --------------------- Pointer I/O API ----------------------------- */ + +/* write the given value directly into the given pointer */ +void write_ipo_poin (void *poin, int type, float val) { - Object *ob; - Material *ma; - Tex *tex; - World *wo; - Lamp *la; - Camera *ca; - bSound *snd; - - if(ipo==NULL) return; - - switch(ipo->blocktype) { - case ID_OB: - ob= G.main->object.first; - while(ob) { - if(ob->ipo==ipo) { - do_ob_ipo(ob); - /* execute_ipo((ID *)ob, ipo); */ - } - ob= ob->id.next; - } - break; - case ID_MA: - ma= G.main->mat.first; - while(ma) { - if(ma->ipo==ipo) execute_ipo((ID *)ma, ipo); - ma= ma->id.next; - } - break; - case ID_TE: - tex= G.main->tex.first; - while(tex) { - if(tex->ipo==ipo) execute_ipo((ID *)tex, ipo); - tex=tex->id.next; - } + /* Note: we only support a limited number of types, with the value + * to set needing to be cast to the appropriate type first + * -> (float to integer conversions could be slow) + */ + switch(type) { + case IPO_FLOAT: + *((float *)poin)= val; break; - case ID_WO: - wo= G.main->world.first; - while(wo) { - if(wo->ipo==ipo) execute_ipo((ID *)wo, ipo); - wo= wo->id.next; - } + + case IPO_FLOAT_DEGR: /* special hack for rotation so that it fits on same axis as other transforms */ + *((float *)poin)= (float)(val * M_PI_2 / 9.0); break; - case ID_LA: - la= G.main->lamp.first; - while(la) { - if(la->ipo==ipo) execute_ipo((ID *)la, ipo); - la= la->id.next; - } + + case IPO_INT: + case IPO_INT_BIT: // fixme... directly revealing bitflag combinations is evil! + case IPO_LONG: + *((int *)poin)= (int)val; break; - case ID_CA: - ca= G.main->camera.first; - while(ca) { - if(ca->ipo==ipo) execute_ipo((ID *)ca, ipo); - ca= ca->id.next; - } + + case IPO_SHORT: + case IPO_SHORT_BIT: // fixme... directly revealing bitflag combinations is evil! + *((short *)poin)= (short)val; break; - case ID_SO: - snd= G.main->sound.first; - while(snd) { - if(snd->ipo==ipo) execute_ipo((ID *)snd, ipo); - snd= snd->id.next; - } + + case IPO_CHAR: + case IPO_CHAR_BIT: // fixme... directly revealing bitflag combinations is evil! + *((char *)poin)= (char)val; break; } } -void do_ipo(Ipo *ipo) -{ - if(ipo) { - float ctime= frame_to_float(G.scene->r.cfra); - calc_ipo(ipo, ctime); - - do_ipo_nocalc(ipo); - } -} - - - -void do_mat_ipo(Material *ma) -{ - float ctime; - - if(ma==NULL || ma->ipo==NULL) return; - - ctime= frame_to_float(G.scene->r.cfra); - /* if(ob->ipoflag & OB_OFFS_OB) ctime-= ob->sf; */ - - calc_ipo(ma->ipo, ctime); - - execute_ipo((ID *)ma, ma->ipo); -} - -void do_ob_ipo(Object *ob) -{ - float ctime; - unsigned int lay; - - if(ob->ipo==NULL) return; - - /* do not set ob->ctime here: for example when parent in invisible layer */ - - ctime= bsystem_time(ob, (float) G.scene->r.cfra, 0.0); - - calc_ipo(ob->ipo, ctime); - - /* Patch: remember localview */ - lay= ob->lay & 0xFF000000; - - execute_ipo((ID *)ob, ob->ipo); - - ob->lay |= lay; - if(ob->id.name[2]=='S' && ob->id.name[3]=='C' && ob->id.name[4]=='E') { - if(strcmp(G.scene->id.name+2, ob->id.name+6)==0) { - G.scene->lay= ob->lay; - //XXX copy_view3d_lock(0); - /* no redraw here! creates too many calls */ - } - } -} - -void do_ob_ipodrivers(Object *ob, Ipo *ipo, float ctime) +/* read the value from the pointer that was obtained */ +float read_ipo_poin (void *poin, int type) { - IpoCurve *icu; - void *poin; - int type; - - for(icu= ipo->curve.first; icu; icu= icu->next) { - if(icu->driver) { - icu->curval= eval_icu(icu, ctime); - poin= get_ipo_poin((ID *)ob, icu, &type); - if(poin) write_ipo_poin(poin, type, icu->curval); - } - } -} - -void do_seq_ipo(Sequence *seq, int cfra) -{ - float ctime, div; - - /* seq_ipo has an exception: calc both fields immediately */ + float val = 0.0; - if(seq->ipo) { - if((seq->flag & SEQ_IPO_FRAME_LOCKED) != 0) { - ctime = frame_to_float(cfra); - div = 1.0; - } else { - ctime= frame_to_float(cfra - seq->startdisp); - div= (seq->enddisp - seq->startdisp)/100.0f; - if(div==0.0) return; - } + /* Note: we only support a limited number of types, with the value + * to set needing to be cast to the appropriate type first + * -> (int to float conversions may loose accuracy in rare cases) + */ + switch (type) { + case IPO_FLOAT: + val= *((float *)poin); + break; - /* 2nd field */ - calc_ipo(seq->ipo, (ctime+0.5f)/div); - execute_ipo((ID *)seq, seq->ipo); - seq->facf1= seq->facf0; - - /* 1st field */ - calc_ipo(seq->ipo, ctime/div); - execute_ipo((ID *)seq, seq->ipo); - - } - else seq->facf1= seq->facf0= 1.0f; -} - -int has_ipo_code(Ipo *ipo, int code) -{ - IpoCurve *icu; - - if(ipo==NULL) return 0; - - for(icu= ipo->curve.first; icu; icu= icu->next) { - if(icu->adrcode==code) return 1; - } - return 0; -} - -void do_all_data_ipos() -{ - Material *ma; - Tex *tex; - World *wo; - Ipo *ipo; - Lamp *la; - Key *key; - Camera *ca; - bSound *snd; - Sequence *seq; - Editing *ed; - Base *base; - float ctime; - - ctime= frame_to_float(G.scene->r.cfra); - - /* this exception cannot be depgraphed yet... what todo with objects in other layers?... */ - for(base= G.scene->base.first; base; base= base->next) { - /* only update layer when an ipo */ - if( has_ipo_code(base->object->ipo, OB_LAY) ) { - do_ob_ipo(base->object); - base->lay= base->object->lay; - } - } - - /* layers for the set...*/ - if(G.scene->set) { - for(base= G.scene->set->base.first; base; base= base->next) { - if( has_ipo_code(base->object->ipo, OB_LAY) ) { - do_ob_ipo(base->object); - base->lay= base->object->lay; - } - } - } - - - ipo= G.main->ipo.first; - while(ipo) { - if(ipo->id.us && ipo->blocktype!=ID_OB) { - calc_ipo(ipo, ctime); - } - ipo= ipo->id.next; - } - - for(tex= G.main->tex.first; tex; tex= tex->id.next) { - if(tex->ipo) execute_ipo((ID *)tex, tex->ipo); - } - - for(ma= G.main->mat.first; ma; ma= ma->id.next) { - if(ma->ipo) execute_ipo((ID *)ma, ma->ipo); - } - - for(wo= G.main->world.first; wo; wo= wo->id.next) { - if(wo->ipo) execute_ipo((ID *)wo, wo->ipo); - } - - for(key= G.main->key.first; key; key= key->id.next) { - if(key->ipo) execute_ipo((ID *)key, key->ipo); - } - - la= G.main->lamp.first; - while(la) { - if(la->ipo) execute_ipo((ID *)la, la->ipo); - la= la->id.next; - } - - ca= G.main->camera.first; - while(ca) { - if(ca->ipo) execute_ipo((ID *)ca, ca->ipo); - ca= ca->id.next; - } - - snd= G.main->sound.first; - while(snd) { - if(snd->ipo) execute_ipo((ID *)snd, snd->ipo); - snd= snd->id.next; - } - - /* process FAC Ipos used as volume envelopes */ - ed= G.scene->ed; - if (ed) { - seq= ed->seqbasep->first; - while(seq) { - if ((seq->type == SEQ_RAM_SOUND - || seq->type == SEQ_HD_SOUND) && (seq->ipo) && - (seq->startdisp<=G.scene->r.cfra+2) && - (seq->enddisp>G.scene->r.cfra)) - do_seq_ipo(seq, G.scene->r.cfra); - seq= seq->next; - } - } - -} - - -int calc_ipo_spec(Ipo *ipo, int adrcode, float *ctime) -{ - IpoCurve *icu; - - if(ipo==NULL) return 0; - - for(icu= ipo->curve.first; icu; icu= icu->next) { - if(icu->adrcode == adrcode) { - if(icu->flag & IPO_LOCK); - else calc_icu(icu, *ctime); - - *ctime= icu->curval; - return 1; - } - } - - return 0; -} - - -/* ************************** */ - -void clear_delta_obipo(Ipo *ipo) -{ - Object *ob; + case IPO_FLOAT_DEGR: /* special hack for rotation so that it fits on same axis as other transforms */ + val= *( (float *)poin); + val = (float)(val / (M_PI_2/9.0)); + break; - if(ipo==NULL) return; + case IPO_INT: + case IPO_INT_BIT: // fixme... directly revealing bitflag combinations is evil! + case IPO_LONG: + val= (float)( *((int *)poin) ); + break; + + case IPO_SHORT: + case IPO_SHORT_BIT: // fixme... directly revealing bitflag combinations is evil! + val= *((short *)poin); + break; - ob= G.main->object.first; - while(ob) { - if(ob->id.lib==NULL) { - if(ob->ipo==ipo) { - memset(&ob->dloc, 0, 12); - memset(&ob->drot, 0, 12); - memset(&ob->dsize, 0, 12); - } - } - ob= ob->id.next; + case IPO_CHAR: + case IPO_CHAR_BIT: // fixme... directly revealing bitflag combinations is evil + val= *((char *)poin); + break; } -} - -void add_to_cfra_elem(ListBase *lb, BezTriple *bezt) -{ - CfraElem *ce, *cen; - - ce= lb->first; - while(ce) { - - if( ce->cfra==bezt->vec[1][0] ) { - /* do because of double keys */ - if(bezt->f2 & SELECT) ce->sel= bezt->f2; - return; - } - else if(ce->cfra > bezt->vec[1][0]) break; - - ce= ce->next; - } - cen= MEM_callocN(sizeof(CfraElem), "add_to_cfra_elem"); - if(ce) BLI_insertlinkbefore(lb, ce, cen); - else BLI_addtail(lb, cen); - - cen->cfra= bezt->vec[1][0]; - cen->sel= bezt->f2; + /* return value */ + return val; } +// !!!!!!!!!!!!!!!!!!!!!!!!!!!! FIXME - BAD CRUFT WARNING !!!!!!!!!!!!!!!!!!!!!!! -void make_cfra_list(Ipo *ipo, ListBase *elems) -{ - IpoCurve *icu; - BezTriple *bezt; - int a; - - if(ipo->blocktype==ID_OB) { - for(icu= ipo->curve.first; icu; icu= icu->next) { - if(icu->flag & IPO_VISIBLE) { - switch(icu->adrcode) { - case OB_DLOC_X: - case OB_DLOC_Y: - case OB_DLOC_Z: - case OB_DROT_X: - case OB_DROT_Y: - case OB_DROT_Z: - case OB_DSIZE_X: - case OB_DSIZE_Y: - case OB_DSIZE_Z: - - case OB_LOC_X: - case OB_LOC_Y: - case OB_LOC_Z: - case OB_ROT_X: - case OB_ROT_Y: - case OB_ROT_Z: - case OB_SIZE_X: - case OB_SIZE_Y: - case OB_SIZE_Z: - case OB_PD_FSTR: - case OB_PD_FFALL: - case OB_PD_SDAMP: - case OB_PD_RDAMP: - case OB_PD_PERM: - case OB_PD_FMAXD: - bezt= icu->bezt; - if(bezt) { - a= icu->totvert; - while(a--) { - add_to_cfra_elem(elems, bezt); - bezt++; - } - } - break; - } - } - } - } - else if(ipo->blocktype==ID_AC) { - for(icu= ipo->curve.first; icu; icu= icu->next) { - if(icu->flag & IPO_VISIBLE) { - switch(icu->adrcode) { - case AC_LOC_X: - case AC_LOC_Y: - case AC_LOC_Z: - case AC_SIZE_X: - case AC_SIZE_Y: - case AC_SIZE_Z: - case AC_QUAT_W: - case AC_QUAT_X: - case AC_QUAT_Y: - case AC_QUAT_Z: - bezt= icu->bezt; - if(bezt) { - a= icu->totvert; - while(a--) { - add_to_cfra_elem(elems, bezt); - bezt++; - } - } - break; - } - } - } - } - else { - for(icu= ipo->curve.first; icu; icu= icu->next) { - if(icu->flag & IPO_VISIBLE) { - bezt= icu->bezt; - if(bezt) { - a= icu->totvert; - while(a--) { - add_to_cfra_elem(elems, bezt); - bezt++; - } - } - } - } - } - - /* what's the point of this little block of code? */ -#if 0 - if(ipo->showkey==0) { - /* deselect all keys */ - ce= elems->first; - while(ce) { - ce->sel= 0; - ce= ce->next; - } - } -#endif -} - -/* *********************** INTERFACE FOR KETSJI ********** */ - +/* ***************************** IPO <--> GameEngine Interface ********************************* */ -int IPO_GetChannels(Ipo *ipo, IPO_Channel *channels) +/* channels is max 32 items, allocated by calling function */ +short IPO_GetChannels (Ipo *ipo, IPO_Channel *channels) { - /* channels is max 32 items, allocated by calling function */ - IpoCurve *icu; - int total=0; + int total = 0; - if(ipo==NULL) return 0; + /* don't do anything with no IPO-block */ + if (ipo == NULL) + return 0; - for(icu= ipo->curve.first; icu; icu= icu->next) { + /* store the IPO-curve's adrcode in the relevant channel slot */ + for (icu=ipo->curve.first; (icu) && (total < 31); icu=icu->next, total++) channels[total]= icu->adrcode; - total++; - if(total>31) break; - } + /* return the number of channels stored */ return total; } - - /* Get the float value for channel 'channel' at time 'ctime' */ - -float IPO_GetFloatValue(Ipo *ipo, IPO_Channel channel, float ctime) +float IPO_GetFloatValue (Ipo *ipo, IPO_Channel channel, float ctime) { - if(ipo==NULL) return 0; + /* don't evaluate if no IPO to use */ + if (ipo == NULL) + return 0; + /* only calculate the specified channel */ calc_ipo_spec(ipo, channel, &ctime); - if (OB_ROT_X <= channel && channel <= OB_DROT_Z) { + /* unapply rotation hack, as gameengine doesn't use it */ + if ((OB_ROT_X <= channel) && (channel <= OB_DROT_Z)) ctime *= (float)(M_PI_2/9.0); - } + /* return the value of this channel */ return ctime; } diff --git a/source/blender/blenkernel/intern/key.c b/source/blender/blenkernel/intern/key.c index dd5adac29b0..a34da4377b1 100644 --- a/source/blender/blenkernel/intern/key.c +++ b/source/blender/blenkernel/intern/key.c @@ -281,10 +281,10 @@ void set_four_ipo(float d, float *data, int type) } else if(type==KEY_BSPLINE) { - data[0]= -0.1666f*d3 +0.5f*d2 -0.5f*d +0.16666f; - data[1]= 0.5f*d3 -d2 +0.6666f; - data[2]= -0.5f*d3 +0.5f*d2 +0.5f*d +0.1666f; - data[3]= 0.1666f*d3 ; + data[0]= -0.16666666f*d3 +0.5f*d2 -0.5f*d +0.16666666f; + data[1]= 0.5f*d3 -d2 +0.6666666f; + data[2]= -0.5f*d3 +0.5f*d2 +0.5f*d +0.16666666f; + data[3]= 0.16666666f*d3 ; } } } @@ -310,10 +310,10 @@ void set_afgeleide_four_ipo(float d, float *data, int type) } else if(type==KEY_BSPLINE) { - data[0]= -0.1666f*3.0f*d2 +d -0.5f; + data[0]= -0.16666666f*3.0f*d2 +d -0.5f; data[1]= 1.5f*d2 -2.0f*d; data[2]= -1.5f*d2 +d +0.5f; - data[3]= 0.1666f*3.0f*d2 ; + data[3]= 0.16666666f*3.0f*d2 ; } } } @@ -539,26 +539,26 @@ static void cp_key(int start, int end, int tot, char *poin, Key *key, KeyBlock * case IPO_FLOAT: if(weights) { - memcpy(poin, kref, 4*cp[0]); + memcpy(poin, kref, sizeof(float)*cp[0]); if(*weights!=0.0f) rel_flerp(cp[0], (float *)poin, (float *)kref, (float *)k1, *weights); weights++; } else - memcpy(poin, k1, 4*cp[0]); + memcpy(poin, k1, sizeof(float)*cp[0]); poin+= ofsp[0]; break; case IPO_BPOINT: - memcpy(poin, k1, 3*4); - memcpy(poin+16, k1+12, 4); + memcpy(poin, k1, 3*sizeof(float)); + memcpy(poin+4*sizeof(float), k1+3*sizeof(float), sizeof(float)); poin+= ofsp[0]; break; case IPO_BEZTRIPLE: - memcpy(poin, k1, 4*12); + memcpy(poin, k1, sizeof(float)*10); poin+= ofsp[0]; break; @@ -627,7 +627,7 @@ void cp_cu_key(Curve *cu, KeyBlock *kb, int start, int end) } -static void do_rel_key(int start, int end, int tot, char *basispoin, Key *key, int mode) +void do_rel_key(int start, int end, int tot, char *basispoin, Key *key, int mode) { KeyBlock *kb; int *ofsp, ofs[3], elemsize, b; diff --git a/source/blender/blenkernel/intern/lattice.c b/source/blender/blenkernel/intern/lattice.c index aaacba97d35..3f9143bb405 100644 --- a/source/blender/blenkernel/intern/lattice.c +++ b/source/blender/blenkernel/intern/lattice.c @@ -514,7 +514,7 @@ static int where_on_path_deform(Object *ob, float ctime, float *vec, float *dir) /* co: local coord, result local too */ /* returns quaternion for rotation, using cd->no_rot_axis */ /* axis is using another define!!! */ -static float *calc_curve_deform(Object *par, float *co, short axis, CurveDeform *cd) +static int calc_curve_deform(Object *par, float *co, short axis, CurveDeform *cd, float *quatp) { Curve *cu= par->data; float fac, loc[4], dir[3], cent[3]; @@ -544,7 +544,7 @@ static float *calc_curve_deform(Object *par, float *co, short axis, CurveDeform /* to be sure, mostly after file load */ if(cu->path==NULL) { makeDispListCurveTypes(par, 0); - if(cu->path==NULL) return NULL; // happens on append... + if(cu->path==NULL) return 0; // happens on append... } /* options */ @@ -570,14 +570,13 @@ static float *calc_curve_deform(Object *par, float *co, short axis, CurveDeform } if( where_on_path_deform(par, fac, loc, dir)) { /* returns OK */ - float q[4], mat[3][3]; - float *quat; + float q[4], mat[3][3], quat[4]; if(cd->no_rot_axis) /* set by caller */ dir[cd->no_rot_axis-1]= 0.0f; /* -1 for compatibility with old track defines */ - quat= vectoquat(dir, axis-1, upflag); /* gives static quat */ + vectoquat(dir, axis-1, upflag, quat); /* the tilt */ if(loc[3]!=0.0) { @@ -597,18 +596,26 @@ static float *calc_curve_deform(Object *par, float *co, short axis, CurveDeform /* translation */ VECADD(co, cent, loc); - return quat; + if(quatp) + QUATCOPY(quatp, quat); + + return 1; } - return NULL; + return 0; } void curve_deform_verts(Object *cuOb, Object *target, DerivedMesh *dm, float (*vertexCos)[3], int numVerts, char *vgroup, short defaxis) { - Curve *cu = cuOb->data; - int a, flag = cu->flag; + Curve *cu; + int a, flag; CurveDeform cd; int use_vgroups; - + + if(cuOb->type != OB_CURVE) + return; + + cu = cuOb->data; + flag = cu->flag; cu->flag |= (CU_PATH|CU_FOLLOW); // needed for path & bevlist init_curve_deform(cuOb, target, &cd, (cu->flag & CU_STRETCH)==0); @@ -663,7 +670,7 @@ void curve_deform_verts(Object *cuOb, Object *target, DerivedMesh *dm, float (*v for(j = 0; j < dvert->totweight; j++) { if(dvert->dw[j].def_nr == index) { VECCOPY(vec, vertexCos[a]); - calc_curve_deform(cuOb, vec, defaxis, &cd); + calc_curve_deform(cuOb, vec, defaxis, &cd, NULL); VecLerpf(vertexCos[a], vertexCos[a], vec, dvert->dw[j].weight); Mat4MulVecfl(cd.objectspace, vertexCos[a]); @@ -681,7 +688,7 @@ void curve_deform_verts(Object *cuOb, Object *target, DerivedMesh *dm, float (*v } for(a = 0; a < numVerts; a++) { - calc_curve_deform(cuOb, vertexCos[a], defaxis, &cd); + calc_curve_deform(cuOb, vertexCos[a], defaxis, &cd, NULL); Mat4MulVecfl(cd.objectspace, vertexCos[a]); } } @@ -694,8 +701,13 @@ void curve_deform_verts(Object *cuOb, Object *target, DerivedMesh *dm, float (*v void curve_deform_vector(Object *cuOb, Object *target, float *orco, float *vec, float mat[][3], int no_rot_axis) { CurveDeform cd; - float *quat; + float quat[4]; + if(cuOb->type != OB_CURVE) { + Mat3One(mat); + return; + } + init_curve_deform(cuOb, target, &cd, 0); /* 0 no dloc */ cd.no_rot_axis= no_rot_axis; /* option to only rotate for XY, for example */ @@ -704,8 +716,7 @@ void curve_deform_vector(Object *cuOb, Object *target, float *orco, float *vec, Mat4MulVecfl(cd.curvespace, vec); - quat= calc_curve_deform(cuOb, vec, target->trackflag+1, &cd); - if(quat) { + if(calc_curve_deform(cuOb, vec, target->trackflag+1, &cd, quat)) { float qmat[3][3]; QuatToMat3(quat, qmat); @@ -724,6 +735,9 @@ void lattice_deform_verts(Object *laOb, Object *target, DerivedMesh *dm, int a; int use_vgroups; + if(laOb->type != OB_LATTICE) + return; + init_latt_deform(laOb, target); /* check whether to use vertex groups (only possible if target is a Mesh) @@ -899,7 +913,10 @@ void lattice_calc_modifiers(Object *ob) mti->deformVerts(md, ob, NULL, vertexCos, numVerts); } - if (vertexCos) { + /* always displist to make this work like derivedmesh */ + if (!vertexCos) vertexCos = lattice_getVertexCos(ob, &numVerts); + + { DispList *dl = MEM_callocN(sizeof(*dl), "lt_dl"); dl->type = DL_VERTS; dl->parts = 1; @@ -909,3 +926,15 @@ void lattice_calc_modifiers(Object *ob) BLI_addtail(&ob->disp, dl); } } + +struct MDeformVert* lattice_get_deform_verts(struct Object *oblatt) +{ + if(oblatt->type == OB_LATTICE) + { + Lattice *lt = (oblatt==G.obedit)?editLatt:(Lattice*)oblatt->data; + return lt->dvert; + } + + return NULL; +} + diff --git a/source/blender/blenkernel/intern/library.c b/source/blender/blenkernel/intern/library.c index f0dd3d1ec4b..3acc10e8022 100644 --- a/source/blender/blenkernel/intern/library.c +++ b/source/blender/blenkernel/intern/library.c @@ -76,6 +76,7 @@ #include "DNA_effect_types.h" #include "DNA_brush_types.h" #include "DNA_particle_types.h" +#include "DNA_space_types.h" #include "DNA_windowmanager_types.h" #include "BLI_blenlib.h" @@ -111,8 +112,6 @@ #include "BKE_idprop.h" #include "BKE_particle.h" -//XXX #include "BPI_script.h" - #define MAX_IDPUP 60 /* was 24 */ /* ************* general ************************ */ @@ -226,49 +225,51 @@ void flag_all_listbases_ids(short flag, short value) /* note: MAX_LIBARRAY define should match this code */ int set_listbasepointers(Main *main, ListBase **lb) { + int a = 0; + /* BACKWARDS! also watch order of free-ing! (mesh<->mat) */ - lb[0]= &(main->ipo); - lb[1]= &(main->key); - lb[2]= &(main->image); - lb[3]= &(main->tex); - lb[4]= &(main->mat); - lb[5]= &(main->vfont); + lb[a++]= &(main->ipo); + lb[a++]= &(main->key); + lb[a++]= &(main->nodetree); + lb[a++]= &(main->image); + lb[a++]= &(main->tex); + lb[a++]= &(main->mat); + lb[a++]= &(main->vfont); /* Important!: When adding a new object type, * the specific data should be inserted here */ - lb[6]= &(main->armature); - lb[7]= &(main->action); + lb[a++]= &(main->armature); + lb[a++]= &(main->action); - lb[8]= &(main->mesh); - lb[9]= &(main->curve); - lb[10]= &(main->mball); + lb[a++]= &(main->mesh); + lb[a++]= &(main->curve); + lb[a++]= &(main->mball); - lb[11]= &(main->wave); - lb[12]= &(main->latt); - lb[13]= &(main->lamp); - lb[14]= &(main->camera); + lb[a++]= &(main->wave); + lb[a++]= &(main->latt); + lb[a++]= &(main->lamp); + lb[a++]= &(main->camera); - lb[15]= &(main->text); - lb[16]= &(main->sound); - lb[17]= &(main->group); - lb[18]= &(main->nodetree); - lb[19]= &(main->brush); - lb[20]= &(main->script); - lb[21]= &(main->particle); + lb[a++]= &(main->text); + lb[a++]= &(main->sound); + lb[a++]= &(main->group); + lb[a++]= &(main->brush); + lb[a++]= &(main->script); + lb[a++]= &(main->particle); - lb[22]= &(main->world); - lb[23]= &(main->screen); - lb[24]= &(main->object); - lb[25]= &(main->scene); - lb[26]= &(main->library); - lb[27]= &(main->wm); + lb[a++]= &(main->world); + lb[a++]= &(main->screen); + lb[a++]= &(main->object); + lb[a++]= &(main->scene); + lb[a++]= &(main->library); + lb[a++]= &(main->wm); - lb[28]= NULL; + lb[a]= NULL; - return 28; + return a; } /* *********** ALLOC AND FREE ***************** @@ -411,6 +412,10 @@ void *copy_libblock(void *rt) lb= wich_libbase(G.main, GS(id->name)); idn= alloc_libblock(lb, GS(id->name), id->name+2); + if(idn==NULL) { + printf("ERROR: Illegal ID name for %s (Crashing now)\n", id->name); + } + idn_len= MEM_allocN_len(idn); if(idn_len - sizeof(ID) > 0) { cp= (char *)id; @@ -951,7 +956,7 @@ int new_id(ListBase *lb, ID *id, const char *tname) } /* if result > 21, strncpy don't put the final '\0' to name. */ - if( result > 21 ) name[21]= 0; + if( result >= 21 ) name[21]= 0; result = check_for_dupid( lb, id, name ); strcpy( id->name+2, name ); @@ -991,7 +996,7 @@ static void image_fix_relative_path(Image *ima) { if(ima->id.lib==NULL) return; if(strncmp(ima->name, "//", 2)==0) { - BLI_convertstringcode(ima->name, ima->id.lib->filename, 0); + BLI_convertstringcode(ima->name, ima->id.lib->filename); BLI_makestringcode(G.sce, ima->name); } } @@ -1008,7 +1013,7 @@ static void lib_indirect_test_id(ID *id) Object *ob= (Object *)id; bActionStrip *strip; Mesh *me; - PartEff *paf; + int a; for (strip=ob->nlastrips.first; strip; strip=strip->next){ @@ -1020,10 +1025,6 @@ static void lib_indirect_test_id(ID *id) for(a=0; a<ob->totcol; a++) { LIBTAG(ob->mat[a]); } - - paf = give_parteff(ob); - if (paf) - LIBTAG(paf->group); LIBTAG(ob->dup_group); LIBTAG(ob->proxy); diff --git a/source/blender/blenkernel/intern/material.c b/source/blender/blenkernel/intern/material.c index baaa5becd44..95534deae66 100644 --- a/source/blender/blenkernel/intern/material.c +++ b/source/blender/blenkernel/intern/material.c @@ -58,7 +58,11 @@ #include "BKE_node.h" #include "BKE_utildefines.h" +#ifndef DISABLE_PYTHON #include "BPY_extern.h" +#endif + +#include "GPU_material.h" /* used in UI and render */ Material defmaterial; @@ -75,7 +79,9 @@ void free_material(Material *ma) MTex *mtex; int a; +#ifndef DISABLE_PYTHON BPY_free_scriptlink(&ma->scriptlink); +#endif for(a=0; a<MAX_MTEX; a++) { mtex= ma->mtex[a]; @@ -95,6 +101,9 @@ void free_material(Material *ma) ntreeFreeTree(ma->nodetree); MEM_freeN(ma->nodetree); } + + if(ma->gpumaterial.first) + GPU_material_free(ma); } void init_material(Material *ma) @@ -195,8 +204,10 @@ Material *copy_material(Material *ma) id_us_plus((ID *)man->mtex[a]->tex); } } - + +#ifndef DISABLE_PYTHON BPY_copy_scriptlink(&ma->scriptlink); +#endif if(ma->ramp_col) man->ramp_col= MEM_dupallocN(ma->ramp_col); if(ma->ramp_spec) man->ramp_spec= MEM_dupallocN(ma->ramp_spec); @@ -206,6 +217,8 @@ Material *copy_material(Material *ma) if(ma->nodetree) { man->nodetree= ntreeCopyTree(ma->nodetree, 0); /* 0 == full new tree */ } + + man->gpumaterial.first= man->gpumaterial.last= NULL; return man; } @@ -629,9 +642,7 @@ static void do_init_render_material(Material *ma, int r_mode, float *amb) if(ma->flarec==0) ma->flarec= 1; - /* add all texcoflags from mtex */ - ma->texco= 0; - ma->mapto= 0; + /* add all texcoflags from mtex, texco and mapto were cleared in advance */ for(a=0; a<MAX_MTEX; a++) { /* separate tex switching */ @@ -726,6 +737,16 @@ void init_render_materials(int r_mode, float *amb) { Material *ma; + /* clear these flags before going over materials, to make sure they + * are cleared only once, otherwise node materials contained in other + * node materials can go wrong */ + for(ma= G.main->mat.first; ma; ma= ma->id.next) { + if(ma->id.us) { + ma->texco= 0; + ma->mapto= 0; + } + } + /* two steps, first initialize, then or the flags for layers */ for(ma= G.main->mat.first; ma; ma= ma->id.next) { /* is_used flag comes back in convertblender.c */ diff --git a/source/blender/blenkernel/intern/mball.c b/source/blender/blenkernel/intern/mball.c index a2a1c5dcfa6..d84ad0a5215 100644 --- a/source/blender/blenkernel/intern/mball.c +++ b/source/blender/blenkernel/intern/mball.c @@ -229,14 +229,14 @@ void tex_space_mball(Object *ob) boundbox_set_from_min_max(bb, min, max); } -void make_orco_mball(Object *ob) +float *make_orco_mball(Object *ob) { BoundBox *bb; DispList *dl; - float *data; + float *data, *orco, *orcodata; float loc[3], size[3]; int a; - + /* restore size and loc */ bb= ob->bb; loc[0]= (bb->vec[0][0]+bb->vec[4][0])/2.0f; @@ -247,15 +247,21 @@ void make_orco_mball(Object *ob) size[2]= bb->vec[1][2]-loc[2]; dl= ob->disp.first; + orcodata= MEM_mallocN(sizeof(float)*3*dl->nr, "MballOrco"); + data= dl->verts; + orco= orcodata; a= dl->nr; while(a--) { - data[0]= (data[0]-loc[0])/size[0]; - data[1]= (data[1]-loc[1])/size[1]; - data[2]= (data[2]-loc[2])/size[2]; + orco[0]= (data[0]-loc[0])/size[0]; + orco[1]= (data[1]-loc[1])/size[1]; + orco[2]= (data[2]-loc[2])/size[2]; data+= 3; + orco+= 3; } + + return orcodata; } /** \brief Test, if Object *ob is basic MetaBall. * @@ -369,7 +375,7 @@ Object *find_basis_mball(Object *basis) #define RTF 7 /* right top far corner */ /* the LBN corner of cube (i, j, k), corresponds with location - * (start.x+(i-0.5)*size, start.y+(j-0.5)*size, start.z+(k-0.5)*size) */ + * (i-0.5)*size, (j-0.5)*size, (k-0.5)*size) */ #define HASHBIT (5) #define HASHSIZE (size_t)(1<<(3*HASHBIT)) /*! < hash table size (32768) */ @@ -830,11 +836,11 @@ CORNER *setcorner (PROCESS* p, int i, int j, int k) c = (CORNER *) new_pgn_element(sizeof(CORNER)); c->i = i; - c->x = p->start.x+((float)i-0.5f)*p->size; + c->x = ((float)i-0.5f)*p->size; c->j = j; - c->y = p->start.y+((float)j-0.5f)*p->size; + c->y = ((float)j-0.5f)*p->size; c->k = k; - c->z = p->start.z+((float)k-0.5f)*p->size; + c->z = ((float)k-0.5f)*p->size; c->value = p->function(c->x, c->y, c->z); c->next = p->corners[index]; @@ -1208,8 +1214,8 @@ void converge (MB_POINT *p1, MB_POINT *p2, float v1, float v2, p->y = neg.y; p->z = neg.z; while (1) { - p->x = 0.5f*(pos.x + neg.x); if (i++ == RES) return; + p->x = 0.5f*(pos.x + neg.x); if ((function(p->x,p->y,p->z)) > 0.0) pos.x = p->x; else neg.x = p->x; } } @@ -1218,8 +1224,8 @@ void converge (MB_POINT *p1, MB_POINT *p2, float v1, float v2, p->x = neg.x; p->z = neg.z; while (1) { - p->y = 0.5f*(pos.y + neg.y); if (i++ == RES) return; + p->y = 0.5f*(pos.y + neg.y); if ((function(p->x,p->y,p->z)) > 0.0) pos.y = p->y; else neg.y = p->y; } } @@ -1228,8 +1234,8 @@ void converge (MB_POINT *p1, MB_POINT *p2, float v1, float v2, p->x = neg.x; p->y = neg.y; while (1) { - p->z = 0.5f*(pos.z + neg.z); if (i++ == RES) return; + p->z = 0.5f*(pos.z + neg.z); if ((function(p->x,p->y,p->z)) > 0.0) pos.z = p->z; else neg.z = p->z; } } @@ -1294,6 +1300,8 @@ void find_first_points(PROCESS *mbproc, MetaBall *mb, int a) int index[3]={1,0,-1}; float f =0.0f; float in_v, out_v; + MB_POINT workp; + float tmp_v, workp_v, max_len, len, dx, dy, dz, nx, ny, nz, MAXN; ml = mainb[a]; @@ -1354,24 +1362,50 @@ void find_first_points(PROCESS *mbproc, MetaBall *mb, int a) out_v = mbproc->function(out.x, out.y, out.z); - /* find "first point" on Implicit Surface of MetaElemnt ml */ - converge(&in, &out, in_v, out_v, mbproc->function, &mbproc->start, mb, 0); - - /* indexes of CUBE, which includes "first point" */ - c_i= (int)floor(mbproc->start.x/mbproc->size ); - c_j= (int)floor(mbproc->start.y/mbproc->size ); - c_k= (int)floor(mbproc->start.z/mbproc->size ); - - mbproc->start.x= mbproc->start.y= mbproc->start.z= 0.0; - - /* add CUBE (with indexes c_i, c_j, c_k) to the stack, - * this cube includes found point of Implicit Surface */ - if (ml->flag & MB_NEGATIVE) - add_cube(mbproc, c_i, c_j, c_k, 2); - else - add_cube(mbproc, c_i, c_j, c_k, 1); - + /* find "first points" on Implicit Surface of MetaElemnt ml */ + workp.x = in.x; + workp.y = in.y; + workp.z = in.z; + workp_v = in_v; + max_len = sqrt((out.x-in.x)*(out.x-in.x) + (out.y-in.y)*(out.y-in.y) + (out.z-in.z)*(out.z-in.z)); + + nx = abs((out.x - in.x)/mbproc->size); + ny = abs((out.y - in.y)/mbproc->size); + nz = abs((out.z - in.z)/mbproc->size); + MAXN = MAX3(nx,ny,nz); + if(MAXN!=0.0f) { + dx = (out.x - in.x)/MAXN; + dy = (out.y - in.y)/MAXN; + dz = (out.z - in.z)/MAXN; + + len = 0.0; + while(len<=max_len) { + workp.x += dx; + workp.y += dy; + workp.z += dz; + /* compute value of implicite function */ + tmp_v = mbproc->function(workp.x, workp.y, workp.z); + /* add cube to the stack, when value of implicite function crosses zero value */ + if((tmp_v<0.0 && workp_v>=0.0)||(tmp_v>0.0 && workp_v<=0.0)) { + + /* indexes of CUBE, which includes "first point" */ + c_i= (int)floor(workp.x/mbproc->size); + c_j= (int)floor(workp.y/mbproc->size); + c_k= (int)floor(workp.z/mbproc->size); + + /* add CUBE (with indexes c_i, c_j, c_k) to the stack, + * this cube includes found point of Implicit Surface */ + if (ml->flag & MB_NEGATIVE) + add_cube(mbproc, c_i, c_j, c_k, 2); + else + add_cube(mbproc, c_i, c_j, c_k, 1); + } + len = sqrt((workp.x-in.x)*(workp.x-in.x) + (workp.y-in.y)*(workp.y-in.y) + (workp.z-in.z)*(workp.z-in.z)); + workp_v = tmp_v; + + } + } } } } @@ -1677,15 +1711,13 @@ void fill_metaball_octal_node(octal_node *node, MetaElem *ml, short i) * +------+------+ * */ -void subdivide_metaball_octal_node(octal_node *node, float *size, short depth) +void subdivide_metaball_octal_node(octal_node *node, float size_x, float size_y, float size_z, short depth) { MetaElem *ml; ml_pointer *ml_p; float x,y,z; int a,i; - if(depth==0) return; - /* create new nodes */ for(a=0;a<8;a++){ node->nodes[a]= MEM_mallocN(sizeof(octal_node),"octal_node"); @@ -1699,45 +1731,71 @@ void subdivide_metaball_octal_node(octal_node *node, float *size, short depth) node->nodes[a]->pos= 0; } - size[0]/=2; size[1]/=2; size[2]/=2; + size_x /= 2; + size_y /= 2; + size_z /= 2; /* center of node */ - node->x= x= node->x_min + size[0]; - node->y= y= node->y_min + size[1]; - node->z= z= node->z_min + size[2]; + node->x = x = node->x_min + size_x; + node->y = y = node->y_min + size_y; + node->z = z = node->z_min + size_z; /* setting up of border points of new nodes */ - node->nodes[0]->x_min= node->x_min; - node->nodes[0]->y_min= node->y_min; - node->nodes[0]->z_min= node->z_min; - - node->nodes[1]->x_min= x; - node->nodes[1]->y_min= node->y_min; - node->nodes[1]->z_min= node->z_min; - - node->nodes[2]->x_min= x; - node->nodes[2]->y_min= y; - node->nodes[2]->z_min= node->z_min; - - node->nodes[3]->x_min= node->x_min; - node->nodes[3]->y_min= y; - node->nodes[3]->z_min= node->z_min; - - node->nodes[4]->x_min= node->x_min; - node->nodes[4]->y_min= node->y_min; - node->nodes[4]->z_min= z; - - node->nodes[5]->x_min= x; - node->nodes[5]->y_min= node->y_min; - node->nodes[5]->z_min= z; - - node->nodes[6]->x_min= x; - node->nodes[6]->y_min= y; - node->nodes[6]->z_min= z; - - node->nodes[7]->x_min= node->x_min; - node->nodes[7]->y_min= y; - node->nodes[7]->z_min= z; + node->nodes[0]->x_min = node->x_min; + node->nodes[0]->y_min = node->y_min; + node->nodes[0]->z_min = node->z_min; + node->nodes[0]->x = node->nodes[0]->x_min + size_x/2; + node->nodes[0]->y = node->nodes[0]->y_min + size_y/2; + node->nodes[0]->z = node->nodes[0]->z_min + size_z/2; + + node->nodes[1]->x_min = x; + node->nodes[1]->y_min = node->y_min; + node->nodes[1]->z_min = node->z_min; + node->nodes[1]->x = node->nodes[1]->x_min + size_x/2; + node->nodes[1]->y = node->nodes[1]->y_min + size_y/2; + node->nodes[1]->z = node->nodes[1]->z_min + size_z/2; + + node->nodes[2]->x_min = x; + node->nodes[2]->y_min = y; + node->nodes[2]->z_min = node->z_min; + node->nodes[2]->x = node->nodes[2]->x_min + size_x/2; + node->nodes[2]->y = node->nodes[2]->y_min + size_y/2; + node->nodes[2]->z = node->nodes[2]->z_min + size_z/2; + + node->nodes[3]->x_min = node->x_min; + node->nodes[3]->y_min = y; + node->nodes[3]->z_min = node->z_min; + node->nodes[3]->x = node->nodes[3]->x_min + size_x/2; + node->nodes[3]->y = node->nodes[3]->y_min + size_y/2; + node->nodes[3]->z = node->nodes[3]->z_min + size_z/2; + + node->nodes[4]->x_min = node->x_min; + node->nodes[4]->y_min = node->y_min; + node->nodes[4]->z_min = z; + node->nodes[4]->x = node->nodes[4]->x_min + size_x/2; + node->nodes[4]->y = node->nodes[4]->y_min + size_y/2; + node->nodes[4]->z = node->nodes[4]->z_min + size_z/2; + + node->nodes[5]->x_min = x; + node->nodes[5]->y_min = node->y_min; + node->nodes[5]->z_min = z; + node->nodes[5]->x = node->nodes[5]->x_min + size_x/2; + node->nodes[5]->y = node->nodes[5]->y_min + size_y/2; + node->nodes[5]->z = node->nodes[5]->z_min + size_z/2; + + node->nodes[6]->x_min = x; + node->nodes[6]->y_min = y; + node->nodes[6]->z_min = z; + node->nodes[6]->x = node->nodes[6]->x_min + size_x/2; + node->nodes[6]->y = node->nodes[6]->y_min + size_y/2; + node->nodes[6]->z = node->nodes[6]->z_min + size_z/2; + + node->nodes[7]->x_min = node->x_min; + node->nodes[7]->y_min = y; + node->nodes[7]->z_min = z; + node->nodes[7]->x = node->nodes[7]->x_min + size_x/2; + node->nodes[7]->y = node->nodes[7]->y_min + size_y/2; + node->nodes[7]->z = node->nodes[7]->z_min + size_z/2; ml_p= node->elems.first; @@ -1904,7 +1962,7 @@ void subdivide_metaball_octal_node(octal_node *node, float *size, short depth) if(depth>0){ for(a=0;a<8;a++){ if(node->nodes[a]->count > 0) /* if node is not empty, then it is subdivided */ - subdivide_metaball_octal_node(node->nodes[a], size, depth); + subdivide_metaball_octal_node(node->nodes[a], size_x, size_y, size_z, depth); } } } @@ -1976,7 +2034,7 @@ void init_metaball_octal_tree(int depth) size[2]= node->z_max - node->z_min; /* first node is subdivided recursively */ - subdivide_metaball_octal_node(node, size, metaball_tree->depth); + subdivide_metaball_octal_node(node, size[0], size[1], size[2], metaball_tree->depth); } void metaball_polygonize(Object *ob) diff --git a/source/blender/blenkernel/intern/mesh.c b/source/blender/blenkernel/intern/mesh.c index 1632f57c66e..07a1a5c5f44 100644 --- a/source/blender/blenkernel/intern/mesh.c +++ b/source/blender/blenkernel/intern/mesh.c @@ -78,45 +78,6 @@ #include "BLI_editVert.h" #include "BLI_arithb.h" -int update_realtime_texture(MTFace *tface, double time) -{ - Image *ima; - int inc = 0; - float diff; - int newframe; - - ima = tface->tpage; - - if (!ima) - return 0; - - if (ima->lastupdate<0) - ima->lastupdate = 0; - - if (ima->lastupdate>time) - ima->lastupdate=(float)time; - - if(ima->tpageflag & IMA_TWINANIM) { - if(ima->twend >= ima->xrep*ima->yrep) ima->twend= ima->xrep*ima->yrep-1; - - /* check: is the bindcode not in the array? Then free. (still to do) */ - - diff = (float)(time-ima->lastupdate); - - inc = (int)(diff*(float)ima->animspeed); - - ima->lastupdate+=((float)inc/(float)ima->animspeed); - - newframe = ima->lastframe+inc; - - if (newframe > (int)ima->twend) - newframe = (int)ima->twsta-1 + (newframe-ima->twend)%(ima->twend-ima->twsta); - - ima->lastframe = newframe; - } - return inc; -} - void mesh_update_customdata_pointers(Mesh *me) { me->mvert = CustomData_get_layer(&me->vdata, CD_MVERT); @@ -526,7 +487,7 @@ void transform_mesh_orco_verts(Mesh *me, float (*orco)[3], int totvert, int inve /* rotates the vertices of a face in case v[2] or v[3] (vertex index) is = 0. this is necessary to make the if(mface->v4) check for quads work */ -void test_index_face(MFace *mface, CustomData *fdata, int mfindex, int nr) +int test_index_face(MFace *mface, CustomData *fdata, int mfindex, int nr) { /* first test if the face is legal */ if(mface->v3 && mface->v3==mface->v4) { @@ -568,6 +529,8 @@ void test_index_face(MFace *mface, CustomData *fdata, int mfindex, int nr) CustomData_swap(fdata, mfindex, corner_indices); } } + + return nr; } Mesh *get_mesh(Object *ob) @@ -764,8 +727,7 @@ void mball_to_mesh(ListBase *lb, Mesh *me) mface->v4= index[3]; mface->flag= ME_SMOOTH; - if(mface->v3==mface->v4) - mface->v4= 0; + test_index_face(mface, NULL, 0, (mface->v3==mface->v4)? 3: 4); mface++; index+= 4; @@ -1104,9 +1066,13 @@ float (*mesh_getRefKeyCos(Mesh *me, int *numVerts_r))[3] if(me->key && me->key->refkey) { if(numVerts_r) *numVerts_r= me->totvert; - cos= MEM_mallocN(sizeof(*cos)*me->totvert, "vertexcos1"); - + kb= me->key->refkey; + + /* prevent accessing invalid memory */ + if (me->totvert > kb->totelem) cos= MEM_callocN(sizeof(*cos)*me->totvert, "vertexcos1"); + else cos= MEM_mallocN(sizeof(*cos)*me->totvert, "vertexcos1"); + totvert= MIN2(kb->totelem, me->totvert); memcpy(cos, kb->data, sizeof(*cos)*totvert); @@ -1136,12 +1102,12 @@ UvVertMap *make_uv_vert_map(struct MFace *mface, struct MTFace *tface, unsigned if(totuv==0) return NULL; - vmap= (UvVertMap*)MEM_mallocN(sizeof(*vmap), "UvVertMap"); + vmap= (UvVertMap*)MEM_callocN(sizeof(*vmap), "UvVertMap"); if (!vmap) return NULL; vmap->vert= (UvMapVert**)MEM_callocN(sizeof(*vmap->vert)*totvert, "UvMapVert*"); - buf= vmap->buf= (UvMapVert*)MEM_mallocN(sizeof(*vmap->buf)*totuv, "UvMapVert"); + buf= vmap->buf= (UvMapVert*)MEM_callocN(sizeof(*vmap->buf)*totuv, "UvMapVert"); if (!vmap->vert || !vmap->buf) { free_uv_vert_map(vmap); diff --git a/source/blender/blenkernel/intern/modifier.c b/source/blender/blenkernel/intern/modifier.c index 9b9e94774bc..eebd5e6d056 100644 --- a/source/blender/blenkernel/intern/modifier.c +++ b/source/blender/blenkernel/intern/modifier.c @@ -40,9 +40,11 @@ #include "stdarg.h" #include "math.h" #include "float.h" +#include "ctype.h" #include "BLI_arithb.h" #include "BLI_blenlib.h" +#include "BLI_kdopbvh.h" #include "BLI_kdtree.h" #include "BLI_linklist.h" #include "BLI_rand.h" @@ -52,7 +54,11 @@ #include "MEM_guardedalloc.h" +#include "DNA_action_types.h" #include "DNA_armature_types.h" +#include "DNA_camera_types.h" +#include "DNA_cloth_types.h" +#include "DNA_curve_types.h" #include "DNA_effect_types.h" #include "DNA_material_types.h" #include "DNA_mesh_types.h" @@ -63,8 +69,6 @@ #include "DNA_particle_types.h" #include "DNA_scene_types.h" #include "DNA_texture_types.h" -#include "DNA_curve_types.h" -#include "DNA_camera_types.h" #include "BLI_editVert.h" @@ -73,25 +77,34 @@ #include "BKE_main.h" #include "BKE_anim.h" +#include "BKE_bmesh.h" +#include "BKE_booleanops.h" +#include "BKE_cloth.h" +#include "BKE_collision.h" +#include "BKE_cdderivedmesh.h" #include "BKE_curve.h" #include "BKE_customdata.h" -#include "BKE_global.h" -#include "BKE_cdderivedmesh.h" #include "BKE_DerivedMesh.h" -#include "BKE_booleanops.h" #include "BKE_displist.h" -#include "BKE_modifier.h" +#include "BKE_fluidsim.h" +#include "BKE_global.h" #include "BKE_lattice.h" #include "BKE_library.h" -#include "BKE_subsurf.h" -#include "BKE_object.h" -#include "BKE_mesh.h" -#include "BKE_softbody.h" #include "BKE_material.h" +#include "BKE_mesh.h" +#include "BKE_modifier.h" +#include "BKE_object.h" #include "BKE_particle.h" #include "BKE_pointcache.h" +#include "BKE_softbody.h" +#include "BKE_subsurf.h" +#include "BKE_texture.h" #include "BKE_utildefines.h" + #include "depsgraph_private.h" +#include "BKE_deform.h" +#include "BKE_shrinkwrap.h" +#include "BKE_simple_deform.h" //XXX #include "LOD_DependKludge.h" #include "LOD_decimation.h" @@ -147,9 +160,9 @@ static int curveModifier_isDisabled(ModifierData *md) } static void curveModifier_foreachObjectLink( - ModifierData *md, Object *ob, - void (*walk)(void *userData, Object *ob, Object **obpoin), - void *userData) + ModifierData *md, Object *ob, + void (*walk)(void *userData, Object *ob, Object **obpoin), + void *userData) { CurveModifierData *cmd = (CurveModifierData*) md; @@ -157,8 +170,8 @@ static void curveModifier_foreachObjectLink( } static void curveModifier_updateDepgraph( - ModifierData *md, DagForest *forest, - Object *ob, DagNode *obNode) + ModifierData *md, DagForest *forest, + Object *ob, DagNode *obNode) { CurveModifierData *cmd = (CurveModifierData*) md; @@ -166,23 +179,23 @@ static void curveModifier_updateDepgraph( DagNode *curNode = dag_get_node(forest, cmd->object); dag_add_relation(forest, curNode, obNode, - DAG_RL_DATA_DATA | DAG_RL_OB_DATA); + DAG_RL_DATA_DATA | DAG_RL_OB_DATA, "Curve Modifier"); } } static void curveModifier_deformVerts( - ModifierData *md, Object *ob, DerivedMesh *derivedData, - float (*vertexCos)[3], int numVerts) + ModifierData *md, Object *ob, DerivedMesh *derivedData, + float (*vertexCos)[3], int numVerts) { CurveModifierData *cmd = (CurveModifierData*) md; curve_deform_verts(cmd->object, ob, derivedData, vertexCos, numVerts, - cmd->name, cmd->defaxis); + cmd->name, cmd->defaxis); } static void curveModifier_deformVertsEM( - ModifierData *md, Object *ob, EditMesh *editData, - DerivedMesh *derivedData, float (*vertexCos)[3], int numVerts) + ModifierData *md, Object *ob, EditMesh *editData, + DerivedMesh *derivedData, float (*vertexCos)[3], int numVerts) { DerivedMesh *dm = derivedData; @@ -223,9 +236,9 @@ static int latticeModifier_isDisabled(ModifierData *md) } static void latticeModifier_foreachObjectLink( - ModifierData *md, Object *ob, - void (*walk)(void *userData, Object *ob, Object **obpoin), - void *userData) + ModifierData *md, Object *ob, + void (*walk)(void *userData, Object *ob, Object **obpoin), + void *userData) { LatticeModifierData *lmd = (LatticeModifierData*) md; @@ -233,7 +246,7 @@ static void latticeModifier_foreachObjectLink( } static void latticeModifier_updateDepgraph(ModifierData *md, DagForest *forest, - Object *ob, DagNode *obNode) + Object *ob, DagNode *obNode) { LatticeModifierData *lmd = (LatticeModifierData*) md; @@ -241,7 +254,7 @@ static void latticeModifier_updateDepgraph(ModifierData *md, DagForest *forest, DagNode *latNode = dag_get_node(forest, lmd->object); dag_add_relation(forest, latNode, obNode, - DAG_RL_DATA_DATA | DAG_RL_OB_DATA); + DAG_RL_DATA_DATA | DAG_RL_OB_DATA, "Lattice Modifier"); } } @@ -260,8 +273,8 @@ static void modifier_vgroup_cache(ModifierData *md, float (*vertexCos)[3]) static void latticeModifier_deformVerts( - ModifierData *md, Object *ob, DerivedMesh *derivedData, - float (*vertexCos)[3], int numVerts) + ModifierData *md, Object *ob, DerivedMesh *derivedData, + float (*vertexCos)[3], int numVerts) { LatticeModifierData *lmd = (LatticeModifierData*) md; @@ -269,12 +282,12 @@ static void latticeModifier_deformVerts( modifier_vgroup_cache(md, vertexCos); /* if next modifier needs original vertices */ lattice_deform_verts(lmd->object, ob, derivedData, - vertexCos, numVerts, lmd->name); + vertexCos, numVerts, lmd->name); } static void latticeModifier_deformVertsEM( - ModifierData *md, Object *ob, EditMesh *editData, - DerivedMesh *derivedData, float (*vertexCos)[3], int numVerts) + ModifierData *md, Object *ob, EditMesh *editData, + DerivedMesh *derivedData, float (*vertexCos)[3], int numVerts) { DerivedMesh *dm = derivedData; @@ -320,28 +333,28 @@ static void subsurfModifier_freeData(ModifierData *md) } static DerivedMesh *subsurfModifier_applyModifier( - ModifierData *md, Object *ob, DerivedMesh *derivedData, - int useRenderParams, int isFinalCalc) + ModifierData *md, Object *ob, DerivedMesh *derivedData, + int useRenderParams, int isFinalCalc) { SubsurfModifierData *smd = (SubsurfModifierData*) md; DerivedMesh *result; result = subsurf_make_derived_from_derived(derivedData, smd, - useRenderParams, NULL, - isFinalCalc, 0); + useRenderParams, NULL, + isFinalCalc, 0); return result; } static DerivedMesh *subsurfModifier_applyModifierEM( - ModifierData *md, Object *ob, EditMesh *editData, - DerivedMesh *derivedData) + ModifierData *md, Object *ob, EditMesh *editData, + DerivedMesh *derivedData) { SubsurfModifierData *smd = (SubsurfModifierData*) md; DerivedMesh *result; result = subsurf_make_derived_from_derived(derivedData, smd, 0, - NULL, 0, 1); + NULL, 0, 1); return result; } @@ -373,8 +386,8 @@ static int buildModifier_dependsOnTime(ModifierData *md) } static DerivedMesh *buildModifier_applyModifier(ModifierData *md, Object *ob, - DerivedMesh *derivedData, - int useRenderParams, int isFinalCalc) + DerivedMesh *derivedData, + int useRenderParams, int isFinalCalc) { DerivedMesh *dm = derivedData; DerivedMesh *result; @@ -387,29 +400,29 @@ static DerivedMesh *buildModifier_applyModifier(ModifierData *md, Object *ob, GHashIterator *hashIter; /* maps vert indices in old mesh to indices in new mesh */ GHash *vertHash = BLI_ghash_new(BLI_ghashutil_inthash, - BLI_ghashutil_intcmp); + BLI_ghashutil_intcmp); /* maps edge indices in new mesh to indices in old mesh */ GHash *edgeHash = BLI_ghash_new(BLI_ghashutil_inthash, - BLI_ghashutil_intcmp); + BLI_ghashutil_intcmp); maxVerts = dm->getNumVerts(dm); vertMap = MEM_callocN(sizeof(*vertMap) * maxVerts, - "build modifier vertMap"); + "build modifier vertMap"); for(i = 0; i < maxVerts; ++i) vertMap[i] = i; maxEdges = dm->getNumEdges(dm); edgeMap = MEM_callocN(sizeof(*edgeMap) * maxEdges, - "build modifier edgeMap"); + "build modifier edgeMap"); for(i = 0; i < maxEdges; ++i) edgeMap[i] = i; maxFaces = dm->getNumFaces(dm); faceMap = MEM_callocN(sizeof(*faceMap) * maxFaces, - "build modifier faceMap"); + "build modifier faceMap"); for(i = 0; i < maxFaces; ++i) faceMap[i] = i; if (ob) { frac = bsystem_time(ob, (float)G.scene->r.cfra, - bmd->start - 1.0f) / bmd->length; + bmd->start - 1.0f) / bmd->length; } else { frac = G.scene->r.cfra - bmd->start / bmd->length; } @@ -424,156 +437,511 @@ static DerivedMesh *buildModifier_applyModifier(ModifierData *md, Object *ob, if(bmd->randomize) BLI_array_randomize(faceMap, sizeof(*faceMap), - maxFaces, bmd->seed); + maxFaces, bmd->seed); /* get the set of all vert indices that will be in the final mesh, - * mapped to the new indices - */ + * mapped to the new indices + */ for(i = 0; i < numFaces; ++i) { MFace mf; dm->getFace(dm, faceMap[i], &mf); - if(!BLI_ghash_haskey(vertHash, (void *)mf.v1)) - BLI_ghash_insert(vertHash, (void *)mf.v1, - (void *)BLI_ghash_size(vertHash)); - if(!BLI_ghash_haskey(vertHash, (void *)mf.v2)) - BLI_ghash_insert(vertHash, (void *)mf.v2, - (void *)BLI_ghash_size(vertHash)); - if(!BLI_ghash_haskey(vertHash, (void *)mf.v3)) - BLI_ghash_insert(vertHash, (void *)mf.v3, - (void *)BLI_ghash_size(vertHash)); - if(mf.v4 && !BLI_ghash_haskey(vertHash, (void *)mf.v4)) - BLI_ghash_insert(vertHash, (void *)mf.v4, - (void *)BLI_ghash_size(vertHash)); + if(!BLI_ghash_haskey(vertHash, SET_INT_IN_POINTER(mf.v1))) + BLI_ghash_insert(vertHash, SET_INT_IN_POINTER(mf.v1), + SET_INT_IN_POINTER(BLI_ghash_size(vertHash))); + if(!BLI_ghash_haskey(vertHash, SET_INT_IN_POINTER(mf.v2))) + BLI_ghash_insert(vertHash, SET_INT_IN_POINTER(mf.v2), + SET_INT_IN_POINTER(BLI_ghash_size(vertHash))); + if(!BLI_ghash_haskey(vertHash, SET_INT_IN_POINTER(mf.v3))) + BLI_ghash_insert(vertHash, SET_INT_IN_POINTER(mf.v3), + SET_INT_IN_POINTER(BLI_ghash_size(vertHash))); + if(mf.v4 && !BLI_ghash_haskey(vertHash, SET_INT_IN_POINTER(mf.v4))) + BLI_ghash_insert(vertHash, SET_INT_IN_POINTER(mf.v4), + SET_INT_IN_POINTER(BLI_ghash_size(vertHash))); } /* get the set of edges that will be in the new mesh (i.e. all edges - * that have both verts in the new mesh) - */ + * that have both verts in the new mesh) + */ maxEdges = dm->getNumEdges(dm); for(i = 0; i < maxEdges; ++i) { MEdge me; dm->getEdge(dm, i, &me); - if(BLI_ghash_haskey(vertHash, (void *)me.v1) - && BLI_ghash_haskey(vertHash, (void *)me.v2)) + if(BLI_ghash_haskey(vertHash, SET_INT_IN_POINTER(me.v1)) + && BLI_ghash_haskey(vertHash, SET_INT_IN_POINTER(me.v2))) BLI_ghash_insert(edgeHash, - (void *)BLI_ghash_size(edgeHash), (void *)i); + SET_INT_IN_POINTER(BLI_ghash_size(edgeHash)), SET_INT_IN_POINTER(i)); } } else if(numEdges) { if(bmd->randomize) BLI_array_randomize(edgeMap, sizeof(*edgeMap), - maxEdges, bmd->seed); + maxEdges, bmd->seed); /* get the set of all vert indices that will be in the final mesh, - * mapped to the new indices - */ + * mapped to the new indices + */ for(i = 0; i < numEdges; ++i) { MEdge me; dm->getEdge(dm, edgeMap[i], &me); - if(!BLI_ghash_haskey(vertHash, (void *)me.v1)) - BLI_ghash_insert(vertHash, (void *)me.v1, - (void *)BLI_ghash_size(vertHash)); - if(!BLI_ghash_haskey(vertHash, (void *)me.v2)) - BLI_ghash_insert(vertHash, (void *)me.v2, - (void *)BLI_ghash_size(vertHash)); + if(!BLI_ghash_haskey(vertHash, SET_INT_IN_POINTER(me.v1))) + BLI_ghash_insert(vertHash, SET_INT_IN_POINTER(me.v1), + SET_INT_IN_POINTER(BLI_ghash_size(vertHash))); + if(!BLI_ghash_haskey(vertHash, SET_INT_IN_POINTER(me.v2))) + BLI_ghash_insert(vertHash, SET_INT_IN_POINTER(me.v2), + SET_INT_IN_POINTER(BLI_ghash_size(vertHash))); } /* get the set of edges that will be in the new mesh - */ + */ for(i = 0; i < numEdges; ++i) { MEdge me; dm->getEdge(dm, edgeMap[i], &me); - BLI_ghash_insert(edgeHash, (void *)BLI_ghash_size(edgeHash), - (void *)edgeMap[i]); + BLI_ghash_insert(edgeHash, SET_INT_IN_POINTER(BLI_ghash_size(edgeHash)), + SET_INT_IN_POINTER(edgeMap[i])); } } else { int numVerts = dm->getNumVerts(dm) * frac; if(bmd->randomize) BLI_array_randomize(vertMap, sizeof(*vertMap), - maxVerts, bmd->seed); + maxVerts, bmd->seed); /* get the set of all vert indices that will be in the final mesh, - * mapped to the new indices - */ + * mapped to the new indices + */ for(i = 0; i < numVerts; ++i) - BLI_ghash_insert(vertHash, (void *)vertMap[i], (void *)i); + BLI_ghash_insert(vertHash, SET_INT_IN_POINTER(vertMap[i]), SET_INT_IN_POINTER(i)); } /* now we know the number of verts, edges and faces, we can create - * the mesh - */ + * the mesh + */ result = CDDM_from_template(dm, BLI_ghash_size(vertHash), - BLI_ghash_size(edgeHash), numFaces); + BLI_ghash_size(edgeHash), numFaces); /* copy the vertices across */ for(hashIter = BLI_ghashIterator_new(vertHash); - !BLI_ghashIterator_isDone(hashIter); - BLI_ghashIterator_step(hashIter)) { + !BLI_ghashIterator_isDone(hashIter); + BLI_ghashIterator_step(hashIter)) { + MVert source; + MVert *dest; + int oldIndex = GET_INT_FROM_POINTER(BLI_ghashIterator_getKey(hashIter)); + int newIndex = GET_INT_FROM_POINTER(BLI_ghashIterator_getValue(hashIter)); + + dm->getVert(dm, oldIndex, &source); + dest = CDDM_get_vert(result, newIndex); + + DM_copy_vert_data(dm, result, oldIndex, newIndex, 1); + *dest = source; + } + BLI_ghashIterator_free(hashIter); + + /* copy the edges across, remapping indices */ + for(i = 0; i < BLI_ghash_size(edgeHash); ++i) { + MEdge source; + MEdge *dest; + int oldIndex = GET_INT_FROM_POINTER(BLI_ghash_lookup(edgeHash, SET_INT_IN_POINTER(i))); + + dm->getEdge(dm, oldIndex, &source); + dest = CDDM_get_edge(result, i); + + source.v1 = GET_INT_FROM_POINTER(BLI_ghash_lookup(vertHash, SET_INT_IN_POINTER(source.v1))); + source.v2 = GET_INT_FROM_POINTER(BLI_ghash_lookup(vertHash, SET_INT_IN_POINTER(source.v2))); + + DM_copy_edge_data(dm, result, oldIndex, i, 1); + *dest = source; + } + + /* copy the faces across, remapping indices */ + for(i = 0; i < numFaces; ++i) { + MFace source; + MFace *dest; + int orig_v4; + + dm->getFace(dm, faceMap[i], &source); + dest = CDDM_get_face(result, i); + + orig_v4 = source.v4; + + source.v1 = GET_INT_FROM_POINTER(BLI_ghash_lookup(vertHash, SET_INT_IN_POINTER(source.v1))); + source.v2 = GET_INT_FROM_POINTER(BLI_ghash_lookup(vertHash, SET_INT_IN_POINTER(source.v2))); + source.v3 = GET_INT_FROM_POINTER(BLI_ghash_lookup(vertHash, SET_INT_IN_POINTER(source.v3))); + if(source.v4) + source.v4 = GET_INT_FROM_POINTER(BLI_ghash_lookup(vertHash, SET_INT_IN_POINTER(source.v4))); + + DM_copy_face_data(dm, result, faceMap[i], i, 1); + *dest = source; + + test_index_face(dest, &result->faceData, i, (orig_v4 ? 4 : 3)); + } + + CDDM_calc_normals(result); + + BLI_ghash_free(vertHash, NULL, NULL); + BLI_ghash_free(edgeHash, NULL, NULL); + + MEM_freeN(vertMap); + MEM_freeN(edgeMap); + MEM_freeN(faceMap); + + return result; +} + +/* Mask */ + +static void maskModifier_copyData(ModifierData *md, ModifierData *target) +{ + MaskModifierData *mmd = (MaskModifierData*) md; + MaskModifierData *tmmd = (MaskModifierData*) target; + + strcpy(tmmd->vgroup, mmd->vgroup); +} + +static CustomDataMask maskModifier_requiredDataMask(ModifierData *md) +{ + return (1 << CD_MDEFORMVERT); +} + +static void maskModifier_foreachObjectLink( + ModifierData *md, Object *ob, + void (*walk)(void *userData, Object *ob, Object **obpoin), + void *userData) +{ + MaskModifierData *mmd = (MaskModifierData *)md; + walk(userData, ob, &mmd->ob_arm); +} + +static void maskModifier_updateDepgraph(ModifierData *md, DagForest *forest, + Object *ob, DagNode *obNode) +{ + MaskModifierData *mmd = (MaskModifierData *)md; + + if (mmd->ob_arm) + { + DagNode *armNode = dag_get_node(forest, mmd->ob_arm); + + dag_add_relation(forest, armNode, obNode, + DAG_RL_DATA_DATA | DAG_RL_OB_DATA, "Mask Modifier"); + } +} + +static DerivedMesh *maskModifier_applyModifier(ModifierData *md, Object *ob, + DerivedMesh *derivedData, + int useRenderParams, int isFinalCalc) +{ + MaskModifierData *mmd= (MaskModifierData *)md; + DerivedMesh *dm= derivedData, *result= NULL; + GHash *vertHash=NULL, *edgeHash, *faceHash; + GHashIterator *hashIter; + MDeformVert *dvert= NULL; + int numFaces=0, numEdges=0, numVerts=0; + int maxVerts, maxEdges, maxFaces; + int i; + + /* Overview of Method: + * 1. Get the vertices that are in the vertexgroup of interest + * 2. Filter out unwanted geometry (i.e. not in vertexgroup), by populating mappings with new vs old indices + * 3. Make a new mesh containing only the mapping data + */ + + /* get original number of verts, edges, and faces */ + maxVerts= dm->getNumVerts(dm); + maxEdges= dm->getNumEdges(dm); + maxFaces= dm->getNumFaces(dm); + + /* check if we can just return the original mesh + * - must have verts and therefore verts assigned to vgroups to do anything useful + */ + if ( !(ELEM(mmd->mode, MOD_MASK_MODE_ARM, MOD_MASK_MODE_VGROUP)) || + (maxVerts == 0) || (ob->defbase.first == NULL) ) + { + return derivedData; + } + + /* if mode is to use selected armature bones, aggregate the bone groups */ + if (mmd->mode == MOD_MASK_MODE_ARM) /* --- using selected bones --- */ + { + GHash *vgroupHash, *boneHash; + Object *oba= mmd->ob_arm; + bPoseChannel *pchan; + bDeformGroup *def; + + /* check that there is armature object with bones to use, otherwise return original mesh */ + if (ELEM(NULL, mmd->ob_arm, mmd->ob_arm->pose)) + return derivedData; + + /* hashes for finding mapping of: + * - vgroups to indicies -> vgroupHash (string, int) + * - bones to vgroup indices -> boneHash (index of vgroup, dummy) + */ + vgroupHash= BLI_ghash_new(BLI_ghashutil_strhash, BLI_ghashutil_strcmp); + boneHash= BLI_ghash_new(BLI_ghashutil_inthash, BLI_ghashutil_intcmp); + + /* build mapping of names of vertex groups to indices */ + for (i = 0, def = ob->defbase.first; def; def = def->next, i++) + BLI_ghash_insert(vgroupHash, def->name, SET_INT_IN_POINTER(i)); + + /* get selected-posechannel <-> vertexgroup index mapping */ + for (pchan= oba->pose->chanbase.first; pchan; pchan= pchan->next) + { + /* check if bone is selected */ + // TODO: include checks for visibility too? + // FIXME: the depsgraph needs extensions to make this work in realtime... + if ( (pchan->bone) && (pchan->bone->flag & BONE_SELECTED) ) + { + /* check if hash has group for this bone */ + if (BLI_ghash_haskey(vgroupHash, pchan->name)) + { + int defgrp_index= GET_INT_FROM_POINTER(BLI_ghash_lookup(vgroupHash, pchan->name)); + + /* add index to hash (store under key only) */ + BLI_ghash_insert(boneHash, SET_INT_IN_POINTER(defgrp_index), pchan); + } + } + } + + /* if no bones selected, free hashes and return original mesh */ + if (BLI_ghash_size(boneHash) == 0) + { + BLI_ghash_free(vgroupHash, NULL, NULL); + BLI_ghash_free(boneHash, NULL, NULL); + + return derivedData; + } + + /* repeat the previous check, but for dverts */ + dvert= dm->getVertDataArray(dm, CD_MDEFORMVERT); + if (dvert == NULL) + { + BLI_ghash_free(vgroupHash, NULL, NULL); + BLI_ghash_free(boneHash, NULL, NULL); + + return derivedData; + } + + /* hashes for quickly providing a mapping from old to new - use key=oldindex, value=newindex */ + vertHash= BLI_ghash_new(BLI_ghashutil_inthash, BLI_ghashutil_intcmp); + + /* add vertices which exist in vertexgroups into vertHash for filtering */ + for (i = 0; i < maxVerts; i++) + { + MDeformWeight *def_weight = NULL; + int j; + + for (j= 0; j < dvert[i].totweight; j++) + { + if (BLI_ghash_haskey(boneHash, SET_INT_IN_POINTER(dvert[i].dw[j].def_nr))) + { + def_weight = &dvert[i].dw[j]; + break; + } + } + + /* check if include vert in vertHash */ + if (mmd->flag & MOD_MASK_INV) { + /* if this vert is in the vgroup, don't include it in vertHash */ + if (def_weight) continue; + } + else { + /* if this vert isn't in the vgroup, don't include it in vertHash */ + if (!def_weight) continue; + } + + /* add to ghash for verts (numVerts acts as counter for mapping) */ + BLI_ghash_insert(vertHash, SET_INT_IN_POINTER(i), SET_INT_IN_POINTER(numVerts)); + numVerts++; + } + + /* free temp hashes */ + BLI_ghash_free(vgroupHash, NULL, NULL); + BLI_ghash_free(boneHash, NULL, NULL); + } + else /* --- Using Nominated VertexGroup only --- */ + { + int defgrp_index = -1; + + /* get index of vertex group */ + if (mmd->vgroup[0]) + { + bDeformGroup *def; + + /* find index by comparing names - SLOW... */ + for (i = 0, def = ob->defbase.first; def; def = def->next, i++) + { + if (!strcmp(def->name, mmd->vgroup)) + { + defgrp_index = i; + break; + } + } + } + + /* get dverts */ + if (defgrp_index >= 0) + dvert = dm->getVertDataArray(dm, CD_MDEFORMVERT); + + /* if no vgroup (i.e. dverts) found, return the initial mesh */ + if ((defgrp_index < 0) || (dvert == NULL)) + return dm; + + /* hashes for quickly providing a mapping from old to new - use key=oldindex, value=newindex */ + vertHash= BLI_ghash_new(BLI_ghashutil_inthash, BLI_ghashutil_intcmp); + + /* add vertices which exist in vertexgroup into ghash for filtering */ + for (i = 0; i < maxVerts; i++) + { + MDeformWeight *def_weight = NULL; + int j; + + for (j= 0; j < dvert[i].totweight; j++) + { + if (dvert[i].dw[j].def_nr == defgrp_index) + { + def_weight = &dvert[i].dw[j]; + break; + } + } + + /* check if include vert in vertHash */ + if (mmd->flag & MOD_MASK_INV) { + /* if this vert is in the vgroup, don't include it in vertHash */ + if (def_weight) continue; + } + else { + /* if this vert isn't in the vgroup, don't include it in vertHash */ + if (!def_weight) continue; + } + + /* add to ghash for verts (numVerts acts as counter for mapping) */ + BLI_ghash_insert(vertHash, SET_INT_IN_POINTER(i), SET_INT_IN_POINTER(numVerts)); + numVerts++; + } + } + + /* hashes for quickly providing a mapping from old to new - use key=oldindex, value=newindex */ + edgeHash= BLI_ghash_new(BLI_ghashutil_inthash, BLI_ghashutil_intcmp); + faceHash= BLI_ghash_new(BLI_ghashutil_inthash, BLI_ghashutil_intcmp); + + /* loop over edges and faces, and do the same thing to + * ensure that they only reference existing verts + */ + for (i = 0; i < maxEdges; i++) + { + MEdge me; + dm->getEdge(dm, i, &me); + + /* only add if both verts will be in new mesh */ + if ( BLI_ghash_haskey(vertHash, SET_INT_IN_POINTER(me.v1)) && + BLI_ghash_haskey(vertHash, SET_INT_IN_POINTER(me.v2)) ) + { + BLI_ghash_insert(edgeHash, SET_INT_IN_POINTER(i), SET_INT_IN_POINTER(numEdges)); + numEdges++; + } + } + for (i = 0; i < maxFaces; i++) + { + MFace mf; + dm->getFace(dm, i, &mf); + + /* all verts must be available */ + if ( BLI_ghash_haskey(vertHash, SET_INT_IN_POINTER(mf.v1)) && + BLI_ghash_haskey(vertHash, SET_INT_IN_POINTER(mf.v2)) && + BLI_ghash_haskey(vertHash, SET_INT_IN_POINTER(mf.v3)) && + (mf.v4==0 || BLI_ghash_haskey(vertHash, SET_INT_IN_POINTER(mf.v4))) ) + { + BLI_ghash_insert(faceHash, SET_INT_IN_POINTER(i), SET_INT_IN_POINTER(numFaces)); + numFaces++; + } + } + + + /* now we know the number of verts, edges and faces, + * we can create the new (reduced) mesh + */ + result = CDDM_from_template(dm, numVerts, numEdges, numFaces); + + + /* using ghash-iterators, map data into new mesh */ + /* vertices */ + for ( hashIter = BLI_ghashIterator_new(vertHash); + !BLI_ghashIterator_isDone(hashIter); + BLI_ghashIterator_step(hashIter) ) + { MVert source; MVert *dest; - int oldIndex = (int)BLI_ghashIterator_getKey(hashIter); - int newIndex = (int)BLI_ghashIterator_getValue(hashIter); - + int oldIndex = GET_INT_FROM_POINTER(BLI_ghashIterator_getKey(hashIter)); + int newIndex = GET_INT_FROM_POINTER(BLI_ghashIterator_getValue(hashIter)); + dm->getVert(dm, oldIndex, &source); dest = CDDM_get_vert(result, newIndex); - + DM_copy_vert_data(dm, result, oldIndex, newIndex, 1); *dest = source; } BLI_ghashIterator_free(hashIter); - - /* copy the edges across, remapping indices */ - for(i = 0; i < BLI_ghash_size(edgeHash); ++i) { + + /* edges */ + for ( hashIter = BLI_ghashIterator_new(edgeHash); + !BLI_ghashIterator_isDone(hashIter); + BLI_ghashIterator_step(hashIter) ) + { MEdge source; MEdge *dest; - int oldIndex = (int)BLI_ghash_lookup(edgeHash, (void *)i); - + int oldIndex = GET_INT_FROM_POINTER(BLI_ghashIterator_getKey(hashIter)); + int newIndex = GET_INT_FROM_POINTER(BLI_ghashIterator_getValue(hashIter)); + dm->getEdge(dm, oldIndex, &source); - dest = CDDM_get_edge(result, i); - - source.v1 = (int)BLI_ghash_lookup(vertHash, (void *)source.v1); - source.v2 = (int)BLI_ghash_lookup(vertHash, (void *)source.v2); - - DM_copy_edge_data(dm, result, oldIndex, i, 1); + dest = CDDM_get_edge(result, newIndex); + + source.v1 = GET_INT_FROM_POINTER(BLI_ghash_lookup(vertHash, SET_INT_IN_POINTER(source.v1))); + source.v2 = GET_INT_FROM_POINTER(BLI_ghash_lookup(vertHash, SET_INT_IN_POINTER(source.v2))); + + DM_copy_edge_data(dm, result, oldIndex, newIndex, 1); *dest = source; } - - /* copy the faces across, remapping indices */ - for(i = 0; i < numFaces; ++i) { + BLI_ghashIterator_free(hashIter); + + /* faces */ + for ( hashIter = BLI_ghashIterator_new(faceHash); + !BLI_ghashIterator_isDone(hashIter); + BLI_ghashIterator_step(hashIter) ) + { MFace source; MFace *dest; + int oldIndex = GET_INT_FROM_POINTER(BLI_ghashIterator_getKey(hashIter)); + int newIndex = GET_INT_FROM_POINTER(BLI_ghashIterator_getValue(hashIter)); int orig_v4; - - dm->getFace(dm, faceMap[i], &source); - dest = CDDM_get_face(result, i); - + + dm->getFace(dm, oldIndex, &source); + dest = CDDM_get_face(result, newIndex); + orig_v4 = source.v4; - - source.v1 = (int)BLI_ghash_lookup(vertHash, (void *)source.v1); - source.v2 = (int)BLI_ghash_lookup(vertHash, (void *)source.v2); - source.v3 = (int)BLI_ghash_lookup(vertHash, (void *)source.v3); - if(source.v4) - source.v4 = (int)BLI_ghash_lookup(vertHash, (void *)source.v4); - - DM_copy_face_data(dm, result, faceMap[i], i, 1); + + source.v1 = GET_INT_FROM_POINTER(BLI_ghash_lookup(vertHash, SET_INT_IN_POINTER(source.v1))); + source.v2 = GET_INT_FROM_POINTER(BLI_ghash_lookup(vertHash, SET_INT_IN_POINTER(source.v2))); + source.v3 = GET_INT_FROM_POINTER(BLI_ghash_lookup(vertHash, SET_INT_IN_POINTER(source.v3))); + if (source.v4) + source.v4 = GET_INT_FROM_POINTER(BLI_ghash_lookup(vertHash, SET_INT_IN_POINTER(source.v4))); + + DM_copy_face_data(dm, result, oldIndex, newIndex, 1); *dest = source; - - test_index_face(dest, &result->faceData, i, (orig_v4 ? 4 : 3)); + + test_index_face(dest, &result->faceData, newIndex, (orig_v4 ? 4 : 3)); } - + BLI_ghashIterator_free(hashIter); + + /* recalculate normals */ CDDM_calc_normals(result); - + + /* free hashes */ BLI_ghash_free(vertHash, NULL, NULL); BLI_ghash_free(edgeHash, NULL, NULL); - - MEM_freeN(vertMap); - MEM_freeN(edgeMap); - MEM_freeN(faceMap); - + BLI_ghash_free(faceHash, NULL, NULL); + + /* return the new mesh */ return result; } @@ -586,7 +954,7 @@ static void arrayModifier_initData(ModifierData *md) ArrayModifierData *amd = (ArrayModifierData*) md; /* default to 2 duplicates distributed along the x-axis by an - offset of 1 object-width + offset of 1 object-width */ amd->start_cap = amd->end_cap = amd->curve_ob = amd->offset_ob = NULL; amd->count = 2; @@ -620,9 +988,9 @@ static void arrayModifier_copyData(ModifierData *md, ModifierData *target) } static void arrayModifier_foreachObjectLink( - ModifierData *md, Object *ob, - void (*walk)(void *userData, Object *ob, Object **obpoin), - void *userData) + ModifierData *md, Object *ob, + void (*walk)(void *userData, Object *ob, Object **obpoin), + void *userData) { ArrayModifierData *amd = (ArrayModifierData*) md; @@ -633,7 +1001,7 @@ static void arrayModifier_foreachObjectLink( } static void arrayModifier_updateDepgraph(ModifierData *md, DagForest *forest, - Object *ob, DagNode *obNode) + Object *ob, DagNode *obNode) { ArrayModifierData *amd = (ArrayModifierData*) md; @@ -641,25 +1009,25 @@ static void arrayModifier_updateDepgraph(ModifierData *md, DagForest *forest, DagNode *curNode = dag_get_node(forest, amd->start_cap); dag_add_relation(forest, curNode, obNode, - DAG_RL_DATA_DATA | DAG_RL_OB_DATA); + DAG_RL_DATA_DATA | DAG_RL_OB_DATA, "Array Modifier"); } if (amd->end_cap) { DagNode *curNode = dag_get_node(forest, amd->end_cap); dag_add_relation(forest, curNode, obNode, - DAG_RL_DATA_DATA | DAG_RL_OB_DATA); + DAG_RL_DATA_DATA | DAG_RL_OB_DATA, "Array Modifier"); } if (amd->curve_ob) { DagNode *curNode = dag_get_node(forest, amd->curve_ob); dag_add_relation(forest, curNode, obNode, - DAG_RL_DATA_DATA | DAG_RL_OB_DATA); + DAG_RL_DATA_DATA | DAG_RL_OB_DATA, "Array Modifier"); } if (amd->offset_ob) { DagNode *curNode = dag_get_node(forest, amd->offset_ob); dag_add_relation(forest, curNode, obNode, - DAG_RL_DATA_DATA | DAG_RL_OB_DATA); + DAG_RL_DATA_DATA | DAG_RL_OB_DATA, "Array Modifier"); } } @@ -686,12 +1054,12 @@ typedef struct IndexMapEntry { /* the new vert index that this old vert index maps to */ int new; /* -1 if this vert isn't merged, otherwise the old vert index it - * should be replaced with - */ + * should be replaced with + */ int merge; /* 1 if this vert's first copy is merged with the last copy of its - * merge target, otherwise 0 - */ + * merge target, otherwise 0 + */ short merge_final; } IndexMapEntry; @@ -710,19 +1078,19 @@ static int calc_mapping(IndexMapEntry *indexMap, int oldIndex, int copyNum) } else { /* vert was merged with another vert */ /* follow the chain of merges to the end, or until we've passed - * a number of vertices equal to the copy number - */ + * a number of vertices equal to the copy number + */ if(copyNum <= 0) return indexMap[oldIndex].new; else return calc_mapping(indexMap, indexMap[oldIndex].merge, - copyNum - 1); + copyNum - 1); } } static DerivedMesh *arrayModifier_doArray(ArrayModifierData *amd, - Object *ob, DerivedMesh *dm, - int initFlags) + Object *ob, DerivedMesh *dm, + int initFlags) { int i, j; /* offset matrix */ @@ -752,7 +1120,7 @@ static DerivedMesh *arrayModifier_doArray(ArrayModifierData *amd, MTC_Mat4One(offset); indexMap = MEM_callocN(sizeof(*indexMap) * dm->getNumVerts(dm), - "indexmap"); + "indexmap"); src_mvert = dm->getVertArray(dm); @@ -763,7 +1131,7 @@ static DerivedMesh *arrayModifier_doArray(ArrayModifierData *amd, if(amd->offset_type & MOD_ARR_OFF_RELATIVE) { for(j = 0; j < 3; j++) offset[3][j] += amd->scale[j] * vertarray_size(src_mvert, - maxVerts, j); + maxVerts, j); } if((amd->offset_type & MOD_ARR_OFF_OBJ) && (amd->offset_ob)) { @@ -776,472 +1144,509 @@ static DerivedMesh *arrayModifier_doArray(ArrayModifierData *amd, MTC_Mat4One(obinv); MTC_Mat4MulSerie(result_mat, offset, - obinv, amd->offset_ob->obmat, - NULL, NULL, NULL, NULL, NULL); + obinv, amd->offset_ob->obmat, + NULL, NULL, NULL, NULL, NULL); MTC_Mat4CpyMat4(offset, result_mat); } if(amd->fit_type == MOD_ARR_FITCURVE && amd->curve_ob) { Curve *cu = amd->curve_ob->data; if(cu) { + float tmp_mat[3][3]; + float scale; + + object_to_mat3(amd->curve_ob, tmp_mat); + scale = Mat3ToScalef(tmp_mat); + if(!cu->path) { cu->flag |= CU_PATH; // needed for path & bevlist makeDispListCurveTypes(amd->curve_ob, 0); } if(cu->path) - length = cu->path->totdist; + length = scale*cu->path->totdist; } } /* calculate the maximum number of copies which will fit within the - prescribed length */ + prescribed length */ if(amd->fit_type == MOD_ARR_FITLENGTH - || amd->fit_type == MOD_ARR_FITCURVE) { + || amd->fit_type == MOD_ARR_FITCURVE) { float dist = sqrt(MTC_dot3Float(offset[3], offset[3])); - if(dist > FLT_EPSILON) + if(dist > 1e-6f) /* this gives length = first copy start to last copy end - add a tiny offset for floating point rounding errors */ - count = (length + FLT_EPSILON) / dist; + add a tiny offset for floating point rounding errors */ + count = (length + 1e-6f) / dist; else /* if the offset has no translation, just make one copy */ count = 1; - } + } - if(count < 1) - count = 1; + if(count < 1) + count = 1; /* allocate memory for count duplicates (including original) plus - * start and end caps - */ - finalVerts = dm->getNumVerts(dm) * count; - finalEdges = dm->getNumEdges(dm) * count; - finalFaces = dm->getNumFaces(dm) * count; - if(start_cap) { - finalVerts += start_cap->getNumVerts(start_cap); - finalEdges += start_cap->getNumEdges(start_cap); - finalFaces += start_cap->getNumFaces(start_cap); - } - if(end_cap) { - finalVerts += end_cap->getNumVerts(end_cap); - finalEdges += end_cap->getNumEdges(end_cap); - finalFaces += end_cap->getNumFaces(end_cap); - } - result = CDDM_from_template(dm, finalVerts, finalEdges, finalFaces); - - /* calculate the offset matrix of the final copy (for merging) */ - MTC_Mat4One(final_offset); - - for(j=0; j < count - 1; j++) { - MTC_Mat4MulMat4(tmp_mat, final_offset, offset); - MTC_Mat4CpyMat4(final_offset, tmp_mat); - } - - numVerts = numEdges = numFaces = 0; - mvert = CDDM_get_verts(result); - - for (i = 0; i < maxVerts; i++) { - MVert *inMV; - MVert *mv = &mvert[numVerts]; - MVert *mv2; - float co[3]; - - inMV = &src_mvert[i]; - - DM_copy_vert_data(dm, result, i, numVerts, 1); - *mv = *inMV; - numVerts++; - - indexMap[i].new = numVerts - 1; - indexMap[i].merge = -1; /* default to no merge */ - indexMap[i].merge_final = 0; /* default to no merge */ - - VECCOPY(co, mv->co); + * start and end caps + */ + finalVerts = dm->getNumVerts(dm) * count; + finalEdges = dm->getNumEdges(dm) * count; + finalFaces = dm->getNumFaces(dm) * count; + if(start_cap) { + finalVerts += start_cap->getNumVerts(start_cap); + finalEdges += start_cap->getNumEdges(start_cap); + finalFaces += start_cap->getNumFaces(start_cap); + } + if(end_cap) { + finalVerts += end_cap->getNumVerts(end_cap); + finalEdges += end_cap->getNumEdges(end_cap); + finalFaces += end_cap->getNumFaces(end_cap); + } + result = CDDM_from_template(dm, finalVerts, finalEdges, finalFaces); + + /* calculate the offset matrix of the final copy (for merging) */ + MTC_Mat4One(final_offset); + + for(j=0; j < count - 1; j++) { + MTC_Mat4MulMat4(tmp_mat, final_offset, offset); + MTC_Mat4CpyMat4(final_offset, tmp_mat); + } + + numVerts = numEdges = numFaces = 0; + mvert = CDDM_get_verts(result); + + for (i = 0; i < maxVerts; i++) { + indexMap[i].merge = -1; /* default to no merge */ + indexMap[i].merge_final = 0; /* default to no merge */ + } + + for (i = 0; i < maxVerts; i++) { + MVert *inMV; + MVert *mv = &mvert[numVerts]; + MVert *mv2; + float co[3]; + + inMV = &src_mvert[i]; + + DM_copy_vert_data(dm, result, i, numVerts, 1); + *mv = *inMV; + numVerts++; + + indexMap[i].new = numVerts - 1; + + VECCOPY(co, mv->co); /* Attempts to merge verts from one duplicate with verts from the - * next duplicate which are closer than amd->merge_dist. - * Only the first such vert pair is merged. - * If verts are merged in the first duplicate pair, they are merged - * in all pairs. - */ - if((count > 1) && (amd->flags & MOD_ARR_MERGE)) { - float tmp_co[3]; - VECCOPY(tmp_co, mv->co); - MTC_Mat4MulVecfl(offset, tmp_co); - - for(j = 0; j < maxVerts; j++) { - inMV = &src_mvert[j]; - /* if this vert is within merge limit, merge */ - if(VecLenCompare(tmp_co, inMV->co, amd->merge_dist)) { - indexMap[i].merge = j; - - /* test for merging with final copy of merge target */ - if(amd->flags & MOD_ARR_MERGEFINAL) { - VECCOPY(tmp_co, inMV->co); - inMV = &src_mvert[i]; - MTC_Mat4MulVecfl(final_offset, tmp_co); - if(VecLenCompare(tmp_co, inMV->co, amd->merge_dist)) - indexMap[i].merge_final = 1; - } - break; - } - } - } - - /* if no merging, generate copies of this vert */ - if(indexMap[i].merge < 0) { - for(j=0; j < count - 1; j++) { - mv2 = &mvert[numVerts]; - - DM_copy_vert_data(result, result, numVerts - 1, numVerts, 1); - *mv2 = *mv; - numVerts++; - - MTC_Mat4MulVecfl(offset, co); - VECCOPY(mv2->co, co); - } - } else if(indexMap[i].merge != i && indexMap[i].merge_final) { + * next duplicate which are closer than amd->merge_dist. + * Only the first such vert pair is merged. + * If verts are merged in the first duplicate pair, they are merged + * in all pairs. + */ + if((count > 1) && (amd->flags & MOD_ARR_MERGE)) { + float tmp_co[3]; + VECCOPY(tmp_co, mv->co); + MTC_Mat4MulVecfl(offset, tmp_co); + + for(j = 0; j < maxVerts; j++) { + /* if vertex already merged, don't use it */ + if( indexMap[j].merge != -1 ) continue; + + inMV = &src_mvert[j]; + /* if this vert is within merge limit, merge */ + if(VecLenCompare(tmp_co, inMV->co, amd->merge_dist)) { + indexMap[i].merge = j; + + /* test for merging with final copy of merge target */ + if(amd->flags & MOD_ARR_MERGEFINAL) { + VECCOPY(tmp_co, inMV->co); + inMV = &src_mvert[i]; + MTC_Mat4MulVecfl(final_offset, tmp_co); + if(VecLenCompare(tmp_co, inMV->co, amd->merge_dist)) + indexMap[i].merge_final = 1; + } + break; + } + } + } + + /* if no merging, generate copies of this vert */ + if(indexMap[i].merge < 0) { + for(j=0; j < count - 1; j++) { + mv2 = &mvert[numVerts]; + + DM_copy_vert_data(result, result, numVerts - 1, numVerts, 1); + *mv2 = *mv; + numVerts++; + + MTC_Mat4MulVecfl(offset, co); + VECCOPY(mv2->co, co); + } + } else if(indexMap[i].merge != i && indexMap[i].merge_final) { /* if this vert is not merging with itself, and it is merging - * with the final copy of its merge target, remove the first copy - */ - numVerts--; - DM_free_vert_data(result, numVerts, 1); - } - } - - /* make a hashtable so we can avoid duplicate edges from merging */ - edges = BLI_edgehash_new(); - - maxEdges = dm->getNumEdges(dm); - medge = CDDM_get_edges(result); - for(i = 0; i < maxEdges; i++) { - MEdge inMED; - MEdge med; - MEdge *med2; - int vert1, vert2; - - dm->getEdge(dm, i, &inMED); - - med = inMED; - med.v1 = indexMap[inMED.v1].new; - med.v2 = indexMap[inMED.v2].new; + * with the final copy of its merge target, remove the first copy + */ + numVerts--; + DM_free_vert_data(result, numVerts, 1); + } + } + + /* make a hashtable so we can avoid duplicate edges from merging */ + edges = BLI_edgehash_new(); + + maxEdges = dm->getNumEdges(dm); + medge = CDDM_get_edges(result); + for(i = 0; i < maxEdges; i++) { + MEdge inMED; + MEdge med; + MEdge *med2; + int vert1, vert2; + + dm->getEdge(dm, i, &inMED); + + med = inMED; + med.v1 = indexMap[inMED.v1].new; + med.v2 = indexMap[inMED.v2].new; /* if vertices are to be merged with the final copies of their - * merge targets, calculate that final copy - */ - if(indexMap[inMED.v1].merge_final) { - med.v1 = calc_mapping(indexMap, indexMap[inMED.v1].merge, - count - 1); - } - if(indexMap[inMED.v2].merge_final) { - med.v2 = calc_mapping(indexMap, indexMap[inMED.v2].merge, - count - 1); - } - - if (initFlags) { - med.flag |= ME_EDGEDRAW | ME_EDGERENDER; - } - - if(!BLI_edgehash_haskey(edges, med.v1, med.v2)) { - DM_copy_edge_data(dm, result, i, numEdges, 1); - medge[numEdges] = med; - numEdges++; - - BLI_edgehash_insert(edges, med.v1, med.v2, NULL); - } - - for(j = 1; j < count; j++) - { - vert1 = calc_mapping(indexMap, inMED.v1, j); - vert2 = calc_mapping(indexMap, inMED.v2, j); - /* avoid duplicate edges */ - if(!BLI_edgehash_haskey(edges, vert1, vert2)) { - med2 = &medge[numEdges]; - - DM_copy_edge_data(dm, result, i, numEdges, 1); - *med2 = med; - numEdges++; - - med2->v1 = vert1; - med2->v2 = vert2; - - BLI_edgehash_insert(edges, med2->v1, med2->v2, NULL); - } - } - } - - maxFaces = dm->getNumFaces(dm); - mface = CDDM_get_faces(result); - for (i=0; i < maxFaces; i++) { - MFace inMF; - MFace *mf = &mface[numFaces]; - - dm->getFace(dm, i, &inMF); - - DM_copy_face_data(dm, result, i, numFaces, 1); - *mf = inMF; - - mf->v1 = indexMap[inMF.v1].new; - mf->v2 = indexMap[inMF.v2].new; - mf->v3 = indexMap[inMF.v3].new; - if(inMF.v4) - mf->v4 = indexMap[inMF.v4].new; + * merge targets, calculate that final copy + */ + if(indexMap[inMED.v1].merge_final) { + med.v1 = calc_mapping(indexMap, indexMap[inMED.v1].merge, + count - 1); + } + if(indexMap[inMED.v2].merge_final) { + med.v2 = calc_mapping(indexMap, indexMap[inMED.v2].merge, + count - 1); + } + + if(med.v1 == med.v2) continue; + + if (initFlags) { + med.flag |= ME_EDGEDRAW | ME_EDGERENDER; + } + + if(!BLI_edgehash_haskey(edges, med.v1, med.v2)) { + DM_copy_edge_data(dm, result, i, numEdges, 1); + medge[numEdges] = med; + numEdges++; + + BLI_edgehash_insert(edges, med.v1, med.v2, NULL); + } + + for(j = 1; j < count; j++) + { + vert1 = calc_mapping(indexMap, inMED.v1, j); + vert2 = calc_mapping(indexMap, inMED.v2, j); + /* avoid duplicate edges */ + if(!BLI_edgehash_haskey(edges, vert1, vert2)) { + med2 = &medge[numEdges]; + + DM_copy_edge_data(dm, result, i, numEdges, 1); + *med2 = med; + numEdges++; + + med2->v1 = vert1; + med2->v2 = vert2; + + BLI_edgehash_insert(edges, med2->v1, med2->v2, NULL); + } + } + } + + maxFaces = dm->getNumFaces(dm); + mface = CDDM_get_faces(result); + for (i=0; i < maxFaces; i++) { + MFace inMF; + MFace *mf = &mface[numFaces]; + + dm->getFace(dm, i, &inMF); + + DM_copy_face_data(dm, result, i, numFaces, 1); + *mf = inMF; + + mf->v1 = indexMap[inMF.v1].new; + mf->v2 = indexMap[inMF.v2].new; + mf->v3 = indexMap[inMF.v3].new; + if(inMF.v4) + mf->v4 = indexMap[inMF.v4].new; /* if vertices are to be merged with the final copies of their - * merge targets, calculate that final copy - */ - if(indexMap[inMF.v1].merge_final) - mf->v1 = calc_mapping(indexMap, indexMap[inMF.v1].merge, count-1); - if(indexMap[inMF.v2].merge_final) - mf->v2 = calc_mapping(indexMap, indexMap[inMF.v2].merge, count-1); - if(indexMap[inMF.v3].merge_final) - mf->v3 = calc_mapping(indexMap, indexMap[inMF.v3].merge, count-1); - if(inMF.v4 && indexMap[inMF.v4].merge_final) - mf->v4 = calc_mapping(indexMap, indexMap[inMF.v4].merge, count-1); - - test_index_face(mf, &result->faceData, numFaces, inMF.v4?4:3); - numFaces++; - - /* if the face has fewer than 3 vertices, don't create it */ - if(mf->v3 == 0) { - numFaces--; - DM_free_face_data(result, numFaces, 1); - } - - for(j = 1; j < count; j++) - { - MFace *mf2 = &mface[numFaces]; - - DM_copy_face_data(dm, result, i, numFaces, 1); - *mf2 = *mf; - - mf2->v1 = calc_mapping(indexMap, inMF.v1, j); - mf2->v2 = calc_mapping(indexMap, inMF.v2, j); - mf2->v3 = calc_mapping(indexMap, inMF.v3, j); - if (inMF.v4) - mf2->v4 = calc_mapping(indexMap, inMF.v4, j); - - test_index_face(mf2, &result->faceData, numFaces, inMF.v4?4:3); - numFaces++; - - /* if the face has fewer than 3 vertices, don't create it */ - if(mf2->v3 == 0) { - numFaces--; - DM_free_face_data(result, numFaces, 1); - } - } - } - - /* add start and end caps */ - if(start_cap) { - float startoffset[4][4]; - MVert *cap_mvert; - MEdge *cap_medge; - MFace *cap_mface; - int *origindex; - int *vert_map; - int capVerts, capEdges, capFaces; - - capVerts = start_cap->getNumVerts(start_cap); - capEdges = start_cap->getNumEdges(start_cap); - capFaces = start_cap->getNumFaces(start_cap); - cap_mvert = start_cap->getVertArray(start_cap); - cap_medge = start_cap->getEdgeArray(start_cap); - cap_mface = start_cap->getFaceArray(start_cap); - - Mat4Invert(startoffset, offset); - - vert_map = MEM_callocN(sizeof(*vert_map) * capVerts, - "arrayModifier_doArray vert_map"); - - origindex = result->getVertDataArray(result, CD_ORIGINDEX); - for(i = 0; i < capVerts; i++) { - MVert *mv = &cap_mvert[i]; - short merged = 0; - - if(amd->flags & MOD_ARR_MERGE) { - float tmp_co[3]; - MVert *in_mv; - int j; - - VECCOPY(tmp_co, mv->co); - Mat4MulVecfl(startoffset, tmp_co); - - for(j = 0; j < maxVerts; j++) { - in_mv = &src_mvert[j]; - /* if this vert is within merge limit, merge */ - if(VecLenCompare(tmp_co, in_mv->co, amd->merge_dist)) { - vert_map[i] = calc_mapping(indexMap, j, 0); - merged = 1; - break; - } - } - } - - if(!merged) { - DM_copy_vert_data(start_cap, result, i, numVerts, 1); - mvert[numVerts] = *mv; - Mat4MulVecfl(startoffset, mvert[numVerts].co); - origindex[numVerts] = ORIGINDEX_NONE; - - vert_map[i] = numVerts; - - numVerts++; - } - } - origindex = result->getEdgeDataArray(result, CD_ORIGINDEX); - for(i = 0; i < capEdges; i++) { - int v1, v2; - - v1 = vert_map[cap_medge[i].v1]; - v2 = vert_map[cap_medge[i].v2]; - - if(!BLI_edgehash_haskey(edges, v1, v2)) { - DM_copy_edge_data(start_cap, result, i, numEdges, 1); - medge[numEdges] = cap_medge[i]; - medge[numEdges].v1 = v1; - medge[numEdges].v2 = v2; - origindex[numEdges] = ORIGINDEX_NONE; - - numEdges++; - } - } - origindex = result->getFaceDataArray(result, CD_ORIGINDEX); - for(i = 0; i < capFaces; i++) { - DM_copy_face_data(start_cap, result, i, numFaces, 1); - mface[numFaces] = cap_mface[i]; - mface[numFaces].v1 = vert_map[mface[numFaces].v1]; - mface[numFaces].v2 = vert_map[mface[numFaces].v2]; - mface[numFaces].v3 = vert_map[mface[numFaces].v3]; - if(mface[numFaces].v4) - mface[numFaces].v4 = vert_map[mface[numFaces].v4]; - origindex[numFaces] = ORIGINDEX_NONE; - - numFaces++; - } - - MEM_freeN(vert_map); - start_cap->release(start_cap); - } - - if(end_cap) { - float endoffset[4][4]; - MVert *cap_mvert; - MEdge *cap_medge; - MFace *cap_mface; - int *origindex; - int *vert_map; - int capVerts, capEdges, capFaces; - - capVerts = end_cap->getNumVerts(end_cap); - capEdges = end_cap->getNumEdges(end_cap); - capFaces = end_cap->getNumFaces(end_cap); - cap_mvert = end_cap->getVertArray(end_cap); - cap_medge = end_cap->getEdgeArray(end_cap); - cap_mface = end_cap->getFaceArray(end_cap); - - Mat4MulMat4(endoffset, final_offset, offset); - - vert_map = MEM_callocN(sizeof(*vert_map) * capVerts, - "arrayModifier_doArray vert_map"); - - origindex = result->getVertDataArray(result, CD_ORIGINDEX); - for(i = 0; i < capVerts; i++) { - MVert *mv = &cap_mvert[i]; - short merged = 0; - - if(amd->flags & MOD_ARR_MERGE) { - float tmp_co[3]; - MVert *in_mv; - int j; - - VECCOPY(tmp_co, mv->co); - Mat4MulVecfl(offset, tmp_co); - - for(j = 0; j < maxVerts; j++) { - in_mv = &src_mvert[j]; - /* if this vert is within merge limit, merge */ - if(VecLenCompare(tmp_co, in_mv->co, amd->merge_dist)) { - vert_map[i] = calc_mapping(indexMap, j, count - 1); - merged = 1; - break; - } - } - } - - if(!merged) { - DM_copy_vert_data(end_cap, result, i, numVerts, 1); - mvert[numVerts] = *mv; - Mat4MulVecfl(endoffset, mvert[numVerts].co); - origindex[numVerts] = ORIGINDEX_NONE; - - vert_map[i] = numVerts; - - numVerts++; - } - } - origindex = result->getEdgeDataArray(result, CD_ORIGINDEX); - for(i = 0; i < capEdges; i++) { - int v1, v2; - - v1 = vert_map[cap_medge[i].v1]; - v2 = vert_map[cap_medge[i].v2]; - - if(!BLI_edgehash_haskey(edges, v1, v2)) { - DM_copy_edge_data(end_cap, result, i, numEdges, 1); - medge[numEdges] = cap_medge[i]; - medge[numEdges].v1 = v1; - medge[numEdges].v2 = v2; - origindex[numEdges] = ORIGINDEX_NONE; - - numEdges++; - } - } - origindex = result->getFaceDataArray(result, CD_ORIGINDEX); - for(i = 0; i < capFaces; i++) { - DM_copy_face_data(end_cap, result, i, numFaces, 1); - mface[numFaces] = cap_mface[i]; - mface[numFaces].v1 = vert_map[mface[numFaces].v1]; - mface[numFaces].v2 = vert_map[mface[numFaces].v2]; - mface[numFaces].v3 = vert_map[mface[numFaces].v3]; - if(mface[numFaces].v4) - mface[numFaces].v4 = vert_map[mface[numFaces].v4]; - origindex[numFaces] = ORIGINDEX_NONE; - - numFaces++; - } - - MEM_freeN(vert_map); - end_cap->release(end_cap); - } - - BLI_edgehash_free(edges, NULL); - MEM_freeN(indexMap); - - CDDM_lower_num_verts(result, numVerts); - CDDM_lower_num_edges(result, numEdges); - CDDM_lower_num_faces(result, numFaces); - - return result; + * merge targets, calculate that final copy + */ + if(indexMap[inMF.v1].merge_final) + mf->v1 = calc_mapping(indexMap, indexMap[inMF.v1].merge, count-1); + if(indexMap[inMF.v2].merge_final) + mf->v2 = calc_mapping(indexMap, indexMap[inMF.v2].merge, count-1); + if(indexMap[inMF.v3].merge_final) + mf->v3 = calc_mapping(indexMap, indexMap[inMF.v3].merge, count-1); + if(inMF.v4 && indexMap[inMF.v4].merge_final) + mf->v4 = calc_mapping(indexMap, indexMap[inMF.v4].merge, count-1); + + if(test_index_face(mf, &result->faceData, numFaces, inMF.v4?4:3) < 3) + continue; + + numFaces++; + + /* if the face has fewer than 3 vertices, don't create it */ + if(mf->v3 == 0 || (mf->v1 && (mf->v1 == mf->v3 || mf->v1 == mf->v4))) { + numFaces--; + DM_free_face_data(result, numFaces, 1); + } + + for(j = 1; j < count; j++) + { + MFace *mf2 = &mface[numFaces]; + + DM_copy_face_data(dm, result, i, numFaces, 1); + *mf2 = *mf; + + mf2->v1 = calc_mapping(indexMap, inMF.v1, j); + mf2->v2 = calc_mapping(indexMap, inMF.v2, j); + mf2->v3 = calc_mapping(indexMap, inMF.v3, j); + if (inMF.v4) + mf2->v4 = calc_mapping(indexMap, inMF.v4, j); + + test_index_face(mf2, &result->faceData, numFaces, inMF.v4?4:3); + numFaces++; + + /* if the face has fewer than 3 vertices, don't create it */ + if(mf2->v3 == 0 || (mf2->v1 && (mf2->v1 == mf2->v3 || mf2->v1 == + mf2->v4))) { + numFaces--; + DM_free_face_data(result, numFaces, 1); + } + } + } + + /* add start and end caps */ + if(start_cap) { + float startoffset[4][4]; + MVert *cap_mvert; + MEdge *cap_medge; + MFace *cap_mface; + int *origindex; + int *vert_map; + int capVerts, capEdges, capFaces; + + capVerts = start_cap->getNumVerts(start_cap); + capEdges = start_cap->getNumEdges(start_cap); + capFaces = start_cap->getNumFaces(start_cap); + cap_mvert = start_cap->getVertArray(start_cap); + cap_medge = start_cap->getEdgeArray(start_cap); + cap_mface = start_cap->getFaceArray(start_cap); + + Mat4Invert(startoffset, offset); + + vert_map = MEM_callocN(sizeof(*vert_map) * capVerts, + "arrayModifier_doArray vert_map"); + + origindex = result->getVertDataArray(result, CD_ORIGINDEX); + for(i = 0; i < capVerts; i++) { + MVert *mv = &cap_mvert[i]; + short merged = 0; + + if(amd->flags & MOD_ARR_MERGE) { + float tmp_co[3]; + MVert *in_mv; + int j; + + VECCOPY(tmp_co, mv->co); + Mat4MulVecfl(startoffset, tmp_co); + + for(j = 0; j < maxVerts; j++) { + in_mv = &src_mvert[j]; + /* if this vert is within merge limit, merge */ + if(VecLenCompare(tmp_co, in_mv->co, amd->merge_dist)) { + vert_map[i] = calc_mapping(indexMap, j, 0); + merged = 1; + break; + } + } + } + + if(!merged) { + DM_copy_vert_data(start_cap, result, i, numVerts, 1); + mvert[numVerts] = *mv; + Mat4MulVecfl(startoffset, mvert[numVerts].co); + origindex[numVerts] = ORIGINDEX_NONE; + + vert_map[i] = numVerts; + + numVerts++; + } + } + origindex = result->getEdgeDataArray(result, CD_ORIGINDEX); + for(i = 0; i < capEdges; i++) { + int v1, v2; + + v1 = vert_map[cap_medge[i].v1]; + v2 = vert_map[cap_medge[i].v2]; + + if(!BLI_edgehash_haskey(edges, v1, v2)) { + DM_copy_edge_data(start_cap, result, i, numEdges, 1); + medge[numEdges] = cap_medge[i]; + medge[numEdges].v1 = v1; + medge[numEdges].v2 = v2; + origindex[numEdges] = ORIGINDEX_NONE; + + numEdges++; + } + } + origindex = result->getFaceDataArray(result, CD_ORIGINDEX); + for(i = 0; i < capFaces; i++) { + DM_copy_face_data(start_cap, result, i, numFaces, 1); + mface[numFaces] = cap_mface[i]; + mface[numFaces].v1 = vert_map[mface[numFaces].v1]; + mface[numFaces].v2 = vert_map[mface[numFaces].v2]; + mface[numFaces].v3 = vert_map[mface[numFaces].v3]; + if(mface[numFaces].v4) { + mface[numFaces].v4 = vert_map[mface[numFaces].v4]; + + test_index_face(&mface[numFaces], &result->faceData, + numFaces, 4); + } + else + { + test_index_face(&mface[numFaces], &result->faceData, + numFaces, 3); + } + + origindex[numFaces] = ORIGINDEX_NONE; + + numFaces++; + } + + MEM_freeN(vert_map); + start_cap->release(start_cap); + } + + if(end_cap) { + float endoffset[4][4]; + MVert *cap_mvert; + MEdge *cap_medge; + MFace *cap_mface; + int *origindex; + int *vert_map; + int capVerts, capEdges, capFaces; + + capVerts = end_cap->getNumVerts(end_cap); + capEdges = end_cap->getNumEdges(end_cap); + capFaces = end_cap->getNumFaces(end_cap); + cap_mvert = end_cap->getVertArray(end_cap); + cap_medge = end_cap->getEdgeArray(end_cap); + cap_mface = end_cap->getFaceArray(end_cap); + + Mat4MulMat4(endoffset, final_offset, offset); + + vert_map = MEM_callocN(sizeof(*vert_map) * capVerts, + "arrayModifier_doArray vert_map"); + + origindex = result->getVertDataArray(result, CD_ORIGINDEX); + for(i = 0; i < capVerts; i++) { + MVert *mv = &cap_mvert[i]; + short merged = 0; + + if(amd->flags & MOD_ARR_MERGE) { + float tmp_co[3]; + MVert *in_mv; + int j; + + VECCOPY(tmp_co, mv->co); + Mat4MulVecfl(offset, tmp_co); + + for(j = 0; j < maxVerts; j++) { + in_mv = &src_mvert[j]; + /* if this vert is within merge limit, merge */ + if(VecLenCompare(tmp_co, in_mv->co, amd->merge_dist)) { + vert_map[i] = calc_mapping(indexMap, j, count - 1); + merged = 1; + break; + } + } + } + + if(!merged) { + DM_copy_vert_data(end_cap, result, i, numVerts, 1); + mvert[numVerts] = *mv; + Mat4MulVecfl(endoffset, mvert[numVerts].co); + origindex[numVerts] = ORIGINDEX_NONE; + + vert_map[i] = numVerts; + + numVerts++; + } + } + origindex = result->getEdgeDataArray(result, CD_ORIGINDEX); + for(i = 0; i < capEdges; i++) { + int v1, v2; + + v1 = vert_map[cap_medge[i].v1]; + v2 = vert_map[cap_medge[i].v2]; + + if(!BLI_edgehash_haskey(edges, v1, v2)) { + DM_copy_edge_data(end_cap, result, i, numEdges, 1); + medge[numEdges] = cap_medge[i]; + medge[numEdges].v1 = v1; + medge[numEdges].v2 = v2; + origindex[numEdges] = ORIGINDEX_NONE; + + numEdges++; + } + } + origindex = result->getFaceDataArray(result, CD_ORIGINDEX); + for(i = 0; i < capFaces; i++) { + DM_copy_face_data(end_cap, result, i, numFaces, 1); + mface[numFaces] = cap_mface[i]; + mface[numFaces].v1 = vert_map[mface[numFaces].v1]; + mface[numFaces].v2 = vert_map[mface[numFaces].v2]; + mface[numFaces].v3 = vert_map[mface[numFaces].v3]; + if(mface[numFaces].v4) { + mface[numFaces].v4 = vert_map[mface[numFaces].v4]; + + test_index_face(&mface[numFaces], &result->faceData, + numFaces, 4); + } + else + { + test_index_face(&mface[numFaces], &result->faceData, + numFaces, 3); + } + origindex[numFaces] = ORIGINDEX_NONE; + + numFaces++; + } + + MEM_freeN(vert_map); + end_cap->release(end_cap); + } + + BLI_edgehash_free(edges, NULL); + MEM_freeN(indexMap); + + CDDM_lower_num_verts(result, numVerts); + CDDM_lower_num_edges(result, numEdges); + CDDM_lower_num_faces(result, numFaces); + + return result; } static DerivedMesh *arrayModifier_applyModifier( - ModifierData *md, Object *ob, DerivedMesh *derivedData, - int useRenderParams, int isFinalCalc) + ModifierData *md, Object *ob, DerivedMesh *derivedData, + int useRenderParams, int isFinalCalc) { DerivedMesh *result; ArrayModifierData *amd = (ArrayModifierData*) md; result = arrayModifier_doArray(amd, ob, derivedData, 0); - CDDM_calc_normals(result); + if(result != derivedData) + CDDM_calc_normals(result); return result; } static DerivedMesh *arrayModifier_applyModifierEM( - ModifierData *md, Object *ob, EditMesh *editData, - DerivedMesh *derivedData) + ModifierData *md, Object *ob, EditMesh *editData, + DerivedMesh *derivedData) { return arrayModifier_applyModifier(md, ob, derivedData, 0, 1); } @@ -1252,7 +1657,7 @@ static void mirrorModifier_initData(ModifierData *md) { MirrorModifierData *mmd = (MirrorModifierData*) md; - mmd->flag |= MOD_MIR_AXIS_X; + mmd->flag |= (MOD_MIR_AXIS_X | MOD_MIR_VGROUP); mmd->tolerance = 0.001; mmd->mirror_ob = NULL; } @@ -1269,9 +1674,9 @@ static void mirrorModifier_copyData(ModifierData *md, ModifierData *target) } static void mirrorModifier_foreachObjectLink( - ModifierData *md, Object *ob, - void (*walk)(void *userData, Object *ob, Object **obpoin), - void *userData) + ModifierData *md, Object *ob, + void (*walk)(void *userData, Object *ob, Object **obpoin), + void *userData) { MirrorModifierData *mmd = (MirrorModifierData*) md; @@ -1279,7 +1684,7 @@ static void mirrorModifier_foreachObjectLink( } static void mirrorModifier_updateDepgraph(ModifierData *md, DagForest *forest, - Object *ob, DagNode *obNode) + Object *ob, DagNode *obNode) { MirrorModifierData *mmd = (MirrorModifierData*) md; @@ -1287,15 +1692,127 @@ static void mirrorModifier_updateDepgraph(ModifierData *md, DagForest *forest, DagNode *latNode = dag_get_node(forest, mmd->mirror_ob); dag_add_relation(forest, latNode, obNode, - DAG_RL_DATA_DATA | DAG_RL_OB_DATA); + DAG_RL_DATA_DATA | DAG_RL_OB_DATA, "Mirror Modifier"); } } +/* finds the best possible flipped name. For renaming; check for unique names afterwards */ +/* if strip_number: removes number extensions */ +void vertgroup_flip_name (char *name, int strip_number) +{ + int len; + char prefix[128]={""}; /* The part before the facing */ + char suffix[128]={""}; /* The part after the facing */ + char replace[128]={""}; /* The replacement string */ + char number[128]={""}; /* The number extension string */ + char *index=NULL; + + len= strlen(name); + if(len<3) return; // we don't do names like .R or .L + + /* We first check the case with a .### extension, let's find the last period */ + if(isdigit(name[len-1])) { + index= strrchr(name, '.'); // last occurrance + if (index && isdigit(index[1]) ) { // doesnt handle case bone.1abc2 correct..., whatever! + if(strip_number==0) + strcpy(number, index); + *index= 0; + len= strlen(name); + } + } + + strcpy (prefix, name); + +#define IS_SEPARATOR(a) ((a)=='.' || (a)==' ' || (a)=='-' || (a)=='_') + + /* first case; separator . - _ with extensions r R l L */ + if( IS_SEPARATOR(name[len-2]) ) { + switch(name[len-1]) { + case 'l': + prefix[len-1]= 0; + strcpy(replace, "r"); + break; + case 'r': + prefix[len-1]= 0; + strcpy(replace, "l"); + break; + case 'L': + prefix[len-1]= 0; + strcpy(replace, "R"); + break; + case 'R': + prefix[len-1]= 0; + strcpy(replace, "L"); + break; + } + } + /* case; beginning with r R l L , with separator after it */ + else if( IS_SEPARATOR(name[1]) ) { + switch(name[0]) { + case 'l': + strcpy(replace, "r"); + strcpy(suffix, name+1); + prefix[0]= 0; + break; + case 'r': + strcpy(replace, "l"); + strcpy(suffix, name+1); + prefix[0]= 0; + break; + case 'L': + strcpy(replace, "R"); + strcpy(suffix, name+1); + prefix[0]= 0; + break; + case 'R': + strcpy(replace, "L"); + strcpy(suffix, name+1); + prefix[0]= 0; + break; + } + } + else if(len > 5) { + /* hrms, why test for a separator? lets do the rule 'ultimate left or right' */ + index = BLI_strcasestr(prefix, "right"); + if (index==prefix || index==prefix+len-5) { + if(index[0]=='r') + strcpy (replace, "left"); + else { + if(index[1]=='I') + strcpy (replace, "LEFT"); + else + strcpy (replace, "Left"); + } + *index= 0; + strcpy (suffix, index+5); + } + else { + index = BLI_strcasestr(prefix, "left"); + if (index==prefix || index==prefix+len-4) { + if(index[0]=='l') + strcpy (replace, "right"); + else { + if(index[1]=='E') + strcpy (replace, "RIGHT"); + else + strcpy (replace, "Right"); + } + *index= 0; + strcpy (suffix, index+4); + } + } + } + +#undef IS_SEPARATOR + + sprintf (name, "%s%s%s%s", prefix, replace, suffix, number); +} + static DerivedMesh *doMirrorOnAxis(MirrorModifierData *mmd, - Object *ob, - DerivedMesh *dm, - int initFlags, - int axis) + Object *ob, + DerivedMesh *dm, + int initFlags, + int axis) { int i; float tolerance = mmd->tolerance; @@ -1304,6 +1821,9 @@ static DerivedMesh *doMirrorOnAxis(MirrorModifierData *mmd, int maxVerts = dm->getNumVerts(dm); int maxEdges = dm->getNumEdges(dm); int maxFaces = dm->getNumFaces(dm); + int vector_size=0, j, a, b; + bDeformGroup *def, *defb; + bDeformGroup **vector_def = NULL; int (*indexMap)[2]; float mtx[4][4], imtx[4][4]; @@ -1313,9 +1833,24 @@ static DerivedMesh *doMirrorOnAxis(MirrorModifierData *mmd, result = CDDM_from_template(dm, maxVerts * 2, maxEdges * 2, maxFaces * 2); + + if (mmd->flag & MOD_MIR_VGROUP) { + /* calculate the number of deformedGroups */ + for(vector_size = 0, def = ob->defbase.first; def; + def = def->next, vector_size++); + + /* load the deformedGroups for fast access */ + vector_def = + (bDeformGroup **)MEM_mallocN(sizeof(bDeformGroup*) * vector_size, + "group_index"); + for(a = 0, def = ob->defbase.first; def; def = def->next, a++) { + vector_def[a] = def; + } + } + if (mmd->mirror_ob) { float obinv[4][4]; - + Mat4Invert(obinv, mmd->mirror_ob->obmat); Mat4MulMat4(mtx, ob->obmat, obinv); Mat4Invert(imtx, mtx); @@ -1326,27 +1861,27 @@ static DerivedMesh *doMirrorOnAxis(MirrorModifierData *mmd, MVert *mv = CDDM_get_vert(result, numVerts); int isShared; float co[3]; - + dm->getVert(dm, i, &inMV); - + VecCopyf(co, inMV.co); - + if (mmd->mirror_ob) { VecMat4MulVecfl(co, mtx, co); } isShared = ABS(co[axis])<=tolerance; - + /* Because the topology result (# of vertices) must be the same if - * the mesh data is overridden by vertex cos, have to calc sharedness - * based on original coordinates. This is why we test before copy. - */ + * the mesh data is overridden by vertex cos, have to calc sharedness + * based on original coordinates. This is why we test before copy. + */ DM_copy_vert_data(dm, result, i, numVerts, 1); *mv = inMV; numVerts++; - + indexMap[i][0] = numVerts - 1; indexMap[i][1] = !isShared; - + if(isShared) { co[axis] = 0; if (mmd->mirror_ob) { @@ -1357,41 +1892,73 @@ static DerivedMesh *doMirrorOnAxis(MirrorModifierData *mmd, mv->flag |= ME_VERT_MERGED; } else { MVert *mv2 = CDDM_get_vert(result, numVerts); - + MDeformVert *dvert = NULL; + DM_copy_vert_data(dm, result, i, numVerts, 1); *mv2 = *mv; - numVerts++; - + co[axis] = -co[axis]; if (mmd->mirror_ob) { VecMat4MulVecfl(co, imtx, co); } VecCopyf(mv2->co, co); + + if (mmd->flag & MOD_MIR_VGROUP){ + dvert = DM_get_vert_data(result, numVerts, CD_MDEFORMVERT); + + if (dvert) + { + for(j = 0; j < dvert[0].totweight; ++j) + { + char tmpname[32]; + + if(dvert->dw[j].def_nr < 0 || + dvert->dw[j].def_nr >= vector_size) + continue; + + def = vector_def[dvert->dw[j].def_nr]; + strcpy(tmpname, def->name); + vertgroup_flip_name(tmpname,0); + + for(b = 0, defb = ob->defbase.first; defb; + defb = defb->next, b++) + { + if(!strcmp(defb->name, tmpname)) + { + dvert->dw[j].def_nr = b; + break; + } + } + } + } + } + + numVerts++; } } for(i = 0; i < maxEdges; i++) { MEdge inMED; MEdge *med = CDDM_get_edge(result, numEdges); - + dm->getEdge(dm, i, &inMED); - + DM_copy_edge_data(dm, result, i, numEdges, 1); *med = inMED; numEdges++; - + med->v1 = indexMap[inMED.v1][0]; med->v2 = indexMap[inMED.v2][0]; if(initFlags) med->flag |= ME_EDGEDRAW | ME_EDGERENDER; - + if(indexMap[inMED.v1][1] || indexMap[inMED.v2][1]) { MEdge *med2 = CDDM_get_edge(result, numEdges); - + DM_copy_edge_data(dm, result, i, numEdges, 1); *med2 = *med; numEdges++; - + med2->v1 += indexMap[inMED.v1][1]; med2->v2 += indexMap[inMED.v2][1]; } @@ -1400,33 +1967,33 @@ static DerivedMesh *doMirrorOnAxis(MirrorModifierData *mmd, for(i = 0; i < maxFaces; i++) { MFace inMF; MFace *mf = CDDM_get_face(result, numFaces); - + dm->getFace(dm, i, &inMF); - + DM_copy_face_data(dm, result, i, numFaces, 1); *mf = inMF; numFaces++; - + mf->v1 = indexMap[inMF.v1][0]; mf->v2 = indexMap[inMF.v2][0]; mf->v3 = indexMap[inMF.v3][0]; mf->v4 = indexMap[inMF.v4][0]; if(indexMap[inMF.v1][1] - || indexMap[inMF.v2][1] - || indexMap[inMF.v3][1] - || (mf->v4 && indexMap[inMF.v4][1])) { + || indexMap[inMF.v2][1] + || indexMap[inMF.v3][1] + || (mf->v4 && indexMap[inMF.v4][1])) { MFace *mf2 = CDDM_get_face(result, numFaces); static int corner_indices[4] = {2, 1, 0, 3}; - + DM_copy_face_data(dm, result, i, numFaces, 1); *mf2 = *mf; - + mf2->v1 += indexMap[inMF.v1][1]; mf2->v2 += indexMap[inMF.v2][1]; mf2->v3 += indexMap[inMF.v3][1]; if(inMF.v4) mf2->v4 += indexMap[inMF.v4][1]; - + /* mirror UVs if enabled */ if(mmd->flag & (MOD_MIR_MIRROR_U | MOD_MIR_MIRROR_V)) { MTFace *tf = result->getFaceData(result, numFaces, CD_MTFACE); @@ -1440,16 +2007,18 @@ static DerivedMesh *doMirrorOnAxis(MirrorModifierData *mmd, } } } - + /* Flip face normal */ SWAP(int, mf2->v1, mf2->v3); DM_swap_face_data(result, numFaces, corner_indices); - + test_index_face(mf2, &result->faceData, numFaces, inMF.v4?4:3); numFaces++; } } + if (vector_def) MEM_freeN(vector_def); + MEM_freeN(indexMap); CDDM_lower_num_verts(result, numVerts); @@ -1460,8 +2029,8 @@ static DerivedMesh *doMirrorOnAxis(MirrorModifierData *mmd, } static DerivedMesh *mirrorModifier__doMirror(MirrorModifierData *mmd, - Object *ob, DerivedMesh *dm, - int initFlags) + Object *ob, DerivedMesh *dm, + int initFlags) { DerivedMesh *result = dm; @@ -1484,22 +2053,23 @@ static DerivedMesh *mirrorModifier__doMirror(MirrorModifierData *mmd, } static DerivedMesh *mirrorModifier_applyModifier( - ModifierData *md, Object *ob, DerivedMesh *derivedData, - int useRenderParams, int isFinalCalc) + ModifierData *md, Object *ob, DerivedMesh *derivedData, + int useRenderParams, int isFinalCalc) { DerivedMesh *result; MirrorModifierData *mmd = (MirrorModifierData*) md; result = mirrorModifier__doMirror(mmd, ob, derivedData, 0); - CDDM_calc_normals(result); + if(result != derivedData) + CDDM_calc_normals(result); return result; } static DerivedMesh *mirrorModifier_applyModifierEM( - ModifierData *md, Object *ob, EditMesh *editData, - DerivedMesh *derivedData) + ModifierData *md, Object *ob, EditMesh *editData, + DerivedMesh *derivedData) { return mirrorModifier_applyModifier(md, ob, derivedData, 0, 1); } @@ -1570,6 +2140,8 @@ typedef struct SmoothMesh { DerivedMesh *dm; float threshold; /* the cosine of the smoothing angle */ int flags; + MemArena *arena; + ListBase propagatestack, reusestack; } SmoothMesh; static SmoothVert *smoothvert_copy(SmoothVert *vert, SmoothMesh *mesh) @@ -1622,15 +2194,15 @@ static int smoothedge_has_vert(SmoothEdge *edge, SmoothVert *vert) } static SmoothMesh *smoothmesh_new(int num_verts, int num_edges, int num_faces, - int max_verts, int max_edges, int max_faces) + int max_verts, int max_edges, int max_faces) { SmoothMesh *mesh = MEM_callocN(sizeof(*mesh), "smoothmesh"); mesh->verts = MEM_callocN(sizeof(*mesh->verts) * max_verts, - "SmoothMesh.verts"); + "SmoothMesh.verts"); mesh->edges = MEM_callocN(sizeof(*mesh->edges) * max_edges, - "SmoothMesh.edges"); + "SmoothMesh.edges"); mesh->faces = MEM_callocN(sizeof(*mesh->faces) * max_faces, - "SmoothMesh.faces"); + "SmoothMesh.faces"); mesh->num_verts = num_verts; mesh->num_edges = num_edges; @@ -1652,6 +2224,9 @@ static void smoothmesh_free(SmoothMesh *mesh) for(i = 0; i < mesh->num_edges; ++i) BLI_linklist_free(mesh->edges[i].faces, NULL); + + if(mesh->arena) + BLI_memarena_free(mesh->arena); MEM_freeN(mesh->verts); MEM_freeN(mesh->edges); @@ -1729,7 +2304,7 @@ static void smoothmesh_print(SmoothMesh *mesh) printf("%3d: ind={%3d, %3d}, pos={% 5.1f, % 5.1f, % 5.1f}", i, vert->oldIndex, vert->newIndex, - mv.co[0], mv.co[1], mv.co[2]); + mv.co[0], mv.co[1], mv.co[2]); printf(", faces={"); for(node = vert->faces; node != NULL; node = node->next) { printf(" %d", ((SmoothFace *)node->link)->newIndex); @@ -1744,8 +2319,8 @@ static void smoothmesh_print(SmoothMesh *mesh) printf("%4d: indices={%4d, %4d}, verts={%4d, %4d}", i, - edge->oldIndex, edge->newIndex, - edge->verts[0]->newIndex, edge->verts[1]->newIndex); + edge->oldIndex, edge->newIndex, + edge->verts[0]->newIndex, edge->verts[1]->newIndex); if(edge->verts[0] == edge->verts[1]) printf(" <- DUPLICATE VERTEX"); printf(", faces={"); for(node = edge->faces; node != NULL; node = node->next) { @@ -1787,7 +2362,7 @@ static SmoothMesh *smoothmesh_from_derivedmesh(DerivedMesh *dm) totface = dm->getNumFaces(dm); mesh = smoothmesh_new(totvert, totedge, totface, - totvert, totedge, totface); + totvert, totedge, totface); mesh->dm = dm; @@ -1857,9 +2432,9 @@ static SmoothMesh *smoothmesh_from_derivedmesh(DerivedMesh *dm) static DerivedMesh *CDDM_from_smoothmesh(SmoothMesh *mesh) { DerivedMesh *result = CDDM_from_template(mesh->dm, - mesh->num_verts, - mesh->num_edges, - mesh->num_faces); + mesh->num_verts, + mesh->num_edges, + mesh->num_faces); MVert *new_verts = CDDM_get_verts(result); MEdge *new_edges = CDDM_get_edges(result); MFace *new_faces = CDDM_get_faces(result); @@ -1870,7 +2445,7 @@ static DerivedMesh *CDDM_from_smoothmesh(SmoothMesh *mesh) MVert *newMV = &new_verts[vert->newIndex]; DM_copy_vert_data(mesh->dm, result, - vert->oldIndex, vert->newIndex, 1); + vert->oldIndex, vert->newIndex, 1); mesh->dm->getVert(mesh->dm, vert->oldIndex, newMV); } @@ -1879,7 +2454,7 @@ static DerivedMesh *CDDM_from_smoothmesh(SmoothMesh *mesh) MEdge *newME = &new_edges[edge->newIndex]; DM_copy_edge_data(mesh->dm, result, - edge->oldIndex, edge->newIndex, 1); + edge->oldIndex, edge->newIndex, 1); mesh->dm->getEdge(mesh->dm, edge->oldIndex, newME); newME->v1 = edge->verts[0]->newIndex; newME->v2 = edge->verts[1]->newIndex; @@ -1890,7 +2465,7 @@ static DerivedMesh *CDDM_from_smoothmesh(SmoothMesh *mesh) MFace *newMF = &new_faces[face->newIndex]; DM_copy_face_data(mesh->dm, result, - face->oldIndex, face->newIndex, 1); + face->oldIndex, face->newIndex, 1); mesh->dm->getFace(mesh->dm, face->oldIndex, newMF); newMF->v1 = face->edges[0]->verts[face->flip[0]]->newIndex; @@ -1920,7 +2495,7 @@ static SmoothVert *other_vert(SmoothEdge *edge, SmoothVert *vert) * (this should never happen) */ static SmoothEdge *other_edge(SmoothFace *face, SmoothVert *vert, - SmoothEdge *edge) + SmoothEdge *edge) { int i,j; for(i = 0; i < SMOOTHFACE_MAX_EDGES && face->edges[i]; i++) { @@ -1932,8 +2507,8 @@ static SmoothEdge *other_edge(SmoothFace *face, SmoothVert *vert, } /* if we get to here, something's wrong (there should always be 2 edges - * which use the same vert in a face) - */ + * which use the same vert in a face) + */ return NULL; } @@ -1962,18 +2537,18 @@ static void linklist_copy(LinkNode **target, LinkNode *source) for(; source; source = source->next) { if(node) { node->next = MEM_mallocN(sizeof(*node->next), "nlink_copy"); - node = node->next; - } else { - node = *target = MEM_mallocN(sizeof(**target), "nlink_copy"); - } - node->link = source->link; - node->next = NULL; - } + node = node->next; +} else { + node = *target = MEM_mallocN(sizeof(**target), "nlink_copy"); +} + node->link = source->link; + node->next = NULL; +} } #endif -/* appends source to target if it's not already in target */ -static void linklist_append_unique(LinkNode **target, void *source) + /* appends source to target if it's not already in target */ + static void linklist_append_unique(LinkNode **target, void *source) { LinkNode *node; LinkNode *prev = NULL; @@ -2007,7 +2582,7 @@ static void linklist_prepend_linklist(LinkNode **list, LinkNode *prepend) node->next = *list; *list = prepend; - } +} } #endif @@ -2050,7 +2625,7 @@ static void linklist_empty(LinkNode **list, LinkNodeFreeFP freefunc) * frees the pointer with freefunc if freefunc is not NULL */ static void linklist_remove_first(LinkNode **list, void *value, - LinkNodeFreeFP freefunc) + LinkNodeFreeFP freefunc) { LinkNode *node = *list; LinkNode *prev = NULL; @@ -2075,7 +2650,7 @@ static void linklist_remove_first(LinkNode **list, void *value, /* removes all elements in source from target */ static void linklist_remove_list(LinkNode **target, LinkNode *source, - LinkNodeFreeFP freefunc) + LinkNodeFreeFP freefunc) { for(; source; source = source->next) linklist_remove_first(target, source->link, freefunc); @@ -2150,15 +2725,15 @@ static void face_replace_edge(void *ptr, void *userdata) #ifdef EDGESPLIT_DEBUG_3 printf("replacing edge %4d with %4d in face %4d", - find->newIndex, replace->newIndex, face->newIndex); + find->newIndex, replace->newIndex, face->newIndex); if(face->edges[3]) printf(": {%2d %2d %2d %2d}", face->edges[0]->newIndex, face->edges[1]->newIndex, - face->edges[2]->newIndex, face->edges[3]->newIndex); + face->edges[2]->newIndex, face->edges[3]->newIndex); else printf(": {%2d %2d %2d}", face->edges[0]->newIndex, face->edges[1]->newIndex, - face->edges[2]->newIndex); + face->edges[2]->newIndex); #endif for(i = 0; i < SMOOTHFACE_MAX_EDGES && face->edges[i]; i++) { @@ -2173,11 +2748,11 @@ static void face_replace_edge(void *ptr, void *userdata) if(face->edges[3]) printf(" -> {%2d %2d %2d %2d}\n", face->edges[0]->newIndex, face->edges[1]->newIndex, - face->edges[2]->newIndex, face->edges[3]->newIndex); + face->edges[2]->newIndex, face->edges[3]->newIndex); else printf(" -> {%2d %2d %2d}\n", face->edges[0]->newIndex, face->edges[1]->newIndex, - face->edges[2]->newIndex); + face->edges[2]->newIndex); #endif } @@ -2187,7 +2762,7 @@ static int edge_is_loose(SmoothEdge *edge) } static int edge_is_sharp(SmoothEdge *edge, int flags, - float threshold) + float threshold) { #ifdef EDGESPLIT_DEBUG_1 printf("edge %d: ", edge->newIndex); @@ -2218,7 +2793,7 @@ static int edge_is_sharp(SmoothEdge *edge, int flags, * - returns to the start edge (NULL is returned) */ static SmoothEdge *find_other_sharp_edge(SmoothVert *vert, SmoothEdge *edge, - LinkNode **visited_faces, float threshold, int flags) + LinkNode **visited_faces, float threshold, int flags) { SmoothFace *face = NULL; SmoothEdge *edge2 = NULL; @@ -2244,10 +2819,10 @@ static SmoothEdge *find_other_sharp_edge(SmoothVert *vert, SmoothEdge *edge, BLI_linklist_prepend(visited_faces, face); /* search until we hit a loose edge or a sharp edge or an edge we've - * seen before - */ + * seen before + */ while(face && !edge_is_sharp(edge2, flags, threshold) - && !linklist_contains(visited_edges, edge2)) { + && !linklist_contains(visited_edges, edge2)) { #ifdef EDGESPLIT_DEBUG_3 printf("current face %4d; current edge %4d\n", face->newIndex, edge2->newIndex); @@ -2273,25 +2848,25 @@ static SmoothEdge *find_other_sharp_edge(SmoothVert *vert, SmoothEdge *edge, printf("loose edge: %4d\n", edge2->newIndex); #endif } - } + } - /* either we came back to the start edge or we found a sharp/loose edge */ - if(linklist_contains(visited_edges, edge2)) - /* we came back to the start edge */ - edge2 = NULL; + /* either we came back to the start edge or we found a sharp/loose edge */ + if(linklist_contains(visited_edges, edge2)) + /* we came back to the start edge */ + edge2 = NULL; - BLI_linklist_free(visited_edges, NULL); + BLI_linklist_free(visited_edges, NULL); #ifdef EDGESPLIT_DEBUG_1 - printf("=== END === find_other_sharp_edge(edge = %4d, vert = %4d), " - "returning edge %d\n", - edge->newIndex, vert->newIndex, edge2 ? edge2->newIndex : -1); + printf("=== END === find_other_sharp_edge(edge = %4d, vert = %4d), " + "returning edge %d\n", + edge->newIndex, vert->newIndex, edge2 ? edge2->newIndex : -1); #endif - return edge2; + return edge2; } static void split_single_vert(SmoothVert *vert, SmoothFace *face, - SmoothMesh *mesh) + SmoothMesh *mesh) { SmoothVert *copy_vert; ReplaceData repdata; @@ -2303,10 +2878,53 @@ static void split_single_vert(SmoothVert *vert, SmoothFace *face, face_replace_vert(face, &repdata); } +typedef struct PropagateEdge { + struct PropagateEdge *next, *prev; + SmoothEdge *edge; + SmoothVert *vert; +} PropagateEdge; + +static void push_propagate_stack(SmoothEdge *edge, SmoothVert *vert, SmoothMesh *mesh) +{ + PropagateEdge *pedge = mesh->reusestack.first; + + if(pedge) { + BLI_remlink(&mesh->reusestack, pedge); + } + else { + if(!mesh->arena) { + mesh->arena = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE); + BLI_memarena_use_calloc(mesh->arena); + } + + pedge = BLI_memarena_alloc(mesh->arena, sizeof(PropagateEdge)); + } + + pedge->edge = edge; + pedge->vert = vert; + BLI_addhead(&mesh->propagatestack, pedge); +} + +static void pop_propagate_stack(SmoothEdge **edge, SmoothVert **vert, SmoothMesh *mesh) +{ + PropagateEdge *pedge = mesh->propagatestack.first; + + if(pedge) { + *edge = pedge->edge; + *vert = pedge->vert; + BLI_remlink(&mesh->propagatestack, pedge); + BLI_addhead(&mesh->reusestack, pedge); + } + else { + *edge = NULL; + *vert = NULL; + } +} + static void split_edge(SmoothEdge *edge, SmoothVert *vert, SmoothMesh *mesh); static void propagate_split(SmoothEdge *edge, SmoothVert *vert, - SmoothMesh *mesh) + SmoothMesh *mesh) { SmoothEdge *edge2; LinkNode *visited_faces = NULL; @@ -2316,7 +2934,7 @@ static void propagate_split(SmoothEdge *edge, SmoothVert *vert, #endif edge2 = find_other_sharp_edge(vert, edge, &visited_faces, - mesh->threshold, mesh->flags); + mesh->threshold, mesh->flags); if(!edge2) { /* didn't find a sharp or loose edge, so we've hit a dead end */ @@ -2375,12 +2993,12 @@ static void split_edge(SmoothEdge *edge, SmoothVert *vert, SmoothMesh *mesh) #endif edge2 = find_other_sharp_edge(vert, edge, &visited_faces, - mesh->threshold, mesh->flags); + mesh->threshold, mesh->flags); if(!edge2) { /* didn't find a sharp or loose edge, so try the other vert */ vert2 = other_vert(edge, vert); - propagate_split(edge, vert2, mesh); + push_propagate_stack(edge, vert2, mesh); } else if(!edge_is_loose(edge2)) { /* edge2 is not loose, so it must be sharp */ SmoothEdge *copy_edge = smoothedge_copy(edge, mesh); @@ -2400,20 +3018,20 @@ static void split_edge(SmoothEdge *edge, SmoothVert *vert, SmoothMesh *mesh) vert2 = smoothvert_copy(vert, mesh); /* replace vert with its copy in visited_faces (must be done after - * edge replacement so edges have correct vertices) - */ + * edge replacement so edges have correct vertices) + */ repdata.find = vert; repdata.replace = vert2; BLI_linklist_apply(visited_faces, face_replace_vert, &repdata); /* all copying and replacing is done; the mesh should be consistent. - * now propagate the split to the vertices at either end - */ - propagate_split(copy_edge, other_vert(copy_edge, vert2), mesh); - propagate_split(copy_edge2, other_vert(copy_edge2, vert2), mesh); + * now propagate the split to the vertices at either end + */ + push_propagate_stack(copy_edge, other_vert(copy_edge, vert2), mesh); + push_propagate_stack(copy_edge2, other_vert(copy_edge2, vert2), mesh); if(smoothedge_has_vert(edge, vert)) - propagate_split(edge, vert, mesh); + push_propagate_stack(edge, vert, mesh); } else { /* edge2 is loose */ SmoothEdge *copy_edge = smoothedge_copy(edge, mesh); @@ -2427,19 +3045,19 @@ static void split_edge(SmoothEdge *edge, SmoothVert *vert, SmoothMesh *mesh) vert2 = smoothvert_copy(vert, mesh); /* replace vert with its copy in visited_faces (must be done after - * edge replacement so edges have correct vertices) - */ + * edge replacement so edges have correct vertices) + */ repdata.find = vert; repdata.replace = vert2; BLI_linklist_apply(visited_faces, face_replace_vert, &repdata); /* copying and replacing is done; the mesh should be consistent. - * now propagate the split to the vertex at the other end - */ - propagate_split(copy_edge, other_vert(copy_edge, vert2), mesh); + * now propagate the split to the vertex at the other end + */ + push_propagate_stack(copy_edge, other_vert(copy_edge, vert2), mesh); if(smoothedge_has_vert(edge, vert)) - propagate_split(edge, vert, mesh); + push_propagate_stack(edge, vert, mesh); } BLI_linklist_free(visited_faces, NULL); @@ -2450,7 +3068,7 @@ static void split_edge(SmoothEdge *edge, SmoothVert *vert, SmoothMesh *mesh) } static void tag_and_count_extra_edges(SmoothMesh *mesh, float split_angle, - int flags, int *extra_edges) + int flags, int *extra_edges) { /* if normal1 dot normal2 < threshold, angle is greater, so split */ /* FIXME not sure if this always works */ @@ -2477,40 +3095,41 @@ static void tag_and_count_extra_edges(SmoothMesh *mesh, float split_angle, for(node = edge->faces->next->next->next; node; node = node->next) (*extra_edges)++; } else if((flags & (MOD_EDGESPLIT_FROMANGLE | MOD_EDGESPLIT_FROMFLAG)) - && !edge_is_loose(edge)) { + && !edge_is_loose(edge)) { /* (the edge can only be sharp if we're checking angle or flag, - * and it has at least 2 faces) */ + * and it has at least 2 faces) */ - /* if we're checking the sharp flag and it's set, good */ - if((flags & MOD_EDGESPLIT_FROMFLAG) && (edge->flag & ME_SHARP)) { - /* this edge is sharp */ - sharp = 1; + /* if we're checking the sharp flag and it's set, good */ + if((flags & MOD_EDGESPLIT_FROMFLAG) && (edge->flag & ME_SHARP)) { + /* this edge is sharp */ + sharp = 1; - (*extra_edges)++; - } else if(flags & MOD_EDGESPLIT_FROMANGLE) { - /* we know the edge has 2 faces, so check the angle */ - SmoothFace *face1 = edge->faces->link; - SmoothFace *face2 = edge->faces->next->link; - float edge_angle_cos = MTC_dot3Float(face1->normal, - face2->normal); - - if(edge_angle_cos < threshold) { - /* this edge is sharp */ - sharp = 1; - - (*extra_edges)++; - } - } - } + (*extra_edges)++; + } else if(flags & MOD_EDGESPLIT_FROMANGLE) { + /* we know the edge has 2 faces, so check the angle */ + SmoothFace *face1 = edge->faces->link; + SmoothFace *face2 = edge->faces->next->link; + float edge_angle_cos = MTC_dot3Float(face1->normal, + face2->normal); + + if(edge_angle_cos < threshold) { + /* this edge is sharp */ + sharp = 1; - /* set/clear sharp flag appropriately */ - if(sharp) edge->flag |= ME_SHARP; - else edge->flag &= ~ME_SHARP; + (*extra_edges)++; + } + } + } + + /* set/clear sharp flag appropriately */ + if(sharp) edge->flag |= ME_SHARP; + else edge->flag &= ~ME_SHARP; } } static void split_sharp_edges(SmoothMesh *mesh, float split_angle, int flags) { + SmoothVert *vert; int i; /* if normal1 dot normal2 < threshold, angle is greater, so split */ /* FIXME not sure if this always works */ @@ -2523,10 +3142,16 @@ static void split_sharp_edges(SmoothMesh *mesh, float split_angle, int flags) for(i = 0; i < mesh->num_edges; i++) { SmoothEdge *edge = &mesh->edges[i]; - if(edge_is_sharp(edge, flags, mesh->threshold)) + if(edge_is_sharp(edge, flags, mesh->threshold)) { split_edge(edge, edge->verts[0], mesh); - } + do { + pop_propagate_stack(&edge, &vert, mesh); + if(edge && smoothedge_has_vert(edge, vert)) + propagate_split(edge, vert, mesh); + } while(edge); + } + } } static int count_bridge_verts(SmoothMesh *mesh) @@ -2548,19 +3173,19 @@ static int count_bridge_verts(SmoothMesh *mesh) next_edge = face->edges[next]; /* if there are other faces sharing this vertex but not - * these edges, the vertex will be split, so count it - */ + * these edges, the vertex will be split, so count it + */ /* vert has to have at least one face (this one), so faces != 0 */ if(!edge->faces->next && !next_edge->faces->next - && vert->faces->next) { + && vert->faces->next) { count++; - } + } } } /* each bridge vert will be counted once per face that uses it, - * so count is too high, but it's ok for now - */ + * so count is too high, but it's ok for now + */ return count; } @@ -2583,21 +3208,21 @@ static void split_bridge_verts(SmoothMesh *mesh) next_edge = face->edges[next]; /* if there are other faces sharing this vertex but not - * these edges, split the vertex - */ + * these edges, split the vertex + */ /* vert has to have at least one face (this one), so faces != 0 */ if(!edge->faces->next && !next_edge->faces->next - && vert->faces->next) + && vert->faces->next) /* FIXME this needs to find all faces that share edges with - * this one and split off together - */ + * this one and split off together + */ split_single_vert(vert, face, mesh); } } } static DerivedMesh *edgesplitModifier_do(EdgeSplitModifierData *emd, - Object *ob, DerivedMesh *dm) + Object *ob, DerivedMesh *dm) { SmoothMesh *mesh; DerivedMesh *result; @@ -2640,8 +3265,8 @@ static DerivedMesh *edgesplitModifier_do(EdgeSplitModifierData *emd, #ifdef EDGESPLIT_DEBUG_0 printf("Edgesplit: Estimated %d verts & %d edges, " - "found %d verts & %d edges\n", max_verts, max_edges, - mesh->num_verts, mesh->num_edges); + "found %d verts & %d edges\n", max_verts, max_edges, + mesh->num_verts, mesh->num_edges); #endif result = CDDM_from_smoothmesh(mesh); @@ -2651,26 +3276,111 @@ static DerivedMesh *edgesplitModifier_do(EdgeSplitModifierData *emd, } static DerivedMesh *edgesplitModifier_applyModifier( - ModifierData *md, Object *ob, DerivedMesh *derivedData, - int useRenderParams, int isFinalCalc) + ModifierData *md, Object *ob, DerivedMesh *derivedData, + int useRenderParams, int isFinalCalc) { DerivedMesh *result; EdgeSplitModifierData *emd = (EdgeSplitModifierData*) md; result = edgesplitModifier_do(emd, ob, derivedData); - CDDM_calc_normals(result); + if(result != derivedData) + CDDM_calc_normals(result); return result; } static DerivedMesh *edgesplitModifier_applyModifierEM( - ModifierData *md, Object *ob, EditMesh *editData, - DerivedMesh *derivedData) + ModifierData *md, Object *ob, EditMesh *editData, + DerivedMesh *derivedData) { return edgesplitModifier_applyModifier(md, ob, derivedData, 0, 1); } +/* Bevel */ + +static void bevelModifier_initData(ModifierData *md) +{ + BevelModifierData *bmd = (BevelModifierData*) md; + + bmd->value = 0.1f; + bmd->res = 1; + bmd->flags = 0; + bmd->val_flags = 0; + bmd->lim_flags = 0; + bmd->e_flags = 0; + bmd->bevel_angle = 30; + bmd->defgrp_name[0] = '\0'; +} + +static void bevelModifier_copyData(ModifierData *md, ModifierData *target) +{ + BevelModifierData *bmd = (BevelModifierData*) md; + BevelModifierData *tbmd = (BevelModifierData*) target; + + tbmd->value = bmd->value; + tbmd->res = bmd->res; + tbmd->flags = bmd->flags; + tbmd->val_flags = bmd->val_flags; + tbmd->lim_flags = bmd->lim_flags; + tbmd->e_flags = bmd->e_flags; + tbmd->bevel_angle = bmd->bevel_angle; + strncpy(tbmd->defgrp_name, bmd->defgrp_name, 32); +} + +CustomDataMask bevelModifier_requiredDataMask(ModifierData *md) +{ + BevelModifierData *bmd = (BevelModifierData *)md; + CustomDataMask dataMask = 0; + + /* ask for vertexgroups if we need them */ + if(bmd->defgrp_name[0]) dataMask |= (1 << CD_MDEFORMVERT); + + return dataMask; +} + +static DerivedMesh *bevelModifier_applyModifier( + ModifierData *md, Object *ob, DerivedMesh *derivedData, + int useRenderParams, int isFinalCalc) +{ + DerivedMesh *result; + BME_Mesh *bm; + + /*bDeformGroup *def;*/ + int /*i,*/ options, defgrp_index = -1; + BevelModifierData *bmd = (BevelModifierData*) md; + + options = bmd->flags|bmd->val_flags|bmd->lim_flags|bmd->e_flags; + + //~ if ((options & BME_BEVEL_VWEIGHT) && bmd->defgrp_name[0]) { + //~ for (i = 0, def = ob->defbase.first; def; def = def->next, i++) { + //~ if (!strcmp(def->name, bmd->defgrp_name)) { + //~ defgrp_index = i; + //~ break; + //~ } + //~ } + //~ if (defgrp_index < 0) { + //~ options &= ~BME_BEVEL_VWEIGHT; + //~ } + //~ } + + bm = BME_derivedmesh_to_bmesh(derivedData); + BME_bevel(bm,bmd->value,bmd->res,options,defgrp_index,bmd->bevel_angle,NULL); + result = BME_bmesh_to_derivedmesh(bm,derivedData); + BME_free_mesh(bm); + + CDDM_calc_normals(result); + + return result; +} + +static DerivedMesh *bevelModifier_applyModifierEM( + ModifierData *md, Object *ob, EditMesh *editData, + DerivedMesh *derivedData) +{ + return bevelModifier_applyModifier(md, ob, derivedData, 0, 1); +} + /* Displace */ static void displaceModifier_initData(ModifierData *md) @@ -2712,8 +3422,22 @@ CustomDataMask displaceModifier_requiredDataMask(ModifierData *md) return dataMask; } +static int displaceModifier_dependsOnTime(ModifierData *md) +{ + DisplaceModifierData *dmd = (DisplaceModifierData *)md; + + if(dmd->texture) + { + return BKE_texture_dependsOnTime(dmd->texture); + } + else + { + return 0; + } +} + static void displaceModifier_foreachObjectLink(ModifierData *md, Object *ob, - ObjectWalkFunc walk, void *userData) + ObjectWalkFunc walk, void *userData) { DisplaceModifierData *dmd = (DisplaceModifierData*) md; @@ -2721,13 +3445,13 @@ static void displaceModifier_foreachObjectLink(ModifierData *md, Object *ob, } static void displaceModifier_foreachIDLink(ModifierData *md, Object *ob, - IDWalkFunc walk, void *userData) + IDWalkFunc walk, void *userData) { DisplaceModifierData *dmd = (DisplaceModifierData*) md; walk(userData, ob, (ID **)&dmd->texture); - displaceModifier_foreachObjectLink(md, ob, (ObjectWalkFunc) walk, userData); + displaceModifier_foreachObjectLink(md, ob, (ObjectWalkFunc)walk, userData); } static int displaceModifier_isDisabled(ModifierData *md) @@ -2738,8 +3462,8 @@ static int displaceModifier_isDisabled(ModifierData *md) } static void displaceModifier_updateDepgraph( - ModifierData *md, DagForest *forest, - Object *ob, DagNode *obNode) + ModifierData *md, DagForest *forest, + Object *ob, DagNode *obNode) { DisplaceModifierData *dmd = (DisplaceModifierData*) md; @@ -2747,7 +3471,7 @@ static void displaceModifier_updateDepgraph( DagNode *curNode = dag_get_node(forest, dmd->map_object); dag_add_relation(forest, curNode, obNode, - DAG_RL_DATA_DATA | DAG_RL_OB_DATA); + DAG_RL_DATA_DATA | DAG_RL_OB_DATA, "Displace Modifier"); } } @@ -2761,17 +3485,17 @@ static void validate_layer_name(const CustomData *data, int type, char *name) if(index < 0) { /* either no layer was specified, or the layer we want has been - * deleted, so assign the active layer to name - */ + * deleted, so assign the active layer to name + */ index = CustomData_get_active_layer_index(data, CD_MTFACE); strcpy(name, data->layers[index].name); } } static void get_texture_coords(DisplaceModifierData *dmd, Object *ob, - DerivedMesh *dm, - float (*co)[3], float (*texco)[3], - int numVerts) + DerivedMesh *dm, + float (*co)[3], float (*texco)[3], + int numVerts) { int i; int texmapping = dmd->texmapping; @@ -2789,14 +3513,14 @@ static void get_texture_coords(DisplaceModifierData *dmd, Object *ob, MFace *mface = dm->getFaceArray(dm); MFace *mf; char *done = MEM_callocN(sizeof(*done) * numVerts, - "get_texture_coords done"); + "get_texture_coords done"); int numFaces = dm->getNumFaces(dm); MTFace *tf; validate_layer_name(&dm->faceData, CD_MTFACE, dmd->uvlayer_name); tf = CustomData_get_layer_named(&dm->faceData, CD_MTFACE, - dmd->uvlayer_name); + dmd->uvlayer_name); /* verts are given the UV from the first face that uses them */ for(i = 0, mf = mface; i < numFaces; ++i, ++mf, ++tf) { @@ -2840,18 +3564,18 @@ static void get_texture_coords(DisplaceModifierData *dmd, Object *ob, for(i = 0; i < numVerts; ++i, ++co, ++texco) { switch(texmapping) { - case MOD_DISP_MAP_LOCAL: - VECCOPY(*texco, *co); - break; - case MOD_DISP_MAP_GLOBAL: - VECCOPY(*texco, *co); - Mat4MulVecfl(ob->obmat, *texco); - break; - case MOD_DISP_MAP_OBJECT: - VECCOPY(*texco, *co); - Mat4MulVecfl(ob->obmat, *texco); - Mat4MulVecfl(dmd->map_object->imat, *texco); - break; + case MOD_DISP_MAP_LOCAL: + VECCOPY(*texco, *co); + break; + case MOD_DISP_MAP_GLOBAL: + VECCOPY(*texco, *co); + Mat4MulVecfl(ob->obmat, *texco); + break; + case MOD_DISP_MAP_OBJECT: + VECCOPY(*texco, *co); + Mat4MulVecfl(ob->obmat, *texco); + Mat4MulVecfl(dmd->map_object->imat, *texco); + break; } } } @@ -2861,23 +3585,23 @@ static void get_texture_value(Tex *texture, float *tex_co, TexResult *texres) int result_type; result_type = multitex_ext(texture, tex_co, NULL, - NULL, 1, texres); + NULL, 1, texres); /* if the texture gave an RGB value, we assume it didn't give a valid - * intensity, so calculate one (formula from do_material_tex). - * if the texture didn't give an RGB value, copy the intensity across - */ + * intensity, so calculate one (formula from do_material_tex). + * if the texture didn't give an RGB value, copy the intensity across + */ if(result_type & TEX_RGB) texres->tin = (0.35 * texres->tr + 0.45 * texres->tg - + 0.2 * texres->tb); + + 0.2 * texres->tb); else texres->tr = texres->tg = texres->tb = texres->tin; } /* dm must be a CDDerivedMesh */ static void displaceModifier_do( - DisplaceModifierData *dmd, Object *ob, - DerivedMesh *dm, float (*vertexCos)[3], int numVerts) + DisplaceModifierData *dmd, Object *ob, + DerivedMesh *dm, float (*vertexCos)[3], int numVerts) { int i; MVert *mvert; @@ -2904,7 +3628,7 @@ static void displaceModifier_do( dvert = dm->getVertDataArray(dm, CD_MDEFORMVERT); tex_co = MEM_callocN(sizeof(*tex_co) * numVerts, - "displaceModifier_do tex_co"); + "displaceModifier_do tex_co"); get_texture_coords(dmd, ob, dm, vertexCos, tex_co, numVerts); for(i = 0; i < numVerts; ++i) { @@ -2933,25 +3657,25 @@ static void displaceModifier_do( delta *= strength; switch(dmd->direction) { - case MOD_DISP_DIR_X: - vertexCos[i][0] += delta; - break; - case MOD_DISP_DIR_Y: - vertexCos[i][1] += delta; - break; - case MOD_DISP_DIR_Z: - vertexCos[i][2] += delta; - break; - case MOD_DISP_DIR_RGB_XYZ: - vertexCos[i][0] += (texres.tr - dmd->midlevel) * strength; - vertexCos[i][1] += (texres.tg - dmd->midlevel) * strength; - vertexCos[i][2] += (texres.tb - dmd->midlevel) * strength; - break; - case MOD_DISP_DIR_NOR: - vertexCos[i][0] += delta * mvert[i].no[0] / 32767.0f; - vertexCos[i][1] += delta * mvert[i].no[1] / 32767.0f; - vertexCos[i][2] += delta * mvert[i].no[2] / 32767.0f; - break; + case MOD_DISP_DIR_X: + vertexCos[i][0] += delta; + break; + case MOD_DISP_DIR_Y: + vertexCos[i][1] += delta; + break; + case MOD_DISP_DIR_Z: + vertexCos[i][2] += delta; + break; + case MOD_DISP_DIR_RGB_XYZ: + vertexCos[i][0] += (texres.tr - dmd->midlevel) * strength; + vertexCos[i][1] += (texres.tg - dmd->midlevel) * strength; + vertexCos[i][2] += (texres.tb - dmd->midlevel) * strength; + break; + case MOD_DISP_DIR_NOR: + vertexCos[i][0] += delta * mvert[i].no[0] / 32767.0f; + vertexCos[i][1] += delta * mvert[i].no[1] / 32767.0f; + vertexCos[i][2] += delta * mvert[i].no[2] / 32767.0f; + break; } } @@ -2959,8 +3683,8 @@ static void displaceModifier_do( } static void displaceModifier_deformVerts( - ModifierData *md, Object *ob, DerivedMesh *derivedData, - float (*vertexCos)[3], int numVerts) + ModifierData *md, Object *ob, DerivedMesh *derivedData, + float (*vertexCos)[3], int numVerts) { DerivedMesh *dm; @@ -2972,14 +3696,14 @@ static void displaceModifier_deformVerts( CDDM_calc_normals(dm); displaceModifier_do((DisplaceModifierData *)md, ob, dm, - vertexCos, numVerts); + vertexCos, numVerts); dm->release(dm); } static void displaceModifier_deformVertsEM( - ModifierData *md, Object *ob, EditMesh *editData, - DerivedMesh *derivedData, float (*vertexCos)[3], int numVerts) + ModifierData *md, Object *ob, EditMesh *editData, + DerivedMesh *derivedData, float (*vertexCos)[3], int numVerts) { DerivedMesh *dm; @@ -2990,7 +3714,7 @@ static void displaceModifier_deformVertsEM( CDDM_calc_normals(dm); displaceModifier_do((DisplaceModifierData *)md, ob, dm, - vertexCos, numVerts); + vertexCos, numVerts); dm->release(dm); } @@ -3038,7 +3762,7 @@ CustomDataMask uvprojectModifier_requiredDataMask(ModifierData *md) } static void uvprojectModifier_foreachObjectLink(ModifierData *md, Object *ob, - ObjectWalkFunc walk, void *userData) + ObjectWalkFunc walk, void *userData) { UVProjectModifierData *umd = (UVProjectModifierData*) md; int i; @@ -3048,18 +3772,18 @@ static void uvprojectModifier_foreachObjectLink(ModifierData *md, Object *ob, } static void uvprojectModifier_foreachIDLink(ModifierData *md, Object *ob, - IDWalkFunc walk, void *userData) + IDWalkFunc walk, void *userData) { UVProjectModifierData *umd = (UVProjectModifierData*) md; walk(userData, ob, (ID **)&umd->image); uvprojectModifier_foreachObjectLink(md, ob, (ObjectWalkFunc)walk, - userData); + userData); } static void uvprojectModifier_updateDepgraph(ModifierData *md, - DagForest *forest, Object *ob, DagNode *obNode) + DagForest *forest, Object *ob, DagNode *obNode) { UVProjectModifierData *umd = (UVProjectModifierData*) md; int i; @@ -3069,7 +3793,7 @@ static void uvprojectModifier_updateDepgraph(ModifierData *md, DagNode *curNode = dag_get_node(forest, umd->projectors[i]); dag_add_relation(forest, curNode, obNode, - DAG_RL_DATA_DATA | DAG_RL_OB_DATA); + DAG_RL_DATA_DATA | DAG_RL_OB_DATA, "UV Project Modifier"); } } } @@ -3081,7 +3805,7 @@ typedef struct Projector { } Projector; static DerivedMesh *uvprojectModifier_do(UVProjectModifierData *umd, - Object *ob, DerivedMesh *dm) + Object *ob, DerivedMesh *dm) { float (*coords)[3], (*co)[3]; MTFace *tface; @@ -3110,13 +3834,13 @@ static DerivedMesh *uvprojectModifier_do(UVProjectModifierData *umd, /* make sure we are not modifying the original UV layer */ tface = CustomData_duplicate_referenced_layer_named(&dm->faceData, - CD_MTFACE, - umd->uvlayer_name); + CD_MTFACE, + umd->uvlayer_name); numVerts = dm->getNumVerts(dm); coords = MEM_callocN(sizeof(*coords) * numVerts, - "uvprojectModifier_do coords"); + "uvprojectModifier_do coords"); dm->getVertCos(dm, coords); /* convert coords to world space */ @@ -3127,12 +3851,12 @@ static DerivedMesh *uvprojectModifier_do(UVProjectModifierData *umd, for(i = 0; i < num_projectors; ++i) { float tmpmat[4][4]; float offsetmat[4][4]; - + Camera *cam = NULL; /* calculate projection matrix */ Mat4Invert(projectors[i].projmat, projectors[i].ob->obmat); if(projectors[i].ob->type == OB_CAMERA) { - Camera *cam = (Camera *)projectors[i].ob->data; + cam = (Camera *)projectors[i].ob->data; if(cam->type == CAM_PERSP) { float perspmat[4][4]; float xmax; @@ -3152,7 +3876,7 @@ static DerivedMesh *uvprojectModifier_do(UVProjectModifierData *umd, ymin = -ymax; i_window(xmin, xmax, ymin, ymax, - cam->clipsta, cam->clipend, perspmat); + cam->clipsta, cam->clipend, perspmat); Mat4MulMat4(tmpmat, projectors[i].projmat, perspmat); } else if(cam->type == CAM_ORTHO) { float orthomat[4][4]; @@ -3172,7 +3896,7 @@ static DerivedMesh *uvprojectModifier_do(UVProjectModifierData *umd, ymin = -ymax; i_ortho(xmin, xmax, ymin, ymax, - cam->clipsta, cam->clipend, orthomat); + cam->clipsta, cam->clipend, orthomat); Mat4MulMat4(tmpmat, projectors[i].projmat, orthomat); } } else { @@ -3182,6 +3906,20 @@ static DerivedMesh *uvprojectModifier_do(UVProjectModifierData *umd, Mat4One(offsetmat); Mat4MulFloat3(offsetmat[0], 0.5); offsetmat[3][0] = offsetmat[3][1] = offsetmat[3][2] = 0.5; + + if (cam) { + if (umd->aspectx == umd->aspecty) { + offsetmat[3][0] -= cam->shiftx; + offsetmat[3][1] -= cam->shifty; + } else if (umd->aspectx < umd->aspecty) { + offsetmat[3][0] -=(cam->shiftx * umd->aspecty/umd->aspectx); + offsetmat[3][1] -= cam->shifty; + } else { + offsetmat[3][0] -= cam->shiftx; + offsetmat[3][1] -=(cam->shifty * umd->aspectx/umd->aspecty); + } + } + Mat4MulMat4(projectors[i].projmat, tmpmat, offsetmat); /* calculate worldspace projector normal (for best projector test) */ @@ -3216,8 +3954,8 @@ static DerivedMesh *uvprojectModifier_do(UVProjectModifierData *umd, } } else { /* multiple projectors, select the closest to face normal - * direction - */ + * direction + */ float co1[3], co2[3], co3[3], co4[3]; float face_no[3]; int j; @@ -3237,14 +3975,14 @@ static DerivedMesh *uvprojectModifier_do(UVProjectModifierData *umd, } /* find the projector which the face points at most directly - * (projector normal with largest dot product is best) - */ + * (projector normal with largest dot product is best) + */ best_dot = MTC_dot3Float(projectors[0].normal, face_no); best_projector = &projectors[0]; for(j = 1; j < num_projectors; ++j) { float tmp_dot = MTC_dot3Float(projectors[j].normal, - face_no); + face_no); if(tmp_dot > best_dot) { best_dot = tmp_dot; best_projector = &projectors[j]; @@ -3283,8 +4021,8 @@ static DerivedMesh *uvprojectModifier_do(UVProjectModifierData *umd, } static DerivedMesh *uvprojectModifier_applyModifier( - ModifierData *md, Object *ob, DerivedMesh *derivedData, - int useRenderParams, int isFinalCalc) + ModifierData *md, Object *ob, DerivedMesh *derivedData, + int useRenderParams, int isFinalCalc) { DerivedMesh *result; UVProjectModifierData *umd = (UVProjectModifierData*) md; @@ -3295,8 +4033,8 @@ static DerivedMesh *uvprojectModifier_applyModifier( } static DerivedMesh *uvprojectModifier_applyModifierEM( - ModifierData *md, Object *ob, EditMesh *editData, - DerivedMesh *derivedData) + ModifierData *md, Object *ob, EditMesh *editData, + DerivedMesh *derivedData) { return uvprojectModifier_applyModifier(md, ob, derivedData, 0, 1); } @@ -3321,8 +4059,8 @@ static void decimateModifier_copyData(ModifierData *md, ModifierData *target) //XXX #if 0 static DerivedMesh *decimateModifier_applyModifier( - ModifierData *md, Object *ob, DerivedMesh *derivedData, - int useRenderParams, int isFinalCalc) + ModifierData *md, Object *ob, DerivedMesh *derivedData, + int useRenderParams, int isFinalCalc) { DecimateModifierData *dmd = (DecimateModifierData*) md; DerivedMesh *dm = derivedData, *result = NULL; @@ -3346,7 +4084,7 @@ static DerivedMesh *decimateModifier_applyModifier( if(numTris<3) { modifier_setError(md, - "There must be more than 3 input faces (triangles)."); + "Modifier requires more than 3 input faces (triangles)."); goto exit; } @@ -3436,7 +4174,7 @@ static DerivedMesh *decimateModifier_applyModifier( MEM_freeN(lod.triangle_index_buffer); exit: - return result; + return result; } #endif @@ -3488,8 +4226,8 @@ CustomDataMask smoothModifier_requiredDataMask(ModifierData *md) } static void smoothModifier_do( - SmoothModifierData *smd, Object *ob, DerivedMesh *dm, - float (*vertexCos)[3], int numVerts) + SmoothModifierData *smd, Object *ob, DerivedMesh *dm, + float (*vertexCos)[3], int numVerts) { MDeformVert *dvert = NULL; MEdge *medges = NULL; @@ -3499,10 +4237,10 @@ static void smoothModifier_do( float *ftmp, fac, facm; ftmp = (float*)MEM_callocN(3*sizeof(float)*numVerts, - "smoothmodifier_f"); + "smoothmodifier_f"); if (!ftmp) return; uctmp = (unsigned char*)MEM_callocN(sizeof(unsigned char)*numVerts, - "smoothmodifier_uc"); + "smoothmodifier_uc"); if (!uctmp) { if (ftmp) MEM_freeN(ftmp); return; @@ -3531,7 +4269,7 @@ static void smoothModifier_do( dvert = dm->getVertDataArray(dm, CD_MDEFORMVERT); /* NOTICE: this can be optimized a little bit by moving the - * if (dvert) out of the loop, if needed */ + * if (dvert) out of the loop, if needed */ for (j = 0; j < smd->repeat; j++) { for (i = 0; i < numDMEdges; i++) { float fvec[3]; @@ -3627,8 +4365,8 @@ static void smoothModifier_do( } static void smoothModifier_deformVerts( - ModifierData *md, Object *ob, DerivedMesh *derivedData, - float (*vertexCos)[3], int numVerts) + ModifierData *md, Object *ob, DerivedMesh *derivedData, + float (*vertexCos)[3], int numVerts) { DerivedMesh *dm; @@ -3639,14 +4377,14 @@ static void smoothModifier_deformVerts( CDDM_calc_normals(dm); smoothModifier_do((SmoothModifierData *)md, ob, dm, - vertexCos, numVerts); + vertexCos, numVerts); dm->release(dm); } static void smoothModifier_deformVertsEM( - ModifierData *md, Object *ob, EditMesh *editData, - DerivedMesh *derivedData, float (*vertexCos)[3], int numVerts) + ModifierData *md, Object *ob, EditMesh *editData, + DerivedMesh *derivedData, float (*vertexCos)[3], int numVerts) { DerivedMesh *dm; @@ -3657,7 +4395,7 @@ static void smoothModifier_deformVertsEM( CDDM_calc_normals(dm); smoothModifier_do((SmoothModifierData *)md, ob, dm, - vertexCos, numVerts); + vertexCos, numVerts); dm->release(dm); } @@ -3672,7 +4410,7 @@ static void castModifier_initData(ModifierData *md) cmd->radius = 0.0f; cmd->size = 0.0f; cmd->flag = MOD_CAST_X | MOD_CAST_Y | MOD_CAST_Z - | MOD_CAST_SIZE_FROM_RADIUS; + | MOD_CAST_SIZE_FROM_RADIUS; cmd->type = MOD_CAST_TYPE_SPHERE; cmd->defgrp_name[0] = '\0'; cmd->object = NULL; @@ -3717,9 +4455,9 @@ CustomDataMask castModifier_requiredDataMask(ModifierData *md) } static void castModifier_foreachObjectLink( - ModifierData *md, Object *ob, - void (*walk)(void *userData, Object *ob, Object **obpoin), - void *userData) + ModifierData *md, Object *ob, + void (*walk)(void *userData, Object *ob, Object **obpoin), + void *userData) { CastModifierData *cmd = (CastModifierData*) md; @@ -3727,21 +4465,22 @@ static void castModifier_foreachObjectLink( } static void castModifier_updateDepgraph( - ModifierData *md, DagForest *forest, Object *ob, - DagNode *obNode) + ModifierData *md, DagForest *forest, Object *ob, + DagNode *obNode) { CastModifierData *cmd = (CastModifierData*) md; if (cmd->object) { DagNode *curNode = dag_get_node(forest, cmd->object); - dag_add_relation(forest, curNode, obNode, DAG_RL_OB_DATA); + dag_add_relation(forest, curNode, obNode, DAG_RL_OB_DATA, + "Cast Modifier"); } } static void castModifier_sphere_do( - CastModifierData *cmd, Object *ob, DerivedMesh *dm, - float (*vertexCos)[3], int numVerts) + CastModifierData *cmd, Object *ob, DerivedMesh *dm, + float (*vertexCos)[3], int numVerts) { MDeformVert *dvert = NULL; @@ -3766,8 +4505,8 @@ static void castModifier_sphere_do( ctrl_ob = cmd->object; /* spherify's center is {0, 0, 0} (the ob's own center in its local - * space), by default, but if the user defined a control object, - * we use its location, transformed to ob's local space */ + * space), by default, but if the user defined a control object, + * we use its location, transformed to ob's local space */ if (ctrl_ob) { if(flag & MOD_CAST_USE_OB_TRANSFORM) { Mat4Invert(ctrl_ob->imat, ctrl_ob->obmat); @@ -3784,11 +4523,11 @@ static void castModifier_sphere_do( /* 1) (flag was checked in the "if (ctrl_ob)" block above) */ /* 2) cmd->radius > 0.0f: only the vertices within this radius from - * the center of the effect should be deformed */ + * the center of the effect should be deformed */ if (cmd->radius > FLT_EPSILON) has_radius = 1; /* 3) if we were given a vertex group name, - * only those vertices should be affected */ + * only those vertices should be affected */ if (cmd->defgrp_name[0]) { bDeformGroup *def; @@ -3820,10 +4559,10 @@ static void castModifier_sphere_do( } /* ready to apply the effect, one vertex at a time; - * tiny optimization: the code is separated (with parts repeated) + * tiny optimization: the code is separated (with parts repeated) * in two possible cases: - * with or w/o a vgroup. With lots of if's in the code below, - * further optimizations are possible, if needed */ + * with or w/o a vgroup. With lots of if's in the code below, + * further optimizations are possible, if needed */ if (dvert) { /* with a vgroup */ float fac_orig = fac; for (i = 0; i < numVerts; i++) { @@ -3926,8 +4665,8 @@ static void castModifier_sphere_do( } static void castModifier_cuboid_do( - CastModifierData *cmd, Object *ob, DerivedMesh *dm, - float (*vertexCos)[3], int numVerts) + CastModifierData *cmd, Object *ob, DerivedMesh *dm, + float (*vertexCos)[3], int numVerts) { MDeformVert *dvert = NULL; Object *ctrl_ob = NULL; @@ -3951,11 +4690,11 @@ static void castModifier_cuboid_do( /* 1) (flag was checked in the "if (ctrl_ob)" block above) */ /* 2) cmd->radius > 0.0f: only the vertices within this radius from - * the center of the effect should be deformed */ + * the center of the effect should be deformed */ if (cmd->radius > FLT_EPSILON) has_radius = 1; /* 3) if we were given a vertex group name, - * only those vertices should be affected */ + * only those vertices should be affected */ if (cmd->defgrp_name[0]) { bDeformGroup *def; @@ -3995,12 +4734,12 @@ static void castModifier_cuboid_do( } else { /* get bound box */ /* We can't use the object's bound box because other modifiers - * may have changed the vertex data. */ + * may have changed the vertex data. */ INIT_MINMAX(min, max); /* Cast's center is the ob's own center in its local space, - * by default, but if the user defined a control object, we use - * its location, transformed to ob's local space. */ + * by default, but if the user defined a control object, we use + * its location, transformed to ob's local space. */ if (ctrl_ob) { float vec[3]; @@ -4036,10 +4775,10 @@ static void castModifier_cuboid_do( bb[4][2] = bb[5][2] = bb[6][2] = bb[7][2] = max[2]; /* ready to apply the effect, one vertex at a time; - * tiny optimization: the code is separated (with parts repeated) + * tiny optimization: the code is separated (with parts repeated) * in two possible cases: - * with or w/o a vgroup. With lots of if's in the code below, - * further optimizations are possible, if needed */ + * with or w/o a vgroup. With lots of if's in the code below, + * further optimizations are possible, if needed */ if (dvert) { /* with a vgroup */ float fac_orig = fac; for (i = 0; i < numVerts; i++) { @@ -4059,8 +4798,8 @@ static void castModifier_cuboid_do( if (has_radius) { if (fabs(tmp_co[0]) > cmd->radius || - fabs(tmp_co[1]) > cmd->radius || - fabs(tmp_co[2]) > cmd->radius) continue; + fabs(tmp_co[1]) > cmd->radius || + fabs(tmp_co[2]) > cmd->radius) continue; } for (j = 0; j < dvert[i].totweight; ++j) { @@ -4077,10 +4816,10 @@ static void castModifier_cuboid_do( /* The algo used to project the vertices to their * bounding box (bb) is pretty simple: * for each vertex v: - * 1) find in which octant v is in; - * 2) find which outer "wall" of that octant is closer to v; - * 3) calculate factor (var fbb) to project v to that wall; - * 4) project. */ + * 1) find in which octant v is in; + * 2) find which outer "wall" of that octant is closer to v; + * 3) calculate factor (var fbb) to project v to that wall; + * 4) project. */ /* find in which octant this vertex is in */ octant = 0; @@ -4114,7 +4853,7 @@ static void castModifier_cuboid_do( continue; /* finally, this is the factor we wanted, to project the vertex - * to its bounding box (bb) */ + * to its bounding box (bb) */ fbb = apex[coord] / tmp_co[coord]; /* calculate the new vertex position */ @@ -4155,8 +4894,8 @@ static void castModifier_cuboid_do( if (has_radius) { if (fabs(tmp_co[0]) > cmd->radius || - fabs(tmp_co[1]) > cmd->radius || - fabs(tmp_co[2]) > cmd->radius) continue; + fabs(tmp_co[1]) > cmd->radius || + fabs(tmp_co[2]) > cmd->radius) continue; } octant = 0; @@ -4206,8 +4945,8 @@ static void castModifier_cuboid_do( } static void castModifier_deformVerts( - ModifierData *md, Object *ob, DerivedMesh *derivedData, - float (*vertexCos)[3], int numVerts) + ModifierData *md, Object *ob, DerivedMesh *derivedData, + float (*vertexCos)[3], int numVerts) { DerivedMesh *dm = derivedData; CastModifierData *cmd = (CastModifierData *)md; @@ -4225,8 +4964,8 @@ static void castModifier_deformVerts( } static void castModifier_deformVertsEM( - ModifierData *md, Object *ob, EditMesh *editData, - DerivedMesh *derivedData, float (*vertexCos)[3], int numVerts) + ModifierData *md, Object *ob, EditMesh *editData, + DerivedMesh *derivedData, float (*vertexCos)[3], int numVerts) { DerivedMesh *dm = derivedData; CastModifierData *cmd = (CastModifierData *)md; @@ -4245,13 +4984,13 @@ static void castModifier_deformVertsEM( /* Wave */ -static void waveModifier_initData(ModifierData *md) +static void waveModifier_initData(ModifierData *md) { WaveModifierData *wmd = (WaveModifierData*) md; // whadya know, moved here from Iraq - + wmd->flag |= (MOD_WAVE_X | MOD_WAVE_Y | MOD_WAVE_CYCL - | MOD_WAVE_NORM_X | MOD_WAVE_NORM_Y | MOD_WAVE_NORM_Z); - + | MOD_WAVE_NORM_X | MOD_WAVE_NORM_Y | MOD_WAVE_NORM_Z); + wmd->objectcenter = NULL; wmd->texture = NULL; wmd->map_object = NULL; @@ -4261,6 +5000,7 @@ static void waveModifier_initData(ModifierData *md) wmd->narrow= 1.5f; wmd->lifetime= 0.0f; wmd->damp= 10.0f; + wmd->falloff= 0.0f; wmd->texmapping = MOD_WAV_MAP_LOCAL; wmd->defgrp_name[0] = 0; } @@ -4280,6 +5020,7 @@ static void waveModifier_copyData(ModifierData *md, ModifierData *target) twmd->starty = wmd->starty; twmd->timeoffs = wmd->timeoffs; twmd->width = wmd->width; + twmd->falloff = wmd->falloff; twmd->objectcenter = wmd->objectcenter; twmd->texture = wmd->texture; twmd->map_object = wmd->map_object; @@ -4293,8 +5034,8 @@ static int waveModifier_dependsOnTime(ModifierData *md) } static void waveModifier_foreachObjectLink( - ModifierData *md, Object *ob, - ObjectWalkFunc walk, void *userData) + ModifierData *md, Object *ob, + ObjectWalkFunc walk, void *userData) { WaveModifierData *wmd = (WaveModifierData*) md; @@ -4303,7 +5044,7 @@ static void waveModifier_foreachObjectLink( } static void waveModifier_foreachIDLink(ModifierData *md, Object *ob, - IDWalkFunc walk, void *userData) + IDWalkFunc walk, void *userData) { WaveModifierData *wmd = (WaveModifierData*) md; @@ -4313,21 +5054,23 @@ static void waveModifier_foreachIDLink(ModifierData *md, Object *ob, } static void waveModifier_updateDepgraph( - ModifierData *md, DagForest *forest, Object *ob, - DagNode *obNode) + ModifierData *md, DagForest *forest, Object *ob, + DagNode *obNode) { WaveModifierData *wmd = (WaveModifierData*) md; if(wmd->objectcenter) { DagNode *curNode = dag_get_node(forest, wmd->objectcenter); - dag_add_relation(forest, curNode, obNode, DAG_RL_OB_DATA); + dag_add_relation(forest, curNode, obNode, DAG_RL_OB_DATA, + "Wave Modifier"); } if(wmd->map_object) { DagNode *curNode = dag_get_node(forest, wmd->map_object); - dag_add_relation(forest, curNode, obNode, DAG_RL_OB_DATA); + dag_add_relation(forest, curNode, obNode, DAG_RL_OB_DATA, + "Wave Modifer"); } } @@ -4349,9 +5092,9 @@ CustomDataMask waveModifier_requiredDataMask(ModifierData *md) } static void wavemod_get_texture_coords(WaveModifierData *wmd, Object *ob, - DerivedMesh *dm, - float (*co)[3], float (*texco)[3], - int numVerts) + DerivedMesh *dm, + float (*co)[3], float (*texco)[3], + int numVerts) { int i; int texmapping = wmd->texmapping; @@ -4369,14 +5112,14 @@ static void wavemod_get_texture_coords(WaveModifierData *wmd, Object *ob, MFace *mface = dm->getFaceArray(dm); MFace *mf; char *done = MEM_callocN(sizeof(*done) * numVerts, - "get_texture_coords done"); + "get_texture_coords done"); int numFaces = dm->getNumFaces(dm); MTFace *tf; validate_layer_name(&dm->faceData, CD_MTFACE, wmd->uvlayer_name); tf = CustomData_get_layer_named(&dm->faceData, CD_MTFACE, - wmd->uvlayer_name); + wmd->uvlayer_name); /* verts are given the UV from the first face that uses them */ for(i = 0, mf = mface; i < numFaces; ++i, ++mf, ++tf) { @@ -4420,25 +5163,25 @@ static void wavemod_get_texture_coords(WaveModifierData *wmd, Object *ob, for(i = 0; i < numVerts; ++i, ++co, ++texco) { switch(texmapping) { - case MOD_WAV_MAP_LOCAL: - VECCOPY(*texco, *co); - break; - case MOD_WAV_MAP_GLOBAL: - VECCOPY(*texco, *co); - Mat4MulVecfl(ob->obmat, *texco); - break; - case MOD_WAV_MAP_OBJECT: - VECCOPY(*texco, *co); - Mat4MulVecfl(ob->obmat, *texco); - Mat4MulVecfl(wmd->map_object->imat, *texco); - break; + case MOD_WAV_MAP_LOCAL: + VECCOPY(*texco, *co); + break; + case MOD_WAV_MAP_GLOBAL: + VECCOPY(*texco, *co); + Mat4MulVecfl(ob->obmat, *texco); + break; + case MOD_WAV_MAP_OBJECT: + VECCOPY(*texco, *co); + Mat4MulVecfl(ob->obmat, *texco); + Mat4MulVecfl(wmd->map_object->imat, *texco); + break; } } } static void waveModifier_do( - WaveModifierData *md, Object *ob, DerivedMesh *dm, - float (*vertexCos)[3], int numVerts) + WaveModifierData *md, Object *ob, DerivedMesh *dm, + float (*vertexCos)[3], int numVerts) { WaveModifierData *wmd = (WaveModifierData*) md; MVert *mvert = NULL; @@ -4446,7 +5189,7 @@ static void waveModifier_do( int defgrp_index; float ctime = bsystem_time(ob, (float)G.scene->r.cfra, 0.0); float minfac = - (float)(1.0 / exp(wmd->width * wmd->narrow * wmd->width * wmd->narrow)); + (float)(1.0 / exp(wmd->width * wmd->narrow * wmd->width * wmd->narrow)); float lifefac = wmd->height; float (*tex_co)[3] = NULL; @@ -4488,16 +5231,16 @@ static void waveModifier_do( if(x > wmd->lifetime) { lifefac = x - wmd->lifetime; - + if(lifefac > wmd->damp) lifefac = 0.0; else lifefac = - (float)(wmd->height * (1.0 - sqrt(lifefac / wmd->damp))); + (float)(wmd->height * (1.0 - sqrt(lifefac / wmd->damp))); } } if(wmd->texture) { tex_co = MEM_mallocN(sizeof(*tex_co) * numVerts, - "waveModifier_do tex_co"); + "waveModifier_do tex_co"); wavemod_get_texture_coords(wmd, ob, dm, vertexCos, tex_co, numVerts); } @@ -4509,6 +5252,8 @@ static void waveModifier_do( float x = co[0] - wmd->startx; float y = co[1] - wmd->starty; float amplit= 0.0f; + float dist = 0.0f; + float falloff_fac = 0.0f; TexResult texres; MDeformWeight *def_weight = NULL; @@ -4531,32 +5276,54 @@ static void waveModifier_do( get_texture_value(wmd->texture, tex_co[i], &texres); } + /*get dist*/ + if(wmd->flag & MOD_WAVE_X) { + if(wmd->flag & MOD_WAVE_Y){ + dist = (float)sqrt(x*x + y*y); + } + else{ + dist = fabs(x); + } + } + else if(wmd->flag & MOD_WAVE_Y) { + dist = fabs(y); + } + + falloff_fac = (1.0-(dist / wmd->falloff)); + CLAMP(falloff_fac,0,1); if(wmd->flag & MOD_WAVE_X) { if(wmd->flag & MOD_WAVE_Y) amplit = (float)sqrt(x*x + y*y); else amplit = x; } - else if(wmd->flag & MOD_WAVE_Y) + else if(wmd->flag & MOD_WAVE_Y) amplit= y; - + /* this way it makes nice circles */ amplit -= (ctime - wmd->timeoffs) * wmd->speed; if(wmd->flag & MOD_WAVE_CYCL) { amplit = (float)fmod(amplit - wmd->width, 2.0 * wmd->width) - + wmd->width; + + wmd->width; } /* GAUSSIAN */ if(amplit > -wmd->width && amplit < wmd->width) { amplit = amplit * wmd->narrow; amplit = (float)(1.0 / exp(amplit * amplit) - minfac); + + /*apply texture*/ if(wmd->texture) amplit = amplit * texres.tin; + /*apply weight*/ if(def_weight) amplit = amplit * def_weight->weight; + /*apply falloff*/ + if (wmd->falloff > 0) + amplit = amplit * falloff_fac; + if(mvert) { /* move along normals */ if(wmd->flag & MOD_WAVE_NORM_X) { @@ -4581,8 +5348,8 @@ static void waveModifier_do( } static void waveModifier_deformVerts( - ModifierData *md, Object *ob, DerivedMesh *derivedData, - float (*vertexCos)[3], int numVerts) + ModifierData *md, Object *ob, DerivedMesh *derivedData, + float (*vertexCos)[3], int numVerts) { DerivedMesh *dm; WaveModifierData *wmd = (WaveModifierData *)md; @@ -4604,15 +5371,15 @@ static void waveModifier_deformVerts( } static void waveModifier_deformVertsEM( - ModifierData *md, Object *ob, EditMesh *editData, - DerivedMesh *derivedData, float (*vertexCos)[3], int numVerts) + ModifierData *md, Object *ob, EditMesh *editData, + DerivedMesh *derivedData, float (*vertexCos)[3], int numVerts) { DerivedMesh *dm; WaveModifierData *wmd = (WaveModifierData *)md; if(!wmd->texture && !wmd->defgrp_name[0] && !(wmd->flag & MOD_WAVE_NORM)) dm = derivedData; - else if(derivedData) dm = derivedData; + else if(derivedData) dm = CDDM_copy(derivedData); else dm = CDDM_from_editmesh(editData, ob->data); if(wmd->flag & MOD_WAVE_NORM) { @@ -4662,9 +5429,9 @@ static int armatureModifier_isDisabled(ModifierData *md) } static void armatureModifier_foreachObjectLink( - ModifierData *md, Object *ob, - void (*walk)(void *userData, Object *ob, Object **obpoin), - void *userData) + ModifierData *md, Object *ob, + void (*walk)(void *userData, Object *ob, Object **obpoin), + void *userData) { ArmatureModifierData *amd = (ArmatureModifierData*) md; @@ -4672,8 +5439,8 @@ static void armatureModifier_foreachObjectLink( } static void armatureModifier_updateDepgraph( - ModifierData *md, DagForest *forest, Object *ob, - DagNode *obNode) + ModifierData *md, DagForest *forest, Object *ob, + DagNode *obNode) { ArmatureModifierData *amd = (ArmatureModifierData*) md; @@ -4681,21 +5448,21 @@ static void armatureModifier_updateDepgraph( DagNode *curNode = dag_get_node(forest, amd->object); dag_add_relation(forest, curNode, obNode, - DAG_RL_DATA_DATA | DAG_RL_OB_DATA); + DAG_RL_DATA_DATA | DAG_RL_OB_DATA, "Armature Modifier"); } } static void armatureModifier_deformVerts( - ModifierData *md, Object *ob, DerivedMesh *derivedData, - float (*vertexCos)[3], int numVerts) + ModifierData *md, Object *ob, DerivedMesh *derivedData, + float (*vertexCos)[3], int numVerts) { ArmatureModifierData *amd = (ArmatureModifierData*) md; modifier_vgroup_cache(md, vertexCos); /* if next modifier needs original vertices */ armature_deform_verts(amd->object, ob, derivedData, vertexCos, NULL, - numVerts, amd->deformflag, - (float(*)[3])amd->prevCos, amd->defgrp_name); + numVerts, amd->deformflag, + (float(*)[3])amd->prevCos, amd->defgrp_name); /* free cache */ if(amd->prevCos) { MEM_freeN(amd->prevCos); @@ -4704,8 +5471,8 @@ static void armatureModifier_deformVerts( } static void armatureModifier_deformVertsEM( - ModifierData *md, Object *ob, EditMesh *editData, - DerivedMesh *derivedData, float (*vertexCos)[3], int numVerts) + ModifierData *md, Object *ob, EditMesh *editData, + DerivedMesh *derivedData, float (*vertexCos)[3], int numVerts) { ArmatureModifierData *amd = (ArmatureModifierData*) md; DerivedMesh *dm = derivedData; @@ -4713,15 +5480,15 @@ static void armatureModifier_deformVertsEM( if(!derivedData) dm = CDDM_from_editmesh(editData, ob->data); armature_deform_verts(amd->object, ob, dm, vertexCos, NULL, numVerts, - amd->deformflag, NULL, amd->defgrp_name); + amd->deformflag, NULL, amd->defgrp_name); if(!derivedData) dm->release(dm); } static void armatureModifier_deformMatricesEM( - ModifierData *md, Object *ob, EditMesh *editData, - DerivedMesh *derivedData, float (*vertexCos)[3], - float (*defMats)[3][3], int numVerts) + ModifierData *md, Object *ob, EditMesh *editData, + DerivedMesh *derivedData, float (*vertexCos)[3], + float (*defMats)[3][3], int numVerts) { ArmatureModifierData *amd = (ArmatureModifierData*) md; DerivedMesh *dm = derivedData; @@ -4729,7 +5496,7 @@ static void armatureModifier_deformMatricesEM( if(!derivedData) dm = CDDM_from_editmesh(editData, ob->data); armature_deform_verts(amd->object, ob, dm, vertexCos, defMats, numVerts, - amd->deformflag, NULL, amd->defgrp_name); + amd->deformflag, NULL, amd->defgrp_name); if(!derivedData) dm->release(dm); } @@ -4784,9 +5551,9 @@ static int hookModifier_isDisabled(ModifierData *md) } static void hookModifier_foreachObjectLink( - ModifierData *md, Object *ob, - void (*walk)(void *userData, Object *ob, Object **obpoin), - void *userData) + ModifierData *md, Object *ob, + void (*walk)(void *userData, Object *ob, Object **obpoin), + void *userData) { HookModifierData *hmd = (HookModifierData*) md; @@ -4794,20 +5561,21 @@ static void hookModifier_foreachObjectLink( } static void hookModifier_updateDepgraph(ModifierData *md, DagForest *forest, - Object *ob, DagNode *obNode) + Object *ob, DagNode *obNode) { HookModifierData *hmd = (HookModifierData*) md; if (hmd->object) { DagNode *curNode = dag_get_node(forest, hmd->object); - dag_add_relation(forest, curNode, obNode, DAG_RL_OB_DATA); + dag_add_relation(forest, curNode, obNode, DAG_RL_OB_DATA, + "Hook Modifier"); } } static void hookModifier_deformVerts( - ModifierData *md, Object *ob, DerivedMesh *derivedData, - float (*vertexCos)[3], int numVerts) + ModifierData *md, Object *ob, DerivedMesh *derivedData, + float (*vertexCos)[3], int numVerts) { HookModifierData *hmd = (HookModifierData*) md; float vec[3], mat[4][4]; @@ -4816,7 +5584,7 @@ static void hookModifier_deformVerts( Mat4Invert(ob->imat, ob->obmat); Mat4MulSerie(mat, ob->imat, hmd->object->obmat, hmd->parentinv, - NULL, NULL, NULL, NULL, NULL); + NULL, NULL, NULL, NULL, NULL); /* vertex indices? */ if(hmd->indexar) { @@ -4824,24 +5592,24 @@ static void hookModifier_deformVerts( int index = hmd->indexar[i]; /* This should always be true and I don't generally like - * "paranoid" style code like this, but old files can have - * indices that are out of range because old blender did - * not correct them on exit editmode. - zr - */ + * "paranoid" style code like this, but old files can have + * indices that are out of range because old blender did + * not correct them on exit editmode. - zr + */ if(index < numVerts) { float *co = vertexCos[index]; float fac = hmd->force; /* if DerivedMesh is present and has original index data, - * use it - */ + * use it + */ if(dm && dm->getVertData(dm, 0, CD_ORIGINDEX)) { int j; int orig_index; for(j = 0; j < numVerts; ++j) { fac = hmd->force; orig_index = *(int *)dm->getVertData(dm, j, - CD_ORIGINDEX); + CD_ORIGINDEX); if(orig_index == index) { co = vertexCos[j]; if(hmd->falloff != 0.0) { @@ -4885,44 +5653,44 @@ static void hookModifier_deformVerts( if(dm) if(dm->getVertData(dm, 0, CD_MDEFORMVERT)) { + use_dverts = 1; + maxVerts = dm->getNumVerts(dm); + } else use_dverts = 0; + else if(me->dvert) { use_dverts = 1; - maxVerts = dm->getNumVerts(dm); + maxVerts = me->totvert; } else use_dverts = 0; - else if(me->dvert) { - use_dverts = 1; - maxVerts = me->totvert; - } else use_dverts = 0; - if(curdef && use_dverts) { - MDeformVert *dvert = me->dvert; - int i, j; + if(curdef && use_dverts) { + MDeformVert *dvert = me->dvert; + int i, j; - for(i = 0; i < maxVerts; i++, dvert++) { - if(dm) dvert = dm->getVertData(dm, i, CD_MDEFORMVERT); - for(j = 0; j < dvert->totweight; j++) { - if(dvert->dw[j].def_nr == index) { - float fac = hmd->force*dvert->dw[j].weight; - float *co = vertexCos[i]; + for(i = 0; i < maxVerts; i++, dvert++) { + if(dm) dvert = dm->getVertData(dm, i, CD_MDEFORMVERT); + for(j = 0; j < dvert->totweight; j++) { + if(dvert->dw[j].def_nr == index) { + float fac = hmd->force*dvert->dw[j].weight; + float *co = vertexCos[i]; - if(hmd->falloff != 0.0) { - float len = VecLenf(co, hmd->cent); - if(len > hmd->falloff) fac = 0.0; - else if(len > 0.0) - fac *= sqrt(1.0 - len / hmd->falloff); - } + if(hmd->falloff != 0.0) { + float len = VecLenf(co, hmd->cent); + if(len > hmd->falloff) fac = 0.0; + else if(len > 0.0) + fac *= sqrt(1.0 - len / hmd->falloff); + } - VecMat4MulVecfl(vec, mat, co); - VecLerpf(co, co, vec, fac); + VecMat4MulVecfl(vec, mat, co); + VecLerpf(co, co, vec, fac); + } } } } - } } } static void hookModifier_deformVertsEM( - ModifierData *md, Object *ob, EditMesh *editData, - DerivedMesh *derivedData, float (*vertexCos)[3], int numVerts) + ModifierData *md, Object *ob, EditMesh *editData, + DerivedMesh *derivedData, float (*vertexCos)[3], int numVerts) { DerivedMesh *dm = derivedData; @@ -4936,12 +5704,322 @@ static void hookModifier_deformVertsEM( /* Softbody */ static void softbodyModifier_deformVerts( - ModifierData *md, Object *ob, DerivedMesh *derivedData, - float (*vertexCos)[3], int numVerts) + ModifierData *md, Object *ob, DerivedMesh *derivedData, + float (*vertexCos)[3], int numVerts) { sbObjectStep(ob, (float)G.scene->r.cfra, vertexCos, numVerts); } +static int softbodyModifier_dependsOnTime(ModifierData *md) +{ + return 1; +} + + +/* Cloth */ + +static void clothModifier_initData(ModifierData *md) +{ + ClothModifierData *clmd = (ClothModifierData*) md; + + clmd->sim_parms = MEM_callocN(sizeof(ClothSimSettings), "cloth sim parms"); + clmd->coll_parms = MEM_callocN(sizeof(ClothCollSettings), "cloth coll parms"); + clmd->point_cache = BKE_ptcache_add(); + + /* check for alloc failing */ + if(!clmd->sim_parms || !clmd->coll_parms || !clmd->point_cache) + return; + + cloth_init (clmd); +} + +static DerivedMesh *clothModifier_applyModifier(ModifierData *md, Object *ob, + DerivedMesh *derivedData, int useRenderParams, int isFinalCalc) +{ + ClothModifierData *clmd = (ClothModifierData*) md; + DerivedMesh *result=NULL; + + /* check for alloc failing */ + if(!clmd->sim_parms || !clmd->coll_parms) + { + clothModifier_initData(md); + + if(!clmd->sim_parms || !clmd->coll_parms) + return derivedData; + } + + result = clothModifier_do(clmd, ob, derivedData, useRenderParams, isFinalCalc); + + if(result) + { + CDDM_calc_normals(result); + return result; + } + return derivedData; +} + +static void clothModifier_updateDepgraph( + ModifierData *md, DagForest *forest, Object *ob, + DagNode *obNode) +{ + ClothModifierData *clmd = (ClothModifierData*) md; + + Base *base; + + if(clmd) + { + for(base = G.scene->base.first; base; base= base->next) + { + Object *ob1= base->object; + if(ob1 != ob) + { + CollisionModifierData *coll_clmd = (CollisionModifierData *)modifiers_findByType(ob1, eModifierType_Collision); + if(coll_clmd) + { + DagNode *curNode = dag_get_node(forest, ob1); + dag_add_relation(forest, curNode, obNode, DAG_RL_DATA_DATA|DAG_RL_OB_DATA, "Cloth Collision"); + } + } + } + } +} + +CustomDataMask clothModifier_requiredDataMask(ModifierData *md) +{ + CustomDataMask dataMask = 0; + + /* ask for vertexgroups if we need them */ + dataMask |= (1 << CD_MDEFORMVERT); + + return dataMask; +} + +static void clothModifier_copyData(ModifierData *md, ModifierData *target) +{ + ClothModifierData *clmd = (ClothModifierData*) md; + ClothModifierData *tclmd = (ClothModifierData*) target; + + if(tclmd->sim_parms) + MEM_freeN(tclmd->sim_parms); + if(tclmd->coll_parms) + MEM_freeN(tclmd->coll_parms); + if(tclmd->point_cache) + BKE_ptcache_free(tclmd->point_cache); + + tclmd->sim_parms = MEM_dupallocN(clmd->sim_parms); + tclmd->coll_parms = MEM_dupallocN(clmd->coll_parms); + tclmd->point_cache = BKE_ptcache_copy(clmd->point_cache); + tclmd->clothObject = NULL; +} + +static int clothModifier_dependsOnTime(ModifierData *md) +{ + return 1; +} + +static void clothModifier_freeData(ModifierData *md) +{ + ClothModifierData *clmd = (ClothModifierData*) md; + + if (clmd) + { + if(G.rt > 0) + printf("clothModifier_freeData\n"); + + cloth_free_modifier_extern (clmd); + + if(clmd->sim_parms) + MEM_freeN(clmd->sim_parms); + if(clmd->coll_parms) + MEM_freeN(clmd->coll_parms); + if(clmd->point_cache) + BKE_ptcache_free(clmd->point_cache); + } +} + +/* Collision */ + +static void collisionModifier_initData(ModifierData *md) +{ + CollisionModifierData *collmd = (CollisionModifierData*) md; + + collmd->x = NULL; + collmd->xnew = NULL; + collmd->current_x = NULL; + collmd->current_xnew = NULL; + collmd->current_v = NULL; + collmd->time = -1; + collmd->numverts = 0; + collmd->bvhtree = NULL; +} + +static void collisionModifier_freeData(ModifierData *md) +{ + CollisionModifierData *collmd = (CollisionModifierData*) md; + + if (collmd) + { + if(collmd->bvhtree) + BLI_bvhtree_free(collmd->bvhtree); + if(collmd->x) + MEM_freeN(collmd->x); + if(collmd->xnew) + MEM_freeN(collmd->xnew); + if(collmd->current_x) + MEM_freeN(collmd->current_x); + if(collmd->current_xnew) + MEM_freeN(collmd->current_xnew); + if(collmd->current_v) + MEM_freeN(collmd->current_v); + if(collmd->mfaces) + MEM_freeN(collmd->mfaces); + + collmd->x = NULL; + collmd->xnew = NULL; + collmd->current_x = NULL; + collmd->current_xnew = NULL; + collmd->current_v = NULL; + collmd->time = -1; + collmd->numverts = 0; + collmd->bvhtree = NULL; + collmd->mfaces = NULL; + } +} + +static int collisionModifier_dependsOnTime(ModifierData *md) +{ + return 1; +} + +static void collisionModifier_deformVerts( + ModifierData *md, Object *ob, DerivedMesh *derivedData, + float (*vertexCos)[3], int numVerts) +{ + CollisionModifierData *collmd = (CollisionModifierData*) md; + DerivedMesh *dm = NULL; + float current_time = 0; + unsigned int numverts = 0, i = 0; + MVert *tempVert = NULL; + + /* if possible use/create DerivedMesh */ + if(derivedData) dm = CDDM_copy(derivedData); + else if(ob->type==OB_MESH) dm = CDDM_from_mesh(ob->data, ob); + + if(!ob->pd) + { + printf("collisionModifier_deformVerts: Should not happen!\n"); + return; + } + + if(dm) + { + CDDM_apply_vert_coords(dm, vertexCos); + CDDM_calc_normals(dm); + + current_time = bsystem_time ( ob, ( float ) G.scene->r.cfra, 0.0 ); + + if(G.rt > 0) + printf("current_time %f, collmd->time %f\n", current_time, collmd->time); + + numverts = dm->getNumVerts ( dm ); + + if((current_time > collmd->time)|| (BKE_ptcache_get_continue_physics())) + { + // check if mesh has changed + if(collmd->x && (numverts != collmd->numverts)) + collisionModifier_freeData((ModifierData *)collmd); + + if(collmd->time == -1) // first time + { + collmd->x = dm->dupVertArray(dm); // frame start position + + for ( i = 0; i < numverts; i++ ) + { + // we save global positions + Mat4MulVecfl ( ob->obmat, collmd->x[i].co ); + } + + collmd->xnew = MEM_dupallocN(collmd->x); // frame end position + collmd->current_x = MEM_dupallocN(collmd->x); // inter-frame + collmd->current_xnew = MEM_dupallocN(collmd->x); // inter-frame + collmd->current_v = MEM_dupallocN(collmd->x); // inter-frame + + collmd->numverts = numverts; + + collmd->mfaces = dm->dupFaceArray(dm); + collmd->numfaces = dm->getNumFaces(dm); + + // create bounding box hierarchy + collmd->bvhtree = bvhtree_build_from_mvert(collmd->mfaces, collmd->numfaces, collmd->x, numverts, ob->pd->pdef_sboft); + + collmd->time = current_time; + } + else if(numverts == collmd->numverts) + { + // put positions to old positions + tempVert = collmd->x; + collmd->x = collmd->xnew; + collmd->xnew = tempVert; + + memcpy(collmd->xnew, dm->getVertArray(dm), numverts*sizeof(MVert)); + + for ( i = 0; i < numverts; i++ ) + { + // we save global positions + Mat4MulVecfl ( ob->obmat, collmd->xnew[i].co ); + } + + memcpy(collmd->current_xnew, collmd->x, numverts*sizeof(MVert)); + memcpy(collmd->current_x, collmd->x, numverts*sizeof(MVert)); + + /* check if GUI setting has changed for bvh */ + if(collmd->bvhtree) + { + if(ob->pd->pdef_sboft != BLI_bvhtree_getepsilon(collmd->bvhtree)) + { + BLI_bvhtree_free(collmd->bvhtree); + collmd->bvhtree = bvhtree_build_from_mvert(collmd->mfaces, collmd->numfaces, collmd->current_x, numverts, ob->pd->pdef_sboft); + } + + } + + /* happens on file load (ONLY when i decomment changes in readfile.c) */ + if(!collmd->bvhtree) + { + collmd->bvhtree = bvhtree_build_from_mvert(collmd->mfaces, collmd->numfaces, collmd->current_x, numverts, ob->pd->pdef_sboft); + } + else + { + // recalc static bounding boxes + bvhtree_update_from_mvert ( collmd->bvhtree, collmd->mfaces, collmd->numfaces, collmd->current_x, collmd->current_xnew, collmd->numverts, 1 ); + } + + collmd->time = current_time; + } + else if(numverts != collmd->numverts) + { + collisionModifier_freeData((ModifierData *)collmd); + } + + } + else if(current_time < collmd->time) + { + collisionModifier_freeData((ModifierData *)collmd); + } + else + { + if(numverts != collmd->numverts) + { + collisionModifier_freeData((ModifierData *)collmd); + } + } + } + + if(dm) + dm->release(dm); +} + + /* Boolean */ static void booleanModifier_copyData(ModifierData *md, ModifierData *target) @@ -4961,9 +6039,9 @@ static int booleanModifier_isDisabled(ModifierData *md) } static void booleanModifier_foreachObjectLink( - ModifierData *md, Object *ob, - void (*walk)(void *userData, Object *ob, Object **obpoin), - void *userData) + ModifierData *md, Object *ob, + void (*walk)(void *userData, Object *ob, Object **obpoin), + void *userData) { BooleanModifierData *bmd = (BooleanModifierData*) md; @@ -4971,8 +6049,8 @@ static void booleanModifier_foreachObjectLink( } static void booleanModifier_updateDepgraph( - ModifierData *md, DagForest *forest, Object *ob, - DagNode *obNode) + ModifierData *md, DagForest *forest, Object *ob, + DagNode *obNode) { BooleanModifierData *bmd = (BooleanModifierData*) md; @@ -4980,32 +6058,32 @@ static void booleanModifier_updateDepgraph( DagNode *curNode = dag_get_node(forest, bmd->object); dag_add_relation(forest, curNode, obNode, - DAG_RL_DATA_DATA | DAG_RL_OB_DATA); + DAG_RL_DATA_DATA | DAG_RL_OB_DATA, "Boolean Modifier"); } } static DerivedMesh *booleanModifier_applyModifier( - ModifierData *md, Object *ob, DerivedMesh *derivedData, - int useRenderParams, int isFinalCalc) + ModifierData *md, Object *ob, DerivedMesh *derivedData, + int useRenderParams, int isFinalCalc) { // XXX doesn't handle derived data BooleanModifierData *bmd = (BooleanModifierData*) md; /* we do a quick sanity check */ if(((Mesh *)ob->data)->totface > 3 - && bmd->object && ((Mesh *)bmd->object->data)->totface > 3) { + && bmd->object && ((Mesh *)bmd->object->data)->totface > 3) { DerivedMesh *result = NewBooleanDerivedMesh(bmd->object, ob, - 1 + bmd->operation); + 1 + bmd->operation); /* if new mesh returned, return it; otherwise there was - * an error, so delete the modifier object */ + * an error, so delete the modifier object */ if(result) return result; else bmd->object = NULL; - } + } - return derivedData; + return derivedData; } /* Particles */ @@ -5026,7 +6104,6 @@ static void particleSystemModifier_freeData(ModifierData *md) psmd->dm=0; } - psmd->psys->flag &= ~PSYS_ENABLED; psmd->psys->flag |= PSYS_DELETE; } static void particleSystemModifier_copyData(ModifierData *md, ModifierData *target) @@ -5035,6 +6112,7 @@ static void particleSystemModifier_copyData(ModifierData *md, ModifierData *targ ParticleSystemModifierData *tpsmd= (ParticleSystemModifierData*) target; tpsmd->dm = 0; + tpsmd->totdmvert = tpsmd->totdmedge = tpsmd->totdmface = 0; //tpsmd->facepa = 0; tpsmd->flag = psmd->flag; /* need to keep this to recognise a bit later in copy_object */ @@ -5056,7 +6134,7 @@ CustomDataMask particleSystemModifier_requiredDataMask(ModifierData *md) } /* particles only need this if they are after a non deform modifier, and - * the modifier stack will only create them in that case. */ + * the modifier stack will only create them in that case. */ dataMask |= CD_MASK_ORIGSPACE; dataMask |= CD_MASK_ORCO; @@ -5088,12 +6166,13 @@ static int is_last_displist(Object *ob) } /* saves the current emitter state for a particle system and calculates particles */ static void particleSystemModifier_deformVerts( - ModifierData *md, Object *ob, DerivedMesh *derivedData, - float (*vertexCos)[3], int numVerts) + ModifierData *md, Object *ob, DerivedMesh *derivedData, + float (*vertexCos)[3], int numVerts) { DerivedMesh *dm = derivedData; ParticleSystemModifierData *psmd= (ParticleSystemModifierData*) md; ParticleSystem * psys=0; + Mesh *me; int needsFree=0; if(ob->particlesystem.first) @@ -5101,6 +6180,14 @@ static void particleSystemModifier_deformVerts( else return; + /* multires check */ + if(ob->type == OB_MESH) { + me= (Mesh*)ob->data; + if(me->mr && me->mr->current != 1) + modifier_setError(md, + "Particles only supported on first multires level."); + } + if(!psys_check_enabled(ob, psys)) return; @@ -5151,6 +6238,7 @@ static void particleSystemModifier_deformVerts( /* make new dm */ psmd->dm=CDDM_copy(dm); + CDDM_apply_vert_coords(psmd->dm, vertexCos); CDDM_calc_normals(psmd->dm); if(needsFree){ @@ -5163,8 +6251,8 @@ static void particleSystemModifier_deformVerts( /* report change in mesh structure */ if(psmd->dm->getNumVerts(psmd->dm)!=psmd->totdmvert || - psmd->dm->getNumEdges(psmd->dm)!=psmd->totdmedge || - psmd->dm->getNumFaces(psmd->dm)!=psmd->totdmface){ + psmd->dm->getNumEdges(psmd->dm)!=psmd->totdmedge || + psmd->dm->getNumFaces(psmd->dm)!=psmd->totdmface){ /* in file read dm hasn't really changed but just wasn't saved in file */ psys->recalc |= PSYS_RECALC_HAIR; @@ -5174,13 +6262,13 @@ static void particleSystemModifier_deformVerts( psmd->totdmvert= psmd->dm->getNumVerts(psmd->dm); psmd->totdmedge= psmd->dm->getNumEdges(psmd->dm); psmd->totdmface= psmd->dm->getNumFaces(psmd->dm); - } + } - if(psys){ - particle_system_update(ob,psys); - psmd->flag |= eParticleSystemFlag_psys_updated; - psmd->flag &= ~eParticleSystemFlag_DM_changed; - } + if(psys){ + particle_system_update(ob,psys); + psmd->flag |= eParticleSystemFlag_psys_updated; + psmd->flag &= ~eParticleSystemFlag_DM_changed; + } } /* disabled particles in editmode for now, until support for proper derivedmesh @@ -5206,7 +6294,7 @@ static void particleInstanceModifier_initData(ModifierData *md) ParticleInstanceModifierData *pimd= (ParticleInstanceModifierData*) md; pimd->flag = eParticleInstanceFlag_Parents|eParticleInstanceFlag_Unborn| - eParticleInstanceFlag_Alive|eParticleInstanceFlag_Dead; + eParticleInstanceFlag_Alive|eParticleInstanceFlag_Dead; pimd->psys = 1; } @@ -5225,7 +6313,7 @@ static int particleInstanceModifier_dependsOnTime(ModifierData *md) return 0; } static void particleInstanceModifier_updateDepgraph(ModifierData *md, DagForest *forest, - Object *ob, DagNode *obNode) + Object *ob, DagNode *obNode) { ParticleInstanceModifierData *pimd = (ParticleInstanceModifierData*) md; @@ -5233,12 +6321,13 @@ static void particleInstanceModifier_updateDepgraph(ModifierData *md, DagForest DagNode *curNode = dag_get_node(forest, pimd->ob); dag_add_relation(forest, curNode, obNode, - DAG_RL_DATA_DATA | DAG_RL_OB_DATA); + DAG_RL_DATA_DATA | DAG_RL_OB_DATA, + "Particle Instance Modifier"); } } static void particleInstanceModifier_foreachObjectLink(ModifierData *md, Object *ob, - ObjectWalkFunc walk, void *userData) + ObjectWalkFunc walk, void *userData) { ParticleInstanceModifierData *pimd = (ParticleInstanceModifierData*) md; @@ -5246,8 +6335,8 @@ static void particleInstanceModifier_foreachObjectLink(ModifierData *md, Object } static DerivedMesh * particleInstanceModifier_applyModifier( - ModifierData *md, Object *ob, DerivedMesh *derivedData, - int useRenderParams, int isFinalCalc) + ModifierData *md, Object *ob, DerivedMesh *derivedData, + int useRenderParams, int isFinalCalc) { DerivedMesh *dm = derivedData, *result; ParticleInstanceModifierData *pimd= (ParticleInstanceModifierData*) md; @@ -5257,7 +6346,7 @@ static DerivedMesh * particleInstanceModifier_applyModifier( MVert *mvert, *orig_mvert; int i,totvert, totpart=0, totface, maxvert, maxface, first_particle=0; short track=ob->trackflag%3, trackneg; - float max_co=0.0, min_co=0.0, temp_co[3]; + float max_co=0.0, min_co=0.0, temp_co[3], cross[3]; trackneg=((ob->trackflag>2)?1:0); @@ -5336,15 +6425,29 @@ static DerivedMesh * particleInstanceModifier_applyModifier( if(trackneg) state.time=1.0f-state.time; psys_get_particle_on_path(pimd->ob,psys,first_particle + i/totvert,&state,1); + + mv->co[0] = 0.0; + + Normalize(state.vel); + + if(state.vel[0] < -0.9999 || state.vel[0] > 0.9999) { + state.rot[0] = 1.0; + state.rot[1] = state.rot[2] = state.rot[3] = 0.0f; + } + else { + /* a cross product of state.vel and a unit vector in x-direction */ + cross[0] = 0.0f; + cross[1] = -state.vel[2]; + cross[2] = state.vel[1]; + + /* state.vel[0] is the only component surviving from a dot product with a vector in x-direction*/ + VecRotToQuat(cross,saacos(state.vel[0]),state.rot); + } } else{ state.time=-1.0; psys_get_particle_state(pimd->ob,psys,i/totvert,&state,1); - } - - /*displace vertice to path location*/ - if(pimd->flag & eParticleInstanceFlag_Path) - mv->co[0]=0.0; + } QuatMulVecf(state.rot,mv->co); VECADD(mv->co,mv->co,state.co); @@ -5402,8 +6505,8 @@ static DerivedMesh * particleInstanceModifier_applyModifier( return result; } static DerivedMesh *particleInstanceModifier_applyModifierEM( - ModifierData *md, Object *ob, EditMesh *editData, - DerivedMesh *derivedData) + ModifierData *md, Object *ob, EditMesh *editData, + DerivedMesh *derivedData) { return particleInstanceModifier_applyModifier(md, ob, derivedData, 0, 1); } @@ -5429,6 +6532,8 @@ static void explodeModifier_copyData(ModifierData *md, ModifierData *target) temd->facepa = 0; temd->flag = emd->flag; + temd->protect = emd->protect; + temd->vgroup = emd->vgroup; } static int explodeModifier_dependsOnTime(ModifierData *md) { @@ -5445,25 +6550,9 @@ CustomDataMask explodeModifier_requiredDataMask(ModifierData *md) return dataMask; } -/* this should really be put somewhere permanently */ -static float vert_weight(MDeformVert *dvert, int group) -{ - MDeformWeight *dw; - int i; - - if(dvert) { - dw= dvert->dw; - for(i= dvert->totweight; i>0; i--, dw++) { - if(dw->def_nr == group) return dw->weight; - if(i==1) break; /*otherwise dw will point to somewhere it shouldn't*/ - } - } - return 0.0; -} - static void explodeModifier_createFacepa(ExplodeModifierData *emd, - ParticleSystemModifierData *psmd, - Object *ob, DerivedMesh *dm) + ParticleSystemModifierData *psmd, + Object *ob, DerivedMesh *dm) { ParticleSystem *psys=psmd->psys; MFace *fa=0, *mface=0; @@ -5504,7 +6593,7 @@ static void explodeModifier_createFacepa(ExplodeModifierData *emd, for(i=0; i<totvert; i++){ val = BLI_frand(); val = (1.0f-emd->protect)*val + emd->protect*0.5f; - if(val < vert_weight(dvert+i,emd->vgroup-1)) + if(val < deformvert_get_weight(dvert+i,emd->vgroup-1)) vertpa[i] = -1; } } @@ -5513,7 +6602,7 @@ static void explodeModifier_createFacepa(ExplodeModifierData *emd, /* make tree of emitter locations */ tree=BLI_kdtree_new(totpart); for(p=0,pa=psys->particles; p<totpart; p++,pa++){ - psys_particle_on_dm(ob,psmd->dm,psys->part->from,pa->num,pa->num_dmcache,pa->fuv,pa->foffset,co,0,0,0,0,0); + psys_particle_on_dm(psmd->dm,psys->part->from,pa->num,pa->num_dmcache,pa->fuv,pa->foffset,co,0,0,0,0,0); BLI_kdtree_insert(tree, p, co, NULL); } BLI_kdtree_balance(tree); @@ -5549,20 +6638,29 @@ static void explodeModifier_createFacepa(ExplodeModifierData *emd, if(vertpa) MEM_freeN(vertpa); BLI_kdtree_free(tree); } + +static int edgesplit_get(EdgeHash *edgehash, int v1, int v2) +{ + return GET_INT_FROM_POINTER(BLI_edgehash_lookup(edgehash, v1, v2)); +} + static DerivedMesh * explodeModifier_splitEdges(ExplodeModifierData *emd, DerivedMesh *dm){ DerivedMesh *splitdm; MFace *mf=0,*df1=0,*df2=0,*df3=0; MFace *mface=CDDM_get_faces(dm); MVert *dupve, *mv; + EdgeHash *edgehash; + EdgeHashIterator *ehi; int totvert=dm->getNumVerts(dm); int totface=dm->getNumFaces(dm); - int *edgesplit = MEM_callocN(sizeof(int)*totvert*totvert,"explode_edgesplit"); - int *facesplit = MEM_callocN(sizeof(int)*totface,"explode_edgesplit"); + int *facesplit = MEM_callocN(sizeof(int)*totface,"explode_facesplit"); int *vertpa = MEM_callocN(sizeof(int)*totvert,"explode_vertpa2"); int *facepa = emd->facepa; int *fs, totesplit=0,totfsplit=0,totin=0,curdupvert=0,curdupface=0,curdupin=0; - int i,j,v1,v2,v3,v4; + int i,j,v1,v2,v3,v4,esplit; + + edgehash= BLI_edgehash_new(); /* recreate vertpa from facepa calculation */ for (i=0,mf=mface; i<totface; i++,mf++) { @@ -5582,22 +6680,22 @@ static DerivedMesh * explodeModifier_splitEdges(ExplodeModifierData *emd, Derive v4=vertpa[mf->v4]; if(v1!=v2){ - edgesplit[mf->v1*totvert+mf->v2]=edgesplit[mf->v2*totvert+mf->v1]=1; + BLI_edgehash_insert(edgehash, mf->v1, mf->v2, NULL); (*fs)++; } if(v2!=v3){ - edgesplit[mf->v2*totvert+mf->v3]=edgesplit[mf->v3*totvert+mf->v2]=1; + BLI_edgehash_insert(edgehash, mf->v2, mf->v3, NULL); (*fs)++; } if(v3!=v4){ - edgesplit[mf->v3*totvert+mf->v4]=edgesplit[mf->v4*totvert+mf->v3]=1; + BLI_edgehash_insert(edgehash, mf->v3, mf->v4, NULL); (*fs)++; } if(v1!=v4){ - edgesplit[mf->v1*totvert+mf->v4]=edgesplit[mf->v4*totvert+mf->v1]=1; + BLI_edgehash_insert(edgehash, mf->v1, mf->v4, NULL); (*fs)++; } @@ -5606,28 +6704,29 @@ static DerivedMesh * explodeModifier_splitEdges(ExplodeModifierData *emd, Derive *fs=1; else if(v1!=v2){ if(v1!=v4) - edgesplit[mf->v2*totvert+mf->v3]=edgesplit[mf->v3*totvert+mf->v2]=1; + BLI_edgehash_insert(edgehash, mf->v2, mf->v3, NULL); else - edgesplit[mf->v3*totvert+mf->v4]=edgesplit[mf->v4*totvert+mf->v3]=1; + BLI_edgehash_insert(edgehash, mf->v3, mf->v4, NULL); } else{ if(v1!=v4) - edgesplit[mf->v1*totvert+mf->v2]=edgesplit[mf->v2*totvert+mf->v1]=1; + BLI_edgehash_insert(edgehash, mf->v1, mf->v2, NULL); else - edgesplit[mf->v1*totvert+mf->v4]=edgesplit[mf->v4*totvert+mf->v1]=1; + BLI_edgehash_insert(edgehash, mf->v1, mf->v4, NULL); } } } } /* count splits & reindex */ + ehi= BLI_edgehashIterator_new(edgehash); totesplit=totvert; - for(j=0; j<totvert; j++){ - for(i=j+1; i<totvert; i++){ - if(edgesplit[j*totvert+i]) - edgesplit[j*totvert+i]=edgesplit[i*totvert+j]=totesplit++; - } + for(; !BLI_edgehashIterator_isDone(ehi); BLI_edgehashIterator_step(ehi)) { + BLI_edgehashIterator_setValue(ehi, SET_INT_IN_POINTER(totesplit)); + totesplit++; } + BLI_edgehashIterator_free(ehi); + /* count new faces due to splitting */ for(i=0,fs=facesplit; i<totface; i++,fs++){ if(*fs==1) @@ -5675,23 +6774,23 @@ static DerivedMesh * explodeModifier_splitEdges(ExplodeModifierData *emd, Derive /* create new verts */ curdupvert=totvert; - for(j=0; j<totvert; j++){ - for(i=j+1; i<totvert; i++){ - if(edgesplit[j*totvert+i]){ - mv=CDDM_get_vert(splitdm,j); - dupve=CDDM_get_vert(splitdm,edgesplit[j*totvert+i]); + ehi= BLI_edgehashIterator_new(edgehash); + for(; !BLI_edgehashIterator_isDone(ehi); BLI_edgehashIterator_step(ehi)) { + BLI_edgehashIterator_getKey(ehi, &i, &j); + esplit= GET_INT_FROM_POINTER(BLI_edgehashIterator_getValue(ehi)); + mv=CDDM_get_vert(splitdm,j); + dupve=CDDM_get_vert(splitdm,esplit); - DM_copy_vert_data(splitdm,splitdm,j,edgesplit[j*totvert+i],1); + DM_copy_vert_data(splitdm,splitdm,j,esplit,1); - *dupve=*mv; + *dupve=*mv; - mv=CDDM_get_vert(splitdm,i); + mv=CDDM_get_vert(splitdm,i); - VECADD(dupve->co,dupve->co,mv->co); - VecMulf(dupve->co,0.5); - } - } + VECADD(dupve->co,dupve->co,mv->co); + VecMulf(dupve->co,0.5); } + BLI_edgehashIterator_free(ehi); /* create new faces */ curdupface=totface; @@ -5712,14 +6811,14 @@ static DerivedMesh * explodeModifier_splitEdges(ExplodeModifierData *emd, Derive curdupface++; if(v1==v2){ - df1->v1=edgesplit[mf->v1*totvert+mf->v4]; - df1->v2=edgesplit[mf->v2*totvert+mf->v3]; + df1->v1=edgesplit_get(edgehash, mf->v1, mf->v4); + df1->v2=edgesplit_get(edgehash, mf->v2, mf->v3); mf->v3=df1->v2; mf->v4=df1->v1; } else{ - df1->v1=edgesplit[mf->v1*totvert+mf->v2]; - df1->v4=edgesplit[mf->v3*totvert+mf->v4]; + df1->v1=edgesplit_get(edgehash, mf->v1, mf->v2); + df1->v4=edgesplit_get(edgehash, mf->v3, mf->v4); mf->v2=df1->v1; mf->v3=df1->v4; } @@ -5742,8 +6841,8 @@ static DerivedMesh * explodeModifier_splitEdges(ExplodeModifierData *emd, Derive if(v1!=v2){ if(v1!=v4){ - df1->v1=edgesplit[mf->v1*totvert+mf->v4]; - df1->v2=edgesplit[mf->v1*totvert+mf->v2]; + df1->v1=edgesplit_get(edgehash, mf->v1, mf->v4); + df1->v2=edgesplit_get(edgehash, mf->v1, mf->v2); df2->v1=df1->v3=mf->v2; df2->v3=df1->v4=mf->v4; df2->v2=mf->v3; @@ -5756,8 +6855,8 @@ static DerivedMesh * explodeModifier_splitEdges(ExplodeModifierData *emd, Derive facepa[i]=v1; } else{ - df1->v2=edgesplit[mf->v1*totvert+mf->v2]; - df1->v3=edgesplit[mf->v2*totvert+mf->v3]; + df1->v2=edgesplit_get(edgehash, mf->v1, mf->v2); + df1->v3=edgesplit_get(edgehash, mf->v2, mf->v3); df1->v4=mf->v3; df2->v2=mf->v3; df2->v3=mf->v4; @@ -5773,8 +6872,8 @@ static DerivedMesh * explodeModifier_splitEdges(ExplodeModifierData *emd, Derive } else{ if(v1!=v4){ - df1->v3=edgesplit[mf->v3*totvert+mf->v4]; - df1->v4=edgesplit[mf->v1*totvert+mf->v4]; + df1->v3=edgesplit_get(edgehash, mf->v3, mf->v4); + df1->v4=edgesplit_get(edgehash, mf->v1, mf->v4); df1->v2=mf->v3; mf->v1=df1->v4; @@ -5786,8 +6885,8 @@ static DerivedMesh * explodeModifier_splitEdges(ExplodeModifierData *emd, Derive facepa[i]=v4; } else{ - df1->v3=edgesplit[mf->v2*totvert+mf->v3]; - df1->v4=edgesplit[mf->v3*totvert+mf->v4]; + df1->v3=edgesplit_get(edgehash, mf->v2, mf->v3); + df1->v4=edgesplit_get(edgehash, mf->v3, mf->v4); df1->v1=mf->v4; df1->v2=mf->v2; df2->v3=mf->v4; @@ -5823,9 +6922,9 @@ static DerivedMesh * explodeModifier_splitEdges(ExplodeModifierData *emd, Derive curdupface++; if(v1==v2){ - df2->v1=df1->v1=edgesplit[mf->v1*totvert+mf->v4]; - df3->v1=df1->v2=edgesplit[mf->v2*totvert+mf->v3]; - df3->v3=df2->v2=df1->v3=edgesplit[mf->v3*totvert+mf->v4]; + df2->v1=df1->v1=edgesplit_get(edgehash, mf->v1, mf->v4); + df3->v1=df1->v2=edgesplit_get(edgehash, mf->v2, mf->v3); + df3->v3=df2->v2=df1->v3=edgesplit_get(edgehash, mf->v3, mf->v4); df3->v2=mf->v3; df2->v3=mf->v4; df1->v4=df2->v4=df3->v4=0; @@ -5838,9 +6937,9 @@ static DerivedMesh * explodeModifier_splitEdges(ExplodeModifierData *emd, Derive facepa[curdupface-2]=v4; } else if(v2==v3){ - df3->v1=df2->v3=df1->v1=edgesplit[mf->v1*totvert+mf->v4]; - df2->v2=df1->v2=edgesplit[mf->v1*totvert+mf->v2]; - df3->v2=df1->v3=edgesplit[mf->v3*totvert+mf->v4]; + df3->v1=df2->v3=df1->v1=edgesplit_get(edgehash, mf->v1, mf->v4); + df2->v2=df1->v2=edgesplit_get(edgehash, mf->v1, mf->v2); + df3->v2=df1->v3=edgesplit_get(edgehash, mf->v3, mf->v4); df3->v3=mf->v4; df2->v1=mf->v1; @@ -5854,9 +6953,9 @@ static DerivedMesh * explodeModifier_splitEdges(ExplodeModifierData *emd, Derive facepa[curdupface-2]=v1; } else if(v3==v4){ - df3->v2=df2->v1=df1->v1=edgesplit[mf->v1*totvert+mf->v2]; - df2->v3=df1->v2=edgesplit[mf->v2*totvert+mf->v3]; - df3->v3=df1->v3=edgesplit[mf->v1*totvert+mf->v4]; + df3->v2=df2->v1=df1->v1=edgesplit_get(edgehash, mf->v1, mf->v2); + df2->v3=df1->v2=edgesplit_get(edgehash, mf->v2, mf->v3); + df3->v3=df1->v3=edgesplit_get(edgehash, mf->v1, mf->v4); df3->v1=mf->v1; df2->v2=mf->v2; @@ -5870,9 +6969,9 @@ static DerivedMesh * explodeModifier_splitEdges(ExplodeModifierData *emd, Derive facepa[curdupface-2]=v2; } else{ - df3->v1=df1->v1=edgesplit[mf->v1*totvert+mf->v2]; - df3->v3=df2->v1=df1->v2=edgesplit[mf->v2*totvert+mf->v3]; - df2->v3=df1->v3=edgesplit[mf->v3*totvert+mf->v4]; + df3->v1=df1->v1=edgesplit_get(edgehash, mf->v1, mf->v2); + df3->v3=df2->v1=df1->v2=edgesplit_get(edgehash, mf->v2, mf->v3); + df2->v3=df1->v3=edgesplit_get(edgehash, mf->v3, mf->v4); df3->v2=mf->v2; df2->v2=mf->v3; @@ -5923,11 +7022,11 @@ static DerivedMesh * explodeModifier_splitEdges(ExplodeModifierData *emd, Derive *df3=*mf; curdupface++; - df1->v1=edgesplit[mf->v1*totvert+mf->v2]; - df3->v2=df1->v3=edgesplit[mf->v2*totvert+mf->v3]; + df1->v1=edgesplit_get(edgehash, mf->v1, mf->v2); + df3->v2=df1->v3=edgesplit_get(edgehash, mf->v2, mf->v3); - df2->v1=edgesplit[mf->v1*totvert+mf->v4]; - df3->v4=df2->v3=edgesplit[mf->v3*totvert+mf->v4]; + df2->v1=edgesplit_get(edgehash, mf->v1, mf->v4); + df3->v4=df2->v3=edgesplit_get(edgehash, mf->v3, mf->v4); df3->v1=df2->v2=df1->v4=curdupin; @@ -5964,11 +7063,11 @@ static DerivedMesh * explodeModifier_splitEdges(ExplodeModifierData *emd, Derive curdupface++; if(v2==v3){ - df1->v1=edgesplit[mf->v1*totvert+mf->v2]; - df3->v1=df1->v2=df1->v3=edgesplit[mf->v2*totvert+mf->v3]; - df2->v1=df1->v4=edgesplit[mf->v1*totvert+mf->v4]; + df1->v1=edgesplit_get(edgehash, mf->v1, mf->v2); + df3->v1=df1->v2=df1->v3=edgesplit_get(edgehash, mf->v2, mf->v3); + df2->v1=df1->v4=edgesplit_get(edgehash, mf->v1, mf->v4); - df3->v3=df2->v3=edgesplit[mf->v3*totvert+mf->v4]; + df3->v3=df2->v3=edgesplit_get(edgehash, mf->v3, mf->v4); df3->v2=mf->v3; df3->v4=0; @@ -5982,11 +7081,11 @@ static DerivedMesh * explodeModifier_splitEdges(ExplodeModifierData *emd, Derive facepa[curdupface-1]=v3; } else{ - df3->v1=df2->v1=df1->v2=edgesplit[mf->v1*totvert+mf->v2]; - df2->v4=df1->v3=edgesplit[mf->v3*totvert+mf->v4]; - df1->v4=edgesplit[mf->v1*totvert+mf->v4]; + df3->v1=df2->v1=df1->v2=edgesplit_get(edgehash, mf->v1, mf->v2); + df2->v4=df1->v3=edgesplit_get(edgehash, mf->v3, mf->v4); + df1->v4=edgesplit_get(edgehash, mf->v1, mf->v4); - df3->v3=df2->v2=edgesplit[mf->v2*totvert+mf->v3]; + df3->v3=df2->v2=edgesplit_get(edgehash, mf->v2, mf->v3); df3->v4=0; @@ -6010,7 +7109,7 @@ static DerivedMesh * explodeModifier_splitEdges(ExplodeModifierData *emd, Derive } } - MEM_freeN(edgesplit); + BLI_edgehash_free(edgehash, NULL); MEM_freeN(facesplit); MEM_freeN(vertpa); @@ -6018,19 +7117,20 @@ static DerivedMesh * explodeModifier_splitEdges(ExplodeModifierData *emd, Derive } static DerivedMesh * explodeModifier_explodeMesh(ExplodeModifierData *emd, - ParticleSystemModifierData *psmd, Object *ob, - DerivedMesh *to_explode) + ParticleSystemModifierData *psmd, Object *ob, + DerivedMesh *to_explode) { DerivedMesh *explode, *dm=to_explode; MFace *mf=0; - MVert *dupvert=0; ParticleSettings *part=psmd->psys->part; - ParticleData *pa, *pars=psmd->psys->particles; + ParticleData *pa=NULL, *pars=psmd->psys->particles; ParticleKey state; + EdgeHash *vertpahash; + EdgeHashIterator *ehi; float *vertco=0, imat[4][4]; float loc0[3], nor[3]; float timestep, cfra; - int *facepa=emd->facepa, *vertpa=0; + int *facepa=emd->facepa; int totdup=0,totvert=0,totface=0,totpart=0; int i, j, v, mindex=0; @@ -6045,81 +7145,87 @@ static DerivedMesh * explodeModifier_explodeMesh(ExplodeModifierData *emd, else cfra=bsystem_time(ob,(float)G.scene->r.cfra,0.0); - /* table for vertice <-> particle relations (row totpart+1 is for yet unexploded verts) */ - vertpa = MEM_callocN(sizeof(int)*(totpart+1)*totvert, "explode_vertpatab"); - for(i=0; i<(totpart+1)*totvert; i++) - vertpa[i] = -1; + /* hash table for vertice <-> particle relations */ + vertpahash= BLI_edgehash_new(); for (i=0; i<totface; i++) { + /* do mindex + totvert to ensure the vertex index to be the first + * with BLI_edgehashIterator_getKey */ if(facepa[i]==totpart || cfra <= (pars+facepa[i])->time) - mindex = totpart*totvert; + mindex = totvert+totpart; else - mindex = facepa[i]*totvert; + mindex = totvert+facepa[i]; mf=CDDM_get_face(dm,i); - /*set face vertices to exist in particle group*/ - vertpa[mindex+mf->v1] = 1; - vertpa[mindex+mf->v2] = 1; - vertpa[mindex+mf->v3] = 1; + /* set face vertices to exist in particle group */ + BLI_edgehash_insert(vertpahash, mf->v1, mindex, NULL); + BLI_edgehash_insert(vertpahash, mf->v2, mindex, NULL); + BLI_edgehash_insert(vertpahash, mf->v3, mindex, NULL); if(mf->v4) - vertpa[mindex+mf->v4] = 1; + BLI_edgehash_insert(vertpahash, mf->v4, mindex, NULL); } - /*make new vertice indexes & count total vertices after duplication*/ - for(i=0; i<(totpart+1)*totvert; i++){ - if(vertpa[i] != -1) - vertpa[i] = totdup++; + /* make new vertice indexes & count total vertices after duplication */ + ehi= BLI_edgehashIterator_new(vertpahash); + for(; !BLI_edgehashIterator_isDone(ehi); BLI_edgehashIterator_step(ehi)) { + BLI_edgehashIterator_setValue(ehi, SET_INT_IN_POINTER(totdup)); + totdup++; } + BLI_edgehashIterator_free(ehi); - /*the final duplicated vertices*/ + /* the final duplicated vertices */ explode= CDDM_from_template(dm, totdup, 0,totface); - dupvert= CDDM_get_verts(explode); + /*dupvert= CDDM_get_verts(explode);*/ /* getting back to object space */ Mat4Invert(imat,ob->obmat); psmd->psys->lattice = psys_get_lattice(ob, psmd->psys); - /*duplicate & displace vertices*/ - for(i=0, pa=pars; i<=totpart; i++, pa++){ - if(i!=totpart){ - psys_particle_on_emitter(ob, psmd,part->from,pa->num,-1,pa->fuv,pa->foffset,loc0,nor,0,0,0,0); - Mat4MulVecfl(ob->obmat,loc0); + /* duplicate & displace vertices */ + ehi= BLI_edgehashIterator_new(vertpahash); + for(; !BLI_edgehashIterator_isDone(ehi); BLI_edgehashIterator_step(ehi)) { + MVert source; + MVert *dest; - state.time=cfra; - psys_get_particle_state(ob,psmd->psys,i,&state,1); - } + /* get particle + vertex from hash */ + BLI_edgehashIterator_getKey(ehi, &j, &i); + i -= totvert; + v= GET_INT_FROM_POINTER(BLI_edgehashIterator_getValue(ehi)); - for(j=0; j<totvert; j++){ - v=vertpa[i*totvert+j]; - if(v != -1) { - MVert source; - MVert *dest; + dm->getVert(dm, j, &source); + dest = CDDM_get_vert(explode,v); - dm->getVert(dm, j, &source); - dest = CDDM_get_vert(explode,v); + DM_copy_vert_data(dm,explode,j,v,1); + *dest = source; - DM_copy_vert_data(dm,explode,j,v,1); - *dest = source; + if(i!=totpart) { + /* get particle */ + pa= pars+i; - if(i!=totpart){ - vertco=CDDM_get_vert(explode,v)->co; - - Mat4MulVecfl(ob->obmat,vertco); + /* get particle state */ + psys_particle_on_emitter(psmd,part->from,pa->num,-1,pa->fuv,pa->foffset,loc0,nor,0,0,0,0); + Mat4MulVecfl(ob->obmat,loc0); - VECSUB(vertco,vertco,loc0); + state.time=cfra; + psys_get_particle_state(ob,psmd->psys,i,&state,1); - /* apply rotation, size & location */ - QuatMulVecf(state.rot,vertco); - VecMulf(vertco,pa->size); - VECADD(vertco,vertco,state.co); + vertco=CDDM_get_vert(explode,v)->co; + + Mat4MulVecfl(ob->obmat,vertco); - Mat4MulVecfl(imat,vertco); - } - } + VECSUB(vertco,vertco,loc0); + + /* apply rotation, size & location */ + QuatMulVecf(state.rot,vertco); + VecMulf(vertco,pa->size); + VECADD(vertco,vertco,state.co); + + Mat4MulVecfl(imat,vertco); } } + BLI_edgehashIterator_free(ehi); /*map new vertices to faces*/ for (i=0; i<totface; i++) { @@ -6141,15 +7247,15 @@ static DerivedMesh * explodeModifier_explodeMesh(ExplodeModifierData *emd, orig_v4 = source.v4; if(facepa[i]!=totpart && cfra <= pa->time) - mindex = totpart*totvert; + mindex = totvert+totpart; else - mindex = facepa[i]*totvert; + mindex = totvert+facepa[i]; - source.v1 = vertpa[mindex+source.v1]; - source.v2 = vertpa[mindex+source.v2]; - source.v3 = vertpa[mindex+source.v3]; + source.v1 = edgesplit_get(vertpahash, source.v1, mindex); + source.v2 = edgesplit_get(vertpahash, source.v2, mindex); + source.v3 = edgesplit_get(vertpahash, source.v3, mindex); if(source.v4) - source.v4 = vertpa[mindex+source.v4]; + source.v4 = edgesplit_get(vertpahash, source.v4, mindex); DM_copy_face_data(dm,explode,i,i,1); @@ -6158,9 +7264,10 @@ static DerivedMesh * explodeModifier_explodeMesh(ExplodeModifierData *emd, test_index_face(mf, &explode->faceData, i, (mf->v4 ? 4 : 3)); } + MEM_printmemlist_stats(); /* cleanup */ - if(vertpa) MEM_freeN(vertpa); + BLI_edgehash_free(vertpahash, NULL); /* finalization */ CDDM_calc_edges(explode); @@ -6186,8 +7293,8 @@ static ParticleSystemModifierData * explodeModifier_findPrecedingParticlesystem( return psmd; } static DerivedMesh * explodeModifier_applyModifier( - ModifierData *md, Object *ob, DerivedMesh *derivedData, - int useRenderParams, int isFinalCalc) + ModifierData *md, Object *ob, DerivedMesh *derivedData, + int useRenderParams, int isFinalCalc) { DerivedMesh *dm = derivedData; ExplodeModifierData *emd= (ExplodeModifierData*) md; @@ -6198,12 +7305,13 @@ static DerivedMesh * explodeModifier_applyModifier( if(psys==0 || psys->totpart==0) return derivedData; if(psys->part==0 || psys->particles==0) return derivedData; + if(psmd->dm==0) return derivedData; /* 1. find faces to be exploded if needed */ if(emd->facepa==0 - || psmd->flag&eParticleSystemFlag_Pars - || emd->flag&eExplodeFlag_CalcFaces - || MEM_allocN_len(emd->facepa)/sizeof(int) != dm->getNumFaces(dm)){ + || psmd->flag&eParticleSystemFlag_Pars + || emd->flag&eExplodeFlag_CalcFaces + || MEM_allocN_len(emd->facepa)/sizeof(int) != dm->getNumFaces(dm)){ if(psmd->flag & eParticleSystemFlag_Pars) psmd->flag &= ~eParticleSystemFlag_Pars; @@ -6211,24 +7319,111 @@ static DerivedMesh * explodeModifier_applyModifier( emd->flag &= ~eExplodeFlag_CalcFaces; explodeModifier_createFacepa(emd,psmd,ob,derivedData); - } + } + + /* 2. create new mesh */ + if(emd->flag & eExplodeFlag_EdgeSplit){ + int *facepa = emd->facepa; + DerivedMesh *splitdm=explodeModifier_splitEdges(emd,dm); + DerivedMesh *explode=explodeModifier_explodeMesh(emd,psmd,ob,splitdm); + + MEM_freeN(emd->facepa); + emd->facepa=facepa; + splitdm->release(splitdm); + return explode; + } + else + return explodeModifier_explodeMesh(emd,psmd,ob,derivedData); + } + return derivedData; +} - /* 2. create new mesh */ - if(emd->flag & eExplodeFlag_EdgeSplit){ - int *facepa = emd->facepa; - DerivedMesh *splitdm=explodeModifier_splitEdges(emd,dm); - DerivedMesh *explode=explodeModifier_explodeMesh(emd,psmd,ob,splitdm); +/* Fluidsim */ +static void fluidsimModifier_initData(ModifierData *md) +{ + FluidsimModifierData *fluidmd= (FluidsimModifierData*) md; + + fluidsim_init(fluidmd); +} +static void fluidsimModifier_freeData(ModifierData *md) +{ + FluidsimModifierData *fluidmd= (FluidsimModifierData*) md; + + fluidsim_free(fluidmd); +} - MEM_freeN(emd->facepa); - emd->facepa=facepa; - splitdm->release(splitdm); - return explode; - } - else - return explodeModifier_explodeMesh(emd,psmd,ob,derivedData); +static void fluidsimModifier_copyData(ModifierData *md, ModifierData *target) +{ + FluidsimModifierData *fluidmd= (FluidsimModifierData*) md; + FluidsimModifierData *tfluidmd= (FluidsimModifierData*) target; + + if(tfluidmd->fss) + MEM_freeN(tfluidmd->fss); + + tfluidmd->fss = MEM_dupallocN(fluidmd->fss); +} + +static DerivedMesh * fluidsimModifier_applyModifier( + ModifierData *md, Object *ob, DerivedMesh *derivedData, + int useRenderParams, int isFinalCalc) +{ + FluidsimModifierData *fluidmd= (FluidsimModifierData*) md; + DerivedMesh *result = NULL; + + /* check for alloc failing */ + if(!fluidmd->fss) + { + fluidsimModifier_initData(md); + + if(!fluidmd->fss) + return derivedData; + } + + result = fluidsimModifier_do(fluidmd, ob, derivedData, useRenderParams, isFinalCalc); + + if(result) + { + return result; } + return derivedData; } + +static void fluidsimModifier_updateDepgraph( + ModifierData *md, DagForest *forest, + Object *ob, DagNode *obNode) +{ + FluidsimModifierData *fluidmd= (FluidsimModifierData*) md; + Base *base; + + if(fluidmd && fluidmd->fss) + { + if(fluidmd->fss->type == OB_FLUIDSIM_DOMAIN) + { + for(base = G.scene->base.first; base; base= base->next) + { + Object *ob1= base->object; + if(ob1 != ob) + { + FluidsimModifierData *fluidmdtmp = (FluidsimModifierData *)modifiers_findByType(ob1, eModifierType_Fluidsim); + + // only put dependancies from NON-DOMAIN fluids in here + if(fluidmdtmp && fluidmdtmp->fss && (fluidmdtmp->fss->type!=OB_FLUIDSIM_DOMAIN)) + { + DagNode *curNode = dag_get_node(forest, ob1); + dag_add_relation(forest, curNode, obNode, DAG_RL_DATA_DATA|DAG_RL_OB_DATA, "Fluidsim Object"); + } + } + } + } + } +} + +static int fluidsimModifier_dependsOnTime(ModifierData *md) +{ + return 1; +} + /* MeshDeform */ static void meshdeformModifier_initData(ModifierData *md) @@ -6277,9 +7472,9 @@ static int meshdeformModifier_isDisabled(ModifierData *md) } static void meshdeformModifier_foreachObjectLink( - ModifierData *md, Object *ob, - void (*walk)(void *userData, Object *ob, Object **obpoin), - void *userData) + ModifierData *md, Object *ob, + void (*walk)(void *userData, Object *ob, Object **obpoin), + void *userData) { MeshDeformModifierData *mmd = (MeshDeformModifierData*) md; @@ -6287,8 +7482,8 @@ static void meshdeformModifier_foreachObjectLink( } static void meshdeformModifier_updateDepgraph( - ModifierData *md, DagForest *forest, Object *ob, - DagNode *obNode) + ModifierData *md, DagForest *forest, Object *ob, + DagNode *obNode) { MeshDeformModifierData *mmd = (MeshDeformModifierData*) md; @@ -6296,7 +7491,8 @@ static void meshdeformModifier_updateDepgraph( DagNode *curNode = dag_get_node(forest, mmd->object); dag_add_relation(forest, curNode, obNode, - DAG_RL_DATA_DATA|DAG_RL_OB_DATA|DAG_RL_DATA_OB|DAG_RL_OB_OB); + DAG_RL_DATA_DATA|DAG_RL_OB_DATA|DAG_RL_DATA_OB|DAG_RL_OB_OB, + "Mesh Deform Modifier"); } } @@ -6353,11 +7549,11 @@ static float meshdeform_dynamic_bind(MeshDeformModifierData *mmd, float (*dco)[3 } static void meshdeformModifier_do( - ModifierData *md, Object *ob, DerivedMesh *dm, - float (*vertexCos)[3], int numVerts) + ModifierData *md, Object *ob, DerivedMesh *dm, + float (*vertexCos)[3], int numVerts) { MeshDeformModifierData *mmd = (MeshDeformModifierData*) md; - float imat[4][4], cagemat[4][4], icagemat[4][4], iobmat[3][3]; + float imat[4][4], cagemat[4][4], iobmat[4][4], icagemat[3][3], cmat[4][4]; float weight, totweight, fac, co[3], *weights, (*dco)[3], (*bindcos)[3]; int a, b, totvert, totcagevert, defgrp_index; DerivedMesh *tmpdm, *cagedm; @@ -6380,15 +7576,24 @@ static void meshdeformModifier_do( if(!cagedm) return; - /* compute matrices to go in and out of cage object space */ + /* compute matrices to go in and out of cage object space */ Mat4Invert(imat, mmd->object->obmat); Mat4MulMat4(cagemat, ob->obmat, imat); - Mat4Invert(icagemat, cagemat); - Mat3CpyMat4(iobmat, icagemat); + Mat4MulMat4(cmat, cagemat, mmd->bindmat); + Mat4Invert(iobmat, cmat); + Mat3CpyMat4(icagemat, iobmat); /* bind weights if needed */ - if(!mmd->bindcos) - //XXX harmonic_coordinates_bind(mmd, vertexCos, numVerts, cagemat); + if(!mmd->bindcos) { + static int recursive = 0; + + /* progress bar redraw can make this recursive .. */ + if(!recursive) { + recursive = 1; + //XXX harmonic_coordinates_bind(mmd, vertexCos, numVerts, cagemat); + recursive = 0; + } + } /* verify we have compatible weights */ totvert= numVerts; @@ -6482,11 +7687,11 @@ static void meshdeformModifier_do( if(totweight > 0.0f) { VecMulf(co, fac/totweight); - Mat3MulVecfl(iobmat, co); + Mat3MulVecfl(icagemat, co); if(G.rt != 527) VECADD(vertexCos[b], vertexCos[b], co) - else - VECCOPY(vertexCos[b], co) + else + VECCOPY(vertexCos[b], co) } } @@ -6496,8 +7701,8 @@ static void meshdeformModifier_do( } static void meshdeformModifier_deformVerts( - ModifierData *md, Object *ob, DerivedMesh *derivedData, - float (*vertexCos)[3], int numVerts) + ModifierData *md, Object *ob, DerivedMesh *derivedData, + float (*vertexCos)[3], int numVerts) { DerivedMesh *dm; @@ -6515,8 +7720,8 @@ static void meshdeformModifier_deformVerts( } static void meshdeformModifier_deformVertsEM( - ModifierData *md, Object *ob, EditMesh *editData, - DerivedMesh *derivedData, float (*vertexCos)[3], int numVerts) + ModifierData *md, Object *ob, EditMesh *editData, + DerivedMesh *derivedData, float (*vertexCos)[3], int numVerts) { DerivedMesh *dm; @@ -6531,6 +7736,235 @@ static void meshdeformModifier_deformVertsEM( dm->release(dm); } + +/* Shrinkwrap */ + +static void shrinkwrapModifier_initData(ModifierData *md) +{ + ShrinkwrapModifierData *smd = (ShrinkwrapModifierData*) md; + smd->shrinkType = MOD_SHRINKWRAP_NEAREST_SURFACE; + smd->shrinkOpts = MOD_SHRINKWRAP_PROJECT_ALLOW_POS_DIR; + smd->keepDist = 0.0f; + + smd->target = NULL; + smd->auxTarget = NULL; +} + +static void shrinkwrapModifier_copyData(ModifierData *md, ModifierData *target) +{ + ShrinkwrapModifierData *smd = (ShrinkwrapModifierData*)md; + ShrinkwrapModifierData *tsmd = (ShrinkwrapModifierData*)target; + + tsmd->target = smd->target; + tsmd->auxTarget = smd->auxTarget; + + strcpy(tsmd->vgroup_name, smd->vgroup_name); + + tsmd->keepDist = smd->keepDist; + tsmd->shrinkType= smd->shrinkType; + tsmd->shrinkOpts= smd->shrinkOpts; + tsmd->projAxis = smd->projAxis; + tsmd->subsurfLevels = smd->subsurfLevels; +} + +CustomDataMask shrinkwrapModifier_requiredDataMask(ModifierData *md) +{ + ShrinkwrapModifierData *smd = (ShrinkwrapModifierData *)md; + CustomDataMask dataMask = 0; + + /* ask for vertexgroups if we need them */ + if(smd->vgroup_name[0]) + dataMask |= (1 << CD_MDEFORMVERT); + + if(smd->shrinkType == MOD_SHRINKWRAP_PROJECT + && smd->projAxis == MOD_SHRINKWRAP_PROJECT_OVER_NORMAL) + dataMask |= (1 << CD_MVERT); + + return dataMask; +} + +static int shrinkwrapModifier_isDisabled(ModifierData *md) +{ + ShrinkwrapModifierData *smd = (ShrinkwrapModifierData*) md; + return !smd->target; +} + + +static void shrinkwrapModifier_foreachObjectLink(ModifierData *md, Object *ob, ObjectWalkFunc walk, void *userData) +{ + ShrinkwrapModifierData *smd = (ShrinkwrapModifierData*) md; + + walk(userData, ob, &smd->target); + walk(userData, ob, &smd->auxTarget); +} + +static void shrinkwrapModifier_deformVerts(ModifierData *md, Object *ob, DerivedMesh *derivedData, float (*vertexCos)[3], int numVerts) +{ + DerivedMesh *dm = NULL; + CustomDataMask dataMask = shrinkwrapModifier_requiredDataMask(md); + + /* We implement requiredDataMask but thats not really usefull since mesh_calc_modifiers pass a NULL derivedData or without the modified vertexs applied */ + if(dataMask) + { + if(derivedData) dm = CDDM_copy(derivedData); + else if(ob->type==OB_MESH) dm = CDDM_from_mesh(ob->data, ob); + else if(ob->type==OB_LATTICE) dm = NULL; + else return; + + if(dm != NULL && (dataMask & CD_MVERT)) + { + CDDM_apply_vert_coords(dm, vertexCos); + CDDM_calc_normals(dm); + } + } + + shrinkwrapModifier_deform((ShrinkwrapModifierData*)md, ob, dm, vertexCos, numVerts); + + if(dm) + dm->release(dm); +} + +static void shrinkwrapModifier_deformVertsEM(ModifierData *md, Object *ob, EditMesh *editData, DerivedMesh *derivedData, float (*vertexCos)[3], int numVerts) +{ + DerivedMesh *dm = NULL; + CustomDataMask dataMask = shrinkwrapModifier_requiredDataMask(md); + + if(dataMask) + { + if(derivedData) dm = CDDM_copy(derivedData); + else if(ob->type==OB_MESH) dm = CDDM_from_editmesh(editData, ob->data); + else if(ob->type==OB_LATTICE) dm = NULL; + else return; + + if(dm != NULL && (dataMask & CD_MVERT)) + { + CDDM_apply_vert_coords(dm, vertexCos); + CDDM_calc_normals(dm); + } + } + + shrinkwrapModifier_deform((ShrinkwrapModifierData*)md, ob, dm, vertexCos, numVerts); + + if(dm) + dm->release(dm); +} + +static void shrinkwrapModifier_updateDepgraph(ModifierData *md, DagForest *forest, Object *ob, DagNode *obNode) +{ + ShrinkwrapModifierData *smd = (ShrinkwrapModifierData*) md; + + if (smd->target) + dag_add_relation(forest, dag_get_node(forest, smd->target), obNode, DAG_RL_OB_DATA | DAG_RL_DATA_DATA, "Shrinkwrap Modifier"); + + if (smd->auxTarget) + dag_add_relation(forest, dag_get_node(forest, smd->auxTarget), obNode, DAG_RL_OB_DATA | DAG_RL_DATA_DATA, "Shrinkwrap Modifier"); +} + +/* SimpleDeform */ +static void simpledeformModifier_initData(ModifierData *md) +{ + SimpleDeformModifierData *smd = (SimpleDeformModifierData*) md; + + smd->mode = MOD_SIMPLEDEFORM_MODE_TWIST; + smd->axis = 0; + + smd->origin = NULL; + smd->factor = 0.35f; + smd->limit[0] = 0.0f; + smd->limit[1] = 1.0f; +} + +static void simpledeformModifier_copyData(ModifierData *md, ModifierData *target) +{ + SimpleDeformModifierData *smd = (SimpleDeformModifierData*)md; + SimpleDeformModifierData *tsmd = (SimpleDeformModifierData*)target; + + tsmd->mode = smd->mode; + tsmd->axis = smd->axis; + tsmd->origin= smd->origin; + tsmd->factor= smd->factor; + memcpy(tsmd->limit, smd->limit, sizeof(tsmd->limit)); +} + +static CustomDataMask simpledeformModifier_requiredDataMask(ModifierData *md) +{ + SimpleDeformModifierData *smd = (SimpleDeformModifierData *)md; + CustomDataMask dataMask = 0; + + /* ask for vertexgroups if we need them */ + if(smd->vgroup_name[0]) + dataMask |= (1 << CD_MDEFORMVERT); + + return dataMask; +} + +static void simpledeformModifier_foreachObjectLink(ModifierData *md, Object *ob, void (*walk)(void *userData, Object *ob, Object **obpoin), void *userData) +{ + SimpleDeformModifierData *smd = (SimpleDeformModifierData*)md; + walk(userData, ob, &smd->origin); +} + +static void simpledeformModifier_updateDepgraph(ModifierData *md, DagForest *forest, Object *ob, DagNode *obNode) +{ + SimpleDeformModifierData *smd = (SimpleDeformModifierData*)md; + + if (smd->origin) + dag_add_relation(forest, dag_get_node(forest, smd->origin), obNode, DAG_RL_OB_DATA, "SimpleDeform Modifier"); +} + +static void simpledeformModifier_deformVerts(ModifierData *md, Object *ob, DerivedMesh *derivedData, float (*vertexCos)[3], int numVerts) +{ + DerivedMesh *dm = NULL; + CustomDataMask dataMask = simpledeformModifier_requiredDataMask(md); + + /* We implement requiredDataMask but thats not really usefull since mesh_calc_modifiers pass a NULL derivedData or without the modified vertexs applied */ + if(dataMask) + { + if(derivedData) dm = CDDM_copy(derivedData); + else if(ob->type==OB_MESH) dm = CDDM_from_mesh(ob->data, ob); + else if(ob->type==OB_LATTICE) dm = NULL; + else return; + + if(dm != NULL && (dataMask & CD_MVERT)) + { + CDDM_apply_vert_coords(dm, vertexCos); + CDDM_calc_normals(dm); + } + } + + SimpleDeformModifier_do((SimpleDeformModifierData*)md, ob, dm, vertexCos, numVerts); + + if(dm) + dm->release(dm); + +} + +static void simpledeformModifier_deformVertsEM(ModifierData *md, Object *ob, EditMesh *editData, DerivedMesh *derivedData, float (*vertexCos)[3], int numVerts) +{ + DerivedMesh *dm = NULL; + CustomDataMask dataMask = simpledeformModifier_requiredDataMask(md); + + /* We implement requiredDataMask but thats not really usefull since mesh_calc_modifiers pass a NULL derivedData or without the modified vertexs applied */ + if(dataMask) + { + if(derivedData) dm = CDDM_copy(derivedData); + else if(ob->type==OB_MESH) dm = CDDM_from_editmesh(editData, ob->data); + else if(ob->type==OB_LATTICE) dm = NULL; + else return; + + if(dm != NULL && (dataMask & CD_MVERT)) + { + CDDM_apply_vert_coords(dm, vertexCos); + CDDM_calc_normals(dm); + } + } + + SimpleDeformModifier_do((SimpleDeformModifierData*)md, ob, dm, vertexCos, numVerts); + + if(dm) + dm->release(dm); +} + /***/ static ModifierTypeInfo typeArr[NUM_MODIFIER_TYPES]; @@ -6545,15 +7979,15 @@ ModifierTypeInfo *modifierType_getInfo(ModifierType type) /* Initialize and return the appropriate type info structure, * assumes that modifier has: - * name == typeName, - * structName == typeName + 'ModifierData' - */ + * name == typeName, + * structName == typeName + 'ModifierData' + */ #define INIT_TYPE(typeName) \ (strcpy(typeArr[eModifierType_##typeName].name, #typeName), \ strcpy(typeArr[eModifierType_##typeName].structName, \ - #typeName "ModifierData"), \ +#typeName "ModifierData"), \ typeArr[eModifierType_##typeName].structSize = \ - sizeof(typeName##ModifierData), \ + sizeof(typeName##ModifierData), \ &typeArr[eModifierType_##typeName]) mti = &typeArr[eModifierType_None]; @@ -6562,13 +7996,13 @@ ModifierTypeInfo *modifierType_getInfo(ModifierType type) mti->structSize = sizeof(ModifierData); mti->type = eModifierType_None; mti->flags = eModifierTypeFlag_AcceptsMesh - | eModifierTypeFlag_AcceptsCVs; + | eModifierTypeFlag_AcceptsCVs; mti->isDisabled = noneModifier_isDisabled; mti = INIT_TYPE(Curve); mti->type = eModifierTypeType_OnlyDeform; mti->flags = eModifierTypeFlag_AcceptsCVs - | eModifierTypeFlag_SupportsEditmode; + | eModifierTypeFlag_SupportsEditmode; mti->initData = curveModifier_initData; mti->copyData = curveModifier_copyData; mti->requiredDataMask = curveModifier_requiredDataMask; @@ -6581,7 +8015,7 @@ ModifierTypeInfo *modifierType_getInfo(ModifierType type) mti = INIT_TYPE(Lattice); mti->type = eModifierTypeType_OnlyDeform; mti->flags = eModifierTypeFlag_AcceptsCVs - | eModifierTypeFlag_SupportsEditmode; + | eModifierTypeFlag_SupportsEditmode; mti->copyData = latticeModifier_copyData; mti->requiredDataMask = latticeModifier_requiredDataMask; mti->isDisabled = latticeModifier_isDisabled; @@ -6593,9 +8027,9 @@ ModifierTypeInfo *modifierType_getInfo(ModifierType type) mti = INIT_TYPE(Subsurf); mti->type = eModifierTypeType_Constructive; mti->flags = eModifierTypeFlag_AcceptsMesh - | eModifierTypeFlag_SupportsMapping - | eModifierTypeFlag_SupportsEditmode - | eModifierTypeFlag_EnableInEditmode; + | eModifierTypeFlag_SupportsMapping + | eModifierTypeFlag_SupportsEditmode + | eModifierTypeFlag_EnableInEditmode; mti->initData = subsurfModifier_initData; mti->copyData = subsurfModifier_copyData; mti->freeData = subsurfModifier_freeData; @@ -6609,13 +8043,22 @@ ModifierTypeInfo *modifierType_getInfo(ModifierType type) mti->copyData = buildModifier_copyData; mti->dependsOnTime = buildModifier_dependsOnTime; mti->applyModifier = buildModifier_applyModifier; + + mti = INIT_TYPE(Mask); + mti->type = eModifierTypeType_Nonconstructive; + mti->flags = eModifierTypeFlag_AcceptsMesh; + mti->copyData = maskModifier_copyData; + mti->requiredDataMask= maskModifier_requiredDataMask; + mti->foreachObjectLink = maskModifier_foreachObjectLink; + mti->updateDepgraph = maskModifier_updateDepgraph; + mti->applyModifier = maskModifier_applyModifier; mti = INIT_TYPE(Array); mti->type = eModifierTypeType_Constructive; mti->flags = eModifierTypeFlag_AcceptsMesh - | eModifierTypeFlag_SupportsMapping - | eModifierTypeFlag_SupportsEditmode - | eModifierTypeFlag_EnableInEditmode; + | eModifierTypeFlag_SupportsMapping + | eModifierTypeFlag_SupportsEditmode + | eModifierTypeFlag_EnableInEditmode; mti->initData = arrayModifier_initData; mti->copyData = arrayModifier_copyData; mti->foreachObjectLink = arrayModifier_foreachObjectLink; @@ -6626,9 +8069,9 @@ ModifierTypeInfo *modifierType_getInfo(ModifierType type) mti = INIT_TYPE(Mirror); mti->type = eModifierTypeType_Constructive; mti->flags = eModifierTypeFlag_AcceptsMesh - | eModifierTypeFlag_SupportsMapping - | eModifierTypeFlag_SupportsEditmode - | eModifierTypeFlag_EnableInEditmode; + | eModifierTypeFlag_SupportsMapping + | eModifierTypeFlag_SupportsEditmode + | eModifierTypeFlag_EnableInEditmode; mti->initData = mirrorModifier_initData; mti->copyData = mirrorModifier_copyData; mti->foreachObjectLink = mirrorModifier_foreachObjectLink; @@ -6639,20 +8082,32 @@ ModifierTypeInfo *modifierType_getInfo(ModifierType type) mti = INIT_TYPE(EdgeSplit); mti->type = eModifierTypeType_Constructive; mti->flags = eModifierTypeFlag_AcceptsMesh - | eModifierTypeFlag_SupportsMapping - | eModifierTypeFlag_SupportsEditmode - | eModifierTypeFlag_EnableInEditmode; + | eModifierTypeFlag_SupportsMapping + | eModifierTypeFlag_SupportsEditmode + | eModifierTypeFlag_EnableInEditmode; mti->initData = edgesplitModifier_initData; mti->copyData = edgesplitModifier_copyData; mti->applyModifier = edgesplitModifier_applyModifier; mti->applyModifierEM = edgesplitModifier_applyModifierEM; + mti = INIT_TYPE(Bevel); + mti->type = eModifierTypeType_Constructive; + mti->flags = eModifierTypeFlag_AcceptsMesh + | eModifierTypeFlag_SupportsEditmode + | eModifierTypeFlag_EnableInEditmode; + mti->initData = bevelModifier_initData; + mti->copyData = bevelModifier_copyData; + mti->requiredDataMask = bevelModifier_requiredDataMask; + mti->applyModifier = bevelModifier_applyModifier; + mti->applyModifierEM = bevelModifier_applyModifierEM; + mti = INIT_TYPE(Displace); mti->type = eModifierTypeType_OnlyDeform; mti->flags = eModifierTypeFlag_AcceptsMesh|eModifierTypeFlag_SupportsEditmode; mti->initData = displaceModifier_initData; mti->copyData = displaceModifier_copyData; mti->requiredDataMask = displaceModifier_requiredDataMask; + mti->dependsOnTime = displaceModifier_dependsOnTime; mti->foreachObjectLink = displaceModifier_foreachObjectLink; mti->foreachIDLink = displaceModifier_foreachIDLink; mti->updateDepgraph = displaceModifier_updateDepgraph; @@ -6663,9 +8118,9 @@ ModifierTypeInfo *modifierType_getInfo(ModifierType type) mti = INIT_TYPE(UVProject); mti->type = eModifierTypeType_Nonconstructive; mti->flags = eModifierTypeFlag_AcceptsMesh - | eModifierTypeFlag_SupportsMapping - | eModifierTypeFlag_SupportsEditmode - | eModifierTypeFlag_EnableInEditmode; + | eModifierTypeFlag_SupportsMapping + | eModifierTypeFlag_SupportsEditmode + | eModifierTypeFlag_EnableInEditmode; mti->initData = uvprojectModifier_initData; mti->copyData = uvprojectModifier_copyData; mti->requiredDataMask = uvprojectModifier_requiredDataMask; @@ -6685,7 +8140,7 @@ ModifierTypeInfo *modifierType_getInfo(ModifierType type) mti = INIT_TYPE(Smooth); mti->type = eModifierTypeType_OnlyDeform; mti->flags = eModifierTypeFlag_AcceptsMesh - | eModifierTypeFlag_SupportsEditmode; + | eModifierTypeFlag_SupportsEditmode; mti->initData = smoothModifier_initData; mti->copyData = smoothModifier_copyData; mti->requiredDataMask = smoothModifier_requiredDataMask; @@ -6695,7 +8150,7 @@ ModifierTypeInfo *modifierType_getInfo(ModifierType type) mti = INIT_TYPE(Cast); mti->type = eModifierTypeType_OnlyDeform; mti->flags = eModifierTypeFlag_AcceptsCVs - | eModifierTypeFlag_SupportsEditmode; + | eModifierTypeFlag_SupportsEditmode; mti->initData = castModifier_initData; mti->copyData = castModifier_copyData; mti->requiredDataMask = castModifier_requiredDataMask; @@ -6707,7 +8162,7 @@ ModifierTypeInfo *modifierType_getInfo(ModifierType type) mti = INIT_TYPE(Wave); mti->type = eModifierTypeType_OnlyDeform; mti->flags = eModifierTypeFlag_AcceptsCVs - | eModifierTypeFlag_SupportsEditmode; + | eModifierTypeFlag_SupportsEditmode; mti->initData = waveModifier_initData; mti->copyData = waveModifier_copyData; mti->dependsOnTime = waveModifier_dependsOnTime; @@ -6721,7 +8176,7 @@ ModifierTypeInfo *modifierType_getInfo(ModifierType type) mti = INIT_TYPE(Armature); mti->type = eModifierTypeType_OnlyDeform; mti->flags = eModifierTypeFlag_AcceptsCVs - | eModifierTypeFlag_SupportsEditmode; + | eModifierTypeFlag_SupportsEditmode; mti->initData = armatureModifier_initData; mti->copyData = armatureModifier_copyData; mti->requiredDataMask = armatureModifier_requiredDataMask; @@ -6735,7 +8190,7 @@ ModifierTypeInfo *modifierType_getInfo(ModifierType type) mti = INIT_TYPE(Hook); mti->type = eModifierTypeType_OnlyDeform; mti->flags = eModifierTypeFlag_AcceptsCVs - | eModifierTypeFlag_SupportsEditmode; + | eModifierTypeFlag_SupportsEditmode; mti->initData = hookModifier_initData; mti->copyData = hookModifier_copyData; mti->requiredDataMask = hookModifier_requiredDataMask; @@ -6749,14 +8204,36 @@ ModifierTypeInfo *modifierType_getInfo(ModifierType type) mti = INIT_TYPE(Softbody); mti->type = eModifierTypeType_OnlyDeform; mti->flags = eModifierTypeFlag_AcceptsCVs - | eModifierTypeFlag_RequiresOriginalData; + | eModifierTypeFlag_RequiresOriginalData; mti->deformVerts = softbodyModifier_deformVerts; + mti->dependsOnTime = softbodyModifier_dependsOnTime; + + mti = INIT_TYPE(Cloth); + mti->type = eModifierTypeType_Nonconstructive; + mti->initData = clothModifier_initData; + mti->flags = eModifierTypeFlag_AcceptsMesh + | eModifierTypeFlag_UsesPointCache; + mti->dependsOnTime = clothModifier_dependsOnTime; + mti->freeData = clothModifier_freeData; + mti->requiredDataMask = clothModifier_requiredDataMask; + mti->copyData = clothModifier_copyData; + mti->applyModifier = clothModifier_applyModifier; + mti->updateDepgraph = clothModifier_updateDepgraph; + + mti = INIT_TYPE(Collision); + mti->type = eModifierTypeType_OnlyDeform; + mti->initData = collisionModifier_initData; + mti->flags = eModifierTypeFlag_AcceptsMesh; + mti->dependsOnTime = collisionModifier_dependsOnTime; + mti->freeData = collisionModifier_freeData; + mti->deformVerts = collisionModifier_deformVerts; + // mti->copyData = collisionModifier_copyData; mti = INIT_TYPE(Boolean); mti->type = eModifierTypeType_Nonconstructive; mti->flags = eModifierTypeFlag_AcceptsMesh - | eModifierTypeFlag_RequiresOriginalData - | eModifierTypeFlag_UsesPointCache; + | eModifierTypeFlag_RequiresOriginalData + | eModifierTypeFlag_UsesPointCache; mti->copyData = booleanModifier_copyData; mti->isDisabled = booleanModifier_isDisabled; mti->applyModifier = booleanModifier_applyModifier; @@ -6766,7 +8243,7 @@ ModifierTypeInfo *modifierType_getInfo(ModifierType type) mti = INIT_TYPE(MeshDeform); mti->type = eModifierTypeType_OnlyDeform; mti->flags = eModifierTypeFlag_AcceptsCVs - | eModifierTypeFlag_SupportsEditmode; + | eModifierTypeFlag_SupportsEditmode; mti->initData = meshdeformModifier_initData; mti->freeData = meshdeformModifier_freeData; mti->copyData = meshdeformModifier_copyData; @@ -6780,11 +8257,11 @@ ModifierTypeInfo *modifierType_getInfo(ModifierType type) mti = INIT_TYPE(ParticleSystem); mti->type = eModifierTypeType_OnlyDeform; mti->flags = eModifierTypeFlag_AcceptsMesh - | eModifierTypeFlag_SupportsMapping - | eModifierTypeFlag_UsesPointCache; + | eModifierTypeFlag_SupportsMapping + | eModifierTypeFlag_UsesPointCache; #if 0 - | eModifierTypeFlag_SupportsEditmode; - |eModifierTypeFlag_EnableInEditmode; + | eModifierTypeFlag_SupportsEditmode; + |eModifierTypeFlag_EnableInEditmode; #endif mti->initData = particleSystemModifier_initData; mti->freeData = particleSystemModifier_freeData; @@ -6798,9 +8275,9 @@ ModifierTypeInfo *modifierType_getInfo(ModifierType type) mti = INIT_TYPE(ParticleInstance); mti->type = eModifierTypeType_Constructive; mti->flags = eModifierTypeFlag_AcceptsMesh - | eModifierTypeFlag_SupportsMapping - | eModifierTypeFlag_SupportsEditmode - | eModifierTypeFlag_EnableInEditmode; + | eModifierTypeFlag_SupportsMapping + | eModifierTypeFlag_SupportsEditmode + | eModifierTypeFlag_EnableInEditmode; mti->initData = particleInstanceModifier_initData; mti->copyData = particleInstanceModifier_copyData; mti->dependsOnTime = particleInstanceModifier_dependsOnTime; @@ -6818,6 +8295,46 @@ ModifierTypeInfo *modifierType_getInfo(ModifierType type) mti->dependsOnTime = explodeModifier_dependsOnTime; mti->requiredDataMask = explodeModifier_requiredDataMask; mti->applyModifier = explodeModifier_applyModifier; + + mti = INIT_TYPE(Fluidsim); + mti->type = eModifierTypeType_Nonconstructive + | eModifierTypeFlag_RequiresOriginalData; + mti->flags = eModifierTypeFlag_AcceptsMesh; + mti->initData = fluidsimModifier_initData; + mti->freeData = fluidsimModifier_freeData; + mti->copyData = fluidsimModifier_copyData; + mti->dependsOnTime = fluidsimModifier_dependsOnTime; + mti->applyModifier = fluidsimModifier_applyModifier; + mti->updateDepgraph = fluidsimModifier_updateDepgraph; + + mti = INIT_TYPE(Shrinkwrap); + mti->type = eModifierTypeType_OnlyDeform; + mti->flags = eModifierTypeFlag_AcceptsMesh + | eModifierTypeFlag_AcceptsCVs + | eModifierTypeFlag_SupportsEditmode + | eModifierTypeFlag_EnableInEditmode; + mti->initData = shrinkwrapModifier_initData; + mti->copyData = shrinkwrapModifier_copyData; + mti->requiredDataMask = shrinkwrapModifier_requiredDataMask; + mti->isDisabled = shrinkwrapModifier_isDisabled; + mti->foreachObjectLink = shrinkwrapModifier_foreachObjectLink; + mti->deformVerts = shrinkwrapModifier_deformVerts; + mti->deformVertsEM = shrinkwrapModifier_deformVertsEM; + mti->updateDepgraph = shrinkwrapModifier_updateDepgraph; + + mti = INIT_TYPE(SimpleDeform); + mti->type = eModifierTypeType_OnlyDeform; + mti->flags = eModifierTypeFlag_AcceptsMesh + | eModifierTypeFlag_AcceptsCVs + | eModifierTypeFlag_SupportsEditmode + | eModifierTypeFlag_EnableInEditmode; + mti->initData = simpledeformModifier_initData; + mti->copyData = simpledeformModifier_copyData; + mti->requiredDataMask = simpledeformModifier_requiredDataMask; + mti->deformVerts = simpledeformModifier_deformVerts; + mti->deformVertsEM = simpledeformModifier_deformVertsEM; + mti->foreachObjectLink = simpledeformModifier_foreachObjectLink; + mti->updateDepgraph = simpledeformModifier_updateDepgraph; typeArrInit = 0; #undef INIT_TYPE @@ -6841,7 +8358,7 @@ ModifierData *modifier_new(int type) md->type = type; md->mode = eModifierMode_Realtime - | eModifierMode_Render | eModifierMode_Expanded; + | eModifierMode_Render | eModifierMode_Expanded; if (mti->flags & eModifierTypeFlag_EnableInEditmode) md->mode |= eModifierMode_Editmode; @@ -6873,7 +8390,7 @@ int modifier_supportsMapping(ModifierData *md) ModifierTypeInfo *mti = modifierType_getInfo(md->type); return (mti->type==eModifierTypeType_OnlyDeform || - (mti->flags & eModifierTypeFlag_SupportsMapping)); + (mti->flags & eModifierTypeFlag_SupportsMapping)); } ModifierData *modifiers_findByType(Object *ob, ModifierType type) @@ -6905,7 +8422,7 @@ void modifiers_clearErrors(Object *ob) } void modifiers_foreachObjectLink(Object *ob, ObjectWalkFunc walk, - void *userData) + void *userData) { ModifierData *md = ob->modifiers.first; @@ -6948,9 +8465,9 @@ int modifier_couldBeCage(ModifierData *md) ModifierTypeInfo *mti = modifierType_getInfo(md->type); return ( (md->mode & eModifierMode_Realtime) && - (md->mode & eModifierMode_Editmode) && - (!mti->isDisabled || !mti->isDisabled(md)) && - modifier_supportsMapping(md)); + (md->mode & eModifierMode_Editmode) && + (!mti->isDisabled || !mti->isDisabled(md)) && + modifier_supportsMapping(md)); } void modifier_setError(ModifierData *md, char *format, ...) @@ -6981,7 +8498,7 @@ int modifiers_getCageIndex(Object *ob, int *lastPossibleCageIndex_r) ModifierData *md = ob->modifiers.first; int i, cageIndex = -1; - /* Find the last modifier acting on the cage. */ + /* Find the last modifier acting on the cage. */ for (i=0; md; i++,md=md->next) { ModifierTypeInfo *mti = modifierType_getInfo(md->type); @@ -7010,6 +8527,13 @@ int modifiers_isSoftbodyEnabled(Object *ob) return (md && md->mode & (eModifierMode_Realtime | eModifierMode_Render)); } +int modifiers_isClothEnabled(Object *ob) +{ + ModifierData *md = modifiers_findByType(ob, eModifierType_Cloth); + + return (md && md->mode & (eModifierMode_Realtime | eModifierMode_Render)); +} + int modifiers_isParticleEnabled(Object *ob) { ModifierData *md = modifiers_findByType(ob, eModifierType_ParticleSystem); @@ -7033,11 +8557,11 @@ LinkNode *modifiers_calcDataMasks(ModifierData *md, CustomDataMask dataMask) } /* build the list of required data masks - each mask in the list must - * include all elements of the masks that follow it - * - * note the list is currently in reverse order, so "masks that follow it" - * actually means "masks that precede it" at the moment - */ + * include all elements of the masks that follow it + * + * note the list is currently in reverse order, so "masks that follow it" + * actually means "masks that precede it" at the moment + */ for(curr = dataMasks, prev = NULL; curr; prev = curr, curr = curr->next) { if(prev) { CustomDataMask prev_mask = (CustomDataMask)prev->link; @@ -7060,8 +8584,8 @@ LinkNode *modifiers_calcDataMasks(ModifierData *md, CustomDataMask dataMask) ModifierData *modifiers_getVirtualModifierList(Object *ob) { /* Kinda hacky, but should be fine since we are never - * reentrant and avoid free hassles. - */ + * reentrant and avoid free hassles. + */ static ArmatureModifierData amd; static CurveModifierData cmd; static LatticeModifierData lmd; @@ -7119,7 +8643,7 @@ Object *modifiers_isDeformedByArmature(Object *ob) ArmatureModifierData *amd= NULL; /* return the first selected armature, this lets us use multiple armatures - */ + */ for (; md; md=md->next) { if (md->type==eModifierType_Armature) { amd = (ArmatureModifierData*) md; @@ -7144,7 +8668,7 @@ Object *modifiers_isDeformedByLattice(Object *ob) LatticeModifierData *lmd= NULL; /* return the first selected armature, this lets us use multiple armatures - */ + */ for (; md; md=md->next) { if (md->type==eModifierType_Lattice) { lmd = (LatticeModifierData*) md; @@ -7234,3 +8758,4 @@ void modifier_freeTemporaryData(ModifierData *md) } + diff --git a/source/blender/blenkernel/intern/multires-firstlevel.c b/source/blender/blenkernel/intern/multires-firstlevel.c index b27d1c4b156..8c667a9ecf0 100644 --- a/source/blender/blenkernel/intern/multires-firstlevel.c +++ b/source/blender/blenkernel/intern/multires-firstlevel.c @@ -1,5 +1,5 @@ /* - * $Id: multires-firstlevel.c 13001 2007-12-26 09:39:15Z nicholasbishop $ + * $Id$ * * ***** BEGIN GPL LICENSE BLOCK ***** * @@ -73,7 +73,7 @@ char type_ok(const int type) } /* Copy vdata or fdata from Mesh or EditMesh to Multires. */ -void multires_update_customdata(MultiresLevel *lvl1, CustomData *src, CustomData *dst, const int type) +void multires_update_customdata(MultiresLevel *lvl1, EditMesh *em, CustomData *src, CustomData *dst, const int type) { if(src && dst && type_ok(type)) { const int tot= (type == CD_MDEFORMVERT ? lvl1->totvert : lvl1->totface); @@ -82,7 +82,7 @@ void multires_update_customdata(MultiresLevel *lvl1, CustomData *src, CustomData CustomData_free(dst, tot); if(CustomData_has_layer(src, type)) { - if(G.obedit) { + if(em) { EditVert *eve= G.editMesh->verts.first; EditFace *efa= G.editMesh->faces.first; CustomData_copy(src, dst, cdmask(type), CD_CALLOC, tot); @@ -225,9 +225,9 @@ void multires_del_lower_customdata(Multires *mr, MultiresLevel *cr_lvl) void multires_update_first_level(Mesh *me, EditMesh *em) { if(me && me->mr && me->mr->current == 1) { - multires_update_customdata(me->mr->levels.first, em ? &em->vdata : &me->vdata, + multires_update_customdata(me->mr->levels.first, em, em ? &em->vdata : &me->vdata, &me->mr->vdata, CD_MDEFORMVERT); - multires_update_customdata(me->mr->levels.first, em ? &em->fdata : &me->fdata, + multires_update_customdata(me->mr->levels.first, em, em ? &em->fdata : &me->fdata, &me->mr->fdata, CD_MTFACE); multires_update_edge_flags(me, em); } diff --git a/source/blender/blenkernel/intern/multires.c b/source/blender/blenkernel/intern/multires.c index a3c1d9ef19d..96e81c604fa 100644 --- a/source/blender/blenkernel/intern/multires.c +++ b/source/blender/blenkernel/intern/multires.c @@ -205,7 +205,7 @@ static void multires_get_edge(MultiresEdge *e, EditEdge *eed, MEdge *m, short *f } } -static void multires_get_face(MultiresFace *f, EditFace *efa, MFace *m) +static void multires_get_face(MultiresFace *f, CustomData *fdata, int findex, EditFace *efa, MFace *m) { if(efa) { MFace tmp; @@ -215,7 +215,7 @@ static void multires_get_face(MultiresFace *f, EditFace *efa, MFace *m) tmp.v3= efa->v3->tmp.l; tmp.v4= 0; if(efa->v4) tmp.v4= efa->v4->tmp.l; - //XXX test_index_face(&tmp, NULL, 0, efa->v4?4:3); + //XXX test_index_face(&tmp, fdata, findex, efa->v4?4:3); for(j=0; j<4; ++j) f->v[j]= (&tmp.v1)[j]; /* Flags */ @@ -370,7 +370,7 @@ void multires_create(Object *ob, Mesh *me) /* Load vertices and vdata (MDeformVerts) */ lvl->totvert= em ? BLI_countlist(&em->verts) : me->totvert; me->mr->verts= MEM_callocN(sizeof(MVert)*lvl->totvert,"multires verts"); - multires_update_customdata(me->mr->levels.first, em ? &em->vdata : &me->vdata, + multires_update_customdata(me->mr->levels.first, em, em ? &em->vdata : &me->vdata, &me->mr->vdata, CD_MDEFORMVERT); if(em) eve= em->verts.first; for(i=0; i<lvl->totvert; ++i) { @@ -381,11 +381,11 @@ void multires_create(Object *ob, Mesh *me) /* Load faces and fdata (MTFaces) */ lvl->totface= em ? BLI_countlist(&em->faces) : me->totface; lvl->faces= MEM_callocN(sizeof(MultiresFace)*lvl->totface,"multires faces"); - multires_update_customdata(me->mr->levels.first, em ? &em->fdata : &me->fdata, + multires_update_customdata(me->mr->levels.first, em, em ? &em->fdata : &me->fdata, &me->mr->fdata, CD_MTFACE); if(em) efa= em->faces.first; for(i=0; i<lvl->totface; ++i) { - multires_get_face(&lvl->faces[i], efa, &me->mface[i]); + multires_get_face(&lvl->faces[i], &me->mr->fdata, i, efa, &me->mface[i]); if(em) efa= efa->next; } @@ -639,6 +639,10 @@ static void multires_update_vertices(Mesh *me, EditMesh *em) MultiApplyData data; int i, j; + /* XXX added this to prevent crash, but if it works? (ton) */ + if(me->mr->verts==NULL) + return; + /* Prepare deltas */ pr_deltas= MEM_callocN(sizeof(vec3f)*last_lvl->totvert, "multires deltas 1"); cr_deltas= MEM_callocN(sizeof(vec3f)*last_lvl->totvert, "multires deltas 2"); @@ -762,7 +766,7 @@ static void multires_update_faces(Mesh *me, EditMesh *em) if(em) efa= em->faces.first; for(i=0; i<cr_lvl->totface; ++i) { MultiresFace mftmp; - multires_get_face(&mftmp, efa, &me->mface[i]); + multires_get_face(&mftmp, &me->mr->fdata, i, efa, &me->mface[i]); if(cr_lvl->faces[i].flag != mftmp.flag) cr_flag_damaged[i]= 1; if(cr_lvl->faces[i].mat_nr != mftmp.mat_nr) @@ -1284,7 +1288,7 @@ void multires_edge_level_update(Object *ob, Mesh *me) if(!G.obedit) { MultiresLevel *cr_lvl= BLI_findlink(&me->mr->levels,me->mr->current-1); MultiresLevel *edge_lvl= BLI_findlink(&me->mr->levels,me->mr->edgelvl-1); - const int threshold= edge_lvl->totedge * powf(2, me->mr->current - me->mr->edgelvl); + const int threshold= edge_lvl->totedge * pow(2, me->mr->current - me->mr->edgelvl); unsigned i; for(i=0; i<cr_lvl->totedge; ++i) { diff --git a/source/blender/blenkernel/intern/node.c b/source/blender/blenkernel/intern/node.c index 5c9b609bbaf..8d60b1b4359 100644 --- a/source/blender/blenkernel/intern/node.c +++ b/source/blender/blenkernel/intern/node.c @@ -27,6 +27,10 @@ * ***** END GPL LICENSE BLOCK ***** */ +#ifndef DISABLE_PYTHON +#include <Python.h> +#endif + #include <stdlib.h> #include <string.h> @@ -34,6 +38,7 @@ #include "DNA_image_types.h" #include "DNA_node_types.h" #include "DNA_material_types.h" +#include "DNA_text_types.h" #include "DNA_scene_types.h" #include "BKE_blender.h" @@ -44,6 +49,7 @@ #include "BKE_main.h" #include "BKE_node.h" #include "BKE_texture.h" +#include "BKE_text.h" #include "BKE_utildefines.h" #include "BLI_arithb.h" @@ -61,10 +67,12 @@ #include "RE_render_ext.h" /* <- ibuf_sample() */ #include "CMP_node.h" +#include "intern/CMP_util.h" /* stupid include path... */ + #include "SHD_node.h" -/* not very important, but the stack solver likes to know a maximum */ -#define MAX_SOCKET 64 +#include "GPU_extensions.h" +#include "GPU_material.h" static ListBase empty_list = {NULL, NULL}; ListBase node_all_composit = {NULL, NULL}; @@ -72,7 +80,7 @@ ListBase node_all_shaders = {NULL, NULL}; /* ************** Type stuff ********** */ -static bNodeType *node_get_type(bNodeTree *ntree, int type, bNodeTree *ngroup) +static bNodeType *node_get_type(bNodeTree *ntree, int type, bNodeTree *ngroup, ID *id) { if(type==NODE_GROUP) { if(ngroup && GS(ngroup->id.name)==ID_NT) { @@ -83,7 +91,7 @@ static bNodeType *node_get_type(bNodeTree *ntree, int type, bNodeTree *ngroup) else { bNodeType *ntype = ntree->alltypes.first; for(; ntype; ntype= ntype->next) - if(ntype->type==type) + if(ntype->type==type && id==ntype->id ) return ntype; return NULL; @@ -105,7 +113,27 @@ void ntreeInitTypes(bNodeTree *ntree) for(node= ntree->nodes.first; node; node= next) { next= node->next; - node->typeinfo= node_get_type(ntree, node->type, (bNodeTree *)node->id); + if(node->type==NODE_DYNAMIC) { + bNodeType *stype= NULL; + if(node->id==NULL) { /* empty script node */ + stype= node_get_type(ntree, node->type, NULL, NULL); + } else { /* not an empty script node */ + stype= node_get_type(ntree, node->type, NULL, node->id); + if(!stype) { + stype= node_get_type(ntree, node->type, NULL, NULL); + /* needed info if the pynode script fails now: */ + if (node->id) node->storage= ntree; + } else { + node->custom1= 0; + node->custom1= BSET(node->custom1,NODE_DYNAMIC_ADDEXIST); + } + } + node->typeinfo= stype; + node->typeinfo->initfunc(node); + } else { + node->typeinfo= node_get_type(ntree, node->type, (bNodeTree *)node->id, NULL); + } + if(node->typeinfo==NULL) { printf("Error: Node type %s doesn't exist anymore, removed\n", node->name); nodeFreeNode(ntree, node); @@ -115,6 +143,18 @@ void ntreeInitTypes(bNodeTree *ntree) ntree->init |= NTREE_TYPE_INIT; } +/* updates node with (modified) bNodeType.. this should be done for all trees */ +void ntreeUpdateType(bNodeTree *ntree, bNodeType *ntype) +{ + bNode *node; + + for(node= ntree->nodes.first; node; node= node->next) { + if(node->typeinfo== ntype) { + nodeUpdateType(ntree, node, ntype); + } + } +} + /* only used internal... we depend on type definitions! */ static bNodeSocket *node_add_socket_type(ListBase *lb, bNodeSocketType *stype) { @@ -137,7 +177,7 @@ static bNodeSocket *node_add_socket_type(ListBase *lb, bNodeSocketType *stype) if(lb) BLI_addtail(lb, sock); - + return sock; } @@ -483,6 +523,28 @@ bNode *nodeMakeGroupFromSelected(bNodeTree *ntree) BLI_addtail(&ngroup->nodes, node); node->locx-= 0.5f*(min[0]+max[0]); node->locy-= 0.5f*(min[1]+max[1]); + + /* set selin and selout of the nodetree */ + for(sock= node->inputs.first; sock; sock= sock->next) { + if(sock->flag & SOCK_SEL) { + ngroup->selin= sock; + break; + } + } + for(sock= node->outputs.first; sock; sock= sock->next) { + if(sock->flag & SOCK_SEL) { + ngroup->selout= sock; + break; + } + } + + /* set socket own_index to zero since it can still have a value + * from being in a group before, otherwise it doesn't get a unique + * index in group_verify_own_indices */ + for(sock= node->inputs.first; sock; sock= sock->next) + sock->own_index= 0; + for(sock= node->outputs.first; sock; sock= sock->next) + sock->own_index= 0; } } @@ -499,7 +561,7 @@ bNode *nodeMakeGroupFromSelected(bNodeTree *ntree) ntreeMakeOwnType(ngroup); /* make group node */ - gnode= nodeAddNodeType(ntree, NODE_GROUP, ngroup); + gnode= nodeAddNodeType(ntree, NODE_GROUP, ngroup, NULL); gnode->locx= 0.5f*(min[0]+max[0]); gnode->locy= 0.5f*(min[1]+max[1]); @@ -538,6 +600,10 @@ bNode *nodeMakeGroupFromSelected(bNodeTree *ntree) } } } + + /* update node levels */ + ntreeSolveOrder(ntree); + return gnode; } @@ -653,7 +719,8 @@ void nodeGroupSocketUseFlags(bNodeTree *ngroup) } } -static void find_node_with_socket(bNodeTree *ntree, bNodeSocket *sock, bNode **nodep, int *sockindex) +/* finds a node based on given socket */ +int nodeFindNode(bNodeTree *ntree, bNodeSocket *sock, bNode **nodep, int *sockindex) { bNode *node; bNodeSocket *tsock; @@ -671,13 +738,15 @@ static void find_node_with_socket(bNodeTree *ntree, bNodeSocket *sock, bNode **n if(tsock) break; } + if(node) { *nodep= node; - *sockindex= index; - } - else { - *nodep= NULL; + if(sockindex) *sockindex= index; + return 1; } + + *nodep= NULL; + return 0; } /* returns 1 if its OK */ @@ -717,7 +786,7 @@ int nodeGroupUnGroup(bNodeTree *ntree, bNode *gnode) for(link= ntree->links.first; link; link= link->next) { if(link->tonode==gnode) { /* link->tosock->tosock is on the node we look for */ - find_node_with_socket(ngroup, link->tosock->tosock, &nextn, &index); + nodeFindNode(ngroup, link->tosock->tosock, &nextn, &index); if(nextn==NULL) printf("wrong stuff!\n"); else if(nextn->new_node==NULL) printf("wrong stuff too!\n"); else { @@ -727,7 +796,7 @@ int nodeGroupUnGroup(bNodeTree *ntree, bNode *gnode) } else if(link->fromnode==gnode) { /* link->fromsock->tosock is on the node we look for */ - find_node_with_socket(ngroup, link->fromsock->tosock, &nextn, &index); + nodeFindNode(ngroup, link->fromsock->tosock, &nextn, &index); if(nextn==NULL) printf("1 wrong stuff!\n"); else if(nextn->new_node==NULL) printf("1 wrong stuff too!\n"); else { @@ -749,30 +818,28 @@ int nodeGroupUnGroup(bNodeTree *ntree, bNode *gnode) return 1; } -/* ************** Add stuff ********** */ +void nodeCopyGroup(bNode *gnode) +{ + bNodeSocket *sock; + + gnode->id->us--; + gnode->id= (ID *)ntreeCopyTree((bNodeTree *)gnode->id, 0); -bNode *nodeAddNodeType(bNodeTree *ntree, int type, bNodeTree *ngroup) + /* new_sock was set in nodeCopyNode */ + for(sock=gnode->inputs.first; sock; sock=sock->next) + if(sock->tosock) + sock->tosock= sock->tosock->new_sock; + + for(sock=gnode->outputs.first; sock; sock=sock->next) + if(sock->tosock) + sock->tosock= sock->tosock->new_sock; +} + +/* ************** Add stuff ********** */ +void nodeAddSockets(bNode *node, bNodeType *ntype) { - bNode *node; - bNodeType *ntype= node_get_type(ntree, type, ngroup); bNodeSocketType *stype; - - node= MEM_callocN(sizeof(bNode), "new node"); - BLI_addtail(&ntree->nodes, node); - node->typeinfo= ntype; - - if(ngroup) - BLI_strncpy(node->name, ngroup->id.name+2, NODE_MAXSTR); - else - BLI_strncpy(node->name, ntype->name, NODE_MAXSTR); - node->type= ntype->type; - node->flag= NODE_SELECT|ntype->flag; - node->width= ntype->width; - node->miniwidth= 42.0f; /* small value only, allows print of first chars */ - - if(type==NODE_GROUP) - node->id= (ID *)ngroup; - + if(ntype->inputs) { stype= ntype->inputs; while(stype->type != -1) { @@ -787,34 +854,111 @@ bNode *nodeAddNodeType(bNodeTree *ntree, int type, bNodeTree *ngroup) stype++; } } - +} + + +bNode *nodeAddNodeType(bNodeTree *ntree, int type, bNodeTree *ngroup, ID *id) +{ + bNode *node= NULL; + bNodeType *ntype= NULL; + + if(type>=NODE_DYNAMIC_MENU) { + int a=0, idx= type-NODE_DYNAMIC_MENU; + ntype= ntree->alltypes.first; + while(ntype) { + if(ntype->type==NODE_DYNAMIC) { + if(a==idx) + break; + a++; + } + ntype= ntype->next; + } + } else + ntype= node_get_type(ntree, type, ngroup, id); + + node= MEM_callocN(sizeof(bNode), "new node"); + BLI_addtail(&ntree->nodes, node); + node->typeinfo= ntype; + if(type>=NODE_DYNAMIC_MENU) + node->custom2= type; /* for node_dynamic_init */ + + if(ngroup) + BLI_strncpy(node->name, ngroup->id.name+2, NODE_MAXSTR); + else if(type>NODE_DYNAMIC_MENU) { + BLI_strncpy(node->name, ntype->id->name+2, NODE_MAXSTR); + } + else + BLI_strncpy(node->name, ntype->name, NODE_MAXSTR); + node->type= ntype->type; + node->flag= NODE_SELECT|ntype->flag; + node->width= ntype->width; + node->miniwidth= 42.0f; /* small value only, allows print of first chars */ + + if(type==NODE_GROUP) + node->id= (ID *)ngroup; + /* need init handler later? */ - /* got it-bob*/ - if(ntype->initfunc!=NULL) - ntype->initfunc(node); + /* got it-bob*/ + if(ntype->initfunc!=NULL) + ntype->initfunc(node); + + nodeAddSockets(node, ntype); + + return node; +} + +void nodeMakeDynamicType(bNode *node) +{ + /* find SH_DYNAMIC_NODE ntype */ + bNodeType *ntype= node_all_shaders.first; + while(ntype) { + if(ntype->type==NODE_DYNAMIC && ntype->id==NULL) + break; + ntype= ntype->next; + } + + /* make own type struct to fill */ + if(ntype) { + /*node->typeinfo= MEM_dupallocN(ntype);*/ + bNodeType *newtype= MEM_callocN(sizeof(bNodeType), "dynamic bNodeType"); + *newtype= *ntype; + newtype->name= BLI_strdup(ntype->name); + node->typeinfo= newtype; + } +} - return node; +void nodeUpdateType(bNodeTree *ntree, bNode* node, bNodeType *ntype) +{ + verify_socket_list(ntree, &node->inputs, ntype->inputs); + verify_socket_list(ntree, &node->outputs, ntype->outputs); } /* keep socket listorder identical, for copying links */ /* ntree is the target tree */ -bNode *nodeCopyNode(struct bNodeTree *ntree, struct bNode *node) +bNode *nodeCopyNode(struct bNodeTree *ntree, struct bNode *node, int internal) { bNode *nnode= MEM_callocN(sizeof(bNode), "dupli node"); - bNodeSocket *sock; + bNodeSocket *sock, *oldsock; *nnode= *node; BLI_addtail(&ntree->nodes, nnode); BLI_duplicatelist(&nnode->inputs, &node->inputs); - for(sock= nnode->inputs.first; sock; sock= sock->next) - sock->own_index= 0; + oldsock= node->inputs.first; + for(sock= nnode->inputs.first; sock; sock= sock->next, oldsock= oldsock->next) { + oldsock->new_sock= sock; + if(internal) + sock->own_index= 0; + } BLI_duplicatelist(&nnode->outputs, &node->outputs); - for(sock= nnode->outputs.first; sock; sock= sock->next) { - sock->own_index= 0; + oldsock= node->outputs.first; + for(sock= nnode->outputs.first; sock; sock= sock->next, oldsock= oldsock->next) { + if(internal) + sock->own_index= 0; sock->stack_index= 0; sock->ns.data= NULL; + oldsock->new_sock= sock; } if(nnode->id) @@ -892,12 +1036,34 @@ bNodeTree *ntreeCopyTree(bNodeTree *ntree, int internal_select) node->new_node= NULL; if(internal_select==0 || (node->flag & NODE_SELECT)) { - nnode= nodeCopyNode(newtree, node); /* sets node->new */ + nnode= nodeCopyNode(newtree, node, internal_select); /* sets node->new */ if(internal_select) { node->flag &= ~NODE_SELECT; nnode->flag |= NODE_SELECT; } node->flag &= ~NODE_ACTIVE; + + /* deselect original sockets */ + for(sock= node->inputs.first; sock; sock= sock->next) { + if(sock->flag & SOCK_SEL) sock->flag&= ~SOCK_SEL; + } + for(sock= node->outputs.first; sock; sock= sock->next) { + if(sock->flag & SOCK_SEL) sock->flag&= ~SOCK_SEL; + } + + /* set tree selin and selout to new sockets */ + for(sock= nnode->inputs.first; sock; sock= sock->next) { + if(sock->flag & SOCK_SEL) { + ntree->selin= sock; + break; + } + } + for(sock= nnode->outputs.first; sock; sock= sock->next) { + if(sock->flag & SOCK_SEL) { + ntree->selout= sock; + break; + } + } } if(node==last) break; } @@ -941,7 +1107,7 @@ bNodeTree *ntreeCopyTree(bNodeTree *ntree, int internal_select) /* ************** Free stuff ********** */ /* goes over entire tree */ -static void node_unlink_node(bNodeTree *ntree, bNode *node) +void nodeUnlinkNode(bNodeTree *ntree, bNode *node) { bNodeLink *link, *next; bNodeSocket *sock; @@ -985,7 +1151,7 @@ static void composit_free_node_cache(bNode *node) void nodeFreeNode(bNodeTree *ntree, bNode *node) { - node_unlink_node(ntree, node); + nodeUnlinkNode(ntree, node); BLI_remlink(&ntree->nodes, node); /* since it is called while free database, node->id is undefined */ @@ -1003,6 +1169,7 @@ void nodeFreeNode(bNodeTree *ntree, bNode *node) if(node->typeinfo && node->typeinfo->freestoragefunc) { node->typeinfo->freestoragefunc(node); } + MEM_freeN(node); } @@ -1204,11 +1371,21 @@ bNode *nodeGetActiveID(bNodeTree *ntree, short idtype) bNode *node; if(ntree==NULL) return NULL; + + /* check for group edit */ + for(node= ntree->nodes.first; node; node= node->next) + if(node->flag & NODE_GROUP_EDIT) + break; + + if(node) + ntree= (bNodeTree*)node->id; + /* now find active node with this id */ for(node= ntree->nodes.first; node; node= node->next) if(node->id && GS(node->id->name)==idtype) if(node->flag & NODE_ACTIVE_ID) break; + return node; } @@ -1629,7 +1806,7 @@ static void composit_begin_exec(bNodeTree *ntree, int is_group) if(is_group==0) { for(sock= node->outputs.first; sock; sock= sock->next) { - bNodeStack *ns= ntree->stack[0] + sock->stack_index; + bNodeStack *ns= ntree->stack + sock->stack_index; if(sock->ns.data) { ns->data= sock->ns.data; @@ -1662,7 +1839,7 @@ static void composit_end_exec(bNodeTree *ntree, int is_group) bNodeSocket *sock; for(sock= node->outputs.first; sock; sock= sock->next) { - ns= ntree->stack[0] + sock->stack_index; + ns= ntree->stack + sock->stack_index; if(ns->data) { sock->ns.data= ns->data; ns->data= NULL; @@ -1680,7 +1857,7 @@ static void composit_end_exec(bNodeTree *ntree, int is_group) if(is_group==0) { /* internally, group buffers are not stored */ - for(ns= ntree->stack[0], a=0; a<ntree->stacksize; a++, ns++) { + for(ns= ntree->stack, a=0; a<ntree->stacksize; a++, ns++) { if(ns->data) { printf("freed leftover buffer from stack\n"); free_compbuf(ns->data); @@ -1721,15 +1898,47 @@ static void group_tag_used_outputs(bNode *gnode, bNodeStack *stack) /* per tree (and per group) unique indices are created */ /* the index_ext we need to be able to map from groups to the group-node own stack */ +typedef struct bNodeThreadStack { + struct bNodeThreadStack *next, *prev; + bNodeStack *stack; + int used; +} bNodeThreadStack; + +static bNodeThreadStack *ntreeGetThreadStack(bNodeTree *ntree, int thread) +{ + ListBase *lb= &ntree->threadstack[thread]; + bNodeThreadStack *nts; + + for(nts=lb->first; nts; nts=nts->next) { + if(!nts->used) { + nts->used= 1; + return nts; + } + } + + nts= MEM_callocN(sizeof(bNodeThreadStack), "bNodeThreadStack"); + nts->stack= MEM_dupallocN(ntree->stack); + nts->used= 1; + BLI_addtail(lb, nts); + + return nts; +} + +static void ntreeReleaseThreadStack(bNodeThreadStack *nts) +{ + nts->used= 0; +} + void ntreeBeginExecTree(bNodeTree *ntree) { /* let's make it sure */ if(ntree->init & NTREE_EXEC_INIT) return; - - /* allocate the stack pointer array */ - ntree->stack= MEM_callocN(BLENDER_MAX_THREADS*sizeof(void *), "stack array"); - + + /* allocate the thread stack listbase array */ + if(ntree->type!=NTREE_COMPOSIT) + ntree->threadstack= MEM_callocN(BLENDER_MAX_THREADS*sizeof(ListBase), "thread stack array"); + /* goes recursive over all groups */ ntree->stacksize= ntree_begin_exec_tree(ntree); @@ -1739,7 +1948,7 @@ void ntreeBeginExecTree(bNodeTree *ntree) int a; /* allocate the base stack */ - ns=ntree->stack[0]= MEM_callocN(ntree->stacksize*sizeof(bNodeStack), "node stack"); + ns=ntree->stack= MEM_callocN(ntree->stacksize*sizeof(bNodeStack), "node stack"); /* tag inputs, the get_stack() gives own socket stackdata if not in use */ for(a=0; a<ntree->stacksize; a++, ns++) ns->hasinput= 1; @@ -1749,7 +1958,7 @@ void ntreeBeginExecTree(bNodeTree *ntree) bNodeSocket *sock; for(sock= node->inputs.first; sock; sock= sock->next) { if(sock->link) { - ns= ntree->stack[0] + sock->link->fromsock->stack_index; + ns= ntree->stack + sock->link->fromsock->stack_index; ns->hasoutput= 1; ns->sockettype= sock->link->fromsock->type; } @@ -1757,16 +1966,11 @@ void ntreeBeginExecTree(bNodeTree *ntree) sock->ns.sockettype= sock->type; } if(node->type==NODE_GROUP && node->id) - group_tag_used_outputs(node, ntree->stack[0]); + group_tag_used_outputs(node, ntree->stack); } - /* composite does 1 node per thread, so no multiple stacks needed */ if(ntree->type==NTREE_COMPOSIT) composit_begin_exec(ntree, 0); - else { - for(a=1; a<BLENDER_MAX_THREADS; a++) - ntree->stack[a]= MEM_dupallocN(ntree->stack[0]); - } } ntree->init |= NTREE_EXEC_INIT; @@ -1776,6 +1980,7 @@ void ntreeEndExecTree(bNodeTree *ntree) { if(ntree->init & NTREE_EXEC_INIT) { + bNodeThreadStack *nts; int a; /* another callback candidate! */ @@ -1783,14 +1988,21 @@ void ntreeEndExecTree(bNodeTree *ntree) composit_end_exec(ntree, 0); if(ntree->stack) { - for(a=0; a<BLENDER_MAX_THREADS; a++) - if(ntree->stack[a]) - MEM_freeN(ntree->stack[a]); - MEM_freeN(ntree->stack); ntree->stack= NULL; } + if(ntree->threadstack) { + for(a=0; a<BLENDER_MAX_THREADS; a++) { + for(nts=ntree->threadstack[a].first; nts; nts=nts->next) + if (nts->stack) MEM_freeN(nts->stack); + BLI_freelistN(&ntree->threadstack[a]); + } + + MEM_freeN(ntree->threadstack); + ntree->threadstack= NULL; + } + ntree->init &= ~NTREE_EXEC_INIT; } } @@ -1819,12 +2031,20 @@ void ntreeExecTree(bNodeTree *ntree, void *callerdata, int thread) bNodeStack *nsin[MAX_SOCKET]; /* arbitrary... watch this */ bNodeStack *nsout[MAX_SOCKET]; /* arbitrary... watch this */ bNodeStack *stack; + bNodeThreadStack *nts = NULL; /* only when initialized */ if((ntree->init & NTREE_EXEC_INIT)==0) ntreeBeginExecTree(ntree); - stack= ntree->stack[thread]; + /* composite does 1 node per thread, so no multiple stacks needed */ + if(ntree->type==NTREE_COMPOSIT) { + stack= ntree->stack; + } + else { + nts= ntreeGetThreadStack(ntree, thread); + stack= nts->stack; + } for(node= ntree->nodes.first; node; node= node->next) { if(node->typeinfo->execfunc) { @@ -1836,10 +2056,34 @@ void ntreeExecTree(bNodeTree *ntree, void *callerdata, int thread) node_group_execute(stack, callerdata, node, nsin, nsout); } } + + if(nts) + ntreeReleaseThreadStack(nts); } /* ***************************** threaded version for execute composite nodes ************* */ +/* these are nodes without input, only giving values */ +/* or nodes with only value inputs */ +static int node_only_value(bNode *node) +{ + bNodeSocket *sock; + + if(ELEM3(node->type, CMP_NODE_TIME, CMP_NODE_VALUE, CMP_NODE_RGB)) + return 1; + + /* doing this for all node types goes wrong. memory free errors */ + if(node->inputs.first && node->type==CMP_NODE_MAP_VALUE) { + int retval= 1; + for(sock= node->inputs.first; sock; sock= sock->next) { + if(sock->link) + retval &= node_only_value(sock->link->fromnode); + } + return retval; + } + return 0; +} + /* not changing info, for thread callback */ typedef struct ThreadData { @@ -1856,7 +2100,14 @@ static void *exec_composite_node(void *node_v) node_get_stack(node, thd->stack, nsin, nsout); - if(node->typeinfo->execfunc) { + if((node->flag & NODE_MUTED) && (!node_only_value(node))) { + /* viewers we execute, for feedback to user */ + if(ELEM(node->type, CMP_NODE_VIEWER, CMP_NODE_SPLITVIEWER)) + node->typeinfo->execfunc(thd->rd, node, nsin, nsout); + else + node_compo_pass_on(node, nsin, nsout); + } + else if(node->typeinfo->execfunc) { node->typeinfo->execfunc(thd->rd, node, nsin, nsout); } else if(node->type==NODE_GROUP && node->id) { @@ -1867,27 +2118,6 @@ static void *exec_composite_node(void *node_v) return 0; } -/* these are nodes without input, only giving values */ -/* or nodes with only value inputs */ -static int node_only_value(bNode *node) -{ - bNodeSocket *sock; - - if(ELEM3(node->type, CMP_NODE_TIME, CMP_NODE_VALUE, CMP_NODE_RGB)) - return 1; - - /* doing this for all node types goes wrong. memory free errors */ - if(node->inputs.first && node->type==CMP_NODE_MAP_VALUE) { - int retval= 1; - for(sock= node->inputs.first; sock; sock= sock->next) { - if(sock->link) - retval &= node_only_value(sock->link->fromnode); - } - return retval; - } - return 0; -} - /* return total of executable nodes, for timecursor */ /* only compositor uses it */ static int setExecutableNodes(bNodeTree *ntree, ThreadData *thd) @@ -2010,7 +2240,7 @@ static void freeExecutableNode(bNodeTree *ntree) for(node= ntree->nodes.first; node; node= node->next) { if(node->exec & NODE_FREEBUFS) { for(sock= node->outputs.first; sock; sock= sock->next) { - bNodeStack *ns= ntree->stack[0] + sock->stack_index; + bNodeStack *ns= ntree->stack + sock->stack_index; if(ns->data) { free_compbuf(ns->data); ns->data= NULL; @@ -2064,7 +2294,7 @@ void ntreeCompositExecTree(bNodeTree *ntree, RenderData *rd, int do_preview) /* setup callerdata for thread callback */ thdata.rd= rd; - thdata.stack= ntree->stack[0]; + thdata.stack= ntree->stack; /* fixed seed, for example noise texture */ BLI_srandom(rd->cfra); @@ -2128,6 +2358,117 @@ void ntreeCompositExecTree(bNodeTree *ntree, RenderData *rd, int do_preview) ntreeEndExecTree(ntree); } +/* GPU material from shader nodes */ + +static void gpu_from_node_stack(ListBase *sockets, bNodeStack **ns, GPUNodeStack *gs) +{ + bNodeSocket *sock; + int i; + + for (sock=sockets->first, i=0; sock; sock=sock->next, i++) { + memset(&gs[i], 0, sizeof(gs[i])); + + QUATCOPY(gs[i].vec, ns[i]->vec); + gs[i].link= ns[i]->data; + + if (sock->type == SOCK_VALUE) + gs[i].type= GPU_FLOAT; + else if (sock->type == SOCK_VECTOR) + gs[i].type= GPU_VEC3; + else if (sock->type == SOCK_RGBA) + gs[i].type= GPU_VEC4; + else + gs[i].type= GPU_NONE; + + gs[i].name = ""; + gs[i].hasinput= ns[i]->hasinput && ns[i]->data; + gs[i].hasoutput= ns[i]->hasinput && ns[i]->data; + gs[i].sockettype= ns[i]->sockettype; + } + + gs[i].type= GPU_NONE; +} + +static void data_from_gpu_stack(ListBase *sockets, bNodeStack **ns, GPUNodeStack *gs) +{ + bNodeSocket *sock; + int i; + + for (sock=sockets->first, i=0; sock; sock=sock->next, i++) { + ns[i]->data= gs[i].link; + ns[i]->hasinput= gs[i].hasinput && gs[i].link; + ns[i]->hasoutput= gs[i].hasoutput; + ns[i]->sockettype= gs[i].sockettype; + } +} + +static void gpu_node_group_execute(bNodeStack *stack, GPUMaterial *mat, bNode *gnode, bNodeStack **in, bNodeStack **out) +{ + bNode *node; + bNodeTree *ntree= (bNodeTree *)gnode->id; + bNodeStack *nsin[MAX_SOCKET]; /* arbitrary... watch this */ + bNodeStack *nsout[MAX_SOCKET]; /* arbitrary... watch this */ + GPUNodeStack gpuin[MAX_SOCKET+1], gpuout[MAX_SOCKET+1]; + int doit = 0; + + if(ntree==NULL) return; + + stack+= gnode->stack_index; + + for(node= ntree->nodes.first; node; node= node->next) { + if(node->typeinfo->gpufunc) { + group_node_get_stack(node, stack, nsin, nsout, in, out); + + doit = 0; + + /* for groups, only execute outputs for edited group */ + if(node->typeinfo->nclass==NODE_CLASS_OUTPUT) { + if(gnode->flag & NODE_GROUP_EDIT) + if(node->flag & NODE_DO_OUTPUT) + doit = 1; + } + else + doit = 1; + + if(doit) { + gpu_from_node_stack(&node->inputs, nsin, gpuin); + gpu_from_node_stack(&node->outputs, nsout, gpuout); + if(node->typeinfo->gpufunc(mat, node, gpuin, gpuout)) + data_from_gpu_stack(&node->outputs, nsout, gpuout); + } + } + } +} + +void ntreeGPUMaterialNodes(bNodeTree *ntree, GPUMaterial *mat) +{ + bNode *node; + bNodeStack *stack; + bNodeStack *nsin[MAX_SOCKET]; /* arbitrary... watch this */ + bNodeStack *nsout[MAX_SOCKET]; /* arbitrary... watch this */ + GPUNodeStack gpuin[MAX_SOCKET+1], gpuout[MAX_SOCKET+1]; + + if((ntree->init & NTREE_EXEC_INIT)==0) + ntreeBeginExecTree(ntree); + + stack= ntree->stack; + + for(node= ntree->nodes.first; node; node= node->next) { + if(node->typeinfo->gpufunc) { + node_get_stack(node, stack, nsin, nsout); + gpu_from_node_stack(&node->inputs, nsin, gpuin); + gpu_from_node_stack(&node->outputs, nsout, gpuout); + if(node->typeinfo->gpufunc(mat, node, gpuin, gpuout)) + data_from_gpu_stack(&node->outputs, nsout, gpuout); + } + else if(node->type==NODE_GROUP && node->id) { + node_get_stack(node, stack, nsin, nsout); + gpu_node_group_execute(stack, mat, node, nsin, nsout); + } + } + + ntreeEndExecTree(ntree); +} /* **************** call to switch lamploop for material node ************ */ @@ -2172,6 +2513,8 @@ static void force_hidden_passes(bNode *node, int passflag) if(!(passflag & SCE_PASS_RADIO)) sock->flag |= SOCK_UNAVAIL; sock= BLI_findlink(&node->outputs, RRES_OUT_INDEXOB); if(!(passflag & SCE_PASS_INDEXOB)) sock->flag |= SOCK_UNAVAIL; + sock= BLI_findlink(&node->outputs, RRES_OUT_MIST); + if(!(passflag & SCE_PASS_MIST)) sock->flag |= SOCK_UNAVAIL; } @@ -2281,12 +2624,12 @@ void ntreeCompositTagGenerators(bNodeTree *ntree) /* ************* node definition init ********** */ -static bNodeType *is_nodetype_registered(ListBase *typelist, int type) +static bNodeType *is_nodetype_registered(ListBase *typelist, int type, ID *id) { bNodeType *ntype= typelist->first; for(;ntype; ntype= ntype->next ) - if(ntype->type==type) + if(ntype->type==type && ntype->id==id) return ntype; return NULL; @@ -2295,10 +2638,10 @@ static bNodeType *is_nodetype_registered(ListBase *typelist, int type) /* type can be from a static array, we make copy for duplicate types (like group) */ void nodeRegisterType(ListBase *typelist, const bNodeType *ntype) { - bNodeType *found= is_nodetype_registered(typelist, ntype->type); + bNodeType *found= is_nodetype_registered(typelist, ntype->type, ntype->id); if(found==NULL) { - bNodeType *ntypen= MEM_mallocN(sizeof(bNodeType), "node type"); + bNodeType *ntypen= MEM_callocN(sizeof(bNodeType), "node type"); *ntypen= *ntype; BLI_addtail(typelist, ntypen); } @@ -2335,6 +2678,8 @@ static void registerCompositNodes(ListBase *ntypelist) nodeRegisterType(ntypelist, &cmp_node_filter); nodeRegisterType(ntypelist, &cmp_node_blur); + nodeRegisterType(ntypelist, &cmp_node_dblur); + nodeRegisterType(ntypelist, &cmp_node_bilateralblur); nodeRegisterType(ntypelist, &cmp_node_vecblur); nodeRegisterType(ntypelist, &cmp_node_dilateerode); nodeRegisterType(ntypelist, &cmp_node_defocus); @@ -2352,6 +2697,7 @@ static void registerCompositNodes(ListBase *ntypelist) nodeRegisterType(ntypelist, &cmp_node_combyuva); nodeRegisterType(ntypelist, &cmp_node_sepycca); nodeRegisterType(ntypelist, &cmp_node_combycca); + nodeRegisterType(ntypelist, &cmp_node_premulkey); nodeRegisterType(ntypelist, &cmp_node_diff_matte); nodeRegisterType(ntypelist, &cmp_node_chroma); @@ -2366,7 +2712,6 @@ static void registerCompositNodes(ListBase *ntypelist) nodeRegisterType(ntypelist, &cmp_node_crop); nodeRegisterType(ntypelist, &cmp_node_displace); nodeRegisterType(ntypelist, &cmp_node_mapuv); - nodeRegisterType(ntypelist, &cmp_node_glare); nodeRegisterType(ntypelist, &cmp_node_tonemap); nodeRegisterType(ntypelist, &cmp_node_lensdist); @@ -2393,12 +2738,46 @@ static void registerShaderNodes(ListBase *ntypelist) nodeRegisterType(ntypelist, &sh_node_value); nodeRegisterType(ntypelist, &sh_node_rgb); nodeRegisterType(ntypelist, &sh_node_texture); + nodeRegisterType(ntypelist, &node_dynamic_typeinfo); nodeRegisterType(ntypelist, &sh_node_invert); nodeRegisterType(ntypelist, &sh_node_seprgb); nodeRegisterType(ntypelist, &sh_node_combrgb); nodeRegisterType(ntypelist, &sh_node_hue_sat); } +static void remove_dynamic_typeinfos(ListBase *list) +{ + bNodeType *ntype= list->first; + bNodeType *next= NULL; + while(ntype) { + next= ntype->next; + if(ntype->type==NODE_DYNAMIC && ntype->id!=NULL) { + BLI_remlink(list, ntype); + if(ntype->inputs) { + bNodeSocketType *sock= ntype->inputs; + while(sock->type!=-1) { + MEM_freeN(sock->name); + sock++; + } + MEM_freeN(ntype->inputs); + } + if(ntype->outputs) { + bNodeSocketType *sock= ntype->outputs; + while(sock->type!=-1) { + MEM_freeN(sock->name); + sock++; + } + MEM_freeN(ntype->outputs); + } + if(ntype->name) { + MEM_freeN(ntype->name); + } + MEM_freeN(ntype); + } + ntype= next; + } +} + void init_nodesystem(void) { registerCompositNodes(&node_all_composit); @@ -2407,8 +2786,8 @@ void init_nodesystem(void) void free_nodesystem(void) { + /*remove_dynamic_typeinfos(&node_all_composit);*/ /* unused for now */ BLI_freelistN(&node_all_composit); + remove_dynamic_typeinfos(&node_all_shaders); BLI_freelistN(&node_all_shaders); } - - diff --git a/source/blender/blenkernel/intern/object.c b/source/blender/blenkernel/intern/object.c index e5edd2239ff..2d7a5283b0c 100644 --- a/source/blender/blenkernel/intern/object.c +++ b/source/blender/blenkernel/intern/object.c @@ -50,6 +50,8 @@ #include "DNA_lattice_types.h" #include "DNA_material_types.h" #include "DNA_mesh_types.h" +#include "DNA_meta_types.h" +#include "DNA_curve_types.h" #include "DNA_meshdata_types.h" #include "DNA_modifier_types.h" #include "DNA_nla_types.h" @@ -68,6 +70,7 @@ #include "BKE_armature.h" #include "BKE_action.h" +#include "BKE_bullet.h" #include "BKE_colortools.h" #include "BKE_deform.h" #include "BKE_DerivedMesh.h" @@ -87,7 +90,6 @@ #include "BKE_constraint.h" #include "BKE_curve.h" #include "BKE_displist.h" -#include "BKE_effect.h" #include "BKE_group.h" #include "BKE_icons.h" #include "BKE_ipo.h" @@ -99,6 +101,7 @@ #include "BKE_modifier.h" #include "BKE_object.h" #include "BKE_particle.h" +#include "BKE_pointcache.h" #include "BKE_property.h" #include "BKE_sca.h" #include "BKE_scene.h" @@ -107,7 +110,11 @@ #include "LBM_fluidsim.h" +#ifndef DISABLE_PYTHON #include "BPY_extern.h" +#endif + +#include "GPU_material.h" /* Local function protos */ static void solve_parenting (Object *ob, Object *par, float obmat[][4], float slowmat[][4], int simul); @@ -153,6 +160,33 @@ void update_base_layer(Object *ob) } } +void object_free_particlesystems(Object *ob) +{ + while(ob->particlesystem.first){ + ParticleSystem *psys = ob->particlesystem.first; + + BLI_remlink(&ob->particlesystem,psys); + + psys_free(ob,psys); + } +} + +void object_free_softbody(Object *ob) +{ + if(ob->soft) { + sbFree(ob->soft); + ob->soft= NULL; + } +} + +void object_free_bulletsoftbody(Object *ob) +{ + if(ob->bsoft) { + bsbFree(ob->bsoft); + ob->bsoft= NULL; + } +} + void object_free_modifiers(Object *ob) { while (ob->modifiers.first) { @@ -164,13 +198,10 @@ void object_free_modifiers(Object *ob) } /* particle modifiers were freed, so free the particlesystems as well */ - while(ob->particlesystem.first){ - ParticleSystem *psys = ob->particlesystem.first; - - BLI_remlink(&ob->particlesystem,psys); + object_free_particlesystems(ob); - psys_free(ob,psys); - } + /* same for softbody */ + object_free_softbody(ob); } /* here we will collect all local displist stuff */ @@ -221,14 +252,12 @@ void free_object(Object *ob) ob->path= 0; if(ob->ipo) ob->ipo->id.us--; if(ob->action) ob->action->id.us--; + if(ob->poselib) ob->poselib->id.us--; if(ob->dup_group) ob->dup_group->id.us--; if(ob->defbase.first) BLI_freelistN(&ob->defbase); - if(ob->pose) { - free_pose_channels(ob->pose); - MEM_freeN(ob->pose); - } - free_effects(&ob->effect); + if(ob->pose) + free_pose(ob->pose); free_properties(&ob->prop); object_free_modifiers(ob); @@ -239,8 +268,10 @@ void free_object(Object *ob) free_constraints(&ob->constraints); free_constraint_channels(&ob->constraintChannels); free_nlastrips(&ob->nlastrips); - + +#ifndef DISABLE_PYTHON BPY_free_scriptlink(&ob->scriptlink); +#endif if(ob->pd){ if(ob->pd->tex) @@ -248,7 +279,8 @@ void free_object(Object *ob) MEM_freeN(ob->pd); } if(ob->soft) sbFree(ob->soft); - if(ob->fluidsimSettings) fluidsimSettingsFree(ob->fluidsimSettings); + if(ob->bsoft) bsbFree(ob->bsoft); + if(ob->gpulamp.first) GPU_lamp_free(ob); } static void unlink_object__unlinkModifierLinks(void *userData, Object *ob, Object **obpoin) @@ -274,6 +306,7 @@ void unlink_object(Object *ob) Camera *camera; bConstraint *con; bActionStrip *strip; + ModifierData *md; int a; unlink_controllers(&ob->controllers); @@ -372,10 +405,13 @@ void unlink_object(Object *ob) /* object is deflector or field */ if(ob->pd) { - if(give_parteff(obt)) - obt->recalc |= OB_RECALC_DATA; - else if(obt->soft) + if(obt->soft) obt->recalc |= OB_RECALC_DATA; + + /* cloth */ + for(md=obt->modifiers.first; md; md=md->next) + if(md->type == eModifierType_Cloth) + obt->recalc |= OB_RECALC_DATA; } /* strips */ @@ -511,11 +547,11 @@ void unlink_object(Object *ob) if(v3d->camera==ob) { v3d->camera= NULL; - if(v3d->persp>1) v3d->persp= 1; + if(v3d->persp==V3D_CAMOB) v3d->persp= V3D_PERSP; } if(v3d->localvd && v3d->localvd->camera==ob ) { v3d->localvd->camera= NULL; - if(v3d->localvd->persp>1) v3d->localvd->persp= 1; + if(v3d->localvd->persp==V3D_CAMOB) v3d->localvd->persp= V3D_PERSP; } } else if(sl->spacetype==SPACE_IPO) { @@ -602,9 +638,9 @@ Camera *copy_camera(Camera *cam) camn= copy_libblock(cam); id_us_plus((ID *)camn->ipo); - +#ifndef DISABLE_PYTHON BPY_copy_scriptlink(&camn->scriptlink); - +#endif return camn; } @@ -671,9 +707,11 @@ float dof_camera(Object *ob) if (cam->dof_ob) { /* too simple, better to return the distance on the view axis only * return VecLenf(ob->obmat[3], cam->dof_ob->obmat[3]); */ + float mat[4][4], obmat[4][4]; - float mat[4][4]; - Mat4Invert(ob->imat, ob->obmat); + Mat4CpyMat4(obmat, ob->obmat); + Mat4Ortho(obmat); + Mat4Invert(ob->imat, obmat); Mat4MulMat4(mat, cam->dof_ob->obmat, ob->imat); return fabs(mat[3][2]); } @@ -709,6 +747,22 @@ void *add_lamp(char *name) la->preview=NULL; la->falloff_type = LA_FALLOFF_INVLINEAR; la->curfalloff = curvemapping_add(1, 0.0f, 1.0f, 1.0f, 0.0f); + la->sun_effect_type = 0; + la->horizon_brightness = 1.0; + la->spread = 1.0; + la->sun_brightness = 1.0; + la->sun_size = 1.0; + la->backscattered_light = 1.0; + la->atm_turbidity = 2.0; + la->atm_inscattering_factor = 1.0; + la->atm_extinction_factor = 1.0; + la->atm_distance_factor = 1.0; + la->sun_intensity = 1.0; + la->skyblendtype= MA_RAMP_ADD; + la->skyblendfac= 1.0f; + la->sky_colorspace= BLI_CS_CIE; + la->sky_exposure= 1.0f; + curvemapping_initialize(la->curfalloff); return la; } @@ -733,9 +787,9 @@ Lamp *copy_lamp(Lamp *la) id_us_plus((ID *)lan->ipo); if (la->preview) lan->preview = BKE_previewimg_copy(la->preview); - +#ifndef DISABLE_PYTHON BPY_copy_scriptlink(&la->scriptlink); - +#endif return lan; } @@ -793,7 +847,9 @@ void make_local_lamp(Lamp *la) void free_camera(Camera *ca) { +#ifndef DISABLE_PYTHON BPY_free_scriptlink(&ca->scriptlink); +#endif } void free_lamp(Lamp *la) @@ -802,8 +858,9 @@ void free_lamp(Lamp *la) int a; /* scriptlinks */ - +#ifndef DISABLE_PYTHON BPY_free_scriptlink(&la->scriptlink); +#endif for(a=0; a<MAX_MTEX; a++) { mtex= la->mtex[a]; @@ -879,10 +936,12 @@ Object *add_only_object(int type, char *name) ob->type= type; /* ob->transflag= OB_QUAT; */ +#if 0 /* not used yet */ QuatOne(ob->quat); QuatOne(ob->dquat); +#endif - ob->col[0]= ob->col[1]= ob->col[2]= 0.0; + ob->col[0]= ob->col[1]= ob->col[2]= 1.0; ob->col[3]= 1.0; ob->loc[0]= ob->loc[1]= ob->loc[2]= 0.0; @@ -919,8 +978,9 @@ Object *add_only_object(int type, char *name) ob->anisotropicFriction[0] = 1.0f; ob->anisotropicFriction[1] = 1.0f; ob->anisotropicFriction[2] = 1.0f; - ob->gameflag= OB_PROP; - + ob->gameflag= OB_PROP|OB_COLLISION; + ob->margin = 0.0; + /* NT fluid sim defaults */ ob->fluidsimFlag = 0; ob->fluidsimSettings = NULL; @@ -971,11 +1031,13 @@ void base_init_from_view3d(Base *base, View3D *v3d) if (U.flag & USER_ADD_VIEWALIGNED) { v3d->viewquat[0]= -v3d->viewquat[0]; - if (ob->transflag & OB_QUAT) { + + /* Quats arnt used yet */ + /*if (ob->transflag & OB_QUAT) { QUATCOPY(ob->quat, v3d->viewquat); - } else { + } else {*/ QuatToEul(v3d->viewquat, ob->rot); - } + /*}*/ v3d->viewquat[0]= -v3d->viewquat[0]; } } @@ -990,15 +1052,28 @@ SoftBody *copy_softbody(SoftBody *sb) sbn->totspring= sbn->totpoint= 0; sbn->bpoint= NULL; sbn->bspring= NULL; - sbn->ctime= 0.0f; sbn->keys= NULL; sbn->totkey= sbn->totpointkey= 0; sbn->scratch= NULL; + + sbn->pointcache= BKE_ptcache_copy(sb->pointcache); + return sbn; } +BulletSoftBody *copy_bulletsoftbody(BulletSoftBody *bsb) +{ + BulletSoftBody *bsbn; + + if (bsb == NULL) + return NULL; + bsbn = MEM_dupallocN(bsb); + /* no pointer in this structure yet */ + return bsbn; +} + ParticleSystem *copy_particlesystem(ParticleSystem *psys) { ParticleSystem *psysn; @@ -1016,23 +1091,63 @@ ParticleSystem *copy_particlesystem(ParticleSystem *psys) pa->keys= MEM_dupallocN(pa->keys); } - if(psys->soft) + if(psys->soft) { psysn->soft= copy_softbody(psys->soft); + psysn->soft->particles = psysn; + } psysn->pathcache= NULL; psysn->childcache= NULL; psysn->edit= NULL; psysn->effectors.first= psysn->effectors.last= 0; + + psysn->pathcachebufs.first = psysn->pathcachebufs.last = NULL; + psysn->childcachebufs.first = psysn->childcachebufs.last = NULL; + psysn->reactevents.first = psysn->reactevents.last = NULL; + psysn->renderdata = NULL; + + psysn->pointcache= BKE_ptcache_copy(psys->pointcache); id_us_plus((ID *)psysn->part); return psysn; } +void copy_object_particlesystems(Object *obn, Object *ob) +{ + ParticleSystemModifierData *psmd; + ParticleSystem *psys, *npsys; + ModifierData *md; + + obn->particlesystem.first= obn->particlesystem.last= NULL; + for(psys=ob->particlesystem.first; psys; psys=psys->next) { + npsys= copy_particlesystem(psys); + + BLI_addtail(&obn->particlesystem, npsys); + + /* need to update particle modifiers too */ + for(md=obn->modifiers.first; md; md=md->next) { + if(md->type==eModifierType_ParticleSystem) { + psmd= (ParticleSystemModifierData*)md; + if(psmd->psys==psys) + psmd->psys= npsys; + } + } + } +} + +void copy_object_softbody(Object *obn, Object *ob) +{ + if(ob->soft) + obn->soft= copy_softbody(ob->soft); +} + static void copy_object_pose(Object *obn, Object *ob) { bPoseChannel *chan; + /* note: need to clear obn->pose pointer first, so that copy_pose works (otherwise there's a crash) */ + obn->pose= NULL; copy_pose(&obn->pose, ob->pose, 1); /* 1 = copy constraints */ for (chan = obn->pose->chanbase.first; chan; chan=chan->next){ @@ -1045,7 +1160,10 @@ static void copy_object_pose(Object *obn, Object *ob) ListBase targets = {NULL, NULL}; bConstraintTarget *ct; - if(con->ipo) { + /* note that we can't change lib linked ipo blocks. for making + * proxies this still works correct however because the object + * is changed to object->proxy_from when evaluating the driver. */ + if(con->ipo && !con->ipo->id.lib) { IpoCurve *icu; for(icu= con->ipo->curve.first; icu; icu= icu->next) { if(icu->driver && icu->driver->ob==ob) @@ -1072,7 +1190,6 @@ Object *copy_object(Object *ob) { Object *obn; ModifierData *md; - ParticleSystem *psys; int a; obn= copy_libblock(ob); @@ -1085,7 +1202,6 @@ Object *copy_object(Object *ob) obn->path= NULL; obn->flag &= ~OB_FROMGROUP; - copy_effects(&obn->effect, &ob->effect); obn->modifiers.first = obn->modifiers.last= NULL; for (md=ob->modifiers.first; md; md=md->next) { @@ -1093,10 +1209,12 @@ Object *copy_object(Object *ob) modifier_copyData(md, nmd); BLI_addtail(&obn->modifiers, nmd); } - +#ifndef DISABLE_PYTHON BPY_copy_scriptlink(&ob->scriptlink); - +#endif + obn->prop.first = obn->prop.last = NULL; copy_properties(&obn->prop, &ob->prop); + copy_sensors(&obn->sensors, &ob->sensors); copy_controllers(&obn->controllers, &ob->controllers); copy_actuators(&obn->actuators, &ob->actuators); @@ -1129,32 +1247,9 @@ Object *copy_object(Object *ob) id_us_plus(&(obn->pd->tex->id)); } obn->soft= copy_softbody(ob->soft); + obn->bsoft = copy_bulletsoftbody(ob->bsoft); - /* NT copy fluid sim setting memory */ - if(obn->fluidsimSettings) { - obn->fluidsimSettings = fluidsimSettingsCopy(ob->fluidsimSettings); - /* copying might fail... */ - if(obn->fluidsimSettings) { - obn->fluidsimSettings->orgMesh = (Mesh *)obn->data; - } - } - - obn->particlesystem.first= obn->particlesystem.last= NULL; - for(psys=ob->particlesystem.first; psys; psys=psys->next) { - ParticleSystemModifierData *psmd; - ParticleSystem *npsys= copy_particlesystem(psys); - - BLI_addtail(&obn->particlesystem, npsys); - - /* need to update particle modifiers too */ - for(md=obn->modifiers.first; md; md=md->next) { - if(md->type==eModifierType_ParticleSystem) { - psmd= (ParticleSystemModifierData*)md; - if(psmd->psys==psys) - psmd->psys= npsys; - } - } - } + copy_object_particlesystems(obn, ob); obn->derivedDeform = NULL; obn->derivedFinal = NULL; @@ -1163,6 +1258,7 @@ Object *copy_object(Object *ob) obn->vnode = NULL; #endif + obn->gpulamp.first = obn->gpulamp.last = NULL; return obn; } @@ -1170,11 +1266,13 @@ Object *copy_object(Object *ob) void expand_local_object(Object *ob) { bActionStrip *strip; + ParticleSystem *psys; int a; id_lib_extern((ID *)ob->action); id_lib_extern((ID *)ob->ipo); id_lib_extern((ID *)ob->data); + id_lib_extern((ID *)ob->dup_group); for(a=0; a<ob->totcol; a++) { id_lib_extern((ID *)ob->mat[a]); @@ -1182,7 +1280,8 @@ void expand_local_object(Object *ob) for (strip=ob->nlastrips.first; strip; strip=strip->next) { id_lib_extern((ID *)strip->act); } - + for(psys=ob->particlesystem.first; psys; psys=psys->next) + id_lib_extern((ID *)psys->part); } void make_local_object(Object *ob) @@ -1306,11 +1405,29 @@ void object_make_proxy(Object *ob, Object *target, Object *gob) /* skip constraints, constraintchannels, nla? */ - + /* set object type and link to data */ ob->type= target->type; ob->data= target->data; id_us_plus((ID *)ob->data); /* ensures lib data becomes LIB_EXTERN */ + /* copy material and index information */ + ob->actcol= ob->totcol= 0; + if(ob->mat) MEM_freeN(ob->mat); + ob->mat = NULL; + if ((target->totcol) && (target->mat) && ELEM5(ob->type, OB_MESH, OB_CURVE, OB_SURF, OB_FONT, OB_MBALL)) { //XXX OB_SUPPORT_MATERIAL + int i; + ob->colbits = target->colbits; + + ob->actcol= target->actcol; + ob->totcol= target->totcol; + + ob->mat = MEM_dupallocN(target->mat); + for(i=0; i<target->totcol; i++) { + /* dont need to run test_object_materials since we know this object is new and not used elsewhere */ + id_us_plus((ID *)ob->mat[i]); + } + } + /* type conversions */ if(target->type == OB_ARMATURE) { copy_object_pose(ob, target); /* data copy, object pointers in constraints */ @@ -1357,12 +1474,12 @@ float bsystem_time(Object *ob, float cfra, float ofs) cfra*= G.scene->r.framelen; if (ob) { - if (no_speed_curve==0 && ob->ipo) + if (no_speed_curve==0 && ob->ipo) cfra= calc_ipo_time(ob->ipo, cfra); /* ofset frames */ if ((ob->ipoflag & OB_OFFS_PARENT) && (ob->partype & PARSLOW)==0) - cfra-= ob->sf; + cfra-= give_timeoffset(ob); } cfra-= ofs; @@ -1370,25 +1487,46 @@ float bsystem_time(Object *ob, float cfra, float ofs) return cfra; } -void object_to_mat3(Object *ob, float mat[][3]) /* no parent */ +void object_scale_to_mat3(Object *ob, float mat[][3]) { - float smat[3][3], vec[3]; - float rmat[3][3]; - float q1[4]; - - /* size */ + float vec[3]; if(ob->ipo) { vec[0]= ob->size[0]+ob->dsize[0]; vec[1]= ob->size[1]+ob->dsize[1]; vec[2]= ob->size[2]+ob->dsize[2]; - SizeToMat3(vec, smat); + SizeToMat3(vec, mat); } else { - SizeToMat3(ob->size, smat); + SizeToMat3(ob->size, mat); } +} + +void object_rot_to_mat3(Object *ob, float mat[][3]) +{ + float vec[3]; + if(ob->ipo) { + vec[0]= ob->rot[0]+ob->drot[0]; + vec[1]= ob->rot[1]+ob->drot[1]; + vec[2]= ob->rot[2]+ob->drot[2]; + EulToMat3(vec, mat); + } + else { + EulToMat3(ob->rot, mat); + } +} + +void object_to_mat3(Object *ob, float mat[][3]) /* no parent */ +{ + float smat[3][3]; + float rmat[3][3]; + /*float q1[4];*/ + + /* size */ + object_scale_to_mat3(ob, smat); /* rot */ - if(ob->transflag & OB_QUAT) { + /* Quats arnt used yet */ + /*if(ob->transflag & OB_QUAT) { if(ob->ipo) { QuatMul(q1, ob->quat, ob->dquat); QuatToMat3(q1, rmat); @@ -1397,17 +1535,9 @@ void object_to_mat3(Object *ob, float mat[][3]) /* no parent */ QuatToMat3(ob->quat, rmat); } } - else { - if(ob->ipo) { - vec[0]= ob->rot[0]+ob->drot[0]; - vec[1]= ob->rot[1]+ob->drot[1]; - vec[2]= ob->rot[2]+ob->drot[2]; - EulToMat3(vec, rmat); - } - else { - EulToMat3(ob->rot, rmat); - } - } + else {*/ + object_rot_to_mat3(ob, rmat); + /*}*/ Mat3MulMat3(mat, rmat, smat); } @@ -1432,8 +1562,8 @@ int enable_cu_speed= 1; static void ob_parcurve(Object *ob, Object *par, float mat[][4]) { Curve *cu; - float q[4], vec[4], dir[3], *quat, x1, ctime; - float timeoffs= 0.0; + float q[4], vec[4], dir[3], quat[4], x1, ctime; + float timeoffs = 0.0, sf_orig = 0.0; Mat4One(mat); @@ -1444,7 +1574,8 @@ static void ob_parcurve(Object *ob, Object *par, float mat[][4]) /* exception, timeoffset is regarded as distance offset */ if(cu->flag & CU_OFFS_PATHDIST) { - SWAP(float, timeoffs, ob->sf); + timeoffs = give_timeoffset(ob); + SWAP(float, sf_orig, ob->sf); } /* catch exceptions: feature for nla stride editing */ @@ -1461,7 +1592,7 @@ static void ob_parcurve(Object *ob, Object *par, float mat[][4]) } } else { - ctime= G.scene->r.cfra - ob->sf; + ctime= G.scene->r.cfra - give_timeoffset(ob); ctime /= cu->pathlen; CLAMP(ctime, 0.0, 1.0); @@ -1472,7 +1603,7 @@ static void ob_parcurve(Object *ob, Object *par, float mat[][4]) ctime += timeoffs/cu->path->totdist; /* restore */ - SWAP(float, timeoffs, ob->sf); + SWAP(float, sf_orig, ob->sf); } @@ -1480,7 +1611,7 @@ static void ob_parcurve(Object *ob, Object *par, float mat[][4]) if( where_on_path(par, ctime, vec, dir) ) { if(cu->flag & CU_FOLLOW) { - quat= vectoquat(dir, ob->trackflag, ob->upflag); + vectoquat(dir, ob->trackflag, ob->upflag, quat); /* the tilt */ Normalize(dir); @@ -1541,7 +1672,7 @@ static void give_parvert(Object *par, int nr, float *vec) for(eve= em->verts.first; eve; eve= eve->next) { if(eve->keyindex==nr) { - memcpy(vec, eve->co, 12); + memcpy(vec, eve->co, sizeof(float)*3); break; } } @@ -1563,9 +1694,12 @@ static void give_parvert(Object *par, int nr, float *vec) } } - if(count > 0) { + if (count==0) { + /* keep as 0,0,0 */ + } else if(count > 0) { VecMulf(vec, 1.0f / count); } else { + /* use first index if its out of range */ dm->getVertCo(dm, 0, vec); } } @@ -1576,18 +1710,20 @@ static void give_parvert(Object *par, int nr, float *vec) Curve *cu; BPoint *bp; BezTriple *bezt; + int found= 0; cu= par->data; nu= cu->nurb.first; //XXX if(par==G.obedit) nu= editNurb.first; count= 0; - while(nu) { + while(nu && !found) { if((nu->type & 7)==CU_BEZIER) { bezt= nu->bezt; a= nu->pntsu; while(a--) { if(count==nr) { + found= 1; VECCOPY(vec, bezt->vec[1]); break; } @@ -1600,7 +1736,8 @@ static void give_parvert(Object *par, int nr, float *vec) a= nu->pntsu*nu->pntsv; while(a--) { if(count==nr) { - memcpy(vec, bp->vec, 12); + found= 1; + memcpy(vec, bp->vec, sizeof(float)*3); break; } count++; @@ -1730,7 +1867,7 @@ void where_is_object_time(Object *ob, float ctime) if(ob->parent) { Object *par= ob->parent; - if(ob->ipoflag & OB_OFFS_PARENT) ctime-= ob->sf; + if(ob->ipoflag & OB_OFFS_PARENT) ctime-= give_timeoffset(ob); /* hurms, code below conflicts with depgraph... (ton) */ /* and even worse, it gives bad effects for NLA stride too (try ctime != par->ctime, with MBlur) */ @@ -1754,7 +1891,7 @@ void where_is_object_time(Object *ob, float ctime) if(ob->partype & PARSLOW) { // include framerate - fac1= (1.0f/(1.0f+ fabs(ob->sf))); + fac1= (1.0f/(1.0f+ fabs(give_timeoffset(ob)))); if(fac1>=1.0) return; fac2= 1.0f-fac1; @@ -1788,10 +1925,11 @@ void where_is_object_time(Object *ob, float ctime) constraints_clear_evalob(cob); } - +#ifndef DISABLE_PYTHON if(ob->scriptlink.totscript && !during_script()) { if (G.f & G_DOSCRIPTLINKS) BPY_do_pyscript((ID *)ob, SCRIPT_REDRAW); } +#endif /* set negative scale flag in object */ Crossf(vec, ob->obmat[0], ob->obmat[1]); @@ -1880,13 +2018,13 @@ static void solve_parenting (Object *ob, Object *par, float obmat[][4], float sl } void solve_tracking (Object *ob, float targetmat[][4]) { - float *quat; + float quat[4]; float vec[3]; float totmat[3][3]; float tmat[4][4]; VecSubf(vec, ob->obmat[3], targetmat[3]); - quat= vectoquat(vec, ob->trackflag, ob->upflag); + vectoquat(vec, ob->trackflag, ob->upflag, quat); QuatToMat3(quat, totmat); if(ob->parent && (ob->transflag & OB_POWERTRACK)) { @@ -1937,7 +2075,7 @@ for a lamp that is the child of another object */ if(ob->partype & PARSLOW) { - fac1= (float)(1.0/(1.0+ fabs(ob->sf))); + fac1= (float)(1.0/(1.0+ fabs(give_timeoffset(ob)))); fac2= 1.0f-fac1; fp1= ob->obmat[0]; fp2= slowmat[0]; @@ -1998,7 +2136,7 @@ BoundBox *unit_boundbox() BoundBox *bb; float min[3] = {-1,-1,-1}, max[3] = {-1,-1,-1}; - bb= MEM_mallocN(sizeof(BoundBox), "bb"); + bb= MEM_callocN(sizeof(BoundBox), "bb"); boundbox_set_from_min_max(bb, min, max); return bb; @@ -2142,7 +2280,7 @@ void object_handle_update(Object *ob) if(ob->recalc & OB_RECALC) { if(ob->recalc & OB_RECALC_OB) { - + // printf("recalcob %s\n", ob->id.name+2); /* handle proxy copy for target */ @@ -2158,6 +2296,9 @@ void object_handle_update(Object *ob) } else where_is_object(ob); +#ifndef DISABLE_PYTHON + if (G.f & G_DOSCRIPTLINKS) BPY_do_pyscript((ID *)ob, SCRIPT_OBJECTUPDATE); +#endif } if(ob->recalc & OB_RECALC_DATA) { @@ -2177,6 +2318,16 @@ void object_handle_update(Object *ob) else if(ob->type==OB_LATTICE) { lattice_calc_modifiers(ob); } + else if(ob->type==OB_CAMERA) { + Camera *cam = (Camera *)ob->data; + calc_ipo(cam->ipo, frame_to_float(G.scene->r.cfra)); + execute_ipo(&cam->id, cam->ipo); + } + else if(ob->type==OB_LAMP) { + Lamp *la = (Lamp *)ob->data; + calc_ipo(la->ipo, frame_to_float(G.scene->r.cfra)); + execute_ipo(&la->id, la->ipo); + } else if(ob->type==OB_ARMATURE) { /* this happens for reading old files and to match library armatures with poses */ if(ob->pose==NULL || (ob->pose->flag & POSE_RECALC)) @@ -2194,6 +2345,7 @@ void object_handle_update(Object *ob) if(ob->particlesystem.first) { ParticleSystem *tpsys, *psys; + DerivedMesh *dm; psys= ob->particlesystem.first; while(psys) { @@ -2210,7 +2362,21 @@ void object_handle_update(Object *ob) else psys= psys->next; } + + if(G.rendering && ob->transflag & OB_DUPLIPARTS) { + /* this is to make sure we get render level duplis in groups: + * the derivedmesh must be created before init_render_mesh, + * since object_duplilist does dupliparticles before that */ + dm = mesh_create_derived_render(ob, CD_MASK_BAREMESH|CD_MASK_MTFACE|CD_MASK_MCOL); + dm->release(dm); + + for(psys=ob->particlesystem.first; psys; psys=psys->next) + psys_get_modifier(ob, psys)->flag &= ~eParticleSystemFlag_psys_updated; + } } +#ifndef DISABLE_PYTHON + if (G.f & G_DOSCRIPTLINKS) BPY_do_pyscript((ID *)ob, SCRIPT_OBDATAUPDATE); +#endif } /* the no-group proxy case, we call update */ @@ -2230,3 +2396,78 @@ void object_handle_update(Object *ob) // printf("set proxy pointer for later group stuff %s\n", ob->id.name); } } + +float give_timeoffset(Object *ob) { + if ((ob->ipoflag & OB_OFFS_PARENTADD) && ob->parent) { + return ob->sf + give_timeoffset(ob->parent); + } else { + return ob->sf; + } +} + +int give_obdata_texspace(Object *ob, int **texflag, float **loc, float **size, float **rot) { + + if (ob->data==NULL) + return 0; + + switch (GS(((ID *)ob->data)->name)) { + case ID_ME: + { + Mesh *me= ob->data; + if (texflag) *texflag = &me->texflag; + if (loc) *loc = me->loc; + if (size) *size = me->size; + if (rot) *rot = me->rot; + break; + } + case ID_CU: + { + Curve *cu= ob->data; + if (texflag) *texflag = &cu->texflag; + if (loc) *loc = cu->loc; + if (size) *size = cu->size; + if (rot) *rot = cu->rot; + break; + } + case ID_MB: + { + MetaBall *mb= ob->data; + if (texflag) *texflag = &mb->texflag; + if (loc) *loc = mb->loc; + if (size) *size = mb->size; + if (rot) *rot = mb->rot; + break; + } + default: + return 0; + } + return 1; +} + +/* + * Test a bounding box for ray intersection + * assumes the ray is already local to the boundbox space + */ +int ray_hit_boundbox(struct BoundBox *bb, float ray_start[3], float ray_normal[3]) +{ + static int triangle_indexes[12][3] = {{0, 1, 2}, {0, 2, 3}, + {3, 2, 6}, {3, 6, 7}, + {1, 2, 6}, {1, 6, 5}, + {5, 6, 7}, {4, 5, 7}, + {0, 3, 7}, {0, 4, 7}, + {0, 1, 5}, {0, 4, 5}}; + int result = 0; + int i; + + for (i = 0; i < 12 && result == 0; i++) + { + float lambda; + int v1, v2, v3; + v1 = triangle_indexes[i][0]; + v2 = triangle_indexes[i][1]; + v3 = triangle_indexes[i][2]; + result = RayIntersectsTriangle(ray_start, ray_normal, bb->vec[v1], bb->vec[v2], bb->vec[v3], &lambda, NULL); + } + + return result; +} diff --git a/source/blender/blenkernel/intern/packedFile.c b/source/blender/blenkernel/intern/packedFile.c index ab67aa00fbd..22e4e8a8309 100644 --- a/source/blender/blenkernel/intern/packedFile.c +++ b/source/blender/blenkernel/intern/packedFile.c @@ -184,7 +184,7 @@ PackedFile * newPackedFile(char * filename) // convert relative filenames to absolute filenames strcpy(name, filename); - BLI_convertstringcode(name, G.sce, G.scene->r.cfra); + BLI_convertstringcode(name, G.sce); // open the file // and create a PackedFile structure @@ -285,7 +285,7 @@ int writePackedFile(char * filename, PackedFile *pf, int guimode) if (guimode); //XXX waitcursor(1); strcpy(name, filename); - BLI_convertstringcode(name, G.sce, G.scene->r.cfra); + BLI_convertstringcode(name, G.sce); if (BLI_exists(name)) { for (number = 1; number <= 999; number++) { @@ -316,11 +316,11 @@ int writePackedFile(char * filename, PackedFile *pf, int guimode) if (remove_tmp) { if (ret_value == RET_ERROR) { - if (BLI_rename(tempname, name) == RET_ERROR) { + if (BLI_rename(tempname, name) != 0) { if(guimode); //XXX error("Error restoring tempfile. Check files: '%s' '%s'", tempname, name); } } else { - if (BLI_delete(tempname, 0, 0) == RET_ERROR) { + if (BLI_delete(tempname, 0, 0) != 0) { if(guimode); //XXX error("Error deleting '%s' (ignored)"); } } @@ -350,7 +350,7 @@ int checkPackedFile(char * filename, PackedFile * pf) char name[FILE_MAXDIR + FILE_MAXFILE]; strcpy(name, filename); - BLI_convertstringcode(name, G.sce, G.scene->r.cfra); + BLI_convertstringcode(name, G.sce); if (stat(name, &st)) { ret_val = PF_NOFILE; diff --git a/source/blender/blenkernel/intern/particle.c b/source/blender/blenkernel/intern/particle.c index be69a0daaf7..32da154321e 100644 --- a/source/blender/blenkernel/intern/particle.c +++ b/source/blender/blenkernel/intern/particle.c @@ -46,6 +46,7 @@ #include "DNA_object_types.h" #include "DNA_curve_types.h" #include "DNA_key_types.h" +#include "DNA_ipo_types.h" #include "BLI_arithb.h" #include "BLI_blenlib.h" @@ -74,6 +75,7 @@ #include "BKE_modifier.h" #include "BKE_mesh.h" #include "BKE_cdderivedmesh.h" +#include "BKE_pointcache.h" #include "RE_render_ext.h" @@ -149,6 +151,49 @@ char *psys_menu_string(Object *ob, int for_sb) return str; } + +/* we allocate path cache memory in chunks instead of a big continguous + * chunk, windows' memory allocater fails to find big blocks of memory often */ + +#define PATH_CACHE_BUF_SIZE 1024 + +static ParticleCacheKey **psys_alloc_path_cache_buffers(ListBase *bufs, int tot, int steps) +{ + LinkData *buf; + ParticleCacheKey **cache; + int i, totkey, totbufkey; + + tot= MAX2(tot, 1); + totkey = 0; + cache = MEM_callocN(tot*sizeof(void*), "PathCacheArray"); + + while(totkey < tot) { + totbufkey= MIN2(tot-totkey, PATH_CACHE_BUF_SIZE); + buf= MEM_callocN(sizeof(LinkData), "PathCacheLinkData"); + buf->data= MEM_callocN(sizeof(ParticleCacheKey)*totbufkey*steps, "ParticleCacheKey"); + + for(i=0; i<totbufkey; i++) + cache[totkey+i] = ((ParticleCacheKey*)buf->data) + i*steps; + + totkey += totbufkey; + BLI_addtail(bufs, buf); + } + + return cache; +} + +static void psys_free_path_cache_buffers(ParticleCacheKey **cache, ListBase *bufs) +{ + LinkData *buf; + + if(cache) + MEM_freeN(cache); + + for(buf= bufs->first; buf; buf=buf->next) + MEM_freeN(buf->data); + BLI_freelistN(bufs); +} + /************************************************/ /* Getting stuff */ /************************************************/ @@ -199,7 +244,7 @@ Object *psys_get_lattice(Object *ob, ParticleSystem *psys) { Object *lattice=0; - if(!psys_in_edit_mode(psys)==0){ + if(psys_in_edit_mode(psys)==0){ ModifierData *md = (ModifierData*)psys_get_modifier(ob,psys); @@ -221,14 +266,14 @@ void psys_disable_all(Object *ob) ParticleSystem *psys=ob->particlesystem.first; for(; psys; psys=psys->next) - psys->flag &= ~PSYS_ENABLED; + psys->flag |= PSYS_DISABLED; } void psys_enable_all(Object *ob) { ParticleSystem *psys=ob->particlesystem.first; for(; psys; psys=psys->next) - psys->flag |= PSYS_ENABLED; + psys->flag &= ~PSYS_DISABLED; } int psys_ob_has_hair(Object *ob) { @@ -247,10 +292,17 @@ int psys_in_edit_mode(ParticleSystem *psys) int psys_check_enabled(Object *ob, ParticleSystem *psys) { ParticleSystemModifierData *psmd; + Mesh *me; - if(!(psys->flag & PSYS_ENABLED)) + if(psys->flag & PSYS_DISABLED || psys->flag & PSYS_DELETE) return 0; + if(ob->type == OB_MESH) { + me= (Mesh*)ob->data; + if(me->mr && me->mr->current != 1) + return 0; + } + psmd= psys_get_modifier(ob, psys); if(psys->renderdata) { if(!(psmd->modifier.mode & eModifierMode_Render)) @@ -267,10 +319,17 @@ int psys_check_enabled(Object *ob, ParticleSystem *psys) /************************************************/ void psys_free_settings(ParticleSettings *part) { - if(part->pd) + if(part->pd) { MEM_freeN(part->pd); + part->pd = NULL; + } + if(part->pd2) { + MEM_freeN(part->pd2); + part->pd2 = NULL; + } } -void free_hair(ParticleSystem *psys) + +void free_hair(ParticleSystem *psys, int softbody) { ParticleData *pa; int i, totpart=psys->totpart; @@ -282,6 +341,11 @@ void free_hair(ParticleSystem *psys) } psys->flag &= ~PSYS_HAIR_DONE; + + if(softbody && psys->soft) { + sbFree(psys->soft); + psys->soft = NULL; + } } void free_keyed_keys(ParticleSystem *psys) { @@ -290,39 +354,41 @@ 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]); - - MEM_freeN(psys->childcache); - - psys->childcache = NULL; - psys->totchildcache = 0; - } + psys_free_path_cache_buffers(psys->childcache, &psys->childcachebufs); + psys->childcache = NULL; + psys->totchildcache = 0; } void psys_free_path_cache(ParticleSystem *psys) { - if(psys->pathcache){ - if(psys->pathcache[0]) - MEM_freeN(psys->pathcache[0]); - - MEM_freeN(psys->pathcache); + psys_free_path_cache_buffers(psys->pathcache, &psys->pathcachebufs); + psys->pathcache= NULL; + psys->totcached= 0; - psys->pathcache = NULL; - psys->totcached = 0; + free_child_path_cache(psys); +} +void psys_free_children(ParticleSystem *psys) +{ + if(psys->child) { + MEM_freeN(psys->child); + psys->child=0; + psys->totchild=0; } + free_child_path_cache(psys); } /* free everything */ void psys_free(Object *ob, ParticleSystem * psys) -{ +{ if(psys){ + int nr = 0; + ParticleSystem * tpsys; + if(ob->particlesystem.first == NULL && G.f & G_PARTICLEEDIT) G.f &= ~G_PARTICLEEDIT; psys_free_path_cache(psys); - free_hair(psys); + free_hair(psys, 1); free_keyed_keys(psys); @@ -342,17 +408,33 @@ void psys_free(Object *ob, ParticleSystem * psys) if(psys->effectors.first) psys_end_effectors(psys); + + // check if we are last non-visible particle system + for(tpsys=ob->particlesystem.first; tpsys; tpsys=tpsys->next){ + if(tpsys->part) + { + if(ELEM(tpsys->part->draw_as,PART_DRAW_OB,PART_DRAW_GR)) + { + nr++; + break; + } + } + } + // clear do-not-draw-flag + if(!nr) + ob->transflag &= ~OB_DUPLIPARTS; if(psys->part){ psys->part->id.us--; psys->part=0; } - if(psys->soft){ - sbFree(psys->soft); - psys->soft = 0; - } + if(psys->reactevents.first) + BLI_freelistN(&psys->reactevents); + if(psys->pointcache) + BKE_ptcache_free(psys->pointcache); + MEM_freeN(psys); } } @@ -362,7 +444,7 @@ void psys_free(Object *ob, ParticleSystem * psys) * removing the previous data. this should be solved properly once */ typedef struct ParticleRenderElem { - int curchild, totchild; + int curchild, totchild, reduce; float lambda, t, scalemin, scalemax; } ParticleRenderElem; @@ -379,6 +461,7 @@ typedef struct ParticleRenderData { int winx, winy; int dosimplify; + int timeoffset; ParticleRenderElem *elems; int *origindex; } ParticleRenderData; @@ -391,7 +474,7 @@ static float psys_render_viewport_falloff(double rate, float dist, float 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; + float co[4], view[3], ortho1[3], ortho2[3], w, dx, dy, radius; /* transform to view space */ VECCOPY(co, center); @@ -440,13 +523,15 @@ static float psys_render_projected_area(ParticleSystem *psys, float *center, flo return area; } -void psys_render_set(Object *ob, ParticleSystem *psys, float viewmat[][4], float winmat[][4], int winx, int winy) +void psys_render_set(Object *ob, ParticleSystem *psys, float viewmat[][4], float winmat[][4], int winx, int winy, int timeoffset) { ParticleRenderData*data; ParticleSystemModifierData *psmd= psys_get_modifier(ob, psys); if(!G.rendering) return; + if(psys->renderdata) + return; data= MEM_callocN(sizeof(ParticleRenderData), "ParticleRenderData"); @@ -474,6 +559,8 @@ void psys_render_set(Object *ob, ParticleSystem *psys, float viewmat[][4], float data->winx= winx; data->winy= winy; + data->timeoffset= timeoffset; + psys->renderdata= data; } @@ -515,8 +602,8 @@ void psys_render_restore(Object *ob, ParticleSystem *psys) psmd->totdmface= data->totdmface; psmd->flag &= ~eParticleSystemFlag_psys_updated; - if(psys->part->from==PART_FROM_FACE && psmd->dm) - psys_calc_dmfaces(ob, psmd->dm, psys); + if(psmd->dm) + psys_calc_dmcache(ob, psmd->dm, psys); MEM_freeN(data); psys->renderdata= NULL; @@ -531,7 +618,7 @@ int psys_render_simplify_distribution(ParticleThreadContext *ctx, int tot) ParticleRenderData *data; ParticleRenderElem *elems, *elem; ParticleSettings *part= ctx->psys->part; - float *facearea, (*facecenter)[3], size[3], fac, powrate; + float *facearea, (*facecenter)[3], size[3], fac, powrate, scaleclamp; float co1[3], co2[3], co3[3], co4[3], lambda, arearatio, t, area, viewport; double vprate; int *origindex, *facetotvert; @@ -539,7 +626,13 @@ int psys_render_simplify_distribution(ParticleThreadContext *ctx, int tot) 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)) + if(!ctx->psys->renderdata) + return tot; + + data= ctx->psys->renderdata; + if(data->timeoffset) + return 0; + if(!(part->simplify_flag & PART_SIMPLIFY_ENABLE)) return tot; mvert= dm->getVertArray(dm); @@ -556,7 +649,9 @@ int psys_render_simplify_distribution(ParticleThreadContext *ctx, int tot) facetotvert= MEM_callocN(sizeof(int)*totorigface, "SimplifyFaceArea"); elems= MEM_callocN(sizeof(ParticleRenderElem)*totorigface, "SimplifyFaceElem"); - data= ctx->psys->renderdata; + if(data->elems) + MEM_freeN(data->elems); + data->dosimplify= 1; data->elems= elems; data->origindex= origindex; @@ -614,19 +709,30 @@ int psys_render_simplify_distribution(ParticleThreadContext *ctx, int tot) 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) { + if((arearatio < 1.0f || viewport < 1.0f) && elem->totchild) { /* lambda is percentage of elements to keep */ lambda= (arearatio < 1.0f)? pow(arearatio, powrate): 1.0f; lambda *= viewport; + lambda= MAX2(lambda, 1.0f/elem->totchild); + /* compute transition region */ t= part->simplify_transition; elem->t= (lambda-t < 0.0f)? lambda: (lambda+t > 1.0f)? 1.0f-lambda: t; + elem->reduce= 1; /* 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); + elem->scalemin= sqrt(elem->scalemin); + elem->scalemax= sqrt(elem->scalemax); + + /* clamp scaling */ + scaleclamp= MIN2(elem->totchild, 10.0f); + elem->scalemin= MIN2(scaleclamp, elem->scalemin); + elem->scalemax= MIN2(scaleclamp, elem->scalemax); + /* extend lambda to include transition */ lambda= lambda + elem->t; if(lambda > 1.0f) @@ -637,6 +743,7 @@ int psys_render_simplify_distribution(ParticleThreadContext *ctx, int tot) elem->scalemax= 1.0f; //sqrt(lambda); elem->scalemin= 1.0f; //sqrt(lambda); + elem->reduce= 0; } elem->lambda= lambda; @@ -698,7 +805,7 @@ int psys_render_simplify_params(ParticleSystem *psys, ChildParticle *cpa, float scalemin= elem->scalemin; scalemax= elem->scalemax; - if(lambda >= 1.0f) { + if(!elem->reduce) { scale= scalemin; alpha= 1.0f; } @@ -746,7 +853,7 @@ static void weighted_particle_vector(float *v1, float *v2, float *v3, float *v4, vec[1]= weights[0]*v1[1] + weights[1]*v2[1] + weights[2]*v3[1] + weights[3]*v4[1]; vec[2]= weights[0]*v1[2] + weights[1]*v2[2] + weights[2]*v3[2] + weights[3]*v4[2]; } -static void interpolate_particle(short type, ParticleKey keys[4], float dt, ParticleKey *result) +static void interpolate_particle(short type, ParticleKey keys[4], float dt, ParticleKey *result, int velocity) { float t[4]; @@ -758,18 +865,20 @@ static void interpolate_particle(short type, ParticleKey keys[4], float dt, Part weighted_particle_vector(keys[0].co, keys[1].co, keys[2].co, keys[3].co, t, result->co); - //if(ve){ - // if(dt>0.999f){ - // set_four_ipo(dt+0.001f,t,ipo_type); - // weighted_particle_vector(key0->co,key1->co,key2->co,key3->co,t,temp); - // VECSUB(ve,temp,co); - // } - // else{ - // set_four_ipo(dt-0.001f,t,ipo_type); - // weighted_particle_vector(key0->co,key1->co,key2->co,key3->co,t,temp); - // VECSUB(ve,co,temp); - // } - //} + if(velocity){ + float temp[3]; + + if(dt>0.999f){ + set_four_ipo(dt-0.001f, t, type); + weighted_particle_vector(keys[0].co, keys[1].co, keys[2].co, keys[3].co, t, temp); + VECSUB(result->vel, result->co, temp); + } + else{ + set_four_ipo(dt+0.001f, t, type); + weighted_particle_vector(keys[0].co, keys[1].co, keys[2].co, keys[3].co, t, temp); + VECSUB(result->vel, temp, result->co); + } + } } } @@ -910,7 +1019,8 @@ void psys_interpolate_face(MVert *mvert, MFace *mface, MTFace *tface, float (*or } } } -void psys_interpolate_uvs(MTFace *tface, int quad, float *w, float *uvco){ +void psys_interpolate_uvs(MTFace *tface, int quad, float *w, float *uvco) +{ float v10= tface->uv[0][0]; float v11= tface->uv[0][1]; float v20= tface->uv[1][0]; @@ -931,9 +1041,35 @@ void psys_interpolate_uvs(MTFace *tface, int quad, float *w, float *uvco){ uvco[1]= w[0]*v11 + w[1]*v21 + w[2]*v31; } } + +void psys_interpolate_mcol(MCol *mcol, int quad, float *w, MCol *mc) +{ + char *cp, *cp1, *cp2, *cp3, *cp4; + + cp= (char *)mc; + cp1= (char *)&mcol[0]; + cp2= (char *)&mcol[1]; + cp3= (char *)&mcol[2]; + + if(quad) { + cp4= (char *)&mcol[3]; + + cp[0]= (int)(w[0]*cp1[0] + w[1]*cp2[0] + w[2]*cp3[0] + w[3]*cp4[0]); + cp[1]= (int)(w[0]*cp1[1] + w[1]*cp2[1] + w[2]*cp3[1] + w[3]*cp4[1]); + cp[2]= (int)(w[0]*cp1[2] + w[1]*cp2[2] + w[2]*cp3[2] + w[3]*cp4[2]); + cp[3]= (int)(w[0]*cp1[3] + w[1]*cp2[3] + w[2]*cp3[3] + w[3]*cp4[3]); + } + else { + cp[0]= (int)(w[0]*cp1[0] + w[1]*cp2[0] + w[2]*cp3[0]); + cp[1]= (int)(w[0]*cp1[1] + w[1]*cp2[1] + w[2]*cp3[1]); + cp[2]= (int)(w[0]*cp1[2] + w[1]*cp2[2] + w[2]*cp3[2]); + cp[3]= (int)(w[0]*cp1[3] + w[1]*cp2[3] + w[2]*cp3[3]); + } +} + float psys_interpolate_value_from_verts(DerivedMesh *dm, short from, int index, float *fw, float *values) { - if(values==0) + if(values==0 || index==-1) return 0.0; switch(from){ @@ -979,8 +1115,8 @@ static void psys_origspace_to_w(OrigSpaceFace *osface, int quad, float *w, float } } -/* find the derived mesh face for a particle, set the mf passed. -This is slow, can be optimized but only for many lookups, return the face lookup index*/ +/* find the derived mesh face for a particle, set the mf passed. this is slow + * and can be optimized but only for many lookups. returns the face index. */ int psys_particle_dm_face_lookup(Object *ob, DerivedMesh *dm, int index, float *fw, struct LinkNode *node) { Mesh *me= (Mesh*)ob->data; @@ -1013,7 +1149,7 @@ int psys_particle_dm_face_lookup(Object *ob, DerivedMesh *dm, int index, float * if(node) { /* we have a linked list of faces that we use, faster! */ for(;node; node=node->next) { - findex= (int)node->link; + findex= GET_INT_FROM_POINTER(node->link); faceuv= osface[findex].uv; quad= mface[findex].v4; @@ -1048,131 +1184,142 @@ int psys_particle_dm_face_lookup(Object *ob, DerivedMesh *dm, int index, float * return DMCACHE_NOTFOUND; } -/* interprets particle data to get a point on a mesh in object space */ -#define PARTICLE_ERROR(_nor, _vec) _vec[0]=_vec[1]=_vec[2]=0.0; if(_nor){ _nor[0]=_nor[1]=0.0; _nor[2]=1.0; } -void psys_particle_on_dm(Object *ob, DerivedMesh *dm, int from, int index, int index_dmcache, float *fw, float foffset, float *vec, float *nor, float *utan, float *vtan, float *orco, float *ornor) +static int psys_map_index_on_dm(DerivedMesh *dm, int from, int index, int index_dmcache, float *fw, float foffset, int *mapindex, float *mapfw) { - float (*orcodata)[3]; - - if(index < 0){ /* 'no dm' error has happened! */ - PARTICLE_ERROR(nor, vec); - return; - } - orcodata= dm->getVertDataArray(dm, CD_ORCO); + if(index < 0) + return 0; if (dm->deformedOnly || index_dmcache == DMCACHE_ISCHILD) { - /* this works for meshes with deform verts only - constructive modifiers wont work properly*/ - float temp1[3]; - + /* for meshes that are either only defined or for child particles, the + * index and fw do not require any mapping, so we can directly use it */ if(from == PART_FROM_VERT) { - if(index >= dm->getNumVerts(dm)) { - PARTICLE_ERROR(nor, vec); - return; - } - - dm->getVertCo(dm,index,vec); - if(nor){ - dm->getVertNo(dm,index,nor); - Normalize(nor); - } - if(orco) - VECCOPY(orco, orcodata[index]) - if(ornor) { - dm->getVertNo(dm,index,nor); - Normalize(nor); - } - } - else { /* PART_FROM_FACE / PART_FROM_VOLUME */ - MFace *mface; - MTFace *mtface=0; - MVert *mvert; - int uv_index; - - if(index >= dm->getNumFaces(dm)) { - PARTICLE_ERROR(nor, vec); - return; - } - - mface=dm->getFaceData(dm,index,CD_MFACE); - mvert=dm->getVertDataArray(dm,CD_MVERT); - uv_index=CustomData_get_active_layer_index(&dm->faceData,CD_MTFACE); + if(index >= dm->getNumVerts(dm)) + return 0; - if(uv_index>=0){ - CustomDataLayer *layer=&dm->faceData.layers[uv_index]; - mtface= &((MTFace*)layer->data)[index]; - } + *mapindex = index; + } + else { /* FROM_FACE/FROM_VOLUME */ + if(index >= dm->getNumFaces(dm)) + return 0; - if(from==PART_FROM_VOLUME){ - psys_interpolate_face(mvert,mface,mtface,orcodata,fw,vec,temp1,utan,vtan,orco,ornor); - if(nor) - VECCOPY(nor,temp1); - Normalize(temp1); - VecMulf(temp1,-foffset); - VECADD(vec,vec,temp1); - } - else - psys_interpolate_face(mvert,mface,mtface,orcodata,fw,vec,nor,utan,vtan,orco,ornor); + *mapindex = index; + QUATCOPY(mapfw, fw); } } else { - /* Need to support constructive modifiers, this is a bit more tricky - we need a customdata layer like UV's so we can position the particle */ - - /* Only face supported at the moment */ - if (from==PART_FROM_FACE) { + /* for other meshes that have been modified, we try to map the particle + * to their new location, which means a different index, and for faces + * also a new face interpolation weights */ + if(from == PART_FROM_VERT) { + if (index_dmcache == DMCACHE_NOTFOUND || index_dmcache > dm->getNumVerts(dm)) + return 0; + + *mapindex = index_dmcache; + } + else { /* FROM_FACE/FROM_VOLUME */ /* find a face on the derived mesh that uses this face */ - Mesh *me= (Mesh*)ob->data; - MVert *mvert; MFace *mface; - MTFace *mtface; OrigSpaceFace *osface; - int *origindex; - float fw_mod[4]; - int i, totface; - - mvert= dm->getVertDataArray(dm,CD_MVERT); + int i; + + i = index_dmcache; + + if(i== DMCACHE_NOTFOUND || i >= dm->getNumFaces(dm)) + return 0; + + *mapindex = i; + /* modify the original weights to become + * weights for the derived mesh face */ osface= dm->getFaceDataArray(dm, CD_ORIGSPACE); - origindex= dm->getFaceDataArray(dm, CD_ORIGINDEX); + mface= dm->getFaceData(dm, i, CD_MFACE); - /* For this to work we need origindex and OrigSpace coords */ - if(origindex==NULL || osface==NULL || index>=me->totface) { - PARTICLE_ERROR(nor, vec); - return; - } - - if (index_dmcache == DMCACHE_NOTFOUND) - i = psys_particle_dm_face_lookup(ob, dm, index, fw, (LinkNode*)NULL); + if(osface == NULL) + mapfw[0]= mapfw[1]= mapfw[2]= mapfw[3]= 0.0f; else - i = index_dmcache; + psys_origspace_to_w(&osface[i], mface->v4, fw, mapfw); + } + } - totface = dm->getNumFaces(dm); + return 1; +} - /* Any time this happens, and the face has not been removed, - * its a BUG watch out for this error! */ - if (i==-1) { - printf("Cannot find original face %i\n", index); - PARTICLE_ERROR(nor, vec); - return; - } - else if(i >= totface) - return; +/* interprets particle data to get a point on a mesh in object space */ +void psys_particle_on_dm(DerivedMesh *dm, int from, int index, int index_dmcache, float *fw, float foffset, float *vec, float *nor, float *utan, float *vtan, float *orco, float *ornor) +{ + float tmpnor[3], mapfw[4]; + float (*orcodata)[3]; + int mapindex; - mface= dm->getFaceData(dm, i, CD_MFACE); - mtface= dm->getFaceData(dm, i, CD_MTFACE); - osface += i; + if(!psys_map_index_on_dm(dm, from, index, index_dmcache, fw, foffset, &mapindex, mapfw)) { + if(vec) { vec[0]=vec[1]=vec[2]=0.0; } + if(nor) { nor[0]=nor[1]=0.0; nor[2]=1.0; } + if(orco) { orco[0]=orco[1]=orco[2]=0.0; } + if(ornor) { ornor[0]=ornor[1]=0.0; ornor[2]=1.0; } + if(utan) { utan[0]=utan[1]=utan[2]=0.0; } + if(vtan) { vtan[0]=vtan[1]=vtan[2]=0.0; } - /* we need to modify the original weights to become weights for - * the derived mesh face */ - psys_origspace_to_w(osface, mface->v4, fw, fw_mod); - psys_interpolate_face(mvert,mface,mtface,orcodata,fw_mod,vec,nor,utan,vtan,orco,ornor); + return; + } + + orcodata= dm->getVertDataArray(dm, CD_ORCO); + + if(from == PART_FROM_VERT) { + dm->getVertCo(dm,mapindex,vec); + + if(nor) { + dm->getVertNo(dm,mapindex,nor); + Normalize(nor); } - else { - /* TODO PARTICLE - support verts and volume */ - PARTICLE_ERROR(nor, vec); + + if(orco) + VECCOPY(orco, orcodata[mapindex]) + + if(ornor) { + dm->getVertNo(dm,mapindex,nor); + Normalize(nor); } + + if(utan && vtan) { + utan[0]= utan[1]= utan[2]= 0.0f; + vtan[0]= vtan[1]= vtan[2]= 0.0f; + } + } + else { /* PART_FROM_FACE / PART_FROM_VOLUME */ + MFace *mface; + MTFace *mtface; + MVert *mvert; + + mface=dm->getFaceData(dm,mapindex,CD_MFACE); + mvert=dm->getVertDataArray(dm,CD_MVERT); + mtface=CustomData_get_layer(&dm->faceData,CD_MTFACE); + + if(mtface) + mtface += mapindex; + + if(from==PART_FROM_VOLUME) { + psys_interpolate_face(mvert,mface,mtface,orcodata,mapfw,vec,tmpnor,utan,vtan,orco,ornor); + if(nor) + VECCOPY(nor,tmpnor); + + Normalize(tmpnor); + VecMulf(tmpnor,-foffset); + VECADD(vec,vec,tmpnor); + } + else + psys_interpolate_face(mvert,mface,mtface,orcodata,mapfw,vec,nor,utan,vtan,orco,ornor); } } -#undef PARTICLE_ERROR + +float psys_particle_value_from_verts(DerivedMesh *dm, short from, ParticleData *pa, float *values) +{ + float mapfw[4]; + int mapindex; + + if(!psys_map_index_on_dm(dm, from, pa->num, pa->num_dmcache, pa->fuv, pa->foffset, &mapindex, mapfw)) + return 0.0f; + + return psys_interpolate_value_from_verts(dm, from, mapindex, mapfw, values); +} ParticleSystemModifierData *psys_get_modifier(Object *ob, ParticleSystem *psys) { @@ -1193,7 +1340,7 @@ ParticleSystemModifierData *psys_get_modifier(Object *ob, ParticleSystem *psys) /* Particles on a shape */ /************************************************/ /* ready for future use */ -void psys_particle_on_shape(int distr, int index, float *fuv, float *vec, float *nor, float *utan, float *vtan, float *orco, float *ornor) +static void psys_particle_on_shape(int distr, int index, float *fuv, float *vec, float *nor, float *utan, float *vtan, float *orco, float *ornor) { /* TODO */ float zerovec[3]={0.0f,0.0f,0.0f}; @@ -1219,16 +1366,16 @@ void psys_particle_on_shape(int distr, int index, float *fuv, float *vec, float /************************************************/ /* Particles on emitter */ /************************************************/ -void psys_particle_on_emitter(Object *ob, ParticleSystemModifierData *psmd, int from, int index, int index_dmcache, float *fuv, float foffset, float *vec, float *nor, float *utan, float *vtan, float *orco, float *ornor){ +void psys_particle_on_emitter(ParticleSystemModifierData *psmd, int from, int index, int index_dmcache, float *fuv, float foffset, float *vec, float *nor, float *utan, float *vtan, float *orco, float *ornor){ if(psmd){ - if(psmd->psys->part->distr==PART_DISTR_GRID){ + if(psmd->psys->part->distr==PART_DISTR_GRID && psmd->psys->part->from != PART_FROM_VERT){ if(vec){ VECCOPY(vec,fuv); } return; } /* we cant use the num_dmcache */ - psys_particle_on_dm(ob, psmd->dm,from,index,index_dmcache,fuv,foffset,vec,nor,utan,vtan,orco,ornor); + psys_particle_on_dm(psmd->dm,from,index,index_dmcache,fuv,foffset,vec,nor,utan,vtan,orco,ornor); } else psys_particle_on_shape(from,index,fuv,vec,nor,utan,vtan,orco,ornor); @@ -1263,7 +1410,7 @@ static float vert_weight(MDeformVert *dvert, int group) } static void do_prekink(ParticleKey *state, ParticleKey *par, float *par_rot, float time, float freq, float shape, float amplitude, short type, short axis, float obmat[][4]) { - float vec[3]={0.0,0.0,0.0}, q1[4]={1,0,0,0},*q2; + float vec[3]={0.0,0.0,0.0}, q1[4]={1,0,0,0},q2[4]; float t; CLAMP(time,0.0,1.0); @@ -1285,10 +1432,9 @@ static void do_prekink(ParticleKey *state, ParticleKey *par, float *par_rot, flo case PART_KINK_CURL: vec[axis]=1.0; if(par_rot) - q2=par_rot; - else{ - q2=vectoquat(par->vel,axis,(axis+1)%3); - } + QUATCOPY(q2,par_rot) + else + vectoquat(par->vel,axis,(axis+1)%3, q2); QuatMulVecf(q2,vec); VecMulf(vec,amplitude); VECADD(state->co,state->co,vec); @@ -1335,9 +1481,9 @@ static void do_prekink(ParticleKey *state, ParticleKey *par, float *par_rot, flo float inp_y,inp_z,length; if(par_rot) - q2=par_rot; + QUATCOPY(q2,par_rot) else - q2=vectoquat(par->vel,axis,(axis+1)%3); + vectoquat(par->vel,axis,(axis+1)%3,q2); QuatMulVecf(q2,y_vec); QuatMulVecf(q2,z_vec); @@ -1394,59 +1540,6 @@ static void do_prekink(ParticleKey *state, ParticleKey *par, float *par_rot, flo } } break; - //case PART_KINK_ROT: - // vec[axis]=1.0; - - // QuatMulVecf(par->rot,vec); - - // VecMulf(vec,amplitude*(float)sin(t)); - - // VECADD(state->co,state->co,vec); - // break; - } -} -static void do_postkink(ParticleKey *state, ParticleKey *par, float *par_rot, float time, float freq, float shape, float amplitude, short type, short axis, float obmat[][4]) -{ - static ParticleKey first; - static float q[4]; - float vec[3]={0.0,0.0,0.0}; - float t; - - CLAMP(time,0.0,1.0); - - t=time; - - t*=(float)M_PI*freq; - - if(par==0) return; - - switch(type){ - case PART_KINK_ROLL: - if(time<(0.5+shape/2.0f)){ - float *q2; - memcpy(&first,state,sizeof(ParticleKey)); - Normalize(first.vel); - if(par_rot) - q2=par_rot; - else - q2=vectoquat(par->vel,axis,(axis+1)%3); - QUATCOPY(q,q2); - } - else{ - float fac; - shape=0.5f+shape/2.0f; - t-=(float)M_PI*(shape*freq + 0.5f); - - vec[axis]=1.0; - - QuatMulVecf(q,vec); - - fac=amplitude*(1.0f+((1.0f-time)/(1.0f-shape)*(float)sin(t))); - VECADDFAC(state->co,first.co,vec,fac); - fac=amplitude*((1.0f-time)/(1.0f-shape)*(float)cos(t)); - VECADDFAC(state->co,state->co,first.vel,fac); - } - break; } } static void do_clump(ParticleKey *state, ParticleKey *par, float time, float clumpfac, float clumppow, float pa_clump) @@ -1539,7 +1632,7 @@ int do_guide(ParticleKey *state, int pa_num, float time, ListBase *lb) VecRotToQuat(guidedir,guidevec[3]-ec->firstloc[3],rot2); QuatMulVecf(rot2,pa_loc); - //q=vectoquat(guidedir, pd->kink_axis, (pd->kink_axis+1)%3); + //vectoquat(guidedir, pd->kink_axis, (pd->kink_axis+1)%3, q); //QuatMul(par.rot,rot2,q); } //else{ @@ -1553,13 +1646,11 @@ int do_guide(ParticleKey *state, int pa_num, float time, ListBase *lb) /* TODO */ //else{ ///* curve size*/ - // calc_curve_subdiv_radius(cu,cu->nurb.first,((Nurb*)cu->nurb.first)-> //} par.co[0]=par.co[1]=par.co[2]=0.0f; VECCOPY(key.co,pa_loc); do_prekink(&key, &par, 0, guidetime, pd->kink_freq, pd->kink_shape, pd->kink_amp, pd->kink, pd->kink_axis, 0); do_clump(&key, &par, guidetime, pd->clump_fac, pd->clump_pow, 1.0f); - do_postkink(&key, &par, 0, guidetime, pd->kink_freq, pd->kink_shape, pd->kink_amp, pd->kink, pd->kink_axis, 0); VECCOPY(pa_loc,key.co); VECADD(pa_loc,pa_loc,guidevec); @@ -1626,40 +1717,46 @@ static void do_rough_end(float *loc, float t, float fac, float shape, ParticleKe VECADD(state->co,state->co,rough); } +static void do_path_effectors(Object *ob, ParticleSystem *psys, int i, ParticleCacheKey *ca, int k, int steps, float *rootco, float effector, float dfra, float cfra, float *length, float *vec) +{ + float force[3] = {0.0f,0.0f,0.0f}, vel[3] = {0.0f,0.0f,0.0f}; + ParticleKey eff_key; + ParticleData *pa; + + VECCOPY(eff_key.co,(ca-1)->co); + VECCOPY(eff_key.vel,(ca-1)->vel); + QUATCOPY(eff_key.rot,(ca-1)->rot); + + pa= psys->particles+i; + do_effectors(i, pa, &eff_key, ob, psys, rootco, force, vel, dfra, cfra); + + VecMulf(force, effector*pow((float)k / (float)steps, 100.0f * psys->part->eff_hair) / (float)steps); + + VecAddf(force, force, vec); + + Normalize(force); + + if(k < steps) { + VecSubf(vec, (ca+1)->co, ca->co); + *length = VecLength(vec); + } + + VECADDFAC(ca->co, (ca-1)->co, force, *length); +} static int check_path_length(int k, ParticleCacheKey *keys, ParticleCacheKey *state, float max_length, float *cur_length, float length, float *dvec) { if(*cur_length + length > max_length){ - //if(p<totparent){ - // if(k<=(int)cache[totpart+p]->time){ - // /* parents need to be calculated fully first so that they don't mess up their children */ - // /* we'll make a note of where we got to though so that they're easy to finish later */ - // state->time=(max_length-*cur_length)/length; - // cache[totpart+p]->time=(float)k; - // } - //} - //else{ VecMulf(dvec, (max_length - *cur_length) / length); VECADD(state->co, (state - 1)->co, dvec); keys->steps = k; /* something over the maximum step value */ return k=100000; - //} } else { *cur_length+=length; return k; } } -static void finalize_path_length(ParticleCacheKey *keys) -{ - ParticleCacheKey *state = keys; - float dvec[3]; - state += state->steps; - - VECSUB(dvec, state->co, (state - 1)->co); - VecMulf(dvec, state->steps); - VECADD(state->co, (state - 1)->co, dvec); -} static void offset_child(ChildParticle *cpa, ParticleKey *par, ParticleKey *child, float flat, float radius) { VECCOPY(child->co,cpa->fuv); @@ -1709,14 +1806,14 @@ void psys_find_parents(Object *ob, ParticleSystemModifierData *psmd, ParticleSys tree=BLI_kdtree_new(totparent); for(p=0,cpa=psys->child; p<totparent; p++,cpa++){ - psys_particle_on_emitter(ob,psmd,from,cpa->num,-1,cpa->fuv,cpa->foffset,co,0,0,0,orco,0); + psys_particle_on_emitter(psmd,from,cpa->num,-1,cpa->fuv,cpa->foffset,co,0,0,0,orco,0); BLI_kdtree_insert(tree, p, orco, NULL); } BLI_kdtree_balance(tree); for(; p<totchild; p++,cpa++){ - psys_particle_on_emitter(ob,psmd,from,cpa->num,DMCACHE_ISCHILD,cpa->fuv,cpa->foffset,co,0,0,0,orco,0); + psys_particle_on_emitter(psmd,from,cpa->num,DMCACHE_ISCHILD,cpa->fuv,cpa->foffset,co,0,0,0,orco,0); cpa->parent=BLI_kdtree_find_nearest(tree, orco, NULL, NULL); } @@ -1817,6 +1914,8 @@ int psys_threads_init_path(ParticleThread *threads, float cfra, int editupdate) 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 */ @@ -1838,14 +1937,15 @@ void psys_thread_create_path(ParticleThread *thread, struct ChildParticle *cpa, ParticleCacheKey **cache= psys->childcache; ParticleCacheKey **pcache= psys->pathcache; ParticleCacheKey *state, *par = NULL, *key[4]; - ParticleData *pa; + ParticleData *pa=NULL; ParticleTexture ptex; float *cpa_fuv=0; float co[3], orco[3], ornor[3], t, rough_t, cpa_1st[3], dvec[3]; float branch_begin, branch_end, branch_prob, branchfac, rough_rand; float pa_rough1, pa_rough2, pa_roughe; - float length, pa_length, pa_clump, pa_kink; + float length, pa_length, pa_clump, pa_kink, pa_effector; float max_length = 1.0f, cur_length = 0.0f; + float eff_length, eff_vec[3]; int k, cpa_num, guided=0; short cpa_from; @@ -1905,7 +2005,7 @@ void psys_thread_create_path(ParticleThread *thread, struct ChildParticle *cpa, cpa_fuv = cpa->fuv; cpa_from = PART_FROM_FACE; - psys_particle_on_emitter(ob,ctx->psmd,cpa_from,cpa_num,DMCACHE_ISCHILD,cpa->fuv,foffset,co,ornor,0,0,orco,0); + psys_particle_on_emitter(ctx->psmd,cpa_from,cpa_num,DMCACHE_ISCHILD,cpa->fuv,foffset,co,ornor,0,0,orco,0); /* we need to save the actual root position of the child for positioning it accurately to the surface of the emitter */ VECCOPY(cpa_1st,co); @@ -1931,7 +2031,7 @@ void psys_thread_create_path(ParticleThread *thread, struct ChildParticle *cpa, cpa_num=pa->num; cpa_fuv=pa->fuv; - psys_particle_on_emitter(ob,ctx->psmd,cpa_from,cpa_num,DMCACHE_ISCHILD,cpa_fuv,pa->foffset,co,ornor,0,0,orco,0); + psys_particle_on_emitter(ctx->psmd,cpa_from,cpa_num,DMCACHE_ISCHILD,cpa_fuv,pa->foffset,co,ornor,0,0,orco,0); } keys->steps = ctx->steps; @@ -1958,6 +2058,7 @@ void psys_thread_create_path(ParticleThread *thread, struct ChildParticle *cpa, pa_rough1=ptex.rough; pa_rough2=ptex.rough; pa_roughe=ptex.rough; + pa_effector= 1.0f; if(ctx->vg_length) pa_length*=psys_interpolate_value_from_verts(ctx->dm,cpa_from,cpa_num,cpa_fuv,ctx->vg_length); @@ -1971,16 +2072,17 @@ void psys_thread_create_path(ParticleThread *thread, struct ChildParticle *cpa, pa_rough2*=psys_interpolate_value_from_verts(ctx->dm,cpa_from,cpa_num,cpa_fuv,ctx->vg_rough2); if(ctx->vg_roughe) pa_roughe*=psys_interpolate_value_from_verts(ctx->dm,cpa_from,cpa_num,cpa_fuv,ctx->vg_roughe); + if(ctx->vg_effector) + pa_effector*=psys_interpolate_value_from_verts(ctx->dm,cpa_from,cpa_num,cpa_fuv,ctx->vg_effector); /* create the child path */ for(k=0,state=keys; k<=ctx->steps; k++,state++){ - t=(float)k/(float)ctx->steps; - if(ctx->between){ int w=0; state->co[0] = state->co[1] = state->co[2] = 0.0f; state->vel[0] = state->vel[1] = state->vel[2] = 0.0f; + state->rot[0] = state->rot[1] = state->rot[2] = state->rot[3] = 0.0f; //QUATCOPY(state->rot,key[0]->rot); @@ -2009,6 +2111,23 @@ void psys_thread_create_path(ParticleThread *thread, struct ChildParticle *cpa, key[0]++; } + } + + /* apply effectors */ + if(part->flag & PART_CHILD_EFFECT) { + for(k=0,state=keys; k<=ctx->steps; k++,state++) { + if(k) { + do_path_effectors(ob, psys, cpa->pa[0], state, k, ctx->steps, keys->co, pa_effector, 0.0f, ctx->cfra, &eff_length, eff_vec); + } + else { + VecSubf(eff_vec,(state+1)->co,state->co); + eff_length= VecLength(eff_vec); + } + } + } + + for(k=0,state=keys; k<=ctx->steps; k++,state++){ + t=(float)k/(float)ctx->steps; if(ctx->totparent){ if(i>=ctx->totparent) @@ -2023,8 +2142,9 @@ void psys_thread_create_path(ParticleThread *thread, struct ChildParticle *cpa, } /* apply different deformations to the child path */ - if(part->flag & PART_CHILD_GUIDE) - guided = do_guide((ParticleKey*)state, i, t, &(psys->effectors)); //safe to cast, since only co and vel are used + if(part->flag & PART_CHILD_EFFECT) + /* state is safe to cast, since only co and vel are used */ + guided = do_guide((ParticleKey*)state, cpa->parent, t, &(psys->effectors)); if(guided==0){ if(part->kink) @@ -2032,10 +2152,6 @@ void psys_thread_create_path(ParticleThread *thread, struct ChildParticle *cpa, part->kink_freq * pa_kink, part->kink_shape, part->kink_amp, part->kink, part->kink_axis, ob->obmat); do_clump((ParticleKey*)state, (ParticleKey*)par, t, part->clumpfac, part->clumppow, pa_clump); - - if(part->kink) - do_postkink((ParticleKey*)state, (ParticleKey*)par, par->rot, t, - part->kink_freq * pa_kink, part->kink_shape, part->kink_amp, part->kink, part->kink_axis, ob->obmat); } if(part->flag & PART_BRANCHING && ctx->between == 0 && part->flag & PART_ANIM_BRANCHING) @@ -2044,7 +2160,7 @@ void psys_thread_create_path(ParticleThread *thread, struct ChildParticle *cpa, rough_t = t; if(part->rough1 != 0.0 && pa_rough1 != 0.0) - do_rough(orco, rough_t, pa_rough1*part->rough1, part->rough1_size, 0.0, (ParticleKey*)state); + do_rough(orco, rough_t, pa_rough1*part->rough1, part->rough1_size, 0.0, (ParticleKey*)state); if(part->rough2 != 0.0 && pa_rough2 != 0.0) do_rough(cpa->rand, rough_t, pa_rough2*part->rough2, part->rough2_size, part->rough2_thres, (ParticleKey*)state); @@ -2122,13 +2238,9 @@ void psys_thread_create_path(ParticleThread *thread, struct ChildParticle *cpa, get_strand_normal(ctx->ma, ornor, cur_length, state->vel); } } - - /* now let's finalise the interpolated parents that we might have left half done before */ - if(i<ctx->totparent) - finalize_path_length(keys); } -void *exec_child_path_cache(void *data) +static void *exec_child_path_cache(void *data) { ParticleThread *thread= (ParticleThread*)data; ParticleThreadContext *ctx= thread->ctx; @@ -2149,11 +2261,11 @@ void psys_cache_child_paths(Object *ob, ParticleSystem *psys, float cfra, int ed ParticleSettings *part = psys->part; ParticleThread *pthreads; ParticleThreadContext *ctx; - ParticleCacheKey **cache, *tcache; + ParticleCacheKey **cache; ListBase threads; int i, totchild, totparent, totthread; - pthreads= psys_threads_create(ob, psys, G.scene->r.threads); + pthreads= psys_threads_create(ob, psys); if(!psys_threads_init_path(pthreads, cfra, editupdate)) { psys_threads_free(pthreads); @@ -2170,12 +2282,7 @@ void psys_cache_child_paths(Object *ob, ParticleSystem *psys, float cfra, int ed else { /* clear out old and create new empty path cache */ free_child_path_cache(psys); - - cache = psys->childcache = MEM_callocN(totchild*sizeof(void *), "Child path cache array"); - tcache = MEM_callocN(totchild * (ctx->steps + 1) * sizeof(ParticleCacheKey), "Child path cache"); - for(i=0; i<totchild; i++) - cache[i] = tcache + i * (ctx->steps + 1); - + psys->childcache= psys_alloc_path_cache_buffers(&psys->childcachebufs, totchild, ctx->steps+1); psys->totchildcache = totchild; } @@ -2204,6 +2311,7 @@ void psys_cache_paths(Object *ob, ParticleSystem *psys, float cfra, int editupda ParticleCacheKey *ca, **cache=psys->pathcache; ParticleSystemModifierData *psmd = psys_get_modifier(ob, psys); ParticleEditSettings *pset = &G.scene->toolsettings->particle; + ParticleSettings *part = psys->part; ParticleData *pa; ParticleKey keys[4], result, *kkey[2] = {NULL, NULL}; @@ -2218,7 +2326,7 @@ void psys_cache_paths(Object *ob, ParticleSystem *psys, float cfra, int editupda Material *ma; float birthtime = 0.0, dietime = 0.0; - float t, time, keytime, dfra = 1.0, frs_sec = G.scene->r.frs_sec; + float t, time = 0.0, keytime = 0.0, dfra = 1.0, frs_sec = G.scene->r.frs_sec; float col[3] = {0.5f, 0.5f, 0.5f}; float prev_tangent[3], hairmat[4][4]; int k,i; @@ -2227,6 +2335,10 @@ void psys_cache_paths(Object *ob, ParticleSystem *psys, float cfra, int editupda char nosel[4], sel[4]; float sel_col[3]; float nosel_col[3]; + float length, vec[3]; + float *vg_effector= NULL, effector=0.0f; + float *vg_length= NULL, pa_length=1.0f, max_length=1.0f, cur_length=0.0f; + float len, dvec[3]; /* we don't have anything valid to create paths from so let's quit here */ if((psys->flag & PSYS_HAIR_DONE)==0 && (psys->flag & PSYS_KEYED)==0) @@ -2260,21 +2372,28 @@ void psys_cache_paths(Object *ob, ParticleSystem *psys, float cfra, int editupda else { /* clear out old and create new empty path cache */ psys_free_path_cache(psys); - - /* allocate cache array for fast access and set pointers to contiguous mem block */ - cache = psys->pathcache = MEM_callocN(MAX2(1, totpart) * sizeof(void *), "Path cache array"); - cache[0] = MEM_callocN(totpart * (steps + 1) * sizeof(ParticleCacheKey), "Path cache"); - for(i=1; i<totpart; i++) - cache[i] = cache[0] + i * (steps + 1); + cache= psys_alloc_path_cache_buffers(&psys->pathcachebufs, totpart, steps+1); + psys->pathcache= cache; } - if(edit==NULL && psys->soft && psys->softflag & OB_SB_ENABLE) + if(edit==NULL && psys->soft && psys->softflag & OB_SB_ENABLE) { soft = psys->soft; + if(!soft->bpoint) + soft= NULL; + } psys->lattice = psys_get_lattice(ob, psys); ma= give_current_material(ob, psys->part->omat); if(ma && (psys->part->draw & PART_DRAW_MAT_COL)) VECCOPY(col, &ma->r) + + if(psys->part->from!=PART_FROM_PARTICLE) { + if(!(psys->part->flag & PART_CHILD_EFFECT)) + vg_effector = psys_cache_vgroup(psmd->dm, psys, PSYS_VG_EFFECTOR); + + if(!edit && !psys->totchild) + vg_length = psys_cache_vgroup(psmd->dm, psys, PSYS_VG_LENGTH); + } /*---first main loop: create all actual particles' paths---*/ for(i=0,pa=psys->particles; i<totpart; i++, pa++){ @@ -2287,6 +2406,12 @@ void psys_cache_paths(Object *ob, ParticleSystem *psys, float cfra, int editupda if(editupdate && !(pa->flag & PARS_EDIT_RECALC)) continue; else memset(cache[i], 0, sizeof(*cache[i])*(steps+1)); + if(!edit && !psys->totchild) { + pa_length = part->length * (1.0f - part->randlength*pa->r_ave[0]); + if(vg_length) + pa_length *= psys_particle_value_from_verts(psmd->dm,part->from,pa,vg_length); + } + cache[i]->steps = steps; if(edit) @@ -2368,7 +2493,7 @@ void psys_cache_paths(Object *ob, ParticleSystem *psys, float cfra, int editupda if(soft) { if(hkey[1] != pa->hair + pa->totkey - 1) - bp_to_particle(keys + 3, bp[1], hkey[1] + 1); + bp_to_particle(keys + 3, bp[1] + 1, hkey[1] + 1); else bp_to_particle(keys + 3, bp[1], hkey[1]); } @@ -2393,8 +2518,7 @@ void psys_cache_paths(Object *ob, ParticleSystem *psys, float cfra, int editupda /* now we should have in chronologiacl order k1<=k2<=t<=k3<=k4 with keytime between [0,1]->[k2,k3] (k1 & k4 used for cardinal & bspline interpolation)*/ interpolate_particle((psys->flag & PSYS_KEYED) ? -1 /* signal for cubic interpolation */ : ((psys->part->flag & PART_HAIR_BSPLINE) ? KEY_BSPLINE : KEY_CARDINAL) - ,keys, keytime, &result); - + ,keys, keytime, &result, 0); /* the velocity needs to be converted back from cubic interpolation */ if(psys->flag & PSYS_KEYED){ @@ -2403,38 +2527,92 @@ void psys_cache_paths(Object *ob, ParticleSystem *psys, float cfra, int editupda else if(soft==NULL) { /* softbody and keyed are allready in global space */ Mat4MulVecfl(hairmat, result.co); } - + + VECCOPY(ca->co, result.co); + + /* selection coloring in edit mode */ + if(edit){ + if(pset->brushtype==PE_BRUSH_WEIGHT){ + if(k==steps) + VecLerpf(ca->col, nosel_col, sel_col, hkey[0]->weight); + else + VecLerpf(ca->col, nosel_col, sel_col, + (1.0f - keytime) * hkey[0]->weight + keytime * hkey[1]->weight); + } + else{ + if((ekey + (hkey[0] - pa->hair))->flag & PEK_SELECT){ + if((ekey + (hkey[1] - pa->hair))->flag & PEK_SELECT){ + VECCOPY(ca->col, sel_col); + } + else{ + VecLerpf(ca->col, sel_col, nosel_col, keytime); + } + } + else{ + if((ekey + (hkey[1] - pa->hair))->flag & PEK_SELECT){ + VecLerpf(ca->col, nosel_col, sel_col, keytime); + } + else{ + VECCOPY(ca->col, nosel_col); + } + } + } + } + else{ + VECCOPY(ca->col, col); + } + } + + /*--modify paths--*/ + + VecSubf(vec,(cache[i]+1)->co,cache[i]->co); + length = VecLength(vec); + + effector= 1.0f; + if(vg_effector) + effector*= psys_particle_value_from_verts(psmd->dm,psys->part->from,pa,vg_effector); + + for(k=0, ca=cache[i]; k<=steps; k++, ca++) { + /* apply effectors */ + if(!(psys->part->flag & PART_CHILD_EFFECT) && edit==0 && k) + do_path_effectors(ob, psys, i, ca, k, steps, cache[i]->co, effector, dfra, cfra, &length, vec); /* apply guide curves to path data */ - if(edit==0 && psys->effectors.first && (psys->part->flag & PART_CHILD_GUIDE)==0) - do_guide(&result, i, time, &psys->effectors); + if(edit==0 && psys->effectors.first && (psys->part->flag & PART_CHILD_EFFECT)==0) + /* ca is safe to cast, since only co and vel are used */ + do_guide((ParticleKey*)ca, i, (float)k/(float)steps, &psys->effectors); + + /* apply lattice */ + if(psys->lattice && edit==0) + calc_latt_deform(ca->co, 1.0f); /* figure out rotation */ if(k) { - float angle, tangent[3], normal[3], q[4]; + float cosangle, angle, tangent[3], normal[3], q[4]; if(k == 1) { - float *q2; - - VECSUB(tangent, result.co, (ca - 1)->co); + VECSUB(tangent, ca->co, (ca - 1)->co); - q2 = vectoquat(tangent, OB_POSX, OB_POSZ); - - QUATCOPY((ca - 1)->rot, q2); + vectoquat(tangent, OB_POSX, OB_POSZ, (ca-1)->rot); VECCOPY(prev_tangent, tangent); Normalize(prev_tangent); } else { - VECSUB(tangent, result.co, (ca - 1)->co); + VECSUB(tangent, ca->co, (ca - 1)->co); Normalize(tangent); - angle = saacos(Inpf(tangent, prev_tangent)); - if((angle > -0.000001) && (angle < 0.000001)){ + cosangle= Inpf(tangent, prev_tangent); + + /* note we do the comparison on cosangle instead of + * angle, since floating point accuracy makes it give + * different results across platforms */ + if(cosangle > 0.999999f) { QUATCOPY((ca - 1)->rot, (ca - 2)->rot); } - else{ + else { + angle= saacos(cosangle); Crossf(normal, prev_tangent, tangent); VecRotToQuat(normal, angle, q); QuatMul((ca - 1)->rot, q, (ca - 2)->rot); @@ -2443,13 +2621,13 @@ void psys_cache_paths(Object *ob, ParticleSystem *psys, float cfra, int editupda VECCOPY(prev_tangent, tangent); } - if(k == steps) { + if(k == steps) QUATCOPY(ca->rot, (ca - 1)->rot); - } } - VECCOPY(ca->co, result.co); + /* set velocity */ + if(k){ VECSUB(ca->vel, ca->co, (ca-1)->co); @@ -2459,40 +2637,27 @@ void psys_cache_paths(Object *ob, ParticleSystem *psys, float cfra, int editupda } - /* selection coloring in edit mode */ - if(edit){ - if(pset->brushtype==PE_BRUSH_WEIGHT){ - if(k==steps) - VecLerpf(ca->col, nosel_col, sel_col, hkey[0]->weight); + if(!edit && !psys->totchild) { + /* check if path needs to be cut before actual end of data points */ + if(k){ + VECSUB(dvec,ca->co,(ca-1)->co); + if(part->flag&PART_ABS_LENGTH) + len=VecLength(dvec); else - VecLerpf(ca->col,nosel_col,sel_col, - (1.0f - keytime) * hkey[0]->weight + keytime * hkey[1]->weight); + len=1.0f/(float)steps; + + k=check_path_length(k,cache[i],ca,max_length,&cur_length,len,dvec); } else{ - if((ekey + (hkey[0] - pa->hair))->flag & PEK_SELECT){ - if((ekey + (hkey[1] - pa->hair))->flag & PEK_SELECT){ - VECCOPY(ca->col, sel_col); - } - else{ - VecLerpf(ca->col, sel_col, nosel_col, keytime); - } - } - else{ - if((ekey + (hkey[1] - pa->hair))->flag & PEK_SELECT){ - VecLerpf(ca->col, nosel_col, sel_col, keytime); - } - else{ - VECCOPY(ca->col, nosel_col); - } - } + /* initialize length calculation */ + if(part->flag&PART_ABS_LENGTH) + max_length= part->abslength*pa_length; + else + max_length= pa_length; + + cur_length= 0.0f; } } - else{ - VECCOPY(ca->col, col); - } - - if(psys->lattice && edit==0) - calc_latt_deform(ca->co, 1.0f); } } @@ -2502,6 +2667,12 @@ void psys_cache_paths(Object *ob, ParticleSystem *psys, float cfra, int editupda end_latt_deform(); psys->lattice=0; } + + if(vg_effector) + MEM_freeN(vg_effector); + + if(vg_length) + MEM_freeN(vg_length); } /************************************************/ /* Particle Key handling */ @@ -2640,7 +2811,7 @@ void psys_mat_hair_to_object(Object *ob, DerivedMesh *dm, short from, ParticleDa float vec[3]; psys_face_mat(0, dm, pa, hairmat, 0); - psys_particle_on_dm(ob, dm, from, pa->num, pa->num_dmcache, pa->fuv, pa->foffset, vec, 0, 0, 0, 0, 0); + psys_particle_on_dm(dm, from, pa->num, pa->num_dmcache, pa->fuv, pa->foffset, vec, 0, 0, 0, 0, 0); VECCOPY(hairmat[3],vec); } @@ -2649,62 +2820,14 @@ void psys_mat_hair_to_orco(Object *ob, DerivedMesh *dm, short from, ParticleData float vec[3], orco[3]; psys_face_mat(ob, dm, pa, hairmat, 1); - psys_particle_on_dm(ob, dm, from, pa->num, pa->num_dmcache, pa->fuv, pa->foffset, vec, 0, 0, 0, orco, 0); + psys_particle_on_dm(dm, from, pa->num, pa->num_dmcache, pa->fuv, pa->foffset, vec, 0, 0, 0, orco, 0); /* see psys_face_mat for why this function is called */ transform_mesh_orco_verts(ob->data, &orco, 1, 1); VECCOPY(hairmat[3],orco); } -/* -void psys_key_to_geometry(DerivedMesh *dm, ParticleData *pa, ParticleKey *key) -{ - float q[4], v1[3], v2[3], v3[3]; - - dm->getVertCo(dm,pa->verts[0],v1); - dm->getVertCo(dm,pa->verts[1],v2); - dm->getVertCo(dm,pa->verts[2],v3); - - triatoquat(v1, v2, v3, q); - - QuatInv(q); - - VECSUB(key->co,key->co,v1); - - VECADD(key->vel,key->vel,key->co); - - QuatMulVecf(q, key->co); - QuatMulVecf(q, key->vel); - - VECSUB(key->vel,key->vel,key->co); - - QuatMul(key->rot,q,key->rot); -} - -void psys_key_from_geometry(DerivedMesh *dm, ParticleData *pa, ParticleKey *key) -{ - float q[4], v1[3], v2[3], v3[3]; - - dm->getVertCo(dm,pa->verts[0],v1); - dm->getVertCo(dm,pa->verts[1],v2); - dm->getVertCo(dm,pa->verts[2],v3); - - triatoquat(v1, v2, v3, q); - - VECADD(key->vel,key->vel,key->co); - - QuatMulVecf(q, key->co); - QuatMulVecf(q, key->vel); - - VECSUB(key->vel,key->vel,key->co); - - VECADD(key->co,key->co,v1); - - QuatMul(key->rot,q,key->rot); -} -*/ - -void psys_vec_rot_to_face(DerivedMesh *dm, ParticleData *pa, float *vec)//to_geometry(DerivedMesh *dm, ParticleData *pa, float *vec) +void psys_vec_rot_to_face(DerivedMesh *dm, ParticleData *pa, float *vec) { float mat[4][4]; @@ -2713,36 +2836,6 @@ void psys_vec_rot_to_face(DerivedMesh *dm, ParticleData *pa, float *vec)//to_geo Mat4Mul3Vecfl(mat, vec); } -/* unused */ -#if 0 -static void psys_vec_rot_from_face(DerivedMesh *dm, ParticleData *pa, float *vec)//from_geometry(DerivedMesh *dm, ParticleData *pa, float *vec) -{ - float q[4], v1[3], v2[3], v3[3]; - /* - dm->getVertCo(dm,pa->verts[0],v1); - dm->getVertCo(dm,pa->verts[1],v2); - dm->getVertCo(dm,pa->verts[2],v3); - */ - /* replace with this */ - MFace *mface; - int i; // = psys_particle_dm_face_lookup(dm, pa->num, pa->fuv, pa->foffset, (LinkNode*)NULL); - i = pa->num_dmcache==DMCACHE_NOTFOUND ? pa->num : pa->num_dmcache; - if (i==-1 || i >= dm->getNumFaces(dm)) { vec[0] = vec[1] = 0; vec[2] = 1; return; } - mface=dm->getFaceData(dm,i,CD_MFACE); - - dm->getVertCo(dm,mface->v1,v1); - dm->getVertCo(dm,mface->v2,v2); - dm->getVertCo(dm,mface->v3,v3); - /* done */ - - triatoquat(v1, v2, v3, q); - - QuatMulVecf(q, vec); - - //VECADD(vec,vec,v1); -} -#endif - void psys_mat_hair_to_global(Object *ob, DerivedMesh *dm, short from, ParticleData *pa, float hairmat[][4]) { float facemat[4][4]; @@ -2779,10 +2872,10 @@ static void default_particle_settings(ParticleSettings *part) part->integrator= PART_INT_MIDPOINT; part->phystype= PART_PHYS_NEWTON; - part->hair_step= 10; + part->hair_step= 5; part->keys_step= 5; - part->draw_step= 4; - part->ren_step= 6; + part->draw_step= 2; + part->ren_step= 3; part->adapt_angle= 5; part->adapt_pix= 3; part->kink_axis= 2; @@ -2790,7 +2883,6 @@ static void default_particle_settings(ParticleSettings *part) part->disp=100; part->from= PART_FROM_FACE; part->length= 1.0; - part->rotfac= 1.0; part->nbetween= 4; part->boidneighbours= 5; @@ -2853,11 +2945,12 @@ ParticleSettings *psys_copy_settings(ParticleSettings *part) partn= copy_libblock(part); if(partn->pd) partn->pd= MEM_dupallocN(part->pd); + if(partn->pd2) partn->pd2= MEM_dupallocN(part->pd2); return partn; } -void psys_make_local_settings(ParticleSettings *part) +void make_local_particlesettings(ParticleSettings *part) { Object *ob; ParticleSettings *par; @@ -2943,7 +3036,7 @@ void psys_flush_settings(ParticleSettings *part, int event, int hair_recalc) tpsys=BLI_findlink(&tob->particlesystem,psys->target_psys-1); if(tpsys && tpsys->part==part){ - psys->flag |= event; + psys->recalc |= event; flush++; } } @@ -2953,9 +3046,86 @@ void psys_flush_settings(ParticleSettings *part, int event, int hair_recalc) } } } + +LinkNode *psys_using_settings(ParticleSettings *part, int flush_update) +{ + Object *ob, *tob; + ParticleSystem *psys, *tpsys; + LinkNode *node= NULL; + int found; + + /* update all that have same particle settings */ + for(ob=G.main->object.first; ob; ob=ob->id.next) { + found= 0; + + for(psys=ob->particlesystem.first; psys; psys=psys->next) { + if(psys->part == part) { + BLI_linklist_append(&node, psys); + found++; + } + else if(psys->part->type == PART_REACTOR){ + tob= (psys->target_ob)? psys->target_ob: ob; + tpsys= BLI_findlink(&tob->particlesystem, psys->target_psys-1); + + if(tpsys && tpsys->part==part) { + BLI_linklist_append(&node, tpsys); + found++; + } + } + } + + if(flush_update && found) + DAG_object_flush_update(G.scene, ob, OB_RECALC_DATA); + } + + return node; +} + + /************************************************/ /* Textures */ /************************************************/ + +static int get_particle_uv(DerivedMesh *dm, ParticleData *pa, int face_index, float *fuv, char *name, float *texco) +{ + MFace *mf; + MTFace *tf; + int i; + + tf= CustomData_get_layer_named(&dm->faceData, CD_MTFACE, name); + + if(tf == NULL) + tf= CustomData_get_layer(&dm->faceData, CD_MTFACE); + + if(tf == NULL) + return 0; + + if(pa) { + i= (pa->num_dmcache==DMCACHE_NOTFOUND)? pa->num: pa->num_dmcache; + if(i >= dm->getNumFaces(dm)) + i = -1; + } + else + i= face_index; + + if (i==-1) { + texco[0]= 0.0f; + texco[1]= 0.0f; + texco[2]= 0.0f; + } + else { + mf= dm->getFaceData(dm, i, CD_MFACE); + + psys_interpolate_uvs(&tf[i], mf->v4, fuv, texco); + + texco[0]= texco[0]*2.0f - 1.0f; + texco[1]= texco[1]*2.0f - 1.0f; + texco[2]= 0.0f; + } + + return 1; +} + static void get_cpa_texture(DerivedMesh *dm, Material *ma, int face_index, float *fw, float *orco, ParticleTexture *ptex, int event) { MTex *mtex; @@ -2965,31 +3135,18 @@ static void get_cpa_texture(DerivedMesh *dm, Material *ma, int face_index, float if(ma) for(m=0; m<MAX_MTEX; m++){ mtex=ma->mtex[m]; if(mtex && (ma->septex & (1<<m))==0){ + float def=mtex->def_var; float var=mtex->varfac; short blend=mtex->blendtype; short neg=mtex->pmaptoneg; - if(mtex->texco & TEXCO_UV && fw){ - int uv_index=CustomData_get_named_layer_index(&dm->faceData,CD_MTFACE,mtex->uvname); - if(uv_index<0){ - uv_index=CustomData_get_active_layer_index(&dm->faceData,CD_MTFACE); - } - if(uv_index>=0){ - CustomDataLayer *layer=&dm->faceData.layers[uv_index]; - MTFace *mtface= &((MTFace*)layer->data)[face_index]; - MFace *mf=dm->getFaceData(dm,face_index,CD_MFACE); - psys_interpolate_uvs(mtface,mf->v4,fw,texco); - texco[0]*=2.0; - texco[1]*=2.0; - texco[0]-=1.0; - texco[1]-=1.0; - } - else + if((mtex->texco & TEXCO_UV) && fw) { + if(!get_particle_uv(dm, NULL, face_index, fw, mtex->uvname, texco)) VECCOPY(texco,orco); } - else{ + else VECCOPY(texco,orco); - } + externtex(mtex, texco, &value, rgba, rgba+1, rgba+2, rgba+3); if((event & mtex->pmapto) & MAP_PA_TIME){ if((setvars&MAP_PA_TIME)==0){ @@ -2999,13 +3156,13 @@ static void get_cpa_texture(DerivedMesh *dm, Material *ma, int face_index, float ptex->time= texture_value_blend(mtex->def_var,ptex->time,value,var,blend,neg & MAP_PA_TIME); } if((event & mtex->pmapto) & MAP_PA_LENGTH) - ptex->length= texture_value_blend(value,ptex->length,value,var,blend,neg & MAP_PA_LENGTH); + ptex->length= texture_value_blend(def,ptex->length,value,var,blend,neg & MAP_PA_LENGTH); if((event & mtex->pmapto) & MAP_PA_CLUMP) - ptex->clump= texture_value_blend(value,ptex->clump,value,var,blend,neg & MAP_PA_CLUMP); + ptex->clump= texture_value_blend(def,ptex->clump,value,var,blend,neg & MAP_PA_CLUMP); if((event & mtex->pmapto) & MAP_PA_KINK) - ptex->kink= texture_value_blend(value,ptex->kink,value,var,blend,neg & MAP_PA_KINK); + ptex->kink= texture_value_blend(def,ptex->kink,value,var,blend,neg & MAP_PA_KINK); if((event & mtex->pmapto) & MAP_PA_ROUGH) - ptex->rough= texture_value_blend(value,ptex->rough,value,var,blend,neg & MAP_PA_ROUGH); + ptex->rough= texture_value_blend(def,ptex->rough,value,var,blend,neg & MAP_PA_ROUGH); } } if(event & MAP_PA_TIME) { CLAMP(ptex->time,0.0,1.0); } @@ -3025,61 +3182,48 @@ void psys_get_texture(Object *ob, Material *ma, ParticleSystemModifierData *psmd mtex=ma->mtex[m]; if(mtex && (ma->septex & (1<<m))==0){ float var=mtex->varfac; + float def=mtex->def_var; short blend=mtex->blendtype; short neg=mtex->pmaptoneg; - if(mtex->texco & TEXCO_UV){ - int uv_index=CustomData_get_named_layer_index(&psmd->dm->faceData,CD_MTFACE,mtex->uvname); - if(uv_index<0){ - uv_index=CustomData_get_active_layer_index(&psmd->dm->faceData,CD_MTFACE); - } - if(uv_index>=0){ - CustomDataLayer *layer=&psmd->dm->faceData.layers[uv_index]; - MTFace *mtface= &((MTFace*)layer->data)[pa->num]; - MFace *mf=psmd->dm->getFaceData(psmd->dm,pa->num,CD_MFACE); - psys_interpolate_uvs(mtface,mf->v4,pa->fuv,texco); - texco[0]*=2.0; - texco[1]*=2.0; - texco[0]-=1.0; - texco[1]-=1.0; + if((mtex->texco & TEXCO_UV) && ELEM(psys->part->from, PART_FROM_FACE, PART_FROM_VOLUME)) { + if(!get_particle_uv(psmd->dm, pa, 0, pa->fuv, mtex->uvname, texco)) { + /* failed to get uv's, let's try orco's */ + psys_particle_on_emitter(psmd,psys->part->from,pa->num,pa->num_dmcache,pa->fuv,pa->foffset,co,0,0,0,texco, 0); } - else - //psys_particle_on_emitter(ob,psmd,psys->part->from,pa->num,pa->fuv,pa->foffset,texco,0,0,0); - /* <jahka> anyways I think it will be too small a difference to notice, so psys_get_texture should only know about the original mesh structure.. no dm needed anywhere */ - /* <brecht> the code only does dm based lookup now, so passing num_dmcache anyway to avoid^ - * massive slowdown here */ - psys_particle_on_emitter(ob,psmd,psys->part->from,pa->num,pa->num_dmcache,pa->fuv,pa->foffset,co,0,0,0,texco, 0); } - else{ - //psys_particle_on_emitter(ob,psmd,psys->part->from,pa->num,pa->fuv,pa->offset,texco,0,0,0); - /* ditto above */ - psys_particle_on_emitter(ob,psmd,psys->part->from,pa->num,pa->num_dmcache,pa->fuv,pa->foffset,co,0,0,0,texco, 0); + else { + psys_particle_on_emitter(psmd,psys->part->from,pa->num,pa->num_dmcache,pa->fuv,pa->foffset,co,0,0,0,texco, 0); } + externtex(mtex, texco, &value, rgba, rgba+1, rgba+2, rgba+3); if((event & mtex->pmapto) & MAP_PA_TIME){ + /* the first time has to set the base value for time regardless of blend mode */ if((setvars&MAP_PA_TIME)==0){ - ptex->time=0.0; - setvars|=MAP_PA_TIME; + ptex->time *= 1.0f - var; + ptex->time += var * ((neg & MAP_PA_TIME)? 1.0f - value : value); + setvars |= MAP_PA_TIME; } - ptex->time= texture_value_blend(mtex->def_var,ptex->time,value,var,blend,neg & MAP_PA_TIME); + else + ptex->time= texture_value_blend(def,ptex->time,value,var,blend,neg & MAP_PA_TIME); } if((event & mtex->pmapto) & MAP_PA_LIFE) - ptex->life= texture_value_blend(mtex->def_var,ptex->life,value,var,blend,neg & MAP_PA_LIFE); + ptex->life= texture_value_blend(def,ptex->life,value,var,blend,neg & MAP_PA_LIFE); if((event & mtex->pmapto) & MAP_PA_DENS) - ptex->exist= texture_value_blend(mtex->def_var,ptex->exist,value,var,blend,neg & MAP_PA_DENS); + ptex->exist= texture_value_blend(def,ptex->exist,value,var,blend,neg & MAP_PA_DENS); if((event & mtex->pmapto) & MAP_PA_SIZE) - ptex->size= texture_value_blend(mtex->def_var,ptex->size,value,var,blend,neg & MAP_PA_SIZE); + ptex->size= texture_value_blend(def,ptex->size,value,var,blend,neg & MAP_PA_SIZE); if((event & mtex->pmapto) & MAP_PA_IVEL) - ptex->ivel= texture_value_blend(mtex->def_var,ptex->ivel,value,var,blend,neg & MAP_PA_IVEL); + ptex->ivel= texture_value_blend(def,ptex->ivel,value,var,blend,neg & MAP_PA_IVEL); if((event & mtex->pmapto) & MAP_PA_PVEL) texture_rgb_blend(ptex->pvel,rgba,ptex->pvel,value,var,blend); if((event & mtex->pmapto) & MAP_PA_LENGTH) - ptex->length= texture_value_blend(mtex->def_var,ptex->length,value,var,blend,neg & MAP_PA_LENGTH); + ptex->length= texture_value_blend(def,ptex->length,value,var,blend,neg & MAP_PA_LENGTH); if((event & mtex->pmapto) & MAP_PA_CLUMP) - ptex->clump= texture_value_blend(mtex->def_var,ptex->clump,value,var,blend,neg & MAP_PA_CLUMP); + ptex->clump= texture_value_blend(def,ptex->clump,value,var,blend,neg & MAP_PA_CLUMP); if((event & mtex->pmapto) & MAP_PA_KINK) - ptex->kink= texture_value_blend(mtex->def_var,ptex->kink,value,var,blend,neg & MAP_PA_CLUMP); + ptex->kink= texture_value_blend(def,ptex->kink,value,var,blend,neg & MAP_PA_CLUMP); } } if(event & MAP_PA_TIME) { CLAMP(ptex->time,0.0,1.0); } @@ -3116,7 +3260,7 @@ float psys_get_size(Object *ob, Material *ma, ParticleSystemModifierData *psmd, } if(vg_size) - size*=psys_interpolate_value_from_verts(psmd->dm,part->from,pa->num,pa->fuv,vg_size); + size*=psys_particle_value_from_verts(psmd->dm,part->from,pa,vg_size); if(part->randsize!=0.0) size*= 1.0f - part->randsize*pa->sizemul; @@ -3149,16 +3293,24 @@ float psys_get_child_size(ParticleSystem *psys, ChildParticle *cpa, float cfra, float size, time; if(part->childtype==PART_CHILD_FACES){ - if(pa_time) - time=*pa_time; - else - time=psys_get_child_time(psys,cpa,cfra); + size=part->size; if((part->flag&PART_ABS_TIME)==0 && part->ipo){ + IpoCurve *icu; + + if(pa_time) + time=*pa_time; + else + time=psys_get_child_time(psys,cpa,cfra); + + /* correction for lifetime */ calc_ipo(part->ipo, 100*time); - execute_ipo((ID *)part, part->ipo); + + for(icu = part->ipo->curve.first; icu; icu=icu->next) { + if(icu->adrcode == PART_SIZE) + size = icu->curval; + } } - size=part->size; } else size=psys->particles[cpa->parent].size; @@ -3179,13 +3331,13 @@ void psys_get_particle_on_path(Object *ob, ParticleSystem *psys, int p, Particle ParticleData *pa; ChildParticle *cpa; ParticleTexture ptex; - ParticleKey tstate; - HairKey *hkey[2]; + ParticleKey *kkey[2] = {NULL, NULL}; + HairKey *hkey[2] = {NULL, NULL}; ParticleKey *par=0, keys[4]; - float t, real_t, dfra, keytime; + float t, real_t, dfra, keytime, frs_sec = G.scene->r.frs_sec; float co[3], orco[3]; - float imat[4][4], hairmat[4][4], cpa_1st[3]; + float hairmat[4][4]; float pa_clump = 0.0, pa_kink = 0.0; int totparent = 0; int totpart = psys->totpart; @@ -3218,19 +3370,39 @@ void psys_get_particle_on_path(Object *ob, ParticleSystem *psys, int p, Particle return; } - hkey[0] = pa->hair; - hkey[1] = pa->hair + 1; + if(psys->flag & PSYS_KEYED) { + kkey[0] = pa->keys; + kkey[1] = kkey[0] + 1; + + real_t = kkey[0]->time + t * (kkey[0][pa->totkey-1].time - kkey[0]->time); + } + else { + hkey[0] = pa->hair; + hkey[1] = pa->hair + 1; - real_t = hkey[0]->time + (hkey[0][pa->totkey-1].time - hkey[0]->time) * t; + real_t = hkey[0]->time + (hkey[0][pa->totkey-1].time - hkey[0]->time) * t; + } - while(hkey[1]->time < real_t) - hkey[1]++; + if(psys->flag & PSYS_KEYED) { + while(kkey[1]->time < real_t) { + kkey[1]++; + } + kkey[0] = kkey[1] - 1; - hkey[0] = hkey[1] - 1; + memcpy(keys + 1, kkey[0], sizeof(ParticleKey)); + memcpy(keys + 2, kkey[1], sizeof(ParticleKey)); + } + else { + while(hkey[1]->time < real_t) + hkey[1]++; - hair_to_particle(keys + 1, hkey[0]); - hair_to_particle(keys + 2, hkey[1]); + hkey[0] = hkey[1] - 1; + hair_to_particle(keys + 1, hkey[0]); + hair_to_particle(keys + 2, hkey[1]); + } + + if((psys->flag & PSYS_KEYED)==0) { //if(soft){ // if(key[0] != sbel.keys) // DB_copy_key(&k1,key[0]-1); @@ -3255,6 +3427,7 @@ void psys_get_particle_on_path(Object *ob, ParticleSystem *psys, int p, Particle hair_to_particle(keys + 3, hkey[1] + 1); else hair_to_particle(keys + 3, hkey[1]); + } //} //psys_get_particle_on_path(bsys,p,t,bkey,ckey[0]); @@ -3264,35 +3437,48 @@ void psys_get_particle_on_path(Object *ob, ParticleSystem *psys, int p, Particle //else{ // /* TODO: different rotations */ // float nvel[3]; - // float *q2; // VECCOPY(nvel,state->vel); // VecMulf(nvel,-1.0f); - // q2=vectoquat(nvel, OB_POSX, OB_POSZ); - // QUATCOPY(state->rot,q2); + // vectoquat(nvel, OB_POSX, OB_POSZ, state->rot); //} dfra = keys[2].time - keys[1].time; keytime = (real_t - keys[1].time) / dfra; - interpolate_particle((psys->part->flag & PART_HAIR_BSPLINE) ? KEY_BSPLINE : KEY_CARDINAL - ,keys, keytime, state); + /* convert velocity to timestep size */ + if(psys->flag & PSYS_KEYED){ + VecMulf(keys[1].vel, dfra / frs_sec); + VecMulf(keys[2].vel, dfra / frs_sec); + QuatInterpol(state->rot,keys[1].rot,keys[2].rot,keytime); + } - if((pa->flag & PARS_REKEY)==0) { - psys_mat_hair_to_global(ob, psmd->dm, part->from, pa, hairmat); - Mat4MulVecfl(hairmat, state->co); + interpolate_particle((psys->flag & PSYS_KEYED) ? -1 /* signal for cubic interpolation */ + : ((psys->part->flag & PART_HAIR_BSPLINE) ? KEY_BSPLINE : KEY_CARDINAL) + ,keys, keytime, state, 1); - if(psys->effectors.first && (part->flag & PART_CHILD_GUIDE)==0) { - do_guide(state, p, state->time, &psys->effectors); - /* TODO: proper velocity handling */ - } + /* the velocity needs to be converted back from cubic interpolation */ + if(psys->flag & PSYS_KEYED){ + VecMulf(state->vel, frs_sec / dfra); + } + else { + if((pa->flag & PARS_REKEY)==0) { + psys_mat_hair_to_global(ob, psmd->dm, part->from, pa, hairmat); + Mat4MulVecfl(hairmat, state->co); + Mat4Mul3Vecfl(hairmat, state->vel); + + if(psys->effectors.first && (part->flag & PART_CHILD_GUIDE)==0) { + do_guide(state, p, state->time, &psys->effectors); + /* TODO: proper velocity handling */ + } - if(psys->lattice && edit==0) - calc_latt_deform(state->co,1.0f); + if(psys->lattice && edit==0) + calc_latt_deform(state->co,1.0f); + } } } else if(totchild){ - Mat4Invert(imat,ob->obmat); + //Mat4Invert(imat,ob->obmat); cpa=psys->child+p-totpart; @@ -3321,22 +3507,12 @@ void psys_get_particle_on_path(Object *ob, ParticleSystem *psys, int p, Particle cpa_fuv = cpa->fuv; cpa_from = PART_FROM_FACE; - psys_particle_on_emitter(ob,psmd,cpa_from,cpa_num,DMCACHE_ISCHILD,cpa->fuv,foffset,co,0,0,0,orco,0); + psys_particle_on_emitter(psmd,cpa_from,cpa_num,DMCACHE_ISCHILD,cpa->fuv,foffset,co,0,0,0,orco,0); /* we need to save the actual root position of the child for positioning it accurately to the surface of the emitter */ - VECCOPY(cpa_1st,co); - - //w=0; - //while(w<4 && cpa->pa[w]>=0){ - // pa=psys->particles+cpa->pa[w]; - // psys_particle_on_emitter(ob,psmd,part->from,pa->num,pa->fuv,pa->foffset,vec,0,0,0); - // cpa_1st[0] -= cpa->w[w]*vec[0]; - // cpa_1st[1] -= cpa->w[w]*vec[1]; - // cpa_1st[2] -= cpa->w[w]*vec[2]; - // w++; - //} + //VECCOPY(cpa_1st,co); - Mat4MulVecfl(ob->obmat,cpa_1st); + //Mat4MulVecfl(ob->obmat,cpa_1st); pa=0; } @@ -3353,7 +3529,7 @@ void psys_get_particle_on_path(Object *ob, ParticleSystem *psys, int p, Particle cpa_num=pa->num; cpa_fuv=pa->fuv; - psys_particle_on_emitter(ob,psmd,cpa_from,cpa_num,DMCACHE_ISCHILD,cpa_fuv,pa->foffset,co,0,0,0,orco,0); + psys_particle_on_emitter(psmd,cpa_from,cpa_num,DMCACHE_ISCHILD,cpa_fuv,pa->foffset,co,0,0,0,orco,0); } /* correct child ipo timing */ @@ -3391,7 +3567,7 @@ void psys_get_particle_on_path(Object *ob, ParticleSystem *psys, int p, Particle w++; } /* apply offset for correct positioning */ - VECADD(state->co,state->co,cpa_1st); + //VECADD(state->co,state->co,cpa_1st); } else{ /* offset the child from the parent position */ @@ -3418,12 +3594,8 @@ void psys_get_particle_on_path(Object *ob, ParticleSystem *psys, int p, Particle do_clump(state, par, t, part->clumpfac, part->clumppow, 1.0f); - if(part->kink) - do_postkink(state, par, par->rot, t, part->kink_freq * pa_kink, part->kink_shape, - part->kink_amp, part->kink, part->kink_axis, ob->obmat); - if(part->rough1 != 0.0) - do_rough(orco, t, part->rough1, part->rough1_size, 0.0, state); + do_rough(orco, t, part->rough1, part->rough1_size, 0.0, state); if(part->rough2 != 0.0) do_rough(cpa->rand, t, part->rough2, part->rough2_size, part->rough2_thres, state); @@ -3431,19 +3603,18 @@ void psys_get_particle_on_path(Object *ob, ParticleSystem *psys, int p, Particle if(part->rough_end != 0.0) do_rough_end(cpa->rand, t, part->rough_end, part->rough_end_shape, state, par); - if(vel){ - if(t>=0.001f){ - tstate.time=t-0.001f; - psys_get_particle_on_path(ob,psys,p,&tstate,0); - VECSUB(state->vel,state->co,tstate.co); - } - else{ - tstate.time=t+0.001f; - psys_get_particle_on_path(ob,psys,p,&tstate,0); - VECSUB(state->vel,tstate.co,state->co); - } - } - + //if(vel){ + // if(t>=0.001f){ + // tstate.time=t-0.001f; + // psys_get_particle_on_path(ob,psys,p,&tstate,0); + // VECSUB(state->vel,state->co,tstate.co); + // } + // else{ + // tstate.time=t+0.001f; + // psys_get_particle_on_path(ob,psys,p,&tstate,0); + // VECSUB(state->vel,tstate.co,state->co); + // } + //} } } /* gets particle's state at a time, returns 1 if particle exists and can be seen and 0 if not */ @@ -3453,6 +3624,7 @@ int psys_get_particle_state(Object *ob, ParticleSystem *psys, int p, ParticleKey float cfra; int totpart=psys->totpart, between=0; + /* negative time means "use current time" */ if(state->time>0) cfra=state->time; else @@ -3484,50 +3656,92 @@ int psys_get_particle_state(Object *ob, ParticleSystem *psys, int p, ParticleKey return 0; } - //if(bsys->flag & (BSYS_DONE|BSYS_KEYED)){ - // if(between){ - // //ChildParticle *cpa=psys->child+p-totpart; - // //state->time= (cfra-(part->sta+(part->end-part->sta)*cpa->rand[0]))/(part->lifetime*cpa->rand[1]); - // } - // else - // state->time= (cfra-pa->time)/(pa->dietime-pa->time);//pa->lifetime; + if(psys->flag & PSYS_KEYED){ + if(between){ + ChildParticle *cpa=psys->child+p-totpart; + state->time= (cfra-(part->sta+(part->end-part->sta)*cpa->rand[0]))/(part->lifetime*cpa->rand[1]); + } + else + state->time= (cfra-pa->time)/(pa->dietime-pa->time); - // psys_get_particle_on_path(ob,psys,p,state,1); - // return 1; - //} - //else{ - //if(psys->totchild && p>=psys->totpart){ - // ChildParticle *cpa=psys->child+p-psys->totpart; - // ParticleKey *key1, skey; - // float t=(cfra-pa->time)/pa->lifetime, clump; - - // pa=psys->particles+cpa->parent; - - // if(pa->alive==PARS_DEAD && part->flag&PART_STICKY && pa->flag&PARS_STICKY && pa->stick_ob){ - // key1=&skey; - // copy_particle_key(key1,&pa->state,0); - // key_from_object(pa->stick_ob,key1); - // } - // else{ - // key1=&pa->state; - // } - // - // offset_child(cpa, key1, state, part->childflat, part->childrad); - // - // CLAMP(t,0.0,1.0); - // if(part->kink) /* TODO: part->kink_freq*pa_kink */ - // do_prekink(state,key1,t,part->kink_freq,part->kink_shape,part->kink_amp,part->kink,part->kink_axis,ob->obmat); - // - // /* TODO: pa_clump vgroup */ - // do_clump(state,key1,t,part->clumpfac,part->clumppow,0); - - // if(part->kink) /* TODO: part->kink_freq*pa_kink */ - // do_postkink(state,key1,t,part->kink_freq,part->kink_shape,part->kink_amp,part->kink,part->kink_axis,ob->obmat); + psys_get_particle_on_path(ob,psys,p,state,1); + return 1; + } + else{ + if(between) + return 0; /* currently not supported */ + else if(psys->totchild && p>=psys->totpart){ + ChildParticle *cpa=psys->child+p-psys->totpart; + ParticleKey *key1, skey; + float t = (cfra - pa->time + pa->loop * pa->lifetime) / pa->lifetime; + + pa = psys->particles + cpa->parent; + + if(pa->alive==PARS_DEAD && part->flag&PART_STICKY && pa->flag&PARS_STICKY && pa->stick_ob) { + key1 = &skey; + copy_particle_key(key1,&pa->state,0); + key_from_object(pa->stick_ob,key1); + } + else { + key1=&pa->state; + } + + offset_child(cpa, key1, state, part->childflat, part->childrad); + + CLAMP(t,0.0,1.0); + if(part->kink) /* TODO: part->kink_freq*pa_kink */ + do_prekink(state,key1,key1->rot,t,part->kink_freq,part->kink_shape,part->kink_amp,part->kink,part->kink_axis,ob->obmat); + + /* TODO: pa_clump vgroup */ + do_clump(state,key1,t,part->clumpfac,part->clumppow,1.0); - //} - //else{ + if(psys->lattice) + calc_latt_deform(state->co,1.0f); + } + else{ if (pa) { /* TODO PARTICLE - should this ever be NULL? - Campbell */ - copy_particle_key(state,&pa->state,0); + if(pa->state.time==state->time || ELEM(part->phystype,PART_PHYS_NO,PART_PHYS_KEYED)) + copy_particle_key(state, &pa->state, 1); + else if(pa->prev_state.time==state->time) + copy_particle_key(state, &pa->prev_state, 1); + else { + /* let's interpolate to try to be as accurate as possible */ + if(pa->state.time + 1.0f > state->time && pa->prev_state.time - 1.0f < state->time) { + ParticleKey keys[4]; + float dfra, keytime, frs_sec = G.scene->r.frs_sec; + + if(pa->prev_state.time >= pa->state.time) { + /* prev_state is wrong so let's not use it, this can happen at frame 1 or particle birth */ + copy_particle_key(state, &pa->state, 1); + + VECADDFAC(state->co, state->co, state->vel, (state->time-pa->state.time)/frs_sec); + } + else { + copy_particle_key(keys+1, &pa->prev_state, 1); + copy_particle_key(keys+2, &pa->state, 1); + + dfra = keys[2].time - keys[1].time; + + keytime = (state->time - keys[1].time) / dfra; + + /* convert velocity to timestep size */ + VecMulf(keys[1].vel, dfra / frs_sec); + VecMulf(keys[2].vel, dfra / frs_sec); + + interpolate_particle(-1, keys, keytime, state, 1); + + /* convert back to real velocity */ + VecMulf(state->vel, frs_sec / dfra); + + VecLerpf(state->ave, keys[1].ave, keys[2].ave, keytime); + QuatInterpol(state->rot, keys[1].rot, keys[2].rot, keytime); + } + } + else { + /* extrapolating over big ranges is not accurate so let's just give something close to reasonable back */ + copy_particle_key(state, &pa->state, 0); + } + } if(pa->alive==PARS_DEAD && part->flag&PART_STICKY && pa->flag&PARS_STICKY && pa->stick_ob){ key_from_object(pa->stick_ob,state); @@ -3536,9 +3750,111 @@ int psys_get_particle_state(Object *ob, ParticleSystem *psys, int p, ParticleKey if(psys->lattice) calc_latt_deform(state->co,1.0f); } - //} + } return 1; - //} + } +} + +void psys_get_dupli_texture(Object *ob, ParticleSettings *part, ParticleSystemModifierData *psmd, ParticleData *pa, ChildParticle *cpa, float *uv, float *orco) +{ + MFace *mface; + MTFace *mtface; + float loc[3]; + int num; + + if(cpa) { + if(part->childtype == PART_CHILD_FACES) { + mtface= CustomData_get_layer(&psmd->dm->faceData, CD_MTFACE); + if(mtface) { + mface= psmd->dm->getFaceData(psmd->dm, cpa->num, CD_MFACE); + mtface += cpa->num; + psys_interpolate_uvs(mtface, mface->v4, cpa->fuv, uv); + } + else + uv[0]= uv[1]= 0.0f; + } + 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) + if(pa->num < psmd->dm->getNumFaces(psmd->dm)) + num= pa->num; + + 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 + 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); + } +} + +void psys_get_dupli_path_transform(Object *ob, ParticleSystem *psys, ParticleSystemModifierData *psmd, ParticleData *pa, ChildParticle *cpa, ParticleCacheKey *cache, float mat[][4], float *scale) +{ + float loc[3], nor[3], vec[3], side[3], len, obrotmat[4][4], qmat[4][4]; + float xvec[3] = {-1.0, 0.0, 0.0}, q[4]; + + VecSubf(vec, (cache+cache->steps-1)->co, cache->co); + len= Normalize(vec); + + if(pa) + psys_particle_on_emitter(psmd,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); + + if(psys->part->rotmode) { + if(!pa) + pa= psys->particles+cpa->pa[0]; + + vectoquat(xvec, ob->trackflag, ob->upflag, q); + QuatToMat4(q, obrotmat); + obrotmat[3][3]= 1.0f; + + QuatToMat4(pa->state.rot, qmat); + Mat4MulMat4(mat, obrotmat, qmat); + } + else { + /* make sure that we get a proper side vector */ + if(fabs(Inpf(nor,vec))>0.999999) { + if(fabs(Inpf(nor,xvec))>0.999999) { + nor[0] = 0.0f; + nor[1] = 1.0f; + nor[2] = 0.0f; + } + else { + nor[0] = 1.0f; + nor[1] = 0.0f; + nor[2] = 0.0f; + } + } + Crossf(side, nor, vec); + Normalize(side); + Crossf(nor, vec, side); + + Mat4One(mat); + VECCOPY(mat[0], vec); + VECCOPY(mat[1], side); + VECCOPY(mat[2], nor); + } + + *scale= len; } diff --git a/source/blender/blenkernel/intern/particle_system.c b/source/blender/blenkernel/intern/particle_system.c index e3b3629fb72..6d8d6c233ac 100644 --- a/source/blender/blenkernel/intern/particle_system.c +++ b/source/blender/blenkernel/intern/particle_system.c @@ -53,13 +53,15 @@ #include "BLI_arithb.h" #include "BLI_blenlib.h" #include "BLI_kdtree.h" +#include "BLI_kdopbvh.h" #include "BLI_linklist.h" #include "BLI_threads.h" #include "BKE_anim.h" #include "BKE_cdderivedmesh.h" +#include "BKE_collision.h" #include "BKE_displist.h" - +#include "BKE_effect.h" #include "BKE_particle.h" #include "BKE_global.h" #include "BKE_utildefines.h" @@ -73,10 +75,28 @@ #include "BKE_pointcache.h" #include "BKE_mesh.h" #include "BKE_modifier.h" +#include "BKE_scene.h" +#include "PIL_time.h" #include "RE_shader_ext.h" +/* fluid sim particle import */ +#ifndef DISABLE_ELBEEM +#include "DNA_object_fluidsim.h" +#include "LBM_fluidsim.h" +#include "elbeem.h" +#include <zlib.h> +#include <string.h> + +#ifdef WIN32 +#ifndef snprintf +#define snprintf _snprintf +#endif +#endif + +#endif // DISABLE_ELBEEM + /************************************************/ /* Reacting to system events */ /************************************************/ @@ -98,13 +118,65 @@ static int get_current_display_percentage(ParticleSystem *psys) return psys->part->disp; } -static void alloc_particles(Object *ob, ParticleSystem *psys, int new_totpart) +void psys_reset(ParticleSystem *psys, int mode) +{ + ParticleSettings *part= psys->part; + ParticleData *pa; + int i; + + if(ELEM(mode, PSYS_RESET_ALL, PSYS_RESET_DEPSGRAPH)) { + if(mode == PSYS_RESET_ALL || !(part->type == PART_HAIR && (psys->flag & PSYS_EDITED))) { + if(psys->particles) { + if(psys->particles->keys) + MEM_freeN(psys->particles->keys); + + for(i=0, pa=psys->particles; i<psys->totpart; i++, pa++) + if(pa->hair) MEM_freeN(pa->hair); + + MEM_freeN(psys->particles); + psys->particles= NULL; + } + + psys->totpart= 0; + psys->totkeyed= 0; + psys->flag &= ~(PSYS_HAIR_DONE|PSYS_KEYED); + + if(psys->reactevents.first) + BLI_freelistN(&psys->reactevents); + } + } + else if(mode == PSYS_RESET_CACHE_MISS) { + /* set all particles to be skipped */ + ParticleData *pa = psys->particles; + int p=0; + + for(; p<psys->totpart; p++, pa++) + pa->flag |= PARS_NO_DISP; + } + + /* reset children */ + if(psys->child) { + MEM_freeN(psys->child); + psys->child= 0; + } + + psys->totchild= 0; + + /* reset path cache */ + psys_free_path_cache(psys); + + /* reset point cache */ + psys->pointcache->flag &= ~PTCACHE_SIMULATION_VALID; + psys->pointcache->simframe= 0; +} + +static void realloc_particles(Object *ob, ParticleSystem *psys, int new_totpart) { ParticleData *newpars = 0, *pa; int i, totpart, totsaved = 0; if(new_totpart<0) { - if(psys->part->distr==PART_DISTR_GRID) { + if(psys->part->distr==PART_DISTR_GRID && psys->part->from != PART_FROM_VERT) { totpart= psys->part->grid_res; totpart*=totpart*totpart; } @@ -122,6 +194,12 @@ static void alloc_particles(Object *ob, ParticleSystem *psys, int new_totpart) if(totsaved) memcpy(newpars,psys->particles,totsaved*sizeof(ParticleData)); + if(psys->particles->keys) + MEM_freeN(psys->particles->keys); + + for(i=0, pa=psys->particles; i<totsaved; i++, pa++) + if(pa->keys) pa->keys= NULL; + for(i=totsaved, pa=psys->particles+totsaved; i<psys->totpart; i++, pa++) if(pa->hair) MEM_freeN(pa->hair); @@ -138,15 +216,24 @@ static void alloc_particles(Object *ob, ParticleSystem *psys, int new_totpart) psys->totpart=totpart; } -static int get_alloc_child_particles_tot(ParticleSystem *psys) +static int get_psys_child_number(ParticleSystem *psys) { - int child_nbr; + int 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; + if(psys->renderdata) { + nbr= psys->part->ren_child_nbr; + return get_render_child_particle_number(&G.scene->r, nbr); + } + else + return psys->part->child_nbr; +} + +static int get_psys_tot_child(ParticleSystem *psys) +{ + return psys->totpart*get_psys_child_number(psys); } static void alloc_child_particles(ParticleSystem *psys, int tot) @@ -164,12 +251,13 @@ static void alloc_child_particles(ParticleSystem *psys, int tot) } } -/* only run this if from == PART_FROM_FACE */ -void psys_calc_dmfaces(Object *ob, DerivedMesh *dm, ParticleSystem *psys) +void psys_calc_dmcache(Object *ob, DerivedMesh *dm, ParticleSystem *psys) { - /* use for building derived mesh face-origin info, - node - the allocated links - total derived mesh face count - node_array - is the array of nodes alligned with the base mesh's faces, so each original face can reference its derived faces + /* use for building derived mesh mapping info: + + node: the allocated links - total derived mesh element count + nodearray: the array of nodes aligned with the base mesh's elements, so + each original elements can reference its derived elements */ Mesh *me= (Mesh*)ob->data; ParticleData *pa= 0; @@ -178,57 +266,60 @@ void psys_calc_dmfaces(Object *ob, DerivedMesh *dm, ParticleSystem *psys) /* CACHE LOCATIONS */ if(!dm->deformedOnly) { /* Will use later to speed up subsurf/derivedmesh */ + LinkNode *node, *nodedmelem, **nodearray; + int totdmelem, totelem, i, *origindex; + + if(psys->part->from == PART_FROM_VERT) { + totdmelem= dm->getNumVerts(dm); + totelem= me->totvert; + origindex= DM_get_vert_data_layer(dm, CD_ORIGINDEX); + } + else { /* FROM_FACE/FROM_VOLUME */ + totdmelem= dm->getNumFaces(dm); + totelem= me->totface; + origindex= DM_get_face_data_layer(dm, CD_ORIGINDEX); + } - int tot_dm_face = dm->getNumFaces(dm); - int totface = me->totface; - int *origindex = DM_get_face_data_layer(dm, CD_ORIGINDEX); - int i; - LinkNode *node, *node_dm_faces, **node_array; + nodedmelem= MEM_callocN(sizeof(LinkNode)*totdmelem, "psys node elems"); + nodearray= MEM_callocN(sizeof(LinkNode *)*totelem, "psys node array"); - node_dm_faces = node = MEM_callocN(sizeof(LinkNode)*tot_dm_face, "faceindicies"); - node_array = MEM_callocN(sizeof(LinkNode *)*totface, "faceindicies array"); - - for(i=0; i < tot_dm_face; i++, origindex++, node++) { - node->link = (void *)i; // or use the index? + for(i=0, node=nodedmelem; i<totdmelem; i++, origindex++, node++) { + node->link= SET_INT_IN_POINTER(i); + if(*origindex != -1) { - if(node_array[*origindex]) { + if(nodearray[*origindex]) { /* prepend */ - node->next = node_array[*origindex]; - node_array[*origindex] = node; - } else { - node_array[*origindex] = node; + node->next = nodearray[*origindex]; + nodearray[*origindex]= node; } + else + nodearray[*origindex]= node; } } - /* cache the faces! */ - - + /* cache the verts/faces! */ for(p=0,pa=psys->particles; p<psys->totpart; p++,pa++) { - //i = pa->num; - //if (i<totface) // should never happen - i = psys_particle_dm_face_lookup(ob, dm, pa->num, pa->fuv, node_array[pa->num]); - pa->num_dmcache = i; + if(psys->part->from == PART_FROM_VERT) { + if(nodearray[pa->num]) + pa->num_dmcache= GET_INT_FROM_POINTER(nodearray[pa->num]->link); + } + else { /* FROM_FACE/FROM_VOLUME */ + /* Note that somtimes the pa->num is over the nodearray size, this is bad, maybe there is a better place to fix this, + * but for now passing NULL is OK. every face will be searched for the particle so its slower - Campbell */ + pa->num_dmcache= psys_particle_dm_face_lookup(ob, dm, pa->num, pa->fuv, pa->num < totelem ? nodearray[pa->num] : NULL); + } } - //for (i=0; i < totface; i++) { - // i = psys_particle_dm_face_lookup(ob, dm, node_array[], fuv, (LinkNode*)NULL); - //} - MEM_freeN(node_array); - MEM_freeN(node_dm_faces); - - } else { - /* set the num_dmcache to an invalid value, just incase */ - /* TODO PARTICLE, make the following line unnecessary, each function should know to use the num or num_dmcache */ + MEM_freeN(nodearray); + MEM_freeN(nodedmelem); + } + else { + /* TODO PARTICLE, make the following line unnecessary, each function + * should know to use the num or num_dmcache, set the num_dmcache to + * an invalid value, just incase */ - /* - for(p=0,pa=psys->particles; p<psys->totpart; p++,pa++) { - pa->num_dmcache = pa->num; - } - */ - for(p=0,pa=psys->particles; p<psys->totpart; p++,pa++) { + for(p=0,pa=psys->particles; p<psys->totpart; p++,pa++) pa->num_dmcache = -1; - } } } @@ -388,6 +479,29 @@ static void distribute_particles_in_grid(DerivedMesh *dm, ParticleSystem *psys) } } +/* modified copy from rayshade.c */ +static void hammersley_create(float *out, int n, int seed, float amount) +{ + RNG *rng; + double p, t, offs[2]; + int k, kk; + + rng = rng_new(31415926 + n + seed); + offs[0]= rng_getDouble(rng) + amount; + offs[1]= rng_getDouble(rng) + amount; + rng_free(rng); + + for (k = 0; k < n; k++) { + t = 0; + for (p = 0.5, kk = k; kk; p *= 0.5, kk >>= 1) + if (kk & 1) /* kk mod 2 = 1 */ + t += p; + + out[2*k + 0]= fmod((double)k/(double)n + offs[0], 1.0); + out[2*k + 1]= fmod(t + offs[1], 1.0); + } +} + /* modified copy from effect.c */ static void init_mv_jit(float *jit, int num, int seed2, float amount) { @@ -485,7 +599,7 @@ void psys_thread_distribute_particle(ParticleThread *thread, ParticleData *pa, C ParticleData *tpa; ParticleSettings *part= ctx->psys->part; float *v1, *v2, *v3, *v4, nor[3], orco1[3], co1[3], co2[3], nor1[3], ornor1[3]; - float cur_d, min_d; + float cur_d, min_d, randu, randv; int from= ctx->from; int cfrom= ctx->cfrom; int distr= ctx->distr; @@ -503,7 +617,7 @@ void psys_thread_distribute_particle(ParticleThread *thread, ParticleData *pa, C KDTreeNearest ptn[3]; int w, maxw; - psys_particle_on_dm(ctx->ob,ctx->dm,from,pa->num,pa->num_dmcache,pa->fuv,pa->foffset,co1,0,0,0,orco1,0); + psys_particle_on_dm(ctx->dm,from,pa->num,pa->num_dmcache,pa->fuv,pa->foffset,co1,0,0,0,orco1,0); transform_mesh_orco_verts((Mesh*)ob->data, &orco1, 1, 1); maxw = BLI_kdtree_find_n_nearest(ctx->tree,3,orco1,NULL,ptn); @@ -527,7 +641,9 @@ void psys_thread_distribute_particle(ParticleThread *thread, ParticleData *pa, C //ctx->jitoff[i]=(float)fmod(ctx->jitoff[i]+ctx->maxweight/ctx->weight[i],(float)ctx->jitlevel); break; case PART_DISTR_RAND: - psys_uv_to_w(rng_getFloat(thread->rng), rng_getFloat(thread->rng), mface->v4, pa->fuv); + randu= rng_getFloat(thread->rng); + randv= rng_getFloat(thread->rng); + psys_uv_to_w(randu, randv, mface->v4, pa->fuv); break; } pa->foffset= 0.0f; @@ -625,7 +741,9 @@ void psys_thread_distribute_particle(ParticleThread *thread, ParticleData *pa, C // ctx->jitoff[i]=(float)fmod(ctx->jitoff[i]+ctx->maxweight/ctx->weight[i],(float)ctx->jitlevel); // break; // case PART_DISTR_RAND: - psys_uv_to_w(rng_getFloat(thread->rng), rng_getFloat(thread->rng), mf->v4, cpa->fuv); + randu= rng_getFloat(thread->rng); + randv= rng_getFloat(thread->rng); + psys_uv_to_w(randu, randv, mf->v4, cpa->fuv); // break; //} @@ -643,7 +761,7 @@ void psys_thread_distribute_particle(ParticleThread *thread, ParticleData *pa, C do_seams= (part->flag&PART_CHILD_SEAMS && ctx->seams); - psys_particle_on_dm(ob,dm,cfrom,cpa->num,DMCACHE_ISCHILD,cpa->fuv,cpa->foffset,co1,nor1,0,0,orco1,ornor1); + psys_particle_on_dm(dm,cfrom,cpa->num,DMCACHE_ISCHILD,cpa->fuv,cpa->foffset,co1,nor1,0,0,orco1,ornor1); transform_mesh_orco_verts((Mesh*)ob->data, &orco1, 1, 1); maxw = BLI_kdtree_find_n_nearest(ctx->tree,(do_seams)?10:4,orco1,ornor1,ptn); @@ -741,7 +859,7 @@ void psys_thread_distribute_particle(ParticleThread *thread, ParticleData *pa, C } } -void *exec_distribution(void *data) +static void *exec_distribution(void *data) { ParticleThread *thread= (ParticleThread*)data; ParticleSystem *psys= thread->ctx->psys; @@ -773,6 +891,29 @@ void *exec_distribution(void *data) return 0; } +/* 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) +{ + int index1 = COMPARE_ORIG_INDEX[*(const int*)p1]; + int index2 = COMPARE_ORIG_INDEX[*(const int*)p2]; + + if(index1 < index2) + return -1; + else if(index1 == index2) { + /* this pointer comparison appears to make qsort stable for glibc, + * and apparently on solaris too, makes the renders reproducable */ + if(p1 < p2) + return -1; + else if(p1 == p2) + return 0; + else + return 1; + } + else + return 1; +} + /* creates a distribution of coordinates on a DerivedMesh */ /* */ /* 1. lets check from what we are emitting */ @@ -834,14 +975,14 @@ int psys_threads_init_distribution(ParticleThread *threads, DerivedMesh *finaldm tree=BLI_kdtree_new(totpart); for(p=0,pa=psys->particles; p<totpart; p++,pa++){ - psys_particle_on_dm(ob,dm,part->from,pa->num,pa->num_dmcache,pa->fuv,pa->foffset,co,nor,0,0,orco,ornor); + 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_alloc_child_particles_tot(psys); + totpart=get_psys_tot_child(psys); cfrom=from=PART_FROM_FACE; if(part->flag&PART_CHILD_SEAMS){ @@ -890,9 +1031,9 @@ int psys_threads_init_distribution(ParticleThread *threads, DerivedMesh *finaldm } else{ /* no need to figure out distribution */ - int child_nbr= (psys->renderdata)? part->ren_child_nbr: part->child_nbr; + int child_nbr= get_psys_child_number(psys); - totpart= get_alloc_child_particles_tot(psys); + totpart= get_psys_tot_child(psys); alloc_child_particles(psys, totpart); cpa=psys->child; for(i=0; i<child_nbr; i++){ @@ -923,7 +1064,7 @@ int psys_threads_init_distribution(ParticleThread *threads, DerivedMesh *finaldm dm= CDDM_from_mesh((Mesh*)ob->data, ob); /* special handling of grid distribution */ - if(part->distr==PART_DISTR_GRID){ + if(part->distr==PART_DISTR_GRID && from != PART_FROM_VERT){ distribute_particles_in_grid(dm,psys); dm->release(dm); return 0; @@ -980,17 +1121,21 @@ int psys_threads_init_distribution(ParticleThread *threads, DerivedMesh *finaldm if(tot==0){ no_distr=1; if(children){ - fprintf(stderr,"Particle child distribution error: Nothing to emit from!\n"); - 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; + 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 { - fprintf(stderr,"Particle distribution error: Nothing to emit from!\n"); + 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; @@ -1131,7 +1276,7 @@ int psys_threads_init_distribution(ParticleThread *threads, DerivedMesh *finaldm double step, pos; step= (totpart <= 1)? 0.5: 1.0/(totpart-1); - pos= 0.0f; + pos= 1e-16f; /* tiny offset to avoid zero weight face */ i= 0; for(p=0; p<totpart; p++, pos+=step) { @@ -1139,6 +1284,8 @@ int psys_threads_init_distribution(ParticleThread *threads, DerivedMesh *finaldm i++; index[p]= MIN2(tot-1, i); + + /* avoid zero weight face */ if(p == totpart-1 && weight[index[p]] == 0.0f) index[p]= index[p-1]; @@ -1148,6 +1295,13 @@ int psys_threads_init_distribution(ParticleThread *threads, DerivedMesh *finaldm MEM_freeN(sum); + /* for hair, sort by origindex, allows optimizations in rendering */ + if(part->type == PART_HAIR) { + COMPARE_ORIG_INDEX= dm->getFaceDataArray(dm, CD_ORIGINDEX); + if(COMPARE_ORIG_INDEX) + qsort(index, totpart, sizeof(int), compare_orig_index); + } + /* 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++) @@ -1165,9 +1319,15 @@ int psys_threads_init_distribution(ParticleThread *threads, DerivedMesh *finaldm //if(jitlevel>100) jitlevel= 100; } - jit= MEM_callocN(2+ jitlevel*2*sizeof(float), "jit"); + jit= MEM_callocN((2+ jitlevel*2)*sizeof(float), "jit"); - init_mv_jit(jit, jitlevel, psys->seed, part->jitfac); + /* for small amounts of particles we use regular jitter since it looks + * a bit better, for larger amounts we switch to hammersley sequence + * because it is much faster */ + if(jitlevel < 25) + init_mv_jit(jit, jitlevel, psys->seed, part->jitfac); + else + hammersley_create(jit, jitlevel+1, psys->seed, part->jitfac); BLI_array_randomize(jit, 2*sizeof(float), jitlevel, psys->seed); /* for custom jit or even distribution */ } @@ -1212,7 +1372,7 @@ static void distribute_particles_on_dm(DerivedMesh *finaldm, Object *ob, Particl ParticleThreadContext *ctx; int i, totthread; - pthreads= psys_threads_create(ob, psys, G.scene->r.threads); + pthreads= psys_threads_create(ob, psys); if(!psys_threads_init_distribution(pthreads, finaldm, from)) { psys_threads_free(pthreads); @@ -1231,8 +1391,7 @@ static void distribute_particles_on_dm(DerivedMesh *finaldm, Object *ob, Particl else exec_distribution(&pthreads[0]); - if (from == PART_FROM_FACE) - psys_calc_dmfaces(ob, finaldm, psys); + psys_calc_dmcache(ob, finaldm, psys); ctx= pthreads[0].ctx; if(ctx->dm != finaldm) @@ -1285,11 +1444,16 @@ static void distribute_particles(Object *ob, ParticleSystem *psys, int from) } /* threaded child particle distribution and path caching */ -ParticleThread *psys_threads_create(struct Object *ob, struct ParticleSystem *psys, int totthread) +ParticleThread *psys_threads_create(struct Object *ob, struct ParticleSystem *psys) { ParticleThread *threads; ParticleThreadContext *ctx; - int i; + int i, totthread; + + if(G.scene->r.mode & R_FIXED_THREADS) + totthread= G.scene->r.threads; + else + totthread= BLI_system_thread_count(); threads= MEM_callocN(sizeof(ParticleThread)*totthread, "ParticleThread"); ctx= MEM_callocN(sizeof(ParticleThreadContext), "ParticleThreadContext"); @@ -1326,7 +1490,7 @@ void psys_threads_free(ParticleThread *threads) if(ctx->vg_rough1) MEM_freeN(ctx->vg_rough1); if(ctx->vg_rough2) - MEM_freeN(ctx->vg_roughe); + MEM_freeN(ctx->vg_rough2); if(ctx->vg_roughe) MEM_freeN(ctx->vg_roughe); @@ -1376,7 +1540,7 @@ void initialize_particle(ParticleData *pa, int p, Object *ob, ParticleSystem *ps BLI_srandom(psys->seed+p); - if(part->from!=PART_FROM_PARTICLE){ + if(part->from!=PART_FROM_PARTICLE && part->type!=PART_FLUID){ ma=give_current_material(ob,part->omat); /* TODO: needs some work to make most blendtypes generally usefull */ @@ -1446,7 +1610,7 @@ void initialize_particle(ParticleData *pa, int p, Object *ob, ParticleSystem *ps NormalQuat(pa->r_rot); - if(part->distr!=PART_DISTR_GRID){ + if(part->distr!=PART_DISTR_GRID && part->from != PART_FROM_VERT){ /* any unique random number will do (r_ave[0]) */ if(ptex.exist < 0.5*(1.0+pa->r_ave[0])) pa->flag |= PARS_UNEXIST; @@ -1468,53 +1632,54 @@ static void initialize_all_particles(Object *ob, ParticleSystem *psys, ParticleS for(p=0, pa=psys->particles; p<totpart; p++, pa++) initialize_particle(pa,p,ob,psys,psmd); - /* store the derived mesh face index for each particle */ - icu=find_ipocurve(psys->part->ipo,PART_EMIT_FREQ); - if(icu){ - float time=psys->part->sta, end=psys->part->end; - float v1, v2, a=0.0f, t1,t2, d; + if(psys->part->type != PART_FLUID) { + icu=find_ipocurve(psys->part->ipo,PART_EMIT_FREQ); + if(icu){ + float time=psys->part->sta, end=psys->part->end; + float v1, v2, a=0.0f, t1,t2, d; + + p=0; + pa=psys->particles; + + calc_icu(icu,time); + v1=icu->curval; + if(v1<0.0f) v1=0.0f; + + calc_icu(icu,time+1.0f); + v2=icu->curval; + if(v2<0.0f) v2=0.0f; + + for(p=0, pa=psys->particles; p<totpart && time<end; p++, pa++){ + while(a+0.5f*(v1+v2) < (float)(p+1) && time<end){ + a+=0.5f*(v1+v2); + v1=v2; + time++; + calc_icu(icu,time+1.0f); + v2=icu->curval; + } + if(time<end){ + if(v1==v2){ + pa->time=time+((float)(p+1)-a)/v1; + } + else{ + d=(float)sqrt(v1*v1-2.0f*(v2-v1)*(a-(float)(p+1))); + t1=(-v1+d)/(v2-v1); + t2=(-v1-d)/(v2-v1); - p=0; - pa=psys->particles; + /* the root between 0-1 is the correct one */ + if(t1>0.0f && t1<=1.0f) + pa->time=time+t1; + else + pa->time=time+t2; + } + } - calc_icu(icu,time); - v1=icu->curval; - if(v1<0.0f) v1=0.0f; - - calc_icu(icu,time+1.0f); - v2=icu->curval; - if(v2<0.0f) v2=0.0f; - - for(p=0, pa=psys->particles; p<totpart && time<end; p++, pa++){ - while(a+0.5f*(v1+v2) < (float)(p+1) && time<end){ - a+=0.5f*(v1+v2); - v1=v2; - time++; - calc_icu(icu,time+1.0f); - v2=icu->curval; + pa->dietime = pa->time+pa->lifetime; + pa->flag &= ~PARS_UNEXIST; } - if(time<end){ - if(v1==v2){ - pa->time=time+((float)(p+1)-a)/v1; - } - else{ - d=(float)sqrt(v1*v1-2.0f*(v2-v1)*(a-(float)(p+1))); - t1=(-v1+d)/(v2-v1); - t2=(-v1-d)/(v2-v1); - - /* the root between 0-1 is the correct one */ - if(t1>0.0f && t1<=1.0f) - pa->time=time+t1; - else - pa->time=time+t2; - } + for(; p<totpart; p++, pa++){ + pa->flag |= PARS_UNEXIST; } - - pa->dietime = pa->time+pa->lifetime; - pa->flag &= ~PARS_UNEXIST; - } - for(; p<totpart; p++, pa++){ - pa->flag |= PARS_UNEXIST; } } } @@ -1526,11 +1691,10 @@ void reset_particle(ParticleData *pa, ParticleSystem *psys, ParticleSystemModifi ParticleTexture ptex; ParticleKey state; IpoCurve *icu=0; - float fac, nor[3]={0,0,0},loc[3],tloc[3],vel[3]={0.0,0.0,0.0},rot[4],*q2=0; + float fac, phasefac, nor[3]={0,0,0},loc[3],tloc[3],vel[3]={0.0,0.0,0.0},rot[4],q2[4]; float r_vel[3],r_ave[3],r_rot[4],p_vel[3]={0.0,0.0,0.0}; - float x_vec[3]={1.0,0.0,0.0}, utan[3]={0.0,1.0,0.0}, vtan[3]={0.0,0.0,1.0}; - - float q_one[4]={1.0,0.0,0.0,0.0}, q_phase[4]; + float x_vec[3]={1.0,0.0,0.0}, utan[3]={0.0,1.0,0.0}, vtan[3]={0.0,0.0,1.0}, rot_vec[3]={0.0,0.0,0.0}; + float q_phase[4]; part=psys->part; ptex.ivel=1.0; @@ -1545,11 +1709,12 @@ void reset_particle(ParticleData *pa, ParticleSystem *psys, ParticleSystemModifi tob=ob; tpsys=BLI_findlink(&tob->particlesystem,psys->target_psys-1); - - /*TODO: get precise location of particle at birth*/ - state.time=cfra; - psys_get_particle_state(tob,tpsys,pa->num,&state,1); + state.time = pa->time; + if(pa->num == -1) + memset(&state, 0, sizeof(state)); + else + psys_get_particle_state(tob,tpsys,pa->num,&state,1); psys_get_from_key(&state,loc,nor,rot,0); QuatMulVecf(rot,vtan); @@ -1571,7 +1736,7 @@ void reset_particle(ParticleData *pa, ParticleSystem *psys, ParticleSystemModifi where_is_object_time(ob,pa->time); /* get birth location from object */ - psys_particle_on_emitter(ob,psmd,part->from,pa->num, pa->num_dmcache, pa->fuv,pa->foffset,loc,nor,utan,vtan,0,0); + psys_particle_on_emitter(psmd,part->from,pa->num, pa->num_dmcache, pa->fuv,pa->foffset,loc,nor,utan,vtan,0,0); /* save local coordinates for later */ VECCOPY(tloc,loc); @@ -1579,9 +1744,8 @@ void reset_particle(ParticleData *pa, ParticleSystem *psys, ParticleSystemModifi /* get possible textural influence */ psys_get_texture(ob,give_current_material(ob,part->omat),psmd,psys,pa,&ptex,MAP_PA_IVEL); - if(vg_vel){ - ptex.ivel*=psys_interpolate_value_from_verts(psmd->dm,part->from,pa->num,pa->fuv,vg_vel); - } + if(vg_vel && pa->num != -1) + ptex.ivel*=psys_particle_value_from_verts(psmd->dm,part->from,pa,vg_vel); /* particles live in global space so */ /* let's convert: */ @@ -1596,7 +1760,7 @@ void reset_particle(ParticleData *pa, ParticleSystem *psys, ParticleSystemModifi /* -tangent */ if(part->tanfac!=0.0){ - float phase=vg_rot?2.0f*(psys_interpolate_value_from_verts(psmd->dm,part->from,pa->num,pa->fuv,vg_rot)-0.5f):0.0f; + float phase=vg_rot?2.0f*(psys_particle_value_from_verts(psmd->dm,part->from,pa,vg_rot)-0.5f):0.0f; VecMulf(vtan,-(float)cos(M_PI*(part->tanphase+phase))); fac=-(float)sin(M_PI*(part->tanphase+phase)); VECADDFAC(vtan,vtan,utan,fac); @@ -1630,7 +1794,7 @@ void reset_particle(ParticleData *pa, ParticleSystem *psys, ParticleSystemModifi } /* -rotation */ - if(part->rotmode==PART_ROT_RAND){ + if(part->randrotfac != 0.0f){ QUATCOPY(r_rot,pa->r_rot); Mat4ToQuat(ob->obmat,rot); QuatMul(r_rot,r_rot,rot); @@ -1638,6 +1802,12 @@ void reset_particle(ParticleData *pa, ParticleSystem *psys, ParticleSystemModifi } /* conversion done so now we apply new: */ /* -velocity from: */ + + /* *reactions */ + if(dtime>0.0f){ + VECSUB(vel,pa->state.vel,pa->prev_state.vel); + } + /* *emitter velocity */ if(dtime!=0.0 && part->obfac!=0.0){ VECSUB(vel,loc,pa->state.co); @@ -1650,7 +1820,7 @@ void reset_particle(ParticleData *pa, ParticleSystem *psys, ParticleSystemModifi /* *emitter tangent */ if(part->tanfac!=0.0) - VECADDFAC(vel,vel,vtan,part->tanfac*(vg_tan?psys_interpolate_value_from_verts(psmd->dm,part->from,pa->num,pa->fuv,vg_tan):1.0f)); + VECADDFAC(vel,vel,vtan,part->tanfac*(vg_tan?psys_particle_value_from_verts(psmd->dm,part->from,pa,vg_tan):1.0f)); /* *texture */ /* TODO */ @@ -1681,34 +1851,49 @@ void reset_particle(ParticleData *pa, ParticleSystem *psys, ParticleSystemModifi pa->state.rot[1]=pa->state.rot[2]=pa->state.rot[3]=0.0; if(part->rotmode){ + /* create vector into which rotation is aligned */ switch(part->rotmode){ case PART_ROT_NOR: - VecMulf(nor,-1.0); - q2= vectoquat(nor, OB_POSX, OB_POSZ); - VecMulf(nor,-1.0); + VecCopyf(rot_vec, nor); break; case PART_ROT_VEL: - VecMulf(vel,-1.0); - q2= vectoquat(vel, OB_POSX, OB_POSZ); - VecMulf(vel,-1.0); + VecCopyf(rot_vec, vel); + break; + case PART_ROT_GLOB_X: + case PART_ROT_GLOB_Y: + case PART_ROT_GLOB_Z: + rot_vec[part->rotmode - PART_ROT_GLOB_X] = 1.0f; break; - case PART_ROT_RAND: - q2= r_rot; + case PART_ROT_OB_X: + case PART_ROT_OB_Y: + case PART_ROT_OB_Z: + VecCopyf(rot_vec, ob->obmat[part->rotmode - PART_ROT_OB_X]); break; } - /* how much to rotate from rest position */ - QuatInterpol(rot,q_one,q2,part->rotfac); + + /* create rotation quat */ + VecMulf(rot_vec,-1.0); + vectoquat(rot_vec, OB_POSX, OB_POSZ, q2); + + /* randomize rotation quat */ + if(part->randrotfac!=0.0f) + QuatInterpol(rot, q2, r_rot, part->randrotfac); + else + QuatCopy(rot,q2); - /* phase */ - VecRotToQuat(x_vec,part->phasefac*(float)M_PI,q_phase); + /* rotation phase */ + phasefac = part->phasefac; + if(part->randphasefac != 0.0f) /* abuse r_ave[0] as a random number */ + phasefac += part->randphasefac * pa->r_ave[0]; + VecRotToQuat(x_vec, phasefac*(float)M_PI, q_phase); - /* combine amount & phase */ - QuatMul(pa->state.rot,rot,q_phase); + /* combine base rotation & phase */ + QuatMul(pa->state.rot, rot, q_phase); } /* -angular velocity */ - pa->state.ave[0]=pa->state.ave[1]=pa->state.ave[2]=0.0; + pa->state.ave[0] = pa->state.ave[1] = pa->state.ave[2] = 0.0; if(part->avemode){ switch(part->avemode){ @@ -1729,15 +1914,15 @@ void reset_particle(ParticleData *pa, ParticleSystem *psys, ParticleSystemModifi } } - pa->dietime=pa->time+pa->lifetime; + pa->dietime = pa->time + pa->lifetime; if(pa->time >= cfra) - pa->alive=PARS_UNBORN; + pa->alive = PARS_UNBORN; - pa->state.time=cfra; + pa->state.time = cfra; - pa->stick_ob=0; - pa->flag&=~PARS_STICKY; + pa->stick_ob = 0; + pa->flag &= ~PARS_STICKY; } static void reset_all_particles(Object *ob, ParticleSystem *psys, ParticleSystemModifierData *psmd, float dtime, float cfra, int from) { @@ -1807,7 +1992,8 @@ int psys_count_keyed_targets(Object *ob, ParticleSystem *psys) BLI_freelistN(&lb); return select; } -void set_keyed_keys(Object *ob, ParticleSystem *psys) + +static void set_keyed_keys(Object *ob, ParticleSystem *psys) { Object *kob = ob; ParticleSystem *kpsys = psys; @@ -1817,17 +2003,16 @@ void set_keyed_keys(Object *ob, ParticleSystem *psys) float prevtime, nexttime, keyedtime; /* no proper targets so let's clear and bail out */ - if(psys->totkeyed==0){ + if(psys->totkeyed==0) { free_keyed_keys(psys); psys->flag &= ~PSYS_KEYED; return; } - if(totpart && psys->particles->totkey != totkeys){ + if(totpart && psys->particles->totkey != totkeys) { free_keyed_keys(psys); - psys->particles->keys = MEM_callocN(psys->totpart * totkeys * sizeof(ParticleKey),"Keyed keys"); - + psys->particles->keys = MEM_callocN(psys->totpart*totkeys*sizeof(ParticleKey), "Keyed keys"); psys->particles->totkey = totkeys; for(i=1, pa=psys->particles+1; i<totpart; i++,pa++){ @@ -1839,9 +2024,10 @@ void set_keyed_keys(Object *ob, ParticleSystem *psys) psys->flag &= ~PSYS_KEYED; state.time=-1.0; - for(k=0; k<totkeys; k++){ - for(i=0,pa=psys->particles; i<totpart; i++, pa++){ - psys_get_particle_state(kob, kpsys, i%kpsys->totpart, pa->keys + k, 1); + for(k=0; k<totkeys; k++) { + for(i=0,pa=psys->particles; i<totpart; i++, pa++) { + if(kpsys->totpart > 0) + psys_get_particle_state(kob, kpsys, i%kpsys->totpart, pa->keys + k, 1); if(k==0) pa->keys->time = pa->time; @@ -1923,22 +2109,18 @@ static void react_to_events(ParticleSystem *psys, int pa_num) for(re=psys->reactevents.first; re; re=re->next){ birth=0; if(part->from==PART_FROM_PARTICLE){ - if(pa->num==re->pa_num){ + if(pa->num==re->pa_num && pa->alive==PARS_UNBORN){ if(re->event==PART_EVENT_NEAR){ ParticleData *tpa = re->psys->particles+re->pa_num; float pa_time=tpa->time + pa->foffset*tpa->lifetime; - if(re->time > pa_time){ - pa->alive=PARS_ALIVE; + if(re->time >= pa_time){ pa->time=pa_time; pa->dietime=pa->time+pa->lifetime; } } else{ - if(pa->alive==PARS_UNBORN){ - pa->alive=PARS_ALIVE; - pa->time=re->time; - pa->dietime=pa->time+pa->lifetime; - } + pa->time=re->time; + pa->dietime=pa->time+pa->lifetime; } } } @@ -1946,7 +2128,6 @@ static void react_to_events(ParticleSystem *psys, int pa_num) dist=VecLenf(pa->state.co, re->state.co); if(dist <= re->size){ if(pa->alive==PARS_UNBORN){ - pa->alive=PARS_ALIVE; pa->time=re->time; pa->dietime=pa->time+pa->lifetime; birth=1; @@ -1982,273 +2163,70 @@ void psys_get_reactor_target(Object *ob, ParticleSystem *psys, Object **target_o /************************************************/ /* Point Cache */ /************************************************/ -void clear_particles_from_cache(Object *ob, ParticleSystem *psys, int cfra) -{ - ParticleSystemModifierData *psmd = psys_get_modifier(ob,psys); - int stack_index = modifiers_indexInObject(ob,(ModifierData*)psmd); - BKE_ptcache_id_clear((ID *)ob, PTCACHE_CLEAR_ALL, cfra, stack_index); -} static void write_particles_to_cache(Object *ob, ParticleSystem *psys, int cfra) { - FILE *fp = NULL; - ParticleSystemModifierData *psmd = psys_get_modifier(ob,psys); + PTCacheID pid; + PTCacheFile *pf; ParticleData *pa; - int stack_index = modifiers_indexInObject(ob,(ModifierData*)psmd); - int i, totpart = psys->totpart; + int i, totpart= psys->totpart; - if(totpart == 0) return; + if(totpart == 0) + return; - fp = BKE_ptcache_id_fopen((ID *)ob, 'w', cfra, stack_index); - if(!fp) return; + BKE_ptcache_id_from_particles(&pid, ob, psys); + pf= BKE_ptcache_file_open(&pid, PTCACHE_FILE_WRITE, cfra); + if(!pf) + return; + /* assuming struct consists of tightly packed floats */ for(i=0, pa=psys->particles; i<totpart; i++, pa++) - fwrite(&pa->state, sizeof(ParticleKey), 1, fp); + BKE_ptcache_file_write_floats(pf, (float*)&pa->state, sizeof(ParticleKey)/sizeof(float)); - fclose(fp); + BKE_ptcache_file_close(pf); } + static int get_particles_from_cache(Object *ob, ParticleSystem *psys, int cfra) { - FILE *fp = NULL; - ParticleSystemModifierData *psmd = psys_get_modifier(ob,psys); + PTCacheID pid; + PTCacheFile *pf; ParticleData *pa; - int stack_index = modifiers_indexInObject(ob,(ModifierData*)psmd); - int i, totpart = psys->totpart, ret = 1; + int i, totpart= psys->totpart; - if(totpart == 0) return 0; + if(totpart == 0) + return 0; - fp = BKE_ptcache_id_fopen((ID *)ob, 'r', cfra, stack_index); - if(!fp) - ret = 0; - else { - for(i=0, pa=psys->particles; i<totpart; i++, pa++) - if((fread(&pa->state, sizeof(ParticleKey), 1, fp)) != 1) { - ret = 0; - break; - } - - fclose(fp); + BKE_ptcache_id_from_particles(&pid, ob, psys); + pf= BKE_ptcache_file_open(&pid, PTCACHE_FILE_READ, cfra); + if(!pf) + return 0; + + /* assuming struct consists of tightly packed floats */ + for(i=0, pa=psys->particles; i<totpart; i++, pa++) { + if(cfra!=pa->state.time) + copy_particle_key(&pa->prev_state,&pa->state,1); + if(!BKE_ptcache_file_read_floats(pf, (float*)&pa->state, sizeof(ParticleKey)/sizeof(float))) { + BKE_ptcache_file_close(pf); + return 0; + } } - return ret; + BKE_ptcache_file_close(pf); + + return 1; } + /************************************************/ /* Effectors */ /************************************************/ -static float effector_falloff(PartDeflect *pd, float *eff_velocity, float *vec_to_part) -{ - float eff_dir[3], temp[3]; - float falloff=1.0, fac, r_fac; - - VecCopyf(eff_dir,eff_velocity); - Normalize(eff_dir); - - if(pd->flag & PFIELD_POSZ && Inpf(eff_dir,vec_to_part)<0.0f) - falloff=0.0f; - else switch(pd->falloff){ - case PFIELD_FALL_SPHERE: - fac=VecLength(vec_to_part); - if(pd->flag&PFIELD_USEMAX && fac>pd->maxdist){ - falloff=0.0f; - break; - } - - if(pd->flag & PFIELD_USEMIN){ - if(fac>pd->mindist) - fac+=1.0f-pd->mindist; - else - fac=1.0f; - } - else if(fac<0.001) - fac=0.001f; - - falloff=1.0f/(float)pow((double)fac,(double)pd->f_power); - break; - - case PFIELD_FALL_TUBE: - fac=Inpf(vec_to_part,eff_dir); - if(pd->flag&PFIELD_USEMAX && ABS(fac)>pd->maxdist){ - falloff=0.0f; - break; - } - - VECADDFAC(temp,vec_to_part,eff_dir,-fac); - r_fac=VecLength(temp); - if(pd->flag&PFIELD_USEMAXR && r_fac>pd->maxrad){ - falloff=0.0f; - break; - } - - fac=ABS(fac); - - - if(pd->flag & PFIELD_USEMIN){ - if(fac>pd->mindist) - fac+=1.0f-pd->mindist; - else - fac=1.0f; - } - else if(fac<0.001) - fac=0.001f; - - if(pd->flag & PFIELD_USEMINR){ - if(r_fac>pd->minrad) - r_fac+=1.0f-pd->minrad; - else - r_fac=1.0f; - } - else if(r_fac<0.001) - r_fac=0.001f; - - falloff=1.0f/((float)pow((double)fac,(double)pd->f_power)*(float)pow((double)r_fac,(double)pd->f_power_r)); - - break; - case PFIELD_FALL_CONE: - fac=Inpf(vec_to_part,eff_dir); - if(pd->flag&PFIELD_USEMAX && ABS(fac)>pd->maxdist){ - falloff=0.0f; - break; - } - r_fac=saacos(fac/VecLength(vec_to_part))*180.0f/(float)M_PI; - if(pd->flag&PFIELD_USEMAXR && r_fac>pd->maxrad){ - falloff=0.0f; - break; - } - - if(pd->flag & PFIELD_USEMIN){ - if(fac>pd->mindist) - fac+=1.0f-pd->mindist; - else - fac=1.0f; - } - else if(fac<0.001) - fac=0.001f; - - if(pd->flag & PFIELD_USEMINR){ - if(r_fac>pd->minrad) - r_fac+=1.0f-pd->minrad; - else - r_fac=1.0f; - } - else if(r_fac<0.001) - r_fac=0.001f; - - falloff=1.0f/((float)pow((double)fac,(double)pd->f_power)*(float)pow((double)r_fac,(double)pd->f_power_r)); - - break; -// case PFIELD_FALL_INSIDE: - //for(i=0; i<totface; i++,mface++){ - // VECCOPY(v1,mvert[mface->v1].co); - // VECCOPY(v2,mvert[mface->v2].co); - // VECCOPY(v3,mvert[mface->v3].co); - - // if(AxialLineIntersectsTriangle(a,co1, co2, v2, v3, v1, &lambda)){ - // if(from==PART_FROM_FACE) - // (pa+(int)(lambda*size[a])*a0mul)->flag &= ~PARS_UNEXIST; - // else /* store number of intersections */ - // (pa+(int)(lambda*size[a])*a0mul)->loop++; - // } - // - // if(mface->v4){ - // VECCOPY(v4,mvert[mface->v4].co); - - // if(AxialLineIntersectsTriangle(a,co1, co2, v4, v1, v3, &lambda)){ - // if(from==PART_FROM_FACE) - // (pa+(int)(lambda*size[a])*a0mul)->flag &= ~PARS_UNEXIST; - // else - // (pa+(int)(lambda*size[a])*a0mul)->loop++; - // } - // } - //} - -// break; - } - - return falloff; -} -static void do_physical_effector(short type, float force_val, float distance, float falloff, float size, float damp, - float *eff_velocity, float *vec_to_part, float *velocity, float *field, int planar) -{ - float mag_vec[3]={0,0,0}; - float temp[3], temp2[3]; - float eff_vel[3]; - - VecCopyf(eff_vel,eff_velocity); - Normalize(eff_vel); - - switch(type){ - case PFIELD_WIND: - VECCOPY(mag_vec,eff_vel); - - VecMulf(mag_vec,force_val*falloff); - VecAddf(field,field,mag_vec); - break; - - case PFIELD_FORCE: - if(planar) - Projf(mag_vec,vec_to_part,eff_vel); - else - VecCopyf(mag_vec,vec_to_part); - - VecMulf(mag_vec,force_val*falloff); - VecAddf(field,field,mag_vec); - break; - - case PFIELD_VORTEX: - Crossf(mag_vec,eff_vel,vec_to_part); - Normalize(mag_vec); - - VecMulf(mag_vec,force_val*distance*falloff); - VecAddf(field,field,mag_vec); - - break; - case PFIELD_MAGNET: - if(planar) - VecCopyf(temp,eff_vel); - else - /* magnetic field of a moving charge */ - Crossf(temp,eff_vel,vec_to_part); - - Crossf(temp2,velocity,temp); - VecAddf(mag_vec,mag_vec,temp2); - - VecMulf(mag_vec,force_val*falloff); - VecAddf(field,field,mag_vec); - break; - case PFIELD_HARMONIC: - if(planar) - Projf(mag_vec,vec_to_part,eff_vel); - else - VecCopyf(mag_vec,vec_to_part); - - VecMulf(mag_vec,force_val*falloff); - VecSubf(field,field,mag_vec); - - VecCopyf(mag_vec,velocity); - /* 1.9 is an experimental value to get critical damping at damp=1.0 */ - VecMulf(mag_vec,damp*1.9f*(float)sqrt(force_val)); - VecSubf(field,field,mag_vec); - break; - case PFIELD_NUCLEAR: - /*pow here is root of cosine expression below*/ - //rad=(float)pow(2.0,-1.0/power)*distance/size; - //VECCOPY(mag_vec,vec_to_part); - //Normalize(mag_vec); - //VecMulf(mag_vec,(float)cos(3.0*M_PI/2.0*(1.0-1.0/(pow(rad,power)+1.0)))/(rad+0.2f)); - //VECADDFAC(field,field,mag_vec,force_val); - break; - } -} static void do_texture_effector(Tex *tex, short mode, short is_2d, float nabla, short object, float *pa_co, float obmat[4][4], float force_val, float falloff, float *field) { TexResult result[4]; float tex_co[3], strength, mag_vec[3]; - int i; - - if(tex==0) return; + int hasrgb; + if(tex==NULL) return; - for(i=0; i<4; i++) - result[i].nor=0; + result[0].nor = result[1].nor = result[2].nor = result[3].nor = 0; strength= force_val*falloff;///(float)pow((double)distance,(double)power); @@ -2264,9 +2242,9 @@ static void do_texture_effector(Tex *tex, short mode, short is_2d, float nabla, Mat4Mul3Vecfl(obmat,tex_co); } - multitex_ext(tex, tex_co, NULL,NULL, 1, result); + hasrgb = multitex_ext(tex, tex_co, NULL,NULL, 1, result); - if(mode==PFIELD_TEX_RGB){ + if(hasrgb && mode==PFIELD_TEX_RGB){ mag_vec[0]= (0.5f-result->tr)*strength; mag_vec[1]= (0.5f-result->tg)*strength; mag_vec[2]= (0.5f-result->tb)*strength; @@ -2285,7 +2263,7 @@ static void do_texture_effector(Tex *tex, short mode, short is_2d, float nabla, tex_co[2]+= nabla; multitex_ext(tex, tex_co, NULL,NULL, 1, result+3); - if(mode==PFIELD_TEX_GRAD){ + if(mode==PFIELD_TEX_GRAD || !hasrgb){ /* if we dont have rgb fall back to grad */ mag_vec[0]= (result[0].tin-result[1].tin)*strength; mag_vec[1]= (result[0].tin-result[2].tin)*strength; mag_vec[2]= (result[0].tin-result[3].tin)*strength; @@ -2333,7 +2311,9 @@ static void add_to_effectors(ListBase *lb, Object *ob, Object *obsrc, ParticleSy } } else if(pd->forcefield) + { type |= PSYS_EC_EFFECTOR; + } } if(pd && pd->deflect) @@ -2345,6 +2325,9 @@ static void add_to_effectors(ListBase *lb, Object *ob, Object *obsrc, ParticleSy ec->type=type; ec->distances=0; ec->locations=0; + ec->rng = rng_new(1); + rng_srandom(ec->rng, (unsigned int)(ceil(PIL_check_seconds_timer()))); // use better seed + BLI_addtail(lb, ec); } @@ -2358,11 +2341,14 @@ static void add_to_effectors(ListBase *lb, Object *ob, Object *obsrc, ParticleSy for(i=0; epsys; epsys=epsys->next,i++){ type=0; - if(epsys!=psys){ + if(epsys!=psys || (psys->part->flag & PART_SELF_EFFECT)){ epart=epsys->part; - if(epsys->part->pd && epsys->part->pd->forcefield) + if((epsys->part->pd && epsys->part->pd->forcefield) + || (epsys->part->pd2 && epsys->part->pd2->forcefield)) + { type=PSYS_EC_PARTICLE; + } if(epart->type==PART_REACTOR) { tob=epsys->target_ob; @@ -2377,6 +2363,9 @@ static void add_to_effectors(ListBase *lb, Object *ob, Object *obsrc, ParticleSy ec->ob= ob; ec->type=type; ec->psys_nbr=i; + ec->rng = rng_new(1); + rng_srandom(ec->rng, (unsigned int)(ceil(PIL_check_seconds_timer()))); + BLI_addtail(lb, ec); } } @@ -2384,34 +2373,51 @@ static void add_to_effectors(ListBase *lb, Object *ob, Object *obsrc, ParticleSy } } + +static void psys_init_effectors_recurs(Object *ob, Object *obsrc, ParticleSystem *psys, ListBase *listb, int level) +{ + Group *group; + GroupObject *go; + unsigned int layer= obsrc->lay; + + if(level>MAX_DUPLI_RECUR) return; + + if(ob->lay & layer) { + if(ob->pd || ob->particlesystem.first) + add_to_effectors(listb, ob, obsrc, psys); + + if(ob->dup_group) { + group= ob->dup_group; + for(go= group->gobject.first; go; go= go->next) + psys_init_effectors_recurs(go->ob, obsrc, psys, listb, level+1); + } + } +} + void psys_init_effectors(Object *obsrc, Group *group, ParticleSystem *psys) { - ListBase *listb=&psys->effectors; + ListBase *listb= &psys->effectors; Base *base; - unsigned int layer= obsrc->lay; listb->first=listb->last=0; if(group) { GroupObject *go; - for(go= group->gobject.first; go; go= go->next) { - if( (go->ob->lay & layer) && (go->ob->pd || go->ob->particlesystem.first)) { - add_to_effectors(listb, go->ob, obsrc, psys); - } - } + for(go= group->gobject.first; go; go= go->next) + psys_init_effectors_recurs(go->ob, obsrc, psys, listb, 0); } else { - for(base = G.scene->base.first; base; base= base->next) { - if( (base->lay & layer) && (base->object->pd || base->object->particlesystem.first)) { - add_to_effectors(listb, base->object, obsrc, psys); - } - } + for(base = G.scene->base.first; base; base= base->next) + psys_init_effectors_recurs(base->object, obsrc, psys, listb, 0); } } void psys_end_effectors(ParticleSystem *psys) { + /* NOTE: + ec->ob is not valid in here anymore! - dg + */ ListBase *lb=&psys->effectors; if(lb->first) { ParticleEffectorCache *ec; @@ -2430,23 +2436,28 @@ void psys_end_effectors(ParticleSystem *psys) if(ec->tree) BLI_kdtree_free(ec->tree); + + if(ec->rng) + rng_free(ec->rng); + } BLI_freelistN(lb); } } -static void precalc_effectors(Object *ob, ParticleSystem *psys, ParticleSystemModifierData *psmd) +static void precalc_effectors(Object *ob, ParticleSystem *psys, ParticleSystemModifierData *psmd, float cfra) { ListBase *lb=&psys->effectors; ParticleEffectorCache *ec; ParticleSettings *part=psys->part; ParticleData *pa; float vec2[3],loc[3],*co=0; - int p,totpart,totvert; + int p,totpart; for(ec= lb->first; ec; ec= ec->next) { PartDeflect *pd= ec->ob->pd; + co = NULL; if(ec->type==PSYS_EC_EFFECTOR && pd->forcefield==PFIELD_GUIDE && ec->ob->type==OB_CURVE && part->phystype!=PART_PHYS_BOIDS) { @@ -2467,7 +2478,7 @@ static void precalc_effectors(Object *ob, ParticleSystem *psys, ParticleSystemMo ec->locations=MEM_callocN(totpart*3*sizeof(float),"particle locations"); for(p=0,pa=psys->particles; p<totpart; p++, pa++){ - psys_particle_on_emitter(ob,psmd,part->from,pa->num,pa->num_dmcache,pa->fuv,pa->foffset,loc,0,0,0,0,0); + psys_particle_on_emitter(psmd,part->from,pa->num,pa->num_dmcache,pa->fuv,pa->foffset,loc,0,0,0,0,0); Mat4MulVecfl(ob->obmat,loc); ec->distances[p]=VecLenf(loc,vec); VECSUB(loc,loc,vec); @@ -2475,96 +2486,18 @@ static void precalc_effectors(Object *ob, ParticleSystem *psys, ParticleSystemMo } } } - else if(ec->type==PSYS_EC_DEFLECT){ - DerivedMesh *dm; - MFace *mface=0; - MVert *mvert=0; - int i, totface; - float v1[3],v2[3],v3[3],v4[4], *min, *max; - - if(ob==ec->ob) - dm=psmd->dm; - else{ - psys_disable_all(ec->ob); - - dm=mesh_get_derived_final(ec->ob,0); - - psys_enable_all(ec->ob); - } - - if(dm){ - totvert=dm->getNumVerts(dm); - totface=dm->getNumFaces(dm); - mface=dm->getFaceDataArray(dm,CD_MFACE); - mvert=dm->getVertDataArray(dm,CD_MVERT); - - /* Decide which is faster to calculate by the amount of*/ - /* matrice multiplications needed to convert spaces. */ - /* With size deflect we have to convert allways because */ - /* the object can be scaled nonuniformly (sphere->ellipsoid). */ - if(totvert<2*psys->totpart || part->flag & PART_SIZE_DEFL){ - co=ec->vert_cos=MEM_callocN(sizeof(float)*3*totvert,"Particle deflection vert cos"); - /* convert vert coordinates to global (particle) coordinates */ - for(i=0; i<totvert; i++, co+=3){ - VECCOPY(co,mvert[i].co); - Mat4MulVecfl(ec->ob->obmat,co); - } - co=ec->vert_cos; - } - else - ec->vert_cos=0; - - INIT_MINMAX(ec->ob_minmax,ec->ob_minmax+3); - - min=ec->face_minmax=MEM_callocN(sizeof(float)*6*totface,"Particle deflection face minmax"); - max=min+3; - - for(i=0; i<totface; i++,mface++,min+=6,max+=6){ - if(co){ - VECCOPY(v1,co+3*mface->v1); - VECCOPY(v2,co+3*mface->v2); - VECCOPY(v3,co+3*mface->v3); - } - else{ - VECCOPY(v1,mvert[mface->v1].co); - VECCOPY(v2,mvert[mface->v2].co); - VECCOPY(v3,mvert[mface->v3].co); - } - INIT_MINMAX(min,max); - DO_MINMAX(v1,min,max); - DO_MINMAX(v2,min,max); - DO_MINMAX(v3,min,max); - - if(mface->v4){ - if(co){ - VECCOPY(v4,co+3*mface->v4); - } - else{ - VECCOPY(v4,mvert[mface->v4].co); - } - DO_MINMAX(v4,min,max); - } - - DO_MINMAX(min,ec->ob_minmax,ec->ob_minmax+3); - DO_MINMAX(max,ec->ob_minmax,ec->ob_minmax+3); - } - } - else - ec->face_minmax=0; - } else if(ec->type==PSYS_EC_PARTICLE){ + Object *eob = ec->ob; + ParticleSystem *epsys = BLI_findlink(&eob->particlesystem,ec->psys_nbr); + ParticleSettings *epart = epsys->part; + ParticleData *epa; + int p, totepart = epsys->totpart; + if(psys->part->phystype==PART_PHYS_BOIDS){ - Object *eob = ec->ob; - ParticleSystem *epsys; - ParticleSettings *epart; - ParticleData *epa; ParticleKey state; PartDeflect *pd; - int totepart, p; - epsys= BLI_findlink(&eob->particlesystem,ec->psys_nbr); - epart= epsys->part; + pd= epart->pd; - totepart= epsys->totpart; if(pd->forcefield==PFIELD_FORCE && totepart){ KDTree *tree; @@ -2578,13 +2511,19 @@ static void precalc_effectors(Object *ob, ParticleSystem *psys, ParticleSystemMo BLI_kdtree_balance(tree); } } + + } + else if(ec->type==PSYS_EC_DEFLECT) { + CollisionModifierData *collmd = ( CollisionModifierData * ) ( modifiers_findByType ( ec->ob, eModifierType_Collision ) ); + if(collmd) + collision_move_object(collmd, 1.0, 0.0); } } } /* calculate forces that all effectors apply to a particle*/ -static void do_effectors(int pa_no, ParticleData *pa, ParticleKey *state, Object *ob, ParticleSystem *psys, float *force_field, float *vel,float framestep, float cfra) +void do_effectors(int pa_no, ParticleData *pa, ParticleKey *state, Object *ob, ParticleSystem *psys, float *rootco, float *force_field, float *vel,float framestep, float cfra) { Object *eob; ParticleSystem *epsys; @@ -2595,35 +2534,31 @@ static void do_effectors(int pa_no, ParticleData *pa, ParticleKey *state, Object ListBase *lb=&psys->effectors; ParticleEffectorCache *ec; float distance, vec_to_part[3]; - float falloff; + float falloff, charge = 0.0f; int p; /* check all effector objects for interaction */ if(lb->first){ + if(psys->part->pd && psys->part->pd->forcefield==PFIELD_CHARGE){ + /* Only the charge of the effected particle is used for + interaction, not fall-offs. If the fall-offs aren't the + same this will be unphysical, but for animation this + could be the wanted behavior. If you want physical + correctness the fall-off should be spherical 2.0 anyways. + */ + charge = psys->part->pd->f_strength; + } + if(psys->part->pd2 && psys->part->pd2->forcefield==PFIELD_CHARGE){ + charge += psys->part->pd2->f_strength; + } for(ec = lb->first; ec; ec= ec->next){ eob= ec->ob; if(ec->type & PSYS_EC_EFFECTOR){ pd=eob->pd; if(psys->part->type!=PART_HAIR && psys->part->integrator) where_is_object_time(eob,cfra); - /* Get IPO force strength and fall off values here */ - //if (has_ipo_code(eob->ipo, OB_PD_FSTR)) - // force_val = IPO_GetFloatValue(eob->ipo, OB_PD_FSTR, cfra); - //else - // force_val = pd->f_strength; - - //if (has_ipo_code(eob->ipo, OB_PD_FFALL)) - // ffall_val = IPO_GetFloatValue(eob->ipo, OB_PD_FFALL, cfra); - //else - // ffall_val = pd->f_power; - - //if (has_ipo_code(eob->ipo, OB_PD_FMAXD)) - // maxdist = IPO_GetFloatValue(eob->ipo, OB_PD_FMAXD, cfra); - //else - // maxdist = pd->maxdist; /* use center of object for distance calculus */ - //obloc= eob->obmat[3]; VecSubf(vec_to_part, state->co, eob->obmat[3]); distance = VecLength(vec_to_part); @@ -2631,23 +2566,27 @@ static void do_effectors(int pa_no, ParticleData *pa, ParticleKey *state, Object if(falloff<=0.0f) ; /* don't do anything */ - else if(pd->forcefield==PFIELD_TEXTURE) + else if(pd->forcefield==PFIELD_TEXTURE) { do_texture_effector(pd->tex, pd->tex_mode, pd->flag&PFIELD_TEX_2D, pd->tex_nabla, - pd->flag & PFIELD_TEX_OBJECT, state->co, eob->obmat, - pd->f_strength, falloff, force_field); - else - do_physical_effector(pd->forcefield,pd->f_strength,distance, - falloff,pd->f_dist,pd->f_damp,eob->obmat[2],vec_to_part, - pa->state.vel,force_field,pd->flag&PFIELD_PLANAR); + pd->flag & PFIELD_TEX_OBJECT, (pd->flag & PFIELD_TEX_ROOTCO) ? rootco : state->co, eob->obmat, + pd->f_strength, falloff, force_field); + } else { + do_physical_effector(eob, state->co, pd->forcefield,pd->f_strength,distance, + falloff,0.0,pd->f_damp,eob->obmat[2],vec_to_part, + state->vel,force_field,pd->flag&PFIELD_PLANAR,ec->rng,pd->f_noise,charge,pa->size); + } } if(ec->type & PSYS_EC_PARTICLE){ - int totepart; + int totepart, i; epsys= BLI_findlink(&eob->particlesystem,ec->psys_nbr); epart= epsys->part; - pd= epart->pd; + pd=epart->pd; totepart= epsys->totpart; - - if(pd->forcefield==PFIELD_HARMONIC){ + + if(totepart <= 0) + continue; + + if(pd && pd->forcefield==PFIELD_HARMONIC){ /* every particle is mapped to only one harmonic effector particle */ p= pa_no%epsys->totpart; totepart= p+1; @@ -2659,33 +2598,29 @@ static void do_effectors(int pa_no, ParticleData *pa, ParticleKey *state, Object epsys->lattice=psys_get_lattice(ob,psys); for(; p<totepart; p++){ + /* particle skips itself as effector */ + if(epsys==psys && p == pa_no) continue; + epa = epsys->particles + p; - estate.time=-1.0; + estate.time=cfra; if(psys_get_particle_state(eob,epsys,p,&estate,0)){ VECSUB(vec_to_part, state->co, estate.co); distance = VecLength(vec_to_part); - - //if(pd->forcefield==PFIELD_HARMONIC){ - // //if(cfra < epa->time + radius){ /* radius is fade-in in ui */ - // // eforce*=(cfra-epa->time)/radius; - // //} - //} - //else{ - // /* Limit minimum distance to effector particle so that */ - // /* the force is not too big */ - // if (distance < 0.001) distance = 0.001f; - //} - falloff=effector_falloff(pd,estate.vel,vec_to_part); + for(i=0, pd = epart->pd; i<2; i++,pd = epart->pd2) { + if(pd==NULL || pd->forcefield==0) continue; - if(falloff<=0.0f) - ; /* don't do anything */ - else - do_physical_effector(pd->forcefield,pd->f_strength,distance, - falloff,epart->size,pd->f_damp,estate.vel,vec_to_part, - state->vel,force_field,0); + falloff=effector_falloff(pd,estate.vel,vec_to_part); + + if(falloff<=0.0f) + ; /* don't do anything */ + else + do_physical_effector(eob, state->co, pd->forcefield,pd->f_strength,distance, + falloff,epart->size,pd->f_damp,estate.vel,vec_to_part, + state->vel,force_field,0, ec->rng, pd->f_noise,charge,pa->size); + } } - else if(pd->forcefield==PFIELD_HARMONIC && cfra-framestep <= epa->dietime && cfra>epa->dietime){ + else if(pd && pd->forcefield==PFIELD_HARMONIC && cfra-framestep <= epa->dietime && cfra>epa->dietime){ /* first step after key release */ psys_get_particle_state(eob,epsys,p,&estate,1); VECADD(vel,vel,estate.vel); @@ -2706,7 +2641,7 @@ static void do_effectors(int pa_no, ParticleData *pa, ParticleKey *state, Object /* Newtonian physics */ /************************************************/ /* gathers all forces that effect particles and calculates a new state for the particle */ -static void apply_particle_forces(int pa_no, ParticleData *pa, Object *ob, ParticleSystem *psys, ParticleSettings *part, float timestep, float dfra, float cfra, ParticleKey *state) +static void apply_particle_forces(int pa_no, ParticleData *pa, Object *ob, ParticleSystem *psys, ParticleSettings *part, float timestep, float dfra, float cfra) { ParticleKey states[5], tkey; float force[3],tvel[3],dx[4][3],dv[4][3]; @@ -2714,7 +2649,7 @@ static void apply_particle_forces(int pa_no, ParticleData *pa, Object *ob, Parti int i, steps=1; /* maintain angular velocity */ - VECCOPY(state->ave,pa->state.ave); + VECCOPY(pa->state.ave,pa->prev_state.ave); if(part->flag & PART_SIZEMASS) pa_mass*=pa->size; @@ -2737,7 +2672,8 @@ static void apply_particle_forces(int pa_no, ParticleData *pa, Object *ob, Parti force[0]=force[1]=force[2]=0.0; tvel[0]=tvel[1]=tvel[2]=0.0; /* add effectors */ - do_effectors(pa_no,pa,states+i,ob,psys,force,tvel,dfra,fra); + if(part->type != PART_HAIR) + do_effectors(pa_no,pa,states+i,ob,psys,states->co,force,tvel,dfra,fra); /* calculate air-particle interaction */ if(part->dragfac!=0.0f){ @@ -2757,17 +2693,14 @@ static void apply_particle_forces(int pa_no, ParticleData *pa, Object *ob, Parti /* add global acceleration (gravitation) */ VECADD(force,force,part->acc); - - //VecMulf(force,dtime); /* calculate next state */ VECADD(states[i].vel,states[i].vel,tvel); - //VecMulf(force,0.5f*dt); switch(part->integrator){ case PART_INT_EULER: - VECADDFAC(state->co,states->co,states->vel,dtime); - VECADDFAC(state->vel,states->vel,force,dtime); + VECADDFAC(pa->state.co,states->co,states->vel,dtime); + VECADDFAC(pa->state.vel,states->vel,force,dtime); break; case PART_INT_MIDPOINT: if(i==0){ @@ -2776,8 +2709,8 @@ static void apply_particle_forces(int pa_no, ParticleData *pa, Object *ob, Parti fra=psys->cfra+0.5f*dfra; } else{ - VECADDFAC(state->co,states->co,states[1].vel,dtime); - VECADDFAC(state->vel,states->vel,force,dtime); + VECADDFAC(pa->state.co,states->co,states[1].vel,dtime); + VECADDFAC(pa->state.vel,states->vel,force,dtime); } break; case PART_INT_RK4: @@ -2817,77 +2750,78 @@ static void apply_particle_forces(int pa_no, ParticleData *pa, Object *ob, Parti VECCOPY(dv[3],force); VecMulf(dv[3],dtime); - VECADDFAC(state->co,states->co,dx[0],1.0f/6.0f); - VECADDFAC(state->co,state->co,dx[1],1.0f/3.0f); - VECADDFAC(state->co,state->co,dx[2],1.0f/3.0f); - VECADDFAC(state->co,state->co,dx[3],1.0f/6.0f); + VECADDFAC(pa->state.co,states->co,dx[0],1.0f/6.0f); + VECADDFAC(pa->state.co,pa->state.co,dx[1],1.0f/3.0f); + VECADDFAC(pa->state.co,pa->state.co,dx[2],1.0f/3.0f); + VECADDFAC(pa->state.co,pa->state.co,dx[3],1.0f/6.0f); - VECADDFAC(state->vel,states->vel,dv[0],1.0f/6.0f); - VECADDFAC(state->vel,state->vel,dv[1],1.0f/3.0f); - VECADDFAC(state->vel,state->vel,dv[2],1.0f/3.0f); - VECADDFAC(state->vel,state->vel,dv[3],1.0f/6.0f); + VECADDFAC(pa->state.vel,states->vel,dv[0],1.0f/6.0f); + VECADDFAC(pa->state.vel,pa->state.vel,dv[1],1.0f/3.0f); + VECADDFAC(pa->state.vel,pa->state.vel,dv[2],1.0f/3.0f); + VECADDFAC(pa->state.vel,pa->state.vel,dv[3],1.0f/6.0f); } break; } - //VECADD(states[i+1].co,states[i+1].co,force); } /* damp affects final velocity */ if(part->dampfac!=0.0) - VecMulf(state->vel,1.0f-part->dampfac); + VecMulf(pa->state.vel,1.0f-part->dampfac); /* finally we do guides */ time=(cfra-pa->time)/pa->lifetime; CLAMP(time,0.0,1.0); - VECCOPY(tkey.co,state->co); - VECCOPY(tkey.vel,state->vel); - tkey.time=state->time; - if(do_guide(&tkey,pa_no,time,&psys->effectors)){ - VECCOPY(state->co,tkey.co); - /* guides don't produce valid velocity */ - VECSUB(state->vel,tkey.co,pa->state.co); - VecMulf(state->vel,1.0f/dtime); - state->time=tkey.time; + VECCOPY(tkey.co,pa->state.co); + VECCOPY(tkey.vel,pa->state.vel); + tkey.time=pa->state.time; + + if(part->type != PART_HAIR) { + if(do_guide(&tkey,pa_no,time,&psys->effectors)) { + VECCOPY(pa->state.co,tkey.co); + /* guides don't produce valid velocity */ + VECSUB(pa->state.vel,tkey.co,pa->prev_state.co); + VecMulf(pa->state.vel,1.0f/dtime); + pa->state.time=tkey.time; + } } } -static void rotate_particle(ParticleSettings *part, ParticleData *pa, float dfra, float timestep, ParticleKey *state) +static void rotate_particle(ParticleSettings *part, ParticleData *pa, float dfra, float timestep) { float rotfac, rot1[4], rot2[4]={1.0,0.0,0.0,0.0}, dtime=dfra*timestep; if((part->flag & PART_ROT_DYN)==0){ - if(ELEM(part->avemode,PART_AVE_SPIN,PART_AVE_VEL)){ + if(part->avemode==PART_AVE_SPIN){ float angle; - float len1 = VecLength(pa->state.vel); - float len2 = VecLength(state->vel); + float len1 = VecLength(pa->prev_state.vel); + float len2 = VecLength(pa->state.vel); if(len1==0.0f || len2==0.0f) - state->ave[0]=state->ave[1]=state->ave[2]=0.0f; + pa->state.ave[0]=pa->state.ave[1]=pa->state.ave[2]=0.0f; else{ - Crossf(state->ave,pa->state.vel,state->vel); - Normalize(state->ave); - angle=Inpf(pa->state.vel,state->vel)/(len1*len2); - VecMulf(state->ave,saacos(angle)/dtime); + Crossf(pa->state.ave,pa->prev_state.vel,pa->state.vel); + Normalize(pa->state.ave); + angle=Inpf(pa->prev_state.vel,pa->state.vel)/(len1*len2); + VecMulf(pa->state.ave,saacos(angle)/dtime); } - } - if(part->avemode == PART_AVE_SPIN) - VecRotToQuat(state->vel,dtime*part->avefac,rot2); + VecRotToQuat(pa->state.vel,dtime*part->avefac,rot2); + } } - rotfac=VecLength(state->ave); + rotfac=VecLength(pa->state.ave); if(rotfac==0.0){ /* QuatOne (in VecRotToQuat) doesn't give unit quat [1,0,0,0]?? */ rot1[0]=1.0; rot1[1]=rot1[2]=rot1[3]=0; } else{ - VecRotToQuat(state->ave,rotfac*dtime,rot1); + VecRotToQuat(pa->state.ave,rotfac*dtime,rot1); } - QuatMul(state->rot,rot1,pa->state.rot); - QuatMul(state->rot,rot2,state->rot); + QuatMul(pa->state.rot,rot1,pa->prev_state.rot); + QuatMul(pa->state.rot,rot2,pa->state.rot); /* keep rotation quat in good health */ - NormalQuat(state->rot); + NormalQuat(pa->state.rot); } /* convert from triangle barycentric weights to quad mean value weights */ @@ -2922,7 +2856,7 @@ int psys_intersect_dm(Object *ob, DerivedMesh *dm, float *vert_cos, float *co1, dm=mesh_get_derived_final(ob,0); if(dm==0) - mesh_get_derived_deform(ob,0); + dm=mesh_get_derived_deform(ob,0); psys_enable_all(ob); @@ -3032,37 +2966,122 @@ int psys_intersect_dm(Object *ob, DerivedMesh *dm, float *vert_cos, float *co1, } return intersect; } + +/* container for moving data between deflet_particle and particle_intersect_face */ +typedef struct ParticleCollision +{ + struct Object *ob, *ob_t; // collided and current objects + struct CollisionModifierData *md; // collision modifier for ob_t; + float nor[3]; // normal at collision point + float vel[3]; // velocity of collision point + float co1[3], co2[3]; // ray start and end points + float ray_len; // original length of co2-co1, needed for collision time evaluation + float t; // time of previous collision, needed for substracting face velocity +} +ParticleCollision; + +static void particle_intersect_face(void *userdata, int index, const BVHTreeRay *ray, BVHTreeRayHit *hit) +{ + ParticleCollision *col = (ParticleCollision *) userdata; + MFace *face = col->md->mfaces + index; + MVert *x = col->md->x; + MVert *v = col->md->current_v; + float vel[3], co1[3], co2[3], uv[2], ipoint[3], temp[3], t; + + float *t0, *t1, *t2, *t3; + t0 = x[ face->v1 ].co; + t1 = x[ face->v2 ].co; + t2 = x[ face->v3 ].co; + t3 = face->v4 ? x[ face->v4].co : NULL; + + /* calculate average velocity of face */ + VECCOPY(vel, v[ face->v1 ].co); + VECADD(vel, vel, v[ face->v2 ].co); + VECADD(vel, vel, v[ face->v3 ].co); + VecMulf(vel, 0.33334f); + + /* substract face velocity, in other words convert to + a coordinate system where only the particle moves */ + VECADDFAC(co1, col->co1, vel, -col->t); + VECSUB(co2, col->co2, vel); + + do + { + if(ray->radius == 0.0f) { + if(LineIntersectsTriangle(co1, co2, t0, t1, t2, &t, uv)) { + if(t >= 0.0f && t < hit->dist/col->ray_len) { + hit->dist = col->ray_len * t; + hit->index = index; + + /* calculate normal that's facing the particle */ + CalcNormFloat(t0, t1, t2, col->nor); + VECSUB(temp, co2, co1); + if(Inpf(col->nor, temp) > 0.0f) + VecMulf(col->nor, -1.0f); + + VECCOPY(col->vel,vel); + + col->ob = col->ob_t; + } + } + } + else { + if(SweepingSphereIntersectsTriangleUV(co1, co2, ray->radius, t0, t1, t2, &t, ipoint)) { + if(t >=0.0f && t < hit->dist/col->ray_len) { + hit->dist = col->ray_len * t; + hit->index = index; + + VecLerpf(temp, co1, co2, t); + + VECSUB(col->nor, temp, ipoint); + Normalize(col->nor); + + VECCOPY(col->vel,vel); + + col->ob = col->ob_t; + } + } + } + + t1 = t2; + t2 = t3; + t3 = NULL; + + } while(t2); +} /* particle - mesh collision code */ /* in addition to basic point to surface collisions handles friction & damping,*/ /* angular momentum <-> linear momentum and swept sphere - mesh collisions */ /* 1. check for all possible deflectors for closest intersection on particle path */ /* 2. if deflection was found kill the particle or calculate new coordinates */ -static void deflect_particle(Object *pob, ParticleSystemModifierData *psmd, ParticleSystem *psys, ParticleSettings *part, ParticleData *pa, int p, float dfra, float cfra, ParticleKey *state, int *pa_die){ - Object *ob, *min_ob; - MFace *mface; - MVert *mvert; - DerivedMesh *dm; +static void deflect_particle(Object *pob, ParticleSystemModifierData *psmd, ParticleSystem *psys, ParticleSettings *part, ParticleData *pa, int p, float timestep, float dfra, float cfra){ + Object *ob = NULL; ListBase *lb=&psys->effectors; ParticleEffectorCache *ec; - ParticleKey cstate; - float imat[4][4]; - float co1[3],co2[3],def_loc[3],def_nor[3],unit_nor[3],def_tan[3],dvec[3],def_vel[3],dave[3],dvel[3]; - float pa_minmax[6]; - float min_w[4], zerovec[3]={0.0,0.0,0.0}, ipoint[3]; - float min_d,dotprod,damp,frict,o_len,d_len,radius=-1.0f; - int min_face=0, intersect=1, through=0; - short deflections=0, global=0; + ParticleKey reaction_state; + ParticleCollision col; + BVHTreeRayHit hit; + float ray_dir[3], zerovec[3]={0.0,0.0,0.0}; + float radius = ((part->flag & PART_SIZE_DEFL)?pa->size:0.0f); + int deflections=0, max_deflections=10; - VECCOPY(def_loc,pa->state.co); - VECCOPY(def_vel,pa->state.vel); + VECCOPY(col.co1, pa->prev_state.co); + VECCOPY(col.co2, pa->state.co); + col.t = 0.0f; /* 10 iterations to catch multiple deflections */ - if(lb->first) while(deflections<10){ - intersect=0; - global=0; - min_d=20000.0; - min_ob=NULL; + if(lb->first) while(deflections < max_deflections){ /* 1. */ + + VECSUB(ray_dir, col.co2, col.co1); + hit.index = -1; + hit.dist = col.ray_len = VecLength(ray_dir); + + /* even if particle is stationary we want to check for moving colliders */ + /* if hit.dist is zero the bvhtree_ray_cast will just ignore everything */ + if(hit.dist == 0.0f) + hit.dist = col.ray_len = 0.000001f; + for(ec=lb->first; ec; ec=ec->next){ if(ec->type & PSYS_EC_DEFLECT){ ob= ec->ob; @@ -3070,256 +3089,168 @@ static void deflect_particle(Object *pob, ParticleSystemModifierData *psmd, Part if(part->type!=PART_HAIR) where_is_object_time(ob,cfra); - if(ob==pob){ - dm=psmd->dm; - /* particles should not collide with emitter at birth */ - if(pa->time < cfra && pa->time >= psys->cfra) - continue; - } - else - dm=0; - - VECCOPY(co1,def_loc); - VECCOPY(co2,state->co); - - if(ec->vert_cos==0){ - /* convert particle coordinates to object coordinates */ - Mat4Invert(imat,ob->obmat); - - Mat4MulVecfl(imat,co1); - Mat4MulVecfl(imat,co2); - } + /* particles should not collide with emitter at birth */ + if(ob==pob && pa->time < cfra && pa->time >= psys->cfra) + continue; - INIT_MINMAX(pa_minmax,pa_minmax+3); - DO_MINMAX(co1,pa_minmax,pa_minmax+3); - DO_MINMAX(co2,pa_minmax,pa_minmax+3); - if(part->flag&PART_SIZE_DEFL){ - pa_minmax[0]-=pa->size; - pa_minmax[1]-=pa->size; - pa_minmax[2]-=pa->size; - pa_minmax[3]+=pa->size; - pa_minmax[4]+=pa->size; - pa_minmax[5]+=pa->size; - - radius=pa->size; - } + col.md = ( CollisionModifierData * ) ( modifiers_findByType ( ec->ob, eModifierType_Collision ) ); + col.ob_t = ob; - if(ec->face_minmax==0 || AabbIntersectAabb(pa_minmax,pa_minmax+3,ec->ob_minmax,ec->ob_minmax+3)) - if(psys_intersect_dm(ob,dm,ec->vert_cos,co1,co2,&min_d,&min_face,min_w, - ec->face_minmax,pa_minmax,radius,ipoint)){ - min_ob=ob; - if(ec->vert_cos) - global=1; - else - global=0; - } + if(col.md && col.md->bvhtree) + BLI_bvhtree_ray_cast(col.md->bvhtree, col.co1, ray_dir, radius, &hit, particle_intersect_face, &col); } } /* 2. */ - if(min_ob){ - BLI_srandom((int)cfra+p); - ob=min_ob; - - if(ob==pob){ - dm=psmd->dm; - } - else{ - psys_disable_all(ob); - - dm=mesh_get_derived_final(ob,0); - - psys_enable_all(ob); - } - - mface=dm->getFaceDataArray(dm,CD_MFACE); - mface+=min_face; - mvert=dm->getVertDataArray(dm,CD_MVERT); - - - /* permeability check */ - if(BLI_frand()<ob->pd->pdef_perm) - through=1; - else - through=0; - - if(through==0 && (part->flag & PART_DIE_ON_COL || ob->pd->flag & PDEFLE_KILL_PART)){ - pa->dietime = cfra-(1.0f-min_d)*dfra; - VecLerpf(def_loc,co1,co2,min_d); - - if(global==0) - Mat4MulVecfl(ob->obmat,def_loc); - - VECCOPY(state->co,def_loc); - VecLerpf(state->vel,pa->state.vel,state->vel,min_d); - QuatInterpol(state->rot,pa->state.rot,state->rot,min_d); - VecLerpf(state->ave,pa->state.ave,state->ave,min_d); + if(hit.index>=0) { + PartDeflect *pd = col.ob->pd; + int through = (BLI_frand() < pd->pdef_perm) ? 1 : 0; + float co[3]; /* point of collision */ + float vec[3]; /* movement through collision */ + float t = hit.dist/col.ray_len; /* time of collision between this iteration */ + float dt = col.t + t * (1.0f - col.t); /* time of collision between frame change*/ + + VecLerpf(co, col.co1, col.co2, t); + VECSUB(vec, col.co2, col.co1); + + VecMulf(col.vel, 1.0f-col.t); + + /* particle dies in collision */ + if(through == 0 && (part->flag & PART_DIE_ON_COL || pd->flag & PDEFLE_KILL_PART)) { + pa->alive = PARS_DYING; + pa->dietime = pa->state.time + (cfra - pa->state.time) * dt; + + /* we have to add this for dying particles too so that reactors work correctly */ + VECADDFAC(co, co, col.nor, (through ? -0.0001f : 0.0001f)); - *pa_die=1; + VECCOPY(pa->state.co, co); + VecLerpf(pa->state.vel, pa->prev_state.vel, pa->state.vel, dt); + QuatInterpol(pa->state.rot, pa->prev_state.rot, pa->state.rot, dt); + VecLerpf(pa->state.ave, pa->prev_state.ave, pa->state.ave, dt); /* particle is dead so we don't need to calculate further */ - deflections=10; + deflections=max_deflections; /* store for reactors */ - copy_particle_key(&cstate,state,0); + copy_particle_key(&reaction_state,&pa->state,0); if(part->flag & PART_STICKY){ pa->stick_ob=ob; pa->flag |= PARS_STICKY; - //stick_particle_to_object(ob,pa,state); } } - else{ - VecLerpf(def_loc,co1,co2,min_d); - - if(radius>0.0f){ - VECSUB(unit_nor,def_loc,ipoint); - } - else{ - /* get deflection point & normal */ - psys_interpolate_face(mvert,mface,0,0,min_w,ipoint,unit_nor,0,0,0,0); - if(global){ - Mat4Mul3Vecfl(ob->obmat,unit_nor); - Mat4MulVecfl(ob->obmat,ipoint); - } - } - - Normalize(unit_nor); - - VECSUB(dvec,co1,co2); - /* scale to remaining length after deflection */ - VecMulf(dvec,1.0f-min_d); + else { + float nor_vec[3], tan_vec[3], tan_vel[3], vel[3]; + float damp, frict; + float inp, inp_v; + + /* get damping & friction factors */ + damp = pd->pdef_damp + pd->pdef_rdamp * 2 * (BLI_frand() - 0.5f); + CLAMP(damp,0.0,1.0); - /* flip normal to face particle */ - if(Inpf(unit_nor,dvec)<0.0f) - VecMulf(unit_nor,-1.0f); + frict = pd->pdef_frict + pd->pdef_rfrict * 2 * (BLI_frand() - 0.5f); + CLAMP(frict,0.0,1.0); - /* store for easy velocity calculation */ - o_len=VecLength(dvec); + /* treat normal & tangent components separately */ + inp = Inpf(col.nor, vec); + inp_v = Inpf(col.nor, col.vel); - /* project particle movement to normal & create tangent */ - dotprod=Inpf(dvec,unit_nor); - VECCOPY(def_nor,unit_nor); - VecMulf(def_nor,dotprod); - VECSUB(def_tan,def_nor,dvec); + VECADDFAC(tan_vec, vec, col.nor, -inp); + VECADDFAC(tan_vel, col.vel, col.nor, -inp_v); + if((part->flag & PART_ROT_DYN)==0) + VecLerpf(tan_vec, tan_vec, tan_vel, frict); - damp=ob->pd->pdef_damp+ob->pd->pdef_rdamp*2*(BLI_frand()-0.5f); + VECCOPY(nor_vec, col.nor); + inp *= 1.0f - damp; - /* create location after deflection */ - VECCOPY(dvec,def_nor); - damp=ob->pd->pdef_damp+ob->pd->pdef_rdamp*2*(BLI_frand()-0.5f); - CLAMP(damp,0.0,1.0); - VecMulf(dvec,1.0f-damp); if(through) - VecMulf(dvec,-1.0); - - frict=ob->pd->pdef_frict+ob->pd->pdef_rfrict*2.0f*(BLI_frand()-0.5f); - CLAMP(frict,0.0,1.0); - VECADDFAC(dvec,dvec,def_tan,1.0f-frict); + inp_v *= damp; - /* store for easy velocity calculation */ - d_len=VecLength(dvec); + /* special case for object hitting the particle from behind */ + if(through==0 && ((inp_v>0 && inp>0 && inp_v>inp) || (inp_v<0 && inp<0 && inp_v<inp))) + VecMulf(nor_vec, inp_v); + else + VecMulf(nor_vec, inp_v + (through ? 1.0f : -1.0f) * inp); - /* just to be sure we don't hit the current face again */ - if(through){ - VECADDFAC(ipoint,ipoint,unit_nor,-0.0001f); - VECADDFAC(def_loc,def_loc,unit_nor,-0.0001f); + /* angular <-> linear velocity - slightly more physical and looks even nicer than before */ + if(part->flag & PART_ROT_DYN) { + float surface_vel[3], rot_vel[3], friction[3], dave[3], dvel[3]; - if(part->flag & PART_ROT_DYN){ - VECADDFAC(def_tan,def_tan,unit_nor,-0.0001f); - VECADDFAC(def_nor,def_nor,unit_nor,-0.0001f); - } - } - else{ - VECADDFAC(ipoint,ipoint,unit_nor,0.0001f); - VECADDFAC(def_loc,def_loc,unit_nor,0.0001f); + /* apparent velocity along collision surface */ + VECSUB(surface_vel, tan_vec, tan_vel); - if(part->flag & PART_ROT_DYN){ - VECADDFAC(def_tan,def_tan,unit_nor,0.0001f); - VECADDFAC(def_nor,def_nor,unit_nor,0.0001f); - } - } + /* direction of rolling friction */ + Crossf(rot_vel, pa->state.ave, col.nor); + /* convert to current dt */ + VecMulf(rot_vel, (timestep*dfra) * (1.0f - col.t)); + VecMulf(rot_vel, pa->size); - /* lets get back to global space */ - if(global==0){ - Mat4Mul3Vecfl(ob->obmat,dvec); - Mat4MulVecfl(ob->obmat,ipoint); - Mat4MulVecfl(ob->obmat,def_loc);/* def_loc remains as intersection point for next iteration */ - } + /* apply sliding friction */ + VECSUB(surface_vel, surface_vel, rot_vel); + VECCOPY(friction, surface_vel); - /* store for reactors */ - VECCOPY(cstate.co,ipoint); - VecLerpf(cstate.vel,pa->state.vel,state->vel,min_d); - QuatInterpol(cstate.rot,pa->state.rot,state->rot,min_d); - - /* slightly unphysical but looks nice enough */ - if(part->flag & PART_ROT_DYN){ - if(global==0){ - Mat4Mul3Vecfl(ob->obmat,def_nor); - Mat4Mul3Vecfl(ob->obmat,def_tan); - } + VecMulf(surface_vel, 1.0 - frict); + VecMulf(friction, frict); - Normalize(def_tan); - Normalize(def_nor); - VECCOPY(unit_nor,def_nor); + /* sliding changes angular velocity */ + Crossf(dave, col.nor, friction); + VecMulf(dave, 1.0f/MAX2(pa->size, 0.001)); - /* create normal velocity */ - VecMulf(def_nor,Inpf(pa->state.vel,def_nor)); + /* we assume rolling friction is around 0.01 of sliding friction */ + VecMulf(rot_vel, 1.0 - frict*0.01); - /* create tangential velocity */ - VecMulf(def_tan,Inpf(pa->state.vel,def_tan)); - - /* angular velocity change due to tangential velocity */ - Crossf(dave,unit_nor,def_tan); - VecMulf(dave,1.0f/pa->size); - - /* linear velocity change due to angular velocity */ - VecMulf(unit_nor,pa->size); /* point of impact from particle center */ - Crossf(dvel,pa->state.ave,unit_nor); + /* change in angular velocity has to be added to the linear velocity too */ + Crossf(dvel, dave, col.nor); + VecMulf(dvel, pa->size); + VECADD(rot_vel, rot_vel, dvel); - if(through) - VecMulf(def_nor,-1.0); + VECADD(surface_vel, surface_vel, rot_vel); + VECADD(tan_vec, surface_vel, tan_vel); - VecMulf(def_nor,1.0f-damp); - VECSUB(dvel,dvel,def_nor); + /* convert back to normal time */ + VecMulf(dave, 1.0f/MAX2((timestep*dfra) * (1.0f - col.t), 0.00001)); - VecMulf(dvel,1.0f-frict); - VecMulf(dave,1.0f-frict); + VecMulf(pa->state.ave, 1.0 - frict*0.01); + VECADD(pa->state.ave, pa->state.ave, dave); } - - if(d_len<0.001 && VecLength(pa->state.vel)<0.001){ - /* kill speed to stop slipping */ - VECCOPY(state->vel,zerovec); - VECCOPY(state->co,def_loc); - if(part->flag & PART_ROT_DYN) - VECCOPY(state->ave,zerovec); - deflections=10; - } - else{ - /* apply new coordinates */ - VECADD(state->co,def_loc,dvec); + /* combine components together again */ + VECADD(vec, nor_vec, tan_vec); + + /* calculate velocity from collision vector */ + VECCOPY(vel, vec); + VecMulf(vel, 1.0f/MAX2((timestep*dfra) * (1.0f - col.t), 0.00001)); - Normalize(dvec); + /* make sure we don't hit the current face again */ + VECADDFAC(co, co, col.nor, (through ? -0.0001f : 0.0001f)); - /* we have to use original velocity because otherwise we get slipping */ - /* when forces like gravity balance out damping & friction */ - VecMulf(dvec,VecLength(pa->state.vel)*(d_len/o_len)); - VECCOPY(state->vel,dvec); + /* store state for reactors */ + VECCOPY(reaction_state.co, co); + VecLerpf(reaction_state.vel, pa->prev_state.vel, pa->state.vel, dt); + QuatInterpol(reaction_state.rot, pa->prev_state.rot, pa->state.rot, dt); - if(part->flag & PART_ROT_DYN){ - VECADD(state->vel,state->vel,dvel); - VecMulf(state->vel,0.5); - VECADD(state->ave,state->ave,dave); - VecMulf(state->ave,0.5); + /* set coordinates for next iteration */ + VECCOPY(col.co1, co); + VECADDFAC(col.co2, co, vec, 1.0f - t); + col.t = dt; + + if(VecLength(vec) < 0.001 && VecLength(pa->state.vel) < 0.001) { + /* kill speed to stop slipping */ + VECCOPY(pa->state.vel,zerovec); + VECCOPY(pa->state.co, co); + if(part->flag & PART_ROT_DYN) { + VECCOPY(pa->state.ave,zerovec); } } + else { + VECCOPY(pa->state.co, col.co2); + VECCOPY(pa->state.vel, vel); + } } deflections++; - cstate.time=cfra-(1.0f-min_d)*dfra; - //particle_react_to_collision(min_ob,pob,psys,pa,p,&cstate); - push_reaction(pob,psys,p,PART_EVENT_COLLIDE,&cstate); + reaction_state.time = cfra - (1.0f - dt) * dfra; + push_reaction(col.ob, psys, p, PART_EVENT_COLLIDE, &reaction_state); } else return; @@ -3380,7 +3311,9 @@ static int boid_see_mesh(ListBase *lb, Object *pob, ParticleSystem *psys, float else{ psys_disable_all(ob); - dm=mesh_get_derived_deform(ob,0); + dm=mesh_get_derived_final(ob,0); + if(dm==0) + dm=mesh_get_derived_deform(ob,0); psys_enable_all(ob); } @@ -3483,7 +3416,7 @@ static int add_boid_acc(BoidVecFunc *bvf, float lat_max, float tan_max, float *l } } /* determines the acceleration that the boid tries to acchieve */ -static void boid_brain(BoidVecFunc *bvf, ParticleData *pa, Object *ob, ParticleSystem *psys, ParticleSettings *part, KDTree *tree, float timestep, float cfra, float *acc, int *pa_die) +static void boid_brain(BoidVecFunc *bvf, ParticleData *pa, Object *ob, ParticleSystem *psys, ParticleSettings *part, KDTree *tree, float timestep, float cfra, float *acc) { ParticleData *pars=psys->particles; KDTreeNearest ptn[MAX_BOIDNEIGHBOURS+1]; @@ -3514,18 +3447,18 @@ static void boid_brain(BoidVecFunc *bvf, ParticleData *pa, Object *ob, ParticleS switch(part->boidrule[i]){ case BOID_COLLIDE: /* collision avoidance */ - bvf->Copyf(dvec,pa->state.vel); + bvf->Copyf(dvec,pa->prev_state.vel); bvf->Mulf(dvec,5.0f); - bvf->Addf(dvec,dvec,pa->state.co); - if(boid_see_mesh(&psys->effectors,ob,psys,pa->state.co,dvec,ob_co,ob_nor,cfra)){ + bvf->Addf(dvec,dvec,pa->prev_state.co); + if(boid_see_mesh(&psys->effectors,ob,psys,pa->prev_state.co,dvec,ob_co,ob_nor,cfra)){ float probelen = bvf->Length(dvec); float proj; float oblen; Normalize(ob_nor); - proj = bvf->Inpf(ob_nor,pa->state.vel); + proj = bvf->Inpf(ob_nor,pa->prev_state.vel); - bvf->Subf(dvec,pa->state.co,ob_co); + bvf->Subf(dvec,pa->prev_state.co,ob_co); oblen=bvf->Length(dvec); bvf->Copyf(dvec,ob_nor); @@ -3545,12 +3478,12 @@ static void boid_brain(BoidVecFunc *bvf, ParticleData *pa, Object *ob, ParticleS if(pd->forcefield==PFIELD_FORCE && pd->f_strength<0.0){ float distance; - VECSUB(dvec,eob->obmat[3],pa->state.co); + VECSUB(dvec,eob->obmat[3],pa->prev_state.co); distance=Normalize(dvec); if(part->flag & PART_DIE_ON_COL && distance < pd->mindist){ - *pa_die=1; + pa->alive = PARS_DYING; pa->dietime=cfra; i=BOID_TOT_RULES; break; @@ -3579,17 +3512,17 @@ static void boid_brain(BoidVecFunc *bvf, ParticleData *pa, Object *ob, ParticleS pd= epart->pd; totepart= epsys->totpart; - if(pd->forcefield==PFIELD_FORCE && pd->f_strength<0.0){ - count=BLI_kdtree_find_n_nearest(ec->tree,epart->boidneighbours,pa->state.co,NULL,ptn2); + if(pd->forcefield==PFIELD_FORCE && pd->f_strength<0.0 && ec->tree){ + count=BLI_kdtree_find_n_nearest(ec->tree,epart->boidneighbours,pa->prev_state.co,NULL,ptn2); for(p=0; p<count; p++){ state.time=-1.0; if(psys_get_particle_state(eob,epsys,ptn2[p].index,&state,0)){ - VECSUB(dvec, state.co, pa->state.co); + VECSUB(dvec, state.co, pa->prev_state.co); distance = Normalize(dvec); if(part->flag & PART_DIE_ON_COL && distance < (epsys->particles+ptn2[p].index)->size){ - *pa_die=1; + pa->alive = PARS_DYING; pa->dietime=cfra; i=BOID_TOT_RULES; break; @@ -3614,10 +3547,12 @@ static void boid_brain(BoidVecFunc *bvf, ParticleData *pa, Object *ob, ParticleS near=0; for(n=1; n<neighbours; n++){ if(ptn[n].dist<2.0f*pa->size){ - bvf->Subf(dvec,pa->state.co,pars[ptn[n].index].state.co); - bvf->Mulf(dvec,(2.0f*pa->size-ptn[n].dist)/ptn[n].dist); - bvf->Addf(avoid,avoid,dvec); - near++; + if(ptn[n].dist!=0.0f) { + bvf->Subf(dvec,pa->prev_state.co,pars[ptn[n].index].state.co); + bvf->Mulf(dvec,(2.0f*pa->size-ptn[n].dist)/ptn[n].dist); + bvf->Addf(avoid,avoid,dvec); + near++; + } } /* ptn[] is distance ordered so no need to check others */ else break; @@ -3636,7 +3571,7 @@ static void boid_brain(BoidVecFunc *bvf, ParticleData *pa, Object *ob, ParticleS } bvf->Mulf(center,1.0f/((float)neighbours-1.0f)); - bvf->Subf(dvec,center,pa->state.co); + bvf->Subf(dvec,center,pa->prev_state.co); bvf->Mulf(dvec,part->boidfac[BOID_CENTER]*2.0f); @@ -3645,9 +3580,9 @@ static void boid_brain(BoidVecFunc *bvf, ParticleData *pa, Object *ob, ParticleS break; case BOID_AV_VEL: /* average velocity */ - cur_vel=bvf->Length(pa->state.vel); + cur_vel=bvf->Length(pa->prev_state.vel); if(cur_vel>0.0){ - bvf->Copyf(dvec,pa->state.vel); + bvf->Copyf(dvec,pa->prev_state.vel); bvf->Mulf(dvec,part->boidfac[BOID_AV_VEL]*(avg_vel-cur_vel)/cur_vel); not_finished=add_boid_acc(bvf,max_lat_acc,max_tan_acc,&lat_accu,&tan_accu,acc,dvec,0); } @@ -3662,7 +3597,7 @@ static void boid_brain(BoidVecFunc *bvf, ParticleData *pa, Object *ob, ParticleS } bvf->Mulf(velocity,1.0f/((float)neighbours-1.0f)); - bvf->Subf(dvec,velocity,pa->state.vel); + bvf->Subf(dvec,velocity,pa->prev_state.vel); bvf->Mulf(dvec,part->boidfac[BOID_VEL_MATCH]); @@ -3680,7 +3615,7 @@ static void boid_brain(BoidVecFunc *bvf, ParticleData *pa, Object *ob, ParticleS if(pd->forcefield==PFIELD_FORCE && pd->f_strength>0.0){ float distance; - VECSUB(dvec,eob->obmat[3],pa->state.co); + VECSUB(dvec,eob->obmat[3],pa->prev_state.co); distance=Normalize(dvec); @@ -3697,7 +3632,7 @@ static void boid_brain(BoidVecFunc *bvf, ParticleData *pa, Object *ob, ParticleS where_on_path(eob, (cfra-pa->time)/pa->lifetime, temp, dvec); - VECSUB(dvec,temp,pa->state.co); + VECSUB(dvec,temp,pa->prev_state.co); distance=Normalize(dvec); @@ -3724,12 +3659,12 @@ static void boid_brain(BoidVecFunc *bvf, ParticleData *pa, Object *ob, ParticleS pd= epart->pd; totepart= epsys->totpart; - if(pd->forcefield==PFIELD_FORCE && pd->f_strength>0.0){ - count=BLI_kdtree_find_n_nearest(ec->tree,epart->boidneighbours,pa->state.co,NULL,ptn2); + if(pd->forcefield==PFIELD_FORCE && pd->f_strength>0.0 && ec->tree){ + count=BLI_kdtree_find_n_nearest(ec->tree,epart->boidneighbours,pa->prev_state.co,NULL,ptn2); for(p=0; p<count; p++){ state.time=-1.0; if(psys_get_particle_state(eob,epsys,ptn2[p].index,&state,0)){ - VECSUB(dvec, state.co, pa->state.co); + VECSUB(dvec, state.co, pa->prev_state.co); distance = Normalize(dvec); @@ -3751,7 +3686,7 @@ static void boid_brain(BoidVecFunc *bvf, ParticleData *pa, Object *ob, ParticleS /* level flight */ if((part->flag & PART_BOIDS_2D)==0){ dvec[0]=dvec[1]=0.0; - dvec[2]=-pa->state.vel[2]; + dvec[2]=-pa->prev_state.vel[2]; VecMulf(dvec,part->boidfac[BOID_LEVEL]); not_finished=add_boid_acc(bvf,max_lat_acc,max_tan_acc,&lat_accu,&tan_accu,acc,dvec,0); @@ -3761,15 +3696,15 @@ static void boid_brain(BoidVecFunc *bvf, ParticleData *pa, Object *ob, ParticleS } } /* tries to realize the wanted acceleration */ -static void boid_body(BoidVecFunc *bvf, ParticleData *pa, ParticleSystem *psys, ParticleSettings *part, float timestep, float *acc, ParticleKey *state) +static void boid_body(BoidVecFunc *bvf, ParticleData *pa, ParticleSystem *psys, ParticleSettings *part, float timestep, float *acc) { float dvec[3], bvec[3], length, max_vel=part->max_vel; - float *q2, q[4]; + float q2[4], q[4]; float g=9.81f, pa_mass=part->mass; float yvec[3]={0.0,1.0,0.0}, zvec[3]={0.0,0.0,-1.0}, bank; /* apply new velocity, location & rotation */ - copy_particle_key(state,&pa->state,0); + copy_particle_key(&pa->state,&pa->prev_state,0); if(part->flag & PART_SIZEMASS) pa_mass*=pa->size; @@ -3781,32 +3716,49 @@ static void boid_body(BoidVecFunc *bvf, ParticleData *pa, ParticleSystem *psys, bvf->Copyf(dvec,acc); bvf->Mulf(dvec,timestep*timestep*0.5f); - bvf->Copyf(bvec,state->vel); + bvf->Copyf(bvec,pa->state.vel); bvf->Mulf(bvec,timestep); bvf->Addf(dvec,dvec,bvec); - bvf->Addf(state->co,state->co,dvec); + bvf->Addf(pa->state.co,pa->state.co,dvec); - /* air speed from wind effectors */ - if(psys->effectors.first){ + /* air speed from wind and vortex effectors */ + if(psys->effectors.first) { ParticleEffectorCache *ec; - for(ec=psys->effectors.first; ec; ec=ec->next){ - if(ec->type & PSYS_EC_EFFECTOR){ + for(ec=psys->effectors.first; ec; ec=ec->next) { + if(ec->type & PSYS_EC_EFFECTOR) { Object *eob = ec->ob; PartDeflect *pd = eob->pd; + float direction[3], vec_to_part[3]; + float falloff; + + if(pd->f_strength != 0.0f) { + VecCopyf(direction, eob->obmat[2]); + VecSubf(vec_to_part, pa->state.co, eob->obmat[3]); + + falloff=effector_falloff(pd, direction, vec_to_part); + + switch(pd->forcefield) { + case PFIELD_WIND: + if(falloff <= 0.0f) + ; /* don't do anything */ + else { + Normalize(direction); + VecMulf(direction, pd->f_strength * falloff); + bvf->Addf(pa->state.co, pa->state.co, direction); + } + break; + case PFIELD_VORTEX: + { + float distance, mag_vec[3]; + Crossf(mag_vec, direction, vec_to_part); + Normalize(mag_vec); - if(pd->forcefield==PFIELD_WIND && pd->f_strength!=0.0){ - float distance, wind[3]; - VecCopyf(wind,eob->obmat[2]); - distance=VecLenf(state->co,eob->obmat[3]); - - if (distance < 0.001) distance = 0.001f; + distance = VecLength(vec_to_part); - if(pd->flag&PFIELD_USEMAX && distance > pd->maxdist) - ; - else{ - Normalize(wind); - VecMulf(wind,pd->f_strength/(float)pow((double)distance,(double)pd->f_power)); - bvf->Addf(state->co,state->co,wind); + VecMulf(mag_vec, pd->f_strength * distance * falloff); + bvf->Addf(pa->state.co, pa->state.co, mag_vec); + break; + } } } } @@ -3814,8 +3766,8 @@ static void boid_body(BoidVecFunc *bvf, ParticleData *pa, ParticleSystem *psys, } - if((part->flag & PART_BOIDS_2D)==0 && pa->state.vel[0]!=0.0 && pa->state.vel[0]!=0.0 && pa->state.vel[0]!=0.0){ - Crossf(yvec,state->vel,zvec); + if((part->flag & PART_BOIDS_2D)==0 && pa->prev_state.vel[0]!=0.0 && pa->prev_state.vel[0]!=0.0 && pa->prev_state.vel[0]!=0.0){ + Crossf(yvec,pa->state.vel,zvec); Normalize(yvec); @@ -3842,27 +3794,27 @@ static void boid_body(BoidVecFunc *bvf, ParticleData *pa, ParticleSystem *psys, } - VecRotToQuat(state->vel,bank,q); + VecRotToQuat(pa->state.vel,bank,q); - VECCOPY(dvec,state->vel); + VECCOPY(dvec,pa->state.vel); VecMulf(dvec,-1.0f); - q2= vectoquat(dvec, OB_POSX, OB_POSZ); + vectoquat(dvec, OB_POSX, OB_POSZ, q2); - QuatMul(state->rot,q,q2); + QuatMul(pa->state.rot,q,q2); bvf->Mulf(acc,timestep); - bvf->Addf(state->vel,state->vel,acc); + bvf->Addf(pa->state.vel,pa->state.vel,acc); if(part->flag & PART_BOIDS_2D){ - state->vel[2]=0.0; - state->co[2]=part->groundz; + pa->state.vel[2]=0.0; + pa->state.co[2]=part->groundz; - if(psys->keyed_ob){ + if(psys->keyed_ob && (psys->keyed_ob->type == OB_MESH)){ Object *zob=psys->keyed_ob; int min_face; float co1[3],co2[3],min_d=2.0,min_w[4],imat[4][4]; - VECCOPY(co1,state->co); - VECCOPY(co2,state->co); + VECCOPY(co1,pa->state.co); + VECCOPY(co2,pa->state.co); co1[2]=1000.0f; co2[2]=-1000.0f; @@ -3893,7 +3845,7 @@ static void boid_body(BoidVecFunc *bvf, ParticleData *pa, ParticleSystem *psys, Normalize(nor); - VECCOPY(state->co,loc); + VECCOPY(pa->state.co,loc); zvec[2]=1.0; @@ -3905,22 +3857,22 @@ static void boid_body(BoidVecFunc *bvf, ParticleData *pa, ParticleSystem *psys, VecRotToQuat(loc,bank,q); - QUATCOPY(q1,state->rot); + QUATCOPY(q1,pa->state.rot); - QuatMul(state->rot,q,q1); + QuatMul(pa->state.rot,q,q1); } } } } - length=bvf->Length(state->vel); + length=bvf->Length(pa->state.vel); if(length > max_vel) - bvf->Mulf(state->vel,max_vel/length); + bvf->Mulf(pa->state.vel,max_vel/length); } /************************************************/ /* Hair */ /************************************************/ -void save_hair(Object *ob, ParticleSystem *psys, ParticleSystemModifierData *psmd, float cfra){ +static void save_hair(Object *ob, ParticleSystem *psys, ParticleSystemModifierData *psmd, float cfra){ ParticleData *pa; HairKey *key; int totpart; @@ -3972,14 +3924,13 @@ static void dynamics_step(Object *ob, ParticleSystem *psys, ParticleSystemModifi float *vg_vel, float *vg_tan, float *vg_rot, float *vg_size) { ParticleData *pa; - ParticleKey *outstate, *key; ParticleSettings *part=psys->part; KDTree *tree=0; BoidVecFunc bvf; IpoCurve *icu_esize=find_ipocurve(part->ipo,PART_EMIT_SIZE); Material *ma=give_current_material(ob,part->omat); float timestep; - int p, totpart, pa_die; + int p, totpart; /* current time */ float ctime, ipotime; /* frame & time changes */ @@ -4007,7 +3958,7 @@ static void dynamics_step(Object *ob, ParticleSystem *psys, ParticleSystemModifi vg_size=psys_cache_vgroup(psmd->dm,psys,PSYS_VG_SIZE); for(p=0, pa=psys->particles; p<totpart; p++,pa++){ - if(pa->flag & (PARS_NO_DISP+PARS_UNEXIST)) continue; + if(pa->flag & PARS_UNEXIST) continue; /* set correct ipo timing */ if((part->flag&PART_ABS_TIME)==0 && part->ipo){ @@ -4017,36 +3968,31 @@ static void dynamics_step(Object *ob, ParticleSystem *psys, ParticleSystemModifi } pa->size=psys_get_size(ob,ma,psmd,icu_esize,psys,part,pa,vg_size); - if(part->type==PART_REACTOR) - initialize_particle(pa,p,ob,psys,psmd); - reset_particle(pa,psys,psmd,ob,dtime,cfra,vg_vel,vg_tan,vg_rot); - if(cfra>pa->time && part->flag & PART_LOOP && (part->flag & PART_LOOP_INSTANT)==0){ - pa->loop=(short)((cfra-pa->time)/pa->lifetime)+1; + if(cfra>pa->time && part->flag & PART_LOOP && part->type!=PART_HAIR){ + pa->loop=(short)((cfra-pa->time)/pa->lifetime); pa->alive=PARS_UNBORN; } else{ - pa->loop=0; - if(cfra<=pa->time) - pa->alive=PARS_UNBORN; + pa->loop = 0; + if(cfra <= pa->time) + pa->alive = PARS_UNBORN; /* without dynamics the state is allways known so no need to kill */ - else if(ELEM(part->phystype,PART_PHYS_NO,PART_PHYS_KEYED)==0) - pa->alive=PARS_KILLED; + else if(ELEM(part->phystype, PART_PHYS_NO, PART_PHYS_KEYED)){ + if(cfra < pa->dietime) + pa->alive = PARS_ALIVE; + } + else + pa->alive = PARS_KILLED; } } if(vg_size) MEM_freeN(vg_size); - - //if(part->phystype==PART_PHYS_SOLID) - // reset_to_first_fragment(psys); } else{ BLI_srandom(31415926 + (int)cfra + psys->seed); - - /* outstate is used so that particles are updated in parallel */ - outstate=MEM_callocN(totpart*sizeof(ParticleKey),"Particle Outstates"); /* update effectors */ if(psys->effectors.first) @@ -4055,7 +4001,7 @@ static void dynamics_step(Object *ob, ParticleSystem *psys, ParticleSystemModifi psys_init_effectors(ob,part->eff_group,psys); if(psys->effectors.first) - precalc_effectors(ob,psys,psmd); + precalc_effectors(ob,psys,psmd,cfra); if(part->phystype==PART_PHYS_BOIDS){ /* create particle tree for fast inter-particle comparisons */ @@ -4071,10 +4017,10 @@ static void dynamics_step(Object *ob, ParticleSystem *psys, ParticleSystemModifi } /* main loop: calculate physics for all particles */ - for(p=0, pa=psys->particles, key=outstate; p<totpart; p++,pa++,key++){ - if(pa->flag & (PARS_NO_DISP|PARS_UNEXIST)) continue; + for(p=0, pa=psys->particles; p<totpart; p++,pa++){ + if(pa->flag & PARS_UNEXIST) continue; - copy_particle_key(key,&pa->state,1); + copy_particle_key(&pa->prev_state,&pa->state,1); /* set correct ipo timing */ if((part->flag&PART_ABS_TIME)==0 && part->ipo){ @@ -4084,104 +4030,85 @@ static void dynamics_step(Object *ob, ParticleSystem *psys, ParticleSystemModifi } pa->size=psys_get_size(ob,ma,psmd,icu_esize,psys,part,pa,vg_size); - pa_die=0; + /* reactions can change birth time so they need to be checked first */ + if(psys->reactevents.first && ELEM(pa->alive,PARS_DEAD,PARS_KILLED)==0) + react_to_events(psys,p); + + birthtime = pa->time + pa->loop * pa->lifetime; + dietime = birthtime + pa->lifetime; - if(pa->alive==PARS_UNBORN || pa->alive==PARS_KILLED || ELEM(part->phystype,PART_PHYS_NO,PART_PHYS_KEYED)){ - /* allways reset particles to emitter before birth */ + /* allways reset particles to emitter before birth */ + if(pa->alive==PARS_UNBORN + || pa->alive==PARS_KILLED + || ELEM(part->phystype,PART_PHYS_NO,PART_PHYS_KEYED) + || birthtime >= cfra){ reset_particle(pa,psys,psmd,ob,dtime,cfra,vg_vel,vg_tan,vg_rot); - copy_particle_key(key,&pa->state,1); } - if(dfra>0.0 || psys->recalc){ - - if(psys->reactevents.first && ELEM(pa->alive,PARS_DEAD,PARS_KILLED)==0) - react_to_events(psys,p); + pa_dfra = dfra; + pa_dtime = dtime; - pa_dfra= dfra; - pa_dtime= dtime; + if(birthtime <= cfra && birthtime >= psys->cfra){ + /* particle is born some time between this and last step*/ + pa->alive = PARS_ALIVE; + pa_dfra = cfra - birthtime; + pa_dtime = pa_dfra*timestep; + } + else if(dietime <= cfra && psys->cfra < dietime){ + /* particle dies some time between this and last step */ + pa_dfra = dietime - psys->cfra; + pa_dtime = pa_dfra * timestep; + pa->alive = PARS_DYING; + } + else if(dietime < cfra){ + /* nothing to be done when particle is dead */ + } - if(pa->flag & PART_LOOP && pa->flag & PART_LOOP_INSTANT) - birthtime=pa->dietime; - else - birthtime=pa->time+pa->loop*pa->lifetime; - dietime=birthtime+pa->lifetime; + if(dfra>0.0 && ELEM(pa->alive,PARS_ALIVE,PARS_DYING)){ + switch(part->phystype){ + case PART_PHYS_NEWTON: + /* do global forces & effectors */ + apply_particle_forces(p,pa,ob,psys,part,timestep,pa_dfra,cfra); + + /* deflection */ + deflect_particle(ob,psmd,psys,part,pa,p,timestep,pa_dfra,cfra); - if(birthtime < cfra && birthtime >= psys->cfra){ - /* particle is born some time between this and last step*/ - pa->alive=PARS_ALIVE; - pa_dfra= cfra - birthtime; - pa_dtime= pa_dfra*timestep; - } - else if(dietime <= cfra && psys->cfra < dietime){ - /* particle dies some time between this and last step */ - pa_dfra= dietime - psys->cfra; - pa_dtime= pa_dfra*timestep; - pa_die=1; - } - else if(dietime < cfra){ - /* TODO: figure out if there's something to be done when particle is dead */ + /* rotations */ + rotate_particle(part,pa,pa_dfra,timestep); + break; + case PART_PHYS_BOIDS: + { + float acc[3]; + boid_brain(&bvf,pa,ob,psys,part,tree,timestep,cfra,acc); + if(pa->alive != PARS_DYING) + boid_body(&bvf,pa,psys,part,timestep,acc); + break; + } } - copy_particle_key(key,&pa->state,1); - - if(dfra>0.0 && pa->alive==PARS_ALIVE){ - switch(part->phystype){ - case PART_PHYS_NEWTON: - /* do global forces & effectors */ - apply_particle_forces(p,pa,ob,psys,part,timestep,pa_dfra,cfra,key); - - /* deflection */ - deflect_particle(ob,psmd,psys,part,pa,p,pa_dfra,cfra,key,&pa_die); - - /* rotations */ - rotate_particle(part,pa,pa_dfra,timestep,key); + if(pa->alive == PARS_DYING){ + push_reaction(ob,psys,p,PART_EVENT_DEATH,&pa->state); - break; - case PART_PHYS_BOIDS: - { - float acc[3]; - boid_brain(&bvf,pa,ob,psys,part,tree,timestep,cfra,acc,&pa_die); - if(pa_die==0) - boid_body(&bvf,pa,psys,part,timestep,acc,key); - break; - } + if(part->flag & PART_LOOP && part->type!=PART_HAIR){ + pa->loop++; + reset_particle(pa,psys,psmd,ob,0.0,cfra,vg_vel,vg_tan,vg_rot); + pa->alive=PARS_ALIVE; } + else{ + pa->alive=PARS_DEAD; + pa->state.time=pa->dietime; - push_reaction(ob,psys,p,PART_EVENT_NEAR,key); - - if(pa_die){ - push_reaction(ob,psys,p,PART_EVENT_DEATH,key); - - if(part->flag & PART_LOOP){ - pa->loop++; - - if(part->flag & PART_LOOP_INSTANT){ - reset_particle(pa,psys,psmd,ob,0.0,cfra,vg_vel,vg_tan,vg_rot); - pa->alive=PARS_ALIVE; - copy_particle_key(key,&pa->state,1); - } - else - pa->alive=PARS_UNBORN; - } - else{ - pa->alive=PARS_DEAD; - key->time=pa->dietime; - - if(pa->flag&PARS_STICKY) - psys_key_to_object(pa->stick_ob,key,0); - } + if(pa->flag&PARS_STICKY) + psys_key_to_object(pa->stick_ob,&pa->state,0); } - else - key->time=cfra; } + else + pa->state.time=cfra; + + push_reaction(ob,psys,p,PART_EVENT_NEAR,&pa->state); } } - /* apply outstates to particles */ - for(p=0, pa=psys->particles, key=outstate; p<totpart; p++,pa++,key++) - copy_particle_key(&pa->state,key,1); - - MEM_freeN(outstate); } if(psys->reactevents.first) BLI_freelistN(&psys->reactevents); @@ -4196,9 +4123,8 @@ static void psys_update_path_cache(Object *ob, ParticleSystemModifierData *psmd, ParticleSettings *part=psys->part; ParticleEditSettings *pset=&G.scene->toolsettings->particle; int distr=0,alloc=0; - int child_nbr= (psys->renderdata)? part->ren_child_nbr: part->child_nbr; - if((psys->part->childtype && psys->totchild != psys->totpart*child_nbr) || psys->recalc&PSYS_ALLOC) + if((psys->part->childtype && psys->totchild != get_psys_tot_child(psys)) || psys->recalc&PSYS_ALLOC) alloc=1; if(alloc || psys->recalc&PSYS_DISTR || (psys->vgroup[PSYS_VG_DENSITY] && (G.f & G_WEIGHTPAINT))) @@ -4206,9 +4132,9 @@ static void psys_update_path_cache(Object *ob, ParticleSystemModifierData *psmd, if(distr){ if(alloc) - alloc_particles(ob,psys,psys->totpart); + realloc_particles(ob,psys,psys->totpart); - if(get_alloc_child_particles_tot(psys)) { + if(get_psys_tot_child(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); @@ -4220,13 +4146,14 @@ static void psys_update_path_cache(Object *ob, ParticleSystemModifierData *psmd, } if((part->type==PART_HAIR || psys->flag&PSYS_KEYED) && (psys_in_edit_mode(psys) - || part->draw_as==PART_DRAW_PATH || part->draw&PART_DRAW_KEYS)){ + || (part->type==PART_HAIR || part->draw_as==PART_DRAW_PATH))){ psys_cache_paths(ob, psys, cfra, 0); /* for render, child particle paths are computed on the fly */ if(part->childtype) { if(((psys->totchild!=0)) || (psys_in_edit_mode(psys) && (pset->flag&PE_SHOW_CHILD))) - psys_cache_child_paths(ob, psys, cfra, 0); + if(!(psys->part->type == PART_HAIR) || (psys->flag & PSYS_HAIR_DONE)) + psys_cache_child_paths(ob, psys, cfra, 0); } } else if(psys->pathcache) @@ -4236,17 +4163,27 @@ static void psys_update_path_cache(Object *ob, ParticleSystemModifierData *psmd, static void hair_step(Object *ob, ParticleSystemModifierData *psmd, ParticleSystem *psys, float cfra) { ParticleSettings *part = psys->part; + ParticleData *pa; + int p; + float disp = (float)get_current_display_percentage(psys)/50.0f-1.0f; + + for(p=0, pa=psys->particles; p<psys->totpart; p++,pa++){ + if(pa->r_rot[0] > disp) + pa->flag |= PARS_NO_DISP; + else + pa->flag &= ~PARS_NO_DISP; + } if(psys->recalc & PSYS_DISTR) /* need this for changing subsurf levels */ - psys_calc_dmfaces(ob, psmd->dm, psys); + psys_calc_dmcache(ob, psmd->dm, psys); if(psys->effectors.first) psys_end_effectors(psys); psys_init_effectors(ob,part->eff_group,psys); if(psys->effectors.first) - precalc_effectors(ob,psys,psmd); + precalc_effectors(ob,psys,psmd,cfra); if(psys_in_edit_mode(psys)) ; //XXX PE_recalc_world_cos(ob, psys); @@ -4255,7 +4192,7 @@ static void hair_step(Object *ob, ParticleSystemModifierData *psmd, ParticleSyst } /* updates cached particles' alive & other flags etc..*/ -static void cached_step(Object *ob, ParticleSystemModifierData *psmd, ParticleSystem *psys, float cfra, float *vg_size) +static void cached_step(Object *ob, ParticleSystemModifierData *psmd, ParticleSystem *psys, float cfra) { ParticleSettings *part=psys->part; ParticleData *pa; @@ -4263,34 +4200,10 @@ static void cached_step(Object *ob, ParticleSystemModifierData *psmd, ParticleSy IpoCurve *icu_esize=find_ipocurve(part->ipo,PART_EMIT_SIZE); Material *ma=give_current_material(ob,part->omat); int p; - float ipotime=cfra, disp; - - /* deprecated */ - //if(psys->recalc&PSYS_DISTR){ - // /* The dm could have been changed so particle emitter element */ - // /* indices might be wrong. There's really no "nice" way to handle*/ - // /* this so we just try not to crash by correcting indices. */ - // int totnum=-1; - // switch(part->from){ - // case PART_FROM_VERT: - // totnum=psmd->dm->getNumVerts(psmd->dm); - // break; - // case PART_FROM_FACE: - // case PART_FROM_VOLUME: - // totnum=psmd->dm->getNumFaces(psmd->dm); - // break; - // } - - // if(totnum==0){ - // /* Now we're in real trouble, there's no emitter elements!! */ - // for(p=0, pa=psys->particles; p<psys->totpart; p++,pa++) - // pa->num=-1; - // } - // else if(totnum>0){ - // for(p=0, pa=psys->particles; p<psys->totpart; p++,pa++) - // pa->num=pa->num%totnum; - // } - //} + float ipotime=cfra, disp, birthtime, dietime, *vg_size= NULL; + + if(part->from!=PART_FROM_PARTICLE) + vg_size= psys_cache_vgroup(psmd->dm,psys,PSYS_VG_SIZE); if(psys->effectors.first) psys_end_effectors(psys); @@ -4298,7 +4211,7 @@ static void cached_step(Object *ob, ParticleSystemModifierData *psmd, ParticleSy //if(part->flag & (PART_BAKED_GUIDES+PART_BAKED_DEATHS)){ psys_init_effectors(ob,part->eff_group,psys); if(psys->effectors.first) - precalc_effectors(ob,psys,psmd); + precalc_effectors(ob,psys,psmd,cfra); //} disp= (float)get_current_display_percentage(psys)/50.0f-1.0f; @@ -4313,22 +4226,30 @@ static void cached_step(Object *ob, ParticleSystemModifierData *psmd, ParticleSy psys->lattice=psys_get_lattice(ob,psys); + if(part->flag & PART_LOOP && part->type!=PART_HAIR) + pa->loop = (short)((cfra - pa->time) / pa->lifetime); + else + pa->loop = 0; + + birthtime = pa->time + pa->loop * pa->lifetime; + dietime = birthtime + (1 + pa->loop) * (pa->dietime - pa->time); + /* update alive status and push events */ - if(pa->time>cfra) - pa->alive=PARS_UNBORN; - else if(pa->dietime<=cfra){ - if(pa->dietime>psys->cfra){ - state.time=pa->dietime; + if(pa->time > cfra) + pa->alive = PARS_UNBORN; + else if(dietime <= cfra){ + if(dietime > psys->cfra){ + state.time = pa->dietime; psys_get_particle_state(ob,psys,p,&state,1); push_reaction(ob,psys,p,PART_EVENT_DEATH,&state); } - pa->alive=PARS_DEAD; + pa->alive = PARS_DEAD; } else{ - pa->alive=PARS_ALIVE; - state.time=cfra; + pa->alive = PARS_ALIVE; + state.time = cfra; psys_get_particle_state(ob,psys,p,&state,1); - state.time=cfra; + state.time = cfra; push_reaction(ob,psys,p,PART_EVENT_NEAR,&state); } @@ -4342,126 +4263,324 @@ static void cached_step(Object *ob, ParticleSystemModifierData *psmd, ParticleSy else pa->flag &= ~PARS_NO_DISP; } + + /* make sure that children are up to date */ + if(psys->part->childtype && psys->totchild != get_psys_tot_child(psys)) { + realloc_particles(ob, psys, psys->totpart); + distribute_particles(ob, psys, PART_FROM_CHILD); + } + + if(vg_size) + MEM_freeN(vg_size); +} + +void psys_changed_type(ParticleSystem *psys) +{ + ParticleSettings *part; + + part= psys->part; + + /* system type has changed so set sensible defaults and clear non applicable flags */ + 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(psys->part->phystype != PART_PHYS_KEYED) + psys->flag &= ~PSYS_KEYED; + + if(part->type == PART_HAIR) { + part->draw_as = PART_DRAW_PATH; + part->rotfrom = PART_ROT_IINCR; + } + else { + free_hair(psys, 1); + + if(part->draw_as == PART_DRAW_PATH) + if(psys->part->phystype != PART_PHYS_KEYED) + part->draw_as = PART_DRAW_DOT; + } + + psys->softflag= 0; + + psys_reset(psys, PSYS_RESET_ALL); } + +static void particles_fluid_step(Object *ob, ParticleSystem *psys, int cfra) +{ + if(psys->particles){ + MEM_freeN(psys->particles); + psys->particles = 0; + psys->totpart = 0; + } + + /* fluid sim particle import handling, actual loading of particles from file */ + #ifndef DISABLE_ELBEEM + { + FluidsimModifierData *fluidmd = (FluidsimModifierData *)modifiers_findByType(ob, eModifierType_Fluidsim); + + if( fluidmd && fluidmd->fss) { + FluidsimSettings *fss= fluidmd->fss; + ParticleSettings *part = psys->part; + ParticleData *pa=0; + char *suffix = "fluidsurface_particles_####"; + char *suffix2 = ".gz"; + char filename[256]; + char debugStrBuffer[256]; + int curFrame = G.scene->r.cfra -1; // warning - sync with derived mesh fsmesh loading + int p, j, numFileParts, totpart; + int readMask, activeParts = 0, fileParts = 0; + gzFile gzf; + + if(ob==G.obedit) // off... + return; + + // ok, start loading + strcpy(filename, fss->surfdataPath); + strcat(filename, suffix); + BLI_convertstringcode(filename, G.sce); + BLI_convertstringframe(filename, curFrame); // fixed #frame-no + strcat(filename, suffix2); + + gzf = gzopen(filename, "rb"); + if (!gzf) { + snprintf(debugStrBuffer,256,"readFsPartData::error - Unable to open file for reading '%s' \n", filename); + //elbeemDebugOut(debugStrBuffer); + return; + } + + gzread(gzf, &totpart, sizeof(totpart)); + numFileParts = totpart; + totpart = (G.rendering)?totpart:(part->disp*totpart)/100; + + part->totpart= totpart; + part->sta=part->end = 1.0f; + part->lifetime = G.scene->r.efra + 1; + + /* initialize particles */ + realloc_particles(ob, psys, part->totpart); + initialize_all_particles(ob, psys, 0); + + // set up reading mask + readMask = fss->typeFlags; + + for(p=0, pa=psys->particles; p<totpart; p++, pa++) { + int ptype=0; + + gzread(gzf, &ptype, sizeof( ptype )); + if(ptype&readMask) { + activeParts++; + + gzread(gzf, &(pa->size), sizeof( float )); + + pa->size /= 10.0f; + + for(j=0; j<3; j++) { + float wrf; + gzread(gzf, &wrf, sizeof( wrf )); + pa->state.co[j] = wrf; + //fprintf(stderr,"Rj%d ",j); + } + for(j=0; j<3; j++) { + float wrf; + gzread(gzf, &wrf, sizeof( wrf )); + pa->state.vel[j] = wrf; + } + + pa->state.ave[0] = pa->state.ave[1] = pa->state.ave[2] = 0.0f; + pa->state.rot[0] = 1.0; + pa->state.rot[1] = pa->state.rot[2] = pa->state.rot[3] = 0.0; + + pa->alive = PARS_ALIVE; + //if(a<25) fprintf(stderr,"FSPARTICLE debug set %s , a%d = %f,%f,%f , life=%f \n", filename, a, pa->co[0],pa->co[1],pa->co[2], pa->lifetime ); + } else { + // skip... + for(j=0; j<2*3+1; j++) { + float wrf; gzread(gzf, &wrf, sizeof( wrf )); + } + } + fileParts++; + } + gzclose( gzf ); + + totpart = psys->totpart = activeParts; + snprintf(debugStrBuffer,256,"readFsPartData::done - particles:%d, active:%d, file:%d, mask:%d \n", psys->totpart,activeParts,fileParts,readMask); + elbeemDebugOut(debugStrBuffer); + } // fluid sim particles done + } + #endif // DISABLE_ELBEEM +} + /* Calculates the next state for all particles of the system */ /* In particles code most fra-ending are frames, time-ending are fra*timestep (seconds)*/ static void system_step(Object *ob, ParticleSystem *psys, ParticleSystemModifierData *psmd, float cfra) { ParticleSettings *part; ParticleData *pa; - int totpart,oldtotpart=0,p; - float disp, *vg_vel=0, *vg_tan=0, *vg_rot=0, *vg_size=0; - int init=0,distr=0,alloc=0; - int child_nbr; + PointCache *cache; + PTCacheID pid; + int totpart, oldtotpart, totchild, oldtotchild, p; + float disp, *vg_vel= 0, *vg_tan= 0, *vg_rot= 0, *vg_size= 0; + int init= 0, distr= 0, alloc= 0, usecache= 0, only_children_changed= 0; + int framenr, framedelta, startframe, endframe; - /*----start validity checks----*/ + part= psys->part; + cache= psys->pointcache; - part=psys->part; + framenr= (int)CFRA; + framedelta= framenr - cache->simframe; - if(part->flag&PART_ABS_TIME && part->ipo){ + BKE_ptcache_id_from_particles(&pid, ob, psys); + BKE_ptcache_id_time(&pid, 0.0f, &startframe, &endframe, NULL); + + /* update ipo's */ + if((part->flag & PART_ABS_TIME) && part->ipo) { calc_ipo(part->ipo, cfra); execute_ipo((ID *)part, part->ipo); } - if(part->from!=PART_FROM_PARTICLE) - vg_size=psys_cache_vgroup(psmd->dm,psys,PSYS_VG_SIZE); + /* hair if it's already done is handled separate */ + if(part->type == PART_HAIR && (psys->flag & PSYS_HAIR_DONE)) { + hair_step(ob, psmd, psys, cfra); + psys->cfra = cfra; + psys->recalc = 0; + return; + } + /* fluid is also handled separate */ + else if(part->type == PART_FLUID) { + particles_fluid_step(ob, psys, framenr); + psys->cfra = cfra; + psys->recalc = 0; + return; + } - if(part->type == PART_HAIR) { - if(psys->flag & PSYS_HAIR_DONE) { - hair_step(ob, psmd, psys, cfra); + /* cache shouldn't be used for hair or "none" or "keyed" physics */ + if(part->type == PART_HAIR || ELEM(part->phystype, PART_PHYS_NO, PART_PHYS_KEYED)) + usecache= 0; + else if(BKE_ptcache_get_continue_physics()) + usecache= 0; + else + usecache= 1; + + if(usecache) { + /* frame clamping */ + if(framenr < startframe) { + psys_reset(psys, PSYS_RESET_CACHE_MISS); psys->cfra = cfra; psys->recalc = 0; return; } - } - else { - if(psys->recalc) - clear_particles_from_cache(ob,psys,(int)cfra); - else if(get_particles_from_cache(ob, psys, (int)cfra)){ - cached_step(ob,psmd,psys,cfra,vg_size); - psys->cfra=cfra; - psys->recalc = 0; - return; + else if(framenr > endframe) { + framenr= endframe; } } - /* if still here react to events */ + /* verify if we need to reallocate */ + oldtotpart = psys->totpart; + oldtotchild = psys->totchild; - if(psys->recalc&PSYS_TYPE) { - /* system type has changed so set sensible defaults and clear non applicable flags */ - if(part->from == PART_FROM_PARTICLE) { - if(part->type != PART_REACTOR) - part->from = PART_FROM_FACE; - if(part->distr == PART_DISTR_GRID) - part->distr = PART_DISTR_JIT; - } - - if(psys->part->phystype != PART_PHYS_KEYED) - psys->flag &= ~PSYS_KEYED; - - if(part->type == PART_HAIR) { - part->draw_as = PART_DRAW_PATH; - part->rotfrom = PART_ROT_IINCR; - } - else - free_hair(psys); - - psys->recalc &= ~PSYS_TYPE; - alloc = 1; - - /* this is a bad level call, but currently type change - * can happen after redraw, so force redraw from here */ -// XXX allqueue(REDRAWBUTSOBJECT, 0); - } - else - oldtotpart = psys->totpart; - - if(part->distr == PART_DISTR_GRID) - totpart = part->grid_res * part->grid_res * part->grid_res; + if(part->distr == PART_DISTR_GRID && part->from != PART_FROM_VERT) + totpart = part->grid_res*part->grid_res*part->grid_res; else totpart = psys->part->totpart; + totchild = get_psys_tot_child(psys); - child_nbr= (psys->renderdata)? part->ren_child_nbr: part->child_nbr; - if(oldtotpart != totpart || psys->recalc&PSYS_ALLOC || (psys->part->childtype && psys->totchild != psys->totpart*child_nbr)) + if(oldtotpart != totpart || (psys->part->childtype && oldtotchild != totchild)) { + only_children_changed = (oldtotpart == totpart); + realloc_particles(ob, psys, totpart); alloc = 1; + distr= 1; + init= 1; + } - /* bad context? XXX */ - if(alloc || psys->recalc&PSYS_DISTR || (psys->vgroup[PSYS_VG_DENSITY] && (G.f & G_WEIGHTPAINT) && ob==(G.scene->basact?G.scene->basact->object:NULL))) - distr = 1; - - if(distr || psys->recalc&PSYS_INIT) - init = 1; + if(psys->recalc & PSYS_DISTR) { + distr= 1; + init= 1; + } if(init) { if(distr) { if(alloc) - alloc_particles(ob, psys, totpart); + realloc_particles(ob, psys, totpart); distribute_particles(ob, psys, part->from); - if(get_alloc_child_particles_tot(psys)) + if((psys->part->type == PART_HAIR) && !(psys->flag & PSYS_HAIR_DONE)) + /* don't generate children while growing hair - waste of time */ + psys_free_children(psys); + else if(get_psys_tot_child(psys)) distribute_particles(ob, psys, PART_FROM_CHILD); } - initialize_all_particles(ob, psys, psmd); - if(alloc) - reset_all_particles(ob, psys, psmd, 0.0, cfra, oldtotpart); + if(only_children_changed==0) { + initialize_all_particles(ob, psys, psmd); + + if(alloc) + reset_all_particles(ob, psys, psmd, 0.0, cfra, oldtotpart); + } /* flag for possible explode modifiers after this system */ psmd->flag |= eParticleSystemFlag_Pars; } + /* try to read from the cache */ + if(usecache) { + if(get_particles_from_cache(ob, psys, framenr)) { + if(part->phystype==PART_PHYS_KEYED && psys->flag&PSYS_FIRST_KEYED) { + psys_count_keyed_targets(ob,psys); + set_keyed_keys(ob, psys); + } + + cached_step(ob,psmd,psys,cfra); + psys->cfra=cfra; + psys->recalc = 0; + + if(part->phystype==PART_PHYS_KEYED && psys->flag&PSYS_FIRST_KEYED) { + psys_update_path_cache(ob,psmd,psys,framenr); + } + + cache->simframe= framenr; + cache->flag |= PTCACHE_SIMULATION_VALID; + + return; + } + else if(ob->id.lib || (cache->flag & PTCACHE_BAKED)) { + psys_reset(psys, PSYS_RESET_CACHE_MISS); + psys->cfra=cfra; + psys->recalc = 0; + return; + } + + if(framenr != startframe && framedelta != 1) { + psys_reset(psys, PSYS_RESET_CACHE_MISS); + psys->cfra = cfra; + psys->recalc = 0; + return; + } + } + else { + cache->flag &= ~PTCACHE_SIMULATION_VALID; + cache->simframe= 0; + } + + /* if on second frame, write cache for first frame */ + if(usecache && framenr == startframe+1) + write_particles_to_cache(ob, psys, startframe); if(part->phystype==PART_PHYS_KEYED && psys->flag&PSYS_FIRST_KEYED) psys_count_keyed_targets(ob,psys); - if(part->from!=PART_FROM_PARTICLE){ - vg_vel=psys_cache_vgroup(psmd->dm,psys,PSYS_VG_VEL); - vg_tan=psys_cache_vgroup(psmd->dm,psys,PSYS_VG_TAN); - vg_rot=psys_cache_vgroup(psmd->dm,psys,PSYS_VG_ROT); + /* initialize vertex groups */ + if(part->from!=PART_FROM_PARTICLE) { + vg_vel= psys_cache_vgroup(psmd->dm,psys,PSYS_VG_VEL); + vg_tan= psys_cache_vgroup(psmd->dm,psys,PSYS_VG_TAN); + vg_rot= psys_cache_vgroup(psmd->dm,psys,PSYS_VG_ROT); + vg_size= psys_cache_vgroup(psmd->dm,psys,PSYS_VG_SIZE); } - /* set particles to be not calculated */ + /* set particles to be not calculated TODO: can't work with pointcache */ disp= (float)get_current_display_percentage(psys)/50.0f-1.0f; for(p=0, pa=psys->particles; p<totpart; p++,pa++){ @@ -4471,15 +4590,30 @@ static void system_step(Object *ob, ParticleSystem *psys, ParticleSystemModifier pa->flag &= ~PARS_NO_DISP; } - /* ok now we're all set so let's go */ - if(psys->totpart) - dynamics_step(ob,psys,psmd,cfra,vg_vel,vg_tan,vg_rot,vg_size); + if(psys->totpart) { + int dframe, totframesback = 0; + + /* handle negative frame start at the first frame by doing + * all the steps before the first frame */ + if(framenr == startframe && part->sta < startframe) + totframesback = (startframe - (int)part->sta); + + for(dframe=-totframesback; dframe<=0; dframe++) { + /* ok now we're all set so let's go */ + dynamics_step(ob,psys,psmd,cfra+dframe,vg_vel,vg_tan,vg_rot,vg_size); + psys->cfra = cfra+dframe; + } + } + + cache->simframe= framenr; + cache->flag |= PTCACHE_SIMULATION_VALID; psys->recalc = 0; - psys->cfra=cfra; + psys->cfra = cfra; - if(part->type!=PART_HAIR) - write_particles_to_cache(ob, psys, cfra); + /* only write cache starting from second frame */ + if(usecache && framenr != startframe) + write_particles_to_cache(ob, psys, framenr); /* for keyed particles the path is allways known so it can be drawn */ if(part->phystype==PART_PHYS_KEYED && psys->flag&PSYS_FIRST_KEYED){ @@ -4489,8 +4623,11 @@ static void system_step(Object *ob, ParticleSystem *psys, ParticleSystemModifier else if(psys->pathcache) psys_free_path_cache(psys); - if(vg_vel) - MEM_freeN(vg_vel); + /* cleanup */ + if(vg_vel) MEM_freeN(vg_vel); + if(vg_tan) MEM_freeN(vg_tan); + if(vg_rot) MEM_freeN(vg_rot); + if(vg_size) MEM_freeN(vg_size); if(psys->lattice){ end_latt_deform(); @@ -4498,105 +4635,97 @@ static void system_step(Object *ob, ParticleSystem *psys, ParticleSystemModifier } } -void psys_to_softbody(Object *ob, ParticleSystem *psys, int force_recalc) +static void psys_to_softbody(Object *ob, ParticleSystem *psys) { SoftBody *sb; short softflag; - if((psys->softflag&OB_SB_ENABLE)==0) return; - - if(psys->recalc || force_recalc) - psys->softflag|=OB_SB_REDO; + if(!(psys->softflag & OB_SB_ENABLE)) + return; /* let's replace the object's own softbody with the particle softbody */ /* a temporary solution before cloth simulation is implemented, jahka */ /* save these */ - sb=ob->soft; - softflag=ob->softflag; + sb= ob->soft; + softflag= ob->softflag; /* swich to new ones */ - ob->soft=psys->soft; - ob->softflag=psys->softflag; + ob->soft= psys->soft; + ob->softflag= psys->softflag; /* do softbody */ sbObjectStep(ob, (float)G.scene->r.cfra, NULL, psys_count_keys(psys)); /* return things back to normal */ - psys->soft=ob->soft; - psys->softflag=ob->softflag; + psys->soft= ob->soft; + psys->softflag= ob->softflag; - ob->soft=sb; - ob->softflag=softflag; + ob->soft= sb; + ob->softflag= softflag; } + static int hair_needs_recalc(ParticleSystem *psys) { - if((psys->flag & PSYS_EDITED)==0 && ( - (psys->flag & PSYS_HAIR_DONE)==0 - || psys->recalc & PSYS_RECALC_HAIR) - ) { + if((psys->flag & PSYS_EDITED)==0 && + ((psys->flag & PSYS_HAIR_DONE)==0 || psys->recalc & PSYS_RECALC_HAIR)) { psys->recalc &= ~PSYS_RECALC_HAIR; return 1; } return 0; } -/* main particle update call, checks that things are ok on the large scale before actual particle calculations */ -void particle_system_update(Object *ob, ParticleSystem *psys){ - ParticleSystemModifierData *psmd=0; +/* main particle update call, checks that things are ok on the large scale before actual particle calculations */ +void particle_system_update(Object *ob, ParticleSystem *psys) +{ + ParticleSystemModifierData *psmd; float cfra; if(!psys_check_enabled(ob, psys)) return; - cfra=bsystem_time(ob,(float)G.scene->r.cfra,0.0); + cfra= bsystem_time(ob, (float)G.scene->r.cfra, 0.0f); psmd= psys_get_modifier(ob, psys); /* system was already updated from modifier stack */ - if(psmd->flag&eParticleSystemFlag_psys_updated) { + if(psmd->flag & eParticleSystemFlag_psys_updated) { psmd->flag &= ~eParticleSystemFlag_psys_updated; /* make sure it really was updated to cfra */ - if(psys->cfra==cfra) + if(psys->cfra == cfra) return; } if(!psmd->dm) return; - /* baked path softbody */ - if(psys->part->type==PART_HAIR && psys->soft) - psys_to_softbody(ob, psys, 0); - - /* not needed, this is all handled in hair_step */ - ///* is the mesh changing under the edited particles? */ - //if((psys->flag & PSYS_EDITED) && psys->part->type==PART_HAIR && psys->recalc & PSYS_RECALC_HAIR) { - // /* Just update the particles on the mesh */ - // psys_update_edithair_dmfaces(ob, psmd->dm, psys); - //} - - if(psys->part->type==PART_HAIR && hair_needs_recalc(psys)){ + /* (re-)create hair */ + if(psys->part->type==PART_HAIR && hair_needs_recalc(psys)) { float hcfra=0.0f; int i; - free_hair(psys); + + free_hair(psys, 0); /* first step is negative so particles get killed and reset */ - psys->cfra=1.0f; + psys->cfra= 1.0f; for(i=0; i<=psys->part->hair_step; i++){ hcfra=100.0f*(float)i/(float)psys->part->hair_step; - system_step(ob,psys,psmd,hcfra); - save_hair(ob,psys,psmd,hcfra); + system_step(ob, psys, psmd, hcfra); + save_hair(ob, psys, psmd, hcfra); } psys->flag |= PSYS_HAIR_DONE; - - if(psys->softflag&OB_SB_ENABLE) - psys_to_softbody(ob,psys,1); } - system_step(ob,psys,psmd,cfra); + /* handle softbody hair */ + if(psys->part->type==PART_HAIR && psys->soft) + psys_to_softbody(ob, psys); + + /* the main particle system step */ + system_step(ob, psys, psmd, cfra); - Mat4Invert(psys->imat, ob->obmat); /* used for duplicators */ + /* save matrix for duplicators */ + Mat4Invert(psys->imat, ob->obmat); } diff --git a/source/blender/blenkernel/intern/pointcache.c b/source/blender/blenkernel/intern/pointcache.c index 6da13cf4021..596ac882d24 100644 --- a/source/blender/blenkernel/intern/pointcache.c +++ b/source/blender/blenkernel/intern/pointcache.c @@ -24,22 +24,37 @@ * ***** END GPL LICENSE BLOCK ***** */ - #include <stdlib.h> #include <stdio.h> #include <string.h> #include <sys/stat.h> #include <sys/types.h> -#include "BKE_pointcache.h" +#include "MEM_guardedalloc.h" -#include "BKE_utildefines.h" -#include "BKE_global.h" -#include "BKE_library.h" +#include "DNA_ID.h" +#include "DNA_cloth_types.h" +#include "DNA_modifier_types.h" +#include "DNA_object_types.h" +#include "DNA_object_force.h" +#include "DNA_particle_types.h" +#include "DNA_scene_types.h" #include "BLI_blenlib.h" + +#include "BKE_cloth.h" +#include "BKE_depsgraph.h" +#include "BKE_global.h" +#include "BKE_library.h" +#include "BKE_main.h" +#include "BKE_modifier.h" +#include "BKE_object.h" +#include "BKE_particle.h" +#include "BKE_pointcache.h" +#include "BKE_softbody.h" #include "BKE_utildefines.h" + /* needed for directory lookup */ #ifndef WIN32 #include <dirent.h> @@ -47,30 +62,150 @@ #include "BLI_winstuff.h" #endif +/* untitled blend's need getpid for a unique name */ +#ifdef WIN32 +#include <process.h> +#else +#include <unistd.h> +#endif + +#ifdef _WIN32 +#ifndef snprintf +#define snprintf _snprintf +#endif +#endif + +/* Creating ID's */ + +void BKE_ptcache_id_from_softbody(PTCacheID *pid, Object *ob, SoftBody *sb) +{ + ParticleSystemModifierData *psmd; + ModifierData *md; + int a; + + memset(pid, 0, sizeof(PTCacheID)); + + pid->ob= ob; + pid->data= sb; + pid->type= PTCACHE_TYPE_SOFTBODY; + pid->cache= sb->pointcache; + + if(sb->particles) { + psmd= psys_get_modifier(ob, sb->particles); + pid->stack_index= modifiers_indexInObject(ob, (ModifierData*)psmd); + } + else { + for(a=0, md=ob->modifiers.first; md; md=md->next, a++) { + if(md->type == eModifierType_Softbody) { + pid->stack_index = a; + break; + } + } + } +} + +void BKE_ptcache_id_from_particles(PTCacheID *pid, Object *ob, ParticleSystem *psys) +{ + ParticleSystemModifierData *psmd= psys_get_modifier(ob, psys); + + memset(pid, 0, sizeof(PTCacheID)); + + pid->ob= ob; + pid->data= psys; + pid->type= PTCACHE_TYPE_PARTICLES; + pid->stack_index= modifiers_indexInObject(ob, (ModifierData *)psmd); + pid->cache= psys->pointcache; +} + +void BKE_ptcache_id_from_cloth(PTCacheID *pid, Object *ob, ClothModifierData *clmd) +{ + memset(pid, 0, sizeof(PTCacheID)); + + pid->ob= ob; + pid->data= clmd; + pid->type= PTCACHE_TYPE_CLOTH; + pid->stack_index= modifiers_indexInObject(ob, (ModifierData *)clmd); + pid->cache= clmd->point_cache; +} + +void BKE_ptcache_ids_from_object(ListBase *lb, Object *ob) +{ + PTCacheID *pid; + ParticleSystem *psys; + ModifierData *md; + + lb->first= lb->last= NULL; + + if(ob->soft) { + pid= MEM_callocN(sizeof(PTCacheID), "PTCacheID"); + BKE_ptcache_id_from_softbody(pid, ob, ob->soft); + BLI_addtail(lb, pid); + } + + for(psys=ob->particlesystem.first; psys; psys=psys->next) { + pid= MEM_callocN(sizeof(PTCacheID), "PTCacheID"); + BKE_ptcache_id_from_particles(pid, ob, psys); + BLI_addtail(lb, pid); + + if(psys->soft) { + pid= MEM_callocN(sizeof(PTCacheID), "PTCacheID"); + BKE_ptcache_id_from_softbody(pid, ob, psys->soft); + BLI_addtail(lb, pid); + } + } + + for(md=ob->modifiers.first; md; md=md->next) { + if(md->type == eModifierType_Cloth) { + pid= MEM_callocN(sizeof(PTCacheID), "PTCacheID"); + BKE_ptcache_id_from_cloth(pid, ob, (ClothModifierData*)md); + BLI_addtail(lb, pid); + } + } +} + /* Takes an Object ID and returns a unique name - id: object id - cfra: frame for the cache, can be negative - stack_index: index in the modifier stack. we can have cache for more then one stack_index */ -static int ptcache_path(char *filename) +#define MAX_PTCACHE_PATH FILE_MAX +#define MAX_PTCACHE_FILE ((FILE_MAXDIR+FILE_MAXFILE)*2) + +static int ptcache_path(PTCacheID *pid, char *filename) { - char dir[FILE_MAX], file[FILE_MAX]; /* we dont want the dir, only the file */ + Library *lib; int i; + + lib= (pid)? pid->ob->id.lib: NULL; + + if (G.relbase_valid || lib) { + char file[MAX_PTCACHE_PATH]; /* we dont want the dir, only the file */ + char *blendfilename; + + blendfilename= (lib)? lib->filename: G.sce; + + BLI_split_dirfile_basic(blendfilename, NULL, file); + i = strlen(file); + + /* remove .blend */ + if (i > 6) + file[i-6] = '\0'; + + snprintf(filename, MAX_PTCACHE_PATH, "//"PTCACHE_PATH"%s", file); /* add blend file name to pointcache dir */ + BLI_convertstringcode(filename, blendfilename); + BLI_add_slash(filename); + return strlen(filename); + } - BLI_split_dirfile(G.sce, dir, file); - i = strlen(file); - - /* remove .blend */ - if (i > 6) - file[i-6] = '\0'; - - sprintf(filename, PTCACHE_PATH"%s/", file); /* add blend file name to pointcache dir */ - BLI_convertstringcode(filename, G.sce, 0); + /* use the temp path. this is weak but better then not using point cache at all */ + /* btempdir is assumed to exist and ALWAYS has a trailing slash */ + snprintf(filename, MAX_PTCACHE_PATH, "%s"PTCACHE_PATH"%d", btempdir, abs(getpid())); + BLI_add_slash(filename); return strlen(filename); } -int BKE_ptcache_id_filename(struct ID *id, char *filename, int cfra, int stack_index, short do_path, short do_ext) +static int BKE_ptcache_id_filename(PTCacheID *pid, char *filename, int cfra, short do_path, short do_ext) { int len=0; char *idname; @@ -78,23 +213,23 @@ int BKE_ptcache_id_filename(struct ID *id, char *filename, int cfra, int stack_i filename[0] = '\0'; newname = filename; - if (!G.relbase_valid) return 0; /* save blend fiel before using pointcache */ + /*if (!G.relbase_valid) return 0; *//* save blend file before using pointcache */ /* start with temp dir */ if (do_path) { - len = ptcache_path(filename); + len = ptcache_path(pid, filename); newname += len; } - idname = (id->name+2); + idname = (pid->ob->id.name+2); /* convert chars to hex so they are always a valid filename */ while('\0' != *idname) { - sprintf(newname, "%02X", (char)(*idname++)); + snprintf(newname, MAX_PTCACHE_FILE, "%02X", (char)(*idname++)); newname+=2; len += 2; } if (do_ext) { - sprintf(newname, "_%06d_%02d"PTCACHE_EXT, cfra, stack_index); /* always 6 chars */ + snprintf(newname, MAX_PTCACHE_FILE, "_%06d_%02d"PTCACHE_EXT, cfra, pid->stack_index); /* always 6 chars */ len += 16; } @@ -102,65 +237,98 @@ int BKE_ptcache_id_filename(struct ID *id, char *filename, int cfra, int stack_i } /* youll need to close yourself after! */ -FILE *BKE_ptcache_id_fopen(struct ID *id, char mode, int cfra, int stack_index) +PTCacheFile *BKE_ptcache_file_open(PTCacheID *pid, int mode, int cfra) { - /* mode is same as fopen's modes */ + PTCacheFile *pf; FILE *fp = NULL; char filename[(FILE_MAXDIR+FILE_MAXFILE)*2]; - if (!G.relbase_valid) return NULL; /* save blend fiel before using pointcache */ + /* don't allow writing for linked objects */ + if(pid->ob->id.lib && mode == PTCACHE_FILE_WRITE) + return NULL; + + /*if (!G.relbase_valid) return NULL; *//* save blend file before using pointcache */ - BKE_ptcache_id_filename(id, filename, cfra, stack_index, 1, 1); + BKE_ptcache_id_filename(pid, filename, cfra, 1, 1); - if (mode=='r') { + if (mode==PTCACHE_FILE_READ) { if (!BLI_exists(filename)) { return NULL; } fp = fopen(filename, "rb"); - } else if (mode=='w') { + } else if (mode==PTCACHE_FILE_WRITE) { BLI_make_existing_file(filename); /* will create the dir if needs be, same as //textures is created */ fp = fopen(filename, "wb"); } - if (!fp) { + if (!fp) return NULL; - } + + pf= MEM_mallocN(sizeof(PTCacheFile), "PTCacheFile"); + pf->fp= fp; - return fp; + return pf; +} + +void BKE_ptcache_file_close(PTCacheFile *pf) +{ + fclose(pf->fp); + MEM_freeN(pf); +} + +int BKE_ptcache_file_read_floats(PTCacheFile *pf, float *f, int tot) +{ + return (fread(f, sizeof(float), tot, pf->fp) == tot); +} + +int BKE_ptcache_file_write_floats(PTCacheFile *pf, float *f, int tot) +{ + return (fwrite(f, sizeof(float), tot, pf->fp) == tot); } /* youll need to close yourself after! - * mode, + * mode - PTCACHE_CLEAR_ALL, */ -void BKE_ptcache_id_clear(struct ID *id, char mode, int cfra, int stack_index) +void BKE_ptcache_id_clear(PTCacheID *pid, int mode, int cfra) { int len; /* store the length of the string */ /* mode is same as fopen's modes */ DIR *dir; struct dirent *de; - char path[FILE_MAX]; - char filename[(FILE_MAXDIR+FILE_MAXFILE)*2]; - char path_full[(FILE_MAXDIR+FILE_MAXFILE)*2]; - - if (!G.relbase_valid) return; /* save blend fiel before using pointcache */ + char path[MAX_PTCACHE_PATH]; + char filename[MAX_PTCACHE_FILE]; + char path_full[MAX_PTCACHE_FILE]; + char ext[MAX_PTCACHE_PATH]; + + if(!pid->cache) + return; + + /* don't allow clearing for linked objects */ + if(pid->ob->id.lib) + return; + + /*if (!G.relbase_valid) return; *//* save blend file before using pointcache */ /* clear all files in the temp dir with the prefix of the ID and the ".bphys" suffix */ switch (mode) { case PTCACHE_CLEAR_ALL: case PTCACHE_CLEAR_BEFORE: case PTCACHE_CLEAR_AFTER: - ptcache_path(path); - len = BKE_ptcache_id_filename(id, filename, cfra, stack_index, 0, 0); /* no path */ + ptcache_path(pid, path); + + len = BKE_ptcache_id_filename(pid, filename, cfra, 0, 0); /* no path */ dir = opendir(path); if (dir==NULL) return; + + snprintf(ext, sizeof(ext), "_%02d"PTCACHE_EXT, pid->stack_index); while ((de = readdir(dir)) != NULL) { - if (strstr(de->d_name, PTCACHE_EXT)) { /* do we have the right extension?*/ + if (strstr(de->d_name, ext)) { /* do we have the right extension?*/ if (strncmp(filename, de->d_name, len ) == 0) { /* do we have the right prefix */ if (mode == PTCACHE_CLEAR_ALL) { BLI_join_dirfile(path_full, path, de->d_name); @@ -169,8 +337,9 @@ void BKE_ptcache_id_clear(struct ID *id, char mode, int cfra, int stack_index) /* read the number of the file */ int frame, len2 = strlen(de->d_name); char num[7]; + if (len2 > 15) { /* could crash if trying to copy a string out of this range*/ - strncpy(num, de->d_name + (strlen(de->d_name) - 15), 6); + BLI_strncpy(num, de->d_name + (strlen(de->d_name) - 15), sizeof(num)); frame = atoi(num); if((mode==PTCACHE_CLEAR_BEFORE && frame < cfra) || @@ -188,18 +357,248 @@ void BKE_ptcache_id_clear(struct ID *id, char mode, int cfra, int stack_index) break; case PTCACHE_CLEAR_FRAME: - len = BKE_ptcache_id_filename(id, filename, cfra, stack_index, 1, 1); /* no path */ + len = BKE_ptcache_id_filename(pid, filename, cfra, 1, 1); /* no path */ BLI_delete(filename, 0, 0); break; } - return; } -int BKE_ptcache_id_exist(struct ID *id, int cfra, int stack_index) +int BKE_ptcache_id_exist(PTCacheID *pid, int cfra) { - char filename[(FILE_MAXDIR+FILE_MAXFILE)*2]; + char filename[MAX_PTCACHE_FILE]; + + if(!pid->cache) + return 0; - BKE_ptcache_id_filename(id, filename, cfra, stack_index, 1, 1); + BKE_ptcache_id_filename(pid, filename, cfra, 1, 1); return BLI_exists(filename); } + +void BKE_ptcache_id_time(PTCacheID *pid, float cfra, int *startframe, int *endframe, float *timescale) +{ + Object *ob; + PointCache *cache; + float offset, time, nexttime; + + /* time handling for point cache: + * - simulation time is scaled by result of bsystem_time + * - for offsetting time only time offset is taken into account, since + * that's always the same and can't be animated. a timeoffset which + * varies over time is not simpe to support. + * - field and motion blur offsets are currently ignored, proper solution + * is probably to interpolate results from two frames for that .. + */ + + ob= pid->ob; + cache= pid->cache; + + if(timescale) { + time= bsystem_time(ob, cfra, 0.0f); + nexttime= bsystem_time(ob, cfra+1.0f, 0.0f); + + *timescale= MAX2(nexttime - time, 0.0f); + } + + if(startframe && endframe) { + *startframe= cache->startframe; + *endframe= cache->endframe; + + if ((ob->ipoflag & OB_OFFS_PARENT) && (ob->partype & PARSLOW)==0) { + offset= give_timeoffset(ob); + + *startframe += (int)(offset+0.5f); + *endframe += (int)(offset+0.5f); + } + } +} + +int BKE_ptcache_id_reset(PTCacheID *pid, int mode) +{ + PointCache *cache; + int reset, clear; + + if(!pid->cache) + return 0; + + cache= pid->cache; + reset= 0; + clear= 0; + + if(mode == PTCACHE_RESET_DEPSGRAPH) { + if(!(cache->flag & PTCACHE_BAKED) && !BKE_ptcache_get_continue_physics()) { + reset= 1; + clear= 1; + } + else + cache->flag |= PTCACHE_OUTDATED; + } + else if(mode == PTCACHE_RESET_BAKED) { + if(!BKE_ptcache_get_continue_physics()) { + reset= 1; + clear= 1; + } + else + cache->flag |= PTCACHE_OUTDATED; + } + else if(mode == PTCACHE_RESET_OUTDATED) { + reset = 1; + + if(cache->flag & PTCACHE_OUTDATED) + if(!(cache->flag & PTCACHE_BAKED)) + clear= 1; + } + + if(reset) { + cache->flag &= ~(PTCACHE_OUTDATED|PTCACHE_SIMULATION_VALID); + cache->simframe= 0; + + if(pid->type == PTCACHE_TYPE_CLOTH) + cloth_free_modifier(pid->ob, pid->data); + else if(pid->type == PTCACHE_TYPE_SOFTBODY) + sbFreeSimulation(pid->data); + else if(pid->type == PTCACHE_TYPE_PARTICLES) + psys_reset(pid->data, PSYS_RESET_DEPSGRAPH); + } + if(clear) + BKE_ptcache_id_clear(pid, PTCACHE_CLEAR_ALL, 0); + + return (reset || clear); +} + +int BKE_ptcache_object_reset(Object *ob, int mode) +{ + PTCacheID pid; + ParticleSystem *psys; + ModifierData *md; + int reset, skip; + + reset= 0; + skip= 0; + + if(ob->soft) { + BKE_ptcache_id_from_softbody(&pid, ob, ob->soft); + reset |= BKE_ptcache_id_reset(&pid, mode); + } + + for(psys=ob->particlesystem.first; psys; psys=psys->next) { + /* Baked softbody hair has to be checked first, because we don't want to reset */ + /* particles or softbody in that case -jahka */ + if(psys->soft) { + BKE_ptcache_id_from_softbody(&pid, ob, psys->soft); + if(mode == PSYS_RESET_ALL || !(psys->part->type == PART_HAIR && (pid.cache->flag & PTCACHE_BAKED))) + reset |= BKE_ptcache_id_reset(&pid, mode); + else + skip = 1; + } + + if(skip == 0) { + BKE_ptcache_id_from_particles(&pid, ob, psys); + reset |= BKE_ptcache_id_reset(&pid, mode); + } + } + + for(md=ob->modifiers.first; md; md=md->next) { + if(md->type == eModifierType_Cloth) { + BKE_ptcache_id_from_cloth(&pid, ob, (ClothModifierData*)md); + reset |= BKE_ptcache_id_reset(&pid, mode); + } + } + + return reset; +} + +/* Use this when quitting blender, with unsaved files */ +void BKE_ptcache_remove(void) +{ + char path[MAX_PTCACHE_PATH]; + char path_full[MAX_PTCACHE_PATH]; + int rmdir = 1; + + ptcache_path(NULL, path); + + if (BLI_exist(path)) { + /* The pointcache dir exists? - remove all pointcache */ + + DIR *dir; + struct dirent *de; + + dir = opendir(path); + if (dir==NULL) + return; + + while ((de = readdir(dir)) != NULL) { + if( strcmp(de->d_name, ".")==0 || strcmp(de->d_name, "..")==0) { + /* do nothing */ + } else if (strstr(de->d_name, PTCACHE_EXT)) { /* do we have the right extension?*/ + BLI_join_dirfile(path_full, path, de->d_name); + BLI_delete(path_full, 0, 0); + } else { + rmdir = 0; /* unknown file, dont remove the dir */ + } + } + + closedir(dir); + } else { + rmdir = 0; /* path dosnt exist */ + } + + if (rmdir) { + BLI_delete(path, 1, 0); + } +} + +/* Continuous Interaction */ + +static int CONTINUE_PHYSICS = 0; + +void BKE_ptcache_set_continue_physics(int enable) +{ + Object *ob; + + if(CONTINUE_PHYSICS != enable) { + CONTINUE_PHYSICS = enable; + + if(CONTINUE_PHYSICS == 0) { + for(ob=G.main->object.first; ob; ob=ob->id.next) + if(BKE_ptcache_object_reset(ob, PTCACHE_RESET_OUTDATED)) + DAG_object_flush_update(G.scene, ob, OB_RECALC_DATA); + } + } +} + +int BKE_ptcache_get_continue_physics() +{ + return CONTINUE_PHYSICS; +} + +/* Point Cache */ + +PointCache *BKE_ptcache_add() +{ + PointCache *cache; + + cache= MEM_callocN(sizeof(PointCache), "PointCache"); + cache->startframe= 1; + cache->endframe= 250; + + return cache; +} + +void BKE_ptcache_free(PointCache *cache) +{ + MEM_freeN(cache); +} + +PointCache *BKE_ptcache_copy(PointCache *cache) +{ + PointCache *ncache; + + ncache= MEM_dupallocN(cache); + + ncache->flag= 0; + ncache->simframe= 0; + + return ncache; +} + diff --git a/source/blender/blenkernel/intern/property.c b/source/blender/blenkernel/intern/property.c index b86efb97fc5..509cd8cf750 100644 --- a/source/blender/blenkernel/intern/property.c +++ b/source/blender/blenkernel/intern/property.c @@ -81,8 +81,7 @@ bProperty *copy_property(bProperty *prop) void copy_properties(ListBase *lbn, ListBase *lbo) { bProperty *prop, *propn; - - lbn->first= lbn->last= 0; + free_properties( lbn ); /* incase we are copying to an object with props */ prop= lbo->first; while(prop) { propn= copy_property(prop); @@ -137,7 +136,7 @@ bProperty *new_property(int type) return prop; } -bProperty *get_property(Object *ob, char *name) +bProperty *get_ob_property(Object *ob, char *name) { bProperty *prop; @@ -149,6 +148,17 @@ bProperty *get_property(Object *ob, char *name) return NULL; } +void set_ob_property(Object *ob, bProperty *propc) +{ + bProperty *prop; + prop= get_ob_property(ob, propc->name); + if(prop) { + free_property(prop); + BLI_remlink(&ob->prop, prop); + } + BLI_addtail(&ob->prop, copy_property(propc)); +} + /* negative: prop is smaller * positive: prop is larger */ diff --git a/source/blender/blenkernel/intern/sca.c b/source/blender/blenkernel/intern/sca.c index 81da2fcbd96..e8c6c5c199f 100644 --- a/source/blender/blenkernel/intern/sca.c +++ b/source/blender/blenkernel/intern/sca.c @@ -150,6 +150,12 @@ void init_sensor(bSensor *sens) case SENS_PROPERTY: sens->data= MEM_callocN(sizeof(bPropertySensor), "propsens"); break; + case SENS_ACTUATOR: + sens->data= MEM_callocN(sizeof(bActuatorSensor), "actsens"); + break; + case SENS_DELAY: + sens->data= MEM_callocN(sizeof(bDelaySensor), "delaysens"); + break; case SENS_MOUSE: ms=sens->data= MEM_callocN(sizeof(bMouseSensor), "mousesens"); //XXX ms->type= LEFTMOUSE; @@ -411,6 +417,7 @@ void init_actuator(bActuator *act) switch(act->type) { #ifdef __NLA case ACT_ACTION: + case ACT_SHAPEACTION: act->data= MEM_callocN(sizeof(bActionActuator), "actionact"); break; #endif @@ -458,6 +465,15 @@ void init_actuator(bActuator *act) case ACT_VISIBILITY: act->data= MEM_callocN(sizeof(bVisibilityActuator), "visibility act"); break; + case ACT_2DFILTER: + act->data = MEM_callocN(sizeof( bTwoDFilterActuator ), "2d filter act"); + break; + case ACT_PARENT: + act->data = MEM_callocN(sizeof( bParentActuator ), "parent act"); + break; + case ACT_STATE: + act->data = MEM_callocN(sizeof( bStateActuator ), "state act"); + break; default: ; /* this is very severe... I cannot make any memory for this */ /* logic brick... */ diff --git a/source/blender/blenkernel/intern/scene.c b/source/blender/blenkernel/intern/scene.c index ebb2a80314f..096c04b7bd3 100644 --- a/source/blender/blenkernel/intern/scene.c +++ b/source/blender/blenkernel/intern/scene.c @@ -61,11 +61,13 @@ #include "BKE_anim.h" #include "BKE_armature.h" #include "BKE_colortools.h" +#include "BKE_colortools.h" #include "BKE_constraint.h" #include "BKE_depsgraph.h" #include "BKE_global.h" #include "BKE_group.h" #include "BKE_ipo.h" +#include "BKE_idprop.h" #include "BKE_image.h" #include "BKE_key.h" #include "BKE_library.h" @@ -78,8 +80,12 @@ #include "BKE_utildefines.h" //XXX #include "BIF_previewrender.h" +//XXX #include "BIF_editseq.h" +#ifndef DISABLE_PYTHON #include "BPY_extern.h" +#endif + #include "BLI_arithb.h" #include "BLI_blenlib.h" @@ -136,7 +142,10 @@ void free_scene(Scene *sce) if(sce->radio) MEM_freeN(sce->radio); sce->radio= 0; +#ifndef DISABLE_PYTHON BPY_free_scriptlink(&sce->scriptlink); +#endif + if (sce->r.avicodecdata) { free_avicodecdata(sce->r.avicodecdata); MEM_freeN(sce->r.avicodecdata); @@ -147,8 +156,14 @@ void free_scene(Scene *sce) MEM_freeN(sce->r.qtcodecdata); sce->r.qtcodecdata = NULL; } + if (sce->r.ffcodecdata.properties) { + IDP_FreeProperty(sce->r.ffcodecdata.properties); + MEM_freeN(sce->r.ffcodecdata.properties); + sce->r.ffcodecdata.properties = NULL; + } BLI_freelistN(&sce->markers); + BLI_freelistN(&sce->transform_spaces); BLI_freelistN(&sce->r.layers); if(sce->toolsettings){ @@ -179,6 +194,7 @@ Scene *add_scene(char *name) sce->lay= 1; sce->selectmode= SCE_SELECT_VERTEX; sce->editbutsize= 0.1; + sce->autokey_mode= U.autokey_mode; sce->r.mode= R_GAMMA; sce->r.cfra= 1; @@ -215,6 +231,15 @@ Scene *add_scene(char *name) sce->r.stereomode = 1; // no stereo + sce->r.simplify_subsurf= 6; + sce->r.simplify_particles= 1.0f; + sce->r.simplify_shadowsamples= 16; + sce->r.simplify_aosss= 1.0f; + + sce->r.cineonblack= 95; + sce->r.cineonwhite= 685; + sce->r.cineongamma= 1.7f; + sce->toolsettings = MEM_callocN(sizeof(struct ToolSettings),"Tool Settings Struct"); sce->toolsettings->cornertype=1; sce->toolsettings->degr = 90; @@ -234,6 +259,21 @@ Scene *add_scene(char *name) sce->toolsettings->select_thresh= 0.01f; sce->toolsettings->jointrilimit = 0.8f; + sce->toolsettings->skgen_resolution = 100; + sce->toolsettings->skgen_threshold_internal = 0.01f; + sce->toolsettings->skgen_threshold_external = 0.01f; + sce->toolsettings->skgen_angle_limit = 45.0f; + sce->toolsettings->skgen_length_ratio = 1.3f; + sce->toolsettings->skgen_length_limit = 1.5f; + sce->toolsettings->skgen_correlation_limit = 0.98f; + sce->toolsettings->skgen_symmetry_limit = 0.1f; + sce->toolsettings->skgen_postpro = SKGEN_SMOOTH; + sce->toolsettings->skgen_postpro_passes = 1; + sce->toolsettings->skgen_options = SKGEN_FILTER_INTERNAL|SKGEN_FILTER_EXTERNAL|SKGEN_FILTER_SMART|SKGEN_HARMONIC|SKGEN_SUB_CORRELATION|SKGEN_STICK_TO_EMBEDDING; + sce->toolsettings->skgen_subdivisions[0] = SKGEN_SUB_CORRELATION; + sce->toolsettings->skgen_subdivisions[1] = SKGEN_SUB_LENGTH; + sce->toolsettings->skgen_subdivisions[2] = SKGEN_SUB_ANGLE; + pset= &sce->toolsettings->particle; pset->flag= PE_KEEP_LENGTHS|PE_LOCK_FIRST|PE_DEFLECT_EMITTER; pset->emitterdist= 0.25f; @@ -252,13 +292,12 @@ Scene *add_scene(char *name) strcpy(sce->r.backbuf, "//backbuf"); strcpy(sce->r.pic, U.renderdir); - strcpy(sce->r.ftype, "//ftype"); BLI_init_rctf(&sce->r.safety, 0.1f, 0.9f, 0.1f, 0.9f); sce->r.osa= 8; sculptdata_init(sce); - + /* note; in header_info.c the scene copy happens..., if you add more to renderdata it has to be checked there */ scene_add_render_layer(sce); @@ -285,6 +324,9 @@ void set_scene_bg(Scene *sce) GroupObject *go; int flag; + // Note: this here is defined in editseq.c (BIF_editseq.h), NOT in blenkernel! + //XXX clear_last_seq(); + G.scene= sce; /* check for cyclic sets, for reading old files but also for definite security (py?) */ @@ -330,7 +372,7 @@ void set_scene_bg(Scene *sce) /* no full animation update, this to enable render code to work (render code calls own animation updates) */ /* do we need FRAMECHANGED in set_scene? */ -// if (G.f & G_DOSCRIPTLINKS) BPY_do_all_scripts(SCRIPT_FRAMECHANGED); +// if (G.f & G_DOSCRIPTLINKS) BPY_do_all_scripts(SCRIPT_FRAMECHANGED, 0); } /* called from creator.c */ @@ -413,6 +455,9 @@ int next_object(int val, Base **base, Object **ob) duplilist= object_duplilist(G.scene, (*base)->object); dupob= duplilist->first; + + if(!dupob) + free_object_duplilist(duplilist); } } } @@ -494,27 +539,23 @@ void scene_select_base(Scene *sce, Base *selbase) int scene_check_setscene(Scene *sce) { Scene *scene; + int a, totscene; if(sce->set==NULL) return 1; - /* LIB_DOIT is the free flag to tag library data */ + totscene= 0; for(scene= G.main->scene.first; scene; scene= scene->id.next) - scene->id.flag &= ~LIB_DOIT; - - scene= sce; - while(scene->set) { - scene->id.flag |= LIB_DOIT; - /* when set has flag set, we got a cycle */ - if(scene->set->id.flag & LIB_DOIT) - break; - scene= scene->set; - } - - if(scene->set) { - /* the tested scene gets zero'ed, that's typically current scene */ - sce->set= NULL; - return 0; + totscene++; + + for(a=0, scene=sce; scene->set; scene=scene->set, a++) { + /* more iterations than scenes means we have a cycle */ + if(a > totscene) { + /* the tested scene gets zero'ed, that's typically current scene */ + sce->set= NULL; + return 0; + } } + return 1; } @@ -550,9 +591,9 @@ void scene_update_for_newframe(Scene *sce, unsigned int lay) /* object ipos are calculated in where_is_object */ do_all_data_ipos(); - - if (G.f & G_DOSCRIPTLINKS) BPY_do_all_scripts(SCRIPT_FRAMECHANGED); - +#ifndef DISABLE_PYTHON + if (G.f & G_DOSCRIPTLINKS) BPY_do_all_scripts(SCRIPT_FRAMECHANGED, 0); +#endif /* sets first, we allow per definition current scene to have dependencies on sets */ for(sce= sce->set; sce; sce= sce->set) scene_update(sce, lay); @@ -587,8 +628,10 @@ void sculptdata_init(Scene *sce) sd= &sce->sculptdata; - if(sd->cumap) + if(sd->cumap) { curvemapping_free(sd->cumap); + sd->cumap = NULL; + } memset(sd, 0, sizeof(SculptData)); @@ -600,9 +643,9 @@ void sculptdata_init(Scene *sce) sd->grabbrush.strength = sd->layerbrush.strength = sd->flattenbrush.strength = 25; sd->drawbrush.dir = sd->pinchbrush.dir = sd->inflatebrush.dir = sd->layerbrush.dir= 1; - sd->drawbrush.airbrush = sd->smoothbrush.airbrush = - sd->pinchbrush.airbrush = sd->inflatebrush.airbrush = - sd->layerbrush.airbrush = sd->flattenbrush.airbrush = 0; + sd->drawbrush.flag = sd->smoothbrush.flag = + sd->pinchbrush.flag = sd->inflatebrush.flag = + sd->layerbrush.flag = sd->flattenbrush.flag = 0; sd->drawbrush.view= 0; sd->brush_type= DRAW_BRUSH; sd->texact= -1; @@ -656,13 +699,8 @@ void sculptsession_free(Scene *sce) if(ss->mats) MEM_freeN(ss->mats); - if(ss->propset) { - if(ss->propset->texdata) - MEM_freeN(ss->propset->texdata); - if(ss->propset->num) - MEM_freeN(ss->propset->num); - MEM_freeN(ss->propset); - } + if(ss->radialcontrol) + MEM_freeN(ss->radialcontrol); sculpt_vertexusers_free(ss); if(ss->texcache) @@ -702,3 +740,38 @@ void sculpt_reset_curve(SculptData *sd) curvemapping_changed(sd->cumap, 0); } + +/* render simplification */ + +int get_render_subsurf_level(RenderData *r, int lvl) +{ + if(G.rt == 1 && (r->mode & R_SIMPLIFY)) + return MIN2(r->simplify_subsurf, lvl); + else + return lvl; +} + +int get_render_child_particle_number(RenderData *r, int num) +{ + if(G.rt == 1 && (r->mode & R_SIMPLIFY)) + return (int)(r->simplify_particles*num); + else + return num; +} + +int get_render_shadow_samples(RenderData *r, int samples) +{ + if(G.rt == 1 && (r->mode & R_SIMPLIFY) && samples > 0) + return MIN2(r->simplify_shadowsamples, samples); + else + return samples; +} + +float get_render_aosss_error(RenderData *r, float error) +{ + if(G.rt == 1 && (r->mode & R_SIMPLIFY)) + return ((1.0f-r->simplify_aosss)*10.0f + 1.0f)*error; + else + return error; +} + diff --git a/source/blender/blenkernel/intern/screen.c b/source/blender/blenkernel/intern/screen.c index 926a62e9f69..bf2ed2c658e 100644 --- a/source/blender/blenkernel/intern/screen.c +++ b/source/blender/blenkernel/intern/screen.c @@ -20,7 +20,9 @@ * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. * All rights reserved. * - * Contributor(s): Blender Foundation 2002-2008 + * The Original Code is: all of this file. + * + * Contributor(s): none yet. * * ***** END GPL LICENSE BLOCK ***** */ diff --git a/source/blender/blenkernel/intern/script.c b/source/blender/blenkernel/intern/script.c index ab7ae0e09fe..e34b1d0a1dd 100644 --- a/source/blender/blenkernel/intern/script.c +++ b/source/blender/blenkernel/intern/script.c @@ -32,18 +32,21 @@ */ #include "BKE_script.h" -//XXX #include "BPI_script.h" +#include "DNA_space_types.h" #include "MEM_guardedalloc.h" - +/* #include "BLI_blenlib.h" #include "BKE_utildefines.h" #include "BKE_library.h" #include "BKE_global.h" #include "BKE_main.h" +#ifndef DISABLE_PYTHON #include "BPY_extern.h" // Blender Python library +#endif +*/ /* XXX this function and so also the file should not be needed anymore, * since we have to force clearing all Python related data before freeing @@ -54,13 +57,8 @@ void free_script (Script *script) { if (!script) return; - - if (script->py_globaldict || script->py_button || - script->py_event || script->py_draw) - { - BPY_clear_script(script); - } - - return; +#ifndef DISABLE_PYTHON + BPY_clear_script(script); +#endif } #endif diff --git a/source/blender/blenkernel/intern/shrinkwrap.c b/source/blender/blenkernel/intern/shrinkwrap.c new file mode 100644 index 00000000000..ab98fb1f007 --- /dev/null +++ b/source/blender/blenkernel/intern/shrinkwrap.c @@ -0,0 +1,599 @@ +/** + * shrinkwrap.c + * + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * The Original Code is Copyright (C) Blender Foundation. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): André Pinto + * + * ***** END GPL LICENSE BLOCK ***** + */ +#include <string.h> +#include <float.h> +#include <math.h> +#include <memory.h> +#include <stdio.h> +#include <time.h> +#include <assert.h> + +#include "DNA_object_types.h" +#include "DNA_modifier_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_mesh_types.h" + +#include "BKE_shrinkwrap.h" +#include "BKE_DerivedMesh.h" +#include "BKE_lattice.h" +#include "BKE_utildefines.h" +#include "BKE_deform.h" +#include "BKE_cdderivedmesh.h" +#include "BKE_displist.h" +#include "BKE_global.h" +#include "BKE_subsurf.h" + +#include "BLI_arithb.h" +#include "BLI_kdtree.h" +#include "BLI_kdopbvh.h" + +#include "RE_raytrace.h" +#include "MEM_guardedalloc.h" + + +/* Util macros */ +#define TO_STR(a) #a +#define JOIN(a,b) a##b + +#define OUT_OF_MEMORY() ((void)printf("Shrinkwrap: Out of memory\n")) + +/* Benchmark macros */ +#if !defined(_WIN32) && 0 + +#include <sys/time.h> + +#define BENCH(a) \ + do { \ + double _t1, _t2; \ + struct timeval _tstart, _tend; \ + clock_t _clock_init = clock(); \ + gettimeofday ( &_tstart, NULL); \ + (a); \ + gettimeofday ( &_tend, NULL); \ + _t1 = ( double ) _tstart.tv_sec + ( double ) _tstart.tv_usec/ ( 1000*1000 ); \ + _t2 = ( double ) _tend.tv_sec + ( double ) _tend.tv_usec/ ( 1000*1000 ); \ + printf("%s: %fs (real) %fs (cpu)\n", #a, _t2-_t1, (float)(clock()-_clock_init)/CLOCKS_PER_SEC);\ + } while(0) + +#else + +#define BENCH(a) (a) + +#endif + +typedef void ( *Shrinkwrap_ForeachVertexCallback) (DerivedMesh *target, float *co, float *normal); + +/* get derived mesh */ +//TODO is anyfunction that does this? returning the derivedFinal witouth we caring if its in edit mode or not? +DerivedMesh *object_get_derived_final(Object *ob, CustomDataMask dataMask) +{ + if (ob==G.obedit) + { + DerivedMesh *final = NULL; + editmesh_get_derived_cage_and_final(&final, dataMask); + return final; + } + else + return mesh_get_derived_final(ob, dataMask); +} + +/* Space transform */ +void space_transform_from_matrixs(SpaceTransform *data, float local[4][4], float target[4][4]) +{ + float itarget[4][4]; + Mat4Invert(itarget, target); + Mat4MulSerie(data->local2target, itarget, local, 0, 0, 0, 0, 0, 0); + Mat4Invert(data->target2local, data->local2target); +} + +void space_transform_apply(const SpaceTransform *data, float *co) +{ + VecMat4MulVecfl(co, ((SpaceTransform*)data)->local2target, co); +} + +void space_transform_invert(const SpaceTransform *data, float *co) +{ + VecMat4MulVecfl(co, ((SpaceTransform*)data)->target2local, co); +} + +void space_transform_apply_normal(const SpaceTransform *data, float *no) +{ + Mat4Mul3Vecfl( ((SpaceTransform*)data)->local2target, no); + Normalize(no); // TODO: could we just determine de scale value from the matrix? +} + +void space_transform_invert_normal(const SpaceTransform *data, float *no) +{ + Mat4Mul3Vecfl(((SpaceTransform*)data)->target2local, no); + Normalize(no); // TODO: could we just determine de scale value from the matrix? +} + +/* + * Returns the squared distance between two given points + */ +static float squared_dist(const float *a, const float *b) +{ + float tmp[3]; + VECSUB(tmp, a, b); + return INPR(tmp, tmp); +} + +/* Main shrinkwrap function */ +void shrinkwrapModifier_deform(ShrinkwrapModifierData *smd, Object *ob, DerivedMesh *dm, float (*vertexCos)[3], int numVerts) +{ + + ShrinkwrapCalcData calc = NULL_ShrinkwrapCalcData; + + //remove loop dependencies on derived meshs (TODO should this be done elsewhere?) + if(smd->target == ob) smd->target = NULL; + if(smd->auxTarget == ob) smd->auxTarget = NULL; + + + //Configure Shrinkwrap calc data + calc.smd = smd; + calc.ob = ob; + calc.original = dm; + calc.numVerts = numVerts; + calc.vertexCos = vertexCos; + + //DeformVertex + calc.vgroup = get_named_vertexgroup_num(calc.ob, calc.smd->vgroup_name); + if(calc.original) + { + calc.dvert = calc.original->getVertDataArray(calc.original, CD_MDEFORMVERT); + } + else if(calc.ob->type == OB_LATTICE) + { + calc.dvert = lattice_get_deform_verts(calc.ob); + } + + + if(smd->target) + { + //TODO currently we need a copy in case object_get_derived_final returns an emDM that does not defines getVertArray or getFace array + calc.target = CDDM_copy( object_get_derived_final(smd->target, CD_MASK_BAREMESH) ); + + //TODO there might be several "bugs" on non-uniform scales matrixs.. because it will no longer be nearest surface, not sphere projection + //because space has been deformed + space_transform_setup(&calc.local2target, ob, smd->target); + + calc.keepDist = smd->keepDist; //TODO: smd->keepDist is in global units.. must change to local + } + + + //Projecting target defined - lets work! + if(calc.target) + { + switch(smd->shrinkType) + { + case MOD_SHRINKWRAP_NEAREST_SURFACE: + BENCH(shrinkwrap_calc_nearest_surface_point(&calc)); + break; + + case MOD_SHRINKWRAP_PROJECT: + BENCH(shrinkwrap_calc_normal_projection(&calc)); + break; + + case MOD_SHRINKWRAP_NEAREST_VERTEX: + BENCH(shrinkwrap_calc_nearest_vertex(&calc)); + break; + } + } + + //free memory + if(calc.target) + calc.target->release( calc.target ); +} + +/* + * Shrinkwrap to the nearest vertex + * + * it builds a kdtree of vertexs we can attach to and then + * for each vertex performs a nearest vertex search on the tree + */ +void shrinkwrap_calc_nearest_vertex(ShrinkwrapCalcData *calc) +{ + int i; + + BVHTreeFromMesh treeData = NULL_BVHTreeFromMesh; + BVHTreeNearest nearest = NULL_BVHTreeNearest; + + + BENCH(bvhtree_from_mesh_verts(&treeData, calc->target, 0.0, 2, 6)); + if(treeData.tree == NULL) + { + OUT_OF_MEMORY(); + return; + } + + //Setup nearest + nearest.index = -1; + nearest.dist = FLT_MAX; + +#pragma omp parallel for default(none) private(i) firstprivate(nearest) shared(treeData,calc) schedule(static) + for(i = 0; i<calc->numVerts; ++i) + { + float *co = calc->vertexCos[i]; + float tmp_co[3]; + float weight = vertexgroup_get_vertex_weight(calc->dvert, i, calc->vgroup); + if(weight == 0.0f) continue; + + VECCOPY(tmp_co, co); + space_transform_apply(&calc->local2target, tmp_co); //Convert the coordinates to the tree coordinates + + //Use local proximity heuristics (to reduce the nearest search) + // + //If we already had an hit before.. we assume this vertex is going to have a close hit to that other vertex + //so we can initiate the "nearest.dist" with the expected value to that last hit. + //This will lead in prunning of the search tree. + if(nearest.index != -1) + nearest.dist = squared_dist(tmp_co, nearest.co); + else + nearest.dist = FLT_MAX; + + BLI_bvhtree_find_nearest(treeData.tree, tmp_co, &nearest, treeData.nearest_callback, &treeData); + + + //Found the nearest vertex + if(nearest.index != -1) + { + //Adjusting the vertex weight, so that after interpolating it keeps a certain distance from the nearest position + float dist = sasqrt(nearest.dist); + if(dist > FLT_EPSILON) weight *= (dist - calc->keepDist)/dist; + + //Convert the coordinates back to mesh coordinates + VECCOPY(tmp_co, nearest.co); + space_transform_invert(&calc->local2target, tmp_co); + + VecLerpf(co, co, tmp_co, weight); //linear interpolation + } + } + + free_bvhtree_from_mesh(&treeData); +} + +/* + * This function raycast a single vertex and updates the hit if the "hit" is considered valid. + * Returns TRUE if "hit" was updated. + * Opts control whether an hit is valid or not + * Supported options are: + * MOD_SHRINKWRAP_CULL_TARGET_FRONTFACE (front faces hits are ignored) + * MOD_SHRINKWRAP_CULL_TARGET_BACKFACE (back faces hits are ignored) + */ +int normal_projection_project_vertex(char options, const float *vert, const float *dir, const SpaceTransform *transf, BVHTree *tree, BVHTreeRayHit *hit, BVHTree_RayCastCallback callback, void *userdata) +{ + float tmp_co[3], tmp_no[3]; + const float *co, *no; + BVHTreeRayHit hit_tmp; + + //Copy from hit (we need to convert hit rays from one space coordinates to the other + memcpy( &hit_tmp, hit, sizeof(hit_tmp) ); + + //Apply space transform (TODO readjust dist) + if(transf) + { + VECCOPY( tmp_co, vert ); + space_transform_apply( transf, tmp_co ); + co = tmp_co; + + VECCOPY( tmp_no, dir ); + space_transform_apply_normal( transf, tmp_no ); + no = tmp_no; + + hit_tmp.dist *= Mat4ToScalef( ((SpaceTransform*)transf)->local2target ); + } + else + { + co = vert; + no = dir; + } + + hit_tmp.index = -1; + + BLI_bvhtree_ray_cast(tree, co, no, 0.0f, &hit_tmp, callback, userdata); + + if(hit_tmp.index != -1) + { + float dot = INPR( dir, hit_tmp.no); + + if(((options & MOD_SHRINKWRAP_CULL_TARGET_FRONTFACE) && dot <= 0.0f) + || ((options & MOD_SHRINKWRAP_CULL_TARGET_BACKFACE) && dot >= 0.0f)) + return FALSE; //Ignore hit + + + //Inverting space transform (TODO make coeherent with the initial dist readjust) + if(transf) + { + space_transform_invert( transf, hit_tmp.co ); + space_transform_invert_normal( transf, hit_tmp.no ); + + hit_tmp.dist = VecLenf( (float*)vert, hit_tmp.co ); + } + + memcpy(hit, &hit_tmp, sizeof(hit_tmp) ); + return TRUE; + } + return FALSE; +} + + +void shrinkwrap_calc_normal_projection(ShrinkwrapCalcData *calc) +{ + int i; + + //Options about projection direction + const char use_normal = calc->smd->shrinkOpts; + float proj_axis[3] = {0.0f, 0.0f, 0.0f}; + MVert *vert = NULL; //Needed in case of vertex normal + DerivedMesh* ss_mesh = NULL; + + //Raycast and tree stuff + BVHTreeRayHit hit; + BVHTreeFromMesh treeData = NULL_BVHTreeFromMesh; //target + + //auxiliar target + DerivedMesh * aux_mesh = NULL; + BVHTreeFromMesh auxData= NULL_BVHTreeFromMesh; + SpaceTransform local2aux; + +do +{ + + //Prepare data to retrieve the direction in which we should project each vertex + if(calc->smd->projAxis == MOD_SHRINKWRAP_PROJECT_OVER_NORMAL) + { + //No Mvert information: jump to "free memory and return" part + if(calc->original == NULL) break; + + if(calc->smd->subsurfLevels) + { + SubsurfModifierData smd; + memset(&smd, 0, sizeof(smd)); + smd.subdivType = ME_CC_SUBSURF; //catmull clark + smd.levels = calc->smd->subsurfLevels; //levels + + ss_mesh = subsurf_make_derived_from_derived(calc->original, &smd, FALSE, NULL, 0, 0); + + if(ss_mesh) + { + vert = ss_mesh->getVertDataArray(ss_mesh, CD_MVERT); + if(vert) + { + //TRICKY: this code assumes subsurface will have the transformed original vertices + //in their original order at the end of the vert array. + vert = vert + + ss_mesh->getNumVerts(ss_mesh) + - calc->original->getNumVerts(calc->original); + } + } + + //To make sure we are not letting any memory behind + assert(smd.emCache == NULL); + assert(smd.mCache == NULL); + } + else + vert = calc->original->getVertDataArray(calc->original, CD_MVERT); + + //Not able to get vert information: jump to "free memory and return" part + if(vert == NULL) break; + } + else + { + //The code supports any axis that is a combination of X,Y,Z.. altought currently UI only allows to set the 3 diferent axis + if(calc->smd->projAxis & MOD_SHRINKWRAP_PROJECT_OVER_X_AXIS) proj_axis[0] = 1.0f; + if(calc->smd->projAxis & MOD_SHRINKWRAP_PROJECT_OVER_Y_AXIS) proj_axis[1] = 1.0f; + if(calc->smd->projAxis & MOD_SHRINKWRAP_PROJECT_OVER_Z_AXIS) proj_axis[2] = 1.0f; + + Normalize(proj_axis); + + //Invalid projection direction: jump to "free memory and return" part + if(INPR(proj_axis, proj_axis) < FLT_EPSILON) break; + } + + //If the user doesn't allows to project in any direction of projection axis... then theres nothing todo. + if((use_normal & (MOD_SHRINKWRAP_PROJECT_ALLOW_POS_DIR | MOD_SHRINKWRAP_PROJECT_ALLOW_NEG_DIR)) == 0) + break; //jump to "free memory and return" part + + + //Build target tree + BENCH(bvhtree_from_mesh_faces(&treeData, calc->target, calc->keepDist, 4, 6)); + if(treeData.tree == NULL) + break; //jump to "free memory and return" part + + + //Build auxiliar target + if(calc->smd->auxTarget) + { + space_transform_setup( &local2aux, calc->ob, calc->smd->auxTarget); + + aux_mesh = CDDM_copy( object_get_derived_final(calc->smd->auxTarget, CD_MASK_BAREMESH) ); //TODO currently we need a copy in case object_get_derived_final returns an emDM that does not defines getVertArray or getFace array + if(aux_mesh) + BENCH(bvhtree_from_mesh_faces(&auxData, aux_mesh, 0.0, 4, 6)); + else + printf("Auxiliar target finalDerived mesh is null\n"); + } + + + //Now, everything is ready to project the vertexs! +#pragma omp parallel for private(i,hit) schedule(static) + for(i = 0; i<calc->numVerts; ++i) + { + float *co = calc->vertexCos[i]; + float tmp_co[3], tmp_no[3]; + float lim = 10000.0f; //TODO: we should use FLT_MAX here, but sweepsphere code isnt prepared for that + float weight = vertexgroup_get_vertex_weight(calc->dvert, i, calc->vgroup); + + if(weight == 0.0f) continue; + + if(ss_mesh) + { + VECCOPY(tmp_co, vert[i].co); + } + else + { + VECCOPY(tmp_co, co); + } + + + if(vert) + NormalShortToFloat(tmp_no, vert[i].no); + else + VECCOPY( tmp_no, proj_axis ); + + + hit.index = -1; + hit.dist = lim; + + + //Project over positive direction of axis + if(use_normal & MOD_SHRINKWRAP_PROJECT_ALLOW_POS_DIR) + { + + if(auxData.tree) + normal_projection_project_vertex(0, tmp_co, tmp_no, &local2aux, auxData.tree, &hit, auxData.raycast_callback, &auxData); + + normal_projection_project_vertex(calc->smd->shrinkOpts, tmp_co, tmp_no, &calc->local2target, treeData.tree, &hit, treeData.raycast_callback, &treeData); + } + + //Project over negative direction of axis + if(use_normal & MOD_SHRINKWRAP_PROJECT_ALLOW_NEG_DIR) + { + float inv_no[3] = { -tmp_no[0], -tmp_no[1], -tmp_no[2] }; + + + if(auxData.tree) + normal_projection_project_vertex(0, tmp_co, inv_no, &local2aux, auxData.tree, &hit, auxData.raycast_callback, &auxData); + + normal_projection_project_vertex(calc->smd->shrinkOpts, tmp_co, inv_no, &calc->local2target, treeData.tree, &hit, treeData.raycast_callback, &treeData); + } + + + if(hit.index != -1) + { + VecLerpf(co, co, hit.co, weight); + } + } + + +//Simple do{} while(0) structure to allow to easily jump to the "free memory and return" part +} while(0); + + //free data structures + + free_bvhtree_from_mesh(&treeData); + free_bvhtree_from_mesh(&auxData); + + if(aux_mesh) + aux_mesh->release(aux_mesh); + + if(ss_mesh) + ss_mesh->release(ss_mesh); +} + +/* + * Shrinkwrap moving vertexs to the nearest surface point on the target + * + * it builds a BVHTree from the target mesh and then performs a + * NN matchs for each vertex + */ +void shrinkwrap_calc_nearest_surface_point(ShrinkwrapCalcData *calc) +{ + int i; + + BVHTreeFromMesh treeData = NULL_BVHTreeFromMesh; + BVHTreeNearest nearest = NULL_BVHTreeNearest; + + + + //Create a bvh-tree of the given target + BENCH(bvhtree_from_mesh_faces( &treeData, calc->target, 0.0, 2, 6)); + if(treeData.tree == NULL) + { + OUT_OF_MEMORY(); + return; + } + + //Setup nearest + nearest.index = -1; + nearest.dist = FLT_MAX; + + + //Find the nearest vertex +#pragma omp parallel for default(none) private(i) firstprivate(nearest) shared(calc,treeData) schedule(static) + for(i = 0; i<calc->numVerts; ++i) + { + float *co = calc->vertexCos[i]; + float tmp_co[3]; + float weight = vertexgroup_get_vertex_weight(calc->dvert, i, calc->vgroup); + if(weight == 0.0f) continue; + + //Convert the vertex to tree coordinates + VECCOPY(tmp_co, co); + space_transform_apply(&calc->local2target, tmp_co); + + //Use local proximity heuristics (to reduce the nearest search) + // + //If we already had an hit before.. we assume this vertex is going to have a close hit to that other vertex + //so we can initiate the "nearest.dist" with the expected value to that last hit. + //This will lead in prunning of the search tree. + if(nearest.index != -1) + nearest.dist = squared_dist(tmp_co, nearest.co); + else + nearest.dist = FLT_MAX; + + BLI_bvhtree_find_nearest(treeData.tree, tmp_co, &nearest, treeData.nearest_callback, &treeData); + + //Found the nearest vertex + if(nearest.index != -1) + { + if(calc->smd->shrinkOpts & MOD_SHRINKWRAP_KEEP_ABOVE_SURFACE) + { + //Make the vertex stay on the front side of the face + VECADDFAC(tmp_co, nearest.co, nearest.no, calc->keepDist); + } + else + { + //Adjusting the vertex weight, so that after interpolating it keeps a certain distance from the nearest position + float dist = sasqrt( nearest.dist ); + if(dist > FLT_EPSILON) + VecLerpf(tmp_co, tmp_co, nearest.co, (dist - calc->keepDist)/dist); //linear interpolation + else + VECCOPY( tmp_co, nearest.co ); + } + + //Convert the coordinates back to mesh coordinates + space_transform_invert(&calc->local2target, tmp_co); + VecLerpf(co, co, tmp_co, weight); //linear interpolation + } + } + + + free_bvhtree_from_mesh(&treeData); +} + diff --git a/source/blender/blenkernel/intern/simple_deform.c b/source/blender/blenkernel/intern/simple_deform.c new file mode 100644 index 00000000000..2978a6f7f01 --- /dev/null +++ b/source/blender/blenkernel/intern/simple_deform.c @@ -0,0 +1,256 @@ +/** + * deform_simple.c + * + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * The Original Code is Copyright (C) Blender Foundation. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): André Pinto + * + * ***** END GPL LICENSE BLOCK ***** + */ +#include "DNA_object_types.h" +#include "DNA_modifier_types.h" +#include "DNA_meshdata_types.h" + +#include "BKE_simple_deform.h" +#include "BKE_DerivedMesh.h" +#include "BKE_lattice.h" +#include "BKE_deform.h" +#include "BKE_utildefines.h" +#include "BLI_arithb.h" +#include "BKE_shrinkwrap.h" + +#include <string.h> +#include <math.h> + + +//Clamps/Limits the given coordinate to: limits[0] <= co[axis] <= limits[1] +//The ammount of clamp is saved on dcut +static void axis_limit(int axis, const float limits[2], float co[3], float dcut[3]) +{ + float val = co[axis]; + if(limits[0] > val) val = limits[0]; + if(limits[1] < val) val = limits[1]; + + dcut[axis] = co[axis] - val; + co[axis] = val; +} + +static void simpleDeform_taper(const float factor, const float dcut[3], float *co) +{ + float x = co[0], y = co[1], z = co[2]; + float scale = z*factor; + + co[0] = x + x*scale; + co[1] = y + y*scale; + co[2] = z; + + if(dcut) + { + co[0] += dcut[0]; + co[1] += dcut[1]; + co[2] += dcut[2]; + } +} + +static void simpleDeform_stretch(const float factor, const float dcut[3], float *co) +{ + float x = co[0], y = co[1], z = co[2]; + float scale; + + scale = (z*z*factor-factor + 1.0); + + co[0] = x*scale; + co[1] = y*scale; + co[2] = z*(1.0+factor); + + + if(dcut) + { + co[0] += dcut[0]; + co[1] += dcut[1]; + co[2] += dcut[2]; + } +} + +static void simpleDeform_twist(const float factor, const float *dcut, float *co) +{ + float x = co[0], y = co[1], z = co[2]; + float theta, sint, cost; + + theta = z*factor; + sint = sin(theta); + cost = cos(theta); + + co[0] = x*cost - y*sint; + co[1] = x*sint + y*cost; + co[2] = z; + + if(dcut) + { + co[0] += dcut[0]; + co[1] += dcut[1]; + co[2] += dcut[2]; + } +} + +static void simpleDeform_bend(const float factor, const float dcut[3], float *co) +{ + float x = co[0], y = co[1], z = co[2]; + float theta, sint, cost; + + theta = x*factor; + sint = sin(theta); + cost = cos(theta); + + if(fabs(factor) > 1e-7f) + { + co[0] = -(y-1.0f/factor)*sint; + co[1] = (y-1.0f/factor)*cost + 1.0f/factor; + co[2] = z; + } + + + if(dcut) + { + co[0] += cost*dcut[0]; + co[1] += sint*dcut[0]; + co[2] += dcut[2]; + } + +} + + +/* simple deform modifier */ +void SimpleDeformModifier_do(SimpleDeformModifierData *smd, struct Object *ob, struct DerivedMesh *dm, float (*vertexCos)[3], int numVerts) +{ + static const float lock_axis[2] = {0.0f, 0.0f}; + + int i; + int limit_axis = 0; + float smd_limit[2], smd_factor; + SpaceTransform *transf = NULL, tmp_transf; + void (*simpleDeform_callback)(const float factor, const float dcut[3], float *co) = NULL; //Mode callback + int vgroup = get_named_vertexgroup_num(ob, smd->vgroup_name); + MDeformVert *dvert = NULL; + + //Safe-check + if(smd->origin == ob) smd->origin = NULL; //No self references + + if(smd->limit[0] < 0.0) smd->limit[0] = 0.0f; + if(smd->limit[0] > 1.0) smd->limit[0] = 1.0f; + + smd->limit[0] = MIN2(smd->limit[0], smd->limit[1]); //Upper limit >= than lower limit + + //Calculate matrixs do convert between coordinate spaces + if(smd->origin) + { + transf = &tmp_transf; + + if(smd->originOpts & MOD_SIMPLEDEFORM_ORIGIN_LOCAL) + { + space_transform_from_matrixs(transf, ob->obmat, smd->origin->obmat); + } + else + { + Mat4CpyMat4(transf->local2target, smd->origin->obmat); + Mat4Invert(transf->target2local, transf->local2target); + } + } + + //Setup vars + limit_axis = (smd->mode == MOD_SIMPLEDEFORM_MODE_BEND) ? 0 : 2; //Bend limits on X.. all other modes limit on Z + + //Update limits if needed + { + float lower = FLT_MAX; + float upper = -FLT_MAX; + + for(i=0; i<numVerts; i++) + { + float tmp[3]; + VECCOPY(tmp, vertexCos[i]); + + if(transf) space_transform_apply(transf, tmp); + + lower = MIN2(lower, tmp[limit_axis]); + upper = MAX2(upper, tmp[limit_axis]); + } + + + //SMD values are normalized to the BV, calculate the absolut values + smd_limit[1] = lower + (upper-lower)*smd->limit[1]; + smd_limit[0] = lower + (upper-lower)*smd->limit[0]; + + smd_factor = smd->factor / MAX2(FLT_EPSILON, smd_limit[1]-smd_limit[0]); + } + + + if(dm) + { + dvert = dm->getVertDataArray(dm, CD_MDEFORMVERT); + } + else if(ob->type == OB_LATTICE) + { + dvert = lattice_get_deform_verts(ob); + } + + + + switch(smd->mode) + { + case MOD_SIMPLEDEFORM_MODE_TWIST: simpleDeform_callback = simpleDeform_twist; break; + case MOD_SIMPLEDEFORM_MODE_BEND: simpleDeform_callback = simpleDeform_bend; break; + case MOD_SIMPLEDEFORM_MODE_TAPER: simpleDeform_callback = simpleDeform_taper; break; + case MOD_SIMPLEDEFORM_MODE_STRETCH: simpleDeform_callback = simpleDeform_stretch; break; + default: + return; //No simpledeform mode? + } + + for(i=0; i<numVerts; i++) + { + float weight = vertexgroup_get_vertex_weight(dvert, i, vgroup); + + if(weight != 0.0f) + { + float co[3], dcut[3] = {0.0f, 0.0f, 0.0f}; + + if(transf) space_transform_apply(transf, vertexCos[i]); + + VECCOPY(co, vertexCos[i]); + + //Apply axis limits + if(smd->mode != MOD_SIMPLEDEFORM_MODE_BEND) //Bend mode shoulnt have any lock axis + { + if(smd->axis & MOD_SIMPLEDEFORM_LOCK_AXIS_X) axis_limit(0, lock_axis, co, dcut); + if(smd->axis & MOD_SIMPLEDEFORM_LOCK_AXIS_Y) axis_limit(1, lock_axis, co, dcut); + } + axis_limit(limit_axis, smd_limit, co, dcut); + + simpleDeform_callback(smd_factor, dcut, co); //Apply deform + VecLerpf(vertexCos[i], vertexCos[i], co, weight); //Use vertex weight has coef of linear interpolation + + if(transf) space_transform_invert(transf, vertexCos[i]); + } + } +} + + diff --git a/source/blender/blenkernel/intern/softbody.c b/source/blender/blenkernel/intern/softbody.c index ac56d876df7..1ae547ba7b8 100644 --- a/source/blender/blenkernel/intern/softbody.c +++ b/source/blender/blenkernel/intern/softbody.c @@ -69,6 +69,8 @@ variables on the UI for now #include "BLI_blenlib.h" #include "BLI_arithb.h" #include "BLI_ghash.h" +#include "BLI_threads.h" + #include "BKE_curve.h" #include "BKE_effect.h" #include "BKE_global.h" @@ -84,6 +86,7 @@ variables on the UI for now //XXX #include "BIF_editdeform.h" //XXX #include "BIF_graphics.h" #include "PIL_time.h" +// #include "ONL_opennl.h" remove linking to ONL for now /* callbacks for errors and interrupts and some goo */ static int (*SB_localInterruptCallBack)(void) = NULL; @@ -94,7 +97,7 @@ static int (*SB_localInterruptCallBack)(void) = NULL; typedef struct BodySpring { int v1, v2; - float len, strength, cf; + float len, strength, cf,load; float ext_force[3]; /* edges colliding and sailing */ short order; short flag; @@ -102,7 +105,7 @@ typedef struct BodySpring { typedef struct BodyFace { int v1, v2, v3 ,v4; - float ext_force[3]; /* edges colliding and sailing */ + float ext_force[3]; /* faces colliding */ short flag; } BodyFace; @@ -117,6 +120,25 @@ typedef struct SBScratch { float aabbmin[3],aabbmax[3]; }SBScratch; +typedef struct SB_thread_context{ + Object *ob; + float forcetime; + float timenow; + int ifirst; + int ilast; + ListBase *do_effector; + int do_deflector; + float fieldfactor; + float windfactor; + int nr; + int tot; +}SB_thread_context; + +#define NLF_BUILD 1 +#define NLF_SOLVE 2 + +#define MID_PRESERVE 1 + #define SOFTGOALSNAP 0.999f /* if bp-> goal is above make it a *forced follow original* and skip all ODE stuff for this bp removes *unnecessary* stiffnes from ODE system @@ -126,7 +148,9 @@ typedef struct SBScratch { #define BSF_INTERSECT 1 /* edge intersects collider face */ #define SBF_DOFUZZY 1 /* edge intersects collider face */ -#define BFF_INTERSECT 1 /* edge intersects collider face */ + +#define BFF_INTERSECT 1 /* collider edge intrudes face */ +#define BFF_CLOSEVERT 2 /* collider vertex repulses face */ float SoftHeunTol = 1.0f; /* humm .. this should be calculated from sb parameters and sizes */ @@ -212,7 +236,7 @@ typedef struct ccd_Mesh { -ccd_Mesh *ccd_mesh_make(Object *ob, DerivedMesh *dm) +static ccd_Mesh *ccd_mesh_make(Object *ob, DerivedMesh *dm) { ccd_Mesh *pccd_M = NULL; ccdf_minmax *mima =NULL; @@ -309,7 +333,7 @@ ccd_Mesh *ccd_mesh_make(Object *ob, DerivedMesh *dm) } return pccd_M; } -void ccd_mesh_update(Object *ob,ccd_Mesh *pccd_M, DerivedMesh *dm) +static void ccd_mesh_update(Object *ob,ccd_Mesh *pccd_M, DerivedMesh *dm) { ccdf_minmax *mima =NULL; MFace *mface=NULL; @@ -448,7 +472,7 @@ void ccd_mesh_update(Object *ob,ccd_Mesh *pccd_M, DerivedMesh *dm) return ; } -void ccd_mesh_free(ccd_Mesh *ccdm) +static void ccd_mesh_free(ccd_Mesh *ccdm) { if(ccdm && (ccdm->savety == CCD_SAVETY )){ /*make sure we're not nuking objects we don't know*/ MEM_freeN(ccdm->mface); @@ -460,7 +484,7 @@ void ccd_mesh_free(ccd_Mesh *ccdm) } } -void ccd_build_deflector_hache(Object *vertexowner,GHash *hash) +static void ccd_build_deflector_hache(Object *vertexowner,GHash *hash) { Base *base; Object *ob; @@ -512,7 +536,7 @@ void ccd_build_deflector_hache(Object *vertexowner,GHash *hash) } /* while (base) */ } -void ccd_update_deflector_hache(Object *vertexowner,GHash *hash) +static void ccd_update_deflector_hache(Object *vertexowner,GHash *hash) { Base *base; Object *ob; @@ -582,6 +606,7 @@ static void add_mesh_quad_diag_springs(Object *ob) if (ob->soft){ int nofquads; + float s_shear = ob->soft->shearstiff*ob->soft->shearstiff; nofquads = count_mesh_quads(me); if (nofquads) { @@ -602,12 +627,12 @@ static void add_mesh_quad_diag_springs(Object *ob) if(mface->v4) { bs->v1= mface->v1; bs->v2= mface->v3; - bs->strength= 1.0; + bs->strength= s_shear; bs->order =2; bs++; bs->v1= mface->v2; bs->v2= mface->v4; - bs->strength= 1.0; + bs->strength= s_shear; bs->order =2; bs++; @@ -689,6 +714,7 @@ static void add_2nd_order_springs(Object *ob,float stiffness) { int counter = 0; BodySpring *bs_new; + stiffness *=stiffness; add_2nd_order_roller(ob,stiffness,&counter,0); /* counting */ if (counter) { @@ -803,50 +829,6 @@ static void calculate_collision_balls(Object *ob) } -char set_octant_flags(float *ce, float *pos, float ball) -{ - float x,y,z; - char res = 0; - int a; - - for (a=0;a<7;a++){ - switch(a){ - case 0: x=pos[0]; y=pos[1]; z=pos[2]; break; - case 1: x=pos[0]+ball; y=pos[1]; z=pos[2]; break; - case 2: x=pos[0]-ball; y=pos[1]; z=pos[2]; break; - case 3: x=pos[0]; y=pos[1]+ball; z=pos[2]; break; - case 4: x=pos[0]; y=pos[1]-ball; z=pos[2]; break; - case 5: x=pos[0]; y=pos[1]; z=pos[2]+ball; break; - case 6: x=pos[0]; y=pos[1]; z=pos[2]-ball; break; - } - - x=pos[0]; y=pos[1]; z=pos[2]; - - if (x > ce[0]){ - if (y > ce[1]){ - if (z > ce[2]) res|= 1; - else res|= 2; - } - else{ - if (z > ce[2]) res|= 4; - else res|= 8; - } - } - - else{ - if (y > ce[1]){ - if (z > ce[2]) res|= 16; - else res|= 32; - } - else{ - if (z > ce[2]) res|= 64; - else res|= 128; - } - } - } - return res; -} - /* creates new softbody if didn't exist yet, makes new points and springs arrays */ static void renew_softbody(Object *ob, int totpoint, int totspring) { @@ -881,6 +863,8 @@ static void renew_softbody(Object *ob, int totpoint, int totspring) bp->nofsprings= 0; bp->springs= NULL; bp->choke = 0.0f; + bp->choke2 = 0.0f; + bp->frozen = 1.0f; bp->colball = 0.0f; bp->flag = 0; @@ -985,6 +969,7 @@ static void Vec3PlusStVec(float *v, float s, float *v1) } /* +++ dependancy information functions*/ + static int are_there_deflectors(unsigned int layer) { Base *base; @@ -1002,40 +987,11 @@ static int query_external_colliders(Object *me) { return(are_there_deflectors(me->lay)); } - -#if 0 -static int query_external_forces(Object *me) -{ -/* silly but true: we need to create effector cache to see if anything is in it */ - ListBase *ec = pdInitEffectors(me,NULL); - int result = 0; - if (ec){ - result = 1; - pdEndEffectors(ec); /* sorry ec, yes i'm an idiot, but i needed to know if you were there */ - } - return result; -} - -/* -any of that external objects may have an IPO or something alike .. -so unless we can ask them if they are moving we have to assume they do -*/ -static int query_external_time(Object *me) -{ - if (query_external_colliders(me)) return 1; - if (query_external_forces(me)) return 1; - return 0; -} -static int query_internal_time(Object *me) -{ - if (me->softflag & OB_SB_GOAL) return 1; - return 0; -} -#endif /* --- dependancy information functions*/ + /* +++ the aabb "force" section*/ -int sb_detect_aabb_collisionCached( float force[3], unsigned int par_layer,struct Object *vertexowner,float time) +static int sb_detect_aabb_collisionCached( float force[3], unsigned int par_layer,struct Object *vertexowner,float time) { Object *ob; SoftBody *sb=vertexowner->soft; @@ -1099,15 +1055,112 @@ int sb_detect_aabb_collisionCached( float force[3], unsigned int par_layer,struc /* +++ the face external section*/ +static int sb_detect_face_pointCached(float face_v1[3],float face_v2[3],float face_v3[3],float *damp, + float force[3], unsigned int par_layer,struct Object *vertexowner,float time) + { + Object *ob; + GHash *hash; + GHashIterator *ihash; + float nv1[3], edge1[3], edge2[3], d_nvect[3], aabbmin[3],aabbmax[3]; + float facedist,outerfacethickness,tune = 10.f; + int a, deflected=0; + + aabbmin[0] = MIN3(face_v1[0],face_v2[0],face_v3[0]); + aabbmin[1] = MIN3(face_v1[1],face_v2[1],face_v3[1]); + aabbmin[2] = MIN3(face_v1[2],face_v2[2],face_v3[2]); + aabbmax[0] = MAX3(face_v1[0],face_v2[0],face_v3[0]); + aabbmax[1] = MAX3(face_v1[1],face_v2[1],face_v3[1]); + aabbmax[2] = MAX3(face_v1[2],face_v2[2],face_v3[2]); + + /* calculate face normal once again SIGH */ + VECSUB(edge1, face_v1, face_v2); + VECSUB(edge2, face_v3, face_v2); + Crossf(d_nvect, edge2, edge1); + Normalize(d_nvect); + -int sb_detect_face_collisionCached(float face_v1[3],float face_v2[3],float face_v3[3],float *damp, + hash = vertexowner->soft->scratch->colliderhash; + ihash = BLI_ghashIterator_new(hash); + while (!BLI_ghashIterator_isDone(ihash) ) { + + ccd_Mesh *ccdm = BLI_ghashIterator_getValue (ihash); + ob = BLI_ghashIterator_getKey (ihash); + /* only with deflecting set */ + if(ob->pd && ob->pd->deflect) { + MVert *mvert= NULL; + MVert *mprevvert= NULL; + if(ccdm){ + mvert= ccdm->mvert; + a = ccdm->totvert; + mprevvert= ccdm->mprevvert; + outerfacethickness =ob->pd->pdef_sboft; + if ((aabbmax[0] < ccdm->bbmin[0]) || + (aabbmax[1] < ccdm->bbmin[1]) || + (aabbmax[2] < ccdm->bbmin[2]) || + (aabbmin[0] > ccdm->bbmax[0]) || + (aabbmin[1] > ccdm->bbmax[1]) || + (aabbmin[2] > ccdm->bbmax[2]) ) { + /* boxes dont intersect */ + BLI_ghashIterator_step(ihash); + continue; + } + + } + else{ + /*aye that should be cached*/ + printf("missing cache error \n"); + BLI_ghashIterator_step(ihash); + continue; + } + + + /* use mesh*/ + if (mvert) { + while(a){ + VECCOPY(nv1,mvert[a-1].co); + if(mprevvert){ + VecMulf(nv1,time); + Vec3PlusStVec(nv1,(1.0f-time),mprevvert[a-1].co); + } + /* origin to face_v2*/ + VECSUB(nv1, nv1, face_v2); + facedist = Inpf(nv1,d_nvect); + if (ABS(facedist)<outerfacethickness){ + if (point_in_tri_prism(nv1, face_v1,face_v2,face_v3) ){ + float df; + if (facedist > 0){ + df = (outerfacethickness-facedist)/outerfacethickness; + } + else { + df = (outerfacethickness+facedist)/outerfacethickness; + } + + *damp=df*tune*ob->pd->pdef_sbdamp; + + df = 0.01f*exp(- 100.0f*df); + Vec3PlusStVec(force,-df,d_nvect); + deflected = 3; + } + } + a--; + }/* while(a)*/ + } /* if (mvert) */ + } /* if(ob->pd && ob->pd->deflect) */ + BLI_ghashIterator_step(ihash); + } /* while () */ + BLI_ghashIterator_free(ihash); + return deflected; +} + + +static int sb_detect_face_collisionCached(float face_v1[3],float face_v2[3],float face_v3[3],float *damp, float force[3], unsigned int par_layer,struct Object *vertexowner,float time) { Object *ob; GHash *hash; GHashIterator *ihash; float nv1[3], nv2[3], nv3[3], nv4[3], edge1[3], edge2[3], d_nvect[3], aabbmin[3],aabbmax[3]; - float t; + float t,tune = 10.0f; int a, deflected=0; aabbmin[0] = MIN3(face_v1[0],face_v2[0],face_v3[0]); @@ -1206,8 +1259,8 @@ int sb_detect_face_collisionCached(float face_v1[3],float face_v2[3],float face_ LineIntersectsTriangle(nv1, nv2, face_v1, face_v2, face_v3, &t, NULL) || LineIntersectsTriangle(nv2, nv3, face_v1, face_v2, face_v3, &t, NULL) || LineIntersectsTriangle(nv3, nv1, face_v1, face_v2, face_v3, &t, NULL) ){ - Vec3PlusStVec(force,-1.0f,d_nvect); - *damp=ob->pd->pdef_sbdamp; + Vec3PlusStVec(force,-0.5f,d_nvect); + *damp=tune*ob->pd->pdef_sbdamp; deflected = 2; } if (mface->v4){ /* quad */ @@ -1217,11 +1270,12 @@ int sb_detect_face_collisionCached(float face_v1[3],float face_v2[3],float face_ Crossf(d_nvect, edge2, edge1); Normalize(d_nvect); if ( - LineIntersectsTriangle(nv1, nv3, face_v1, face_v2, face_v3, &t, NULL) || + /* LineIntersectsTriangle(nv1, nv3, face_v1, face_v2, face_v3, &t, NULL) || + we did that edge allready */ LineIntersectsTriangle(nv3, nv4, face_v1, face_v2, face_v3, &t, NULL) || LineIntersectsTriangle(nv4, nv1, face_v1, face_v2, face_v3, &t, NULL) ){ - Vec3PlusStVec(force,-1.0f,d_nvect); - *damp=ob->pd->pdef_sbdamp; + Vec3PlusStVec(force,-0.5f,d_nvect); + *damp=tune*ob->pd->pdef_sbdamp; deflected = 2; } } @@ -1237,12 +1291,12 @@ int sb_detect_face_collisionCached(float face_v1[3],float face_v2[3],float face_ -void scan_for_ext_face_forces(Object *ob,float timenow) +static void scan_for_ext_face_forces(Object *ob,float timenow) { SoftBody *sb = ob->soft; BodyFace *bf; int a; - float damp; + float damp=0.0f,choke=1.0f; float tune = -10.0f; float feedback[3]; @@ -1252,43 +1306,71 @@ void scan_for_ext_face_forces(Object *ob,float timenow) bf = sb->scratch->bodyface; for(a=0; a<sb->scratch->totface; a++, bf++) { bf->ext_force[0]=bf->ext_force[1]=bf->ext_force[2]=0.0f; +/*+++edges intruding*/ + bf->flag &= ~BFF_INTERSECT; feedback[0]=feedback[1]=feedback[2]=0.0f; - bf->flag &= ~BFF_INTERSECT; - if (sb_detect_face_collisionCached(sb->bpoint[bf->v1].pos,sb->bpoint[bf->v2].pos, sb->bpoint[bf->v3].pos, &damp, feedback, ob->lay ,ob , timenow)){ - Vec3PlusStVec(bf->ext_force,tune,feedback); + Vec3PlusStVec(sb->bpoint[bf->v1].force,tune,feedback); + Vec3PlusStVec(sb->bpoint[bf->v2].force,tune,feedback); + Vec3PlusStVec(sb->bpoint[bf->v3].force,tune,feedback); +// Vec3PlusStVec(bf->ext_force,tune,feedback); bf->flag |= BFF_INTERSECT; + choke = MIN2(MAX2(damp,choke),1.0f); } feedback[0]=feedback[1]=feedback[2]=0.0f; if ((bf->v4) && (sb_detect_face_collisionCached(sb->bpoint[bf->v1].pos,sb->bpoint[bf->v3].pos, sb->bpoint[bf->v4].pos, &damp, feedback, ob->lay ,ob , timenow))){ - Vec3PlusStVec(bf->ext_force,tune,feedback); - bf->flag |= BFF_INTERSECT; + Vec3PlusStVec(sb->bpoint[bf->v1].force,tune,feedback); + Vec3PlusStVec(sb->bpoint[bf->v3].force,tune,feedback); + Vec3PlusStVec(sb->bpoint[bf->v4].force,tune,feedback); +// Vec3PlusStVec(bf->ext_force,tune,feedback); + bf->flag |= BFF_INTERSECT; + choke = MIN2(MAX2(damp,choke),1.0f); } - } +/*---edges intruding*/ + +/*+++ close vertices*/ + if (( bf->flag & BFF_INTERSECT)==0){ + bf->flag &= ~BFF_CLOSEVERT; + tune = -1.0f; + feedback[0]=feedback[1]=feedback[2]=0.0f; + if (sb_detect_face_pointCached(sb->bpoint[bf->v1].pos,sb->bpoint[bf->v2].pos, sb->bpoint[bf->v3].pos, + &damp, feedback, ob->lay ,ob , timenow)){ + Vec3PlusStVec(sb->bpoint[bf->v1].force,tune,feedback); + Vec3PlusStVec(sb->bpoint[bf->v2].force,tune,feedback); + Vec3PlusStVec(sb->bpoint[bf->v3].force,tune,feedback); +// Vec3PlusStVec(bf->ext_force,tune,feedback); + bf->flag |= BFF_CLOSEVERT; + choke = MIN2(MAX2(damp,choke),1.0f); + } - - + feedback[0]=feedback[1]=feedback[2]=0.0f; + if ((bf->v4) && (sb_detect_face_pointCached(sb->bpoint[bf->v1].pos,sb->bpoint[bf->v3].pos, sb->bpoint[bf->v4].pos, + &damp, feedback, ob->lay ,ob , timenow))){ + Vec3PlusStVec(sb->bpoint[bf->v1].force,tune,feedback); + Vec3PlusStVec(sb->bpoint[bf->v3].force,tune,feedback); + Vec3PlusStVec(sb->bpoint[bf->v4].force,tune,feedback); +// Vec3PlusStVec(bf->ext_force,tune,feedback); + bf->flag |= BFF_CLOSEVERT; + choke = MIN2(MAX2(damp,choke),1.0f); + } + } +/*--- close vertices*/ + } bf = sb->scratch->bodyface; for(a=0; a<sb->scratch->totface; a++, bf++) { - if ( bf->flag & BFF_INTERSECT) + if (( bf->flag & BFF_INTERSECT) || ( bf->flag & BFF_CLOSEVERT)) { - VECADD(sb->bpoint[bf->v1].force,sb->bpoint[bf->v1].force,bf->ext_force); - VECADD(sb->bpoint[bf->v2].force,sb->bpoint[bf->v2].force,bf->ext_force); - VECADD(sb->bpoint[bf->v3].force,sb->bpoint[bf->v3].force,bf->ext_force); + sb->bpoint[bf->v1].choke2=MAX2(sb->bpoint[bf->v1].choke2,choke); + sb->bpoint[bf->v2].choke2=MAX2(sb->bpoint[bf->v2].choke2,choke); + sb->bpoint[bf->v3].choke2=MAX2(sb->bpoint[bf->v3].choke2,choke); if (bf->v4){ - VECADD(sb->bpoint[bf->v4].force,sb->bpoint[bf->v4].force,bf->ext_force); + sb->bpoint[bf->v2].choke2=MAX2(sb->bpoint[bf->v2].choke2,choke); } - - } - + } } - - - - } } @@ -1297,7 +1379,7 @@ void scan_for_ext_face_forces(Object *ob,float timenow) /* +++ the spring external section*/ -int sb_detect_edge_collisionCached(float edge_v1[3],float edge_v2[3],float *damp, +static int sb_detect_edge_collisionCached(float edge_v1[3],float edge_v2[3],float *damp, float force[3], unsigned int par_layer,struct Object *vertexowner,float time) { Object *ob; @@ -1447,17 +1529,16 @@ int sb_detect_edge_collisionCached(float edge_v1[3],float edge_v2[3],float *damp } -void scan_for_ext_spring_forces(Object *ob,float timenow) + +static void _scan_for_ext_spring_forces(Object *ob,float timenow,int ifirst,int ilast, struct ListBase *do_effector) { SoftBody *sb = ob->soft; - ListBase *do_effector; int a; float damp; float feedback[3]; - do_effector= pdInitEffectors(ob,NULL); if (sb && sb->totspring){ - for(a=0; a<sb->totspring; a++) { + for(a=ifirst; a<ilast; a++) { BodySpring *bs = &sb->bspring[a]; bs->ext_force[0]=bs->ext_force[1]=bs->ext_force[2]=0.0f; feedback[0]=feedback[1]=feedback[2]=0.0f; @@ -1498,25 +1579,110 @@ void scan_for_ext_spring_forces(Object *ob,float timenow) } f = Normalize(vel); f = -0.0001f*f*f*sb->aeroedge; - /* todo add a nice angle dependant function */ - /* look up one at bergman scheafer */ + /* (todo) add a nice angle dependant function done for now BUT */ + /* still there could be some nice drag/lift function, but who needs it */ VECSUB(sp, sb->bpoint[bs->v1].pos , sb->bpoint[bs->v2].pos); Projf(pr,vel,sp); VECSUB(vel,vel,pr); Normalize(vel); - Vec3PlusStVec(bs->ext_force,f,vel); + if (ob->softflag & OB_SB_AERO_ANGLE){ + Normalize(sp); + Vec3PlusStVec(bs->ext_force,f*(1.0f-ABS(Inpf(vel,sp))),vel); + } + else{ + Vec3PlusStVec(bs->ext_force,f,vel); // to keep compatible with 2.45 release files + } } /* --- springs seeing wind */ } } } - if(do_effector) - pdEndEffectors(do_effector); } + + +static void scan_for_ext_spring_forces(Object *ob,float timenow) +{ + SoftBody *sb = ob->soft; + ListBase *do_effector= NULL; + do_effector= pdInitEffectors(ob,NULL); + if (sb){ + _scan_for_ext_spring_forces(ob,timenow,0,sb->totspring,do_effector); + } + if(do_effector) + pdEndEffectors(do_effector); +} + +static void *exec_scan_for_ext_spring_forces(void *data) +{ + SB_thread_context *pctx = (SB_thread_context*)data; + _scan_for_ext_spring_forces(pctx->ob,pctx->timenow,pctx->ifirst,pctx->ilast,pctx->do_effector); + return 0; +} + +static void sb_sfesf_threads_run(struct Object *ob, float timenow,int totsprings,int *ptr_to_break_func()) +{ + ListBase *do_effector = NULL; + ListBase threads; + SB_thread_context *sb_threads; + int i, totthread,left,dec; + int lowsprings =100; /* wild guess .. may increase with better thread management 'above' or even be UI option sb->spawn_cf_threads_nopts */ + + do_effector= pdInitEffectors(ob,NULL); + + /* figure the number of threads while preventing pretty pointless threading overhead */ + if(G.scene->r.mode & R_FIXED_THREADS) + totthread= G.scene->r.threads; + else + totthread= BLI_system_thread_count(); + /* what if we got zillions of CPUs running but less to spread*/ + while ((totsprings/totthread < lowsprings) && (totthread > 1)) { + totthread--; + } + + sb_threads= MEM_callocN(sizeof(SB_thread_context)*totthread, "SBSpringsThread"); + memset(sb_threads, 0, sizeof(SB_thread_context)*totthread); + left = totsprings; + dec = totsprings/totthread +1; + for(i=0; i<totthread; i++) { + sb_threads[i].ob = ob; + sb_threads[i].forcetime = 0.0; // not used here + sb_threads[i].timenow = timenow; + sb_threads[i].ilast = left; + left = left - dec; + if (left >0){ + sb_threads[i].ifirst = left; + } + else + sb_threads[i].ifirst = 0; + sb_threads[i].do_effector = do_effector; + sb_threads[i].do_deflector = 0;// not used here + sb_threads[i].fieldfactor = 0.0f;// not used here + sb_threads[i].windfactor = 0.0f;// not used here + sb_threads[i].nr= i; + sb_threads[i].tot= totthread; + } + if(totthread > 1) { + BLI_init_threads(&threads, exec_scan_for_ext_spring_forces, totthread); + + for(i=0; i<totthread; i++) + BLI_insert_thread(&threads, &sb_threads[i]); + + BLI_end_threads(&threads); + } + else + exec_scan_for_ext_spring_forces(&sb_threads[0]); + /* clean up */ + MEM_freeN(sb_threads); + + if(do_effector) + pdEndEffectors(do_effector); +} + + /* --- the spring external section*/ -int choose_winner(float*w, float* pos,float*a,float*b,float*c,float*ca,float*cb,float*cc) +static int choose_winner(float*w, float* pos,float*a,float*b,float*c,float*ca,float*cb,float*cc) { float mindist,cp; int winner =1; @@ -1543,7 +1709,7 @@ int choose_winner(float*w, float* pos,float*a,float*b,float*c,float*ca,float*cb, -int sb_detect_vertex_collisionCached(float opco[3], float facenormal[3], float *damp, +static int sb_detect_vertex_collisionCached(float opco[3], float facenormal[3], float *damp, float force[3], unsigned int par_layer,struct Object *vertexowner, float time,float vel[3], float *intrusion) { @@ -1833,253 +1999,6 @@ int sb_detect_vertex_collisionCached(float opco[3], float facenormal[3], float * return deflected; } -/* not complete yet .. - try to find a pos resolving all inside collisions -*/ -#if 0 //mute it for now -int sb_detect_vertex_collisionCachedEx(float opco[3], float facenormal[3], float *damp, - float force[3], unsigned int par_layer,struct Object *vertexowner, - float time,float vel[3], float *intrusion) -{ - Object *ob; - GHash *hash; - GHashIterator *ihash; - float nv1[3], nv2[3], nv3[3], nv4[3], edge1[3], edge2[3],d_nvect[3], dv1[3],ve[3],avel[3], - vv1[3], vv2[3], vv3[3], vv4[3], - facedist,n_mag,force_mag_norm,minx,miny,minz,maxx,maxy,maxz, - innerfacethickness,outerfacethickness, - closestinside, - ee = 5.0f, ff = 0.1f, fa; - int a, deflected=0, cavel=0; -/* init */ - *intrusion = 0.0f; - hash = vertexowner->soft->scratch->colliderhash; - ihash = BLI_ghashIterator_new(hash); -/* go */ - while (!BLI_ghashIterator_isDone(ihash) ) { - - ccd_Mesh *ccdm = BLI_ghashIterator_getValue (ihash); - ob = BLI_ghashIterator_getKey (ihash); - /* only with deflecting set */ - if(ob->pd && ob->pd->deflect) { - MFace *mface= NULL; - MVert *mvert= NULL; - MVert *mprevvert= NULL; - ccdf_minmax *mima= NULL; - - if(ccdm){ - mface= ccdm->mface; - mvert= ccdm->mvert; - mprevvert= ccdm->mprevvert; - mima= ccdm->mima; - a = ccdm->totface; - - minx =ccdm->bbmin[0]; - miny =ccdm->bbmin[1]; - minz =ccdm->bbmin[2]; - - maxx =ccdm->bbmax[0]; - maxy =ccdm->bbmax[1]; - maxz =ccdm->bbmax[2]; - - if ((opco[0] < minx) || - (opco[1] < miny) || - (opco[2] < minz) || - (opco[0] > maxx) || - (opco[1] > maxy) || - (opco[2] > maxz) ) { - /* outside the padded boundbox --> collision object is too far away */ - BLI_ghashIterator_step(ihash); - continue; - } - } - else{ - /*aye that should be cached*/ - printf("missing cache error \n"); - BLI_ghashIterator_step(ihash); - continue; - } - - /* do object level stuff */ - /* need to have user control for that since it depends on model scale */ - innerfacethickness =-ob->pd->pdef_sbift; - outerfacethickness =ob->pd->pdef_sboft; - closestinside = innerfacethickness; - fa = (ff*outerfacethickness-outerfacethickness); - fa *= fa; - fa = 1.0f/fa; - avel[0]=avel[1]=avel[2]=0.0f; - /* use mesh*/ - while (a--) { - if ( - (opco[0] < mima->minx) || - (opco[0] > mima->maxx) || - (opco[1] < mima->miny) || - (opco[1] > mima->maxy) || - (opco[2] < mima->minz) || - (opco[2] > mima->maxz) - ) { - mface++; - mima++; - continue; - } - - if (mvert){ - - VECCOPY(nv1,mvert[mface->v1].co); - VECCOPY(nv2,mvert[mface->v2].co); - VECCOPY(nv3,mvert[mface->v3].co); - if (mface->v4){ - VECCOPY(nv4,mvert[mface->v4].co); - } - - if (mprevvert){ - /* grab the average speed of the collider vertices - before we spoil nvX - humm could be done once a SB steps but then we' need to store that too - since the AABB reduced propabitlty to get here drasticallly - it might be a nice tradeof CPU <--> memory - */ - VECSUB(vv1,nv1,mprevvert[mface->v1].co); - VECSUB(vv2,nv2,mprevvert[mface->v2].co); - VECSUB(vv3,nv3,mprevvert[mface->v3].co); - if (mface->v4){ - VECSUB(vv4,nv4,mprevvert[mface->v4].co); - } - - VecMulf(nv1,time); - Vec3PlusStVec(nv1,(1.0f-time),mprevvert[mface->v1].co); - - VecMulf(nv2,time); - Vec3PlusStVec(nv2,(1.0f-time),mprevvert[mface->v2].co); - - VecMulf(nv3,time); - Vec3PlusStVec(nv3,(1.0f-time),mprevvert[mface->v3].co); - - if (mface->v4){ - VecMulf(nv4,time); - Vec3PlusStVec(nv4,(1.0f-time),mprevvert[mface->v4].co); - } - } - } - - /* switch origin to be nv2*/ - VECSUB(edge1, nv1, nv2); - VECSUB(edge2, nv3, nv2); - VECSUB(dv1,opco,nv2); /* abuse dv1 to have vertex in question at *origin* of triangle */ - - Crossf(d_nvect, edge2, edge1); - n_mag = Normalize(d_nvect); - facedist = Inpf(dv1,d_nvect); - - if ((facedist > closestinside) && (facedist < outerfacethickness)){ -// if ((facedist > innerfacethickness) && (facedist < outerfacethickness)){ - if (point_in_tri_prism(opco, nv1, nv2, nv3) ){ - force_mag_norm =(float)exp(-ee*facedist); - if (facedist > outerfacethickness*ff) - force_mag_norm =(float)force_mag_norm*fa*(facedist - outerfacethickness)*(facedist - outerfacethickness); - *damp=ob->pd->pdef_sbdamp; - - if (facedist > 0.0f){ - *damp *= (1.0f - facedist/outerfacethickness); - Vec3PlusStVec(force,force_mag_norm,d_nvect); - if (deflected < 2){ - deflected = 1; - if ((mprevvert) && (*damp > 0.0f)){ - choose_winner(ve,opco,nv1,nv2,nv3,vv1,vv2,vv3); - VECADD(avel,avel,ve); - cavel ++; - } - } - - } - else{ - Vec3PlusStVec(force,force_mag_norm,d_nvect); - VECCOPY(facenormal,d_nvect); - if ((mprevvert) && (*damp > 0.0f)){ - choose_winner(avel,opco,nv1,nv2,nv3,vv1,vv2,vv3); - cavel = 1; - deflected = 2; - closestinside = facedist; - } - } - *intrusion = facedist; - } - } - if (mface->v4){ /* quad */ - /* switch origin to be nv4 */ - VECSUB(edge1, nv3, nv4); - VECSUB(edge2, nv1, nv4); - VECSUB(dv1,opco,nv4); /* abuse dv1 to have vertex in question at *origin* of triangle */ - - Crossf(d_nvect, edge2, edge1); - n_mag = Normalize(d_nvect); - facedist = Inpf(dv1,d_nvect); - - if ((facedist > innerfacethickness) && (facedist < outerfacethickness)){ - if (point_in_tri_prism(opco, nv1, nv3, nv4) ){ - force_mag_norm =(float)exp(-ee*facedist); - if (facedist > outerfacethickness*ff) - force_mag_norm =(float)force_mag_norm*fa*(facedist - outerfacethickness)*(facedist - outerfacethickness); - Vec3PlusStVec(force,force_mag_norm,d_nvect); - *damp=ob->pd->pdef_sbdamp; - - if (facedist > 0.0f){ - *damp *= (1.0f - facedist/outerfacethickness); - Vec3PlusStVec(force,force_mag_norm,d_nvect); - if (deflected < 2){ - deflected = 1; - if ((mprevvert) && (*damp > 0.0f)){ - choose_winner(ve,opco,nv1,nv3,nv4,vv1,vv3,vv4); - VECADD(avel,avel,ve); - cavel ++; - } - } - - } - else{ - Vec3PlusStVec(force,force_mag_norm,d_nvect); - VECCOPY(facenormal,d_nvect); - if ((mprevvert) && (*damp > 0.0f)){ - choose_winner(avel,opco,nv1,nv3,nv4,vv1,vv3,vv4); - cavel = 1; - deflected = 2; - closestinside = facedist; - } - } - - - - *intrusion = facedist; - } - - } - } - mface++; - mima++; - }/* while a */ - } /* if(ob->pd && ob->pd->deflect) */ - BLI_ghashIterator_step(ihash); - } /* while () */ - BLI_ghashIterator_free(ihash); - if (cavel) VecMulf(avel,1.0f/(float)cavel); - VECCOPY(vel,avel); - - /* we did stay "outside" but have some close to contact forces - just to be complete fake a face normal - */ - if (deflected ==1){ - VECCOPY(facenormal,force); - Normalize(facenormal); - } - else{ - facenormal[0] = facenormal[1] = facenormal[2] = 0.0f; - } - return deflected; -} -#endif - - /* sandbox to plug in various deflection algos */ static int sb_deflect_face(Object *ob,float *actpos,float *facenormal,float *force,float *cf,float time,float *vel,float *intrusion) @@ -2092,102 +2011,191 @@ static int sb_deflect_face(Object *ob,float *actpos,float *facenormal,float *for return(deflected); } +/* hiding this for now .. but the jacobian may pop up on other tasks .. so i'd like to keep it +static void dfdx_spring(int ia, int ic, int op, float dir[3],float L,float len,float factor) +{ + float m,delta_ij; + int i ,j; + if (L < len){ + for(i=0;i<3;i++) + for(j=0;j<3;j++){ + delta_ij = (i==j ? (1.0f): (0.0f)); + m=factor*(dir[i]*dir[j] + (1-L/len)*(delta_ij - dir[i]*dir[j])); + nlMatrixAdd(ia+i,op+ic+j,m); + } + } + else{ + for(i=0;i<3;i++) + for(j=0;j<3;j++){ + m=factor*dir[i]*dir[j]; + nlMatrixAdd(ia+i,op+ic+j,m); + } + } +} + + +static void dfdx_goal(int ia, int ic, int op, float factor) +{ + int i; + for(i=0;i<3;i++) nlMatrixAdd(ia+i,op+ic+i,factor); +} -static void softbody_calc_forces(Object *ob, float forcetime, float timenow) +static void dfdv_goal(int ia, int ic,float factor) +{ + int i; + for(i=0;i<3;i++) nlMatrixAdd(ia+i,ic+i,factor); +} +*/ +static void sb_spring_force(Object *ob,int bpi,BodySpring *bs,float iks,float forcetime,int nl_flags) { -/* rule we never alter free variables :bp->vec bp->pos in here ! - * this will ruin adaptive stepsize AKA heun! (BM) - */ + SoftBody *sb= ob->soft; /* is supposed to be there */ + BodyPoint *bp1,*bp2; + + float dir[3],dvel[3]; + float distance,forcefactor,kd,absvel,projvel; + int ia,ic; + /* prepare depending on which side of the spring we are on */ + if (bpi == bs->v1){ + bp1 = &sb->bpoint[bs->v1]; + bp2 = &sb->bpoint[bs->v2]; + ia =3*bs->v1; + ic =3*bs->v2; + } + else if (bpi == bs->v2){ + bp1 = &sb->bpoint[bs->v2]; + bp2 = &sb->bpoint[bs->v1]; + ia =3*bs->v2; + ic =3*bs->v1; + } + else{ + /* TODO make this debug option */ + /**/ + printf("bodypoint <bpi> is not attached to spring <*bs> --> sb_spring_force()\n"); + return; + } + + /* do bp1 <--> bp2 elastic */ + VecSubf(dir,bp1->pos,bp2->pos); + distance = Normalize(dir); + if (bs->len < distance) + iks = 1.0f/(1.0f-sb->inspring)-1.0f ;/* inner spring constants function */ + else + iks = 1.0f/(1.0f-sb->inpush)-1.0f ;/* inner spring constants function */ + + if(bs->len > 0.0f) /* check for degenerated springs */ + forcefactor = iks/bs->len; + else + forcefactor = iks; + forcefactor *= bs->strength; + Vec3PlusStVec(bp1->force,(bs->len - distance)*forcefactor,dir); + + /* do bp1 <--> bp2 viscous */ + VecSubf(dvel,bp1->vec,bp2->vec); + kd = sb->infrict * sb_fric_force_scale(ob); + absvel = Normalize(dvel); + projvel = Inpf(dir,dvel); + kd *= absvel * projvel; + Vec3PlusStVec(bp1->force,-kd,dir); + + /* do jacobian stuff if needed */ + if(nl_flags & NLF_BUILD){ + //int op =3*sb->totpoint; + //float mvel = -forcetime*kd; + //float mpos = -forcetime*forcefactor; + /* depending on my pos */ + // dfdx_spring(ia,ia,op,dir,bs->len,distance,-mpos); + /* depending on my vel */ + // dfdv_goal(ia,ia,mvel); // well that ignores geometie + if(bp2->goal < SOFTGOALSNAP){ /* ommit this bp when it snaps */ + /* depending on other pos */ + // dfdx_spring(ia,ic,op,dir,bs->len,distance,mpos); + /* depending on other vel */ + // dfdv_goal(ia,ia,-mvel); // well that ignores geometie + } + } +} + + +/* since this is definitely the most CPU consuming task here .. try to spread it */ +/* core function _softbody_calc_forces_slice_in_a_thread */ +/* result is int to be able to flag user break */ +static int _softbody_calc_forces_slice_in_a_thread(Object *ob, float forcetime, float timenow,int ifirst,int ilast,int *ptr_to_break_func(),ListBase *do_effector,int do_deflector,float fieldfactor, float windfactor) +{ + float iks; + int bb,do_selfcollision,do_springcollision,do_aero; + int number_of_points_here = ilast - ifirst; SoftBody *sb= ob->soft; /* is supposed to be there */ BodyPoint *bp; - BodyPoint *bproot; - BodySpring *bs; - ListBase *do_effector; - float iks, ks, kd, gravity, actspringlen, forcefactor, sd[3]; - float fieldfactor = 1000.0f, windfactor = 250.0f; - float tune = sb->ballstiff; - int a, b, do_deflector,do_selfcollision,do_springcollision,do_aero; - - - gravity = sb->grav * sb_grav_force_scale(ob); - + /* intitialize */ + if (sb) { /* check conditions for various options */ - do_deflector= query_external_colliders(ob); - do_effector= pdInitEffectors(ob,NULL); + /* +++ could be done on object level to squeeze out the last bits of it */ do_selfcollision=((ob->softflag & OB_SB_EDGES) && (sb->bspring)&& (ob->softflag & OB_SB_SELF)); do_springcollision=do_deflector && (ob->softflag & OB_SB_EDGES) &&(ob->softflag & OB_SB_EDGECOLL); do_aero=((sb->aeroedge)&& (ob->softflag & OB_SB_EDGES)); - - iks = 1.0f/(1.0f-sb->inspring)-1.0f ;/* inner spring constants function */ - bproot= sb->bpoint; /* need this for proper spring addressing */ - - - - if (do_springcollision || do_aero) scan_for_ext_spring_forces(ob,timenow); - if (do_deflector) { - float defforce[3]; - do_deflector = sb_detect_aabb_collisionCached(defforce,ob->lay,ob,timenow); + /* --- could be done on object level to squeeze out the last bits of it */ + } + else { + printf("Error expected a SB here \n"); + return (999); } - if (do_selfcollision ){ - float ce[3]; - VecMidf(ce,sb->scratch->aabbmax,sb->scratch->aabbmin); - for(a=sb->totpoint, bp= sb->bpoint; a>0; a--, bp++) { - bp->octantflag = set_octant_flags(ce,bp->pos,bp->colball); - } +/* debugerin */ + if (sb->totpoint < ifirst) { + printf("Aye 998"); + return (998); } +/* debugerin */ - for(a=sb->totpoint, bp= sb->bpoint; a>0; a--, bp++) { + + bp = &sb->bpoint[ifirst]; + for(bb=number_of_points_here; bb>0; bb--, bp++) { /* clear forces accumulator */ bp->force[0]= bp->force[1]= bp->force[2]= 0.0; - /* naive ball self collision */ /* needs to be done if goal snaps or not */ if(do_selfcollision){ int attached; BodyPoint *obp; + BodySpring *bs; int c,b; float velcenter[3],dvel[3],def[3]; float distance; float compare; + float bstune = sb->ballstiff; - for(c=sb->totpoint, obp= sb->bpoint; c>=a; c--, obp++) { - - if ((bp->octantflag & obp->octantflag) == 0) continue; - + for(c=sb->totpoint, obp= sb->bpoint; c>=ifirst+bb; c--, obp++) { compare = (obp->colball + bp->colball); VecSubf(def, bp->pos, obp->pos); - /* rather check the AABBoxes before ever calulating the real distance */ /* mathematically it is completly nuts, but performace is pretty much (3) times faster */ if ((ABS(def[0]) > compare) || (ABS(def[1]) > compare) || (ABS(def[2]) > compare)) continue; - distance = Normalize(def); if (distance < compare ){ /* exclude body points attached with a spring */ attached = 0; for(b=obp->nofsprings;b>0;b--){ bs = sb->bspring + obp->springs[b-1]; - if (( sb->totpoint-a == bs->v2) || ( sb->totpoint-a == bs->v1)){ + if (( ilast-bb == bs->v2) || ( ilast-bb == bs->v1)){ attached=1; continue;} } if (!attached){ - float f = tune/(distance) + tune/(compare*compare)*distance - 2.0f*tune/compare ; + float f = bstune/(distance) + bstune/(compare*compare)*distance - 2.0f*bstune/compare ; VecMidf(velcenter, bp->vec, obp->vec); VecSubf(dvel,velcenter,bp->vec); VecMulf(dvel,sb->nodemass); - Vec3PlusStVec(bp->force,sb->balldamp,dvel); Vec3PlusStVec(bp->force,f*(1.0f-sb->balldamp),def); + Vec3PlusStVec(bp->force,sb->balldamp,dvel); + /* exploit force(a,b) == -force(b,a) part2/2 */ VecSubf(dvel,velcenter,obp->vec); VecMulf(dvel,sb->nodemass); Vec3PlusStVec(obp->force,sb->balldamp,dvel); Vec3PlusStVec(obp->force,-f*(1.0f-sb->balldamp),def); - } } } @@ -2197,24 +2205,26 @@ static void softbody_calc_forces(Object *ob, float forcetime, float timenow) if(bp->goal < SOFTGOALSNAP){ /* ommit this bp when it snaps */ float auxvect[3]; float velgoal[3]; - float absvel =0, projvel= 0; - + /* do goal stuff */ if(ob->softflag & OB_SB_GOAL) { /* true elastic goal */ - VecSubf(auxvect,bp->origT,bp->pos); + float ks,kd; + VecSubf(auxvect,bp->pos,bp->origT); ks = 1.0f/(1.0f- bp->goal*sb->goalspring)-1.0f ; - bp->force[0]+= ks*(auxvect[0]); - bp->force[1]+= ks*(auxvect[1]); - bp->force[2]+= ks*(auxvect[2]); + bp->force[0]+= -ks*(auxvect[0]); + bp->force[1]+= -ks*(auxvect[1]); + bp->force[2]+= -ks*(auxvect[2]); + /* calulate damping forces generated by goals*/ VecSubf(velgoal,bp->origS, bp->origE); kd = sb->goalfrict * sb_fric_force_scale(ob) ; + VecAddf(auxvect,velgoal,bp->vec); if (forcetime > 0.0 ) { /* make sure friction does not become rocket motor on time reversal */ - bp->force[0]-= kd * (velgoal[0] + bp->vec[0]); - bp->force[1]-= kd * (velgoal[1] + bp->vec[1]); - bp->force[2]-= kd * (velgoal[2] + bp->vec[2]); + bp->force[0]-= kd * (auxvect[0]); + bp->force[1]-= kd * (auxvect[1]); + bp->force[2]-= kd * (auxvect[2]); } else { bp->force[0]-= kd * (velgoal[0] - bp->vec[0]); @@ -2224,13 +2234,15 @@ static void softbody_calc_forces(Object *ob, float forcetime, float timenow) } /* done goal stuff */ - /* gravitation */ + if (sb){ + float gravity = sb->grav * sb_grav_force_scale(ob); bp->force[2]-= gravity*sb->nodemass; /* individual mass of node here */ - + } /* particle field & vortex */ if(do_effector) { + float kd; float force[3]= {0.0f, 0.0f, 0.0f}; float speed[3]= {0.0f, 0.0f, 0.0f}; float eval_sb_fric_force_scale = sb_fric_force_scale(ob); /* just for calling function once */ @@ -2251,7 +2263,7 @@ static void softbody_calc_forces(Object *ob, float forcetime, float timenow) } else { /* BP friction in media (not) moving*/ - kd= sb->mediafrict* sb_fric_force_scale(ob); + float kd = sb->mediafrict* sb_fric_force_scale(ob); /* assume it to be proportional to actual velocity */ bp->force[0]-= bp->vec[0]*kd; bp->force[1]-= bp->vec[1]*kd; @@ -2260,40 +2272,34 @@ static void softbody_calc_forces(Object *ob, float forcetime, float timenow) } /* +++cached collision targets */ bp->choke = 0.0f; + bp->choke2 = 0.0f; bp->flag &= ~SBF_DOFUZZY; if(do_deflector) { float cfforce[3],defforce[3] ={0.0f,0.0f,0.0f}, vel[3] = {0.0f,0.0f,0.0f}, facenormal[3], cf = 1.0f,intrusion; - kd = 1.0f; + float kd = 1.0f; if (sb_deflect_face(ob,bp->pos,facenormal,defforce,&cf,timenow,vel,&intrusion)){ - if (intrusion < 0.0f){ - /*bjornmose: uugh.. what an evil hack - violation of the 'don't touch bp->pos in here' rule - but works nice, like this--> - we predict the solution beeing out of the collider - in heun step No1 and leave the heun step No2 adapt to it - so we kind of introduced a implicit solver for this case - */ - Vec3PlusStVec(bp->pos,-intrusion,facenormal); - - sb->scratch->flag |= SBF_DOFUZZY; - bp->flag |= SBF_DOFUZZY; - bp->choke = sb->choke*0.01f; - } - else{ + if (intrusion < 0.0f){ + sb->scratch->flag |= SBF_DOFUZZY; + bp->flag |= SBF_DOFUZZY; + bp->choke = sb->choke*0.01f; + } + VECSUB(cfforce,bp->vec,vel); Vec3PlusStVec(bp->force,-cf*50.0f,cfforce); - } - - Vec3PlusStVec(bp->force,kd,defforce); + + Vec3PlusStVec(bp->force,kd,defforce); } } /* ---cached collision targets */ /* +++springs */ + iks = 1.0f/(1.0f-sb->inspring)-1.0f ;/* inner spring constants function */ if(ob->softflag & OB_SB_EDGES) { if (sb->bspring){ /* spring list exists at all ? */ + int b; + BodySpring *bs; for(b=bp->nofsprings;b>0;b--){ bs = sb->bspring + bp->springs[b-1]; if (do_springcollision || do_aero){ @@ -2302,67 +2308,532 @@ static void softbody_calc_forces(Object *ob, float forcetime, float timenow) bp->choke = bs->cf; } - - if (( (sb->totpoint-a) == bs->v1) ){ - actspringlen= VecLenf( (bproot+bs->v2)->pos, bp->pos); - VecSubf(sd,(bproot+bs->v2)->pos, bp->pos); - Normalize(sd); - - /* friction stuff V1 */ - VecSubf(velgoal,bp->vec,(bproot+bs->v2)->vec); - kd = sb->infrict * sb_fric_force_scale(ob); - absvel = Normalize(velgoal); - projvel = ABS(Inpf(sd,velgoal)); - kd *= absvel * projvel; - Vec3PlusStVec(bp->force,-kd,velgoal); - - if(bs->len > 0.0f) /* check for degenerated springs */ - forcefactor = (bs->len - actspringlen)/bs->len * iks; - else - forcefactor = actspringlen * iks; - forcefactor *= bs->strength; - - Vec3PlusStVec(bp->force,-forcefactor,sd); - - } - - if (( (sb->totpoint-a) == bs->v2) ){ - actspringlen= VecLenf( (bproot+bs->v1)->pos, bp->pos); - VecSubf(sd,bp->pos,(bproot+bs->v1)->pos); - Normalize(sd); - - /* friction stuff V2 */ - VecSubf(velgoal,bp->vec,(bproot+bs->v1)->vec); - kd = sb->infrict * sb_fric_force_scale(ob); - absvel = Normalize(velgoal); - projvel = ABS(Inpf(sd,velgoal)); - kd *= absvel * projvel; - Vec3PlusStVec(bp->force,-kd,velgoal); - - if(bs->len > 0.0) - forcefactor = (bs->len - actspringlen)/bs->len * iks; - else - forcefactor = actspringlen * iks; - forcefactor *= bs->strength; - Vec3PlusStVec(bp->force,+forcefactor,sd); - } + // sb_spring_force(Object *ob,int bpi,BodySpring *bs,float iks,float forcetime,int nl_flags) + sb_spring_force(ob,ilast-bb,bs,iks,forcetime,0); }/* loop springs */ }/* existing spring list */ }/*any edges*/ /* ---springs */ }/*omit on snap */ }/*loop all bp's*/ +return 0; /*done fine*/ +} + +static void *exec_softbody_calc_forces(void *data) +{ + SB_thread_context *pctx = (SB_thread_context*)data; + _softbody_calc_forces_slice_in_a_thread(pctx->ob,pctx->forcetime,pctx->timenow,pctx->ifirst,pctx->ilast,NULL,pctx->do_effector,pctx->do_deflector,pctx->fieldfactor,pctx->windfactor); + return 0; +} + +static void sb_cf_threads_run(struct Object *ob, float forcetime, float timenow,int totpoint,int *ptr_to_break_func(),struct ListBase *do_effector,int do_deflector,float fieldfactor, float windfactor) +{ + ListBase threads; + SB_thread_context *sb_threads; + int i, totthread,left,dec; + int lowpoints =100; /* wild guess .. may increase with better thread management 'above' or even be UI option sb->spawn_cf_threads_nopts */ + + /* figure the number of threads while preventing pretty pointless threading overhead */ + if(G.scene->r.mode & R_FIXED_THREADS) + totthread= G.scene->r.threads; + else + totthread= BLI_system_thread_count(); + /* what if we got zillions of CPUs running but less to spread*/ + while ((totpoint/totthread < lowpoints) && (totthread > 1)) { + totthread--; + } + + /* printf("sb_cf_threads_run spawning %d threads \n",totthread); */ + + sb_threads= MEM_callocN(sizeof(SB_thread_context)*totthread, "SBThread"); + memset(sb_threads, 0, sizeof(SB_thread_context)*totthread); + left = totpoint; + dec = totpoint/totthread +1; + for(i=0; i<totthread; i++) { + sb_threads[i].ob = ob; + sb_threads[i].forcetime = forcetime; + sb_threads[i].timenow = timenow; + sb_threads[i].ilast = left; + left = left - dec; + if (left >0){ + sb_threads[i].ifirst = left; + } + else + sb_threads[i].ifirst = 0; + sb_threads[i].do_effector = do_effector; + sb_threads[i].do_deflector = do_deflector; + sb_threads[i].fieldfactor = fieldfactor; + sb_threads[i].windfactor = windfactor; + sb_threads[i].nr= i; + sb_threads[i].tot= totthread; + } + + + if(totthread > 1) { + BLI_init_threads(&threads, exec_softbody_calc_forces, totthread); + + for(i=0; i<totthread; i++) + BLI_insert_thread(&threads, &sb_threads[i]); + + BLI_end_threads(&threads); + } + else + exec_softbody_calc_forces(&sb_threads[0]); + /* clean up */ + MEM_freeN(sb_threads); +} +static void softbody_calc_forcesEx(Object *ob, float forcetime, float timenow, int nl_flags) +{ +/* rule we never alter free variables :bp->vec bp->pos in here ! + * this will ruin adaptive stepsize AKA heun! (BM) + */ + SoftBody *sb= ob->soft; /* is supposed to be there */ + BodyPoint *bproot; + ListBase *do_effector; + float iks, gravity; + float fieldfactor = 1000.0f, windfactor = 250.0f; + int do_deflector,do_selfcollision,do_springcollision,do_aero; + + gravity = sb->grav * sb_grav_force_scale(ob); + + /* check conditions for various options */ + do_deflector= query_external_colliders(ob); + do_selfcollision=((ob->softflag & OB_SB_EDGES) && (sb->bspring)&& (ob->softflag & OB_SB_SELF)); + do_springcollision=do_deflector && (ob->softflag & OB_SB_EDGES) &&(ob->softflag & OB_SB_EDGECOLL); + do_aero=((sb->aeroedge)&& (ob->softflag & OB_SB_EDGES)); + + iks = 1.0f/(1.0f-sb->inspring)-1.0f ;/* inner spring constants function */ + bproot= sb->bpoint; /* need this for proper spring addressing */ + + if (do_springcollision || do_aero) + sb_sfesf_threads_run(ob,timenow,sb->totspring,NULL); + + /* after spring scan because it uses Effoctors too */ + do_effector= pdInitEffectors(ob,NULL); + + if (do_deflector) { + float defforce[3]; + do_deflector = sb_detect_aabb_collisionCached(defforce,ob->lay,ob,timenow); + } + + sb_cf_threads_run(ob,forcetime,timenow,sb->totpoint,NULL,do_effector,do_deflector,fieldfactor,windfactor); /* finally add forces caused by face collision */ - if (ob->softflag & OB_SB_FACECOLL) scan_for_ext_face_forces(ob,timenow); - /* cleanup */ + if (ob->softflag & OB_SB_FACECOLL) scan_for_ext_face_forces(ob,timenow); + + /* finish matrix and solve */ if(do_effector) pdEndEffectors(do_effector); } -static void softbody_apply_forces(Object *ob, float forcetime, int mode, float *err) + +static void softbody_calc_forces(Object *ob, float forcetime, float timenow, int nl_flags) +{ + /* redirection to the new threaded Version */ + if (!(G.rt & 0x10)){ // 16 + softbody_calc_forcesEx(ob, forcetime, timenow, nl_flags); + return; + } + else{ + /* so the following will die */ + /* |||||||||||||||||||||||||| */ + /* VVVVVVVVVVVVVVVVVVVVVVVVVV */ + /*backward compatibility note: + fixing bug [17428] which forces adaptive step size to tiny steps + in some situations + .. keeping G.rt==17 0x11 option for old files 'needing' the bug*/ + + /* rule we never alter free variables :bp->vec bp->pos in here ! + * this will ruin adaptive stepsize AKA heun! (BM) + */ + SoftBody *sb= ob->soft; /* is supposed to be there */ + BodyPoint *bp; + BodyPoint *bproot; + BodySpring *bs; + ListBase *do_effector; + float iks, ks, kd, gravity; + float fieldfactor = 1000.0f, windfactor = 250.0f; + float tune = sb->ballstiff; + int a, b, do_deflector,do_selfcollision,do_springcollision,do_aero; + + + /* jacobian + NLboolean success; + + if(nl_flags){ + nlBegin(NL_SYSTEM); + nlBegin(NL_MATRIX); + } + */ + + + gravity = sb->grav * sb_grav_force_scale(ob); + + /* check conditions for various options */ + do_deflector= query_external_colliders(ob); + do_selfcollision=((ob->softflag & OB_SB_EDGES) && (sb->bspring)&& (ob->softflag & OB_SB_SELF)); + do_springcollision=do_deflector && (ob->softflag & OB_SB_EDGES) &&(ob->softflag & OB_SB_EDGECOLL); + do_aero=((sb->aeroedge)&& (ob->softflag & OB_SB_EDGES)); + + iks = 1.0f/(1.0f-sb->inspring)-1.0f ;/* inner spring constants function */ + bproot= sb->bpoint; /* need this for proper spring addressing */ + + if (do_springcollision || do_aero) scan_for_ext_spring_forces(ob,timenow); + /* after spring scan because it uses Effoctors too */ + do_effector= pdInitEffectors(ob,NULL); + + if (do_deflector) { + float defforce[3]; + do_deflector = sb_detect_aabb_collisionCached(defforce,ob->lay,ob,timenow); + } + + for(a=sb->totpoint, bp= sb->bpoint; a>0; a--, bp++) { + /* clear forces accumulator */ + bp->force[0]= bp->force[1]= bp->force[2]= 0.0; + if(nl_flags & NLF_BUILD){ + //int ia =3*(sb->totpoint-a); + //int op =3*sb->totpoint; + /* dF/dV = v */ + /* jacobioan + nlMatrixAdd(op+ia,ia,-forcetime); + nlMatrixAdd(op+ia+1,ia+1,-forcetime); + nlMatrixAdd(op+ia+2,ia+2,-forcetime); + + nlMatrixAdd(ia,ia,1); + nlMatrixAdd(ia+1,ia+1,1); + nlMatrixAdd(ia+2,ia+2,1); + + nlMatrixAdd(op+ia,op+ia,1); + nlMatrixAdd(op+ia+1,op+ia+1,1); + nlMatrixAdd(op+ia+2,op+ia+2,1); + */ + + + } + + /* naive ball self collision */ + /* needs to be done if goal snaps or not */ + if(do_selfcollision){ + int attached; + BodyPoint *obp; + int c,b; + float velcenter[3],dvel[3],def[3]; + float distance; + float compare; + + for(c=sb->totpoint, obp= sb->bpoint; c>=a; c--, obp++) { + + //if ((bp->octantflag & obp->octantflag) == 0) continue; + + compare = (obp->colball + bp->colball); + VecSubf(def, bp->pos, obp->pos); + + /* rather check the AABBoxes before ever calulating the real distance */ + /* mathematically it is completly nuts, but performace is pretty much (3) times faster */ + if ((ABS(def[0]) > compare) || (ABS(def[1]) > compare) || (ABS(def[2]) > compare)) continue; + + distance = Normalize(def); + if (distance < compare ){ + /* exclude body points attached with a spring */ + attached = 0; + for(b=obp->nofsprings;b>0;b--){ + bs = sb->bspring + obp->springs[b-1]; + if (( sb->totpoint-a == bs->v2) || ( sb->totpoint-a == bs->v1)){ + attached=1; + continue;} + } + if (!attached){ + float f = tune/(distance) + tune/(compare*compare)*distance - 2.0f*tune/compare ; + + VecMidf(velcenter, bp->vec, obp->vec); + VecSubf(dvel,velcenter,bp->vec); + VecMulf(dvel,sb->nodemass); + + Vec3PlusStVec(bp->force,f*(1.0f-sb->balldamp),def); + Vec3PlusStVec(bp->force,sb->balldamp,dvel); + + if(nl_flags & NLF_BUILD){ + //int ia =3*(sb->totpoint-a); + //int ic =3*(sb->totpoint-c); + //int op =3*sb->totpoint; + //float mvel = forcetime*sb->nodemass*sb->balldamp; + //float mpos = forcetime*tune*(1.0f-sb->balldamp); + /*some quick and dirty entries to the jacobian*/ + //dfdx_goal(ia,ia,op,mpos); + //dfdv_goal(ia,ia,mvel); + /* exploit force(a,b) == -force(b,a) part1/2 */ + //dfdx_goal(ic,ic,op,mpos); + //dfdv_goal(ic,ic,mvel); + + + /*TODO sit down an X-out the true jacobian entries*/ + /*well does not make to much sense because the eigenvalues + of the jacobian go negative; and negative eigenvalues + on a complex iterative system z(n+1)=A * z(n) + give imaginary roots in the charcateristic polynom + --> solutions that to z(t)=u(t)* exp ( i omega t) --> oscilations we don't want here + where u(t) is a unknown amplitude function (worst case rising fast) + */ + } + + /* exploit force(a,b) == -force(b,a) part2/2 */ + VecSubf(dvel,velcenter,obp->vec); + VecMulf(dvel,sb->nodemass); + + Vec3PlusStVec(obp->force,sb->balldamp,dvel); + Vec3PlusStVec(obp->force,-f*(1.0f-sb->balldamp),def); + + + } + } + } + } + /* naive ball self collision done */ + + if(bp->goal < SOFTGOALSNAP){ /* ommit this bp when it snaps */ + float auxvect[3]; + float velgoal[3]; + + /* do goal stuff */ + if(ob->softflag & OB_SB_GOAL) { + /* true elastic goal */ + VecSubf(auxvect,bp->pos,bp->origT); + ks = 1.0f/(1.0f- bp->goal*sb->goalspring)-1.0f ; + bp->force[0]+= -ks*(auxvect[0]); + bp->force[1]+= -ks*(auxvect[1]); + bp->force[2]+= -ks*(auxvect[2]); + + if(nl_flags & NLF_BUILD){ + //int ia =3*(sb->totpoint-a); + //int op =3*(sb->totpoint); + /* depending on my pos */ + //dfdx_goal(ia,ia,op,ks*forcetime); + } + + + /* calulate damping forces generated by goals*/ + VecSubf(velgoal,bp->origS, bp->origE); + kd = sb->goalfrict * sb_fric_force_scale(ob) ; + VecAddf(auxvect,velgoal,bp->vec); + + if (forcetime > 0.0 ) { /* make sure friction does not become rocket motor on time reversal */ + bp->force[0]-= kd * (auxvect[0]); + bp->force[1]-= kd * (auxvect[1]); + bp->force[2]-= kd * (auxvect[2]); + if(nl_flags & NLF_BUILD){ + //int ia =3*(sb->totpoint-a); + Normalize(auxvect); + /* depending on my vel */ + //dfdv_goal(ia,ia,kd*forcetime); + } + + } + else { + bp->force[0]-= kd * (velgoal[0] - bp->vec[0]); + bp->force[1]-= kd * (velgoal[1] - bp->vec[1]); + bp->force[2]-= kd * (velgoal[2] - bp->vec[2]); + } + } + /* done goal stuff */ + + + /* gravitation */ + bp->force[2]-= gravity*sb->nodemass; /* individual mass of node here */ + //bp->force[1]-= gravity*sb->nodemass; /* individual mass of node here */ + + + /* particle field & vortex */ + if(do_effector) { + float force[3]= {0.0f, 0.0f, 0.0f}; + float speed[3]= {0.0f, 0.0f, 0.0f}; + float eval_sb_fric_force_scale = sb_fric_force_scale(ob); /* just for calling function once */ + + pdDoEffectors(do_effector, bp->pos, force, speed, (float)G.scene->r.cfra, 0.0f, PE_WIND_AS_SPEED); + + /* apply forcefield*/ + VecMulf(force,fieldfactor* eval_sb_fric_force_scale); + VECADD(bp->force, bp->force, force); + + /* BP friction in moving media */ + kd= sb->mediafrict* eval_sb_fric_force_scale; + bp->force[0] -= kd * (bp->vec[0] + windfactor*speed[0]/eval_sb_fric_force_scale); + bp->force[1] -= kd * (bp->vec[1] + windfactor*speed[1]/eval_sb_fric_force_scale); + bp->force[2] -= kd * (bp->vec[2] + windfactor*speed[2]/eval_sb_fric_force_scale); + /* now we'll have nice centrifugal effect for vortex */ + + } + else { + /* BP friction in media (not) moving*/ + kd= sb->mediafrict* sb_fric_force_scale(ob); + /* assume it to be proportional to actual velocity */ + bp->force[0]-= bp->vec[0]*kd; + bp->force[1]-= bp->vec[1]*kd; + bp->force[2]-= bp->vec[2]*kd; + /* friction in media done */ + if(nl_flags & NLF_BUILD){ + //int ia =3*(sb->totpoint-a); + /* da/dv = */ + + // nlMatrixAdd(ia,ia,forcetime*kd); + // nlMatrixAdd(ia+1,ia+1,forcetime*kd); + // nlMatrixAdd(ia+2,ia+2,forcetime*kd); + } + + } + /* +++cached collision targets */ + bp->choke = 0.0f; + bp->choke2 = 0.0f; + bp->flag &= ~SBF_DOFUZZY; + if(do_deflector) { + float cfforce[3],defforce[3] ={0.0f,0.0f,0.0f}, vel[3] = {0.0f,0.0f,0.0f}, facenormal[3], cf = 1.0f,intrusion; + kd = 1.0f; + + if (sb_deflect_face(ob,bp->pos,facenormal,defforce,&cf,timenow,vel,&intrusion)){ + if ((!nl_flags)&&(intrusion < 0.0f)){ + if(G.rt & 0x01){ // 17 we did check for bit 0x10 before + /*fixing bug [17428] this forces adaptive step size to tiny steps + in some situations .. keeping G.rt==17 option for old files 'needing' the bug + */ + /*bjornmose: uugh.. what an evil hack + violation of the 'don't touch bp->pos in here' rule + but works nice, like this--> + we predict the solution beeing out of the collider + in heun step No1 and leave the heun step No2 adapt to it + so we kind of introduced a implicit solver for this case + */ + Vec3PlusStVec(bp->pos,-intrusion,facenormal); + } + else{ + + VECSUB(cfforce,bp->vec,vel); + Vec3PlusStVec(bp->force,-cf*50.0f,cfforce); + } + + + sb->scratch->flag |= SBF_DOFUZZY; + bp->flag |= SBF_DOFUZZY; + bp->choke = sb->choke*0.01f; + } + else{ + VECSUB(cfforce,bp->vec,vel); + Vec3PlusStVec(bp->force,-cf*50.0f,cfforce); + } + Vec3PlusStVec(bp->force,kd,defforce); + if (nl_flags & NLF_BUILD){ + // int ia =3*(sb->totpoint-a); + // int op =3*sb->totpoint; + //dfdx_goal(ia,ia,op,mpos); // don't do unless you know + //dfdv_goal(ia,ia,-cf); + + } + + } + + } + /* ---cached collision targets */ + + /* +++springs */ + if(ob->softflag & OB_SB_EDGES) { + if (sb->bspring){ /* spring list exists at all ? */ + for(b=bp->nofsprings;b>0;b--){ + bs = sb->bspring + bp->springs[b-1]; + if (do_springcollision || do_aero){ + VecAddf(bp->force,bp->force,bs->ext_force); + if (bs->flag & BSF_INTERSECT) + bp->choke = bs->cf; + + } + // sb_spring_force(Object *ob,int bpi,BodySpring *bs,float iks,float forcetime,int nl_flags) + // rather remove nl_falgs from code .. will make things a lot cleaner + sb_spring_force(ob,sb->totpoint-a,bs,iks,forcetime,0); + }/* loop springs */ + }/* existing spring list */ + }/*any edges*/ + /* ---springs */ + }/*omit on snap */ + }/*loop all bp's*/ + + + /* finally add forces caused by face collision */ + if (ob->softflag & OB_SB_FACECOLL) scan_for_ext_face_forces(ob,timenow); + + /* finish matrix and solve */ +#if (0) // remove onl linking for now .. still i am not sure .. the jacobian can be usefull .. so keep that BM + if(nl_flags & NLF_SOLVE){ + //double sct,sst=PIL_check_seconds_timer(); + for(a=sb->totpoint, bp= sb->bpoint; a>0; a--, bp++) { + int iv =3*(sb->totpoint-a); + int ip =3*(2*sb->totpoint-a); + int n; + for (n=0;n<3;n++) {nlRightHandSideSet(0, iv+n, bp->force[0+n]);} + for (n=0;n<3;n++) {nlRightHandSideSet(0, ip+n, bp->vec[0+n]);} + } + nlEnd(NL_MATRIX); + nlEnd(NL_SYSTEM); + + if ((G.rt == 32) && (nl_flags & NLF_BUILD)) + { + printf("####MEE#####\n"); + nlPrintMatrix(); + } + + success= nlSolveAdvanced(NULL, 1); + + // nlPrintMatrix(); /* for debug purpose .. anyhow cropping B vector looks like working */ + if(success){ + float f; + int index =0; + /* for debug purpose .. anyhow cropping B vector looks like working */ + if (G.rt ==32) + for(a=2*sb->totpoint, bp= sb->bpoint; a>0; a--, bp++) { + f=nlGetVariable(0,index); + printf("(%f ",f);index++; + f=nlGetVariable(0,index); + printf("%f ",f);index++; + f=nlGetVariable(0,index); + printf("%f)",f);index++; + } + + index =0; + for(a=sb->totpoint, bp= sb->bpoint; a>0; a--, bp++) { + f=nlGetVariable(0,index); + bp->impdv[0] = f; index++; + f=nlGetVariable(0,index); + bp->impdv[1] = f; index++; + f=nlGetVariable(0,index); + bp->impdv[2] = f; index++; + } + /* + for(a=sb->totpoint, bp= sb->bpoint; a>0; a--, bp++) { + f=nlGetVariable(0,index); + bp->impdx[0] = f; index++; + f=nlGetVariable(0,index); + bp->impdx[1] = f; index++; + f=nlGetVariable(0,index); + bp->impdx[2] = f; index++; + } + */ + } + else{ + printf("Matrix inversion failed \n"); + for(a=sb->totpoint, bp= sb->bpoint; a>0; a--, bp++) { + VECCOPY(bp->impdv,bp->force); + } + + } + + //sct=PIL_check_seconds_timer(); + //if (sct-sst > 0.01f) printf(" implicit solver time %f %s \r",sct-sst,ob->id.name); + } + /* cleanup */ +#endif + if(do_effector) pdEndEffectors(do_effector); + } +} + +static void softbody_apply_forces(Object *ob, float forcetime, int mode, float *err, int mid_flags) { /* time evolution */ /* actually does an explicit euler step mode == 0 */ @@ -2370,7 +2841,7 @@ static void softbody_apply_forces(Object *ob, float forcetime, int mode, float * SoftBody *sb= ob->soft; /* is supposed to be there */ BodyPoint *bp; float dx[3],dv[3],aabbmin[3],aabbmax[3],cm[3]={0.0f,0.0f,0.0f}; - float timeovermass; + float timeovermass/*,freezeloc=0.00001f,freezeforce=0.00000000001f*/; float maxerrpos= 0.0f,maxerrvel = 0.0f; int a,fuzzy=0; @@ -2385,14 +2856,14 @@ static void softbody_apply_forces(Object *ob, float forcetime, int mode, float * for(a=sb->totpoint, bp= sb->bpoint; a>0; a--, bp++) { if(bp->goal < SOFTGOALSNAP){ + /* this makes t~ = t */ + if(mid_flags & MID_PRESERVE) VECCOPY(dx,bp->vec); /* so here is (v)' = a(cceleration) = sum(F_springs)/m + gravitation + some friction forces + more forces*/ /* the ( ... )' operator denotes derivate respective time */ /* the euler step for velocity then becomes */ /* v(t + dt) = v(t) + a(t) * dt */ - bp->force[0]*= timeovermass; /* individual mass of node here */ - bp->force[1]*= timeovermass; - bp->force[2]*= timeovermass; + VecMulf(bp->force,timeovermass);/* individual mass of node here */ /* some nasty if's to have heun in here too */ VECCOPY(dv,bp->force); @@ -2413,14 +2884,24 @@ static void softbody_apply_forces(Object *ob, float forcetime, int mode, float * } else {VECADD(bp->vec, bp->vec, bp->force);} + /* this makes t~ = t+dt */ + if(!(mid_flags & MID_PRESERVE)) VECCOPY(dx,bp->vec); + /* so here is (x)'= v(elocity) */ /* the euler step for location then becomes */ - /* x(t + dt) = x(t) + v(t) * dt */ - - VECCOPY(dx,bp->vec); - dx[0]*=forcetime; - dx[1]*=forcetime; - dx[2]*=forcetime; + /* x(t + dt) = x(t) + v(t~) * dt */ + VecMulf(dx,forcetime); + + /* the freezer coming sooner or later */ + /* + if ((Inpf(dx,dx)<freezeloc )&&(Inpf(bp->force,bp->force)<freezeforce )){ + bp->frozen /=2; + } + else{ + bp->frozen =MIN2(bp->frozen*1.05f,1.0f); + } + VecMulf(dx,bp->frozen); + */ /* again some nasty if's to have heun in here too */ if (mode ==1){ VECCOPY(bp->prevpos,bp->pos); @@ -2430,7 +2911,7 @@ static void softbody_apply_forces(Object *ob, float forcetime, int mode, float * if (mode ==2){ bp->pos[0] = bp->prevpos[0] + 0.5f * ( dx[0] + bp->prevdx[0]); bp->pos[1] = bp->prevpos[1] + 0.5f * ( dx[1] + bp->prevdx[1]); - bp->pos[2] = bp->prevpos[2] + 0.5f* ( dx[2] + bp->prevdx[2]); + bp->pos[2] = bp->prevpos[2] + 0.5f * ( dx[2] + bp->prevdx[2]); maxerrpos = MAX2(maxerrpos,ABS(dx[0] - bp->prevdx[0])); maxerrpos = MAX2(maxerrpos,ABS(dx[1] - bp->prevdx[1])); maxerrpos = MAX2(maxerrpos,ABS(dx[2] - bp->prevdx[2])); @@ -2439,10 +2920,11 @@ static void softbody_apply_forces(Object *ob, float forcetime, int mode, float * the collider object signals to get out by pushing hard. on the other hand we don't want to end up in deep space so we add some <viscosity> to balance that out */ + if (bp->choke2 > 0.0f){ + VecMulf(bp->vec,(1.0f - bp->choke2)); + } if (bp->choke > 0.0f){ - bp->vec[0] = bp->vec[0]*(1.0f - bp->choke); - bp->vec[1] = bp->vec[1]*(1.0f - bp->choke); - bp->vec[2] = bp->vec[2]*(1.0f - bp->choke); + VecMulf(bp->vec,(1.0f - bp->choke)); } } @@ -2489,6 +2971,81 @@ static void softbody_restore_prev_step(Object *ob) } } +#if 0 +static void softbody_store_step(Object *ob) +{ + SoftBody *sb= ob->soft; /* is supposed to be there*/ + BodyPoint *bp; + int a; + + for(a=sb->totpoint, bp= sb->bpoint; a>0; a--, bp++) { + VECCOPY(bp->prevvec,bp->vec); + VECCOPY(bp->prevpos,bp->pos); + } +} + + +/* used by predictors and correctors */ +static void softbody_store_state(Object *ob,float *ppos,float *pvel) +{ + SoftBody *sb= ob->soft; /* is supposed to be there*/ + BodyPoint *bp; + int a; + float *pp=ppos,*pv=pvel; + + for(a=sb->totpoint, bp= sb->bpoint; a>0; a--, bp++) { + + VECCOPY(pv, bp->vec); + pv+=3; + + VECCOPY(pp, bp->pos); + pp+=3; + } +} + +/* used by predictors and correctors */ +static void softbody_retrieve_state(Object *ob,float *ppos,float *pvel) +{ + SoftBody *sb= ob->soft; /* is supposed to be there*/ + BodyPoint *bp; + int a; + float *pp=ppos,*pv=pvel; + + for(a=sb->totpoint, bp= sb->bpoint; a>0; a--, bp++) { + + VECCOPY(bp->vec,pv); + pv+=3; + + VECCOPY(bp->pos,pp); + pp+=3; + } +} + +/* used by predictors and correctors */ +static void softbody_swap_state(Object *ob,float *ppos,float *pvel) +{ + SoftBody *sb= ob->soft; /* is supposed to be there*/ + BodyPoint *bp; + int a; + float *pp=ppos,*pv=pvel; + float temp[3]; + + for(a=sb->totpoint, bp= sb->bpoint; a>0; a--, bp++) { + + VECCOPY(temp, bp->vec); + VECCOPY(bp->vec,pv); + VECCOPY(pv,temp); + pv+=3; + + VECCOPY(temp, bp->pos); + VECCOPY(bp->pos,pp); + VECCOPY(pp,temp); + pp+=3; + } +} +#endif + + /* care for bodypoints taken out of the 'ordinary' solver step ** because they are screwed to goal by bolts ** they just need to move along with the goal in time @@ -2509,6 +3066,30 @@ static void softbody_apply_goalsnap(Object *ob) } } + +static void apply_spring_memory(Object *ob) +{ + SoftBody *sb = ob->soft; + BodySpring *bs; + BodyPoint *bp1, *bp2; + int a; + float b,l,r; + + b = sb->plastic; + if (sb && sb->totspring){ + for(a=0; a<sb->totspring; a++) { + bs = &sb->bspring[a]; + bp1 =&sb->bpoint[bs->v1]; + bp2 =&sb->bpoint[bs->v2]; + l = VecLenf(bp1->pos,bp2->pos); + r = bs->len/l; + if (( r > 1.05f) || (r < 0.95)){ + bs->len = ((100.0f - b) * bs->len + b*l)/100.0f; + } + } + } +} + /* expects full initialized softbody */ static void interpolate_exciter(Object *ob, int timescale, int time) { @@ -2597,9 +3178,9 @@ static void springs_from_mesh(Object *ob) } /* recalculate spring length for meshes here */ - /* special hidden feature! shrink to fit */ - if (G.rt > 500){ - scale = (G.rt - 500) / 100.0f; + /* public version shrink to fit */ + if (sb->springpreload != 0 ){ + scale = sb->springpreload / 100.0f; } for(a=0; a<sb->totspring; a++) { BodySpring *bs = &sb->bspring[a]; @@ -2712,7 +3293,7 @@ static void mesh_faces_to_scratch(Object *ob) helper function to get proper spring length when object is rescaled */ -float globallen(float *v1,float *v2,Object *ob) +static float globallen(float *v1,float *v2,Object *ob) { float p1[3],p2[3]; VECCOPY(p1,v1); @@ -3002,9 +3583,12 @@ static void particles_to_softbody(Object *ob) /* renew ends with ob->soft with points and edges, also checks & makes ob->soft */ renew_softbody(ob, totpoint, totedge); - psys->particles->bpi = 0; - for(a=1, pa=psys->particles+1; a<psys->totpart; a++, pa++) - pa->bpi = (pa-1)->bpi + pa->totkey; + /* find first BodyPoint index for each particle */ + if(psys->totpart > 0) { + psys->particles->bpi = 0; + for(a=1, pa=psys->particles+1; a<psys->totpart; a++, pa++) + pa->bpi = (pa-1)->bpi + (pa-1)->totkey; + } /* we always make body points */ sb= ob->soft; @@ -3066,97 +3650,68 @@ static void softbody_to_object(Object *ob, float (*vertexCos)[3], int numVerts, } } -void softbody_clear_cache(Object *ob, float framenr) -{ - SoftBody *sb = ob->soft; - ModifierData *md = ob->modifiers.first; - int stack_index = -1; - int a; - - if(sb==NULL) return; - - if(sb->particles) - stack_index = modifiers_indexInObject(ob,(ModifierData*)psys_get_modifier(ob,sb->particles)); - else { - for(a=0; md; md=md->next, a++) { - if(md->type == eModifierType_Softbody) { - stack_index = a; - break; - } - } - } - - BKE_ptcache_id_clear((ID *)ob, PTCACHE_CLEAR_ALL, framenr, stack_index); -} -static void softbody_write_cache(Object *ob, float framenr) +void sbWriteCache(Object *ob, int framenr) { - FILE *fp = NULL; - SoftBody *sb = ob->soft; + SoftBody *sb= ob->soft; BodyPoint *bp; - ModifierData *md = ob->modifiers.first; - int stack_index = -1; + PTCacheID pid; + PTCacheFile *pf; int a; - if(sb->totpoint == 0) return; - - if(sb->particles) - stack_index = modifiers_indexInObject(ob,(ModifierData*)psys_get_modifier(ob,sb->particles)); - else { - for(a=0; md; md=md->next, a++) { - if(md->type == eModifierType_Softbody) { - stack_index = a; - break; - } - } - } + if(sb->totpoint == 0) + return; - fp = BKE_ptcache_id_fopen((ID *)ob, 'w', framenr, stack_index); - if(!fp) return; + BKE_ptcache_id_from_softbody(&pid, ob, sb); + pf= BKE_ptcache_file_open(&pid, PTCACHE_FILE_WRITE, framenr); + if(!pf) + return; + + for(a=0, bp=sb->bpoint; a<sb->totpoint; a++, bp++) + BKE_ptcache_file_write_floats(pf, bp->pos, 3); for(a=0, bp=sb->bpoint; a<sb->totpoint; a++, bp++) - fwrite(&bp->pos, sizeof(float), 3, fp); - - fclose(fp); + BKE_ptcache_file_write_floats(pf, bp->vec, 3); + + BKE_ptcache_file_close(pf); } + static int softbody_read_cache(Object *ob, float framenr) { - FILE *fp = NULL; - SoftBody *sb = ob->soft; + SoftBody *sb= ob->soft; BodyPoint *bp; - ModifierData *md = ob->modifiers.first; - int stack_index = -1; - int a, ret = 1; + PTCacheID pid; + PTCacheFile *pf; + int a; - if(sb->totpoint == 0) return 0; + if(sb->totpoint == 0) + return 0; + + BKE_ptcache_id_from_softbody(&pid, ob, sb); + pf= BKE_ptcache_file_open(&pid, PTCACHE_FILE_READ, framenr); + if(!pf) + return 0; - if(sb->particles) - stack_index = modifiers_indexInObject(ob,(ModifierData*)psys_get_modifier(ob,sb->particles)); - else { - for(a=0; md; md=md->next, a++) { - if(md->type == eModifierType_Softbody) { - stack_index = a; - break; - } + for(a=0, bp=sb->bpoint; a<sb->totpoint; a++, bp++) { + if(!BKE_ptcache_file_read_floats(pf, bp->pos, 3)) { + BKE_ptcache_file_close(pf); + return 0; } } - fp = BKE_ptcache_id_fopen((ID *)ob, 'r', framenr, stack_index); - if(!fp) - ret = 0; - else { - for(a=0, bp=sb->bpoint; a<sb->totpoint; a++, bp++) - if(fread(&bp->pos, sizeof(float), 3, fp) != 3) { - ret = 0; - break; - } - - fclose(fp); + for(a=0, bp=sb->bpoint; a<sb->totpoint; a++, bp++) { + if(!BKE_ptcache_file_read_floats(pf, bp->vec, 3)) { + BKE_ptcache_file_close(pf); + return 0; + } } - return ret; + BKE_ptcache_file_close(pf); + + return 1; } + /* +++ ************ maintaining scratch *************** */ -void sb_new_scratch(SoftBody *sb) +static void sb_new_scratch(SoftBody *sb) { if (!sb) return; sb->scratch = MEM_callocN(sizeof(SBScratch), "SBScratch"); @@ -3192,6 +3747,8 @@ SoftBody *sbNew(void) sb->inspring= 0.5f; sb->infrict= 0.5f; + /*todo backward file compat should copy inspring to inpush while reading old files*/ + sb->inpush = 0.5f; sb->interval= 10; sb->sfra= G.scene->r.sfra; @@ -3204,9 +3761,16 @@ SoftBody *sbNew(void) sb->minloops = 10; + sb->maxloops = 300; sb->choke = 3; sb_new_scratch(sb); + /*todo backward file compat should set sb->shearstiff = 1.0f while reading old files*/ + sb->shearstiff = 1.0f; + sb->solverflags |= SBSO_OLDERR; + + sb->pointcache = BKE_ptcache_add(); + return sb; } @@ -3214,14 +3778,19 @@ SoftBody *sbNew(void) void sbFree(SoftBody *sb) { free_softbody_intern(sb); + BKE_ptcache_free(sb->pointcache); MEM_freeN(sb); } +void sbFreeSimulation(SoftBody *sb) +{ + free_softbody_intern(sb); +} /* makes totally fresh start situation */ void sbObjectToSoftbody(Object *ob) { - ob->softflag |= OB_SB_REDO; + //ob->softflag |= OB_SB_REDO; free_softbody_intern(ob->soft); } @@ -3245,306 +3814,408 @@ void sbSetInterruptCallBack(int (*f)(void)) SB_localInterruptCallBack = f; } - -/* simulates one step. framenr is in frames */ -void sbObjectStep(Object *ob, float framenr, float (*vertexCos)[3], int numVerts) +static void softbody_update_positions(Object *ob, SoftBody *sb, float (*vertexCos)[3], int numVerts) { - ParticleSystemModifierData *psmd=0; - ParticleData *pa=0; - SoftBody *sb; + ParticleSystemModifierData *psmd= NULL; + ParticleData *pa= NULL; HairKey *key= NULL; BodyPoint *bp; + float hairmat[4][4]; int a; - float dtime,ctime,forcetime,err; + + /* update the vertex locations */ + if(sb->particles && sb->particles->totpart>0) { + psmd= psys_get_modifier(ob,sb->particles); + + pa= sb->particles->particles; + key= pa->hair; + + psys_mat_hair_to_global(ob, psmd->dm, sb->particles->part->from, pa, hairmat); + } + + for(a=0,bp=sb->bpoint; a<numVerts; a++, bp++) { + /* store where goals are now */ + VECCOPY(bp->origS, bp->origE); + /* copy the position of the goals at desired end time */ + if(sb->particles) { + if(key == pa->hair + pa->totkey) { + pa++; + key = pa->hair; + + psys_mat_hair_to_global(ob, psmd->dm, sb->particles->part->from, pa, hairmat); + } + VECCOPY(bp->origE, key->co); + Mat4MulVecfl(hairmat,bp->origE); + + key++; + } + else{ + VECCOPY(bp->origE, vertexCos[a]); + /* vertexCos came from local world, go global */ + Mat4MulVecfl(ob->obmat, bp->origE); + } + /* just to be save give bp->origT a defined value + will be calulated in interpolate_exciter()*/ + VECCOPY(bp->origT, bp->origE); + } +} + +static void softbody_reset(Object *ob, SoftBody *sb, float (*vertexCos)[3], int numVerts) +{ + ParticleSystemModifierData *psmd= NULL; + HairKey *key= NULL; + ParticleData *pa= NULL; + BodyPoint *bp; float hairmat[4][4]; + int a; - /* This part only sets goals and springs, based on original mesh/curve/lattice data. - Copying coordinates happens in next chunk by setting softbody flag OB_SB_RESET */ - /* remake softbody if: */ - if( (ob->softflag & OB_SB_REDO) || /* signal after weightpainting */ - (ob->soft==NULL) || /* just to be nice we allow full init */ - (ob->soft->bpoint==NULL) || /* after reading new file, or acceptable as signal to refresh */ - (numVerts!=ob->soft->totpoint) || /* should never happen, just to be safe */ - ((ob->softflag & OB_SB_EDGES) && !ob->soft->bspring && object_has_edges(ob))) /* happens when in UI edges was set */ - { - if(ob->soft && ob->soft->bpoint) /* don't clear on file load */ - softbody_clear_cache(ob, framenr); + if(sb->particles && sb->particles->totpart>0) { + psmd= psys_get_modifier(ob, sb->particles); + pa= sb->particles->particles; + key= pa->hair; - if(ob->soft->particles){ - particles_to_softbody(ob); + psys_mat_hair_to_global(ob, psmd->dm, sb->particles->part->from, pa, hairmat); + } + + for(a=0,bp=sb->bpoint; a<numVerts; a++, bp++) { + if(sb->particles) { + if(key == pa->hair + pa->totkey) { + pa++; + key = pa->hair; + + psys_mat_hair_to_global(ob, psmd->dm, sb->particles->part->from, pa, hairmat); + } + VECCOPY(bp->pos, key->co); + Mat4MulVecfl(hairmat, bp->pos); + key++; + } + else { + VECCOPY(bp->pos, vertexCos[a]); + Mat4MulVecfl(ob->obmat, bp->pos); /* yep, sofbody is global coords*/ } - else switch(ob->type) { + VECCOPY(bp->origS, bp->pos); + VECCOPY(bp->origE, bp->pos); + VECCOPY(bp->origT, bp->pos); + bp->vec[0]= bp->vec[1]= bp->vec[2]= 0.0f; + + /* the bp->prev*'s are for rolling back from a canceled try to propagate in time + adaptive step size algo in a nutshell: + 1. set sheduled time step to new dtime + 2. try to advance the sheduled time step, beeing optimistic execute it + 3. check for success + 3.a we 're fine continue, may be we can increase sheduled time again ?? if so, do so! + 3.b we did exceed error limit --> roll back, shorten the sheduled time and try again at 2. + 4. check if we did reach dtime + 4.a nope we need to do some more at 2. + 4.b yup we're done + */ + + VECCOPY(bp->prevpos, bp->pos); + VECCOPY(bp->prevvec, bp->vec); + VECCOPY(bp->prevdx, bp->vec); + VECCOPY(bp->prevdv, bp->vec); + } + + /* make a nice clean scratch struc */ + free_scratch(sb); /* clear if any */ + sb_new_scratch(sb); /* make a new */ + sb->scratch->needstobuildcollider=1; + + if((sb->particles)==0) { + /* copy some info to scratch */ + switch(ob->type) { case OB_MESH: - mesh_to_softbody(ob); + if (ob->softflag & OB_SB_FACECOLL) mesh_faces_to_scratch(ob); break; case OB_LATTICE: - lattice_to_softbody(ob); break; case OB_CURVE: case OB_SURF: - curve_surf_to_softbody(ob); break; default: - renew_softbody(ob, numVerts, 0); break; } + } +} + +static void softbody_step(Object *ob, SoftBody *sb, float dtime) +{ + /* the simulator */ + float forcetime; + double sct,sst=PIL_check_seconds_timer(); + ccd_update_deflector_hache(ob,sb->scratch->colliderhash); - /* still need to update to correct vertex locations, happens on next step */ - ob->softflag |= OB_SB_RESET; - ob->softflag &= ~OB_SB_REDO; + if(sb->scratch->needstobuildcollider){ + if (query_external_colliders(ob)){ + ccd_build_deflector_hache(ob,sb->scratch->colliderhash); + } + sb->scratch->needstobuildcollider=0; } - sb= ob->soft; + if (sb->solver_ID < 2) { + /* special case of 2nd order Runge-Kutta type AKA Heun */ + int mid_flags=0; + float err = 0; + float forcetimemax = 1.0f; + float forcetimemin = 0.001f; + float timedone =0.0; /* how far did we get without violating error condition */ + /* loops = counter for emergency brake + * we don't want to lock up the system if physics fail + */ + int loops =0 ; + + SoftHeunTol = sb->rklimit; /* humm .. this should be calculated from sb parameters and sizes */ + if (sb->minloops > 0) forcetimemax = 1.0f / sb->minloops; + + if (sb->maxloops > 0) forcetimemin = 1.0f / sb->maxloops; - /* still no points? go away */ - if(sb->totpoint==0) return; + if(sb->solver_ID>0) mid_flags |= MID_PRESERVE; + + //forcetime = dtime; /* hope for integrating in one step */ + forcetime =forcetimemax; /* hope for integrating in one step */ + while ( (ABS(timedone) < ABS(dtime)) && (loops < 2000) ) + { + /* set goals in time */ + interpolate_exciter(ob,200,(int)(200.0*(timedone/dtime))); + + sb->scratch->flag &= ~SBF_DOFUZZY; + /* do predictive euler step */ + softbody_calc_forces(ob, forcetime,timedone/dtime,0); - if(sb->particles){ - psmd=psys_get_modifier(ob,sb->particles); - pa=sb->particles->particles; - } + softbody_apply_forces(ob, forcetime, 1, NULL,mid_flags); - /* checking time: */ + /* crop new slope values to do averaged slope step */ + softbody_calc_forces(ob, forcetime,timedone/dtime,0); - ctime= bsystem_time(ob, framenr, 0.0); + softbody_apply_forces(ob, forcetime, 2, &err,mid_flags); + softbody_apply_goalsnap(ob); + + if (err > SoftHeunTol) { /* error needs to be scaled to some quantity */ + + if (forcetime > forcetimemin){ + forcetime = MAX2(forcetime / 2.0f,forcetimemin); + softbody_restore_prev_step(ob); + //printf("down,"); + } + else { + timedone += forcetime; + } + } + else { + float newtime = forcetime * 1.1f; /* hope for 1.1 times better conditions in next step */ + + if (sb->scratch->flag & SBF_DOFUZZY){ + //if (err > SoftHeunTol/(2.0f*sb->fuzzyness)) { /* stay with this stepsize unless err really small */ + newtime = forcetime; + //} + } + else { + if (err > SoftHeunTol/2.0f) { /* stay with this stepsize unless err really small */ + newtime = forcetime; + } + } + timedone += forcetime; + newtime=MIN2(forcetimemax,MAX2(newtime,forcetimemin)); + //if (newtime > forcetime) printf("up,"); + if (forcetime > 0.0) + forcetime = MIN2(dtime - timedone,newtime); + else + forcetime = MAX2(dtime - timedone,newtime); + } + loops++; + if(sb->solverflags & SBSO_MONITOR ){ + sct=PIL_check_seconds_timer(); + if (sct-sst > 0.5f) printf("%3.0f%% \r",100.0f*timedone); + } + /* ask for user break */ + if (SB_localInterruptCallBack && SB_localInterruptCallBack()) break; - if (ob->softflag&OB_SB_RESET) { - dtime = 0.0; - } else { - dtime= ctime - sb->ctime; + } + /* move snapped to final position */ + interpolate_exciter(ob, 2, 2); + softbody_apply_goalsnap(ob); + + // if(G.f & G_DEBUG){ + if(sb->solverflags & SBSO_MONITOR ){ + if (loops > HEUNWARNLIMIT) /* monitor high loop counts */ + printf("\r needed %d steps/frame",loops); + } + } + else if (sb->solver_ID == 2) + {/* do semi "fake" implicit euler */ + //removed + }/*SOLVER SELECT*/ + else if (sb->solver_ID == 4) + { + /* do semi "fake" implicit euler */ + }/*SOLVER SELECT*/ + else if (sb->solver_ID == 3){ + /* do "stupid" semi "fake" implicit euler */ + //removed - if(softbody_read_cache(ob, framenr)) { - if(sb->particles==0) - softbody_to_object(ob, vertexCos, numVerts, sb->local); - sb->ctime = ctime; - return; + }/*SOLVER SELECT*/ + else{ + printf("softbody no valid solver ID!"); + }/*SOLVER SELECT*/ + if(sb->plastic){ apply_spring_memory(ob);} + + if(sb->solverflags & SBSO_MONITOR ){ + sct=PIL_check_seconds_timer(); + if ((sct-sst > 0.5f) || (G.f & G_DEBUG)) printf(" solver time %f sec %s \n",sct-sst,ob->id.name); } +} - /* the simulator */ +/* simulates one step. framenr is in frames */ +void sbObjectStep(Object *ob, float cfra, float (*vertexCos)[3], int numVerts) +{ + ParticleSystemModifierData *psmd=0; + ParticleData *pa=0; + SoftBody *sb= ob->soft; + PointCache *cache; + PTCacheID pid; + float dtime, timescale; + int framedelta, framenr, startframe, endframe; - /* update the vertex locations */ - if (dtime!=0.0) { - if(sb->particles) { - pa=sb->particles->particles; - key = pa->hair; + cache= sb->pointcache; - psys_mat_hair_to_global(ob, psmd->dm, sb->particles->part->from, pa, hairmat); - } + framenr= (int)cfra; + framedelta= framenr - cache->simframe; - for(a=0,bp=sb->bpoint; a<numVerts; a++, bp++) { - /* store where goals are now */ - VECCOPY(bp->origS, bp->origE); - /* copy the position of the goals at desired end time */ - if(sb->particles) { - if(key == pa->hair + pa->totkey) { - pa++; - key = pa->hair; + BKE_ptcache_id_from_softbody(&pid, ob, sb); + BKE_ptcache_id_time(&pid, framenr, &startframe, &endframe, ×cale); - psys_mat_hair_to_global(ob, psmd->dm, sb->particles->part->from, pa, hairmat); - } - VECCOPY(bp->origE, key->co); - Mat4MulVecfl(hairmat,bp->origE); + /* check for changes in mesh, should only happen in case the mesh + * structure changes during an animation */ + if(sb->bpoint && numVerts != sb->totpoint) { + cache->flag &= ~PTCACHE_SIMULATION_VALID; + cache->simframe= 0; - key++; - } - else{ - VECCOPY(bp->origE, vertexCos[a]); - /* vertexCos came from local world, go global */ - Mat4MulVecfl(ob->obmat, bp->origE); - } - /* just to be save give bp->origT a defined value - will be calulated in interpolate_exciter()*/ - VECCOPY(bp->origT, bp->origE); - } + return; } - if((ob->softflag&OB_SB_RESET) || /* got a reset signal */ - (dtime<0.0) || /* back in time */ - (dtime>=9.9*G.scene->r.framelen) /* too far forward in time --> goals won't be accurate enough */ - ) - { - if(sb->particles) { - pa=sb->particles->particles; - key = pa->hair; + /* clamp frame ranges */ + if(framenr < startframe) { + cache->flag &= ~PTCACHE_SIMULATION_VALID; + cache->simframe= 0; - psys_mat_hair_to_global(ob, psmd->dm, sb->particles->part->from, pa, hairmat); - } + return; + } + else if(framenr > endframe) { + framenr = endframe; + } - for(a=0,bp=sb->bpoint; a<numVerts; a++, bp++) { - if(sb->particles) { - if(key == pa->hair + pa->totkey) { - pa++; - key = pa->hair; + /* verify if we need to create the softbody data */ + if(sb->bpoint == NULL || + ((ob->softflag & OB_SB_EDGES) && !ob->soft->bspring && object_has_edges(ob))) { - psys_mat_hair_to_global(ob, psmd->dm, sb->particles->part->from, pa, hairmat); - } - VECCOPY(bp->pos, key->co); - Mat4MulVecfl(hairmat, bp->pos); - key++; - } - else { - VECCOPY(bp->pos, vertexCos[a]); - Mat4MulVecfl(ob->obmat, bp->pos); /* yep, sofbody is global coords*/ - } - VECCOPY(bp->origS, bp->pos); - VECCOPY(bp->origE, bp->pos); - VECCOPY(bp->origT, bp->pos); - bp->vec[0]= bp->vec[1]= bp->vec[2]= 0.0f; - - /* the bp->prev*'s are for rolling back from a canceled try to propagate in time - adaptive step size algo in a nutshell: - 1. set sheduled time step to new dtime - 2. try to advance the sheduled time step, beeing optimistic execute it - 3. check for success - 3.a we 're fine continue, may be we can increase sheduled time again ?? if so, do so! - 3.b we did exceed error limit --> roll back, shorten the sheduled time and try again at 2. - 4. check if we did reach dtime - 4.a nope we need to do some more at 2. - 4.b yup we're done - */ - - VECCOPY(bp->prevpos, bp->pos); - VECCOPY(bp->prevvec, bp->vec); - VECCOPY(bp->prevdx, bp->vec); - VECCOPY(bp->prevdv, bp->vec); + if(sb->particles){ + particles_to_softbody(ob); } - /* make a nice clean scratch struc */ - free_scratch(sb); /* clear if any */ - sb_new_scratch(sb); /* make a new */ - sb->scratch->needstobuildcollider=1; - - if((sb->particles)==0) { - /* copy some info to scratch */ + else { switch(ob->type) { - case OB_MESH: - if (ob->softflag & OB_SB_FACECOLL) mesh_faces_to_scratch(ob); - break; - case OB_LATTICE: - break; - case OB_CURVE: - case OB_SURF: - break; - default: - break; + case OB_MESH: + mesh_to_softbody(ob); + break; + case OB_LATTICE: + lattice_to_softbody(ob); + break; + case OB_CURVE: + case OB_SURF: + curve_surf_to_softbody(ob); + break; + default: + renew_softbody(ob, numVerts, 0); + break; } } - ob->softflag &= ~OB_SB_RESET; + softbody_update_positions(ob, sb, vertexCos, numVerts); + softbody_reset(ob, sb, vertexCos, numVerts); } - else if(dtime>0.0) { - double sct,sst=PIL_check_seconds_timer(); - ccd_update_deflector_hache(ob,sb->scratch->colliderhash); + /* continue physics special case */ + if(BKE_ptcache_get_continue_physics()) { + cache->flag &= ~PTCACHE_SIMULATION_VALID; + cache->simframe= 0; - if(sb->scratch->needstobuildcollider){ - if (query_external_colliders(ob)){ - ccd_build_deflector_hache(ob,sb->scratch->colliderhash); - } - sb->scratch->needstobuildcollider=0; - } + /* do simulation */ + dtime = timescale; + softbody_update_positions(ob, sb, vertexCos, numVerts); + softbody_step(ob, sb, dtime); - if (TRUE) { /* */ - /* special case of 2nd order Runge-Kutta type AKA Heun */ - float forcetimemax = 1.0f; - float forcetimemin = 0.001f; - float timedone =0.0; /* how far did we get without violating error condition */ - /* loops = counter for emergency brake - * we don't want to lock up the system if physics fail - */ - int loops =0 ; - SoftHeunTol = sb->rklimit; /* humm .. this should be calculated from sb parameters and sizes */ - if (sb->minloops > 0) forcetimemax = 1.0f / sb->minloops; - - if (sb->maxloops > 0) forcetimemin = 1.0f / sb->maxloops; - - - //forcetime = dtime; /* hope for integrating in one step */ - forcetime =forcetimemax; /* hope for integrating in one step */ - while ( (ABS(timedone) < ABS(dtime)) && (loops < 2000) ) - { - /* set goals in time */ - interpolate_exciter(ob,200,(int)(200.0*(timedone/dtime))); - - sb->scratch->flag &= ~SBF_DOFUZZY; - /* do predictive euler step */ - softbody_calc_forces(ob, forcetime,timedone/dtime); - softbody_apply_forces(ob, forcetime, 1, NULL); + if(sb->particles==0) + softbody_to_object(ob, vertexCos, numVerts, 0); + return; + } - /* crop new slope values to do averaged slope step */ - softbody_calc_forces(ob, forcetime,timedone/dtime); - softbody_apply_forces(ob, forcetime, 2, &err); + /* still no points? go away */ + if(sb->totpoint==0) return; - softbody_apply_goalsnap(ob); - - if (err > SoftHeunTol) { /* error needs to be scaled to some quantity */ - - if (forcetime > forcetimemin){ - forcetime = MAX2(forcetime / 2.0f,forcetimemin); - softbody_restore_prev_step(ob); - //printf("down,"); - } - else { - timedone += forcetime; - } - } - else { - float newtime = forcetime * 1.1f; /* hope for 1.1 times better conditions in next step */ - - if (sb->scratch->flag & SBF_DOFUZZY){ - //if (err > SoftHeunTol/(2.0f*sb->fuzzyness)) { /* stay with this stepsize unless err really small */ - newtime = forcetime; - //} - } - else { - if (err > SoftHeunTol/2.0f) { /* stay with this stepsize unless err really small */ - newtime = forcetime; - } - } - timedone += forcetime; - newtime=MIN2(forcetimemax,MAX2(newtime,forcetimemin)); - //if (newtime > forcetime) printf("up,"); - if (forcetime > 0.0) - forcetime = MIN2(dtime - timedone,newtime); - else - forcetime = MAX2(dtime - timedone,newtime); - } - loops++; - if(sb->solverflags & SBSO_MONITOR ){ - sct=PIL_check_seconds_timer(); - if (sct-sst > 0.5f) printf("%3.0f%% \r",100.0f*timedone); - } - if (SB_localInterruptCallBack && SB_localInterruptCallBack()) break; + if(sb->particles){ + psmd= psys_get_modifier(ob, sb->particles); + pa= sb->particles->particles; + } - } - /* move snapped to final position */ - interpolate_exciter(ob, 2, 2); - softbody_apply_goalsnap(ob); - - // if(G.f & G_DEBUG){ - if(sb->solverflags & SBSO_MONITOR ){ - if (loops > HEUNWARNLIMIT) /* monitor high loop counts */ - printf("\r needed %d steps/frame ",loops); - } - - } - else{ - /* do brute force explicit euler */ - /* removed but left this branch for better integrators / solvers (BM) */ - /* yah! Nicholas Guttenberg (NichG) here is the place to plug in */ - } - if(sb->solverflags & SBSO_MONITOR ){ - sct=PIL_check_seconds_timer(); - if (sct-sst > 0.5f) printf(" solver time %f %s \r",sct-sst,ob->id.name); - } + /* try to read from cache */ + if(softbody_read_cache(ob, framenr)) { + if(sb->particles==0) + softbody_to_object(ob, vertexCos, numVerts, sb->local); + + cache->flag |= PTCACHE_SIMULATION_VALID; + cache->simframe= framenr; + + return; + } + else if(ob->id.lib || (cache->flag & PTCACHE_BAKED)) { + /* if baked and nothing in cache, do nothing */ + if(cache->flag & PTCACHE_SIMULATION_VALID) { + cache->flag &= ~PTCACHE_SIMULATION_VALID; + cache->simframe= 0; + } + + return; } - if(sb->particles==0) - softbody_to_object(ob, vertexCos, numVerts, 0); - sb->ctime= ctime; + if(framenr == startframe) { + /* first frame, no simulation to do, just set the positions */ + softbody_update_positions(ob, sb, vertexCos, numVerts); + + cache->flag |= PTCACHE_SIMULATION_VALID; + cache->simframe= framenr; + + /* don't write cache on first frame, but on second frame write + * cache for frame 1 and 2 */ + } + else if(framedelta == 1) { + /* if on second frame, write cache for first frame */ + if(framenr == startframe+1) + sbWriteCache(ob, startframe); + + softbody_update_positions(ob, sb, vertexCos, numVerts); + + /* do simulation */ + cache->flag |= PTCACHE_SIMULATION_VALID; + cache->simframe= framenr; + + /* checking time: */ + dtime = framedelta*timescale; - softbody_write_cache(ob, framenr); + softbody_step(ob, sb, dtime); + + if(sb->particles==0) + softbody_to_object(ob, vertexCos, numVerts, 0); + + sbWriteCache(ob, framenr); + } + else { + /* time step backwards or too large forward - do nothing */ + if(cache->flag & PTCACHE_SIMULATION_VALID) { + cache->flag &= ~PTCACHE_SIMULATION_VALID; + cache->simframe= 0; + } + } } diff --git a/source/blender/blenkernel/intern/sound.c b/source/blender/blenkernel/intern/sound.c index 3b14cb8adac..2c5b49246fb 100644 --- a/source/blender/blenkernel/intern/sound.c +++ b/source/blender/blenkernel/intern/sound.c @@ -109,13 +109,13 @@ PackedFile* sound_find_packedfile(bSound *sound) // convert sound->name to abolute filename strcpy(soundname, sound->name); - BLI_convertstringcode(soundname, G.sce, G.scene->r.cfra); + BLI_convertstringcode(soundname, G.sce); search = G.main->sound.first; while (search) { if (search->sample && search->sample->packedfile) { strcpy(searchname, search->sample->name); - BLI_convertstringcode(searchname, G.sce, G.scene->r.cfra); + BLI_convertstringcode(searchname, G.sce); if (BLI_streq(searchname, soundname)) { pf = search->sample->packedfile; @@ -125,7 +125,7 @@ PackedFile* sound_find_packedfile(bSound *sound) if (search->newpackedfile) { strcpy(searchname, search->name); - BLI_convertstringcode(searchname, G.sce, G.scene->r.cfra); + BLI_convertstringcode(searchname, G.sce); if (BLI_streq(searchname, soundname)) { pf = search->newpackedfile; break; diff --git a/source/blender/blenkernel/intern/subsurf_ccg.c b/source/blender/blenkernel/intern/subsurf_ccg.c index 60e01181f2d..f5124ed9056 100644 --- a/source/blender/blenkernel/intern/subsurf_ccg.c +++ b/source/blender/blenkernel/intern/subsurf_ccg.c @@ -39,15 +39,17 @@ #include "DNA_meshdata_types.h" #include "DNA_modifier_types.h" #include "DNA_object_types.h" +#include "DNA_scene_types.h" #include "BKE_cdderivedmesh.h" #include "BKE_customdata.h" +#include "BKE_DerivedMesh.h" +#include "BKE_displist.h" #include "BKE_utildefines.h" #include "BKE_global.h" #include "BKE_mesh.h" +#include "BKE_scene.h" #include "BKE_subsurf.h" -#include "BKE_displist.h" -#include "BKE_DerivedMesh.h" #include "BLI_blenlib.h" #include "BLI_editVert.h" @@ -58,6 +60,10 @@ #include "BIF_gl.h" +#include "GPU_draw.h" +#include "GPU_extensions.h" +#include "GPU_material.h" + #include "CCGSubSurf.h" typedef struct _VertData { @@ -79,9 +85,9 @@ struct CCGDerivedMesh { typedef struct CCGDerivedMesh CCGDerivedMesh; -static int ccgDM_getVertMapIndex(CCGDerivedMesh *ccgdm, CCGSubSurf *ss, CCGVert *v); -static int ccgDM_getEdgeMapIndex(CCGDerivedMesh *ccgdm, CCGSubSurf *ss, CCGEdge *e); -static int ccgDM_getFaceMapIndex(CCGDerivedMesh *ccgdm, CCGSubSurf *ss, CCGFace *f); +static int ccgDM_getVertMapIndex(CCGSubSurf *ss, CCGVert *v); +static int ccgDM_getEdgeMapIndex(CCGSubSurf *ss, CCGEdge *e); +static int ccgDM_getFaceMapIndex(CCGSubSurf *ss, CCGFace *f); /// @@ -154,8 +160,8 @@ static CCGSubSurf *_getSubSurf(CCGSubSurf *prevSS, int subdivLevels, int useAgin } static int getEdgeIndex(CCGSubSurf *ss, CCGEdge *e, int x, int edgeSize) { - CCGVert *v0 = ccgSubSurf_getEdgeVert0(ss, e); - CCGVert *v1 = ccgSubSurf_getEdgeVert1(ss, e); + CCGVert *v0 = ccgSubSurf_getEdgeVert0(e); + CCGVert *v1 = ccgSubSurf_getEdgeVert1(e); int v0idx = *((int*) ccgSubSurf_getVertUserData(ss, v0)); int v1idx = *((int*) ccgSubSurf_getVertUserData(ss, v1)); int edgeBase = *((int*) ccgSubSurf_getEdgeUserData(ss, e)); @@ -170,7 +176,7 @@ static int getEdgeIndex(CCGSubSurf *ss, CCGEdge *e, int x, int edgeSize) { } static int getFaceIndex(CCGSubSurf *ss, CCGFace *f, int S, int x, int y, int edgeSize, int gridSize) { int faceBase = *((int*) ccgSubSurf_getFaceUserData(ss, f)); - int numVerts = ccgSubSurf_getFaceNumVerts(ss, f); + int numVerts = ccgSubSurf_getFaceNumVerts(f); if (x==gridSize-1 && y==gridSize-1) { CCGVert *v = ccgSubSurf_getFaceVert(ss, f, S); @@ -179,7 +185,7 @@ static int getFaceIndex(CCGSubSurf *ss, CCGFace *f, int S, int x, int y, int edg CCGVert *v = ccgSubSurf_getFaceVert(ss, f, S); CCGEdge *e = ccgSubSurf_getFaceEdge(ss, f, S); int edgeBase = *((int*) ccgSubSurf_getEdgeUserData(ss, e)); - if (v==ccgSubSurf_getEdgeVert0(ss, e)) { + if (v==ccgSubSurf_getEdgeVert0(e)) { return edgeBase + (gridSize-1-y)-1; } else { return edgeBase + (edgeSize-2-1)-((gridSize-1-y)-1); @@ -188,7 +194,7 @@ static int getFaceIndex(CCGSubSurf *ss, CCGFace *f, int S, int x, int y, int edg CCGVert *v = ccgSubSurf_getFaceVert(ss, f, S); CCGEdge *e = ccgSubSurf_getFaceEdge(ss, f, (S+numVerts-1)%numVerts); int edgeBase = *((int*) ccgSubSurf_getEdgeUserData(ss, e)); - if (v==ccgSubSurf_getEdgeVert0(ss, e)) { + if (v==ccgSubSurf_getEdgeVert0(e)) { return edgeBase + (gridSize-1-x)-1; } else { return edgeBase + (edgeSize-2-1)-((gridSize-1-x)-1); @@ -218,7 +224,7 @@ static void get_face_uv_map_vert(UvVertMap *vmap, struct MFace *mf, int fi, CCGV break; } - fverts[j]= (CCGVertHDL)(nv->f*4 + nv->tfindex); + fverts[j]= SET_INT_IN_POINTER(nv->f*4 + nv->tfindex); } } @@ -235,7 +241,7 @@ static int ss_sync_from_uv(CCGSubSurf *ss, CCGSubSurf *origss, DerivedMesh *dm, EdgeHash *ehash; float creaseFactor = (float)ccgSubSurf_getSubdivisionLevels(ss); - limit[0]= limit[1]= 0.0001f; + limit[0]= limit[1]= STD_UV_CONNECT_LIMIT; vmap= make_uv_vert_map(mface, tface, totface, totvert, 0, limit); if (!vmap) return 0; @@ -256,7 +262,7 @@ static int ss_sync_from_uv(CCGSubSurf *ss, CCGSubSurf *origss, DerivedMesh *dm, for (v=get_uv_map_vert(vmap, i); v; v=v->next) { if (v->separate) { CCGVert *ssv; - CCGVertHDL vhdl = (CCGVertHDL)(v->f*4 + v->tfindex); + CCGVertHDL vhdl = SET_INT_IN_POINTER(v->f*4 + v->tfindex); float uv[3]; uv[0]= (tface+v->f)->uv[v->tfindex][0]; @@ -274,26 +280,26 @@ static int ss_sync_from_uv(CCGSubSurf *ss, CCGSubSurf *origss, DerivedMesh *dm, for (i=0; i<totface; i++) { MFace *mf = &((MFace*) mface)[i]; int nverts= mf->v4? 4: 3; - CCGFace *origf= ccgSubSurf_getFace(origss, (CCGFaceHDL)i); + CCGFace *origf= ccgSubSurf_getFace(origss, SET_INT_IN_POINTER(i)); unsigned int *fv = &mf->v1; get_face_uv_map_vert(vmap, mf, i, fverts); for (j=0; j<nverts; j++) { - int v0 = (int)fverts[j]; - int v1 = (int)fverts[(j+1)%nverts]; + int v0 = GET_INT_FROM_POINTER(fverts[j]); + int v1 = GET_INT_FROM_POINTER(fverts[(j+1)%nverts]); MVert *mv0 = mvert + *(fv+j); MVert *mv1 = mvert + *(fv+((j+1)%nverts)); if (!BLI_edgehash_haskey(ehash, v0, v1)) { CCGEdge *e, *orige= ccgSubSurf_getFaceEdge(origss, origf, j); - CCGEdgeHDL ehdl= (CCGEdgeHDL)(i*4 + j); + CCGEdgeHDL ehdl= SET_INT_IN_POINTER(i*4 + j); float crease; if ((mv0->flag&mv1->flag) & ME_VERT_MERGED) crease = creaseFactor; else - crease = ccgSubSurf_getEdgeCrease(origss, orige); + crease = ccgSubSurf_getEdgeCrease(orige); ccgSubSurf_syncEdge(ss, ehdl, fverts[j], fverts[(j+1)%nverts], crease, &e); BLI_edgehash_insert(ehash, v0, v1, NULL); @@ -310,7 +316,7 @@ static int ss_sync_from_uv(CCGSubSurf *ss, CCGSubSurf *origss, DerivedMesh *dm, CCGFace *f; get_face_uv_map_vert(vmap, mf, i, fverts); - ccgSubSurf_syncFace(ss, (CCGFaceHDL)i, nverts, fverts, &f); + ccgSubSurf_syncFace(ss, SET_INT_IN_POINTER(i), nverts, fverts, &f); } free_uv_vert_map(vmap); @@ -352,7 +358,7 @@ static void set_subsurf_uv(CCGSubSurf *ss, DerivedMesh *dm, DerivedMesh *result, fi = ccgSubSurf_getFaceIterator(uvss); for(; !ccgFaceIterator_isStopped(fi); ccgFaceIterator_next(fi)) { CCGFace *f = ccgFaceIterator_getCurrent(fi); - faceMap[(int) ccgSubSurf_getFaceFaceHandle(uvss, f)] = f; + faceMap[GET_INT_FROM_POINTER(ccgSubSurf_getFaceFaceHandle(uvss, f))] = f; } ccgFaceIterator_free(fi); @@ -361,7 +367,7 @@ static void set_subsurf_uv(CCGSubSurf *ss, DerivedMesh *dm, DerivedMesh *result, for(index = 0; index < totface; index++) { CCGFace *f = faceMap[index]; - int numVerts = ccgSubSurf_getFaceNumVerts(uvss, f); + int numVerts = ccgSubSurf_getFaceNumVerts(f); for (S=0; S<numVerts; S++) { VertData *faceGridData= ccgSubSurf_getFaceGridDataArray(uvss, f, S); @@ -392,12 +398,12 @@ static void set_subsurf_uv(CCGSubSurf *ss, DerivedMesh *dm, DerivedMesh *result, static unsigned int ss_getEdgeFlags(CCGSubSurf *ss, CCGEdge *e, int ssFromEditmesh, DispListMesh *dlm, MEdge *medge, MTFace *tface) { unsigned int flags = 0; - int N = ccgSubSurf_getEdgeNumFaces(ss, e); + int N = ccgSubSurf_getEdgeNumFaces(e); if (!N) flags |= ME_LOOSEEDGE; if (ssFromEditmesh) { - EditEdge *eed = ccgSubSurf_getEdgeEdgeHandle(ss, e); + EditEdge *eed = ccgSubSurf_getEdgeEdgeHandle(e); flags |= ME_EDGEDRAW|ME_EDGERENDER; if (eed->seam) { @@ -463,7 +469,7 @@ static void calc_ss_weights(int gridFaces, } } -DerivedMesh *ss_to_cdderivedmesh(CCGSubSurf *ss, int ssFromEditmesh, +static DerivedMesh *ss_to_cdderivedmesh(CCGSubSurf *ss, int ssFromEditmesh, int drawInteriorEdges, int useSubsurfUv, DerivedMesh *dm) { @@ -473,7 +479,6 @@ DerivedMesh *ss_to_cdderivedmesh(CCGSubSurf *ss, int ssFromEditmesh, int gridFaces = gridSize - 1; int edgeBase, faceBase; int i, j, k, S, x, y, index; - int vertBase = 0; CCGVertIterator *vi; CCGEdgeIterator *ei; CCGFaceIterator *fi; @@ -496,7 +501,7 @@ DerivedMesh *ss_to_cdderivedmesh(CCGSubSurf *ss, int ssFromEditmesh, for(; !ccgVertIterator_isStopped(vi); ccgVertIterator_next(vi)) { CCGVert *v = ccgVertIterator_getCurrent(vi); - vertMap2[(int) ccgSubSurf_getVertVertHandle(ss, v)] = v; + vertMap2[GET_INT_FROM_POINTER(ccgSubSurf_getVertVertHandle(v))] = v; } ccgVertIterator_free(vi); @@ -506,7 +511,7 @@ DerivedMesh *ss_to_cdderivedmesh(CCGSubSurf *ss, int ssFromEditmesh, for(; !ccgEdgeIterator_isStopped(ei); ccgEdgeIterator_next(ei)) { CCGEdge *e = ccgEdgeIterator_getCurrent(ei); - edgeMap2[(int) ccgSubSurf_getEdgeEdgeHandle(ss, e)] = e; + edgeMap2[GET_INT_FROM_POINTER(ccgSubSurf_getEdgeEdgeHandle(e))] = e; } totface = ccgSubSurf_getNumFaces(ss); @@ -515,7 +520,7 @@ DerivedMesh *ss_to_cdderivedmesh(CCGSubSurf *ss, int ssFromEditmesh, for(; !ccgFaceIterator_isStopped(fi); ccgFaceIterator_next(fi)) { CCGFace *f = ccgFaceIterator_getCurrent(fi); - faceMap2[(int) ccgSubSurf_getFaceFaceHandle(ss, f)] = f; + faceMap2[GET_INT_FROM_POINTER(ccgSubSurf_getFaceFaceHandle(ss, f))] = f; } ccgFaceIterator_free(fi); @@ -536,18 +541,18 @@ DerivedMesh *ss_to_cdderivedmesh(CCGSubSurf *ss, int ssFromEditmesh, for(index = 0; index < totface; index++) { CCGFace *f = faceMap2[index]; - int x, y, S, numVerts = ccgSubSurf_getFaceNumVerts(ss, f); + int x, y, S, numVerts = ccgSubSurf_getFaceNumVerts(f); FaceVertWeight *weight = (numVerts == 4) ? qweight : tweight; int vertIdx[4]; for(S = 0; S < numVerts; S++) { CCGVert *v = ccgSubSurf_getFaceVert(ss, f, S); - vertIdx[S] = (int)ccgSubSurf_getVertVertHandle(ss, v); + vertIdx[S] = GET_INT_FROM_POINTER(ccgSubSurf_getVertVertHandle(v)); } DM_interp_vert_data(dm, result, vertIdx, weight[0][0], numVerts, i); - VecCopyf(mvert->co, ccgSubSurf_getFaceCenterData(ss, f)); + VecCopyf(mvert->co, ccgSubSurf_getFaceCenterData(f)); *origIndex = ORIGINDEX_NONE; ++mvert; ++origIndex; @@ -606,10 +611,10 @@ DerivedMesh *ss_to_cdderivedmesh(CCGSubSurf *ss, int ssFromEditmesh, int vertIdx[2]; CCGVert *v; - v = ccgSubSurf_getEdgeVert0(ss, e); - vertIdx[0] = (int)ccgSubSurf_getVertVertHandle(ss, v); - v = ccgSubSurf_getEdgeVert1(ss, e); - vertIdx[1] = (int)ccgSubSurf_getVertVertHandle(ss, v); + v = ccgSubSurf_getEdgeVert0(e); + vertIdx[0] = GET_INT_FROM_POINTER(ccgSubSurf_getVertVertHandle(v)); + v = ccgSubSurf_getEdgeVert1(e); + vertIdx[1] = GET_INT_FROM_POINTER(ccgSubSurf_getVertVertHandle(v)); for(x = 1; x < edgeSize - 1; x++) { float w[2]; @@ -627,18 +632,17 @@ DerivedMesh *ss_to_cdderivedmesh(CCGSubSurf *ss, int ssFromEditmesh, edgeBase += edgeSize-2; } - vertBase = i; for(index = 0; index < totvert; index++) { CCGVert *v = vertMap2[index]; int vertIdx; - vertIdx = (int)ccgSubSurf_getVertVertHandle(ss, v); + vertIdx = GET_INT_FROM_POINTER(ccgSubSurf_getVertVertHandle(v)); DM_copy_vert_data(dm, result, vertIdx, i, 1); VecCopyf(mvert->co, ccgSubSurf_getVertData(ss, v)); *((int*)ccgSubSurf_getVertUserData(ss, v)) = i; - *origIndex = ccgDM_getVertMapIndex(NULL, ss, v); + *origIndex = ccgDM_getVertMapIndex(ss, v); ++mvert; ++origIndex; i++; @@ -651,7 +655,7 @@ DerivedMesh *ss_to_cdderivedmesh(CCGSubSurf *ss, int ssFromEditmesh, for(index = 0; index < totface; index++) { CCGFace *f = faceMap2[index]; - int numVerts = ccgSubSurf_getFaceNumVerts(ss, f); + int numVerts = ccgSubSurf_getFaceNumVerts(f); for(k = 0; k < numVerts; k++) { for(x = 0; x < gridFaces; x++) { @@ -693,9 +697,10 @@ DerivedMesh *ss_to_cdderivedmesh(CCGSubSurf *ss, int ssFromEditmesh, for(index = 0; index < totedge; index++) { CCGEdge *e = edgeMap2[index]; unsigned int flags = 0; - int edgeIdx = (int)ccgSubSurf_getEdgeEdgeHandle(ss, e); + char bweight = 0; + int edgeIdx = GET_INT_FROM_POINTER(ccgSubSurf_getEdgeEdgeHandle(e)); - if(!ccgSubSurf_getEdgeNumFaces(ss, e)) flags |= ME_LOOSEEDGE; + if(!ccgSubSurf_getEdgeNumFaces(e)) flags |= ME_LOOSEEDGE; if(edgeIdx != -1 && dm) { @@ -703,13 +708,15 @@ DerivedMesh *ss_to_cdderivedmesh(CCGSubSurf *ss, int ssFromEditmesh, dm->getEdge(dm, edgeIdx, &origMed); flags |= origMed.flag; + bweight = origMed.bweight; } for(x = 0; x < edgeSize - 1; x++) { med->v1 = getEdgeIndex(ss, e, x, edgeSize); med->v2 = getEdgeIndex(ss, e, x + 1, edgeSize); med->flag = flags; - *origIndex = ccgDM_getEdgeMapIndex(NULL, ss, e); + med->bweight = bweight; + *origIndex = ccgDM_getEdgeMapIndex(ss, e); ++med; ++origIndex; i++; @@ -723,11 +730,11 @@ DerivedMesh *ss_to_cdderivedmesh(CCGSubSurf *ss, int ssFromEditmesh, for(index = 0; index < totface; index++) { CCGFace *f = faceMap2[index]; - int numVerts = ccgSubSurf_getFaceNumVerts(ss, f); + int numVerts = ccgSubSurf_getFaceNumVerts(f); int mat_nr; int flag; - int mapIndex = ccgDM_getFaceMapIndex(NULL, ss, f); - int faceIdx = (int)ccgSubSurf_getFaceFaceHandle(ss, f); + int mapIndex = ccgDM_getFaceMapIndex(ss, f); + int faceIdx = GET_INT_FROM_POINTER(ccgSubSurf_getFaceFaceHandle(ss, f)); if(!ssFromEditmesh) { MFace origMFace; @@ -831,9 +838,9 @@ static void ss_sync_from_derivedmesh(CCGSubSurf *ss, DerivedMesh *dm, CCGVert *v; if(vertexCos) { - ccgSubSurf_syncVert(ss, (CCGVertHDL)i, vertexCos[i], 0, &v); + ccgSubSurf_syncVert(ss, SET_INT_IN_POINTER(i), vertexCos[i], 0, &v); } else { - ccgSubSurf_syncVert(ss, (CCGVertHDL)i, mv->co, 0, &v); + ccgSubSurf_syncVert(ss, SET_INT_IN_POINTER(i), mv->co, 0, &v); } ((int*)ccgSubSurf_getVertUserData(ss, v))[1] = *index; @@ -848,8 +855,8 @@ static void ss_sync_from_derivedmesh(CCGSubSurf *ss, DerivedMesh *dm, crease = useFlatSubdiv ? creaseFactor : me->crease * creaseFactor / 255.0f; - ccgSubSurf_syncEdge(ss, (CCGEdgeHDL)i, (CCGVertHDL)me->v1, - (CCGVertHDL)me->v2, crease, &e); + ccgSubSurf_syncEdge(ss, SET_INT_IN_POINTER(i), SET_INT_IN_POINTER(me->v1), + SET_INT_IN_POINTER(me->v2), crease, &e); ((int*)ccgSubSurf_getEdgeUserData(ss, e))[1] = *index; } @@ -859,16 +866,16 @@ static void ss_sync_from_derivedmesh(CCGSubSurf *ss, DerivedMesh *dm, for (i = 0; i < totface; i++, mf++, index++) { CCGFace *f; - fVerts[0] = (CCGVertHDL) mf->v1; - fVerts[1] = (CCGVertHDL) mf->v2; - fVerts[2] = (CCGVertHDL) mf->v3; - fVerts[3] = (CCGVertHDL) mf->v4; + fVerts[0] = SET_INT_IN_POINTER(mf->v1); + fVerts[1] = SET_INT_IN_POINTER(mf->v2); + fVerts[2] = SET_INT_IN_POINTER(mf->v3); + fVerts[3] = SET_INT_IN_POINTER(mf->v4); // this is very bad, means mesh is internally consistent. // it is not really possible to continue without modifying // other parts of code significantly to handle missing faces. // since this really shouldn't even be possible we just bail. - if(ccgSubSurf_syncFace(ss, (CCGFaceHDL)i, fVerts[3] ? 4 : 3, + if(ccgSubSurf_syncFace(ss, SET_INT_IN_POINTER(i), fVerts[3] ? 4 : 3, fVerts, &f) == eCCGError_InvalidValue) { static int hasGivenError = 0; @@ -890,15 +897,15 @@ static void ss_sync_from_derivedmesh(CCGSubSurf *ss, DerivedMesh *dm, /***/ -static int ccgDM_getVertMapIndex(CCGDerivedMesh *ccgdm, CCGSubSurf *ss, CCGVert *v) { +static int ccgDM_getVertMapIndex(CCGSubSurf *ss, CCGVert *v) { return ((int*) ccgSubSurf_getVertUserData(ss, v))[1]; } -static int ccgDM_getEdgeMapIndex(CCGDerivedMesh *ccgdm, CCGSubSurf *ss, CCGEdge *e) { +static int ccgDM_getEdgeMapIndex(CCGSubSurf *ss, CCGEdge *e) { return ((int*) ccgSubSurf_getEdgeUserData(ss, e))[1]; } -static int ccgDM_getFaceMapIndex(CCGDerivedMesh *ccgdm, CCGSubSurf *ss, CCGFace *f) { +static int ccgDM_getFaceMapIndex(CCGSubSurf *ss, CCGFace *f) { return ((int*) ccgSubSurf_getFaceUserData(ss, f))[1]; } @@ -931,7 +938,7 @@ static void ccgDM_getMinMax(DerivedMesh *dm, float min_r[3], float max_r[3]) { for (; !ccgFaceIterator_isStopped(fi); ccgFaceIterator_next(fi)) { CCGFace *f = ccgFaceIterator_getCurrent(fi); - int S, x, y, numVerts = ccgSubSurf_getFaceNumVerts(ss, f); + int S, x, y, numVerts = ccgSubSurf_getFaceNumVerts(f); for (S=0; S<numVerts; S++) { VertData *faceGridData = ccgSubSurf_getFaceGridDataArray(ss, f, S); @@ -970,7 +977,7 @@ static void ccgDM_getFinalVert(DerivedMesh *dm, int vertNum, MVert *mv) memset(mv, 0, sizeof(*mv)); - if(vertNum < ccgdm->edgeMap[0].startVert) { + if((vertNum < ccgdm->edgeMap[0].startVert) && (ccgSubSurf_getNumFaces(ss) > 0)) { /* this vert comes from face data */ int lastface = ccgSubSurf_getNumFaces(ss) - 1; CCGFace *f; @@ -987,7 +994,7 @@ static void ccgDM_getFinalVert(DerivedMesh *dm, int vertNum, MVert *mv) ++i; f = ccgdm->faceMap[i].face; - numVerts = ccgSubSurf_getFaceNumVerts(ss, f); + numVerts = ccgSubSurf_getFaceNumVerts(f); gridSideVerts = gridSize - 2; gridInternalVerts = gridSideVerts * gridSideVerts; @@ -997,7 +1004,7 @@ static void ccgDM_getFinalVert(DerivedMesh *dm, int vertNum, MVert *mv) offset = vertNum - ccgdm->faceMap[i].startVert; if(offset < 1) { - VecCopyf(mv->co, ccgSubSurf_getFaceCenterData(ss, f)); + VecCopyf(mv->co, ccgSubSurf_getFaceCenterData(f)); } else if(offset < gridSideEnd) { offset -= 1; grid = offset / gridSideVerts; @@ -1011,7 +1018,7 @@ static void ccgDM_getFinalVert(DerivedMesh *dm, int vertNum, MVert *mv) x = offset % gridSideVerts + 1; VecCopyf(mv->co, ccgSubSurf_getFaceGridData(ss, f, grid, x, y)); } - } else if(vertNum < ccgdm->vertMap[0].startVert) { + } else if((vertNum < ccgdm->vertMap[0].startVert) && (ccgSubSurf_getNumEdges(ss) > 0)) { /* this vert comes from edge data */ CCGEdge *e; int lastedge = ccgSubSurf_getNumEdges(ss) - 1; @@ -1059,7 +1066,7 @@ static void ccgDM_getFinalEdge(DerivedMesh *dm, int edgeNum, MEdge *med) ++i; f = ccgdm->faceMap[i].face; - numVerts = ccgSubSurf_getFaceNumVerts(ss, f); + numVerts = ccgSubSurf_getFaceNumVerts(f); gridSideEdges = gridSize - 1; gridInternalEdges = (gridSideEdges - 1) * gridSideEdges * 2; @@ -1095,7 +1102,7 @@ static void ccgDM_getFinalEdge(DerivedMesh *dm, int edgeNum, MEdge *med) e = ccgdm->edgeMap[i].edge; - if(!ccgSubSurf_getEdgeNumFaces(ss, e)) flags |= ME_LOOSEEDGE; + if(!ccgSubSurf_getEdgeNumFaces(e)) flags |= ME_LOOSEEDGE; x = edgeNum - ccgdm->edgeMap[i].startEdge; @@ -1137,7 +1144,7 @@ static void ccgDM_getFinalFace(DerivedMesh *dm, int faceNum, MFace *mf) ++i; f = ccgdm->faceMap[i].face; - numVerts = ccgSubSurf_getFaceNumVerts(ss, f); + numVerts = ccgSubSurf_getFaceNumVerts(f); offset = faceNum - ccgdm->faceMap[i].startFace; grid = offset / gridFaces; @@ -1167,9 +1174,9 @@ static void ccgDM_copyFinalVertArray(DerivedMesh *dm, MVert *mvert) totface = ccgSubSurf_getNumFaces(ss); for(index = 0; index < totface; index++) { CCGFace *f = ccgdm->faceMap[index].face; - int x, y, S, numVerts = ccgSubSurf_getFaceNumVerts(ss, f); + int x, y, S, numVerts = ccgSubSurf_getFaceNumVerts(f); - VecCopyf(mvert[i++].co, ccgSubSurf_getFaceCenterData(ss, f)); + VecCopyf(mvert[i++].co, ccgSubSurf_getFaceCenterData(f)); for(S = 0; S < numVerts; S++) { for(x = 1; x < gridSize - 1; x++) { @@ -1222,7 +1229,7 @@ static void ccgDM_copyFinalEdgeArray(DerivedMesh *dm, MEdge *medge) totface = ccgSubSurf_getNumFaces(ss); for(index = 0; index < totface; index++) { CCGFace *f = ccgdm->faceMap[index].face; - int x, y, S, numVerts = ccgSubSurf_getFaceNumVerts(ss, f); + int x, y, S, numVerts = ccgSubSurf_getFaceNumVerts(f); for(S = 0; S < numVerts; S++) { for(x = 0; x < gridSize - 1; x++) { @@ -1266,9 +1273,9 @@ static void ccgDM_copyFinalEdgeArray(DerivedMesh *dm, MEdge *medge) CCGEdge *e = ccgdm->edgeMap[index].edge; unsigned int flags = 0; int x; - int edgeIdx = (int)ccgSubSurf_getEdgeEdgeHandle(ss, e); + int edgeIdx = GET_INT_FROM_POINTER(ccgSubSurf_getEdgeEdgeHandle(e)); - if(!ccgSubSurf_getEdgeNumFaces(ss, e)) flags |= ME_LOOSEEDGE; + if(!ccgSubSurf_getEdgeNumFaces(e)) flags |= ME_LOOSEEDGE; if(edgeFlags) { if(edgeIdx != -1) { @@ -1303,7 +1310,7 @@ static void ccgDM_copyFinalFaceArray(DerivedMesh *dm, MFace *mface) totface = ccgSubSurf_getNumFaces(ss); for(index = 0; index < totface; index++) { CCGFace *f = ccgdm->faceMap[index].face; - int x, y, S, numVerts = ccgSubSurf_getFaceNumVerts(ss, f); + int x, y, S, numVerts = ccgSubSurf_getFaceNumVerts(f); int mat_nr = 0; int flag = ME_SMOOTH; /* assume face is smooth by default */ @@ -1350,7 +1357,7 @@ static void ccgdm_getVertCos(DerivedMesh *dm, float (*cos)[3]) { for (; !ccgVertIterator_isStopped(vi); ccgVertIterator_next(vi)) { CCGVert *v = ccgVertIterator_getCurrent(vi); - vertMap2[(int) ccgSubSurf_getVertVertHandle(ss, v)] = v; + vertMap2[GET_INT_FROM_POINTER(ccgSubSurf_getVertVertHandle(v))] = v; } ccgVertIterator_free(vi); @@ -1360,7 +1367,7 @@ static void ccgdm_getVertCos(DerivedMesh *dm, float (*cos)[3]) { for (i=0; !ccgEdgeIterator_isStopped(ei); i++,ccgEdgeIterator_next(ei)) { CCGEdge *e = ccgEdgeIterator_getCurrent(ei); - edgeMap2[(int) ccgSubSurf_getEdgeEdgeHandle(ss, e)] = e; + edgeMap2[GET_INT_FROM_POINTER(ccgSubSurf_getEdgeEdgeHandle(e))] = e; } totface = ccgSubSurf_getNumFaces(ss); @@ -1369,16 +1376,16 @@ static void ccgdm_getVertCos(DerivedMesh *dm, float (*cos)[3]) { for (; !ccgFaceIterator_isStopped(fi); ccgFaceIterator_next(fi)) { CCGFace *f = ccgFaceIterator_getCurrent(fi); - faceMap2[(int) ccgSubSurf_getFaceFaceHandle(ss, f)] = f; + faceMap2[GET_INT_FROM_POINTER(ccgSubSurf_getFaceFaceHandle(ss, f))] = f; } ccgFaceIterator_free(fi); i = 0; for (index=0; index<totface; index++) { CCGFace *f = faceMap2[index]; - int x, y, S, numVerts = ccgSubSurf_getFaceNumVerts(ss, f); + int x, y, S, numVerts = ccgSubSurf_getFaceNumVerts(f); - VecCopyf(cos[i++], ccgSubSurf_getFaceCenterData(ss, f)); + VecCopyf(cos[i++], ccgSubSurf_getFaceCenterData(f)); for (S=0; S<numVerts; S++) { for (x=1; x<gridSize-1; x++) { @@ -1420,7 +1427,7 @@ static void ccgDM_foreachMappedVert(DerivedMesh *dm, void (*func)(void *userData for (; !ccgVertIterator_isStopped(vi); ccgVertIterator_next(vi)) { CCGVert *v = ccgVertIterator_getCurrent(vi); VertData *vd = ccgSubSurf_getVertData(ccgdm->ss, v); - int index = ccgDM_getVertMapIndex(ccgdm, ccgdm->ss, v); + int index = ccgDM_getVertMapIndex(ccgdm->ss, v); if (index!=-1) func(userData, index, vd->co, vd->no, NULL); @@ -1437,7 +1444,7 @@ static void ccgDM_foreachMappedEdge(DerivedMesh *dm, void (*func)(void *userData for (; !ccgEdgeIterator_isStopped(ei); ccgEdgeIterator_next(ei)) { CCGEdge *e = ccgEdgeIterator_getCurrent(ei); VertData *edgeData = ccgSubSurf_getEdgeDataArray(ss, e); - int index = ccgDM_getEdgeMapIndex(ccgdm, ss, e); + int index = ccgDM_getEdgeMapIndex(ss, e); if (index!=-1) { for (i=0; i<edgeSize-1; i++) @@ -1478,9 +1485,9 @@ static void ccgDM_drawVerts(DerivedMesh *dm) { fi = ccgSubSurf_getFaceIterator(ss); for (; !ccgFaceIterator_isStopped(fi); ccgFaceIterator_next(fi)) { CCGFace *f = ccgFaceIterator_getCurrent(fi); - int x, y, S, numVerts = ccgSubSurf_getFaceNumVerts(ss, f); + int x, y, S, numVerts = ccgSubSurf_getFaceNumVerts(f); - glVertex3fv(ccgSubSurf_getFaceCenterData(ss, f)); + glVertex3fv(ccgSubSurf_getFaceCenterData(f)); for (S=0; S<numVerts; S++) for (x=1; x<gridSize-1; x++) glVertex3fv(ccgSubSurf_getFaceGridEdgeData(ss, f, S, x)); @@ -1507,7 +1514,7 @@ static void ccgDM_drawEdges(DerivedMesh *dm, int drawLooseEdges) { CCGEdge *e = ccgEdgeIterator_getCurrent(ei); VertData *edgeData = ccgSubSurf_getEdgeDataArray(ss, e); - if (!drawLooseEdges && !ccgSubSurf_getEdgeNumFaces(ss, e)) + if (!drawLooseEdges && !ccgSubSurf_getEdgeNumFaces(e)) continue; if (useAging && !(G.f&G_BACKBUFSEL)) { @@ -1530,7 +1537,7 @@ static void ccgDM_drawEdges(DerivedMesh *dm, int drawLooseEdges) { if (ccgdm->drawInteriorEdges) { for (; !ccgFaceIterator_isStopped(fi); ccgFaceIterator_next(fi)) { CCGFace *f = ccgFaceIterator_getCurrent(fi); - int S, x, y, numVerts = ccgSubSurf_getFaceNumVerts(ss, f); + int S, x, y, numVerts = ccgSubSurf_getFaceNumVerts(f); for (S=0; S<numVerts; S++) { VertData *faceGridData = ccgSubSurf_getFaceGridDataArray(ss, f, S); @@ -1568,7 +1575,7 @@ static void ccgDM_drawLooseEdges(DerivedMesh *dm) { CCGEdge *e = ccgEdgeIterator_getCurrent(ei); VertData *edgeData = ccgSubSurf_getEdgeDataArray(ss, e); - if (!ccgSubSurf_getEdgeNumFaces(ss, e)) { + if (!ccgSubSurf_getEdgeNumFaces(e)) { glBegin(GL_LINE_STRIP); for (i=0; i<edgeSize-1; i++) { glVertex3fv(edgeData[i].co); @@ -1596,7 +1603,7 @@ static void ccgDM_glNormalFast(float *a, float *b, float *c, float *d) } /* Only used by non-editmesh types */ -static void ccgDM_drawFacesSolid(DerivedMesh *dm, int (*setMaterial)(int)) { +static void ccgDM_drawFacesSolid(DerivedMesh *dm, int (*setMaterial)(int, void *attribs)) { CCGDerivedMesh *ccgdm = (CCGDerivedMesh*) dm; CCGSubSurf *ss = ccgdm->ss; CCGFaceIterator *fi = ccgSubSurf_getFaceIterator(ss); @@ -1605,8 +1612,8 @@ static void ccgDM_drawFacesSolid(DerivedMesh *dm, int (*setMaterial)(int)) { for (; !ccgFaceIterator_isStopped(fi); ccgFaceIterator_next(fi)) { CCGFace *f = ccgFaceIterator_getCurrent(fi); - int S, x, y, numVerts = ccgSubSurf_getFaceNumVerts(ss, f); - int index = (int) ccgSubSurf_getFaceFaceHandle(ss, f); + int S, x, y, numVerts = ccgSubSurf_getFaceNumVerts(f); + int index = GET_INT_FROM_POINTER(ccgSubSurf_getFaceFaceHandle(ss, f)); int drawSmooth, mat_nr; if(faceFlags) { @@ -1618,7 +1625,7 @@ static void ccgDM_drawFacesSolid(DerivedMesh *dm, int (*setMaterial)(int)) { mat_nr= 0; } - if (!setMaterial(mat_nr+1)) + if (!setMaterial(mat_nr+1, NULL)) continue; glShadeModel(drawSmooth? GL_SMOOTH: GL_FLAT); @@ -1663,6 +1670,168 @@ static void ccgDM_drawFacesSolid(DerivedMesh *dm, int (*setMaterial)(int)) { ccgFaceIterator_free(fi); } + + /* Only used by non-editmesh types */ +static void ccgDM_drawMappedFacesGLSL(DerivedMesh *dm, int (*setMaterial)(int, void *attribs), int (*setDrawOptions)(void *userData, int index), void *userData) { + CCGDerivedMesh *ccgdm = (CCGDerivedMesh*) dm; + CCGSubSurf *ss = ccgdm->ss; + CCGFaceIterator *fi = ccgSubSurf_getFaceIterator(ss); + GPUVertexAttribs gattribs; + DMVertexAttribs attribs; + MTFace *tf = dm->getFaceDataArray(dm, CD_MTFACE); + int gridSize = ccgSubSurf_getGridSize(ss); + int gridFaces = gridSize - 1; + int edgeSize = ccgSubSurf_getEdgeSize(ss); + int transp, orig_transp, new_transp; + char *faceFlags = DM_get_face_data_layer(dm, CD_FLAGS); + int a, b, i, doDraw, numVerts, matnr, new_matnr, totface; + + doDraw = 0; + numVerts = 0; + matnr = -1; + transp = GPU_get_material_blend_mode(); + orig_transp = transp; + + memset(&attribs, 0, sizeof(attribs)); + +#define PASSATTRIB(dx, dy, vert) { \ + if(attribs.totorco) { \ + index = getFaceIndex(ss, f, S, x+dx, y+dy, edgeSize, gridSize); \ + glVertexAttrib3fvARB(attribs.orco.glIndex, attribs.orco.array[index]); \ + } \ + for(b = 0; b < attribs.tottface; b++) { \ + MTFace *tf = &attribs.tface[b].array[a]; \ + glVertexAttrib2fvARB(attribs.tface[b].glIndex, tf->uv[vert]); \ + } \ + for(b = 0; b < attribs.totmcol; b++) { \ + MCol *cp = &attribs.mcol[b].array[a*4 + vert]; \ + GLubyte col[4]; \ + col[0]= cp->b; col[1]= cp->g; col[2]= cp->r; col[3]= cp->a; \ + glVertexAttrib4ubvARB(attribs.mcol[b].glIndex, col); \ + } \ + if(attribs.tottang) { \ + float *tang = attribs.tang.array[a*4 + vert]; \ + glVertexAttrib3fvARB(attribs.tang.glIndex, tang); \ + } \ +} + + totface = ccgSubSurf_getNumFaces(ss); + for(a = 0, i = 0; i < totface; i++) { + CCGFace *f = ccgdm->faceMap[i].face; + int S, x, y, drawSmooth; + int index = GET_INT_FROM_POINTER(ccgSubSurf_getFaceFaceHandle(ss, f)); + int origIndex = ccgDM_getFaceMapIndex(ss, f); + + numVerts = ccgSubSurf_getFaceNumVerts(f); + + if(faceFlags) { + drawSmooth = (faceFlags[index*4] & ME_SMOOTH); + new_matnr= faceFlags[index*4 + 1] + 1; + } + else { + drawSmooth = 1; + new_matnr= 1; + } + + if(new_matnr != matnr) { + doDraw = setMaterial(matnr = new_matnr, &gattribs); + if(doDraw) + DM_vertex_attributes_from_gpu(dm, &gattribs, &attribs); + } + + if(!doDraw || (setDrawOptions && !setDrawOptions(userData, origIndex))) { + a += gridFaces*gridFaces*numVerts; + continue; + } + + if(tf) { + new_transp = tf[i].transp; + + if(new_transp != transp) { + if(new_transp == GPU_BLEND_SOLID && orig_transp != GPU_BLEND_SOLID) + GPU_set_material_blend_mode(orig_transp); + else + GPU_set_material_blend_mode(new_transp); + transp = new_transp; + } + } + + glShadeModel(drawSmooth? GL_SMOOTH: GL_FLAT); + for (S=0; S<numVerts; S++) { + VertData *faceGridData = ccgSubSurf_getFaceGridDataArray(ss, f, S); + VertData *vda, *vdb; + + if (drawSmooth) { + for (y=0; y<gridFaces; y++) { + glBegin(GL_QUAD_STRIP); + for (x=0; x<gridFaces; x++) { + vda = &faceGridData[(y+0)*gridSize + x]; + vdb = &faceGridData[(y+1)*gridSize + x]; + + PASSATTRIB(0, 0, 0); + glNormal3fv(vda->no); + glVertex3fv(vda->co); + + PASSATTRIB(0, 1, 1); + glNormal3fv(vdb->no); + glVertex3fv(vdb->co); + + if(x != gridFaces-1) + a++; + } + + vda = &faceGridData[(y+0)*gridSize + x]; + vdb = &faceGridData[(y+1)*gridSize + x]; + + PASSATTRIB(0, 0, 3); + glNormal3fv(vda->no); + glVertex3fv(vda->co); + + PASSATTRIB(0, 1, 2); + glNormal3fv(vdb->no); + glVertex3fv(vdb->co); + + glEnd(); + + a++; + } + } else { + glBegin(GL_QUADS); + for (y=0; y<gridFaces; y++) { + for (x=0; x<gridFaces; x++) { + float *aco = faceGridData[(y+0)*gridSize + x].co; + float *bco = faceGridData[(y+0)*gridSize + x + 1].co; + float *cco = faceGridData[(y+1)*gridSize + x + 1].co; + float *dco = faceGridData[(y+1)*gridSize + x].co; + + ccgDM_glNormalFast(aco, bco, cco, dco); + + PASSATTRIB(0, 1, 1); + glVertex3fv(dco); + PASSATTRIB(1, 1, 2); + glVertex3fv(cco); + PASSATTRIB(1, 0, 3); + glVertex3fv(bco); + PASSATTRIB(0, 0, 0); + glVertex3fv(aco); + + a++; + } + } + glEnd(); + } + } + } + +#undef PASSATTRIB + + ccgFaceIterator_free(fi); +} + +static void ccgDM_drawFacesGLSL(DerivedMesh *dm, int (*setMaterial)(int, void *attribs)) { + dm->drawMappedFacesGLSL(dm, setMaterial, NULL, NULL); +} + static void ccgDM_drawFacesColored(DerivedMesh *dm, int useTwoSided, unsigned char *col1, unsigned char *col2) { CCGDerivedMesh *ccgdm = (CCGDerivedMesh*) dm; CCGSubSurf *ss = ccgdm->ss; @@ -1686,7 +1855,7 @@ static void ccgDM_drawFacesColored(DerivedMesh *dm, int useTwoSided, unsigned ch glBegin(GL_QUADS); for (; !ccgFaceIterator_isStopped(fi); ccgFaceIterator_next(fi)) { CCGFace *f = ccgFaceIterator_getCurrent(fi); - int S, x, y, numVerts = ccgSubSurf_getFaceNumVerts(ss, f); + int S, x, y, numVerts = ccgSubSurf_getFaceNumVerts(f); for (S=0; S<numVerts; S++) { VertData *faceGridData = ccgSubSurf_getFaceGridDataArray(ss, f, S); @@ -1744,9 +1913,9 @@ static void ccgDM_drawFacesTex_common(DerivedMesh *dm, totface = ccgSubSurf_getNumFaces(ss); for(i = 0; i < totface; i++) { CCGFace *f = ccgdm->faceMap[i].face; - int S, x, y, numVerts = ccgSubSurf_getFaceNumVerts(ss, f); - int drawSmooth, index = ccgDM_getFaceMapIndex(ccgdm, ss, f); - int origIndex = (int)ccgSubSurf_getFaceFaceHandle(ss, f); + int S, x, y, numVerts = ccgSubSurf_getFaceNumVerts(f); + int drawSmooth, index = ccgDM_getFaceMapIndex(ss, f); + int origIndex = GET_INT_FROM_POINTER(ccgSubSurf_getFaceFaceHandle(ss, f)); unsigned char *cp= NULL; int mat_nr; @@ -1827,28 +1996,28 @@ static void ccgDM_drawFacesTex_common(DerivedMesh *dm, glBegin(GL_QUADS); for (y=0; y<gridFaces; y++) { for (x=0; x<gridFaces; x++) { - float *a = faceGridData[(y+0)*gridSize + x].co; - float *b = faceGridData[(y+0)*gridSize + x + 1].co; - float *c = faceGridData[(y+1)*gridSize + x + 1].co; - float *d = faceGridData[(y+1)*gridSize + x].co; + float *a_co = faceGridData[(y+0)*gridSize + x].co; + float *b_co = faceGridData[(y+0)*gridSize + x + 1].co; + float *c_co = faceGridData[(y+1)*gridSize + x + 1].co; + float *d_co = faceGridData[(y+1)*gridSize + x].co; - ccgDM_glNormalFast(a, b, c, d); + ccgDM_glNormalFast(a_co, b_co, c_co, d_co); if(tf) glTexCoord2fv(tf->uv[1]); if(cp) glColor3ub(cp[7], cp[6], cp[5]); - glVertex3fv(d); + glVertex3fv(d_co); if(tf) glTexCoord2fv(tf->uv[2]); if(cp) glColor3ub(cp[11], cp[10], cp[9]); - glVertex3fv(c); + glVertex3fv(c_co); if(tf) glTexCoord2fv(tf->uv[3]); if(cp) glColor3ub(cp[15], cp[14], cp[13]); - glVertex3fv(b); + glVertex3fv(b_co); if(tf) glTexCoord2fv(tf->uv[0]); if(cp) glColor3ub(cp[3], cp[2], cp[1]); - glVertex3fv(a); + glVertex3fv(a_co); if(tf) tf++; if(cp) cp += 16; @@ -1904,7 +2073,6 @@ static void ccgDM_drawUVEdges(DerivedMesh *dm) } static void ccgDM_drawMappedFaces(DerivedMesh *dm, int (*setDrawOptions)(void *userData, int index, int *drawSmooth_r), void *userData, int useColors) { - GLubyte act_face_stipple[32*32/8] = DM_FACE_STIPPLE; CCGDerivedMesh *ccgdm = (CCGDerivedMesh*) dm; CCGSubSurf *ss = ccgdm->ss; CCGFaceIterator *fi = ccgSubSurf_getFaceIterator(ss); @@ -1913,11 +2081,11 @@ static void ccgDM_drawMappedFaces(DerivedMesh *dm, int (*setDrawOptions)(void *u for (i=0; !ccgFaceIterator_isStopped(fi); i++,ccgFaceIterator_next(fi)) { CCGFace *f = ccgFaceIterator_getCurrent(fi); - int S, x, y, numVerts = ccgSubSurf_getFaceNumVerts(ss, f); - int drawSmooth, index = ccgDM_getFaceMapIndex(ccgdm, ss, f); + int S, x, y, numVerts = ccgSubSurf_getFaceNumVerts(f); + int drawSmooth, index = ccgDM_getFaceMapIndex(ss, f); int origIndex; - origIndex = (int)ccgSubSurf_getFaceFaceHandle(ss, f); + origIndex = GET_INT_FROM_POINTER(ccgSubSurf_getFaceFaceHandle(ss, f)); if(faceFlags) drawSmooth = (faceFlags[origIndex*4] & ME_SMOOTH); else drawSmooth = 1; @@ -1929,7 +2097,7 @@ static void ccgDM_drawMappedFaces(DerivedMesh *dm, int (*setDrawOptions)(void *u if (draw) { if (draw==2) { glEnable(GL_POLYGON_STIPPLE); - glPolygonStipple(act_face_stipple); + glPolygonStipple(0); //XXX stipple_quarttone); } for (S=0; S<numVerts; S++) { @@ -1995,7 +2163,7 @@ static void ccgDM_drawMappedEdges(DerivedMesh *dm, int (*setDrawOptions)(void *u for (; !ccgEdgeIterator_isStopped(ei); ccgEdgeIterator_next(ei)) { CCGEdge *e = ccgEdgeIterator_getCurrent(ei); VertData *edgeData = ccgSubSurf_getEdgeDataArray(ss, e); - int index = ccgDM_getEdgeMapIndex(ccgdm, ss, e); + int index = ccgDM_getEdgeMapIndex(ss, e); glBegin(GL_LINE_STRIP); if (index!=-1 && (!setDrawOptions || setDrawOptions(userData, index))) { @@ -2025,7 +2193,7 @@ static void ccgDM_drawMappedEdgesInterp(DerivedMesh *dm, int (*setDrawOptions)(v for (; !ccgEdgeIterator_isStopped(ei); ccgEdgeIterator_next(ei)) { CCGEdge *e = ccgEdgeIterator_getCurrent(ei); VertData *edgeData = ccgSubSurf_getEdgeDataArray(ss, e); - int index = ccgDM_getEdgeMapIndex(ccgdm, ss, e); + int index = ccgDM_getEdgeMapIndex(ss, e); glBegin(GL_LINE_STRIP); if (index!=-1 && (!setDrawOptions || setDrawOptions(userData, index))) { @@ -2052,7 +2220,7 @@ static void ccgDM_foreachMappedFaceCenter(DerivedMesh *dm, void (*func)(void *us for (; !ccgFaceIterator_isStopped(fi); ccgFaceIterator_next(fi)) { CCGFace *f = ccgFaceIterator_getCurrent(fi); - int index = ccgDM_getFaceMapIndex(ccgdm, ss, f); + int index = ccgDM_getFaceMapIndex(ss, f); if (index!=-1) { /* Face center data normal isn't updated atm. */ @@ -2088,17 +2256,17 @@ static CCGDerivedMesh *getCCGDerivedMesh(CCGSubSurf *ss, int index, totvert, totedge, totface; int i; int vertNum, edgeNum, faceNum; - int *vertOrigIndex, *edgeOrigIndex, *faceOrigIndex; + int *vertOrigIndex, *faceOrigIndex; /* *edgeOrigIndex - as yet, unused */ int *edgeFlags; char *faceFlags; int edgeSize; int gridSize; int gridFaces; int gridSideVerts; - int gridInternalVerts; + /*int gridInternalVerts; - as yet unused */ int gridSideEdges; int gridInternalEdges; - MVert *mvert = NULL; + /* MVert *mvert = NULL; - as yet unused */ MEdge *medge = NULL; MFace *mface = NULL; FaceVertWeight *qweight, *tweight; @@ -2141,8 +2309,10 @@ static CCGDerivedMesh *getCCGDerivedMesh(CCGSubSurf *ss, ccgdm->dm.drawFacesSolid = ccgDM_drawFacesSolid; ccgdm->dm.drawFacesColored = ccgDM_drawFacesColored; ccgdm->dm.drawFacesTex = ccgDM_drawFacesTex; + ccgdm->dm.drawFacesGLSL = ccgDM_drawFacesGLSL; ccgdm->dm.drawMappedFaces = ccgDM_drawMappedFaces; ccgdm->dm.drawMappedFacesTex = ccgDM_drawMappedFacesTex; + ccgdm->dm.drawMappedFacesGLSL = ccgDM_drawMappedFacesGLSL; ccgdm->dm.drawUVEdges = ccgDM_drawUVEdges; ccgdm->dm.drawMappedEdgesInterp = ccgDM_drawMappedEdgesInterp; @@ -2160,7 +2330,7 @@ static CCGDerivedMesh *getCCGDerivedMesh(CCGSubSurf *ss, for(; !ccgVertIterator_isStopped(vi); ccgVertIterator_next(vi)) { CCGVert *v = ccgVertIterator_getCurrent(vi); - ccgdm->vertMap[(int) ccgSubSurf_getVertVertHandle(ss, v)].vert = v; + ccgdm->vertMap[GET_INT_FROM_POINTER(ccgSubSurf_getVertVertHandle(v))].vert = v; } ccgVertIterator_free(vi); @@ -2170,7 +2340,7 @@ static CCGDerivedMesh *getCCGDerivedMesh(CCGSubSurf *ss, for(; !ccgEdgeIterator_isStopped(ei); ccgEdgeIterator_next(ei)) { CCGEdge *e = ccgEdgeIterator_getCurrent(ei); - ccgdm->edgeMap[(int) ccgSubSurf_getEdgeEdgeHandle(ss, e)].edge = e; + ccgdm->edgeMap[GET_INT_FROM_POINTER(ccgSubSurf_getEdgeEdgeHandle(e))].edge = e; } totface = ccgSubSurf_getNumFaces(ss); @@ -2179,7 +2349,7 @@ static CCGDerivedMesh *getCCGDerivedMesh(CCGSubSurf *ss, for(; !ccgFaceIterator_isStopped(fi); ccgFaceIterator_next(fi)) { CCGFace *f = ccgFaceIterator_getCurrent(fi); - ccgdm->faceMap[(int) ccgSubSurf_getFaceFaceHandle(ss, f)].face = f; + ccgdm->faceMap[GET_INT_FROM_POINTER(ccgSubSurf_getFaceFaceHandle(ss, f))].face = f; } ccgFaceIterator_free(fi); @@ -2187,7 +2357,7 @@ static CCGDerivedMesh *getCCGDerivedMesh(CCGSubSurf *ss, gridSize = ccgSubSurf_getGridSize(ss); gridFaces = gridSize - 1; gridSideVerts = gridSize - 2; - gridInternalVerts = gridSideVerts * gridSideVerts; + /*gridInternalVerts = gridSideVerts * gridSideVerts; - as yet, unused */ gridSideEdges = gridSize - 1; gridInternalEdges = (gridSideEdges - 1) * gridSideEdges * 2; @@ -2197,22 +2367,22 @@ static CCGDerivedMesh *getCCGDerivedMesh(CCGSubSurf *ss, edgeNum = 0; faceNum = 0; - mvert = dm->getVertArray(dm); + /* mvert = dm->getVertArray(dm); - as yet unused */ medge = dm->getEdgeArray(dm); mface = dm->getFaceArray(dm); vertOrigIndex = DM_get_vert_data_layer(&ccgdm->dm, CD_ORIGINDEX); - edgeOrigIndex = DM_get_edge_data_layer(&ccgdm->dm, CD_ORIGINDEX); + /*edgeOrigIndex = DM_get_edge_data_layer(&ccgdm->dm, CD_ORIGINDEX);*/ faceOrigIndex = DM_get_face_data_layer(&ccgdm->dm, CD_ORIGINDEX); faceFlags = DM_get_face_data_layer(&ccgdm->dm, CD_FLAGS); for(index = 0; index < totface; ++index) { CCGFace *f = ccgdm->faceMap[index].face; - int numVerts = ccgSubSurf_getFaceNumVerts(ss, f); + int numVerts = ccgSubSurf_getFaceNumVerts(f); int numFinalEdges = numVerts * (gridSideEdges + gridInternalEdges); - int mapIndex = ccgDM_getFaceMapIndex(ccgdm, ss, f); - int origIndex = (int)ccgSubSurf_getFaceFaceHandle(ss, f); + int mapIndex = ccgDM_getFaceMapIndex(ss, f); + int origIndex = GET_INT_FROM_POINTER(ccgSubSurf_getFaceFaceHandle(ss, f)); FaceVertWeight *weight = (numVerts == 4) ? qweight : tweight; int S, x, y; int vertIdx[4]; @@ -2227,7 +2397,7 @@ static CCGDerivedMesh *getCCGDerivedMesh(CCGSubSurf *ss, for(S = 0; S < numVerts; S++) { CCGVert *v = ccgSubSurf_getFaceVert(ss, f, S); - vertIdx[S] = (int)ccgSubSurf_getVertVertHandle(ss, v); + vertIdx[S] = GET_INT_FROM_POINTER(ccgSubSurf_getVertVertHandle(v)); } DM_interp_vert_data(dm, &ccgdm->dm, vertIdx, weight[0][0], @@ -2330,16 +2500,16 @@ static CCGDerivedMesh *getCCGDerivedMesh(CCGSubSurf *ss, for(index = 0; index < totedge; ++index) { CCGEdge *e = ccgdm->edgeMap[index].edge; int numFinalEdges = edgeSize - 1; - int mapIndex = ccgDM_getEdgeMapIndex(ccgdm, ss, e); + int mapIndex = ccgDM_getEdgeMapIndex(ss, e); int x; int vertIdx[2]; - int edgeIdx = (int)ccgSubSurf_getEdgeEdgeHandle(ss, e); + int edgeIdx = GET_INT_FROM_POINTER(ccgSubSurf_getEdgeEdgeHandle(e)); CCGVert *v; - v = ccgSubSurf_getEdgeVert0(ss, e); - vertIdx[0] = (int)ccgSubSurf_getVertVertHandle(ss, v); - v = ccgSubSurf_getEdgeVert1(ss, e); - vertIdx[1] = (int)ccgSubSurf_getVertVertHandle(ss, v); + v = ccgSubSurf_getEdgeVert0(e); + vertIdx[0] = GET_INT_FROM_POINTER(ccgSubSurf_getVertVertHandle(v)); + v = ccgSubSurf_getEdgeVert1(e); + vertIdx[1] = GET_INT_FROM_POINTER(ccgSubSurf_getVertVertHandle(v)); ccgdm->edgeMap[index].startVert = vertNum; ccgdm->edgeMap[index].startEdge = edgeNum; @@ -2370,10 +2540,10 @@ static CCGDerivedMesh *getCCGDerivedMesh(CCGSubSurf *ss, for(index = 0; index < totvert; ++index) { CCGVert *v = ccgdm->vertMap[index].vert; - int mapIndex = ccgDM_getVertMapIndex(ccgdm, ccgdm->ss, v); + int mapIndex = ccgDM_getVertMapIndex(ccgdm->ss, v); int vertIdx; - vertIdx = (int)ccgSubSurf_getVertVertHandle(ss, v); + vertIdx = GET_INT_FROM_POINTER(ccgSubSurf_getVertVertHandle(v)); ccgdm->vertMap[index].startVert = vertNum; @@ -2417,7 +2587,14 @@ struct DerivedMesh *subsurf_make_derived_from_derived( useSubsurfUv, dm); } else if(useRenderParams) { /* Do not use cache in render mode. */ - CCGSubSurf *ss = _getSubSurf(NULL, smd->renderLevels, 0, 1, useSimple); + CCGSubSurf *ss; + int levels; + + levels= get_render_subsurf_level(&G.scene->r, smd->renderLevels); + if(levels == 0) + return dm; + + ss = _getSubSurf(NULL, levels, 0, 1, useSimple); ss_sync_from_derivedmesh(ss, dm, vertCos, useSimple); @@ -2499,9 +2676,9 @@ void subsurf_calculate_limit_positions(Mesh *me, float (*positions_r)[3]) vi = ccgSubSurf_getVertIterator(ss); for (; !ccgVertIterator_isStopped(vi); ccgVertIterator_next(vi)) { CCGVert *v = ccgVertIterator_getCurrent(vi); - int idx = (int) ccgSubSurf_getVertVertHandle(ss, v); - int N = ccgSubSurf_getVertNumEdges(ss, v); - int numFaces = ccgSubSurf_getVertNumFaces(ss, v); + int idx = GET_INT_FROM_POINTER(ccgSubSurf_getVertVertHandle(v)); + int N = ccgSubSurf_getVertNumEdges(v); + int numFaces = ccgSubSurf_getVertNumFaces(v); float *co; int i; @@ -2509,12 +2686,12 @@ void subsurf_calculate_limit_positions(Mesh *me, float (*positions_r)[3]) face_sum[0]= face_sum[1]= face_sum[2]= 0.0; for (i=0; i<N; i++) { - CCGEdge *e = ccgSubSurf_getVertEdge(ss, v, i); + CCGEdge *e = ccgSubSurf_getVertEdge(v, i); VecAddf(edge_sum, edge_sum, ccgSubSurf_getEdgeData(ss, e, 1)); } for (i=0; i<numFaces; i++) { - CCGFace *f = ccgSubSurf_getVertFace(ss, v, i); - VecAddf(face_sum, face_sum, ccgSubSurf_getFaceCenterData(ss, f)); + CCGFace *f = ccgSubSurf_getVertFace(v, i); + VecAddf(face_sum, face_sum, ccgSubSurf_getFaceCenterData(f)); } /* ad-hoc correction for boundary vertices, to at least avoid them diff --git a/source/blender/blenkernel/intern/suggestions.c b/source/blender/blenkernel/intern/suggestions.c new file mode 100644 index 00000000000..6530909336c --- /dev/null +++ b/source/blender/blenkernel/intern/suggestions.c @@ -0,0 +1,254 @@ +/** + * $Id: $ + * + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * The Original Code is Copyright (C) 2008, Blender Foundation + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): Ian Thompson. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#include <stdlib.h> +#include <string.h> +#include <ctype.h> + +#include "MEM_guardedalloc.h" +#include "BLI_blenlib.h" +#include "DNA_text_types.h" +#include "BKE_text.h" +#include "BKE_suggestions.h" + +/**********************/ +/* Static definitions */ +/**********************/ + +static Text *activeToolText = NULL; +static SuggList suggestions = {NULL, NULL, NULL, NULL, NULL}; +static char *documentation = NULL; +//static int doc_lines = 0; + +static int txttl_cmp(const char *first, const char *second, int len) { + int cmp, i; + for (cmp=0, i=0; i<len; i++) { + if ( (cmp= toupper(first[i])-toupper(second[i])) ) { + break; + } + } + return cmp; +} + +static void txttl_free_suggest() { + SuggItem *item, *prev; + for (item = suggestions.last; item; item=prev) { + prev = item->prev; + MEM_freeN(item); + } + suggestions.first = suggestions.last = NULL; + suggestions.firstmatch = suggestions.lastmatch = NULL; + suggestions.selected = NULL; + suggestions.top = 0; +} + +static void txttl_free_docs() { + if (documentation) { + MEM_freeN(documentation); + documentation = NULL; + } +} + +/**************************/ +/* General tool functions */ +/**************************/ + +void free_texttools() { + txttl_free_suggest(); + txttl_free_docs(); +} + +void texttool_text_set_active(Text *text) { + if (activeToolText == text) return; + texttool_text_clear(); + activeToolText = text; +} + +void texttool_text_clear() { + free_texttools(); + activeToolText = NULL; +} + +short texttool_text_is_active(Text *text) { + return activeToolText==text ? 1 : 0; +} + +/***************************/ +/* Suggestion list methods */ +/***************************/ + +void texttool_suggest_add(const char *name, char type) { + SuggItem *newitem, *item; + int len, cmp; + + newitem = MEM_mallocN(sizeof(SuggItem) + strlen(name) + 1, "SuggestionItem"); + if (!newitem) { + printf("Failed to allocate memory for suggestion.\n"); + return; + } + + newitem->name = (char *) (newitem + 1); + len = strlen(name); + strncpy(newitem->name, name, len); + newitem->name[len] = '\0'; + newitem->type = type; + newitem->prev = newitem->next = NULL; + + /* Perform simple linear search for ordered storage */ + if (!suggestions.first || !suggestions.last) { + suggestions.first = suggestions.last = newitem; + } else { + cmp = -1; + for (item=suggestions.last; item; item=item->prev) { + cmp = txttl_cmp(name, item->name, len); + + /* Newitem comes after this item, insert here */ + if (cmp >= 0) { + newitem->prev = item; + if (item->next) + item->next->prev = newitem; + newitem->next = item->next; + item->next = newitem; + + /* At last item, set last pointer here */ + if (item == suggestions.last) + suggestions.last = newitem; + break; + } + } + /* Reached beginning of list, insert before first */ + if (cmp < 0) { + newitem->next = suggestions.first; + suggestions.first->prev = newitem; + suggestions.first = newitem; + } + } + suggestions.firstmatch = suggestions.lastmatch = suggestions.selected = NULL; + suggestions.top= 0; +} + +void texttool_suggest_prefix(const char *prefix) { + SuggItem *match, *first, *last; + int cmp, len = strlen(prefix), top = 0; + + if (!suggestions.first) return; + if (len==0) { + suggestions.selected = suggestions.firstmatch = suggestions.first; + suggestions.lastmatch = suggestions.last; + return; + } + + first = last = NULL; + for (match=suggestions.first; match; match=match->next) { + cmp = txttl_cmp(prefix, match->name, len); + if (cmp==0) { + if (!first) { + first = match; + suggestions.top = top; + } + } else if (cmp<0) { + if (!last) { + last = match->prev; + break; + } + } + top++; + } + if (first) { + if (!last) last = suggestions.last; + suggestions.firstmatch = first; + suggestions.lastmatch = last; + suggestions.selected = first; + } else { + suggestions.firstmatch = NULL; + suggestions.lastmatch = NULL; + suggestions.selected = NULL; + suggestions.top = 0; + } +} + +void texttool_suggest_clear() { + txttl_free_suggest(); +} + +SuggItem *texttool_suggest_first() { + return suggestions.firstmatch; +} + +SuggItem *texttool_suggest_last() { + return suggestions.lastmatch; +} + +void texttool_suggest_select(SuggItem *sel) { + suggestions.selected = sel; +} + +SuggItem *texttool_suggest_selected() { + return suggestions.selected; +} + +int *texttool_suggest_top() { + return &suggestions.top; +} + +/*************************/ +/* Documentation methods */ +/*************************/ + +void texttool_docs_show(const char *docs) { + int len; + + if (!docs) return; + + len = strlen(docs); + + if (documentation) { + MEM_freeN(documentation); + documentation = NULL; + } + + /* Ensure documentation ends with a '\n' */ + if (docs[len-1] != '\n') { + documentation = MEM_mallocN(len+2, "Documentation"); + strncpy(documentation, docs, len); + documentation[len++] = '\n'; + } else { + documentation = MEM_mallocN(len+1, "Documentation"); + strncpy(documentation, docs, len); + } + documentation[len] = '\0'; +} + +char *texttool_docs_get() { + return documentation; +} + +void texttool_docs_clear() { + txttl_free_docs(); +} diff --git a/source/blender/blenkernel/intern/text.c b/source/blender/blenkernel/intern/text.c index 0dbaa9d3843..d1d252ab210 100644 --- a/source/blender/blenkernel/intern/text.c +++ b/source/blender/blenkernel/intern/text.c @@ -30,6 +30,8 @@ */ #include <string.h> /* strstr */ +#include <sys/types.h> +#include <sys/stat.h> #include "MEM_guardedalloc.h" @@ -44,7 +46,9 @@ #include "BKE_global.h" #include "BKE_main.h" +#ifndef DISABLE_PYTHON #include "BPY_extern.h" +#endif #ifdef HAVE_CONFIG_H #include <config.h> @@ -80,12 +84,19 @@ The st->top determines at what line the top of the text is displayed. If the user moves the cursor the st containing that cursor should be popped ... other st's retain their own top location. -*/ /***************/ - +Markers +-- +The mrk->flags define the behaviour and relationships between markers. The +upper two bytes are used to hold a group ID, the lower two are normal flags. If +TMARK_EDITALL is set the group ID defines which other markers should be edited. -/****************/ /* - Undo +The mrk->clr field is used to visually group markers where the flags may not +match. A template system, for example, may allow editing of repeating tokens +(in one group) but include other marked positions (in another group) all in the +same template with the same colour. +Undo +-- Undo/Redo works by storing events in a queue, and a pointer to the current position in the @@ -144,10 +155,13 @@ void free_text(Text *text) } BLI_freelistN(&text->lines); + BLI_freelistN(&text->markers); if(text->name) MEM_freeN(text->name); MEM_freeN(text->undo_buf); +#ifndef DISABLE_PYTHON if (text->compiled) BPY_free_compiled_text(text); +#endif } Text *add_empty_text(char *name) @@ -168,10 +182,11 @@ Text *add_empty_text(char *name) ta->flags= TXT_ISDIRTY | TXT_ISTMP | TXT_ISMEM; ta->lines.first= ta->lines.last= NULL; + ta->markers.first= ta->markers.last= NULL; tmp= (TextLine*) MEM_mallocN(sizeof(TextLine), "textline"); tmp->line= (char*) MEM_mallocN(1, "textline_string"); - tmp->format= (char*) MEM_mallocN(2, "Syntax_format"); + tmp->format= NULL; tmp->line[0]=0; tmp->len= 0; @@ -208,18 +223,18 @@ static void cleanup_textline(TextLine * tl) int reopen_text(Text *text) { FILE *fp; - int i, llen, len; + int i, llen, len, res; unsigned char *buffer; TextLine *tmp; - char sdir[FILE_MAXDIR]; char sfile[FILE_MAXFILE]; char str[FILE_MAXDIR+FILE_MAXFILE]; + struct stat st; if (!text || !text->name) return 0; BLI_strncpy(str, text->name, FILE_MAXDIR+FILE_MAXFILE); - BLI_convertstringcode(str, G.sce, G.scene->r.cfra); - BLI_split_dirfile(str, sdir, sfile); + BLI_convertstringcode(str, G.sce); + BLI_split_dirfile_basic(str, NULL, sfile); fp= fopen(str, "r"); if(fp==NULL) return 0; @@ -242,7 +257,7 @@ int reopen_text(Text *text) text->undo_len= TXT_INIT_UNDO; text->undo_buf= MEM_mallocN(text->undo_len, "undo buf"); - text->flags= TXT_ISDIRTY | TXT_ISTMP; + text->flags= TXT_ISTMP; fseek(fp, 0L, SEEK_END); len= ftell(fp); @@ -256,6 +271,9 @@ int reopen_text(Text *text) len = fread(buffer, 1, len, fp); fclose(fp); + + res= stat(str, &st); + text->mtime= st.st_mtime; text->nlines=0; i=0; @@ -264,7 +282,7 @@ int reopen_text(Text *text) if (buffer[i]=='\n') { tmp= (TextLine*) MEM_mallocN(sizeof(TextLine), "textline"); tmp->line= (char*) MEM_mallocN(llen+1, "textline_string"); - tmp->format= (char*) MEM_mallocN(llen+2, "Syntax_format"); + tmp->format= NULL; if(llen) memcpy(tmp->line, &buffer[i-llen], llen); tmp->line[llen]=0; @@ -284,7 +302,7 @@ int reopen_text(Text *text) if (llen!=0 || text->nlines==0) { tmp= (TextLine*) MEM_mallocN(sizeof(TextLine), "textline"); tmp->line= (char*) MEM_mallocN(llen+1, "textline_string"); - tmp->format= (char*) MEM_mallocN(llen+2, "Syntax_format"); + tmp->format= NULL; if(llen) memcpy(tmp->line, &buffer[i-llen], llen); @@ -307,18 +325,18 @@ int reopen_text(Text *text) Text *add_text(char *file) { FILE *fp; - int i, llen, len; + int i, llen, len, res; unsigned char *buffer; TextLine *tmp; Text *ta; - char sdir[FILE_MAXDIR]; char sfile[FILE_MAXFILE]; char str[FILE_MAXDIR+FILE_MAXFILE]; + struct stat st; BLI_strncpy(str, file, FILE_MAXDIR+FILE_MAXFILE); if (G.scene) /* can be NULL (bg mode) */ - BLI_convertstringcode(str, G.sce, G.scene->r.cfra); - BLI_split_dirfile(str, sdir, sfile); + BLI_convertstringcode(str, G.sce); + BLI_split_dirfile_basic(str, NULL, sfile); fp= fopen(str, "r"); if(fp==NULL) return NULL; @@ -327,6 +345,7 @@ Text *add_text(char *file) ta->id.us= 1; ta->lines.first= ta->lines.last= NULL; + ta->markers.first= ta->markers.last= NULL; ta->curl= ta->sell= NULL; /* ta->flags= TXT_ISTMP | TXT_ISEXT; */ @@ -349,6 +368,9 @@ Text *add_text(char *file) len = fread(buffer, 1, len, fp); fclose(fp); + + res= stat(str, &st); + ta->mtime= st.st_mtime; ta->nlines=0; i=0; @@ -357,7 +379,7 @@ Text *add_text(char *file) if (buffer[i]=='\n') { tmp= (TextLine*) MEM_mallocN(sizeof(TextLine), "textline"); tmp->line= (char*) MEM_mallocN(llen+1, "textline_string"); - tmp->format= (char*) MEM_mallocN(llen+2, "Syntax_format"); + tmp->format= NULL; if(llen) memcpy(tmp->line, &buffer[i-llen], llen); tmp->line[llen]=0; @@ -377,7 +399,7 @@ Text *add_text(char *file) if (llen!=0 || ta->nlines==0) { tmp= (TextLine*) MEM_mallocN(sizeof(TextLine), "textline"); tmp->line= (char*) MEM_mallocN(llen+1, "textline_string"); - tmp->format= (char*) MEM_mallocN(llen+2, "Syntax_format"); + tmp->format= NULL; if(llen) memcpy(tmp->line, &buffer[i-llen], llen); @@ -411,6 +433,7 @@ Text *copy_text(Text *ta) tan->flags = ta->flags | TXT_ISDIRTY | TXT_ISTMP; tan->lines.first= tan->lines.last= NULL; + tan->markers.first= tan->markers.last= NULL; tan->curl= tan->sell= NULL; tan->nlines= ta->nlines; @@ -420,7 +443,7 @@ Text *copy_text(Text *ta) while (line) { tmp= (TextLine*) MEM_mallocN(sizeof(TextLine), "textline"); tmp->line= MEM_mallocN(line->len+1, "textline_string"); - tmp->format= MEM_mallocN(line->len+2, "Syntax_format"); + tmp->format= NULL; strcpy(tmp->line, line->line); @@ -441,14 +464,14 @@ Text *copy_text(Text *ta) /* Editing utility functions */ /*****************************/ -static void make_new_line (TextLine *line, char *newline, char *newformat) +static void make_new_line (TextLine *line, char *newline) { if (line->line) MEM_freeN(line->line); if (line->format) MEM_freeN(line->format); line->line= newline; line->len= strlen(newline); - line->format= newformat; + line->format= NULL; } static TextLine *txt_new_line(char *str) @@ -459,7 +482,7 @@ static TextLine *txt_new_line(char *str) tmp= (TextLine *) MEM_mallocN(sizeof(TextLine), "textline"); tmp->line= MEM_mallocN(strlen(str)+1, "textline_string"); - tmp->format= MEM_mallocN(strlen(str)+2, "Syntax_format"); + tmp->format= NULL; strcpy(tmp->line, str); @@ -477,7 +500,7 @@ static TextLine *txt_new_linen(char *str, int n) tmp= (TextLine *) MEM_mallocN(sizeof(TextLine), "textline"); tmp->line= MEM_mallocN(n+1, "textline_string"); - tmp->format= MEM_mallocN(n+2, "Syntax_format"); + tmp->format= NULL; BLI_strncpy(tmp->line, str, n+1); @@ -551,7 +574,23 @@ int txt_get_span (TextLine *from, TextLine *to) static void txt_make_dirty (Text *text) { text->flags |= TXT_ISDIRTY; +#ifndef DISABLE_PYTHON if (text->compiled) BPY_free_compiled_text(text); +#endif +} + +/* 0:whitespace, 1:punct, 2:alphanumeric */ +static short txt_char_type (char ch) +{ + if (ch <= ' ') return 0; /* 32 */ + if (ch <= '/') return 1; /* 47 */ + if (ch <= '9') return 2; /* 57 */ + if (ch <= '@') return 1; /* 64 */ + if (ch <= 'Z') return 2; /* 90 */ + if (ch == '_') return 2; /* 95, dont delimit '_' */ + if (ch <= '`') return 1; /* 96 */ + if (ch <= 'z') return 2; /* 122 */ + return 1; } /****************************/ @@ -607,8 +646,7 @@ void txt_move_up(Text *text, short sel) if(!undoing) txt_undo_add_op(text, sel?UNDO_SUP:UNDO_CUP); } } else { - *charp= 0; - if(!undoing) txt_undo_add_op(text, sel?UNDO_SUP:UNDO_CUP); + txt_move_bol(text, sel); } if(!sel) txt_pop_sel(text); @@ -633,8 +671,7 @@ void txt_move_down(Text *text, short sel) } else if(!undoing) txt_undo_add_op(text, sel?UNDO_SDOWN:UNDO_CDOWN); } else { - *charp= (*linep)->len; - if(!undoing) txt_undo_add_op(text, sel?UNDO_SDOWN:UNDO_CDOWN); + txt_move_eol(text, sel); } if(!sel) txt_pop_sel(text); @@ -690,6 +727,68 @@ void txt_move_right(Text *text, short sel) if(!sel) txt_pop_sel(text); } +void txt_jump_left(Text *text, short sel) +{ + TextLine **linep, *oldl; + int *charp, oldc, count, i; + unsigned char oldu; + + if (!text) return; + if(sel) txt_curs_sel(text, &linep, &charp); + else { txt_pop_first(text); txt_curs_cur(text, &linep, &charp); } + if (!*linep) return; + + oldl= *linep; + oldc= *charp; + oldu= undoing; + undoing= 1; /* Don't push individual moves to undo stack */ + + count= 0; + for (i=0; i<3; i++) { + if (count < 2) { + while (*charp>0 && txt_char_type((*linep)->line[*charp-1])==i) { + txt_move_left(text, sel); + count++; + } + } + } + if (count==0) txt_move_left(text, sel); + + undoing= oldu; + if(!undoing) txt_undo_add_toop(text, sel?UNDO_STO:UNDO_CTO, txt_get_span(text->lines.first, oldl), oldc, txt_get_span(text->lines.first, *linep), (unsigned short)*charp); +} + +void txt_jump_right(Text *text, short sel) +{ + TextLine **linep, *oldl; + int *charp, oldc, count, i; + unsigned char oldu; + + if (!text) return; + if(sel) txt_curs_sel(text, &linep, &charp); + else { txt_pop_last(text); txt_curs_cur(text, &linep, &charp); } + if (!*linep) return; + + oldl= *linep; + oldc= *charp; + oldu= undoing; + undoing= 1; /* Don't push individual moves to undo stack */ + + count= 0; + for (i=0; i<3; i++) { + if (count < 2) { + while (*charp<(*linep)->len && txt_char_type((*linep)->line[*charp])==i) { + txt_move_right(text, sel); + count++; + } + } + } + if (count==0) txt_move_right(text, sel); + + undoing= oldu; + if(!undoing) txt_undo_add_toop(text, sel?UNDO_STO:UNDO_CTO, txt_get_span(text->lines.first, oldl), oldc, txt_get_span(text->lines.first, *linep), (unsigned short)*charp); +} + void txt_move_bol (Text *text, short sel) { TextLine **linep; @@ -762,6 +861,11 @@ void txt_move_eof (Text *text, short sel) void txt_move_toline (Text *text, unsigned int line, short sel) { + txt_move_to(text, line, 0, sel); +} + +void txt_move_to (Text *text, unsigned int line, unsigned int ch, short sel) +{ TextLine **linep, *oldl; int *charp, oldc; unsigned int i; @@ -778,10 +882,12 @@ void txt_move_toline (Text *text, unsigned int line, short sel) if ((*linep)->next) *linep= (*linep)->next; else break; } - *charp= 0; + if (ch>(*linep)->len) + ch= (*linep)->len; + *charp= ch; if(!sel) txt_pop_sel(text); - if(!undoing) txt_undo_add_toop(text, sel?UNDO_STO:UNDO_CTO, txt_get_span(text->lines.first, oldl), oldc, txt_get_span(text->lines.first, *linep), (unsigned short)*charp); + if(!undoing) txt_undo_add_toop(text, sel?UNDO_STO:UNDO_CTO, txt_get_span(text->lines.first, oldl), oldc, txt_get_span(text->lines.first, *linep), (unsigned short)*charp); } /****************************/ @@ -866,7 +972,9 @@ int txt_has_sel(Text *text) static void txt_delete_sel (Text *text) { TextLine *tmpl; - char *buf, *format; + TextMarker *mrk; + char *buf; + int move, lineno; if (!text) return; if (!text->curl) return; @@ -883,13 +991,33 @@ static void txt_delete_sel (Text *text) } buf= MEM_mallocN(text->curc+(text->sell->len - text->selc)+1, "textline_string"); - format= MEM_mallocN(text->curc+(text->sell->len - text->selc)+2, "Syntax_format"); + if (text->curl != text->sell) { + txt_clear_marker_region(text, text->curl, text->curc, text->curl->len, 0, 0); + move= txt_get_span(text->curl, text->sell); + } else { + mrk= txt_find_marker_region(text, text->curl, text->curc, text->selc, 0, 0); + if (mrk && (mrk->start > text->curc || mrk->end < text->selc)) + txt_clear_marker_region(text, text->curl, text->curc, text->selc, 0, 0); + move= 0; + } + + mrk= txt_find_marker_region(text, text->sell, text->selc-1, text->sell->len, 0, 0); + if (mrk) { + lineno= mrk->lineno; + do { + mrk->lineno -= move; + if (mrk->start > text->curc) mrk->start -= text->selc - text->curc; + mrk->end -= text->selc - text->curc; + mrk= mrk->next; + } while (mrk && mrk->lineno==lineno); + } + strncpy(buf, text->curl->line, text->curc); strcpy(buf+text->curc, text->sell->line + text->selc); buf[text->curc+(text->sell->len - text->selc)]=0; - make_new_line(text->curl, buf, format); + make_new_line(text->curl, buf); tmpl= text->sell; while (tmpl != text->curl) { @@ -943,7 +1071,8 @@ char *txt_to_buf (Text *text) if (!text) return NULL; if (!text->curl) return NULL; if (!text->sell) return NULL; - + if (!text->lines.first) return NULL; + linef= text->lines.first; charf= 0; @@ -995,22 +1124,31 @@ char *txt_to_buf (Text *text) return buf; } -int txt_find_string(Text *text, char *findstr) +int txt_find_string(Text *text, char *findstr, int wrap) { TextLine *tl, *startl; char *s= NULL; + int oldcl, oldsl, oldcc, oldsc; if (!text || !text->curl || !text->sell) return 0; txt_order_cursors(text); + oldcl= txt_get_span(text->lines.first, text->curl); + oldsl= txt_get_span(text->lines.first, text->sell); tl= startl= text->sell; + oldcc= text->curc; + oldsc= text->selc; s= strstr(&tl->line[text->selc], findstr); while (!s) { tl= tl->next; - if (!tl) - tl= text->lines.first; + if (!tl) { + if (wrap) + tl= text->lines.first; + else + break; + } s= strstr(tl->line, findstr); if (tl==startl) @@ -1018,10 +1156,10 @@ int txt_find_string(Text *text, char *findstr) } if (s) { - text->curl= text->sell= tl; - text->curc= (int) (s-tl->line); - text->selc= text->curc + strlen(findstr); - + int newl= txt_get_span(text->lines.first, tl); + int newc= (int)(s-tl->line); + txt_move_to(text, newl, newc, 0); + txt_move_to(text, newl, newc + strlen(findstr), 1); return 1; } else return 0; @@ -1029,7 +1167,8 @@ int txt_find_string(Text *text, char *findstr) void txt_cut_sel (Text *text) { - txt_copy_sel(text); + if (!G.background) /* Python uses txt_cut_sel, which it should not, working around for now */ + ; //XXX txt_copy_clipboard(text); txt_delete_sel(text); txt_make_dirty(text); @@ -1620,7 +1759,6 @@ void txt_do_undo(Text *text) case UNDO_SWAP: txt_curs_swap(text); - txt_do_undo(text); /* swaps should appear transparent */ break; case UNDO_DBLOCK: @@ -1735,6 +1873,19 @@ void txt_do_undo(Text *text) break; } + + /* next undo step may need evaluating */ + if (text->undo_pos>=0) { + switch (text->undo_buf[text->undo_pos]) { + case UNDO_STO: + txt_do_undo(text); + txt_do_redo(text); /* selections need restoring */ + break; + case UNDO_SWAP: + txt_do_undo(text); /* swaps should appear transparent */ + break; + } + } undoing= 0; } @@ -1809,7 +1960,7 @@ void txt_do_redo(Text *text) case UNDO_SWAP: txt_curs_swap(text); - txt_do_undo(text); /* swaps should appear transparent a*/ + txt_do_redo(text); /* swaps should appear transparent a*/ break; case UNDO_CTO: @@ -1946,22 +2097,37 @@ void txt_do_redo(Text *text) void txt_split_curline (Text *text) { TextLine *ins; - char *left, *right, *fleft, *fright; + TextMarker *mrk; + char *left, *right; + int lineno= -1; if (!text) return; if (!text->curl) return; - txt_delete_sel(text); + txt_delete_sel(text); + + /* Move markers */ + + lineno= txt_get_span(text->lines.first, text->curl); + mrk= text->markers.first; + while (mrk) { + if (mrk->lineno==lineno && mrk->start>text->curc) { + mrk->lineno++; + mrk->start -= text->curc; + mrk->end -= text->curc; + } else if (mrk->lineno > lineno) { + mrk->lineno++; + } + mrk= mrk->next; + } /* Make the two half strings */ left= MEM_mallocN(text->curc+1, "textline_string"); - fleft= MEM_mallocN(text->curc+2, "Syntax_format"); if (text->curc) memcpy(left, text->curl->line, text->curc); left[text->curc]=0; right= MEM_mallocN(text->curl->len - text->curc+1, "textline_string"); - fright= MEM_mallocN(text->curl->len - text->curc+2, "Syntax_format"); if (text->curl->len - text->curc) memcpy(right, text->curl->line+text->curc, text->curl->len-text->curc); right[text->curl->len - text->curc]=0; @@ -1972,11 +2138,11 @@ void txt_split_curline (Text *text) ins= MEM_mallocN(sizeof(TextLine), "textline"); ins->line= left; - ins->format= fleft; + ins->format= NULL; ins->len= text->curc; text->curl->line= right; - text->curl->format= fright; + text->curl->format= NULL; text->curl->len= text->curl->len - text->curc; BLI_insertlinkbefore(&text->lines, text->curl, ins); @@ -1992,9 +2158,23 @@ void txt_split_curline (Text *text) static void txt_delete_line (Text *text, TextLine *line) { + TextMarker *mrk=NULL, *nxt; + int lineno= -1; + if (!text) return; if (!text->curl) return; + lineno= txt_get_span(text->lines.first, line); + mrk= text->markers.first; + while (mrk) { + nxt= mrk->next; + if (mrk->lineno==lineno) + BLI_freelinkN(&text->markers, mrk); + else if (mrk->lineno > lineno) + mrk->lineno--; + mrk= nxt; + } + BLI_remlink (&text->lines, line); if (line->line) MEM_freeN(line->line); @@ -2008,21 +2188,35 @@ static void txt_delete_line (Text *text, TextLine *line) static void txt_combine_lines (Text *text, TextLine *linea, TextLine *lineb) { - char *tmp, *format; + char *tmp; + TextMarker *mrk= NULL; + int lineno=-1; if (!text) return; if(!linea || !lineb) return; + + mrk= txt_find_marker_region(text, lineb, 0, lineb->len, 0, 0); + if (mrk) { + lineno= mrk->lineno; + do { + mrk->lineno--; + mrk->start += linea->len; + mrk->end += linea->len; + mrk= mrk->next; + } while (mrk && mrk->lineno==lineno); + } + if (lineno==-1) lineno= txt_get_span(text->lines.first, lineb); + if (!mrk) mrk= text->markers.first; tmp= MEM_mallocN(linea->len+lineb->len+1, "textline_string"); - format= MEM_mallocN(linea->len+lineb->len+1, "Syntax_format"); strcpy(tmp, linea->line); strcat(tmp, lineb->line); - make_new_line(linea, tmp, format); + make_new_line(linea, tmp); - txt_delete_line(text, lineb); + txt_delete_line(text, lineb); txt_make_dirty(text); txt_clean_text(text); @@ -2036,7 +2230,9 @@ void txt_delete_char (Text *text) if (!text->curl) return; if (txt_has_sel(text)) { /* deleting a selection */ - txt_delete_sel(text); + txt_delete_sel(text); + txt_make_dirty(text); + return; } else if (text->curc== text->curl->len) { /* Appending two lines */ if (text->curl->next) { @@ -2045,6 +2241,24 @@ void txt_delete_char (Text *text) } } else { /* Just deleting a char */ int i= text->curc; + + TextMarker *mrk= txt_find_marker_region(text, text->curl, i-1, text->curl->len, 0, 0); + if (mrk) { + int lineno= mrk->lineno; + if (mrk->end==i) { + if ((mrk->flags & TMARK_TEMP) && !(mrk->flags & TMARK_EDITALL)) { + txt_clear_markers(text, mrk->group, TMARK_TEMP); + } else { + BLI_freelinkN(&text->markers, mrk); + } + return; + } + do { + if (mrk->start>i) mrk->start--; + mrk->end--; + mrk= mrk->next; + } while (mrk && mrk->lineno==lineno); + } c= text->curl->line[i]; while(i< text->curl->len) { @@ -2062,6 +2276,12 @@ void txt_delete_char (Text *text) if(!undoing) txt_undo_add_charop(text, UNDO_DEL, c); } +void txt_delete_word (Text *text) +{ + txt_jump_right(text, 1); + txt_delete_sel(text); +} + void txt_backspace_char (Text *text) { char c='\n'; @@ -2070,29 +2290,49 @@ void txt_backspace_char (Text *text) if (!text->curl) return; if (txt_has_sel(text)) { /* deleting a selection */ - txt_delete_sel(text); + txt_delete_sel(text); + txt_make_dirty(text); + return; } else if (text->curc==0) { /* Appending two lines */ - if (text->curl->prev) { - text->curl= text->curl->prev; - text->curc= text->curl->len; - - txt_combine_lines(text, text->curl, text->curl->next); - txt_pop_sel(text); - } - } - else { /* Just backspacing a char */ - int i= text->curc-1; + if (!text->curl->prev) return; - c= text->curl->line[i]; - while(i< text->curl->len) { - text->curl->line[i]= text->curl->line[i+1]; - i++; - } - text->curl->len--; - text->curc--; + text->curl= text->curl->prev; + text->curc= text->curl->len; - txt_pop_sel(text); + txt_combine_lines(text, text->curl, text->curl->next); + txt_pop_sel(text); + } + else { /* Just backspacing a char */ + int i= text->curc-1; + + TextMarker *mrk= txt_find_marker_region(text, text->curl, i, text->curl->len, 0, 0); + if (mrk) { + int lineno= mrk->lineno; + if (mrk->start==i+1) { + if ((mrk->flags & TMARK_TEMP) && !(mrk->flags & TMARK_EDITALL)) { + txt_clear_markers(text, mrk->group, TMARK_TEMP); + } else { + BLI_freelinkN(&text->markers, mrk); + } + return; + } + do { + if (mrk->start>i) mrk->start--; + mrk->end--; + mrk= mrk->next; + } while (mrk && mrk->lineno==lineno); + } + + c= text->curl->line[i]; + while(i< text->curl->len) { + text->curl->line[i]= text->curl->line[i+1]; + i++; + } + text->curl->len--; + text->curc--; + + txt_pop_sel(text); } txt_make_dirty(text); @@ -2101,10 +2341,17 @@ void txt_backspace_char (Text *text) if(!undoing) txt_undo_add_charop(text, UNDO_BS, c); } +void txt_backspace_word (Text *text) +{ + txt_jump_left(text, 1); + txt_delete_sel(text); +} + int txt_add_char (Text *text, char add) { - int len; - char *tmp, *format; + int len, lineno; + char *tmp; + TextMarker *mrk; if (!text) return 0; if (!text->curl) return 0; @@ -2116,8 +2363,17 @@ int txt_add_char (Text *text, char add) txt_delete_sel(text); + mrk= txt_find_marker_region(text, text->curl, text->curc-1, text->curl->len, 0, 0); + if (mrk) { + lineno= mrk->lineno; + do { + if (mrk->start>text->curc) mrk->start++; + mrk->end++; + mrk= mrk->next; + } while (mrk && mrk->lineno==lineno); + } + tmp= MEM_mallocN(text->curl->len+2, "textline_string"); - format= MEM_mallocN(text->curl->len+4, "Syntax_format"); if(text->curc) memcpy(tmp, text->curl->line, text->curc); tmp[text->curc]= add; @@ -2125,7 +2381,7 @@ int txt_add_char (Text *text, char add) len= text->curl->len - text->curc; if(len>0) memcpy(tmp+text->curc+1, text->curl->line+text->curc, len); tmp[text->curl->len+1]=0; - make_new_line(text->curl, tmp, format); + make_new_line(text->curl, tmp); text->curc++; @@ -2138,10 +2394,42 @@ int txt_add_char (Text *text, char add) return 1; } +int txt_replace_char (Text *text, char add) +{ + char del; + + if (!text) return 0; + if (!text->curl) return 0; + + /* If text is selected or we're at the end of the line just use txt_add_char */ + if (text->curc==text->curl->len || txt_has_sel(text) || add=='\n') { + TextMarker *mrk; + int i= txt_add_char(text, add); + mrk= txt_find_marker(text, text->curl, text->curc, 0, 0); + if (mrk && mrk->end==text->curc) mrk->end--; + return i; + } + + del= text->curl->line[text->curc]; + text->curl->line[text->curc]= (unsigned char) add; + text->curc++; + txt_pop_sel(text); + + txt_make_dirty(text); + txt_clean_text(text); + + /* Should probably create a new op for this */ + if(!undoing) { + txt_undo_add_charop(text, UNDO_DEL, del); + txt_undo_add_charop(text, UNDO_INSERT, add); + } + return 1; +} + void indent(Text *text) { int len, num; - char *tmp, *format; + char *tmp; char add = '\t'; if (!text) return; @@ -2152,7 +2440,6 @@ void indent(Text *text) while (TRUE) { tmp= MEM_mallocN(text->curl->len+2, "textline_string"); - format= MEM_mallocN(text->curl->len+3, "Syntax_format"); text->curc = 0; if(text->curc) memcpy(tmp, text->curl->line, text->curc); @@ -2162,7 +2449,7 @@ void indent(Text *text) if(len>0) memcpy(tmp+text->curc+1, text->curl->line+text->curc, len); tmp[text->curl->len+1]=0; - make_new_line(text->curl, tmp, format); + make_new_line(text->curl, tmp); text->curc++; @@ -2243,7 +2530,7 @@ void unindent(Text *text) void comment(Text *text) { int len, num; - char *tmp, *format; + char *tmp; char add = '#'; if (!text) return; @@ -2254,7 +2541,6 @@ void comment(Text *text) while (TRUE) { tmp= MEM_mallocN(text->curl->len+2, "textline_string"); - format = MEM_mallocN(text->curl->len+3, "Syntax_format"); text->curc = 0; if(text->curc) memcpy(tmp, text->curl->line, text->curc); @@ -2264,7 +2550,7 @@ void comment(Text *text) if(len>0) memcpy(tmp+text->curc+1, text->curl->line+text->curc, len); tmp[text->curl->len+1]=0; - make_new_line(text->curl, tmp, format); + make_new_line(text->curl, tmp); text->curc++; @@ -2348,7 +2634,7 @@ int setcurr_tab (Text *text) int test = 0; char *word = ":"; char *comm = "#"; - char back_words[3][7] = {"return", "break", "pass"}; + char back_words[4][7] = {"return", "break", "pass", "yield"}; if (!text) return 0; if (!text->curl) return 0; @@ -2365,16 +2651,25 @@ int setcurr_tab (Text *text) if(strstr(text->curl->line, word)) { //if we find a : then add a tab but not if it is in a comment - if(strcspn(text->curl->line, word) < strcspn(text->curl->line, comm)) + int a, indent = 0; + for(a=0; text->curl->line[a] != '\0'; a++) { + if (text->curl->line[a]=='#') { + break; + } else if (text->curl->line[a]==':') { + indent = 1; + } else if (text->curl->line[a]==']') { + indent = 0; + } + } + if (indent) { i++; - } } - while(test < 3) + for(test=0; test < 4; test++) { - //if there are these 3 key words then remove a tab because we are done with the block + //if there are these 4 key words then remove a tab because we are done with the block if(strstr(text->curl->line, back_words[test]) && i > 0) { if(strcspn(text->curl->line, back_words[test]) < strcspn(text->curl->line, comm)) @@ -2382,8 +2677,145 @@ int setcurr_tab (Text *text) i--; } } - test++; } return i; } +/*********************************/ +/* Text marker utility functions */ +/*********************************/ + +/* Creates and adds a marker to the list maintaining sorted order */ +void txt_add_marker(Text *text, TextLine *line, int start, int end, char color[4], int group, int flags) { + TextMarker *tmp, *marker; + + marker= MEM_mallocN(sizeof(TextMarker), "text_marker"); + + marker->lineno= txt_get_span(text->lines.first, line); + marker->start= MIN2(start, end); + marker->end= MAX2(start, end); + marker->group= group; + marker->flags= flags; + + marker->color[0]= color[0]; + marker->color[1]= color[1]; + marker->color[2]= color[2]; + marker->color[3]= color[3]; + + for (tmp=text->markers.last; tmp; tmp=tmp->prev) + if (tmp->lineno < marker->lineno || (tmp->lineno==marker->lineno && tmp->start < marker->start)) + break; + + if (tmp) BLI_insertlinkafter(&text->markers, tmp, marker); + else BLI_addhead(&text->markers, marker); +} + +/* Returns the first matching marker on the specified line between two points. + If the group or flags fields are non-zero the returned flag must be in the + specified group and have at least the specified flags set. */ +TextMarker *txt_find_marker_region(Text *text, TextLine *line, int start, int end, int group, int flags) { + TextMarker *marker, *next; + int lineno= txt_get_span(text->lines.first, line); + + for (marker=text->markers.first; marker; marker=next) { + next= marker->next; + + if (group && marker->group != group) continue; + else if ((marker->flags & flags) != flags) continue; + else if (marker->lineno < lineno) continue; + else if (marker->lineno > lineno) break; + + if ((marker->start==marker->end && start<=marker->start && marker->start<=end) || + (marker->start<end && marker->end>start)) + return marker; + } + return NULL; +} + +/* Clears all markers on the specified line between two points. If the group or + flags fields are non-zero the returned flag must be in the specified group + and have at least the specified flags set. */ +short txt_clear_marker_region(Text *text, TextLine *line, int start, int end, int group, int flags) { + TextMarker *marker, *next; + int lineno= txt_get_span(text->lines.first, line); + short cleared= 0; + + for (marker=text->markers.first; marker; marker=next) { + next= marker->next; + + if (group && marker->group != group) continue; + else if ((marker->flags & flags) != flags) continue; + else if (marker->lineno < lineno) continue; + else if (marker->lineno > lineno) break; + + if ((marker->start==marker->end && start<=marker->start && marker->start<=end) || + (marker->start<end && marker->end>start)) { + BLI_freelinkN(&text->markers, marker); + cleared= 1; + } + } + return cleared; +} + +/* Clears all markers in the specified group (if given) with at least the + specified flags set. Useful for clearing temporary markers (group=0, + flags=TMARK_TEMP) */ +short txt_clear_markers(Text *text, int group, int flags) { + TextMarker *marker, *next; + short cleared= 0; + + for (marker=text->markers.first; marker; marker=next) { + next= marker->next; + + if ((!group || marker->group==group) && + (marker->flags & flags) == flags) { + BLI_freelinkN(&text->markers, marker); + cleared= 1; + } + } + return cleared; +} + +/* Finds the marker at the specified line and cursor position with at least the + specified flags set in the given group (if non-zero). */ +TextMarker *txt_find_marker(Text *text, TextLine *line, int curs, int group, int flags) { + TextMarker *marker; + int lineno= txt_get_span(text->lines.first, line); + + for (marker=text->markers.first; marker; marker=marker->next) { + if (group && marker->group != group) continue; + else if ((marker->flags & flags) != flags) continue; + else if (marker->lineno < lineno) continue; + else if (marker->lineno > lineno) break; + + if (marker->start <= curs && curs <= marker->end) + return marker; + } + return NULL; +} + +/* Finds the previous marker in the same group. If no other is found, the same + marker will be returned */ +TextMarker *txt_prev_marker(Text *text, TextMarker *marker) { + TextMarker *tmp= marker; + while (tmp) { + if (tmp->prev) tmp= tmp->prev; + else tmp= text->markers.last; + if (tmp->group == marker->group) + return tmp; + } + return NULL; /* Only if marker==NULL */ +} + +/* Finds the next marker in the same group. If no other is found, the same + marker will be returned */ +TextMarker *txt_next_marker(Text *text, TextMarker *marker) { + TextMarker *tmp= marker; + while (tmp) { + if (tmp->next) tmp= tmp->next; + else tmp= text->markers.first; + if (tmp->group == marker->group) + return tmp; + } + return NULL; /* Only if marker==NULL */ +} diff --git a/source/blender/blenkernel/intern/texture.c b/source/blender/blenkernel/intern/texture.c index 859cf663e6d..372c51b0da5 100644 --- a/source/blender/blenkernel/intern/texture.c +++ b/source/blender/blenkernel/intern/texture.c @@ -52,6 +52,9 @@ #include "DNA_image_types.h" #include "DNA_world_types.h" #include "DNA_brush_types.h" +#include "DNA_node_types.h" +#include "DNA_color_types.h" +#include "DNA_scene_types.h" #include "IMB_imbuf_types.h" #include "IMB_imbuf.h" @@ -71,6 +74,7 @@ #include "BKE_icons.h" #include "BKE_ipo.h" #include "BKE_brush.h" +#include "BKE_node.h" /* ------------------------------------------------------------------------- */ @@ -103,6 +107,7 @@ void open_plugin_tex(PluginTex *pit) pit->result= 0; pit->cfra= 0; pit->version= 0; + pit->instance_init= 0; /* clear the error list */ PIL_dynlib_get_error_as_string(NULL); @@ -122,7 +127,7 @@ void open_plugin_tex(PluginTex *pit) if (version != 0) { pit->version= version(); - if (pit->version>=2 && pit->version<=5) { + if( pit->version >= 2 && pit->version <=6) { int (*info_func)(PluginInfo *); PluginInfo *info= (PluginInfo*) MEM_mallocN(sizeof(PluginInfo), "plugin_info"); @@ -342,6 +347,15 @@ int do_colorband(ColorBand *coba, float in, float out[4]) else fac= 0.0f; + if (coba->ipotype==4) { + /* constant */ + out[0]= cbd2->r; + out[1]= cbd2->g; + out[2]= cbd2->b; + out[3]= cbd2->a; + return 1; + } + if(coba->ipotype>=2) { /* ipo from right to left: 3 2 1 0 */ @@ -385,6 +399,17 @@ int do_colorband(ColorBand *coba, float in, float out[4]) return 1; /* OK */ } +void colorband_table_RGBA(ColorBand *coba, float **array, int *size) +{ + int a; + + *size = CM_TABLE+1; + *array = MEM_callocN(sizeof(float)*(*size)*4, "ColorBand"); + + for(a=0; a<*size; a++) + do_colorband(coba, (float)a/(float)CM_TABLE, &(*array)[a*4]); +} + /* ******************* TEX ************************ */ void free_texture(Tex *tex) @@ -540,6 +565,8 @@ Tex *copy_texture(Tex *tex) if(texn->type==TEX_IMAGE) id_us_plus((ID *)texn->ima); else texn->ima= 0; + id_us_plus((ID *)texn->ipo); + if(texn->plugin) { texn->plugin= MEM_dupallocN(texn->plugin); open_plugin_tex(texn->plugin); @@ -724,9 +751,10 @@ Tex *give_current_texture(Object *ob, int act) Lamp *la = 0; MTex *mtex = 0; Tex *tex = 0; + bNode *node; if(ob==0) return 0; - if(ob->totcol==0) return 0; + if(ob->totcol==0 && !(ob->type==OB_LAMP)) return 0; if(ob->type==OB_LAMP) { la=(Lamp *)ob->data; @@ -734,7 +762,6 @@ Tex *give_current_texture(Object *ob, int act) mtex= la->mtex[(int)(la->texact)]; if(mtex) tex= mtex->tex; } - else tex= 0; } else { if(act>ob->totcol) act= ob->totcol; else if(act==0) act= 1; @@ -747,18 +774,42 @@ Tex *give_current_texture(Object *ob, int act) if(matarar && *matarar) ma= (*matarar)[act-1]; else ma= 0; - + } + + if(ma && ma->use_nodes && ma->nodetree) { + node= nodeGetActiveID(ma->nodetree, ID_TE); + + if(node) { + tex= (Tex *)node->id; + ma= NULL; + } + else { + node= nodeGetActiveID(ma->nodetree, ID_MA); + if(node) + ma= (Material*)node->id; + } } if(ma) { mtex= ma->mtex[(int)(ma->texact)]; if(mtex) tex= mtex->tex; } - else tex= 0; } return tex; } +Tex *give_current_world_texture(void) +{ + MTex *mtex = 0; + Tex *tex = 0; + + if(!(G.scene->world)) return 0; + + mtex= G.scene->world->mtex[(int)(G.scene->world->texact)]; + if(mtex) tex= mtex->tex; + + return tex; +} /* ------------------------------------------------------------------------- */ @@ -816,3 +867,19 @@ void BKE_free_envmap(EnvMap *env) } /* ------------------------------------------------------------------------- */ +int BKE_texture_dependsOnTime(const struct Tex *texture) +{ + if(texture->plugin) { + // assume all plugins depend on time + return 1; + } else if( texture->ima && + ELEM(texture->ima->source, IMA_SRC_SEQUENCE, IMA_SRC_MOVIE)) { + return 1; + } else if(texture->ipo) { + // assume any ipo means the texture is animated + return 1; + } + return 0; +} + +/* ------------------------------------------------------------------------- */ diff --git a/source/blender/blenkernel/intern/world.c b/source/blender/blenkernel/intern/world.c index 44c35252359..bef372e39f1 100644 --- a/source/blender/blenkernel/intern/world.c +++ b/source/blender/blenkernel/intern/world.c @@ -53,7 +53,9 @@ #include "BKE_main.h" #include "BKE_icons.h" +#ifndef DISABLE_PYTHON #include "BPY_extern.h" +#endif #ifdef HAVE_CONFIG_H #include <config.h> @@ -63,9 +65,10 @@ void free_world(World *wrld) { MTex *mtex; int a; - + +#ifndef DISABLE_PYTHON BPY_free_scriptlink(&wrld->scriptlink); - +#endif for(a=0; a<MAX_MTEX; a++) { mtex= wrld->mtex[a]; if(mtex && mtex->tex) mtex->tex->id.us--; @@ -94,12 +97,12 @@ World *add_world(char *name) wrld->exp= 0.0f; wrld->exposure=wrld->range= 1.0f; - wrld->aodist= 5.0; + wrld->aodist= 5.0f; wrld->aosamp= 5; - wrld->aoenergy= 1.0; - wrld->aobias= 0.05; + wrld->aoenergy= 1.0f; + wrld->aobias= 0.05f; wrld->ao_samp_method = WO_AOSAMP_HAMMERSLEY; - + wrld->ao_approx_error= 0.25f; wrld->physicsEngine= WOPHY_BULLET;//WOPHY_SUMO; Bullet by default wrld->preview = NULL; @@ -123,9 +126,9 @@ World *copy_world(World *wrld) } if (wrld->preview) wrldn->preview = BKE_previewimg_copy(wrld->preview); - +#ifndef DISABLE_PYTHON BPY_copy_scriptlink(&wrld->scriptlink); - +#endif id_us_plus((ID *)wrldn->ipo); return wrldn; diff --git a/source/blender/blenkernel/intern/writeavi.c b/source/blender/blenkernel/intern/writeavi.c index b70bdc2c07e..e63588e6d7f 100644 --- a/source/blender/blenkernel/intern/writeavi.c +++ b/source/blender/blenkernel/intern/writeavi.c @@ -116,7 +116,7 @@ void makeavistring (RenderData *rd, char *string) if (string==0) return; strcpy(string, rd->pic); - BLI_convertstringcode(string, G.sce, rd->cfra); + BLI_convertstringcode(string, G.sce); BLI_make_existing_file(string); diff --git a/source/blender/blenkernel/intern/writeffmpeg.c b/source/blender/blenkernel/intern/writeffmpeg.c index e5d8d560a1b..7008f254871 100644 --- a/source/blender/blenkernel/intern/writeffmpeg.c +++ b/source/blender/blenkernel/intern/writeffmpeg.c @@ -33,6 +33,7 @@ #include <ffmpeg/avcodec.h> #include <ffmpeg/rational.h> #include <ffmpeg/swscale.h> +#include <ffmpeg/opt.h> #if LIBAVFORMAT_VERSION_INT < (49 << 16) #define FFMPEG_OLD_FRAME_RATE 1 @@ -41,6 +42,12 @@ #define FFMPEG_CODEC_TIME_BASE 1 #endif +#if LIBAVFORMAT_VERSION_INT >= (52 << 16) +#define OUTFILE_PB (outfile->pb) +#else +#define OUTFILE_PB (&outfile->pb) +#endif + #if defined(WIN32) && (!(defined snprintf)) #define snprintf _snprintf #endif @@ -51,6 +58,7 @@ #include "BLI_blenlib.h" #include "BKE_global.h" +#include "BKE_idprop.h" #include "IMB_imbuf_types.h" #include "IMB_imbuf.h" @@ -210,6 +218,18 @@ static const char** get_file_extensions(int format) static const char * rv[] = { ".avi", NULL }; return rv; } + case FFMPEG_FLV: { + static const char * rv[] = { ".flv", NULL }; + return rv; + } + case FFMPEG_MKV: { + static const char * rv[] = { ".mkv", NULL }; + return rv; + } + case FFMPEG_OGG: { + static const char * rv[] = { ".ogg", ".ogv", NULL }; + return rv; + } default: return NULL; } @@ -231,14 +251,18 @@ static void write_video_frame(AVFrame* frame) AVPacket packet; av_init_packet(&packet); + if (c->coded_frame->pts != AV_NOPTS_VALUE) { #ifdef FFMPEG_CODEC_TIME_BASE - packet.pts = av_rescale_q(c->coded_frame->pts, - c->time_base, - video_stream->time_base); + packet.pts = av_rescale_q(c->coded_frame->pts, + c->time_base, + video_stream->time_base); #else - packet.pts = c->coded_frame->pts; + packet.pts = c->coded_frame->pts; #endif - fprintf(stderr, "Video Frame PTS: %lld\n", packet.pts); + fprintf(stderr, "Video Frame PTS: %lld\n", packet.pts); + } else { + fprintf(stderr, "Video Frame PTS: not set\n"); + } if (c->coded_frame->key_frame) packet.flags |= PKT_FLAG_KEY; packet.stream_index = video_stream->index; @@ -323,6 +347,75 @@ static AVFrame* generate_video_frame(uint8_t* pixels) return current_frame; } +static void set_ffmpeg_property_option(AVCodecContext* c, IDProperty * prop) +{ + char name[128]; + char * param; + const AVOption * rv = NULL; + + fprintf(stderr, "FFMPEG expert option: %s: ", prop->name); + + strncpy(name, prop->name, 128); + + param = strchr(name, ':'); + + if (param) { + *param++ = 0; + } + + switch(prop->type) { + case IDP_STRING: + fprintf(stderr, "%s.\n", IDP_String(prop)); + rv = av_set_string(c, prop->name, IDP_String(prop)); + break; + case IDP_FLOAT: + fprintf(stderr, "%g.\n", IDP_Float(prop)); + rv = av_set_double(c, prop->name, IDP_Float(prop)); + break; + case IDP_INT: + fprintf(stderr, "%d.\n", IDP_Int(prop)); + + if (param) { + if (IDP_Int(prop)) { + rv = av_set_string(c, name, param); + } else { + return; + } + } else { + rv = av_set_int(c, prop->name, IDP_Int(prop)); + } + break; + } + + if (!rv) { + fprintf(stderr, "ffmpeg-option not supported: %s! Skipping.\n", + prop->name); + } +} + +static void set_ffmpeg_properties(AVCodecContext* c, const char * prop_name) +{ + IDProperty * prop; + void * iter; + IDProperty * curr; + + if (!G.scene->r.ffcodecdata.properties) { + return; + } + + prop = IDP_GetPropertyFromGroup( + G.scene->r.ffcodecdata.properties, (char*) prop_name); + if (!prop) { + return; + } + + iter = IDP_GetGroupIterator(prop); + + while ((curr = IDP_GroupIterNext(iter)) != NULL) { + set_ffmpeg_property_option(c, curr); + } +} + /* prepare a video stream for the output file */ static AVStream* alloc_video_stream(int codec_id, AVFormatContext* of, @@ -409,13 +502,18 @@ static AVStream* alloc_video_stream(int codec_id, AVFormatContext* of, } /* Determine whether we are encoding interlaced material or not */ - if (G.scene->r.mode & (1 << 6)) { + if (G.scene->r.mode & R_FIELDS) { fprintf(stderr, "Encoding interlaced video\n"); c->flags |= CODEC_FLAG_INTERLACED_DCT; c->flags |= CODEC_FLAG_INTERLACED_ME; - } - c->sample_aspect_ratio.num = G.scene->r.xasp; - c->sample_aspect_ratio.den = G.scene->r.yasp; + } + + /* xasp & yasp got float lately... */ + + c->sample_aspect_ratio = av_d2q( + ((double) G.scene->r.xasp / (double) G.scene->r.yasp), 255); + + set_ffmpeg_properties(c, "video"); if (avcodec_open(c, codec) < 0) { // @@ -461,6 +559,9 @@ static AVStream* alloc_audio_stream(int codec_id, AVFormatContext* of) //XXX error("Couldn't find a valid audio codec"); return NULL; } + + set_ffmpeg_properties(c, "audio"); + if (avcodec_open(c, codec) < 0) { //XXX error("Couldn't initialize audio codec"); return NULL; @@ -573,6 +674,8 @@ void start_ffmpeg_impl(struct RenderData *rd, int rectx, int recty) switch(ffmpeg_type) { case FFMPEG_AVI: case FFMPEG_MOV: + case FFMPEG_OGG: + case FFMPEG_MKV: fmt->video_codec = ffmpeg_codec; break; case FFMPEG_DV: @@ -590,6 +693,9 @@ void start_ffmpeg_impl(struct RenderData *rd, int rectx, int recty) case FFMPEG_XVID: fmt->video_codec = CODEC_ID_XVID; break; + case FFMPEG_FLV: + fmt->video_codec = CODEC_ID_FLV1; + break; case FFMPEG_MPEG4: default: fmt->video_codec = CODEC_ID_MPEG4; @@ -615,6 +721,9 @@ void start_ffmpeg_impl(struct RenderData *rd, int rectx, int recty) return; } } + + fmt->audio_codec = ffmpeg_audio_codec; + if (ffmpeg_type == FFMPEG_DV) { fmt->audio_codec = CODEC_ID_PCM_S16LE; if (ffmpeg_multiplex_audio @@ -684,7 +793,8 @@ void makeffmpegstring(char* string) { if (!string || !exts) return; strcpy(string, G.scene->r.pic); - BLI_convertstringcode(string, G.sce, G.scene->r.cfra); + BLI_convertstringcode(string, G.sce); + BLI_convertstringframe(string, G.scene->r.cfra); BLI_make_existing_file(string); @@ -756,7 +866,7 @@ void append_ffmpeg(int frame, int *pixels, int rectx, int recty) write_video_frame(generate_video_frame((unsigned char*) pixels)); if (ffmpeg_autosplit) { - if (url_ftell(&outfile->pb) > FFMPEG_AUTOSPLIT_SIZE) { + if (url_ftell(OUTFILE_PB) > FFMPEG_AUTOSPLIT_SIZE) { end_ffmpeg(); ffmpeg_autosplit_count++; start_ffmpeg_impl(ffmpeg_renderdata, @@ -772,7 +882,7 @@ void end_ffmpeg(void) fprintf(stderr, "Closing ffmpeg...\n"); - if (audio_stream) { + if (audio_stream && video_stream) { write_audio_frames(); } @@ -803,7 +913,7 @@ void end_ffmpeg(void) } if (outfile && outfile->oformat) { if (!(outfile->oformat->flags & AVFMT_NOFILE)) { - url_fclose(&outfile->pb); + url_fclose(OUTFILE_PB); } } if (outfile) { diff --git a/source/blender/blenkernel/intern/writeframeserver.c b/source/blender/blenkernel/intern/writeframeserver.c index c48bea70dc9..64bf5f85d23 100644 --- a/source/blender/blenkernel/intern/writeframeserver.c +++ b/source/blender/blenkernel/intern/writeframeserver.c @@ -249,7 +249,7 @@ static int handle_request(char * req) return -1; } -int frameserver_loop() +int frameserver_loop(void) { fd_set readfds; struct timeval tv; |