diff options
author | Andre Susano Pinto <andresusanopinto@gmail.com> | 2009-08-01 14:21:26 +0400 |
---|---|---|
committer | Andre Susano Pinto <andresusanopinto@gmail.com> | 2009-08-01 14:21:26 +0400 |
commit | c3a4936d9dc4abc6be5c74a16521d252893a2805 (patch) | |
tree | 0c4ef038058d84e37a0c302bef89240eb780d2e0 /source/blender/blenkernel | |
parent | 2830f25ff3bf7a80c88b86132f76081ced3e86a1 (diff) | |
parent | 78bbe5c479f80a331528d730486770c4f46e74fc (diff) |
svn merge -r 21508:22111 https://svn.blender.org/svnroot/bf-blender/branches/blender2.5/blender
Diffstat (limited to 'source/blender/blenkernel')
61 files changed, 9809 insertions, 3035 deletions
diff --git a/source/blender/blenkernel/BKE_action.h b/source/blender/blenkernel/BKE_action.h index f0796697670..d35acb5447a 100644 --- a/source/blender/blenkernel/BKE_action.h +++ b/source/blender/blenkernel/BKE_action.h @@ -1,6 +1,6 @@ /* BKE_action.h May 2001 * - * Blender kernel action functionality + * Blender kernel action and pose functionality * * Reevan McKay * @@ -26,7 +26,7 @@ * All rights reserved. * * Contributor(s): Full recode, Ton Roosendaal, Crete 2005 - * Full recode, Joshua Leung, 2009 + * Full recode, Joshua Leung, 2009 * * ***** END GPL LICENSE BLOCK ***** */ @@ -68,6 +68,9 @@ void make_local_action(struct bAction *act); /* Some kind of bounding box operation on the action */ void calc_action_range(const struct bAction *act, float *start, float *end, int incl_hidden); +/* Does action have any motion data at all? */ +short action_has_motion(const struct bAction *act); + /* Action Groups API ----------------- */ /* Make the given Action Group the active one */ @@ -100,8 +103,7 @@ void free_pose(struct bPose *pose); * Allocate a new pose on the heap, and copy the src pose and it's channels * into the new pose. *dst is set to the newly allocated structure, and assumed to be NULL. */ -void copy_pose(struct bPose **dst, struct bPose *src, - int copyconstraints); +void copy_pose(struct bPose **dst, struct bPose *src, int copyconstraints); @@ -109,9 +111,8 @@ void copy_pose(struct bPose **dst, struct bPose *src, * Return a pointer to the pose channel of the given name * from this pose. */ -struct bPoseChannel *get_pose_channel(const struct bPose *pose, - const char *name); - +struct bPoseChannel *get_pose_channel(const struct bPose *pose, const char *name); + /** * Return a pointer to the active pose channel from this Object. * (Note: Object, not bPose is used here, as we need layer info from Armature) @@ -123,8 +124,9 @@ struct bPoseChannel *get_active_posechannel(struct Object *ob); * already exists in this pose - if not a new one is * allocated and initialized. */ -struct bPoseChannel *verify_pose_channel(struct bPose* pose, - const char* name); +struct bPoseChannel *verify_pose_channel(struct bPose* pose, const char* name); + + /* sets constraint flags */ void update_pose_constraint_flags(struct bPose *pose); @@ -133,23 +135,29 @@ void update_pose_constraint_flags(struct bPose *pose); // XXX to be depreceated for a more general solution in animsys... void framechange_poses_clear_unkeyed(void); +/* Bone Groups API --------------------- */ + +/* Adds a new bone-group */ +void pose_add_group(struct Object *ob); + +/* Remove the active bone-group */ +void pose_remove_group(struct Object *ob); + +/* Assorted Evaluation ----------------- */ + /* Used for the Action Constraint */ void what_does_obaction(struct Scene *scene, struct Object *ob, struct Object *workob, struct bPose *pose, struct bAction *act, char groupname[], float cframe); -/* exported for game engine */ -void game_blend_poses(struct bPose *dst, struct bPose *src, float srcweight/*, short mode*/); /* was blend_poses */ -void extract_pose_from_pose(struct bPose *pose, const struct bPose *src); - /* for proxy */ void copy_pose_result(struct bPose *to, struct bPose *from); /* clear all transforms */ void rest_pose(struct bPose *pose); -/* map global time (frame nr) to strip converted time, doesn't clip */ -float get_action_frame(struct Object *ob, float cframe); -/* map strip time to global time (frame nr) */ -float get_action_frame_inv(struct Object *ob, float cframe); +/* Game Engine ------------------------- */ +/* exported for game engine */ +void game_blend_poses(struct bPose *dst, struct bPose *src, float srcweight/*, short mode*/); /* was blend_poses */ +void extract_pose_from_pose(struct bPose *pose, const struct bPose *src); /* functions used by the game engine */ void game_copy_pose(struct bPose **dst, struct bPose *src); diff --git a/source/blender/blenkernel/BKE_animsys.h b/source/blender/blenkernel/BKE_animsys.h index b45917e6ca1..2447d1823af 100644 --- a/source/blender/blenkernel/BKE_animsys.h +++ b/source/blender/blenkernel/BKE_animsys.h @@ -1,5 +1,28 @@ -/* 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. + * + * Contributor(s): Joshua Leung (original author) + * + * ***** END GPL LICENSE BLOCK ***** */ #ifndef BKE_ANIM_SYS_H diff --git a/source/blender/blenkernel/BKE_blender.h b/source/blender/blenkernel/BKE_blender.h index 795c7585b9c..19b9c315939 100644 --- a/source/blender/blenkernel/BKE_blender.h +++ b/source/blender/blenkernel/BKE_blender.h @@ -71,6 +71,7 @@ void pushpop_test(void); /* global undo */ extern void BKE_write_undo(struct bContext *C, char *name); extern void BKE_undo_step(struct bContext *C, int step); +extern void BKE_undo_name(struct bContext *C, const char *name); extern void BKE_reset_undo(void); extern char *BKE_undo_menu_string(void); extern void BKE_undo_number(struct bContext *C, int nr); diff --git a/source/blender/blenkernel/BKE_boids.h b/source/blender/blenkernel/BKE_boids.h new file mode 100644 index 00000000000..acceff863b9 --- /dev/null +++ b/source/blender/blenkernel/BKE_boids.h @@ -0,0 +1,62 @@ +/* BKE_particle.h + * + * + * $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 by Janne Karhu. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): none yet. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#ifndef BKE_BOIDS_H +#define BKE_BOIDS_H + +#include "DNA_boid_types.h" + +typedef struct BoidBrainData { + Scene *scene; + struct Object *ob; + struct ParticleSystem *psys; + struct ParticleSettings *part; + float timestep, cfra, dfra; + float wanted_co[3], wanted_speed; + + /* Goal stuff */ + struct Object *goal_ob; + float goal_co[3]; + float goal_nor[3]; + float goal_priority; +} BoidBrainData; + +void boids_precalc_rules(struct ParticleSettings *part, float cfra); +void boid_brain(BoidBrainData *bbd, int p, struct ParticleData *pa); +void boid_body(BoidBrainData *bbd, struct ParticleData *pa); +void boid_default_settings(BoidSettings *boids); +BoidRule *boid_new_rule(int type); +BoidState *boid_new_state(BoidSettings *boids); +BoidState *boid_duplicate_state(BoidSettings *boids, BoidState *state); +void boid_free_settings(BoidSettings *boids); +BoidSettings *boid_copy_settings(BoidSettings *boids); +BoidState *boid_get_current_state(BoidSettings *boids); +#endif diff --git a/source/blender/blenkernel/BKE_cloth.h b/source/blender/blenkernel/BKE_cloth.h index 4270c677338..f3165c959bf 100644 --- a/source/blender/blenkernel/BKE_cloth.h +++ b/source/blender/blenkernel/BKE_cloth.h @@ -65,8 +65,7 @@ struct CollisionTree; #elif defined (__sun) || defined (__sun__) # define DO_INLINE #else -# define DO_INLINE inline -# define LINUX +# define DO_INLINE static inline #endif #define CLOTH_MAX_THREAD 2 diff --git a/source/blender/blenkernel/BKE_colortools.h b/source/blender/blenkernel/BKE_colortools.h index 6f6c4a834df..c83a260690b 100644 --- a/source/blender/blenkernel/BKE_colortools.h +++ b/source/blender/blenkernel/BKE_colortools.h @@ -33,7 +33,16 @@ struct CurveMapping; struct CurveMap; struct ImBuf; struct rctf; - + +void gamma_correct_rec709(float *c, float gamma); +void gamma_correct(float *c, float gamma); +float srgb_to_linearrgb(float c); +float linearrgb_to_srgb(float c); +void color_manage_linearize(float *col_to, float *col_from); + +void floatbuf_to_srgb_byte(float *rectf, unsigned char *rectc, int x1, int x2, int y1, int y2, int w); +void floatbuf_to_byte(float *rectf, unsigned char *rectc, int x1, int x2, int y1, int y2, int w); + struct CurveMapping *curvemapping_add(int tot, float minx, float miny, float maxx, float maxy); void curvemapping_free(struct CurveMapping *cumap); struct CurveMapping *curvemapping_copy(struct CurveMapping *cumap); @@ -60,5 +69,6 @@ void curvemapping_initialize(struct CurveMapping *cumap); void curvemapping_table_RGBA(struct CurveMapping *cumap, float **array, int *size); void colorcorrection_do_ibuf(struct ImBuf *ibuf, const char *profile); + #endif diff --git a/source/blender/blenkernel/BKE_context.h b/source/blender/blenkernel/BKE_context.h index f536e117b7b..e47ad969b91 100644 --- a/source/blender/blenkernel/BKE_context.h +++ b/source/blender/blenkernel/BKE_context.h @@ -101,6 +101,10 @@ bContextStore *CTX_store_copy(bContextStore *store); void CTX_store_free(bContextStore *store); void CTX_store_free_list(ListBase *contexts); +/* need to store if python is initialized or not */ +int CTX_py_init_get(bContext *C); +void CTX_py_init_set(bContext *C, int value); + /* Window Manager Context */ struct wmWindowManager *CTX_wm_manager(const bContext *C); @@ -111,11 +115,24 @@ struct SpaceLink *CTX_wm_space_data(const bContext *C); struct ARegion *CTX_wm_region(const bContext *C); void *CTX_wm_region_data(const bContext *C); struct ARegion *CTX_wm_menu(const bContext *C); +struct ReportList *CTX_wm_reports(const bContext *C); struct View3D *CTX_wm_view3d(const bContext *C); struct RegionView3D *CTX_wm_region_view3d(const bContext *C); struct SpaceText *CTX_wm_space_text(const bContext *C); struct SpaceImage *CTX_wm_space_image(const bContext *C); +struct SpaceConsole *CTX_wm_space_console(const bContext *C); +struct SpaceButs *CTX_wm_space_buts(const bContext *C); +struct SpaceFile *CTX_wm_space_file(const bContext *C); +struct SpaceSeq *CTX_wm_space_seq(const bContext *C); +struct SpaceOops *CTX_wm_space_outliner(const bContext *C); +struct SpaceNla *CTX_wm_space_nla(const bContext *C); +struct SpaceTime *CTX_wm_space_time(const bContext *C); +struct SpaceNode *CTX_wm_space_node(const bContext *C); +struct SpaceLogic *CTX_wm_space_logic(const bContext *C); +struct SpaceIpo *CTX_wm_space_graph(const bContext *C); +struct SpaceAction *CTX_wm_space_action(const bContext *C); +struct SpaceInfo *CTX_wm_space_info(const bContext *C); void CTX_wm_manager_set(bContext *C, struct wmWindowManager *wm); void CTX_wm_window_set(bContext *C, struct wmWindow *win); @@ -186,6 +203,9 @@ int CTX_data_selected_bases(const bContext *C, ListBase *list); int CTX_data_visible_objects(const bContext *C, ListBase *list); int CTX_data_visible_bases(const bContext *C, ListBase *list); +int CTX_data_selectable_objects(const bContext *C, ListBase *list); +int CTX_data_selectable_bases(const bContext *C, ListBase *list); + struct Object *CTX_data_active_object(const bContext *C); struct Base *CTX_data_active_base(const bContext *C); struct Object *CTX_data_edit_object(const bContext *C); diff --git a/source/blender/blenkernel/BKE_fcurve.h b/source/blender/blenkernel/BKE_fcurve.h index 9b8a2990fe5..cda64c6b241 100644 --- a/source/blender/blenkernel/BKE_fcurve.h +++ b/source/blender/blenkernel/BKE_fcurve.h @@ -1,12 +1,33 @@ -/* 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. + * + * Contributor(s): Joshua Leung (full recode) + * + * ***** END GPL LICENSE BLOCK ***** */ #ifndef BKE_FCURVE_H #define BKE_FCURVE_H -//struct ListBase; - struct FCurve; struct FModifier; struct ChannelDriver; @@ -54,8 +75,8 @@ typedef struct FModifierTypeInfo { short size; /* size in bytes of the struct */ short acttype; /* eFMI_Action_Types */ short requires; /* eFMI_Requirement_Flags */ - char name[32]; /* name of modifier in interface */ - char structName[32]; /* name of struct for SDNA */ + char name[64]; /* name of modifier in interface */ + char structName[64]; /* name of struct for SDNA */ /* data management function pointers - special handling */ /* free any data that is allocated separately (optional) */ @@ -104,14 +125,20 @@ FModifierTypeInfo *get_fmodifier_typeinfo(int type); /* ---------------------- */ -struct FModifier *fcurve_add_modifier(struct FCurve *fcu, int type); -void fcurve_copy_modifiers(ListBase *dst, ListBase *src); -void fcurve_remove_modifier(struct FCurve *fcu, struct FModifier *fcm); -void fcurve_free_modifiers(struct FCurve *fcu); -void fcurve_bake_modifiers(struct FCurve *fcu, int start, int end); +struct FModifier *add_fmodifier(ListBase *modifiers, int type); +void copy_fmodifiers(ListBase *dst, ListBase *src); +void remove_fmodifier(ListBase *modifiers, struct FModifier *fcm); +void free_fmodifiers(ListBase *modifiers); + +struct FModifier *find_active_fmodifier(ListBase *modifiers); +void set_active_fmodifier(ListBase *modifiers, struct FModifier *fcm); + +short list_has_suitable_fmodifier(ListBase *modifiers, int mtype, short acttype); -struct FModifier *fcurve_find_active_modifier(struct FCurve *fcu); -void fcurve_set_active_modifier(struct FCurve *fcu, struct FModifier *fcm); +float evaluate_time_fmodifiers(ListBase *modifiers, struct FCurve *fcu, float cvalue, float evaltime); +void evaluate_value_fmodifiers(ListBase *modifiers, struct FCurve *fcu, float *cvalue, float evaltime); + +void fcurve_bake_modifiers(struct FCurve *fcu, int start, int end); /* ************** F-Curves API ******************** */ @@ -126,9 +153,6 @@ void copy_fcurves(ListBase *dst, ListBase *src); /* find matching F-Curve in the given list of F-Curves */ struct FCurve *list_find_fcurve(ListBase *list, const char rna_path[], const int array_index); -/* test if there is a keyframe at cfra */ -short on_keyframe_fcurve(struct FCurve *fcu, float cfra); - /* get the time extents for F-Curve */ void calc_fcurve_range(struct FCurve *fcu, float *min, float *max); diff --git a/source/blender/blenkernel/BKE_modifier.h b/source/blender/blenkernel/BKE_modifier.h index 144ed3bc624..b65d77751e2 100644 --- a/source/blender/blenkernel/BKE_modifier.h +++ b/source/blender/blenkernel/BKE_modifier.h @@ -33,6 +33,7 @@ #include "DNA_modifier_types.h" /* needed for all enum typdefs */ #include "BKE_customdata.h" +struct ID; struct EditMesh; struct DerivedMesh; struct DagForest; diff --git a/source/blender/blenkernel/BKE_nla.h b/source/blender/blenkernel/BKE_nla.h index 230096d7ea7..241a8fd7b59 100644 --- a/source/blender/blenkernel/BKE_nla.h +++ b/source/blender/blenkernel/BKE_nla.h @@ -17,12 +17,12 @@ * 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 ***** */ @@ -30,15 +30,92 @@ #ifndef BKE_NLA_H #define BKE_NLA_H -struct bActionStrip; -struct ListBase; -struct Object; +struct AnimData; +struct NlaStrip; +struct NlaTrack; +struct bAction; + +/* ----------------------------- */ +/* Data Management */ + +void free_nlastrip(ListBase *strips, struct NlaStrip *strip); +void free_nlatrack(ListBase *tracks, struct NlaTrack *nlt); +void free_nladata(ListBase *tracks); + +struct NlaStrip *copy_nlastrip(struct NlaStrip *strip); +struct NlaTrack *copy_nlatrack(struct NlaTrack *nlt); +void copy_nladata(ListBase *dst, ListBase *src); + +struct NlaTrack *add_nlatrack(struct AnimData *adt, struct NlaTrack *prev); +struct NlaStrip *add_nlastrip(struct bAction *act); +struct NlaStrip *add_nlastrip_to_stack(struct AnimData *adt, struct bAction *act); + +/* ----------------------------- */ +/* API */ + +short BKE_nlastrips_has_space(ListBase *strips, float start, float end); +void BKE_nlastrips_sort_strips(ListBase *strips); + +short BKE_nlastrips_add_strip(ListBase *strips, struct NlaStrip *strip); + + +void BKE_nlastrips_make_metas(ListBase *strips, short temp); +void BKE_nlastrips_clear_metas(ListBase *strips, short onlySel, short onlyTemp); +void BKE_nlastrips_clear_metastrip(ListBase *strips, struct NlaStrip *strip); +short BKE_nlameta_add_strip(struct NlaStrip *mstrip, struct NlaStrip *strip); +void BKE_nlameta_flush_transforms(struct NlaStrip *mstrip); + +/* ............ */ + +struct NlaTrack *BKE_nlatrack_find_active(ListBase *tracks); +void BKE_nlatrack_set_active(ListBase *tracks, struct NlaTrack *nlt); + +void BKE_nlatrack_solo_toggle(struct AnimData *adt, struct NlaTrack *nlt); + +short BKE_nlatrack_has_space(struct NlaTrack *nlt, float start, float end); +void BKE_nlatrack_sort_strips(struct NlaTrack *nlt); + +short BKE_nlatrack_add_strip(struct NlaTrack *nlt, struct NlaStrip *strip); + +/* ............ */ + +struct NlaStrip *BKE_nlastrip_find_active(struct NlaTrack *nlt); + +short BKE_nlastrip_within_bounds(struct NlaStrip *strip, float min, float max); + +void BKE_nlastrip_validate_name(struct AnimData *adt, struct NlaStrip *strip); + +/* ............ */ + +short BKE_nlatrack_has_animated_strips(struct NlaTrack *nlt); +short BKE_nlatracks_have_animated_strips(ListBase *tracks); +void BKE_nlastrip_validate_fcurves(struct NlaStrip *strip); + +void BKE_nla_validate_state(struct AnimData *adt); + +/* ............ */ + +void BKE_nla_action_pushdown(struct AnimData *adt); + +short BKE_nla_tweakmode_enter(struct AnimData *adt); +void BKE_nla_tweakmode_exit(struct AnimData *adt); + +/* ----------------------------- */ +/* Time Mapping */ + +/* time mapping conversion modes */ +enum { + /* convert from global time to strip time - for evaluation */ + NLATIME_CONVERT_EVAL = 0, + /* convert from global time to strip time - for editing corrections */ + // XXX old 0 invert + NLATIME_CONVERT_UNMAP, + /* convert from strip time to global time */ + // xxx old 1 invert + NLATIME_CONVERT_MAP, +} eNlaTime_ConvertModes; + +float BKE_nla_tweakedit_remap(struct AnimData *adt, float cframe, short mode); -void free_actionstrip (struct bActionStrip* strip); -void free_nlastrips (struct ListBase *nlalist); -void copy_nlastrips (struct ListBase *dst, struct ListBase *src); -void copy_actionstrip (struct bActionStrip **dst, struct bActionStrip **src); -void find_stridechannel(struct Object *ob, struct bActionStrip *strip); -struct bActionStrip *convert_action_to_strip (struct Object *ob); #endif diff --git a/source/blender/blenkernel/BKE_object.h b/source/blender/blenkernel/BKE_object.h index 3d71193f37a..a57529ccf75 100644 --- a/source/blender/blenkernel/BKE_object.h +++ b/source/blender/blenkernel/BKE_object.h @@ -98,11 +98,6 @@ void object_to_mat4(struct Object *ob, float mat[][4]); void set_no_parent_ipo(int val); -void disable_where_script(short on); -int during_script(void); -void disable_where_scriptlink(short on); -int during_scriptlink(void); - void where_is_object_time(struct Scene *scene, struct Object *ob, float ctime); void where_is_object(struct Scene *scene, struct Object *ob); void where_is_object_simul(struct Scene *scene, struct Object *ob); diff --git a/source/blender/blenkernel/BKE_particle.h b/source/blender/blenkernel/BKE_particle.h index aa24706077d..cf02efc34ac 100644 --- a/source/blender/blenkernel/BKE_particle.h +++ b/source/blender/blenkernel/BKE_particle.h @@ -55,6 +55,9 @@ struct IpoCurve; struct LinkNode; struct KDTree; struct RNG; +struct SurfaceModifierData; +struct BVHTreeRay; +struct BVHTreeRayHit; typedef struct ParticleEffectorCache { struct ParticleEffectorCache *next, *prev; @@ -95,16 +98,6 @@ typedef struct ParticleTexture{ float rough1, rough2, roughe; /* used in path caching */ } ParticleTexture; -typedef struct BoidVecFunc{ - void (*Addf)(float *v, float *v1, float *v2); - void (*Subf)(float *v, float *v1, float *v2); - void (*Mulf)(float *v, float f); - float (*Length)(float *v); - float (*Normalize)(float *v); - float (*Inpf)(float *v1, float *v2); - void (*Copyf)(float *v1, float *v2); -} BoidVecFunc; - typedef struct ParticleSeam{ float v0[3], v1[3]; float nor[3], dir[3], tan[3]; @@ -209,6 +202,19 @@ typedef struct ParticleBillboardData } ParticleBillboardData; +/* container for moving data between deflet_particle and particle_intersect_face */ +typedef struct ParticleCollision +{ + struct Object *ob, *ob_t; // collided and current objects + struct CollisionModifierData *md; // collision modifier for ob_t; + float nor[3]; // normal at collision point + float vel[3]; // velocity of collision point + float co1[3], co2[3]; // ray start and end points + float ray_len; // original length of co2-co1, needed for collision time evaluation + float t; // time of previous collision, needed for substracting face velocity +} +ParticleCollision; + /* ----------- functions needed outside particlesystem ---------------- */ /* particle.c */ int count_particles(struct ParticleSystem *psys); @@ -231,6 +237,7 @@ int psys_ob_has_hair(struct Object *ob); int psys_in_edit_mode(struct Scene *scene, struct ParticleSystem *psys); int psys_check_enabled(struct Object *ob, struct ParticleSystem *psys); +void psys_free_boid_rules(struct ListBase *list); void psys_free_settings(struct ParticleSettings *part); void free_child_path_cache(struct ParticleSystem *psys); void psys_free_path_cache(struct ParticleSystem *psys); @@ -260,7 +267,6 @@ void psys_flush_particle_settings(struct Scene *scene, struct ParticleSettings * void make_local_particlesettings(struct ParticleSettings *part); struct LinkNode *psys_using_settings(struct Scene *scene, struct ParticleSettings *part, int flush_update); -void psys_changed_type(struct ParticleSystem *psys); void psys_reset(struct ParticleSystem *psys, int mode); void psys_find_parents(struct Object *ob, struct ParticleSystemModifierData *psmd, struct ParticleSystem *psys); @@ -289,14 +295,19 @@ void psys_thread_create_path(ParticleThread *thread, struct ChildParticle *cpa, void psys_make_billboard(ParticleBillboardData *bb, float xvec[3], float yvec[3], float zvec[3], float center[3]); /* particle_system.c */ -int psys_count_keyed_targets(struct Object *ob, struct ParticleSystem *psys); +struct ParticleSystem *psys_get_target_system(struct Object *ob, struct ParticleTarget *pt); +void psys_count_keyed_targets(struct Object *ob, struct ParticleSystem *psys); void psys_get_reactor_target(struct Object *ob, struct ParticleSystem *psys, struct Object **target_ob, struct ParticleSystem **target_psys); void psys_init_effectors(struct Scene *scene, struct Object *obsrc, struct Group *group, struct ParticleSystem *psys); void psys_end_effectors(struct ParticleSystem *psys); +void psys_make_temp_pointcache(struct Object *ob, struct ParticleSystem *psys); +void psys_end_temp_pointcache(struct ParticleSystem *psys); void psys_get_pointcache_start_end(struct Scene *scene, struct ParticleSystem *psys, int *sfra, int *efra); +void psys_check_boid_data(struct ParticleSystem *psys); + void particle_system_update(struct Scene *scene, struct Object *ob, struct ParticleSystem *psys); /* ----------- functions needed only inside particlesystem ------------ */ @@ -320,11 +331,13 @@ float psys_interpolate_value_from_verts(struct DerivedMesh *dm, short from, int void psys_get_from_key(struct ParticleKey *key, float *loc, float *vel, float *rot, float *time); int psys_intersect_dm(struct Scene *scene, struct Object *ob, struct DerivedMesh *dm, float *vert_cos, float *co1, float* co2, float *min_d, int *min_face, float *min_uv, float *face_minmax, float *pa_minmax, float radius, float *ipoint); +void particle_intersect_face(void *userdata, int index, const struct BVHTreeRay *ray, struct BVHTreeRayHit *hit); void psys_particle_on_dm(struct DerivedMesh *dm, int from, int index, int index_dmcache, float *fw, float foffset, float *vec, float *nor, float *utan, float *vtan, float *orco, float *ornor); /* particle_system.c */ void initialize_particle(struct ParticleData *pa, int p, struct Object *ob, struct ParticleSystem *psys, struct ParticleSystemModifierData *psmd); +int effector_find_co(struct Scene *scene, float *pco, struct SurfaceModifierData *sur, struct Object *ob, struct PartDeflect *pd, float *co, float *nor, float *vel, int *index); void do_effectors(int pa_no, struct ParticleData *pa, struct ParticleKey *state, struct Scene *scene, struct Object *ob, struct ParticleSystem *psys, float *texco, float *force_field, float *vel,float framestep, float cfra); void psys_calc_dmcache(struct Object *ob, struct DerivedMesh *dm, struct ParticleSystem *psys); diff --git a/source/blender/blenkernel/BKE_pointcache.h b/source/blender/blenkernel/BKE_pointcache.h index c5d423c13ba..8062f807055 100644 --- a/source/blender/blenkernel/BKE_pointcache.h +++ b/source/blender/blenkernel/BKE_pointcache.h @@ -166,4 +166,6 @@ void BKE_ptcache_quick_cache_all(struct Scene *scene); void BKE_ptcache_make_cache(struct PTCacheBaker* baker); void BKE_ptcache_toggle_disk_cache(struct PTCacheID *pid); +void BKE_ptcache_load_external(struct PTCacheID *pid); + #endif diff --git a/source/blender/blenkernel/BKE_property.h b/source/blender/blenkernel/BKE_property.h index 6af1deda727..78a9ecddaac 100644 --- a/source/blender/blenkernel/BKE_property.h +++ b/source/blender/blenkernel/BKE_property.h @@ -41,6 +41,7 @@ struct bProperty *copy_property(struct bProperty *prop); void copy_properties(struct ListBase *lbn, struct ListBase *lbo); void init_property(struct bProperty *prop); struct bProperty *new_property(int type); +void unique_property(struct bProperty *first, struct bProperty *prop, int force); struct bProperty *get_ob_property(struct Object *ob, char *name); void set_ob_property(struct Object *ob, struct bProperty *propc); int compare_property(struct bProperty *prop, char *str); diff --git a/source/blender/blenkernel/BKE_report.h b/source/blender/blenkernel/BKE_report.h index 1bb7152fbf3..1d72b1c81f0 100644 --- a/source/blender/blenkernel/BKE_report.h +++ b/source/blender/blenkernel/BKE_report.h @@ -32,41 +32,14 @@ extern "C" { #endif -#include "DNA_listBase.h" +#include "DNA_windowmanager_types.h" /* Reporting Information and Errors * * These functions also accept NULL in case no error reporting * is needed. */ -typedef enum ReportType { - RPT_DEBUG = 0, - RPT_INFO = 1000, - RPT_WARNING = 2000, - RPT_ERROR = 3000, - RPT_ERROR_INVALID_INPUT = 3001, - RPT_ERROR_INVALID_CONTEXT = 3002, - RPT_ERROR_OUT_OF_MEMORY = 3003 -} ReportType; - -enum ReportListFlags { - RPT_PRINT = 1, - RPT_STORE = 2, -}; - -typedef struct Report { - struct Report *next, *prev; - ReportType type; - char *typestr; - char *message; -} Report; - -typedef struct ReportList { - ListBase list; - ReportType printlevel; - ReportType storelevel; - int flag; -} ReportList; +/* report structures are stored in DNA */ void BKE_reports_init(ReportList *reports, int flag); void BKE_reports_clear(ReportList *reports); diff --git a/source/blender/blenkernel/BKE_screen.h b/source/blender/blenkernel/BKE_screen.h index be625fb856a..4fcb7c881be 100644 --- a/source/blender/blenkernel/BKE_screen.h +++ b/source/blender/blenkernel/BKE_screen.h @@ -46,10 +46,8 @@ struct wmWindow; struct wmWindowManager; struct uiLayout; struct uiMenuItem; -struct StructRNA; -struct PointerRNA; -struct FunctionRNA; -struct ParameterList; + +#include "RNA_types.h" /* spacetype has everything stored to get an editor working, it gets initialized via ED_spacetypes_init() in editors/area/spacetypes.c */ @@ -169,11 +167,8 @@ typedef struct PanelType { /* draw entirely, view changes should be handled here */ void (*draw)(const struct bContext *, struct Panel *); - /* python integration */ - void *py_data; - struct StructRNA *py_srna; - int (*py_call)(struct PointerRNA *, struct FunctionRNA *, struct ParameterList *); - void (*py_free)(void *py_data); + /* RNA integration */ + ExtensionRNA ext; } PanelType; /* header types */ @@ -187,13 +182,16 @@ typedef struct HeaderType { /* draw entirely, view changes should be handled here */ void (*draw)(const struct bContext *, struct Header *); - /* python integration */ - void *py_data; - struct StructRNA *py_srna; - int (*py_call)(struct PointerRNA *, struct FunctionRNA *, struct ParameterList *); - void (*py_free)(void *py_data); + /* RNA integration */ + ExtensionRNA ext; } HeaderType; +typedef struct Header { + struct HeaderType *type; /* runtime */ + struct uiLayout *layout; /* runtime for drawing */ +} Header; + + /* menu types */ typedef struct MenuType { @@ -208,13 +206,15 @@ typedef struct MenuType { /* draw entirely, view changes should be handled here */ void (*draw)(const struct bContext *, struct Menu *); - /* python integration */ - void *py_data; - struct StructRNA *py_srna; - int (*py_call)(struct PointerRNA *, struct FunctionRNA *, struct ParameterList *); - void (*py_free)(void *py_data); + /* RNA integration */ + ExtensionRNA ext; } MenuType; +typedef struct Menu { + struct MenuType *type; /* runtime */ + struct uiLayout *layout; /* runtime for drawing */ +} Menu; + /* spacetypes */ struct SpaceType *BKE_spacetype_from_id(int spaceid); struct ARegionType *BKE_regiontype_from_id(struct SpaceType *st, int regionid); diff --git a/source/blender/blenkernel/BKE_sketch.h b/source/blender/blenkernel/BKE_sketch.h new file mode 100644 index 00000000000..424dd5c9f80 --- /dev/null +++ b/source/blender/blenkernel/BKE_sketch.h @@ -0,0 +1,157 @@ +/** + * + * $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. + * + * Contributor(s): none yet. + * + * ***** END GPL LICENSE BLOCK ***** + */ +#ifndef BKE_SKETCH_H +#define BKE_SKETCH_H + +typedef enum SK_PType +{ + PT_CONTINUOUS, + PT_EXACT, +} SK_PType; + +typedef enum SK_PMode +{ + PT_SNAP, + PT_PROJECT, +} SK_PMode; + +typedef struct SK_Point +{ + float p[3]; + short p2d[2]; + float no[3]; + float size; + SK_PType type; + SK_PMode mode; +} SK_Point; + +typedef struct SK_Stroke +{ + struct SK_Stroke *next, *prev; + + SK_Point *points; + int nb_points; + int buf_size; + int selected; +} SK_Stroke; + +#define SK_OVERDRAW_LIMIT 5 + +typedef struct SK_Overdraw +{ + SK_Stroke *target; + int start, end; + int count; +} SK_Overdraw; + +#define SK_Stroke_BUFFER_INIT_SIZE 20 + +typedef struct SK_DrawData +{ + short mval[2]; + short previous_mval[2]; + SK_PType type; +} SK_DrawData; + +typedef struct SK_Intersection +{ + struct SK_Intersection *next, *prev; + SK_Stroke *stroke; + int before; + int after; + int gesture_index; + float p[3]; + float lambda; /* used for sorting intersection points */ +} SK_Intersection; + +typedef struct SK_Sketch +{ + ListBase strokes; + ListBase depth_peels; + SK_Stroke *active_stroke; + SK_Stroke *gesture; + SK_Point next_point; + SK_Overdraw over; +} SK_Sketch; + + +typedef struct SK_Gesture { + SK_Stroke *stk; + SK_Stroke *segments; + + ListBase intersections; + ListBase self_intersections; + + int nb_self_intersections; + int nb_intersections; + int nb_segments; +} SK_Gesture; + + +/************************************************/ + +void freeSketch(SK_Sketch *sketch); +SK_Sketch* createSketch(); + +void sk_removeStroke(SK_Sketch *sketch, SK_Stroke *stk); + +void sk_freeStroke(SK_Stroke *stk); +SK_Stroke* sk_createStroke(); + +SK_Point *sk_lastStrokePoint(SK_Stroke *stk); + +void sk_allocStrokeBuffer(SK_Stroke *stk); +void sk_shrinkStrokeBuffer(SK_Stroke *stk); +void sk_growStrokeBuffer(SK_Stroke *stk); +void sk_growStrokeBufferN(SK_Stroke *stk, int n); + +void sk_replaceStrokePoint(SK_Stroke *stk, SK_Point *pt, int n); +void sk_insertStrokePoint(SK_Stroke *stk, SK_Point *pt, int n); +void sk_appendStrokePoint(SK_Stroke *stk, SK_Point *pt); +void sk_insertStrokePoints(SK_Stroke *stk, SK_Point *pts, int len, int start, int end); + +void sk_trimStroke(SK_Stroke *stk, int start, int end); +void sk_straightenStroke(SK_Stroke *stk, int start, int end, float p_start[3], float p_end[3]); +void sk_polygonizeStroke(SK_Stroke *stk, int start, int end); +void sk_flattenStroke(SK_Stroke *stk, int start, int end); +void sk_reverseStroke(SK_Stroke *stk); + +void sk_filterLastContinuousStroke(SK_Stroke *stk); +void sk_filterStroke(SK_Stroke *stk, int start, int end); + +void sk_initPoint(SK_Point *pt, SK_DrawData *dd, float *no); +void sk_copyPoint(SK_Point *dst, SK_Point *src); + +int sk_stroke_filtermval(SK_DrawData *dd); +void sk_endContinuousStroke(SK_Stroke *stk); + +void sk_updateNextPoint(SK_Sketch *sketch, SK_Stroke *stk); + +void sk_initDrawData(SK_DrawData *dd, short mval[2]); + +void sk_deleteSelectedStrokes(SK_Sketch *sketch); +void sk_selectAllSketch(SK_Sketch *sketch, int mode); + +#endif diff --git a/source/blender/blenkernel/BKE_smoke.h b/source/blender/blenkernel/BKE_smoke.h new file mode 100644 index 00000000000..b37e1d08fcc --- /dev/null +++ b/source/blender/blenkernel/BKE_smoke.h @@ -0,0 +1,55 @@ +/** + * BKE_cloth.h + * + * $Id$ + * + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * The Original Code is Copyright (C) Blender Foundation. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): Daniel Genrich + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#ifndef BKE_SMOKE_H_ +#define BKE_SMOKE_H_ + +void smokeModifier_do(struct SmokeModifierData *smd, struct Scene *scene, struct Object *ob, struct DerivedMesh *dm); + +void smokeModifier_free (struct SmokeModifierData *smd); +void smokeModifier_reset(struct SmokeModifierData *smd); +void smokeModifier_createType(struct SmokeModifierData *smd); + +void smoke_set_tray(struct SmokeModifierData *smd, size_t index, float transparency); +float smoke_get_tray(struct SmokeModifierData *smd, size_t index); +float smoke_get_tvox(struct SmokeModifierData *smd, size_t index); +void smoke_set_tvox(struct SmokeModifierData *smd, size_t index, float tvox); + +void smoke_set_bigtray(struct SmokeModifierData *smd, size_t index, float transparency); +float smoke_get_bigtray(struct SmokeModifierData *smd, size_t index); +float smoke_get_bigtvox(struct SmokeModifierData *smd, size_t index); +void smoke_set_bigtvox(struct SmokeModifierData *smd, size_t index, float tvox); + +long long smoke_get_mem_req(int xres, int yres, int zres, int amplify); +void smoke_prepare_View(struct SmokeModifierData *smd, float *light); +void smoke_prepare_bigView(struct SmokeModifierData *smd, float *light); + +#endif /* BKE_SMOKE_H_ */ diff --git a/source/blender/blenkernel/BKE_softbody.h b/source/blender/blenkernel/BKE_softbody.h index 05a9bfb0ca9..971ac7a5f01 100644 --- a/source/blender/blenkernel/BKE_softbody.h +++ b/source/blender/blenkernel/BKE_softbody.h @@ -44,7 +44,9 @@ typedef struct BodyPoint { float choke,choke2,frozen; float colball; short flag; - char octantflag; + //char octantflag; + float mass; + float springweight; } BodyPoint; /* allocates and initializes general main data */ diff --git a/source/blender/blenkernel/BKE_utildefines.h b/source/blender/blenkernel/BKE_utildefines.h index 1c88b94df85..f5287e4e100 100644 --- a/source/blender/blenkernel/BKE_utildefines.h +++ b/source/blender/blenkernel/BKE_utildefines.h @@ -136,6 +136,7 @@ #define IS_EQT(a, b, c) ((a > b)? (((a-b) <= c)? 1:0) : ((((b-a) <= c)? 1:0))) #define IN_RANGE(a, b, c) ((b < c)? ((b<a && a<c)? 1:0) : ((c<a && a<b)? 1:0)) +#define IN_RANGE_INCL(a, b, c) ((b < c)? ((b<=a && a<=c)? 1:0) : ((c<=a && a<=b)? 1:0)) /* this weirdo pops up in two places ... */ #if !defined(WIN32) diff --git a/source/blender/blenkernel/BKE_writeffmpeg.h b/source/blender/blenkernel/BKE_writeffmpeg.h index dba3944a58d..be136bd9d6d 100644 --- a/source/blender/blenkernel/BKE_writeffmpeg.h +++ b/source/blender/blenkernel/BKE_writeffmpeg.h @@ -46,19 +46,29 @@ extern "C" { #define FFMPEG_MKV 9 #define FFMPEG_OGG 10 -#define FFMPEG_PRESET_NONE 0 -#define FFMPEG_PRESET_DVD 1 -#define FFMPEG_PRESET_SVCD 2 -#define FFMPEG_PRESET_VCD 3 -#define FFMPEG_PRESET_DV 4 -#define FFMPEG_PRESET_H264 5 +#define FFMPEG_PRESET_NONE 0 +#define FFMPEG_PRESET_DVD 1 +#define FFMPEG_PRESET_SVCD 2 +#define FFMPEG_PRESET_VCD 3 +#define FFMPEG_PRESET_DV 4 +#define FFMPEG_PRESET_H264 5 +#define FFMPEG_PRESET_THEORA 6 +#define FFMPEG_PRESET_XVID 7 +struct IDProperty; struct RenderData; extern void start_ffmpeg(struct RenderData *rd, int rectx, int recty); extern void end_ffmpeg(void); extern void append_ffmpeg(struct RenderData *rd, int frame, int *pixels, int rectx, int recty); +extern void ffmpeg_set_preset(struct RenderData *rd, int preset); +extern void ffmpeg_verify_image_type(struct RenderData *rd); + +extern struct IDProperty *ffmpeg_property_add(struct RenderData *Rd, char *type, int opt_index, int parent_index); +extern int ffmpeg_property_add_string(struct RenderData *rd, const char *type, const char *str); +extern void ffmpeg_property_del(struct RenderData *rd, void *type, void *prop_); + #ifdef __cplusplus } #endif diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt index ebe0ea74c28..656a292da15 100644 --- a/source/blender/blenkernel/CMakeLists.txt +++ b/source/blender/blenkernel/CMakeLists.txt @@ -32,9 +32,8 @@ SET(INC ../imbuf ../avi ../../../intern/elbeem/extern ../../../intern/opennl/extern ../../../intern/iksolver/extern ../blenloader ../quicktime ../../../extern/bullet2/src - ../nodes ../../../extern/glew/include ../gpu ../makesrna + ../nodes ../../../extern/glew/include ../gpu ../makesrna ../../../intern/smoke/extern ../../../intern/bsp/extern - ${SDL_INC} ${ZLIB_INC} ) @@ -55,6 +54,12 @@ IF(WITH_QUICKTIME) ADD_DEFINITIONS(-DWITH_QUICKTIME) ENDIF(WITH_QUICKTIME) +IF(WITH_SDL) + SET(INC ${INC} ${SDL_INC}) +ELSE(WITH_SDL) + ADD_DEFINITIONS(-DDISABLE_SDL) +ENDIF(WITH_SDL) + IF(WITH_FFMPEG) SET(INC ${INC} ${FFMPEG_INC}) ADD_DEFINITIONS(-DWITH_FFMPEG) diff --git a/source/blender/blenkernel/SConscript b/source/blender/blenkernel/SConscript index dbc990d0613..7016d992f02 100644 --- a/source/blender/blenkernel/SConscript +++ b/source/blender/blenkernel/SConscript @@ -10,6 +10,7 @@ incs += ' #/intern/iksolver/extern ../blenloader' incs += ' #/extern/bullet2/src' incs += ' #/intern/opennl/extern #/intern/bsp/extern' incs += ' ../gpu #/extern/glew/include' +incs += ' #/intern/smoke/extern' incs += ' ' + env['BF_OPENGL_INC'] incs += ' ' + env['BF_ZLIB_INC'] diff --git a/source/blender/blenkernel/intern/Makefile b/source/blender/blenkernel/intern/Makefile index a6a5066b574..fe7187200aa 100644 --- a/source/blender/blenkernel/intern/Makefile +++ b/source/blender/blenkernel/intern/Makefile @@ -68,6 +68,7 @@ CPPFLAGS += -I$(NAN_DECIMATION)/include CPPFLAGS += -I$(NAN_ELBEEM)/include CPPFLAGS += -I$(NAN_OPENNL)/include CPPFLAGS += -I$(NAN_BSP)/include +CPPFLAGS += -I$(NAN_SMOKE)/include # path to zlib CPPFLAGS += -I$(NAN_ZLIB)/include diff --git a/source/blender/blenkernel/intern/action.c b/source/blender/blenkernel/intern/action.c index f7e15cef4c4..f4d4eb1cc9c 100644 --- a/source/blender/blenkernel/intern/action.c +++ b/source/blender/blenkernel/intern/action.c @@ -21,6 +21,7 @@ * All rights reserved. * * Contributor(s): Full recode, Ton Roosendaal, Crete 2005 + * Full recode, Joshua Leung, 2009 * * ***** END GPL LICENSE BLOCK ***** */ @@ -31,7 +32,8 @@ #include <string.h> #include <math.h> -#include <stdlib.h> /* for NULL */ +#include <stdlib.h> +#include <stddef.h> #include "MEM_guardedalloc.h" @@ -68,8 +70,6 @@ #include "RNA_access.h" #include "RNA_types.h" -//XXX #include "nla.h" - /* *********************** NOTE ON POSE AND ACTION ********************** - Pose is the local (object level) component of armature. The current @@ -765,74 +765,77 @@ void framechange_poses_clear_unkeyed(void) } } -/* ************************ END Pose channels *************** */ - -/* ************** time ****************** */ +/* ************************** Bone Groups ************************** */ -static bActionStrip *get_active_strip(Object *ob) +/* Adds a new bone-group */ +void pose_add_group (Object *ob) { -#if 0 // XXX old animation system - bActionStrip *strip; + bPose *pose= (ob) ? ob->pose : NULL; + bActionGroup *grp; - if(ob->action==NULL) - return NULL; - - for (strip=ob->nlastrips.first; strip; strip=strip->next) - if(strip->flag & ACTSTRIP_ACTIVE) - break; + if (ELEM(NULL, ob, ob->pose)) + return; - if(strip && strip->act==ob->action) - return strip; -#endif // XXX old animation system - - return NULL; + grp= MEM_callocN(sizeof(bActionGroup), "PoseGroup"); + strcpy(grp->name, "Group"); + BLI_addtail(&pose->agroups, grp); + BLI_uniquename(&pose->agroups, grp, "Group", '.', offsetof(bActionGroup, name), 32); + + pose->active_group= BLI_countlist(&pose->agroups); } -/* non clipped mapping of strip */ -static float get_actionstrip_frame(bActionStrip *strip, float cframe, int invert) +/* Remove the active bone-group */ +void pose_remove_group (Object *ob) { - 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) */ + bPose *pose= (ob) ? ob->pose : NULL; + bActionGroup *grp = NULL; + bPoseChannel *pchan; - actlength = strip->actend-strip->actstart; - if (actlength == 0.0f) actlength = 1.0f; - length = repeat * scale * actlength; + /* sanity checks */ + if (ELEM(NULL, ob, pose)) + return; + if (pose->active_group <= 0) + return; - /* 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; + /* get group to remove */ + grp= BLI_findlink(&pose->agroups, pose->active_group-1); + if (grp) { + /* adjust group references (the trouble of using indices!): + * - firstly, make sure nothing references it + * - also, make sure that those after this item get corrected + */ + for (pchan= pose->chanbase.first; pchan; pchan= pchan->next) { + if (pchan->agrp_index == pose->active_group) + pchan->agrp_index= 0; + else if (pchan->agrp_index > pose->active_group) + pchan->agrp_index--; + } + + /* now, remove it from the pose */ + BLI_freelinkN(&pose->agroups, grp); + pose->active_group= 0; + } } -/* if the conditions match, it converts current time to strip time */ -float get_action_frame(Object *ob, float cframe) -{ - bActionStrip *strip= get_active_strip(ob); - - if(strip) - return get_actionstrip_frame(strip, cframe, 0); - return cframe; -} +/* ************** time ****************** */ -/* inverted, strip time to current time */ -float get_action_frame_inv(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, 1); - return cframe; + /* 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; + } + } + + /* nothing found */ + return 0; } - - - /* Calculate the extents of given action */ void calc_action_range(const bAction *act, float *start, float *end, int incl_hidden) { diff --git a/source/blender/blenkernel/intern/anim_sys.c b/source/blender/blenkernel/intern/anim_sys.c index 441e17f3318..2b4a2c135eb 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 */ @@ -90,8 +120,15 @@ AnimData *BKE_id_add_animdata (ID *id) IdAdtTemplate *iat= (IdAdtTemplate *)id; /* check if there's already AnimData, in which case, don't add */ - if (iat->adt == NULL) - iat->adt= MEM_callocN(sizeof(AnimData), "AnimData"); + if (iat->adt == NULL) { + AnimData *adt; + + /* add animdata */ + adt= iat->adt= MEM_callocN(sizeof(AnimData), "AnimData"); + + /* set default settings */ + adt->act_influence= 1.0f; + } return iat->adt; } @@ -116,7 +153,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); @@ -146,9 +189,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); @@ -355,10 +399,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; @@ -455,11 +499,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); + } } } } @@ -481,7 +528,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 */ @@ -514,6 +560,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) { @@ -540,152 +590,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; + + /* 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! */ + } + } - char *path; /* ready-to-use path (i.e. remapped already) */ - int array_index; /* if applicable... */ + /* 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; + } - float value; /* value of this channel */ -} NlaEvalChannel; - + /* 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; - /* execute these settings as per normal */ - //animsys_evaluate_fcurves(&actstrip_ptr, &strip->fcurves, NULL, ctime); + /* 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; + } + + /* 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; - /* skip if track is muted */ - if (nlt->flag & NLATRACK_MUTED) + /* 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; - /* 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! + /* optimise - abort if no channels */ + if (tmp_buffer->first == NULL) + return; + + /* 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; + + /* 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); - /* 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); + /* 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); + + /* second strip */ + tmp_nes.strip_mode= NES_TIME_TRANSITION_END; + tmp_nes.strip= s2; + 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; - BLI_addtail(list, nes); + /* assumulate temp-buffer and full-buffer, using the 'real' strip */ + nlaevalchan_buffers_accumulate(channels, &tmp_channels, 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; + } + } } /* ---------------------- */ @@ -696,6 +1197,9 @@ static void nladata_flush_channels (PointerRNA *ptr, ListBase *channels) */ static void animsys_evaluate_nla (PointerRNA *ptr, AnimData *adt, float ctime) { + ListBase dummy_trackslist = {NULL, NULL}; + NlaStrip dummy_strip; + NlaTrack *nlt; short track_index=0; @@ -703,9 +1207,49 @@ 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; + } + + /* add 'active' Action (may be tweaking track) as last strip to evaluate in NLA stack + * - only do this if we're not exclusively evaluating the 'solo' NLA-track + */ + if ((adt->action) && !(adt->flag & ADT_NLA_SOLO_TRACK)) { + /* make dummy NLA strip, and add that to the stack */ + memset(&dummy_strip, 0, sizeof(NlaStrip)); + dummy_trackslist.first= dummy_trackslist.last= &dummy_strip; + + dummy_strip.act= adt->action; + dummy_strip.remap= adt->remap; + + calc_action_range(dummy_strip.act, &dummy_strip.actstart, &dummy_strip.actend, 1); + dummy_strip.start = dummy_strip.actstart; + dummy_strip.end = (IS_EQ(dummy_strip.actstart, dummy_strip.actend)) ? (dummy_strip.actstart + 1.0f): (dummy_strip.actend); + + dummy_strip.blendmode= adt->act_blendmode; + dummy_strip.extendmode= adt->act_extendmode; + dummy_strip.influence= adt->act_influence; + + /* add this to our list of evaluation strips */ + nlastrips_ctime_get_strip(&estrips, &dummy_trackslist, -1, ctime); + } /* only continue if there are strips to evaluate */ if (estrips.first == NULL) @@ -714,10 +1258,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); @@ -799,17 +1343,19 @@ 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 + * - active action is evaluated as part of the NLA stack as the last item + */ animsys_evaluate_nla(&id_ptr, adt, 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 */ @@ -876,10 +1422,22 @@ 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... diff --git a/source/blender/blenkernel/intern/armature.c b/source/blender/blenkernel/intern/armature.c index 1b930a74449..eb8a894e800 100644 --- a/source/blender/blenkernel/intern/armature.c +++ b/source/blender/blenkernel/intern/armature.c @@ -66,8 +66,7 @@ #include "BKE_object.h" #include "BKE_object.h" #include "BKE_utildefines.h" - -//XXX #include "BIF_editdeform.h" +#include "BKE_sketch.h" #include "IK_solver.h" @@ -83,6 +82,7 @@ bArmature *add_armature(char *name) arm= alloc_libblock (&G.main->armature, ID_AR, name); arm->deformflag = ARM_DEF_VGROUP|ARM_DEF_ENVELOPE; + arm->flag = ARM_COL_CUSTOM; /* custom bone-group colors */ arm->layer= 1; return arm; } @@ -140,6 +140,12 @@ void free_armature(bArmature *arm) MEM_freeN(arm->edbo); arm->edbo= NULL; } + + /* free sketch */ + if (arm->sketch) { + freeSketch(arm->sketch); + arm->sketch = NULL; + } } } diff --git a/source/blender/blenkernel/intern/blender.c b/source/blender/blenkernel/intern/blender.c index d3d21018c1c..2728aa30e6e 100644 --- a/source/blender/blenkernel/intern/blender.c +++ b/source/blender/blenkernel/intern/blender.c @@ -387,10 +387,6 @@ static void setup_app_data(bContext *C, BlendFileData *bfd, char *filename) /* now tag update flags, to ensure deformers get calculated on redraw */ DAG_scene_update_flags(CTX_data_scene(C), CTX_data_scene(C)->lay); - if (G.f & G_DOSCRIPTLINKS) { - /* there's an onload scriptlink to execute in screenmain */ -// XXX mainqenter(ONLOAD_SCRIPT, 1); - } if (G.sce != filename) /* these are the same at times, should never copy to the same location */ strcpy(G.sce, filename); @@ -692,6 +688,22 @@ void BKE_undo_number(bContext *C, int nr) BKE_undo_step(C, 0); } +/* go back to the last occurance of name in stack */ +void BKE_undo_name(bContext *C, const char *name) +{ + UndoElem *uel; + + for(uel= undobase.last; uel; uel= uel->prev) { + if(strcmp(name, uel->name)==0) + break; + } + if(uel && uel->prev) { + curundo= uel->prev; + BKE_undo_step(C, 0); + } +} + + char *BKE_undo_menu_string(void) { UndoElem *uel; diff --git a/source/blender/blenkernel/intern/boids.c b/source/blender/blenkernel/intern/boids.c new file mode 100644 index 00000000000..d8926fc5753 --- /dev/null +++ b/source/blender/blenkernel/intern/boids.c @@ -0,0 +1,1524 @@ +/* boids.c + * + * + * $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 by Janne Karhu. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): none yet. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#include <string.h> +#include <math.h> + +#include "MEM_guardedalloc.h" + +#include "DNA_particle_types.h" +#include "DNA_modifier_types.h" +#include "DNA_object_force.h" +#include "DNA_object_types.h" +#include "DNA_scene_types.h" +#include "DNA_boid_types.h" +#include "DNA_listBase.h" + +#include "BLI_rand.h" +#include "BLI_arithb.h" +#include "BLI_blenlib.h" +#include "BLI_kdtree.h" +#include "BLI_kdopbvh.h" +#include "BKE_effect.h" +#include "BKE_boids.h" +#include "BKE_particle.h" +#include "BKE_utildefines.h" +#include "BKE_modifier.h" + +#include "RNA_enum_types.h" + +typedef struct BoidValues { + float max_speed, max_acc; + float max_ave, min_speed; + float personal_space, jump_speed; +} BoidValues; + +static int apply_boid_rule(BoidBrainData *bbd, BoidRule *rule, BoidValues *val, ParticleData *pa, float fuzziness); + +static int rule_none(BoidRule *rule, BoidBrainData *data, BoidValues *val, ParticleData *pa) +{ + return 0; +} + +static int rule_goal_avoid(BoidRule *rule, BoidBrainData *bbd, BoidValues *val, ParticleData *pa) +{ + BoidRuleGoalAvoid *gabr = (BoidRuleGoalAvoid*) rule; + BoidSettings *boids = bbd->part->boids; + ParticleEffectorCache *ec; + Object *priority_ob = NULL; + float vec[3] = {0.0f, 0.0f, 0.0f}, loc[3] = {0.0f, 0.0f, 0.0f}; + float mul = (rule->type == eBoidRuleType_Avoid ? 1.0 : -1.0); + float priority = 0.0f, len; + int ret = 0; + + /* first find out goal/predator with highest priority */ + /* if rule->ob specified use it */ + if(gabr->ob && (rule->type != eBoidRuleType_Goal || gabr->ob != pa->stick_ob)) { + PartDeflect *pd = gabr->ob->pd; + float vec_to_part[3]; + + if(pd && pd->forcefield == PFIELD_BOID) { + effector_find_co(bbd->scene, pa->prev_state.co, NULL, gabr->ob, pd, loc, vec, NULL, NULL); + + VecSubf(vec_to_part, pa->prev_state.co, loc); + + priority = mul * pd->f_strength * effector_falloff(pd, vec, vec_to_part); + } + else + priority = 1.0; + + priority = 1.0; + priority_ob = gabr->ob; + } + else for(ec=bbd->psys->effectors.first; ec; ec=ec->next) { + if(ec->type & PSYS_EC_EFFECTOR) { + Object *eob = ec->ob; + PartDeflect *pd = eob->pd; + + /* skip current object */ + if(rule->type == eBoidRuleType_Goal && eob == pa->stick_ob) + continue; + + if(pd->forcefield == PFIELD_BOID && mul * pd->f_strength > 0.0f) { + float vec_to_part[3], temp; + + effector_find_co(bbd->scene, pa->prev_state.co, NULL, eob, pd, loc, vec, NULL, NULL); + + VecSubf(vec_to_part, pa->prev_state.co, loc); + + temp = mul * pd->f_strength * effector_falloff(pd, vec, vec_to_part); + + if(temp == 0.0f) + ; /* do nothing */ + else if(temp > priority) { + priority = temp; + priority_ob = eob; + len = VecLength(vec_to_part); + } + /* choose closest object with same priority */ + else if(temp == priority) { + float len2 = VecLength(vec_to_part); + + if(len2 < len) { + priority_ob = eob; + len = len2; + } + } + } + } + } + + /* then use that effector */ + if(priority > (rule->type==eBoidRuleType_Avoid ? gabr->fear_factor : 0.0f)) { /* with avoid, factor is "fear factor" */ + Object *eob = priority_ob; + PartDeflect *pd = eob->pd; + float vec_to_part[3]; + float surface = 0.0f; + float nor[3]; + + if(gabr->options & BRULE_GOAL_AVOID_PREDICT) { + /* estimate future location of target */ + surface = (float)effector_find_co(bbd->scene, pa->prev_state.co, NULL, eob, pd, loc, nor, vec, NULL); + + VecSubf(vec_to_part, pa->prev_state.co, loc); + len = Normalize(vec_to_part); + + VecMulf(vec, len / (val->max_speed * bbd->timestep)); + VecAddf(loc, loc, vec); + VecSubf(vec_to_part, pa->prev_state.co, loc); + } + else { + surface = (float)effector_find_co(bbd->scene, pa->prev_state.co, NULL, eob, pd, loc, nor, NULL, NULL); + + VecSubf(vec_to_part, pa->prev_state.co, loc); + len = VecLength(vec_to_part); + } + + if(rule->type == eBoidRuleType_Goal && boids->options & BOID_ALLOW_CLIMB && surface!=0.0f) { + if(!bbd->goal_ob || bbd->goal_priority < priority) { + bbd->goal_ob = eob; + VECCOPY(bbd->goal_co, loc); + VECCOPY(bbd->goal_nor, nor); + } + } + else if(rule->type == eBoidRuleType_Avoid && pa->boid->mode == eBoidMode_Climbing && + priority > 2.0f * gabr->fear_factor) { + /* detach from surface and try to fly away from danger */ + VECCOPY(vec_to_part, pa->r_ve); + VecMulf(vec_to_part, -1.0f); + } + + VECCOPY(bbd->wanted_co, vec_to_part); + VecMulf(bbd->wanted_co, mul); + + bbd->wanted_speed = val->max_speed * priority; + + /* with goals factor is approach velocity factor */ + if(rule->type == eBoidRuleType_Goal && boids->landing_smoothness > 0.0f) { + float len2 = 2.0f*VecLength(pa->prev_state.vel); + + surface *= pa->size * boids->height; + + if(len2 > 0.0f && len - surface < len2) { + len2 = (len - surface)/len2; + bbd->wanted_speed *= pow(len2, boids->landing_smoothness); + } + } + + ret = 1; + } + + return ret; +} + +static int rule_avoid_collision(BoidRule *rule, BoidBrainData *bbd, BoidValues *val, ParticleData *pa) +{ + BoidRuleAvoidCollision *acbr = (BoidRuleAvoidCollision*) rule; + KDTreeNearest *ptn = NULL; + ParticleEffectorCache *ec; + ParticleTarget *pt; + float vec[3] = {0.0f, 0.0f, 0.0f}, loc[3] = {0.0f, 0.0f, 0.0f}; + float co1[3], vel1[3], co2[3], vel2[3]; + float len, t, inp, t_min = 2.0f; + int n, neighbors = 0, nearest = 0; + int ret = 0; + + //check deflector objects first + if(acbr->options & BRULE_ACOLL_WITH_DEFLECTORS) { + ParticleCollision col; + BVHTreeRayHit hit; + float radius = val->personal_space * pa->size, ray_dir[3]; + + VECCOPY(col.co1, pa->prev_state.co); + VecAddf(col.co2, pa->prev_state.co, pa->prev_state.vel); + VecSubf(ray_dir, col.co2, col.co1); + VecMulf(ray_dir, acbr->look_ahead); + col.t = 0.0f; + hit.index = -1; + hit.dist = col.ray_len = VecLength(ray_dir); + + /* find out closest deflector object */ + for(ec=bbd->psys->effectors.first; ec; ec=ec->next) { + if(ec->type & PSYS_EC_DEFLECT) { + Object *eob = ec->ob; + + /* don't check with current ground object */ + if(eob == pa->stick_ob) + continue; + + col.md = ( CollisionModifierData * ) ( modifiers_findByType ( eob, eModifierType_Collision ) ); + col.ob_t = eob; + + if(col.md && col.md->bvhtree) + BLI_bvhtree_ray_cast(col.md->bvhtree, col.co1, ray_dir, radius, &hit, particle_intersect_face, &col); + } + } + /* then avoid that object */ + if(hit.index>=0) { + /* TODO: not totally happy with this part */ + t = hit.dist/col.ray_len; + + VECCOPY(bbd->wanted_co, col.nor); + + VecMulf(bbd->wanted_co, (1.0f - t) * val->personal_space * pa->size); + + bbd->wanted_speed = sqrt(t) * VecLength(pa->prev_state.vel); + + return 1; + } + } + + //check boids in own system + if(acbr->options & BRULE_ACOLL_WITH_BOIDS) + { + neighbors = BLI_kdtree_range_search(bbd->psys->tree, acbr->look_ahead * VecLength(pa->prev_state.vel), pa->prev_state.co, pa->prev_state.ave, &ptn); + if(neighbors > 1) for(n=1; n<neighbors; n++) { + VECCOPY(co1, pa->prev_state.co); + VECCOPY(vel1, pa->prev_state.vel); + VECCOPY(co2, (bbd->psys->particles + ptn[n].index)->prev_state.co); + VECCOPY(vel2, (bbd->psys->particles + ptn[n].index)->prev_state.vel); + + VecSubf(loc, co1, co2); + + VecSubf(vec, vel1, vel2); + + inp = Inpf(vec,vec); + + /* velocities not parallel */ + if(inp != 0.0f) { + t = -Inpf(loc, vec)/inp; + /* cpa is not too far in the future so investigate further */ + if(t > 0.0f && t < t_min) { + VECADDFAC(co1, co1, vel1, t); + VECADDFAC(co2, co2, vel2, t); + + VecSubf(vec, co2, co1); + + len = Normalize(vec); + + /* distance of cpa is close enough */ + if(len < 2.0f * val->personal_space * pa->size) { + t_min = t; + + VecMulf(vec, VecLength(vel1)); + VecMulf(vec, (2.0f - t)/2.0f); + VecSubf(bbd->wanted_co, vel1, vec); + bbd->wanted_speed = VecLength(bbd->wanted_co); + ret = 1; + } + } + } + } + } + if(ptn){ MEM_freeN(ptn); ptn=NULL; } + + /* check boids in other systems */ + for(pt=bbd->psys->targets.first; pt; pt=pt->next) { + ParticleSystem *epsys = psys_get_target_system(bbd->ob, pt); + + if(epsys) { + neighbors = BLI_kdtree_range_search(epsys->tree, acbr->look_ahead * VecLength(pa->prev_state.vel), pa->prev_state.co, pa->prev_state.ave, &ptn); + if(neighbors > 0) for(n=0; n<neighbors; n++) { + VECCOPY(co1, pa->prev_state.co); + VECCOPY(vel1, pa->prev_state.vel); + VECCOPY(co2, (epsys->particles + ptn[n].index)->prev_state.co); + VECCOPY(vel2, (epsys->particles + ptn[n].index)->prev_state.vel); + + VecSubf(loc, co1, co2); + + VecSubf(vec, vel1, vel2); + + inp = Inpf(vec,vec); + + /* velocities not parallel */ + if(inp != 0.0f) { + t = -Inpf(loc, vec)/inp; + /* cpa is not too far in the future so investigate further */ + if(t > 0.0f && t < t_min) { + VECADDFAC(co1, co1, vel1, t); + VECADDFAC(co2, co2, vel2, t); + + VecSubf(vec, co2, co1); + + len = Normalize(vec); + + /* distance of cpa is close enough */ + if(len < 2.0f * val->personal_space * pa->size) { + t_min = t; + + VecMulf(vec, VecLength(vel1)); + VecMulf(vec, (2.0f - t)/2.0f); + VecSubf(bbd->wanted_co, vel1, vec); + bbd->wanted_speed = VecLength(bbd->wanted_co); + ret = 1; + } + } + } + } + + if(ptn){ MEM_freeN(ptn); ptn=NULL; } + } + } + + + if(ptn && nearest==0) + MEM_freeN(ptn); + + return ret; +} +static int rule_separate(BoidRule *rule, BoidBrainData *bbd, BoidValues *val, ParticleData *pa) +{ + KDTreeNearest *ptn = NULL; + ParticleTarget *pt; + float len = 2.0f * val->personal_space * pa->size + 1.0f; + float vec[3] = {0.0f, 0.0f, 0.0f}; + int neighbors = BLI_kdtree_range_search(bbd->psys->tree, 2.0f * val->personal_space * pa->size, pa->prev_state.co, NULL, &ptn); + int ret = 0; + + if(neighbors > 1 && ptn[1].dist!=0.0f) { + VecSubf(vec, pa->prev_state.co, bbd->psys->particles[ptn[1].index].state.co); + VecMulf(vec, (2.0f * val->personal_space * pa->size - ptn[1].dist) / ptn[1].dist); + VecAddf(bbd->wanted_co, bbd->wanted_co, vec); + bbd->wanted_speed = val->max_speed; + len = ptn[1].dist; + ret = 1; + } + if(ptn){ MEM_freeN(ptn); ptn=NULL; } + + /* check other boid systems */ + for(pt=bbd->psys->targets.first; pt; pt=pt->next) { + ParticleSystem *epsys = psys_get_target_system(bbd->ob, pt); + + if(epsys) { + neighbors = BLI_kdtree_range_search(epsys->tree, 2.0f * val->personal_space * pa->size, pa->prev_state.co, NULL, &ptn); + + if(neighbors > 0 && ptn[0].dist < len) { + VecSubf(vec, pa->prev_state.co, ptn[0].co); + VecMulf(vec, (2.0f * val->personal_space * pa->size - ptn[0].dist) / ptn[1].dist); + VecAddf(bbd->wanted_co, bbd->wanted_co, vec); + bbd->wanted_speed = val->max_speed; + len = ptn[0].dist; + ret = 1; + } + + if(ptn){ MEM_freeN(ptn); ptn=NULL; } + } + } + return ret; +} +static int rule_flock(BoidRule *rule, BoidBrainData *bbd, BoidValues *val, ParticleData *pa) +{ + KDTreeNearest ptn[11]; + float vec[3] = {0.0f, 0.0f, 0.0f}, loc[3] = {0.0f, 0.0f, 0.0f}; + int neighbors = BLI_kdtree_find_n_nearest(bbd->psys->tree, 11, pa->state.co, pa->prev_state.ave, ptn); + int n; + int ret = 0; + + if(neighbors > 1) { + for(n=1; n<neighbors; n++) { + VecAddf(loc, loc, bbd->psys->particles[ptn[n].index].prev_state.co); + VecAddf(vec, vec, bbd->psys->particles[ptn[n].index].prev_state.vel); + } + + VecMulf(loc, 1.0f/((float)neighbors - 1.0f)); + VecMulf(vec, 1.0f/((float)neighbors - 1.0f)); + + VecSubf(loc, loc, pa->prev_state.co); + VecSubf(vec, vec, pa->prev_state.vel); + + VecAddf(bbd->wanted_co, bbd->wanted_co, vec); + VecAddf(bbd->wanted_co, bbd->wanted_co, loc); + bbd->wanted_speed = VecLength(bbd->wanted_co); + + ret = 1; + } + return ret; +} +static int rule_follow_leader(BoidRule *rule, BoidBrainData *bbd, BoidValues *val, ParticleData *pa) +{ + BoidRuleFollowLeader *flbr = (BoidRuleFollowLeader*) rule; + float vec[3] = {0.0f, 0.0f, 0.0f}, loc[3] = {0.0f, 0.0f, 0.0f}; + float mul, len; + int n = (flbr->queue_size <= 1) ? bbd->psys->totpart : flbr->queue_size; + int i, ret = 0, p = pa - bbd->psys->particles; + + if(flbr->ob) { + float vec2[3], t; + + /* first check we're not blocking the leader*/ + VecSubf(vec, flbr->loc, flbr->oloc); + VecMulf(vec, 1.0f/bbd->timestep); + + VecSubf(loc, pa->prev_state.co, flbr->oloc); + + mul = Inpf(vec, vec); + + /* leader is not moving */ + if(mul < 0.01) { + len = VecLength(loc); + /* too close to leader */ + if(len < 2.0f * val->personal_space * pa->size) { + VECCOPY(bbd->wanted_co, loc); + bbd->wanted_speed = val->max_speed; + return 1; + } + } + else { + t = Inpf(loc, vec)/mul; + + /* possible blocking of leader in near future */ + if(t > 0.0f && t < 3.0f) { + VECCOPY(vec2, vec); + VecMulf(vec2, t); + + VecSubf(vec2, loc, vec2); + + len = VecLength(vec2); + + if(len < 2.0f * val->personal_space * pa->size) { + VECCOPY(bbd->wanted_co, vec2); + bbd->wanted_speed = val->max_speed * (3.0f - t)/3.0f; + return 1; + } + } + } + + /* not blocking so try to follow leader */ + if(p && flbr->options & BRULE_LEADER_IN_LINE) { + VECCOPY(vec, bbd->psys->particles[p-1].prev_state.vel); + VECCOPY(loc, bbd->psys->particles[p-1].prev_state.co); + } + else { + VECCOPY(loc, flbr->oloc); + VecSubf(vec, flbr->loc, flbr->oloc); + VecMulf(vec, 1.0/bbd->timestep); + } + + /* fac is seconds behind leader */ + VECADDFAC(loc, loc, vec, -flbr->distance); + + VecSubf(bbd->wanted_co, loc, pa->prev_state.co); + bbd->wanted_speed = VecLength(bbd->wanted_co); + + ret = 1; + } + else if(p % n) { + float vec2[3], t, t_min = 3.0f; + + /* first check we're not blocking any leaders */ + for(i = 0; i< bbd->psys->totpart; i+=n){ + VECCOPY(vec, bbd->psys->particles[i].prev_state.vel); + + VecSubf(loc, pa->prev_state.co, bbd->psys->particles[i].prev_state.co); + + mul = Inpf(vec, vec); + + /* leader is not moving */ + if(mul < 0.01) { + len = VecLength(loc); + /* too close to leader */ + if(len < 2.0f * val->personal_space * pa->size) { + VECCOPY(bbd->wanted_co, loc); + bbd->wanted_speed = val->max_speed; + return 1; + } + } + else { + t = Inpf(loc, vec)/mul; + + /* possible blocking of leader in near future */ + if(t > 0.0f && t < t_min) { + VECCOPY(vec2, vec); + VecMulf(vec2, t); + + VecSubf(vec2, loc, vec2); + + len = VecLength(vec2); + + if(len < 2.0f * val->personal_space * pa->size) { + t_min = t; + VECCOPY(bbd->wanted_co, loc); + bbd->wanted_speed = val->max_speed * (3.0f - t)/3.0f; + ret = 1; + } + } + } + } + + if(ret) return 1; + + /* not blocking so try to follow leader */ + if(flbr->options & BRULE_LEADER_IN_LINE) { + VECCOPY(vec, bbd->psys->particles[p-1].prev_state.vel); + VECCOPY(loc, bbd->psys->particles[p-1].prev_state.co); + } + else { + VECCOPY(vec, bbd->psys->particles[p - p%n].prev_state.vel); + VECCOPY(loc, bbd->psys->particles[p - p%n].prev_state.co); + } + + /* fac is seconds behind leader */ + VECADDFAC(loc, loc, vec, -flbr->distance); + + VecSubf(bbd->wanted_co, loc, pa->prev_state.co); + bbd->wanted_speed = VecLength(bbd->wanted_co); + + ret = 1; + } + + return ret; +} +static int rule_average_speed(BoidRule *rule, BoidBrainData *bbd, BoidValues *val, ParticleData *pa) +{ + BoidRuleAverageSpeed *asbr = (BoidRuleAverageSpeed*)rule; + float vec[3] = {0.0f, 0.0f, 0.0f}; + + if(asbr->wander > 0.0f) { + /* abuse pa->r_ave for wandering */ + pa->r_ave[0] += asbr->wander * (-1.0f + 2.0f * BLI_frand()); + pa->r_ave[1] += asbr->wander * (-1.0f + 2.0f * BLI_frand()); + pa->r_ave[2] += asbr->wander * (-1.0f + 2.0f * BLI_frand()); + + Normalize(pa->r_ave); + + VECCOPY(vec, pa->r_ave); + + QuatMulVecf(pa->prev_state.rot, vec); + + VECCOPY(bbd->wanted_co, pa->prev_state.ave); + + VecMulf(bbd->wanted_co, 1.1f); + + VecAddf(bbd->wanted_co, bbd->wanted_co, vec); + + /* leveling */ + if(asbr->level > 0.0f) { + Projf(vec, bbd->wanted_co, bbd->psys->part->acc); + VecMulf(vec, asbr->level); + VecSubf(bbd->wanted_co, bbd->wanted_co, vec); + } + } + else { + VECCOPY(bbd->wanted_co, pa->prev_state.ave); + + /* may happen at birth */ + if(Inp2f(bbd->wanted_co,bbd->wanted_co)==0.0f) { + bbd->wanted_co[0] = 2.0f*(0.5f - BLI_frand()); + bbd->wanted_co[1] = 2.0f*(0.5f - BLI_frand()); + bbd->wanted_co[2] = 2.0f*(0.5f - BLI_frand()); + } + + /* leveling */ + if(asbr->level > 0.0f) { + Projf(vec, bbd->wanted_co, bbd->psys->part->acc); + VecMulf(vec, asbr->level); + VecSubf(bbd->wanted_co, bbd->wanted_co, vec); + } + + } + bbd->wanted_speed = asbr->speed * val->max_speed; + + return 1; +} +static int rule_fight(BoidRule *rule, BoidBrainData *bbd, BoidValues *val, ParticleData *pa) +{ + BoidRuleFight *fbr = (BoidRuleFight*)rule; + KDTreeNearest *ptn = NULL; + ParticleTarget *pt; + ParticleData *epars; + ParticleData *enemy_pa; + /* friends & enemies */ + float closest_enemy[3] = {0.0f,0.0f,0.0f}; + float closest_dist = fbr->distance + 1.0f; + float f_strength = 0.0f, e_strength = 0.0f; + float health = 0.0f; + int n, ret = 0; + + /* calculate own group strength */ + int neighbors = BLI_kdtree_range_search(bbd->psys->tree, fbr->distance, pa->prev_state.co, NULL, &ptn); + for(n=0; n<neighbors; n++) + health += bbd->psys->particles[ptn[n].index].boid->health; + + f_strength += bbd->part->boids->strength * health; + + if(ptn){ MEM_freeN(ptn); ptn=NULL; } + + /* add other friendlies and calculate enemy strength and find closest enemy */ + for(pt=bbd->psys->targets.first; pt; pt=pt->next) { + ParticleSystem *epsys = psys_get_target_system(bbd->ob, pt); + if(epsys) { + epars = epsys->particles; + + neighbors = BLI_kdtree_range_search(epsys->tree, fbr->distance, pa->prev_state.co, NULL, &ptn); + + health = 0.0f; + + for(n=0; n<neighbors; n++) { + health += epars[ptn[n].index].boid->health; + + if(n==0 && pt->mode==PTARGET_MODE_ENEMY && ptn[n].dist < closest_dist) { + VECCOPY(closest_enemy, ptn[n].co); + closest_dist = ptn[n].dist; + enemy_pa = epars + ptn[n].index; + } + } + if(pt->mode==PTARGET_MODE_ENEMY) + e_strength += epsys->part->boids->strength * health; + else if(pt->mode==PTARGET_MODE_FRIEND) + f_strength += epsys->part->boids->strength * health; + + if(ptn){ MEM_freeN(ptn); ptn=NULL; } + } + } + /* decide action if enemy presence found */ + if(e_strength > 0.0f) { + VecSubf(bbd->wanted_co, closest_enemy, pa->prev_state.co); + + /* attack if in range */ + if(closest_dist <= bbd->part->boids->range + pa->size + enemy_pa->size) { + float damage = BLI_frand(); + float enemy_dir[3] = {bbd->wanted_co[0],bbd->wanted_co[1],bbd->wanted_co[2]}; + + Normalize(enemy_dir); + + /* fight mode */ + bbd->wanted_speed = 0.0f; + + /* must face enemy to fight */ + if(Inpf(pa->prev_state.ave, enemy_dir)>0.5f) { + enemy_pa->boid->health -= bbd->part->boids->strength * bbd->timestep * ((1.0f-bbd->part->boids->accuracy)*damage + bbd->part->boids->accuracy); + } + } + else { + /* approach mode */ + bbd->wanted_speed = val->max_speed; + } + + /* check if boid doesn't want to fight */ + if(pa->boid->health/bbd->part->boids->health * bbd->part->boids->aggression < e_strength / f_strength) { + /* decide to flee */ + if(closest_dist < fbr->flee_distance * fbr->distance) { + VecMulf(bbd->wanted_co, -1.0f); + bbd->wanted_speed = val->max_speed; + } + else { /* wait for better odds */ + bbd->wanted_speed = 0.0f; + } + } + + ret = 1; + } + + return ret; +} + +typedef int (*boid_rule_cb)(BoidRule *rule, BoidBrainData *data, BoidValues *val, ParticleData *pa); + +static boid_rule_cb boid_rules[] = { + rule_none, + rule_goal_avoid, + rule_goal_avoid, + rule_avoid_collision, + rule_separate, + rule_flock, + rule_follow_leader, + rule_average_speed, + rule_fight, + //rule_help, + //rule_protect, + //rule_hide, + //rule_follow_path, + //rule_follow_wall +}; + +static void set_boid_values(BoidValues *val, BoidSettings *boids, ParticleData *pa) +{ + if(ELEM(pa->boid->mode, eBoidMode_OnLand, eBoidMode_Climbing)) { + val->max_speed = boids->land_max_speed * pa->boid->health/boids->health; + val->max_acc = boids->land_max_acc * val->max_speed; + val->max_ave = boids->land_max_ave * M_PI * pa->boid->health/boids->health; + val->min_speed = 0.0f; /* no minimum speed on land */ + val->personal_space = boids->land_personal_space; + val->jump_speed = boids->land_jump_speed * pa->boid->health/boids->health; + } + else { + val->max_speed = boids->air_max_speed * pa->boid->health/boids->health; + val->max_acc = boids->air_max_acc * val->max_speed; + val->max_ave = boids->air_max_ave * M_PI * pa->boid->health/boids->health; + val->min_speed = boids->air_min_speed * boids->air_max_speed; + val->personal_space = boids->air_personal_space; + val->jump_speed = 0.0f; /* no jumping in air */ + } +} +static Object *boid_find_ground(BoidBrainData *bbd, ParticleData *pa, float *ground_co, float *ground_nor) +{ + if(pa->boid->mode == eBoidMode_Climbing) { + SurfaceModifierData *surmd = NULL; + float x[3], v[3]; + + surmd = (SurfaceModifierData *)modifiers_findByType ( pa->stick_ob, eModifierType_Surface ); + + /* take surface velocity into account */ + effector_find_co(bbd->scene, pa->state.co, surmd, NULL, NULL, x, NULL, v, NULL); + VecAddf(x, x, v); + + /* get actual position on surface */ + effector_find_co(bbd->scene, x, surmd, NULL, NULL, ground_co, ground_nor, NULL, NULL); + + return pa->stick_ob; + } + else { + float zvec[3] = {0.0f, 0.0f, 2000.0f}; + ParticleCollision col; + BVHTreeRayHit hit; + ParticleEffectorCache *ec; + float radius = 0.0f, t, ray_dir[3]; + + VECCOPY(col.co1, pa->state.co); + VECCOPY(col.co2, pa->state.co); + VecAddf(col.co1, col.co1, zvec); + VecSubf(col.co2, col.co2, zvec); + VecSubf(ray_dir, col.co2, col.co1); + col.t = 0.0f; + hit.index = -1; + hit.dist = col.ray_len = VecLength(ray_dir); + + /* find out upmost deflector object */ + for(ec=bbd->psys->effectors.first; ec; ec=ec->next) { + if(ec->type & PSYS_EC_DEFLECT) { + Object *eob = ec->ob; + + col.md = ( CollisionModifierData * ) ( modifiers_findByType ( eob, eModifierType_Collision ) ); + col.ob_t = eob; + + if(col.md && col.md->bvhtree) + BLI_bvhtree_ray_cast(col.md->bvhtree, col.co1, ray_dir, radius, &hit, particle_intersect_face, &col); + } + } + /* then use that object */ + if(hit.index>=0) { + t = hit.dist/col.ray_len; + VecLerpf(ground_co, col.co1, col.co2, t); + VECCOPY(ground_nor, col.nor); + Normalize(ground_nor); + return col.ob; + } + else { + /* default to z=0 */ + VECCOPY(ground_co, pa->state.co); + ground_co[2] = 0; + ground_nor[0] = ground_nor[1] = 0.0f; + ground_nor[2] = 1.0f; + return NULL; + } + } +} +static int boid_rule_applies(ParticleData *pa, BoidSettings *boids, BoidRule *rule) +{ + if(rule==NULL) + return 0; + + if(ELEM(pa->boid->mode, eBoidMode_OnLand, eBoidMode_Climbing) && rule->flag & BOIDRULE_ON_LAND) + return 1; + + if(pa->boid->mode==eBoidMode_InAir && rule->flag & BOIDRULE_IN_AIR) + return 1; + + return 0; +} +void boids_precalc_rules(ParticleSettings *part, float cfra) +{ + BoidState *state = part->boids->states.first; + BoidRule *rule; + for(; state; state=state->next) { + for(rule = state->rules.first; rule; rule=rule->next) { + if(rule->type==eBoidRuleType_FollowLeader) { + BoidRuleFollowLeader *flbr = (BoidRuleFollowLeader*) rule; + + if(flbr->ob && flbr->cfra != cfra) { + /* save object locations for velocity calculations */ + VECCOPY(flbr->oloc, flbr->loc); + VECCOPY(flbr->loc, flbr->ob->obmat[3]); + flbr->cfra = cfra; + } + } + } + } +} +static void boid_climb(BoidSettings *boids, ParticleData *pa, float *surface_co, float *surface_nor) +{ + float nor[3], vel[3]; + VECCOPY(nor, surface_nor); + + /* gather apparent gravity to r_ve */ + VECADDFAC(pa->r_ve, pa->r_ve, surface_nor, -1.0); + Normalize(pa->r_ve); + + /* raise boid it's size from surface */ + VecMulf(nor, pa->size * boids->height); + VecAddf(pa->state.co, surface_co, nor); + + /* remove normal component from velocity */ + Projf(vel, pa->state.vel, surface_nor); + VecSubf(pa->state.vel, pa->state.vel, vel); +} +static float boid_goal_signed_dist(float *boid_co, float *goal_co, float *goal_nor) +{ + float vec[3]; + + VecSubf(vec, boid_co, goal_co); + + return Inpf(vec, goal_nor); +} +/* wanted_co is relative to boid location */ +static int apply_boid_rule(BoidBrainData *bbd, BoidRule *rule, BoidValues *val, ParticleData *pa, float fuzziness) +{ + if(rule==NULL) + return 0; + + if(boid_rule_applies(pa, bbd->part->boids, rule)==0) + return 0; + + if(boid_rules[rule->type](rule, bbd, val, pa)==0) + return 0; + + if(fuzziness < 0.0f || VecLenCompare(bbd->wanted_co, pa->prev_state.vel, fuzziness * VecLength(pa->prev_state.vel))==0) + return 1; + else + return 0; +} +static BoidState *get_boid_state(BoidSettings *boids, ParticleData *pa) { + BoidState *state = boids->states.first; + + for(; state; state=state->next) { + if(state->id==pa->boid->state_id) + return state; + } + + /* for some reason particle isn't at a valid state */ + state = boids->states.first; + if(state) + pa->boid->state_id = state->id; + + return state; +} +//static int boid_condition_is_true(BoidCondition *cond) { +// /* TODO */ +// return 0; +//} + +/* determines the velocity the boid wants to have */ +void boid_brain(BoidBrainData *bbd, int p, ParticleData *pa) +{ + BoidRule *rule; + BoidSettings *boids = bbd->part->boids; + BoidValues val; + BoidState *state = get_boid_state(boids, pa); + //BoidCondition *cond; + + if(pa->boid->health <= 0.0f) { + pa->alive = PARS_DYING; + return; + } + + //planned for near future + //cond = state->conditions.first; + //for(; cond; cond=cond->next) { + // if(boid_condition_is_true(cond)) { + // pa->boid->state_id = cond->state_id; + // state = get_boid_state(boids, pa); + // break; /* only first true condition is used */ + // } + //} + + bbd->wanted_co[0]=bbd->wanted_co[1]=bbd->wanted_co[2]=bbd->wanted_speed=0.0f; + + /* create random seed for every particle & frame */ + BLI_srandom(bbd->psys->seed + p + (int)bbd->cfra + (int)(1000*pa->r_rot[0])); + + set_boid_values(&val, bbd->part->boids, pa); + + /* go through rules */ + switch(state->ruleset_type) { + case eBoidRulesetType_Fuzzy: + { + for(rule = state->rules.first; rule; rule = rule->next) { + if(apply_boid_rule(bbd, rule, &val, pa, state->rule_fuzziness)) + break; /* only first nonzero rule that comes through fuzzy rule is applied */ + } + break; + } + case eBoidRulesetType_Random: + { + /* use random rule for each particle (allways same for same particle though) */ + rule = BLI_findlink(&state->rules, (int)(1000.0f * pa->r_rot[1]) % BLI_countlist(&state->rules)); + + apply_boid_rule(bbd, rule, &val, pa, -1.0); + } + case eBoidRulesetType_Average: + { + float wanted_co[3] = {0.0f, 0.0f, 0.0f}, wanted_speed = 0.0f; + int n = 0; + for(rule = state->rules.first; rule; rule=rule->next) { + if(apply_boid_rule(bbd, rule, &val, pa, -1.0f)) { + VecAddf(wanted_co, wanted_co, bbd->wanted_co); + wanted_speed += bbd->wanted_speed; + n++; + bbd->wanted_co[0]=bbd->wanted_co[1]=bbd->wanted_co[2]=bbd->wanted_speed=0.0f; + } + } + + if(n > 1) { + VecMulf(wanted_co, 1.0f/(float)n); + wanted_speed /= (float)n; + } + + VECCOPY(bbd->wanted_co, wanted_co); + bbd->wanted_speed = wanted_speed; + break; + } + + } + + /* decide on jumping & liftoff */ + if(pa->boid->mode == eBoidMode_OnLand) { + /* fuzziness makes boids capable of misjudgement */ + float mul = 1.0 + state->rule_fuzziness; + + if(boids->options & BOID_ALLOW_FLIGHT && bbd->wanted_co[2] > 0.0f) { + float cvel[3], dir[3]; + + VECCOPY(dir, pa->prev_state.ave); + Normalize2(dir); + + VECCOPY(cvel, bbd->wanted_co); + Normalize2(cvel); + + if(Inp2f(cvel, dir) > 0.95 / mul) + pa->boid->mode = eBoidMode_Liftoff; + } + else if(val.jump_speed > 0.0f) { + float jump_v[3]; + int jump = 0; + + /* jump to get to a location */ + if(bbd->wanted_co[2] > 0.0f) { + float cvel[3], dir[3]; + float z_v, ground_v, cur_v; + float len; + + VECCOPY(dir, pa->prev_state.ave); + Normalize2(dir); + + VECCOPY(cvel, bbd->wanted_co); + Normalize2(cvel); + + len = Vec2Length(pa->prev_state.vel); + + /* first of all, are we going in a suitable direction? */ + /* or at a suitably slow speed */ + if(Inp2f(cvel, dir) > 0.95f / mul || len <= state->rule_fuzziness) { + /* try to reach goal at highest point of the parabolic path */ + cur_v = Vec2Length(pa->prev_state.vel); + z_v = sasqrt(-2.0f * bbd->part->acc[2] * bbd->wanted_co[2]); + ground_v = Vec2Length(bbd->wanted_co)*sasqrt(-0.5f * bbd->part->acc[2] / bbd->wanted_co[2]); + + len = sasqrt((ground_v-cur_v)*(ground_v-cur_v) + z_v*z_v); + + if(len < val.jump_speed * mul || bbd->part->boids->options & BOID_ALLOW_FLIGHT) { + jump = 1; + + len = MIN2(len, val.jump_speed); + + VECCOPY(jump_v, dir); + jump_v[2] = z_v; + VecMulf(jump_v, ground_v); + + Normalize(jump_v); + VecMulf(jump_v, len); + Vec2Addf(jump_v, jump_v, pa->prev_state.vel); + } + } + } + + /* jump to go faster */ + if(jump == 0 && val.jump_speed > val.max_speed && bbd->wanted_speed > val.max_speed) { + + } + + if(jump) { + VECCOPY(pa->prev_state.vel, jump_v); + pa->boid->mode = eBoidMode_Falling; + } + } + } +} +/* tries to realize the wanted velocity taking all constraints into account */ +void boid_body(BoidBrainData *bbd, ParticleData *pa) +{ + BoidSettings *boids = bbd->part->boids; + BoidValues val; + float acc[3] = {0.0f, 0.0f, 0.0f}, tan_acc[3], nor_acc[3]; + float dvec[3], bvec[3]; + float new_dir[3], new_speed; + float old_dir[3], old_speed; + float wanted_dir[3]; + float q[4], mat[3][3]; /* rotation */ + float ground_co[3] = {0.0f, 0.0f, 0.0f}, ground_nor[3] = {0.0f, 0.0f, 1.0f}; + float force[3] = {0.0f, 0.0f, 0.0f}, tvel[3] = {0.0f, 0.0f, 1.0f}; + float pa_mass=bbd->part->mass, dtime=bbd->dfra*bbd->timestep; + int p = pa - bbd->psys->particles; + + set_boid_values(&val, boids, pa); + + /* make sure there's something in new velocity, location & rotation */ + copy_particle_key(&pa->state,&pa->prev_state,0); + + if(bbd->part->flag & PART_SIZEMASS) + pa_mass*=pa->size; + + /* if boids can't fly they fall to the ground */ + if((boids->options & BOID_ALLOW_FLIGHT)==0 && ELEM(pa->boid->mode, eBoidMode_OnLand, eBoidMode_Climbing)==0 && bbd->part->acc[2] != 0.0f) + pa->boid->mode = eBoidMode_Falling; + + if(pa->boid->mode == eBoidMode_Falling) { + /* Falling boids are only effected by gravity. */ + acc[2] = bbd->part->acc[2]; + } + else { + /* figure out acceleration */ + float landing_level = 2.0f; + float level = landing_level + 1.0f; + float new_vel[3]; + + if(pa->boid->mode == eBoidMode_Liftoff) { + pa->boid->mode = eBoidMode_InAir; + pa->stick_ob = boid_find_ground(bbd, pa, ground_co, ground_nor); + } + else if(pa->boid->mode == eBoidMode_InAir && boids->options & BOID_ALLOW_LAND) { + /* auto-leveling & landing if close to ground */ + + pa->stick_ob = boid_find_ground(bbd, pa, ground_co, ground_nor); + + /* level = how many particle sizes above ground */ + level = (pa->prev_state.co[2] - ground_co[2])/(2.0f * pa->size) - 0.5; + + landing_level = - boids->landing_smoothness * pa->prev_state.vel[2] * pa_mass; + + if(pa->prev_state.vel[2] < 0.0f) { + if(level < 1.0f) { + bbd->wanted_co[0] = bbd->wanted_co[1] = bbd->wanted_co[2] = 0.0f; + bbd->wanted_speed = 0.0f; + pa->boid->mode = eBoidMode_Falling; + } + else if(level < landing_level) { + bbd->wanted_speed *= (level - 1.0f)/landing_level; + bbd->wanted_co[2] *= (level - 1.0f)/landing_level; + } + } + } + + VECCOPY(old_dir, pa->prev_state.ave); + VECCOPY(wanted_dir, bbd->wanted_co); + new_speed = Normalize(wanted_dir); + + /* first check if we have valid direction we want to go towards */ + if(new_speed == 0.0f) { + VECCOPY(new_dir, old_dir); + } + else { + float old_dir2[2], wanted_dir2[2], nor[3], angle; + Vec2Copyf(old_dir2, old_dir); + Normalize2(old_dir2); + Vec2Copyf(wanted_dir2, wanted_dir); + Normalize2(wanted_dir2); + + /* choose random direction to turn if wanted velocity */ + /* is directly behind regardless of z-coordinate */ + if(Inp2f(old_dir2, wanted_dir2) < -0.99f) { + wanted_dir[0] = 2.0f*(0.5f - BLI_frand()); + wanted_dir[1] = 2.0f*(0.5f - BLI_frand()); + wanted_dir[2] = 2.0f*(0.5f - BLI_frand()); + Normalize(wanted_dir); + } + + /* constrain direction with maximum angular velocity */ + angle = saacos(Inpf(old_dir, wanted_dir)); + angle = MIN2(angle, val.max_ave); + + Crossf(nor, old_dir, wanted_dir); + VecRotToQuat(nor, angle, q); + VECCOPY(new_dir, old_dir); + QuatMulVecf(q, new_dir); + Normalize(new_dir); + + /* save direction in case resulting velocity too small */ + VecRotToQuat(nor, angle*dtime, q); + VECCOPY(pa->state.ave, old_dir); + QuatMulVecf(q, pa->state.ave); + Normalize(pa->state.ave); + } + + /* constrain speed with maximum acceleration */ + old_speed = VecLength(pa->prev_state.vel); + + if(bbd->wanted_speed < old_speed) + new_speed = MAX2(bbd->wanted_speed, old_speed - val.max_acc); + else + new_speed = MIN2(bbd->wanted_speed, old_speed + val.max_acc); + + /* combine direction and speed */ + VECCOPY(new_vel, new_dir); + VecMulf(new_vel, new_speed); + + /* maintain minimum flying velocity if not landing */ + if(level >= landing_level) { + float len2 = Inp2f(new_vel,new_vel); + float root; + + len2 = MAX2(len2, val.min_speed*val.min_speed); + root = sasqrt(new_speed*new_speed - len2); + + new_vel[2] = new_vel[2] < 0.0f ? -root : root; + + Normalize2(new_vel); + Vec2Mulf(new_vel, sasqrt(len2)); + } + + /* finally constrain speed to max speed */ + new_speed = Normalize(new_vel); + VecMulf(new_vel, MIN2(new_speed, val.max_speed)); + + /* get acceleration from difference of velocities */ + VecSubf(acc, new_vel, pa->prev_state.vel); + + /* break acceleration to components */ + Projf(tan_acc, acc, pa->prev_state.ave); + VecSubf(nor_acc, acc, tan_acc); + } + + /* account for effectors */ + do_effectors(p, pa, &pa->state, bbd->scene, bbd->ob, bbd->psys, pa->state.co, force, tvel, bbd->dfra, bbd->cfra); + + if(ELEM(pa->boid->mode, eBoidMode_OnLand, eBoidMode_Climbing)) { + float length = Normalize(force); + + length = MAX2(0.0f, length - boids->land_stick_force); + + VecMulf(force, length); + } + + VecAddf(acc, acc, force); + + /* store smoothed acceleration for nice banking etc. */ + VECADDFAC(pa->boid->acc, pa->boid->acc, acc, dtime); + VecMulf(pa->boid->acc, 1.0f / (1.0f + dtime)); + + /* integrate new location & velocity */ + + /* by regarding the acceleration as a force at this stage we*/ + /* can get better control allthough it's a bit unphysical */ + VecMulf(acc, 1.0f/pa_mass); + + VECCOPY(dvec, acc); + VecMulf(dvec, dtime*dtime*0.5f); + + VECCOPY(bvec, pa->prev_state.vel); + VecMulf(bvec, dtime); + VecAddf(dvec, dvec, bvec); + VecAddf(pa->state.co, pa->state.co, dvec); + + VECADDFAC(pa->state.vel, pa->state.vel, acc, dtime); + + if(pa->boid->mode != eBoidMode_InAir) + pa->stick_ob = boid_find_ground(bbd, pa, ground_co, ground_nor); + + /* change modes, constrain movement & keep track of down vector */ + switch(pa->boid->mode) { + case eBoidMode_InAir: + { + float grav[3] = {0.0f, 0.0f, bbd->part->acc[2] < 0.0f ? -1.0f : 0.0f}; + + /* don't take forward acceleration into account (better banking) */ + if(Inpf(pa->boid->acc, pa->state.vel) > 0.0f) { + Projf(dvec, pa->boid->acc, pa->state.vel); + VecSubf(dvec, pa->boid->acc, dvec); + } + else { + VECCOPY(dvec, pa->boid->acc); + } + + /* gather apparent gravity to r_ve */ + VECADDFAC(pa->r_ve, grav, dvec, -boids->banking); + Normalize(pa->r_ve); + + /* stick boid on goal when close enough */ + if(bbd->goal_ob && boid_goal_signed_dist(pa->state.co, bbd->goal_co, bbd->goal_nor) <= pa->size * boids->height) { + pa->boid->mode = eBoidMode_Climbing; + pa->stick_ob = bbd->goal_ob; + boid_find_ground(bbd, pa, ground_co, ground_nor); + boid_climb(boids, pa, ground_co, ground_nor); + } + /* land boid when belowg ground */ + else if(boids->options & BOID_ALLOW_LAND && pa->state.co[2] <= ground_co[2] + pa->size * boids->height) { + pa->state.co[2] = ground_co[2] + pa->size * boids->height; + pa->state.vel[2] = 0.0f; + pa->boid->mode = eBoidMode_OnLand; + } + break; + } + case eBoidMode_Falling: + { + float grav[3] = {0.0f, 0.0f, bbd->part->acc[2] < 0.0f ? -1.0f : 0.0f}; + + /* gather apparent gravity to r_ve */ + VECADDFAC(pa->r_ve, pa->r_ve, grav, dtime); + Normalize(pa->r_ve); + + if(boids->options & BOID_ALLOW_LAND) { + /* stick boid on goal when close enough */ + if(bbd->goal_ob && boid_goal_signed_dist(pa->state.co, bbd->goal_co, bbd->goal_nor) <= pa->size * boids->height) { + pa->boid->mode = eBoidMode_Climbing; + pa->stick_ob = bbd->goal_ob; + boid_find_ground(bbd, pa, ground_co, ground_nor); + boid_climb(boids, pa, ground_co, ground_nor); + } + /* land boid when really near ground */ + else if(pa->state.co[2] <= ground_co[2] + 1.01 * pa->size * boids->height){ + pa->state.co[2] = ground_co[2] + pa->size * boids->height; + pa->state.vel[2] = 0.0f; + pa->boid->mode = eBoidMode_OnLand; + } + /* if we're falling, can fly and want to go upwards lets fly */ + else if(boids->options & BOID_ALLOW_FLIGHT && bbd->wanted_co[2] > 0.0f) + pa->boid->mode = eBoidMode_InAir; + } + else + pa->boid->mode = eBoidMode_InAir; + break; + } + case eBoidMode_Climbing: + { + boid_climb(boids, pa, ground_co, ground_nor); + //float nor[3]; + //VECCOPY(nor, ground_nor); + + ///* gather apparent gravity to r_ve */ + //VECADDFAC(pa->r_ve, pa->r_ve, ground_nor, -1.0); + //Normalize(pa->r_ve); + + ///* raise boid it's size from surface */ + //VecMulf(nor, pa->size * boids->height); + //VecAddf(pa->state.co, ground_co, nor); + + ///* remove normal component from velocity */ + //Projf(v, pa->state.vel, ground_nor); + //VecSubf(pa->state.vel, pa->state.vel, v); + break; + } + case eBoidMode_OnLand: + { + /* stick boid on goal when close enough */ + if(bbd->goal_ob && boid_goal_signed_dist(pa->state.co, bbd->goal_co, bbd->goal_nor) <= pa->size * boids->height) { + pa->boid->mode = eBoidMode_Climbing; + pa->stick_ob = bbd->goal_ob; + boid_find_ground(bbd, pa, ground_co, ground_nor); + boid_climb(boids, pa, ground_co, ground_nor); + } + /* ground is too far away so boid falls */ + else if(pa->state.co[2]-ground_co[2] > 1.1 * pa->size * boids->height) + pa->boid->mode = eBoidMode_Falling; + else { + /* constrain to surface */ + pa->state.co[2] = ground_co[2] + pa->size * boids->height; + pa->state.vel[2] = 0.0f; + } + + if(boids->banking > 0.0f) { + float grav[3]; + /* Don't take gravity's strength in to account, */ + /* otherwise amount of banking is hard to control. */ + VECCOPY(grav, ground_nor); + VecMulf(grav, -1.0f); + + Projf(dvec, pa->boid->acc, pa->state.vel); + VecSubf(dvec, pa->boid->acc, dvec); + + /* gather apparent gravity to r_ve */ + VECADDFAC(pa->r_ve, grav, dvec, -boids->banking); + Normalize(pa->r_ve); + } + else { + /* gather negative surface normal to r_ve */ + VECADDFAC(pa->r_ve, pa->r_ve, ground_nor, -1.0f); + Normalize(pa->r_ve); + } + break; + } + } + + /* save direction to state.ave unless the boid is falling */ + /* (boids can't effect their direction when falling) */ + if(pa->boid->mode!=eBoidMode_Falling && VecLength(pa->state.vel) > 0.1*pa->size) { + VECCOPY(pa->state.ave, pa->state.vel); + Normalize(pa->state.ave); + } + + /* apply damping */ + if(ELEM(pa->boid->mode, eBoidMode_OnLand, eBoidMode_Climbing)) + VecMulf(pa->state.vel, 1.0f - 0.2f*bbd->part->dampfac); + + /* calculate rotation matrix based on forward & down vectors */ + if(pa->boid->mode == eBoidMode_InAir) { + VECCOPY(mat[0], pa->state.ave); + + Projf(dvec, pa->r_ve, pa->state.ave); + VecSubf(mat[2], pa->r_ve, dvec); + Normalize(mat[2]); + } + else { + Projf(dvec, pa->state.ave, pa->r_ve); + VecSubf(mat[0], pa->state.ave, dvec); + Normalize(mat[0]); + + VECCOPY(mat[2], pa->r_ve); + } + VecMulf(mat[2], -1.0f); + Crossf(mat[1], mat[2], mat[0]); + + /* apply rotation */ + Mat3ToQuat_is_ok(mat, q); + QuatCopy(pa->state.rot, q); +} + +BoidRule *boid_new_rule(int type) +{ + BoidRule *rule = NULL; + if(type <= 0) + return NULL; + + switch(type) { + case eBoidRuleType_Goal: + case eBoidRuleType_Avoid: + rule = MEM_callocN(sizeof(BoidRuleGoalAvoid), "BoidRuleGoalAvoid"); + break; + case eBoidRuleType_AvoidCollision: + rule = MEM_callocN(sizeof(BoidRuleAvoidCollision), "BoidRuleAvoidCollision"); + ((BoidRuleAvoidCollision*)rule)->look_ahead = 2.0f; + break; + case eBoidRuleType_FollowLeader: + rule = MEM_callocN(sizeof(BoidRuleFollowLeader), "BoidRuleFollowLeader"); + ((BoidRuleFollowLeader*)rule)->distance = 1.0f; + break; + case eBoidRuleType_AverageSpeed: + rule = MEM_callocN(sizeof(BoidRuleAverageSpeed), "BoidRuleAverageSpeed"); + ((BoidRuleAverageSpeed*)rule)->speed = 0.5f; + break; + case eBoidRuleType_Fight: + rule = MEM_callocN(sizeof(BoidRuleFight), "BoidRuleFight"); + ((BoidRuleFight*)rule)->distance = 100.0f; + ((BoidRuleFight*)rule)->flee_distance = 100.0f; + break; + default: + rule = MEM_callocN(sizeof(BoidRule), "BoidRule"); + break; + } + + rule->type = type; + rule->flag |= BOIDRULE_IN_AIR|BOIDRULE_ON_LAND; + strcpy(rule->name, boidrule_type_items[type-1].name); + + return rule; +} +void boid_default_settings(BoidSettings *boids) +{ + boids->air_max_speed = 10.0f; + boids->air_max_acc = 0.5f; + boids->air_max_ave = 0.5f; + boids->air_personal_space = 1.0f; + + boids->land_max_speed = 5.0f; + boids->land_max_acc = 0.5f; + boids->land_max_ave = 0.5f; + boids->land_personal_space = 1.0f; + + boids->options = BOID_ALLOW_FLIGHT; + + boids->landing_smoothness = 3.0f; + boids->banking = 1.0f; + boids->height = 1.0f; + + boids->health = 1.0f; + boids->accuracy = 1.0f; + boids->aggression = 2.0f; + boids->range = 1.0f; + boids->strength = 0.1f; +} + +BoidState *boid_new_state(BoidSettings *boids) +{ + BoidState *state = MEM_callocN(sizeof(BoidState), "BoidState"); + + state->id = boids->last_state_id++; + if(state->id) + sprintf(state->name, "State %i", state->id); + else + strcpy(state->name, "State"); + + state->rule_fuzziness = 0.5; + state->volume = 1.0f; + state->channels |= ~0; + + return state; +} + +BoidState *boid_duplicate_state(BoidSettings *boids, BoidState *state) { + BoidState *staten = MEM_dupallocN(state); + + BLI_duplicatelist(&staten->rules, &state->rules); + BLI_duplicatelist(&staten->conditions, &state->conditions); + BLI_duplicatelist(&staten->actions, &state->actions); + + staten->id = boids->last_state_id++; + + return staten; +} +void boid_free_settings(BoidSettings *boids) +{ + if(boids) { + BoidState *state = boids->states.first; + + for(; state; state=state->next) { + BLI_freelistN(&state->rules); + BLI_freelistN(&state->conditions); + BLI_freelistN(&state->actions); + } + + BLI_freelistN(&boids->states); + + MEM_freeN(boids); + } +} +BoidSettings *boid_copy_settings(BoidSettings *boids) +{ + BoidSettings *nboids = NULL; + + if(boids) { + BoidState *state; + BoidState *nstate; + + nboids = MEM_dupallocN(boids); + + BLI_duplicatelist(&nboids->states, &boids->states); + + state = boids->states.first; + nstate = nboids->states.first; + for(; state; state=state->next, nstate=nstate->next) { + BLI_duplicatelist(&nstate->rules, &state->rules); + BLI_duplicatelist(&nstate->conditions, &state->conditions); + BLI_duplicatelist(&nstate->actions, &state->actions); + } + } + + return nboids; +} +BoidState *boid_get_current_state(BoidSettings *boids) +{ + BoidState *state = boids->states.first; + + for(; state; state=state->next) { + if(state->flag & BOIDSTATE_CURRENT) + break; + } + + return state; +} + diff --git a/source/blender/blenkernel/intern/colortools.c b/source/blender/blenkernel/intern/colortools.c index e8716aba296..2280ec71f7a 100644 --- a/source/blender/blenkernel/intern/colortools.c +++ b/source/blender/blenkernel/intern/colortools.c @@ -58,6 +58,91 @@ #include "IMB_imbuf.h" #include "IMB_imbuf_types.h" +/* ********************************* color transforms ********************************* */ + +/*Transform linear RGB values to nonlinear RGB values. Rec. + 709 is ITU-R Recommendation BT. 709 (1990) ``Basic + Parameter Values for the HDTV Standard for the Studio and + for International Programme Exchange'', formerly CCIR Rec. + 709.*/ +void gamma_correct_rec709(float *c, float gamma) +{ + /* Rec. 709 gamma correction. */ + float cc = 0.018f; + + if (*c < cc) + *c *= ((1.099f * (float)pow(cc, gamma)) - 0.099f) / cc; + else + *c = (1.099f * (float)pow(*c, gamma)) - 0.099f; +} + +void gamma_correct(float *c, float gamma) +{ + *c = pow((*c), gamma); +} + +float srgb_to_linearrgb(float c) +{ + if (c < 0.04045f) + return (c < 0.f)?0.f:c / 12.92; + else + return pow((c + 0.055)/1.055, 2.4); +} + +float linearrgb_to_srgb(float c) +{ + if (c < 0.0031308) + return (c < 0.f)?0.f:c * 12.92; + else + return 1.055 * pow(c, 1.0/2.4) - 0.055; +} + +/* utility function convert an RGB triplet from sRGB to linear RGB color space */ +void color_manage_linearize(float *col_to, float *col_from) +{ + col_to[0] = srgb_to_linearrgb(col_from[0]); + col_to[1] = srgb_to_linearrgb(col_from[1]); + col_to[2] = srgb_to_linearrgb(col_from[2]); +} + +void floatbuf_to_srgb_byte(float *rectf, unsigned char *rectc, int x1, int x2, int y1, int y2, int w) +{ + int x, y; + float *rf= rectf; + float srgb[3]; + unsigned char *rc= rectc; + + for(y=y1; y<y2; y++) { + for(x=x1; x<x2; x++, rf+=4, rc+=4) { + srgb[0]= linearrgb_to_srgb(rf[0]); + srgb[1]= linearrgb_to_srgb(rf[1]); + srgb[2]= linearrgb_to_srgb(rf[2]); + + rc[0]= FTOCHAR(srgb[0]); + rc[1]= FTOCHAR(srgb[1]); + rc[2]= FTOCHAR(srgb[2]); + rc[3]= FTOCHAR(rf[3]); + } + } +} + +void floatbuf_to_byte(float *rectf, unsigned char *rectc, int x1, int x2, int y1, int y2, int w) +{ + int x, y; + float *rf= rectf; + unsigned char *rc= rectc; + + for(y=y1; y<y2; y++) { + for(x=x1; x<x2; x++, rf+=4, rc+=4) { + rc[0]= FTOCHAR(rf[0]); + rc[1]= FTOCHAR(rf[1]); + rc[2]= FTOCHAR(rf[2]); + rc[3]= FTOCHAR(rf[3]); + } + } +} + + /* ********************************* color curve ********************* */ /* ***************** operations on full struct ************* */ diff --git a/source/blender/blenkernel/intern/context.c b/source/blender/blenkernel/intern/context.c index 4a68d90a4ed..78c29a96bff 100644 --- a/source/blender/blenkernel/intern/context.c +++ b/source/blender/blenkernel/intern/context.c @@ -68,6 +68,7 @@ struct bContext { struct Scene *scene; int recursion; + int py_init; /* true if python is initialized */ } data; /* data evaluation */ @@ -162,6 +163,16 @@ void CTX_store_free_list(ListBase *contexts) } } +/* is python initialied? */ +int CTX_py_init_get(bContext *C) +{ + return C->data.py_init; +} +void CTX_py_init_set(bContext *C, int value) +{ + C->data.py_init= value; +} + /* window manager context */ wmWindowManager *CTX_wm_manager(const bContext *C) @@ -204,6 +215,11 @@ struct ARegion *CTX_wm_menu(const bContext *C) return C->wm.menu; } +struct ReportList *CTX_wm_reports(const bContext *C) +{ + return &(C->wm.manager->reports); +} + View3D *CTX_wm_view3d(const bContext *C) { if(C->wm.area && C->wm.area->spacetype==SPACE_VIEW3D) @@ -226,6 +242,13 @@ struct SpaceText *CTX_wm_space_text(const bContext *C) return NULL; } +struct SpaceConsole *CTX_wm_space_console(const bContext *C) +{ + if(C->wm.area && C->wm.area->spacetype==SPACE_CONSOLE) + return C->wm.area->spacedata.first; + return NULL; +} + struct SpaceImage *CTX_wm_space_image(const bContext *C) { if(C->wm.area && C->wm.area->spacetype==SPACE_IMAGE) @@ -233,6 +256,83 @@ struct SpaceImage *CTX_wm_space_image(const bContext *C) return NULL; } +struct SpaceButs *CTX_wm_space_buts(const bContext *C) +{ + if(C->wm.area && C->wm.area->spacetype==SPACE_BUTS) + return C->wm.area->spacedata.first; + return NULL; +} + +struct SpaceFile *CTX_wm_space_file(const bContext *C) +{ + if(C->wm.area && C->wm.area->spacetype==SPACE_FILE) + return C->wm.area->spacedata.first; + return NULL; +} + +struct SpaceSeq *CTX_wm_space_seq(const bContext *C) +{ + if(C->wm.area && C->wm.area->spacetype==SPACE_SEQ) + return C->wm.area->spacedata.first; + return NULL; +} + +struct SpaceOops *CTX_wm_space_outliner(const bContext *C) +{ + if(C->wm.area && C->wm.area->spacetype==SPACE_OUTLINER) + return C->wm.area->spacedata.first; + return NULL; +} + +struct SpaceNla *CTX_wm_space_nla(const bContext *C) +{ + if(C->wm.area && C->wm.area->spacetype==SPACE_NLA) + return C->wm.area->spacedata.first; + return NULL; +} + +struct SpaceTime *CTX_wm_space_time(const bContext *C) +{ + if(C->wm.area && C->wm.area->spacetype==SPACE_TIME) + return C->wm.area->spacedata.first; + return NULL; +} + +struct SpaceNode *CTX_wm_space_node(const bContext *C) +{ + if(C->wm.area && C->wm.area->spacetype==SPACE_NODE) + return C->wm.area->spacedata.first; + return NULL; +} + +struct SpaceLogic *CTX_wm_space_logic(const bContext *C) +{ + if(C->wm.area && C->wm.area->spacetype==SPACE_LOGIC) + return C->wm.area->spacedata.first; + return NULL; +} + +struct SpaceIpo *CTX_wm_space_graph(const bContext *C) +{ + if(C->wm.area && C->wm.area->spacetype==SPACE_IPO) + return C->wm.area->spacedata.first; + return NULL; +} + +struct SpaceAction *CTX_wm_space_action(const bContext *C) +{ + if(C->wm.area && C->wm.area->spacetype==SPACE_ACTION) + return C->wm.area->spacedata.first; + return NULL; +} + +struct SpaceInfo *CTX_wm_space_info(const bContext *C) +{ + if(C->wm.area && C->wm.area->spacetype==SPACE_INFO) + return C->wm.area->spacedata.first; + return NULL; +} + void CTX_wm_manager_set(bContext *C, wmWindowManager *wm) { C->wm.manager= wm; @@ -606,6 +706,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/depsgraph.c b/source/blender/blenkernel/intern/depsgraph.c index de036e25d82..6c765b02e5d 100644 --- a/source/blender/blenkernel/intern/depsgraph.c +++ b/source/blender/blenkernel/intern/depsgraph.c @@ -39,6 +39,7 @@ #include "DNA_anim_types.h" #include "DNA_action_types.h" #include "DNA_armature_types.h" +#include "DNA_boid_types.h" #include "DNA_curve_types.h" #include "DNA_camera_types.h" #include "DNA_ID.h" @@ -555,6 +556,8 @@ static void build_dag_object(DagForest *dag, DagNode *scenenode, Scene *scene, O GroupObject *go; for(; psys; psys=psys->next) { + BoidRule *rule = NULL; + BoidState *state = NULL; ParticleSettings *part= psys->part; dag_add_relation(dag, node, node, DAG_RL_OB_DATA, "Particle-Object Relation"); @@ -562,10 +565,15 @@ static void build_dag_object(DagForest *dag, DagNode *scenenode, Scene *scene, O 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(ELEM(part->phystype,PART_PHYS_KEYED,PART_PHYS_BOIDS)) { + ParticleTarget *pt = psys->targets.first; + + for(; pt; pt=pt->next) { + if(pt->ob && BLI_findlink(&pt->ob->particlesystem, pt->psys-1)) { + node2 = dag_get_node(dag, pt->ob); + dag_add_relation(dag, node2, node, DAG_RL_DATA_DATA|DAG_RL_OB_DATA, "Particle Targets"); + } + } } if(part->ren_as == PART_DRAW_OB && part->dup_ob) { @@ -582,33 +590,47 @@ static void build_dag_object(DagForest *dag, DagNode *scenenode, Scene *scene, O } } - if(psys->effectors.first) - psys_end_effectors(psys); + psys_end_effectors(psys); psys_init_effectors(scene, ob, psys->part->eff_group, psys); - if(psys->effectors.first) { - for(nec= psys->effectors.first; nec; nec= nec->next) { - Object *ob1= nec->ob; + for(nec= psys->effectors.first; nec; nec= nec->next) { + Object *ob1= nec->ob; - if(nec->type & PSYS_EC_EFFECTOR) { - node2 = dag_get_node(dag, ob1); - if(ob1->pd->forcefield==PFIELD_GUIDE) - dag_add_relation(dag, node2, node, DAG_RL_DATA_DATA|DAG_RL_OB_DATA, "Particle Field"); - else - dag_add_relation(dag, node2, node, DAG_RL_OB_DATA, "Particle Field"); - } - else if(nec->type & PSYS_EC_DEFLECT) { - node2 = dag_get_node(dag, ob1); - dag_add_relation(dag, node2, node, DAG_RL_DATA_DATA|DAG_RL_OB_DATA, "Particle Collision"); - } - else if(nec->type & PSYS_EC_PARTICLE) { - node2 = dag_get_node(dag, ob1); - dag_add_relation(dag, node2, node, DAG_RL_DATA_DATA, "Particle Field"); - } - - if(nec->type & PSYS_EC_REACTOR) { - node2 = dag_get_node(dag, ob1); - dag_add_relation(dag, node, node2, DAG_RL_DATA_DATA, "Particle Reactor"); + if(nec->type & PSYS_EC_EFFECTOR) { + node2 = dag_get_node(dag, ob1); + if(ob1->pd->forcefield==PFIELD_GUIDE) + dag_add_relation(dag, node2, node, DAG_RL_DATA_DATA|DAG_RL_OB_DATA, "Particle Field"); + else + dag_add_relation(dag, node2, node, DAG_RL_OB_DATA, "Particle Field"); + } + else if(nec->type & PSYS_EC_DEFLECT) { + node2 = dag_get_node(dag, ob1); + dag_add_relation(dag, node2, node, DAG_RL_DATA_DATA|DAG_RL_OB_DATA, "Particle Collision"); + } + else if(nec->type & PSYS_EC_PARTICLE) { + node2 = dag_get_node(dag, ob1); + dag_add_relation(dag, node2, node, DAG_RL_DATA_DATA, "Particle Field"); + } + + if(nec->type & PSYS_EC_REACTOR) { + node2 = dag_get_node(dag, ob1); + dag_add_relation(dag, node, node2, DAG_RL_DATA_DATA, "Particle Reactor"); + } + } + + if(part->boids) { + for(state = part->boids->states.first; state; state=state->next) { + for(rule = state->rules.first; rule; rule=rule->next) { + Object *ruleob = NULL; + if(rule->type==eBoidRuleType_Avoid) + ruleob = ((BoidRuleGoalAvoid*)rule)->ob; + else if(rule->type==eBoidRuleType_FollowLeader) + ruleob = ((BoidRuleFollowLeader*)rule)->ob; + + if(ruleob) { + node2 = dag_get_node(dag, ruleob); + dag_add_relation(dag, node2, node, DAG_RL_OB_DATA, "Boid Rule"); + } } } } @@ -1975,8 +1997,6 @@ static void dag_object_time_update_flags(Object *ob) } } - if(ob->scriptlink.totscript) ob->recalc |= OB_RECALC_OB; - if(ob->parent) { /* motion path or bone child */ if(ob->parent->type==OB_CURVE || ob->parent->type==OB_ARMATURE) ob->recalc |= OB_RECALC_OB; @@ -2063,7 +2083,6 @@ static void dag_object_time_update_flags(Object *ob) } } } - /* flag all objects that need recalc, for changes in time for example */ void DAG_scene_update_flags(Scene *scene, unsigned int lay) { diff --git a/source/blender/blenkernel/intern/effect.c b/source/blender/blenkernel/intern/effect.c index 553fdfe530e..e3c4f12184e 100644 --- a/source/blender/blenkernel/intern/effect.c +++ b/source/blender/blenkernel/intern/effect.c @@ -105,6 +105,7 @@ PartDeflect *object_add_collision_fields(void) pd->pdef_sbift = 0.2f; pd->pdef_sboft = 0.02f; pd->seed = ((unsigned int)(ceil(PIL_check_seconds_timer()))+1) % 128; + pd->f_strength = 1.0f; return pd; } @@ -504,6 +505,9 @@ void do_physical_effector(Scene *scene, Object *ob, float *opco, short type, flo VecAddf(field,field,mag_vec); break; } + case PFIELD_BOID: + /* Boid field is handled completely in boids code. */ + break; } } diff --git a/source/blender/blenkernel/intern/exotic.c b/source/blender/blenkernel/intern/exotic.c index 4e7e76dfae3..8827897a509 100644 --- a/source/blender/blenkernel/intern/exotic.c +++ b/source/blender/blenkernel/intern/exotic.c @@ -334,7 +334,7 @@ static void read_stl_mesh_ascii(Scene *scene, char *str) */ numtenthousand = 1; vertdata = malloc(numtenthousand*3*30000*sizeof(float)); // uses realloc! - if (!vertdata); STLALLOCERROR; + if (!vertdata) { STLALLOCERROR; } linenum = 1; /* Get rid of the first line */ @@ -357,7 +357,7 @@ static void read_stl_mesh_ascii(Scene *scene, char *str) ++numtenthousand; vertdata = realloc(vertdata, numtenthousand*3*30000*sizeof(float)); - if (!vertdata); STLALLOCERROR; + if (!vertdata) { STLALLOCERROR; } } /* Don't read normal, but check line for proper syntax anyway @@ -1415,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; @@ -1433,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; @@ -1482,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) { @@ -1691,7 +1687,7 @@ static void displist_to_objects(Scene *scene, ListBase *lbase) if(totvert==0) { - if(ivsurf==0) ; //XXX error("Found no data"); + if(ivsurf==0) {}; //XXX error("Found no data"); if(lbase->first) BLI_freelistN(lbase); return; @@ -1904,16 +1900,15 @@ void write_stl(Scene *scene, char *str) if(BLI_testextensie(str,".ble")) str[ strlen(str)-4]= 0; if(BLI_testextensie(str,".stl")==0) strcat(str, ".stl"); - if (!during_script()) { - if (BLI_exists(str)) - ; //XXX if(saveover(str)==0) - //XXX return; + if (BLI_exists(str)) { + ; //XXX if(saveover(str)==0) + //XXX return; } fpSTL= fopen(str, "wb"); if(fpSTL==NULL) { - if (!during_script()) ; //XXX error("Can't write file"); + //XXX error("Can't write file"); return; } strcpy(temp_dir, str); @@ -2237,11 +2232,11 @@ void write_vrml(Scene *scene, char *str) if(BLI_testextensie(str,".blend")) str[ strlen(str)-6]= 0; if(BLI_testextensie(str,".ble")) str[ strlen(str)-4]= 0; if(BLI_testextensie(str,".wrl")==0) strcat(str, ".wrl"); - //XXX saveover() if(!during_script() && saveover(str)==0) return; + //XXX saveover() if(saveover(str)==0) return; fp= fopen(str, "w"); - if(fp==NULL && !during_script()) { + if(fp==NULL) { //XXX error("Can't write file"); return; } @@ -2548,15 +2543,15 @@ void write_dxf(struct Scene *scene, char *str) if(BLI_testextensie(str,".ble")) str[ strlen(str)-4]= 0; if(BLI_testextensie(str,".dxf")==0) strcat(str, ".dxf"); - if (!during_script()) { - if (BLI_exists(str)) - ; //XXX if(saveover(str)==0) - // return; + + if (BLI_exists(str)) { + ; //XXX if(saveover(str)==0) + // return; } fp= fopen(str, "w"); - if(fp==NULL && !during_script()) { + if(fp==NULL) { //XXX error("Can't write file"); return; } @@ -2804,8 +2799,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"); @@ -4053,7 +4051,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; @@ -4072,9 +4069,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 2b4032af503..ad9e481ffd2 100644 --- a/source/blender/blenkernel/intern/fluidsim.c +++ b/source/blender/blenkernel/intern/fluidsim.c @@ -83,9 +83,9 @@ void fluidsim_init(FluidsimModifierData *fluidmd) fss->type = OB_FLUIDSIM_ENABLE; fss->show_advancedoptions = 0; - fss->resolutionxyz = 50; - fss->previewresxyz = 25; - fss->realsize = 0.03; + fss->resolutionxyz = 65; + fss->previewresxyz = 45; + fss->realsize = 0.5; fss->guiDisplayMode = 2; // preview fss->renderDisplayMode = 3; // render @@ -98,7 +98,7 @@ void fluidsim_init(FluidsimModifierData *fluidmd) fss->gravy = 0.0; fss->gravz = -9.81; fss->animStart = 0.0; - fss->animEnd = 0.30; + fss->animEnd = 4.0; fss->gstar = 0.005; // used as normgstar fss->maxRefine = -1; // maxRefine is set according to resolutionxyz during bake @@ -114,15 +114,15 @@ void fluidsim_init(FluidsimModifierData *fluidmd) // no bounding box needed // todo - reuse default init from elbeem! - fss->typeFlags = OB_FSBND_NOSLIP; + fss->typeFlags = OB_FSBND_PARTSLIP; fss->domainNovecgen = 0; fss->volumeInitType = 1; // volume - fss->partSlipValue = 0.0; + fss->partSlipValue = 0.2; fss->generateTracers = 0; fss->generateParticles = 0.0; fss->surfaceSmoothing = 1.0; - fss->surfaceSubdivs = 1.0; + fss->surfaceSubdivs = 0.0; fss->particleInfSize = 0.0; fss->particleInfAlpha = 0.0; diff --git a/source/blender/blenkernel/intern/fmodifier.c b/source/blender/blenkernel/intern/fmodifier.c new file mode 100644 index 00000000000..64558d0b456 --- /dev/null +++ b/source/blender/blenkernel/intern/fmodifier.c @@ -0,0 +1,1197 @@ +/** + * $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. + * + * Contributor(s): Joshua Leung (full recode) + * + * ***** END GPL LICENSE BLOCK ***** + */ + + +#include <math.h> +#include <stdio.h> +#include <stddef.h> +#include <string.h> +#include <float.h> + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "MEM_guardedalloc.h" + +#include "DNA_anim_types.h" + +#include "BLI_blenlib.h" +#include "BLI_arithb.h" +#include "BLI_noise.h" + +#include "BKE_fcurve.h" +#include "BKE_curve.h" +#include "BKE_global.h" +#include "BKE_idprop.h" +#include "BKE_utildefines.h" + +#include "RNA_access.h" +#include "RNA_types.h" + +#ifndef DISABLE_PYTHON +#include "BPY_extern.h" /* for BPY_pydriver_eval() */ +#endif + +#define SMALL -1.0e-10 +#define SELECT 1 + +/* ******************************** F-Modifiers ********************************* */ + +/* Info ------------------------------- */ + +/* F-Modifiers are modifiers which operate on F-Curves. However, they can also be defined + * on NLA-Strips to affect all of the F-Curves referenced by the NLA-Strip. + */ + +/* 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])) + */ + +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; + } +} + +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; + } +} + +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 */ +}; + +/* Built-In Function Generator F-Curve Modifier --------------------------- */ + +/* This uses the general equation for equations: + * y = amplitude * fn(phase_multiplier*x + phase_offset) + y_offset + * + * where amplitude, phase_multiplier/offset, y_offset are user-defined coefficients, + * x is the evaluation 'time', and 'y' is the resultant value + * + * Functions available are + * sin, cos, tan, sinc (normalised sin), natural log, square root + */ + +static void fcm_fn_generator_new_data (void *mdata) +{ + FMod_FunctionGenerator *data= (FMod_FunctionGenerator *)mdata; + + /* set amplitude and phase multiplier to 1.0f so that something is generated */ + data->amplitude= 1.0f; + data->phase_multiplier= 1.0f; +} + +/* Unary 'normalised sine' function + * y = sin(PI + x) / (PI * x), + * except for x = 0 when y = 1. + */ +static double sinc (double x) +{ + if (fabs(x) < 0.0001) + return 1.0; + else + return sin(M_PI * x) / (M_PI * x); +} + +static void fcm_fn_generator_evaluate (FCurve *fcu, FModifier *fcm, float *cvalue, float evaltime) +{ + FMod_FunctionGenerator *data= (FMod_FunctionGenerator *)fcm->data; + double arg= data->phase_multiplier*evaltime + data->phase_offset; + 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->type) + { + /* simple ones */ + case FCM_GENERATOR_FN_SIN: /* sine wave */ + fn= sin; + break; + case FCM_GENERATOR_FN_COS: /* cosine wave */ + fn= cos; + break; + case FCM_GENERATOR_FN_SINC: /* normalised sine wave */ + fn= sinc; + 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->type); + } + + /* execute function callback to set value if appropriate */ + if (fn) { + float value= (float)(data->amplitude*fn(arg) + data->value_offset); + + if (data->flag & FCM_GENERATOR_ADDITIVE) + *cvalue += value; + else + *cvalue= value; + } +} + +static FModifierTypeInfo FMI_FN_GENERATOR = { + FMODIFIER_TYPE_FN_GENERATOR, /* type */ + sizeof(FMod_FunctionGenerator), /* size */ + FMI_TYPE_GENERATE_CURVE, /* action type */ + FMI_REQUIRES_NOTHING, /* requirements */ + "Built-In Function", /* name */ + "FMod_FunctionGenerator", /* struct name */ + NULL, /* free data */ + NULL, /* copy data */ + fcm_fn_generator_new_data, /* new data */ + NULL, /* verify */ + NULL, /* evaluate time */ + fcm_fn_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_FN_GENERATOR; /* Built-In Function Generator F-Curve Modifier */ + fmodifiersTypeInfo[3]= &FMI_ENVELOPE; /* Envelope F-Curve Modifier */ + fmodifiersTypeInfo[4]= &FMI_CYCLES; /* Cycles F-Curve Modifier */ + fmodifiersTypeInfo[5]= &FMI_NOISE; /* Apply-Noise F-Curve Modifier */ + fmodifiersTypeInfo[6]= NULL/*&FMI_FILTER*/; /* Filter F-Curve Modifier */ // XXX unimplemented + fmodifiersTypeInfo[7]= &FMI_PYTHON; /* Custom Python F-Curve Modifier */ + fmodifiersTypeInfo[8]= &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 *add_fmodifier (ListBase *modifiers, int type) +{ + FModifierTypeInfo *fmi= get_fmodifier_typeinfo(type); + FModifier *fcm; + + /* sanity checks */ + if ELEM(NULL, modifiers, fmi) + return NULL; + + /* special checks for whether modifier can be added */ + if ((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(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-Modifiers in the Modifier stacks */ +void copy_fmodifiers (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-Modifier from the given stack */ +void remove_fmodifier (ListBase *modifiers, 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 (modifiers) + BLI_freelinkN(modifiers, fcm); + else { + // XXX this case can probably be removed some day, as it shouldn't happen... + printf("remove_fmodifier() - no modifier stack given \n"); + MEM_freeN(fcm); + } +} + +/* Remove all of a given F-Curve's modifiers */ +void free_fmodifiers (ListBase *modifiers) +{ + FModifier *fcm, *fmn; + + /* sanity check */ + if (modifiers == NULL) + return; + + /* free each modifier in order - modifier is unlinked from list and freed */ + for (fcm= modifiers->first; fcm; fcm= fmn) { + fmn= fcm->next; + remove_fmodifier(modifiers, fcm); + } +} + +/* Find the active F-Modifier */ +FModifier *find_active_fmodifier (ListBase *modifiers) +{ + FModifier *fcm; + + /* sanity checks */ + if ELEM(NULL, modifiers, modifiers->first) + return NULL; + + /* loop over modifiers until 'active' one is found */ + for (fcm= modifiers->first; fcm; fcm= fcm->next) { + if (fcm->flag & FMODIFIER_FLAG_ACTIVE) + return fcm; + } + + /* no modifier is active */ + return NULL; +} + +/* Set the active F-Modifier */ +void set_active_fmodifier (ListBase *modifiers, FModifier *fcm) +{ + FModifier *fm; + + /* sanity checks */ + if ELEM(NULL, modifiers, modifiers->first) + return; + + /* deactivate all, and set current one active */ + for (fm= modifiers->first; fm; fm= fm->next) + fm->flag &= ~FMODIFIER_FLAG_ACTIVE; + + /* make given modifier active */ + if (fcm) + fcm->flag |= FMODIFIER_FLAG_ACTIVE; +} + +/* Do we have any modifiers which match certain criteria + * - mtype - type of modifier (if 0, doesn't matter) + * - acttype - type of action to perform (if -1, doesn't matter) + */ +short list_has_suitable_fmodifier (ListBase *modifiers, int mtype, short acttype) +{ + FModifier *fcm; + + /* if there are no specific filtering criteria, just skip */ + if ((mtype == 0) && (acttype == 0)) + return (modifiers && modifiers->first); + + /* sanity checks */ + if ELEM(NULL, modifiers, modifiers->first) + return 0; + + /* find the first mdifier fitting these criteria */ + for (fcm= modifiers->first; fcm; fcm= fcm->next) { + FModifierTypeInfo *fmi= fmodifier_get_typeinfo(fcm); + short mOk=1, aOk=1; /* by default 1, so that when only one test, won't fail */ + + /* check if applicable ones are fullfilled */ + if (mtype) + mOk= (fcm->type == mtype); + if (acttype > -1) + aOk= (fmi->acttype == acttype); + + /* if both are ok, we've found a hit */ + if (mOk && aOk) + return 1; + } + + /* no matches */ + return 0; +} + +/* Evaluation API --------------------------- */ + +/* 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 + */ +float evaluate_time_fmodifiers (ListBase *modifiers, FCurve *fcu, float cvalue, float evaltime) +{ + FModifier *fcm; + float m_evaltime= evaltime; + + /* sanity checks */ + if ELEM(NULL, modifiers, modifiers->last) + return evaltime; + + /* find the first modifier from end of stack that modifies time, and calculate the time the modifier + * would calculate time at + */ + for (fcm= 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) + m_evaltime= fmi->evaluate_modifier_time(fcu, fcm, cvalue, evaltime); + break; + } + } + + /* return the modified evaltime */ + return m_evaltime; +} + +/* Evalautes the given set of F-Curve Modifiers using the given data + * Should only be called after evaluate_time_fmodifiers() has been called... + */ +void evaluate_value_fmodifiers (ListBase *modifiers, FCurve *fcu, float *cvalue, float evaltime) +{ + FModifier *fcm; + + /* sanity checks */ + if ELEM(NULL, modifiers, modifiers->first) + return; + + /* evaluate modifiers */ + for (fcm= 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); + } + } +} + +/* ---------- */ + +/* 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 */ + free_fmodifiers(&fcu->modifiers); + + /* restore driver */ + fcu->driver= driver; +} diff --git a/source/blender/blenkernel/intern/image.c b/source/blender/blenkernel/intern/image.c index 754ec06f23f..62af05fbc9a 100644 --- a/source/blender/blenkernel/intern/image.c +++ b/source/blender/blenkernel/intern/image.c @@ -456,19 +456,19 @@ static ImBuf *add_ibuf_size(int width, int height, char *name, int floatbuf, sho * easy to tweak like this, speed isn't really that much of an issue in this situation... */ /* checkers */ - for(y=0; y<ibuf->y; y++) { - dark = pow(-1, floor(y / checkerwidth)); + for(y=0; y<height; y++) { + dark = powf(-1.0f, floorf(y / checkerwidth)); - for(x=0; x<ibuf->x; x++) { + for(x=0; x<width; x++) { if (x % checkerwidth == 0) dark *= -1; if (floatbuf) { if (dark > 0) { - rect_float[0] = rect_float[1] = rect_float[2] = 0.25; - rect_float[3] = 1.0; + rect_float[0] = rect_float[1] = rect_float[2] = 0.25f; + rect_float[3] = 1.0f; } else { - rect_float[0] = rect_float[1] = rect_float[2] = 0.58; - rect_float[3] = 1.0; + rect_float[0] = rect_float[1] = rect_float[2] = 0.58f; + rect_float[3] = 1.0f; } rect_float+=4; } @@ -489,11 +489,11 @@ static ImBuf *add_ibuf_size(int width, int height, char *name, int floatbuf, sho if (floatbuf) rect_float= (float*)ibuf->rect_float; else rect= (unsigned char*)ibuf->rect; - for(y=0; y<ibuf->y; y++) { - hoffs = 0.125 * floor(y / checkerwidth); + for(y=0; y<height; y++) { + hoffs = 0.125f * floorf(y / checkerwidth); - for(x=0; x<ibuf->x; x++) { - h = 0.125 * floor(x / checkerwidth); + for(x=0; x<width; x++) { + h = 0.125f * floorf(x / checkerwidth); if ((fabs((x % checkerwidth) - (checkerwidth / 2)) < 4) && (fabs((y % checkerwidth) - (checkerwidth / 2)) < 4)) { @@ -501,19 +501,19 @@ static ImBuf *add_ibuf_size(int width, int height, char *name, int floatbuf, sho if ((fabs((x % checkerwidth) - (checkerwidth / 2)) < 1) || (fabs((y % checkerwidth) - (checkerwidth / 2)) < 1)) { - hue = fmod(fabs(h-hoffs), 1.0); + hue = fmodf(fabs(h-hoffs), 1.0f); hsv_to_rgb(hue, s, v, &r, &g, &b); if (floatbuf) { rect_float[0]= r; rect_float[1]= g; rect_float[2]= b; - rect_float[3]= 1.0; + rect_float[3]= 1.0f; } else { - rect[0]= (char)(r * 255.0); - rect[1]= (char)(g * 255.0); - rect[2]= (char)(b * 255.0); + rect[0]= (char)(r * 255.0f); + rect[1]= (char)(g * 255.0f); + rect[2]= (char)(b * 255.0f); rect[3]= 255; } } @@ -526,8 +526,15 @@ static ImBuf *add_ibuf_size(int width, int height, char *name, int floatbuf, sho } } } else { /* blank image */ - for(y=0; y<ibuf->y; y++) { - for(x=0; x<ibuf->x; x++) { + char ccol[4]; + + ccol[0]= (char)(color[0]*255.0f); + ccol[1]= (char)(color[1]*255.0f); + ccol[2]= (char)(color[2]*255.0f); + ccol[3]= (char)(color[3]*255.0f); + + for(y=0; y<height; y++) { + for(x=0; x<width; x++) { if (floatbuf) { rect_float[0]= color[0]; rect_float[1]= color[1]; @@ -536,10 +543,10 @@ static ImBuf *add_ibuf_size(int width, int height, char *name, int floatbuf, sho rect_float+=4; } else { - rect[0]= (char)(color[0] * 255.0); - rect[1]= (char)(color[1] * 255.0); - rect[2]= (char)(color[2] * 255.0); - rect[3]= (char)(color[3] * 255.0); + rect[0]= ccol[0]; + rect[1]= ccol[1]; + rect[2]= ccol[2]; + rect[3]= ccol[3]; rect+=4; } } @@ -860,6 +867,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; } @@ -870,7 +880,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"; } @@ -882,7 +892,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"; } @@ -1230,7 +1240,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 @@ -1305,7 +1315,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); diff --git a/source/blender/blenkernel/intern/ipo.c b/source/blender/blenkernel/intern/ipo.c index 54618813a0b..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" @@ -825,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 */ @@ -840,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 */ @@ -870,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 */ @@ -1144,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 */ @@ -1463,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() */ @@ -1509,7 +1599,30 @@ 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 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; + } + + /* 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); @@ -1530,9 +1643,6 @@ void do_versions_ipos_to_animato(Main *main) ob->ipo->id.us--; ob->ipo= NULL; } - - /* finally NLA */ - // XXX todo... for now, new NLA code not hooked up yet, so keep old stuff (but not for too long!) } /* check PoseChannels for constraints with local data */ diff --git a/source/blender/blenkernel/intern/material.c b/source/blender/blenkernel/intern/material.c index 57b88bb0b3f..08a19cada7d 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" @@ -79,10 +80,6 @@ void free_material(Material *ma) { MTex *mtex; int a; - -#ifndef DISABLE_PYTHON - BPY_free_scriptlink(&ma->scriptlink); -#endif for(a=0; a<MAX_MTEX; a++) { mtex= ma->mtex[a]; @@ -209,10 +206,6 @@ Material *copy_material(Material *ma) id_us_plus((ID *)man->mtex[a]->tex); } } - -#ifndef DISABLE_PYTHON - BPY_copy_scriptlink(&ma->scriptlink); -#endif if(ma->ramp_col) man->ramp_col= MEM_dupallocN(ma->ramp_col); if(ma->ramp_spec) man->ramp_spec= MEM_dupallocN(ma->ramp_spec); @@ -445,7 +438,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 +466,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 +491,7 @@ void test_object_materials(ID *id) Curve *cu; MetaBall *mb; Material **newmatar; + char *newmatbits; int totcol=0; if(id==0) return; @@ -524,16 +518,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 +547,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 +560,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,7 +604,9 @@ void assign_material(Object *ob, Material *ma, int act) if(mao) mao->id.us--; (*matarar)[act-1]= ma; } - id_us_plus((ID *)ma); + + if(ma) + id_us_plus((ID *)ma); test_object_materials(ob->data); } @@ -623,19 +638,7 @@ void object_add_material_slot(Object *ob) if(ob->totcol>=MAXMAT) return; ma= give_current_material(ob, ob->actcol); - if(ma==NULL) - ma= add_material("Material"); - else - ma= copy_material(ma); - - 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; } @@ -660,6 +663,7 @@ static void do_init_render_material(Material *ma, int r_mode, float *amb) ma->mapto |= mtex->mapto; if(r_mode & R_OSA) { if ELEM3(mtex->tex->type, TEX_IMAGE, TEX_PLUGIN, TEX_ENVMAP) ma->texco |= TEXCO_OSA; + else if(mtex->texflag & MTEX_NEW_BUMP) ma->texco |= TEXCO_OSA; // NEWBUMP: need texture derivatives for procedurals as well } if(ma->texco & (TEXCO_ORCO|TEXCO_REFL|TEXCO_NORM|TEXCO_STRAND|TEXCO_STRESS)) needuv= 1; @@ -880,9 +884,8 @@ void object_remove_material_slot(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 +903,18 @@ void object_remove_material_slot(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 3facf975992..c7454d3b832 100644 --- a/source/blender/blenkernel/intern/mesh.c +++ b/source/blender/blenkernel/intern/mesh.c @@ -795,6 +795,8 @@ void nurbs_to_mesh(Object *ob) dl= cu->disp.first; while(dl) { + int smooth= dl->rt & CU_SMOOTH ? 1 : 0; + if(dl->type==DL_SEGM) { startvert= vertcount; a= dl->parts*dl->nr; @@ -811,6 +813,7 @@ void nurbs_to_mesh(Object *ob) for(b=1; b<dl->nr; b++) { mface->v1= startvert+ofs+b-1; mface->v2= startvert+ofs+b; + if(smooth) mface->flag |= ME_SMOOTH; mface++; } } @@ -835,6 +838,7 @@ void nurbs_to_mesh(Object *ob) mface->v1= startvert+ofs+b; if(b==dl->nr-1) mface->v2= startvert+ofs; else mface->v2= startvert+ofs+b+1; + if(smooth) mface->flag |= ME_SMOOTH; mface++; } } @@ -860,6 +864,7 @@ void nurbs_to_mesh(Object *ob) mface->v4= 0; test_index_face(mface, NULL, 0, 3); + if(smooth) mface->flag |= ME_SMOOTH; mface++; index+= 3; } @@ -907,6 +912,8 @@ void nurbs_to_mesh(Object *ob) mface->v4= p2; mface->mat_nr= (unsigned char)dl->col; test_index_face(mface, NULL, 0, 4); + + if(smooth) mface->flag |= ME_SMOOTH; mface++; p4= p3; diff --git a/source/blender/blenkernel/intern/modifier.c b/source/blender/blenkernel/intern/modifier.c index 046dfcc9f87..d86d563aaa2 100644 --- a/source/blender/blenkernel/intern/modifier.c +++ b/source/blender/blenkernel/intern/modifier.c @@ -58,6 +58,7 @@ #include "DNA_cloth_types.h" #include "DNA_curve_types.h" #include "DNA_effect_types.h" +#include "DNA_group_types.h" #include "DNA_material_types.h" #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" @@ -67,6 +68,7 @@ #include "DNA_object_force.h" #include "DNA_particle_types.h" #include "DNA_scene_types.h" +#include "DNA_smoke_types.h" #include "DNA_texture_types.h" #include "BLI_editVert.h" @@ -96,6 +98,7 @@ #include "BKE_object.h" #include "BKE_particle.h" #include "BKE_pointcache.h" +#include "BKE_smoke.h" #include "BKE_softbody.h" #include "BKE_subsurf.h" #include "BKE_texture.h" @@ -5069,7 +5072,7 @@ static void waveModifier_initData(ModifierData *md) wmd->map_object = NULL; wmd->height= 0.5f; wmd->width= 1.5f; - wmd->speed= 0.5f; + wmd->speed= 0.25f; wmd->narrow= 1.5f; wmd->lifetime= 0.0f; wmd->damp= 10.0f; @@ -5787,6 +5790,100 @@ static int softbodyModifier_dependsOnTime(ModifierData *md) return 1; } +/* Smoke */ + +static void smokeModifier_initData(ModifierData *md) +{ + SmokeModifierData *smd = (SmokeModifierData*) md; + + smd->domain = NULL; + smd->flow = NULL; + smd->coll = NULL; + smd->type = 0; + smd->time = -1; + + /* + smd->fluid = NULL; + smd->maxres = 48; + smd->amplify = 4; + smd->omega = 0.5; + smd->time = 0; + smd->flags = 0; + smd->noise = MOD_SMOKE_NOISEWAVE; + smd->visibility = 1; + + // init 3dview buffer + smd->tvox = NULL; + smd->tray = NULL; + smd->tvoxbig = NULL; + smd->traybig = NULL; + smd->viewsettings = 0; + smd->bind = NULL; + smd->max_textures = 0; + */ +} + +static void smokeModifier_freeData(ModifierData *md) +{ + SmokeModifierData *smd = (SmokeModifierData*) md; + + smokeModifier_free (smd); +} + +static void smokeModifier_deformVerts( + ModifierData *md, Object *ob, DerivedMesh *derivedData, + float (*vertexCos)[3], int numVerts, int useRenderParams, int isFinalCalc) +{ + SmokeModifierData *smd = (SmokeModifierData*) md; + DerivedMesh *dm = NULL; + + if(derivedData) dm = derivedData; + else if(ob->type == OB_MESH) dm = CDDM_from_mesh(ob->data, ob); + else return; + + CDDM_apply_vert_coords(dm, vertexCos); + CDDM_calc_normals(dm); + + smokeModifier_do(smd, md->scene, ob, dm); + + if(dm != derivedData) dm->release(dm); +} + +static int smokeModifier_dependsOnTime(ModifierData *md) +{ + return 1; +} + +static void smokeModifier_updateDepgraph( + ModifierData *md, DagForest *forest, Scene *scene, Object *ob, + DagNode *obNode) +{ + SmokeModifierData *smd = (SmokeModifierData *) md; + /* + if(smd && (smd->type & MOD_SMOKE_TYPE_DOMAIN) && smd->domain) + { + if(smd->domain->fluid_group) + { + GroupObject *go = NULL; + + for(go = smd->domain->fluid_group->gobject.first; go; go = go->next) + { + if(go->ob) + { + SmokeModifierData *smd2 = (SmokeModifierData *)modifiers_findByType(go->ob, eModifierType_Smoke); + + // check for initialized smoke object + if(smd2 && (smd2->type & MOD_SMOKE_TYPE_FLOW) && smd2->flow) + { + DagNode *curNode = dag_get_node(forest, go->ob); + dag_add_relation(forest, curNode, obNode, DAG_RL_DATA_DATA|DAG_RL_OB_DATA, "Smoke Flow"); + } + } + } + } + } + */ +} /* Cloth */ @@ -6113,9 +6210,14 @@ static void surfaceModifier_freeData(ModifierData *md) MEM_freeN(surmd->bvhtree); } - if(surmd->dm) - surmd->dm->release(surmd->dm); + surmd->dm->release(surmd->dm); + + if(surmd->x) + MEM_freeN(surmd->x); + if(surmd->v) + MEM_freeN(surmd->v); + surmd->bvhtree = NULL; surmd->dm = NULL; } @@ -6128,7 +6230,7 @@ static int surfaceModifier_dependsOnTime(ModifierData *md) static void surfaceModifier_deformVerts( ModifierData *md, Object *ob, DerivedMesh *derivedData, - float (*vertexCos)[3], int numVerts, int useRenderParams, int isFinalCalc) + float (*vertexCos)[3], int numVerts, int useRenderParams, int isFinalCalc) { SurfaceModifierData *surmd = (SurfaceModifierData*) md; unsigned int numverts = 0, i = 0; @@ -6148,14 +6250,47 @@ static void surfaceModifier_deformVerts( if(surmd->dm) { + int init = 0; + float *vec; + MVert *x, *v; + CDDM_apply_vert_coords(surmd->dm, vertexCos); CDDM_calc_normals(surmd->dm); numverts = surmd->dm->getNumVerts ( surmd->dm ); - /* convert to global coordinates */ - for(i = 0; i<numverts; i++) - Mat4MulVecfl(ob->obmat, CDDM_get_vert(surmd->dm, i)->co); + if(numverts != surmd->numverts || surmd->x == NULL || surmd->v == NULL || md->scene->r.cfra != surmd->cfra+1) { + if(surmd->x) { + MEM_freeN(surmd->x); + surmd->x = NULL; + } + if(surmd->v) { + MEM_freeN(surmd->v); + surmd->v = NULL; + } + + surmd->x = MEM_callocN(numverts * sizeof(MVert), "MVert"); + surmd->v = MEM_callocN(numverts * sizeof(MVert), "MVert"); + + surmd->numverts = numverts; + + init = 1; + } + + /* convert to global coordinates and calculate velocity */ + for(i = 0, x = surmd->x, v = surmd->v; i<numverts; i++, x++, v++) { + vec = CDDM_get_vert(surmd->dm, i)->co; + Mat4MulVecfl(ob->obmat, vec); + + if(init) + v->co[0] = v->co[1] = v->co[2] = 0.0f; + else + VecSubf(v->co, vec, x->co); + + VecCopyf(x->co, vec); + } + + surmd->cfra = md->scene->r.cfra; if(surmd->bvhtree) free_bvhtree_from_mesh(surmd->bvhtree); @@ -8439,6 +8574,15 @@ ModifierTypeInfo *modifierType_getInfo(ModifierType type) | eModifierTypeFlag_Single; mti->deformVerts = softbodyModifier_deformVerts; mti->dependsOnTime = softbodyModifier_dependsOnTime; + + mti = INIT_TYPE(Smoke); + mti->type = eModifierTypeType_OnlyDeform; + mti->initData = smokeModifier_initData; + mti->freeData = smokeModifier_freeData; + mti->flags = eModifierTypeFlag_AcceptsMesh; + mti->deformVerts = smokeModifier_deformVerts; + mti->dependsOnTime = smokeModifier_dependsOnTime; + mti->updateDepgraph = smokeModifier_updateDepgraph; mti = INIT_TYPE(Cloth); mti->type = eModifierTypeType_Nonconstructive; diff --git a/source/blender/blenkernel/intern/multires.c b/source/blender/blenkernel/intern/multires.c index 5def910ddef..22a471f6521 100644 --- a/source/blender/blenkernel/intern/multires.c +++ b/source/blender/blenkernel/intern/multires.c @@ -101,7 +101,7 @@ void multiresModifier_join(Object *ob) /* First find the highest level of subdivision */ base = FIRSTBASE; while(base) { - if(TESTBASELIB_BGMODE(base) && base->object->type==OB_MESH) { + if(TESTBASELIB_BGMODE(v3d, base) && base->object->type==OB_MESH) { ModifierData *md; for(md = base->object->modifiers.first; md; md = md->next) { if(md->type == eModifierType_Multires) { @@ -124,7 +124,7 @@ void multiresModifier_join(Object *ob) /* Subdivide all the displacements to the highest level */ base = FIRSTBASE; while(base) { - if(TESTBASELIB_BGMODE(base) && base->object->type==OB_MESH) { + if(TESTBASELIB_BGMODE(v3d, base) && base->object->type==OB_MESH) { ModifierData *md = NULL; MultiresModifierData *mmd = NULL; diff --git a/source/blender/blenkernel/intern/nla.c b/source/blender/blenkernel/intern/nla.c index dc2bf26759f..1871ec006f4 100644 --- a/source/blender/blenkernel/intern/nla.c +++ b/source/blender/blenkernel/intern/nla.c @@ -17,171 +17,1506 @@ * 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 */ - if (!*src){ - *dst=NULL; +/* 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); +} - *dst = MEM_dupallocN(sstrip); +/* 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); +} - dstrip = *dst; - if (dstrip->act) - dstrip->act->id.us++; +/* 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; +} - if (dstrip->ipo) - dstrip->ipo->id.us++; +/* Copying ------------------------------------------- */ + +/* Copy NLA strip */ +NlaStrip *copy_nlastrip (NlaStrip *strip) +{ + NlaStrip *strip_d; + NlaStrip *cs, *cs_d; + + /* sanity check */ + if (strip == NULL) + return NULL; + + /* make a copy */ + strip_d= MEM_dupallocN(strip); + strip_d->next= strip_d->prev= NULL; - if (dstrip->modifiers.first) { - BLI_duplicatelist (&dstrip->modifiers, &sstrip->modifiers); + /* 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; +} - 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 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; +} + +/* 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 (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->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; + } +} + +/* 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) +{ + 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); + } } -//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_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) { - bActionStrip *nstrip; + 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; +} - /* Make new actionstrip */ - nstrip = MEM_callocN(sizeof(bActionStrip), "bActionStrip"); + +/* 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; + } + + /* remove the selected strips from the track, and add to the meta */ + BLI_remlink(strips, strip); + BLI_addtail(&mstrip->strips, strip); + + /* 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; + } + } +} + +/* Split a meta-strip into a set of normal strips */ +void BKE_nlastrips_clear_metastrip (ListBase *strips, NlaStrip *strip) +{ + NlaStrip *cs, *csn; + + /* 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); + } + } + } + } +} + +/* 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) +{ + /* 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; - /* 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; + /* compute the new strip endpoints using the proportions */ + nStart= (p1 * nLen) + mstrip->start; + nEnd= (p2 * nLen) + mstrip->start; - find_stridechannel(ob, nstrip); - //set_active_strip(ob, nstrip); /* is in editnla as does UI calls */ + /* 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; - nstrip->repeat = 1.0; + 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(ob->nlastrips.first == NULL) - ob->nlaflag |= OB_NLA_OVERRIDE; +/* NLA-Tracks ---------------------------------------- */ + +/* Find the active NLA-track for the given stack */ +NlaTrack *BKE_nlatrack_find_active (ListBase *tracks) +{ + NlaTrack *nlt; - 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, 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; + } + /* 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; + } + + /* 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; +} -/* not strip itself! */ -void free_actionstrip(bActionStrip* strip) +/* 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) { - if (!strip) + 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; +} - if (strip->act){ - strip->act->id.us--; - strip->act = NULL; +/* 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) +{ + /* 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); +} + +/* 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); +} + +/* 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; } - if (strip->ipo){ - strip->ipo->id.us--; - strip->ipo = NULL; + + /* 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 (strip->modifiers.first) { - BLI_freelistN(&strip->modifiers); + if ( (stripLen > boundsLen) && + !(IN_RANGE(min, strip->start, strip->end) || + IN_RANGE(max, strip->start, strip->end)) ) + { + return 0; } + /* should be ok! */ + return 1; } -void free_nlastrips (ListBase *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) { - bActionStrip *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 ------------------------------------------- */ - if (!nlalist->first) +/* 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 ------------------------------------ */ - /* Do any specific freeing */ - for (strip=nlalist->first; strip; strip=strip->next) +/* 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); +} + +/* ---- */ + +/* Get strips which overlap the given one at the start/end of its range + * - strip: strip that we're finding overlaps for + * - track: nla-track that the overlapping strips should be found from + * - start, end: frames for the offending endpoints + */ +static void nlastrip_get_endpoint_overlaps (NlaStrip *strip, NlaTrack *track, float **start, float **end) +{ + NlaStrip *nls; + + /* find strips that overlap over the start/end of the given strip, + * but which don't cover the entire length + */ + // TODO: this scheme could get quite slow for doing this on many strips... + for (nls= track->strips.first; nls; nls= nls->next) { + /* check if strip overlaps (extends over or exactly on) the entire range of the strip we're validating */ + if ((nls->start <= strip->start) && (nls->end >= strip->end)) { + *start= NULL; + *end= NULL; + return; + } + + /* check if strip doesn't even occur anywhere near... */ + if (nls->end < strip->start) + continue; /* skip checking this strip... not worthy of mention */ + if (nls->start > strip->end) + return; /* the range we're after has already passed */ + + /* if this strip is not part of an island of continuous strips, it can be used + * - this check needs to be done for each end of the strip we try and use... + */ + if ((nls->next == NULL) || IS_EQ(nls->next->start, nls->end)==0) { + if ((nls->end > strip->start) && (nls->end < strip->end)) + *start= &nls->end; + } + if ((nls->prev == NULL) || IS_EQ(nls->prev->end, nls->start)==0) { + if ((nls->start < strip->end) && (nls->start > strip->start)) + *end= &nls->start; + } + } +} + +/* Determine auto-blending for the given strip */ +void BKE_nlastrip_validate_autoblends (NlaTrack *nlt, NlaStrip *nls) +{ + 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) + nlastrip_get_endpoint_overlaps(nls, nlt->prev, &ps, &pe); + if (nlt->next) + nlastrip_get_endpoint_overlaps(nls, nlt->next, &ns, &ne); + + /* set overlaps for this strip + * - don't use the values obtained though if the end in question + * is directly followed/preceeded by another strip, forming an + * 'island' of continuous strips + */ + if ( (ps || ns) && ((nls->prev == NULL) || IS_EQ(nls->prev->end, nls->start)==0) ) + { + /* start overlaps - pick the largest overlap */ + if ( ((ps && ns) && (*ps > *ns)) || (ps) ) + nls->blendin= *ps - nls->start; + else + nls->blendin= *ns - nls->start; + } + else /* no overlap allowed/needed */ + nls->blendin= 0.0f; + + if ( (pe || ne) && ((nls->next == NULL) || IS_EQ(nls->next->start, nls->end)==0) ) { - free_actionstrip (strip); - }; + /* end overlaps - pick the largest overlap */ + if ( ((pe && ne) && (*pe > *ne)) || (pe) ) + nls->blendout= nls->end - *pe; + else + nls->blendout= nls->end - *ne; + } + else /* no overlap allowed/needed */ + nls->blendout= 0.0f; +} - /* Free the whole list */ - BLI_freelistN(nlalist); +/* Ensure that auto-blending and other settings are set correctly */ +void BKE_nla_validate_state (AnimData *adt) +{ + NlaStrip *strip, *fstrip=NULL; + NlaTrack *nlt; + + /* sanity checks */ + if ELEM(NULL, adt, adt->nla_tracks.first) + return; + + /* adjust blending values for auto-blending, and also do an initial pass to find the earliest strip */ + for (nlt= adt->nla_tracks.first; nlt; nlt= nlt->next) { + for (strip= nlt->strips.first; strip; strip= strip->next) { + /* auto-blending first */ + BKE_nlastrip_validate_autoblends(nlt, strip); + + /* extend mode - find first strip */ + if ((fstrip == NULL) || (strip->start < fstrip->start)) + fstrip= strip; + } + } + + /* second pass over the strips to adjust the extend-mode to fix any problems */ + for (nlt= adt->nla_tracks.first; nlt; nlt= nlt->next) { + for (strip= nlt->strips.first; strip; strip= strip->next) { + /* apart from 'nothing' option which user has to explicitly choose, we don't really know if + * we should be overwriting the extend setting (but assume that's what the user wanted) + */ + // TODO: 1 solution is to tie this in with auto-blending... + if (strip->extendmode != NLASTRIP_EXTEND_NOTHING) { + if (strip == fstrip) + strip->extendmode= NLASTRIP_EXTEND_HOLD; + else + strip->extendmode= NLASTRIP_EXTEND_HOLD_FORWARD; + } + } + } +} + +/* 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/object.c b/source/blender/blenkernel/intern/object.c index 81bd78f1851..e463d007a2d 100644 --- a/source/blender/blenkernel/intern/object.c +++ b/source/blender/blenkernel/intern/object.c @@ -42,6 +42,7 @@ #include "DNA_anim_types.h" #include "DNA_action_types.h" #include "DNA_armature_types.h" +#include "DNA_boid_types.h" #include "DNA_camera_types.h" #include "DNA_constraint_types.h" #include "DNA_curve_types.h" @@ -243,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); @@ -263,10 +266,6 @@ void free_object(Object *ob) free_actuators(&ob->actuators); free_constraints(&ob->constraints); - -#ifndef DISABLE_PYTHON - BPY_free_scriptlink(&ob->scriptlink); -#endif if(ob->pd){ if(ob->pd->tex) @@ -427,17 +426,16 @@ 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); + BoidState *state = NULL; + BoidRule *rule = NULL; - if(psys && psys->keyed_ob) { - tpsys->keyed_ob= psys->keyed_ob; - tpsys->keyed_psys= psys->keyed_psys; + ParticleTarget *pt = tpsys->targets.first; + for(; pt; pt=pt->next) { + if(pt->ob==ob) { + pt->ob = NULL; + obt->recalc |= OB_RECALC_DATA; + break; } - else - tpsys->keyed_ob= NULL; - - obt->recalc |= OB_RECALC_DATA; } if(tpsys->target_ob==ob) { @@ -459,6 +457,22 @@ void unlink_object(Scene *scene, Object *ob) } } } + if(tpsys->part->boids) { + for(state = tpsys->part->boids->states.first; state; state=state->next) { + for(rule = state->rules.first; rule; rule=rule->next) { + if(rule->type==eBoidRuleType_Avoid) { + BoidRuleGoalAvoid *gabr = (BoidRuleGoalAvoid*)rule; + if(gabr->ob==ob) + gabr->ob= NULL; + } + else if(rule->type==eBoidRuleType_FollowLeader) { + BoidRuleFollowLeader *flbr = (BoidRuleFollowLeader*)rule; + if(flbr->ob==ob) + flbr->ob= NULL; + } + } + } + } } if(ob->pd) obt->recalc |= OB_RECALC_DATA; @@ -627,10 +641,7 @@ Camera *copy_camera(Camera *cam) camn= copy_libblock(cam); camn->adt= BKE_copy_animdata(cam->adt); - -#ifndef DISABLE_PYTHON - BPY_copy_scriptlink(&camn->scriptlink); -#endif + return camn; } @@ -716,7 +727,7 @@ void *add_lamp(char *name) la->r= la->g= la->b= la->k= 1.0f; la->haint= la->energy= 1.0f; - la->dist= 20.0f; + la->dist= 25.0f; la->spotsize= 45.0f; la->spotblend= 0.15f; la->att2= 1.0f; @@ -735,7 +746,7 @@ void *add_lamp(char *name) la->ray_samp_method = LA_SAMP_HALTON; la->adapt_thresh = 0.001f; la->preview=NULL; - la->falloff_type = LA_FALLOFF_INVLINEAR; + la->falloff_type = LA_FALLOFF_INVSQUARE; la->curfalloff = curvemapping_add(1, 0.0f, 1.0f, 1.0f, 0.0f); la->sun_effect_type = 0; la->horizon_brightness = 1.0; @@ -779,9 +790,7 @@ Lamp *copy_lamp(Lamp *la) #endif // XXX old animation system if (la->preview) lan->preview = BKE_previewimg_copy(la->preview); -#ifndef DISABLE_PYTHON - BPY_copy_scriptlink(&la->scriptlink); -#endif + return lan; } @@ -839,9 +848,6 @@ void make_local_lamp(Lamp *la) void free_camera(Camera *ca) { -#ifndef DISABLE_PYTHON - BPY_free_scriptlink(&ca->scriptlink); -#endif BKE_free_animdata((ID *)ca); } @@ -850,11 +856,6 @@ void free_lamp(Lamp *la) MTex *mtex; int a; - /* scriptlinks */ -#ifndef DISABLE_PYTHON - BPY_free_scriptlink(&la->scriptlink); -#endif - for(a=0; a<MAX_MTEX; a++) { mtex= la->mtex[a]; if(mtex && mtex->tex) mtex->tex->id.us--; @@ -945,7 +946,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; @@ -1050,18 +1050,29 @@ 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->particles->boid) { + psysn->particles->boid = MEM_dupallocN(psys->particles->boid); + for(a=1, pa=psysn->particles+1; a<psysn->totpart; a++, pa++) + pa->boid = (pa-1)->boid + 1; + } + + if(psys->targets.first) + BLI_duplicatelist(&psysn->targets, &psys->targets); psysn->pathcache= NULL; psysn->childcache= NULL; @@ -1168,6 +1179,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); @@ -1181,9 +1193,7 @@ Object *copy_object(Object *ob) modifier_copyData(md, nmd); BLI_addtail(&obn->modifiers, nmd); } -#ifndef DISABLE_PYTHON - BPY_copy_scriptlink(&ob->scriptlink); -#endif + obn->prop.first = obn->prop.last = NULL; copy_properties(&obn->prop, &ob->prop); @@ -1198,18 +1208,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]); @@ -1325,7 +1329,18 @@ void make_local_object(Object *ob) expand_local_object(ob); } -/* returns true if the Object data is a from an external blend file (libdata) */ +/* + * Returns true if the Object is a from an external blend file (libdata) + */ +int object_is_libdata(Object *ob) +{ + if (!ob) return 0; + if (ob->proxy) return 0; + if (ob->id.lib) return 1; + return 0; +} + +/* Returns true if the Object data is a from an external blend file (libdata) */ int object_data_is_libdata(Object *ob) { if(!ob) return 0; @@ -1401,7 +1416,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; @@ -1410,6 +1427,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]); @@ -1575,14 +1593,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); @@ -1806,26 +1824,6 @@ void set_no_parent_ipo(int val) no_parent_ipo= val; } -static int during_script_flag=0; -void disable_where_script(short on) -{ - during_script_flag= on; -} - -int during_script(void) { - return during_script_flag; -} - -static int during_scriptlink_flag=0; -void disable_where_scriptlink(short on) -{ - during_scriptlink_flag= on; -} - -int during_scriptlink(void) { - return during_scriptlink_flag; -} - void where_is_object_time(Scene *scene, Object *ob, float ctime) { float *fp1, *fp2, slowmat[4][4] = MAT4_UNITY; @@ -1925,11 +1923,6 @@ void where_is_object_time(Scene *scene, Object *ob, float ctime) constraints_clear_evalob(cob); } -#ifndef DISABLE_PYTHON - if(ob->scriptlink.totscript && !during_script()) { - if (G.f & G_DOSCRIPTLINKS) BPY_do_pyscript((ID *)ob, SCRIPT_REDRAW); - } -#endif /* set negative scale flag in object */ Crossf(vec, ob->obmat[0], ob->obmat[1]); @@ -2301,9 +2294,6 @@ void object_handle_update(Scene *scene, Object *ob) } else where_is_object(scene, ob); -#ifndef DISABLE_PYTHON - if (G.f & G_DOSCRIPTLINKS) BPY_do_pyscript((ID *)ob, SCRIPT_OBJECTUPDATE); -#endif } if(ob->recalc & OB_RECALC_DATA) { @@ -2357,10 +2347,17 @@ void object_handle_update(Scene *scene, Object *ob) if(ob->particlesystem.first) { ParticleSystem *tpsys, *psys; DerivedMesh *dm; + ob->transflag &= ~OB_DUPLIPARTS; psys= ob->particlesystem.first; while(psys) { if(psys_check_enabled(ob, psys)) { + /* check use of dupli objects here */ + if(psys->part && psys->part->draw_as == PART_DRAW_REND && + ((psys->part->ren_as == PART_DRAW_OB && psys->part->dup_ob) + || (psys->part->ren_as == PART_DRAW_GR && psys->part->dup_group))) + ob->transflag |= OB_DUPLIPARTS; + particle_system_update(scene, ob, psys); psys= psys->next; } @@ -2385,9 +2382,6 @@ void object_handle_update(Scene *scene, Object *ob) psys_get_modifier(ob, psys)->flag &= ~eParticleSystemFlag_psys_updated; } } -#ifndef DISABLE_PYTHON - if (G.f & G_DOSCRIPTLINKS) BPY_do_pyscript((ID *)ob, SCRIPT_OBDATAUPDATE); -#endif } /* the no-group proxy case, we call update */ diff --git a/source/blender/blenkernel/intern/particle.c b/source/blender/blenkernel/intern/particle.c index 4488f8cdffd..e4f8a484061 100644 --- a/source/blender/blenkernel/intern/particle.c +++ b/source/blender/blenkernel/intern/particle.c @@ -36,6 +36,7 @@ #include "MEM_guardedalloc.h" #include "DNA_scene_types.h" +#include "DNA_boid_types.h" #include "DNA_particle_types.h" #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" @@ -58,6 +59,7 @@ #include "BKE_anim.h" +#include "BKE_boids.h" #include "BKE_global.h" #include "BKE_main.h" #include "BKE_lattice.h" @@ -84,7 +86,8 @@ static void get_cpa_texture(DerivedMesh *dm, Material *ma, int face_index, 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, ParticleKey *state, float t); + 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){ @@ -354,10 +357,13 @@ void psys_free_settings(ParticleSettings *part) MEM_freeN(part->pd); part->pd = NULL; } + if(part->pd2) { MEM_freeN(part->pd2); part->pd2 = NULL; } + + boid_free_settings(part->boids); } void free_hair(ParticleSystem *psys, int softbody) @@ -438,6 +444,9 @@ void psys_free(Object *ob, ParticleSystem * psys) psys->free_edit(psys); if(psys->particles){ + if(psys->particles->boid) + MEM_freeN(psys->particles->boid); + MEM_freeN(psys->particles); psys->particles = 0; psys->totpart = 0; @@ -478,10 +487,18 @@ void psys_free(Object *ob, ParticleSystem * psys) if(psys->pointcache) BKE_ptcache_free(psys->pointcache); + if(psys->targets.first) + BLI_freelistN(&psys->targets); + + BLI_kdtree_free(psys->tree); + 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 */ @@ -889,7 +906,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) { @@ -938,6 +955,214 @@ void psys_interpolate_particle(short type, ParticleKey keys[4], float dt, Partic +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) { + ParticleTarget *pt = psys->targets.first; + + pt=pt->next; + + while(pt && pa->time + pt->time < real_t) + pt= pt->next; + + if(pt) { + pt=pt->prev; + + if(pa->time + pt->time + pt->duration > real_t) + real_t = pa->time + pt->time; + } + else + real_t = pa->time + ((ParticleTarget*)psys->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 */ /************************************************/ @@ -1438,16 +1663,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; @@ -1734,7 +1949,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]; @@ -1747,32 +1962,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) { @@ -2007,7 +2214,7 @@ 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, cpa_1st[3], dvec[3]; + 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]; @@ -2065,8 +2272,6 @@ 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; @@ -2078,6 +2283,10 @@ void psys_thread_create_path(ParticleThread *thread, struct ChildParticle *cpa, 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; } else{ @@ -2099,6 +2308,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; @@ -2190,7 +2401,7 @@ void psys_thread_create_path(ParticleThread *thread, struct ChildParticle *cpa, } /* apply different deformations to the child path */ - do_child_modifiers(ctx->scene, ob, psys, part, &ptex, (ParticleKey *)par, par_rot, cpa, orco, (ParticleKey *)state, t); + do_child_modifiers(ctx->scene, ob, psys, part, &ptex, (ParticleKey *)par, par_rot, cpa, orco, hairmat, (ParticleKey *)state, t); /* TODO: better branching */ //if(part->flag & PART_BRANCHING && ctx->between == 0 && part->flag & PART_ANIM_BRANCHING) @@ -2247,20 +2458,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*ptex.length; - else - max_length= ptex.length; - + max_length= ptex.length; cur_length= 0.0f; } @@ -2353,36 +2556,6 @@ void psys_cache_child_paths(Scene *scene, Object *ob, ParticleSystem *psys, floa psys_threads_free(pthreads); } -static void get_pointcache_keys_for_time(ParticleSystem *psys, int index, float t, ParticleKey *key1, ParticleKey *key2) -{ - PointCache *cache = psys->pointcache; - static PTCacheMem *pm = NULL; - - if(cache->flag & PTCACHE_DISK_CACHE) { - /* TODO */ - } - else { - 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) { - pm = cache->mem_cache.first; - copy_particle_key(key2, ((ParticleKey *)pm->data) + index, 1); - copy_particle_key(key1, ((ParticleKey *)pm->data) + index, 1); - } - } - } -} /* 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. */ @@ -2395,8 +2568,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}; - HairKey *hkey[2] = {NULL, NULL}; + ParticleKey result; ParticleEdit *edit = 0; ParticleEditKey *ekey = 0; @@ -2405,11 +2577,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[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; @@ -2492,7 +2667,6 @@ 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); @@ -2504,38 +2678,27 @@ void psys_cache_paths(Scene *scene, Object *ob, ParticleSystem *psys, float cfra ekey = edit->keys[i]; /*--get the first data points--*/ - if(keyed) { - kkey[0] = pa->keys; - kkey[1] = kkey[0] + 1; - - birthtime = kkey[0]->time; - dietime = kkey[0][pa->totkey-1].time; - } - else if(baked) { - get_pointcache_keys_for_time(psys, -1, 0.0f, NULL, NULL); - - birthtime = pa->time; - dietime = pa->dietime; - } - else { - hkey[0] = pa->hair; - hkey[1] = hkey[0] + 1; + pind.keyed = keyed; + pind.cached = baked; + pind.soft = soft; + init_particle_interpolation(ob, psys, pa, &pind); - birthtime = hkey[0]->time; - dietime = hkey[0][pa->totkey-1].time; - psys_mat_hair_to_global(ob, psmd->dm, psys->part->from, pa, hairmat); - } + /* 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(birthtime, part->path_start); - dietime = MIN2(dietime, part->path_end); + birthtime = MAX2(pind.birthtime, part->path_start); + dietime = MIN2(pind.dietime, part->path_end); } else { - float tb = birthtime; - birthtime = tb + part->path_start * (dietime - tb); - dietime = tb + part->path_end * (dietime - tb); + float tb = pind.birthtime; + birthtime = tb + part->path_start * (pind.dietime - tb); + dietime = tb + part->path_end * (pind.dietime - tb); } if(birthtime >= dietime) { @@ -2545,11 +2708,10 @@ void psys_cache_paths(Scene *scene, Object *ob, ParticleSystem *psys, float cfra dietime = birthtime + pa_length * (dietime - birthtime); } - - if(soft){ - bp[0] = soft->bpoint + pa->bpi; - bp[1] = bp[0] + 1; - } + else + /* XXX brecht: don't know if this code from 2.4 is correct + * still, but makes hair appear again in particle mode */ + dietime= pind.hkey[0][pa->totkey-1].time; /*--interpolate actual path from data points--*/ for(k=0, ca=cache[i]; k<=steps; k++, ca++){ @@ -2557,90 +2719,12 @@ void psys_cache_paths(Scene *scene, Object *ob, ParticleSystem *psys, float cfra t = birthtime + time * (dietime - birthtime); - if(keyed) { - while(kkey[1]->time < t) { - kkey[1]++; - } - - kkey[0] = kkey[1] - 1; - } - else if(baked) { - get_pointcache_keys_for_time(psys, i, t, keys+1, keys+2); - } - 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(keyed) { - memcpy(keys + 1, kkey[0], sizeof(ParticleKey)); - memcpy(keys + 2, kkey[1], sizeof(ParticleKey)); - } - else if(baked) - ; /* keys already set */ - else { - hair_to_particle(keys + 1, hkey[0]); - hair_to_particle(keys + 2, hkey[1]); - } - - - if(!keyed && !baked) { - 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; + result.time = -t; - keytime = (t - keys[1].time) / dfra; - - /* convert velocity to timestep size */ - if(keyed || baked){ - VecMulf(keys[1].vel, dfra / frs_sec); - VecMulf(keys[2].vel, dfra / frs_sec); - } + do_particle_interpolation(psys, i, pa, t, frs_sec, &pind, &result); - /* 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((keyed || baked) ? -1 /* signal for cubic interpolation */ - : ((psys->part->flag & PART_HAIR_BSPLINE) ? KEY_BSPLINE : KEY_CARDINAL) - ,keys, keytime, &result, 0); - - /* the velocity needs to be converted back from cubic interpolation */ - if(keyed || baked){ - 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); } @@ -2650,14 +2734,14 @@ void psys_cache_paths(Scene *scene, Object *ob, ParticleSystem *psys, float cfra if(edit){ if(pset->brushtype==PE_BRUSH_WEIGHT){ if(k==steps) - VecLerpf(ca->col, nosel_col, sel_col, hkey[0]->weight); + VecLerpf(ca->col, nosel_col, sel_col, pind.hkey[0]->weight); else VecLerpf(ca->col, nosel_col, sel_col, - (1.0f - keytime) * hkey[0]->weight + keytime * hkey[1]->weight); + (1.0f - keytime) * pind.hkey[0]->weight + keytime * pind.hkey[1]->weight); } else{ - if((ekey + (hkey[0] - pa->hair))->flag & PEK_SELECT){ - if((ekey + (hkey[1] - pa->hair))->flag & PEK_SELECT){ + if((ekey + (pind.hkey[0] - pa->hair))->flag & PEK_SELECT){ + if((ekey + (pind.hkey[1] - pa->hair))->flag & PEK_SELECT){ VECCOPY(ca->col, sel_col); } else{ @@ -2665,7 +2749,7 @@ void psys_cache_paths(Scene *scene, Object *ob, ParticleSystem *psys, float cfra } } else{ - if((ekey + (hkey[1] - pa->hair))->flag & PEK_SELECT){ + if((ekey + (pind.hkey[1] - pa->hair))->flag & PEK_SELECT){ VecLerpf(ca->col, nosel_col, sel_col, keytime); } else{ @@ -2678,8 +2762,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); @@ -2708,12 +2793,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); @@ -2780,15 +2871,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); @@ -2959,7 +3041,12 @@ void object_add_particle_system(Scene *scene, Object *ob) psys->pointcache = BKE_ptcache_add(); BLI_addtail(&ob->particlesystem, psys); - psys->part = psys_new_settings("PSys", NULL); + psys->part = psys_new_settings("ParticleSettings", NULL); + + if(BLI_countlist(&ob->particlesystem)>1) + sprintf(psys->name, "ParticleSystem %i", BLI_countlist(&ob->particlesystem)); + else + strcpy(psys->name, "ParticleSystem"); md= modifier_new(eModifierType_ParticleSystem); sprintf(md->name, "ParticleSystem %i", BLI_countlist(&ob->particlesystem)); @@ -3008,7 +3095,7 @@ static void default_particle_settings(ParticleSettings *part) part->bb_uv_split=1; part->bb_align=PART_BB_VIEW; part->bb_split_offset=PART_BB_OFF_LINEAR; - part->flag=PART_REACT_MULTIPLE|PART_HAIR_GEOMETRY; + part->flag=PART_REACT_MULTIPLE|PART_HAIR_GEOMETRY|PART_EDISTR|PART_TRAND; part->sta= 1.0; part->end= 100.0; @@ -3017,8 +3104,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; @@ -3032,19 +3117,13 @@ 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; - part->max_vel = 10.0f; - part->average_vel = 0.3f; - part->max_tan_acc = 0.2f; - part->max_lat_acc = 1.0f; + part->normfac= 1.0f; part->reactshape=1.0f; part->mass=1.0; - part->size=1.0; + part->size=0.05; part->childsize=1.0; part->child_nbr=10; @@ -3059,17 +3138,19 @@ 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= PART_DRAW_EMITTER|PART_DRAW_MAT_COL; part->draw_line[0]=0.5; part->path_start = 0.0f; part->path_end = 1.0f; - part->banking=1.0; - part->max_bank=1.0; + part->keyed_loops = 1; + + for(i=0; i<10; i++) + part->effector_weight[i]=1.0f; - for(i=0; i<BOID_TOT_RULES; i++){ - part->boidrule[i]=(char)i; - part->boidfac[i]=0.5; - } #if 0 // XXX old animation system part->ipo = NULL; @@ -3103,6 +3184,8 @@ ParticleSettings *psys_copy_settings(ParticleSettings *part) partn= copy_libblock(part); if(partn->pd) partn->pd= MEM_dupallocN(part->pd); if(partn->pd2) partn->pd2= MEM_dupallocN(part->pd2); + + partn->boids = boid_copy_settings(part->boids); return partn; } @@ -3480,7 +3563,7 @@ float psys_get_child_size(ParticleSystem *psys, ChildParticle *cpa, float cfra, } 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=part->length*(1.0f - part->randlength*cpa->rand[0]); + ptex->length= 1.0f - part->randlength*cpa->rand[0]; ptex->clump=1.0; ptex->kink=1.0; ptex->rough1= 1.0; @@ -3489,6 +3572,8 @@ static void get_child_modifier_parameters(ParticleSettings *part, ParticleThread 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); @@ -3511,7 +3596,7 @@ static void get_child_modifier_parameters(ParticleSettings *part, ParticleThread 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, ParticleKey *state, float t) +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; @@ -3528,13 +3613,13 @@ static void do_child_modifiers(Scene *scene, Object *ob, ParticleSystem *psys, P } if(part->rough1 != 0.0 && ptex->rough1 != 0.0) - do_rough(orco, t, ptex->rough1*part->rough1, part->rough1_size, 0.0, state); + 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, t, ptex->rough2*part->rough2, part->rough2_size, part->rough2_thres, state); + 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, t, ptex->roughe*part->rough_end, part->rough_end_shape, state, par); + 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) @@ -3545,12 +3630,11 @@ void psys_get_particle_on_path(Scene *scene, Object *ob, ParticleSystem *psys, i ParticleData *pa; ChildParticle *cpa; ParticleTexture ptex; - ParticleKey *kkey[2] = {NULL, NULL}; - HairKey *hkey[2] = {NULL, NULL}; 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]; int totparent = 0; @@ -3558,7 +3642,7 @@ void psys_get_particle_on_path(Scene *scene, Object *ob, ParticleSystem *psys, i int totchild = psys->totchild; short between = 0, edit = 0; - int keyed = psys->flag & PSYS_KEYED; + 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; @@ -3580,88 +3664,14 @@ void psys_get_particle_on_path(Scene *scene, Object *ob, ParticleSystem *psys, i key_from_object(pa->stick_ob,state); return; } - - if(keyed) { - kkey[0] = pa->keys; - kkey[1] = kkey[0] + 1; - - if(state->time < 0.0f) - real_t = -state->time; - else - real_t = kkey[0]->time + t * (kkey[0][pa->totkey-1].time - kkey[0]->time); - } - else if(cached) { - get_pointcache_keys_for_time(psys, -1, 0.0f, NULL, NULL); - } - else { - hkey[0] = pa->hair; - hkey[1] = pa->hair + 1; - - if(state->time < 0.0f) - real_t = -state->time; - else - real_t = hkey[0]->time + t * (hkey[0][pa->totkey-1].time - hkey[0]->time); - } - if(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 if(cached) { - if(state->time < 0.0f) /* flag for time in frames */ - real_t = -state->time; - else - real_t = pa->time + t * (pa->dietime - pa->time); - - get_pointcache_keys_for_time(psys, p, real_t, keys+1, keys+2); - } - 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]); - } + 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(!keyed && !cached) { - if(hkey[0] != pa->hair) - hair_to_particle(keys, hkey[0] - 1); - else - hair_to_particle(keys, hkey[0]); - - 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 = (real_t - keys[1].time) / dfra; - - /* convert velocity to timestep size */ - if(keyed || cached){ - VecMulf(keys[1].vel, dfra / frs_sec); - VecMulf(keys[2].vel, dfra / frs_sec); - QuatInterpol(state->rot,keys[1].rot,keys[2].rot,keytime); - } - - psys_interpolate_particle((keyed || cached) ? -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(keyed || cached){ - VecMulf(state->vel, frs_sec / dfra); - } - else { if((pa->flag & PARS_REKEY)==0) { psys_mat_hair_to_global(ob, psmd->dm, part->from, pa, hairmat); Mat4MulVecfl(hairmat, state->co); @@ -3709,8 +3719,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; @@ -3721,11 +3729,14 @@ 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 = state->time; psys_get_particle_on_path(scene, ob, psys, cpa->parent, keys,1); @@ -3737,6 +3748,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 */ @@ -3785,7 +3798,7 @@ void psys_get_particle_on_path(Scene *scene, Object *ob, ParticleSystem *psys, i copy_particle_key(&tstate, state, 1); /* apply different deformations to the child path */ - do_child_modifiers(scene, ob, psys, part, &ptex, par, par->rot, cpa, orco, state, t); + do_child_modifiers(scene, ob, psys, part, &ptex, par, par->rot, cpa, orco, hairmat, state, t); /* try to estimate correct velocity */ if(vel){ @@ -3856,7 +3869,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; @@ -3893,57 +3906,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); - - psys_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 07e0e82a86d..b5d58e9db8c 100644 --- a/source/blender/blenkernel/intern/particle_system.c +++ b/source/blender/blenkernel/intern/particle_system.c @@ -37,6 +37,7 @@ #include "MEM_guardedalloc.h" +#include "DNA_boid_types.h" #include "DNA_particle_types.h" #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" @@ -49,6 +50,7 @@ #include "DNA_scene_types.h" #include "DNA_texture_types.h" #include "DNA_ipo_types.h" // XXX old animation system stuff... to be removed! +#include "DNA_listBase.h" #include "BLI_rand.h" #include "BLI_jitter.h" @@ -60,6 +62,7 @@ #include "BLI_threads.h" #include "BKE_anim.h" +#include "BKE_boids.h" #include "BKE_cdderivedmesh.h" #include "BKE_collision.h" #include "BKE_displist.h" @@ -111,10 +114,7 @@ static int get_current_display_percentage(ParticleSystem *psys) 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; @@ -175,6 +175,7 @@ void psys_reset(ParticleSystem *psys, int mode) static void realloc_particles(Object *ob, ParticleSystem *psys, int new_totpart) { ParticleData *newpars = 0, *pa; + BoidData *newboids = 0; int i, totpart, totsaved = 0; if(new_totpart<0) { @@ -188,22 +189,34 @@ static void realloc_particles(Object *ob, ParticleSystem *psys, int new_totpart) else totpart=new_totpart; - if(totpart) + if(totpart) { newpars= MEM_callocN(totpart*sizeof(ParticleData), "particles"); + + if(psys->part->phystype == PART_PHYS_BOIDS) + newboids = MEM_callocN(totpart*sizeof(BoidData), "Boid Data"); + } if(psys->particles) { totsaved=MIN2(psys->totpart,totpart); /*save old pars*/ - if(totsaved) + if(totsaved) { memcpy(newpars,psys->particles,totsaved*sizeof(ParticleData)); + if(newboids) + memcpy(newboids, psys->particles->boid, totsaved*sizeof(BoidData)); + } + if(psys->particles->keys) MEM_freeN(psys->particles->keys); - for(i=0, pa=psys->particles; i<psys->totpart; i++, pa++) + if(psys->particles->boid) + MEM_freeN(psys->particles->boid); + + for(i=0, pa=newpars; i<totsaved; i++, pa++) { if(pa->keys) { pa->keys= NULL; pa->totkey= 0; } + } for(i=totsaved, pa=psys->particles+totsaved; i<psys->totpart; i++, pa++) if(pa->hair) MEM_freeN(pa->hair); @@ -212,6 +225,13 @@ static void realloc_particles(Object *ob, ParticleSystem *psys, int new_totpart) } psys->particles=newpars; + if(newboids) { + pa = psys->particles; + pa->boid = newboids; + for(i=1, pa++; i<totpart; i++,pa++) + pa->boid = (pa-1)->boid + 1; + } + if(psys->child) { MEM_freeN(psys->child); psys->child=0; @@ -1610,12 +1630,15 @@ void initialize_particle(ParticleData *pa, int p, Object *ob, ParticleSystem *ps rand= BLI_frand(); /* while loops are to have a spherical distribution (avoid cubic distribution) */ - length=2.0f; - while(length>1.0){ - pa->r_ve[0]=2.0f*(BLI_frand()-0.5f); - pa->r_ve[1]=2.0f*(BLI_frand()-0.5f); - pa->r_ve[2]=2.0f*(BLI_frand()-0.5f); - length=VecLength(pa->r_ve); + if(part->phystype != PART_PHYS_BOIDS) { + /* boids store gravity in r_ve, so skip here */ + length=2.0f; + while(length>1.0){ + pa->r_ve[0]=2.0f*(BLI_frand()-0.5f); + pa->r_ve[1]=2.0f*(BLI_frand()-0.5f); + pa->r_ve[2]=2.0f*(BLI_frand()-0.5f); + length=VecLength(pa->r_ve); + } } length=2.0f; @@ -1831,122 +1854,164 @@ void reset_particle(Scene *scene, ParticleData *pa, ParticleSystem *psys, Partic QuatMul(r_rot,r_rot,rot); } } - /* conversion done so now we apply new: */ - /* -velocity from: */ - /* *reactions */ - if(dtime>0.0f){ - VECSUB(vel,pa->state.vel,pa->prev_state.vel); - } + if(part->phystype==PART_PHYS_BOIDS) { + float dvec[3], q[4], mat[3][3]; + + VECCOPY(pa->state.co,loc); - /* *emitter velocity */ - if(dtime!=0.0 && part->obfac!=0.0){ - VECSUB(vel,loc,pa->state.co); - VecMulf(vel,part->obfac/dtime); + /* boids don't get any initial velocity */ + pa->state.vel[0]=pa->state.vel[1]=pa->state.vel[2]=0.0f; + + /* boids store direction in ave */ + if(fabs(nor[2])==1.0f) { + VecSubf(pa->state.ave, loc, ob->obmat[3]); + Normalize(pa->state.ave); + } + else { + VECCOPY(pa->state.ave, nor); + } + /* and gravity in r_ve */ + pa->r_ve[0] = pa->r_ve[1] = 0.0f; + pa->r_ve[2] = -1.0f; + if(part->acc[2]!=0.0f) + pa->r_ve[2] = part->acc[2]; + + /* calculate rotation matrix */ + Projf(dvec, pa->r_ve, pa->state.ave); + VecSubf(mat[0], pa->state.ave, dvec); + Normalize(mat[0]); + VECCOPY(mat[2], pa->r_ve); + VecMulf(mat[2], -1.0f); + Normalize(mat[2]); + Crossf(mat[1], mat[2], mat[0]); + + /* apply rotation */ + Mat3ToQuat_is_ok(mat, q); + QuatCopy(pa->state.rot, q); + + pa->boid->health = part->boids->health; + pa->boid->mode = eBoidMode_InAir; + pa->boid->state_id = ((BoidState*)part->boids->states.first)->id; + pa->boid->acc[0]=pa->boid->acc[1]=pa->boid->acc[2]=0.0f; } - - /* *emitter normal */ - if(part->normfac!=0.0) - VECADDFAC(vel,vel,nor,part->normfac); - - /* *emitter tangent */ - if(part->tanfac!=0.0) - VECADDFAC(vel,vel,vtan,part->tanfac*(vg_tan?psys_particle_value_from_verts(psmd->dm,part->from,pa,vg_tan):1.0f)); + else { + /* conversion done so now we apply new: */ + /* -velocity from: */ - /* *texture */ - /* TODO */ + /* *reactions */ + if(dtime>0.0f){ + VECSUB(vel,pa->state.vel,pa->prev_state.vel); + } - /* *random */ - if(part->randfac!=0.0) - VECADDFAC(vel,vel,r_vel,part->randfac); + /* *emitter velocity */ + if(dtime!=0.0 && part->obfac!=0.0){ + VECSUB(vel,loc,pa->state.co); + VecMulf(vel,part->obfac/dtime); + } + + /* *emitter normal */ + if(part->normfac!=0.0) + VECADDFAC(vel,vel,nor,part->normfac); + + /* *emitter tangent */ + if(psmd && part->tanfac!=0.0) + VECADDFAC(vel,vel,vtan,part->tanfac*(vg_tan?psys_particle_value_from_verts(psmd->dm,part->from,pa,vg_tan):1.0f)); - /* *particle */ - if(part->partfac!=0.0) - VECADDFAC(vel,vel,p_vel,part->partfac); + /* *texture */ + /* TODO */ -#if 0 // XXX old animation system - icu=find_ipocurve(psys->part->ipo,PART_EMIT_VEL); - if(icu){ - calc_icu(icu,100*((pa->time-part->sta)/(part->end-part->sta))); - ptex.ivel*=icu->curval; - } -#endif // XXX old animation system + /* *random */ + if(part->randfac!=0.0) + VECADDFAC(vel,vel,r_vel,part->randfac); - VecMulf(vel,ptex.ivel); - - VECCOPY(pa->state.vel,vel); + /* *particle */ + if(part->partfac!=0.0) + VECADDFAC(vel,vel,p_vel,part->partfac); - /* -location from emitter */ - VECCOPY(pa->state.co,loc); + //icu=find_ipocurve(psys->part->ipo,PART_EMIT_VEL); + //if(icu){ + // calc_icu(icu,100*((pa->time-part->sta)/(part->end-part->sta))); + // ptex.ivel*=icu->curval; + //} - /* -rotation */ - pa->state.rot[0]=1.0; - pa->state.rot[1]=pa->state.rot[2]=pa->state.rot[3]=0.0; + VecMulf(vel,ptex.ivel); - if(part->rotmode){ - /* create vector into which rotation is aligned */ - switch(part->rotmode){ - case PART_ROT_NOR: - VecCopyf(rot_vec, nor); - break; - case PART_ROT_VEL: - VecCopyf(rot_vec, vel); - break; - case PART_ROT_GLOB_X: - case PART_ROT_GLOB_Y: - case PART_ROT_GLOB_Z: - rot_vec[part->rotmode - PART_ROT_GLOB_X] = 1.0f; - break; - case PART_ROT_OB_X: - case PART_ROT_OB_Y: - case PART_ROT_OB_Z: - VecCopyf(rot_vec, ob->obmat[part->rotmode - PART_ROT_OB_X]); - break; - } + //if(ELEM(part->phystype, PART_PHYS_GRADU_EX, PART_PHYS_GRADU_SIM)) + // VecAddf(vel,vel,part->acc); - /* create rotation quat */ - VecNegf(rot_vec); - vectoquat(rot_vec, OB_POSX, OB_POSZ, q2); + VECCOPY(pa->state.vel,vel); - /* randomize rotation quat */ - if(part->randrotfac!=0.0f) - QuatInterpol(rot, q2, r_rot, part->randrotfac); - else - QuatCopy(rot,q2); - - /* rotation phase */ - phasefac = part->phasefac; - if(part->randphasefac != 0.0f) /* abuse r_ave[0] as a random number */ - phasefac += part->randphasefac * pa->r_ave[0]; - VecRotToQuat(x_vec, phasefac*(float)M_PI, q_phase); + /* -location from emitter */ + VECCOPY(pa->state.co,loc); - /* combine base rotation & phase */ - QuatMul(pa->state.rot, rot, q_phase); - } + /* -rotation */ + pa->state.rot[0]=1.0; + pa->state.rot[1]=pa->state.rot[2]=pa->state.rot[3]=0.0; + + if(part->rotmode){ + /* create vector into which rotation is aligned */ + switch(part->rotmode){ + case PART_ROT_NOR: + VecCopyf(rot_vec, nor); + break; + case PART_ROT_VEL: + VecCopyf(rot_vec, vel); + break; + case PART_ROT_GLOB_X: + case PART_ROT_GLOB_Y: + case PART_ROT_GLOB_Z: + rot_vec[part->rotmode - PART_ROT_GLOB_X] = 1.0f; + break; + case PART_ROT_OB_X: + case PART_ROT_OB_Y: + case PART_ROT_OB_Z: + VecCopyf(rot_vec, ob->obmat[part->rotmode - PART_ROT_OB_X]); + break; + } + + /* create rotation quat */ + VecNegf(rot_vec); + vectoquat(rot_vec, OB_POSX, OB_POSZ, q2); - /* -angular velocity */ + /* randomize rotation quat */ + if(part->randrotfac!=0.0f) + QuatInterpol(rot, q2, r_rot, part->randrotfac); + else + QuatCopy(rot,q2); - pa->state.ave[0] = pa->state.ave[1] = pa->state.ave[2] = 0.0; + /* rotation phase */ + phasefac = part->phasefac; + if(part->randphasefac != 0.0f) /* abuse r_ave[0] as a random number */ + phasefac += part->randphasefac * pa->r_ave[0]; + VecRotToQuat(x_vec, phasefac*(float)M_PI, q_phase); - if(part->avemode){ - switch(part->avemode){ - case PART_AVE_SPIN: - VECCOPY(pa->state.ave,vel); - break; - case PART_AVE_RAND: - VECCOPY(pa->state.ave,r_ave); - break; + /* combine base rotation & phase */ + QuatMul(pa->state.rot, rot, q_phase); } - Normalize(pa->state.ave); - VecMulf(pa->state.ave,part->avefac); -#if 0 // XXX old animation system - icu=find_ipocurve(psys->part->ipo,PART_EMIT_AVE); - if(icu){ - calc_icu(icu,100*((pa->time-part->sta)/(part->end-part->sta))); - VecMulf(pa->state.ave,icu->curval); + /* -angular velocity */ + + pa->state.ave[0] = pa->state.ave[1] = pa->state.ave[2] = 0.0; + + if(part->avemode){ + switch(part->avemode){ + case PART_AVE_SPIN: + VECCOPY(pa->state.ave,vel); + break; + case PART_AVE_RAND: + VECCOPY(pa->state.ave,r_ave); + break; + } + Normalize(pa->state.ave); + VecMulf(pa->state.ave,part->avefac); + + //icu=find_ipocurve(psys->part->ipo,PART_EMIT_AVE); + //if(icu){ + // calc_icu(icu,100*((pa->time-part->sta)/(part->end-part->sta))); + // VecMulf(pa->state.ave,icu->curval); + //} } -#endif // XXX old animation system } pa->dietime = pa->time + pa->lifetime; @@ -1974,67 +2039,57 @@ static void reset_all_particles(Scene *scene, Object *ob, ParticleSystem *psys, MEM_freeN(vg_vel); } /************************************************/ -/* Keyed particles */ +/* Particle targets */ /************************************************/ -/* 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) +ParticleSystem *psys_get_target_system(Object *ob, ParticleTarget *pt) { - ParticleSystem *kpsys=psys,*tpsys; - ParticleSettings *tpart; - Object *kob=ob,*tob; - int select=ob->flag&SELECT; - short totkeyed=0; - Base *base; + ParticleSystem *psys = NULL; - 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++; - } - else{ - tob=0; - totkeyed++; - } + if(pt->ob == NULL || pt->ob == ob) + psys = BLI_findlink(&ob->particlesystem, pt->psys-1); + else + psys = BLI_findlink(&pt->ob->particlesystem, pt->psys-1); + + if(psys) + pt->flag |= PTARGET_VALID; + else + pt->flag &= ~PTARGET_VALID; + + return psys; +} +/************************************************/ +/* Keyed particles */ +/************************************************/ +/* Counts valid keyed targets */ +void psys_count_keyed_targets(Object *ob, ParticleSystem *psys) +{ + ParticleSystem *kpsys; + ParticleTarget *pt = psys->targets.first; + int keys_valid = 1; + psys->totkeyed = 0; + + for(; pt; pt=pt->next) { + kpsys = psys_get_target_system(ob, pt); + + if(kpsys && kpsys->totpart) { + psys->totkeyed += keys_valid; + if(psys->flag & PSYS_KEYED_TIMING && pt->duration != 0.0f) + psys->totkeyed += 1; + } + else { + 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; + ParticleTarget *pt; ParticleData *pa; - int totpart = psys->totpart, i, k, totkeys = psys->totkeyed + 1; - float prevtime, nexttime, keyedtime; + int totpart = psys->totpart, i, k, totkeys = psys->totkeyed; /* no proper targets so let's clear and bail out */ if(psys->totkeyed==0) { @@ -2046,7 +2101,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++){ @@ -2057,32 +2112,36 @@ static void set_keyed_keys(Scene *scene, Object *ob, ParticleSystem *psys) psys->flag &= ~PSYS_KEYED; + + pt = psys->targets.first; for(k=0; k<totkeys; k++) { + if(pt->ob) + kpsys = BLI_findlink(&pt->ob->particlesystem, pt->psys - 1); + else + kpsys = BLI_findlink(&ob->particlesystem, pt->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, pt->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 + pt->time; + if(pt->duration != 0.0f && k+1 < totkeys) { + copy_particle_key(pa->keys+k+1, pa->keys+k, 1); + (pa->keys+k+1)->time = pa->time + pt->time + pt->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 && pt->duration!=0.0f) + k++; + + pt = (pt->next && pt->next->flag & PTARGET_VALID)? pt->next : psys->targets.first; } psys->flag |= PSYS_KEYED; @@ -2198,6 +2257,60 @@ void psys_get_reactor_target(Object *ob, ParticleSystem *psys, Object **target_o /************************************************/ /* Point Cache */ /************************************************/ +void psys_make_temp_pointcache(Object *ob, ParticleSystem *psys) +{ + PointCache *cache = psys->pointcache; + PTCacheFile *pf = NULL; + PTCacheMem *pm = NULL; + PTCacheID pid; + 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((cache->flag & PTCACHE_DISK_CACHE)==0 || cache->mem_cache.first) + return; + + BKE_ptcache_id_from_particles(&pid, ob, psys); + + 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; + + 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; @@ -2247,6 +2360,7 @@ static void particle_cache_interpolate(int index, void *psys_ptr, float frs_sec, VecMulf(keys[2].vel, dfra / frs_sec); psys_interpolate_particle(-1, keys, (cfra - cfra1) / dfra, &pa->state, 1); + QuatInterpol(pa->state.rot, keys[1].rot,keys[2].rot, (cfra - cfra1) / dfra); VecMulf(pa->state.vel, frs_sec / dfra); @@ -2290,6 +2404,30 @@ static int get_particles_from_cache(Scene *scene, Object *ob, ParticleSystem *ps /************************************************/ /* Effectors */ /************************************************/ +static void update_particle_tree(ParticleSystem *psys) +{ + if(psys) { + ParticleData *pa = psys->particles; + int p, totpart = psys->totpart; + + if(!psys->tree || psys->tree_frame != psys->cfra) { + + BLI_kdtree_free(psys->tree); + + psys->tree = BLI_kdtree_new(totpart); + + for(p=0, pa=psys->particles; p<totpart; p++,pa++){ + if(pa->flag & (PARS_NO_DISP+PARS_UNEXIST) || pa->alive != PARS_ALIVE) + continue; + + BLI_kdtree_insert(psys->tree, p, pa->state.co, NULL); + } + BLI_kdtree_balance(psys->tree); + + psys->tree_frame = psys->cfra; + } + } +} static void do_texture_effector(Tex *tex, short mode, short is_2d, float nabla, short object, float *pa_co, float obmat[4][4], float force_val, float falloff, float *field) { TexResult result[4]; @@ -2491,31 +2629,29 @@ void psys_end_effectors(ParticleSystem *psys) /* NOTE: ec->ob is not valid in here anymore! - dg */ - ListBase *lb=&psys->effectors; - if(lb->first) { - ParticleEffectorCache *ec; - for(ec= lb->first; ec; ec= ec->next){ - if(ec->distances) - MEM_freeN(ec->distances); + ParticleEffectorCache *ec = psys->effectors.first; - if(ec->locations) - MEM_freeN(ec->locations); + for(; ec; ec= ec->next){ + if(ec->distances) + MEM_freeN(ec->distances); - if(ec->face_minmax) - MEM_freeN(ec->face_minmax); + if(ec->locations) + MEM_freeN(ec->locations); - if(ec->vert_cos) - MEM_freeN(ec->vert_cos); + if(ec->face_minmax) + MEM_freeN(ec->face_minmax); - if(ec->tree) - BLI_kdtree_free(ec->tree); - - if(ec->rng) - rng_free(ec->rng); - } + if(ec->vert_cos) + MEM_freeN(ec->vert_cos); - BLI_freelistN(lb); + if(ec->tree) + BLI_kdtree_free(ec->tree); + + if(ec->rng) + rng_free(ec->rng); } + + BLI_freelistN(&psys->effectors); } static void precalc_effectors(Scene *scene, Object *ob, ParticleSystem *psys, ParticleSystemModifierData *psmd, float cfra) @@ -2598,7 +2734,84 @@ static void precalc_effectors(Scene *scene, Object *ob, ParticleSystem *psys, Pa } } +int effector_find_co(Scene *scene, float *pco, SurfaceModifierData *sur, Object *ob, PartDeflect *pd, float *co, float *nor, float *vel, int *index) +{ + SurfaceModifierData *surmd = NULL; + int ret = 0; + + if(sur) + surmd = sur; + else if(pd && pd->flag&PFIELD_SURFACE) + { + surmd = (SurfaceModifierData *)modifiers_findByType ( ob, eModifierType_Surface ); + } + + if(surmd) { + /* closest point in the object surface is an effector */ + BVHTreeNearest nearest; + + nearest.index = -1; + nearest.dist = FLT_MAX; + + BLI_bvhtree_find_nearest(surmd->bvhtree->tree, pco, &nearest, surmd->bvhtree->nearest_callback, surmd->bvhtree); + + if(nearest.index != -1) { + VECCOPY(co, nearest.co); + + if(nor) { + VECCOPY(nor, nearest.no); + } + + if(vel) { + MFace *mface = CDDM_get_face(surmd->dm, nearest.index); + + VECCOPY(vel, surmd->v[mface->v1].co); + VecAddf(vel, vel, surmd->v[mface->v2].co); + VecAddf(vel, vel, surmd->v[mface->v3].co); + if(mface->v4) + VecAddf(vel, vel, surmd->v[mface->v4].co); + + VecMulf(vel, mface->v4 ? 0.25f : 0.333f); + } + + if(index) + *index = nearest.index; + ret = 1; + } + else { + co[0] = co[1] = co[2] = 0.0f; + + if(nor) + nor[0] = nor[1] = nor[2] = 0.0f; + + if(vel) + vel[0] = vel[1] = vel[2] = 0.0f; + } + } + else { + /* use center of object for distance calculus */ + VECCOPY(co, ob->obmat[3]); + + if(nor) { + VECCOPY(nor, ob->obmat[2]); + } + + if(vel) { + Object obcopy = *ob; + + VECCOPY(vel, ob->obmat[3]); + + where_is_object_time(scene, ob, scene->r.cfra - 1.0); + + VecSubf(vel, vel, ob->obmat[3]); + + *ob = obcopy; + } + } + + return ret; +} /* calculate forces that all effectors apply to a particle*/ void do_effectors(int pa_no, ParticleData *pa, ParticleKey *state, Scene *scene, Object *ob, ParticleSystem *psys, float *rootco, float *force_field, float *vel,float framestep, float cfra) { @@ -2608,12 +2821,11 @@ void do_effectors(int pa_no, ParticleData *pa, ParticleKey *state, Scene *scene, ParticleData *epa; ParticleKey estate; PartDeflect *pd; - SurfaceModifierData *surmd = NULL; ListBase *lb=&psys->effectors; ParticleEffectorCache *ec; - float distance, vec_to_part[3]; - float falloff, charge = 0.0f; - int p; + float distance, vec_to_part[3], pco[3], co[3]; + float falloff, charge = 0.0f, strength; + int p, face_index=-1; /* check all effector objects for interaction */ if(lb->first){ @@ -2637,45 +2849,33 @@ void do_effectors(int pa_no, ParticleData *pa, ParticleKey *state, Scene *scene, where_is_object_time(scene, eob,cfra); if(pd && pd->flag&PFIELD_SURFACE) { - surmd = (SurfaceModifierData *)modifiers_findByType ( eob, eModifierType_Surface ); - } - if(surmd) { - /* closest point in the object surface is an effector */ - BVHTreeNearest nearest; float velocity[3]; - - nearest.index = -1; - nearest.dist = FLT_MAX; - /* using velocity corrected location allows for easier sliding over effector surface */ VecCopyf(velocity, state->vel); VecMulf(velocity, psys_get_timestep(psys->part)); - VecAddf(vec_to_part, state->co, velocity); - - BLI_bvhtree_find_nearest(surmd->bvhtree->tree, vec_to_part, &nearest, surmd->bvhtree->nearest_callback, surmd->bvhtree); - - if(nearest.index != -1) { - VecSubf(vec_to_part, state->co, nearest.co); - } - else - vec_to_part[0] = vec_to_part[1] = vec_to_part[2] = 0.0f; + VecAddf(pco, state->co, velocity); } else - /* use center of object for distance calculus */ - VecSubf(vec_to_part, state->co, eob->obmat[3]); + VECCOPY(pco, state->co); + + effector_find_co(scene, pco, NULL, eob, pd, co, NULL, NULL, &face_index); + + VecSubf(vec_to_part, state->co, co); distance = VecLength(vec_to_part); falloff=effector_falloff(pd,eob->obmat[2],vec_to_part); + strength = pd->f_strength * psys->part->effector_weight[0] * psys->part->effector_weight[pd->forcefield]; + if(falloff<=0.0f) ; /* don't do anything */ else if(pd->forcefield==PFIELD_TEXTURE) { do_texture_effector(pd->tex, pd->tex_mode, pd->flag&PFIELD_TEX_2D, pd->tex_nabla, pd->flag & PFIELD_TEX_OBJECT, (pd->flag & PFIELD_TEX_ROOTCO) ? rootco : state->co, eob->obmat, - pd->f_strength, falloff, force_field); + strength, falloff, force_field); } else { - do_physical_effector(scene, eob, state->co, pd->forcefield,pd->f_strength,distance, + do_physical_effector(scene, eob, state->co, pd->forcefield,strength,distance, falloff,0.0,pd->f_damp,eob->obmat[2],vec_to_part, state->vel,force_field,pd->flag&PFIELD_PLANAR,ec->rng,pd->f_noise,charge,pa->size); } @@ -2716,10 +2916,12 @@ void do_effectors(int pa_no, ParticleData *pa, ParticleKey *state, Scene *scene, falloff=effector_falloff(pd,estate.vel,vec_to_part); + strength = pd->f_strength * psys->part->effector_weight[0] * psys->part->effector_weight[pd->forcefield]; + if(falloff<=0.0f) ; /* don't do anything */ else - do_physical_effector(scene, eob, state->co, pd->forcefield,pd->f_strength,distance, + do_physical_effector(scene, eob, state->co, pd->forcefield,strength,distance, falloff,epart->size,pd->f_damp,estate.vel,vec_to_part, state->vel,force_field,0, ec->rng, pd->f_noise,charge,pa->size); } @@ -3071,20 +3273,7 @@ int psys_intersect_dm(Scene *scene, Object *ob, DerivedMesh *dm, float *vert_cos return intersect; } -/* container for moving data between deflet_particle and particle_intersect_face */ -typedef struct ParticleCollision -{ - struct Object *ob, *ob_t; // collided and current objects - struct CollisionModifierData *md; // collision modifier for ob_t; - float nor[3]; // normal at collision point - float vel[3]; // velocity of collision point - float co1[3], co2[3]; // ray start and end points - float ray_len; // original length of co2-co1, needed for collision time evaluation - float t; // time of previous collision, needed for substracting face velocity -} -ParticleCollision; - -static void particle_intersect_face(void *userdata, int index, const BVHTreeRay *ray, BVHTreeRayHit *hit) +void particle_intersect_face(void *userdata, int index, const BVHTreeRay *ray, BVHTreeRayHit *hit) { ParticleCollision *col = (ParticleCollision *) userdata; MFace *face = col->md->mfaces + index; @@ -3159,20 +3348,27 @@ static void particle_intersect_face(void *userdata, int index, const BVHTreeRay /* 1. check for all possible deflectors for closest intersection on particle path */ /* 2. if deflection was found kill the particle or calculate new coordinates */ static void deflect_particle(Scene *scene, Object *pob, ParticleSystemModifierData *psmd, ParticleSystem *psys, ParticleSettings *part, ParticleData *pa, int p, float timestep, float dfra, float cfra){ - Object *ob = NULL; + Object *ob = NULL, *skip_ob = NULL; ListBase *lb=&psys->effectors; ParticleEffectorCache *ec; ParticleKey reaction_state; ParticleCollision col; BVHTreeRayHit hit; float ray_dir[3], zerovec[3]={0.0,0.0,0.0}; - float radius = ((part->flag & PART_SIZE_DEFL)?pa->size:0.0f); + float radius = ((part->flag & PART_SIZE_DEFL)?pa->size:0.0f), boid_z; int deflections=0, max_deflections=10; VECCOPY(col.co1, pa->prev_state.co); VECCOPY(col.co2, pa->state.co); col.t = 0.0f; + /* override for boids */ + if(part->phystype == PART_PHYS_BOIDS) { + radius = pa->size; + boid_z = pa->state.co[2]; + skip_ob = pa->stick_ob; + } + /* 10 iterations to catch multiple deflections */ if(lb->first) while(deflections < max_deflections){ /* 1. */ @@ -3190,13 +3386,17 @@ static void deflect_particle(Scene *scene, Object *pob, ParticleSystemModifierDa if(ec->type & PSYS_EC_DEFLECT){ ob= ec->ob; - if(part->type!=PART_HAIR) - where_is_object_time(scene, ob,cfra); + /* for boids: don't check with current ground object */ + if(ob==skip_ob) + continue; /* particles should not collide with emitter at birth */ if(ob==pob && pa->time < cfra && pa->time >= psys->cfra) continue; + if(part->type!=PART_HAIR) + where_is_object_time(scene,ob,cfra); + col.md = ( CollisionModifierData * ) ( modifiers_findByType ( ec->ob, eModifierType_Collision ) ); col.ob_t = ob; @@ -3328,6 +3528,13 @@ static void deflect_particle(Scene *scene, Object *pob, ParticleSystemModifierDa /* make sure we don't hit the current face again */ VECADDFAC(co, co, col.nor, (through ? -0.0001f : 0.0001f)); + if(part->phystype == PART_PHYS_BOIDS && part->boids->options & BOID_ALLOW_LAND) { + if(pa->boid->mode == eBoidMode_OnLand || co[2] <= boid_z) { + co[2] = boid_z; + vel[2] = 0.0f; + } + } + /* store state for reactors */ VECCOPY(reaction_state.co, co); VecLerpf(reaction_state.vel, pa->prev_state.vel, pa->state.vel, dt); @@ -3361,619 +3568,6 @@ static void deflect_particle(Scene *scene, Object *pob, ParticleSystemModifierDa } } /************************************************/ -/* Boid physics */ -/************************************************/ -static int boid_see_mesh(ListBase *lb, Scene *scene, Object *pob, ParticleSystem *psys, float *vec1, float *vec2, float *loc, float *nor, float cfra) -{ - Object *ob, *min_ob; - DerivedMesh *dm; - MFace *mface; - MVert *mvert; - ParticleEffectorCache *ec; - ParticleSystemModifierData *psmd=psys_get_modifier(pob,psys); - float imat[4][4]; - float co1[3], co2[3], min_w[4], min_d; - int min_face=0, intersect=0; - - if(lb->first){ - intersect=0; - min_d=20000.0; - min_ob=NULL; - for(ec=lb->first; ec; ec=ec->next){ - if(ec->type & PSYS_EC_DEFLECT){ - ob= ec->ob; - - if(psys->part->type!=PART_HAIR) - where_is_object_time(scene, ob,cfra); - - if(ob==pob) - dm=psmd->dm; - else - dm=0; - - VECCOPY(co1,vec1); - VECCOPY(co2,vec2); - - if(ec->vert_cos==0){ - /* convert particle coordinates to object coordinates */ - Mat4Invert(imat,ob->obmat); - - Mat4MulVecfl(imat,co1); - Mat4MulVecfl(imat,co2); - } - - if(psys_intersect_dm(scene,ob,dm,ec->vert_cos,co1,co2,&min_d,&min_face,min_w,ec->face_minmax,0,0,0)) - min_ob=ob; - } - } - if(min_ob){ - ob=min_ob; - - if(ob==pob){ - dm=psmd->dm; - } - else{ - psys_disable_all(ob); - - dm=mesh_get_derived_final(scene, ob, 0); - if(dm==0) - dm=mesh_get_derived_deform(scene, ob, 0); - - psys_enable_all(ob); - } - - mface=dm->getFaceDataArray(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); - - VECADD(nor,nor,loc); - Mat4MulVecfl(ob->obmat,loc); - Mat4MulVecfl(ob->obmat,nor); - VECSUB(nor,nor,loc); - return 1; - } - } - return 0; -} -/* vector calculus functions in 2d vs. 3d */ -static void set_boid_vec_func(BoidVecFunc *bvf, int is_2d) -{ - if(is_2d){ - bvf->Addf = Vec2Addf; - bvf->Subf = Vec2Subf; - bvf->Mulf = Vec2Mulf; - bvf->Length = Vec2Length; - bvf->Normalize = Normalize2; - bvf->Inpf = Inp2f; - bvf->Copyf = Vec2Copyf; - } - else{ - bvf->Addf = VecAddf; - bvf->Subf = VecSubf; - bvf->Mulf = VecMulf; - bvf->Length = VecLength; - bvf->Normalize = Normalize; - bvf->Inpf = Inpf; - bvf->Copyf = VecCopyf; - } -} -/* boids have limited processing capability so once there's too much information (acceleration) no more is processed */ -static int add_boid_acc(BoidVecFunc *bvf, float lat_max, float tan_max, float *lat_accu, float *tan_accu, float *acc, float *dvec, float *vel) -{ - static float tangent[3]; - static float tan_length; - - if(vel){ - bvf->Copyf(tangent,vel); - tan_length=bvf->Normalize(tangent); - return 1; - } - else{ - float cur_tan, cur_lat; - float tan_acc[3], lat_acc[3]; - int ret=0; - - bvf->Copyf(tan_acc,tangent); - - if(tan_length>0.0){ - bvf->Mulf(tan_acc,Inpf(tangent,dvec)); - - bvf->Subf(lat_acc,dvec,tan_acc); - } - else{ - bvf->Copyf(tan_acc,dvec); - lat_acc[0]=lat_acc[1]=lat_acc[2]=0.0f; - *lat_accu=lat_max; - } - - cur_tan=bvf->Length(tan_acc); - cur_lat=bvf->Length(lat_acc); - - /* add tangential acceleration */ - if(*lat_accu+cur_lat<=lat_max){ - bvf->Addf(acc,acc,lat_acc); - *lat_accu+=cur_lat; - ret=1; - } - else{ - bvf->Mulf(lat_acc,(lat_max-*lat_accu)/cur_lat); - bvf->Addf(acc,acc,lat_acc); - *lat_accu=lat_max; - } - - /* add lateral acceleration */ - if(*tan_accu+cur_tan<=tan_max){ - bvf->Addf(acc,acc,tan_acc); - *tan_accu+=cur_tan; - ret=1; - } - else{ - bvf->Mulf(tan_acc,(tan_max-*tan_accu)/cur_tan); - bvf->Addf(acc,acc,tan_acc); - *tan_accu=tan_max; - } - - return ret; - } -} -/* determines the acceleration that the boid tries to acchieve */ -static void boid_brain(BoidVecFunc *bvf, ParticleData *pa, Scene *scene, Object *ob, ParticleSystem *psys, ParticleSettings *part, KDTree *tree, float timestep, float cfra, float *acc) -{ - ParticleData *pars=psys->particles; - KDTreeNearest ptn[MAX_BOIDNEIGHBOURS+1]; - ParticleEffectorCache *ec=0; - float dvec[3]={0.0,0.0,0.0}, ob_co[3], ob_nor[3]; - float avoid[3]={0.0,0.0,0.0}, velocity[3]={0.0,0.0,0.0}, center[3]={0.0,0.0,0.0}; - float cubedist[MAX_BOIDNEIGHBOURS+1]; - int i, n, neighbours=0, near, not_finished=1; - - float cur_vel; - float lat_accu=0.0f, max_lat_acc=part->max_vel*part->max_lat_acc; - float tan_accu=0.0f, max_tan_acc=part->max_vel*part->max_tan_acc; - float avg_vel=part->average_vel*part->max_vel; - - acc[0]=acc[1]=acc[2]=0.0f; - /* the +1 neighbour is because boid itself is in the tree */ - neighbours=BLI_kdtree_find_n_nearest(tree,part->boidneighbours+1,pa->state.co,NULL,ptn); - - for(n=1; n<neighbours; n++){ - cubedist[n]=(float)pow((double)(ptn[n].dist/pa->size),3.0); - cubedist[n]=1.0f/MAX2(cubedist[n],1.0f); - } - - /* initialize tangent */ - add_boid_acc(bvf,0.0,0.0,0,0,0,0,pa->state.vel); - - for(i=0; i<BOID_TOT_RULES && not_finished; i++){ - switch(part->boidrule[i]){ - case BOID_COLLIDE: - /* collision avoidance */ - bvf->Copyf(dvec,pa->prev_state.vel); - bvf->Mulf(dvec,5.0f); - bvf->Addf(dvec,dvec,pa->prev_state.co); - if(boid_see_mesh(&psys->effectors,scene, ob,psys,pa->prev_state.co,dvec,ob_co,ob_nor,cfra)){ - float probelen = bvf->Length(dvec); - float proj; - float oblen; - - Normalize(ob_nor); - proj = bvf->Inpf(ob_nor,pa->prev_state.vel); - - bvf->Subf(dvec,pa->prev_state.co,ob_co); - oblen=bvf->Length(dvec); - - bvf->Copyf(dvec,ob_nor); - bvf->Mulf(dvec,-proj); - bvf->Mulf(dvec,((probelen/oblen)-1.0f)*100.0f*part->boidfac[BOID_COLLIDE]); - - not_finished=add_boid_acc(bvf,max_lat_acc,max_tan_acc,&lat_accu,&tan_accu,acc,dvec,0); - } - break; - case BOID_AVOID: - /* predator avoidance */ - if(psys->effectors.first){ - for(ec=psys->effectors.first; ec; ec=ec->next){ - if(ec->type & PSYS_EC_EFFECTOR){ - Object *eob = ec->ob; - PartDeflect *pd = eob->pd; - - if(pd->forcefield==PFIELD_FORCE && pd->f_strength<0.0){ - float distance; - VECSUB(dvec,eob->obmat[3],pa->prev_state.co); - - distance=Normalize(dvec); - - if(part->flag & PART_DIE_ON_COL && distance < pd->mindist){ - pa->alive = PARS_DYING; - pa->dietime=cfra; - i=BOID_TOT_RULES; - break; - } - - if(pd->flag&PFIELD_USEMAX && distance > pd->maxdist) - ; - else{ - bvf->Mulf(dvec,part->boidfac[BOID_AVOID]*pd->f_strength/(float)pow((double)distance,(double)pd->f_power)); - - not_finished=add_boid_acc(bvf,max_lat_acc,max_tan_acc,&lat_accu,&tan_accu,acc,dvec,0); - } - } - } - else if(ec->type & PSYS_EC_PARTICLE){ - Object *eob = ec->ob; - ParticleSystem *epsys; - ParticleSettings *epart; - ParticleKey state; - PartDeflect *pd; - KDTreeNearest ptn2[MAX_BOIDNEIGHBOURS]; - int totepart, p, count; - float distance; - epsys= BLI_findlink(&eob->particlesystem,ec->psys_nbr); - epart= epsys->part; - pd= epart->pd; - totepart= epsys->totpart; - - if(pd->forcefield==PFIELD_FORCE && pd->f_strength<0.0 && ec->tree){ - count=BLI_kdtree_find_n_nearest(ec->tree,epart->boidneighbours,pa->prev_state.co,NULL,ptn2); - for(p=0; p<count; p++){ - state.time=-1.0; - if(psys_get_particle_state(scene, eob,epsys,ptn2[p].index,&state,0)){ - VECSUB(dvec, state.co, pa->prev_state.co); - - distance = Normalize(dvec); - - if(part->flag & PART_DIE_ON_COL && distance < (epsys->particles+ptn2[p].index)->size){ - pa->alive = PARS_DYING; - pa->dietime=cfra; - i=BOID_TOT_RULES; - break; - } - - if(pd->flag&PFIELD_USEMAX && distance > pd->maxdist) - ; - else{ - bvf->Mulf(dvec,part->boidfac[BOID_AVOID]*pd->f_strength/(float)pow((double)distance,(double)pd->f_power)); - - not_finished=add_boid_acc(bvf,max_lat_acc,max_tan_acc,&lat_accu,&tan_accu,acc,dvec,0); - } - } - } - } - } - } - } - break; - case BOID_CROWD: - /* crowd avoidance */ - near=0; - for(n=1; n<neighbours; n++){ - if(ptn[n].dist<2.0f*pa->size){ - if(ptn[n].dist!=0.0f) { - bvf->Subf(dvec,pa->prev_state.co,pars[ptn[n].index].state.co); - bvf->Mulf(dvec,(2.0f*pa->size-ptn[n].dist)/ptn[n].dist); - bvf->Addf(avoid,avoid,dvec); - near++; - } - } - /* ptn[] is distance ordered so no need to check others */ - else break; - } - if(near){ - bvf->Mulf(avoid,part->boidfac[BOID_CROWD]*2.0f/timestep); - - not_finished=add_boid_acc(bvf,max_lat_acc,max_tan_acc,&lat_accu,&tan_accu,acc,avoid,0); - } - break; - case BOID_CENTER: - /* flock centering */ - if(neighbours>1){ - for(n=1; n<neighbours; n++){ - bvf->Addf(center,center,pars[ptn[n].index].state.co); - } - bvf->Mulf(center,1.0f/((float)neighbours-1.0f)); - - bvf->Subf(dvec,center,pa->prev_state.co); - - bvf->Mulf(dvec,part->boidfac[BOID_CENTER]*2.0f); - - not_finished=add_boid_acc(bvf,max_lat_acc,max_tan_acc,&lat_accu,&tan_accu,acc,dvec,0); - } - break; - case BOID_AV_VEL: - /* average velocity */ - cur_vel=bvf->Length(pa->prev_state.vel); - if(cur_vel>0.0){ - bvf->Copyf(dvec,pa->prev_state.vel); - bvf->Mulf(dvec,part->boidfac[BOID_AV_VEL]*(avg_vel-cur_vel)/cur_vel); - not_finished=add_boid_acc(bvf,max_lat_acc,max_tan_acc,&lat_accu,&tan_accu,acc,dvec,0); - } - break; - case BOID_VEL_MATCH: - /* velocity matching */ - if(neighbours>1){ - for(n=1; n<neighbours; n++){ - bvf->Copyf(dvec,pars[ptn[n].index].state.vel); - bvf->Mulf(dvec,cubedist[n]); - bvf->Addf(velocity,velocity,dvec); - } - bvf->Mulf(velocity,1.0f/((float)neighbours-1.0f)); - - bvf->Subf(dvec,velocity,pa->prev_state.vel); - - bvf->Mulf(dvec,part->boidfac[BOID_VEL_MATCH]); - - not_finished=add_boid_acc(bvf,max_lat_acc,max_tan_acc,&lat_accu,&tan_accu,acc,dvec,0); - } - break; - case BOID_GOAL: - /* goal seeking */ - if(psys->effectors.first){ - for(ec=psys->effectors.first; ec; ec=ec->next){ - if(ec->type & PSYS_EC_EFFECTOR){ - Object *eob = ec->ob; - PartDeflect *pd = eob->pd; - float temp[4]; - - if(pd->forcefield==PFIELD_FORCE && pd->f_strength>0.0){ - float distance; - VECSUB(dvec,eob->obmat[3],pa->prev_state.co); - - distance=Normalize(dvec); - - if(pd->flag&PFIELD_USEMAX && distance > pd->maxdist) - ; - else{ - VecMulf(dvec,pd->f_strength*part->boidfac[BOID_GOAL]/(float)pow((double)distance,(double)pd->f_power)); - - not_finished=add_boid_acc(bvf,max_lat_acc,max_tan_acc,&lat_accu,&tan_accu,acc,dvec,0); - } - } - else if(pd->forcefield==PFIELD_GUIDE){ - float distance; - - where_on_path(eob, (cfra-pa->time)/pa->lifetime, temp, dvec); - - VECSUB(dvec,temp,pa->prev_state.co); - - distance=Normalize(dvec); - - if(pd->flag&PFIELD_USEMAX && distance > pd->maxdist) - ; - else{ - VecMulf(dvec,pd->f_strength*part->boidfac[BOID_GOAL]/(float)pow((double)distance,(double)pd->f_power)); - - not_finished=add_boid_acc(bvf,max_lat_acc,max_tan_acc,&lat_accu,&tan_accu,acc,dvec,0); - } - } - } - else if(ec->type & PSYS_EC_PARTICLE){ - Object *eob = ec->ob; - ParticleSystem *epsys; - ParticleSettings *epart; - ParticleKey state; - PartDeflect *pd; - KDTreeNearest ptn2[MAX_BOIDNEIGHBOURS]; - int totepart, p, count; - float distance; - epsys= BLI_findlink(&eob->particlesystem,ec->psys_nbr); - epart= epsys->part; - pd= epart->pd; - totepart= epsys->totpart; - - if(pd->forcefield==PFIELD_FORCE && pd->f_strength>0.0 && ec->tree){ - count=BLI_kdtree_find_n_nearest(ec->tree,epart->boidneighbours,pa->prev_state.co,NULL,ptn2); - for(p=0; p<count; p++){ - state.time=-1.0; - if(psys_get_particle_state(scene, eob,epsys,ptn2[p].index,&state,0)){ - VECSUB(dvec, state.co, pa->prev_state.co); - - distance = Normalize(dvec); - - if(pd->flag&PFIELD_USEMAX && distance > pd->maxdist) - ; - else{ - bvf->Mulf(dvec,part->boidfac[BOID_AVOID]*pd->f_strength/(float)pow((double)distance,(double)pd->f_power)); - - not_finished=add_boid_acc(bvf,max_lat_acc,max_tan_acc,&lat_accu,&tan_accu,acc,dvec,0); - } - } - } - } - } - } - } - break; - case BOID_LEVEL: - /* level flight */ - if((part->flag & PART_BOIDS_2D)==0){ - dvec[0]=dvec[1]=0.0; - dvec[2]=-pa->prev_state.vel[2]; - - VecMulf(dvec,part->boidfac[BOID_LEVEL]); - not_finished=add_boid_acc(bvf,max_lat_acc,max_tan_acc,&lat_accu,&tan_accu,acc,dvec,0); - } - break; - } - } -} -/* tries to realize the wanted acceleration */ -static void boid_body(Scene *scene, BoidVecFunc *bvf, ParticleData *pa, ParticleSystem *psys, ParticleSettings *part, float timestep, float *acc) -{ - float dvec[3], bvec[3], length, max_vel=part->max_vel; - float q2[4], q[4]; - float g=9.81f, pa_mass=part->mass; - float yvec[3]={0.0,1.0,0.0}, zvec[3]={0.0,0.0,-1.0}, bank; - - /* apply new velocity, location & rotation */ - copy_particle_key(&pa->state,&pa->prev_state,0); - - if(part->flag & PART_SIZEMASS) - pa_mass*=pa->size; - - /* by regarding the acceleration as a force at this stage we*/ - /* can get better controll allthough it's a bit unphysical */ - bvf->Mulf(acc,1.0f/pa_mass); - - bvf->Copyf(dvec,acc); - bvf->Mulf(dvec,timestep*timestep*0.5f); - - bvf->Copyf(bvec,pa->state.vel); - bvf->Mulf(bvec,timestep); - bvf->Addf(dvec,dvec,bvec); - bvf->Addf(pa->state.co,pa->state.co,dvec); - - /* air speed from wind and vortex effectors */ - if(psys->effectors.first) { - ParticleEffectorCache *ec; - for(ec=psys->effectors.first; ec; ec=ec->next) { - if(ec->type & PSYS_EC_EFFECTOR) { - Object *eob = ec->ob; - PartDeflect *pd = eob->pd; - float direction[3], vec_to_part[3]; - float falloff; - - if(pd->f_strength != 0.0f) { - VecCopyf(direction, eob->obmat[2]); - VecSubf(vec_to_part, pa->state.co, eob->obmat[3]); - - falloff=effector_falloff(pd, direction, vec_to_part); - - switch(pd->forcefield) { - case PFIELD_WIND: - if(falloff <= 0.0f) - ; /* don't do anything */ - else { - Normalize(direction); - VecMulf(direction, pd->f_strength * falloff); - bvf->Addf(pa->state.co, pa->state.co, direction); - } - break; - case PFIELD_VORTEX: - { - float distance, mag_vec[3]; - Crossf(mag_vec, direction, vec_to_part); - Normalize(mag_vec); - - distance = VecLength(vec_to_part); - - VecMulf(mag_vec, pd->f_strength * distance * falloff); - bvf->Addf(pa->state.co, pa->state.co, mag_vec); - break; - } - } - } - } - } - } - - - if((part->flag & PART_BOIDS_2D)==0 && pa->prev_state.vel[0]!=0.0 && pa->prev_state.vel[0]!=0.0 && pa->prev_state.vel[0]!=0.0){ - Crossf(yvec,pa->state.vel,zvec); - - Normalize(yvec); - - bank=Inpf(yvec,acc); - - bank=-(float)atan((double)(bank/g)); - - bank*=part->banking; - - bank-=pa->bank; - if(bank>M_PI*part->max_bank){ - bank=pa->bank+(float)M_PI*part->max_bank; - } - else if(bank<-M_PI*part->max_bank){ - bank=pa->bank-(float)M_PI*part->max_bank; - } - else - bank+=pa->bank; - - pa->bank=bank; - } - else{ - bank=0.0; - } - - - VecRotToQuat(pa->state.vel,bank,q); - - VECCOPY(dvec,pa->state.vel); - VecNegf(dvec); - vectoquat(dvec, OB_POSX, OB_POSZ, q2); - - QuatMul(pa->state.rot,q,q2); - - bvf->Mulf(acc,timestep); - bvf->Addf(pa->state.vel,pa->state.vel,acc); - - 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->getFaceDataArray(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); - if(length > max_vel) - bvf->Mulf(pa->state.vel,max_vel/length); -} -/************************************************/ /* Hair */ /************************************************/ static void save_hair(Scene *scene, Object *ob, ParticleSystem *psys, ParticleSystemModifierData *psmd, float cfra){ @@ -4030,9 +3624,9 @@ static void dynamics_step(Scene *scene, Object *ob, ParticleSystem *psys, Partic ParticleData *pa; ParticleSettings *part=psys->part; KDTree *tree=0; - BoidVecFunc bvf; IpoCurve *icu_esize= NULL; //=find_ipocurve(part->ipo,PART_EMIT_SIZE); // XXX old animation system Material *ma=give_current_material(ob,part->omat); + BoidBrainData bbd; float timestep; int p, totpart; /* current time */ @@ -4112,16 +3706,23 @@ static void dynamics_step(Scene *scene, Object *ob, ParticleSystem *psys, Partic precalc_effectors(scene, ob,psys,psmd,cfra); if(part->phystype==PART_PHYS_BOIDS){ - /* create particle tree for fast inter-particle comparisons */ - tree=BLI_kdtree_new(totpart); - for(p=0, pa=psys->particles; p<totpart; p++,pa++){ - if(pa->flag & (PARS_NO_DISP+PARS_UNEXIST) || pa->alive!=PARS_ALIVE) - continue; - - BLI_kdtree_insert(tree, p, pa->state.co, NULL); + ParticleTarget *pt = psys->targets.first; + bbd.scene = scene; + bbd.ob = ob; + bbd.psys = psys; + bbd.part = part; + bbd.cfra = cfra; + bbd.dfra = dfra; + bbd.timestep = timestep; + + update_particle_tree(psys); + + boids_precalc_rules(part, cfra); + + for(; pt; pt=pt->next) { + if(pt->ob) + update_particle_tree(BLI_findlink(&pt->ob->particlesystem, pt->psys-1)); } - BLI_kdtree_balance(tree); - set_boid_vec_func(&bvf,part->flag&PART_BOIDS_2D); } /* main loop: calculate physics for all particles */ @@ -4190,10 +3791,14 @@ static void dynamics_step(Scene *scene, Object *ob, ParticleSystem *psys, Partic break; case PART_PHYS_BOIDS: { - float acc[3]; - boid_brain(&bvf, pa, scene, ob, psys, part, tree, timestep,cfra,acc); - if(pa->alive != PARS_DYING) - boid_body(scene, &bvf,pa,psys,part,timestep,acc); + bbd.goal_ob = NULL; + boid_brain(&bbd, p, pa); + if(pa->alive != PARS_DYING) { + boid_body(&bbd, pa); + + /* deflection */ + deflect_particle(scene,ob,psmd,psys,part,pa,p,timestep,pa_dfra,cfra); + } break; } } @@ -4238,7 +3843,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){ @@ -4373,9 +3978,10 @@ 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) { - pa->alive = PARS_UNBORN; - reset_particle(scene, pa, psys, psmd, ob, 0.0f, cfra, NULL, NULL, NULL); + if(pa->time >= cfra) { + pa->alive = pa->time==cfra ? PARS_ALIVE : PARS_UNBORN; + if((psys->pointcache->flag & PTCACHE_EXTERNAL) == 0) + reset_particle(scene, pa, psys, psmd, ob, 0.0f, cfra, NULL, NULL, NULL); } else if(dietime <= cfra){ if(dietime > psys->cfra){ @@ -4416,12 +4022,15 @@ static void cached_step(Scene *scene, Object *ob, ParticleSystemModifierData *ps 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) @@ -4430,7 +4039,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) { @@ -4442,6 +4051,8 @@ void psys_changed_type(ParticleSystem *psys) 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 { free_hair(psys, 1); @@ -4454,7 +4065,60 @@ void psys_changed_type(ParticleSystem *psys) psys_reset(psys, PSYS_RESET_ALL); } +void psys_check_boid_data(ParticleSystem *psys) +{ + ParticleData *pa = psys->particles; + int p = 1; + + if(!pa) + return; + + if(psys->part && psys->part->phystype==PART_PHYS_BOIDS) { + if(!pa->boid) { + pa->boid = MEM_callocN(psys->totpart * sizeof(BoidData), "Boid Data"); + + for(pa++; p<psys->totpart; p++, pa++) + pa->boid = (pa-1)->boid + 1; + } + } + else if(pa->boid){ + MEM_freeN(pa->boid); + for(; p<psys->totpart; p++, pa++) + pa->boid = NULL; + } +} +static void psys_changed_physics(Object *ob, ParticleSystem *psys) +{ + ParticleSettings *part = psys->part; + if(ELEM(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; + } + + if(part->phystype == PART_PHYS_BOIDS && part->boids == NULL) { + BoidState *state; + + psys_check_boid_data(psys); + + part->boids = MEM_callocN(sizeof(BoidSettings), "Boid Settings"); + boid_default_settings(part->boids); + + state = boid_new_state(part->boids); + BLI_addtail(&state->rules, boid_new_rule(eBoidRuleType_Separate)); + BLI_addtail(&state->rules, boid_new_rule(eBoidRuleType_Flock)); + + ((BoidRule*)state->rules.first)->flag |= BOIDRULE_CURRENT; + + state->flag |= BOIDSTATE_CURRENT; + BLI_addtail(&part->boids->states, state); + } +} static void particles_fluid_step(Scene *scene, Object *ob, ParticleSystem *psys, int cfra) { if(psys->particles){ @@ -4588,6 +4252,8 @@ static void system_step(Scene *scene, Object *ob, ParticleSystem *psys, Particle 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) { @@ -4636,7 +4302,9 @@ static void system_step(Scene *scene, Object *ob, ParticleSystem *psys, Particle oldtotpart = psys->totpart; oldtotchild = psys->totchild; - if(part->distr == PART_DISTR_GRID && part->from != PART_FROM_VERT) + if(psys->pointcache->flag & PTCACHE_EXTERNAL) + totpart = pid.cache->totpoint; + else if(part->distr == PART_DISTR_GRID && part->from != PART_FROM_VERT) totpart = part->grid_res*part->grid_res*part->grid_res; else totpart = psys->part->totpart; @@ -4736,7 +4404,7 @@ static void system_step(Scene *scene, Object *ob, ParticleSystem *psys, Particle 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 */ @@ -4783,7 +4451,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); } @@ -4866,7 +4534,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 2fe46be7a89..4bb571aa8ca 100644 --- a/source/blender/blenkernel/intern/pointcache.c +++ b/source/blender/blenkernel/intern/pointcache.c @@ -181,7 +181,11 @@ static int ptcache_path(PTCacheID *pid, char *filename) lib= (pid)? pid->ob->id.lib: NULL; - if (G.relbase_valid || lib) { + if(pid->cache->flag & PTCACHE_EXTERNAL) { + strcpy(filename, pid->cache->path); + return BLI_add_slash(filename); /* new strlen() */ + } + else if (G.relbase_valid || lib) { char file[MAX_PTCACHE_PATH]; /* we dont want the dir, only the file */ char *blendfilename; @@ -196,15 +200,14 @@ static int ptcache_path(PTCacheID *pid, char *filename) snprintf(filename, MAX_PTCACHE_PATH, "//"PTCACHE_PATH"%s", file); /* add blend file name to pointcache dir */ BLI_convertstringcode(filename, blendfilename); - BLI_add_slash(filename); - return strlen(filename); + return BLI_add_slash(filename); /* new strlen() */ } /* use the temp path. this is weak but better then not using point cache at all */ /* btempdir is assumed to exist and ALWAYS has a trailing slash */ snprintf(filename, MAX_PTCACHE_PATH, "%s"PTCACHE_PATH"%d", btempdir, abs(getpid())); - BLI_add_slash(filename); - return strlen(filename); + + return BLI_add_slash(filename); /* new strlen() */ } static int BKE_ptcache_id_filename(PTCacheID *pid, char *filename, int cfra, short do_path, short do_ext) @@ -215,14 +218,14 @@ 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 disk pointcache */ + if (!G.relbase_valid && (pid->cache->flag & PTCACHE_EXTERNAL)==0) return 0; /* save blend file before using disk pointcache */ /* start with temp dir */ if (do_path) { len = ptcache_path(pid, filename); newname += len; } - if(strcmp(pid->cache->name, "")==0) { + if(strcmp(pid->cache->name, "")==0 && (pid->cache->flag & PTCACHE_EXTERNAL)==0) { idname = (pid->ob->id.name+2); /* convert chars to hex so they are always a valid filename */ while('\0' != *idname) { @@ -239,7 +242,15 @@ static int BKE_ptcache_id_filename(PTCacheID *pid, char *filename, int cfra, sho } if (do_ext) { - snprintf(newname, MAX_PTCACHE_FILE, "_%06d_%02d"PTCACHE_EXT, cfra, pid->stack_index); /* always 6 chars */ + if(pid->cache->flag & PTCACHE_EXTERNAL) { + if(pid->cache->index >= 0) + snprintf(newname, MAX_PTCACHE_FILE, "_%06d_%02d"PTCACHE_EXT, cfra, pid->stack_index); /* always 6 chars */ + else + snprintf(newname, MAX_PTCACHE_FILE, "_%06d"PTCACHE_EXT, cfra); /* always 6 chars */ + } + else { + snprintf(newname, MAX_PTCACHE_FILE, "_%06d_%02d"PTCACHE_EXT, cfra, pid->stack_index); /* always 6 chars */ + } len += 16; } @@ -257,7 +268,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 disk pointcache */ + if (!G.relbase_valid && (pid->cache->flag & PTCACHE_EXTERNAL)==0) return NULL; /* save blend file before using disk pointcache */ BKE_ptcache_id_filename(pid, filename, cfra, 1, 1); @@ -315,8 +326,10 @@ static int ptcache_pid_totelem(PTCacheID *pid) ParticleSystem *psys = pid->data; return psys->totpart; } - else if(pid->type==PTCACHE_TYPE_CLOTH) - return 0; // TODO + else if(pid->type==PTCACHE_TYPE_CLOTH) { + ClothModifierData *clmd = pid->data; + return clmd->clothObject->numverts; + } return 0; } @@ -327,6 +340,21 @@ void BKE_ptcache_update_info(PTCacheID *pid) int totframes = 0; char mem_info[64]; + if(cache->flag & PTCACHE_EXTERNAL) { + int cfra = cache->startframe; + + for(; cfra<=cache->endframe; cfra++) { + if(BKE_ptcache_id_exist(pid, cfra)) + totframes++; + } + + if(totframes) + sprintf(cache->info, "%i points read for %i frames", cache->totpoint, totframes); + else + sprintf(cache->info, "No valid data to read!"); + return; + } + if(cache->flag & PTCACHE_DISK_CACHE) { int cfra = cache->startframe; @@ -429,6 +457,7 @@ int BKE_ptcache_read_cache(PTCacheReader *reader) if(pf) { BKE_ptcache_file_close(pf); + pf = NULL; MEM_freeN(data); } @@ -523,7 +552,9 @@ int BKE_ptcache_read_cache(PTCacheReader *reader) if(pf) { BKE_ptcache_file_close(pf); + pf = NULL; BKE_ptcache_file_close(pf2); + pf2 = NULL; MEM_freeN(data1); MEM_freeN(data2); } @@ -567,10 +598,13 @@ int BKE_ptcache_read_cache(PTCacheReader *reader) if(pf) { BKE_ptcache_file_close(pf); + pf = NULL; MEM_freeN(data); } - if(pf2) + if(pf2) { BKE_ptcache_file_close(pf2); + pf = NULL; + } ret = PTCACHE_READ_OLD; } @@ -602,13 +636,15 @@ int BKE_ptcache_write_cache(PTCacheWriter *writer) PTCacheFile *pf= NULL; int elemsize = ptcache_pid_elemsize(writer->pid); int i, incr = elemsize / sizeof(float); - int add = 0, overwrite = 0, ocfra; + 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); @@ -616,7 +652,7 @@ int BKE_ptcache_write_cache(PTCacheWriter *writer) add = 1; } else { - int cfra = cache->endframe; + int ocfra; /* find last cached frame */ while(cfra > cache->startframe && !BKE_ptcache_id_exist(writer->pid, cfra)) cfra--; @@ -626,7 +662,7 @@ int BKE_ptcache_write_cache(PTCacheWriter *writer) while(ocfra > cache->startframe && !BKE_ptcache_id_exist(writer->pid, ocfra)) ocfra--; - if(writer->cfra > cfra) { + if(cfra >= cache->startframe && writer->cfra > cfra) { if(ocfra >= cache->startframe && cfra - ocfra < cache->step) overwrite = 1; else @@ -636,7 +672,7 @@ int BKE_ptcache_write_cache(PTCacheWriter *writer) if(add || overwrite) { if(overwrite) - BKE_ptcache_id_clear(writer->pid, PTCACHE_CLEAR_FRAME, ocfra); + BKE_ptcache_id_clear(writer->pid, PTCACHE_CLEAR_FRAME, cfra); pf = BKE_ptcache_file_open(writer->pid, PTCACHE_FILE_WRITE, writer->cfra); if(!pf) @@ -665,7 +701,7 @@ int BKE_ptcache_write_cache(PTCacheWriter *writer) pm2 = cache->mem_cache.last; if(pm2 && writer->cfra > pm2->frame) { - if(pm2 && pm2->prev && pm2->frame - pm2->prev->frame < cache->step) + if(pm2->prev && pm2->frame - pm2->prev->frame < cache->step) overwrite = 1; else add = 1; @@ -734,7 +770,7 @@ void BKE_ptcache_id_clear(PTCacheID *pid, int mode, int cfra) char path_full[MAX_PTCACHE_FILE]; char ext[MAX_PTCACHE_PATH]; - if(!pid->cache) + if(!pid->cache || pid->cache->flag & PTCACHE_BAKED) return; /* don't allow clearing for linked objects */ @@ -1102,6 +1138,10 @@ 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; @@ -1211,8 +1251,9 @@ void BKE_ptcache_make_cache(PTCacheBaker* baker) cache = pid->cache; if((cache->flag & PTCACHE_BAKED)==0) { if(pid->type==PTCACHE_TYPE_PARTICLES) { - /* skip hair particles */ - if(((ParticleSystem*)pid->data)->part->type == PART_HAIR) + 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); @@ -1310,6 +1351,7 @@ void BKE_ptcache_toggle_disk_cache(PTCacheID *pid) { if (!G.relbase_valid){ cache->flag &= ~PTCACHE_DISK_CACHE; + printf("File must be saved before using disk cache!\n"); return; } @@ -1393,3 +1435,77 @@ void BKE_ptcache_toggle_disk_cache(PTCacheID *pid) { BKE_ptcache_update_info(pid); } + +void BKE_ptcache_load_external(PTCacheID *pid) +{ + PointCache *cache = pid->cache; + int len; /* store the length of the string */ + + /* mode is same as fopen's modes */ + DIR *dir; + struct dirent *de; + char path[MAX_PTCACHE_PATH]; + char filename[MAX_PTCACHE_FILE]; + char path_full[MAX_PTCACHE_FILE]; + char ext[MAX_PTCACHE_PATH]; + + if(!cache) + return; + + cache->startframe = MAXFRAME; + cache->endframe = -1; + cache->totpoint = 0; + + ptcache_path(pid, path); + + len = BKE_ptcache_id_filename(pid, filename, 1, 0, 0); /* no path */ + + dir = opendir(path); + if (dir==NULL) + return; + + if(cache->index >= 0) + snprintf(ext, sizeof(ext), "_%02d"PTCACHE_EXT, cache->index); + else + strcpy(ext, PTCACHE_EXT); + + 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 */ + /* 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); + + cache->startframe = MIN2(cache->startframe, frame); + cache->endframe = MAX2(cache->endframe, frame); + } + } + } + } + closedir(dir); + + if(cache->startframe != MAXFRAME) { + PTCacheFile *pf; + int elemsize = ptcache_pid_elemsize(pid); + int incr = elemsize / sizeof(float); + float *data = NULL; + pf= BKE_ptcache_file_open(pid, PTCACHE_FILE_READ, cache->startframe); + + if(pf) { + data = MEM_callocN(elemsize, "pointcache read data"); + while(BKE_ptcache_file_read_floats(pf, data, incr)) + cache->totpoint++; + + BKE_ptcache_file_close(pf); + MEM_freeN(data); + } + } + + cache->flag &= ~(PTCACHE_OUTDATED|PTCACHE_FRAMES_SKIPPED); + + BKE_ptcache_update_info(pid); +} diff --git a/source/blender/blenkernel/intern/property.c b/source/blender/blenkernel/intern/property.c index 261b58454bd..5cae527957b 100644 --- a/source/blender/blenkernel/intern/property.c +++ b/source/blender/blenkernel/intern/property.c @@ -33,6 +33,7 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> +#include <ctype.h> #ifdef HAVE_CONFIG_H #include <config.h> @@ -99,25 +100,18 @@ void init_property(bProperty *prop) if(prop->poin && prop->poin != &prop->data) MEM_freeN(prop->poin); prop->poin= 0; - prop->otype= prop->type; prop->data= 0; switch(prop->type) { case GPROP_BOOL: - prop->poin= &prop->data; - break; case GPROP_INT: - prop->poin= &prop->data; - break; case GPROP_FLOAT: + case GPROP_TIME: prop->poin= &prop->data; break; case GPROP_STRING: prop->poin= MEM_callocN(MAX_PROPSTRING, "property string"); break; - case GPROP_TIME: - prop->poin= &prop->data; - break; } } @@ -136,6 +130,60 @@ bProperty *new_property(int type) return prop; } +/* used by unique_property() only */ +static bProperty *get_property__internal(bProperty *first, bProperty *self, const char *name) +{ + bProperty *p; + for(p= first; p; p= p->next) { + if (p!=self && (strcmp(p->name, name)==0)) + return p; + } + return NULL; +} +void unique_property(bProperty *first, bProperty *prop, int force) +{ + bProperty *p; + + /* set the first if its not set */ + if(first==NULL) { + first= prop; + while(first->prev) { + first= first->prev; + } + } + + if(force) { + /* change other names to make them unique */ + while((p = get_property__internal(first, prop, prop->name))) { + unique_property(first, p, 0); + } + }else { + /* change our own name until its unique */ + if(get_property__internal(first, prop, prop->name)) { + /* there is a collision */ + char new_name[sizeof(prop->name)]; + char base_name[sizeof(prop->name)]; + char num[sizeof(prop->name)]; + int i= 0; + + /* strip numbers */ + strcpy(base_name, prop->name); + for(i= strlen(base_name)-1; (i>=0 && isdigit(base_name[i])); i--) { + base_name[i]= '\0'; + } + i= 0; + + do { /* ensure we have enough chars for the new number in the name */ + sprintf(num, "%d", i++); + BLI_strncpy(new_name, base_name, sizeof(prop->name) - strlen(num)); + strcat(new_name, num); + } while(get_property__internal(first, prop, new_name)); + + strcpy(prop->name, new_name); + } + } +} + bProperty *get_ob_property(Object *ob, char *name) { bProperty *prop; diff --git a/source/blender/blenkernel/intern/report.c b/source/blender/blenkernel/intern/report.c index 8de8cf8d0f4..391adfb762a 100644 --- a/source/blender/blenkernel/intern/report.c +++ b/source/blender/blenkernel/intern/report.c @@ -49,6 +49,7 @@ static char *report_type_str(int type) switch(type) { case RPT_DEBUG: return "Debug"; case RPT_INFO: return "Info"; + case RPT_OPERATOR: return "Operator"; case RPT_WARNING: return "Warning"; case RPT_ERROR: return "Error"; case RPT_ERROR_INVALID_INPUT: return "Invalid Input Error"; @@ -72,15 +73,21 @@ void BKE_reports_init(ReportList *reports, int flag) void BKE_reports_clear(ReportList *reports) { - Report *report; + Report *report, *report_next; if(!reports) return; - for(report=reports->list.first; report; report=report->next) + report= reports->list.first; + + while (report) { + report_next= report->next; MEM_freeN(report->message); + MEM_freeN(report); + report= report_next; + } - BLI_freelistN(&reports->list); + reports->list.first= reports->list.last= NULL; } void BKE_report(ReportList *reports, ReportType type, const char *message) @@ -101,7 +108,7 @@ void BKE_report(ReportList *reports, ReportType type, const char *message) len= strlen(message); report->message= MEM_callocN(sizeof(char)*(len+1), "ReportMessage"); memcpy(report->message, message, sizeof(char)*(len+1)); - + report->len= len; BLI_addtail(&reports->list, report); } } @@ -128,7 +135,7 @@ void BKE_reportf(ReportList *reports, ReportType type, const char *format, ...) va_end(args); report->message= BLI_dynstr_get_cstring(ds); - + report->len= BLI_dynstr_get_len(ds); BLI_dynstr_free(ds); report->type= type; @@ -154,6 +161,7 @@ void BKE_reports_prepend(ReportList *reports, const char *prepend) MEM_freeN(report->message); report->message= BLI_dynstr_get_cstring(ds); + report->len= BLI_dynstr_get_len(ds); BLI_dynstr_free(ds); } @@ -178,6 +186,7 @@ void BKE_reports_prependf(ReportList *reports, const char *prepend, ...) MEM_freeN(report->message); report->message= BLI_dynstr_get_cstring(ds); + report->len= BLI_dynstr_get_len(ds); BLI_dynstr_free(ds); } diff --git a/source/blender/blenkernel/intern/scene.c b/source/blender/blenkernel/intern/scene.c index 23da5c66850..d629654c426 100644 --- a/source/blender/blenkernel/intern/scene.c +++ b/source/blender/blenkernel/intern/scene.c @@ -54,7 +54,6 @@ #include "DNA_meta_types.h" #include "DNA_object_types.h" #include "DNA_scene_types.h" -#include "DNA_scriptlink_types.h" #include "DNA_texture_types.h" #include "DNA_userdef_types.h" @@ -142,10 +141,6 @@ void free_scene(Scene *sce) BLI_freelistN(&sce->base); seq_free_editing(sce->ed); - -#ifndef DISABLE_PYTHON - BPY_free_scriptlink(&sce->scriptlink); -#endif BKE_free_animdata((ID *)sce); BKE_keyingsets_free(&sce->keyingsets); @@ -204,17 +199,17 @@ Scene *add_scene(char *name) sce= alloc_libblock(&G.main->scene, ID_SCE, name); sce->lay= 1; - 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; @@ -223,26 +218,18 @@ Scene *add_scene(char *name) sce->r.frs_sec= 25; sce->r.frs_sec_base= 1; sce->r.ocres = 128; + sce->r.color_mgt_flag |= R_COLOR_MANAGEMENT; 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; - - sce->r.xplay= 640; - 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; - - sce->r.stereomode = 1; // no stereo - sce->r.domeangle = 180; - sce->r.domemode = 1; - sce->r.domeres = 4; - sce->r.domeresbuf = 1.0f; - sce->r.dometilt = 0; sce->r.simplify_subsurf= 6; sce->r.simplify_particles= 1.0f; @@ -318,6 +305,29 @@ Scene *add_scene(char *name) /* note; in header_info.c the scene copy happens..., if you add more to renderdata it has to be checked there */ scene_add_render_layer(sce); + /* game data */ + sce->gm.stereoflag = STEREO_NOSTEREO; + sce->gm.stereomode = STEREO_ANAGLYPH; + sce->gm.dome.angle = 180; + sce->gm.dome.mode = DOME_FISHEYE; + sce->gm.dome.res = 4; + sce->gm.dome.resbuf = 1.0f; + sce->gm.dome.tilt = 0; + + sce->gm.xplay= 800; + sce->gm.yplay= 600; + sce->gm.freqplay= 60; + sce->gm.depth= 32; + + sce->gm.gravity= 9.8f; + sce->gm.physicsEngine= WOPHY_BULLET; + sce->gm.mode = 32; //XXX ugly harcoding, still not sure we should drop mode. 32 == 1 << 5 == use_occlusion_culling + sce->gm.occlusionRes = 128; + sce->gm.ticrate = 60; + sce->gm.maxlogicstep = 5; + sce->gm.physubstep = 1; + sce->gm.maxphystep = 5; + return sce; } @@ -383,9 +393,6 @@ void set_scene_bg(Scene *scene) ob->ctime= -1234567.0; /* force ipo to be calculated later */ } /* no full animation update, this to enable render code to work (render code calls own animation updates) */ - - /* do we need FRAMECHANGED in set_scene? */ -// if (G.f & G_DOSCRIPTLINKS) BPY_do_all_scripts(SCRIPT_FRAMECHANGED, 0); } /* called from creator.c */ @@ -644,9 +651,6 @@ void scene_update_for_newframe(Scene *sce, unsigned int lay) /* clear animation overrides */ // XXX TODO... -#ifndef DISABLE_PYTHON - if (G.f & G_DOSCRIPTLINKS) BPY_do_all_scripts(SCRIPT_FRAMECHANGED, 0); -#endif /* sets first, we allow per definition current scene to have dependencies on sets */ for(sce= sce->set; sce; sce= sce->set) scene_update(sce, lay); diff --git a/source/blender/blenkernel/intern/screen.c b/source/blender/blenkernel/intern/screen.c index 4b6eddf60d0..cc740d7fb3d 100644 --- a/source/blender/blenkernel/intern/screen.c +++ b/source/blender/blenkernel/intern/screen.c @@ -61,16 +61,16 @@ static void spacetype_free(SpaceType *st) BLI_freelistN(&art->drawcalls); for(pt= art->paneltypes.first; pt; pt= pt->next) - if(pt->py_free) - pt->py_free(pt->py_data); + if(pt->ext.free) + pt->ext.free(pt->ext.data); for(ht= art->headertypes.first; ht; ht= ht->next) - if(ht->py_free) - ht->py_free(ht->py_data); + if(ht->ext.free) + ht->ext.free(ht->ext.data); for(mt= art->menutypes.first; mt; mt= mt->next) - if(mt->py_free) - mt->py_free(mt->py_data); + if(mt->ext.free) + mt->ext.free(mt->ext.data); BLI_freelistN(&art->paneltypes); BLI_freelistN(&art->headertypes); @@ -112,7 +112,7 @@ ARegionType *BKE_regiontype_from_id(SpaceType *st, int regionid) if(art->regionid==regionid) return art; - printf("Error, region type missing in %s\n", st->name); + printf("Error, region type missing in - name:\"%s\", id:%d\n", st->name, st->spaceid); return st->regiontypes.first; } @@ -179,6 +179,9 @@ ARegion *BKE_area_region_copy(SpaceType *st, ARegion *ar) else newar->regiondata= MEM_dupallocN(ar->regiondata); } + + if(ar->v2d.tab_offset) + newar->v2d.tab_offset= MEM_dupallocN(ar->v2d.tab_offset); newar->panels.first= newar->panels.last= NULL; BLI_duplicatelist(&newar->panels, &ar->panels); @@ -271,10 +274,14 @@ void BKE_area_region_free(SpaceType *st, ARegion *ar) } else if(ar->type && ar->type->free) ar->type->free(ar); + + if(ar->v2d.tab_offset) { + MEM_freeN(ar->v2d.tab_offset); + ar->v2d.tab_offset= NULL; + } - if(ar) { + if(ar) BLI_freelistN(&ar->panels); - } } /* not area itself */ @@ -291,10 +298,6 @@ void BKE_screen_area_free(ScrArea *sa) BKE_spacedata_freelist(&sa->spacedata); BLI_freelistN(&sa->actionzones); - -#ifndef DISABLE_PYTHON - BPY_free_scriptlink(&sa->scriptlink); -#endif } /* don't free screen itself */ diff --git a/source/blender/blenkernel/intern/sequence.c b/source/blender/blenkernel/intern/sequence.c index c8d95929027..4832086ac88 100644 --- a/source/blender/blenkernel/intern/sequence.c +++ b/source/blender/blenkernel/intern/sequence.c @@ -58,7 +58,7 @@ /* **** XXX ******** */ static int seqrectx= 0; /* bad bad global! */ static int seqrecty= 0; -static void waitcursor() {} +static void waitcursor(int val) {} static int blender_test_break() {return 0;} /* **** XXX ******** */ @@ -2017,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); @@ -2038,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 @@ -2093,7 +2094,7 @@ 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) + && !have_seq && !build_proxy_run) #endif diff --git a/source/blender/blenkernel/intern/sketch.c b/source/blender/blenkernel/intern/sketch.c new file mode 100644 index 00000000000..8deae7e8e10 --- /dev/null +++ b/source/blender/blenkernel/intern/sketch.c @@ -0,0 +1,600 @@ +/** + * + * $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. + * + * Contributor(s): none yet. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#include <string.h> +#include <math.h> +#include <float.h> + +#include "MEM_guardedalloc.h" + +#include "BLI_blenlib.h" +#include "BLI_arithb.h" + +#include "BKE_sketch.h" +#include "BKE_utildefines.h" + +#include "DNA_userdef_types.h" + +void freeSketch(SK_Sketch *sketch) +{ + SK_Stroke *stk, *next; + + for (stk = sketch->strokes.first; stk; stk = next) + { + next = stk->next; + + sk_freeStroke(stk); + } + + BLI_freelistN(&sketch->depth_peels); + + MEM_freeN(sketch); +} + +SK_Sketch* createSketch() +{ + SK_Sketch *sketch; + + sketch = MEM_callocN(sizeof(SK_Sketch), "SK_Sketch"); + + sketch->active_stroke = NULL; + sketch->gesture = NULL; + + sketch->strokes.first = NULL; + sketch->strokes.last = NULL; + + return sketch; +} + +void sk_initPoint(SK_Point *pt, SK_DrawData *dd, float *no) +{ + if (no) + { + VECCOPY(pt->no, no); + Normalize(pt->no); + } + else + { + pt->no[0] = 0; + pt->no[1] = 0; + pt->no[2] = 1; + } + pt->p2d[0] = dd->mval[0]; + pt->p2d[1] = dd->mval[1]; + /* more init code here */ +} + +void sk_copyPoint(SK_Point *dst, SK_Point *src) +{ + memcpy(dst, src, sizeof(SK_Point)); +} + +void sk_allocStrokeBuffer(SK_Stroke *stk) +{ + stk->points = MEM_callocN(sizeof(SK_Point) * stk->buf_size, "SK_Point buffer"); +} + +void sk_freeStroke(SK_Stroke *stk) +{ + MEM_freeN(stk->points); + MEM_freeN(stk); +} + +SK_Stroke* sk_createStroke() +{ + SK_Stroke *stk; + + stk = MEM_callocN(sizeof(SK_Stroke), "SK_Stroke"); + + stk->selected = 0; + stk->nb_points = 0; + stk->buf_size = SK_Stroke_BUFFER_INIT_SIZE; + + sk_allocStrokeBuffer(stk); + + return stk; +} + +void sk_shrinkStrokeBuffer(SK_Stroke *stk) +{ + if (stk->nb_points < stk->buf_size) + { + SK_Point *old_points = stk->points; + + stk->buf_size = stk->nb_points; + + sk_allocStrokeBuffer(stk); + + memcpy(stk->points, old_points, sizeof(SK_Point) * stk->nb_points); + + MEM_freeN(old_points); + } +} + +void sk_growStrokeBuffer(SK_Stroke *stk) +{ + if (stk->nb_points == stk->buf_size) + { + SK_Point *old_points = stk->points; + + stk->buf_size *= 2; + + sk_allocStrokeBuffer(stk); + + memcpy(stk->points, old_points, sizeof(SK_Point) * stk->nb_points); + + MEM_freeN(old_points); + } +} + +void sk_growStrokeBufferN(SK_Stroke *stk, int n) +{ + if (stk->nb_points + n > stk->buf_size) + { + SK_Point *old_points = stk->points; + + while (stk->nb_points + n > stk->buf_size) + { + stk->buf_size *= 2; + } + + sk_allocStrokeBuffer(stk); + + memcpy(stk->points, old_points, sizeof(SK_Point) * stk->nb_points); + + MEM_freeN(old_points); + } +} + + +void sk_replaceStrokePoint(SK_Stroke *stk, SK_Point *pt, int n) +{ + memcpy(stk->points + n, pt, sizeof(SK_Point)); +} + +void sk_insertStrokePoint(SK_Stroke *stk, SK_Point *pt, int n) +{ + int size = stk->nb_points - n; + + sk_growStrokeBuffer(stk); + + memmove(stk->points + n + 1, stk->points + n, size * sizeof(SK_Point)); + + memcpy(stk->points + n, pt, sizeof(SK_Point)); + + stk->nb_points++; +} + +void sk_appendStrokePoint(SK_Stroke *stk, SK_Point *pt) +{ + sk_growStrokeBuffer(stk); + + memcpy(stk->points + stk->nb_points, pt, sizeof(SK_Point)); + + stk->nb_points++; +} + +void sk_insertStrokePoints(SK_Stroke *stk, SK_Point *pts, int len, int start, int end) +{ + int size = end - start + 1; + + sk_growStrokeBufferN(stk, len - size); + + if (len != size) + { + int tail_size = stk->nb_points - end + 1; + + memmove(stk->points + start + len, stk->points + end + 1, tail_size * sizeof(SK_Point)); + } + + memcpy(stk->points + start, pts, len * sizeof(SK_Point)); + + stk->nb_points += len - size; +} + +void sk_trimStroke(SK_Stroke *stk, int start, int end) +{ + int size = end - start + 1; + + if (start > 0) + { + memmove(stk->points, stk->points + start, size * sizeof(SK_Point)); + } + + stk->nb_points = size; +} + +void sk_straightenStroke(SK_Stroke *stk, int start, int end, float p_start[3], float p_end[3]) +{ + SK_Point pt1, pt2; + SK_Point *prev, *next; + float delta_p[3]; + int i, total; + + total = end - start; + + VecSubf(delta_p, p_end, p_start); + + prev = stk->points + start; + next = stk->points + end; + + VECCOPY(pt1.p, p_start); + VECCOPY(pt1.no, prev->no); + pt1.mode = prev->mode; + pt1.type = prev->type; + + VECCOPY(pt2.p, p_end); + VECCOPY(pt2.no, next->no); + pt2.mode = next->mode; + pt2.type = next->type; + + sk_insertStrokePoint(stk, &pt1, start + 1); /* insert after start */ + sk_insertStrokePoint(stk, &pt2, end + 1); /* insert before end (since end was pushed back already) */ + + for (i = 1; i < total; i++) + { + float delta = (float)i / (float)total; + float *p = stk->points[start + 1 + i].p; + + VECCOPY(p, delta_p); + VecMulf(p, delta); + VecAddf(p, p, p_start); + } +} + +void sk_polygonizeStroke(SK_Stroke *stk, int start, int end) +{ + int offset; + int i; + + /* find first exact points outside of range */ + for (;start > 0; start--) + { + if (stk->points[start].type == PT_EXACT) + { + break; + } + } + + for (;end < stk->nb_points - 1; end++) + { + if (stk->points[end].type == PT_EXACT) + { + break; + } + } + + offset = start + 1; + + for (i = start + 1; i < end; i++) + { + if (stk->points[i].type == PT_EXACT) + { + if (offset != i) + { + memcpy(stk->points + offset, stk->points + i, sizeof(SK_Point)); + } + + offset++; + } + } + + /* some points were removes, move end of array */ + if (offset < end) + { + int size = stk->nb_points - end; + memmove(stk->points + offset, stk->points + end, size * sizeof(SK_Point)); + stk->nb_points = offset + size; + } +} + +void sk_flattenStroke(SK_Stroke *stk, int start, int end) +{ + float normal[3], distance[3]; + float limit; + int i, total; + + total = end - start + 1; + + VECCOPY(normal, stk->points[start].no); + + VecSubf(distance, stk->points[end].p, stk->points[start].p); + Projf(normal, distance, normal); + limit = Normalize(normal); + + for (i = 1; i < total - 1; i++) + { + float d = limit * i / total; + float offset[3]; + float *p = stk->points[start + i].p; + + VecSubf(distance, p, stk->points[start].p); + Projf(distance, distance, normal); + + VECCOPY(offset, normal); + VecMulf(offset, d); + + VecSubf(p, p, distance); + VecAddf(p, p, offset); + } +} + +void sk_removeStroke(SK_Sketch *sketch, SK_Stroke *stk) +{ + if (sketch->active_stroke == stk) + { + sketch->active_stroke = NULL; + } + + BLI_remlink(&sketch->strokes, stk); + sk_freeStroke(stk); +} + +void sk_reverseStroke(SK_Stroke *stk) +{ + SK_Point *old_points = stk->points; + int i = 0; + + sk_allocStrokeBuffer(stk); + + for (i = 0; i < stk->nb_points; i++) + { + sk_copyPoint(stk->points + i, old_points + stk->nb_points - 1 - i); + } + + MEM_freeN(old_points); +} + + +/* Ramer-Douglas-Peucker algorithm for line simplification */ +void sk_filterStroke(SK_Stroke *stk, int start, int end) +{ + SK_Point *old_points = stk->points; + int nb_points = stk->nb_points; + char *marked = NULL; + char work; + int i; + + if (start == -1) + { + start = 0; + end = stk->nb_points - 1; + } + + sk_allocStrokeBuffer(stk); + stk->nb_points = 0; + + /* adding points before range */ + for (i = 0; i < start; i++) + { + sk_appendStrokePoint(stk, old_points + i); + } + + marked = MEM_callocN(nb_points, "marked array"); + marked[start] = 1; + marked[end] = 1; + + work = 1; + + /* while still reducing */ + while (work) + { + int ls, le; + work = 0; + + ls = start; + le = start+1; + + /* while not over interval */ + while (ls < end) + { + int max_i = 0; + short v1[2]; + float max_dist = 16; /* more than 4 pixels */ + + /* find the next marked point */ + while(marked[le] == 0) + { + le++; + } + + /* perpendicular vector to ls-le */ + v1[1] = old_points[le].p2d[0] - old_points[ls].p2d[0]; + v1[0] = old_points[ls].p2d[1] - old_points[le].p2d[1]; + + + for( i = ls + 1; i < le; i++ ) + { + float mul; + float dist; + short v2[2]; + + v2[0] = old_points[i].p2d[0] - old_points[ls].p2d[0]; + v2[1] = old_points[i].p2d[1] - old_points[ls].p2d[1]; + + if (v2[0] == 0 && v2[1] == 0) + { + continue; + } + + mul = (float)(v1[0]*v2[0] + v1[1]*v2[1]) / (float)(v2[0]*v2[0] + v2[1]*v2[1]); + + dist = mul * mul * (v2[0]*v2[0] + v2[1]*v2[1]); + + if (dist > max_dist) + { + max_dist = dist; + max_i = i; + } + } + + if (max_i != 0) + { + work = 1; + marked[max_i] = 1; + } + + ls = le; + le = ls + 1; + } + } + + + /* adding points after range */ + for (i = start; i <= end; i++) + { + if (marked[i]) + { + sk_appendStrokePoint(stk, old_points + i); + } + } + + MEM_freeN(marked); + + /* adding points after range */ + for (i = end + 1; i < nb_points; i++) + { + sk_appendStrokePoint(stk, old_points + i); + } + + MEM_freeN(old_points); + + sk_shrinkStrokeBuffer(stk); +} + + +void sk_filterLastContinuousStroke(SK_Stroke *stk) +{ + int start, end; + + end = stk->nb_points -1; + + for (start = end - 1; start > 0 && stk->points[start].type == PT_CONTINUOUS; start--) + { + /* nothing to do here*/ + } + + if (end - start > 1) + { + sk_filterStroke(stk, start, end); + } +} + +SK_Point *sk_lastStrokePoint(SK_Stroke *stk) +{ + SK_Point *pt = NULL; + + if (stk->nb_points > 0) + { + pt = stk->points + (stk->nb_points - 1); + } + + return pt; +} + +void sk_endContinuousStroke(SK_Stroke *stk) +{ + stk->points[stk->nb_points - 1].type = PT_EXACT; +} + +void sk_updateNextPoint(SK_Sketch *sketch, SK_Stroke *stk) +{ + if (stk) + { + memcpy(&sketch->next_point, stk->points[stk->nb_points - 1].p, sizeof(SK_Point)); + } +} + +int sk_stroke_filtermval(SK_DrawData *dd) +{ + int retval = 0; + if (ABS(dd->mval[0] - dd->previous_mval[0]) + ABS(dd->mval[1] - dd->previous_mval[1]) > U.gp_manhattendist) + { + retval = 1; + } + + return retval; +} + +void sk_initDrawData(SK_DrawData *dd, short mval[2]) +{ + dd->mval[0] = mval[0]; + dd->mval[1] = mval[1]; + dd->previous_mval[0] = -1; + dd->previous_mval[1] = -1; + dd->type = PT_EXACT; +} + + +void sk_deleteSelectedStrokes(SK_Sketch *sketch) +{ + SK_Stroke *stk, *next; + + for (stk = sketch->strokes.first; stk; stk = next) + { + next = stk->next; + + if (stk->selected == 1) + { + sk_removeStroke(sketch, stk); + } + } +} + +void sk_selectAllSketch(SK_Sketch *sketch, int mode) +{ + SK_Stroke *stk = NULL; + + if (mode == -1) + { + for (stk = sketch->strokes.first; stk; stk = stk->next) + { + stk->selected = 0; + } + } + else if (mode == 0) + { + for (stk = sketch->strokes.first; stk; stk = stk->next) + { + stk->selected = 1; + } + } + else if (mode == 1) + { + int selected = 1; + + for (stk = sketch->strokes.first; stk; stk = stk->next) + { + selected &= stk->selected; + } + + selected ^= 1; + + for (stk = sketch->strokes.first; stk; stk = stk->next) + { + stk->selected = selected; + } + } +} diff --git a/source/blender/blenkernel/intern/smoke.c b/source/blender/blenkernel/intern/smoke.c new file mode 100644 index 00000000000..fbf6beb835f --- /dev/null +++ b/source/blender/blenkernel/intern/smoke.c @@ -0,0 +1,1342 @@ +/** + * BKE_cloth.h + * + * $Id$ + * + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * The Original Code is Copyright (C) Blender Foundation. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): Daniel Genrich + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/* Part of the code copied from elbeem fluid library, copyright by Nils Thuerey */ + +#include <GL/glew.h> + +#include "MEM_guardedalloc.h" + +#include <float.h> +#include <math.h> +#include "stdio.h" + +#include "BLI_linklist.h" +#include "BLI_rand.h" +#include "BLI_jitter.h" +#include "BLI_blenlib.h" +#include "BLI_arithb.h" +#include "BLI_edgehash.h" +#include "BLI_kdtree.h" +#include "BLI_kdopbvh.h" + +#include "BKE_cdderivedmesh.h" +#include "BKE_customdata.h" +#include "BKE_DerivedMesh.h" +#include "BKE_modifier.h" +#include "BKE_particle.h" +#include "BKE_utildefines.h" + +#include "DNA_customdata_types.h" +#include "DNA_group_types.h" +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_modifier_types.h" +#include "DNA_object_types.h" +#include "DNA_particle_types.h" +#include "DNA_scene_types.h" +#include "DNA_smoke_types.h" + +#include "smoke_API.h" + +#include "BKE_smoke.h" + +#ifdef _WIN32 +#include <time.h> +#include <stdio.h> +#include <conio.h> +#include <windows.h> + +static LARGE_INTEGER liFrequency; +static LARGE_INTEGER liStartTime; +static LARGE_INTEGER liCurrentTime; + +static void tstart ( void ) +{ + QueryPerformanceFrequency ( &liFrequency ); + QueryPerformanceCounter ( &liStartTime ); +} +static void tend ( void ) +{ + QueryPerformanceCounter ( &liCurrentTime ); +} +static double tval() +{ + return ((double)( (liCurrentTime.QuadPart - liStartTime.QuadPart)* (double)1000.0/(double)liFrequency.QuadPart )); +} +#else +#include <sys/time.h> +static struct timeval _tstart, _tend; +static struct timezone tz; +static void tstart ( void ) +{ + gettimeofday ( &_tstart, &tz ); +} +static void tend ( void ) +{ + gettimeofday ( &_tend,&tz ); +} +static double tval() +{ + double t1, t2; + t1 = ( double ) _tstart.tv_sec*1000 + ( double ) _tstart.tv_usec/ ( 1000 ); + t2 = ( double ) _tend.tv_sec*1000 + ( double ) _tend.tv_usec/ ( 1000 ); + return t2-t1; +} +#endif + +struct Object; +struct Scene; +struct DerivedMesh; +struct SmokeModifierData; + +// forward declerations +static void get_cell(struct SmokeModifierData *smd, float *pos, int *cell, int correct); +static void get_bigcell(struct SmokeModifierData *smd, float *pos, int *cell, int correct); +void calcTriangleDivs(Object *ob, MVert *verts, int numverts, MFace *tris, int numfaces, int numtris, int **tridivs, float cell_len); + +#define TRI_UVOFFSET (1./4.) + +int smokeModifier_init (SmokeModifierData *smd, Object *ob, Scene *scene, DerivedMesh *dm) +{ + if((smd->type & MOD_SMOKE_TYPE_DOMAIN) && smd->domain && !smd->domain->fluid) + { + size_t i; + float min[3] = {FLT_MAX, FLT_MAX, FLT_MAX}, max[3] = {-FLT_MAX, -FLT_MAX, -FLT_MAX}; + float size[3]; + MVert *verts = dm->getVertArray(dm); + float scale = 0.0; + int res = smd->domain->maxres; + + // get BB of domain + for(i = 0; i < dm->getNumVerts(dm); i++) + { + float tmp[3]; + + VECCOPY(tmp, verts[i].co); + Mat4MulVecfl(ob->obmat, tmp); + + // min BB + min[0] = MIN2(min[0], tmp[0]); + min[1] = MIN2(min[1], tmp[1]); + min[2] = MIN2(min[2], tmp[2]); + + // max BB + max[0] = MAX2(max[0], tmp[0]); + max[1] = MAX2(max[1], tmp[1]); + max[2] = MAX2(max[2], tmp[2]); + } + + VECCOPY(smd->domain->p0, min); + VECCOPY(smd->domain->p1, max); + + // calc other res with max_res provided + VECSUB(size, max, min); + if(size[0] > size[1]) + { + if(size[0] > size[1]) + { + scale = res / size[0]; + smd->domain->dx = size[0] / res; + smd->domain->res[0] = res; + smd->domain->res[1] = (int)(size[1] * scale + 0.5); + smd->domain->res[2] = (int)(size[2] * scale + 0.5); + } + else + { + scale = res / size[1]; + smd->domain->dx = size[1] / res; + smd->domain->res[1] = res; + smd->domain->res[0] = (int)(size[0] * scale + 0.5); + smd->domain->res[2] = (int)(size[2] * scale + 0.5); + } + } + else + { + if(size[1] > size[2]) + { + scale = res / size[1]; + smd->domain->dx = size[1] / res; + smd->domain->res[1] = res; + smd->domain->res[0] = (int)(size[0] * scale + 0.5); + smd->domain->res[2] = (int)(size[2] * scale + 0.5); + } + else + { + scale = res / size[2]; + smd->domain->dx = size[2] / res; + smd->domain->res[2] = res; + smd->domain->res[0] = (int)(size[0] * scale + 0.5); + smd->domain->res[1] = (int)(size[1] * scale + 0.5); + } + } + + printf("res[0]: %d, res[1]: %d, res[2]: %d\n", smd->domain->res[0], smd->domain->res[1], smd->domain->res[2]); + + // dt max is 0.1 + smd->domain->fluid = smoke_init(smd->domain->res, smd->domain->amplify, smd->domain->p0, smd->domain->p1, 2.5 / FPS); + smd->time = scene->r.cfra; + smd->domain->firstframe = smd->time; + + smoke_initBlenderRNA(smd->domain->fluid, &(smd->domain->alpha), &(smd->domain->beta)); + + return 1; + } + else if((smd->type & MOD_SMOKE_TYPE_FLOW) && smd->flow) + { + // handle flow object here + // XXX TODO + + smd->time = scene->r.cfra; + + // update particle lifetime to be one frame + // smd->flow->psys->part->lifetime = scene->r.efra + 1; + + return 1; + } + else if((smd->type & MOD_SMOKE_TYPE_COLL) && smd->coll) + { + smd->time = scene->r.cfra; + + if(!smd->coll->points) + { + // init collision points + SmokeCollSettings *scs = smd->coll; + MVert *mvert = dm->getVertArray(dm); + MFace *mface = dm->getFaceArray(dm); + size_t i = 0, divs = 0; + int *tridivs = NULL; + float cell_len = 1.0 / 50.0; // for res = 50 + size_t newdivs = 0; + size_t max_points = 0; + size_t quads = 0, facecounter = 0; + + // count quads + for(i = 0; i < dm->getNumFaces(dm); i++) + { + if(mface[i].v4) + quads++; + } + + calcTriangleDivs(ob, mvert, dm->getNumVerts(dm), mface, dm->getNumFaces(dm), dm->getNumFaces(dm) + quads, &tridivs, cell_len); + + // count triangle divisions + for(i = 0; i < dm->getNumFaces(dm) + quads; i++) + { + divs += (tridivs[3 * i] + 1) * (tridivs[3 * i + 1] + 1) * (tridivs[3 * i + 2] + 1); + } + + // printf("divs: %d\n", divs); + + scs->points = MEM_callocN(sizeof(float) * (dm->getNumVerts(dm) + divs) * 3, "SmokeCollPoints"); + + for(i = 0; i < dm->getNumVerts(dm); i++) + { + float tmpvec[3]; + VECCOPY(tmpvec, mvert[i].co); + Mat4MulVecfl (ob->obmat, tmpvec); + VECCOPY(&scs->points[i * 3], tmpvec); + } + + for(i = 0, facecounter = 0; i < dm->getNumFaces(dm); i++) + { + int again = 0; + do + { + size_t j, k; + int divs1 = tridivs[3 * facecounter + 0]; + int divs2 = tridivs[3 * facecounter + 1]; + int divs3 = tridivs[3 * facecounter + 2]; + float side1[3], side2[3], trinormorg[3], trinorm[3]; + + if(again == 1 && mface[i].v4) + { + VECSUB(side1, mvert[ mface[i].v3 ].co, mvert[ mface[i].v1 ].co); + VECSUB(side2, mvert[ mface[i].v4 ].co, mvert[ mface[i].v1 ].co); + } + else + { + VECSUB(side1, mvert[ mface[i].v2 ].co, mvert[ mface[i].v1 ].co); + VECSUB(side2, mvert[ mface[i].v3 ].co, mvert[ mface[i].v1 ].co); + } + + Crossf(trinormorg, side1, side2); + Normalize(trinormorg); + VECCOPY(trinorm, trinormorg); + VecMulf(trinorm, 0.25 * cell_len); + + for(j = 0; j <= divs1; j++) + { + for(k = 0; k <= divs2; k++) + { + float p1[3], p2[3], p3[3], p[3]={0,0,0}; + const float uf = (float)(j + TRI_UVOFFSET) / (float)(divs1 + 0.0); + const float vf = (float)(k + TRI_UVOFFSET) / (float)(divs2 + 0.0); + float tmpvec[3]; + + if(uf+vf > 1.0) + { + // printf("bigger - divs1: %d, divs2: %d\n", divs1, divs2); + continue; + } + + VECCOPY(p1, mvert[ mface[i].v1 ].co); + if(again == 1 && mface[i].v4) + { + VECCOPY(p2, mvert[ mface[i].v3 ].co); + VECCOPY(p3, mvert[ mface[i].v4 ].co); + } + else + { + VECCOPY(p2, mvert[ mface[i].v2 ].co); + VECCOPY(p3, mvert[ mface[i].v3 ].co); + } + + VecMulf(p1, (1.0-uf-vf)); + VecMulf(p2, uf); + VecMulf(p3, vf); + + VECADD(p, p1, p2); + VECADD(p, p, p3); + + if(newdivs > divs) + printf("mem problem\n"); + + // mMovPoints.push_back(p + trinorm); + VECCOPY(tmpvec, p); + VECADD(tmpvec, tmpvec, trinorm); + Mat4MulVecfl (ob->obmat, tmpvec); + VECCOPY(&scs->points[3 * (dm->getNumVerts(dm) + newdivs)], tmpvec); + newdivs++; + + if(newdivs > divs) + printf("mem problem\n"); + + // mMovPoints.push_back(p - trinorm); + VECCOPY(tmpvec, p); + VECSUB(tmpvec, tmpvec, trinorm); + Mat4MulVecfl (ob->obmat, tmpvec); + VECCOPY(&scs->points[3 * (dm->getNumVerts(dm) + newdivs)], tmpvec); + newdivs++; + } + } + + if(again == 0 && mface[i].v4) + again++; + else + again = 0; + + facecounter++; + + } while(again!=0); + } + + scs->numpoints = dm->getNumVerts(dm) + newdivs; + + MEM_freeN(tridivs); + } + + } + + return 0; +} + +/*! init triangle divisions */ +void calcTriangleDivs(Object *ob, MVert *verts, int numverts, MFace *faces, int numfaces, int numtris, int **tridivs, float cell_len) +{ + // mTriangleDivs1.resize( faces.size() ); + // mTriangleDivs2.resize( faces.size() ); + // mTriangleDivs3.resize( faces.size() ); + + size_t i = 0, facecounter = 0; + float maxscale[3] = {1,1,1}; // = channelFindMaxVf(mcScale); + float maxpart = ABS(maxscale[0]); + float scaleFac = 0; + float fsTri = 0; + if(ABS(maxscale[1])>maxpart) maxpart = ABS(maxscale[1]); + if(ABS(maxscale[2])>maxpart) maxpart = ABS(maxscale[2]); + scaleFac = 1.0 / maxpart; + // featureSize = mLevel[mMaxRefine].nodeSize + fsTri = cell_len * 0.5 * scaleFac; + + if(*tridivs) + MEM_freeN(*tridivs); + + *tridivs = MEM_callocN(sizeof(int) * numtris * 3, "Smoke_Tridivs"); + + for(i = 0, facecounter = 0; i < numfaces; i++) + { + float p0[3], p1[3], p2[3]; + float side1[3]; + float side2[3]; + float side3[3]; + int divs1=0, divs2=0, divs3=0; + + VECCOPY(p0, verts[faces[i].v1].co); + Mat4MulVecfl (ob->obmat, p0); + VECCOPY(p1, verts[faces[i].v2].co); + Mat4MulVecfl (ob->obmat, p1); + VECCOPY(p2, verts[faces[i].v3].co); + Mat4MulVecfl (ob->obmat, p2); + + VECSUB(side1, p1, p0); + VECSUB(side2, p2, p0); + VECSUB(side3, p1, p2); + + if(INPR(side1, side1) > fsTri*fsTri) + { + float tmp = Normalize(side1); + divs1 = (int)(tmp/fsTri); + } + if(INPR(side2, side2) > fsTri*fsTri) + { + float tmp = Normalize(side2); + divs2 = (int)(tmp/fsTri); + + /* + // debug + if(i==0) + printf("b tmp: %f, fsTri: %f, divs2: %d\n", tmp, fsTri, divs2); + */ + } + + (*tridivs)[3 * facecounter + 0] = divs1; + (*tridivs)[3 * facecounter + 1] = divs2; + (*tridivs)[3 * facecounter + 2] = divs3; + + // TODO quad case + if(faces[i].v4) + { + divs1=0, divs2=0, divs3=0; + + facecounter++; + + VECCOPY(p1, verts[faces[i].v3].co); + Mat4MulVecfl (ob->obmat, p1); + VECCOPY(p2, verts[faces[i].v4].co); + Mat4MulVecfl (ob->obmat, p2); + + VECSUB(side1, p1, p0); + VECSUB(side2, p2, p0); + VECSUB(side3, p1, p2); + + if(INPR(side1, side1) > fsTri*fsTri) + { + float tmp = Normalize(side1); + divs1 = (int)(tmp/fsTri); + } + if(INPR(side2, side2) > fsTri*fsTri) + { + float tmp = Normalize(side2); + divs2 = (int)(tmp/fsTri); + } + + (*tridivs)[3 * facecounter + 0] = divs1; + (*tridivs)[3 * facecounter + 1] = divs2; + (*tridivs)[3 * facecounter + 2] = divs3; + } + facecounter++; + } +} + +void smokeModifier_freeDomain(SmokeModifierData *smd) +{ + if(smd->domain) + { + // free visualisation buffers + if(smd->domain->bind) + { + glDeleteTextures(smd->domain->max_textures, (GLuint *)smd->domain->bind); + MEM_freeN(smd->domain->bind); + } + smd->domain->max_textures = 0; // unnecessary but let's be sure + + if(smd->domain->tray) + MEM_freeN(smd->domain->tray); + if(smd->domain->tvox) + MEM_freeN(smd->domain->tvox); + if(smd->domain->traybig) + MEM_freeN(smd->domain->traybig); + if(smd->domain->tvoxbig) + MEM_freeN(smd->domain->tvoxbig); + + if(smd->domain->fluid) + { + smoke_free(smd->domain->fluid); + } + MEM_freeN(smd->domain); + smd->domain = NULL; + } +} + +void smokeModifier_freeFlow(SmokeModifierData *smd) +{ + if(smd->flow) + { + MEM_freeN(smd->flow); + smd->flow = NULL; + } +} + +void smokeModifier_freeCollision(SmokeModifierData *smd) +{ + if(smd->coll) + { + if(smd->coll->points) + { + MEM_freeN(smd->coll->points); + smd->coll->points = NULL; + } + + MEM_freeN(smd->coll); + smd->coll = NULL; + } +} + +void smokeModifier_reset(struct SmokeModifierData *smd) +{ + if(smd) + { + if(smd->domain) + { + // free visualisation buffers + if(smd->domain->bind) + { + glDeleteTextures(smd->domain->max_textures, (GLuint *)smd->domain->bind); + MEM_freeN(smd->domain->bind); + smd->domain->bind = NULL; + } + smd->domain->max_textures = 0; + smd->domain->viewsettings = 0; // reset view for new frame + + if(smd->domain->tray) + MEM_freeN(smd->domain->tray); + if(smd->domain->tvox) + MEM_freeN(smd->domain->tvox); + if(smd->domain->traybig) + MEM_freeN(smd->domain->traybig); + if(smd->domain->tvoxbig) + MEM_freeN(smd->domain->tvoxbig); + + smd->domain->tvox = NULL; + smd->domain->tray = NULL; + smd->domain->tvoxbig = NULL; + smd->domain->traybig = NULL; + + if(smd->domain->fluid) + { + smoke_free(smd->domain->fluid); + smd->domain->fluid = NULL; + } + } + else if(smd->flow) + { + + } + else if(smd->coll) + { + if(smd->coll->points) + { + MEM_freeN(smd->coll->points); + smd->coll->points = NULL; + } + } + } +} + +void smokeModifier_free (SmokeModifierData *smd) +{ + if(smd) + { + smokeModifier_freeDomain(smd); + smokeModifier_freeFlow(smd); + smokeModifier_freeCollision(smd); + } +} + +void smokeModifier_createType(struct SmokeModifierData *smd) +{ + if(smd) + { + if(smd->type & MOD_SMOKE_TYPE_DOMAIN) + { + if(smd->domain) + smokeModifier_freeDomain(smd); + + smd->domain = MEM_callocN(sizeof(SmokeDomainSettings), "SmokeDomain"); + + smd->domain->smd = smd; + + /* set some standard values */ + smd->domain->fluid = NULL; + smd->domain->eff_group = NULL; + smd->domain->fluid_group = NULL; + smd->domain->coll_group = NULL; + smd->domain->maxres = 48; + smd->domain->amplify = 2; + smd->domain->omega = 0.5; + smd->domain->alpha = -0.001; + smd->domain->beta = 0.1; + smd->domain->flags = 0; + smd->domain->noise = MOD_SMOKE_NOISEWAVE; + smd->domain->visibility = 1; + + // init 3dview buffer + smd->domain->tvox = NULL; + smd->domain->tray = NULL; + smd->domain->tvoxbig = NULL; + smd->domain->traybig = NULL; + smd->domain->viewsettings = 0; + smd->domain->bind = NULL; + smd->domain->max_textures = 0; + } + else if(smd->type & MOD_SMOKE_TYPE_FLOW) + { + if(smd->flow) + smokeModifier_freeFlow(smd); + + smd->flow = MEM_callocN(sizeof(SmokeFlowSettings), "SmokeFlow"); + + smd->flow->smd = smd; + + /* set some standard values */ + smd->flow->density = 1.0; + smd->flow->temp = 1.0; + + smd->flow->psys = NULL; + + } + else if(smd->type & MOD_SMOKE_TYPE_COLL) + { + if(smd->coll) + smokeModifier_freeCollision(smd); + + smd->coll = MEM_callocN(sizeof(SmokeCollSettings), "SmokeColl"); + + smd->coll->smd = smd; + smd->coll->points = NULL; + smd->coll->numpoints = 0; + } + } +} + +// forward declaration +void smoke_calc_transparency(struct SmokeModifierData *smd, float *light, int big); + +void smokeModifier_do(SmokeModifierData *smd, Scene *scene, Object *ob, DerivedMesh *dm) +{ + if(scene->r.cfra >= smd->time) + smokeModifier_init(smd, ob, scene, dm); + + if((smd->type & MOD_SMOKE_TYPE_FLOW)) + { + if(scene->r.cfra > smd->time) + { + // XXX TODO + } + else if(scene->r.cfra < smd->time) + { + smd->time = scene->r.cfra; + smokeModifier_reset(smd); + } + } + else if((smd->type & MOD_SMOKE_TYPE_DOMAIN)) + { + SmokeDomainSettings *sds = smd->domain; + + if(scene->r.cfra > smd->time) + { + GroupObject *go = NULL; + Base *base = NULL; + int cnt_domain = 0; + + tstart(); + + sds->viewsettings = 0; // reset view for new frame + + // check for 2nd domain, if not there -> no groups are necessary + for(base = scene->base.first; base; base= base->next) + { + Object *ob1= base->object; + + if(ob1 && ob1 != ob) + { + ModifierData *tmd = modifiers_findByType(ob1, eModifierType_Smoke); + + if(tmd && tmd->mode & (eModifierMode_Realtime | eModifierMode_Render)) + { + SmokeModifierData *tsmd = (SmokeModifierData *)tmd; + + if((tsmd->type & MOD_SMOKE_TYPE_DOMAIN)) + { + cnt_domain++; + } + } + } + } + + // do flows and fluids + if(sds->fluid_group || !cnt_domain) + { + Object *otherobj = NULL; + ModifierData *md = NULL; + + if(cnt_domain && !sds->fluid_group) // we use groups since we have 2 domains + go = sds->fluid_group->gobject.first; + else + base = scene->base.first; + + while(base || go) + { + otherobj = NULL; + + if(cnt_domain && !sds->fluid_group) + { + if(go->ob) + otherobj = go->ob; + } + else + otherobj = base->object; + + if(!otherobj) + { + if(cnt_domain && !sds->fluid_group) + go = go->next; + else + base= base->next; + + continue; + } + + md = modifiers_findByType(otherobj, eModifierType_Smoke); + + // check for active smoke modifier + if(md && md->mode & (eModifierMode_Realtime | eModifierMode_Render)) + { + SmokeModifierData *smd2 = (SmokeModifierData *)md; + + // check for initialized smoke object + if((smd2->type & MOD_SMOKE_TYPE_FLOW) && smd2->flow) + { + // we got nice flow object + SmokeFlowSettings *sfs = smd2->flow; + + if(sfs->psys && sfs->psys->part && sfs->psys->part->type==PART_EMITTER) // is particle system selected + { + ParticleSystem *psys = sfs->psys; + ParticleSettings *part=psys->part; + ParticleData *pa = NULL; + int p = 0; + float *density = smoke_get_density(sds->fluid); + float *bigdensity = smoke_get_bigdensity(sds->fluid); + float *heat = smoke_get_heat(sds->fluid); + float *velocity_x = smoke_get_velocity_x(sds->fluid); + float *velocity_y = smoke_get_velocity_y(sds->fluid); + float *velocity_z = smoke_get_velocity_z(sds->fluid); + int bigres[3]; + + smoke_get_bigres(smd->domain->fluid, bigres); + + // mostly copied from particle code + for(p=0, pa=psys->particles; p<psys->totpart; p++, pa++) + { + int cell[3]; + int valid = 1; + size_t i = 0; + size_t index = 0; + + if(pa->alive == PARS_KILLED) continue; + else if(pa->alive == PARS_UNBORN && (part->flag & PART_UNBORN)==0) continue; + else if(pa->alive == PARS_DEAD && (part->flag & PART_DIED)==0) continue; + else if(pa->flag & (PARS_UNEXIST+PARS_NO_DISP)) continue; + + // VECCOPY(pos, pa->state.co); + // Mat4MulVecfl (ob->imat, pos); + + // 1. get corresponding cell + get_cell(smd, pa->state.co, cell, 0); + + // check if cell is valid (in the domain boundary) + for(i = 0; i < 3; i++) + if((cell[i] > sds->res[i] - 1) || (cell[i] < 0)) + valid = 0; + + if(!valid) + continue; + + // 2. set cell values (heat, density and velocity) + index = smoke_get_index(cell[0], sds->res[0], cell[1], sds->res[1], cell[2]); + + heat[index] = sfs->temp; + density[index] = sfs->density; + velocity_x[index] = pa->state.vel[0]; + velocity_y[index] = pa->state.vel[1]; + velocity_z[index] = pa->state.vel[2]; + + // we need different handling for the high-res feature + if(bigdensity) + { + // init all surrounding cells according to amplification, too + int i, j, k; + for(i = 0; i < smd->domain->amplify; i++) + for(j = 0; j < smd->domain->amplify; j++) + for(k = 0; k < smd->domain->amplify; k++) + { + index = smoke_get_index(smd->domain->amplify * cell[0] + i, bigres[0], smd->domain->amplify * cell[1] + j, bigres[1], smd->domain->amplify * cell[2] + k); + bigdensity[index] = sfs->density; + } + } + } + } + } + } + + if(cnt_domain && !sds->fluid_group) + go = go->next; + else + base= base->next; + } + } + + // do effectors + /* + if(sds->eff_group) + { + for(go = sds->eff_group->gobject.first; go; go = go->next) + { + if(go->ob) + { + if(ob->pd) + { + + } + } + } + } + */ + + // do collisions + if(sds->coll_group || !cnt_domain) + { + Object *otherobj = NULL; + ModifierData *md = NULL; + + if(cnt_domain && !sds->coll_group) // we use groups since we have 2 domains + go = sds->coll_group->gobject.first; + else + base = scene->base.first; + + while(base || go) + { + otherobj = NULL; + + if(cnt_domain && !sds->coll_group) + { + if(go->ob) + otherobj = go->ob; + } + else + otherobj = base->object; + + if(!otherobj) + { + if(cnt_domain && !sds->coll_group) + go = go->next; + else + base= base->next; + + continue; + } + + md = modifiers_findByType(otherobj, eModifierType_Smoke); + + // check for active smoke modifier + if(md && md->mode & (eModifierMode_Realtime | eModifierMode_Render)) + { + SmokeModifierData *smd2 = (SmokeModifierData *)md; + + if((smd2->type & MOD_SMOKE_TYPE_COLL) && smd2->coll) + { + // we got nice collision object + SmokeCollSettings *scs = smd2->coll; + int cell[3]; + int valid = 1; + size_t index = 0; + size_t i, j; + unsigned char *obstacles = smoke_get_obstacle(smd->domain->fluid); + + for(i = 0; i < scs->numpoints; i++) + { + // 1. get corresponding cell + get_cell(smd, &scs->points[3 * i], cell, 0); + + // check if cell is valid (in the domain boundary) + for(j = 0; j < 3; j++) + if((cell[j] > sds->res[j] - 1) || (cell[j] < 0)) + valid = 0; + + if(!valid) + continue; + + // 2. set cell values (heat, density and velocity) + index = smoke_get_index(cell[0], sds->res[0], cell[1], sds->res[1], cell[2]); + + obstacles[index] = 1; + + // for moving gobstacles + /* + const LbmFloat maxVelVal = 0.1666; + const LbmFloat maxusqr = maxVelVal*maxVelVal*3. *1.5; + + LbmVec objvel = vec2L((mMOIVertices[n]-mMOIVerticesOld[n]) /dvec); { + const LbmFloat usqr = (objvel[0]*objvel[0]+objvel[1]*objvel[1]+objvel[2]*objvel[2])*1.5; + USQRMAXCHECK(usqr, objvel[0],objvel[1],objvel[2], mMaxVlen, mMxvx,mMxvy,mMxvz); + if(usqr>maxusqr) { + // cutoff at maxVelVal + for(int jj=0; jj<3; jj++) { + if(objvel[jj]>0.) objvel[jj] = maxVelVal; + if(objvel[jj]<0.) objvel[jj] = -maxVelVal; + } + } } + + const LbmFloat dp=dot(objvel, vec2L((*pNormals)[n]) ); + const LbmVec oldov=objvel; // debug + objvel = vec2L((*pNormals)[n]) *dp; + */ + } + } + } + + if(cnt_domain && !sds->coll_group) + go = go->next; + else + base= base->next; + } + } + + // set new time + smd->time = scene->r.cfra; + + // simulate the actual smoke (c++ code in intern/smoke) + smoke_step(sds->fluid); + + tend(); + printf ( "Frame: %d, Time: %f\n", (int)smd->time, ( float ) tval() ); + } + else if(scene->r.cfra < smd->time) + { + // we got back in time, reset smoke in this case (TODO: use cache later) + smd->time = scene->r.cfra; + smokeModifier_reset(smd); + } + } +} + +// update necessary information for 3dview +void smoke_prepare_View(SmokeModifierData *smd, float *light) +{ + float *density = NULL; + int x, y, z; + + if(!smd->domain->tray) + { + // TRay is for self shadowing + smd->domain->tray = MEM_callocN(sizeof(float)*smd->domain->res[0]*smd->domain->res[1]*smd->domain->res[2], "Smoke_tRay"); + } + if(!smd->domain->tvox) + { + // TVox is for tranaparency + smd->domain->tvox = MEM_callocN(sizeof(float)*smd->domain->res[0]*smd->domain->res[1]*smd->domain->res[2], "Smoke_tVox"); + } + + // update 3dview + density = smoke_get_density(smd->domain->fluid); + for(x = 0; x < smd->domain->res[0]; x++) + for(y = 0; y < smd->domain->res[1]; y++) + for(z = 0; z < smd->domain->res[2]; z++) + { + size_t index; + + index = smoke_get_index(x, smd->domain->res[0], y, smd->domain->res[1], z); + // Transparency computation + // formula taken from "Visual Simulation of Smoke" / Fedkiw et al. pg. 4 + // T_vox = exp(-C_ext * h) + // C_ext/sigma_t = density * C_ext + smoke_set_tvox(smd, index, exp(-density[index] * 4.0 * smd->domain->dx)); + } + smoke_calc_transparency(smd, light, 0); +} + +// update necessary information for 3dview ("high res" option) +void smoke_prepare_bigView(SmokeModifierData *smd, float *light) +{ + float *density = NULL; + size_t i = 0; + int bigres[3]; + + smoke_get_bigres(smd->domain->fluid, bigres); + + if(!smd->domain->traybig) + { + // TRay is for self shadowing + smd->domain->traybig = MEM_callocN(sizeof(float)*bigres[0]*bigres[1]*bigres[2], "Smoke_tRayBig"); + } + if(!smd->domain->tvoxbig) + { + // TVox is for tranaparency + smd->domain->tvoxbig = MEM_callocN(sizeof(float)*bigres[0]*bigres[1]*bigres[2], "Smoke_tVoxBig"); + } + + density = smoke_get_bigdensity(smd->domain->fluid); + for (i = 0; i < bigres[0] * bigres[1] * bigres[2]; i++) + { + // Transparency computation + // formula taken from "Visual Simulation of Smoke" / Fedkiw et al. pg. 4 + // T_vox = exp(-C_ext * h) + // C_ext/sigma_t = density * C_ext + smoke_set_bigtvox(smd, i, exp(-density[i] * 4.0 * smd->domain->dx / smd->domain->amplify) ); + } + smoke_calc_transparency(smd, light, 1); +} + + +float smoke_get_tvox(SmokeModifierData *smd, size_t index) +{ + return smd->domain->tvox[index]; +} + +void smoke_set_tvox(SmokeModifierData *smd, size_t index, float tvox) +{ + smd->domain->tvox[index] = tvox; +} + +float smoke_get_tray(SmokeModifierData *smd, size_t index) +{ + return smd->domain->tray[index]; +} + +void smoke_set_tray(SmokeModifierData *smd, size_t index, float transparency) +{ + smd->domain->tray[index] = transparency; +} + +float smoke_get_bigtvox(SmokeModifierData *smd, size_t index) +{ + return smd->domain->tvoxbig[index]; +} + +void smoke_set_bigtvox(SmokeModifierData *smd, size_t index, float tvox) +{ + smd->domain->tvoxbig[index] = tvox; +} + +float smoke_get_bigtray(SmokeModifierData *smd, size_t index) +{ + return smd->domain->traybig[index]; +} + +void smoke_set_bigtray(SmokeModifierData *smd, size_t index, float transparency) +{ + smd->domain->traybig[index] = transparency; +} + +long long smoke_get_mem_req(int xres, int yres, int zres, int amplify) +{ + int totalCells = xres * yres * zres; + int amplifiedCells = totalCells * amplify * amplify * amplify; + + // print out memory requirements + long long int coarseSize = sizeof(float) * totalCells * 22 + + sizeof(unsigned char) * totalCells; + + long long int fineSize = sizeof(float) * amplifiedCells * 7 + // big grids + sizeof(float) * totalCells * 8 + // small grids + sizeof(float) * 128 * 128 * 128; // noise tile + + long long int totalMB = (coarseSize + fineSize) / (1024 * 1024); + + return totalMB; +} + + +static void calc_voxel_transp(SmokeModifierData *smd, int *pixel, float *tRay) +{ + // printf("Pixel(%d, %d, %d)\n", pixel[0], pixel[1], pixel[2]); + const size_t index = smoke_get_index(pixel[0], smd->domain->res[0], pixel[1], smd->domain->res[1], pixel[2]); + + // T_ray *= T_vox + *tRay *= smoke_get_tvox(smd, index); +} + +static void calc_voxel_transp_big(SmokeModifierData *smd, int *pixel, float *tRay) +{ + int bigres[3]; + size_t index; + + smoke_get_bigres(smd->domain->fluid, bigres); + index = smoke_get_index(pixel[0], bigres[0], pixel[1], bigres[1], pixel[2]); + + /* + if(index > bigres[0]*bigres[1]*bigres[2]) + printf("pixel[0]: %d, [1]: %d, [2]: %d\n", pixel[0], pixel[1], pixel[2]); + */ + + // T_ray *= T_vox + *tRay *= smoke_get_bigtvox(smd, index); +} + +static void bresenham_linie_3D(SmokeModifierData *smd, int x1, int y1, int z1, int x2, int y2, int z2, float *tRay, int big) +{ + int dx, dy, dz, i, l, m, n, x_inc, y_inc, z_inc, err_1, err_2, dx2, dy2, dz2; + int pixel[3]; + + pixel[0] = x1; + pixel[1] = y1; + pixel[2] = z1; + + dx = x2 - x1; + dy = y2 - y1; + dz = z2 - z1; + + x_inc = (dx < 0) ? -1 : 1; + l = abs(dx); + y_inc = (dy < 0) ? -1 : 1; + m = abs(dy); + z_inc = (dz < 0) ? -1 : 1; + n = abs(dz); + dx2 = l << 1; + dy2 = m << 1; + dz2 = n << 1; + + if ((l >= m) && (l >= n)) { + err_1 = dy2 - l; + err_2 = dz2 - l; + for (i = 0; i < l; i++) { + if(!big) + calc_voxel_transp(smd, pixel, tRay); + else + calc_voxel_transp_big(smd, pixel, tRay); + if(*tRay < 0.0f) + return; + if (err_1 > 0) { + pixel[1] += y_inc; + err_1 -= dx2; + } + if (err_2 > 0) { + pixel[2] += z_inc; + err_2 -= dx2; + } + err_1 += dy2; + err_2 += dz2; + pixel[0] += x_inc; + } + } else if ((m >= l) && (m >= n)) { + err_1 = dx2 - m; + err_2 = dz2 - m; + for (i = 0; i < m; i++) { + if(!big) + calc_voxel_transp(smd, pixel, tRay); + else + calc_voxel_transp_big(smd, pixel, tRay); + if(*tRay < 0.0f) + return; + if (err_1 > 0) { + pixel[0] += x_inc; + err_1 -= dy2; + } + if (err_2 > 0) { + pixel[2] += z_inc; + err_2 -= dy2; + } + err_1 += dx2; + err_2 += dz2; + pixel[1] += y_inc; + } + } else { + err_1 = dy2 - n; + err_2 = dx2 - n; + for (i = 0; i < n; i++) { + if(!big) + calc_voxel_transp(smd, pixel, tRay); + else + calc_voxel_transp_big(smd, pixel, tRay); + if(*tRay < 0.0f) + return; + if (err_1 > 0) { + pixel[1] += y_inc; + err_1 -= dz2; + } + if (err_2 > 0) { + pixel[0] += x_inc; + err_2 -= dz2; + } + err_1 += dy2; + err_2 += dx2; + pixel[2] += z_inc; + } + } + if(!big) + calc_voxel_transp(smd, pixel, tRay); + else + calc_voxel_transp_big(smd, pixel, tRay); +} + +static void get_cell(struct SmokeModifierData *smd, float *pos, int *cell, int correct) +{ + float tmp[3]; + + VECSUB(tmp, pos, smd->domain->p0); + VecMulf(tmp, 1.0 / smd->domain->dx); + + if(correct) + { + cell[0] = MIN2(smd->domain->res[0] - 1, MAX2(0, (int)(tmp[0] + 0.5))); + cell[1] = MIN2(smd->domain->res[1] - 1, MAX2(0, (int)(tmp[1] + 0.5))); + cell[2] = MIN2(smd->domain->res[2] - 1, MAX2(0, (int)(tmp[2] + 0.5))); + } + else + { + cell[0] = (int)(tmp[0] + 0.5); + cell[1] = (int)(tmp[1] + 0.5); + cell[2] = (int)(tmp[2] + 0.5); + } +} +static void get_bigcell(struct SmokeModifierData *smd, float *pos, int *cell, int correct) +{ + float tmp[3]; + int res[3]; + smoke_get_bigres(smd->domain->fluid, res); + + VECSUB(tmp, pos, smd->domain->p0); + + VecMulf(tmp, smd->domain->amplify / smd->domain->dx ); + + if(correct) + { + cell[0] = MIN2(res[0] - 1, MAX2(0, (int)(tmp[0] + 0.5))); + cell[1] = MIN2(res[1] - 1, MAX2(0, (int)(tmp[1] + 0.5))); + cell[2] = MIN2(res[2] - 1, MAX2(0, (int)(tmp[2] + 0.5))); + } + else + { + cell[0] = (int)(tmp[0] + 0.5); + cell[1] = (int)(tmp[1] + 0.5); + cell[2] = (int)(tmp[2] + 0.5); + } +} + + +void smoke_calc_transparency(struct SmokeModifierData *smd, float *light, int big) +{ + int x, y, z; + float bv[6]; + int res[3]; + float bigfactor = 1.0; + + // x + bv[0] = smd->domain->p0[0]; + bv[1] = smd->domain->p1[0]; + // y + bv[2] = smd->domain->p0[1]; + bv[3] = smd->domain->p1[1]; + // z + bv[4] = smd->domain->p0[2]; + bv[5] = smd->domain->p1[2]; +/* + printf("bv[0]: %f, [1]: %f, [2]: %f, [3]: %f, [4]: %f, [5]: %f\n", bv[0], bv[1], bv[2], bv[3], bv[4], bv[5]); + + printf("p0[0]: %f, p0[1]: %f, p0[2]: %f\n", smd->domain->p0[0], smd->domain->p0[1], smd->domain->p0[2]); + printf("p1[0]: %f, p1[1]: %f, p1[2]: %f\n", smd->domain->p1[0], smd->domain->p1[1], smd->domain->p1[2]); + printf("dx: %f, amp: %d\n", smd->domain->dx, smd->domain->amplify); +*/ + if(!big) + { + res[0] = smd->domain->res[0]; + res[1] = smd->domain->res[1]; + res[2] = smd->domain->res[2]; + } + else + { + smoke_get_bigres(smd->domain->fluid, res); + bigfactor = 1.0 / smd->domain->amplify; + } + +#pragma omp parallel for schedule(static) private(y, z) shared(big, smd, light, res, bigfactor) + for(x = 0; x < res[0]; x++) + for(y = 0; y < res[1]; y++) + for(z = 0; z < res[2]; z++) + { + float voxelCenter[3]; + size_t index; + float pos[3]; + int cell[3]; + float tRay = 1.0; + + index = smoke_get_index(x, res[0], y, res[1], z); + + // voxelCenter = m_voxelarray[i].GetCenter(); + voxelCenter[0] = smd->domain->p0[0] + smd->domain->dx * bigfactor * x + smd->domain->dx * bigfactor * 0.5; + voxelCenter[1] = smd->domain->p0[1] + smd->domain->dx * bigfactor * y + smd->domain->dx * bigfactor * 0.5; + voxelCenter[2] = smd->domain->p0[2] + smd->domain->dx * bigfactor * z + smd->domain->dx * bigfactor * 0.5; + + // printf("vc[0]: %f, vc[1]: %f, vc[2]: %f\n", voxelCenter[0], voxelCenter[1], voxelCenter[2]); + // printf("light[0]: %f, light[1]: %f, light[2]: %f\n", light[0], light[1], light[2]); + + // get starting position (in voxel coords) + if(BLI_bvhtree_bb_raycast(bv, light, voxelCenter, pos) > FLT_EPSILON) + { + // we're ouside + // printf("out: pos[0]: %f, pos[1]: %f, pos[2]: %f\n", pos[0], pos[1], pos[2]); + if(!big) + get_cell(smd, pos, cell, 1); + else + get_bigcell(smd, pos, cell, 1); + } + else + { + // printf("in: pos[0]: %f, pos[1]: %f, pos[2]: %f\n", light[0], light[1], light[2]); + // we're inside + if(!big) + get_cell(smd, light, cell, 1); + else + get_bigcell(smd, light, cell, 1); + } + + // printf("cell - [0]: %d, [1]: %d, [2]: %d\n", cell[0], cell[1], cell[2]); + bresenham_linie_3D(smd, cell[0], cell[1], cell[2], x, y, z, &tRay, big); + + if(!big) + smoke_set_tray(smd, index, tRay); + else + smoke_set_bigtray(smd, index, tRay); + } +} + diff --git a/source/blender/blenkernel/intern/softbody.c b/source/blender/blenkernel/intern/softbody.c index bc6b487080c..fe63585ae1c 100644 --- a/source/blender/blenkernel/intern/softbody.c +++ b/source/blender/blenkernel/intern/softbody.c @@ -82,7 +82,7 @@ variables on the UI for now #include "BKE_DerivedMesh.h" #include "BKE_pointcache.h" #include "BKE_modifier.h" - +#include "BKE_deform.h" //XXX #include "BIF_editdeform.h" //XXX #include "BIF_graphics.h" #include "PIL_time.h" @@ -2051,7 +2051,7 @@ static void sb_spring_force(Object *ob,int bpi,BodySpring *bs,float iks,float fo BodyPoint *bp1,*bp2; float dir[3],dvel[3]; - float distance,forcefactor,kd,absvel,projvel; + float distance,forcefactor,kd,absvel,projvel,kw; int ia,ic; /* prepare depending on which side of the spring we are on */ if (bpi == bs->v1){ @@ -2085,7 +2085,10 @@ static void sb_spring_force(Object *ob,int bpi,BodySpring *bs,float iks,float fo forcefactor = iks/bs->len; else forcefactor = iks; - forcefactor *= bs->strength; + kw = (bp1->springweight+bp2->springweight)/2.0f; + kw = kw * kw; + kw = kw * kw; + forcefactor *= bs->strength * kw; Vec3PlusStVec(bp1->force,(bs->len - distance)*forcefactor,dir); /* do bp1 <--> bp2 viscous */ @@ -2185,14 +2188,14 @@ static int _softbody_calc_forces_slice_in_a_thread(Scene *scene, Object *ob, flo VecMidf(velcenter, bp->vec, obp->vec); VecSubf(dvel,velcenter,bp->vec); - VecMulf(dvel,sb->nodemass); + VecMulf(dvel,bp->mass); Vec3PlusStVec(bp->force,f*(1.0f-sb->balldamp),def); Vec3PlusStVec(bp->force,sb->balldamp,dvel); /* exploit force(a,b) == -force(b,a) part2/2 */ VecSubf(dvel,velcenter,obp->vec); - VecMulf(dvel,sb->nodemass); + VecMulf(dvel,bp->mass); Vec3PlusStVec(obp->force,sb->balldamp,dvel); Vec3PlusStVec(obp->force,-f*(1.0f-sb->balldamp),def); @@ -2237,7 +2240,7 @@ static int _softbody_calc_forces_slice_in_a_thread(Scene *scene, Object *ob, flo /* gravitation */ if (sb){ float gravity = sb->grav * sb_grav_force_scale(ob); - bp->force[2]-= gravity*sb->nodemass; /* individual mass of node here */ + bp->force[2]-= gravity*bp->mass; /* individual mass of node here */ } /* particle field & vortex */ @@ -2549,7 +2552,7 @@ static void softbody_calc_forces(Scene *scene, Object *ob, float forcetime, floa VecMidf(velcenter, bp->vec, obp->vec); VecSubf(dvel,velcenter,bp->vec); - VecMulf(dvel,sb->nodemass); + VecMulf(dvel,bp->mass); Vec3PlusStVec(bp->force,f*(1.0f-sb->balldamp),def); Vec3PlusStVec(bp->force,sb->balldamp,dvel); @@ -2580,7 +2583,7 @@ static void softbody_calc_forces(Scene *scene, Object *ob, float forcetime, floa /* exploit force(a,b) == -force(b,a) part2/2 */ VecSubf(dvel,velcenter,obp->vec); - VecMulf(dvel,sb->nodemass); + VecMulf(dvel,(bp->mass+obp->mass)/2.0f); Vec3PlusStVec(obp->force,sb->balldamp,dvel); Vec3PlusStVec(obp->force,-f*(1.0f-sb->balldamp),def); @@ -2640,8 +2643,7 @@ static void softbody_calc_forces(Scene *scene, Object *ob, float forcetime, floa /* gravitation */ - bp->force[2]-= gravity*sb->nodemass; /* individual mass of node here */ - //bp->force[1]-= gravity*sb->nodemass; /* individual mass of node here */ + bp->force[2]-= gravity*bp->mass; /* individual mass of node here */ /* particle field & vortex */ @@ -2850,11 +2852,20 @@ static void softbody_apply_forces(Object *ob, float forcetime, int mode, float * aabbmin[0]=aabbmin[1]=aabbmin[2] = 1e20f; aabbmax[0]=aabbmax[1]=aabbmax[2] = -1e20f; + /* old one with homogenous masses */ /* claim a minimum mass for vertex */ + /* if (sb->nodemass > 0.009999f) timeovermass = forcetime/sb->nodemass; else timeovermass = forcetime/0.009999f; + */ for(a=sb->totpoint, bp= sb->bpoint; a>0; a--, bp++) { +/* now we have individual masses */ +/* claim a minimum mass for vertex */ + if (bp->mass > 0.009999f) timeovermass = forcetime/bp->mass; + else timeovermass = forcetime/0.009999f; + + if(bp->goal < SOFTGOALSNAP){ /* this makes t~ = t */ if(mid_flags & MID_PRESERVE) VECCOPY(dx,bp->vec); @@ -3228,10 +3239,36 @@ static void mesh_to_softbody(Scene *scene, Object *ob) /* to proove the concept this would enable per vertex *mass painting* - strcpy(name,"SOFTMASS"); - error = get_scalar_from_named_vertexgroup(ob,name, a,&temp); - if (!error) bp->mass = temp * ob->rangeofmass; */ + /* first set the default */ + bp->mass = sb->nodemass; + + if (sb->namedVG_Mass[0]) + { + int grp= get_named_vertexgroup_num (ob,sb->namedVG_Mass); + /* printf("VGN %s %d \n",sb->namedVG_Mass,grp); */ + if(grp > -1){ + get_scalar_from_vertexgroup(ob, a,(short) (grp), &bp->mass); + bp->mass = bp->mass * sb->nodemass; + /* printf("bp->mass %f \n",bp->mass); */ + + } + } + /* first set the default */ + bp->springweight = 1.0f; + + if (sb->namedVG_Spring_K[0]) + { + int grp= get_named_vertexgroup_num (ob,sb->namedVG_Spring_K); + //printf("VGN %s %d \n",sb->namedVG_Spring_K,grp); + if(grp > -1){ + get_scalar_from_vertexgroup(ob, a,(short) (grp), &bp->springweight); + //printf("bp->springweight %f \n",bp->springweight); + + } + } + + } /* but we only optionally add body edge springs */ diff --git a/source/blender/blenkernel/intern/texture.c b/source/blender/blenkernel/intern/texture.c index 66f7fe8a44b..eeffbfe5ef6 100644 --- a/source/blender/blenkernel/intern/texture.c +++ b/source/blender/blenkernel/intern/texture.c @@ -435,12 +435,15 @@ void default_tex(Tex *tex) VarStruct *varstr; int a; + tex->type= TEX_CLOUDS; tex->stype= 0; tex->flag= TEX_CHECKER_ODD; - tex->imaflag= TEX_INTERPOL+TEX_MIPMAP+TEX_USEALPHA; + tex->imaflag= TEX_INTERPOL|TEX_MIPMAP|TEX_USEALPHA; tex->extend= TEX_REPEAT; tex->cropxmin= tex->cropymin= 0.0; tex->cropxmax= tex->cropymax= 1.0; + tex->texfilter = TXF_EWA; + tex->afmax = 8; tex->xrepeat= tex->yrepeat= 1; tex->fie_ima= 2; tex->sfra= 1; @@ -531,7 +534,7 @@ void default_mtex(MTex *mtex) mtex->size[1]= 1.0; mtex->size[2]= 1.0; mtex->tex= 0; - mtex->texflag= 0; + mtex->texflag= MTEX_NEW_BUMP; mtex->colormodel= 0; mtex->r= 1.0; mtex->g= 0.0; @@ -540,7 +543,7 @@ void default_mtex(MTex *mtex) mtex->def_var= 1.0; mtex->blendtype= MTEX_BLEND; mtex->colfac= 1.0; - mtex->norfac= 0.5; + mtex->norfac= 1.0; mtex->varfac= 1.0; mtex->dispfac=0.2; mtex->normapspace= MTEX_NSPACE_TANGENT; @@ -784,7 +787,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/world.c b/source/blender/blenkernel/intern/world.c index 7fe129ed6fc..f795c147f54 100644 --- a/source/blender/blenkernel/intern/world.c +++ b/source/blender/blenkernel/intern/world.c @@ -36,7 +36,6 @@ #include "DNA_world_types.h" #include "DNA_texture_types.h" -#include "DNA_scriptlink_types.h" #include "DNA_scene_types.h" #include "DNA_object_types.h" #include "DNA_camera_types.h" @@ -66,10 +65,7 @@ void free_world(World *wrld) { MTex *mtex; int a; - -#ifndef DISABLE_PYTHON - BPY_free_scriptlink(&wrld->scriptlink); -#endif + for(a=0; a<MAX_MTEX; a++) { mtex= wrld->mtex[a]; if(mtex && mtex->tex) mtex->tex->id.us--; @@ -94,7 +90,6 @@ World *add_world(char *name) wrld->skytype= WO_SKYBLEND; wrld->stardist= 15.0f; wrld->starsize= 2.0f; - wrld->gravity= 9.8f; wrld->exp= 0.0f; wrld->exposure=wrld->range= 1.0f; @@ -106,14 +101,7 @@ World *add_world(char *name) wrld->ao_samp_method = WO_AOSAMP_HAMMERSLEY; wrld->ao_approx_error= 0.25f; - wrld->physicsEngine= WOPHY_BULLET;//WOPHY_SUMO; Bullet by default - wrld->mode = WO_DBVT_CULLING; // DBVT culling by default - wrld->occlusionRes = 128; wrld->preview = NULL; - wrld->ticrate = 60; - wrld->maxlogicstep = 5; - wrld->physubstep = 1; - wrld->maxphystep = 5; return wrld; } @@ -134,9 +122,6 @@ World *copy_world(World *wrld) } if (wrld->preview) wrldn->preview = BKE_previewimg_copy(wrld->preview); -#ifndef DISABLE_PYTHON - BPY_copy_scriptlink(&wrld->scriptlink); -#endif #if 0 // XXX old animation system id_us_plus((ID *)wrldn->ipo); 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 diff --git a/source/blender/blenkernel/nla_private.h b/source/blender/blenkernel/nla_private.h new file mode 100644 index 00000000000..325798f325f --- /dev/null +++ b/source/blender/blenkernel/nla_private.h @@ -0,0 +1,85 @@ +/** + * $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 ***** + */ + +#ifndef NLA_PRIVATE +#define NLA_PRIVATE + +/* --------------- NLA Evaluation DataTypes ----------------------- */ + +/* 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 */ + + short track_index; /* the index of the track within the list */ + short strip_mode; /* which end of the strip are we looking at */ + + float strip_time; /* time at which which strip is being evaluated */ +} NlaEvalStrip; + +/* NlaEvalStrip->strip_mode */ +enum { + /* standard evaluation */ + NES_TIME_BEFORE = -1, + NES_TIME_WITHIN, + NES_TIME_AFTER, + + /* transition-strip evaluations */ + NES_TIME_TRANSITION_START, + NES_TIME_TRANSITION_END, +} eNlaEvalStrip_StripMode; + + +/* 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; + + PointerRNA ptr; /* pointer to struct containing property to use */ + PropertyRNA *prop; /* RNA-property type to use (should be in the struct given) */ + int index; /* array index (where applicable) */ + + float value; /* value of this channel */ +} NlaEvalChannel; + +/* --------------- NLA Functions (not to be used as a proper API) ----------------------- */ + +/* convert from strip time <-> global time */ +float nlastrip_get_frame(NlaStrip *strip, float cframe, short mode); + +/* --------------- NLA Evaluation (very-private stuff) ----------------------- */ +/* these functions are only defined here to avoid problems with the order in which they get defined... */ + +NlaEvalStrip *nlastrips_ctime_get_strip(ListBase *list, ListBase *strips, short index, float ctime); +void nlastrip_evaluate(PointerRNA *ptr, ListBase *channels, ListBase *modifiers, NlaEvalStrip *nes); +void nladata_flush_channels(ListBase *channels); + +#endif // NLA_PRIVATE |