diff options
author | Joseph Eagar <joeedh@gmail.com> | 2009-07-16 10:27:37 +0400 |
---|---|---|
committer | Joseph Eagar <joeedh@gmail.com> | 2009-07-16 10:27:37 +0400 |
commit | 0b1649b2b86b52b44bdabf8e3542d8adffb55623 (patch) | |
tree | 542e10220a719f2a54e8afbee7b11195b6dac344 /source/blender/blenkernel/intern | |
parent | 3bade135035dacfdb4a3833f3fac3bf5811ce7de (diff) | |
parent | 0bfc98706ef93f90bd74b195b98c36c7dcea94dd (diff) |
merge with 2.5 at r21568
Diffstat (limited to 'source/blender/blenkernel/intern')
44 files changed, 5323 insertions, 3384 deletions
diff --git a/source/blender/blenkernel/intern/BME_tools.c b/source/blender/blenkernel/intern/BME_tools.c index a41307de183..32065ea5151 100644 --- a/source/blender/blenkernel/intern/BME_tools.c +++ b/source/blender/blenkernel/intern/BME_tools.c @@ -191,7 +191,7 @@ static BME_Poly *BME_split_face(BME_Mesh *bm, BME_Poly *f, BME_Vert *v1, BME_Ver 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; + if (example && (example->flag & SELECT)) f->flag |= ME_FACE_SEL; nf->h = f->h; nf->mat_nr = f->mat_nr; if (nl && example) { diff --git a/source/blender/blenkernel/intern/DerivedMesh.c b/source/blender/blenkernel/intern/DerivedMesh.c index 7bd47d4a4c0..a1068080927 100644 --- a/source/blender/blenkernel/intern/DerivedMesh.c +++ b/source/blender/blenkernel/intern/DerivedMesh.c @@ -1770,18 +1770,18 @@ static void mesh_calc_modifiers(Scene *scene, Object *ob, float (*inputVertexCos modifiers_clearErrors(ob); + if(useRenderParams) required_mode = eModifierMode_Render; + else required_mode = eModifierMode_Realtime; + /* we always want to keep original indices */ dataMask |= CD_MASK_ORIGINDEX; - datamasks = modifiers_calcDataMasks(md, dataMask); + datamasks = modifiers_calcDataMasks(ob, md, dataMask, required_mode); curr = datamasks; if(deform_r) *deform_r = NULL; *final_r = NULL; - if(useRenderParams) required_mode = eModifierMode_Render; - else required_mode = eModifierMode_Realtime; - if(useDeform) { if(useDeform > 0 && do_ob_key(scene, ob)) /* shape key makes deform verts */ deformedVerts = mesh_getVertexCos(me, &numVerts); @@ -1794,8 +1794,7 @@ static void mesh_calc_modifiers(Scene *scene, Object *ob, float (*inputVertexCos md->scene= scene; - if((md->mode & required_mode) != required_mode) continue; - if(mti->isDisabled && mti->isDisabled(md)) continue; + if(!modifier_isEnabled(md, required_mode)) continue; if(useDeform < 0 && mti->dependsOnTime && mti->dependsOnTime(md)) continue; if(mti->type == eModifierTypeType_OnlyDeform) { @@ -1844,19 +1843,18 @@ static void mesh_calc_modifiers(Scene *scene, Object *ob, float (*inputVertexCos md->scene= scene; - if((md->mode & required_mode) != required_mode) continue; + if(!modifier_isEnabled(md, required_mode)) continue; if(mti->type == eModifierTypeType_OnlyDeform && !useDeform) continue; if((mti->flags & eModifierTypeFlag_RequiresOriginalData) && dm) { modifier_setError(md, "Modifier requires original data, bad stack position."); continue; } - if(mti->isDisabled && mti->isDisabled(md)) continue; if(needMapping && !modifier_supportsMapping(md)) continue; if(useDeform < 0 && mti->dependsOnTime && mti->dependsOnTime(md)) continue; /* add an orco layer if needed by this modifier */ if(dm && mti->requiredDataMask) { - mask = mti->requiredDataMask(md); + mask = mti->requiredDataMask(ob, md); if(mask & CD_MASK_ORCO) add_orco_dm(ob, NULL, dm, orcodm); } @@ -2029,14 +2027,11 @@ static int editbmesh_modifier_is_enabled(ModifierData *md, DerivedMesh *dm) ModifierTypeInfo *mti = modifierType_getInfo(md->type); int required_mode = eModifierMode_Realtime | eModifierMode_Editmode; - if((md->mode & required_mode) != required_mode) return 0; + if(!modifier_isEnabled(md, required_mode)) return 0; if((mti->flags & eModifierTypeFlag_RequiresOriginalData) && dm) { modifier_setError(md, "Modifier requires original data, bad stack position."); return 0; } - if(mti->isDisabled && mti->isDisabled(md)) return 0; - if(!(mti->flags & eModifierTypeFlag_SupportsEditmode)) return 0; - if(md->mode & eModifierMode_DisableTemporary) return 0; return 1; } @@ -2051,6 +2046,7 @@ static void editbmesh_calc_modifiers(Scene *scene, Object *ob, BMEditMesh *em, D DerivedMesh *dm, *orcodm = NULL; int i, numVerts = 0, cageIndex = modifiers_getCageIndex(ob, NULL); LinkNode *datamasks, *curr; + int required_mode = eModifierMode_Realtime | eModifierMode_Editmode; modifiers_clearErrors(ob); @@ -2064,7 +2060,7 @@ static void editbmesh_calc_modifiers(Scene *scene, Object *ob, BMEditMesh *em, D /* we always want to keep original indices */ dataMask |= CD_MASK_ORIGINDEX; - datamasks = modifiers_calcDataMasks(md, dataMask); + datamasks = modifiers_calcDataMasks(ob, md, dataMask, required_mode); curr = datamasks; for(i = 0; md; i++, md = md->next, curr = curr->next) { @@ -2077,7 +2073,7 @@ static void editbmesh_calc_modifiers(Scene *scene, Object *ob, BMEditMesh *em, D /* add an orco layer if needed by this modifier */ if(dm && mti->requiredDataMask) { - mask = mti->requiredDataMask(md); + mask = mti->requiredDataMask(ob, md); if(mask & CD_MASK_ORCO) add_orco_dm(ob, em, dm, orcodm); } diff --git a/source/blender/blenkernel/intern/action.c b/source/blender/blenkernel/intern/action.c index d54bc749b71..96896509f60 100644 --- a/source/blender/blenkernel/intern/action.c +++ b/source/blender/blenkernel/intern/action.c @@ -581,6 +581,77 @@ void game_copy_pose(bPose **dst, bPose *src) *dst=out; } + +/* Only allowed for Poses with identical channels */ +void game_blend_poses(bPose *dst, bPose *src, float srcweight/*, short mode*/) +{ + short mode= ACTSTRIPMODE_BLEND; + + bPoseChannel *dchan; + const bPoseChannel *schan; + bConstraint *dcon, *scon; + float dstweight; + int i; + + switch (mode){ + case ACTSTRIPMODE_BLEND: + dstweight = 1.0F - srcweight; + break; + case ACTSTRIPMODE_ADD: + dstweight = 1.0F; + break; + default : + dstweight = 1.0F; + } + + schan= src->chanbase.first; + for (dchan = dst->chanbase.first; dchan; dchan=dchan->next, schan= schan->next){ + if (schan->flag & (POSE_ROT|POSE_LOC|POSE_SIZE)) { + /* replaced quat->matrix->quat conversion with decent quaternion interpol (ton) */ + + /* Do the transformation blend */ + if (schan->flag & POSE_ROT) { + /* quat interpolation done separate */ + if (schan->rotmode == PCHAN_ROT_QUAT) { + float dquat[4], squat[4]; + + QUATCOPY(dquat, dchan->quat); + QUATCOPY(squat, schan->quat); + if (mode==ACTSTRIPMODE_BLEND) + QuatInterpol(dchan->quat, dquat, squat, srcweight); + else { + QuatMulFac(squat, srcweight); + QuatMul(dchan->quat, dquat, squat); + } + + NormalQuat(dchan->quat); + } + } + + for (i=0; i<3; i++) { + /* blending for loc and scale are pretty self-explanatory... */ + if (schan->flag & POSE_LOC) + dchan->loc[i] = (dchan->loc[i]*dstweight) + (schan->loc[i]*srcweight); + if (schan->flag & POSE_SIZE) + dchan->size[i] = 1.0f + ((dchan->size[i]-1.0f)*dstweight) + ((schan->size[i]-1.0f)*srcweight); + + /* euler-rotation interpolation done here instead... */ + // FIXME: are these results decent? + if ((schan->flag & POSE_ROT) && (schan->rotmode)) + dchan->eul[i] = (dchan->eul[i]*dstweight) + (schan->eul[i]*srcweight); + } + dchan->flag |= schan->flag; + } + for(dcon= dchan->constraints.first, scon= schan->constraints.first; dcon && scon; dcon= dcon->next, scon= scon->next) { + /* no 'add' option for constraint blending */ + dcon->enforce= dcon->enforce*(1.0f-srcweight) + scon->enforce*srcweight; + } + } + + /* this pose is now in src time */ + dst->ctime= src->ctime; +} + void game_free_pose(bPose *pose) { if (pose) { @@ -698,70 +769,23 @@ void framechange_poses_clear_unkeyed(void) /* ************** time ****************** */ -static bActionStrip *get_active_strip(Object *ob) -{ -#if 0 // XXX old animation system - bActionStrip *strip; - - if(ob->action==NULL) - return NULL; - - for (strip=ob->nlastrips.first; strip; strip=strip->next) - if(strip->flag & ACTSTRIP_ACTIVE) - break; - - if(strip && strip->act==ob->action) - return strip; -#endif // XXX old animation system - - return NULL; -} - -/* non clipped mapping of strip */ -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); - - if (strip->scale == 0.0f) strip->scale= 1.0f; - scale = (float)fabs(strip->scale); /* scale must be positive (for now) */ - - actlength = strip->actend-strip->actstart; - if (actlength == 0.0f) actlength = 1.0f; - length = repeat * scale * actlength; - - /* invert = convert action-strip time to global time */ - if (invert) - return length*(cframe - strip->actstart)/(repeat*actlength) + strip->start; - else - return repeat*actlength*(cframe - strip->start)/length + strip->actstart; -} - -/* if the conditions match, it converts current time to strip time */ -float get_action_frame(Object *ob, float cframe) +/* Check if the given action has any keyframes */ +short action_has_motion(const bAction *act) { - bActionStrip *strip= get_active_strip(ob); + FCurve *fcu; - if(strip) - return get_actionstrip_frame(strip, cframe, 0); - return cframe; -} - -/* inverted, strip time to current time */ -float get_action_frame_inv(Object *ob, float cframe) -{ - bActionStrip *strip= get_active_strip(ob); + /* return on the first F-Curve that has some keyframes/samples defined */ + if (act) { + for (fcu= act->curves.first; fcu; fcu= fcu->next) { + if (fcu->totvert) + return 1; + } + } - if(strip) - return get_actionstrip_frame(strip, cframe, 1); - return cframe; + /* nothing found */ + return 0; } - - - /* Calculate the extents of given action */ void calc_action_range(const bAction *act, float *start, float *end, int incl_hidden) { @@ -800,6 +824,7 @@ void calc_action_range(const bAction *act, float *start, float *end, int incl_hi /* Copy the data from the action-pose (src) into the pose */ /* both args are assumed to be valid */ /* exported to game engine */ +/* Note! this assumes both poses are aligned, this isnt always true when dealing with user poses */ void extract_pose_from_pose(bPose *pose, const bPose *src) { const bPoseChannel *schan; @@ -810,7 +835,7 @@ void extract_pose_from_pose(bPose *pose, const bPose *src) return; } - for (schan=src->chanbase.first; schan; schan=schan->next, pchan= pchan->next) { + for (schan=src->chanbase.first; (schan && pchan); schan=schan->next, pchan= pchan->next) { copy_pose_channel_data(pchan, schan); } } @@ -1038,75 +1063,6 @@ static void blend_pose_offset_bone(bActionStrip *strip, bPose *dst, bPose *src, VecAddf(dst->cyclic_offset, dst->cyclic_offset, src->cyclic_offset); } - -/* Only allowed for Poses with identical channels */ -void blend_poses(bPose *dst, bPose *src, float srcweight, short mode) -{ - bPoseChannel *dchan; - const bPoseChannel *schan; - bConstraint *dcon, *scon; - float dstweight; - int i; - - switch (mode){ - case ACTSTRIPMODE_BLEND: - dstweight = 1.0F - srcweight; - break; - case ACTSTRIPMODE_ADD: - dstweight = 1.0F; - break; - default : - dstweight = 1.0F; - } - - schan= src->chanbase.first; - for (dchan = dst->chanbase.first; dchan; dchan=dchan->next, schan= schan->next){ - if (schan->flag & (POSE_ROT|POSE_LOC|POSE_SIZE)) { - /* replaced quat->matrix->quat conversion with decent quaternion interpol (ton) */ - - /* Do the transformation blend */ - if (schan->flag & POSE_ROT) { - /* quat interpolation done separate */ - if (schan->rotmode == PCHAN_ROT_QUAT) { - float dquat[4], squat[4]; - - QUATCOPY(dquat, dchan->quat); - QUATCOPY(squat, schan->quat); - if (mode==ACTSTRIPMODE_BLEND) - QuatInterpol(dchan->quat, dquat, squat, srcweight); - else { - QuatMulFac(squat, srcweight); - QuatMul(dchan->quat, dquat, squat); - } - - NormalQuat(dchan->quat); - } - } - - for (i=0; i<3; i++) { - /* blending for loc and scale are pretty self-explanatory... */ - if (schan->flag & POSE_LOC) - dchan->loc[i] = (dchan->loc[i]*dstweight) + (schan->loc[i]*srcweight); - if (schan->flag & POSE_SIZE) - dchan->size[i] = 1.0f + ((dchan->size[i]-1.0f)*dstweight) + ((schan->size[i]-1.0f)*srcweight); - - /* euler-rotation interpolation done here instead... */ - // FIXME: are these results decent? - if ((schan->flag & POSE_ROT) && (schan->rotmode)) - dchan->eul[i] = (dchan->eul[i]*dstweight) + (schan->eul[i]*srcweight); - } - dchan->flag |= schan->flag; - } - for(dcon= dchan->constraints.first, scon= schan->constraints.first; dcon && scon; dcon= dcon->next, scon= scon->next) { - /* no 'add' option for constraint blending */ - dcon->enforce= dcon->enforce*(1.0f-srcweight) + scon->enforce*srcweight; - } - } - - /* this pose is now in src time */ - dst->ctime= src->ctime; -} - typedef struct NlaIpoChannel { struct NlaIpoChannel *next, *prev; float val; diff --git a/source/blender/blenkernel/intern/anim.c b/source/blender/blenkernel/intern/anim.c index 7451e40cac7..7d9d261f306 100644 --- a/source/blender/blenkernel/intern/anim.c +++ b/source/blender/blenkernel/intern/anim.c @@ -783,8 +783,9 @@ static void new_particle_duplilist(ListBase *lb, ID *id, Scene *scene, Object *p BLI_srandom(31415926 + psys->seed); lay= 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)) { + if((psys->renderdata || part->draw_as==PART_DRAW_REND) && + ((part->ren_as == PART_DRAW_OB && part->dup_ob) || + (part->ren_as == PART_DRAW_GR && part->dup_group && part->dup_group->gobject.first))) { /* if we have a hair particle system, use the path cache */ if(part->type == PART_HAIR) { @@ -801,7 +802,7 @@ static void new_particle_duplilist(ListBase *lb, ID *id, Scene *scene, Object *p psys->lattice = psys_get_lattice(scene, par, psys); /* gather list of objects or single object */ - if(part->draw_as==PART_DRAW_GR) { + if(part->ren_as==PART_DRAW_GR) { group_handle_recalc_and_update(scene, par, part->dup_group); for(go=part->dup_group->gobject.first; go; go=go->next) @@ -847,7 +848,7 @@ static void new_particle_duplilist(ListBase *lb, ID *id, Scene *scene, Object *p size = psys_get_child_size(psys, cpa, ctime, 0); } - if(part->draw_as==PART_DRAW_GR) { + if(part->ren_as==PART_DRAW_GR) { /* for groups, pick the object based on settings */ if(part->draw&PART_DRAW_RAND_GR) b= BLI_rand() % totgroup; @@ -891,7 +892,7 @@ static void new_particle_duplilist(ListBase *lb, ID *id, Scene *scene, Object *p pamat[3][3]= 1.0f; } - if(part->draw_as==PART_DRAW_GR && psys->part->draw & PART_DRAW_WHOLE_GR) { + if(part->ren_as==PART_DRAW_GR && psys->part->draw & PART_DRAW_WHOLE_GR) { 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); @@ -927,7 +928,7 @@ static void new_particle_duplilist(ListBase *lb, ID *id, Scene *scene, Object *p } /* restore objects since they were changed in where_is_object_time */ - if(part->draw_as==PART_DRAW_GR) { + if(part->ren_as==PART_DRAW_GR) { for(a=0; a<totgroup; a++) *(oblist[a])= obcopylist[a]; } diff --git a/source/blender/blenkernel/intern/anim_sys.c b/source/blender/blenkernel/intern/anim_sys.c index 30dcb383ef6..e4882d8f555 100644 --- a/source/blender/blenkernel/intern/anim_sys.c +++ b/source/blender/blenkernel/intern/anim_sys.c @@ -1,10 +1,37 @@ -/* Testing code for new animation system in 2.5 - * Copyright 2009, Joshua Leung +/** + * $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) 2009 Blender Foundation, Joshua Leung + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): Joshua Leung (full recode) + * + * ***** END GPL LICENSE BLOCK ***** */ #include <stdio.h> #include <string.h> #include <stddef.h> +#include <float.h> +#include <math.h> #include "MEM_guardedalloc.h" @@ -12,9 +39,12 @@ #include "BLI_arithb.h" #include "BLI_dynstr.h" +#include "DNA_anim_types.h" + #include "BKE_animsys.h" #include "BKE_action.h" #include "BKE_fcurve.h" +#include "BKE_nla.h" #include "BKE_global.h" #include "BKE_main.h" #include "BKE_utildefines.h" @@ -22,7 +52,7 @@ #include "RNA_access.h" #include "RNA_types.h" -#include "DNA_anim_types.h" +#include "nla_private.h" /* ***************************************** */ /* AnimData API */ @@ -43,6 +73,7 @@ static short id_has_animdata (ID *id) case ID_OB: case ID_CU: case ID_KE: + case ID_PA: case ID_MA: case ID_TE: case ID_NT: case ID_LA: case ID_CA: case ID_WO: case ID_SCE: @@ -115,7 +146,13 @@ void BKE_free_animdata (ID *id) /* unlink action (don't free, as it's in its own list) */ if (adt->action) adt->action->id.us--; + /* same goes for the temporarily displaced action */ + if (adt->tmpact) + adt->tmpact->id.us--; + /* free nla data */ + free_nladata(&adt->nla_tracks); + /* free drivers - stored as a list of F-Curves */ free_fcurves(&adt->drivers); @@ -145,9 +182,10 @@ AnimData *BKE_copy_animdata (AnimData *adt) // XXX review this... it might not be optimal behaviour yet... //id_us_plus((ID *)dadt->action); dadt->action= copy_action(adt->action); + dadt->tmpact= copy_action(adt->tmpact); /* duplicate NLA data */ - // XXX todo... + copy_nladata(&dadt->nla_tracks, &adt->nla_tracks); /* duplicate drivers (F-Curves) */ copy_fcurves(&dadt->drivers, &adt->drivers); @@ -354,10 +392,10 @@ void BKE_keyingsets_free (ListBase *list) short animsys_remap_path (AnimMapper *remap, char *path, char **dst) { /* is there a valid remapping table to use? */ - if (remap) { + //if (remap) { /* find a matching entry... to use to remap */ // ...TODO... - } + //} /* nothing suitable found, so just set dst to look at path (i.e. no alloc/free needed) */ *dst= path; @@ -454,11 +492,14 @@ static void animsys_evaluate_fcurves (PointerRNA *ptr, ListBase *list, AnimMappe /* calculate then execute each curve */ for (fcu= list->first; fcu; fcu= fcu->next) { - /* check if this curve should be skipped */ - if ((fcu->flag & (FCURVE_MUTED|FCURVE_DISABLED)) == 0) - { - calculate_fcurve(fcu, ctime); - animsys_execute_fcurve(ptr, remap, fcu); + /* check if this F-Curve doesn't belong to a muted group */ + if ((fcu->grp == NULL) || (fcu->grp->flag & AGRP_MUTED)==0) { + /* check if this curve should be skipped */ + if ((fcu->flag & (FCURVE_MUTED|FCURVE_DISABLED)) == 0) + { + calculate_fcurve(fcu, ctime); + animsys_execute_fcurve(ptr, remap, fcu); + } } } } @@ -480,7 +521,6 @@ static void animsys_evaluate_drivers (PointerRNA *ptr, AnimData *adt, float ctim short ok= 0; /* check if this driver's curve should be skipped */ - // FIXME: maybe we shouldn't check for muted, though that would make things more confusing, as there's already too many ways to disable? if ((fcu->flag & (FCURVE_MUTED|FCURVE_DISABLED)) == 0) { /* check if driver itself is tagged for recalculation */ @@ -513,6 +553,10 @@ void animsys_evaluate_action_group (PointerRNA *ptr, bAction *act, bActionGroup if ELEM(NULL, act, agrp) return; if ((remap) && (remap->target != act)) remap= NULL; + /* if group is muted, don't evaluated any of the F-Curve */ + if (agrp->flag & AGRP_MUTED) + return; + /* calculate then execute each curve */ for (fcu= agrp->channels.first; (fcu) && (fcu->grp == agrp); fcu= fcu->next) { @@ -539,152 +583,603 @@ void animsys_evaluate_action (PointerRNA *ptr, bAction *act, AnimMapper *remap, /* ***************************************** */ /* NLA System - Evaluation */ -/* used for list of strips to accumulate at current time */ -typedef struct NlaEvalStrip { - struct NlaEvalStrip *next, *prev; - - NlaTrack *track; /* track that this strip belongs to */ - NlaStrip *strip; /* strip that's being used */ - NlaStrip *sblend; /* strip that's being blended towards (if applicable) */ - - short track_index; /* the index of the track within the list */ - short strip_mode; /* which end of the strip are we looking at */ -} NlaEvalStrip; - -/* bNlaEvalStrip->strip_mode */ -enum { - NES_TIME_BEFORE = -1, - NES_TIME_WITHIN, - NES_TIME_AFTER, - NES_TIME_AFTER_BLEND -} eNlaEvalStrip_StripMode; +/* calculate influence of strip based for given frame based on blendin/out values */ +static float nlastrip_get_influence (NlaStrip *strip, float cframe) +{ + /* sanity checks - normalise the blendin/out values? */ + strip->blendin= (float)fabs(strip->blendin); + strip->blendout= (float)fabs(strip->blendout); + + /* result depends on where frame is in respect to blendin/out values */ + if (IS_EQ(strip->blendin, 0)==0 && (cframe <= (strip->start + strip->blendin))) { + /* there is some blend-in */ + return (float)fabs(cframe - strip->start) / (strip->blendin); + } + else if (IS_EQ(strip->blendout, 0)==0 && (cframe >= (strip->end - strip->blendout))) { + /* there is some blend-out */ + return (float)fabs(strip->end - cframe) / (strip->blendout); + } + else { + /* in the middle of the strip, we should be full strength */ + return 1.0f; + } +} +/* evaluate the evaluation time and influence for the strip, storing the results in the strip */ +static void nlastrip_evaluate_controls (NlaStrip *strip, float ctime) +{ + /* firstly, analytically generate values for influence and time (if applicable) */ + if ((strip->flag & NLASTRIP_FLAG_USR_TIME) == 0) + strip->strip_time= nlastrip_get_frame(strip, ctime, NLATIME_CONVERT_EVAL); + if ((strip->flag & NLASTRIP_FLAG_USR_INFLUENCE) == 0) + strip->influence= nlastrip_get_influence(strip, ctime); + + /* now strip's evaluate F-Curves for these settings (if applicable) */ + if (strip->fcurves.first) { + PointerRNA strip_ptr; + + /* create RNA-pointer needed to set values */ + RNA_pointer_create(NULL, &RNA_NlaStrip, strip, &strip_ptr); + + /* execute these settings as per normal */ + animsys_evaluate_fcurves(&strip_ptr, &strip->fcurves, NULL, ctime); + } +} -/* temp channel for accumulating data from NLA (avoids needing to clear all values first) */ -// TODO: maybe this will be used as the 'cache' stuff needed for editable values too? -typedef struct NlaEvalChannel { - struct NlaEvalChannel *next, *prev; +/* gets the strip active at the current time for a list of strips for evaluation purposes */ +NlaEvalStrip *nlastrips_ctime_get_strip (ListBase *list, ListBase *strips, short index, float ctime) +{ + NlaStrip *strip, *estrip=NULL; + NlaEvalStrip *nes; + short side= 0; - char *path; /* ready-to-use path (i.e. remapped already) */ - int array_index; /* if applicable... */ + /* loop over strips, checking if they fall within the range */ + for (strip= strips->first; strip; strip= strip->next) { + /* check if current time occurs within this strip */ + if (IN_RANGE_INCL(ctime, strip->start, strip->end)) { + /* this strip is active, so try to use it */ + estrip= strip; + side= NES_TIME_WITHIN; + break; + } + + /* if time occurred before current strip... */ + if (ctime < strip->start) { + if (strip == strips->first) { + /* before first strip - only try to use it if it extends backwards in time too */ + if (strip->extendmode == NLASTRIP_EXTEND_HOLD) + estrip= strip; + + /* side is 'before' regardless of whether there's a useful strip */ + side= NES_TIME_BEFORE; + } + else { + /* before next strip - previous strip has ended, but next hasn't begun, + * so blending mode depends on whether strip is being held or not... + * - only occurs when no transition strip added, otherwise the transition would have + * been picked up above... + */ + strip= strip->prev; + + if (strip->extendmode != NLASTRIP_EXTEND_NOTHING) + estrip= strip; + side= NES_TIME_AFTER; + } + break; + } + + /* if time occurred after current strip... */ + if (ctime > strip->end) { + /* only if this is the last strip should we do anything, and only if that is being held */ + if (strip == strips->last) { + if (strip->extendmode != NLASTRIP_EXTEND_NOTHING) + estrip= strip; + + side= NES_TIME_AFTER; + break; + } + + /* otherwise, skip... as the 'before' case will catch it more elegantly! */ + } + } - float value; /* value of this channel */ -} NlaEvalChannel; - + /* check if a valid strip was found + * - must not be muted (i.e. will have contribution + */ + if ((estrip == NULL) || (estrip->flag & NLASTRIP_FLAG_MUTED)) + return NULL; + + /* if ctime was not within the boundaries of the strip, clamp! */ + switch (side) { + case NES_TIME_BEFORE: /* extend first frame only */ + ctime= estrip->start; + break; + case NES_TIME_AFTER: /* extend last frame only */ + ctime= estrip->end; + break; + } + + /* evaluate strip's evaluation controls + * - skip if no influence (i.e. same effect as muting the strip) + * - negative influence is not supported yet... how would that be defined? + */ + // TODO: this sounds a bit hacky having a few isolated F-Curves stuck on some data it operates on... + nlastrip_evaluate_controls(estrip, ctime); + if (estrip->influence <= 0.0f) + return NULL; + + /* check if strip has valid data to evaluate, + * and/or perform any additional type-specific actions + */ + switch (estrip->type) { + case NLASTRIP_TYPE_CLIP: + /* clip must have some action to evaluate */ + if (estrip->act == NULL) + return NULL; + break; + case NLASTRIP_TYPE_TRANSITION: + /* there must be strips to transition from and to (i.e. prev and next required) */ + if (ELEM(NULL, estrip->prev, estrip->next)) + return NULL; + + /* evaluate controls for the relevant extents of the bordering strips... */ + nlastrip_evaluate_controls(estrip->prev, estrip->start); + nlastrip_evaluate_controls(estrip->next, estrip->end); + break; + } + + /* add to list of strips we need to evaluate */ + nes= MEM_callocN(sizeof(NlaEvalStrip), "NlaEvalStrip"); + + nes->strip= estrip; + nes->strip_mode= side; + nes->track_index= index; + nes->strip_time= estrip->strip_time; + + if (list) + BLI_addtail(list, nes); + + return nes; +} /* ---------------------- */ -/* evaluate the F-Curves controlling settings for the NLA-strips (currently, not relinkable) */ -static void nlastrip_evaluate_fcurves (NlaStrip *strip, float ctime) +/* find an NlaEvalChannel that matches the given criteria + * - ptr and prop are the RNA data to find a match for + */ +static NlaEvalChannel *nlaevalchan_find_match (ListBase *channels, PointerRNA *ptr, PropertyRNA *prop, int array_index) { - //PointerRNA actstrip_ptr; - //FCurve *fcu; + NlaEvalChannel *nec; - /* create RNA-pointer needed to set values */ - //RNA_pointer_create(NULL, &RNA_NlaStrip, strip, &actstrip_ptr); + /* sanity check */ + if (channels == NULL) + return NULL; + + /* loop through existing channels, checking for a channel which affects the same property */ + for (nec= channels->first; nec; nec= nec->next) { + /* - comparing the PointerRNA's is done by comparing the pointers + * to the actual struct the property resides in, since that all the + * other data stored in PointerRNA cannot allow us to definitively + * identify the data + */ + if ((nec->ptr.data == ptr->data) && (nec->prop == prop) && (nec->index == array_index)) + return nec; + } - /* execute these settings as per normal */ - //animsys_evaluate_fcurves(&actstrip_ptr, &strip->fcurves, NULL, ctime); + /* not found */ + return NULL; } +/* verify that an appropriate NlaEvalChannel for this F-Curve exists */ +static NlaEvalChannel *nlaevalchan_verify (PointerRNA *ptr, ListBase *channels, NlaEvalStrip *nes, FCurve *fcu, short *newChan) +{ + NlaEvalChannel *nec; + NlaStrip *strip= nes->strip; + PropertyRNA *prop; + PointerRNA new_ptr; + char *path = NULL; + short free_path=0; + + /* sanity checks */ + if (channels == NULL) + return NULL; + + /* get RNA pointer+property info from F-Curve for more convenient handling */ + /* get path, remapped as appropriate to work in its new environment */ + free_path= animsys_remap_path(strip->remap, fcu->rna_path, &path); + + /* a valid property must be available, and it must be animateable */ + if (RNA_path_resolve(ptr, path, &new_ptr, &prop) == 0) { + if (G.f & G_DEBUG) printf("NLA Strip Eval: Cannot resolve path \n"); + return NULL; + } + /* only ok if animateable */ + else if (RNA_property_animateable(&new_ptr, prop) == 0) { + if (G.f & G_DEBUG) printf("NLA Strip Eval: Property not animateable \n"); + return NULL; + } + + /* try to find a match */ + nec= nlaevalchan_find_match(channels, &new_ptr, prop, fcu->array_index); + + /* allocate a new struct for this if none found */ + if (nec == NULL) { + nec= MEM_callocN(sizeof(NlaEvalChannel), "NlaEvalChannel"); + *newChan= 1; + BLI_addtail(channels, nec); + + nec->ptr= new_ptr; + nec->prop= prop; + nec->index= fcu->array_index; + } + else + *newChan= 0; + + /* we can now return */ + return nec; +} -/* gets the strip active at the current time for a track */ -static void nlatrack_ctime_get_strip (ListBase *list, NlaTrack *nlt, short index, float ctime) +/* accumulate (i.e. blend) the given value on to the channel it affects */ +static void nlaevalchan_accumulate (NlaEvalChannel *nec, NlaEvalStrip *nes, short newChan, float value) { - NlaStrip *strip, *astrip=NULL, *bstrip=NULL; - NlaEvalStrip *nes; - short side= 0; + NlaStrip *strip= nes->strip; + short blendmode= strip->blendmode; + float inf= strip->influence; + + /* if channel is new, just store value regardless of blending factors, etc. */ + if (newChan) { + nec->value= value; + return; + } + + /* if this is being performed as part of transition evaluation, incorporate + * an additional weighting factor for the influence + */ + if (nes->strip_mode == NES_TIME_TRANSITION_END) + inf *= nes->strip_time; + + /* premultiply the value by the weighting factor */ + if (IS_EQ(inf, 0)) return; + value *= inf; + + /* perform blending */ + switch (blendmode) { + case NLASTRIP_MODE_ADD: + /* simply add the scaled value on to the stack */ + nec->value += value; + break; + + case NLASTRIP_MODE_SUBTRACT: + /* simply subtract the scaled value from the stack */ + nec->value -= value; + break; + + case NLASTRIP_MODE_MULTIPLY: + /* multiply the scaled value with the stack */ + nec->value *= value; + break; + + case NLASTRIP_MODE_REPLACE: + default: // TODO: do we really want to blend by default? it seems more uses might prefer add... + /* do linear interpolation + * - the influence of the accumulated data (elsewhere, that is called dstweight) + * is 1 - influence, since the strip's influence is srcweight + */ + nec->value= nec->value * (1.0f - inf) + value; + break; + } +} + +/* accumulate the results of a temporary buffer with the results of the full-buffer */ +static void nlaevalchan_buffers_accumulate (ListBase *channels, ListBase *tmp_buffer, NlaEvalStrip *nes) +{ + NlaEvalChannel *nec, *necn, *necd; - /* skip if track is muted */ - if (nlt->flag & NLATRACK_MUTED) + /* optimise - abort if no channels */ + if (tmp_buffer->first == NULL) return; - /* loop over strips, checking if they fall within the range */ - for (strip= nlt->strips.first; strip; strip= strip->next) { - /* only consider if: - * - current time occurs within strip's extents - * - current time occurs before strip (if it is the first) - * - current time occurs after strip (if hold is on) - * - current time occurs between strips (1st of those isn't holding) - blend! + /* accumulate results in tmp_channels buffer to the accumulation buffer */ + for (nec= tmp_buffer->first; nec; nec= necn) { + /* get pointer to next channel in case we remove the current channel from the temp-buffer */ + necn= nec->next; + + /* try to find an existing matching channel for this setting in the accumulation buffer */ + necd= nlaevalchan_find_match(channels, &nec->ptr, nec->prop, nec->index); + + /* if there was a matching channel already in the buffer, accumulate to it, + * otherwise, add the current channel to the buffer for efficiency */ - if (IN_RANGE(ctime, strip->start, strip->end)) { - astrip= strip; - side= NES_TIME_WITHIN; - break; + if (necd) + nlaevalchan_accumulate(necd, nes, 0, nec->value); + else { + BLI_remlink(tmp_buffer, nec); + BLI_addtail(channels, nec); } - else if (ctime < strip->start) { - if (strip == nlt->strips.first) { - astrip= strip; - side= NES_TIME_BEFORE; - break; - } - else { - astrip= strip->prev; - - if (astrip->flag & NLASTRIP_HOLDLASTFRAME) { - side= NES_TIME_AFTER; - break; - } - else { - bstrip= strip; - side= NES_TIME_AFTER_BLEND; - break; - } - } + } + + /* free temp-channels that haven't been assimilated into the buffer */ + BLI_freelistN(tmp_buffer); +} + +/* ---------------------- */ +/* F-Modifier stack joining/separation utilities - should we generalise these for BLI_listbase.h interface? */ + +/* Temporarily join two lists of modifiers together, storing the result in a third list */ +static void nlaeval_fmodifiers_join_stacks (ListBase *result, ListBase *list1, ListBase *list2) +{ + FModifier *fcm1, *fcm2; + + /* if list1 is invalid... */ + if ELEM(NULL, list1, list1->first) { + if (list2 && list2->first) { + result->first= list2->first; + result->last= list2->last; } } + /* if list 2 is invalid... */ + else if ELEM(NULL, list2, list2->first) { + result->first= list1->first; + result->last= list1->last; + } + else { + /* list1 should be added first, and list2 second, with the endpoints of these being the endpoints for result + * - the original lists must be left unchanged though, as we need that fact for restoring + */ + result->first= list1->first; + result->last= list2->last; + + fcm1= list1->last; + fcm2= list2->first; + + fcm1->next= fcm2; + fcm2->prev= fcm1; + } +} + +/* Split two temporary lists of modifiers */ +static void nlaeval_fmodifiers_split_stacks (ListBase *list1, ListBase *list2) +{ + FModifier *fcm1, *fcm2; - /* check if strip has been found (and whether it has data worth considering) */ - if (ELEM(NULL, astrip, astrip->act)) + /* if list1/2 is invalid... just skip */ + if ELEM(NULL, list1, list2) return; - if (astrip->flag & NLASTRIP_MUTE) + if ELEM(NULL, list1->first, list2->first) return; + + /* get endpoints */ + fcm1= list1->last; + fcm2= list2->first; - /* check if blending between strips */ - if (side == NES_TIME_AFTER_BLEND) { - /* blending between strips... so calculate influence+act_time of both */ - nlastrip_evaluate_fcurves(astrip, ctime); - nlastrip_evaluate_fcurves(bstrip, ctime); + /* clear their links */ + fcm1->next= NULL; + fcm2->prev= NULL; +} + +/* ---------------------- */ + +/* evaluate action-clip strip */ +static void nlastrip_evaluate_actionclip (PointerRNA *ptr, ListBase *channels, ListBase *modifiers, NlaEvalStrip *nes) +{ + ListBase tmp_modifiers = {NULL, NULL}; + NlaStrip *strip= nes->strip; + FCurve *fcu; + float evaltime; + + /* join this strip's modifiers to the parent's modifiers (own modifiers first) */ + nlaeval_fmodifiers_join_stacks(&tmp_modifiers, &strip->modifiers, modifiers); + + /* evaluate strip's modifiers which modify time to evaluate the base curves at */ + evaltime= evaluate_time_fmodifiers(&tmp_modifiers, NULL, 0.0f, strip->strip_time); + + /* evaluate all the F-Curves in the action, saving the relevant pointers to data that will need to be used */ + for (fcu= strip->act->curves.first; fcu; fcu= fcu->next) { + NlaEvalChannel *nec; + float value = 0.0f; + short newChan = -1; - if ((astrip->influence <= 0.0f) && (bstrip->influence <= 0.0f)) - return; + /* check if this curve should be skipped */ + if (fcu->flag & (FCURVE_MUTED|FCURVE_DISABLED)) + continue; + if ((fcu->grp) && (fcu->grp->flag & AGRP_MUTED)) + continue; + + /* evaluate the F-Curve's value for the time given in the strip + * NOTE: we use the modified time here, since strip's F-Curve Modifiers are applied on top of this + */ + value= evaluate_fcurve(fcu, evaltime); + + /* apply strip's F-Curve Modifiers on this value + * NOTE: we apply the strip's original evaluation time not the modified one (as per standard F-Curve eval) + */ + evaluate_value_fmodifiers(&tmp_modifiers, fcu, &value, strip->strip_time); + + + /* get an NLA evaluation channel to work with, and accumulate the evaluated value with the value(s) + * stored in this channel if it has been used already + */ + nec= nlaevalchan_verify(ptr, channels, nes, fcu, &newChan); + if (nec) + nlaevalchan_accumulate(nec, nes, newChan, value); + } + + /* unlink this strip's modifiers from the parent's modifiers again */ + nlaeval_fmodifiers_split_stacks(&strip->modifiers, modifiers); +} + +/* evaluate transition strip */ +static void nlastrip_evaluate_transition (PointerRNA *ptr, ListBase *channels, ListBase *modifiers, NlaEvalStrip *nes) +{ + ListBase tmp_channels = {NULL, NULL}; + ListBase tmp_modifiers = {NULL, NULL}; + NlaEvalStrip tmp_nes; + NlaStrip *s1, *s2; + + /* join this strip's modifiers to the parent's modifiers (own modifiers first) */ + nlaeval_fmodifiers_join_stacks(&tmp_modifiers, &nes->strip->modifiers, modifiers); + + /* get the two strips to operate on + * - we use the endpoints of the strips directly flanking our strip + * using these as the endpoints of the transition (destination and source) + * - these should have already been determined to be valid... + * - if this strip is being played in reverse, we need to swap these endpoints + * otherwise they will be interpolated wrong + */ + if (nes->strip->flag & NLASTRIP_FLAG_REVERSE) { + s1= nes->strip->next; + s2= nes->strip->prev; } else { - /* calculate/set the influence+act_time of this strip - don't consider if 0 influence */ - nlastrip_evaluate_fcurves(astrip, ctime); - - if (astrip->influence <= 0.0f) - return; + s1= nes->strip->prev; + s2= nes->strip->next; } + /* prepare template for 'evaluation strip' + * - based on the transition strip's evaluation strip data + * - strip_mode is NES_TIME_TRANSITION_* based on which endpoint + * - strip_time is the 'normalised' (i.e. in-strip) time for evaluation, + * which doubles up as an additional weighting factor for the strip influences + * which allows us to appear to be 'interpolating' between the two extremes + */ + tmp_nes= *nes; - /* allocate new eval-strip for this strip + add to stack */ - nes= MEM_callocN(sizeof(NlaEvalStrip), "NlaEvalStrip"); + /* evaluate these strips into a temp-buffer (tmp_channels) */ + // FIXME: modifier evalation here needs some work... + /* first strip */ + tmp_nes.strip_mode= NES_TIME_TRANSITION_START; + tmp_nes.strip= s1; + nlastrip_evaluate(ptr, &tmp_channels, &tmp_modifiers, &tmp_nes); - nes->track= nlt; - nes->strip= astrip; - nes->sblend= bstrip; - nes->track_index= index; - nes->strip_mode= side; + /* second strip */ + tmp_nes.strip_mode= NES_TIME_TRANSITION_END; + tmp_nes.strip= s2; + nlastrip_evaluate(ptr, &tmp_channels, &tmp_modifiers, &tmp_nes); + + + /* assumulate temp-buffer and full-buffer, using the 'real' strip */ + nlaevalchan_buffers_accumulate(channels, &tmp_channels, nes); - BLI_addtail(list, nes); + /* unlink this strip's modifiers from the parent's modifiers again */ + nlaeval_fmodifiers_split_stacks(&nes->strip->modifiers, modifiers); } -/* ---------------------- */ +/* evaluate meta-strip */ +static void nlastrip_evaluate_meta (PointerRNA *ptr, ListBase *channels, ListBase *modifiers, NlaEvalStrip *nes) +{ + ListBase tmp_channels = {NULL, NULL}; + ListBase tmp_modifiers = {NULL, NULL}; + NlaStrip *strip= nes->strip; + NlaEvalStrip *tmp_nes; + float evaltime; + + /* meta-strip was calculated normally to have some time to be evaluated at + * and here we 'look inside' the meta strip, treating it as a decorated window to + * it's child strips, which get evaluated as if they were some tracks on a strip + * (but with some extra modifiers to apply). + * + * NOTE: keep this in sync with animsys_evaluate_nla() + */ + + /* join this strip's modifiers to the parent's modifiers (own modifiers first) */ + nlaeval_fmodifiers_join_stacks(&tmp_modifiers, &strip->modifiers, modifiers); + + /* find the child-strip to evaluate */ + evaltime= (nes->strip_time * (strip->end - strip->start)) + strip->start; + tmp_nes= nlastrips_ctime_get_strip(NULL, &strip->strips, -1, evaltime); + if (tmp_nes == NULL) + return; + + /* evaluate child-strip into tmp_channels buffer before accumulating + * in the accumulation buffer + */ + nlastrip_evaluate(ptr, &tmp_channels, &tmp_modifiers, tmp_nes); + + /* assumulate temp-buffer and full-buffer, using the 'real' strip */ + nlaevalchan_buffers_accumulate(channels, &tmp_channels, nes); + + /* free temp eval-strip */ + MEM_freeN(tmp_nes); + + /* unlink this strip's modifiers from the parent's modifiers again */ + nlaeval_fmodifiers_split_stacks(&strip->modifiers, modifiers); +} /* evaluates the given evaluation strip */ -// FIXME: will we need the evaluation cache table set up to blend stuff in? -// TODO: only evaluate here, but flush in one go using the accumulated channels at end... -static void nlastrip_ctime_evaluate (ListBase *channels, NlaEvalStrip *nes, float ctime) +void nlastrip_evaluate (PointerRNA *ptr, ListBase *channels, ListBase *modifiers, NlaEvalStrip *nes) { - // 1. (in old code) was to extract 'IPO-channels' from actions - // 2. blend between the 'accumulated' data, and the new data + NlaStrip *strip= nes->strip; + + /* to prevent potential infinite recursion problems (i.e. transition strip, beside meta strip containing a transition + * several levels deep inside it), we tag the current strip as being evaluated, and clear this when we leave + */ + // TODO: be careful with this flag, since some edit tools may be running and have set this while animplayback was running + if (strip->flag & NLASTRIP_FLAG_EDIT_TOUCHED) + return; + strip->flag |= NLASTRIP_FLAG_EDIT_TOUCHED; + + /* actions to take depend on the type of strip */ + switch (strip->type) { + case NLASTRIP_TYPE_CLIP: /* action-clip */ + nlastrip_evaluate_actionclip(ptr, channels, modifiers, nes); + break; + case NLASTRIP_TYPE_TRANSITION: /* transition */ + nlastrip_evaluate_transition(ptr, channels, modifiers, nes); + break; + case NLASTRIP_TYPE_META: /* meta */ + nlastrip_evaluate_meta(ptr, channels, modifiers, nes); + break; + } + + /* clear temp recursion safe-check */ + strip->flag &= ~NLASTRIP_FLAG_EDIT_TOUCHED; } /* write the accumulated settings to */ -static void nladata_flush_channels (PointerRNA *ptr, ListBase *channels) +void nladata_flush_channels (ListBase *channels) { + NlaEvalChannel *nec; + + /* sanity checks */ + if (channels == NULL) + return; + /* for each channel with accumulated values, write its value on the property it affects */ + for (nec= channels->first; nec; nec= nec->next) { + PointerRNA *ptr= &nec->ptr; + PropertyRNA *prop= nec->prop; + int array_index= nec->index; + float value= nec->value; + + /* write values - see animsys_write_rna_setting() to sync the code */ + switch (RNA_property_type(prop)) + { + case PROP_BOOLEAN: + if (RNA_property_array_length(prop)) + RNA_property_boolean_set_index(ptr, prop, array_index, (int)value); + else + RNA_property_boolean_set(ptr, prop, (int)value); + break; + case PROP_INT: + if (RNA_property_array_length(prop)) + RNA_property_int_set_index(ptr, prop, array_index, (int)value); + else + RNA_property_int_set(ptr, prop, (int)value); + break; + case PROP_FLOAT: + if (RNA_property_array_length(prop)) + RNA_property_float_set_index(ptr, prop, array_index, value); + else + RNA_property_float_set(ptr, prop, value); + break; + case PROP_ENUM: + RNA_property_enum_set(ptr, prop, (int)value); + break; + default: + // can't do anything with other types of property.... + break; + } + } } /* ---------------------- */ @@ -702,9 +1197,26 @@ static void animsys_evaluate_nla (PointerRNA *ptr, AnimData *adt, float ctime) ListBase echannels= {NULL, NULL}; NlaEvalStrip *nes; + // TODO: need to zero out all channels used, otherwise we have problems with threadsafety + // and also when the user jumps between different times instead of moving sequentially... + /* 1. get the stack of strips to evaluate at current time (influence calculated here) */ - for (nlt=adt->nla_tracks.first; nlt; nlt=nlt->next, track_index++) - nlatrack_ctime_get_strip(&estrips, nlt, track_index, ctime); + for (nlt=adt->nla_tracks.first; nlt; nlt=nlt->next, track_index++) { + /* if tweaking is on and this strip is the tweaking track, stop on this one */ + if ((adt->flag & ADT_NLA_EDIT_ON) && (nlt->flag & NLATRACK_DISABLED)) + break; + + /* skip if we're only considering a track tagged 'solo' */ + if ((adt->flag & ADT_NLA_SOLO_TRACK) && (nlt->flag & NLATRACK_SOLO)==0) + continue; + /* skip if track is muted */ + if (nlt->flag & NLATRACK_MUTED) + continue; + + /* otherwise, get strip to evaluate for this channel */ + nes= nlastrips_ctime_get_strip(&estrips, &nlt->strips, track_index, ctime); + if (nes) nes->track= nlt; + } /* only continue if there are strips to evaluate */ if (estrips.first == NULL) @@ -713,10 +1225,10 @@ static void animsys_evaluate_nla (PointerRNA *ptr, AnimData *adt, float ctime) /* 2. for each strip, evaluate then accumulate on top of existing channels, but don't set values yet */ for (nes= estrips.first; nes; nes= nes->next) - nlastrip_ctime_evaluate(&echannels, nes, ctime); + nlastrip_evaluate(ptr, &echannels, NULL, nes); /* 3. flush effects of accumulating channels in NLA to the actual data they affect */ - nladata_flush_channels(ptr, &echannels); + nladata_flush_channels(&echannels); /* 4. free temporary evaluation data */ BLI_freelistN(&estrips); @@ -798,17 +1310,23 @@ void BKE_animsys_evaluate_animdata (ID *id, AnimData *adt, float ctime, short re * - NLA before Active Action, as Active Action behaves as 'tweaking track' * that overrides 'rough' work in NLA */ + // TODO: need to double check that this all works correctly if ((recalc & ADT_RECALC_ANIM) || (adt->recalc & ADT_RECALC_ANIM)) { /* evaluate NLA data */ if ((adt->nla_tracks.first) && !(adt->flag & ADT_NLA_EVAL_OFF)) { + /* evaluate NLA-stack */ animsys_evaluate_nla(&id_ptr, adt, ctime); + + /* evaluate 'active' Action (may be tweaking track) on top of results of NLA-evaluation + * - only do this if we're not exclusively evaluating the 'solo' NLA-track + */ + if ((adt->action) && !(adt->flag & ADT_NLA_SOLO_TRACK)) + animsys_evaluate_action(&id_ptr, adt->action, adt->remap, ctime); } - - /* evaluate Action data */ - // FIXME: what if the solo track was not tweaking one, then nla-solo should be checked too? - if (adt->action) + /* evaluate Active Action only */ + else if (adt->action) animsys_evaluate_action(&id_ptr, adt->action, adt->remap, ctime); /* reset tag */ @@ -875,14 +1393,29 @@ void BKE_animsys_evaluate_all_animation (Main *main, float ctime) EVAL_ANIM_IDS(main->camera.first, ADT_RECALC_ANIM); /* shapekeys */ + // TODO: we probably need the same hack as for curves (ctime-hack) EVAL_ANIM_IDS(main->key.first, ADT_RECALC_ANIM); /* curves */ - // TODO... + /* we need to perform a special hack here to ensure that the ctime + * value of the curve gets set in case there's no animation for that + * - it needs to be set before animation is evaluated just so that + * animation can successfully override... + */ + for (id= main->curve.first; id; id= id->next) { + AnimData *adt= BKE_animdata_from_id(id); + Curve *cu= (Curve *)id; + + cu->ctime= ctime; + BKE_animsys_evaluate_animdata(id, adt, ctime, ADT_RECALC_ANIM); + } /* meshes */ // TODO... + /* particles */ + EVAL_ANIM_IDS(main->particle.first, ADT_RECALC_ANIM); + /* objects */ /* ADT_RECALC_ANIM doesn't need to be supplied here, since object AnimData gets * this tagged by Depsgraph on framechange diff --git a/source/blender/blenkernel/intern/blender.c b/source/blender/blenkernel/intern/blender.c index 5fc7d18689d..d3d21018c1c 100644 --- a/source/blender/blenkernel/intern/blender.c +++ b/source/blender/blenkernel/intern/blender.c @@ -221,7 +221,6 @@ static void clear_global(void) { // extern short winqueue_break; /* screen.c */ -// XXX freeAllRad(); fastshade_free_render(); /* lamps hang otherwise */ free_main(G.main); /* free all lib data */ diff --git a/source/blender/blenkernel/intern/booleanops.c b/source/blender/blenkernel/intern/booleanops.c index ef3a99f5fd8..b6d1f7612b0 100644 --- a/source/blender/blenkernel/intern/booleanops.c +++ b/source/blender/blenkernel/intern/booleanops.c @@ -161,6 +161,7 @@ typedef struct { DerivedMesh *dm; int pos; int offset; + int flip; } FaceIt; static void FaceIt_Destruct(CSG_FaceIteratorDescriptor * iterator) @@ -187,9 +188,15 @@ static void FaceIt_Fill(CSG_IteratorPtr it, CSG_IFace *face) MFace *mfaces = face_it->dm->getTessFaceArray(face_it->dm); MFace *mface = &mfaces[face_it->pos]; - face->vertex_index[0] = mface->v1; + /* reverse face vertices if necessary */ face->vertex_index[1] = mface->v2; + if( face_it->flip == 0 ) { + face->vertex_index[0] = mface->v1; face->vertex_index[2] = mface->v3; + } else { + face->vertex_index[2] = mface->v1; + face->vertex_index[0] = mface->v3; + } if (mface->v4) { face->vertex_index[3] = mface->v4; face->vertex_number = 4; @@ -213,7 +220,7 @@ static void FaceIt_Reset(CSG_IteratorPtr it) } static void FaceIt_Construct( - CSG_FaceIteratorDescriptor *output, DerivedMesh *dm, int offset) + CSG_FaceIteratorDescriptor *output, DerivedMesh *dm, int offset, Object *ob) { FaceIt *it; if (output == 0) return; @@ -228,6 +235,25 @@ static void FaceIt_Construct( it->offset = offset; it->pos = 0; + /* determine if we will need to reverse order of face vertices */ + if (ob->size[0] < 0.0f) { + if (ob->size[1] < 0.0f && ob->size[2] < 0.0f) { + it->flip = 1; + } else if (ob->size[1] >= 0.0f && ob->size[2] >= 0.0f) { + it->flip = 1; + } else { + it->flip = 0; + } + } else { + if (ob->size[1] < 0.0f && ob->size[2] < 0.0f) { + it->flip = 0; + } else if (ob->size[1] >= 0.0f && ob->size[2] >= 0.0f) { + it->flip = 0; + } else { + it->flip = 1; + } + } + // assign iterator function pointers. output->Step = FaceIt_Step; output->Fill = FaceIt_Fill; @@ -427,7 +453,7 @@ static void BuildMeshDescriptors( struct CSG_VertexIteratorDescriptor * vertex_it) { VertexIt_Construct(vertex_it,dm, ob); - FaceIt_Construct(face_it,dm,face_offset); + FaceIt_Construct(face_it,dm,face_offset,ob); } static void FreeMeshDescriptors( diff --git a/source/blender/blenkernel/intern/cdderivedmesh.c b/source/blender/blenkernel/intern/cdderivedmesh.c index 1e12fcb2378..120e1360d48 100644 --- a/source/blender/blenkernel/intern/cdderivedmesh.c +++ b/source/blender/blenkernel/intern/cdderivedmesh.c @@ -1590,7 +1590,7 @@ void CDDM_calc_edges(DerivedMesh *dm) BLI_edgehashIterator_free(ehi); /* free old CustomData and assign new one */ - CustomData_free(&dm->edgeData, dm->numVertData); + CustomData_free(&dm->edgeData, dm->numEdgeData); dm->edgeData = edgeData; dm->numEdgeData = numEdges; diff --git a/source/blender/blenkernel/intern/cloth.c b/source/blender/blenkernel/intern/cloth.c index d0509c35ee0..b16a046cd58 100644 --- a/source/blender/blenkernel/intern/cloth.c +++ b/source/blender/blenkernel/intern/cloth.c @@ -33,6 +33,7 @@ #include "DNA_mesh_types.h" #include "DNA_object_force.h" #include "DNA_scene_types.h" +#include "DNA_particle_types.h" #include "BKE_deform.h" #include "BKE_DerivedMesh.h" @@ -42,6 +43,7 @@ #include "BKE_object.h" #include "BKE_modifier.h" #include "BKE_utildefines.h" +#include "BKE_particle.h" #include "BKE_pointcache.h" @@ -339,78 +341,102 @@ void bvhselftree_update_from_cloth(ClothModifierData *clmd, int moving) } int modifiers_indexInObject(Object *ob, ModifierData *md_seek); +static void cloth_write_state(int index, void *cloth_v, float *data) +{ + Cloth *cloth= cloth_v; + ClothVertex *vert = cloth->verts + index; -int cloth_read_cache(Object *ob, ClothModifierData *clmd, float framenr) + memcpy(data, vert->x, 3 * sizeof(float)); + memcpy(data + 3, vert->xconst, 3 * sizeof(float)); + memcpy(data + 6, vert->v, 3 * sizeof(float)); +} +static void cloth_read_state(int index, void *cloth_v, float *data) { - PTCacheID pid; - PTCacheFile *pf; - Cloth *cloth = clmd->clothObject; - unsigned int a, ret = 1; + Cloth *cloth= cloth_v; + ClothVertex *vert = cloth->verts + index; - 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; + memcpy(vert->x, data, 3 * sizeof(float)); + memcpy(vert->xconst, data + 3, 3 * sizeof(float)); + memcpy(vert->v, data + 6, 3 * sizeof(float)); } +static void cloth_cache_interpolate(int index, void *cloth_v, float frs_sec, float cfra, float cfra1, float cfra2, float *data1, float *data2) +{ + Cloth *cloth= cloth_v; + ClothVertex *vert = cloth->verts + index; + ParticleKey keys[4]; + float dfra; -void cloth_clear_cache(Object *ob, ClothModifierData *clmd, float framenr) + if(cfra1 == cfra2) { + cloth_read_state(index, cloth, data1); + return; + } + + memcpy(keys[1].co, data1, 3 * sizeof(float)); + memcpy(keys[1].vel, data1 + 6, 3 * sizeof(float)); + + memcpy(keys[2].co, data2, 3 * sizeof(float)); + memcpy(keys[2].vel, data2 + 6, 3 * sizeof(float)); + + dfra = cfra2 - cfra1; + + VecMulf(keys[1].vel, dfra); + VecMulf(keys[2].vel, dfra); + + psys_interpolate_particle(-1, keys, (cfra - cfra1) / dfra, keys, 1); + + VecMulf(keys->vel, 1.0f / dfra); + + memcpy(vert->x, keys->co, 3 * sizeof(float)); + memcpy(vert->v, keys->vel, 3 * sizeof(float)); + + /* not sure what to do with this - jahka */ + memcpy(vert->xconst, data1 + 3, 3 * sizeof(float)); +} +void cloth_write_cache(Object *ob, ClothModifierData *clmd, int cfra) { + PTCacheWriter writer; 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); + writer.calldata = clmd->clothObject; + writer.cfra = cfra; + writer.set_elem = cloth_write_state; + writer.pid = &pid; + writer.totelem = clmd->clothObject->numverts; + + BKE_ptcache_write_cache(&writer); } -void cloth_write_cache(Object *ob, ClothModifierData *clmd, float framenr) +int cloth_read_cache(Scene *scene, Object *ob, ClothModifierData *clmd, float cfra, int *old_framenr) { - Cloth *cloth = clmd->clothObject; + PTCacheReader reader; PTCacheID pid; - PTCacheFile *pf; - unsigned int a; - if(!cloth) - return; + BKE_ptcache_id_from_cloth(&pid, ob, clmd); + + reader.calldata = clmd->clothObject; + reader.cfra = cfra; + reader.interpolate_elem = cloth_cache_interpolate; + reader.old_frame = old_framenr; + reader.pid = &pid; + reader.scene = scene; + reader.set_elem = cloth_read_state; + reader.totelem = clmd->clothObject->numverts; + + return BKE_ptcache_read_cache(&reader); +} +void cloth_clear_cache(Object *ob, ClothModifierData *clmd, float framenr) +{ + PTCacheID pid; BKE_ptcache_id_from_cloth(&pid, ob, clmd); - pf = BKE_ptcache_file_open(&pid, PTCACHE_FILE_WRITE, framenr); - if(!pf) + + // don't do anything as long as we're in editmode! + if(pid.cache->flag & PTCACHE_BAKE_EDIT_ACTIVE) 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); + BKE_ptcache_id_clear(&pid, PTCACHE_CLEAR_AFTER, framenr); } static int do_init_cloth(Object *ob, ClothModifierData *clmd, DerivedMesh *result, int framenr) @@ -486,6 +512,7 @@ DerivedMesh *clothModifier_do(ClothModifierData *clmd, Scene *scene, Object *ob, PTCacheID pid; float timescale; int framedelta, framenr, startframe, endframe; + int cache_result, old_framenr; clmd->scene= scene; /* nice to pass on later :) */ framenr= (int)scene->r.cfra; @@ -499,6 +526,7 @@ DerivedMesh *clothModifier_do(ClothModifierData *clmd, Scene *scene, Object *ob, if(!result) { cache->flag &= ~PTCACHE_SIMULATION_VALID; cache->simframe= 0; + cache->last_exact= 0; return dm; } @@ -510,6 +538,7 @@ DerivedMesh *clothModifier_do(ClothModifierData *clmd, Scene *scene, Object *ob, if(result->getNumVerts(result) != clmd->clothObject->numverts) { cache->flag &= ~PTCACHE_SIMULATION_VALID; cache->simframe= 0; + cache->last_exact= 0; return result; } } @@ -521,6 +550,7 @@ DerivedMesh *clothModifier_do(ClothModifierData *clmd, Scene *scene, Object *ob, if(BKE_ptcache_get_continue_physics()) { cache->flag &= ~PTCACHE_SIMULATION_VALID; cache->simframe= 0; + cache->last_exact= 0; /* do simulation */ if(!do_init_cloth(ob, clmd, result, framenr)) @@ -536,6 +566,7 @@ DerivedMesh *clothModifier_do(ClothModifierData *clmd, Scene *scene, Object *ob, if(framenr < startframe) { cache->flag &= ~PTCACHE_SIMULATION_VALID; cache->simframe= 0; + cache->last_exact= 0; return result; } else if(framenr > endframe) { @@ -552,7 +583,9 @@ DerivedMesh *clothModifier_do(ClothModifierData *clmd, Scene *scene, Object *ob, return result; /* try to read from cache */ - if(cloth_read_cache(ob, clmd, framenr)) { + cache_result = cloth_read_cache(scene, ob, clmd, framenr, &old_framenr); + + if(cache_result == PTCACHE_READ_EXACT || cache_result == PTCACHE_READ_INTERPOLATED) { cache->flag |= PTCACHE_SIMULATION_VALID; cache->simframe= framenr; @@ -561,25 +594,40 @@ DerivedMesh *clothModifier_do(ClothModifierData *clmd, Scene *scene, Object *ob, return result; } + else if(cache_result==PTCACHE_READ_OLD) { + BKE_ptcache_id_reset(scene, &pid, PTCACHE_RESET_FREE); + + implicit_set_positions(clmd); + + cache->flag |= PTCACHE_SIMULATION_VALID; + cache->simframe= old_framenr; + } 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; + cache->last_exact= 0; return result; } if(framenr == startframe) { + if(cache->flag & PTCACHE_REDO_NEEDED) { + BKE_ptcache_id_reset(scene, &pid, PTCACHE_RESET_OUTDATED); + do_init_cloth(ob, clmd, result, framenr); + } 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) { + else { /* if on second frame, write cache for first frame */ - if(framenr == startframe+1) + if(cache->simframe == startframe && (cache->flag & PTCACHE_OUTDATED || cache->last_exact==0)) cloth_write_cache(ob, clmd, startframe); + clmd->sim_parms->timescale *= framenr - cache->simframe; + /* do simulation */ cache->flag |= PTCACHE_SIMULATION_VALID; cache->simframe= framenr; @@ -587,16 +635,13 @@ DerivedMesh *clothModifier_do(ClothModifierData *clmd, Scene *scene, Object *ob, if(!do_step_cloth(ob, clmd, result, framenr)) { cache->flag &= ~PTCACHE_SIMULATION_VALID; cache->simframe= 0; + cache->last_exact= 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; } diff --git a/source/blender/blenkernel/intern/constraint.c b/source/blender/blenkernel/intern/constraint.c index 059687030cb..f1420db706f 100644 --- a/source/blender/blenkernel/intern/constraint.c +++ b/source/blender/blenkernel/intern/constraint.c @@ -3029,7 +3029,7 @@ static void transform_evaluate (bConstraint *con, bConstraintOb *cob, ListBase * if (VALID_CONS_TARGET(ct)) { float loc[3], eul[3], size[3]; float dvec[3], sval[3]; - short i; + int i; /* obtain target effect */ switch (data->from) { @@ -3076,7 +3076,7 @@ static void transform_evaluate (bConstraint *con, bConstraintOb *cob, ListBase * switch (data->to) { case 2: /* scaling */ for (i=0; i<3; i++) - size[i]= data->to_min[i] + (sval[data->map[i]] * (data->to_max[i] - data->to_min[i])); + size[i]= data->to_min[i] + (sval[(int)data->map[i]] * (data->to_max[i] - data->to_min[i])); break; case 1: /* rotation */ for (i=0; i<3; i++) { @@ -3086,7 +3086,7 @@ static void transform_evaluate (bConstraint *con, bConstraintOb *cob, ListBase * tmax= data->to_max[i]; /* all values here should be in degrees */ - eul[i]= tmin + (sval[data->map[i]] * (tmax - tmin)); + eul[i]= tmin + (sval[(int)data->map[i]] * (tmax - tmin)); /* now convert final value back to radians */ eul[i] = (float)(eul[i] / 180 * M_PI); @@ -3095,7 +3095,7 @@ static void transform_evaluate (bConstraint *con, bConstraintOb *cob, ListBase * default: /* location */ /* get new location */ for (i=0; i<3; i++) - loc[i]= (data->to_min[i] + (sval[data->map[i]] * (data->to_max[i] - data->to_min[i]))); + loc[i]= (data->to_min[i] + (sval[(int)data->map[i]] * (data->to_max[i] - data->to_min[i]))); /* add original location back on (so that it can still be moved) */ VecAddf(loc, cob->matrix[3], loc); diff --git a/source/blender/blenkernel/intern/context.c b/source/blender/blenkernel/intern/context.c index ae541365b1e..1b499384886 100644 --- a/source/blender/blenkernel/intern/context.c +++ b/source/blender/blenkernel/intern/context.c @@ -280,6 +280,7 @@ void CTX_wm_menu_set(bContext *C, ARegion *menu) struct bContextDataResult { PointerRNA ptr; ListBase list; + const char **dir; }; static int ctx_data_get(bContext *C, const char *member, bContextDataResult *result) @@ -357,25 +358,33 @@ static int ctx_data_collection_get(const bContext *C, const char *member, ListBa return 1; } + list->first= NULL; + list->last= NULL; + return 0; } -PointerRNA CTX_data_pointer_get(bContext *C, const char *member) +PointerRNA CTX_data_pointer_get(const bContext *C, const char *member) { bContextDataResult result; - if(ctx_data_get((bContext*)C, member, &result)) { + if(ctx_data_get((bContext*)C, member, &result)) return result.ptr; - } - else { - PointerRNA ptr; - memset(&ptr, 0, sizeof(ptr)); - return ptr; - } + else + return PointerRNA_NULL; +} +PointerRNA CTX_data_pointer_get_type(const bContext *C, const char *member, StructRNA *type) +{ + PointerRNA ptr = CTX_data_pointer_get(C, member); + + if(ptr.data && RNA_struct_is_a(ptr.type, type)) + return ptr; + + return PointerRNA_NULL; } -ListBase CTX_data_collection_get(bContext *C, const char *member) +ListBase CTX_data_collection_get(const bContext *C, const char *member) { bContextDataResult result; @@ -389,7 +398,7 @@ ListBase CTX_data_collection_get(bContext *C, const char *member) } } -void CTX_data_get(bContext *C, const char *member, PointerRNA *r_ptr, ListBase *r_lb) +void CTX_data_get(const bContext *C, const char *member, PointerRNA *r_ptr, ListBase *r_lb) { bContextDataResult result; @@ -403,11 +412,75 @@ void CTX_data_get(bContext *C, const char *member, PointerRNA *r_ptr, ListBase * } } +static void data_dir_add(ListBase *lb, const char *member) +{ + LinkData *link; + + if(strcmp(member, "scene") == 0) /* exception */ + return; + + for(link=lb->first; link; link=link->next) + if(strcmp(link->data, member) == 0) + return; + + link= MEM_callocN(sizeof(LinkData), "LinkData"); + link->data= (void*)member; + BLI_addtail(lb, link); +} + +ListBase CTX_data_dir_get(const bContext *C) +{ + bContextDataResult result; + ListBase lb; + int a; + + memset(&lb, 0, sizeof(lb)); + + if(C->wm.store) { + bContextStoreEntry *entry; + + for(entry=C->wm.store->entries.first; entry; entry=entry->next) + data_dir_add(&lb, entry->name); + } + if(C->wm.region && C->wm.region->type && C->wm.region->type->context) { + memset(&result, 0, sizeof(result)); + C->wm.region->type->context(C, "", &result); + + if(result.dir) + for(a=0; result.dir[a]; a++) + data_dir_add(&lb, result.dir[a]); + } + if(C->wm.area && C->wm.area->type && C->wm.area->type->context) { + memset(&result, 0, sizeof(result)); + C->wm.area->type->context(C, "", &result); + + if(result.dir) + for(a=0; result.dir[a]; a++) + data_dir_add(&lb, result.dir[a]); + } + if(C->wm.screen && C->wm.screen->context) { + bContextDataCallback cb= C->wm.screen->context; + memset(&result, 0, sizeof(result)); + cb(C, "", &result); + + if(result.dir) + for(a=0; result.dir[a]; a++) + data_dir_add(&lb, result.dir[a]); + } + + return lb; +} + int CTX_data_equals(const char *member, const char *str) { return (strcmp(member, str) == 0); } +int CTX_data_dir(const char *member) +{ + return (strcmp(member, "") == 0); +} + void CTX_data_id_pointer_set(bContextDataResult *result, ID *id) { RNA_id_pointer_create(id, &result->ptr); @@ -451,6 +524,11 @@ int ctx_data_list_count(const bContext *C, int (*func)(const bContext*, ListBase return 0; } +void CTX_data_dir_set(bContextDataResult *result, const char **dir) +{ + result->dir= dir; +} + /* data context */ Main *CTX_data_main(const bContext *C) @@ -528,6 +606,16 @@ int CTX_data_visible_bases(const bContext *C, ListBase *list) return ctx_data_collection_get(C, "visible_bases", list); } +int CTX_data_selectable_objects(const bContext *C, ListBase *list) +{ + return ctx_data_collection_get(C, "selectable_objects", list); +} + +int CTX_data_selectable_bases(const bContext *C, ListBase *list) +{ + return ctx_data_collection_get(C, "selectable_bases", list); +} + struct Object *CTX_data_active_object(const bContext *C) { return ctx_data_pointer_get(C, "active_object"); diff --git a/source/blender/blenkernel/intern/curve.c b/source/blender/blenkernel/intern/curve.c index fab9669d55f..7dd868278f4 100644 --- a/source/blender/blenkernel/intern/curve.c +++ b/source/blender/blenkernel/intern/curve.c @@ -155,7 +155,7 @@ Curve *add_curve(char *name, int type) cu->str= MEM_mallocN(12, "str"); strcpy(cu->str, "Text"); cu->pos= 4; - cu->strinfo= MEM_callocN(12*sizeof(CharInfo), "strinfo"); + cu->strinfo= MEM_callocN(12*sizeof(CharInfo), "strinfo new"); cu->totbox= cu->actbox= 1; cu->tb= MEM_callocN(MAXTEXTBOX*sizeof(TextBox), "textbox"); cu->tb[0].w = cu->tb[0].h = 0.0; diff --git a/source/blender/blenkernel/intern/depsgraph.c b/source/blender/blenkernel/intern/depsgraph.c index dfe3b7ea279..f52eec34cc7 100644 --- a/source/blender/blenkernel/intern/depsgraph.c +++ b/source/blender/blenkernel/intern/depsgraph.c @@ -61,6 +61,8 @@ #include "DNA_view2d_types.h" #include "DNA_view3d_types.h" +#include "BLI_ghash.h" + #include "BKE_action.h" #include "BKE_effect.h" #include "BKE_global.h" @@ -557,23 +559,30 @@ static void build_dag_object(DagForest *dag, DagNode *scenenode, Scene *scene, O dag_add_relation(dag, node, node, DAG_RL_OB_DATA, "Particle-Object Relation"); - if(psys->flag & PSYS_DISABLED || psys->flag & PSYS_DELETE) + if(!psys_check_enabled(ob, psys)) 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, "Particle Keyed Physics"); + if(part->phystype==PART_PHYS_KEYED) { + KeyedParticleTarget *kpt = psys->keyed_targets.first; + + for(; kpt; kpt=kpt->next) { + if(kpt->ob && BLI_findlink(&kpt->ob->particlesystem, kpt->psys-1)) { + node2 = dag_get_node(dag, kpt->ob); + dag_add_relation(dag, node2, node, DAG_RL_DATA_DATA|DAG_RL_OB_DATA, "Particle Keyed Physics"); + } + else + break; + } } - if(part->draw_as == PART_DRAW_OB && part->dup_ob) { + if(part->ren_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) { + if(part->ren_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"); @@ -754,6 +763,9 @@ void free_forest(DagForest *Dag) itN = itN->next; MEM_freeN(tempN); } + + BLI_ghash_free(Dag->nodeHash, NULL, NULL); + Dag->nodeHash= NULL; Dag->DagNode.first = NULL; Dag->DagNode.last = NULL; Dag->numNodes = 0; @@ -762,13 +774,9 @@ void free_forest(DagForest *Dag) DagNode * dag_find_node (DagForest *forest,void * fob) { - DagNode *node = forest->DagNode.first; - - while (node) { - if (node->ob == fob) - return node; - node = node->next; - } + if(forest->nodeHash) + return BLI_ghash_lookup(forest->nodeHash, fob); + return NULL; } @@ -794,7 +802,12 @@ DagNode * dag_add_node (DagForest *forest, void * fob) forest->DagNode.first = node; forest->numNodes = 1; } + + if(!forest->nodeHash) + forest->nodeHash= BLI_ghash_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp); + BLI_ghash_insert(forest->nodeHash, fob, node); } + return node; } @@ -1805,17 +1818,10 @@ 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(Scene *sce, DagNode *node, int curtime) { - Base *base; DagAdjList *itA; node->lasttime= curtime; - node->lay= 0; - for(base= sce->base.first; base; base= base->next) { - if(node->ob == base->object) { - node->lay= ((Object *)node->ob)->lay; - break; - } - } + node->lay= node->scelay; for(itA = node->child; itA; itA= itA->next) { if(itA->node->type==ID_OB) { @@ -1832,7 +1838,7 @@ static unsigned int flush_layer_node(Scene *sce, DagNode *node, int curtime) } /* node was checked to have lasttime != curtime , and is of type ID_OB */ -static void flush_pointcache_reset(DagNode *node, int curtime, int reset) +static void flush_pointcache_reset(Scene *scene, DagNode *node, int curtime, int reset) { DagAdjList *itA; Object *ob; @@ -1845,13 +1851,13 @@ static void flush_pointcache_reset(DagNode *node, int curtime, int reset) ob= (Object*)(node->ob); if(reset || (ob->recalc & OB_RECALC)) { - if(BKE_ptcache_object_reset(ob, PTCACHE_RESET_DEPSGRAPH)) + if(BKE_ptcache_object_reset(scene, ob, PTCACHE_RESET_DEPSGRAPH)) ob->recalc |= OB_RECALC_DATA; - flush_pointcache_reset(itA->node, curtime, 1); + flush_pointcache_reset(scene, itA->node, curtime, 1); } else - flush_pointcache_reset(itA->node, curtime, 0); + flush_pointcache_reset(scene, itA->node, curtime, 0); } } } @@ -1860,9 +1866,10 @@ static void flush_pointcache_reset(DagNode *node, int curtime, int reset) /* flushes all recalc flags in objects down the dependency tree */ void DAG_scene_flush_update(Scene *sce, unsigned int lay, int time) { - DagNode *firstnode; + DagNode *firstnode, *node; DagAdjList *itA; Object *ob; + Base *base; int lasttime; if(sce->theDag==NULL) { @@ -1879,6 +1886,15 @@ void DAG_scene_flush_update(Scene *sce, unsigned int lay, int time) sce->theDag->time++; // so we know which nodes were accessed lasttime= sce->theDag->time; + + for(base= sce->base.first; base; base= base->next) { + node= dag_get_node(sce->theDag, base->object); + if(node) + node->scelay= base->object->lay; + else + node->scelay= 0; + } + for(itA = firstnode->child; itA; itA= itA->next) if(itA->node->lasttime!=lasttime && itA->node->type==ID_OB) flush_layer_node(sce, itA->node, lasttime); @@ -1899,13 +1915,13 @@ void DAG_scene_flush_update(Scene *sce, unsigned int lay, int time) ob= (Object*)(itA->node->ob); if(ob->recalc & OB_RECALC) { - if(BKE_ptcache_object_reset(ob, PTCACHE_RESET_DEPSGRAPH)) + if(BKE_ptcache_object_reset(sce, ob, PTCACHE_RESET_DEPSGRAPH)) ob->recalc |= OB_RECALC_DATA; - flush_pointcache_reset(itA->node, lasttime, 1); + flush_pointcache_reset(sce, itA->node, lasttime, 1); } else - flush_pointcache_reset(itA->node, lasttime, 0); + flush_pointcache_reset(sce, itA->node, lasttime, 0); } } } @@ -2123,7 +2139,7 @@ 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); + BKE_ptcache_object_reset(sce, ob, PTCACHE_RESET_DEPSGRAPH); /* all users of this ob->data should be checked */ /* BUT! displists for curves are still only on cu */ @@ -2138,7 +2154,7 @@ void DAG_object_flush_update(Scene *sce, Object *ob, short flag) for (obt=G.main->object.first; obt; obt= obt->id.next) { if (obt != ob && obt->data==ob->data) { obt->recalc |= OB_RECALC_DATA; - BKE_ptcache_object_reset(obt, PTCACHE_RESET_DEPSGRAPH); + BKE_ptcache_object_reset(sce, obt, PTCACHE_RESET_DEPSGRAPH); } } } diff --git a/source/blender/blenkernel/intern/displist.c b/source/blender/blenkernel/intern/displist.c index b6525cd936f..3136630ce93 100644 --- a/source/blender/blenkernel/intern/displist.c +++ b/source/blender/blenkernel/intern/displist.c @@ -315,13 +315,19 @@ static void init_fastshade_shadeinput(Render *re) static Render *fastshade_get_render(Scene *scene) { - Render *re= RE_GetRender("_Shade View_"); - if(re==NULL) { - re= RE_NewRender("_Shade View_"); - - RE_Database_Baking(re, scene, 0, 0); /* 0= no faces */ + /* XXX ugly global still, but we can't do preview while rendering */ + if(G.rendering==0) { + + Render *re= RE_GetRender("_Shade View_"); + if(re==NULL) { + re= RE_NewRender("_Shade View_"); + + RE_Database_Baking(re, scene, 0, 0); /* 0= no faces */ + } + return re; } - return re; + + return NULL; } /* called on file reading */ @@ -611,18 +617,20 @@ static void mesh_create_shadedColors(Render *re, Object *ob, int onlyForMesh, un void shadeMeshMCol(Scene *scene, Object *ob, Mesh *me) { + Render *re= fastshade_get_render(scene); int a; char *cp; unsigned int *mcol= (unsigned int*)me->mcol; - Render *re= fastshade_get_render(scene); - mesh_create_shadedColors(re, ob, 1, &mcol, NULL); - me->mcol= (MCol*)mcol; + if(re) { + mesh_create_shadedColors(re, ob, 1, &mcol, NULL); + me->mcol= (MCol*)mcol; - /* swap bytes */ - for(cp= (char *)me->mcol, a= 4*me->totface; a>0; a--, cp+=4) { - SWAP(char, cp[0], cp[3]); - SWAP(char, cp[1], cp[2]); + /* swap bytes */ + for(cp= (char *)me->mcol, a= 4*me->totface; a>0; a--, cp+=4) { + SWAP(char, cp[0], cp[3]); + SWAP(char, cp[1], cp[2]); + } } } @@ -641,6 +649,8 @@ void shadeDispList(Scene *scene, Base *base) int a, need_orco; re= fastshade_get_render(scene); + if(re==NULL) + return; dl = find_displist(&ob->disp, DL_VERTCOL); if (dl) { @@ -1371,7 +1381,7 @@ static void displist_surf_indices(DispList *dl) } -void makeDispListSurf(Scene *scene, Object *ob, ListBase *dispbase, int forRender) +void makeDispListSurf(Scene *scene, Object *ob, ListBase *dispbase, int forRender, int forOrco) { ListBase *nubase; Nurb *nu; @@ -1388,7 +1398,8 @@ void makeDispListSurf(Scene *scene, Object *ob, ListBase *dispbase, int forRende else nubase= &cu->nurb; - curve_calc_modifiers_pre(scene, ob, forRender, &originalVerts, &deformedVerts, &numVerts); + if(!forOrco) + curve_calc_modifiers_pre(scene, ob, forRender, &originalVerts, &deformedVerts, &numVerts); for (nu=nubase->first; nu; nu=nu->next) { if(forRender || nu->hide==0) { @@ -1442,7 +1453,8 @@ void makeDispListSurf(Scene *scene, Object *ob, ListBase *dispbase, int forRende tex_space_curve(cu); } - curve_calc_modifiers_post(scene, ob, dispbase, forRender, originalVerts, deformedVerts); + if(!forOrco) + curve_calc_modifiers_post(scene, ob, dispbase, forRender, originalVerts, deformedVerts); } void makeDispListCurveTypes(Scene *scene, Object *ob, int forOrco) @@ -1458,7 +1470,7 @@ void makeDispListCurveTypes(Scene *scene, Object *ob, int forOrco) freedisplist(dispbase); if(ob->type==OB_SURF) { - makeDispListSurf(scene, ob, dispbase, 0); + makeDispListSurf(scene, ob, dispbase, 0, forOrco); } else if (ELEM(ob->type, OB_CURVE, OB_FONT)) { ListBase dlbev; diff --git a/source/blender/blenkernel/intern/effect.c b/source/blender/blenkernel/intern/effect.c index 9858025af5a..553fdfe530e 100644 --- a/source/blender/blenkernel/intern/effect.c +++ b/source/blender/blenkernel/intern/effect.c @@ -29,6 +29,8 @@ * ***** END GPL LICENSE BLOCK ***** */ +#include "BLI_storage.h" /* _LARGEFILE_SOURCE */ + #include <math.h> #include <stdlib.h> @@ -54,6 +56,8 @@ #include "BLI_jitter.h" #include "BLI_rand.h" +#include "PIL_time.h" + #include "BKE_action.h" #include "BKE_anim.h" /* needed for where_on_path */ #include "BKE_armature.h" @@ -91,6 +95,20 @@ //XXX #include "BIF_screen.h" +PartDeflect *object_add_collision_fields(void) +{ + PartDeflect *pd; + + pd= MEM_callocN(sizeof(PartDeflect), "PartDeflect"); + + pd->pdef_sbdamp = 0.1f; + pd->pdef_sbift = 0.2f; + pd->pdef_sboft = 0.02f; + pd->seed = ((unsigned int)(ceil(PIL_check_seconds_timer()))+1) % 128; + + return pd; +} + /* temporal struct, used for reading return of mesh_get_mapped_verts_nors() */ typedef struct VeNoCo { diff --git a/source/blender/blenkernel/intern/exotic.c b/source/blender/blenkernel/intern/exotic.c index 0afbbead2c6..5566851db43 100644 --- a/source/blender/blenkernel/intern/exotic.c +++ b/source/blender/blenkernel/intern/exotic.c @@ -27,48 +27,9 @@ * * - Blender Foundation * - * ***** END GPL LICENSE BLOCK ***** - * - * eigen videoscape formaat: - * - * - * lamp: - * 3DG2 - aantal_lampen - - type - spsi spbl - r, g, b, energy - locx, locy, locz - vecx, vecy, vecz - - - curve / nurbs: - 3DG3 - 5 of 11 (curve of surf) - aantal_nurbs - extr1 extr2 - - mat[0][0] mat[0][1] mat[0][2] mat[0][3] - mat[1][0] mat[1][1] mat[1][2] mat[1][3] - ... - - type - pntsu, pntsv - resolu, resolv - orderu, orderv - flagu, flagv - - (als type==nurb) x y z w - x y z w - ... - (als type==bez) xyz xyz xyz h1 h2 h3 - xyz xyz xyz h1 h2 h3 - ... - * - * - */ + * ***** END GPL LICENSE BLOCK *****/ +#include "BLI_storage.h" #include <ctype.h> /* isdigit, isspace */ #include <math.h> @@ -482,385 +443,6 @@ static void read_stl_mesh_ascii(Scene *scene, char *str) #undef STLREADLINE #undef STLREADVERT -static void read_videoscape_mesh(Scene *scene, char *str) -{ - Object *ob; - Mesh *me; - MVert *mvert; - MFace *mface; - Material *ma; - FILE *fp; - float *vertdata, *vd, min[3], max[3], cent[3], ftemp; - unsigned int color[32], col; - int totcol, a, b, verts, tottria=0, totquad=0, totedge=0, poly, nr0, nr, first; - int end; - char s[50]; - - fp= fopen(str, "rb"); - if(fp==NULL) { - //XXX error("Can't read file"); - return; - } - - fscanf(fp, "%40s", s); - - fscanf(fp, "%d\n", &verts); - if(verts<=0) { - fclose(fp); - //XXX error("Read error"); - return; - } - - if(verts>MESH_MAX_VERTS) { - //XXX error("too many vertices"); - fclose(fp); - return; - } - - INIT_MINMAX(min, max); - vd= vertdata= MEM_mallocN(sizeof(float)*3*verts, "videoscapelezer"); - - for(a=0; a<verts; a++) { - fscanf(fp, "%f %f %f", vd, vd+1, vd+2); - DO_MINMAX(vd, min, max); - vd+=3; - } - - /* count faces and colors */ - for(a=0; a<32; a++) color[a]= 0; - totcol= 0; - end= 1; - while(end>0) { - end= fscanf(fp,"%d", &poly); - if(end<=0) break; - - if(poly==3) tottria++; - else if(poly==4) totquad++; - else totedge+= poly; - - for(a=0;a<poly;a++) { - end= fscanf(fp,"%d", &nr); - if(end<=0) break; - } - if(end<=0) break; - - end= fscanf(fp,"%i\n", &col); - col &= 0xF0F0F0; - for(a=0; a<totcol; a++) { - if(color[a]==col) break; - } - if(a>=totcol && totcol<32) { - color[totcol]= col; - totcol++; - } - } - - /* new object */ - ob= add_object(scene, OB_MESH); - me= ob->data; - me->totvert= verts; - me->totface= totedge+tottria+totquad; - - me->mvert= CustomData_add_layer(&me->vdata, CD_MVERT, CD_CALLOC, - NULL, me->totvert); - me->mface= CustomData_add_layer(&me->fdata, CD_MFACE, CD_CALLOC, - NULL, me->totface); - - /* colors */ - if(totcol) { - ob->mat= MEM_callocN(sizeof(void *)*totcol, "ob->mat"); - me->mat= MEM_callocN(sizeof(void *)*totcol, "me->mat"); - me->totcol= totcol; - ob->totcol= (unsigned char) me->totcol; - ob->actcol= 1; - } - - /* materials */ - for(a=0; a<totcol; a++) { - ma= G.main->mat.first; - while(ma) { - if(ma->mtex[0]==0) { - col= rgb_to_cpack(ma->r, ma->g, ma->b); - if(color[a]==col) { - me->mat[a]= ma; - ma->id.us++; - break; - } - } - ma= ma->id.next; - } - if(ma==0) { - ma= add_material("ext"); - me->mat[a]= ma; - cpack_to_rgb(color[a], cent, cent+1, cent+2); - ma->r= cent[0]; - ma->g= cent[1]; - ma->b= cent[2]; - automatname(ma); - } - } - - /* verts */ - - cent[0]= (min[0]+max[0])/2.0f; - cent[1]= (min[1]+max[1])/2.0f; - cent[2]= (min[2]+max[2])/2.0f; - VECCOPY(ob->loc, cent); - - a= me->totvert; - vd= vertdata; - mvert= me->mvert; - while(a--) { - VecSubf(mvert->co, vd, cent); - mvert++; - vd+= 3; - } - - /* faces */ - if(me->totface) { - rewind(fp); - - fscanf(fp, "%40s", s); - fscanf(fp, "%d\n", &verts); - /* fake read */ - for(a=0;a<verts;a++) { - fscanf(fp, "%f %f %f", &ftemp, &ftemp, &ftemp); - } - - a= me->totface; - mface= me->mface; - while(a--) { - end= fscanf(fp,"%d", &poly); - if(end<=0) break; - - if(poly==3 || poly==4) { - fscanf(fp,"%d", &nr); - mface->v1= MIN2(nr, me->totvert-1); - fscanf(fp,"%d", &nr); - mface->v2= MIN2(nr, me->totvert-1); - fscanf(fp,"%d", &nr); - mface->v3= MIN2(nr, me->totvert-1); - if(poly==4) { - if( fscanf(fp,"%d", &nr) <=0 ) break; - mface->v4= MIN2(nr, me->totvert-1); - } - - test_index_face(mface, NULL, 0, poly); - - mface++; - } - else { - if( fscanf(fp,"%d", &nr0) <=0) break; - first= nr0; - for(b=1; b<poly; b++) { - end= fscanf(fp,"%d", &nr); - if(end<=0) break; - nr= MIN2(nr, me->totvert-1); - mface->v1= nr; - mface->v2= nr0; - nr0= nr; - mface++; - a--; - } - mface->v1= first; - mface->v2= nr; - mface++; - if(end<=0) break; - } - end= fscanf(fp,"%i", &col); - col &= 0xF0F0F0; - if(end<=0) break; - - for(b=0; b<totcol; b++) { - if(color[b]==col) { - (mface-1)->mat_nr= b; - break; - } - } - } - } - - fclose(fp); - MEM_freeN(vertdata); - - mesh_add_normals_flags(me); - make_edges(me, 0); - - //XXX waitcursor(1); -} - -static void read_videoscape_lamp(Scene *scene, char *str) -{ - Object *ob; - Lamp *la; - FILE *fp; - float vec[3], q1[4]; - int tot, val; - char s[50]; - - fp= fopen(str, "rb"); - if(fp==NULL) { - //XXX error("Can't read file"); - return; - } - - fscanf(fp, "%40s", s); - fscanf(fp, "%d\n", &tot); - - while(tot--) { - ob= add_object(scene, OB_LAMP); - la= ob->data; - - fscanf(fp, "%d\n", &val); - la->type= val; - if(la->type==1) la->type= LA_SPOT; - else if(la->type==2) la->type= LA_SUN; - - fscanf(fp, "%f %f\n", &la->spotsize, &la->spotblend); - - fscanf(fp, "%f %f %f %f\n", &la->r, &la->g, &la->b, &la->energy); - - 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); - vectoquat(vec, 5, 2, q1); - QuatToEul(q1, ob->rot); - - if(val<=0) break; - - } - fclose(fp); -} - -static void read_videoscape_nurbs(Scene *scene, char *str) -{ - Object *ob; - Curve *cu; - Nurb *nu; - BezTriple *bezt; - BPoint *bp; - FILE *fp; - float tmat[4][4], omat[3][3], imat[3][3], mat[3][3]; - int a, tot, type, val; - char s[50]; - - fp= fopen(str, "rb"); - if(fp==NULL) { - //XXX error("Can't read file"); - return; - } - - fscanf(fp, "%40s", s); - fscanf(fp, "%d\n", &type); - - if(type==5) ob= add_object(scene, OB_SURF); - else ob= add_object(scene, OB_CURVE); - cu= ob->data; - - fscanf(fp, "%d\n", &tot); - fscanf(fp, "%d %d\n", &type, &val); - - cu->ext1= 0.002f*type; - cu->ext2= 0.002f*val; - - for(a=0; a<4; a++) fscanf(fp, "%e %e %e %e\n", tmat[a], tmat[a]+1, tmat[a]+2, tmat[a]+3); - - VECCOPY(ob->loc, tmat[3]); - - Mat3CpyMat4(omat, tmat); - Mat3ToEul(omat, ob->rot); - EulToMat3(ob->rot, mat); - Mat3Inv(imat, mat); - Mat3MulMat3((float ( * )[3])tmat, imat, omat); - - while(tot--) { - nu= (Nurb*)MEM_callocN(sizeof(Nurb),"nu from exotic"); - BLI_addtail(&cu->nurb, nu); - - fscanf(fp, "%d\n", &type); - nu->type= type; - - fscanf(fp, "%d %d\n", &type, &val); - nu->pntsu= type; nu->pntsv= val; - fscanf(fp, "%d %d\n", &type, &val); - nu->resolu= type; nu->resolv= val; - fscanf(fp, "%d %d\n", &type, &val); - nu->orderu= type; nu->orderv= val; - fscanf(fp, "%d %d\n", &type, &val); - nu->flagu= type; nu->flagv= val; - - if( (nu->type & 7)==CU_BEZIER) { - a= nu->pntsu; - nu->bezt= bezt= MEM_callocN(a*sizeof(BezTriple), "bezt from exotic"); - while(a--) { - fscanf(fp, "%f %f %f ", bezt->vec[0], bezt->vec[0]+1, bezt->vec[0]+2); - Mat4MulVecfl(tmat, bezt->vec[0]); - fscanf(fp, "%f %f %f ", bezt->vec[1], bezt->vec[1]+1, bezt->vec[1]+2); - Mat4MulVecfl(tmat, bezt->vec[1]); - fscanf(fp, "%f %f %f ", bezt->vec[2], bezt->vec[2]+1, bezt->vec[2]+2); - Mat4MulVecfl(tmat, bezt->vec[2]); - fscanf(fp, "%d %d\n", &type, &val); - bezt->h1= type; - bezt->h2= val; - bezt++; - } - } - else { - a= nu->pntsu*nu->pntsv; - if(a) { - nu->bp= bp= MEM_callocN(a*sizeof(BPoint), "bp from exotic"); - while(a--) { - fscanf(fp, "%f %f %f %f\n", bp->vec, bp->vec+1, bp->vec+2, bp->vec+3); - Mat4MulVecfl(tmat, bp->vec); - bp++; - } - - val= KNOTSU(nu); - nu->knotsu= MEM_mallocN(sizeof(float)*val, "knots"); - for(a=0; a<val; a++) fscanf(fp, "%f\n", nu->knotsu+a); - - if(nu->pntsv>1) { - val= KNOTSV(nu); - nu->knotsv= MEM_mallocN(sizeof(float)*val, "knots"); - for(a=0; a<val; a++) fscanf(fp, "%f\n", nu->knotsv+a); - } - } - else { - BLI_remlink(&cu->nurb, nu); - MEM_freeN(nu); - } - } - } - fclose(fp); -} - -static void read_videoscape(Scene *scene, char *str) -{ - int file, type; - unsigned int val; - unsigned short numlen; - char name[FILE_MAXDIR+FILE_MAXFILE], head[FILE_MAXDIR+FILE_MAXFILE], tail[FILE_MAXFILE]; - - strcpy(name, str); - - while( TRUE ) { - file= open(name, O_BINARY|O_RDONLY); - if(file<=0) break; - else { - read(file, &type, 4); - close(file); - - if(type==DDG1) read_videoscape_mesh(scene, name); - else if(type==DDG2) read_videoscape_lamp(scene, name); - else if(type==DDG3) read_videoscape_nurbs(scene, name); - } - - val = BLI_stringdec(name, head, tail, &numlen); - BLI_stringenc(name, head, tail, numlen, val + 1); - - } -} - - /* ***************** INVENTOR ******************* */ @@ -1833,11 +1415,6 @@ static void displist_to_mesh(Scene *scene, DispList *dlfirst) return; } - if(totcol>16) { - //XXX error("Found more than 16 different colors"); - totcol= 16; - } - vec[0]= (min[0]+max[0])/2; vec[1]= (min[1]+max[1])/2; vec[2]= (min[2]+max[2])/2; @@ -1851,6 +1428,7 @@ static void displist_to_mesh(Scene *scene, DispList *dlfirst) /* colors */ if(totcol) { ob->mat= MEM_callocN(sizeof(void *)*totcol, "ob->mat"); + ob->matbits= MEM_callocN(sizeof(char)*totcol, "ob->matbits"); me->mat= MEM_callocN(sizeof(void *)*totcol, "me->mat"); me->totcol= totcol; ob->totcol= (unsigned char) me->totcol; @@ -1900,7 +1478,7 @@ static void displist_to_mesh(Scene *scene, DispList *dlfirst) dl= dlfirst; while(dl) { - colnr= (dl->col>15 ? 15: dl->col); + colnr= dl->col; if(colnr) colnr--; if(dl->type==DL_SURF) { @@ -2204,16 +1782,7 @@ int BKE_read_exotic(Scene *scene, char *name) if ((*s0 != FORM) && (strncmp(str, "BLEN", 4) != 0) && !BLI_testextensie(name,".blend.gz")) { //XXX waitcursor(1); - - if(ELEM4(*s0, DDG1, DDG2, DDG3, DDG4)) { - if(0) { // XXX obedit) { - //XXX error("Unable to perform function in EditMode"); - } else { - read_videoscape(scene, name); - retval = 1; - } - } - else if(strncmp(str, "#Inventor V1.0", 14)==0) { + if(strncmp(str, "#Inventor V1.0", 14)==0) { if( strncmp(str+15, "ascii", 5)==0) { read_inventor(scene, name, &lbase); displist_to_objects(scene, &lbase); @@ -2385,167 +1954,6 @@ void write_stl(Scene *scene, char *str) //XXX waitcursor(0); } -static void write_videoscape_mesh(Scene *scene, Object *ob, char *str) -{ - Mesh *me= ob->data; - EditMesh *em = BKE_mesh_get_editmesh(me); - Material *ma; - MFace *mface; - FILE *fp; - EditVert *eve; - EditFace *evl; - unsigned int kleur[32]; - float co[3]; - int a; - intptr_t tot; - char *cp; - - if(ob && ob->type==OB_MESH); - else { - return; - } - - kleur[0]= 0x00C0C0C0; - - cp= (char *)kleur; - for(a=0; a<ob->totcol; a++, cp+=4) { - - ma= give_current_material(ob, a+1); - if(ma) { - cp[0]= (unsigned char) (255.0*ma->emit); - cp[1]= (unsigned char) (255.0*ma->b); - cp[2]= (unsigned char) (255.0*ma->g); - cp[3]= (unsigned char) (255.0*ma->r); - if(ENDIAN_ORDER==L_ENDIAN) SWITCH_INT(kleur[a]); - } - else kleur[a]= 0x00C0C0C0; - - if(a>30) break; - } - - fp= fopen(str, "wb"); - if(fp==NULL) return; - - fprintf(fp,"3DG1\n"); - - if(em) { - - fprintf(fp, "%d\n", em->totvert); - - tot= 0; - eve= em->verts.first; - while(eve) { - VECCOPY(co, eve->co); - Mat4MulVecfl(ob->obmat, co); - fprintf(fp, "%f %f %f\n", co[0], co[1], co[2] ); - eve->tmp.l = tot; - tot++; - eve= eve->next; - } - evl= em->faces.first; - while(evl) { - - if(evl->v4==0) { - fprintf(fp, "3 %ld %ld %ld 0x%x\n", - (intptr_t) evl->v1->tmp.l, - (intptr_t) evl->v2->tmp.l, - (intptr_t) evl->v3->tmp.l, - kleur[evl->mat_nr]); - } - else { - fprintf(fp, "4 %ld %ld %ld %ld 0x%x\n", - (intptr_t) evl->v1->tmp.l, - (intptr_t) evl->v2->tmp.l, - (intptr_t) evl->v3->tmp.l, - (intptr_t) evl->v4->tmp.l, - kleur[evl->mat_nr]); - } - evl= evl->next; - } - } - else { - DerivedMesh *dm = mesh_get_derived_deform(scene, ob, CD_MASK_BAREMESH); - - me= ob->data; - - fprintf(fp, "%d\n", me->totvert); - - mface= me->mface; - for(a=0; a<me->totvert; a++) { - dm->getVertCo(dm, a, co); - Mat4MulVecfl(ob->obmat, co); - fprintf(fp, "%f %f %f\n", co[0], co[1], co[2] ); - } - for(a=0; a<me->totface; a++, mface++) { - if(mface->v4==0) { - fprintf(fp, "3 %d %d %d 0x%x\n", mface->v1, mface->v2, mface->v3, kleur[(int)mface->mat_nr]); - } - else { - fprintf(fp, "4 %d %d %d %d 0x%x\n", mface->v1, mface->v2, mface->v3, mface->v4, kleur[(int)mface->mat_nr]); - } - } - - dm->release(dm); - } - - fclose(fp); - - if (em) BKE_mesh_end_editmesh(me, em); - -} - - -void write_videoscape(Scene *scene, char *str) -{ - Base *base; - int file, val, lampdone=0; - unsigned short numlen; - char head[FILE_MAXFILE], tail[FILE_MAXFILE]; - - if(BLI_testextensie(str,".trace")) str[ strlen(str)-6]= 0; - if(BLI_testextensie(str,".blend")) str[ strlen(str)-6]= 0; - if(BLI_testextensie(str,".ble")) str[ strlen(str)-4]= 0; - if(BLI_testextensie(str,".obj")==0) strcat(str, ".obj"); - - file= open(str,O_BINARY|O_RDONLY); - close(file); - //XXX saveover() - // if(file>-1) if(!during_script() && saveover(str)==0) return; - - strcpy(temp_dir, str); - - base= scene->base.first; - while(base) { - if((base->flag & SELECT) && (base->lay & scene->lay)) { - if(base->object->type==OB_MESH) { - write_videoscape_mesh(scene, base->object, str); - val = BLI_stringdec(str, head, tail, &numlen); - BLI_stringenc(str, head, tail, numlen, val + 1); - } - else if(base->object->type==OB_CURVE || base->object->type==OB_SURF) { - /* write_videoscape_nurbs(base->object, str); */ - /* val = stringdec(str, head, tail, &numlen); */ - /* stringenc(str, head, tail, numlen, val + 1); */ - } - else if(lampdone==0 && base->object->type==OB_LAMP) { - /* lampdone= 1; */ - /* write_videoscape_lamps(str); */ - /* val = stringdec(str, head, tail, &numlen); */ - /* stringenc(str, head, tail, numlen, val + 1); */ - } - } - base= base->next; - } - - - /* remove when higher numbers exist */ - while(remove(str)==0) { - - val = BLI_stringdec(str, head, tail, &numlen); - BLI_stringenc(str, head, tail, numlen, val + 1); - } -} - /* ******************************* WRITE VRML ***************************** */ static void replace_chars(char *str1, char *str2) @@ -3392,8 +2800,11 @@ static void dxf_add_mat (Object *ob, Mesh *me, float color[3], char *layer) if (!me) return; - if(ob) ob->mat= MEM_callocN(sizeof(void *)*1, "ob->mat"); - if(ob) ob->actcol= 1; + if(ob) { + ob->mat= MEM_callocN(sizeof(void *)*1, "ob->mat"); + ob->matbits= MEM_callocN(sizeof(char)*1, "ob->matbits"); + ob->actcol= 1; + } me->totcol= 1; me->mat= MEM_callocN(sizeof(void *)*1, "me->mat"); @@ -4641,7 +4052,6 @@ static void dxf_read(Scene *scene, char *filename) ob->type= OB_MESH; ob->dt= OB_SHADED; - if(U.flag & USER_MAT_ON_OB) ob->colbits= -1; ob->trackflag= OB_POSY; ob->upflag= OB_POSZ; @@ -4660,9 +4070,10 @@ static void dxf_read(Scene *scene, char *filename) VECCOPY(ob->rot, obrot); ob->mat= MEM_callocN(sizeof(void *)*1, "ob->mat"); + ob->matbits= MEM_callocN(sizeof(char)*1, "ob->matbits"); ob->totcol= (unsigned char) ((Mesh*)ob->data)->totcol; ob->actcol= 1; - + /* note: materials are either linked to mesh or object, if both then you have to increase user counts. below line is not needed. I leave it commented out here as warning (ton) */ diff --git a/source/blender/blenkernel/intern/fcurve.c b/source/blender/blenkernel/intern/fcurve.c index ad8115ba9aa..ebd94b94f8c 100644 --- a/source/blender/blenkernel/intern/fcurve.c +++ b/source/blender/blenkernel/intern/fcurve.c @@ -1,5 +1,30 @@ -/* Testing code for new animation system in 2.5 - * Copyright 2009, Joshua Leung +/** + * $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) 2009 Blender Foundation, Joshua Leung + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): Joshua Leung (full recode) + * + * ***** END GPL LICENSE BLOCK ***** */ @@ -31,7 +56,7 @@ #include "RNA_types.h" #ifndef DISABLE_PYTHON -#include "BPY_extern.h" /* for BPY_pydriver_eval() */ +#include "BPY_extern.h" #endif #define SMALL -1.0e-10 @@ -59,7 +84,7 @@ void free_fcurve (FCurve *fcu) /* free extra data - i.e. modifiers, and driver */ fcurve_free_driver(fcu); - fcurve_free_modifiers(fcu); + free_fmodifiers(&fcu->modifiers); /* free f-curve itself */ MEM_freeN(fcu); @@ -115,7 +140,7 @@ FCurve *copy_fcurve (FCurve *fcu) fcu_d->driver= fcurve_copy_driver(fcu_d->driver); /* copy modifiers */ - fcurve_copy_modifiers(&fcu_d->modifiers, &fcu->modifiers); + copy_fmodifiers(&fcu_d->modifiers, &fcu->modifiers); /* return new data */ return fcu_d; @@ -175,20 +200,6 @@ FCurve *list_find_fcurve (ListBase *list, const char rna_path[], const int array return NULL; } -short on_keyframe_fcurve(FCurve *fcu, float cfra) -{ - BezTriple *bezt; - unsigned i; - - bezt= fcu->bezt; - for (i=0; i<fcu->totvert; i++, bezt++) { - if (IS_EQ(bezt->vec[1][0], cfra)) - return 1; - } - - return 0; -} - /* Calculate the extents of F-Curve's data */ void calc_fcurve_bounds (FCurve *fcu, float *xmin, float *xmax, float *ymin, float *ymax) { @@ -1245,1016 +1256,6 @@ static float fcurve_eval_samples (FCurve *fcu, FPoint *fpts, float evaltime) return cvalue; } -/* ******************************** F-Curve Modifiers ********************************* */ - -/* Template --------------------------- */ - -/* Each modifier defines a set of functions, which will be called at the appropriate - * times. In addition to this, each modifier should have a type-info struct, where - * its functions are attached for use. - */ - -/* Template for type-info data: - * - make a copy of this when creating new modifiers, and just change the functions - * pointed to as necessary - * - although the naming of functions doesn't matter, it would help for code - * readability, to follow the same naming convention as is presented here - * - any functions that a constraint doesn't need to define, don't define - * for such cases, just use NULL - * - these should be defined after all the functions have been defined, so that - * forward-definitions/prototypes don't need to be used! - * - keep this copy #if-def'd so that future constraints can get based off this - */ -#if 0 -static FModifierTypeInfo FMI_MODNAME = { - FMODIFIER_TYPE_MODNAME, /* type */ - sizeof(FMod_ModName), /* size */ - FMI_TYPE_SOME_ACTION, /* action type */ - FMI_REQUIRES_SOME_REQUIREMENT, /* requirements */ - "Modifier Name", /* name */ - "FMod_ModName", /* struct name */ - fcm_modname_free, /* free data */ - fcm_modname_relink, /* relink data */ - fcm_modname_copy, /* copy data */ - fcm_modname_new_data, /* new data */ - fcm_modname_verify, /* verify */ - fcm_modname_time, /* evaluate time */ - fcm_modname_evaluate /* evaluate */ -}; -#endif - -/* Generator F-Curve Modifier --------------------------- */ - -/* Generators available: - * 1) simple polynomial generator: - * - Exanded form - (y = C[0]*(x^(n)) + C[1]*(x^(n-1)) + ... + C[n]) - * - Factorised form - (y = (C[0][0]*x + C[0][1]) * (C[1][0]*x + C[1][1]) * ... * (C[n][0]*x + C[n][1])) - * 2) simple builin 'functions': - * of the form (y = C[0] * fn( C[1]*x + C[2] ) + C[3]) - * where fn() can be any one of: - * sin, cos, tan, ln, sqrt - * 3) expression... - */ - -static void fcm_generator_free (FModifier *fcm) -{ - FMod_Generator *data= (FMod_Generator *)fcm->data; - - /* free polynomial coefficients array */ - if (data->coefficients) - MEM_freeN(data->coefficients); -} - -static void fcm_generator_copy (FModifier *fcm, FModifier *src) -{ - FMod_Generator *gen= (FMod_Generator *)fcm->data; - FMod_Generator *ogen= (FMod_Generator *)src->data; - - /* copy coefficients array? */ - if (ogen->coefficients) - gen->coefficients= MEM_dupallocN(ogen->coefficients); -} - -static void fcm_generator_new_data (void *mdata) -{ - FMod_Generator *data= (FMod_Generator *)mdata; - float *cp; - - /* set default generator to be linear 0-1 (gradient = 1, y-offset = 0) */ - data->poly_order= 1; - data->arraysize= 2; - cp= data->coefficients= MEM_callocN(sizeof(float)*2, "FMod_Generator_Coefs"); - cp[0] = 0; // y-offset - cp[1] = 1; // gradient -} - -static void fcm_generator_verify (FModifier *fcm) -{ - FMod_Generator *data= (FMod_Generator *)fcm->data; - - /* requirements depend on mode */ - switch (data->mode) { - case FCM_GENERATOR_POLYNOMIAL: /* expanded polynomial expression */ - { - /* arraysize needs to be order+1, so resize if not */ - if (data->arraysize != (data->poly_order+1)) { - float *nc; - - /* make new coefficients array, and copy over as much data as can fit */ - nc= MEM_callocN(sizeof(float)*(data->poly_order+1), "FMod_Generator_Coefs"); - - if (data->coefficients) { - if (data->arraysize > (data->poly_order+1)) - memcpy(nc, data->coefficients, sizeof(float)*(data->poly_order+1)); - else - memcpy(nc, data->coefficients, sizeof(float)*data->arraysize); - - /* free the old data */ - MEM_freeN(data->coefficients); - } - - /* set the new data */ - data->coefficients= nc; - data->arraysize= data->poly_order+1; - } - } - break; - - case FCM_GENERATOR_POLYNOMIAL_FACTORISED: /* expanded polynomial expression */ - { - /* arraysize needs to be 2*order, so resize if not */ - if (data->arraysize != (data->poly_order * 2)) { - float *nc; - - /* make new coefficients array, and copy over as much data as can fit */ - nc= MEM_callocN(sizeof(float)*(data->poly_order*2), "FMod_Generator_Coefs"); - - if (data->coefficients) { - if (data->arraysize > (data->poly_order * 2)) - memcpy(nc, data->coefficients, sizeof(float)*(data->poly_order * 2)); - else - memcpy(nc, data->coefficients, sizeof(float)*data->arraysize); - - /* free the old data */ - MEM_freeN(data->coefficients); - } - - /* set the new data */ - data->coefficients= nc; - data->arraysize= data->poly_order * 2; - } - } - break; - - case FCM_GENERATOR_FUNCTION: /* builtin function */ - { - /* arraysize needs to be 4*/ - if (data->arraysize != 4) { - float *nc; - - /* free the old data */ - if (data->coefficients) - MEM_freeN(data->coefficients); - - /* make new coefficients array, and init using default values */ - nc= data->coefficients= MEM_callocN(sizeof(float)*4, "FMod_Generator_Coefs"); - data->arraysize= 4; - - nc[0]= 1.0f; - nc[1]= 1.0f; - nc[2]= 0.0f; - nc[3]= 0.0f; - } - } - break; - } -} - -static void fcm_generator_evaluate (FCurve *fcu, FModifier *fcm, float *cvalue, float evaltime) -{ - FMod_Generator *data= (FMod_Generator *)fcm->data; - - /* behaviour depends on mode - * NOTE: the data in its default state is fine too - */ - switch (data->mode) { - case FCM_GENERATOR_POLYNOMIAL: /* expanded polynomial expression */ - { - /* we overwrite cvalue with the sum of the polynomial */ - float *powers = MEM_callocN(sizeof(float)*data->arraysize, "Poly Powers"); - float value= 0.0f; - unsigned int i; - - /* for each x^n, precalculate value based on previous one first... this should be - * faster that calling pow() for each entry - */ - for (i=0; i < data->arraysize; i++) { - /* first entry is x^0 = 1, otherwise, calculate based on previous */ - if (i) - powers[i]= powers[i-1] * evaltime; - else - powers[0]= 1; - } - - /* for each coefficient, add to value, which we'll write to *cvalue in one go */ - for (i=0; i < data->arraysize; i++) - value += data->coefficients[i] * powers[i]; - - /* only if something changed, write *cvalue in one go */ - if (data->poly_order) { - if (data->flag & FCM_GENERATOR_ADDITIVE) - *cvalue += value; - else - *cvalue= value; - } - - /* cleanup */ - if (powers) - MEM_freeN(powers); - } - break; - - case FCM_GENERATOR_POLYNOMIAL_FACTORISED: /* factorised polynomial */ - { - float value= 1.0f, *cp=NULL; - unsigned int i; - - /* for each coefficient pair, solve for that bracket before accumulating in value by multiplying */ - for (cp=data->coefficients, i=0; (cp) && (i < data->poly_order); cp+=2, i++) - value *= (cp[0]*evaltime + cp[1]); - - /* only if something changed, write *cvalue in one go */ - if (data->poly_order) { - if (data->flag & FCM_GENERATOR_ADDITIVE) - *cvalue += value; - else - *cvalue= value; - } - } - break; - - case FCM_GENERATOR_FUNCTION: /* builtin function */ - { - double arg= data->coefficients[1]*evaltime + data->coefficients[2]; - double (*fn)(double v) = NULL; - - /* get function pointer to the func to use: - * WARNING: must perform special argument validation hereto guard against crashes - */ - switch (data->func_type) - { - /* simple ones */ - case FCM_GENERATOR_FN_SIN: /* sine wave */ - fn= sin; - break; - case FCM_GENERATOR_FN_COS: /* cosine wave */ - fn= cos; - break; - - /* validation required */ - case FCM_GENERATOR_FN_TAN: /* tangent wave */ - { - /* check that argument is not on one of the discontinuities (i.e. 90deg, 270 deg, etc) */ - if IS_EQ(fmod((arg - M_PI_2), M_PI), 0.0) { - if ((data->flag & FCM_GENERATOR_ADDITIVE) == 0) - *cvalue = 0.0f; /* no value possible here */ - } - else - fn= tan; - } - break; - case FCM_GENERATOR_FN_LN: /* natural log */ - { - /* check that value is greater than 1? */ - if (arg > 1.0f) { - fn= log; - } - else { - if ((data->flag & FCM_GENERATOR_ADDITIVE) == 0) - *cvalue = 0.0f; /* no value possible here */ - } - } - break; - case FCM_GENERATOR_FN_SQRT: /* square root */ - { - /* no negative numbers */ - if (arg > 0.0f) { - fn= sqrt; - } - else { - if ((data->flag & FCM_GENERATOR_ADDITIVE) == 0) - *cvalue = 0.0f; /* no value possible here */ - } - } - break; - - default: - printf("Invalid Function-Generator for F-Modifier - %d \n", data->func_type); - } - - /* execute function callback to set value if appropriate */ - if (fn) { - float value= (float)(data->coefficients[0]*fn(arg) + data->coefficients[3]); - - if (data->flag & FCM_GENERATOR_ADDITIVE) - *cvalue += value; - else - *cvalue= value; - } - } - break; - -#ifndef DISABLE_PYTHON - case FCM_GENERATOR_EXPRESSION: /* py-expression */ - // TODO... - break; -#endif /* DISABLE_PYTHON */ - } -} - -static FModifierTypeInfo FMI_GENERATOR = { - FMODIFIER_TYPE_GENERATOR, /* type */ - sizeof(FMod_Generator), /* size */ - FMI_TYPE_GENERATE_CURVE, /* action type */ - FMI_REQUIRES_NOTHING, /* requirements */ - "Generator", /* name */ - "FMod_Generator", /* struct name */ - fcm_generator_free, /* free data */ - fcm_generator_copy, /* copy data */ - fcm_generator_new_data, /* new data */ - fcm_generator_verify, /* verify */ - NULL, /* evaluate time */ - fcm_generator_evaluate /* evaluate */ -}; - -/* Envelope F-Curve Modifier --------------------------- */ - -static void fcm_envelope_free (FModifier *fcm) -{ - FMod_Envelope *env= (FMod_Envelope *)fcm->data; - - /* free envelope data array */ - if (env->data) - MEM_freeN(env->data); -} - -static void fcm_envelope_copy (FModifier *fcm, FModifier *src) -{ - FMod_Envelope *env= (FMod_Envelope *)fcm->data; - FMod_Envelope *oenv= (FMod_Envelope *)src->data; - - /* copy envelope data array */ - if (oenv->data) - env->data= MEM_dupallocN(oenv->data); -} - -static void fcm_envelope_new_data (void *mdata) -{ - FMod_Envelope *env= (FMod_Envelope *)mdata; - - /* set default min/max ranges */ - env->min= -1.0f; - env->max= 1.0f; -} - -static void fcm_envelope_verify (FModifier *fcm) -{ - FMod_Envelope *env= (FMod_Envelope *)fcm->data; - - /* if the are points, perform bubble-sort on them, as user may have changed the order */ - if (env->data) { - // XXX todo... - } -} - -static void fcm_envelope_evaluate (FCurve *fcu, FModifier *fcm, float *cvalue, float evaltime) -{ - FMod_Envelope *env= (FMod_Envelope *)fcm->data; - FCM_EnvelopeData *fed, *prevfed, *lastfed; - float min=0.0f, max=0.0f, fac=0.0f; - int a; - - /* get pointers */ - if (env->data == NULL) return; - prevfed= env->data; - fed= prevfed + 1; - lastfed= prevfed + (env->totvert-1); - - /* get min/max values for envelope at evaluation time (relative to mid-value) */ - if (prevfed->time >= evaltime) { - /* before or on first sample, so just extend value */ - min= prevfed->min; - max= prevfed->max; - } - else if (lastfed->time <= evaltime) { - /* after or on last sample, so just extend value */ - min= lastfed->min; - max= lastfed->max; - } - else { - /* evaltime occurs somewhere between segments */ - // TODO: implement binary search for this to make it faster? - for (a=0; prevfed && fed && (a < env->totvert-1); a++, prevfed=fed, fed++) { - /* evaltime occurs within the interval defined by these two envelope points */ - if ((prevfed->time <= evaltime) && (fed->time >= evaltime)) { - float afac, bfac, diff; - - diff= fed->time - prevfed->time; - afac= (evaltime - prevfed->time) / diff; - bfac= (fed->time - evaltime) / diff; - - min= bfac*prevfed->min + afac*fed->min; - max= bfac*prevfed->max + afac*fed->max; - - break; - } - } - } - - /* adjust *cvalue - * - fac is the ratio of how the current y-value corresponds to the reference range - * - thus, the new value is found by mapping the old range to the new! - */ - fac= (*cvalue - (env->midval + env->min)) / (env->max - env->min); - *cvalue= min + fac*(max - min); -} - -static FModifierTypeInfo FMI_ENVELOPE = { - FMODIFIER_TYPE_ENVELOPE, /* type */ - sizeof(FMod_Envelope), /* size */ - FMI_TYPE_REPLACE_VALUES, /* action type */ - 0, /* requirements */ - "Envelope", /* name */ - "FMod_Envelope", /* struct name */ - fcm_envelope_free, /* free data */ - fcm_envelope_copy, /* copy data */ - fcm_envelope_new_data, /* new data */ - fcm_envelope_verify, /* verify */ - NULL, /* evaluate time */ - fcm_envelope_evaluate /* evaluate */ -}; - -/* Cycles F-Curve Modifier --------------------------- */ - -/* This modifier changes evaltime to something that exists within the curve's frame-range, - * then re-evaluates modifier stack up to this point using the new time. This re-entrant behaviour - * is very likely to be more time-consuming than the original approach... (which was tighly integrated into - * the calculation code...). - * - * NOTE: this needs to be at the start of the stack to be of use, as it needs to know the extents of the keyframes/sample-data - * Possible TODO - store length of cycle information that can be initialised from the extents of the keyframes/sample-data, and adjusted - * as appropriate - */ - -/* temp data used during evaluation */ -typedef struct tFCMED_Cycles { - float cycyofs; /* y-offset to apply */ -} tFCMED_Cycles; - -static void fcm_cycles_new_data (void *mdata) -{ - FMod_Cycles *data= (FMod_Cycles *)mdata; - - /* turn on cycles by default */ - data->before_mode= data->after_mode= FCM_EXTRAPOLATE_CYCLIC; -} - -static float fcm_cycles_time (FCurve *fcu, FModifier *fcm, float cvalue, float evaltime) -{ - FMod_Cycles *data= (FMod_Cycles *)fcm->data; - float prevkey[2], lastkey[2], cycyofs=0.0f; - short side=0, mode=0; - int cycles=0; - - /* check if modifier is first in stack, otherwise disable ourself... */ - // FIXME... - if (fcm->prev) { - fcm->flag |= FMODIFIER_FLAG_DISABLED; - return evaltime; - } - - /* calculate new evaltime due to cyclic interpolation */ - if (fcu && fcu->bezt) { - BezTriple *prevbezt= fcu->bezt; - BezTriple *lastbezt= prevbezt + fcu->totvert-1; - - prevkey[0]= prevbezt->vec[1][0]; - prevkey[1]= prevbezt->vec[1][1]; - - lastkey[0]= lastbezt->vec[1][0]; - lastkey[1]= lastbezt->vec[1][1]; - } - else if (fcu && fcu->fpt) { - FPoint *prevfpt= fcu->fpt; - FPoint *lastfpt= prevfpt + fcu->totvert-1; - - prevkey[0]= prevfpt->vec[0]; - prevkey[1]= prevfpt->vec[1]; - - lastkey[0]= lastfpt->vec[0]; - lastkey[1]= lastfpt->vec[1]; - } - else - return evaltime; - - /* check if modifier will do anything - * 1) if in data range, definitely don't do anything - * 2) if before first frame or after last frame, make sure some cycling is in use - */ - if (evaltime < prevkey[0]) { - if (data->before_mode) { - side= -1; - mode= data->before_mode; - cycles= data->before_cycles; - } - } - else if (evaltime > lastkey[0]) { - if (data->after_mode) { - side= 1; - mode= data->after_mode; - cycles= data->after_cycles; - } - } - if ELEM(0, side, mode) - return evaltime; - - /* find relative place within a cycle */ - { - float cycdx=0, cycdy=0, ofs=0; - float cycle= 0; - - /* ofs is start frame of cycle */ - ofs= prevkey[0]; - - /* calculate period and amplitude (total height) of a cycle */ - cycdx= lastkey[0] - prevkey[0]; - cycdy= lastkey[1] - prevkey[1]; - - /* check if cycle is infinitely small, to be point of being impossible to use */ - if (cycdx == 0) - return evaltime; - - /* calculate the 'number' of the cycle */ - cycle= ((float)side * (evaltime - ofs) / cycdx); - - /* check that cyclic is still enabled for the specified time */ - if (cycles == 0) { - /* catch this case so that we don't exit when we have cycles=0 - * as this indicates infinite cycles... - */ - } - else if (cycle > (cycles+1)) { - /* we are too far away from range to evaluate - * TODO: but we should still hold last value... - */ - return evaltime; - } - - /* check if 'cyclic extrapolation', and thus calculate y-offset for this cycle */ - if (mode == FCM_EXTRAPOLATE_CYCLIC_OFFSET) { - cycyofs = (float)floor((evaltime - ofs) / cycdx); - cycyofs *= cycdy; - } - - /* calculate where in the cycle we are (overwrite evaltime to reflect this) */ - if ((mode == FCM_EXTRAPOLATE_MIRROR) && ((int)(cycle) % 2)) { - /* when 'mirror' option is used and cycle number is odd, this cycle is played in reverse - * - for 'before' extrapolation, we need to flip in a different way, otherwise values past - * then end of the curve get referenced (result of fmod will be negative, and with different phase) - */ - if (side < 0) - evaltime= (float)(prevkey[0] - fmod(evaltime-ofs, cycdx)); - else - evaltime= (float)(lastkey[0] - fmod(evaltime-ofs, cycdx)); - } - else { - /* the cycle is played normally... */ - evaltime= (float)(fmod(evaltime-ofs, cycdx) + ofs); - } - if (evaltime < ofs) evaltime += cycdx; - } - - /* store temp data if needed */ - if (mode == FCM_EXTRAPOLATE_CYCLIC_OFFSET) { - tFCMED_Cycles *edata; - - /* for now, this is just a float, but we could get more stuff... */ - fcm->edata= edata= MEM_callocN(sizeof(tFCMED_Cycles), "tFCMED_Cycles"); - edata->cycyofs= cycyofs; - } - - /* return the new frame to evaluate */ - return evaltime; -} - -static void fcm_cycles_evaluate (FCurve *fcu, FModifier *fcm, float *cvalue, float evaltime) -{ - tFCMED_Cycles *edata= (tFCMED_Cycles *)fcm->edata; - - /* use temp data */ - if (edata) { - /* add cyclic offset - no need to check for now, otherwise the data wouldn't exist! */ - *cvalue += edata->cycyofs; - - /* free temp data */ - MEM_freeN(edata); - fcm->edata= NULL; - } -} - -static FModifierTypeInfo FMI_CYCLES = { - FMODIFIER_TYPE_CYCLES, /* type */ - sizeof(FMod_Cycles), /* size */ - FMI_TYPE_EXTRAPOLATION, /* action type */ - FMI_REQUIRES_ORIGINAL_DATA, /* requirements */ - "Cycles", /* name */ - "FMod_Cycles", /* struct name */ - NULL, /* free data */ - NULL, /* copy data */ - fcm_cycles_new_data, /* new data */ - NULL /*fcm_cycles_verify*/, /* verify */ - fcm_cycles_time, /* evaluate time */ - fcm_cycles_evaluate /* evaluate */ -}; - -/* Noise F-Curve Modifier --------------------------- */ - -static void fcm_noise_new_data (void *mdata) -{ - FMod_Noise *data= (FMod_Noise *)mdata; - - /* defaults */ - data->size= 1.0f; - data->strength= 1.0f; - data->phase= 1.0f; - data->depth = 0; - data->modification = FCM_NOISE_MODIF_REPLACE; -} - -static void fcm_noise_evaluate (FCurve *fcu, FModifier *fcm, float *cvalue, float evaltime) -{ - FMod_Noise *data= (FMod_Noise *)fcm->data; - float noise; - - noise = BLI_turbulence(data->size, evaltime, data->phase, 0.f, data->depth); - - switch (data->modification) { - case FCM_NOISE_MODIF_ADD: - *cvalue= *cvalue + noise * data->strength; - break; - case FCM_NOISE_MODIF_SUBTRACT: - *cvalue= *cvalue - noise * data->strength; - break; - case FCM_NOISE_MODIF_MULTIPLY: - *cvalue= *cvalue * noise * data->strength; - break; - case FCM_NOISE_MODIF_REPLACE: - default: - *cvalue= *cvalue + (noise - 0.5f) * data->strength; - break; - } -} - -static FModifierTypeInfo FMI_NOISE = { - FMODIFIER_TYPE_NOISE, /* type */ - sizeof(FMod_Noise), /* size */ - FMI_TYPE_REPLACE_VALUES, /* action type */ - 0, /* requirements */ - "Noise", /* name */ - "FMod_Noise", /* struct name */ - NULL, /* free data */ - NULL, /* copy data */ - fcm_noise_new_data, /* new data */ - NULL /*fcm_noise_verify*/, /* verify */ - NULL, /* evaluate time */ - fcm_noise_evaluate /* evaluate */ -}; - -/* Filter F-Curve Modifier --------------------------- */ - -#if 0 // XXX not yet implemented -static FModifierTypeInfo FMI_FILTER = { - FMODIFIER_TYPE_FILTER, /* type */ - sizeof(FMod_Filter), /* size */ - FMI_TYPE_REPLACE_VALUES, /* action type */ - 0, /* requirements */ - "Filter", /* name */ - "FMod_Filter", /* struct name */ - NULL, /* free data */ - NULL, /* copy data */ - NULL, /* new data */ - NULL /*fcm_filter_verify*/, /* verify */ - NULL, /* evlauate time */ - fcm_filter_evaluate /* evaluate */ -}; -#endif // XXX not yet implemented - - -/* Python F-Curve Modifier --------------------------- */ - -static void fcm_python_free (FModifier *fcm) -{ - FMod_Python *data= (FMod_Python *)fcm->data; - - /* id-properties */ - IDP_FreeProperty(data->prop); - MEM_freeN(data->prop); -} - -static void fcm_python_new_data (void *mdata) -{ - FMod_Python *data= (FMod_Python *)mdata; - - /* everything should be set correctly by calloc, except for the prop->type constant.*/ - data->prop = MEM_callocN(sizeof(IDProperty), "PyFModifierProps"); - data->prop->type = IDP_GROUP; -} - -static void fcm_python_copy (FModifier *fcm, FModifier *src) -{ - FMod_Python *pymod = (FMod_Python *)fcm->data; - FMod_Python *opymod = (FMod_Python *)src->data; - - pymod->prop = IDP_CopyProperty(opymod->prop); -} - -static void fcm_python_evaluate (FCurve *fcu, FModifier *fcm, float *cvalue, float evaltime) -{ -#ifndef DISABLE_PYTHON - //FMod_Python *data= (FMod_Python *)fcm->data; - - /* FIXME... need to implement this modifier... - * It will need it execute a script using the custom properties - */ -#endif /* DISABLE_PYTHON */ -} - -static FModifierTypeInfo FMI_PYTHON = { - FMODIFIER_TYPE_PYTHON, /* type */ - sizeof(FMod_Python), /* size */ - FMI_TYPE_GENERATE_CURVE, /* action type */ - FMI_REQUIRES_RUNTIME_CHECK, /* requirements */ - "Python", /* name */ - "FMod_Python", /* struct name */ - fcm_python_free, /* free data */ - fcm_python_copy, /* copy data */ - fcm_python_new_data, /* new data */ - NULL /*fcm_python_verify*/, /* verify */ - NULL /*fcm_python_time*/, /* evaluate time */ - fcm_python_evaluate /* evaluate */ -}; - - -/* Limits F-Curve Modifier --------------------------- */ - -static float fcm_limits_time (FCurve *fcu, FModifier *fcm, float cvalue, float evaltime) -{ - FMod_Limits *data= (FMod_Limits *)fcm->data; - - /* check for the time limits */ - if ((data->flag & FCM_LIMIT_XMIN) && (evaltime < data->rect.xmin)) - return data->rect.xmin; - if ((data->flag & FCM_LIMIT_XMAX) && (evaltime > data->rect.xmax)) - return data->rect.xmax; - - /* modifier doesn't change time */ - return evaltime; -} - -static void fcm_limits_evaluate (FCurve *fcu, FModifier *fcm, float *cvalue, float evaltime) -{ - FMod_Limits *data= (FMod_Limits *)fcm->data; - - /* value limits now */ - if ((data->flag & FCM_LIMIT_YMIN) && (*cvalue < data->rect.ymin)) - *cvalue= data->rect.ymin; - if ((data->flag & FCM_LIMIT_YMAX) && (*cvalue > data->rect.ymax)) - *cvalue= data->rect.ymax; -} - -static FModifierTypeInfo FMI_LIMITS = { - FMODIFIER_TYPE_LIMITS, /* type */ - sizeof(FMod_Limits), /* size */ - FMI_TYPE_GENERATE_CURVE, /* action type */ /* XXX... err... */ - FMI_REQUIRES_RUNTIME_CHECK, /* requirements */ - "Limits", /* name */ - "FMod_Limits", /* struct name */ - NULL, /* free data */ - NULL, /* copy data */ - NULL, /* new data */ - NULL, /* verify */ - fcm_limits_time, /* evaluate time */ - fcm_limits_evaluate /* evaluate */ -}; - -/* F-Curve Modifier API --------------------------- */ -/* All of the F-Curve Modifier api functions use FModifierTypeInfo structs to carry out - * and operations that involve F-Curve modifier specific code. - */ - -/* These globals only ever get directly accessed in this file */ -static FModifierTypeInfo *fmodifiersTypeInfo[FMODIFIER_NUM_TYPES]; -static short FMI_INIT= 1; /* when non-zero, the list needs to be updated */ - -/* This function only gets called when FMI_INIT is non-zero */ -static void fmods_init_typeinfo () -{ - fmodifiersTypeInfo[0]= NULL; /* 'Null' F-Curve Modifier */ - fmodifiersTypeInfo[1]= &FMI_GENERATOR; /* Generator F-Curve Modifier */ - fmodifiersTypeInfo[2]= &FMI_ENVELOPE; /* Envelope F-Curve Modifier */ - fmodifiersTypeInfo[3]= &FMI_CYCLES; /* Cycles F-Curve Modifier */ - fmodifiersTypeInfo[4]= &FMI_NOISE; /* Apply-Noise F-Curve Modifier */ - fmodifiersTypeInfo[5]= NULL/*&FMI_FILTER*/; /* Filter F-Curve Modifier */ // XXX unimplemented - fmodifiersTypeInfo[6]= &FMI_PYTHON; /* Custom Python F-Curve Modifier */ - fmodifiersTypeInfo[7]= &FMI_LIMITS; /* Limits F-Curve Modifier */ -} - -/* This function should be used for getting the appropriate type-info when only - * a F-Curve modifier type is known - */ -FModifierTypeInfo *get_fmodifier_typeinfo (int type) -{ - /* initialise the type-info list? */ - if (FMI_INIT) { - fmods_init_typeinfo(); - FMI_INIT = 0; - } - - /* only return for valid types */ - if ( (type >= FMODIFIER_TYPE_NULL) && - (type <= FMODIFIER_NUM_TYPES ) ) - { - /* there shouldn't be any segfaults here... */ - return fmodifiersTypeInfo[type]; - } - else { - printf("No valid F-Curve Modifier type-info data available. Type = %i \n", type); - } - - return NULL; -} - -/* This function should always be used to get the appropriate type-info, as it - * has checks which prevent segfaults in some weird cases. - */ -FModifierTypeInfo *fmodifier_get_typeinfo (FModifier *fcm) -{ - /* only return typeinfo for valid modifiers */ - if (fcm) - return get_fmodifier_typeinfo(fcm->type); - else - return NULL; -} - -/* API --------------------------- */ - -/* Add a new F-Curve Modifier to the given F-Curve of a certain type */ -FModifier *fcurve_add_modifier (FCurve *fcu, int type) -{ - FModifierTypeInfo *fmi= get_fmodifier_typeinfo(type); - FModifier *fcm; - - /* sanity checks */ - if ELEM(NULL, fcu, fmi) - return NULL; - - /* special checks for whether modifier can be added */ - if ((fcu->modifiers.first) && (type == FMODIFIER_TYPE_CYCLES)) { - /* cycles modifier must be first in stack, so for now, don't add if it can't be */ - // TODO: perhaps there is some better way, but for now, - printf("Error: Cannot add 'Cycles' modifier to F-Curve, as 'Cycles' modifier can only be first in stack. \n"); - return NULL; - } - - /* add modifier itself */ - fcm= MEM_callocN(sizeof(FModifier), "F-Curve Modifier"); - fcm->type = type; - fcm->flag = FMODIFIER_FLAG_EXPANDED; - BLI_addtail(&fcu->modifiers, fcm); - - /* add modifier's data */ - fcm->data= MEM_callocN(fmi->size, fmi->structName); - - /* init custom settings if necessary */ - if (fmi->new_data) - fmi->new_data(fcm->data); - - /* return modifier for further editing */ - return fcm; -} - -/* Duplicate all of the F-Curve Modifiers in the Modifier stacks */ -void fcurve_copy_modifiers (ListBase *dst, ListBase *src) -{ - FModifier *fcm, *srcfcm; - - if ELEM(NULL, dst, src) - return; - - dst->first= dst->last= NULL; - BLI_duplicatelist(dst, src); - - for (fcm=dst->first, srcfcm=src->first; fcm && srcfcm; srcfcm=srcfcm->next, fcm=fcm->next) { - FModifierTypeInfo *fmi= fmodifier_get_typeinfo(fcm); - - /* make a new copy of the F-Modifier's data */ - fcm->data = MEM_dupallocN(fcm->data); - - /* only do specific constraints if required */ - if (fmi && fmi->copy_data) - fmi->copy_data(fcm, srcfcm); - } -} - -/* Remove and free the given F-Curve Modifier from the given F-Curve's stack */ -void fcurve_remove_modifier (FCurve *fcu, FModifier *fcm) -{ - FModifierTypeInfo *fmi= fmodifier_get_typeinfo(fcm); - - /* sanity check */ - if (fcm == NULL) - return; - - /* free modifier's special data (stored inside fcm->data) */ - if (fcm->data) { - if (fmi && fmi->free_data) - fmi->free_data(fcm); - - /* free modifier's data (fcm->data) */ - MEM_freeN(fcm->data); - } - - /* remove modifier from stack */ - if (fcu) - BLI_freelinkN(&fcu->modifiers, fcm); - else { - // XXX this case can probably be removed some day, as it shouldn't happen... - printf("fcurve_remove_modifier() - no fcurve \n"); - MEM_freeN(fcm); - } -} - -/* Remove all of a given F-Curve's modifiers */ -void fcurve_free_modifiers (FCurve *fcu) -{ - FModifier *fcm, *fmn; - - /* sanity check */ - if (fcu == NULL) - return; - - /* free each modifier in order - modifier is unlinked from list and freed */ - for (fcm= fcu->modifiers.first; fcm; fcm= fmn) { - fmn= fcm->next; - fcurve_remove_modifier(fcu, fcm); - } -} - -/* Bake modifiers for given F-Curve to curve sample data, in the frame range defined - * by start and end (inclusive). - */ -void fcurve_bake_modifiers (FCurve *fcu, int start, int end) -{ - ChannelDriver *driver; - - /* sanity checks */ - // TODO: make these tests report errors using reports not printf's - if ELEM(NULL, fcu, fcu->modifiers.first) { - printf("Error: No F-Curve with F-Curve Modifiers to Bake\n"); - return; - } - - /* temporarily, disable driver while we sample, so that they don't influence the outcome */ - driver= fcu->driver; - fcu->driver= NULL; - - /* bake the modifiers, by sampling the curve at each frame */ - fcurve_store_samples(fcu, NULL, start, end, fcurve_samplingcb_evalcurve); - - /* free the modifiers now */ - fcurve_free_modifiers(fcu); - - /* restore driver */ - fcu->driver= driver; -} - -/* Find the active F-Curve Modifier */ -FModifier *fcurve_find_active_modifier (FCurve *fcu) -{ - FModifier *fcm; - - /* sanity checks */ - if ELEM(NULL, fcu, fcu->modifiers.first) - return NULL; - - /* loop over modifiers until 'active' one is found */ - for (fcm= fcu->modifiers.first; fcm; fcm= fcm->next) { - if (fcm->flag & FMODIFIER_FLAG_ACTIVE) - return fcm; - } - - /* no modifier is active */ - return NULL; -} - -/* Set the active F-Curve Modifier */ -void fcurve_set_active_modifier (FCurve *fcu, FModifier *fcm) -{ - FModifier *fm; - - /* sanity checks */ - if ELEM(NULL, fcu, fcu->modifiers.first) - return; - - /* deactivate all, and set current one active */ - for (fm= fcu->modifiers.first; fm; fm= fm->next) - fm->flag &= ~FMODIFIER_FLAG_ACTIVE; - - /* make given modifier active */ - if (fcm) - fcm->flag |= FMODIFIER_FLAG_ACTIVE; -} - /* ***************************** F-Curve - Evaluation ********************************* */ /* Evaluate and return the value of the given F-Curve at the specified frame ("evaltime") @@ -2262,7 +1263,6 @@ void fcurve_set_active_modifier (FCurve *fcu, FModifier *fcm) */ float evaluate_fcurve (FCurve *fcu, float evaltime) { - FModifier *fcm; float cvalue= 0.0f; float devaltime; @@ -2275,28 +1275,8 @@ float evaluate_fcurve (FCurve *fcu, float evaltime) evaltime= cvalue= evaluate_driver(fcu->driver, evaltime); } - /* evaluate time modifications imposed by some F-Curve Modifiers - * - this step acts as an optimisation to prevent the F-Curve stack being evaluated - * several times by modifiers requesting the time be modified, as the final result - * would have required using the modified time - * - modifiers only ever recieve the unmodified time, as subsequent modifiers should be - * working on the 'global' result of the modified curve, not some localised segment, - * so nevaltime gets set to whatever the last time-modifying modifier likes... - * - we start from the end of the stack, as only the last one matters for now - */ - devaltime= evaltime; - - for (fcm= fcu->modifiers.last; fcm; fcm= fcm->prev) { - FModifierTypeInfo *fmi= fmodifier_get_typeinfo(fcm); - - /* only evaluate if there's a callback for this */ - // TODO: implement the 'influence' control feature... - if (fmi && fmi->evaluate_modifier_time) { - if ((fcm->flag & (FMODIFIER_FLAG_DISABLED|FMODIFIER_FLAG_MUTED)) == 0) - devaltime= fmi->evaluate_modifier_time(fcu, fcm, cvalue, evaltime); - break; - } - } + /* evaluate modifiers which modify time to evaluate the base curve at */ + devaltime= evaluate_time_fmodifiers(&fcu->modifiers, fcu, cvalue, evaltime); /* evaluate curve-data * - 'devaltime' instead of 'evaltime', as this is the time that the last time-modifying @@ -2308,16 +1288,7 @@ float evaluate_fcurve (FCurve *fcu, float evaltime) cvalue= fcurve_eval_samples(fcu, fcu->fpt, devaltime); /* evaluate modifiers */ - for (fcm= fcu->modifiers.first; fcm; fcm= fcm->next) { - FModifierTypeInfo *fmi= fmodifier_get_typeinfo(fcm); - - /* only evaluate if there's a callback for this */ - // TODO: implement the 'influence' control feature... - if (fmi && fmi->evaluate_modifier) { - if ((fcm->flag & (FMODIFIER_FLAG_DISABLED|FMODIFIER_FLAG_MUTED)) == 0) - fmi->evaluate_modifier(fcu, fcm, &cvalue, evaltime); - } - } + evaluate_value_fmodifiers(&fcu->modifiers, fcu, &cvalue, evaltime); /* if curve can only have integral values, perform truncation (i.e. drop the decimal part) * here so that the curve can be sampled correctly @@ -2330,10 +1301,16 @@ float evaluate_fcurve (FCurve *fcu, float evaltime) } /* Calculate the value of the given F-Curve at the given frame, and set its curval */ -// TODO: will this be necessary? void calculate_fcurve (FCurve *fcu, float ctime) { - /* calculate and set curval (evaluates driver too) */ - fcu->curval= evaluate_fcurve(fcu, ctime); + /* only calculate + set curval (overriding the existing value) if curve has + * any data which warrants this... + */ + if ( (fcu->totvert) || (fcu->driver && !(fcu->driver->flag & DRIVER_FLAG_INVALID)) || + list_has_suitable_fmodifier(&fcu->modifiers, 0, FMI_TYPE_GENERATE_CURVE) ) + { + /* calculate and set curval (evaluates driver too if necessary) */ + fcu->curval= evaluate_fcurve(fcu, ctime); + } } diff --git a/source/blender/blenkernel/intern/fluidsim.c b/source/blender/blenkernel/intern/fluidsim.c index 14bff9b5d57..6c684994e30 100644 --- a/source/blender/blenkernel/intern/fluidsim.c +++ b/source/blender/blenkernel/intern/fluidsim.c @@ -28,11 +28,15 @@ * ***** END GPL LICENSE BLOCK ***** */ +#include "BLI_storage.h" /* _LARGEFILE_SOURCE */ + #include "MEM_guardedalloc.h" #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" +#include "DNA_object_fluidsim.h" #include "DNA_object_force.h" // for pointcache +#include "DNA_object_types.h" #include "DNA_particle_types.h" #include "DNA_scene_types.h" // N_T @@ -76,7 +80,7 @@ void fluidsim_init(FluidsimModifierData *fluidmd) if(!fss) return; - fss->type = OB_FSBND_NOSLIP; + fss->type = OB_FLUIDSIM_ENABLE; fss->show_advancedoptions = 0; fss->resolutionxyz = 50; @@ -110,7 +114,7 @@ void fluidsim_init(FluidsimModifierData *fluidmd) // no bounding box needed // todo - reuse default init from elbeem! - fss->typeFlags = 0; + fss->typeFlags = OB_FSBND_NOSLIP; fss->domainNovecgen = 0; fss->volumeInitType = 1; // volume fss->partSlipValue = 0.0; @@ -656,5 +660,20 @@ void initElbeemMesh(struct Scene *scene, struct Object *ob, dm->release(dm); } +void fluid_estimate_memory(Object *ob, FluidsimSettings *fss, char *value) +{ + Mesh *mesh; + + value[0]= '\0'; + + if(ob->type == OB_MESH) { + /* use mesh bounding box and object scaling */ + mesh= ob->data; + + fluid_get_bb(mesh->mvert, mesh->totvert, ob->obmat, fss->bbStart, fss->bbSize); + elbeemEstimateMemreq(fss->resolutionxyz, fss->bbSize[0],fss->bbSize[1],fss->bbSize[2], fss->maxRefine, value); + } +} + #endif // DISABLE_ELBEEM diff --git a/source/blender/blenkernel/intern/font.c b/source/blender/blenkernel/intern/font.c index c3cf6e06c09..70901778585 100644 --- a/source/blender/blenkernel/intern/font.c +++ b/source/blender/blenkernel/intern/font.c @@ -333,11 +333,11 @@ static VFontData *vfont_get_data(VFont *vfont) BLI_addtail(&ttfdata, tmpfnt); } } else { - pf= newPackedFile(vfont->name); + pf= newPackedFile(NULL, vfont->name); if(!tmpfnt) { - tpf= newPackedFile(vfont->name); + tpf= newPackedFile(NULL, vfont->name); // Add temporary packed file to globals tmpfnt= (struct TmpFont *) MEM_callocN(sizeof(struct TmpFont), "temp_font"); @@ -385,8 +385,8 @@ VFont *load_vfont(char *name) strcpy(dir, name); BLI_splitdirstring(dir, filename); - pf= newPackedFile(name); - tpf= newPackedFile(name); + pf= newPackedFile(NULL, name); + tpf= newPackedFile(NULL, name); is_builtin= 0; } @@ -682,7 +682,7 @@ struct chartrans *BKE_text_to_curve(Scene *scene, Object *ob, int mode) cu->ulheight = 0.05; if (cu->strinfo==NULL) /* old file */ - cu->strinfo = MEM_callocN((slen+1) * sizeof(CharInfo), "strinfo compat"); + cu->strinfo = MEM_callocN((slen+4) * sizeof(CharInfo), "strinfo compat"); custrinfo= cu->strinfo; if (cu->editfont) @@ -1145,7 +1145,7 @@ struct chartrans *BKE_text_to_curve(Scene *scene, Object *ob, int mode) if (cu->sepchar==0) { for (i= 0; i<slen; i++) { cha = (uintptr_t) mem[i]; - info = &(cu->strinfo[i]); + info = &(custrinfo[i]); if (info->mat_nr > (ob->totcol)) { /* printf("Error: Illegal material index (%d) in text object, setting to 0\n", info->mat_nr); */ info->mat_nr = 0; diff --git a/source/blender/blenkernel/intern/gpencil.c b/source/blender/blenkernel/intern/gpencil.c index 6086aa58d40..dd8f44c71d5 100644 --- a/source/blender/blenkernel/intern/gpencil.c +++ b/source/blender/blenkernel/intern/gpencil.c @@ -1,5 +1,5 @@ /** - * $Id: gpencil.c 19758 2009-04-16 13:10:08Z aligorith $ + * $Id$ * * ***** BEGIN GPL LICENSE BLOCK ***** * diff --git a/source/blender/blenkernel/intern/idprop.c b/source/blender/blenkernel/intern/idprop.c index 3be47778674..54366aadd92 100644 --- a/source/blender/blenkernel/intern/idprop.c +++ b/source/blender/blenkernel/intern/idprop.c @@ -230,16 +230,16 @@ void IDP_ResizeArray(IDProperty *prop, int newlen) */ newsize = (newsize >> 3) + (newsize < 9 ? 3 : 6) + newsize; - newarr = MEM_callocN(idp_size_table[prop->subtype]*newsize, "idproperty array resized"); + newarr = MEM_callocN(idp_size_table[(int)prop->subtype]*newsize, "idproperty array resized"); if (newlen >= prop->len) { /* newlen is bigger*/ - memcpy(newarr, prop->data.pointer, prop->len*idp_size_table[prop->subtype]); + memcpy(newarr, prop->data.pointer, prop->len*idp_size_table[(int)prop->subtype]); idp_resize_group_array(prop, newlen, newarr); } else { /* newlen is smaller*/ idp_resize_group_array(prop, newlen, newarr); - memcpy(newarr, prop->data.pointer, newlen*prop->len*idp_size_table[prop->subtype]); + memcpy(newarr, prop->data.pointer, newlen*prop->len*idp_size_table[(int)prop->subtype]); } MEM_freeN(prop->data.pointer); @@ -546,7 +546,7 @@ int IDP_EqualsProperties(IDProperty *prop1, IDProperty *prop2) return BSTR_EQ(IDP_String(prop1), IDP_String(prop2)); else if(prop1->type == IDP_ARRAY) { if(prop1->len == prop2->len && prop1->subtype == prop2->subtype) - return memcmp(IDP_Array(prop1), IDP_Array(prop2), idp_size_table[prop1->subtype]*prop1->len); + return memcmp(IDP_Array(prop1), IDP_Array(prop2), idp_size_table[(int)prop1->subtype]*prop1->len); else return 0; } diff --git a/source/blender/blenkernel/intern/image.c b/source/blender/blenkernel/intern/image.c index 8eef9984c92..93924d1ea0d 100644 --- a/source/blender/blenkernel/intern/image.c +++ b/source/blender/blenkernel/intern/image.c @@ -253,6 +253,10 @@ void free_image(Image *ima) if (ima->preview) { BKE_previewimg_free(&ima->preview); } + if (ima->render_text) { + MEM_freeN(ima->render_text); + ima->render_text= NULL; + } } /* only image block itself */ @@ -856,6 +860,9 @@ int BKE_imtype_is_movie(int imtype) case R_AVICODEC: case R_QUICKTIME: case R_FFMPEG: + case R_H264: + case R_THEORA: + case R_XVID: case R_FRAMESERVER: return 1; } @@ -866,7 +873,7 @@ void BKE_add_image_extension(Scene *scene, char *string, int imtype) { char *extension=""; - if(scene->r.imtype== R_IRIS) { + if(imtype== R_IRIS) { if(!BLI_testextensie(string, ".rgb")) extension= ".rgb"; } @@ -878,7 +885,7 @@ void BKE_add_image_extension(Scene *scene, char *string, int imtype) if(!BLI_testextensie(string, ".hdr")) extension= ".hdr"; } - else if(imtype==R_PNG || imtype==R_FFMPEG) { + else if (ELEM5(imtype, R_PNG, R_FFMPEG, R_H264, R_THEORA, R_XVID)) { if(!BLI_testextensie(string, ".png")) extension= ".png"; } @@ -1226,7 +1233,7 @@ int BKE_write_ibuf(Scene *scene, ImBuf *ibuf, char *name, int imtype, int subimt else if ((imtype==R_RADHDR)) { ibuf->ftype= RADHDR; } - else if (imtype==R_PNG || imtype==R_FFMPEG) { + else if (ELEM5(imtype, R_PNG, R_FFMPEG, R_H264, R_THEORA, R_XVID)) { ibuf->ftype= PNG; } #ifdef WITH_DDS @@ -1301,7 +1308,7 @@ int BKE_write_ibuf(Scene *scene, ImBuf *ibuf, char *name, int imtype, int subimt BLI_make_existing_file(name); - if(scene->r.scemode & R_STAMP_INFO) + if(scene->r.stamp & R_STAMP_ALL) BKE_stamp_info(scene, ibuf); ok = IMB_saveiff(ibuf, name, IB_rect | IB_zbuf | IB_zbuffloat); @@ -1431,7 +1438,7 @@ void BKE_image_signal(Image *ima, ImageUser *iuser, int signal) /* try to repack file */ if(ima->packedfile) { PackedFile *pf; - pf = newPackedFile(ima->name); + pf = newPackedFile(NULL, ima->name); if (pf) { freePackedFile(ima->packedfile); ima->packedfile = pf; @@ -1750,7 +1757,7 @@ static ImBuf *image_load_image_file(Image *ima, ImageUser *iuser, int cfra) /* make packed file for autopack */ if ((ima->packedfile == NULL) && (G.fileflags & G_AUTOPACK)) - ima->packedfile = newPackedFile(str); + ima->packedfile = newPackedFile(NULL, str); } if(ima->flag & IMA_DO_PREMUL) @@ -1812,7 +1819,7 @@ static ImBuf *image_get_render_result(Image *ima, ImageUser *iuser) Render *re= NULL; RenderResult *rr= NULL; - if(iuser->scene) { + if(iuser && iuser->scene) { re= RE_GetRender(iuser->scene->id.name); rr= RE_GetResult(re); } diff --git a/source/blender/blenkernel/intern/implicit.c b/source/blender/blenkernel/intern/implicit.c index 40c98c1d9cc..fc5213d5532 100644 --- a/source/blender/blenkernel/intern/implicit.c +++ b/source/blender/blenkernel/intern/implicit.c @@ -1600,6 +1600,10 @@ int implicit_solver (Object *ob, float frame, ClothModifierData *clmd, ListBase if(clmd->coll_parms->flags & CLOTH_COLLSETTINGS_FLAG_ENABLED) { + float temp = clmd->sim_parms->stepsPerFrame; + /* not too nice hack, but collisions need this correction -jahka */ + clmd->sim_parms->stepsPerFrame /= clmd->sim_parms->timescale; + // collisions // itstart(); @@ -1614,7 +1618,7 @@ int implicit_solver (Object *ob, float frame, ClothModifierData *clmd, ListBase // call collision function // TODO: check if "step" or "step+dt" is correct - dg - result = cloth_bvh_objcollision(ob, clmd, step, dt); + result = cloth_bvh_objcollision(ob, clmd, step/clmd->sim_parms->timescale, dt/clmd->sim_parms->timescale); // correct velocity again, just to be sure we had to change it due to adaptive collisions for(i = 0; i < numverts; i++) @@ -1637,6 +1641,9 @@ int implicit_solver (Object *ob, float frame, ClothModifierData *clmd, ListBase } } + /* restore original stepsPerFrame */ + clmd->sim_parms->stepsPerFrame = temp; + // X = Xnew; cp_lfvector(id->X, id->Xnew, numverts); @@ -1654,7 +1661,6 @@ int implicit_solver (Object *ob, float frame, ClothModifierData *clmd, ListBase 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 { diff --git a/source/blender/blenkernel/intern/ipo.c b/source/blender/blenkernel/intern/ipo.c index 8cbf25eaeed..cf7e486613b 100644 --- a/source/blender/blenkernel/intern/ipo.c +++ b/source/blender/blenkernel/intern/ipo.c @@ -58,6 +58,7 @@ #include "DNA_key_types.h" #include "DNA_material_types.h" #include "DNA_mesh_types.h" +#include "DNA_nla_types.h" #include "DNA_object_types.h" #include "DNA_object_force.h" #include "DNA_particle_types.h" @@ -85,6 +86,7 @@ #include "BKE_library.h" #include "BKE_main.h" #include "BKE_mesh.h" +#include "BKE_nla.h" #include "BKE_object.h" @@ -235,17 +237,15 @@ static char *ob_adrcodes_to_paths (int adrcode, int *array_index) *array_index= 1; return "delta_scale"; case OB_DSIZE_Z: *array_index= 2; return "delta_scale"; - -#if 0 - case OB_COL_R: - poin= &(ob->col[0]); break; + case OB_COL_R: + *array_index= 0; return "color"; case OB_COL_G: - poin= &(ob->col[1]); break; + *array_index= 1; return "color"; case OB_COL_B: - poin= &(ob->col[2]); break; + *array_index= 2; return "color"; case OB_COL_A: - poin= &(ob->col[3]); break; - + *array_index= 3; return "color"; +#if 0 case OB_PD_FSTR: if (ob->pd) poin= &(ob->pd->f_strength); break; @@ -545,7 +545,7 @@ static char *material_adrcodes_to_paths (int adrcode, int *array_index) return "ambient"; case MA_SPEC: - return "specularity"; + return "specular_reflection"; case MA_HARD: return "specular_hardness"; @@ -827,6 +827,10 @@ char *get_rna_access (int blocktype, int adrcode, char actname[], char constname char buf[512]; int dummy_index= 0; + /* hack: if constname is set, we can only be dealing with an Constraint curve */ + if (constname) + blocktype= ID_CO; + /* get property name based on blocktype */ switch (blocktype) { case ID_OB: /* object */ @@ -842,7 +846,7 @@ char *get_rna_access (int blocktype, int adrcode, char actname[], char constname break; case ID_CO: /* constraint */ - propname= constraint_adrcodes_to_paths(adrcode, &dummy_index); + propname= constraint_adrcodes_to_paths(adrcode, &dummy_index); break; case ID_TE: /* texture */ @@ -872,7 +876,10 @@ char *get_rna_access (int blocktype, int adrcode, char actname[], char constname /* XXX problematic blocktypes */ case ID_CU: /* curve */ - propname= "speed"; // XXX this was a 'dummy curve' that didn't really correspond to any real var... + /* this used to be a 'dummy' curve which got evaluated on the fly... + * now we've got real var for this! + */ + propname= "eval_time"; break; case ID_SEQ: /* sequencer strip */ @@ -1146,7 +1153,7 @@ static void icu_to_fcurves (ListBase *groups, ListBase *list, IpoCurve *icu, cha /* Add a new FModifier (Cyclic) instead of setting extend value * as that's the new equivilant of that option. */ - FModifier *fcm= fcurve_add_modifier(fcu, FMODIFIER_TYPE_CYCLES); + FModifier *fcm= add_fmodifier(&fcu->modifiers, FMODIFIER_TYPE_CYCLES); FMod_Cycles *data= (FMod_Cycles *)fcm->data; /* if 'offset' one is in use, set appropriate settings */ @@ -1465,6 +1472,87 @@ static void action_to_animdata (ID *id, bAction *act) action_to_animato(act, &adt->action->groups, &adt->action->curves, &adt->drivers); } +/* ------------------------- */ + +// TODO: +// - NLA group duplicators info +// - NLA curve/stride modifiers... + +/* Convert NLA-Strip to new system */ +static void nlastrips_to_animdata (ID *id, ListBase *strips) +{ + AnimData *adt= BKE_animdata_from_id(id); + NlaTrack *nlt = NULL; + NlaStrip *strip; + bActionStrip *as, *asn; + + /* for each one of the original strips, convert to a new strip and free the old... */ + for (as= strips->first; as; as= asn) { + asn= as->next; + + /* this old strip is only worth something if it had an action... */ + if (as->act) { + /* convert Action data (if not yet converted), storing the results in the same Action */ + action_to_animato(as->act, &as->act->groups, &as->act->curves, &adt->drivers); + + /* create a new-style NLA-strip which references this Action, then copy over relevant settings */ + { + /* init a new strip, and assign the action to it + * - no need to muck around with the user-counts, since this is just + * passing over the ref to the new owner, not creating an additional ref + */ + strip= MEM_callocN(sizeof(NlaStrip), "NlaStrip"); + strip->act= as->act; + + /* endpoints */ + strip->start= as->start; + strip->end= as->end; + strip->actstart= as->actstart; + strip->actend= as->actend; + + /* action reuse */ + strip->repeat= as->repeat; + strip->scale= as->scale; + if (as->flag & ACTSTRIP_LOCK_ACTION) strip->flag |= NLASTRIP_FLAG_SYNC_LENGTH; + + /* blending */ + strip->blendin= as->blendin; + strip->blendout= as->blendout; + strip->blendmode= (as->mode==ACTSTRIPMODE_ADD) ? NLASTRIP_MODE_ADD : NLASTRIP_MODE_REPLACE; + if (as->flag & ACTSTRIP_AUTO_BLENDS) strip->flag |= NLASTRIP_FLAG_AUTO_BLENDS; + + /* assorted setting flags */ + if (as->flag & ACTSTRIP_SELECT) strip->flag |= NLASTRIP_FLAG_SELECT; + if (as->flag & ACTSTRIP_ACTIVE) strip->flag |= NLASTRIP_FLAG_ACTIVE; + + if (as->flag & ACTSTRIP_MUTE) strip->flag |= NLASTRIP_FLAG_MUTED; + if (as->flag & ACTSTRIP_REVERSE) strip->flag |= NLASTRIP_FLAG_REVERSE; + + /* by default, we now always extrapolate, while in the past this was optional */ + if ((as->flag & ACTSTRIP_HOLDLASTFRAME)==0) + strip->extendmode= NLASTRIP_EXTEND_NOTHING; + } + + /* try to add this strip to the current NLA-Track (i.e. the 'last' one on the stack atm) */ + if (BKE_nlatrack_add_strip(nlt, strip) == 0) { + /* trying to add to the current failed (no space), + * so add a new track to the stack, and add to that... + */ + nlt= add_nlatrack(adt, NULL); + BKE_nlatrack_add_strip(nlt, strip); + } + } + + /* modifiers */ + // FIXME: for now, we just free them... + if (as->modifiers.first) + BLI_freelistN(&as->modifiers); + + /* free the old strip */ + BLI_freelinkN(strips, as); + } +} + /* *************************************************** */ /* External API - Only Called from do_versions() */ @@ -1511,18 +1599,34 @@ void do_versions_ipos_to_animato(Main *main) if (G.f & G_DEBUG) printf("\tconverting ob %s \n", id->name+2); /* check if object has any animation data */ - if ((ob->ipo) || (ob->action) || (ob->nlastrips.first)) { + if (ob->nlastrips.first) { /* Add AnimData block */ adt= BKE_id_add_animdata(id); - /* IPO first */ + /* IPO first to take into any non-NLA'd Object Animation */ if (ob->ipo) { ipo_to_animdata(id, ob->ipo, NULL, NULL); + ob->ipo->id.us--; ob->ipo= NULL; } - /* now Action */ + /* Action is skipped since it'll be used by some strip in the NLA anyway, + * causing errors with evaluation in the new evaluation pipeline + */ + if (ob->action) { + ob->action->id.us--; + ob->action= NULL; + } + + /* finally NLA */ + nlastrips_to_animdata(id, &ob->nlastrips); + } + else if ((ob->ipo) || (ob->action)) { + /* Add AnimData block */ + adt= BKE_id_add_animdata(id); + + /* Action first - so that Action name get conserved */ if (ob->action) { action_to_animdata(id, ob->action); @@ -1533,8 +1637,12 @@ void do_versions_ipos_to_animato(Main *main) } } - /* finally NLA */ - // XXX todo... for now, new NLA code not hooked up yet, so keep old stuff (but not for too long!) + /* IPO second... */ + if (ob->ipo) { + ipo_to_animdata(id, ob->ipo, NULL, NULL); + ob->ipo->id.us--; + ob->ipo= NULL; + } } /* check PoseChannels for constraints with local data */ diff --git a/source/blender/blenkernel/intern/lattice.c b/source/blender/blenkernel/intern/lattice.c index 5cf52d09314..67d63d527cb 100644 --- a/source/blender/blenkernel/intern/lattice.c +++ b/source/blender/blenkernel/intern/lattice.c @@ -232,8 +232,8 @@ void free_lattice(Lattice *lt) if(lt->def) MEM_freeN(lt->def); if(lt->dvert) free_dverts(lt->dvert, lt->pntsu*lt->pntsv*lt->pntsw); if(lt->editlatt) { - if(lt->def) MEM_freeN(lt->def); - if(lt->dvert) free_dverts(lt->dvert, lt->pntsu*lt->pntsv*lt->pntsw); + if(lt->editlatt->def) MEM_freeN(lt->editlatt->def); + if(lt->editlatt->dvert) free_dverts(lt->editlatt->dvert, lt->pntsu*lt->pntsv*lt->pntsw); MEM_freeN(lt->editlatt); } } @@ -817,59 +817,68 @@ void outside_lattice(Lattice *lt) int u, v, w; float fac1, du=0.0, dv=0.0, dw=0.0; - bp= lt->def; + if(lt->flag & LT_OUTSIDE) { + bp= lt->def; - if(lt->pntsu>1) du= 1.0f/((float)lt->pntsu-1); - if(lt->pntsv>1) dv= 1.0f/((float)lt->pntsv-1); - if(lt->pntsw>1) dw= 1.0f/((float)lt->pntsw-1); - - for(w=0; w<lt->pntsw; w++) { - - for(v=0; v<lt->pntsv; v++) { - - for(u=0; u<lt->pntsu; u++, bp++) { - if(u==0 || v==0 || w==0 || u==lt->pntsu-1 || v==lt->pntsv-1 || w==lt->pntsw-1); - else { - - bp->hide= 1; - bp->f1 &= ~SELECT; - - /* u extrema */ - bp1= latt_bp(lt, 0, v, w); - bp2= latt_bp(lt, lt->pntsu-1, v, w); - - fac1= du*u; - bp->vec[0]= (1.0f-fac1)*bp1->vec[0] + fac1*bp2->vec[0]; - bp->vec[1]= (1.0f-fac1)*bp1->vec[1] + fac1*bp2->vec[1]; - bp->vec[2]= (1.0f-fac1)*bp1->vec[2] + fac1*bp2->vec[2]; - - /* v extrema */ - bp1= latt_bp(lt, u, 0, w); - bp2= latt_bp(lt, u, lt->pntsv-1, w); - - fac1= dv*v; - bp->vec[0]+= (1.0f-fac1)*bp1->vec[0] + fac1*bp2->vec[0]; - bp->vec[1]+= (1.0f-fac1)*bp1->vec[1] + fac1*bp2->vec[1]; - bp->vec[2]+= (1.0f-fac1)*bp1->vec[2] + fac1*bp2->vec[2]; - - /* w extrema */ - bp1= latt_bp(lt, u, v, 0); - bp2= latt_bp(lt, u, v, lt->pntsw-1); - - fac1= dw*w; - bp->vec[0]+= (1.0f-fac1)*bp1->vec[0] + fac1*bp2->vec[0]; - bp->vec[1]+= (1.0f-fac1)*bp1->vec[1] + fac1*bp2->vec[1]; - bp->vec[2]+= (1.0f-fac1)*bp1->vec[2] + fac1*bp2->vec[2]; - - VecMulf(bp->vec, 0.3333333f); + if(lt->pntsu>1) du= 1.0f/((float)lt->pntsu-1); + if(lt->pntsv>1) dv= 1.0f/((float)lt->pntsv-1); + if(lt->pntsw>1) dw= 1.0f/((float)lt->pntsw-1); + + for(w=0; w<lt->pntsw; w++) { + + for(v=0; v<lt->pntsv; v++) { + + for(u=0; u<lt->pntsu; u++, bp++) { + if(u==0 || v==0 || w==0 || u==lt->pntsu-1 || v==lt->pntsv-1 || w==lt->pntsw-1); + else { + bp->hide= 1; + bp->f1 &= ~SELECT; + + /* u extrema */ + bp1= latt_bp(lt, 0, v, w); + bp2= latt_bp(lt, lt->pntsu-1, v, w); + + fac1= du*u; + bp->vec[0]= (1.0f-fac1)*bp1->vec[0] + fac1*bp2->vec[0]; + bp->vec[1]= (1.0f-fac1)*bp1->vec[1] + fac1*bp2->vec[1]; + bp->vec[2]= (1.0f-fac1)*bp1->vec[2] + fac1*bp2->vec[2]; + + /* v extrema */ + bp1= latt_bp(lt, u, 0, w); + bp2= latt_bp(lt, u, lt->pntsv-1, w); + + fac1= dv*v; + bp->vec[0]+= (1.0f-fac1)*bp1->vec[0] + fac1*bp2->vec[0]; + bp->vec[1]+= (1.0f-fac1)*bp1->vec[1] + fac1*bp2->vec[1]; + bp->vec[2]+= (1.0f-fac1)*bp1->vec[2] + fac1*bp2->vec[2]; + + /* w extrema */ + bp1= latt_bp(lt, u, v, 0); + bp2= latt_bp(lt, u, v, lt->pntsw-1); + + fac1= dw*w; + bp->vec[0]+= (1.0f-fac1)*bp1->vec[0] + fac1*bp2->vec[0]; + bp->vec[1]+= (1.0f-fac1)*bp1->vec[1] + fac1*bp2->vec[1]; + bp->vec[2]+= (1.0f-fac1)*bp1->vec[2] + fac1*bp2->vec[2]; + + VecMulf(bp->vec, 0.3333333f); + + } } + } } - } - + else { + bp= lt->def; + + for(w=0; w<lt->pntsw; w++) + for(v=0; v<lt->pntsv; v++) + for(u=0; u<lt->pntsu; u++, bp++) + bp->hide= 0; + } } float (*lattice_getVertexCos(struct Object *ob, int *numVerts_r))[3] diff --git a/source/blender/blenkernel/intern/material.c b/source/blender/blenkernel/intern/material.c index b410c521dea..d1fdac65dbb 100644 --- a/source/blender/blenkernel/intern/material.c +++ b/source/blender/blenkernel/intern/material.c @@ -43,6 +43,7 @@ #include "DNA_object_types.h" #include "DNA_scene_types.h" #include "DNA_texture_types.h" +#include "DNA_userdef_types.h" #include "BLI_blenlib.h" #include "BLI_arithb.h" @@ -445,7 +446,7 @@ Material *give_current_material(Object *ob, int act) if(act>ob->totcol) act= ob->totcol; else if(act<=0) act= 1; - if( BTST(ob->colbits, act-1) ) { /* in object */ + if(ob->matbits[act-1]) { /* in object */ ma= ob->mat[act-1]; } else { /* in data */ @@ -473,7 +474,7 @@ ID *material_from(Object *ob, int act) if(ob->totcol==0) return ob->data; if(act==0) act= 1; - if( BTST(ob->colbits, act-1) ) return (ID *)ob; + if(ob->matbits[act-1]) return (ID *)ob; else return ob->data; } @@ -498,6 +499,7 @@ void test_object_materials(ID *id) Curve *cu; MetaBall *mb; Material **newmatar; + char *newmatbits; int totcol=0; if(id==0) return; @@ -524,16 +526,22 @@ void test_object_materials(ID *id) if(totcol==0) { if(ob->totcol) { MEM_freeN(ob->mat); - ob->mat= 0; + MEM_freeN(ob->matbits); + ob->mat= NULL; + ob->matbits= NULL; } } else if(ob->totcol<totcol) { newmatar= MEM_callocN(sizeof(void *)*totcol, "newmatar"); + newmatbits= MEM_callocN(sizeof(char)*totcol, "newmatbits"); if(ob->totcol) { memcpy(newmatar, ob->mat, sizeof(void *)*ob->totcol); + memcpy(newmatbits, ob->matbits, sizeof(char)*ob->totcol); MEM_freeN(ob->mat); + MEM_freeN(ob->matbits); } ob->mat= newmatar; + ob->matbits= newmatbits; } ob->totcol= totcol; if(ob->totcol && ob->actcol==0) ob->actcol= 1; @@ -547,6 +555,7 @@ void test_object_materials(ID *id) void assign_material(Object *ob, Material *ma, int act) { Material *mao, **matar, ***matarar; + char *matbits; short *totcolp; if(act>MAXMAT) return; @@ -559,29 +568,41 @@ void assign_material(Object *ob, Material *ma, int act) if(totcolp==0 || matarar==0) return; - if( act > *totcolp) { + if(act > *totcolp) { matar= MEM_callocN(sizeof(void *)*act, "matarray1"); - if( *totcolp) { - memcpy(matar, *matarar, sizeof(void *)*( *totcolp )); + + if(*totcolp) { + memcpy(matar, *matarar, sizeof(void *)*(*totcolp)); MEM_freeN(*matarar); } + *matarar= matar; *totcolp= act; } if(act > ob->totcol) { matar= MEM_callocN(sizeof(void *)*act, "matarray2"); + matbits= MEM_callocN(sizeof(char)*act, "matbits1"); if( ob->totcol) { memcpy(matar, ob->mat, sizeof(void *)*( ob->totcol )); + memcpy(matbits, ob->matbits, sizeof(char)*(*totcolp)); MEM_freeN(ob->mat); + MEM_freeN(ob->matbits); } ob->mat= matar; + ob->matbits= matbits; ob->totcol= act; + + /* copy object/mesh linking, or assign based on userpref */ + if(ob->actcol) + ob->matbits[act-1]= ob->matbits[ob->actcol-1]; + else + ob->matbits[act-1]= (U.flag & USER_MAT_ON_OB)? 1: 0; } /* do it */ - if( BTST(ob->colbits, act-1) ) { /* in object */ + if(ob->matbits[act-1]) { /* in object */ mao= ob->mat[act-1]; if(mao) mao->id.us--; ob->mat[act-1]= ma; @@ -591,6 +612,7 @@ void assign_material(Object *ob, Material *ma, int act) if(mao) mao->id.us--; (*matarar)[act-1]= ma; } + id_us_plus((ID *)ma); test_object_materials(ob->data); } @@ -615,7 +637,7 @@ int find_material_index(Object *ob, Material *ma) return 0; } -void new_material_to_objectdata(Object *ob) +void object_add_material_slot(Object *ob) { Material *ma; @@ -630,12 +652,6 @@ void new_material_to_objectdata(Object *ob) ma->id.us= 0; /* eeh... */ - if(ob->actcol) { - if( BTST(ob->colbits, ob->actcol-1) ) { - ob->colbits= BSET(ob->colbits, ob->totcol); - } - } - assign_material(ob, ma, ob->totcol+1); ob->actcol= ob->totcol; } @@ -854,7 +870,7 @@ void automatname(Material *ma) } -void delete_material_index(Object *ob) +void object_remove_material_slot(Object *ob) { Material *mao, ***matarar; Object *obt; @@ -880,9 +896,8 @@ void delete_material_index(Object *ob) if(mao) mao->id.us--; } - for(a=ob->actcol; a<ob->totcol; a++) { + for(a=ob->actcol; a<ob->totcol; a++) (*matarar)[a-1]= (*matarar)[a]; - } (*totcolp)--; if(*totcolp==0) { @@ -900,13 +915,18 @@ void delete_material_index(Object *ob) mao= obt->mat[actcol-1]; if(mao) mao->id.us--; - for(a=actcol; a<obt->totcol; a++) obt->mat[a-1]= obt->mat[a]; + for(a=actcol; a<obt->totcol; a++) { + obt->mat[a-1]= obt->mat[a]; + obt->matbits[a-1]= obt->matbits[a]; + } obt->totcol--; if(obt->actcol > obt->totcol) obt->actcol= obt->totcol; if(obt->totcol==0) { MEM_freeN(obt->mat); + MEM_freeN(obt->matbits); obt->mat= 0; + obt->matbits= NULL; } } obt= obt->id.next; diff --git a/source/blender/blenkernel/intern/mesh.c b/source/blender/blenkernel/intern/mesh.c index 0af3196de92..8f67fdc74d2 100644 --- a/source/blender/blenkernel/intern/mesh.c +++ b/source/blender/blenkernel/intern/mesh.c @@ -81,6 +81,7 @@ EditMesh *BKE_mesh_get_editmesh(Mesh *me) void BKE_mesh_end_editmesh(Mesh *me, EditMesh *em) { + BM_Free_Mesh(me->edit_btmesh->bm); me->edit_btmesh->bm = editmesh_to_bmesh(em); BMEdit_RecalcTesselation(me->edit_btmesh); } @@ -97,12 +98,11 @@ void mesh_update_customdata_pointers(Mesh *me) me->mface = CustomData_get_layer(&me->fdata, CD_MFACE); me->mcol = CustomData_get_layer(&me->fdata, CD_MCOL); me->mtface = CustomData_get_layer(&me->fdata, CD_MTFACE); - + me->mpoly = CustomData_get_layer(&me->pdata, CD_MPOLY); me->mloop = CustomData_get_layer(&me->ldata, CD_MLOOP); me->mtpoly = CustomData_get_layer(&me->pdata, CD_MTEXPOLY); - me->mloopcol = CustomData_get_layer(&me->ldata, CD_MLOOPCOL); me->mloopuv = CustomData_get_layer(&me->ldata, CD_MLOOPUV); } diff --git a/source/blender/blenkernel/intern/modifier.c b/source/blender/blenkernel/intern/modifier.c index 4e1f6d8265a..fc4788d9ff3 100644 --- a/source/blender/blenkernel/intern/modifier.c +++ b/source/blender/blenkernel/intern/modifier.c @@ -64,6 +64,7 @@ #include "DNA_meshdata_types.h" #include "DNA_modifier_types.h" #include "DNA_object_types.h" +#include "DNA_object_fluidsim.h" #include "DNA_object_force.h" #include "DNA_particle_types.h" #include "DNA_scene_types.h" @@ -119,6 +120,72 @@ static struct DerivedMesh *NewBooleanDerivedMesh() {return NULL;} //XXX #include "BIF_meshlaplacian.h" +/* Utility */ + +static int is_last_displist(Object *ob) +{ + Curve *cu = ob->data; + static int curvecount=0, totcurve=0; + + if(curvecount == 0){ + DispList *dl; + + totcurve = 0; + for(dl=cu->disp.first; dl; dl=dl->next) + totcurve++; + } + + curvecount++; + + if(curvecount == totcurve){ + curvecount = 0; + return 1; + } + + return 0; +} + +static DerivedMesh *get_original_dm(Scene *scene, Object *ob, float (*vertexCos)[3], int orco) +{ + DerivedMesh *dm= NULL; + + if(ob->type==OB_MESH) { + dm = CDDM_from_mesh((Mesh*)(ob->data), ob); + + if(vertexCos) { + CDDM_apply_vert_coords(dm, vertexCos); + //CDDM_calc_normals(dm); + } + + if(orco) + DM_add_vert_layer(dm, CD_ORCO, CD_ASSIGN, get_mesh_orco_verts(ob)); + } + else if(ELEM3(ob->type,OB_FONT,OB_CURVE,OB_SURF)) { + Object *tmpobj; + Curve *tmpcu; + + if(is_last_displist(ob)) { + /* copies object and modifiers (but not the data) */ + tmpobj= copy_object(ob); + tmpcu = (Curve *)tmpobj->data; + tmpcu->id.us--; + + /* copies the data */ + tmpobj->data = copy_curve((Curve *) ob->data); + + makeDispListCurveTypes(scene, tmpobj, 1); + nurbs_to_mesh(tmpobj); + + dm = CDDM_from_mesh((Mesh*)(tmpobj->data), tmpobj); + //CDDM_calc_normals(dm); + + free_libblock_us(&G.main->object, tmpobj); + } + } + + return dm; +} + /***/ static int noneModifier_isDisabled(ModifierData *md) @@ -145,7 +212,7 @@ static void curveModifier_copyData(ModifierData *md, ModifierData *target) strncpy(tcmd->name, cmd->name, 32); } -CustomDataMask curveModifier_requiredDataMask(ModifierData *md) +CustomDataMask curveModifier_requiredDataMask(Object *ob, ModifierData *md) { CurveModifierData *cmd = (CurveModifierData *)md; CustomDataMask dataMask = 0; @@ -221,7 +288,7 @@ static void latticeModifier_copyData(ModifierData *md, ModifierData *target) strncpy(tlmd->name, lmd->name, 32); } -CustomDataMask latticeModifier_requiredDataMask(ModifierData *md) +CustomDataMask latticeModifier_requiredDataMask(Object *ob, ModifierData *md) { LatticeModifierData *lmd = (LatticeModifierData *)md; CustomDataMask dataMask = 0; @@ -605,7 +672,7 @@ static void maskModifier_copyData(ModifierData *md, ModifierData *target) strcpy(tmmd->vgroup, mmd->vgroup); } -static CustomDataMask maskModifier_requiredDataMask(ModifierData *md) +static CustomDataMask maskModifier_requiredDataMask(Object *ob, ModifierData *md) { return (1 << CD_MDEFORMVERT); } @@ -3353,7 +3420,7 @@ static void bevelModifier_copyData(ModifierData *md, ModifierData *target) strncpy(tbmd->defgrp_name, bmd->defgrp_name, 32); } -CustomDataMask bevelModifier_requiredDataMask(ModifierData *md) +CustomDataMask bevelModifier_requiredDataMask(Object *ob, ModifierData *md) { BevelModifierData *bmd = (BevelModifierData *)md; CustomDataMask dataMask = 0; @@ -3433,7 +3500,7 @@ static void displaceModifier_copyData(ModifierData *md, ModifierData *target) strncpy(tdmd->uvlayer_name, dmd->uvlayer_name, 32); } -CustomDataMask displaceModifier_requiredDataMask(ModifierData *md) +CustomDataMask displaceModifier_requiredDataMask(Object *ob, ModifierData *md) { DisplaceModifierData *dmd = (DisplaceModifierData *)md; CustomDataMask dataMask = 0; @@ -3500,7 +3567,7 @@ static void displaceModifier_updateDepgraph( } } -static void validate_layer_name(const CustomData *data, int type, char *name) +static void validate_layer_name(const CustomData *data, int type, char *name, char *outname) { int index = -1; @@ -3513,8 +3580,10 @@ static void validate_layer_name(const CustomData *data, int type, char *name) * deleted, so assign the active layer to name */ index = CustomData_get_active_layer_index(data, CD_MTFACE); - strcpy(name, data->layers[index].name); + strcpy(outname, data->layers[index].name); } + else + strcpy(outname, name); } static void get_texture_coords(DisplaceModifierData *dmd, Object *ob, @@ -3540,12 +3609,11 @@ static void get_texture_coords(DisplaceModifierData *dmd, Object *ob, char *done = MEM_callocN(sizeof(*done) * numVerts, "get_texture_coords done"); int numFaces = dm->getNumTessFaces(dm); + char uvname[32]; MTFace *tf; - validate_layer_name(&dm->faceData, CD_MTFACE, dmd->uvlayer_name); - - tf = CustomData_get_layer_named(&dm->faceData, CD_MTFACE, - dmd->uvlayer_name); + validate_layer_name(&dm->faceData, CD_MTFACE, dmd->uvlayer_name, uvname); + tf = CustomData_get_layer_named(&dm->faceData, CD_MTFACE, uvname); /* verts are given the UV from the first face that uses them */ for(i = 0, mf = mface; i < numFaces; ++i, ++mf, ++tf) { @@ -3776,7 +3844,7 @@ static void uvprojectModifier_copyData(ModifierData *md, ModifierData *target) tumd->aspecty = umd->aspecty; } -CustomDataMask uvprojectModifier_requiredDataMask(ModifierData *md) +CustomDataMask uvprojectModifier_requiredDataMask(Object *ob, ModifierData *md) { CustomDataMask dataMask = 0; @@ -3841,6 +3909,7 @@ static DerivedMesh *uvprojectModifier_do(UVProjectModifierData *umd, Projector projectors[MOD_UVPROJECT_MAXPROJECTORS]; int num_projectors = 0; float aspect; + char uvname[32]; if(umd->aspecty != 0) aspect = umd->aspectx / umd->aspecty; else aspect = 1.0f; @@ -3855,12 +3924,11 @@ static DerivedMesh *uvprojectModifier_do(UVProjectModifierData *umd, if(!dm->getTessFaceDataArray(dm, CD_MTFACE)) return dm; /* make sure we're using an existing layer */ - validate_layer_name(&dm->faceData, CD_MTFACE, umd->uvlayer_name); + validate_layer_name(&dm->faceData, CD_MTFACE, umd->uvlayer_name, uvname); /* 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, uvname); numVerts = dm->getNumVerts(dm); @@ -4242,7 +4310,7 @@ int smoothModifier_isDisabled(ModifierData *md) return 0; } -CustomDataMask smoothModifier_requiredDataMask(ModifierData *md) +CustomDataMask smoothModifier_requiredDataMask(Object *ob, ModifierData *md) { SmoothModifierData *smd = (SmoothModifierData *)md; CustomDataMask dataMask = 0; @@ -4471,7 +4539,7 @@ int castModifier_isDisabled(ModifierData *md) return 0; } -CustomDataMask castModifier_requiredDataMask(ModifierData *md) +CustomDataMask castModifier_requiredDataMask(Object *ob, ModifierData *md) { CastModifierData *cmd = (CastModifierData *)md; CustomDataMask dataMask = 0; @@ -5102,7 +5170,7 @@ static void waveModifier_updateDepgraph( } } -CustomDataMask waveModifier_requiredDataMask(ModifierData *md) +CustomDataMask waveModifier_requiredDataMask(Object *ob, ModifierData *md) { WaveModifierData *wmd = (WaveModifierData *)md; CustomDataMask dataMask = 0; @@ -5142,12 +5210,11 @@ static void wavemod_get_texture_coords(WaveModifierData *wmd, Object *ob, char *done = MEM_callocN(sizeof(*done) * numVerts, "get_texture_coords done"); int numFaces = dm->getNumTessFaces(dm); + char uvname[32]; MTFace *tf; - validate_layer_name(&dm->faceData, CD_MTFACE, wmd->uvlayer_name); - - tf = CustomData_get_layer_named(&dm->faceData, CD_MTFACE, - wmd->uvlayer_name); + validate_layer_name(&dm->faceData, CD_MTFACE, wmd->uvlayer_name, uvname); + tf = CustomData_get_layer_named(&dm->faceData, CD_MTFACE, uvname); /* verts are given the UV from the first face that uses them */ for(i = 0, mf = mface; i < numFaces; ++i, ++mf, ++tf) { @@ -5439,7 +5506,7 @@ static void armatureModifier_copyData(ModifierData *md, ModifierData *target) strncpy(tamd->defgrp_name, amd->defgrp_name, 32); } -CustomDataMask armatureModifier_requiredDataMask(ModifierData *md) +CustomDataMask armatureModifier_requiredDataMask(Object *ob, ModifierData *md) { CustomDataMask dataMask = 0; @@ -5553,7 +5620,7 @@ static void hookModifier_copyData(ModifierData *md, ModifierData *target) strncpy(thmd->name, hmd->name, 32); } -CustomDataMask hookModifier_requiredDataMask(ModifierData *md) +CustomDataMask hookModifier_requiredDataMask(Object *ob, ModifierData *md) { HookModifierData *hmd = (HookModifierData *)md; CustomDataMask dataMask = 0; @@ -5812,7 +5879,7 @@ static void clothModifier_updateDepgraph( } } -CustomDataMask clothModifier_requiredDataMask(ModifierData *md) +CustomDataMask clothModifier_requiredDataMask(Object *ob, ModifierData *md) { CustomDataMask dataMask = 0; @@ -6069,7 +6136,8 @@ static void surfaceModifier_freeData(ModifierData *md) MEM_freeN(surmd->bvhtree); } - surmd->dm->release(surmd->dm); + if(surmd->dm) + surmd->dm->release(surmd->dm); surmd->bvhtree = NULL; surmd->dm = NULL; @@ -6093,7 +6161,7 @@ static void surfaceModifier_deformVerts( /* if possible use/create DerivedMesh */ if(derivedData) surmd->dm = CDDM_copy(derivedData); - else if(ob->type==OB_MESH) surmd->dm = CDDM_from_mesh(ob->data, ob); + else surmd->dm = get_original_dm(md->scene, ob, NULL, 0); if(!ob->pd) { @@ -6195,7 +6263,7 @@ static DerivedMesh *booleanModifier_applyModifier( return derivedData; } -CustomDataMask booleanModifier_requiredDataMask(ModifierData *md) +CustomDataMask booleanModifier_requiredDataMask(Object *ob, ModifierData *md) { CustomDataMask dataMask = (1 << CD_MTFACE) + (1 << CD_MEDGE); @@ -6243,12 +6311,30 @@ static void particleSystemModifier_copyData(ModifierData *md, ModifierData *targ tpsmd->psys = psmd->psys; } -CustomDataMask particleSystemModifier_requiredDataMask(ModifierData *md) +CustomDataMask particleSystemModifier_requiredDataMask(Object *ob, ModifierData *md) { ParticleSystemModifierData *psmd= (ParticleSystemModifierData*) md; - CustomDataMask dataMask = (1 << CD_MTFACE) + (1 << CD_MEDGE); + CustomDataMask dataMask = 0; + Material *ma; + MTex *mtex; int i; + if(!psmd->psys->part) + return 0; + + ma= give_current_material(ob, psmd->psys->part->omat); + if(ma) { + for(i=0; i<MAX_MTEX; i++) { + mtex=ma->mtex[i]; + if(mtex && (ma->septex & (1<<i))==0) + if(mtex->pmapto && (mtex->texco & TEXCO_UV)) + dataMask |= (1 << CD_MTFACE); + } + } + + if(psmd->psys->part->tanfac!=0.0) + dataMask |= (1 << CD_MTFACE); + /* ask for vertexgroups if we need them */ for(i=0; i<PSYS_TOT_VG; i++){ if(psmd->psys->vgroup[i]){ @@ -6265,70 +6351,6 @@ CustomDataMask particleSystemModifier_requiredDataMask(ModifierData *md) return dataMask; } -static int is_last_displist(Object *ob) -{ - Curve *cu = ob->data; - static int curvecount=0, totcurve=0; - - if(curvecount==0){ - DispList *dl; - - totcurve=0; - for(dl=cu->disp.first; dl; dl=dl->next){ - totcurve++; - } - } - - curvecount++; - - if(curvecount==totcurve){ - curvecount=0; - return 1; - } - - return 0; -} - -static DerivedMesh *get_original_dm(Scene *scene, Object *ob, float (*vertexCos)[3], int orco) -{ - DerivedMesh *dm= NULL; - - if(ob->type==OB_MESH) { - dm = CDDM_from_mesh((Mesh*)(ob->data), ob); - - if(vertexCos) { - CDDM_apply_vert_coords(dm, vertexCos); - //CDDM_calc_normals(dm); - } - - if(orco) - DM_add_vert_layer(dm, CD_ORCO, CD_ASSIGN, get_mesh_orco_verts(ob)); - } - else if(ELEM3(ob->type,OB_FONT,OB_CURVE,OB_SURF)) { - Object *tmpobj; - Curve *tmpcu; - - if(is_last_displist(ob)) { - /* copies object and modifiers (but not the data) */ - tmpobj= copy_object(ob); - tmpcu = (Curve *)tmpobj->data; - tmpcu->id.us--; - - /* copies the data */ - tmpobj->data = copy_curve((Curve *) ob->data); - - makeDispListCurveTypes(scene, tmpobj, 1); - nurbs_to_mesh(tmpobj); - - dm = CDDM_from_mesh((Mesh*)(tmpobj->data), tmpobj); - //CDDM_calc_normals(dm); - - free_libblock_us(&G.main->object, tmpobj); - } - } - - return dm; -} /* saves the current emitter state for a particle system and calculates particles */ static void particleSystemModifier_deformVerts( @@ -6391,6 +6413,7 @@ static void particleSystemModifier_deformVerts( } if(psys){ + psmd->flag &= ~eParticleSystemFlag_psys_updated; particle_system_update(md->scene, ob, psys); psmd->flag |= eParticleSystemFlag_psys_updated; psmd->flag &= ~eParticleSystemFlag_DM_changed; @@ -6422,6 +6445,8 @@ static void particleInstanceModifier_initData(ModifierData *md) pimd->flag = eParticleInstanceFlag_Parents|eParticleInstanceFlag_Unborn| eParticleInstanceFlag_Alive|eParticleInstanceFlag_Dead; pimd->psys = 1; + pimd->position = 1.0f; + pimd->axis = 2; } static void particleInstanceModifier_copyData(ModifierData *md, ModifierData *target) @@ -6432,6 +6457,8 @@ static void particleInstanceModifier_copyData(ModifierData *md, ModifierData *ta tpimd->ob = pimd->ob; tpimd->psys = pimd->psys; tpimd->flag = pimd->flag; + tpimd->position = pimd->position; + tpimd->random_position = pimd->random_position; } static int particleInstanceModifier_dependsOnTime(ModifierData *md) @@ -6471,8 +6498,9 @@ static DerivedMesh * particleInstanceModifier_applyModifier( MFace *mface, *orig_mface; MVert *mvert, *orig_mvert; int i,totvert, totpart=0, totface, maxvert, maxface, first_particle=0; - short track=ob->trackflag%3, trackneg; + short track=ob->trackflag%3, trackneg, axis = pimd->axis; float max_co=0.0, min_co=0.0, temp_co[3], cross[3]; + float *size=NULL; trackneg=((ob->trackflag>2)?1:0); @@ -6499,6 +6527,25 @@ static DerivedMesh * particleInstanceModifier_applyModifier( if(totpart==0) return derivedData; + if(pimd->flag & eParticleInstanceFlag_UseSize) { + int p; + float *si; + si = size = MEM_callocN(totpart * sizeof(float), "particle size array"); + + if(pimd->flag & eParticleInstanceFlag_Parents) { + for(p=0, pa= psys->particles; p<psys->totpart; p++, pa++, si++) + *si = pa->size; + } + + if(pimd->flag & eParticleInstanceFlag_Children) { + ChildParticle *cpa = psys->child; + + for(p=0; p<psys->totchild; p++, cpa++, si++) { + *si = psys_get_child_size(psys, cpa, 0.0f, NULL); + } + } + } + pars=psys->particles; totvert=dm->getNumVerts(dm); @@ -6509,7 +6556,7 @@ static DerivedMesh * particleInstanceModifier_applyModifier( psys->lattice=psys_get_lattice(md->scene, ob, psys); - if(psys->flag & (PSYS_HAIR_DONE|PSYS_KEYED)){ + if(psys->flag & (PSYS_HAIR_DONE|PSYS_KEYED) || psys->pointcache->flag & PTCACHE_BAKED){ float min_r[3], max_r[3]; INIT_MINMAX(min_r, max_r); @@ -6534,40 +6581,59 @@ static DerivedMesh * particleInstanceModifier_applyModifier( /*change orientation based on object trackflag*/ VECCOPY(temp_co,mv->co); - mv->co[0]=temp_co[track]; - mv->co[1]=temp_co[(track+1)%3]; - mv->co[2]=temp_co[(track+2)%3]; - - if(psys->flag & (PSYS_HAIR_DONE|PSYS_KEYED) && pimd->flag & eParticleInstanceFlag_Path){ - state.time=(mv->co[0]-min_co)/(max_co-min_co); - if(trackneg) - state.time=1.0f-state.time; - psys_get_particle_on_path(md->scene, pimd->ob, psys,first_particle + i/totvert, &state,1); + mv->co[axis]=temp_co[track]; + mv->co[(axis+1)%3]=temp_co[(track+1)%3]; + mv->co[(axis+2)%3]=temp_co[(track+2)%3]; + + if((psys->flag & (PSYS_HAIR_DONE|PSYS_KEYED) || psys->pointcache->flag & PTCACHE_BAKED) && pimd->flag & eParticleInstanceFlag_Path){ + float ran = 0.0f; + if(pimd->random_position != 0.0f) { + /* just use some static collection of random numbers */ + /* TODO: use something else that's unique to each instanced object */ + pa = psys->particles + (i/totvert)%totpart; + ran = pimd->random_position * 0.5 * (1.0f + pa->r_ave[0]); + } - mv->co[0] = 0.0; + if(pimd->flag & eParticleInstanceFlag_KeepShape) { + state.time = pimd->position * (1.0f - ran); + } + else { + state.time=(mv->co[axis]-min_co)/(max_co-min_co) * pimd->position * (1.0f - ran); + + if(trackneg) + state.time=1.0f-state.time; + + mv->co[axis] = 0.0; + } + + psys_get_particle_on_path(md->scene, pimd->ob, psys,first_particle + i/totvert, &state,1); Normalize(state.vel); - if(state.vel[0] < -0.9999 || state.vel[0] > 0.9999) { - state.rot[0] = 1.0; + /* TODO: incremental rotations somehow */ + if(state.vel[axis] < -0.9999 || state.vel[axis] > 0.9999) { + state.rot[0] = 1; 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]; + float temp[3] = {0.0f,0.0f,0.0f}; + temp[axis] = 1.0f; - /* 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); + Crossf(cross, temp, state.vel); + + /* state.vel[axis] is the only component surviving from a dot product with the axis */ + VecRotToQuat(cross,saacos(state.vel[axis]),state.rot); } + } else{ state.time=-1.0; - psys_get_particle_state(md->scene, pimd->ob, psys, i/totvert, &state,1); + psys_get_particle_state(md->scene, pimd->ob, psys, first_particle + i/totvert, &state,1); } QuatMulVecf(state.rot,mv->co); + if(pimd->flag & eParticleInstanceFlag_UseSize) + VecMulf(mv->co, size[i/totvert]); VECADD(mv->co,mv->co,state.co); } @@ -6621,6 +6687,9 @@ static DerivedMesh * particleInstanceModifier_applyModifier( } CDDM_tessfaces_to_faces(result); + if(size) + MEM_freeN(size); + return result; } static DerivedMesh *particleInstanceModifier_applyModifierEM( @@ -6658,7 +6727,7 @@ static int explodeModifier_dependsOnTime(ModifierData *md) { return 1; } -CustomDataMask explodeModifier_requiredDataMask(ModifierData *md) +CustomDataMask explodeModifier_requiredDataMask(Object *ob, ModifierData *md) { ExplodeModifierData *emd= (ExplodeModifierData*) md; CustomDataMask dataMask = 0; @@ -7260,10 +7329,10 @@ static DerivedMesh * explodeModifier_explodeMesh(ExplodeModifierData *emd, timestep= psys_get_timestep(part); - if(part->flag & PART_GLOB_TIME) + //if(part->flag & PART_GLOB_TIME) cfra=bsystem_time(scene, 0,(float)scene->r.cfra,0.0); - else - cfra=bsystem_time(scene, ob,(float)scene->r.cfra,0.0); + //else + // cfra=bsystem_time(scene, ob,(float)scene->r.cfra,0.0); /* hash table for vertice <-> particle relations */ vertpahash= BLI_edgehash_new(); @@ -7574,7 +7643,7 @@ static void meshdeformModifier_copyData(ModifierData *md, ModifierData *target) tmmd->object = mmd->object; } -CustomDataMask meshdeformModifier_requiredDataMask(ModifierData *md) +CustomDataMask meshdeformModifier_requiredDataMask(Object *ob, ModifierData *md) { MeshDeformModifierData *mmd = (MeshDeformModifierData *)md; CustomDataMask dataMask = 0; @@ -7837,10 +7906,12 @@ static void meshdeformModifier_deformVerts( { DerivedMesh *dm; - if(!derivedData && ob->type==OB_MESH) - dm= CDDM_from_mesh(ob->data, ob); - else - dm= derivedData; + if (!derivedData) { + dm= get_original_dm(md->scene, ob, NULL, 0); + if (dm == NULL) return; + } + else dm= derivedData; + modifier_vgroup_cache(md, vertexCos); /* if next modifier needs original vertices */ @@ -7952,7 +8023,7 @@ static void shrinkwrapModifier_copyData(ModifierData *md, ModifierData *target) tsmd->subsurfLevels = smd->subsurfLevels; } -CustomDataMask shrinkwrapModifier_requiredDataMask(ModifierData *md) +CustomDataMask shrinkwrapModifier_requiredDataMask(Object *ob, ModifierData *md) { ShrinkwrapModifierData *smd = (ShrinkwrapModifierData *)md; CustomDataMask dataMask = 0; @@ -7986,7 +8057,7 @@ static void shrinkwrapModifier_foreachObjectLink(ModifierData *md, Object *ob, O static void shrinkwrapModifier_deformVerts(ModifierData *md, Object *ob, DerivedMesh *derivedData, float (*vertexCos)[3], int numVerts, int useRenderParams, int isFinalCalc) { DerivedMesh *dm = NULL; - CustomDataMask dataMask = shrinkwrapModifier_requiredDataMask(md); + CustomDataMask dataMask = shrinkwrapModifier_requiredDataMask(ob, 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) @@ -8012,7 +8083,7 @@ static void shrinkwrapModifier_deformVerts(ModifierData *md, Object *ob, Derived static void shrinkwrapModifier_deformVertsEM(ModifierData *md, Object *ob, BMEditMesh *editData, DerivedMesh *derivedData, float (*vertexCos)[3], int numVerts) { DerivedMesh *dm = NULL; - CustomDataMask dataMask = shrinkwrapModifier_requiredDataMask(md); + CustomDataMask dataMask = shrinkwrapModifier_requiredDataMask(ob, md); if(dataMask) { @@ -8071,7 +8142,7 @@ static void simpledeformModifier_copyData(ModifierData *md, ModifierData *target memcpy(tsmd->limit, smd->limit, sizeof(tsmd->limit)); } -static CustomDataMask simpledeformModifier_requiredDataMask(ModifierData *md) +static CustomDataMask simpledeformModifier_requiredDataMask(Object *ob, ModifierData *md) { SimpleDeformModifierData *smd = (SimpleDeformModifierData *)md; CustomDataMask dataMask = 0; @@ -8100,7 +8171,7 @@ static void simpledeformModifier_updateDepgraph(ModifierData *md, DagForest *for static void simpledeformModifier_deformVerts(ModifierData *md, Object *ob, DerivedMesh *derivedData, float (*vertexCos)[3], int numVerts, int useRenderParams, int isFinalCalc) { DerivedMesh *dm = NULL; - CustomDataMask dataMask = simpledeformModifier_requiredDataMask(md); + CustomDataMask dataMask = simpledeformModifier_requiredDataMask(ob, 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) @@ -8127,7 +8198,7 @@ static void simpledeformModifier_deformVerts(ModifierData *md, Object *ob, Deriv static void simpledeformModifier_deformVertsEM(ModifierData *md, Object *ob, BMEditMesh *editData, DerivedMesh *derivedData, float (*vertexCos)[3], int numVerts) { DerivedMesh *dm = NULL; - CustomDataMask dataMask = simpledeformModifier_requiredDataMask(md); + CustomDataMask dataMask = simpledeformModifier_requiredDataMask(ob, 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) @@ -8389,7 +8460,8 @@ ModifierTypeInfo *modifierType_getInfo(ModifierType type) mti = INIT_TYPE(Softbody); mti->type = eModifierTypeType_OnlyDeform; mti->flags = eModifierTypeFlag_AcceptsCVs - | eModifierTypeFlag_RequiresOriginalData; + | eModifierTypeFlag_RequiresOriginalData + | eModifierTypeFlag_Single; mti->deformVerts = softbodyModifier_deformVerts; mti->dependsOnTime = softbodyModifier_dependsOnTime; @@ -8397,7 +8469,8 @@ ModifierTypeInfo *modifierType_getInfo(ModifierType type) mti->type = eModifierTypeType_Nonconstructive; mti->initData = clothModifier_initData; mti->flags = eModifierTypeFlag_AcceptsMesh - | eModifierTypeFlag_UsesPointCache; + | eModifierTypeFlag_UsesPointCache + | eModifierTypeFlag_Single; mti->dependsOnTime = clothModifier_dependsOnTime; mti->freeData = clothModifier_freeData; mti->requiredDataMask = clothModifier_requiredDataMask; @@ -8408,7 +8481,8 @@ ModifierTypeInfo *modifierType_getInfo(ModifierType type) mti = INIT_TYPE(Collision); mti->type = eModifierTypeType_OnlyDeform; mti->initData = collisionModifier_initData; - mti->flags = eModifierTypeFlag_AcceptsMesh; + mti->flags = eModifierTypeFlag_AcceptsMesh + | eModifierTypeFlag_Single; mti->dependsOnTime = collisionModifier_dependsOnTime; mti->freeData = collisionModifier_freeData; mti->deformVerts = collisionModifier_deformVerts; @@ -8491,7 +8565,8 @@ ModifierTypeInfo *modifierType_getInfo(ModifierType type) mti = INIT_TYPE(Fluidsim); mti->type = eModifierTypeType_Nonconstructive - | eModifierTypeFlag_RequiresOriginalData; + | eModifierTypeFlag_RequiresOriginalData + | eModifierTypeFlag_Single; mti->flags = eModifierTypeFlag_AcceptsMesh; mti->initData = fluidsimModifier_initData; mti->freeData = fluidsimModifier_freeData; @@ -8739,7 +8814,20 @@ int modifiers_isParticleEnabled(Object *ob) return (md && md->mode & (eModifierMode_Realtime | eModifierMode_Render)); } -LinkNode *modifiers_calcDataMasks(ModifierData *md, CustomDataMask dataMask) +int modifier_isEnabled(ModifierData *md, int required_mode) +{ + ModifierTypeInfo *mti = modifierType_getInfo(md->type); + + if((md->mode & required_mode) != required_mode) return 0; + if(mti->isDisabled && mti->isDisabled(md)) return 0; + if(md->mode & eModifierMode_DisableTemporary) return 0; + if(required_mode & eModifierMode_Editmode) + if(!(mti->flags & eModifierTypeFlag_SupportsEditmode)) return 0; + + return 1; +} + +LinkNode *modifiers_calcDataMasks(Object *ob, ModifierData *md, CustomDataMask dataMask, int required_mode) { LinkNode *dataMasks = NULL; LinkNode *curr, *prev; @@ -8749,7 +8837,9 @@ LinkNode *modifiers_calcDataMasks(ModifierData *md, CustomDataMask dataMask) ModifierTypeInfo *mti = modifierType_getInfo(md->type); CustomDataMask mask = 0; - if(mti->requiredDataMask) mask = mti->requiredDataMask(md); + if(modifier_isEnabled(md, required_mode)) + if(mti->requiredDataMask) + mask = mti->requiredDataMask(ob, md); BLI_linklist_prepend(&dataMasks, SET_INT_IN_POINTER(mask)); } diff --git a/source/blender/blenkernel/intern/multires.c b/source/blender/blenkernel/intern/multires.c index 2a2b7e53722..03838e3c258 100644 --- a/source/blender/blenkernel/intern/multires.c +++ b/source/blender/blenkernel/intern/multires.c @@ -1270,8 +1270,10 @@ void multires_free(Multires *mr) if(lvl) { CustomData_free(&mr->vdata, lvl->totvert); CustomData_free(&mr->fdata, lvl->totface); - MEM_freeN(mr->edge_flags); - MEM_freeN(mr->edge_creases); + if(mr->edge_flags) + MEM_freeN(mr->edge_flags); + if(mr->edge_creases) + MEM_freeN(mr->edge_creases); } while(lvl) { diff --git a/source/blender/blenkernel/intern/nla.c b/source/blender/blenkernel/intern/nla.c index dc2bf26759f..457df9be7a9 100644 --- a/source/blender/blenkernel/intern/nla.c +++ b/source/blender/blenkernel/intern/nla.c @@ -17,171 +17,1433 @@ * 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) 2001-2002 by NaN Holding BV. + * The Original Code is Copyright (C) 2009 Blender Foundation, Joshua Leung * All rights reserved. * * The Original Code is: all of this file. * - * Contributor(s): none yet. + * Contributor(s): Joshua Leung (full recode) * * ***** END GPL LICENSE BLOCK ***** */ #include <stdlib.h> +#include <stddef.h> +#include <stdio.h> +#include <string.h> +#include <math.h> +#include <float.h> #include "MEM_guardedalloc.h" #include "BLI_blenlib.h" +#include "BLI_ghash.h" -#include "DNA_space_types.h" -#include "DNA_nla_types.h" +#include "DNA_anim_types.h" #include "DNA_action_types.h" -#include "DNA_ID.h" -#include "DNA_ipo_types.h" -#include "DNA_object_types.h" -#include "BKE_nla.h" +#include "BKE_animsys.h" #include "BKE_action.h" +#include "BKE_fcurve.h" +#include "BKE_nla.h" #include "BKE_blender.h" #include "BKE_library.h" -#include "BKE_object.h" /* for convert_action_to_strip(ob) */ +#include "BKE_object.h" +#include "BKE_utildefines.h" + +#include "RNA_access.h" +#include "nla_private.h" #ifdef HAVE_CONFIG_H #include <config.h> #endif -/* NOTE: in group.c the strips get copied for group-nla override, this assumes - that strips are one single block, without additional data to be copied */ -void copy_actionstrip (bActionStrip **dst, bActionStrip **src){ - bActionStrip *dstrip; - bActionStrip *sstrip = *src; +/* *************************************************** */ +/* Data Management */ + +/* Freeing ------------------------------------------- */ + +/* Remove the given NLA strip from the NLA track it occupies, free the strip's data, + * and the strip itself. + */ +void free_nlastrip (ListBase *strips, NlaStrip *strip) +{ + NlaStrip *cs, *csn; + + /* sanity checks */ + if (strip == NULL) + return; + + /* free child-strips */ + for (cs= strip->strips.first; cs; cs= csn) { + csn= cs->next; + free_nlastrip(&strip->strips, cs); + } + + /* remove reference to action */ + if (strip->act) + strip->act->id.us--; + + /* free remapping info */ + //if (strip->remap) + // BKE_animremap_free(); + + /* free own F-Curves */ + free_fcurves(&strip->fcurves); + + /* free own F-Modifiers */ + free_fmodifiers(&strip->modifiers); + + /* free the strip itself */ + if (strips) + BLI_freelinkN(strips, strip); + else + MEM_freeN(strip); +} - if (!*src){ - *dst=NULL; +/* Remove the given NLA track from the set of NLA tracks, free the track's data, + * and the track itself. + */ +void free_nlatrack (ListBase *tracks, NlaTrack *nlt) +{ + NlaStrip *strip, *stripn; + + /* sanity checks */ + if (nlt == NULL) return; + + /* free strips */ + for (strip= nlt->strips.first; strip; strip= stripn) { + stripn= strip->next; + free_nlastrip(&nlt->strips, strip); } + + /* free NLA track itself now */ + if (tracks) + BLI_freelinkN(tracks, nlt); + else + MEM_freeN(nlt); +} - *dst = MEM_dupallocN(sstrip); +/* Free the elements of type NLA Tracks provided in the given list, but do not free + * the list itself since that is not free-standing + */ +void free_nladata (ListBase *tracks) +{ + NlaTrack *nlt, *nltn; + + /* sanity checks */ + if ELEM(NULL, tracks, tracks->first) + return; + + /* free tracks one by one */ + for (nlt= tracks->first; nlt; nlt= nltn) { + nltn= nlt->next; + free_nlatrack(tracks, nlt); + } + + /* clear the list's pointers to be safe */ + tracks->first= tracks->last= NULL; +} - dstrip = *dst; - if (dstrip->act) - dstrip->act->id.us++; +/* Copying ------------------------------------------- */ - if (dstrip->ipo) - dstrip->ipo->id.us++; +/* Copy NLA strip */ +NlaStrip *copy_nlastrip (NlaStrip *strip) +{ + NlaStrip *strip_d; + NlaStrip *cs, *cs_d; - if (dstrip->modifiers.first) { - BLI_duplicatelist (&dstrip->modifiers, &sstrip->modifiers); + /* sanity check */ + if (strip == NULL) + return NULL; + + /* make a copy */ + strip_d= MEM_dupallocN(strip); + strip_d->next= strip_d->prev= NULL; + + /* increase user-count of action */ + if (strip_d->act) + strip_d->act->id.us++; + + /* copy F-Curves and modifiers */ + copy_fcurves(&strip_d->fcurves, &strip->fcurves); + copy_fmodifiers(&strip_d->modifiers, &strip->modifiers); + + /* make a copy of all the child-strips, one at a time */ + strip_d->strips.first= strip_d->strips.last= NULL; + + for (cs= strip->strips.first; cs; cs= cs->next) { + cs_d= copy_nlastrip(cs); + BLI_addtail(&strip_d->strips, cs_d); } + /* return the strip */ + return strip_d; } -void copy_nlastrips (ListBase *dst, ListBase *src) +/* Copy NLA Track */ +NlaTrack *copy_nlatrack (NlaTrack *nlt) { - bActionStrip *strip; + NlaStrip *strip, *strip_d; + NlaTrack *nlt_d; + + /* sanity check */ + if (nlt == NULL) + return NULL; + + /* make a copy */ + nlt_d= MEM_dupallocN(nlt); + nlt_d->next= nlt_d->prev= NULL; + + /* make a copy of all the strips, one at a time */ + nlt_d->strips.first= nlt_d->strips.last= NULL; + + for (strip= nlt->strips.first; strip; strip= strip->next) { + strip_d= copy_nlastrip(strip); + BLI_addtail(&nlt_d->strips, strip_d); + } + + /* return the copy */ + return nlt_d; +} - dst->first=dst->last=NULL; +/* Copy all NLA data */ +void copy_nladata (ListBase *dst, ListBase *src) +{ + NlaTrack *nlt, *nlt_d; + + /* sanity checks */ + if ELEM(NULL, dst, src) + return; + + /* copy each NLA-track, one at a time */ + for (nlt= src->first; nlt; nlt= nlt->next) { + /* make a copy, and add the copy to the destination list */ + nlt_d= copy_nlatrack(nlt); + BLI_addtail(dst, nlt_d); + } +} - BLI_duplicatelist (dst, src); +/* Adding ------------------------------------------- */ - /* Update specific data */ - if (!dst->first) - return; +/* Add a NLA Track to the given AnimData + * - prev: NLA-Track to add the new one after + */ +NlaTrack *add_nlatrack (AnimData *adt, NlaTrack *prev) +{ + NlaTrack *nlt; + + /* sanity checks */ + if (adt == NULL) + return NULL; + + /* allocate new track */ + nlt= MEM_callocN(sizeof(NlaTrack), "NlaTrack"); + + /* set settings requiring the track to not be part of the stack yet */ + nlt->flag = NLATRACK_SELECTED; + nlt->index= BLI_countlist(&adt->nla_tracks); + + /* add track to stack, and make it the active one */ + if (prev) + BLI_insertlinkafter(&adt->nla_tracks, prev, nlt); + else + BLI_addtail(&adt->nla_tracks, nlt); + BKE_nlatrack_set_active(&adt->nla_tracks, nlt); + + /* must have unique name, but we need to seed this */ + sprintf(nlt->name, "NlaTrack"); + BLI_uniquename(&adt->nla_tracks, nlt, "NlaTrack", '.', offsetof(NlaTrack, name), 64); + + /* return the new track */ + return nlt; +} + +/* Add a NLA Strip referencing the given Action */ +NlaStrip *add_nlastrip (bAction *act) +{ + NlaStrip *strip; + + /* sanity checks */ + if (act == NULL) + return NULL; + + /* allocate new strip */ + strip= MEM_callocN(sizeof(NlaStrip), "NlaStrip"); + + /* generic settings + * - selected flag to highlight this to the user + * - auto-blends to ensure that blend in/out values are automatically + * determined by overlaps of strips + * - (XXX) synchronisation of strip-length in accordance with changes to action-length + * is not done though, since this should only really happens in editmode for strips now + * though this decision is still subject to further review... + */ + strip->flag = NLASTRIP_FLAG_SELECT|NLASTRIP_FLAG_AUTO_BLENDS; + + /* assign the action reference */ + strip->act= act; + id_us_plus(&act->id); + + /* determine initial range + * - strip length cannot be 0... ever... + */ + calc_action_range(strip->act, &strip->actstart, &strip->actend, 1); + + strip->start = strip->actstart; + strip->end = (IS_EQ(strip->actstart, strip->actend)) ? (strip->actstart + 1.0f): (strip->actend); + + /* strip should be referenced as-is */ + strip->scale= 1.0f; + strip->repeat = 1.0f; + + /* return the new strip */ + return strip; +} - for (strip = dst->first; strip; strip=strip->next){ - if (strip->act) - strip->act->id.us++; - if (strip->ipo) - strip->ipo->id.us++; - if (strip->modifiers.first) { - ListBase listb; - BLI_duplicatelist (&listb, &strip->modifiers); - strip->modifiers= listb; +/* Add new NLA-strip to the top of the NLA stack - i.e. into the last track if space, or a new one otherwise */ +NlaStrip *add_nlastrip_to_stack (AnimData *adt, bAction *act) +{ + NlaStrip *strip; + NlaTrack *nlt; + + /* sanity checks */ + if ELEM(NULL, adt, act) + return NULL; + + /* create a new NLA strip */ + strip= add_nlastrip(act); + if (strip == NULL) + return NULL; + + /* firstly try adding strip to last track, but if that fails, add to a new track */ + if (BKE_nlatrack_add_strip(adt->nla_tracks.last, strip) == 0) { + /* trying to add to the last track failed (no track or no space), + * so add a new track to the stack, and add to that... + */ + nlt= add_nlatrack(adt, NULL); + BKE_nlatrack_add_strip(nlt, strip); + } + + /* automatically name it too */ + BKE_nlastrip_validate_name(adt, strip); + + /* returns the strip added */ + return strip; +} + +/* *************************************************** */ +/* NLA Evaluation <-> Editing Stuff */ + +/* Strip Mapping ------------------------------------- */ + +/* non clipped mapping for strip-time <-> global time (for Action-Clips) + * invert = convert action-strip time to global time + */ +static float nlastrip_get_frame_actionclip (NlaStrip *strip, float cframe, short mode) +{ + float actlength, repeat, scale; + + /* get number of repeats */ + if (IS_EQ(strip->repeat, 0.0f)) strip->repeat = 1.0f; + repeat = strip->repeat; + + /* scaling */ + if (IS_EQ(strip->scale, 0.0f)) strip->scale= 1.0f; + scale = (float)fabs(strip->scale); /* scale must be positive - we've got a special flag for reversing */ + + /* length of referenced action */ + actlength = strip->actend - strip->actstart; + if (IS_EQ(actlength, 0.0f)) actlength = 1.0f; + + /* reversed = play strip backwards */ + if (strip->flag & NLASTRIP_FLAG_REVERSE) { + // FIXME: this won't work right with Graph Editor? + if (mode == NLATIME_CONVERT_MAP) { + return strip->end - scale*(cframe - strip->actstart); + } + else if (mode == NLATIME_CONVERT_UNMAP) { + int repeatsNum = (int)((cframe - strip->start) / (actlength * scale)); + + /* this method doesn't clip the values to lie within the action range only + * - the '(repeatsNum * actlength * scale)' compensates for the fmod(...) + * - the fmod(...) works in the same way as for eval + */ + return strip->actend - (repeatsNum * actlength * scale) + - (fmod(cframe - strip->start, actlength*scale) / scale); + } + else { + if (IS_EQ(cframe, strip->end) && IS_EQ(strip->repeat, ((int)strip->repeat))) { + /* this case prevents the motion snapping back to the first frame at the end of the strip + * by catching the case where repeats is a whole number, which means that the end of the strip + * could also be interpreted as the end of the start of a repeat + */ + return strip->actstart; + } + else { + /* - the 'fmod(..., actlength*scale)' is needed to get the repeats working + * - the '/ scale' is needed to ensure that scaling influences the timing within the repeat + */ + return strip->actend - fmod(cframe - strip->start, actlength*scale) / scale; + } + } + } + else { + if (mode == NLATIME_CONVERT_MAP) { + return strip->start + scale*(cframe - strip->actstart); + } + else if (mode == NLATIME_CONVERT_UNMAP) { + int repeatsNum = (int)((cframe - strip->start) / (actlength * scale)); + + /* this method doesn't clip the values to lie within the action range only + * - the '(repeatsNum * actlength * scale)' compensates for the fmod(...) + * - the fmod(...) works in the same way as for eval + */ + return strip->actstart + (repeatsNum * actlength * scale) + + (fmod(cframe - strip->start, actlength*scale) / scale); + } + else /* if (mode == NLATIME_CONVERT_EVAL) */{ + if (IS_EQ(cframe, strip->end) && IS_EQ(strip->repeat, ((int)strip->repeat))) { + /* this case prevents the motion snapping back to the first frame at the end of the strip + * by catching the case where repeats is a whole number, which means that the end of the strip + * could also be interpreted as the end of the start of a repeat + */ + return strip->actend; + } + else { + /* - the 'fmod(..., actlength*scale)' is needed to get the repeats working + * - the '/ scale' is needed to ensure that scaling influences the timing within the repeat + */ + return strip->actstart + fmod(cframe - strip->start, actlength*scale) / scale; + } } } } -/* from editnla, for convert_action_to_strip -- no UI code so should be ok here.. */ -void find_stridechannel(Object *ob, bActionStrip *strip) +/* non clipped mapping for strip-time <-> global time (for Transitions) + * invert = convert action-strip time to global time + */ +static float nlastrip_get_frame_transition (NlaStrip *strip, float cframe, short mode) { - if(ob && ob->pose) { - bPoseChannel *pchan; - for(pchan= ob->pose->chanbase.first; pchan; pchan= pchan->next) - if(pchan->flag & POSE_STRIDE) - break; - if(pchan) - BLI_strncpy(strip->stridechannel, pchan->name, 32); + float length; + + /* length of strip */ + length= strip->end - strip->start; + + /* reversed = play strip backwards */ + if (strip->flag & NLASTRIP_FLAG_REVERSE) { + if (mode == NLATIME_CONVERT_MAP) + return strip->end - (length * cframe); else - strip->stridechannel[0]= 0; + return (strip->end - cframe) / length; + } + else { + if (mode == NLATIME_CONVERT_MAP) + return (length * cframe) + strip->start; + else + return (cframe - strip->start) / length; } } -//called by convert_nla / bpy api with an object with the action to be converted to a new strip -bActionStrip *convert_action_to_strip (Object *ob) +/* non clipped mapping for strip-time <-> global time + * mode = eNlaTime_ConvertModes[] -> NLATIME_CONVERT_* + * + * only secure for 'internal' (i.e. within AnimSys evaluation) operations, + * but should not be directly relied on for stuff which interacts with editors + */ +float nlastrip_get_frame (NlaStrip *strip, float cframe, short mode) { - bActionStrip *nstrip; + switch (strip->type) { + case NLASTRIP_TYPE_META: /* meta - for now, does the same as transition (is really just an empty container) */ + case NLASTRIP_TYPE_TRANSITION: /* transition */ + return nlastrip_get_frame_transition(strip, cframe, mode); + + case NLASTRIP_TYPE_CLIP: /* action-clip (default) */ + default: + return nlastrip_get_frame_actionclip(strip, cframe, mode); + } +} - /* Make new actionstrip */ - nstrip = MEM_callocN(sizeof(bActionStrip), "bActionStrip"); - - /* Link the action to the nstrip */ - nstrip->act = ob->action; - id_us_plus(&nstrip->act->id); - calc_action_range(nstrip->act, &nstrip->actstart, &nstrip->actend, 1); - nstrip->start = nstrip->actstart; - nstrip->end = nstrip->actend; - nstrip->flag = ACTSTRIP_SELECT|ACTSTRIP_LOCK_ACTION; + +/* Non clipped mapping for strip-time <-> global time + * mode = eNlaTime_ConvertModesp[] -> NLATIME_CONVERT_* + * + * Public API method - perform this mapping using the given AnimData block + * and perform any necessary sanity checks on the value + */ +float BKE_nla_tweakedit_remap (AnimData *adt, float cframe, short mode) +{ + NlaStrip *strip; + + /* sanity checks + * - obviously we've got to have some starting data + * - when not in tweakmode, the active Action does not have any scaling applied :) + * - when in tweakmode, if the no-mapping flag is set, do not map + */ + if ((adt == NULL) || (adt->flag & ADT_NLA_EDIT_ON)==0 || (adt->flag & ADT_NLA_EDIT_NOMAP)) + return cframe; + + /* if the active-strip info has been stored already, access this, otherwise look this up + * and store for (very probable) future usage + */ + if (adt->actstrip == NULL) { + NlaTrack *nlt= BKE_nlatrack_find_active(&adt->nla_tracks); + adt->actstrip= BKE_nlastrip_find_active(nlt); + } + strip= adt->actstrip; + + /* sanity checks + * - in rare cases, we may not be able to find this strip for some reason (internal error) + * - for now, if the user has defined a curve to control the time, this correction cannot be performed + * reliably... + */ + if ((strip == NULL) || (strip->flag & NLASTRIP_FLAG_USR_TIME)) + return cframe; + + /* perform the correction now... */ + return nlastrip_get_frame(strip, cframe, mode); +} + +/* *************************************************** */ +/* NLA API */ + +/* List of Strips ------------------------------------ */ +/* (these functions are used for NLA-Tracks and also for nested/meta-strips) */ + +/* Check if there is any space in the given list to add the given strip */ +short BKE_nlastrips_has_space (ListBase *strips, float start, float end) +{ + NlaStrip *strip; + + /* sanity checks */ + if ((strips == NULL) || IS_EQ(start, end)) + return 0; + if (start > end) { + puts("BKE_nlastrips_has_space() error... start and end arguments swapped"); + SWAP(float, start, end); + } + + /* loop over NLA strips checking for any overlaps with this area... */ + for (strip= strips->first; strip; strip= strip->next) { + /* if start frame of strip is past the target end-frame, that means that + * we've gone past the window we need to check for, so things are fine + */ + if (strip->start > end) + return 1; + + /* if the end of the strip is greater than either of the boundaries, the range + * must fall within the extents of the strip + */ + if ((strip->end > start) || (strip->end > end)) + return 0; + } + + /* if we are still here, we haven't encountered any overlapping strips */ + return 1; +} + +/* Rearrange the strips in the track so that they are always in order + * (usually only needed after a strip has been moved) + */ +void BKE_nlastrips_sort_strips (ListBase *strips) +{ + ListBase tmp = {NULL, NULL}; + NlaStrip *strip, *sstrip, *stripn; + + /* sanity checks */ + if ELEM(NULL, strips, strips->first) + return; + + /* we simply perform insertion sort on this list, since it is assumed that per track, + * there are only likely to be at most 5-10 strips + */ + for (strip= strips->first; strip; strip= stripn) { + short not_added = 1; + + stripn= strip->next; + + /* remove this strip from the list, and add it to the new list, searching from the end of + * the list, assuming that the lists are in order + */ + BLI_remlink(strips, strip); + + for (sstrip= tmp.last; sstrip; sstrip= sstrip->prev) { + /* check if add after */ + if (sstrip->end <= strip->start) { + BLI_insertlinkafter(&tmp, sstrip, strip); + not_added= 0; + break; + } + } + + /* add before first? */ + if (not_added) + BLI_addhead(&tmp, strip); + } + + /* reassign the start and end points of the strips */ + strips->first= tmp.first; + strips->last= tmp.last; +} + +/* Add the given NLA-Strip to the given list of strips, assuming that it + * isn't currently a member of another list + */ +short BKE_nlastrips_add_strip (ListBase *strips, NlaStrip *strip) +{ + NlaStrip *ns; + short not_added = 1; + + /* sanity checks */ + if ELEM(NULL, strips, strip) + return 0; + + /* check if any space to add */ + if (BKE_nlastrips_has_space(strips, strip->start, strip->end)==0) + return 0; + + /* find the right place to add the strip to the nominated track */ + for (ns= strips->first; ns; ns= ns->next) { + /* if current strip occurs after the new strip, add it before */ + if (ns->start > strip->end) { + BLI_insertlinkbefore(strips, ns, strip); + not_added= 0; + break; + } + } + if (not_added) { + /* just add to the end of the list of the strips then... */ + BLI_addtail(strips, strip); + } + + /* added... */ + return 1; +} + + +/* Meta-Strips ------------------------------------ */ + +/* Convert 'islands' (i.e. continuous string of) selected strips to be + * contained within 'Meta-Strips' which act as strips which contain strips. + * temp: are the meta-strips to be created 'temporary' ones used for transforms? + */ +void BKE_nlastrips_make_metas (ListBase *strips, short temp) +{ + NlaStrip *mstrip = NULL; + NlaStrip *strip, *stripn; + + /* sanity checks */ + if ELEM(NULL, strips, strips->first) + return; + + /* group all continuous chains of selected strips into meta-strips */ + for (strip= strips->first; strip; strip= stripn) { + stripn= strip->next; + + if (strip->flag & NLASTRIP_FLAG_SELECT) { + /* if there is an existing meta-strip, add this strip to it, otherwise, create a new one */ + if (mstrip == NULL) { + /* add a new meta-strip, and add it before the current strip that it will replace... */ + mstrip= MEM_callocN(sizeof(NlaStrip), "Meta-NlaStrip"); + mstrip->type = NLASTRIP_TYPE_META; + BLI_insertlinkbefore(strips, strip, mstrip); + + /* set flags */ + mstrip->flag = NLASTRIP_FLAG_SELECT; + + /* set temp flag if appropriate (i.e. for transform-type editing) */ + if (temp) + mstrip->flag |= NLASTRIP_FLAG_TEMP_META; + + /* set default repeat/scale values to prevent warnings */ + mstrip->repeat= mstrip->scale= 1.0f; + + /* make its start frame be set to the start frame of the current strip */ + mstrip->start= strip->start; + } - find_stridechannel(ob, nstrip); - //set_active_strip(ob, nstrip); /* is in editnla as does UI calls */ + /* remove the selected strips from the track, and add to the meta */ + BLI_remlink(strips, strip); + BLI_addtail(&mstrip->strips, strip); - nstrip->repeat = 1.0; + /* expand the meta's dimensions to include the newly added strip- i.e. its last frame */ + mstrip->end= strip->end; + } + else { + /* current strip wasn't selected, so the end of 'island' of selected strips has been reached, + * so stop adding strips to the current meta + */ + mstrip= NULL; + } + } +} - if(ob->nlastrips.first == NULL) - ob->nlaflag |= OB_NLA_OVERRIDE; +/* Split a meta-strip into a set of normal strips */ +void BKE_nlastrips_clear_metastrip (ListBase *strips, NlaStrip *strip) +{ + NlaStrip *cs, *csn; - BLI_addtail(&ob->nlastrips, nstrip); - return nstrip; /* is created, malloced etc. here so is safe to just return the pointer? - this is needed for setting this active in UI, and probably useful for API too */ + /* sanity check */ + if ELEM(NULL, strips, strip) + return; + /* move each one of the meta-strip's children before the meta-strip + * in the list of strips after unlinking them from the meta-strip + */ + for (cs= strip->strips.first; cs; cs= csn) { + csn= cs->next; + BLI_remlink(&strip->strips, cs); + BLI_insertlinkbefore(strips, strip, cs); + } + + /* free the meta-strip now */ + BLI_freelinkN(strips, strip); } +/* Remove meta-strips (i.e. flatten the list of strips) from the top-level of the list of strips + * sel: only consider selected meta-strips, otherwise all meta-strips are removed + * onlyTemp: only remove the 'temporary' meta-strips used for transforms + */ +void BKE_nlastrips_clear_metas (ListBase *strips, short onlySel, short onlyTemp) +{ + NlaStrip *strip, *stripn; + + /* sanity checks */ + if ELEM(NULL, strips, strips->first) + return; + + /* remove meta-strips fitting the criteria of the arguments */ + for (strip= strips->first; strip; strip= stripn) { + stripn= strip->next; + + /* check if strip is a meta-strip */ + if (strip->type == NLASTRIP_TYPE_META) { + /* if check if selection and 'temporary-only' considerations are met */ + if ((onlySel==0) || (strip->flag & NLASTRIP_FLAG_SELECT)) { + if ((!onlyTemp) || (strip->flag & NLASTRIP_FLAG_TEMP_META)) { + BKE_nlastrips_clear_metastrip(strips, strip); + } + } + } + } +} -/* not strip itself! */ -void free_actionstrip(bActionStrip* strip) +/* Add the given NLA-Strip to the given Meta-Strip, assuming that the + * strip isn't attached to anyy list of strips + */ +short BKE_nlameta_add_strip (NlaStrip *mstrip, NlaStrip *strip) { - if (!strip) + /* sanity checks */ + if ELEM(NULL, mstrip, strip) + return 0; + + /* firstly, check if the meta-strip has space for this */ + if (BKE_nlastrips_has_space(&mstrip->strips, strip->start, strip->end) == 0) + return 0; + + /* check if this would need to be added to the ends of the meta, + * and subsequently, if the neighbouring strips allow us enough room + */ + if (strip->start < mstrip->start) { + /* check if strip to the left (if it exists) ends before the + * start of the strip we're trying to add + */ + if ((mstrip->prev == NULL) || (mstrip->prev->end <= strip->start)) { + /* add strip to start of meta's list, and expand dimensions */ + BLI_addhead(&mstrip->strips, strip); + mstrip->start= strip->start; + + return 1; + } + else /* failed... no room before */ + return 0; + } + else if (strip->end > mstrip->end) { + /* check if strip to the right (if it exists) starts before the + * end of the strip we're trying to add + */ + if ((mstrip->next == NULL) || (mstrip->next->start >= strip->end)) { + /* add strip to end of meta's list, and expand dimensions */ + BLI_addtail(&mstrip->strips, strip); + mstrip->end= strip->end; + + return 1; + } + else /* failed... no room after */ + return 0; + } + else { + /* just try to add to the meta-strip (no dimension changes needed) */ + return BKE_nlastrips_add_strip(&mstrip->strips, strip); + } +} + +/* Adjust the settings of NLA-Strips contained within a Meta-Strip (recursively), + * until the Meta-Strips children all fit within the Meta-Strip's new dimensions + */ +void BKE_nlameta_flush_transforms (NlaStrip *mstrip) +{ + NlaStrip *strip; + float oStart, oEnd, offset; + float oLen, nLen; + short scaleChanged= 0; + + /* sanity checks + * - strip must exist + * - strip must be a meta-strip with some contents + */ + if ELEM(NULL, mstrip, mstrip->strips.first) + return; + if (mstrip->type != NLASTRIP_TYPE_META) return; + + /* get the original start/end points, and calculate the start-frame offset + * - these are simply the start/end frames of the child strips, + * since we assume they weren't transformed yet + */ + oStart= ((NlaStrip *)mstrip->strips.first)->start; + oEnd= ((NlaStrip *)mstrip->strips.last)->end; + offset= mstrip->start - oStart; + + /* optimisation: + * don't flush if nothing changed yet + * TODO: maybe we need a flag to say always flush? + */ + if (IS_EQ(oStart, mstrip->start) && IS_EQ(oEnd, mstrip->end)) + return; + + /* check if scale changed */ + oLen = oEnd - oStart; + nLen = mstrip->end - mstrip->start; + if (IS_EQ(nLen, oLen) == 0) + scaleChanged= 1; + + /* for each child-strip, calculate new start/end points based on this new info */ + for (strip= mstrip->strips.first; strip; strip= strip->next) { + if (scaleChanged) { + PointerRNA ptr; + float p1, p2, nStart, nEnd; + + /* compute positions of endpoints relative to old extents of strip */ + p1= (strip->start - oStart) / oLen; + p2= (strip->end - oStart) / oLen; + + /* compute the new strip endpoints using the proportions */ + nStart= (p1 * nLen) + mstrip->start; + nEnd= (p2 * nLen) + mstrip->start; + + /* firstly, apply the new positions manually, then apply using RNA + * - first time is to make sure no truncation errors from one endpoint not being + * set yet occur + * - second time is to make sure scale is computed properly... + */ + strip->start= nStart; + strip->end= nEnd; + + RNA_pointer_create(NULL, &RNA_NlaStrip, strip, &ptr); + RNA_float_set(&ptr, "start_frame", nStart); + RNA_float_set(&ptr, "end_frame", nEnd); + } + else { + /* just apply the changes in offset to both ends of the strip */ + strip->start += offset; + strip->end += offset; + } + + /* finally, make sure the strip's children (if it is a meta-itself), get updated */ + BKE_nlameta_flush_transforms(strip); + } +} - if (strip->act){ - strip->act->id.us--; - strip->act = NULL; +/* NLA-Tracks ---------------------------------------- */ + +/* Find the active NLA-track for the given stack */ +NlaTrack *BKE_nlatrack_find_active (ListBase *tracks) +{ + NlaTrack *nlt; + + /* sanity check */ + if ELEM(NULL, tracks, tracks->first) + return NULL; + + /* try to find the first active track */ + for (nlt= tracks->first; nlt; nlt= nlt->next) { + if (nlt->flag & NLATRACK_ACTIVE) + return nlt; } - if (strip->ipo){ - strip->ipo->id.us--; - strip->ipo = NULL; + + /* none found */ + return NULL; +} + +/* Toggle the 'solo' setting for the given NLA-track, making sure that it is the only one + * that has this status in its AnimData block. + */ +void BKE_nlatrack_solo_toggle (AnimData *adt, NlaTrack *nlt) +{ + NlaTrack *nt; + + /* sanity check */ + if ELEM(NULL, adt, adt->nla_tracks.first) + return; + + /* firstly, make sure 'solo' flag for all tracks is disabled */ + for (nt= adt->nla_tracks.first; nt; nt= nt->next) { + if (nt != nlt) + nt->flag &= ~NLATRACK_SOLO; } - if (strip->modifiers.first) { - BLI_freelistN(&strip->modifiers); + + /* now, enable 'solo' for the given track if appropriate */ + if (nlt) { + /* toggle solo status */ + nlt->flag ^= NLATRACK_SOLO; + + /* set or clear solo-status on AnimData */ + if (nlt->flag & NLATRACK_SOLO) + adt->flag |= ADT_NLA_SOLO_TRACK; + else + adt->flag &= ~ADT_NLA_SOLO_TRACK; } + else + adt->flag &= ~ADT_NLA_SOLO_TRACK; +} + +/* Make the given NLA-track the active one for the given stack. If no track is provided, + * this function can be used to simply deactivate all the NLA tracks in the given stack too. + */ +void BKE_nlatrack_set_active (ListBase *tracks, NlaTrack *nlt_a) +{ + NlaTrack *nlt; + /* sanity check */ + if ELEM(NULL, tracks, tracks->first) + return; + + /* deactive all the rest */ + for (nlt= tracks->first; nlt; nlt= nlt->next) + nlt->flag &= ~NLATRACK_ACTIVE; + + /* set the given one as the active one */ + if (nlt_a) + nlt_a->flag |= NLATRACK_ACTIVE; } -void free_nlastrips (ListBase *nlalist) +/* Check if there is any space in the given track to add a strip of the given length */ +short BKE_nlatrack_has_space (NlaTrack *nlt, float start, float end) { - bActionStrip *strip; + /* sanity checks */ + if ((nlt == NULL) || IS_EQ(start, end)) + return 0; + if (start > end) { + puts("BKE_nlatrack_has_space() error... start and end arguments swapped"); + SWAP(float, start, end); + } + + /* check if there's any space left in the track for a strip of the given length */ + return BKE_nlastrips_has_space(&nlt->strips, start, end); +} - if (!nlalist->first) +/* Rearrange the strips in the track so that they are always in order + * (usually only needed after a strip has been moved) + */ +void BKE_nlatrack_sort_strips (NlaTrack *nlt) +{ + /* sanity checks */ + if ELEM(NULL, nlt, nlt->strips.first) return; + + /* sort the strips with a more generic function */ + BKE_nlastrips_sort_strips(&nlt->strips); +} - /* Do any specific freeing */ - for (strip=nlalist->first; strip; strip=strip->next) +/* Add the given NLA-Strip to the given NLA-Track, assuming that it + * isn't currently attached to another one + */ +short BKE_nlatrack_add_strip (NlaTrack *nlt, NlaStrip *strip) +{ + /* sanity checks */ + if ELEM(NULL, nlt, strip) + return 0; + + /* try to add the strip to the track using a more generic function */ + return BKE_nlastrips_add_strip(&nlt->strips, strip); +} + +/* NLA Strips -------------------------------------- */ + +/* Find the active NLA-strip within the given track */ +NlaStrip *BKE_nlastrip_find_active (NlaTrack *nlt) +{ + NlaStrip *strip; + + /* sanity check */ + if ELEM(NULL, nlt, nlt->strips.first) + return NULL; + + /* try to find the first active strip */ + for (strip= nlt->strips.first; strip; strip= strip->next) { + if (strip->flag & NLASTRIP_FLAG_ACTIVE) + return strip; + } + + /* none found */ + return NULL; +} + +/* Does the given NLA-strip fall within the given bounds (times)? */ +short BKE_nlastrip_within_bounds (NlaStrip *strip, float min, float max) +{ + const float stripLen= (strip) ? strip->end - strip->start : 0.0f; + const float boundsLen= (float)fabs(max - min); + + /* sanity checks */ + if ((strip == NULL) || IS_EQ(stripLen, 0.0f) || IS_EQ(boundsLen, 0.0f)) + return 0; + + /* only ok if at least part of the strip is within the bounding window + * - first 2 cases cover when the strip length is less than the bounding area + * - second 2 cases cover when the strip length is greater than the bounding area + */ + if ( (stripLen < boundsLen) && + !(IN_RANGE(strip->start, min, max) || + IN_RANGE(strip->end, min, max)) ) + { + return 0; + } + if ( (stripLen > boundsLen) && + !(IN_RANGE(min, strip->start, strip->end) || + IN_RANGE(max, strip->start, strip->end)) ) { - free_actionstrip (strip); - }; + return 0; + } + + /* should be ok! */ + return 1; +} - /* Free the whole list */ - BLI_freelistN(nlalist); +/* Is the given NLA-strip the first one to occur for the given AnimData block */ +// TODO: make this an api method if necesary, but need to add prefix first +short nlastrip_is_first (AnimData *adt, NlaStrip *strip) +{ + NlaTrack *nlt; + NlaStrip *ns; + + /* sanity checks */ + if ELEM(NULL, adt, strip) + return 0; + + /* check if strip has any strips before it */ + if (strip->prev) + return 0; + + /* check other tracks to see if they have a strip that's earlier */ + // TODO: or should we check that the strip's track is also the first? + for (nlt= adt->nla_tracks.first; nlt; nlt= nlt->next) { + /* only check the first strip, assuming that they're all in order */ + ns= nlt->strips.first; + if (ns) { + if (ns->start < strip->start) + return 0; + } + } + + /* should be first now */ + return 1; } + +/* Animated Strips ------------------------------------------- */ + +/* Check if the given NLA-Track has any strips with own F-Curves */ +short BKE_nlatrack_has_animated_strips (NlaTrack *nlt) +{ + NlaStrip *strip; + + /* sanity checks */ + if ELEM(NULL, nlt, nlt->strips.first) + return 0; + + /* check each strip for F-Curves only (don't care about whether the flags are set) */ + for (strip= nlt->strips.first; strip; strip= strip->next) { + if (strip->fcurves.first) + return 1; + } + + /* none found */ + return 0; +} + +/* Check if given NLA-Tracks have any strips with own F-Curves */ +short BKE_nlatracks_have_animated_strips (ListBase *tracks) +{ + NlaTrack *nlt; + + /* sanity checks */ + if ELEM(NULL, tracks, tracks->first) + return 0; + + /* check each track, stopping on the first hit */ + for (nlt= tracks->first; nlt; nlt= nlt->next) { + if (BKE_nlatrack_has_animated_strips(nlt)) + return 1; + } + + /* none found */ + return 0; +} + +/* Validate the NLA-Strips 'control' F-Curves based on the flags set*/ +void BKE_nlastrip_validate_fcurves (NlaStrip *strip) +{ + FCurve *fcu; + + /* sanity checks */ + if (strip == NULL) + return; + + /* if controlling influence... */ + if (strip->flag & NLASTRIP_FLAG_USR_INFLUENCE) { + /* try to get F-Curve */ + fcu= list_find_fcurve(&strip->fcurves, "influence", 0); + + /* add one if not found */ + if (fcu == NULL) { + /* make new F-Curve */ + fcu= MEM_callocN(sizeof(FCurve), "NlaStrip FCurve"); + BLI_addtail(&strip->fcurves, fcu); + + /* set default flags */ + fcu->flag = (FCURVE_VISIBLE|FCURVE_AUTO_HANDLES|FCURVE_SELECTED); + + /* store path - make copy, and store that */ + fcu->rna_path= BLI_strdupn("influence", 9); + + // TODO: insert a few keyframes to ensure default behaviour? + } + } + + /* if controlling time... */ + if (strip->flag & NLASTRIP_FLAG_USR_TIME) { + /* try to get F-Curve */ + fcu= list_find_fcurve(&strip->fcurves, "strip_time", 0); + + /* add one if not found */ + if (fcu == NULL) { + /* make new F-Curve */ + fcu= MEM_callocN(sizeof(FCurve), "NlaStrip FCurve"); + BLI_addtail(&strip->fcurves, fcu); + + /* set default flags */ + fcu->flag = (FCURVE_VISIBLE|FCURVE_AUTO_HANDLES|FCURVE_SELECTED); + + /* store path - make copy, and store that */ + fcu->rna_path= BLI_strdupn("strip_time", 10); + + // TODO: insert a few keyframes to ensure default behaviour? + } + } +} + +/* Sanity Validation ------------------------------------ */ + +/* Find (and set) a unique name for a strip from the whole AnimData block + * Uses a similar method to the BLI method, but is implemented differently + * as we need to ensure that the name is unique over several lists of tracks, + * not just a single track. + */ +void BKE_nlastrip_validate_name (AnimData *adt, NlaStrip *strip) +{ + GHash *gh; + NlaStrip *tstrip; + NlaTrack *nlt; + + /* sanity checks */ + if ELEM(NULL, adt, strip) + return; + + /* give strip a default name if none already */ + if (strip->name[0]==0) { + switch (strip->type) { + case NLASTRIP_TYPE_CLIP: /* act-clip */ + sprintf(strip->name, "Act: %s", (strip->act)?(strip->act->id.name+2):("<None>")); + break; + case NLASTRIP_TYPE_TRANSITION: /* transition */ + sprintf(strip->name, "Transition"); + break; + case NLASTRIP_TYPE_META: /* meta */ + sprintf(strip->name, "Meta"); + break; + default: + sprintf(strip->name, "NLA Strip"); + break; + } + } + + /* build a hash-table of all the strips in the tracks + * - this is easier than iterating over all the tracks+strips hierarchy everytime + * (and probably faster) + */ + gh= BLI_ghash_new(BLI_ghashutil_strhash, BLI_ghashutil_strcmp); + + for (nlt= adt->nla_tracks.first; nlt; nlt= nlt->next) { + for (tstrip= nlt->strips.first; tstrip; tstrip= tstrip->next) { + /* don't add the strip of interest */ + if (tstrip == strip) + continue; + + /* use the name of the strip as the key, and the strip as the value, since we're mostly interested in the keys */ + BLI_ghash_insert(gh, tstrip->name, tstrip); + } + } + + /* if the hash-table has a match for this name, try other names... + * - in an extreme case, it might not be able to find a name, but then everything else in Blender would fail too :) + */ + if (BLI_ghash_haskey(gh, strip->name)) { + char tempname[128]; + int number = 1; + char *dot; + + /* Strip off the suffix */ + dot = strchr(strip->name, '.'); + if (dot) *dot=0; + + /* Try different possibilities */ + for (number = 1; number <= 999; number++) { + /* assemble alternative name */ + BLI_snprintf(tempname, 128, "%s%c%03d", strip->name, ".", number); + + /* if hash doesn't have this, set it */ + if (BLI_ghash_haskey(gh, tempname) == 0) { + BLI_strncpy(strip->name, tempname, sizeof(strip->name)); + break; + } + } + } + + /* free the hash... */ + BLI_ghash_free(gh, NULL, NULL); +} + +/* ---- */ + +/* Determine auto-blending for the given strip */ +void BKE_nlastrip_validate_autoblends (NlaTrack *nlt, NlaStrip *nls) +{ + NlaTrack *track; + NlaStrip *strip; + //float *ps=NULL, *pe=NULL; + //float *ns=NULL, *ne=NULL; + + /* sanity checks */ + if ELEM(NULL, nls, nlt) + return; + if ((nlt->prev == NULL) && (nlt->next == NULL)) + return; + if ((nls->flag & NLASTRIP_FLAG_AUTO_BLENDS)==0) + return; + + /* get test ranges */ + if (nlt->prev) { + /* find strips that overlap over the start/end of the given strip, + * but which don't cover the entire length + */ + track= nlt->prev; + for (strip= track->strips.first; strip; strip= strip->next) { + + } + } + if (nlt->next) { + /* find strips that overlap over the start/end of the given strip, + * but which don't cover the entire length + */ + track= nlt->next; + for (strip= track->strips.first; strip; strip= strip->next) { + + } + } +} + +/* Ensure that auto-blending and other settings are set correctly */ +void BKE_nla_validate_state (AnimData *adt) +{ + NlaStrip *strip; + NlaTrack *nlt; + + /* sanity checks */ + if ELEM(NULL, adt, adt->nla_tracks.first) + return; + + /* adjust blending values for auto-blending */ + for (nlt= adt->nla_tracks.first; nlt; nlt= nlt->next) { + for (strip= nlt->strips.first; strip; strip= strip->next) { + BKE_nlastrip_validate_autoblends(nlt, strip); + } + } +} + +/* Core Tools ------------------------------------------- */ + +/* For the given AnimData block, add the active action to the NLA + * stack (i.e. 'push-down' action). The UI should only allow this + * for normal editing only (i.e. not in editmode for some strip's action), + * so no checks for this are performed. + */ +// TODO: maybe we should have checks for this too... +void BKE_nla_action_pushdown (AnimData *adt) +{ + NlaStrip *strip; + + /* sanity checks */ + // TODO: need to report the error for this + if ELEM(NULL, adt, adt->action) + return; + + /* if the action is empty, we also shouldn't try to add to stack, + * as that will cause us grief down the track + */ + // TODO: what about modifiers? + if (action_has_motion(adt->action) == 0) { + printf("BKE_nla_action_pushdown(): action has no data \n"); + return; + } + + /* add a new NLA strip to the track, which references the active action */ + strip= add_nlastrip_to_stack(adt, adt->action); + + /* do other necessary work on strip */ + if (strip) { + /* clear reference to action now that we've pushed it onto the stack */ + adt->action->id.us--; + adt->action= NULL; + + /* if the strip is the first one in the track it lives in, check if there + * are strips in any other tracks that may be before this, and set the extend + * mode accordingly + */ + if (nlastrip_is_first(adt, strip) == 0) { + /* not first, so extend mode can only be NLASTRIP_EXTEND_HOLD_FORWARD not NLASTRIP_EXTEND_HOLD, + * so that it doesn't override strips in previous tracks + */ + // FIXME: this needs to be more automated, since user can rearrange strips + strip->extendmode= NLASTRIP_EXTEND_HOLD_FORWARD; + } + } +} + + +/* Find the active strip + track combo, and set them up as the tweaking track, + * and return if successful or not. + */ +short BKE_nla_tweakmode_enter (AnimData *adt) +{ + NlaTrack *nlt, *activeTrack=NULL; + NlaStrip *strip, *activeStrip=NULL; + + /* verify that data is valid */ + if ELEM(NULL, adt, adt->nla_tracks.first) + return 0; + + /* if block is already in tweakmode, just leave, but we should report + * that this block is in tweakmode (as our returncode) + */ + if (adt->flag & ADT_NLA_EDIT_ON) + return 1; + + /* go over the tracks, finding the active one, and its active strip + * - if we cannot find both, then there's nothing to do + */ + for (nlt= adt->nla_tracks.first; nlt; nlt= nlt->next) { + /* check if active */ + if (nlt->flag & NLATRACK_ACTIVE) { + /* store reference to this active track */ + activeTrack= nlt; + + /* now try to find active strip */ + activeStrip= BKE_nlastrip_find_active(nlt); + break; + } + } + if ELEM3(NULL, activeTrack, activeStrip, activeStrip->act) { + printf("NLA tweakmode enter - neither active requirement found \n"); + return 0; + } + + /* go over all the tracks up to the active one, tagging each strip that uses the same + * action as the active strip, but leaving everything else alone + */ + for (nlt= activeTrack->prev; nlt; nlt= nlt->prev) { + for (strip= nlt->strips.first; strip; strip= strip->next) { + if (strip->act == activeStrip->act) + strip->flag |= NLASTRIP_FLAG_TWEAKUSER; + else + strip->flag &= ~NLASTRIP_FLAG_TWEAKUSER; + } + } + + + /* go over all the tracks after AND INCLUDING the active one, tagging them as being disabled + * - the active track needs to also be tagged, otherwise, it'll overlap with the tweaks going on + */ + for (nlt= activeTrack; nlt; nlt= nlt->next) + nlt->flag |= NLATRACK_DISABLED; + + /* handle AnimData level changes: + * - 'real' active action to temp storage (no need to change user-counts) + * - action of active strip set to be the 'active action', and have its usercount incremented + * - editing-flag for this AnimData block should also get turned on (for more efficient restoring) + * - take note of the active strip for mapping-correction of keyframes in the action being edited + */ + adt->tmpact= adt->action; + adt->action= activeStrip->act; + adt->actstrip= activeStrip; + id_us_plus(&activeStrip->act->id); + adt->flag |= ADT_NLA_EDIT_ON; + + /* done! */ + return 1; +} + +/* Exit tweakmode for this AnimData block */ +void BKE_nla_tweakmode_exit (AnimData *adt) +{ + NlaStrip *strip; + NlaTrack *nlt; + + /* verify that data is valid */ + if ELEM(NULL, adt, adt->nla_tracks.first) + return; + + /* hopefully the flag is correct - skip if not on */ + if ((adt->flag & ADT_NLA_EDIT_ON) == 0) + return; + + // TODO: need to sync the user-strip with the new state of the action! + + /* for all Tracks, clear the 'disabled' flag + * for all Strips, clear the 'tweak-user' flag + */ + for (nlt= adt->nla_tracks.first; nlt; nlt= nlt->next) { + nlt->flag &= ~NLATRACK_DISABLED; + + for (strip= nlt->strips.first; strip; strip= strip->next) + strip->flag &= ~NLASTRIP_FLAG_TWEAKUSER; + } + + /* handle AnimData level changes: + * - 'temporary' active action needs its usercount decreased, since we're removing this reference + * - 'real' active action is restored from storage + * - storage pointer gets cleared (to avoid having bad notes hanging around) + * - editing-flag for this AnimData block should also get turned off + * - clear pointer to active strip + */ + if (adt->action) adt->action->id.us--; + adt->action= adt->tmpact; + adt->tmpact= NULL; + adt->actstrip= NULL; + adt->flag &= ~ADT_NLA_EDIT_ON; +} + +/* *************************************************** */ diff --git a/source/blender/blenkernel/intern/node.c b/source/blender/blenkernel/intern/node.c index a83b8817580..0f42ba0d2e2 100644 --- a/source/blender/blenkernel/intern/node.c +++ b/source/blender/blenkernel/intern/node.c @@ -3050,6 +3050,7 @@ static void registerTextureNodes(ListBase *ntypelist) nodeRegisterType(ntypelist, &tex_node_rotate); nodeRegisterType(ntypelist, &tex_node_translate); nodeRegisterType(ntypelist, &tex_node_scale); + nodeRegisterType(ntypelist, &tex_node_at); nodeRegisterType(ntypelist, &tex_node_proc_voronoi); nodeRegisterType(ntypelist, &tex_node_proc_blend); diff --git a/source/blender/blenkernel/intern/object.c b/source/blender/blenkernel/intern/object.c index c45a1593b8a..82eca760a60 100644 --- a/source/blender/blenkernel/intern/object.c +++ b/source/blender/blenkernel/intern/object.c @@ -244,7 +244,9 @@ void free_object(Object *ob) if(ob->mat[a]) ob->mat[a]->id.us--; } if(ob->mat) MEM_freeN(ob->mat); + if(ob->matbits) MEM_freeN(ob->matbits); ob->mat= 0; + ob->matbits= 0; if(ob->bb) MEM_freeN(ob->bb); ob->bb= 0; if(ob->path) free_path(ob->path); @@ -428,17 +430,14 @@ void unlink_object(Scene *scene, Object *ob) if(obt->particlesystem.first) { ParticleSystem *tpsys= obt->particlesystem.first; for(; tpsys; tpsys=tpsys->next) { - if(tpsys->keyed_ob==ob) { - ParticleSystem *psys= BLI_findlink(&ob->particlesystem,tpsys->keyed_psys-1); - - if(psys && psys->keyed_ob) { - tpsys->keyed_ob= psys->keyed_ob; - tpsys->keyed_psys= psys->keyed_psys; + KeyedParticleTarget *kpt = tpsys->keyed_targets.first; + for(; kpt; kpt=kpt->next) { + if(kpt->ob==ob) { + BLI_remlink(&tpsys->keyed_targets, kpt); + MEM_freeN(kpt); + obt->recalc |= OB_RECALC_DATA; + break; } - else - tpsys->keyed_ob= NULL; - - obt->recalc |= OB_RECALC_DATA; } if(tpsys->target_ob==ob) { @@ -946,7 +945,6 @@ Object *add_only_object(int type, char *name) Mat4One(ob->parentinv); Mat4One(ob->obmat); ob->dt= OB_SHADED; - if(U.flag & USER_MAT_ON_OB) ob->colbits= -1; ob->empty_drawtype= OB_ARROWS; ob->empty_drawsize= 1.0; @@ -1051,18 +1049,23 @@ ParticleSystem *copy_particlesystem(ParticleSystem *psys) psysn= MEM_dupallocN(psys); psysn->particles= MEM_dupallocN(psys->particles); psysn->child= MEM_dupallocN(psys->child); + if(psysn->particles->keys) + psysn->particles->keys = MEM_dupallocN(psys->particles->keys); for(a=0, pa=psysn->particles; a<psysn->totpart; a++, pa++) { if(pa->hair) pa->hair= MEM_dupallocN(pa->hair); - if(pa->keys) - pa->keys= MEM_dupallocN(pa->keys); + if(a) + pa->keys= (pa-1)->keys + (pa-1)->totkey; } if(psys->soft) { psysn->soft= copy_softbody(psys->soft); psysn->soft->particles = psysn; } + + if(psys->keyed_targets.first) + BLI_duplicatelist(&psysn->keyed_targets, &psys->keyed_targets); psysn->pathcache= NULL; psysn->childcache= NULL; @@ -1169,6 +1172,7 @@ Object *copy_object(Object *ob) if(ob->totcol) { obn->mat= MEM_dupallocN(ob->mat); + obn->matbits= MEM_dupallocN(ob->matbits); } if(ob->bb) obn->bb= MEM_dupallocN(ob->bb); @@ -1199,18 +1203,12 @@ Object *copy_object(Object *ob) armature_rebuild_pose(obn, obn->data); } copy_defgroups(&obn->defbase, &ob->defbase); -#if 0 // XXX old animation system - copy_nlastrips(&obn->nlastrips, &ob->nlastrips); -#endif // XXX old animation system copy_constraints(&obn->constraints, &ob->constraints); /* increase user numbers */ id_us_plus((ID *)obn->data); -#if 0 // XXX old animation system - id_us_plus((ID *)obn->ipo); - id_us_plus((ID *)obn->action); -#endif // XXX old animation system id_us_plus((ID *)obn->dup_group); + // FIXME: add this for animdata too... for(a=0; a<obn->totcol; a++) id_us_plus((ID *)obn->mat[a]); @@ -1402,7 +1400,9 @@ void object_make_proxy(Object *ob, Object *target, Object *gob) /* copy material and index information */ ob->actcol= ob->totcol= 0; if(ob->mat) MEM_freeN(ob->mat); + if(ob->matbits) MEM_freeN(ob->matbits); ob->mat = NULL; + ob->matbits= 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; @@ -1411,6 +1411,7 @@ void object_make_proxy(Object *ob, Object *target, Object *gob) ob->totcol= target->totcol; ob->mat = MEM_dupallocN(target->mat); + ob->matbits = MEM_dupallocN(target->matbits); 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]); @@ -1576,14 +1577,14 @@ static void ob_parcurve(Scene *scene, Object *ob, Object *par, float mat[][4]) } /* catch exceptions: curve paths used as a duplicator */ else if(enable_cu_speed) { - ctime= bsystem_time(scene, ob, (float)scene->r.cfra, 0.0); - -#if 0 // XXX old animation system - if(calc_ipo_spec(cu->ipo, CU_SPEED, &ctime)==0) { - ctime /= cu->pathlen; - CLAMP(ctime, 0.0, 1.0); - } -#endif // XXX old animation system + /* ctime is now a proper var setting of Curve which gets set by Animato like any other var that's animated, + * but this will only work if it actually is animated... + * + * we firstly calculate the modulus of cu->ctime/cu->pathlen to clamp ctime within the 0.0 to 1.0 times pathlen + * range, then divide this (the modulus) by pathlen to get a value between 0.0 and 1.0 + */ + ctime= fmod(cu->ctime, cu->pathlen) / cu->pathlen; + CLAMP(ctime, 0.0, 1.0); } else { ctime= scene->r.cfra - give_timeoffset(ob); diff --git a/source/blender/blenkernel/intern/packedFile.c b/source/blender/blenkernel/intern/packedFile.c index 22e4e8a8309..02b0f6a45a0 100644 --- a/source/blender/blenkernel/intern/packedFile.c +++ b/source/blender/blenkernel/intern/packedFile.c @@ -61,8 +61,9 @@ #include "BKE_image.h" #include "BKE_font.h" #include "BKE_packedFile.h" +#include "BKE_report.h" -int seekPackedFile(PackedFile * pf, int offset, int whence) +int seekPackedFile(PackedFile *pf, int offset, int whence) { int oldseek = -1, seek = 0; @@ -92,12 +93,12 @@ int seekPackedFile(PackedFile * pf, int offset, int whence) return(oldseek); } -void rewindPackedFile(PackedFile * pf) +void rewindPackedFile(PackedFile *pf) { seekPackedFile(pf, 0, SEEK_SET); } -int readPackedFile(PackedFile * pf, void * data, int size) +int readPackedFile(PackedFile *pf, void *data, int size) { if ((pf != NULL) && (size >= 0) && (data != NULL)) { if (size + pf->seek > pf->size) { @@ -118,66 +119,55 @@ int readPackedFile(PackedFile * pf, void * data, int size) return(size); } -int countPackedFiles() +int countPackedFiles(Main *bmain) { - int count = 0; Image *ima; VFont *vf; bSample *sample; + int count = 0; // let's check if there are packed files... - ima = G.main->image.first; - while (ima) { - if (ima->packedfile) { + for(ima=bmain->image.first; ima; ima=ima->id.next) + if(ima->packedfile) count++; - } - ima= ima->id.next; - } - vf = G.main->vfont.first; - while (vf) { - if (vf->packedfile) { + for(vf=bmain->vfont.first; vf; vf=vf->id.next) + if(vf->packedfile) count++; - } - vf = vf->id.next; - } - sample = samples->first; - while (sample) { - if (sample->packedfile) { - count++; - } - sample = sample->id.next; - } + if(samples) + for(sample=samples->first; sample; sample=sample->id.next) + if(sample->packedfile) + count++; - return(count); + return count; } -void freePackedFile(PackedFile * pf) +void freePackedFile(PackedFile *pf) { - if (pf) { + if(pf) { MEM_freeN(pf->data); MEM_freeN(pf); - } else { - printf("freePackedFile: Trying to free a NULL pointer\n"); } + else + printf("freePackedFile: Trying to free a NULL pointer\n"); } -PackedFile * newPackedFileMemory(void *mem, int memlen) +PackedFile *newPackedFileMemory(void *mem, int memlen) { - PackedFile * pf = MEM_callocN(sizeof(*pf), "PackedFile"); + PackedFile *pf = MEM_callocN(sizeof(*pf), "PackedFile"); pf->data = mem; pf->size = memlen; return pf; } -PackedFile * newPackedFile(char * filename) +PackedFile *newPackedFile(ReportList *reports, char *filename) { - PackedFile * pf = NULL; + PackedFile *pf = NULL; int file, filelen; char name[FILE_MAXDIR+FILE_MAXFILE]; - void * data; + void *data; //XXX waitcursor(1); @@ -191,7 +181,7 @@ PackedFile * newPackedFile(char * filename) file= open(name, O_BINARY|O_RDONLY); if (file <= 0) { - // error("Can't open file: %s", name); + BKE_reportf(reports, RPT_ERROR, "Can't open file: %s", name); } else { filelen = BLI_filesize(file); @@ -214,36 +204,24 @@ PackedFile * newPackedFile(char * filename) return (pf); } -void packAll() +void packAll(Main *bmain, ReportList *reports) { Image *ima; VFont *vf; bSample *sample; - ima = G.main->image.first; - while (ima) { - if (ima->packedfile == NULL) { - ima->packedfile = newPackedFile(ima->name); - } - ima= ima->id.next; - } - - vf = G.main->vfont.first; - while (vf) { - if (vf->packedfile == NULL) { - vf->packedfile = newPackedFile(vf->name); - } - vf = vf->id.next; - } + for(ima=bmain->image.first; ima; ima=ima->id.next) + if(ima->packedfile == NULL) + ima->packedfile = newPackedFile(reports, ima->name); + for(vf=bmain->vfont.first; vf; vf=vf->id.next) + if(vf->packedfile == NULL) + vf->packedfile = newPackedFile(reports, vf->name); - sample = samples->first; - while (sample) { - if (sample->packedfile == NULL) { - sound_set_packedfile(sample, newPackedFile(sample->name)); - } - sample = sample->id.next; - } + if(samples) + for(sample=samples->first; sample; sample=sample->id.next) + if(sample->packedfile == NULL) + sound_set_packedfile(sample, newPackedFile(reports, sample->name)); } @@ -252,10 +230,10 @@ void packAll() // attempt to create a function that generates an unique filename // this will work when all funtions in fileops.c understand relative filenames... -char * find_new_name(char * name) +char *find_new_name(char *name) { char tempname[FILE_MAXDIR + FILE_MAXFILE]; - char * newname; + char *newname; if (fop_exists(name)) { for (number = 1; number <= 999; number++) { @@ -274,13 +252,13 @@ char * find_new_name(char * name) */ -int writePackedFile(char * filename, PackedFile *pf, int guimode) +int writePackedFile(ReportList *reports, char *filename, PackedFile *pf, int guimode) { int file, number, remove_tmp = FALSE; int ret_value = RET_OK; char name[FILE_MAXDIR + FILE_MAXFILE]; char tempname[FILE_MAXDIR + FILE_MAXFILE]; -/* void * data; */ +/* void *data; */ if (guimode); //XXX waitcursor(1); @@ -305,23 +283,23 @@ int writePackedFile(char * filename, PackedFile *pf, int guimode) file = open(name, O_BINARY + O_WRONLY + O_CREAT + O_TRUNC, 0666); if (file >= 0) { if (write(file, pf->data, pf->size) != pf->size) { - if(guimode) ; //XXX error("Error writing file: %s", name); + BKE_reportf(reports, RPT_ERROR, "Error writing file: %s", name); ret_value = RET_ERROR; } close(file); } else { - if(guimode); //XXX error("Error creating file: %s", name); + BKE_reportf(reports, RPT_ERROR, "Error creating file: %s", name); ret_value = RET_ERROR; } if (remove_tmp) { if (ret_value == RET_ERROR) { if (BLI_rename(tempname, name) != 0) { - if(guimode); //XXX error("Error restoring tempfile. Check files: '%s' '%s'", tempname, name); + BKE_reportf(reports, RPT_ERROR, "Error restoring tempfile. Check files: '%s' '%s'", tempname, name); } } else { if (BLI_delete(tempname, 0, 0) != 0) { - if(guimode); //XXX error("Error deleting '%s' (ignored)"); + BKE_reportf(reports, RPT_ERROR, "Error deleting '%s' (ignored)", tempname); } } } @@ -342,7 +320,7 @@ PF_NOFILE - the original file doens't exist */ -int checkPackedFile(char * filename, PackedFile * pf) +int checkPackedFile(char *filename, PackedFile *pf) { struct stat st; int ret_val, i, len, file; @@ -390,68 +368,21 @@ int checkPackedFile(char * filename, PackedFile * pf) /* -unpackFile() looks at the existing files (abs_name, local_name) and a packed file. -If how == PF_ASK it offers the user a couple of options what to do with the packed file. + unpackFile() looks at the existing files (abs_name, local_name) and a packed file. -It returns a char * to the existing file name / new file name or NULL when +It returns a char *to the existing file name / new file name or NULL when there was an error or when the user desides to cancel the operation. */ -char *unpackFile(char * abs_name, char * local_name, PackedFile * pf, int how) +char *unpackFile(ReportList *reports, char *abs_name, char *local_name, PackedFile *pf, int how) { - char menu[6 * (FILE_MAXDIR + FILE_MAXFILE + 100)]; - char line[FILE_MAXDIR + FILE_MAXFILE + 100]; - char * newname = NULL, * temp = NULL; + char *newname = NULL, *temp = NULL; // char newabs[FILE_MAXDIR + FILE_MAXFILE]; // char newlocal[FILE_MAXDIR + FILE_MAXFILE]; if (pf != NULL) { - if (how == PF_ASK) { - sprintf(menu, "UnPack file%%t|Remove Pack %%x%d", PF_REMOVE); - - if (strcmp(abs_name, local_name)) { - switch (checkPackedFile(local_name, pf)) { - case PF_NOFILE: - sprintf(line, "|Create %s%%x%d", local_name, PF_WRITE_LOCAL); - strcat(menu, line); - break; - case PF_EQUAL: - sprintf(line, "|Use %s (identical)%%x%d", local_name, PF_USE_LOCAL); - strcat(menu, line); - break; - case PF_DIFFERS: - sprintf(line, "|Use %s (differs)%%x%d", local_name, PF_USE_LOCAL); - strcat(menu, line); - sprintf(line, "|Overwrite %s%%x%d", local_name, PF_WRITE_LOCAL); - strcat(menu, line); - break; - } - // sprintf(line, "|%%x%d", PF_INVALID); - // strcat(menu, line); - } - - switch (checkPackedFile(abs_name, pf)) { - case PF_NOFILE: - sprintf(line, "|Create %s%%x%d", abs_name, PF_WRITE_ORIGINAL); - strcat(menu, line); - break; - case PF_EQUAL: - sprintf(line, "|Use %s (identical)%%x%d", abs_name, PF_USE_ORIGINAL); - strcat(menu, line); - break; - case PF_DIFFERS: - sprintf(line, "|Use %s (differs)%%x%d", abs_name, PF_USE_ORIGINAL); - strcat(menu, line); - sprintf(line, "|Overwrite %s%%x%d", abs_name, PF_WRITE_ORIGINAL); - strcat(menu, line); - break; - } - - //XXX how = pupmenu(menu); - } - switch (how) { case -1: case PF_KEEP: @@ -467,7 +398,7 @@ char *unpackFile(char * abs_name, char * local_name, PackedFile * pf, int how) } // else fall through and create it case PF_WRITE_LOCAL: - if (writePackedFile(local_name, pf, 1) == RET_OK) { + if (writePackedFile(reports, local_name, pf, 1) == RET_OK) { temp = local_name; } break; @@ -479,7 +410,7 @@ char *unpackFile(char * abs_name, char * local_name, PackedFile * pf, int how) } // else fall through and create it case PF_WRITE_ORIGINAL: - if (writePackedFile(abs_name, pf, 1) == RET_OK) { + if (writePackedFile(reports, abs_name, pf, 1) == RET_OK) { temp = abs_name; } break; @@ -498,10 +429,10 @@ char *unpackFile(char * abs_name, char * local_name, PackedFile * pf, int how) } -int unpackVFont(VFont * vfont, int how) +int unpackVFont(ReportList *reports, VFont *vfont, int how) { char localname[FILE_MAXDIR + FILE_MAXFILE], fi[FILE_MAXFILE]; - char * newname; + char *newname; int ret_value = RET_ERROR; if (vfont != NULL) { @@ -510,7 +441,7 @@ int unpackVFont(VFont * vfont, int how) sprintf(localname, "//fonts/%s", fi); - newname = unpackFile(vfont->name, localname, vfont->packedfile, how); + newname = unpackFile(reports, vfont->name, localname, vfont->packedfile, how); if (newname != NULL) { ret_value = RET_OK; freePackedFile(vfont->packedfile); @@ -523,10 +454,10 @@ int unpackVFont(VFont * vfont, int how) return (ret_value); } -int unpackSample(bSample *sample, int how) +int unpackSample(ReportList *reports, bSample *sample, int how) { char localname[FILE_MAXDIR + FILE_MAX], fi[FILE_MAX]; - char * newname; + char *newname; int ret_value = RET_ERROR; PackedFile *pf; @@ -535,7 +466,7 @@ int unpackSample(bSample *sample, int how) BLI_splitdirstring(localname, fi); sprintf(localname, "//samples/%s", fi); - newname = unpackFile(sample->name, localname, sample->packedfile, how); + newname = unpackFile(reports, sample->name, localname, sample->packedfile, how); if (newname != NULL) { strcpy(sample->name, newname); MEM_freeN(newname); @@ -553,10 +484,10 @@ int unpackSample(bSample *sample, int how) return(ret_value); } -int unpackImage(Image * ima, int how) +int unpackImage(ReportList *reports, Image *ima, int how) { char localname[FILE_MAXDIR + FILE_MAX], fi[FILE_MAX]; - char * newname; + char *newname; int ret_value = RET_ERROR; if (ima != NULL) { @@ -564,7 +495,7 @@ int unpackImage(Image * ima, int how) BLI_splitdirstring(localname, fi); sprintf(localname, "//textures/%s", fi); - newname = unpackFile(ima->name, localname, ima->packedfile, how); + newname = unpackFile(reports, ima->name, localname, ima->packedfile, how); if (newname != NULL) { ret_value = RET_OK; freePackedFile(ima->packedfile); @@ -578,33 +509,23 @@ int unpackImage(Image * ima, int how) return(ret_value); } -void unpackAll(int how) +void unpackAll(Main *bmain, ReportList *reports, int how) { Image *ima; VFont *vf; bSample *sample; - - ima = G.main->image.first; - while (ima) { - if (ima->packedfile) { - unpackImage(ima, how); - } - ima= ima->id.next; - } - - vf = G.main->vfont.first; - while (vf) { - if (vf->packedfile) { - unpackVFont(vf, how); - } - vf = vf->id.next; - } - sample = samples->first; - while (sample) { - if (sample->packedfile) { - unpackSample(sample, how); - } - sample = sample->id.next; - } + for(ima=bmain->image.first; ima; ima=ima->id.next) + if(ima->packedfile) + unpackImage(reports, ima, how); + + for(vf=bmain->vfont.first; vf; vf=vf->id.next) + if(vf->packedfile) + unpackVFont(reports, vf, how); + + if(samples) + for(sample=samples->first; sample; sample=sample->id.next) + if(sample->packedfile) + unpackSample(reports, sample, how); } + diff --git a/source/blender/blenkernel/intern/particle.c b/source/blender/blenkernel/intern/particle.c index ab7f68dedd0..8e0e948f0a4 100644 --- a/source/blender/blenkernel/intern/particle.c +++ b/source/blender/blenkernel/intern/particle.c @@ -81,6 +81,11 @@ static void key_from_object(Object *ob, ParticleKey *key); static void get_cpa_texture(DerivedMesh *dm, Material *ma, int face_index, float *fuv, float *orco, ParticleTexture *ptex, int event); +static void get_child_modifier_parameters(ParticleSettings *part, ParticleThreadContext *ctx, + ChildParticle *cpa, short cpa_from, int cpa_num, float *cpa_fuv, float *orco, ParticleTexture *ptex); +static void do_child_modifiers(Scene *scene, Object *ob, ParticleSystem *psys, ParticleSettings *part, + ParticleTexture *ptex, ParticleKey *par, float *par_rot, ChildParticle *cpa, + float *orco, float mat[4][4], ParticleKey *state, float t); /* few helpers for countall etc. */ int count_particles(ParticleSystem *psys){ @@ -222,6 +227,34 @@ short psys_get_current_num(Object *ob) return i; } +void psys_set_current_num(Object *ob, int index) +{ + ParticleSystem *psys; + short i; + + if(ob==0) return; + + for(psys=ob->particlesystem.first, i=0; psys; psys=psys->next, i++) { + if(i == index) + psys->flag |= PSYS_CURRENT; + else + psys->flag &= ~PSYS_CURRENT; + } +} +Object *psys_find_object(Scene *scene, ParticleSystem *psys) +{ + Base *base = scene->base.first; + ParticleSystem *tpsys; + + for(base = scene->base.first; base; base = base->next) { + for(tpsys = base->object->particlesystem.first; psys; psys=psys->next) { + if(tpsys == psys) + return base->object; + } + } + + return NULL; +} /* change object's active particle system */ void psys_change_act(void *ob_v, void *act_v) { @@ -293,7 +326,7 @@ int psys_check_enabled(Object *ob, ParticleSystem *psys) ParticleSystemModifierData *psmd; Mesh *me; - if(psys->flag & PSYS_DISABLED || psys->flag & PSYS_DELETE) + if(psys->flag & PSYS_DISABLED || psys->flag & PSYS_DELETE || !psys->part) return 0; if(ob->type == OB_MESH) { @@ -424,7 +457,7 @@ void psys_free(Object *ob, ParticleSystem * psys) for(tpsys=ob->particlesystem.first; tpsys; tpsys=tpsys->next){ if(tpsys->part) { - if(ELEM(tpsys->part->draw_as,PART_DRAW_OB,PART_DRAW_GR)) + if(ELEM(tpsys->part->ren_as,PART_DRAW_OB,PART_DRAW_GR)) { nr++; break; @@ -446,10 +479,16 @@ void psys_free(Object *ob, ParticleSystem * psys) if(psys->pointcache) BKE_ptcache_free(psys->pointcache); + if(psys->keyed_targets.first) + BLI_freelistN(&psys->keyed_targets); + MEM_freeN(psys); } } +/************************************************/ +/* Rendering */ +/************************************************/ /* these functions move away particle data and bring it back after * rendering, to make different render settings possible without * removing the previous data. this should be solved properly once */ @@ -463,6 +502,7 @@ typedef struct ParticleRenderData { ChildParticle *child; ParticleCacheKey **pathcache; ParticleCacheKey **childcache; + ListBase pathcachebufs, childcachebufs; int totchild, totcached, totchildcache; DerivedMesh *dm; int totdmvert, totdmedge, totdmface; @@ -549,8 +589,12 @@ void psys_render_set(Object *ob, ParticleSystem *psys, float viewmat[][4], float data->child= psys->child; data->totchild= psys->totchild; data->pathcache= psys->pathcache; + data->pathcachebufs.first = psys->pathcachebufs.first; + data->pathcachebufs.last = psys->pathcachebufs.last; data->totcached= psys->totcached; data->childcache= psys->childcache; + data->childcachebufs.first = psys->childcachebufs.first; + data->childcachebufs.last = psys->childcachebufs.last; data->totchildcache= psys->totchildcache; if(psmd->dm) @@ -563,6 +607,8 @@ void psys_render_set(Object *ob, ParticleSystem *psys, float viewmat[][4], float psys->pathcache= NULL; psys->childcache= NULL; psys->totchild= psys->totcached= psys->totchildcache= 0; + psys->pathcachebufs.first = psys->pathcachebufs.last = NULL; + psys->childcachebufs.first = psys->childcachebufs.last = NULL; Mat4CpyMat4(data->winmat, winmat); Mat4MulMat4(data->viewmat, ob->obmat, viewmat); @@ -603,8 +649,12 @@ void psys_render_restore(Object *ob, ParticleSystem *psys) psys->child= data->child; psys->totchild= data->totchild; psys->pathcache= data->pathcache; + psys->pathcachebufs.first = data->pathcachebufs.first; + psys->pathcachebufs.last = data->pathcachebufs.last; psys->totcached= data->totcached; psys->childcache= data->childcache; + psys->childcachebufs.first = data->childcachebufs.first; + psys->childcachebufs.last = data->childcachebufs.last; psys->totchildcache= data->totchildcache; psmd->dm= data->dm; @@ -635,7 +685,7 @@ int psys_render_simplify_distribution(ParticleThreadContext *ctx, int tot) int *origindex, *facetotvert; int a, b, totorigface, totface, newtot, skipped; - if(part->draw_as!=PART_DRAW_PATH || !(part->draw & PART_DRAW_REN_STRAND)) + if(part->ren_as!=PART_DRAW_PATH || !(part->draw & PART_DRAW_REN_STRAND)) return tot; if(!ctx->psys->renderdata) return tot; @@ -846,7 +896,7 @@ int psys_render_simplify_params(ParticleSystem *psys, ChildParticle *cpa, float } /************************************************/ -/* Interpolated Particles */ +/* Interpolation */ /************************************************/ static float interpolate_particle_value(float v1, float v2, float v3, float v4, float *w, int four) { @@ -864,7 +914,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, int velocity) +void psys_interpolate_particle(short type, ParticleKey keys[4], float dt, ParticleKey *result, int velocity) { float t[4]; @@ -895,6 +945,214 @@ static void interpolate_particle(short type, ParticleKey keys[4], float dt, Part +typedef struct ParticleInterpolationData { + ParticleKey *kkey[2]; + HairKey *hkey[2]; + BodyPoint *bp[2]; + SoftBody *soft; + int keyed, cached; + float birthtime, dietime; +} ParticleInterpolationData; +/* Assumes pointcache->mem_cache exists, so for disk cached particles call psys_make_temp_pointcache() before use */ +static void get_pointcache_keys_for_time(Object *ob, ParticleSystem *psys, int index, float t, ParticleKey *key1, ParticleKey *key2) +{ + PointCache *cache = psys->pointcache; + static PTCacheMem *pm = NULL; /* not thread safe */ + + if(index < 0) { /* initialize */ + pm = cache->mem_cache.first; + + if(pm) + pm = pm->next; + } + else { + if(pm) { + while(pm && pm->next && (float)pm->frame < t) + pm = pm->next; + + copy_particle_key(key2, ((ParticleKey *)pm->data) + index, 1); + copy_particle_key(key1, ((ParticleKey *)(pm->prev)->data) + index, 1); + } + else if(cache->mem_cache.first) { + PTCacheMem *pm2 = cache->mem_cache.first; + copy_particle_key(key2, ((ParticleKey *)pm2->data) + index, 1); + copy_particle_key(key1, ((ParticleKey *)pm2->data) + index, 1); + } + } +} +static void init_particle_interpolation(Object *ob, ParticleSystem *psys, ParticleData *pa, ParticleInterpolationData *pind) +{ + + if(pind->keyed) { + pind->kkey[0] = pa->keys; + + if(pa->totkey > 1) + pind->kkey[1] = pa->keys + 1; + else + pind->kkey[1] = NULL; + + pind->birthtime = pa->keys->time; + pind->dietime = (pa->keys + pa->totkey - 1)->time; + } + else if(pind->cached) { + get_pointcache_keys_for_time(ob, psys, -1, 0.0f, NULL, NULL); + + pind->birthtime = pa->time; + pind->dietime = pa->dietime; + } + else { + pind->hkey[0] = pa->hair; + pind->hkey[1] = pa->hair + 1; + + pind->birthtime = pa->hair->time; + pind->dietime = (pa->hair + pa->totkey - 1)->time; + } + + if(pind->soft) { + pind->bp[0] = pind->soft->bpoint + pa->bpi; + pind->bp[1] = pind->soft->bpoint + pa->bpi + 1; + } +} +static void hair_to_particle(ParticleKey *key, HairKey *hkey) +{ + VECCOPY(key->co, hkey->co); + key->time = hkey->time; +} +static void bp_to_particle(ParticleKey *key, BodyPoint *bp, HairKey *hkey) +{ + VECCOPY(key->co, bp->pos); + key->time = hkey->time; +} + +static void do_particle_interpolation(ParticleSystem *psys, int p, ParticleData *pa, float t, float frs_sec, ParticleInterpolationData *pind, ParticleKey *result) +{ + ParticleKey keys[4]; + float real_t, dfra, keytime; + + /* interpret timing and find keys */ + if(pind->keyed) { + /* we have only one key, so let's use that */ + if(pind->kkey[1]==NULL) { + copy_particle_key(result, pind->kkey[0], 1); + return; + } + + if(result->time < 0.0f) + real_t = -result->time; + else + real_t = pind->kkey[0]->time + t * (pind->kkey[0][pa->totkey-1].time - pind->kkey[0]->time); + + if(psys->part->phystype==PART_PHYS_KEYED && psys->flag & PSYS_KEYED_TIMING) { + KeyedParticleTarget *kpt = psys->keyed_targets.first; + + kpt=kpt->next; + + while(kpt && pa->time + kpt->time < real_t) + kpt= kpt->next; + + if(kpt) { + kpt=kpt->prev; + + if(pa->time + kpt->time + kpt->duration > real_t) + real_t = pa->time + kpt->time; + } + else + real_t = pa->time + ((KeyedParticleTarget*)psys->keyed_targets.last)->time; + } + + CLAMP(real_t, pa->time, pa->dietime); + + while(pind->kkey[1]->time < real_t) + pind->kkey[1]++; + + pind->kkey[0] = pind->kkey[1] - 1; + } + else if(pind->cached) { + if(result->time < 0.0f) /* flag for time in frames */ + real_t = -result->time; + else + real_t = pa->time + t * (pa->dietime - pa->time); + } + else { + if(result->time < 0.0f) + real_t = -result->time; + else + real_t = pind->hkey[0]->time + t * (pind->hkey[0][pa->totkey-1].time - pind->hkey[0]->time); + + while(pind->hkey[1]->time < real_t) { + pind->hkey[1]++; + pind->bp[1]++; + } + + pind->hkey[0] = pind->hkey[1] - 1; + } + + /* set actual interpolation keys */ + if(pind->soft) { + pind->bp[0] = pind->bp[1] - 1; + bp_to_particle(keys + 1, pind->bp[0], pind->hkey[0]); + bp_to_particle(keys + 2, pind->bp[1], pind->hkey[1]); + } + else if(pind->keyed) { + memcpy(keys + 1, pind->kkey[0], sizeof(ParticleKey)); + memcpy(keys + 2, pind->kkey[1], sizeof(ParticleKey)); + } + else if(pind->cached) { + get_pointcache_keys_for_time(NULL, psys, p, real_t, keys+1, keys+2); + } + else { + hair_to_particle(keys + 1, pind->hkey[0]); + hair_to_particle(keys + 2, pind->hkey[1]); + } + + /* set secondary interpolation keys for hair */ + if(!pind->keyed && !pind->cached) { + if(pind->soft) { + if(pind->hkey[0] != pa->hair) + bp_to_particle(keys, pind->bp[0] - 1, pind->hkey[0] - 1); + else + bp_to_particle(keys, pind->bp[0], pind->hkey[0]); + } + else { + if(pind->hkey[0] != pa->hair) + hair_to_particle(keys, pind->hkey[0] - 1); + else + hair_to_particle(keys, pind->hkey[0]); + } + + if(pind->soft) { + if(pind->hkey[1] != pa->hair + pa->totkey - 1) + bp_to_particle(keys + 3, pind->bp[1] + 1, pind->hkey[1] + 1); + else + bp_to_particle(keys + 3, pind->bp[1], pind->hkey[1]); + } + else { + if(pind->hkey[1] != pa->hair + pa->totkey - 1) + hair_to_particle(keys + 3, pind->hkey[1] + 1); + else + hair_to_particle(keys + 3, pind->hkey[1]); + } + } + + dfra = keys[2].time - keys[1].time; + keytime = (real_t - keys[1].time) / dfra; + + /* convert velocity to timestep size */ + if(pind->keyed || pind->cached){ + VecMulf(keys[1].vel, dfra / frs_sec); + VecMulf(keys[2].vel, dfra / frs_sec); + QuatInterpol(result->rot,keys[1].rot,keys[2].rot,keytime); + } + + /* 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)*/ + psys_interpolate_particle((pind->keyed || pind->cached) ? -1 /* signal for cubic interpolation */ + : ((psys->part->flag & PART_HAIR_BSPLINE) ? KEY_BSPLINE : KEY_CARDINAL) + ,keys, keytime, result, 1); + + /* the velocity needs to be converted back from cubic interpolation */ + if(pind->keyed || pind->cached) + VecMulf(result->vel, frs_sec / dfra); +} /************************************************/ /* Particles on a dm */ /************************************************/ @@ -1395,16 +1653,6 @@ void psys_particle_on_emitter(ParticleSystemModifierData *psmd, int from, int in /************************************************/ /* Path Cache */ /************************************************/ -static void hair_to_particle(ParticleKey *key, HairKey *hkey) -{ - VECCOPY(key->co, hkey->co); - key->time = hkey->time; -} -static void bp_to_particle(ParticleKey *key, BodyPoint *bp, HairKey *hkey) -{ - VECCOPY(key->co, bp->pos); - key->time = hkey->time; -} static float vert_weight(MDeformVert *dvert, int group) { MDeformWeight *dw; @@ -1691,7 +1939,7 @@ int do_guide(Scene *scene, ParticleKey *state, int pa_num, float time, ListBase } return 0; } -static void do_rough(float *loc, float t, float fac, float size, float thres, ParticleKey *state) +static void do_rough(float *loc, float mat[4][4], float t, float fac, float size, float thres, ParticleKey *state) { float rough[3]; float rco[3]; @@ -1704,32 +1952,24 @@ static void do_rough(float *loc, float t, float fac, float size, float thres, Pa rough[0]=-1.0f+2.0f*BLI_gTurbulence(size, rco[0], rco[1], rco[2], 2,0,2); rough[1]=-1.0f+2.0f*BLI_gTurbulence(size, rco[1], rco[2], rco[0], 2,0,2); rough[2]=-1.0f+2.0f*BLI_gTurbulence(size, rco[2], rco[0], rco[1], 2,0,2); - VECADDFAC(state->co,state->co,rough,fac); + + VECADDFAC(state->co,state->co,mat[0],fac*rough[0]); + VECADDFAC(state->co,state->co,mat[1],fac*rough[1]); + VECADDFAC(state->co,state->co,mat[2],fac*rough[2]); } -static void do_rough_end(float *loc, float t, float fac, float shape, ParticleKey *state, ParticleKey *par) +static void do_rough_end(float *loc, float mat[4][4], float t, float fac, float shape, ParticleKey *state) { - float rough[3], rnor[3]; + float rough[2]; float roughfac; roughfac=fac*(float)pow((double)t,shape); VECCOPY(rough,loc); rough[0]=-1.0f+2.0f*rough[0]; rough[1]=-1.0f+2.0f*rough[1]; - rough[2]=-1.0f+2.0f*rough[2]; VecMulf(rough,roughfac); - - if(par){ - VECCOPY(rnor,par->vel); - } - else{ - VECCOPY(rnor,state->vel); - } - Normalize(rnor); - Projf(rnor,rough,rnor); - VECSUB(rough,rough,rnor); - - VECADD(state->co,state->co,rough); + VECADDFAC(state->co,state->co,mat[0],rough[0]); + VECADDFAC(state->co,state->co,mat[1],rough[1]); } static void do_path_effectors(Scene *scene, Object *ob, ParticleSystem *psys, int i, ParticleCacheKey *ca, int k, int steps, float *rootco, float effector, float dfra, float cfra, float *length, float *vec) { @@ -1964,13 +2204,11 @@ void psys_thread_create_path(ParticleThread *thread, struct ChildParticle *cpa, ParticleData *pa=NULL; ParticleTexture ptex; float *cpa_fuv=0, *par_rot=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, pa_effector; - float max_length = 1.0f, cur_length = 0.0f; + float co[3], orco[3], ornor[3], hairmat[4][4], t, cpa_1st[3], dvec[3]; + float branch_begin, branch_end, branch_prob, rough_rand; + float length, max_length = 1.0f, cur_length = 0.0f; float eff_length, eff_vec[3]; - int k, cpa_num, guided = 0; + int k, cpa_num; short cpa_from; if(part->flag & PART_BRANCHING) { @@ -2024,16 +2262,20 @@ void psys_thread_create_path(ParticleThread *thread, struct ChildParticle *cpa, cpa_num = cpa->num; foffset= cpa->foffset; - if(part->childtype == PART_CHILD_FACES) - foffset = -(2.0f + part->childspread); cpa_fuv = cpa->fuv; cpa_from = PART_FROM_FACE; 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); - Mat4MulVecfl(ob->obmat,cpa_1st); + if(part->path_start==0.0f) { + /* 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); + Mat4MulVecfl(ob->obmat,cpa_1st); + } + + pa = psys->particles + cpa->parent; + + psys_mat_hair_to_global(ob, ctx->psmd->dm, psys->part->from, pa, hairmat); pa=0; } @@ -2056,6 +2298,8 @@ void psys_thread_create_path(ParticleThread *thread, struct ChildParticle *cpa, cpa_fuv=pa->fuv; psys_particle_on_emitter(ctx->psmd,cpa_from,cpa_num,DMCACHE_ISCHILD,cpa_fuv,pa->foffset,co,ornor,0,0,orco,0); + + psys_mat_hair_to_global(ob, ctx->psmd->dm, psys->part->from, pa, hairmat); } keys->steps = ctx->steps; @@ -2070,43 +2314,13 @@ void psys_thread_create_path(ParticleThread *thread, struct ChildParticle *cpa, #endif // XXX old animation system /* get different child parameters from textures & vgroups */ - ptex.length=part->length*(1.0f - part->randlength*cpa->rand[0]); - ptex.clump=1.0; - ptex.kink=1.0; - ptex.rough= 1.0; - ptex.exist= 1.0; - - get_cpa_texture(ctx->dm,ctx->ma,cpa_num,cpa_fuv,orco,&ptex, - MAP_PA_DENS|MAP_PA_LENGTH|MAP_PA_CLUMP|MAP_PA_KINK|MAP_PA_ROUGH); - - pa_length=ptex.length; - pa_clump=ptex.clump; - pa_kink=ptex.kink; - pa_rough1=ptex.rough; - pa_rough2=ptex.rough; - pa_roughe=ptex.rough; - pa_effector= 1.0f; + get_child_modifier_parameters(part, ctx, cpa, cpa_from, cpa_num, cpa_fuv, orco, &ptex); if(ptex.exist < cpa->rand[1]) { keys->steps = -1; return; } - if(ctx->vg_length) - pa_length*=psys_interpolate_value_from_verts(ctx->dm,cpa_from,cpa_num,cpa_fuv,ctx->vg_length); - if(ctx->vg_clump) - pa_clump*=psys_interpolate_value_from_verts(ctx->dm,cpa_from,cpa_num,cpa_fuv,ctx->vg_clump); - if(ctx->vg_kink) - pa_kink*=psys_interpolate_value_from_verts(ctx->dm,cpa_from,cpa_num,cpa_fuv,ctx->vg_kink); - if(ctx->vg_rough1) - pa_rough1*=psys_interpolate_value_from_verts(ctx->dm,cpa_from,cpa_num,cpa_fuv,ctx->vg_rough1); - if(ctx->vg_rough2) - 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++){ if(ctx->between){ @@ -2130,12 +2344,14 @@ void psys_thread_create_path(ParticleThread *thread, struct ChildParticle *cpa, key[w]++; w++; } - if(k==0){ - /* calculate the offset between actual child root position and first position interpolated from parents */ - VECSUB(cpa_1st,cpa_1st,state->co); + if(part->path_start==0.0f) { + if(k==0){ + /* calculate the offset between actual child root position and first position interpolated from parents */ + VECSUB(cpa_1st,cpa_1st,state->co); + } + /* apply offset for correct positioning */ + VECADD(state->co,state->co,cpa_1st); } - /* apply offset for correct positioning */ - VECADD(state->co,state->co,cpa_1st); } else{ /* offset the child from the parent position */ @@ -2149,7 +2365,7 @@ void psys_thread_create_path(ParticleThread *thread, struct ChildParticle *cpa, if(part->flag & PART_CHILD_EFFECT) { for(k=0,state=keys; k<=ctx->steps; k++,state++) { if(k) { - do_path_effectors(ctx->scene, ob, psys, cpa->pa[0], state, k, ctx->steps, keys->co, pa_effector, 0.0f, ctx->cfra, &eff_length, eff_vec); + do_path_effectors(ctx->scene, ob, psys, cpa->pa[0], state, k, ctx->steps, keys->co, ptex.effector, 0.0f, ctx->cfra, &eff_length, eff_vec); } else { VecSubf(eff_vec,(state+1)->co,state->co); @@ -2175,67 +2391,50 @@ void psys_thread_create_path(ParticleThread *thread, struct ChildParticle *cpa, } /* apply different deformations to the child path */ - if(part->flag & PART_CHILD_EFFECT) - /* state is safe to cast, since only co and vel are used */ - guided = do_guide(ctx->scene, (ParticleKey*)state, cpa->parent, t, &(psys->effectors)); - - if(guided==0){ - if(part->kink) - do_prekink((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); - - do_clump((ParticleKey*)state, (ParticleKey*)par, t, part->clumpfac, part->clumppow, pa_clump); - } - - if(part->flag & PART_BRANCHING && ctx->between == 0 && part->flag & PART_ANIM_BRANCHING) - rough_t = t * rough_rand; - else - 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_child_modifiers(ctx->scene, ob, psys, part, &ptex, (ParticleKey *)par, par_rot, cpa, orco, hairmat, (ParticleKey *)state, t); - 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); - - if(part->rough_end != 0.0 && pa_roughe != 0.0) - do_rough_end(cpa->rand, rough_t, pa_roughe*part->rough_end, part->rough_end_shape, (ParticleKey*)state, (ParticleKey*)par); - - if(part->flag & PART_BRANCHING && ctx->between==0){ - if(branch_prob > part->branch_thres){ - branchfac=0.0f; - } - else{ - if(part->flag & PART_SYMM_BRANCHING){ - if(t < branch_begin || t > branch_end) - branchfac=0.0f; - else{ - if((t-branch_begin)/(branch_end-branch_begin)<0.5) - branchfac=2.0f*(t-branch_begin)/(branch_end-branch_begin); - else - branchfac=2.0f*(branch_end-t)/(branch_end-branch_begin); + /* TODO: better branching */ + //if(part->flag & PART_BRANCHING && ctx->between == 0 && part->flag & PART_ANIM_BRANCHING) + // rough_t = t * rough_rand; + //else + // rough_t = t; - CLAMP(branchfac,0.0f,1.0f); - } - } - else{ - if(t < branch_begin){ - branchfac=0.0f; - } - else{ - branchfac=(t-branch_begin)/((1.0f-branch_begin)*0.5f); - CLAMP(branchfac,0.0f,1.0f); - } - } - } + /* TODO: better branching */ + //if(part->flag & PART_BRANCHING && ctx->between==0){ + // if(branch_prob > part->branch_thres){ + // branchfac=0.0f; + // } + // else{ + // if(part->flag & PART_SYMM_BRANCHING){ + // if(t < branch_begin || t > branch_end) + // branchfac=0.0f; + // else{ + // if((t-branch_begin)/(branch_end-branch_begin)<0.5) + // branchfac=2.0f*(t-branch_begin)/(branch_end-branch_begin); + // else + // branchfac=2.0f*(branch_end-t)/(branch_end-branch_begin); + + // CLAMP(branchfac,0.0f,1.0f); + // } + // } + // else{ + // if(t < branch_begin){ + // branchfac=0.0f; + // } + // else{ + // branchfac=(t-branch_begin)/((1.0f-branch_begin)*0.5f); + // CLAMP(branchfac,0.0f,1.0f); + // } + // } + // } - if(i<psys->totpart) - VecLerpf(state->co, (pcache[i] + k)->co, state->co, branchfac); - else - /* this is not threadsafe, but should only happen for - * branching particles particles, which are not threaded */ - VecLerpf(state->co, (cache[i - psys->totpart] + k)->co, state->co, branchfac); - } + // if(i<psys->totpart) + // VecLerpf(state->co, (pcache[i] + k)->co, state->co, branchfac); + // else + // /* this is not threadsafe, but should only happen for + // * branching particles particles, which are not threaded */ + // VecLerpf(state->co, (cache[i - psys->totpart] + k)->co, state->co, branchfac); + //} /* we have to correct velocity because of kink & clump */ if(k>1){ @@ -2249,20 +2448,12 @@ void psys_thread_create_path(ParticleThread *thread, struct ChildParticle *cpa, /* check if path needs to be cut before actual end of data points */ if(k){ VECSUB(dvec,state->co,(state-1)->co); - if(part->flag&PART_ABS_LENGTH) - length=VecLength(dvec); - else - length=1.0f/(float)ctx->steps; - + length=1.0f/(float)ctx->steps; k=check_path_length(k,keys,state,max_length,&cur_length,length,dvec); } else{ /* initialize length calculation */ - if(part->flag&PART_ABS_LENGTH) - max_length= part->abslength*pa_length; - else - max_length= pa_length; - + max_length= ptex.length; cur_length= 0.0f; } @@ -2355,7 +2546,6 @@ void psys_cache_child_paths(Scene *scene, Object *ob, ParticleSystem *psys, floa psys_threads_free(pthreads); } - /* Calculates paths ready for drawing/rendering. */ /* -Usefull for making use of opengl vertex arrays for super fast strand drawing. */ /* -Makes child strands possible and creates them too into the cache. */ @@ -2368,7 +2558,7 @@ void psys_cache_paths(Scene *scene, Object *ob, ParticleSystem *psys, float cfra ParticleSettings *part = psys->part; ParticleData *pa; - ParticleKey keys[4], result, *kkey[2] = {NULL, NULL}; + ParticleKey result, *kkey[2] = {NULL, NULL}; HairKey *hkey[2] = {NULL, NULL}; ParticleEdit *edit = 0; @@ -2378,11 +2568,14 @@ void psys_cache_paths(Scene *scene, Object *ob, ParticleSystem *psys, float cfra BodyPoint *bp[2] = {NULL, NULL}; Material *ma; + + ParticleInterpolationData pind; float birthtime = 0.0, dietime = 0.0; float t, time = 0.0, keytime = 0.0, dfra = 1.0, frs_sec = scene->r.frs_sec; - float col[3] = {0.5f, 0.5f, 0.5f}; + float col[4] = {0.5f, 0.5f, 0.5f, 1.0f}; float prev_tangent[3], hairmat[4][4]; + float rotmat[3][3]; int k,i; int steps = (int)pow(2.0, (double)psys->part->draw_step); int totpart = psys->totpart; @@ -2390,13 +2583,18 @@ void psys_cache_paths(Scene *scene, Object *ob, ParticleSystem *psys, float cfra 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]; + float *vg_length= NULL, pa_length=1.0f; + int keyed, baked; /* 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) + if((psys->flag & PSYS_HAIR_DONE)==0 && (psys->flag & PSYS_KEYED)==0 && (psys->pointcache->flag & PTCACHE_BAKED)==0) return; + BLI_srandom(psys->seed); + + keyed = psys->flag & PSYS_KEYED; + baked = psys->pointcache->flag & PTCACHE_BAKED; + if(psys->renderdata) { steps = (int)pow(2.0, (double)psys->part->ren_step); } @@ -2460,7 +2658,7 @@ void psys_cache_paths(Scene *scene, Object *ob, ParticleSystem *psys, float cfra 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]); + pa_length = 1.0f - part->randlength * 0.5 * (1.0f + pa->r_ave[0]); if(vg_length) pa_length *= psys_particle_value_from_verts(psmd->dm,part->from,pa,vg_length); } @@ -2471,26 +2669,35 @@ void psys_cache_paths(Scene *scene, Object *ob, ParticleSystem *psys, float cfra ekey = edit->keys[i]; /*--get the first data points--*/ - if(psys->flag & PSYS_KEYED) { - kkey[0] = pa->keys; - kkey[1] = kkey[0] + 1; - - birthtime = kkey[0]->time; - dietime = kkey[0][pa->totkey-1].time; - } - else { - hkey[0] = pa->hair; - hkey[1] = hkey[0] + 1; - - birthtime = hkey[0]->time; - dietime = hkey[0][pa->totkey-1].time; + pind.keyed = keyed; + pind.cached = baked; + pind.soft = soft; + init_particle_interpolation(ob, psys, pa, &pind); + + + /* hairmat is needed for for non-hair particle too so we get proper rotations */ + psys_mat_hair_to_global(ob, psmd->dm, psys->part->from, pa, hairmat); + VECCOPY(rotmat[0], hairmat[2]); + VECCOPY(rotmat[1], hairmat[1]); + VECCOPY(rotmat[2], hairmat[0]); + + if(!edit) { + if(part->draw & PART_ABS_PATH_TIME) { + birthtime = MAX2(pind.birthtime, part->path_start); + dietime = MIN2(pind.dietime, part->path_end); + } + else { + float tb = pind.birthtime; + birthtime = tb + part->path_start * (pind.dietime - tb); + dietime = tb + part->path_end * (pind.dietime - tb); + } - psys_mat_hair_to_global(ob, psmd->dm, psys->part->from, pa, hairmat); - } + if(birthtime >= dietime) { + cache[i]->steps = -1; + continue; + } - if(soft){ - bp[0] = soft->bpoint + pa->bpi; - bp[1] = bp[0] + 1; + dietime = birthtime + pa_length * (dietime - birthtime); } /*--interpolate actual path from data points--*/ @@ -2499,85 +2706,12 @@ void psys_cache_paths(Scene *scene, Object *ob, ParticleSystem *psys, float cfra t = birthtime + time * (dietime - birthtime); - if(psys->flag & PSYS_KEYED) { - while(kkey[1]->time < t) { - kkey[1]++; - } - - kkey[0] = kkey[1] - 1; - } - else { - while(hkey[1]->time < t) { - hkey[1]++; - bp[1]++; - } - - hkey[0] = hkey[1] - 1; - } - - if(soft) { - bp[0] = bp[1] - 1; - bp_to_particle(keys + 1, bp[0], hkey[0]); - bp_to_particle(keys + 2, bp[1], hkey[1]); - } - else if(psys->flag & PSYS_KEYED) { - memcpy(keys + 1, kkey[0], sizeof(ParticleKey)); - memcpy(keys + 2, kkey[1], sizeof(ParticleKey)); - } - else { - hair_to_particle(keys + 1, hkey[0]); - hair_to_particle(keys + 2, hkey[1]); - } - - - if((psys->flag & PSYS_KEYED)==0) { - if(soft) { - if(hkey[0] != pa->hair) - bp_to_particle(keys, bp[0] - 1, hkey[0] - 1); - else - bp_to_particle(keys, bp[0], hkey[0]); - } - else { - if(hkey[0] != pa->hair) - hair_to_particle(keys, hkey[0] - 1); - else - hair_to_particle(keys, hkey[0]); - } - - if(soft) { - if(hkey[1] != pa->hair + pa->totkey - 1) - bp_to_particle(keys + 3, bp[1] + 1, hkey[1] + 1); - else - bp_to_particle(keys + 3, bp[1], hkey[1]); - } - else { - if(hkey[1] != pa->hair + pa->totkey - 1) - hair_to_particle(keys + 3, hkey[1] + 1); - else - hair_to_particle(keys + 3, hkey[1]); - } - } - - dfra = keys[2].time - keys[1].time; - - keytime = (t - keys[1].time) / dfra; - - /* convert velocity to timestep size */ - if(psys->flag & PSYS_KEYED){ - VecMulf(keys[1].vel, dfra / frs_sec); - VecMulf(keys[2].vel, dfra / frs_sec); - } + result.time = -t; - /* 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, 0); + do_particle_interpolation(psys, i, pa, t, frs_sec, &pind, &result); - /* the velocity needs to be converted back from cubic interpolation */ - if(psys->flag & PSYS_KEYED){ - VecMulf(result.vel, frs_sec / dfra); - } - else if(soft==NULL) { /* softbody and keyed are allready in global space */ + /* keyed, baked and softbody are allready in global space */ + if(!keyed && !baked && !soft) { Mat4MulVecfl(hairmat, result.co); } @@ -2615,8 +2749,9 @@ void psys_cache_paths(Scene *scene, Object *ob, ParticleSystem *psys, float cfra VECCOPY(ca->col, col); } } + - /*--modify paths--*/ + /*--modify paths and calculate rotation & velocity--*/ VecSubf(vec,(cache[i]+1)->co,cache[i]->co); length = VecLength(vec); @@ -2645,12 +2780,18 @@ void psys_cache_paths(Scene *scene, Object *ob, ParticleSystem *psys, float cfra float cosangle, angle, tangent[3], normal[3], q[4]; if(k == 1) { + /* calculate initial tangent for incremental rotations */ VECSUB(tangent, ca->co, (ca - 1)->co); - - vectoquat(tangent, OB_POSX, OB_POSZ, (ca-1)->rot); - VECCOPY(prev_tangent, tangent); Normalize(prev_tangent); + + /* First rotation is based on emitting face orientation. */ + /* This is way better than having flipping rotations resulting */ + /* from using a global axis as a rotation pole (vec_to_quat()). */ + /* It's not an ideal solution though since it disregards the */ + /* initial tangent, but taking that in to account will allow */ + /* the possibility of flipping again. -jahka */ + Mat3ToQuat_is_ok(rotmat, (ca-1)->rot); } else { VECSUB(tangent, ca->co, (ca - 1)->co); @@ -2689,28 +2830,6 @@ void psys_cache_paths(Scene *scene, Object *ob, ParticleSystem *psys, float cfra } } - - 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 - len=1.0f/(float)steps; - - k=check_path_length(k,cache[i],ca,max_length,&cur_length,len,dvec); - } - else{ - /* initialize length calculation */ - if(part->flag&PART_ABS_LENGTH) - max_length= part->abslength*pa_length; - else - max_length= pa_length; - - cur_length= 0.0f; - } - } } } @@ -2739,15 +2858,6 @@ void copy_particle_key(ParticleKey *to, ParticleKey *from, int time){ memcpy(to,from,sizeof(ParticleKey)); to->time=to_time; } - /* - VECCOPY(to->co,from->co); - VECCOPY(to->vel,from->vel); - QUATCOPY(to->rot,from->rot); - if(time) - to->time=from->time; - to->flag=from->flag; - to->sbw=from->sbw; - */ } void psys_get_from_key(ParticleKey *key, float *loc, float *vel, float *rot, float *time){ if(loc) VECCOPY(loc,key->co); @@ -2901,13 +3011,69 @@ void psys_mat_hair_to_global(Object *ob, DerivedMesh *dm, short from, ParticleDa /************************************************/ /* ParticleSettings handling */ /************************************************/ +void object_add_particle_system(Scene *scene, Object *ob) +{ + ParticleSystem *psys; + ModifierData *md; + ParticleSystemModifierData *psmd; + + if(!ob || ob->type != OB_MESH) + return; + + psys = ob->particlesystem.first; + for(; psys; psys=psys->next) + psys->flag &= ~PSYS_CURRENT; + + psys = MEM_callocN(sizeof(ParticleSystem), "particle_system"); + psys->pointcache = BKE_ptcache_add(); + BLI_addtail(&ob->particlesystem, psys); + + psys->part = psys_new_settings("PSys", NULL); + + md= modifier_new(eModifierType_ParticleSystem); + sprintf(md->name, "ParticleSystem %i", BLI_countlist(&ob->particlesystem)); + psmd= (ParticleSystemModifierData*) md; + psmd->psys=psys; + BLI_addtail(&ob->modifiers, md); + + psys->totpart=0; + psys->flag = PSYS_ENABLED|PSYS_CURRENT; + psys->cfra=bsystem_time(scene,ob,scene->r.cfra+1,0.0); + + DAG_scene_sort(scene); + DAG_object_flush_update(scene, ob, OB_RECALC_DATA); +} +void object_remove_particle_system(Scene *scene, Object *ob) +{ + ParticleSystem *psys = psys_get_current(ob); + ParticleSystemModifierData *psmd; + + if(!psys) + return; + + /* clear modifier */ + psmd= psys_get_modifier(ob, psys); + BLI_remlink(&ob->modifiers, psmd); + modifier_free((ModifierData *)psmd); + + /* clear particle system */ + BLI_remlink(&ob->particlesystem, psys); + psys_free(ob,psys); + + if(ob->particlesystem.first) + ((ParticleSystem *) ob->particlesystem.first)->flag |= PSYS_CURRENT; + + DAG_scene_sort(scene); + DAG_object_flush_update(scene, ob, OB_RECALC_DATA); +} static void default_particle_settings(ParticleSettings *part) { int i; part->type= PART_EMITTER; part->distr= PART_DISTR_JIT; - part->draw_as=PART_DRAW_DOT; + part->draw_as = PART_DRAW_REND; + part->ren_as = PART_DRAW_HALO; part->bb_uv_split=1; part->bb_align=PART_BB_VIEW; part->bb_split_offset=PART_BB_OFF_LINEAR; @@ -2920,8 +3086,6 @@ static void default_particle_settings(ParticleSettings *part) part->totpart= 1000; part->grid_res= 10; part->timetweak= 1.0; - part->keyed_time= 0.5; - //part->userjit; part->integrator= PART_INT_MIDPOINT; part->phystype= PART_PHYS_NEWTON; @@ -2935,7 +3099,6 @@ static void default_particle_settings(ParticleSettings *part) part->reactevent= PART_EVENT_DEATH; part->disp=100; part->from= PART_FROM_FACE; - part->length= 1.0; part->nbetween= 4; part->boidneighbours= 5; @@ -2962,7 +3125,14 @@ static void default_particle_settings(ParticleSettings *part) part->rough2_size=1.0; part->rough_end_shape=1.0; + part->clength=1.0f; + part->clength_thres=0.0f; + part->draw_line[0]=0.5; + part->path_start = 0.0f; + part->path_end = 1.0f; + + part->keyed_loops = 1; part->banking=1.0; part->max_bank=1.0; @@ -2987,6 +3157,9 @@ ParticleSettings *psys_new_settings(char *name, Main *main) { ParticleSettings *part; + if(main==NULL) + main = G.main; + part= alloc_libblock(&main->particle, ID_PA, name); default_particle_settings(part); @@ -3062,7 +3235,6 @@ void make_local_particlesettings(ParticleSettings *part) } } } - void psys_flush_particle_settings(Scene *scene, ParticleSettings *part, int recalc) { Base *base = scene->base.first; @@ -3169,7 +3341,7 @@ 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){ + if(mtex && (ma->septex & (1<<m))==0 && mtex->pmapto){ float def=mtex->def_var; float var=mtex->varfac; short blend=mtex->blendtype; @@ -3197,7 +3369,7 @@ static void get_cpa_texture(DerivedMesh *dm, Material *ma, int face_index, float if((event & mtex->pmapto) & 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(def,ptex->rough,value,var,blend,neg & MAP_PA_ROUGH); + ptex->rough1= ptex->rough2= ptex->roughe= texture_value_blend(def,ptex->rough1,value,var,blend,neg & MAP_PA_ROUGH); if((event & mtex->pmapto) & MAP_PA_DENS) ptex->exist= texture_value_blend(def,ptex->exist,value,var,blend,neg & MAP_PA_DENS); } @@ -3206,7 +3378,11 @@ static void get_cpa_texture(DerivedMesh *dm, Material *ma, int face_index, float if(event & MAP_PA_LENGTH) { CLAMP(ptex->length,0.0,1.0); } if(event & MAP_PA_CLUMP) { CLAMP(ptex->clump,0.0,1.0); } if(event & MAP_PA_KINK) { CLAMP(ptex->kink,0.0,1.0); } - if(event & MAP_PA_ROUGH) { CLAMP(ptex->rough,0.0,1.0); } + if(event & MAP_PA_ROUGH) { + CLAMP(ptex->rough1,0.0,1.0); + CLAMP(ptex->rough2,0.0,1.0); + CLAMP(ptex->roughe,0.0,1.0); + } if(event & MAP_PA_DENS) { CLAMP(ptex->exist,0.0,1.0); } } void psys_get_texture(Object *ob, Material *ma, ParticleSystemModifierData *psmd, ParticleSystem *psys, ParticleData *pa, ParticleTexture *ptex, int event) @@ -3218,7 +3394,7 @@ void psys_get_texture(Object *ob, Material *ma, ParticleSystemModifierData *psmd if(ma) for(m=0; m<MAX_MTEX; m++){ mtex=ma->mtex[m]; - if(mtex && (ma->septex & (1<<m))==0){ + if(mtex && (ma->septex & (1<<m))==0 && mtex->pmapto){ float var=mtex->varfac; float def=mtex->def_var; short blend=mtex->blendtype; @@ -3307,12 +3483,12 @@ float psys_get_size(Object *ob, Material *ma, ParticleSystemModifierData *psmd, return size*part->size; } -float psys_get_child_time(ParticleSystem *psys, ChildParticle *cpa, float cfra) +float psys_get_child_time(ParticleSystem *psys, ChildParticle *cpa, float cfra, float *birthtime, float *dietime) { ParticleSettings *part = psys->part; + float time, life; if(part->childtype==PART_CHILD_FACES){ - float time; int w=0; time=0.0; while(w<4 && cpa->pa[w]>=0){ @@ -3320,12 +3496,21 @@ float psys_get_child_time(ParticleSystem *psys, ChildParticle *cpa, float cfra) w++; } - return (cfra-time)/(part->lifetime*(1.0f-part->randlife*cpa->rand[1])); + life = part->lifetime*(1.0f-part->randlife*cpa->rand[1]); } else{ ParticleData *pa = psys->particles + cpa->parent; - return (cfra-pa->time)/pa->lifetime; + + time = pa->time; + life = pa->lifetime; } + + if(birthtime) + *birthtime = time; + if(dietime) + *dietime = time+life; + + return (cfra-time)/life; } float psys_get_child_size(ParticleSystem *psys, ChildParticle *cpa, float cfra, float *pa_time) { @@ -3342,7 +3527,7 @@ float psys_get_child_size(ParticleSystem *psys, ChildParticle *cpa, float cfra, if(pa_time) time=*pa_time; else - time=psys_get_child_time(psys,cpa,cfra); + time=psys_get_child_time(psys,cpa,cfra,NULL,NULL); /* correction for lifetime */ calc_ipo(part->ipo, 100*time); @@ -3364,6 +3549,66 @@ float psys_get_child_size(ParticleSystem *psys, ChildParticle *cpa, float cfra, return size; } +static void get_child_modifier_parameters(ParticleSettings *part, ParticleThreadContext *ctx, ChildParticle *cpa, short cpa_from, int cpa_num, float *cpa_fuv, float *orco, ParticleTexture *ptex) +{ + ptex->length= 1.0f - part->randlength*cpa->rand[0]; + ptex->clump=1.0; + ptex->kink=1.0; + ptex->rough1= 1.0; + ptex->rough2= 1.0; + ptex->roughe= 1.0; + ptex->exist= 1.0; + ptex->effector= 1.0; + + ptex->length*= part->clength_thres < cpa->rand[1] ? part->clength : 1.0f; + + get_cpa_texture(ctx->dm,ctx->ma,cpa_num,cpa_fuv,orco,ptex, + MAP_PA_DENS|MAP_PA_LENGTH|MAP_PA_CLUMP|MAP_PA_KINK|MAP_PA_ROUGH); + + + if(ptex->exist < cpa->rand[1]) + return; + + if(ctx->vg_length) + ptex->length*=psys_interpolate_value_from_verts(ctx->dm,cpa_from,cpa_num,cpa_fuv,ctx->vg_length); + if(ctx->vg_clump) + ptex->clump*=psys_interpolate_value_from_verts(ctx->dm,cpa_from,cpa_num,cpa_fuv,ctx->vg_clump); + if(ctx->vg_kink) + ptex->kink*=psys_interpolate_value_from_verts(ctx->dm,cpa_from,cpa_num,cpa_fuv,ctx->vg_kink); + if(ctx->vg_rough1) + ptex->rough1*=psys_interpolate_value_from_verts(ctx->dm,cpa_from,cpa_num,cpa_fuv,ctx->vg_rough1); + if(ctx->vg_rough2) + ptex->rough2*=psys_interpolate_value_from_verts(ctx->dm,cpa_from,cpa_num,cpa_fuv,ctx->vg_rough2); + if(ctx->vg_roughe) + ptex->roughe*=psys_interpolate_value_from_verts(ctx->dm,cpa_from,cpa_num,cpa_fuv,ctx->vg_roughe); + if(ctx->vg_effector) + ptex->effector*=psys_interpolate_value_from_verts(ctx->dm,cpa_from,cpa_num,cpa_fuv,ctx->vg_effector); +} +static void do_child_modifiers(Scene *scene, Object *ob, ParticleSystem *psys, ParticleSettings *part, ParticleTexture *ptex, ParticleKey *par, float *par_rot, ChildParticle *cpa, float *orco, float mat[4][4], ParticleKey *state, float t) +{ + int guided = 0; + + if(part->flag & PART_CHILD_EFFECT) + /* state is safe to cast, since only co and vel are used */ + guided = do_guide(scene, (ParticleKey*)state, cpa->parent, t, &(psys->effectors)); + + if(guided==0){ + if(part->kink) + do_prekink(state, par, par_rot, t, part->kink_freq * ptex->kink, part->kink_shape, + part->kink_amp, part->kink, part->kink_axis, ob->obmat); + + do_clump(state, par, t, part->clumpfac, part->clumppow, ptex->clump); + } + + if(part->rough1 != 0.0 && ptex->rough1 != 0.0) + do_rough(orco, mat, t, ptex->rough1*part->rough1, part->rough1_size, 0.0, state); + + if(part->rough2 != 0.0 && ptex->rough2 != 0.0) + do_rough(cpa->rand, mat, t, ptex->rough2*part->rough2, part->rough2_size, part->rough2_thres, state); + + if(part->rough_end != 0.0 && ptex->roughe != 0.0) + do_rough_end(cpa->rand, mat, t, ptex->roughe*part->rough_end, part->rough_end_shape, state); +} /* get's hair (or keyed) particles state at the "path time" specified in state->time */ void psys_get_particle_on_path(Scene *scene, Object *ob, ParticleSystem *psys, int p, ParticleKey *state, int vel) { @@ -3375,17 +3620,21 @@ void psys_get_particle_on_path(Scene *scene, Object *ob, ParticleSystem *psys, i ParticleTexture ptex; ParticleKey *kkey[2] = {NULL, NULL}; HairKey *hkey[2] = {NULL, NULL}; - ParticleKey *par=0, keys[4]; + ParticleKey *par=0, keys[4], tstate; + ParticleThreadContext ctx; /* fake thread context for child modifiers */ + ParticleInterpolationData pind; - float t, real_t, dfra, keytime, frs_sec = scene->r.frs_sec; + float t, frs_sec = scene->r.frs_sec; float co[3], orco[3]; float hairmat[4][4]; - float pa_clump = 0.0, pa_kink = 0.0; int totparent = 0; int totpart = psys->totpart; int totchild = psys->totchild; short between = 0, edit = 0; + int keyed = part->phystype & PART_PHYS_KEYED && psys->flag & PSYS_KEYED; + int cached = !keyed && part->type != PART_HAIR; + float *cpa_fuv; int cpa_num; short cpa_from; //if(psys_in_edit_mode(scene, psys)){ @@ -3394,12 +3643,6 @@ void psys_get_particle_on_path(Scene *scene, Object *ob, ParticleSystem *psys, i // edit=1; //} - /* user want's cubic interpolation but only without sb it possible */ - //if(interpolation==PART_INTER_CUBIC && baked && psys->softflag==OB_SB_ENABLE) - // interpolation=PART_INTER_BSPLINE; - //else if(baked==0) /* it doesn't make sense to use other types for keyed */ - // interpolation=PART_INTER_CUBIC; - t=state->time; CLAMP(t, 0.0, 1.0); @@ -3411,99 +3654,14 @@ void psys_get_particle_on_path(Scene *scene, Object *ob, ParticleSystem *psys, i key_from_object(pa->stick_ob,state); return; } - - 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; - } - - if(psys->flag & PSYS_KEYED) { - while(kkey[1]->time < real_t) { - kkey[1]++; - } - kkey[0] = kkey[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]++; - - 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); - // else - // DB_copy_key(&k1,&k2); - //} - //else{ - if(hkey[0] != pa->hair) - hair_to_particle(keys, hkey[0] - 1); - else - hair_to_particle(keys, hkey[0]); - //} - - //if(soft){ - // if(key[1] != sbel.keys + sbel.totkey-1) - // DB_copy_key(&k4,key[1]+1); - // else - // DB_copy_key(&k4,&k3); - //} - //else { - if(hkey[1] != pa->hair + pa->totkey - 1) - hair_to_particle(keys + 3, hkey[1] + 1); - else - hair_to_particle(keys + 3, hkey[1]); - } - //} - //psys_get_particle_on_path(scene, bsys,p,t,bkey,ckey[0]); + pind.keyed = keyed; + pind.cached = cached; + pind.soft = NULL; + init_particle_interpolation(ob, psys, pa, &pind); + do_particle_interpolation(psys, p, pa, t, frs_sec, &pind, state); - //if(part->rotfrom==PART_ROT_KEYS) - // QuatInterpol(state->rot,k2.rot,k3.rot,keytime); - //else{ - // /* TODO: different rotations */ - // float nvel[3]; - // VECCOPY(nvel,state->vel); - // VecMulf(nvel,-1.0f); - // vectoquat(nvel, OB_POSX, OB_POSZ, state->rot); - //} - - dfra = keys[2].time - keys[1].time; - - keytime = (real_t - keys[1].time) / dfra; - - /* 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); - } - - 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); - - /* the velocity needs to be converted back from cubic interpolation */ - if(psys->flag & PSYS_KEYED){ - VecMulf(state->vel, frs_sec / dfra); - } - else { + if(!keyed && !cached) { if((pa->flag & PARS_REKEY)==0) { psys_mat_hair_to_global(ob, psmd->dm, part->from, pa, hairmat); Mat4MulVecfl(hairmat, state->co); @@ -3521,8 +3679,11 @@ void psys_get_particle_on_path(Scene *scene, Object *ob, ParticleSystem *psys, i } else if(totchild){ //Mat4Invert(imat,ob->obmat); - + cpa=psys->child+p-totpart; + + if(state->time < 0.0f) + t = psys_get_child_time(psys, cpa, -state->time, NULL, NULL); if(totchild && part->from!=PART_FROM_PARTICLE && part->childtype==PART_CHILD_FACES){ totparent=(int)(totchild*part->parents*0.3); @@ -3539,7 +3700,7 @@ void psys_get_particle_on_path(Scene *scene, Object *ob, ParticleSystem *psys, i /* get parent states */ while(w<4 && cpa->pa[w]>=0){ - keys[w].time = t; + keys[w].time = state->time; psys_get_particle_on_path(scene, ob, psys, cpa->pa[w], keys+w, 1); w++; } @@ -3548,8 +3709,6 @@ void psys_get_particle_on_path(Scene *scene, Object *ob, ParticleSystem *psys, i cpa_num=cpa->num; foffset= cpa->foffset; - if(part->childtype == PART_CHILD_FACES) - foffset = -(2.0f + part->childspread); cpa_fuv = cpa->fuv; cpa_from = PART_FROM_FACE; @@ -3560,12 +3719,15 @@ void psys_get_particle_on_path(Scene *scene, Object *ob, ParticleSystem *psys, i //Mat4MulVecfl(ob->obmat,cpa_1st); + pa = psys->particles + cpa->parent; + + psys_mat_hair_to_global(ob, psmd->dm, psys->part->from, pa, hairmat); + pa=0; } else{ /* get the parent state */ - - keys->time = t; + keys->time = state->time; psys_get_particle_on_path(scene, ob, psys, cpa->parent, keys,1); /* get the original coordinates (orco) for texture usage */ @@ -3576,6 +3738,8 @@ void psys_get_particle_on_path(Scene *scene, Object *ob, ParticleSystem *psys, i cpa_fuv=pa->fuv; psys_particle_on_emitter(psmd,cpa_from,cpa_num,DMCACHE_ISCHILD,cpa_fuv,pa->foffset,co,0,0,0,orco,0); + + psys_mat_hair_to_global(ob, psmd->dm, psys->part->from, pa, hairmat); } /* correct child ipo timing */ @@ -3587,15 +3751,11 @@ void psys_get_particle_on_path(Scene *scene, Object *ob, ParticleSystem *psys, i #endif // XXX old animation system /* get different child parameters from textures & vgroups */ - ptex.clump=1.0; - ptex.kink=1.0; - - get_cpa_texture(psmd->dm,ma,cpa_num,cpa_fuv,orco,&ptex,MAP_PA_CLUMP|MAP_PA_KINK); - - pa_clump=ptex.clump; - pa_kink=ptex.kink; - - /* TODO: vertex groups */ + memset(&ctx, 0, sizeof(ParticleThreadContext)); + ctx.dm = psmd->dm; + ctx.ma = ma; + /* TODO: assign vertex groups */ + get_child_modifier_parameters(part, &ctx, cpa, cpa_from, cpa_num, cpa_fuv, orco, &ptex); if(between){ int w=0; @@ -3623,46 +3783,33 @@ void psys_get_particle_on_path(Scene *scene, Object *ob, ParticleSystem *psys, i } par = keys; - //if(totparent){ - // if(p-totpart>=totparent){ - // key.time=t; - // psys_get_particle_on_path(ob,psys,totpart+cpa->parent,&key,1); - // bti->convert_dynamic_key(bsys,&key,par,cpar); - // } - // else - // par=0; - //} - //else - // DB_get_key_on_path(bsys,cpa->parent,t,par,cpar); - /* apply different deformations to the child path */ - if(part->kink) - do_prekink(state, par, par->rot, t, part->kink_freq * pa_kink, part->kink_shape, - part->kink_amp, part->kink, part->kink_axis, ob->obmat); - - do_clump(state, par, t, part->clumpfac, part->clumppow, 1.0f); - - if(part->rough1 != 0.0) - 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); + if(vel) + copy_particle_key(&tstate, state, 1); - if(part->rough_end != 0.0) - do_rough_end(cpa->rand, t, part->rough_end, part->rough_end_shape, state, par); + /* apply different deformations to the child path */ + do_child_modifiers(scene, ob, psys, part, &ptex, par, par->rot, cpa, orco, hairmat, state, t); + + /* try to estimate correct velocity */ + if(vel){ + ParticleKey tstate; + float length = VecLength(state->vel); + + if(t>=0.001f){ + tstate.time=t-0.001f; + psys_get_particle_on_path(scene,ob,psys,p,&tstate,0); + VECSUB(state->vel,state->co,tstate.co); + Normalize(state->vel); + } + else{ + tstate.time=t+0.001f; + psys_get_particle_on_path(scene, ob,psys,p,&tstate,0); + VECSUB(state->vel,tstate.co,state->co); + Normalize(state->vel); + } - //if(vel){ - // if(t>=0.001f){ - // tstate.time=t-0.001f; - // psys_get_particle_on_path(scene,ob,psys,p,&tstate,0); - // VECSUB(state->vel,state->co,tstate.co); - // } - // else{ - // tstate.time=t+0.001f; - // psys_get_particle_on_path(scene, ob,psys,p,&tstate,0); - // VECSUB(state->vel,tstate.co,state->co); - // } - //} + VecMulf(state->vel, length); + } } } /* gets particle's state at a time, returns 1 if particle exists and can be seen and 0 if not */ @@ -3689,7 +3836,7 @@ int psys_get_particle_state(struct Scene *scene, Object *ob, ParticleSystem *psy pa=psys->particles+p; if(between){ - state->time = psys_get_child_time(psys,&psys->child[p-totpart],cfra); + state->time = psys_get_child_time(psys,&psys->child[p-totpart],cfra,NULL,NULL); if(always==0) if((state->time<0.0 && (part->flag & PART_UNBORN)==0) @@ -3702,6 +3849,8 @@ int psys_get_particle_state(struct Scene *scene, Object *ob, ParticleSystem *psy if((pa->alive==PARS_UNBORN && (part->flag & PART_UNBORN)==0) || (pa->alive==PARS_DEAD && (part->flag & PART_DIED)==0)) return 0; + + state->time = MIN2(state->time, pa->dietime); } if(psys->flag & PSYS_KEYED){ @@ -3710,7 +3859,7 @@ int psys_get_particle_state(struct Scene *scene, Object *ob, ParticleSystem *psy 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); + state->time= -cfra; psys_get_particle_on_path(scene, ob, psys, p, state,1); return 1; @@ -3747,57 +3896,55 @@ int psys_get_particle_state(struct Scene *scene, Object *ob, ParticleSystem *psy calc_latt_deform(psys->lattice, state->co,1.0f); } else{ - if (pa) { /* TODO PARTICLE - should this ever be NULL? - Campbell */ - 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 = scene->r.frs_sec; + 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 = 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); + 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); + 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; + dfra = keys[2].time - keys[1].time; - keytime = (state->time - keys[1].time) / dfra; + 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); + /* convert velocity to timestep size */ + VecMulf(keys[1].vel, dfra / frs_sec); + VecMulf(keys[2].vel, dfra / frs_sec); + + psys_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); + VecLerpf(state->ave, keys[1].ave, keys[2].ave, keytime); + QuatInterpol(state->rot, keys[1].rot, keys[2].rot, keytime); } } - - if(pa->alive==PARS_DEAD && part->flag&PART_STICKY && pa->flag&PARS_STICKY && pa->stick_ob){ - key_from_object(pa->stick_ob,state); + 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(psys->lattice) - calc_latt_deform(psys->lattice, state->co,1.0f); + if(pa->alive==PARS_DEAD && part->flag&PART_STICKY && pa->flag&PARS_STICKY && pa->stick_ob){ + key_from_object(pa->stick_ob,state); } + + if(psys->lattice) + calc_latt_deform(psys->lattice, state->co,1.0f); } return 1; diff --git a/source/blender/blenkernel/intern/particle_system.c b/source/blender/blenkernel/intern/particle_system.c index c365f449801..9004f4b9973 100644 --- a/source/blender/blenkernel/intern/particle_system.c +++ b/source/blender/blenkernel/intern/particle_system.c @@ -29,6 +29,8 @@ * ***** END GPL LICENSE BLOCK ***** */ +#include "BLI_storage.h" /* _LARGEFILE_SOURCE */ + #include <stdlib.h> #include <math.h> #include <string.h> @@ -104,14 +106,12 @@ static int get_current_display_percentage(ParticleSystem *psys) { ParticleSettings *part=psys->part; - if(psys->renderdata || (part->child_nbr && part->childtype)) + if(psys->renderdata || (part->child_nbr && part->childtype) + || (psys->pointcache->flag & PTCACHE_BAKING)) return 100; if(part->phystype==PART_PHYS_KEYED){ - if(psys->flag & PSYS_FIRST_KEYED) - return psys->part->disp; - else - return 100; + return psys->part->disp; } else return psys->part->disp; @@ -196,7 +196,7 @@ static void realloc_particles(Object *ob, ParticleSystem *psys, int new_totpart) if(psys->particles->keys) MEM_freeN(psys->particles->keys); - for(i=0, pa=psys->particles; i<psys->totpart; i++, pa++) + for(i=0, pa=newpars; i<totsaved; i++, pa++) if(pa->keys) { pa->keys= NULL; pa->totkey= 0; @@ -277,7 +277,7 @@ void psys_calc_dmcache(Object *ob, DerivedMesh *dm, ParticleSystem *psys) origindex= DM_get_vert_data_layer(dm, CD_ORIGINDEX); } else { /* FROM_FACE/FROM_VOLUME */ - totdmelem= dm->getNumTessFaces(dm); + totdmelem= dm->getNumFaces(dm); totelem= me->totface; origindex= DM_get_face_data_layer(dm, CD_ORIGINDEX); } @@ -409,7 +409,7 @@ static void distribute_particles_in_grid(DerivedMesh *dm, ParticleSystem *psys) int a, a1, a2, a0mul, a1mul, a2mul, totface; int amax= from==PART_FROM_FACE ? 3 : 1; - totface=dm->getNumTessFaces(dm); + totface=dm->getNumFaces(dm); mface=dm->getTessFaceDataArray(dm,CD_MFACE); for(a=0; a<amax; a++){ @@ -660,7 +660,7 @@ void psys_thread_distribute_particle(ParticleThread *thread, ParticleData *pa, C if(from==PART_FROM_VOLUME){ MVert *mvert=dm->getVertDataArray(dm,CD_MVERT); - tot=dm->getNumTessFaces(dm); + tot=dm->getNumFaces(dm); psys_interpolate_face(mvert,mface,0,0,pa->fuv,co1,nor,0,0,0,0); @@ -1105,7 +1105,7 @@ int psys_threads_init_distribution(ParticleThread *threads, Scene *scene, Derive break; case PART_FROM_VOLUME: case PART_FROM_FACE: - tot = dm->getNumTessFaces(dm); + tot = dm->getNumFaces(dm); break; case PART_FROM_PARTICLE: if(psys->target_ob) @@ -1300,9 +1300,23 @@ int psys_threads_init_distribution(ParticleThread *threads, Scene *scene, Derive /* for hair, sort by origindex, allows optimizations in rendering */ /* however with virtual parents the children need to be in random order */ if(part->type == PART_HAIR && !(part->childtype==PART_CHILD_FACES && part->parents!=0.0)) { - COMPARE_ORIG_INDEX= dm->getTessFaceDataArray(dm, CD_ORIGINDEX); - if(COMPARE_ORIG_INDEX) - qsort(index, totpart, sizeof(int), compare_orig_index); + if(from != PART_FROM_PARTICLE) { + COMPARE_ORIG_INDEX = NULL; + + if(from == PART_FROM_VERT) { + if(dm->numVertData) + COMPARE_ORIG_INDEX= dm->getVertDataArray(dm, CD_ORIGINDEX); + } + else { + if(dm->numFaceData) + COMPARE_ORIG_INDEX= dm->getTessFaceDataArray(dm, CD_ORIGINDEX); + } + + if(COMPARE_ORIG_INDEX) { + qsort(index, totpart, sizeof(int), compare_orig_index); + COMPARE_ORIG_INDEX = NULL; + } + } } /* weights are no longer used except for FROM_PARTICLE, which needs them zeroed for indexing */ @@ -1747,7 +1761,10 @@ void reset_particle(Scene *scene, ParticleData *pa, ParticleSystem *psys, Partic where_is_object_time(scene, ob,pa->time); /* get birth location from object */ - psys_particle_on_emitter(psmd,part->from,pa->num, pa->num_dmcache, pa->fuv,pa->foffset,loc,nor,utan,vtan,0,0); + if(part->tanfac!=0.0) + psys_particle_on_emitter(psmd,part->from,pa->num, pa->num_dmcache, pa->fuv,pa->foffset,loc,nor,utan,vtan,0,0); + else + psys_particle_on_emitter(psmd,part->from,pa->num, pa->num_dmcache, pa->fuv,pa->foffset,loc,nor,0,0,0,0); /* save local coordinates for later */ VECCOPY(tloc,loc); @@ -1956,64 +1973,59 @@ static void reset_all_particles(Scene *scene, Object *ob, ParticleSystem *psys, /************************************************/ /* Keyed particles */ /************************************************/ -/* a bit of an unintuitive function :) counts objects in a keyed chain and returns 1 if some of them were selected (used in drawing) */ -int psys_count_keyed_targets(Object *ob, ParticleSystem *psys) +/* Counts valid keyed targets */ +void psys_count_keyed_targets(Object *ob, ParticleSystem *psys) { - ParticleSystem *kpsys=psys,*tpsys; - ParticleSettings *tpart; - Object *kob=ob,*tob; - int select=ob->flag&SELECT; - short totkeyed=0; - Base *base; - - ListBase lb; - lb.first=lb.last=0; - - tob=psys->keyed_ob; - while(tob){ - if((tpsys=BLI_findlink(&tob->particlesystem,kpsys->keyed_psys-1))){ - tpart=tpsys->part; - - if(tpart->phystype==PART_PHYS_KEYED){ - if(lb.first){ - for(base=lb.first;base;base=base->next){ - if(tob==base->object){ - fprintf(stderr,"Error: loop in keyed chain!\n"); - BLI_freelistN(&lb); - return select; - } - } - } - base=MEM_callocN(sizeof(Base), "keyed base"); - base->object=tob; - BLI_addtail(&lb,base); - - if(tob->flag&SELECT) - select++; - kob=tob; - kpsys=tpsys; - tob=tpsys->keyed_ob; - totkeyed++; + ParticleSystem *kpsys; + KeyedParticleTarget *kpt = psys->keyed_targets.first; + int psys_num = BLI_findindex(&ob->particlesystem, psys); + int keys_valid = 1; + psys->totkeyed = 0; + + for(; kpt; kpt=kpt->next) { + kpsys = NULL; + if(kpt->ob==ob || kpt->ob==NULL) { + if(kpt->psys >= psys_num) + kpsys = BLI_findlink(&ob->particlesystem, kpt->psys-1); + + if(kpsys && kpsys->totpart) { + kpt->flag |= KEYED_TARGET_VALID; + psys->totkeyed += keys_valid; + if(psys->flag & PSYS_KEYED_TIMING && kpt->duration != 0.0f) + psys->totkeyed += 1; } - else{ - tob=0; - totkeyed++; + else { + kpt->flag &= ~KEYED_TARGET_VALID; + keys_valid = 0; + } + } + else { + if(kpt->ob) + kpsys = BLI_findlink(&kpt->ob->particlesystem, kpt->psys-1); + + if(kpsys && kpsys->totpart) { + kpt->flag |= KEYED_TARGET_VALID; + psys->totkeyed += keys_valid; + if(psys->flag & PSYS_KEYED_TIMING && kpt->duration != 0.0f) + psys->totkeyed += 1; + } + else { + kpt->flag &= ~KEYED_TARGET_VALID; + keys_valid = 0; } } - else - tob=0; } - psys->totkeyed=totkeyed; - BLI_freelistN(&lb); - return select; + + psys->totkeyed *= psys->flag & PSYS_KEYED_TIMING ? 1 : psys->part->keyed_loops; } static void set_keyed_keys(Scene *scene, Object *ob, ParticleSystem *psys) { Object *kob = ob; ParticleSystem *kpsys = psys; + KeyedParticleTarget *kpt; ParticleData *pa; - int totpart = psys->totpart, i, k, totkeys = psys->totkeyed + 1; + int totpart = psys->totpart, i, k, totkeys = psys->totkeyed; float prevtime, nexttime, keyedtime; /* no proper targets so let's clear and bail out */ @@ -2026,7 +2038,7 @@ static void set_keyed_keys(Scene *scene, Object *ob, ParticleSystem *psys) 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(totpart*totkeys*sizeof(ParticleKey), "Keyed keys"); psys->particles->totkey = totkeys; for(i=1, pa=psys->particles+1; i<totpart; i++,pa++){ @@ -2037,32 +2049,36 @@ static void set_keyed_keys(Scene *scene, Object *ob, ParticleSystem *psys) psys->flag &= ~PSYS_KEYED; + + kpt = psys->keyed_targets.first; for(k=0; k<totkeys; k++) { + if(kpt->ob) + kpsys = BLI_findlink(&kpt->ob->particlesystem, kpt->psys - 1); + else + kpsys = BLI_findlink(&ob->particlesystem, kpt->psys - 1); + for(i=0,pa=psys->particles; i<totpart; i++, pa++) { (pa->keys + k)->time = -1.0; /* use current time */ - if(kpsys->totpart > 0) - psys_get_particle_state(scene, kob, kpsys, i%kpsys->totpart, pa->keys + k, 1); + psys_get_particle_state(scene, kpt->ob, kpsys, i%kpsys->totpart, pa->keys + k, 1); - if(k==0) - pa->keys->time = pa->time; - else if(k==totkeys-1) - (pa->keys + k)->time = pa->time + pa->lifetime; - else{ - if(psys->flag & PSYS_KEYED_TIME){ - prevtime = (pa->keys + k - 1)->time; - nexttime = pa->time + pa->lifetime; - keyedtime = kpsys->part->keyed_time; - (pa->keys + k)->time = (1.0f - keyedtime) * prevtime + keyedtime * nexttime; + if(psys->flag & PSYS_KEYED_TIMING){ + (pa->keys+k)->time = pa->time + kpt->time; + if(kpt->duration != 0.0f && k+1 < totkeys) { + copy_particle_key(pa->keys+k+1, pa->keys+k, 1); + (pa->keys+k+1)->time = pa->time + kpt->time + kpt->duration; } - else - (pa->keys+k)->time = pa->time + (float)k / (float)(totkeys - 1) * pa->lifetime; } + else if(totkeys > 1) + (pa->keys+k)->time = pa->time + (float)k / (float)(totkeys - 1) * pa->lifetime; + else + pa->keys->time = pa->time; } - if(kpsys->keyed_ob){ - kob = kpsys->keyed_ob; - kpsys = BLI_findlink(&kob->particlesystem, kpsys->keyed_psys - 1); - } + + if(psys->flag & PSYS_KEYED_TIMING && kpt->duration!=0.0f) + k++; + + kpt = (kpt->next && kpt->next->flag & KEYED_TARGET_VALID) ? kpt = kpt->next : psys->keyed_targets.first; } psys->flag |= PSYS_KEYED; @@ -2178,57 +2194,147 @@ void psys_get_reactor_target(Object *ob, ParticleSystem *psys, Object **target_o /************************************************/ /* Point Cache */ /************************************************/ - -static void write_particles_to_cache(Object *ob, ParticleSystem *psys, int cfra) +void psys_make_temp_pointcache(Object *ob, ParticleSystem *psys) { + PointCache *cache = psys->pointcache; + PTCacheFile *pf = NULL; + PTCacheMem *pm = NULL; PTCacheID pid; - PTCacheFile *pf; - ParticleData *pa; - int i, totpart= psys->totpart; + int cfra, sfra = cache->startframe, efra = cache->endframe; + int totelem = psys->totpart; + int float_count = sizeof(ParticleKey) / sizeof(float); + int tot = totelem * float_count; - if(totpart == 0) + if((cache->flag & PTCACHE_DISK_CACHE)==0 || cache->mem_cache.first) return; BKE_ptcache_id_from_particles(&pid, ob, psys); - pf= BKE_ptcache_file_open(&pid, PTCACHE_FILE_WRITE, cfra); - if(!pf) + + for(cfra=sfra; cfra <= efra; cfra++) { + pf = BKE_ptcache_file_open(&pid, PTCACHE_FILE_READ, cfra); + + if(pf) { + pm = MEM_callocN(sizeof(PTCacheMem), "Pointcache temp mem"); + pm->data = MEM_callocN(sizeof(float)*tot, "Pointcache temp mem data"); + + if(fread(pm->data, sizeof(float), tot, pf->fp)!= tot) { + printf("Error reading from disk cache\n"); + + MEM_freeN(pm->data); + MEM_freeN(pm); + BKE_ptcache_file_close(pf); + return; + } + + pm->frame = cfra; + pm->totpoint = totelem; + + BLI_addtail(&cache->mem_cache, pm); + + BKE_ptcache_file_close(pf); + } + } +} +void psys_clear_temp_pointcache(ParticleSystem *psys) +{ + PTCacheMem *pm = psys->pointcache->mem_cache.first; + + if((psys->pointcache->flag & PTCACHE_DISK_CACHE)==0) return; - /* assuming struct consists of tightly packed floats */ - for(i=0, pa=psys->particles; i<totpart; i++, pa++) - BKE_ptcache_file_write_floats(pf, (float*)&pa->state, sizeof(ParticleKey)/sizeof(float)); + for(; pm; pm=pm->next) { + MEM_freeN(pm->data); + } + + BLI_freelistN(&psys->pointcache->mem_cache); +} +void psys_get_pointcache_start_end(Scene *scene, ParticleSystem *psys, int *sfra, int *efra) +{ + ParticleSettings *part = psys->part; + + *sfra = MAX2(1, (int)part->sta); + *efra = MIN2((int)(part->end + part->lifetime + 1.0), scene->r.efra); +} +static void particle_write_state(int index, void *psys_ptr, float *data) +{ + ParticleSystem *psys= psys_ptr; - BKE_ptcache_file_close(pf); + memcpy(data, (float *)(&(psys->particles+index)->state), sizeof(ParticleKey)); } +static void particle_read_state(int index, void *psys_ptr, float *data) +{ + ParticleSystem *psys= psys_ptr; + ParticleData *pa = psys->particles + index; + ParticleKey *key = (ParticleKey *)data; + + if(key->time > pa->state.time) + copy_particle_key(&pa->prev_state, &pa->state, 1); -static int get_particles_from_cache(Object *ob, ParticleSystem *psys, int cfra) + copy_particle_key(&pa->state, key, 1); +} +static void particle_cache_interpolate(int index, void *psys_ptr, float frs_sec, float cfra, float cfra1, float cfra2, float *data1, float *data2) { - PTCacheID pid; - PTCacheFile *pf; - ParticleData *pa; - int i, totpart= psys->totpart; + ParticleSystem *psys= psys_ptr; + ParticleData *pa = psys->particles + index; + ParticleKey keys[4]; + float dfra; - if(totpart == 0) - return 0; + cfra = MIN2(cfra, pa->dietime); + cfra1 = MIN2(cfra1, pa->dietime); + cfra2 = MIN2(cfra2, pa->dietime); - BKE_ptcache_id_from_particles(&pid, ob, psys); - pf= BKE_ptcache_file_open(&pid, PTCACHE_FILE_READ, cfra); - if(!pf) - return 0; + keys[1] = *((ParticleKey*)data1); + keys[2] = *((ParticleKey*)data2); - /* 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; - } + if(cfra1 == cfra2) { + copy_particle_key(&pa->state, &keys[1], 1); + return; } - BKE_ptcache_file_close(pf); + dfra = cfra2 - cfra1; - return 1; + VecMulf(keys[1].vel, dfra / frs_sec); + VecMulf(keys[2].vel, dfra / frs_sec); + + psys_interpolate_particle(-1, keys, (cfra - cfra1) / dfra, &pa->state, 1); + + VecMulf(pa->state.vel, frs_sec / dfra); + + pa->state.time = cfra; +} +static void write_particles_to_cache(Object *ob, ParticleSystem *psys, int cfra) +{ + PTCacheWriter writer; + PTCacheID pid; + + BKE_ptcache_id_from_particles(&pid, ob, psys); + + writer.calldata = psys; + writer.cfra = cfra; + writer.set_elem = particle_write_state; + writer.pid = &pid; + writer.totelem = psys->totpart; + + BKE_ptcache_write_cache(&writer); +} + +static int get_particles_from_cache(Scene *scene, Object *ob, ParticleSystem *psys, float cfra, int *old_frame) +{ + PTCacheReader reader; + PTCacheID pid; + + BKE_ptcache_id_from_particles(&pid, ob, psys); + + reader.calldata = psys; + reader.cfra = cfra; + reader.interpolate_elem = particle_cache_interpolate; + reader.old_frame = old_frame; + reader.pid = &pid; + reader.scene = scene; + reader.set_elem = particle_read_state; + reader.totelem = psys->totpart; + + return BKE_ptcache_read_cache(&reader); } /************************************************/ @@ -2355,6 +2461,8 @@ static void add_to_effectors(ListBase *lb, Scene *scene, Object *ob, Object *obs Object *tob; for(i=0; epsys; epsys=epsys->next,i++){ + if(!psys_check_enabled(ob, epsys)) + continue; type=0; if(epsys!=psys || (psys->part->flag & PART_SELF_EFFECT)){ epart=epsys->part; @@ -2922,7 +3030,7 @@ int psys_intersect_dm(Scene *scene, Object *ob, DerivedMesh *dm, float *vert_cos VECCOPY(p_max,pa_minmax+3); } - totface=dm->getNumTessFaces(dm); + totface=dm->getNumFaces(dm); mface=dm->getTessFaceDataArray(dm,CD_MFACE); mvert=dm->getVertDataArray(dm,CD_MVERT); @@ -3854,61 +3962,6 @@ static void boid_body(Scene *scene, BoidVecFunc *bvf, ParticleData *pa, Particle if(part->flag & PART_BOIDS_2D){ pa->state.vel[2]=0.0; pa->state.co[2]=part->groundz; - - 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,pa->state.co); - VECCOPY(co2,pa->state.co); - - co1[2]=1000.0f; - co2[2]=-1000.0f; - - Mat4Invert(imat,zob->obmat); - Mat4MulVecfl(imat,co1); - Mat4MulVecfl(imat,co2); - - if(psys_intersect_dm(scene,zob,0,0,co1,co2,&min_d,&min_face,min_w,0,0,0,0)){ - DerivedMesh *dm; - MFace *mface; - MVert *mvert; - float loc[3],nor[3],q1[4]; - - psys_disable_all(zob); - dm=mesh_get_derived_final(scene, zob, 0); - psys_enable_all(zob); - - mface=dm->getTessFaceDataArray(dm,CD_MFACE); - mface+=min_face; - mvert=dm->getVertDataArray(dm,CD_MVERT); - - /* get deflection point & normal */ - psys_interpolate_face(mvert,mface,0,0,min_w,loc,nor,0,0,0,0); - - Mat4MulVecfl(zob->obmat,loc); - Mat4Mul3Vecfl(zob->obmat,nor); - - Normalize(nor); - - VECCOPY(pa->state.co,loc); - - zvec[2]=1.0; - - Crossf(loc,zvec,nor); - - bank=VecLength(loc); - if(bank>0.0){ - bank=saasin(bank); - - VecRotToQuat(loc,bank,q); - - QUATCOPY(q1,pa->state.rot); - - QuatMul(pa->state.rot,q,q1); - } - } - } } length=bvf->Length(pa->state.vel); @@ -4068,7 +4121,7 @@ static void dynamics_step(Scene *scene, Object *ob, ParticleSystem *psys, Partic /* main loop: calculate physics for all particles */ for(p=0, pa=psys->particles; p<totpart; p++,pa++){ - if(pa->flag & PARS_UNEXIST) continue; + if(pa->flag & (PARS_UNEXIST+PARS_NO_DISP)) continue; copy_particle_key(&pa->prev_state,&pa->state,1); @@ -4093,25 +4146,26 @@ static void dynamics_step(Scene *scene, Object *ob, ParticleSystem *psys, Partic if(pa->alive==PARS_UNBORN || pa->alive==PARS_KILLED || ELEM(part->phystype,PART_PHYS_NO,PART_PHYS_KEYED) - || birthtime >= cfra){ + || birthtime >= psys->cfra){ reset_particle(scene, pa,psys,psmd,ob,dtime,cfra,vg_vel,vg_tan,vg_rot); } pa_dfra = dfra; pa_dtime = dtime; - if(birthtime <= cfra && birthtime >= psys->cfra){ + + if(dietime <= cfra && psys->cfra < dietime){ + /* particle dies some time between this and last step */ + pa_dfra = dietime - ((birthtime > psys->cfra) ? birthtime : psys->cfra); + pa_dtime = pa_dfra * timestep; + pa->alive = PARS_DYING; + } + else 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 */ } @@ -4179,7 +4233,7 @@ static void psys_update_path_cache(Scene *scene, Object *ob, ParticleSystemModif if((psys->part->childtype && psys->totchild != get_psys_tot_child(scene, psys)) || psys->recalc&PSYS_RECALC_RESET) alloc=1; - if(alloc || psys->recalc&PSYS_RECALC_RESET || (psys->vgroup[PSYS_VG_DENSITY] && (G.f & G_WEIGHTPAINT))) + if(alloc || psys->recalc&PSYS_RECALC_CHILD || (psys->vgroup[PSYS_VG_DENSITY] && (G.f & G_WEIGHTPAINT))) distr=1; if(distr){ @@ -4197,7 +4251,7 @@ static void psys_update_path_cache(Scene *scene, Object *ob, ParticleSystemModif } } - if((part->type==PART_HAIR || psys->flag&PSYS_KEYED) && ( psys_in_edit_mode(scene, psys) || (part->type==PART_HAIR + if((part->type==PART_HAIR || psys->flag&PSYS_KEYED || psys->pointcache->flag & PTCACHE_BAKED) && ( psys_in_edit_mode(scene, psys) || (part->type==PART_HAIR || (part->ren_as == PART_DRAW_PATH && (part->draw_as == PART_DRAW_REND || psys->renderdata))))){ psys_cache_paths(scene, ob, psys, cfra, 0); @@ -4314,11 +4368,13 @@ static void cached_step(Scene *scene, Object *ob, ParticleSystemModifierData *ps dietime = birthtime + (1 + pa->loop) * (pa->dietime - pa->time); /* update alive status and push events */ - if(pa->time > cfra) + if(pa->time > cfra) { pa->alive = PARS_UNBORN; + reset_particle(scene, pa, psys, psmd, ob, 0.0f, cfra, NULL, NULL, NULL); + } else if(dietime <= cfra){ if(dietime > psys->cfra){ - state.time = pa->dietime; + state.time = dietime; psys_get_particle_state(scene, ob,psys,p,&state,1); push_reaction(ob,psys,p,PART_EVENT_DEATH,&state); } @@ -4349,16 +4405,21 @@ static void cached_step(Scene *scene, Object *ob, ParticleSystemModifierData *ps distribute_particles(scene, ob, psys, PART_FROM_CHILD); } + psys_update_path_cache(scene, ob,psmd,psys,cfra); + if(vg_size) MEM_freeN(vg_size); } -void psys_changed_type(ParticleSystem *psys) +static void psys_changed_type(Object *ob, ParticleSystem *psys) { ParticleSettings *part; + PTCacheID pid; part= psys->part; + BKE_ptcache_id_from_particles(&pid, ob, psys); + /* system type has changed so set sensible defaults and clear non applicable flags */ if(part->from == PART_FROM_PARTICLE) { if(part->type != PART_REACTOR) @@ -4367,7 +4428,7 @@ void psys_changed_type(ParticleSystem *psys) part->distr = PART_DISTR_JIT; } - if(psys->part->phystype != PART_PHYS_KEYED) + if(part->phystype != PART_PHYS_KEYED) psys->flag &= ~PSYS_KEYED; if(part->type == PART_HAIR) { @@ -4376,15 +4437,35 @@ void psys_changed_type(ParticleSystem *psys) if(ELEM3(part->draw_as, PART_DRAW_NOT, PART_DRAW_REND, PART_DRAW_PATH)==0) part->draw_as = PART_DRAW_REND; + + CLAMP(part->path_start, 0.0f, 100.0f); + CLAMP(part->path_end, 0.0f, 100.0f); + + BKE_ptcache_id_clear(&pid, PTCACHE_CLEAR_ALL, 0); } - else + else { free_hair(psys, 1); + CLAMP(part->path_start, part->sta, part->end + part->lifetime); + CLAMP(part->path_end, part->sta, part->end + part->lifetime); + } + psys->softflag= 0; psys_reset(psys, PSYS_RESET_ALL); } - +void psys_changed_physics(Object *ob, ParticleSystem *psys) +{ + if(ELEM(psys->part->phystype, PART_PHYS_NO, PART_PHYS_KEYED)) { + PTCacheID pid; + BKE_ptcache_id_from_particles(&pid, ob, psys); + BKE_ptcache_id_clear(&pid, PTCACHE_CLEAR_ALL, 0); + } + else { + free_keyed_keys(psys); + psys->flag &= ~PSYS_KEYED; + } +} static void particles_fluid_step(Scene *scene, Object *ob, ParticleSystem *psys, int cfra) { if(psys->particles){ @@ -4503,7 +4584,7 @@ static void system_step(Scene *scene, Object *ob, ParticleSystem *psys, Particle 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; + int framenr, framedelta, startframe, endframe, old_framenr; part= psys->part; cache= psys->pointcache; @@ -4511,9 +4592,15 @@ static void system_step(Scene *scene, Object *ob, ParticleSystem *psys, Particle framenr= (int)scene->r.cfra; framedelta= framenr - cache->simframe; + /* set suitable cache range automatically */ + if((cache->flag & (PTCACHE_BAKING|PTCACHE_BAKED))==0) + psys_get_pointcache_start_end(scene, psys, &cache->startframe, &cache->endframe); + BKE_ptcache_id_from_particles(&pid, ob, psys); BKE_ptcache_id_time(&pid, scene, 0.0f, &startframe, &endframe, NULL); + psys_clear_temp_pointcache(psys); + /* update ipo's */ #if 0 // XXX old animation system if((part->flag & PART_ABS_TIME) && part->ipo) { @@ -4568,9 +4655,8 @@ static void system_step(Scene *scene, Object *ob, ParticleSystem *psys, Particle totpart = psys->part->totpart; totchild = get_psys_tot_child(scene, psys); - if(oldtotpart != totpart || (psys->part->childtype && oldtotchild != totchild)) { + if(oldtotpart != totpart || oldtotchild != totchild) { only_children_changed = (oldtotpart == totpart); - realloc_particles(ob, psys, totpart); alloc = 1; distr= 1; init= 1; @@ -4583,10 +4669,15 @@ static void system_step(Scene *scene, Object *ob, ParticleSystem *psys, Particle if(init) { if(distr) { - if(alloc) + if(alloc) { realloc_particles(ob, psys, totpart); - distribute_particles(scene, ob, psys, part->from); + if(usecache && !only_children_changed) + BKE_ptcache_id_clear(&pid, PTCACHE_CLEAR_ALL, 0); + } + + if(!only_children_changed) + distribute_particles(scene, ob, psys, part->from); if((psys->part->type == PART_HAIR) && !(psys->flag & PSYS_HAIR_DONE)) /* don't generate children while growing hair - waste of time */ @@ -4595,13 +4686,15 @@ static void system_step(Scene *scene, Object *ob, ParticleSystem *psys, Particle distribute_particles(scene, ob, psys, PART_FROM_CHILD); } - if(only_children_changed==0) { + if(!only_children_changed) { free_keyed_keys(psys); initialize_all_particles(ob, psys, psmd); + - if(alloc) + if(alloc) { reset_all_particles(scene, ob, psys, psmd, 0.0, cfra, oldtotpart); + } } /* flag for possible explode modifiers after this system */ @@ -4610,49 +4703,53 @@ static void system_step(Scene *scene, Object *ob, ParticleSystem *psys, Particle /* 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(scene, ob, psys); - } + int result = get_particles_from_cache(scene, ob, psys, (float)framenr, &old_framenr); + if(result == PTCACHE_READ_EXACT || result == PTCACHE_READ_INTERPOLATED) { cached_step(scene, 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(scene, ob, psmd, psys, framenr); - } - cache->simframe= framenr; cache->flag |= PTCACHE_SIMULATION_VALID; + if(result == PTCACHE_READ_INTERPOLATED && cache->flag & PTCACHE_REDO_NEEDED) + write_particles_to_cache(ob, psys, cfra); + return; } + else if(result==PTCACHE_READ_OLD) { + /* set old cfra */ + psys->cfra = (float)old_framenr; + + for(p=0, pa=psys->particles; p<totpart; p++, pa++) { + /* update alive status */ + if(pa->time > psys->cfra) + pa->alive = PARS_UNBORN; + else if(pa->dietime <= psys->cfra) + pa->alive = PARS_DEAD; + else + pa->alive = PARS_ALIVE; + } + } 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; + cache->last_exact= 0; } /* if on second frame, write cache for first frame */ - if(usecache && framenr == startframe+1) + if(usecache && psys->cfra == startframe && (cache->flag & PTCACHE_OUTDATED || cache->last_exact==0)) write_particles_to_cache(ob, psys, startframe); - if(part->phystype==PART_PHYS_KEYED && psys->flag&PSYS_FIRST_KEYED) + if(part->phystype==PART_PHYS_KEYED) psys_count_keyed_targets(ob,psys); /* initialize vertex groups */ @@ -4699,7 +4796,7 @@ static void system_step(Scene *scene, Object *ob, ParticleSystem *psys, Particle 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){ + if(part->phystype==PART_PHYS_KEYED) { set_keyed_keys(scene, ob, psys); psys_update_path_cache(scene, ob, psmd, psys,(int)cfra); } @@ -4751,8 +4848,7 @@ static void psys_to_softbody(Scene *scene, Object *ob, ParticleSystem *psys) static int hair_needs_recalc(ParticleSystem *psys) { if((psys->flag & PSYS_EDITED)==0 && - ((psys->flag & PSYS_HAIR_DONE)==0 || psys->recalc & PSYS_RECALC_REDO)) { - psys->recalc &= ~PSYS_RECALC_REDO; + ((psys->flag & PSYS_HAIR_DONE)==0 || psys->recalc & PSYS_RECALC_RESET)) { return 1; } @@ -4783,7 +4879,9 @@ void particle_system_update(Scene *scene, Object *ob, ParticleSystem *psys) return; if(psys->recalc & PSYS_RECALC_TYPE) - psys_changed_type(psys); + psys_changed_type(ob, psys); + else if(psys->recalc & PSYS_RECALC_PHYS) + psys_changed_physics(ob, psys); /* (re-)create hair */ if(psys->part->type==PART_HAIR && hair_needs_recalc(psys)) { diff --git a/source/blender/blenkernel/intern/pointcache.c b/source/blender/blenkernel/intern/pointcache.c index b00755f7135..6107510fa47 100644 --- a/source/blender/blenkernel/intern/pointcache.c +++ b/source/blender/blenkernel/intern/pointcache.c @@ -51,9 +51,11 @@ #include "BKE_object.h" #include "BKE_particle.h" #include "BKE_pointcache.h" +#include "BKE_scene.h" #include "BKE_softbody.h" #include "BKE_utildefines.h" +#include "BLI_blenlib.h" /* needed for directory lookup */ #ifndef WIN32 @@ -213,21 +215,29 @@ static int BKE_ptcache_id_filename(PTCacheID *pid, char *filename, int cfra, sho filename[0] = '\0'; newname = filename; - /*if (!G.relbase_valid) return 0; *//* save blend file before using pointcache */ + if (!G.relbase_valid) return 0; /* save blend file before using disk pointcache */ /* start with temp dir */ if (do_path) { len = ptcache_path(pid, filename); newname += len; } - idname = (pid->ob->id.name+2); - /* convert chars to hex so they are always a valid filename */ - while('\0' != *idname) { - snprintf(newname, MAX_PTCACHE_FILE, "%02X", (char)(*idname++)); - newname+=2; - len += 2; + if(strcmp(pid->cache->name, "")==0) { + idname = (pid->ob->id.name+2); + /* convert chars to hex so they are always a valid filename */ + while('\0' != *idname) { + snprintf(newname, MAX_PTCACHE_FILE, "%02X", (char)(*idname++)); + newname+=2; + len += 2; + } } - + else { + int temp = strlen(pid->cache->name); + strcpy(newname, pid->cache->name); + newname+=temp; + len += temp; + } + if (do_ext) { snprintf(newname, MAX_PTCACHE_FILE, "_%06d_%02d"PTCACHE_EXT, cfra, pid->stack_index); /* always 6 chars */ len += 16; @@ -247,7 +257,7 @@ PTCacheFile *BKE_ptcache_file_open(PTCacheID *pid, int mode, int cfra) if(pid->ob->id.lib && mode == PTCACHE_FILE_WRITE) return NULL; - /*if (!G.relbase_valid) return NULL; *//* save blend file before using pointcache */ + if (!G.relbase_valid) return NULL; /* save blend file before using disk pointcache */ BKE_ptcache_id_filename(pid, filename, cfra, 1, 1); @@ -286,6 +296,437 @@ int BKE_ptcache_file_write_floats(PTCacheFile *pf, float *f, int tot) return (fwrite(f, sizeof(float), tot, pf->fp) == tot); } +static int ptcache_pid_elemsize(PTCacheID *pid) +{ + if(pid->type==PTCACHE_TYPE_SOFTBODY) + return 0; // TODO + else if(pid->type==PTCACHE_TYPE_PARTICLES) + return sizeof(ParticleKey); + else if(pid->type==PTCACHE_TYPE_CLOTH) + return 9 * sizeof(float); + + return 0; +} +static int ptcache_pid_totelem(PTCacheID *pid) +{ + if(pid->type==PTCACHE_TYPE_SOFTBODY) + return 0; // TODO + else if(pid->type==PTCACHE_TYPE_PARTICLES) { + ParticleSystem *psys = pid->data; + return psys->totpart; + } + else if(pid->type==PTCACHE_TYPE_CLOTH) { + ClothModifierData *clmd = pid->data; + return clmd->clothObject->numverts; + } + + return 0; +} + +void BKE_ptcache_update_info(PTCacheID *pid) +{ + PointCache *cache = pid->cache; + int totframes = 0; + char mem_info[64]; + + if(cache->flag & PTCACHE_DISK_CACHE) { + int cfra = cache->startframe; + + for(; cfra<=cache->endframe; cfra++) { + if(BKE_ptcache_id_exist(pid, cfra)) + totframes++; + } + + sprintf(mem_info, "%i frames on disk", totframes); + } + else { + PTCacheMem *pm = cache->mem_cache.first; + float framesize = 0.0f, bytes = 0.0f; + int mb; + + if(pm) + framesize = (float)ptcache_pid_elemsize(pid) * (float)pm->totpoint; + + for(; pm; pm=pm->next) + totframes++; + + bytes = totframes * framesize; + + mb = (bytes > 1024.0f * 1024.0f); + + sprintf(mem_info, "%i frames in memory (%.1f %s)", + totframes, + bytes / (mb ? 1024.0f * 1024.0f : 1024.0f), + mb ? "Mb" : "kb"); + } + + if(cache->flag & PTCACHE_OUTDATED) { + sprintf(cache->info, "%s, cache is outdated!", mem_info); + } + else if(cache->flag & PTCACHE_FRAMES_SKIPPED) { + sprintf(cache->info, "%s, not exact since frame %i.", mem_info, cache->last_exact); + } + else + sprintf(cache->info, "%s.", mem_info); +} +/* reads cache from disk or memory */ +/* possible to get old or interpolated result */ +int BKE_ptcache_read_cache(PTCacheReader *reader) +{ + PTCacheID *pid = reader->pid; + PTCacheFile *pf=NULL, *pf2=NULL; + PTCacheMem *pm=NULL, *pm2=NULL; + int totelem = reader->totelem; + float cfra = reader->cfra; + int cfrai = (int)cfra; + int elemsize = ptcache_pid_elemsize(pid); + int i, incr = elemsize / sizeof(float); + float frs_sec = reader->scene->r.frs_sec; + int cfra1=0, cfra2; + int ret = 0; + + if(totelem == 0) + return 0; + + + /* first check if we have the actual frame cached */ + if(cfra == (float)cfrai) { + if(pid->cache->flag & PTCACHE_DISK_CACHE) { + pf= BKE_ptcache_file_open(pid, PTCACHE_FILE_READ, cfrai); + } + else { + pm = pid->cache->mem_cache.first; + + for(; pm; pm=pm->next) { + if(pm->frame == cfrai) + break; + } + } + } + + /* if found, use exact frame */ + if(pf || pm) { + float *data; + + if(pm) + data = pm->data; + else + data = MEM_callocN(elemsize, "pointcache read data"); + + for(i=0; i<totelem; i++) { + if(pf) { + if(!BKE_ptcache_file_read_floats(pf, data, incr)) { + BKE_ptcache_file_close(pf); + MEM_freeN(data); + return 0; + } + + reader->set_elem(i, reader->calldata, data); + } + else { + reader->set_elem(i, reader->calldata, data); + data += incr; + } + } + + if(pf) { + BKE_ptcache_file_close(pf); + pf = NULL; + MEM_freeN(data); + } + + ret = PTCACHE_READ_EXACT; + } + + if(ret) + ; + /* no exact cache frame found so try to find cached frames around cfra */ + else if(pid->cache->flag & PTCACHE_DISK_CACHE) { + pf=NULL; + while(cfrai > pid->cache->startframe && !pf) { + cfrai--; + pf= BKE_ptcache_file_open(pid, PTCACHE_FILE_READ, cfrai); + cfra1 = cfrai; + } + + if(reader->old_frame) + *(reader->old_frame) = cfrai; + + cfrai = (int)cfra; + while(cfrai < pid->cache->endframe && !pf2) { + cfrai++; + pf2= BKE_ptcache_file_open(pid, PTCACHE_FILE_READ, cfrai); + cfra2 = cfrai; + } + } + else if(pid->cache->mem_cache.first){ + pm = pid->cache->mem_cache.first; + + while(pm->next && pm->next->frame < cfra) + pm= pm->next; + + if(pm) { + if(reader->old_frame) + *(reader->old_frame) = pm->frame; + cfra1 = pm->frame; + } + + pm2 = pid->cache->mem_cache.last; + + if(pm2 && pm2->frame < cfra) + pm2 = NULL; + else { + while(pm2->prev && pm2->prev->frame > cfra) + pm2= pm2->prev; + + if(pm2) + cfra2 = pm2->frame; + } + } + + if(ret) + ; + else if((pf && pf2) || (pm && pm2)) { + /* interpolate from nearest frames if cache isn't outdated */ + float *data1, *data2; + + if(pm) { + data1 = pm->data; + data2 = pm2->data; + } + else { + data1 = MEM_callocN(elemsize, "pointcache read data1"); + data2 = MEM_callocN(elemsize, "pointcache read data2"); + } + + for(i=0; i<totelem; i++) { + if(pf && pf2) { + if(!BKE_ptcache_file_read_floats(pf, data1, incr)) { + BKE_ptcache_file_close(pf); + BKE_ptcache_file_close(pf2); + MEM_freeN(data1); + MEM_freeN(data2); + return 0; + } + if(!BKE_ptcache_file_read_floats(pf2, data2, incr)) { + BKE_ptcache_file_close(pf); + BKE_ptcache_file_close(pf2); + MEM_freeN(data1); + MEM_freeN(data2); + return 0; + } + reader->interpolate_elem(i, reader->calldata, frs_sec, cfra, (float)cfra1, (float)cfra2, data1, data2); + } + else { + reader->interpolate_elem(i, reader->calldata, frs_sec, cfra, (float)cfra1, (float)cfra2, data1, data2); + data1 += incr; + data2 += incr; + } + } + + if(pf) { + BKE_ptcache_file_close(pf); + pf = NULL; + BKE_ptcache_file_close(pf2); + pf2 = NULL; + MEM_freeN(data1); + MEM_freeN(data2); + } + + ret = PTCACHE_READ_INTERPOLATED; + } + else if(pf || pm) { + /* use last valid cache frame */ + float *data; + + /* don't read cache if allready simulated past cached frame */ + if(cfra1 && cfra1 <= pid->cache->simframe) { + if(pf) + BKE_ptcache_file_close(pf); + if(pf2) + BKE_ptcache_file_close(pf2); + + return 0; + } + + if(pm) + data = pm->data; + else + data = MEM_callocN(elemsize, "pointcache read data"); + + for(i=0; i<totelem; i++) { + if(pf) { + if(!BKE_ptcache_file_read_floats(pf, data, incr)) { + BKE_ptcache_file_close(pf); + if(pf2) + BKE_ptcache_file_close(pf2); + return 0; + } + reader->set_elem(i, reader->calldata, data); + } + else { + reader->set_elem(i, reader->calldata, data); + data += incr; + } + } + + if(pf) { + BKE_ptcache_file_close(pf); + pf = NULL; + MEM_freeN(data); + } + if(pf2) { + BKE_ptcache_file_close(pf2); + pf = NULL; + } + + ret = PTCACHE_READ_OLD; + } + + if(pf) + BKE_ptcache_file_close(pf); + if(pf2) + BKE_ptcache_file_close(pf2); + + if((pid->cache->flag & PTCACHE_QUICK_CACHE)==0) { + /* clear invalid cache frames so that better stuff can be simulated */ + if(pid->cache->flag & PTCACHE_OUTDATED) { + BKE_ptcache_id_clear(pid, PTCACHE_CLEAR_AFTER, cfra); + } + else if(pid->cache->flag & PTCACHE_FRAMES_SKIPPED) { + if(cfra <= pid->cache->last_exact) + pid->cache->flag &= ~PTCACHE_FRAMES_SKIPPED; + + BKE_ptcache_id_clear(pid, PTCACHE_CLEAR_AFTER, MAX2(cfra,pid->cache->last_exact)); + } + } + + return ret; +} +/* writes cache to disk or memory */ +int BKE_ptcache_write_cache(PTCacheWriter *writer) +{ + PointCache *cache = writer->pid->cache; + PTCacheFile *pf= NULL; + int elemsize = ptcache_pid_elemsize(writer->pid); + int i, incr = elemsize / sizeof(float); + int add = 0, overwrite = 0; + float temp[14]; + + if(writer->totelem == 0 || writer->cfra <= 0) + return 0; + + if(cache->flag & PTCACHE_DISK_CACHE) { + int cfra = cache->endframe; + + /* allways start from scratch on the first frame */ + if(writer->cfra == cache->startframe) { + BKE_ptcache_id_clear(writer->pid, PTCACHE_CLEAR_ALL, writer->cfra); + cache->flag &= ~PTCACHE_REDO_NEEDED; + add = 1; + } + else { + int ocfra; + /* find last cached frame */ + while(cfra > cache->startframe && !BKE_ptcache_id_exist(writer->pid, cfra)) + cfra--; + + /* find second last cached frame */ + ocfra = cfra-1; + while(ocfra > cache->startframe && !BKE_ptcache_id_exist(writer->pid, ocfra)) + ocfra--; + + if(cfra >= cache->startframe && writer->cfra > cfra) { + if(ocfra >= cache->startframe && cfra - ocfra < cache->step) + overwrite = 1; + else + add = 1; + } + } + + if(add || overwrite) { + if(overwrite) + BKE_ptcache_id_clear(writer->pid, PTCACHE_CLEAR_FRAME, cfra); + + pf = BKE_ptcache_file_open(writer->pid, PTCACHE_FILE_WRITE, writer->cfra); + if(!pf) + return 0; + + for(i=0; i<writer->totelem; i++) { + writer->set_elem(i, writer->calldata, temp); + BKE_ptcache_file_write_floats(pf, temp, incr); + } + } + } + else { + PTCacheMem *pm; + PTCacheMem *pm2; + float *pmdata; + + pm2 = cache->mem_cache.first; + + /* allways start from scratch on the first frame */ + if(writer->cfra == cache->startframe) { + BKE_ptcache_id_clear(writer->pid, PTCACHE_CLEAR_ALL, writer->cfra); + cache->flag &= ~PTCACHE_REDO_NEEDED; + add = 1; + } + else { + pm2 = cache->mem_cache.last; + + if(pm2 && writer->cfra > pm2->frame) { + if(pm2->prev && pm2->frame - pm2->prev->frame < cache->step) + overwrite = 1; + else + add = 1; + } + } + + if(overwrite) { + pm = cache->mem_cache.last; + pmdata = pm->data; + + for(i=0; i<writer->totelem; i++, pmdata+=incr) { + writer->set_elem(i, writer->calldata, temp); + memcpy(pmdata, temp, elemsize); + } + + pm->frame = writer->cfra; + } + else if(add) { + pm = MEM_callocN(sizeof(PTCacheMem), "Pointcache mem"); + pm->data = MEM_callocN(elemsize * writer->totelem, "Pointcache mem data"); + pmdata = pm->data; + + for(i=0; i<writer->totelem; i++, pmdata+=incr) { + writer->set_elem(i, writer->calldata, temp); + memcpy(pmdata, temp, elemsize); + } + + pm->frame = writer->cfra; + pm->totpoint = writer->totelem; + + BLI_addtail(&cache->mem_cache, pm); + } + } + + if(add || overwrite) { + if(writer->cfra - cache->last_exact == 1 + || writer->cfra == cache->startframe) { + cache->last_exact = writer->cfra; + cache->flag &= ~PTCACHE_FRAMES_SKIPPED; + } + else + cache->flag |= PTCACHE_FRAMES_SKIPPED; + } + + if(pf) + BKE_ptcache_file_close(pf); + + BKE_ptcache_update_info(writer->pid); + + return 1; +} /* youll need to close yourself after! * mode - PTCACHE_CLEAR_ALL, @@ -317,62 +758,116 @@ void BKE_ptcache_id_clear(PTCacheID *pid, int mode, int cfra) case PTCACHE_CLEAR_ALL: case PTCACHE_CLEAR_BEFORE: case PTCACHE_CLEAR_AFTER: - 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, 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); - BLI_delete(path_full, 0, 0); - } else { - /* 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*/ - BLI_strncpy(num, de->d_name + (strlen(de->d_name) - 15), sizeof(num)); - frame = atoi(num); - - if((mode==PTCACHE_CLEAR_BEFORE && frame < cfra) || - (mode==PTCACHE_CLEAR_AFTER && frame > cfra) ) { + if(pid->cache->flag & PTCACHE_DISK_CACHE) { + 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, 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) { + pid->cache->last_exact = 0; + BLI_join_dirfile(path_full, path, de->d_name); + BLI_delete(path_full, 0, 0); + } else { + /* 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*/ + BLI_strncpy(num, de->d_name + (strlen(de->d_name) - 15), sizeof(num)); + frame = atoi(num); - BLI_join_dirfile(path_full, path, de->d_name); - BLI_delete(path_full, 0, 0); + if((mode==PTCACHE_CLEAR_BEFORE && frame < cfra) || + (mode==PTCACHE_CLEAR_AFTER && frame > cfra) ) { + + BLI_join_dirfile(path_full, path, de->d_name); + BLI_delete(path_full, 0, 0); + } } } } } } + closedir(dir); + } + else { + PTCacheMem *pm= pid->cache->mem_cache.first; + PTCacheMem *link= NULL; + + if(mode == PTCACHE_CLEAR_ALL) { + pid->cache->last_exact = 0; + for(; pm; pm=pm->next) + MEM_freeN(pm->data); + BLI_freelistN(&pid->cache->mem_cache); + } else { + while(pm) { + if((mode==PTCACHE_CLEAR_BEFORE && pm->frame < cfra) || + (mode==PTCACHE_CLEAR_AFTER && pm->frame > cfra) ) { + link = pm; + pm = pm->next; + MEM_freeN(link->data); + BLI_freelinkN(&pid->cache->mem_cache, link); + } + else + pm = pm->next; + } + } } - closedir(dir); break; case PTCACHE_CLEAR_FRAME: - len = BKE_ptcache_id_filename(pid, filename, cfra, 1, 1); /* no path */ - BLI_delete(filename, 0, 0); + if(pid->cache->flag & PTCACHE_DISK_CACHE) { + if(BKE_ptcache_id_exist(pid, cfra)) { + BKE_ptcache_id_filename(pid, filename, cfra, 1, 1); /* no path */ + BLI_delete(filename, 0, 0); + } + } + else { + PTCacheMem *pm = pid->cache->mem_cache.first; + + for(; pm; pm=pm->next) { + if(pm->frame == cfra) { + MEM_freeN(pm->data); + BLI_freelinkN(&pid->cache->mem_cache, pm); + break; + } + } + } break; } + + BKE_ptcache_update_info(pid); } int BKE_ptcache_id_exist(PTCacheID *pid, int cfra) { - char filename[MAX_PTCACHE_FILE]; - if(!pid->cache) return 0; - BKE_ptcache_id_filename(pid, filename, cfra, 1, 1); + if(pid->cache->flag & PTCACHE_DISK_CACHE) { + char filename[MAX_PTCACHE_FILE]; + + BKE_ptcache_id_filename(pid, filename, cfra, 1, 1); - return BLI_exists(filename); + return BLI_exists(filename); + } + else { + PTCacheMem *pm = pid->cache->mem_cache.first; + + for(; pm; pm=pm->next) { + if(pm->frame==cfra) + return 1; + } + return 0; + } } void BKE_ptcache_id_time(PTCacheID *pid, Scene *scene, float cfra, int *startframe, int *endframe, float *timescale) @@ -381,6 +876,9 @@ void BKE_ptcache_id_time(PTCacheID *pid, Scene *scene, float cfra, int *startfra PointCache *cache; float offset, time, nexttime; + /* TODO: this has to be sorter out once bsystem_time gets redone, */ + /* now caches can handle interpolating etc. too - jahka */ + /* 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 @@ -414,10 +912,10 @@ void BKE_ptcache_id_time(PTCacheID *pid, Scene *scene, float cfra, int *startfra } } -int BKE_ptcache_id_reset(PTCacheID *pid, int mode) +int BKE_ptcache_id_reset(Scene *scene, PTCacheID *pid, int mode) { PointCache *cache; - int reset, clear; + int reset, clear, after; if(!pid->cache) return 0; @@ -425,14 +923,17 @@ int BKE_ptcache_id_reset(PTCacheID *pid, int mode) cache= pid->cache; reset= 0; clear= 0; + after= 0; if(mode == PTCACHE_RESET_DEPSGRAPH) { if(!(cache->flag & PTCACHE_BAKED) && !BKE_ptcache_get_continue_physics()) { - reset= 1; - clear= 1; + if(cache->flag & PTCACHE_QUICK_CACHE) + clear= 1; + + after= 1; } - else - cache->flag |= PTCACHE_OUTDATED; + + cache->flag |= PTCACHE_OUTDATED; } else if(mode == PTCACHE_RESET_BAKED) { if(!BKE_ptcache_get_continue_physics()) { @@ -451,8 +952,9 @@ int BKE_ptcache_id_reset(PTCacheID *pid, int mode) } if(reset) { - cache->flag &= ~(PTCACHE_OUTDATED|PTCACHE_SIMULATION_VALID); + cache->flag &= ~(PTCACHE_REDO_NEEDED|PTCACHE_SIMULATION_VALID); cache->simframe= 0; + cache->last_exact= 0; if(pid->type == PTCACHE_TYPE_CLOTH) cloth_free_modifier(pid->ob, pid->data); @@ -463,11 +965,13 @@ int BKE_ptcache_id_reset(PTCacheID *pid, int mode) } if(clear) BKE_ptcache_id_clear(pid, PTCACHE_CLEAR_ALL, 0); + else if(after) + BKE_ptcache_id_clear(pid, PTCACHE_CLEAR_AFTER, CFRA); - return (reset || clear); + return (reset || clear || after); } -int BKE_ptcache_object_reset(Object *ob, int mode) +int BKE_ptcache_object_reset(Scene *scene, Object *ob, int mode) { PTCacheID pid; ParticleSystem *psys; @@ -479,7 +983,7 @@ int BKE_ptcache_object_reset(Object *ob, int mode) if(ob->soft) { BKE_ptcache_id_from_softbody(&pid, ob, ob->soft); - reset |= BKE_ptcache_id_reset(&pid, mode); + reset |= BKE_ptcache_id_reset(scene, &pid, mode); } for(psys=ob->particlesystem.first; psys; psys=psys->next) { @@ -488,23 +992,23 @@ int BKE_ptcache_object_reset(Object *ob, int mode) 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); + reset |= BKE_ptcache_id_reset(scene, &pid, mode); else skip = 1; } - else if((psys->recalc & PSYS_RECALC_RESET)==0) + else if(psys->recalc & PSYS_RECALC_REDO || psys->recalc & PSYS_RECALC_CHILD) skip = 1; if(skip == 0) { BKE_ptcache_id_from_particles(&pid, ob, psys); - reset |= BKE_ptcache_id_reset(&pid, mode); + reset |= BKE_ptcache_id_reset(scene, &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); + reset |= BKE_ptcache_id_reset(scene, &pid, mode); } } @@ -564,7 +1068,7 @@ void BKE_ptcache_set_continue_physics(Scene *scene, int 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)) + if(BKE_ptcache_object_reset(scene, ob, PTCACHE_RESET_OUTDATED)) DAG_object_flush_update(scene, ob, OB_RECALC_DATA); } } @@ -584,12 +1088,21 @@ PointCache *BKE_ptcache_add() cache= MEM_callocN(sizeof(PointCache), "PointCache"); cache->startframe= 1; cache->endframe= 250; + cache->step= 10; return cache; } void BKE_ptcache_free(PointCache *cache) { + PTCacheMem *pm = cache->mem_cache.first; + if(pm) { + for(; pm; pm=pm->next) + MEM_freeN(pm->data); + + BLI_freelistN(&cache->mem_cache); + } + MEM_freeN(cache); } @@ -599,9 +1112,300 @@ PointCache *BKE_ptcache_copy(PointCache *cache) ncache= MEM_dupallocN(cache); + /* hmm, should these be copied over instead? */ + ncache->mem_cache.first = NULL; + ncache->mem_cache.last = NULL; + ncache->flag= 0; ncache->simframe= 0; return ncache; } + + +/* Baking */ +static int count_quick_cache(Scene *scene, int *quick_step) +{ + Base *base = scene->base.first; + PTCacheID *pid; + ListBase pidlist; + int autocache_count= 0; + + for(base = scene->base.first; base; base = base->next) { + if(base->object) { + BKE_ptcache_ids_from_object(&pidlist, base->object); + + for(pid=pidlist.first; pid; pid=pid->next) { + if((pid->cache->flag & PTCACHE_BAKED) + || (pid->cache->flag & PTCACHE_QUICK_CACHE)==0) + continue; + + if(pid->cache->flag & PTCACHE_OUTDATED || (pid->cache->flag & PTCACHE_SIMULATION_VALID)==0) { + if(!autocache_count) + *quick_step = pid->cache->step; + else + *quick_step = MIN2(*quick_step, pid->cache->step); + + autocache_count++; + } + } + + BLI_freelistN(&pidlist); + } + } + + return autocache_count; +} +void BKE_ptcache_quick_cache_all(Scene *scene) +{ + PTCacheBaker baker; + + baker.bake=0; + baker.break_data=NULL; + baker.break_test=NULL; + baker.pid=NULL; + baker.progressbar=NULL; + baker.progresscontext=NULL; + baker.render=0; + baker.anim_init = 0; + baker.scene=scene; + + if(count_quick_cache(scene, &baker.quick_step)) + BKE_ptcache_make_cache(&baker); +} + +/* if bake is not given run simulations to current frame */ +void BKE_ptcache_make_cache(PTCacheBaker* baker) +{ + Scene *scene = baker->scene; + Base *base; + ListBase pidlist; + PTCacheID *pid = baker->pid; + PointCache *cache; + float frameleno = scene->r.framelen; + int cfrao = CFRA; + int startframe = MAXFRAME; + int endframe = baker->anim_init ? scene->r.sfra : CFRA; + int bake = baker->bake; + int render = baker->render; + int step = baker->quick_step; + + G.afbreek = 0; + + /* set caches to baking mode and figure out start frame */ + if(pid) { + /* cache/bake a single object */ + cache = pid->cache; + if((cache->flag & PTCACHE_BAKED)==0) { + if(pid->type==PTCACHE_TYPE_PARTICLES) + psys_get_pointcache_start_end(scene, pid->data, &cache->startframe, &cache->endframe); + + if(bake || cache->flag & PTCACHE_REDO_NEEDED) + BKE_ptcache_id_clear(pid, PTCACHE_CLEAR_ALL, 0); + + startframe = MAX2(cache->last_exact, cache->startframe); + + if(bake) { + endframe = cache->endframe; + cache->flag |= PTCACHE_BAKING; + } + else { + endframe = MIN2(endframe, cache->endframe); + } + + cache->flag &= ~PTCACHE_BAKED; + } + } + else for(base=scene->base.first; base; base= base->next) { + /* cache/bake everything in the scene */ + BKE_ptcache_ids_from_object(&pidlist, base->object); + + for(pid=pidlist.first; pid; pid=pid->next) { + cache = pid->cache; + if((cache->flag & PTCACHE_BAKED)==0) { + if(pid->type==PTCACHE_TYPE_PARTICLES) { + ParticleSystem *psys = (ParticleSystem*)pid->data; + /* skip hair & keyed particles */ + if(psys->part->type == PART_HAIR || psys->part->phystype == PART_PHYS_KEYED) + continue; + + psys_get_pointcache_start_end(scene, pid->data, &cache->startframe, &cache->endframe); + } + + if((cache->flag & PTCACHE_REDO_NEEDED || (cache->flag & PTCACHE_SIMULATION_VALID)==0) + && ((cache->flag & PTCACHE_QUICK_CACHE)==0 || render || bake)) + BKE_ptcache_id_clear(pid, PTCACHE_CLEAR_ALL, 0); + + startframe = MIN2(startframe, cache->startframe); + + if(bake || render) { + cache->flag |= PTCACHE_BAKING; + + if(bake) + endframe = MAX2(endframe, cache->endframe); + } + + cache->flag &= ~PTCACHE_BAKED; + + } + } + BLI_freelistN(&pidlist); + } + + CFRA= startframe; + scene->r.framelen = 1.0; + + for(; CFRA <= endframe; CFRA+=step) { + float prog; + + if(bake) + prog = (int)(100.0 * (float)(CFRA - startframe)/(float)(endframe-startframe)); + else + prog = CFRA; + + /* NOTE: baking should not redraw whole ui as this slows things down */ + if(baker->progressbar) + baker->progressbar(baker->progresscontext, prog); + + scene_update_for_newframe(scene, scene->lay); + + /* NOTE: breaking baking should leave calculated frames in cache, not clear it */ + if(baker->break_test && baker->break_test(baker->break_data)) + break; + } + + /* clear baking flag */ + if(pid) { + cache->flag &= ~(PTCACHE_BAKING|PTCACHE_REDO_NEEDED); + cache->flag |= PTCACHE_SIMULATION_VALID; + if(bake) + cache->flag |= PTCACHE_BAKED; + } + else for(base=scene->base.first; base; base= base->next) { + BKE_ptcache_ids_from_object(&pidlist, base->object); + + for(pid=pidlist.first; pid; pid=pid->next) { + /* skip hair particles */ + if(pid->type==PTCACHE_TYPE_PARTICLES && ((ParticleSystem*)pid->data)->part->type == PART_HAIR) + continue; + + cache = pid->cache; + + if(step > 1) + cache->flag &= ~(PTCACHE_BAKING|PTCACHE_OUTDATED); + else + cache->flag &= ~(PTCACHE_BAKING|PTCACHE_REDO_NEEDED); + + cache->flag |= PTCACHE_SIMULATION_VALID; + + if(bake) + cache->flag |= PTCACHE_BAKED; + } + BLI_freelistN(&pidlist); + } + + scene->r.framelen = frameleno; + CFRA = cfrao; + + if(bake) /* already on cfra unless baking */ + scene_update_for_newframe(scene, scene->lay); + + /* TODO: call redraw all windows somehow */ +} + +void BKE_ptcache_toggle_disk_cache(PTCacheID *pid) { + PointCache *cache = pid->cache; + PTCacheFile *pf; + PTCacheMem *pm; + int totelem=0; + int float_count=0; + int tot; + int last_exact = cache->last_exact; + + if (!G.relbase_valid){ + cache->flag &= ~PTCACHE_DISK_CACHE; + printf("File must be saved before using disk cache!\n"); + return; + } + + totelem = ptcache_pid_totelem(pid); + float_count = ptcache_pid_elemsize(pid) / sizeof(float); + + if(totelem==0 || float_count==0) + return; + + tot = totelem*float_count; + + /* MEM -> DISK */ + if(cache->flag & PTCACHE_DISK_CACHE) { + pm = cache->mem_cache.first; + + BKE_ptcache_id_clear(pid, PTCACHE_CLEAR_ALL, 0); + + for(; pm; pm=pm->next) { + pf = BKE_ptcache_file_open(pid, PTCACHE_FILE_WRITE, pm->frame); + + if(pf) { + if(fwrite(pm->data, sizeof(float), tot, pf->fp) != tot) { + printf("Error writing to disk cache\n"); + + cache->flag &= ~PTCACHE_DISK_CACHE; + + BKE_ptcache_file_close(pf); + return; + } + BKE_ptcache_file_close(pf); + } + else + printf("Error creating disk cache file\n"); + } + + cache->flag &= ~PTCACHE_DISK_CACHE; + BKE_ptcache_id_clear(pid, PTCACHE_CLEAR_ALL, 0); + cache->flag |= PTCACHE_DISK_CACHE; + } + /* DISK -> MEM */ + else { + int cfra; + int sfra = cache->startframe; + int efra = cache->endframe; + + BKE_ptcache_id_clear(pid, PTCACHE_CLEAR_ALL, 0); + + for(cfra=sfra; cfra <= efra; cfra++) { + pf = BKE_ptcache_file_open(pid, PTCACHE_FILE_READ, cfra); + + if(pf) { + pm = MEM_callocN(sizeof(PTCacheMem), "Pointcache mem"); + pm->data = MEM_callocN(sizeof(float)*tot, "Pointcache mem data"); + + if(fread(pm->data, sizeof(float), tot, pf->fp)!= tot) { + printf("Error reading from disk cache\n"); + + cache->flag |= PTCACHE_DISK_CACHE; + + MEM_freeN(pm->data); + MEM_freeN(pm); + BKE_ptcache_file_close(pf); + return; + } + + pm->frame = cfra; + pm->totpoint = totelem; + + BLI_addtail(&pid->cache->mem_cache, pm); + + BKE_ptcache_file_close(pf); + } + } + + cache->flag |= PTCACHE_DISK_CACHE; + BKE_ptcache_id_clear(pid, PTCACHE_CLEAR_ALL, 0); + cache->flag &= ~PTCACHE_DISK_CACHE; + } + + cache->last_exact = last_exact; + + BKE_ptcache_update_info(pid); +} diff --git a/source/blender/blenkernel/intern/report.c b/source/blender/blenkernel/intern/report.c index 116fd069948..8de8cf8d0f4 100644 --- a/source/blender/blenkernel/intern/report.c +++ b/source/blender/blenkernel/intern/report.c @@ -65,8 +65,8 @@ void BKE_reports_init(ReportList *reports, int flag) memset(reports, 0, sizeof(ReportList)); - reports->storelevel= RPT_WARNING; - reports->printlevel= RPT_WARNING; + reports->storelevel= RPT_INFO; + reports->printlevel= RPT_INFO; reports->flag= flag; } diff --git a/source/blender/blenkernel/intern/scene.c b/source/blender/blenkernel/intern/scene.c index 156bdae9b00..598336886c2 100644 --- a/source/blender/blenkernel/intern/scene.c +++ b/source/blender/blenkernel/intern/scene.c @@ -142,8 +142,6 @@ void free_scene(Scene *sce) BLI_freelistN(&sce->base); seq_free_editing(sce->ed); - if(sce->radio) MEM_freeN(sce->radio); - sce->radio= 0; #ifndef DISABLE_PYTHON BPY_free_scriptlink(&sce->scriptlink); @@ -205,21 +203,18 @@ Scene *add_scene(char *name) sce= alloc_libblock(&G.main->scene, ID_SCE, 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.mode= R_GAMMA|R_OSA|R_SHADOW|R_SSS|R_ENVMAP|R_RAYTRACE; sce->r.cfra= 1; sce->r.sfra= 1; sce->r.efra= 250; - sce->r.xsch= 320; - sce->r.ysch= 256; + sce->r.xsch= 1920; + sce->r.ysch= 1080; sce->r.xasp= 1; sce->r.yasp= 1; - sce->r.xparts= 4; - sce->r.yparts= 4; - sce->r.size= 100; + sce->r.xparts= 8; + sce->r.yparts= 8; + sce->r.size= 25; sce->r.planes= 24; sce->r.quality= 90; sce->r.framapto= 100; @@ -230,7 +225,7 @@ Scene *add_scene(char *name) sce->r.ocres = 128; sce->r.bake_mode= 1; /* prevent to include render stuff here */ - sce->r.bake_filter= 2; + sce->r.bake_filter= 8; sce->r.bake_osa= 5; sce->r.bake_flag= R_BAKE_CLEAR; sce->r.bake_normal_space= R_BAKE_SPACE_TANGENT; @@ -239,6 +234,9 @@ Scene *add_scene(char *name) sce->r.yplay= 480; sce->r.freqplay= 60; sce->r.depth= 32; + + sce->r.scemode= R_DOCOMP|R_DOSEQ|R_EXTENSION; + sce->r.stamp= R_STAMP_TIME|R_STAMP_FRAME|R_STAMP_DATE|R_STAMP_SCENE|R_STAMP_CAMERA; sce->r.threads= 1; @@ -277,6 +275,10 @@ Scene *add_scene(char *name) sce->toolsettings->select_thresh= 0.01f; sce->toolsettings->jointrilimit = 0.8f; + sce->toolsettings->selectmode= SCE_SELECT_VERTEX; + sce->toolsettings->normalsize= 0.1; + sce->toolsettings->autokey_mode= U.autokey_mode; + sce->toolsettings->skgen_resolution = 100; sce->toolsettings->skgen_threshold_internal = 0.01f; sce->toolsettings->skgen_threshold_external = 0.01f; diff --git a/source/blender/blenkernel/intern/screen.c b/source/blender/blenkernel/intern/screen.c index e25e4be90c8..4b6eddf60d0 100644 --- a/source/blender/blenkernel/intern/screen.c +++ b/source/blender/blenkernel/intern/screen.c @@ -78,6 +78,8 @@ static void spacetype_free(SpaceType *st) } BLI_freelistN(&st->regiontypes); + BLI_freelistN(&st->toolshelf); + } void BKE_spacetypes_free(void) diff --git a/source/blender/blenkernel/intern/sequence.c b/source/blender/blenkernel/intern/sequence.c index 3365af36f8c..3eeecd94a85 100644 --- a/source/blender/blenkernel/intern/sequence.c +++ b/source/blender/blenkernel/intern/sequence.c @@ -1,5 +1,5 @@ /** -* $Id: sequence.c 17508 2008-11-20 00:34:24Z campbellbarton $ +* $Id$ * * ***** BEGIN GPL LICENSE BLOCK ***** * @@ -184,11 +184,11 @@ void seq_free_sequence(Editing *ed, Sequence *seq) if(seq->anim) IMB_free_anim(seq->anim); //XXX if(seq->hdaudio) sound_close_hdaudio(seq->hdaudio); - /* XXX if (seq->type & SEQ_EFFECT) { + if (seq->type & SEQ_EFFECT) { struct SeqEffectHandle sh = get_sequence_effect(seq); sh.free(seq); - }*/ + } if (ed->act_seq==seq) ed->act_seq= NULL; @@ -1288,7 +1288,7 @@ static void seq_proxy_build_frame(Scene *scene, Sequence * seq, int cfra, int re depth = 32 is intentionally left in, otherwise ALPHA channels won't work... */ - quality = 90; + quality = seq->strip->proxy->quality; ibuf->ftype= JPG | quality; BLI_make_existing_file(name); @@ -1305,6 +1305,7 @@ static void seq_proxy_build_frame(Scene *scene, Sequence * seq, int cfra, int re void seq_proxy_rebuild(Scene *scene, Sequence * seq) { int cfra; + float rsize = seq->strip->proxy->size; waitcursor(1); @@ -1322,6 +1323,8 @@ void seq_proxy_rebuild(Scene *scene, Sequence * seq) tse->flag &= ~STRIPELEM_PREVIEW_DONE; } + + /* a _lot_ faster for movie files, if we read frames in sequential order */ if (seq->flag & SEQ_REVERSE_FRAMES) { @@ -1330,7 +1333,8 @@ void seq_proxy_rebuild(Scene *scene, Sequence * seq) TStripElem * tse = give_tstripelem(seq, cfra); if (!(tse->flag & STRIPELEM_PREVIEW_DONE)) { - seq_proxy_build_frame(scene, seq, cfra, scene->r.size); +//XXX set_timecursor(cfra); + seq_proxy_build_frame(scene, seq, cfra, rsize); tse->flag |= STRIPELEM_PREVIEW_DONE; } if (blender_test_break()) { @@ -1343,7 +1347,8 @@ void seq_proxy_rebuild(Scene *scene, Sequence * seq) TStripElem * tse = give_tstripelem(seq, cfra); if (!(tse->flag & STRIPELEM_PREVIEW_DONE)) { - seq_proxy_build_frame(scene, seq, cfra, scene->r.size); +//XXX set_timecursor(cfra); + seq_proxy_build_frame(scene, seq, cfra, rsize); tse->flag |= STRIPELEM_PREVIEW_DONE; } if (blender_test_break()) { @@ -1552,7 +1557,8 @@ static int input_have_to_preprocess(Scene *scene, Sequence * seq, TStripElem* se mul = seq->mul; - if(seq->blend_mode == SEQ_BLEND_REPLACE) { + if(seq->blend_mode == SEQ_BLEND_REPLACE && + !(seq->type & SEQ_EFFECT)) { #if 0 // XXX old animation system if (seq->ipo && seq->ipo->curve.first) { do_seq_ipo(scene, seq, cfra); @@ -1897,10 +1903,14 @@ static void do_build_seq_ibuf(Scene *scene, Sequence * seq, TStripElem *se, int input_preprocess(scene, seq, se, cfra); } } else if(seq->type & SEQ_EFFECT) { + int use_preprocess = FALSE; /* should the effect be recalculated? */ if (!build_proxy_run && se->ibuf == 0) { se->ibuf = seq_proxy_fetch(scene, seq, cfra, render_size); + if (se->ibuf) { + use_preprocess = TRUE; + } } if(se->ibuf == 0) { @@ -1913,6 +1923,22 @@ static void do_build_seq_ibuf(Scene *scene, Sequence * seq, TStripElem *se, int se->ibuf= IMB_allocImBuf((short)seqrectx, (short)seqrecty, 32, IB_rect, 0); do_effect(scene, cfra, seq, se); + if (input_have_to_preprocess(scene, seq, se, cfra) && + !build_proxy_run) { + if ((se->se1 && (se->ibuf == se->se1->ibuf)) || + (se->se2 && (se->ibuf == se->se2->ibuf))) { + struct ImBuf * i + = IMB_dupImBuf(se->ibuf); + + IMB_freeImBuf(se->ibuf); + + se->ibuf = i; + } + use_preprocess = TRUE; + } + } + if (use_preprocess) { + input_preprocess(scene, seq, se, cfra); } } else if(seq->type == SEQ_IMAGE) { if(se->ok == STRIPELEM_OK && se->ibuf == 0) { @@ -1991,7 +2017,8 @@ static void do_build_seq_ibuf(Scene *scene, Sequence * seq, TStripElem *se, int RenderResult rres; int doseq, rendering= G.rendering; char scenename[64]; - int sce_valid =sce&& (sce->camera || sce->r.scemode & R_DOSEQ); + int have_seq= (sce->r.scemode & R_DOSEQ) && sce->ed && sce->ed->seqbase.first; + int sce_valid =sce && (sce->camera || have_seq); if (se->ibuf == NULL && sce_valid && !build_proxy_run) { se->ibuf = seq_proxy_fetch(scene, seq, cfra, render_size); @@ -2012,7 +2039,7 @@ static void do_build_seq_ibuf(Scene *scene, Sequence * seq, TStripElem *se, int } else if (se->ibuf==NULL && sce_valid) { /* no need to display a waitcursor on sequencer scene strips */ - if (!(sce->r.scemode & R_DOSEQ)) + if (!have_seq) waitcursor(1); /* Hack! This function can be called from do_render_seq(), in that case @@ -2067,8 +2094,8 @@ static void do_build_seq_ibuf(Scene *scene, Sequence * seq, TStripElem *se, int // XXX #if 0 if((G.f & G_PLAYANIM)==0 /* bad, is set on do_render_seq */ - && !(sce->r.scemode & R_DOSEQ)) - waitcursor(0); + && !have_seq + && !build_proxy_run) #endif CFRA = oldcfra; @@ -3116,6 +3143,17 @@ void update_changed_seq_and_deps(Scene *scene, Sequence *changed_seq, int len_ch update_changed_seq_recurs(scene, seq, changed_seq, len_change, ibuf_change); } +#if 0 // XXX from 2.4x, needs updating +void free_imbuf_seq() +{ + Scene * sce = G.main->scene.first; + while(sce) { + free_imbuf_seq_editing(sce->ed); + sce= sce->id.next; + } +} +#endif + void free_imbuf_seq_with_ipo(Scene *scene, struct Ipo *ipo) { /* force update of all sequences with this ipo, on ipo changes */ @@ -3140,9 +3178,14 @@ void free_imbuf_seq_with_ipo(Scene *scene, struct Ipo *ipo) /* bad levell call... */ void do_render_seq(RenderResult *rr, int cfra) { + static int recurs_depth = 0 ImBuf *ibuf; - ibuf= give_ibuf_seq(scene, rr->rectx, rr->recty, cfra, 0, scene->r.size); + recurs_depth++; + + ibuf= give_ibuf_seq(rr->rectx, rr->recty, cfra, 0, 100.0); + + recurs_depth--; if(ibuf) { if(ibuf->rect_float) { @@ -3179,7 +3222,7 @@ void do_render_seq(RenderResult *rr, int cfra) on freeing _all_ buffers every time on long timelines...) (schlaile) */ - { + if (recurs_depth == 0) { /* with nested scenes, only free on toplevel... */ uintptr_t mem_in_use; uintptr_t mmap_in_use; uintptr_t max; diff --git a/source/blender/blenkernel/intern/subsurf_ccg.c b/source/blender/blenkernel/intern/subsurf_ccg.c index 59f97ba13df..61f10239148 100644 --- a/source/blender/blenkernel/intern/subsurf_ccg.c +++ b/source/blender/blenkernel/intern/subsurf_ccg.c @@ -60,6 +60,7 @@ #include "BLI_edgehash.h" #include "BIF_gl.h" +#include "BIF_glutil.h" #include "GPU_draw.h" #include "GPU_extensions.h" diff --git a/source/blender/blenkernel/intern/texture.c b/source/blender/blenkernel/intern/texture.c index 66f7fe8a44b..bcdea06936f 100644 --- a/source/blender/blenkernel/intern/texture.c +++ b/source/blender/blenkernel/intern/texture.c @@ -784,7 +784,7 @@ Tex *give_current_texture(Object *ob, int act) if(act>ob->totcol) act= ob->totcol; else if(act==0) act= 1; - if( BTST(ob->colbits, act-1) ) { /* in object */ + if(ob->matbits[act-1]) { /* in object */ ma= ob->mat[act-1]; } else { /* in data */ diff --git a/source/blender/blenkernel/intern/writeavi.c b/source/blender/blenkernel/intern/writeavi.c index f84bd690347..cfbe3f629d6 100644 --- a/source/blender/blenkernel/intern/writeavi.c +++ b/source/blender/blenkernel/intern/writeavi.c @@ -40,6 +40,7 @@ #include "BLI_blenlib.h" #include "BKE_global.h" +#include "BKE_utildefines.h" #include "BKE_writeavi.h" #include "AVI_avi.h" @@ -87,7 +88,7 @@ bMovieHandle *BKE_get_movie_handle(int imtype) } #endif #ifdef WITH_FFMPEG - if (imtype == R_FFMPEG) { + if (ELEM4(imtype, R_FFMPEG, R_H264, R_XVID, R_THEORA)) { mh.start_movie = start_ffmpeg; mh.append_movie = append_ffmpeg; mh.end_movie = end_ffmpeg; diff --git a/source/blender/blenkernel/intern/writeffmpeg.c b/source/blender/blenkernel/intern/writeffmpeg.c index 25dc6fa2fd7..5e3c8024524 100644 --- a/source/blender/blenkernel/intern/writeffmpeg.c +++ b/source/blender/blenkernel/intern/writeffmpeg.c @@ -937,5 +937,322 @@ void end_ffmpeg(void) img_convert_ctx = 0; } } + +/* properties */ + +void ffmpeg_property_del(RenderData *rd, void *type, void *prop_) +{ + struct IDProperty *prop = (struct IDProperty *) prop_; + IDProperty * group; + + if (!rd->ffcodecdata.properties) { + return; + } + + group = IDP_GetPropertyFromGroup( + rd->ffcodecdata.properties, (char*) type); + if (group && prop) { + IDP_RemFromGroup(group, prop); + IDP_FreeProperty(prop); + MEM_freeN(prop); + } +} + +IDProperty *ffmpeg_property_add(RenderData *rd, char * type, int opt_index, int parent_index) +{ + AVCodecContext c; + const AVOption * o; + const AVOption * parent; + IDProperty * group; + IDProperty * prop; + IDPropertyTemplate val; + int idp_type; + char name[256]; + + avcodec_get_context_defaults(&c); + + o = c.av_class->option + opt_index; + parent = c.av_class->option + parent_index; + + if (!rd->ffcodecdata.properties) { + IDPropertyTemplate val; + + rd->ffcodecdata.properties + = IDP_New(IDP_GROUP, val, "ffmpeg"); + } + + group = IDP_GetPropertyFromGroup( + rd->ffcodecdata.properties, (char*) type); + + if (!group) { + IDPropertyTemplate val; + + group = IDP_New(IDP_GROUP, val, (char*) type); + IDP_AddToGroup(rd->ffcodecdata.properties, group); + } + + if (parent_index) { + sprintf(name, "%s:%s", parent->name, o->name); + } else { + strcpy(name, o->name); + } + + fprintf(stderr, "ffmpeg_property_add: %s %d %d %s\n", + type, parent_index, opt_index, name); + + prop = IDP_GetPropertyFromGroup(group, name); + if (prop) { + return prop; + } + + switch (o->type) { + case FF_OPT_TYPE_INT: + case FF_OPT_TYPE_INT64: + val.i = o->default_val; + idp_type = IDP_INT; + break; + case FF_OPT_TYPE_DOUBLE: + case FF_OPT_TYPE_FLOAT: + val.f = o->default_val; + idp_type = IDP_FLOAT; + break; + case FF_OPT_TYPE_STRING: + val.str = " "; + idp_type = IDP_STRING; + break; + case FF_OPT_TYPE_CONST: + val.i = 1; + idp_type = IDP_INT; + break; + default: + return NULL; + } + prop = IDP_New(idp_type, val, name); + IDP_AddToGroup(group, prop); + return prop; +} + +/* not all versions of ffmpeg include that, so here we go ... */ + +static const AVOption *my_av_find_opt(void *v, const char *name, + const char *unit, int mask, int flags){ + AVClass *c= *(AVClass**)v; + const AVOption *o= c->option; + + for(;o && o->name; o++){ + if(!strcmp(o->name, name) && + (!unit || (o->unit && !strcmp(o->unit, unit))) && + (o->flags & mask) == flags ) + return o; + } + return NULL; +} + +int ffmpeg_property_add_string(RenderData *rd, const char * type, const char * str) +{ + AVCodecContext c; + const AVOption * o = 0; + const AVOption * p = 0; + char name_[128]; + char * name; + char * param; + IDProperty * prop; + + avcodec_get_context_defaults(&c); + + strncpy(name_, str, 128); + + name = name_; + while (*name == ' ') name++; + + param = strchr(name, ':'); + + if (!param) { + param = strchr(name, ' '); + } + if (param) { + *param++ = 0; + while (*param == ' ') param++; + } + + o = my_av_find_opt(&c, name, NULL, 0, 0); + if (!o) { + return 0; + } + if (param && o->type == FF_OPT_TYPE_CONST) { + return 0; + } + if (param && o->type != FF_OPT_TYPE_CONST && o->unit) { + p = my_av_find_opt(&c, param, o->unit, 0, 0); + prop = ffmpeg_property_add(rd, + (char*) type, p - c.av_class->option, + o - c.av_class->option); + } else { + prop = ffmpeg_property_add(rd, + (char*) type, o - c.av_class->option, 0); + } + + + if (!prop) { + return 0; + } + + if (param && !p) { + switch (prop->type) { + case IDP_INT: + IDP_Int(prop) = atoi(param); + break; + case IDP_FLOAT: + IDP_Float(prop) = atof(param); + break; + case IDP_STRING: + strncpy(IDP_String(prop), param, prop->len); + break; + } + } + return 1; +} + +void ffmpeg_set_preset(RenderData *rd, int preset) +{ + int isntsc = (rd->frs_sec != 25); + + switch (preset) { + case FFMPEG_PRESET_VCD: + rd->ffcodecdata.type = FFMPEG_MPEG1; + rd->ffcodecdata.video_bitrate = 1150; + rd->xsch = 352; + rd->ysch = isntsc ? 240 : 288; + rd->ffcodecdata.gop_size = isntsc ? 18 : 15; + rd->ffcodecdata.rc_max_rate = 1150; + rd->ffcodecdata.rc_min_rate = 1150; + rd->ffcodecdata.rc_buffer_size = 40*8; + rd->ffcodecdata.mux_packet_size = 2324; + rd->ffcodecdata.mux_rate = 2352 * 75 * 8; + break; + + case FFMPEG_PRESET_SVCD: + rd->ffcodecdata.type = FFMPEG_MPEG2; + rd->ffcodecdata.video_bitrate = 2040; + rd->xsch = 480; + rd->ysch = isntsc ? 480 : 576; + rd->ffcodecdata.gop_size = isntsc ? 18 : 15; + rd->ffcodecdata.rc_max_rate = 2516; + rd->ffcodecdata.rc_min_rate = 0; + rd->ffcodecdata.rc_buffer_size = 224*8; + rd->ffcodecdata.mux_packet_size = 2324; + rd->ffcodecdata.mux_rate = 0; + break; + + case FFMPEG_PRESET_DVD: + rd->ffcodecdata.type = FFMPEG_MPEG2; + rd->ffcodecdata.video_bitrate = 6000; + rd->xsch = 720; + rd->ysch = isntsc ? 480 : 576; + rd->ffcodecdata.gop_size = isntsc ? 18 : 15; + rd->ffcodecdata.rc_max_rate = 9000; + rd->ffcodecdata.rc_min_rate = 0; + rd->ffcodecdata.rc_buffer_size = 224*8; + rd->ffcodecdata.mux_packet_size = 2048; + rd->ffcodecdata.mux_rate = 10080000; + break; + + case FFMPEG_PRESET_DV: + rd->ffcodecdata.type = FFMPEG_DV; + rd->xsch = 720; + rd->ysch = isntsc ? 480 : 576; + break; + + case FFMPEG_PRESET_H264: + rd->ffcodecdata.type = FFMPEG_AVI; + rd->ffcodecdata.codec = CODEC_ID_H264; + rd->ffcodecdata.video_bitrate = 6000; + rd->ffcodecdata.gop_size = isntsc ? 18 : 15; + rd->ffcodecdata.rc_max_rate = 9000; + rd->ffcodecdata.rc_min_rate = 0; + rd->ffcodecdata.rc_buffer_size = 224*8; + rd->ffcodecdata.mux_packet_size = 2048; + rd->ffcodecdata.mux_rate = 10080000; + + ffmpeg_property_add_string(rd, "video", "coder:vlc"); + ffmpeg_property_add_string(rd, "video", "flags:loop"); + ffmpeg_property_add_string(rd, "video", "cmp:chroma"); + ffmpeg_property_add_string(rd, "video", "partitions:parti4x4"); + ffmpeg_property_add_string(rd, "video", "partitions:partp8x8"); + ffmpeg_property_add_string(rd, "video", "partitions:partb8x8"); + ffmpeg_property_add_string(rd, "video", "me:hex"); + ffmpeg_property_add_string(rd, "video", "subq:5"); + ffmpeg_property_add_string(rd, "video", "me_range:16"); + ffmpeg_property_add_string(rd, "video", "keyint_min:25"); + ffmpeg_property_add_string(rd, "video", "sc_threshold:40"); + ffmpeg_property_add_string(rd, "video", "i_qfactor:0.71"); + ffmpeg_property_add_string(rd, "video", "b_strategy:1"); + + break; + + case FFMPEG_PRESET_THEORA: + case FFMPEG_PRESET_XVID: + if(preset == FFMPEG_PRESET_XVID) { + rd->ffcodecdata.type = FFMPEG_AVI; + rd->ffcodecdata.codec = CODEC_ID_XVID; + } + else if(preset == FFMPEG_PRESET_THEORA) { + rd->ffcodecdata.type = FFMPEG_OGG; // XXX broken + rd->ffcodecdata.codec = CODEC_ID_THEORA; + } + + rd->ffcodecdata.video_bitrate = 6000; + rd->ffcodecdata.gop_size = isntsc ? 18 : 15; + rd->ffcodecdata.rc_max_rate = 9000; + rd->ffcodecdata.rc_min_rate = 0; + rd->ffcodecdata.rc_buffer_size = 224*8; + rd->ffcodecdata.mux_packet_size = 2048; + rd->ffcodecdata.mux_rate = 10080000; + break; + + } +} + +void ffmpeg_verify_image_type(RenderData *rd) +{ + int audio= 0; + + if(rd->imtype == R_FFMPEG) { + if(rd->ffcodecdata.type <= 0 || + rd->ffcodecdata.codec <= 0 || + rd->ffcodecdata.audio_codec <= 0 || + rd->ffcodecdata.video_bitrate <= 1) { + + rd->ffcodecdata.codec = CODEC_ID_MPEG2VIDEO; + ffmpeg_set_preset(rd, FFMPEG_PRESET_DVD); + } + + audio= 1; + } + else if(rd->imtype == R_H264) { + if(rd->ffcodecdata.codec != CODEC_ID_H264) { + ffmpeg_set_preset(rd, FFMPEG_PRESET_H264); + audio= 1; + } + } + else if(rd->imtype == R_XVID) { + if(rd->ffcodecdata.codec != CODEC_ID_XVID) { + ffmpeg_set_preset(rd, FFMPEG_PRESET_XVID); + audio= 1; + } + } + else if(rd->imtype == R_THEORA) { + if(rd->ffcodecdata.codec != CODEC_ID_THEORA) { + ffmpeg_set_preset(rd, FFMPEG_PRESET_THEORA); + audio= 1; + } + } + + if(audio && rd->ffcodecdata.audio_codec <= 0) { + rd->ffcodecdata.audio_codec = CODEC_ID_MP2; + rd->ffcodecdata.audio_bitrate = 128; + } +} + #endif |