diff options
Diffstat (limited to 'source/blender/blenkernel')
68 files changed, 16593 insertions, 3618 deletions
diff --git a/source/blender/blenkernel/BKE_DerivedMesh.h b/source/blender/blenkernel/BKE_DerivedMesh.h index 872717fdb9b..29fc1438c47 100644 --- a/source/blender/blenkernel/BKE_DerivedMesh.h +++ b/source/blender/blenkernel/BKE_DerivedMesh.h @@ -57,6 +57,7 @@ struct Mesh; struct EditMesh; struct ModifierData; struct MCol; +struct ColorBand; /* number of sub-elements each mesh element has (for interpolation) */ #define SUB_ELEMS_VERT 0 @@ -69,6 +70,7 @@ struct DerivedMesh { CustomData vertData, edgeData, faceData; int numVertData, numEdgeData, numFaceData; int needsFree; /* checked on ->release, is set to 0 for cached results */ + int deformedOnly; /* set by modifier stack if only deformed from original */ /* Misc. Queries */ @@ -395,6 +397,9 @@ void DM_interp_face_data(struct DerivedMesh *source, struct DerivedMesh *dest, void DM_swap_face_data(struct DerivedMesh *dm, int index, int *corner_indices); +/* Temporary? A function to give a colorband to derivedmesh for vertexcolor ranges */ +void vDM_ColorBand_store(struct ColorBand *coba); + /* Simple function to get me->totvert amount of vertices/normals, correctly deformed and subsurfered. Needed especially when vertexgroups are involved. In use now by vertex/weigt paint and particles */ @@ -425,10 +430,33 @@ DerivedMesh *editmesh_get_derived_cage(CustomDataMask dataMask); DerivedMesh *editmesh_get_derived_cage_and_final(DerivedMesh **final_r, CustomDataMask dataMask); +/* returns an array of deform matrices for crazyspace correction, and the + number of modifiers left */ +int editmesh_get_first_deform_matrices(float (**deformmats)[3][3], + float (**deformcos)[3]); + void weight_to_rgb(float input, float *fr, float *fg, float *fb); /* determines required DerivedMesh data according to view and edit modes */ CustomDataMask get_viewedit_datamask(); +/* repeate this pattern + X000X000 + 00000000 + 00X000X0 + 00000000 */ + +#define DM_FACE_STIPPLE \ +{ \ + 136,136,136,136,0,0,0,0,34,34,34,34,0,0,0,0, \ + 136,136,136,136,0,0,0,0,34,34,34,34,0,0,0,0, \ + 136,136,136,136,0,0,0,0,34,34,34,34,0,0,0,0, \ + 136,136,136,136,0,0,0,0,34,34,34,34,0,0,0,0, \ + 136,136,136,136,0,0,0,0,34,34,34,34,0,0,0,0, \ + 136,136,136,136,0,0,0,0,34,34,34,34,0,0,0,0, \ + 136,136,136,136,0,0,0,0,34,34,34,34,0,0,0,0, \ + 136,136,136,136,0,0,0,0,34,34,34,34,0,0,0,0 \ +} + #endif diff --git a/source/blender/blenkernel/BKE_action.h b/source/blender/blenkernel/BKE_action.h index e848ef0a11b..cbec4cf04fb 100644 --- a/source/blender/blenkernel/BKE_action.h +++ b/source/blender/blenkernel/BKE_action.h @@ -101,6 +101,9 @@ struct bPoseChannel *verify_pose_channel(struct bPose* pose, /* sets constraint flags */ void update_pose_constraint_flags(struct bPose *pose); +/* clears BONE_UNKEYED flags for frame changing */ +void framechange_poses_clear_unkeyed(void); + /** * Allocate a new bAction on the heap and copy * the contents of src into it. If src is NULL NULL is returned. @@ -116,8 +119,12 @@ void calc_action_range(const struct bAction *act, float *start, float *end, int /** * Set the pose channels from the given action. */ -void extract_pose_from_action(struct bPose *pose, struct bAction *act, - float ctime); +void extract_pose_from_action(struct bPose *pose, struct bAction *act, float ctime); + +/** + * Get the effects of the given action using a workob + */ +void what_does_obaction(struct Object *ob, struct bAction *act, float cframe); /** * Iterate through the action channels of the action @@ -153,11 +160,5 @@ float get_action_frame_inv(struct Object *ob, float cframe); }; #endif -/* nla strip->mode, for action blending */ -enum { - POSE_BLEND = 0, - POSE_ADD -}; - #endif diff --git a/source/blender/blenkernel/BKE_armature.h b/source/blender/blenkernel/BKE_armature.h index 37022d89ac1..fb527051a0d 100644 --- a/source/blender/blenkernel/BKE_armature.h +++ b/source/blender/blenkernel/BKE_armature.h @@ -80,7 +80,7 @@ void make_local_armature(struct bArmature *arm); struct bArmature *copy_armature(struct bArmature *arm); void bone_flip_name (char *name, int strip_number); -struct bArmature* get_armature (struct Object* ob); +struct bArmature *get_armature (struct Object *ob); struct Bone *get_named_bone (struct bArmature *arm, const char *name); float distfactor_to_bone (float vec[3], float b1[3], float b2[3], float rad1, float rad2, float rdist); @@ -112,7 +112,7 @@ typedef struct Mat4 { float mat[4][4]; } Mat4; -Mat4 *b_bone_spline_setup(struct bPoseChannel *pchan); +Mat4 *b_bone_spline_setup(struct bPoseChannel *pchan, int rest); #ifdef __cplusplus } diff --git a/source/blender/blenkernel/BKE_bad_level_calls.h b/source/blender/blenkernel/BKE_bad_level_calls.h index ddc18851a9f..7a838ff7614 100644 --- a/source/blender/blenkernel/BKE_bad_level_calls.h +++ b/source/blender/blenkernel/BKE_bad_level_calls.h @@ -63,6 +63,9 @@ struct Script; struct Text; struct IpoDriver; /* DNA_curve_types.h */ struct Object; +struct bPythonConstraint; +struct bConstraintOb; +struct bConstraintTarget; void BPY_do_pyscript (struct ID *id, short int event); void BPY_clear_script (struct Script *script); void BPY_free_compiled_text (struct Text *text); @@ -72,6 +75,10 @@ float BPY_pydriver_eval(struct IpoDriver *driver); void BPY_pydriver_update(void); /* button python evaluation */ int BPY_button_eval(char *expr, double *value); +/* pyconstraints */ +void BPY_pyconstraint_eval(struct bPythonConstraint *con, struct bConstraintOb *cob, struct ListBase *targets); +void BPY_pyconstraint_targets(struct bPythonConstraint *con, struct bConstraintTarget *ct); + /* writefile.c */ struct Oops; @@ -179,6 +186,9 @@ void post_tag_change(struct VTag *vtag); void post_taggroup_create(struct VTagGroup *vtaggroup); char *verse_client_name(void); void post_transform(struct VNode *vnode); +void post_transform_pos(struct VNode *vnode); +void post_transform_rot(struct VNode *vnode); +void post_transform_scale(struct VNode *vnode); void post_object_free_constraint(struct VNode *vnode); void post_link_set(struct VLink *vlink); void post_link_destroy(struct VLink *vlink); @@ -215,5 +225,18 @@ void antialias_tagbuf(int xsize, int ysize, char *rectmove); /* imagetexture.c */ void ibuf_sample(struct ImBuf *ibuf, float fx, float fy, float dx, float dy, float *result); +/* modifier.c */ +struct MeshDeformModifierData; + +void harmonic_coordinates_bind(struct MeshDeformModifierData *mmd, + float (*vertexcos)[3], int totvert, float cagemat[][4]); + +/* particle.c */ +struct ParticleSystem; + +void PE_free_particle_edit(struct ParticleSystem *psys); +void PE_get_colors(char sel[4], char nosel[4]); +void PE_recalc_world_cos(struct Object *ob, struct ParticleSystem *psys); + #endif diff --git a/source/blender/blenkernel/BKE_blender.h b/source/blender/blenkernel/BKE_blender.h index 4b9e10651cf..5cd905d07ac 100644 --- a/source/blender/blenkernel/BKE_blender.h +++ b/source/blender/blenkernel/BKE_blender.h @@ -43,8 +43,8 @@ extern "C" { struct ListBase; struct MemFile; -#define BLENDER_VERSION 244 -#define BLENDER_SUBVERSION 2 +#define BLENDER_VERSION 245 +#define BLENDER_SUBVERSION 8 #define BLENDER_MINVERSION 240 #define BLENDER_MINSUBVERSION 0 diff --git a/source/blender/blenkernel/BKE_constraint.h b/source/blender/blenkernel/BKE_constraint.h index 2bec643d5b0..aa8e9cf18f8 100644 --- a/source/blender/blenkernel/BKE_constraint.h +++ b/source/blender/blenkernel/BKE_constraint.h @@ -25,7 +25,7 @@ * * The Original Code is: all of this file. * - * Contributor(s): none yet. + * Contributor(s): 2007 - Joshua Leung (major recode) * * ***** END GPL/BL DUAL LICENSE BLOCK ***** */ @@ -34,42 +34,107 @@ #define BKE_CONSTRAINT_H struct bConstraint; -struct Object; +struct bConstraintTarget; struct ListBase; +struct Object; struct bConstraintChannel; -struct bAction; -struct bArmature; - -/* Function prototypes */ -void unique_constraint_name (struct bConstraint *con, struct ListBase *list); -void *new_constraint_data (short type); -void evaluate_constraint (struct bConstraint *constraint, struct Object *ob, short ownertype, void *ownerdata, float targetmat[][4]); -void free_constraints (struct ListBase *conlist); -void copy_constraints (struct ListBase *dst, struct ListBase *src); -void copy_constraint_channels (ListBase *dst, ListBase *src); -void clone_constraint_channels (struct ListBase *dst, struct ListBase *src); -void relink_constraints (struct ListBase *list); -void free_constraint_data (struct bConstraint *con); - -/* channels */ -struct bConstraintChannel *get_constraint_channel (ListBase *list, const char *name); -struct bConstraintChannel *verify_constraint_channel (ListBase *list, const char *name); -void free_constraint_channels (ListBase *chanbase); - -/* Gemeric functions */ -void do_constraint_channels (struct ListBase *conbase, struct ListBase *chanbase, float ctime); -short get_constraint_target_matrix (struct bConstraint *con, short ownertype, void *ownerdata, float mat[][4], float size[3], float time); -char constraint_has_target (struct bConstraint *con); -struct Object *get_constraint_target(struct bConstraint *con, char **subtarget); -void set_constraint_target(struct bConstraint *con, struct Object *ob, char *subtarget); - - -/* Constraint target/owner types */ -#define TARGET_OBJECT 1 // string is "" -#define TARGET_BONE 2 // string is bone-name -#define TARGET_VERT 3 // string is "VE:#" -#define TARGET_FACE 4 // string is "FA:#" -#define TARGET_CV 5 // string is "CV:#" +struct bPoseChannel; + +/* ---------------------------------------------------------------------------- */ + +/* special struct for use in constraint evaluation */ +typedef struct bConstraintOb { + struct Object *ob; /* if pchan, then armature that it comes from, otherwise constraint owner */ + struct bPoseChannel *pchan; /* pose channel that owns the constraints being evaluated */ + + float matrix[4][4]; /* matrix where constraints are accumulated + solved */ + float startmat[4][4]; /* original matrix (before constraint solving) */ + + short type; /* type of owner */ +} bConstraintOb; + +/* ---------------------------------------------------------------------------- */ + +/* Constraint Type-Info (shorthand in code = cti): + * This struct provides function pointers for runtime, so that functions can be + * written more generally (with fewer/no special exceptions for various constraints). + * + * Callers of these functions must check that they actually point to something useful, + * as some constraints don't define some of these. + * + * Warning: it is not too advisable to reorder order of members of this struct, + * as you'll have to edit quite a few ($NUM_CONSTRAINT_TYPES) of these + * structs. + */ +typedef struct bConstraintTypeInfo { + /* admin/ident */ + short type; /* CONSTRAINT_TYPE_### */ + short size; /* size in bytes of the struct */ + char name[32]; /* name constraint in interface */ + char structName[32]; /* name of struct for SDNA */ + + /* data management function pointers - special handling */ + /* free any data that is allocated separately (optional) */ + void (*free_data)(struct bConstraint *con); + /* adjust pointer to other ID-data using ID_NEW(), but not to targets (optional) */ + void (*relink_data)(struct bConstraint *con); + /* copy any special data that is allocated separately (optional) */ + void (*copy_data)(struct bConstraint *con, struct bConstraint *src); + /* set settings for data that will be used for bConstraint.data (memory already allocated) */ + void (*new_data)(void *cdata); + + /* target handling function pointers */ + /* for multi-target constraints: return that list; otherwise make a temporary list */ + void (*get_constraint_targets)(struct bConstraint *con, struct ListBase *list); + /* for single-target constraints only: flush data back to source data, and the free memory used */ + void (*flush_constraint_targets)(struct bConstraint *con, struct ListBase *list, short nocopy); + + /* evaluation */ + /* set the ct->matrix for the given constraint target (at the given ctime) */ + void (*get_target_matrix)(struct bConstraint *con, struct bConstraintOb *cob, struct bConstraintTarget *ct, float ctime); + /* evaluate the constraint for the given time */ + void (*evaluate_constraint)(struct bConstraint *con, struct bConstraintOb *cob, struct ListBase *targets); +} bConstraintTypeInfo; + +/* Function Prototypes for bConstraintTypeInfo's */ +bConstraintTypeInfo *constraint_get_typeinfo(struct bConstraint *con); +bConstraintTypeInfo *get_constraint_typeinfo(int type); + +/* ---------------------------------------------------------------------------- */ +/* Useful macros for testing various common flag combinations */ + +/* Constraint Target Macros */ +#define VALID_CONS_TARGET(ct) ((ct) && (ct->tar)) + + +/* ---------------------------------------------------------------------------- */ + +/* Constraint function prototypes */ +void unique_constraint_name(struct bConstraint *con, struct ListBase *list); + +void free_constraints(struct ListBase *conlist); +void copy_constraints(struct ListBase *dst, struct ListBase *src); +void relink_constraints(struct ListBase *list); +void free_constraint_data(struct bConstraint *con); + + +/* Constraint Channel function prototypes */ +struct bConstraintChannel *get_constraint_channel(struct ListBase *list, const char *name); +struct bConstraintChannel *verify_constraint_channel(struct ListBase *list, const char *name); +void do_constraint_channels(struct ListBase *conbase, struct ListBase *chanbase, float ctime, short onlydrivers); +void copy_constraint_channels(struct ListBase *dst, struct ListBase *src); +void clone_constraint_channels(struct ListBase *dst, struct ListBase *src); +void free_constraint_channels(struct ListBase *chanbase); + +/* Constraint Evaluation function prototypes */ +struct bConstraintOb *constraints_make_evalob(struct Object *ob, void *subdata, short datatype); +void constraints_clear_evalob(struct bConstraintOb *cob); + +void constraint_mat_convertspace(struct Object *ob, struct bPoseChannel *pchan, float mat[][4], short from, short to); + +void get_constraint_target_matrix(struct bConstraint *con, int n, short ownertype, void *ownerdata, float mat[][4], float ctime); +void solve_constraints(struct ListBase *conlist, struct bConstraintOb *cob, float ctime); + #endif diff --git a/source/blender/blenkernel/BKE_customdata.h b/source/blender/blenkernel/BKE_customdata.h index 4a486e6795f..d0535f1752e 100644 --- a/source/blender/blenkernel/BKE_customdata.h +++ b/source/blender/blenkernel/BKE_customdata.h @@ -185,6 +185,8 @@ int CustomData_get_layer_index(const struct CustomData *data, int type); int CustomData_get_named_layer_index(const struct CustomData *data, int type, char *name); int CustomData_get_active_layer_index(const struct CustomData *data, int type); int CustomData_get_render_layer_index(const struct CustomData *data, int type); +int CustomData_get_active_layer(const struct CustomData *data, int type); +int CustomData_get_render_layer(const struct CustomData *data, int type); /* copies the data from source to the data element at index in the first * layer of type @@ -207,6 +209,10 @@ void *CustomData_set_layer_n(const struct CustomData *data, int type, int n, voi void CustomData_set_layer_active(struct CustomData *data, int type, int n); void CustomData_set_layer_render(struct CustomData *data, int type, int n); +/* same as above but works with an index from CustomData_get_layer_index */ +void CustomData_set_layer_active_index(struct CustomData *data, int type, int n); +void CustomData_set_layer_render_index(struct CustomData *data, int type, int n); + /* adds flag to the layer flags */ void CustomData_set_layer_flag(struct CustomData *data, int type, int flag); diff --git a/source/blender/blenkernel/BKE_deform.h b/source/blender/blenkernel/BKE_deform.h index de0dddabfd3..67e2a8948d5 100644 --- a/source/blender/blenkernel/BKE_deform.h +++ b/source/blender/blenkernel/BKE_deform.h @@ -46,6 +46,7 @@ void copy_defgroups (struct ListBase *lb1, struct ListBase *lb2); struct bDeformGroup *copy_defgroup (struct bDeformGroup *ingroup); struct bDeformGroup *get_named_vertexgroup (Object *ob, char *name); int get_defgroup_num (struct Object *ob, struct bDeformGroup *dg); +int get_named_vertexgroup_num (Object *ob, char *name); void unique_vertexgroup_name (struct bDeformGroup *dg, struct Object *ob); #endif diff --git a/source/blender/blenkernel/BKE_displist.h b/source/blender/blenkernel/BKE_displist.h index 979ed31fb20..2dc4de32132 100644 --- a/source/blender/blenkernel/BKE_displist.h +++ b/source/blender/blenkernel/BKE_displist.h @@ -123,5 +123,7 @@ void filldisplist(struct ListBase *dispbase, struct ListBase *to); void fastshade_free_render(void); +float calc_taper(struct Object *taperobj, int cur, int tot); + #endif diff --git a/source/blender/blenkernel/BKE_global.h b/source/blender/blenkernel/BKE_global.h index 98a0cb99942..e71145e8d79 100644 --- a/source/blender/blenkernel/BKE_global.h +++ b/source/blender/blenkernel/BKE_global.h @@ -175,21 +175,27 @@ typedef struct Global { #define G_DRAWCREASES (1 << 19) #define G_DRAWSEAMS (1 << 20) #define G_HIDDENEDGES (1 << 21) - /* Measurement info Drawing */ #define G_DRAW_EDGELEN (1 << 22) #define G_DRAW_FACEAREA (1 << 23) #define G_DRAW_EDGEANG (1 << 24) #define G_RECORDKEYS (1 << 25) - /*#ifdef WITH_VERSE*/ #define G_VERSE_CONNECTED (1 << 26) #define G_DRAW_VERSE_DEBUG (1 << 27) /*#endif*/ - #define G_DRAWSHARP (1 << 28) /* draw edges with the sharp flag */ #define G_SCULPTMODE (1 << 29) +#define G_PARTICLEEDIT (1 << 30) + +#define G_AUTOMATKEYS (1 << 30) +#define G_HIDDENHANDLES (1 << 31) /* used for curves only */ + +/* macro for testing face select mode + * Texture paint could be removed since selected faces are not used + * however hiding faces is useful */ +#define FACESEL_PAINT_TEST ((G.f&G_FACESELECT) && (G.f & (G_VERTEXPAINT|G_WEIGHTPAINT|G_TEXTUREPAINT))) /* G.fileflags */ @@ -238,9 +244,10 @@ typedef struct Global { #define B_ENDIAN 0 /* G.moving, signals drawing in (3d) window to denote transform */ -#define G_TRANSFORM_OBJ 1 -#define G_TRANSFORM_EDIT 2 -#define G_TRANSFORM_MANIP 4 +#define G_TRANSFORM_OBJ 1 +#define G_TRANSFORM_EDIT 2 +#define G_TRANSFORM_MANIP 4 +#define G_TRANSFORM_PARTICLE 8 /* G.special1 */ diff --git a/source/blender/blenkernel/BKE_icons.h b/source/blender/blenkernel/BKE_icons.h index 315c7dc7a53..737adea78be 100644 --- a/source/blender/blenkernel/BKE_icons.h +++ b/source/blender/blenkernel/BKE_icons.h @@ -1,34 +1,32 @@ /** - * $Id$ - * - * ***** BEGIN GPL/BL DUAL LICENSE BLOCK ***** - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. The Blender - * Foundation also sells licenses for use in proprietary software under - * the Blender License. See http://www.blender.org/BL/ for information - * about this. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - * - * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. - * All rights reserved. - * - * The Original Code is: all of this file. - * - * Contributor(s): none yet. - * - * ***** END GPL/BL DUAL LICENSE BLOCK ***** - */ +* $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) 2006-2007 Blender Foundation. +* All rights reserved. +* +* The Original Code is: all of this file. +* +* Contributor(s): none yet. +* +* ***** END GPL LICENSE BLOCK ***** +* +*/ #ifndef BKE_ICONS_H #define BKE_ICONS_H @@ -44,12 +42,13 @@ struct Icon void *drawinfo; void *obj; short type; - short changed; DrawInfoFreeFP drawinfo_free; }; typedef struct Icon Icon; +struct PreviewImage; + void BKE_icons_init(int first_dyn_id); /* return icon id for library object or create new icon if not found */ @@ -71,5 +70,19 @@ void BKE_icon_changed(int icon_id); /* free all icons */ void BKE_icons_free(); +/* free the preview image */ +void BKE_previewimg_free(struct PreviewImage **prv); + +/* free the preview image belonging to the id */ +void BKE_previewimg_free_id(ID *id); + +/* create a new preview image */ +struct PreviewImage* BKE_previewimg_create() ; + +/* create a copy of the preview image */ +struct PreviewImage* BKE_previewimg_copy(struct PreviewImage *prv); + +/* retrieve existing or create new preview image */ +PreviewImage* BKE_previewimg_get(ID *id); #endif /* BKE_ICONS_H */ diff --git a/source/blender/blenkernel/BKE_idprop.h b/source/blender/blenkernel/BKE_idprop.h index 92b624f80ab..961cca511de 100644 --- a/source/blender/blenkernel/BKE_idprop.h +++ b/source/blender/blenkernel/BKE_idprop.h @@ -137,6 +137,7 @@ void IDP_FreeIterBeforeEnd(void *vself); to create the Group property and attach it to id if it doesn't exist; otherwise the function will return NULL if there's no Group property attached to the ID.*/ struct IDProperty *IDP_GetProperties(struct ID *id, int create_if_needed); +struct IDProperty *IDP_CopyProperty(struct IDProperty *prop); /* Allocate a new ID. diff --git a/source/blender/blenkernel/BKE_image.h b/source/blender/blenkernel/BKE_image.h index 58f96491a1b..b308342ac1e 100644 --- a/source/blender/blenkernel/BKE_image.h +++ b/source/blender/blenkernel/BKE_image.h @@ -46,6 +46,8 @@ struct anim; /* call from library */ void free_image(struct Image *me); +void BKE_stamp_info(struct ImBuf *ibuf); +void BKE_stamp_buf(unsigned char *rect, float *rectf, int width, int height); int BKE_write_ibuf(struct ImBuf *ibuf, char *name, int imtype, int subimtype, int quality); void BKE_makepicstring(char *string, char *base, int frame, int imtype); void BKE_add_image_extension(char *string, int imtype); diff --git a/source/blender/blenkernel/BKE_key.h b/source/blender/blenkernel/BKE_key.h index 94ba1cbd69d..5325b3a8bc1 100644 --- a/source/blender/blenkernel/BKE_key.h +++ b/source/blender/blenkernel/BKE_key.h @@ -59,6 +59,7 @@ int do_ob_key(struct Object *ob); struct Key *ob_get_key(struct Object *ob); struct KeyBlock *ob_get_keyblock(struct Object *ob); +struct KeyBlock *key_get_keyblock(struct Key *key, int index); #endif diff --git a/source/blender/blenkernel/BKE_lattice.h b/source/blender/blenkernel/BKE_lattice.h index 55eb1d27cc0..bf505fa23d7 100644 --- a/source/blender/blenkernel/BKE_lattice.h +++ b/source/blender/blenkernel/BKE_lattice.h @@ -64,8 +64,8 @@ void lattice_deform_verts(struct Object *laOb, struct Object *target, int numVerts, char *vgroup); void armature_deform_verts(struct Object *armOb, struct Object *target, struct DerivedMesh *dm, float (*vertexCos)[3], - int numVerts, int deformflag, - const char *defgrp_name); + float (*defMats)[3][3], int numVerts, int deformflag, + float (*prevCos)[3], const char *defgrp_name); float (*lattice_getVertexCos(struct Object *ob, int *numVerts_r))[3]; void lattice_applyVertexCos(struct Object *ob, float (*vertexCos)[3]); void lattice_calc_modifiers(struct Object *ob); diff --git a/source/blender/blenkernel/BKE_main.h b/source/blender/blenkernel/BKE_main.h index 6e0f2fdb284..2cfa1dc5cc4 100644 --- a/source/blender/blenkernel/BKE_main.h +++ b/source/blender/blenkernel/BKE_main.h @@ -78,6 +78,7 @@ typedef struct Main { ListBase action; ListBase nodetree; ListBase brush; + ListBase particle; } Main; diff --git a/source/blender/blenkernel/BKE_mesh.h b/source/blender/blenkernel/BKE_mesh.h index 4ec62821455..a8e4969ad43 100644 --- a/source/blender/blenkernel/BKE_mesh.h +++ b/source/blender/blenkernel/BKE_mesh.h @@ -90,11 +90,16 @@ void mesh_calc_normals(struct MVert *mverts, int numVerts, struct MFace *mfaces, /* Return a newly MEM_malloc'd array of all the mesh vertex locations * (_numVerts_r_ may be NULL) */ float (*mesh_getVertexCos(struct Mesh *me, int *numVerts_r))[3]; +float (*mesh_getRefKeyCos(struct Mesh *me, int *numVerts_r))[3]; /* map from uv vertex to face (for select linked, stitch, uv suburf) */ -struct UvVertMap; -typedef struct UvVertMap UvVertMap; +/* UvVertMap */ + +typedef struct UvVertMap { + struct UvMapVert **vert; + struct UvMapVert *buf; +} UvVertMap; typedef struct UvMapVert { struct UvMapVert *next; @@ -106,6 +111,14 @@ UvVertMap *make_uv_vert_map(struct MFace *mface, struct MTFace *tface, unsigned UvMapVert *get_uv_map_vert(UvVertMap *vmap, unsigned int v); void free_uv_vert_map(UvVertMap *vmap); + +/* functions for making menu's from customdata layers */ +int mesh_layers_menu_charlen(struct CustomData *data, int type); /* use this to work out how many chars to allocate */ +void mesh_layers_menu_concat(struct CustomData *data, int type, char *str); +int mesh_layers_menu(struct CustomData *data, int type); + + + #ifdef __cplusplus } #endif diff --git a/source/blender/blenkernel/BKE_modifier.h b/source/blender/blenkernel/BKE_modifier.h index ce3f33bd35c..24da941db90 100644 --- a/source/blender/blenkernel/BKE_modifier.h +++ b/source/blender/blenkernel/BKE_modifier.h @@ -59,6 +59,12 @@ typedef enum { eModifierTypeType_Constructive, eModifierTypeType_Nonconstructive, + + /* both deformVerts & applyModifier are valid calls + * used for particles modifier that doesn't actually modify the object + * unless it's a mesh and can be exploded -> curve can also emit particles + */ + eModifierTypeType_DeformOrConstruct } ModifierTypeType; typedef enum { @@ -81,8 +87,8 @@ typedef enum { eModifierTypeFlag_RequiresOriginalData = (1<<5), } ModifierTypeFlag; -typedef void (*ObjectWalkFunc)(void *userData, Object *ob, Object **obpoin); -typedef void (*IDWalkFunc)(void *userData, Object *ob, ID **idpoin); +typedef void (*ObjectWalkFunc)(void *userData, struct Object *ob, struct Object **obpoin); +typedef void (*IDWalkFunc)(void *userData, struct Object *ob, struct ID **idpoin); typedef struct ModifierTypeInfo { /* The user visible name for this modifier */ @@ -125,6 +131,11 @@ typedef struct ModifierTypeInfo { struct EditMesh *editData, struct DerivedMesh *derivedData, float (*vertexCos)[3], int numVerts); + /* Set deform matrix per vertex for crazyspace correction */ + void (*deformMatricesEM)( + struct ModifierData *md, struct Object *ob, + struct EditMesh *editData, struct DerivedMesh *derivedData, + float (*vertexCos)[3], float (*defMats)[3][3], int numVerts); /********************* Non-deform modifier functions *********************/ @@ -257,6 +268,7 @@ void modifier_copyData(struct ModifierData *md, struct ModifierData *ta int modifier_dependsOnTime(struct ModifierData *md); int modifier_supportsMapping(struct ModifierData *md); int modifier_couldBeCage(struct ModifierData *md); +int modifier_isDeformer(struct ModifierData *md); void modifier_setError(struct ModifierData *md, char *format, ...); void modifiers_foreachObjectLink(struct Object *ob, @@ -271,11 +283,14 @@ int modifiers_getCageIndex(struct Object *ob, int *lastPossibleCageIndex_r); int modifiers_isSoftbodyEnabled(struct Object *ob); +int modifiers_isParticleEnabled(struct Object *ob); struct Object *modifiers_isDeformedByArmature(struct Object *ob); struct Object *modifiers_isDeformedByLattice(struct Object *ob); int modifiers_usesArmature(struct Object *ob, struct bArmature *arm); int modifiers_isDeformed(struct Object *ob); +int modifiers_indexInObject(struct Object *ob, struct ModifierData *md); + /* Calculates and returns a linked list of CustomDataMasks indicating the * data required by each modifier in the stack pointed to by md for correct * evaluation, assuming the data indicated by dataMask is required at the diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index 44266a1c854..2ffa1d205da 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -206,6 +206,9 @@ struct ShadeResult; #define SH_NODE_SQUEEZE 117 #define SH_NODE_MATERIAL_EXT 118 #define SH_NODE_INVERT 119 +#define SH_NODE_SEPRGB 120 +#define SH_NODE_COMBRGB 121 +#define SH_NODE_HUE_SAT 122 /* custom defines options for Material node */ @@ -298,6 +301,12 @@ void set_node_shader_lamp_loop(void (*lamp_loop_func)(struct ShadeInput *, str #define CMP_NODE_BRIGHTCONTRAST 249 #define CMP_NODE_GAMMA 250 #define CMP_NODE_INVERT 251 +#define CMP_NODE_NORMALIZE 252 +#define CMP_NODE_CROP 253 + +#define CMP_NODE_GLARE 301 +#define CMP_NODE_TONEMAP 302 +#define CMP_NODE_LENSDIST 303 /* channel toggles */ #define CMP_CHAN_RGB 1 @@ -316,8 +325,9 @@ void set_node_shader_lamp_loop(void (*lamp_loop_func)(struct ShadeInput *, str #define CMP_FILT_SHADOW 6 /* scale node type, in custom1 */ -#define CMP_SCALE_RELATIVE 0 -#define CMP_SCALE_ABSOLUTE 1 +#define CMP_SCALE_RELATIVE 0 +#define CMP_SCALE_ABSOLUTE 1 +#define CMP_SCALE_SCENEPERCENT 2 /* the type definitions array */ diff --git a/source/blender/blenkernel/BKE_object.h b/source/blender/blenkernel/BKE_object.h index 52d529ea32d..6e363515f41 100644 --- a/source/blender/blenkernel/BKE_object.h +++ b/source/blender/blenkernel/BKE_object.h @@ -45,6 +45,7 @@ struct BoundBox; struct View3D; struct SoftBody; struct Group; +struct bAction; void clear_workob(void); void copy_baseflags(void); @@ -63,6 +64,7 @@ int exist_object(struct Object *obtest); void *add_camera(char *name); struct Camera *copy_camera(struct Camera *cam); void make_local_camera(struct Camera *cam); +float dof_camera(struct Object *ob); void *add_lamp(char *name); struct Lamp *copy_lamp(struct Lamp *la); void make_local_lamp(struct Lamp *la); @@ -81,7 +83,7 @@ void set_mblur_offs(float blur); void set_field_offs(float field); void disable_speed_curve(int val); -float bsystem_time(struct Object *ob, struct Object *par, float cfra, float ofs); +float bsystem_time(struct Object *ob, float cfra, float ofs); void object_to_mat3(struct Object *ob, float mat[][3]); void object_to_mat4(struct Object *ob, float mat[][4]); @@ -97,6 +99,7 @@ void where_is_object(struct Object *ob); void where_is_object_simul(struct Object *ob); void what_does_parent(struct Object *ob); + struct BoundBox *unit_boundbox(void); void boundbox_set_from_min_max(struct BoundBox *bb, float min[3], float max[3]); struct BoundBox *object_get_boundbox(struct Object *ob); @@ -104,7 +107,6 @@ void object_boundbox_flag(struct Object *ob, int flag, int set); void minmax_object(struct Object *ob, float *min, float *max); void minmax_object_duplis(struct Object *ob, float *min, float *max); void solve_tracking (struct Object *ob, float targetmat[][4]); -void solve_constraints (struct Object *ob, short obtype, void *obdata, float ctime); void object_handle_update(struct Object *ob); diff --git a/source/blender/blenkernel/BKE_particle.h b/source/blender/blenkernel/BKE_particle.h new file mode 100644 index 00000000000..afed219dbc6 --- /dev/null +++ b/source/blender/blenkernel/BKE_particle.h @@ -0,0 +1,254 @@ +/* BKE_particle.h + * + * + * $Id: BKE_particle.h $ + * + * ***** BEGIN GPL/BL DUAL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. The Blender + * Foundation also sells licenses for use in proprietary software under + * the Blender License. See http://www.blender.org/BL/ for information + * about this. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * The Original Code is Copyright (C) 2007 by Janne Karhu. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): none yet. + * + * ***** END GPL/BL DUAL LICENSE BLOCK ***** + */ + +#ifndef BKE_PARTICLE_H +#define BKE_PARTICLE_H + +#include "DNA_particle_types.h" +#include "DNA_object_types.h" + +struct ParticleSystemModifierData; +struct ParticleSystem; +struct ParticleKey; +struct HairKey; + +struct Main; +struct Group; +struct Object; +struct DerivedMesh; +struct ModifierData; +struct MTFace; +struct MFace; +struct MVert; +struct IpoCurve; +struct LinkNode; +struct KDTree; + +typedef struct ParticleEffectorCache { + struct ParticleEffectorCache *next, *prev; + struct Object *ob; + + /* precalculated variables for guides */ + float firstloc[4], firstdir[3]; + float *distances; + float *locations; + /* precalculated variables for deflection */ + float ob_minmax[6]; + float *face_minmax; + float *vert_cos; + /* precalculated variables for boids */ + struct KDTree *tree; + + short type, psys_nbr; + + struct Object obcopy; /* for restoring transformation data */ +} ParticleEffectorCache; + +typedef struct ParticleReactEvent { + struct ParticleReactEvent *next, *prev; + int event, pa_num; + Object *ob; + struct ParticleSystem *psys; + struct ParticleKey state; + + float time, size; +}ParticleReactEvent; + +typedef struct ParticleTexture{ + float ivel; /* used in reset */ + float time, life, exist, size; /* used in init */ + float pvel[3]; /* used in physics */ + float length, clump, kink; /* 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]; + float length2; +} ParticleSeam; + +typedef struct ParticleCacheKey{ + float co[3]; + float vel[3]; + float rot[4]; + float col[3]; + int steps; +} ParticleCacheKey; + +typedef struct ParticleEditKey{ + float *co; + float *vel; + float *rot; + float *time; + + float world_co[3]; + float length; + short flag; +} ParticleEditKey; + +typedef struct ParticleUndo { + struct ParticleUndo *next, *prev; + struct ParticleEditKey **keys; + struct KDTree *emitter_field; + struct ParticleData *particles; + float *emitter_cosnos; + int totpart, totkeys; + char name[64]; +} ParticleUndo; + +typedef struct ParticleEdit{ + ListBase undo; + struct ParticleUndo *curundo; + struct KDTree *emitter_field; + ParticleEditKey **keys; + int *mirror_cache; + float *emitter_cosnos; + + int totkeys; +} ParticleEdit; + +/* ----------- functions needed outside particlesystem ---------------- */ +/* particle.c */ +int count_particles(struct ParticleSystem *psys); +int count_particles_mod(struct ParticleSystem *psys, int totgr, int cur); +int psys_count_keys(struct ParticleSystem *psys); +char *psys_menu_string(struct Object *ob, int for_sb); + +struct ParticleSystem *psys_get_current(struct Object *ob); +short psys_get_current_num(struct Object *ob); +//struct ParticleSystem *psys_get(struct Object *ob, int index); +struct ParticleData *psys_get_selected_particle(struct ParticleSystem *psys, int *index); +struct ParticleKey *psys_get_selected_key(struct ParticleSystem *psys, int pa_index, int *key_index); +void psys_change_act(void *ob_v, void *act_v); +struct Object *psys_get_lattice(struct Object *ob, struct ParticleSystem *psys); +void psys_disable_all(struct Object *ob); +void psys_enable_all(struct Object *ob); +int psys_ob_has_hair(struct Object *ob); +int psys_in_edit_mode(struct ParticleSystem *psys); + +void psys_free_settings(struct ParticleSettings *part); +void free_child_path_cache(struct ParticleSystem *psys); +void psys_free_path_cache(struct ParticleSystem *psys); +void free_hair(struct ParticleSystem *psys); +void free_keyed_keys(struct ParticleSystem *psys); +void psys_free(struct Object * ob, struct ParticleSystem * psys); + +void clear_particles_from_cache(struct Object *ob, struct ParticleSystem *psys, int cfra); +//void psys_remove_from_particle_list(struct Object *ob, short nbr, struct ParticleSystem *psys); + +void psys_interpolate_uvs(struct MTFace *tface, int quad, float *uv, float *uvco); + +void copy_particle_key(struct ParticleKey *to, struct ParticleKey *from, int time); + +void psys_particle_on_emitter(struct Object *ob, struct ParticleSystemModifierData *psmd, int distr, int index, int index_dmcache, float *fuv, float foffset, float *vec, float *nor, float *utan, float *vtan); +struct ParticleSystemModifierData *psys_get_modifier(struct Object *ob, struct ParticleSystem *psys); + +struct ParticleSettings *psys_new_settings(char *name, struct Main *main); +struct ParticleSettings *psys_copy_settings(struct ParticleSettings *part); +void psys_flush_settings(struct ParticleSettings *part, int event, int hair_recalc); + +void psys_find_parents(struct Object *ob, struct ParticleSystemModifierData *psmd, struct ParticleSystem *psys); + +void psys_cache_paths(struct Object *ob, struct ParticleSystem *psys, float cfra, int editupdate); +void psys_cache_child_paths(struct Object *ob, struct ParticleSystem *psys, float cfra, int editupdate); +int do_guide(struct ParticleKey *state, int pa_num, float time, struct ListBase *lb); +float psys_get_size(struct Object *ob, struct Material *ma, struct ParticleSystemModifierData *psmd, struct IpoCurve *icu_size, struct ParticleSystem *psys, struct ParticleSettings *part, struct ParticleData *pa, float *vg_size); +float psys_get_timestep(struct ParticleSettings *part); +float psys_get_child_time(struct ParticleSystem *psys, int child_nbr, float cfra); +float psys_get_child_size(struct ParticleSystem *psys, int child_nbr, float cfra, float *pa_time); +void psys_get_particle_on_path(struct Object *ob, struct ParticleSystem *psys, int pa_num, struct ParticleKey *state, int vel); +int psys_get_particle_state(struct Object *ob, struct ParticleSystem *psys, int p, struct ParticleKey *state, int always); + +/* particle_system.c */ +int 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 Object *obsrc, struct Group *group, struct ParticleSystem *psys); +void psys_end_effectors(struct ParticleSystem *psys); + +void particle_system_update(struct Object *ob, struct ParticleSystem *psys); + +/* ----------- functions needed only inside particlesystem ------------ */ +/* particle.c */ +void psys_key_to_object(struct Object *ob, struct ParticleKey *key, float imat[][4]); +//void psys_key_to_geometry(struct DerivedMesh *dm, struct ParticleData *pa, struct ParticleKey *key); +//void psys_key_from_geometry(struct DerivedMesh *dm, struct ParticleData *pa, struct ParticleKey *key); +//void psys_face_mat(struct DerivedMesh *dm, struct ParticleData *pa, float mat[][4]); +void psys_vec_rot_to_face(struct DerivedMesh *dm, struct ParticleData *pa, float *vec); +//void psys_vec_rot_from_face(struct DerivedMesh *dm, struct ParticleData *pa, float *vec); +void psys_mat_hair_to_object(struct Object *ob, struct DerivedMesh *dm, short from, struct ParticleData *pa, float hairmat[][4]); +void psys_mat_hair_to_global(struct Object *ob, struct DerivedMesh *dm, short from, struct ParticleData *pa, float hairmat[][4]); + +float *psys_cache_vgroup(struct DerivedMesh *dm, struct ParticleSystem *psys, int vgroup); +void psys_get_texture(struct Object *ob, struct Material *ma, struct ParticleSystemModifierData *psmd, struct ParticleSystem *psys, struct ParticleData *pa, struct ParticleTexture *ptex, int event); +void psys_interpolate_face(struct MVert *mvert, struct MFace *mface, struct MTFace *tface, float *uv, float *vec, float *nor, float *utan, float *vtan); +float psys_interpolate_value_from_verts(struct DerivedMesh *dm, short from, int index, float *fw, float *values); +void psys_get_from_key(struct ParticleKey *key, float *loc, float *vel, float *rot, float *time); + +int psys_intersect_dm(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 psys_particle_on_dm(struct Object *ob, struct DerivedMesh *dm, int from, int index, int index_dmcache, float *fw, float foffset, float *vec, float *nor, float *utan, float *vtan); + +/* particle_system.c */ +void initialize_particle(struct ParticleData *pa, int p, struct Object *ob, struct ParticleSystem *psys, struct ParticleSystemModifierData *psmd); +void reset_particle(struct ParticleData *pa, struct ParticleSystem *psys, struct ParticleSystemModifierData *psmd, struct Object *ob, float dtime, float cfra, float *vg_vel, float *vg_tan, float *vg_rot); + +int psys_particle_dm_face_lookup(struct Object *ob, struct DerivedMesh *dm, int index, float *fw, struct LinkNode *node); + +/* ParticleEffectorCache->type */ +#define PSYS_EC_EFFECTOR 1 +#define PSYS_EC_DEFLECT 2 +#define PSYS_EC_PARTICLE 4 +#define PSYS_EC_REACTOR 8 + +/* ParticleEditKey->flag */ +#define PEK_SELECT 1 +#define PEK_TO_SELECT 2 +#define PEK_TAG 4 +#define PEK_HIDE 8 + +/* index_dmcache */ +#define DMCACHE_NOTFOUND -1 +#define DMCACHE_ISCHILD -2 + +#endif diff --git a/source/blender/blenkernel/BKE_pointcache.h b/source/blender/blenkernel/BKE_pointcache.h new file mode 100644 index 00000000000..46d240a4183 --- /dev/null +++ b/source/blender/blenkernel/BKE_pointcache.h @@ -0,0 +1,48 @@ +/* +* +* ***** 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) 2006 Blender Foundation. +* All rights reserved. +* +* The Original Code is: all of this file. +* +* Contributor(s): Campbell Barton <ideasman42@gmail.com> +* +* ***** END GPL LICENSE BLOCK ***** +*/ + +#ifndef BKE_POINTCACHE_H +#define BKE_POINTCACHE_H + +#include "DNA_ID.h" + +/* options for clearing pointcache - used for BKE_ptcache_id_clear + Before and after are non inclusive (they wont remove the cfra) */ +#define PTCACHE_CLEAR_ALL 0 +#define PTCACHE_CLEAR_FRAME 1 +#define PTCACHE_CLEAR_BEFORE 2 +#define PTCACHE_CLEAR_AFTER 3 + +#define PTCACHE_EXT ".bphys" +#define PTCACHE_PATH "//pointcache/" + +int BKE_ptcache_id_filename(struct ID *id, char *filename, int cfra, int stack_index, short do_path, short do_ext); +FILE * BKE_ptcache_id_fopen(struct ID *id, char mode, int cfra, int stack_index); +void BKE_ptcache_id_clear(struct ID *id, char mode, int cfra, int stack_index); + +#endif diff --git a/source/blender/blenkernel/BKE_softbody.h b/source/blender/blenkernel/BKE_softbody.h index 15200bf46f8..0f58b93730a 100644 --- a/source/blender/blenkernel/BKE_softbody.h +++ b/source/blender/blenkernel/BKE_softbody.h @@ -37,12 +37,25 @@ struct Object; struct SoftBody; +typedef struct BodyPoint { + float origS[3], origE[3], origT[3], pos[3], vec[3], force[3]; + float goal; + float prevpos[3], prevvec[3], prevdx[3], prevdv[3]; /* used for Heun integration */ + int nofsprings; int *springs; + float choke; + float colball; + short flag; + char octantflag; +} BodyPoint; + /* allocates and initializes general main data */ extern struct SoftBody *sbNew(void); /* frees internal data and softbody itself */ extern void sbFree(struct SoftBody *sb); +extern void softbody_clear_cache(struct Object *ob, float framenr); + /* do one simul step, reading and writing vertex locs from given array */ extern void sbObjectStep(struct Object *ob, float framnr, float (*vertexCos)[3], int numVerts); diff --git a/source/blender/blenkernel/BKE_texture.h b/source/blender/blenkernel/BKE_texture.h index 83229ec35a2..902423482b1 100644 --- a/source/blender/blenkernel/BKE_texture.h +++ b/source/blender/blenkernel/BKE_texture.h @@ -52,8 +52,11 @@ int test_dlerr(const char *name, const char *symbol); void open_plugin_tex(struct PluginTex *pit); struct PluginTex *add_plugin_tex(char *str); void free_plugin_tex(struct PluginTex *pit); + +void init_colorband(struct ColorBand *coba, int rangetype); struct ColorBand *add_colorband(int rangetype); int do_colorband(struct ColorBand *coba, float in, float out[4]); + void default_tex(struct Tex *tex); struct Tex *add_texture(char *name); void default_mtex(struct MTex *mtex); diff --git a/source/blender/blenkernel/BKE_utildefines.h b/source/blender/blenkernel/BKE_utildefines.h index 81dd2807812..f3f66190c31 100644 --- a/source/blender/blenkernel/BKE_utildefines.h +++ b/source/blender/blenkernel/BKE_utildefines.h @@ -57,6 +57,11 @@ #define ELEM6(a, b, c, d, e, f, g) ( ELEM(a, b, c) || ELEM4(a, d, e, f, g) ) #define ELEM7(a, b, c, d, e, f, g, h) ( ELEM3(a, b, c, d) || ELEM4(a, e, f, g, h) ) #define ELEM8(a, b, c, d, e, f, g, h, i) ( ELEM4(a, b, c, d, e) || ELEM4(a, f, g, h, i) ) +#define ELEM9(a, b, c, d, e, f, g, h, i, j) ( ELEM4(a, b, c, d, e) || ELEM5(a, f, g, h, i, j) ) + +/* shift around elements */ +#define SHIFT3(type, a, b, c) { type tmp; tmp = a; a = c; c = b; b = tmp; } +#define SHIFT4(type, a, b, c, d) { type tmp; tmp = a; a = d; d = c; c = b; b = tmp; } /* string compare */ #define STREQ(str, a) ( strcmp((str), (a))==0 ) @@ -97,6 +102,7 @@ #define ABS(a) ( (a)<0 ? (-(a)) : (a) ) #define VECCOPY(v1,v2) {*(v1)= *(v2); *(v1+1)= *(v2+1); *(v1+2)= *(v2+2);} +#define VECCOPY2D(v1,v2) {*(v1)= *(v2); *(v1+1)= *(v2+1);} #define QUATCOPY(v1,v2) {*(v1)= *(v2); *(v1+1)= *(v2+1); *(v1+2)= *(v2+2); *(v1+3)= *(v2+3);} #define LONGCOPY(a, b, c) {int lcpc=c, *lcpa=(int *)a, *lcpb=(int *)b; while(lcpc-->0) *(lcpa++)= *(lcpb++);} diff --git a/source/blender/blenkernel/BKE_verse.h b/source/blender/blenkernel/BKE_verse.h index 8f8fb61100f..aba42302364 100644 --- a/source/blender/blenkernel/BKE_verse.h +++ b/source/blender/blenkernel/BKE_verse.h @@ -46,21 +46,12 @@ struct VerseEdge; #define VEDHASH(a, b) ((a<b ? a : b) % VEDHASHSIZE) /* - * virtual data type (used only for retype) - */ -typedef struct verse_parent { - struct verse_parent *next, *prev; - VLayerID layer_id; - uint32 id; -} verse_parent; - -/* * verse data: 4 float value */ typedef struct quat_real32_item { struct quat_real32_item *next, *prev; - VLayerID layer_id; - void *parent; + struct VLayer *vlayer; /* pointer at VerseLayer */ + uint32 id; /* id of item */ real32 value[4]; } quat_real32_item; @@ -69,8 +60,8 @@ typedef struct quat_real32_item { */ typedef struct quat_uint32_item { struct quat_uint32_item *next, *prev; - VLayerID layer_id; - void *parent; + struct VLayer *vlayer; /* pointer at VerseLayer */ + uint32 id; /* id of item */ uint32 value[4]; } quat_uint32_item; @@ -79,8 +70,8 @@ typedef struct quat_uint32_item { */ typedef struct vec_real32_item { struct vec_real32_item *next, *prev; - VLayerID layer_id; - void *parent; + struct VLayer *vlayer; /* pointer at VerseLayer */ + uint32 id; /* id of item */ real32 value[3]; } vec_real32_item; @@ -89,8 +80,8 @@ typedef struct vec_real32_item { */ typedef struct real32_item { struct real32_item *next, *prev; - VLayerID layer_id; - void *parent; + struct VLayer *vlayer; /* pointer at VerseLayer */ + uint32 id; /* id of item */ real32 value; } real32_item; @@ -99,8 +90,8 @@ typedef struct real32_item { */ typedef struct uint32_item { struct uint32_item *next, *prev; - VLayerID layer_id; - void *parent; + struct VLayer *vlayer; /* pointer at VerseLayer */ + uint32 id; /* id of item */ uint32 value; } uint32_item; @@ -109,8 +100,8 @@ typedef struct uint32_item { */ typedef struct uint8_item { struct uint8_item *next, *prev; - VLayerID layer_id; - void *parent; + struct VLayer *vlayer; /* pointer at VerseLayer */ + uint32 id; /* id of item */ uint8 value; } uint8_item; diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt index b8c23a6517d..c0776583a04 100644 --- a/source/blender/blenkernel/CMakeLists.txt +++ b/source/blender/blenkernel/CMakeLists.txt @@ -34,6 +34,7 @@ SET(INC ../python ../render/extern/include ../../../intern/decimation/extern ../imbuf ../avi ../../../intern/elbeem/extern ../../../intern/iksolver/extern ../blenloader ../quicktime + ../../../intern/bmfont ../nodes ${SDL_INC} ${ZLIB_INC} diff --git a/source/blender/blenkernel/SConscript b/source/blender/blenkernel/SConscript index d7b48105d3d..9ecc76046c7 100644 --- a/source/blender/blenkernel/SConscript +++ b/source/blender/blenkernel/SConscript @@ -7,6 +7,7 @@ incs = '. #/intern/guardedalloc ../include ../blenlib ../makesdna' incs += ' ../python ../render/extern/include #/intern/decimation/extern' incs += ' ../imbuf ../avi #/intern/elbeem/extern ../nodes' incs += ' #/intern/iksolver/extern ../blenloader ../quicktime' +incs += ' #/intern/bmfont' incs += ' ' + env['BF_OPENGL_INC'] incs += ' ' + env['BF_ZLIB_INC'] @@ -27,6 +28,9 @@ if env['WITH_BF_VERSE']: if env['WITH_BF_OPENEXR'] == 1: defs += ' WITH_OPENEXR' +if env['WITH_BF_DDS'] == 1: + defs += ' WITH_DDS' + if env['WITH_BF_FFMPEG'] == 1: defs += ' WITH_FFMPEG' incs += ' ' + env['BF_FFMPEG_INC'] diff --git a/source/blender/blenkernel/bad_level_call_stubs/Makefile b/source/blender/blenkernel/bad_level_call_stubs/Makefile index 2d98ea5a1af..1d4db1037ea 100644 --- a/source/blender/blenkernel/bad_level_call_stubs/Makefile +++ b/source/blender/blenkernel/bad_level_call_stubs/Makefile @@ -39,10 +39,6 @@ include nan_compile.mk CFLAGS += $(LEVEL_2_C_WARNINGS) CFLAGS += $(FIX_STUBS_WARNINGS) -ifeq ($(OS),$(findstring $(OS), "beos darwin freebsd linux openbsd solaris windows")) - CFLAGS += -funsigned-char -endif - CPPFLAGS += $(OGL_CPPFLAGS) CPPFLAGS += -I../../makesdna CPPFLAGS += -I../../include diff --git a/source/blender/blenkernel/bad_level_call_stubs/stubs.c b/source/blender/blenkernel/bad_level_call_stubs/stubs.c index 344f763608c..7e1aca3bb20 100644 --- a/source/blender/blenkernel/bad_level_call_stubs/stubs.c +++ b/source/blender/blenkernel/bad_level_call_stubs/stubs.c @@ -54,9 +54,14 @@ struct IpoCurve; struct FluidsimSettings; struct Render; struct RenderResult; +struct Object; +struct bPythonConstraint; +struct bConstraintOb; +struct bConstraintTarget; +struct ListBase; char *getIpoCurveName( struct IpoCurve * icu ); -void insert_vert_ipo(struct IpoCurve *icu, float x, float y); +void insert_vert_icu(struct IpoCurve *icu, float x, float y, short fast); struct IpoCurve *verify_ipocurve(struct ID *id, short a, char *b, char *d, int e); void elbeemDebugOut(char *msg); void fluidsimSettingsFree(struct FluidsimSettings* sb); @@ -80,7 +85,7 @@ char *getIpoCurveName( struct IpoCurve * icu ) return 0; } -void insert_vert_ipo(struct IpoCurve *icu, float x, float y) +void insert_vert_icu(struct IpoCurve *icu, float x, float y, short fast) { } @@ -124,6 +129,15 @@ int BPY_button_eval(char *expr, double *value) return 0; } +/* PyConstraints - BPY_interface.c */ +void BPY_pyconstraint_eval(struct bPythonConstraint *con, struct bConstraintOb *cob, struct ListBase *targets) +{ +} +void BPY_pyconstraint_target(struct bPythonConstraint *con, struct bConstraintTarget *ct) +{ +} + + /* writefile.c */ /* struct Oops; */ void free_oops(struct Oops *oops){} @@ -194,6 +208,9 @@ void IK_FreeSolver(IK_Solver *solver) {}; void IK_SolverAddGoal(IK_Solver *solver, IK_Segment *tip, float goal[3], float weight) {} void IK_SolverAddGoalOrientation(IK_Solver *solver, IK_Segment *tip, float goal[][3], float weight) {} +void IK_SolverSetPoleVectorConstraint(IK_Solver *solver, IK_Segment *tip, float goal[3], float polegoal[3], float poleangle, int getangle) {} +float IK_SolverGetPoleAngle(IK_Solver *solver) { return 0.0f; } + int IK_Solve(IK_Solver *solver, float tolerance, int max_iterations) { return 0; } /* exotic.c */ @@ -229,6 +246,8 @@ void fluidsimSettingsCopy(struct FluidsimSettings* sb) {} /*new render funcs */ int externtex(struct MTex *mtex, float *vec, float *tin, float *tr, float *tg, float *tb, float *ta) { return 0; } +void texture_rgb_blend(float *in, float *tex, float *out, float fact, float facg, int blendtype) {} +float texture_value_blend(float tex, float out, float fact, float facg, int blendtype, int flip) { return 0; } void RE_FreeRenderResult(struct RenderResult *rr) {} void RE_GetResultImage(struct Render *re, struct RenderResult *rr) {} @@ -307,6 +326,23 @@ void antialias_tagbuf(int xsize, int ysize, char *rectmove) {} /* imagetexture.c stub */ void ibuf_sample(struct ImBuf *ibuf, float fx, float fy, float dx, float dy, float *result) {} -void update_for_newframe() -{ -} +void update_for_newframe() {} + +struct FileList; +void BIF_filelist_freelib(struct FileList* filelist) {}; + +/* edittime.c stub */ +TimeMarker *get_frame_marker(int frame){return 0;}; + +/* editseq.c */ +Sequence *get_forground_frame_seq(int frame){return 0;}; + +/* modifier.c stub */ +void harmonic_coordinates_bind(struct MeshDeformModifierData *mmd, + float (*vertexcos)[3], int totvert, float cagemat[][4]) {} + +/* particle.c */ +void PE_free_particle_edit(struct ParticleSystem *psys) {} +void PE_get_colors(char sel[4], char nosel[4]) {} +void PE_recalc_world_cos(struct Object *ob, struct ParticleSystem *psys) {} + diff --git a/source/blender/blenkernel/intern/DerivedMesh.c b/source/blender/blenkernel/intern/DerivedMesh.c index 5f8e9c7b207..5e688af14a4 100644 --- a/source/blender/blenkernel/intern/DerivedMesh.c +++ b/source/blender/blenkernel/intern/DerivedMesh.c @@ -51,9 +51,11 @@ #include "DNA_object_force.h" #include "DNA_object_fluidsim.h" // N_T #include "DNA_scene_types.h" // N_T +#include "DNA_texture_types.h" #include "DNA_view3d_types.h" #include "DNA_screen_types.h" #include "DNA_space_types.h" +#include "DNA_particle_types.h" #include "BLI_arithb.h" #include "BLI_blenlib.h" @@ -61,20 +63,22 @@ #include "BLI_editVert.h" #include "BLI_linklist.h" -#include "BKE_utildefines.h" #include "BKE_cdderivedmesh.h" #include "BKE_customdata.h" #include "BKE_DerivedMesh.h" +#include "BKE_deform.h" #include "BKE_displist.h" #include "BKE_effect.h" #include "BKE_global.h" +#include "BKE_key.h" #include "BKE_material.h" +#include "BKE_modifier.h" #include "BKE_mesh.h" #include "BKE_object.h" #include "BKE_subsurf.h" -#include "BKE_deform.h" -#include "BKE_modifier.h" -#include "BKE_key.h" +#include "BKE_texture.h" +#include "BKE_utildefines.h" +#include "BKE_particle.h" #ifdef WITH_VERSE #include "BKE_verse.h" @@ -633,9 +637,10 @@ static void emDM_foreachMappedFaceCenter(DerivedMesh *dm, void (*func)(void *use } static void emDM_drawMappedFaces(DerivedMesh *dm, int (*setDrawOptions)(void *userData, int index, int *drawSmooth_r), void *userData, int useColors) { + GLubyte act_face_stipple[32*32/8] = DM_FACE_STIPPLE; EditMeshDerivedMesh *emdm= (EditMeshDerivedMesh*) dm; EditFace *efa; - int i; + int i, draw; if (emdm->vertexCos) { EditVert *eve; @@ -645,7 +650,13 @@ static void emDM_drawMappedFaces(DerivedMesh *dm, int (*setDrawOptions)(void *us for (i=0,efa= emdm->em->faces.first; efa; i++,efa= efa->next) { int drawSmooth = (efa->flag & ME_SMOOTH); - if(!setDrawOptions || setDrawOptions(userData, i, &drawSmooth)) { + draw = setDrawOptions==NULL ? 1 : setDrawOptions(userData, i, &drawSmooth); + if(draw) { + if (draw==2) { /* enabled with stipple */ + glEnable(GL_POLYGON_STIPPLE); + glPolygonStipple(act_face_stipple); + } + glShadeModel(drawSmooth?GL_SMOOTH:GL_FLAT); glBegin(efa->v4?GL_QUADS:GL_TRIANGLES); @@ -668,12 +679,20 @@ static void emDM_drawMappedFaces(DerivedMesh *dm, int (*setDrawOptions)(void *us } } glEnd(); + + if (draw==2) + glDisable(GL_POLYGON_STIPPLE); } } } else { for (i=0,efa= emdm->em->faces.first; efa; i++,efa= efa->next) { int drawSmooth = (efa->flag & ME_SMOOTH); - if(!setDrawOptions || setDrawOptions(userData, i, &drawSmooth)) { + draw = setDrawOptions==NULL ? 1 : setDrawOptions(userData, i, &drawSmooth); + if(draw) { + if (draw==2) { /* enabled with stipple */ + glEnable(GL_POLYGON_STIPPLE); + glPolygonStipple(act_face_stipple); + } glShadeModel(drawSmooth?GL_SMOOTH:GL_FLAT); glBegin(efa->v4?GL_QUADS:GL_TRIANGLES); @@ -696,11 +715,180 @@ static void emDM_drawMappedFaces(DerivedMesh *dm, int (*setDrawOptions)(void *us } } glEnd(); + + if (draw==2) + glDisable(GL_POLYGON_STIPPLE); + } + } + } +} + +static void emDM_drawFacesTex_common(DerivedMesh *dm, + int (*drawParams)(MTFace *tface, MCol *mcol, int matnr), + int (*drawParamsMapped)(void *userData, int index), + void *userData) +{ + EditMeshDerivedMesh *emdm= (EditMeshDerivedMesh*) dm; + EditMesh *em= emdm->em; + float (*vertexCos)[3]= emdm->vertexCos; + float (*vertexNos)[3]= emdm->vertexNos; + EditFace *efa; + int i; + + if (vertexCos) { + EditVert *eve; + + for (i=0,eve=em->verts.first; eve; eve= eve->next) + eve->tmp.l = (long) i++; + + for (i=0,efa= em->faces.first; efa; i++,efa= efa->next) { + MTFace *tf= CustomData_em_get(&em->fdata, efa->data, CD_MTFACE); + MCol *mcol= CustomData_em_get(&em->fdata, efa->data, CD_MCOL); + unsigned char *cp= NULL; + int drawSmooth= (efa->flag & ME_SMOOTH); + int flag; + + if(drawParams) + flag= drawParams(tf, mcol, efa->mat_nr); + else if(drawParamsMapped) + flag= drawParamsMapped(userData, i); + else + flag= 1; + + if(flag != 0) { /* flag 0 == the face is hidden or invisible */ + if (flag==1 && mcol) + cp= (unsigned char*)mcol; + + glShadeModel(drawSmooth?GL_SMOOTH:GL_FLAT); + + glBegin(efa->v4?GL_QUADS:GL_TRIANGLES); + if (!drawSmooth) { + glNormal3fv(emdm->faceNos[i]); + + if(tf) glTexCoord2fv(tf->uv[0]); + if(cp) glColor3ub(cp[3], cp[2], cp[1]); + glVertex3fv(vertexCos[(int) efa->v1->tmp.l]); + + if(tf) glTexCoord2fv(tf->uv[1]); + if(cp) glColor3ub(cp[7], cp[6], cp[5]); + glVertex3fv(vertexCos[(int) efa->v2->tmp.l]); + + if(tf) glTexCoord2fv(tf->uv[2]); + if(cp) glColor3ub(cp[11], cp[10], cp[9]); + glVertex3fv(vertexCos[(int) efa->v3->tmp.l]); + + if(efa->v4) { + if(tf) glTexCoord2fv(tf->uv[3]); + if(cp) glColor3ub(cp[15], cp[14], cp[13]); + glVertex3fv(vertexCos[(int) efa->v4->tmp.l]); + } + } else { + if(tf) glTexCoord2fv(tf->uv[0]); + if(cp) glColor3ub(cp[3], cp[2], cp[1]); + glNormal3fv(vertexNos[(int) efa->v1->tmp.l]); + glVertex3fv(vertexCos[(int) efa->v1->tmp.l]); + + if(tf) glTexCoord2fv(tf->uv[1]); + if(cp) glColor3ub(cp[7], cp[6], cp[5]); + glNormal3fv(vertexNos[(int) efa->v2->tmp.l]); + glVertex3fv(vertexCos[(int) efa->v2->tmp.l]); + + if(tf) glTexCoord2fv(tf->uv[2]); + if(cp) glColor3ub(cp[11], cp[10], cp[9]); + glNormal3fv(vertexNos[(int) efa->v3->tmp.l]); + glVertex3fv(vertexCos[(int) efa->v3->tmp.l]); + + if(efa->v4) { + if(tf) glTexCoord2fv(tf->uv[3]); + if(cp) glColor3ub(cp[15], cp[14], cp[13]); + glNormal3fv(vertexNos[(int) efa->v4->tmp.l]); + glVertex3fv(vertexCos[(int) efa->v4->tmp.l]); + } + } + glEnd(); + } + } + } else { + for (i=0,efa= em->faces.first; efa; i++,efa= efa->next) { + MTFace *tf= CustomData_em_get(&em->fdata, efa->data, CD_MTFACE); + MCol *mcol= CustomData_em_get(&em->fdata, efa->data, CD_MCOL); + unsigned char *cp= NULL; + int drawSmooth= (efa->flag & ME_SMOOTH); + int flag; + + if(drawParams) + flag= drawParams(tf, mcol, efa->mat_nr); + else if(drawParamsMapped) + flag= drawParamsMapped(userData, i); + else + flag= 1; + + if(flag != 0) { /* flag 0 == the face is hidden or invisible */ + if (flag==1 && mcol) + cp= (unsigned char*)mcol; + + glShadeModel(drawSmooth?GL_SMOOTH:GL_FLAT); + + glBegin(efa->v4?GL_QUADS:GL_TRIANGLES); + if (!drawSmooth) { + glNormal3fv(efa->n); + + if(tf) glTexCoord2fv(tf->uv[0]); + if(cp) glColor3ub(cp[3], cp[2], cp[1]); + glVertex3fv(efa->v1->co); + + if(tf) glTexCoord2fv(tf->uv[1]); + if(cp) glColor3ub(cp[7], cp[6], cp[5]); + glVertex3fv(efa->v2->co); + + if(tf) glTexCoord2fv(tf->uv[2]); + if(cp) glColor3ub(cp[11], cp[10], cp[9]); + glVertex3fv(efa->v3->co); + + if(efa->v4) { + if(tf) glTexCoord2fv(tf->uv[3]); + if(cp) glColor3ub(cp[15], cp[14], cp[13]); + glVertex3fv(efa->v4->co); + } + } else { + if(tf) glTexCoord2fv(tf->uv[0]); + if(cp) glColor3ub(cp[3], cp[2], cp[1]); + glNormal3fv(efa->v1->no); + glVertex3fv(efa->v1->co); + + if(tf) glTexCoord2fv(tf->uv[1]); + if(cp) glColor3ub(cp[7], cp[6], cp[5]); + glNormal3fv(efa->v2->no); + glVertex3fv(efa->v2->co); + + if(tf) glTexCoord2fv(tf->uv[2]); + if(cp) glColor3ub(cp[11], cp[10], cp[9]); + glNormal3fv(efa->v3->no); + glVertex3fv(efa->v3->co); + + if(efa->v4) { + if(tf) glTexCoord2fv(tf->uv[3]); + if(cp) glColor3ub(cp[15], cp[14], cp[13]); + glNormal3fv(efa->v4->no); + glVertex3fv(efa->v4->co); + } + } + glEnd(); } } } } +static void emDM_drawFacesTex(DerivedMesh *dm, int (*setDrawOptions)(MTFace *tface, MCol *mcol, int matnr)) +{ + emDM_drawFacesTex_common(dm, setDrawOptions, NULL, NULL); +} + +static void emDM_drawMappedFacesTex(DerivedMesh *dm, int (*setDrawOptions)(void *userData, int index), void *userData) +{ + emDM_drawFacesTex_common(dm, NULL, setDrawOptions, userData); +} + static void emDM_getMinMax(DerivedMesh *dm, float min_r[3], float max_r[3]) { EditMeshDerivedMesh *emdm= (EditMeshDerivedMesh*) dm; @@ -946,6 +1134,8 @@ static DerivedMesh *getEditMeshDerivedMesh(EditMesh *em, Object *ob, emdm->dm.drawMappedEdges = emDM_drawMappedEdges; emdm->dm.drawMappedEdgesInterp = emDM_drawMappedEdgesInterp; emdm->dm.drawMappedFaces = emDM_drawMappedFaces; + emdm->dm.drawMappedFacesTex = emDM_drawMappedFacesTex; + emdm->dm.drawFacesTex = emDM_drawFacesTex; emdm->dm.drawUVEdges = emDM_drawUVEdges; emdm->dm.release = emDM_release; @@ -1614,7 +1804,7 @@ CustomDataMask get_viewedit_datamask() ScrArea *sa; /* check if we need tfaces & mcols due to face select or texture paint */ - if(G.f & G_FACESELECT || G.f & G_TEXTUREPAINT) { + if(FACESEL_PAINT_TEST || G.f & G_TEXTUREPAINT) { mask |= CD_MASK_MTFACE | CD_MASK_MCOL; } else { /* check if we need tfaces & mcols due to view mode */ @@ -1625,7 +1815,7 @@ CustomDataMask get_viewedit_datamask() /* this includes normals for mesh_create_shadedColors */ mask |= CD_MASK_MTFACE | CD_MASK_MCOL | CD_MASK_NORMAL; } - if(view->drawtype == OB_TEXTURE) { + if((view->drawtype == OB_TEXTURE) || ((view->drawtype == OB_SOLID) && (view->flag2 & V3D_SOLID_TEX))) { mask |= CD_MASK_MTFACE | CD_MASK_MCOL; } } @@ -1729,7 +1919,10 @@ static void mesh_calc_modifiers(Object *ob, float (*inputVertexCos)[3], } else { if(!fluidsimMeshUsed) { /* default behaviour for meshes */ - deformedVerts = inputVertexCos; + if(inputVertexCos) + deformedVerts = inputVertexCos; + else + deformedVerts = mesh_getRefKeyCos(me, &numVerts); } else { /* the fluid sim mesh might have more vertices than the original * one, so inputVertexCos shouldnt be used @@ -1770,6 +1963,7 @@ static void mesh_calc_modifiers(Object *ob, float (*inputVertexCos)[3], */ if(mti->type == eModifierTypeType_OnlyDeform) { + /* No existing verts to deform, need to build them. */ if(!deformedVerts) { if(dm) { @@ -1811,9 +2005,12 @@ static void mesh_calc_modifiers(Object *ob, float (*inputVertexCos)[3], /* set the DerivedMesh to only copy needed data */ DM_set_only_copy(dm, (CustomDataMask)curr->link); - - ndm = mti->applyModifier(md, ob, dm, useRenderParams, - !inputVertexCos); + + if(((CustomDataMask)curr->link) & CD_MASK_ORIGSPACE) + if(!CustomData_has_layer(&dm->faceData, CD_ORIGSPACE)) + CustomData_add_layer(&dm->faceData, CD_ORIGSPACE, CD_DEFAULT, NULL, dm->getNumFaces(dm)); + + ndm = mti->applyModifier(md, ob, dm, useRenderParams, !inputVertexCos); if(ndm) { /* if the modifier returned a new dm, release the old one */ @@ -1887,6 +2084,24 @@ static float (*editmesh_getVertexCos(EditMesh *em, int *numVerts_r))[3] return cos; } +static int editmesh_modifier_is_enabled(ModifierData *md, DerivedMesh *dm) +{ + ModifierTypeInfo *mti = modifierType_getInfo(md->type); + int required_mode = eModifierMode_Realtime | eModifierMode_Editmode; + + if((md->mode & required_mode) != required_mode) return 0; + if((mti->flags & eModifierTypeFlag_RequiresOriginalData) && dm) { + modifier_setError(md, "Internal error, modifier requires" + "original data (bad stack position)."); + return 0; + } + if(mti->isDisabled && mti->isDisabled(md)) return 0; + if(!(mti->flags & eModifierTypeFlag_SupportsEditmode)) return 0; + if(md->mode & eModifierMode_DisableTemporary) return 0; + + return 1; +} + static void editmesh_calc_modifiers(DerivedMesh **cage_r, DerivedMesh **final_r, CustomDataMask dataMask) @@ -1897,7 +2112,6 @@ static void editmesh_calc_modifiers(DerivedMesh **cage_r, float (*deformedVerts)[3] = NULL; DerivedMesh *dm; int i, numVerts = 0, cageIndex = modifiers_getCageIndex(ob, NULL); - int required_mode = eModifierMode_Realtime | eModifierMode_Editmode; LinkNode *datamasks, *curr; modifiers_clearErrors(ob); @@ -1918,14 +2132,8 @@ static void editmesh_calc_modifiers(DerivedMesh **cage_r, for(i = 0; md; i++, md = md->next, curr = curr->next) { ModifierTypeInfo *mti = modifierType_getInfo(md->type); - if((md->mode & required_mode) != required_mode) continue; - if((mti->flags & eModifierTypeFlag_RequiresOriginalData) && dm) { - modifier_setError(md, "Internal error, modifier requires" - "original data (bad stack position)."); + if(!editmesh_modifier_is_enabled(md, dm)) continue; - } - if(mti->isDisabled && mti->isDisabled(md)) continue; - if(!(mti->flags & eModifierTypeFlag_SupportsEditmode)) continue; /* How to apply modifier depends on (a) what we already have as * a result of previous modifiers (could be a DerivedMesh or just @@ -1980,6 +2188,10 @@ static void editmesh_calc_modifiers(DerivedMesh **cage_r, /* set the DerivedMesh to only copy needed data */ DM_set_only_copy(dm, (CustomDataMask)curr->link); + if(((CustomDataMask)curr->link) & CD_MASK_ORIGSPACE) + if(!CustomData_has_layer(&dm->faceData, CD_ORIGSPACE)) + CustomData_add_layer(&dm->faceData, CD_ORIGSPACE, CD_DEFAULT, NULL, dm->getNumFaces(dm)); + ndm = mti->applyModifierEM(md, ob, em, dm); if (ndm) { @@ -2071,10 +2283,10 @@ void weight_to_rgb(float input, float *fr, float *fg, float *fb) *fb= 0.0f; } } -static void calc_weightpaint_vert_color(Object *ob, int vert, unsigned char *col) +static void calc_weightpaint_vert_color(Object *ob, ColorBand *coba, int vert, unsigned char *col) { Mesh *me = ob->data; - float fr, fg, fb, input = 0.0f; + float colf[4], input = 0.0f; int i; if (me->dvert) { @@ -2085,29 +2297,41 @@ static void calc_weightpaint_vert_color(Object *ob, int vert, unsigned char *col CLAMP(input, 0.0f, 1.0f); - weight_to_rgb(input, &fr, &fg, &fb); + if(coba) + do_colorband(coba, input, colf); + else + weight_to_rgb(input, colf, colf+1, colf+2); - col[3] = (unsigned char)(fr * 255.0f); - col[2] = (unsigned char)(fg * 255.0f); - col[1] = (unsigned char)(fb * 255.0f); + col[3] = (unsigned char)(colf[0] * 255.0f); + col[2] = (unsigned char)(colf[1] * 255.0f); + col[1] = (unsigned char)(colf[2] * 255.0f); col[0] = 255; } + +static ColorBand *stored_cb= NULL; + +void vDM_ColorBand_store(ColorBand *coba) +{ + stored_cb= coba; +} + static unsigned char *calc_weightpaint_colors(Object *ob) { Mesh *me = ob->data; MFace *mf = me->mface; + ColorBand *coba= stored_cb; /* warning, not a local var */ unsigned char *wtcol; int i; wtcol = MEM_callocN (sizeof (unsigned char) * me->totface*4*4, "weightmap"); memset(wtcol, 0x55, sizeof (unsigned char) * me->totface*4*4); - for (i=0; i<me->totface; i++, mf++){ - calc_weightpaint_vert_color(ob, mf->v1, &wtcol[(i*4 + 0)*4]); - calc_weightpaint_vert_color(ob, mf->v2, &wtcol[(i*4 + 1)*4]); - calc_weightpaint_vert_color(ob, mf->v3, &wtcol[(i*4 + 2)*4]); + for (i=0; i<me->totface; i++, mf++) { + calc_weightpaint_vert_color(ob, coba, mf->v1, &wtcol[(i*4 + 0)*4]); + calc_weightpaint_vert_color(ob, coba, mf->v2, &wtcol[(i*4 + 1)*4]); + calc_weightpaint_vert_color(ob, coba, mf->v3, &wtcol[(i*4 + 2)*4]); if (mf->v4) - calc_weightpaint_vert_color(ob, mf->v4, &wtcol[(i*4 + 3)*4]); + calc_weightpaint_vert_color(ob, coba, mf->v4, &wtcol[(i*4 + 3)*4]); } return wtcol; @@ -2146,7 +2370,7 @@ static void mesh_build_data(Object *ob, CustomDataMask dataMask) if(ob!=G.obedit) { Object *obact = G.scene->basact?G.scene->basact->object:NULL; - int editing = (G.f & (G_FACESELECT|G_WEIGHTPAINT|G_VERTEXPAINT|G_TEXTUREPAINT)); + int editing = (FACESEL_PAINT_TEST)|(G.f & G_PARTICLEEDIT); int needMapping = editing && (ob==obact); if( (G.f & G_WEIGHTPAINT) && ob==obact ) { @@ -2461,6 +2685,61 @@ float *mesh_get_mapped_verts_nors(Object *ob) return vertexcosnos; } +/* ********* crazyspace *************** */ + +int editmesh_get_first_deform_matrices(float (**deformmats)[3][3], float (**deformcos)[3]) +{ + Object *ob = G.obedit; + EditMesh *em = G.editMesh; + ModifierData *md; + DerivedMesh *dm; + int i, a, numleft = 0, numVerts = 0; + int cageIndex = modifiers_getCageIndex(ob, NULL); + float (*defmats)[3][3] = NULL, (*deformedVerts)[3] = NULL; + + modifiers_clearErrors(ob); + + dm = NULL; + md = ob->modifiers.first; + + /* compute the deformation matrices and coordinates for the first + modifiers with on cage editing that are enabled and support computing + deform matrices */ + for(i = 0; md && i <= cageIndex; i++, md = md->next) { + ModifierTypeInfo *mti = modifierType_getInfo(md->type); + + if(!editmesh_modifier_is_enabled(md, dm)) + continue; + + if(mti->type==eModifierTypeType_OnlyDeform && mti->deformMatricesEM) { + if(!defmats) { + dm= getEditMeshDerivedMesh(em, ob, NULL); + deformedVerts= editmesh_getVertexCos(em, &numVerts); + defmats= MEM_callocN(sizeof(*defmats)*numVerts, "defmats"); + + for(a=0; a<numVerts; a++) + Mat3One(defmats[a]); + } + + mti->deformMatricesEM(md, ob, em, dm, deformedVerts, defmats, + numVerts); + } + else + break; + } + + for(; md && i <= cageIndex; md = md->next, i++) + if(editmesh_modifier_is_enabled(md, dm) && modifier_isDeformer(md)) + numleft++; + + if(dm) + dm->release(dm); + + *deformmats= defmats; + *deformcos= deformedVerts; + + return numleft; +} /* ************************* fluidsim bobj file handling **************************** */ diff --git a/source/blender/blenkernel/intern/Makefile b/source/blender/blenkernel/intern/Makefile index a6b14a72690..89b91ed2a8f 100644 --- a/source/blender/blenkernel/intern/Makefile +++ b/source/blender/blenkernel/intern/Makefile @@ -36,10 +36,6 @@ DIR = $(OCGDIR)/blender/$(LIBNAME) include nan_compile.mk -ifeq ($(OS),$(findstring $(OS), "beos darwin freebsd openbsd linux solaris windows")) - CFLAGS += -funsigned-char -endif - CFLAGS += $(LEVEL_1_C_WARNINGS) # OpenGL and Python @@ -64,6 +60,8 @@ CPPFLAGS += -I../../include # to include the render stuff: CPPFLAGS += -I../../render/extern/include +# for image stamping +CPPFLAGS += -I$(NAN_BMFONT)/include # for sound #CPPFLAGS += -I../../../kernel/gen_system CPPFLAGS += $(NAN_SDLCFLAGS) @@ -101,6 +99,10 @@ ifeq ($(WITH_OPENEXR), true) CPPFLAGS += -DWITH_OPENEXR endif +ifeq ($(WITH_DDS), true) + CPPFLAGS += -DWITH_DDS +endif + ifeq ($(WITH_QUICKTIME), true) CPPFLAGS += -I../../quicktime CPPFLAGS += -DWITH_QUICKTIME diff --git a/source/blender/blenkernel/intern/action.c b/source/blender/blenkernel/intern/action.c index aa9d3d30f98..e30d7dd4ac9 100644 --- a/source/blender/blenkernel/intern/action.c +++ b/source/blender/blenkernel/intern/action.c @@ -112,7 +112,6 @@ static void make_local_action_channels(bAction *act) } } } - } void make_local_action(bAction *act) @@ -165,27 +164,27 @@ void make_local_action(bAction *act) } -void free_action(bAction *act) +void free_action (bAction *act) { bActionChannel *chan; /* Free channels */ - for (chan=act->chanbase.first; chan; chan=chan->next){ + for (chan=act->chanbase.first; chan; chan=chan->next) { if (chan->ipo) chan->ipo->id.us--; free_constraint_channels(&chan->constraintChannels); } if (act->chanbase.first) - BLI_freelistN (&act->chanbase); + BLI_freelistN(&act->chanbase); } -bAction* copy_action(bAction *src) +bAction *copy_action (bAction *src) { bAction *dst = NULL; bActionChannel *dchan, *schan; - if(!src) return NULL; + if (!src) return NULL; dst= copy_libblock(src); duplicatelist(&(dst->chanbase), &(src->chanbase)); @@ -225,12 +224,12 @@ bPoseChannel *verify_pose_channel(bPose* pose, const char* name) { bPoseChannel *chan; - if (!pose){ + if (!pose) { return NULL; } /* See if this channel exists */ - for (chan=pose->chanbase.first; chan; chan=chan->next){ + for (chan=pose->chanbase.first; chan; chan=chan->next) { if (!strcmp (name, chan->name)) return chan; } @@ -239,7 +238,7 @@ bPoseChannel *verify_pose_channel(bPose* pose, const char* name) chan = MEM_callocN(sizeof(bPoseChannel), "verifyPoseChannel"); strncpy (chan->name, name, 31); - /* init vars to prevent mat errors */ + /* init vars to prevent math errors */ chan->quat[0] = 1.0F; chan->size[0] = chan->size[1] = chan->size[2] = 1.0F; @@ -247,6 +246,8 @@ bPoseChannel *verify_pose_channel(bPose* pose, const char* name) chan->limitmax[0]= chan->limitmax[1]= chan->limitmax[2]= 180.0f; chan->stiffness[0]= chan->stiffness[1]= chan->stiffness[2]= 0.0f; + Mat4One(chan->constinv); + BLI_addtail (&pose->chanbase, chan); return chan; @@ -304,8 +305,10 @@ static void copy_pose_channel_data(bPoseChannel *pchan, const bPoseChannel *chan pchan->flag= chan->flag; con= chan->constraints.first; - for(pcon= pchan->constraints.first; pcon; pcon= pcon->next) + for(pcon= pchan->constraints.first; pcon; pcon= pcon->next) { pcon->enforce= con->enforce; + pcon->headtail= con->headtail; + } } /* checks for IK constraint, can do more constraints flags later */ @@ -350,6 +353,28 @@ void update_pose_constraint_flags(bPose *pose) } } +/* Clears all BONE_UNKEYED flags for every pose channel in every pose + * This should only be called on frame changing, when it is acceptable to + * do this. Otherwise, these flags should not get cleared as poses may get lost. + */ +void framechange_poses_clear_unkeyed(void) +{ + Object *ob; + bPose *pose; + bPoseChannel *pchan; + + /* This needs to be done for each object that has a pose */ + // TODO: proxies may/may not be correctly handled here... (this needs checking) + for (ob= G.main->object.first; ob; ob= ob->id.next) { + /* we only need to do this on objects with a pose */ + if ( (pose= ob->pose) ) { + for (pchan= pose->chanbase.first; pchan; pchan= pchan->next) { + if (pchan->bone) + pchan->bone->flag &= ~BONE_UNKEYED; + } + } + } +} /* ************************ END Pose channels *************** */ @@ -456,10 +481,10 @@ static void blend_pose_strides(bPose *dst, bPose *src, float srcweight, short mo float dstweight; switch (mode){ - case POSE_BLEND: + case ACTSTRIPMODE_BLEND: dstweight = 1.0F - srcweight; break; - case POSE_ADD: + case ACTSTRIPMODE_ADD: dstweight = 1.0F; break; default : @@ -561,10 +586,10 @@ void blend_poses(bPose *dst, bPose *src, float srcweight, short mode) int i; switch (mode){ - case POSE_BLEND: + case ACTSTRIPMODE_BLEND: dstweight = 1.0F - srcweight; break; - case POSE_ADD: + case ACTSTRIPMODE_ADD: dstweight = 1.0F; break; default : @@ -580,10 +605,12 @@ void blend_poses(bPose *dst, bPose *src, float srcweight, short mode) if (schan->flag & POSE_ROT) { QUATCOPY(dquat, dchan->quat); QUATCOPY(squat, schan->quat); - if(mode==POSE_BLEND) + if(mode==ACTSTRIPMODE_BLEND) QuatInterpol(dchan->quat, dquat, squat, srcweight); - else - QuatAdd(dchan->quat, dquat, squat, srcweight); + else { + QuatMulFac(squat, srcweight); + QuatMul(dchan->quat, dquat, squat); + } NormalQuat (dchan->quat); } @@ -679,9 +706,15 @@ void extract_pose_from_action(bPose *pose, bAction *act, float ctime) /* Copy the data from the action into the pose */ for (pchan= pose->chanbase.first; pchan; pchan=pchan->next) { + /* skip this pose channel if it has been tagged as having unkeyed poses */ + if ((pchan->bone) && (pchan->bone->flag & BONE_UNKEYED)) + continue; + + /* get action channel and clear pchan-transform flags */ achan= get_action_channel(act, pchan->name); pchan->flag &= ~(POSE_LOC|POSE_ROT|POSE_SIZE); - if(achan) { + + if (achan) { ipo = achan->ipo; if (ipo) { /* Evaluates and sets the internal ipo value */ @@ -689,7 +722,8 @@ void extract_pose_from_action(bPose *pose, bAction *act, float ctime) /* This call also sets the pchan flags */ execute_action_ipo(achan, pchan); } - do_constraint_channels(&pchan->constraints, &achan->constraintChannels, ctime); + /* 0 = do all ipos, not only drivers */ + do_constraint_channels(&pchan->constraints, &achan->constraintChannels, ctime, 0); } } @@ -789,7 +823,7 @@ static void extract_ipochannels_from_action(ListBase *lb, ID *id, bAction *act, if(conchan && conchan->ipo) { calc_ipo(conchan->ipo, ctime); - + icu= conchan->ipo->curve.first; // only one ipo now if(icu) { nic= MEM_callocN(sizeof(NlaIpoChannel), "NlaIpoChannel constr"); @@ -823,10 +857,10 @@ static void blend_ipochannels(ListBase *dst, ListBase *src, float srcweight, int float dstweight; switch (mode){ - case POSE_BLEND: + case ACTSTRIPMODE_BLEND: dstweight = 1.0F - srcweight; break; - case POSE_ADD: + case ACTSTRIPMODE_ADD: dstweight = 1.0F; break; default : @@ -1031,6 +1065,46 @@ static Object *get_parent_path(Object *ob) /* ************** do the action ************ */ +/* For the calculation of the effects of an action at the given frame on an object + * This is currently only used for the action constraint + */ +void what_does_obaction (Object *ob, bAction *act, float cframe) +{ + ListBase tchanbase= {NULL, NULL}; + + clear_workob(); + Mat4CpyMat4(workob.obmat, ob->obmat); + Mat4CpyMat4(workob.parentinv, ob->parentinv); + Mat4CpyMat4(workob.constinv, ob->constinv); + workob.parent= ob->parent; + workob.track= ob->track; + + workob.trackflag= ob->trackflag; + workob.upflag= ob->upflag; + + workob.partype= ob->partype; + workob.par1= ob->par1; + workob.par2= ob->par2; + workob.par3= ob->par3; + + workob.constraints.first = ob->constraints.first; + workob.constraints.last = ob->constraints.last; + + strcpy(workob.parsubstr, ob->parsubstr); + + /* extract_ipochannels_from_action needs id's! */ + workob.action= act; + + extract_ipochannels_from_action(&tchanbase, &ob->id, act, "Object", bsystem_time(&workob, cframe, 0.0)); + + if (tchanbase.first) { + execute_ipochannels(&tchanbase); + BLI_freelistN(&tchanbase); + } +} + +/* ----- nla, etc. --------- */ + static void do_nla(Object *ob, int blocktype) { bPose *tpose= NULL; @@ -1039,7 +1113,7 @@ static void do_nla(Object *ob, int blocktype) bActionStrip *strip, *striplast=NULL, *stripfirst=NULL; float striptime, frametime, length, actlength; float blendfac, stripframe; - float scene_cfra= G.scene->r.cfra; + float scene_cfra= frame_to_float(G.scene->r.cfra); int doit, dostride; if(blocktype==ID_AR) { @@ -1069,10 +1143,10 @@ static void do_nla(Object *ob, int blocktype) } } if(strip==NULL) { /* extend */ - if(stripfirst) - scene_cfra= stripfirst->start; - else if(striplast) + if(striplast) scene_cfra= striplast->end; + else if(stripfirst) + scene_cfra= stripfirst->start; } /* and now go over all strips */ @@ -1109,7 +1183,7 @@ static void do_nla(Object *ob, int blocktype) if(cu->path) { /* Find the position on the path */ - ctime= bsystem_time(ob, parent, scene_cfra, 0.0); + ctime= bsystem_time(ob, scene_cfra, 0.0); if(calc_ipo_spec(cu->ipo, CU_SPEED, &ctime)==0) { /* correct for actions not starting on zero */ @@ -1131,7 +1205,7 @@ static void do_nla(Object *ob, int blocktype) } frametime = (striptime * actlength) + strip->actstart; - frametime= bsystem_time(ob, 0, frametime, 0.0); + frametime= bsystem_time(ob, frametime, 0.0); if(blocktype==ID_AR) { extract_pose_from_action (tpose, strip->act, frametime); @@ -1182,7 +1256,7 @@ static void do_nla(Object *ob, int blocktype) frametime = actlength * (strip->repeat-(int)strip->repeat); if(frametime<=0.000001f) frametime= actlength; /* rounding errors... */ - frametime= bsystem_time(ob, 0, frametime+strip->actstart, 0.0); + frametime= bsystem_time(ob, frametime+strip->actstart, 0.0); if(blocktype==ID_AR) extract_pose_from_action (tpose, strip->act, frametime); @@ -1251,12 +1325,11 @@ static void do_nla(Object *ob, int blocktype) void do_all_pose_actions(Object *ob) { - - // only to have safe calls from editor + /* only to have safe calls from editor */ if(ob==NULL) return; if(ob->type!=OB_ARMATURE || ob->pose==NULL) return; - if(ob->pose->flag & POSE_LOCKED) { // no actions to execute while transform + if(ob->pose->flag & POSE_LOCKED) { /* no actions to execute while transform */ if(ob->pose->flag & POSE_DO_UNLOCK) ob->pose->flag &= ~(POSE_LOCKED|POSE_DO_UNLOCK); } @@ -1265,11 +1338,14 @@ void do_all_pose_actions(Object *ob) cframe= get_action_frame(ob, cframe); - extract_pose_from_action (ob->pose, ob->action, bsystem_time(ob, 0, cframe, 0.0)); + extract_pose_from_action (ob->pose, ob->action, bsystem_time(ob, cframe, 0.0)); } else if(ob->nlastrips.first) { do_nla(ob, ID_AR); } + + /* clear POSE_DO_UNLOCK flags that might have slipped through (just in case) */ + ob->pose->flag &= ~POSE_DO_UNLOCK; } /* called from where_is_object */ @@ -1286,9 +1362,9 @@ void do_all_object_actions(Object *ob) cframe= get_action_frame(ob, cframe); - extract_ipochannels_from_action(&tchanbase, &ob->id, ob->action, "Object", bsystem_time(ob, 0, cframe, 0.0)); + extract_ipochannels_from_action(&tchanbase, &ob->id, ob->action, "Object", bsystem_time(ob, cframe, 0.0)); if(key) - extract_ipochannels_from_action(&tchanbase, &key->id, ob->action, "Shape", bsystem_time(ob, 0, cframe, 0.0)); + extract_ipochannels_from_action(&tchanbase, &key->id, ob->action, "Shape", bsystem_time(ob, cframe, 0.0)); if(tchanbase.first) { execute_ipochannels(&tchanbase); @@ -1299,5 +1375,3 @@ void do_all_object_actions(Object *ob) do_nla(ob, ID_OB); } } - - diff --git a/source/blender/blenkernel/intern/anim.c b/source/blender/blenkernel/intern/anim.c index 0019308a569..43168733b3f 100644 --- a/source/blender/blenkernel/intern/anim.c +++ b/source/blender/blenkernel/intern/anim.c @@ -38,6 +38,7 @@ #include "MEM_guardedalloc.h" #include "BLI_blenlib.h" #include "BLI_arithb.h" +#include "BLI_rand.h" #include "DNA_listBase.h" #include "DNA_curve_types.h" @@ -47,6 +48,7 @@ #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" #include "DNA_object_types.h" +#include "DNA_particle_types.h" #include "DNA_scene_types.h" #include "DNA_view3d_types.h" #include "DNA_vfont_types.h" @@ -60,8 +62,10 @@ #include "BKE_global.h" #include "BKE_ipo.h" #include "BKE_key.h" +#include "BKE_lattice.h" #include "BKE_main.h" #include "BKE_object.h" +#include "BKE_particle.h" #include "BKE_utildefines.h" #include "BKE_bad_level_calls.h" @@ -384,8 +388,12 @@ static void vertex_dupli__mapFunc(void *userData, int index, float *co, float *n VECCOPY(obmat[3], vec); if(vdd->par->transflag & OB_DUPLIROT) { - - vec[0]= -no_f[0]; vec[1]= -no_f[1]; vec[2]= -no_f[2]; + if(no_f) { + vec[0]= -no_f[0]; vec[1]= -no_f[1]; vec[2]= -no_f[2]; + } + else if(no_s) { + vec[0]= -no_s[0]; vec[1]= -no_s[1]; vec[2]= -no_s[2]; + } q2= vectoquat(vec, vdd->ob->trackflag, vdd->ob->upflag); @@ -559,134 +567,143 @@ static void face_duplilist(ListBase *lb, Scene *sce, Object *par) dm->release(dm); } - - -static void particle_duplilist(ListBase *lb, Scene *sce, Object *par, PartEff *paf) +static void new_particle_duplilist(ListBase *lb, Scene *sce, Object *par, ParticleSystem *psys) { - Object *ob, copyob; - Base *base; - Particle *pa; - float ctime, vec1[3]; - float vec[3], tmat[4][4], mat[3][3]; - float *q2; - int lay, a, counter; /* counter is used to find in render the indexed object */ - - pa= paf->keys; - if(pa==NULL || (G.rendering && paf->disp!=100)) { - build_particle_system(par); - pa= paf->keys; - if(pa==NULL) return; - } - - ctime= bsystem_time(par, 0, (float)G.scene->r.cfra, 0.0); + GroupObject *go; + Object *ob, **oblist=0; + ParticleSettings *part; + ParticleData *pa; + ParticleKey state; + float ctime, pa_time; + float tmat[4][4], mat[3][3], obrotmat[3][3], parotmat[3][3], size=0.0; + float xvec[3] = {-1.0, 0.0, 0.0}, *q; + int lay, a, k, step_nbr = 0, counter; + int totpart, totchild, totgroup=0, pa_num; + + if(psys==0) return; + part=psys->part; + + if(part==0) return; + + ctime = bsystem_time(par, (float)G.scene->r.cfra, 0.0); + + totpart = psys->totpart; + totchild = psys->totchild; + + BLI_srandom(31415926 + psys->seed); + lay= G.scene->lay; + if((part->draw_as == PART_DRAW_OB && part->dup_ob) || + (part->draw_as == PART_DRAW_GR && part->dup_group && part->dup_group->gobject.first)) { - for(base= sce->base.first; base; base= base->next) { - if(base->object->type>0 && (base->lay & lay) && G.obedit!=base->object) { - ob= base->object->parent; - while(ob) { - if(ob==par) { - - ob= base->object; - /* temp copy, to have ipos etc to work OK */ - copyob= *ob; - - /* don't want parent animation to apply on past object positions */ - if(!(paf->flag & PAF_STATIC)) - ob->parent= NULL; - - for(a=0, pa= paf->keys, counter=0; a<paf->totpart; a++, pa+=paf->totkey, counter++) { - - if(paf->flag & PAF_STATIC) { - float mtime; - - where_is_particle(paf, pa, pa->time, vec1); - mtime= pa->time+pa->lifetime; - - for(ctime= pa->time; ctime<mtime; ctime+=paf->staticstep, counter++) { - - /* make sure hair grows until the end.. */ - if(ctime>pa->time+pa->lifetime) ctime= pa->time+pa->lifetime; - - /* to give ipos in object correct offset */ - where_is_object_time(ob, ctime-pa->time); - - where_is_particle(paf, pa, ctime, vec); // makes sure there's always a vec - Mat4MulVecfl(par->obmat, vec); - - if(paf->stype==PAF_VECT) { - where_is_particle(paf, pa, ctime+1.0, vec1); // makes sure there's always a vec - Mat4MulVecfl(par->obmat, vec1); - - VecSubf(vec1, vec1, vec); - q2= vectoquat(vec1, ob->trackflag, ob->upflag); - - QuatToMat3(q2, mat); - Mat4CpyMat4(tmat, ob->obmat); - Mat4MulMat43(ob->obmat, tmat, mat); - } - - VECCOPY(ob->obmat[3], vec); - /* put object back in original state, so it cam be restored OK */ - Mat4CpyMat4(tmat, ob->obmat); - Mat4CpyMat4(ob->obmat, copyob.obmat); - new_dupli_object(lb, ob, tmat, par->lay, counter); - } - } - else { // non static particles - - if((paf->flag & PAF_UNBORN)==0 && ctime < pa->time) continue; - if((paf->flag & PAF_DIED)==0 && ctime > pa->time+pa->lifetime) continue; + if(psys->flag & (PSYS_HAIR_DONE|PSYS_KEYED) && part->draw & PART_DRAW_KEYS) + step_nbr = part->keys_step; + else + step_nbr = 0; - //if(ctime < pa->time+pa->lifetime) { + psys->lattice = psys_get_lattice(par, psys); - /* to give ipos in object correct offset, ob->parent is NULLed */ - where_is_object_time(ob, ctime-pa->time); - - where_is_particle(paf, pa, ctime, vec); - if(paf->stype==PAF_VECT) { - - /* if particle died, we use previous position */ - if(ctime > pa->time+pa->lifetime) { - where_is_particle(paf, pa, pa->time+pa->lifetime-1.0f, vec1); - VecSubf(vec1, vec, vec1); - } - else { - where_is_particle(paf, pa, ctime+1.0f, vec1); - VecSubf(vec1, vec1, vec); - } - q2= vectoquat(vec1, ob->trackflag, ob->upflag); - - QuatToMat3(q2, mat); - Mat4CpyMat4(tmat, ob->obmat); - Mat4MulMat43(ob->obmat, tmat, mat); - } + if(part->draw_as==PART_DRAW_GR) { + group_handle_recalc_and_update(par, part->dup_group); - VECCOPY(ob->obmat[3], vec); - - /* put object back in original state, so it can be restored OK */ - Mat4CpyMat4(tmat, ob->obmat); - Mat4CpyMat4(ob->obmat, copyob.obmat); - new_dupli_object(lb, ob, tmat, par->lay, counter); - } + go= part->dup_group->gobject.first; + while(go) { + go=go->next; + totgroup++; + } + + oblist= MEM_callocN(totgroup*sizeof(Object *), "dupgroup object list"); + go= part->dup_group->gobject.first; + for(a=0; a<totgroup; a++, go=go->next) + oblist[a]=go->ob; + } + + if(totchild==0 || part->draw & PART_DRAW_PARENT) + a=0; + else + a=totpart; + + for(pa=psys->particles,counter=0; a<totpart+totchild; a++,pa++,counter++) { + if(a<totpart) { + if(pa->flag & (PARS_UNEXIST+PARS_NO_DISP)) continue; + + pa_num=pa->num; + + pa_time=pa->time; + + size=pa->size; + } + else { + /* TODO: figure these two out */ + pa_num = a; + pa_time = psys->particles[psys->child[a - totpart].parent].time; + + size=psys_get_child_size(psys, a - totpart, ctime, 0); + } + + if(part->draw_as==PART_DRAW_GR) { + if(part->draw&PART_DRAW_RAND_GR) + ob = oblist[BLI_rand() % totgroup]; + else if(part->from==PART_FROM_PARTICLE) + ob = oblist[pa_num % totgroup]; + else + ob = oblist[a % totgroup]; + } + else + ob = part->dup_ob; + + for(k=0; k<=step_nbr; k++, counter++) { + if(step_nbr) { + state.time = (float)k / (float)step_nbr; + psys_get_particle_on_path(par, psys, a, &state, 0); + } + else { + state.time = -1.0; + if(psys_get_particle_state(par, psys, a, &state, 0) == 0) + continue; + } + + QuatToMat3(state.rot, parotmat); + + if(part->draw_as==PART_DRAW_GR && psys->part->draw & PART_DRAW_WHOLE_GR) { + for(go= part->dup_group->gobject.first; go; go= go->next) { + + Mat4CpyMat4(tmat, go->ob->obmat); + Mat4MulMat43(tmat, go->ob->obmat, parotmat); + Mat4MulFloat3((float *)tmat, size); + + VECADD(tmat[3], go->ob->obmat[3], state.co); + + new_dupli_object(lb, go->ob, tmat, par->lay, counter); } - /* temp copy, to have ipos etc to work OK */ - *ob= copyob; + } + else { + /* to give ipos in object correct offset */ + where_is_object_time(ob, ctime-pa_time); - break; + q = vectoquat(xvec, ob->trackflag, ob->upflag); + QuatToMat3(q, obrotmat); + + Mat3MulMat3(mat, parotmat, obrotmat); + Mat4CpyMat4(tmat, ob->obmat); + Mat4MulMat43(tmat, ob->obmat, mat); + Mat4MulFloat3((float *)tmat, size); + + VECCOPY(tmat[3], state.co); + + new_dupli_object(lb, ob, tmat, par->lay, counter); } - ob= ob->parent; } } } - - if(G.rendering && paf->disp!=100) { - MEM_freeN(paf->keys); - paf->keys= NULL; + if(oblist) + MEM_freeN(oblist); + + if(psys->lattice) { + end_latt_deform(); + psys->lattice = 0; } - - } static Object *find_family_object(Object **obar, char *family, char ch) @@ -767,13 +784,14 @@ ListBase *object_duplilist(Scene *sce, Object *ob) duplilist->first= duplilist->last= NULL; if(ob->transflag & OB_DUPLI) { - if(ob->transflag & OB_DUPLIVERTS) { + if(ob->transflag & OB_DUPLIPARTS) { + ParticleSystem *psys = ob->particlesystem.first; + for(; psys; psys=psys->next) + new_particle_duplilist(duplilist, sce, ob, psys); + } + else if(ob->transflag & OB_DUPLIVERTS) { if(ob->type==OB_MESH) { - PartEff *paf; - if( (paf=give_parteff(ob)) ) - particle_duplilist(duplilist, sce, ob, paf); - else - vertex_duplilist(duplilist, sce, ob); + vertex_duplilist(duplilist, sce, ob); } else if(ob->type==OB_FONT) { font_duplilist(duplilist, ob); @@ -819,14 +837,18 @@ int count_duplilist(Object *ob) if(ob->transflag & OB_DUPLIVERTS) { if(ob->type==OB_MESH) { if(ob->transflag & OB_DUPLIVERTS) { - PartEff *paf; - if( (paf=give_parteff(ob)) ) { - return paf->totpart; - } - else { + ParticleSystem *psys = ob->particlesystem.first; + int pdup=0; + + for(; psys; psys=psys->next) + pdup += psys->totpart; + + if(pdup==0){ Mesh *me= ob->data; return me->totvert; } + else + return pdup; } } } diff --git a/source/blender/blenkernel/intern/armature.c b/source/blender/blenkernel/intern/armature.c index 669724bd250..59786afd999 100644 --- a/source/blender/blenkernel/intern/armature.c +++ b/source/blender/blenkernel/intern/armature.c @@ -404,17 +404,38 @@ static void equalize_bezier(float *data, int desired) /* returns pointer to static array, filled with desired amount of bone->segments elements */ /* this calculation is done within unit bone space */ -Mat4 *b_bone_spline_setup(bPoseChannel *pchan) +Mat4 *b_bone_spline_setup(bPoseChannel *pchan, int rest) { static Mat4 bbone_array[MAX_BBONE_SUBDIV]; + static Mat4 bbone_rest_array[MAX_BBONE_SUBDIV]; + Mat4 *result_array= (rest)? bbone_rest_array: bbone_array; bPoseChannel *next, *prev; Bone *bone= pchan->bone; - float h1[3], h2[3], length, hlength1, hlength2, roll; - float mat3[3][3], imat[4][4]; + float h1[3], h2[3], scale[3], length, hlength1, hlength2, roll1=0.0f, roll2; + float mat3[3][3], imat[4][4], posemat[4][4], scalemat[4][4], iscalemat[4][4]; float data[MAX_BBONE_SUBDIV+1][4], *fp; - int a; - + int a, doscale= 0; + length= bone->length; + + if(!rest) { + /* check if we need to take non-uniform bone scaling into account */ + scale[0]= VecLength(pchan->pose_mat[0]); + scale[1]= VecLength(pchan->pose_mat[1]); + scale[2]= VecLength(pchan->pose_mat[2]); + + if(fabs(scale[0] - scale[1]) > 1e-6f || fabs(scale[1] - scale[2]) > 1e-6f) { + Mat4One(scalemat); + scalemat[0][0]= scale[0]; + scalemat[1][1]= scale[1]; + scalemat[2][2]= scale[2]; + Mat4Invert(iscalemat, scalemat); + + length *= scale[1]; + doscale = 1; + } + } + hlength1= bone->ease1*length*0.390464f; // 0.5*sqrt(2)*kappa, the handle length for near-perfect circles hlength2= bone->ease2*length*0.390464f; @@ -430,32 +451,75 @@ Mat4 *b_bone_spline_setup(bPoseChannel *pchan) first point = (0,0,0) last point = (0, length, 0) */ - Mat4Invert(imat, pchan->pose_mat); + if(rest) { + Mat4Invert(imat, pchan->bone->arm_mat); + } + else if(doscale) { + Mat4CpyMat4(posemat, pchan->pose_mat); + Mat4Ortho(posemat); + Mat4Invert(imat, posemat); + } + else + Mat4Invert(imat, pchan->pose_mat); if(prev) { + float difmat[4][4], result[3][3], imat3[3][3]; + /* transform previous point inside this bone space */ - VECCOPY(h1, prev->pose_head); + if(rest) + VECCOPY(h1, prev->bone->arm_head) + else + VECCOPY(h1, prev->pose_head) Mat4MulVecfl(imat, h1); - /* if previous bone is B-bone too, use average handle direction */ - if(prev->bone->segments>1) h1[1]-= length; + + if(prev->bone->segments>1) { + /* if previous bone is B-bone too, use average handle direction */ + h1[1]-= length; + roll1= 0.0f; + } + Normalize(h1); VecMulf(h1, -hlength1); + + if(prev->bone->segments==1) { + /* find the previous roll to interpolate */ + if(rest) + Mat4MulMat4(difmat, prev->bone->arm_mat, imat); + else + Mat4MulMat4(difmat, prev->pose_mat, imat); + Mat3CpyMat4(result, difmat); // the desired rotation at beginning of next bone + + vec_roll_to_mat3(h1, 0.0f, mat3); // the result of vec_roll without roll + + Mat3Inv(imat3, mat3); + Mat3MulMat3(mat3, result, imat3); // the matrix transforming vec_roll to desired roll + + roll1= atan2(mat3[2][0], mat3[2][2]); + } } else { h1[0]= 0.0f; h1[1]= hlength1; h1[2]= 0.0f; + roll1= 0.0f; } if(next) { float difmat[4][4], result[3][3], imat3[3][3]; /* transform next point inside this bone space */ - VECCOPY(h2, next->pose_tail); + if(rest) + VECCOPY(h2, next->bone->arm_tail) + else + VECCOPY(h2, next->pose_tail) Mat4MulVecfl(imat, h2); /* if next bone is B-bone too, use average handle direction */ if(next->bone->segments>1); else h2[1]-= length; + Normalize(h2); /* find the next roll to interpolate as well */ - Mat4MulMat4(difmat, next->pose_mat, imat); + if(rest) + Mat4MulMat4(difmat, next->bone->arm_mat, imat); + else + Mat4MulMat4(difmat, next->pose_mat, imat); Mat3CpyMat4(result, difmat); // the desired rotation at beginning of next bone vec_roll_to_mat3(h2, 0.0f, mat3); // the result of vec_roll without roll @@ -463,18 +527,16 @@ Mat4 *b_bone_spline_setup(bPoseChannel *pchan) Mat3Inv(imat3, mat3); Mat3MulMat3(mat3, imat3, result); // the matrix transforming vec_roll to desired roll - roll= atan2(mat3[2][0], mat3[2][2]); + roll2= atan2(mat3[2][0], mat3[2][2]); /* and only now negate handle */ - Normalize(h2); VecMulf(h2, -hlength2); - } else { h2[0]= 0.0f; h2[1]= -hlength2; h2[2]= 0.0f; - roll= 0.0; + roll2= 0.0; } - + /* make curve */ if(bone->segments > MAX_BBONE_SUBDIV) bone->segments= MAX_BBONE_SUBDIV; @@ -482,7 +544,7 @@ Mat4 *b_bone_spline_setup(bPoseChannel *pchan) forward_diff_bezier(0.0, h1[0], h2[0], 0.0, data[0], MAX_BBONE_SUBDIV, 4); forward_diff_bezier(0.0, h1[1], length + h2[1], length, data[0]+1, MAX_BBONE_SUBDIV, 4); forward_diff_bezier(0.0, h1[2], h2[2], 0.0, data[0]+2, MAX_BBONE_SUBDIV, 4); - forward_diff_bezier(0.0, 0.390464f*roll, (1.0f-0.390464f)*roll, roll, data[0]+3, MAX_BBONE_SUBDIV, 4); + forward_diff_bezier(roll1, roll1 + 0.390464f*(roll2-roll1), roll2 - 0.390464f*(roll2-roll1), roll2, data[0]+3, MAX_BBONE_SUBDIV, 4); equalize_bezier(data[0], bone->segments); // note: does stride 4! @@ -490,53 +552,93 @@ Mat4 *b_bone_spline_setup(bPoseChannel *pchan) for(a=0, fp= data[0]; a<bone->segments; a++, fp+=4) { VecSubf(h1, fp+4, fp); vec_roll_to_mat3(h1, fp[3], mat3); // fp[3] is roll - Mat4CpyMat3(bbone_array[a].mat, mat3); - VECCOPY(bbone_array[a].mat[3], fp); + + Mat4CpyMat3(result_array[a].mat, mat3); + VECCOPY(result_array[a].mat[3], fp); + + if(doscale) { + /* correct for scaling when this matrix is used in scaled space */ + Mat4MulSerie(result_array[a].mat, iscalemat, result_array[a].mat, + scalemat, NULL, NULL, NULL, NULL, NULL); + } } - return bbone_array; + return result_array; } /* ************ Armature Deform ******************* */ -static void pchan_b_bone_defmats(bPoseChannel *pchan) +static void pchan_b_bone_defmats(bPoseChannel *pchan, int use_quaternion, int rest_def) { Bone *bone= pchan->bone; - Mat4 *b_bone= b_bone_spline_setup(pchan); + Mat4 *b_bone= b_bone_spline_setup(pchan, 0); + Mat4 *b_bone_rest= (rest_def)? NULL: b_bone_spline_setup(pchan, 1); Mat4 *b_bone_mats; + DualQuat *b_bone_dual_quats= NULL; + float tmat[4][4]; int a; - pchan->b_bone_mats=b_bone_mats= MEM_mallocN((1+bone->segments)*sizeof(Mat4), "BBone defmats"); + /* allocate b_bone matrices and dual quats */ + b_bone_mats= MEM_mallocN((1+bone->segments)*sizeof(Mat4), "BBone defmats"); + pchan->b_bone_mats= b_bone_mats; + + if(use_quaternion) { + b_bone_dual_quats= MEM_mallocN((bone->segments)*sizeof(DualQuat), "BBone dqs"); + pchan->b_bone_dual_quats= b_bone_dual_quats; + } - /* first matrix is the inverse arm_mat, to bring points in local bone space */ + /* first matrix is the inverse arm_mat, to bring points in local bone space + for finding out which segment it belongs to */ Mat4Invert(b_bone_mats[0].mat, bone->arm_mat); - - /* then we multiply the bbone_mats with arm_mat */ + + /* then we make the b_bone_mats: + - first transform to local bone space + - translate over the curve to the bbone mat space + - transform with b_bone matrix + - transform back into global space */ + Mat4One(tmat); + for(a=0; a<bone->segments; a++) { - Mat4MulMat4(b_bone_mats[a+1].mat, b_bone[a].mat, bone->arm_mat); + if(b_bone_rest) + Mat4Invert(tmat, b_bone_rest[a].mat); + else + tmat[3][1] = -a*(bone->length/(float)bone->segments); + + Mat4MulSerie(b_bone_mats[a+1].mat, pchan->chan_mat, bone->arm_mat, + b_bone[a].mat, tmat, b_bone_mats[0].mat, NULL, NULL, NULL); + + if(use_quaternion) + Mat4ToDQuat(bone->arm_mat, b_bone_mats[a+1].mat, &b_bone_dual_quats[a]); } } -static void b_bone_deform(bPoseChannel *pchan, Bone *bone, float *defpos) +static void b_bone_deform(bPoseChannel *pchan, Bone *bone, float *co, DualQuat *dq, float defmat[][3]) { Mat4 *b_bone= pchan->b_bone_mats; - float segment; + float (*mat)[4]= b_bone[0].mat; + float segment, y; int a; - /* need to transform defpos back to bonespace */ - Mat4MulVecfl(b_bone[0].mat, defpos); + /* need to transform co back to bonespace, only need y */ + y= mat[0][1]*co[0] + mat[1][1]*co[1] + mat[2][1]*co[2] + mat[3][1]; /* now calculate which of the b_bones are deforming this */ segment= bone->length/((float)bone->segments); - a= (int) (defpos[1]/segment); + a= (int)(y/segment); - /* note; by clamping it extends deform at endpoints, goes best with straight joints in restpos. */ + /* note; by clamping it extends deform at endpoints, goes best with + straight joints in restpos. */ CLAMP(a, 0, bone->segments-1); - /* since the bbone mats translate from (0.0.0) on the curve, we subtract */ - defpos[1] -= ((float)a)*segment; - - Mat4MulVecfl(b_bone[a+1].mat, defpos); + if(dq) { + DQuatCpyDQuat(dq, &((DualQuat*)pchan->b_bone_dual_quats)[a]); + } + else { + Mat4MulVecfl(b_bone[a+1].mat, co); + + if(defmat) + Mat3CpyMat4(defmat, b_bone[a+1].mat); + } } /* using vec with dist to bone b1 - b2 */ @@ -590,12 +692,25 @@ float distfactor_to_bone (float vec[3], float b1[3], float b2[3], float rad1, fl } } -static float dist_bone_deform(bPoseChannel *pchan, float *vec, float *co) +static void pchan_deform_mat_add(bPoseChannel *pchan, float weight, float bbonemat[][3], float mat[][3]) +{ + float wmat[3][3]; + + if(pchan->bone->segments>1) + Mat3CpyMat3(wmat, bbonemat); + else + Mat3CpyMat4(wmat, pchan->chan_mat); + + Mat3MulFloat((float*)wmat, weight); + Mat3AddMat3(mat, mat, wmat); +} + +static float dist_bone_deform(bPoseChannel *pchan, float *vec, DualQuat *dq, float mat[][3], float *co) { Bone *bone= pchan->bone; - float fac; - float cop[3]; - float contrib=0.0; + float fac, contrib=0.0; + float cop[3], bbonemat[3][3]; + DualQuat bbonedq; if(bone==NULL) return 0.0f; @@ -603,62 +718,95 @@ static float dist_bone_deform(bPoseChannel *pchan, float *vec, float *co) fac= distfactor_to_bone(cop, bone->arm_head, bone->arm_tail, bone->rad_head, bone->rad_tail, bone->dist); - if (fac>0.0){ + if (fac>0.0) { fac*=bone->weight; contrib= fac; if(contrib>0.0) { + if(vec) { + if(bone->segments>1) + // applies on cop and bbonemat + b_bone_deform(pchan, bone, cop, NULL, (mat)?bbonemat:NULL); + else + Mat4MulVecfl(pchan->chan_mat, cop); - VECCOPY (cop, co); - - if(bone->segments>1) - b_bone_deform(pchan, bone, cop); // applies on cop - - Mat4MulVecfl(pchan->chan_mat, cop); - - VecSubf (cop, cop, co); // Make this a delta from the base position - cop[0]*=fac; cop[1]*=fac; cop[2]*=fac; - VecAddf (vec, vec, cop); + // Make this a delta from the base position + VecSubf (cop, cop, co); + cop[0]*=fac; cop[1]*=fac; cop[2]*=fac; + VecAddf (vec, vec, cop); + + if(mat) + pchan_deform_mat_add(pchan, fac, bbonemat, mat); + } + else { + if(bone->segments>1) { + b_bone_deform(pchan, bone, cop, &bbonedq, NULL); + DQuatAddWeighted(dq, &bbonedq, fac); + } + else + DQuatAddWeighted(dq, pchan->dual_quat, fac); + } } } return contrib; } -static void pchan_bone_deform(bPoseChannel *pchan, float weight, float *vec, float *co, float *contrib) +static void pchan_bone_deform(bPoseChannel *pchan, float weight, float *vec, DualQuat *dq, float mat[][3], float *co, float *contrib) { - float cop[3]; + float cop[3], bbonemat[3][3]; + DualQuat bbonedq; if (!weight) return; - VECCOPY (cop, co); - - if(pchan->bone->segments>1) - b_bone_deform(pchan, pchan->bone, cop); // applies on cop - - Mat4MulVecfl(pchan->chan_mat, cop); - - vec[0]+=(cop[0]-co[0])*weight; - vec[1]+=(cop[1]-co[1])*weight; - vec[2]+=(cop[2]-co[2])*weight; + VECCOPY(cop, co); + + if(vec) { + if(pchan->bone->segments>1) + // applies on cop and bbonemat + b_bone_deform(pchan, pchan->bone, cop, NULL, (mat)?bbonemat:NULL); + else + Mat4MulVecfl(pchan->chan_mat, cop); + + vec[0]+=(cop[0]-co[0])*weight; + vec[1]+=(cop[1]-co[1])*weight; + vec[2]+=(cop[2]-co[2])*weight; + + if(mat) + pchan_deform_mat_add(pchan, weight, bbonemat, mat); + } + else { + if(pchan->bone->segments>1) { + b_bone_deform(pchan, pchan->bone, cop, &bbonedq, NULL); + DQuatAddWeighted(dq, &bbonedq, weight); + } + else + DQuatAddWeighted(dq, pchan->dual_quat, weight); + } (*contrib)+=weight; } void armature_deform_verts(Object *armOb, Object *target, DerivedMesh *dm, - float (*vertexCos)[3], int numVerts, int deformflag, - const char *defgrp_name) + float (*vertexCos)[3], float (*defMats)[3][3], + int numVerts, int deformflag, + float (*prevCos)[3], const char *defgrp_name) { bPoseChannel *pchan, **defnrToPC = NULL; MDeformVert *dverts = NULL; bDeformGroup *dg; + DualQuat *dualquats= NULL; float obinv[4][4], premat[4][4], postmat[4][4]; int use_envelope = deformflag & ARM_DEF_ENVELOPE; + int use_quaternion = deformflag & ARM_DEF_QUATERNION; + int bbone_rest_def = deformflag & ARM_DEF_B_BONE_REST; + int invert_vgroup= deformflag & ARM_DEF_INVERT_VGROUP; int numGroups = 0; /* safety for vertexgroup index overflow */ int i, target_totvert = 0; /* safety for vertexgroup overflow */ int use_dverts = 0; int armature_def_nr = -1; + int totchan; if(armOb == G.obedit) return; @@ -669,11 +817,23 @@ void armature_deform_verts(Object *armOb, Object *target, DerivedMesh *dm, /* bone defmats are already in the channels, chan_mat */ - /* initialize B_bone matrices */ + /* initialize B_bone matrices and dual quaternions */ + if(use_quaternion) { + totchan= BLI_countlist(&armOb->pose->chanbase); + dualquats= MEM_callocN(sizeof(DualQuat)*totchan, "dualquats"); + } + + totchan= 0; for(pchan = armOb->pose->chanbase.first; pchan; pchan = pchan->next) { - if(!(pchan->bone->flag & BONE_NO_DEFORM)) + if(!(pchan->bone->flag & BONE_NO_DEFORM)) { if(pchan->bone->segments > 1) - pchan_b_bone_defmats(pchan); + pchan_b_bone_defmats(pchan, use_quaternion, bbone_rest_def); + + if(use_quaternion) { + pchan->dual_quat= &dualquats[totchan++]; + Mat4ToDQuat(pchan->bone->arm_mat, pchan->chan_mat, pchan->dual_quat); + } + } } /* get the def_nr for the overall armature vertex group if present */ @@ -722,13 +882,28 @@ void armature_deform_verts(Object *armOb, Object *target, DerivedMesh *dm, for(i = 0; i < numVerts; i++) { MDeformVert *dvert; - float *co = vertexCos[i]; - float vec[3]; + DualQuat sumdq, *dq = NULL; + float *co, dco[3]; + float sumvec[3], summat[3][3]; + float *vec = NULL, (*smat)[3] = NULL; float contrib = 0.0f; - float armature_weight = 1.0f; /* default to 1 if no overall def group */ + float armature_weight = 1.0f; /* default to 1 if no overall def group */ + float prevco_weight = 1.0f; /* weight for optional cached vertexcos */ int j; - vec[0] = vec[1] = vec[2] = 0.0f; + if(use_quaternion) { + memset(&sumdq, 0, sizeof(DualQuat)); + dq= &sumdq; + } + else { + sumvec[0] = sumvec[1] = sumvec[2] = 0.0f; + vec= sumvec; + + if(defMats) { + Mat3Clr((float*)summat); + smat = summat; + } + } if(use_dverts || armature_def_nr >= 0) { if(dm) dvert = dm->getVertData(dm, i, CD_MDEFORMVERT); @@ -745,11 +920,22 @@ void armature_deform_verts(Object *armOb, Object *target, DerivedMesh *dm, break; } } + /* hackish: the blending factor can be used for blending with prevCos too */ + if(prevCos) { + if(invert_vgroup) + prevco_weight= 1.0f-armature_weight; + else + prevco_weight= armature_weight; + armature_weight= 1.0f; + } } /* check if there's any point in calculating for this vert */ if(armature_weight == 0.0f) continue; + /* get the coord we work on */ + co= prevCos?prevCos[i]:vertexCos[i]; + /* Apply the object's matrix */ Mat4MulVecfl(premat, co); @@ -772,7 +958,7 @@ void armature_deform_verts(Object *armOb, Object *target, DerivedMesh *dm, bone->rad_tail, bone->dist); } - pchan_bone_deform(pchan, weight, vec, co, &contrib); + pchan_bone_deform(pchan, weight, vec, dq, smat, co, &contrib); } } /* if there are vertexgroups but not groups with bones @@ -782,7 +968,7 @@ void armature_deform_verts(Object *armOb, Object *target, DerivedMesh *dm, for(pchan = armOb->pose->chanbase.first; pchan; pchan = pchan->next) { if(!(pchan->bone->flag & BONE_NO_DEFORM)) - contrib += dist_bone_deform(pchan, vec, co); + contrib += dist_bone_deform(pchan, vec, dq, smat, co); } } } @@ -790,19 +976,61 @@ void armature_deform_verts(Object *armOb, Object *target, DerivedMesh *dm, for(pchan = armOb->pose->chanbase.first; pchan; pchan = pchan->next) { if(!(pchan->bone->flag & BONE_NO_DEFORM)) - contrib += dist_bone_deform(pchan, vec, co); + contrib += dist_bone_deform(pchan, vec, dq, smat, co); } } /* actually should be EPSILON? weight values and contrib can be like 10e-39 small */ if(contrib > 0.0001f) { - VecMulf(vec, armature_weight / contrib); - VecAddf(co, vec, co); + if(use_quaternion) { + DQuatNormalize(dq, contrib); + + if(armature_weight != 1.0f) { + VECCOPY(dco, co); + DQuatMulVecfl(dq, dco, (defMats)? summat: NULL); + VecSubf(dco, dco, co); + VecMulf(dco, armature_weight); + VecAddf(co, co, dco); + } + else + DQuatMulVecfl(dq, co, (defMats)? summat: NULL); + + smat = summat; + } + else { + VecMulf(vec, armature_weight/contrib); + VecAddf(co, vec, co); + } + + if(defMats) { + float pre[3][3], post[3][3], tmpmat[3][3]; + + Mat3CpyMat4(pre, premat); + Mat3CpyMat4(post, postmat); + Mat3CpyMat3(tmpmat, defMats[i]); + + if(!use_quaternion) /* quaternion already is scale corrected */ + Mat3MulFloat((float*)smat, armature_weight/contrib); + + Mat3MulSerie(defMats[i], tmpmat, pre, smat, post, + NULL, NULL, NULL, NULL); + } } + /* always, check above code */ Mat4MulVecfl(postmat, co); + + + /* interpolate with previous modifier position using weight group */ + if(prevCos) { + float mw= 1.0f - prevco_weight; + vertexCos[i][0]= prevco_weight*vertexCos[i][0] + mw*co[0]; + vertexCos[i][1]= prevco_weight*vertexCos[i][1] + mw*co[1]; + vertexCos[i][2]= prevco_weight*vertexCos[i][2] + mw*co[2]; + } } + if(dualquats) MEM_freeN(dualquats); if(defnrToPC) MEM_freeN(defnrToPC); /* free B_bone matrices */ @@ -811,6 +1039,12 @@ void armature_deform_verts(Object *armOb, Object *target, DerivedMesh *dm, MEM_freeN(pchan->b_bone_mats); pchan->b_bone_mats = NULL; } + if(pchan->b_bone_dual_quats) { + MEM_freeN(pchan->b_bone_dual_quats); + pchan->b_bone_dual_quats = NULL; + } + + pchan->dual_quat = NULL; } } @@ -1070,7 +1304,6 @@ static void pose_proxy_synchronize(Object *ob, Object *from, int layer_protected bPose *pose= ob->pose, *frompose= from->pose; bPoseChannel *pchan, *pchanp, pchanw; bConstraint *con; - char *str; if(frompose==NULL) return; @@ -1096,14 +1329,27 @@ static void pose_proxy_synchronize(Object *ob, Object *from, int layer_protected /* constraints, set target ob pointer to own object */ copy_constraints(&pchanw.constraints, &pchanp->constraints); - - for(con= pchanw.constraints.first; con; con= con->next) { - if(from==get_constraint_target(con, &str)) - set_constraint_target(con, ob, NULL); + + for (con= pchanw.constraints.first; con; con= con->next) { + bConstraintTypeInfo *cti= constraint_get_typeinfo(con); + ListBase targets = {NULL, NULL}; + bConstraintTarget *ct; + + if (cti && cti->get_constraint_targets) { + cti->get_constraint_targets(con, &targets); + + for (ct= targets.first; ct; ct= ct->next) { + if (ct->tar == from) + ct->tar = ob; + } + + if (cti->flush_constraint_targets) + cti->flush_constraint_targets(con, &targets, 0); + } } /* free stuff from current channel */ - if(pchan->path) MEM_freeN(pchan->path); + if (pchan->path) MEM_freeN(pchan->path); free_constraints(&pchan->constraints); /* the final copy */ @@ -1321,7 +1567,7 @@ static void execute_posetree(Object *ob, PoseTree *tree) float goalrot[3][3], goalpos[3]; float rootmat[4][4], imat[4][4]; float goal[4][4], goalinv[4][4]; - float size[3], irest_basis[3][3], full_basis[3][3]; + float irest_basis[3][3], full_basis[3][3]; float end_pose[4][4], world_pose[4][4]; float length, basis[3][3], rest_basis[3][3], start[3], *ikstretch=NULL; int a, flag, hasstretch=0; @@ -1329,7 +1575,7 @@ static void execute_posetree(Object *ob, PoseTree *tree) IK_Segment *seg, *parent, **iktree, *iktarget; IK_Solver *solver; PoseTarget *target; - bKinematicConstraint *data; + bKinematicConstraint *data, *poleangledata=NULL; Bone *bone; if (tree->totchannel == 0) @@ -1417,7 +1663,7 @@ static void execute_posetree(Object *ob, PoseTree *tree) if(tree->stretch && (pchan->ikstretch > 0.0)) { float ikstretch = pchan->ikstretch*pchan->ikstretch; - IK_SetStiffness(seg, IK_TRANS_Y, MIN2(1.0-ikstretch, 0.99)); + IK_SetStiffness(seg, IK_TRANS_Y, MIN2(1.0-ikstretch, 0.999)); IK_SetLimit(seg, IK_TRANS_Y, 0.001, 1e10); } } @@ -1440,53 +1686,88 @@ static void execute_posetree(Object *ob, PoseTree *tree) Mat4MulMat4 (imat, rootmat, ob->obmat); Mat4Invert (goalinv, imat); - for(target=tree->targets.first; target; target=target->next) { - data= (bKinematicConstraint*)target->con->data; - - /* 1.0=ctime, we pass on object for auto-ik */ - get_constraint_target_matrix(target->con, TARGET_BONE, ob, rootmat, size, 1.0); + for (target=tree->targets.first; target; target=target->next) { + float polepos[3]; + int poleconstrain= 0; + data= (bKinematicConstraint*)target->con->data; + + /* 1.0=ctime, we pass on object for auto-ik (owner-type here is object, even though + * strictly speaking, it is a posechannel) + */ + get_constraint_target_matrix(target->con, 0, CONSTRAINT_OBTYPE_OBJECT, ob, rootmat, 1.0); + /* and set and transform goal */ Mat4MulMat4(goal, rootmat, goalinv); - + VECCOPY(goalpos, goal[3]); Mat3CpyMat4(goalrot, goal); + + /* same for pole vector target */ + if(data->poletar) { + get_constraint_target_matrix(target->con, 1, CONSTRAINT_OBTYPE_OBJECT, ob, rootmat, 1.0); + + if(data->flag & CONSTRAINT_IK_SETANGLE) { + /* don't solve IK when we are setting the pole angle */ + break; + } + else { + Mat4MulMat4(goal, rootmat, goalinv); + VECCOPY(polepos, goal[3]); + poleconstrain= 1; + + if(data->flag & CONSTRAINT_IK_GETANGLE) { + poleangledata= data; + data->flag &= ~CONSTRAINT_IK_GETANGLE; + } + } + } /* do we need blending? */ - if(target->con->enforce!=1.0) { + if (target->con->enforce!=1.0) { float q1[4], q2[4], q[4]; float fac= target->con->enforce; float mfac= 1.0-fac; pchan= tree->pchan[target->tip]; - + /* end effector in world space */ Mat4CpyMat4(end_pose, pchan->pose_mat); VECCOPY(end_pose[3], pchan->pose_tail); Mat4MulSerie(world_pose, goalinv, ob->obmat, end_pose, 0, 0, 0, 0, 0); - + /* blend position */ goalpos[0]= fac*goalpos[0] + mfac*world_pose[3][0]; goalpos[1]= fac*goalpos[1] + mfac*world_pose[3][1]; goalpos[2]= fac*goalpos[2] + mfac*world_pose[3][2]; - + /* blend rotation */ Mat3ToQuat(goalrot, q1); Mat4ToQuat(world_pose, q2); QuatInterpol(q, q1, q2, mfac); QuatToMat3(q, goalrot); } - + iktarget= iktree[target->tip]; - - if(data->weight != 0.0) + + if(data->weight != 0.0) { + if(poleconstrain) + IK_SolverSetPoleVectorConstraint(solver, iktarget, goalpos, + polepos, data->poleangle*M_PI/180, (poleangledata == data)); IK_SolverAddGoal(solver, iktarget, goalpos, data->weight); - if((data->flag & CONSTRAINT_IK_ROT) && (data->orientweight != 0.0) && (data->flag & CONSTRAINT_IK_AUTO)==0) - IK_SolverAddGoalOrientation(solver, iktarget, goalrot, data->orientweight); + } + if((data->flag & CONSTRAINT_IK_ROT) && (data->orientweight != 0.0)) + if((data->flag & CONSTRAINT_IK_AUTO)==0) + IK_SolverAddGoalOrientation(solver, iktarget, goalrot, + data->orientweight); } /* solve */ IK_Solve(solver, 0.0f, tree->iterations); + + if(poleangledata) + poleangledata->poleangle= IK_SolverGetPoleAngle(solver)*180/M_PI; + IK_FreeSolver(solver); /* gather basis changes */ @@ -1603,220 +1884,46 @@ static void where_is_ik_bone(bPoseChannel *pchan, float ik_mat[][3]) // nr = t pchan->flag |= POSE_DONE; } -static void do_local_constraint(bPoseChannel *pchan, bConstraint *con) +/* NLA strip modifiers */ +static void do_strip_modifiers(Object *armob, Bone *bone, bPoseChannel *pchan) { - switch(con->type) { - case CONSTRAINT_TYPE_LOCLIKE: - { - bLocateLikeConstraint *data= con->data; - float fac= con->enforce; + bActionModifier *amod; + bActionStrip *strip, *strip2; + float scene_cfra= G.scene->r.cfra; + int do_modif; + + for (strip=armob->nlastrips.first; strip; strip=strip->next) { + do_modif=0; + + if (scene_cfra>=strip->start && scene_cfra<=strip->end) + do_modif=1; + + if ((scene_cfra > strip->end) && (strip->flag & ACTSTRIP_HOLDLASTFRAME)) { + do_modif=1; - if(data->tar && data->subtarget[0]) { - bPoseChannel *pchant= get_pose_channel(data->tar->pose, data->subtarget); - if(pchant) { - float loc[3]; - - /* copy location of tip of bone? */ - if (data->flag & LOCLIKE_TIP) { - float mat[4][4], tmat[4][4]; - - Mat4One(tmat); - VECCOPY(tmat[3], pchant->pose_tail); - - armature_mat_pose_to_delta(mat, tmat, pchant->bone->arm_mat); - VECCOPY(loc, mat[3]); - } - else - VECCOPY(loc, pchant->loc); - - /* do offsets? */ - if (data->flag & LOCLIKE_OFFSET) - VecAddf(loc, loc, pchan->loc); - - if (data->flag & LOCLIKE_X) - pchan->loc[0]= FloatLerpf(loc[0], pchan->loc[0], fac); - if (data->flag & LOCLIKE_Y) - pchan->loc[1]= FloatLerpf(loc[1], pchan->loc[1], fac); - if (data->flag & LOCLIKE_Z) - pchan->loc[2]= FloatLerpf(loc[2], pchan->loc[2], fac); - if (data->flag & LOCLIKE_X_INVERT) - pchan->loc[0]= FloatLerpf(pchant->loc[0], pchan->loc[0], -fac); - if (data->flag & LOCLIKE_Y_INVERT) - pchan->loc[1]= FloatLerpf(pchant->loc[1], pchan->loc[1], -fac); - if (data->flag & LOCLIKE_Z_INVERT) - pchan->loc[2]= FloatLerpf(pchant->loc[2], pchan->loc[2], -fac); - } - } - } - break; - case CONSTRAINT_TYPE_ROTLIKE: - { - bRotateLikeConstraint *data= con->data; - if(data->tar && data->subtarget[0]) { - bPoseChannel *pchant= get_pose_channel(data->tar->pose, data->subtarget); - if(pchant) { - if(data->flag != (ROTLIKE_X|ROTLIKE_Y|ROTLIKE_Z)) { - float eul[3], eult[3], euln[3]; - float fac= con->enforce; - - QuatToEul(pchan->quat, eul); - QuatToEul(pchant->quat, eult); - VECCOPY(euln, eul); - - if(data->flag & ROTLIKE_X) euln[0]= FloatLerpf(eult[0], eul[0], fac); - if(data->flag & ROTLIKE_Y) euln[1]= FloatLerpf(eult[1], eul[1], fac); - if(data->flag & ROTLIKE_Z) euln[2]= FloatLerpf(eult[2], eul[2], fac); - if(data->flag & ROTLIKE_X_INVERT) euln[0]= FloatLerpf(eult[0], eul[0], -fac); - if(data->flag & ROTLIKE_Y_INVERT) euln[1]= FloatLerpf(eult[1], eul[1], -fac); - if(data->flag & ROTLIKE_Z_INVERT) euln[2]= FloatLerpf(eult[2], eul[2], -fac); - - compatible_eul(eul, euln); - EulToQuat(euln, pchan->quat); - } - else { - QuatInterpol(pchan->quat, pchan->quat, pchant->quat, con->enforce); - } + /* if there are any other strips active, ignore modifiers for this strip - + * 'hold' option should only hold action modifiers if there are + * no other active strips */ + for (strip2=strip->next; strip2; strip2=strip2->next) { + if (strip2 == strip) continue; + + if (scene_cfra>=strip2->start && scene_cfra<=strip2->end) { + if (!(strip2->flag & ACTSTRIP_MUTE)) + do_modif=0; } } - } - break; - case CONSTRAINT_TYPE_SIZELIKE: - { - bSizeLikeConstraint *data= con->data; - float fac= con->enforce; - if(data->tar && data->subtarget[0]) { - bPoseChannel *pchant= get_pose_channel(data->tar->pose, data->subtarget); - if(pchant) { - if (data->flag & SIZELIKE_X) - pchan->size[0]= FloatLerpf(pchant->size[0], pchan->size[0], fac); - if (data->flag & SIZELIKE_Y) - pchan->size[1]= FloatLerpf(pchant->size[1], pchan->size[1], fac); - if (data->flag & SIZELIKE_Z) - pchan->size[2]= FloatLerpf(pchant->size[2], pchan->size[2], fac); + /* if there are any later, activated, strips with 'hold' set, they take precedence, + * so ignore modifiers for this strip */ + for (strip2=strip->next; strip2; strip2=strip2->next) { + if (scene_cfra < strip2->start) continue; + if ((strip2->flag & ACTSTRIP_HOLDLASTFRAME) && !(strip2->flag & ACTSTRIP_MUTE)) { + do_modif=0; } } } - break; - case CONSTRAINT_TYPE_LOCLIMIT: - { - bLocLimitConstraint *data= con->data; - float fac= con->enforce; - - if (data->flag & LIMIT_XMIN) { - if(pchan->loc[0] < data->xmin) - pchan->loc[0] = FloatLerpf(data->xmin, pchan->loc[0], fac); - } - if (data->flag & LIMIT_XMAX) { - if (pchan->loc[0] > data->xmax) - pchan->loc[0] = FloatLerpf(data->xmax, pchan->loc[0], fac); - } - if (data->flag & LIMIT_YMIN) { - if(pchan->loc[1] < data->ymin) - pchan->loc[1] = FloatLerpf(data->ymin, pchan->loc[1], fac); - } - if (data->flag & LIMIT_YMAX) { - if (pchan->loc[1] > data->ymax) - pchan->loc[1] = FloatLerpf(data->ymax, pchan->loc[1], fac); - } - if (data->flag & LIMIT_ZMIN) { - if(pchan->loc[2] < data->zmin) - pchan->loc[2] = FloatLerpf(data->zmin, pchan->loc[2], fac); - } - if (data->flag & LIMIT_ZMAX) { - if (pchan->loc[2] > data->zmax) - pchan->loc[2] = FloatLerpf(data->zmax, pchan->loc[2], fac); - } - } - break; - case CONSTRAINT_TYPE_ROTLIMIT: - { - bRotLimitConstraint *data = con->data; - float eul[3]; - float fac= con->enforce; - - QuatToEul(pchan->quat, eul); - - /* eulers: radians to degrees! */ - eul[0] = (eul[0] / (2*M_PI) * 360); - eul[1] = (eul[1] / (2*M_PI) * 360); - eul[2] = (eul[2] / (2*M_PI) * 360); - - /* limiting of euler values... */ - if (data->flag & LIMIT_XROT) { - if (eul[0] < data->xmin) - eul[0] = FloatLerpf(data->xmin, eul[0], fac); - - if (eul[0] > data->xmax) - eul[0] = FloatLerpf(data->xmax, eul[0], fac); - } - if (data->flag & LIMIT_YROT) { - if (eul[1] < data->ymin) - eul[1] = FloatLerpf(data->ymin, eul[1], fac); - - if (eul[1] > data->ymax) - eul[1] = FloatLerpf(data->ymax, eul[1], fac); - } - if (data->flag & LIMIT_ZROT) { - if (eul[2] < data->zmin) - eul[2] = FloatLerpf(data->zmin, eul[2], fac); - - if (eul[2] > data->zmax) - eul[2] = FloatLerpf(data->zmax, eul[2], fac); - } - - /* eulers: degrees to radians ! */ - eul[0] = (eul[0] / 360 * (2*M_PI)); - eul[1] = (eul[1] / 360 * (2*M_PI)); - eul[2] = (eul[2] / 360 * (2*M_PI)); - - /* convert back */ - EulToQuat(eul, pchan->quat); - } - break; - case CONSTRAINT_TYPE_SIZELIMIT: - { - bSizeLimitConstraint *data= con->data; - float fac= con->enforce; - - if (data->flag & LIMIT_XMIN) { - if(pchan->size[0] < data->xmin) - pchan->size[0] = FloatLerpf(data->xmin, pchan->size[0], fac); - } - if (data->flag & LIMIT_XMAX) { - if (pchan->size[0] > data->xmax) - pchan->size[0] = FloatLerpf(data->xmax, pchan->size[0], fac); - } - if (data->flag & LIMIT_YMIN) { - if(pchan->size[1] < data->ymin) - pchan->size[1] = FloatLerpf(data->ymin, pchan->size[1], fac); - } - if (data->flag & LIMIT_YMAX) { - if (pchan->size[1] > data->ymax) - pchan->size[1] = FloatLerpf(data->ymax, pchan->size[1], fac); - } - if (data->flag & LIMIT_ZMIN) { - if(pchan->size[2] < data->zmin) - pchan->size[2] = FloatLerpf(data->zmin, pchan->size[2], fac); - } - if (data->flag & LIMIT_ZMAX) { - if (pchan->size[2] > data->zmax) - pchan->size[2] = FloatLerpf(data->zmax, pchan->size[2], fac); - } - } - break; - } -} - -static void do_strip_modifiers(Object *armob, Bone *bone, bPoseChannel *pchan) -{ - bActionModifier *amod; - bActionStrip *strip; - float scene_cfra= G.scene->r.cfra; - - for (strip=armob->nlastrips.first; strip; strip=strip->next) { - if(scene_cfra>=strip->start && scene_cfra<=strip->end) { - + + if (do_modif) { /* temporal solution to prevent 2 strips accumulating */ if(scene_cfra==strip->end && strip->next && strip->next->start==scene_cfra) continue; @@ -1906,32 +2013,16 @@ static void where_is_pose_bone(Object *ob, bPoseChannel *pchan, float ctime) { Bone *bone, *parbone; bPoseChannel *parchan; - float vec[3], quat[4]; - int did_local= 0; /* copying quaternion should be limited, chan_calc_mat() normalizes quat */ + float vec[3]; /* set up variables for quicker access below */ bone= pchan->bone; parbone= bone->parent; parchan= pchan->parent; - - /* Do local constraints, these only work on the channel data (loc rot size) */ - QUATCOPY(quat, pchan->quat); - if(pchan->constraints.first) { - bConstraint *con; - for(con=pchan->constraints.first; con; con= con->next) { - if(con->flag & CONSTRAINT_LOCAL) { - do_local_constraint(pchan, con); - did_local= 1; - } - } - } /* this gives a chan_mat with actions (ipos) results */ chan_calc_mat(pchan); - if(did_local) - QUATCOPY(pchan->quat, quat); /* local constraint hack. bad! */ - /* construct the posemat based on PoseChannels, that we do before applying constraints */ /* pose_mat(b)= pose_mat(b-1) * yoffs(b-1) * d_root(b) * bone_mat(b) * chan_mat(b) */ @@ -1961,6 +2052,21 @@ static void where_is_pose_bone(Object *ob, bPoseChannel *pchan, float ctime) Mat4MulSerie(pchan->pose_mat, tmat, offs_bone, pchan->chan_mat, NULL, NULL, NULL, NULL, NULL); } + else if(bone->flag & BONE_NO_SCALE) { + float orthmat[4][4], vec[3]; + + /* get the official transform, but we only use the vector from it (optimize...) */ + Mat4MulSerie(pchan->pose_mat, parchan->pose_mat, offs_bone, pchan->chan_mat, NULL, NULL, NULL, NULL, NULL); + VECCOPY(vec, pchan->pose_mat[3]); + + /* do this again, but with an ortho-parent matrix */ + Mat4CpyMat4(orthmat, parchan->pose_mat); + Mat4Ortho(orthmat); + Mat4MulSerie(pchan->pose_mat, orthmat, offs_bone, pchan->chan_mat, NULL, NULL, NULL, NULL, NULL); + + /* copy correct transform */ + VECCOPY(pchan->pose_mat[3], vec); + } else Mat4MulSerie(pchan->pose_mat, parchan->pose_mat, offs_bone, pchan->chan_mat, NULL, NULL, NULL, NULL, NULL); } @@ -1970,44 +2076,33 @@ static void where_is_pose_bone(Object *ob, bPoseChannel *pchan, float ctime) VecAddf(pchan->pose_mat[3], pchan->pose_mat[3], ob->pose->cyclic_offset); } + /* do NLA strip modifiers - i.e. curve follow */ do_strip_modifiers(ob, bone, pchan); /* Do constraints */ - if(pchan->constraints.first) { - static Object conOb; - static int initialized= 0; + if (pchan->constraints.first) { + bConstraintOb *cob; + /* make a copy of location of PoseChannel for later */ VECCOPY(vec, pchan->pose_mat[3]); - /* Build a workob to pass the bone to the constraint solver */ - if(initialized==0) { - memset(&conOb, 0, sizeof(Object)); - initialized= 1; - } - conOb.size[0]= conOb.size[1]= conOb.size[2]= 1.0; - conOb.data = ob->data; - conOb.type = ob->type; - conOb.parent = ob; // ik solver retrieves the armature that way !?!?!?! - conOb.pose= ob->pose; // needed for retrieving pchan - conOb.trackflag = ob->trackflag; - conOb.upflag = ob->upflag; - - /* Collect the constraints from the pose (listbase copy) */ - conOb.constraints = pchan->constraints; - - /* conOb.obmat takes bone to worldspace */ - Mat4MulMat4 (conOb.obmat, pchan->pose_mat, ob->obmat); + /* prepare PoseChannel for Constraint solving + * - makes a copy of matrix, and creates temporary struct to use + */ + cob= constraints_make_evalob(ob, pchan, CONSTRAINT_OBTYPE_BONE); - /* Solve */ - solve_constraints (&conOb, TARGET_BONE, (void*)pchan, ctime); // ctime doesnt alter objects + /* Solve PoseChannel's Constraints */ + solve_constraints(&pchan->constraints, cob, ctime); // ctime doesnt alter objects - /* Take out of worldspace */ - Mat4MulMat4 (pchan->pose_mat, conOb.obmat, ob->imat); + /* cleanup after Constraint Solving + * - applies matrix back to pchan, and frees temporary struct used + */ + constraints_clear_evalob(cob); /* prevent constraints breaking a chain */ - if(pchan->bone->flag & BONE_CONNECTED) + if(pchan->bone->flag & BONE_CONNECTED) { VECCOPY(pchan->pose_mat[3], vec); - + } } /* calculate head */ @@ -2016,7 +2111,6 @@ static void where_is_pose_bone(Object *ob, bPoseChannel *pchan, float ctime) VECCOPY(vec, pchan->pose_mat[1]); VecMulf(vec, bone->length); VecAddf(pchan->pose_tail, pchan->pose_head, vec); - } /* This only reads anim data from channels, and writes to channels */ @@ -2027,7 +2121,7 @@ void where_is_pose (Object *ob) Bone *bone; bPoseChannel *pchan; float imat[4][4]; - float ctime= bsystem_time(ob, NULL, (float)G.scene->r.cfra, 0.0); /* not accurate... */ + float ctime= bsystem_time(ob, (float)G.scene->r.cfra, 0.0); /* not accurate... */ arm = get_armature(ob); diff --git a/source/blender/blenkernel/intern/blender.c b/source/blender/blenkernel/intern/blender.c index cad8d3b0861..9845f571126 100644 --- a/source/blender/blenkernel/intern/blender.c +++ b/source/blender/blenkernel/intern/blender.c @@ -266,7 +266,7 @@ static void clear_global(void) free_vertexpaint(); - G.f &= ~(G_WEIGHTPAINT + G_VERTEXPAINT + G_FACESELECT); + G.f &= ~(G_WEIGHTPAINT + G_VERTEXPAINT + G_FACESELECT + G_PARTICLEEDIT); } /* make sure path names are correct for OS */ @@ -409,8 +409,9 @@ static void setup_app_data(BlendFileData *bfd, char *filename) /* there's an onload scriptlink to execute in screenmain */ mainqenter(ONLOAD_SCRIPT, 1); } - - strcpy(G.sce, filename); + if (G.sce != filename) /* these are the same at times, should never copy to the same location */ + strcpy(G.sce, filename); + strcpy(G.main->name, filename); /* is guaranteed current file */ MEM_freeN(bfd); diff --git a/source/blender/blenkernel/intern/cdderivedmesh.c b/source/blender/blenkernel/intern/cdderivedmesh.c index 2d5f5f091c3..6a856307916 100644 --- a/source/blender/blenkernel/intern/cdderivedmesh.c +++ b/source/blender/blenkernel/intern/cdderivedmesh.c @@ -751,6 +751,7 @@ DerivedMesh *CDDM_from_mesh(Mesh *mesh, Object *ob) /* this does a referenced copy, the only new layers being ORIGINDEX */ DM_init(dm, mesh->totvert, mesh->totedge, mesh->totface); + dm->deformedOnly = 1; CustomData_merge(&mesh->vdata, &dm->vertData, CD_MASK_MESH, CD_REFERENCE, mesh->totvert); @@ -798,6 +799,8 @@ DerivedMesh *CDDM_from_editmesh(EditMesh *em, Mesh *me) MFace *mface = cddm->mface; int i, *index; + dm->deformedOnly = 1; + CustomData_merge(&em->vdata, &dm->vertData, CD_MASK_DERIVEDMESH, CD_CALLOC, dm->numVertData); /* CustomData_merge(&em->edata, &dm->edgeData, CD_MASK_DERIVEDMESH, @@ -889,6 +892,7 @@ DerivedMesh *CDDM_copy(DerivedMesh *source) /* this initializes dm, and copies all non mvert/medge/mface layers */ DM_from_template(dm, source, numVerts, numEdges, numFaces); + dm->deformedOnly = source->deformedOnly; CustomData_copy_data(&source->vertData, &dm->vertData, 0, 0, numVerts); CustomData_copy_data(&source->edgeData, &dm->edgeData, 0, 0, numEdges); diff --git a/source/blender/blenkernel/intern/colortools.c b/source/blender/blenkernel/intern/colortools.c index fda31d9e7c0..83b014cdd63 100644 --- a/source/blender/blenkernel/intern/colortools.c +++ b/source/blender/blenkernel/intern/colortools.c @@ -62,12 +62,18 @@ CurveMapping *curvemapping_add(int tot, float minx, float miny, float maxx, floa { CurveMapping *cumap; int a; + float clipminx, clipminy, clipmaxx, clipmaxy; cumap= MEM_callocN(sizeof(CurveMapping), "new curvemap"); cumap->flag= CUMA_DO_CLIP; if(tot==4) cumap->cur= 3; /* rhms, hack for 'col' curve? */ - BLI_init_rctf(&cumap->curr, minx, maxx, miny, maxy); + clipminx = MIN2(minx, maxx); + clipminy = MIN2(miny, maxy); + clipmaxx = MAX2(minx, maxx); + clipmaxy = MAX2(miny, maxy); + + BLI_init_rctf(&cumap->curr, clipminx, clipmaxx, clipminy, clipmaxy); cumap->clipr= cumap->curr; cumap->white[0]= cumap->white[1]= cumap->white[2]= 1.0f; diff --git a/source/blender/blenkernel/intern/constraint.c b/source/blender/blenkernel/intern/constraint.c index 97f5ed18cbe..2dc488f8377 100644 --- a/source/blender/blenkernel/intern/constraint.c +++ b/source/blender/blenkernel/intern/constraint.c @@ -25,7 +25,7 @@ * * The Original Code is: all of this file. * - * Contributor(s): none yet. + * Contributor(s): 2007, Joshua Leung, major recode * * ***** END GPL/BL DUAL LICENSE BLOCK ***** */ @@ -45,19 +45,29 @@ #include "DNA_object_types.h" #include "DNA_action_types.h" #include "DNA_curve_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_lattice_types.h" #include "DNA_scene_types.h" +#include "DNA_text_types.h" #include "BKE_utildefines.h" #include "BKE_action.h" -#include "BKE_anim.h" // for the curve calculation part +#include "BKE_anim.h" /* for the curve calculation part */ #include "BKE_armature.h" #include "BKE_blender.h" #include "BKE_constraint.h" #include "BKE_displist.h" +#include "BKE_deform.h" +#include "BKE_DerivedMesh.h" /* for geometry targets */ +#include "BKE_cdderivedmesh.h" /* for geometry targets */ #include "BKE_object.h" #include "BKE_ipo.h" #include "BKE_global.h" #include "BKE_library.h" +#include "BKE_idprop.h" + + +#include "BPY_extern.h" #include "blendef.h" @@ -69,35 +79,19 @@ #define M_PI 3.14159265358979323846 #endif -/* used by object.c */ -void Mat4BlendMat4(float [][4], float [][4], float [][4], float ); - -/* Local function prototypes */ -/* ********************* Data level ****************** */ - -void free_constraint_data (bConstraint *con) -{ - if (con->data) { - /* any constraint-type specific stuff here */ - - MEM_freeN(con->data); - } -} - -void free_constraints (ListBase *conlist) -{ - bConstraint *con; - - /* Do any specific freeing */ - for (con=conlist->first; con; con=con->next) { - free_constraint_data(con); - } - - /* Free the whole list */ - BLI_freelistN(conlist); -} +/* ******************* Constraint Channels ********************** */ +/* Constraint Channels exist in one of two places: + * - Under Action Channels in an Action (act->chanbase->achan->constraintChannels) + * - Under Object without object-level action yet (ob->constraintChannels) + * + * The main purpose that constraint channels serve is to act as a link + * between an IPO-block which + */ +/* ------------ Data Management ----------- */ + +/* Free constraint channels, and reduce the number of users of the related ipo-blocks */ void free_constraint_channels (ListBase *chanbase) { bConstraintChannel *chan; @@ -111,117 +105,14 @@ void free_constraint_channels (ListBase *chanbase) BLI_freelistN(chanbase); } -void relink_constraints (struct ListBase *list) -{ - bConstraint *con; - - for (con = list->first; con; con=con->next) { - switch (con->type) { - case CONSTRAINT_TYPE_KINEMATIC: - { - bKinematicConstraint *data; - data = con->data; - - ID_NEW(data->tar); - } - break; - case CONSTRAINT_TYPE_TRACKTO: - { - bTrackToConstraint *data; - data = con->data; - - ID_NEW(data->tar); - } - break; - case CONSTRAINT_TYPE_MINMAX: - { - bMinMaxConstraint *data; - data = con->data; - - ID_NEW(data->tar); - } - break; - case CONSTRAINT_TYPE_LOCKTRACK: - { - bLockTrackConstraint *data; - data = con->data; - - ID_NEW(data->tar); - } - break; - case CONSTRAINT_TYPE_ACTION: - { - bActionConstraint *data; - data = con->data; - - ID_NEW(data->tar); - } - break; - case CONSTRAINT_TYPE_LOCLIKE: - { - bLocateLikeConstraint *data; - data = con->data; - - ID_NEW(data->tar); - } - break; - case CONSTRAINT_TYPE_ROTLIKE: - { - bRotateLikeConstraint *data; - data = con->data; - - ID_NEW(data->tar); - } - break; - case CONSTRAINT_TYPE_SIZELIKE: - { - bSizeLikeConstraint *data; - data = con->data; - - ID_NEW(data->tar); - } - break; - case CONSTRAINT_TYPE_FOLLOWPATH: - { - bFollowPathConstraint *data; - data = con->data; - - ID_NEW(data->tar); - } - break; - case CONSTRAINT_TYPE_STRETCHTO: - { - bStretchToConstraint *data; - data = con->data; - - ID_NEW(data->tar); - } - break; - case CONSTRAINT_TYPE_RIGIDBODYJOINT: - { - bRigidBodyJointConstraint *data; - data = con->data; - - ID_NEW(data->tar); - } - break; - case CONSTRAINT_TYPE_CLAMPTO: - { - bClampToConstraint *data; - data = con->data; - - ID_NEW(data->tar); - } - break; - } - } -} - +/* Make a copy of the constraint channels from dst to src, and also give the + * new constraint channels their own copy of the original's IPO. + */ void copy_constraint_channels (ListBase *dst, ListBase *src) { bConstraintChannel *dchan, *schan; - dst->first=dst->last=NULL; + dst->first = dst->last = NULL; duplicatelist(dst, src); for (dchan=dst->first, schan=src->first; dchan; dchan=dchan->next, schan=schan->next) { @@ -229,11 +120,14 @@ void copy_constraint_channels (ListBase *dst, ListBase *src) } } +/* Make a copy of the constraint channels from dst to src, but make the + * new constraint channels use the same IPO-data as their twin. + */ void clone_constraint_channels (ListBase *dst, ListBase *src) { bConstraintChannel *dchan, *schan; - dst->first=dst->last=NULL; + dst->first = dst->last = NULL; duplicatelist(dst, src); for (dchan=dst->first, schan=src->first; dchan; dchan=dchan->next, schan=schan->next) { @@ -241,664 +135,919 @@ void clone_constraint_channels (ListBase *dst, ListBase *src) } } -void copy_constraints (ListBase *dst, ListBase *src) -{ - bConstraint *con; - - dst->first= dst->last= NULL; - - duplicatelist (dst, src); - - for (con = dst->first; con; con=con->next) { - con->data = MEM_dupallocN (con->data); - } -} +/* ------------- Constraint Channel Tools ------------ */ - -/* **************** Editor Functions **************** */ - -char constraint_has_target (bConstraint *con) +/* Find the constraint channel with a given name */ +bConstraintChannel *get_constraint_channel (ListBase *list, const char name[]) { - switch (con->type) { - case CONSTRAINT_TYPE_TRACKTO: - { - bTrackToConstraint *data = con->data; - if (data->tar) - return 1; - } - break; - case CONSTRAINT_TYPE_KINEMATIC: - { - bKinematicConstraint *data = con->data; - if (data->tar) - return 1; - } - break; - case CONSTRAINT_TYPE_FOLLOWPATH: - { - bFollowPathConstraint *data = con->data; - if (data->tar) - return 1; - } - break; - case CONSTRAINT_TYPE_ROTLIKE: - { - bRotateLikeConstraint *data = con->data; - if (data->tar) - return 1; - } - break; - case CONSTRAINT_TYPE_LOCLIKE: - { - bLocateLikeConstraint *data = con->data; - if (data->tar) - return 1; - } - break; - case CONSTRAINT_TYPE_SIZELIKE: - { - bSizeLikeConstraint *data = con->data; - if (data->tar) - return 1; - } - break; - case CONSTRAINT_TYPE_MINMAX: - { - bMinMaxConstraint *data = con->data; - if (data->tar) - return 1; - } - break; - case CONSTRAINT_TYPE_ACTION: - { - bActionConstraint *data = con->data; - if (data->tar) - return 1; - } - break; - case CONSTRAINT_TYPE_LOCKTRACK: - { - bLockTrackConstraint *data = con->data; - if (data->tar) - return 1; - } - case CONSTRAINT_TYPE_STRETCHTO: - { - bStretchToConstraint *data = con->data; - if (data->tar) - return 1; - } - break; - case CONSTRAINT_TYPE_RIGIDBODYJOINT: - { - bRigidBodyJointConstraint *data = con->data; - if (data->tar) - return 1; - } - break; - case CONSTRAINT_TYPE_CLAMPTO: - { - bClampToConstraint *data = con->data; - if (data->tar) - return 1; + bConstraintChannel *chan; + + for (chan = list->first; chan; chan=chan->next) { + if (!strcmp(name, chan->name)) { + return chan; } - break; } - // Unknown types or CONSTRAINT_TYPE_NULL or no target - return 0; + + return NULL; } -Object *get_constraint_target(bConstraint *con, char **subtarget) +/* Find or create a new constraint channel */ +bConstraintChannel *verify_constraint_channel (ListBase *list, const char name[]) { - /* If the target for this constraint is target, return a pointer - * to the name for this constraints subtarget ... NULL otherwise - */ - switch (con->type) { - case CONSTRAINT_TYPE_ACTION: - { - bActionConstraint *data = con->data; - *subtarget= data->subtarget; - return data->tar; - } - break; - case CONSTRAINT_TYPE_LOCLIKE: - { - bLocateLikeConstraint *data = con->data; - *subtarget= data->subtarget; - return data->tar; - } - break; - case CONSTRAINT_TYPE_ROTLIKE: - { - bRotateLikeConstraint *data = con->data; - *subtarget= data->subtarget; - return data->tar; - } - break; - case CONSTRAINT_TYPE_SIZELIKE: - { - bSizeLikeConstraint *data = con->data; - *subtarget= data->subtarget; - return data->tar; - } - break; - case CONSTRAINT_TYPE_KINEMATIC: - { - bKinematicConstraint *data = con->data; - *subtarget= data->subtarget; - return data->tar; - } - break; - case CONSTRAINT_TYPE_TRACKTO: - { - bTrackToConstraint *data = con->data; - *subtarget= data->subtarget; - return data->tar; - } - break; - case CONSTRAINT_TYPE_MINMAX: - { - bMinMaxConstraint *data = con->data; - *subtarget= data->subtarget; - return data->tar; - } - break; - case CONSTRAINT_TYPE_LOCKTRACK: - { - bLockTrackConstraint *data = con->data; - *subtarget= data->subtarget; - return data->tar; - } - break; - case CONSTRAINT_TYPE_FOLLOWPATH: - { - bFollowPathConstraint *data = con->data; - *subtarget= NULL; - return data->tar; - } - break; - case CONSTRAINT_TYPE_STRETCHTO: - { - bStretchToConstraint *data = con->data; - *subtarget= data->subtarget; - return (data->tar); - } - break; - case CONSTRAINT_TYPE_RIGIDBODYJOINT: - { - bRigidBodyJointConstraint *data = con->data; - *subtarget= NULL; - return data->tar; - } - break; - case CONSTRAINT_TYPE_CLAMPTO: - { - bClampToConstraint *data = con->data; - *subtarget= NULL; - return data->tar; - } - break; - default: - *subtarget= NULL; - break; + bConstraintChannel *chan; + + chan= get_constraint_channel(list, name); + + if (chan == NULL) { + chan= MEM_callocN(sizeof(bConstraintChannel), "new constraint channel"); + BLI_addtail(list, chan); + strcpy(chan->name, name); } - return NULL; + return chan; } -void set_constraint_target(bConstraint *con, Object *ob, char *subtarget) +/* --------- Constraint Channel Evaluation/Execution --------- */ + +/* IPO-system call: calculate IPO-block for constraint channels, and flush that + * info onto the corresponding constraint. + */ +void do_constraint_channels (ListBase *conbase, ListBase *chanbase, float ctime, short onlydrivers) { - /* Set the target for this constraint */ - switch (con->type) { - case CONSTRAINT_TYPE_ACTION: - { - bActionConstraint *data = con->data; - data->tar= ob; - if(subtarget) BLI_strncpy(data->subtarget, subtarget, 32); - } - break; - case CONSTRAINT_TYPE_LOCLIKE: - { - bLocateLikeConstraint *data = con->data; - data->tar= ob; - if(subtarget) BLI_strncpy(data->subtarget, subtarget, 32); - } - break; - case CONSTRAINT_TYPE_ROTLIKE: - { - bRotateLikeConstraint *data = con->data; - data->tar= ob; - if(subtarget) BLI_strncpy(data->subtarget, subtarget, 32); - } - break; - case CONSTRAINT_TYPE_SIZELIKE: - { - bSizeLikeConstraint *data = con->data; - data->tar= ob; - if(subtarget) BLI_strncpy(data->subtarget, subtarget, 32); - } - break; - case CONSTRAINT_TYPE_KINEMATIC: - { - bKinematicConstraint *data = con->data; - data->tar= ob; - if(subtarget) BLI_strncpy(data->subtarget, subtarget, 32); - } - break; - case CONSTRAINT_TYPE_TRACKTO: - { - bTrackToConstraint *data = con->data; - data->tar= ob; - if(subtarget) BLI_strncpy(data->subtarget, subtarget, 32); - } - break; - case CONSTRAINT_TYPE_LOCKTRACK: - { - bLockTrackConstraint *data = con->data; - data->tar= ob; - if(subtarget) BLI_strncpy(data->subtarget, subtarget, 32); - } - break; - case CONSTRAINT_TYPE_FOLLOWPATH: - { - bFollowPathConstraint *data = con->data; - data->tar= ob; - } - break; - case CONSTRAINT_TYPE_STRETCHTO: - { - bStretchToConstraint *data = con->data; - data->tar= ob; - if(subtarget) BLI_strncpy(data->subtarget, subtarget, 32); - } - break; - case CONSTRAINT_TYPE_RIGIDBODYJOINT: - { - bRigidBodyJointConstraint *data = con->data; - data->tar= ob; - } - break; - case CONSTRAINT_TYPE_MINMAX: - { - bMinMaxConstraint *data = (bMinMaxConstraint*)con->data; - data->tar= ob; - if(subtarget) BLI_strncpy(data->subtarget, subtarget, 32); - } - break; - case CONSTRAINT_TYPE_CLAMPTO: - { - bClampToConstraint *data = con->data; - data->tar= ob; + bConstraint *con; + bConstraintChannel *chan; + IpoCurve *icu= NULL; + + /* for each Constraint, calculate its Influence from the corresponding ConstraintChannel */ + for (con=conbase->first; con; con=con->next) { + chan = get_constraint_channel(chanbase, con->name); + + if (chan && chan->ipo) { + calc_ipo(chan->ipo, ctime); + + for (icu=chan->ipo->curve.first; icu; icu=icu->next) { + if (!onlydrivers || icu->driver) { + switch (icu->adrcode) { + case CO_ENFORCE: + { + /* Influence is clamped to 0.0f -> 1.0f range */ + con->enforce = CLAMPIS(icu->curval, 0.0f, 1.0f); + } + break; + case CO_HEADTAIL: + { + con->headtail = icu->curval; + } + break; + } + } + } } - break; } } +/* ************************ Constraints - General Utilities *************************** */ +/* These functions here don't act on any specific constraints, and are therefore should/will + * not require any of the special function-pointers afforded by the relevant constraint + * type-info structs. + */ + +/* -------------- Naming -------------- */ + +/* Find the first available, non-duplicate name for a given constraint */ void unique_constraint_name (bConstraint *con, ListBase *list) { - char tempname[64]; - int number; - char *dot; - int exists = 0; bConstraint *curcon; + char tempname[64]; + int number = 1, exists = 0; + char *dot; /* See if we are given an empty string */ if (con->name[0] == '\0') { /* give it default name first */ - strcpy (con->name, "Const"); + strcpy(con->name, "Const"); } /* See if we even need to do this */ - for (curcon = list->first; curcon; curcon=curcon->next){ - if (curcon!=con){ - if (!strcmp(curcon->name, con->name)){ + if (list == NULL) + return; + + for (curcon = list->first; curcon; curcon=curcon->next) { + if (curcon != con) { + if (!strcmp(curcon->name, con->name)) { exists = 1; break; } } } - if (!exists) + if (exists == 0) return; /* Strip off the suffix */ - dot=strchr(con->name, '.'); + dot = strchr(con->name, '.'); if (dot) *dot=0; - for (number = 1; number <=999; number++){ - sprintf (tempname, "%s.%03d", con->name, number); + for (number = 1; number <= 999; number++) { + sprintf(tempname, "%s.%03d", con->name, number); exists = 0; - for (curcon=list->first; curcon; curcon=curcon->next){ - if (con!=curcon){ - if (!strcmp (curcon->name, tempname)){ + for (curcon=list->first; curcon; curcon=curcon->next) { + if (con != curcon) { + if (strcmp(curcon->name, tempname)==0) { exists = 1; break; } } } - if (!exists){ - strcpy (con->name, tempname); + if (exists == 0) { + strcpy(con->name, tempname); return; } } } -void *new_constraint_data (short type) +/* ----------------- Evaluation Loop Preparation --------------- */ + +/* package an object/bone for use in constraint evaluation */ +/* This function MEM_calloc's a bConstraintOb struct, that will need to be freed after evaluation */ +bConstraintOb *constraints_make_evalob (Object *ob, void *subdata, short datatype) { - void *result; + bConstraintOb *cob; - switch (type) { - case CONSTRAINT_TYPE_KINEMATIC: - { - bKinematicConstraint *data; - data = MEM_callocN(sizeof(bKinematicConstraint), "kinematicConstraint"); - - data->weight= (float)1.0; - data->orientweight= (float)1.0; - data->iterations = 500; - data->flag= CONSTRAINT_IK_TIP|CONSTRAINT_IK_STRETCH|CONSTRAINT_IK_POS; - - result = data; - } - break; - case CONSTRAINT_TYPE_TRACKTO: - { - bTrackToConstraint *data; - data = MEM_callocN(sizeof(bTrackToConstraint), "tracktoConstraint"); - - data->reserved1 = TRACK_Y; - data->reserved2 = UP_Z; - - result = data; - } - break; - case CONSTRAINT_TYPE_MINMAX: - { - bMinMaxConstraint *data; - data = MEM_callocN(sizeof(bMinMaxConstraint), "minmaxConstraint"); - - data->minmaxflag = TRACK_Z; - data->offset = 0.0f; - data->cache[0] = data->cache[1] = data->cache[2] = 0.0f; - data->flag = 0; + /* create regardless of whether we have any data! */ + cob= MEM_callocN(sizeof(bConstraintOb), "bConstraintOb"); + + /* based on type of available data */ + switch (datatype) { + case CONSTRAINT_OBTYPE_OBJECT: + { + /* disregard subdata... calloc should set other values right */ + if (ob) { + cob->ob = ob; + cob->type = datatype; + Mat4CpyMat4(cob->matrix, ob->obmat); + } + else + Mat4One(cob->matrix); - result = data; - } - break; - case CONSTRAINT_TYPE_ROTLIKE: - { - bRotateLikeConstraint *data; - data = MEM_callocN(sizeof(bRotateLikeConstraint), "rotlikeConstraint"); - data->flag = ROTLIKE_X|ROTLIKE_Y|ROTLIKE_Z; - result = data; - } - break; - case CONSTRAINT_TYPE_LOCLIKE: - { - bLocateLikeConstraint *data; - data = MEM_callocN(sizeof(bLocateLikeConstraint), "loclikeConstraint"); - data->flag = LOCLIKE_X|LOCLIKE_Y|LOCLIKE_Z; - result = data; + Mat4CpyMat4(cob->startmat, cob->matrix); } - break; - case CONSTRAINT_TYPE_SIZELIKE: + break; + case CONSTRAINT_OBTYPE_BONE: { - bSizeLikeConstraint *data; - data = MEM_callocN(sizeof(bLocateLikeConstraint), "sizelikeConstraint"); - data->flag |= SIZELIKE_X|SIZELIKE_Y|SIZELIKE_Z; - result = data; + /* only set if we have valid bone, otherwise default */ + if (ob && subdata) { + cob->ob = ob; + cob->pchan = (bPoseChannel *)subdata; + cob->type = datatype; + + /* matrix in world-space */ + Mat4MulMat4(cob->matrix, cob->pchan->pose_mat, ob->obmat); + } + else + Mat4One(cob->matrix); + + Mat4CpyMat4(cob->startmat, cob->matrix); } - break; - case CONSTRAINT_TYPE_ACTION: - { - bActionConstraint *data; - data = MEM_callocN(sizeof(bActionConstraint), "actionConstraint"); - data->local= 1; + break; - result = data; - } - break; - case CONSTRAINT_TYPE_LOCKTRACK: - { - bLockTrackConstraint *data; - data = MEM_callocN(sizeof(bLockTrackConstraint), "locktrackConstraint"); - - data->trackflag = TRACK_Y; - data->lockflag = LOCK_Z; - - result = data; - } - break; - case CONSTRAINT_TYPE_FOLLOWPATH: - { - bFollowPathConstraint *data; - data = MEM_callocN(sizeof(bFollowPathConstraint), "followpathConstraint"); - - data->trackflag = TRACK_Y; - data->upflag = UP_Z; - data->offset = 0; - data->followflag = 0; - - result = data; - } - break; - case CONSTRAINT_TYPE_STRETCHTO: - { - bStretchToConstraint *data; - data = MEM_callocN(sizeof(bStretchToConstraint), "StretchToConstraint"); + default: /* other types not yet handled */ + Mat4One(cob->matrix); + Mat4One(cob->startmat); + break; + } + + return cob; +} - data->volmode = 0; - data->plane = 0; - data->orglength = 0.0; - data->bulge = 1.0; - result = data; - } - break; - case CONSTRAINT_TYPE_LOCLIMIT: - { - bLocLimitConstraint *data; - data = MEM_callocN(sizeof(bLocLimitConstraint), "LocLimitConstraint"); - result = data; - } - break; - case CONSTRAINT_TYPE_ROTLIMIT: - { - bRotLimitConstraint *data; - data = MEM_callocN(sizeof(bRotLimitConstraint), "RotLimitConstraint"); - result = data; - } - break; - case CONSTRAINT_TYPE_SIZELIMIT: - { - bSizeLimitConstraint *data; - data = MEM_callocN(sizeof(bSizeLimitConstraint), "SizeLimitConstraint"); - result = data; - } - break; - case CONSTRAINT_TYPE_RIGIDBODYJOINT: - { - bRigidBodyJointConstraint *data; - int i; - Base *base_iter; - - data = MEM_callocN(sizeof(bRigidBodyJointConstraint), "RigidBodyToConstraint"); - base_iter = G.scene->base.first; - while( base_iter && !data->tar ) { - if( ( ( base_iter->flag & SELECT ) && -// ( base_iter->lay & G.vd->lay ) ) && - ( base_iter != G.scene->basact ) ) - ) - data->tar=base_iter->object; - base_iter = base_iter->next; - } - data->type=1; - data->pivX=0.0; - data->pivY=0.0; - data->pivZ=0.0; - data->axX=0.0; - data->axY=0.0; - data->axZ=0.0; - for (i=0;i<6;i++) - { - data->minLimit[i]=0.0; - data->maxLimit[i]=0.0; +/* cleanup after constraint evaluation */ +void constraints_clear_evalob (bConstraintOb *cob) +{ + float delta[4][4], imat[4][4]; + + /* prevent crashes */ + if (cob == NULL) + return; + + /* calculate delta of constraints evaluation */ + Mat4Invert(imat, cob->startmat); + Mat4MulMat4(delta, cob->matrix, imat); + + /* copy matrices back to source */ + switch (cob->type) { + case CONSTRAINT_OBTYPE_OBJECT: + { + /* cob->ob might not exist! */ + if (cob->ob) { + /* copy new ob-matrix back to owner */ + Mat4CpyMat4(cob->ob->obmat, cob->matrix); + + /* copy inverse of delta back to owner */ + Mat4Invert(cob->ob->constinv, delta); } - data->extraFz=0.0; - result = data; } - break; - case CONSTRAINT_TYPE_CLAMPTO: + break; + case CONSTRAINT_OBTYPE_BONE: { - bClampToConstraint *data; - data = MEM_callocN(sizeof(bClampToConstraint), "ClampToConstraint"); - result = data; + /* cob->ob or cob->pchan might not exist */ + if (cob->ob && cob->pchan) { + /* copy new pose-matrix back to owner */ + Mat4MulMat4(cob->pchan->pose_mat, cob->matrix, cob->ob->imat); + + /* copy inverse of delta back to owner */ + Mat4Invert(cob->pchan->constinv, delta); + } } - break; - - default: - result = NULL; - break; + break; } - - return result; + + /* free tempolary struct */ + MEM_freeN(cob); } -bConstraintChannel *get_constraint_channel (ListBase *list, const char *name) -{ - bConstraintChannel *chan; +/* -------------- Space-Conversion API -------------- */ - for (chan = list->first; chan; chan=chan->next) { - if (!strcmp(name, chan->name)) { - return chan; +/* This function is responsible for the correct transformations/conversions + * of a matrix from one space to another for constraint evaluation. + * For now, this is only implemented for Objects and PoseChannels. + */ +void constraint_mat_convertspace (Object *ob, bPoseChannel *pchan, float mat[][4], short from, short to) +{ + float tempmat[4][4]; + float diff_mat[4][4]; + float imat[4][4]; + + /* prevent crashes in these unlikely events */ + if (ob==NULL || mat==NULL) return; + /* optimise trick - check if need to do anything */ + if (from == to) return; + + /* are we dealing with pose-channels or objects */ + if (pchan) { + /* pose channels */ + switch (from) { + case CONSTRAINT_SPACE_WORLD: /* ---------- FROM WORLDSPACE ---------- */ + { + /* world to pose */ + if (to==CONSTRAINT_SPACE_POSE || to==CONSTRAINT_SPACE_LOCAL || to==CONSTRAINT_SPACE_PARLOCAL) { + Mat4Invert(imat, ob->obmat); + Mat4CpyMat4(tempmat, mat); + Mat4MulMat4(mat, tempmat, imat); + } + + /* pose to local */ + if (to == CONSTRAINT_SPACE_LOCAL) { + /* call self with slightly different values */ + constraint_mat_convertspace(ob, pchan, mat, CONSTRAINT_SPACE_POSE, to); + } + /* pose to local + parent */ + else if (to == CONSTRAINT_SPACE_PARLOCAL) { + /* call self with slightly different values */ + constraint_mat_convertspace(ob, pchan, mat, CONSTRAINT_SPACE_POSE, to); + } + } + break; + case CONSTRAINT_SPACE_POSE: /* ---------- FROM POSESPACE ---------- */ + { + /* pose to world */ + if (to == CONSTRAINT_SPACE_WORLD) { + Mat4CpyMat4(tempmat, mat); + Mat4MulMat4(mat, tempmat, ob->obmat); + } + /* pose to local */ + else if (to == CONSTRAINT_SPACE_LOCAL) { + if (pchan->bone) { + if (pchan->parent) { + float offs_bone[4][4]; + + /* construct offs_bone the same way it is done in armature.c */ + Mat4CpyMat3(offs_bone, pchan->bone->bone_mat); + VECCOPY(offs_bone[3], pchan->bone->head); + offs_bone[3][1]+= pchan->bone->parent->length; + + if (pchan->bone->flag & BONE_HINGE) { + /* pose_mat = par_pose-space_location * chan_mat */ + float tmat[4][4]; + + /* the rotation of the parent restposition */ + Mat4CpyMat4(tmat, pchan->bone->parent->arm_mat); + + /* the location of actual parent transform */ + VECCOPY(tmat[3], offs_bone[3]); + offs_bone[3][0]= offs_bone[3][1]= offs_bone[3][2]= 0.0f; + Mat4MulVecfl(pchan->parent->pose_mat, tmat[3]); + + Mat4MulMat4(diff_mat, offs_bone, tmat); + Mat4Invert(imat, diff_mat); + } + else { + /* pose_mat = par_pose_mat * bone_mat * chan_mat */ + Mat4MulMat4(diff_mat, offs_bone, pchan->parent->pose_mat); + Mat4Invert(imat, diff_mat); + } + } + else { + /* pose_mat = chan_mat * arm_mat */ + Mat4Invert(imat, pchan->bone->arm_mat); + } + + Mat4CpyMat4(tempmat, mat); + Mat4MulMat4(mat, tempmat, imat); + } + } + /* pose to local with parent */ + else if (to == CONSTRAINT_SPACE_PARLOCAL) { + if (pchan->bone) { + Mat4Invert(imat, pchan->bone->arm_mat); + Mat4CpyMat4(tempmat, mat); + Mat4MulMat4(mat, tempmat, imat); + } + } + } + break; + case CONSTRAINT_SPACE_LOCAL: /* ------------ FROM LOCALSPACE --------- */ + { + /* local to pose */ + if (to==CONSTRAINT_SPACE_POSE || to==CONSTRAINT_SPACE_WORLD) { + /* do inverse procedure that was done for pose to local */ + if (pchan->bone) { + /* we need the posespace_matrix = local_matrix + (parent_posespace_matrix + restpos) */ + if (pchan->parent) { + float offs_bone[4][4]; + + /* construct offs_bone the same way it is done in armature.c */ + Mat4CpyMat3(offs_bone, pchan->bone->bone_mat); + VECCOPY(offs_bone[3], pchan->bone->head); + offs_bone[3][1]+= pchan->bone->parent->length; + + if (pchan->bone->flag & BONE_HINGE) { + /* pose_mat = par_pose-space_location * chan_mat */ + float tmat[4][4]; + + /* the rotation of the parent restposition */ + Mat4CpyMat4(tmat, pchan->bone->parent->arm_mat); + + /* the location of actual parent transform */ + VECCOPY(tmat[3], offs_bone[3]); + offs_bone[3][0]= offs_bone[3][1]= offs_bone[3][2]= 0.0f; + Mat4MulVecfl(pchan->parent->pose_mat, tmat[3]); + + Mat4MulMat4(diff_mat, offs_bone, tmat); + Mat4CpyMat4(tempmat, mat); + Mat4MulMat4(mat, tempmat, diff_mat); + } + else { + /* pose_mat = par_pose_mat * bone_mat * chan_mat */ + Mat4MulMat4(diff_mat, offs_bone, pchan->parent->pose_mat); + Mat4CpyMat4(tempmat, mat); + Mat4MulMat4(mat, tempmat, diff_mat); + } + } + else { + Mat4CpyMat4(diff_mat, pchan->bone->arm_mat); + + Mat4CpyMat4(tempmat, mat); + Mat4MulMat4(mat, tempmat, diff_mat); + } + } + } + /* local to world */ + if (to == CONSTRAINT_SPACE_WORLD) { + /* call self with slightly different values */ + constraint_mat_convertspace(ob, pchan, mat, CONSTRAINT_SPACE_POSE, to); + } + } + break; + case CONSTRAINT_SPACE_PARLOCAL: /* -------------- FROM LOCAL WITH PARENT ---------- */ + { + /* local to pose */ + if (to==CONSTRAINT_SPACE_POSE || to==CONSTRAINT_SPACE_WORLD) { + if (pchan->bone) { + Mat4CpyMat4(diff_mat, pchan->bone->arm_mat); + Mat4CpyMat4(tempmat, mat); + Mat4MulMat4(mat, diff_mat, tempmat); + } + } + /* local to world */ + if (to == CONSTRAINT_SPACE_WORLD) { + /* call self with slightly different values */ + constraint_mat_convertspace(ob, pchan, mat, CONSTRAINT_SPACE_POSE, to); + } + } + break; + } + } + else { + /* objects */ + if (from==CONSTRAINT_SPACE_WORLD && to==CONSTRAINT_SPACE_LOCAL) { + /* check if object has a parent - otherwise this won't work */ + if (ob->parent) { + /* 'subtract' parent's effects from owner */ + Mat4MulMat4(diff_mat, ob->parentinv, ob->parent->obmat); + Mat4Invert(imat, diff_mat); + Mat4CpyMat4(tempmat, mat); + Mat4MulMat4(mat, tempmat, imat); + } + } + else if (from==CONSTRAINT_SPACE_LOCAL && to==CONSTRAINT_SPACE_WORLD) { + /* check that object has a parent - otherwise this won't work */ + if (ob->parent) { + /* 'add' parent's effect back to owner */ + Mat4CpyMat4(tempmat, mat); + Mat4MulMat4(diff_mat, ob->parentinv, ob->parent->obmat); + Mat4MulMat4(mat, tempmat, diff_mat); + } } } - return NULL; } -/* finds or creates new constraint channel */ -bConstraintChannel *verify_constraint_channel (ListBase *list, const char *name) +/* ------------ General Target Matrix Tools ---------- */ + +/* function that sets the given matrix based on given vertex group in mesh */ +static void contarget_get_mesh_mat (Object *ob, char *substring, float mat[][4]) { - bConstraintChannel *chan; + DerivedMesh *dm; + float vec[3] = {0.0f, 0.0f, 0.0f}, tvec[3]; + float normal[3] = {0.0f, 0.0f, 0.0f}, plane[3]; + float imat[3][3], tmat[3][3]; + int dgroup; - chan= get_constraint_channel (list, name); - if(chan==NULL) { - chan= MEM_callocN(sizeof(bConstraintChannel), "new constraint chan"); - BLI_addtail(list, chan); - strcpy(chan->name, name); + /* initialize target matrix using target matrix */ + Mat4CpyMat4(mat, ob->obmat); + + /* get index of vertex group */ + dgroup = get_named_vertexgroup_num(ob, substring); + if (dgroup < 0) return; + + /* get DerivedMesh */ + if (G.obedit && G.editMesh) { + /* we are in editmode, so get a special derived mesh */ + dm = CDDM_from_editmesh(G.editMesh, ob->data); + } + else { + /* when not in EditMode, this should exist */ + dm = (DerivedMesh *)ob->derivedFinal; } - return chan; + /* only continue if there's a valid DerivedMesh */ + if (dm) { + MDeformVert *dvert = dm->getVertDataArray(dm, CD_MDEFORMVERT); + int *index = (int *)dm->getVertDataArray(dm, CD_ORIGINDEX); + int numVerts = dm->getNumVerts(dm); + int i, j, count = 0; + float co[3], nor[3]; + + /* check that dvert and index are valid pointers (just in case) */ + if (dvert && index) { + /* get the average of all verts with that are in the vertex-group */ + for (i = 0; i < numVerts; i++, index++) { + for (j = 0; j < dvert[i].totweight; j++) { + /* does this vertex belong to nominated vertex group? */ + if (dvert[i].dw[j].def_nr == dgroup) { + dm->getVertCo(dm, i, co); + dm->getVertNo(dm, i, nor); + VecAddf(vec, vec, co); + VecAddf(normal, normal, nor); + count++; + break; + } + + } + } + + + /* calculate averages of normal and coordinates */ + if (count > 0) { + VecMulf(vec, 1.0f / count); + VecMulf(normal, 1.0f / count); + } + + + /* derive the rotation from the average normal: + * - code taken from transform_manipulator.c, + * calc_manipulator_stats, V3D_MANIP_NORMAL case + */ + /* we need the transpose of the inverse for a normal... */ + Mat3CpyMat4(imat, ob->obmat); + + Mat3Inv(tmat, imat); + Mat3Transp(tmat); + Mat3MulVecfl(tmat, normal); + + Normalize(normal); + VECCOPY(plane, tmat[1]); + + VECCOPY(tmat[2], normal); + Crossf(tmat[0], normal, plane); + Crossf(tmat[1], tmat[2], tmat[0]); + + Mat4CpyMat3(mat, tmat); + Mat4Ortho(mat); + + + /* apply the average coordinate as the new location */ + VecMat4MulVecfl(tvec, ob->obmat, vec); + VECCOPY(mat[3], tvec); + } + } + + /* free temporary DerivedMesh created (in EditMode case) */ + if (G.editMesh) { + if (dm) dm->release(dm); + } } - -/* ***************** Evaluating ********************* */ - -/* does ipos only */ -void do_constraint_channels (ListBase *conbase, ListBase *chanbase, float ctime) +/* function that sets the given matrix based on given vertex group in lattice */ +static void contarget_get_lattice_mat (Object *ob, char *substring, float mat[][4]) { - bConstraint *con; - bConstraintChannel *chan; - IpoCurve *icu=NULL; + Lattice *lt= (Lattice *)ob->data; - for (con=conbase->first; con; con=con->next) { - chan = get_constraint_channel(chanbase, con->name); - if (chan && chan->ipo){ - calc_ipo(chan->ipo, ctime); - for (icu=chan->ipo->curve.first; icu; icu=icu->next){ - switch (icu->adrcode){ - case CO_ENFORCE: - con->enforce = icu->curval; - if (con->enforce<0.0f) con->enforce= 0.0f; - else if (con->enforce>1.0f) con->enforce= 1.0f; - break; - } + DispList *dl = find_displist(&ob->disp, DL_VERTS); + float *co = dl?dl->verts:NULL; + BPoint *bp = lt->def; + + MDeformVert *dvert = lt->dvert; + int tot_verts= lt->pntsu*lt->pntsv*lt->pntsw; + float vec[3]= {0.0f, 0.0f, 0.0f}, tvec[3]; + int dgroup=0, grouped=0; + int i, n; + + /* initialize target matrix using target matrix */ + Mat4CpyMat4(mat, ob->obmat); + + /* get index of vertex group */ + dgroup = get_named_vertexgroup_num(ob, substring); + if (dgroup < 0) return; + if (dvert == NULL) return; + + /* 1. Loop through control-points checking if in nominated vertex-group. + * 2. If it is, add it to vec to find the average point. + */ + for (i=0; i < tot_verts; i++, dvert++) { + for (n= 0; n < dvert->totweight; n++) { + /* found match - vert is in vgroup */ + if (dvert->dw[n].def_nr == dgroup) { + /* copy coordinates of point to temporary vector, then add to find average */ + if (co) + memcpy(tvec, co, 3*sizeof(float)); + else + memcpy(tvec, bp->vec, 3*sizeof(float)); + + VecAddf(vec, vec, tvec); + grouped++; + + break; } } + + /* advance pointer to coordinate data */ + if (co) co+= 3; + else bp++; } + + /* find average location, then multiply by ob->obmat to find world-space location */ + if (grouped) + VecMulf(vec, 1.0f / grouped); + VecMat4MulVecfl(tvec, ob->obmat, vec); + + /* copy new location to matrix */ + VECCOPY(mat[3], tvec); } -void Mat4BlendMat4(float out[][4], float dst[][4], float src[][4], float srcweight) +/* generic function to get the appropriate matrix for most target cases */ +/* The cases where the target can be object data have not been implemented */ +static void constraint_target_to_mat4 (Object *ob, char *substring, float mat[][4], short from, short to, float headtail) { - float squat[4], dquat[4], fquat[4]; - float ssize[3], dsize[3], fsize[4]; - float sloc[3], dloc[3], floc[3]; - float mat3[3][3], dstweight; - float qmat[3][3], smat[3][3]; - int i; + /* Case OBJECT */ + if (!strlen(substring)) { + Mat4CpyMat4(mat, ob->obmat); + constraint_mat_convertspace(ob, NULL, mat, from, to); + } + /* Case VERTEXGROUP */ + /* Current method just takes the average location of all the points in the + * VertexGroup, and uses that as the location value of the targets. Where + * possible, the orientation will also be calculated, by calculating an + * 'average' vertex normal, and deriving the rotaation from that. + * + * NOTE: EditMode is not currently supported, and will most likely remain that + * way as constraints can only really affect things on object/bone level. + */ + else if (ob->type == OB_MESH) { + contarget_get_mesh_mat(ob, substring, mat); + constraint_mat_convertspace(ob, NULL, mat, from, to); + } + else if (ob->type == OB_LATTICE) { + contarget_get_lattice_mat(ob, substring, mat); + constraint_mat_convertspace(ob, NULL, mat, from, to); + } + /* Case BONE */ + else { + bPoseChannel *pchan; + + pchan = get_pose_channel(ob->pose, substring); + if (pchan) { + /* Multiply the PoseSpace accumulation/final matrix for this + * PoseChannel by the Armature Object's Matrix to get a worldspace + * matrix. + */ + if (headtail < 0.000001) { + /* skip length interpolation if set to head */ + Mat4MulMat4(mat, pchan->pose_mat, ob->obmat); + } + else { + float tempmat[4][4], loc[3]; + + /* interpolate along length of bone */ + VecLerpf(loc, pchan->pose_head, pchan->pose_tail, headtail); + + /* use interpolated distance for subtarget */ + Mat4CpyMat4(tempmat, pchan->pose_mat); + VecCopyf(tempmat[3], loc); + + Mat4MulMat4(mat, tempmat, ob->obmat); + } + } + else + Mat4CpyMat4(mat, ob->obmat); + + /* convert matrix space as required */ + constraint_mat_convertspace(ob, pchan, mat, from, to); + } +} - dstweight = 1.0F-srcweight; +/* ************************* Specific Constraints ***************************** */ +/* Each constraint defines a set of functions, which will be called at the appropriate + * times. In addition to this, each constraint 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 constraints, 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 bConstraintTypeInfo CTI_CONSTRNAME = { + CONSTRAINT_TYPE_CONSTRNAME, /* type */ + sizeof(bConstrNameConstraint), /* size */ + "ConstrName", /* name */ + "bConstrNameConstraint", /* struct name */ + constrname_free, /* free data */ + constrname_relink, /* relink data */ + constrname_copy, /* copy data */ + constrname_new_data, /* new data */ + constrname_get_tars, /* get constraint targets */ + constrname_flush_tars, /* flush constraint targets */ + constrname_get_tarmat, /* get target matrix */ + constrname_evaluate /* evaluate */ +}; +#endif - Mat3CpyMat4(mat3, dst); - Mat3ToQuat(mat3, dquat); - Mat3ToSize(mat3, dsize); - VECCOPY (dloc, dst[3]); +/* This function should be used for the get_target_matrix member of all + * constraints that are not picky about what happens to their target matrix. + */ +static void default_get_tarmat (bConstraint *con, bConstraintOb *cob, bConstraintTarget *ct, float ctime) +{ + if (VALID_CONS_TARGET(ct)) + constraint_target_to_mat4(ct->tar, ct->subtarget, ct->matrix, CONSTRAINT_SPACE_WORLD, ct->space, con->headtail); + else if (ct) + Mat4One(ct->matrix); +} - Mat3CpyMat4(mat3, src); - Mat3ToQuat(mat3, squat); - Mat3ToSize(mat3, ssize); - VECCOPY (sloc, src[3]); +/* This following macro should be used for all standard single-target *_get_tars functions + * to save typing and reduce maintainance woes. + * (Hopefully all compilers will be happy with the lines with just a space on them. Those are + * really just to help this code easier to read) + */ +#define SINGLETARGET_GET_TARS(con, datatar, datasubtarget, ct, list) \ + { \ + ct= MEM_callocN(sizeof(bConstraintTarget), "tempConstraintTarget"); \ + \ + ct->tar= datatar; \ + strcpy(ct->subtarget, datasubtarget); \ + ct->space= con->tarspace; \ + ct->flag= CONSTRAINT_TAR_TEMP; \ + \ + if (ct->tar) { \ + if ((ct->tar->type==OB_ARMATURE) && (ct->subtarget[0])) ct->type = CONSTRAINT_OBTYPE_BONE; \ + else if (ELEM(ct->tar->type, OB_MESH, OB_LATTICE) && (ct->subtarget[0])) ct->type = CONSTRAINT_OBTYPE_VERT; \ + else ct->type = CONSTRAINT_OBTYPE_OBJECT; \ + } \ + \ + BLI_addtail(list, ct); \ + } - /* Do the actual blend */ - for (i=0; i<3; i++){ - floc[i] = (dloc[i]*dstweight) + (sloc[i]*srcweight); - fsize[i] = 1.0f + ((dsize[i]-1.0f)*dstweight) + ((ssize[i]-1.0f)*srcweight); - fquat[i+1] = (dquat[i+1]*dstweight) + (squat[i+1]*srcweight); +/* This following macro should be used for all standard single-target *_get_tars functions + * to save typing and reduce maintainance woes. It does not do the subtarget related operations + * (Hopefully all compilers will be happy with the lines with just a space on them. Those are + * really just to help this code easier to read) + */ +#define SINGLETARGETNS_GET_TARS(con, datatar, ct, list) \ + { \ + ct= MEM_callocN(sizeof(bConstraintTarget), "tempConstraintTarget"); \ + \ + ct->tar= datatar; \ + ct->space= con->tarspace; \ + ct->flag= CONSTRAINT_TAR_TEMP; \ + \ + if (ct->tar) ct->type = CONSTRAINT_OBTYPE_OBJECT; \ + \ + BLI_addtail(list, ct); \ + } + +/* This following macro should be used for all standard single-target *_flush_tars functions + * to save typing and reduce maintainance woes. + * (Hopefully all compilers will be happy with the lines with just a space on them. Those are + * really just to help this code easier to read) + */ +#define SINGLETARGET_FLUSH_TARS(con, datatar, datasubtarget, ct, list, nocopy) \ + { \ + if (ct) { \ + if (nocopy == 0) { \ + datatar= ct->tar; \ + strcpy(datasubtarget, ct->subtarget); \ + con->tarspace= ct->space; \ + } \ + \ + BLI_freelinkN(list, ct); \ + } \ } - /* Do one more iteration for the quaternions only and normalize the quaternion if needed */ - fquat[0] = 1.0f + ((dquat[0]-1.0f)*dstweight) + ((squat[0]-1.0f)*srcweight); - NormalQuat (fquat); +/* This following macro should be used for all standard single-target *_flush_tars functions + * to save typing and reduce maintainance woes. It does not do the subtarget related operations + * (Hopefully all compilers will be happy with the lines with just a space on them. Those are + * really just to help this code easier to read) + */ +#define SINGLETARGETNS_FLUSH_TARS(con, datatar, ct, list, nocopy) \ + { \ + if (ct) { \ + if (nocopy == 0) { \ + datatar= ct->tar; \ + con->tarspace= ct->space; \ + } \ + \ + BLI_freelinkN(list, ct); \ + } \ + } + +/* --------- ChildOf Constraint ------------ */ - QuatToMat3(fquat, qmat); - SizeToMat3(fsize, smat); +static void childof_new_data (void *cdata) +{ + bChildOfConstraint *data= (bChildOfConstraint *)cdata; + + data->flag = (CHILDOF_LOCX | CHILDOF_LOCY | CHILDOF_LOCZ | + CHILDOF_ROTX |CHILDOF_ROTY | CHILDOF_ROTZ | + CHILDOF_SIZEX | CHILDOF_SIZEY | CHILDOF_SIZEZ); + Mat4One(data->invmat); +} - Mat3MulMat3(mat3, qmat, smat); - Mat4CpyMat3(out, mat3); - VECCOPY (out[3], floc); +static void childof_get_tars (bConstraint *con, ListBase *list) +{ + if (con && list) { + bChildOfConstraint *data= con->data; + bConstraintTarget *ct; + + /* standard target-getting macro for single-target constraints */ + SINGLETARGET_GET_TARS(con, data->tar, data->subtarget, ct, list) + } } -static void constraint_target_to_mat4 (Object *ob, const char *substring, float mat[][4], float size[3]) +static void childof_flush_tars (bConstraint *con, ListBase *list, short nocopy) { + if (con && list) { + bChildOfConstraint *data= con->data; + bConstraintTarget *ct= list->first; + + /* the following macro is used for all standard single-target constraints */ + SINGLETARGET_FLUSH_TARS(con, data->tar, data->subtarget, ct, list, nocopy) + } +} - /* Case OBJECT */ - if (!strlen(substring)) { - Mat4CpyMat4 (mat, ob->obmat); - VECCOPY (size, ob->size); // whats this for, hack! (ton) +static void childof_evaluate (bConstraint *con, bConstraintOb *cob, ListBase *targets) +{ + bChildOfConstraint *data= con->data; + bConstraintTarget *ct= targets->first; + + /* only evaluate if there is a target */ + if (VALID_CONS_TARGET(ct)) { + float parmat[4][4], invmat[4][4], tempmat[4][4]; + float loc[3], eul[3], size[3]; + float loco[3], eulo[3], sizo[3]; + + /* get offset (parent-inverse) matrix */ + Mat4CpyMat4(invmat, data->invmat); + + /* extract components of both matrices */ + VECCOPY(loc, ct->matrix[3]); + Mat4ToEul(ct->matrix, eul); + Mat4ToSize(ct->matrix, size); + + VECCOPY(loco, invmat[3]); + Mat4ToEul(invmat, eulo); + Mat4ToSize(invmat, sizo); + + /* disable channels not enabled */ + if (!(data->flag & CHILDOF_LOCX)) loc[0]= loco[0]= 0.0f; + if (!(data->flag & CHILDOF_LOCY)) loc[1]= loco[1]= 0.0f; + if (!(data->flag & CHILDOF_LOCZ)) loc[2]= loco[2]= 0.0f; + if (!(data->flag & CHILDOF_ROTX)) eul[0]= eulo[0]= 0.0f; + if (!(data->flag & CHILDOF_ROTY)) eul[1]= eulo[1]= 0.0f; + if (!(data->flag & CHILDOF_ROTZ)) eul[2]= eulo[2]= 0.0f; + if (!(data->flag & CHILDOF_SIZEX)) size[0]= sizo[0]= 1.0f; + if (!(data->flag & CHILDOF_SIZEY)) size[1]= sizo[1]= 1.0f; + if (!(data->flag & CHILDOF_SIZEZ)) size[2]= sizo[2]= 1.0f; + + /* make new target mat and offset mat */ + LocEulSizeToMat4(ct->matrix, loc, eul, size); + LocEulSizeToMat4(invmat, loco, eulo, sizo); + + /* multiply target (parent matrix) by offset (parent inverse) to get + * the effect of the parent that will be exherted on the owner + */ + Mat4MulMat4(parmat, invmat, ct->matrix); + + /* now multiply the parent matrix by the owner matrix to get the + * the effect of this constraint (i.e. owner is 'parented' to parent) + */ + Mat4CpyMat4(tempmat, cob->matrix); + Mat4MulMat4(cob->matrix, tempmat, parmat); } - /* Case BONE */ - else { - bPoseChannel *pchan; - float bsize[3]={1, 1, 1}; +} - pchan = get_pose_channel(ob->pose, substring); - if (pchan){ - /** - * Multiply the objectspace bonematrix by the skeletons's global - * transform to obtain the worldspace transformation of the target - */ - Mat4MulMat4 (mat, pchan->pose_mat, ob->obmat); - } - else - Mat4CpyMat4 (mat, ob->obmat); +static bConstraintTypeInfo CTI_CHILDOF = { + CONSTRAINT_TYPE_CHILDOF, /* type */ + sizeof(bChildOfConstraint), /* size */ + "ChildOf", /* name */ + "bChildOfConstraint", /* struct name */ + NULL, /* free data */ + NULL, /* relink data */ + NULL, /* copy data */ + childof_new_data, /* new data */ + childof_get_tars, /* get constraint targets */ + childof_flush_tars, /* flush constraint targets */ + default_get_tarmat, /* get a target matrix */ + childof_evaluate /* evaluate */ +}; + +/* -------- TrackTo Constraint ------- */ + +static void trackto_new_data (void *cdata) +{ + bTrackToConstraint *data= (bTrackToConstraint *)cdata; + + data->reserved1 = TRACK_Y; + data->reserved2 = UP_Z; +} + +static void trackto_get_tars (bConstraint *con, ListBase *list) +{ + if (con && list) { + bTrackToConstraint *data= con->data; + bConstraintTarget *ct; + + /* standard target-getting macro for single-target constraints */ + SINGLETARGET_GET_TARS(con, data->tar, data->subtarget, ct, list) + } +} - VECCOPY(size, bsize); // whats this for, hack! (ton) +static void trackto_flush_tars (bConstraint *con, ListBase *list, short nocopy) +{ + if (con && list) { + bTrackToConstraint *data= con->data; + bConstraintTarget *ct= list->first; + + /* the following macro is used for all standard single-target constraints */ + SINGLETARGET_FLUSH_TARS(con, data->tar, data->subtarget, ct, list, nocopy) } } -/* stupid little cross product function, 0:x, 1:y, 2:z axes */ -static int basis_cross(int n, int m) +static int basis_cross (int n, int m) { - if(n-m == 1) return 1; - if(n-m == -1) return -1; - if(n-m == 2) return -1; - if(n-m == -2) return 1; - else return 0; + switch (n-m) { + case 1: + case -2: + return 1; + + case -1: + case 2: + return -1; + + default: + return 0; + } } -static void vectomat(float *vec, float *target_up, short axis, short upflag, short flags, float m[][3]) +static void vectomat (float *vec, float *target_up, short axis, short upflag, short flags, float m[][3]) { float n[3]; float u[3]; /* vector specifying the up axis */ @@ -906,19 +1055,18 @@ static void vectomat(float *vec, float *target_up, short axis, short upflag, sho float right[3]; float neg = -1; int right_index; - + VecCopyf(n, vec); - if(Normalize(n) == 0.0) { + if (Normalize(n) == 0.0) { n[0] = 0.0; n[1] = 0.0; n[2] = 1.0; } - if(axis > 2) axis -= 3; + if (axis > 2) axis -= 3; else VecMulf(n,-1); /* n specifies the transformation of the track axis */ - - if(flags & TARGET_Z_UP) { + if (flags & TARGET_Z_UP) { /* target Z axis is the global up axis */ u[0] = target_up[0]; u[1] = target_up[1]; @@ -936,7 +1084,7 @@ static void vectomat(float *vec, float *target_up, short axis, short upflag, sho VecSubf(proj, u, proj); /* then onto the plane */ /* proj specifies the transformation of the up axis */ - if(Normalize(proj) == 0.0) { /* degenerate projection */ + if (Normalize(proj) == 0.0) { /* degenerate projection */ proj[0] = 0.0; proj[1] = 1.0; proj[2] = 0.0; @@ -946,24 +1094,24 @@ static void vectomat(float *vec, float *target_up, short axis, short upflag, sho Crossf(right, proj, n); Normalize(right); - if(axis != upflag) { + if (axis != upflag) { right_index = 3 - axis - upflag; - neg = (float) basis_cross(axis, upflag); - + neg = (float)basis_cross(axis, upflag); + /* account for up direction, track direction */ m[right_index][0] = neg * right[0]; m[right_index][1] = neg * right[1]; m[right_index][2] = neg * right[2]; - + m[upflag][0] = proj[0]; m[upflag][1] = proj[1]; m[upflag][2] = proj[2]; - + m[axis][0] = n[0]; m[axis][1] = n[1]; m[axis][2] = n[2]; } - + /* identity matrix - don't do anything if the two axes are the same */ else { m[0][0]= m[1][1]= m[2][2]= 1.0; m[0][1]= m[0][2]= m[0][3]= 0.0; @@ -973,1316 +1121,2288 @@ static void vectomat(float *vec, float *target_up, short axis, short upflag, sho } -/* called during solve_constraints */ -/* also for make_parent, to find correct inverse of "follow path" */ -/* warning, ownerdata is void... is not Bone anymore, but PoseChannel or Object */ -/* ctime is global time, uncorrected for local bsystem_time */ -short get_constraint_target_matrix (bConstraint *con, short ownertype, void* ownerdata, float mat[][4], float size[3], float ctime) +static void trackto_evaluate (bConstraint *con, bConstraintOb *cob, ListBase *targets) { - short valid=0; + bTrackToConstraint *data= con->data; + bConstraintTarget *ct= targets->first; + + if (VALID_CONS_TARGET(ct)) { + float size[3], vec[3]; + float totmat[3][3]; + float tmat[4][4]; + + /* Get size property, since ob->size is only the object's own relative size, not its global one */ + Mat4ToSize(cob->matrix, size); + + /* Clear the object's rotation */ + cob->matrix[0][0]=size[0]; + cob->matrix[0][1]=0; + cob->matrix[0][2]=0; + cob->matrix[1][0]=0; + cob->matrix[1][1]=size[1]; + cob->matrix[1][2]=0; + cob->matrix[2][0]=0; + cob->matrix[2][1]=0; + cob->matrix[2][2]=size[2]; + + /* targetmat[2] instead of ownermat[2] is passed to vectomat + * for backwards compatability it seems... (Aligorith) + */ + VecSubf(vec, cob->matrix[3], ct->matrix[3]); + vectomat(vec, ct->matrix[2], + (short)data->reserved1, (short)data->reserved2, + data->flags, totmat); + + Mat4CpyMat4(tmat, cob->matrix); + Mat4MulMat34(cob->matrix, totmat, tmat); + } +} - switch (con->type){ - case CONSTRAINT_TYPE_NULL: - { - Mat4One(mat); - } - break; - case CONSTRAINT_TYPE_ACTION: - { - if (ownertype == TARGET_BONE) { - extern void chan_calc_mat(bPoseChannel *chan); - bActionConstraint *data = (bActionConstraint*)con->data; - bPose *pose; - bPoseChannel *pchan, *tchan; - float tempmat3[3][3]; - float eul[3]; - float s,t; - - Mat4One(mat); // return mat - - if (data->tar==NULL) return 0; - - /* need proper check for bone... */ - if(data->subtarget[0]) { - pchan = get_pose_channel(data->tar->pose, data->subtarget); - if (pchan) { - float arm_mat[3][3], pose_mat[3][3]; /* arm mat should be bone mat! bug... */ - - Mat3CpyMat4(arm_mat, pchan->bone->arm_mat); - Mat3CpyMat4(pose_mat, pchan->pose_mat); - - /* new; true local rotation constraint */ - if(data->local) { - float diff_mat[3][3], par_mat[3][3], ipar_mat[3][3]; - /* we need the local rotation = current rotation - (parent rotation + restpos) */ - - if (pchan->parent) { - Mat3CpyMat4(par_mat, pchan->parent->pose_mat); - Mat3MulMat3(diff_mat, par_mat, arm_mat); - - Mat3Inv(ipar_mat, diff_mat); - } - else { - Mat3Inv(ipar_mat, arm_mat); - } - - Mat3MulMat3(tempmat3, ipar_mat, pose_mat); - } - else { /* we use the deform mat, for backwards compatibility */ - float imat[3][3]; - - Mat3Inv(imat, arm_mat); - Mat3MulMat3(tempmat3, pose_mat, imat); - } - } - else Mat3One(tempmat3); - } - else { - float ans[4][4]; - - constraint_target_to_mat4(data->tar, data->subtarget, ans, size); - /* extract rotation, is in global world coordinates */ - Mat3CpyMat4(tempmat3, ans); - } - - Mat3ToEul(tempmat3, eul); - eul[0]*=(float)(180.0/M_PI); - eul[1]*=(float)(180.0/M_PI); - eul[2]*=(float)(180.0/M_PI); - - /* Target defines the animation */ - s = (eul[data->type]-data->min)/(data->max-data->min); - if (s<0) - s=0; - if (s>1) - s=1; +static bConstraintTypeInfo CTI_TRACKTO = { + CONSTRAINT_TYPE_TRACKTO, /* type */ + sizeof(bTrackToConstraint), /* size */ + "TrackTo", /* name */ + "bTrackToConstraint", /* struct name */ + NULL, /* free data */ + NULL, /* relink data */ + NULL, /* copy data */ + trackto_new_data, /* new data */ + trackto_get_tars, /* get constraint targets */ + trackto_flush_tars, /* flush constraint targets */ + default_get_tarmat, /* get target matrix */ + trackto_evaluate /* evaluate */ +}; + +/* --------- Inverse-Kinemetics --------- */ + +static void kinematic_new_data (void *cdata) +{ + bKinematicConstraint *data= (bKinematicConstraint *)cdata; + + data->weight= (float)1.0; + data->orientweight= (float)1.0; + data->iterations = 500; + data->flag= CONSTRAINT_IK_TIP|CONSTRAINT_IK_STRETCH|CONSTRAINT_IK_POS; +} - t = ( s * (data->end-data->start)) + data->start; +static void kinematic_get_tars (bConstraint *con, ListBase *list) +{ + if (con && list) { + bKinematicConstraint *data= con->data; + bConstraintTarget *ct; + + /* standard target-getting macro for single-target constraints */ + SINGLETARGET_GET_TARS(con, data->tar, data->subtarget, ct, list) + SINGLETARGET_GET_TARS(con, data->poletar, data->polesubtarget, ct, list) + } +} - /* Get the appropriate information from the action, we make temp pose */ - pose = MEM_callocN(sizeof(bPose), "pose"); - - pchan = ownerdata; - tchan= verify_pose_channel(pose, pchan->name); - extract_pose_from_action (pose, data->act, t); - - chan_calc_mat(tchan); - - Mat4CpyMat4(mat, tchan->chan_mat); +static void kinematic_flush_tars (bConstraint *con, ListBase *list, short nocopy) +{ + if (con && list) { + bKinematicConstraint *data= con->data; + bConstraintTarget *ct= list->first; + + /* the following macro is used for all standard single-target constraints */ + SINGLETARGET_FLUSH_TARS(con, data->tar, data->subtarget, ct, list, nocopy) + ct= ct->next; + SINGLETARGET_FLUSH_TARS(con, data->poletar, data->polesubtarget, ct, list, nocopy) + } +} - /* Clean up */ - free_pose_channels(pose); - MEM_freeN(pose); - } +static void kinematic_get_tarmat (bConstraint *con, bConstraintOb *cob, bConstraintTarget *ct, float ctime) +{ + bKinematicConstraint *data= con->data; + + if (VALID_CONS_TARGET(ct)) + constraint_target_to_mat4(ct->tar, ct->subtarget, ct->matrix, CONSTRAINT_SPACE_WORLD, ct->space, con->headtail); + else if (ct) { + if (data->flag & CONSTRAINT_IK_AUTO) { + Object *ob= cob->ob; + if (ob == NULL) { + Mat4One(ct->matrix); + } + else { + float vec[3]; + /* move grabtarget into world space */ + VECCOPY(vec, data->grabtarget); + Mat4MulVecfl(ob->obmat, vec); + Mat4CpyMat4(ct->matrix, ob->obmat); + VECCOPY(ct->matrix[3], vec); + } } - break; - case CONSTRAINT_TYPE_LOCLIKE: - { - bLocateLikeConstraint *data = (bLocateLikeConstraint*)con->data; - Object *ob= data->tar; + else + Mat4One(ct->matrix); + } +} + +static bConstraintTypeInfo CTI_KINEMATIC = { + CONSTRAINT_TYPE_KINEMATIC, /* type */ + sizeof(bKinematicConstraint), /* size */ + "IK", /* name */ + "bKinematicConstraint", /* struct name */ + NULL, /* free data */ + NULL, /* relink data */ + NULL, /* copy data */ + kinematic_new_data, /* new data */ + kinematic_get_tars, /* get constraint targets */ + kinematic_flush_tars, /* flush constraint targets */ + kinematic_get_tarmat, /* get target matrix */ + NULL /* evaluate - solved as separate loop */ +}; + +/* -------- Follow-Path Constraint ---------- */ + +static void followpath_new_data (void *cdata) +{ + bFollowPathConstraint *data= (bFollowPathConstraint *)cdata; + + data->trackflag = TRACK_Y; + data->upflag = UP_Z; + data->offset = 0; + data->followflag = 0; +} + +static void followpath_get_tars (bConstraint *con, ListBase *list) +{ + if (con && list) { + bFollowPathConstraint *data= con->data; + bConstraintTarget *ct; + + /* standard target-getting macro for single-target constraints without subtargets */ + SINGLETARGETNS_GET_TARS(con, data->tar, ct, list) + } +} + +static void followpath_flush_tars (bConstraint *con, ListBase *list, short nocopy) +{ + if (con && list) { + bFollowPathConstraint *data= con->data; + bConstraintTarget *ct= list->first; + + /* the following macro is used for all standard single-target constraints */ + SINGLETARGETNS_FLUSH_TARS(con, data->tar, ct, list, nocopy) + } +} + +static void followpath_get_tarmat (bConstraint *con, bConstraintOb *cob, bConstraintTarget *ct, float ctime) +{ + bFollowPathConstraint *data= con->data; + + if (VALID_CONS_TARGET(ct)) { + Curve *cu= ct->tar->data; + float q[4], vec[4], dir[3], *quat, x1; + float totmat[4][4]; + float curvetime; + + Mat4One(totmat); + Mat4One(ct->matrix); + + /* note: when creating constraints that follow path, the curve gets the CU_PATH set now, + * currently for paths to work it needs to go through the bevlist/displist system (ton) + */ + + /* only happens on reload file, but violates depsgraph still... fix! */ + if (cu->path==NULL || cu->path->data==NULL) + makeDispListCurveTypes(ct->tar, 0); + + if (cu->path && cu->path->data) { + curvetime= bsystem_time(ct->tar, (float)ctime, 0.0) - data->offset; - if (data->tar) { - if (strlen(data->subtarget)) { - bPoseChannel *pchan; - float tmat[4][4]; - float bsize[3]={1, 1, 1}; + if (calc_ipo_spec(cu->ipo, CU_SPEED, &curvetime)==0) { + curvetime /= cu->pathlen; + CLAMP(curvetime, 0.0, 1.0); + } + + if ( where_on_path(ct->tar, curvetime, vec, dir) ) { + if (data->followflag) { + quat= vectoquat(dir, (short) data->trackflag, (short) data->upflag); - pchan = get_pose_channel(ob->pose, data->subtarget); - if (pchan) { - Mat4CpyMat4(tmat, pchan->pose_mat); - - if (data->flag & LOCLIKE_TIP) - VECCOPY(tmat[3], pchan->pose_tail); - - Mat4MulMat4 (mat, tmat, ob->obmat); - } - else - Mat4CpyMat4 (mat, ob->obmat); + Normalize(dir); + q[0]= (float)cos(0.5*vec[3]); + x1= (float)sin(0.5*vec[3]); + q[1]= -x1*dir[0]; + q[2]= -x1*dir[1]; + q[3]= -x1*dir[2]; + QuatMul(quat, q, quat); - VECCOPY(size, bsize); // what's this hack for? - } - else { - Mat4CpyMat4 (mat, ob->obmat); - VECCOPY(size, data->tar->size); // what's this hack for? + QuatToMat4(quat, totmat); } - valid=1; + VECCOPY(totmat[3], vec); + + Mat4MulSerie(ct->matrix, ct->tar->obmat, totmat, NULL, NULL, NULL, NULL, NULL, NULL); } - else - Mat4One (mat); - } - break; - case CONSTRAINT_TYPE_MINMAX: - { - bMinMaxConstraint *data = (bMinMaxConstraint*)con->data; + } + } + else if (ct) + Mat4One(ct->matrix); +} - if (data->tar){ - constraint_target_to_mat4(data->tar, data->subtarget, mat, size); - valid=1; - } - else - Mat4One (mat); - } - break; - case CONSTRAINT_TYPE_ROTLIKE: - { - bRotateLikeConstraint *data; - data = (bRotateLikeConstraint*)con->data; +static void followpath_evaluate (bConstraint *con, bConstraintOb *cob, ListBase *targets) +{ + bConstraintTarget *ct= targets->first; + + /* only evaluate if there is a target */ + if (VALID_CONS_TARGET(ct)) { + float obmat[4][4]; + float size[3], obsize[3]; + + /* get Object local transform (loc/rot/size) to determine transformation from path */ + //object_to_mat4(ob, obmat); + Mat4CpyMat4(obmat, cob->matrix); // FIXME!!! + + /* get scaling of object before applying constraint */ + Mat4ToSize(cob->matrix, size); + + /* apply targetmat - containing location on path, and rotation */ + Mat4MulSerie(cob->matrix, ct->matrix, obmat, NULL, NULL, NULL, NULL, NULL, NULL); + + /* un-apply scaling caused by path */ + Mat4ToSize(cob->matrix, obsize); + if (obsize[0]) + VecMulf(cob->matrix[0], size[0] / obsize[0]); + if (obsize[1]) + VecMulf(cob->matrix[1], size[1] / obsize[1]); + if (obsize[2]) + VecMulf(cob->matrix[2], size[2] / obsize[2]); + } +} - if (data->tar){ - constraint_target_to_mat4(data->tar, data->subtarget, mat, size); - valid=1; - } - else - Mat4One (mat); - } - break; - case CONSTRAINT_TYPE_SIZELIKE: - { - bSizeLikeConstraint *data; - data = (bSizeLikeConstraint*)con->data; +static bConstraintTypeInfo CTI_FOLLOWPATH = { + CONSTRAINT_TYPE_FOLLOWPATH, /* type */ + sizeof(bFollowPathConstraint), /* size */ + "Follow Path", /* name */ + "bFollowPathConstraint", /* struct name */ + NULL, /* free data */ + NULL, /* relink data */ + NULL, /* copy data */ + followpath_new_data, /* new data */ + followpath_get_tars, /* get constraint targets */ + followpath_flush_tars, /* flush constraint targets */ + followpath_get_tarmat, /* get target matrix */ + followpath_evaluate /* evaluate */ +}; + +/* --------- Limit Location --------- */ + + +static void loclimit_evaluate (bConstraint *con, bConstraintOb *cob, ListBase *targets) +{ + bLocLimitConstraint *data = con->data; + + if (data->flag & LIMIT_XMIN) { + if (cob->matrix[3][0] < data->xmin) + cob->matrix[3][0] = data->xmin; + } + if (data->flag & LIMIT_XMAX) { + if (cob->matrix[3][0] > data->xmax) + cob->matrix[3][0] = data->xmax; + } + if (data->flag & LIMIT_YMIN) { + if (cob->matrix[3][1] < data->ymin) + cob->matrix[3][1] = data->ymin; + } + if (data->flag & LIMIT_YMAX) { + if (cob->matrix[3][1] > data->ymax) + cob->matrix[3][1] = data->ymax; + } + if (data->flag & LIMIT_ZMIN) { + if (cob->matrix[3][2] < data->zmin) + cob->matrix[3][2] = data->zmin; + } + if (data->flag & LIMIT_ZMAX) { + if (cob->matrix[3][2] > data->zmax) + cob->matrix[3][2] = data->zmax; + } +} - if (data->tar){ - constraint_target_to_mat4(data->tar, data->subtarget, mat, size); - valid=1; - } - else - Mat4One (mat); - } - break; - case CONSTRAINT_TYPE_TRACKTO: - { - bTrackToConstraint *data; - data = (bTrackToConstraint*)con->data; +static bConstraintTypeInfo CTI_LOCLIMIT = { + CONSTRAINT_TYPE_LOCLIMIT, /* type */ + sizeof(bLocLimitConstraint), /* size */ + "Limit Location", /* name */ + "bLocLimitConstraint", /* struct name */ + NULL, /* free data */ + NULL, /* relink data */ + NULL, /* copy data */ + NULL, /* new data */ + NULL, /* get constraint targets */ + NULL, /* flush constraint targets */ + NULL, /* get target matrix */ + loclimit_evaluate /* evaluate */ +}; + +/* -------- Limit Rotation --------- */ + +static void rotlimit_evaluate (bConstraint *con, bConstraintOb *cob, ListBase *targets) +{ + bRotLimitConstraint *data = con->data; + float loc[3]; + float eul[3]; + float size[3]; + + VECCOPY(loc, cob->matrix[3]); + Mat4ToSize(cob->matrix, size); + + Mat4ToEul(cob->matrix, eul); + + /* eulers: radians to degrees! */ + eul[0] = (eul[0] / M_PI * 180); + eul[1] = (eul[1] / M_PI * 180); + eul[2] = (eul[2] / M_PI * 180); + + /* limiting of euler values... */ + if (data->flag & LIMIT_XROT) { + if (eul[0] < data->xmin) + eul[0] = data->xmin; + + if (eul[0] > data->xmax) + eul[0] = data->xmax; + } + if (data->flag & LIMIT_YROT) { + if (eul[1] < data->ymin) + eul[1] = data->ymin; + + if (eul[1] > data->ymax) + eul[1] = data->ymax; + } + if (data->flag & LIMIT_ZROT) { + if (eul[2] < data->zmin) + eul[2] = data->zmin; + + if (eul[2] > data->zmax) + eul[2] = data->zmax; + } + + /* eulers: degrees to radians ! */ + eul[0] = (eul[0] / 180 * M_PI); + eul[1] = (eul[1] / 180 * M_PI); + eul[2] = (eul[2] / 180 * M_PI); + + LocEulSizeToMat4(cob->matrix, loc, eul, size); +} - if (data->tar){ - constraint_target_to_mat4(data->tar, data->subtarget, mat, size); - valid=1; - } - else - Mat4One (mat); - } - break; - case CONSTRAINT_TYPE_KINEMATIC: - { - bKinematicConstraint *data; - data = (bKinematicConstraint*)con->data; +static bConstraintTypeInfo CTI_ROTLIMIT = { + CONSTRAINT_TYPE_ROTLIMIT, /* type */ + sizeof(bRotLimitConstraint), /* size */ + "Limit Rotation", /* name */ + "bRotLimitConstraint", /* struct name */ + NULL, /* free data */ + NULL, /* relink data */ + NULL, /* copy data */ + NULL, /* new data */ + NULL, /* get constraint targets */ + NULL, /* flush constraint targets */ + NULL, /* get target matrix */ + rotlimit_evaluate /* evaluate */ +}; + +/* --------- Limit Scaling --------- */ + + +static void sizelimit_evaluate (bConstraint *con, bConstraintOb *cob, ListBase *targets) +{ + bSizeLimitConstraint *data = con->data; + float obsize[3], size[3]; + + Mat4ToSize(cob->matrix, size); + Mat4ToSize(cob->matrix, obsize); + + if (data->flag & LIMIT_XMIN) { + if (size[0] < data->xmin) + size[0] = data->xmin; + } + if (data->flag & LIMIT_XMAX) { + if (size[0] > data->xmax) + size[0] = data->xmax; + } + if (data->flag & LIMIT_YMIN) { + if (size[1] < data->ymin) + size[1] = data->ymin; + } + if (data->flag & LIMIT_YMAX) { + if (size[1] > data->ymax) + size[1] = data->ymax; + } + if (data->flag & LIMIT_ZMIN) { + if (size[2] < data->zmin) + size[2] = data->zmin; + } + if (data->flag & LIMIT_ZMAX) { + if (size[2] > data->zmax) + size[2] = data->zmax; + } + + if (obsize[0]) + VecMulf(cob->matrix[0], size[0]/obsize[0]); + if (obsize[1]) + VecMulf(cob->matrix[1], size[1]/obsize[1]); + if (obsize[2]) + VecMulf(cob->matrix[2], size[2]/obsize[2]); +} - if (data->tar){ - constraint_target_to_mat4(data->tar, data->subtarget, mat, size); - valid=1; - } - else if (data->flag & CONSTRAINT_IK_AUTO) { - Object *ob= ownerdata; - - if(ob==NULL) - Mat4One(mat); - else { - float vec[3]; - /* move grabtarget into world space */ - VECCOPY(vec, data->grabtarget); - Mat4MulVecfl(ob->obmat, vec); - Mat4CpyMat4(mat, ob->obmat); - VECCOPY(mat[3], vec); - } - } - else - Mat4One (mat); - } - break; - case CONSTRAINT_TYPE_LOCKTRACK: - { - bLockTrackConstraint *data; - data = (bLockTrackConstraint*)con->data; +static bConstraintTypeInfo CTI_SIZELIMIT = { + CONSTRAINT_TYPE_SIZELIMIT, /* type */ + sizeof(bSizeLimitConstraint), /* size */ + "Limit Scaling", /* name */ + "bSizeLimitConstraint", /* struct name */ + NULL, /* free data */ + NULL, /* relink data */ + NULL, /* copy data */ + NULL, /* new data */ + NULL, /* get constraint targets */ + NULL, /* flush constraint targets */ + NULL, /* get target matrix */ + sizelimit_evaluate /* evaluate */ +}; + +/* ----------- Copy Location ------------- */ + +static void loclike_new_data (void *cdata) +{ + bLocateLikeConstraint *data= (bLocateLikeConstraint *)cdata; + + data->flag = LOCLIKE_X|LOCLIKE_Y|LOCLIKE_Z; +} - if (data->tar){ - constraint_target_to_mat4(data->tar, data->subtarget, mat, size); - valid=1; - } - else - Mat4One (mat); - } - break; - case CONSTRAINT_TYPE_FOLLOWPATH: - { - bFollowPathConstraint *data; - data = (bFollowPathConstraint*)con->data; +static void loclike_get_tars (bConstraint *con, ListBase *list) +{ + if (con && list) { + bLocateLikeConstraint *data= con->data; + bConstraintTarget *ct; + + /* standard target-getting macro for single-target constraints */ + SINGLETARGET_GET_TARS(con, data->tar, data->subtarget, ct, list) + } +} - if (data->tar){ - Curve *cu; - float q[4], vec[4], dir[3], *quat, x1, totmat[4][4]; - float curvetime; +static void loclike_flush_tars (bConstraint *con, ListBase *list, short nocopy) +{ + if (con && list) { + bLocateLikeConstraint *data= con->data; + bConstraintTarget *ct= list->first; + + /* the following macro is used for all standard single-target constraints */ + SINGLETARGET_FLUSH_TARS(con, data->tar, data->subtarget, ct, list, nocopy) + } +} - Mat4One (totmat); - Mat4One (mat); +static void loclike_evaluate (bConstraint *con, bConstraintOb *cob, ListBase *targets) +{ + bLocateLikeConstraint *data= con->data; + bConstraintTarget *ct= targets->first; + + if (VALID_CONS_TARGET(ct)) { + float offset[3] = {0.0f, 0.0f, 0.0f}; + + if (data->flag & LOCLIKE_OFFSET) + VECCOPY(offset, cob->matrix[3]); + + if (data->flag & LOCLIKE_X) { + cob->matrix[3][0] = ct->matrix[3][0]; + + if (data->flag & LOCLIKE_X_INVERT) cob->matrix[3][0] *= -1; + cob->matrix[3][0] += offset[0]; + } + if (data->flag & LOCLIKE_Y) { + cob->matrix[3][1] = ct->matrix[3][1]; + + if (data->flag & LOCLIKE_Y_INVERT) cob->matrix[3][1] *= -1; + cob->matrix[3][1] += offset[1]; + } + if (data->flag & LOCLIKE_Z) { + cob->matrix[3][2] = ct->matrix[3][2]; + + if (data->flag & LOCLIKE_Z_INVERT) cob->matrix[3][2] *= -1; + cob->matrix[3][2] += offset[2]; + } + } +} - cu= data->tar->data; +static bConstraintTypeInfo CTI_LOCLIKE = { + CONSTRAINT_TYPE_LOCLIKE, /* type */ + sizeof(bLocateLikeConstraint), /* size */ + "Copy Location", /* name */ + "bLocateLikeConstraint", /* struct name */ + NULL, /* free data */ + NULL, /* relink data */ + NULL, /* copy data */ + loclike_new_data, /* new data */ + loclike_get_tars, /* get constraint targets */ + loclike_flush_tars, /* flush constraint targets */ + default_get_tarmat, /* get target matrix */ + loclike_evaluate /* evaluate */ +}; + +/* ----------- Copy Rotation ------------- */ + +static void rotlike_new_data (void *cdata) +{ + bRotateLikeConstraint *data= (bRotateLikeConstraint *)cdata; + + data->flag = ROTLIKE_X|ROTLIKE_Y|ROTLIKE_Z; +} - /* note; when creating constraints that follow path, the curve gets the CU_PATH set now, - currently for paths to work it needs to go through the bevlist/displist system (ton) */ - - if(cu->path==NULL || cu->path->data==NULL) /* only happens on reload file, but violates depsgraph still... fix! */ - makeDispListCurveTypes(data->tar, 0); - if(cu->path && cu->path->data) { - - curvetime= bsystem_time(data->tar, data->tar->parent, (float)ctime, 0.0) - data->offset; +static void rotlike_get_tars (bConstraint *con, ListBase *list) +{ + if (con && list) { + bRotateLikeConstraint *data= con->data; + bConstraintTarget *ct; + + /* standard target-getting macro for single-target constraints */ + SINGLETARGET_GET_TARS(con, data->tar, data->subtarget, ct, list) + } +} - if(calc_ipo_spec(cu->ipo, CU_SPEED, &curvetime)==0) { - curvetime /= cu->pathlen; - CLAMP(curvetime, 0.0, 1.0); - } +static void rotlike_flush_tars (bConstraint *con, ListBase *list, short nocopy) +{ + if (con && list) { + bRotateLikeConstraint *data= con->data; + bConstraintTarget *ct= list->first; + + /* the following macro is used for all standard single-target constraints */ + SINGLETARGET_FLUSH_TARS(con, data->tar, data->subtarget, ct, list, nocopy) + } +} - if(where_on_path(data->tar, curvetime, vec, dir) ) { +static void rotlike_evaluate (bConstraint *con, bConstraintOb *cob, ListBase *targets) +{ + bRotateLikeConstraint *data= con->data; + bConstraintTarget *ct= targets->first; + + if (VALID_CONS_TARGET(ct)) { + float loc[3]; + float eul[3], obeul[3]; + float size[3]; + + VECCOPY(loc, cob->matrix[3]); + Mat4ToSize(cob->matrix, size); + + Mat4ToEul(ct->matrix, eul); + Mat4ToEul(cob->matrix, obeul); + + if ((data->flag & ROTLIKE_X)==0) + eul[0] = obeul[0]; + else { + if (data->flag & ROTLIKE_OFFSET) + euler_rot(eul, obeul[0], 'x'); + + if (data->flag & ROTLIKE_X_INVERT) + eul[0] *= -1; + } + + if ((data->flag & ROTLIKE_Y)==0) + eul[1] = obeul[1]; + else { + if (data->flag & ROTLIKE_OFFSET) + euler_rot(eul, obeul[1], 'y'); + + if (data->flag & ROTLIKE_Y_INVERT) + eul[1] *= -1; + } + + if ((data->flag & ROTLIKE_Z)==0) + eul[2] = obeul[2]; + else { + if (data->flag & ROTLIKE_OFFSET) + euler_rot(eul, obeul[2], 'z'); + + if (data->flag & ROTLIKE_Z_INVERT) + eul[2] *= -1; + } + + compatible_eul(eul, obeul); + LocEulSizeToMat4(cob->matrix, loc, eul, size); + } +} - if(data->followflag){ - quat= vectoquat(dir, (short) data->trackflag, (short) data->upflag); +static bConstraintTypeInfo CTI_ROTLIKE = { + CONSTRAINT_TYPE_ROTLIKE, /* type */ + sizeof(bRotateLikeConstraint), /* size */ + "Copy Rotation", /* name */ + "bRotateLikeConstraint", /* struct name */ + NULL, /* free data */ + NULL, /* relink data */ + NULL, /* copy data */ + rotlike_new_data, /* new data */ + rotlike_get_tars, /* get constraint targets */ + rotlike_flush_tars, /* flush constraint targets */ + default_get_tarmat, /* get target matrix */ + rotlike_evaluate /* evaluate */ +}; + +/* ---------- Copy Scaling ---------- */ + +static void sizelike_new_data (void *cdata) +{ + bSizeLikeConstraint *data= (bSizeLikeConstraint *)cdata; + + data->flag = SIZELIKE_X|SIZELIKE_Y|SIZELIKE_Z; +} - Normalize(dir); - q[0]= (float)cos(0.5*vec[3]); - x1= (float)sin(0.5*vec[3]); - q[1]= -x1*dir[0]; - q[2]= -x1*dir[1]; - q[3]= -x1*dir[2]; - QuatMul(quat, q, quat); - +static void sizelike_get_tars (bConstraint *con, ListBase *list) +{ + if (con && list) { + bSizeLikeConstraint *data= con->data; + bConstraintTarget *ct; + + /* standard target-getting macro for single-target constraints */ + SINGLETARGET_GET_TARS(con, data->tar, data->subtarget, ct, list) + } +} - QuatToMat4(quat, totmat); - } - VECCOPY(totmat[3], vec); +static void sizelike_flush_tars (bConstraint *con, ListBase *list, short nocopy) +{ + if (con && list) { + bSizeLikeConstraint *data= con->data; + bConstraintTarget *ct= list->first; + + /* the following macro is used for all standard single-target constraints */ + SINGLETARGET_FLUSH_TARS(con, data->tar, data->subtarget, ct, list, nocopy) + } +} - Mat4MulSerie(mat, data->tar->obmat, totmat, NULL, NULL, NULL, NULL, NULL, NULL); - } - } - valid=1; +static void sizelike_evaluate (bConstraint *con, bConstraintOb *cob, ListBase *targets) +{ + bSizeLikeConstraint *data= con->data; + bConstraintTarget *ct= targets->first; + + if (VALID_CONS_TARGET(ct)) { + float obsize[3], size[3]; + + Mat4ToSize(ct->matrix, size); + Mat4ToSize(cob->matrix, obsize); + + if ((data->flag & SIZELIKE_X) && (obsize[0] != 0)) { + if (data->flag & SIZELIKE_OFFSET) { + size[0] += (obsize[0] - 1.0f); + VecMulf(cob->matrix[0], size[0] / obsize[0]); } else - Mat4One (mat); + VecMulf(cob->matrix[0], size[0] / obsize[0]); } - break; - case CONSTRAINT_TYPE_STRETCHTO: - { - bStretchToConstraint *data; - data = (bStretchToConstraint*)con->data; - - if (data->tar){ - constraint_target_to_mat4(data->tar, data->subtarget, mat, size); - valid = 1; + if ((data->flag & SIZELIKE_Y) && (obsize[1] != 0)) { + if (data->flag & SIZELIKE_OFFSET) { + size[1] += (obsize[1] - 1.0f); + VecMulf(cob->matrix[1], size[1] / obsize[1]); } else - Mat4One (mat); + VecMulf(cob->matrix[1], size[1] / obsize[1]); } - break; - case CONSTRAINT_TYPE_CLAMPTO: - { - bClampToConstraint *data; - data = (bClampToConstraint*)con->data; - - if (data->tar) { - Curve *cu= data->tar->data; - - /* note; when creating constraints that follow path, the curve gets the CU_PATH set now, - currently for paths to work it needs to go through the bevlist/displist system (ton) */ - - if(cu->path==NULL || cu->path->data==NULL) /* only happens on reload file, but violates depsgraph still... fix! */ - makeDispListCurveTypes(data->tar, 0); + if ((data->flag & SIZELIKE_Z) && (obsize[2] != 0)) { + if (data->flag & SIZELIKE_OFFSET) { + size[2] += (obsize[2] - 1.0f); + VecMulf(cob->matrix[2], size[2] / obsize[2]); } - - Mat4One (mat); + else + VecMulf(cob->matrix[2], size[2] / obsize[2]); } - break; - - default: - Mat4One(mat); - break; } +} + +static bConstraintTypeInfo CTI_SIZELIKE = { + CONSTRAINT_TYPE_SIZELIKE, /* type */ + sizeof(bSizeLikeConstraint), /* size */ + "Copy Scale", /* name */ + "bSizeLikeConstraint", /* struct name */ + NULL, /* free data */ + NULL, /* relink data */ + NULL, /* copy data */ + sizelike_new_data, /* new data */ + sizelike_get_tars, /* get constraint targets */ + sizelike_flush_tars, /* flush constraint targets */ + default_get_tarmat, /* get target matrix */ + sizelike_evaluate /* evaluate */ +}; + +/* ----------- Python Constraint -------------- */ + +static void pycon_free (bConstraint *con) +{ + bPythonConstraint *data= con->data; + + /* id-properties */ + IDP_FreeProperty(data->prop); + MEM_freeN(data->prop); + + /* multiple targets */ + BLI_freelistN(&data->targets); +} - return valid; +static void pycon_relink (bConstraint *con) +{ + bPythonConstraint *data= con->data; + + ID_NEW(data->text); } -/* only called during solve_constraints */ -/* bone constraints create a fake object to work on, then ob is a workob */ -/* if ownerdata is set, it's the posechannel */ -void evaluate_constraint (bConstraint *constraint, Object *ob, short ownertype, void *ownerdata, float targetmat[][4]) +static void pycon_copy (bConstraint *con, bConstraint *srccon) { - float M_oldmat[4][4]; - float M_identity[4][4]; + bPythonConstraint *pycon = (bPythonConstraint *)con->data; + bPythonConstraint *opycon = (bPythonConstraint *)srccon->data; - if (!constraint || !ob) - return; + pycon->prop = IDP_CopyProperty(opycon->prop); + duplicatelist(&pycon->targets, &opycon->targets); +} - Mat4One (M_identity); +static void pycon_new_data (void *cdata) +{ + bPythonConstraint *data= (bPythonConstraint *)cdata; - switch (constraint->type) { - case CONSTRAINT_TYPE_NULL: - case CONSTRAINT_TYPE_KINEMATIC: /* removed */ - break; + /* everything should be set correctly by calloc, except for the prop->type constant.*/ + data->prop = MEM_callocN(sizeof(IDProperty), "PyConstraintProps"); + data->prop->type = IDP_GROUP; +} + +static void pycon_get_tars (bConstraint *con, ListBase *list) +{ + if (con && list) { + bPythonConstraint *data= con->data; + + list->first = data->targets.first; + list->last = data->targets.last; + } +} + +/* Whether this approach is maintained remains to be seen (aligorith) */ +static void pycon_get_tarmat (bConstraint *con, bConstraintOb *cob, bConstraintTarget *ct, float ctime) +{ + bPythonConstraint *data= con->data; - case CONSTRAINT_TYPE_ACTION: - { - bActionConstraint *data; - float temp[4][4]; + if (VALID_CONS_TARGET(ct)) { + /* special exception for curves - depsgraph issues */ + if (ct->tar->type == OB_CURVE) { + Curve *cu= ct->tar->data; - data = constraint->data; - Mat4CpyMat4 (temp, ob->obmat); - - Mat4MulMat4(ob->obmat, targetmat, temp); + /* this check is to make sure curve objects get updated on file load correctly.*/ + if (cu->path==NULL || cu->path->data==NULL) /* only happens on reload file, but violates depsgraph still... fix! */ + makeDispListCurveTypes(ct->tar, 0); } - break; - case CONSTRAINT_TYPE_LOCLIKE: - { - bLocateLikeConstraint *data; - float offset[3] = {0.0f, 0.0f, 0.0f}; + + /* firstly calculate the matrix the normal way, then let the py-function override + * this matrix if it needs to do so + */ + constraint_target_to_mat4(ct->tar, ct->subtarget, ct->matrix, CONSTRAINT_SPACE_WORLD, ct->space, con->headtail); + BPY_pyconstraint_target(data, ct); + } + else if (ct) + Mat4One(ct->matrix); +} - data = constraint->data; - - if (data->flag & LOCLIKE_OFFSET) - VECCOPY(offset, ob->obmat[3]); - - if (data->flag & LOCLIKE_X) { - ob->obmat[3][0] = targetmat[3][0]; - - if(data->flag & LOCLIKE_X_INVERT) ob->obmat[3][0] *= -1; - ob->obmat[3][0] += offset[0]; - } - if (data->flag & LOCLIKE_Y) { - ob->obmat[3][1] = targetmat[3][1]; - - if(data->flag & LOCLIKE_Y_INVERT) ob->obmat[3][1] *= -1; - ob->obmat[3][1] += offset[1]; - } - if (data->flag & LOCLIKE_Z) { - ob->obmat[3][2] = targetmat[3][2]; - - if(data->flag & LOCLIKE_Z_INVERT) ob->obmat[3][2] *= -1; - ob->obmat[3][2] += offset[2]; - } +static void pycon_evaluate (bConstraint *con, bConstraintOb *cob, ListBase *targets) +{ + bPythonConstraint *data= con->data; + +/* currently removed, until I this can be re-implemented for multiple targets */ +#if 0 + /* Firstly, run the 'driver' function which has direct access to the objects involved + * Technically, this is potentially dangerous as users may abuse this and cause dependency-problems, + * but it also allows certain 'clever' rigging hacks to work. + */ + BPY_pyconstraint_driver(data, cob, targets); +#endif + + /* Now, run the actual 'constraint' function, which should only access the matrices */ + BPY_pyconstraint_eval(data, cob, targets); +} + +static bConstraintTypeInfo CTI_PYTHON = { + CONSTRAINT_TYPE_PYTHON, /* type */ + sizeof(bPythonConstraint), /* size */ + "Script", /* name */ + "bPythonConstraint", /* struct name */ + pycon_free, /* free data */ + pycon_relink, /* relink data */ + pycon_copy, /* copy data */ + pycon_new_data, /* new data */ + pycon_get_tars, /* get constraint targets */ + NULL, /* flush constraint targets */ + pycon_get_tarmat, /* get target matrix */ + pycon_evaluate /* evaluate */ +}; + +/* -------- Action Constraint ----------- */ + +static void actcon_relink (bConstraint *con) +{ + bActionConstraint *data= con->data; + ID_NEW(data->act); +} + +static void actcon_new_data (void *cdata) +{ + bActionConstraint *data= (bActionConstraint *)cdata; + + /* set type to 20 (Loc X), as 0 is Rot X for backwards compatability */ + data->type = 20; +} + +static void actcon_get_tars (bConstraint *con, ListBase *list) +{ + if (con && list) { + bActionConstraint *data= con->data; + bConstraintTarget *ct; + + /* standard target-getting macro for single-target constraints */ + SINGLETARGET_GET_TARS(con, data->tar, data->subtarget, ct, list) + } +} + +static void actcon_flush_tars (bConstraint *con, ListBase *list, short nocopy) +{ + if (con && list) { + bActionConstraint *data= con->data; + bConstraintTarget *ct= list->first; + + /* the following macro is used for all standard single-target constraints */ + SINGLETARGET_FLUSH_TARS(con, data->tar, data->subtarget, ct, list, nocopy) + } +} + +static void actcon_get_tarmat (bConstraint *con, bConstraintOb *cob, bConstraintTarget *ct, float ctime) +{ + extern void chan_calc_mat(bPoseChannel *chan); + bActionConstraint *data = con->data; + + if (VALID_CONS_TARGET(ct)) { + float tempmat[4][4], vec[3]; + float s, t; + short axis; + + /* initialise return matrix */ + Mat4One(ct->matrix); + + /* get the transform matrix of the target */ + constraint_target_to_mat4(ct->tar, ct->subtarget, tempmat, CONSTRAINT_SPACE_WORLD, ct->space, con->headtail); + + /* determine where in transform range target is */ + /* data->type is mapped as follows for backwards compatability: + * 00,01,02 - rotation (it used to be like this) + * 10,11,12 - scaling + * 20,21,22 - location + */ + if (data->type < 10) { + /* extract rotation (is in whatever space target should be in) */ + Mat4ToEul(tempmat, vec); + vec[0] *= (float)(180.0/M_PI); + vec[1] *= (float)(180.0/M_PI); + vec[2] *= (float)(180.0/M_PI); + axis= data->type; + } + else if (data->type < 20) { + /* extract scaling (is in whatever space target should be in) */ + Mat4ToSize(tempmat, vec); + axis= data->type - 10; + } + else { + /* extract location */ + VECCOPY(vec, tempmat[3]); + axis= data->type - 20; } - break; - case CONSTRAINT_TYPE_ROTLIKE: - { - bRotateLikeConstraint *data; - float loc[3]; - float eul[3], obeul[3]; - float size[3]; + + /* Target defines the animation */ + s = (vec[axis]-data->min) / (data->max-data->min); + CLAMP(s, 0, 1); + t = ( s * (data->end-data->start)) + data->start; + + /* Get the appropriate information from the action */ + if (cob->type == CONSTRAINT_OBTYPE_BONE) { + bPose *pose; + bPoseChannel *pchan, *tchan; - data = constraint->data; + /* make a temporary pose and evaluate using that */ + pose = MEM_callocN(sizeof(bPose), "pose"); - VECCOPY(loc, ob->obmat[3]); - Mat4ToSize(ob->obmat, size); + pchan = cob->pchan; + tchan= verify_pose_channel(pose, pchan->name); + extract_pose_from_action(pose, data->act, t); - Mat4ToEul(targetmat, eul); - Mat4ToEul(ob->obmat, obeul); - - if(data->flag != (ROTLIKE_X|ROTLIKE_Y|ROTLIKE_Z)) { - if(!(data->flag & ROTLIKE_X)) { - eul[0]= obeul[0]; - } - if(!(data->flag & ROTLIKE_Y)) { - eul[1]= obeul[1]; - } - if(!(data->flag & ROTLIKE_Z)) { - eul[2]= obeul[2]; - } - compatible_eul(eul, obeul); - } + chan_calc_mat(tchan); - if((data->flag & ROTLIKE_X) && (data->flag & ROTLIKE_X_INVERT)) - eul[0]*=-1; - if((data->flag & ROTLIKE_Y) && (data->flag & ROTLIKE_Y_INVERT)) - eul[1]*=-1; - if((data->flag & ROTLIKE_Z) && (data->flag & ROTLIKE_Z_INVERT)) - eul[2]*=-1; + Mat4CpyMat4(ct->matrix, tchan->chan_mat); - LocEulSizeToMat4(ob->obmat, loc, eul, size); + /* Clean up */ + free_pose_channels(pose); + MEM_freeN(pose); } - break; - case CONSTRAINT_TYPE_SIZELIKE: - { - bSizeLikeConstraint *data; - float obsize[3], size[3]; - - data = constraint->data; - - Mat4ToSize(targetmat, size); - Mat4ToSize(ob->obmat, obsize); - - if (data->flag & SIZELIKE_X && obsize[0] != 0) - VecMulf(ob->obmat[0], size[0] / obsize[0]); - if (data->flag & SIZELIKE_Y && obsize[1] != 0) - VecMulf(ob->obmat[1], size[1] / obsize[1]); - if (data->flag & SIZELIKE_Z && obsize[2] != 0) - VecMulf(ob->obmat[2], size[2] / obsize[2]); - } - break; - case CONSTRAINT_TYPE_MINMAX: - { - bMinMaxConstraint *data; - float val1, val2; - int index; - float obmat[4][4],imat[4][4],tarmat[4][4],tmat[4][4]; + else if (cob->type == CONSTRAINT_OBTYPE_OBJECT) { + /* evaluate using workob */ + what_does_obaction(cob->ob, data->act, t); + object_to_mat4(&workob, ct->matrix); + } + else { + /* behaviour undefined... */ + puts("Error: unknown owner type for Action Constraint"); + } + } +} - data = constraint->data; - - Mat4CpyMat4(obmat,ob->obmat); - Mat4CpyMat4(tarmat,targetmat); - - if (data->flag&MINMAX_USEROT) { - /* take rotation of target into account by doing the transaction in target's localspace */ - Mat4Invert(imat,tarmat); - Mat4MulMat4(tmat,obmat,imat); - Mat4CpyMat4(obmat,tmat); - Mat4One(tarmat); - } +static void actcon_evaluate (bConstraint *con, bConstraintOb *cob, ListBase *targets) +{ + bConstraintTarget *ct= targets->first; + + if (VALID_CONS_TARGET(ct)) { + float temp[4][4]; + + /* Nice and simple... we just need to multiply the matrices, as the get_target_matrix + * function has already taken care of everything else. + */ + Mat4CpyMat4(temp, cob->matrix); + Mat4MulMat4(cob->matrix, ct->matrix, temp); + } +} - switch (data->minmaxflag) { - case TRACK_Z: - val1 = tarmat[3][2]; - val2 = obmat[3][2]-data->offset; - index = 2; - break; - case TRACK_Y: - val1 = tarmat[3][1]; - val2 = obmat[3][1]-data->offset; - index = 1; - break; - case TRACK_X: - val1 = tarmat[3][0]; - val2 = obmat[3][0]-data->offset; - index = 0; - break; - case TRACK_nZ: - val2 = tarmat[3][2]; - val1 = obmat[3][2]-data->offset; - index = 2; - break; - case TRACK_nY: - val2 = tarmat[3][1]; - val1 = obmat[3][1]-data->offset; - index = 1; - break; - case TRACK_nX: - val2 = tarmat[3][0]; - val1 = obmat[3][0]-data->offset; - index = 0; - break; - default: - return; - } - - if (val1 > val2) { - obmat[3][index] = tarmat[3][index] + data->offset; - if (data->flag & MINMAX_STICKY) { - if (data->flag & MINMAX_STUCK) { - VECCOPY(obmat[3], data->cache); - } - else { - VECCOPY(data->cache, obmat[3]); - data->flag|=MINMAX_STUCK; - } - } - if (data->flag & MINMAX_USEROT) { - /* get out of localspace */ - Mat4MulMat4(tmat,obmat,targetmat); - Mat4CpyMat4(ob->obmat,tmat); - } - else { - VECCOPY(ob->obmat[3],obmat[3]); - } - } - else { - data->flag&=~MINMAX_STUCK; - } - - } - break; - case CONSTRAINT_TYPE_TRACKTO: - { - bTrackToConstraint *data; - float size[3]; - float vec[3]; - float totmat[3][3]; - float tmat[4][4]; +static bConstraintTypeInfo CTI_ACTION = { + CONSTRAINT_TYPE_ACTION, /* type */ + sizeof(bActionConstraint), /* size */ + "Action", /* name */ + "bActionConstraint", /* struct name */ + NULL, /* free data */ + actcon_relink, /* relink data */ + NULL, /* copy data */ + actcon_new_data, /* new data */ + actcon_get_tars, /* get constraint targets */ + actcon_flush_tars, /* flush constraint targets */ + actcon_get_tarmat, /* get target matrix */ + actcon_evaluate /* evaluate */ +}; + +/* --------- Locked Track ---------- */ + +static void locktrack_new_data (void *cdata) +{ + bLockTrackConstraint *data= (bLockTrackConstraint *)cdata; + + data->trackflag = TRACK_Y; + data->lockflag = LOCK_Z; +} - data=(bTrackToConstraint*)constraint->data; - - if (data->tar) { - /* Get size property, since ob->size is only the object's own relative size, not its global one */ - Mat4ToSize (ob->obmat, size); - - Mat4CpyMat4 (M_oldmat, ob->obmat); - - // Clear the object's rotation - ob->obmat[0][0]=size[0]; - ob->obmat[0][1]=0; - ob->obmat[0][2]=0; - ob->obmat[1][0]=0; - ob->obmat[1][1]=size[1]; - ob->obmat[1][2]=0; - ob->obmat[2][0]=0; - ob->obmat[2][1]=0; - ob->obmat[2][2]=size[2]; +static void locktrack_get_tars (bConstraint *con, ListBase *list) +{ + if (con && list) { + bLockTrackConstraint *data= con->data; + bConstraintTarget *ct; + + /* the following macro is used for all standard single-target constraints */ + SINGLETARGET_GET_TARS(con, data->tar, data->subtarget, ct, list) + } +} + +static void locktrack_flush_tars (bConstraint *con, ListBase *list, short nocopy) +{ + if (con && list) { + bLockTrackConstraint *data= con->data; + bConstraintTarget *ct= list->first; + + /* the following macro is used for all standard single-target constraints */ + SINGLETARGET_FLUSH_TARS(con, data->tar, data->subtarget, ct, list, nocopy) + } +} + +static void locktrack_evaluate (bConstraint *con, bConstraintOb *cob, ListBase *targets) +{ + bLockTrackConstraint *data= con->data; + bConstraintTarget *ct= targets->first; - - VecSubf(vec, ob->obmat[3], targetmat[3]); - vectomat(vec, targetmat[2], - (short)data->reserved1, (short)data->reserved2, - data->flags, totmat); - - Mat4CpyMat4(tmat, ob->obmat); - - Mat4MulMat34(ob->obmat, totmat, tmat); - } - } - break; - case CONSTRAINT_TYPE_LOCKTRACK: + if (VALID_CONS_TARGET(ct)) { + float vec[3],vec2[3]; + float totmat[3][3]; + float tmpmat[3][3]; + float invmat[3][3]; + float tmat[4][4]; + float mdet; + + /* Vector object -> target */ + VecSubf(vec, ct->matrix[3], cob->matrix[3]); + switch (data->lockflag){ + case LOCK_X: /* LOCK X */ { - bLockTrackConstraint *data; - float vec[3],vec2[3]; - float totmat[3][3]; - float tmpmat[3][3]; - float invmat[3][3]; - float tmat[4][4]; - float mdet; - - data=(bLockTrackConstraint*)constraint->data; - - if (data->tar) { - Mat4CpyMat4 (M_oldmat, ob->obmat); - - /* Vector object -> target */ - VecSubf(vec, targetmat[3], ob->obmat[3]); - switch (data->lockflag){ - case LOCK_X: /* LOCK X */ - { - switch (data->trackflag){ - case TRACK_Y: /* LOCK X TRACK Y */ - { - /* Projection of Vector on the plane */ - Projf(vec2, vec, ob->obmat[0]); - VecSubf(totmat[1], vec, vec2); - Normalize(totmat[1]); - - /* the x axis is fixed*/ - totmat[0][0] = ob->obmat[0][0]; - totmat[0][1] = ob->obmat[0][1]; - totmat[0][2] = ob->obmat[0][2]; - Normalize(totmat[0]); - - /* the z axis gets mapped onto - a third orthogonal vector */ - Crossf(totmat[2], totmat[0], totmat[1]); - } - break; - case TRACK_Z: /* LOCK X TRACK Z */ - { - /* Projection of Vector on the plane */ - Projf(vec2, vec, ob->obmat[0]); - VecSubf(totmat[2], vec, vec2); - Normalize(totmat[2]); - - /* the x axis is fixed*/ - totmat[0][0] = ob->obmat[0][0]; - totmat[0][1] = ob->obmat[0][1]; - totmat[0][2] = ob->obmat[0][2]; - Normalize(totmat[0]); - - /* the z axis gets mapped onto - a third orthogonal vector */ - Crossf(totmat[1], totmat[2], totmat[0]); - } - break; - case TRACK_nY: /* LOCK X TRACK -Y */ - { - /* Projection of Vector on the plane */ - Projf(vec2, vec, ob->obmat[0]); - VecSubf(totmat[1], vec, vec2); - Normalize(totmat[1]); - VecMulf(totmat[1],-1); - - /* the x axis is fixed*/ - totmat[0][0] = ob->obmat[0][0]; - totmat[0][1] = ob->obmat[0][1]; - totmat[0][2] = ob->obmat[0][2]; - Normalize(totmat[0]); - - /* the z axis gets mapped onto - a third orthogonal vector */ - Crossf(totmat[2], totmat[0], totmat[1]); - } - break; - case TRACK_nZ: /* LOCK X TRACK -Z */ - { - /* Projection of Vector on the plane */ - Projf(vec2, vec, ob->obmat[0]); - VecSubf(totmat[2], vec, vec2); - Normalize(totmat[2]); - VecMulf(totmat[2],-1); - - /* the x axis is fixed*/ - totmat[0][0] = ob->obmat[0][0]; - totmat[0][1] = ob->obmat[0][1]; - totmat[0][2] = ob->obmat[0][2]; - Normalize(totmat[0]); - - /* the z axis gets mapped onto - a third orthogonal vector */ - Crossf(totmat[1], totmat[2], totmat[0]); - } - break; - default: - { - totmat[0][0] = 1;totmat[0][1] = 0;totmat[0][2] = 0; - totmat[1][0] = 0;totmat[1][1] = 1;totmat[1][2] = 0; - totmat[2][0] = 0;totmat[2][1] = 0;totmat[2][2] = 1; - } - break; - } - } + switch (data->trackflag) { + case TRACK_Y: /* LOCK X TRACK Y */ + { + /* Projection of Vector on the plane */ + Projf(vec2, vec, cob->matrix[0]); + VecSubf(totmat[1], vec, vec2); + Normalize(totmat[1]); + + /* the x axis is fixed */ + totmat[0][0] = cob->matrix[0][0]; + totmat[0][1] = cob->matrix[0][1]; + totmat[0][2] = cob->matrix[0][2]; + Normalize(totmat[0]); + + /* the z axis gets mapped onto a third orthogonal vector */ + Crossf(totmat[2], totmat[0], totmat[1]); + } break; - case LOCK_Y: /* LOCK Y */ - { - switch (data->trackflag){ - case TRACK_X: /* LOCK Y TRACK X */ - { - /* Projection of Vector on the plane */ - Projf(vec2, vec, ob->obmat[1]); - VecSubf(totmat[0], vec, vec2); - Normalize(totmat[0]); - - /* the y axis is fixed*/ - totmat[1][0] = ob->obmat[1][0]; - totmat[1][1] = ob->obmat[1][1]; - totmat[1][2] = ob->obmat[1][2]; - Normalize(totmat[1]); - - /* the z axis gets mapped onto - a third orthogonal vector */ - Crossf(totmat[2], totmat[0], totmat[1]); - } - break; - case TRACK_Z: /* LOCK Y TRACK Z */ - { - /* Projection of Vector on the plane */ - Projf(vec2, vec, ob->obmat[1]); - VecSubf(totmat[2], vec, vec2); - Normalize(totmat[2]); - - /* the y axis is fixed*/ - totmat[1][0] = ob->obmat[1][0]; - totmat[1][1] = ob->obmat[1][1]; - totmat[1][2] = ob->obmat[1][2]; - Normalize(totmat[1]); - - /* the z axis gets mapped onto - a third orthogonal vector */ - Crossf(totmat[0], totmat[1], totmat[2]); - } - break; - case TRACK_nX: /* LOCK Y TRACK -X */ - { - /* Projection of Vector on the plane */ - Projf(vec2, vec, ob->obmat[1]); - VecSubf(totmat[0], vec, vec2); - Normalize(totmat[0]); - VecMulf(totmat[0],-1); - - /* the y axis is fixed*/ - totmat[1][0] = ob->obmat[1][0]; - totmat[1][1] = ob->obmat[1][1]; - totmat[1][2] = ob->obmat[1][2]; - Normalize(totmat[1]); - - /* the z axis gets mapped onto - a third orthogonal vector */ - Crossf(totmat[2], totmat[0], totmat[1]); - } - break; - case TRACK_nZ: /* LOCK Y TRACK -Z */ - { - /* Projection of Vector on the plane */ - Projf(vec2, vec, ob->obmat[1]); - VecSubf(totmat[2], vec, vec2); - Normalize(totmat[2]); - VecMulf(totmat[2],-1); - - /* the y axis is fixed*/ - totmat[1][0] = ob->obmat[1][0]; - totmat[1][1] = ob->obmat[1][1]; - totmat[1][2] = ob->obmat[1][2]; - Normalize(totmat[1]); - - /* the z axis gets mapped onto - a third orthogonal vector */ - Crossf(totmat[0], totmat[1], totmat[2]); - } - break; - default: - { - totmat[0][0] = 1;totmat[0][1] = 0;totmat[0][2] = 0; - totmat[1][0] = 0;totmat[1][1] = 1;totmat[1][2] = 0; - totmat[2][0] = 0;totmat[2][1] = 0;totmat[2][2] = 1; - } - break; - } - } + case TRACK_Z: /* LOCK X TRACK Z */ + { + /* Projection of Vector on the plane */ + Projf(vec2, vec, cob->matrix[0]); + VecSubf(totmat[2], vec, vec2); + Normalize(totmat[2]); + + /* the x axis is fixed */ + totmat[0][0] = cob->matrix[0][0]; + totmat[0][1] = cob->matrix[0][1]; + totmat[0][2] = cob->matrix[0][2]; + Normalize(totmat[0]); + + /* the z axis gets mapped onto a third orthogonal vector */ + Crossf(totmat[1], totmat[2], totmat[0]); + } break; - case LOCK_Z: /* LOCK Z */ - { - switch (data->trackflag){ - case TRACK_X: /* LOCK Z TRACK X */ - { - /* Projection of Vector on the plane */ - Projf(vec2, vec, ob->obmat[2]); - VecSubf(totmat[0], vec, vec2); - Normalize(totmat[0]); - - /* the z axis is fixed*/ - totmat[2][0] = ob->obmat[2][0]; - totmat[2][1] = ob->obmat[2][1]; - totmat[2][2] = ob->obmat[2][2]; - Normalize(totmat[2]); - - /* the x axis gets mapped onto - a third orthogonal vector */ - Crossf(totmat[1], totmat[2], totmat[0]); - } - break; - case TRACK_Y: /* LOCK Z TRACK Y */ - { - /* Projection of Vector on the plane */ - Projf(vec2, vec, ob->obmat[2]); - VecSubf(totmat[1], vec, vec2); - Normalize(totmat[1]); - - /* the z axis is fixed*/ - totmat[2][0] = ob->obmat[2][0]; - totmat[2][1] = ob->obmat[2][1]; - totmat[2][2] = ob->obmat[2][2]; - Normalize(totmat[2]); - - /* the x axis gets mapped onto - a third orthogonal vector */ - Crossf(totmat[0], totmat[1], totmat[2]); - } - break; - case TRACK_nX: /* LOCK Z TRACK -X */ - { - /* Projection of Vector on the plane */ - Projf(vec2, vec, ob->obmat[2]); - VecSubf(totmat[0], vec, vec2); - Normalize(totmat[0]); - VecMulf(totmat[0],-1); - - /* the z axis is fixed*/ - totmat[2][0] = ob->obmat[2][0]; - totmat[2][1] = ob->obmat[2][1]; - totmat[2][2] = ob->obmat[2][2]; - Normalize(totmat[2]); + case TRACK_nY: /* LOCK X TRACK -Y */ + { + /* Projection of Vector on the plane */ + Projf(vec2, vec, cob->matrix[0]); + VecSubf(totmat[1], vec, vec2); + Normalize(totmat[1]); + VecMulf(totmat[1],-1); + + /* the x axis is fixed */ + totmat[0][0] = cob->matrix[0][0]; + totmat[0][1] = cob->matrix[0][1]; + totmat[0][2] = cob->matrix[0][2]; + Normalize(totmat[0]); + + /* the z axis gets mapped onto a third orthogonal vector */ + Crossf(totmat[2], totmat[0], totmat[1]); + } + break; + case TRACK_nZ: /* LOCK X TRACK -Z */ + { + /* Projection of Vector on the plane */ + Projf(vec2, vec, cob->matrix[0]); + VecSubf(totmat[2], vec, vec2); + Normalize(totmat[2]); + VecMulf(totmat[2],-1); - /* the x axis gets mapped onto - a third orthogonal vector */ - Crossf(totmat[1], totmat[2], totmat[0]); - } - break; - case TRACK_nY: /* LOCK Z TRACK -Y */ - { - /* Projection of Vector on the plane */ - Projf(vec2, vec, ob->obmat[2]); - VecSubf(totmat[1], vec, vec2); - Normalize(totmat[1]); - VecMulf(totmat[1],-1); - - /* the z axis is fixed*/ - totmat[2][0] = ob->obmat[2][0]; - totmat[2][1] = ob->obmat[2][1]; - totmat[2][2] = ob->obmat[2][2]; - Normalize(totmat[2]); + /* the x axis is fixed */ + totmat[0][0] = cob->matrix[0][0]; + totmat[0][1] = cob->matrix[0][1]; + totmat[0][2] = cob->matrix[0][2]; + Normalize(totmat[0]); - /* the x axis gets mapped onto - a third orthogonal vector */ - Crossf(totmat[0], totmat[1], totmat[2]); - } - break; - default: - { - totmat[0][0] = 1;totmat[0][1] = 0;totmat[0][2] = 0; - totmat[1][0] = 0;totmat[1][1] = 1;totmat[1][2] = 0; - totmat[2][0] = 0;totmat[2][1] = 0;totmat[2][2] = 1; - } - break; - } - } + /* the z axis gets mapped onto a third orthogonal vector */ + Crossf(totmat[1], totmat[2], totmat[0]); + } break; default: - { - totmat[0][0] = 1;totmat[0][1] = 0;totmat[0][2] = 0; - totmat[1][0] = 0;totmat[1][1] = 1;totmat[1][2] = 0; - totmat[2][0] = 0;totmat[2][1] = 0;totmat[2][2] = 1; - } - break; - } - /* Block to keep matrix heading */ - tmpmat[0][0] = ob->obmat[0][0];tmpmat[0][1] = ob->obmat[0][1];tmpmat[0][2] = ob->obmat[0][2]; - tmpmat[1][0] = ob->obmat[1][0];tmpmat[1][1] = ob->obmat[1][1];tmpmat[1][2] = ob->obmat[1][2]; - tmpmat[2][0] = ob->obmat[2][0];tmpmat[2][1] = ob->obmat[2][1];tmpmat[2][2] = ob->obmat[2][2]; - Normalize(tmpmat[0]); - Normalize(tmpmat[1]); - Normalize(tmpmat[2]); - Mat3Inv(invmat,tmpmat); - Mat3MulMat3(tmpmat,totmat,invmat); - totmat[0][0] = tmpmat[0][0];totmat[0][1] = tmpmat[0][1];totmat[0][2] = tmpmat[0][2]; - totmat[1][0] = tmpmat[1][0];totmat[1][1] = tmpmat[1][1];totmat[1][2] = tmpmat[1][2]; - totmat[2][0] = tmpmat[2][0];totmat[2][1] = tmpmat[2][1];totmat[2][2] = tmpmat[2][2]; - - Mat4CpyMat4(tmat, ob->obmat); - - mdet = Det3x3( totmat[0][0],totmat[0][1],totmat[0][2], - totmat[1][0],totmat[1][1],totmat[1][2], - totmat[2][0],totmat[2][1],totmat[2][2]); - if (mdet==0) { totmat[0][0] = 1;totmat[0][1] = 0;totmat[0][2] = 0; totmat[1][0] = 0;totmat[1][1] = 1;totmat[1][2] = 0; totmat[2][0] = 0;totmat[2][1] = 0;totmat[2][2] = 1; } - - /* apply out transformaton to the object */ - Mat4MulMat34(ob->obmat, totmat, tmat); - } - } - break; - case CONSTRAINT_TYPE_FOLLOWPATH: - { - bFollowPathConstraint *data; - float obmat[4][4]; - float size[3], obsize[3]; - - data=(bFollowPathConstraint*)constraint->data; - - if (data->tar) { - /* get Object local transform (loc/rot/size) to determine transformation from path */ - object_to_mat4(ob, obmat); - - /* get scaling of object before applying constraint */ - Mat4ToSize(ob->obmat, size); - - /* apply targetmat - containing location on path, and rotation */ - Mat4MulSerie(ob->obmat, targetmat, obmat, NULL, NULL, NULL, NULL, NULL, NULL); - - /* un-apply scaling caused by path */ - Mat4ToSize(ob->obmat, obsize); - if (obsize[0] != 0) - VecMulf(ob->obmat[0], size[0] / obsize[0]); - if (obsize[1] != 0) - VecMulf(ob->obmat[1], size[1] / obsize[1]); - if (obsize[2] != 0) - VecMulf(ob->obmat[2], size[2] / obsize[2]); + break; } } - break; - case CONSTRAINT_TYPE_STRETCHTO: - { - bStretchToConstraint *data; - float size[3],scale[3],vec[3],xx[3],zz[3],orth[3]; - float totmat[3][3]; - float tmat[4][4]; - float dist; - - data=(bStretchToConstraint*)constraint->data; - Mat4ToSize (ob->obmat, size); - - if (data->tar) { - /* store X orientation before destroying obmat */ - xx[0] = ob->obmat[0][0]; - xx[1] = ob->obmat[0][1]; - xx[2] = ob->obmat[0][2]; - Normalize(xx); - - /* store Z orientation before destroying obmat */ - zz[0] = ob->obmat[2][0]; - zz[1] = ob->obmat[2][1]; - zz[2] = ob->obmat[2][2]; - Normalize(zz); - - VecSubf(vec, ob->obmat[3], targetmat[3]); - vec[0] /= size[0]; - vec[1] /= size[1]; - vec[2] /= size[2]; - - dist = Normalize(vec); - //dist = VecLenf( ob->obmat[3], targetmat[3]); - - if (data->orglength == 0) data->orglength = dist; - if (data->bulge ==0) data->bulge = 1.0; - - scale[1] = dist/data->orglength; - switch (data->volmode) { - /* volume preserving scaling */ - case VOLUME_XZ : - scale[0] = 1.0f - (float)sqrt(data->bulge) + (float)sqrt(data->bulge*(data->orglength/dist)); - scale[2] = scale[0]; - break; - case VOLUME_X: - scale[0] = 1.0f + data->bulge * (data->orglength /dist - 1); - scale[2] = 1.0; - break; - case VOLUME_Z: - scale[0] = 1.0; - scale[2] = 1.0f + data->bulge * (data->orglength /dist - 1); - break; - /* don't care for volume */ - case NO_VOLUME: - scale[0] = 1.0; - scale[2] = 1.0; - break; - default: /* should not happen, but in case*/ - return; - } /* switch (data->volmode) */ - - /* Clear the object's rotation and scale */ - ob->obmat[0][0]=size[0]*scale[0]; - ob->obmat[0][1]=0; - ob->obmat[0][2]=0; - ob->obmat[1][0]=0; - ob->obmat[1][1]=size[1]*scale[1]; - ob->obmat[1][2]=0; - ob->obmat[2][0]=0; - ob->obmat[2][1]=0; - ob->obmat[2][2]=size[2]*scale[2]; - - VecSubf(vec, ob->obmat[3], targetmat[3]); - Normalize(vec); - /* new Y aligns object target connection*/ - totmat[1][0] = -vec[0]; - totmat[1][1] = -vec[1]; - totmat[1][2] = -vec[2]; - switch (data->plane) { - case PLANE_X: - /* build new Z vector */ - /* othogonal to "new Y" "old X! plane */ - Crossf(orth, vec, xx); - Normalize(orth); - - /* new Z*/ - totmat[2][0] = orth[0]; - totmat[2][1] = orth[1]; - totmat[2][2] = orth[2]; - - /* we decided to keep X plane*/ - Crossf(xx,orth, vec); - Normalize(xx); - totmat[0][0] = xx[0]; - totmat[0][1] = xx[1]; - totmat[0][2] = xx[2]; - break; - case PLANE_Z: - /* build new X vector */ - /* othogonal to "new Y" "old Z! plane */ - Crossf(orth, vec, zz); - Normalize(orth); - - /* new X */ - totmat[0][0] = -orth[0]; - totmat[0][1] = -orth[1]; - totmat[0][2] = -orth[2]; - - /* we decided to keep Z */ - Crossf(zz,orth, vec); - Normalize(zz); - totmat[2][0] = zz[0]; - totmat[2][1] = zz[1]; - totmat[2][2] = zz[2]; - break; - } /* switch (data->plane) */ - - Mat4CpyMat4(tmat, ob->obmat); - - Mat4MulMat34(ob->obmat, totmat, tmat); - } - } - break; - case CONSTRAINT_TYPE_LOCLIMIT: + break; + case LOCK_Y: /* LOCK Y */ { - bLocLimitConstraint *data; - - data = constraint->data; - - /* limit location relative to origin or parent */ - if ((data->flag2 & LIMIT_NOPARENT) && ob->parent) { - /* limiting relative to parent */ - float parmat[4][4]; /* matrix of parent */ - float objLoc[3], parLoc[3]; /* location of object, and location of parent */ - float relLoc[3]; /* objLoc - parLoc*/ - - /* get matrix of parent */ - Mat4CpyMat4(parmat, ob->parent->obmat); - - /* get locations as vectors */ - objLoc[0] = ob->obmat[3][0]; - objLoc[1] = ob->obmat[3][1]; - objLoc[2] = ob->obmat[3][2]; - - parLoc[0] = parmat[3][0]; - parLoc[1] = parmat[3][1]; - parLoc[2] = parmat[3][2]; - - /* get relative location of obj from parent */ - VecSubf(relLoc, objLoc, parLoc); - - /* limiting location */ - if (data->flag & LIMIT_XMIN) { - if(relLoc[0] < data->xmin) - ob->obmat[3][0] = (parLoc[0]+data->xmin); - } - if (data->flag & LIMIT_XMAX) { - if (relLoc[0] > data->xmax) - ob->obmat[3][0] = (parLoc[0]+data->xmax); - } - if (data->flag & LIMIT_YMIN) { - if(relLoc[1] < data->ymin) - ob->obmat[3][1] = (parLoc[1]+data->ymin); - } - if (data->flag & LIMIT_YMAX) { - if (relLoc[1] > data->ymax) - ob->obmat[3][1] = (parLoc[1]+data->ymax); - } - if (data->flag & LIMIT_ZMIN) { - if(relLoc[2] < data->zmin) - ob->obmat[3][2] = (parLoc[2]+data->zmin); - } - if (data->flag & LIMIT_ZMAX) { - if (relLoc[2] > data->zmax) - ob->obmat[3][2] = (parLoc[2]+data->zmax); - } - } else { - /* limiting relative to origin */ - if (data->flag & LIMIT_XMIN) { - if(ob->obmat[3][0] < data->xmin) - ob->obmat[3][0] = data->xmin; - } - if (data->flag & LIMIT_XMAX) { - if (ob->obmat[3][0] > data->xmax) - ob->obmat[3][0] = data->xmax; + switch (data->trackflag) { + case TRACK_X: /* LOCK Y TRACK X */ + { + /* Projection of Vector on the plane */ + Projf(vec2, vec, cob->matrix[1]); + VecSubf(totmat[0], vec, vec2); + Normalize(totmat[0]); + + /* the y axis is fixed */ + totmat[1][0] = cob->matrix[1][0]; + totmat[1][1] = cob->matrix[1][1]; + totmat[1][2] = cob->matrix[1][2]; + Normalize(totmat[1]); + + /* the z axis gets mapped onto a third orthogonal vector */ + Crossf(totmat[2], totmat[0], totmat[1]); } - if (data->flag & LIMIT_YMIN) { - if(ob->obmat[3][1] < data->ymin) - ob->obmat[3][1] = data->ymin; + break; + case TRACK_Z: /* LOCK Y TRACK Z */ + { + /* Projection of Vector on the plane */ + Projf(vec2, vec, cob->matrix[1]); + VecSubf(totmat[2], vec, vec2); + Normalize(totmat[2]); + + /* the y axis is fixed */ + totmat[1][0] = cob->matrix[1][0]; + totmat[1][1] = cob->matrix[1][1]; + totmat[1][2] = cob->matrix[1][2]; + Normalize(totmat[1]); + + /* the z axis gets mapped onto a third orthogonal vector */ + Crossf(totmat[0], totmat[1], totmat[2]); } - if (data->flag & LIMIT_YMAX) { - if (ob->obmat[3][1] > data->ymax) - ob->obmat[3][1] = data->ymax; + break; + case TRACK_nX: /* LOCK Y TRACK -X */ + { + /* Projection of Vector on the plane */ + Projf(vec2, vec, cob->matrix[1]); + VecSubf(totmat[0], vec, vec2); + Normalize(totmat[0]); + VecMulf(totmat[0],-1); + + /* the y axis is fixed */ + totmat[1][0] = cob->matrix[1][0]; + totmat[1][1] = cob->matrix[1][1]; + totmat[1][2] = cob->matrix[1][2]; + Normalize(totmat[1]); + + /* the z axis gets mapped onto a third orthogonal vector */ + Crossf(totmat[2], totmat[0], totmat[1]); } - if (data->flag & LIMIT_ZMIN) { - if(ob->obmat[3][2] < data->zmin) - ob->obmat[3][2] = data->zmin; + break; + case TRACK_nZ: /* LOCK Y TRACK -Z */ + { + /* Projection of Vector on the plane */ + Projf(vec2, vec, cob->matrix[1]); + VecSubf(totmat[2], vec, vec2); + Normalize(totmat[2]); + VecMulf(totmat[2],-1); + + /* the y axis is fixed */ + totmat[1][0] = cob->matrix[1][0]; + totmat[1][1] = cob->matrix[1][1]; + totmat[1][2] = cob->matrix[1][2]; + Normalize(totmat[1]); + + /* the z axis gets mapped onto a third orthogonal vector */ + Crossf(totmat[0], totmat[1], totmat[2]); } - if (data->flag & LIMIT_ZMAX) { - if (ob->obmat[3][2] > data->zmax) - ob->obmat[3][2] = data->zmax; + break; + default: + { + totmat[0][0] = 1;totmat[0][1] = 0;totmat[0][2] = 0; + totmat[1][0] = 0;totmat[1][1] = 1;totmat[1][2] = 0; + totmat[2][0] = 0;totmat[2][1] = 0;totmat[2][2] = 1; } + break; } } - break; - case CONSTRAINT_TYPE_ROTLIMIT: + break; + case LOCK_Z: /* LOCK Z */ { - bRotLimitConstraint *data; - float loc[3]; - float eul[3]; - float size[3]; - - data = constraint->data; - - VECCOPY(loc, ob->obmat[3]); - Mat4ToSize(ob->obmat, size); - - Mat4ToEul(ob->obmat, eul); - - /* eulers: radians to degrees! */ - eul[0] = (eul[0] / M_PI * 180); - eul[1] = (eul[1] / M_PI * 180); - eul[2] = (eul[2] / M_PI * 180); - - /* limiting of euler values... */ - if (data->flag & LIMIT_XROT) { - if (eul[0] < data->xmin) - eul[0] = data->xmin; + switch (data->trackflag) { + case TRACK_X: /* LOCK Z TRACK X */ + { + /* Projection of Vector on the plane */ + Projf(vec2, vec, cob->matrix[2]); + VecSubf(totmat[0], vec, vec2); + Normalize(totmat[0]); - if (eul[0] > data->xmax) - eul[0] = data->xmax; - } - if (data->flag & LIMIT_YROT) { - if (eul[1] < data->ymin) - eul[1] = data->ymin; + /* the z axis is fixed */ + totmat[2][0] = cob->matrix[2][0]; + totmat[2][1] = cob->matrix[2][1]; + totmat[2][2] = cob->matrix[2][2]; + Normalize(totmat[2]); - if (eul[1] > data->ymax) - eul[1] = data->ymax; - } - if (data->flag & LIMIT_ZROT) { - if (eul[2] < data->zmin) - eul[2] = data->zmin; + /* the x axis gets mapped onto a third orthogonal vector */ + Crossf(totmat[1], totmat[2], totmat[0]); + } + break; + case TRACK_Y: /* LOCK Z TRACK Y */ + { + /* Projection of Vector on the plane */ + Projf(vec2, vec, cob->matrix[2]); + VecSubf(totmat[1], vec, vec2); + Normalize(totmat[1]); - if (eul[2] > data->zmax) - eul[2] = data->zmax; - } - - /* eulers: degrees to radians ! */ - eul[0] = (eul[0] / 180 * M_PI); - eul[1] = (eul[1] / 180 * M_PI); - eul[2] = (eul[2] / 180 * M_PI); - - LocEulSizeToMat4(ob->obmat, loc, eul, size); - } - break; - case CONSTRAINT_TYPE_SIZELIMIT: - { - bSizeLimitConstraint *data; - float obsize[3], size[3]; - int clearNegScale=0; - - data = constraint->data; - - Mat4ToSize(ob->obmat, size); - Mat4ToSize(ob->obmat, obsize); - - if (data->flag & LIMIT_XMIN) { - if (ob->transflag & OB_NEG_SCALE) { - size[0] *= -1; + /* the z axis is fixed */ + totmat[2][0] = cob->matrix[2][0]; + totmat[2][1] = cob->matrix[2][1]; + totmat[2][2] = cob->matrix[2][2]; + Normalize(totmat[2]); + + /* the x axis gets mapped onto a third orthogonal vector */ + Crossf(totmat[0], totmat[1], totmat[2]); + } + break; + case TRACK_nX: /* LOCK Z TRACK -X */ + { + /* Projection of Vector on the plane */ + Projf(vec2, vec, cob->matrix[2]); + VecSubf(totmat[0], vec, vec2); + Normalize(totmat[0]); + VecMulf(totmat[0],-1); - if (size[0] < data->xmin) { - size[0] = data->xmin; - clearNegScale += 1; - } - } else { - if (size[0] < data->xmin) - size[0] = data->xmin; - } - } - if (data->flag & LIMIT_XMAX) { - if (size[0] > data->xmax) - size[0] = data->xmax; - } - if (data->flag & LIMIT_YMIN) { - if (ob->transflag & OB_NEG_SCALE) { - size[1] *= -1; + /* the z axis is fixed */ + totmat[2][0] = cob->matrix[2][0]; + totmat[2][1] = cob->matrix[2][1]; + totmat[2][2] = cob->matrix[2][2]; + Normalize(totmat[2]); - if (size[1] < data->ymin) { - size[1] = data->ymin; - clearNegScale += 1; - } - } else { - if (size[1] < data->ymin) - size[1] = data->ymin; - } - } - if (data->flag & LIMIT_YMAX) { - if (size[1] > data->ymax) - size[1] = data->ymax; - } - if (data->flag & LIMIT_ZMIN) { - if (ob->transflag & OB_NEG_SCALE) { - size[2] *= -1; + /* the x axis gets mapped onto a third orthogonal vector */ + Crossf(totmat[1], totmat[2], totmat[0]); + } + break; + case TRACK_nY: /* LOCK Z TRACK -Y */ + { + /* Projection of Vector on the plane */ + Projf(vec2, vec, cob->matrix[2]); + VecSubf(totmat[1], vec, vec2); + Normalize(totmat[1]); + VecMulf(totmat[1],-1); - if (size[2] < data->zmin) { - size[2] = data->zmin; - clearNegScale += 1; - } - } else { - if (size[2] < data->zmin) - size[2] = data->zmin; - } - } - if (data->flag & LIMIT_ZMAX) { - if (size[2] > data->zmax) - size[2] = data->zmax; + /* the z axis is fixed */ + totmat[2][0] = cob->matrix[2][0]; + totmat[2][1] = cob->matrix[2][1]; + totmat[2][2] = cob->matrix[2][2]; + Normalize(totmat[2]); + + /* the x axis gets mapped onto a third orthogonal vector */ + Crossf(totmat[0], totmat[1], totmat[2]); + } + break; + default: + { + totmat[0][0] = 1;totmat[0][1] = 0;totmat[0][2] = 0; + totmat[1][0] = 0;totmat[1][1] = 1;totmat[1][2] = 0; + totmat[2][0] = 0;totmat[2][1] = 0;totmat[2][2] = 1; + } + break; } - - if (clearNegScale != 0) { - ob->transflag &= ~OB_NEG_SCALE; /* is this how we remove that flag? */ + } + break; + default: + { + totmat[0][0] = 1;totmat[0][1] = 0;totmat[0][2] = 0; + totmat[1][0] = 0;totmat[1][1] = 1;totmat[1][2] = 0; + totmat[2][0] = 0;totmat[2][1] = 0;totmat[2][2] = 1; } - - VecMulf(ob->obmat[0], size[0]/obsize[0]); - VecMulf(ob->obmat[1], size[1]/obsize[1]); - VecMulf(ob->obmat[2], size[2]/obsize[2]); + break; } - break; - case CONSTRAINT_TYPE_RIGIDBODYJOINT: - { + /* Block to keep matrix heading */ + tmpmat[0][0] = cob->matrix[0][0];tmpmat[0][1] = cob->matrix[0][1];tmpmat[0][2] = cob->matrix[0][2]; + tmpmat[1][0] = cob->matrix[1][0];tmpmat[1][1] = cob->matrix[1][1];tmpmat[1][2] = cob->matrix[1][2]; + tmpmat[2][0] = cob->matrix[2][0];tmpmat[2][1] = cob->matrix[2][1];tmpmat[2][2] = cob->matrix[2][2]; + Normalize(tmpmat[0]); + Normalize(tmpmat[1]); + Normalize(tmpmat[2]); + Mat3Inv(invmat, tmpmat); + Mat3MulMat3(tmpmat, totmat, invmat); + totmat[0][0] = tmpmat[0][0];totmat[0][1] = tmpmat[0][1];totmat[0][2] = tmpmat[0][2]; + totmat[1][0] = tmpmat[1][0];totmat[1][1] = tmpmat[1][1];totmat[1][2] = tmpmat[1][2]; + totmat[2][0] = tmpmat[2][0];totmat[2][1] = tmpmat[2][1];totmat[2][2] = tmpmat[2][2]; + + Mat4CpyMat4(tmat, cob->matrix); + + mdet = Det3x3( totmat[0][0],totmat[0][1],totmat[0][2], + totmat[1][0],totmat[1][1],totmat[1][2], + totmat[2][0],totmat[2][1],totmat[2][2]); + if (mdet==0) { + totmat[0][0] = 1;totmat[0][1] = 0;totmat[0][2] = 0; + totmat[1][0] = 0;totmat[1][1] = 1;totmat[1][2] = 0; + totmat[2][0] = 0;totmat[2][1] = 0;totmat[2][2] = 1; + } + + /* apply out transformaton to the object */ + Mat4MulMat34(cob->matrix, totmat, tmat); + } +} +static bConstraintTypeInfo CTI_LOCKTRACK = { + CONSTRAINT_TYPE_LOCKTRACK, /* type */ + sizeof(bLockTrackConstraint), /* size */ + "Locked Track", /* name */ + "bLockTrackConstraint", /* struct name */ + NULL, /* free data */ + NULL, /* relink data */ + NULL, /* copy data */ + locktrack_new_data, /* new data */ + locktrack_get_tars, /* get constraint targets */ + locktrack_flush_tars, /* flush constraint targets */ + default_get_tarmat, /* get target matrix */ + locktrack_evaluate /* evaluate */ +}; + +/* ---------- Stretch To ------------ */ + +static void stretchto_new_data (void *cdata) +{ + bStretchToConstraint *data= (bStretchToConstraint *)cdata; + + data->volmode = 0; + data->plane = 0; + data->orglength = 0.0; + data->bulge = 1.0; +} - } - break; - case CONSTRAINT_TYPE_CLAMPTO: - { - bClampToConstraint *data; - Curve *cu; - float obmat[4][4], targetMatrix[4][4], ownLoc[3]; - float curveMin[3], curveMax[3]; +static void stretchto_get_tars (bConstraint *con, ListBase *list) +{ + if (con && list) { + bStretchToConstraint *data= con->data; + bConstraintTarget *ct; + + /* standard target-getting macro for single-target constraints */ + SINGLETARGET_GET_TARS(con, data->tar, data->subtarget, ct, list) + } +} + +static void stretchto_flush_tars (bConstraint *con, ListBase *list, short nocopy) +{ + if (con && list) { + bStretchToConstraint *data= con->data; + bConstraintTarget *ct= list->first; + + /* the following macro is used for all standard single-target constraints */ + SINGLETARGET_FLUSH_TARS(con, data->tar, data->subtarget, ct, list, nocopy) + } +} + +static void stretchto_evaluate (bConstraint *con, bConstraintOb *cob, ListBase *targets) +{ + bStretchToConstraint *data= con->data; + bConstraintTarget *ct= targets->first; + + /* only evaluate if there is a target */ + if (VALID_CONS_TARGET(ct)) { + float size[3], scale[3], vec[3], xx[3], zz[3], orth[3]; + float totmat[3][3]; + float tmat[4][4]; + float dist; + + /* store scaling before destroying obmat */ + Mat4ToSize(cob->matrix, size); + + /* store X orientation before destroying obmat */ + xx[0] = cob->matrix[0][0]; + xx[1] = cob->matrix[0][1]; + xx[2] = cob->matrix[0][2]; + Normalize(xx); + + /* store Z orientation before destroying obmat */ + zz[0] = cob->matrix[2][0]; + zz[1] = cob->matrix[2][1]; + zz[2] = cob->matrix[2][2]; + Normalize(zz); + + VecSubf(vec, cob->matrix[3], ct->matrix[3]); + vec[0] /= size[0]; + vec[1] /= size[1]; + vec[2] /= size[2]; + + dist = Normalize(vec); + //dist = VecLenf( ob->obmat[3], targetmat[3]); + + /* data->orglength==0 occurs on first run, and after 'R' button is clicked */ + if (data->orglength == 0) + data->orglength = dist; + if (data->bulge == 0) + data->bulge = 1.0; + + scale[1] = dist/data->orglength; + switch (data->volmode) { + /* volume preserving scaling */ + case VOLUME_XZ : + scale[0] = 1.0f - (float)sqrt(data->bulge) + (float)sqrt(data->bulge*(data->orglength/dist)); + scale[2] = scale[0]; + break; + case VOLUME_X: + scale[0] = 1.0f + data->bulge * (data->orglength /dist - 1); + scale[2] = 1.0; + break; + case VOLUME_Z: + scale[0] = 1.0; + scale[2] = 1.0f + data->bulge * (data->orglength /dist - 1); + break; + /* don't care for volume */ + case NO_VOLUME: + scale[0] = 1.0; + scale[2] = 1.0; + break; + default: /* should not happen, but in case*/ + return; + } /* switch (data->volmode) */ + + /* Clear the object's rotation and scale */ + cob->matrix[0][0]=size[0]*scale[0]; + cob->matrix[0][1]=0; + cob->matrix[0][2]=0; + cob->matrix[1][0]=0; + cob->matrix[1][1]=size[1]*scale[1]; + cob->matrix[1][2]=0; + cob->matrix[2][0]=0; + cob->matrix[2][1]=0; + cob->matrix[2][2]=size[2]*scale[2]; + + VecSubf(vec, cob->matrix[3], ct->matrix[3]); + Normalize(vec); + + /* new Y aligns object target connection*/ + totmat[1][0] = -vec[0]; + totmat[1][1] = -vec[1]; + totmat[1][2] = -vec[2]; + switch (data->plane) { + case PLANE_X: + /* build new Z vector */ + /* othogonal to "new Y" "old X! plane */ + Crossf(orth, vec, xx); + Normalize(orth); - data = constraint->data; + /* new Z*/ + totmat[2][0] = orth[0]; + totmat[2][1] = orth[1]; + totmat[2][2] = orth[2]; - /* prevent crash if user deletes curve */ - if ((data->tar == NULL) || (data->tar->type != OB_CURVE) ) - return; - else - cu= data->tar->data; + /* we decided to keep X plane*/ + Crossf(xx, orth, vec); + Normalize(xx); + totmat[0][0] = xx[0]; + totmat[0][1] = xx[1]; + totmat[0][2] = xx[2]; + break; + case PLANE_Z: + /* build new X vector */ + /* othogonal to "new Y" "old Z! plane */ + Crossf(orth, vec, zz); + Normalize(orth); - Mat4CpyMat4(obmat, ob->obmat); - Mat4One(targetMatrix); - VECCOPY(ownLoc, obmat[3]); + /* new X */ + totmat[0][0] = -orth[0]; + totmat[0][1] = -orth[1]; + totmat[0][2] = -orth[2]; - INIT_MINMAX(curveMin, curveMax) - minmax_object(data->tar, curveMin, curveMax); + /* we decided to keep Z */ + Crossf(zz, orth, vec); + Normalize(zz); + totmat[2][0] = zz[0]; + totmat[2][1] = zz[1]; + totmat[2][2] = zz[2]; + break; + } /* switch (data->plane) */ + + Mat4CpyMat4(tmat, cob->matrix); + Mat4MulMat34(cob->matrix, totmat, tmat); + } +} + +static bConstraintTypeInfo CTI_STRETCHTO = { + CONSTRAINT_TYPE_STRETCHTO, /* type */ + sizeof(bStretchToConstraint), /* size */ + "Stretch To", /* name */ + "bStretchToConstraint", /* struct name */ + NULL, /* free data */ + NULL, /* relink data */ + NULL, /* copy data */ + stretchto_new_data, /* new data */ + stretchto_get_tars, /* get constraint targets */ + stretchto_flush_tars, /* flush constraint targets */ + default_get_tarmat, /* get target matrix */ + stretchto_evaluate /* evaluate */ +}; + +/* ---------- Floor ------------ */ + +static void minmax_new_data (void *cdata) +{ + bMinMaxConstraint *data= (bMinMaxConstraint *)cdata; + + data->minmaxflag = TRACK_Z; + data->offset = 0.0f; + data->cache[0] = data->cache[1] = data->cache[2] = 0.0f; + data->flag = 0; +} + +static void minmax_get_tars (bConstraint *con, ListBase *list) +{ + if (con && list) { + bMinMaxConstraint *data= con->data; + bConstraintTarget *ct; + + /* standard target-getting macro for single-target constraints */ + SINGLETARGET_GET_TARS(con, data->tar, data->subtarget, ct, list) + } +} + +static void minmax_flush_tars (bConstraint *con, ListBase *list, short nocopy) +{ + if (con && list) { + bMinMaxConstraint *data= con->data; + bConstraintTarget *ct= list->first; + + /* the following macro is used for all standard single-target constraints */ + SINGLETARGET_FLUSH_TARS(con, data->tar, data->subtarget, ct, list, nocopy) + } +} + +static void minmax_evaluate (bConstraint *con, bConstraintOb *cob, ListBase *targets) +{ + bMinMaxConstraint *data= con->data; + bConstraintTarget *ct= targets->first; + + /* only evaluate if there is a target */ + if (VALID_CONS_TARGET(ct)) { + float obmat[4][4], imat[4][4], tarmat[4][4], tmat[4][4]; + float val1, val2; + int index; + + Mat4CpyMat4(obmat, cob->matrix); + Mat4CpyMat4(tarmat, ct->matrix); + + if (data->flag & MINMAX_USEROT) { + /* take rotation of target into account by doing the transaction in target's localspace */ + Mat4Invert(imat, tarmat); + Mat4MulMat4(tmat, obmat, imat); + Mat4CpyMat4(obmat, tmat); + Mat4One(tarmat); + } + + switch (data->minmaxflag) { + case TRACK_Z: + val1 = tarmat[3][2]; + val2 = obmat[3][2]-data->offset; + index = 2; + break; + case TRACK_Y: + val1 = tarmat[3][1]; + val2 = obmat[3][1]-data->offset; + index = 1; + break; + case TRACK_X: + val1 = tarmat[3][0]; + val2 = obmat[3][0]-data->offset; + index = 0; + break; + case TRACK_nZ: + val2 = tarmat[3][2]; + val1 = obmat[3][2]-data->offset; + index = 2; + break; + case TRACK_nY: + val2 = tarmat[3][1]; + val1 = obmat[3][1]-data->offset; + index = 1; + break; + case TRACK_nX: + val2 = tarmat[3][0]; + val1 = obmat[3][0]-data->offset; + index = 0; + break; + default: + return; + } + + if (val1 > val2) { + obmat[3][index] = tarmat[3][index] + data->offset; + if (data->flag & MINMAX_STICKY) { + if (data->flag & MINMAX_STUCK) { + VECCOPY(obmat[3], data->cache); + } + else { + VECCOPY(data->cache, obmat[3]); + data->flag |= MINMAX_STUCK; + } + } + if (data->flag & MINMAX_USEROT) { + /* get out of localspace */ + Mat4MulMat4(tmat, obmat, ct->matrix); + Mat4CpyMat4(cob->matrix, tmat); + } + else { + VECCOPY(cob->matrix[3], obmat[3]); + } + } + else { + data->flag &= ~MINMAX_STUCK; + } + } +} + +static bConstraintTypeInfo CTI_MINMAX = { + CONSTRAINT_TYPE_MINMAX, /* type */ + sizeof(bMinMaxConstraint), /* size */ + "Floor", /* name */ + "bMinMaxConstraint", /* struct name */ + NULL, /* free data */ + NULL, /* relink data */ + NULL, /* copy data */ + minmax_new_data, /* new data */ + minmax_get_tars, /* get constraint targets */ + minmax_flush_tars, /* flush constraint targets */ + default_get_tarmat, /* get target matrix */ + minmax_evaluate /* evaluate */ +}; + +/* ------- RigidBody Joint ---------- */ + +static void rbj_new_data (void *cdata) +{ + bRigidBodyJointConstraint *data= (bRigidBodyJointConstraint *)cdata; + + // removed code which set target of this constraint + data->type=1; +} + +static void rbj_get_tars (bConstraint *con, ListBase *list) +{ + if (con && list) { + bRigidBodyJointConstraint *data= con->data; + bConstraintTarget *ct; + + /* standard target-getting macro for single-target constraints without subtargets */ + SINGLETARGETNS_GET_TARS(con, data->tar, ct, list) + } +} + +static void rbj_flush_tars (bConstraint *con, ListBase *list, short nocopy) +{ + if (con && list) { + bRigidBodyJointConstraint *data= con->data; + bConstraintTarget *ct= list->first; + + /* the following macro is used for all standard single-target constraints */ + SINGLETARGETNS_FLUSH_TARS(con, data->tar, ct, list, nocopy) + } +} + +static bConstraintTypeInfo CTI_RIGIDBODYJOINT = { + CONSTRAINT_TYPE_RIGIDBODYJOINT, /* type */ + sizeof(bRigidBodyJointConstraint), /* size */ + "RigidBody Joint", /* name */ + "bRigidBodyJointConstraint", /* struct name */ + NULL, /* free data */ + NULL, /* relink data */ + NULL, /* copy data */ + rbj_new_data, /* new data */ + rbj_get_tars, /* get constraint targets */ + rbj_flush_tars, /* flush constraint targets */ + default_get_tarmat, /* get target matrix */ + NULL /* evaluate - this is not solved here... is just an interface for game-engine */ +}; + +/* -------- Clamp To ---------- */ + +static void clampto_get_tars (bConstraint *con, ListBase *list) +{ + if (con && list) { + bClampToConstraint *data= con->data; + bConstraintTarget *ct; + + /* standard target-getting macro for single-target constraints without subtargets */ + SINGLETARGETNS_GET_TARS(con, data->tar, ct, list) + } +} + +static void clampto_flush_tars (bConstraint *con, ListBase *list, short nocopy) +{ + if (con && list) { + bClampToConstraint *data= con->data; + bConstraintTarget *ct= list->first; + + /* the following macro is used for all standard single-target constraints */ + SINGLETARGETNS_FLUSH_TARS(con, data->tar, ct, list, nocopy) + } +} + +static void clampto_get_tarmat (bConstraint *con, bConstraintOb *cob, bConstraintTarget *ct, float ctime) +{ + if (VALID_CONS_TARGET(ct)) { + Curve *cu= ct->tar->data; + + /* note: when creating constraints that follow path, the curve gets the CU_PATH set now, + * currently for paths to work it needs to go through the bevlist/displist system (ton) + */ + + /* only happens on reload file, but violates depsgraph still... fix! */ + if (cu->path==NULL || cu->path->data==NULL) + makeDispListCurveTypes(ct->tar, 0); + } + + /* technically, this isn't really needed for evaluation, but we don't know what else + * might end up calling this... + */ + if (ct) + Mat4One(ct->matrix); +} + +static void clampto_evaluate (bConstraint *con, bConstraintOb *cob, ListBase *targets) +{ + bClampToConstraint *data= con->data; + bConstraintTarget *ct= targets->first; + + /* only evaluate if there is a target and it is a curve */ + if (VALID_CONS_TARGET(ct) && (ct->tar->type == OB_CURVE)) { + Curve *cu= data->tar->data; + float obmat[4][4], targetMatrix[4][4], ownLoc[3]; + float curveMin[3], curveMax[3]; + + Mat4CpyMat4(obmat, cob->matrix); + Mat4One(targetMatrix); + VECCOPY(ownLoc, obmat[3]); + + INIT_MINMAX(curveMin, curveMax) + minmax_object(ct->tar, curveMin, curveMax); + + /* get targetmatrix */ + if (cu->path && cu->path->data) { + float vec[4], dir[3], totmat[4][4]; + float curvetime; + short clamp_axis; - /* get targetmatrix */ - if(cu->path && cu->path->data) { - float vec[4], dir[3], totmat[4][4]; - float curvetime; - short clamp_axis; + /* find best position on curve */ + /* 1. determine which axis to sample on? */ + if (data->flag == CLAMPTO_AUTO) { + float size[3]; + VecSubf(size, curveMax, curveMin); + + /* find axis along which the bounding box has the greatest + * extent. Otherwise, default to the x-axis, as that is quite + * frequently used. + */ + if ((size[2]>size[0]) && (size[2]>size[1])) + clamp_axis= CLAMPTO_Z - 1; + else if ((size[1]>size[0]) && (size[1]>size[2])) + clamp_axis= CLAMPTO_Y - 1; + else + clamp_axis = CLAMPTO_X - 1; + } + else + clamp_axis= data->flag - 1; + + /* 2. determine position relative to curve on a 0-1 scale based on bounding box */ + if (data->flag2 & CLAMPTO_CYCLIC) { + /* cyclic, so offset within relative bounding box is used */ + float len= (curveMax[clamp_axis] - curveMin[clamp_axis]); + float offset; - /* find best position on curve */ - /* 1. determine which axis to sample on? */ - if (data->flag==CLAMPTO_AUTO) { - float size[3]; - VecSubf(size, curveMax, curveMin); + /* find bounding-box range where target is located */ + if (ownLoc[clamp_axis] < curveMin[clamp_axis]) { + /* bounding-box range is before */ + offset= curveMin[clamp_axis]; + + while (ownLoc[clamp_axis] < offset) + offset -= len; - /* find axis along which the bounding box has the greatest - * extent. Otherwise, default to the x-axis, as that is quite - * frequently used. - */ - if ((size[2]>size[0]) && (size[2]>size[1])) - clamp_axis= CLAMPTO_Z; - else if ((size[1]>size[0]) && (size[1]>size[2])) - clamp_axis= CLAMPTO_Y; - else - clamp_axis = CLAMPTO_X; + /* now, we calculate as per normal, except using offset instead of curveMin[clamp_axis] */ + curvetime = (ownLoc[clamp_axis] - offset) / (len); } - else - clamp_axis= data->flag; + else if (ownLoc[clamp_axis] > curveMax[clamp_axis]) { + /* bounding-box range is after */ + offset= curveMax[clamp_axis]; + + while (ownLoc[clamp_axis] > offset) { + if ((offset + len) > ownLoc[clamp_axis]) + break; + else + offset += len; + } - /* 2. determine position relative to curve on a 0-1 scale */ - if (clamp_axis > 0) clamp_axis--; + /* now, we calculate as per normal, except using offset instead of curveMax[clamp_axis] */ + curvetime = (ownLoc[clamp_axis] - offset) / (len); + } + else { + /* as the location falls within bounds, just calculate */ + curvetime = (ownLoc[clamp_axis] - curveMin[clamp_axis]) / (len); + } + } + else { + /* no cyclic, so position is clamped to within the bounding box */ if (ownLoc[clamp_axis] <= curveMin[clamp_axis]) curvetime = 0.0; else if (ownLoc[clamp_axis] >= curveMax[clamp_axis]) curvetime = 1.0; else - curvetime = (ownLoc[clamp_axis] - curveMin[clamp_axis]) / (curveMax[clamp_axis] - curveMin[clamp_axis]); // umm + curvetime = (ownLoc[clamp_axis] - curveMin[clamp_axis]) / (curveMax[clamp_axis] - curveMin[clamp_axis]); + } + + /* 3. position on curve */ + if (where_on_path(ct->tar, curvetime, vec, dir) ) { + Mat4One(totmat); + VECCOPY(totmat[3], vec); - /* 3. position on curve */ - if(where_on_path(data->tar, curvetime, vec, dir) ) { - Mat4One(totmat); - VECCOPY(totmat[3], vec); + Mat4MulSerie(targetMatrix, ct->tar->obmat, totmat, NULL, NULL, NULL, NULL, NULL, NULL); + } + } + + /* obtain final object position */ + VECCOPY(cob->matrix[3], targetMatrix[3]); + } +} + +static bConstraintTypeInfo CTI_CLAMPTO = { + CONSTRAINT_TYPE_CLAMPTO, /* type */ + sizeof(bClampToConstraint), /* size */ + "Clamp To", /* name */ + "bClampToConstraint", /* struct name */ + NULL, /* free data */ + NULL, /* relink data */ + NULL, /* copy data */ + NULL, /* new data */ + clampto_get_tars, /* get constraint targets */ + clampto_flush_tars, /* flush constraint targets */ + clampto_get_tarmat, /* get target matrix */ + clampto_evaluate /* evaluate */ +}; + +/* ---------- Transform Constraint ----------- */ + +static void transform_new_data (void *cdata) +{ + bTransformConstraint *data= (bTransformConstraint *)cdata; + + data->map[0]= 0; + data->map[1]= 1; + data->map[2]= 2; +} + +static void transform_get_tars (bConstraint *con, ListBase *list) +{ + if (con && list) { + bTransformConstraint *data= con->data; + bConstraintTarget *ct; + + /* standard target-getting macro for single-target constraints */ + SINGLETARGET_GET_TARS(con, data->tar, data->subtarget, ct, list) + } +} + +static void transform_flush_tars (bConstraint *con, ListBase *list, short nocopy) +{ + if (con && list) { + bTransformConstraint *data= con->data; + bConstraintTarget *ct= list->first; + + /* the following macro is used for all standard single-target constraints */ + SINGLETARGET_FLUSH_TARS(con, data->tar, data->subtarget, ct, list, nocopy) + } +} + +static void transform_evaluate (bConstraint *con, bConstraintOb *cob, ListBase *targets) +{ + bTransformConstraint *data= con->data; + bConstraintTarget *ct= targets->first; + + /* only evaluate if there is a target */ + if (VALID_CONS_TARGET(ct)) { + float loc[3], eul[3], size[3]; + float dvec[3], sval[3]; + short i; + + /* obtain target effect */ + switch (data->from) { + case 2: /* scale */ + Mat4ToSize(ct->matrix, dvec); + break; + case 1: /* rotation */ + Mat4ToEul(ct->matrix, dvec); + break; + default: /* location */ + VecCopyf(dvec, ct->matrix[3]); + break; + } + + /* extract components of owner's matrix */ + VECCOPY(loc, cob->matrix[3]); + Mat4ToEul(cob->matrix, eul); + Mat4ToSize(cob->matrix, size); + + /* determine where in range current transforms lie */ + if (data->expo) { + for (i=0; i<3; i++) { + if (data->from_max[i] - data->from_min[i]) + sval[i]= (dvec[i] - data->from_min[i]) / (data->from_max[i] - data->from_min[i]); + else + sval[i]= 0.0f; + } + } + else { + /* clamp transforms out of range */ + for (i=0; i<3; i++) { + CLAMP(dvec[i], data->from_min[i], data->from_max[i]); + if (data->from_max[i] - data->from_min[i]) + sval[i]= (dvec[i] - data->from_min[i]) / (data->from_max[i] - data->from_min[i]); + else + sval[i]= 0.0f; + } + } + + /* convert radian<->degree */ + if (data->from==1 && data->to==0) { + /* from radians to degrees */ + for (i=0; i<3; i++) + sval[i] = sval[i] / M_PI * 180; + } + else if (data->from==0 && data->to==1) { + /* from degrees to radians */ + for (i=0; i<3; i++) + sval[i] = sval[i] / 180 * M_PI; + } + + /* apply transforms */ + switch (data->to) { + case 2: /* scaling */ + for (i=0; i<3; i++) + size[i]= data->to_min[i] + (sval[data->map[i]] * (data->to_max[i] - data->to_min[i])); + break; + case 1: /* rotation */ + for (i=0; i<3; i++) { + float tmin, tmax; + + /* convert destination min/max ranges from degrees to radians */ + tmin= data->to_min[i] / M_PI * 180; + tmax= data->to_max[i] / M_PI * 180; - Mat4MulSerie(targetMatrix, data->tar->obmat, totmat, NULL, NULL, NULL, NULL, NULL, NULL); + eul[i]= tmin + (sval[data->map[i]] * (tmax - tmin)); + } + break; + default: /* location */ + /* get new location */ + for (i=0; i<3; i++) + loc[i]= (data->to_min[i] + (sval[data->map[i]] * (data->to_max[i] - data->to_min[i]))); + + /* add original location back on (so that it can still be moved) */ + VecAddf(loc, cob->matrix[3], loc); + break; + } + + /* apply to matrix */ + LocEulSizeToMat4(cob->matrix, loc, eul, size); + } +} + +static bConstraintTypeInfo CTI_TRANSFORM = { + CONSTRAINT_TYPE_TRANSFORM, /* type */ + sizeof(bTransformConstraint), /* size */ + "Transform", /* name */ + "bTransformConstraint", /* struct name */ + NULL, /* free data */ + NULL, /* relink data */ + NULL, /* copy data */ + transform_new_data, /* new data */ + transform_get_tars, /* get constraint targets */ + transform_flush_tars, /* flush constraint targets */ + default_get_tarmat, /* get a target matrix */ + transform_evaluate /* evaluate */ +}; + +/* ************************* Constraints Type-Info *************************** */ +/* All of the constraints api functions use bConstraintTypeInfo structs to carry out + * and operations that involve constraint specifc code. + */ + +/* These globals only ever get directly accessed in this file */ +static bConstraintTypeInfo *constraintsTypeInfo[NUM_CONSTRAINT_TYPES+1]; +static short CTI_INIT= 1; /* when non-zero, the list needs to be updated */ + +/* This function only gets called when CTI_INIT is non-zero */ +static void constraints_init_typeinfo () { + constraintsTypeInfo[0]= NULL; /* 'Null' Constraint */ + constraintsTypeInfo[1]= &CTI_CHILDOF; /* ChildOf Constraint */ + constraintsTypeInfo[2]= &CTI_TRACKTO; /* TrackTo Constraint */ + constraintsTypeInfo[3]= &CTI_KINEMATIC; /* IK Constraint */ + constraintsTypeInfo[4]= &CTI_FOLLOWPATH; /* Follow-Path Constraint */ + constraintsTypeInfo[5]= &CTI_ROTLIMIT; /* Limit Rotation Constraint */ + constraintsTypeInfo[6]= &CTI_LOCLIMIT; /* Limit Location Constraint */ + constraintsTypeInfo[7]= &CTI_SIZELIMIT; /* Limit Scaling Constraint */ + constraintsTypeInfo[8]= &CTI_ROTLIKE; /* Copy Rotation Constraint */ + constraintsTypeInfo[9]= &CTI_LOCLIKE; /* Copy Location Constraint */ + constraintsTypeInfo[10]= &CTI_SIZELIKE; /* Copy Scaling Constraint */ + constraintsTypeInfo[11]= &CTI_PYTHON; /* Python/Script Constraint */ + constraintsTypeInfo[12]= &CTI_ACTION; /* Action Constraint */ + constraintsTypeInfo[13]= &CTI_LOCKTRACK; /* Locked-Track Constraint */ + constraintsTypeInfo[14]= NULL; /* 'Distance Limit' Constraint */ + constraintsTypeInfo[15]= &CTI_STRETCHTO; /* StretchTo Constaint */ + constraintsTypeInfo[16]= &CTI_MINMAX; /* Floor Constraint */ + constraintsTypeInfo[17]= &CTI_RIGIDBODYJOINT; /* RigidBody Constraint */ + constraintsTypeInfo[18]= &CTI_CLAMPTO; /* ClampTo Constraint */ + constraintsTypeInfo[19]= &CTI_TRANSFORM; /* Transformation Constraint */ +} + +/* This function should be used for getting the appropriate type-info when only + * a constraint type is known + */ +bConstraintTypeInfo *get_constraint_typeinfo (int type) +{ + /* initialise the type-info list? */ + if (CTI_INIT) { + constraints_init_typeinfo(); + CTI_INIT = 0; + } + + /* only return for valid types */ + if ( (type >= CONSTRAINT_TYPE_NULL) && + (type <= NUM_CONSTRAINT_TYPES ) ) + { + /* there shouldn't be any segfaults here... */ + return constraintsTypeInfo[type]; + } + else { + printf("No valid constraint 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. + */ +bConstraintTypeInfo *constraint_get_typeinfo (bConstraint *con) +{ + /* only return typeinfo for valid constraints */ + if (con) + return get_constraint_typeinfo(con->type); + else + return NULL; +} + +/* ************************* General Constraints API ************************** */ +/* The functions here are called by various parts of Blender. Very few (should be none if possible) + * constraint-specific code should occur here. + */ + +/* ---------- Data Management ------- */ + +/* Free data of a specific constraint if it has any info */ +void free_constraint_data (bConstraint *con) +{ + if (con->data) { + bConstraintTypeInfo *cti= constraint_get_typeinfo(con); + + /* perform any special freeing constraint may have */ + if (cti && cti->free_data) + cti->free_data(con); + + /* free constraint data now */ + MEM_freeN(con->data); + } +} + +/* Free all constraints from a constraint-stack */ +void free_constraints (ListBase *conlist) +{ + bConstraint *con; + + /* Free constraint data and also any extra data */ + for (con= conlist->first; con; con= con->next) { + free_constraint_data(con); + } + + /* Free the whole list */ + BLI_freelistN(conlist); +} + +/* Reassign links that constraints have to other data (called during file loading?) */ +void relink_constraints (ListBase *conlist) +{ + bConstraint *con; + bConstraintTarget *ct; + + for (con= conlist->first; con; con= con->next) { + bConstraintTypeInfo *cti= constraint_get_typeinfo(con); + + if (cti) { + /* relink any targets */ + if (cti->get_constraint_targets) { + ListBase targets = {NULL, NULL}; + + cti->get_constraint_targets(con, &targets); + for (ct= targets.first; ct; ct= ct->next) { + ID_NEW(ct->tar); + } + + if (cti->flush_constraint_targets) + cti->flush_constraint_targets(con, &targets, 0); + } + + /* relink any other special data */ + if (cti->relink_data) + cti->relink_data(con); + } + } +} + +/* duplicate all of the constraints in a constraint stack */ +void copy_constraints (ListBase *dst, ListBase *src) +{ + bConstraint *con, *srccon; + + dst->first= dst->last= NULL; + duplicatelist(dst, src); + + for (con=dst->first, srccon=src->first; con; srccon=srccon->next, con=con->next) { + bConstraintTypeInfo *cti= constraint_get_typeinfo(con); + + /* make a new copy of the constraint's data */ + con->data = MEM_dupallocN(con->data); + + /* only do specific constraints if required */ + if (cti && cti->copy_data) + cti->copy_data(con, srccon); + } +} + +/* -------- Target-Matrix Stuff ------- */ + +/* This function is a relic from the prior implementations of the constraints system, when all + * constraints either had one or no targets. It used to be called during the main constraint solving + * loop, but is now only used for the remaining cases for a few constraints. + * + * None of the actual calculations of the matricies should be done here! Also, this function is + * not to be used by any new constraints, particularly any that have multiple targets. + */ +void get_constraint_target_matrix (bConstraint *con, int n, short ownertype, void *ownerdata, float mat[][4], float ctime) +{ + bConstraintTypeInfo *cti= constraint_get_typeinfo(con); + ListBase targets = {NULL, NULL}; + bConstraintOb *cob; + bConstraintTarget *ct; + + if (cti && cti->get_constraint_targets) { + /* make 'constraint-ob' */ + cob= MEM_callocN(sizeof(bConstraintOb), "tempConstraintOb"); + cob->type= ownertype; + switch (ownertype) { + case CONSTRAINT_OBTYPE_OBJECT: /* it is usually this case */ + { + cob->ob= (Object *)ownerdata; + cob->pchan= NULL; + if (cob->ob) { + Mat4CpyMat4(cob->matrix, cob->ob->obmat); + Mat4CpyMat4(cob->startmat, cob->matrix); + } + else { + Mat4One(cob->matrix); + Mat4One(cob->startmat); + } + } + break; + case CONSTRAINT_OBTYPE_BONE: /* this may occur in some cases */ + { + cob->ob= NULL; /* this might not work at all :/ */ + cob->pchan= (bPoseChannel *)ownerdata; + if (cob->pchan) { + Mat4CpyMat4(cob->matrix, cob->pchan->pose_mat); + Mat4CpyMat4(cob->startmat, cob->matrix); + } + else { + Mat4One(cob->matrix); + Mat4One(cob->startmat); } } + break; + } + + /* get targets - we only need the first one though (and there should only be one) */ + cti->get_constraint_targets(con, &targets); + + /* only calculate the target matrix on the first target */ + ct= (bConstraintTarget *)targets.first; + while(ct && n-- > 0) + ct= ct->next; + + if (ct) { + if (cti->get_target_matrix) + cti->get_target_matrix(con, cob, ct, ctime); + Mat4CpyMat4(mat, ct->matrix); + } + + /* free targets + 'constraint-ob' */ + if (cti->flush_constraint_targets) + cti->flush_constraint_targets(con, &targets, 1); + MEM_freeN(cob); + } + else { + /* invalid constraint - perhaps... */ + Mat4One(mat); + } +} + +/* ---------- Evaluation ----------- */ + +/* This function is called whenever constraints need to be evaluated. Currently, all + * constraints that can be evaluated are everytime this gets run. + * + * constraints_make_evalob and constraints_clear_evalob should be called before and + * after running this function, to sort out cob + */ +void solve_constraints (ListBase *conlist, bConstraintOb *cob, float ctime) +{ + bConstraint *con; + float solution[4][4], delta[4][4]; + float oldmat[4][4], imat[4][4]; + float enf; + + /* check that there is a valid constraint object to evaluate */ + if (cob == NULL) + return; + + /* loop over available constraints, solving and blending them */ + for (con= conlist->first; con; con= con->next) { + bConstraintTypeInfo *cti= constraint_get_typeinfo(con); + ListBase targets = {NULL, NULL}; + + /* these we can skip completely (invalid constraints...) */ + if (cti == NULL) continue; + if (con->flag & CONSTRAINT_DISABLE) continue; + /* these constraints can't be evaluated anyway */ + if (cti->evaluate_constraint == NULL) continue; + /* influence == 0 should be ignored */ + if (con->enforce == 0.0f) continue; + + /* influence of constraint + * - value should have been set from IPO's/Constraint Channels already + */ + enf = con->enforce; + + /* move owner matrix into right space */ + constraint_mat_convertspace(cob->ob, cob->pchan, cob->matrix, CONSTRAINT_SPACE_WORLD, con->ownspace); + Mat4CpyMat4(oldmat, cob->matrix); + + /* prepare targets for constraint solving */ + if (cti->get_constraint_targets) { + bConstraintTarget *ct; + + /* get targets + * - constraints should use ct->matrix, not directly accessing values + * - ct->matrix members have not yet been calculated here! + */ + cti->get_constraint_targets(con, &targets); - /* obtain final object position */ - VECCOPY(ob->obmat[3], targetMatrix[3]); + /* set matrices + * - calculate if possible, otherwise just initialise as identity matrix + */ + if (cti->get_target_matrix) { + for (ct= targets.first; ct; ct= ct->next) + cti->get_target_matrix(con, cob, ct, ctime); + } + else { + for (ct= targets.first; ct; ct= ct->next) + Mat4One(ct->matrix); + } + } + + /* Solve the constraint */ + cti->evaluate_constraint(con, cob, &targets); + + /* clear targets after use + * - this should free temp targets but no data should be copied back + * as constraints may have done some nasty things to it... + */ + if (cti->flush_constraint_targets) { + cti->flush_constraint_targets(con, &targets, 1); } - break; - default: - printf ("Error: Unknown constraint type\n"); - break; + + /* Interpolate the enforcement, to blend result of constraint into final owner transform */ + /* 1. Remove effects of original matrix from constraint solution ==> delta */ + Mat4Invert(imat, oldmat); + Mat4CpyMat4(solution, cob->matrix); + Mat4MulMat4(delta, solution, imat); + + /* 2. If constraint influence is not full strength, then interpolate + * identity_matrix --> delta_matrix to get the effect the constraint actually exerts + */ + if (enf < 1.0) { + float identity[4][4]; + Mat4One(identity); + Mat4BlendMat4(delta, identity, delta, enf); + } + + /* 3. Now multiply the delta by the matrix in use before the evaluation */ + Mat4MulMat4(cob->matrix, delta, oldmat); + + /* move owner back into worldspace for next constraint/other business */ + if ((con->flag & CONSTRAINT_SPACEONCE) == 0) + constraint_mat_convertspace(cob->ob, cob->pchan, cob->matrix, con->ownspace, CONSTRAINT_SPACE_WORLD); } } diff --git a/source/blender/blenkernel/intern/curve.c b/source/blender/blenkernel/intern/curve.c index 769c31ad8a9..6676bf0e03f 100644 --- a/source/blender/blenkernel/intern/curve.c +++ b/source/blender/blenkernel/intern/curve.c @@ -1445,7 +1445,7 @@ static void alfa_bezpart(BezTriple *prevbezt, BezTriple *bezt, Nurb *nu, float * for(a=0; a<resolu; a++, fac+= dfac) { - set_four_ipo(fac, t, KEY_LINEAR); + set_four_ipo(fac, t, nu->tilt_interp); data_a[a]= t[0]*pprev->alfa + t[1]*prevbezt->alfa + t[2]*bezt->alfa + t[3]*next->alfa; } @@ -2351,6 +2351,8 @@ void sethandlesNurb(short code) /* code==2: set vectorhandle */ /* code==3 (HD_ALIGN) it toggle, vectorhandles become HD_FREE */ /* code==4: sets icu flag to become IPO_AUTO_HORIZ, horizontal extremes on auto-handles */ + /* code==5: Set align, like 3 but no toggle */ + /* code==6: Clear align, like 3 but no toggle */ Nurb *nu; BezTriple *bezt; short a, ok=0; @@ -2381,22 +2383,28 @@ void sethandlesNurb(short code) /* there is 1 handle not FREE: FREE it all, else make ALIGNED */ nu= editNurb.first; - while(nu) { - if( (nu->type & 7)==1) { - bezt= nu->bezt; - a= nu->pntsu; - while(a--) { - if(bezt->f1 && bezt->h1) ok= 1; - if(bezt->f3 && bezt->h2) ok= 1; - if(ok) break; - bezt++; + if (code == 5) { + ok = HD_ALIGN; + } else if (code == 6) { + ok = HD_FREE; + } else { + /* Toggle */ + while(nu) { + if( (nu->type & 7)==1) { + bezt= nu->bezt; + a= nu->pntsu; + while(a--) { + if(bezt->f1 && bezt->h1) ok= 1; + if(bezt->f3 && bezt->h2) ok= 1; + if(ok) break; + bezt++; + } } + nu= nu->next; } - nu= nu->next; + if(ok) ok= HD_FREE; + else ok= HD_ALIGN; } - if(ok) ok= HD_FREE; - else ok= HD_ALIGN; - nu= editNurb.first; while(nu) { if( (nu->type & 7)==1) { diff --git a/source/blender/blenkernel/intern/customdata.c b/source/blender/blenkernel/intern/customdata.c index 2c8199f90f3..aa0dc10f4e0 100644 --- a/source/blender/blenkernel/intern/customdata.c +++ b/source/blender/blenkernel/intern/customdata.c @@ -271,7 +271,7 @@ static void layerSwap_tface(void *data, int *corner_indices) static void layerDefault_tface(void *data, int count) { - static MTFace default_tf = {{{0, 1}, {0, 0}, {1, 0}, {1, 1}}, NULL, + static MTFace default_tf = {{{0, 0}, {1, 0}, {1, 1}, {0, 1}}, NULL, 0, 0, TF_DYNAMIC, 0, 0}; MTFace *tf = (MTFace*)data; int i; @@ -280,6 +280,83 @@ static void layerDefault_tface(void *data, int count) tf[i] = default_tf; } +static void layerCopy_origspace_face(const void *source, void *dest, int count) +{ + const OrigSpaceFace *source_tf = (const OrigSpaceFace*)source; + OrigSpaceFace *dest_tf = (OrigSpaceFace*)dest; + int i; + + for(i = 0; i < count; ++i) + dest_tf[i] = source_tf[i]; +} + +static void layerInterp_origspace_face(void **sources, float *weights, + float *sub_weights, int count, void *dest) +{ + OrigSpaceFace *osf = dest; + int i, j, k; + float uv[4][2]; + float *sub_weight; + + if(count <= 0) return; + + memset(uv, 0, sizeof(uv)); + + sub_weight = sub_weights; + for(i = 0; i < count; ++i) { + float weight = weights ? weights[i] : 1; + OrigSpaceFace *src = sources[i]; + + for(j = 0; j < 4; ++j) { + if(sub_weights) { + for(k = 0; k < 4; ++k, ++sub_weight) { + float w = (*sub_weight) * weight; + float *tmp_uv = src->uv[k]; + + uv[j][0] += tmp_uv[0] * w; + uv[j][1] += tmp_uv[1] * w; + } + } else { + uv[j][0] += src->uv[j][0] * weight; + uv[j][1] += src->uv[j][1] * weight; + } + } + } + + *osf = *(OrigSpaceFace *)sources[0]; + for(j = 0; j < 4; ++j) { + osf->uv[j][0] = uv[j][0]; + osf->uv[j][1] = uv[j][1]; + } +} + +static void layerSwap_origspace_face(void *data, int *corner_indices) +{ + OrigSpaceFace *osf = data; + float uv[4][2]; + int j; + + for(j = 0; j < 4; ++j) { + uv[j][0] = osf->uv[corner_indices[j]][0]; + uv[j][1] = osf->uv[corner_indices[j]][1]; + } + memcpy(osf->uv, uv, sizeof(osf->uv)); +} + +static void layerDefault_origspace_face(void *data, int count) +{ + static OrigSpaceFace default_osf = {{{0, 0}, {1, 0}, {1, 1}, {0, 1}}}; + OrigSpaceFace *osf = (OrigSpaceFace*)data; + int i; + + for(i = 0; i < count; i++) + osf[i] = default_osf; +} +/* --------- */ + + + + static void layerInterp_mcol(void **sources, float *weights, float *sub_weights, int count, void *dest) { @@ -370,11 +447,13 @@ const LayerTypeInfo LAYERTYPEINFO[CD_NUMTYPES] = { {sizeof(MFloatProperty), "MFloatProperty",1,"Float",NULL,NULL,NULL,NULL}, {sizeof(MIntProperty), "MIntProperty",1,"Int",NULL,NULL,NULL,NULL}, {sizeof(MStringProperty), "MStringProperty",1,"String",NULL,NULL,NULL,NULL}, + {sizeof(OrigSpaceFace), "OrigSpaceFace", 1, "UVTex", layerCopy_origspace_face, NULL, + layerInterp_origspace_face, layerSwap_origspace_face, layerDefault_origspace_face}, }; const char *LAYERTYPENAMES[CD_NUMTYPES] = { "CDMVert", "CDMSticky", "CDMDeformVert", "CDMEdge", "CDMFace", "CDMTFace", - "CDMCol", "CDOrigIndex", "CDNormal", "CDFlags","CDMFloatProperty","CDMIntProperty","CDMStringProperty"}; + "CDMCol", "CDOrigIndex", "CDNormal", "CDFlags","CDMFloatProperty","CDMIntProperty","CDMStringProperty", "CDOrigSpace"}; const CustomDataMask CD_MASK_BAREMESH = CD_MASK_MVERT | CD_MASK_MEDGE | CD_MASK_MFACE; @@ -387,7 +466,8 @@ const CustomDataMask CD_MASK_EDITMESH = CD_MASK_MCOL|CD_MASK_PROP_FLT | CD_MASK_PROP_INT | CD_MASK_PROP_STR; const CustomDataMask CD_MASK_DERIVEDMESH = CD_MASK_MSTICKY | CD_MASK_MDEFORMVERT | CD_MASK_MTFACE | - CD_MASK_MCOL | CD_MASK_ORIGINDEX; + CD_MASK_MCOL | CD_MASK_ORIGINDEX | + CD_MASK_PROP_FLT | CD_MASK_PROP_INT | CD_MASK_PROP_STR | CD_MASK_ORIGSPACE; static const LayerTypeInfo *layerType_getInfo(int type) { @@ -544,6 +624,29 @@ int CustomData_get_render_layer_index(const CustomData *data, int type) return -1; } +int CustomData_get_active_layer(const CustomData *data, int type) +{ + int i; + + for(i=0; i < data->totlayer; ++i) + if(data->layers[i].type == type) + return data->layers[i].active; + + return -1; +} + +int CustomData_get_render_layer(const CustomData *data, int type) +{ + int i; + + for(i=0; i < data->totlayer; ++i) + if(data->layers[i].type == type) + return data->layers[i].active_rnd; + + return -1; +} + + void CustomData_set_layer_active(CustomData *data, int type, int n) { int i; @@ -562,6 +665,25 @@ void CustomData_set_layer_render(CustomData *data, int type, int n) data->layers[i].active_rnd = n; } +/* for using with an index from CustomData_get_active_layer_index and CustomData_get_render_layer_index */ +void CustomData_set_layer_active_index(CustomData *data, int type, int n) +{ + int i; + + for(i=0; i < data->totlayer; ++i) + if(data->layers[i].type == type) + data->layers[i].active = n-i; +} + +void CustomData_set_layer_render_index(CustomData *data, int type, int n) +{ + int i; + + for(i=0; i < data->totlayer; ++i) + if(data->layers[i].type == type) + data->layers[i].active_rnd = n-i; +} + void CustomData_set_layer_flag(struct CustomData *data, int type, int flag) { diff --git a/source/blender/blenkernel/intern/deform.c b/source/blender/blenkernel/intern/deform.c index aa6220e88bc..9356ba14071 100644 --- a/source/blender/blenkernel/intern/deform.c +++ b/source/blender/blenkernel/intern/deform.c @@ -113,14 +113,31 @@ bDeformGroup *get_named_vertexgroup (Object *ob, char *name) return NULL; } -int get_defgroup_num (Object *ob, bDeformGroup *dg) +int get_named_vertexgroup_num (Object *ob, char *name) +{ + /* Return the location of the named deform group within the list of + * deform groups. This function is a combination of get_defgroup_num and + * get_named_vertexgroup. The other two could be called instead, but that + * require looping over the vertexgroups twice. + */ + bDeformGroup *curdef; + int def_nr; + + for (curdef=ob->defbase.first, def_nr=0; curdef; curdef=curdef->next, def_nr++) { + if (!strcmp(curdef->name, name)) + return def_nr; + } + + return -1; +} + +int get_defgroup_num (Object *ob, bDeformGroup *dg) { /* Fetch the location of this deform group * within the linked list of deform groups. * (this number is stored in the deform * weights of the deform verts to link them - * to this deform group) deform deform - * deform blah blah deform + * to this deform group). */ bDeformGroup *eg; @@ -129,8 +146,7 @@ int get_defgroup_num (Object *ob, bDeformGroup *dg) eg = ob->defbase.first; def_nr = 0; - /* loop through all deform groups - */ + /* loop through all deform groups */ while (eg != NULL) { /* if the current deform group is diff --git a/source/blender/blenkernel/intern/depsgraph.c b/source/blender/blenkernel/intern/depsgraph.c index a71ed731b07..924f544285e 100644 --- a/source/blender/blenkernel/intern/depsgraph.c +++ b/source/blender/blenkernel/intern/depsgraph.c @@ -44,6 +44,7 @@ #include "DNA_action_types.h" #include "DNA_armature_types.h" #include "DNA_curve_types.h" +#include "DNA_camera_types.h" #include "DNA_ID.h" #include "DNA_effect_types.h" #include "DNA_group_types.h" @@ -56,6 +57,7 @@ #include "DNA_object_force.h" #include "DNA_object_fluidsim.h" #include "DNA_oops_types.h" +#include "DNA_particle_types.h" #include "DNA_scene_types.h" #include "DNA_screen_types.h" #include "DNA_space_types.h" @@ -71,8 +73,9 @@ #include "BKE_mball.h" #include "BKE_modifier.h" #include "BKE_object.h" -#include "BKE_scene.h" +#include "BKE_particle.h" #include "BKE_utildefines.h" +#include "BKE_scene.h" #include "MEM_guardedalloc.h" #include "blendef.h" @@ -347,10 +350,12 @@ static void dag_add_driver_relation(Ipo *ipo, DagForest *dag, DagNode *node, int static void build_dag_object(DagForest *dag, DagNode *scenenode, Object *ob, int mask) { bConstraint *con; + bConstraintChannel *conchan; DagNode * node; DagNode * node2; DagNode * node3; Key *key; + ParticleSystem *psys; int addtoroot= 1; node = dag_get_node(dag, ob); @@ -366,25 +371,34 @@ static void build_dag_object(DagForest *dag, DagNode *scenenode, Object *ob, int if (ob->pose){ bPoseChannel *pchan; bConstraint *con; - Object * target; - char *subtarget; - for (pchan = ob->pose->chanbase.first; pchan; pchan=pchan->next){ - for (con = pchan->constraints.first; con; con=con->next){ - if (constraint_has_target(con)) { + for (pchan = ob->pose->chanbase.first; pchan; pchan=pchan->next) { + for (con = pchan->constraints.first; con; con=con->next) { + bConstraintTypeInfo *cti= constraint_get_typeinfo(con); + ListBase targets = {NULL, NULL}; + bConstraintTarget *ct; + + if (cti && cti->get_constraint_targets) { + cti->get_constraint_targets(con, &targets); - target = get_constraint_target(con, &subtarget); - if (target!=ob) { - // fprintf(stderr,"armature %s target :%s \n", ob->id.name, target->id.name); - node3 = dag_get_node(dag, target); - - if(subtarget && subtarget[0]) - dag_add_relation(dag,node3,node, DAG_RL_OB_DATA|DAG_RL_DATA_DATA); - else - dag_add_relation(dag,node3,node, DAG_RL_OB_DATA); - + for (ct= targets.first; ct; ct= ct->next) { + if (ct->tar && ct->tar != ob) { + // fprintf(stderr,"armature %s target :%s \n", ob->id.name, target->id.name); + node3 = dag_get_node(dag, ct->tar); + + if (ct->subtarget[0]) + dag_add_relation(dag,node3,node, DAG_RL_OB_DATA|DAG_RL_DATA_DATA); + else if(ELEM(con->type, CONSTRAINT_TYPE_FOLLOWPATH, CONSTRAINT_TYPE_CLAMPTO)) + dag_add_relation(dag,node3,node, DAG_RL_DATA_DATA|DAG_RL_OB_DATA); + else + dag_add_relation(dag,node3,node, DAG_RL_OB_DATA); + } } + + if (cti->flush_constraint_targets) + cti->flush_constraint_targets(con, &targets, 1); } + } } } @@ -398,9 +412,12 @@ static void build_dag_object(DagForest *dag, DagNode *scenenode, Object *ob, int if(key && key->ipo) dag_add_driver_relation(key->ipo, dag, node, 1); + for (conchan=ob->constraintChannels.first; conchan; conchan=conchan->next) + if(conchan->ipo) + dag_add_driver_relation(conchan->ipo, dag, node, 0); + if(ob->action) { bActionChannel *chan; - bConstraintChannel *conchan; for (chan = ob->action->chanbase.first; chan; chan=chan->next){ if(chan->ipo) dag_add_driver_relation(chan->ipo, dag, node, 1); @@ -477,7 +494,13 @@ static void build_dag_object(DagForest *dag, DagNode *scenenode, Object *ob, int dag_add_relation(dag, node, node2, DAG_RL_DATA_DATA|DAG_RL_OB_OB); /* inverted relation, so addtoroot shouldn't be set to zero */ } - + if (ob->type==OB_CAMERA) { + Camera *cam = (Camera *)ob->data; + if (cam->dof_ob) { + node2 = dag_get_node(dag, cam->dof_ob); + dag_add_relation(dag,node2,node,DAG_RL_OB_OB); + } + } if (ob->transflag & OB_DUPLI) { if((ob->transflag & OB_DUPLIGROUP) && ob->dup_group) { GroupObject *go; @@ -566,22 +589,85 @@ static void build_dag_object(DagForest *dag, DagNode *scenenode, Object *ob, int } } } + + psys= ob->particlesystem.first; + if(psys) { + ParticleEffectorCache *nec; + + for(; psys; psys=psys->next) { + ParticleSettings *part= psys->part; + + dag_add_relation(dag, node, node, DAG_RL_OB_DATA); + + 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); + } + + if(psys->effectors.first) + psys_end_effectors(psys); + psys_init_effectors(ob,psys->part->eff_group,psys); + + if(psys->effectors.first) { + 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); + else + dag_add_relation(dag, node2, node, DAG_RL_OB_DATA); + } + 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); + } + else if(nec->type & PSYS_EC_PARTICLE) { + node2 = dag_get_node(dag, ob1); + dag_add_relation(dag, node2, node, DAG_RL_DATA_DATA); + } + + if(nec->type & PSYS_EC_REACTOR) { + node2 = dag_get_node(dag, ob1); + dag_add_relation(dag, node, node2, DAG_RL_DATA_DATA); + } + } + } + } + } - for (con = ob->constraints.first; con; con=con->next){ - if (constraint_has_target(con)) { - char *str; - Object *obt= get_constraint_target(con, &str); + for (con = ob->constraints.first; con; con=con->next) { + bConstraintTypeInfo *cti= constraint_get_typeinfo(con); + ListBase targets = {NULL, NULL}; + bConstraintTarget *ct; + + if (cti && cti->get_constraint_targets) { + cti->get_constraint_targets(con, &targets); - node2 = dag_get_node(dag, obt); - if(con->type==CONSTRAINT_TYPE_FOLLOWPATH) - dag_add_relation(dag, node2, node, DAG_RL_DATA_OB|DAG_RL_OB_OB); - else { - if(obt->type==OB_ARMATURE && str[0]) - dag_add_relation(dag, node2, node, DAG_RL_DATA_OB|DAG_RL_OB_OB); + for (ct= targets.first; ct; ct= ct->next) { + Object *obt; + + if (ct->tar) + obt= ct->tar; else - dag_add_relation(dag, node2, node, DAG_RL_OB_OB); + continue; + + node2 = dag_get_node(dag, obt); + if (ELEM(con->type, CONSTRAINT_TYPE_FOLLOWPATH, CONSTRAINT_TYPE_CLAMPTO)) + dag_add_relation(dag, node2, node, DAG_RL_DATA_OB|DAG_RL_OB_OB); + else { + if (ELEM3(obt->type, OB_ARMATURE, OB_MESH, OB_LATTICE) && (ct->subtarget[0])) + dag_add_relation(dag, node2, node, DAG_RL_DATA_OB|DAG_RL_OB_OB); + else + dag_add_relation(dag, node2, node, DAG_RL_OB_OB); + } + addtoroot = 0; } - addtoroot = 0; + + if (cti->flush_constraint_targets) + cti->flush_constraint_targets(con, &targets, 1); } } @@ -1705,10 +1791,23 @@ static void dag_object_time_update_flags(Object *ob) if(ob->ipo) ob->recalc |= OB_RECALC_OB; else if(ob->constraints.first) { bConstraint *con; - for (con = ob->constraints.first; con; con=con->next){ - if (constraint_has_target(con)) { - ob->recalc |= OB_RECALC_OB; - break; + for (con = ob->constraints.first; con; con=con->next) { + bConstraintTypeInfo *cti= constraint_get_typeinfo(con); + ListBase targets = {NULL, NULL}; + bConstraintTarget *ct; + + if (cti && cti->get_constraint_targets) { + cti->get_constraint_targets(con, &targets); + + for (ct= targets.first; ct; ct= ct->next) { + if (ct->tar) { + ob->recalc |= OB_RECALC_OB; + break; + } + } + + if (cti->flush_constraint_targets) + cti->flush_constraint_targets(con, &targets, 1); } } } @@ -1765,6 +1864,8 @@ static void dag_object_time_update_flags(Object *ob) ob->recalc |= OB_RECALC_DATA; // NT FSPARTICLE } } + if(ob->particlesystem.first) + ob->recalc |= OB_RECALC_DATA; break; case OB_CURVE: case OB_SURF: @@ -1794,6 +1895,17 @@ static void dag_object_time_update_flags(Object *ob) if(ob->transflag & OB_DUPLI) ob->recalc |= OB_RECALC_DATA; break; } + + if(ob->particlesystem.first) { + ParticleSystem *psys= ob->particlesystem.first; + + for(; psys; psys=psys->next) { + if(psys->flag & PSYS_ENABLED) { + ob->recalc |= OB_RECALC_DATA; + break; + } + } + } } } @@ -2063,42 +2175,50 @@ void DAG_pose_sort(Object *ob) dag_add_parent_relation(dag, node2, node, 0); addtoroot = 0; } - for (con = pchan->constraints.first; con; con=con->next){ - if (constraint_has_target(con)) { - char *subtarget; - Object *target = get_constraint_target(con, &subtarget); + for (con = pchan->constraints.first; con; con=con->next) { + bConstraintTypeInfo *cti= constraint_get_typeinfo(con); + ListBase targets = {NULL, NULL}; + bConstraintTarget *ct; + + if (cti && cti->get_constraint_targets) { + cti->get_constraint_targets(con, &targets); - if (target==ob && subtarget) { - bPoseChannel *target= get_pose_channel(ob->pose, subtarget); - if(target) { - node2= dag_get_node(dag, target); - dag_add_relation(dag, node2, node, 0); - dag_add_parent_relation(dag, node2, node, 0); - - if(con->type==CONSTRAINT_TYPE_KINEMATIC) { - bKinematicConstraint *data = (bKinematicConstraint*)con->data; - bPoseChannel *parchan; - int segcount= 0; + for (ct= targets.first; ct; ct= ct->next) { + if (ct->tar==ob && ct->subtarget[0]) { + bPoseChannel *target= get_pose_channel(ob->pose, ct->subtarget); + if (target) { + node2= dag_get_node(dag, target); + dag_add_relation(dag, node2, node, 0); + dag_add_parent_relation(dag, node2, node, 0); - /* exclude tip from chain? */ - if(!(data->flag & CONSTRAINT_IK_TIP)) - parchan= pchan->parent; - else - parchan= pchan; - - /* Walk to the chain's root */ - while (parchan){ - node3= dag_get_node(dag, parchan); - dag_add_relation(dag, node2, node3, 0); - dag_add_parent_relation(dag, node2, node3, 0); + if (con->type==CONSTRAINT_TYPE_KINEMATIC) { + bKinematicConstraint *data = (bKinematicConstraint *)con->data; + bPoseChannel *parchan; + int segcount= 0; - segcount++; - if(segcount==data->rootbone || segcount>255) break; // 255 is weak - parchan= parchan->parent; + /* exclude tip from chain? */ + if(!(data->flag & CONSTRAINT_IK_TIP)) + parchan= pchan->parent; + else + parchan= pchan; + + /* Walk to the chain's root */ + while (parchan) { + node3= dag_get_node(dag, parchan); + dag_add_relation(dag, node2, node3, 0); + dag_add_parent_relation(dag, node2, node3, 0); + + segcount++; + if (segcount==data->rootbone || segcount>255) break; // 255 is weak + parchan= parchan->parent; + } } } } } + + if (cti->flush_constraint_targets) + cti->flush_constraint_targets(con, &targets, 1); } } if (addtoroot == 1 ) { diff --git a/source/blender/blenkernel/intern/displist.c b/source/blender/blenkernel/intern/displist.c index 30a4c8c3433..f410167c2ac 100644 --- a/source/blender/blenkernel/intern/displist.c +++ b/source/blender/blenkernel/intern/displist.c @@ -1098,7 +1098,7 @@ void curve_to_filledpoly(Curve *cu, ListBase *nurb, ListBase *dispbase) - first point left, last point right - based on subdivided points in original curve, not on points in taper curve (still) */ -static float calc_taper(Object *taperobj, int cur, int tot) +float calc_taper(Object *taperobj, int cur, int tot) { Curve *cu; DispList *dl; @@ -1256,7 +1256,7 @@ void curve_calc_modifiers_post(Object *ob, ListBase *nurb, ListBase *dispbase, i if ((md->mode & required_mode) != required_mode) continue; if (mti->isDisabled && mti->isDisabled(md)) continue; - if (mti->type!=eModifierTypeType_OnlyDeform) continue; + if (mti->type!=eModifierTypeType_OnlyDeform && mti->type!=eModifierTypeType_DeformOrConstruct) continue; for (dl=dispbase->first; dl; dl=dl->next) { mti->deformVerts(md, ob, NULL, (float(*)[3]) dl->verts, (dl->type==DL_INDEX3)?dl->nr:dl->parts*dl->nr); diff --git a/source/blender/blenkernel/intern/effect.c b/source/blender/blenkernel/intern/effect.c index f42d0fa79e2..1b345616888 100644 --- a/source/blender/blenkernel/intern/effect.c +++ b/source/blender/blenkernel/intern/effect.c @@ -799,7 +799,7 @@ static int pdDoDeflection(RNG *rng, float opco[3], float npco[3], float opno[3], // t= 0.5; // this is labda of line, can use it optimize quad intersection // sorry but no .. see below (BM) - if( LineIntersectsTriangle(opco, npco, nv1, nv2, nv3, &t) ) { + if( LineIntersectsTriangle(opco, npco, nv1, nv2, nv3, &t, NULL) ) { if (t < min_t) { deflected = 1; deflected_now = 1; @@ -810,7 +810,7 @@ static int pdDoDeflection(RNG *rng, float opco[3], float npco[3], float opno[3], // it might give a smaller t on (close to) the edge .. this is numerics not esoteric maths :) // note: the 2 triangles don't need to share a plane ! (BM) if (mface->v4) { - if( LineIntersectsTriangle(opco, npco, nv1, nv3, nv4, &t2) ) { + if( LineIntersectsTriangle(opco, npco, nv1, nv3, nv4, &t2, NULL) ) { if (t2 < min_t) { deflected = 1; deflected_now = 2; @@ -1686,7 +1686,9 @@ void build_particle_system(Object *ob) float *volengths= NULL, *folengths= NULL; int deform=0, a, totpart, paf_sta, paf_end; int waitcursor_set= 0, totvert, totface, curface, curvert; +#ifndef DISABLE_ELBEEM int readMask, activeParts, fileParts; +#endif /* return conditions */ if(ob->type!=OB_MESH) return; diff --git a/source/blender/blenkernel/intern/exotic.c b/source/blender/blenkernel/intern/exotic.c index fe52a3fc198..0f1f8c6078a 100644 --- a/source/blender/blenkernel/intern/exotic.c +++ b/source/blender/blenkernel/intern/exotic.c @@ -3025,7 +3025,7 @@ void write_vrml(char *str) /* FIRST: write all the datablocks */ - fprintf(fp, "#VRML V1.0 ascii\n\n# Blender V2.0\n\n# 'Switch' is used as a hack, to ensure it is not part of the drawing\n\n"); + fprintf(fp, "#VRML V1.0 ascii\n\n# Blender V%d\n\n# 'Switch' is used as a hack, to ensure it is not part of the drawing\n\n", G.version); fprintf(fp, "Separator {\n"); fprintf(fp, "Switch {\n"); @@ -3037,9 +3037,16 @@ void write_vrml(char *str) ma= ma->id.next; } + /* only write meshes we're using in this scene */ + flag_listbase_ids(&G.main->mesh, LIB_DOIT, 0); + + for(base= G.scene->base.first; base; base= base->next) + if(base->object->type== OB_MESH) + ((ID *)base->object->data)->flag |= LIB_DOIT; + me= G.main->mesh.first; while(me) { - if(me->id.us) { + if(me->id.flag & LIB_DOIT) { /* is the mesh used in this scene ? */ write_mesh_vrml(fp, me); } me= me->id.next; @@ -3342,10 +3349,18 @@ void write_dxf(char *str) write_group(0, "SECTION"); write_group(2, "BLOCKS"); + + /* only write meshes we're using in this scene */ + flag_listbase_ids(&G.main->mesh, LIB_DOIT, 0); + + for(base= G.scene->base.first; base; base= base->next) + if(base->object->type== OB_MESH) + ((ID *)base->object->data)->flag |= LIB_DOIT; + /* Write all the meshes */ me= G.main->mesh.first; while(me) { - if(me->id.us) { + if(me->id.flag & LIB_DOIT) { /* is the mesh used in this scene ? */ write_mesh_dxf(fp, me); } me= me->id.next; diff --git a/source/blender/blenkernel/intern/icons.c b/source/blender/blenkernel/intern/icons.c index ee37a4ec9f9..f144d2badd1 100644 --- a/source/blender/blenkernel/intern/icons.c +++ b/source/blender/blenkernel/intern/icons.c @@ -1,34 +1,32 @@ /** - * $Id$ - * - * ***** BEGIN GPL/BL DUAL LICENSE BLOCK ***** - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. The Blender - * Foundation also sells licenses for use in proprietary software under - * the Blender License. See http://www.blender.org/BL/ for information - * about this. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - * - * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. - * All rights reserved. - * - * The Original Code is: all of this file. - * - * Contributor(s): none yet. - * - * ***** END GPL/BL DUAL LICENSE BLOCK ***** - */ +* $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) 2006-2007 Blender Foundation. +* All rights reserved. +* +* The Original Code is: all of this file. +* +* Contributor(s): none yet. +* +* ***** END GPL LICENSE BLOCK ***** +* +*/ #include <math.h> #include <stdlib.h> @@ -41,6 +39,11 @@ #include "MEM_guardedalloc.h" #include "DNA_ID.h" +#include "DNA_image_types.h" +#include "DNA_lamp_types.h" +#include "DNA_material_types.h" +#include "DNA_texture_types.h" +#include "DNA_world_types.h" #include "BLI_ghash.h" @@ -111,6 +114,101 @@ void BKE_icons_free() gIcons = NULL; } +struct PreviewImage* BKE_previewimg_create() +{ + PreviewImage* prv_img = NULL; + int i; + + prv_img = MEM_callocN(sizeof(PreviewImage), "img_prv"); + + for (i=0; i<PREVIEW_MIPMAPS; ++i) { + prv_img->changed[i] = 1; + } + return prv_img; +} + +void BKE_previewimg_free(PreviewImage **prv) +{ + if(prv && (*prv)) { + int i; + + for (i=0; i<PREVIEW_MIPMAPS;++i) { + if ((*prv)->rect[i]) { + MEM_freeN((*prv)->rect[i]); + (*prv)->rect[i] = NULL; + } + } + MEM_freeN((*prv)); + *prv = NULL; + } +} + +struct PreviewImage* BKE_previewimg_copy(PreviewImage *prv) +{ + PreviewImage* prv_img = NULL; + int i; + + if (prv) { + prv_img = MEM_dupallocN(prv); + for (i=0; i < PREVIEW_MIPMAPS; ++i) { + if (prv->rect[i]) { + prv_img->rect[i] = MEM_dupallocN(prv->rect[i]); + } else { + prv_img->rect[i] = NULL; + } + } + } + return prv_img; +} + +void BKE_previewimg_free_id(ID *id) +{ + if (GS(id->name) == ID_MA) { + Material *mat = (Material*)id; + BKE_previewimg_free(&mat->preview); + } else if (GS(id->name) == ID_TE) { + Tex *tex = (Tex*)id; + BKE_previewimg_free(&tex->preview); + } else if (GS(id->name) == ID_WO) { + World *wo = (World*)id; + BKE_previewimg_free(&wo->preview); + } else if (GS(id->name) == ID_LA) { + Lamp *la = (Lamp*)id; + BKE_previewimg_free(&la->preview); + } else if (GS(id->name) == ID_IM) { + Image *img = (Image*)id; + BKE_previewimg_free(&img->preview); + } +} + +PreviewImage* BKE_previewimg_get(ID *id) +{ + PreviewImage* prv_img = NULL; + + if (GS(id->name) == ID_MA) { + Material *mat = (Material*)id; + if (!mat->preview) mat->preview = BKE_previewimg_create(); + prv_img = mat->preview; + } else if (GS(id->name) == ID_TE) { + Tex *tex = (Tex*)id; + if (!tex->preview) tex->preview = BKE_previewimg_create(); + prv_img = tex->preview; + } else if (GS(id->name) == ID_WO) { + World *wo = (World*)id; + if (!wo->preview) wo->preview = BKE_previewimg_create(); + prv_img = wo->preview; + } else if (GS(id->name) == ID_LA) { + Lamp *la = (Lamp*)id; + if (!la->preview) la->preview = BKE_previewimg_create(); + prv_img = la->preview; + } else if (GS(id->name) == ID_IM) { + Image *img = (Image*)id; + if (!img->preview) img->preview = BKE_previewimg_create(); + prv_img = img->preview; + } + + return prv_img; +} void BKE_icon_changed(int id) { @@ -122,7 +220,15 @@ void BKE_icon_changed(int id) if (icon) { - icon->changed = 1; + PreviewImage *prv = BKE_previewimg_get((ID*)icon->obj); + + /* all previews changed */ + if (prv) { + int i; + for (i=0; i<PREVIEW_MIPMAPS; ++i) { + prv->changed[i] = 1; + } + } } } @@ -151,7 +257,6 @@ int BKE_icon_getid(struct ID* id) /* next two lines make sure image gets created */ new_icon->drawinfo = 0; new_icon->drawinfo_free = 0; - new_icon->changed = 1; BLI_ghash_insert(gIcons, (void *)id->icon_id, new_icon); diff --git a/source/blender/blenkernel/intern/idprop.c b/source/blender/blenkernel/intern/idprop.c index 4ff4073fdbe..bf2a3aae11a 100644 --- a/source/blender/blenkernel/intern/idprop.c +++ b/source/blender/blenkernel/intern/idprop.c @@ -104,6 +104,31 @@ void IDP_ResizeArray(IDProperty *prop, int newlen) MEM_freeN(prop->data.pointer); } + + static IDProperty *idp_generic_copy(IDProperty *prop) + { + IDProperty *newp = MEM_callocN(sizeof(IDProperty), "IDProperty array dup"); + + strncpy(newp->name, prop->name, MAX_IDPROP_NAME); + newp->type = prop->type; + newp->flag = prop->flag; + newp->data.val = prop->data.val; + + return newp; + } + +IDProperty *IDP_CopyArray(IDProperty *prop) +{ + IDProperty *newp = idp_generic_copy(prop); + + if (prop->data.pointer) newp->data.pointer = MEM_dupallocN(prop->data.pointer); + newp->len = prop->len; + newp->subtype = prop->subtype; + newp->totallen = prop->totallen; + + return newp; +} + /*taken from readfile.c*/ #define SWITCH_LONGINT(a) { \ char s_i, *p_i; \ @@ -116,6 +141,19 @@ void IDP_ResizeArray(IDProperty *prop, int newlen) /* ---------- String Type ------------ */ +IDProperty *IDP_CopyString(IDProperty *prop) +{ + IDProperty *newp = idp_generic_copy(prop); + + if (prop->data.pointer) newp->data.pointer = MEM_dupallocN(prop->data.pointer); + newp->len = prop->len; + newp->subtype = prop->subtype; + newp->totallen = prop->totallen; + + return newp; +} + + void IDP_AssignString(IDProperty *prop, char *st) { int stlen; @@ -154,7 +192,7 @@ void IDP_FreeString(IDProperty *prop) } -/*-------- ID Type -------*/ +/*-------- ID Type, not in use yet -------*/ void IDP_LinkID(IDProperty *prop, ID *id) { @@ -171,6 +209,17 @@ void IDP_UnlinkID(IDProperty *prop) /*-------- Group Functions -------*/ /*checks if a property with the same name as prop exists, and if so replaces it.*/ +IDProperty *IDP_CopyGroup(IDProperty *prop) +{ + IDProperty *newp = idp_generic_copy(prop), *link; + + for (link=prop->data.group.first; link; link=link->next) { + BLI_addtail(&newp->data.group, IDP_CopyProperty(link)); + } + + return newp; +} + void IDP_ReplaceInGroup(IDProperty *group, IDProperty *prop) { IDProperty *loop; @@ -274,6 +323,7 @@ void IDP_FreeGroup(IDProperty *prop) for (loop=prop->data.group.first; loop; loop=next) { next = loop->next; + BLI_remlink(&prop->data.group, loop); IDP_FreeProperty(loop); MEM_freeN(loop); } @@ -281,6 +331,15 @@ void IDP_FreeGroup(IDProperty *prop) /*-------- Main Functions --------*/ +IDProperty *IDP_CopyProperty(IDProperty *prop) +{ + switch (prop->type) { + case IDP_GROUP: return IDP_CopyGroup(prop); + case IDP_STRING: return IDP_CopyString(prop); + case IDP_ARRAY: return IDP_CopyArray(prop); + default: return idp_generic_copy(prop); + } +} IDProperty *IDP_GetProperties(ID *id, int create_if_needed) { diff --git a/source/blender/blenkernel/intern/image.c b/source/blender/blenkernel/intern/image.c index 21fa38fd83d..bb60c221839 100644 --- a/source/blender/blenkernel/intern/image.c +++ b/source/blender/blenkernel/intern/image.c @@ -52,6 +52,7 @@ #include "DNA_image_types.h" #include "DNA_packedFile_types.h" #include "DNA_scene_types.h" +#include "DNA_camera_types.h" #include "DNA_texture_types.h" #include "DNA_userdef_types.h" @@ -70,6 +71,8 @@ #include "BKE_texture.h" #include "BKE_utildefines.h" +#include "BIF_editseq.h" + #include "PIL_time.h" #include "RE_pipeline.h" @@ -77,6 +80,12 @@ /* bad level; call to free_realtime_image */ #include "BKE_bad_level_calls.h" +/* for stamp drawing to an image */ +#include "BMF_Api.h" + +#include "blendef.h" +#include "BSE_time.h" + /* max int, to indicate we don't store sequences in ibuf */ #define IMA_NO_INDEX 0x7FEFEFEF @@ -206,12 +215,6 @@ static void image_free_buffers(Image *ima) if(ima->anim) IMB_free_anim(ima->anim); ima->anim= NULL; - if (ima->preview) { - MEM_freeN(ima->preview->rect); - MEM_freeN(ima->preview); - ima->preview = NULL; - } - if(ima->rr) { RE_FreeRenderResult(ima->rr); ima->rr= NULL; @@ -233,6 +236,10 @@ void free_image(Image *ima) } BKE_icon_delete(&ima->id); ima->id.icon_id = 0; + if (ima->preview) { + BKE_previewimg_free(&ima->preview); + } + } /* only image block itself */ @@ -245,6 +252,7 @@ static Image *image_alloc(const char *name, short source, short type) ima->ok= IMA_OK; ima->xrep= ima->yrep= 1; + ima->aspx= ima->aspy= 1.0; ima->gen_x= 256; ima->gen_y= 256; ima->gen_type= 1; /* no defines yet? */ @@ -335,7 +343,7 @@ Image *BKE_add_image_file(const char *name) /* first search an identical image */ for(ima= G.main->image.first; ima; ima= ima->id.next) { - if(ima->source!=IMA_SRC_VIEWER) { + if(ima->source!=IMA_SRC_VIEWER && ima->source!=IMA_SRC_GENERATED) { BLI_strncpy(strtest, ima->name, sizeof(ima->name)); BLI_convertstringcode(strtest, G.sce, G.scene->r.cfra); @@ -633,6 +641,10 @@ int BKE_imtype_to_ftype(int imtype) return RADHDR; else if (imtype==R_PNG) return PNG; +#ifdef WITH_DDS + else if (imtype==R_DDS) + return DDS; +#endif else if (imtype==R_BMP) return BMP; else if (imtype==R_TIFF) @@ -663,6 +675,10 @@ int BKE_ftype_to_imtype(int ftype) return R_RADHDR; else if (ftype & PNG) return R_PNG; +#ifdef WITH_DDS + else if (ftype & DDS) + return R_DDS; +#endif else if (ftype & BMP) return R_BMP; else if (ftype & TIF) @@ -719,6 +735,12 @@ void BKE_add_image_extension(char *string, int imtype) if(!BLI_testextensie(string, ".png")) extension= ".png"; } +#ifdef WITH_DDS + else if(imtype==R_DDS) { + if(!BLI_testextensie(string, ".dds")) + extension= ".dds"; + } +#endif else if(imtype==R_RAWTGA) { if(!BLI_testextensie(string, ".tga")) extension= ".tga"; @@ -757,6 +779,278 @@ void BKE_add_image_extension(char *string, int imtype) strcat(string, extension); } +/* could allow access externally - 512 is for long names, 64 is for id names */ +typedef struct StampData { + char file[512]; + char note[512]; + char date[512]; + char marker[512]; + char time[512]; + char frame[512]; + char camera[64]; + char scene[64]; + char strip[64]; +} StampData; + +static void stampdata(StampData *stamp_data, int do_prefix) +{ + char text[256]; + +#ifndef WIN32 + struct tm *tl; + time_t t; +#else + char sdate[9]; +#endif /* WIN32 */ + + if (G.scene->r.stamp & R_STAMP_FILENAME) { + if (do_prefix) sprintf(stamp_data->file, "File %s", G.sce); + else sprintf(stamp_data->file, "%s", G.sce); + stamp_data->note[0] = '\0'; + } else { + stamp_data->file[0] = '\0'; + } + + if (G.scene->r.stamp & R_STAMP_NOTE) { + if (do_prefix) sprintf(stamp_data->note, "Note %s", G.scene->r.stamp_udata); + else sprintf(stamp_data->note, "%s", G.scene->r.stamp_udata); + } else { + stamp_data->note[0] = '\0'; + } + + if (G.scene->r.stamp & R_STAMP_DATE) { +#ifdef WIN32 + _strdate (sdate); + sprintf (text, "%s", sdate); +#else + t = time (NULL); + tl = localtime (&t); + sprintf (text, "%04d-%02d-%02d", tl->tm_year+1900, tl->tm_mon+1, tl->tm_mday); +#endif /* WIN32 */ + if (do_prefix) sprintf(stamp_data->date, "Date %s", text); + else sprintf(stamp_data->date, "%s", text); + } else { + stamp_data->date[0] = '\0'; + } + + if (G.scene->r.stamp & R_STAMP_MARKER) { + TimeMarker *marker = get_frame_marker(CFRA); + + if (marker) strcpy(text, marker->name); + else strcpy(text, "<none>"); + + if (do_prefix) sprintf(stamp_data->marker, "Marker %s", text); + else sprintf(stamp_data->marker, "%s", text); + } else { + stamp_data->marker[0] = '\0'; + } + + if (G.scene->r.stamp & R_STAMP_TIME) { + int h, m, s, f; + h= m= s= f= 0; + f = (int)(G.scene->r.cfra % G.scene->r.frs_sec); + s = (int)(G.scene->r.cfra / G.scene->r.frs_sec); + + if (s) { + m = (int)(s / 60); + s %= 60; + + if (m) { + h = (int)(m / 60); + m %= 60; + } + } + + if (G.scene->r.frs_sec < 100) + sprintf (text, "%02d:%02d:%02d.%02d", h, m, s, f); + else + sprintf (text, "%02d:%02d:%02d.%03d", h, m, s, f); + + if (do_prefix) sprintf(stamp_data->time, "Time %s", text); + else sprintf(stamp_data->time, "%s", text); + } else { + stamp_data->time[0] = '\0'; + } + + if (G.scene->r.stamp & R_STAMP_FRAME) { + char format[32]; + if (do_prefix) sprintf(format, "Frame %%0%di\n", 1 + (int) log10(G.scene->r.efra)); + else sprintf(format, "%%0%di\n", 1 + (int) log10(G.scene->r.efra)); + sprintf (stamp_data->frame, format, G.scene->r.cfra); + } else { + stamp_data->frame[0] = '\0'; + } + + if (G.scene->r.stamp & R_STAMP_CAMERA) { + if (do_prefix) sprintf(stamp_data->camera, "Camera %s", ((Camera *) G.scene->camera)->id.name+2); + else sprintf(stamp_data->camera, "%s", ((Camera *) G.scene->camera)->id.name+2); + } else { + stamp_data->camera[0] = '\0'; + } + + if (G.scene->r.stamp & R_STAMP_SCENE) { + if (do_prefix) sprintf(stamp_data->scene, "Scene %s", G.scene->id.name+2); + else sprintf(stamp_data->scene, "%s", G.scene->id.name+2); + } else { + stamp_data->scene[0] = '\0'; + } + + if (G.scene->r.stamp & R_STAMP_SEQSTRIP) { + Sequence *seq = get_forground_frame_seq(CFRA); + + if (seq) strcpy(text, seq->name+2); + else strcpy(text, "<none>"); + + if (do_prefix) sprintf(stamp_data->strip, "Strip %s", text); + else sprintf(stamp_data->strip, "%s", text); + } else { + stamp_data->strip[0] = '\0'; + } +} + +void BKE_stamp_buf(unsigned char *rect, float *rectf, int width, int height) +{ + struct StampData stamp_data; + + int x=1,y=1; + int font_height; + int text_width; + int text_pad; + struct BMF_Font *font; + + if (!rect && !rectf) + return; + + stampdata(&stamp_data, 1); + + switch (G.scene->r.stamp_font_id) { + case 1: /* tiny */ + font = BMF_GetFont(BMF_kHelveticaBold8); + break; + case 2: /* small */ + font = BMF_GetFont(BMF_kHelveticaBold10); + break; + case 3: /* medium */ + font = BMF_GetFont(BMF_kScreen12); + break; + case 0: /* large - default */ + font = BMF_GetFont(BMF_kScreen15); + break; + case 4: /* huge */ + font = BMF_GetFont(BMF_kHelveticaBold14); + break; + default: + font = NULL; + break; + } + + font_height = BMF_GetFontHeight(font); + /* All texts get halfspace+1 pixel on each side and 1 pix + above and below as padding against their backing rectangles */ + text_pad = BMF_GetStringWidth(font, " "); + + x = 1; /* Inits for everyone, text position, so 1 for padding, not 0 */ + y = height - font_height - 1; /* Also inits for everyone, notice padding pixel */ + + if (stamp_data.file[0]) { + /* Top left corner */ + text_width = BMF_GetStringWidth(font, stamp_data.file); + buf_rectfill_area(rect, rectf, width, height, G.scene->r.bg_stamp, x-1, y-1, x+text_width+text_pad+1, y+font_height+1); + BMF_DrawStringBuf(font, stamp_data.file, x+(text_pad/2), y, G.scene->r.fg_stamp, rect, rectf, width, height); + y -= font_height+2; /* Top and bottom 1 pix padding each */ + } + + /* Top left corner, below File */ + if (stamp_data.note[0]) { + text_width = BMF_GetStringWidth(font, stamp_data.note); + buf_rectfill_area(rect, rectf, width, height, G.scene->r.bg_stamp, x-1, y-1, x+text_width+text_pad+1, y+font_height+1); + BMF_DrawStringBuf(font, stamp_data.note, x+(text_pad/2), y, G.scene->r.fg_stamp, rect, rectf, width, height); + y -= font_height+2; /* Top and bottom 1 pix padding each */ + } + + /* Top left corner, below File (or Note) */ + if (stamp_data.date[0]) { + text_width = BMF_GetStringWidth(font, stamp_data.date); + buf_rectfill_area(rect, rectf, width, height, G.scene->r.bg_stamp, x-1, y-1, x+text_width+text_pad+1, y+font_height+1); + BMF_DrawStringBuf(font, stamp_data.date, x+(text_pad/2), y, G.scene->r.fg_stamp, rect, rectf, width, height); + } + + /* Bottom left corner, leaving space for timing */ + if (stamp_data.marker[0]) { + x = 1; + y = font_height+2+1; /* 2 for padding in TIME|FRAME fields below and 1 for padding in this one */ + text_width = BMF_GetStringWidth(font, stamp_data.marker); + buf_rectfill_area(rect, rectf, width, height, G.scene->r.bg_stamp, x-1, y-1, x+text_width+text_pad+1, y+font_height+1); + BMF_DrawStringBuf(font, stamp_data.marker, x+(text_pad/2), y, G.scene->r.fg_stamp, rect, rectf, width, height); + } + + /* Left bottom corner */ + if (stamp_data.time[0]) { + x = 1; + y = 1; + text_width = BMF_GetStringWidth(font, stamp_data.time); + buf_rectfill_area(rect, rectf, width, height, G.scene->r.bg_stamp, x-1, y-1, x+text_width+text_pad+1, y+font_height+1); + BMF_DrawStringBuf(font, stamp_data.time, x+(text_pad/2), y, G.scene->r.fg_stamp, rect, rectf, width, height); + x += text_width+text_pad+2; /* Both sides have 1 pix additional padding each */ + } + + if (stamp_data.frame[0]) { + text_width = BMF_GetStringWidth(font, stamp_data.frame); + /* Left bottom corner (after SMPTE if exists) */ + if (!stamp_data.time[0]) x = 1; + y = 1; + buf_rectfill_area(rect, rectf, width, height, G.scene->r.bg_stamp, x-1, y-1, x+text_width+text_pad+1, y+font_height+1); + BMF_DrawStringBuf(font, stamp_data.frame, x+(text_pad/2), y, G.scene->r.fg_stamp, rect, rectf, width, height); + } + + if (stamp_data.camera[0]) { + text_width = BMF_GetStringWidth(font, stamp_data.camera); + /* Center of bottom edge */ + x = (width/2) - (BMF_GetStringWidth(font, stamp_data.camera)/2); + y = 1; + buf_rectfill_area(rect, rectf, width, height, G.scene->r.bg_stamp, x-1, y-1, x+text_width+text_pad+1, y+font_height+1); + BMF_DrawStringBuf(font, stamp_data.camera, x+(text_pad/2), y, G.scene->r.fg_stamp, rect, rectf, width, height); + } + + if (stamp_data.scene[0]) { + text_width = BMF_GetStringWidth(font, stamp_data.scene); + /* Bottom right corner */ + x = width - (text_width+1+text_pad); + y = 1; + buf_rectfill_area(rect, rectf, width, height, G.scene->r.bg_stamp, x-1, y-1, x+text_width+text_pad+1, y+font_height+1); + BMF_DrawStringBuf(font, stamp_data.scene, x+(text_pad/2), y, G.scene->r.fg_stamp, rect, rectf, width, height); + } + + if (stamp_data.strip[0]) { + text_width = BMF_GetStringWidth(font, stamp_data.strip); + /* Top right corner */ + x = width - (text_width+1+text_pad); + y = height - font_height - 1; + buf_rectfill_area(rect, rectf, width, height, G.scene->r.bg_stamp, x-1, y-1, x+text_width+text_pad+1, y+font_height+1); + BMF_DrawStringBuf(font, stamp_data.strip, x+(text_pad/2), y, G.scene->r.fg_stamp, rect, rectf, width, height); + } + +} + +void BKE_stamp_info(struct ImBuf *ibuf) +{ + struct StampData stamp_data; + + if (!ibuf) return; + + /* fill all the data values, no prefix */ + stampdata(&stamp_data, 0); + + if (stamp_data.file[0]) IMB_imginfo_change_field (ibuf, "File", stamp_data.file); + if (stamp_data.note[0]) IMB_imginfo_change_field (ibuf, "Note", stamp_data.note); + if (stamp_data.date[0]) IMB_imginfo_change_field (ibuf, "Date", stamp_data.date); + if (stamp_data.marker[0]) IMB_imginfo_change_field (ibuf, "Marker", stamp_data.marker); + if (stamp_data.time[0]) IMB_imginfo_change_field (ibuf, "Time", stamp_data.time); + if (stamp_data.frame[0]) IMB_imginfo_change_field (ibuf, "Frame", stamp_data.frame); + if (stamp_data.camera[0]) IMB_imginfo_change_field (ibuf, "Camera", stamp_data.camera); + if (stamp_data.scene[0]) IMB_imginfo_change_field (ibuf, "Scene", stamp_data.scene); + if (stamp_data.strip[0]) IMB_imginfo_change_field (ibuf, "Strip", stamp_data.strip); +} int BKE_write_ibuf(ImBuf *ibuf, char *name, int imtype, int subimtype, int quality) { @@ -771,6 +1065,11 @@ int BKE_write_ibuf(ImBuf *ibuf, char *name, int imtype, int subimtype, int quali else if ((imtype==R_PNG)) { ibuf->ftype= PNG; } +#ifdef WITH_DDS + else if ((imtype==R_DDS)) { + ibuf->ftype= DDS; + } +#endif else if ((imtype==R_BMP)) { ibuf->ftype= BMP; } @@ -812,6 +1111,9 @@ int BKE_write_ibuf(ImBuf *ibuf, char *name, int imtype, int subimtype, int quali } BLI_make_existing_file(name); + + if(G.scene->r.scemode & R_STAMP_INFO) + BKE_stamp_info(ibuf); ok = IMB_saveiff(ibuf, name, IB_rect | IB_zbuf | IB_zbuffloat); if (ok == 0) { @@ -1236,7 +1538,7 @@ static ImBuf *image_load_image_file(Image *ima, ImageUser *iuser, int cfra) BLI_convertstringcode(str, G.sce, cfra); /* read ibuf */ - ibuf = IMB_loadiffname(str, IB_rect|IB_multilayer); + ibuf = IMB_loadiffname(str, IB_rect|IB_multilayer|IB_imginfo); } if (ibuf) { diff --git a/source/blender/blenkernel/intern/ipo.c b/source/blender/blenkernel/intern/ipo.c index 08924ac96d5..03a34df090a 100644 --- a/source/blender/blenkernel/intern/ipo.c +++ b/source/blender/blenkernel/intern/ipo.c @@ -52,6 +52,7 @@ #include "DNA_mesh_types.h" #include "DNA_object_types.h" #include "DNA_object_force.h" +#include "DNA_particle_types.h" #include "DNA_sequence_types.h" #include "DNA_scene_types.h" #include "DNA_sound_types.h" @@ -85,7 +86,7 @@ */ int co_ar[CO_TOTIPO]= { - CO_ENFORCE + CO_ENFORCE, CO_HEADTAIL }; int ob_ar[OB_TOTIPO]= { @@ -93,7 +94,7 @@ int ob_ar[OB_TOTIPO]= { OB_ROT_X, OB_ROT_Y, OB_ROT_Z, OB_DROT_X, OB_DROT_Y, OB_DROT_Z, OB_SIZE_X, OB_SIZE_Y, OB_SIZE_Z, OB_DSIZE_X, OB_DSIZE_Y, OB_DSIZE_Z, OB_LAY, OB_TIME, OB_COL_R, OB_COL_G, OB_COL_B, OB_COL_A, - OB_PD_FSTR, OB_PD_FFALL, OB_PD_SDAMP, OB_PD_RDAMP, OB_PD_PERM + OB_PD_FSTR, OB_PD_FFALL, OB_PD_SDAMP, OB_PD_RDAMP, OB_PD_PERM, OB_PD_FMAXD }; int ac_ar[AC_TOTIPO]= { @@ -180,6 +181,12 @@ int fluidsim_ar[FLUIDSIM_TOTIPO]= { FLUIDSIM_ACTIVE }; +int part_ar[PART_TOTIPO]= { + PART_EMIT_FREQ, PART_EMIT_LIFE, PART_EMIT_VEL, PART_EMIT_AVE, PART_EMIT_SIZE, + PART_AVE, PART_SIZE, PART_DRAG, PART_BROWN, PART_DAMP, PART_LENGTH, PART_CLUMP, + PART_GRAV_X, PART_GRAV_Y, PART_GRAV_Z, PART_KINK_AMP, PART_KINK_FREQ, PART_KINK_SHAPE, + PART_BB_TILT +}; float frame_to_float(int cfra) /* see also bsystem_time in object.c */ @@ -751,6 +758,46 @@ void berekenx(float *f, float *o, int b) } } +#define TFM_WITHOUT_BONE 1 + +static void posechannel_get_local_transform(bPoseChannel *pchan, float *quat, float *eul, float *size, int flag) +{ + float pose_mat[3][3]; + float diff_mat[3][3], ipar_mat[3][3]; + + /* we need the local transform = current transform - (parent transform + bone transform) */ + + Mat3CpyMat4(pose_mat, pchan->pose_mat); + + if (pchan->parent) { + + if(flag & TFM_WITHOUT_BONE) { + float par_mat[3][3]; + Mat3CpyMat4(par_mat, pchan->parent->pose_mat); + Mat3MulMat3(diff_mat, par_mat, pchan->bone->bone_mat); + } + else + Mat3CpyMat4(diff_mat, pchan->parent->pose_mat); + + Mat3Inv(ipar_mat, diff_mat); + } + else { + if(flag & TFM_WITHOUT_BONE) + Mat3Inv(ipar_mat, pchan->bone->bone_mat); + else + Mat3One(ipar_mat); + } + + Mat3MulMat3(diff_mat, ipar_mat, pose_mat); + + if(quat) + Mat3ToQuat(diff_mat, quat); + if(eul) + Mat3ToEul(diff_mat, eul); + if(size) + Mat3ToSize(diff_mat, size); +} + /* has to return a float value */ static float eval_driver(IpoDriver *driver, float ipotime) { @@ -802,48 +849,51 @@ static float eval_driver(IpoDriver *driver, float ipotime) else { /* ID_AR */ bPoseChannel *pchan= get_pose_channel(ob->pose, driver->name); if(pchan && pchan->bone) { - float pose_mat[3][3]; - float diff_mat[3][3], par_mat[3][3], ipar_mat[3][3]; - float eul[3], size[3]; - - /* we need the local transform = current transform - (parent transform + bone transform) */ - - Mat3CpyMat4(pose_mat, pchan->pose_mat); - if (pchan->parent) { - Mat3CpyMat4(par_mat, pchan->parent->pose_mat); - Mat3MulMat3(diff_mat, par_mat, pchan->bone->bone_mat); - - Mat3Inv(ipar_mat, diff_mat); + /* rotation difference is not a simple driver (i.e. value drives value), but the angle between 2 bones is driving stuff... which is useful */ + if(driver->adrcode==OB_ROT_DIFF) { + bPoseChannel *pchan2= get_pose_channel(ob->pose, driver->name+DRIVER_NAME_OFFS); + if(pchan2 && pchan2->bone) { + float q1[4], q2[4], quat[4], angle; + + Mat4ToQuat(pchan->pose_mat, q1); + Mat4ToQuat(pchan2->pose_mat, q2); + // posechannel_get_local_transform(pchan , q1, NULL, NULL, 0); + // posechannel_get_local_transform(pchan2, q2, NULL, NULL, 0); + + QuatInv(q1); + QuatMul(quat, q1, q2); + angle = 2.0f * (saacos(quat[0])); + angle= ABS(angle); + + return angle>M_PI?2.0f*M_PI-angle:angle; + } } else { - Mat3Inv(ipar_mat, pchan->bone->bone_mat); - } - - Mat3MulMat3(diff_mat, ipar_mat, pose_mat); - - Mat3ToEul(diff_mat, eul); - Mat3ToSize(diff_mat, size); - - switch(driver->adrcode) { - case OB_LOC_X: - return pchan->loc[0]; - case OB_LOC_Y: - return pchan->loc[1]; - case OB_LOC_Z: - return pchan->loc[2]; - case OB_ROT_X: - return eul[0]/(M_PI_2/9.0); - case OB_ROT_Y: - return eul[1]/(M_PI_2/9.0); - case OB_ROT_Z: - return eul[2]/(M_PI_2/9.0); - case OB_SIZE_X: - return size[0]; - case OB_SIZE_Y: - return size[1]; - case OB_SIZE_Z: - return size[2]; + float eul[3], size[3]; + + posechannel_get_local_transform(pchan, NULL, eul, size, TFM_WITHOUT_BONE); + + switch(driver->adrcode) { + case OB_LOC_X: + return pchan->loc[0]; + case OB_LOC_Y: + return pchan->loc[1]; + case OB_LOC_Z: + return pchan->loc[2]; + case OB_ROT_X: + return eul[0]/(M_PI_2/9.0); + case OB_ROT_Y: + return eul[1]/(M_PI_2/9.0); + case OB_ROT_Z: + return eul[2]/(M_PI_2/9.0); + case OB_SIZE_X: + return size[0]; + case OB_SIZE_Y: + return size[1]; + case OB_SIZE_Z: + return size[2]; + } } } } @@ -995,10 +1045,13 @@ void calc_ipo(Ipo *ipo, float ctime) IpoCurve *icu; if(ipo==NULL) return; + if(ipo->muteipo) return; for(icu= ipo->curve.first; icu; icu= icu->next) { - if(icu->driver || (icu->flag & IPO_LOCK)==0) - calc_icu(icu, ctime); + if(icu->driver || (icu->flag & IPO_LOCK)==0) { + if((icu->flag & IPO_MUTE)==0) + calc_icu(icu, ctime); + } } } @@ -1185,6 +1238,7 @@ void *get_ipo_poin(ID *id, IpoCurve *icu, int *type) Lamp *la; Sequence *seq; World *wo; + ParticleSettings *part; *type= IPO_FLOAT; @@ -1262,6 +1316,9 @@ void *get_ipo_poin(ID *id, IpoCurve *icu, int *type) case OB_PD_PERM: if(ob->pd) poin= &(ob->pd->pdef_perm); break; + case OB_PD_FMAXD: + if(ob->pd) poin= &(ob->pd->maxdist); + break; } } else if( GS(id->name)==ID_MA) { @@ -1514,13 +1571,54 @@ void *get_ipo_poin(ID *id, IpoCurve *icu, int *type) poin= &(snd->attenuation); break; } } - + else if( GS(id->name)==ID_PA) { + + part= (ParticleSettings *)id; + + switch(icu->adrcode) { + case PART_EMIT_FREQ: + case PART_EMIT_LIFE: + case PART_EMIT_VEL: + case PART_EMIT_AVE: + case PART_EMIT_SIZE: + poin= NULL; break; + case PART_CLUMP: + poin= &(part->clumpfac); break; + case PART_AVE: + poin= &(part->avefac); break; + case PART_SIZE: + poin= &(part->size); break; + case PART_DRAG: + poin= &(part->dragfac); break; + case PART_BROWN: + poin= &(part->brownfac); break; + case PART_DAMP: + poin= &(part->dampfac); break; + case PART_LENGTH: + poin= &(part->length); break; + case PART_GRAV_X: + poin= &(part->acc[0]); break; + case PART_GRAV_Y: + poin= &(part->acc[1]); break; + case PART_GRAV_Z: + poin= &(part->acc[2]); break; + case PART_KINK_AMP: + poin= &(part->kink_amp); break; + case PART_KINK_FREQ: + poin= &(part->kink_freq); break; + case PART_KINK_SHAPE: + poin= &(part->kink_shape); break; + case PART_BB_TILT: + poin= &(part->bb_tilt); break; + } + } + return poin; } void set_icu_vars(IpoCurve *icu) { - + /* defaults. 0.0 for y-extents makes these ignored */ icu->ymin= icu->ymax= 0.0; icu->ipo= IPO_BEZ; @@ -1809,6 +1907,37 @@ void set_icu_vars(IpoCurve *icu) break; } } + else if(icu->blocktype==ID_PA){ + + switch(icu->adrcode) { + case PART_EMIT_LIFE: + case PART_SIZE: + case PART_KINK_FREQ: + case PART_EMIT_VEL: + case PART_EMIT_AVE: + case PART_EMIT_SIZE: + icu->ymin= 0.0; + break; + case PART_CLUMP: + case PART_DRAG: + case PART_DAMP: + case PART_LENGTH: + icu->ymin= 0.0; + icu->ymax= 1.0; + break; + case PART_KINK_SHAPE: + icu->ymin= -0.999; + icu->ymax= 0.999; + } + } + else if(icu->blocktype==ID_CO) { + icu->ymin= 0.0; + icu->ymax= 1.0f; + } + + /* by default, slider limits will be icu->ymin and icu->ymax */ + icu->slide_min= icu->ymin; + icu->slide_max= icu->ymax; } /* not for actions or constraints! */ @@ -1997,7 +2126,7 @@ void do_ob_ipo(Object *ob) /* do not set ob->ctime here: for example when parent in invisible layer */ - ctime= bsystem_time(ob, 0, (float) G.scene->r.cfra, 0.0); + ctime= bsystem_time(ob, (float) G.scene->r.cfra, 0.0); calc_ipo(ob->ipo, ctime); @@ -2271,6 +2400,7 @@ void make_cfra_list(Ipo *ipo, ListBase *elems) case OB_PD_SDAMP: case OB_PD_RDAMP: case OB_PD_PERM: + case OB_PD_FMAXD: bezt= icu->bezt; if(bezt) { a= icu->totvert; diff --git a/source/blender/blenkernel/intern/key.c b/source/blender/blenkernel/intern/key.c index 345bc522449..234a096edce 100644 --- a/source/blender/blenkernel/intern/key.c +++ b/source/blender/blenkernel/intern/key.c @@ -680,11 +680,15 @@ static void do_rel_key(int start, int end, int tot, char *basispoin, Key *key, i /* only with value, and no difference allowed */ if(icuval!=0.0f && kb->totelem==tot) { + KeyBlock *refb; float weight, *weights= kb->weights; poin= basispoin; - reffrom= key->refkey->data; from= kb->data; + /* reference now can be any block */ + refb= BLI_findlink(&key->block, kb->relative); + if(refb==NULL) continue; + reffrom= refb->data; poin+= start*ofs[0]; reffrom+= key->elemsize*start; // key elemsize yes! @@ -1025,7 +1029,7 @@ static int do_mesh_key(Object *ob, Mesh *me) for(a=0; a<me->totvert; a+=step, cfra+= delta) { - ctime= bsystem_time(0, 0, cfra, 0.0); + ctime= bsystem_time(0, cfra, 0.0); if(calc_ipo_spec(me->key->ipo, KEY_SPEED, &ctime)==0) { ctime /= 100.0; CLAMP(ctime, 0.0, 1.0); @@ -1060,7 +1064,7 @@ static int do_mesh_key(Object *ob, Mesh *me) } } else { - ctime= bsystem_time(ob, 0, G.scene->r.cfra, 0.0); + ctime= bsystem_time(ob, G.scene->r.cfra, 0.0); if(calc_ipo_spec(me->key->ipo, KEY_SPEED, &ctime)==0) { ctime /= 100.0; @@ -1180,7 +1184,7 @@ static int do_curve_key(Curve *cu) for(a=0; a<tot; a+=step, cfra+= delta) { - ctime= bsystem_time(0, 0, cfra, 0.0); + ctime= bsystem_time(0, cfra, 0.0); if(calc_ipo_spec(cu->key->ipo, KEY_SPEED, &ctime)==0) { ctime /= 100.0; CLAMP(ctime, 0.0, 1.0); @@ -1202,7 +1206,7 @@ static int do_curve_key(Curve *cu) } else { - ctime= bsystem_time(NULL, 0, (float)G.scene->r.cfra, 0.0); + ctime= bsystem_time(NULL, (float)G.scene->r.cfra, 0.0); if(cu->key->type==KEY_RELATIVE) { do_rel_cu_key(cu, ctime); @@ -1244,7 +1248,7 @@ static int do_latt_key(Object *ob, Lattice *lt) for(a=0; a<tot; a++, cfra+= delta) { - ctime= bsystem_time(0, 0, cfra, 0.0); + ctime= bsystem_time(0, cfra, 0.0); if(calc_ipo_spec(lt->key->ipo, KEY_SPEED, &ctime)==0) { ctime /= 100.0; CLAMP(ctime, 0.0, 1.0); @@ -1261,7 +1265,7 @@ static int do_latt_key(Object *ob, Lattice *lt) } } else { - ctime= bsystem_time(NULL, 0, (float)G.scene->r.cfra, 0.0); + ctime= bsystem_time(NULL, (float)G.scene->r.cfra, 0.0); if(lt->key->type==KEY_RELATIVE) { KeyBlock *kb; @@ -1342,7 +1346,7 @@ int do_ob_key(Object *ob) if(ob->ipoflag & OB_ACTION_KEY) do_all_object_actions(ob); else { - calc_ipo(key->ipo, bsystem_time(ob, 0, G.scene->r.cfra, 0.0)); + calc_ipo(key->ipo, bsystem_time(ob, G.scene->r.cfra, 0.0)); execute_ipo((ID *)key, key->ipo); } @@ -1386,3 +1390,23 @@ KeyBlock *ob_get_keyblock(Object *ob) return NULL; } + +/* get the appropriate KeyBlock given an index */ +KeyBlock *key_get_keyblock(Key *key, int index) +{ + KeyBlock *kb; + int i; + + if (key) { + kb= key->block.first; + + for (i= 1; i < key->totkey; i++) { + kb= kb->next; + + if (index==i) + return kb; + } + } + + return NULL; +} diff --git a/source/blender/blenkernel/intern/library.c b/source/blender/blenkernel/intern/library.c index 0163cced795..e81d3bac655 100644 --- a/source/blender/blenkernel/intern/library.c +++ b/source/blender/blenkernel/intern/library.c @@ -78,6 +78,8 @@ #include "DNA_nla_types.h" #include "DNA_effect_types.h" #include "DNA_brush_types.h" +#include "DNA_particle_types.h" +#include "BKE_particle.h" #include "BLI_blenlib.h" #include "BLI_dynstr.h" @@ -194,6 +196,8 @@ ListBase *wich_libbase(Main *mainlib, short type) return &(mainlib->nodetree); case ID_BR: return &(mainlib->brush); + case ID_PA: + return &(mainlib->particle); } return 0; } @@ -254,16 +258,17 @@ int set_listbasepointers(Main *main, ListBase **lb) lb[18]= &(main->nodetree); lb[19]= &(main->brush); lb[20]= &(main->script); + lb[21]= &(main->particle); - lb[21]= &(main->world); - lb[22]= &(main->screen); - lb[23]= &(main->object); - lb[24]= &(main->scene); - lb[25]= &(main->library); + lb[22]= &(main->world); + lb[23]= &(main->screen); + lb[24]= &(main->object); + lb[25]= &(main->scene); + lb[26]= &(main->library); - lb[26]= NULL; + lb[27]= NULL; - return 26; + return 27; } /* *********** ALLOC AND FREE ***************** @@ -359,6 +364,9 @@ static ID *alloc_libblock_notest(short type) case ID_BR: id = MEM_callocN(sizeof(Brush), "brush"); break; + case ID_PA: + id = MEM_callocN(sizeof(ParticleSettings), "ParticleSettings"); + break; } return id; } @@ -409,7 +417,8 @@ void *copy_libblock(void *rt) id->newid= idn; idn->flag |= LIB_NEW; - + if (id->properties) idn->properties = IDP_CopyProperty(id->properties); + return idn; } @@ -502,6 +511,9 @@ void free_libblock(ListBase *lb, void *idv) case ID_BR: free_brush((Brush *)id); break; + case ID_PA: + psys_free_settings((ParticleSettings *)id); + break; } if (id->properties) { diff --git a/source/blender/blenkernel/intern/material.c b/source/blender/blenkernel/intern/material.c index 56b8307020a..49d3021090e 100644 --- a/source/blender/blenkernel/intern/material.c +++ b/source/blender/blenkernel/intern/material.c @@ -90,6 +90,7 @@ void free_material(Material *ma) if(ma->ramp_col) MEM_freeN(ma->ramp_col); if(ma->ramp_spec) MEM_freeN(ma->ramp_spec); + BKE_previewimg_free(&ma->preview); BKE_icon_delete((struct ID*)ma); ma->id.icon_id = 0; @@ -139,6 +140,12 @@ void init_material(Material *ma) ma->tx_falloff= 1.0; ma->shad_alpha= 1.0f; + ma->gloss_mir = ma->gloss_tra= 1.0; + ma->samp_gloss_mir = ma->samp_gloss_tra= 18; + ma->adapt_thresh_mir = ma->adapt_thresh_tra = 0.005; + ma->dist_mir = 0.0; + ma->fadeto_mir = MA_RAYMIR_FADETOSKY; + ma->rampfac_col= 1.0; ma->rampfac_spec= 1.0; ma->pr_lamp= 3; /* two lamps, is bits */ @@ -159,6 +166,8 @@ void init_material(Material *ma) ma->sss_back= 1.0f; ma->mode= MA_TRACEBLE|MA_SHADBUF|MA_SHADOW|MA_RADIO|MA_RAYBIAS|MA_TANGENT_STR; + + ma->preview = NULL; } Material *add_material(char *name) @@ -196,6 +205,8 @@ Material *copy_material(Material *ma) if(ma->ramp_col) man->ramp_col= MEM_dupallocN(ma->ramp_col); if(ma->ramp_spec) man->ramp_spec= MEM_dupallocN(ma->ramp_spec); + if (ma->preview) man->preview = BKE_previewimg_copy(ma->preview); + if(ma->nodetree) { man->nodetree= ntreeCopyTree(ma->nodetree, 0); /* 0 == full new tree */ } @@ -746,9 +757,9 @@ int material_in_material(Material *parmat, Material *mat) /* ****************** */ char colname_array[125][20]= { -"Black","DarkRed","HalveRed","Red","Red", +"Black","DarkRed","HalfRed","Red","Red", "DarkGreen","DarkOlive","Brown","Chocolate","OrangeRed", -"HalveGreen","GreenOlive","DryOlive","Goldenrod","DarkOrange", +"HalfGreen","GreenOlive","DryOlive","Goldenrod","DarkOrange", "LightGreen","Chartreuse","YellowGreen","Yellow","Gold", "Green","LawnGreen","GreenYellow","LightOlive","Yellow", "DarkBlue","DarkPurple","HotPink","VioletPink","RedPink", @@ -756,7 +767,7 @@ char colname_array[125][20]= { "SeaGreen","PaleGreen","GreenKhaki","LightBrown","LightSalmon", "SpringGreen","PaleGreen","MediumOlive","YellowBrown","LightGold", "LightGreen","LightGreen","LightGreen","GreenYellow","PaleYellow", -"HalveBlue","DarkSky","HalveMagenta","VioletRed","DeepPink", +"HalfBlue","DarkSky","HalfMagenta","VioletRed","DeepPink", "SteelBlue","SkyBlue","Orchid","LightHotPink","HotPink", "SeaGreen","SlateGray","MediumGrey","Burlywood","LightPink", "SpringGreen","Aquamarine","PaleGreen","Khaki","PaleOrange", diff --git a/source/blender/blenkernel/intern/mesh.c b/source/blender/blenkernel/intern/mesh.c index 4c6bfda1517..4e551e28885 100644 --- a/source/blender/blenkernel/intern/mesh.c +++ b/source/blender/blenkernel/intern/mesh.c @@ -486,16 +486,7 @@ static float *make_orco_mesh_internal(Object *ob, int render) /* Get appropriate vertex coordinates */ if(me->key && me->texcomesh==0 && me->key->refkey) { - KeyBlock *kb= me->key->refkey; - float *fp= kb->data; - totvert= MIN2(kb->totelem, me->totvert); - vcos = MEM_callocN(sizeof(*vcos)*me->totvert, "orco mesh"); - - for(a=0; a<totvert; a++, fp+=3) { - vcos[a][0]= fp[0]; - vcos[a][1]= fp[1]; - vcos[a][2]= fp[2]; - } + vcos= mesh_getRefKeyCos(me, &totvert); } else { MultiresLevel *lvl = NULL; @@ -933,8 +924,8 @@ void nurbs_to_mesh(Object *ob) index= dl->index; while(a--) { mface->v1= startvert+index[0]; - mface->v2= startvert+index[1]; - mface->v3= startvert+index[2]; + mface->v2= startvert+index[2]; + mface->v3= startvert+index[1]; mface->v4= 0; test_index_face(mface, NULL, 0, 3); @@ -1120,9 +1111,8 @@ float (*mesh_getVertexCos(Mesh *me, int *numVerts_r))[3] float (*cos)[3] = MEM_mallocN(sizeof(*cos)*numVerts, "vertexcos1"); if (numVerts_r) *numVerts_r = numVerts; - for (i=0; i<numVerts; i++) { + for (i=0; i<numVerts; i++) VECCOPY(cos[i], me->mvert[i].co); - } return cos; #ifdef WITH_VERSE @@ -1130,12 +1120,24 @@ float (*mesh_getVertexCos(Mesh *me, int *numVerts_r))[3] #endif } -/* UvVertMap */ +float (*mesh_getRefKeyCos(Mesh *me, int *numVerts_r))[3] +{ + KeyBlock *kb; + float (*cos)[3] = NULL; + int totvert; + + if(me->key && me->key->refkey) { + if(numVerts_r) *numVerts_r= me->totvert; + cos= MEM_mallocN(sizeof(*cos)*me->totvert, "vertexcos1"); -struct UvVertMap { - struct UvMapVert **vert; - struct UvMapVert *buf; -}; + kb= me->key->refkey; + totvert= MIN2(kb->totelem, me->totvert); + + memcpy(cos, kb->data, sizeof(*cos)*totvert); + } + + return cos; +} UvVertMap *make_uv_vert_map(struct MFace *mface, struct MTFace *tface, unsigned int totface, unsigned int totvert, int selected, float *limit) { diff --git a/source/blender/blenkernel/intern/modifier.c b/source/blender/blenkernel/intern/modifier.c index 707f050d56e..9336dde0151 100644 --- a/source/blender/blenkernel/intern/modifier.c +++ b/source/blender/blenkernel/intern/modifier.c @@ -41,12 +41,14 @@ #include "math.h" #include "float.h" -#include "BLI_blenlib.h" -#include "BLI_rand.h" #include "BLI_arithb.h" +#include "BLI_blenlib.h" +#include "BLI_kdtree.h" #include "BLI_linklist.h" +#include "BLI_rand.h" #include "BLI_edgehash.h" #include "BLI_ghash.h" +#include "BLI_memarena.h" #include "MEM_guardedalloc.h" @@ -58,6 +60,7 @@ #include "DNA_modifier_types.h" #include "DNA_object_types.h" #include "DNA_object_force.h" +#include "DNA_particle_types.h" #include "DNA_scene_types.h" #include "DNA_texture_types.h" #include "DNA_curve_types.h" @@ -71,20 +74,24 @@ #include "BKE_main.h" #include "BKE_anim.h" #include "BKE_bad_level_calls.h" +#include "BKE_curve.h" #include "BKE_customdata.h" #include "BKE_global.h" -#include "BKE_utildefines.h" #include "BKE_cdderivedmesh.h" #include "BKE_DerivedMesh.h" #include "BKE_booleanops.h" #include "BKE_displist.h" #include "BKE_modifier.h" #include "BKE_lattice.h" +#include "BKE_library.h" #include "BKE_subsurf.h" #include "BKE_object.h" #include "BKE_mesh.h" #include "BKE_softbody.h" #include "BKE_material.h" +#include "BKE_particle.h" +#include "BKE_pointcache.h" +#include "BKE_utildefines.h" #include "depsgraph_private.h" #include "LOD_DependKludge.h" @@ -237,12 +244,29 @@ static void latticeModifier_updateDepgraph(ModifierData *md, DagForest *forest, } } +static void modifier_vgroup_cache(ModifierData *md, float (*vertexCos)[3]) +{ + md= md->next; + if(md) { + if(md->type==eModifierType_Armature) { + ArmatureModifierData *amd = (ArmatureModifierData*) md; + if(amd->multi) + amd->prevCos= MEM_dupallocN(vertexCos); + } + /* lattice/mesh modifier too */ + } +} + + static void latticeModifier_deformVerts( ModifierData *md, Object *ob, DerivedMesh *derivedData, float (*vertexCos)[3], int numVerts) { LatticeModifierData *lmd = (LatticeModifierData*) md; + + modifier_vgroup_cache(md, vertexCos); /* if next modifier needs original vertices */ + lattice_deform_verts(lmd->object, ob, derivedData, vertexCos, numVerts, lmd->name); } @@ -383,7 +407,7 @@ static DerivedMesh *buildModifier_applyModifier(ModifierData *md, Object *ob, for(i = 0; i < maxFaces; ++i) faceMap[i] = i; if (ob) { - frac = bsystem_time(ob, 0, (float)G.scene->r.cfra, + frac = bsystem_time(ob, (float)G.scene->r.cfra, bmd->start - 1.0f) / bmd->length; } else { frac = G.scene->r.cfra - bmd->start / bmd->length; @@ -1229,6 +1253,7 @@ static void mirrorModifier_initData(ModifierData *md) mmd->flag |= MOD_MIR_AXIS_X; mmd->tolerance = 0.001; + mmd->mirror_ob = NULL; } static void mirrorModifier_copyData(ModifierData *md, ModifierData *target) @@ -1239,12 +1264,37 @@ static void mirrorModifier_copyData(ModifierData *md, ModifierData *target) tmmd->axis = mmd->axis; tmmd->flag = mmd->flag; tmmd->tolerance = mmd->tolerance; + tmmd->mirror_ob = mmd->mirror_ob;; +} + +static void mirrorModifier_foreachObjectLink( + ModifierData *md, Object *ob, + void (*walk)(void *userData, Object *ob, Object **obpoin), + void *userData) +{ + MirrorModifierData *mmd = (MirrorModifierData*) md; + + walk(userData, ob, &mmd->mirror_ob); +} + +static void mirrorModifier_updateDepgraph(ModifierData *md, DagForest *forest, + Object *ob, DagNode *obNode) +{ + MirrorModifierData *mmd = (MirrorModifierData*) md; + + if(mmd->mirror_ob) { + DagNode *latNode = dag_get_node(forest, mmd->mirror_ob); + + dag_add_relation(forest, latNode, obNode, + DAG_RL_DATA_DATA | DAG_RL_OB_DATA); + } } static DerivedMesh *doMirrorOnAxis(MirrorModifierData *mmd, - DerivedMesh *dm, - int initFlags, - int axis) + Object *ob, + DerivedMesh *dm, + int initFlags, + int axis) { int i; float tolerance = mmd->tolerance; @@ -1254,6 +1304,7 @@ static DerivedMesh *doMirrorOnAxis(MirrorModifierData *mmd, int maxEdges = dm->getNumEdges(dm); int maxFaces = dm->getNumFaces(dm); int (*indexMap)[2]; + float mtx[4][4], imtx[4][4]; numVerts = numEdges = numFaces = 0; @@ -1261,13 +1312,28 @@ static DerivedMesh *doMirrorOnAxis(MirrorModifierData *mmd, result = CDDM_from_template(dm, maxVerts * 2, maxEdges * 2, maxFaces * 2); + if (mmd->mirror_ob) { + float obinv[4][4]; + + Mat4Invert(obinv, mmd->mirror_ob->obmat); + Mat4MulMat4(mtx, ob->obmat, obinv); + Mat4Invert(imtx, mtx); + } + for(i = 0; i < maxVerts; i++) { MVert inMV; MVert *mv = CDDM_get_vert(result, numVerts); int isShared; + float co[3]; dm->getVert(dm, i, &inMV); - isShared = ABS(inMV.co[axis])<=tolerance; + + VecCopyf(co, inMV.co); + + if (mmd->mirror_ob) { + VecMat4MulVecfl(co, mtx, co); + } + isShared = ABS(co[axis])<=tolerance; /* Because the topology result (# of vertices) must be the same if * the mesh data is overridden by vertex cos, have to calc sharedness @@ -1281,7 +1347,12 @@ static DerivedMesh *doMirrorOnAxis(MirrorModifierData *mmd, indexMap[i][1] = !isShared; if(isShared) { - mv->co[axis] = 0; + co[axis] = 0; + if (mmd->mirror_ob) { + VecMat4MulVecfl(co, imtx, co); + } + VecCopyf(mv->co, co); + mv->flag |= ME_VERT_MERGED; } else { MVert *mv2 = CDDM_get_vert(result, numVerts); @@ -1290,7 +1361,11 @@ static DerivedMesh *doMirrorOnAxis(MirrorModifierData *mmd, *mv2 = *mv; numVerts++; - mv2->co[axis] = -mv2->co[axis]; + co[axis] = -co[axis]; + if (mmd->mirror_ob) { + VecMat4MulVecfl(co, imtx, co); + } + VecCopyf(mv2->co, co); } } @@ -1384,23 +1459,23 @@ static DerivedMesh *doMirrorOnAxis(MirrorModifierData *mmd, } static DerivedMesh *mirrorModifier__doMirror(MirrorModifierData *mmd, - DerivedMesh *dm, + Object *ob, DerivedMesh *dm, int initFlags) { DerivedMesh *result = dm; /* check which axes have been toggled and mirror accordingly */ if(mmd->flag & MOD_MIR_AXIS_X) { - result = doMirrorOnAxis(mmd, result, initFlags, 0); + result = doMirrorOnAxis(mmd, ob, result, initFlags, 0); } if(mmd->flag & MOD_MIR_AXIS_Y) { DerivedMesh *tmp = result; - result = doMirrorOnAxis(mmd, result, initFlags, 1); + result = doMirrorOnAxis(mmd, ob, result, initFlags, 1); if(tmp != dm) tmp->release(tmp); /* free intermediate results */ } if(mmd->flag & MOD_MIR_AXIS_Z) { DerivedMesh *tmp = result; - result = doMirrorOnAxis(mmd, result, initFlags, 2); + result = doMirrorOnAxis(mmd, ob, result, initFlags, 2); if(tmp != dm) tmp->release(tmp); /* free intermediate results */ } @@ -1414,7 +1489,7 @@ static DerivedMesh *mirrorModifier_applyModifier( DerivedMesh *result; MirrorModifierData *mmd = (MirrorModifierData*) md; - result = mirrorModifier__doMirror(mmd, derivedData, 0); + result = mirrorModifier__doMirror(mmd, ob, derivedData, 0); CDDM_calc_normals(result); @@ -2453,7 +2528,42 @@ static void split_sharp_edges(SmoothMesh *mesh, float split_angle, int flags) } -static void split_single_verts(SmoothMesh *mesh) +static int count_bridge_verts(SmoothMesh *mesh) +{ + int i, j, count = 0; + + for(i = 0; i < mesh->num_faces; i++) { + SmoothFace *face = &mesh->faces[i]; + + for(j = 0; j < SMOOTHFACE_MAX_EDGES && face->edges[j]; j++) { + SmoothEdge *edge = face->edges[j]; + SmoothEdge *next_edge; + SmoothVert *vert = edge->verts[1 - face->flip[j]]; + int next = (j + 1) % SMOOTHFACE_MAX_EDGES; + + /* wrap next around if at last edge */ + if(!face->edges[next]) next = 0; + + next_edge = face->edges[next]; + + /* if there are other faces sharing this vertex but not + * these edges, the vertex will be split, so count it + */ + /* vert has to have at least one face (this one), so faces != 0 */ + if(!edge->faces->next && !next_edge->faces->next + && vert->faces->next) { + count++; + } + } + } + + /* each bridge vert will be counted once per face that uses it, + * so count is too high, but it's ok for now + */ + return count; +} + +static void split_bridge_verts(SmoothMesh *mesh) { int i,j; @@ -2501,6 +2611,7 @@ static DerivedMesh *edgesplitModifier_do(EdgeSplitModifierData *emd, /* 2. count max number of elements to add */ tag_and_count_extra_edges(mesh, emd->split_angle, emd->flags, &max_edges); max_verts = max_edges * 2 + mesh->max_verts; + max_verts += count_bridge_verts(mesh); max_edges += mesh->max_edges; /* 3. reallocate smoothmesh arrays & copy elements across */ @@ -2518,9 +2629,8 @@ static DerivedMesh *edgesplitModifier_do(EdgeSplitModifierData *emd, printf("********** Post-edge-split **********\n"); smoothmesh_print(mesh); #endif -#if 1 - split_single_verts(mesh); -#endif + + split_bridge_verts(mesh); #ifdef EDGESPLIT_DEBUG_1 printf("********** Post-vert-split **********\n"); @@ -2577,7 +2687,14 @@ static void displaceModifier_copyData(ModifierData *md, ModifierData *target) DisplaceModifierData *dmd = (DisplaceModifierData*) md; DisplaceModifierData *tdmd = (DisplaceModifierData*) target; - *tdmd = *dmd; + tdmd->texture = dmd->texture; + tdmd->strength = dmd->strength; + tdmd->direction = dmd->direction; + strncpy(tdmd->defgrp_name, dmd->defgrp_name, 32); + tdmd->midlevel = dmd->midlevel; + tdmd->texmapping = dmd->texmapping; + tdmd->map_object = dmd->map_object; + strncpy(tdmd->uvlayer_name, dmd->uvlayer_name, 32); } CustomDataMask displaceModifier_requiredDataMask(ModifierData *md) @@ -4323,11 +4440,11 @@ static void waveModifier_do( MVert *mvert = NULL; MDeformVert *dvert = NULL; int defgrp_index; - float ctime = bsystem_time(ob, 0, (float)G.scene->r.cfra, 0.0); + float ctime = bsystem_time(ob, (float)G.scene->r.cfra, 0.0); float minfac = (float)(1.0 / exp(wmd->width * wmd->narrow * wmd->width * wmd->narrow)); float lifefac = wmd->height; - float (*tex_co)[3]; + float (*tex_co)[3] = NULL; if(wmd->flag & MOD_WAVE_NORM && ob->type == OB_MESH) mvert = dm->getVertArray(dm); @@ -4570,8 +4687,16 @@ static void armatureModifier_deformVerts( { ArmatureModifierData *amd = (ArmatureModifierData*) md; - armature_deform_verts(amd->object, ob, derivedData, vertexCos, numVerts, - amd->deformflag, amd->defgrp_name); + modifier_vgroup_cache(md, vertexCos); /* if next modifier needs original vertices */ + + armature_deform_verts(amd->object, ob, derivedData, vertexCos, NULL, + numVerts, amd->deformflag, + (float(*)[3])amd->prevCos, amd->defgrp_name); + /* free cache */ + if(amd->prevCos) { + MEM_freeN(amd->prevCos); + amd->prevCos= NULL; + } } static void armatureModifier_deformVertsEM( @@ -4583,8 +4708,24 @@ static void armatureModifier_deformVertsEM( if(!derivedData) dm = CDDM_from_editmesh(editData, ob->data); - armature_deform_verts(amd->object, ob, dm, vertexCos, numVerts, - amd->deformflag, amd->defgrp_name); + armature_deform_verts(amd->object, ob, dm, vertexCos, NULL, numVerts, + amd->deformflag, NULL, amd->defgrp_name); + + if(!derivedData) dm->release(dm); +} + +static void armatureModifier_deformMatricesEM( + ModifierData *md, Object *ob, EditMesh *editData, + DerivedMesh *derivedData, float (*vertexCos)[3], + float (*defMats)[3][3], int numVerts) +{ + ArmatureModifierData *amd = (ArmatureModifierData*) md; + DerivedMesh *dm = derivedData; + + if(!derivedData) dm = CDDM_from_editmesh(editData, ob->data); + + armature_deform_verts(amd->object, ob, dm, vertexCos, defMats, numVerts, + amd->deformflag, NULL, amd->defgrp_name); if(!derivedData) dm->release(dm); } @@ -4849,7 +4990,7 @@ static DerivedMesh *booleanModifier_applyModifier( /* we do a quick sanity check */ if(((Mesh *)ob->data)->totface > 3 && bmd->object && ((Mesh *)bmd->object->data)->totface > 3) { - DerivedMesh *result = NewBooleanDerivedMesh(ob, bmd->object, + DerivedMesh *result = NewBooleanDerivedMesh(bmd->object, ob, 1 + bmd->operation); /* if new mesh returned, return it; otherwise there was @@ -4863,6 +5004,1513 @@ static DerivedMesh *booleanModifier_applyModifier( return derivedData; } +/* Particles */ +static void particleSystemModifier_initData(ModifierData *md) +{ + ParticleSystemModifierData *psmd= (ParticleSystemModifierData*) md; + psmd->psys= 0; + psmd->dm=0; + +} +static void particleSystemModifier_freeData(ModifierData *md) +{ + ParticleSystemModifierData *psmd= (ParticleSystemModifierData*) md; + + if(psmd->dm){ + psmd->dm->needsFree = 1; + psmd->dm->release(psmd->dm); + psmd->dm=0; + } + + psmd->psys->flag &= ~PSYS_ENABLED; + psmd->psys->flag |= PSYS_DELETE; +} +static void particleSystemModifier_copyData(ModifierData *md, ModifierData *target) +{ + ParticleSystemModifierData *psmd= (ParticleSystemModifierData*) md; + ParticleSystemModifierData *tpsmd= (ParticleSystemModifierData*) target; + + tpsmd->dm = 0; + //tpsmd->facepa = 0; + tpsmd->flag = psmd->flag; + /* need to keep this to recognise a bit later in copy_object */ + tpsmd->psys = psmd->psys; +} + +CustomDataMask particleSystemModifier_requiredDataMask(ModifierData *md) +{ + ParticleSystemModifierData *psmd= (ParticleSystemModifierData*) md; + CustomDataMask dataMask = (1 << CD_MTFACE) + (1 << CD_MEDGE); + int i; + + /* ask for vertexgroups if we need them */ + for(i=0; i<PSYS_TOT_VG; i++){ + if(psmd->psys->vgroup[i]){ + dataMask |= (1 << CD_MDEFORMVERT); + break; + } + } + + /* particles only need this if they are after a non deform modifier, and + * the modifier stack will only create them in that case. */ + dataMask |= CD_MASK_ORIGSPACE; + + return dataMask; +} +static int is_last_displist(Object *ob) +{ + Curve *cu = ob->data; + static int curvecount=0, totcurve=0; + + if(curvecount==0){ + DispList *dl; + + totcurve=0; + for(dl=cu->disp.first; dl; dl=dl->next){ + totcurve++; + } + } + + curvecount++; + + if(curvecount==totcurve){ + curvecount=0; + return 1; + } + + return 0; +} +/* saves the current emitter state for a particle system and calculates particles */ +static void particleSystemModifier_deformVerts( + ModifierData *md, Object *ob, DerivedMesh *derivedData, + float (*vertexCos)[3], int numVerts) +{ + DerivedMesh *dm = derivedData; + ParticleSystemModifierData *psmd= (ParticleSystemModifierData*) md; + ParticleSystem * psys=0; + int totvert=0,totedge=0,totface=0,needsFree=0; + + if(ob->particlesystem.first) + psys=psmd->psys; + else + return; + + if((psys->flag&PSYS_ENABLED)==0) + return; + + if(dm==0){ + if(ob->type==OB_MESH){ + dm = CDDM_from_mesh((Mesh*)(ob->data), ob); + + CDDM_apply_vert_coords(dm, vertexCos); + //CDDM_calc_normals(dm); + + needsFree=1; + } + else if(ELEM3(ob->type,OB_FONT,OB_CURVE,OB_SURF)){ + Object *tmpobj; + Curve *tmpcu; + + if(is_last_displist(ob)){ + /* copies object and modifiers (but not the data) */ + tmpobj= copy_object( ob ); + tmpcu = (Curve *)tmpobj->data; + tmpcu->id.us--; + + /* copies the data */ + tmpobj->data = copy_curve( (Curve *) ob->data ); + + makeDispListCurveTypes( tmpobj, 1 ); + nurbs_to_mesh( tmpobj ); + + dm = CDDM_from_mesh((Mesh*)(tmpobj->data), tmpobj); + //CDDM_calc_normals(dm); + + free_libblock_us( &G.main->object, tmpobj ); + + needsFree=1; + } + else return; + } + else return; + } + + /* clear old dm */ + if(psmd->dm){ + totvert=psmd->dm->getNumVerts(psmd->dm); + totedge=psmd->dm->getNumEdges(psmd->dm); + totface=psmd->dm->getNumFaces(psmd->dm); + psmd->dm->needsFree = 1; + psmd->dm->release(psmd->dm); + } + + /* make new dm */ + psmd->dm=CDDM_copy(dm); + CDDM_calc_normals(psmd->dm); + + if(needsFree){ + dm->needsFree = 1; + dm->release(dm); + } + + /* protect dm */ + psmd->dm->needsFree = 0; + + /* report change in mesh structure */ + if(psmd->dm->getNumVerts(psmd->dm)!=totvert || + psmd->dm->getNumEdges(psmd->dm)!=totedge || + psmd->dm->getNumFaces(psmd->dm)!=totface){ + /* in file read dm hasn't really changed but just wasn't saved in file */ + if(psmd->flag & eParticleSystemFlag_Loaded) + psmd->flag &= ~eParticleSystemFlag_Loaded; + else{ + /* TODO PARTICLE - Added this so changing subsurf under hair updates it + should it be done elsewhere? - Campbell */ + psys->recalc |= PSYS_RECALC_HAIR; + psys->recalc |= PSYS_DISTR; + psmd->flag |= eParticleSystemFlag_DM_changed; + } + } + + if(psys){ + particle_system_update(ob,psys); + psmd->flag |= eParticleSystemFlag_psys_updated; + psmd->flag &= ~eParticleSystemFlag_DM_changed; + } +} + +static void particleSystemModifier_deformVertsEM( + ModifierData *md, Object *ob, EditMesh *editData, + DerivedMesh *derivedData, float (*vertexCos)[3], int numVerts) +{ + DerivedMesh *dm = derivedData; + + if(!derivedData) dm = CDDM_from_editmesh(editData, ob->data); + + particleSystemModifier_deformVerts(md, ob, dm, vertexCos, numVerts); + + if(!derivedData) dm->release(dm); +} + +/* Particle Instance */ +static void particleInstanceModifier_initData(ModifierData *md) +{ + ParticleInstanceModifierData *pimd= (ParticleInstanceModifierData*) md; + + pimd->flag = eParticleInstanceFlag_Parents|eParticleInstanceFlag_Unborn| + eParticleInstanceFlag_Alive|eParticleInstanceFlag_Dead; + pimd->psys = 1; + +} +static void particleInstanceModifier_copyData(ModifierData *md, ModifierData *target) +{ + ParticleInstanceModifierData *pimd= (ParticleInstanceModifierData*) md; + ParticleInstanceModifierData *tpimd= (ParticleInstanceModifierData*) target; + + tpimd->ob = pimd->ob; + tpimd->psys = pimd->psys; + tpimd->flag = pimd->flag; +} + +static int particleInstanceModifier_dependsOnTime(ModifierData *md) +{ + return 0; +} +static void particleInstanceModifier_updateDepgraph(ModifierData *md, DagForest *forest, + Object *ob, DagNode *obNode) +{ + ParticleInstanceModifierData *pimd = (ParticleInstanceModifierData*) md; + + if (pimd->ob) { + DagNode *curNode = dag_get_node(forest, pimd->ob); + + dag_add_relation(forest, curNode, obNode, + DAG_RL_DATA_DATA | DAG_RL_OB_DATA); + } +} + +static void particleInstanceModifier_foreachObjectLink(ModifierData *md, Object *ob, + ObjectWalkFunc walk, void *userData) +{ + ParticleInstanceModifierData *pimd = (ParticleInstanceModifierData*) md; + + walk(userData, ob, &pimd->ob); +} + +static DerivedMesh * particleInstanceModifier_applyModifier( + ModifierData *md, Object *ob, DerivedMesh *derivedData, + int useRenderParams, int isFinalCalc) +{ + DerivedMesh *dm = derivedData, *result; + ParticleInstanceModifierData *pimd= (ParticleInstanceModifierData*) md; + ParticleSystem * psys=0; + ParticleData *pa=0, *pars=0; + MFace *mface, *orig_mface; + MVert *mvert, *orig_mvert; + int i,totvert, totpart=0, totface, maxvert, maxface, first_particle=0; + short track=ob->trackflag%3, trackneg; + float max_co=0.0, min_co=0.0, temp_co[3]; + + trackneg=((ob->trackflag>2)?1:0); + + if(pimd->ob==ob){ + pimd->ob=0; + return derivedData; + } + + if(pimd->ob){ + psys = BLI_findlink(&pimd->ob->particlesystem,pimd->psys-1); + if(psys==0 || psys->totpart==0) + return derivedData; + } + else return derivedData; + + if(pimd->flag & eParticleInstanceFlag_Parents) + totpart+=psys->totpart; + if(pimd->flag & eParticleInstanceFlag_Children){ + if(totpart==0) + first_particle=psys->totpart; + totpart+=psys->totchild; + } + + if(totpart==0) + return derivedData; + + pars=psys->particles; + + totvert=dm->getNumVerts(dm); + totface=dm->getNumFaces(dm); + + maxvert=totvert*totpart; + maxface=totface*totpart; + + psys->lattice=psys_get_lattice(ob, psys); + + if(psys->flag & (PSYS_HAIR_DONE|PSYS_KEYED)){ + float co[3]; + for(i=0; i< totvert; i++){ + dm->getVertCo(dm,i,co); + if(i==0){ + min_co=max_co=co[track]; + } + else{ + if(co[track]<min_co) + min_co=co[track]; + + if(co[track]>max_co) + max_co=co[track]; + } + } + } + + result = CDDM_from_template(dm, maxvert,dm->getNumEdges(dm)*totpart,maxface); + + mvert=result->getVertArray(result); + orig_mvert=dm->getVertArray(dm); + + for(i=0; i<maxvert; i++){ + MVert *inMV; + MVert *mv = mvert + i; + ParticleKey state; + + inMV = orig_mvert + i%totvert; + DM_copy_vert_data(dm, result, i%totvert, i, 1); + *mv = *inMV; + + /*change orientation based on object trackflag*/ + VECCOPY(temp_co,mv->co); + mv->co[0]=temp_co[track]; + mv->co[1]=temp_co[(track+1)%3]; + mv->co[2]=temp_co[(track+2)%3]; + + if(psys->flag & (PSYS_HAIR_DONE|PSYS_KEYED) && pimd->flag & eParticleInstanceFlag_Path){ + state.time=(mv->co[0]-min_co)/(max_co-min_co); + if(trackneg) + state.time=1.0f-state.time; + psys_get_particle_on_path(pimd->ob,psys,first_particle + i/totvert,&state,1); + } + else{ + state.time=-1.0; + psys_get_particle_state(pimd->ob,psys,i/totvert,&state,1); + } + + /*displace vertice to path location*/ + if(pimd->flag & eParticleInstanceFlag_Path) + mv->co[0]=0.0; + + QuatMulVecf(state.rot,mv->co); + VECADD(mv->co,mv->co,state.co); + } + + mface=result->getFaceArray(result); + orig_mface=dm->getFaceArray(dm); + + for(i=0; i<maxface; i++){ + MFace *inMF; + MFace *mf = mface + i; + + if(pimd->flag & eParticleInstanceFlag_Parents){ + if(i/totface>=psys->totpart){ + if(psys->part->childtype==PART_CHILD_PARTICLES) + pa=psys->particles+(psys->child+i/totface-psys->totpart)->parent; + else + pa=0; + } + else + pa=pars+i/totface; + } + else{ + if(psys->part->childtype==PART_CHILD_PARTICLES) + pa=psys->particles+(psys->child+i/totface)->parent; + else + pa=0; + } + + if(pa){ + if(pa->alive==PARS_UNBORN && (pimd->flag&eParticleInstanceFlag_Unborn)==0) continue; + if(pa->alive==PARS_ALIVE && (pimd->flag&eParticleInstanceFlag_Alive)==0) continue; + if(pa->alive==PARS_DEAD && (pimd->flag&eParticleInstanceFlag_Dead)==0) continue; + } + + inMF = orig_mface + i%totface; + DM_copy_face_data(dm, result, i%totface, i, 1); + *mf = *inMF; + + mf->v1+=(i/totface)*totvert; + mf->v2+=(i/totface)*totvert; + mf->v3+=(i/totface)*totvert; + if(mf->v4) + mf->v4+=(i/totface)*totvert; + } + + CDDM_calc_edges(result); + CDDM_calc_normals(result); + + if(psys->lattice){ + end_latt_deform(); + psys->lattice=0; + } + + return result; +} +static DerivedMesh *particleInstanceModifier_applyModifierEM( + ModifierData *md, Object *ob, EditMesh *editData, + DerivedMesh *derivedData) +{ + return particleInstanceModifier_applyModifier(md, ob, derivedData, 0, 1); +} + +/* Explode */ +static void explodeModifier_initData(ModifierData *md) +{ + ExplodeModifierData *emd= (ExplodeModifierData*) md; + + emd->facepa=0; + emd->flag |= eExplodeFlag_Unborn+eExplodeFlag_Alive+eExplodeFlag_Dead; +} +static void explodeModifier_freeData(ModifierData *md) +{ + ExplodeModifierData *emd= (ExplodeModifierData*) md; + + if(emd->facepa) MEM_freeN(emd->facepa); +} +static void explodeModifier_copyData(ModifierData *md, ModifierData *target) +{ + ExplodeModifierData *emd= (ExplodeModifierData*) md; + ExplodeModifierData *temd= (ExplodeModifierData*) target; + + temd->facepa = 0; + temd->flag = emd->flag; +} +static int explodeModifier_dependsOnTime(ModifierData *md) +{ + return 1; +} +CustomDataMask explodeModifier_requiredDataMask(ModifierData *md) +{ + ExplodeModifierData *emd= (ExplodeModifierData*) md; + CustomDataMask dataMask = 0; + + if(emd->vgroup) + dataMask |= (1 << CD_MDEFORMVERT); + + return dataMask; +} + +/* this should really be put somewhere permanently */ +static float vert_weight(MDeformVert *dvert, int group) +{ + MDeformWeight *dw; + int i; + + if(dvert) { + dw= dvert->dw; + for(i= dvert->totweight; i>0; i--, dw++) { + if(dw->def_nr == group) return dw->weight; + if(i==1) break; /*otherwise dw will point to somewhere it shouldn't*/ + } + } + return 0.0; +} + +static void explodeModifier_createFacepa(ExplodeModifierData *emd, + ParticleSystemModifierData *psmd, + Object *ob, DerivedMesh *dm) +{ + ParticleSystem *psys=psmd->psys; + MFace *fa=0, *mface=0; + MVert *mvert = 0; + ParticleData *pa; + KDTree *tree; + float center[3], co[3]; + int *facepa=0,*vertpa=0,totvert=0,totface=0,totpart=0; + int i,p,v1,v2,v3,v4=0; + + mvert = dm->getVertArray(dm); + mface = dm->getFaceArray(dm); + totface= dm->getNumFaces(dm); + totvert= dm->getNumVerts(dm); + totpart= psmd->psys->totpart; + + BLI_srandom(psys->seed); + + if(emd->facepa) + MEM_freeN(emd->facepa); + + facepa = emd->facepa = MEM_callocN(sizeof(int)*totface, "explode_facepa"); + + vertpa = MEM_callocN(sizeof(int)*totvert, "explode_vertpa"); + + /* initialize all faces & verts to no particle */ + for(i=0; i<totface; i++) + facepa[i]=totpart; + + for (i=0; i<totvert; i++) + vertpa[i]=totpart; + + /* set protected verts */ + if(emd->vgroup){ + MDeformVert *dvert = dm->getVertDataArray(dm, CD_MDEFORMVERT); + float val; + if(dvert){ + for(i=0; i<totvert; i++){ + val = BLI_frand(); + val = (1.0f-emd->protect)*val + emd->protect*0.5f; + if(val < vert_weight(dvert+i,emd->vgroup-1)) + vertpa[i] = -1; + } + } + } + + /* make tree of emitter locations */ + tree=BLI_kdtree_new(totpart); + for(p=0,pa=psys->particles; p<totpart; p++,pa++){ + psys_particle_on_dm(ob,dm,psys->part->from,pa->num,pa->num_dmcache,pa->fuv,pa->foffset,co,0,0,0); + BLI_kdtree_insert(tree, p, co, NULL); + } + BLI_kdtree_balance(tree); + + /* set face-particle-indexes to nearest particle to face center */ + for(i=0,fa=mface; i<totface; i++,fa++){ + VecAddf(center,mvert[fa->v1].co,mvert[fa->v2].co); + VecAddf(center,center,mvert[fa->v3].co); + if(fa->v4){ + VecAddf(center,center,mvert[fa->v4].co); + VecMulf(center,0.25); + } + else + VecMulf(center,0.3333f); + + p= BLI_kdtree_find_nearest(tree,center,NULL,NULL); + + v1=vertpa[fa->v1]; + v2=vertpa[fa->v2]; + v3=vertpa[fa->v3]; + if(fa->v4) + v4=vertpa[fa->v4]; + + if(v1>=0 && v2>=0 && v3>=0 && (fa->v4==0 || v4>=0)) + facepa[i]=p; + + if(v1>=0) vertpa[fa->v1]=p; + if(v2>=0) vertpa[fa->v2]=p; + if(v3>=0) vertpa[fa->v3]=p; + if(fa->v4 && v4>=0) vertpa[fa->v4]=p; + } + + if(vertpa) MEM_freeN(vertpa); + BLI_kdtree_free(tree); +} +static DerivedMesh * explodeModifier_splitEdges(ExplodeModifierData *emd, DerivedMesh *dm){ + DerivedMesh *splitdm; + MFace *mf=0,*df1=0,*df2=0,*df3=0; + MFace *mface=CDDM_get_faces(dm); + MVert *dupve, *mv; + int totvert=dm->getNumVerts(dm); + int totface=dm->getNumFaces(dm); + + int *edgesplit = MEM_callocN(sizeof(int)*totvert*totvert,"explode_edgesplit"); + int *facesplit = MEM_callocN(sizeof(int)*totface,"explode_edgesplit"); + int *vertpa = MEM_callocN(sizeof(int)*totvert,"explode_vertpa2"); + int *facepa = emd->facepa; + int *fs, totesplit=0,totfsplit=0,totin=0,curdupvert=0,curdupface=0,curdupin=0; + int i,j,v1,v2,v3,v4; + + /* recreate vertpa from facepa calculation */ + for (i=0,mf=mface; i<totface; i++,mf++) { + vertpa[mf->v1]=facepa[i]; + vertpa[mf->v2]=facepa[i]; + vertpa[mf->v3]=facepa[i]; + if(mf->v4) + vertpa[mf->v4]=facepa[i]; + } + + /* mark edges for splitting and how to split faces */ + for (i=0,mf=mface,fs=facesplit; i<totface; i++,mf++,fs++) { + if(mf->v4){ + v1=vertpa[mf->v1]; + v2=vertpa[mf->v2]; + v3=vertpa[mf->v3]; + v4=vertpa[mf->v4]; + + if(v1!=v2){ + edgesplit[mf->v1*totvert+mf->v2]=edgesplit[mf->v2*totvert+mf->v1]=1; + (*fs)++; + } + + if(v2!=v3){ + edgesplit[mf->v2*totvert+mf->v3]=edgesplit[mf->v3*totvert+mf->v2]=1; + (*fs)++; + } + + if(v3!=v4){ + edgesplit[mf->v3*totvert+mf->v4]=edgesplit[mf->v4*totvert+mf->v3]=1; + (*fs)++; + } + + if(v1!=v4){ + edgesplit[mf->v1*totvert+mf->v4]=edgesplit[mf->v4*totvert+mf->v1]=1; + (*fs)++; + } + + if(*fs==2){ + if((v1==v2 && v3==v4) || (v1==v4 && v2==v3)) + *fs=1; + else if(v1!=v2){ + if(v1!=v4) + edgesplit[mf->v2*totvert+mf->v3]=edgesplit[mf->v3*totvert+mf->v2]=1; + else + edgesplit[mf->v3*totvert+mf->v4]=edgesplit[mf->v4*totvert+mf->v3]=1; + } + else{ + if(v1!=v4) + edgesplit[mf->v1*totvert+mf->v2]=edgesplit[mf->v2*totvert+mf->v1]=1; + else + edgesplit[mf->v1*totvert+mf->v4]=edgesplit[mf->v4*totvert+mf->v1]=1; + } + } + } + } + + /* count splits & reindex */ + totesplit=totvert; + for(j=0; j<totvert; j++){ + for(i=j+1; i<totvert; i++){ + if(edgesplit[j*totvert+i]) + edgesplit[j*totvert+i]=edgesplit[i*totvert+j]=totesplit++; + } + } + /* count new faces due to splitting */ + for(i=0,fs=facesplit; i<totface; i++,fs++){ + if(*fs==1) + totfsplit+=1; + else if(*fs==2) + totfsplit+=2; + else if(*fs==3) + totfsplit+=3; + else if(*fs==4){ + totfsplit+=3; + + mf=dm->getFaceData(dm,i,CD_MFACE);//CDDM_get_face(dm,i); + + if(vertpa[mf->v1]!=vertpa[mf->v2] && vertpa[mf->v2]!=vertpa[mf->v3]) + totin++; + } + } + + splitdm= CDDM_from_template(dm, totesplit+totin, dm->getNumEdges(dm),totface+totfsplit); + + /* copy new faces & verts (is it really this painful with custom data??) */ + for(i=0; i<totvert; i++){ + MVert source; + MVert *dest; + dm->getVert(dm, i, &source); + dest = CDDM_get_vert(splitdm, i); + + DM_copy_vert_data(dm, splitdm, i, i, 1); + *dest = source; + } + for(i=0; i<totface; i++){ + MFace source; + MFace *dest; + dm->getFace(dm, i, &source); + dest = CDDM_get_face(splitdm, i); + + DM_copy_face_data(dm, splitdm, i, i, 1); + *dest = source; + } + + /* override original facepa (original pointer is saved in caller function) */ + facepa= MEM_callocN(sizeof(int)*(totface+totfsplit),"explode_facepa"); + memcpy(facepa,emd->facepa,totface*sizeof(int)); + emd->facepa=facepa; + + /* create new verts */ + curdupvert=totvert; + for(j=0; j<totvert; j++){ + for(i=j+1; i<totvert; i++){ + if(edgesplit[j*totvert+i]){ + mv=CDDM_get_vert(splitdm,j); + dupve=CDDM_get_vert(splitdm,edgesplit[j*totvert+i]); + + DM_copy_vert_data(splitdm,splitdm,j,edgesplit[j*totvert+i],1); + + *dupve=*mv; + + mv=CDDM_get_vert(splitdm,i); + + VECADD(dupve->co,dupve->co,mv->co); + VecMulf(dupve->co,0.5); + } + } + } + + /* create new faces */ + curdupface=totface; + curdupin=totesplit; + for(i=0,fs=facesplit; i<totface; i++,fs++){ + if(*fs){ + mf=CDDM_get_face(splitdm,i); + + v1=vertpa[mf->v1]; + v2=vertpa[mf->v2]; + v3=vertpa[mf->v3]; + v4=vertpa[mf->v4]; + /* ouch! creating new faces & remapping them to new verts is no fun */ + if(*fs==1){ + df1=CDDM_get_face(splitdm,curdupface); + DM_copy_face_data(splitdm,splitdm,i,curdupface,1); + *df1=*mf; + curdupface++; + + if(v1==v2){ + df1->v1=edgesplit[mf->v1*totvert+mf->v4]; + df1->v2=edgesplit[mf->v2*totvert+mf->v3]; + mf->v3=df1->v2; + mf->v4=df1->v1; + } + else{ + df1->v1=edgesplit[mf->v1*totvert+mf->v2]; + df1->v4=edgesplit[mf->v3*totvert+mf->v4]; + mf->v2=df1->v1; + mf->v3=df1->v4; + } + + facepa[i]=v1; + facepa[curdupface-1]=v3; + + test_index_face(df1, &splitdm->faceData, curdupface, (df1->v4 ? 4 : 3)); + } + if(*fs==2){ + df1=CDDM_get_face(splitdm,curdupface); + DM_copy_face_data(splitdm,splitdm,i,curdupface,1); + *df1=*mf; + curdupface++; + + df2=CDDM_get_face(splitdm,curdupface); + DM_copy_face_data(splitdm,splitdm,i,curdupface,1); + *df2=*mf; + curdupface++; + + if(v1!=v2){ + if(v1!=v4){ + df1->v1=edgesplit[mf->v1*totvert+mf->v4]; + df1->v2=edgesplit[mf->v1*totvert+mf->v2]; + df2->v1=df1->v3=mf->v2; + df2->v3=df1->v4=mf->v4; + df2->v2=mf->v3; + + mf->v2=df1->v2; + mf->v3=df1->v1; + + df2->v4=mf->v4=0; + + facepa[i]=v1; + } + else{ + df1->v2=edgesplit[mf->v1*totvert+mf->v2]; + df1->v3=edgesplit[mf->v2*totvert+mf->v3]; + df1->v4=mf->v3; + df2->v2=mf->v3; + df2->v3=mf->v4; + + mf->v1=df1->v2; + mf->v3=df1->v3; + + df2->v4=mf->v4=0; + + facepa[i]=v2; + } + facepa[curdupface-1]=facepa[curdupface-2]=v3; + } + else{ + if(v1!=v4){ + df1->v3=edgesplit[mf->v3*totvert+mf->v4]; + df1->v4=edgesplit[mf->v1*totvert+mf->v4]; + df1->v2=mf->v3; + + mf->v1=df1->v4; + mf->v2=df1->v3; + mf->v3=mf->v4; + + df2->v4=mf->v4=0; + + facepa[i]=v4; + } + else{ + df1->v3=edgesplit[mf->v2*totvert+mf->v3]; + df1->v4=edgesplit[mf->v3*totvert+mf->v4]; + df1->v1=mf->v4; + df1->v2=mf->v2; + df2->v3=mf->v4; + + mf->v1=df1->v4; + mf->v2=df1->v3; + + df2->v4=mf->v4=0; + + facepa[i]=v3; + } + + facepa[curdupface-1]=facepa[curdupface-2]=v1; + } + + test_index_face(df1, &splitdm->faceData, curdupface-2, (df1->v4 ? 4 : 3)); + test_index_face(df1, &splitdm->faceData, curdupface-1, (df1->v4 ? 4 : 3)); + } + else if(*fs==3){ + df1=CDDM_get_face(splitdm,curdupface); + DM_copy_face_data(splitdm,splitdm,i,curdupface,1); + *df1=*mf; + curdupface++; + + df2=CDDM_get_face(splitdm,curdupface); + DM_copy_face_data(splitdm,splitdm,i,curdupface,1); + *df2=*mf; + curdupface++; + + df3=CDDM_get_face(splitdm,curdupface); + DM_copy_face_data(splitdm,splitdm,i,curdupface,1); + *df3=*mf; + curdupface++; + + if(v1==v2){ + df2->v1=df1->v1=edgesplit[mf->v1*totvert+mf->v4]; + df3->v1=df1->v2=edgesplit[mf->v2*totvert+mf->v3]; + df3->v3=df2->v2=df1->v3=edgesplit[mf->v3*totvert+mf->v4]; + df3->v2=mf->v3; + df2->v3=mf->v4; + df1->v4=df2->v4=df3->v4=0; + + mf->v3=df1->v2; + mf->v4=df1->v1; + + facepa[i]=facepa[curdupface-3]=v1; + facepa[curdupface-1]=v3; + facepa[curdupface-2]=v4; + } + else if(v2==v3){ + df3->v1=df2->v3=df1->v1=edgesplit[mf->v1*totvert+mf->v4]; + df2->v2=df1->v2=edgesplit[mf->v1*totvert+mf->v2]; + df3->v2=df1->v3=edgesplit[mf->v3*totvert+mf->v4]; + + df3->v3=mf->v4; + df2->v1=mf->v1; + df1->v4=df2->v4=df3->v4=0; + + mf->v1=df1->v2; + mf->v4=df1->v3; + + facepa[i]=facepa[curdupface-3]=v2; + facepa[curdupface-1]=v4; + facepa[curdupface-2]=v1; + } + else if(v3==v4){ + df3->v2=df2->v1=df1->v1=edgesplit[mf->v1*totvert+mf->v2]; + df2->v3=df1->v2=edgesplit[mf->v2*totvert+mf->v3]; + df3->v3=df1->v3=edgesplit[mf->v1*totvert+mf->v4]; + + df3->v1=mf->v1; + df2->v2=mf->v2; + df1->v4=df2->v4=df3->v4=0; + + mf->v1=df1->v3; + mf->v2=df1->v2; + + facepa[i]=facepa[curdupface-3]=v3; + facepa[curdupface-1]=v1; + facepa[curdupface-2]=v2; + } + else{ + df3->v1=df1->v1=edgesplit[mf->v1*totvert+mf->v2]; + df3->v3=df2->v1=df1->v2=edgesplit[mf->v2*totvert+mf->v3]; + df2->v3=df1->v3=edgesplit[mf->v3*totvert+mf->v4]; + + df3->v2=mf->v2; + df2->v2=mf->v3; + df1->v4=df2->v4=df3->v4=0; + + mf->v2=df1->v1; + mf->v3=df1->v3; + + facepa[i]=facepa[curdupface-3]=v1; + facepa[curdupface-1]=v2; + facepa[curdupface-2]=v3; + } + + test_index_face(df1, &splitdm->faceData, curdupface-3, (df1->v4 ? 4 : 3)); + test_index_face(df1, &splitdm->faceData, curdupface-2, (df1->v4 ? 4 : 3)); + test_index_face(df1, &splitdm->faceData, curdupface-1, (df1->v4 ? 4 : 3)); + } + else if(*fs==4){ + if(v1!=v2 && v2!=v3){ + + /* set new vert to face center */ + mv=CDDM_get_vert(splitdm,mf->v1); + dupve=CDDM_get_vert(splitdm,curdupin); + DM_copy_vert_data(splitdm,splitdm,mf->v1,curdupin,1); + *dupve=*mv; + + mv=CDDM_get_vert(splitdm,mf->v2); + VECADD(dupve->co,dupve->co,mv->co); + mv=CDDM_get_vert(splitdm,mf->v3); + VECADD(dupve->co,dupve->co,mv->co); + mv=CDDM_get_vert(splitdm,mf->v4); + VECADD(dupve->co,dupve->co,mv->co); + VecMulf(dupve->co,0.25); + + + df1=CDDM_get_face(splitdm,curdupface); + DM_copy_face_data(splitdm,splitdm,i,curdupface,1); + *df1=*mf; + curdupface++; + + df2=CDDM_get_face(splitdm,curdupface); + DM_copy_face_data(splitdm,splitdm,i,curdupface,1); + *df2=*mf; + curdupface++; + + df3=CDDM_get_face(splitdm,curdupface); + DM_copy_face_data(splitdm,splitdm,i,curdupface,1); + *df3=*mf; + curdupface++; + + df1->v1=edgesplit[mf->v1*totvert+mf->v2]; + df3->v2=df1->v3=edgesplit[mf->v2*totvert+mf->v3]; + + df2->v1=edgesplit[mf->v1*totvert+mf->v4]; + df3->v4=df2->v3=edgesplit[mf->v3*totvert+mf->v4]; + + df3->v1=df2->v2=df1->v4=curdupin; + + mf->v2=df1->v1; + mf->v3=curdupin; + mf->v4=df2->v1; + + curdupin++; + + facepa[i]=v1; + facepa[curdupface-3]=v2; + facepa[curdupface-2]=v3; + facepa[curdupface-1]=v4; + + test_index_face(df1, &splitdm->faceData, curdupface-3, (df1->v4 ? 4 : 3)); + + test_index_face(df1, &splitdm->faceData, curdupface-2, (df1->v4 ? 4 : 3)); + test_index_face(df1, &splitdm->faceData, curdupface-1, (df1->v4 ? 4 : 3)); + } + else{ + df1=CDDM_get_face(splitdm,curdupface); + DM_copy_face_data(splitdm,splitdm,i,curdupface,1); + *df1=*mf; + curdupface++; + + df2=CDDM_get_face(splitdm,curdupface); + DM_copy_face_data(splitdm,splitdm,i,curdupface,1); + *df2=*mf; + curdupface++; + + df3=CDDM_get_face(splitdm,curdupface); + DM_copy_face_data(splitdm,splitdm,i,curdupface,1); + *df3=*mf; + curdupface++; + + if(v2==v3){ + df1->v1=edgesplit[mf->v1*totvert+mf->v2]; + df3->v1=df1->v2=df1->v3=edgesplit[mf->v2*totvert+mf->v3]; + df2->v1=df1->v4=edgesplit[mf->v1*totvert+mf->v4]; + + df3->v3=df2->v3=edgesplit[mf->v3*totvert+mf->v4]; + + df3->v2=mf->v3; + df3->v4=0; + + mf->v2=df1->v1; + mf->v3=df1->v4; + mf->v4=0; + + facepa[i]=v1; + facepa[curdupface-3]=facepa[curdupface-2]=v2; + facepa[curdupface-1]=v3; + } + else{ + df3->v1=df2->v1=df1->v2=edgesplit[mf->v1*totvert+mf->v2]; + df2->v4=df1->v3=edgesplit[mf->v3*totvert+mf->v4]; + df1->v4=edgesplit[mf->v1*totvert+mf->v4]; + + df3->v3=df2->v2=edgesplit[mf->v2*totvert+mf->v3]; + + df3->v4=0; + + mf->v1=df1->v4; + mf->v2=df1->v3; + mf->v3=mf->v4; + mf->v4=0; + + facepa[i]=v4; + facepa[curdupface-3]=facepa[curdupface-2]=v1; + facepa[curdupface-1]=v2; + } + + test_index_face(df1, &splitdm->faceData, curdupface-3, (df1->v4 ? 4 : 3)); + test_index_face(df1, &splitdm->faceData, curdupface-2, (df1->v4 ? 4 : 3)); + test_index_face(df1, &splitdm->faceData, curdupface-1, (df1->v4 ? 4 : 3)); + } + } + + test_index_face(df1, &splitdm->faceData, i, (df1->v4 ? 4 : 3)); + } + } + + MEM_freeN(edgesplit); + MEM_freeN(facesplit); + MEM_freeN(vertpa); + + return splitdm; + +} +static DerivedMesh * explodeModifier_explodeMesh(ExplodeModifierData *emd, + ParticleSystemModifierData *psmd, Object *ob, + DerivedMesh *to_explode) +{ + DerivedMesh *explode, *dm=to_explode; + MFace *mf=0; + MVert *dupvert=0; + ParticleSettings *part=psmd->psys->part; + ParticleData *pa, *pars=psmd->psys->particles; + ParticleKey state; + float *vertco=0, imat[4][4]; + float loc0[3], nor[3]; + float timestep, cfra; + int *facepa=emd->facepa, *vertpa=0; + int totdup=0,totvert=0,totface=0,totpart=0; + int i, j, v, mindex=0; + + totface= dm->getNumFaces(dm); + totvert= dm->getNumVerts(dm); + totpart= psmd->psys->totpart; + + timestep= psys_get_timestep(part); + + if(part->flag & PART_GLOB_TIME) + cfra=bsystem_time(0,(float)G.scene->r.cfra,0.0); + else + cfra=bsystem_time(ob,(float)G.scene->r.cfra,0.0); + + /* table for vertice <-> particle relations (row totpart+1 is for yet unexploded verts) */ + vertpa = MEM_callocN(sizeof(int)*(totpart+1)*totvert, "explode_vertpatab"); + for(i=0; i<(totpart+1)*totvert; i++) + vertpa[i] = -1; + + for (i=0; i<totface; i++) { + if(facepa[i]==totpart || cfra <= (pars+facepa[i])->time) + mindex = totpart*totvert; + else + mindex = facepa[i]*totvert; + + mf=CDDM_get_face(dm,i); + + /*set face vertices to exist in particle group*/ + vertpa[mindex+mf->v1] = 1; + vertpa[mindex+mf->v2] = 1; + vertpa[mindex+mf->v3] = 1; + if(mf->v4) + vertpa[mindex+mf->v4] = 1; + } + + /*make new vertice indexes & count total vertices after duplication*/ + for(i=0; i<(totpart+1)*totvert; i++){ + if(vertpa[i] != -1) + vertpa[i] = totdup++; + } + + /*the final duplicated vertices*/ + explode= CDDM_from_template(dm, totdup, 0,totface); + dupvert= CDDM_get_verts(explode); + + /* getting back to object space */ + Mat4Invert(imat,ob->obmat); + + psmd->psys->lattice = psys_get_lattice(ob, psmd->psys); + + /*duplicate & displace vertices*/ + for(i=0, pa=pars; i<=totpart; i++, pa++){ + if(i!=totpart){ + psys_particle_on_emitter(ob, psmd,part->from,pa->num,-1,pa->fuv,pa->foffset,loc0,nor,0,0); + Mat4MulVecfl(ob->obmat,loc0); + + state.time=cfra; + psys_get_particle_state(ob,psmd->psys,i,&state,1); + } + + for(j=0; j<totvert; j++){ + v=vertpa[i*totvert+j]; + if(v != -1) { + MVert source; + MVert *dest; + + dm->getVert(dm, j, &source); + dest = CDDM_get_vert(explode,v); + + DM_copy_vert_data(dm,explode,j,v,1); + *dest = source; + + if(i!=totpart){ + vertco=CDDM_get_vert(explode,v)->co; + + Mat4MulVecfl(ob->obmat,vertco); + + VECSUB(vertco,vertco,loc0); + + /* apply rotation, size & location */ + QuatMulVecf(state.rot,vertco); + VecMulf(vertco,pa->size); + VECADD(vertco,vertco,state.co); + + Mat4MulVecfl(imat,vertco); + } + } + } + } + + /*map new vertices to faces*/ + for (i=0; i<totface; i++) { + MFace source; + int orig_v4; + + if(facepa[i]!=totpart) + { + pa=pars+facepa[i]; + + if(pa->alive==PARS_UNBORN && (emd->flag&eExplodeFlag_Unborn)==0) continue; + if(pa->alive==PARS_ALIVE && (emd->flag&eExplodeFlag_Alive)==0) continue; + if(pa->alive==PARS_DEAD && (emd->flag&eExplodeFlag_Dead)==0) continue; + } + + dm->getFace(dm,i,&source); + mf=CDDM_get_face(explode,i); + + orig_v4 = source.v4; + + if(facepa[i]!=totpart && cfra <= pa->time) + mindex = totpart*totvert; + else + mindex = facepa[i]*totvert; + + source.v1 = vertpa[mindex+source.v1]; + source.v2 = vertpa[mindex+source.v2]; + source.v3 = vertpa[mindex+source.v3]; + if(source.v4) + source.v4 = vertpa[mindex+source.v4]; + + DM_copy_face_data(dm,explode,i,i,1); + + *mf = source; + + test_index_face(mf, &explode->faceData, i, (mf->v4 ? 4 : 3)); + } + + + /* cleanup */ + if(vertpa) MEM_freeN(vertpa); + + /* finalization */ + CDDM_calc_edges(explode); + CDDM_calc_normals(explode); + + if(psmd->psys->lattice){ + end_latt_deform(); + psmd->psys->lattice=0; + } + + return explode; +} + +static ParticleSystemModifierData * explodeModifier_findPrecedingParticlesystem(Object *ob, ModifierData *emd) +{ + ModifierData *md; + ParticleSystemModifierData *psmd=0; + + for (md=ob->modifiers.first; emd!=md; md=md->next){ + if(md->type==eModifierType_ParticleSystem) + psmd= (ParticleSystemModifierData*) md; + } + return psmd; +} +static DerivedMesh * explodeModifier_applyModifier( + ModifierData *md, Object *ob, DerivedMesh *derivedData, + int useRenderParams, int isFinalCalc) +{ + DerivedMesh *dm = derivedData; + ExplodeModifierData *emd= (ExplodeModifierData*) md; + ParticleSystemModifierData *psmd=explodeModifier_findPrecedingParticlesystem(ob,md);; + + if(psmd){ + ParticleSystem * psys=psmd->psys; + + if(psys==0 || psys->totpart==0) return derivedData; + if(psys->part==0 || psys->particles==0) return derivedData; + + /* 1. find faces to be exploded if needed */ + if(emd->facepa==0 || psmd->flag&eParticleSystemFlag_Pars || emd->flag&eExplodeFlag_CalcFaces){ + if(psmd->flag & eParticleSystemFlag_Pars) + psmd->flag &= ~eParticleSystemFlag_Pars; + + if(emd->flag & eExplodeFlag_CalcFaces) + emd->flag &= ~eExplodeFlag_CalcFaces; + + explodeModifier_createFacepa(emd,psmd,ob,derivedData); + } + + /* 2. create new mesh */ + if(emd->flag & eExplodeFlag_EdgeSplit){ + int *facepa = emd->facepa; + DerivedMesh *splitdm=explodeModifier_splitEdges(emd,dm); + DerivedMesh *explode=explodeModifier_explodeMesh(emd,psmd,ob,splitdm); + + MEM_freeN(emd->facepa); + emd->facepa=facepa; + splitdm->release(splitdm); + return explode; + } + else + return explodeModifier_explodeMesh(emd,psmd,ob,derivedData); + } + return derivedData; +} +/* MeshDeform */ + +static void meshdeformModifier_initData(ModifierData *md) +{ + MeshDeformModifierData *mmd = (MeshDeformModifierData*) md; + + mmd->gridsize= 5; +} + +static void meshdeformModifier_freeData(ModifierData *md) +{ + MeshDeformModifierData *mmd = (MeshDeformModifierData*) md; + + if(mmd->bindweights) MEM_freeN(mmd->bindweights); + if(mmd->bindcos) MEM_freeN(mmd->bindcos); + if(mmd->dyngrid) MEM_freeN(mmd->dyngrid); + if(mmd->dyninfluences) MEM_freeN(mmd->dyninfluences); + if(mmd->dynverts) MEM_freeN(mmd->dynverts); +} + +static void meshdeformModifier_copyData(ModifierData *md, ModifierData *target) +{ + MeshDeformModifierData *mmd = (MeshDeformModifierData*) md; + MeshDeformModifierData *tmmd = (MeshDeformModifierData*) target; + + tmmd->gridsize = mmd->gridsize; + tmmd->object = mmd->object; +} + +CustomDataMask meshdeformModifier_requiredDataMask(ModifierData *md) +{ + MeshDeformModifierData *mmd = (MeshDeformModifierData *)md; + CustomDataMask dataMask = 0; + + /* ask for vertexgroups if we need them */ + if(mmd->defgrp_name[0]) dataMask |= (1 << CD_MDEFORMVERT); + + return dataMask; +} + +static int meshdeformModifier_isDisabled(ModifierData *md) +{ + MeshDeformModifierData *mmd = (MeshDeformModifierData*) md; + + return !mmd->object; +} + +static void meshdeformModifier_foreachObjectLink( + ModifierData *md, Object *ob, + void (*walk)(void *userData, Object *ob, Object **obpoin), + void *userData) +{ + MeshDeformModifierData *mmd = (MeshDeformModifierData*) md; + + walk(userData, ob, &mmd->object); +} + +static void meshdeformModifier_updateDepgraph( + ModifierData *md, DagForest *forest, Object *ob, + DagNode *obNode) +{ + MeshDeformModifierData *mmd = (MeshDeformModifierData*) md; + + if (mmd->object) { + DagNode *curNode = dag_get_node(forest, mmd->object); + + dag_add_relation(forest, curNode, obNode, + DAG_RL_DATA_DATA|DAG_RL_OB_DATA|DAG_RL_DATA_OB|DAG_RL_OB_OB); + } +} + +static float meshdeform_dynamic_bind(MeshDeformModifierData *mmd, float (*dco)[3], float *vec) +{ + MDefCell *cell; + MDefInfluence *inf; + float gridvec[3], dvec[3], ivec[3], co[3], wx, wy, wz; + float weight, cageweight, totweight, *cageco; + int i, j, a, x, y, z, size; + + co[0]= co[1]= co[2]= 0.0f; + totweight= 0.0f; + size= mmd->dyngridsize; + + for(i=0; i<3; i++) { + gridvec[i]= (vec[i] - mmd->dyncellmin[i] - mmd->dyncellwidth*0.5f)/mmd->dyncellwidth; + ivec[i]= (int)gridvec[i]; + dvec[i]= gridvec[i] - ivec[i]; + } + + for(i=0; i<8; i++) { + if(i & 1) { x= ivec[0]+1; wx= dvec[0]; } + else { x= ivec[0]; wx= 1.0f-dvec[0]; } + + if(i & 2) { y= ivec[1]+1; wy= dvec[1]; } + else { y= ivec[1]; wy= 1.0f-dvec[1]; } + + if(i & 4) { z= ivec[2]+1; wz= dvec[2]; } + else { z= ivec[2]; wz= 1.0f-dvec[2]; } + + CLAMP(x, 0, size-1); + CLAMP(y, 0, size-1); + CLAMP(z, 0, size-1); + + a= x + y*size + z*size*size; + weight= wx*wy*wz; + + cell= &mmd->dyngrid[a]; + inf= mmd->dyninfluences + cell->offset; + for(j=0; j<cell->totinfluence; j++, inf++) { + cageco= dco[inf->vertex]; + cageweight= weight*inf->weight; + co[0] += cageweight*cageco[0]; + co[1] += cageweight*cageco[1]; + co[2] += cageweight*cageco[2]; + totweight += cageweight; + } + } + + VECCOPY(vec, co); + + return totweight; +} + +static void meshdeformModifier_do( + ModifierData *md, Object *ob, DerivedMesh *dm, + float (*vertexCos)[3], int numVerts) +{ + MeshDeformModifierData *mmd = (MeshDeformModifierData*) md; + float imat[4][4], cagemat[4][4], icagemat[4][4], iobmat[3][3]; + float weight, totweight, fac, co[3], *weights, (*dco)[3], (*bindcos)[3]; + int a, b, totvert, totcagevert, defgrp_index; + DerivedMesh *tmpdm, *cagedm; + MDeformVert *dvert = NULL; + MDeformWeight *dw; + MVert *cagemvert; + + if(!mmd->object || (!mmd->bindcos && !mmd->needbind)) + return; + + /* get cage derivedmesh */ + if(mmd->object == G.obedit) { + tmpdm= editmesh_get_derived_cage_and_final(&cagedm, 0); + if(tmpdm) + tmpdm->release(tmpdm); + } + else + cagedm= mesh_get_derived_final(mmd->object, CD_MASK_BAREMESH); + + /* TODO: this could give inifinite loop for circular dependency */ + if(!cagedm) + return; + + /* compute matrices to go in and out of cage object space */ + Mat4Invert(imat, (mmd->bindcos)? mmd->bindmat: mmd->object->obmat); + Mat4MulMat4(cagemat, ob->obmat, imat); + Mat4Invert(icagemat, cagemat); + Mat4Invert(imat, ob->obmat); + Mat3CpyMat4(iobmat, imat); + + /* bind weights if needed */ + if(!mmd->bindcos) + harmonic_coordinates_bind(mmd, vertexCos, numVerts, cagemat); + + /* verify we have compatible weights */ + totvert= numVerts; + totcagevert= cagedm->getNumVerts(cagedm); + + if(mmd->totvert!=totvert || mmd->totcagevert!=totcagevert || !mmd->bindcos) { + cagedm->release(cagedm); + return; + } + + /* setup deformation data */ + cagemvert= cagedm->getVertArray(cagedm); + weights= mmd->bindweights; + bindcos= (float(*)[3])mmd->bindcos; + + dco= MEM_callocN(sizeof(*dco)*totcagevert, "MDefDco"); + for(a=0; a<totcagevert; a++) { + VECCOPY(co, cagemvert[a].co); + Mat4MulVecfl(mmd->object->obmat, co); + VECSUB(dco[a], co, bindcos[a]); + } + + defgrp_index = -1; + + if(mmd->defgrp_name[0]) { + bDeformGroup *def; + + for(a=0, def=ob->defbase.first; def; def=def->next, a++) { + if(!strcmp(def->name, mmd->defgrp_name)) { + defgrp_index= a; + break; + } + } + + if (defgrp_index >= 0) + dvert= dm->getVertDataArray(dm, CD_MDEFORMVERT); + } + + /* do deformation */ + fac= 1.0f; + + for(b=0; b<totvert; b++) { + if(mmd->flag & MOD_MDEF_DYNAMIC_BIND) + if(!mmd->dynverts[b]) + continue; + + if(dvert) { + for(dw=NULL, a=0; a<dvert[b].totweight; a++) { + if(dvert[b].dw[a].def_nr == defgrp_index) { + dw = &dvert[b].dw[a]; + break; + } + } + + if(mmd->flag & MOD_MDEF_INVERT_VGROUP) { + if(!dw) fac= 1.0f; + else if(dw->weight == 1.0f) continue; + else fac=1.0f-dw->weight; + } + else { + if(!dw) continue; + else fac= dw->weight; + } + } + + if(mmd->flag & MOD_MDEF_DYNAMIC_BIND) { + VECCOPY(co, vertexCos[b]); + Mat4MulVecfl(cagemat, co); + totweight= meshdeform_dynamic_bind(mmd, dco, co); + } + else { + totweight= 0.0f; + co[0]= co[1]= co[2]= 0.0f; + + for(a=0; a<totcagevert; a++) { + weight= weights[a + b*totcagevert]; + co[0]+= weight*dco[a][0]; + co[1]+= weight*dco[a][1]; + co[2]+= weight*dco[a][2]; + totweight += weight; + } + } + + if(totweight > 0.0f) { + VecMulf(co, fac/totweight); + Mat3MulVecfl(iobmat, co); + VECADD(vertexCos[b], vertexCos[b], co); + } + } + + /* release cage derivedmesh */ + MEM_freeN(dco); + cagedm->release(cagedm); +} + +static void meshdeformModifier_deformVerts( + ModifierData *md, Object *ob, DerivedMesh *derivedData, + float (*vertexCos)[3], int numVerts) +{ + DerivedMesh *dm; + + if(!derivedData && ob->type==OB_MESH) + dm= CDDM_from_mesh(ob->data, ob); + else + dm= derivedData; + + modifier_vgroup_cache(md, vertexCos); /* if next modifier needs original vertices */ + + meshdeformModifier_do(md, ob, dm, vertexCos, numVerts); + + if(dm != derivedData) + dm->release(dm); +} + +static void meshdeformModifier_deformVertsEM( + ModifierData *md, Object *ob, EditMesh *editData, + DerivedMesh *derivedData, float (*vertexCos)[3], int numVerts) +{ + DerivedMesh *dm; + + if(!derivedData && ob->type == OB_MESH) + dm = CDDM_from_editmesh(editData, ob->data); + else + dm = derivedData; + + meshdeformModifier_do(md, ob, dm, vertexCos, numVerts); + + if(dm != derivedData) + dm->release(dm); +} + /***/ static ModifierTypeInfo typeArr[NUM_MODIFIER_TYPES]; @@ -4963,6 +6611,8 @@ ModifierTypeInfo *modifierType_getInfo(ModifierType type) | eModifierTypeFlag_EnableInEditmode; mti->initData = mirrorModifier_initData; mti->copyData = mirrorModifier_copyData; + mti->foreachObjectLink = mirrorModifier_foreachObjectLink; + mti->updateDepgraph = mirrorModifier_updateDepgraph; mti->applyModifier = mirrorModifier_applyModifier; mti->applyModifierEM = mirrorModifier_applyModifierEM; @@ -5060,6 +6710,7 @@ ModifierTypeInfo *modifierType_getInfo(ModifierType type) mti->updateDepgraph = armatureModifier_updateDepgraph; mti->deformVerts = armatureModifier_deformVerts; mti->deformVertsEM = armatureModifier_deformVertsEM; + mti->deformMatricesEM = armatureModifier_deformMatricesEM; mti = INIT_TYPE(Hook); mti->type = eModifierTypeType_OnlyDeform; @@ -5090,6 +6741,56 @@ ModifierTypeInfo *modifierType_getInfo(ModifierType type) mti->foreachObjectLink = booleanModifier_foreachObjectLink; mti->updateDepgraph = booleanModifier_updateDepgraph; + mti = INIT_TYPE(MeshDeform); + mti->type = eModifierTypeType_OnlyDeform; + mti->flags = eModifierTypeFlag_AcceptsCVs + | eModifierTypeFlag_SupportsEditmode; + mti->initData = meshdeformModifier_initData; + mti->freeData = meshdeformModifier_freeData; + mti->copyData = meshdeformModifier_copyData; + mti->requiredDataMask = meshdeformModifier_requiredDataMask; + mti->isDisabled = meshdeformModifier_isDisabled; + mti->foreachObjectLink = meshdeformModifier_foreachObjectLink; + mti->updateDepgraph = meshdeformModifier_updateDepgraph; + mti->deformVerts = meshdeformModifier_deformVerts; + mti->deformVertsEM = meshdeformModifier_deformVertsEM; + + mti = INIT_TYPE(ParticleSystem); + mti->type = eModifierTypeType_OnlyDeform; + mti->flags = eModifierTypeFlag_AcceptsMesh + |eModifierTypeFlag_SupportsEditmode + |eModifierTypeFlag_EnableInEditmode; + mti->initData = particleSystemModifier_initData; + mti->freeData = particleSystemModifier_freeData; + mti->copyData = particleSystemModifier_copyData; + mti->deformVerts = particleSystemModifier_deformVerts; + mti->deformVertsEM = particleSystemModifier_deformVertsEM; + mti->requiredDataMask = particleSystemModifier_requiredDataMask; + + mti = INIT_TYPE(ParticleInstance); + mti->type = eModifierTypeType_Constructive; + mti->flags = eModifierTypeFlag_AcceptsMesh + | eModifierTypeFlag_SupportsMapping + | eModifierTypeFlag_SupportsEditmode + | eModifierTypeFlag_EnableInEditmode; + mti->initData = particleInstanceModifier_initData; + mti->copyData = particleInstanceModifier_copyData; + mti->dependsOnTime = particleInstanceModifier_dependsOnTime; + mti->foreachObjectLink = particleInstanceModifier_foreachObjectLink; + mti->applyModifier = particleInstanceModifier_applyModifier; + mti->applyModifierEM = particleInstanceModifier_applyModifierEM; + mti->updateDepgraph = particleInstanceModifier_updateDepgraph; + + mti = INIT_TYPE(Explode); + mti->type = eModifierTypeType_Nonconstructive; + mti->flags = eModifierTypeFlag_AcceptsMesh; + mti->initData = explodeModifier_initData; + mti->freeData = explodeModifier_freeData; + mti->copyData = explodeModifier_copyData; + mti->dependsOnTime = explodeModifier_dependsOnTime; + mti->requiredDataMask = explodeModifier_requiredDataMask; + mti->applyModifier = explodeModifier_applyModifier; + typeArrInit = 0; #undef INIT_TYPE } @@ -5261,6 +6962,7 @@ int modifiers_getCageIndex(Object *ob, int *lastPossibleCageIndex_r) if (!(md->mode & eModifierMode_Editmode)) continue; if (mti->isDisabled && mti->isDisabled(md)) continue; if (!(mti->flags & eModifierTypeFlag_SupportsEditmode)) continue; + if (md->mode & eModifierMode_DisableTemporary) continue; if (!modifier_supportsMapping(md)) break; @@ -5281,6 +6983,13 @@ int modifiers_isSoftbodyEnabled(Object *ob) return (md && md->mode & (eModifierMode_Realtime | eModifierMode_Render)); } +int modifiers_isParticleEnabled(Object *ob) +{ + ModifierData *md = modifiers_findByType(ob, eModifierType_ParticleSystem); + + return (md && md->mode & (eModifierMode_Realtime | eModifierMode_Render)); +} + LinkNode *modifiers_calcDataMasks(ModifierData *md, CustomDataMask dataMask) { LinkNode *dataMasks = NULL; @@ -5440,20 +7149,37 @@ int modifiers_usesArmature(Object *ob, bArmature *arm) return 0; } +int modifier_isDeformer(ModifierData *md) +{ + if (md->type==eModifierType_Armature) + return 1; + if (md->type==eModifierType_Curve) + return 1; + if (md->type==eModifierType_Lattice) + return 1; + + return 0; +} + int modifiers_isDeformed(Object *ob) { ModifierData *md = modifiers_getVirtualModifierList(ob); for (; md; md=md->next) { if(ob==G.obedit && (md->mode & eModifierMode_Editmode)==0); - else { - if (md->type==eModifierType_Armature) - return 1; - if (md->type==eModifierType_Curve) - return 1; - if (md->type==eModifierType_Lattice) - return 1; - } + else if(modifier_isDeformer(md)) + return 1; } return 0; } + +int modifiers_indexInObject(Object *ob, ModifierData *md_seek) +{ + int i= 0; + ModifierData *md; + + for (md=ob->modifiers.first; (md && md_seek!=md); md=md->next, i++); + if (!md) return -1; /* modifier isnt in the object */ + return i; +} + diff --git a/source/blender/blenkernel/intern/node.c b/source/blender/blenkernel/intern/node.c index e7b7b36aaa4..651115b7180 100644 --- a/source/blender/blenkernel/intern/node.c +++ b/source/blender/blenkernel/intern/node.c @@ -2331,6 +2331,7 @@ static void registerCompositNodes(ListBase *ntypelist) nodeRegisterType(ntypelist, &cmp_node_normal); nodeRegisterType(ntypelist, &cmp_node_curve_vec); nodeRegisterType(ntypelist, &cmp_node_map_value); + nodeRegisterType(ntypelist, &cmp_node_normalize); nodeRegisterType(ntypelist, &cmp_node_filter); nodeRegisterType(ntypelist, &cmp_node_blur); @@ -2362,8 +2363,13 @@ static void registerCompositNodes(ListBase *ntypelist) nodeRegisterType(ntypelist, &cmp_node_rotate); nodeRegisterType(ntypelist, &cmp_node_scale); nodeRegisterType(ntypelist, &cmp_node_flip); + nodeRegisterType(ntypelist, &cmp_node_crop); nodeRegisterType(ntypelist, &cmp_node_displace); nodeRegisterType(ntypelist, &cmp_node_mapuv); + + nodeRegisterType(ntypelist, &cmp_node_glare); + nodeRegisterType(ntypelist, &cmp_node_tonemap); + nodeRegisterType(ntypelist, &cmp_node_lensdist); } static void registerShaderNodes(ListBase *ntypelist) @@ -2388,6 +2394,9 @@ static void registerShaderNodes(ListBase *ntypelist) nodeRegisterType(ntypelist, &sh_node_rgb); nodeRegisterType(ntypelist, &sh_node_texture); nodeRegisterType(ntypelist, &sh_node_invert); + nodeRegisterType(ntypelist, &sh_node_seprgb); + nodeRegisterType(ntypelist, &sh_node_combrgb); + nodeRegisterType(ntypelist, &sh_node_hue_sat); } void init_nodesystem(void) diff --git a/source/blender/blenkernel/intern/object.c b/source/blender/blenkernel/intern/object.c index 4cb6f64569f..d9b2ce84fa5 100644 --- a/source/blender/blenkernel/intern/object.c +++ b/source/blender/blenkernel/intern/object.c @@ -60,6 +60,7 @@ #include "DNA_object_force.h" #include "DNA_object_fluidsim.h" #include "DNA_oops_types.h" +#include "DNA_particle_types.h" #include "DNA_scene_types.h" #include "DNA_screen_types.h" #include "DNA_space_types.h" @@ -70,6 +71,7 @@ #include "BKE_armature.h" #include "BKE_action.h" +#include "BKE_colortools.h" #include "BKE_deform.h" #include "BKE_DerivedMesh.h" #include "BKE_nla.h" @@ -100,6 +102,7 @@ #include "BKE_mball.h" #include "BKE_modifier.h" #include "BKE_object.h" +#include "BKE_particle.h" #include "BKE_property.h" #include "BKE_sca.h" #include "BKE_scene.h" @@ -163,6 +166,15 @@ void object_free_modifiers(Object *ob) modifier_free(md); } + + /* particle modifiers were freed, so free the particlesystems as well */ + while(ob->particlesystem.first){ + ParticleSystem *psys = ob->particlesystem.first; + + BLI_remlink(&ob->particlesystem,psys); + + psys_free(ob,psys); + } } /* here we will collect all local displist stuff */ @@ -221,7 +233,6 @@ void free_object(Object *ob) MEM_freeN(ob->pose); } free_effects(&ob->effect); - BLI_freelistN(&ob->network); free_properties(&ob->prop); object_free_modifiers(ob); @@ -235,7 +246,11 @@ void free_object(Object *ob) BPY_free_scriptlink(&ob->scriptlink); - if(ob->pd) MEM_freeN(ob->pd); + if(ob->pd){ + if(ob->pd->tex) + ob->pd->tex->id.us--; + MEM_freeN(ob->pd); + } if(ob->soft) sbFree(ob->soft); if(ob->fluidsimSettings) fluidsimSettingsFree(ob->fluidsimSettings); } @@ -260,10 +275,10 @@ void unlink_object(Object *ob) Tex *tex; Ipo *ipo; Group *group; + Camera *camera; bConstraint *con; bActionStrip *strip; int a; - char *str; unlink_controllers(&ob->controllers); unlink_actuators(&ob->actuators); @@ -312,9 +327,23 @@ void unlink_object(Object *ob) bPoseChannel *pchan; for(pchan= obt->pose->chanbase.first; pchan; pchan= pchan->next) { for (con = pchan->constraints.first; con; con=con->next) { - if(ob==get_constraint_target(con, &str)) { - set_constraint_target(con, NULL, NULL); - obt->recalc |= OB_RECALC_DATA; + bConstraintTypeInfo *cti= constraint_get_typeinfo(con); + ListBase targets = {NULL, NULL}; + bConstraintTarget *ct; + + if (cti && cti->get_constraint_targets) { + cti->get_constraint_targets(con, &targets); + + for (ct= targets.first; ct; ct= ct->next) { + if (ct->tar == ob) { + ct->tar = NULL; + strcpy(ct->subtarget, ""); + obt->recalc |= OB_RECALC_DATA; + } + } + + if (cti->flush_constraint_targets) + cti->flush_constraint_targets(con, &targets, 0); } } if(pchan->custom==ob) @@ -325,9 +354,23 @@ void unlink_object(Object *ob) sca_remove_ob_poin(obt, ob); for (con = obt->constraints.first; con; con=con->next) { - if(ob==get_constraint_target(con, &str)) { - set_constraint_target(con, NULL, NULL); - obt->recalc |= OB_RECALC_OB; + bConstraintTypeInfo *cti= constraint_get_typeinfo(con); + ListBase targets = {NULL, NULL}; + bConstraintTarget *ct; + + if (cti && cti->get_constraint_targets) { + cti->get_constraint_targets(con, &targets); + + for (ct= targets.first; ct; ct= ct->next) { + if (ct->tar == ob) { + ct->tar = NULL; + strcpy(ct->subtarget, ""); + obt->recalc |= OB_RECALC_DATA; + } + } + + if (cti->flush_constraint_targets) + cti->flush_constraint_targets(con, &targets, 0); } } @@ -352,6 +395,47 @@ void unlink_object(Object *ob) } } + /* particle systems */ + if(obt->particlesystem.first) { + ParticleSystem *tpsys= obt->particlesystem.first; + for(; tpsys; tpsys=tpsys->next) { + if(tpsys->keyed_ob==ob) { + ParticleSystem *psys= BLI_findlink(&ob->particlesystem,tpsys->keyed_psys-1); + + if(psys && psys->keyed_ob) { + tpsys->keyed_ob= psys->keyed_ob; + tpsys->keyed_psys= psys->keyed_psys; + } + else + tpsys->keyed_ob= NULL; + + obt->recalc |= OB_RECALC_DATA; + } + + if(tpsys->target_ob==ob) { + tpsys->target_ob= NULL; + obt->recalc |= OB_RECALC_DATA; + } + + if(tpsys->part->dup_ob==ob) + tpsys->part->dup_ob= NULL; + + if(tpsys->part->flag&PART_STICKY) { + ParticleData *pa; + int p; + + for(p=0,pa=tpsys->particles; p<tpsys->totpart; p++,pa++) { + if(pa->stick_ob==ob) { + pa->stick_ob= 0; + pa->flag &= ~PARS_STICKY; + } + } + } + } + if(ob->pd) + obt->recalc |= OB_RECALC_DATA; + } + obt= obt->id.next; } @@ -473,6 +557,15 @@ void unlink_object(Object *ob) rem_from_group(group, ob); group= group->id.next; } + + /* cameras */ + camera= G.main->camera.first; + while(camera) { + if (camera->dof_ob==ob) { + camera->dof_ob = NULL; + } + camera= camera->id.next; + } } int exist_object(Object *obtest) @@ -573,7 +666,23 @@ void make_local_camera(Camera *cam) } } - +/* get the camera's dof value, takes the dof object into account */ +float dof_camera(Object *ob) +{ + Camera *cam = (Camera *)ob->data; + if (ob->type != OB_CAMERA) + return 0.0; + if (cam->dof_ob) { + /* too simple, better to return the distance on the view axis only + * return VecLenf(ob->obmat[3], cam->dof_ob->obmat[3]); */ + + float mat[4][4]; + Mat4Invert(ob->imat, ob->obmat); + Mat4MulMat4(mat, cam->dof_ob->obmat, ob->imat); + return fabs(mat[3][2]); + } + return cam->YF_dofdist; +} void *add_lamp(char *name) { @@ -599,7 +708,12 @@ void *add_lamp(char *name) la->area_size=la->area_sizey=la->area_sizez= 1.0; la->buffers= 1; la->buftype= LA_SHADBUF_HALFWAY; - + la->ray_samp_method = LA_SAMP_HALTON; + la->adapt_thresh = 0.001; + la->preview=NULL; + la->falloff_type = LA_FALLOFF_INVLINEAR; + la->curfalloff = curvemapping_add(1, 0.0f, 1.0f, 1.0f, 0.0f); + curvemapping_initialize(la->curfalloff); return la; } @@ -618,8 +732,12 @@ Lamp *copy_lamp(Lamp *la) } } + lan->curfalloff = curvemapping_copy(la->curfalloff); + id_us_plus((ID *)lan->ipo); + if (la->preview) lan->preview = BKE_previewimg_copy(la->preview); + BPY_copy_scriptlink(&la->scriptlink); return lan; @@ -690,7 +808,7 @@ void free_lamp(Lamp *la) /* scriptlinks */ BPY_free_scriptlink(&la->scriptlink); - + for(a=0; a<MAX_MTEX; a++) { mtex= la->mtex[a]; if(mtex && mtex->tex) mtex->tex->id.us--; @@ -698,6 +816,9 @@ void free_lamp(Lamp *la) } la->ipo= 0; + curvemapping_free(la->curfalloff); + + BKE_previewimg_free(&la->preview); BKE_icon_delete(&la->id); la->id.icon_id = 0; } @@ -772,6 +893,7 @@ Object *add_only_object(int type, char *name) ob->rot[0]= ob->rot[1]= ob->rot[2]= 0.0; ob->size[0]= ob->size[1]= ob->size[2]= 1.0; + Mat4One(ob->constinv); Mat4One(ob->parentinv); Mat4One(ob->obmat); ob->dt= OB_SHADED; @@ -850,13 +972,15 @@ void base_init_from_view3d(Base *base, View3D *v3d) VECCOPY(ob->loc, G.scene->cursor); } - v3d->viewquat[0]= -v3d->viewquat[0]; - if (ob->transflag & OB_QUAT) { - QUATCOPY(ob->quat, v3d->viewquat); - } else { - QuatToEul(v3d->viewquat, ob->rot); + if (U.flag & USER_ADD_VIEWALIGNED) { + v3d->viewquat[0]= -v3d->viewquat[0]; + if (ob->transflag & OB_QUAT) { + QUATCOPY(ob->quat, v3d->viewquat); + } else { + QuatToEul(v3d->viewquat, ob->rot); + } + v3d->viewquat[0]= -v3d->viewquat[0]; } - v3d->viewquat[0]= -v3d->viewquat[0]; } SoftBody *copy_softbody(SoftBody *sb) @@ -878,6 +1002,22 @@ SoftBody *copy_softbody(SoftBody *sb) return sbn; } +ParticleSystem *copy_particlesystem(ParticleSystem *psys) +{ + ParticleSystem *psysn; + + psysn= MEM_dupallocN(psys); + psysn->particles= MEM_dupallocN(psys->particles); + + psysn->child= MEM_dupallocN(psys->child); + + psysn->effectors.first= psysn->effectors.last= 0; + + id_us_plus((ID *)psysn->part); + + return psysn; +} + static void copy_object_pose(Object *obn, Object *ob) { bPoseChannel *chan; @@ -886,11 +1026,25 @@ static void copy_object_pose(Object *obn, Object *ob) for (chan = obn->pose->chanbase.first; chan; chan=chan->next){ bConstraint *con; - char *str; + chan->flag &= ~(POSE_LOC|POSE_ROT|POSE_SIZE); - for(con= chan->constraints.first; con; con= con->next) { - if(ob==get_constraint_target(con, &str)) - set_constraint_target(con, obn, NULL); + + for (con= chan->constraints.first; con; con= con->next) { + bConstraintTypeInfo *cti= constraint_get_typeinfo(con); + ListBase targets = {NULL, NULL}; + bConstraintTarget *ct; + + if (cti && cti->get_constraint_targets) { + cti->get_constraint_targets(con, &targets); + + for (ct= targets.first; ct; ct= ct->next) { + if (ct->tar == ob) + ct->tar = obn; + } + + if (cti->flush_constraint_targets) + cti->flush_constraint_targets(con, &targets, 0); + } } } } @@ -899,6 +1053,7 @@ Object *copy_object(Object *ob) { Object *obn; ModifierData *md; + ParticleSystem *psys; int a; obn= copy_libblock(ob); @@ -920,8 +1075,6 @@ Object *copy_object(Object *ob) BLI_addtail(&obn->modifiers, nmd); } - obn->network.first= obn->network.last= 0; - BPY_copy_scriptlink(&ob->scriptlink); copy_properties(&obn->prop, &ob->prop); @@ -951,7 +1104,11 @@ Object *copy_object(Object *ob) obn->disp.first= obn->disp.last= NULL; - if(ob->pd) obn->pd= MEM_dupallocN(ob->pd); + if(ob->pd){ + obn->pd= MEM_dupallocN(ob->pd); + if(obn->pd->tex) + id_us_plus(&(obn->pd->tex->id)); + } obn->soft= copy_softbody(ob->soft); /* NT copy fluid sim setting memory */ @@ -962,6 +1119,23 @@ Object *copy_object(Object *ob) obn->fluidsimSettings->orgMesh = (Mesh *)obn->data; } } + + obn->particlesystem.first= obn->particlesystem.last= NULL; + for(psys=ob->particlesystem.first; psys; psys=psys->next) { + ParticleSystemModifierData *psmd; + ParticleSystem *npsys= copy_particlesystem(psys); + + BLI_addtail(&obn->particlesystem, npsys); + + /* need to update particle modifiers too */ + for(md=obn->modifiers.first; md; md=md->next) { + if(md->type==eModifierType_ParticleSystem) { + psmd= (ParticleSystemModifierData*)md; + if(psmd->psys==psys) + psmd->psys= npsys; + } + } + } obn->derivedDeform = NULL; obn->derivedFinal = NULL; @@ -1061,6 +1235,20 @@ void make_local_object(Object *ob) /* *************** PROXY **************** */ +/* when you make proxy, ensure the exposed layers are extern */ +void armature_set_id_extern(Object *ob) +{ + bArmature *arm= ob->data; + bPoseChannel *pchan; + int lay= arm->layer_protected; + + for (pchan = ob->pose->chanbase.first; pchan; pchan=pchan->next) { + if(!(pchan->bone->layer & lay)) + id_lib_extern((ID *)pchan->custom); + } + +} + /* proxy rule: lib_object->proxy_from == the one we borrow from, set temporally while object_update */ /* local_object->proxy == pointer to library object, saved in files and read */ /* local_object->proxy_group == pointer to group dupli-object, saved in files and read */ @@ -1084,6 +1272,8 @@ void object_make_proxy(Object *ob, Object *target, Object *gob) VECCOPY(ob->loc, gob->loc); VECCOPY(ob->rot, gob->rot); VECCOPY(ob->size, gob->size); + + group_tag_recalc(gob->dup_group); } else { VECCOPY(ob->loc, target->loc); @@ -1106,6 +1296,8 @@ void object_make_proxy(Object *ob, Object *target, Object *gob) copy_object_pose(ob, target); /* data copy, object pointers in constraints */ rest_pose(ob->pose); /* clear all transforms in channels */ armature_rebuild_pose(ob, ob->data); /* set all internal links */ + + armature_set_id_extern(ob); } } @@ -1134,20 +1326,23 @@ void disable_speed_curve(int val) } /* ob can be NULL */ -float bsystem_time(Object *ob, Object *par, float cfra, float ofs) +float bsystem_time(Object *ob, float cfra, float ofs) { /* returns float ( see frame_to_float in ipo.c) */ - + + /* bluroffs and fieldoffs are ugly globals that are set by render */ cfra+= bluroffs+fieldoffs; /* global time */ cfra*= G.scene->r.framelen; - if(no_speed_curve==0) if(ob && ob->ipo) cfra= calc_ipo_time(ob->ipo, cfra); - - /* ofset frames */ - if(ob && (ob->ipoflag & OB_OFFS_PARENT)) { - if((ob->partype & PARSLOW)==0) cfra-= ob->sf; + if (ob) { + if (no_speed_curve==0 && ob->ipo) + cfra= calc_ipo_time(ob->ipo, cfra); + + /* ofset frames */ + if ((ob->ipoflag & OB_OFFS_PARENT) && (ob->partype & PARSLOW)==0) + cfra-= ob->sf; } cfra-= ofs; @@ -1238,7 +1433,7 @@ static void ob_parcurve(Object *ob, Object *par, float mat[][4]) } /* catch exceptions: curve paths used as a duplicator */ else if(enable_cu_speed) { - ctime= bsystem_time(ob, par, (float)G.scene->r.cfra, 0.0); + ctime= bsystem_time(ob, (float)G.scene->r.cfra, 0.0); if(calc_ipo_spec(cu->ipo, CU_SPEED, &ctime)==0) { ctime /= cu->pathlen; @@ -1490,7 +1685,7 @@ void where_is_object_time(Object *ob, float ctime) if(ob==NULL) return; /* this is needed to be able to grab objects with ipos, otherwise it always freezes them */ - stime= bsystem_time(ob, 0, ctime, 0.0); + stime= bsystem_time(ob, ctime, 0.0); if(stime != ob->ctime) { ob->ctime= stime; @@ -1502,12 +1697,14 @@ void where_is_object_time(Object *ob, float ctime) else do_all_object_actions(ob); - /* do constraint ipos ..., note it needs stime */ - do_constraint_channels(&ob->constraints, &ob->constraintChannels, stime); + /* do constraint ipos ..., note it needs stime (0 = all ipos) */ + do_constraint_channels(&ob->constraints, &ob->constraintChannels, stime, 0); } else { /* but, the drivers have to be done */ if(ob->ipo) do_ob_ipodrivers(ob, ob->ipo, stime); + /* do constraint ipos ..., note it needs stime (1 = only drivers ipos) */ + do_constraint_channels(&ob->constraints, &ob->constraintChannels, stime, 1); } if(ob->parent) { @@ -1560,9 +1757,18 @@ void where_is_object_time(Object *ob, float ctime) } - /* constraints need ctime, not stime. it calls where_is_object_time and bsystem_time */ - solve_constraints (ob, TARGET_OBJECT, NULL, ctime); - + /* solve constraints */ + if (ob->constraints.first) { + bConstraintOb *cob; + + cob= constraints_make_evalob(ob, NULL, CONSTRAINT_OBTYPE_OBJECT); + + /* constraints need ctime, not stime. Some call where_is_object_time and bsystem_time */ + solve_constraints (&ob->constraints, cob, ctime); + + constraints_clear_evalob(cob); + } + if(ob->scriptlink.totscript && !during_script()) { if (G.f & G_DOSCRIPTLINKS) BPY_do_pyscript((ID *)ob, SCRIPT_REDRAW); } @@ -1728,132 +1934,26 @@ for a lamp that is the child of another object */ if(ob->track) solve_tracking(ob, ob->track->obmat); - solve_constraints(ob, TARGET_OBJECT, NULL, G.scene->r.cfra); + /* solve constraints */ + if (ob->constraints.first) { + bConstraintOb *cob; + + cob= constraints_make_evalob(ob, NULL, CONSTRAINT_OBTYPE_OBJECT); + solve_constraints (&ob->constraints, cob, G.scene->r.cfra); + constraints_clear_evalob(cob); + } /* WATCH IT!!! */ ob->ipo= ipo; - -} -extern void Mat4BlendMat4(float out[][4], float dst[][4], float src[][4], float srcweight); - -void solve_constraints (Object *ob, short obtype, void *obdata, float ctime) -{ - bConstraint *con; - float tmat[4][4], focusmat[4][4], lastmat[4][4]; - int i, clear=1, tot=0; - float a=0; - float aquat[4], quat[4]; - float aloc[3], loc[3]; - float asize[3], size[3]; - float oldmat[4][4]; - float smat[3][3], rmat[3][3], mat[3][3]; - float enf; - - for (con = ob->constraints.first; con; con=con->next) { - // inverse kinematics is solved seperate - if (con->type==CONSTRAINT_TYPE_KINEMATIC) continue; - // and this we can skip completely - if (con->flag & CONSTRAINT_DISABLE) continue; - // local constraints are handled in armature.c only - if (con->flag & CONSTRAINT_LOCAL) continue; - - /* Clear accumulators if necessary*/ - if (clear) { - clear= 0; - a= 0; - tot= 0; - memset(aquat, 0, sizeof(float)*4); - memset(aloc, 0, sizeof(float)*3); - memset(asize, 0, sizeof(float)*3); - } - - enf = con->enforce; // value from ipos (from action channels) - - /* Get the targetmat */ - get_constraint_target_matrix(con, obtype, obdata, tmat, size, ctime); - - Mat4CpyMat4(focusmat, tmat); - - /* Extract the components & accumulate */ - Mat4ToQuat(focusmat, quat); - VECCOPY(loc, focusmat[3]); - Mat3CpyMat4(mat, focusmat); - Mat3ToSize(mat, size); - - a+= enf; - tot++; - - for(i=0; i<3; i++) { - aquat[i+1]+=(quat[i+1]) * enf; - aloc[i]+=(loc[i]) * enf; - asize[i]+=(size[i]-1.0f) * enf; - } - aquat[0]+=(quat[0])*enf; - Mat4CpyMat4(lastmat, focusmat); - - /* removed for now, probably becomes option? (ton) */ - - /* If the next constraint is not the same type (or there isn't one), - * then evaluate the accumulator & request a clear */ - if (TRUE) { //(!con->next)||(con->next && con->next->type!=con->type)) { - clear= 1; - Mat4CpyMat4(oldmat, ob->obmat); - - /* If we have several inputs, do a blend of them */ - if (tot) { - if (tot>1) { - if (a) { - for (i=0; i<3; i++) { - asize[i]=1.0f + (asize[i]/(a)); - aloc[i]=(aloc[i]/a); - } - - NormalQuat(aquat); - - QuatToMat3(aquat, rmat); - SizeToMat3(asize, smat); - Mat3MulMat3(mat, rmat, smat); - Mat4CpyMat3(focusmat, mat); - VECCOPY(focusmat[3], aloc); - - evaluate_constraint(con, ob, obtype, obdata, focusmat); - } - - } - /* If we only have one, blend with the current obmat */ - else { - float solution[4][4]; - float delta[4][4]; - float imat[4][4]; - float identity[4][4]; - - /* solve the constraint then blend it to the previous one */ - evaluate_constraint(con, ob, obtype, obdata, lastmat); - - Mat4CpyMat4 (solution, ob->obmat); - - /* Interpolate the enforcement */ - Mat4Invert (imat, oldmat); - Mat4MulMat4 (delta, solution, imat); - - if (a<1.0) { - Mat4One(identity); - Mat4BlendMat4(delta, identity, delta, a); - } - Mat4MulMat4 (ob->obmat, delta, oldmat); - } - } - } - } } /* for calculation of the inverse parent transform, only used for editor */ void what_does_parent(Object *ob) { - clear_workob(); Mat4One(workob.obmat); Mat4One(workob.parentinv); + Mat4One(workob.constinv); workob.parent= ob->parent; workob.track= ob->track; @@ -1868,7 +1968,7 @@ void what_does_parent(Object *ob) workob.constraints.first = ob->constraints.first; workob.constraints.last = ob->constraints.last; - strcpy (workob.parsubstr, ob->parsubstr); + strcpy(workob.parsubstr, ob->parsubstr); where_is_object(&workob); } @@ -2071,8 +2171,28 @@ void object_handle_update(Object *ob) where_is_pose(ob); } } + + if(ob->particlesystem.first) { + ParticleSystem *tpsys, *psys; + + psys= ob->particlesystem.first; + while(psys) { + if(psys->flag & PSYS_ENABLED) { + particle_system_update(ob, psys); + psys= psys->next; + } + else if(psys->flag & PSYS_DELETE) { + tpsys=psys->next; + BLI_remlink(&ob->particlesystem, psys); + psys_free(ob,psys); + psys= tpsys; + } + else + psys= psys->next; + } + } } - + /* the no-group proxy case, we call update */ if(ob->proxy && ob->proxy_group==NULL) { /* set pointer in library proxy target, for copying, but restore it */ diff --git a/source/blender/blenkernel/intern/particle.c b/source/blender/blenkernel/intern/particle.c new file mode 100644 index 00000000000..c0de1901f69 --- /dev/null +++ b/source/blender/blenkernel/intern/particle.c @@ -0,0 +1,2978 @@ +/* particle.c + * + * + * $Id: particle.c $ + * + * ***** BEGIN GPL/BL DUAL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. The Blender + * Foundation also sells licenses for use in proprietary software under + * the Blender License. See http://www.blender.org/BL/ for information + * about this. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * The Original Code is Copyright (C) 2007 by Janne Karhu. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): none yet. + * + * ***** END GPL/BL DUAL LICENSE BLOCK ***** + */ + +#include <stdlib.h> +#include <math.h> +#include <string.h> + +#include "MEM_guardedalloc.h" + +#include "DNA_scene_types.h" +#include "DNA_particle_types.h" +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_modifier_types.h" +#include "DNA_object_force.h" +#include "DNA_texture_types.h" +#include "DNA_material_types.h" +#include "DNA_object_types.h" +#include "DNA_curve_types.h" +#include "DNA_key_types.h" + +#include "BLI_arithb.h" +#include "BLI_blenlib.h" +#include "BLI_dynstr.h" +#include "BLI_kdtree.h" +#include "BLI_linklist.h" +#include "BLI_rand.h" + +#include "BKE_anim.h" + +#include "BKE_global.h" +#include "BKE_main.h" +#include "BKE_lattice.h" +#include "BKE_utildefines.h" +#include "BKE_displist.h" +#include "BKE_particle.h" +#include "BKE_DerivedMesh.h" +#include "BKE_ipo.h" +#include "BKE_object.h" +#include "BKE_softbody.h" +#include "BKE_material.h" +#include "BKE_key.h" +#include "BKE_library.h" +#include "BKE_depsgraph.h" +#include "BKE_bad_level_calls.h" +#include "BKE_modifier.h" + +#include "blendef.h" +#include "RE_render_ext.h" + +static void key_from_object(Object *ob, ParticleKey *key); +static void get_cpa_texture(DerivedMesh *dm, Material *ma, int face_index, + float *fuv, float *orco, ParticleTexture *ptex, int event); + +/* few helpers for countall etc. */ +int count_particles(ParticleSystem *psys){ + ParticleSettings *part=psys->part; + ParticleData *pa; + int tot=0,p; + + for(p=0,pa=psys->particles; p<psys->totpart; p++,pa++){ + if(pa->alive == PARS_KILLED); + else if(pa->alive == PARS_UNBORN && (part->flag & PART_UNBORN)==0); + else if(pa->alive == PARS_DEAD && (part->flag & PART_DIED)==0); + else if(pa->flag & (PARS_UNEXIST+PARS_NO_DISP)); + else tot++; + } + return tot; +} +int count_particles_mod(ParticleSystem *psys, int totgr, int cur){ + ParticleSettings *part=psys->part; + ParticleData *pa; + int tot=0,p; + + for(p=0,pa=psys->particles; p<psys->totpart; p++,pa++){ + if(pa->alive == PARS_KILLED); + else if(pa->alive == PARS_UNBORN && (part->flag & PART_UNBORN)==0); + else if(pa->alive == PARS_DEAD && (part->flag & PART_DIED)==0); + else if(pa->flag & (PARS_UNEXIST+PARS_NO_DISP)); + else if(p%totgr==cur) tot++; + } + return tot; +} +int psys_count_keys(ParticleSystem *psys) +{ + ParticleData *pa; + int i, totpart=psys->totpart, totkey=0; + + for(i=0, pa=psys->particles; i<totpart; i++, pa++) + totkey += pa->totkey; + + return totkey; +} +/* remember to free the pointer returned from this! */ +char *psys_menu_string(Object *ob, int for_sb) +{ + ParticleSystem *psys; + DynStr *ds; + char *str, num[6]; + int i; + + ds = BLI_dynstr_new(); + + if(for_sb) + BLI_dynstr_append(ds, "|Object%x-1"); + + for(i=0,psys=ob->particlesystem.first; psys; i++,psys=psys->next){ + + BLI_dynstr_append(ds, "|"); + sprintf(num,"%i. ",i+1); + BLI_dynstr_append(ds, num); + BLI_dynstr_append(ds, psys->part->id.name+2); + sprintf(num,"%%x%i",i+1); + BLI_dynstr_append(ds, num); + } + + str = BLI_dynstr_get_cstring(ds); + + BLI_dynstr_free(ds); + + return str; +} +/************************************************/ +/* Getting stuff */ +/************************************************/ +/* get object's active particle system safely */ +ParticleSystem *psys_get_current(Object *ob) +{ + ParticleSystem *psys; + if(ob==0) return 0; + + for(psys=ob->particlesystem.first; psys; psys=psys->next){ + if(psys->flag & PSYS_CURRENT) + return psys; + } + + return 0; +} +short psys_get_current_num(Object *ob) +{ + ParticleSystem *psys; + short i; + + if(ob==0) return 0; + + for(psys=ob->particlesystem.first, i=0; psys; psys=psys->next, i++) + if(psys->flag & PSYS_CURRENT) + return i; + + return i; +} +/* change object's active particle system */ +void psys_change_act(void *ob_v, void *act_v) +{ + Object *ob = ob_v; + ParticleSystem *npsys, *psys; + short act = *((short*)act_v)-1; + + if(act>=0){ + npsys=BLI_findlink(&ob->particlesystem,act); + psys=psys_get_current(ob); + + if(psys) + psys->flag &= ~PSYS_CURRENT; + if(npsys) + npsys->flag |= PSYS_CURRENT; + } +} +Object *psys_get_lattice(Object *ob, ParticleSystem *psys) +{ + Object *lattice=0; + + if(!psys_in_edit_mode(psys)==0){ + + ModifierData *md = (ModifierData*)psys_get_modifier(ob,psys); + + for(; md; md=md->next){ + if(md->type==eModifierType_Lattice){ + LatticeModifierData *lmd = (LatticeModifierData *)md; + lattice=lmd->object; + break; + } + } + if(lattice) + init_latt_deform(lattice,0); + } + + return lattice; +} +void psys_disable_all(Object *ob) +{ + ParticleSystem *psys=ob->particlesystem.first; + + for(; psys; psys=psys->next) + psys->flag &= ~PSYS_ENABLED; +} +void psys_enable_all(Object *ob) +{ + ParticleSystem *psys=ob->particlesystem.first; + + for(; psys; psys=psys->next) + psys->flag |= PSYS_ENABLED; +} +int psys_ob_has_hair(Object *ob) +{ + ParticleSystem *psys = ob->particlesystem.first; + + for(; psys; psys=psys->next) + if(psys->part->type == PART_HAIR) + return 1; + + return 0; +} +int psys_in_edit_mode(ParticleSystem *psys) +{ + return ((G.f & G_PARTICLEEDIT) && psys==psys_get_current(OBACT) && psys->edit); +} + +/************************************************/ +/* Freeing stuff */ +/************************************************/ +void psys_free_settings(ParticleSettings *part) +{ + if(part->pd) + MEM_freeN(part->pd); +} +void free_hair(ParticleSystem *psys) +{ + ParticleData *pa; + int i, totpart=psys->totpart; + + for(i=0, pa=psys->particles; i<totpart; i++, pa++) { + if(pa->hair) + MEM_freeN(pa->hair); + pa->hair = NULL; + } + + psys->flag &= ~PSYS_HAIR_DONE; +} +void free_keyed_keys(ParticleSystem *psys) +{ + if(psys->particles && psys->particles->keys) + MEM_freeN(psys->particles->keys); +} +void free_child_path_cache(ParticleSystem *psys) +{ + + if(psys->childcache){ + if(psys->childcache[0]) + MEM_freeN(psys->childcache[0]); + + MEM_freeN(psys->childcache); + + psys->childcache = NULL; + psys->totchildcache = 0; + } +} +void psys_free_path_cache(ParticleSystem *psys) +{ + if(psys->pathcache){ + if(psys->pathcache[0]) + MEM_freeN(psys->pathcache[0]); + + MEM_freeN(psys->pathcache); + + psys->pathcache = NULL; + psys->totcached = 0; + } + free_child_path_cache(psys); +} +/* free everything */ +void psys_free(Object *ob, ParticleSystem * psys) +{ + if(psys){ + if(ob->particlesystem.first == NULL && G.f & G_PARTICLEEDIT) + G.f &= ~G_PARTICLEEDIT; + + psys_free_path_cache(psys); + + free_hair(psys); + + free_keyed_keys(psys); + + PE_free_particle_edit(psys); + + if(psys->particles){ + MEM_freeN(psys->particles); + psys->particles = 0; + psys->totpart = 0; + } + + if(psys->child){ + MEM_freeN(psys->child); + psys->child = 0; + psys->totchild = 0; + } + + if(psys->effectors.first) + psys_end_effectors(psys); + + if(psys->part){ + psys->part->id.us--; + psys->part=0; + } + + if(psys->soft){ + sbFree(psys->soft); + psys->soft = 0; + } + + MEM_freeN(psys); + } +} + +/************************************************/ +/* Interpolated Particles */ +/************************************************/ +static float interpolate_particle_value(float v1, float v2, float v3, float v4, float *w, int four) +{ + float value; + + value= w[0]*v1 + w[1]*v2 + w[2]*v3; + if(four) + value += w[3]*v4; + + return value; +} +static void weighted_particle_vector(float *v1, float *v2, float *v3, float *v4, float *weights, float *vec) +{ + vec[0]= weights[0]*v1[0] + weights[1]*v2[0] + weights[2]*v3[0] + weights[3]*v4[0]; + vec[1]= weights[0]*v1[1] + weights[1]*v2[1] + weights[2]*v3[1] + weights[3]*v4[1]; + vec[2]= weights[0]*v1[2] + weights[1]*v2[2] + weights[2]*v3[2] + weights[3]*v4[2]; +} +static void interpolate_particle(short type, ParticleKey keys[4], float dt, ParticleKey *result) +{ + float t[4]; + + if(type<0) { + VecfCubicInterpol(keys[1].co, keys[1].vel, keys[2].co, keys[2].vel, dt, result->co, result->vel); + } + else { + set_four_ipo(dt, t, type); + + weighted_particle_vector(keys[0].co, keys[1].co, keys[2].co, keys[3].co, t, result->co); + + //if(ve){ + // if(dt>0.999f){ + // set_four_ipo(dt+0.001f,t,ipo_type); + // weighted_particle_vector(key0->co,key1->co,key2->co,key3->co,t,temp); + // VECSUB(ve,temp,co); + // } + // else{ + // set_four_ipo(dt-0.001f,t,ipo_type); + // weighted_particle_vector(key0->co,key1->co,key2->co,key3->co,t,temp); + // VECSUB(ve,co,temp); + // } + //} + } +} + + + +/************************************************/ +/* Particles on a dm */ +/************************************************/ +/* interpolate a location on a face based on face coordinates */ +void psys_interpolate_face(MVert *mvert, MFace *mface, MTFace *tface, float *w, float *vec, float *nor, float *utan, float *vtan){ + float *v1=0, *v2=0, *v3=0, *v4=0; + float e1[3],e2[3],s1,s2,t1,t2; + float *uv1, *uv2, *uv3, *uv4; + float n1[3], n2[3], n3[3], n4[3]; + float tuv[4][2]; + + v1= (mvert+mface->v1)->co; + v2= (mvert+mface->v2)->co; + v3= (mvert+mface->v3)->co; + VECCOPY(n1,(mvert+mface->v1)->no); + VECCOPY(n2,(mvert+mface->v2)->no); + VECCOPY(n3,(mvert+mface->v3)->no); + Normalize(n1); + Normalize(n2); + Normalize(n3); + + if(mface->v4) { + v4= (mvert+mface->v4)->co; + VECCOPY(n4,(mvert+mface->v4)->no); + Normalize(n4); + + vec[0]= w[0]*v1[0] + w[1]*v2[0] + w[2]*v3[0] + w[3]*v4[0]; + vec[1]= w[0]*v1[1] + w[1]*v2[1] + w[2]*v3[1] + w[3]*v4[1]; + vec[2]= w[0]*v1[2] + w[1]*v2[2] + w[2]*v3[2] + w[3]*v4[2]; + + if(nor){ + if(mface->flag & ME_SMOOTH){ + nor[0]= w[0]*n1[0] + w[1]*n2[0] + w[2]*n3[0] + w[3]*n4[0]; + nor[1]= w[0]*n1[1] + w[1]*n2[1] + w[2]*n3[1] + w[3]*n4[1]; + nor[2]= w[0]*n1[2] + w[1]*n2[2] + w[2]*n3[2] + w[3]*n4[2]; + } + else + CalcNormFloat4(v1,v2,v3,v4,nor); + } + } + else { + vec[0]= w[0]*v1[0] + w[1]*v2[0] + w[2]*v3[0]; + vec[1]= w[0]*v1[1] + w[1]*v2[1] + w[2]*v3[1]; + vec[2]= w[0]*v1[2] + w[1]*v2[2] + w[2]*v3[2]; + + if(nor){ + if(mface->flag & ME_SMOOTH){ + nor[0]= w[0]*n1[0] + w[1]*n2[0] + w[2]*n3[0]; + nor[1]= w[0]*n1[1] + w[1]*n2[1] + w[2]*n3[1]; + nor[2]= w[0]*n1[2] + w[1]*n2[2] + w[2]*n3[2]; + } + else + CalcNormFloat(v1,v2,v3,nor); + } + } + + /* calculate tangent vectors */ + if(utan && vtan){ + if(tface){ + uv1= tface->uv[0]; + uv2= tface->uv[1]; + uv3= tface->uv[2]; + uv4= tface->uv[3]; + } + else{ + uv1= tuv[0]; uv2= tuv[1]; uv3= tuv[2]; uv4= tuv[3]; + spheremap(v1[0], v1[1], v1[2], uv1, uv1+1); + spheremap(v2[0], v2[1], v2[2], uv2, uv2+1); + spheremap(v3[0], v3[1], v3[2], uv3, uv3+1); + if(v4) + spheremap(v4[0], v4[1], v4[2], uv4, uv4+1); + } + + if(v4){ + s1= uv3[0] - uv1[0]; + s2= uv4[0] - uv1[0]; + + t1= uv3[1] - uv1[1]; + t2= uv4[1] - uv1[1]; + + VecSubf(e1, v3, v1); + VecSubf(e2, v4, v1); + } + else{ + s1= uv2[0] - uv1[0]; + s2= uv3[0] - uv1[0]; + + t1= uv2[1] - uv1[1]; + t2= uv3[1] - uv1[1]; + + VecSubf(e1, v2, v1); + VecSubf(e2, v3, v1); + } + + vtan[0] = (s1*e2[0] - s2*e1[0]); + vtan[1] = (s1*e2[1] - s2*e1[1]); + vtan[2] = (s1*e2[2] - s2*e1[2]); + + utan[0] = (t1*e2[0] - t2*e1[0]); + utan[1] = (t1*e2[1] - t2*e1[1]); + utan[2] = (t1*e2[2] - t2*e1[2]); + } +} +void psys_interpolate_uvs(MTFace *tface, int quad, float *w, float *uvco){ + float v10= tface->uv[0][0]; + float v11= tface->uv[0][1]; + float v20= tface->uv[1][0]; + float v21= tface->uv[1][1]; + float v30= tface->uv[2][0]; + float v31= tface->uv[2][1]; + float v40,v41; + + if(quad) { + v40= tface->uv[3][0]; + v41= tface->uv[3][1]; + + uvco[0]= w[0]*v10 + w[1]*v20 + w[2]*v30 + w[3]*v40; + uvco[1]= w[0]*v11 + w[1]*v21 + w[2]*v31 + w[3]*v41; + } + else { + uvco[0]= w[0]*v10 + w[1]*v20 + w[2]*v30; + uvco[1]= w[0]*v11 + w[1]*v21 + w[2]*v31; + } +} +float psys_interpolate_value_from_verts(DerivedMesh *dm, short from, int index, float *fw, float *values) +{ + if(values==0) + return 0.0; + + switch(from){ + case PART_FROM_VERT: + return values[index]; + case PART_FROM_FACE: + case PART_FROM_VOLUME: + { + MFace *mf=dm->getFaceData(dm,index,CD_MFACE); + return interpolate_particle_value(values[mf->v1],values[mf->v2],values[mf->v3],values[mf->v4],fw,mf->v4); + } + + } + return 0.0; +} + +/* conversion of pa->fw to origspace layer coordinates */ +static void psys_w_to_origspace(float *w, float *uv) +{ + uv[0]= w[1] + w[2]; + uv[1]= w[2] + w[3]; +} + +/* conversion of pa->fw to weights in face from origspace */ +static void psys_origspace_to_w(OrigSpaceFace *osface, int quad, float *w, float *neww) +{ + float v[4][3], co[3]; + + v[0][0]= osface->uv[0][0]; v[0][1]= osface->uv[0][1]; v[0][2]= 0.0f; + v[1][0]= osface->uv[1][0]; v[1][1]= osface->uv[1][1]; v[1][2]= 0.0f; + v[2][0]= osface->uv[2][0]; v[2][1]= osface->uv[2][1]; v[2][2]= 0.0f; + + psys_w_to_origspace(w, co); + co[2]= 0.0f; + + if(quad) { + v[3][0]= osface->uv[3][0]; v[3][1]= osface->uv[3][1]; v[3][2]= 0.0f; + MeanValueWeights(v, 4, co, neww); + } + else { + MeanValueWeights(v, 3, co, neww); + neww[3]= 0.0f; + } +} + +/* find the derived mesh face for a particle, set the mf passed. +This is slow, can be optimized but only for many lookups, return the face lookup index*/ +int psys_particle_dm_face_lookup(Object *ob, DerivedMesh *dm, int index, float *fw, struct LinkNode *node) +{ + Mesh *me= (Mesh*)ob->data; + MFace *mface; + OrigSpaceFace *osface; + int *origindex; + int quad, findex, totface; + float uv[2], (*faceuv)[2]; + + mface = dm->getFaceDataArray(dm, CD_MFACE); + origindex = dm->getFaceDataArray(dm, CD_ORIGINDEX); + osface = dm->getFaceDataArray(dm, CD_ORIGSPACE); + + totface = dm->getNumFaces(dm); + + if(osface==NULL || origindex==NULL) { + /* Assume we dont need osface data */ + if (index <totface) { + printf("\tNO CD_ORIGSPACE, assuming not needed\n"); + return index; + } else { + printf("\tNO CD_ORIGSPACE, error out of range\n"); + return DMCACHE_NOTFOUND; + } + } + else if(index >= me->totface) + return DMCACHE_NOTFOUND; /* index not in the original mesh */ + + psys_w_to_origspace(fw, uv); + + if(node) { /* we have a linked list of faces that we use, faster! */ + for(;node; node=node->next) { + findex= (int)node->link; + faceuv= osface[findex].uv; + quad= mface[findex].v4; + + /* check that this intersects - Its possible this misses :/ - + * could also check its not between */ + if(quad) { + if(IsectPQ2Df(uv, faceuv[0], faceuv[1], faceuv[2], faceuv[3])) + return findex; + } + else if(IsectPT2Df(uv, faceuv[0], faceuv[1], faceuv[2])) + return findex; + } + } + else { /* if we have no node, try every face */ + for(findex=0; findex<totface; findex++) { + if(origindex[findex] == index) { + faceuv= osface[findex].uv; + quad= mface[findex].v4; + + /* check that this intersects - Its possible this misses :/ - + * could also check its not between */ + if(quad) { + if(IsectPQ2Df(uv, faceuv[0], faceuv[1], faceuv[2], faceuv[3])) + return findex; + } + else if(IsectPT2Df(uv, faceuv[0], faceuv[1], faceuv[2])) + return findex; + } + } + } + + return DMCACHE_NOTFOUND; +} + +/* interprets particle data to get a point on a mesh in object space */ +#define PARTICLE_ERROR(_nor, _vec) _vec[0]=_vec[1]=_vec[2]=0.0; if(_nor){ _nor[0]=_nor[1]=0.0; _nor[2]=1.0; } +void psys_particle_on_dm(Object *ob, DerivedMesh *dm, int from, int index, int index_dmcache, float *fw, float foffset, float *vec, float *nor, float *utan, float *vtan) +{ + if(index < 0){ /* 'no dm' error has happened! */ + PARTICLE_ERROR(nor, vec); + return; + } + + if (dm->deformedOnly || index_dmcache == DMCACHE_ISCHILD) { + /* this works for meshes with deform verts only - constructive modifiers wont work properly*/ + float temp1[3]; + + if(index_dmcache == DMCACHE_ISCHILD && index >= dm->getNumFaces(dm)) { + PARTICLE_ERROR(nor, vec); + return; + } + + if(from == PART_FROM_VERT) { + dm->getVertCo(dm,index,vec); + if(nor){ + dm->getVertNo(dm,index,nor); + Normalize(nor); + } + } + else { /* PART_FROM_FACE / PART_FROM_VOLUME */ + MFace *mface=dm->getFaceData(dm,index,CD_MFACE); + MTFace *mtface=0; + MVert *mvert=dm->getVertDataArray(dm,CD_MVERT); + int uv_index=CustomData_get_active_layer_index(&dm->faceData,CD_MTFACE); + + if(uv_index>=0){ + CustomDataLayer *layer=&dm->faceData.layers[uv_index]; + mtface= &((MTFace*)layer->data)[index]; + } + + if(from==PART_FROM_VOLUME){ + psys_interpolate_face(mvert,mface,mtface,fw,vec,temp1,utan,vtan); + if(nor) + VECCOPY(nor,temp1); + Normalize(temp1); + VecMulf(temp1,-foffset); + VECADD(vec,vec,temp1); + } + else + psys_interpolate_face(mvert,mface,mtface,fw,vec,nor,utan,vtan); + } + } else { + /* Need to support constructive modifiers, this is a bit more tricky + we need a customdata layer like UV's so we can position the particle */ + + /* Only face supported at the moment */ + if (from==PART_FROM_FACE) { + /* find a face on the derived mesh that uses this face */ + Mesh *me= (Mesh*)ob->data; + MVert *mvert; + MFace *mface; + MTFace *mtface; + OrigSpaceFace *osface; + int *origindex; + float fw_mod[4]; + int i, totface; + + mvert= dm->getVertDataArray(dm,CD_MVERT); + + osface= dm->getFaceDataArray(dm, CD_ORIGSPACE); + origindex= dm->getFaceDataArray(dm, CD_ORIGINDEX); + + /* For this to work we need origindex and OrigSpace coords */ + if(origindex==NULL || osface==NULL || index>=me->totface) { + PARTICLE_ERROR(nor, vec); + return; + } + + if (index_dmcache == DMCACHE_NOTFOUND) + i = psys_particle_dm_face_lookup(ob, dm, index, fw, (LinkNode*)NULL); + else + i = index_dmcache; + + totface = dm->getNumFaces(dm); + + /* Any time this happens, and the face has not been removed, + * its a BUG watch out for this error! */ + if (i==-1) { + printf("Cannot find original face %i\n", index); + PARTICLE_ERROR(nor, vec); + return; + } + else if(i >= totface) + return; + + mface= dm->getFaceData(dm, i, CD_MFACE); + mtface= dm->getFaceData(dm, i, CD_MTFACE); + osface += i; + + /* we need to modify the original weights to become weights for + * the derived mesh face */ + psys_origspace_to_w(osface, mface->v4, fw, fw_mod); + psys_interpolate_face(mvert,mface,mtface,fw_mod,vec,nor,utan,vtan); + } + else { + /* TODO PARTICLE - support verts and volume */ + PARTICLE_ERROR(nor, vec); + } + } +} +#undef PARTICLE_ERROR + +ParticleSystemModifierData *psys_get_modifier(Object *ob, ParticleSystem *psys) +{ + ModifierData *md; + ParticleSystemModifierData *psmd; + + for(md=ob->modifiers.first; md; md=md->next){ + if(md->type==eModifierType_ParticleSystem){ + psmd= (ParticleSystemModifierData*) md; + if(psmd->psys==psys){ + return psmd; + } + } + } + return 0; +} +/************************************************/ +/* Particles on a shape */ +/************************************************/ +/* ready for future use */ +void psys_particle_on_shape(int distr, int index, float *fuv, float *vec, float *nor, float *utan, float *vtan) +{ + /* TODO */ + float zerovec[3]={0.0f,0.0f,0.0f}; + if(vec){ + VECCOPY(vec,zerovec); + } + if(nor){ + VECCOPY(nor,zerovec); + } + if(utan){ + VECCOPY(utan,zerovec); + } + if(vtan){ + VECCOPY(vtan,zerovec); + } +} +/************************************************/ +/* Particles on emitter */ +/************************************************/ +void psys_particle_on_emitter(Object *ob, ParticleSystemModifierData *psmd, int from, int index, int index_dmcache, float *fuv, float foffset, float *vec, float *nor, float *utan, float *vtan){ + if(psmd){ + if(psmd->psys->part->distr==PART_DISTR_GRID){ + if(vec){ + VECCOPY(vec,fuv); + } + return; + } + /* we cant use the num_dmcache */ + psys_particle_on_dm(ob, psmd->dm,from,index,index_dmcache,fuv,foffset,vec,nor,utan,vtan); + } + else + psys_particle_on_shape(from,index,fuv,vec,nor,utan,vtan); + +} +/************************************************/ +/* 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; + int i; + + if(dvert) { + dw= dvert->dw; + for(i= dvert->totweight; i>0; i--, dw++) { + if(dw->def_nr == group) return dw->weight; + if(i==1) break; /*otherwise dw will point to somewhere it shouldn't*/ + } + } + return 0.0; +} +static void do_prekink(ParticleKey *state, ParticleKey *par, float *par_rot, float time, float freq, float shape, float amplitude, short type, short axis, float obmat[][4]) +{ + float vec[3]={0.0,0.0,0.0}, q1[4]={1,0,0,0},*q2; + float t; + + CLAMP(time,0.0,1.0); + + if(shape!=0.0f && type!=PART_KINK_BRAID) { + if(shape<0.0f) + time= (float)pow(time, 1.0+shape); + else + time= (float)pow(time, 1.0/(1.0-shape)); + } + + t=time; + + t*=(float)M_PI*freq; + + if(par==0) return; + + switch(type){ + case PART_KINK_CURL: + vec[axis]=1.0; + if(par_rot) + q2=par_rot; + else{ + q2=vectoquat(par->vel,axis,(axis+1)%3); + } + QuatMulVecf(q2,vec); + VecMulf(vec,amplitude); + VECADD(state->co,state->co,vec); + + VECSUB(vec,state->co,par->co); + + if(t!=0.0) + VecRotToQuat(par->vel,t,q1); + + QuatMulVecf(q1,vec); + + VECADD(state->co,par->co,vec); + break; + case PART_KINK_RADIAL: + VECSUB(vec,state->co,par->co); + + Normalize(vec); + VecMulf(vec,amplitude*(float)sin(t)); + + VECADD(state->co,state->co,vec); + break; + case PART_KINK_WAVE: + vec[axis]=1.0; + if(obmat) + Mat4MulVecfl(obmat,vec); + + if(par_rot) + QuatMulVecf(par_rot,vec); + + Projf(q1,vec,par->vel); + + VECSUB(vec,vec,q1); + Normalize(vec); + + VecMulf(vec,amplitude*(float)sin(t)); + + VECADD(state->co,state->co,vec); + break; + case PART_KINK_BRAID: + if(par){ + float y_vec[3]={0.0,1.0,0.0}; + float z_vec[3]={0.0,0.0,1.0}; + float vec_from_par[3], vec_one[3], radius, state_co[3]; + float inp_y,inp_z,length; + + if(par_rot) + q2=par_rot; + else + q2=vectoquat(par->vel,axis,(axis+1)%3); + QuatMulVecf(q2,y_vec); + QuatMulVecf(q2,z_vec); + + VECSUB(vec_from_par,state->co,par->co); + VECCOPY(vec_one,vec_from_par); + radius=Normalize(vec_one); + + inp_y=Inpf(y_vec,vec_one); + inp_z=Inpf(z_vec,vec_one); + + if(inp_y>0.5){ + VECCOPY(state_co,y_vec); + + VecMulf(y_vec,amplitude*(float)cos(t)); + VecMulf(z_vec,amplitude/2.0f*(float)sin(2.0f*t)); + } + else if(inp_z>0.0){ + VECCOPY(state_co,z_vec); + VecMulf(state_co,(float)sin(M_PI/3.0f)); + VECADDFAC(state_co,state_co,y_vec,-0.5f); + + VecMulf(y_vec,-amplitude*(float)cos(t + M_PI/3.0f)); + VecMulf(z_vec,amplitude/2.0f*(float)cos(2.0f*t + M_PI/6.0f)); + } + else{ + VECCOPY(state_co,z_vec); + VecMulf(state_co,-(float)sin(M_PI/3.0f)); + VECADDFAC(state_co,state_co,y_vec,-0.5f); + + VecMulf(y_vec,amplitude*(float)-sin(t+M_PI/6.0f)); + VecMulf(z_vec,amplitude/2.0f*(float)-sin(2.0f*t+M_PI/3.0f)); + } + + VecMulf(state_co,amplitude); + VECADD(state_co,state_co,par->co); + VECSUB(vec_from_par,state->co,state_co); + + length=Normalize(vec_from_par); + VecMulf(vec_from_par,MIN2(length,amplitude/2.0f)); + + VECADD(state_co,par->co,y_vec); + VECADD(state_co,state_co,z_vec); + VECADD(state_co,state_co,vec_from_par); + + shape=(2.0f*(float)M_PI)*(1.0f+shape); + + if(t<shape){ + shape=t/shape; + shape=(float)sqrt((double)shape); + VecLerpf(state->co,state->co,state_co,shape); + } + else{ + VECCOPY(state->co,state_co); + } + } + break; + //case PART_KINK_ROT: + // vec[axis]=1.0; + + // QuatMulVecf(par->rot,vec); + + // VecMulf(vec,amplitude*(float)sin(t)); + + // VECADD(state->co,state->co,vec); + // break; + } +} +static void do_postkink(ParticleKey *state, ParticleKey *par, float *par_rot, float time, float freq, float shape, float amplitude, short type, short axis, float obmat[][4]) +{ + static ParticleKey first; + static float q[4]; + float vec[3]={0.0,0.0,0.0}; + float t; + + CLAMP(time,0.0,1.0); + + t=time; + + t*=(float)M_PI*freq; + + if(par==0) return; + + switch(type){ + case PART_KINK_ROLL: + if(time<(0.5+shape/2.0f)){ + float *q2; + memcpy(&first,state,sizeof(ParticleKey)); + Normalize(first.vel); + if(par_rot) + q2=par_rot; + else + q2=vectoquat(par->vel,axis,(axis+1)%3); + QUATCOPY(q,q2); + } + else{ + float fac; + shape=0.5f+shape/2.0f; + t-=(float)M_PI*(shape*freq + 0.5f); + + vec[axis]=1.0; + + QuatMulVecf(q,vec); + + fac=amplitude*(1.0f+((1.0f-time)/(1.0f-shape)*(float)sin(t))); + VECADDFAC(state->co,first.co,vec,fac); + fac=amplitude*((1.0f-time)/(1.0f-shape)*(float)cos(t)); + VECADDFAC(state->co,state->co,first.vel,fac); + } + break; + } +} +static void do_clump(ParticleKey *state, ParticleKey *par, float time, float clumpfac, float clumppow, float pa_clump) +{ + if(par && clumpfac!=0.0){ + float clump, cpow; + + if(clumppow<0.0) + cpow=1.0f+clumppow; + else + cpow=1.0f+9.0f*clumppow; + + if(clumpfac<0.0) /* clump roots instead of tips */ + clump = -clumpfac*pa_clump*(float)pow(1.0-(double)time,(double)cpow); + else + clump = clumpfac*pa_clump*(float)pow((double)time,(double)cpow); + VecLerpf(state->co,state->co,par->co,clump); + } +} +int do_guide(ParticleKey *state, int pa_num, float time, ListBase *lb) +{ + PartDeflect *pd; + ParticleEffectorCache *ec; + Object *eob; + Curve *cu; + ParticleKey key, par; + + float effect[3]={0.0,0.0,0.0}, distance, f_force, mindist, totforce=0.0; + float guidevec[4], guidedir[3], rot2[4], temp[3], angle, pa_loc[3], pa_zero[3]={0.0f,0.0f,0.0f}; + float veffect[3]={0.0,0.0,0.0}, guidetime; + + effect[0]=effect[1]=effect[2]=0.0; + + if(lb->first){ + for(ec = lb->first; ec; ec= ec->next){ + eob= ec->ob; + if(ec->type & PSYS_EC_EFFECTOR){ + pd=eob->pd; + if(pd->forcefield==PFIELD_GUIDE){ + cu = (Curve*)eob->data; + + distance=ec->distances[pa_num]; + mindist=pd->f_strength; + + VECCOPY(pa_loc, ec->locations+3*pa_num); + VECCOPY(pa_zero,pa_loc); + VECADD(pa_zero,pa_zero,ec->firstloc); + + guidetime=time/(1.0-pd->free_end); + + /* WARNING: bails out with continue here */ + if(((pd->flag & PFIELD_USEMAX) && distance>pd->maxdist) || guidetime>1.0f) continue; + + if(guidetime>1.0f) continue; + + /* calculate contribution factor for this guide */ + f_force=1.0f; + if(distance<=mindist); + else if(pd->flag & PFIELD_USEMAX) { + if(mindist>=pd->maxdist) f_force= 0.0f; + else if(pd->f_power!=0.0f){ + f_force= 1.0f - (distance-mindist)/(pd->maxdist - mindist); + f_force = (float)pow(f_force, pd->f_power); + } + } + else if(pd->f_power!=0.0f){ + f_force= 1.0f/(1.0f + distance-mindist); + f_force = (float)pow(f_force, pd->f_power); + } + + if(pd->flag & PFIELD_GUIDE_PATH_ADD) + where_on_path(eob, f_force*guidetime, guidevec, guidedir); + else + where_on_path(eob, guidetime, guidevec, guidedir); + + Mat4MulVecfl(ec->ob->obmat,guidevec); + Mat4Mul3Vecfl(ec->ob->obmat,guidedir); + + Normalize(guidedir); + + if(guidetime!=0.0){ + /* curve direction */ + Crossf(temp, ec->firstdir, guidedir); + angle=Inpf(ec->firstdir,guidedir)/(VecLength(ec->firstdir)); + angle=saacos(angle); + VecRotToQuat(temp,angle,rot2); + QuatMulVecf(rot2,pa_loc); + + /* curve tilt */ + VecRotToQuat(guidedir,guidevec[3]-ec->firstloc[3],rot2); + QuatMulVecf(rot2,pa_loc); + + //q=vectoquat(guidedir, pd->kink_axis, (pd->kink_axis+1)%3); + //QuatMul(par.rot,rot2,q); + } + //else{ + // par.rot[0]=1.0f; + // par.rot[1]=par.rot[2]=par.rot[3]=0.0f; + //} + + /* curve taper */ + if(cu->taperobj) + VecMulf(pa_loc,calc_taper(cu->taperobj,(int)(f_force*guidetime*100.0),100)); + /* TODO */ + //else{ + ///* curve size*/ + // calc_curve_subdiv_radius(cu,cu->nurb.first,((Nurb*)cu->nurb.first)-> + //} + par.co[0]=par.co[1]=par.co[2]=0.0f; + VECCOPY(key.co,pa_loc); + do_prekink(&key, &par, 0, guidetime, pd->kink_freq, pd->kink_shape, pd->kink_amp, pd->kink, pd->kink_axis, 0); + do_clump(&key, &par, guidetime, pd->clump_fac, pd->clump_pow, 1.0f); + do_postkink(&key, &par, 0, guidetime, pd->kink_freq, pd->kink_shape, pd->kink_amp, pd->kink, pd->kink_axis, 0); + VECCOPY(pa_loc,key.co); + + VECADD(pa_loc,pa_loc,guidevec); + VECSUB(pa_loc,pa_loc,pa_zero); + VECADDFAC(effect,effect,pa_loc,f_force); + VECADDFAC(veffect,veffect,guidedir,f_force); + totforce+=f_force; + } + } + } + + if(totforce!=0.0){ + if(totforce>1.0) + VecMulf(effect,1.0f/totforce); + CLAMP(totforce,0.0,1.0); + VECADD(effect,effect,pa_zero); + VecLerpf(state->co,state->co,effect,totforce); + + Normalize(veffect); + VecMulf(veffect,VecLength(state->vel)); + VECCOPY(state->vel,veffect); + return 1; + } + } + return 0; +} +static void do_rough(float *loc, float t, float fac, float size, float thres, ParticleKey *state) +{ + float rough[3]; + float rco[3]; + + if(thres!=0.0) + if((float)fabs((float)(-1.5+loc[0]+loc[1]+loc[2]))<1.5f*thres) return; + + VECCOPY(rco,loc); + VecMulf(rco,t); + 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); +} +static void do_rough_end(float *loc, float t, float fac, float shape, ParticleKey *state, ParticleKey *par) +{ + float rough[3], rnor[3]; + 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); +} +static int check_path_length(int k, int p, ParticleCacheKey **cache, ParticleCacheKey *state, float length, float *dvec) +{ + static float max_length = 1.0, cur_length = 0.0; + + if(k) { + if(cur_length + length > max_length){ + //if(p<totparent){ + // if(k<=(int)cache[totpart+p]->time){ + // /* parents need to be calculated fully first so that they don't mess up their children */ + // /* we'll make a note of where we got to though so that they're easy to finish later */ + // state->time=(max_length-cur_length)/length; + // cache[totpart+p]->time=(float)k; + // } + //} + //else{ + VecMulf(dvec, (max_length - cur_length) / length); + VECADD(state->co, (state - 1)->co, dvec); + cache[p]->steps = k; + /* something over the maximum step value */ + return k=100000; + //} + } + else { + cur_length+=length; + } + } + else {/* reset signal */ + max_length=length; + cur_length=0.0; + } + return k; +} +static void finalize_path_length(int p, ParticleCacheKey **cache) +{ + ParticleCacheKey *state = cache[p]; + float dvec[3]; + state += state->steps; + + VECSUB(dvec, state->co, (state - 1)->co); + VecMulf(dvec, state->steps); + VECADD(state->co, (state - 1)->co, dvec); +} +static void offset_child(ChildParticle *cpa, ParticleKey *par, ParticleKey *child, float flat, float radius) +{ + VECCOPY(child->co,cpa->fuv); + VecMulf(child->co,radius); + + child->co[0]*=flat; + + VECCOPY(child->vel,par->vel); + + QuatMulVecf(par->rot,child->co); + + QUATCOPY(child->rot,par->rot); + + VECADD(child->co,child->co,par->co); +} +float *psys_cache_vgroup(DerivedMesh *dm, ParticleSystem *psys, int vgroup) +{ + float *vg=0; + + if(psys->vgroup[vgroup]){ + MDeformVert *dvert = dm->getVertDataArray(dm, CD_MDEFORMVERT); + if(dvert){ + int totvert=dm->getNumVerts(dm), i; + vg=MEM_callocN(sizeof(float)*totvert, "vg_cache"); + if(psys->vg_neg&(1<<vgroup)){ + for(i=0; i<totvert; i++) + vg[i]=1.0f-vert_weight(dvert+i,psys->vgroup[vgroup]-1); + } + else{ + for(i=0; i<totvert; i++) + vg[i]=vert_weight(dvert+i,psys->vgroup[vgroup]-1); + } + } + } + return vg; +} +void psys_find_parents(Object *ob, ParticleSystemModifierData *psmd, ParticleSystem *psys) +{ + ParticleSettings *part=psys->part; + KDTree *tree; + ChildParticle *cpa; + int p, totparent,totchild=psys->totchild; + float co[3], *orcos=0; + int from=PART_FROM_FACE; + totparent=(int)(totchild*part->parents*0.3); + + tree=BLI_kdtree_new(totparent); + + for(p=0,cpa=psys->child; p<totparent; p++,cpa++){ + psys_particle_on_emitter(ob,psmd,from,cpa->num,-1,cpa->fuv,cpa->foffset,co,0,0,0); + BLI_kdtree_insert(tree, p, co, NULL); + } + + BLI_kdtree_balance(tree); + + for(; p<totchild; p++,cpa++){ + psys_particle_on_emitter(ob,psmd,from,cpa->num,DMCACHE_ISCHILD,cpa->fuv,cpa->foffset,co,0,0,0); + cpa->parent=BLI_kdtree_find_nearest(tree, co, NULL, NULL); + } + + BLI_kdtree_free(tree); + if(orcos) + MEM_freeN(orcos); +} +void psys_cache_child_paths(Object *ob, ParticleSystem *psys, float cfra, int editupdate) +{ + ParticleSettings *part = psys->part; + ParticleEditSettings *pset = &G.scene->toolsettings->particle; + ParticleSystemModifierData *psmd = psys_get_modifier(ob,psys); + ParticleData *pa; + ChildParticle *cpa; + ParticleCacheKey **cache = psys->childcache, **pcache = psys->pathcache; + ParticleCacheKey *tcache, *state, *par=0, *key[4]; + ParticleTexture ptex; + Material *ma = give_current_material(ob, part->omat); + + float length, pa_length = 1.0, pa_clump = 1.0, pa_kink = 1.0; + float pa_rough1 = 1.0, pa_rough2 = 1.0, pa_roughe = 1.0; + float t, rough_t; + float dvec[3], orco[3], ornor[3], imat[4][4]; + float *vg_length = 0, *vg_clump = 0, *vg_kink = 0; + float *vg_rough1 = 0, *vg_rough2 = 0, *vg_roughe = 0; + float cpa_1st[3]; + + int k, i, totparent=0, between=0, edit=0; + int steps = (int)pow(2.0,(double)part->draw_step); + int totchild = psys->totchild; + int cpa_num; short cpa_from; + + if(part->flag & PART_ANIM_BRANCHING) + BLI_srandom(31415926 + psys->seed + (int)cfra); + else + BLI_srandom(31415926 + psys->seed); + + /*---start figuring out what is actually wanted---*/ + if(psys_in_edit_mode(psys)){ + if(G.rendering==0 && (psys->edit==NULL || pset->flag & PE_SHOW_CHILD)==0) + totchild=0; + edit=1; + } + + if(totchild && part->from!=PART_FROM_PARTICLE && part->childtype==PART_CHILD_FACES){ + totparent=(int)(totchild*part->parents*0.3); + /* part->parents could still be 0 so we can't test with totparent */ + between=1; + } + + if(G.rendering) + steps=(int)pow(2.0,(double)part->ren_step); + else if(part->flag & PART_CHILD_RENDER){ + totchild=0; + } + else{ + totchild=(int)((float)totchild*(float)part->disp/100.0f); + totparent=MIN2(totparent,totchild); + } + + if(totchild==0) return; + + if(editupdate && psys->childcache && !(part->flag & PART_BRANCHING) && totchild == psys->totchildcache) { + cache = psys->childcache; + } + else { + /* clear out old and create new empty path cache */ + free_child_path_cache(psys); + + cache = psys->childcache = MEM_callocN(totchild*sizeof(void *), "Child path cache array"); + tcache = MEM_callocN(totchild * (steps + 1) * sizeof(ParticleCacheKey), "Child path cache"); + for(i=0; i<totchild; i++) + cache[i] = tcache + i * (steps + 1); + } + + psys->lattice = psys_get_lattice(ob,psys); + + /* cache all relevant vertex groups if they exist */ + if(part->from!=PART_FROM_PARTICLE){ + vg_length = psys_cache_vgroup(psmd->dm,psys,PSYS_VG_LENGTH); + vg_clump = psys_cache_vgroup(psmd->dm,psys,PSYS_VG_CLUMP); + vg_kink = psys_cache_vgroup(psmd->dm,psys,PSYS_VG_KINK); + vg_rough1 = psys_cache_vgroup(psmd->dm,psys,PSYS_VG_ROUGH1); + vg_rough2 = psys_cache_vgroup(psmd->dm,psys,PSYS_VG_ROUGH2); + vg_roughe = psys_cache_vgroup(psmd->dm,psys,PSYS_VG_ROUGHE); + } + + /* set correct ipo timing */ + if(part->flag&PART_ABS_TIME && part->ipo){ + calc_ipo(part->ipo, cfra); + execute_ipo((ID *)part, part->ipo); + } + + Mat4Invert(imat,ob->obmat); + + for(i=0,cpa=psys->child; i<totchild; i++, cpa++){ + int guided=0; + float *cpa_fuv=0; + float branch_begin=0.0f, branch_end=0.0f, branch_prob=0.0f; + float branchfac, rough_rand=0.0f; + + if(part->flag & PART_BRANCHING) { + branch_begin=BLI_frand(); + branch_end=branch_begin+(1.0f-branch_begin)*BLI_frand(); + branch_prob=BLI_frand(); + rough_rand=BLI_frand(); + } + + if(i<psys->totpart){ + branch_begin=0.0f; + branch_end=1.0f; + branch_prob=0.0f; + } + + if(between){ + int w, needupdate; + float foffset; + + if(editupdate && !(part->flag & PART_BRANCHING)) { + needupdate= 0; + w= 0; + while(w<4 && cpa->pa[w]>=0) { + if(psys->particles[cpa->pa[w]].flag & PARS_EDIT_RECALC) { + needupdate= 1; + break; + } + w++; + } + + if(!needupdate) + continue; + else + memset(cache[i], 0, sizeof(*cache[i])*(steps+1)); + } + + /* get parent paths */ + w= 0; + while(w<4 && cpa->pa[w]>=0){ + key[w] = pcache[cpa->pa[w]]; + w++; + } + + /* get the original coordinates (orco) for texture usage */ + cpa_num = cpa->num; + + foffset= cpa->foffset; + if(part->childtype == PART_CHILD_FACES) + foffset = -(2.0f + part->childspread); + cpa_fuv = cpa->fuv; + cpa_from = PART_FROM_FACE; + + psys_particle_on_emitter(ob,psmd,cpa_from,cpa_num,DMCACHE_ISCHILD,cpa->fuv,foffset,orco,ornor,0,0); + + /* we need to save the actual root position of the child for positioning it accurately to the surface of the emitter */ + VECCOPY(cpa_1st,orco); + Mat4MulVecfl(ob->obmat,cpa_1st); + + pa=0; + } + else{ + if(editupdate && !(part->flag & PART_BRANCHING)) { + if(!(psys->particles[cpa->parent].flag & PARS_EDIT_RECALC)) + continue; + + memset(cache[i], 0, sizeof(*cache[i])*(steps+1)); + } + + /* get the parent path */ + key[0]=pcache[cpa->parent]; + + /* get the original coordinates (orco) for texture usage */ + pa=psys->particles+cpa->parent; + + cpa_from=part->from; + cpa_num=pa->num; + cpa_fuv=pa->fuv; + + psys_particle_on_emitter(ob,psmd,cpa_from,cpa_num,DMCACHE_ISCHILD,cpa_fuv,pa->foffset,orco,ornor,0,0); + } + + cache[i]->steps = steps; + + /* correct child ipo timing */ + if((part->flag&PART_ABS_TIME)==0 && part->ipo){ + float dsta=part->end-part->sta; + calc_ipo(part->ipo, 100.0f*(cfra-(part->sta+dsta*cpa->rand[1]))/(part->lifetime*(1.0f - part->randlife*cpa->rand[0]))); + execute_ipo((ID *)part, part->ipo); + } + + /* get different child parameters from textures & vgroups */ + ptex.length=part->length*(1.0f - part->randlength*cpa->rand[0]); + ptex.clump=1.0; + ptex.kink=1.0; + + get_cpa_texture(psmd->dm,ma,cpa_num,cpa_fuv,orco,&ptex,MAP_PA_CACHE); + + pa_length=ptex.length; + pa_clump=ptex.clump; + pa_kink=ptex.kink; + pa_rough1=1.0; + pa_rough2=1.0; + pa_roughe=1.0; + + if(vg_length) + pa_length*=psys_interpolate_value_from_verts(psmd->dm,cpa_from,cpa_num,cpa_fuv,vg_length); + if(vg_clump) + pa_clump*=psys_interpolate_value_from_verts(psmd->dm,cpa_from,cpa_num,cpa_fuv,vg_clump); + if(vg_kink) + pa_kink*=psys_interpolate_value_from_verts(psmd->dm,cpa_from,cpa_num,cpa_fuv,vg_kink); + if(vg_rough1) + pa_rough1*=psys_interpolate_value_from_verts(psmd->dm,cpa_from,cpa_num,cpa_fuv,vg_rough1); + if(vg_rough2) + pa_rough2*=psys_interpolate_value_from_verts(psmd->dm,cpa_from,cpa_num,cpa_fuv,vg_rough2); + if(vg_roughe) + pa_roughe*=psys_interpolate_value_from_verts(psmd->dm,cpa_from,cpa_num,cpa_fuv,vg_roughe); + + /* create the child path */ + for(k=0,state=cache[i]; k<=steps; k++,state++){ + t=(float)k/(float)steps; + + if(between){ + int w=0; + + state->co[0] = state->co[1] = state->co[2] = 0.0f; + state->vel[0] = state->vel[1] = state->vel[2] = 0.0f; + + //QUATCOPY(state->rot,key[0]->rot); + + /* child position is the weighted sum of parent positions */ + while(w<4 && cpa->pa[w]>=0){ + state->co[0] += cpa->w[w] * key[w]->co[0]; + state->co[1] += cpa->w[w] * key[w]->co[1]; + state->co[2] += cpa->w[w] * key[w]->co[2]; + + state->vel[0] += cpa->w[w] * key[w]->vel[0]; + state->vel[1] += cpa->w[w] * key[w]->vel[1]; + state->vel[2] += cpa->w[w] * key[w]->vel[2]; + key[w]++; + w++; + } + if(k==0){ + /* calculate the offset between actual child root position and first position interpolated from parents */ + VECSUB(cpa_1st,cpa_1st,state->co); + } + /* apply offset for correct positioning */ + VECADD(state->co,state->co,cpa_1st); + } + else{ + /* offset the child from the parent position */ + offset_child(cpa, (ParticleKey*)key[0], (ParticleKey*)state, part->childflat, part->childrad); + + key[0]++; + } + + if(totparent){ + if(i>=totparent) + par = cache[cpa->parent] + k; + else + par=0; + } + else if(cpa->parent>=0){ + par=pcache[cpa->parent]+k; + } + + /* apply different deformations to the child path */ + if(part->flag & PART_CHILD_GUIDE) + guided = do_guide((ParticleKey*)state, i, t, &(psys->effectors)); //safe to cast, since only co and vel are used + + if(guided==0){ + if(part->kink) + do_prekink((ParticleKey*)state, (ParticleKey*)par, par->rot, t, + part->kink_freq * pa_kink, part->kink_shape, part->kink_amp, part->kink, part->kink_axis, ob->obmat); + + do_clump((ParticleKey*)state, (ParticleKey*)par, t, part->clumpfac, part->clumppow, pa_clump); + + if(part->kink) + do_postkink((ParticleKey*)state, (ParticleKey*)par, par->rot, t, + part->kink_freq * pa_kink, part->kink_shape, part->kink_amp, part->kink, part->kink_axis, ob->obmat); + } + + if(part->flag & PART_BRANCHING && between == 0 && part->flag & PART_ANIM_BRANCHING) + rough_t = t * rough_rand; + else + rough_t = t; + + if(part->rough1 != 0.0 && pa_rough1 != 0.0) + do_rough(orco, rough_t, pa_rough1*part->rough1, part->rough1_size, 0.0, (ParticleKey*)state); + + if(part->rough2 != 0.0 && pa_rough2 != 0.0) + do_rough(cpa->rand, rough_t, pa_rough2*part->rough2, part->rough2_size, part->rough2_thres, (ParticleKey*)state); + + if(part->rough_end != 0.0 && pa_roughe != 0.0) + do_rough_end(cpa->rand, rough_t, pa_roughe*part->rough_end, part->rough_end_shape, (ParticleKey*)state, (ParticleKey*)par); + + if(part->flag & PART_BRANCHING && between==0){ + if(branch_prob > part->branch_thres){ + branchfac=0.0f; + } + else{ + if(part->flag & PART_SYMM_BRANCHING){ + if(t < branch_begin || t > branch_end) + branchfac=0.0f; + else{ + if((t-branch_begin)/(branch_end-branch_begin)<0.5) + branchfac=2.0f*(t-branch_begin)/(branch_end-branch_begin); + else + branchfac=2.0f*(branch_end-t)/(branch_end-branch_begin); + + CLAMP(branchfac,0.0f,1.0f); + } + } + else{ + if(t < branch_begin){ + branchfac=0.0f; + } + else{ + branchfac=(t-branch_begin)/((1.0f-branch_begin)*0.5f); + CLAMP(branchfac,0.0f,1.0f); + } + } + } + + if(i<psys->totpart){ + VecLerpf(state->co, (pcache[i] + k)->co, state->co, branchfac); + } + else + VecLerpf(state->co, (cache[i - psys->totpart] + k)->co, state->co, branchfac); + } + + /* we have to correct velocity because of kink & clump */ + if(k>1){ + VECSUB((state-1)->vel,state->co,(state-2)->co); + VecMulf((state-1)->vel,0.5); + } + + /* 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)steps; + + k=check_path_length(k,i,cache,state,length,dvec); + } + else{ + /* initialize length calculation */ + if(part->flag&PART_ABS_LENGTH) + check_path_length(0,0,0,0,part->abslength*pa_length,0); + else + check_path_length(0,0,0,0,pa_length,0); + } + } + } + /* now let's finalise the interpolated parents that we might have left half done before */ + if(totchild) for(i=0,cpa=psys->child; i<totparent; i++, cpa++) + finalize_path_length(i,cache); + + if(vg_length) + MEM_freeN(vg_length); + if(vg_clump) + MEM_freeN(vg_clump); + if(vg_kink) + MEM_freeN(vg_kink); + if(vg_rough1) + MEM_freeN(vg_rough1); + if(vg_rough2) + MEM_freeN(vg_roughe); + if(vg_roughe) + MEM_freeN(vg_roughe); + + psys->totchildcache = totchild; + + if(psys->lattice){ + end_latt_deform(); + psys->lattice=0; + } +} +/* 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. */ +/* -Cached path data is also used to determine cut position for the editmode tool. */ +void psys_cache_paths(Object *ob, ParticleSystem *psys, float cfra, int editupdate) +{ + ParticleCacheKey *ca, **cache=psys->pathcache; + ParticleSystemModifierData *psmd = psys_get_modifier(ob, psys); + ParticleEditSettings *pset = &G.scene->toolsettings->particle; + + ParticleData *pa; + ParticleKey keys[4], result, *kkey[2] = {NULL, NULL}; + HairKey *hkey[2] = {NULL, NULL}; + + ParticleEdit *edit = 0; + ParticleEditKey *ekey = 0; + + SoftBody *soft = 0; + BodyPoint *bp[2] = {NULL, NULL}; + + float birthtime = 0.0, dietime = 0.0; + float t, time, keytime, dfra = 1.0, frs_sec = G.scene->r.frs_sec; + float col[3] = {0.5f, 0.5f, 0.5f}; + float prev_tangent[3], hairmat[4][4]; + int k,i; + int steps = (int)pow(2.0, (double)psys->part->draw_step); + int totpart = psys->totpart; + char nosel[4], sel[4]; + float sel_col[3]; + float nosel_col[3]; + + /* we don't have anything valid to create paths from so let's quit here */ + if((psys->flag & PSYS_HAIR_DONE)==0 && (psys->flag & PSYS_KEYED)==0) + return; + + if(G.rendering) + steps = (int)pow(2.0, (double)psys->part->ren_step); + else if(psys_in_edit_mode(psys)){ + edit=psys->edit; + + //timed = edit->draw_timed; + + PE_get_colors(sel,nosel); + if(pset->brushtype == PE_BRUSH_WEIGHT){ + sel_col[0] = sel_col[1] = sel_col[2] = 1.0f; + nosel_col[0] = nosel_col[1] = nosel_col[2] = 0.0f; + } + else{ + sel_col[0] = (float)sel[0] / 255.0f; + sel_col[1] = (float)sel[1] / 255.0f; + sel_col[2] = (float)sel[2] / 255.0f; + nosel_col[0] = (float)nosel[0] / 255.0f; + nosel_col[1] = (float)nosel[1] / 255.0f; + nosel_col[2] = (float)nosel[2] / 255.0f; + } + } + + if(editupdate && psys->pathcache && totpart == psys->totcached) { + cache = psys->pathcache; + } + else { + /* clear out old and create new empty path cache */ + psys_free_path_cache(psys); + + /* allocate cache array for fast access and set pointers to contiguous mem block */ + cache = psys->pathcache = MEM_callocN(MAX2(1, totpart) * sizeof(void *), "Path cache array"); + cache[0] = MEM_callocN(totpart * (steps + 1) * sizeof(ParticleCacheKey), "Path cache"); + for(i=1; i<totpart; i++) + cache[i] = cache[0] + i * (steps + 1); + } + + if(edit==NULL && psys->soft && psys->softflag & OB_SB_ENABLE) + soft = psys->soft; + + psys->lattice = psys_get_lattice(ob, psys); + + /*---first main loop: create all actual particles' paths---*/ + for(i=0,pa=psys->particles; i<totpart; i++, pa++){ + if(psys && edit==NULL && (pa->flag & PARS_NO_DISP || pa->flag & PARS_UNEXIST)) { + if(soft) + bp[0] += pa->totkey; /* TODO use of initialized value? */ + continue; + } + + if(editupdate && !(pa->flag & PARS_EDIT_RECALC)) continue; + else memset(cache[i], 0, sizeof(*cache[i])*(steps+1)); + + cache[i]->steps = steps; + + if(edit) + ekey = edit->keys[i]; + + /*--get the first data points--*/ + if(psys->flag & PSYS_KEYED) { + kkey[0] = pa->keys; + kkey[1] = kkey[0] + 1; + + birthtime = kkey[0]->time; + dietime = kkey[0][pa->totkey-1].time; + } + else { + hkey[0] = pa->hair; + hkey[1] = hkey[0] + 1; + + birthtime = hkey[0]->time; + dietime = hkey[0][pa->totkey-1].time; + + psys_mat_hair_to_global(ob, psmd->dm, psys->part->from, pa, hairmat); + } + + if(soft){ + bp[0] = soft->bpoint + pa->bpi; + bp[1] = bp[0] + 1; + } + + /*--interpolate actual path from data points--*/ + for(k=0, ca=cache[i]; k<=steps; k++, ca++){ + time = (float)k / (float)steps; + + t = birthtime + time * (dietime - birthtime); + + if(psys->flag & PSYS_KEYED) { + while(kkey[1]->time < t) { + kkey[1]++; + } + + kkey[0] = kkey[1] - 1; + } + else { + while(hkey[1]->time < t) { + hkey[1]++; + bp[1]++; + } + + hkey[0] = hkey[1] - 1; + } + + if(soft) { + bp[0] = bp[1] - 1; + bp_to_particle(keys + 1, bp[0], hkey[0]); + bp_to_particle(keys + 2, bp[1], hkey[1]); + } + else if(psys->flag & PSYS_KEYED) { + memcpy(keys + 1, kkey[0], sizeof(ParticleKey)); + memcpy(keys + 2, kkey[1], sizeof(ParticleKey)); + } + else { + hair_to_particle(keys + 1, hkey[0]); + hair_to_particle(keys + 2, hkey[1]); + } + + + if((psys->flag & PSYS_KEYED)==0) { + if(soft) { + if(hkey[0] != pa->hair) + bp_to_particle(keys, bp[0] - 1, hkey[0] - 1); + else + bp_to_particle(keys, bp[0], hkey[0]); + } + else { + if(hkey[0] != pa->hair) + hair_to_particle(keys, hkey[0] - 1); + else + hair_to_particle(keys, hkey[0]); + } + + if(soft) { + if(hkey[1] != pa->hair + pa->totkey - 1) + bp_to_particle(keys + 3, bp[1], hkey[1] + 1); + else + bp_to_particle(keys + 3, bp[1], hkey[1]); + } + else { + if(hkey[1] != pa->hair + pa->totkey - 1) + hair_to_particle(keys + 3, hkey[1] + 1); + else + hair_to_particle(keys + 3, hkey[1]); + } + } + + dfra = keys[2].time - keys[1].time; + + keytime = (t - keys[1].time) / dfra; + + /* convert velocity to timestep size */ + if(psys->flag & PSYS_KEYED){ + VecMulf(keys[1].vel, dfra / frs_sec); + VecMulf(keys[2].vel, dfra / frs_sec); + } + + /* now we should have in chronologiacl order k1<=k2<=t<=k3<=k4 with keytime between [0,1]->[k2,k3] (k1 & k4 used for cardinal & bspline interpolation)*/ + interpolate_particle((psys->flag & PSYS_KEYED) ? -1 /* signal for cubic interpolation */ + : ((psys->part->flag & PART_HAIR_BSPLINE) ? KEY_BSPLINE : KEY_CARDINAL) + ,keys, keytime, &result); + + + /* the velocity needs to be converted back from cubic interpolation */ + if(psys->flag & PSYS_KEYED){ + VecMulf(result.vel, frs_sec / dfra); + } + else if(soft==NULL) { /* softbody and keyed are allready in global space */ + Mat4MulVecfl(hairmat, result.co); + } + + + /* apply guide curves to path data */ + if(edit==0 && psys->effectors.first && (psys->part->flag & PART_CHILD_GUIDE)==0) + do_guide(&result, i, time, &psys->effectors); + + /* figure out rotation */ + + if(k) { + float angle, tangent[3], normal[3], q[4]; + + if(k == 1) { + float *q2; + + VECSUB(tangent, result.co, (ca - 1)->co); + + q2 = vectoquat(tangent, OB_POSX, OB_POSZ); + + QUATCOPY((ca - 1)->rot, q2); + + VECCOPY(prev_tangent, tangent); + Normalize(prev_tangent); + } + else { + VECSUB(tangent, result.co, (ca - 1)->co); + Normalize(tangent); + angle = saacos(Inpf(tangent, prev_tangent)); + + if((angle > -0.000001) && (angle < 0.000001)){ + QUATCOPY((ca - 1)->rot, (ca - 2)->rot); + } + else{ + Crossf(normal, prev_tangent, tangent); + VecRotToQuat(normal, angle, q); + QuatMul((ca - 1)->rot, q, (ca - 2)->rot); + } + + VECCOPY(prev_tangent, tangent); + } + + if(k == steps) { + QUATCOPY(ca->rot, (ca - 1)->rot); + } + } + + VECCOPY(ca->co, result.co); + + if(k){ + VECSUB(ca->vel, ca->co, (ca-1)->co); + + if(k==1) { + VECCOPY((ca-1)->vel, ca->vel); + } + + } + + + /* selection coloring in edit mode */ + if(edit){ + if(pset->brushtype==PE_BRUSH_WEIGHT){ + if(k==steps) + VecLerpf(ca->col, nosel_col, sel_col, hkey[0]->weight); + else + VecLerpf(ca->col,nosel_col,sel_col, + (1.0f - keytime) * hkey[0]->weight + keytime * hkey[1]->weight); + } + else{ + if((ekey + (hkey[0] - pa->hair))->flag & PEK_SELECT){ + if((ekey + (hkey[1] - pa->hair))->flag & PEK_SELECT){ + VECCOPY(ca->col, sel_col); + } + else{ + VecLerpf(ca->col, sel_col, nosel_col, keytime); + } + } + else{ + if((ekey + (hkey[1] - pa->hair))->flag & PEK_SELECT){ + VecLerpf(ca->col, nosel_col, sel_col, keytime); + } + else{ + VECCOPY(ca->col, nosel_col); + } + } + } + } + else{ + VECCOPY(ca->col, col); + } + + if(psys->lattice && edit==0) + calc_latt_deform(ca->co, 1.0f); + } + } + + psys->totcached = totpart; + + if(psys && psys->lattice){ + end_latt_deform(); + psys->lattice=0; + } +} +/************************************************/ +/* Particle Key handling */ +/************************************************/ +void copy_particle_key(ParticleKey *to, ParticleKey *from, int time){ + if(time){ + memcpy(to,from,sizeof(ParticleKey)); + } + else{ + float to_time=to->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); + if(vel) VECCOPY(vel,key->vel); + if(rot) QUATCOPY(rot,key->rot); + if(time) *time=key->time; +} +/*-------changing particle keys from space to another-------*/ +void psys_key_to_object(Object *ob, ParticleKey *key, float imat[][4]){ + float q[4], imat2[4][4]; + + if(imat==0){ + Mat4Invert(imat2,ob->obmat); + imat=imat2; + } + + VECADD(key->vel,key->vel,key->co); + + Mat4MulVecfl(imat,key->co); + Mat4MulVecfl(imat,key->vel); + Mat4ToQuat(imat,q); + + VECSUB(key->vel,key->vel,key->co); + QuatMul(key->rot,q,key->rot); +} +static void key_from_object(Object *ob, ParticleKey *key){ + float q[4]; + + VECADD(key->vel,key->vel,key->co); + + Mat4MulVecfl(ob->obmat,key->co); + Mat4MulVecfl(ob->obmat,key->vel); + Mat4ToQuat(ob->obmat,q); + + VECSUB(key->vel,key->vel,key->co); + QuatMul(key->rot,q,key->rot); +} + +static void triatomat(float *v1, float *v2, float *v3, float (*uv)[2], float mat[][4]) +{ + float det, w1, w2, d1[2], d2[2]; + + memset(mat, 0, sizeof(float)*4*4); + mat[3][3]= 1.0f; + + /* first axis is the normal */ + CalcNormFloat(v1, v2, v3, mat[2]); + + /* second axis along (1, 0) in uv space */ + if(uv) { + d1[0]= uv[1][0] - uv[0][0]; + d1[1]= uv[1][1] - uv[0][1]; + d2[0]= uv[2][0] - uv[0][0]; + d2[1]= uv[2][1] - uv[0][1]; + + det = d2[0]*d1[1] - d2[1]*d1[0]; + + if(det != 0.0f) { + det= 1.0f/det; + w1= -d2[1]*det; + w2= d1[1]*det; + + mat[1][0]= w1*(v2[0] - v1[0]) + w2*(v3[0] - v1[0]); + mat[1][1]= w1*(v2[1] - v1[1]) + w2*(v3[1] - v1[1]); + mat[1][2]= w1*(v2[2] - v1[2]) + w2*(v3[2] - v1[2]); + Normalize(mat[1]); + } + else + mat[1][0]= mat[1][1]= mat[1][2]= 0.0f; + } + else { + VecSubf(mat[1], v2, v1); + Normalize(mat[1]); + } + + /* third as a cross product */ + Crossf(mat[0], mat[1], mat[2]); +} + +static void psys_face_mat(DerivedMesh *dm, ParticleData *pa, float mat[][4]) +{ + float v1[3], v2[3], v3[3]; + MFace *mface; + OrigSpaceFace *osface; + + int i = pa->num_dmcache==DMCACHE_NOTFOUND ? pa->num : pa->num_dmcache; + + if (i==-1 || i >= dm->getNumFaces(dm)) { Mat4One(mat); return; } + mface=dm->getFaceData(dm,i,CD_MFACE); + osface=dm->getFaceData(dm,i,CD_ORIGSPACE); + + dm->getVertCo(dm,mface->v1,v1); + dm->getVertCo(dm,mface->v2,v2); + dm->getVertCo(dm,mface->v3,v3); + + triatomat(v1, v2, v3, (osface)? osface->uv: NULL, mat); +} +void psys_mat_hair_to_object(Object *ob, DerivedMesh *dm, short from, ParticleData *pa, float hairmat[][4]) +{ + float vec[3]; + + psys_face_mat(dm, pa, hairmat); + psys_particle_on_dm(ob, dm, from, pa->num, pa->num_dmcache, pa->fuv, pa->foffset, vec, 0, 0, 0); + VECCOPY(hairmat[3],vec); +} + +/* +void psys_key_to_geometry(DerivedMesh *dm, ParticleData *pa, ParticleKey *key) +{ + float q[4], v1[3], v2[3], v3[3]; + + dm->getVertCo(dm,pa->verts[0],v1); + dm->getVertCo(dm,pa->verts[1],v2); + dm->getVertCo(dm,pa->verts[2],v3); + + triatoquat(v1, v2, v3, q); + + QuatInv(q); + + VECSUB(key->co,key->co,v1); + + VECADD(key->vel,key->vel,key->co); + + QuatMulVecf(q, key->co); + QuatMulVecf(q, key->vel); + + VECSUB(key->vel,key->vel,key->co); + + QuatMul(key->rot,q,key->rot); +} + +void psys_key_from_geometry(DerivedMesh *dm, ParticleData *pa, ParticleKey *key) +{ + float q[4], v1[3], v2[3], v3[3]; + + dm->getVertCo(dm,pa->verts[0],v1); + dm->getVertCo(dm,pa->verts[1],v2); + dm->getVertCo(dm,pa->verts[2],v3); + + triatoquat(v1, v2, v3, q); + + VECADD(key->vel,key->vel,key->co); + + QuatMulVecf(q, key->co); + QuatMulVecf(q, key->vel); + + VECSUB(key->vel,key->vel,key->co); + + VECADD(key->co,key->co,v1); + + QuatMul(key->rot,q,key->rot); +} +*/ + +void psys_vec_rot_to_face(DerivedMesh *dm, ParticleData *pa, float *vec)//to_geometry(DerivedMesh *dm, ParticleData *pa, float *vec) +{ + float mat[4][4]; + + psys_face_mat(dm, pa, mat); + Mat4Transp(mat); /* cheap inverse for rotation matrix */ + Mat4Mul3Vecfl(mat, vec); +} + +/* unused */ +#if 0 +static void psys_vec_rot_from_face(DerivedMesh *dm, ParticleData *pa, float *vec)//from_geometry(DerivedMesh *dm, ParticleData *pa, float *vec) +{ + float q[4], v1[3], v2[3], v3[3]; + /* + dm->getVertCo(dm,pa->verts[0],v1); + dm->getVertCo(dm,pa->verts[1],v2); + dm->getVertCo(dm,pa->verts[2],v3); + */ + /* replace with this */ + MFace *mface; + int i; // = psys_particle_dm_face_lookup(dm, pa->num, pa->fuv, pa->foffset, (LinkNode*)NULL); + i = pa->num_dmcache==DMCACHE_NOTFOUND ? pa->num : pa->num_dmcache; + if (i==-1 || i >= dm->getNumFaces(dm)) { vec[0] = vec[1] = 0; vec[2] = 1; return; } + mface=dm->getFaceData(dm,i,CD_MFACE); + + dm->getVertCo(dm,mface->v1,v1); + dm->getVertCo(dm,mface->v2,v2); + dm->getVertCo(dm,mface->v3,v3); + /* done */ + + triatoquat(v1, v2, v3, q); + + QuatMulVecf(q, vec); + + //VECADD(vec,vec,v1); +} +#endif + +void psys_mat_hair_to_global(Object *ob, DerivedMesh *dm, short from, ParticleData *pa, float hairmat[][4]) +{ + float facemat[4][4]; + + psys_mat_hair_to_object(ob, dm, from, pa, facemat); + + Mat4MulMat4(hairmat, facemat, ob->obmat); +} + +/************************************************/ +/* ParticleSettings handling */ +/************************************************/ +static void default_particle_settings(ParticleSettings *part) +{ + int i; + + part->type= PART_EMITTER; + part->distr= PART_DISTR_JIT; + part->draw_as=PART_DRAW_DOT; + part->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->sta= 1.0; + part->end= 100.0; + part->lifetime= 50.0; + part->jitfac= 1.0; + 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; + part->hair_step= 10; + part->keys_step= 5; + part->draw_step= 4; + part->ren_step= 6; + part->adapt_angle= 5; + part->adapt_pix= 3; + part->kink_axis= 2; + part->reactevent= PART_EVENT_DEATH; + part->disp=100; + part->from= PART_FROM_FACE; + part->length= 1.0; + part->rotfac= 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->reactshape=1.0f; + + part->mass=1.0; + part->size=1.0; + part->childsize=1.0; + + part->child_nbr=10; + part->childrad=0.2f; + part->childflat=0.0f; + part->clumppow=0.0f; + part->kink_amp=0.2f; + part->kink_freq=2.0; + + part->rough1_size=1.0; + part->rough2_size=1.0; + part->rough_end_shape=1.0; + + part->draw_line[0]=0.5; + + part->banking=1.0; + part->max_bank=1.0; + + for(i=0; i<BOID_TOT_RULES; i++){ + part->boidrule[i]=(char)i; + part->boidfac[i]=0.5; + } + + part->ipo = NULL; +} + + +ParticleSettings *psys_new_settings(char *name, Main *main) +{ + ParticleSettings *part; + + part= alloc_libblock(&main->particle, ID_PA, name); + + default_particle_settings(part); + + return part; +} + +ParticleSettings *psys_copy_settings(ParticleSettings *part) +{ + ParticleSettings *partn; + + partn= copy_libblock(part); + if(partn->pd) partn->pd= MEM_dupallocN(part->pd); + + return partn; +} + +void psys_make_local_settings(ParticleSettings *part) +{ + Object *ob; + ParticleSettings *par; + int local=0, lib=0; + + /* - only lib users: do nothing + * - only local users: set flag + * - mixed: make copy + */ + + if(part->id.lib==0) return; + if(part->id.us==1) { + part->id.lib= 0; + part->id.flag= LIB_LOCAL; + new_id(0, (ID *)part, 0); + return; + } + + /* test objects */ + ob= G.main->object.first; + while(ob) { + ParticleSystem *psys=ob->particlesystem.first; + for(; psys; psys=psys->next){ + if(psys->part==part) { + if(ob->id.lib) lib= 1; + else local= 1; + } + } + ob= ob->id.next; + } + + if(local && lib==0) { + part->id.lib= 0; + part->id.flag= LIB_LOCAL; + new_id(0, (ID *)part, 0); + } + else if(local && lib) { + + par= psys_copy_settings(part); + par->id.us= 0; + + /* do objects */ + ob= G.main->object.first; + while(ob) { + ParticleSystem *psys=ob->particlesystem.first; + for(; psys; psys=psys->next){ + if(psys->part==part && ob->id.lib==0) { + psys->part= par; + par->id.us++; + part->id.us--; + } + } + ob= ob->id.next; + } + } +} + +/* should be integrated to depgraph signals */ +void psys_flush_settings(ParticleSettings *part, int event, int hair_recalc) +{ + Base *base; + Object *ob, *tob; + ParticleSystem *psys; + int flush; + + /* update all that have same particle settings */ + for(base = G.scene->base.first; base; base= base->next) { + if(base->object->particlesystem.first) { + ob=base->object; + flush=0; + for(psys=ob->particlesystem.first; psys; psys=psys->next){ + if(psys->part==part){ + psys->recalc |= event; + if(hair_recalc) + psys->recalc |= PSYS_RECALC_HAIR; + flush++; + } + else if(psys->part->type==PART_REACTOR){ + ParticleSystem *tpsys; + tob=psys->target_ob; + if(tob==0) + tob=ob; + tpsys=BLI_findlink(&tob->particlesystem,psys->target_psys-1); + + if(tpsys && tpsys->part==part){ + psys->flag |= event; + flush++; + } + } + } + if(flush) + DAG_object_flush_update(G.scene, ob, OB_RECALC_DATA); + } + } +} +/************************************************/ +/* Textures */ +/************************************************/ +static void get_cpa_texture(DerivedMesh *dm, Material *ma, int face_index, float *fw, float *orco, ParticleTexture *ptex, int event) +{ + MTex *mtex; + int m,setvars=0; + float value, rgba[4], texco[3]; + + if(ma) for(m=0; m<MAX_MTEX; m++){ + mtex=ma->mtex[m]; + if(mtex && (ma->septex & (1<<m))==0){ + float var=mtex->varfac; + short blend=mtex->blendtype; + short neg=mtex->pmaptoneg; + + if(mtex->texco & TEXCO_UV && fw){ + int uv_index=CustomData_get_named_layer_index(&dm->faceData,CD_MTFACE,mtex->uvname); + if(uv_index<0){ + uv_index=CustomData_get_active_layer_index(&dm->faceData,CD_MTFACE); + } + if(uv_index>=0){ + CustomDataLayer *layer=&dm->faceData.layers[uv_index]; + MTFace *mtface= &((MTFace*)layer->data)[face_index]; + MFace *mf=dm->getFaceData(dm,face_index,CD_MFACE); + psys_interpolate_uvs(mtface,mf->v4,fw,texco); + texco[0]*=2.0; + texco[1]*=2.0; + texco[0]-=1.0; + texco[1]-=1.0; + } + else + VECCOPY(texco,orco); + } + else{ + VECCOPY(texco,orco); + } + externtex(mtex, texco, &value, rgba, rgba+1, rgba+2, rgba+3); + if((event & mtex->pmapto) & MAP_PA_TIME){ + if((setvars&MAP_PA_TIME)==0){ + ptex->time=0.0; + setvars|=MAP_PA_TIME; + } + ptex->time= texture_value_blend(mtex->def_var,ptex->time,value,var,blend,neg & MAP_PA_TIME); + } + if((event & mtex->pmapto) & MAP_PA_LENGTH) + ptex->length= texture_value_blend(value,ptex->length,value,var,blend,neg & MAP_PA_LENGTH); + if((event & mtex->pmapto) & MAP_PA_CLUMP) + ptex->clump= texture_value_blend(value,ptex->clump,value,var,blend,neg & MAP_PA_CLUMP); + if((event & mtex->pmapto) & MAP_PA_KINK) + ptex->kink= texture_value_blend(value,ptex->kink,value,var,blend,neg & MAP_PA_CLUMP); + } + } + CLAMP(ptex->time,0.0,1.0); + CLAMP(ptex->length,0.0,1.0); + CLAMP(ptex->clump,0.0,1.0); + CLAMP(ptex->kink,0.0,1.0); +} +void psys_get_texture(Object *ob, Material *ma, ParticleSystemModifierData *psmd, ParticleSystem *psys, ParticleData *pa, ParticleTexture *ptex, int event) +{ + MTex *mtex; + int m; + float value, rgba[4], texco[3]; + int setvars=0; + + if(ma) for(m=0; m<MAX_MTEX; m++){ + mtex=ma->mtex[m]; + if(mtex && (ma->septex & (1<<m))==0){ + float var=mtex->varfac; + short blend=mtex->blendtype; + short neg=mtex->pmaptoneg; + + if(mtex->texco & TEXCO_UV){ + int uv_index=CustomData_get_named_layer_index(&psmd->dm->faceData,CD_MTFACE,mtex->uvname); + if(uv_index<0){ + uv_index=CustomData_get_active_layer_index(&psmd->dm->faceData,CD_MTFACE); + } + if(uv_index>=0){ + CustomDataLayer *layer=&psmd->dm->faceData.layers[uv_index]; + MTFace *mtface= &((MTFace*)layer->data)[pa->num]; + MFace *mf=psmd->dm->getFaceData(psmd->dm,pa->num,CD_MFACE); + psys_interpolate_uvs(mtface,mf->v4,pa->fuv,texco); + texco[0]*=2.0; + texco[1]*=2.0; + texco[0]-=1.0; + texco[1]-=1.0; + } + else + //psys_particle_on_emitter(ob,psmd,psys->part->from,pa->num,pa->fuv,pa->foffset,texco,0,0,0); + /* <jahka> anyways I think it will be too small a difference to notice, so psys_get_texture should only know about the original mesh structure.. no dm needed anywhere */ + psys_particle_on_emitter(ob,psmd,psys->part->from,pa->num,-1,pa->fuv,pa->foffset,texco,0,0,0); + } + else{ + //psys_particle_on_emitter(ob,psmd,psys->part->from,pa->num,pa->fuv,pa->offset,texco,0,0,0); + /* ditto above */ + psys_particle_on_emitter(ob,psmd,psys->part->from,pa->num,-1,pa->fuv,pa->foffset,texco,0,0,0); + } + externtex(mtex, texco, &value, rgba, rgba+1, rgba+2, rgba+3); + + if((event & mtex->pmapto) & MAP_PA_TIME){ + if((setvars&MAP_PA_TIME)==0){ + ptex->time=0.0; + setvars|=MAP_PA_TIME; + } + ptex->time= texture_value_blend(mtex->def_var,ptex->time,value,var,blend,neg & MAP_PA_TIME); + } + if((event & mtex->pmapto) & MAP_PA_LIFE) + ptex->life= texture_value_blend(mtex->def_var,ptex->life,value,var,blend,neg & MAP_PA_LIFE); + if((event & mtex->pmapto) & MAP_PA_DENS) + ptex->exist= texture_value_blend(mtex->def_var,ptex->exist,value,var,blend,neg & MAP_PA_DENS); + if((event & mtex->pmapto) & MAP_PA_SIZE) + ptex->size= texture_value_blend(mtex->def_var,ptex->size,value,var,blend,neg & MAP_PA_SIZE); + if((event & mtex->pmapto) & MAP_PA_IVEL) + ptex->ivel= texture_value_blend(mtex->def_var,ptex->ivel,value,var,blend,neg & MAP_PA_IVEL); + if((event & mtex->pmapto) & MAP_PA_PVEL) + texture_rgb_blend(ptex->pvel,rgba,ptex->pvel,value,var,blend); + if((event & mtex->pmapto) & MAP_PA_LENGTH) + ptex->length= texture_value_blend(mtex->def_var,ptex->length,value,var,blend,neg & MAP_PA_LENGTH); + if((event & mtex->pmapto) & MAP_PA_CLUMP) + ptex->clump= texture_value_blend(mtex->def_var,ptex->clump,value,var,blend,neg & MAP_PA_CLUMP); + if((event & mtex->pmapto) & MAP_PA_KINK) + ptex->kink= texture_value_blend(mtex->def_var,ptex->kink,value,var,blend,neg & MAP_PA_CLUMP); + } + } + CLAMP(ptex->time,0.0,1.0); + CLAMP(ptex->life,0.0,1.0); + CLAMP(ptex->exist,0.0,1.0); + CLAMP(ptex->size,0.0,1.0); + CLAMP(ptex->ivel,0.0,1.0); + CLAMP(ptex->length,0.0,1.0); + CLAMP(ptex->clump,0.0,1.0); + CLAMP(ptex->kink,0.0,1.0); +} +/************************************************/ +/* Particle State */ +/************************************************/ +float psys_get_timestep(ParticleSettings *part) +{ + return 0.04f*part->timetweak; +} +/* part->size should be updated with possible ipo effection before this is called */ +float psys_get_size(Object *ob, Material *ma, ParticleSystemModifierData *psmd, IpoCurve *icu_size, ParticleSystem *psys, ParticleSettings *part, ParticleData *pa, float *vg_size) +{ + ParticleTexture ptex; + float size=1.0f; + + if(ma && part->from!=PART_FROM_PARTICLE){ + ptex.size=size; + psys_get_texture(ob,ma,psmd,psys,pa,&ptex,MAP_PA_SIZE); + size=ptex.size; + } + + if(icu_size){ + calc_icu(icu_size,pa->time); + size*=icu_size->curval; + } + + if(vg_size) + size*=psys_interpolate_value_from_verts(psmd->dm,part->from,pa->num,pa->fuv,vg_size); + + if(part->randsize!=0.0) + size*= 1.0f - part->randsize*pa->sizemul; + + return size*part->size; +} +float psys_get_child_time(ParticleSystem *psys, int child_nbr, float cfra) +{ + ParticleSettings *part = psys->part; + ChildParticle *cpa=psys->child+child_nbr; + + if(part->childtype==PART_CHILD_FACES){ + float time; + int w=0; + time=0.0; + while(w<4 && cpa->pa[w]>=0){ + time+=cpa->w[w]*(psys->particles+cpa->pa[w])->time; + w++; + } + + return (cfra-time)/(part->lifetime*(1.0f-part->randlife*cpa->rand[1])); + } + else{ + ParticleData *pa = psys->particles + cpa->parent; + return (cfra-pa->time)/pa->lifetime; + } +} +float psys_get_child_size(ParticleSystem *psys, int child_nbr, float cfra, float *pa_time) +{ + ParticleSettings *part = psys->part; + ChildParticle *cpa = psys->child + child_nbr; + float size, time; + + if(part->childtype==PART_CHILD_FACES){ + if(pa_time) + time=*pa_time; + else + time=psys_get_child_time(psys,child_nbr,cfra); + + if((part->flag&PART_ABS_TIME)==0 && part->ipo){ + calc_ipo(part->ipo, 100*time); + execute_ipo((ID *)part, part->ipo); + } + size=part->size; + } + else + size=psys->particles[cpa->parent].size; + + size*=part->childsize; + + if(part->childrandsize!=0.0) + size *= 1.0f - part->childrandsize*cpa->rand[2]; + + return size; +} +/* get's hair (or keyed) particles state at the "path time" specified in state->time */ +void psys_get_particle_on_path(Object *ob, ParticleSystem *psys, int p, ParticleKey *state, int vel) +{ + ParticleSettings *part = psys->part; + ParticleSystemModifierData *psmd = psys_get_modifier(ob, psys); + Material *ma = give_current_material(ob, part->omat); + ParticleData *pa; + ChildParticle *cpa; + ParticleTexture ptex; + ParticleKey tstate; + HairKey *hkey[2]; + ParticleKey *par=0, keys[4]; + + float t, real_t, dfra, keytime; + float orco[3]; + float imat[4][4], hairmat[4][4], cpa_1st[3]; + float pa_clump = 0.0, pa_kink = 0.0; + int totparent = 0; + int totpart = psys->totpart; + int totchild = psys->totchild; + short between = 0, edit = 0; + + float *cpa_fuv; int cpa_num; short cpa_from; + + //if(psys_in_edit_mode(psys)){ + // if((psys->edit_path->flag & PSYS_EP_SHOW_CHILD)==0) + // totchild=0; + // edit=1; + //} + + if(G.rendering==0 && part->flag & PART_CHILD_RENDER) + totchild=0; + + /* user want's cubic interpolation but only without sb it possible */ + //if(interpolation==PART_INTER_CUBIC && baked && psys->softflag==OB_SB_ENABLE) + // interpolation=PART_INTER_BSPLINE; + //else if(baked==0) /* it doesn't make sense to use other types for keyed */ + // interpolation=PART_INTER_CUBIC; + + t=state->time; + CLAMP(t, 0.0, 1.0); + + if(p<totpart){ + pa = psys->particles + p; + + if(pa->alive==PARS_DEAD && part->flag & PART_STICKY && pa->flag & PARS_STICKY && pa->stick_ob){ + copy_particle_key(state,&pa->state,0); + key_from_object(pa->stick_ob,state); + return; + } + + hkey[0] = pa->hair; + hkey[1] = pa->hair + 1; + + real_t = hkey[0]->time + (hkey[0][pa->totkey-1].time - hkey[0]->time) * t; + + while(hkey[1]->time < real_t) + hkey[1]++; + + hkey[0] = hkey[1] - 1; + + hair_to_particle(keys + 1, hkey[0]); + hair_to_particle(keys + 2, hkey[1]); + + //if(soft){ + // if(key[0] != sbel.keys) + // DB_copy_key(&k1,key[0]-1); + // else + // DB_copy_key(&k1,&k2); + //} + //else{ + if(hkey[0] != pa->hair) + hair_to_particle(keys, hkey[0] - 1); + else + hair_to_particle(keys, hkey[0]); + //} + + //if(soft){ + // if(key[1] != sbel.keys + sbel.totkey-1) + // DB_copy_key(&k4,key[1]+1); + // else + // DB_copy_key(&k4,&k3); + //} + //else { + if(hkey[1] != pa->hair + pa->totkey - 1) + hair_to_particle(keys + 3, hkey[1] + 1); + else + hair_to_particle(keys + 3, hkey[1]); + //} + + //psys_get_particle_on_path(bsys,p,t,bkey,ckey[0]); + + //if(part->rotfrom==PART_ROT_KEYS) + // QuatInterpol(state->rot,k2.rot,k3.rot,keytime); + //else{ + // /* TODO: different rotations */ + // float nvel[3]; + // float *q2; + // VECCOPY(nvel,state->vel); + // VecMulf(nvel,-1.0f); + // q2=vectoquat(nvel, OB_POSX, OB_POSZ); + // QUATCOPY(state->rot,q2); + //} + + dfra = keys[2].time - keys[1].time; + + keytime = (real_t - keys[1].time) / dfra; + + interpolate_particle((psys->part->flag & PART_HAIR_BSPLINE) ? KEY_BSPLINE : KEY_CARDINAL + ,keys, keytime, state); + + if((pa->flag & PARS_REKEY)==0) { + psys_mat_hair_to_global(ob, psmd->dm, part->from, pa, hairmat); + Mat4MulVecfl(hairmat, state->co); + + if(psys->effectors.first && (part->flag & PART_CHILD_GUIDE)==0) { + do_guide(state, p, state->time, &psys->effectors); + /* TODO: proper velocity handling */ + } + + if(psys->lattice && edit==0) + calc_latt_deform(state->co,1.0f); + } + } + else if(totchild){ + Mat4Invert(imat,ob->obmat); + + cpa=psys->child+p-totpart; + + if(totchild && part->from!=PART_FROM_PARTICLE && part->childtype==PART_CHILD_FACES){ + totparent=(int)(totchild*part->parents*0.3); + /* part->parents could still be 0 so we can't test with totparent */ + between=1; + } + if(between){ + int w = 0; + float foffset; + + /* get parent states */ + while(w<4 && cpa->pa[w]>=0){ + keys[w].time = t; + psys_get_particle_on_path(ob, psys, cpa->pa[w], keys+w, 1); + w++; + } + + /* get the original coordinates (orco) for texture usage */ + cpa_num=cpa->num; + + foffset= cpa->foffset; + if(part->childtype == PART_CHILD_FACES) + foffset = -(2.0f + part->childspread); + cpa_fuv = cpa->fuv; + cpa_from = PART_FROM_FACE; + + psys_particle_on_emitter(ob,psmd,cpa_from,cpa_num,DMCACHE_ISCHILD,cpa->fuv,foffset,orco,0,0,0); + + /* we need to save the actual root position of the child for positioning it accurately to the surface of the emitter */ + VECCOPY(cpa_1st,orco); + + //w=0; + //while(w<4 && cpa->pa[w]>=0){ + // pa=psys->particles+cpa->pa[w]; + // psys_particle_on_emitter(ob,psmd,part->from,pa->num,pa->fuv,pa->foffset,vec,0,0,0); + // cpa_1st[0] -= cpa->w[w]*vec[0]; + // cpa_1st[1] -= cpa->w[w]*vec[1]; + // cpa_1st[2] -= cpa->w[w]*vec[2]; + // w++; + //} + + Mat4MulVecfl(ob->obmat,cpa_1st); + + pa=0; + } + else{ + /* get the parent state */ + + keys->time = t; + psys_get_particle_on_path(ob,psys,cpa->parent,keys,1); + + /* get the original coordinates (orco) for texture usage */ + pa=psys->particles+cpa->parent; + + cpa_from=part->from; + cpa_num=pa->num; + cpa_fuv=pa->fuv; + + psys_particle_on_emitter(ob,psmd,cpa_from,cpa_num,DMCACHE_ISCHILD,cpa_fuv,pa->foffset,orco,0,0,0); + } + + /* correct child ipo timing */ + if((part->flag&PART_ABS_TIME)==0 && part->ipo){ + calc_ipo(part->ipo, 100.0f*t); + execute_ipo((ID *)part, part->ipo); + } + + /* get different child parameters from textures & vgroups */ + ptex.clump=1.0; + ptex.kink=1.0; + + get_cpa_texture(psmd->dm,ma,cpa_num,cpa_fuv,orco,&ptex,MAP_PA_CACHE-MAP_PA_LENGTH); + + pa_clump=ptex.clump; + pa_kink=ptex.kink; + + /* TODO: vertex groups */ + + if(between){ + int w=0; + + state->co[0] = state->co[1] = state->co[2] = 0.0f; + state->vel[0] = state->vel[1] = state->vel[2] = 0.0f; + + /* child position is the weighted sum of parent positions */ + while(w<4 && cpa->pa[w]>=0){ + state->co[0] += cpa->w[w] * keys[w].co[0]; + state->co[1] += cpa->w[w] * keys[w].co[1]; + state->co[2] += cpa->w[w] * keys[w].co[2]; + + state->vel[0] += cpa->w[w] * keys[w].vel[0]; + state->vel[1] += cpa->w[w] * keys[w].vel[1]; + state->vel[2] += cpa->w[w] * keys[w].vel[2]; + w++; + } + /* apply offset for correct positioning */ + VECADD(state->co,state->co,cpa_1st); + } + else{ + /* offset the child from the parent position */ + offset_child(cpa, keys, state, part->childflat, part->childrad); + } + + par = keys; + //if(totparent){ + // if(p-totpart>=totparent){ + // key.time=t; + // psys_get_particle_on_path(ob,psys,totpart+cpa->parent,&key,1); + // bti->convert_dynamic_key(bsys,&key,par,cpar); + // } + // else + // par=0; + //} + //else + // DB_get_key_on_path(bsys,cpa->parent,t,par,cpar); + + /* apply different deformations to the child path */ + if(part->kink) + do_prekink(state, par, par->rot, t, part->kink_freq * pa_kink, part->kink_shape, + part->kink_amp, part->kink, part->kink_axis, ob->obmat); + + do_clump(state, par, t, part->clumpfac, part->clumppow, 1.0f); + + if(part->kink) + do_postkink(state, par, par->rot, t, part->kink_freq * pa_kink, part->kink_shape, + part->kink_amp, part->kink, part->kink_axis, ob->obmat); + + if(part->rough1 != 0.0) + do_rough(orco, t, part->rough1, part->rough1_size, 0.0, state); + + if(part->rough2 != 0.0) + do_rough(cpa->rand, t, part->rough2, part->rough2_size, part->rough2_thres, state); + + if(part->rough_end != 0.0) + do_rough_end(cpa->rand, t, part->rough_end, part->rough_end_shape, state, par); + + if(vel){ + if(t>=0.001f){ + tstate.time=t-0.001f; + psys_get_particle_on_path(ob,psys,p,&tstate,0); + VECSUB(state->vel,state->co,tstate.co); + } + else{ + tstate.time=t+0.001f; + psys_get_particle_on_path(ob,psys,p,&tstate,0); + VECSUB(state->vel,tstate.co,state->co); + } + } + + } +} +/* gets particle's state at a time, returns 1 if particle exists and can be seen and 0 if not */ +int psys_get_particle_state(Object *ob, ParticleSystem *psys, int p, ParticleKey *state, int always){ + ParticleSettings *part=psys->part; + ParticleData *pa=0; + float cfra; + int totpart=psys->totpart, between=0; + + if(state->time>0) + cfra=state->time; + else + cfra=bsystem_time(0,(float)G.scene->r.cfra,0.0); + + if(psys->totchild && p>=totpart){ + if(G.rendering==0 && part->flag&PART_CHILD_RENDER) + return 0; + if(part->from!=PART_FROM_PARTICLE && part->childtype==PART_CHILD_FACES){ + between=1; + } + else + pa=psys->particles+(psys->child+p-totpart)->parent; + } + else + pa=psys->particles+p; + + if(between){ + state->time = psys_get_child_time(psys,p-totpart,cfra); + + if(always==0) + if((state->time<0.0 && (part->flag & PART_UNBORN)==0) + || (state->time>1.0 && (part->flag & PART_DIED)==0)) + return 0; + } + else{ + if(pa->alive==PARS_KILLED) return 0; + if(always==0) + if((pa->alive==PARS_UNBORN && (part->flag & PART_UNBORN)==0) + || (pa->alive==PARS_DEAD && (part->flag & PART_DIED)==0)) + return 0; + } + + //if(bsys->flag & (BSYS_DONE|BSYS_KEYED)){ + // if(between){ + // //ChildParticle *cpa=psys->child+p-totpart; + // //state->time= (cfra-(part->sta+(part->end-part->sta)*cpa->rand[0]))/(part->lifetime*cpa->rand[1]); + // } + // else + // state->time= (cfra-pa->time)/(pa->dietime-pa->time);//pa->lifetime; + + // psys_get_particle_on_path(ob,psys,p,state,1); + // return 1; + //} + //else{ + //if(psys->totchild && p>=psys->totpart){ + // ChildParticle *cpa=psys->child+p-psys->totpart; + // ParticleKey *key1, skey; + // float t=(cfra-pa->time)/pa->lifetime, clump; + + // pa=psys->particles+cpa->parent; + + // if(pa->alive==PARS_DEAD && part->flag&PART_STICKY && pa->flag&PARS_STICKY && pa->stick_ob){ + // key1=&skey; + // copy_particle_key(key1,&pa->state,0); + // key_from_object(pa->stick_ob,key1); + // } + // else{ + // key1=&pa->state; + // } + // + // offset_child(cpa, key1, state, part->childflat, part->childrad); + // + // CLAMP(t,0.0,1.0); + // if(part->kink) /* TODO: part->kink_freq*pa_kink */ + // do_prekink(state,key1,t,part->kink_freq,part->kink_shape,part->kink_amp,part->kink,part->kink_axis,ob->obmat); + // + // /* TODO: pa_clump vgroup */ + // do_clump(state,key1,t,part->clumpfac,part->clumppow,0); + + // if(part->kink) /* TODO: part->kink_freq*pa_kink */ + // do_postkink(state,key1,t,part->kink_freq,part->kink_shape,part->kink_amp,part->kink,part->kink_axis,ob->obmat); + + //} + //else{ + if (pa) { /* TODO PARTICLE - should this ever be NULL? - Campbell */ + copy_particle_key(state,&pa->state,0); + + if(pa->alive==PARS_DEAD && part->flag&PART_STICKY && pa->flag&PARS_STICKY && pa->stick_ob){ + key_from_object(pa->stick_ob,state); + } + + if(psys->lattice) + calc_latt_deform(state->co,1.0f); + } + //} + + return 1; + //} +} + diff --git a/source/blender/blenkernel/intern/particle_system.c b/source/blender/blenkernel/intern/particle_system.c new file mode 100644 index 00000000000..0bcf6be0a4a --- /dev/null +++ b/source/blender/blenkernel/intern/particle_system.c @@ -0,0 +1,4371 @@ +/* particle_system.c + * + * + * $Id: particle_system.c $ + * + * ***** BEGIN GPL/BL DUAL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. The Blender + * Foundation also sells licenses for use in proprietary software under + * the Blender License. See http://www.blender.org/BL/ for information + * about this. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * The Original Code is Copyright (C) 2007 by Janne Karhu. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): none yet. + * + * ***** END GPL/BL DUAL LICENSE BLOCK ***** + */ + +#include <stdlib.h> +#include <math.h> +#include <string.h> + +#include "MEM_guardedalloc.h" + +#include "DNA_particle_types.h" +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_modifier_types.h" +#include "DNA_object_force.h" +#include "DNA_object_types.h" +#include "DNA_material_types.h" +#include "DNA_ipo_types.h" +#include "DNA_curve_types.h" +#include "DNA_group_types.h" +#include "DNA_scene_types.h" +#include "DNA_texture_types.h" + +#include "BLI_rand.h" +#include "BLI_jitter.h" +#include "BLI_arithb.h" +#include "BLI_blenlib.h" +#include "BLI_kdtree.h" +#include "BLI_linklist.h" + +#include "BKE_anim.h" +#include "BKE_bad_level_calls.h" +#include "BKE_cdderivedmesh.h" +#include "BKE_displist.h" + +#include "BKE_particle.h" +#include "BKE_global.h" +#include "BKE_utildefines.h" +#include "BKE_DerivedMesh.h" +#include "BKE_object.h" +#include "BKE_material.h" +#include "BKE_ipo.h" +#include "BKE_softbody.h" +#include "BKE_depsgraph.h" +#include "BKE_lattice.h" +#include "BKE_pointcache.h" +#include "BKE_modifier.h" + +#include "BSE_headerbuttons.h" + +#include "blendef.h" + +#include "RE_shader_ext.h" + +/************************************************/ +/* Reacting to system events */ +/************************************************/ + +static int get_current_display_percentage(ParticleSystem *psys) +{ + ParticleSettings *part=psys->part; + + if(G.rendering || (part->child_nbr && part->childtype)) + return 100; + + if(part->phystype==PART_PHYS_KEYED){ + if(psys->flag & PSYS_FIRST_KEYED) + return psys->part->disp; + else + return 100; + } + else + return psys->part->disp; +} + +static void alloc_particles(ParticleSystem *psys, int new_totpart) +{ + ParticleData *newpars = 0, *pa; + int i, totpart, totsaved = 0; + + if(new_totpart<0){ + if(psys->part->distr==PART_DISTR_GRID){ + totpart= psys->part->grid_res; + totpart*=totpart*totpart; + } + else + totpart=psys->part->totpart; + } + else + totpart=new_totpart; + + if(totpart) + newpars= MEM_callocN(totpart*sizeof(ParticleData), "particles"); + if(psys->particles){ + totsaved=MIN2(psys->totpart,totpart); + /*save old pars*/ + if(totsaved) + memcpy(newpars,psys->particles,totsaved*sizeof(ParticleData)); + + for(i=totsaved, pa=psys->particles+totsaved; i<psys->totpart; i++, pa++) + if(pa->hair) MEM_freeN(pa->hair); + + MEM_freeN(psys->particles); + } + psys->particles=newpars; + + if(psys->part->child_nbr && psys->part->childtype){ + if(psys->child) + MEM_freeN(psys->child); + psys->child = NULL; + if(totpart) + psys->child= MEM_callocN(totpart*psys->part->child_nbr*sizeof(ChildParticle), "child_particles"); + psys->totchild=totpart*psys->part->child_nbr; + } + else if(psys->child){ + MEM_freeN(psys->child); + psys->child=0; + psys->totchild=0; + } + + psys->totpart=totpart; +} + +/* only run this if from == PART_FROM_FACE */ +static void psys_calc_dmfaces(Object *ob, DerivedMesh *dm, ParticleSystem *psys) +{ + /* use for building derived mesh face-origin info, + node - the allocated links - total derived mesh face count + node_array - is the array of nodes alligned with the base mesh's faces, so each original face can reference its derived faces + */ + Mesh *me= (Mesh*)ob->data; + ParticleData *pa= 0; + int p; + + /* CACHE LOCATIONS */ + if(!dm->deformedOnly) { + /* Will use later to speed up subsurf/derivedmesh */ + + int tot_dm_face = dm->getNumFaces(dm); + int totface = me->totface; + int *origindex = DM_get_face_data_layer(dm, CD_ORIGINDEX); + int i; + LinkNode *node, *node_dm_faces, **node_array; + + node_dm_faces = node = MEM_callocN(sizeof(LinkNode)*tot_dm_face, "faceindicies"); + node_array = MEM_callocN(sizeof(LinkNode *)*totface, "faceindicies array"); + + for(i=0; i < tot_dm_face; i++, origindex++, node++) { + node->link = (void *)i; // or use the index? + if(*origindex != -1) { + if(node_array[*origindex]) { + /* prepend */ + node->next = node_array[*origindex]; + node_array[*origindex] = node; + } else { + node_array[*origindex] = node; + } + } + } + + /* cache the faces! */ + + + for(p=0,pa=psys->particles; p<psys->totpart; p++,pa++) { + //i = pa->num; + //if (i<totface) // should never happen + i = psys_particle_dm_face_lookup(ob, dm, pa->num, pa->fuv, node_array[pa->num]); + pa->num_dmcache = i; + } + + //for (i=0; i < totface; i++) { + // i = psys_particle_dm_face_lookup(ob, dm, node_array[], fuv, (LinkNode*)NULL); + //} + MEM_freeN(node_array); + MEM_freeN(node_dm_faces); + + } else { + /* set the num_dmcache to an invalid value, just incase */ + /* TODO PARTICLE, make the following line unnecessary, each function should know to use the num or num_dmcache */ + + /* + for(p=0,pa=psys->particles; p<psys->totpart; p++,pa++) { + pa->num_dmcache = pa->num; + } + */ + for(p=0,pa=psys->particles; p<psys->totpart; p++,pa++) { + pa->num_dmcache = -1; + } + } +} + +static void distribute_particles_in_grid(DerivedMesh *dm, ParticleSystem *psys) +{ + ParticleData *pa=0; + float min[3], max[3], delta[3], d; + MVert *mv, *mvert = dm->getVertDataArray(dm,0); + int totvert=dm->getNumVerts(dm), from=psys->part->from; + int i, j, k, p, res=psys->part->grid_res, size[3], axis; + + mv=mvert; + + /* find bounding box of dm */ + VECCOPY(min,mv->co); + VECCOPY(max,mv->co); + mv++; + + for(i=1; i<totvert; i++, mv++){ + min[0]=MIN2(min[0],mv->co[0]); + min[1]=MIN2(min[1],mv->co[1]); + min[2]=MIN2(min[2],mv->co[2]); + + max[0]=MAX2(max[0],mv->co[0]); + max[1]=MAX2(max[1],mv->co[1]); + max[2]=MAX2(max[2],mv->co[2]); + } + + VECSUB(delta,max,min); + + /* determine major axis */ + axis = (delta[0]>=delta[1])?0:((delta[1]>=delta[2])?1:2); + + d = delta[axis]/(float)res; + + size[axis]=res; + size[(axis+1)%3]=(int)ceil(delta[(axis+1)%3]/d); + size[(axis+2)%3]=(int)ceil(delta[(axis+2)%3]/d); + + /* float errors grrr.. */ + size[(axis+1)%3] = MIN2(size[(axis+1)%3],res); + size[(axis+2)%3] = MIN2(size[(axis+2)%3],res); + + min[0]+=d/2.0f; + min[1]+=d/2.0f; + min[2]+=d/2.0f; + + for(i=0,p=0,pa=psys->particles; i<res; i++){ + for(j=0; j<res; j++){ + for(k=0; k<res; k++,p++,pa++){ + pa->fuv[0]=min[0]+(float)i*d; + pa->fuv[1]=min[1]+(float)j*d; + pa->fuv[2]=min[2]+(float)k*d; + pa->flag |= PARS_UNEXIST; + pa->loop=0; /* abused in volume calculation */ + } + } + } + + /* enable particles near verts/edges/faces/inside surface */ + if(from==PART_FROM_VERT){ + float vec[3]; + + pa=psys->particles; + + min[0]-=d/2.0f; + min[1]-=d/2.0f; + min[2]-=d/2.0f; + + for(i=0,mv=mvert; i<totvert; i++,mv++){ + VecSubf(vec,mv->co,min); + vec[0]/=delta[0]; + vec[1]/=delta[1]; + vec[2]/=delta[2]; + (pa +((int)(vec[0]*(size[0]-1))*res + +(int)(vec[1]*(size[1]-1)))*res + +(int)(vec[2]*(size[2]-1)))->flag &= ~PARS_UNEXIST; + } + } + else if(ELEM(from,PART_FROM_FACE,PART_FROM_VOLUME)){ + float co1[3], co2[3]; + + MFace *mface=0; + float v1[3], v2[3], v3[3], v4[4], lambda; + int a, a1, a2, a0mul, a1mul, a2mul, totface; + int amax= from==PART_FROM_FACE ? 3 : 1; + + totface=dm->getNumFaces(dm); + mface=dm->getFaceDataArray(dm,CD_MFACE); + + for(a=0; a<amax; a++){ + if(a==0){ a0mul=res*res; a1mul=res; a2mul=1; } + else if(a==1){ a0mul=res; a1mul=1; a2mul=res*res; } + else{ a0mul=1; a1mul=res*res; a2mul=res; } + + for(a1=0; a1<size[(a+1)%3]; a1++){ + for(a2=0; a2<size[(a+2)%3]; a2++){ + mface=dm->getFaceDataArray(dm,CD_MFACE); + + pa=psys->particles + a1*a1mul + a2*a2mul; + VECCOPY(co1,pa->fuv); + co1[a]-=d/2.0f; + VECCOPY(co2,co1); + co2[a]+=delta[a] + 0.001f*d; + co1[a]-=0.001f*d; + + /* lets intersect the faces */ + for(i=0; i<totface; i++,mface++){ + VECCOPY(v1,mvert[mface->v1].co); + VECCOPY(v2,mvert[mface->v2].co); + VECCOPY(v3,mvert[mface->v3].co); + + if(AxialLineIntersectsTriangle(a,co1, co2, v2, v3, v1, &lambda)){ + if(from==PART_FROM_FACE) + (pa+(int)(lambda*size[a])*a0mul)->flag &= ~PARS_UNEXIST; + else /* store number of intersections */ + (pa+(int)(lambda*size[a])*a0mul)->loop++; + } + + if(mface->v4){ + VECCOPY(v4,mvert[mface->v4].co); + + if(AxialLineIntersectsTriangle(a,co1, co2, v4, v1, v3, &lambda)){ + if(from==PART_FROM_FACE) + (pa+(int)(lambda*size[a])*a0mul)->flag &= ~PARS_UNEXIST; + else + (pa+(int)(lambda*size[a])*a0mul)->loop++; + } + } + } + + if(from==PART_FROM_VOLUME){ + int in=pa->loop%2; + if(in) pa->loop++; + for(i=0; i<size[0]; i++){ + if(in || (pa+i*a0mul)->loop%2) + (pa+i*a0mul)->flag &= ~PARS_UNEXIST; + /* odd intersections == in->out / out->in */ + /* even intersections -> in stays same */ + in=(in + (pa+i*a0mul)->loop) % 2; + } + } + } + } + } + } + + if(psys->part->flag & PART_GRID_INVERT){ + for(i=0,pa=psys->particles; i<size[0]; i++){ + for(j=0; j<size[1]; j++){ + pa=psys->particles + res*(i*res + j); + for(k=0; k<size[2]; k++, pa++){ + pa->flag ^= PARS_UNEXIST; + } + } + } + } +} + +/* modified copy from effect.c */ +static void init_mv_jit(float *jit, int num, int seed2, float amount) +{ + RNG *rng; + float *jit2, x, rad1, rad2, rad3; + int i, num2; + + if(num==0) return; + + rad1= (float)(1.0/sqrt((float)num)); + rad2= (float)(1.0/((float)num)); + rad3= (float)sqrt((float)num)/((float)num); + + rng = rng_new(31415926 + num + seed2); + x= 0; + num2 = 2 * num; + for(i=0; i<num2; i+=2) { + + jit[i]= x + amount*rad1*(0.5f - rng_getFloat(rng)); + jit[i+1]= i/(2.0f*num) + amount*rad1*(0.5f - rng_getFloat(rng)); + + jit[i]-= (float)floor(jit[i]); + jit[i+1]-= (float)floor(jit[i+1]); + + x+= rad3; + x -= (float)floor(x); + } + + jit2= MEM_mallocN(12 + 2*sizeof(float)*num, "initjit"); + + for (i=0 ; i<4 ; i++) { + BLI_jitterate1(jit, jit2, num, rad1); + BLI_jitterate1(jit, jit2, num, rad1); + BLI_jitterate2(jit, jit2, num, rad2); + } + MEM_freeN(jit2); + rng_free(rng); +} + +static void psys_uv_to_w(float u, float v, int quad, float *w) +{ + float vert[4][3], co[3]; + + if(!quad) { + if(u+v > 1.0f) + v= 1.0f-v; + else + u= 1.0f-u; + } + + vert[0][0]= 0.0f; vert[0][1]= 0.0f; vert[0][2]= 0.0f; + vert[1][0]= 1.0f; vert[1][1]= 0.0f; vert[1][2]= 0.0f; + vert[2][0]= 1.0f; vert[2][1]= 1.0f; vert[2][2]= 0.0f; + + co[0]= u; + co[1]= v; + co[2]= 0.0f; + + if(quad) { + vert[3][0]= 0.0f; vert[3][1]= 1.0f; vert[3][2]= 0.0f; + MeanValueWeights(vert, 4, co, w); + } + else { + MeanValueWeights(vert, 3, co, w); + w[3]= 0.0f; + } +} + +static int binary_search_distribution(float *sum, int n, float value) +{ + int mid, low=0, high=n; + + while(low <= high) { + mid= (low + high)/2; + if(sum[mid] <= value && value <= sum[mid+1]) + return mid; + else if(sum[mid] > value) + high= mid - 1; + else if(sum[mid] < value) + low= mid + 1; + else + return mid; + } + + return low; +} + +/* creates a distribution of coordinates on a DerivedMesh */ +/* */ +/* 1. lets check from what we are emitting */ +/* 2. now we know that we have something to emit from so */ +/* let's calculate some weights */ +/* 2.1 from even distribution */ +/* 2.2 and from vertex groups */ +/* 3. next we determine the indexes of emitting thing that */ +/* the particles will have */ +/* 4. let's do jitter if we need it */ +/* 5. now we're ready to set the indexes & distributions to */ +/* the particles */ +/* 6. and we're done! */ + +/* This is to denote functionality that does not yet work with mesh - only derived mesh */ +#define ONLY_WORKING_WITH_PA_VERTS 0 +static void distribute_particles_on_dm(DerivedMesh *finaldm, Object *ob, ParticleSystem *psys, int from) +{ + Object *tob; + ParticleData *pa=0, *tpars=0, *tpa; + ParticleSettings *part; + ParticleSystem *tpsys; + ChildParticle *cpa=0; + KDTree *tree=0; + ParticleSeam *seams=0; + float *jit= NULL; + int p=0,i; + int no_distr=0, cfrom=0; + int tot=0, totpart, *index=0, children=0, totseam=0; + //int *vertpart=0; + int jitlevel= 1, intersect, distr; + float *weight=0,*sum=0,*jitoff=0; + float cur, maxweight=0.0,tweight; + float *v1, *v2, *v3, *v4, co[3], nor[3], co1[3], co2[3], nor1[3]; + float cur_d, min_d; + DerivedMesh *dm= NULL; + + if(ob==0 || psys==0 || psys->part==0) + return; + + part=psys->part; + totpart=psys->totpart; + if(totpart==0) + return; + + if (!finaldm->deformedOnly && !CustomData_has_layer( &finaldm->faceData, CD_ORIGINDEX ) ) { + error("Can't paint with the current modifier stack, disable destructive modifiers"); + return; + } + + BLI_srandom(31415926 + psys->seed); + + if(from==PART_FROM_CHILD){ + distr=PART_DISTR_RAND; + cpa=psys->child; + if(part->from!=PART_FROM_PARTICLE && part->childtype==PART_CHILD_FACES){ + dm= finaldm; + children=1; + + tree=BLI_kdtree_new(totpart); + + for(p=0,pa=psys->particles; p<totpart; p++,pa++){ + psys_particle_on_dm(ob,dm,part->from,pa->num,pa->num_dmcache,pa->fuv,pa->foffset,co,nor,0,0); + BLI_kdtree_insert(tree, p, co, nor); + } + + BLI_kdtree_balance(tree); + + totpart=psys->totchild; + cfrom=from=PART_FROM_FACE; + + if(part->flag&PART_CHILD_SEAMS){ + MEdge *ed, *medge=dm->getEdgeDataArray(dm,CD_MEDGE); + MVert *mvert=dm->getVertDataArray(dm,CD_MVERT); + int totedge=dm->getNumEdges(dm); + + for(p=0, ed=medge; p<totedge; p++,ed++) + if(ed->flag&ME_SEAM) + totseam++; + + if(totseam){ + ParticleSeam *cur_seam=seams=MEM_callocN(totseam*sizeof(ParticleSeam),"Child Distribution Seams"); + float temp[3],temp2[3]; + + for(p=0, ed=medge; p<totedge; p++,ed++){ + if(ed->flag&ME_SEAM){ + VecCopyf(cur_seam->v0,(mvert+ed->v1)->co); + VecCopyf(cur_seam->v1,(mvert+ed->v2)->co); + + VecSubf(cur_seam->dir,cur_seam->v1,cur_seam->v0); + + cur_seam->length2=VecLength(cur_seam->dir); + cur_seam->length2*=cur_seam->length2; + + temp[0]=(float)((mvert+ed->v1)->no[0]); + temp[1]=(float)((mvert+ed->v1)->no[1]); + temp[2]=(float)((mvert+ed->v1)->no[2]); + temp2[0]=(float)((mvert+ed->v2)->no[0]); + temp2[1]=(float)((mvert+ed->v2)->no[1]); + temp2[2]=(float)((mvert+ed->v2)->no[2]); + + VecAddf(cur_seam->nor,temp,temp2); + Normalize(cur_seam->nor); + + Crossf(cur_seam->tan,cur_seam->dir,cur_seam->nor); + + Normalize(cur_seam->tan); + + cur_seam++; + } + } + } + + } + } + else{ + /* no need to figure out distribution */ + for(i=0; i<part->child_nbr; i++){ + for(p=0; p<psys->totpart; p++,cpa++){ + float length=2.0; + cpa->parent=p; + + /* create even spherical distribution inside unit sphere */ + while(length>=1.0f){ + cpa->fuv[0]=2.0f*BLI_frand()-1.0f; + cpa->fuv[1]=2.0f*BLI_frand()-1.0f; + cpa->fuv[2]=2.0f*BLI_frand()-1.0f; + length=VecLength(cpa->fuv); + } + + cpa->rand[0]=BLI_frand(); + cpa->rand[1]=BLI_frand(); + cpa->rand[2]=BLI_frand(); + + cpa->num=-1; + } + } + + return; + } + } + else{ + dm= CDDM_from_mesh((Mesh*)ob->data, ob); + + /* special handling of grid distribution */ + if(part->distr==PART_DISTR_GRID){ + distribute_particles_in_grid(dm,psys); + dm->release(dm); + return; + } + + distr=part->distr; + pa=psys->particles; + if(from==PART_FROM_VERT){ + MVert *mv= dm->getVertDataArray(dm,0); + int totvert = dm->getNumVerts(dm); + + tree=BLI_kdtree_new(totvert); + + for(p=0; p<totvert; p++,mv++){ + VECCOPY(co,mv->co); + BLI_kdtree_insert(tree,p,co,NULL); + } + + BLI_kdtree_balance(tree); + } + } + + /* 1. */ + switch(from){ + case PART_FROM_VERT: + tot = dm->getNumVerts(dm); + break; + case PART_FROM_VOLUME: + case PART_FROM_FACE: + tot = dm->getNumFaces(dm); + break; + case PART_FROM_PARTICLE: + if(psys->target_ob) + tob=psys->target_ob; + else + tob=ob; + + if((tpsys=BLI_findlink(&tob->particlesystem,psys->target_psys-1))){ + tpars=tpsys->particles; + tot=tpsys->totpart; + } + break; + } + + if(tot==0){ + no_distr=1; + if(children){ + fprintf(stderr,"Particle child distribution error: Nothing to emit from!\n"); + for(p=0,cpa=psys->child; p<totpart; p++,cpa++){ + cpa->fuv[0]=cpa->fuv[1]=cpa->fuv[2]=cpa->fuv[3]= 0.0; + cpa->foffset= 0.0f; + cpa->parent=0; + cpa->pa[0]=cpa->pa[1]=cpa->pa[2]=cpa->pa[3]=0; + cpa->num= -1; + } + } + else { + fprintf(stderr,"Particle distribution error: Nothing to emit from!\n"); + for(p=0,pa=psys->particles; p<totpart; p++,pa++){ + pa->fuv[0]=pa->fuv[1]=pa->fuv[2]= pa->fuv[3]= 0.0; + pa->foffset= 0.0f; + pa->num= -1; + } + } + + if(dm != finaldm) dm->release(dm); + return; + } + + /* 2. */ + + weight=MEM_callocN(sizeof(float)*tot, "particle_distribution_weights"); + index=MEM_callocN(sizeof(int)*totpart, "particle_distribution_indexes"); + sum=MEM_callocN(sizeof(float)*(tot+1), "particle_distribution_sum"); + jitoff=MEM_callocN(sizeof(float)*tot, "particle_distribution_jitoff"); + + /* 2.1 */ + if((part->flag&PART_EDISTR || children) && ELEM(from,PART_FROM_PARTICLE,PART_FROM_VERT)==0){ + float totarea=0.0; + + for(i=0; i<tot; i++){ + MFace *mf=dm->getFaceData(dm,i,CD_MFACE); + MVert *mv1=dm->getVertData(dm,mf->v1,CD_MVERT); + MVert *mv2=dm->getVertData(dm,mf->v2,CD_MVERT); + MVert *mv3=dm->getVertData(dm,mf->v3,CD_MVERT); + + if (mf->v4){ + MVert *mv4=dm->getVertData(dm,mf->v4,CD_MVERT); + cur= AreaQ3Dfl(mv1->co,mv2->co,mv3->co,mv4->co); + } + else + cur= AreaT3Dfl(mv1->co,mv2->co,mv3->co); + + if(cur>maxweight) + maxweight=cur; + + weight[i]= cur; + totarea+=cur; + } + + for(i=0; i<tot; i++) + weight[i] /= totarea; + + maxweight /= totarea; + } + else if(from==PART_FROM_PARTICLE){ + float val=(float)tot/(float)totpart; + for(i=0; i<tot; i++) + weight[i]=val; + maxweight=val; + } + else{ + float min=1.0f/(float)(MIN2(tot,totpart)); + for(i=0; i<tot; i++) + weight[i]=min; + maxweight=min; + } + + /* 2.2 */ + if(ELEM3(from,PART_FROM_VERT,PART_FROM_FACE,PART_FROM_VOLUME)){ + float *vweight= psys_cache_vgroup(dm,psys,PSYS_VG_DENSITY); + + if(vweight){ + if(from==PART_FROM_VERT) { + for(i=0;i<tot; i++) + weight[i]*=vweight[i]; + } + else { /* PART_FROM_FACE / PART_FROM_VOLUME */ + for(i=0;i<tot; i++){ + MFace *mf=dm->getFaceData(dm,i,CD_MFACE); + tweight = vweight[mf->v1] + vweight[mf->v2] + vweight[mf->v3]; + + if(mf->v4) { + tweight += vweight[mf->v4]; + tweight /= 4.0; + } + else { + tweight /= 3.0; + } + + weight[i]*=tweight; + } + } + MEM_freeN(vweight); + } + } + + /* 3. */ + sum[0]= 0.0f; + for(i=0;i<tot; i++) + sum[i+1]= sum[i]+weight[i]; + + if(part->flag&PART_TRAND){ + float pos; + + for(p=0; p<totpart; p++) { + pos= BLI_frand(); + index[p]= binary_search_distribution(sum, tot, pos); + jitoff[index[p]]= pos; + } + } + else { + float step, pos; + + step= (totpart <= 1)? 0.5f: 1.0f/(totpart-1); + pos= 0.0f; + i= 0; + + for(p=0; p<totpart; p++, pos+=step) { + while((i < tot) && (pos > sum[i+1])) + i++; + + index[p]= MIN2(tot-1, i); + jitoff[index[p]]= pos; + } + } + + /* weights are no longer used except for FROM_PARTICLE, which needs them zeroed for indexing */ + if(from==PART_FROM_PARTICLE){ + for(i=0; i<tot; i++) + weight[i]=0.0f; + } + + /* 4. */ + if(distr==PART_DISTR_JIT && ELEM(from,PART_FROM_FACE,PART_FROM_VOLUME)) { + jitlevel= part->userjit; + + if(jitlevel == 0) { + jitlevel= totpart/tot; + if(part->flag & PART_EDISTR) jitlevel*= 2; /* looks better in general, not very scietific */ + if(jitlevel<3) jitlevel= 3; + //if(jitlevel>100) jitlevel= 100; + } + + jit= MEM_callocN(2+ jitlevel*2*sizeof(float), "jit"); + + init_mv_jit(jit, jitlevel, psys->seed, part->jitfac); + BLI_array_randomize(jit, 2*sizeof(float), jitlevel, psys->seed); /* for custom jit or even distribution */ + } + + /* 5. */ + if(children) from=PART_FROM_CHILD; + for(p=0,pa=psys->particles; p<totpart; p++,pa++,cpa++){ + switch(from){ + case PART_FROM_VERT: + /* TODO_PARTICLE - use original index */ + pa->num=index[p]; + pa->fuv[0] = 1.0f; + pa->fuv[1] = pa->fuv[2] = pa->fuv[3] = 0.0; + //pa->verts[0] = pa->verts[1] = pa->verts[2] = 0; + +#if ONLY_WORKING_WITH_PA_VERTS + if(tree){ + KDTreeNearest ptn[3]; + int w,maxw; + + psys_particle_on_dm(ob,dm,from,pa->num,pa->num_dmcache,pa->fuv,pa->foffset,co1,0,0,0); + maxw = BLI_kdtree_find_n_nearest(tree,3,co1,NULL,ptn); + + for(w=0; w<maxw; w++){ + pa->verts[w]=ptn->num; + } + } +#endif + break; + case PART_FROM_FACE: + case PART_FROM_VOLUME: + { + MFace *mface; + pa->num = i = index[p]; + mface = dm->getFaceData(dm,i,CD_MFACE); + + switch(distr){ + case PART_DISTR_JIT: + jitoff[i] = fmod(jitoff[i],(float)jitlevel); + psys_uv_to_w(jit[2*(int)jitoff[i]], jit[2*(int)jitoff[i]+1], mface->v4, pa->fuv); + jitoff[i]++; + //jitoff[i]=(float)fmod(jitoff[i]+maxweight/weight[i],(float)jitlevel); + break; + case PART_DISTR_RAND: + psys_uv_to_w(BLI_frand(), BLI_frand(), mface->v4, pa->fuv); + break; + } + pa->foffset= 0.0f; + + /* + pa->verts[0] = mface->v1; + pa->verts[1] = mface->v2; + pa->verts[2] = mface->v3; + */ + + /* experimental */ + if(from==PART_FROM_VOLUME){ + MVert *mvert=dm->getVertDataArray(dm,CD_MVERT); + + tot=dm->getNumFaces(dm); + + psys_interpolate_face(mvert,mface,0,pa->fuv,co1,nor,0,0); + + Normalize(nor); + VecMulf(nor,-100.0); + + VECADD(co2,co1,nor); + + min_d=2.0; + intersect=0; + + for(i=0,mface=dm->getFaceDataArray(dm,CD_MFACE); i<tot; i++,mface++){ + if(i==pa->num) continue; + + v1=mvert[mface->v1].co; + v2=mvert[mface->v2].co; + v3=mvert[mface->v3].co; + + if(LineIntersectsTriangle(co1, co2, v2, v3, v1, &cur_d, 0)){ + if(cur_d<min_d){ + min_d=cur_d; + pa->foffset=cur_d*50.0f; /* to the middle of volume */ + intersect=1; + } + } + if(mface->v4){ + v4=mvert[mface->v4].co; + + if(LineIntersectsTriangle(co1, co2, v4, v1, v3, &cur_d, 0)){ + if(cur_d<min_d){ + min_d=cur_d; + pa->foffset=cur_d*50.0f; /* to the middle of volume */ + intersect=1; + } + } + } + } + if(intersect==0) + pa->foffset=0.0; + else switch(distr){ + case PART_DISTR_JIT: + pa->foffset*= jit[2*(int)jitoff[i]]; + break; + case PART_DISTR_RAND: + pa->foffset*=BLI_frand(); + break; + } + } + break; + } + case PART_FROM_PARTICLE: + + //pa->verts[0]=0; /* not applicable */ + //pa->verts[1]=0; + //pa->verts[2]=0; + + tpa=tpars+index[p]; + pa->num=index[p]; + pa->fuv[0]=tpa->fuv[0]; + pa->fuv[1]=tpa->fuv[1]; + /* abusing foffset a little for timing in near reaction */ + pa->foffset=weight[index[p]]; + weight[index[p]]+=maxweight; + break; + case PART_FROM_CHILD: + if(index[p]>=0){ + MFace *mf; + + mf=dm->getFaceData(dm,index[p],CD_MFACE); + + //switch(distr){ + // case PART_DISTR_JIT: + // i=index[p]; + // psys_uv_to_w(jit[2*(int)jitoff[i]], jit[2*(int)jitoff[i]+1], mf->v4, cpa->fuv); + // jitoff[i]=(float)fmod(jitoff[i]+maxweight/weight[i],(float)jitlevel); + // break; + // case PART_DISTR_RAND: + psys_uv_to_w(BLI_frand(), BLI_frand(), mf->v4, cpa->fuv); + // break; + //} + + cpa->rand[0] = BLI_frand(); + cpa->rand[1] = BLI_frand(); + cpa->rand[2] = BLI_frand(); + cpa->num = index[p]; + + if(tree){ + KDTreeNearest ptn[10]; + int w,maxw, do_seams; + float maxd,mind,dd,totw=0.0; + int parent[10]; + float pweight[10]; + + do_seams= (part->flag&PART_CHILD_SEAMS && seams); + + psys_particle_on_dm(ob,dm,cfrom,cpa->num,DMCACHE_ISCHILD,cpa->fuv,cpa->foffset,co1,nor1,0,0); + maxw = BLI_kdtree_find_n_nearest(tree,(do_seams)?10:4,co1,nor1,ptn); + + maxd=ptn[maxw-1].dist; + mind=ptn[0].dist; + dd=maxd-mind; + + /* the weights here could be done better */ + for(w=0; w<maxw; w++){ + parent[w]=ptn[w].index; + pweight[w]=(float)pow(2.0,(double)(-6.0f*ptn[w].dist/maxd)); + //totw+=cpa->w[w]; + } + for(;w<10; w++){ + parent[w]=-1; + pweight[w]=0.0f; + } + if(do_seams){ + ParticleSeam *seam=seams; + float temp[3],temp2[3],tan[3]; + float inp,cur_len,min_len=10000.0f; + int min_seam=0, near_vert=0; + /* find closest seam */ + for(i=0; i<totseam; i++, seam++){ + VecSubf(temp,co1,seam->v0); + inp=Inpf(temp,seam->dir)/seam->length2; + if(inp<0.0f){ + cur_len=VecLenf(co1,seam->v0); + } + else if(inp>1.0f){ + cur_len=VecLenf(co1,seam->v1); + } + else{ + VecCopyf(temp2,seam->dir); + VecMulf(temp2,inp); + cur_len=VecLenf(temp,temp2); + } + if(cur_len<min_len){ + min_len=cur_len; + min_seam=i; + if(inp<0.0f) near_vert=-1; + else if(inp>1.0f) near_vert=1; + else near_vert=0; + } + } + seam=seams+min_seam; + + VecCopyf(temp,seam->v0); + + if(near_vert){ + if(near_vert==-1) + VecSubf(tan,co1,seam->v0); + else{ + VecSubf(tan,co1,seam->v1); + VecCopyf(temp,seam->v1); + } + + Normalize(tan); + } + else{ + VecCopyf(tan,seam->tan); + VecSubf(temp2,co1,temp); + if(Inpf(tan,temp2)<0.0f) + VecMulf(tan,-1.0f); + } + for(w=0; w<maxw; w++){ + VecSubf(temp2,ptn[w].co,temp); + if(Inpf(tan,temp2)<0.0f){ + parent[w]=-1; + pweight[w]=0.0f; + } + } + + } + + for(w=0,i=0; w<maxw && i<4; w++){ + if(parent[w]>=0){ + cpa->pa[i]=parent[w]; + cpa->w[i]=pweight[w]; + totw+=pweight[w]; + i++; + } + } + for(;i<4; i++){ + cpa->pa[i]=-1; + cpa->w[i]=0.0f; + } + + if(totw>0.0f) for(w=0; w<4; w++) + cpa->w[w]/=totw; + + cpa->parent=cpa->pa[0]; + } + } + else{ + cpa->num=0; + cpa->fuv[0]=cpa->fuv[1]=cpa->fuv[2]=cpa->fuv[3]=0.0f; + cpa->pa[0]=cpa->pa[1]=cpa->pa[2]=cpa->pa[3]=0; + cpa->rand[0]=cpa->rand[1]=cpa->rand[2]=0.0f; + } + break; + } + } + + /* 6. */ + if(jit) MEM_freeN(jit); + if(sum) MEM_freeN(sum); + if(jitoff) MEM_freeN(jitoff); + if(weight){ + MEM_freeN(weight); + weight=0; + } + if(index) MEM_freeN(index); + if(seams) MEM_freeN(seams); + //if(vertpart) MEM_freeN(vertpart); + BLI_kdtree_free(tree); + + if (from == PART_FROM_FACE) + psys_calc_dmfaces(ob, finaldm, psys); + + if(dm != finaldm) dm->release(dm); +} + +/* ready for future use, to emit particles without geometry */ +static void distribute_particles_on_shape(Object *ob, ParticleSystem *psys, int from) +{ + ParticleData *pa; + int totpart=psys->totpart, p; + + fprintf(stderr,"Shape emission not yet possible!\n"); + + for(p=0,pa=psys->particles; p<totpart; p++,pa++){ + pa->fuv[0]=pa->fuv[1]=pa->fuv[2]=pa->fuv[3]= 0.0; + pa->foffset= 0.0f; + pa->num= -1; + } +} +static void distribute_particles(Object *ob, ParticleSystem *psys, int from) +{ + ParticleSystemModifierData *psmd=0; + int distr_error=0; + psmd=psys_get_modifier(ob,psys); + + if(psmd){ + if(psmd->dm) + distribute_particles_on_dm(psmd->dm,ob,psys,from); + else + distr_error=1; + } + else + distribute_particles_on_shape(ob,psys,from); + + if(distr_error){ + ParticleData *pa; + int totpart=psys->totpart, p; + + fprintf(stderr,"Particle distribution error!\n"); + + for(p=0,pa=psys->particles; p<totpart; p++,pa++){ + pa->fuv[0]=pa->fuv[1]=pa->fuv[2]=pa->fuv[3]= 0.0; + pa->foffset= 0.0f; + pa->num= -1; + } + } +} +/* set particle parameters that don't change during particle's life */ +void initialize_particle(ParticleData *pa, int p, Object *ob, ParticleSystem *psys, ParticleSystemModifierData *psmd) +{ + ParticleSettings *part; + ParticleTexture ptex; + Material *ma=0; + IpoCurve *icu=0; + int totpart; + float rand,length; + + part=psys->part; + + totpart=psys->totpart; + + ptex.life=ptex.size=ptex.exist=ptex.length=1.0; + ptex.time=(float)p/(float)totpart; + + BLI_srandom(psys->seed+p); + + if(part->from!=PART_FROM_PARTICLE){ + ma=give_current_material(ob,part->omat); + + /* TODO: needs some work to make most blendtypes generally usefull */ + psys_get_texture(ob,ma,psmd,psys,pa,&ptex,MAP_PA_INIT); + } + + pa->lifetime= part->lifetime*ptex.life; + + if(part->type==PART_HAIR) + pa->time=0.0f; + else if(part->type==PART_REACTOR && (part->flag&PART_REACT_STA_END)==0) + pa->time=MAXFRAMEF; + else{ + //icu=find_ipocurve(psys->part->ipo,PART_EMIT_TIME); + //if(icu){ + // calc_icu(icu,100*ptex.time); + // ptex.time=icu->curval; + //} + + pa->time= part->sta + (part->end - part->sta)*ptex.time; + } + + + if(part->type==PART_HAIR){ + pa->lifetime=100.0f; + } + else{ + icu=find_ipocurve(psys->part->ipo,PART_EMIT_LIFE); + if(icu){ + calc_icu(icu,100*ptex.time); + pa->lifetime*=icu->curval; + } + + /* need to get every rand even if we don't use them so that randoms don't affect eachother */ + rand= BLI_frand(); + if(part->randlife!=0.0) + pa->lifetime*= 1.0f - part->randlife*rand; + } + + pa->dietime= pa->time+pa->lifetime; + + pa->sizemul= BLI_frand(); + + 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); + } + + length=2.0f; + while(length>1.0){ + pa->r_ave[0]=2.0f*(BLI_frand()-0.5f); + pa->r_ave[1]=2.0f*(BLI_frand()-0.5f); + pa->r_ave[2]=2.0f*(BLI_frand()-0.5f); + length=VecLength(pa->r_ave); + } + + pa->r_rot[0]=2.0f*(BLI_frand()-0.5f); + pa->r_rot[1]=2.0f*(BLI_frand()-0.5f); + pa->r_rot[2]=2.0f*(BLI_frand()-0.5f); + pa->r_rot[3]=2.0f*(BLI_frand()-0.5f); + + NormalQuat(pa->r_rot); + + if(part->distr!=PART_DISTR_GRID){ + /* any unique random number will do (r_ave[0]) */ + if(ptex.exist < 0.5*(1.0+pa->r_ave[0])) + pa->flag |= PARS_UNEXIST; + else + pa->flag &= ~PARS_UNEXIST; + } + + pa->loop=0; + /* we can't reset to -1 anymore since we've figured out correct index in distribute_particles */ + /* usage other than straight after distribute has to handle this index by itself - jahka*/ + //pa->num_dmcache = DMCACHE_NOTFOUND; /* assume we dont have a derived mesh face */ +} +static void initialize_all_particles(Object *ob, ParticleSystem *psys, ParticleSystemModifierData *psmd) +{ + IpoCurve *icu=0; + ParticleData *pa; + int p, totpart=psys->totpart; + + for(p=0, pa=psys->particles; p<totpart; p++, pa++) + initialize_particle(pa,p,ob,psys,psmd); + + /* store the derived mesh face index for each particle */ + //if(psys->part->from==PART_FROM_FACE) + // psys_calc_dmfaces(ob, psmd->dm, psys); + + icu=find_ipocurve(psys->part->ipo,PART_EMIT_FREQ); + if(icu){ + float time=psys->part->sta, end=psys->part->end; + float v1, v2, a=0.0f, t1,t2, d; + + p=0; + pa=psys->particles; + + calc_icu(icu,time); + v1=icu->curval; + if(v1<0.0f) v1=0.0f; + + calc_icu(icu,time+1.0f); + v2=icu->curval; + if(v2<0.0f) v2=0.0f; + + for(p=0, pa=psys->particles; p<totpart && time<end; p++, pa++){ + while(a+0.5f*(v1+v2) < (float)(p+1) && time<end){ + a+=0.5f*(v1+v2); + v1=v2; + time++; + calc_icu(icu,time+1.0f); + v2=icu->curval; + } + if(time<end){ + if(v1==v2){ + pa->time=time+((float)(p+1)-a)/v1; + } + else{ + d=(float)sqrt(v1*v1-2.0f*(v2-v1)*(a-(float)(p+1))); + t1=(-v1+d)/(v2-v1); + t2=(-v1-d)/(v2-v1); + + /* the root between 0-1 is the correct one */ + if(t1>0.0f && t1<=1.0f) + pa->time=time+t1; + else + pa->time=time+t2; + } + } + + pa->dietime = pa->time+pa->lifetime; + pa->flag &= ~PARS_UNEXIST; + } + for(; p<totpart; p++, pa++){ + pa->flag |= PARS_UNEXIST; + } + } +} +/* sets particle to the emitter surface with initial velocity & rotation */ +void reset_particle(ParticleData *pa, ParticleSystem *psys, ParticleSystemModifierData *psmd, Object *ob, + float dtime, float cfra, float *vg_vel, float *vg_tan, float *vg_rot) +{ + ParticleSettings *part; + ParticleTexture ptex; + ParticleKey state; + IpoCurve *icu=0; + float fac, nor[3]={0,0,0},loc[3],tloc[3],vel[3]={0.0,0.0,0.0},rot[4],*q2=0; + float r_vel[3],r_ave[3],r_rot[4],p_vel[3]={0.0,0.0,0.0}; + float x_vec[3]={1.0,0.0,0.0}, utan[3]={0.0,1.0,0.0}, vtan[3]={0.0,0.0,1.0}; + + float q_one[4]={1.0,0.0,0.0,0.0}, q_phase[4]; + part=psys->part; + + ptex.ivel=1.0; + + if(part->from==PART_FROM_PARTICLE){ + Object *tob; + ParticleSystem *tpsys=0; + float speed; + + tob=psys->target_ob; + if(tob==0) + tob=ob; + + tpsys=BLI_findlink(&tob->particlesystem,psys->target_psys-1); + + /*TODO: get precise location of particle at birth*/ + + state.time=cfra; + psys_get_particle_state(tob,tpsys,pa->num,&state,1); + psys_get_from_key(&state,loc,nor,rot,0); + + QuatMulVecf(rot,vtan); + QuatMulVecf(rot,utan); + VECCOPY(r_vel,pa->r_ve); + VECCOPY(r_rot,pa->r_rot); + VECCOPY(r_ave,pa->r_ave); + + VECCOPY(p_vel,state.vel); + speed=Normalize(p_vel); + VecMulf(p_vel,Inpf(pa->r_ve,p_vel)); + VECSUB(p_vel,pa->r_ve,p_vel); + Normalize(p_vel); + VecMulf(p_vel,speed); + } + else{ + /* get precise emitter matrix if particle is born */ + if(part->type!=PART_HAIR && pa->time < cfra && pa->time >= psys->cfra) + where_is_object_time(ob,pa->time); + + /* get birth location from object */ + psys_particle_on_emitter(ob,psmd,part->from,pa->num, pa->num_dmcache, pa->fuv,pa->foffset,loc,nor,utan,vtan); + + /* save local coordinates for later */ + VECCOPY(tloc,loc); + + /* get possible textural influence */ + psys_get_texture(ob,give_current_material(ob,part->omat),psmd,psys,pa,&ptex,MAP_PA_IVEL); + + if(vg_vel){ + ptex.ivel*=psys_interpolate_value_from_verts(psmd->dm,part->from,pa->num,pa->fuv,vg_vel); + } + + /* particles live in global space so */ + /* let's convert: */ + /* -location */ + Mat4MulVecfl(ob->obmat,loc); + + /* -normal */ + VECADD(nor,tloc,nor); + Mat4MulVecfl(ob->obmat,nor); + VECSUB(nor,nor,loc); + Normalize(nor); + + /* -tangent */ + if(part->tanfac!=0.0){ + float phase=vg_rot?2.0f*(psys_interpolate_value_from_verts(psmd->dm,part->from,pa->num,pa->fuv,vg_rot)-0.5f):0.0f; + VecMulf(vtan,-(float)cos(M_PI*(part->tanphase+phase))); + fac=-(float)sin(M_PI*(part->tanphase+phase)); + VECADDFAC(vtan,vtan,utan,fac); + + VECADD(vtan,tloc,vtan); + Mat4MulVecfl(ob->obmat,vtan); + VECSUB(vtan,vtan,loc); + + VECCOPY(utan,nor); + VecMulf(utan,Inpf(vtan,nor)); + VECSUB(vtan,vtan,utan); + + Normalize(vtan); + } + + + /* -velocity */ + if(part->randfac!=0.0){ + VECADD(r_vel,tloc,pa->r_ve); + Mat4MulVecfl(ob->obmat,r_vel); + VECSUB(r_vel,r_vel,loc); + Normalize(r_vel); + } + + /* -angular velocity */ + if(part->avemode==PART_AVE_RAND){ + VECADD(r_ave,tloc,pa->r_ave); + Mat4MulVecfl(ob->obmat,r_ave); + VECSUB(r_ave,r_ave,loc); + Normalize(r_ave); + } + + /* -rotation */ + if(part->rotmode==PART_ROT_RAND){ + QUATCOPY(r_rot,pa->r_rot); + Mat4ToQuat(ob->obmat,rot); + QuatMul(r_rot,r_rot,rot); + } + } + /* conversion done so now we apply new: */ + /* -velocity from: */ + /* *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(part->tanfac!=0.0) + VECADDFAC(vel,vel,vtan,part->tanfac*(vg_tan?psys_interpolate_value_from_verts(psmd->dm,part->from,pa->num,pa->fuv,vg_tan):1.0f)); + + /* *texture */ + /* TODO */ + + /* *random */ + if(part->randfac!=0.0) + VECADDFAC(vel,vel,r_vel,part->randfac); + + /* *particle */ + if(part->partfac!=0.0) + VECADDFAC(vel,vel,p_vel,part->partfac); + + 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; + } + + VecMulf(vel,ptex.ivel); + + VECCOPY(pa->state.vel,vel); + + /* -location from emitter */ + VECCOPY(pa->state.co,loc); + + /* -rotation */ + pa->state.rot[0]=1.0; + pa->state.rot[1]=pa->state.rot[2]=pa->state.rot[3]=0.0; + + if(part->rotmode){ + switch(part->rotmode){ + case PART_ROT_NOR: + VecMulf(nor,-1.0); + q2= vectoquat(nor, OB_POSX, OB_POSZ); + VecMulf(nor,-1.0); + break; + case PART_ROT_VEL: + VecMulf(vel,-1.0); + q2= vectoquat(vel, OB_POSX, OB_POSZ); + VecMulf(vel,-1.0); + break; + case PART_ROT_RAND: + q2= r_rot; + break; + } + /* how much to rotate from rest position */ + QuatInterpol(rot,q_one,q2,part->rotfac); + + /* phase */ + VecRotToQuat(x_vec,part->phasefac*(float)M_PI,q_phase); + + /* combine amount & phase */ + QuatMul(pa->state.rot,rot,q_phase); + } + + /* -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); + } + } + + pa->dietime=pa->time+pa->lifetime; + + if(pa->time >= cfra) + pa->alive=PARS_UNBORN; + + pa->state.time=cfra; + + pa->stick_ob=0; + pa->flag&=~PARS_STICKY; +} +static void reset_all_particles(Object *ob, ParticleSystem *psys, ParticleSystemModifierData *psmd, float dtime, float cfra, int from) +{ + ParticleData *pa; + int p, totpart=psys->totpart; + float *vg_vel=psys_cache_vgroup(psmd->dm,psys,PSYS_VG_VEL); + float *vg_tan=psys_cache_vgroup(psmd->dm,psys,PSYS_VG_TAN); + float *vg_rot=psys_cache_vgroup(psmd->dm,psys,PSYS_VG_ROT); + + //if (psys->part->from == PART_FROM_FACE) + // psys_calc_dmfaces(ob, psmd->dm, psys); + + for(p=from, pa=psys->particles+from; p<totpart; p++, pa++) + reset_particle(pa, psys, psmd, ob, dtime, cfra, vg_vel, vg_tan, vg_rot); + + if(vg_vel) + MEM_freeN(vg_vel); +} +/************************************************/ +/* Keyed particles */ +/************************************************/ +/* a bit of an unintuitive function :) counts objects in a keyed chain and returns 1 if some of them were selected (used in drawing) */ +int psys_count_keyed_targets(Object *ob, ParticleSystem *psys) +{ + ParticleSystem *kpsys=psys,*tpsys; + ParticleSettings *tpart; + Object *kob=ob,*tob; + int select=ob->flag&SELECT; + short totkeyed=0; + Base *base; + + ListBase lb; + lb.first=lb.last=0; + + tob=psys->keyed_ob; + while(tob){ + if((tpsys=BLI_findlink(&tob->particlesystem,kpsys->keyed_psys-1))){ + tpart=tpsys->part; + + if(tpart->phystype==PART_PHYS_KEYED){ + if(lb.first){ + for(base=lb.first;base;base=base->next){ + if(tob==base->object){ + fprintf(stderr,"Error: loop in keyed chain!\n"); + BLI_freelistN(&lb); + return select; + } + } + } + base=MEM_callocN(sizeof(Base), "keyed base"); + base->object=tob; + BLI_addtail(&lb,base); + + if(tob->flag&SELECT) + select++; + kob=tob; + kpsys=tpsys; + tob=tpsys->keyed_ob; + totkeyed++; + } + else{ + tob=0; + totkeyed++; + } + } + else + tob=0; + } + psys->totkeyed=totkeyed; + BLI_freelistN(&lb); + return select; +} +void set_keyed_keys(Object *ob, ParticleSystem *psys) +{ + Object *kob = ob; + ParticleSystem *kpsys = psys; + ParticleData *pa; + ParticleKey state; + int totpart = psys->totpart, i, k, totkeys = psys->totkeyed + 1; + float prevtime, nexttime, keyedtime; + + /* no proper targets so let's clear and bail out */ + if(psys->totkeyed==0){ + free_keyed_keys(psys); + psys->flag &= ~PSYS_KEYED; + return; + } + + if(totpart && psys->particles->totkey != totkeys){ + free_keyed_keys(psys); + + psys->particles->keys = MEM_callocN(psys->totpart * totkeys * sizeof(ParticleKey),"Keyed keys"); + + psys->particles->totkey = totkeys; + + for(i=1, pa=psys->particles+1; i<totpart; i++,pa++){ + pa->keys = (pa-1)->keys + totkeys; + pa->totkey = totkeys; + } + } + + psys->flag &= ~PSYS_KEYED; + state.time=-1.0; + + for(k=0; k<totkeys; k++){ + for(i=0,pa=psys->particles; i<totpart; i++, pa++){ + psys_get_particle_state(kob, kpsys, i%kpsys->totpart, pa->keys + k, 1); + + 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; + } + else + (pa->keys+k)->time = pa->time + (float)k / (float)(totkeys - 1) * pa->lifetime; + } + } + if(kpsys->keyed_ob){ + kob = kpsys->keyed_ob; + kpsys = BLI_findlink(&kob->particlesystem, kpsys->keyed_psys - 1); + } + } + + psys->flag |= PSYS_KEYED; +} +/************************************************/ +/* Reactors */ +/************************************************/ +static void push_reaction(Object* ob, ParticleSystem *psys, int pa_num, int event, ParticleKey *state) +{ + Object *rob; + ParticleSystem *rpsys; + ParticleSettings *rpart; + ParticleData *pa; + ListBase *lb=&psys->effectors; + ParticleEffectorCache *ec; + ParticleReactEvent *re; + + if(lb->first) for(ec = lb->first; ec; ec= ec->next){ + if(ec->type & PSYS_EC_REACTOR){ + /* all validity checks already done in add_to_effectors */ + rob=ec->ob; + rpsys=BLI_findlink(&rob->particlesystem,ec->psys_nbr); + rpart=rpsys->part; + if(rpsys->part->reactevent==event){ + pa=psys->particles+pa_num; + re= MEM_callocN(sizeof(ParticleReactEvent), "react event"); + re->event=event; + re->pa_num = pa_num; + re->ob = ob; + re->psys = psys; + re->size = pa->size; + copy_particle_key(&re->state,state,1); + + switch(event){ + case PART_EVENT_DEATH: + re->time=pa->dietime; + break; + case PART_EVENT_COLLIDE: + re->time=state->time; + break; + case PART_EVENT_NEAR: + re->time=state->time; + break; + } + + BLI_addtail(&rpsys->reactevents, re); + } + } + } +} +static void react_to_events(ParticleSystem *psys, int pa_num) +{ + ParticleSettings *part=psys->part; + ParticleData *pa=psys->particles+pa_num; + ParticleReactEvent *re=psys->reactevents.first; + int birth=0; + float dist=0.0f; + + for(re=psys->reactevents.first; re; re=re->next){ + birth=0; + if(part->from==PART_FROM_PARTICLE){ + if(pa->num==re->pa_num){ + if(re->event==PART_EVENT_NEAR){ + ParticleData *tpa = re->psys->particles+re->pa_num; + float pa_time=tpa->time + pa->foffset*tpa->lifetime; + if(re->time > pa_time){ + pa->alive=PARS_ALIVE; + pa->time=pa_time; + pa->dietime=pa->time+pa->lifetime; + } + } + else{ + if(pa->alive==PARS_UNBORN){ + pa->alive=PARS_ALIVE; + pa->time=re->time; + pa->dietime=pa->time+pa->lifetime; + } + } + } + } + else{ + dist=VecLenf(pa->state.co, re->state.co); + if(dist <= re->size){ + if(pa->alive==PARS_UNBORN){ + pa->alive=PARS_ALIVE; + pa->time=re->time; + pa->dietime=pa->time+pa->lifetime; + birth=1; + } + if(birth || part->flag&PART_REACT_MULTIPLE){ + float vec[3]; + VECSUB(vec,pa->state.co, re->state.co); + if(birth==0) + VecMulf(vec,(float)pow(1.0f-dist/re->size,part->reactshape)); + VECADDFAC(pa->state.vel,pa->state.vel,vec,part->reactfac); + VECADDFAC(pa->state.vel,pa->state.vel,re->state.vel,part->partfac); + } + if(birth) + VecMulf(pa->state.vel,(float)pow(1.0f-dist/re->size,part->reactshape)); + } + } + } +} +void psys_get_reactor_target(Object *ob, ParticleSystem *psys, Object **target_ob, ParticleSystem **target_psys) +{ + Object *tob; + + tob=psys->target_ob; + if(tob==0) + tob=ob; + + *target_psys=BLI_findlink(&tob->particlesystem,psys->target_psys-1); + if(*target_psys) + *target_ob=tob; + else + *target_ob=0; +} +/************************************************/ +/* Point Cache */ +/************************************************/ +void clear_particles_from_cache(Object *ob, ParticleSystem *psys, int cfra) +{ + ParticleSystemModifierData *psmd = psys_get_modifier(ob,psys); + int stack_index = modifiers_indexInObject(ob,(ModifierData*)psmd); + + BKE_ptcache_id_clear((ID *)ob, PTCACHE_CLEAR_ALL, cfra, stack_index); +} +static void write_particles_to_cache(Object *ob, ParticleSystem *psys, int cfra) +{ + FILE *fp = NULL; + ParticleSystemModifierData *psmd = psys_get_modifier(ob,psys); + ParticleData *pa; + int stack_index = modifiers_indexInObject(ob,(ModifierData*)psmd); + int i, totpart = psys->totpart; + + if(totpart == 0) return; + + fp = BKE_ptcache_id_fopen((ID *)ob, 'w', cfra, stack_index); + if(!fp) return; + + for(i=0, pa=psys->particles; i<totpart; i++, pa++) + fwrite(&pa->state, sizeof(ParticleKey), 1, fp); + + fclose(fp); +} +static int get_particles_from_cache(Object *ob, ParticleSystem *psys, int cfra) +{ + FILE *fp = NULL; + ParticleSystemModifierData *psmd = psys_get_modifier(ob,psys); + ParticleData *pa; + int stack_index = modifiers_indexInObject(ob,(ModifierData*)psmd); + int i, totpart = psys->totpart, ret = 1; + + if(totpart == 0) return 0; + + fp = BKE_ptcache_id_fopen((ID *)ob, 'r', cfra, stack_index); + if(!fp) + ret = 0; + else { + for(i=0, pa=psys->particles; i<totpart; i++, pa++) + if((fread(&pa->state, sizeof(ParticleKey), 1, fp)) != 1) { + ret = 0; + break; + } + + fclose(fp); + } + + return ret; +} +/************************************************/ +/* Effectors */ +/************************************************/ +static float effector_falloff(PartDeflect *pd, float *eff_velocity, float *vec_to_part) +{ + float eff_dir[3], temp[3]; + float falloff=1.0, fac, r_fac; + + VecCopyf(eff_dir,eff_velocity); + Normalize(eff_dir); + + if(pd->flag & PFIELD_POSZ && Inpf(eff_dir,vec_to_part)<0.0f) + falloff=0.0f; + else switch(pd->falloff){ + case PFIELD_FALL_SPHERE: + fac=VecLength(vec_to_part); + if(pd->flag&PFIELD_USEMAX && fac>pd->maxdist){ + falloff=0.0f; + break; + } + + if(pd->flag & PFIELD_USEMIN){ + if(fac>pd->mindist) + fac+=1.0f-pd->mindist; + else + fac=1.0f; + } + else if(fac<0.001) + fac=0.001f; + + falloff=1.0f/(float)pow((double)fac,(double)pd->f_power); + break; + + case PFIELD_FALL_TUBE: + fac=Inpf(vec_to_part,eff_dir); + if(pd->flag&PFIELD_USEMAX && ABS(fac)>pd->maxdist){ + falloff=0.0f; + break; + } + + VECADDFAC(temp,vec_to_part,eff_dir,-fac); + r_fac=VecLength(temp); + if(pd->flag&PFIELD_USEMAXR && r_fac>pd->maxrad){ + falloff=0.0f; + break; + } + + fac=ABS(fac); + + + if(pd->flag & PFIELD_USEMIN){ + if(fac>pd->mindist) + fac+=1.0f-pd->mindist; + else + fac=1.0f; + } + else if(fac<0.001) + fac=0.001f; + + if(pd->flag & PFIELD_USEMINR){ + if(r_fac>pd->minrad) + r_fac+=1.0f-pd->minrad; + else + r_fac=1.0f; + } + else if(r_fac<0.001) + r_fac=0.001f; + + falloff=1.0f/((float)pow((double)fac,(double)pd->f_power)*(float)pow((double)r_fac,(double)pd->f_power_r)); + + break; + case PFIELD_FALL_CONE: + fac=Inpf(vec_to_part,eff_dir); + if(pd->flag&PFIELD_USEMAX && ABS(fac)>pd->maxdist){ + falloff=0.0f; + break; + } + r_fac=saacos(fac/VecLength(vec_to_part))*180.0f/(float)M_PI; + if(pd->flag&PFIELD_USEMAXR && r_fac>pd->maxrad){ + falloff=0.0f; + break; + } + + if(pd->flag & PFIELD_USEMIN){ + if(fac>pd->mindist) + fac+=1.0f-pd->mindist; + else + fac=1.0f; + } + else if(fac<0.001) + fac=0.001f; + + if(pd->flag & PFIELD_USEMINR){ + if(r_fac>pd->minrad) + r_fac+=1.0f-pd->minrad; + else + r_fac=1.0f; + } + else if(r_fac<0.001) + r_fac=0.001f; + + falloff=1.0f/((float)pow((double)fac,(double)pd->f_power)*(float)pow((double)r_fac,(double)pd->f_power_r)); + + break; +// case PFIELD_FALL_INSIDE: + //for(i=0; i<totface; i++,mface++){ + // VECCOPY(v1,mvert[mface->v1].co); + // VECCOPY(v2,mvert[mface->v2].co); + // VECCOPY(v3,mvert[mface->v3].co); + + // if(AxialLineIntersectsTriangle(a,co1, co2, v2, v3, v1, &lambda)){ + // if(from==PART_FROM_FACE) + // (pa+(int)(lambda*size[a])*a0mul)->flag &= ~PARS_UNEXIST; + // else /* store number of intersections */ + // (pa+(int)(lambda*size[a])*a0mul)->loop++; + // } + // + // if(mface->v4){ + // VECCOPY(v4,mvert[mface->v4].co); + + // if(AxialLineIntersectsTriangle(a,co1, co2, v4, v1, v3, &lambda)){ + // if(from==PART_FROM_FACE) + // (pa+(int)(lambda*size[a])*a0mul)->flag &= ~PARS_UNEXIST; + // else + // (pa+(int)(lambda*size[a])*a0mul)->loop++; + // } + // } + //} + +// break; + } + + return falloff; +} +static void do_physical_effector(short type, float force_val, float distance, float falloff, float size, float damp, + float *eff_velocity, float *vec_to_part, float *velocity, float *field, int planar) +{ + float mag_vec[3]={0,0,0}; + float temp[3], temp2[3]; + float eff_vel[3]; + + VecCopyf(eff_vel,eff_velocity); + Normalize(eff_vel); + + switch(type){ + case PFIELD_WIND: + VECCOPY(mag_vec,eff_vel); + + VecMulf(mag_vec,force_val*falloff); + VecAddf(field,field,mag_vec); + break; + + case PFIELD_FORCE: + if(planar) + Projf(mag_vec,vec_to_part,eff_vel); + else + VecCopyf(mag_vec,vec_to_part); + + VecMulf(mag_vec,force_val*falloff); + VecAddf(field,field,mag_vec); + break; + + case PFIELD_VORTEX: + Crossf(mag_vec,eff_vel,vec_to_part); + Normalize(mag_vec); + + VecMulf(mag_vec,force_val*distance*falloff); + VecAddf(field,field,mag_vec); + + break; + case PFIELD_MAGNET: + if(planar) + VecCopyf(temp,eff_vel); + else + /* magnetic field of a moving charge */ + Crossf(temp,eff_vel,vec_to_part); + + Crossf(temp2,velocity,temp); + VecAddf(mag_vec,mag_vec,temp2); + + VecMulf(mag_vec,force_val*falloff); + VecAddf(field,field,mag_vec); + break; + case PFIELD_HARMONIC: + if(planar) + Projf(mag_vec,vec_to_part,eff_vel); + else + VecCopyf(mag_vec,vec_to_part); + + VecMulf(mag_vec,force_val*falloff); + VecSubf(field,field,mag_vec); + + VecCopyf(mag_vec,velocity); + /* 1.9 is an experimental value to get critical damping at damp=1.0 */ + VecMulf(mag_vec,damp*1.9f*(float)sqrt(force_val)); + VecSubf(field,field,mag_vec); + break; + case PFIELD_NUCLEAR: + /*pow here is root of cosine expression below*/ + //rad=(float)pow(2.0,-1.0/power)*distance/size; + //VECCOPY(mag_vec,vec_to_part); + //Normalize(mag_vec); + //VecMulf(mag_vec,(float)cos(3.0*M_PI/2.0*(1.0-1.0/(pow(rad,power)+1.0)))/(rad+0.2f)); + //VECADDFAC(field,field,mag_vec,force_val); + break; + } +} +static void do_texture_effector(Tex *tex, short mode, short is_2d, float nabla, short object, float *pa_co, float obmat[4][4], float force_val, float falloff, float *field) +{ + TexResult result[4]; + float tex_co[3], strength, mag_vec[3]; + int i; + + if(tex==0) return; + + for(i=0; i<4; i++) + result[i].nor=0; + + strength= force_val*falloff;///(float)pow((double)distance,(double)power); + + VECCOPY(tex_co,pa_co); + + if(is_2d){ + float fac=-Inpf(tex_co,obmat[2]); + VECADDFAC(tex_co,tex_co,obmat[2],fac); + } + + if(object){ + VecSubf(tex_co,tex_co,obmat[3]); + Mat4Mul3Vecfl(obmat,tex_co); + } + + multitex_ext(tex, tex_co, NULL,NULL, 1, result); + + if(mode==PFIELD_TEX_RGB){ + mag_vec[0]= (0.5f-result->tr)*strength; + mag_vec[1]= (0.5f-result->tg)*strength; + mag_vec[2]= (0.5f-result->tb)*strength; + } + else{ + strength/=nabla; + + tex_co[0]+= nabla; + multitex_ext(tex, tex_co, NULL,NULL, 1, result+1); + + tex_co[0]-= nabla; + tex_co[1]+= nabla; + multitex_ext(tex, tex_co, NULL,NULL, 1, result+2); + + tex_co[1]-= nabla; + tex_co[2]+= nabla; + multitex_ext(tex, tex_co, NULL,NULL, 1, result+3); + + if(mode==PFIELD_TEX_GRAD){ + mag_vec[0]= (result[0].tin-result[1].tin)*strength; + mag_vec[1]= (result[0].tin-result[2].tin)*strength; + mag_vec[2]= (result[0].tin-result[3].tin)*strength; + } + else{ /*PFIELD_TEX_CURL*/ + float dbdy,dgdz,drdz,dbdx,dgdx,drdy; + + dbdy= result[2].tb-result[0].tb; + dgdz= result[3].tg-result[0].tg; + drdz= result[3].tr-result[0].tr; + dbdx= result[1].tb-result[0].tb; + dgdx= result[1].tg-result[0].tg; + drdy= result[2].tr-result[0].tr; + + mag_vec[0]=(dbdy-dgdz)*strength; + mag_vec[1]=(drdz-dbdx)*strength; + mag_vec[2]=(dgdx-drdy)*strength; + } + } + + if(is_2d){ + float fac=-Inpf(mag_vec,obmat[2]); + VECADDFAC(mag_vec,mag_vec,obmat[2],fac); + } + + VecAddf(field,field,mag_vec); +} +static void add_to_effectors(ListBase *lb, Object *ob, Object *obsrc, ParticleSystem *psys) +{ + ParticleEffectorCache *ec; + PartDeflect *pd= ob->pd; + short type=0,i; + + if(pd && ob != obsrc){ + if(pd->forcefield == PFIELD_GUIDE) { + if(ob->type==OB_CURVE) { + Curve *cu= ob->data; + if(cu->flag & CU_PATH) { + if(cu->path==NULL || cu->path->data==NULL) + makeDispListCurveTypes(ob, 0); + if(cu->path && cu->path->data) { + type |= PSYS_EC_EFFECTOR; + } + } + } + } + else if(pd->forcefield) + type |= PSYS_EC_EFFECTOR; + } + + if(pd && pd->deflect) + type |= PSYS_EC_DEFLECT; + + if(type){ + ec= MEM_callocN(sizeof(ParticleEffectorCache), "effector cache"); + ec->ob= ob; + ec->type=type; + ec->distances=0; + ec->locations=0; + BLI_addtail(lb, ec); + } + + type=0; + + /* add particles as different effectors */ + if(ob->particlesystem.first){ + ParticleSystem *epsys=ob->particlesystem.first; + ParticleSettings *epart=0; + Object *tob; + + for(i=0; epsys; epsys=epsys->next,i++){ + type=0; + if(epsys!=psys){ + epart=epsys->part; + + if(epsys->part->pd && epsys->part->pd->forcefield) + type=PSYS_EC_PARTICLE; + + if(epart->type==PART_REACTOR) { + tob=epsys->target_ob; + if(tob==0) + tob=ob; + if(BLI_findlink(&tob->particlesystem,epsys->target_psys-1)==psys) + type|=PSYS_EC_REACTOR; + } + + if(type){ + ec= MEM_callocN(sizeof(ParticleEffectorCache), "effector cache"); + ec->ob= ob; + ec->type=type; + ec->psys_nbr=i; + BLI_addtail(lb, ec); + } + } + } + + } +} +void psys_init_effectors(Object *obsrc, Group *group, ParticleSystem *psys) +{ + ListBase *listb=&psys->effectors; + Base *base; + unsigned int layer= obsrc->lay; + + listb->first=listb->last=0; + + if(group) { + GroupObject *go; + + for(go= group->gobject.first; go; go= go->next) { + if( (go->ob->lay & layer) && (go->ob->pd || go->ob->particlesystem.first)) { + add_to_effectors(listb, go->ob, obsrc, psys); + } + } + } + else { + for(base = G.scene->base.first; base; base= base->next) { + if( (base->lay & layer) && (base->object->pd || base->object->particlesystem.first)) { + add_to_effectors(listb, base->object, obsrc, psys); + } + } + } +} + +void psys_end_effectors(ParticleSystem *psys) +{ + ListBase *lb=&psys->effectors; + if(lb->first) { + ParticleEffectorCache *ec; + for(ec= lb->first; ec; ec= ec->next){ + if(ec->distances) + MEM_freeN(ec->distances); + + if(ec->locations) + MEM_freeN(ec->locations); + + if(ec->face_minmax) + MEM_freeN(ec->face_minmax); + + if(ec->vert_cos) + MEM_freeN(ec->vert_cos); + + if(ec->tree) + BLI_kdtree_free(ec->tree); + } + + BLI_freelistN(lb); + } +} + +static void precalc_effectors(Object *ob, ParticleSystem *psys, ParticleSystemModifierData *psmd) +{ + ListBase *lb=&psys->effectors; + ParticleEffectorCache *ec; + ParticleSettings *part=psys->part; + ParticleData *pa; + float vec2[3],loc[3],*co=0; + int p,totpart,totvert; + + for(ec= lb->first; ec; ec= ec->next) { + PartDeflect *pd= ec->ob->pd; + + if(ec->type==PSYS_EC_EFFECTOR && pd->forcefield==PFIELD_GUIDE && ec->ob->type==OB_CURVE + && part->phystype!=PART_PHYS_BOIDS) { + float vec[4]; + + where_on_path(ec->ob, 0.0, vec, vec2); + + Mat4MulVecfl(ec->ob->obmat,vec); + Mat4Mul3Vecfl(ec->ob->obmat,vec2); + + QUATCOPY(ec->firstloc,vec); + VECCOPY(ec->firstdir,vec2); + + totpart=psys->totpart; + + if(totpart){ + ec->distances=MEM_callocN(totpart*sizeof(float),"particle distances"); + ec->locations=MEM_callocN(totpart*3*sizeof(float),"particle locations"); + + for(p=0,pa=psys->particles; p<totpart; p++, pa++){ + psys_particle_on_emitter(ob,psmd,part->from,pa->num,pa->num_dmcache,pa->fuv,pa->foffset,loc,0,0,0); + Mat4MulVecfl(ob->obmat,loc); + ec->distances[p]=VecLenf(loc,vec); + VECSUB(loc,loc,vec); + VECCOPY(ec->locations+3*p,loc); + } + } + } + else if(ec->type==PSYS_EC_DEFLECT){ + DerivedMesh *dm; + MFace *mface=0; + MVert *mvert=0; + int i, totface; + float v1[3],v2[3],v3[3],v4[4], *min, *max; + + if(ob==ec->ob) + dm=psmd->dm; + else{ + psys_disable_all(ec->ob); + + dm=mesh_get_derived_final(ec->ob,0); + + psys_enable_all(ec->ob); + } + + if(dm){ + totvert=dm->getNumVerts(dm); + totface=dm->getNumFaces(dm); + mface=dm->getFaceDataArray(dm,CD_MFACE); + mvert=dm->getVertDataArray(dm,CD_MVERT); + + /* Decide which is faster to calculate by the amount of*/ + /* matrice multiplications needed to convert spaces. */ + /* With size deflect we have to convert allways because */ + /* the object can be scaled nonuniformly (sphere->ellipsoid). */ + if(totvert<2*psys->totpart || part->flag & PART_SIZE_DEFL){ + co=ec->vert_cos=MEM_callocN(sizeof(float)*3*totvert,"Particle deflection vert cos"); + /* convert vert coordinates to global (particle) coordinates */ + for(i=0; i<totvert; i++, co+=3){ + VECCOPY(co,mvert[i].co); + Mat4MulVecfl(ec->ob->obmat,co); + } + co=ec->vert_cos; + } + else + ec->vert_cos=0; + + INIT_MINMAX(ec->ob_minmax,ec->ob_minmax+3); + + min=ec->face_minmax=MEM_callocN(sizeof(float)*6*totface,"Particle deflection face minmax"); + max=min+3; + + for(i=0; i<totface; i++,mface++,min+=6,max+=6){ + if(co){ + VECCOPY(v1,co+3*mface->v1); + VECCOPY(v2,co+3*mface->v2); + VECCOPY(v3,co+3*mface->v3); + } + else{ + VECCOPY(v1,mvert[mface->v1].co); + VECCOPY(v2,mvert[mface->v2].co); + VECCOPY(v3,mvert[mface->v3].co); + } + INIT_MINMAX(min,max); + DO_MINMAX(v1,min,max); + DO_MINMAX(v2,min,max); + DO_MINMAX(v3,min,max); + + if(mface->v4){ + if(co){ + VECCOPY(v4,co+3*mface->v4); + } + else{ + VECCOPY(v4,mvert[mface->v4].co); + } + DO_MINMAX(v4,min,max); + } + + DO_MINMAX(min,ec->ob_minmax,ec->ob_minmax+3); + DO_MINMAX(max,ec->ob_minmax,ec->ob_minmax+3); + } + } + else + ec->face_minmax=0; + } + else if(ec->type==PSYS_EC_PARTICLE){ + if(psys->part->phystype==PART_PHYS_BOIDS){ + Object *eob = ec->ob; + ParticleSystem *epsys; + ParticleSettings *epart; + ParticleData *epa; + ParticleKey state; + PartDeflect *pd; + int totepart, p; + epsys= BLI_findlink(&eob->particlesystem,ec->psys_nbr); + epart= epsys->part; + pd= epart->pd; + totepart= epsys->totpart; + if(pd->forcefield==PFIELD_FORCE && totepart){ + KDTree *tree; + + tree=BLI_kdtree_new(totepart); + ec->tree=tree; + + for(p=0, epa=epsys->particles; p<totepart; p++,epa++) + if(epa->alive==PARS_ALIVE && psys_get_particle_state(eob,epsys,p,&state,0)) + BLI_kdtree_insert(tree, p, state.co, NULL); + + BLI_kdtree_balance(tree); + } + } + } + } +} + + +/* calculate forces that all effectors apply to a particle*/ +static void do_effectors(int pa_no, ParticleData *pa, ParticleKey *state, Object *ob, ParticleSystem *psys, float *force_field, float *vel,float framestep, float cfra) +{ + Object *eob; + ParticleSystem *epsys; + ParticleSettings *epart; + ParticleData *epa; + ParticleKey estate; + PartDeflect *pd; + ListBase *lb=&psys->effectors; + ParticleEffectorCache *ec; + float distance, vec_to_part[3]; + float falloff; + int p; + + /* check all effector objects for interaction */ + if(lb->first){ + for(ec = lb->first; ec; ec= ec->next){ + eob= ec->ob; + if(ec->type & PSYS_EC_EFFECTOR){ + pd=eob->pd; + if(psys->part->type!=PART_HAIR && psys->part->integrator) + where_is_object_time(eob,cfra); + /* Get IPO force strength and fall off values here */ + //if (has_ipo_code(eob->ipo, OB_PD_FSTR)) + // force_val = IPO_GetFloatValue(eob->ipo, OB_PD_FSTR, cfra); + //else + // force_val = pd->f_strength; + + //if (has_ipo_code(eob->ipo, OB_PD_FFALL)) + // ffall_val = IPO_GetFloatValue(eob->ipo, OB_PD_FFALL, cfra); + //else + // ffall_val = pd->f_power; + + //if (has_ipo_code(eob->ipo, OB_PD_FMAXD)) + // maxdist = IPO_GetFloatValue(eob->ipo, OB_PD_FMAXD, cfra); + //else + // maxdist = pd->maxdist; + + /* use center of object for distance calculus */ + //obloc= eob->obmat[3]; + VecSubf(vec_to_part, state->co, eob->obmat[3]); + distance = VecLength(vec_to_part); + + falloff=effector_falloff(pd,eob->obmat[2],vec_to_part); + + 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, state->co, eob->obmat, + pd->f_strength, falloff, force_field); + else + do_physical_effector(pd->forcefield,pd->f_strength,distance, + falloff,pd->f_dist,pd->f_damp,eob->obmat[2],vec_to_part, + pa->state.vel,force_field,pd->flag&PFIELD_PLANAR); + } + if(ec->type & PSYS_EC_PARTICLE){ + int totepart; + epsys= BLI_findlink(&eob->particlesystem,ec->psys_nbr); + epart= epsys->part; + pd= epart->pd; + totepart= epsys->totpart; + + if(pd->forcefield==PFIELD_HARMONIC){ + /* every particle is mapped to only one harmonic effector particle */ + p= pa_no%epsys->totpart; + totepart= p+1; + } + else{ + p=0; + } + + epsys->lattice=psys_get_lattice(ob,psys); + + for(; p<totepart; p++){ + epa = epsys->particles + p; + estate.time=-1.0; + if(psys_get_particle_state(eob,epsys,p,&estate,0)){ + VECSUB(vec_to_part, state->co, estate.co); + distance = VecLength(vec_to_part); + + //if(pd->forcefield==PFIELD_HARMONIC){ + // //if(cfra < epa->time + radius){ /* radius is fade-in in ui */ + // // eforce*=(cfra-epa->time)/radius; + // //} + //} + //else{ + // /* Limit minimum distance to effector particle so that */ + // /* the force is not too big */ + // if (distance < 0.001) distance = 0.001f; + //} + + falloff=effector_falloff(pd,estate.vel,vec_to_part); + + if(falloff<=0.0f) + ; /* don't do anything */ + else + do_physical_effector(pd->forcefield,pd->f_strength,distance, + falloff,epart->size,pd->f_damp,estate.vel,vec_to_part, + state->vel,force_field,0); + } + else if(pd->forcefield==PFIELD_HARMONIC && cfra-framestep <= epa->dietime && cfra>epa->dietime){ + /* first step after key release */ + psys_get_particle_state(eob,epsys,p,&estate,1); + VECADD(vel,vel,estate.vel); + /* TODO: add rotation handling here too */ + } + } + + if(epsys->lattice){ + end_latt_deform(); + epsys->lattice=0; + } + } + } + } +} + +/************************************************/ +/* Newtonian physics */ +/************************************************/ +/* gathers all forces that effect particles and calculates a new state for the particle */ +static void apply_particle_forces(int pa_no, ParticleData *pa, Object *ob, ParticleSystem *psys, ParticleSettings *part, float timestep, float dfra, float cfra, ParticleKey *state) +{ + ParticleKey states[5], tkey; + float force[3],tvel[3],dx[4][3],dv[4][3]; + float dtime=dfra*timestep, time, pa_mass=part->mass, fac, fra=psys->cfra; + int i, steps=1; + + /* maintain angular velocity */ + VECCOPY(state->ave,pa->state.ave); + + if(part->flag & PART_SIZEMASS) + pa_mass*=pa->size; + + switch(part->integrator){ + case PART_INT_EULER: + steps=1; + break; + case PART_INT_MIDPOINT: + steps=2; + break; + case PART_INT_RK4: + steps=4; + break; + } + + copy_particle_key(states,&pa->state,1); + + for(i=0; i<steps; i++){ + force[0]=force[1]=force[2]=0.0; + tvel[0]=tvel[1]=tvel[2]=0.0; + /* add effectors */ + do_effectors(pa_no,pa,states+i,ob,psys,force,tvel,dfra,fra); + + /* calculate air-particle interaction */ + if(part->dragfac!=0.0f){ + fac=-part->dragfac*pa->size*pa->size*VecLength(states[i].vel); + VECADDFAC(force,force,states[i].vel,fac); + } + + /* brownian force */ + if(part->brownfac!=0.0){ + force[0]+=(BLI_frand()-0.5f)*part->brownfac; + force[1]+=(BLI_frand()-0.5f)*part->brownfac; + force[2]+=(BLI_frand()-0.5f)*part->brownfac; + } + + /* force to acceleration*/ + VecMulf(force,1.0f/pa_mass); + + /* add global acceleration (gravitation) */ + VECADD(force,force,part->acc); + + //VecMulf(force,dtime); + + /* calculate next state */ + VECADD(states[i].vel,states[i].vel,tvel); + + //VecMulf(force,0.5f*dt); + switch(part->integrator){ + case PART_INT_EULER: + VECADDFAC(state->co,states->co,states->vel,dtime); + VECADDFAC(state->vel,states->vel,force,dtime); + break; + case PART_INT_MIDPOINT: + if(i==0){ + VECADDFAC(states[1].co,states->co,states->vel,dtime*0.5f); + VECADDFAC(states[1].vel,states->vel,force,dtime*0.5f); + fra=psys->cfra+0.5f*dfra; + } + else{ + VECADDFAC(state->co,states->co,states[1].vel,dtime); + VECADDFAC(state->vel,states->vel,force,dtime); + } + break; + case PART_INT_RK4: + switch(i){ + case 0: + VECCOPY(dx[0],states->vel); + VecMulf(dx[0],dtime); + VECCOPY(dv[0],force); + VecMulf(dv[0],dtime); + + VECADDFAC(states[1].co,states->co,dx[0],0.5f); + VECADDFAC(states[1].vel,states->vel,dv[0],0.5f); + fra=psys->cfra+0.5f*dfra; + break; + case 1: + VECADDFAC(dx[1],states->vel,dv[0],0.5f); + VecMulf(dx[1],dtime); + VECCOPY(dv[1],force); + VecMulf(dv[1],dtime); + + VECADDFAC(states[2].co,states->co,dx[1],0.5f); + VECADDFAC(states[2].vel,states->vel,dv[1],0.5f); + break; + case 2: + VECADDFAC(dx[2],states->vel,dv[1],0.5f); + VecMulf(dx[2],dtime); + VECCOPY(dv[2],force); + VecMulf(dv[2],dtime); + + VECADD(states[3].co,states->co,dx[2]); + VECADD(states[3].vel,states->vel,dv[2]); + fra=cfra; + break; + case 3: + VECADD(dx[3],states->vel,dv[2]); + VecMulf(dx[3],dtime); + VECCOPY(dv[3],force); + VecMulf(dv[3],dtime); + + VECADDFAC(state->co,states->co,dx[0],1.0f/6.0f); + VECADDFAC(state->co,state->co,dx[1],1.0f/3.0f); + VECADDFAC(state->co,state->co,dx[2],1.0f/3.0f); + VECADDFAC(state->co,state->co,dx[3],1.0f/6.0f); + + VECADDFAC(state->vel,states->vel,dv[0],1.0f/6.0f); + VECADDFAC(state->vel,state->vel,dv[1],1.0f/3.0f); + VECADDFAC(state->vel,state->vel,dv[2],1.0f/3.0f); + VECADDFAC(state->vel,state->vel,dv[3],1.0f/6.0f); + } + break; + } + //VECADD(states[i+1].co,states[i+1].co,force); + } + + /* damp affects final velocity */ + if(part->dampfac!=0.0) + VecMulf(state->vel,1.0f-part->dampfac); + + /* finally we do guides */ + time=(cfra-pa->time)/pa->lifetime; + CLAMP(time,0.0,1.0); + + VECCOPY(tkey.co,state->co); + VECCOPY(tkey.vel,state->vel); + tkey.time=state->time; + if(do_guide(&tkey,pa_no,time,&psys->effectors)){ + VECCOPY(state->co,tkey.co); + /* guides don't produce valid velocity */ + VECSUB(state->vel,tkey.co,pa->state.co); + VecMulf(state->vel,1.0f/dtime); + state->time=tkey.time; + } +} +static void rotate_particle(ParticleSettings *part, ParticleData *pa, float dfra, float timestep, ParticleKey *state) +{ + float rotfac, rot1[4], rot2[4]={1.0,0.0,0.0,0.0}, dtime=dfra*timestep; + + if((part->flag & PART_ROT_DYN)==0){ + if(ELEM(part->avemode,PART_AVE_SPIN,PART_AVE_VEL)){ + float angle; + float len1 = VecLength(pa->state.vel); + float len2 = VecLength(state->vel); + + if(len1==0.0f || len2==0.0f) + state->ave[0]=state->ave[1]=state->ave[2]=0.0f; + else{ + Crossf(state->ave,pa->state.vel,state->vel); + Normalize(state->ave); + angle=Inpf(pa->state.vel,state->vel)/(len1*len2); + VecMulf(state->ave,saacos(angle)/dtime); + } + } + + if(part->avemode == PART_AVE_SPIN) + VecRotToQuat(state->vel,dtime*part->avefac,rot2); + } + + rotfac=VecLength(state->ave); + if(rotfac==0.0){ /* QuatOne (in VecRotToQuat) doesn't give unit quat [1,0,0,0]?? */ + rot1[0]=1.0; + rot1[1]=rot1[2]=rot1[3]=0; + } + else{ + VecRotToQuat(state->ave,rotfac*dtime,rot1); + } + QuatMul(state->rot,rot1,pa->state.rot); + QuatMul(state->rot,rot2,state->rot); + + /* keep rotation quat in good health */ + NormalQuat(state->rot); +} + +/* convert from triangle barycentric weights to quad mean value weights */ +static void intersect_dm_quad_weights(float *v1, float *v2, float *v3, float *v4, float *w) +{ + float co[3], vert[4][3]; + + VECCOPY(vert[0], v1); + VECCOPY(vert[1], v2); + VECCOPY(vert[2], v3); + VECCOPY(vert[3], v4); + + co[0]= v1[0]*w[0] + v2[0]*w[1] + v3[0]*w[2] + v4[0]*w[3]; + co[1]= v1[1]*w[0] + v2[1]*w[1] + v3[1]*w[2] + v4[1]*w[3]; + co[2]= v1[2]*w[0] + v2[2]*w[1] + v3[2]*w[2] + v4[2]*w[3]; + + MeanValueWeights(vert, 4, co, w); +} + +/* check intersection with a derivedmesh */ +int psys_intersect_dm(Object *ob, DerivedMesh *dm, float *vert_cos, float *co1, float* co2, float *min_d, int *min_face, float *min_w, + float *face_minmax, float *pa_minmax, float radius, float *ipoint) +{ + MFace *mface=0; + MVert *mvert=0; + int i, totface, intersect=0; + float cur_d, cur_uv[2], v1[3], v2[3], v3[3], v4[3], min[3], max[3], p_min[3],p_max[3]; + float cur_ipoint[3]; + + if(dm==0){ + psys_disable_all(ob); + + dm=mesh_get_derived_final(ob,0); + if(dm==0) + mesh_get_derived_deform(ob,0); + + psys_enable_all(ob); + + if(dm==0) + return 0; + } + + + + if(pa_minmax==0){ + INIT_MINMAX(p_min,p_max); + DO_MINMAX(co1,p_min,p_max); + DO_MINMAX(co2,p_min,p_max); + } + else{ + VECCOPY(p_min,pa_minmax); + VECCOPY(p_max,pa_minmax+3); + } + + totface=dm->getNumFaces(dm); + mface=dm->getFaceDataArray(dm,CD_MFACE); + mvert=dm->getVertDataArray(dm,CD_MVERT); + + /* lets intersect the faces */ + for(i=0; i<totface; i++,mface++){ + if(vert_cos){ + VECCOPY(v1,vert_cos+3*mface->v1); + VECCOPY(v2,vert_cos+3*mface->v2); + VECCOPY(v3,vert_cos+3*mface->v3); + if(mface->v4) + VECCOPY(v4,vert_cos+3*mface->v4) + } + else{ + VECCOPY(v1,mvert[mface->v1].co); + VECCOPY(v2,mvert[mface->v2].co); + VECCOPY(v3,mvert[mface->v3].co); + if(mface->v4) + VECCOPY(v4,mvert[mface->v4].co) + } + + if(face_minmax==0){ + INIT_MINMAX(min,max); + DO_MINMAX(v1,min,max); + DO_MINMAX(v2,min,max); + DO_MINMAX(v3,min,max); + if(mface->v4) + DO_MINMAX(v4,min,max) + if(AabbIntersectAabb(min,max,p_min,p_max)==0) + continue; + } + else{ + VECCOPY(min, face_minmax+6*i); + VECCOPY(max, face_minmax+6*i+3); + if(AabbIntersectAabb(min,max,p_min,p_max)==0) + continue; + } + + if(radius>0.0f){ + if(SweepingSphereIntersectsTriangleUV(co1, co2, radius, v2, v3, v1, &cur_d, cur_ipoint)){ + if(cur_d<*min_d){ + *min_d=cur_d; + VECCOPY(ipoint,cur_ipoint); + *min_face=i; + intersect=1; + } + } + if(mface->v4){ + if(SweepingSphereIntersectsTriangleUV(co1, co2, radius, v4, v1, v3, &cur_d, cur_ipoint)){ + if(cur_d<*min_d){ + *min_d=cur_d; + VECCOPY(ipoint,cur_ipoint); + *min_face=i; + intersect=1; + } + } + } + } + else{ + if(LineIntersectsTriangle(co1, co2, v1, v2, v3, &cur_d, cur_uv)){ + if(cur_d<*min_d){ + *min_d=cur_d; + min_w[0]= 1.0 - cur_uv[0] - cur_uv[1]; + min_w[1]= cur_uv[0]; + min_w[2]= cur_uv[1]; + min_w[3]= 0.0f; + if(mface->v4) + intersect_dm_quad_weights(v1, v2, v3, v4, min_w); + *min_face=i; + intersect=1; + } + } + if(mface->v4){ + if(LineIntersectsTriangle(co1, co2, v1, v3, v4, &cur_d, cur_uv)){ + if(cur_d<*min_d){ + *min_d=cur_d; + min_w[0]= 1.0 - cur_uv[0] - cur_uv[1]; + min_w[1]= 0.0f; + min_w[2]= cur_uv[0]; + min_w[3]= cur_uv[1]; + intersect_dm_quad_weights(v1, v2, v3, v4, min_w); + *min_face=i; + intersect=1; + } + } + } + } + } + return intersect; +} +/* particle - mesh collision code */ +/* in addition to basic point to surface collisions handles friction & damping,*/ +/* angular momentum <-> linear momentum and swept sphere - mesh collisions */ +/* 1. check for all possible deflectors for closest intersection on particle path */ +/* 2. if deflection was found kill the particle or calculate new coordinates */ +static void deflect_particle(Object *pob, ParticleSystemModifierData *psmd, ParticleSystem *psys, ParticleSettings *part, ParticleData *pa, int p, float dfra, float cfra, ParticleKey *state, int *pa_die){ + Object *ob, *min_ob; + MFace *mface; + MVert *mvert; + DerivedMesh *dm; + ListBase *lb=&psys->effectors; + ParticleEffectorCache *ec; + ParticleKey cstate; + float imat[4][4]; + float co1[3],co2[3],def_loc[3],def_nor[3],unit_nor[3],def_tan[3],dvec[3],def_vel[3],dave[3],dvel[3]; + float pa_minmax[6]; + float min_w[4], zerovec[3]={0.0,0.0,0.0}, ipoint[3]; + float min_d,dotprod,damp,frict,o_len,d_len,radius=-1.0f; + int min_face=0, intersect=1, through=0; + short deflections=0, global=0; + + VECCOPY(def_loc,pa->state.co); + VECCOPY(def_vel,pa->state.vel); + + /* 10 iterations to catch multiple deflections */ + if(lb->first) while(deflections<10){ + intersect=0; + global=0; + min_d=20000.0; + min_ob=NULL; + /* 1. */ + for(ec=lb->first; ec; ec=ec->next){ + if(ec->type & PSYS_EC_DEFLECT){ + ob= ec->ob; + + if(part->type!=PART_HAIR) + where_is_object_time(ob,cfra); + + if(ob==pob){ + dm=psmd->dm; + /* particles should not collide with emitter at birth */ + if(pa->time < cfra && pa->time >= psys->cfra) + continue; + } + else + dm=0; + + VECCOPY(co1,def_loc); + VECCOPY(co2,state->co); + + if(ec->vert_cos==0){ + /* convert particle coordinates to object coordinates */ + Mat4Invert(imat,ob->obmat); + + Mat4MulVecfl(imat,co1); + Mat4MulVecfl(imat,co2); + } + + INIT_MINMAX(pa_minmax,pa_minmax+3); + DO_MINMAX(co1,pa_minmax,pa_minmax+3); + DO_MINMAX(co2,pa_minmax,pa_minmax+3); + if(part->flag&PART_SIZE_DEFL){ + pa_minmax[0]-=pa->size; + pa_minmax[1]-=pa->size; + pa_minmax[2]-=pa->size; + pa_minmax[3]+=pa->size; + pa_minmax[4]+=pa->size; + pa_minmax[5]+=pa->size; + + radius=pa->size; + } + + if(ec->face_minmax==0 || AabbIntersectAabb(pa_minmax,pa_minmax+3,ec->ob_minmax,ec->ob_minmax+3)) + if(psys_intersect_dm(ob,dm,ec->vert_cos,co1,co2,&min_d,&min_face,min_w, + ec->face_minmax,pa_minmax,radius,ipoint)){ + min_ob=ob; + if(ec->vert_cos) + global=1; + else + global=0; + } + } + } + + /* 2. */ + if(min_ob){ + BLI_srandom((int)cfra+p); + ob=min_ob; + + if(ob==pob){ + dm=psmd->dm; + } + else{ + psys_disable_all(ob); + + dm=mesh_get_derived_final(ob,0); + + psys_enable_all(ob); + } + + mface=dm->getFaceDataArray(dm,CD_MFACE); + mface+=min_face; + mvert=dm->getVertDataArray(dm,CD_MVERT); + + + /* permeability check */ + if(BLI_frand()<ob->pd->pdef_perm) + through=1; + else + through=0; + + if(through==0 && (part->flag & PART_DIE_ON_COL || ob->pd->flag & PDEFLE_KILL_PART)){ + pa->dietime = cfra-(1.0f-min_d)*dfra; + VecLerpf(def_loc,co1,co2,min_d); + + if(global==0) + Mat4MulVecfl(ob->obmat,def_loc); + + VECCOPY(state->co,def_loc); + VecLerpf(state->vel,pa->state.vel,state->vel,min_d); + QuatInterpol(state->rot,pa->state.rot,state->rot,min_d); + VecLerpf(state->ave,pa->state.ave,state->ave,min_d); + + *pa_die=1; + + /* particle is dead so we don't need to calculate further */ + deflections=10; + + /* store for reactors */ + copy_particle_key(&cstate,state,0); + + if(part->flag & PART_STICKY){ + pa->stick_ob=ob; + pa->flag |= PARS_STICKY; + //stick_particle_to_object(ob,pa,state); + } + } + else{ + VecLerpf(def_loc,co1,co2,min_d); + + if(radius>0.0f){ + VECSUB(unit_nor,def_loc,ipoint); + } + else{ + /* get deflection point & normal */ + psys_interpolate_face(mvert,mface,0,min_w,ipoint,unit_nor,0,0); + if(global){ + Mat4Mul3Vecfl(ob->obmat,unit_nor); + Mat4MulVecfl(ob->obmat,ipoint); + } + } + + Normalize(unit_nor); + + VECSUB(dvec,co1,co2); + /* scale to remaining length after deflection */ + VecMulf(dvec,1.0f-min_d); + + /* flip normal to face particle */ + if(Inpf(unit_nor,dvec)<0.0f) + VecMulf(unit_nor,-1.0f); + + /* store for easy velocity calculation */ + o_len=VecLength(dvec); + + /* project particle movement to normal & create tangent */ + dotprod=Inpf(dvec,unit_nor); + VECCOPY(def_nor,unit_nor); + VecMulf(def_nor,dotprod); + VECSUB(def_tan,def_nor,dvec); + + damp=ob->pd->pdef_damp+ob->pd->pdef_rdamp*2*(BLI_frand()-0.5f); + + /* create location after deflection */ + VECCOPY(dvec,def_nor); + damp=ob->pd->pdef_damp+ob->pd->pdef_rdamp*2*(BLI_frand()-0.5f); + CLAMP(damp,0.0,1.0); + VecMulf(dvec,1.0f-damp); + if(through) + VecMulf(dvec,-1.0); + + frict=ob->pd->pdef_frict+ob->pd->pdef_rfrict*2.0f*(BLI_frand()-0.5f); + CLAMP(frict,0.0,1.0); + VECADDFAC(dvec,dvec,def_tan,1.0f-frict); + + /* store for easy velocity calculation */ + d_len=VecLength(dvec); + + /* just to be sure we don't hit the current face again */ + if(through){ + VECADDFAC(ipoint,ipoint,unit_nor,-0.0001f); + VECADDFAC(def_loc,def_loc,unit_nor,-0.0001f); + + if(part->flag & PART_ROT_DYN){ + VECADDFAC(def_tan,def_tan,unit_nor,-0.0001f); + VECADDFAC(def_nor,def_nor,unit_nor,-0.0001f); + } + } + else{ + VECADDFAC(ipoint,ipoint,unit_nor,0.0001f); + VECADDFAC(def_loc,def_loc,unit_nor,0.0001f); + + if(part->flag & PART_ROT_DYN){ + VECADDFAC(def_tan,def_tan,unit_nor,0.0001f); + VECADDFAC(def_nor,def_nor,unit_nor,0.0001f); + } + } + + /* lets get back to global space */ + if(global==0){ + Mat4Mul3Vecfl(ob->obmat,dvec); + Mat4MulVecfl(ob->obmat,ipoint); + Mat4MulVecfl(ob->obmat,def_loc);/* def_loc remains as intersection point for next iteration */ + } + + /* store for reactors */ + VECCOPY(cstate.co,ipoint); + VecLerpf(cstate.vel,pa->state.vel,state->vel,min_d); + QuatInterpol(cstate.rot,pa->state.rot,state->rot,min_d); + + /* slightly unphysical but looks nice enough */ + if(part->flag & PART_ROT_DYN){ + if(global==0){ + Mat4Mul3Vecfl(ob->obmat,def_nor); + Mat4Mul3Vecfl(ob->obmat,def_tan); + } + + Normalize(def_tan); + Normalize(def_nor); + VECCOPY(unit_nor,def_nor); + + /* create normal velocity */ + VecMulf(def_nor,Inpf(pa->state.vel,def_nor)); + + /* create tangential velocity */ + VecMulf(def_tan,Inpf(pa->state.vel,def_tan)); + + /* angular velocity change due to tangential velocity */ + Crossf(dave,unit_nor,def_tan); + VecMulf(dave,1.0f/pa->size); + + /* linear velocity change due to angular velocity */ + VecMulf(unit_nor,pa->size); /* point of impact from particle center */ + Crossf(dvel,pa->state.ave,unit_nor); + + if(through) + VecMulf(def_nor,-1.0); + + VecMulf(def_nor,1.0f-damp); + VECSUB(dvel,dvel,def_nor); + + VecMulf(dvel,1.0f-frict); + VecMulf(dave,1.0f-frict); + } + + if(d_len<0.001 && VecLength(pa->state.vel)<0.001){ + /* kill speed to stop slipping */ + VECCOPY(state->vel,zerovec); + VECCOPY(state->co,def_loc); + if(part->flag & PART_ROT_DYN) + VECCOPY(state->ave,zerovec); + deflections=10; + } + else{ + + /* apply new coordinates */ + VECADD(state->co,def_loc,dvec); + + Normalize(dvec); + + /* we have to use original velocity because otherwise we get slipping */ + /* when forces like gravity balance out damping & friction */ + VecMulf(dvec,VecLength(pa->state.vel)*(d_len/o_len)); + VECCOPY(state->vel,dvec); + + if(part->flag & PART_ROT_DYN){ + VECADD(state->vel,state->vel,dvel); + VecMulf(state->vel,0.5); + VECADD(state->ave,state->ave,dave); + VecMulf(state->ave,0.5); + } + } + } + deflections++; + + cstate.time=cfra-(1.0f-min_d)*dfra; + //particle_react_to_collision(min_ob,pob,psys,pa,p,&cstate); + push_reaction(pob,psys,p,PART_EVENT_COLLIDE,&cstate); + } + else + return; + } +} +/************************************************/ +/* Boid physics */ +/************************************************/ +static int boid_see_mesh(ListBase *lb, 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(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(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_deform(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,min_w,loc,nor,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, Object *ob, ParticleSystem *psys, ParticleSettings *part, KDTree *tree, float timestep, float cfra, float *acc, int *pa_die) +{ + 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->state.vel); + bvf->Mulf(dvec,5.0f); + bvf->Addf(dvec,dvec,pa->state.co); + if(boid_see_mesh(&psys->effectors,ob,psys,pa->state.co,dvec,ob_co,ob_nor,cfra)){ + float probelen = bvf->Length(dvec); + float proj; + float oblen; + + Normalize(ob_nor); + proj = bvf->Inpf(ob_nor,pa->state.vel); + + bvf->Subf(dvec,pa->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->state.co); + + distance=Normalize(dvec); + + if(part->flag & PART_DIE_ON_COL && distance < pd->mindist){ + *pa_die=1; + 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){ + count=BLI_kdtree_find_n_nearest(ec->tree,epart->boidneighbours,pa->state.co,NULL,ptn2); + for(p=0; p<count; p++){ + state.time=-1.0; + if(psys_get_particle_state(eob,epsys,ptn2[p].index,&state,0)){ + VECSUB(dvec, state.co, pa->state.co); + + distance = Normalize(dvec); + + if(part->flag & PART_DIE_ON_COL && distance < (epsys->particles+ptn2[p].index)->size){ + *pa_die=1; + 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){ + bvf->Subf(dvec,pa->state.co,pars[ptn[n].index].state.co); + bvf->Mulf(dvec,(2.0f*pa->size-ptn[n].dist)/ptn[n].dist); + bvf->Addf(avoid,avoid,dvec); + near++; + } + /* 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->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->state.vel); + if(cur_vel>0.0){ + bvf->Copyf(dvec,pa->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->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->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->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){ + count=BLI_kdtree_find_n_nearest(ec->tree,epart->boidneighbours,pa->state.co,NULL,ptn2); + for(p=0; p<count; p++){ + state.time=-1.0; + if(psys_get_particle_state(eob,epsys,ptn2[p].index,&state,0)){ + VECSUB(dvec, state.co, pa->state.co); + + 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->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(BoidVecFunc *bvf, ParticleData *pa, ParticleSystem *psys, ParticleSettings *part, float timestep, float *acc, ParticleKey *state) +{ + float dvec[3], bvec[3], length, max_vel=part->max_vel; + float *q2, q[4]; + float g=9.81f, pa_mass=part->mass; + float yvec[3]={0.0,1.0,0.0}, zvec[3]={0.0,0.0,-1.0}, bank; + + /* apply new velocity, location & rotation */ + copy_particle_key(state,&pa->state,0); + + 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,state->vel); + bvf->Mulf(bvec,timestep); + bvf->Addf(dvec,dvec,bvec); + bvf->Addf(state->co,state->co,dvec); + + /* air speed from wind 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; + + if(pd->forcefield==PFIELD_WIND && pd->f_strength!=0.0){ + float distance, wind[3]; + VecCopyf(wind,eob->obmat[2]); + distance=VecLenf(state->co,eob->obmat[3]); + + if (distance < 0.001) distance = 0.001f; + + if(pd->flag&PFIELD_USEMAX && distance > pd->maxdist) + ; + else{ + Normalize(wind); + VecMulf(wind,pd->f_strength/(float)pow((double)distance,(double)pd->f_power)); + bvf->Addf(state->co,state->co,wind); + } + } + } + } + } + + + if((part->flag & PART_BOIDS_2D)==0 && pa->state.vel[0]!=0.0 && pa->state.vel[0]!=0.0 && pa->state.vel[0]!=0.0){ + Crossf(yvec,state->vel,zvec); + + 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(state->vel,bank,q); + + VECCOPY(dvec,state->vel); + VecMulf(dvec,-1.0f); + q2= vectoquat(dvec, OB_POSX, OB_POSZ); + + QuatMul(state->rot,q,q2); + + bvf->Mulf(acc,timestep); + bvf->Addf(state->vel,state->vel,acc); + + if(part->flag & PART_BOIDS_2D){ + state->vel[2]=0.0; + state->co[2]=part->groundz; + + if(psys->keyed_ob){ + Object *zob=psys->keyed_ob; + int min_face; + float co1[3],co2[3],min_d=2.0,min_w[4],imat[4][4]; + VECCOPY(co1,state->co); + VECCOPY(co2,state->co); + + co1[2]=1000.0f; + co2[2]=-1000.0f; + + Mat4Invert(imat,zob->obmat); + Mat4MulVecfl(imat,co1); + Mat4MulVecfl(imat,co2); + + if(psys_intersect_dm(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(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,min_w,loc,nor,0,0); + + Mat4MulVecfl(zob->obmat,loc); + Mat4Mul3Vecfl(zob->obmat,nor); + + Normalize(nor); + + VECCOPY(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,state->rot); + + QuatMul(state->rot,q,q1); + } + } + } + } + + length=bvf->Length(state->vel); + if(length > max_vel) + bvf->Mulf(state->vel,max_vel/length); +} +/************************************************/ +/* Hair */ +/************************************************/ +void save_hair(Object *ob, ParticleSystem *psys, ParticleSystemModifierData *psmd, float cfra){ + ParticleData *pa; + HairKey *key; + int totpart; + int i; + + Mat4Invert(ob->imat,ob->obmat); + + psys->lattice=psys_get_lattice(ob,psys); + + if(psys->totpart==0) return; + + totpart=psys->totpart; + + /* save new keys for elements if needed */ + for(i=0,pa=psys->particles; i<totpart; i++,pa++) { + /* first time alloc */ + if(pa->totkey==0 || pa->hair==NULL) { + pa->hair = MEM_callocN((psys->part->hair_step + 1) * sizeof(HairKey), "HairKeys"); + pa->totkey = 0; + } + + key = pa->hair + pa->totkey; + + /* convert from global to geometry space */ + VecCopyf(key->co, pa->state.co); + Mat4MulVecfl(ob->imat, key->co); + + if(pa->totkey) { + VECSUB(key->co, key->co, pa->hair->co); + psys_vec_rot_to_face(psmd->dm, pa, key->co); + } + + key->time = pa->state.time; + + key->weight = 1.0f - key->time / 100.0f; + + pa->totkey++; + + /* root is always in the origin of hair space so we set it to be so after the last key is saved*/ + if(pa->totkey == psys->part->hair_step + 1) + pa->hair->co[0] = pa->hair->co[1] = pa->hair->co[2] = 0.0f; + } +} +/************************************************/ +/* System Core */ +/************************************************/ +/* unbaked particles are calculated dynamically */ +static void dynamics_step(Object *ob, ParticleSystem *psys, ParticleSystemModifierData *psmd, float cfra, + float *vg_vel, float *vg_tan, float *vg_rot, float *vg_size) +{ + ParticleData *pa; + ParticleKey *outstate, *key; + ParticleSettings *part=psys->part; + KDTree *tree=0; + BoidVecFunc bvf; + IpoCurve *icu_esize=find_ipocurve(part->ipo,PART_EMIT_SIZE); + Material *ma=give_current_material(ob,part->omat); + float timestep; + int p, totpart, pa_die; + /* current time */ + float ctime, ipotime; + /* frame & time changes */ + float dfra, dtime, pa_dtime, pa_dfra=0.0; + float birthtime, dietime; + + /* where have we gone in time since last time */ + dfra= cfra - psys->cfra; + + totpart=psys->totpart; + + timestep=psys_get_timestep(part); + dtime= dfra*timestep; + ctime= cfra*timestep; + ipotime= cfra; + + if(part->flag&PART_ABS_TIME && part->ipo){ + calc_ipo(part->ipo, cfra); + execute_ipo((ID *)part, part->ipo); + } + + if(dfra<0.0){ + float *vg_size=0; + if(part->type==PART_REACTOR) + vg_size=psys_cache_vgroup(psmd->dm,psys,PSYS_VG_SIZE); + + for(p=0, pa=psys->particles; p<totpart; p++,pa++){ + if(pa->flag & (PARS_NO_DISP+PARS_UNEXIST)) continue; + + /* set correct ipo timing */ + if((part->flag&PART_ABS_TIME)==0 && part->ipo){ + ipotime=100.0f*(cfra-pa->time)/pa->lifetime; + calc_ipo(part->ipo, ipotime); + execute_ipo((ID *)part, part->ipo); + } + pa->size=psys_get_size(ob,ma,psmd,icu_esize,psys,part,pa,vg_size); + + if(part->type==PART_REACTOR) + initialize_particle(pa,p,ob,psys,psmd); + + reset_particle(pa,psys,psmd,ob,dtime,cfra,vg_vel,vg_tan,vg_rot); + + if(cfra>pa->time && part->flag & PART_LOOP && (part->flag & PART_LOOP_INSTANT)==0){ + pa->loop=(short)((cfra-pa->time)/pa->lifetime)+1; + pa->alive=PARS_UNBORN; + } + else{ + pa->loop=0; + if(cfra<=pa->time) + pa->alive=PARS_UNBORN; + /* without dynamics the state is allways known so no need to kill */ + else if(ELEM(part->phystype,PART_PHYS_NO,PART_PHYS_KEYED)==0) + pa->alive=PARS_KILLED; + } + } + + if(vg_size) + MEM_freeN(vg_size); + + //if(part->phystype==PART_PHYS_SOLID) + // reset_to_first_fragment(psys); + } + else{ + BLI_srandom(31415926 + (int)cfra + psys->seed); + + /* outstate is used so that particles are updated in parallel */ + outstate=MEM_callocN(totpart*sizeof(ParticleKey),"Particle Outstates"); + + /* update effectors */ + if(psys->effectors.first) + psys_end_effectors(psys); + + psys_init_effectors(ob,part->eff_group,psys); + + if(psys->effectors.first) + precalc_effectors(ob,psys,psmd); + + if(part->phystype==PART_PHYS_BOIDS){ + /* create particle tree for fast inter-particle comparisons */ + KDTree *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); + } + BLI_kdtree_balance(tree); + set_boid_vec_func(&bvf,part->flag&PART_BOIDS_2D); + } + + /* main loop: calculate physics for all particles */ + for(p=0, pa=psys->particles, key=outstate; p<totpart; p++,pa++,key++){ + if(pa->flag & (PARS_NO_DISP|PARS_UNEXIST)) continue; + + copy_particle_key(key,&pa->state,1); + + /* set correct ipo timing */ + if((part->flag&PART_ABS_TIME)==0 && part->ipo){ + ipotime=100.0f*(cfra-pa->time)/pa->lifetime; + calc_ipo(part->ipo, ipotime); + execute_ipo((ID *)part, part->ipo); + } + pa->size=psys_get_size(ob,ma,psmd,icu_esize,psys,part,pa,vg_size); + + pa_die=0; + + if(pa->alive==PARS_UNBORN || pa->alive==PARS_KILLED || ELEM(part->phystype,PART_PHYS_NO,PART_PHYS_KEYED)){ + /* allways reset particles to emitter before birth */ + reset_particle(pa,psys,psmd,ob,dtime,cfra,vg_vel,vg_tan,vg_rot); + copy_particle_key(key,&pa->state,1); + } + + if(dfra>0.0 || psys->recalc){ + + if(psys->reactevents.first && ELEM(pa->alive,PARS_DEAD,PARS_KILLED)==0) + react_to_events(psys,p); + + pa_dfra= dfra; + pa_dtime= dtime; + + if(pa->flag & PART_LOOP && pa->flag & PART_LOOP_INSTANT) + birthtime=pa->dietime; + else + birthtime=pa->time+pa->loop*pa->lifetime; + + dietime=birthtime+pa->lifetime; + + if(birthtime < cfra && birthtime >= psys->cfra){ + /* particle is born some time between this and last step*/ + pa->alive=PARS_ALIVE; + pa_dfra= cfra - birthtime; + pa_dtime= pa_dfra*timestep; + } + else if(dietime <= cfra && psys->cfra < dietime){ + /* particle dies some time between this and last step */ + pa_dfra= dietime - psys->cfra; + pa_dtime= pa_dfra*timestep; + pa_die=1; + } + else if(dietime < cfra){ + /* TODO: figure out if there's something to be done when particle is dead */ + } + + copy_particle_key(key,&pa->state,1); + + if(dfra>0.0 && pa->alive==PARS_ALIVE){ + switch(part->phystype){ + case PART_PHYS_NEWTON: + /* do global forces & effectors */ + apply_particle_forces(p,pa,ob,psys,part,timestep,pa_dfra,cfra,key); + + /* deflection */ + deflect_particle(ob,psmd,psys,part,pa,p,pa_dfra,cfra,key,&pa_die); + + /* rotations */ + rotate_particle(part,pa,pa_dfra,timestep,key); + + break; + case PART_PHYS_BOIDS: + { + float acc[3]; + boid_brain(&bvf,pa,ob,psys,part,tree,timestep,cfra,acc,&pa_die); + if(pa_die==0) + boid_body(&bvf,pa,psys,part,timestep,acc,key); + break; + } + } + + push_reaction(ob,psys,p,PART_EVENT_NEAR,key); + + if(pa_die){ + push_reaction(ob,psys,p,PART_EVENT_DEATH,key); + + if(part->flag & PART_LOOP){ + pa->loop++; + + if(part->flag & PART_LOOP_INSTANT){ + reset_particle(pa,psys,psmd,ob,0.0,cfra,vg_vel,vg_tan,vg_rot); + pa->alive=PARS_ALIVE; + copy_particle_key(key,&pa->state,1); + } + else + pa->alive=PARS_UNBORN; + } + else{ + pa->alive=PARS_DEAD; + key->time=pa->dietime; + + if(pa->flag&PARS_STICKY) + psys_key_to_object(pa->stick_ob,key,0); + } + } + else + key->time=cfra; + } + } + } + /* apply outstates to particles */ + for(p=0, pa=psys->particles, key=outstate; p<totpart; p++,pa++,key++) + copy_particle_key(&pa->state,key,1); + + MEM_freeN(outstate); + } + if(psys->reactevents.first) + BLI_freelistN(&psys->reactevents); + + if(tree) + BLI_kdtree_free(tree); +} + +/* check if path cache or children need updating and do it if needed */ +static void psys_update_path_cache(Object *ob, ParticleSystemModifierData *psmd, ParticleSystem *psys, float cfra) +{ + ParticleSettings *part=psys->part; + ParticleEditSettings *pset=&G.scene->toolsettings->particle; + int distr=0,alloc=0; + + if((psys->part->childtype && psys->totchild != psys->totpart*part->child_nbr) || psys->recalc&PSYS_ALLOC) + alloc=1; + + if(alloc || psys->recalc&PSYS_DISTR || (psys->vgroup[PSYS_VG_DENSITY] && (G.f & G_WEIGHTPAINT))) + distr=1; + + if(distr){ + if(alloc) + alloc_particles(psys,psys->totpart); + + if(psys->totchild && part->childtype){ + distribute_particles(ob,psys,PART_FROM_CHILD); + + if(part->from!=PART_FROM_PARTICLE && part->childtype==PART_CHILD_FACES && part->parents!=0.0) + psys_find_parents(ob,psmd,psys); + } + } + + if((part->type==PART_HAIR || psys->flag&PSYS_KEYED) && (psys_in_edit_mode(psys) + || part->draw_as==PART_DRAW_PATH || part->draw&PART_DRAW_KEYS)){ + psys_cache_paths(ob, psys, cfra, 0); + + if(part->childtype){ + if((G.rendering || (part->flag&PART_CHILD_RENDER)==0) + || (psys_in_edit_mode(psys) && (pset->flag&PE_SHOW_CHILD))) + psys_cache_child_paths(ob, psys, cfra, 0); + } + } + else if(psys->pathcache) + psys_free_path_cache(psys); +} + +static void hair_step(Object *ob, ParticleSystemModifierData *psmd, ParticleSystem *psys, float cfra) +{ + ParticleSettings *part = psys->part; + + if(psys->recalc & PSYS_DISTR){ + /* need this for changing subsurf levels */ + psys_calc_dmfaces(ob, psmd->dm, psys); + } + + if(psys->effectors.first) + psys_end_effectors(psys); + + psys_init_effectors(ob,part->eff_group,psys); + if(psys->effectors.first) + precalc_effectors(ob,psys,psmd); + + if(psys_in_edit_mode(psys)) + PE_recalc_world_cos(ob, psys); + + psys_update_path_cache(ob,psmd,psys,cfra); +} + +/* updates cached particles' alive & other flags etc..*/ +static void cached_step(Object *ob, ParticleSystemModifierData *psmd, ParticleSystem *psys, float cfra, float *vg_size) +{ + ParticleSettings *part=psys->part; + ParticleData *pa; + ParticleKey state; + IpoCurve *icu_esize=find_ipocurve(part->ipo,PART_EMIT_SIZE); + Material *ma=give_current_material(ob,part->omat); + int p; + float ipotime=cfra, disp; + + /* deprecated */ + //if(psys->recalc&PSYS_DISTR){ + // /* The dm could have been changed so particle emitter element */ + // /* indices might be wrong. There's really no "nice" way to handle*/ + // /* this so we just try not to crash by correcting indices. */ + // int totnum=-1; + // switch(part->from){ + // case PART_FROM_VERT: + // totnum=psmd->dm->getNumVerts(psmd->dm); + // break; + // case PART_FROM_FACE: + // case PART_FROM_VOLUME: + // totnum=psmd->dm->getNumFaces(psmd->dm); + // break; + // } + + // if(totnum==0){ + // /* Now we're in real trouble, there's no emitter elements!! */ + // for(p=0, pa=psys->particles; p<psys->totpart; p++,pa++) + // pa->num=-1; + // } + // else if(totnum>0){ + // for(p=0, pa=psys->particles; p<psys->totpart; p++,pa++) + // pa->num=pa->num%totnum; + // } + //} + + if(psys->effectors.first) + psys_end_effectors(psys); + + //if(part->flag & (PART_BAKED_GUIDES+PART_BAKED_DEATHS)){ + psys_init_effectors(ob,part->eff_group,psys); + if(psys->effectors.first) + precalc_effectors(ob,psys,psmd); + //} + + disp= (float)get_current_display_percentage(psys)/50.0f-1.0f; + + for(p=0, pa=psys->particles; p<psys->totpart; p++,pa++){ + if((part->flag&PART_ABS_TIME)==0 && part->ipo){ + ipotime=100.0f*(cfra-pa->time)/pa->lifetime; + calc_ipo(part->ipo, ipotime); + execute_ipo((ID *)part, part->ipo); + } + pa->size= psys_get_size(ob,ma,psmd,icu_esize,psys,part,pa,vg_size); + + psys->lattice=psys_get_lattice(ob,psys); + + /* update alive status and push events */ + if(pa->time>cfra) + pa->alive=PARS_UNBORN; + else if(pa->dietime<=cfra){ + if(pa->dietime>psys->cfra){ + state.time=pa->dietime; + psys_get_particle_state(ob,psys,p,&state,1); + push_reaction(ob,psys,p,PART_EVENT_DEATH,&state); + } + pa->alive=PARS_DEAD; + } + else{ + pa->alive=PARS_ALIVE; + state.time=cfra; + psys_get_particle_state(ob,psys,p,&state,1); + state.time=cfra; + push_reaction(ob,psys,p,PART_EVENT_NEAR,&state); + } + + if(psys->lattice){ + end_latt_deform(); + psys->lattice=0; + } + + if(pa->r_rot[0] > disp) + pa->flag |= PARS_NO_DISP; + else + pa->flag &= ~PARS_NO_DISP; + } +} +/* Calculates the next state for all particles of the system */ +/* In particles code most fra-ending are frames, time-ending are fra*timestep (seconds)*/ +static void system_step(Object *ob, ParticleSystem *psys, ParticleSystemModifierData *psmd, float cfra) +{ + ParticleSettings *part; + ParticleData *pa; + int totpart,oldtotpart=0,p; + float disp, *vg_vel=0, *vg_tan=0, *vg_rot=0, *vg_size=0; + int init=0,distr=0,alloc=0; + + /*----start validity checks----*/ + + part=psys->part; + + if(part->flag&PART_ABS_TIME && part->ipo){ + calc_ipo(part->ipo, cfra); + execute_ipo((ID *)part, part->ipo); + } + + if(part->from!=PART_FROM_PARTICLE) + vg_size=psys_cache_vgroup(psmd->dm,psys,PSYS_VG_SIZE); + + if(part->type == PART_HAIR) { + if(psys->flag & PSYS_HAIR_DONE) { + hair_step(ob, psmd, psys, cfra); + psys->cfra = cfra; + psys->recalc = 0; + return; + } + } + else { + if(psys->recalc) + clear_particles_from_cache(ob,psys,(int)cfra); + else if(get_particles_from_cache(ob, psys, (int)cfra)){ + cached_step(ob,psmd,psys,cfra,vg_size); + psys->cfra=cfra; + psys->recalc = 0; + return; + } + } + + /* if still here react to events */ + + if(psys->recalc&PSYS_TYPE) { + /* system type has changed so set sensible defaults and clear non applicable flags */ + if(part->from == PART_FROM_PARTICLE) { + if(part->type != PART_REACTOR) + part->from = PART_FROM_FACE; + if(part->distr == PART_DISTR_GRID) + part->distr = PART_DISTR_JIT; + } + + if(psys->part->phystype != PART_PHYS_KEYED) + psys->flag &= ~PSYS_KEYED; + + if(part->type == PART_HAIR) { + part->draw_as = PART_DRAW_PATH; + part->rotfrom = PART_ROT_IINCR; + } + else + free_hair(psys); + + psys->recalc &= ~PSYS_TYPE; + alloc = 1; + } + else + oldtotpart = psys->totpart; + + if(part->distr == PART_DISTR_GRID) + totpart = part->grid_res * part->grid_res * part->grid_res; + else + totpart = psys->part->totpart; + + if(oldtotpart != totpart || psys->recalc&PSYS_ALLOC || (psys->part->childtype && psys->totchild != psys->totpart*part->child_nbr)) + alloc = 1; + + if(alloc || psys->recalc&PSYS_DISTR || (psys->vgroup[PSYS_VG_DENSITY] && (G.f & G_WEIGHTPAINT) && ob==OBACT)) + distr = 1; + + if(distr || psys->recalc&PSYS_INIT) + init = 1; + + if(init) { + if(distr) { + if(alloc) + alloc_particles(psys, totpart); + + distribute_particles(ob, psys, part->from); + + if(psys->totchild && part->childtype) + distribute_particles(ob, psys, PART_FROM_CHILD); + } + initialize_all_particles(ob, psys, psmd); + + if(alloc) + reset_all_particles(ob, psys, psmd, 0.0, cfra, oldtotpart); + + /* flag for possible explode modifiers after this system */ + psmd->flag |= eParticleSystemFlag_Pars; + } + + + if(part->phystype==PART_PHYS_KEYED && psys->flag&PSYS_FIRST_KEYED) + psys_count_keyed_targets(ob,psys); + + if(part->from!=PART_FROM_PARTICLE){ + vg_vel=psys_cache_vgroup(psmd->dm,psys,PSYS_VG_VEL); + vg_tan=psys_cache_vgroup(psmd->dm,psys,PSYS_VG_TAN); + vg_rot=psys_cache_vgroup(psmd->dm,psys,PSYS_VG_ROT); + } + + /* set particles to be not calculated */ + disp= (float)get_current_display_percentage(psys)/50.0f-1.0f; + + if(disp<1.0f) for(p=0, pa=psys->particles; p<totpart; p++,pa++){ + if(pa->r_rot[0] > disp) + pa->flag |= PARS_NO_DISP; + else + pa->flag &= ~PARS_NO_DISP; + } + + /* ok now we're all set so let's go */ + if(psys->totpart) { + //if(psys->part->from==PART_FROM_FACE) { + // psys_calc_dmfaces(ob, psmd->dm, psys); + //} + dynamics_step(ob,psys,psmd,cfra,vg_vel,vg_tan,vg_rot,vg_size); + } + psys->recalc = 0; + psys->cfra=cfra; + + if(part->type!=PART_HAIR) + write_particles_to_cache(ob, psys, cfra); + + /* for keyed particles the path is allways known so it can be drawn */ + if(part->phystype==PART_PHYS_KEYED && psys->flag&PSYS_FIRST_KEYED){ + set_keyed_keys(ob, psys); + psys_update_path_cache(ob,psmd,psys,(int)cfra); + } + else if(psys->pathcache) + psys_free_path_cache(psys); + + if(vg_vel) + MEM_freeN(vg_vel); + + if(psys->lattice){ + end_latt_deform(); + psys->lattice=0; + } +} + +void psys_to_softbody(Object *ob, ParticleSystem *psys, int force_recalc) +{ + SoftBody *sb; + short softflag; + + if((psys->softflag&OB_SB_ENABLE)==0) return; + + if((ob->recalc&OB_RECALC_TIME)==0) + psys->softflag|=OB_SB_REDO; + + /* let's replace the object's own softbody with the particle softbody */ + /* a temporary solution before cloth simulation is implemented, jahka */ + + /* save these */ + sb=ob->soft; + softflag=ob->softflag; + + /* swich to new ones */ + ob->soft=psys->soft; + ob->softflag=psys->softflag; + + /* signal for before/free bake */ + //if(psys->flag & PSYS_SOFT_BAKE || force_recalc){ + // sbObjectToSoftbody(ob); + // psys->flag &= ~PSYS_SOFT_BAKE; + //} + + /* do softbody */ + sbObjectStep(ob, (float)G.scene->r.cfra, NULL, psys_count_keys(psys)); + + /* return things back to normal */ + psys->soft=ob->soft; + psys->softflag=ob->softflag; + + ob->soft=sb; + ob->softflag=softflag; +} +static int hair_needs_recalc(ParticleSystem *psys) +{ + if((psys->flag & PSYS_EDITED)==0 && ( + (psys->flag & PSYS_HAIR_DONE)==0 + || psys->recalc & PSYS_RECALC_HAIR) + ) { + psys->recalc &= ~PSYS_RECALC_HAIR; + return 1; + } + + return 0; +} +/* main particle update call, checks that things are ok on the large scale before actual particle calculations */ +void particle_system_update(Object *ob, ParticleSystem *psys){ + + ParticleSystemModifierData *psmd=0; + float cfra; + + if((psys->flag & PSYS_ENABLED)==0) return; + + psmd=psys_get_modifier(ob,psys); + + cfra=bsystem_time(ob,(float)CFRA,0.0); + + /* system was already updated from modifier stack */ + if(psmd->flag&eParticleSystemFlag_psys_updated){ + psmd->flag &= ~eParticleSystemFlag_psys_updated; + /* make sure it really was updated to cfra */ + if(psys->cfra==cfra) + return; + } + + /* baked path softbody */ + if(psys->part->type==PART_HAIR && psys->soft) + psys_to_softbody(ob, psys, 0); + + /* not needed, this is all handled in hair_step */ + ///* is the mesh changing under the edited particles? */ + //if((psys->flag & PSYS_EDITED) && psys->part->type==PART_HAIR && psys->recalc & PSYS_RECALC_HAIR) { + // /* Just update the particles on the mesh */ + // psys_update_edithair_dmfaces(ob, psmd->dm, psys); + //} + + if(psys->part->type==PART_HAIR && hair_needs_recalc(psys)){ + float hcfra=0.0f; + int i; + free_hair(psys); + + /* first step is negative so particles get killed and reset */ + psys->cfra=1.0f; + + for(i=0; i<=psys->part->hair_step; i++){ + hcfra=100.0f*(float)i/(float)psys->part->hair_step; + system_step(ob,psys,psmd,hcfra); + save_hair(ob,psys,psmd,hcfra); + } + + psys->flag |= PSYS_HAIR_DONE; + + if(psys->softflag&OB_SB_ENABLE) + psys_to_softbody(ob,psys,1); + } + + system_step(ob,psys,psmd,cfra); + + Mat4CpyMat4(psys->imat, ob->imat); /* used for duplicators */ +} + diff --git a/source/blender/blenkernel/intern/pointcache.c b/source/blender/blenkernel/intern/pointcache.c new file mode 100644 index 00000000000..9492754260c --- /dev/null +++ b/source/blender/blenkernel/intern/pointcache.c @@ -0,0 +1,185 @@ +/** + * + * ***** BEGIN GPL/BL DUAL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. The Blender + * Foundation also sells licenses for use in proprietary software under + * the Blender License. See http://www.blender.org/BL/ for information + * about this. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. + * All rights reserved. + * +* Contributor(s): Campbell Barton <ideasman42@gmail.com> + * + * ***** END GPL/BL DUAL LICENSE BLOCK ***** + */ + + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <sys/stat.h> +#include <sys/types.h> + +#include "BKE_pointcache.h" + +#include "BKE_utildefines.h" +#include "BKE_global.h" +#include "BKE_library.h" + +#include "BLI_blenlib.h" +#include "BKE_utildefines.h" +#include "blendef.h" + +/* needed for directory lookup */ +#ifndef WIN32 + #include <dirent.h> +#else + #include "BLI_winstuff.h" +#endif + +/* Takes an Object ID and returns a unique name + - id: object id + - cfra: frame for the cache, can be negative + - stack_index: index in the modifier stack. we can have cache for more then one stack_index +*/ + +static int ptcache_path(char *filename) +{ + sprintf(filename, PTCACHE_PATH); + BLI_convertstringcode(filename, G.sce, 0); + return strlen(filename); +} + +int BKE_ptcache_id_filename(struct ID *id, char *filename, int cfra, int stack_index, short do_path, short do_ext) +{ + int len=0; + char *idname; + char *newname; + filename[0] = '\0'; + newname = filename; + + /* start with temp dir */ + if (do_path) { + len = ptcache_path(filename); + newname += len; + } + idname = (id->name+2); + /* convert chars to hex so they are always a valid filename */ + while('\0' != *idname) { + sprintf(newname, "%02X", (char)(*idname++)); + newname+=2; + len += 2; + } + + if (do_ext) { + sprintf(newname, "_%06d_%02d"PTCACHE_EXT, cfra, stack_index); /* always 6 chars */ + len += 16; + } + + return len; /* make sure the above string is always 16 chars */ +} + +/* youll need to close yourself after! */ +FILE *BKE_ptcache_id_fopen(struct ID *id, char mode, int cfra, int stack_index) +{ + /* mode is same as fopen's modes */ + FILE *fp = NULL; + char filename[(FILE_MAXDIR+FILE_MAXFILE)*2]; + + BKE_ptcache_id_filename(id, filename, cfra, stack_index, 1, 1); + + if (mode=='r') { + if (!BLI_exists(filename)) { + return NULL; + } + fp = fopen(filename, "rb"); + } else if (mode=='w') { + BLI_make_existing_file(filename); /* will create the dir if needs be, same as //textures is created */ + fp = fopen(filename, "wb"); + } + + if (!fp) { + return NULL; + } + + return fp; +} + +/* youll need to close yourself after! + * mode, + +*/ + +void BKE_ptcache_id_clear(struct ID *id, char mode, int cfra, int stack_index) +{ + int len; /* store the length of the string */ + + /* mode is same as fopen's modes */ + DIR *dir; + struct dirent *de; + char path[FILE_MAX]; + char filename[(FILE_MAXDIR+FILE_MAXFILE)*2]; + char path_full[(FILE_MAXDIR+FILE_MAXFILE)*2]; + + /* clear all files in the temp dir with the prefix of the ID and the ".bphys" suffix */ + switch (mode) { + case PTCACHE_CLEAR_ALL: + case PTCACHE_CLEAR_BEFORE: + case PTCACHE_CLEAR_AFTER: + ptcache_path(path); + len = BKE_ptcache_id_filename(id, filename, cfra, stack_index, 0, 0); /* no path */ + + dir = opendir(path); + if (dir==NULL) + return; + + while ((de = readdir(dir)) != NULL) { + if (strstr(de->d_name, PTCACHE_EXT)) { /* do we have the right extension?*/ + if (strncmp(filename, de->d_name, len ) == 0) { /* do we have the right prefix */ + if (mode == PTCACHE_CLEAR_ALL) { + BLI_join_dirfile(path_full, path, de->d_name); + BLI_delete(path_full, 0, 0); + } else { + /* read the number of the file */ + int frame, len2 = strlen(de->d_name); + char num[7]; + if (len2 > 15) { /* could crash if trying to copy a string out of this range*/ + strncpy(num, de->d_name + (strlen(de->d_name) - 15), 6); + frame = atoi(num); + + if((mode==PTCACHE_CLEAR_BEFORE && frame < cfra) || + (mode==PTCACHE_CLEAR_AFTER && frame > cfra) ) { + + BLI_join_dirfile(path_full, path, de->d_name); + BLI_delete(path_full, 0, 0); + } + } + } + } + } + } + closedir(dir); + break; + + case PTCACHE_CLEAR_FRAME: + len = BKE_ptcache_id_filename(id, filename, cfra, stack_index, 1, 1); /* no path */ + BLI_delete(filename, 0, 0); + break; + } + return; +} + diff --git a/source/blender/blenkernel/intern/scene.c b/source/blender/blenkernel/intern/scene.c index 42a61fb5f11..aa9c489044a 100644 --- a/source/blender/blenkernel/intern/scene.c +++ b/source/blender/blenkernel/intern/scene.c @@ -174,6 +174,8 @@ void free_scene(Scene *sce) Scene *add_scene(char *name) { Scene *sce; + ParticleEditSettings *pset; + int a; sce= alloc_libblock(&G.main->scene, ID_SCE, name); sce->lay= 1; @@ -197,6 +199,8 @@ Scene *add_scene(char *name) sce->r.images= 100; sce->r.framelen= 1.0; sce->r.frs_sec= 25; + sce->r.frs_sec_base= 1; + sce->r.ocres = 128; sce->r.bake_mode= 1; /* prevent to include render stuff here */ sce->r.bake_filter= 2; @@ -229,6 +233,20 @@ Scene *add_scene(char *name) sce->toolsettings->uvcalc_mapalign = 1; sce->toolsettings->unwrapper = 1; sce->toolsettings->select_thresh= 0.01f; + sce->toolsettings->jointrilimit = 0.8f; + + pset= &sce->toolsettings->particle; + pset->flag= PE_KEEP_LENGTHS|PE_LOCK_FIRST|PE_DEFLECT_EMITTER; + pset->emitterdist= 0.25f; + pset->totrekey= 5; + pset->totaddkey= 5; + pset->brushtype= PE_BRUSH_NONE; + for(a=0; a<PE_TOT_BRUSH; a++) { + pset->brush[a].strength= 50; + pset->brush[a].size= 50; + pset->brush[a].step= 10; + } + pset->brush[PE_BRUSH_CUT].strength= 100; sce->jumpframe = 10; sce->audio.mixrate = 44100; @@ -528,6 +546,9 @@ void scene_update_for_newframe(Scene *sce, unsigned int lay) { Scene *scene= sce; + /* clears all BONE_UNKEYED flags for every pose's pchans */ + framechange_poses_clear_unkeyed(); + /* object ipos are calculated in where_is_object */ do_all_data_ipos(); @@ -538,7 +559,6 @@ void scene_update_for_newframe(Scene *sce, unsigned int lay) scene_update(sce, lay); scene_update(scene, lay); - } /* return default layer, also used to patch old files */ diff --git a/source/blender/blenkernel/intern/softbody.c b/source/blender/blenkernel/intern/softbody.c index 16a29fb8e80..bd9d1cb75ca 100644 --- a/source/blender/blenkernel/intern/softbody.c +++ b/source/blender/blenkernel/intern/softbody.c @@ -61,9 +61,11 @@ variables on the UI for now #include "DNA_curve_types.h" #include "DNA_object_types.h" #include "DNA_object_force.h" /* here is the softbody struct */ +#include "DNA_particle_types.h" #include "DNA_key_types.h" #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" +#include "DNA_modifier_types.h" #include "DNA_lattice_types.h" #include "DNA_scene_types.h" @@ -75,9 +77,12 @@ variables on the UI for now #include "BKE_global.h" #include "BKE_key.h" #include "BKE_object.h" +#include "BKE_particle.h" #include "BKE_softbody.h" #include "BKE_utildefines.h" #include "BKE_DerivedMesh.h" +#include "BKE_pointcache.h" +#include "BKE_modifier.h" #include "BIF_editdeform.h" #include "BIF_graphics.h" @@ -90,18 +95,6 @@ static int (*SB_localInterruptCallBack)(void) = NULL; /* ********** soft body engine ******* */ - -typedef struct BodyPoint { - float origS[3], origE[3], origT[3], pos[3], vec[3], force[3]; - float goal; - float prevpos[3], prevvec[3], prevdx[3], prevdv[3]; /* used for Heun integration */ - int nofsprings; int *springs; - float choke; - float colball; - short flag; - char octantflag; -} BodyPoint; - typedef struct BodySpring { int v1, v2; float len, strength, cf; @@ -480,21 +473,31 @@ void ccd_build_deflector_hache(Object *vertexowner,GHash *hash) while (base) { /*Only proceed for mesh object in same layer */ if(base->object->type==OB_MESH && (base->lay & vertexowner->lay)) { + int particles=0; ob= base->object; - if((vertexowner) && (ob == vertexowner)){ - /* if vertexowner is given we don't want to check collision with owner object */ - base = base->next; - continue; + if((vertexowner) && (ob == vertexowner)) { + if(vertexowner->soft->particles){ + particles=1; + } + else { + /* if vertexowner is given we don't want to check collision with owner object */ + base = base->next; + continue; + } } /*+++ only with deflecting set */ if(ob->pd && ob->pd->deflect && BLI_ghash_lookup(hash, ob) == 0) { DerivedMesh *dm= NULL; - - if(ob->softflag & OB_SB_COLLFINAL) { /* so maybe someone wants overkill to collide with subsurfed */ - dm = mesh_get_derived_final(ob, CD_MASK_BAREMESH); - } else { - dm = mesh_get_derived_deform(ob, CD_MASK_BAREMESH); + + if(particles) { + dm = psys_get_modifier(ob,psys_get_current(ob))->dm; + } + else { + if(ob->softflag & OB_SB_COLLFINAL) /* so maybe someone wants overkill to collide with subsurfed */ + dm = mesh_get_derived_final(ob, CD_MASK_BAREMESH); + else + dm = mesh_get_derived_deform(ob, CD_MASK_BAREMESH); } if(dm){ @@ -782,21 +785,21 @@ static void calculate_collision_balls(Object *ob) } if (akku_count > 0) { - if (sb->sbc_mode == 0){ + if (sb->sbc_mode == SBC_MODE_MANUAL){ bp->colball=sb->colball; - } - if (sb->sbc_mode == 1){ - bp->colball = akku/(float)akku_count*sb->colball; } - if (sb->colball == 2){ + if (sb->sbc_mode == SBC_MODE_AVG){ + bp->colball = akku/(float)akku_count*sb->colball; + } + if (sb->sbc_mode == SBC_MODE_MIN){ bp->colball=min*sb->colball; - } - if (sb->colball == 3){ + } + if (sb->sbc_mode == SBC_MODE_MAX){ bp->colball=max*sb->colball; - } - if (sb->colball == 4){ - bp->colball = (min + max)/2.0f*sb->colball; - } + } + if (sb->sbc_mode == SBC_MODE_AVGMINMAX){ + bp->colball = (min + max)/2.0f*sb->colball; + } } else bp->colball=0; }/*for bp*/ @@ -852,10 +855,11 @@ static void renew_softbody(Object *ob, int totpoint, int totspring) { SoftBody *sb; int i; - + short softflag; if(ob->soft==NULL) ob->soft= sbNew(); else free_softbody_intern(ob->soft); sb= ob->soft; + softflag=ob->softflag; if(totpoint) { sb->totpoint= totpoint; @@ -869,8 +873,8 @@ static void renew_softbody(Object *ob, int totpoint, int totspring) for (i=0; i<totpoint; i++) { BodyPoint *bp = &sb->bpoint[i]; - if(ob->softflag & OB_SB_GOAL) { - bp->goal= ob->soft->defgoal; + if(softflag & OB_SB_GOAL) { + bp->goal= sb->defgoal; } else { bp->goal= 0.0f; @@ -900,7 +904,6 @@ static void free_softbody_baked(SoftBody *sb) sb->keys= NULL; sb->totkey= 0; - } static void free_scratch(SoftBody *sb) { @@ -1203,9 +1206,9 @@ int sb_detect_face_collisionCached(float face_v1[3],float face_v2[3],float face_ Crossf(d_nvect, edge2, edge1); Normalize(d_nvect); if ( - LineIntersectsTriangle(nv1, nv2, face_v1, face_v2, face_v3, &t) || - LineIntersectsTriangle(nv2, nv3, face_v1, face_v2, face_v3, &t) || - LineIntersectsTriangle(nv3, nv1, face_v1, face_v2, face_v3, &t) ){ + LineIntersectsTriangle(nv1, nv2, face_v1, face_v2, face_v3, &t, NULL) || + LineIntersectsTriangle(nv2, nv3, face_v1, face_v2, face_v3, &t, NULL) || + LineIntersectsTriangle(nv3, nv1, face_v1, face_v2, face_v3, &t, NULL) ){ Vec3PlusStVec(force,-1.0f,d_nvect); *damp=ob->pd->pdef_sbdamp; deflected = 2; @@ -1217,9 +1220,9 @@ int sb_detect_face_collisionCached(float face_v1[3],float face_v2[3],float face_ Crossf(d_nvect, edge2, edge1); Normalize(d_nvect); if ( - LineIntersectsTriangle(nv1, nv3, face_v1, face_v2, face_v3, &t) || - LineIntersectsTriangle(nv3, nv4, face_v1, face_v2, face_v3, &t) || - LineIntersectsTriangle(nv4, nv1, face_v1, face_v2, face_v3, &t) ){ + LineIntersectsTriangle(nv1, nv3, face_v1, face_v2, face_v3, &t, NULL) || + LineIntersectsTriangle(nv3, nv4, face_v1, face_v2, face_v3, &t, NULL) || + LineIntersectsTriangle(nv4, nv1, face_v1, face_v2, face_v3, &t, NULL) ){ Vec3PlusStVec(force,-1.0f,d_nvect); *damp=ob->pd->pdef_sbdamp; deflected = 2; @@ -1402,7 +1405,7 @@ int sb_detect_edge_collisionCached(float edge_v1[3],float edge_v2[3],float *damp Crossf(d_nvect, edge2, edge1); Normalize(d_nvect); - if ( LineIntersectsTriangle(edge_v1, edge_v2, nv1, nv2, nv3, &t)){ + if ( LineIntersectsTriangle(edge_v1, edge_v2, nv1, nv2, nv3, &t, NULL)){ float v1[3],v2[3]; float intrusiondepth,i1,i2; VECSUB(v1, edge_v1, nv2); @@ -1421,7 +1424,7 @@ int sb_detect_edge_collisionCached(float edge_v1[3],float edge_v2[3],float *damp Crossf(d_nvect, edge2, edge1); Normalize(d_nvect); - if (LineIntersectsTriangle( edge_v1, edge_v2,nv1, nv3, nv4, &t)){ + if (LineIntersectsTriangle( edge_v1, edge_v2,nv1, nv3, nv4, &t, NULL)){ float v1[3],v2[3]; float intrusiondepth,i1,i2; VECSUB(v1, edge_v1, nv4); @@ -1547,19 +1550,22 @@ int sb_detect_vertex_collisionCached(float opco[3], float facenormal[3], float * float force[3], unsigned int par_layer,struct Object *vertexowner, float time,float vel[3], float *intrusion) { - Object *ob; + Object *ob= NULL; GHash *hash; GHashIterator *ihash; - float nv1[3], nv2[3], nv3[3], nv4[3], edge1[3], edge2[3],d_nvect[3], dv1[3],ve[3],avel[3], - vv1[3], vv2[3], vv3[3], vv4[3], + float nv1[3], nv2[3], nv3[3], nv4[3], edge1[3], edge2[3],d_nvect[3], dv1[3],ve[3],avel[3]={0.0,0.0,0.0}, + vv1[3], vv2[3], vv3[3], vv4[3], coledge[3], mindistedge = 1000.0f, + outerforceaccu[3],innerforceaccu[3], facedist,n_mag,force_mag_norm,minx,miny,minz,maxx,maxy,maxz, innerfacethickness = -0.5f, outerfacethickness = 0.2f, - ee = 5.0f, ff = 0.1f, fa; + ee = 5.0f, ff = 0.1f, fa=1; int a, deflected=0, cavel=0,ci=0; /* init */ *intrusion = 0.0f; hash = vertexowner->soft->scratch->colliderhash; ihash = BLI_ghashIterator_new(hash); + outerforceaccu[0]=outerforceaccu[1]=outerforceaccu[2]=0.0f; + innerforceaccu[0]=innerforceaccu[1]=innerforceaccu[2]=0.0f; /* go */ while (!BLI_ghashIterator_isDone(ihash) ) { @@ -1675,16 +1681,24 @@ int sb_detect_vertex_collisionCached(float opco[3], float facenormal[3], float * Crossf(d_nvect, edge2, edge1); n_mag = Normalize(d_nvect); facedist = Inpf(dv1,d_nvect); + // so rules are + // if ((facedist > innerfacethickness) && (facedist < outerfacethickness)){ if (point_in_tri_prism(opco, nv1, nv2, nv3) ){ force_mag_norm =(float)exp(-ee*facedist); if (facedist > outerfacethickness*ff) force_mag_norm =(float)force_mag_norm*fa*(facedist - outerfacethickness)*(facedist - outerfacethickness); - Vec3PlusStVec(force,force_mag_norm,d_nvect); *damp=ob->pd->pdef_sbdamp; if (facedist > 0.0f){ *damp *= (1.0f - facedist/outerfacethickness); + Vec3PlusStVec(outerforceaccu,force_mag_norm,d_nvect); + deflected = 3; + + } + else { + Vec3PlusStVec(innerforceaccu,force_mag_norm,d_nvect); + if (deflected < 2) deflected = 2; } if ((mprevvert) && (*damp > 0.0f)){ choose_winner(ve,opco,nv1,nv2,nv3,vv1,vv2,vv3); @@ -1693,7 +1707,6 @@ int sb_detect_vertex_collisionCached(float opco[3], float facenormal[3], float * } *intrusion += facedist; ci++; - deflected = 2; } } if (mface->v4){ /* quad */ @@ -1711,11 +1724,17 @@ int sb_detect_vertex_collisionCached(float opco[3], float facenormal[3], float * force_mag_norm =(float)exp(-ee*facedist); if (facedist > outerfacethickness*ff) force_mag_norm =(float)force_mag_norm*fa*(facedist - outerfacethickness)*(facedist - outerfacethickness); - Vec3PlusStVec(force,force_mag_norm,d_nvect); *damp=ob->pd->pdef_sbdamp; - if (facedist > 0.0f){ - *damp *= (1.0f - facedist/outerfacethickness); - } + if (facedist > 0.0f){ + *damp *= (1.0f - facedist/outerfacethickness); + Vec3PlusStVec(outerforceaccu,force_mag_norm,d_nvect); + deflected = 3; + + } + else { + Vec3PlusStVec(innerforceaccu,force_mag_norm,d_nvect); + if (deflected < 2) deflected = 2; + } if ((mprevvert) && (*damp > 0.0f)){ choose_winner(ve,opco,nv1,nv3,nv4,vv1,vv3,vv4); @@ -1724,10 +1743,62 @@ int sb_detect_vertex_collisionCached(float opco[3], float facenormal[3], float * } *intrusion += facedist; ci++; - deflected = 2; } } + if ((deflected < 2)&& (G.rt != 444)) // we did not hit a face until now + { // see if 'outer' hits an edge + float dist; + + PclosestVL3Dfl(ve, opco, nv1, nv2); + VECSUB(ve,opco,ve); + dist = Normalize(ve); + if ((dist < outerfacethickness)&&(dist < mindistedge )){ + VECCOPY(coledge,ve); + mindistedge = dist, + deflected=1; + } + + PclosestVL3Dfl(ve, opco, nv2, nv3); + VECSUB(ve,opco,ve); + dist = Normalize(ve); + if ((dist < outerfacethickness)&&(dist < mindistedge )){ + VECCOPY(coledge,ve); + mindistedge = dist, + deflected=1; + } + + PclosestVL3Dfl(ve, opco, nv3, nv1); + VECSUB(ve,opco,ve); + dist = Normalize(ve); + if ((dist < outerfacethickness)&&(dist < mindistedge )){ + VECCOPY(coledge,ve); + mindistedge = dist, + deflected=1; + } + if (mface->v4){ /* quad */ + PclosestVL3Dfl(ve, opco, nv3, nv4); + VECSUB(ve,opco,ve); + dist = Normalize(ve); + if ((dist < outerfacethickness)&&(dist < mindistedge )){ + VECCOPY(coledge,ve); + mindistedge = dist, + deflected=1; + } + + PclosestVL3Dfl(ve, opco, nv1, nv4); + VECSUB(ve,opco,ve); + dist = Normalize(ve); + if ((dist < outerfacethickness)&&(dist < mindistedge )){ + VECCOPY(coledge,ve); + mindistedge = dist, + deflected=1; + } + + } + + + } } mface++; mima++; @@ -1735,6 +1806,25 @@ int sb_detect_vertex_collisionCached(float opco[3], float facenormal[3], float * } /* if(ob->pd && ob->pd->deflect) */ BLI_ghashIterator_step(ihash); } /* while () */ + + if (deflected == 1){ // no face but 'outer' edge cylinder sees vert + force_mag_norm =(float)exp(-ee*mindistedge); + if (mindistedge > outerfacethickness*ff) + force_mag_norm =(float)force_mag_norm*fa*(mindistedge - outerfacethickness)*(mindistedge - outerfacethickness); + Vec3PlusStVec(force,force_mag_norm,coledge); + *damp=ob->pd->pdef_sbdamp; + if (mindistedge > 0.0f){ + *damp *= (1.0f - mindistedge/outerfacethickness); + } + + } + if (deflected == 2){ // face inner detected + VECADD(force,force,innerforceaccu); + } + if (deflected == 3){ // face outer detected + VECADD(force,force,outerforceaccu); + } + BLI_ghashIterator_free(ihash); if (cavel) VecMulf(avel,1.0f/(float)cavel); VECCOPY(vel,avel); @@ -2313,6 +2403,7 @@ static void softbody_apply_forces(Object *ob, float forcetime, int mode, float * VECCOPY(bp->prevvec, bp->vec); VECCOPY(bp->prevdv, dv); } + if (mode ==2){ /* be optimistic and execute step */ bp->vec[0] = bp->prevvec[0] + 0.5f * (dv[0] + bp->prevdv[0]); @@ -2330,10 +2421,9 @@ static void softbody_apply_forces(Object *ob, float forcetime, int mode, float * /* x(t + dt) = x(t) + v(t) * dt */ VECCOPY(dx,bp->vec); - dx[0]*=forcetime ; - dx[1]*=forcetime ; - dx[2]*=forcetime ; - + dx[0]*=forcetime; + dx[1]*=forcetime; + dx[2]*=forcetime; /* again some nasty if's to have heun in here too */ if (mode ==1){ VECCOPY(bp->prevpos,bp->pos); @@ -2492,6 +2582,7 @@ static void springs_from_mesh(Object *ob) Mesh *me= ob->data; BodyPoint *bp; int a; + float scale =1.0f; sb= ob->soft; if (me && sb) @@ -2509,9 +2600,13 @@ static void springs_from_mesh(Object *ob) } /* recalculate spring length for meshes here */ + /* special hidden feature! shrink to fit */ + if (G.rt > 500){ + scale = (G.rt - 500) / 100.0f; + } for(a=0; a<sb->totspring; a++) { BodySpring *bs = &sb->bspring[a]; - bs->len= VecLenf(sb->bpoint[bs->v1].origS, sb->bpoint[bs->v2].origS); + bs->len= scale*VecLenf(sb->bpoint[bs->v1].origS, sb->bpoint[bs->v2].origS); } } } @@ -2861,6 +2956,103 @@ static void curve_surf_to_softbody(Object *ob) } +static void springs_from_particles(Object *ob) +{ + ParticleSystem *psys; + ParticleSystemModifierData *psmd=0; + ParticleData *pa=0; + HairKey *key=0; + SoftBody *sb; + BodyPoint *bp; + BodySpring *bs; + int a,k; + float hairmat[4][4]; + + psys= ob->soft->particles; + sb= ob->soft; + if(ob && sb && psys) { + psmd = psys_get_modifier(ob, psys); + + bp= sb->bpoint; + for(a=0, pa=psys->particles; a<psys->totpart; a++, pa++) { + for(k=0, key=pa->hair; k<pa->totkey; k++, bp++, key++) { + VECCOPY(bp->origS, key->co); + + psys_mat_hair_to_global(ob, psmd->dm, psys->part->from, pa, hairmat); + + Mat4MulVecfl(hairmat, bp->origS); + } + } + + for(a=0, bs=sb->bspring; a<sb->totspring; a++, bs++) + bs->len= VecLenf(sb->bpoint[bs->v1].origS, sb->bpoint[bs->v2].origS); + } +} + +static void particles_to_softbody(Object *ob) +{ + SoftBody *sb; + BodyPoint *bp; + BodySpring *bs; + ParticleData *pa; + HairKey *key; + ParticleSystem *psys= ob->soft->particles; + float goalfac; + int a, k, curpoint; + int totpoint= psys_count_keys(psys); + int totedge= totpoint-psys->totpart; + + /* renew ends with ob->soft with points and edges, also checks & makes ob->soft */ + renew_softbody(ob, totpoint, totedge); + + psys->particles->bpi = 0; + for(a=1, pa=psys->particles+1; a<psys->totpart; a++, pa++) + pa->bpi = (pa-1)->bpi + pa->totkey; + + /* we always make body points */ + sb= ob->soft; + bp= sb->bpoint; + bs= sb->bspring; + goalfac= ABS(sb->maxgoal - sb->mingoal); + + if((ob->softflag & OB_SB_GOAL)) { + for(a=0, pa=psys->particles; a<psys->totpart; a++, pa++) { + for(k=0, key=pa->hair; k<pa->totkey; k++,bp++,key++) { + if(k) { + bp->goal= key->weight; + bp->goal= sb->mingoal + bp->goal*goalfac; + bp->goal= (float)pow(bp->goal, 4.0f); + } + else{ + /* hair roots are allways fixed fully to goal */ + bp->goal= 1.0f; + } + } + } + } + + bp= sb->bpoint; + curpoint=0; + for(a=0, pa=psys->particles; a<psys->totpart; a++, curpoint++, pa++) { + for(k=0; k<pa->totkey-1; k++,bs++,curpoint++) { + bs->v1=curpoint; + bs->v2=curpoint+1; + bs->strength= 1.0; + bs->order=1; + } + } + + build_bps_springlist(ob); /* scan for springs attached to bodypoints ONCE */ + /* insert *other second order* springs if desired */ + if(sb->secondspring > 0.0000001f) { + add_2nd_order_springs(ob,sb->secondspring*10.0); /* exploits the the first run of build_bps_springlist(ob);*/ + build_bps_springlist(ob); /* yes we need to do it again*/ + } + springs_from_particles(ob); /* write the 'rest'-lenght of the springs */ + if(ob->softflag & OB_SB_SELF) + calculate_collision_balls(ob); +} + /* copies softbody result back in object */ static void softbody_to_object(Object *ob, float (*vertexCos)[3], int numVerts, int local) { @@ -2877,117 +3069,94 @@ static void softbody_to_object(Object *ob, float (*vertexCos)[3], int numVerts, } } -/* return 1 if succesfully baked and applied step */ -static int softbody_baked_step(Object *ob, float framenr, float (*vertexCos)[3], int numVerts) +void softbody_clear_cache(Object *ob, float framenr) { - SoftBody *sb= ob->soft; - SBVertex *key0, *key1, *key2, *key3; - BodyPoint *bp; - float data[4], sfra, efra, cfra, dfra, fac; /* start, end, current, delta */ - int ofs1, a; - - /* precondition check */ - if(sb==NULL || sb->keys==NULL || sb->totkey==0) return 0; - /* so we got keys, but no bodypoints... even without simul we need it for the bake */ - if(sb->bpoint==NULL) sb->bpoint= MEM_callocN( sb->totpoint*sizeof(BodyPoint), "bodypoint"); - - /* convert cfra time to system time */ - sfra= (float)sb->sfra; - cfra= bsystem_time(ob, NULL, framenr, 0.0); - efra= (float)sb->efra; - dfra= (float)sb->interval; + SoftBody *sb = ob->soft; + ModifierData *md = ob->modifiers.first; + int stack_index = -1; + int a; - /* offset in keys array */ - ofs1= (int)floor( (cfra-sfra)/dfra ); + if(sb==NULL) return; - if(ofs1 < 0) { - key0=key1=key2=key3= *sb->keys; - } - else if(ofs1 >= sb->totkey-1) { - key0=key1=key2=key3= *(sb->keys+sb->totkey-1); - } + if(sb->particles) + stack_index = modifiers_indexInObject(ob,(ModifierData*)psys_get_modifier(ob,sb->particles)); else { - key1= *(sb->keys+ofs1); - key2= *(sb->keys+ofs1+1); - - if(ofs1>0) key0= *(sb->keys+ofs1-1); - else key0= key1; - - if(ofs1<sb->totkey-2) key3= *(sb->keys+ofs1+2); - else key3= key2; - } - - sb->ctime= cfra; /* needed? */ - - /* timing */ - fac= ((cfra-sfra)/dfra) - (float)ofs1; - CLAMP(fac, 0.0, 1.0); - set_four_ipo(fac, data, KEY_BSPLINE); - if (key0&&key1&&key2&&key3) // may be null because we SHIFT_ESCAPED - for(a=sb->totpoint, bp= sb->bpoint; a>0; a--, bp++, key0++, key1++, key2++, key3++) { - bp->pos[0]= data[0]*key0->vec[0] + data[1]*key1->vec[0] + data[2]*key2->vec[0] + data[3]*key3->vec[0]; - bp->pos[1]= data[0]*key0->vec[1] + data[1]*key1->vec[1] + data[2]*key2->vec[1] + data[3]*key3->vec[1]; - bp->pos[2]= data[0]*key0->vec[2] + data[1]*key1->vec[2] + data[2]*key2->vec[2] + data[3]*key3->vec[2]; + for(a=0; md; md=md->next, a++) { + if(md->type == eModifierType_Softbody) { + stack_index = a; + break; + } + } } - - softbody_to_object(ob, vertexCos, numVerts, sb->local); - - return 1; -} -/* only gets called after succesfully doing softbody_step */ -/* already checked for OB_SB_BAKE flag */ -static void softbody_baked_add(Object *ob, float framenr) + BKE_ptcache_id_clear((ID *)ob, PTCACHE_CLEAR_ALL, framenr, stack_index); +} +static void softbody_write_cache(Object *ob, float framenr) { - SoftBody *sb= ob->soft; - SBVertex *key; + FILE *fp = NULL; + SoftBody *sb = ob->soft; BodyPoint *bp; - float sfra, efra, cfra, dfra, fac1; /* start, end, current, delta */ - int ofs1, a; - - /* convert cfra time to system time */ - sfra= (float)sb->sfra; - fac1= ob->sf; ob->sf= 0.0f; /* disable startframe */ - cfra= bsystem_time(ob, NULL, framenr, 0.0); - ob->sf= fac1; - efra= (float)sb->efra; - dfra= (float)sb->interval; - - if(sb->totkey==0) { - if(sb->sfra >= sb->efra) return; /* safety, UI or py setting allows */ - if(sb->interval<1) sb->interval= 1; /* just be sure */ - - sb->totkey= 1 + (int)(ceil( (efra-sfra)/dfra ) ); - sb->keys= MEM_callocN( sizeof(void *)*sb->totkey, "sb keys"); + ModifierData *md = ob->modifiers.first; + int stack_index = -1; + int a; + + if(sb->totpoint == 0) return; + + if(sb->particles) + stack_index = modifiers_indexInObject(ob,(ModifierData*)psys_get_modifier(ob,sb->particles)); + else { + for(a=0; md; md=md->next, a++) { + if(md->type == eModifierType_Softbody) { + stack_index = a; + break; + } + } } + + fp = BKE_ptcache_id_fopen((ID *)ob, 'w', framenr, stack_index); + if(!fp) return; + + for(a=0, bp=sb->bpoint; a<sb->totpoint; a++, bp++) + fwrite(&bp->pos, sizeof(float), 3, fp); - /* inverse matrix might not be uptodate... */ - Mat4Invert(ob->imat, ob->obmat); - - /* now find out if we have to store a key */ - - /* offset in keys array */ - if(cfra>=(efra)) { - ofs1= sb->totkey-1; - fac1= 0.0; - } + fclose(fp); +} +static int softbody_read_cache(Object *ob, float framenr) +{ + FILE *fp = NULL; + SoftBody *sb = ob->soft; + BodyPoint *bp; + ModifierData *md = ob->modifiers.first; + int stack_index = -1; + int a, ret = 1; + + if(sb->totpoint == 0) return 0; + + if(sb->particles) + stack_index = modifiers_indexInObject(ob,(ModifierData*)psys_get_modifier(ob,sb->particles)); else { - ofs1= (int)floor( (cfra-sfra)/dfra ); - fac1= ((cfra-sfra)/dfra) - (float)ofs1; - } - if( fac1 < 1.0/dfra ) { - - key= *(sb->keys+ofs1); - if(key == NULL) { - *(sb->keys+ofs1)= key= MEM_mallocN(sb->totpoint*sizeof(SBVertex), "softbody key"); - - for(a=sb->totpoint, bp= sb->bpoint; a>0; a--, bp++, key++) { - VECCOPY(key->vec, bp->pos); - if(sb->local) - Mat4MulVecfl(ob->imat, key->vec); + for(a=0; md; md=md->next, a++) { + if(md->type == eModifierType_Softbody) { + stack_index = a; + break; } } } + + fp = BKE_ptcache_id_fopen((ID *)ob, 'r', framenr, stack_index); + if(!fp) + ret = 0; + else { + for(a=0, bp=sb->bpoint; a<sb->totpoint; a++, bp++) + if(fread(&bp->pos, sizeof(float), 3, fp) != 3) { + ret = 0; + break; + } + + fclose(fp); + } + + return ret; } /* +++ ************ maintaining scratch *************** */ void sb_new_scratch(SoftBody *sb) @@ -3035,6 +3204,11 @@ SoftBody *sbNew(void) sb->balldamp = 0.50f; sb->ballstiff= 1.0f; sb->sbc_mode = 1; + + + sb->minloops = 10; + + sb->choke = 3; sb_new_scratch(sb); return sb; } @@ -3078,15 +3252,14 @@ void sbSetInterruptCallBack(int (*f)(void)) /* simulates one step. framenr is in frames */ void sbObjectStep(Object *ob, float framenr, float (*vertexCos)[3], int numVerts) { + ParticleSystemModifierData *psmd=0; + ParticleData *pa=0; SoftBody *sb; + HairKey *key= NULL; BodyPoint *bp; int a; float dtime,ctime,forcetime,err; - - /* baking works with global time */ - if(!(ob->softflag & OB_SB_BAKEDO) ) - if(softbody_baked_step(ob, framenr, vertexCos, numVerts) ) return; - + float hairmat[4][4]; /* This part only sets goals and springs, based on original mesh/curve/lattice data. Copying coordinates happens in next chunk by setting softbody flag OB_SB_RESET */ @@ -3097,7 +3270,13 @@ void sbObjectStep(Object *ob, float framenr, float (*vertexCos)[3], int numVerts (numVerts!=ob->soft->totpoint) || /* should never happen, just to be safe */ ((ob->softflag & OB_SB_EDGES) && !ob->soft->bspring && object_has_edges(ob))) /* happens when in UI edges was set */ { - switch(ob->type) { + if(ob->soft && ob->soft->bpoint) /* don't clear on file load */ + softbody_clear_cache(ob, framenr); + + if(ob->soft->particles){ + particles_to_softbody(ob); + } + else switch(ob->type) { case OB_MESH: mesh_to_softbody(ob); break; @@ -3123,10 +3302,14 @@ void sbObjectStep(Object *ob, float framenr, float (*vertexCos)[3], int numVerts /* still no points? go away */ if(sb->totpoint==0) return; + if(sb->particles){ + psmd=psys_get_modifier(ob,sb->particles); + pa=sb->particles->particles; + } /* checking time: */ - ctime= bsystem_time(ob, NULL, framenr, 0.0); + ctime= bsystem_time(ob, framenr, 0.0); if (ob->softflag&OB_SB_RESET) { dtime = 0.0; @@ -3134,17 +3317,45 @@ void sbObjectStep(Object *ob, float framenr, float (*vertexCos)[3], int numVerts dtime= ctime - sb->ctime; } + if(softbody_read_cache(ob, framenr)) { + if(sb->particles==0) + softbody_to_object(ob, vertexCos, numVerts, sb->local); + sb->ctime = ctime; + return; + } + /* the simulator */ /* update the vertex locations */ if (dtime!=0.0) { + if(sb->particles) { + pa=sb->particles->particles; + key = pa->hair; + + psys_mat_hair_to_global(ob, psmd->dm, sb->particles->part->from, pa, hairmat); + } + for(a=0,bp=sb->bpoint; a<numVerts; a++, bp++) { /* store where goals are now */ VECCOPY(bp->origS, bp->origE); /* copy the position of the goals at desired end time */ - VECCOPY(bp->origE, vertexCos[a]); - /* vertexCos came from local world, go global */ - Mat4MulVecfl(ob->obmat, bp->origE); + if(sb->particles) { + if(key == pa->hair + pa->totkey) { + pa++; + key = pa->hair; + + psys_mat_hair_to_global(ob, psmd->dm, sb->particles->part->from, pa, hairmat); + } + VECCOPY(bp->origE, key->co); + Mat4MulVecfl(hairmat,bp->origE); + + key++; + } + else{ + VECCOPY(bp->origE, vertexCos[a]); + /* vertexCos came from local world, go global */ + Mat4MulVecfl(ob->obmat, bp->origE); + } /* just to be save give bp->origT a defined value will be calulated in interpolate_exciter()*/ VECCOPY(bp->origT, bp->origE); @@ -3156,9 +3367,29 @@ void sbObjectStep(Object *ob, float framenr, float (*vertexCos)[3], int numVerts (dtime>=9.9*G.scene->r.framelen) /* too far forward in time --> goals won't be accurate enough */ ) { + if(sb->particles) { + pa=sb->particles->particles; + key = pa->hair; + + psys_mat_hair_to_global(ob, psmd->dm, sb->particles->part->from, pa, hairmat); + } + for(a=0,bp=sb->bpoint; a<numVerts; a++, bp++) { - VECCOPY(bp->pos, vertexCos[a]); - Mat4MulVecfl(ob->obmat, bp->pos); /* yep, sofbody is global coords*/ + if(sb->particles) { + if(key == pa->hair + pa->totkey) { + pa++; + key = pa->hair; + + psys_mat_hair_to_global(ob, psmd->dm, sb->particles->part->from, pa, hairmat); + } + VECCOPY(bp->pos, key->co); + Mat4MulVecfl(hairmat, bp->pos); + key++; + } + else { + VECCOPY(bp->pos, vertexCos[a]); + Mat4MulVecfl(ob->obmat, bp->pos); /* yep, sofbody is global coords*/ + } VECCOPY(bp->origS, bp->pos); VECCOPY(bp->origE, bp->pos); VECCOPY(bp->origT, bp->pos); @@ -3186,23 +3417,22 @@ void sbObjectStep(Object *ob, float framenr, float (*vertexCos)[3], int numVerts sb_new_scratch(sb); /* make a new */ sb->scratch->needstobuildcollider=1; - - /* copy some info to scratch */ - switch(ob->type) { - case OB_MESH: - if (ob->softflag & OB_SB_FACECOLL) mesh_faces_to_scratch(ob); - break; - case OB_LATTICE: - break; - case OB_CURVE: - case OB_SURF: - break; - default: - break; + if((sb->particles)==0) { + /* copy some info to scratch */ + switch(ob->type) { + case OB_MESH: + if (ob->softflag & OB_SB_FACECOLL) mesh_faces_to_scratch(ob); + break; + case OB_LATTICE: + break; + case OB_CURVE: + case OB_SURF: + break; + default: + break; + } } - - ob->softflag &= ~OB_SB_RESET; } else if(dtime>0.0) { @@ -3244,9 +3474,12 @@ void sbObjectStep(Object *ob, float framenr, float (*vertexCos)[3], int numVerts /* do predictive euler step */ softbody_calc_forces(ob, forcetime,timedone/dtime); softbody_apply_forces(ob, forcetime, 1, NULL); + + /* crop new slope values to do averaged slope step */ softbody_calc_forces(ob, forcetime,timedone/dtime); softbody_apply_forces(ob, forcetime, 2, &err); + softbody_apply_goalsnap(ob); if (err > SoftHeunTol) { /* error needs to be scaled to some quantity */ @@ -3311,10 +3544,10 @@ void sbObjectStep(Object *ob, float framenr, float (*vertexCos)[3], int numVerts } } - softbody_to_object(ob, vertexCos, numVerts, 0); + if(sb->particles==0) + softbody_to_object(ob, vertexCos, numVerts, 0); sb->ctime= ctime; - - if(ob->softflag & OB_SB_BAKEDO) softbody_baked_add(ob, framenr); + softbody_write_cache(ob, framenr); } diff --git a/source/blender/blenkernel/intern/subsurf_ccg.c b/source/blender/blenkernel/intern/subsurf_ccg.c index 7f3c5ddf8a7..0b1563beced 100644 --- a/source/blender/blenkernel/intern/subsurf_ccg.c +++ b/source/blender/blenkernel/intern/subsurf_ccg.c @@ -75,8 +75,6 @@ struct CCGDerivedMesh { CCGSubSurf *ss; int drawInteriorEdges, useSubsurfUv; - Mesh *me; - struct {int startVert; CCGVert *vert;} *vertMap; struct {int startVert; int startEdge; CCGEdge *edge;} *edgeMap; struct {int startVert; int startEdge; @@ -335,7 +333,6 @@ static void set_subsurf_uv(CCGSubSurf *ss, DerivedMesh *dm, DerivedMesh *result, MTFace *dmtface = CustomData_get_layer_n(&dm->faceData, CD_MTFACE, n); MTFace *tface = CustomData_get_layer_n(&result->faceData, CD_MTFACE, n); - if(!dmtface || !tface) return; @@ -1095,7 +1092,7 @@ static void ccgDM_getFinalEdge(DerivedMesh *dm, int edgeNum, MEdge *med) /* this vert comes from edge data */ CCGEdge *e; int edgeSize = ccgSubSurf_getEdgeSize(ss); - int x; + int x, *edgeFlag; unsigned int flags = 0; i = (edgeNum - ccgdm->edgeMap[0].startEdge) / (edgeSize - 1); @@ -1109,24 +1106,12 @@ static void ccgDM_getFinalEdge(DerivedMesh *dm, int edgeNum, MEdge *med) med->v1 = getEdgeIndex(ss, e, x, edgeSize); med->v2 = getEdgeIndex(ss, e, x+1, edgeSize); - if(ccgdm->me) { - int edgeIdx = (int) ccgSubSurf_getEdgeEdgeHandle(ss, e); - - if(edgeIdx!=-1) { - MEdge *medge = ccgdm->me->medge; - MEdge *origMed = &medge[edgeIdx]; - - flags |= (origMed->flag & (ME_SEAM | ME_SHARP)) - | ME_EDGEDRAW | ME_EDGERENDER; - } - } else { - int *edgeFlag = dm->getEdgeData(dm, edgeNum, CD_FLAGS); - if(edgeFlag) - flags |= (*edgeFlag & (ME_SEAM | ME_SHARP)) - | ME_EDGEDRAW | ME_EDGERENDER; - else - flags |= ME_EDGEDRAW | ME_EDGERENDER; - } + edgeFlag = dm->getEdgeData(dm, edgeNum, CD_FLAGS); + if(edgeFlag) + flags |= (*edgeFlag & (ME_SEAM | ME_SHARP)) + | ME_EDGEDRAW | ME_EDGERENDER; + else + flags |= ME_EDGEDRAW | ME_EDGERENDER; med->flag = flags; } @@ -1147,7 +1132,7 @@ static void ccgDM_getFinalFace(DerivedMesh *dm, int faceNum, MFace *mf) int grid; int x, y; int lastface = ccgSubSurf_getNumFaces(ss) - 1; - int *faceFlags = dm->getFaceDataArray(dm, CD_FLAGS); + char *faceFlags = dm->getFaceDataArray(dm, CD_FLAGS); memset(mf, 0, sizeof(*mf)); @@ -1169,7 +1154,7 @@ static void ccgDM_getFinalFace(DerivedMesh *dm, int faceNum, MFace *mf) mf->v3 = getFaceIndex(ss, f, grid, x+1, y+1, edgeSize, gridSize); mf->v4 = getFaceIndex(ss, f, grid, x+1, y+0, edgeSize, gridSize); - if(faceFlags) mf->flag = faceFlags[i]; + if(faceFlags) mf->flag = faceFlags[i*4]; else mf->flag = ME_SMOOTH; } @@ -1236,11 +1221,8 @@ static void ccgDM_copyFinalEdgeArray(DerivedMesh *dm, MEdge *medge) int gridSize = ccgSubSurf_getGridSize(ss); int edgeSize = ccgSubSurf_getEdgeSize(ss); int i = 0; - MEdge *origEdges = NULL; int *edgeFlags = dm->getEdgeDataArray(dm, CD_FLAGS); - if(ccgdm->me) origEdges = ccgdm->me->medge; - totface = ccgSubSurf_getNumFaces(ss); for(index = 0; index < totface; index++) { CCGFace *f = ccgdm->faceMap[index].face; @@ -1292,14 +1274,7 @@ static void ccgDM_copyFinalEdgeArray(DerivedMesh *dm, MEdge *medge) if(!ccgSubSurf_getEdgeNumFaces(ss, e)) flags |= ME_LOOSEEDGE; - if(origEdges){ - if(edgeIdx != -1) { - MEdge *origMed = &origEdges[edgeIdx]; - - flags |= (origMed->flag & (ME_SEAM | ME_SHARP)) - | ME_EDGEDRAW | ME_EDGERENDER; - } - } else if(edgeFlags) { + if(edgeFlags) { if(edgeIdx != -1) { flags |= (edgeFlags[i] & (ME_SEAM | ME_SHARP)) | ME_EDGEDRAW | ME_EDGERENDER; @@ -1327,10 +1302,7 @@ static void ccgDM_copyFinalFaceArray(DerivedMesh *dm, MFace *mface) int gridSize = ccgSubSurf_getGridSize(ss); int edgeSize = ccgSubSurf_getEdgeSize(ss); int i = 0; - MFace *origFaces = NULL; - int *faceFlags = dm->getFaceDataArray(dm, CD_FLAGS); - - if(ccgdm->me) origFaces = ccgdm->me->mface; + char *faceFlags = dm->getFaceDataArray(dm, CD_FLAGS); totface = ccgSubSurf_getNumFaces(ss); for(index = 0; index < totface; index++) { @@ -1339,16 +1311,6 @@ static void ccgDM_copyFinalFaceArray(DerivedMesh *dm, MFace *mface) int mat_nr = 0; int flag = ME_SMOOTH; /* assume face is smooth by default */ - if(!faceFlags) { - if(origFaces) { - int origIdx = (int) ccgSubSurf_getFaceFaceHandle(ss, f); - MFace *origMFace = &origFaces[origIdx]; - - mat_nr = origMFace->mat_nr; - flag = origMFace->flag; - } - } - for(S = 0; S < numVerts; S++) { for(y = 0; y < gridSize - 1; y++) { for(x = 0; x < gridSize - 1; x++) { @@ -1362,7 +1324,7 @@ static void ccgDM_copyFinalFaceArray(DerivedMesh *dm, MFace *mface) mf->v4 = getFaceIndex(ss, f, S, x + 1, y + 0, edgeSize, gridSize); mf->mat_nr = mat_nr; - if(faceFlags) mf->flag = faceFlags[index]; + if(faceFlags) mf->flag = faceFlags[index*4]; else mf->flag = flag; i++; @@ -1623,28 +1585,51 @@ static void ccgDM_drawLooseEdges(DerivedMesh *dm) { ccgEdgeIterator_free(ei); } +static void ccgDM_glNormalFast(float *a, float *b, float *c, float *d) +{ + float a_cX = c[0]-a[0], a_cY = c[1]-a[1], a_cZ = c[2]-a[2]; + float b_dX = d[0]-b[0], b_dY = d[1]-b[1], b_dZ = d[2]-b[2]; + float no[3]; + + no[0] = b_dY*a_cZ - b_dZ*a_cY; + no[1] = b_dZ*a_cX - b_dX*a_cZ; + no[2] = b_dX*a_cY - b_dY*a_cX; + + /* don't normalize, GL_NORMALIZE is be enabled */ + glNormal3fv(no); +} + /* Only used by non-editmesh types */ static void ccgDM_drawFacesSolid(DerivedMesh *dm, int (*setMaterial)(int)) { CCGDerivedMesh *ccgdm = (CCGDerivedMesh*) dm; CCGSubSurf *ss = ccgdm->ss; CCGFaceIterator *fi = ccgSubSurf_getFaceIterator(ss); int gridSize = ccgSubSurf_getGridSize(ss); - MFace *mface = ccgdm->me->mface; + char *faceFlags = DM_get_face_data_layer(dm, CD_FLAGS); for (; !ccgFaceIterator_isStopped(fi); ccgFaceIterator_next(fi)) { CCGFace *f = ccgFaceIterator_getCurrent(fi); int S, x, y, numVerts = ccgSubSurf_getFaceNumVerts(ss, f); int index = (int) ccgSubSurf_getFaceFaceHandle(ss, f); - MFace *mf = &mface[index]; + int drawSmooth, mat_nr; + + if(faceFlags) { + drawSmooth = (faceFlags[index*4] & ME_SMOOTH); + mat_nr= faceFlags[index*4 + 1]; + } + else { + drawSmooth = 1; + mat_nr= 0; + } - if (!setMaterial(mf->mat_nr+1)) + if (!setMaterial(mat_nr+1)) continue; - glShadeModel((mf->flag&ME_SMOOTH)?GL_SMOOTH:GL_FLAT); + glShadeModel(drawSmooth? GL_SMOOTH: GL_FLAT); for (S=0; S<numVerts; S++) { VertData *faceGridData = ccgSubSurf_getFaceGridDataArray(ss, f, S); - if (mf->flag&ME_SMOOTH) { + if (drawSmooth) { for (y=0; y<gridSize-1; y++) { glBegin(GL_QUAD_STRIP); for (x=0; x<gridSize; x++) { @@ -1666,14 +1651,8 @@ static void ccgDM_drawFacesSolid(DerivedMesh *dm, int (*setMaterial)(int)) { float *b = faceGridData[(y+0)*gridSize + x + 1].co; float *c = faceGridData[(y+1)*gridSize + x + 1].co; float *d = faceGridData[(y+1)*gridSize + x].co; - float a_cX = c[0]-a[0], a_cY = c[1]-a[1], a_cZ = c[2]-a[2]; - float b_dX = d[0]-b[0], b_dY = d[1]-b[1], b_dZ = d[2]-b[2]; - float no[3]; - no[0] = b_dY*a_cZ - b_dZ*a_cY; - no[1] = b_dZ*a_cX - b_dX*a_cZ; - no[2] = b_dX*a_cY - b_dY*a_cX; - glNormal3fv(no); + ccgDM_glNormalFast(a, b, c, d); glVertex3fv(d); glVertex3fv(c); @@ -1753,132 +1732,188 @@ static void ccgDM_drawFacesColored(DerivedMesh *dm, int useTwoSided, unsigned ch ccgFaceIterator_free(fi); } -static void ccgDM_drawFacesTex(DerivedMesh *dm, int (*setDrawParams)(MTFace *tface, MCol *mcol, int matnr)) -{ - /* unimplemented, no textures in editmode anyway */ -} -static void ccgDM_drawMappedFacesTex(DerivedMesh *dm, int (*setDrawParams)(void *userData, int index), void *userData) +static void ccgDM_drawFacesTex_common(DerivedMesh *dm, + int (*drawParams)(MTFace *tface, MCol *mcol, int matnr), + int (*drawParamsMapped)(void *userData, int index), + void *userData) { - /* unfinished code, no textures in editmode anyway */ - CCGDerivedMesh *ccgdm = (CCGDerivedMesh*) dm; CCGSubSurf *ss = ccgdm->ss; - CCGFaceIterator *fi = ccgSubSurf_getFaceIterator(ss); - int gridSize = ccgSubSurf_getGridSize(ss); - MFace *mface = ccgdm->me->mface; - MCol *mcol = ccgdm->me->mcol; -// float uv[4][2]; -// float col[4][3]; + MCol *mcol = DM_get_face_data_layer(dm, CD_MCOL); + MTFace *tf = DM_get_face_data_layer(dm, CD_MTFACE); + char *faceFlags = DM_get_face_data_layer(dm, CD_FLAGS); + int i, totface, flag, gridSize = ccgSubSurf_getGridSize(ss); + int gridFaces = gridSize - 1; - glBegin(GL_QUADS); - for (; !ccgFaceIterator_isStopped(fi); ccgFaceIterator_next(fi)) { - CCGFace *f = ccgFaceIterator_getCurrent(fi); + totface = ccgSubSurf_getNumFaces(ss); + for(i = 0; i < totface; i++) { + CCGFace *f = ccgdm->faceMap[i].face; int S, x, y, numVerts = ccgSubSurf_getFaceNumVerts(ss, f); - int index = (int) ccgSubSurf_getFaceFaceHandle(ss, f); - MFace *mf = &mface[index]; + int drawSmooth, index = ccgDM_getFaceMapIndex(ccgdm, ss, f); + int origIndex = (int)ccgSubSurf_getFaceFaceHandle(ss, f); unsigned char *cp= NULL; - int findex = ccgDM_getFaceMapIndex(ccgdm, ss, f); - int flag = (findex == -1)? 0: setDrawParams(userData, findex); + int mat_nr; + + if(faceFlags) { + drawSmooth = (faceFlags[origIndex*4] & ME_SMOOTH); + mat_nr= faceFlags[origIndex*4 + 1]; + } + else { + drawSmooth = 1; + mat_nr= 0; + } - if (flag==0) + if(drawParams) + flag = drawParams(tf, mcol, mat_nr); + else + flag= (drawParamsMapped)? drawParamsMapped(userData, index): 1; + + if (flag == 0) { /* flag 0 == the face is hidden or invisible */ + if(tf) tf += gridFaces*gridFaces*numVerts; + if(mcol) mcol += gridFaces*gridFaces*numVerts*4; continue; - else if (flag==1 && mcol) - cp= (unsigned char*) &mcol[index*4]; + } + + /* flag 1 == use vertex colors */ + if(mcol) { + if(flag==1) cp= (unsigned char*)mcol; + mcol += gridFaces*gridFaces*numVerts*4; + } for (S=0; S<numVerts; S++) { VertData *faceGridData = ccgSubSurf_getFaceGridDataArray(ss, f, S); - for (y=0; y<gridSize-1; y++) { - for (x=0; x<gridSize-1; x++) { - VertData *a = &faceGridData[(y+0)*gridSize + x + 0]; - VertData *b = &faceGridData[(y+0)*gridSize + x + 1]; - VertData *c = &faceGridData[(y+1)*gridSize + x + 1]; - VertData *d = &faceGridData[(y+1)*gridSize + x + 0]; + VertData *a, *b; - if (!(mf->flag&ME_SMOOTH)) { - float a_cX = c->co[0]-a->co[0], a_cY = c->co[1]-a->co[1], a_cZ = c->co[2]-a->co[2]; - float b_dX = d->co[0]-b->co[0], b_dY = d->co[1]-b->co[1], b_dZ = d->co[2]-b->co[2]; - float no[3]; + if (drawSmooth) { + glShadeModel(GL_SMOOTH); + for (y=0; y<gridFaces; y++) { + glBegin(GL_QUAD_STRIP); + for (x=0; x<gridFaces; x++) { + a = &faceGridData[(y+0)*gridSize + x]; + b = &faceGridData[(y+1)*gridSize + x]; - no[0] = b_dY*a_cZ - b_dZ*a_cY; - no[1] = b_dZ*a_cX - b_dX*a_cZ; - no[2] = b_dX*a_cY - b_dY*a_cX; + if(tf) glTexCoord2fv(tf->uv[0]); + if(cp) glColor3ub(cp[3], cp[2], cp[1]); + glNormal3fv(a->no); + glVertex3fv(a->co); - glNormal3fv(no); + if(tf) glTexCoord2fv(tf->uv[1]); + if(cp) glColor3ub(cp[7], cp[6], cp[5]); + glNormal3fv(b->no); + glVertex3fv(b->co); + + if(x != gridFaces-1) { + if(tf) tf++; + if(cp) cp += 16; + } } -// if (tf) glTexCoord2fv(tf->uv[0]); -// if (cp) glColor3ub(cp[3], cp[2], cp[1]); -// if (mf->flag&ME_SMOOTH) glNormal3sv(mvert[mf->v1].no); -// glVertex3fv(mvert[mf->v1].co); + a = &faceGridData[(y+0)*gridSize + x]; + b = &faceGridData[(y+1)*gridSize + x]; -/* - { - float x_v = (float) fx/(gridSize-1); - float y_v = (float) fy/(gridSize-1); - float data[6]; + if(tf) glTexCoord2fv(tf->uv[3]); + if(cp) glColor3ub(cp[15], cp[14], cp[13]); + glNormal3fv(a->no); + glVertex3fv(a->co); - for (k=0; k<numDataComponents; k++) { - data[k] = (center_data[k]*(1.0f-x_v) + edge_data[S][k]*x_v)*(1.0f-y_v) + - (edge_data[prevS][k]*(1.0f-x_v) + corner_data[S][k]*x_v)*y_v; - } -*/ - -// if (cp) glColor3ub(cp[3], cp[2], cp[1]); - if (mf->flag&ME_SMOOTH) glNormal3fv(d->no); - glVertex3fv(d->co); -// if (cp) glColor3ub(cp[7], cp[6], cp[5]); - if (mf->flag&ME_SMOOTH) glNormal3fv(c->no); - glVertex3fv(c->co); -// if (cp) glColor3ub(cp[11], cp[10], cp[9]); - if (mf->flag&ME_SMOOTH) glNormal3fv(b->no); + if(tf) glTexCoord2fv(tf->uv[2]); + if(cp) glColor3ub(cp[11], cp[10], cp[9]); + glNormal3fv(b->no); glVertex3fv(b->co); -// if (cp) glColor3ub(cp[15], cp[14], cp[13]); - if (mf->flag&ME_SMOOTH) glNormal3fv(a->no); - glVertex3fv(a->co); + + if(tf) tf++; + if(cp) cp += 16; + + glEnd(); + } + } else { + glShadeModel(GL_FLAT); + glBegin(GL_QUADS); + for (y=0; y<gridFaces; y++) { + for (x=0; x<gridFaces; x++) { + float *a = faceGridData[(y+0)*gridSize + x].co; + float *b = faceGridData[(y+0)*gridSize + x + 1].co; + float *c = faceGridData[(y+1)*gridSize + x + 1].co; + float *d = faceGridData[(y+1)*gridSize + x].co; + + ccgDM_glNormalFast(a, b, c, d); + + if(tf) glTexCoord2fv(tf->uv[1]); + if(cp) glColor3ub(cp[7], cp[6], cp[5]); + glVertex3fv(d); + + if(tf) glTexCoord2fv(tf->uv[2]); + if(cp) glColor3ub(cp[11], cp[10], cp[9]); + glVertex3fv(c); + + if(tf) glTexCoord2fv(tf->uv[3]); + if(cp) glColor3ub(cp[15], cp[14], cp[13]); + glVertex3fv(b); + + if(tf) glTexCoord2fv(tf->uv[0]); + if(cp) glColor3ub(cp[3], cp[2], cp[1]); + glVertex3fv(a); + + if(tf) tf++; + if(cp) cp += 16; + } } + glEnd(); } } } - glEnd(); +} - ccgFaceIterator_free(fi); -/* - MeshDerivedMesh *mdm = (MeshDerivedMesh*) dm; - Mesh *me = mdm->me; - MVert *mvert= mdm->verts; - MFace *mface= me->mface; - MTFace *tface = me->mtface; - float *nors = mdm->nors; - int a; - - for (a=0; a<me->totface; a++) { - MFace *mf= &mface[a]; - if (tf) glTexCoord2fv(tf->uv[1]); - if (cp) glColor3ub(cp[7], cp[6], cp[5]); - if (mf->flag&ME_SMOOTH) glNormal3sv(mvert[mf->v2].no); - glVertex3fv(mvert[mf->v2].co); - - if (tf) glTexCoord2fv(tf->uv[2]); - if (cp) glColor3ub(cp[11], cp[10], cp[9]); - if (mf->flag&ME_SMOOTH) glNormal3sv(mvert[mf->v3].no); - glVertex3fv(mvert[mf->v3].co); - - if(mf->v4) { - if (tf) glTexCoord2fv(tf->uv[3]); - if (cp) glColor3ub(cp[15], cp[14], cp[13]); - if (mf->flag&ME_SMOOTH) glNormal3sv(mvert[mf->v4].no); - glVertex3fv(mvert[mf->v4].co); +static void ccgDM_drawFacesTex(DerivedMesh *dm, int (*setDrawOptions)(MTFace *tface, MCol *mcol, int matnr)) +{ + ccgDM_drawFacesTex_common(dm, setDrawOptions, NULL, NULL); +} + +static void ccgDM_drawMappedFacesTex(DerivedMesh *dm, int (*setDrawOptions)(void *userData, int index), void *userData) +{ + ccgDM_drawFacesTex_common(dm, NULL, setDrawOptions, userData); +} + +static void ccgDM_drawUVEdges(DerivedMesh *dm) +{ + + MFace *mf = dm->getFaceArray(dm); + MTFace *tf = DM_get_face_data_layer(dm, CD_MTFACE); + int i; + + if (tf) { + glBegin(GL_LINES); + for(i = 0; i < dm->numFaceData; i++, mf++, tf++) { + if(!(mf->flag&ME_HIDE)) { + glVertex2fv(tf->uv[0]); + glVertex2fv(tf->uv[1]); + + glVertex2fv(tf->uv[1]); + glVertex2fv(tf->uv[2]); + + if(!mf->v4) { + glVertex2fv(tf->uv[2]); + glVertex2fv(tf->uv[0]); + } else { + glVertex2fv(tf->uv[2]); + glVertex2fv(tf->uv[3]); + + glVertex2fv(tf->uv[3]); + glVertex2fv(tf->uv[0]); + } + } } glEnd(); } -*/ } + static void ccgDM_drawMappedFaces(DerivedMesh *dm, int (*setDrawOptions)(void *userData, int index, int *drawSmooth_r), void *userData, int useColors) { + GLubyte act_face_stipple[32*32/8] = DM_FACE_STIPPLE; CCGDerivedMesh *ccgdm = (CCGDerivedMesh*) dm; CCGSubSurf *ss = ccgdm->ss; CCGFaceIterator *fi = ccgSubSurf_getFaceIterator(ss); int i, gridSize = ccgSubSurf_getGridSize(ss); - int *faceFlags = dm->getFaceDataArray(dm, CD_FLAGS); + char *faceFlags = dm->getFaceDataArray(dm, CD_FLAGS); for (i=0; !ccgFaceIterator_isStopped(fi); i++,ccgFaceIterator_next(fi)) { CCGFace *f = ccgFaceIterator_getCurrent(fi); @@ -1888,53 +1923,65 @@ static void ccgDM_drawMappedFaces(DerivedMesh *dm, int (*setDrawOptions)(void *u origIndex = (int)ccgSubSurf_getFaceFaceHandle(ss, f); - if(faceFlags) drawSmooth = (faceFlags[origIndex] & ME_SMOOTH); + if(faceFlags) drawSmooth = (faceFlags[origIndex*4] & ME_SMOOTH); else drawSmooth = 1; - - if (index!=-1 && (!setDrawOptions || setDrawOptions(userData, index, &drawSmooth))) { - for (S=0; S<numVerts; S++) { - VertData *faceGridData = ccgSubSurf_getFaceGridDataArray(ss, f, S); - if (drawSmooth) { - glShadeModel(GL_SMOOTH); - for (y=0; y<gridSize-1; y++) { - glBegin(GL_QUAD_STRIP); - for (x=0; x<gridSize; x++) { - VertData *a = &faceGridData[(y+0)*gridSize + x]; - VertData *b = &faceGridData[(y+1)*gridSize + x]; - - glNormal3fv(a->no); - glVertex3fv(a->co); - glNormal3fv(b->no); - glVertex3fv(b->co); + + if (index!=-1) { + int draw; + draw = setDrawOptions==NULL ? 1 : setDrawOptions(userData, index, &drawSmooth); + + if (draw) { + if (draw==2) { + glEnable(GL_POLYGON_STIPPLE); + glPolygonStipple(act_face_stipple); + } + + for (S=0; S<numVerts; S++) { + VertData *faceGridData = ccgSubSurf_getFaceGridDataArray(ss, f, S); + if (drawSmooth) { + glShadeModel(GL_SMOOTH); + for (y=0; y<gridSize-1; y++) { + glBegin(GL_QUAD_STRIP); + for (x=0; x<gridSize; x++) { + VertData *a = &faceGridData[(y+0)*gridSize + x]; + VertData *b = &faceGridData[(y+1)*gridSize + x]; + + glNormal3fv(a->no); + glVertex3fv(a->co); + glNormal3fv(b->no); + glVertex3fv(b->co); + } + glEnd(); } - glEnd(); - } - } else { - glShadeModel(GL_FLAT); - glBegin(GL_QUADS); - for (y=0; y<gridSize-1; y++) { - for (x=0; x<gridSize-1; x++) { - float *a = faceGridData[(y+0)*gridSize + x].co; - float *b = faceGridData[(y+0)*gridSize + x + 1].co; - float *c = faceGridData[(y+1)*gridSize + x + 1].co; - float *d = faceGridData[(y+1)*gridSize + x].co; - float a_cX = c[0]-a[0], a_cY = c[1]-a[1], a_cZ = c[2]-a[2]; - float b_dX = d[0]-b[0], b_dY = d[1]-b[1], b_dZ = d[2]-b[2]; - float no[3]; - - no[0] = b_dY*a_cZ - b_dZ*a_cY; - no[1] = b_dZ*a_cX - b_dX*a_cZ; - no[2] = b_dX*a_cY - b_dY*a_cX; - glNormal3fv(no); - - glVertex3fv(d); - glVertex3fv(c); - glVertex3fv(b); - glVertex3fv(a); + } else { + glShadeModel(GL_FLAT); + glBegin(GL_QUADS); + for (y=0; y<gridSize-1; y++) { + for (x=0; x<gridSize-1; x++) { + float *a = faceGridData[(y+0)*gridSize + x].co; + float *b = faceGridData[(y+0)*gridSize + x + 1].co; + float *c = faceGridData[(y+1)*gridSize + x + 1].co; + float *d = faceGridData[(y+1)*gridSize + x].co; + float a_cX = c[0]-a[0], a_cY = c[1]-a[1], a_cZ = c[2]-a[2]; + float b_dX = d[0]-b[0], b_dY = d[1]-b[1], b_dZ = d[2]-b[2]; + float no[3]; + + no[0] = b_dY*a_cZ - b_dZ*a_cY; + no[1] = b_dZ*a_cX - b_dX*a_cZ; + no[2] = b_dX*a_cY - b_dY*a_cX; + glNormal3fv(no); + + glVertex3fv(d); + glVertex3fv(c); + glVertex3fv(b); + glVertex3fv(a); + } } + glEnd(); } - glEnd(); } + if (draw==2) + glDisable(GL_POLYGON_STIPPLE); } } } @@ -2035,7 +2082,7 @@ static void ccgDM_release(DerivedMesh *dm) { static CCGDerivedMesh *getCCGDerivedMesh(CCGSubSurf *ss, int drawInteriorEdges, - int useSubsurfUv, Mesh *me, + int useSubsurfUv, DerivedMesh *dm) { CCGDerivedMesh *ccgdm = MEM_callocN(sizeof(*ccgdm), "ccgdm"); @@ -2046,7 +2093,8 @@ static CCGDerivedMesh *getCCGDerivedMesh(CCGSubSurf *ss, int i; int vertNum, edgeNum, faceNum; int *vertOrigIndex, *edgeOrigIndex, *faceOrigIndex; - int *faceFlags, *edgeFlags; + int *edgeFlags; + char *faceFlags; int edgeSize; int gridSize; int gridFaces; @@ -2059,20 +2107,14 @@ static CCGDerivedMesh *getCCGDerivedMesh(CCGSubSurf *ss, MFace *mface = NULL; FaceVertWeight *qweight, *tweight; - if(dm) { - DM_from_template(&ccgdm->dm, dm, ccgSubSurf_getNumFinalVerts(ss), - ccgSubSurf_getNumFinalEdges(ss), - ccgSubSurf_getNumFinalFaces(ss)); - DM_add_face_layer(&ccgdm->dm, CD_FLAGS, CD_CALLOC, NULL); - DM_add_edge_layer(&ccgdm->dm, CD_FLAGS, CD_CALLOC, NULL); - - CustomData_set_layer_flag(&ccgdm->dm.faceData, CD_FLAGS, CD_FLAG_NOCOPY); - CustomData_set_layer_flag(&ccgdm->dm.edgeData, CD_FLAGS, CD_FLAG_NOCOPY); - } else { - DM_init(&ccgdm->dm, ccgSubSurf_getNumFinalVerts(ss), - ccgSubSurf_getNumFinalEdges(ss), - ccgSubSurf_getNumFinalFaces(ss)); - } + DM_from_template(&ccgdm->dm, dm, ccgSubSurf_getNumFinalVerts(ss), + ccgSubSurf_getNumFinalEdges(ss), + ccgSubSurf_getNumFinalFaces(ss)); + DM_add_face_layer(&ccgdm->dm, CD_FLAGS, CD_CALLOC, NULL); + DM_add_edge_layer(&ccgdm->dm, CD_FLAGS, CD_CALLOC, NULL); + + CustomData_set_layer_flag(&ccgdm->dm.faceData, CD_FLAGS, CD_FLAG_NOCOPY); + CustomData_set_layer_flag(&ccgdm->dm.edgeData, CD_FLAGS, CD_FLAG_NOCOPY); ccgdm->dm.getMinMax = ccgDM_getMinMax; ccgdm->dm.getNumVerts = ccgDM_getNumVerts; @@ -2105,6 +2147,7 @@ static CCGDerivedMesh *getCCGDerivedMesh(CCGSubSurf *ss, ccgdm->dm.drawFacesTex = ccgDM_drawFacesTex; ccgdm->dm.drawMappedFaces = ccgDM_drawMappedFaces; ccgdm->dm.drawMappedFacesTex = ccgDM_drawMappedFacesTex; + ccgdm->dm.drawUVEdges = ccgDM_drawUVEdges; ccgdm->dm.drawMappedEdgesInterp = ccgDM_drawMappedEdgesInterp; ccgdm->dm.drawMappedEdges = ccgDM_drawMappedEdges; @@ -2114,7 +2157,6 @@ static CCGDerivedMesh *getCCGDerivedMesh(CCGSubSurf *ss, ccgdm->ss = ss; ccgdm->drawInteriorEdges = drawInteriorEdges; ccgdm->useSubsurfUv = useSubsurfUv; - ccgdm->me = me; totvert = ccgSubSurf_getNumVerts(ss); ccgdm->vertMap = MEM_mallocN(totvert * sizeof(*ccgdm->vertMap), "vertMap"); @@ -2159,15 +2201,9 @@ static CCGDerivedMesh *getCCGDerivedMesh(CCGSubSurf *ss, edgeNum = 0; faceNum = 0; - if(dm) { - mvert = dm->getVertArray(dm); - medge = dm->getEdgeArray(dm); - mface = dm->getFaceArray(dm); - } else if(me) { - mvert = me->mvert; - medge = me->medge; - mface = me->mface; - } + mvert = dm->getVertArray(dm); + medge = dm->getEdgeArray(dm); + mface = dm->getFaceArray(dm); vertOrigIndex = DM_get_vert_data_layer(&ccgdm->dm, CD_ORIGINDEX); edgeOrigIndex = DM_get_edge_data_layer(&ccgdm->dm, CD_ORIGINDEX); @@ -2277,7 +2313,8 @@ static CCGDerivedMesh *getCCGDerivedMesh(CCGSubSurf *ss, } } - faceFlags[index] = mface[origIndex].flag; + faceFlags[index*4] = mface[origIndex].flag; + faceFlags[index*4 + 1] = mface[origIndex].mat_nr; edgeNum += numFinalEdges; } @@ -2381,7 +2418,7 @@ struct DerivedMesh *subsurf_make_derived_from_derived( return (DerivedMesh *)getCCGDerivedMesh(smd->emCache, drawInteriorEdges, - useSubsurfUv, NULL, dm); + useSubsurfUv, dm); } else if(useRenderParams) { /* Do not use cache in render mode. */ CCGSubSurf *ss = _getSubSurf(NULL, smd->renderLevels, 0, 1, useSimple); @@ -2418,8 +2455,13 @@ struct DerivedMesh *subsurf_make_derived_from_derived( ss_sync_from_derivedmesh(ss, dm, vertCos, useSimple); + return ss_to_cdderivedmesh(ss, 0, drawInteriorEdges, useSubsurfUv, dm); + + /*return (DerivedMesh *)getCCGDerivedMesh(smd->mCache, + drawInteriorEdges, + useSubsurfUv, dm);*/ } else { if (smd->mCache && isFinalCalc) { ccgSubSurf_free(smd->mCache); @@ -2429,6 +2471,11 @@ struct DerivedMesh *subsurf_make_derived_from_derived( ss = _getSubSurf(NULL, smd->levels, 0, 1, useSimple); ss_sync_from_derivedmesh(ss, dm, vertCos, useSimple); + /*smd->mCache = ss; + result = (DerivedMesh *)getCCGDerivedMesh(smd->mCache, + drawInteriorEdges, + useSubsurfUv, dm);*/ + result = ss_to_cdderivedmesh(ss, 0, drawInteriorEdges, useSubsurfUv, dm); @@ -2441,11 +2488,11 @@ struct DerivedMesh *subsurf_make_derived_from_derived( void subsurf_calculate_limit_positions(Mesh *me, float (*positions_r)[3]) { - /* Finds the subsurf limit positions for the verts in a mesh - * and puts them in an array of floats. Please note that the - * calculated vert positions is incorrect for the verts - * on the boundary of the mesh. - */ + /* Finds the subsurf limit positions for the verts in a mesh + * and puts them in an array of floats. Please note that the + * calculated vert positions is incorrect for the verts + * on the boundary of the mesh. + */ CCGSubSurf *ss = _getSubSurf(NULL, 1, 0, 1, 0); float edge_sum[3], face_sum[3]; CCGVertIterator *vi; @@ -2474,6 +2521,11 @@ void subsurf_calculate_limit_positions(Mesh *me, float (*positions_r)[3]) VecAddf(face_sum, face_sum, ccgSubSurf_getFaceCenterData(ss, f)); } + /* ad-hoc correction for boundary vertices, to at least avoid them + moving completely out of place (brecht) */ + if(numFaces && numFaces != N) + VecMulf(face_sum, (float)N/(float)numFaces); + co = ccgSubSurf_getVertData(ss, v); positions_r[idx][0] = (co[0]*N*N + edge_sum[0]*4 + face_sum[0])/(N*(N+5)); positions_r[idx][1] = (co[1]*N*N + edge_sum[1]*4 + face_sum[1])/(N*(N+5)); diff --git a/source/blender/blenkernel/intern/texture.c b/source/blender/blenkernel/intern/texture.c index bda933802ee..baa6fae6fc8 100644 --- a/source/blender/blenkernel/intern/texture.c +++ b/source/blender/blenkernel/intern/texture.c @@ -238,13 +238,10 @@ void init_mapping(TexMapping *texmap) /* ****************** COLORBAND ******************* */ -ColorBand *add_colorband(int rangetype) +void init_colorband(ColorBand *coba, int rangetype) { - ColorBand *coba; int a; - coba= MEM_callocN( sizeof(ColorBand), "colorband"); - coba->data[0].pos= 0.0; coba->data[1].pos= 1.0; @@ -281,6 +278,15 @@ ColorBand *add_colorband(int rangetype) coba->tot= 2; +} + +ColorBand *add_colorband(int rangetype) +{ + ColorBand *coba; + + coba= MEM_callocN( sizeof(ColorBand), "colorband"); + init_colorband(coba, rangetype); + return coba; } @@ -390,6 +396,7 @@ void free_texture(Tex *tex) free_plugin_tex(tex->plugin); if(tex->coba) MEM_freeN(tex->coba); if(tex->env) BKE_free_envmap(tex->env); + BKE_previewimg_free(&tex->preview); BKE_icon_delete((struct ID*)tex); tex->id.icon_id = 0; } @@ -404,7 +411,7 @@ void default_tex(Tex *tex) tex->stype= 0; tex->flag= TEX_CHECKER_ODD; - tex->imaflag= TEX_INTERPOL+TEX_MIPMAP; + tex->imaflag= TEX_INTERPOL+TEX_MIPMAP+TEX_USEALPHA; tex->extend= TEX_REPEAT; tex->cropxmin= tex->cropymin= 0.0; tex->cropxmax= tex->cropymax= 1.0; @@ -418,7 +425,8 @@ void default_tex(Tex *tex) tex->turbul= 5.0; tex->nabla= 0.025; // also in do_versions tex->bright= 1.0; - tex->contrast= tex->filtersize= 1.0; + tex->contrast= 1.0; + tex->filtersize= 1.0; tex->rfac= 1.0; tex->gfac= 1.0; tex->bfac= 1.0; @@ -462,6 +470,8 @@ void default_tex(Tex *tex) tex->iuser.fie_ima= 2; tex->iuser.ok= 1; tex->iuser.frames= 100; + + tex->preview = NULL; } /* ------------------------------------------------------------------------- */ @@ -541,6 +551,8 @@ Tex *copy_texture(Tex *tex) if(texn->coba) texn->coba= MEM_dupallocN(texn->coba); if(texn->env) texn->env= BKE_copy_envmap(texn->env); + if(tex->preview) texn->preview = BKE_previewimg_copy(tex->preview); + return texn; } diff --git a/source/blender/blenkernel/intern/verse_geometry_node.c b/source/blender/blenkernel/intern/verse_geometry_node.c index 8d58d140c79..613d4eadbec 100644 --- a/source/blender/blenkernel/intern/verse_geometry_node.c +++ b/source/blender/blenkernel/intern/verse_geometry_node.c @@ -141,6 +141,11 @@ static void recalculate_verseface_normals(VNode *vnode) */ void add_item_to_send_queue(ListBase *lb, void *item, short type) { + struct VNode *vnode; + struct VLayer *vlayer; + struct VerseVert *vvert; + struct VerseFace *vface; + /* this prevent from adding duplicated faces */ if(type==VERSE_FACE) { struct Link *link = (Link*)lb->first; @@ -188,35 +193,59 @@ void add_item_to_send_queue(ListBase *lb, void *item, short type) send_verse_taggroup((VTagGroup*)item); break; case VERSE_VERT_UINT32: /* parent item has to exist */ - if( ((verse_parent*)((uint32_item*)item)->parent)->id != -1) + vnode = (((uint32_item*)item)->vlayer)->vnode; + vlayer = (VLayer*)BLI_dlist_find_link(&(((VGeomData*)vnode->data)->layers), 0 ); + vvert = (VerseVert*)BLI_dlist_find_link(&(vlayer->dl), ((uint32_item*)item)->id ); + if(vvert != NULL) send_verse_vert_uint32((uint32_item*)item, type); break; case VERSE_VERT_REAL32: /* parent item has to exist */ - if( ((verse_parent*)((real32_item*)item)->parent)->id != -1) + vnode = (((real32_item*)item)->vlayer)->vnode; + vlayer = (VLayer*)BLI_dlist_find_link(&(((VGeomData*)vnode->data)->layers), 0 ); + vvert = (VerseVert*)BLI_dlist_find_link(&(vlayer->dl), ((real32_item*)item)->id ); + if( vvert != NULL) send_verse_vert_real32((real32_item*)item, type); break; case VERSE_VERT_VEC_REAL32: /* parent item has to exist */ - if( ((verse_parent*)((vec_real32_item*)item)->parent)->id != -1) + vnode = (((vec_real32_item*)item)->vlayer)->vnode; + vlayer = (VLayer*)BLI_dlist_find_link(&(((VGeomData*)vnode->data)->layers), 0 ); + vvert = (VerseVert*)BLI_dlist_find_link(&(vlayer->dl), ((vec_real32_item*)item)->id ); + if(vvert != NULL) send_verse_vert_vec_real32((vec_real32_item*)item, type); break; case VERSE_FACE_UINT8: /* parent item has to exist */ - if( ((verse_parent*)((uint8_item*)item)->parent)->id != -1) + vnode = (((uint8_item*)item)->vlayer)->vnode; + vlayer = (VLayer*)BLI_dlist_find_link(&(((VGeomData*)vnode->data)->layers), 1 ); + vface = (VerseFace*)BLI_dlist_find_link(&(vlayer->dl), ((uint8_item*)item)->id ); + if(vface != NULL) send_verse_face_uint8((uint8_item*)item, type); break; case VERSE_FACE_UINT32: /* parent item has to exist */ - if( ((verse_parent*)((uint32_item*)item)->parent)->id != -1) + vnode = (((uint32_item*)item)->vlayer)->vnode; + vlayer = (VLayer*)BLI_dlist_find_link(&(((VGeomData*)vnode->data)->layers), 1 ); + vface = (VerseFace*)BLI_dlist_find_link(&(vlayer->dl), ((uint32_item*)item)->id ); + if(vface != NULL) send_verse_face_uint32((uint32_item*)item, type); break; case VERSE_FACE_REAL32: /* parent item has to exist */ - if( ((verse_parent*)((real32_item*)item)->parent)->id != -1) + vnode = (((real32_item*)item)->vlayer)->vnode; + vlayer = (VLayer*)BLI_dlist_find_link(&(((VGeomData*)vnode->data)->layers), 1 ); + vface = (VerseFace*)BLI_dlist_find_link(&(vlayer->dl), ((real32_item*)item)->id ); + if(vface != NULL) send_verse_face_real32((real32_item*)item, type); break; case VERSE_FACE_QUAT_UINT32: /* parent item has to exist */ - if( ((verse_parent*)((quat_uint32_item*)item)->parent)->id != -1) + vnode = (((quat_uint32_item*)item)->vlayer)->vnode; + vlayer = (VLayer*)BLI_dlist_find_link(&(((VGeomData*)vnode->data)->layers), 1 ); + vface = (VerseFace*)BLI_dlist_find_link(&(vlayer->dl), ((quat_uint32_item*)item)->id ); + if(vface != NULL) send_verse_face_corner_quat_uint32((quat_uint32_item*)item, type); break; case VERSE_FACE_QUAT_REAL32: /* parent item has to exist */ - if( ((verse_parent*)((quat_real32_item*)item)->parent)->id != -1) + vnode = (((quat_real32_item*)item)->vlayer)->vnode; + vlayer = (VLayer*)BLI_dlist_find_link(&(((VGeomData*)vnode->data)->layers), 1 ); + vface = (VerseFace*)BLI_dlist_find_link(&(vlayer->dl), ((quat_real32_item*)item)->id ); + if(vface != NULL) send_verse_face_corner_quat_real32((quat_real32_item*)item, type); break; } @@ -479,12 +508,10 @@ static VerseVert* find_verse_vert_in_queue( */ void send_verse_face_corner_quat_real32(quat_real32_item *item, short type) { - struct VerseFace *vface = (VerseFace*)item->parent; - verse_send_g_polygon_set_corner_real32( - vface->vlayer->vnode->id, - item->layer_id, - vface->id, + item->vlayer->vnode->id, + item->vlayer->id, + item->id, item->value[0], item->value[1], item->value[2], @@ -496,12 +523,10 @@ void send_verse_face_corner_quat_real32(quat_real32_item *item, short type) */ void send_verse_face_corner_quat_uint32(quat_uint32_item *item, short type) { - struct VerseFace *vface = (VerseFace*)item->parent; - verse_send_g_polygon_set_corner_uint32( - vface->vlayer->vnode->id, - item->layer_id, - vface->id, + item->vlayer->vnode->id, + item->vlayer->id, + item->id, item->value[0], item->value[1], item->value[2], @@ -513,12 +538,10 @@ void send_verse_face_corner_quat_uint32(quat_uint32_item *item, short type) */ void send_verse_face_real32(real32_item *item, short type) { - struct VerseFace *vface = (VerseFace*)item->parent; - verse_send_g_polygon_set_face_real32( - vface->vlayer->vnode->id, - item->layer_id, - vface->id, + item->vlayer->vnode->id, + item->vlayer->id, + item->id, item->value); } @@ -527,12 +550,10 @@ void send_verse_face_real32(real32_item *item, short type) */ void send_verse_face_uint32(uint32_item *item, short type) { - struct VerseFace *vface = (VerseFace*)item->parent; - verse_send_g_polygon_set_face_uint32( - vface->vlayer->vnode->id, - item->layer_id, - vface->id, + item->vlayer->vnode->id, + item->vlayer->id, + item->id, item->value); } @@ -541,12 +562,10 @@ void send_verse_face_uint32(uint32_item *item, short type) */ void send_verse_face_uint8(uint8_item *item, short type) { - struct VerseFace *vface = (VerseFace*)item->parent; - verse_send_g_polygon_set_face_uint8( - vface->vlayer->vnode->id, - item->layer_id, - vface->id, + item->vlayer->vnode->id, + item->vlayer->id, + item->id, item->value); } @@ -555,12 +574,10 @@ void send_verse_face_uint8(uint8_item *item, short type) */ void send_verse_vert_vec_real32(vec_real32_item *item, short type) { - struct VerseVert *vvert = (VerseVert*)item->parent; - verse_send_g_vertex_set_xyz_real32( - vvert->vlayer->vnode->id, - item->layer_id, - vvert->id, + item->vlayer->vnode->id, + item->vlayer->id, + item->id, item->value[0], item->value[1], item->value[2]); @@ -571,12 +588,10 @@ void send_verse_vert_vec_real32(vec_real32_item *item, short type) */ void send_verse_vert_real32(real32_item *item, short type) { - struct VerseVert *vvert = (VerseVert*)item->parent; - verse_send_g_vertex_set_real32( - vvert->vlayer->vnode->id, - item->layer_id, - vvert->id, + item->vlayer->vnode->id, + item->vlayer->id, + item->id, item->value); } @@ -585,12 +600,10 @@ void send_verse_vert_real32(real32_item *item, short type) */ void send_verse_vert_uint32(uint32_item *item, short type) { - struct VerseVert *vvert = (VerseVert*)item->parent; - verse_send_g_vertex_set_uint32( - vvert->vlayer->vnode->id, - item->layer_id, - vvert->id, + item->vlayer->vnode->id, + item->vlayer->id, + item->id, item->value); } @@ -663,7 +676,6 @@ static void send_verse_face(VerseFace *vface) vface->flag |= FACE_SENT; if(vface->v3 != -1) { -/* printf("\tSEND: VerseFace: %d, %d, %d, %d, %d\n", vface->id, vface->v0, vface->v3, vface->v2, vface->v1);*/ verse_send_g_polygon_set_corner_uint32( vface->vlayer->vnode->id, vface->vlayer->id, @@ -674,7 +686,6 @@ static void send_verse_face(VerseFace *vface) vface->v1); /* verse use clock-wise winding */ } else { -/* printf("\tSEND: VerseFace: %d, %d, %d, %d, %d\n", vface->id, vface->v0, vface->v2, vface->v1, vface->v3);*/ verse_send_g_polygon_set_corner_uint32( vface->vlayer->vnode->id, vface->vlayer->id, @@ -948,162 +959,80 @@ VGeomData *create_geometry_data(void) return geom; } -/* - * callback function: vertex crease was set - */ -static void cb_g_crease_set_vertex( - void *user_data, - VNodeID node_id, - const char *layer, - uint32 def_crease) +/* Create item containing 4 floats */ +static quat_real32_item *create_quat_real32_item( + VLayer *vlayer, + uint32 item_id, + real32 v0, + real32 v1, + real32 v2, + real32 v3) { -/* struct VerseSession *session = (VerseSession*)current_verse_session(); - struct VNode *vnode;*/ -} + struct quat_real32_item *item; -/* - * callback function: edge crease was set - */ -static void cb_g_crease_set_edge( - void *user_data, - VNodeID node_id, - const char *layer, - uint32 def_crease) -{ -/* struct VerseSession *session = (VerseSession*)current_verse_session(); - struct VNode *vnode;*/ -} + item = (quat_real32_item*)MEM_mallocN(sizeof(quat_real32_item), "quat_real32_item"); -/* - * callback function: float value for polygon was set up - */ -static void cb_g_polygon_set_face_real32( - void *user_def, - VNodeID node_id, - VLayerID layer_id, - uint32 polygon_id, - real32 value) -{ -/* struct VerseSession *session = (VerseSession*)current_verse_session(); - struct VNode *vnode;*/ -} + item->vlayer = vlayer; + item->id = item_id; + item->value[0] = v0; + item->value[1] = v1; + item->value[2] = v2; + item->value[3] = v3; -/* - * callback function: int values for polygon was set up - */ -static void cb_g_polygon_set_face_uint32( - void *user_def, - VNodeID node_id, - VLayerID layer_id, - uint32 polygon_id, - uint32 value) -{ -/* struct VerseSession *session = (VerseSession*)current_verse_session(); - struct VNode *vnode;*/ + return item; } -static uint8_item *create_uint8_item(void) +/* Create item containing 1 float */ +static real32_item *create_real32_item(VLayer *vlayer, uint32 item_id, real32 value) { - struct uint8_item *item; + struct real32_item *item; - item = (uint8_item*)MEM_mallocN(sizeof(uint8_item), "uint8_item"); - item->value = 0; + item = (real32_item*)MEM_mallocN(sizeof(real32_item), "real32_item"); + + item->vlayer = vlayer; + item->id = item_id; + item->value = value; return item; } -/* - * callback function: uint8 value for polygon was set up - */ -static void cb_g_polygon_set_face_uint8( - void *user_def, - VNodeID node_id, - VLayerID layer_id, - uint32 polygon_id, - uint8 value) +/* Create item containing 1 integer */ +static uint32_item *create_uint32_item(VLayer *vlayer, uint32 item_id, uint32 value) { - struct VerseSession *session = (VerseSession*)current_verse_session(); - struct VNode *vnode; - struct VLayer *vlayer; - struct uint8_item *item; + struct uint32_item *item; - if(!session) return; + item = (uint32_item*)MEM_mallocN(sizeof(uint32_item), "uint32_item"); - /* find needed node (we can be sure, that it is geometry node) */ - vnode = (VNode*)BLI_dlist_find_link(&(session->nodes), (unsigned int)node_id); + item->vlayer = vlayer; + item->id = item_id; + item->value = value; - /* find layer containing uint_8 data */ - vlayer = (VLayer*)BLI_dlist_find_link(&(((VGeomData*)vnode->data)->layers), (unsigned int)layer_id); - - /* try to find item*/ - item = BLI_dlist_find_link(&(vlayer->dl), polygon_id); - - if(item) { - item->value = value; - } - else { - item = create_uint8_item(); - BLI_dlist_add_item_index(&(vlayer->dl), item, polygon_id); - item->value = value; - } + return item; } -/* - * callback function: float value for polygon corner was set up - */ -static void cb_g_polygon_set_corner_real32( - void *user_def, - VNodeID node_id, - VLayerID layer_id, - uint32 polygon_id, - real32 v0, - real32 v1, - real32 v2, - real32 v3) +/* Create item containing 1 byte */ +static uint8_item *create_uint8_item(VLayer *vlayer, uint32 item_id, uint8 value) { + struct uint8_item *item; + + item = (uint8_item*)MEM_mallocN(sizeof(uint8_item), "uint8_item"); + + item->vlayer = vlayer; + item->id = item_id; + item->value = value; + + return item; } /* - * callback function: polygon is deleted + * callback function: vertex crease was set */ -static void cb_g_polygon_delete( +static void cb_g_crease_set_vertex( void *user_data, VNodeID node_id, - uint32 polygon_id) + const char *layer, + uint32 def_crease) { - struct VerseSession *session = (VerseSession*)current_verse_session(); - VNode *vnode; - VLayer *vlayer; - VerseFace *vface; - - if(!session) return; - - /* find needed node (we can be sure, that it is geometry node) */ - vnode = BLI_dlist_find_link(&(session->nodes), node_id); - - /* find layer containing faces */ - vlayer = find_verse_layer_type((VGeomData*)vnode->data, POLYGON_LAYER); - - /* find wanted VerseFace */ - vface = BLI_dlist_find_link(&(vlayer->dl), polygon_id); - - if(!vface) return; - - /* update edge hash */ - update_edgehash_of_deleted_verseface(vnode, vface); - - ((VGeomData*)vnode->data)->post_polygon_delete(vface); - - /* decrease references at coresponding VerseVertexes */ - vface->vvert0->counter--; - vface->vvert1->counter--; - vface->vvert2->counter--; - if(vface->vvert3) vface->vvert3->counter--; - - /* delete unneeded VerseVertexes */ - free_unneeded_verseverts_of_verseface(vnode, vface); - - free_verse_face(vlayer, vface); } /* @@ -1365,6 +1294,206 @@ static void update_edgehash_of_new_verseface( } /* + * callback function: edge crease was set + */ +static void cb_g_crease_set_edge( + void *user_data, + VNodeID node_id, + const char *layer, + uint32 def_crease) +{ +} + +/* + * callback function: float value for polygon was set up + */ +static void cb_g_polygon_set_face_real32( + void *user_def, + VNodeID node_id, + VLayerID layer_id, + uint32 polygon_id, + real32 value) +{ + struct VerseSession *session = (VerseSession*)current_verse_session(); + struct VNode *vnode; + struct VLayer *vlayer; + struct real32_item *item; + + if(!session) return; + + /* find needed node (we can be sure, that it is geometry node) */ + vnode = (VNode*)BLI_dlist_find_link(&(session->nodes), (unsigned int)node_id); + + /* find layer containing uint_8 data */ + vlayer = (VLayer*)BLI_dlist_find_link(&(((VGeomData*)vnode->data)->layers), (unsigned int)layer_id); + + /* try to find item*/ + item = BLI_dlist_find_link(&(vlayer->dl), polygon_id); + + if(item) { + item->value = value; + } + else { + item = create_real32_item(vlayer, polygon_id, value); + BLI_dlist_add_item_index(&(vlayer->dl), item, item->id); + } +} + +/* + * callback function: int values for polygon was set up + */ +static void cb_g_polygon_set_face_uint32( + void *user_def, + VNodeID node_id, + VLayerID layer_id, + uint32 polygon_id, + uint32 value) +{ + struct VerseSession *session = (VerseSession*)current_verse_session(); + struct VNode *vnode; + struct VLayer *vlayer; + struct uint32_item *item; + + if(!session) return; + + /* find needed node (we can be sure, that it is geometry node) */ + vnode = (VNode*)BLI_dlist_find_link(&(session->nodes), (unsigned int)node_id); + + /* find layer containing uint_8 data */ + vlayer = (VLayer*)BLI_dlist_find_link(&(((VGeomData*)vnode->data)->layers), (unsigned int)layer_id); + + /* try to find item*/ + item = BLI_dlist_find_link(&(vlayer->dl), polygon_id); + + if(item) { + item->value = value; + } + else { + item = create_uint32_item(vlayer, polygon_id, value); + BLI_dlist_add_item_index(&(vlayer->dl), item, item->id); + } +} + +/* + * callback function: uint8 value for polygon was set up + */ +static void cb_g_polygon_set_face_uint8( + void *user_def, + VNodeID node_id, + VLayerID layer_id, + uint32 polygon_id, + uint8 value) +{ + struct VerseSession *session = (VerseSession*)current_verse_session(); + struct VNode *vnode; + struct VLayer *vlayer; + struct uint8_item *item; + + if(!session) return; + + /* find needed node (we can be sure, that it is geometry node) */ + vnode = (VNode*)BLI_dlist_find_link(&(session->nodes), (unsigned int)node_id); + + /* find layer containing uint_8 data */ + vlayer = (VLayer*)BLI_dlist_find_link(&(((VGeomData*)vnode->data)->layers), (unsigned int)layer_id); + + /* try to find item*/ + item = BLI_dlist_find_link(&(vlayer->dl), polygon_id); + + if(item) { + item->value = value; + } + else { + item = create_uint8_item(vlayer, polygon_id, value); + BLI_dlist_add_item_index(&(vlayer->dl), item, item->id); + } +} + +/* + * callback function: float value for polygon corner was set up + */ +static void cb_g_polygon_set_corner_real32( + void *user_def, + VNodeID node_id, + VLayerID layer_id, + uint32 polygon_id, + real32 v0, + real32 v1, + real32 v2, + real32 v3) +{ + struct VerseSession *session = (VerseSession*)current_verse_session(); + struct VNode *vnode; + struct VLayer *vlayer; + struct quat_real32_item *item; + + if(!session) return; + + /* find needed node (we can be sure, that it is geometry node) */ + vnode = (VNode*)BLI_dlist_find_link(&(session->nodes), (unsigned int)node_id); + + /* find layer containing uint_8 data */ + vlayer = (VLayer*)BLI_dlist_find_link(&(((VGeomData*)vnode->data)->layers), (unsigned int)layer_id); + + /* try to find item*/ + item = BLI_dlist_find_link(&(vlayer->dl), polygon_id); + + if(item) { + item->value[0] = v0; + item->value[1] = v1; + item->value[2] = v2; + item->value[3] = v3; + } + else { + item = create_quat_real32_item(vlayer, polygon_id, v0, v1, v2, v3); + BLI_dlist_add_item_index(&(vlayer->dl), item, item->id); + } +} + +/* + * callback function: polygon is deleted + */ +static void cb_g_polygon_delete( + void *user_data, + VNodeID node_id, + uint32 polygon_id) +{ + struct VerseSession *session = (VerseSession*)current_verse_session(); + VNode *vnode; + VLayer *vlayer; + VerseFace *vface; + + if(!session) return; + + /* find needed node (we can be sure, that it is geometry node) */ + vnode = BLI_dlist_find_link(&(session->nodes), node_id); + + /* find layer containing faces */ + vlayer = find_verse_layer_type((VGeomData*)vnode->data, POLYGON_LAYER); + + /* find wanted VerseFace */ + vface = BLI_dlist_find_link(&(vlayer->dl), polygon_id); + + if(!vface) return; + + /* update edge hash */ + update_edgehash_of_deleted_verseface(vnode, vface); + + ((VGeomData*)vnode->data)->post_polygon_delete(vface); + + /* decrease references at coresponding VerseVertexes */ + vface->vvert0->counter--; + vface->vvert1->counter--; + vface->vvert2->counter--; + if(vface->vvert3) vface->vvert3->counter--; + + /* delete unneeded VerseVertexes */ + free_unneeded_verseverts_of_verseface(vnode, vface); + + free_verse_face(vlayer, vface); +} + +/* * callback function: new polygon (face) created or existing polygon was changed */ static void cb_g_polygon_set_corner_uint32( @@ -1395,8 +1524,6 @@ static void cb_g_polygon_set_corner_uint32( /* we have to test coretness of incoming data */ if(!test_polygon_set_corner_uint32(v0, v1, v2, v3)) return; -/* printf("\tRECEIVE VerseFace: %d, %d, %d, %d, %d\n", polygon_id, v0, v1, v2, v3);*/ - /* Blender uses different order of vertexes */ if(v3!=-1) { /* quat swap */ unsigned int v; v = v1; v1 = v3; v3 = v; @@ -1414,12 +1541,10 @@ static void cb_g_polygon_set_corner_uint32( if(vface) { BLI_remlink(&(vlayer->queue), (void*)vface); BLI_dlist_add_item_index(&(vlayer->dl), (void*)vface, (unsigned int)polygon_id); -/* printf("\treceived changed face (changed by this app)\n");*/ } } if(!vface) { -/* printf("\tno vface\n");*/ /* try to find VerseFace in list of VerseVaces created by me and set up polygon and * layer ids */ vface = find_verse_face_in_queue(vlayer, node_id, polygon_id, v0, v1, v2, v3); @@ -1428,12 +1553,10 @@ static void cb_g_polygon_set_corner_uint32( update_edgehash_of_new_verseface(vnode, v0, v1, v2, v3); if(vface){ -/* printf("\tremove from vface queue\n");*/ /* I creeated this face ... remove VerseFace from queue */ BLI_remlink(&(vlayer->queue), (void*)vface); } else { -/* printf("\tcreate vface\n");*/ /* some other client created this face*/ vface = create_verse_face(vlayer, polygon_id, v0, v1, v2, v3); } @@ -1452,7 +1575,6 @@ static void cb_g_polygon_set_corner_uint32( ((VGeomData*)vnode->data)->post_polygon_create(vface); } else { -/* printf("\torphan vface\n");*/ /* when all needed VerseVertexes weren't received, then VerseFace is moved to * the list of orphans waiting on needed vertexes */ vface->flag |= FACE_RECEIVED; @@ -1461,7 +1583,6 @@ static void cb_g_polygon_set_corner_uint32( } else { VLayer *vert_vlayer = find_verse_layer_type((VGeomData*)vnode->data, VERTEX_LAYER); -/* printf("\tvface changed\n");*/ /* VerseVertexes of existing VerseFace were changed (VerseFace will use some different * VerseVertexes or it will use them in different order) */ @@ -1594,8 +1715,29 @@ static void cb_g_vertex_set_real32( uint32 vertex_id, real32 value) { -/* struct VerseSession *session = (VerseSession*)current_verse_session(); - struct VNode *vnode;*/ + struct VerseSession *session = (VerseSession*)current_verse_session(); + struct VNode *vnode; + struct VLayer *vlayer; + struct real32_item *item; + + if(!session) return; + + /* find needed node (we can be sure, that it is geometry node) */ + vnode = (VNode*)BLI_dlist_find_link(&(session->nodes), (unsigned int)node_id); + + /* find layer containing uint_8 data */ + vlayer = (VLayer*)BLI_dlist_find_link(&(((VGeomData*)vnode->data)->layers), (unsigned int)layer_id); + + /* try to find item*/ + item = BLI_dlist_find_link(&(vlayer->dl), vertex_id); + + if(item) { + item->value = value; + } + else { + item = create_real32_item(vlayer, vertex_id, value); + BLI_dlist_add_item_index(&(vlayer->dl), item, item->id); + } } /* @@ -1608,8 +1750,29 @@ static void cb_g_vertex_set_uint32( uint32 vertex_id, uint32 value) { -/* struct VerseSession *session = (VerseSession*)current_verse_session(); - struct VNode *vnode;*/ + struct VerseSession *session = (VerseSession*)current_verse_session(); + struct VNode *vnode; + struct VLayer *vlayer; + struct uint32_item *item; + + if(!session) return; + + /* find needed node (we can be sure, that it is geometry node) */ + vnode = (VNode*)BLI_dlist_find_link(&(session->nodes), (unsigned int)node_id); + + /* find layer containing uint_8 data */ + vlayer = (VLayer*)BLI_dlist_find_link(&(((VGeomData*)vnode->data)->layers), (unsigned int)layer_id); + + /* try to find item*/ + item = BLI_dlist_find_link(&(vlayer->dl), vertex_id); + + if(item) { + item->value = value; + } + else { + item = create_uint32_item(vlayer, vertex_id, value); + BLI_dlist_add_item_index(&(vlayer->dl), item, item->id); + } } /* diff --git a/source/blender/blenkernel/intern/world.c b/source/blender/blenkernel/intern/world.c index 612c095aac7..625ca57dbf3 100644 --- a/source/blender/blenkernel/intern/world.c +++ b/source/blender/blenkernel/intern/world.c @@ -75,6 +75,8 @@ void free_world(World *wrld) if(mtex && mtex->tex) mtex->tex->id.us--; if(mtex) MEM_freeN(mtex); } + BKE_previewimg_free(&wrld->preview); + wrld->ipo= 0; BKE_icon_delete((struct ID*)wrld); wrld->id.icon_id = 0; @@ -96,13 +98,16 @@ World *add_world(char *name) wrld->exp= 0.0f; wrld->exposure=wrld->range= 1.0f; - wrld->aodist= 10.0; + wrld->aodist= 5.0; wrld->aosamp= 5; wrld->aoenergy= 1.0; wrld->aobias= 0.05; + wrld->ao_samp_method = WO_AOSAMP_HAMMERSLEY; + wrld->physicsEngine= WOPHY_BULLET;//WOPHY_SUMO; Bullet by default - + wrld->preview = NULL; + return wrld; } @@ -121,6 +126,8 @@ World *copy_world(World *wrld) } } + if (wrld->preview) wrldn->preview = BKE_previewimg_copy(wrld->preview); + BPY_copy_scriptlink(&wrld->scriptlink); id_us_plus((ID *)wrldn->ipo); diff --git a/source/blender/blenkernel/intern/writeavi.c b/source/blender/blenkernel/intern/writeavi.c index 39f9af4c3d3..bd6859973b1 100644 --- a/source/blender/blenkernel/intern/writeavi.c +++ b/source/blender/blenkernel/intern/writeavi.c @@ -138,7 +138,8 @@ void start_avi(RenderData *rd, int rectx, int recty) int x, y; char name[256]; AviFormat format; - int quality, framerate; + int quality; + double framerate; makeavistring(rd, name); @@ -147,7 +148,7 @@ void start_avi(RenderData *rd, int rectx, int recty) y = recty; quality= rd->quality; - framerate= rd->frs_sec; + framerate= (double) rd->frs_sec / (double) rd->frs_sec_base; avi = MEM_mallocN (sizeof(AviMovie), "avimovie"); diff --git a/source/blender/blenkernel/intern/writeffmpeg.c b/source/blender/blenkernel/intern/writeffmpeg.c index 703f2803ede..cbaf1f8c605 100644 --- a/source/blender/blenkernel/intern/writeffmpeg.c +++ b/source/blender/blenkernel/intern/writeffmpeg.c @@ -32,6 +32,7 @@ #include <ffmpeg/avformat.h> #include <ffmpeg/avcodec.h> #include <ffmpeg/rational.h> +#include <ffmpeg/swscale.h> #if LIBAVFORMAT_VERSION_INT < (49 << 16) #define FFMPEG_OLD_FRAME_RATE 1 @@ -81,6 +82,7 @@ static AVFormatContext* outfile = 0; static AVStream* video_stream = 0; static AVStream* audio_stream = 0; static AVFrame* current_frame = 0; +static struct SwsContext *img_convert_ctx = 0; static uint8_t* video_buffer = 0; static int video_buffersize = 0; @@ -317,8 +319,9 @@ static AVFrame* generate_video_frame(uint8_t* pixels) } if (c->pix_fmt != PIX_FMT_RGBA32) { - img_convert((AVPicture*)current_frame, c->pix_fmt, - (AVPicture*)rgb_frame, PIX_FMT_RGBA32, width, height); + sws_scale(img_convert_ctx, rgb_frame->data, + rgb_frame->linesize, 0, c->height, + current_frame->data, current_frame->linesize); delete_picture(rgb_frame); } return current_frame; @@ -352,18 +355,26 @@ static AVStream* alloc_video_stream(int codec_id, AVFormatContext* of, if (ffmpeg_type == FFMPEG_DV && G.scene->r.frs_sec != 25) { c->time_base.den = 2997; c->time_base.num = 100; - } else { + } else if ((double) ((int) G.scene->r.frs_sec_base) == + G.scene->r.frs_sec_base) { c->time_base.den = G.scene->r.frs_sec; - c->time_base.num = 1; + c->time_base.num = (int) G.scene->r.frs_sec_base; + } else { + c->time_base.den = G.scene->r.frs_sec * 100000; + c->time_base.num = ((double) G.scene->r.frs_sec_base) * 100000; } #else /* FIXME: Really bad hack (tm) for NTSC support */ if (ffmpeg_type == FFMPEG_DV && G.scene->r.frs_sec != 25) { c->frame_rate = 2997; c->frame_rate_base = 100; - } else { + } else if ((double) ((int) G.scene->r.frs_sec_base) == + G.scene->r.frs_sec_base) { c->frame_rate = G.scene->r.frs_sec; - c->frame_rate_base = 1; + c->frame_rate_base = G.scene->r.frs_sec_base; + } else { + c->frame_rate = G.scene->r.frs_sec * 100000; + c->frame_rate_base = ((double) G.scene->r.frs_sec_base)*100000; } #endif @@ -420,6 +431,13 @@ static AVStream* alloc_video_stream(int codec_id, AVFormatContext* of, "FFMPEG video buffer"); current_frame = alloc_picture(c->pix_fmt, c->width, c->height); + + img_convert_ctx = sws_getContext(c->width, c->height, + PIX_FMT_RGBA32, + c->width, c->height, + c->pix_fmt, + SWS_BICUBIC, + NULL, NULL, NULL); return st; } @@ -749,8 +767,10 @@ void end_ffmpeg(void) fprintf(stderr, "Closing ffmpeg...\n"); - write_audio_frames(); - + if (audio_stream) { + write_audio_frames(); + } + if (outfile) { av_write_trailer(outfile); } @@ -797,6 +817,11 @@ void end_ffmpeg(void) MEM_freeN(audio_input_buffer); audio_input_buffer = 0; } + + if (img_convert_ctx) { + sws_freeContext(img_convert_ctx); + img_convert_ctx = 0; + } } #endif |