diff options
Diffstat (limited to 'source')
381 files changed, 42210 insertions, 3022 deletions
diff --git a/source/blender/CMakeLists.txt b/source/blender/CMakeLists.txt index 19d4f206778..0a197cd3e26 100644 --- a/source/blender/CMakeLists.txt +++ b/source/blender/CMakeLists.txt @@ -31,6 +31,7 @@ set(SRC_DNA_INC ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_armature_types.h ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_boid_types.h ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_brush_types.h + ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_cache_library_types.h ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_camera_types.h ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_cloth_types.h ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_color_types.h @@ -79,6 +80,7 @@ set(SRC_DNA_INC ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_sound_types.h ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_space_types.h ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_speaker_types.h + ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_strands_types.h ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_text_types.h ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_texture_types.h ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_userdef_types.h @@ -87,6 +89,7 @@ set(SRC_DNA_INC ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_view2d_types.h ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_view3d_types.h ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_windowmanager_types.h + ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_widget_types.h ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_world_types.h ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_movieclip_types.h ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_tracking_types.h @@ -109,6 +112,7 @@ add_subdirectory(gpu) add_subdirectory(imbuf) add_subdirectory(nodes) add_subdirectory(modifiers) +add_subdirectory(pointcache) add_subdirectory(makesdna) add_subdirectory(makesrna) diff --git a/source/blender/SConscript b/source/blender/SConscript index e987b6be779..9bdba768448 100644 --- a/source/blender/SConscript +++ b/source/blender/SConscript @@ -44,7 +44,8 @@ SConscript(['avi/SConscript', 'ikplugin/SConscript', 'physics/SConscript', 'windowmanager/SConscript', - 'blenfont/SConscript']) + 'blenfont/SConscript', + 'pointcache/SConscript']) makesrna = SConscript('makesrna/SConscript') diff --git a/source/blender/blenfont/BLF_translation.h b/source/blender/blenfont/BLF_translation.h index c9b0c253ed4..badfe1f4a4c 100644 --- a/source/blender/blenfont/BLF_translation.h +++ b/source/blender/blenfont/BLF_translation.h @@ -148,6 +148,7 @@ const char *BLF_translate_do_new_dataname(const char *msgctxt, const char *msgid #define BLF_I18NCONTEXT_ID_ACTION "Action" #define BLF_I18NCONTEXT_ID_ARMATURE "Armature" #define BLF_I18NCONTEXT_ID_BRUSH "Brush" +#define BLF_I18NCONTEXT_ID_CACHELIBRARY "CacheLibrary" #define BLF_I18NCONTEXT_ID_CAMERA "Camera" #define BLF_I18NCONTEXT_ID_CURVE "Curve" #define BLF_I18NCONTEXT_ID_FREESTYLELINESTYLE "FreestyleLineStyle" diff --git a/source/blender/blenkernel/BKE_DerivedMesh.h b/source/blender/blenkernel/BKE_DerivedMesh.h index 789d86b7816..65e7d47c46c 100644 --- a/source/blender/blenkernel/BKE_DerivedMesh.h +++ b/source/blender/blenkernel/BKE_DerivedMesh.h @@ -182,7 +182,8 @@ struct DerivedMesh { DerivedMeshType type; float auto_bump_scale; DMDirtyFlag dirty; - int totmat; /* total materials. Will be valid only before object drawing. */ + short totfmaps; + short totmat; /* total materials. Will be valid only before object drawing. */ struct Material **mat; /* material array. Will be valid only before object drawing */ /* use for converting to BMesh which doesn't store bevel weight and edge crease by default */ @@ -464,6 +465,10 @@ struct DerivedMesh { void (*setMaterial)(void *userData, int matnr, void *attribs), bool (*setFace)(void *userData, int index), void *userData); + struct GPUDrawObject *(*gpuObjectNew)(DerivedMesh *dm); + void (*copy_gpu_data)(DerivedMesh *dm, int type, float *varray, int *index, + int *mat_orig_to_new, void *user_data); + /** Release reference to the DerivedMesh. This function decides internally * if the DerivedMesh will be freed, or cached for later use. */ void (*release)(DerivedMesh *dm); diff --git a/source/blender/blenkernel/BKE_anim.h b/source/blender/blenkernel/BKE_anim.h index 584f0da323a..bfba221e488 100644 --- a/source/blender/blenkernel/BKE_anim.h +++ b/source/blender/blenkernel/BKE_anim.h @@ -41,6 +41,15 @@ struct bAnimVizSettings; struct bMotionPath; struct bPoseChannel; struct ReportList; +struct GHash; +struct DupliCache; +struct DupliObject; +struct DupliObjectData; +struct DerivedMesh; +struct Strands; +struct StrandsChildren; +struct DupliCacheIterator; +struct CacheLibrary; /* ---------------------------------------------------- */ /* Animation Visualization */ @@ -65,11 +74,43 @@ int where_on_path(struct Object *ob, float ctime, float vec[4], float dir[3], fl /* ---------------------------------------------------- */ /* Dupli-Geometry */ -struct ListBase *object_duplilist_ex(struct EvaluationContext *eval_ctx, struct Scene *sce, struct Object *ob, bool update); -struct ListBase *object_duplilist(struct EvaluationContext *eval_ctx, struct Scene *sce, struct Object *ob); +struct ListBase *object_duplilist_ex(struct EvaluationContext *eval_ctx, struct Scene *scene, struct Object *ob, bool update); +struct ListBase *object_duplilist(struct EvaluationContext *eval_ctx, struct Scene *scene, struct Object *ob); +struct ListBase *group_duplilist_ex(struct EvaluationContext *eval_ctx, struct Scene *scene, struct Group *group, bool update); +struct ListBase *group_duplilist(struct EvaluationContext *eval_ctx, struct Scene *scene, struct Group *group); void free_object_duplilist(struct ListBase *lb); int count_duplilist(struct Object *ob); +void BKE_object_dupli_cache_update(struct Scene *scene, struct Object *ob, struct EvaluationContext *eval_ctx, float frame); +void BKE_object_dupli_cache_clear(struct Object *ob); +void BKE_object_dupli_cache_free(struct Object *ob); +bool BKE_object_dupli_cache_contains(struct Object *ob, struct Object *other); +struct DupliObjectData *BKE_dupli_cache_find_data(struct DupliCache *dupcache, struct Object *ob); + +void BKE_dupli_object_data_init(struct DupliObjectData *data, struct Object *ob); +/* does not free data itself */ +void BKE_dupli_object_data_clear(struct DupliObjectData *data); +void BKE_dupli_object_data_set_mesh(struct DupliObjectData *data, struct DerivedMesh *dm); +void BKE_dupli_object_data_add_strands(struct DupliObjectData *data, const char *name, struct Strands *strands); +void BKE_dupli_object_data_add_strands_children(struct DupliObjectData *data, const char *name, struct StrandsChildren *children); +void BKE_dupli_object_data_find_strands(struct DupliObjectData *data, const char *name, struct Strands **r_strands, struct StrandsChildren **r_children); +bool BKE_dupli_object_data_acquire_strands(struct DupliObjectData *data, struct Strands *strands); +bool BKE_dupli_object_data_acquire_strands_children(struct DupliObjectData *data, struct StrandsChildren *children); + +struct DupliCache *BKE_dupli_cache_new(void); +void BKE_dupli_cache_free(struct DupliCache *dupcache); +void BKE_dupli_cache_clear(struct DupliCache *dupcache); +void BKE_dupli_cache_clear_instances(struct DupliCache *dupcache); +struct DupliObjectData *BKE_dupli_cache_add_object(struct DupliCache *dupcache, struct Object *ob); +struct DupliObject *BKE_dupli_cache_add_instance(struct DupliCache *dupcache, float obmat[4][4], struct DupliObjectData *data); +void BKE_dupli_cache_from_group(struct Scene *scene, struct Group *group, struct CacheLibrary *cachelib, struct DupliCache *dupcache, struct EvaluationContext *eval_ctx, bool calc_strands_base); + +struct DupliCacheIterator *BKE_dupli_cache_iter_new(struct DupliCache *dupcache); +void BKE_dupli_cache_iter_free(struct DupliCacheIterator *iter); +bool BKE_dupli_cache_iter_valid(struct DupliCacheIterator *iter); +void BKE_dupli_cache_iter_next(struct DupliCacheIterator *iter); +struct DupliObjectData *BKE_dupli_cache_iter_get(struct DupliCacheIterator *iter); + typedef struct DupliExtraData { float obmat[4][4]; unsigned int lay; diff --git a/source/blender/blenkernel/BKE_cache_library.h b/source/blender/blenkernel/BKE_cache_library.h new file mode 100644 index 00000000000..62ec261f123 --- /dev/null +++ b/source/blender/blenkernel/BKE_cache_library.h @@ -0,0 +1,254 @@ +/* + * ***** 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2015 Blender Foundation. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): Lukas Toenne + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#ifndef __BKE_CACHE_LIBRARY_H__ +#define __BKE_CACHE_LIBRARY_H__ + +/** \file BKE_cache_library.h + * \ingroup bke + */ + +#include "DNA_cache_library_types.h" + +struct ListBase; +struct Main; +struct bContext; +struct DerivedMesh; +struct Group; +struct Object; +struct Scene; +struct EvaluationContext; +struct ParticleSystem; +struct DupliCache; +struct DupliObjectData; +struct CacheModifier; +struct ID; +struct CacheProcessData; +struct BVHTreeFromMesh; +struct Strands; +struct StrandsChildren; +struct StrandsKeyCacheModifier; +struct Key; +struct KeyBlock; + +struct ClothModifierData; + +struct CacheLibrary *BKE_cache_library_add(struct Main *bmain, const char *name); +struct CacheLibrary *BKE_cache_library_copy(struct CacheLibrary *cachelib); +void BKE_cache_library_free(struct CacheLibrary *cachelib); +void BKE_cache_library_unlink(struct CacheLibrary *cachelib); + +const char *BKE_cache_item_name_prefix(int type); +void BKE_cache_item_name(struct Object *ob, int type, int index, char *name); +int BKE_cache_item_name_length(struct Object *ob, int type, int index); +eCacheReadSampleResult BKE_cache_read_result(int ptc_result); + +bool BKE_cache_library_validate_item(struct CacheLibrary *cachelib, struct Object *ob, int type, int index); + +struct IDProperty *BKE_cache_library_get_input_metadata(struct CacheLibrary *cachelib, bool create); +struct IDProperty *BKE_cache_library_get_output_metadata(struct CacheLibrary *cachelib, bool create); + +/* ========================================================================= */ + +void BKE_cache_library_get_read_flags(struct CacheLibrary *cachelib, bool use_render, bool for_display, bool *read_strands_motion, bool *read_strands_children); + +bool BKE_cache_archive_path_test(struct CacheLibrary *cachelib, const char *path); +void BKE_cache_archive_path_ex(const char *path, struct Library *lib, const char *default_filename, char *result, int max); +void BKE_cache_archive_input_path(struct CacheLibrary *cachelib, char *result, int max); +void BKE_cache_archive_output_path(struct CacheLibrary *cachelib, char *result, int max); + +void BKE_cache_library_dag_recalc_tag(struct EvaluationContext *eval_ctx, struct Main *bmain); + +/*void BKE_cache_library_filter_duplilist(struct CacheLibrary *cachelib, struct ListBase *duplilist);*/ +void BKE_cache_library_tag_used_objects(CacheLibrary *cachelib); + +bool BKE_cache_read_dupli_cache(struct CacheLibrary *cachelib, struct DupliCache *dupcache, + struct Scene *scene, struct Group *dupgroup, float frame, bool use_render, bool for_display); +bool BKE_cache_read_dupli_object(struct CacheLibrary *cachelib, struct DupliObjectData *data, + struct Scene *scene, struct Object *ob, float frame, bool use_render, bool for_display); + +void BKE_cache_process_dupli_cache(struct CacheLibrary *cachelib, struct CacheProcessData *data, + struct Scene *scene, struct Group *dupgroup, float frame_prev, float frame, + bool do_modifiers, bool do_strands_child_deform, bool do_strands_motion); + +/* ========================================================================= */ + +typedef void (*CacheModifier_IDWalkFunc)(void *userdata, struct CacheLibrary *cachelib, struct CacheModifier *md, struct ID **id_ptr); + +typedef struct CacheProcessContext { + struct Main *bmain; + struct Scene *scene; + struct CacheLibrary *cachelib; + struct Group *group; +} CacheProcessContext; + +typedef struct CacheProcessData { + unsigned int lay; + float mat[4][4]; + struct DupliCache *dupcache; +} CacheProcessData; + +typedef enum eCacheProcessFlag { + eCacheProcessFlag_DoStrands = (1 << 0), + eCacheProcessFlag_DoStrandsChildren = (1 << 1), +} eCacheProcessFlag; + +typedef void (*CacheModifier_InitFunc)(struct CacheModifier *md); +typedef void (*CacheModifier_FreeFunc)(struct CacheModifier *md); +typedef void (*CacheModifier_CopyFunc)(struct CacheModifier *md, struct CacheModifier *target); +typedef void (*CacheModifier_ForeachIDLinkFunc)(struct CacheModifier *md, struct CacheLibrary *cachelib, + CacheModifier_IDWalkFunc walk, void *userData); +typedef void (*CacheModifier_ProcessFunc)(struct CacheModifier *md, struct CacheProcessContext *ctx, struct CacheProcessData *data, + int frame, int frame_prev, int process_flag); + +typedef struct CacheModifierTypeInfo { + /* The user visible name for this modifier */ + char name[32]; + + /* The DNA struct name for the modifier data type, + * used to write the DNA data out. + */ + char struct_name[32]; + + /* The size of the modifier data type, used by allocation. */ + int struct_size; + + /********************* Non-optional functions *********************/ + + /* Copy instance data for this modifier type. Should copy all user + * level settings to the target modifier. + */ + CacheModifier_CopyFunc copy; + + /* Should call the given walk function with a pointer to each ID + * pointer (i.e. each datablock pointer) that the modifier data + * stores. This is used for linking on file load and for + * unlinking datablocks or forwarding datablock references. + * + * This function is optional. + */ + CacheModifier_ForeachIDLinkFunc foreachIDLink; + + /* Process data and write results to the modifier's output archive */ + CacheModifier_ProcessFunc process; + + /********************* Optional functions *********************/ + + /* Initialize new instance data for this modifier type, this function + * should set modifier variables to their default values. + * + * This function is optional. + */ + CacheModifier_InitFunc init; + + /* Free internal modifier data variables, this function should + * not free the md variable itself. + * + * This function is optional. + */ + CacheModifier_FreeFunc free; +} CacheModifierTypeInfo; + +void BKE_cache_modifier_init(void); + +const char *BKE_cache_modifier_type_name(eCacheModifier_Type type); +const char *BKE_cache_modifier_type_struct_name(eCacheModifier_Type type); +int BKE_cache_modifier_type_struct_size(eCacheModifier_Type type); + +bool BKE_cache_modifier_unique_name(struct ListBase *modifiers, struct CacheModifier *md); +struct CacheModifier *BKE_cache_modifier_add(struct CacheLibrary *cachelib, const char *name, eCacheModifier_Type type); +void BKE_cache_modifier_remove(struct CacheLibrary *cachelib, struct CacheModifier *md); +void BKE_cache_modifier_clear(struct CacheLibrary *cachelib); +struct CacheModifier *BKE_cache_modifier_copy(struct CacheLibrary *cachelib, struct CacheModifier *md); + +void BKE_cache_modifier_foreachIDLink(struct CacheLibrary *cachelib, struct CacheModifier *md, CacheModifier_IDWalkFunc walk, void *userdata); + +bool BKE_cache_modifier_find_object(struct DupliCache *dupcache, struct Object *ob, struct DupliObjectData **r_data); +bool BKE_cache_modifier_find_strands(struct DupliCache *dupcache, struct Object *ob, int hair_system, struct DupliObjectData **r_data, struct Strands **r_strands, struct StrandsChildren **r_children, const char **r_name); + +struct KeyBlock *BKE_cache_modifier_strands_key_insert_key(struct StrandsKeyCacheModifier *md, struct Strands *strands, const char *name, const bool from_mix); +bool BKE_cache_modifier_strands_key_get(struct Object *ob, struct StrandsKeyCacheModifier **r_skmd, struct DerivedMesh **r_dm, struct Strands **r_strands, + struct DupliObjectData **r_dobdata, const char **r_name, float r_mat[4][4]); +bool BKE_cache_library_uses_key(struct CacheLibrary *cachelib, struct Key *key); + +/* ========================================================================= */ + +typedef struct CacheEffectorInstance { + struct CacheEffectorInstance *next, *prev; + + float mat[4][4]; + float imat[4][4]; + // TODO add linear/angular velocity if necessary +} CacheEffectorInstance; + +typedef struct CacheEffector { + int type; + + ListBase instances; + + struct DerivedMesh *dm; + struct BVHTreeFromMesh *treedata; + struct ForceFieldVertexCache *vertex_cache; + + float strength, falloff; + float mindist, maxdist; + bool double_sided; +} CacheEffector; + +typedef enum eCacheEffector_Type { + eCacheEffector_Type_Deflect = 0, + eCacheEffector_Type_Drag = 1, +} eCacheEffector_Type; + +typedef struct CacheEffectorPoint { + int index; + float x[3], v[3]; +} CacheEffectorPoint; + +typedef struct CacheEffectorResult { + float f[3]; +} CacheEffectorResult; + +int BKE_cache_effectors_get(struct CacheEffector *effectors, int max, struct CacheLibrary *cachelib, struct DupliCache *dupcache, float obmat[4][4]); +void BKE_cache_effectors_free(struct CacheEffector *effectors, int tot); +void BKE_cache_effector_velocity_update(struct CacheLibrary *cachelib, struct DupliCache *dupcache, float obmat[4][4], float frame); +int BKE_cache_effectors_eval(struct CacheEffector *effectors, int tot, struct CacheEffectorPoint *point, struct CacheEffectorResult *result); +int BKE_cache_effectors_eval_ex(struct CacheEffector *effectors, int tot, struct CacheEffectorPoint *point, struct CacheEffectorResult *result, + bool (*filter)(void *, struct CacheEffector *), void *filter_data); + +/* ========================================================================= */ + +struct CacheArchiveInfo *BKE_cache_archive_info_new(void); +void BKE_cache_archive_info_free(struct CacheArchiveInfo *info); +void BKE_cache_archive_info_clear(struct CacheArchiveInfo *info); + +struct CacheArchiveInfoNode *BKE_cache_archive_info_find_node(struct CacheArchiveInfo *info, struct CacheArchiveInfoNode *parent, + eCacheArchiveInfoNode_Type type, const char *name); +struct CacheArchiveInfoNode *BKE_cache_archive_info_add_node(struct CacheArchiveInfo *info, struct CacheArchiveInfoNode *parent, + eCacheArchiveInfoNode_Type type, const char *name); + +#endif diff --git a/source/blender/blenkernel/BKE_context.h b/source/blender/blenkernel/BKE_context.h index f7af3a7f8ec..74dafcac671 100644 --- a/source/blender/blenkernel/BKE_context.h +++ b/source/blender/blenkernel/BKE_context.h @@ -58,6 +58,7 @@ struct bPoseChannel; struct bGPdata; struct bGPDlayer; struct bGPDframe; +struct wmWidget; struct wmWindow; struct wmWindowManager; struct SpaceText; @@ -106,6 +107,7 @@ enum { CTX_MODE_PAINT_VERTEX, CTX_MODE_PAINT_TEXTURE, CTX_MODE_PARTICLE, + CTX_MODE_HAIR, CTX_MODE_OBJECT }; diff --git a/source/blender/blenkernel/BKE_customdata.h b/source/blender/blenkernel/BKE_customdata.h index ab49270ca64..8cd6311e84c 100644 --- a/source/blender/blenkernel/BKE_customdata.h +++ b/source/blender/blenkernel/BKE_customdata.h @@ -57,6 +57,8 @@ extern const CustomDataMask CD_MASK_EDITMESH; extern const CustomDataMask CD_MASK_DERIVEDMESH; extern const CustomDataMask CD_MASK_BMESH; extern const CustomDataMask CD_MASK_FACECORNERS; +extern const CustomDataMask CD_MASK_STRANDS; +extern const CustomDataMask CD_MASK_STRANDS_BMESH; extern const CustomDataMask CD_MASK_EVERYTHING; /* for ORIGINDEX layer type, indicates no original index for this element */ @@ -259,6 +261,7 @@ void *CustomData_get(const struct CustomData *data, int index, int type); void *CustomData_get_n(const struct CustomData *data, int type, int index, int n); void *CustomData_bmesh_get(const struct CustomData *data, void *block, int type); void *CustomData_bmesh_get_n(const struct CustomData *data, void *block, int type, int n); +void *CustomData_bmesh_get_named(const struct CustomData *data, void *block, int type, const char *name); /* gets the layer at physical index n, with no type checking. */ diff --git a/source/blender/blenkernel/BKE_depsgraph.h b/source/blender/blenkernel/BKE_depsgraph.h index 862f91f567e..d1214d39469 100644 --- a/source/blender/blenkernel/BKE_depsgraph.h +++ b/source/blender/blenkernel/BKE_depsgraph.h @@ -44,6 +44,7 @@ extern "C" { #endif +struct Group; struct ID; struct Main; struct Object; @@ -113,6 +114,7 @@ void DAG_scene_free(struct Scene *sce); * example a datablock was removed. */ void DAG_scene_update_flags(struct Main *bmain, struct Scene *sce, unsigned int lay, const bool do_time, const bool do_invisible_flush); +void DAG_scene_update_group_flags(struct Main *bmain, struct Scene *scene, struct Group *group, unsigned int lay, const bool do_time, const bool do_invisible_flush); void DAG_on_visible_update(struct Main *bmain, const bool do_time); void DAG_id_tag_update(struct ID *id, short flag); diff --git a/source/blender/blenkernel/BKE_editstrands.h b/source/blender/blenkernel/BKE_editstrands.h new file mode 100644 index 00000000000..ddca51e4736 --- /dev/null +++ b/source/blender/blenkernel/BKE_editstrands.h @@ -0,0 +1,104 @@ +/* + * ***** 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) Blender Foundation + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): Lukas Toenne + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#ifndef __BKE_EDITSTRANDS_H__ +#define __BKE_EDITSTRANDS_H__ + +/** \file blender/blenkernel/BKE_editstrands.h + * \ingroup bke + */ + +#include "BLI_utildefines.h" + +#include "DNA_customdata_types.h" + +#include "BKE_customdata.h" +#include "bmesh.h" + +struct BMesh; +struct DerivedMesh; +struct Key; +struct Object; +struct Strands; + +typedef struct BMEditStrands { + struct BMesh *bm; + + /*this is for undoing failed operations*/ + struct BMEditStrands *emcopy; + int emcopyusers; + + /* Object this editmesh came from (if it came from one) */ + struct Object *ob; + struct DerivedMesh *root_dm; + + int flag; + + unsigned int vertex_glbuf; + unsigned int elem_glbuf; + unsigned int dot_glbuf; + + /*temp variables for x-mirror editing*/ + int mirror_cdlayer; /* -1 is invalid */ +} BMEditStrands; + +/* BMEditStrands->flag */ +typedef enum BMEditStrandsFlag { + BM_STRANDS_DIRTY_SEGLEN = 1, +} BMEditStrandsFlag; + +struct BMEditStrands *BKE_editstrands_create(struct BMesh *bm, struct DerivedMesh *root_dm, float mat[4][4]); +struct BMEditStrands *BKE_editstrands_copy(struct BMEditStrands *es); +struct BMEditStrands *BKE_editstrands_from_object(struct Object *ob); +void BKE_editstrands_update_linked_customdata(struct BMEditStrands *es); +void BKE_editstrands_free(struct BMEditStrands *es); + +/* === constraints === */ + +/* Stores vertex locations for temporary reference: + * Vertex locations get modified by tools, but then need to be corrected + * by calculating a smooth solution based on the difference to original pre-tool locations. + */ +typedef float (*BMEditStrandsLocations)[3]; +BMEditStrandsLocations BKE_editstrands_get_locations(struct BMEditStrands *edit); +void BKE_editstrands_free_locations(BMEditStrandsLocations locs); + +void BKE_editstrands_solve_constraints(struct Object *ob, struct BMEditStrands *es, BMEditStrandsLocations orig); +void BKE_editstrands_ensure(struct BMEditStrands *es); + + +/* === cache shape key conversion === */ + +struct BMesh *BKE_cache_strands_to_bmesh(struct Strands *strands, struct Key *key, float mat[4][4], int act_key_nr, struct DerivedMesh *dm); +struct Strands *BKE_cache_strands_from_bmesh(struct BMEditStrands *edit, struct Key *key, float mat[4][4], struct DerivedMesh *dm); + +/* === particle conversion === */ + +struct BMesh *BKE_particles_to_bmesh(struct Object *ob, struct ParticleSystem *psys); +void BKE_particles_from_bmesh(struct Object *ob, struct ParticleSystem *psys); + +#endif diff --git a/source/blender/blenkernel/BKE_effect.h b/source/blender/blenkernel/BKE_effect.h index f8fee444d91..69c3d632c49 100644 --- a/source/blender/blenkernel/BKE_effect.h +++ b/source/blender/blenkernel/BKE_effect.h @@ -110,7 +110,8 @@ typedef struct EffectorCache { } EffectorCache; void free_partdeflect(struct PartDeflect *pd); -struct ListBase *pdInitEffectors(struct Scene *scene, struct Object *ob_src, struct ParticleSystem *psys_src, struct EffectorWeights *weights, bool precalc); +struct ListBase *pdInitEffectors(struct Scene *scene, struct Object *ob_src, struct ParticleSystem *psys_src, struct EffectorWeights *weights); +struct ListBase *pdInitEffectors_ex(struct Scene *scene, struct Object *ob_src, struct ParticleSystem *psys_src, int layers, struct EffectorWeights *weights, bool precalc); void pdEndEffectors(struct ListBase **effectors); void pdPrecalculateEffectors(struct ListBase *effectors); void pdDoEffectors(struct ListBase *effectors, struct ListBase *colliders, struct EffectorWeights *weights, struct EffectedPoint *point, float *force, float *impulse); @@ -193,7 +194,10 @@ void BKE_sim_debug_data_free(void); void BKE_sim_debug_data_add_element(int type, const float v1[3], const float v2[3], float r, float g, float b, const char *category, unsigned int hash); +void BKE_sim_debug_data_add_element_ex(struct SimDebugData *debug_data, int type, const float v1[3], const float v2[3], + float r, float g, float b, unsigned int category_hash, unsigned int hash); void BKE_sim_debug_data_remove_element(unsigned int hash); +void BKE_sim_debug_data_remove_element_ex(struct SimDebugData *debug_data, unsigned int hash); #define BKE_sim_debug_data_add_dot(p, r, g, b, category, ...) { \ const float v2[3] = { 0.0f, 0.0f, 0.0f }; \ diff --git a/source/blender/blenkernel/BKE_facemap.h b/source/blender/blenkernel/BKE_facemap.h new file mode 100644 index 00000000000..1367ef52f2f --- /dev/null +++ b/source/blender/blenkernel/BKE_facemap.h @@ -0,0 +1,54 @@ +/* + * ***** 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Contributor(s): Antony Riakiotakis + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#ifndef __BKE_FACEMAP_H__ +#define __BKE_FACEMAP_H__ + +/** \file BKE_facemap.h + * \ingroup bke + * \brief Functions for dealing with objects and facemaps. + */ + +#ifdef __cplusplus +extern "C" { +#endif + +struct bFaceMap; +struct ListBase; +struct Object; + +/* Face map operations */ +struct bFaceMap *BKE_object_facemap_add(struct Object *ob); +struct bFaceMap *BKE_object_facemap_add_name(struct Object *ob, const char *name); +void BKE_object_facemap_remove(struct Object *ob, struct bFaceMap *fmap); +void BKE_object_fmap_remove_all(struct Object *ob); + +int fmap_name_index(struct Object *ob, const char *name); +void fmap_unique_name(struct bFaceMap *fmap, struct Object *ob); +struct bFaceMap *fmap_find_name(struct Object *ob, const char *name); +void fmap_copy_list(struct ListBase *outbase, struct ListBase *inbase); + +#ifdef __cplusplus +} +#endif + +#endif /* __BKE_FACEMAP_H__ */ diff --git a/source/blender/blenkernel/BKE_key.h b/source/blender/blenkernel/BKE_key.h index abe12282a1b..1e9e392406b 100644 --- a/source/blender/blenkernel/BKE_key.h +++ b/source/blender/blenkernel/BKE_key.h @@ -40,6 +40,8 @@ struct Curve; struct Object; struct Lattice; struct Mesh; +struct ParticleSystem; +struct Strands; struct WeightsArrayCache; /* Kernel prototypes */ @@ -50,11 +52,16 @@ extern "C" { void BKE_key_free(struct Key *sc); void BKE_key_free_nolib(struct Key *key); struct Key *BKE_key_add(struct ID *id); +struct Key *BKE_key_add_ex(struct ID *from, int fromtype, int fromindex); +struct Key *BKE_key_add_particles(struct Object *ob, struct ParticleSystem *psys); struct Key *BKE_key_copy(struct Key *key); struct Key *BKE_key_copy_nolib(struct Key *key); void BKE_key_make_local(struct Key *key); void BKE_key_sort(struct Key *key); +void BKE_key_set_from_id(struct Key *key, struct ID *id); +void BKE_key_set_from_particles(struct Key *key, struct Object *ob, struct ParticleSystem *psys); + void key_curve_position_weights(float t, float data[4], int type); void key_curve_tangent_weights(float t, float data[4], int type); void key_curve_normal_weights(float t, float data[4], int type); @@ -64,11 +71,24 @@ float *BKE_key_evaluate_object_ex( float *arr, size_t arr_size); float *BKE_key_evaluate_object( struct Object *ob, int *r_totelem); +float *BKE_key_evaluate_strands_ex( + struct Strands *strands, struct Key *key, struct KeyBlock *actkb, bool lock_shape, + int *r_totelem, float *arr, size_t arr_size); +float *BKE_key_evaluate_strands( + struct Strands *strand, struct Key *key, struct KeyBlock *actkbs, bool lock_shape, + int *r_totelem, bool use_motion); +float *BKE_key_evaluate_particles_ex( + struct Object *ob, struct ParticleSystem *psys, float cfra, int *r_totelem, + float *arr, size_t arr_size); +float *BKE_key_evaluate_particles( + struct Object *ob, struct ParticleSystem *psys, float cfra, int *r_totelem); struct Key **BKE_key_from_object_p(struct Object *ob); struct Key *BKE_key_from_object(struct Object *ob); struct KeyBlock *BKE_keyblock_from_object(struct Object *ob); struct KeyBlock *BKE_keyblock_from_object_reference(struct Object *ob); +struct KeyBlock *BKE_keyblock_from_particles(struct ParticleSystem *psys); +struct KeyBlock *BKE_keyblock_from_particles_reference(struct ParticleSystem *psys); struct KeyBlock *BKE_keyblock_add(struct Key *key, const char *name); struct KeyBlock *BKE_keyblock_add_ctime(struct Key *key, const char *name, const bool do_force); @@ -83,10 +103,14 @@ typedef struct WeightsArrayCache { float **defgroup_weights; } WeightsArrayCache; -float **BKE_keyblock_get_per_block_weights(struct Object *ob, struct Key *key, struct WeightsArrayCache *cache); +float **BKE_keyblock_get_per_block_object_weights(struct Object *ob, struct Key *key, struct WeightsArrayCache *cache); +float **BKE_keyblock_strands_get_per_block_weights(struct Strands *strands, struct Key *key, struct WeightsArrayCache *cache); +float **BKE_keyblock_get_per_block_particle_weights(struct Object *ob, struct ParticleSystem *psys, float cfra, struct Key *key, struct WeightsArrayCache *cache); void BKE_keyblock_free_per_block_weights(struct Key *key, float **per_keyblock_weights, struct WeightsArrayCache *cache); void BKE_key_evaluate_relative(const int start, int end, const int tot, char *basispoin, struct Key *key, struct KeyBlock *actkb, float **per_keyblock_weights, const int mode); +void BKE_key_evaluate_strands_relative(const int start, int end, const int tot, char *basispoin, struct Key *key, struct KeyBlock *actkb, + float **per_keyblock_weights, const int mode); /* conversion functions */ /* Note: 'update_from' versions do not (re)allocate mem in kb, while 'convert_from' do. */ @@ -102,14 +126,22 @@ void BKE_keyblock_update_from_mesh(struct Mesh *me, struct KeyBlock *kb); void BKE_keyblock_convert_from_mesh(struct Mesh *me, struct KeyBlock *kb); void BKE_keyblock_convert_to_mesh(struct KeyBlock *kb, struct Mesh *me); +void BKE_keyblock_update_from_strands(struct Strands *strands, struct KeyBlock *kb, bool use_motion); +void BKE_keyblock_convert_from_strands(struct Strands *strands, struct Key *key, struct KeyBlock *kb, bool use_motion); +void BKE_keyblock_convert_to_strands(struct KeyBlock *kb, struct Strands *strands, bool use_motion); + void BKE_keyblock_update_from_vertcos(struct Object *ob, struct KeyBlock *kb, float (*vertCos)[3]); void BKE_keyblock_convert_from_vertcos(struct Object *ob, struct KeyBlock *kb, float (*vertCos)[3]); float (*BKE_keyblock_convert_to_vertcos(struct Object *ob, struct KeyBlock *kb))[3]; void BKE_keyblock_update_from_offset(struct Object *ob, struct KeyBlock *kb, float (*ofs)[3]); +void BKE_keyblock_convert_to_hair_keys(struct KeyBlock *kb, struct Object *ob, struct ParticleSystem *psys); +void BKE_keyblock_convert_from_hair_keys(struct Object *ob, struct ParticleSystem *psys, struct KeyBlock *kb); + /* other management */ bool BKE_keyblock_move(struct Object *ob, int org_index, int new_index); +bool BKE_keyblock_move_ex(struct Key *key, int *shapenr, int org_index, int new_index); bool BKE_keyblock_is_basis(struct Key *key, const int index); diff --git a/source/blender/blenkernel/BKE_library.h b/source/blender/blenkernel/BKE_library.h index 6ecc955e26c..63192cad11d 100644 --- a/source/blender/blenkernel/BKE_library.h +++ b/source/blender/blenkernel/BKE_library.h @@ -71,7 +71,7 @@ void id_clear_lib_data(struct Main *bmain, struct ID *id); struct ListBase *which_libbase(struct Main *mainlib, short type); -#define MAX_LIBARRAY 35 +#define MAX_LIBARRAY 36 int set_listbasepointers(struct Main *main, struct ListBase *lb[MAX_LIBARRAY]); void BKE_libblock_free(struct Main *bmain, void *idv); diff --git a/source/blender/blenkernel/BKE_main.h b/source/blender/blenkernel/BKE_main.h index ec654ea4b71..a0c67e055ae 100644 --- a/source/blender/blenkernel/BKE_main.h +++ b/source/blender/blenkernel/BKE_main.h @@ -94,6 +94,7 @@ typedef struct Main { ListBase movieclip; ListBase mask; ListBase linestyle; + ListBase cache_library; char id_tag_update[256]; diff --git a/source/blender/blenkernel/BKE_mesh.h b/source/blender/blenkernel/BKE_mesh.h index 287152baf0a..7cb121bcf5e 100644 --- a/source/blender/blenkernel/BKE_mesh.h +++ b/source/blender/blenkernel/BKE_mesh.h @@ -39,6 +39,7 @@ struct LinkNode; struct BLI_Stack; struct MemArena; struct BMesh; +struct DupliObjectData; struct Main; struct Mesh; struct MPoly; @@ -128,6 +129,7 @@ void BKE_mesh_split_faces(struct Mesh *mesh); struct Mesh *BKE_mesh_new_from_object(struct Main *bmain, struct Scene *sce, struct Object *ob, int apply_modifiers, int settings, int calc_tessface, int calc_undeformed); +struct Mesh *BKE_mesh_new_from_dupli_data(struct Main *bmain, struct DupliObjectData *data, bool calc_tessface, bool calc_undeformed); /* vertex level transformations & checks (no derived mesh) */ diff --git a/source/blender/blenkernel/BKE_mesh_sample.h b/source/blender/blenkernel/BKE_mesh_sample.h new file mode 100644 index 00000000000..6b489550847 --- /dev/null +++ b/source/blender/blenkernel/BKE_mesh_sample.h @@ -0,0 +1,68 @@ +/* + * ***** 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#ifndef __BKE_MESH_SAMPLE_H__ +#define __BKE_MESH_SAMPLE_H__ + +/** \file BKE_mesh_sample.h + * \ingroup bke + */ + +struct DerivedMesh; +struct Key; +struct KeyBlock; + +struct MSurfaceSample; + +/* ==== Evaluate ==== */ + +bool BKE_mesh_sample_eval(struct DerivedMesh *dm, const struct MSurfaceSample *sample, float loc[3], float nor[3], float tang[3]); +bool BKE_mesh_sample_shapekey(struct Key *key, struct KeyBlock *kb, const struct MSurfaceSample *sample, float loc[3]); + + +/* ==== Sampling ==== */ + +/* Storage descriptor to allow generic data storage by arbitrary algorithms */ +typedef struct MSurfaceSampleStorage { + bool (*store_sample)(void *data, int capacity, int index, const struct MSurfaceSample *sample); + void *data; + int capacity; + int free_data; +} MSurfaceSampleStorage; + +void BKE_mesh_sample_storage_single(struct MSurfaceSampleStorage *storage, struct MSurfaceSample *sample); +void BKE_mesh_sample_storage_array(struct MSurfaceSampleStorage *storage, struct MSurfaceSample *samples, int capacity); +void BKE_mesh_sample_storage_release(struct MSurfaceSampleStorage *storage); + +int BKE_mesh_sample_generate_random(struct MSurfaceSampleStorage *dst, struct DerivedMesh *dm, unsigned int seed, int totsample); + +typedef bool (*MeshSampleRayCallback)(void *userdata, float ray_start[3], float ray_end[3]); +int BKE_mesh_sample_generate_raycast(struct MSurfaceSampleStorage *dst, struct DerivedMesh *dm, MeshSampleRayCallback ray_cb, void *userdata, int totsample); + +/* ==== Utilities ==== */ + +struct ParticleSystem; +struct ParticleData; +struct BVHTreeFromMesh; + +bool BKE_mesh_sample_from_particle(struct MSurfaceSample *sample, struct ParticleSystem *psys, struct DerivedMesh *dm, struct ParticleData *pa); +bool BKE_mesh_sample_to_particle(struct MSurfaceSample *sample, struct ParticleSystem *psys, struct DerivedMesh *dm, struct BVHTreeFromMesh *bvhtree, struct ParticleData *pa); + +#endif /* __BKE_MESH_SAMPLE_H__ */ diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index d19b0c9167f..dc157f67b06 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -790,7 +790,8 @@ struct ShadeResult; #define SH_NODE_COMBXYZ 189 #define SH_NODE_OUTPUT_LINESTYLE 190 #define SH_NODE_UVALONGSTROKE 191 -#define SH_NODE_OPENVDB 192 +#define SH_NODE_TEX_POINTDENSITY 192 +#define SH_NODE_OPENVDB 193 /* custom defines options for Material node */ #define SH_NODE_MAT_DIFF 1 diff --git a/source/blender/blenkernel/BKE_object.h b/source/blender/blenkernel/BKE_object.h index 9482ec778d3..8d1fe7640ee 100644 --- a/source/blender/blenkernel/BKE_object.h +++ b/source/blender/blenkernel/BKE_object.h @@ -67,7 +67,7 @@ void BKE_object_update_base_layer(struct Scene *scene, struct Object *ob); void BKE_object_free(struct Object *ob); void BKE_object_free_ex(struct Object *ob, bool do_id_user); void BKE_object_free_derived_caches(struct Object *ob); -void BKE_object_free_caches(struct Object *object); +void BKE_object_free_caches(struct Object *object, bool free_smoke_sim); void BKE_object_modifier_hook_reset(struct Object *ob, struct HookModifierData *hmd); diff --git a/source/blender/blenkernel/BKE_object_deform.h b/source/blender/blenkernel/BKE_object_deform.h index e956815d6f7..fdcaf0c4ede 100644 --- a/source/blender/blenkernel/BKE_object_deform.h +++ b/source/blender/blenkernel/BKE_object_deform.h @@ -33,10 +33,12 @@ extern "C" { #endif -struct Object; +struct bDeformGroup; +struct bFaceMap; struct ID; +struct ListBase; struct MDeformVert; -struct bDeformGroup; +struct Object; /* General vgroup operations */ void BKE_object_defgroup_remap_update_users(struct Object *ob, int *map); @@ -53,7 +55,6 @@ bool BKE_object_defgroup_clear_all(struct Object *ob, const bool use_selection); void BKE_object_defgroup_remove(struct Object *ob, struct bDeformGroup *defgroup); void BKE_object_defgroup_remove_all(struct Object *ob); - /* Select helpers */ enum eVGroupSelect; bool *BKE_object_defgroup_subset_from_select_type( diff --git a/source/blender/blenkernel/BKE_particle.h b/source/blender/blenkernel/BKE_particle.h index e03789f502b..f8ddb33b0ba 100644 --- a/source/blender/blenkernel/BKE_particle.h +++ b/source/blender/blenkernel/BKE_particle.h @@ -63,6 +63,14 @@ struct BVHTreeRay; struct BVHTreeRayHit; struct EdgeHash; +/* XXX disabled for now due to stability issues and limited usefulness */ +//#define USE_PARTICLE_PREVIEW +/* XXX disabled because this requires sorting of children, + * but during render children are constantly recreated based on the deformed mesh, + * which leads to different indices every time and therefore different randomization values. + */ +//#define USE_PARTICLE_HULL_DRAWING + #define PARTICLE_P ParticleData * pa; int p #define LOOP_PARTICLES for (p = 0, pa = psys->particles; p < psys->totpart; p++, pa++) #define LOOP_EXISTING_PARTICLES for (p = 0, pa = psys->particles; p < psys->totpart; p++, pa++) if (!(pa->flag & PARS_UNEXIST)) @@ -110,6 +118,7 @@ typedef struct ParticleTexture { float damp, gravity, field; /* used in physics */ float length, clump, kink_freq, kink_amp, effector; /* used in path caching */ float rough1, rough2, roughe; /* used in path caching */ + float color[3]; } ParticleTexture; typedef struct ParticleSeam { @@ -124,7 +133,7 @@ typedef struct ParticleCacheKey { float rot[4]; float col[3]; float time; - int segments; + int segments, hull_parent; } ParticleCacheKey; typedef struct ParticleThreadContext { @@ -242,6 +251,25 @@ typedef struct ParticleDrawData { int totpoint, totve; } ParticleDrawData; +typedef struct ParticleInterpolationData { + struct HairKey *hkey[2]; + + struct DerivedMesh *dm; + struct MVert *mvert[2]; + + int keyed; + struct ParticleKey *kkey[2]; + + struct PointCache *cache; + struct PTCacheMem *pm; + + struct PTCacheEditPoint *epoint; + struct PTCacheEditKey *ekey[2]; + + float birthtime, dietime; + int bspline; +} ParticleInterpolationData; + #define PARTICLE_DRAW_DATA_UPDATED 1 #define PSYS_FRAND_COUNT 1024 @@ -296,6 +324,8 @@ bool psys_check_edited(struct ParticleSystem *psys); void psys_check_group_weights(struct ParticleSettings *part); int psys_uses_gravity(struct ParticleSimulationData *sim); +struct KeyBlock *BKE_psys_insert_shape_key(struct Scene *scene, struct Object *ob, struct ParticleSystem *psys, const char *name, const bool from_mix); + /* free */ void BKE_particlesettings_free(struct ParticleSettings *part); void psys_free_path_cache(struct ParticleSystem *psys, struct PTCacheEdit *edit); @@ -324,6 +354,7 @@ void BKE_particlesettings_make_local(struct ParticleSettings *part); void psys_reset(struct ParticleSystem *psys, int mode); +bool psys_hair_update_preview(struct ParticleSimulationData *sim); void psys_find_parents(struct ParticleSimulationData *sim); void psys_cache_paths(struct ParticleSimulationData *sim, float cfra); @@ -337,6 +368,10 @@ float psys_get_child_size(struct ParticleSystem *psys, struct ChildParticle *cpa void psys_get_particle_on_path(struct ParticleSimulationData *sim, int pa_num, struct ParticleKey *state, const bool vel); int psys_get_particle_state(struct ParticleSimulationData *sim, int p, struct ParticleKey *state, int always); +/* interpolation */ +void init_particle_interpolation(struct Object *ob, struct ParticleSystem *psys, struct ParticleData *pa, struct ParticleInterpolationData *pind); +void do_particle_interpolation(struct ParticleSystem *psys, int p, struct ParticleData *pa, float t, struct ParticleInterpolationData *pind, struct ParticleKey *result); + /* child paths */ void BKE_particlesettings_clump_curve_init(struct ParticleSettings *part); void BKE_particlesettings_rough_curve_init(struct ParticleSettings *part); @@ -393,18 +428,22 @@ void psys_vec_rot_to_face(struct DerivedMesh *dm, struct ParticleData *pa, float void psys_mat_hair_to_object(struct Object *ob, struct DerivedMesh *dm, short from, struct ParticleData *pa, float hairmat[4][4]); void psys_mat_hair_to_global(struct Object *ob, struct DerivedMesh *dm, short from, struct ParticleData *pa, float hairmat[4][4]); void psys_mat_hair_to_orco(struct Object *ob, struct DerivedMesh *dm, short from, struct ParticleData *pa, float hairmat[4][4]); +void psys_child_mat_to_object(struct Object *ob, struct ParticleSystem *psys, struct ParticleSystemModifierData *psmd, struct ChildParticle *cpa, float hairmat[4][4]); float psys_get_dietime_from_cache(struct PointCache *cache, int index); void psys_free_pdd(struct ParticleSystem *psys); float *psys_cache_vgroup(struct DerivedMesh *dm, struct ParticleSystem *psys, int vgroup); +bool particle_mtex_eval(struct ParticleSimulationData *sim, MTex *mtex, ParticleData *pa, float cfra, float *value, float rgba[4]); void psys_get_texture(struct ParticleSimulationData *sim, struct ParticleData *pa, struct ParticleTexture *ptex, int event, float cfra); +float psys_get_texture_shapefac(struct ParticleSimulationData *sim, struct ParticleData *pa, float cfra, const char *shapekey); void psys_interpolate_face(struct MVert *mvert, struct MFace *mface, struct MTFace *tface, float (*orcodata)[3], float w[4], float vec[3], float nor[3], float utan[3], float vtan[3], float orco[3], float ornor[3]); float psys_particle_value_from_verts(struct DerivedMesh *dm, short from, struct ParticleData *pa, float *values); void psys_get_from_key(struct ParticleKey *key, float loc[3], float vel[3], float rot[4], float *time); +int psys_get_index_on_dm(struct ParticleSystem *psys, struct DerivedMesh *dm, ParticleData *pa, int *mapindex, float mapfw[4]); /* BLI_bvhtree_ray_cast callback */ void BKE_psys_collision_neartest_cb(void *userdata, int index, const struct BVHTreeRay *ray, struct BVHTreeRayHit *hit); diff --git a/source/blender/blenkernel/BKE_pointcache.h b/source/blender/blenkernel/BKE_pointcache.h index a3ffad1f66b..3d67b91d767 100644 --- a/source/blender/blenkernel/BKE_pointcache.h +++ b/source/blender/blenkernel/BKE_pointcache.h @@ -267,10 +267,8 @@ void BKE_ptcache_id_from_rigidbody(PTCacheID *pid, struct Object *ob, struct Rig void BKE_ptcache_ids_from_object(struct ListBase *lb, struct Object *ob, struct Scene *scene, int duplis); -/***************** Global funcs ****************************/ -void BKE_ptcache_remove(void); - /************ ID specific functions ************************/ +void BKE_ptcache_id_clear_ex(PTCacheID *pid, int mode, unsigned int cfra, bool allow_file_delete); void BKE_ptcache_id_clear(PTCacheID *id, int mode, unsigned int cfra); int BKE_ptcache_id_exist(PTCacheID *id, int cfra); int BKE_ptcache_id_reset(struct Scene *scene, PTCacheID *id, int mode); @@ -314,10 +312,10 @@ void BKE_ptcache_quick_cache_all(struct Main *bmain, struct Scene *scene); void BKE_ptcache_bake(struct PTCacheBaker *baker); /* Convert disk cache to memory cache. */ -void BKE_ptcache_disk_to_mem(struct PTCacheID *pid); +void BKE_ptcache_disk_to_mem(struct PTCacheID *pid, bool clear); /* Convert memory cache to disk cache. */ -void BKE_ptcache_mem_to_disk(struct PTCacheID *pid); +void BKE_ptcache_mem_to_disk(struct PTCacheID *pid, bool clear); /* Convert disk cache to memory cache and vice versa. Clears the cache that was converted. */ void BKE_ptcache_toggle_disk_cache(struct PTCacheID *pid); diff --git a/source/blender/blenkernel/BKE_scene.h b/source/blender/blenkernel/BKE_scene.h index 442a43871c1..e6b19296068 100644 --- a/source/blender/blenkernel/BKE_scene.h +++ b/source/blender/blenkernel/BKE_scene.h @@ -40,6 +40,7 @@ extern "C" { struct AviCodecData; struct Base; struct EvaluationContext; +struct Group; struct Main; struct Object; struct QuicktimeCodecData; @@ -119,6 +120,11 @@ void BKE_scene_frame_set(struct Scene *scene, double cfra); void BKE_scene_update_tagged(struct EvaluationContext *eval_ctx, struct Main *bmain, struct Scene *sce); void BKE_scene_update_for_newframe(struct EvaluationContext *eval_ctx, struct Main *bmain, struct Scene *sce, unsigned int lay); void BKE_scene_update_for_newframe_ex(struct EvaluationContext *eval_ctx, struct Main *bmain, struct Scene *sce, unsigned int lay, bool do_invisible_flush); +void BKE_scene_update_group_for_newframe(struct EvaluationContext *eval_ctx, + struct Main *bmain, + struct Scene *scene, + struct Group *group, + unsigned int lay); struct SceneRenderLayer *BKE_scene_add_render_layer(struct Scene *sce, const char *name); bool BKE_scene_remove_render_layer(struct Main *main, struct Scene *scene, struct SceneRenderLayer *srl); diff --git a/source/blender/blenkernel/BKE_screen.h b/source/blender/blenkernel/BKE_screen.h index 48616418e67..60fd402ef4b 100644 --- a/source/blender/blenkernel/BKE_screen.h +++ b/source/blender/blenkernel/BKE_screen.h @@ -95,6 +95,9 @@ typedef struct SpaceType { /* on startup, define dropboxes for spacetype+regions */ void (*dropboxes)(void); + /* on startup define areas with widget types */ + void (*widgets)(void); + /* return context data */ int (*context)(const struct bContext *, const char *, struct bContextDataResult *); diff --git a/source/blender/blenkernel/BKE_sequencer.h b/source/blender/blenkernel/BKE_sequencer.h index f73548373ef..5f159b14fb8 100644 --- a/source/blender/blenkernel/BKE_sequencer.h +++ b/source/blender/blenkernel/BKE_sequencer.h @@ -227,6 +227,7 @@ void BKE_sequencer_base_clipboard_pointers_restore(struct ListBase *seqbase, str void BKE_sequence_free(struct Scene *scene, struct Sequence *seq); void BKE_sequence_free_anim(struct Sequence *seq); const char *BKE_sequence_give_name(struct Sequence *seq); +ListBase *BKE_sequence_seqbase_get(struct Sequence *seq, int *r_offset); void BKE_sequence_calc(struct Scene *scene, struct Sequence *seq); void BKE_sequence_calc_disp(struct Scene *scene, struct Sequence *seq); void BKE_sequence_reload_new_file(struct Scene *scene, struct Sequence *seq, const bool lock_range); diff --git a/source/blender/blenkernel/BKE_strands.h b/source/blender/blenkernel/BKE_strands.h new file mode 100644 index 00000000000..2ec1363727b --- /dev/null +++ b/source/blender/blenkernel/BKE_strands.h @@ -0,0 +1,358 @@ +/* + * Copyright 2015, Blender Foundation. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __BKE_STRANDS_H__ +#define __BKE_STRANDS_H__ + +#include "BLI_utildefines.h" + +#include "DNA_strands_types.h" + +struct StrandChildIterator; + +struct Strands *BKE_strands_new(int strands, int verts); +struct Strands *BKE_strands_copy(struct Strands *strands); +void BKE_strands_free(struct Strands *strands); + +void BKE_strands_add_motion_state(struct Strands *strands); +void BKE_strands_remove_motion_state(struct Strands *strands); +void BKE_strands_state_copy_rest_positions(struct Strands *strands); +void BKE_strands_state_clear_velocities(struct Strands *strands); + +void BKE_strands_ensure_normals(struct Strands *strands); + +void BKE_strands_get_minmax(struct Strands *strands, float min[3], float max[3], bool use_motion_state); + + +struct StrandsChildren *BKE_strands_children_new(int strands, int verts); +struct StrandsChildren *BKE_strands_children_copy(struct StrandsChildren *strands); +void BKE_strands_children_free(struct StrandsChildren *strands); + +void BKE_strands_children_add_uvs(struct StrandsChildren *strands, int num_layers); +void BKE_strands_children_add_vcols(struct StrandsChildren *strands, int num_layers); + +void BKE_strands_children_deform(struct StrandsChildren *strands, struct Strands *parents, bool use_motion); + +void BKE_strands_children_ensure_normals(struct StrandsChildren *strands); + +void BKE_strands_children_get_minmax(struct StrandsChildren *strands, float min[3], float max[3]); + +/* ------------------------------------------------------------------------- */ +/* Strand Curves Iterator */ + +typedef struct StrandIterator { + int index, tot; + struct StrandsCurve *curve; + struct StrandsVertex *verts; + struct StrandsMotionState *state; +} StrandIterator; + +BLI_INLINE void BKE_strand_iter_init(StrandIterator *iter, Strands *strands) +{ + iter->tot = strands->totcurves; + iter->index = 0; + iter->curve = strands->curves; + iter->verts = strands->verts; + iter->state = strands->state; +} + +BLI_INLINE bool BKE_strand_iter_valid(StrandIterator *iter) +{ + return iter->index < iter->tot; +} + +BLI_INLINE void BKE_strand_iter_next(StrandIterator *iter) +{ + const int numverts = iter->curve->numverts; + + ++iter->index; + ++iter->curve; + iter->verts += numverts; + if (iter->state) + iter->state += numverts; +} + +BLI_INLINE size_t BKE_strand_iter_curve_offset(Strands *strands, StrandIterator *iter) +{ + return iter->curve - strands->curves; +} + +BLI_INLINE size_t BKE_strand_iter_vertex_offset(Strands *strands, StrandIterator *iter) +{ + return iter->verts - strands->verts; +} + +/* ------------------------------------------------------------------------- */ +/* Strand Vertices Iterator */ + +typedef struct StrandVertexIterator { + int index, tot; + StrandsVertex *vertex; + StrandsMotionState *state; +} StrandVertexIterator; + +BLI_INLINE void BKE_strand_vertex_iter_init(StrandVertexIterator *iter, StrandIterator *strand_iter) +{ + iter->tot = strand_iter->curve->numverts; + iter->index = 0; + iter->vertex = strand_iter->verts; + iter->state = strand_iter->state; +} + +BLI_INLINE bool BKE_strand_vertex_iter_valid(StrandVertexIterator *iter) +{ + return iter->index < iter->tot; +} + +BLI_INLINE void BKE_strand_vertex_iter_next(StrandVertexIterator *iter) +{ + ++iter->vertex; + if (iter->state) + ++iter->state; + ++iter->index; +} + +BLI_INLINE size_t BKE_strand_vertex_iter_vertex_offset(Strands *strands, StrandVertexIterator *iter) +{ + return iter->vertex - strands->verts; +} + +/* ------------------------------------------------------------------------- */ +/* Strand Edges Iterator */ + +typedef struct StrandEdgeIterator { + int index, tot; + StrandsVertex *vertex0, *vertex1; + StrandsMotionState *state0, *state1; +} StrandEdgeIterator; + +BLI_INLINE void BKE_strand_edge_iter_init(StrandEdgeIterator *iter, StrandIterator *strand_iter) +{ + iter->tot = strand_iter->curve->numverts - 1; + iter->index = 0; + iter->vertex0 = strand_iter->verts; + iter->state0 = strand_iter->state; + iter->vertex1 = strand_iter->verts + 1; + iter->state1 = strand_iter->state + 1; +} + +BLI_INLINE bool BKE_strand_edge_iter_valid(StrandEdgeIterator *iter) +{ + return iter->index < iter->tot; +} + +BLI_INLINE void BKE_strand_edge_iter_next(StrandEdgeIterator *iter) +{ + ++iter->vertex0; + ++iter->vertex1; + if (iter->state0) { + ++iter->state0; + ++iter->state1; + } + ++iter->index; +} + +BLI_INLINE size_t BKE_strand_edge_iter_vertex0_offset(Strands *strands, StrandEdgeIterator *iter) +{ + return iter->vertex0 - strands->verts; +} + +BLI_INLINE size_t BKE_strand_edge_iter_vertex1_offset(Strands *strands, StrandEdgeIterator *iter) +{ + return iter->vertex1 - strands->verts; +} + +/* ------------------------------------------------------------------------- */ +/* Strand Bends Iterator */ + +typedef struct StrandBendIterator { + int index, tot; + StrandsVertex *vertex0, *vertex1, *vertex2; + StrandsMotionState *state0, *state1, *state2; +} StrandBendIterator; + +BLI_INLINE void BKE_strand_bend_iter_init(StrandBendIterator *iter, StrandIterator *strand_iter) +{ + iter->tot = strand_iter->curve->numverts - 2; + iter->index = 0; + iter->vertex0 = strand_iter->verts; + iter->state0 = strand_iter->state; + iter->vertex1 = strand_iter->verts + 1; + iter->state1 = strand_iter->state + 1; + iter->vertex2 = strand_iter->verts + 2; + iter->state2 = strand_iter->state + 2; +} + +BLI_INLINE bool BKE_strand_bend_iter_valid(StrandBendIterator *iter) +{ + return iter->index < iter->tot; +} + +BLI_INLINE void BKE_strand_bend_iter_next(StrandBendIterator *iter) +{ + ++iter->vertex0; + ++iter->vertex1; + ++iter->vertex2; + if (iter->state0) { + ++iter->state0; + ++iter->state1; + ++iter->state2; + } + ++iter->index; +} + +BLI_INLINE size_t BKE_strand_bend_iter_vertex0_offset(Strands *strands, StrandBendIterator *iter) +{ + return iter->vertex0 - strands->verts; +} + +BLI_INLINE size_t BKE_strand_bend_iter_vertex1_offset(Strands *strands, StrandBendIterator *iter) +{ + return iter->vertex1 - strands->verts; +} + +BLI_INLINE size_t BKE_strand_bend_iter_vertex2_offset(Strands *strands, StrandBendIterator *iter) +{ + return iter->vertex2 - strands->verts; +} + +void BKE_strand_bend_iter_transform_rest(StrandBendIterator *iter, float mat[3][3]); +void BKE_strand_bend_iter_transform_state(StrandBendIterator *iter, float mat[3][3]); + +/* ------------------------------------------------------------------------- */ +/* Strand Child Curves Iterator */ + +typedef struct StrandChildIterator { + int index, tot, numuv, numvcol; + StrandsChildCurve *curve; + StrandsChildCurveUV *curve_uv; + StrandsChildCurveVCol *curve_vcol; + StrandsChildVertex *verts; +} StrandChildIterator; + +BLI_INLINE void BKE_strand_child_iter_init(StrandChildIterator *iter, StrandsChildren *strands) +{ + iter->tot = strands->totcurves; + iter->numuv = strands->numuv; + iter->numvcol = strands->numvcol; + iter->index = 0; + + iter->curve = strands->curves; + iter->curve_uv = strands->curve_uvs; + iter->curve_vcol = strands->curve_vcols; + iter->verts = strands->verts; +} + +BLI_INLINE bool BKE_strand_child_iter_valid(StrandChildIterator *iter) +{ + return iter->index < iter->tot; +} + +BLI_INLINE void BKE_strand_child_iter_next(StrandChildIterator *iter) +{ + const int numverts = iter->curve->numverts; + + ++iter->index; + ++iter->curve; + if (iter->curve_uv) + iter->curve_uv += iter->numuv; + if (iter->curve_vcol) + iter->curve_vcol += iter->numvcol; + iter->verts += numverts; +} + +BLI_INLINE size_t BKE_strand_child_iter_curve_offset(StrandsChildren *strands, StrandChildIterator *iter) +{ + return iter->curve - strands->curves; +} + +BLI_INLINE size_t BKE_strand_child_iter_vertex_offset(StrandsChildren *strands, StrandChildIterator *iter) +{ + return iter->verts - strands->verts; +} + +/* ------------------------------------------------------------------------- */ +/* Strand Child Vertices Iterator */ + +typedef struct StrandChildVertexIterator { + int index, tot; + StrandsChildVertex *vertex; +} StrandChildVertexIterator; + +BLI_INLINE void BKE_strand_child_vertex_iter_init(StrandChildVertexIterator *iter, StrandChildIterator *strand_iter) +{ + iter->tot = strand_iter->curve->numverts; + iter->index = 0; + iter->vertex = strand_iter->verts; +} + +BLI_INLINE bool BKE_strand_child_vertex_iter_valid(StrandChildVertexIterator *iter) +{ + return iter->index < iter->tot; +} + +BLI_INLINE void BKE_strand_child_vertex_iter_next(StrandChildVertexIterator *iter) +{ + ++iter->vertex; + ++iter->index; +} + +BLI_INLINE size_t BKE_strand_child_vertex_iter_vertex_offset(StrandsChildren *strands, StrandChildVertexIterator *iter) +{ + return iter->vertex - strands->verts; +} + +/* ------------------------------------------------------------------------- */ +/* Strand Child Edges Iterator */ + +typedef struct StrandChildEdgeIterator { + int index, tot; + StrandsChildVertex *vertex0, *vertex1; +} StrandChildEdgeIterator; + +BLI_INLINE void BKE_strand_child_edge_iter_init(StrandChildEdgeIterator *iter, StrandChildIterator *strand_iter) +{ + iter->tot = strand_iter->curve->numverts - 1; + iter->index = 0; + iter->vertex0 = strand_iter->verts; + iter->vertex1 = strand_iter->verts + 1; +} + +BLI_INLINE bool BKE_strand_child_edge_iter_valid(StrandChildEdgeIterator *iter) +{ + return iter->index < iter->tot; +} + +BLI_INLINE void BKE_strand_child_edge_iter_next(StrandChildEdgeIterator *iter) +{ + ++iter->vertex0; + ++iter->vertex1; + ++iter->index; +} + +BLI_INLINE size_t BKE_strand_child_edge_iter_vertex0_offset(StrandsChildren *strands, StrandChildEdgeIterator *iter) +{ + return iter->vertex0 - strands->verts; +} + +BLI_INLINE size_t BKE_strand_child_edge_iter_vertex1_offset(StrandsChildren *strands, StrandChildEdgeIterator *iter) +{ + return iter->vertex1 - strands->verts; +} + +#endif /* __BKE_STRANDS_H__ */ diff --git a/source/blender/blenkernel/BKE_texture.h b/source/blender/blenkernel/BKE_texture.h index 4e5214bc364..95918b9ca0b 100644 --- a/source/blender/blenkernel/BKE_texture.h +++ b/source/blender/blenkernel/BKE_texture.h @@ -115,6 +115,7 @@ void BKE_texture_envmap_free(struct EnvMap *env); struct EnvMap *BKE_texture_envmap_add(void); struct EnvMap *BKE_texture_envmap_copy(struct EnvMap *env); +void BKE_texture_pointdensity_init_data(struct PointDensity *pd); void BKE_texture_pointdensity_free_data(struct PointDensity *pd); void BKE_texture_pointdensity_free(struct PointDensity *pd); struct PointDensity *BKE_texture_pointdensity_add(void); diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt index 59a2c7271de..b4e37d2061c 100644 --- a/source/blender/blenkernel/CMakeLists.txt +++ b/source/blender/blenkernel/CMakeLists.txt @@ -38,6 +38,7 @@ set(INC ../modifiers ../nodes ../physics + ../pointcache ../render/extern/include ../../../intern/ghost ../../../intern/guardedalloc @@ -78,6 +79,7 @@ set(SRC intern/brush.c intern/bullet.c intern/bvhutils.c + intern/cache_library.c intern/camera.c intern/cdderivedmesh.c intern/cloth.c @@ -97,7 +99,9 @@ set(SRC intern/editderivedmesh.c intern/editmesh.c intern/editmesh_bvh.c + intern/editstrands.c intern/effect.c + intern/facemap.c intern/fcurve.c intern/fluidsim.c intern/fmodifier.c @@ -127,6 +131,7 @@ set(SRC intern/mesh_evaluate.c intern/mesh_mapping.c intern/mesh_remap.c + intern/mesh_sample.c intern/mesh_validate.c intern/modifier.c intern/modifiers_bmesh.c @@ -145,6 +150,7 @@ set(SRC intern/particle.c intern/particle_child.c intern/particle_distribute.c + intern/particle_interpolate.c intern/particle_system.c intern/pbvh.c intern/pbvh_bmesh.c @@ -165,6 +171,7 @@ set(SRC intern/softbody.c intern/sound.c intern/speaker.c + intern/strands.c intern/subsurf_ccg.c intern/suggestions.c intern/text.c @@ -198,6 +205,7 @@ set(SRC BKE_brush.h BKE_bullet.h BKE_bvhutils.h + BKE_cache_library.h BKE_camera.h BKE_ccg.h BKE_cdderivedmesh.h @@ -215,9 +223,11 @@ set(SRC BKE_depsgraph.h BKE_displist.h BKE_dynamicpaint.h + BKE_editstrands.h BKE_editmesh.h BKE_editmesh_bvh.h BKE_effect.h + BKE_facemap.h BKE_fcurve.h BKE_fluidsim.h BKE_font.h @@ -244,6 +254,7 @@ set(SRC BKE_mesh.h BKE_mesh_mapping.h BKE_mesh_remap.h + BKE_mesh_sample.h BKE_modifier.h BKE_movieclip.h BKE_multires.h @@ -271,6 +282,7 @@ set(SRC BKE_softbody.h BKE_sound.h BKE_speaker.h + BKE_strands.h BKE_subsurf.h BKE_suggestions.h BKE_text.h diff --git a/source/blender/blenkernel/SConscript b/source/blender/blenkernel/SConscript index b8ce53721e5..40719648a23 100644 --- a/source/blender/blenkernel/SConscript +++ b/source/blender/blenkernel/SConscript @@ -67,6 +67,7 @@ incs = [ '../modifiers', '../nodes', '../physics', + '../pointcache', '../render/extern/include', '../windowmanager', env['BF_ZLIB_INC'], diff --git a/source/blender/blenkernel/intern/bpath.c b/source/blender/blenkernel/intern/bpath.c index 76544e5ed42..8248ea2fa37 100644 --- a/source/blender/blenkernel/intern/bpath.c +++ b/source/blender/blenkernel/intern/bpath.c @@ -124,6 +124,7 @@ static bool bpath_relative_convert_visit_cb(void *userdata, char *path_dst, cons strcpy(path_dst, path_src); BLI_path_rel(path_dst, data->basedir); if (BLI_path_is_rel(path_dst)) { + BKE_reportf(data->reports, RPT_INFO, "Path '%s' made relative to '%s'", path_src, path_dst); data->count_changed++; } else { @@ -167,6 +168,7 @@ static bool bpath_absolute_convert_visit_cb(void *userdata, char *path_dst, cons strcpy(path_dst, path_src); BLI_path_abs(path_dst, data->basedir); if (BLI_path_is_rel(path_dst) == false) { + BKE_reportf(data->reports, RPT_INFO, "Path '%s' made absolute to '%s'", path_src, path_dst); data->count_changed++; } else { @@ -497,10 +499,6 @@ void BKE_bpath_traverse_id(Main *bmain, ID *id, BPathVisitor visit_cb, const int BPATH_TRAVERSE_POINTCACHE(smd->domain->ptcaches[0]); } } - else if (md->type == eModifierType_Cloth) { - ClothModifierData *clmd = (ClothModifierData *) md; - BPATH_TRAVERSE_POINTCACHE(clmd->ptcaches); - } else if (md->type == eModifierType_Ocean) { OceanModifierData *omd = (OceanModifierData *) md; rewrite_path_fixed(omd->cachepath, visit_cb, absbase, bpath_user_data); diff --git a/source/blender/blenkernel/intern/brush.c b/source/blender/blenkernel/intern/brush.c index 2464b3b2668..4c75abf2ce9 100644 --- a/source/blender/blenkernel/intern/brush.c +++ b/source/blender/blenkernel/intern/brush.c @@ -68,7 +68,7 @@ static void brush_defaults(Brush *brush) brush->blend = 0; brush->flag = 0; - brush->ob_mode = OB_MODE_ALL_PAINT; + brush->ob_mode = OB_MODE_ALL_BRUSH; /* BRUSH SCULPT TOOL SETTINGS */ brush->weight = 1.0f; /* weight of brush 0 - 1.0 */ diff --git a/source/blender/blenkernel/intern/cache_library.c b/source/blender/blenkernel/intern/cache_library.c new file mode 100644 index 00000000000..370a2fc1887 --- /dev/null +++ b/source/blender/blenkernel/intern/cache_library.c @@ -0,0 +1,2256 @@ +/* + * ***** 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2015 by NaN Holding BV. + * All rights reserved. + * + * Contributor(s): Lukas Toenne + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/blenkernel/intern/cache_library.c + * \ingroup bke + */ + +#include <string.h> + +#include "MEM_guardedalloc.h" + +#include "BLI_blenlib.h" +#include "BLI_fileops.h" +#include "BLI_ghash.h" +#include "BLI_listbase.h" +#include "BLI_math.h" +#include "BLI_path_util.h" +#include "BLI_string.h" +#include "BLI_utildefines.h" + +#include "DNA_cache_library_types.h" +#include "DNA_group_types.h" +#include "DNA_key_types.h" +#include "DNA_modifier_types.h" +#include "DNA_object_force.h" +#include "DNA_object_types.h" +#include "DNA_particle_types.h" +#include "DNA_scene_types.h" + +#include "BKE_anim.h" +#include "BKE_bvhutils.h" +#include "BKE_cache_library.h" +#include "BKE_cdderivedmesh.h" +#include "BKE_colortools.h" +#include "BKE_context.h" +#include "BKE_depsgraph.h" +#include "BKE_DerivedMesh.h" +#include "BKE_editstrands.h" +#include "BKE_effect.h" +#include "BKE_global.h" +#include "BKE_group.h" +#include "BKE_idprop.h" +#include "BKE_key.h" +#include "BKE_library.h" +#include "BKE_main.h" +#include "BKE_modifier.h" +#include "BKE_scene.h" +#include "BKE_screen.h" +#include "BKE_strands.h" + +#include "BLF_translation.h" + +#include "PTC_api.h" + +#include "BPH_mass_spring.h" + +CacheLibrary *BKE_cache_library_add(Main *bmain, const char *name) +{ + CacheLibrary *cachelib; + char basename[MAX_NAME]; + + cachelib = BKE_libblock_alloc(bmain, ID_CL, name); + + BLI_strncpy(basename, cachelib->id.name+2, sizeof(basename)); + BLI_filename_make_safe(basename); + BLI_snprintf(cachelib->output_filepath, sizeof(cachelib->output_filepath), "//cache/%s.%s", basename, PTC_get_default_archive_extension()); + + cachelib->source_mode = CACHE_LIBRARY_SOURCE_SCENE; + cachelib->display_mode = CACHE_LIBRARY_DISPLAY_MODIFIERS; + cachelib->display_flag = CACHE_LIBRARY_DISPLAY_MOTION | CACHE_LIBRARY_DISPLAY_CHILDREN; + + /* cache everything by default */ + cachelib->data_types = CACHE_TYPE_ALL; + + return cachelib; +} + +CacheLibrary *BKE_cache_library_copy(CacheLibrary *cachelib) +{ + CacheLibrary *cachelibn; + + cachelibn = BKE_libblock_copy(&cachelib->id); + + if (cachelibn->filter_group) + id_us_plus(&cachelibn->filter_group->id); + + { + CacheModifier *md; + BLI_listbase_clear(&cachelibn->modifiers); + for (md = cachelib->modifiers.first; md; md = md->next) { + BKE_cache_modifier_copy(cachelibn, md); + } + } + + cachelibn->archive_info = NULL; + + if (cachelib->id.lib) { + BKE_id_lib_local_paths(G.main, cachelib->id.lib, &cachelibn->id); + } + + return cachelibn; +} + +void BKE_cache_library_free(CacheLibrary *cachelib) +{ + BKE_cache_modifier_clear(cachelib); + + if (cachelib->filter_group) + id_us_min(&cachelib->filter_group->id); + + if (cachelib->archive_info) + BKE_cache_archive_info_free(cachelib->archive_info); +} + +void BKE_cache_library_unlink(CacheLibrary *UNUSED(cachelib)) +{ +} + +/* ========================================================================= */ + +const char *BKE_cache_item_name_prefix(int type) +{ + /* note: avoid underscores and the like here, + * the prefixes must be unique and safe when combined with arbitrary strings! + */ + switch (type) { + case CACHE_TYPE_OBJECT: return "OBJECT"; + case CACHE_TYPE_DERIVED_MESH: return "MESH"; + case CACHE_TYPE_HAIR: return "HAIR"; + case CACHE_TYPE_HAIR_PATHS: return "HAIRPATHS"; + case CACHE_TYPE_PARTICLES: return "PARTICLES"; + default: BLI_assert(false); return NULL; break; + } +} + +void BKE_cache_item_name(Object *ob, int type, int index, char *name) +{ + if (index >= 0) + sprintf(name, "%s_%s_%d", BKE_cache_item_name_prefix(type), ob->id.name+2, index); + else + sprintf(name, "%s_%s", BKE_cache_item_name_prefix(type), ob->id.name+2); +} + +int BKE_cache_item_name_length(Object *ob, int type, int index) +{ + char *str_dummy = (char *)""; + if (index >= 0) + return BLI_snprintf(str_dummy, 0, "%s_%s_%d", BKE_cache_item_name_prefix(type), ob->id.name + 2, index); + else + return BLI_snprintf(str_dummy, 0, "%s_%s", BKE_cache_item_name_prefix(type), ob->id.name + 2); +} + +eCacheReadSampleResult BKE_cache_read_result(int ptc_result) +{ + switch (ptc_result) { + case PTC_READ_SAMPLE_INVALID: return CACHE_READ_SAMPLE_INVALID; + case PTC_READ_SAMPLE_EARLY: return CACHE_READ_SAMPLE_EARLY; + case PTC_READ_SAMPLE_LATE: return CACHE_READ_SAMPLE_LATE; + case PTC_READ_SAMPLE_EXACT: return CACHE_READ_SAMPLE_EXACT; + case PTC_READ_SAMPLE_INTERPOLATED: return CACHE_READ_SAMPLE_INTERPOLATED; + default: BLI_assert(false); break; /* should never happen, enums out of sync? */ + } + return CACHE_READ_SAMPLE_INVALID; +} + +bool BKE_cache_library_validate_item(CacheLibrary *cachelib, Object *ob, int type, int index) +{ + if (!cachelib) + return false; + + if (ELEM(type, CACHE_TYPE_DERIVED_MESH)) { + if (ob->type != OB_MESH) + return false; + } + else if (ELEM(type, CACHE_TYPE_PARTICLES, CACHE_TYPE_HAIR, CACHE_TYPE_HAIR_PATHS)) { + ParticleSystem *psys = BLI_findlink(&ob->particlesystem, index); + + if (!psys) + return false; + + if (ELEM(type, CACHE_TYPE_PARTICLES)) { + if (psys->part->type != PART_EMITTER) + return false; + } + + if (ELEM(type, CACHE_TYPE_HAIR, CACHE_TYPE_HAIR_PATHS)) { + if (psys->part->type != PART_HAIR) + return false; + } + } + + return true; +} + +/* ========================================================================= */ + +/* unused + * filtering now only tags objects to exclude them from storing data, + * but does not actually remove them from the duplilist. + */ +#if 0 +void BKE_cache_library_filter_duplilist(CacheLibrary *cachelib, ListBase *duplilist) +{ + if (cachelib->filter_group) { + GroupObject *gob; + + /* tag only filter group objects as valid */ + BKE_main_id_tag_idcode(G.main, ID_OB, false); + for (gob = cachelib->filter_group->gobject.first; gob; gob = gob->next) + gob->ob->id.flag |= LIB_DOIT; + } + else { + /* all objects valid */ + BKE_main_id_tag_idcode(G.main, ID_OB, true); + } + + { + /* remove invalid duplis */ + DupliObject *dob, *dob_next; + for (dob = duplilist->first; dob; dob = dob_next) { + dob_next = dob->next; + + if (!(dob->ob->id.flag & LIB_DOIT)) { + BLI_remlink(duplilist, dob); + MEM_freeN(dob); + } + } + } + + /* clear LIB_DOIT tags */ + BKE_main_id_tag_idcode(G.main, ID_OB, false); +} +#endif + +void BKE_cache_library_tag_used_objects(CacheLibrary *cachelib) +{ + if (cachelib->filter_group) { + GroupObject *gob; + + /* tag only filter group objects as valid */ + BKE_main_id_tag_idcode(G.main, ID_OB, false); + for (gob = cachelib->filter_group->gobject.first; gob; gob = gob->next) + gob->ob->id.flag |= LIB_DOIT; + } + else { + /* all objects valid */ + BKE_main_id_tag_idcode(G.main, ID_OB, true); + } +} + +/* ========================================================================= */ + +static IDProperty *cache_library_get_metadata(CacheLibrary *cachelib, const char *name, bool create) +{ + IDProperty *idprops = IDP_GetProperties((ID *)cachelib, create); + IDProperty *metadata = NULL; + if (idprops) { + metadata = IDP_GetPropertyFromGroup(idprops, name); + if (!metadata && create) { + IDPropertyTemplate val; + val.i = 0; + metadata = IDP_New(IDP_GROUP, &val, name); + IDP_AddToGroup(idprops, metadata); + } + } + return metadata; +} + +IDProperty *BKE_cache_library_get_input_metadata(CacheLibrary *cachelib, bool create) +{ + return cache_library_get_metadata(cachelib, "input_metadata", create); +} + +IDProperty *BKE_cache_library_get_output_metadata(CacheLibrary *cachelib, bool create) +{ + return cache_library_get_metadata(cachelib, "output_metadata", create); +} + +BLI_INLINE bool path_is_dirpath(const char *path) +{ + /* last char is a slash? */ + const char *last_slash = BLI_last_slash(path); + return last_slash ? (*(last_slash + 1) == '\0') : false; +} + +bool BKE_cache_archive_path_test(CacheLibrary *cachelib, const char *path) +{ + if (BLI_path_is_rel(path)) { + if (!(G.relbase_valid || cachelib->id.lib)) + return false; + } + + return true; + +} + +void BKE_cache_archive_path_ex(const char *path, Library *lib, const char *default_filename, char *result, int max) +{ + char abspath[FILE_MAX]; + + result[0] = '\0'; + + if (BLI_path_is_rel(path)) { + if (G.relbase_valid || lib) { + const char *relbase = lib ? lib->filepath : G.main->name; + + BLI_strncpy(abspath, path, sizeof(abspath)); + BLI_path_abs(abspath, relbase); + } + else { + /* can't construct a valid path */ + return; + } + } + else { + BLI_strncpy(abspath, path, sizeof(abspath)); + } + + if (abspath[0] != '\0') { + if (path_is_dirpath(abspath) || BLI_is_dir(abspath)) { + if (default_filename && default_filename[0] != '\0') + BLI_join_dirfile(result, max, abspath, default_filename); + } + else { + BLI_strncpy(result, abspath, max); + } + } +} + +void BKE_cache_archive_input_path(CacheLibrary *cachelib, char *result, int max) +{ + BKE_cache_archive_path_ex(cachelib->input_filepath, cachelib->id.lib, NULL, result, max); +} + +void BKE_cache_archive_output_path(CacheLibrary *cachelib, char *result, int max) +{ + BKE_cache_archive_path_ex(cachelib->output_filepath, cachelib->id.lib, cachelib->id.name+2, result, max); +} + +static bool has_active_cache(CacheLibrary *cachelib) +{ + const bool is_baking = cachelib->flag & CACHE_LIBRARY_BAKING; + + /* don't read results from output archive when baking */ + if (!is_baking) { + if (cachelib->display_mode == CACHE_LIBRARY_DISPLAY_RESULT) { + return true; + } + } + + if (ELEM(cachelib->source_mode, CACHE_LIBRARY_SOURCE_CACHE, CACHE_LIBRARY_DISPLAY_MODIFIERS)) { + return true; + } + + return false; +} + +static struct PTCReaderArchive *find_active_cache(Scene *scene, CacheLibrary *cachelib) +{ + char filename[FILE_MAX]; + struct PTCReaderArchive *archive = NULL; + + const bool is_baking = cachelib->flag & CACHE_LIBRARY_BAKING; + + /* don't read results from output archive when baking */ + if (!is_baking) { + if (cachelib->display_mode == CACHE_LIBRARY_DISPLAY_RESULT) { + /* try using the output cache */ + BKE_cache_archive_output_path(cachelib, filename, sizeof(filename)); + archive = PTC_open_reader_archive(scene, filename); + } + } + + if (!archive && ELEM(cachelib->source_mode, CACHE_LIBRARY_SOURCE_CACHE, CACHE_LIBRARY_DISPLAY_MODIFIERS)) { + BKE_cache_archive_input_path(cachelib, filename, sizeof(filename)); + archive = PTC_open_reader_archive(scene, filename); + } + + /* get basic archive info */ + if (cachelib->archive_info) + BKE_cache_archive_info_clear(cachelib->archive_info); + else + cachelib->archive_info = BKE_cache_archive_info_new(); + BLI_strncpy(cachelib->archive_info->filepath, filename, sizeof(cachelib->archive_info->filepath)); + if (archive) { + IDProperty *metadata = BKE_cache_library_get_input_metadata(cachelib, true); + PTC_get_archive_info(archive, cachelib->archive_info, metadata); + } + + return archive; +} + +void BKE_cache_library_get_read_flags(CacheLibrary *cachelib, bool use_render, bool for_display, bool *read_strands_motion, bool *read_strands_children) +{ + if (!use_render && for_display) { + *read_strands_motion = cachelib->display_flag & CACHE_LIBRARY_DISPLAY_MOTION; + *read_strands_children = cachelib->display_flag & CACHE_LIBRARY_DISPLAY_CHILDREN; + } + else { + *read_strands_motion = true; + *read_strands_children = true; + } +} + +bool BKE_cache_read_dupli_cache(CacheLibrary *cachelib, DupliCache *dupcache, + Scene *scene, Group *dupgroup, float frame, bool use_render, bool for_display) +{ + bool read_strands_motion, read_strands_children, read_simdebug = G.debug & G_DEBUG_SIMDATA; + struct PTCReaderArchive *archive; + struct PTCReader *reader; + + if (!dupcache) + return false; + + dupcache->result = CACHE_READ_SAMPLE_INVALID; + + if (!dupgroup || !cachelib) + return false; + + archive = find_active_cache(scene, cachelib); + if (!archive) + return false; + + PTC_reader_archive_use_render(archive, use_render); + + BKE_cache_library_get_read_flags(cachelib, use_render, for_display, &read_strands_motion, &read_strands_children); + // TODO duplicache reader should only overwrite data that is not sequentially generated by modifiers (simulations) ... + reader = PTC_reader_duplicache(dupgroup->id.name, dupgroup, dupcache, + read_strands_motion, read_strands_children, read_simdebug); + PTC_reader_init(reader, archive); + + dupcache->result = BKE_cache_read_result(PTC_read_sample(reader, frame)); + + PTC_reader_free(reader); + PTC_close_reader_archive(archive); + + return (dupcache->result != CACHE_READ_SAMPLE_INVALID); +} + +bool BKE_cache_read_dupli_object(CacheLibrary *cachelib, DupliObjectData *data, + Scene *scene, Object *ob, float frame, bool use_render, bool for_display) +{ + bool read_strands_motion, read_strands_children; + struct PTCReaderArchive *archive; + struct PTCReader *reader; + /*eCacheReadSampleResult result;*/ /* unused */ + + if (!data || !ob || !cachelib) + return false; + + archive = find_active_cache(scene, cachelib); + if (!archive) + return false; + + PTC_reader_archive_use_render(archive, use_render); + + BKE_cache_library_get_read_flags(cachelib, use_render, for_display, &read_strands_motion, &read_strands_children); + reader = PTC_reader_duplicache_object(ob->id.name, ob, data, read_strands_motion, read_strands_children); + PTC_reader_init(reader, archive); + + /*result = */BKE_cache_read_result(PTC_read_sample(reader, frame)); + + PTC_reader_free(reader); + PTC_close_reader_archive(archive); + + return true; +} + + +void BKE_cache_library_dag_recalc_tag(EvaluationContext *UNUSED(eval_ctx), Main *bmain) +{ + CacheLibrary *cachelib; + for (cachelib = bmain->cache_library.first; cachelib; cachelib = cachelib->id.next) { + if (has_active_cache(cachelib)) + DAG_id_tag_update(&cachelib->id, OB_RECALC_DATA | OB_RECALC_TIME); + } +} + +/* ========================================================================= */ + +CacheModifierTypeInfo cache_modifier_types[NUM_CACHE_MODIFIER_TYPES]; + +static CacheModifierTypeInfo *cache_modifier_type_get(eCacheModifier_Type type) +{ + return &cache_modifier_types[type]; +} + +static void cache_modifier_type_set(eCacheModifier_Type type, CacheModifierTypeInfo *mti) +{ + memcpy(&cache_modifier_types[type], mti, sizeof(CacheModifierTypeInfo)); +} + +const char *BKE_cache_modifier_type_name(eCacheModifier_Type type) +{ + return cache_modifier_type_get(type)->name; +} + +const char *BKE_cache_modifier_type_struct_name(eCacheModifier_Type type) +{ + return cache_modifier_type_get(type)->struct_name; +} + +int BKE_cache_modifier_type_struct_size(eCacheModifier_Type type) +{ + return cache_modifier_type_get(type)->struct_size; +} + +/* ------------------------------------------------------------------------- */ + +bool BKE_cache_modifier_unique_name(ListBase *modifiers, CacheModifier *md) +{ + if (modifiers && md) { + CacheModifierTypeInfo *mti = cache_modifier_type_get(md->type); + + return BLI_uniquename(modifiers, md, DATA_(mti->name), '.', offsetof(CacheModifier, name), sizeof(md->name)); + } + return false; +} + +CacheModifier *BKE_cache_modifier_add(CacheLibrary *cachelib, const char *name, eCacheModifier_Type type) +{ + CacheModifierTypeInfo *mti = cache_modifier_type_get(type); + + CacheModifier *md = MEM_callocN(mti->struct_size, "cache modifier"); + md->type = type; + + if (!name) + name = mti->name; + BLI_strncpy_utf8(md->name, name, sizeof(md->name)); + /* make sure modifier has unique name */ + BKE_cache_modifier_unique_name(&cachelib->modifiers, md); + + if (mti->init) + mti->init(md); + + BLI_addtail(&cachelib->modifiers, md); + + return md; +} + +void BKE_cache_modifier_remove(CacheLibrary *cachelib, CacheModifier *md) +{ + CacheModifierTypeInfo *mti = cache_modifier_type_get(md->type); + + BLI_remlink(&cachelib->modifiers, md); + + if (mti->free) + mti->free(md); + + MEM_freeN(md); +} + +void BKE_cache_modifier_clear(CacheLibrary *cachelib) +{ + CacheModifier *md, *md_next; + for (md = cachelib->modifiers.first; md; md = md_next) { + CacheModifierTypeInfo *mti = cache_modifier_type_get(md->type); + md_next = md->next; + + if (mti->free) + mti->free(md); + + MEM_freeN(md); + } + + BLI_listbase_clear(&cachelib->modifiers); +} + +CacheModifier *BKE_cache_modifier_copy(CacheLibrary *cachelib, CacheModifier *md) +{ + CacheModifierTypeInfo *mti = cache_modifier_type_get(md->type); + + CacheModifier *tmd = MEM_dupallocN(md); + + if (mti->copy) + mti->copy(md, tmd); + + BLI_addtail(&cachelib->modifiers, tmd); + + return tmd; +} + +void BKE_cache_modifier_foreachIDLink(struct CacheLibrary *cachelib, struct CacheModifier *md, CacheModifier_IDWalkFunc walk, void *userdata) +{ + CacheModifierTypeInfo *mti = cache_modifier_type_get(md->type); + + if (mti->foreachIDLink) + mti->foreachIDLink(md, cachelib, walk, userdata); +} + +void BKE_cache_process_dupli_cache(CacheLibrary *cachelib, CacheProcessData *data, + Scene *scene, Group *dupgroup, float frame_prev, float frame, + bool do_modifiers, bool do_strands_child_deform, bool do_strands_motion) +{ + CacheProcessContext ctx; + CacheModifier *md; + + ctx.bmain = G.main; + ctx.scene = scene; + ctx.cachelib = cachelib; + ctx.group = dupgroup; + + if (do_modifiers) { + for (md = cachelib->modifiers.first; md; md = md->next) { + CacheModifierTypeInfo *mti = cache_modifier_type_get(md->type); + + // TODO parent modifiers only here + if (mti->process) + mti->process(md, &ctx, data, frame, frame_prev, eCacheProcessFlag_DoStrands); + } + } + + /* deform child strands to follow parent motion */ + if (do_modifiers || do_strands_child_deform) { + struct DupliCacheIterator *it; + + it = BKE_dupli_cache_iter_new(data->dupcache); + for (; BKE_dupli_cache_iter_valid(it); BKE_dupli_cache_iter_next(it)) { + DupliObjectData *dobdata = BKE_dupli_cache_iter_get(it); + DupliObjectDataStrands *link; + + for (link = dobdata->strands.first; link; link = link->next) { + if (link->strands_children) + BKE_strands_children_deform(link->strands_children, link->strands, do_strands_motion); + } + } + BKE_dupli_cache_iter_free(it); + } + + if (do_modifiers) { + for (md = cachelib->modifiers.first; md; md = md->next) { + CacheModifierTypeInfo *mti = cache_modifier_type_get(md->type); + + // TODO child modifiers only here + if (mti->process) + mti->process(md, &ctx, data, frame, frame_prev, eCacheProcessFlag_DoStrandsChildren); + } + } +} + +/* ------------------------------------------------------------------------- */ + +static ForceFieldVertexCache *forcefield_vertex_cache_new(void); +static void forcefield_vertex_cache_free(ForceFieldVertexCache *cache); +static void forcefield_vertex_cache_clear(ForceFieldVertexCache *cache); +static void forcefield_vertex_cache_init(ForceFieldVertexCache *cache, float frame, DerivedMesh *dm); + +static void effector_set_mesh(CacheEffector *eff, Object *ob, DerivedMesh *dm, bool create_dm, bool create_bvhtree, bool world_space) +{ + if (create_dm && dm) { + unsigned int numverts, i; + MVert *mvert, *mv; + + eff->dm = CDDM_copy(dm); + if (!eff->dm) + return; + + DM_ensure_tessface(eff->dm); + CDDM_calc_normals(eff->dm); + + numverts = eff->dm->getNumVerts(eff->dm); + mvert = eff->dm->getVertArray(eff->dm); + + if (world_space) { + /* convert to world coordinates */ + for (i = 0, mv = mvert; i < numverts; ++i, ++mv) { + mul_m4_v3(ob->obmat, mv->co); + } + } + + if (create_bvhtree) { + if (eff->treedata) + free_bvhtree_from_mesh(eff->treedata); + else + eff->treedata = MEM_callocN(sizeof(BVHTreeFromMesh), "cache effector bvhtree data"); + + bvhtree_from_mesh_faces(eff->treedata, eff->dm, 0.0, 2, 6); + } + } +} + +static void effector_set_instances(CacheEffector *eff, Object *ob, float obmat[4][4], ListBase *duplilist) +{ + DupliObject *dob; + + for (dob = duplilist->first; dob; dob = dob->next) { + CacheEffectorInstance *inst; + + if (dob->ob != ob) + continue; + + inst = MEM_callocN(sizeof(CacheEffectorInstance), "cache effector instance"); + mul_m4_m4m4(inst->mat, obmat, dob->mat); + invert_m4_m4(inst->imat, inst->mat); + + BLI_addtail(&eff->instances, inst); + } +} + +static bool forcefield_get_effector(DupliCache *dupcache, float obmat[4][4], ForceFieldCacheModifier *ffmd, CacheEffector *eff) +{ + DupliObjectData *dobdata; + + if (!ffmd->object) + return false; + + dobdata = BKE_dupli_cache_find_data(dupcache, ffmd->object); + if (!dobdata) + return false; + + effector_set_mesh(eff, dobdata->ob, dobdata->dm, true, true, false); + effector_set_instances(eff, dobdata->ob, obmat, &dupcache->duplilist); + + switch (ffmd->type) { + case eForceFieldCacheModifier_Type_Deflect: + eff->type = eCacheEffector_Type_Deflect; + break; + case eForceFieldCacheModifier_Type_Drag: + eff->type = eCacheEffector_Type_Drag; + break; + } + + eff->strength = ffmd->strength; + eff->falloff = ffmd->falloff; + eff->mindist = ffmd->min_distance; + eff->maxdist = ffmd->max_distance; + eff->double_sided = ffmd->flag & eForceFieldCacheModifier_Flag_DoubleSided; + eff->vertex_cache = ffmd->vertex_cache; + + return true; +} + +int BKE_cache_effectors_get(CacheEffector *effectors, int max, CacheLibrary *cachelib, DupliCache *dupcache, float obmat[4][4]) +{ + CacheModifier *md; + int tot = 0; + + if (tot >= max) + return tot; + + memset(effectors, 0, sizeof(CacheEffector) * max); + + for (md = cachelib->modifiers.first; md; md = md->next) { + switch (md->type) { + case eCacheModifierType_ForceField: { + ForceFieldCacheModifier *ffmd = (ForceFieldCacheModifier *)md; + if (forcefield_get_effector(dupcache, obmat, ffmd, &effectors[tot])) + tot++; + break; + } + } + + BLI_assert(tot <= max); + if (tot == max) + break; + } + + return tot; +} + +void BKE_cache_effectors_free(CacheEffector *effectors, int tot) +{ + CacheEffector *eff; + int i; + + for (i = 0, eff = effectors; i < tot; ++i, ++eff) { + BLI_freelistN(&eff->instances); + + if (eff->treedata) { + free_bvhtree_from_mesh(eff->treedata); + MEM_freeN(eff->treedata); + } + + if (eff->dm) { + eff->dm->release(eff->dm); + } + } +} + +static bool forcefield_velocity_update(DupliCache *dupcache, float obmat[4][4], float frame, ForceFieldCacheModifier *ffmd) +{ + DupliObjectData *dobdata; + bool use_vertex_cache = false; + + if (!ffmd->object) + return false; + + dobdata = BKE_dupli_cache_find_data(dupcache, ffmd->object); + if (!dobdata) + return false; + + switch (ffmd->type) { + case eForceFieldCacheModifier_Type_Drag: + use_vertex_cache = true; + break; + } + + if (use_vertex_cache) { + if (!ffmd->vertex_cache) { + ffmd->vertex_cache = forcefield_vertex_cache_new(); + } + + forcefield_vertex_cache_init(ffmd->vertex_cache, frame, dobdata->dm); + { + int i; + for (i = 0; i < ffmd->vertex_cache->totvert; ++i) { + float x[3], v[3]; + mul_v3_m4v3(x, obmat, ffmd->vertex_cache->co_prev[i]); + copy_v3_v3(v, ffmd->vertex_cache->vel[i]); + mul_mat3_m4_v3(obmat, v); + BKE_sim_debug_data_add_vector(x, v, 1,1,0, "hairsim", 45232, i); + } + } + } + + return true; +} + +void BKE_cache_effector_velocity_update(CacheLibrary *cachelib, DupliCache *dupcache, float obmat[4][4], float frame) +{ + CacheModifier *md; + + for (md = cachelib->modifiers.first; md; md = md->next) { + switch (md->type) { + case eCacheModifierType_ForceField: + forcefield_velocity_update(dupcache, obmat, frame, (ForceFieldCacheModifier *)md); + break; + } + } +} + +static bool cache_effector_falloff(const CacheEffector *eff, float distance, float *r_factor) +{ + float mindist = eff->mindist; + float maxdist = eff->maxdist; + float falloff = eff->falloff; + float range = maxdist - mindist; + + if (r_factor) *r_factor = 0.0f; + + if (range <= 0.0f) + return false; + + if (distance > eff->maxdist) + return false; + CLAMP_MIN(distance, eff->mindist); + CLAMP_MIN(falloff, 0.0f); + + if (r_factor) *r_factor = powf(1.0f - (distance - mindist) / range, falloff); + return true; +} + +typedef struct CacheEffectorTessfaceData { + int face_index; + MFace *mface; + MVert *mvert[4]; + float weight[4]; +} CacheEffectorTessfaceData; + +static void cache_effector_velocity(const CacheEffector *eff, CacheEffectorInstance *inst, CacheEffectorTessfaceData *tessface, float vel[3]) +{ + zero_v3(vel); + + if (!eff->vertex_cache) + return; + + BLI_assert(eff->vertex_cache->totvert == eff->dm->getNumVerts(eff->dm)); + + madd_v3_v3fl(vel, eff->vertex_cache->vel[tessface->mface->v1], tessface->weight[0]); + madd_v3_v3fl(vel, eff->vertex_cache->vel[tessface->mface->v2], tessface->weight[1]); + madd_v3_v3fl(vel, eff->vertex_cache->vel[tessface->mface->v3], tessface->weight[2]); + if (tessface->mface->v4) + madd_v3_v3fl(vel, eff->vertex_cache->vel[tessface->mface->v4], tessface->weight[3]); + + /* vertex cache velocities are in local space, effector results are all expected in world space */ + mul_mat3_m4_v3(inst->mat, vel); +} + +static bool cache_effector_find_nearest(CacheEffector *eff, CacheEffectorInstance *inst, CacheEffectorPoint *point, + float r_vec[3], float r_nor[3], float *r_dist, bool *r_inside, + CacheEffectorTessfaceData *r_tessface) +{ + const bool need_inside = r_dist || r_inside; + + BVHTreeNearest nearest = {0, }; + float world_near_co[3], world_near_no[3]; + float co[3], vec[3], dist; + bool inside; + + if (!eff->treedata) + return false; + + nearest.dist_sq = FLT_MAX; + + /* lookup in object space */ + mul_v3_m4v3(co, inst->imat, point->x); + + BLI_bvhtree_find_nearest(eff->treedata->tree, co, &nearest, eff->treedata->nearest_callback, eff->treedata); + if (nearest.index < 0) + return false; + + /* convert back to world space */ + mul_v3_m4v3(world_near_co, inst->mat, nearest.co); + copy_v3_v3(world_near_no, nearest.no); + mul_mat3_m4_v3(inst->mat, world_near_no); + + sub_v3_v3v3(vec, point->x, world_near_co); + dist = normalize_v3(vec); + + if (need_inside) { + inside = false; + if (!eff->double_sided) { + if (dot_v3v3(vec, world_near_no) < 0.0f) { + dist = -dist; + inside = true; + } + } + } + + if (r_vec) copy_v3_v3(r_vec, vec); + if (r_nor) copy_v3_v3(r_nor, world_near_no); + if (r_dist) *r_dist = dist; + if (r_inside) *r_inside = inside; + + if (r_tessface && eff->dm) { + CacheEffectorTessfaceData *t = r_tessface; + DerivedMesh *dm = eff->dm; + MFace *mf = dm->getTessFaceArray(dm) + nearest.index; + MVert *mverts = dm->getVertArray(dm); + + t->face_index = nearest.index; + t->mface = mf; + t->mvert[0] = &mverts[mf->v1]; + t->mvert[1] = &mverts[mf->v2]; + t->mvert[2] = &mverts[mf->v3]; + + if (mf->v4) { + t->mvert[3] = &mverts[mf->v4]; + interp_weights_face_v3(t->weight, t->mvert[0]->co, t->mvert[1]->co, t->mvert[2]->co, t->mvert[3]->co, nearest.co); + } + else { + t->mvert[3] = NULL; + interp_weights_face_v3(t->weight, t->mvert[0]->co, t->mvert[1]->co, t->mvert[2]->co, NULL, nearest.co); + } + } + + return true; +} + +static bool cache_effector_deflect(CacheEffector *eff, CacheEffectorInstance *inst, CacheEffectorPoint *point, CacheEffectorResult *result) +{ + float vec[3], dist, falloff; + bool inside; + + if (!cache_effector_find_nearest(eff, inst, point, vec, NULL, &dist, &inside, NULL)) + return false; + if (!cache_effector_falloff(eff, dist, &falloff)) + return false; + + mul_v3_v3fl(result->f, vec, eff->strength * falloff); + if (inside) + negate_v3(result->f); + return true; +} + +static bool cache_effector_drag(CacheEffector *eff, CacheEffectorInstance *inst, CacheEffectorPoint *point, CacheEffectorResult *result) +{ + float vec[3], dist, vel[3]; + float falloff; + CacheEffectorTessfaceData facedata; + + if (!cache_effector_find_nearest(eff, inst, point, vec, NULL, &dist, NULL, &facedata)) + return false; + if (!cache_effector_falloff(eff, dist, &falloff)) + return false; + + cache_effector_velocity(eff, inst, &facedata, vel); + + /* relative velocity */ + sub_v3_v3v3(vel, point->v, vel); + + mul_v3_v3fl(result->f, vel, -eff->strength * falloff); + + return true; +} + +static void cache_effector_result_init(CacheEffectorResult *result) +{ + zero_v3(result->f); +} + +static void cache_effector_result_add(CacheEffectorResult *result, const CacheEffectorResult *other) +{ + add_v3_v3(result->f, other->f); +} + +int BKE_cache_effectors_eval_ex(CacheEffector *effectors, int tot, CacheEffectorPoint *point, CacheEffectorResult *result, + bool (*filter)(void *, CacheEffector *), void *filter_data) +{ + CacheEffector *eff; + int i, applied = 0; + + cache_effector_result_init(result); + + for (i = 0, eff = effectors; i < tot; ++i, ++eff) { + const eCacheEffector_Type type = eff->type; + CacheEffectorInstance *inst; + + for (inst = eff->instances.first; inst; inst = inst->next) { + CacheEffectorResult inst_result; + cache_effector_result_init(&inst_result); + + if (filter && !filter(filter_data, eff)) + continue; + + switch (type) { + case eCacheEffector_Type_Deflect: + if (cache_effector_deflect(eff, inst, point, &inst_result)) { + cache_effector_result_add(result, &inst_result); + ++applied; + } + break; + case eCacheEffector_Type_Drag: + if (cache_effector_drag(eff, inst, point, &inst_result)) { + cache_effector_result_add(result, &inst_result); + ++applied; + } + break; + } + } + } + + return applied; +} + +int BKE_cache_effectors_eval(CacheEffector *effectors, int tot, CacheEffectorPoint *point, CacheEffectorResult *result) +{ + return BKE_cache_effectors_eval_ex(effectors, tot, point, result, NULL, NULL); +} + +/* ========================================================================= */ + +bool BKE_cache_modifier_find_object(DupliCache *dupcache, Object *ob, DupliObjectData **r_data) +{ + DupliObjectData *dobdata; + + if (!ob) + return false; + dobdata = BKE_dupli_cache_find_data(dupcache, ob); + if (!dobdata) + return false; + + if (r_data) *r_data = dobdata; + return true; +} + +bool BKE_cache_modifier_find_strands(DupliCache *dupcache, Object *ob, int hair_system, DupliObjectData **r_data, Strands **r_strands, StrandsChildren **r_children, const char **r_name) +{ + DupliObjectData *dobdata; + ParticleSystem *psys; + DupliObjectDataStrands *link; + Strands *strands; + StrandsChildren *children; + + if (!ob) + return false; + dobdata = BKE_dupli_cache_find_data(dupcache, ob); + if (!dobdata) + return false; + + psys = BLI_findlink(&ob->particlesystem, hair_system); + if (!psys || (psys->part->type != PART_HAIR)) + return false; + + strands = NULL; + children = NULL; + for (link = dobdata->strands.first; link; link = link->next) { + if (link->strands && STREQ(link->name, psys->name)) { + strands = link->strands; + children = link->strands_children; + break; + } + } + if ((r_strands && !strands) || (r_children && !children)) + return false; + + if (r_data) *r_data = dobdata; + if (r_strands) *r_strands = strands; + if (r_children) *r_children = children; + if (r_name) *r_name = psys->name; + return true; +} + +static void hairsim_params_init(HairSimParams *params) +{ + params->timescale = 1.0f; + params->substeps = 5; + + params->mass = 0.3f; + params->drag = 0.1f; + + params->stretch_stiffness = 10000.0f; + params->stretch_damping = 0.1f; + params->bend_stiffness = 100.0f; + params->bend_damping = 1.0f; + params->goal_stiffness = 0.0f; + params->goal_damping = 1.0f; + { + CurveMapping *cm = curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f); + cm->cm[0].curve[0].x = 0.0f; + cm->cm[0].curve[0].y = 1.0f; + cm->cm[0].curve[1].x = 1.0f; + cm->cm[0].curve[1].y = 0.0f; + curvemapping_changed_all(cm); + params->goal_stiffness_mapping = cm; + } + { + CurveMapping *cm = curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f); + cm->cm[0].curve[0].x = 0.0f; + cm->cm[0].curve[0].y = 1.0f; + cm->cm[0].curve[1].x = 1.0f; + cm->cm[0].curve[1].y = 1.0f; + curvemapping_changed_all(cm); + params->bend_stiffness_mapping = cm; + } + + params->effector_weights = BKE_add_effector_weights(NULL); +} + +static void hairsim_init(HairSimCacheModifier *hsmd) +{ + hsmd->object = NULL; + hsmd->hair_system = -1; + + hairsim_params_init(&hsmd->sim_params); +} + +static void hairsim_copy(HairSimCacheModifier *hsmd, HairSimCacheModifier *thsmd) +{ + if (hsmd->sim_params.effector_weights) + thsmd->sim_params.effector_weights = MEM_dupallocN(hsmd->sim_params.effector_weights); + if (hsmd->sim_params.goal_stiffness_mapping) + thsmd->sim_params.goal_stiffness_mapping = curvemapping_copy(hsmd->sim_params.goal_stiffness_mapping); + if (hsmd->sim_params.bend_stiffness_mapping) + thsmd->sim_params.bend_stiffness_mapping = curvemapping_copy(hsmd->sim_params.bend_stiffness_mapping); +} + +static void hairsim_free(HairSimCacheModifier *hsmd) +{ + if (hsmd->sim_params.effector_weights) + MEM_freeN(hsmd->sim_params.effector_weights); + if (hsmd->sim_params.goal_stiffness_mapping) + curvemapping_free(hsmd->sim_params.goal_stiffness_mapping); + if (hsmd->sim_params.bend_stiffness_mapping) + curvemapping_free(hsmd->sim_params.bend_stiffness_mapping); +} + +static void hairsim_foreach_id_link(HairSimCacheModifier *hsmd, CacheLibrary *cachelib, CacheModifier_IDWalkFunc walk, void *userdata) +{ + walk(userdata, cachelib, &hsmd->modifier, (ID **)(&hsmd->object)); + if (hsmd->sim_params.effector_weights) + walk(userdata, cachelib, &hsmd->modifier, (ID **)(&hsmd->sim_params.effector_weights->group)); +} + +static void hairsim_process(HairSimCacheModifier *hsmd, CacheProcessContext *ctx, CacheProcessData *data, int frame, int frame_prev, int process_flag) +{ +#define MAX_CACHE_EFFECTORS 64 + + Object *ob = hsmd->object; + Strands *strands; + float mat[4][4]; + ListBase *effectors; + CacheEffector cache_effectors[MAX_CACHE_EFFECTORS]; + int tot_cache_effectors; + struct Implicit_Data *solver_data; + + /* only applies to parent strands */ + if (!(process_flag & eCacheProcessFlag_DoStrands)) + return; + + if (!BKE_cache_modifier_find_strands(data->dupcache, ob, hsmd->hair_system, NULL, &strands, NULL, NULL)) + return; + + /* Note: motion state data should always be created regardless of actual sim. + * This is necessary so the cache writer actually writes the first (empty) sample + * and the samples get mapped correctly to frames when reading. + */ + BKE_strands_add_motion_state(strands); + + /* skip first step and potential backward steps */ + if (frame > frame_prev) { + if (hsmd->sim_params.flag & eHairSimParams_Flag_UseGoalStiffnessCurve && hsmd->sim_params.goal_stiffness_mapping) + curvemapping_changed_all(hsmd->sim_params.goal_stiffness_mapping); + if (hsmd->sim_params.flag & eHairSimParams_Flag_UseBendStiffnessCurve && hsmd->sim_params.bend_stiffness_mapping) + curvemapping_changed_all(hsmd->sim_params.bend_stiffness_mapping); + + if (ob) + mul_m4_m4m4(mat, data->mat, ob->obmat); + else + copy_m4_m4(mat, data->mat); + + BKE_cache_effector_velocity_update(ctx->cachelib, data->dupcache, data->mat, (float)frame); + + solver_data = BPH_strands_solver_create(strands, &hsmd->sim_params); + effectors = pdInitEffectors_ex(ctx->scene, ob, NULL, data->lay, hsmd->sim_params.effector_weights, true); + tot_cache_effectors = BKE_cache_effectors_get(cache_effectors, MAX_CACHE_EFFECTORS, ctx->cachelib, data->dupcache, data->mat); + + BPH_strands_solve(strands, mat, solver_data, &hsmd->sim_params, (float)frame, (float)frame_prev, ctx->scene, effectors, cache_effectors, tot_cache_effectors); + + pdEndEffectors(&effectors); + BKE_cache_effectors_free(cache_effectors, tot_cache_effectors); + BPH_mass_spring_solver_free(solver_data); + } + +#undef MAX_CACHE_EFFECTORS +} + +CacheModifierTypeInfo cacheModifierType_HairSimulation = { + /* name */ "HairSimulation", + /* structName */ "HairSimCacheModifier", + /* structSize */ sizeof(HairSimCacheModifier), + + /* copy */ (CacheModifier_CopyFunc)hairsim_copy, + /* foreachIDLink */ (CacheModifier_ForeachIDLinkFunc)hairsim_foreach_id_link, + /* process */ (CacheModifier_ProcessFunc)hairsim_process, + /* init */ (CacheModifier_InitFunc)hairsim_init, + /* free */ (CacheModifier_FreeFunc)hairsim_free, +}; + +/* ------------------------------------------------------------------------- */ + +static ForceFieldVertexCache *forcefield_vertex_cache_new(void) +{ + ForceFieldVertexCache *cache = MEM_callocN(sizeof(ForceFieldVertexCache), "force field vertex cache"); + return cache; +} + +static void forcefield_vertex_cache_free(ForceFieldVertexCache *cache) +{ + if (cache->co_prev) + MEM_freeN(cache->co_prev); + if (cache->vel) + MEM_freeN(cache->vel); + MEM_freeN(cache); +} + +static void forcefield_vertex_cache_clear(ForceFieldVertexCache *cache) +{ + if (cache->co_prev) + MEM_freeN(cache->co_prev); + if (cache->vel) + MEM_freeN(cache->vel); + memset(cache, 0, sizeof(ForceFieldVertexCache)); +} + +static void forcefield_vertex_cache_init(ForceFieldVertexCache *cache, float frame, DerivedMesh *dm) +{ + MVert *mvert = dm->getVertArray(dm); + float dframe = frame - cache->frame_prev; + float inv_dframe = dframe > 0.0f ? 1.0f / dframe : 0.0f; + bool has_co_prev = (cache->co_prev != NULL); + int totvert = dm->getNumVerts(dm); + int i; + + if (cache->totvert != totvert) { + forcefield_vertex_cache_clear(cache); + dframe = 0.0f; + } + + if (!cache->co_prev) + cache->co_prev = MEM_mallocN(sizeof(float) * 3 * totvert, "force field vertex cache vertices"); + if (!cache->vel) + cache->vel = MEM_mallocN(sizeof(float) * 3 * totvert, "force field vertex cache vertices"); + + for (i = 0; i < totvert; ++i) { + if (has_co_prev) { + sub_v3_v3v3(cache->vel[i], mvert[i].co, cache->co_prev[i]); + mul_v3_fl(cache->vel[i], inv_dframe); + } + else { + zero_v3(cache->vel[i]); + } + + copy_v3_v3(cache->co_prev[i], mvert[i].co); + } + cache->frame_prev = frame; + cache->totvert = totvert; +} + +static void forcefield_init(ForceFieldCacheModifier *ffmd) +{ + ffmd->object = NULL; + + ffmd->vertex_cache = NULL; + + ffmd->strength = 0.0f; + ffmd->falloff = 1.0f; + ffmd->min_distance = 0.0f; + ffmd->max_distance = 1.0f; +} + +static void forcefield_copy(ForceFieldCacheModifier *UNUSED(ffmd), ForceFieldCacheModifier *tffmd) +{ + tffmd->vertex_cache = NULL; +} + +static void forcefield_free(ForceFieldCacheModifier *ffmd) +{ + if (ffmd->vertex_cache) { + forcefield_vertex_cache_free(ffmd->vertex_cache); + ffmd->vertex_cache = NULL; + } +} + +static void forcefield_foreach_id_link(ForceFieldCacheModifier *ffmd, CacheLibrary *cachelib, CacheModifier_IDWalkFunc walk, void *userdata) +{ + walk(userdata, cachelib, &ffmd->modifier, (ID **)(&ffmd->object)); +} + +CacheModifierTypeInfo cacheModifierType_ForceField = { + /* name */ "ForceField", + /* structName */ "ForceFieldCacheModifier", + /* structSize */ sizeof(ForceFieldCacheModifier), + + /* copy */ (CacheModifier_CopyFunc)forcefield_copy, + /* foreachIDLink */ (CacheModifier_ForeachIDLinkFunc)forcefield_foreach_id_link, + /* process */ (CacheModifier_ProcessFunc)NULL, + /* init */ (CacheModifier_InitFunc)forcefield_init, + /* free */ (CacheModifier_FreeFunc)forcefield_free, +}; + +/* ------------------------------------------------------------------------- */ + +static void shrinkwrap_init(ShrinkWrapCacheModifier *smd) +{ + smd->object = NULL; + smd->hair_system = -1; +} + +static void shrinkwrap_copy(ShrinkWrapCacheModifier *UNUSED(smd), ShrinkWrapCacheModifier *UNUSED(tsmd)) +{ +} + +static void shrinkwrap_free(ShrinkWrapCacheModifier *UNUSED(smd)) +{ +} + +static void shrinkwrap_foreach_id_link(ShrinkWrapCacheModifier *smd, CacheLibrary *cachelib, CacheModifier_IDWalkFunc walk, void *userdata) +{ + walk(userdata, cachelib, &smd->modifier, (ID **)(&smd->object)); + walk(userdata, cachelib, &smd->modifier, (ID **)(&smd->target)); +} + +typedef struct ShrinkWrapCacheData { + DerivedMesh *dm; + BVHTreeFromMesh treedata; + + ListBase instances; +} ShrinkWrapCacheData; + +typedef struct ShrinkWrapCacheInstance { + struct ShrinkWrapCacheInstance *next, *prev; + + float mat[4][4]; + float imat[4][4]; +} ShrinkWrapCacheInstance; + +static void shrinkwrap_data_get_bvhtree(ShrinkWrapCacheData *data, DerivedMesh *dm, bool create_bvhtree) +{ + data->dm = CDDM_copy(dm); + if (!data->dm) + return; + + DM_ensure_tessface(data->dm); + CDDM_calc_normals(data->dm); + + if (create_bvhtree) { + bvhtree_from_mesh_faces(&data->treedata, data->dm, 0.0, 2, 6); + } +} + +static void shrinkwrap_data_get_instances(ShrinkWrapCacheData *data, Object *ob, float obmat[4][4], ListBase *duplilist) +{ + if (duplilist) { + DupliObject *dob; + + for (dob = duplilist->first; dob; dob = dob->next) { + ShrinkWrapCacheInstance *inst; + + if (dob->ob != ob) + continue; + + inst = MEM_callocN(sizeof(ShrinkWrapCacheInstance), "shrink wrap instance"); + mul_m4_m4m4(inst->mat, obmat, dob->mat); + invert_m4_m4(inst->imat, inst->mat); + + BLI_addtail(&data->instances, inst); + } + } + else { + ShrinkWrapCacheInstance *inst = MEM_callocN(sizeof(ShrinkWrapCacheInstance), "shrink wrap instance"); + mul_m4_m4m4(inst->mat, obmat, ob->obmat); + invert_m4_m4(inst->imat, inst->mat); + + BLI_addtail(&data->instances, inst); + } +} + +static void shrinkwrap_data_free(ShrinkWrapCacheData *data) +{ + BLI_freelistN(&data->instances); + + free_bvhtree_from_mesh(&data->treedata); + + if (data->dm) { + data->dm->release(data->dm); + } +} + +static void shrinkwrap_apply_vertex(ShrinkWrapCacheModifier *UNUSED(smd), ShrinkWrapCacheData *data, ShrinkWrapCacheInstance *inst, const float *point, float *out) +{ + BVHTreeNearest nearest = {0, }; + float co[3], near_co[3], near_no[3]; + + if (!data->treedata.tree) + return; + + nearest.index = -1; + nearest.dist_sq = FLT_MAX; + + /* lookup in target space */ + mul_v3_m4v3(co, inst->imat, point); + + BLI_bvhtree_find_nearest(data->treedata.tree, co, &nearest, data->treedata.nearest_callback, &data->treedata); + if (nearest.index < 0) + return; + + /* convert back to world space */ + mul_v3_m4v3(near_co, inst->mat, nearest.co); + copy_v3_v3(near_no, nearest.no); + mul_mat3_m4_v3(inst->mat, near_no); + + { + float vec[3]; + + sub_v3_v3v3(vec, point, near_co); + + /* project along the distance vector */ + if (dot_v3v3(vec, near_no) < 0.0f) { + copy_v3_v3(out, near_co); + } + } +} + +static void shrinkwrap_apply(ShrinkWrapCacheModifier *smd, ShrinkWrapCacheData *data, Strands *strands, StrandsChildren *children, bool do_motion) +{ + /* XXX this is not great, the result depends on order of instances in the duplilist ... + * but good enough for single instance use case. + */ + ShrinkWrapCacheInstance *inst; + for (inst = data->instances.first; inst; inst = inst->next) { + + if (strands) { + StrandIterator it_strand; + for (BKE_strand_iter_init(&it_strand, strands); BKE_strand_iter_valid(&it_strand); BKE_strand_iter_next(&it_strand)) { + StrandVertexIterator it_vert; + for (BKE_strand_vertex_iter_init(&it_vert, &it_strand); BKE_strand_vertex_iter_valid(&it_vert); BKE_strand_vertex_iter_next(&it_vert)) { + if (do_motion && strands->state) + shrinkwrap_apply_vertex(smd, data, inst, it_vert.state->co, it_vert.state->co); + else + shrinkwrap_apply_vertex(smd, data, inst, it_vert.vertex->co, it_vert.vertex->co); + } + } + } + + if (children) { + StrandChildIterator it_strand; + for (BKE_strand_child_iter_init(&it_strand, children); BKE_strand_child_iter_valid(&it_strand); BKE_strand_child_iter_next(&it_strand)) { + StrandChildVertexIterator it_vert; + for (BKE_strand_child_vertex_iter_init(&it_vert, &it_strand); BKE_strand_child_vertex_iter_valid(&it_vert); BKE_strand_child_vertex_iter_next(&it_vert)) { + shrinkwrap_apply_vertex(smd, data, inst, it_vert.vertex->co, it_vert.vertex->co); + } + } + } + } +} + +static void shrinkwrap_process(ShrinkWrapCacheModifier *smd, CacheProcessContext *ctx, CacheProcessData *data, int UNUSED(frame), int UNUSED(frame_prev), int process_flag) +{ + bool do_strands_motion = true; + + const bool dupli_target = smd->flag & eShrinkWrapCacheModifier_Flag_InternalTarget; + Object *ob = smd->object; + DupliObject *dob; + Strands *strands = NULL; + DerivedMesh *target_dm; + float mat[4][4]; + + ShrinkWrapCacheData shrinkwrap; + + /* only applies to parent strands */ + if (!(process_flag & eCacheProcessFlag_DoStrands)) + return; + + if (!BKE_cache_modifier_find_strands(data->dupcache, ob, smd->hair_system, NULL, &strands, NULL, NULL)) + return; + + if (dupli_target) { + DupliObjectData *target_data; + if (!BKE_cache_modifier_find_object(data->dupcache, smd->target, &target_data)) + return; + target_dm = target_data->dm; + } + else { + if (!smd->target) + return; + target_dm = mesh_get_derived_final(ctx->scene, smd->target, CD_MASK_BAREMESH); + } + + for (dob = data->dupcache->duplilist.first; dob; dob = dob->next) { + if (dob->ob != ob) + continue; + + memset(&shrinkwrap, 0, sizeof(shrinkwrap)); + shrinkwrap_data_get_bvhtree(&shrinkwrap, target_dm, true); + + if (dupli_target) { + /* instances are calculated relative to the strands object */ + invert_m4_m4(mat, dob->mat); + shrinkwrap_data_get_instances(&shrinkwrap, smd->target, mat, &data->dupcache->duplilist); + } + else { + /* instances are calculated relative to the strands object */ + mul_m4_m4m4(mat, data->mat, dob->mat); + invert_m4(mat); + shrinkwrap_data_get_instances(&shrinkwrap, smd->target, mat, NULL); + } + + shrinkwrap_apply(smd, &shrinkwrap, strands, NULL, do_strands_motion); + + shrinkwrap_data_free(&shrinkwrap); + + /* XXX assume a single instance ... otherwise would just overwrite previous strands data */ + break; + } +} + +CacheModifierTypeInfo cacheModifierType_ShrinkWrap = { + /* name */ "ShrinkWrap", + /* structName */ "ShrinkWrapCacheModifier", + /* structSize */ sizeof(ShrinkWrapCacheModifier), + + /* copy */ (CacheModifier_CopyFunc)shrinkwrap_copy, + /* foreachIDLink */ (CacheModifier_ForeachIDLinkFunc)shrinkwrap_foreach_id_link, + /* process */ (CacheModifier_ProcessFunc)shrinkwrap_process, + /* init */ (CacheModifier_InitFunc)shrinkwrap_init, + /* free */ (CacheModifier_FreeFunc)shrinkwrap_free, +}; + +/* ------------------------------------------------------------------------- */ + +static void strandskey_init(StrandsKeyCacheModifier *skmd) +{ + skmd->object = NULL; + skmd->hair_system = -1; + + skmd->key = BKE_key_add_ex(NULL, KEY_OWNER_CACHELIB, -1); + skmd->key->type = KEY_RELATIVE; +} + +static void strandskey_copy(StrandsKeyCacheModifier *skmd, StrandsKeyCacheModifier *tskmd) +{ + tskmd->key = BKE_key_copy(skmd->key); + + tskmd->edit = NULL; +} + +static void strandskey_free(StrandsKeyCacheModifier *skmd) +{ + BKE_key_free(skmd->key); + + if (skmd->edit) { + BKE_editstrands_free(skmd->edit); + MEM_freeN(skmd->edit); + skmd->edit = NULL; + } +} + +static void strandskey_foreach_id_link(StrandsKeyCacheModifier *skmd, CacheLibrary *cachelib, CacheModifier_IDWalkFunc walk, void *userdata) +{ + walk(userdata, cachelib, &skmd->modifier, (ID **)(&skmd->object)); +} + +static void strandskey_process(StrandsKeyCacheModifier *skmd, CacheProcessContext *UNUSED(ctx), CacheProcessData *data, int UNUSED(frame), int UNUSED(frame_prev), int process_flag) +{ + const bool use_motion = skmd->flag & eStrandsKeyCacheModifier_Flag_UseMotionState; + Object *ob = skmd->object; + Strands *strands; + KeyBlock *actkb; + float *shape; + + /* only applies to parents */ + if (!(process_flag & eCacheProcessFlag_DoStrands)) + return; + if (!BKE_cache_modifier_find_strands(data->dupcache, ob, skmd->hair_system, NULL, &strands, NULL, NULL)) + return; + if (use_motion && !strands->state) + return; + + actkb = BLI_findlink(&skmd->key->block, skmd->shapenr); + shape = BKE_key_evaluate_strands(strands, skmd->key, actkb, skmd->flag & eStrandsKeyCacheModifier_Flag_ShapeLock, NULL, use_motion); + if (shape) { + StrandsVertex *vert = strands->verts; + StrandsMotionState *state = use_motion ? strands->state : NULL; + int totvert = strands->totverts; + int i; + + float *fp = shape; + for (i = 0; i < totvert; ++i) { + if (state) { + copy_v3_v3(state->co, fp); + ++state; + } + else { + copy_v3_v3(vert->co, fp); + ++vert; + } + fp += 3; + } + + MEM_freeN(shape); + } +} + +CacheModifierTypeInfo cacheModifierType_StrandsKey = { + /* name */ "StrandsKey", + /* structName */ "StrandsKeyCacheModifier", + /* structSize */ sizeof(StrandsKeyCacheModifier), + + /* copy */ (CacheModifier_CopyFunc)strandskey_copy, + /* foreachIDLink */ (CacheModifier_ForeachIDLinkFunc)strandskey_foreach_id_link, + /* process */ (CacheModifier_ProcessFunc)strandskey_process, + /* init */ (CacheModifier_InitFunc)strandskey_init, + /* free */ (CacheModifier_FreeFunc)strandskey_free, +}; + +KeyBlock *BKE_cache_modifier_strands_key_insert_key(StrandsKeyCacheModifier *skmd, Strands *strands, const char *name, const bool from_mix) +{ + const bool use_motion = skmd->flag & eStrandsKeyCacheModifier_Flag_UseMotionState; + Key *key = skmd->key; + KeyBlock *kb; + bool newkey = false; + + if (key == NULL) { + key = skmd->key = BKE_key_add_ex(NULL, KEY_OWNER_CACHELIB, -1); + key->type = KEY_RELATIVE; + newkey = true; + } + else if (BLI_listbase_is_empty(&key->block)) { + newkey = true; + } + + if (newkey || from_mix == false) { + /* create from mesh */ + kb = BKE_keyblock_add_ctime(key, name, false); + BKE_keyblock_convert_from_strands(strands, key, kb, use_motion); + } + else { + /* copy from current values */ + KeyBlock *actkb = BLI_findlink(&skmd->key->block, skmd->shapenr); + bool shape_lock = skmd->flag & eStrandsKeyCacheModifier_Flag_ShapeLock; + int totelem; + float *data = BKE_key_evaluate_strands(strands, key, actkb, shape_lock, &totelem, use_motion); + + /* create new block with prepared data */ + kb = BKE_keyblock_add_ctime(key, name, false); + kb->data = data; + kb->totelem = totelem; + } + + return kb; +} + +bool BKE_cache_modifier_strands_key_get(Object *ob, StrandsKeyCacheModifier **r_skmd, DerivedMesh **r_dm, Strands **r_strands, DupliObjectData **r_dobdata, const char **r_name, float r_mat[4][4]) +{ + CacheLibrary *cachelib = ob->cache_library; + CacheModifier *md; + + if (!cachelib) + return false; + + /* ignore when the object is not actually using the cachelib */ + if (!((ob->transflag & OB_DUPLIGROUP) && ob->dup_group && ob->dup_cache)) + return false; + + for (md = cachelib->modifiers.first; md; md = md->next) { + if (md->type == eCacheModifierType_StrandsKey) { + StrandsKeyCacheModifier *skmd = (StrandsKeyCacheModifier *)md; + DupliObjectData *dobdata; + + if (BKE_cache_modifier_find_strands(ob->dup_cache, skmd->object, skmd->hair_system, &dobdata, r_strands, NULL, r_name)) { + if (r_skmd) *r_skmd = skmd; + if (r_dm) *r_dm = dobdata->dm; + if (r_dobdata) *r_dobdata = dobdata; + + /* relative transform from the original hair object to the duplicator local space */ + /* XXX bad hack, common problem: we want to display strand edit data in the place of "the" instance, + * but in fact there can be multiple instances of the same dupli object data, so this is ambiguous ... + * For our basic use case, just pick the first dupli instance, assuming that it's the only one. + * ugh ... + */ + if (r_mat) { + DupliObject *dob; + for (dob = ob->dup_cache->duplilist.first; dob; dob = dob->next) { + if (dob->ob == skmd->object) + break; + } + if (dob) { + /* note: plain duplis from the dupli cache list are relative + * to the duplicator already! (not in world space like final duplis) + */ + copy_m4_m4(r_mat, dob->mat); + } + else + unit_m4(r_mat); + } + + return true; + } + } + } + + return false; +} + +/* ------------------------------------------------------------------------- */ + +static void haircut_init(HaircutCacheModifier *hmd) +{ + hmd->object = NULL; + hmd->hair_system = -1; +} + +static void haircut_copy(HaircutCacheModifier *UNUSED(hmd), HaircutCacheModifier *UNUSED(thmd)) +{ +} + +static void haircut_free(HaircutCacheModifier *UNUSED(hmd)) +{ +} + +static void haircut_foreach_id_link(HaircutCacheModifier *smd, CacheLibrary *cachelib, CacheModifier_IDWalkFunc walk, void *userdata) +{ + walk(userdata, cachelib, &smd->modifier, (ID **)(&smd->object)); + walk(userdata, cachelib, &smd->modifier, (ID **)(&smd->target)); +} + +typedef struct HaircutCacheData { + DerivedMesh *dm; + BVHTreeFromMesh treedata; + + ListBase instances; +} HaircutCacheData; + +typedef struct HaircutCacheInstance { + struct HaircutCacheInstance *next, *prev; + + float mat[4][4]; + float imat[4][4]; +} HaircutCacheInstance; + +static void haircut_data_get_bvhtree(HaircutCacheData *data, DerivedMesh *dm, bool create_bvhtree) +{ + data->dm = CDDM_copy(dm); + if (!data->dm) + return; + + DM_ensure_tessface(data->dm); + CDDM_calc_normals(data->dm); + + if (create_bvhtree) { + bvhtree_from_mesh_faces(&data->treedata, data->dm, 0.0, 2, 6); + } +} + +static void haircut_data_get_instances(HaircutCacheData *data, Object *ob, float obmat[4][4], ListBase *duplilist) +{ + if (duplilist) { + DupliObject *dob; + + for (dob = duplilist->first; dob; dob = dob->next) { + HaircutCacheInstance *inst; + + if (dob->ob != ob) + continue; + + inst = MEM_callocN(sizeof(HaircutCacheInstance), "haircut instance"); + mul_m4_m4m4(inst->mat, obmat, dob->mat); + invert_m4_m4(inst->imat, inst->mat); + + BLI_addtail(&data->instances, inst); + } + } + else { + HaircutCacheInstance *inst = MEM_callocN(sizeof(HaircutCacheInstance), "haircut instance"); + mul_m4_m4m4(inst->mat, obmat, ob->obmat); + invert_m4_m4(inst->imat, inst->mat); + + BLI_addtail(&data->instances, inst); + } +} + +static void haircut_data_free(HaircutCacheData *data) +{ + BLI_freelistN(&data->instances); + + free_bvhtree_from_mesh(&data->treedata); + + if (data->dm) { + data->dm->release(data->dm); + } +} + +/* XXX intersection counting does not work reliably */ +#if 0 +typedef struct PointInsideBVH { + BVHTreeFromMesh bvhdata; + int num_hits; +} PointInsideBVH; + +static void point_inside_bvh_cb(void *userdata, int index, const BVHTreeRay *ray, BVHTreeRayHit *hit) +{ + PointInsideBVH *data = userdata; + + data->bvhdata.raycast_callback(&data->bvhdata, index, ray, hit); + + if (hit->index != -1) + ++data->num_hits; +} + +/* true if the point is inside the target mesh */ +static bool haircut_test_point(HaircutCacheModifier *hmd, HaircutCacheData *data, HaircutCacheInstance *inst, const float *v) +{ + const float dir[3] = {1.0f, 0.0f, 0.0f}; + float start[3]; + PointInsideBVH userdata; + + if (!(hmd->cut_mode & eHaircutCacheModifier_CutMode_Enter)) + return false; + + userdata.bvhdata = data->treedata; + userdata.num_hits = 0; + + /* lookup in target space */ + mul_v3_m4v3(start, inst->imat, v); + + BLI_bvhtree_ray_cast_all(data->treedata.tree, start, dir, 0.0f, point_inside_bvh_cb, &userdata); + + /* for any point inside a watertight mesh the number of hits is uneven */ + return (userdata.num_hits % 2) == 1; +} +#else +/* true if the point is inside the target mesh */ +static bool haircut_test_point(HaircutCacheModifier *hmd, HaircutCacheData *data, HaircutCacheInstance *inst, const float *v) +{ + BVHTreeRayHit hit = {0, }; + float start[3], dir[3] = {0.0f, 0.0f, 1.0f}; + bool is_entering; + + if (!(hmd->cut_mode & eHaircutCacheModifier_CutMode_Enter)) + return false; + if (!data->treedata.tree) + return false; + + /* lookup in target space */ + mul_v3_m4v3(start, inst->imat, v); + + hit.index = -1; + hit.dist = FLT_MAX; + + BLI_bvhtree_ray_cast(data->treedata.tree, start, dir, 0.0f, &hit, data->treedata.raycast_callback, &data->treedata); + if (hit.index < 0) { + return false; + } + + mul_mat3_m4_v3(inst->mat, hit.no); + + is_entering = (dot_v3v3(dir, hit.no) < 0.0f); + + return !is_entering; +} +#endif + +static bool haircut_find_segment_cut(HaircutCacheModifier *hmd, HaircutCacheData *data, HaircutCacheInstance *inst, + const float *v1, const float *v2, float *r_lambda) +{ + BVHTreeRayHit hit = {0, }; + float start[3], dir[3], length; + bool is_entering; + + if (!data->treedata.tree) + return false; + + /* lookup in target space */ + mul_v3_m4v3(start, inst->imat, v1); + sub_v3_v3v3(dir, v2, v1); + mul_mat3_m4_v3(inst->imat, dir); + length = normalize_v3(dir); + + if (length == 0.0f) + return false; + + hit.index = -1; + hit.dist = length; + + BLI_bvhtree_ray_cast(data->treedata.tree, start, dir, 0.0f, &hit, data->treedata.raycast_callback, &data->treedata); + if (hit.index < 0) + return false; + + is_entering = (dot_v3v3(dir, hit.no) < 0.0f); + if ((hmd->cut_mode & eHaircutCacheModifier_CutMode_Enter && is_entering) || + (hmd->cut_mode & eHaircutCacheModifier_CutMode_Exit && !is_entering)) + { + if (r_lambda) *r_lambda = len_v3v3(hit.co, start) / length; + return true; + } + + return false; +} + +static bool haircut_find_first_strand_cut(HaircutCacheModifier *hmd, HaircutCacheData *data, StrandChildIterator *it_strand, float *r_cutoff) +{ + StrandChildVertexIterator it_vert; + int vprev = -1; + float cutoff = 0.0f; + + for (BKE_strand_child_vertex_iter_init(&it_vert, it_strand); BKE_strand_child_vertex_iter_valid(&it_vert); BKE_strand_child_vertex_iter_next(&it_vert)) { + StrandsChildVertex *verts = it_strand->verts; + bool found_cut = false; + float lambda_min = 1.0f; + HaircutCacheInstance *inst; + + if (it_vert.index == 0) { + for (inst = data->instances.first; inst; inst = inst->next) { + /* test root vertex */ + if (haircut_test_point(hmd, data, inst, verts[it_vert.index].co)) { + if (r_cutoff) *r_cutoff = 0.0f; + return true; + } + } + } + else { + for (inst = data->instances.first; inst; inst = inst->next) { + float lambda; + if (haircut_find_segment_cut(hmd, data, inst, verts[vprev].co, verts[it_vert.index].co, &lambda)) { + found_cut = true; + if (lambda < lambda_min) + lambda_min = lambda; + } + } + + if (found_cut) { + cutoff += lambda_min; + if (r_cutoff) *r_cutoff = cutoff; + return true; + } + else + cutoff += 1.0f; + } + + vprev = it_vert.index; + } + + if (r_cutoff) *r_cutoff = -1.0f; /* indicates "no cutoff" */ + return false; +} + +/* shortens the last visible segment to have exact cutoff length */ +static void haircut_strand_adjust_tip(StrandChildIterator *it_strand, float cutoff) +{ + StrandsChildCurve *curve = it_strand->curve; + + int last, end; + float *a, *b; + float t; + + if (cutoff < 0 || cutoff >= (float)(curve->numverts-1)) + return; + + last = (int)cutoff; + end = last + 1; + BLI_assert(last < curve->numverts); + BLI_assert(end < curve->numverts); + + a = it_strand->verts[last].co; + b = it_strand->verts[end].co; + t = cutoff - floorf(cutoff); + interp_v3_v3v3(b, a, b, t); +} + +static void haircut_apply(HaircutCacheModifier *hmd, CacheProcessContext *UNUSED(ctx), HaircutCacheData *data, StrandsChildren *strands) +{ + StrandChildIterator it_strand; + for (BKE_strand_child_iter_init(&it_strand, strands); BKE_strand_child_iter_valid(&it_strand); BKE_strand_child_iter_next(&it_strand)) { + float cutoff = -1.0f; + if (haircut_find_first_strand_cut(hmd, data, &it_strand, &cutoff)) { + it_strand.curve->cutoff = cutoff; + haircut_strand_adjust_tip(&it_strand, cutoff); + } + } +} + +static void haircut_process(HaircutCacheModifier *hmd, CacheProcessContext *ctx, CacheProcessData *data, int UNUSED(frame), int UNUSED(frame_prev), int process_flag) +{ + bool do_strands_children = (process_flag & eCacheProcessFlag_DoStrandsChildren); + const bool dupli_target = hmd->flag & eHaircutCacheModifier_Flag_InternalTarget; + Object *ob = hmd->object; + DupliObject *dob; + StrandsChildren *strands; + DerivedMesh *target_dm; + float mat[4][4]; + + HaircutCacheData haircut; + + /* only applies to children */ + if (!do_strands_children) + return; + if (!BKE_cache_modifier_find_strands(data->dupcache, ob, hmd->hair_system, NULL, NULL, do_strands_children ? &strands : NULL, NULL)) + return; + + if (dupli_target) { + DupliObjectData *target_data; + if (!BKE_cache_modifier_find_object(data->dupcache, hmd->target, &target_data)) + return; + target_dm = target_data->dm; + } + else { + if (!hmd->target) + return; + target_dm = mesh_get_derived_final(ctx->scene, hmd->target, CD_MASK_BAREMESH); + } + + for (dob = data->dupcache->duplilist.first; dob; dob = dob->next) { + if (dob->ob != ob) + continue; + + memset(&haircut, 0, sizeof(haircut)); + haircut_data_get_bvhtree(&haircut, target_dm, true); + if (dupli_target) { + /* instances are calculated relative to the strands object */ + invert_m4_m4(mat, dob->mat); + haircut_data_get_instances(&haircut, hmd->target, mat, &data->dupcache->duplilist); + } + else { + /* instances are calculated relative to the strands object */ + mul_m4_m4m4(mat, data->mat, dob->mat); + invert_m4(mat); + haircut_data_get_instances(&haircut, hmd->target, mat, NULL); + } + + haircut_apply(hmd, ctx, &haircut, strands); + + haircut_data_free(&haircut); + + /* XXX assume a single instance ... otherwise would just overwrite previous strands data */ + break; + } +} + +CacheModifierTypeInfo cacheModifierType_Haircut = { + /* name */ "Haircut", + /* structName */ "HaircutCacheModifier", + /* structSize */ sizeof(HaircutCacheModifier), + + /* copy */ (CacheModifier_CopyFunc)haircut_copy, + /* foreachIDLink */ (CacheModifier_ForeachIDLinkFunc)haircut_foreach_id_link, + /* process */ (CacheModifier_ProcessFunc)haircut_process, + /* init */ (CacheModifier_InitFunc)haircut_init, + /* free */ (CacheModifier_FreeFunc)haircut_free, +}; + +/* ------------------------------------------------------------------------- */ + +bool BKE_cache_library_uses_key(CacheLibrary *cachelib, Key *key) +{ + CacheModifier *md; + for (md = cachelib->modifiers.first; md; md = md->next) { + if (md->type == eCacheModifierType_StrandsKey) { + StrandsKeyCacheModifier *skmd = (StrandsKeyCacheModifier *)md; + if (skmd->key == key) + return true; + } + } + return false; +} + +void BKE_cache_modifier_init(void) +{ + cache_modifier_type_set(eCacheModifierType_HairSimulation, &cacheModifierType_HairSimulation); + cache_modifier_type_set(eCacheModifierType_ForceField, &cacheModifierType_ForceField); + cache_modifier_type_set(eCacheModifierType_ShrinkWrap, &cacheModifierType_ShrinkWrap); + cache_modifier_type_set(eCacheModifierType_StrandsKey, &cacheModifierType_StrandsKey); + cache_modifier_type_set(eCacheModifierType_Haircut, &cacheModifierType_Haircut); +} + +/* ========================================================================= */ + +#if 0 +static unsigned int hash_combine(unsigned int kx, unsigned int ky) +{ +#define rot(x,k) (((x)<<(k)) | ((x)>>(32-(k)))) + + unsigned int a, b, c; + + a = b = c = 0xdeadbeef + (2 << 2) + 13; + a += kx; + b += ky; + + c ^= b; c -= rot(b,14); + a ^= c; a -= rot(c,11); + b ^= a; b -= rot(a,25); + c ^= b; c -= rot(b,16); + a ^= c; a -= rot(c,4); + b ^= a; b -= rot(a,14); + c ^= b; c -= rot(b,24); + + return c; + +#undef rot +} + +static unsigned int cache_archive_info_node_hash(const void *key) +{ + const CacheArchiveInfoNode *node = key; + + unsigned int hash = hash_combine(BLI_ghashutil_strhash(node->name), BLI_ghashutil_inthash(node->type)); + if (node->parent_hash != 0) + hash = hash_combine(hash, node->parent_hash); + return hash; +} + +static bool cache_archive_info_node_cmp(const CacheArchiveInfoNode *a, const CacheArchiveInfoNode *b) +{ + if (a->parent_hash != b->parent_hash) + return true; + else if (a->type != b->type) + return true; + else if (!STREQ(a->name, b->name)) + return true; + else + return false; +} +#endif + +static void cache_archive_info_node_free(CacheArchiveInfoNode *node) +{ + CacheArchiveInfoNode *child, *child_next; + for (child = node->child_nodes.first; child; child = child_next) { + child_next = child->next; + cache_archive_info_node_free(child); + } + + MEM_freeN(node); +} + +CacheArchiveInfo *BKE_cache_archive_info_new(void) +{ + CacheArchiveInfo *info = MEM_callocN(sizeof(CacheArchiveInfo), "cache archive info"); + + return info; +} + +void BKE_cache_archive_info_free(CacheArchiveInfo *info) +{ + if (info) { + if (info->root_node) + cache_archive_info_node_free(info->root_node); + + MEM_freeN(info); + } +} + +void BKE_cache_archive_info_clear(CacheArchiveInfo *info) +{ + info->filepath[0] = '\0'; + info->app_name[0] = '\0'; + info->date_written[0] = '\0'; + info->description[0] = '\0'; + + if (info->root_node) { + cache_archive_info_node_free(info->root_node); + info->root_node = NULL; + } +} + +CacheArchiveInfoNode *BKE_cache_archive_info_find_node(CacheArchiveInfo *info, CacheArchiveInfoNode *parent, + eCacheArchiveInfoNode_Type type, const char *name) +{ + if (parent) { + CacheArchiveInfoNode *child; + for (child = parent->child_nodes.first; child; child = child->next) { + if (STREQ(child->name, name) && child->type == type) + return child; + } + } + else if (info->root_node) { + if (STREQ(info->root_node->name, name) && info->root_node->type == type) + return info->root_node; + } + return NULL; +} + +CacheArchiveInfoNode *BKE_cache_archive_info_add_node(CacheArchiveInfo *info, CacheArchiveInfoNode *parent, + eCacheArchiveInfoNode_Type type, const char *name) +{ + CacheArchiveInfoNode *node; + + BLI_assert(parent || !info->root_node); + + node = MEM_callocN(sizeof(CacheArchiveInfoNode), "cache archive info node"); + node->type = type; + BLI_strncpy(node->name, name, sizeof(node->name)); + + /* these values are only optionally calculated, -1 indicates unknown */ + node->bytes_size = -1; + node->array_size = -1; + + if (parent) + BLI_addtail(&parent->child_nodes, node); + else + info->root_node = node; + + return node; +} diff --git a/source/blender/blenkernel/intern/cdderivedmesh.c b/source/blender/blenkernel/intern/cdderivedmesh.c index 3feea12f017..8660f122c10 100644 --- a/source/blender/blenkernel/intern/cdderivedmesh.c +++ b/source/blender/blenkernel/intern/cdderivedmesh.c @@ -395,7 +395,7 @@ static void cdDM_drawEdges(DerivedMesh *dm, bool drawLooseEdges, bool drawAllEdg MEdge *medge = cddm->medge; int i; int prevstart = 0; - int prevdraw = 1; + bool prevdraw = true; bool draw = true; if (cddm->pbvh && cddm->pbvh_draw && @@ -417,14 +417,14 @@ static void cdDM_drawEdges(DerivedMesh *dm, bool drawLooseEdges, bool drawAllEdg draw = false; } if (prevdraw != draw) { - if (prevdraw > 0 && (i - prevstart) > 0) { + if (prevdraw && (i - prevstart) > 0) { GPU_buffer_draw_elements(dm->drawObject->edges, GL_LINES, prevstart * 2, (i - prevstart) * 2); } prevstart = i; } prevdraw = draw; } - if (prevdraw > 0 && (i - prevstart) > 0) { + if (prevdraw && (i - prevstart) > 0) { GPU_buffer_draw_elements(dm->drawObject->edges, GL_LINES, prevstart * 2, (i - prevstart) * 2); } GPU_buffer_unbind(); diff --git a/source/blender/blenkernel/intern/cloth.c b/source/blender/blenkernel/intern/cloth.c index 3b3fe323f2b..17ed9de91e0 100644 --- a/source/blender/blenkernel/intern/cloth.c +++ b/source/blender/blenkernel/intern/cloth.c @@ -337,7 +337,7 @@ static int do_init_cloth(Object *ob, ClothModifierData *clmd, DerivedMesh *resul return 0; } - BKE_cloth_solver_set_positions(clmd); + BPH_cloth_solver_set_positions(clmd); clmd->clothObject->last_frame= MINFRAME-1; } @@ -370,7 +370,7 @@ static int do_step_cloth(Object *ob, ClothModifierData *clmd, DerivedMesh *resul mul_m4_v3(ob->obmat, verts->xconst); } - effectors = pdInitEffectors(clmd->scene, ob, NULL, clmd->sim_parms->effector_weights, true); + effectors = pdInitEffectors(clmd->scene, ob, NULL, clmd->sim_parms->effector_weights); /* Support for dynamic vertex groups, changing from frame to frame */ cloth_apply_vgroup ( clmd, result ); @@ -500,7 +500,6 @@ void clothModifier_do(ClothModifierData *clmd, Scene *scene, Object *ob, Derived cache_result = BKE_ptcache_read(&pid, (float)framenr+scene->r.subframe); if (cache_result == PTCACHE_READ_EXACT || cache_result == PTCACHE_READ_INTERPOLATED) { - BKE_cloth_solver_set_positions(clmd); cloth_to_object (ob, clmd, vertexCos); BKE_ptcache_validate(cache, framenr); @@ -513,7 +512,7 @@ void clothModifier_do(ClothModifierData *clmd, Scene *scene, Object *ob, Derived return; } else if (cache_result==PTCACHE_READ_OLD) { - BKE_cloth_solver_set_positions(clmd); + BPH_cloth_solver_set_positions(clmd); } else if ( /*ob->id.lib ||*/ (cache->flag & PTCACHE_BAKED)) { /* 2.4x disabled lib, but this can be used in some cases, testing further - campbell */ /* if baked and nothing in cache, do nothing */ @@ -729,11 +728,10 @@ static void cloth_apply_vgroup ( ClothModifierData *clmd, DerivedMesh *dm ) clothObj = clmd->clothObject; - numverts = dm->getNumVerts (dm); + numverts = dm->getNumVerts(dm); - verts = clothObj->verts; - if (cloth_uses_vgroup(clmd)) { + verts = clothObj->verts; for ( i = 0; i < numverts; i++, verts++ ) { /* Reset Goal values to standard */ @@ -794,6 +792,17 @@ static void cloth_apply_vgroup ( ClothModifierData *clmd, DerivedMesh *dm ) } } } + +#ifdef USE_PARTICLE_PREVIEW + { + MVert *mvert = dm->getVertArray(dm); + verts = clothObj->verts; + for ( i = 0; i < numverts; i++, verts++ ) { + if (mvert[i].flag & ME_VERT_TMP_TAG) + verts->flags |= CLOTH_VERT_FLAG_EXCLUDE; + } + } +#endif } @@ -900,7 +909,7 @@ static int cloth_from_object(Object *ob, ClothModifierData *clmd, DerivedMesh *d BPH_cloth_solver_init(ob, clmd); if (!first) - BKE_cloth_solver_set_positions(clmd); + BPH_cloth_solver_set_positions(clmd); clmd->clothObject->bvhtree = bvhtree_build_from_cloth ( clmd, MAX2(clmd->coll_parms->epsilon, clmd->coll_parms->distance_repel) ); diff --git a/source/blender/blenkernel/intern/collision.c b/source/blender/blenkernel/intern/collision.c index 763024ec565..9ff131157ac 100644 --- a/source/blender/blenkernel/intern/collision.c +++ b/source/blender/blenkernel/intern/collision.c @@ -1362,7 +1362,7 @@ void cloth_find_point_contacts(Object *ob, ClothModifierData *clmd, float step, // create temporary cloth points bvh cloth_bvh = BLI_bvhtree_new(numverts, MAX2(clmd->coll_parms->epsilon, clmd->coll_parms->distance_repel), 4, 6); /* fill tree */ - for (i = 0; i < numverts; i++) { + for (i = 0; i < cloth->numverts; i++) { float co[6]; copy_v3_v3(&co[0*3], verts[i].x); diff --git a/source/blender/blenkernel/intern/context.c b/source/blender/blenkernel/intern/context.c index 7142c092583..b463a1650b7 100644 --- a/source/blender/blenkernel/intern/context.c +++ b/source/blender/blenkernel/intern/context.c @@ -58,6 +58,8 @@ /* struct */ +struct wmWidget; + struct bContext { int thread; @@ -926,6 +928,7 @@ int CTX_data_mode_enum(const bContext *C) else if (ob->mode & OB_MODE_VERTEX_PAINT) return CTX_MODE_PAINT_VERTEX; else if (ob->mode & OB_MODE_TEXTURE_PAINT) return CTX_MODE_PAINT_TEXTURE; else if (ob->mode & OB_MODE_PARTICLE_EDIT) return CTX_MODE_PARTICLE; + else if (ob->mode & OB_MODE_HAIR_EDIT) return CTX_MODE_HAIR; } } @@ -949,6 +952,7 @@ static const char *data_mode_strings[] = { "vertexpaint", "imagepaint", "particlemode", + "hairmode", "objectmode", NULL }; diff --git a/source/blender/blenkernel/intern/curve.c b/source/blender/blenkernel/intern/curve.c index 6322570d562..9f83ffa2577 100644 --- a/source/blender/blenkernel/intern/curve.c +++ b/source/blender/blenkernel/intern/curve.c @@ -222,7 +222,7 @@ Curve *BKE_curve_copy(Curve *cu) cun->bb = MEM_dupallocN(cu->bb); cun->key = BKE_key_copy(cu->key); - if (cun->key) cun->key->from = (ID *)cun; + BKE_key_set_from_id(cun->key, (ID *)cun); cun->editnurb = NULL; cun->editfont = NULL; diff --git a/source/blender/blenkernel/intern/customdata.c b/source/blender/blenkernel/intern/customdata.c index eced263d493..403e815ce5c 100644 --- a/source/blender/blenkernel/intern/customdata.c +++ b/source/blender/blenkernel/intern/customdata.c @@ -55,6 +55,7 @@ #include "BKE_customdata.h" #include "BKE_customdata_file.h" +#include "BKE_editstrands.h" #include "BKE_global.h" #include "BKE_main.h" #include "BKE_mesh_mapping.h" @@ -1195,6 +1196,16 @@ static void layerSwap_flnor(void *data, const int *corner_indices) memcpy(flnors, nors, sizeof(nors)); } +static void layerDefault_fmap(void *data, int count) +{ + int *fmap_num = (int *)data; + int i; + for (i = 0; i < count; i++) + *fmap_num = -1; + +} + + static const LayerTypeInfo LAYERTYPEINFO[CD_NUMTYPES] = { /* 0: CD_MVERT */ {sizeof(MVert), "MVert", 1, NULL, NULL, NULL, NULL, NULL, NULL}, @@ -1312,6 +1323,10 @@ static const LayerTypeInfo LAYERTYPEINFO[CD_NUMTYPES] = { {sizeof(short[4][3]), "", 0, NULL, NULL, NULL, NULL, layerSwap_flnor, NULL}, /* 41: CD_CUSTOMLOOPNORMAL */ {sizeof(short[2]), "vec2s", 1, NULL, NULL, NULL, NULL, NULL, NULL}, + /* 42: CD_FACEMAP */ + {sizeof(int), "", 0, NULL, NULL, NULL, NULL, NULL, layerDefault_fmap, NULL}, + /* 43: CD_MSURFACE_SAMPLE */ + {sizeof(MSurfaceSample), "MSurfaceSample", 1, NULL, NULL, NULL, NULL, NULL, NULL}, }; /* note, numbers are from trunk and need updating for bmesh */ @@ -1328,12 +1343,13 @@ static const char *LAYERTYPENAMES[CD_NUMTYPES] = { /* 30-34 */ "CDSubSurfCrease", "CDOrigSpaceLoop", "CDPreviewLoopCol", "CDBMElemPyPtr", "CDPaintMask", /* 35-36 */ "CDGridPaintMask", "CDMVertSkin", /* 37-38 */ "CDFreestyleEdge", "CDFreestyleFace", - /* 39-41 */ "CDMLoopTangent", "CDTessLoopNormal", "CDCustomLoopNormal", + /* 39-42 */ "CDMLoopTangent", "CDTessLoopNormal", "CDCustomLoopNormal", "CDFaceMap", + /* 43 */ "CDMSurfaceSample", }; const CustomDataMask CD_MASK_BAREMESH = - CD_MASK_MVERT | CD_MASK_MEDGE | CD_MASK_MFACE | CD_MASK_MLOOP | CD_MASK_MPOLY | CD_MASK_BWEIGHT; + CD_MASK_MVERT | CD_MASK_MEDGE | CD_MASK_MFACE | CD_MASK_MLOOP | CD_MASK_MPOLY | CD_MASK_BWEIGHT | CD_MASK_FACEMAP; const CustomDataMask CD_MASK_MESH = CD_MASK_MVERT | CD_MASK_MEDGE | CD_MASK_MFACE | CD_MASK_MSTICKY | CD_MASK_MDEFORMVERT | CD_MASK_MTFACE | CD_MASK_MCOL | @@ -1341,13 +1357,13 @@ const CustomDataMask CD_MASK_MESH = CD_MASK_MLOOPUV | CD_MASK_MLOOPCOL | CD_MASK_MPOLY | CD_MASK_MLOOP | CD_MASK_MTEXPOLY | CD_MASK_RECAST | CD_MASK_PAINT_MASK | CD_MASK_GRID_PAINT_MASK | CD_MASK_MVERT_SKIN | CD_MASK_FREESTYLE_EDGE | CD_MASK_FREESTYLE_FACE | - CD_MASK_CUSTOMLOOPNORMAL; + CD_MASK_CUSTOMLOOPNORMAL | CD_MASK_FACEMAP; const CustomDataMask CD_MASK_EDITMESH = CD_MASK_MSTICKY | CD_MASK_MDEFORMVERT | CD_MASK_MTFACE | CD_MASK_MLOOPUV | CD_MASK_MLOOPCOL | CD_MASK_MTEXPOLY | CD_MASK_SHAPE_KEYINDEX | CD_MASK_MCOL | CD_MASK_PROP_FLT | CD_MASK_PROP_INT | CD_MASK_PROP_STR | CD_MASK_MDISPS | CD_MASK_SHAPEKEY | CD_MASK_RECAST | CD_MASK_PAINT_MASK | - CD_MASK_GRID_PAINT_MASK | CD_MASK_MVERT_SKIN | CD_MASK_CUSTOMLOOPNORMAL; + CD_MASK_GRID_PAINT_MASK | CD_MASK_MVERT_SKIN | CD_MASK_CUSTOMLOOPNORMAL | CD_MASK_FACEMAP; const CustomDataMask CD_MASK_DERIVEDMESH = CD_MASK_MSTICKY | CD_MASK_MDEFORMVERT | CD_MASK_MTFACE | CD_MASK_MCOL | CD_MASK_PROP_FLT | CD_MASK_PROP_INT | CD_MASK_CLOTH_ORCO | @@ -1355,17 +1371,28 @@ const CustomDataMask CD_MASK_DERIVEDMESH = CD_MASK_PROP_STR | CD_MASK_ORIGSPACE | CD_MASK_ORIGSPACE_MLOOP | CD_MASK_ORCO | CD_MASK_TANGENT | CD_MASK_PREVIEW_MCOL | CD_MASK_SHAPEKEY | CD_MASK_RECAST | CD_MASK_ORIGINDEX | CD_MASK_MVERT_SKIN | CD_MASK_FREESTYLE_EDGE | CD_MASK_FREESTYLE_FACE | - CD_MASK_CUSTOMLOOPNORMAL; + CD_MASK_CUSTOMLOOPNORMAL | CD_MASK_FACEMAP; const CustomDataMask CD_MASK_BMESH = CD_MASK_MLOOPUV | CD_MASK_MLOOPCOL | CD_MASK_MTEXPOLY | CD_MASK_MSTICKY | CD_MASK_MDEFORMVERT | CD_MASK_PROP_FLT | CD_MASK_PROP_INT | CD_MASK_PROP_STR | CD_MASK_SHAPEKEY | CD_MASK_SHAPE_KEYINDEX | CD_MASK_MDISPS | CD_MASK_CREASE | CD_MASK_BWEIGHT | CD_MASK_RECAST | CD_MASK_PAINT_MASK | CD_MASK_GRID_PAINT_MASK | CD_MASK_MVERT_SKIN | CD_MASK_FREESTYLE_EDGE | CD_MASK_FREESTYLE_FACE | - CD_MASK_CUSTOMLOOPNORMAL; + CD_MASK_CUSTOMLOOPNORMAL | CD_MASK_FACEMAP; const CustomDataMask CD_MASK_FACECORNERS = /* XXX Not used anywhere! */ CD_MASK_MTFACE | CD_MASK_MCOL | CD_MASK_MTEXPOLY | CD_MASK_MLOOPUV | CD_MASK_MLOOPCOL | CD_MASK_NORMAL | CD_MASK_MLOOPTANGENT; +const CustomDataMask CD_MASK_STRANDS = + CD_MASK_MVERT | CD_MASK_MEDGE | + CD_MASK_MSTICKY | CD_MASK_MDEFORMVERT | CD_MASK_MCOL | + CD_MASK_PROP_FLT | CD_MASK_PROP_INT | CD_MASK_PROP_STR | CD_MASK_MDISPS | + CD_MASK_MVERT_SKIN | CD_MASK_FREESTYLE_EDGE | + CD_MASK_MSURFACE_SAMPLE; +const CustomDataMask CD_MASK_STRANDS_BMESH = + CD_MASK_MSTICKY | CD_MASK_MDEFORMVERT | CD_MASK_PROP_FLT | CD_MASK_PROP_INT | + CD_MASK_PROP_STR | CD_MASK_SHAPEKEY | CD_MASK_SHAPE_KEYINDEX | CD_MASK_MDISPS | + CD_MASK_MVERT_SKIN | CD_MASK_FREESTYLE_EDGE | + CD_MASK_MSURFACE_SAMPLE; const CustomDataMask CD_MASK_EVERYTHING = CD_MASK_MVERT | CD_MASK_MSTICKY /* DEPRECATED */ | CD_MASK_MDEFORMVERT | CD_MASK_MEDGE | CD_MASK_MFACE | CD_MASK_MTFACE | CD_MASK_MCOL | CD_MASK_ORIGINDEX | CD_MASK_NORMAL /* | CD_MASK_POLYINDEX */ | CD_MASK_PROP_FLT | @@ -1377,7 +1404,7 @@ const CustomDataMask CD_MASK_EVERYTHING = /* BMESH ONLY END */ CD_MASK_PAINT_MASK | CD_MASK_GRID_PAINT_MASK | CD_MASK_MVERT_SKIN | CD_MASK_FREESTYLE_EDGE | CD_MASK_FREESTYLE_FACE | - CD_MASK_MLOOPTANGENT | CD_MASK_TESSLOOPNORMAL | CD_MASK_CUSTOMLOOPNORMAL; + CD_MASK_MLOOPTANGENT | CD_MASK_TESSLOOPNORMAL | CD_MASK_CUSTOMLOOPNORMAL | CD_MASK_FACEMAP | CD_MASK_MSURFACE_SAMPLE; static const LayerTypeInfo *layerType_getInfo(int type) { @@ -2826,6 +2853,18 @@ void *CustomData_bmesh_get_layer_n(const CustomData *data, void *block, int n) return POINTER_OFFSET(block, data->layers[n].offset); } +/*Bmesh Custom Data Functions. Should replace editmesh ones with these as well, due to more effecient memory alloc*/ +void *CustomData_bmesh_get_named(const CustomData *data, void *block, int type, const char *name) +{ + int layer_index; + + /* get the layer index of the named layer of type */ + layer_index = CustomData_get_named_layer_index(data, type, name); + if (layer_index == -1) return NULL; + + return (char *)block + data->layers[layer_index].offset; +} + bool CustomData_layer_has_math(const struct CustomData *data, int layer_n) { const LayerTypeInfo *typeInfo = layerType_getInfo(data->layers[layer_n].type); diff --git a/source/blender/blenkernel/intern/depsgraph.c b/source/blender/blenkernel/intern/depsgraph.c index 2fd53045e29..ffaafe94b96 100644 --- a/source/blender/blenkernel/intern/depsgraph.c +++ b/source/blender/blenkernel/intern/depsgraph.c @@ -45,6 +45,7 @@ #include "BLI_threads.h" #include "DNA_anim_types.h" +#include "DNA_cache_library_types.h" #include "DNA_camera_types.h" #include "DNA_group_types.h" #include "DNA_lamp_types.h" @@ -629,8 +630,22 @@ static void build_dag_object(DagForest *dag, DagNode *scenenode, Main *bmain, Sc /* inverted relation, so addtoroot shouldn't be set to zero */ } + /* XXX Fake dependency: duplicator object becomes a child of group objects. + * This exploits the layer visibility mechanism, making the group objects update + * when the duplicator is visible (even if group objects are not visible themselves). + * It is not a true dependency, the duplicator does not in any way depend on group objects or data! + */ if (ob->transflag & OB_DUPLI) { + /* XXX In theory it would be possible to disable the visibility dependency when dupli groups are cached, + * since we use the results from the cache instead of the generated object data anyway. + * However, the caching system depends a lot on DNA objects currently and behaves unpredictably without this ... + */ +#if 0 + bool is_cached = ob->cache_library && ob->cache_library->source_mode == CACHE_LIBRARY_SOURCE_CACHE; + if (!is_cached && (ob->transflag & OB_DUPLIGROUP) && ob->dup_group) { +#else if ((ob->transflag & OB_DUPLIGROUP) && ob->dup_group) { +#endif GroupObject *go; for (go = ob->dup_group->gobject.first; go; go = go->next) { if (go->ob) { @@ -770,6 +785,10 @@ static void build_dag_object(DagForest *dag, DagNode *scenenode, Main *bmain, Sc if (!psys_check_enabled(ob, psys)) continue; + key = psys->key; + if (key && key->adt) + dag_add_driver_relation(key->adt, dag, node, 1); + if (ELEM(part->phystype, PART_PHYS_KEYED, PART_PHYS_BOIDS)) { ParticleTarget *pt = psys->targets.first; @@ -798,7 +817,7 @@ static void build_dag_object(DagForest *dag, DagNode *scenenode, Main *bmain, Sc } } - effectors = pdInitEffectors(scene, ob, psys, part->effector_weights, false); + effectors = pdInitEffectors_ex(scene, ob, psys, ob->lay, part->effector_weights, false); if (effectors) { for (eff = effectors->first; eff; eff = eff->next) { @@ -2193,6 +2212,10 @@ static void dag_object_time_update_flags(Main *bmain, Scene *scene, Object *ob) } } + /* invalidate dupli cache */ + if (ob->dup_cache) + ob->dup_cache->flag |= DUPCACHE_FLAG_DIRTY; + if (ob->recalc & OB_RECALC_OB) lib_id_recalc_tag(bmain, &ob->id); if (ob->recalc & OB_RECALC_DATA) @@ -2281,6 +2304,90 @@ void DAG_scene_update_flags(Main *bmain, Scene *scene, unsigned int lay, const b } } +void DAG_scene_update_group_flags(Main *bmain, + Scene *scene, + Group *group, + unsigned int lay, + const bool do_time, + const bool do_invisible_flush) +{ + DagNode *root_node = scene->theDag->DagNode.first, *node; + GroupObject *go; + DagNodeQueue *queue; + + /* Tag all possible objects for update. */ + DAG_scene_update_flags(bmain, scene, lay, do_time, do_invisible_flush); + + /* Initialize colors of nodes. */ + for (node = root_node; node != NULL; node = node->next) { + node->color = DAG_WHITE; + node->scheduled = false; + } + + /* Tag nodes which corresponds to objects which are to be updated. */ + for (go = group->gobject.first; go != NULL; go = go->next) { + if (go->ob != NULL) { + node = dag_find_node(scene->theDag, go->ob); + if (node != NULL) { + node->scheduled = true; + } + } + } + + /* Flush schedule flags to parent. */ + queue = queue_create(DAGQUEUEALLOC); + for (node = root_node; node != NULL; node = node->next) { + if (node->color == DAG_WHITE) { + push_stack(queue, node); + node->color = DAG_GRAY; + while (queue->count) { + DagNode *current_node = get_top_node_queue(queue); + DagAdjList *itA; + bool skip = false; + /* Check if all child nodes were scheduled. */ + for (itA = current_node->child; itA; itA = itA->next) { + if (itA->node->color == DAG_WHITE) { + itA->node->color = DAG_GRAY; + push_stack(queue, itA->node); + skip = true; + break; + } + } + /* Check if there are scheduled children and if so schedule + * current node as well since it's needed for chidlren. + */ + if (!skip) { + current_node = pop_queue(queue); + if (current_node->type == ID_OB) { + for (itA = current_node->child; itA; itA = itA->next) { + if (itA->node->scheduled) { + current_node->scheduled = true; + break; + } + } + } + node->color = DAG_BLACK; + } + } + } + } + queue_delete(queue); + + /* Clear recalc flags from objects which corresponds to nodes which are + * not needed for the interesting group update. + */ + for (node = root_node; node != NULL; node = node->next) { + if (node->type == ID_OB) { + Object *object = node->ob; + if (!node->scheduled) { + object->recalc &= ~OB_RECALC_ALL; + } + } + node->color = DAG_WHITE; + node->scheduled = false; + } +} + /* struct returned by DagSceneLayer */ typedef struct DagSceneLayer { struct DagSceneLayer *next, *prev; @@ -2559,12 +2666,26 @@ static void dag_id_flush_update(Main *bmain, Scene *sce, ID *id) /* set flags based on ShapeKey */ if (idtype == ID_KE) { for (obt = bmain->object.first; obt; obt = obt->id.next) { - Key *key = BKE_key_from_object(obt); - if (!(ob && obt == ob) && ((ID *)key == id)) { - obt->flag |= (OB_RECALC_OB | OB_RECALC_DATA); - lib_id_recalc_tag(bmain, &obt->id); - lib_id_recalc_data_tag(bmain, &obt->id); - BKE_ptcache_object_reset(sce, obt, PTCACHE_RESET_DEPSGRAPH); + if (!(ob && obt == ob)) { + Key *key = BKE_key_from_object(obt); + ParticleSystem *psys; + + if ((ID *)key == id) { + obt->flag |= (OB_RECALC_OB | OB_RECALC_DATA); + lib_id_recalc_tag(bmain, &obt->id); + lib_id_recalc_data_tag(bmain, &obt->id); + BKE_ptcache_object_reset(sce, obt, PTCACHE_RESET_DEPSGRAPH); + } + + for (psys = obt->particlesystem.first; psys; psys = psys->next) { + key = psys->key; + if ((ID *)key == id) { + obt->flag |= (OB_RECALC_OB | OB_RECALC_DATA); + lib_id_recalc_tag(bmain, &obt->id); + lib_id_recalc_data_tag(bmain, &obt->id); + BKE_ptcache_object_reset(sce, obt, PTCACHE_RESET_DEPSGRAPH); + } + } } } } @@ -2645,6 +2766,21 @@ static void dag_id_flush_update(Main *bmain, Scene *sce, ID *id) } } } + + /* set flags based on CacheLibrary */ + if (idtype == ID_CL) { + for (obt = bmain->object.first; obt; obt = obt->id.next) { + if (!(ob && obt == ob) && ((ID *)obt->cache_library == id)) { + obt->flag |= (OB_RECALC_OB | OB_RECALC_DATA); + lib_id_recalc_tag(bmain, &obt->id); + lib_id_recalc_data_tag(bmain, &obt->id); + + /* invalidate dupli cache */ + if (obt->dup_cache) + obt->dup_cache->flag |= DUPCACHE_FLAG_DIRTY; + } + } + } /* camera's matrix is used to orient reconstructed stuff, * so it should happen tracking-related constraints recalculation diff --git a/source/blender/blenkernel/intern/dynamicpaint.c b/source/blender/blenkernel/intern/dynamicpaint.c index 8dac578ac0a..ce9e85c6813 100644 --- a/source/blender/blenkernel/intern/dynamicpaint.c +++ b/source/blender/blenkernel/intern/dynamicpaint.c @@ -4221,7 +4221,7 @@ static int dynamicPaint_prepareEffectStep(DynamicPaintSurface *surface, Scene *s /* Init force data if required */ if (surface->effect & MOD_DPAINT_EFFECT_DO_DRIP) { float vel[3] = {0}; - ListBase *effectors = pdInitEffectors(scene, ob, NULL, surface->effector_weights, true); + ListBase *effectors = pdInitEffectors(scene, ob, NULL, surface->effector_weights); /* allocate memory for force data (dir vector + strength) */ *force = MEM_mallocN(sData->total_points * 4 * sizeof(float), "PaintEffectForces"); diff --git a/source/blender/blenkernel/intern/editstrands.c b/source/blender/blenkernel/intern/editstrands.c new file mode 100644 index 00000000000..fcf98dfc2ee --- /dev/null +++ b/source/blender/blenkernel/intern/editstrands.c @@ -0,0 +1,272 @@ +/* + * ***** 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) Blender Foundation + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): Lukas Toenne + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/blenkernel/intern/editstrands.c + * \ingroup bke + */ + +#include "MEM_guardedalloc.h" + +#include "BLI_math.h" +#include "BLI_mempool.h" + +#include "DNA_cache_library_types.h" +#include "DNA_customdata_types.h" +#include "DNA_modifier_types.h" +#include "DNA_object_types.h" +#include "DNA_particle_types.h" +#include "DNA_strands_types.h" + +#include "BKE_bvhutils.h" +#include "BKE_cache_library.h" +#include "BKE_customdata.h" +#include "BKE_cdderivedmesh.h" +#include "BKE_DerivedMesh.h" +#include "BKE_editstrands.h" +#include "BKE_effect.h" +#include "BKE_mesh_sample.h" +#include "BKE_particle.h" + +#include "BPH_strands.h" + +#include "intern/bmesh_strands_conv.h" + +/* mat can be used to transform the dm into another space, + * in case the edited object is not the active object: + * mat = inv(M_act) * M_edit + */ +BMEditStrands *BKE_editstrands_create(BMesh *bm, DerivedMesh *root_dm, float mat[4][4]) +{ + BMEditStrands *es = MEM_callocN(sizeof(BMEditStrands), __func__); + + es->bm = bm; + es->root_dm = CDDM_copy(root_dm); + + if (mat) { + DerivedMesh *dm = es->root_dm; + MVert *mv = dm->getVertArray(dm); + int totvert = dm->getNumVerts(dm), i; + for (i = 0; i < totvert; ++i, ++mv) { + mul_m4_v3(mat, mv->co); + } + } + + return es; +} + +BMEditStrands *BKE_editstrands_copy(BMEditStrands *es) +{ + BMEditStrands *es_copy = MEM_callocN(sizeof(BMEditStrands), __func__); + *es_copy = *es; + + es_copy->bm = BM_mesh_copy(es->bm); + es_copy->root_dm = CDDM_copy(es->root_dm); + + return es_copy; +} + +/** + * \brief Return the BMEditStrands for a given object + */ +BMEditStrands *BKE_editstrands_from_object(Object *ob) +{ + { + ParticleSystem *psys = psys_get_current(ob); + if (psys && psys->hairedit) + return psys->hairedit; + } + + { + StrandsKeyCacheModifier *skmd; + if (BKE_cache_modifier_strands_key_get(ob, &skmd, NULL, NULL, NULL, NULL, NULL)) { + if (skmd->edit) + return skmd->edit; + } + } + + return NULL; +} + +void BKE_editstrands_update_linked_customdata(BMEditStrands *es) +{ + BMesh *bm = es->bm; + + /* this is done for BMEditMesh, but should never exist for strands */ + BLI_assert(!CustomData_has_layer(&bm->pdata, CD_MTEXPOLY)); +} + +/*does not free the BMEditStrands struct itself*/ +void BKE_editstrands_free(BMEditStrands *es) +{ + if (es->bm) + BM_mesh_free(es->bm); + if (es->root_dm) + es->root_dm->release(es->root_dm); +} + +/* === constraints === */ + +BMEditStrandsLocations BKE_editstrands_get_locations(BMEditStrands *edit) +{ + BMesh *bm = edit->bm; + BMEditStrandsLocations locs = MEM_mallocN(3*sizeof(float) * bm->totvert, "editstrands locations"); + + BMVert *v; + BMIter iter; + int i; + + BM_ITER_MESH_INDEX(v, &iter, bm, BM_VERTS_OF_MESH, i) { + copy_v3_v3(locs[i], v->co); + } + + return locs; +} + +void BKE_editstrands_free_locations(BMEditStrandsLocations locs) +{ + MEM_freeN(locs); +} + +void BKE_editstrands_solve_constraints(Object *ob, BMEditStrands *es, BMEditStrandsLocations orig) +{ + BKE_editstrands_ensure(es); + + BPH_strands_solve_constraints(ob, es, orig); +} + +static void editstrands_calc_segment_lengths(BMesh *bm) +{ + BMVert *root; + BMIter iter; + + BM_ITER_STRANDS(root, &iter, bm, BM_STRANDS_OF_MESH) { + BMVert *v, *vprev = NULL; + BMIter iter_strand; + BM_ITER_STRANDS_ELEM(v, &iter_strand, root, BM_VERTS_OF_STRAND) { + if (vprev) { + float length = len_v3v3(v->co, vprev->co); + BM_elem_float_data_named_set(&bm->vdata, vprev, CD_PROP_FLT, CD_HAIR_SEGMENT_LENGTH, length); + } + vprev = v; + } + if (vprev) { + /* set last to 0 */ + BM_elem_float_data_named_set(&bm->vdata, vprev, CD_PROP_FLT, CD_HAIR_SEGMENT_LENGTH, 0.0f); + } + } +} + +void BKE_editstrands_ensure(BMEditStrands *es) +{ + BM_strands_cd_flag_ensure(es->bm, 0); + + if (es->flag & BM_STRANDS_DIRTY_SEGLEN) { + editstrands_calc_segment_lengths(es->bm); + + es->flag &= ~BM_STRANDS_DIRTY_SEGLEN; + } +} + + +/* === cache shape key conversion === */ + +BMesh *BKE_cache_strands_to_bmesh(struct Strands *strands, struct Key *key, float mat[4][4], int act_key_nr, DerivedMesh *dm) +{ + const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_STRANDS(strands); + BMesh *bm; + + DM_ensure_tessface(dm); + + bm = BM_mesh_create(&allocsize); + BM_strands_bm_from_strands(bm, strands, mat, key, dm, true, act_key_nr); + editstrands_calc_segment_lengths(bm); + + return bm; +} + +struct Strands *BKE_cache_strands_from_bmesh(BMEditStrands *edit, struct Key *key, float mat[4][4], DerivedMesh *dm) +{ + BMesh *bm = edit ? edit->bm : NULL; + Strands *strands = NULL; + + if (bm && dm) { + BVHTreeFromMesh bvhtree = {NULL}; + + DM_ensure_tessface(dm); + + bvhtree_from_mesh_faces(&bvhtree, dm, 0.0, 2, 6); + + strands = BM_strands_bm_to_strands(bm, strands, mat, key, dm, &bvhtree); + + free_bvhtree_from_mesh(&bvhtree); + } + + return strands; +} + + +/* === particle conversion === */ + +BMesh *BKE_particles_to_bmesh(Object *ob, ParticleSystem *psys) +{ + ParticleSystemModifierData *psmd = psys_get_modifier(ob, psys); + + const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_PSYS(psys); + BMesh *bm; + + bm = BM_mesh_create(&allocsize); + + if (psmd && psmd->dm) { + DM_ensure_tessface(psmd->dm); + + BM_strands_bm_from_psys(bm, ob, psys, psmd->dm, true, /*psys->shapenr*/ -1); + + editstrands_calc_segment_lengths(bm); + } + + return bm; +} + +void BKE_particles_from_bmesh(Object *ob, ParticleSystem *psys) +{ + ParticleSystemModifierData *psmd = psys_get_modifier(ob, psys); + BMesh *bm = psys->hairedit ? psys->hairedit->bm : NULL; + + if (bm) { + if (psmd && psmd->dm) { + BVHTreeFromMesh bvhtree = {NULL}; + + DM_ensure_tessface(psmd->dm); + + bvhtree_from_mesh_faces(&bvhtree, psmd->dm, 0.0, 2, 6); + + BM_strands_bm_to_psys(bm, ob, psys, psmd->dm, &bvhtree); + + free_bvhtree_from_mesh(&bvhtree); + } + } +} diff --git a/source/blender/blenkernel/intern/effect.c b/source/blender/blenkernel/intern/effect.c index 785561d8239..4842b40bf84 100644 --- a/source/blender/blenkernel/intern/effect.c +++ b/source/blender/blenkernel/intern/effect.c @@ -203,18 +203,17 @@ static void add_particles_to_effectors(ListBase **effectors, Scene *scene, Effec } /* returns ListBase handle with objects taking part in the effecting */ -ListBase *pdInitEffectors(Scene *scene, Object *ob_src, ParticleSystem *psys_src, - EffectorWeights *weights, bool precalc) +ListBase *pdInitEffectors_ex(Scene *scene, Object *ob_src, ParticleSystem *psys_src, int layers, + EffectorWeights *weights, bool precalc) { Base *base; - unsigned int layer= ob_src->lay; ListBase *effectors = NULL; if (weights->group) { GroupObject *go; for (go= weights->group->gobject.first; go; go= go->next) { - if ( (go->ob->lay & layer) ) { + if ( (go->ob->lay & layers) ) { if ( go->ob->pd && go->ob->pd->forcefield ) add_object_to_effectors(&effectors, scene, weights, go->ob, ob_src); @@ -229,7 +228,7 @@ ListBase *pdInitEffectors(Scene *scene, Object *ob_src, ParticleSystem *psys_src } else { for (base = scene->base.first; base; base= base->next) { - if ( (base->lay & layer) ) { + if ( (base->lay & layers) ) { if ( base->object->pd && base->object->pd->forcefield ) add_object_to_effectors(&effectors, scene, weights, base->object, ob_src); @@ -249,6 +248,12 @@ ListBase *pdInitEffectors(Scene *scene, Object *ob_src, ParticleSystem *psys_src return effectors; } +ListBase *pdInitEffectors(Scene *scene, Object *ob_src, ParticleSystem *psys_src, + EffectorWeights *weights) +{ + return pdInitEffectors_ex(scene, ob_src, psys_src, ob_src->lay, weights, true); +} + void pdEndEffectors(ListBase **effectors) { if (*effectors) { @@ -831,6 +836,10 @@ static void do_physical_effector(EffectorCache *eff, EffectorData *efd, Effected } copy_v3_v3(force, efd->vec_to_point); + if (pd->shape == PFIELD_SHAPE_SURFACE && (pd->flag & PFIELD_USE_SIGNED_DISTANCE)) { + if (dot_v3v3(efd->vec_to_point, efd->nor) < 0.0f) + mul_v3_fl(force, -1.0f); + } switch (pd->forcefield) { case PFIELD_WIND: @@ -1121,10 +1130,10 @@ static void debug_data_insert(SimDebugData *debug_data, SimDebugElement *elem) BLI_ghash_insert(debug_data->gh, elem, elem); } -void BKE_sim_debug_data_add_element(int type, const float v1[3], const float v2[3], float r, float g, float b, const char *category, unsigned int hash) +void BKE_sim_debug_data_add_element(int type, const float v1[3], const float v2[3], + float r, float g, float b, const char *category, unsigned int hash) { unsigned int category_hash = BLI_ghashutil_strhash_p(category); - SimDebugElement *elem; if (!_sim_debug_data) { if (G.debug & G_DEBUG_SIMDATA) @@ -1133,6 +1142,16 @@ void BKE_sim_debug_data_add_element(int type, const float v1[3], const float v2[ return; } + BKE_sim_debug_data_add_element_ex(_sim_debug_data, type, v1, v2, r, g, b, category_hash, hash); +} + +void BKE_sim_debug_data_add_element_ex(SimDebugData *debug_data, int type, const float v1[3], const float v2[3], + float r, float g, float b, unsigned int category_hash, unsigned int hash) +{ + SimDebugElement *elem; + if (!debug_data) + return; + elem = MEM_callocN(sizeof(SimDebugElement), "sim debug data element"); elem->type = type; elem->category_hash = category_hash; @@ -1143,17 +1162,22 @@ void BKE_sim_debug_data_add_element(int type, const float v1[3], const float v2[ copy_v3_v3(elem->v1, v1); copy_v3_v3(elem->v2, v2); - debug_data_insert(_sim_debug_data, elem); + debug_data_insert(debug_data, elem); } void BKE_sim_debug_data_remove_element(unsigned int hash) { + BKE_sim_debug_data_remove_element_ex(_sim_debug_data, hash); +} + +void BKE_sim_debug_data_remove_element_ex(SimDebugData *debug_data, unsigned int hash) +{ SimDebugElement dummy; - if (!_sim_debug_data) + if (!debug_data) return; dummy.hash = hash; - BLI_ghash_remove(_sim_debug_data->gh, &dummy, NULL, debug_element_free); + BLI_ghash_remove(debug_data->gh, &dummy, NULL, debug_element_free); } void BKE_sim_debug_data_clear(void) diff --git a/source/blender/blenkernel/intern/facemap.c b/source/blender/blenkernel/intern/facemap.c new file mode 100644 index 00000000000..2dca535bea3 --- /dev/null +++ b/source/blender/blenkernel/intern/facemap.c @@ -0,0 +1,259 @@ + +/* + * ***** 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2008 Blender Foundation. + * All rights reserved. + * + * + * Contributor(s): Blender Foundation + * + * ***** END GPL LICENSE BLOCK ***** + */ + + +#include "DNA_object_types.h" +#include "DNA_mesh_types.h" + +#include "WM_types.h" +#include "WM_api.h" + +#include "BKE_context.h" +#include "BKE_customdata.h" +#include "BKE_facemap.h" +#include "BKE_editmesh.h" +#include "BKE_object.h" +#include "BKE_object_deform.h" + +#include "BKE_depsgraph.h" + +#include "BLI_utildefines.h" +#include "BLI_path_util.h" +#include "BLI_string.h" +#include "BLI_listbase.h" + +#include "BLF_translation.h" + +#include "MEM_guardedalloc.h" + +#include "RNA_define.h" +#include "RNA_access.h" + +#include <string.h> + + +static bool fmap_unique_check(void *arg, const char *name) +{ + struct {Object *ob; void *fm; } *data = arg; + + bFaceMap *fmap; + + for (fmap = data->ob->fmaps.first; fmap; fmap = fmap->next) { + if (data->fm != fmap) { + if (!strcmp(fmap->name, name)) { + return true; + } + } + } + + return false; +} + +static bFaceMap *fmap_duplicate(bFaceMap *infmap) +{ + bFaceMap *outfmap; + + if (!infmap) + return NULL; + + outfmap = MEM_callocN(sizeof(bFaceMap), "copy facemap"); + + /* For now, just copy everything over. */ + memcpy(outfmap, infmap, sizeof(bFaceMap)); + + outfmap->next = outfmap->prev = NULL; + + return outfmap; +} + +void fmap_copy_list(ListBase *outbase, ListBase *inbase) +{ + bFaceMap *fmap, *fmapn; + + BLI_listbase_clear(outbase); + + for (fmap = inbase->first; fmap; fmap = fmap->next) { + fmapn = fmap_duplicate(fmap); + BLI_addtail(outbase, fmapn); + } +} + +void fmap_unique_name(bFaceMap *fmap, Object *ob) +{ + struct {Object *ob; void *fmap; } data; + data.ob = ob; + data.fmap = fmap; + + BLI_uniquename_cb(fmap_unique_check, &data, DATA_("Group"), '.', fmap->name, sizeof(fmap->name)); +} + +bFaceMap *BKE_object_facemap_add_name(Object *ob, const char *name) +{ + bFaceMap *fmap; + + if (!ob || ob->type != OB_MESH) + return NULL; + + fmap = MEM_callocN(sizeof(bFaceMap), __func__); + + BLI_strncpy(fmap->name, name, sizeof(fmap->name)); + + BLI_addtail(&ob->fmaps, fmap); + + ob->actfmap = BLI_listbase_count(&ob->fmaps); + + fmap_unique_name(fmap, ob); + + return fmap; +} + +bFaceMap *BKE_object_facemap_add(Object *ob) +{ + return BKE_object_facemap_add_name(ob, DATA_("FaceMap")); +} + + +static void object_fmap_remove_edit_mode(Object *ob, bFaceMap *fmap, bool do_selected, bool purge) +{ + const int fmap_nr = BLI_findindex(&ob->fmaps, fmap); + + if (ob->type == OB_MESH) { + Mesh *me = ob->data; + + if (me->edit_btmesh) { + BMEditMesh *em = me->edit_btmesh; + const int cd_fmap_offset = CustomData_get_offset(&em->bm->pdata, CD_FACEMAP); + + if (cd_fmap_offset != -1) { + BMFace *efa; + BMIter iter; + int *map; + + if (purge) { + BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { + map = BM_ELEM_CD_GET_VOID_P(efa, cd_fmap_offset); + + if (map) { + if (*map == fmap_nr) + *map = -1; + else if (*map > fmap_nr) + *map -= 1; + } + } + } + else { + BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { + map = BM_ELEM_CD_GET_VOID_P(efa, cd_fmap_offset); + + if (map && *map == fmap_nr && (!do_selected || BM_elem_flag_test(efa, BM_ELEM_SELECT))) { + *map = -1; + } + } + } + } + + if (ob->actfmap == BLI_listbase_count(&ob->fmaps)) + ob->actfmap--; + + BLI_remlink(&ob->fmaps, fmap); + MEM_freeN(fmap); + } + } +} + +static void object_fmap_remove_object_mode(Object *ob, bFaceMap *fmap, bool purge) +{ + const int fmap_nr = BLI_findindex(&ob->fmaps, fmap); + + if (ob->type == OB_MESH) { + Mesh *me = ob->data; + + if (CustomData_has_layer(&me->pdata, CD_FACEMAP)) { + int *map = CustomData_get_layer(&me->pdata, CD_FACEMAP); + int i; + + if (map) { + for (i = 0; i < me->totpoly; i++) { + if (map[i] == fmap_nr) + map[i] = -1; + else if (purge && map[i] > fmap_nr) + map[i]--; + } + } + } + + if (ob->actfmap == BLI_listbase_count(&ob->fmaps)) + ob->actfmap--; + + BLI_remlink(&ob->fmaps, fmap); + MEM_freeN(fmap); + } +} + +void BKE_object_facemap_remove(Object *ob, bFaceMap *fmap) +{ + if (BKE_object_is_in_editmode(ob)) + object_fmap_remove_edit_mode(ob, fmap, false, true); + else + object_fmap_remove_object_mode(ob, fmap, true); +} + +void BKE_object_fmap_remove_all(Object *ob) +{ + bFaceMap *fmap = (bFaceMap *)ob->fmaps.first; + + if (fmap) { + const bool edit_mode = BKE_object_is_in_editmode_vgroup(ob); + + while (fmap) { + bFaceMap *next_fmap = fmap->next; + + if (edit_mode) + object_fmap_remove_edit_mode(ob, fmap, false, false); + else + object_fmap_remove_object_mode(ob, fmap, false); + + fmap = next_fmap; + } + } + /* remove all dverts */ + if (ob->type == OB_MESH) { + Mesh *me = ob->data; + CustomData_free_layer(&me->pdata, CD_FACEMAP, me->totpoly, 0); + } + ob->actfmap = 0; +} + +int fmap_name_index(Object *ob, const char *name) +{ + return (name) ? BLI_findstringindex(&ob->fmaps, name, offsetof(bFaceMap, name)) : -1; +} + +bFaceMap *fmap_find_name(Object *ob, const char *name) +{ + return BLI_findstring(&ob->fmaps, name, offsetof(bFaceMap, name)); +} diff --git a/source/blender/blenkernel/intern/fmodifier.c b/source/blender/blenkernel/intern/fmodifier.c index 396a260c3b8..6e78d08b508 100644 --- a/source/blender/blenkernel/intern/fmodifier.c +++ b/source/blender/blenkernel/intern/fmodifier.c @@ -1398,7 +1398,10 @@ float evaluate_time_fmodifiers(FModifierStackStorage *storage, ListBase *modifie /* sanity checks */ if (ELEM(NULL, modifiers, modifiers->last)) return evaltime; - + + if (fcu->flag & FCURVE_MOD_OFF) + return evaltime; + /* Starting from the end of the stack, calculate the time effects of various stacked modifiers * on the time the F-Curve should be evaluated at. * @@ -1455,6 +1458,9 @@ void evaluate_value_fmodifiers(FModifierStackStorage *storage, ListBase *modifie /* sanity checks */ if (ELEM(NULL, modifiers, modifiers->first)) return; + + if (fcu->flag & FCURVE_MOD_OFF) + return; /* evaluate modifiers */ for (fcm = modifiers->first; fcm; fcm = fcm->next) { diff --git a/source/blender/blenkernel/intern/group.c b/source/blender/blenkernel/intern/group.c index ae3ab833a87..78f8a42ffe6 100644 --- a/source/blender/blenkernel/intern/group.c +++ b/source/blender/blenkernel/intern/group.c @@ -36,6 +36,7 @@ #include "MEM_guardedalloc.h" +#include "DNA_cache_library_types.h" #include "DNA_group_types.h" #include "DNA_material_types.h" #include "DNA_object_types.h" @@ -45,7 +46,7 @@ #include "BLI_blenlib.h" #include "BLI_utildefines.h" - +#include "BKE_cache_library.h" #include "BKE_depsgraph.h" #include "BKE_global.h" #include "BKE_group.h" @@ -318,7 +319,7 @@ bool BKE_group_is_animated(Group *group, Object *UNUSED(parent)) GroupObject *go; #if 0 /* XXX OLD ANIMSYS, NLASTRIPS ARE NO LONGER USED */ - if (parent->nlastrips.first) + if (parent && parent->nlastrips.first) return 1; #endif @@ -385,7 +386,7 @@ void BKE_group_handle_recalc_and_update(EvaluationContext *eval_ctx, Scene *scen * but when its enabled at some point it will need to be changed so as not to update so much - campbell */ /* if animated group... */ - if (parent->nlastrips.first) { + if (parent && parent->nlastrips.first) { int cfrao; /* switch to local time */ diff --git a/source/blender/blenkernel/intern/idcode.c b/source/blender/blenkernel/intern/idcode.c index 091d8a6ea17..82612558966 100644 --- a/source/blender/blenkernel/intern/idcode.c +++ b/source/blender/blenkernel/intern/idcode.c @@ -57,6 +57,7 @@ static IDType idtypes[] = { { ID_AC, "Action", "actions", BLF_I18NCONTEXT_ID_ACTION, IDTYPE_FLAGS_ISLINKABLE }, { ID_AR, "Armature", "armatures", BLF_I18NCONTEXT_ID_ARMATURE, IDTYPE_FLAGS_ISLINKABLE }, { ID_BR, "Brush", "brushes", BLF_I18NCONTEXT_ID_BRUSH, IDTYPE_FLAGS_ISLINKABLE }, + { ID_CL, "CacheLibrary", "cache_libraries", BLF_I18NCONTEXT_ID_CACHELIBRARY, IDTYPE_FLAGS_ISLINKABLE }, { ID_CA, "Camera", "cameras", BLF_I18NCONTEXT_ID_CAMERA, IDTYPE_FLAGS_ISLINKABLE }, { ID_CU, "Curve", "curves", BLF_I18NCONTEXT_ID_CURVE, IDTYPE_FLAGS_ISLINKABLE }, { ID_GD, "GPencil", "grease_pencil", BLF_I18NCONTEXT_ID_GPENCIL, IDTYPE_FLAGS_ISLINKABLE }, /* rename gpencil */ diff --git a/source/blender/blenkernel/intern/key.c b/source/blender/blenkernel/intern/key.c index 8427b17c01a..092dd90041f 100644 --- a/source/blender/blenkernel/intern/key.c +++ b/source/blender/blenkernel/intern/key.c @@ -45,34 +45,43 @@ #include "DNA_anim_types.h" #include "DNA_key_types.h" #include "DNA_lattice_types.h" +#include "DNA_material_types.h" #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" +#include "DNA_modifier_types.h" #include "DNA_object_types.h" +#include "DNA_particle_types.h" #include "DNA_scene_types.h" +#include "DNA_strands_types.h" +#include "DNA_texture_types.h" #include "BKE_animsys.h" #include "BKE_curve.h" #include "BKE_customdata.h" #include "BKE_deform.h" +#include "BKE_pointcache.h" #include "BKE_global.h" #include "BKE_key.h" #include "BKE_lattice.h" #include "BKE_library.h" +#include "BKE_particle.h" #include "BKE_editmesh.h" #include "BKE_scene.h" - +#include "BKE_strands.h" #include "RNA_access.h" -#define KEY_MODE_DUMMY 0 /* use where mode isn't checked for */ -#define KEY_MODE_BPOINT 1 -#define KEY_MODE_BEZTRIPLE 2 +#include "RE_render_ext.h" /* old defines from DNA_ipo_types.h for data-type, stored in DNA - don't modify! */ #define IPO_FLOAT 4 #define IPO_BEZTRIPLE 100 #define IPO_BPOINT 101 +#define KEY_MODE_DUMMY 0 /* use where mode isn't checked for */ +#define KEY_MODE_BPOINT 1 +#define KEY_MODE_BEZTRIPLE 2 + void BKE_key_free(Key *key) { KeyBlock *kb; @@ -97,55 +106,82 @@ void BKE_key_free_nolib(Key *key) } } -Key *BKE_key_add(ID *id) /* common function */ +static void key_set_elemstr(short fromtype, char *r_elemstr, int *r_elemsize) +{ + /* XXX the code here uses some defines which will soon be deprecated... */ + char elemtype = IPO_FLOAT; + char numelem = 0; + int elemsize = 0; + + switch (fromtype) { + case KEY_OWNER_MESH: + numelem = 3; + elemtype = IPO_FLOAT; + elemsize = 12; + break; + case KEY_OWNER_LATTICE: + numelem = 3; + elemtype = IPO_FLOAT; + elemsize = 12; + break; + case KEY_OWNER_CURVE: + numelem = 4; + elemtype = IPO_BPOINT; + elemsize = 16; + break; + case KEY_OWNER_PARTICLES: + numelem = 3; + elemtype = IPO_FLOAT; + elemsize = 12; + break; + case KEY_OWNER_CACHELIB: + numelem = 3; + elemtype = IPO_FLOAT; + elemsize = 12; + break; + } + + r_elemstr[0] = numelem; + r_elemstr[1] = elemtype; + r_elemstr[2] = 0; + *r_elemsize = elemsize; +} + +Key *BKE_key_add_ex(ID *from, int fromtype, int fromindex) /* common function */ { Key *key; - char *el; key = BKE_libblock_alloc(G.main, ID_KE, "Key"); key->type = KEY_NORMAL; - key->from = id; + key->from = from; + key->fromtype = fromtype; + key->fromindex = fromindex; key->uidgen = 1; - /* XXX the code here uses some defines which will soon be deprecated... */ - switch (GS(id->name)) { - case ID_ME: - el = key->elemstr; - - el[0] = 3; - el[1] = IPO_FLOAT; - el[2] = 0; - - key->elemsize = 12; - - break; - case ID_LT: - el = key->elemstr; - - el[0] = 3; - el[1] = IPO_FLOAT; - el[2] = 0; - - key->elemsize = 12; - - break; - case ID_CU: - el = key->elemstr; - - el[0] = 4; - el[1] = IPO_BPOINT; - el[2] = 0; - - key->elemsize = 16; - - break; - } + key_set_elemstr(fromtype, key->elemstr, &key->elemsize); return key; } +Key *BKE_key_add(ID *id) +{ + int fromtype = 0; + switch (GS(id->name)) { + case ID_ME: fromtype = KEY_OWNER_MESH; break; + case ID_CU: fromtype = KEY_OWNER_CURVE; break; + case ID_LT: fromtype = KEY_OWNER_LATTICE; break; + default: BLI_assert(false); break; /* other fromtypes should use the _ex version for specifying the type */ + } + return BKE_key_add_ex(id, fromtype, -1); +} + +Key *BKE_key_add_particles(Object *ob, ParticleSystem *psys) /* particles are "special" */ +{ + return BKE_key_add_ex((ID *)ob, KEY_OWNER_PARTICLES, BLI_findindex(&ob->particlesystem, psys)); +} + Key *BKE_key_copy(Key *key) { Key *keyn; @@ -250,6 +286,28 @@ void BKE_key_sort(Key *key) key->refkey = key->block.first; } +void BKE_key_set_from_id(Key *key, ID *id) +{ + if (key) { + key->from = id; + switch (GS(id->name)) { + case ID_ME: key->fromtype = KEY_OWNER_MESH; break; + case ID_CU: key->fromtype = KEY_OWNER_CURVE; break; + case ID_LT: key->fromtype = KEY_OWNER_LATTICE; break; + } + key->fromindex = -1; + } +} + +void BKE_key_set_from_particles(Key *key, Object *ob, ParticleSystem *psys) +{ + if (key) { + key->from = (ID *)ob; + key->fromtype = KEY_OWNER_PARTICLES; + key->fromindex = BLI_findindex(&ob->particlesystem, psys); + } +} + /**************** do the key ****************/ void key_curve_position_weights(float t, float data[4], int type) @@ -514,7 +572,7 @@ static char *key_block_get_data(Key *key, KeyBlock *actkb, KeyBlock *kb, char ** if (kb == actkb) { /* this hack makes it possible to edit shape keys in * edit mode with shape keys blending applied */ - if (GS(key->from->name) == ID_ME) { + if (key->from && key->fromtype == KEY_OWNER_MESH) { Mesh *me; BMVert *eve; BMIter iter; @@ -546,20 +604,20 @@ static char *key_block_get_data(Key *key, KeyBlock *actkb, KeyBlock *kb, char ** /* currently only the first value of 'ofs' may be set. */ static bool key_pointer_size(const Key *key, const int mode, int *poinsize, int *ofs) { - if (key->from == NULL) { + /* some types allow NULL for key->from */ + if (!key->from && !ELEM(key->fromtype, KEY_OWNER_CACHELIB)) return false; - } - - switch (GS(key->from->name)) { - case ID_ME: + + switch (key->fromtype) { + case KEY_OWNER_MESH: *ofs = sizeof(float) * 3; *poinsize = *ofs; break; - case ID_LT: + case KEY_OWNER_LATTICE: *ofs = sizeof(float) * 3; *poinsize = *ofs; break; - case ID_CU: + case KEY_OWNER_CURVE: if (mode == KEY_MODE_BPOINT) { *ofs = sizeof(float) * 4; *poinsize = *ofs; @@ -568,13 +626,20 @@ static bool key_pointer_size(const Key *key, const int mode, int *poinsize, int ofs[0] = sizeof(float) * 12; *poinsize = (*ofs) / 3; } - break; + case KEY_OWNER_PARTICLES: + *ofs = sizeof(float) * 3; + *poinsize = *ofs; + break; + case KEY_OWNER_CACHELIB: + *ofs = sizeof(float) * 3; + *poinsize = *ofs; + break; + default: BLI_assert(!"invalid 'key->from' ID type"); return false; } - return true; } @@ -696,6 +761,87 @@ static void cp_key(const int start, int end, const int tot, char *poin, Key *key if (freekref) MEM_freeN(freekref); } +static void cp_key_strands(const int start, int end, const int tot, char *poin, Key *key, KeyBlock *actkb, KeyBlock *kb, float *weights, const int mode) +{ + float ktot = 0.0, kd = 0.0; + int elemsize, poinsize = 0, a, ofs, flagflo = 0; + char *k1, *freek1; + + /* currently always 0, in future key_pointer_size may assign */ + ofs = 0; + + if (!key_pointer_size(key, mode, &poinsize, &ofs)) + return; + + if (end > tot) end = tot; + + if (tot != kb->totelem) { + ktot = 0.0; + flagflo = 1; + if (kb->totelem) { + kd = kb->totelem / (float)tot; + } + else { + return; + } + } + + k1 = key_block_get_data(key, actkb, kb, &freek1); + + /* this exception is needed curves with multiple splines */ + if (start != 0) { + + poin += poinsize * start; + + if (flagflo) { + ktot += start * kd; + a = (int)floor(ktot); + if (a) { + ktot -= a; + k1 += a * key->elemsize; + } + } + else { + k1 += start * key->elemsize; + } + } + + /* just do it here, not above! */ + elemsize = key->elemsize; + if (mode == KEY_MODE_BEZTRIPLE) elemsize *= 3; + + for (a = start; a < end; a++) { + if (weights) { + if (*weights != 0.0f) + madd_v3_v3fl((float *)poin, (float *)k1, *weights); + weights++; + } + else { + add_v3_v3((float *)poin, (float *)k1); + } + + poin += ofs; + + /* are we going to be nasty? */ + if (flagflo) { + ktot += kd; + while (ktot >= 1.0f) { + ktot -= 1.0f; + k1 += elemsize; + } + } + else { + k1 += elemsize; + } + + if (mode == KEY_MODE_BEZTRIPLE) { + a += 2; + } + } + + if (freek1) MEM_freeN(freek1); +} + static void cp_cu_key(Curve *cu, Key *key, KeyBlock *actkb, KeyBlock *kb, const int start, int end, char *out, const int tot) { Nurb *nu; @@ -730,7 +876,8 @@ void BKE_key_evaluate_relative(const int start, int end, const int tot, char *ba { KeyBlock *kb; int *ofsp, ofs[3], elemsize, b; - char *cp, *poin, *reffrom, *from, elemstr[8]; + char *poin, *reffrom, *from; + char elemstr[8]; int poinsize, keyblock_index; /* currently always 0, in future key_pointer_size may assign */ @@ -761,23 +908,25 @@ void BKE_key_evaluate_relative(const int start, int end, const int tot, char *ba /* only with value, and no difference allowed */ if (!(kb->flag & KEYBLOCK_MUTE) && icuval != 0.0f && kb->totelem == tot) { - KeyBlock *refb; float weight, *weights = per_keyblock_weights ? per_keyblock_weights[keyblock_index] : NULL; char *freefrom = NULL, *freereffrom = NULL; - /* reference now can be any block */ - refb = BLI_findlink(&key->block, kb->relative); - if (refb == NULL) continue; - poin = basispoin; from = key_block_get_data(key, actkb, kb, &freefrom); - reffrom = key_block_get_data(key, actkb, refb, &freereffrom); + { + /* reference now can be any block */ + KeyBlock *refb = BLI_findlink(&key->block, kb->relative); + if (refb == NULL) continue; + + reffrom = key_block_get_data(key, actkb, refb, &freereffrom); + } poin += start * poinsize; reffrom += key->elemsize * start; // key elemsize yes! from += key->elemsize * start; for (b = start; b < end; b++) { + char *cp; weight = weights ? (*weights * icuval) : icuval; @@ -826,6 +975,57 @@ void BKE_key_evaluate_relative(const int start, int end, const int tot, char *ba } } +void BKE_key_evaluate_strands_relative(const int start, int end, const int tot, char *basispoin, Key *key, KeyBlock *actkb, + float **per_keyblock_weights, const int mode) +{ + KeyBlock *kb; + int ofs, elemsize, b; + char *poin, *from; + int poinsize, keyblock_index; + + if (!key_pointer_size(key, mode, &poinsize, &ofs)) + return; + + if (end > tot) end = tot; + + /* just here, not above! */ + elemsize = key->elemsize; + if (mode == KEY_MODE_BEZTRIPLE) elemsize *= 3; + + for (kb = key->block.first, keyblock_index = 0; kb; kb = kb->next, keyblock_index++) { + if (kb != key->refkey) { + float icuval = kb->curval; + + /* only with value, and no difference allowed */ + if (!(kb->flag & KEYBLOCK_MUTE) && icuval != 0.0f && kb->totelem == tot) { + float weight, *weights = per_keyblock_weights ? per_keyblock_weights[keyblock_index] : NULL; + char *freefrom = NULL; + + poin = basispoin; + from = key_block_get_data(key, actkb, kb, &freefrom); + + poin += start * poinsize; + from += key->elemsize * start; + + for (b = start; b < end; b++) { + float delta[3]; + + weight = weights ? (*weights * icuval) : icuval; + + sub_v3_v3v3(delta, (float *)from, (float *)poin); + madd_v3_v3fl((float *)poin, delta, weight); + + poin += ofs; + from += elemsize; + if (mode == KEY_MODE_BEZTRIPLE) b += 2; + if (weights) weights++; + } + + if (freefrom) MEM_freeN(freefrom); + } + } + } +} static void do_key(const int start, int end, const int tot, char *poin, Key *key, KeyBlock *actkb, KeyBlock **k, float *t, const int mode) { @@ -1052,7 +1252,199 @@ static void do_key(const int start, int end, const int tot, char *poin, Key *key if (freek4) MEM_freeN(freek4); } -static float *get_weights_array(Object *ob, char *vgroup, WeightsArrayCache *cache) +static void do_key_strands(const int start, int end, const int tot, char *poin, Key *key, KeyBlock *actkb, KeyBlock **k, float *t, const int mode) +{ + float k1tot = 0.0, k2tot = 0.0, k3tot = 0.0, k4tot = 0.0; + float k1d = 0.0, k2d = 0.0, k3d = 0.0, k4d = 0.0; + int a, ofs; + int flagdo = 15, flagflo = 0, elemsize, poinsize = 0; + char *k1, *k2, *k3, *k4, *freek1, *freek2, *freek3, *freek4; + + /* currently always 0, in future key_pointer_size may assign */ + if (!key_pointer_size(key, mode, &poinsize, &ofs)) + return; + + if (end > tot) end = tot; + + k1 = key_block_get_data(key, actkb, k[0], &freek1); + k2 = key_block_get_data(key, actkb, k[1], &freek2); + k3 = key_block_get_data(key, actkb, k[2], &freek3); + k4 = key_block_get_data(key, actkb, k[3], &freek4); + + /* test for more or less points (per key!) */ + if (tot != k[0]->totelem) { + k1tot = 0.0; + flagflo |= 1; + if (k[0]->totelem) { + k1d = k[0]->totelem / (float)tot; + } + else { + flagdo -= 1; + } + } + if (tot != k[1]->totelem) { + k2tot = 0.0; + flagflo |= 2; + if (k[0]->totelem) { + k2d = k[1]->totelem / (float)tot; + } + else { + flagdo -= 2; + } + } + if (tot != k[2]->totelem) { + k3tot = 0.0; + flagflo |= 4; + if (k[0]->totelem) { + k3d = k[2]->totelem / (float)tot; + } + else { + flagdo -= 4; + } + } + if (tot != k[3]->totelem) { + k4tot = 0.0; + flagflo |= 8; + if (k[0]->totelem) { + k4d = k[3]->totelem / (float)tot; + } + else { + flagdo -= 8; + } + } + + /* this exception is needed for curves with multiple splines */ + if (start != 0) { + + poin += poinsize * start; + + if (flagdo & 1) { + if (flagflo & 1) { + k1tot += start * k1d; + a = (int)floor(k1tot); + if (a) { + k1tot -= a; + k1 += a * key->elemsize; + } + } + else { + k1 += start * key->elemsize; + } + } + if (flagdo & 2) { + if (flagflo & 2) { + k2tot += start * k2d; + a = (int)floor(k2tot); + if (a) { + k2tot -= a; + k2 += a * key->elemsize; + } + } + else { + k2 += start * key->elemsize; + } + } + if (flagdo & 4) { + if (flagflo & 4) { + k3tot += start * k3d; + a = (int)floor(k3tot); + if (a) { + k3tot -= a; + k3 += a * key->elemsize; + } + } + else { + k3 += start * key->elemsize; + } + } + if (flagdo & 8) { + if (flagflo & 8) { + k4tot += start * k4d; + a = (int)floor(k4tot); + if (a) { + k4tot -= a; + k4 += a * key->elemsize; + } + } + else { + k4 += start * key->elemsize; + } + } + + } + + /* only here, not above! */ + elemsize = key->elemsize; + if (mode == KEY_MODE_BEZTRIPLE) elemsize *= 3; + + for (a = start; a < end; a++) { + + zero_v3((float *)poin); + madd_v3_v3fl((float *)poin, (float *)k1, t[0]); + madd_v3_v3fl((float *)poin, (float *)k2, t[1]); + madd_v3_v3fl((float *)poin, (float *)k3, t[2]); + madd_v3_v3fl((float *)poin, (float *)k4, t[3]); + + poin += ofs; + + /* lets do it the difficult way: when keys have a different size */ + if (flagdo & 1) { + if (flagflo & 1) { + k1tot += k1d; + while (k1tot >= 1.0f) { + k1tot -= 1.0f; + k1 += elemsize; + } + } + else k1 += elemsize; + } + if (flagdo & 2) { + if (flagflo & 2) { + k2tot += k2d; + while (k2tot >= 1.0f) { + k2tot -= 1.0f; + k2 += elemsize; + } + } + else { + k2 += elemsize; + } + } + if (flagdo & 4) { + if (flagflo & 4) { + k3tot += k3d; + while (k3tot >= 1.0f) { + k3tot -= 1.0f; + k3 += elemsize; + } + } + else { + k3 += elemsize; + } + } + if (flagdo & 8) { + if (flagflo & 8) { + k4tot += k4d; + while (k4tot >= 1.0f) { + k4tot -= 1.0f; + k4 += elemsize; + } + } + else { + k4 += elemsize; + } + } + + if (mode == KEY_MODE_BEZTRIPLE) a += 2; + } + + if (freek1) MEM_freeN(freek1); + if (freek2) MEM_freeN(freek2); + if (freek3) MEM_freeN(freek3); + if (freek4) MEM_freeN(freek4); +} + +static float *get_object_weights_array(Object *ob, char *vgroup, WeightsArrayCache *cache) { MDeformVert *dvert = NULL; BMEditMesh *em = NULL; @@ -1124,7 +1516,109 @@ static float *get_weights_array(Object *ob, char *vgroup, WeightsArrayCache *cac return NULL; } -float **BKE_keyblock_get_per_block_weights(Object *ob, Key *key, WeightsArrayCache *cache) +static float *get_weights_array_strands(Strands *strands, const char *UNUSED(vgroup), bool is_refkey, WeightsArrayCache *UNUSED(cache)) +{ + int totvert = strands->totverts; + + if (is_refkey) { + /* for the refkey, return zero weights, so the refkey actually uses the unmodified data */ + float *weights = MEM_callocN(totvert * sizeof(float), "weights"); + return weights; + } + else { + /* TODO no vgroup support for strands yet */ + return NULL; + } +} + +float **BKE_keyblock_get_per_block_object_weights(Object *ob, Key *key, WeightsArrayCache *cache) +{ + KeyBlock *keyblock; + float **per_keyblock_weights; + int keyblock_index; + + per_keyblock_weights = + MEM_mallocN(sizeof(*per_keyblock_weights) * key->totkey, + "per keyblock weights"); + + for (keyblock = key->block.first, keyblock_index = 0; + keyblock; + keyblock = keyblock->next, keyblock_index++) + { + per_keyblock_weights[keyblock_index] = get_object_weights_array(ob, keyblock->vgroup, cache); + } + + return per_keyblock_weights; +} + +static int particle_count_keys(ParticleSystem *psys) +{ + ParticleData *pa; + int p; + int totkey = 0; + for (p = 0, pa = psys->particles; p < psys->totpart; ++p, ++pa) { + totkey += pa->totkey; + } + return totkey; +} + +static float *get_particle_weights_array(Object *ob, ParticleSystem *psys, const char *name, float cfra) +{ + ParticleSettings *part = psys->part; + ParticleSimulationData sim; + MTex **mtexp; + int m, i, p, k; + int totvert = 0; + float *weights = NULL; + + sim.scene = NULL; + sim.ob = ob; + sim.psys = psys; + sim.psmd = psys_get_modifier(ob, psys); + + for (m = 0, mtexp = part->mtex; m < MAX_MTEX; ++m, ++mtexp) { + MTex *mtex = *mtexp; + if (mtex && (mtex->mapto & PAMAP_SHAPEKEY) && STREQ(mtex->shapekey, name)) { + const float def = mtex->def_var; + const short blend = mtex->blendtype; + + ParticleData *pa; + float value, rgba[4]; + + /* lazy init */ + if (!weights) { + totvert = particle_count_keys(psys); + weights = MEM_mallocN(totvert * sizeof(float), "weights"); + for (i = 0; i < totvert; ++i) + weights[i] = 1.0f; + } + + i = 0; + for (p = 0, pa = psys->particles; p < psys->totpart; ++p, ++pa) { + if (particle_mtex_eval(&sim, mtex, pa, cfra, &value, rgba)) { + /* weight is the same for all hair keys, + * only need to evaluate the texture once! + */ + float result = texture_value_blend(def, weights[i], value, mtex->shapefac, blend); + for (k = 0; k < pa->totkey; ++k) { + weights[i + k] = result; + } + } + + i += pa->totkey; + } + } + } + + if (weights) { + for (i = 0; i < totvert; ++i) + CLAMP(weights[i], 0.0f, 1.0f); + } + + return weights; +} + +float **BKE_keyblock_get_per_block_particle_weights(Object *ob, ParticleSystem *psys, float cfra, Key *key, WeightsArrayCache *UNUSED(cache)) { KeyBlock *keyblock; float **per_keyblock_weights; @@ -1138,7 +1632,27 @@ float **BKE_keyblock_get_per_block_weights(Object *ob, Key *key, WeightsArrayCac keyblock; keyblock = keyblock->next, keyblock_index++) { - per_keyblock_weights[keyblock_index] = get_weights_array(ob, keyblock->vgroup, cache); + per_keyblock_weights[keyblock_index] = get_particle_weights_array(ob, psys, keyblock->name, cfra); + } + + return per_keyblock_weights; +} + +float **BKE_keyblock_strands_get_per_block_weights(Strands *strands, Key *key, WeightsArrayCache *cache) +{ + KeyBlock *keyblock; + float **per_keyblock_weights; + int keyblock_index; + + per_keyblock_weights = + MEM_mallocN(sizeof(*per_keyblock_weights) * key->totkey, + "per keyblock weights"); + + for (keyblock = key->block.first, keyblock_index = 0; + keyblock; + keyblock = keyblock->next, keyblock_index++) + { + per_keyblock_weights[keyblock_index] = get_weights_array_strands(strands, keyblock->vgroup, keyblock == key->refkey, cache); } return per_keyblock_weights; @@ -1179,7 +1693,7 @@ static void do_mesh_key(Object *ob, Key *key, char *out, const int tot) if (key->type == KEY_RELATIVE) { WeightsArrayCache cache = {0, NULL}; float **per_keyblock_weights; - per_keyblock_weights = BKE_keyblock_get_per_block_weights(ob, key, &cache); + per_keyblock_weights = BKE_keyblock_get_per_block_object_weights(ob, key, &cache); BKE_key_evaluate_relative(0, tot, tot, (char *)out, key, actkb, per_keyblock_weights, KEY_MODE_DUMMY); BKE_keyblock_free_per_block_weights(key, per_keyblock_weights, &cache); } @@ -1270,7 +1784,7 @@ static void do_latt_key(Object *ob, Key *key, char *out, const int tot) if (key->type == KEY_RELATIVE) { float **per_keyblock_weights; - per_keyblock_weights = BKE_keyblock_get_per_block_weights(ob, key, NULL); + per_keyblock_weights = BKE_keyblock_get_per_block_object_weights(ob, key, NULL); BKE_key_evaluate_relative(0, tot, tot, (char *)out, key, actkb, per_keyblock_weights, KEY_MODE_DUMMY); BKE_keyblock_free_per_block_weights(key, per_keyblock_weights, NULL); } @@ -1290,6 +1804,32 @@ static void do_latt_key(Object *ob, Key *key, char *out, const int tot) if (lt->flag & LT_OUTSIDE) outside_lattice(lt); } +static void do_psys_key(Object *ob, ParticleSystem *psys, float cfra, Key *key, char *out, const int tot) +{ + KeyBlock *k[4], *actkb = BKE_keyblock_from_particles(psys); + float t[4]; + int flag = 0; + + if (key->type == KEY_RELATIVE) { + WeightsArrayCache cache = {0, NULL}; + float **per_keyblock_weights; + + per_keyblock_weights = BKE_keyblock_get_per_block_particle_weights(ob, psys, cfra, key, &cache); + BKE_key_evaluate_relative(0, tot, tot, (char *)out, key, actkb, per_keyblock_weights, KEY_MODE_DUMMY); + BKE_keyblock_free_per_block_weights(key, per_keyblock_weights, &cache); + } + else { + const float ctime_scaled = key->ctime / 100.0f; + + flag = setkeys(ctime_scaled, &key->block, k, t, 0); + + if (flag == 0) + do_key(0, tot, tot, (char *)out, key, actkb, k, t, KEY_MODE_DUMMY); + else + cp_key(0, tot, tot, (char *)out, key, actkb, k[2], NULL, KEY_MODE_DUMMY); + } +} + /* returns key coordinates (+ tilt) when key applied, NULL otherwise */ float *BKE_key_evaluate_object_ex( Object *ob, int *r_totelem, @@ -1349,7 +1889,7 @@ float *BKE_key_evaluate_object_ex( } /* prevent python from screwing this up? anyhoo, the from pointer could be dropped */ - key->from = (ID *)ob->data; + BKE_key_set_from_id(key, (ID *)ob->data); if (ob->shapeflag & OB_SHAPE_LOCK) { /* shape locked, copy the locked shape instead of blending */ @@ -1364,7 +1904,7 @@ float *BKE_key_evaluate_object_ex( } if (OB_TYPE_SUPPORT_VGROUP(ob->type)) { - float *weights = get_weights_array(ob, kb->vgroup, NULL); + float *weights = get_object_weights_array(ob, kb->vgroup, NULL); cp_key(0, tot, tot, out, key, actkb, kb, weights, 0); @@ -1392,6 +1932,196 @@ float *BKE_key_evaluate_object(Object *ob, int *r_totelem) return BKE_key_evaluate_object_ex(ob, r_totelem, NULL, 0); } +static void do_strands_key(Strands *strands, Key *key, KeyBlock *actkb, char *out, const int tot) +{ + KeyBlock *k[4]; + float t[4]; + int flag = 0; + + if (key->type == KEY_RELATIVE) { + WeightsArrayCache cache = {0, NULL}; + float **per_keyblock_weights ; + per_keyblock_weights = BKE_keyblock_strands_get_per_block_weights(strands, key, &cache); + BKE_key_evaluate_strands_relative(0, tot, tot, (char *)out, key, actkb, per_keyblock_weights, KEY_MODE_DUMMY); + BKE_keyblock_free_per_block_weights(key, per_keyblock_weights, &cache); + } + else { + const float ctime_scaled = key->ctime / 100.0f; + + flag = setkeys(ctime_scaled, &key->block, k, t, 0); + + if (flag == 0) { + do_key_strands(0, tot, tot, (char *)out, key, actkb, k, t, KEY_MODE_DUMMY); + } + else { + cp_key_strands(0, tot, tot, (char *)out, key, actkb, k[2], NULL, KEY_MODE_DUMMY); + } + } +} + +float *BKE_key_evaluate_strands_ex(Strands *strands, Key *key, KeyBlock *actkb, bool lock_shape, int *r_totelem, float *arr, size_t arr_size) +{ + char *out; + int tot = 0, size = 0; + + if (key == NULL || BLI_listbase_is_empty(&key->block)) + return NULL; + + /* compute size of output array */ + tot = strands->totverts; + size = tot * 3 * sizeof(float); + /* if nothing to interpolate, cancel */ + if (tot == 0 || size == 0) + return NULL; + + /* allocate array */ + if (arr == NULL) { + out = MEM_callocN(size, "BKE_key_evaluate_strands out"); + } + else { + if (arr_size != size) { + return NULL; + } + + out = (char *)arr; + } + + if (lock_shape && actkb) { + /* shape locked, copy the locked shape instead of blending */ + float *weights; + + if (actkb->flag & KEYBLOCK_MUTE) + actkb = key->refkey; + + /* XXX weights not supported for strands yet */ + weights = get_weights_array_strands(strands, actkb->vgroup, actkb == key->refkey, NULL); + + cp_key_strands(0, tot, tot, out, key, actkb, actkb, weights, 0); + + if (weights) + MEM_freeN(weights); + } + else { + do_strands_key(strands, key, actkb, out, tot); + } + + if (r_totelem) { + *r_totelem = tot; + } + return (float *)out; +} + +float *BKE_key_evaluate_strands(Strands *strands, Key *key, KeyBlock *actkb, bool lock_shape, int *r_totelem, bool use_motion) +{ + size_t size = sizeof(float) * 3 * strands->totverts; + float *data = MEM_mallocN(size, "strands shape key data"); + float *result; + float *fp; + int i; + + if (use_motion && strands->state) { + for (i = 0, fp = data; i < strands->totverts; ++i, fp += 3) + copy_v3_v3(fp, strands->state[i].co); + } + else { + for (i = 0, fp = data; i < strands->totverts; ++i, fp += 3) + copy_v3_v3(fp, strands->verts[i].co); + } + + result = BKE_key_evaluate_strands_ex(strands, key, actkb, lock_shape, r_totelem, data, size); + if (result != data) + MEM_freeN(data); + + return result; +} + +/* returns key coordinates when key applied, NULL otherwise */ +float *BKE_key_evaluate_particles_ex(Object *ob, ParticleSystem *psys, float cfra, int *r_totelem, + float *arr, size_t arr_size) +{ + const bool use_editmode = (ob->mode & OB_MODE_PARTICLE_EDIT) && psys == psys_get_current(ob) && (psys->edit || psys->pointcache->edit) && !psys->renderdata; + Key *key = psys->key; + KeyBlock *actkb = BKE_keyblock_from_particles(psys); + char *out; + int tot = 0, size = 0; + int i; + + if (key == NULL || BLI_listbase_is_empty(&key->block)) + return NULL; + + /* compute size of output array */ + tot = 0; + for (i = 0; i < psys->totpart; ++i) + tot += psys->particles[i].totkey; + size = tot * 3 * sizeof(float); + + /* if nothing to interpolate, cancel */ + if (tot == 0 || size == 0) + return NULL; + + /* allocate array */ + if (arr == NULL) { + out = MEM_callocN(size, "BKE_key_evaluate_object out"); + } + else { + if (arr_size != size) { + return NULL; + } + + out = (char *)arr; + } + + /* prevent python from screwing this up? anyhoo, the from pointer could be dropped */ + BKE_key_set_from_particles(key, ob, psys); + + if (use_editmode) { + /* in edit mode, only evaluate the active shape */ + KeyBlock *kb = BLI_findlink(&key->block, psys->shapenr - 1); + + if (kb && (kb->flag & KEYBLOCK_MUTE)) + kb = key->refkey; + + if (kb == NULL) { + kb = key->block.first; + psys->shapenr = 1; + } + + cp_key(0, tot, tot, out, key, actkb, kb, NULL, 0); + } + else if (ob->shapeflag & OB_SHAPE_LOCK) { + /* shape locked, copy the locked shape instead of blending */ + KeyBlock *kb = BLI_findlink(&key->block, psys->shapenr - 1); + float *weights; + + if (kb && (kb->flag & KEYBLOCK_MUTE)) + kb = key->refkey; + + if (kb == NULL) { + kb = key->block.first; + psys->shapenr = 1; + } + + weights = get_particle_weights_array(ob, psys, kb->name, cfra); + + cp_key(0, tot, tot, out, key, actkb, kb, weights, 0); + + if (weights) MEM_freeN(weights); + } + else { + do_psys_key(ob, psys, cfra, key, out, tot); + } + + if (r_totelem) { + *r_totelem = tot; + } + return (float *)out; +} + +float *BKE_key_evaluate_particles(Object *ob, ParticleSystem *psys, float cfra, int *r_totelem) +{ + return BKE_key_evaluate_particles_ex(ob, psys, cfra, r_totelem, NULL, 0); +} + Key **BKE_key_from_object_p(Object *ob) { if (ob == NULL) @@ -1522,6 +2252,29 @@ KeyBlock *BKE_keyblock_from_object_reference(Object *ob) return NULL; } +/* only the active keyblock */ +KeyBlock *BKE_keyblock_from_particles(ParticleSystem *psys) +{ + Key *key = psys->key; + + if (key) { + KeyBlock *kb = BLI_findlink(&key->block, psys->shapenr - 1); + return kb; + } + + return NULL; +} + +KeyBlock *BKE_keyblock_from_particles_reference(ParticleSystem *psys) +{ + Key *key = psys->key; + + if (key) + return key->refkey; + + return NULL; +} + /* get the appropriate KeyBlock given an index */ KeyBlock *BKE_keyblock_from_key(Key *key, int index) { @@ -1774,6 +2527,72 @@ void BKE_keyblock_convert_to_mesh(KeyBlock *kb, Mesh *me) } } +/************************* Strands ************************/ +void BKE_keyblock_update_from_strands(Strands *strands, KeyBlock *kb, bool use_motion) +{ + float (*fp)[3]; + int a, tot; + + BLI_assert(strands->totverts == kb->totelem); + + tot = strands->totverts; + if (tot == 0) return; + + fp = kb->data; + /* use vertex locations as fallback, so we always get a valid shape */ + if (use_motion && strands->state) { + StrandsMotionState *state = strands->state; + for (a = 0; a < tot; a++, fp++, state++) { + copy_v3_v3(*fp, state->co); + } + } + else { + StrandsVertex *vert = strands->verts; + for (a = 0; a < tot; a++, fp++, vert++) { + copy_v3_v3(*fp, vert->co); + } + } +} + +void BKE_keyblock_convert_from_strands(Strands *strands, Key *key, KeyBlock *kb, bool use_motion) +{ + int tot = strands->totverts; + + if (strands->totverts == 0) return; + + MEM_SAFE_FREE(kb->data); + + kb->data = MEM_mallocN(key->elemsize * tot, __func__); + kb->totelem = tot; + + BKE_keyblock_update_from_strands(strands, kb, use_motion); +} + +void BKE_keyblock_convert_to_strands(KeyBlock *kb, Strands *strands, bool use_motion) +{ + const float (*fp)[3]; + int a, tot; + + fp = kb->data; + + tot = min_ii(kb->totelem, strands->totverts); + + if (use_motion) { + if (strands->state) { + StrandsMotionState *state = strands->state; + for (a = 0; a < tot; a++, fp++, state++) { + copy_v3_v3(state->co, *fp); + } + } + } + else { + StrandsVertex *vert = strands->verts; + for (a = 0; a < tot; a++, fp++, vert++) { + copy_v3_v3(vert->co, *fp); + } + } +} + /************************* raw coords ************************/ void BKE_keyblock_update_from_vertcos(Object *ob, KeyBlock *kb, float (*vertCos)[3]) { @@ -1960,6 +2779,48 @@ void BKE_keyblock_update_from_offset(Object *ob, KeyBlock *kb, float (*ofs)[3]) } } +/************************* Mesh ************************/ + +void BKE_keyblock_convert_to_hair_keys(struct KeyBlock *kb, struct Object *UNUSED(ob), struct ParticleSystem *psys) +{ + ParticleData *pa; + HairKey *hkey; + float *fp; + int i, k; + + fp = kb->data; + for (i = 0, pa = psys->particles; i < psys->totpart; ++i, ++pa) { + for (k = 0, hkey = pa->hair; k < pa->totkey; ++k, ++hkey) { + copy_v3_v3(hkey->co, fp); + fp += 3; + } + } +} + +void BKE_keyblock_convert_from_hair_keys(struct Object *UNUSED(ob), struct ParticleSystem *psys, struct KeyBlock *kb) +{ + ParticleData *pa; + HairKey *hkey; + float *fp; + int i, k; + + if (kb->data) MEM_freeN(kb->data); + + kb->totelem = 0; + for (i = 0, pa = psys->particles; i < psys->totpart; ++i, ++pa) { + kb->totelem += pa->totkey; + } + kb->data = MEM_mallocN(psys->key->elemsize * kb->totelem, "kb->data"); + + fp = kb->data; + for (i = 0, pa = psys->particles; i < psys->totpart; ++i, ++pa) { + for (k = 0, hkey = pa->hair; k < pa->totkey; ++k, ++hkey) { + copy_v3_v3(fp, hkey->co); + fp += 3; + } + } +} + /* ==========================================================*/ /** Move shape key from org_index to new_index. Safe, clamps index to valid range, updates reference keys, @@ -1969,11 +2830,10 @@ void BKE_keyblock_update_from_offset(Object *ob, KeyBlock *kb, float (*ofs)[3]) * \param org_index if < 0, current object's active shape will be used as skey to move. * \return true if something was done, else false. */ -bool BKE_keyblock_move(Object *ob, int org_index, int new_index) +bool BKE_keyblock_move_ex(Key *key, int *shapenr, int org_index, int new_index) { - Key *key = BKE_key_from_object(ob); KeyBlock *kb; - const int act_index = ob->shapenr - 1; + const int act_index = *shapenr - 1; const int totkey = key->totkey; int i; bool rev, in_range = false; @@ -2033,13 +2893,13 @@ bool BKE_keyblock_move(Object *ob, int org_index, int new_index) /* Need to update active shape number if it's affected, same principle as for relative indices above. */ if (org_index == act_index) { - ob->shapenr = new_index + 1; + *shapenr = new_index + 1; } else if (act_index < org_index && act_index >= new_index) { - ob->shapenr++; + (*shapenr)++; } else if (act_index > org_index && act_index <= new_index) { - ob->shapenr--; + (*shapenr)--; } /* First key is always refkey, matches interface and BKE_key_sort */ @@ -2048,6 +2908,14 @@ bool BKE_keyblock_move(Object *ob, int org_index, int new_index) return true; } +bool BKE_keyblock_move(Object *ob, int org_index, int new_index) +{ + int shapenr; + bool result = BKE_keyblock_move_ex(BKE_key_from_object(ob), &shapenr, org_index, new_index); + ob->shapenr = shapenr; + return result; +} + /** * Check if given keyblock (as index) is used as basis by others in given key. */ diff --git a/source/blender/blenkernel/intern/lattice.c b/source/blender/blenkernel/intern/lattice.c index 86927609ff6..582c164a776 100644 --- a/source/blender/blenkernel/intern/lattice.c +++ b/source/blender/blenkernel/intern/lattice.c @@ -275,7 +275,7 @@ Lattice *BKE_lattice_copy(Lattice *lt) ltn->def = MEM_dupallocN(lt->def); ltn->key = BKE_key_copy(ltn->key); - if (ltn->key) ltn->key->from = (ID *)ltn; + BKE_key_set_from_id(ltn->key, (ID *)ltn); if (lt->dvert) { int tot = lt->pntsu * lt->pntsv * lt->pntsw; diff --git a/source/blender/blenkernel/intern/library.c b/source/blender/blenkernel/intern/library.c index bf6f7a5f22c..c850216f5ef 100644 --- a/source/blender/blenkernel/intern/library.c +++ b/source/blender/blenkernel/intern/library.c @@ -45,6 +45,7 @@ #include "DNA_anim_types.h" #include "DNA_armature_types.h" #include "DNA_brush_types.h" +#include "DNA_cache_library_types.h" #include "DNA_camera_types.h" #include "DNA_group_types.h" #include "DNA_gpencil_types.h" @@ -79,6 +80,7 @@ #include "BKE_armature.h" #include "BKE_bpath.h" #include "BKE_brush.h" +#include "BKE_cache_library.h" #include "BKE_camera.h" #include "BKE_context.h" #include "BKE_curve.h" @@ -107,6 +109,7 @@ #include "BKE_paint.h" #include "BKE_particle.h" #include "BKE_packedFile.h" +#include "BKE_pointcache.h" #include "BKE_speaker.h" #include "BKE_sound.h" #include "BKE_screen.h" @@ -411,6 +414,10 @@ bool id_unlink(ID *id, int test) if (test) return true; BKE_object_unlink((Object *)id); break; + case ID_CL: + if (test) return true; + BKE_cache_library_unlink((CacheLibrary *)id); + break; } if (id->us == 0) { @@ -523,6 +530,8 @@ ListBase *which_libbase(Main *mainlib, short type) return &(mainlib->palettes); case ID_PC: return &(mainlib->paintcurves); + case ID_CL: + return &(mainlib->cache_library); } return NULL; } @@ -618,6 +627,7 @@ int set_listbasepointers(Main *main, ListBase **lb) lb[a++] = &(main->linestyle); /* referenced by scenes */ lb[a++] = &(main->scene); lb[a++] = &(main->library); + lb[a++] = &(main->cache_library); lb[a++] = &(main->wm); lb[a++] = &(main->mask); @@ -749,6 +759,9 @@ static ID *alloc_libblock_notest(short type) case ID_PC: id = MEM_callocN(sizeof(PaintCurve), "Paint Curve"); break; + case ID_CL: + id = MEM_callocN(sizeof(CacheLibrary), "Cache Library"); + break; } return id; } @@ -1037,6 +1050,9 @@ void BKE_libblock_free_ex(Main *bmain, void *idv, bool do_id_user) case ID_PC: BKE_paint_curve_free((PaintCurve *)id); break; + case ID_CL: + BKE_cache_library_free((CacheLibrary *)id); + break; } /* avoid notifying on removed data */ diff --git a/source/blender/blenkernel/intern/mesh.c b/source/blender/blenkernel/intern/mesh.c index 37a98eae58b..c8223657a05 100644 --- a/source/blender/blenkernel/intern/mesh.c +++ b/source/blender/blenkernel/intern/mesh.c @@ -571,7 +571,7 @@ Mesh *BKE_mesh_copy_ex(Main *bmain, Mesh *me) men->bb = MEM_dupallocN(men->bb); men->key = BKE_key_copy(me->key); - if (men->key) men->key->from = (ID *)men; + BKE_key_set_from_id(men->key, (ID *)men); if (me->id.lib) { BKE_id_lib_local_paths(bmain, me->id.lib, &men->id); @@ -2524,6 +2524,66 @@ Mesh *BKE_mesh_new_from_object( return tmpmesh; } +/* settings: 1 - preview, 2 - render */ +Mesh *BKE_mesh_new_from_dupli_data( + Main *bmain, DupliObjectData *data, + bool calc_tessface, bool calc_undeformed) +{ + Object *ob = data->ob; + DerivedMesh *dm = data->dm; + CustomDataMask mask; + + Mesh *tmpmesh; + + if (!ob || !dm) + return NULL; + + mask = CD_MASK_MESH; /* this seems more suitable, exporter, + * for example, needs CD_MASK_MDEFORMVERT */ + if (calc_undeformed) + mask |= CD_MASK_ORCO; + + tmpmesh = BKE_mesh_add(bmain, "Mesh"); + DM_to_mesh(dm, tmpmesh, ob, mask, true); + + /* BKE_mesh_add/copy gives us a user count we don't need */ + tmpmesh->id.us--; + + /* Copy materials to new mesh */ + switch (ob->type) { + case OB_MESH: { + Mesh *origmesh = ob->data; + int i; + + tmpmesh->flag = origmesh->flag; + tmpmesh->mat = MEM_dupallocN(origmesh->mat); + tmpmesh->totcol = origmesh->totcol; + tmpmesh->smoothresh = origmesh->smoothresh; + if (origmesh->mat) { + for (i = origmesh->totcol; i-- > 0; ) { + /* are we an object material or data based? */ + tmpmesh->mat[i] = ob->matbits[i] ? ob->mat[i] : origmesh->mat[i]; + + if (tmpmesh->mat[i]) { + tmpmesh->mat[i]->id.us++; + } + } + } + break; + } + } /* end copy materials */ + + if (calc_tessface) { + /* cycles and exporters rely on this still */ + BKE_mesh_tessface_ensure(tmpmesh); + } + + /* make sure materials get updated in objects */ + test_object_materials(bmain, &tmpmesh->id); + + return tmpmesh; +} + /* **** Depsgraph evaluation **** */ void BKE_mesh_eval_geometry(EvaluationContext *UNUSED(eval_ctx), diff --git a/source/blender/blenkernel/intern/mesh_sample.c b/source/blender/blenkernel/intern/mesh_sample.c new file mode 100644 index 00000000000..b1a89ec64fc --- /dev/null +++ b/source/blender/blenkernel/intern/mesh_sample.c @@ -0,0 +1,381 @@ +/* + * ***** 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/blenkernel/intern/mesh_sample.c + * \ingroup bke + * + * Sample a mesh surface or volume and evaluate samples on deformed meshes. + */ + +#include "MEM_guardedalloc.h" + +#include "DNA_key_types.h" +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" + +#include "BLI_utildefines.h" +#include "BLI_math.h" +#include "BLI_rand.h" + +#include "BKE_bvhutils.h" +#include "BKE_mesh_sample.h" +#include "BKE_customdata.h" +#include "BKE_DerivedMesh.h" + +#include "BLI_strict_flags.h" + +/* ==== Evaluate ==== */ + +bool BKE_mesh_sample_eval(DerivedMesh *dm, const MSurfaceSample *sample, float loc[3], float nor[3], float tang[3]) +{ + MVert *mverts = dm->getVertArray(dm); + unsigned int totverts = (unsigned int)dm->getNumVerts(dm); + MVert *v1, *v2, *v3; + + zero_v3(loc); + zero_v3(nor); + zero_v3(tang); + + if (sample->orig_verts[0] >= totverts || + sample->orig_verts[1] >= totverts || + sample->orig_verts[2] >= totverts) + return false; + + v1 = &mverts[sample->orig_verts[0]]; + v2 = &mverts[sample->orig_verts[1]]; + v3 = &mverts[sample->orig_verts[2]]; + + { /* location */ + madd_v3_v3fl(loc, v1->co, sample->orig_weights[0]); + madd_v3_v3fl(loc, v2->co, sample->orig_weights[1]); + madd_v3_v3fl(loc, v3->co, sample->orig_weights[2]); + } + + { /* normal */ + float vnor[3]; + + normal_short_to_float_v3(vnor, v1->no); + madd_v3_v3fl(nor, vnor, sample->orig_weights[0]); + normal_short_to_float_v3(vnor, v2->no); + madd_v3_v3fl(nor, vnor, sample->orig_weights[1]); + normal_short_to_float_v3(vnor, v3->no); + madd_v3_v3fl(nor, vnor, sample->orig_weights[2]); + + normalize_v3(nor); + } + + { /* tangent */ + float edge[3]; + + /* XXX simply using the v1-v2 edge as a tangent vector for now ... + * Eventually mikktspace generated tangents (CD_TANGENT tessface layer) + * should be used for consistency, but requires well-defined tessface + * indices for the mesh surface samples. + */ + + sub_v3_v3v3(edge, v2->co, v1->co); + /* make edge orthogonal to nor */ + madd_v3_v3fl(edge, nor, -dot_v3v3(edge, nor)); + normalize_v3_v3(tang, edge); + } + + return true; +} + +bool BKE_mesh_sample_shapekey(Key *key, KeyBlock *kb, const MSurfaceSample *sample, float loc[3]) +{ + float *v1, *v2, *v3; + + (void)key; /* Unused in release builds. */ + + BLI_assert(key->elemsize == 3 * sizeof(float)); + BLI_assert(sample->orig_verts[0] < (unsigned int)kb->totelem); + BLI_assert(sample->orig_verts[1] < (unsigned int)kb->totelem); + BLI_assert(sample->orig_verts[2] < (unsigned int)kb->totelem); + + v1 = (float *)kb->data + sample->orig_verts[0] * 3; + v2 = (float *)kb->data + sample->orig_verts[1] * 3; + v3 = (float *)kb->data + sample->orig_verts[2] * 3; + + zero_v3(loc); + madd_v3_v3fl(loc, v1, sample->orig_weights[0]); + madd_v3_v3fl(loc, v2, sample->orig_weights[1]); + madd_v3_v3fl(loc, v3, sample->orig_weights[2]); + + /* TODO use optional vgroup weights to determine if a shapeky actually affects the sample */ + return true; +} + + +/* ==== Sampling Utilities ==== */ + +BLI_INLINE void mesh_sample_weights_from_loc(MSurfaceSample *sample, DerivedMesh *dm, int face_index, const float loc[3]) +{ + MFace *face = &dm->getTessFaceArray(dm)[face_index]; + unsigned int index[4] = { face->v1, face->v2, face->v3, face->v4 }; + MVert *mverts = dm->getVertArray(dm); + + float *v1 = mverts[face->v1].co; + float *v2 = mverts[face->v2].co; + float *v3 = mverts[face->v3].co; + float *v4 = face->v4 ? mverts[face->v4].co : NULL; + float w[4]; + int tri[3]; + + interp_weights_face_v3_index(tri, w, v1, v2, v3, v4, loc); + + sample->orig_verts[0] = index[tri[0]]; + sample->orig_verts[1] = index[tri[1]]; + sample->orig_verts[2] = index[tri[2]]; + sample->orig_weights[0] = w[tri[0]]; + sample->orig_weights[1] = w[tri[1]]; + sample->orig_weights[2] = w[tri[2]]; +} + +/* ==== Sampling ==== */ + +static bool mesh_sample_store_array_sample(void *vdata, int capacity, int index, const MSurfaceSample *sample) +{ + MSurfaceSample *data = vdata; + if (index >= capacity) + return false; + + data[index] = *sample; + return true; +} + +void BKE_mesh_sample_storage_single(MSurfaceSampleStorage *storage, MSurfaceSample *sample) +{ + /* handled as just a special array case with capacity = 1 */ + storage->store_sample = mesh_sample_store_array_sample; + storage->capacity = 1; + storage->data = sample; + storage->free_data = false; +} + +void BKE_mesh_sample_storage_array(MSurfaceSampleStorage *storage, MSurfaceSample *samples, int capacity) +{ + storage->store_sample = mesh_sample_store_array_sample; + storage->capacity = capacity; + storage->data = samples; + storage->free_data = false; +} + +void BKE_mesh_sample_storage_release(MSurfaceSampleStorage *storage) +{ + if (storage->free_data) + MEM_freeN(storage->data); +} + + +int BKE_mesh_sample_generate_random(MSurfaceSampleStorage *dst, DerivedMesh *dm, unsigned int seed, int totsample) +{ + MFace *mfaces; + int totfaces; + RNG *rng; + MFace *mface; + float a, b; + int i, stored = 0; + + rng = BLI_rng_new(seed); + + DM_ensure_tessface(dm); + mfaces = dm->getTessFaceArray(dm); + totfaces = dm->getNumTessFaces(dm); + + for (i = 0; i < totsample; ++i) { + MSurfaceSample sample = {{0}}; + + mface = &mfaces[BLI_rng_get_int(rng) % totfaces]; + + if (mface->v4 && BLI_rng_get_int(rng) % 2 == 0) { + sample.orig_verts[0] = mface->v3; + sample.orig_verts[1] = mface->v4; + sample.orig_verts[2] = mface->v1; + } + else { + sample.orig_verts[0] = mface->v1; + sample.orig_verts[1] = mface->v2; + sample.orig_verts[2] = mface->v3; + } + + a = BLI_rng_get_float(rng); + b = BLI_rng_get_float(rng); + if (a + b > 1.0f) { + a = 1.0f - a; + b = 1.0f - b; + } + sample.orig_weights[0] = 1.0f - (a + b); + sample.orig_weights[1] = a; + sample.orig_weights[2] = b; + + if (dst->store_sample(dst->data, dst->capacity, i, &sample)) + ++stored; + else + break; + } + + BLI_rng_free(rng); + + return stored; +} + + +static bool sample_bvh_raycast(MSurfaceSample *sample, DerivedMesh *dm, BVHTreeFromMesh *bvhdata, const float ray_start[3], const float ray_end[3]) +{ + BVHTreeRayHit hit; + float ray_normal[3], dist; + + sub_v3_v3v3(ray_normal, ray_end, ray_start); + dist = normalize_v3(ray_normal); + + hit.index = -1; + hit.dist = dist; + + if (BLI_bvhtree_ray_cast(bvhdata->tree, ray_start, ray_normal, 0.0f, + &hit, bvhdata->raycast_callback, bvhdata) >= 0) { + + mesh_sample_weights_from_loc(sample, dm, hit.index, hit.co); + + return true; + } + else + return false; +} + +int BKE_mesh_sample_generate_raycast(MSurfaceSampleStorage *dst, DerivedMesh *dm, MeshSampleRayCallback ray_cb, void *userdata, int totsample) +{ + BVHTreeFromMesh bvhdata; + float ray_start[3], ray_end[3]; + int i, stored = 0; + + DM_ensure_tessface(dm); + + memset(&bvhdata, 0, sizeof(BVHTreeFromMesh)); + bvhtree_from_mesh_faces(&bvhdata, dm, 0.0f, 4, 6); + + if (bvhdata.tree) { + for (i = 0; i < totsample; ++i) { + if (ray_cb(userdata, ray_start, ray_end)) { + MSurfaceSample sample; + if (sample_bvh_raycast(&sample, dm, &bvhdata, ray_start, ray_end)) { + if (dst->store_sample(dst->data, dst->capacity, i, &sample)) + ++stored; + else + break; + } + } + } + } + + free_bvhtree_from_mesh(&bvhdata); + + return stored; +} + +/* ==== Utilities ==== */ + +#include "DNA_particle_types.h" + +#include "BKE_bvhutils.h" +#include "BKE_particle.h" + +bool BKE_mesh_sample_from_particle(MSurfaceSample *sample, ParticleSystem *psys, DerivedMesh *dm, ParticleData *pa) +{ + MVert *mverts; + MFace *mface; + float mapfw[4]; + int mapindex; + float *co1 = NULL, *co2 = NULL, *co3 = NULL, *co4 = NULL; + float vec[3]; + float w[4]; + + if (!psys_get_index_on_dm(psys, dm, pa, &mapindex, mapfw)) + return false; + + mface = dm->getTessFaceData(dm, mapindex, CD_MFACE); + mverts = dm->getVertDataArray(dm, CD_MVERT); + + co1 = mverts[mface->v1].co; + co2 = mverts[mface->v2].co; + co3 = mverts[mface->v3].co; + + if (mface->v4) { + co4 = mverts[mface->v4].co; + + interp_v3_v3v3v3v3(vec, co1, co2, co3, co4, mapfw); + } + else { + interp_v3_v3v3v3(vec, co1, co2, co3, mapfw); + } + + /* test both triangles of the face */ + interp_weights_face_v3(w, co1, co2, co3, NULL, vec); + if (w[0] <= 1.0f && w[1] <= 1.0f && w[2] <= 1.0f) { + sample->orig_verts[0] = mface->v1; + sample->orig_verts[1] = mface->v2; + sample->orig_verts[2] = mface->v3; + + copy_v3_v3(sample->orig_weights, w); + return true; + } + else if (mface->v4) { + interp_weights_face_v3(w, co3, co4, co1, NULL, vec); + sample->orig_verts[0] = mface->v3; + sample->orig_verts[1] = mface->v4; + sample->orig_verts[2] = mface->v1; + + copy_v3_v3(sample->orig_weights, w); + return true; + } + else + return false; +} + +bool BKE_mesh_sample_to_particle(MSurfaceSample *sample, ParticleSystem *UNUSED(psys), DerivedMesh *dm, BVHTreeFromMesh *bvhtree, ParticleData *pa) +{ + BVHTreeNearest nearest; + float vec[3], nor[3], tang[3]; + + BKE_mesh_sample_eval(dm, sample, vec, nor, tang); + + nearest.index = -1; + nearest.dist_sq = FLT_MAX; + BLI_bvhtree_find_nearest(bvhtree->tree, vec, &nearest, bvhtree->nearest_callback, bvhtree); + if (nearest.index >= 0) { + MFace *mface = dm->getTessFaceData(dm, nearest.index, CD_MFACE); + MVert *mverts = dm->getVertDataArray(dm, CD_MVERT); + + float *co1 = mverts[mface->v1].co; + float *co2 = mverts[mface->v2].co; + float *co3 = mverts[mface->v3].co; + float *co4 = mface->v4 ? mverts[mface->v4].co : NULL; + + pa->num = nearest.index; + pa->num_dmcache = DMCACHE_NOTFOUND; + + interp_weights_face_v3(pa->fuv, co1, co2, co3, co4, vec); + pa->foffset = 0.0f; /* XXX any sensible way to reconstruct this? */ + + return true; + } + else + return false; +} diff --git a/source/blender/blenkernel/intern/modifier.c b/source/blender/blenkernel/intern/modifier.c index 1f6d40dafa3..4d007e12ef5 100644 --- a/source/blender/blenkernel/intern/modifier.c +++ b/source/blender/blenkernel/intern/modifier.c @@ -57,6 +57,7 @@ #include "BLF_translation.h" #include "BKE_appdir.h" +#include "BKE_cache_library.h" #include "BKE_key.h" #include "BKE_multires.h" #include "BKE_DerivedMesh.h" @@ -68,7 +69,7 @@ #include "MOD_modifiertypes.h" -static ModifierTypeInfo *modifier_types[NUM_MODIFIER_TYPES] = {NULL}; +static ModifierTypeInfo *cache_modifier_types[NUM_MODIFIER_TYPES] = {NULL}; static VirtualModifierData virtualModifierCommonData; void BKE_modifier_init(void) @@ -76,7 +77,7 @@ void BKE_modifier_init(void) ModifierData *md; /* Initialize modifier types */ - modifier_type_init(modifier_types); /* MOD_utils.c */ + modifier_type_init(cache_modifier_types); /* MOD_utils.c */ /* Initialize global cmmon storage used for virtual modifier list */ md = modifier_new(eModifierType_Armature); @@ -99,13 +100,15 @@ void BKE_modifier_init(void) virtualModifierCommonData.cmd.modifier.mode |= eModifierMode_Virtual; virtualModifierCommonData.lmd.modifier.mode |= eModifierMode_Virtual; virtualModifierCommonData.smd.modifier.mode |= eModifierMode_Virtual; + + BKE_cache_modifier_init(); } const ModifierTypeInfo *modifierType_getInfo(ModifierType type) { /* type unsigned, no need to check < 0 */ - if (type < NUM_MODIFIER_TYPES && modifier_types[type]->name[0] != '\0') { - return modifier_types[type]; + if (type < NUM_MODIFIER_TYPES && cache_modifier_types[type]->name[0] != '\0') { + return cache_modifier_types[type]; } else { return NULL; diff --git a/source/blender/blenkernel/intern/node.c b/source/blender/blenkernel/intern/node.c index 22d149c898c..dae6ede45b2 100644 --- a/source/blender/blenkernel/intern/node.c +++ b/source/blender/blenkernel/intern/node.c @@ -3660,6 +3660,7 @@ static void registerShaderNodes(void) register_node_type_sh_tex_magic(); register_node_type_sh_tex_checker(); register_node_type_sh_tex_brick(); + register_node_type_sh_tex_pointdensity(); } static void registerTextureNodes(void) diff --git a/source/blender/blenkernel/intern/object.c b/source/blender/blenkernel/intern/object.c index f8fe53063bc..365a5d838eb 100644 --- a/source/blender/blenkernel/intern/object.c +++ b/source/blender/blenkernel/intern/object.c @@ -38,6 +38,7 @@ #include "DNA_anim_types.h" #include "DNA_armature_types.h" +#include "DNA_cache_library_types.h" #include "DNA_camera_types.h" #include "DNA_constraint_types.h" #include "DNA_group_types.h" @@ -76,6 +77,7 @@ #include "BKE_armature.h" #include "BKE_action.h" #include "BKE_bullet.h" +#include "BKE_cache_library.h" #include "BKE_deform.h" #include "BKE_depsgraph.h" #include "BKE_DerivedMesh.h" @@ -85,6 +87,7 @@ #include "BKE_curve.h" #include "BKE_displist.h" #include "BKE_effect.h" +#include "BKE_facemap.h" #include "BKE_fcurve.h" #include "BKE_group.h" #include "BKE_key.h" @@ -99,6 +102,7 @@ #include "BKE_multires.h" #include "BKE_node.h" #include "BKE_object.h" +#include "BKE_object_deform.h" #include "BKE_paint.h" #include "BKE_particle.h" #include "BKE_pointcache.h" @@ -107,6 +111,7 @@ #include "BKE_sca.h" #include "BKE_scene.h" #include "BKE_sequencer.h" +#include "BKE_smoke.h" #include "BKE_speaker.h" #include "BKE_softbody.h" #include "BKE_subsurf.h" @@ -325,9 +330,12 @@ void BKE_object_free_derived_caches(Object *ob) } if (ob->derivedFinal) { - ob->derivedFinal->needsFree = 1; - ob->derivedFinal->release(ob->derivedFinal); - ob->derivedFinal = NULL; + /* dupli cache owns the derivedFinal, is freed by duplicator object */ + if (!(ob->transflag & OB_IS_DUPLI_CACHE)) { + ob->derivedFinal->needsFree = 1; + ob->derivedFinal->release(ob->derivedFinal); + ob->derivedFinal = NULL; + } } if (ob->derivedDeform) { ob->derivedDeform->needsFree = 1; @@ -336,9 +344,11 @@ void BKE_object_free_derived_caches(Object *ob) } BKE_object_free_curve_cache(ob); + + BKE_object_dupli_cache_clear(ob); } -void BKE_object_free_caches(Object *object) +void BKE_object_free_caches(Object *object, bool free_smoke_sim) { ModifierData *md; short update_flag = 0; @@ -351,7 +361,7 @@ void BKE_object_free_caches(Object *object) psys = psys->next) { psys_free_path_cache(psys, psys->edit); - update_flag |= PSYS_RECALC; + update_flag |= PSYS_RECALC_REDO; } } @@ -363,9 +373,32 @@ void BKE_object_free_caches(Object *object) psmd->dm->needsFree = 1; psmd->dm->release(psmd->dm); psmd->dm = NULL; + psmd->flag |= eParticleSystemFlag_file_loaded; update_flag |= OB_RECALC_DATA; } } + else if (md->type == eModifierType_Smoke) { + if (free_smoke_sim) { + SmokeModifierData *smd = (SmokeModifierData *) md; + SmokeDomainSettings *sds = smd->domain; + if (sds != NULL) { + bool use_sim = sds->point_cache[0] == NULL; + PointCache *cache; + /* We only reset cache if all the point caches are baked to file. */ + for (cache = sds->ptcaches[0].first; + cache != NULL && use_sim == false; + cache = cache->next) + { + use_sim |= ((cache->flag & (PTCACHE_BAKED|PTCACHE_DISK_CACHE)) != (PTCACHE_BAKED|PTCACHE_DISK_CACHE)); + } + if (!use_sim) { + smokeModifier_reset(smd); + smokeModifier_reset_turbulence(smd); + update_flag |= OB_RECALC_DATA; + } + } + } + } } /* Tag object for update, so once memory critical operation is over and @@ -422,6 +455,8 @@ void BKE_object_free_ex(Object *ob, bool do_id_user) if (ob->gpd) ((ID *)ob->gpd)->us--; if (ob->defbase.first) BLI_freelistN(&ob->defbase); + if (ob->fmaps.first) + BLI_freelistN(&ob->fmaps); if (ob->pose) BKE_pose_free_ex(ob->pose, do_id_user); if (ob->mpath) @@ -456,6 +491,8 @@ void BKE_object_free_ex(Object *ob, bool do_id_user) free_path(ob->curve_cache->path); MEM_freeN(ob->curve_cache); } + + BKE_object_dupli_cache_free(ob); } void BKE_object_free(Object *ob) @@ -474,6 +511,15 @@ static void unlink_object__unlinkModifierLinks(void *userData, Object *ob, Objec } } +static void unlink_object__unlinkCacheModifierLinks(void *userData, CacheLibrary *UNUSED(cachelib), CacheModifier *UNUSED(md), ID **idpoin) +{ + Object *unlinkOb = userData; + + if (*idpoin == (ID *)unlinkOb) { + *idpoin = NULL; + } +} + void BKE_object_unlink(Object *ob) { Main *bmain = G.main; @@ -495,6 +541,7 @@ void BKE_object_unlink(Object *ob) ARegion *ar; RegionView3D *rv3d; LodLevel *lod; + CacheLibrary *cachelib; int a, found; unlink_controllers(&ob->controllers); @@ -682,6 +729,10 @@ void BKE_object_unlink(Object *ob) lod->source = NULL; } + /* dupli cache is cleared entirely if the object in question is duplified to keep it simple */ + if (BKE_object_dupli_cache_contains(obt, ob)) + BKE_object_dupli_cache_clear(obt); + obt = obt->id.next; } @@ -844,6 +895,15 @@ void BKE_object_unlink(Object *ob) } camera = camera->id.next; } + + /* cache libraries */ + for (cachelib = bmain->cache_library.first; cachelib; cachelib = cachelib->id.next) { + CacheModifier *md; + + for (md = cachelib->modifiers.first; md; md = md->next) { + BKE_cache_modifier_foreachIDLink(cachelib, md, unlink_object__unlinkCacheModifierLinks, ob); + } + } } /* actual check for internal data, not context or flags */ @@ -1311,6 +1371,7 @@ ParticleSystem *BKE_object_copy_particlesystem(ParticleSystem *psys) psysn->pathcache = NULL; psysn->childcache = NULL; psysn->edit = NULL; + psysn->hairedit = NULL; psysn->pdd = NULL; psysn->effectors = NULL; psysn->tree = NULL; @@ -1322,13 +1383,8 @@ ParticleSystem *BKE_object_copy_particlesystem(ParticleSystem *psys) psysn->pointcache = BKE_ptcache_copy_list(&psysn->ptcaches, &psys->ptcaches, false); - /* XXX - from reading existing code this seems correct but intended usage of - * pointcache should /w cloth should be added in 'ParticleSystem' - campbell */ - if (psysn->clmd) { - psysn->clmd->point_cache = psysn->pointcache; - } - id_us_plus((ID *)psysn->part); + id_us_plus((ID *)psysn->key); return psysn; } @@ -1512,6 +1568,7 @@ Object *BKE_object_copy_ex(Main *bmain, Object *ob, bool copy_caches) BKE_pose_rebuild(obn, obn->data); } defgroup_copy_list(&obn->defbase, &ob->defbase); + fmap_copy_list(&obn->fmaps, &ob->fmaps); BKE_constraints_copy(&obn->constraints, &ob->constraints, true); obn->mode = OB_MODE_OBJECT; @@ -1521,6 +1578,7 @@ Object *BKE_object_copy_ex(Main *bmain, Object *ob, bool copy_caches) id_us_plus((ID *)obn->data); id_us_plus((ID *)obn->gpd); id_lib_extern((ID *)obn->dup_group); + id_lib_extern((ID *)obn->cache_library); for (a = 0; a < obn->totcol; a++) id_us_plus((ID *)obn->mat[a]); @@ -1552,6 +1610,8 @@ Object *BKE_object_copy_ex(Main *bmain, Object *ob, bool copy_caches) /* Copy runtime surve data. */ obn->curve_cache = NULL; + obn->dup_cache = NULL; + if (ob->id.lib) { BKE_id_lib_local_paths(bmain, ob->id.lib, &obn->id); } @@ -1583,13 +1643,16 @@ static void extern_local_object(Object *ob) id_lib_extern((ID *)ob->data); id_lib_extern((ID *)ob->dup_group); + id_lib_extern((ID *)ob->cache_library); id_lib_extern((ID *)ob->poselib); id_lib_extern((ID *)ob->gpd); extern_local_matarar(ob->mat, ob->totcol); - for (psys = ob->particlesystem.first; psys; psys = psys->next) + for (psys = ob->particlesystem.first; psys; psys = psys->next) { id_lib_extern((ID *)psys->part); + id_lib_extern((ID *)psys->key); + } modifiers_foreachIDLink(ob, extern_local_object__modifiersForeachIDLink, NULL); } @@ -2838,7 +2901,15 @@ bool BKE_object_minmax_dupli(Scene *scene, Object *ob, float r_min[3], float r_m /* pass */ } else { - BoundBox *bb = BKE_object_boundbox_get(dob->ob); + BoundBox *bb = NULL; + if (ob->dup_cache) { + DupliObjectData *dob_data = BKE_dupli_cache_find_data(ob->dup_cache, dob->ob); + if (dob_data && dob_data->dm) { + bb = &dob_data->bb; + } + } + if (!bb) + bb = BKE_object_boundbox_get(dob->ob); if (bb) { int i; diff --git a/source/blender/blenkernel/intern/object_dupli.c b/source/blender/blenkernel/intern/object_dupli.c index 8abe4bdbb97..fd0d0daf7e4 100644 --- a/source/blender/blenkernel/intern/object_dupli.c +++ b/source/blender/blenkernel/intern/object_dupli.c @@ -35,11 +35,16 @@ #include "MEM_guardedalloc.h" +#include "BLI_utildefines.h" +#include "BLI_ghash.h" #include "BLI_listbase.h" +#include "BLI_path_util.h" +#include "BLI_string.h" #include "BLI_string_utf8.h" #include "BLI_math.h" #include "BLI_rand.h" +#include "BLI_task.h" #include "DNA_anim_types.h" #include "DNA_group_types.h" @@ -48,6 +53,7 @@ #include "DNA_vfont_types.h" #include "BKE_animsys.h" +#include "BKE_cache_library.h" #include "BKE_DerivedMesh.h" #include "BKE_depsgraph.h" #include "BKE_font.h" @@ -56,15 +62,18 @@ #include "BKE_lattice.h" #include "BKE_main.h" #include "BKE_mesh.h" +#include "BKE_mesh_sample.h" #include "BKE_object.h" #include "BKE_particle.h" #include "BKE_scene.h" #include "BKE_editmesh.h" #include "BKE_anim.h" - +#include "BKE_strands.h" #include "BLI_strict_flags.h" +#include "BLF_translation.h" + /* Dupli-Geometry */ typedef struct DupliContext { @@ -96,7 +105,7 @@ typedef struct DupliGenerator { static const DupliGenerator *get_dupli_generator(const DupliContext *ctx); /* create initial context for root object */ -static void init_context(DupliContext *r_ctx, EvaluationContext *eval_ctx, Scene *scene, Object *ob, float space_mat[4][4], bool update) +static void init_context_ex(DupliContext *r_ctx, EvaluationContext *eval_ctx, Scene *scene, Object *ob, float space_mat[4][4], const DupliGenerator *gen, bool update) { r_ctx->eval_ctx = eval_ctx; r_ctx->scene = scene; @@ -110,14 +119,19 @@ static void init_context(DupliContext *r_ctx, EvaluationContext *eval_ctx, Scene copy_m4_m4(r_ctx->space_mat, space_mat); else unit_m4(r_ctx->space_mat); - r_ctx->lay = ob->lay; + r_ctx->lay = ob ? ob->lay : 0; r_ctx->level = 0; - r_ctx->gen = get_dupli_generator(r_ctx); + r_ctx->gen = gen ? gen : get_dupli_generator(r_ctx); r_ctx->duplilist = NULL; } +static void init_context(DupliContext *r_ctx, EvaluationContext *eval_ctx, Scene *scene, Object *ob, bool update) +{ + init_context_ex(r_ctx, eval_ctx, scene, ob, NULL, NULL, update); +} + /* create sub-context for recursive duplis */ static void copy_dupli_context(DupliContext *r_ctx, const DupliContext *ctx, Object *ob, float mat[4][4], int index, bool animated) { @@ -126,7 +140,7 @@ static void copy_dupli_context(DupliContext *r_ctx, const DupliContext *ctx, Obj r_ctx->animated |= animated; /* object animation makes all children animated */ /* XXX annoying, previously was done by passing an ID* argument, this at least is more explicit */ - if (ctx->gen->type == OB_DUPLIGROUP) + if (ctx->gen->type == OB_DUPLIGROUP && ctx->object) r_ctx->group = ctx->object->dup_group; r_ctx->object = ob; @@ -219,7 +233,10 @@ static void make_child_duplis(const DupliContext *ctx, void *userdata, MakeChild { Object *parent = ctx->object; Object *obedit = ctx->scene->obedit; - + + if (!parent) + return; + if (ctx->group) { unsigned int lay = ctx->group->layer; GroupObject *go; @@ -255,69 +272,84 @@ static void make_child_duplis(const DupliContext *ctx, void *userdata, MakeChild /*---- Implementations ----*/ -/* OB_DUPLIGROUP */ -static void make_duplis_group(const DupliContext *ctx) +/* Intern function for creating instances of group content + * with or without a parent (parent == NULL is allowed!) + * Note: some of the group animation update functions use the parent object, + * but this is old NLA code that is currently disabled and might be removed entirely. + */ +static void make_duplis_group_intern(const DupliContext *ctx, Group *group, Object *parent) { - bool for_render = (ctx->eval_ctx->mode == DAG_EVAL_RENDER); - Object *ob = ctx->object; - Group *group; + const bool for_render = (ctx->eval_ctx->mode == DAG_EVAL_RENDER); + GroupObject *go; float group_mat[4][4]; int id; - bool animated, hide; - - if (ob->dup_group == NULL) return; - group = ob->dup_group; - - /* combine group offset and obmat */ + bool animated; + unit_m4(group_mat); sub_v3_v3(group_mat[3], group->dupli_ofs); - mul_m4_m4m4(group_mat, ob->obmat, group_mat); - /* don't access 'ob->obmat' from now on. */ - + + if (parent) { + /* combine group offset and obmat */ + mul_m4_m4m4(group_mat, parent->obmat, group_mat); + /* don't access 'parent->obmat' from now on. */ + } + /* handles animated groups */ - + /* we need to check update for objects that are not in scene... */ if (ctx->do_update) { /* note: update is optional because we don't always need object * transformations to be correct. Also fixes bug [#29616]. */ - BKE_group_handle_recalc_and_update(ctx->eval_ctx, ctx->scene, ob, group); + BKE_group_handle_recalc_and_update(ctx->eval_ctx, ctx->scene, parent, group); } - - animated = BKE_group_is_animated(group, ob); - + + animated = BKE_group_is_animated(group, parent); + for (go = group->gobject.first, id = 0; go; go = go->next, id++) { + float mat[4][4]; + bool hide; + /* note, if you check on layer here, render goes wrong... it still deforms verts and uses parent imat */ - if (go->ob != ob) { - float mat[4][4]; - - /* Special case for instancing dupli-groups, see: T40051 - * this object may be instanced via dupli-verts/faces, in this case we don't want to render - * (blender convention), but _do_ show in the viewport. - * - * Regular objects work fine but not if we're instancing dupli-groups, - * because the rules for rendering aren't applied to objects they instance. - * We could recursively pass down the 'hide' flag instead, but that seems unnecessary. - */ - if (for_render && go->ob->parent && go->ob->parent->transflag & (OB_DUPLIVERTS | OB_DUPLIFACES)) { - continue; - } - - /* group dupli offset, should apply after everything else */ - mul_m4_m4m4(mat, group_mat, go->ob->obmat); - - /* check the group instance and object layers match, also that the object visible flags are ok. */ - hide = (go->ob->lay & group->layer) == 0 || - (for_render ? go->ob->restrictflag & OB_RESTRICT_RENDER : go->ob->restrictflag & OB_RESTRICT_VIEW); - - make_dupli(ctx, go->ob, mat, id, animated, hide); - - /* recursion */ - make_recursive_duplis(ctx, go->ob, group_mat, id, animated); + if (go->ob == parent) + continue; + + /* Special case for instancing dupli-groups, see: T40051 + * this object may be instanced via dupli-verts/faces, in this case we don't want to render + * (blender convention), but _do_ show in the viewport. + * + * Regular objects work fine but not if we're instancing dupli-groups, + * because the rules for rendering aren't applied to objects they instance. + * We could recursively pass down the 'hide' flag instead, but that seems unnecessary. + */ + if (for_render && go->ob->parent && go->ob->parent->transflag & (OB_DUPLIVERTS | OB_DUPLIFACES)) { + continue; } + + /* group dupli offset, should apply after everything else */ + mul_m4_m4m4(mat, group_mat, go->ob->obmat); + + /* check the group instance and object layers match, also that the object visible flags are ok. */ + hide = (go->ob->lay & group->layer) == 0 || + (for_render ? go->ob->restrictflag & OB_RESTRICT_RENDER : go->ob->restrictflag & OB_RESTRICT_VIEW); + + make_dupli(ctx, go->ob, mat, id, animated, hide); + + /* recursion */ + make_recursive_duplis(ctx, go->ob, group_mat, id, animated); } } +/* OB_DUPLIGROUP */ +static void make_duplis_group(const DupliContext *ctx) +{ + Object *ob = ctx->object; + if (!ob || !ob->dup_group) + return; + + make_duplis_group_intern(ctx, ob->dup_group, ob); +} + const DupliGenerator gen_dupli_group = { OB_DUPLIGROUP, /* type */ make_duplis_group /* make_duplis */ @@ -331,8 +363,9 @@ static void make_duplis_frames(const DupliContext *ctx) extern int enable_cu_speed; /* object.c */ Object copyob; int cfrao = scene->r.cfra; - int dupend = ob->dupend; + if (!ob) + return; /* dupliframes not supported inside groups */ if (ctx->group) return; @@ -341,7 +374,7 @@ static void make_duplis_frames(const DupliContext *ctx) */ if (ob->parent == NULL && BLI_listbase_is_empty(&ob->constraints) && ob->adt == NULL) return; - + /* make a copy of the object's original data (before any dupli-data overwrites it) * as we'll need this to keep track of unkeyed data * - this doesn't take into account other data that can be reached from the object, @@ -356,7 +389,7 @@ static void make_duplis_frames(const DupliContext *ctx) * updates, as this is not a permanent change to the object */ ob->id.flag |= LIB_ANIM_NO_RECALC; - for (scene->r.cfra = ob->dupsta; scene->r.cfra <= dupend; scene->r.cfra++) { + for (scene->r.cfra = ob->dupsta; scene->r.cfra <= ob->dupend; scene->r.cfra++) { int ok = 1; /* - dupoff = how often a frames within the range shouldn't be made into duplis @@ -512,6 +545,9 @@ static void make_duplis_verts(const DupliContext *ctx) Object *parent = ctx->object; bool use_texcoords = ELEM(ctx->eval_ctx->mode, DAG_EVAL_RENDER, DAG_EVAL_PREVIEW); VertexDupliData vdd; + + if (!parent) + return; vdd.ctx = ctx; vdd.use_rotation = parent->transflag & OB_DUPLIROT; @@ -592,6 +628,8 @@ static void make_duplis_font(const DupliContext *ctx) const wchar_t *text = NULL; bool text_free = false; + if (!par) + return; /* font dupliverts not supported inside groups */ if (ctx->group) return; @@ -779,6 +817,9 @@ static void make_duplis_faces(const DupliContext *ctx) bool use_texcoords = ELEM(ctx->eval_ctx->mode, DAG_EVAL_RENDER, DAG_EVAL_PREVIEW); FaceDupliData fdd; + if (!parent) + return; + fdd.use_scale = ((parent->transflag & OB_DUPLIFACES_SCALE) != 0); /* gather mesh info */ @@ -842,7 +883,8 @@ static void make_duplis_particle_system(const DupliContext *ctx, ParticleSystem int no_draw_flag = PARS_UNEXIST; - if (psys == NULL) return; + if (!psys) + return; part = psys->part; @@ -983,7 +1025,7 @@ static void make_duplis_particle_system(const DupliContext *ctx, ParticleSystem /* for groups, pick the object based on settings */ if (part->draw & PART_DRAW_RAND_GR) - b = BLI_rand() % totgroup; + b = (int)(psys_frand(psys, (unsigned int)(a + 974)) * (float)totgroup) % totgroup; else b = a % totgroup; @@ -1123,6 +1165,9 @@ static void make_duplis_particles(const DupliContext *ctx) ParticleSystem *psys; int psysid; + if (!ctx->object) + return; + /* particle system take up one level in id, the particles another */ for (psys = ctx->object->particlesystem.first, psysid = 0; psys; psys = psys->next, psysid++) { /* particles create one more level for persistent psys index */ @@ -1142,8 +1187,13 @@ const DupliGenerator gen_dupli_particles = { /* select dupli generator from given context */ static const DupliGenerator *get_dupli_generator(const DupliContext *ctx) { - int transflag = ctx->object->transflag; - int restrictflag = ctx->object->restrictflag; + int transflag, restrictflag; + + if (!ctx->object) + return NULL; + + transflag = ctx->object->transflag; + restrictflag = ctx->object->restrictflag; if ((transflag & OB_DUPLI) == 0) return NULL; @@ -1183,15 +1233,36 @@ static const DupliGenerator *get_dupli_generator(const DupliContext *ctx) /* Returns a list of DupliObject */ ListBase *object_duplilist_ex(EvaluationContext *eval_ctx, Scene *scene, Object *ob, bool update) { - ListBase *duplilist = MEM_callocN(sizeof(ListBase), "duplilist"); - DupliContext ctx; - init_context(&ctx, eval_ctx, scene, ob, NULL, update); - if (ctx.gen) { - ctx.duplilist = duplilist; - ctx.gen->make_duplis(&ctx); + if (update) { + BKE_object_dupli_cache_update(scene, ob, eval_ctx, (float)scene->r.cfra + scene->r.subframe); + } + + if (ob->dup_cache && (ob->dup_cache->result != CACHE_READ_SAMPLE_INVALID)) { + /* Note: duplis in the cache don't have the main duplicator obmat applied. + * duplilist also should return a full copy of duplis, so we copy + * the cached list and apply the obmat to each. + */ + ListBase *duplilist = MEM_callocN(sizeof(ListBase), "duplilist"); + DupliObject *dob; + + BLI_duplicatelist(duplilist, &ob->dup_cache->duplilist); + + for (dob = duplilist->first; dob; dob = dob->next) { + mul_m4_m4m4(dob->mat, ob->obmat, dob->mat); + } + + return duplilist; + } + else { + ListBase *duplilist = MEM_callocN(sizeof(ListBase), "duplilist"); + DupliContext ctx; + init_context(&ctx, eval_ctx, scene, ob, update); + if (ctx.gen) { + ctx.duplilist = duplilist; + ctx.gen->make_duplis(&ctx); + } + return duplilist; } - - return duplilist; } /* note: previously updating was always done, this is why it defaults to be on @@ -1201,6 +1272,24 @@ ListBase *object_duplilist(EvaluationContext *eval_ctx, Scene *sce, Object *ob) return object_duplilist_ex(eval_ctx, sce, ob, true); } +ListBase *group_duplilist_ex(EvaluationContext *eval_ctx, Scene *scene, Group *group, bool update) +{ + ListBase *duplilist = MEM_callocN(sizeof(ListBase), "duplilist"); + DupliContext ctx; + + init_context_ex(&ctx, eval_ctx, scene, NULL, NULL, &gen_dupli_group, update); + ctx.duplilist = duplilist; + + make_duplis_group_intern(&ctx, group, NULL); + + return duplilist; +} + +ListBase *group_duplilist(EvaluationContext *eval_ctx, Scene *scene, Group *group) +{ + return group_duplilist_ex(eval_ctx, scene, group, true); +} + void free_object_duplilist(ListBase *lb) { BLI_freelistN(lb); @@ -1237,6 +1326,558 @@ int count_duplilist(Object *ob) return 1; } +/* ------------------------------------------------------------------------- */ + +static void dupli_cache_calc_boundbox(DupliObjectData *data) +{ + DupliObjectDataStrands *link; + float min[3], max[3]; + bool has_data = false; + + INIT_MINMAX(min, max); + if (data->dm) { + data->dm->getMinMax(data->dm, min, max); + has_data = true; + } + for (link = data->strands.first; link; link = link->next) { + if (link->strands) { + BKE_strands_get_minmax(link->strands, min, max, true); + has_data = true; + } + if (link->strands_children) { + BKE_strands_children_get_minmax(link->strands_children, min, max); + has_data = true; + } + } + + if (!has_data) { + zero_v3(min); + zero_v3(max); + } + + BKE_boundbox_init_from_minmax(&data->bb, min, max); +} + +static bool UNUSED_FUNCTION(dupli_object_data_strands_unique_name)(ListBase *lb, DupliObjectDataStrands *link) +{ + if (lb && link) { + return BLI_uniquename(lb, link, DATA_("Strands"), '.', offsetof(DupliObjectDataStrands, name), sizeof(link->name)); + } + return false; +} + +void BKE_dupli_object_data_init(DupliObjectData *data, Object *ob) +{ + data->ob = ob; + + data->dm = NULL; + BLI_listbase_clear(&data->strands); + + memset(&data->bb, 0, sizeof(data->bb)); + dupli_cache_calc_boundbox(data); +} + +void BKE_dupli_object_data_clear(DupliObjectData *data) +{ + DupliObjectDataStrands *link; + + if (data->dm) { + /* we lock DMs in the cache to prevent freeing outside, + * now allow releasing again + */ + data->dm->needsFree = true; + data->dm->release(data->dm); + } + + for (link = data->strands.first; link; link = link->next) { + if (link->strands) + BKE_strands_free(link->strands); + if (link->strands_children) + BKE_strands_children_free(link->strands_children); + } + BLI_freelistN(&data->strands); +} + +void BKE_dupli_object_data_set_mesh(DupliObjectData *data, DerivedMesh *dm) +{ + if (data->dm) { + /* we lock DMs in the cache to prevent freeing outside, + * now allow releasing again + */ + data->dm->needsFree = true; + data->dm->release(data->dm); + } + + data->dm = dm; + /* we own this dm now and need to protect it until we free it ourselves */ + dm->needsFree = false; + + dupli_cache_calc_boundbox(data); +} + +void BKE_dupli_object_data_add_strands(DupliObjectData *data, const char *name, Strands *strands) +{ + DupliObjectDataStrands *link = NULL; + for (link = data->strands.first; link; link = link->next) { + if (STREQ(link->name, name)) + break; + } + + if (!link) { + link = MEM_callocN(sizeof(DupliObjectDataStrands), "strands link"); + BLI_strncpy(link->name, name, sizeof(link->name)); + link->strands = strands; + + BLI_addtail(&data->strands, link); + } + else { + if (link->strands && link->strands != strands) + BKE_strands_free(link->strands); + link->strands = strands; + } + + dupli_cache_calc_boundbox(data); +} + +void BKE_dupli_object_data_add_strands_children(DupliObjectData *data, const char *name, StrandsChildren *children) +{ + DupliObjectDataStrands *link = NULL; + for (link = data->strands.first; link; link = link->next) { + if (STREQ(link->name, name)) + break; + } + + if (!link) { + link = MEM_callocN(sizeof(DupliObjectDataStrands), "strands link"); + BLI_strncpy(link->name, name, sizeof(link->name)); + link->strands_children = children; + + BLI_addtail(&data->strands, link); + } + else { + if (link->strands_children && link->strands_children != children) + BKE_strands_children_free(link->strands_children); + link->strands_children = children; + } + + dupli_cache_calc_boundbox(data); +} + +void BKE_dupli_object_data_find_strands(DupliObjectData *data, const char *name, Strands **r_strands, StrandsChildren **r_children) +{ + DupliObjectDataStrands *link; + for (link = data->strands.first; link; link = link->next) { + if (STREQ(link->name, name)) { + if (r_strands) *r_strands = link->strands; + if (r_children) *r_children = link->strands_children; + return; + } + } + + if (r_strands) *r_strands = NULL; + if (r_children) *r_children = NULL; +} + +bool BKE_dupli_object_data_acquire_strands(DupliObjectData *data, Strands *strands) +{ + DupliObjectDataStrands *link, *link_next; + bool found = false; + + if (!data || !strands) + return false; + + for (link = data->strands.first; link; link = link_next) { + link_next = link->next; + if (link->strands == strands) { + link->strands = NULL; + found = true; + } + } + return found; +} + +bool BKE_dupli_object_data_acquire_strands_children(DupliObjectData *data, StrandsChildren *children) +{ + DupliObjectDataStrands *link, *link_next; + bool found = false; + + if (!data || !children) + return false; + + for (link = data->strands.first; link; link = link_next) { + link_next = link->next; + if (link->strands_children == children) { + link->strands_children = NULL; + found = true; + } + } + return found; +} + +/* ------------------------------------------------------------------------- */ + +static void dupli_object_data_free(DupliObjectData *data) +{ + BKE_dupli_object_data_clear(data); + MEM_freeN(data); +} + +static void dupli_object_free(DupliObject *dob) +{ + MEM_freeN(dob); +} + +DupliCache *BKE_dupli_cache_new(void) +{ + DupliCache *dupcache = MEM_callocN(sizeof(DupliCache), "dupli object cache"); + + dupcache->ghash = BLI_ghash_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, "dupli object data hash"); + + return dupcache; +} + +void BKE_dupli_cache_free(DupliCache *dupcache) +{ + BKE_dupli_cache_clear(dupcache); + + BLI_ghash_free(dupcache->ghash, NULL, (GHashValFreeFP)dupli_object_data_free); + MEM_freeN(dupcache); +} + +void BKE_dupli_cache_clear(DupliCache *dupcache) +{ + DupliObject *dob, *dob_next; + for (dob = dupcache->duplilist.first; dob; dob = dob_next) { + dob_next = dob->next; + + dupli_object_free(dob); + } + BLI_listbase_clear(&dupcache->duplilist); + + BLI_ghash_clear(dupcache->ghash, NULL, (GHashValFreeFP)dupli_object_data_free); +} + +void BKE_dupli_cache_clear_instances(DupliCache *dupcache) +{ + DupliObject *dob, *dob_next; + for (dob = dupcache->duplilist.first; dob; dob = dob_next) { + dob_next = dob->next; + + dupli_object_free(dob); + } + BLI_listbase_clear(&dupcache->duplilist); +} + +static DupliObjectData *dupli_cache_add_object_data(DupliCache *dupcache, Object *ob) +{ + DupliObjectData *data = MEM_callocN(sizeof(DupliObjectData), "dupli object data"); + + data->ob = ob; + BLI_ghash_insert(dupcache->ghash, data->ob, data); + return data; +} + +static DupliObject *dupli_cache_add_object(DupliCache *dupcache) +{ + DupliObject *dob = MEM_callocN(sizeof(DupliObject), "dupli object"); + + unit_m4(dob->mat); + + BLI_addtail(&dupcache->duplilist, dob); + return dob; +} + +static int count_hair_verts(ParticleSystem *psys) +{ + int numverts = 0; + int p; + for (p = 0; p < psys->totpart; ++p) { + numverts += psys->particles[p].totkey; + } + return numverts; +} + +static void dupli_strands_data_update(CacheLibrary *cachelib, DupliObjectData *data, + DupliObject *dob, bool calc_strands_base) { + ParticleSystem *psys; + for (psys = dob->ob->particlesystem.first; psys; psys = psys->next) { + if (cachelib->data_types & CACHE_TYPE_HAIR) { + if (psys->part && psys->part->type == PART_HAIR) { + int numstrands = psys->totpart; + int numverts = count_hair_verts(psys); + ParticleData *pa; + HairKey *hkey; + int p, k; + + Strands *strands = BKE_strands_new(numstrands, numverts); + StrandsCurve *scurve = strands->curves; + StrandsVertex *svert = strands->verts; + + for (p = 0, pa = psys->particles; p < psys->totpart; ++p, ++pa) { + float hairmat[4][4]; + psys_mat_hair_to_object(dob->ob, data->dm, psys->part->from, pa, hairmat); + + scurve->numverts = pa->totkey; + copy_m3_m4(scurve->root_matrix, hairmat); + BKE_mesh_sample_from_particle(&scurve->msurf, psys, data->dm, pa); + + for (k = 0, hkey = pa->hair; k < pa->totkey; ++k, ++hkey) { + copy_v3_v3(svert->co, hkey->co); + if (calc_strands_base) + copy_v3_v3(svert->base, hkey->co); + svert->time = hkey->time; + svert->weight = hkey->weight; + ++svert; + } + ++scurve; + } + + BKE_dupli_object_data_add_strands(data, psys->name, strands); + } + } + } +} + +typedef struct DupliObjectDataFromGroupState { + EvaluationContext *eval_ctx; + Scene *scene; + CacheLibrary *cachelib; + bool calc_strands_base; +} DupliObjectDataFromGroupState; + +typedef struct DupliObjectDataFromGroupTask { + DupliObject *dob; + DupliObjectData *data; +} DupliObjectDataFromGroupTask; + +static void dupli_object_data_from_group_func(TaskPool *pool, void *taskdata, int UNUSED(threadid)) +{ + DupliObjectDataFromGroupState *state = (DupliObjectDataFromGroupState *)BLI_task_pool_userdata(pool); + DupliObjectDataFromGroupTask *task = (DupliObjectDataFromGroupTask *)taskdata; + Object *object = task->dob->ob; + DerivedMesh *dm; + + if (state->eval_ctx->mode == DAG_EVAL_RENDER) { + dm = mesh_create_derived_render(state->scene, object, CD_MASK_BAREMESH); + } + else { + dm = mesh_create_derived_view(state->scene, object, CD_MASK_BAREMESH); + } + + if (dm != NULL) { + BKE_dupli_object_data_set_mesh(task->data, dm); + } + + dupli_strands_data_update(state->cachelib, task->data, task->dob, state->calc_strands_base); +} + +void BKE_dupli_cache_from_group(Scene *scene, Group *group, CacheLibrary *cachelib, DupliCache *dupcache, EvaluationContext *eval_ctx, bool calc_strands_base) +{ + DupliObject *dob; + TaskScheduler *task_scheduler = BLI_task_scheduler_get(); + TaskPool *task_pool; + DupliObjectDataFromGroupState state; + + BKE_dupli_cache_clear(dupcache); + + if (!(group && cachelib)) + return; + + { + /* copy duplilist to the cache */ + ListBase *duplilist = group_duplilist(eval_ctx, scene, group); + dupcache->duplilist = *duplilist; + MEM_freeN(duplilist); + } + + state.eval_ctx = eval_ctx; + state.scene = scene; + state.cachelib = cachelib; + state.calc_strands_base = calc_strands_base; + task_pool = BLI_task_pool_create(task_scheduler, &state); + + /* tag objects for which to store data */ + BKE_cache_library_tag_used_objects(cachelib); + + for (dob = dupcache->duplilist.first; dob; dob = dob->next) { + DupliObjectData *data = BKE_dupli_cache_find_data(dupcache, dob->ob); + if (!data) { + bool strands_handled = false; + data = dupli_cache_add_object_data(dupcache, dob->ob); + + /* generate data only for filtered objects */ + if (dob->ob->id.flag & LIB_DOIT) { + if (cachelib->data_types & CACHE_TYPE_DERIVED_MESH) { + if (dob->ob->type == OB_MESH) { + /* TODO(sergey): Consider using memory pool instead. */ + DupliObjectDataFromGroupTask *task = MEM_mallocN(sizeof(DupliObjectDataFromGroupTask), + "dupcache task"); + task->dob = dob; + task->data = data; + BLI_task_pool_push(task_pool, dupli_object_data_from_group_func, task, true, TASK_PRIORITY_LOW); + /* Task is getting care of strands as well. */ + strands_handled = true; + } + } + if (!strands_handled) { + dupli_strands_data_update(cachelib, data, dob, calc_strands_base); + } + } + } + } + + BLI_task_pool_work_and_wait(task_pool); + BLI_task_pool_free(task_pool); +} + +/* ------------------------------------------------------------------------- */ + +void BKE_object_dupli_cache_update(Scene *scene, Object *ob, EvaluationContext *eval_ctx, float frame) +{ + bool use_render = (eval_ctx->mode == DAG_EVAL_RENDER); + bool is_dupligroup = (ob->transflag & OB_DUPLIGROUP) && ob->dup_group; + bool is_cached = ob->cache_library && (ob->cache_library->source_mode == CACHE_LIBRARY_SOURCE_CACHE || ob->cache_library->display_mode == CACHE_LIBRARY_DISPLAY_RESULT); + bool do_modifiers = ob->cache_library && ob->cache_library->display_mode == CACHE_LIBRARY_DISPLAY_MODIFIERS; + + /* cache is a group duplicator feature only */ + if (is_dupligroup && is_cached) { + + if (ob->dup_cache && !(ob->dup_cache->flag & DUPCACHE_FLAG_DIRTY)) { + /* skip if cache is valid */ + } + else { + if (G.debug & G_DEBUG) + printf("Update dupli cache for object '%s'\n", ob->id.name+2); + + if (ob->dup_cache) { + BKE_dupli_cache_clear(ob->dup_cache); + } + else { + ob->dup_cache = BKE_dupli_cache_new(); + } + + /* skip reading when the cachelib is baking, avoids unnecessary memory allocation */ + if (!(ob->cache_library->flag & CACHE_LIBRARY_BAKING)) { + bool do_strands_motion, do_strands_children; + CacheLibrary *cachelib = ob->cache_library; + CacheProcessData process_data; + + BKE_cache_library_get_read_flags(ob->cache_library, use_render, true, &do_strands_motion, &do_strands_children); + + /* TODO at this point we could apply animation offset */ + BKE_cache_read_dupli_cache(ob->cache_library, ob->dup_cache, scene, ob->dup_group, frame, use_render, true); + + process_data.lay = ob->lay; + copy_m4_m4(process_data.mat, ob->obmat); + process_data.dupcache = ob->dup_cache; + + BKE_cache_process_dupli_cache(cachelib, &process_data, scene, ob->dup_group, (float)scene->r.cfra, frame, do_modifiers, do_strands_children, do_strands_motion); + } + + ob->dup_cache->flag &= ~DUPCACHE_FLAG_DIRTY; + ob->dup_cache->cfra = frame; + } + + } + else { + if (ob->dup_cache) { + BKE_dupli_cache_free(ob->dup_cache); + ob->dup_cache = NULL; + } + } +} + +void BKE_object_dupli_cache_clear(Object *ob) +{ + if (ob->dup_cache) { + BKE_dupli_cache_clear(ob->dup_cache); + } +} + +void BKE_object_dupli_cache_free(Object *ob) +{ + if (ob->dup_cache) { + BKE_dupli_cache_free(ob->dup_cache); + ob->dup_cache = NULL; + } +} + +bool BKE_object_dupli_cache_contains(Object *ob, Object *other) +{ + if (ob->dup_cache) { + DupliObject *dob; + for (dob = ob->dup_cache->duplilist.first; dob; dob = dob->next) { + if (dob->ob == other) + return true; + } + } + return false; +} + + +DupliObjectData *BKE_dupli_cache_find_data(DupliCache *dupcache, Object *ob) +{ + DupliObjectData *data = BLI_ghash_lookup(dupcache->ghash, ob); + return data; +} + +DupliObjectData *BKE_dupli_cache_add_object(DupliCache *dupcache, Object *ob) +{ + DupliObjectData *data = BKE_dupli_cache_find_data(dupcache, ob); + if (!data) { + data = dupli_cache_add_object_data(dupcache, ob); + } + return data; +} + +DupliObject *BKE_dupli_cache_add_instance(DupliCache *dupcache, float obmat[4][4], DupliObjectData *data) +{ + DupliObject *dob = dupli_cache_add_object(dupcache); + + /* data must have been created correctly */ + BLI_assert(BLI_ghash_lookup(dupcache->ghash, data->ob) != NULL); + + dob->ob = data->ob; + copy_m4_m4(dob->mat, obmat); + + dob->data = data; + + return dob; +} + +/* ------------------------------------------------------------------------- */ + +typedef struct DupliCacheIterator { + int unused; +} DupliCacheIterator; + +DupliCacheIterator *BKE_dupli_cache_iter_new(struct DupliCache *dupcache) +{ + return (DupliCacheIterator *)BLI_ghashIterator_new(dupcache->ghash); +} + +void BKE_dupli_cache_iter_free(DupliCacheIterator *iter) +{ + BLI_ghashIterator_free((GHashIterator *)iter); +} + +bool BKE_dupli_cache_iter_valid(DupliCacheIterator *iter) +{ + return !BLI_ghashIterator_done((GHashIterator *)iter); +} + +void BKE_dupli_cache_iter_next(DupliCacheIterator *iter) +{ + BLI_ghashIterator_step((GHashIterator *)iter); +} + +DupliObjectData *BKE_dupli_cache_iter_get(DupliCacheIterator *iter) +{ + return BLI_ghashIterator_getValue((GHashIterator *)iter); +} + +/* ------------------------------------------------------------------------- */ + DupliApplyData *duplilist_apply(Object *ob, Scene *scene, ListBase *duplilist) { DupliApplyData *apply_data = NULL; diff --git a/source/blender/blenkernel/intern/object_update.c b/source/blender/blenkernel/intern/object_update.c index 483968c6bd9..6752beb6d3b 100644 --- a/source/blender/blenkernel/intern/object_update.c +++ b/source/blender/blenkernel/intern/object_update.c @@ -163,6 +163,7 @@ void BKE_object_handle_data_update(EvaluationContext *eval_ctx, ID *data_id = (ID *)ob->data; AnimData *adt = BKE_animdata_from_id(data_id); Key *key; + ParticleSystem *psys; float ctime = BKE_scene_frame_get(scene); if (G.debug & G_DEBUG_DEPSGRAPH) @@ -181,6 +182,12 @@ void BKE_object_handle_data_update(EvaluationContext *eval_ctx, if (!(ob->shapeflag & OB_SHAPE_LOCK)) BKE_animsys_evaluate_animdata(scene, &key->id, key->adt, ctime, ADT_RECALC_DRIVERS); } + for (psys = ob->particlesystem.first; psys; psys = psys->next) { + key = psys->key; + if (key && key->block.first) { + BKE_animsys_evaluate_animdata(scene, &key->id, key->adt, ctime, ADT_RECALC_DRIVERS); + } + } /* includes all keys and modifiers */ switch (ob->type) { diff --git a/source/blender/blenkernel/intern/particle.c b/source/blender/blenkernel/intern/particle.c index 6c354c59d7a..a928ed69b9e 100644 --- a/source/blender/blenkernel/intern/particle.c +++ b/source/blender/blenkernel/intern/particle.c @@ -65,6 +65,7 @@ #include "BKE_boids.h" #include "BKE_cloth.h" #include "BKE_colortools.h" +#include "BKE_editstrands.h" #include "BKE_effect.h" #include "BKE_global.h" #include "BKE_group.h" @@ -362,6 +363,38 @@ int psys_uses_gravity(ParticleSimulationData *sim) { return sim->scene->physics_settings.flag & PHYS_GLOBAL_GRAVITY && sim->psys->part && sim->psys->part->effector_weights->global_gravity != 0.0f; } + +KeyBlock *BKE_psys_insert_shape_key(Scene *scene, Object *ob, ParticleSystem *psys, const char *name, const bool from_mix) +{ + Key *key = psys->key; + KeyBlock *kb; + int newkey = 0; + + if (key == NULL) { + key = psys->key = BKE_key_add_particles(ob, psys); + key->type = KEY_RELATIVE; + newkey = 1; + } + + if (newkey || !from_mix) { + /* create from particles */ + kb = BKE_keyblock_add_ctime(key, name, false); + BKE_keyblock_convert_from_hair_keys(ob, psys, kb); + } + else { + /* copy from current values */ + int totelem; + float *data = BKE_key_evaluate_particles(ob, psys, scene ? scene->r.cfra : 0.0f, &totelem); + + /* create new block with prepared data */ + kb = BKE_keyblock_add_ctime(key, name, false); + kb->data = data; + kb->totelem = totelem; + } + + return kb; +} + /************************************************/ /* Freeing stuff */ /************************************************/ @@ -546,6 +579,11 @@ void psys_free(Object *ob, ParticleSystem *psys) if (psys->edit && psys->free_edit) psys->free_edit(psys->edit); + if (psys->hairedit) { + BKE_editstrands_free(psys->hairedit); + MEM_freeN(psys->hairedit); + psys->hairedit = NULL; + } if (psys->child) { MEM_freeN(psys->child); @@ -570,6 +608,10 @@ void psys_free(Object *ob, ParticleSystem *psys) psys->part->id.us--; psys->part = NULL; } + if (psys->key) { + id_us_min(&psys->key->id); + psys->key = NULL; + } BKE_ptcache_free_list(&psys->ptcaches); psys->pointcache = NULL; @@ -769,400 +811,6 @@ bool psys_render_simplify_params(ParticleSystem *psys, ChildParticle *cpa, float } /************************************************/ -/* Interpolation */ -/************************************************/ -static float interpolate_particle_value(float v1, float v2, float v3, float v4, const float w[4], int four) -{ - float value; - - value = w[0] * v1 + w[1] * v2 + w[2] * v3; - if (four) - value += w[3] * v4; - - CLAMP(value, 0.f, 1.f); - - return value; -} - -void psys_interpolate_particle(short type, ParticleKey keys[4], float dt, ParticleKey *result, bool velocity) -{ - float t[4]; - - if (type < 0) { - interp_cubic_v3(result->co, result->vel, keys[1].co, keys[1].vel, keys[2].co, keys[2].vel, dt); - } - else { - key_curve_position_weights(dt, t, type); - - interp_v3_v3v3v3v3(result->co, keys[0].co, keys[1].co, keys[2].co, keys[3].co, t); - - if (velocity) { - float temp[3]; - - if (dt > 0.999f) { - key_curve_position_weights(dt - 0.001f, t, type); - interp_v3_v3v3v3v3(temp, keys[0].co, keys[1].co, keys[2].co, keys[3].co, t); - sub_v3_v3v3(result->vel, result->co, temp); - } - else { - key_curve_position_weights(dt + 0.001f, t, type); - interp_v3_v3v3v3v3(temp, keys[0].co, keys[1].co, keys[2].co, keys[3].co, t); - sub_v3_v3v3(result->vel, temp, result->co); - } - } - } -} - - -typedef struct ParticleInterpolationData { - HairKey *hkey[2]; - - DerivedMesh *dm; - MVert *mvert[2]; - - int keyed; - ParticleKey *kkey[2]; - - PointCache *cache; - PTCacheMem *pm; - - PTCacheEditPoint *epoint; - PTCacheEditKey *ekey[2]; - - float birthtime, dietime; - int bspline; -} ParticleInterpolationData; -/* Assumes pointcache->mem_cache exists, so for disk cached particles call psys_make_temp_pointcache() before use */ -/* It uses ParticleInterpolationData->pm to store the current memory cache frame so it's thread safe. */ -static void get_pointcache_keys_for_time(Object *UNUSED(ob), PointCache *cache, PTCacheMem **cur, int index, float t, ParticleKey *key1, ParticleKey *key2) -{ - static PTCacheMem *pm = NULL; - int index1, index2; - - if (index < 0) { /* initialize */ - *cur = cache->mem_cache.first; - - if (*cur) - *cur = (*cur)->next; - } - else { - if (*cur) { - while (*cur && (*cur)->next && (float)(*cur)->frame < t) - *cur = (*cur)->next; - - pm = *cur; - - index2 = BKE_ptcache_mem_index_find(pm, index); - index1 = BKE_ptcache_mem_index_find(pm->prev, index); - - BKE_ptcache_make_particle_key(key2, index2, pm->data, (float)pm->frame); - if (index1 < 0) - copy_particle_key(key1, key2, 1); - else - BKE_ptcache_make_particle_key(key1, index1, pm->prev->data, (float)pm->prev->frame); - } - else if (cache->mem_cache.first) { - pm = cache->mem_cache.first; - index2 = BKE_ptcache_mem_index_find(pm, index); - BKE_ptcache_make_particle_key(key2, index2, pm->data, (float)pm->frame); - copy_particle_key(key1, key2, 1); - } - } -} -static int get_pointcache_times_for_particle(PointCache *cache, int index, float *start, float *end) -{ - PTCacheMem *pm; - int ret = 0; - - for (pm = cache->mem_cache.first; pm; pm = pm->next) { - if (BKE_ptcache_mem_index_find(pm, index) >= 0) { - *start = pm->frame; - ret++; - break; - } - } - - for (pm = cache->mem_cache.last; pm; pm = pm->prev) { - if (BKE_ptcache_mem_index_find(pm, index) >= 0) { - *end = pm->frame; - ret++; - break; - } - } - - return ret == 2; -} - -float psys_get_dietime_from_cache(PointCache *cache, int index) -{ - PTCacheMem *pm; - int dietime = 10000000; /* some max value so that we can default to pa->time+lifetime */ - - for (pm = cache->mem_cache.last; pm; pm = pm->prev) { - if (BKE_ptcache_mem_index_find(pm, index) >= 0) - return (float)pm->frame; - } - - return (float)dietime; -} - -static void init_particle_interpolation(Object *ob, ParticleSystem *psys, ParticleData *pa, ParticleInterpolationData *pind) -{ - - if (pind->epoint) { - PTCacheEditPoint *point = pind->epoint; - - pind->ekey[0] = point->keys; - pind->ekey[1] = point->totkey > 1 ? point->keys + 1 : NULL; - - pind->birthtime = *(point->keys->time); - pind->dietime = *((point->keys + point->totkey - 1)->time); - } - else if (pind->keyed) { - ParticleKey *key = pa->keys; - pind->kkey[0] = key; - pind->kkey[1] = pa->totkey > 1 ? key + 1 : NULL; - - pind->birthtime = key->time; - pind->dietime = (key + pa->totkey - 1)->time; - } - else if (pind->cache) { - float start = 0.0f, end = 0.0f; - get_pointcache_keys_for_time(ob, pind->cache, &pind->pm, -1, 0.0f, NULL, NULL); - pind->birthtime = pa ? pa->time : pind->cache->startframe; - pind->dietime = pa ? pa->dietime : pind->cache->endframe; - - if (get_pointcache_times_for_particle(pind->cache, pa - psys->particles, &start, &end)) { - pind->birthtime = MAX2(pind->birthtime, start); - pind->dietime = MIN2(pind->dietime, end); - } - } - else { - HairKey *key = pa->hair; - pind->hkey[0] = key; - pind->hkey[1] = key + 1; - - pind->birthtime = key->time; - pind->dietime = (key + pa->totkey - 1)->time; - - if (pind->dm) { - pind->mvert[0] = CDDM_get_vert(pind->dm, pa->hair_index); - pind->mvert[1] = pind->mvert[0] + 1; - } - } -} -static void edit_to_particle(ParticleKey *key, PTCacheEditKey *ekey) -{ - copy_v3_v3(key->co, ekey->co); - if (ekey->vel) { - copy_v3_v3(key->vel, ekey->vel); - } - key->time = *(ekey->time); -} -static void hair_to_particle(ParticleKey *key, HairKey *hkey) -{ - copy_v3_v3(key->co, hkey->co); - key->time = hkey->time; -} - -static void mvert_to_particle(ParticleKey *key, MVert *mvert, HairKey *hkey) -{ - copy_v3_v3(key->co, mvert->co); - key->time = hkey->time; -} - -static void do_particle_interpolation(ParticleSystem *psys, int p, ParticleData *pa, float t, ParticleInterpolationData *pind, ParticleKey *result) -{ - PTCacheEditPoint *point = pind->epoint; - ParticleKey keys[4]; - int point_vel = (point && point->keys->vel); - float real_t, dfra, keytime, invdt = 1.f; - - /* billboards wont fill in all of these, so start cleared */ - memset(keys, 0, sizeof(keys)); - - /* interpret timing and find keys */ - if (point) { - if (result->time < 0.0f) - real_t = -result->time; - else - real_t = *(pind->ekey[0]->time) + t * (*(pind->ekey[0][point->totkey - 1].time) - *(pind->ekey[0]->time)); - - while (*(pind->ekey[1]->time) < real_t) - pind->ekey[1]++; - - pind->ekey[0] = pind->ekey[1] - 1; - } - else if (pind->keyed) { - /* we have only one key, so let's use that */ - if (pind->kkey[1] == NULL) { - copy_particle_key(result, pind->kkey[0], 1); - return; - } - - if (result->time < 0.0f) - real_t = -result->time; - else - real_t = pind->kkey[0]->time + t * (pind->kkey[0][pa->totkey - 1].time - pind->kkey[0]->time); - - if (psys->part->phystype == PART_PHYS_KEYED && psys->flag & PSYS_KEYED_TIMING) { - ParticleTarget *pt = psys->targets.first; - - pt = pt->next; - - while (pt && pa->time + pt->time < real_t) - pt = pt->next; - - if (pt) { - pt = pt->prev; - - if (pa->time + pt->time + pt->duration > real_t) - real_t = pa->time + pt->time; - } - else - real_t = pa->time + ((ParticleTarget *)psys->targets.last)->time; - } - - CLAMP(real_t, pa->time, pa->dietime); - - while (pind->kkey[1]->time < real_t) - pind->kkey[1]++; - - pind->kkey[0] = pind->kkey[1] - 1; - } - else if (pind->cache) { - if (result->time < 0.0f) /* flag for time in frames */ - real_t = -result->time; - else - real_t = pa->time + t * (pa->dietime - pa->time); - } - else { - if (result->time < 0.0f) - real_t = -result->time; - else - real_t = pind->hkey[0]->time + t * (pind->hkey[0][pa->totkey - 1].time - pind->hkey[0]->time); - - while (pind->hkey[1]->time < real_t) { - pind->hkey[1]++; - pind->mvert[1]++; - } - - pind->hkey[0] = pind->hkey[1] - 1; - } - - /* set actual interpolation keys */ - if (point) { - edit_to_particle(keys + 1, pind->ekey[0]); - edit_to_particle(keys + 2, pind->ekey[1]); - } - else if (pind->dm) { - pind->mvert[0] = pind->mvert[1] - 1; - mvert_to_particle(keys + 1, pind->mvert[0], pind->hkey[0]); - mvert_to_particle(keys + 2, pind->mvert[1], pind->hkey[1]); - } - else if (pind->keyed) { - memcpy(keys + 1, pind->kkey[0], sizeof(ParticleKey)); - memcpy(keys + 2, pind->kkey[1], sizeof(ParticleKey)); - } - else if (pind->cache) { - get_pointcache_keys_for_time(NULL, pind->cache, &pind->pm, p, real_t, keys + 1, keys + 2); - } - else { - hair_to_particle(keys + 1, pind->hkey[0]); - hair_to_particle(keys + 2, pind->hkey[1]); - } - - /* set secondary interpolation keys for hair */ - if (!pind->keyed && !pind->cache && !point_vel) { - if (point) { - if (pind->ekey[0] != point->keys) - edit_to_particle(keys, pind->ekey[0] - 1); - else - edit_to_particle(keys, pind->ekey[0]); - } - else if (pind->dm) { - if (pind->hkey[0] != pa->hair) - mvert_to_particle(keys, pind->mvert[0] - 1, pind->hkey[0] - 1); - else - mvert_to_particle(keys, pind->mvert[0], pind->hkey[0]); - } - else { - if (pind->hkey[0] != pa->hair) - hair_to_particle(keys, pind->hkey[0] - 1); - else - hair_to_particle(keys, pind->hkey[0]); - } - - if (point) { - if (pind->ekey[1] != point->keys + point->totkey - 1) - edit_to_particle(keys + 3, pind->ekey[1] + 1); - else - edit_to_particle(keys + 3, pind->ekey[1]); - } - else if (pind->dm) { - if (pind->hkey[1] != pa->hair + pa->totkey - 1) - mvert_to_particle(keys + 3, pind->mvert[1] + 1, pind->hkey[1] + 1); - else - mvert_to_particle(keys + 3, pind->mvert[1], pind->hkey[1]); - } - else { - if (pind->hkey[1] != pa->hair + pa->totkey - 1) - hair_to_particle(keys + 3, pind->hkey[1] + 1); - else - hair_to_particle(keys + 3, pind->hkey[1]); - } - } - - dfra = keys[2].time - keys[1].time; - keytime = (real_t - keys[1].time) / dfra; - - /* convert velocity to timestep size */ - if (pind->keyed || pind->cache || point_vel) { - invdt = dfra * 0.04f * (psys ? psys->part->timetweak : 1.f); - mul_v3_fl(keys[1].vel, invdt); - mul_v3_fl(keys[2].vel, invdt); - interp_qt_qtqt(result->rot, keys[1].rot, keys[2].rot, keytime); - } - - /* now we should have in chronologiacl order k1<=k2<=t<=k3<=k4 with keytime between [0, 1]->[k2, k3] (k1 & k4 used for cardinal & bspline interpolation)*/ - psys_interpolate_particle((pind->keyed || pind->cache || point_vel) ? -1 /* signal for cubic interpolation */ - : (pind->bspline ? KEY_BSPLINE : KEY_CARDINAL), - keys, keytime, result, 1); - - /* the velocity needs to be converted back from cubic interpolation */ - if (pind->keyed || pind->cache || point_vel) - mul_v3_fl(result->vel, 1.f / invdt); -} - -static void interpolate_pathcache(ParticleCacheKey *first, float t, ParticleCacheKey *result) -{ - int i = 0; - ParticleCacheKey *cur = first; - - /* scale the requested time to fit the entire path even if the path is cut early */ - t *= (first + first->segments)->time; - - while (i < first->segments && cur->time < t) - cur++; - - if (cur->time == t) - *result = *cur; - else { - float dt = (t - (cur - 1)->time) / (cur->time - (cur - 1)->time); - interp_v3_v3v3(result->co, (cur - 1)->co, cur->co, dt); - interp_v3_v3v3(result->vel, (cur - 1)->vel, cur->vel, dt); - interp_qt_qtqt(result->rot, (cur - 1)->rot, cur->rot, dt); - result->time = t; - } - - /* first is actual base rotation, others are incremental from first */ - if (cur == first || cur - 1 == first) - copy_qt_qt(result->rot, first->rot); - else - mul_qt_qtqt(result->rot, first->rot, result->rot); -} - -/************************************************/ /* Particles on a dm */ /************************************************/ /* interpolate a location on a face based on face coordinates */ @@ -1333,6 +981,19 @@ void psys_interpolate_mcol(const MCol *mcol, int quad, const float w[4], MCol *m } } +static float interpolate_particle_value(float v1, float v2, float v3, float v4, const float w[4], int four) +{ + float value; + + value = w[0] * v1 + w[1] * v2 + w[2] * v3; + if (four) + value += w[3] * v4; + + CLAMP(value, 0.f, 1.f); + + return value; +} + static float psys_interpolate_value_from_verts(DerivedMesh *dm, short from, int index, const float fw[4], const float *values) { if (values == 0 || index == -1) @@ -1520,6 +1181,11 @@ static int psys_map_index_on_dm(DerivedMesh *dm, int from, int index, int index_ return 1; } +int psys_get_index_on_dm(ParticleSystem *psys, DerivedMesh *dm, ParticleData *pa, int *mapindex, float mapfw[4]) +{ + return psys_map_index_on_dm(dm, psys->part->from, pa->num, pa->num_dmcache, pa->fuv, pa->foffset, mapindex, mapfw); +} + /* interprets particle data to get a point on a mesh in object space */ void psys_particle_on_dm(DerivedMesh *dm, int from, int index, int index_dmcache, const float fw[4], float foffset, float vec[3], float nor[3], float utan[3], float vtan[3], @@ -1709,8 +1375,8 @@ void psys_particle_on_emitter(ParticleSystemModifierData *psmd, int from, int in extern void do_kink(ParticleKey *state, const float par_co[3], const float par_vel[3], const float par_rot[4], float time, float freq, float shape, float amplitude, float flat, short type, short axis, float obmat[4][4], int smooth_start); -extern float do_clump(ParticleKey *state, const float par_co[3], float time, const float orco_offset[3], float clumpfac, float clumppow, float pa_clump, - bool use_clump_noise, float clump_noise_size, CurveMapping *clumpcurve); +extern float do_clump(ParticleKey *state, const float par_co[3], const float par_orco[3], float time, const float orco[3], float clumpfac, float clumppow, float pa_clump, + bool use_clump_noise, float clump_noise_size, CurveMapping *clumpcurve, float clump_noise_random, float clump_noise_random_size, float mat[4][4]); void precalc_guides(ParticleSimulationData *sim, ListBase *effectors) { @@ -1834,12 +1500,14 @@ int do_guides(ParticleSettings *part, ListBase *effectors, ParticleKey *state, i float par_co[3] = {0.0f, 0.0f, 0.0f}; float par_vel[3] = {0.0f, 0.0f, 0.0f}; float par_rot[4] = {1.0f, 0.0f, 0.0f, 0.0f}; - float orco_offset[3] = {0.0f, 0.0f, 0.0f}; + float par_orco[3] = {0.0f, 0.0f, 0.0f}; + float orco[3] = {0.0f, 0.0f, 0.0f}; copy_v3_v3(key.co, vec_to_point); do_kink(&key, par_co, par_vel, par_rot, guidetime, pd->kink_freq, pd->kink_shape, pd->kink_amp, 0.f, pd->kink, pd->kink_axis, 0, 0); - do_clump(&key, par_co, guidetime, orco_offset, pd->clump_fac, pd->clump_pow, 1.0f, - part->child_flag & PART_CHILD_USE_CLUMP_NOISE, part->clump_noise_size, clumpcurve); + do_clump(&key, par_co, par_orco, guidetime, orco, pd->clump_fac, pd->clump_pow, 1.0f, + part->child_flag & PART_CHILD_USE_CLUMP_NOISE, part->clump_noise_size, clumpcurve, + part->clump_noise_random, part->clump_noise_random_size, NULL); copy_v3_v3(vec_to_point, key.co); } @@ -1868,7 +1536,6 @@ int do_guides(ParticleSettings *part, ListBase *effectors, ParticleKey *state, i } return 0; } - static void do_path_effectors(ParticleSimulationData *sim, int i, ParticleCacheKey *ca, int k, int steps, float *UNUSED(rootco), float effector, float UNUSED(dfra), float UNUSED(cfra), float *length, float *vec) { float force[3] = {0.0f, 0.0f, 0.0f}; @@ -2068,53 +1735,26 @@ static void psys_task_init_path(ParticleTask *task, ParticleSimulationData *sim) task->rng_path = BLI_rng_new(seed); } -/* note: this function must be thread safe, except for branching! */ -static void psys_thread_create_path(ParticleTask *task, struct ChildParticle *cpa, ParticleCacheKey *child_keys, int i) +static void psys_calc_child_parent_weights(ParticleTask *task, struct ChildParticle *cpa, + float orco[3], float ornor[3], float hairmat[4][4], int *cpa_num, float **cpa_fuv, short *cpa_from, + ParticleCacheKey *key[4], float weight[4], float offset[4][3]) { ParticleThreadContext *ctx = task->ctx; Object *ob = ctx->sim.ob; ParticleSystem *psys = ctx->sim.psys; ParticleSettings *part = psys->part; - ParticleCacheKey **cache = psys->childcache; ParticleCacheKey **pcache = psys_in_edit_mode(ctx->sim.scene, psys) && psys->edit ? psys->edit->pathcache : psys->pathcache; - ParticleCacheKey *child, *key[4]; - ParticleTexture ptex; - float *cpa_fuv = 0, *par_rot = 0, rot[4]; - float orco[3], ornor[3], hairmat[4][4], dvec[3], off1[4][3], off2[4][3]; - float eff_length, eff_vec[3], weight[4]; - int k, cpa_num; - short cpa_from; - - if (!pcache) - return; - + if (ctx->between) { ParticleData *pa = psys->particles + cpa->pa[0]; - int w, needupdate; - float foffset, wsum = 0.f; + int w; + float wsum = 0.f; float co[3]; float p_min = part->parting_min; float p_max = part->parting_max; /* Virtual parents don't work nicely with parting. */ float p_fac = part->parents > 0.f ? 0.f : part->parting_fac; - if (ctx->editupdate) { - needupdate = 0; - w = 0; - while (w < 4 && cpa->pa[w] >= 0) { - if (psys->edit->points[cpa->pa[w]].flag & PEP_EDIT_RECALC) { - needupdate = 1; - break; - } - w++; - } - - if (!needupdate) - return; - else - memset(child_keys, 0, sizeof(*child_keys) * (ctx->segments + 1)); - } - /* get parent paths */ for (w = 0; w < 4; w++) { if (cpa->pa[w] >= 0) { @@ -2128,9 +1768,9 @@ static void psys_thread_create_path(ParticleTask *task, struct ChildParticle *cp } /* modify weights to create parting */ - if (p_fac > 0.f) { + if (p_fac > 0.f && key[0]->segments != -1) { for (w = 0; w < 4; w++) { - if (w && weight[w] > 0.f) { + if (w && weight[w] > 0.f && key[w]->segments != -1) { float d; if (part->flag & PART_CHILD_LONG_HAIR) { /* For long hair use tip distance/root distance as parting factor instead of root to tip angle. */ @@ -2168,46 +1808,83 @@ static void psys_thread_create_path(ParticleTask *task, struct ChildParticle *cp } /* get the original coordinates (orco) for texture usage */ - cpa_num = cpa->num; - - foffset = cpa->foffset; - cpa_fuv = cpa->fuv; - cpa_from = PART_FROM_FACE; + *cpa_from = PART_FROM_FACE; + *cpa_num = cpa->num; + *cpa_fuv = cpa->fuv; - psys_particle_on_emitter(ctx->sim.psmd, cpa_from, cpa_num, DMCACHE_ISCHILD, cpa->fuv, foffset, co, ornor, 0, 0, orco, 0); + psys_particle_on_emitter(ctx->sim.psmd, *cpa_from, *cpa_num, DMCACHE_ISCHILD, *cpa_fuv, cpa->foffset, co, ornor, 0, 0, orco, 0); mul_m4_v3(ob->obmat, co); for (w = 0; w < 4; w++) - sub_v3_v3v3(off1[w], co, key[w]->co); + sub_v3_v3v3(offset[w], co, key[w]->co); psys_mat_hair_to_global(ob, ctx->sim.psmd->dm, psys->part->from, pa, hairmat); } else { ParticleData *pa = psys->particles + cpa->parent; float co[3]; - if (ctx->editupdate) { - if (!(psys->edit->points[cpa->parent].flag & PEP_EDIT_RECALC)) - return; - - memset(child_keys, 0, sizeof(*child_keys) * (ctx->segments + 1)); - } /* get the parent path */ key[0] = pcache[cpa->parent]; /* get the original coordinates (orco) for texture usage */ - cpa_from = part->from; - cpa_num = pa->num; + *cpa_from = part->from; + *cpa_num = pa->num; /* XXX hack to avoid messed up particle num and subsequent crash (#40733) */ - if (cpa_num > ctx->sim.psmd->dm->getNumTessFaces(ctx->sim.psmd->dm)) - cpa_num = 0; - cpa_fuv = pa->fuv; + if (*cpa_num > ctx->sim.psmd->dm->getNumTessFaces(ctx->sim.psmd->dm)) + *cpa_num = 0; + *cpa_fuv = pa->fuv; - psys_particle_on_emitter(ctx->sim.psmd, cpa_from, cpa_num, DMCACHE_ISCHILD, cpa_fuv, pa->foffset, co, ornor, 0, 0, orco, 0); + psys_particle_on_emitter(ctx->sim.psmd, *cpa_from, *cpa_num, DMCACHE_ISCHILD, *cpa_fuv, pa->foffset, co, ornor, 0, 0, orco, 0); psys_mat_hair_to_global(ob, ctx->sim.psmd->dm, psys->part->from, pa, hairmat); } +} + +/* note: this function must be thread safe, except for branching! */ +static void psys_thread_create_path(ParticleTask *task, struct ChildParticle *cpa, ParticleCacheKey *child_keys, int i) +{ + ParticleThreadContext *ctx = task->ctx; + ParticleSystem *psys = ctx->sim.psys; + ParticleSettings *part = psys->part; + ParticleCacheKey **cache = psys->childcache; + ParticleCacheKey **pcache = psys_in_edit_mode(ctx->sim.scene, psys) && psys->edit ? psys->edit->pathcache : psys->pathcache; + ParticleCacheKey *child, *key[4]; + ParticleTexture ptex; + float *cpa_fuv = 0, *par_rot = 0, rot[4]; + float orco[3], ornor[3], hairmat[4][4], dvec[3], off1[4][3], off2[4][3]; + float eff_length, eff_vec[3], weight[4]; + int k, cpa_num; + short cpa_from; + + if (!pcache) + return; + + if (ctx->editupdate) { + bool need_update; + + if (ctx->between) { + int w; + need_update = false; + for (w = 0; w < 4 && cpa->pa[w] >= 0; ++w) { + if (psys->edit->points[cpa->pa[w]].flag & PEP_EDIT_RECALC) { + need_update = true; + break; + } + } + } + else { + need_update = psys->edit->points[cpa->parent].flag & PEP_EDIT_RECALC; + } + + if (need_update) + memset(child_keys, 0, sizeof(*child_keys) * (ctx->segments + 1)); + else + return; + } + + psys_calc_child_parent_weights(task, cpa, orco, ornor, hairmat, &cpa_num, &cpa_fuv, &cpa_from, key, weight, off1); child_keys->segments = ctx->segments; @@ -2221,6 +1898,8 @@ static void psys_thread_create_path(ParticleTask *task, struct ChildParticle *cp /* create the child path */ for (k = 0, child = child_keys; k <= ctx->segments; k++, child++) { + child->hull_parent = cpa->hull_parent; + if (ctx->between) { int w = 0; @@ -2453,11 +2132,11 @@ void psys_cache_paths(ParticleSimulationData *sim, float cfra) ParticleSystem *psys = sim->psys; ParticleSettings *part = psys->part; ParticleCacheKey *ca, **cache; + const bool keyed = psys->flag & PSYS_KEYED; + const bool baked = psys->pointcache->mem_cache.first && psys->part->type != PART_HAIR; DerivedMesh *hair_dm = (psys->part->type == PART_HAIR && psys->flag & PSYS_HAIR_DYNAMICS) ? psys->hair_out_dm : NULL; - ParticleKey result; - Material *ma; ParticleInterpolationData pind; ParticleTexture ptex; @@ -2475,7 +2154,6 @@ void psys_cache_paths(ParticleSimulationData *sim, float cfra) float length, vec[3]; float *vg_effector = NULL; float *vg_length = NULL, pa_length = 1.0f; - int keyed, baked; /* we don't have anything valid to create paths from so let's quit here */ if ((psys->flag & PSYS_HAIR_DONE || psys->flag & PSYS_KEYED || psys->pointcache) == 0) @@ -2485,9 +2163,6 @@ void psys_cache_paths(ParticleSimulationData *sim, float cfra) if (psys->renderdata == 0 && (psys->edit == NULL || pset->flag & PE_DRAW_PART) == 0) return; - keyed = psys->flag & PSYS_KEYED; - baked = psys->pointcache->mem_cache.first && psys->part->type != PART_HAIR; - /* clear out old and create new empty path cache */ psys_free_path_cache(psys, psys->edit); cache = psys->pathcache = psys_alloc_path_cache_buffers(&psys->pathcachebufs, totpart, segments + 1); @@ -2557,6 +2232,7 @@ void psys_cache_paths(ParticleSimulationData *sim, float cfra) /*--interpolate actual path from data points--*/ for (k = 0, ca = cache[p]; k <= segments; k++, ca++) { + ParticleKey result; time = (float)k / (float)segments; t = birthtime + time * (dietime - birthtime); result.time = -t; @@ -2573,14 +2249,6 @@ void psys_cache_paths(ParticleSimulationData *sim, float cfra) copy_v3_v3(ca->col, col); } - if (part->type == PART_HAIR) { - HairKey *hkey; - - for (k = 0, hkey = pa->hair; k < pa->totkey; ++k, ++hkey) { - mul_v3_m4v3(hkey->world_co, hairmat, hkey->co); - } - } - /*--modify paths and calculate rotation & velocity--*/ if (!(psys->flag & PSYS_GLOBAL_HAIR)) { @@ -3011,6 +2679,25 @@ void psys_mat_hair_to_global(Object *ob, DerivedMesh *dm, short from, ParticleDa mul_m4_m4m4(hairmat, ob->obmat, facemat); } +void psys_child_mat_to_object(Object *ob, ParticleSystem *psys, ParticleSystemModifierData *psmd, ChildParticle *cpa, float hairmat[4][4]) +{ + const bool between = (psys->part->childtype == PART_CHILD_FACES); + float co[3]; + + if (between) { + ParticleData *pa = &psys->particles[cpa->pa[0]]; + psys_particle_on_emitter(psmd, PART_FROM_FACE, cpa->num, DMCACHE_ISCHILD, cpa->fuv, cpa->foffset, co, NULL, NULL, NULL, NULL, NULL); + psys_mat_hair_to_object(ob, psmd->dm, psys->part->from, pa, hairmat); + } + else { + ParticleData *pa = &psys->particles[cpa->parent]; + psys_particle_on_emitter(psmd, psys->part->from, pa->num, pa->num_dmcache, pa->fuv, pa->foffset, co, NULL, NULL, NULL, NULL, NULL); + psys_mat_hair_to_object(ob, psmd->dm, psys->part->from, pa, hairmat); + } + + copy_v3_v3(hairmat[3], co); +} + /************************************************/ /* ParticleSettings handling */ /************************************************/ @@ -3032,6 +2719,8 @@ ModifierData *object_add_particle_system(Scene *scene, Object *ob, const char *n BLI_addtail(&ob->particlesystem, psys); psys->part = psys_new_settings(DATA_("ParticleSettings"), NULL); + psys->key = BKE_key_add_particles(ob, psys); + psys->key->type = KEY_RELATIVE; if (BLI_listbase_count_ex(&ob->particlesystem, 2) > 1) BLI_snprintf(psys->name, sizeof(psys->name), DATA_("ParticleSystem %i"), BLI_listbase_count(&ob->particlesystem)); @@ -3054,6 +2743,8 @@ ModifierData *object_add_particle_system(Scene *scene, Object *ob, const char *n psys->flag = PSYS_CURRENT; psys->cfra = BKE_scene_frame_get_from_ctime(scene, CFRA + 1); + psys->hair_preview_factor = 100.0f; + DAG_relations_tag_update(G.main); DAG_id_tag_update(&ob->id, OB_RECALC_DATA); @@ -3095,7 +2786,7 @@ void object_remove_particle_system(Scene *UNUSED(scene), Object *ob) if (ob->particlesystem.first) ((ParticleSystem *) ob->particlesystem.first)->flag |= PSYS_CURRENT; else - ob->mode &= ~OB_MODE_PARTICLE_EDIT; + ob->mode &= ~(OB_MODE_PARTICLE_EDIT | OB_MODE_HAIR_EDIT); DAG_relations_tag_update(G.main); DAG_id_tag_update(&ob->id, OB_RECALC_DATA); @@ -3372,6 +3063,11 @@ static int get_particle_uv(DerivedMesh *dm, ParticleData *pa, int face_index, co pvalue = texture_value_blend(def, pvalue, value, texfac, blend); \ } (void)0 +#define SET_PARTICLE_TEXTURE_RGB(type, prgb, texfac) \ + if ((event & mtex->mapto) & type) { \ + texture_rgb_blend(prgb, rgba, prgb, value, texfac, blend); \ + } (void)0 + #define CLAMP_PARTICLE_TEXTURE_POS(type, pvalue) \ if (event & type) { \ if (pvalue < 0.0f) \ @@ -3384,6 +3080,21 @@ static int get_particle_uv(DerivedMesh *dm, ParticleData *pa, int face_index, co CLAMP(pvalue, -1.0f, 1.0f); \ } (void)0 +#define CLAMP_PARTICLE_TEXTURE_RGB(type, prgb) \ + if (event & type) { \ + CLAMP3(prgb, 0.0f, 1.0f); \ + } (void)0 + +/* actual usable texco mode for particles */ +BLI_INLINE int particle_texco(ParticleSettings *part, MTex *mtex) +{ + short texco = mtex->texco; + if (ELEM(texco, TEXCO_UV, TEXCO_ORCO) && + (!ELEM(part->from, PART_FROM_FACE, PART_FROM_VOLUME) || part->distr == PART_DISTR_GRID)) + texco = TEXCO_GLOB; + return texco; +} + static void get_cpa_texture(DerivedMesh *dm, ParticleSystem *psys, ParticleSettings *part, ParticleData *par, int child_index, int face_index, const float fw[4], float *orco, ParticleTexture *ptex, int event, float cfra) { MTex *mtex, **mtexp = part->mtex; @@ -3393,6 +3104,7 @@ static void get_cpa_texture(DerivedMesh *dm, ParticleSystem *psys, ParticleSetti ptex->ivel = ptex->life = ptex->exist = ptex->size = ptex->damp = ptex->gravity = ptex->field = ptex->time = ptex->clump = ptex->kink_freq = ptex->kink_amp = ptex->effector = ptex->rough1 = ptex->rough2 = ptex->roughe = 1.0f; + ptex->color[0] = ptex->color[1] = ptex->color[2] = 1.0f; ptex->length = 1.0f - part->randlength * psys_frand(psys, child_index + 26); ptex->length *= part->clength_thres < psys_frand(psys, child_index + 27) ? part->clength : 1.0f; @@ -3402,10 +3114,7 @@ static void get_cpa_texture(DerivedMesh *dm, ParticleSystem *psys, ParticleSetti if (mtex && mtex->tex && mtex->mapto) { float def = mtex->def_var; short blend = mtex->blendtype; - short texco = mtex->texco; - - if (ELEM(texco, TEXCO_UV, TEXCO_ORCO) && (ELEM(part->from, PART_FROM_FACE, PART_FROM_VOLUME) == 0 || part->distr == PART_DISTR_GRID)) - texco = TEXCO_GLOB; + short texco = particle_texco(part, mtex); switch (texco) { case TEXCO_GLOB: @@ -3441,6 +3150,7 @@ static void get_cpa_texture(DerivedMesh *dm, ParticleSystem *psys, ParticleSetti SET_PARTICLE_TEXTURE(PAMAP_KINK_AMP, ptex->kink_amp, mtex->kinkampfac); SET_PARTICLE_TEXTURE(PAMAP_KINK_FREQ, ptex->kink_freq, mtex->kinkfac); SET_PARTICLE_TEXTURE(PAMAP_DENS, ptex->exist, mtex->padensfac); + SET_PARTICLE_TEXTURE_RGB(PAMAP_COLOR, ptex->color, mtex->pacolfac); } } @@ -3450,71 +3160,87 @@ static void get_cpa_texture(DerivedMesh *dm, ParticleSystem *psys, ParticleSetti CLAMP_PARTICLE_TEXTURE_POS(PAMAP_KINK_FREQ, ptex->kink_freq); CLAMP_PARTICLE_TEXTURE_POS(PAMAP_ROUGH, ptex->rough1); CLAMP_PARTICLE_TEXTURE_POS(PAMAP_DENS, ptex->exist); + CLAMP_PARTICLE_TEXTURE_RGB(PAMAP_COLOR, ptex->color); } -void psys_get_texture(ParticleSimulationData *sim, ParticleData *pa, ParticleTexture *ptex, int event, float cfra) + +bool particle_mtex_eval(ParticleSimulationData *sim, MTex *mtex, ParticleData *pa, float cfra, float *value, float rgba[4]) { Object *ob = sim->ob; Mesh *me = (Mesh *)ob->data; ParticleSettings *part = sim->psys->part; + short texco; + + float texvec[3]; + + if (!(mtex->tex && mtex->mapto)) + return false; + + texco = particle_texco(part, mtex); + switch (texco) { + case TEXCO_GLOB: + copy_v3_v3(texvec, pa->state.co); + break; + case TEXCO_OBJECT: + copy_v3_v3(texvec, pa->state.co); + if (mtex->object) + mul_m4_v3(mtex->object->imat, texvec); + break; + case TEXCO_UV: + if (get_particle_uv(sim->psmd->dm, pa, 0, pa->fuv, mtex->uvname, texvec)) + break; + /* no break, failed to get uv's, so let's try orco's */ + case TEXCO_ORCO: { + float co[3]; + + psys_particle_on_emitter(sim->psmd, sim->psys->part->from, pa->num, pa->num_dmcache, pa->fuv, pa->foffset, co, 0, 0, 0, texvec, 0); + + if (me->bb == NULL || (me->bb->flag & BOUNDBOX_DIRTY)) { + BKE_mesh_texspace_calc(me); + } + sub_v3_v3(texvec, me->loc); + if (me->size[0] != 0.0f) texvec[0] /= me->size[0]; + if (me->size[1] != 0.0f) texvec[1] /= me->size[1]; + if (me->size[2] != 0.0f) texvec[2] /= me->size[2]; + break; + } + case TEXCO_PARTICLE: + /* texture coordinates in range [-1, 1] */ + texvec[0] = 2.f * (cfra - pa->time) / (pa->dietime - pa->time) - 1.f; + if (sim->psys->totpart > 0) + texvec[1] = 2.f * (float)(pa - sim->psys->particles) / (float)sim->psys->totpart - 1.f; + else + texvec[1] = 0.0f; + texvec[2] = 0.f; + break; + } + + externtex(mtex, texvec, value, rgba, rgba + 1, rgba + 2, rgba + 3, 0, NULL, false); + + return true; +} + +void psys_get_texture(ParticleSimulationData *sim, ParticleData *pa, ParticleTexture *ptex, int event, float cfra) +{ + ParticleSettings *part = sim->psys->part; MTex **mtexp = part->mtex; MTex *mtex; int m; - float value, rgba[4], co[3], texvec[3]; + float value, rgba[4]; int setvars = 0; /* initialize ptex */ ptex->ivel = ptex->life = ptex->exist = ptex->size = ptex->damp = ptex->gravity = ptex->field = ptex->length = ptex->clump = ptex->kink_freq = ptex->kink_amp = ptex->effector = ptex->rough1 = ptex->rough2 = ptex->roughe = 1.0f; + ptex->color[0] = ptex->color[1] = ptex->color[2] = 1.0f; ptex->time = (float)(pa - sim->psys->particles) / (float)sim->psys->totpart; for (m = 0; m < MAX_MTEX; m++, mtexp++) { mtex = *mtexp; - if (mtex && mtex->tex && mtex->mapto) { + if (mtex && particle_mtex_eval(sim, mtex, pa, cfra, &value, rgba)) { float def = mtex->def_var; short blend = mtex->blendtype; - short texco = mtex->texco; - - if (texco == TEXCO_UV && (ELEM(part->from, PART_FROM_FACE, PART_FROM_VOLUME) == 0 || part->distr == PART_DISTR_GRID)) - texco = TEXCO_GLOB; - - switch (texco) { - case TEXCO_GLOB: - copy_v3_v3(texvec, pa->state.co); - break; - case TEXCO_OBJECT: - copy_v3_v3(texvec, pa->state.co); - if (mtex->object) - mul_m4_v3(mtex->object->imat, texvec); - break; - case TEXCO_UV: - if (get_particle_uv(sim->psmd->dm, pa, 0, pa->fuv, mtex->uvname, texvec)) - break; - /* no break, failed to get uv's, so let's try orco's */ - case TEXCO_ORCO: - psys_particle_on_emitter(sim->psmd, sim->psys->part->from, pa->num, pa->num_dmcache, pa->fuv, pa->foffset, co, 0, 0, 0, texvec, 0); - - if (me->bb == NULL || (me->bb->flag & BOUNDBOX_DIRTY)) { - BKE_mesh_texspace_calc(me); - } - sub_v3_v3(texvec, me->loc); - if (me->size[0] != 0.0f) texvec[0] /= me->size[0]; - if (me->size[1] != 0.0f) texvec[1] /= me->size[1]; - if (me->size[2] != 0.0f) texvec[2] /= me->size[2]; - break; - case TEXCO_PARTICLE: - /* texture coordinates in range [-1, 1] */ - texvec[0] = 2.f * (cfra - pa->time) / (pa->dietime - pa->time) - 1.f; - if (sim->psys->totpart > 0) - texvec[1] = 2.f * (float)(pa - sim->psys->particles) / (float)sim->psys->totpart - 1.f; - else - texvec[1] = 0.0f; - texvec[2] = 0.f; - break; - } - - externtex(mtex, texvec, &value, rgba, rgba + 1, rgba + 2, rgba + 3, 0, NULL, false); if ((event & mtex->mapto) & PAMAP_TIME) { /* the first time has to set the base value for time regardless of blend mode */ @@ -3536,6 +3262,7 @@ void psys_get_texture(ParticleSimulationData *sim, ParticleData *pa, ParticleTex SET_PARTICLE_TEXTURE(PAMAP_GRAVITY, ptex->gravity, mtex->gravityfac); SET_PARTICLE_TEXTURE(PAMAP_DAMP, ptex->damp, mtex->dampfac); SET_PARTICLE_TEXTURE(PAMAP_LENGTH, ptex->length, mtex->lengthfac); + SET_PARTICLE_TEXTURE_RGB(PAMAP_COLOR, ptex->color, mtex->pacolfac); } } @@ -3548,7 +3275,37 @@ void psys_get_texture(ParticleSimulationData *sim, ParticleData *pa, ParticleTex CLAMP_PARTICLE_TEXTURE_POSNEG(PAMAP_GRAVITY, ptex->gravity); CLAMP_PARTICLE_TEXTURE_POS(PAMAP_DAMP, ptex->damp); CLAMP_PARTICLE_TEXTURE_POS(PAMAP_LENGTH, ptex->length); + CLAMP_PARTICLE_TEXTURE_RGB(PAMAP_COLOR, ptex->color); } + +/* specialized texture eval for shapekey influences */ +float psys_get_texture_shapefac(ParticleSimulationData *sim, ParticleData *pa, float cfra, const char *shapekey) +{ + ParticleSettings *part = sim->psys->part; + MTex **mtexp = part->mtex; + MTex *mtex; + int m; + float value, rgba[4]; + + float result = 1.0f; + + for (m = 0; m < MAX_MTEX; m++, mtexp++) { + mtex = *mtexp; + if (mtex && (mtex->mapto & PAMAP_SHAPEKEY) && STREQ(mtex->shapekey, shapekey)) { + float def = mtex->def_var; + short blend = mtex->blendtype; + + if (particle_mtex_eval(sim, mtex, pa, cfra, &value, rgba)) { + result = texture_value_blend(def, result, value, mtex->shapefac, blend); + } + } + } + + CLAMP(result, 0.0f, 1.0f); + + return result; +} + /************************************************/ /* Particle State */ /************************************************/ @@ -3628,6 +3385,35 @@ static void get_child_modifier_parameters(ParticleSettings *part, ParticleThread if (ctx->vg_effector) ptex->effector *= psys_interpolate_value_from_verts(ctx->dm, cpa_from, cpa_num, cpa_fuv, ctx->vg_effector); } + +static void interpolate_pathcache(ParticleCacheKey *first, float t, ParticleCacheKey *result) +{ + int i = 0; + ParticleCacheKey *cur = first; + + /* scale the requested time to fit the entire path even if the path is cut early */ + t *= (first + first->segments)->time; + + while (i < first->segments && cur->time < t) + cur++; + + if (cur->time == t) + *result = *cur; + else { + float dt = (t - (cur - 1)->time) / (cur->time - (cur - 1)->time); + interp_v3_v3v3(result->co, (cur - 1)->co, cur->co, dt); + interp_v3_v3v3(result->vel, (cur - 1)->vel, cur->vel, dt); + interp_qt_qtqt(result->rot, (cur - 1)->rot, cur->rot, dt); + result->time = t; + } + + /* first is actual base rotation, others are incremental from first */ + if (cur == first || cur - 1 == first) + copy_qt_qt(result->rot, first->rot); + else + mul_qt_qtqt(result->rot, first->rot, result->rot); +} + /* get's hair (or keyed) particles state at the "path time" specified in state->time */ void psys_get_particle_on_path(ParticleSimulationData *sim, int p, ParticleKey *state, const bool vel) { diff --git a/source/blender/blenkernel/intern/particle_child.c b/source/blender/blenkernel/intern/particle_child.c index 7b2e07ea96f..1ccb5574df7 100644 --- a/source/blender/blenkernel/intern/particle_child.c +++ b/source/blender/blenkernel/intern/particle_child.c @@ -41,8 +41,8 @@ struct Material; void do_kink(ParticleKey *state, const float par_co[3], const float par_vel[3], const float par_rot[4], float time, float freq, float shape, float amplitude, float flat, short type, short axis, float obmat[4][4], int smooth_start); -float do_clump(ParticleKey *state, const float par_co[3], float time, const float orco_offset[3], float clumpfac, float clumppow, float pa_clump, - bool use_clump_noise, float clump_noise_size, CurveMapping *clumpcurve); +float do_clump(ParticleKey *state, const float par_co[3], const float par_orco[3], float time, const float orco[3], float clumpfac, float clumppow, float pa_clump, + bool use_clump_noise, float clump_noise_size, CurveMapping *clumpcurve, float clump_noise_random, float clump_noise_random_size, float mat[4][4]); void do_child_modifiers(ParticleSimulationData *sim, ParticleTexture *ptex, const float par_co[3], const float par_vel[3], const float par_rot[4], const float par_orco[3], ChildParticle *cpa, const float orco[3], float mat[4][4], ParticleKey *state, float t); @@ -584,48 +584,64 @@ static float do_clump_level(float result[3], const float co[3], const float par_ return clump; } -float do_clump(ParticleKey *state, const float par_co[3], float time, const float orco_offset[3], float clumpfac, float clumppow, float pa_clump, - bool use_clump_noise, float clump_noise_size, CurveMapping *clumpcurve) +BLI_INLINE void simple_roughness(float mat[4][4], float size, float factor, const float loc[3], float time, float result[3]) +{ + float turbloc[3], rough[3]; + + mul_v3_v3fl(turbloc, loc, time); + rough[0] = -1.0f + 2.0f * BLI_gTurbulence(size, turbloc[0], turbloc[1], turbloc[2], 2, 0, 2); + rough[1] = -1.0f + 2.0f * BLI_gTurbulence(size, turbloc[1], turbloc[2], turbloc[0], 2, 0, 2); + rough[2] = -1.0f + 2.0f * BLI_gTurbulence(size, turbloc[2], turbloc[0], turbloc[1], 2, 0, 2); + + madd_v3_v3fl(result, mat[0], factor * rough[0]); + madd_v3_v3fl(result, mat[1], factor * rough[1]); + madd_v3_v3fl(result, mat[2], factor * rough[2]); +} + +float do_clump(ParticleKey *state, const float par_co[3], const float par_orco[3], float time, const float orco[3], float clumpfac, float clumppow, float pa_clump, + bool use_clump_noise, float clump_noise_size, CurveMapping *clumpcurve, float clump_noise_random, float clump_noise_random_size, float mat[4][4]) { float clump; + float rough_offset[3]; + + zero_v3(rough_offset); if (use_clump_noise && clump_noise_size != 0.0f) { - float center[3], noisevec[3]; + float center_orco[3], center[3], noisevec[3], orco_offset[3]; float da[4], pa[12]; + sub_v3_v3v3(orco_offset, orco, par_orco); mul_v3_v3fl(noisevec, orco_offset, 1.0f / clump_noise_size); voronoi(noisevec[0], noisevec[1], noisevec[2], da, pa, 1.0f, 0); mul_v3_fl(&pa[0], clump_noise_size); - add_v3_v3v3(center, par_co, &pa[0]); + if (clump_noise_random != 0.0f && mat) { + add_v3_v3v3(center_orco, par_orco, &pa[0]); + simple_roughness(mat, clump_noise_random_size, clump_noise_random, center_orco, time, rough_offset); + } + + add_v3_v3v3(center, par_co, &pa[0]); do_clump_level(state->co, state->co, center, time, clumpfac, clumppow, pa_clump, clumpcurve); } clump = do_clump_level(state->co, state->co, par_co, time, clumpfac, clumppow, pa_clump, clumpcurve); + if (use_clump_noise && clump_noise_size != 0.0f && clump_noise_random != 0.0f && mat) { + add_v3_v3(state->co, rough_offset); + } + return clump; } static void do_rough(const float loc[3], float mat[4][4], float t, float fac, float size, float thres, ParticleKey *state) { - float rough[3]; - float rco[3]; - if (thres != 0.0f) { if (fabsf((float)(-1.5f + loc[0] + loc[1] + loc[2])) < 1.5f * thres) { return; } } - copy_v3_v3(rco, loc); - mul_v3_fl(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); - - madd_v3_v3fl(state->co, mat[0], fac * rough[0]); - madd_v3_v3fl(state->co, mat[1], fac * rough[1]); - madd_v3_v3fl(state->co, mat[2], fac * rough[2]); + simple_roughness(mat, size, fac, loc, t, state->co); } static void do_rough_end(const float loc[3], float mat[4][4], float t, float fac, float shape, ParticleKey *state) @@ -645,23 +661,12 @@ static void do_rough_end(const float loc[3], float mat[4][4], float t, float fac static void do_rough_curve(const float loc[3], float mat[4][4], float time, float fac, float size, CurveMapping *roughcurve, ParticleKey *state) { - float rough[3]; - float rco[3]; - if (!roughcurve) return; fac *= CLAMPIS(curvemapping_evaluateF(roughcurve, 0, time), 0.0f, 1.0f); - copy_v3_v3(rco, loc); - mul_v3_fl(rco, time); - 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); - - madd_v3_v3fl(state->co, mat[0], fac * rough[0]); - madd_v3_v3fl(state->co, mat[1], fac * rough[1]); - madd_v3_v3fl(state->co, mat[2], fac * rough[2]); + simple_roughness(mat, size, fac, loc, time, state->co); } void do_child_modifiers(ParticleSimulationData *sim, ParticleTexture *ptex, const float par_co[3], const float par_vel[3], const float par_rot[4], const float par_orco[3], @@ -694,12 +699,11 @@ void do_child_modifiers(ParticleSimulationData *sim, ParticleTexture *ptex, cons guided = do_guides(sim->psys->part, sim->psys->effectors, (ParticleKey *)state, cpa->parent, t); if (guided == 0) { - float orco_offset[3]; float clump; - sub_v3_v3v3(orco_offset, orco, par_orco); - clump = do_clump(state, par_co, t, orco_offset, part->clumpfac, part->clumppow, ptex ? ptex->clump : 1.f, - part->child_flag & PART_CHILD_USE_CLUMP_NOISE, part->clump_noise_size, clumpcurve); + clump = do_clump(state, par_co, par_orco, t, orco, part->clumpfac, part->clumppow, ptex ? ptex->clump : 1.f, + part->child_flag & PART_CHILD_USE_CLUMP_NOISE, part->clump_noise_size, clumpcurve, + part->clump_noise_random, part->clump_noise_random_size, mat); if (kink_freq != 0.f) { kink_amp *= (1.f - kink_amp_clump * clump); diff --git a/source/blender/blenkernel/intern/particle_distribute.c b/source/blender/blenkernel/intern/particle_distribute.c index 50634460028..13bb7c65025 100644 --- a/source/blender/blenkernel/intern/particle_distribute.c +++ b/source/blender/blenkernel/intern/particle_distribute.c @@ -32,6 +32,7 @@ */ #include <string.h> +#include <stdlib.h> #include "MEM_guardedalloc.h" @@ -1153,6 +1154,183 @@ static void distribute_particles_on_shape(ParticleSimulationData *sim, int UNUSE fprintf(stderr,"Shape emission not yet possible!\n"); } +#ifdef USE_PARTICLE_HULL_DRAWING +/* placeholder for child particle sorting, storing emitter hair-space offset */ +typedef struct ChildParticleSort { + int index; + float x, y; + int parent; + bool is_hull; +} ChildParticleSort; + +/* comparison function for sorting children. + * primary criterion is the childrens' primary parent + * secondary criterion is the combined offset from the parent in the tangential plane, + * which is needed for further constructing the convex hull of children around each parent + */ +static int psys_child_cmp(const void *a, const void *b) +{ + const ChildParticleSort *cpa_a = a, *cpa_b = b; + + if (UNLIKELY(cpa_a->parent == cpa_b->parent)) { + if (UNLIKELY(cpa_a->x == cpa_b->x)) + return cpa_a->y < cpa_b->y ? -1 : 1; + else + return cpa_a->x < cpa_b->x ? -1 : 1; + } + else + return cpa_a->parent < cpa_b->parent ? -1 : 1; +} + +BLI_INLINE bool ccw(const ChildParticleSort *p1, const ChildParticleSort *p2, const ChildParticleSort *p3) +{ + return (p2->x - p1->x) * (p3->y - p1->y) - (p2->y - p1->y) * (p3->x - p1->x) > 0.0f; +} + +static int make_convex_child_hull(ChildParticleSort *childdata, int totchild, ChildParticleSort **hull) +{ + const int parent = childdata->parent; + + int tothull, upper_start; + int i; + + tothull = 0; + /* lower hull */ + for (i = 0; i < totchild; ++i) { + ChildParticleSort *cdata = &childdata[i]; + /* limit to the parent group */ + if (cdata->parent != parent) + break; + + while (tothull >= 2 && !ccw(hull[tothull-2], hull[tothull-1], cdata)) + --tothull; + hull[tothull++] = cdata; + } + /* this is the actual size of the child group sharing the same parent */ + totchild = i; + + /* upper hull */ + upper_start = tothull + 1; + for (i = totchild-2; i >= 0; --i) { + ChildParticleSort *cdata = &childdata[i]; + + while (tothull >= upper_start && !ccw(hull[tothull-2], hull[tothull-1], cdata)) + --tothull; + hull[tothull++] = cdata; + } + + return tothull - 1; +} + +BLI_INLINE int count_child_group(ChildParticleSort *data, int start, int totchild) +{ + const int parent = data[start].parent; + int i; + + i = start; + do { + ++i; + } while (i < totchild && data[i].parent == parent); + + return i - start; +} + +/* sort children by primary parent and relative emitter location, then calculate convex hulls */ +static void psys_sort_children(ParticleSimulationData *sim) +{ + ParticleSystem *psys = sim->psys; + ParticleSettings *part = psys->part; + const int totchild = psys->totchild; + const bool between = (part->childtype == PART_CHILD_FACES); + const int cpa_from = between ? PART_FROM_FACE : part->from; + + ChildParticleSort *sort; + int i; + + if (totchild == 0) + return; + if (!sim->psmd->dm) + return; + + sort = MEM_mallocN(sizeof(ChildParticleSort) * totchild, "child sorting data"); + + for (i = 0; i < totchild; ++i) { + ChildParticle *cpa = &psys->child[i]; + ChildParticleSort *cdata = &sort[i]; + + ParticleData *parent; + float co[3], hairmat[4][4], hairimat[4][4]; + + cdata->index = i; + cdata->parent = between ? cpa->pa[0] : cpa->parent; + + psys_particle_on_emitter(sim->psmd, cpa_from, cpa->num, DMCACHE_ISCHILD, cpa->fuv, cpa->foffset, co, NULL, NULL, NULL, NULL, NULL); + + parent = &psys->particles[cdata->parent]; + psys_mat_hair_to_global(sim->ob, sim->psmd->dm, psys->part->from, parent, hairmat); + invert_m4_m4(hairimat, hairmat); + + mul_m4_v3(hairimat, co); + cdata->x = co[0]; + cdata->y = co[1]; + + cdata->is_hull = false; + } + + qsort(sort, totchild, sizeof(ChildParticleSort), psys_child_cmp); + + /* Calculate the convex hull of children for each parent using Andrew's Monotone Chain algorithm + * http://en.wikibooks.org/wiki/Algorithm_Implementation/Geometry/Convex_hull/Monotone_chain + */ + { + ChildParticle *nchild = MEM_mallocN(sizeof(ChildParticle) * totchild, "sorted child particles"); + ChildParticle *ncpa = nchild; + + /* note: the algorithm requires one additional element in the buffer in worst case */ + ChildParticleSort **hull = MEM_mallocN(sizeof(ChildParticleSort*) * (totchild+1), "child convex hull points"); + + int groupstart = 0; + while (groupstart < totchild) { + int groupsize, tothull; + + groupsize = count_child_group(sort, groupstart, totchild); + BLI_assert(groupstart + groupsize <= totchild); + + tothull = make_convex_child_hull(sort + groupstart, groupsize, hull); + + for (i = 0; i < tothull; ++i) { + ChildParticleSort *cdata = hull[i]; + memcpy(ncpa, psys->child + cdata->index, sizeof(ChildParticle)); + ncpa->hull_parent = cdata->parent; + ++ncpa; + + /* tag actual hull children */ + cdata->is_hull = true; + } + + /* insert remaining child particles after the hull children */ + for (i = groupstart; i < groupstart + groupsize; ++i) { + ChildParticleSort *cdata = &sort[i]; + if (!cdata->is_hull) { + memcpy(ncpa, psys->child + cdata->index, sizeof(ChildParticle)); + ncpa->hull_parent = -1; + ++ncpa; + } + } + + groupstart += groupsize; + } + + MEM_freeN(hull); + + MEM_freeN(psys->child); + psys->child = nchild; + } + + MEM_freeN(sort); +} +#endif + void distribute_particles(ParticleSimulationData *sim, int from) { PARTICLE_PSMD; @@ -1167,6 +1345,11 @@ void distribute_particles(ParticleSimulationData *sim, int from) else distribute_particles_on_shape(sim, from); +#ifdef USE_PARTICLE_HULL_DRAWING + if (from == PART_FROM_CHILD) + psys_sort_children(sim); +#endif + if (distr_error) { distribute_invalid(sim->scene, sim->psys, from); diff --git a/source/blender/blenkernel/intern/particle_interpolate.c b/source/blender/blenkernel/intern/particle_interpolate.c new file mode 100644 index 00000000000..88b0adad665 --- /dev/null +++ b/source/blender/blenkernel/intern/particle_interpolate.c @@ -0,0 +1,382 @@ +/* + * ***** 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2007 by Janne Karhu. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): Lukas Toenne. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/blenkernel/intern/particle_interpolate.c + * \ingroup bke + */ + + +#include <stdlib.h> +#include <string.h> + +#include "MEM_guardedalloc.h" + +#include "DNA_key_types.h" +#include "DNA_particle_types.h" + +#include "BLI_utildefines.h" +#include "BLI_math.h" + +#include "BKE_cdderivedmesh.h" +#include "BKE_key.h" +#include "BKE_particle.h" +#include "BKE_pointcache.h" +#include "BKE_scene.h" + +void psys_interpolate_particle(short type, ParticleKey keys[4], float dt, ParticleKey *result, bool velocity) +{ + float t[4]; + + if (type < 0) { + interp_cubic_v3(result->co, result->vel, keys[1].co, keys[1].vel, keys[2].co, keys[2].vel, dt); + } + else { + key_curve_position_weights(dt, t, type); + + interp_v3_v3v3v3v3(result->co, keys[0].co, keys[1].co, keys[2].co, keys[3].co, t); + + if (velocity) { + float temp[3]; + + if (dt > 0.999f) { + key_curve_position_weights(dt - 0.001f, t, type); + interp_v3_v3v3v3v3(temp, keys[0].co, keys[1].co, keys[2].co, keys[3].co, t); + sub_v3_v3v3(result->vel, result->co, temp); + } + else { + key_curve_position_weights(dt + 0.001f, t, type); + interp_v3_v3v3v3v3(temp, keys[0].co, keys[1].co, keys[2].co, keys[3].co, t); + sub_v3_v3v3(result->vel, temp, result->co); + } + } + } +} + +float psys_get_dietime_from_cache(PointCache *cache, int index) +{ + PTCacheMem *pm; + int dietime = 10000000; /* some max value so that we can default to pa->time+lifetime */ + + for (pm = cache->mem_cache.last; pm; pm = pm->prev) { + if (BKE_ptcache_mem_index_find(pm, index) >= 0) + return (float)pm->frame; + } + + return (float)dietime; +} + +/* Assumes pointcache->mem_cache exists, so for disk cached particles call psys_make_temp_pointcache() before use */ +/* It uses ParticleInterpolationData->pm to store the current memory cache frame so it's thread safe. */ +static void get_pointcache_keys_for_time(Object *UNUSED(ob), PointCache *cache, PTCacheMem **cur, int index, float t, ParticleKey *key1, ParticleKey *key2) +{ + static PTCacheMem *pm = NULL; + int index1, index2; + + if (index < 0) { /* initialize */ + *cur = cache->mem_cache.first; + + if (*cur) + *cur = (*cur)->next; + } + else { + if (*cur) { + while (*cur && (*cur)->next && (float)(*cur)->frame < t) + *cur = (*cur)->next; + + pm = *cur; + + index2 = BKE_ptcache_mem_index_find(pm, index); + index1 = BKE_ptcache_mem_index_find(pm->prev, index); + + BKE_ptcache_make_particle_key(key2, index2, pm->data, (float)pm->frame); + if (index1 < 0) + copy_particle_key(key1, key2, 1); + else + BKE_ptcache_make_particle_key(key1, index1, pm->prev->data, (float)pm->prev->frame); + } + else if (cache->mem_cache.first) { + pm = cache->mem_cache.first; + index2 = BKE_ptcache_mem_index_find(pm, index); + BKE_ptcache_make_particle_key(key2, index2, pm->data, (float)pm->frame); + copy_particle_key(key1, key2, 1); + } + } +} + +static int get_pointcache_times_for_particle(PointCache *cache, int index, float *start, float *end) +{ + PTCacheMem *pm; + int ret = 0; + + for (pm = cache->mem_cache.first; pm; pm = pm->next) { + if (BKE_ptcache_mem_index_find(pm, index) >= 0) { + *start = pm->frame; + ret++; + break; + } + } + + for (pm = cache->mem_cache.last; pm; pm = pm->prev) { + if (BKE_ptcache_mem_index_find(pm, index) >= 0) { + *end = pm->frame; + ret++; + break; + } + } + + return ret == 2; +} + +static void edit_to_particle(ParticleKey *key, PTCacheEditKey *ekey) +{ + copy_v3_v3(key->co, ekey->co); + if (ekey->vel) { + copy_v3_v3(key->vel, ekey->vel); + } + key->time = *(ekey->time); +} + +static void hair_to_particle(ParticleKey *key, HairKey *hkey) +{ + copy_v3_v3(key->co, hkey->co); + key->time = hkey->time; +} + +static void mvert_to_particle(ParticleKey *key, MVert *mvert, HairKey *hkey) +{ + copy_v3_v3(key->co, mvert->co); + key->time = hkey->time; +} + +void init_particle_interpolation(Object *ob, ParticleSystem *psys, ParticleData *pa, ParticleInterpolationData *pind) +{ + + if (pind->epoint) { + PTCacheEditPoint *point = pind->epoint; + + pind->ekey[0] = point->keys; + pind->ekey[1] = point->totkey > 1 ? point->keys + 1 : NULL; + + pind->birthtime = *(point->keys->time); + pind->dietime = *((point->keys + point->totkey - 1)->time); + } + else if (pind->keyed) { + ParticleKey *key = pa->keys; + pind->kkey[0] = key; + pind->kkey[1] = pa->totkey > 1 ? key + 1 : NULL; + + pind->birthtime = key->time; + pind->dietime = (key + pa->totkey - 1)->time; + } + else if (pind->cache) { + float start = 0.0f, end = 0.0f; + get_pointcache_keys_for_time(ob, pind->cache, &pind->pm, -1, 0.0f, NULL, NULL); + pind->birthtime = pa ? pa->time : pind->cache->startframe; + pind->dietime = pa ? pa->dietime : pind->cache->endframe; + + if (get_pointcache_times_for_particle(pind->cache, pa - psys->particles, &start, &end)) { + pind->birthtime = MAX2(pind->birthtime, start); + pind->dietime = MIN2(pind->dietime, end); + } + } + else { + HairKey *key = pa->hair; + pind->hkey[0] = key; + pind->hkey[1] = key + 1; + + pind->birthtime = key->time; + pind->dietime = (key + pa->totkey - 1)->time; + + if (pind->dm) { + pind->mvert[0] = CDDM_get_vert(pind->dm, pa->hair_index); + pind->mvert[1] = pind->mvert[0] + 1; + } + } +} + +void do_particle_interpolation(ParticleSystem *psys, int p, ParticleData *pa, float t, ParticleInterpolationData *pind, ParticleKey *result) +{ + PTCacheEditPoint *point = pind->epoint; + ParticleKey keys[4]; + int point_vel = (point && point->keys->vel); + float real_t, dfra, keytime, invdt = 1.f; + + /* billboards wont fill in all of these, so start cleared */ + memset(keys, 0, sizeof(keys)); + + /* interpret timing and find keys */ + if (point) { + if (result->time < 0.0f) + real_t = -result->time; + else + real_t = *(pind->ekey[0]->time) + t * (*(pind->ekey[0][point->totkey - 1].time) - *(pind->ekey[0]->time)); + + while (*(pind->ekey[1]->time) < real_t) + pind->ekey[1]++; + + pind->ekey[0] = pind->ekey[1] - 1; + } + else if (pind->keyed) { + /* we have only one key, so let's use that */ + if (pind->kkey[1] == NULL) { + copy_particle_key(result, pind->kkey[0], 1); + return; + } + + if (result->time < 0.0f) + real_t = -result->time; + else + real_t = pind->kkey[0]->time + t * (pind->kkey[0][pa->totkey - 1].time - pind->kkey[0]->time); + + if (psys->part->phystype == PART_PHYS_KEYED && psys->flag & PSYS_KEYED_TIMING) { + ParticleTarget *pt = psys->targets.first; + + pt = pt->next; + + while (pt && pa->time + pt->time < real_t) + pt = pt->next; + + if (pt) { + pt = pt->prev; + + if (pa->time + pt->time + pt->duration > real_t) + real_t = pa->time + pt->time; + } + else + real_t = pa->time + ((ParticleTarget *)psys->targets.last)->time; + } + + CLAMP(real_t, pa->time, pa->dietime); + + while (pind->kkey[1]->time < real_t) + pind->kkey[1]++; + + pind->kkey[0] = pind->kkey[1] - 1; + } + else if (pind->cache) { + if (result->time < 0.0f) /* flag for time in frames */ + real_t = -result->time; + else + real_t = pa->time + t * (pa->dietime - pa->time); + } + else { + if (result->time < 0.0f) + real_t = -result->time; + else + real_t = pind->hkey[0]->time + t * (pind->hkey[0][pa->totkey - 1].time - pind->hkey[0]->time); + + while (pind->hkey[1]->time < real_t) { + pind->hkey[1]++; + pind->mvert[1]++; + } + + pind->hkey[0] = pind->hkey[1] - 1; + } + + /* set actual interpolation keys */ + if (point) { + edit_to_particle(keys + 1, pind->ekey[0]); + edit_to_particle(keys + 2, pind->ekey[1]); + } + else if (pind->dm) { + pind->mvert[0] = pind->mvert[1] - 1; + mvert_to_particle(keys + 1, pind->mvert[0], pind->hkey[0]); + mvert_to_particle(keys + 2, pind->mvert[1], pind->hkey[1]); + } + else if (pind->keyed) { + memcpy(keys + 1, pind->kkey[0], sizeof(ParticleKey)); + memcpy(keys + 2, pind->kkey[1], sizeof(ParticleKey)); + } + else if (pind->cache) { + get_pointcache_keys_for_time(NULL, pind->cache, &pind->pm, p, real_t, keys + 1, keys + 2); + } + else { + hair_to_particle(keys + 1, pind->hkey[0]); + hair_to_particle(keys + 2, pind->hkey[1]); + } + + /* set secondary interpolation keys for hair */ + if (!pind->keyed && !pind->cache && !point_vel) { + if (point) { + if (pind->ekey[0] != point->keys) + edit_to_particle(keys, pind->ekey[0] - 1); + else + edit_to_particle(keys, pind->ekey[0]); + } + else if (pind->dm) { + if (pind->hkey[0] != pa->hair) + mvert_to_particle(keys, pind->mvert[0] - 1, pind->hkey[0] - 1); + else + mvert_to_particle(keys, pind->mvert[0], pind->hkey[0]); + } + else { + if (pind->hkey[0] != pa->hair) + hair_to_particle(keys, pind->hkey[0] - 1); + else + hair_to_particle(keys, pind->hkey[0]); + } + + if (point) { + if (pind->ekey[1] != point->keys + point->totkey - 1) + edit_to_particle(keys + 3, pind->ekey[1] + 1); + else + edit_to_particle(keys + 3, pind->ekey[1]); + } + else if (pind->dm) { + if (pind->hkey[1] != pa->hair + pa->totkey - 1) + mvert_to_particle(keys + 3, pind->mvert[1] + 1, pind->hkey[1] + 1); + else + mvert_to_particle(keys + 3, pind->mvert[1], pind->hkey[1]); + } + else { + if (pind->hkey[1] != pa->hair + pa->totkey - 1) + hair_to_particle(keys + 3, pind->hkey[1] + 1); + else + hair_to_particle(keys + 3, pind->hkey[1]); + } + } + + dfra = keys[2].time - keys[1].time; + keytime = (real_t - keys[1].time) / dfra; + + /* convert velocity to timestep size */ + if (pind->keyed || pind->cache || point_vel) { + invdt = dfra * 0.04f * (psys ? psys->part->timetweak : 1.f); + mul_v3_fl(keys[1].vel, invdt); + mul_v3_fl(keys[2].vel, invdt); + interp_qt_qtqt(result->rot, keys[1].rot, keys[2].rot, keytime); + } + + /* now we should have in chronologiacl order k1<=k2<=t<=k3<=k4 with keytime between [0, 1]->[k2, k3] (k1 & k4 used for cardinal & bspline interpolation)*/ + psys_interpolate_particle((pind->keyed || pind->cache || point_vel) ? -1 /* signal for cubic interpolation */ + : (pind->bspline ? KEY_BSPLINE : KEY_CARDINAL), + keys, keytime, result, 1); + + /* the velocity needs to be converted back from cubic interpolation */ + if (pind->keyed || pind->cache || point_vel) + mul_v3_fl(result->vel, 1.f / invdt); +} diff --git a/source/blender/blenkernel/intern/particle_system.c b/source/blender/blenkernel/intern/particle_system.c index 3a19ca7304c..68dda2611fd 100644 --- a/source/blender/blenkernel/intern/particle_system.c +++ b/source/blender/blenkernel/intern/particle_system.c @@ -84,6 +84,7 @@ #include "BKE_object.h" #include "BKE_material.h" #include "BKE_cloth.h" +#include "BKE_key.h" #include "BKE_lattice.h" #include "BKE_pointcache.h" #include "BKE_mesh.h" @@ -1185,7 +1186,7 @@ void psys_make_temp_pointcache(Object *ob, ParticleSystem *psys) PTCacheID pid; BKE_ptcache_id_from_particles(&pid, ob, psys); cache->flag &= ~PTCACHE_DISK_CACHE; - BKE_ptcache_disk_to_mem(&pid); + BKE_ptcache_disk_to_mem(&pid, false); cache->flag |= PTCACHE_DISK_CACHE; } } @@ -1275,7 +1276,7 @@ static void psys_update_effectors(ParticleSimulationData *sim) { pdEndEffectors(&sim->psys->effectors); sim->psys->effectors = pdInitEffectors(sim->scene, sim->ob, sim->psys, - sim->psys->part->effector_weights, true); + sim->psys->part->effector_weights); precalc_guides(sim, sim->psys->effectors); } @@ -2880,79 +2881,93 @@ static void collision_check(ParticleSimulationData *sim, int p, float dfra, floa /************************************************/ /* Hair */ /************************************************/ -/* check if path cache or children need updating and do it if needed */ -static void psys_update_path_cache(ParticleSimulationData *sim, float cfra) + +static bool psys_needs_path_cache(ParticleSimulationData *sim) { ParticleSystem *psys = sim->psys; ParticleSettings *part = psys->part; ParticleEditSettings *pset = &sim->scene->toolsettings->particle; Base *base; - int distr=0, alloc=0, skip=0; - - if ((psys->part->childtype && psys->totchild != psys_get_tot_child(sim->scene, psys)) || psys->recalc&PSYS_RECALC_RESET) - alloc=1; - - if (alloc || psys->recalc&PSYS_RECALC_CHILD || (psys->vgroup[PSYS_VG_DENSITY] && (sim->ob && sim->ob->mode & OB_MODE_WEIGHT_PAINT))) - distr=1; - - if (distr) { - if (alloc) - realloc_particles(sim, sim->psys->totpart); - - if (psys_get_tot_child(sim->scene, psys)) { - /* don't generate children while computing the hair keys */ - if (!(psys->part->type == PART_HAIR) || (psys->flag & PSYS_HAIR_DONE)) { - distribute_particles(sim, PART_FROM_CHILD); - - if (part->childtype==PART_CHILD_FACES && part->parents != 0.0f) - psys_find_parents(sim); + + /* particle instance modifier with "path" option need cached paths even if particle system doesn't */ + for (base = sim->scene->base.first; base; base= base->next) { + ModifierData *md = modifiers_findByType(base->object, eModifierType_ParticleInstance); + if (md) { + ParticleInstanceModifierData *pimd = (ParticleInstanceModifierData *)md; + if (pimd->flag & eParticleInstanceFlag_Path && pimd->ob == sim->ob && pimd->psys == (psys - (ParticleSystem*)sim->ob->particlesystem.first)) { + return true; } } - else - psys_free_children(psys); } - + if ((part->type==PART_HAIR || psys->flag&PSYS_KEYED || psys->pointcache->flag & PTCACHE_BAKED)==0) - skip = 1; /* only hair, keyed and baked stuff can have paths */ + return false; /* only hair, keyed and baked stuff can have paths */ else if (part->ren_as != PART_DRAW_PATH && !(part->type==PART_HAIR && ELEM(part->ren_as, PART_DRAW_OB, PART_DRAW_GR))) - skip = 1; /* particle visualization must be set as path */ + return false; /* particle visualization must be set as path */ else if (!psys->renderdata) { - if (part->draw_as != PART_DRAW_REND) - skip = 1; /* draw visualization */ + if (!ELEM(part->draw_as, PART_DRAW_REND, PART_DRAW_HULL)) + return false; /* not a mode that requires paths */ else if (psys->pointcache->flag & PTCACHE_BAKING) - skip = 1; /* no need to cache paths while baking dynamics */ + return false; /* no need to cache paths while baking dynamics */ else if (psys_in_edit_mode(sim->scene, psys)) { if ((pset->flag & PE_DRAW_PART)==0) - skip = 1; + return false; else if (part->childtype==0 && (psys->flag & PSYS_HAIR_DYNAMICS && psys->pointcache->flag & PTCACHE_BAKED)==0) - skip = 1; /* in edit mode paths are needed for child particles and dynamic hair */ + return false; /* in edit mode paths are needed for child particles and dynamic hair */ } } + + return true; +} +/* check if path cache or children need updating and do it if needed */ +static void psys_update_path_cache(ParticleSimulationData *sim, float cfra) +{ + ParticleSystem *psys = sim->psys; + ParticleSettings *part = psys->part; - /* particle instance modifier with "path" option need cached paths even if particle system doesn't */ - for (base = sim->scene->base.first; base; base= base->next) { - ModifierData *md = modifiers_findByType(base->object, eModifierType_ParticleInstance); - if (md) { - ParticleInstanceModifierData *pimd = (ParticleInstanceModifierData *)md; - if (pimd->flag & eParticleInstanceFlag_Path && pimd->ob == sim->ob && pimd->psys == (psys - (ParticleSystem*)sim->ob->particlesystem.first)) { - skip = 0; - break; + /* check if particles need to be reallocated or redistributed */ + { + bool alloc = false, distr = false; + + if ((psys->part->childtype && psys->totchild != psys_get_tot_child(sim->scene, psys)) || psys->recalc&PSYS_RECALC_RESET) + alloc = true; + + if (alloc || psys->recalc&PSYS_RECALC_CHILD || (psys->vgroup[PSYS_VG_DENSITY] && (sim->ob && sim->ob->mode & OB_MODE_WEIGHT_PAINT))) + distr = true; + + if (distr) { + if (alloc) + realloc_particles(sim, sim->psys->totpart); + + if (psys_get_tot_child(sim->scene, psys)) { + /* don't generate children while computing the hair keys */ + if (!(psys->part->type == PART_HAIR) || (psys->flag & PSYS_HAIR_DONE)) { + distribute_particles(sim, PART_FROM_CHILD); + + if (part->childtype==PART_CHILD_FACES && part->parents != 0.0f) + psys_find_parents(sim); + } } + else + psys_free_children(psys); } } - if (!skip) { + if (psys_needs_path_cache(sim)) { + psys_cache_paths(sim, cfra); /* for render, child particle paths are computed on the fly */ if (part->childtype) { + bool do_child_paths = true; + if (!psys->totchild) - skip = 1; + do_child_paths = false; else if (psys->part->type == PART_HAIR && (psys->flag & PSYS_HAIR_DONE)==0) - skip = 1; + do_child_paths = false; - if (!skip) + if (do_child_paths) psys_cache_child_paths(sim, cfra, 0); } } @@ -2999,6 +3014,120 @@ static MDeformVert *hair_set_pinning(MDeformVert *dvert, float weight) return dvert; } +bool psys_hair_update_preview(ParticleSimulationData *sim) +{ +#ifdef USE_PARTICLE_PREVIEW + ParticleSystem *psys = sim->psys; + ParticleSettings *part = psys->part; + DerivedMesh *dm = sim->psmd->dm; + const float ratio = psys->hair_preview_factor * 0.01f; + /* target number of simulated hairs + * NOTE: this has to be reached exactly, in order to allow + * comparison with the psys->hair_num_simulated value! + */ + const int num_simulated = psys->totpart * ratio; + + if (!(part->type == PART_HAIR)) + return false; + if (num_simulated == psys->hair_num_simulated) + return false; + + { /* Random hair selection method */ + KDTree *tree = BLI_kdtree_new(num_simulated); /* kdtree for finding nearest simulated hairs for blending */ + RNG *rng = BLI_rng_new(98250 + psys->seed); + ParticleData *pa; + int cur_simulated = 0; + int i; + + /* construct a kd-tree of all simulated hairs */ + pa = psys->particles; + for (i = 0; i < psys->totpart; ++i, ++pa) { + bool simulate = true; + if (cur_simulated == num_simulated) { + /* don't simulate more than the total number */ + simulate = false; + } + else if (num_simulated - cur_simulated <= psys->totpart - i) { + /* only allow disabling if the target sim number + * can be reached with the remaining hairs + */ + simulate = BLI_rng_get_float(rng) < ratio; + } + + if (simulate) { + float co[3]; + + pa->flag &= ~PARS_HAIR_BLEND; + + if (pa->totkey >= 2) { + psys_particle_on_dm(dm, part->from, pa->num, pa->num_dmcache, pa->fuv, pa->foffset, co, 0, 0, 0, 0, 0); + BLI_kdtree_insert(tree, i, co); + } + + ++cur_simulated; + } + else { + pa->flag |= PARS_HAIR_BLEND; + } + } + + BLI_kdtree_balance(tree); + + /* look up nearest simulated hairs for preview hairs and calculate blending weights */ + pa = psys->particles; + for (i = 0; i < psys->totpart; ++i, ++pa) { + if (pa->flag & PARS_HAIR_BLEND) { + float co[3]; + int maxw, w; + KDTreeNearest nearest[4]; + + psys_particle_on_dm(dm, part->from, pa->num, pa->num_dmcache, pa->fuv, pa->foffset, co, 0, 0, 0, 0, 0); + maxw = BLI_kdtree_find_nearest_n(tree, co, nearest, 4); + if (maxw == 1) { + pa->blend_index[0] = nearest[0].index; + pa->blend_weight[0] = 1.0f; + } + else if (maxw > 1) { + float norm, totdist = 0.0f; + for (w = 0; w < maxw; ++w) + totdist += nearest[w].dist; + norm = totdist > 0.0f ? 1.0f / (totdist * (float)(maxw - 1)) : 0.0f; + + for (w = 0; w < maxw; ++w) { + pa->blend_index[w] = nearest[w].index; + pa->blend_weight[w] = (totdist - nearest[w].dist) * norm; + } + } + /* clear unused weights */ + for (w = maxw; w < 4; ++w) { + pa->blend_index[w] = -1; + pa->blend_weight[w] = 0.0f; + } + } + else { + pa->blend_index[0] = -1; + pa->blend_index[1] = -1; + pa->blend_index[2] = -1; + pa->blend_index[3] = -1; + pa->blend_weight[0] = 0.0f; + pa->blend_weight[1] = 0.0f; + pa->blend_weight[2] = 0.0f; + pa->blend_weight[3] = 0.0f; + } + } + + BLI_kdtree_free(tree); + BLI_rng_free(rng); + } + + psys->hair_num_simulated = num_simulated; + return true; +#else + (void)sim; + return false; +#endif +} + static void hair_create_input_dm(ParticleSimulationData *sim, int totpoint, int totedge, DerivedMesh **r_dm, ClothHairData **r_hairdata) { ParticleSystem *psys = sim->psys; @@ -3014,6 +3143,8 @@ static void hair_create_input_dm(ParticleSimulationData *sim, int totpoint, int float hairmat[4][4]; float max_length; float hair_radius; + float *shapekey_data, *shapekey; + int totshapekey; dm = *r_dm; if (!dm) { @@ -3044,6 +3175,8 @@ static void hair_create_input_dm(ParticleSimulationData *sim, int totpoint, int /* XXX placeholder for more flexible future hair settings */ hair_radius = part->size; + shapekey = shapekey_data = BKE_key_evaluate_particles(sim->ob, psys, sim->scene ? sim->scene->r.cfra : 0.0f, &totshapekey); + /* make vgroup for pin roots etc.. */ hair_index = 1; LOOP_PARTICLES { @@ -3064,8 +3197,15 @@ static void hair_create_input_dm(ParticleSimulationData *sim, int totpoint, int ClothHairData *hair; float *co, *co_next; - co = key->co; - co_next = (key+1)->co; + if (shapekey) { + co = shapekey; + co_next = shapekey + 3; + shapekey += 3; + } + else { + co = key->co; + co_next = (key+1)->co; + } /* create fake root before actual root to resist bending */ if (k==0) { @@ -3085,6 +3225,14 @@ static void hair_create_input_dm(ParticleSimulationData *sim, int totpoint, int dvert = hair_set_pinning(dvert, 1.0f); +#ifdef USE_PARTICLE_PREVIEW + /* tag vert if hair is not simulated */ + if (pa->flag & PARS_HAIR_BLEND) + mvert->flag |= ME_VERT_TMP_TAG; + else + mvert->flag &= ~ME_VERT_TMP_TAG; +#endif + mvert++; medge++; } @@ -3111,6 +3259,14 @@ static void hair_create_input_dm(ParticleSimulationData *sim, int totpoint, int else dvert = hair_set_pinning(dvert, 1.0f); +#ifdef USE_PARTICLE_PREVIEW + /* tag vert if hair is not simulated */ + if (pa->flag & PARS_HAIR_BLEND) + mvert->flag |= ME_VERT_TMP_TAG; + else + mvert->flag &= ~ME_VERT_TMP_TAG; +#endif + mvert++; if (k) medge++; @@ -3120,6 +3276,105 @@ static void hair_create_input_dm(ParticleSimulationData *sim, int totpoint, int } } +#ifdef USE_PARTICLE_PREVIEW +static void hair_deform_preview_curve(ParticleSystem *psys, ParticleData *pa, float (*deformedVerts)[3], ClothHairData *hairdata) +{ + ParticleData *particles = psys->particles; + HairKey *hkey; + float (*vert)[3]; + ClothHairData *root; + int k; + float totlen, norm; + + /* first key is root, no blending for them */ + if (pa->totkey < 2) + return; + + /* calculate normalization factor to equally parameterize hairs */ + totlen = 0.0f; + hkey = pa->hair; + for (k = 0; k < pa->totkey - 1; ++k, ++hkey) + totlen += len_v3v3((hkey+1)->co, hkey->co); + norm = totlen > 0.0f ? 1.0f / totlen : 0.0f; + + totlen = 0.0f; + hkey = pa->hair; + vert = deformedVerts + pa->hair_index; + root = hairdata + pa->hair_index; + for (k = 0; k < pa->totkey; ++k, ++hkey, ++vert, ++root) { + float param; + int w; + + if (k == 0) /* skip root vertex */ + continue; + param = totlen * norm; + totlen += len_v3v3(hkey->co, (hkey-1)->co); + + zero_v3(vert[0]); + for (w = 0; w < 4; ++w) { + ParticleData *blend_pa; + float (*blend_vert)[3]; + ClothHairData *blend_hair; + float blend_key, blend_factor; + int blend_totkey, blend_index; + float co[3]; + + if (pa->blend_index[w] < 0) + continue; + + blend_pa = particles + pa->blend_index[w]; + blend_totkey = blend_pa->totkey; + + blend_key = param * (float)blend_totkey; + blend_index = (int)blend_key; + if (blend_index >= blend_totkey - 1) { + blend_index = blend_totkey - 2; + blend_factor = 1.0f; + } + else { + blend_factor = blend_key - floorf(blend_key); + } + + /* pa->hair_index is set when creating input_dm, + * use it here to map to output mvert index + */ + blend_vert = deformedVerts + blend_pa->hair_index + blend_index; + blend_hair = hairdata + blend_pa->hair_index + blend_index; + + interp_v3_v3v3(co, blend_vert[0], blend_vert[1], blend_factor); + + /* transform parent vector from world to root space, then back into root space of the blended hair */ + sub_v3_v3(co, blend_hair->loc); + /* note: rotation transform disabled, this does not work nicely with global force directions (gravity, wind etc.) + * these forces would also get rotated, giving movement in a different direction than the force would actually incur. + * would have to split internal (stretch, bend) and external forces somehow to make this plausible + */ +#if 0 + mul_transposed_m3_v3(blend_hair->rot, co); + mul_m3_v3(root->rot, co); +#endif + add_v3_v3(co, root->loc); + + madd_v3_v3fl(vert[0], co, pa->blend_weight[w]); + } + } +} + +static void hair_deform_preview_hairs(ParticleSimulationData *sim, float (*deformedVerts)[3], ClothHairData *roots) +{ + ParticleSystem *psys = sim->psys; + ParticleData *pa; + int p; + + pa = psys->particles; + for (p = 0; p < psys->totpart; ++p, ++pa) { + if (pa->flag & PARS_HAIR_BLEND) { + hair_deform_preview_curve(psys, pa, deformedVerts, roots); + } + } +} +#endif + static void do_hair_dynamics(ParticleSimulationData *sim) { ParticleSystem *psys = sim->psys; @@ -3130,6 +3385,11 @@ static void do_hair_dynamics(ParticleSimulationData *sim) float (*deformedVerts)[3]; bool realloc_roots; + if (psys_hair_update_preview(sim)) { + if (psys->clmd) + cloth_free_modifier(psys->clmd); + } + if (!psys->clmd) { psys->clmd = (ClothModifierData*)modifier_new(eModifierType_Cloth); psys->clmd->sim_parms->goalspring = 0.0f; @@ -3181,6 +3441,9 @@ static void do_hair_dynamics(ParticleSimulationData *sim) psys->hair_out_dm->getVertCos(psys->hair_out_dm, deformedVerts); clothModifier_do(psys->clmd, sim->scene, sim->ob, psys->hair_in_dm, deformedVerts); +#ifdef USE_PARTICLE_PREVIEW + hair_deform_preview_hairs(sim, deformedVerts, psys->clmd->roots); +#endif CDDM_apply_vert_coords(psys->hair_out_dm, deformedVerts); @@ -3194,7 +3457,14 @@ static void hair_step(ParticleSimulationData *sim, float cfra) ParticleSystem *psys = sim->psys; ParticleSettings *part = psys->part; PARTICLE_P; + PARTICLE_PSMD; float disp = psys_get_current_display_percentage(psys); + float *shapekey_data = NULL, *shapekey; + int totshapekey; + + if (part->type == PART_HAIR) { + shapekey = shapekey_data = BKE_key_evaluate_particles(sim->ob, sim->psys, sim->scene ? sim->scene->r.cfra : 0.0f, &totshapekey); + } LOOP_PARTICLES { pa->size = part->size; @@ -3205,8 +3475,35 @@ static void hair_step(ParticleSimulationData *sim, float cfra) pa->flag |= PARS_NO_DISP; else pa->flag &= ~PARS_NO_DISP; + + if (part->type == PART_HAIR) { + float hairmat[4][4]; + HairKey *hkey; + int k; + + psys_mat_hair_to_global(sim->ob, psmd->dm, psys->part->from, pa, hairmat); + + /* update world coordinates and calculate shapekey result if needed */ + for (k = 0, hkey = pa->hair; k < pa->totkey; ++k, ++hkey) { + const float *co; + if (shapekey) { + co = shapekey; + shapekey += 3; + + copy_v3_v3(hkey->co, co); + } + else { + co = hkey->co; + } + + mul_v3_m4v3(hkey->world_co, hairmat, co); + } + } } + if (shapekey_data) + MEM_freeN(shapekey_data); + if (psys->recalc & PSYS_RECALC_RESET) { /* need this for changing subsurf levels */ psys_calc_dmcache(sim->ob, sim->psmd->dm, psys); diff --git a/source/blender/blenkernel/intern/pointcache.c b/source/blender/blenkernel/intern/pointcache.c index 2f4a45828ea..4711a5900f0 100644 --- a/source/blender/blenkernel/intern/pointcache.c +++ b/source/blender/blenkernel/intern/pointcache.c @@ -2537,7 +2537,7 @@ int BKE_ptcache_write(PTCacheID *pid, unsigned int cfra) */ /* Clears & resets */ -void BKE_ptcache_id_clear(PTCacheID *pid, int mode, unsigned int cfra) +void BKE_ptcache_id_clear_ex(PTCacheID *pid, int mode, unsigned int cfra, bool allow_file_delete) { unsigned int len; /* store the length of the string */ unsigned int sta, end; @@ -2595,8 +2595,10 @@ void BKE_ptcache_id_clear(PTCacheID *pid, int mode, unsigned int cfra) if (STREQLEN(filename, de->d_name, len)) { /* do we have the right prefix */ if (mode == PTCACHE_CLEAR_ALL) { pid->cache->last_exact = MIN2(pid->cache->startframe, 0); - BLI_join_dirfile(path_full, sizeof(path_full), path, de->d_name); - BLI_delete(path_full, false, false); + if (allow_file_delete) { + BLI_join_dirfile(path_full, sizeof(path_full), path, de->d_name); + BLI_delete(path_full, false, false); + } } else { /* read the number of the file */ @@ -2611,8 +2613,10 @@ void BKE_ptcache_id_clear(PTCacheID *pid, int mode, unsigned int cfra) (mode == PTCACHE_CLEAR_AFTER && frame > cfra)) { - BLI_join_dirfile(path_full, sizeof(path_full), path, de->d_name); - BLI_delete(path_full, false, false); + if (allow_file_delete) { + BLI_join_dirfile(path_full, sizeof(path_full), path, de->d_name); + BLI_delete(path_full, false, false); + } if (pid->cache->cached_frames && frame >=sta && frame <= end) pid->cache->cached_frames[frame-sta] = 0; } @@ -2665,8 +2669,10 @@ void BKE_ptcache_id_clear(PTCacheID *pid, int mode, unsigned int cfra) case PTCACHE_CLEAR_FRAME: if (pid->cache->flag & PTCACHE_DISK_CACHE) { if (BKE_ptcache_id_exist(pid, cfra)) { - ptcache_filename(pid, filename, cfra, 1, 1); /* no path */ - BLI_delete(filename, false, false); + if (allow_file_delete) { + ptcache_filename(pid, filename, cfra, 1, 1); /* no path */ + BLI_delete(filename, false, false); + } } } else { @@ -2688,6 +2694,13 @@ void BKE_ptcache_id_clear(PTCacheID *pid, int mode, unsigned int cfra) BKE_ptcache_update_info(pid); } + +/* Clears & resets */ +void BKE_ptcache_id_clear(PTCacheID *pid, int mode, unsigned int cfra) +{ + BKE_ptcache_id_clear_ex(pid, mode, cfra, false); +} + int BKE_ptcache_id_exist(PTCacheID *pid, int cfra) { if (!pid->cache) @@ -2959,49 +2972,6 @@ int BKE_ptcache_object_reset(Scene *scene, Object *ob, int mode) return reset; } -/* Use this when quitting blender, with unsaved files */ -void BKE_ptcache_remove(void) -{ - char path[MAX_PTCACHE_PATH]; - char path_full[MAX_PTCACHE_PATH]; - int rmdir = 1; - - ptcache_path(NULL, path); - - if (BLI_exists(path)) { - /* The pointcache dir exists? - remove all pointcache */ - - DIR *dir; - struct dirent *de; - - dir = opendir(path); - if (dir==NULL) - return; - - while ((de = readdir(dir)) != NULL) { - if (FILENAME_IS_CURRPAR(de->d_name)) { - /* do nothing */ - } - else if (strstr(de->d_name, PTCACHE_EXT)) { /* do we have the right extension?*/ - BLI_join_dirfile(path_full, sizeof(path_full), path, de->d_name); - BLI_delete(path_full, false, false); - } - else { - rmdir = 0; /* unknown file, don't remove the dir */ - } - } - - closedir(dir); - } - else { - rmdir = 0; /* path dosnt exist */ - } - - if (rmdir) { - BLI_delete(path, true, false); - } -} - /* Point Cache handling */ PointCache *BKE_ptcache_add(ListBase *ptcaches) @@ -3412,21 +3382,24 @@ void BKE_ptcache_bake(PTCacheBaker *baker) /* TODO: call redraw all windows somehow */ } /* Helpers */ -void BKE_ptcache_disk_to_mem(PTCacheID *pid) +void BKE_ptcache_disk_to_mem(PTCacheID *pid, bool clear) { PointCache *cache = pid->cache; PTCacheMem *pm = NULL; - int baked = cache->flag & PTCACHE_BAKED; int cfra, sfra = cache->startframe, efra = cache->endframe; - /* Remove possible bake flag to allow clear */ - cache->flag &= ~PTCACHE_BAKED; - - /* PTCACHE_DISK_CACHE flag was cleared already */ - BKE_ptcache_id_clear(pid, PTCACHE_CLEAR_ALL, 0); - - /* restore possible bake flag */ - cache->flag |= baked; + if (clear) { + int baked = cache->flag & PTCACHE_BAKED; + + /* Remove possible bake flag to allow clear */ + cache->flag &= ~PTCACHE_BAKED; + + /* PTCACHE_DISK_CACHE flag was cleared already */ + BKE_ptcache_id_clear(pid, PTCACHE_CLEAR_ALL, 0); + + /* restore possible bake flag */ + cache->flag |= baked; + } for (cfra=sfra; cfra <= efra; cfra++) { pm = ptcache_disk_frame_to_mem(pid, cfra); @@ -3435,20 +3408,23 @@ void BKE_ptcache_disk_to_mem(PTCacheID *pid) BLI_addtail(&pid->cache->mem_cache, pm); } } -void BKE_ptcache_mem_to_disk(PTCacheID *pid) +void BKE_ptcache_mem_to_disk(PTCacheID *pid, bool clear) { PointCache *cache = pid->cache; PTCacheMem *pm = cache->mem_cache.first; - int baked = cache->flag & PTCACHE_BAKED; - - /* Remove possible bake flag to allow clear */ - cache->flag &= ~PTCACHE_BAKED; - - /* PTCACHE_DISK_CACHE flag was set already */ - BKE_ptcache_id_clear(pid, PTCACHE_CLEAR_ALL, 0); - /* restore possible bake flag */ - cache->flag |= baked; + if (clear) { + int baked = cache->flag & PTCACHE_BAKED; + + /* Remove possible bake flag to allow clear */ + cache->flag &= ~PTCACHE_BAKED; + + /* PTCACHE_DISK_CACHE flag was set already */ + BKE_ptcache_id_clear(pid, PTCACHE_CLEAR_ALL, 0); + + /* restore possible bake flag */ + cache->flag |= baked; + } for (; pm; pm=pm->next) { if (ptcache_mem_frame_to_disk(pid, pm)==0) { @@ -3479,9 +3455,9 @@ void BKE_ptcache_toggle_disk_cache(PTCacheID *pid) } if (cache->flag & PTCACHE_DISK_CACHE) - BKE_ptcache_mem_to_disk(pid); + BKE_ptcache_mem_to_disk(pid, true); else - BKE_ptcache_disk_to_mem(pid); + BKE_ptcache_disk_to_mem(pid, true); cache->flag ^= PTCACHE_DISK_CACHE; BKE_ptcache_id_clear(pid, PTCACHE_CLEAR_ALL, 0); diff --git a/source/blender/blenkernel/intern/rigidbody.c b/source/blender/blenkernel/intern/rigidbody.c index ff9e1a2b831..b4218378eb8 100644 --- a/source/blender/blenkernel/intern/rigidbody.c +++ b/source/blender/blenkernel/intern/rigidbody.c @@ -1238,7 +1238,7 @@ static void rigidbody_update_sim_ob(Scene *scene, RigidBodyWorld *rbw, Object *o ListBase *effectors; /* get effectors present in the group specified by effector_weights */ - effectors = pdInitEffectors(scene, ob, NULL, effector_weights, true); + effectors = pdInitEffectors(scene, ob, NULL, effector_weights); if (effectors) { float eff_force[3] = {0.0f, 0.0f, 0.0f}; float eff_loc[3], eff_vel[3]; diff --git a/source/blender/blenkernel/intern/scene.c b/source/blender/blenkernel/intern/scene.c index bd03706c5b8..bd923d296f1 100644 --- a/source/blender/blenkernel/intern/scene.c +++ b/source/blender/blenkernel/intern/scene.c @@ -64,6 +64,7 @@ #include "BKE_animsys.h" #include "BKE_action.h" #include "BKE_armature.h" +#include "BKE_cache_library.h" #include "BKE_colortools.h" #include "BKE_depsgraph.h" #include "BKE_editmesh.h" @@ -1839,6 +1840,9 @@ void BKE_scene_update_for_newframe_ex(EvaluationContext *eval_ctx, Main *bmain, /* clear animation overrides */ /* XXX TODO... */ + /* tag cached objects */ + BKE_cache_library_dag_recalc_tag(eval_ctx, bmain); + for (sce_iter = sce; sce_iter; sce_iter = sce_iter->set) DAG_scene_relations_update(bmain, sce_iter); @@ -1924,6 +1928,45 @@ void BKE_scene_update_for_newframe_ex(EvaluationContext *eval_ctx, Main *bmain, #endif } +void BKE_scene_update_group_for_newframe(EvaluationContext *eval_ctx, + Main *bmain, + Scene *scene, + Group *group, + unsigned int lay) +{ + float ctime = BKE_scene_frame_get(scene); + Scene *sce_iter; + + /* Step 1: Preparation, same as in regular frame update. */ + BKE_image_update_frame(bmain, scene->r.cfra); + scene_rebuild_rbw_recursive(scene, ctime); + BKE_cache_library_dag_recalc_tag(eval_ctx, bmain); + for (sce_iter = scene; sce_iter; sce_iter = sce_iter->set) { + DAG_scene_relations_update(bmain, sce_iter); + } + + /* Step 2: Tag objects which we need to update. */ + DAG_ids_flush_tagged(bmain); + DAG_scene_update_group_flags(bmain, scene, group, lay, true, false); + + /* Step 3: Update animation. */ +#ifdef POSE_ANIMATION_WORKAROUND + scene_armature_depsgraph_workaround(bmain); +#endif + BKE_animsys_evaluate_all_animation(bmain, scene, ctime); + + /* Step 4: Actual evaluation. */ + BKE_main_id_tag_idcode(bmain, ID_MA, false); + BKE_main_id_tag_idcode(bmain, ID_LA, false); + scene_do_rb_simulation_recursive(scene, ctime); + scene_update_tagged_recursive(eval_ctx, bmain, scene, scene); + scene_depsgraph_hack(eval_ctx, scene, scene); + + /* Step 5: Cleanup after evaluaiton. */ + DAG_ids_check_recalc(bmain, scene, true); + DAG_ids_clear_recalc(bmain); +} + /* return default layer, also used to patch old files */ SceneRenderLayer *BKE_scene_add_render_layer(Scene *sce, const char *name) { diff --git a/source/blender/blenkernel/intern/screen.c b/source/blender/blenkernel/intern/screen.c index 4125a35cb33..83aac79ae0b 100644 --- a/source/blender/blenkernel/intern/screen.c +++ b/source/blender/blenkernel/intern/screen.c @@ -53,6 +53,9 @@ #include "BKE_idprop.h" #include "BKE_screen.h" +#include "WM_api.h" +#include "WM_types.h" + /* ************ Spacetype/regiontype handling ************** */ /* keep global; this has to be accessible outside of windowmanager */ @@ -179,6 +182,7 @@ ARegion *BKE_area_region_copy(SpaceType *st, ARegion *ar) BLI_listbase_clear(&newar->panels_category); BLI_listbase_clear(&newar->panels_category_active); BLI_listbase_clear(&newar->ui_lists); + BLI_listbase_clear(&newar->widgetmaps); newar->swinid = 0; /* use optional regiondata callback */ @@ -290,6 +294,7 @@ void BKE_spacedata_id_unref(struct SpaceLink *sl, const struct ID *id) void BKE_area_region_free(SpaceType *st, ARegion *ar) { uiList *uilst; + struct wmWidgetMap *wmap, *wmap_tmp; if (st) { ARegionType *art = BKE_regiontype_from_id(st, ar->regiontype); @@ -326,6 +331,12 @@ void BKE_area_region_free(SpaceType *st, ARegion *ar) MEM_freeN(uilst->properties); } } + + for (wmap = ar->widgetmaps.first; wmap; wmap = wmap_tmp) { + wmap_tmp = wmap->next; + WM_widgetmap_delete(wmap); + } + BLI_listbase_clear(&ar->widgetmaps); BLI_freelistN(&ar->ui_lists); BLI_freelistN(&ar->ui_previews); BLI_freelistN(&ar->panels_category); diff --git a/source/blender/blenkernel/intern/seqcache.c b/source/blender/blenkernel/intern/seqcache.c index abfc858fd03..69ba7618981 100644 --- a/source/blender/blenkernel/intern/seqcache.c +++ b/source/blender/blenkernel/intern/seqcache.c @@ -27,8 +27,10 @@ */ #include <stddef.h> +#include <stdlib.h> #include "BLI_sys_types.h" /* for intptr_t */ +#include "BKE_global.h" #include "MEM_guardedalloc.h" @@ -121,6 +123,36 @@ static bool seqcache_hashcmp(const void *a_, const void *b_) seq_cmp_render_data(&a->context, &b->context)); } +typedef struct { + int framenr; +} SeqCachePriorityData; + +static void *moviecache_getprioritydata(void *key_v) +{ + SeqCacheKey *key = (SeqCacheKey *) key_v; + SeqCachePriorityData *priority_data; + + priority_data = MEM_callocN(sizeof(*priority_data), "movie cache clip priority data"); + priority_data->framenr = key->cfra; + + return priority_data; +} + +static int moviecache_getitempriority(void *last_userkey_v, void *priority_data_v) +{ + SeqCacheKey *last_userkey = (SeqCacheKey *) last_userkey_v; + SeqCachePriorityData *priority_data = (SeqCachePriorityData *) priority_data_v; + + return -abs(last_userkey->cfra - priority_data->framenr); +} + +static void moviecache_prioritydeleter(void *priority_data_v) +{ + SeqCachePriorityData *priority_data = (SeqCachePriorityData *) priority_data_v; + + MEM_freeN(priority_data); +} + void BKE_sequencer_cache_destruct(void) { if (moviecache) @@ -134,6 +166,8 @@ void BKE_sequencer_cache_cleanup(void) if (moviecache) { IMB_moviecache_free(moviecache); moviecache = IMB_moviecache_create("seqcache", sizeof(SeqCacheKey), seqcache_hashhash, seqcache_hashcmp); + IMB_moviecache_set_priority_callback(moviecache, moviecache_getprioritydata, moviecache_getitempriority, + moviecache_prioritydeleter); } BKE_sequencer_preprocessed_cache_cleanup(); @@ -173,12 +207,14 @@ void BKE_sequencer_cache_put(const SeqRenderData *context, Sequence *seq, float { SeqCacheKey key; - if (i == NULL || context->skip_cache) { + if (i == NULL || context->skip_cache || G.debug_value == 314) { return; } if (!moviecache) { moviecache = IMB_moviecache_create("seqcache", sizeof(SeqCacheKey), seqcache_hashhash, seqcache_hashcmp); + IMB_moviecache_set_priority_callback(moviecache, moviecache_getprioritydata, moviecache_getitempriority, + moviecache_prioritydeleter); } key.seq = seq; @@ -219,7 +255,7 @@ ImBuf *BKE_sequencer_preprocessed_cache_get(const SeqRenderData *context, Sequen { SeqPreprocessCacheElem *elem; - if (!preprocess_cache) + if (!preprocess_cache || G.debug_value == 314) return NULL; if (preprocess_cache->cfra != cfra) diff --git a/source/blender/blenkernel/intern/sequencer.c b/source/blender/blenkernel/intern/sequencer.c index d4d64af6c94..1cff097df5e 100644 --- a/source/blender/blenkernel/intern/sequencer.c +++ b/source/blender/blenkernel/intern/sequencer.c @@ -1135,6 +1135,33 @@ const char *BKE_sequence_give_name(Sequence *seq) return name; } +ListBase *BKE_sequence_seqbase_get(Sequence *seq, int *r_offset) +{ + ListBase *seqbase = NULL; + + switch (seq->type) { + case SEQ_TYPE_META: + { + seqbase = &seq->seqbase; + *r_offset = seq->start; + break; + } + case SEQ_TYPE_SCENE: + { + if (seq->flag & SEQ_SCENE_STRIPS) { + Editing *ed = BKE_sequencer_editing_get(seq->scene, false); + if (ed) { + seqbase = &ed->seqbase; + *r_offset = seq->scene->r.sfra; + } + } + break; + } + } + + return seqbase; +} + /*********************** DO THE SEQUENCE *************************/ static void make_black_ibuf(ImBuf *ibuf) @@ -2463,12 +2490,14 @@ static ImBuf *input_preprocess(const SeqRenderData *context, Sequence *seq, floa multibuf(ibuf, mul); } - if (ibuf->x != context->rectx || ibuf->y != context->recty) { - if (scene->r.mode & R_OSA) { - IMB_scaleImBuf(ibuf, (short)context->rectx, (short)context->recty); - } - else { - IMB_scalefastImBuf(ibuf, (short)context->rectx, (short)context->recty); + if (!is_proxy_image) { + if (ibuf->x != context->rectx || ibuf->y != context->recty) { + if (scene->r.mode & R_OSA) { + IMB_scaleImBuf(ibuf, (short)context->rectx, (short)context->recty); + } + else { + IMB_scalefastImBuf(ibuf, (short)context->rectx, (short)context->recty); + } } } @@ -3281,6 +3310,40 @@ static ImBuf *seq_render_scene_strip(const SeqRenderData *context, Sequence *seq return ibuf; } +/** + * Used for meta-strips & scenes with #SEQ_SCENE_STRIPS flag set. + */ +static ImBuf *do_render_strip_seqbase( + const SeqRenderData *context, Sequence *seq, float nr, + bool use_preprocess) +{ + ImBuf *meta_ibuf = NULL, *ibuf = NULL; + ListBase *seqbase = NULL; + int offset; + + seqbase = BKE_sequence_seqbase_get(seq, &offset); + + if (seqbase && !BLI_listbase_is_empty(seqbase)) { + meta_ibuf = seq_render_strip_stack( + context, seqbase, + /* scene strips don't have their start taken into account */ + nr + offset, 0); + } + + if (meta_ibuf) { + ibuf = meta_ibuf; + if (ibuf && use_preprocess) { + ImBuf *i = IMB_dupImBuf(ibuf); + + IMB_freeImBuf(ibuf); + + ibuf = i; + } + } + + return ibuf; +} + static ImBuf *do_render_strip_uncached(const SeqRenderData *context, Sequence *seq, float cfra) { ImBuf *ibuf = NULL; @@ -3291,22 +3354,27 @@ static ImBuf *do_render_strip_uncached(const SeqRenderData *context, Sequence *s switch (type) { case SEQ_TYPE_META: { - ImBuf *meta_ibuf = NULL; - - if (seq->seqbase.first) - meta_ibuf = seq_render_strip_stack(context, &seq->seqbase, seq->start + nr, 0); - - if (meta_ibuf) { - ibuf = meta_ibuf; - if (ibuf && use_preprocess) { - ImBuf *i = IMB_dupImBuf(ibuf); - - IMB_freeImBuf(ibuf); + ibuf = do_render_strip_seqbase(context, seq, nr, use_preprocess); + break; + } - ibuf = i; + case SEQ_TYPE_SCENE: + { + if (seq->flag & SEQ_SCENE_STRIPS) { + /* TODO, full recursive check */ + if (context->scene != seq->scene) { + ibuf = do_render_strip_seqbase(context, seq, nr, use_preprocess); } } + else { + /* scene can be NULL after deletions */ + ibuf = seq_render_scene_strip(context, seq, nr, cfra); + + /* Scene strips update all animation, so we need to restore original state.*/ + BKE_animsys_evaluate_all_animation(context->bmain, context->scene, cfra); + copy_to_ibuf_still(context, seq, nr, ibuf); + } break; } @@ -3357,18 +3425,6 @@ static ImBuf *do_render_strip_uncached(const SeqRenderData *context, Sequence *s break; } - case SEQ_TYPE_SCENE: - { - /* scene can be NULL after deletions */ - ibuf = seq_render_scene_strip(context, seq, nr, cfra); - - /* Scene strips update all animation, so we need to restore original state.*/ - BKE_animsys_evaluate_all_animation(context->bmain, context->scene, cfra); - - copy_to_ibuf_still(context, seq, nr, ibuf); - break; - } - case SEQ_TYPE_MOVIECLIP: { ibuf = seq_render_movieclip_strip(context, seq, nr); @@ -3420,6 +3476,7 @@ static ImBuf *seq_render_strip(const SeqRenderData *context, Sequence *seq, floa ibuf = copy_from_ibuf_still(context, seq, nr); if (ibuf == NULL) { + /* disable caching in that case */ ibuf = BKE_sequencer_preprocessed_cache_get(context, seq, cfra, SEQ_STRIPELEM_IBUF); if (ibuf == NULL) { @@ -3656,14 +3713,16 @@ static ImBuf *seq_render_strip_stack(const SeqRenderData *context, ListBase *seq for (; i < count; i++) { Sequence *seq = seq_arr[i]; - if (seq_get_early_out_for_blend_mode(seq) == EARLY_DO_EFFECT) { - ImBuf *ibuf1 = out; - ImBuf *ibuf2 = seq_render_strip(context, seq, cfra); + if (context->preview_render_size >= 100) { + if (seq_get_early_out_for_blend_mode(seq) == EARLY_DO_EFFECT) { + ImBuf *ibuf1 = out; + ImBuf *ibuf2 = seq_render_strip(context, seq, cfra); - out = seq_render_strip_stack_apply_effect(context, seq, cfra, ibuf1, ibuf2); + out = seq_render_strip_stack_apply_effect(context, seq, cfra, ibuf1, ibuf2); - IMB_freeImBuf(ibuf1); - IMB_freeImBuf(ibuf2); + IMB_freeImBuf(ibuf1); + IMB_freeImBuf(ibuf2); + } } BKE_sequencer_cache_put(context, seq_arr[i], cfra, SEQ_STRIPELEM_IBUF_COMP, out); diff --git a/source/blender/blenkernel/intern/smoke.c b/source/blender/blenkernel/intern/smoke.c index 5e2c72bedc7..7b078121eb7 100644 --- a/source/blender/blenkernel/intern/smoke.c +++ b/source/blender/blenkernel/intern/smoke.c @@ -154,7 +154,7 @@ struct SmokeModifierData; #else /* WITH_SMOKE */ /* Stubs to use when smoke is disabled */ -struct WTURBULENCE *smoke_turbulence_init(int *UNUSED(res), int UNUSED(amplify), int UNUSED(noisetype), const char *UNUSED(noisefile_path), int UNUSED(use_fire), int UNUSED(use_colors)) { return NULL; } +struct WTURBULENCE *smoke_turbulence_init(int *UNUSED(res), int UNUSED(amplify), int UNUSED(noisetype), const char *UNUSED(noisefile_path), int UNUSED(use_fire), int UNUSED(use_colors), int UNUSED(use_sim)) { return NULL; } //struct FLUID_3D *smoke_init(int *UNUSED(res), float *UNUSED(dx), float *UNUSED(dtdef), int UNUSED(use_heat), int UNUSED(use_fire), int UNUSED(use_colors)) { return NULL; } void smoke_free(struct FLUID_3D *UNUSED(fluid)) {} float *smoke_get_density(struct FLUID_3D *UNUSED(fluid)) { return NULL; } @@ -197,6 +197,8 @@ void smoke_reallocate_highres_fluid(SmokeDomainSettings *sds, float dx, int res[ { int use_fire = (sds->active_fields & (SM_ACTIVE_HEAT | SM_ACTIVE_FIRE)); int use_colors = (sds->active_fields & SM_ACTIVE_COLORS); + int use_sim = !((sds->point_cache[0] != NULL) && + (sds->point_cache[0]->flag & (PTCACHE_BAKED|PTCACHE_DISK_CACHE)) == (PTCACHE_BAKED|PTCACHE_DISK_CACHE)); if (free_old && sds->wt) smoke_turbulence_free(sds->wt); @@ -208,7 +210,7 @@ void smoke_reallocate_highres_fluid(SmokeDomainSettings *sds, float dx, int res[ /* smoke_turbulence_init uses non-threadsafe functions from fftw3 lib (like fftw_plan & co). */ BLI_lock_thread(LOCK_FFTW); - sds->wt = smoke_turbulence_init(res, sds->amplify + 1, sds->noise, BKE_tempdir_session(), use_fire, use_colors); + sds->wt = smoke_turbulence_init(res, sds->amplify + 1, sds->noise, BKE_tempdir_session(), use_fire, use_colors, use_sim); BLI_unlock_thread(LOCK_FFTW); @@ -579,6 +581,8 @@ void smokeModifier_createType(struct SmokeModifierData *smd) smd->domain->viewsettings = MOD_SMOKE_VIEW_SHOWBIG; smd->domain->effector_weights = BKE_add_effector_weights(NULL); + smd->domain->display_thickness = 1.0f; + smd->domain->use_openvdb = false; } else if (smd->type & MOD_SMOKE_TYPE_FLOW) @@ -1043,6 +1047,7 @@ typedef struct EmissionMap { float *velocity; int min[3], max[3], res[3]; int hmin[3], hmax[3], hres[3]; + float *color; int total_cells, valid; } EmissionMap; @@ -1087,7 +1092,7 @@ static void clampBoundsInDomain(SmokeDomainSettings *sds, int min[3], int max[3] } } -static void em_allocateData(EmissionMap *em, bool use_velocity, int hires_mul) +static void em_allocateData(EmissionMap *em, bool use_velocity, bool use_color, int hires_mul) { int i, res[3]; @@ -1103,6 +1108,8 @@ static void em_allocateData(EmissionMap *em, bool use_velocity, int hires_mul) em->influence = MEM_callocN(sizeof(float) * em->total_cells, "smoke_flow_influence"); if (use_velocity) em->velocity = MEM_callocN(sizeof(float) * em->total_cells * 3, "smoke_flow_velocity"); + if (use_color) + em->color = MEM_callocN(sizeof(float) * em->total_cells * 3, "smoke_flow_color"); /* allocate high resolution map if required */ if (hires_mul > 1) { @@ -1127,6 +1134,8 @@ static void em_freeData(EmissionMap *em) MEM_freeN(em->influence_high); if (em->velocity) MEM_freeN(em->velocity); + if (em->color) + MEM_freeN(em->color); } static void em_combineMaps(EmissionMap *output, EmissionMap *em2, int hires_multiplier, int additive, float sample_size) @@ -1149,7 +1158,7 @@ static void em_combineMaps(EmissionMap *output, EmissionMap *em2, int hires_mult } } /* allocate output map */ - em_allocateData(output, (em1.velocity || em2->velocity), hires_multiplier); + em_allocateData(output, (em1.velocity || em2->velocity), (em1.color || em2->color), hires_multiplier); /* base resolution inputs */ for (x = output->min[0]; x < output->max[0]; x++) @@ -1168,6 +1177,9 @@ static void em_combineMaps(EmissionMap *output, EmissionMap *em2, int hires_mult if (output->velocity && em1.velocity) { copy_v3_v3(&output->velocity[index_out * 3], &em1.velocity[index_in * 3]); } + if (output->color && em1.color) { + copy_v3_v3(&output->color[index_out * 3], &em1.color[index_in * 3]); + } } /* apply second input if in range */ @@ -1189,6 +1201,13 @@ static void em_combineMaps(EmissionMap *output, EmissionMap *em2, int hires_mult output->velocity[index_out * 3 + 1] = ADD_IF_LOWER(output->velocity[index_out * 3 + 1], em2->velocity[index_in * 3 + 1]); output->velocity[index_out * 3 + 2] = ADD_IF_LOWER(output->velocity[index_out * 3 + 2], em2->velocity[index_in * 3 + 2]); } + if (output->color && em2->color) { + /* mix by influence */ + float f1 = output->influence[index_out]; + float f2 = em2->influence[index_in]; + float f = f1 + f2 > 0.0f ? f1 / (f1 + f2) : 0.5f; + interp_v3_v3v3(&output->color[index_out * 3], &output->color[index_out * 3], &em2->color[index_in * 3], f); + } } } // low res loop @@ -1240,6 +1259,7 @@ static void emit_from_particles(Object *flow_ob, SmokeDomainSettings *sds, Smoke ParticleSystem *psys = sfs->psys; float *particle_pos; float *particle_vel; + float *particle_texcol; int totpart = psys->totpart, totchild; int p = 0; int valid_particles = 0; @@ -1286,6 +1306,11 @@ static void emit_from_particles(Object *flow_ob, SmokeDomainSettings *sds, Smoke bounds_margin = (int)ceil(solid + smooth); } + if (sfs->flags & MOD_SMOKE_FLOW_USE_PART_TEXCOLOR) + particle_texcol = MEM_callocN(sizeof(float) * (totpart + totchild) * 3, "smoke_flow_particles"); + else + particle_texcol = NULL; + /* calculate local position for each particle */ for (p = 0; p < totpart + totchild; p++) { @@ -1319,6 +1344,16 @@ static void emit_from_particles(Object *flow_ob, SmokeDomainSettings *sds, Smoke BLI_kdtree_insert(tree, valid_particles, pos); } + if (particle_texcol) { + if (p < totpart) { + ParticleTexture ptex; + psys_get_texture(&sim, &psys->particles[p], &ptex, PAMAP_COLOR, state.time); + copy_v3_v3(&particle_texcol[valid_particles * 3], ptex.color); + } + else + zero_v3(&particle_texcol[valid_particles * 3]); + } + /* calculate emission map bounds */ em_boundInsert(em, pos); valid_particles++; @@ -1326,7 +1361,7 @@ static void emit_from_particles(Object *flow_ob, SmokeDomainSettings *sds, Smoke /* set emission map */ clampBoundsInDomain(sds, em->min, em->max, NULL, NULL, bounds_margin, dt); - em_allocateData(em, sfs->flags & MOD_SMOKE_FLOW_INITVELOCITY, hires_multiplier); + em_allocateData(em, sfs->flags & MOD_SMOKE_FLOW_INITVELOCITY, sfs->flags & MOD_SMOKE_FLOW_USE_PART_TEXCOLOR, hires_multiplier); if (!(sfs->flags & MOD_SMOKE_FLOW_USE_PART_SIZE)) { for (p = 0; p < valid_particles; p++) @@ -1358,6 +1393,9 @@ static void emit_from_particles(Object *flow_ob, SmokeDomainSettings *sds, Smoke { VECADDFAC(&em->velocity[index * 3], &em->velocity[index * 3], &particle_vel[p * 3], sfs->vel_multi); } + if (particle_texcol && em->color) { + copy_v3_v3(&em->color[index * 3], &particle_texcol[p * 3]); + } } // particles loop } else if (valid_particles > 0) { // MOD_SMOKE_FLOW_USE_PART_SIZE @@ -1407,6 +1445,9 @@ static void emit_from_particles(Object *flow_ob, SmokeDomainSettings *sds, Smoke { VECADDFAC(&em->velocity[index * 3], &em->velocity[index * 3], &particle_vel[nearest.index * 3], sfs->vel_multi); } + if (particle_texcol && em->color) { + copy_v3_v3(&em->color[index * 3], &particle_texcol[nearest.index * 3]); + } } } @@ -1444,6 +1485,8 @@ static void emit_from_particles(Object *flow_ob, SmokeDomainSettings *sds, Smoke MEM_freeN(particle_pos); if (particle_vel) MEM_freeN(particle_vel); + if (particle_texcol) + MEM_freeN(particle_texcol); } } @@ -1657,7 +1700,7 @@ static void emit_from_derivedmesh(Object *flow_ob, SmokeDomainSettings *sds, Smo /* set emission map */ clampBoundsInDomain(sds, em->min, em->max, NULL, NULL, (int)ceil(sfs->surface_distance), dt); - em_allocateData(em, sfs->flags & MOD_SMOKE_FLOW_INITVELOCITY, hires_multiplier); + em_allocateData(em, sfs->flags & MOD_SMOKE_FLOW_INITVELOCITY, false, hires_multiplier); /* setup loop bounds */ for (i = 0; i < 3; i++) { @@ -1999,7 +2042,7 @@ BLI_INLINE void apply_outflow_fields(int index, float *density, float *heat, flo } } -BLI_INLINE void apply_inflow_fields(SmokeFlowSettings *sfs, float emission_value, int index, float *density, float *heat, float *fuel, float *react, float *color_r, float *color_g, float *color_b) +BLI_INLINE void apply_inflow_fields(SmokeFlowSettings *sfs, float emission_value, const float *color_value, int index, float *density, float *heat, float *fuel, float *react, float *color_r, float *color_g, float *color_b) { int absolute_flow = (sfs->flags & MOD_SMOKE_FLOW_ABSOLUTE); float dens_old = density[index]; @@ -2036,9 +2079,10 @@ BLI_INLINE void apply_inflow_fields(SmokeFlowSettings *sfs, float emission_value /* set color */ if (color_r && dens_flow) { float total_dens = density[index] / (dens_old + dens_flow); - color_r[index] = (color_r[index] + sfs->color[0] * dens_flow) * total_dens; - color_g[index] = (color_g[index] + sfs->color[1] * dens_flow) * total_dens; - color_b[index] = (color_b[index] + sfs->color[2] * dens_flow) * total_dens; + const float *color = (color_value ? color_value : sfs->color); + color_r[index] = (color_r[index] + color[0] * dens_flow) * total_dens; + color_g[index] = (color_g[index] + color[1] * dens_flow) * total_dens; + color_b[index] = (color_b[index] + color[2] * dens_flow) * total_dens; } /* set fire reaction coordinate */ @@ -2185,7 +2229,10 @@ static void update_flowsfluids(Scene *scene, Object *ob, SmokeDomainSettings *sd } /* activate color field if flows add smoke with varying colors */ if (sfs->type != MOD_SMOKE_FLOW_TYPE_FIRE && sfs->density) { - if (!(active_fields & SM_ACTIVE_COLOR_SET)) { + if (sfs->flags & MOD_SMOKE_FLOW_USE_PART_TEXCOLOR) { + active_fields |= SM_ACTIVE_COLORS; + } + else if (!(active_fields & SM_ACTIVE_COLOR_SET)) { copy_v3_v3(sds->active_color, sfs->color); active_fields |= SM_ACTIVE_COLOR_SET; } @@ -2266,6 +2313,7 @@ static void update_flowsfluids(Scene *scene, Object *ob, SmokeDomainSettings *sd float *velocity_map = em->velocity; float *emission_map = em->influence; float *emission_map_high = em->influence_high; + float *color_map = em->color; int ii, jj, kk, gx, gy, gz, ex, ey, ez, dx, dy, dz, block_size; size_t e_index, d_index, index_big; @@ -2293,7 +2341,7 @@ static void update_flowsfluids(Scene *scene, Object *ob, SmokeDomainSettings *sd apply_outflow_fields(d_index, density, heat, fuel, react, color_r, color_g, color_b); } else { // inflow - apply_inflow_fields(sfs, emission_map[e_index], d_index, density, heat, fuel, react, color_r, color_g, color_b); + apply_inflow_fields(sfs, emission_map[e_index], color_map ? &color_map[e_index * 3] : NULL, d_index, density, heat, fuel, react, color_r, color_g, color_b); /* initial velocity */ if (sfs->flags & MOD_SMOKE_FLOW_INITVELOCITY) { @@ -2307,6 +2355,7 @@ static void update_flowsfluids(Scene *scene, Object *ob, SmokeDomainSettings *sd if (bigdensity) { // neighbor cell emission densities (for high resolution smoke smooth interpolation) float c000, c001, c010, c011, c100, c101, c110, c111; + float col000[3], col001[3], col010[3], col011[3], col100[3], col101[3], col110[3], col111[3]; smoke_turbulence_get_res(sds->wt, bigres); block_size = sds->amplify + 1; // high res block size @@ -2321,14 +2370,67 @@ static void update_flowsfluids(Scene *scene, Object *ob, SmokeDomainSettings *sd c110 = (ez > 0) ? emission_map[smoke_get_index(ex, em->res[0], ey, em->res[1], ez - 1)] : 0; c111 = emission_map[smoke_get_index(ex, em->res[0], ey, em->res[1], ez)]; // this cell + if (color_map) { + static const float Z[3] = {0.0f, 0.0f, 0.0f}; + + copy_v3_v3(col000, (ex > 0 && ey > 0 && ez > 0) ? &color_map[smoke_get_index(ex - 1, em->res[0], ey - 1, em->res[1], ez - 1) * 3] : Z); + copy_v3_v3(col001, (ex > 0 && ey > 0) ? &color_map[smoke_get_index(ex - 1, em->res[0], ey - 1, em->res[1], ez) * 3] : Z); + copy_v3_v3(col010, (ex > 0 && ez > 0) ? &color_map[smoke_get_index(ex - 1, em->res[0], ey, em->res[1], ez - 1) * 3] : Z); + copy_v3_v3(col011, (ex > 0) ? &color_map[smoke_get_index(ex - 1, em->res[0], ey, em->res[1], ez) * 3] : Z); + + copy_v3_v3(col100, (ey > 0 && ez > 0) ? &color_map[smoke_get_index(ex, em->res[0], ey - 1, em->res[1], ez - 1) * 3] : Z); + copy_v3_v3(col101, (ey > 0) ? &color_map[smoke_get_index(ex, em->res[0], ey - 1, em->res[1], ez) * 3] : Z); + copy_v3_v3(col110, (ez > 0) ? &color_map[smoke_get_index(ex, em->res[0], ey, em->res[1], ez - 1) * 3] : Z); + copy_v3_v3(col111, &color_map[smoke_get_index(ex, em->res[0], ey, em->res[1], ez) * 3]); // this cell + } + else { + zero_v3(col000); + zero_v3(col001); + zero_v3(col010); + zero_v3(col011); + zero_v3(col100); + zero_v3(col101); + zero_v3(col110); + zero_v3(col111); + } + for (ii = 0; ii < block_size; ii++) for (jj = 0; jj < block_size; jj++) for (kk = 0; kk < block_size; kk++) { - float fx, fy, fz, interpolated_value; + float col[3], interpolated_value, *interpolated_color; int shift_x = 0, shift_y = 0, shift_z = 0; + float w[2][2][2]; + + bool do_interpolation = ((!((sds->highres_sampling == SM_HRES_FULLSAMPLE) && emission_map_high) && + !(sds->highres_sampling == SM_HRES_NEAREST)) || + color_map); + /* weights are used for both density and color, + * so calculate them once in advance + */ + if (do_interpolation) { + /* get relative block position + * for interpolation smoothing */ + float fx = (float)ii / block_size + 0.5f / block_size; + float fy = (float)jj / block_size + 0.5f / block_size; + float fz = (float)kk / block_size + 0.5f / block_size; + float mx = 1.0f - fx; + float my = 1.0f - fy; + float mz = 1.0f - fz; + + w[0][0][0] = mx * my * mz; + w[1][0][0] = fx * my * mz; + w[0][1][0] = mx * fy * mz; + w[1][1][0] = fx * fy * mz; + w[0][0][1] = mx * my * fz; + w[1][0][1] = fx * my * fz; + w[0][1][1] = mx * fy * fz; + w[1][1][1] = fx * fy * fz; + } + else + memset(w, 0, sizeof(float) * 8); /* Use full sample emission map if enabled and available */ if ((sds->highres_sampling == SM_HRES_FULLSAMPLE) && emission_map_high) { @@ -2342,22 +2444,17 @@ static void update_flowsfluids(Scene *scene, Object *ob, SmokeDomainSettings *sd /* Fall back to interpolated */ else { - /* get relative block position - * for interpolation smoothing */ - fx = (float)ii / block_size + 0.5f / block_size; - fy = (float)jj / block_size + 0.5f / block_size; - fz = (float)kk / block_size + 0.5f / block_size; /* calculate trilinear interpolation */ - interpolated_value = c000 * (1 - fx) * (1 - fy) * (1 - fz) + - c100 * fx * (1 - fy) * (1 - fz) + - c010 * (1 - fx) * fy * (1 - fz) + - c001 * (1 - fx) * (1 - fy) * fz + - c101 * fx * (1 - fy) * fz + - c011 * (1 - fx) * fy * fz + - c110 * fx * fy * (1 - fz) + - c111 * fx * fy * fz; - + interpolated_value = 0.0f; + interpolated_value += c000 * w[0][0][0]; + interpolated_value += c100 * w[1][0][0]; + interpolated_value += c010 * w[0][1][0]; + interpolated_value += c110 * w[1][1][0]; + interpolated_value += c001 * w[0][0][1]; + interpolated_value += c101 * w[1][0][1]; + interpolated_value += c011 * w[0][1][1]; + interpolated_value += c111 * w[1][1][1]; /* add some contrast / sharpness * depending on hi-res block size */ @@ -2371,6 +2468,48 @@ static void update_flowsfluids(Scene *scene, Object *ob, SmokeDomainSettings *sd shift_y = (dy < 1) ? 0 : block_size / 2; shift_z = (dz < 1) ? 0 : block_size / 2; } + + if (color_map) { + float wcol[2][2][2], totw, invtotw; + + /* colors are zero (black) in zero-emission cells, + * so use weighted average based on density to avoid artifacts! + */ + wcol[0][0][0] = w[0][0][0] * c000; + wcol[1][0][0] = w[1][0][0] * c100; + wcol[0][1][0] = w[0][1][0] * c010; + wcol[1][1][0] = w[1][1][0] * c110; + wcol[0][0][1] = w[0][0][1] * c001; + wcol[1][0][1] = w[1][0][1] * c101; + wcol[0][1][1] = w[0][1][1] * c011; + wcol[1][1][1] = w[1][1][1] * c111; + + totw = wcol[0][0][0] + wcol[1][0][0] + wcol[0][1][0] + wcol[1][1][0] + + wcol[0][0][1] + wcol[1][0][1] + wcol[0][1][1] + wcol[1][1][1]; + invtotw = totw > 0.0f ? 1.0f/totw : 0.0f; + wcol[0][0][0] *= invtotw; + wcol[1][0][0] *= invtotw; + wcol[0][1][0] *= invtotw; + wcol[1][1][0] *= invtotw; + wcol[0][0][1] *= invtotw; + wcol[1][0][1] *= invtotw; + wcol[0][1][1] *= invtotw; + wcol[1][1][1] *= invtotw; + + zero_v3(col); + madd_v3_v3fl(col, col000, wcol[0][0][0]); + madd_v3_v3fl(col, col100, wcol[1][0][0]); + madd_v3_v3fl(col, col010, wcol[0][1][0]); + madd_v3_v3fl(col, col110, wcol[1][1][0]); + madd_v3_v3fl(col, col001, wcol[0][0][1]); + madd_v3_v3fl(col, col101, wcol[1][0][1]); + madd_v3_v3fl(col, col011, wcol[0][1][1]); + madd_v3_v3fl(col, col111, wcol[1][1][1]); + + interpolated_color = col; + } + else + interpolated_color = NULL; /* get shifted index for current high resolution block */ index_big = smoke_get_index(block_size * dx + ii - shift_x, bigres[0], block_size * dy + jj - shift_y, bigres[1], block_size * dz + kk - shift_z); @@ -2381,7 +2520,7 @@ static void update_flowsfluids(Scene *scene, Object *ob, SmokeDomainSettings *sd } } else { // inflow - apply_inflow_fields(sfs, interpolated_value, index_big, bigdensity, NULL, bigfuel, bigreact, bigcolor_r, bigcolor_g, bigcolor_b); + apply_inflow_fields(sfs, interpolated_value, interpolated_color, index_big, bigdensity, NULL, bigfuel, bigreact, bigcolor_r, bigcolor_g, bigcolor_b); } } // hires loop } // bigdensity @@ -2405,7 +2544,7 @@ static void update_effectors(Scene *scene, Object *ob, SmokeDomainSettings *sds, ListBase *effectors; /* make sure smoke flow influence is 0.0f */ sds->effector_weights->weight[PFIELD_SMOKEFLOW] = 0.0f; - effectors = pdInitEffectors(scene, ob, NULL, sds->effector_weights, true); + effectors = pdInitEffectors(scene, ob, NULL, sds->effector_weights); if (effectors) { @@ -2688,7 +2827,7 @@ static void smokeModifier_process(SmokeModifierData *smd, Scene *scene, Object * int startframe, endframe, framenr; float timescale; - framenr = scene->r.cfra; + framenr = scene->r.cfra - sds->point_cache_offset; //printf("time: %d\n", scene->r.cfra); @@ -2779,6 +2918,7 @@ static void smokeModifier_process(SmokeModifierData *smd, Scene *scene, Object * if (sds->wt) { + smoke_ensure_simulation(sds->fluid, sds->wt); smoke_turbulence_step(sds->wt, sds->fluid); } diff --git a/source/blender/blenkernel/intern/softbody.c b/source/blender/blenkernel/intern/softbody.c index 607f89699a4..dc5b77a1bf4 100644 --- a/source/blender/blenkernel/intern/softbody.c +++ b/source/blender/blenkernel/intern/softbody.c @@ -1647,7 +1647,7 @@ static void scan_for_ext_spring_forces(Scene *scene, Object *ob, float timenow) SoftBody *sb = ob->soft; ListBase *do_effector = NULL; - do_effector = pdInitEffectors(scene, ob, NULL, sb->effector_weights, true); + do_effector = pdInitEffectors(scene, ob, NULL, sb->effector_weights); _scan_for_ext_spring_forces(scene, ob, timenow, 0, sb->totspring, do_effector); pdEndEffectors(&do_effector); } @@ -1667,7 +1667,7 @@ static void sb_sfesf_threads_run(Scene *scene, struct Object *ob, float timenow, int i, totthread, left, dec; int lowsprings =100; /* wild guess .. may increase with better thread management 'above' or even be UI option sb->spawn_cf_threads_nopts */ - do_effector= pdInitEffectors(scene, ob, NULL, ob->soft->effector_weights, true); + do_effector= pdInitEffectors(scene, ob, NULL, ob->soft->effector_weights); /* figure the number of threads while preventing pretty pointless threading overhead */ totthread= BKE_scene_num_threads(scene); @@ -2475,7 +2475,7 @@ static void softbody_calc_forcesEx(Scene *scene, Object *ob, float forcetime, fl sb_sfesf_threads_run(scene, ob, timenow, sb->totspring, NULL); /* after spring scan because it uses Effoctors too */ - do_effector= pdInitEffectors(scene, ob, NULL, sb->effector_weights, true); + do_effector= pdInitEffectors(scene, ob, NULL, sb->effector_weights); if (do_deflector) { float defforce[3]; @@ -2550,7 +2550,7 @@ static void softbody_calc_forces(Scene *scene, Object *ob, float forcetime, floa if (do_springcollision || do_aero) scan_for_ext_spring_forces(scene, ob, timenow); /* after spring scan because it uses Effoctors too */ - do_effector= pdInitEffectors(scene, ob, NULL, ob->soft->effector_weights, true); + do_effector= pdInitEffectors(scene, ob, NULL, ob->soft->effector_weights); if (do_deflector) { float defforce[3]; diff --git a/source/blender/blenkernel/intern/sound.c b/source/blender/blenkernel/intern/sound.c index 46a50917427..81cc55f754d 100644 --- a/source/blender/blenkernel/intern/sound.c +++ b/source/blender/blenkernel/intern/sound.c @@ -604,7 +604,7 @@ void BKE_sound_seek_scene(struct Main *bmain, struct Scene *scene) } } - if (scene->audio.flag & AUDIO_SCRUB && !animation_playing) { + if ((scene->audio.flag & AUDIO_SCRUB) && !animation_playing) { if (scene->audio.flag & AUDIO_SYNC) { AUD_seek(scene->playback_handle, cur_time); AUD_seekSequencer(scene->playback_handle, cur_time); diff --git a/source/blender/blenkernel/intern/strands.c b/source/blender/blenkernel/intern/strands.c new file mode 100644 index 00000000000..38d35a7309e --- /dev/null +++ b/source/blender/blenkernel/intern/strands.c @@ -0,0 +1,407 @@ +/* + * Copyright 2015, Blender Foundation. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "MEM_guardedalloc.h" + +#include "BLI_math.h" +#include "BLI_string.h" + +#include "BKE_strands.h" + +Strands *BKE_strands_new(int curves, int verts) +{ + Strands *strands = MEM_mallocN(sizeof(Strands), "strands"); + + strands->totcurves = curves; + strands->curves = MEM_mallocN(sizeof(StrandsCurve) * curves, "strand curves"); + + strands->totverts = verts; + strands->verts = MEM_mallocN(sizeof(StrandsVertex) * verts, "strand vertices"); + + /* must be added explicitly */ + strands->state = NULL; + + return strands; +} + +Strands *BKE_strands_copy(Strands *strands) +{ + Strands *new_strands = MEM_dupallocN(strands); + if (new_strands->curves) + new_strands->curves = MEM_dupallocN(new_strands->curves); + if (new_strands->verts) + new_strands->verts = MEM_dupallocN(new_strands->verts); + if (new_strands->state) + new_strands->state = MEM_dupallocN(new_strands->state); + return new_strands; +} + +void BKE_strands_free(Strands *strands) +{ + if (strands) { + if (strands->curves) + MEM_freeN(strands->curves); + if (strands->verts) + MEM_freeN(strands->verts); + if (strands->state) + MEM_freeN(strands->state); + MEM_freeN(strands); + } +} + +/* copy the rest positions to initialize the motion state */ +void BKE_strands_state_copy_rest_positions(Strands *strands) +{ + if (strands->state) { + int i; + for (i = 0; i < strands->totverts; ++i) { + copy_v3_v3(strands->state[i].co, strands->verts[i].co); + } + } +} + +/* copy the rest positions to initialize the motion state */ +void BKE_strands_state_clear_velocities(Strands *strands) +{ + if (strands->state) { + int i; + + for (i = 0; i < strands->totverts; ++i) { + zero_v3(strands->state[i].vel); + } + } +} + +void BKE_strands_add_motion_state(Strands *strands) +{ + if (!strands->state) { + int i; + + strands->state = MEM_mallocN(sizeof(StrandsMotionState) * strands->totverts, "strand motion states"); + + BKE_strands_state_copy_rest_positions(strands); + BKE_strands_state_clear_velocities(strands); + + /* initialize normals */ + for (i = 0; i < strands->totverts; ++i) { + copy_v3_v3(strands->state[i].nor, strands->verts[i].nor); + } + } +} + +void BKE_strands_remove_motion_state(Strands *strands) +{ + if (strands) { + if (strands->state) { + MEM_freeN(strands->state); + strands->state = NULL; + } + } +} + +static void calc_normals(Strands *strands, bool use_motion_state) +{ + StrandIterator it_strand; + for (BKE_strand_iter_init(&it_strand, strands); BKE_strand_iter_valid(&it_strand); BKE_strand_iter_next(&it_strand)) { + StrandEdgeIterator it_edge; + int numverts = it_strand.curve->numverts; + if (use_motion_state) { + for (BKE_strand_edge_iter_init(&it_edge, &it_strand); BKE_strand_edge_iter_valid(&it_edge); BKE_strand_edge_iter_next(&it_edge)) { + sub_v3_v3v3(it_edge.state0->nor, it_edge.state1->co, it_edge.state0->co); + normalize_v3(it_edge.state0->nor); + } + if (numverts > 1) + copy_v3_v3(it_strand.state[numverts-1].nor, it_strand.state[numverts-2].nor); + } + else { + for (BKE_strand_edge_iter_init(&it_edge, &it_strand); BKE_strand_edge_iter_valid(&it_edge); BKE_strand_edge_iter_next(&it_edge)) { + sub_v3_v3v3(it_edge.vertex0->nor, it_edge.vertex1->co, it_edge.vertex0->co); + normalize_v3(it_edge.vertex0->nor); + } + if (numverts > 1) + copy_v3_v3(it_strand.verts[numverts-1].nor, it_strand.verts[numverts-2].nor); + } + } +} + +void BKE_strands_ensure_normals(Strands *strands) +{ + const bool use_motion_state = (strands->state); + + calc_normals(strands, false); + + if (use_motion_state) + calc_normals(strands, true); +} + +void BKE_strands_get_minmax(Strands *strands, float min[3], float max[3], bool use_motion_state) +{ + int numverts = strands->totverts; + int i; + + if (use_motion_state && strands->state) { + for (i = 0; i < numverts; ++i) { + minmax_v3v3_v3(min, max, strands->state[i].co); + } + } + else { + for (i = 0; i < numverts; ++i) { + minmax_v3v3_v3(min, max, strands->verts[i].co); + } + } +} + +/* ------------------------------------------------------------------------- */ + +StrandsChildren *BKE_strands_children_new(int curves, int verts) +{ + StrandsChildren *strands = MEM_mallocN(sizeof(StrandsChildren), "strands children"); + + strands->totcurves = curves; + strands->curves = MEM_mallocN(sizeof(StrandsChildCurve) * curves, "strand children curves"); + + strands->totverts = verts; + strands->verts = MEM_mallocN(sizeof(StrandsChildVertex) * verts, "strand children vertices"); + + /* must be added explicitly */ + strands->curve_uvs = NULL; + strands->numuv = 0; + strands->curve_vcols = NULL; + strands->numvcol = 0; + + return strands; +} + +StrandsChildren *BKE_strands_children_copy(StrandsChildren *strands) +{ + StrandsChildren *new_strands = MEM_dupallocN(strands); + if (new_strands->curves) + new_strands->curves = MEM_dupallocN(new_strands->curves); + if (new_strands->curve_uvs) + new_strands->curve_uvs = MEM_dupallocN(new_strands->curve_uvs); + if (new_strands->curve_vcols) + new_strands->curve_vcols = MEM_dupallocN(new_strands->curve_vcols); + if (new_strands->verts) + new_strands->verts = MEM_dupallocN(new_strands->verts); + return new_strands; +} + +void BKE_strands_children_free(StrandsChildren *strands) +{ + if (strands) { + if (strands->curves) + MEM_freeN(strands->curves); + if (strands->curve_uvs) + MEM_freeN(strands->curve_uvs); + if (strands->curve_vcols) + MEM_freeN(strands->curve_vcols); + if (strands->verts) + MEM_freeN(strands->verts); + MEM_freeN(strands); + } +} + +void BKE_strands_children_add_uvs(StrandsChildren *strands, int num_layers) +{ + if (strands->curve_uvs && strands->numuv != num_layers) { + MEM_freeN(strands->curve_uvs); + strands->curve_uvs = NULL; + strands->numuv = 0; + } + + if (!strands->curve_uvs) { + strands->curve_uvs = MEM_callocN(sizeof(StrandsChildCurveUV) * strands->totcurves * num_layers, "strands children uv layers"); + strands->numuv = num_layers; + } +} + +void BKE_strands_children_add_vcols(StrandsChildren *strands, int num_layers) +{ + if (strands->curve_vcols && strands->numvcol != num_layers) { + MEM_freeN(strands->curve_vcols); + strands->curve_vcols = NULL; + strands->numvcol = 0; + } + + if (!strands->curve_vcols) { + strands->curve_vcols = MEM_callocN(sizeof(StrandsChildCurveVCol) * strands->totcurves * num_layers, "strands children vcol layers"); + strands->numvcol = num_layers; + } +} + +static int *strands_calc_vertex_start(Strands *strands) +{ + int *vertstart = MEM_mallocN(sizeof(int) * strands->totcurves, "strand curves vertex start"); + StrandIterator it_strand; + int start; + + start = 0; + for (BKE_strand_iter_init(&it_strand, strands); BKE_strand_iter_valid(&it_strand); BKE_strand_iter_next(&it_strand)) { + vertstart[it_strand.index] = start; + start += it_strand.curve->numverts; + } + + return vertstart; +} + + +/* 'out' is an optional array to write final positions to, instead of writing back to vertex locations. + * It must be at least as large as the number of vertices. + */ +static void strands_children_strand_deform(StrandChildIterator *it_strand, Strands *parents, int *vertstart, bool use_motion, float (*out)[3]) +{ + int i; + + if (!parents || !vertstart) + return; + + if (!parents->state) + use_motion = false; + + for (i = 0; i < 4; ++i) { + int p = it_strand->curve->parents[i]; + float w = it_strand->curve->parent_weights[i]; + if (p >= 0 && w > 0.0f) { + StrandsCurve *parent = &parents->curves[p]; + StrandsVertex *pverts; + StrandsMotionState *pstate; + int pv0, pv1; + StrandChildVertexIterator it_vert; + + if (parent->numverts <= 0) + continue; + + pverts = &parents->verts[vertstart[p]]; + pstate = &parents->state[vertstart[p]]; + pv0 = 0; + for (BKE_strand_child_vertex_iter_init(&it_vert, it_strand); BKE_strand_child_vertex_iter_valid(&it_vert); BKE_strand_child_vertex_iter_next(&it_vert)) { + float time = it_vert.vertex->time; + float dt, x; + float poffset0[3], poffset1[3], offset[3]; + + /* advance to the matching parent edge for interpolation */ + while (pv0 < parent->numverts-1 && pverts[pv0+1].time < time) + ++pv0; + pv1 = (pv0 < parent->numverts-1)? pv0+1 : pv0; + + if (use_motion) { + sub_v3_v3v3(poffset0, pstate[pv0].co, pverts[pv0].base); + sub_v3_v3v3(poffset1, pstate[pv1].co, pverts[pv1].base); + } + else { + sub_v3_v3v3(poffset0, pverts[pv0].co, pverts[pv0].base); + sub_v3_v3v3(poffset1, pverts[pv1].co, pverts[pv1].base); + } + + dt = pverts[pv1].time - pverts[pv0].time; + x = dt > 0.0f ? (time - pverts[pv0].time) / dt : 0.0f; + CLAMP(x, 0.0f, 1.0f); + interp_v3_v3v3(offset, poffset0, poffset1, x); + + if (out) + madd_v3_v3fl(out[it_vert.index], offset, w); + else + madd_v3_v3fl(it_vert.vertex->co, offset, w); + } + } + } +} + +void BKE_strands_children_deform(StrandsChildren *strands, Strands *parents, bool use_motion) +{ + int *vertstart = NULL; + StrandChildIterator it_strand; + + if (parents) + vertstart = strands_calc_vertex_start(parents); + + for (BKE_strand_child_iter_init(&it_strand, strands); BKE_strand_child_iter_valid(&it_strand); BKE_strand_child_iter_next(&it_strand)) { + /* move child strands from their local root space to object space */ + StrandChildVertexIterator it_vert; + for (BKE_strand_child_vertex_iter_init(&it_vert, &it_strand); BKE_strand_child_vertex_iter_valid(&it_vert); BKE_strand_child_vertex_iter_next(&it_vert)) { + mul_v3_m4v3(it_vert.vertex->co, it_strand.curve->root_matrix, it_vert.vertex->base); + } + + strands_children_strand_deform(&it_strand, parents, vertstart, use_motion, NULL); + } + + if (vertstart) + MEM_freeN(vertstart); +} + +static void calc_child_normals(StrandsChildren *strands) +{ + StrandChildIterator it_strand; + for (BKE_strand_child_iter_init(&it_strand, strands); BKE_strand_child_iter_valid(&it_strand); BKE_strand_child_iter_next(&it_strand)) { + StrandChildEdgeIterator it_edge; + int numverts = it_strand.curve->numverts; + for (BKE_strand_child_edge_iter_init(&it_edge, &it_strand); BKE_strand_child_edge_iter_valid(&it_edge); BKE_strand_child_edge_iter_next(&it_edge)) { + sub_v3_v3v3(it_edge.vertex0->nor, it_edge.vertex1->co, it_edge.vertex0->co); + normalize_v3(it_edge.vertex0->nor); + } + if (numverts > 1) + copy_v3_v3(it_strand.verts[numverts-1].nor, it_strand.verts[numverts-2].nor); + } +} + +void BKE_strands_children_ensure_normals(StrandsChildren *strands) +{ + calc_child_normals(strands); +} + +void BKE_strands_children_get_minmax(StrandsChildren *strands, float min[3], float max[3]) +{ + int numverts = strands->totverts; + int i; + + for (i = 0; i < numverts; ++i) { + minmax_v3v3_v3(min, max, strands->verts[i].co); + } +} + +/* ------------------------------------------------------------------------- */ + +void BKE_strand_bend_iter_transform_rest(StrandBendIterator *iter, float mat[3][3]) +{ + float dir0[3], dir1[3]; + + sub_v3_v3v3(dir0, iter->vertex1->co, iter->vertex0->co); + sub_v3_v3v3(dir1, iter->vertex2->co, iter->vertex1->co); + normalize_v3(dir0); + normalize_v3(dir1); + + /* rotation between segments */ + rotation_between_vecs_to_mat3(mat, dir0, dir1); +} + +void BKE_strand_bend_iter_transform_state(StrandBendIterator *iter, float mat[3][3]) +{ + if (iter->state0) { + float dir0[3], dir1[3]; + + sub_v3_v3v3(dir0, iter->state1->co, iter->state0->co); + sub_v3_v3v3(dir1, iter->state2->co, iter->state1->co); + normalize_v3(dir0); + normalize_v3(dir1); + + /* rotation between segments */ + rotation_between_vecs_to_mat3(mat, dir0, dir1); + } + else + unit_m3(mat); +} diff --git a/source/blender/blenkernel/intern/subsurf_ccg.c b/source/blender/blenkernel/intern/subsurf_ccg.c index e8bca50c3d7..16f218d5fb6 100644 --- a/source/blender/blenkernel/intern/subsurf_ccg.c +++ b/source/blender/blenkernel/intern/subsurf_ccg.c @@ -72,6 +72,7 @@ #include "GPU_draw.h" #include "GPU_extensions.h" #include "GPU_glew.h" +#include "GPU_buffers.h" #include "CCGSubSurf.h" @@ -1734,12 +1735,25 @@ static void ccgDM_drawLooseEdges(DerivedMesh *dm) } } +static void ccgDM_NormalFast(float *a, float *b, float *c, float *d, float no[3]) +{ + 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]; + + 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; + + normalize_v3(no); +} + + 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; @@ -1749,7 +1763,8 @@ static void ccgDM_glNormalFast(float *a, float *b, float *c, float *d) } /* Only used by non-editmesh types */ -static void ccgDM_drawFacesSolid(DerivedMesh *dm, float (*partial_redraw_planes)[4], bool fast, DMSetMaterial setMaterial) +static void ccgDM_prepare_normal_data(DerivedMesh *dm, float *varray, int *vindex, + int *mat_orig_to_new, void *UNUSED(user_data)) { CCGDerivedMesh *ccgdm = (CCGDerivedMesh *) dm; CCGSubSurf *ss = ccgdm->ss; @@ -1758,13 +1773,277 @@ static void ccgDM_drawFacesSolid(DerivedMesh *dm, float (*partial_redraw_planes) int gridSize = ccgSubSurf_getGridSize(ss); int gridFaces = gridSize - 1; DMFlagMat *faceFlags = ccgdm->faceFlags; - int step = (fast) ? gridSize - 1 : 1; int i, totface = ccgSubSurf_getNumFaces(ss); - int drawcurrent = 0, matnr = -1, shademodel = -1; + int matnr, shademodel; + int start; CCG_key_top_level(&key, ss); ccgdm_pbvh_update(ccgdm); + for (i = 0; i < totface; i++) { + CCGFace *f = ccgdm->faceMap[i].face; + int S, x, y, numVerts = ccgSubSurf_getFaceNumVerts(f); + int index = GET_INT_FROM_POINTER(ccgSubSurf_getFaceFaceHandle(f)); + short (*ln)[4][3] = NULL; + + if (faceFlags) { + shademodel = (lnors || (faceFlags[index].flag & ME_SMOOTH)) ? GL_SMOOTH : GL_FLAT; + matnr = faceFlags[index].mat_nr; + } + else { + shademodel = GL_SMOOTH; + matnr = 0; + } + + if (lnors) { + ln = lnors; + lnors += gridFaces * gridFaces * numVerts; + } + + for (S = 0; S < numVerts; S++) { + CCGElem *faceGridData = ccgSubSurf_getFaceGridDataArray(ss, f, S); + + if (ln) { + /* Can't use quad strips here... */ + for (y = 0; y < gridFaces; y ++) { + for (x = 0; x < gridFaces; x ++) { + start = vindex[mat_orig_to_new[matnr]]; + + normal_short_to_float_v3(&varray[start], ln[0][1]); + normal_short_to_float_v3(&varray[start + 3], ln[0][2]); + normal_short_to_float_v3(&varray[start + 6], ln[0][3]); + + normal_short_to_float_v3(&varray[start + 9], ln[0][3]); + normal_short_to_float_v3(&varray[start + 12], ln[0][1]); + normal_short_to_float_v3(&varray[start + 15], ln[0][0]); + + vindex[mat_orig_to_new[matnr]] += 18; + + ln ++; + } + } + } + else if (shademodel == GL_SMOOTH) { + for (y = 0; y < gridFaces; y ++) { + for (x = 0; x < gridFaces; x ++) { + float *a = CCG_grid_elem_no(&key, faceGridData, x, y ); + float *b = CCG_grid_elem_no(&key, faceGridData, x + 1, y); + float *c = CCG_grid_elem_no(&key, faceGridData, x + 1, y + 1); + float *d = CCG_grid_elem_no(&key, faceGridData, x, y + 1); + + start = vindex[mat_orig_to_new[matnr]]; + + copy_v3_v3(&varray[start], d); + copy_v3_v3(&varray[start + 3], c); + copy_v3_v3(&varray[start + 6], b); + + copy_v3_v3(&varray[start + 9], d); + copy_v3_v3(&varray[start + 12], b); + copy_v3_v3(&varray[start + 15], a); + + vindex[mat_orig_to_new[matnr]] += 18; + } + } + } + else { + for (y = 0; y < gridFaces; y ++) { + for (x = 0; x < gridFaces; x ++) { + float no[3]; + float *a = CCG_grid_elem_co(&key, faceGridData, x, y ); + float *b = CCG_grid_elem_co(&key, faceGridData, x + 1, y ); + float *c = CCG_grid_elem_co(&key, faceGridData, x + 1, y + 1); + float *d = CCG_grid_elem_co(&key, faceGridData, x, y + 1); + + ccgDM_NormalFast(a, b, c, d, no); + + start = vindex[mat_orig_to_new[matnr]]; + + copy_v3_v3(&varray[start], no); + copy_v3_v3(&varray[start + 3], no); + copy_v3_v3(&varray[start + 6], no); + + copy_v3_v3(&varray[start + 9], no); + copy_v3_v3(&varray[start + 12], no); + copy_v3_v3(&varray[start + 15], no); + + vindex[mat_orig_to_new[matnr]] += 18; + } + } + } + } + } +} + +/* Only used by non-editmesh types */ +static void ccgDM_prepare_vertex_data(DerivedMesh *dm, float *varray, int *vindex, + int *mat_orig_to_new, void *UNUSED(user_data)) +{ + CCGDerivedMesh *ccgdm = (CCGDerivedMesh *) dm; + CCGSubSurf *ss = ccgdm->ss; + CCGKey key; + int gridSize = ccgSubSurf_getGridSize(ss); + int gridFaces = gridSize - 1; + DMFlagMat *faceFlags = ccgdm->faceFlags; + int i, totface = ccgSubSurf_getNumFaces(ss); + int matnr = -1, start; + + CCG_key_top_level(&key, ss); + ccgdm_pbvh_update(ccgdm); + + for (i = 0; i < totface; i++) { + CCGFace *f = ccgdm->faceMap[i].face; + int S, x, y, numVerts = ccgSubSurf_getFaceNumVerts(f); + int index = GET_INT_FROM_POINTER(ccgSubSurf_getFaceFaceHandle(f)); + + if (faceFlags) { + matnr = faceFlags[index].mat_nr; + } + else { + matnr = 0; + } + + for (S = 0; S < numVerts; S++) { + CCGElem *faceGridData = ccgSubSurf_getFaceGridDataArray(ss, f, S); + for (y = 0; y < gridFaces; y++) { + for (x = 0; x < gridFaces; x++) { + float *a = CCG_grid_elem_co(&key, faceGridData, x, y); + float *b = CCG_grid_elem_co(&key, faceGridData, x + 1, y); + float *c = CCG_grid_elem_co(&key, faceGridData, x + 1, y + 1); + float *d = CCG_grid_elem_co(&key, faceGridData, x, y + 1); + + start = vindex[mat_orig_to_new[matnr]]; + + copy_v3_v3(&varray[start], d); + copy_v3_v3(&varray[start + 3], c); + copy_v3_v3(&varray[start + 6], b); + + copy_v3_v3(&varray[start + 9], d); + copy_v3_v3(&varray[start + 12], b); + copy_v3_v3(&varray[start + 15], a); + + vindex[mat_orig_to_new[matnr]] += 18; + } + } + } + } +} + +static void ccgDM_copy_gpu_data(DerivedMesh *dm, int type, float *varray, int *index, + int *mat_orig_to_new, void *UNUSED(user_data)) +{ + switch(type) { + case GPU_BUFFER_VERTEX: + ccgDM_prepare_vertex_data(dm, varray, index, mat_orig_to_new, NULL); + break; + case GPU_BUFFER_NORMAL: + ccgDM_prepare_normal_data(dm, varray, index, mat_orig_to_new, NULL); + break; + default: + break; + } +} + +static GPUDrawObject *ccgDM_GPUObjectNew(DerivedMesh *dm) { +// GPUBufferMaterial *mat; + int *mat_orig_to_new; + CCGDerivedMesh *ccgdm = (CCGDerivedMesh *) dm; + CCGSubSurf *ss = ccgdm->ss; + GPUDrawObject *gdo; + DMFlagMat *faceFlags = ccgdm->faceFlags; + int gridSize = ccgSubSurf_getGridSize(ss); + int gridFaces = gridSize - 1; + int totmat = (faceFlags) ? dm->totmat : 1; + int *points_per_mat; + int i, curmat, curpoint, totface; + + /* object contains at least one material (default included) so zero means uninitialized dm */ + BLI_assert(totmat != 0); + + totface = ccgSubSurf_getNumFaces(ss); + + points_per_mat = MEM_callocN(sizeof(*points_per_mat) * totmat, "GPU_drawobject_new.mat_orig_to_new"); + + if (faceFlags) { + for (i = 0; i < totface; i++) { + CCGFace *f = ccgdm->faceMap[i].face; + int numVerts = ccgSubSurf_getFaceNumVerts(f); + int index = GET_INT_FROM_POINTER(ccgSubSurf_getFaceFaceHandle(f)); + int new_matnr = faceFlags[index].mat_nr; + points_per_mat[new_matnr] += numVerts * gridFaces * gridFaces * 6; + } + } + else { + for (i = 0; i < totface; i++) { + points_per_mat[0] += gridFaces * gridFaces * 6; + } + } + + /* create the GPUDrawObject */ + gdo = MEM_callocN(sizeof(GPUDrawObject), "GPUDrawObject"); + gdo->totvert = ccgSubSurf_getNumFinalFaces(ss) * 6; + gdo->totedge = ccgSubSurf_getNumFinalEdges(ss) * 2; + + /* count the number of materials used by this DerivedMesh */ + for (i = 0; i < totmat; i++) { + if (points_per_mat[i] > 0) + gdo->totmaterial++; + } + + /* allocate an array of materials used by this DerivedMesh */ + gdo->materials = MEM_mallocN(sizeof(GPUBufferMaterial) * gdo->totmaterial, + "GPUDrawObject.materials"); + + /* initialize the materials array */ + for (i = 0, curmat = 0, curpoint = 0; i < totmat; i++) { + if (points_per_mat[i] > 0) { + gdo->materials[curmat].start = curpoint; + gdo->materials[curmat].totpoint = points_per_mat[i]; + gdo->materials[curmat].mat_nr = i; + + curpoint += points_per_mat[i]; + curmat++; + } + } + + /* store total number of points used for triangles */ + gdo->tot_triangle_point = curpoint; + + mat_orig_to_new = MEM_callocN(sizeof(*mat_orig_to_new) * totmat, + "GPUDrawObject.mat_orig_to_new"); + + /* build a map from the original material indices to the new + * GPUBufferMaterial indices */ + for (i = 0; i < gdo->totmaterial; i++) { + mat_orig_to_new[gdo->materials[i].mat_nr] = i; + + } + + /* + for (i = 0; i < totface; i++) { + CCGFace *f = ccgdm->faceMap[i].face; + int index = GET_INT_FROM_POINTER(ccgSubSurf_getFaceFaceHandle(f)); + int new_matnr = faceFlags[index].mat_nr; + + mat = &gdo->materials[mat_orig_to_new[new_matnr]]; + + } + */ + + + MEM_freeN(mat_orig_to_new); + MEM_freeN(points_per_mat); + + return gdo; +} + +/* Only used by non-editmesh types */ +static void ccgDM_drawFacesSolid(DerivedMesh *dm, float (*partial_redraw_planes)[4], bool fast, DMSetMaterial setMaterial) +{ + int a; + CCGDerivedMesh *ccgdm = (CCGDerivedMesh *) dm; + + ccgdm_pbvh_update(ccgdm); + if (ccgdm->pbvh && ccgdm->multires.mmd && !fast) { if (BKE_pbvh_has_faces(ccgdm->pbvh)) { BKE_pbvh_draw(ccgdm->pbvh, partial_redraw_planes, NULL, @@ -1774,6 +2053,34 @@ static void ccgDM_drawFacesSolid(DerivedMesh *dm, float (*partial_redraw_planes) return; } + + GPU_vertex_setup(dm); + GPU_normal_setup(dm); + glShadeModel(GL_SMOOTH); + for (a = 0; a < dm->drawObject->totmaterial; a++) { + if (!setMaterial || setMaterial(dm->drawObject->materials[a].mat_nr + 1, NULL)) { + glDrawArrays(GL_TRIANGLES, dm->drawObject->materials[a].start, + dm->drawObject->materials[a].totpoint); + } + } + GPU_buffer_unbind(); + +#if 0 + + CCGDerivedMesh *ccgdm = (CCGDerivedMesh *) dm; + CCGSubSurf *ss = ccgdm->ss; + CCGKey key; + short (*lnors)[4][3] = dm->getTessFaceDataArray(dm, CD_TESSLOOPNORMAL); + int gridSize = ccgSubSurf_getGridSize(ss); + int gridFaces = gridSize - 1; + DMFlagMat *faceFlags = ccgdm->faceFlags; + int step = (fast) ? gridSize - 1 : 1; + int i, totface = ccgSubSurf_getNumFaces(ss); + int drawcurrent = 0, matnr = -1, shademodel = -1; + + CCG_key_top_level(&key, ss); + ccgdm_pbvh_update(ccgdm); + for (i = 0; i < totface; i++) { CCGFace *f = ccgdm->faceMap[i].face; @@ -1873,6 +2180,8 @@ static void ccgDM_drawFacesSolid(DerivedMesh *dm, float (*partial_redraw_planes) } } } + +#endif } /* Only used by non-editmesh types */ @@ -3431,6 +3740,8 @@ static CCGDerivedMesh *getCCGDerivedMesh(CCGSubSurf *ss, ccgdm->dm.drawMappedEdgesInterp = ccgDM_drawMappedEdgesInterp; ccgdm->dm.drawMappedEdges = ccgDM_drawMappedEdges; + ccgdm->dm.gpuObjectNew = ccgDM_GPUObjectNew; + ccgdm->dm.copy_gpu_data = ccgDM_copy_gpu_data; ccgdm->dm.release = ccgDM_release; diff --git a/source/blender/blenkernel/intern/texture.c b/source/blender/blenkernel/intern/texture.c index 2ea903247b2..88a412d5e95 100644 --- a/source/blender/blenkernel/intern/texture.c +++ b/source/blender/blenkernel/intern/texture.c @@ -1470,11 +1470,8 @@ void BKE_texture_envmap_free(EnvMap *env) /* ------------------------------------------------------------------------- */ -PointDensity *BKE_texture_pointdensity_add(void) +void BKE_texture_pointdensity_init_data(PointDensity *pd) { - PointDensity *pd; - - pd = MEM_callocN(sizeof(PointDensity), "pointdensity"); pd->flag = 0; pd->radius = 0.3f; pd->falloff_type = TEX_PD_FALLOFF_STD; @@ -1498,7 +1495,12 @@ PointDensity *BKE_texture_pointdensity_add(void) pd->falloff_curve->cm->flag &= ~CUMA_EXTEND_EXTRAPOLATE; curvemap_reset(pd->falloff_curve->cm, &pd->falloff_curve->clipr, pd->falloff_curve->preset, CURVEMAP_SLOPE_POSITIVE); curvemapping_changed(pd->falloff_curve, false); +} +PointDensity *BKE_texture_pointdensity_add(void) +{ + PointDensity *pd = MEM_callocN(sizeof(PointDensity), "pointdensity"); + BKE_texture_pointdensity_init_data(pd); return pd; } diff --git a/source/blender/blenlib/BLI_fileops.h b/source/blender/blenlib/BLI_fileops.h index 01aa5d39b96..642896ac701 100644 --- a/source/blender/blenlib/BLI_fileops.h +++ b/source/blender/blenlib/BLI_fileops.h @@ -106,6 +106,7 @@ int BLI_access(const char *filename, int mode) ATTR_WARN_UNUSED_RESULT ATTR_N bool BLI_file_is_writable(const char *file) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); bool BLI_file_touch(const char *file) ATTR_NONNULL(); +void BLI_file_size_string(off_t st_size, char *size, size_t len); #if 0 /* UNUSED */ int BLI_file_gzip(const char *from, const char *to) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); diff --git a/source/blender/blenlib/BLI_fileops_types.h b/source/blender/blenlib/BLI_fileops_types.h index 0e6eab687ad..e596fa55877 100644 --- a/source/blender/blenlib/BLI_fileops_types.h +++ b/source/blender/blenlib/BLI_fileops_types.h @@ -34,6 +34,7 @@ */ #include <sys/stat.h> +#include "BLI_listbase.h" #if defined(WIN32) && !defined(FREE_WINDOWS) typedef unsigned int mode_t; @@ -41,6 +42,19 @@ typedef unsigned int mode_t; struct ImBuf; +typedef struct CollapsedEntry { + /* list that gets populated during file open */ + ListBase list; + /* sorted array of the files for quick access of frames */ + struct direntry **darray; + off_t totalsize; + int minframe; + int maxframe; + int numdigits; + int totfiles; + int curfra; +} CollapsedEntry; + struct direntry { mode_t type; char *relname; @@ -69,6 +83,10 @@ struct direntry { int nr; struct ImBuf *image; unsigned int selflag; /* selection flag */ + off_t realsize; /* real size of file */ + int frame; /* frame of file in a movie sequence */ + + CollapsedEntry collapsed_info; }; struct dirlink { diff --git a/source/blender/blenlib/BLI_math_geom.h b/source/blender/blenlib/BLI_math_geom.h index 4a75e83316e..91610ccfe5b 100644 --- a/source/blender/blenlib/BLI_math_geom.h +++ b/source/blender/blenlib/BLI_math_geom.h @@ -227,6 +227,8 @@ void fill_poly_v2i_n( /* tri or quad, d can be NULL */ void interp_weights_face_v3(float w[4], const float a[3], const float b[3], const float c[3], const float d[3], const float p[3]); +/* also returns three indices of the triangle actually used */ +void interp_weights_face_v3_index(int tri[3], float w[4], const float v1[3], const float v2[3], const float v3[3], const float v4[3], const float co[3]); void interp_weights_poly_v3(float w[], float v[][3], const int n, const float co[3]); void interp_weights_poly_v2(float w[], float v[][2], const int n, const float co[2]); diff --git a/source/blender/blenlib/BLI_path_util.h b/source/blender/blenlib/BLI_path_util.h index c9a54c33f21..c488856ca30 100644 --- a/source/blender/blenlib/BLI_path_util.h +++ b/source/blender/blenlib/BLI_path_util.h @@ -133,7 +133,7 @@ bool BLI_path_abs(char *path, const char *basepath) ATTR_NONNULL(); bool BLI_path_frame(char *path, int frame, int digits) ATTR_NONNULL(); bool BLI_path_frame_range(char *path, int sta, int end, int digits) ATTR_NONNULL(); bool BLI_path_frame_get(char *path, int *r_frame, int *numdigits) ATTR_NONNULL(); -void BLI_path_frame_strip(char *path, bool setsharp, char *ext) ATTR_NONNULL(); +bool BLI_path_frame_strip(char *path, bool setsharp, char *ext); bool BLI_path_frame_check_chars(const char *path) ATTR_NONNULL(); bool BLI_path_cwd(char *path) ATTR_NONNULL(); void BLI_path_rel(char *file, const char *relfile) ATTR_NONNULL(); diff --git a/source/blender/blenlib/intern/BLI_filelist.c b/source/blender/blenlib/intern/BLI_filelist.c index e9ed785efc7..f700a12fbca 100644 --- a/source/blender/blenlib/intern/BLI_filelist.c +++ b/source/blender/blenlib/intern/BLI_filelist.c @@ -218,7 +218,6 @@ static void bli_adddirstrings(struct BuildDirCtx *dir_ctx) const char *types[8] = {"---", "--x", "-w-", "-wx", "r--", "r-x", "rw-", "rwx"}; /* symbolic display, indexed by mode field value */ int num; - off_t st_size; struct direntry *file; struct tm *tm; time_t zero = 0; @@ -288,20 +287,25 @@ static void bli_adddirstrings(struct BuildDirCtx *dir_ctx) * will buy us some time until files get bigger than 4GB or until * everyone starts using __USE_FILE_OFFSET64 or equivalent. */ - st_size = file->s.st_size; + file->realsize = file->s.st_size; - if (st_size > 1024 * 1024 * 1024) { - BLI_snprintf(file->size, sizeof(file->size), "%.2f GiB", ((double)st_size) / (1024 * 1024 * 1024)); - } - else if (st_size > 1024 * 1024) { - BLI_snprintf(file->size, sizeof(file->size), "%.1f MiB", ((double)st_size) / (1024 * 1024)); - } - else if (st_size > 1024) { - BLI_snprintf(file->size, sizeof(file->size), "%d KiB", (int)(st_size / 1024)); - } - else { - BLI_snprintf(file->size, sizeof(file->size), "%d B", (int)st_size); - } + BLI_file_size_string(file->realsize, file->size, sizeof(file->size)); + } +} + +void BLI_file_size_string(off_t st_size, char *size, size_t len) +{ + if (st_size > 1024 * 1024 * 1024) { + BLI_snprintf(size, len, "%.2f GiB", ((double)st_size) / (1024 * 1024 * 1024)); + } + else if (st_size > 1024 * 1024) { + BLI_snprintf(size, len, "%.1f MiB", ((double)st_size) / (1024 * 1024)); + } + else if (st_size > 1024) { + BLI_snprintf(size, len, "%d KiB", (int)(st_size / 1024)); + } + else { + BLI_snprintf(size, len, "%d B", (int)st_size); } } @@ -361,6 +365,9 @@ void BLI_filelist_duplicate( if (dest->poin && dup_poin) { dest->poin = dup_poin(src->poin); } + if (dest->collapsed_info.darray) { + dest->collapsed_info.darray = NULL; + } } } @@ -381,6 +388,8 @@ void BLI_filelist_free(struct direntry *filelist, unsigned int nrentries, void ( MEM_freeN(entry->path); if (entry->poin && free_poin) free_poin(entry->poin); + if (entry->collapsed_info.darray) + MEM_freeN(entry->collapsed_info.darray); } if (filelist != NULL) { diff --git a/source/blender/blenlib/intern/math_geom.c b/source/blender/blenlib/intern/math_geom.c index 82062e80ae8..32308ad1ab5 100644 --- a/source/blender/blenlib/intern/math_geom.c +++ b/source/blender/blenlib/intern/math_geom.c @@ -2425,6 +2425,71 @@ void interp_weights_face_v3(float w[4], const float v1[3], const float v2[3], co } } +void interp_weights_face_v3_index(int tri[3], float w[4], const float v1[3], const float v2[3], const float v3[3], const float v4[3], const float co[3]) +{ + float w2[3]; + + w[0] = w[1] = w[2] = w[3] = 0.0f; + tri[0] = tri[1] = tri[2] = -1; + + /* first check for exact match */ + if (equals_v3v3(co, v1)) { + w[0] = 1.0f; + tri[0] = 0; tri[1] = 1; tri[2] = 3; + } + else if (equals_v3v3(co, v2)) { + w[1] = 1.0f; + tri[0] = 0; tri[1] = 1; tri[2] = 3; + } + else if (equals_v3v3(co, v3)) { + w[2] = 1.0f; + tri[0] = 1; tri[1] = 2; tri[2] = 3; + } + else if (v4 && equals_v3v3(co, v4)) { + w[3] = 1.0f; + tri[0] = 1; tri[1] = 2; tri[2] = 3; + } + else { + /* otherwise compute barycentric interpolation weights */ + float n1[3], n2[3], n[3]; + bool degenerate; + + sub_v3_v3v3(n1, v1, v3); + if (v4) { + sub_v3_v3v3(n2, v2, v4); + } + else { + sub_v3_v3v3(n2, v2, v3); + } + cross_v3_v3v3(n, n1, n2); + + /* OpenGL seems to split this way, so we do too */ + if (v4) { + degenerate = barycentric_weights(v1, v2, v4, co, n, w); + SWAP(float, w[2], w[3]); + tri[0] = 0; tri[1] = 1; tri[2] = 3; + + if (degenerate || (w[0] < 0.0f)) { + /* if w[1] is negative, co is on the other side of the v1-v3 edge, + * so we interpolate using the other triangle */ + degenerate = barycentric_weights(v2, v3, v4, co, n, w2); + + if (!degenerate) { + w[0] = 0.0f; + w[1] = w2[0]; + w[2] = w2[1]; + w[3] = w2[2]; + tri[0] = 1; tri[1] = 2; tri[2] = 3; + } + } + } + else { + barycentric_weights(v1, v2, v3, co, n, w); + tri[0] = 0; tri[1] = 1; tri[2] = 2; + } + } +} + /* return 1 of point is inside triangle, 2 if it's on the edge, 0 if point is outside of triangle */ int barycentric_inside_triangle_v2(const float w[3]) { diff --git a/source/blender/blenlib/intern/path_util.c b/source/blender/blenlib/intern/path_util.c index b1a7d74e46f..d411c2605d7 100644 --- a/source/blender/blenlib/intern/path_util.c +++ b/source/blender/blenlib/intern/path_util.c @@ -912,7 +912,7 @@ bool BLI_path_frame_get(char *path, int *r_frame, int *r_numdigits) return false; } -void BLI_path_frame_strip(char *path, bool setsharp, char *ext) +bool BLI_path_frame_strip(char *path, bool setsharp, char *ext) { if (path && *path) { char *file = (char *)BLI_last_slash(path); @@ -946,20 +946,41 @@ void BLI_path_frame_strip(char *path, bool setsharp, char *ext) c++; - if (numdigits) { - /* replace the number with the suffix and terminate the string */ - while (numdigits--) { - if (ext) *ext++ = *suffix; - - if (setsharp) *c++ = '#'; - else *c++ = *suffix; + if(numdigits) { + /* logic here is a bit complex. Idea is: if ext has been provided, + * fill it with the extension part and do not keep it in filename + * if no ext has been provided, just strip the number or fill it with # + */ + if (ext) { + while (*suffix) { + *ext++ = *suffix++; + } + *ext = 0; - suffix++; + if (setsharp) { + while (numdigits--) { + *c++ = '#'; + } + } + *c = 0; } - *c = 0; - if (ext) *ext = 0; + else { + if (setsharp) { + while (numdigits--) { + *c++ = '#'; + } + } + while (*suffix) { + *c++ = *suffix++; + } + *c = 0; + } + + return true; } } + + return false; } diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c index c8b1eea7aa2..326d727e96a 100644 --- a/source/blender/blenloader/intern/readfile.c +++ b/source/blender/blenloader/intern/readfile.c @@ -58,6 +58,7 @@ #include "DNA_armature_types.h" #include "DNA_actuator_types.h" #include "DNA_brush_types.h" +#include "DNA_cache_library_types.h" #include "DNA_camera_types.h" #include "DNA_cloth_types.h" #include "DNA_controller_types.h" @@ -114,6 +115,7 @@ #include "BKE_action.h" #include "BKE_armature.h" #include "BKE_brush.h" +#include "BKE_cache_library.h" #include "BKE_cloth.h" #include "BKE_constraint.h" #include "BKE_context.h" @@ -123,6 +125,7 @@ #include "BKE_fcurve.h" #include "BKE_global.h" // for G #include "BKE_group.h" +#include "BKE_key.h" #include "BKE_library.h" // for which_libbase #include "BKE_idcode.h" #include "BKE_material.h" @@ -1983,7 +1986,6 @@ static void direct_link_paint_curve(FileData *fd, PaintCurve *pc) pc->points = newdataadr(fd, pc->points); } - static void direct_link_script(FileData *UNUSED(fd), Script *script) { script->id.us = 1; @@ -1991,6 +1993,96 @@ static void direct_link_script(FileData *UNUSED(fd), Script *script) } +/* ************ READ CacheLibrary *************** */ + +static void lib_link_cache_modifiers_cb(void *userData, CacheLibrary *cachelib, CacheModifier *UNUSED(md), ID **idpoin) +{ + FileData *fd = userData; + + *idpoin = newlibadr(fd, cachelib->id.lib, *idpoin); + /* hardcoded bad exception; non-object modifier data gets user count (texture, displace) */ + if (*idpoin && GS((*idpoin)->name)!=ID_OB) + (*idpoin)->us++; +} +static void lib_link_cache_modifiers(FileData *fd, CacheLibrary *cachelib) +{ + CacheModifier *md; + for (md = cachelib->modifiers.first; md; md = md->next) { + BKE_cache_modifier_foreachIDLink(cachelib, md, lib_link_cache_modifiers_cb, fd); + + /* special cases */ + switch (md->type) { + case eCacheModifierType_StrandsKey: { + StrandsKeyCacheModifier *skmd = (StrandsKeyCacheModifier *)md; + /* Key is a local ID block, not handled by foreachIDLink */ + skmd->key = newlibadr_us(fd, cachelib->id.lib, skmd->key); + break; + } + } + } +} + +static void lib_link_cache_library(FileData *fd, Main *main) +{ + CacheLibrary *cachelib; + + for (cachelib = main->cache_library.first; cachelib; cachelib = cachelib->id.next) { + if (cachelib->id.flag & LIB_NEED_LINK) { + cachelib->id.flag -= LIB_NEED_LINK; + + cachelib->filter_group = newlibadr_us(fd, cachelib->id.lib, cachelib->filter_group); + + lib_link_cache_modifiers(fd, cachelib); + } + } +} + +static void direct_link_cache_modifiers(FileData *fd, ListBase *modifiers) +{ + CacheModifier *md; + + link_list(fd, modifiers); + + for (md = modifiers->first; md; md = md->next) { + /* if modifiers disappear, or for upward compatibility */ + if (md->type >= NUM_CACHE_MODIFIER_TYPES) + md->type = eCacheModifierType_None; + + + switch (md->type) { + case eCacheModifierType_HairSimulation: { + HairSimCacheModifier *hsmd = (HairSimCacheModifier *)md; + hsmd->sim_params.effector_weights = newdataadr(fd, hsmd->sim_params.effector_weights); + hsmd->sim_params.goal_stiffness_mapping = newdataadr(fd, hsmd->sim_params.goal_stiffness_mapping); + if (hsmd->sim_params.goal_stiffness_mapping) + direct_link_curvemapping(fd, hsmd->sim_params.goal_stiffness_mapping); + hsmd->sim_params.bend_stiffness_mapping = newdataadr(fd, hsmd->sim_params.bend_stiffness_mapping); + if (hsmd->sim_params.bend_stiffness_mapping) + direct_link_curvemapping(fd, hsmd->sim_params.bend_stiffness_mapping); + break; + } + case eCacheModifierType_ForceField: { + ForceFieldCacheModifier *ffmd = (ForceFieldCacheModifier *)md; + ffmd->vertex_cache = NULL; + break; + } + case eCacheModifierType_StrandsKey: { + StrandsKeyCacheModifier *skmd = (StrandsKeyCacheModifier *)md; + skmd->edit = NULL; + break; + } + } + } +} + +static void direct_link_cache_library(FileData *fd, CacheLibrary *cachelib) +{ + direct_link_cache_modifiers(fd, &cachelib->modifiers); + + cachelib->archive_info = NULL; /* runtime */ +} + + /* ************ READ PACKEDFILE *************** */ static PackedFile *direct_link_packedfile(FileData *fd, PackedFile *oldpf) @@ -3176,6 +3268,10 @@ static void lib_link_key(FileData *fd, Main *main) key->ipo = newlibadr_us(fd, key->id.lib, key->ipo); // XXX deprecated - old animation system key->from = newlibadr(fd, key->id.lib, key->from); + /* versioning: initialize extra owner info */ + if (!key->fromtype && key->from) { + BKE_key_set_from_id(key, key->from); + } key->id.flag -= LIB_NEED_LINK; } @@ -3219,7 +3315,7 @@ static void direct_link_key(FileData *fd, Key *key) key->adt = newdataadr(fd, key->adt); direct_link_animdata(fd, key->adt); - + key->refkey= newdataadr(fd, key->refkey); for (kb = key->block.first; kb; kb = kb->next) { @@ -3959,6 +4055,8 @@ static void lib_link_particlesystems(FileData *fd, Object *ob, ID *id, ListBase BLI_remlink(particles, psys); MEM_freeN(psys); } + + psys->key = newlibadr_us(fd, id->lib, psys->key); } } static void direct_link_particlesystems(FileData *fd, ListBase *particles) @@ -4004,6 +4102,7 @@ static void direct_link_particlesystems(FileData *fd, ListBase *particles) psys->edit = NULL; psys->free_edit = NULL; + psys->hairedit = NULL; psys->pathcache = NULL; psys->childcache = NULL; BLI_listbase_clear(&psys->pathcachebufs); @@ -4440,6 +4539,7 @@ static void lib_link_object(FileData *fd, Main *main) ob->track = newlibadr(fd, ob->id.lib, ob->track); ob->poselib = newlibadr_us(fd, ob->id.lib, ob->poselib); ob->dup_group = newlibadr_us(fd, ob->id.lib, ob->dup_group); + ob->cache_library = newlibadr_us(fd, ob->id.lib, ob->cache_library); ob->proxy = newlibadr_us(fd, ob->id.lib, ob->proxy); if (ob->proxy) { @@ -5033,7 +5133,7 @@ static void direct_link_object(FileData *fd, Object *ob) * See [#34776, #42780] for more information. */ if (fd->memfile || (ob->id.flag & (LIB_EXTERN | LIB_INDIRECT))) { - ob->mode &= ~(OB_MODE_EDIT | OB_MODE_PARTICLE_EDIT); + ob->mode &= ~(OB_MODE_EDIT | OB_MODE_PARTICLE_EDIT | OB_MODE_HAIR_EDIT); if (!fd->memfile) { ob->mode &= ~OB_MODE_POSE; } @@ -5050,6 +5150,7 @@ static void direct_link_object(FileData *fd, Object *ob) direct_link_motionpath(fd, ob->mpath); link_list(fd, &ob->defbase); + link_list(fd, &ob->fmaps); // XXX deprecated - old animation system <<< direct_link_nlastrips(fd, &ob->nlastrips); link_list(fd, &ob->constraintChannels); @@ -5062,6 +5163,8 @@ static void direct_link_object(FileData *fd, Object *ob) /* do it here, below old data gets converted */ direct_link_modifiers(fd, &ob->modifiers); + ob->dup_cache = NULL; + link_list(fd, &ob->effect); paf= ob->effect.first; while (paf) { @@ -5369,6 +5472,14 @@ static void lib_link_scene(FileData *fd, Main *main) sce->toolsettings->particle.shape_object = newlibadr(fd, sce->id.lib, sce->toolsettings->particle.shape_object); + { + HairEditSettings *hair_edit = &sce->toolsettings->hair_edit; + if (hair_edit->brush) + hair_edit->brush = newlibadr(fd, sce->id.lib, hair_edit->brush); + if (hair_edit->shape_object) + hair_edit->shape_object = newlibadr(fd, sce->id.lib, hair_edit->shape_object); + } + for (base = sce->base.first; base; base = next) { next = base->next; @@ -5623,7 +5734,8 @@ static void direct_link_scene(FileData *fd, Scene *sce) sce->toolsettings->particle.paintcursor = NULL; sce->toolsettings->particle.scene = NULL; sce->toolsettings->particle.object = NULL; - + sce->toolsettings->hair_edit.paint_cursor = NULL; + /* in rare cases this is needed, see [#33806] */ if (sce->toolsettings->vpaint) { sce->toolsettings->vpaint->vpaint_prev = NULL; @@ -5994,6 +6106,7 @@ static void lib_link_screen(FileData *fd, Main *main) ads->source = newlibadr(fd, sc->id.lib, ads->source); ads->filter_grp = newlibadr(fd, sc->id.lib, ads->filter_grp); } + sipo->backdrop_camera = newlibadr(fd, sc->id.lib, sipo->backdrop_camera); } else if (sl->spacetype == SPACE_BUTS) { SpaceButs *sbuts = (SpaceButs *)sl; @@ -6321,6 +6434,7 @@ void blo_lib_link_screen_restore(Main *newmain, bScreen *curscreen, Scene *cursc if (ads->filter_grp) ads->filter_grp = restore_pointer_by_name(newmain, (ID *)ads->filter_grp, USER_IGNORE); } + sipo->backdrop_camera = restore_pointer_by_name(newmain, (ID *)sipo->backdrop_camera, USER_IGNORE); /* force recalc of list of channels (i.e. includes calculating F-Curve colors) * thus preventing the "black curves" problem post-undo @@ -6554,6 +6668,7 @@ static void direct_link_region(FileData *fd, ARegion *ar, int spacetype) BLI_listbase_clear(&ar->panels_category); BLI_listbase_clear(&ar->handlers); BLI_listbase_clear(&ar->uiblocks); + BLI_listbase_clear(&ar->widgetmaps); ar->headerstr = NULL; ar->swinid = 0; ar->type = NULL; @@ -6689,6 +6804,7 @@ static bool direct_link_screen(FileData *fd, bScreen *sc) } v3d->localvd = newdataadr(fd, v3d->localvd); BLI_listbase_clear(&v3d->afterdraw_transp); + BLI_listbase_clear(&v3d->afterdraw_nodepth); BLI_listbase_clear(&v3d->afterdraw_xray); BLI_listbase_clear(&v3d->afterdraw_xraytransp); v3d->properties_storage = NULL; @@ -7527,6 +7643,7 @@ static const char *dataname(short id_code) case ID_MC: return "Data from MC"; case ID_MSK: return "Data from MSK"; case ID_LS: return "Data from LS"; + case ID_CL: return "Data from CL"; } return "Data from Lib Block"; @@ -7713,6 +7830,9 @@ static BHead *read_libblock(FileData *fd, Main *main, BHead *bhead, int flag, ID case ID_PC: direct_link_paint_curve(fd, (PaintCurve *)id); break; + case ID_CL: + direct_link_cache_library(fd, (CacheLibrary *)id); + break; } oldnewmap_free_unused(fd->datamap); @@ -7906,6 +8026,7 @@ static void lib_link_all(FileData *fd, Main *main) lib_link_mask(fd, main); lib_link_linestyle(fd, main); lib_link_gpencil(fd, main); + lib_link_cache_library(fd, main); lib_link_mesh(fd, main); /* as last: tpage images with users at zero */ @@ -8782,6 +8903,8 @@ static void expand_object(FileData *fd, Main *mainvar, Object *ob) if (ob->dup_group) expand_doit(fd, mainvar, ob->dup_group); + if (ob->cache_library) + expand_doit(fd, mainvar, ob->cache_library); if (ob->proxy) expand_doit(fd, mainvar, ob->proxy); @@ -9056,6 +9179,12 @@ static void expand_gpencil(FileData *fd, Main *mainvar, bGPdata *gpd) expand_animdata(fd, mainvar, gpd->adt); } +static void expand_cache_library(FileData *fd, Main *mainvar, CacheLibrary *cachelib) +{ + if (cachelib->filter_group) + expand_doit(fd, mainvar, cachelib->filter_group); +} + void BLO_main_expander(void (*expand_doit_func)(void *, Main *, void *)) { expand_doit = expand_doit_func; @@ -9153,6 +9282,9 @@ void BLO_expand_main(void *fdhandle, Main *mainvar) case ID_GD: expand_gpencil(fd, mainvar, (bGPdata *)id); break; + case ID_CL: + expand_cache_library(fd, mainvar, (CacheLibrary *)id); + break; } do_it = true; diff --git a/source/blender/blenloader/intern/versioning_260.c b/source/blender/blenloader/intern/versioning_260.c index 72a320281fe..6a1052969c7 100644 --- a/source/blender/blenloader/intern/versioning_260.c +++ b/source/blender/blenloader/intern/versioning_260.c @@ -2285,7 +2285,6 @@ void blo_do_versions_260(FileData *fd, Library *UNUSED(lib), Main *main) if (!MAIN_VERSION_ATLEAST(main, 268, 4)) { - bScreen *sc; Object *ob; for (ob = main->object.first; ob; ob = ob->id.next) { @@ -2315,26 +2314,6 @@ void blo_do_versions_260(FileData *fd, Library *UNUSED(lib), Main *main) } } - /* - * FIX some files have a zoom level of 0, and was checked during the drawing of the node space - * - * We moved this check to the do versions to be sure the value makes any sense. - */ - for (sc = main->screen.first; sc; sc = sc->id.next) { - ScrArea *sa; - for (sa = sc->areabase.first; sa; sa = sa->next) { - SpaceLink *sl; - for (sl = sa->spacedata.first; sl; sl = sl->next) { - if (sl->spacetype == SPACE_NODE) { - SpaceNode *snode = (SpaceNode *)sl; - if (snode->zoom < 0.02f) { - snode->zoom = 1.0; - } - } - } - } - } - for (ob = main->object.first; ob; ob = ob->id.next) { bSensor *sens; bTouchSensor *ts; diff --git a/source/blender/blenloader/intern/versioning_270.c b/source/blender/blenloader/intern/versioning_270.c index 37c255620d9..fa32469b549 100644 --- a/source/blender/blenloader/intern/versioning_270.c +++ b/source/blender/blenloader/intern/versioning_270.c @@ -36,9 +36,11 @@ #define DNA_DEPRECATED_ALLOW #include "DNA_brush_types.h" +#include "DNA_cache_library_types.h" #include "DNA_camera_types.h" #include "DNA_cloth_types.h" #include "DNA_constraint_types.h" +#include "DNA_key_types.h" #include "DNA_sdna_types.h" #include "DNA_sequence_types.h" #include "DNA_space_types.h" @@ -51,9 +53,11 @@ #include "DNA_actuator_types.h" #include "DNA_camera_types.h" #include "DNA_view3d_types.h" +#include "DNA_smoke_types.h" #include "DNA_genfile.h" +#include "BKE_colortools.h" #include "BKE_main.h" #include "BKE_modifier.h" #include "BKE_node.h" @@ -446,6 +450,55 @@ void blo_do_versions_270(FileData *fd, Library *UNUSED(lib), Main *main) } } } + + if (!DNA_struct_elem_find(fd->filesdna, "ClothSimSettings", "float", "bending_damping")) { + Object *ob; + ModifierData *md; + for (ob = main->object.first; ob; ob = ob->id.next) { + for (md = ob->modifiers.first; md; md = md->next) { + if (md->type == eModifierType_Cloth) { + ClothModifierData *clmd = (ClothModifierData*) md; + clmd->sim_parms->bending_damping = 0.5f; + } + else if (md->type == eModifierType_ParticleSystem) { + ParticleSystemModifierData *pmd = (ParticleSystemModifierData*) md; + if (pmd->psys->clmd) { + pmd->psys->clmd->sim_parms->bending_damping = 0.5f; + } + } + } + } + } + + if (!MAIN_VERSION_ATLEAST(main, 272, 3)) { + bScreen *sc; + for (sc = main->screen.first; sc; sc = sc->id.next) { + ScrArea *sa; + for (sa = sc->areabase.first; sa; sa = sa->next) { + SpaceLink *sl; + for (sl = sa->spacedata.first; sl; sl = sl->next) { + if (sl->spacetype == SPACE_NODE) { + SpaceNode *snode = (SpaceNode *)sl; + snode->backdrop_zoom = 1.0; + } + if (sl->spacetype == SPACE_SEQ) { + SpaceSeq *sseq = (SpaceSeq *)sl; + sseq->overdrop_zoom = 1.0; + } + + } + } + } + } + + if (!DNA_struct_elem_find(fd->filesdna, "ParticleSystem", "float", "hair_preview_factor")) { + Object *ob; + ParticleSystem *psys; + for (ob = main->object.first; ob; ob = ob->id.next) { + for(psys = ob->particlesystem.first; psys; psys = psys->next) + psys->hair_preview_factor = 100.0f; + } + } if (!MAIN_VERSION_ATLEAST(main, 273, 1)) { #define BRUSH_RAKE (1 << 7) @@ -470,6 +523,43 @@ void blo_do_versions_270(FileData *fd, Library *UNUSED(lib), Main *main) #undef BRUSH_RAKE #undef BRUSH_RANDOM_ROTATION + if (!DNA_struct_elem_find(fd->filesdna, "ParticleSettings", "float", "clump_noise_size")) { + ParticleSettings *part; + for (part = main->particle.first; part; part = part->id.next) { + part->clump_noise_size = 1.0f; + } + } + + if (!DNA_struct_elem_find(fd->filesdna, "ParticleSettings", "int", "kink_extra_steps")) { + ParticleSettings *part; + for (part = main->particle.first; part; part = part->id.next) { + part->kink_extra_steps = 4; + } + } + + if (!DNA_struct_elem_find(fd->filesdna, "MTex", "float", "kinkampfac")) { + ParticleSettings *part; + for (part = main->particle.first; part; part = part->id.next) { + int a; + for (a = 0; a < MAX_MTEX; a++) { + MTex *mtex = part->mtex[a]; + if (mtex) { + mtex->kinkampfac = 1.0f; + } + } + } + } + + if (!MAIN_VERSION_ATLEAST(main, 273, 3)) { + ParticleSettings *part; + for (part = main->particle.first; part; part = part->id.next) { + if (part->clumpcurve) + part->child_flag |= PART_CHILD_USE_CLUMP_CURVE; + if (part->roughcurve) + part->child_flag |= PART_CHILD_USE_ROUGH_CURVE; + } + } + /* Customizable Safe Areas */ if (!MAIN_VERSION_ATLEAST(main, 273, 2)) { if (!DNA_struct_elem_find(fd->filesdna, "Scene", "DisplaySafeAreas", "safe_areas")) { @@ -494,6 +584,26 @@ void blo_do_versions_270(FileData *fd, Library *UNUSED(lib), Main *main) } } + if (!MAIN_VERSION_ATLEAST(main, 273, 4)) { + bScreen *sc; + for (sc = main->screen.first; sc; sc = sc->id.next) { + ScrArea *sa; + for (sa = sc->areabase.first; sa; sa = sa->next) { + SpaceLink *sl; + for (sl = sa->spacedata.first; sl; sl = sl->next) { + if (sl->spacetype == SPACE_NODE) { + SpaceNode *snode = (SpaceNode *)sl; + snode->backdrop_zoom = 1.0; + } + if (sl->spacetype == SPACE_SEQ) { + SpaceSeq *sseq = (SpaceSeq *)sl; + sseq->overdrop_zoom = 1.0; + } + } + } + } + } + if (!MAIN_VERSION_ATLEAST(main, 273, 6)) { if (!DNA_struct_elem_find(fd->filesdna, "ClothSimSettings", "float", "bending_damping")) { Object *ob; @@ -611,6 +721,22 @@ void blo_do_versions_270(FileData *fd, Library *UNUSED(lib), Main *main) } } + if (!DNA_struct_elem_find(fd->filesdna, "ParticleInstanceModifierData", "float", "particle_amount")) { + Object *ob; + ModifierData *md; + + for (ob = main->object.first; ob; ob = ob->id.next) { + for (md = ob->modifiers.first; md; md = md->next) { + if (md->type == eModifierType_ParticleInstance) { + ParticleInstanceModifierData *pimd = (ParticleInstanceModifierData *)md; + + pimd->particle_amount = 1.0f; + pimd->particle_offset = 0.0f; + } + } + } + } + if (!MAIN_VERSION_ATLEAST(main, 273, 8)) { Object *ob; for (ob = main->object.first; ob != NULL; ob = ob->id.next) { @@ -650,7 +776,27 @@ void blo_do_versions_270(FileData *fd, Library *UNUSED(lib), Main *main) } } - if (!MAIN_VERSION_ATLEAST(main, 274, 1)) { + if (!MAIN_VERSION_ATLEAST(main, 274, 0)) { + if (!DNA_struct_elem_find(fd->filesdna, "SpaceNode", "float", "backdrop_zoom")) { + bScreen *sc; + for (sc = main->screen.first; sc; sc = sc->id.next) { + ScrArea *sa; + for (sa = sc->areabase.first; sa; sa = sa->next) { + SpaceLink *sl; + for (sl = sa->spacedata.first; sl; sl = sl->next) { + if (sl->spacetype == SPACE_NODE) { + SpaceNode *snode = (SpaceNode *)sl; + snode->backdrop_zoom = 1.0; + } + if (sl->spacetype == SPACE_SEQ) { + SpaceSeq *sseq = (SpaceSeq *)sl; + sseq->overdrop_zoom = 1.0; + } + } + } + } + } + /* particle systems need to be forced to redistribute for jitter mode fix */ { Object *ob; @@ -708,6 +854,29 @@ void blo_do_versions_270(FileData *fd, Library *UNUSED(lib), Main *main) } FOREACH_NODETREE_END } + { + bScreen *scr; + ScrArea *sa; + SpaceLink *sl; + ARegion *ar; + /* Make sure sequencer preview area limits zoom */ + for (scr = main->screen.first; scr; scr = scr->id.next) { + for (sa = scr->areabase.first; sa; sa = sa->next) { + for (sl = sa->spacedata.first; sl; sl = sl->next) { + if (sl->spacetype == SPACE_SEQ) { + ListBase *lb = (sl == sa->spacedata.first) ? &sa->regionbase : &sl->regionbase; + + for (ar = lb->first; ar; ar = ar->next) { + if (ar->regiontype == RGN_TYPE_WINDOW) { + ar->v2d.max[1] = MAXSEQ * 4; + } + } + } + } + } + } + } + if (!MAIN_VERSION_ATLEAST(main, 274, 4)) { SceneRenderView *srv; wmWindowManager *wm; @@ -731,6 +900,11 @@ void blo_do_versions_270(FileData *fd, Library *UNUSED(lib), Main *main) SEQ_BEGIN (scene->ed, seq) { seq->stereo3d_format = MEM_callocN(sizeof(Stereo3dFormat), "Stereo Display 3d Format"); + /* patch sequencer scene strips (used to be the same flag before multiview merge)*/ + if (seq->flag & SEQ_USE_VIEWS) { + seq->flag |= SEQ_SCENE_STRIPS; + seq->flag &= ~SEQ_USE_VIEWS; + } #define SEQ_USE_PROXY_CUSTOM_DIR (1 << 19) #define SEQ_USE_PROXY_CUSTOM_FILE (1 << 21) @@ -800,6 +974,37 @@ void blo_do_versions_270(FileData *fd, Library *UNUSED(lib), Main *main) } } + if (!DNA_struct_elem_find(fd->filesdna, "SmokeDomainSettings", "float", "display_thickness")) { + Object *ob; + ModifierData *md; + for (ob = main->object.first; ob; ob = ob->id.next) { + for (md = ob->modifiers.first; md; md = md->next) { + if (md->type == eModifierType_Smoke) { + SmokeModifierData *smd = (SmokeModifierData *)md; + if (smd->domain) { + smd->domain->display_thickness = 1.0f; + } + } + } + } + } + if (!DNA_struct_elem_find(fd->filesdna, "SpaceIpo", "float", "backdrop_zoom")) { + bScreen *sc; + for (sc = main->screen.first; sc; sc = sc->id.next) { + ScrArea *sa; + for (sa = sc->areabase.first; sa; sa = sa->next) { + SpaceLink *sl; + for (sl = sa->spacedata.first; sl; sl = sl->next) { + if (sl->spacetype == SPACE_IPO) { + SpaceIpo *sipo = (SpaceIpo *)sl; + sipo->backdrop_zoom = 1.0f; + sipo->backdrop_opacity = 0.7f; + } + } + } + } + } + if (!MAIN_VERSION_ATLEAST(main, 274, 6)) { bScreen *screen; @@ -845,4 +1050,33 @@ void blo_do_versions_270(FileData *fd, Library *UNUSED(lib), Main *main) } } } + + if (!DNA_struct_elem_find(fd->filesdna, "HairSimParams", "CurveMapping", "*bend_stiffness_mapping")) { + CacheLibrary *cachelib; + for (cachelib = main->cache_library.first; cachelib; cachelib = cachelib->id.next) { + CacheModifier *md; + for (md = cachelib->modifiers.first; md; md = md->next) { + if (md->type == eCacheModifierType_HairSimulation) { + HairSimCacheModifier *hsmd = (HairSimCacheModifier *)md; + { + CurveMapping *cm = curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f); + cm->cm[0].curve[0].x = 0.0f; + cm->cm[0].curve[0].y = 1.0f; + cm->cm[0].curve[1].x = 1.0f; + cm->cm[0].curve[1].y = 1.0f; + hsmd->sim_params.bend_stiffness_mapping = cm; + } + } + } + } + } + + /* from_extra has been moved to fromtype, fromindex */ + if (!DNA_struct_elem_find(fd->filesdna, "Key", "int", "fromindex")) { + Key *key; + for (key = main->key.first; key; key = key->id.next) { + key->fromtype = key->from_extra.type; + key->fromindex = key->from_extra.index; + } + } } diff --git a/source/blender/blenloader/intern/writefile.c b/source/blender/blenloader/intern/writefile.c index c709ab0ecb3..425686b7f24 100644 --- a/source/blender/blenloader/intern/writefile.c +++ b/source/blender/blenloader/intern/writefile.c @@ -97,6 +97,7 @@ #include "DNA_armature_types.h" #include "DNA_actuator_types.h" #include "DNA_brush_types.h" +#include "DNA_cache_library_types.h" #include "DNA_camera_types.h" #include "DNA_cloth_types.h" #include "DNA_constraint_types.h" @@ -147,6 +148,7 @@ #include "BKE_action.h" #include "BKE_blender.h" #include "BKE_bpath.h" +#include "BKE_cache_library.h" #include "BKE_curve.h" #include "BKE_constraint.h" #include "BKE_global.h" // for G @@ -1509,6 +1511,14 @@ static void write_defgroups(WriteData *wd, ListBase *defbase) writestruct(wd, DATA, "bDeformGroup", 1, defgroup); } +static void write_fmaps(WriteData *wd, ListBase *fbase) +{ + bFaceMap *fmap; + + for (fmap=fbase->first; fmap; fmap=fmap->next) + writestruct(wd, DATA, "bFaceMap", 1, fmap); +} + static void write_modifiers(WriteData *wd, ListBase *modbase) { ModifierData *md; @@ -1686,6 +1696,7 @@ static void write_objects(WriteData *wd, ListBase *idbase) write_pose(wd, ob->pose); write_defgroups(wd, &ob->defbase); + write_fmaps(wd, &ob->fmaps); write_constraints(wd, &ob->constraints); write_motionpath(wd, ob->mpath); @@ -1971,6 +1982,10 @@ static void write_customdata(WriteData *wd, ID *id, int count, CustomData *data, else if (layer->type == CD_GRID_PAINT_MASK) { write_grid_paint_mask(wd, count, layer->data); } + else if (layer->type == CD_FACEMAP) { + const int *layer_data = layer->data; + writedata(wd, DATA, sizeof(*layer_data) * count, layer_data); + } else { CustomData_file_write_info(layer->type, &structname, &structnum); if (structnum) { @@ -2787,7 +2802,10 @@ static void write_screens(WriteData *wd, ListBase *scrbase) writestruct(wd, DATA, "SpaceIpo", 1, sl); if (sipo->ads) writestruct(wd, DATA, "bDopeSheet", 1, sipo->ads); - + + if (sipo->backdrop_camera) + writestruct(wd, DATA, "Object", 1, sipo->backdrop_camera); + /* reenable ghost curves */ sipo->ghostCurves= tmpGhosts; } @@ -3563,6 +3581,46 @@ static void write_linestyles(WriteData *wd, ListBase *idbase) } } +static void write_cache_modifiers(WriteData *wd, CacheLibrary *cachelib) +{ + CacheModifier *md; + for (md = cachelib->modifiers.first; md; md = md->next) { + const char *struct_name = BKE_cache_modifier_type_struct_name(md->type); + if (!struct_name || struct_name[0] == '\0') + continue; + + writestruct(wd, DATA, struct_name, 1, md); + + switch (md->type) { + case eCacheModifierType_HairSimulation: { + HairSimCacheModifier *hsmd = (HairSimCacheModifier *)md; + writestruct(wd, DATA, "EffectorWeights", 1, hsmd->sim_params.effector_weights); + if (hsmd->sim_params.goal_stiffness_mapping) + write_curvemapping(wd, hsmd->sim_params.goal_stiffness_mapping); + if (hsmd->sim_params.bend_stiffness_mapping) + write_curvemapping(wd, hsmd->sim_params.bend_stiffness_mapping); + break; + } + } + } +} + +static void write_cachelibraries(WriteData *wd, ListBase *idbase) +{ + CacheLibrary *cachelib; + + for (cachelib = idbase->first; cachelib; cachelib = cachelib->id.next) { + if (cachelib->id.us > 0 || wd->current) { + + writestruct(wd, ID_CL, "CacheLibrary", 1, cachelib); + if (cachelib->id.properties) + IDP_WriteProperty(cachelib->id.properties, wd); + + write_cache_modifiers(wd, cachelib); + } + } +} + /* context is usually defined by WM, two cases where no WM is available: * - for forward compatibility, curscreen has to be saved * - for undofile, curscene needs to be saved */ @@ -3691,6 +3749,7 @@ static int write_file_handle( write_scripts (wd, &mainvar->script); write_gpencils (wd, &mainvar->gpencil); write_linestyles(wd, &mainvar->linestyle); + write_cachelibraries(wd, &mainvar->cache_library); write_libraries(wd, mainvar->next); if (write_user_block) { diff --git a/source/blender/bmesh/CMakeLists.txt b/source/blender/bmesh/CMakeLists.txt index 80adb595ac9..6002b414779 100644 --- a/source/blender/bmesh/CMakeLists.txt +++ b/source/blender/bmesh/CMakeLists.txt @@ -114,6 +114,10 @@ set(SRC intern/bmesh_queries.c intern/bmesh_queries.h intern/bmesh_queries_inline.h + intern/bmesh_strands.c + intern/bmesh_strands.h + intern/bmesh_strands_conv.c + intern/bmesh_strands_conv.h intern/bmesh_structure.c intern/bmesh_structure.h intern/bmesh_structure_inline.h diff --git a/source/blender/bmesh/bmesh.h b/source/blender/bmesh/bmesh.h index 87b1818fa5d..7ba700d17a2 100644 --- a/source/blender/bmesh/bmesh.h +++ b/source/blender/bmesh/bmesh.h @@ -256,6 +256,8 @@ extern "C" { #include "intern/bmesh_mesh_validate.h" #include "intern/bmesh_mods.h" #include "intern/bmesh_operators.h" +#include "intern/bmesh_strands.h" +#include "intern/bmesh_strands_conv.h" #include "intern/bmesh_polygon.h" #include "intern/bmesh_queries.h" #include "intern/bmesh_walkers.h" diff --git a/source/blender/bmesh/bmesh_class.h b/source/blender/bmesh/bmesh_class.h index ada0fabd28e..058fec548a3 100644 --- a/source/blender/bmesh/bmesh_class.h +++ b/source/blender/bmesh/bmesh_class.h @@ -276,8 +276,16 @@ enum { #define BM_CHECK_TYPE_ELEM(ele) \ CHECK_TYPE_ANY(ele, _BM_GENERIC_TYPE_ELEM_NONCONST, _BM_GENERIC_TYPE_ELEM_CONST) +#ifndef __cplusplus #define BM_CHECK_TYPE_ELEM_ASSIGN(ele) \ (BM_CHECK_TYPE_ELEM(ele)), ele +#else +/* for C++: cast the lhs to a void*, + * because C++ does not allow implicit void* casting of the rhs + */ +#define BM_CHECK_TYPE_ELEM_ASSIGN(ele) \ + (BM_CHECK_TYPE_ELEM(ele), CHECK_TYPE_NONCONST(ele)), *(void**)(&(ele)) +#endif /* BMHeader->hflag (char) */ enum { diff --git a/source/blender/bmesh/intern/bmesh_interp.c b/source/blender/bmesh/intern/bmesh_interp.c index 30ac76ab7e1..81155bc017e 100644 --- a/source/blender/bmesh/intern/bmesh_interp.c +++ b/source/blender/bmesh/intern/bmesh_interp.c @@ -911,6 +911,34 @@ void BM_elem_float_data_set(CustomData *cd, void *element, int type, const float if (f) *f = val; } +float BM_elem_float_data_named_get(CustomData *cd, void *element, int type, const char *name) +{ + const float *f = CustomData_bmesh_get_named(cd, ((BMHeader *)element)->data, type, name); + return f ? *f : 0.0f; +} + +void BM_elem_float_data_named_set(CustomData *cd, void *element, int type, const char *name, const float val) +{ + float *f = CustomData_bmesh_get_named(cd, ((BMHeader *)element)->data, type, name); + if (f) *f = val; +} + +void BM_elem_meshsample_data_named_get(CustomData *cd, void *element, int type, const char *name, MSurfaceSample *val) +{ + const MSurfaceSample *s = CustomData_bmesh_get_named(cd, ((BMHeader *)element)->data, type, name); + if (s) + memcpy(val, s, sizeof(MSurfaceSample)); + else + memset(val, 0, sizeof(MSurfaceSample)); +} + +void BM_elem_meshsample_data_named_set(CustomData *cd, void *element, int type, const char *name, const MSurfaceSample *val) +{ + MSurfaceSample *s = CustomData_bmesh_get_named(cd, ((BMHeader *)element)->data, type, name); + if (s) + memcpy(s, val, sizeof(MSurfaceSample)); +} + /** \name Loop interpolation functions: BM_vert_loop_groups_data_layer_*** * * Handling loop custom-data such as UV's, while keeping contiguous fans is rather tedious. diff --git a/source/blender/bmesh/intern/bmesh_interp.h b/source/blender/bmesh/intern/bmesh_interp.h index 969e92f37db..6168a655c93 100644 --- a/source/blender/bmesh/intern/bmesh_interp.h +++ b/source/blender/bmesh/intern/bmesh_interp.h @@ -29,6 +29,8 @@ struct LinkNode; struct MemArena; +struct MSurfaceSample; + void BM_loop_interp_multires(BMesh *bm, BMLoop *l_dst, const BMFace *f_src); void BM_vert_interp_from_face(BMesh *bm, BMVert *v_dst, const BMFace *f_src); @@ -44,6 +46,10 @@ void BM_data_layer_copy(BMesh *bm, CustomData *data, int type, int src_n, int d float BM_elem_float_data_get(CustomData *cd, void *element, int type); void BM_elem_float_data_set(CustomData *cd, void *element, int type, const float val); +float BM_elem_float_data_named_get(CustomData *cd, void *element, int type, const char *name); +void BM_elem_float_data_named_set(CustomData *cd, void *element, int type, const char *name, const float val); +void BM_elem_meshsample_data_named_get(CustomData *cd, void *element, int type, const char *name, struct MSurfaceSample *val); +void BM_elem_meshsample_data_named_set(CustomData *cd, void *element, int type, const char *name, const struct MSurfaceSample *val); void BM_face_interp_from_face_ex( BMesh *bm, BMFace *f_dst, const BMFace *f_src, const bool do_vertex, diff --git a/source/blender/bmesh/intern/bmesh_iterators_inline.h b/source/blender/bmesh/intern/bmesh_iterators_inline.h index e68440021e6..43b054d7845 100644 --- a/source/blender/bmesh/intern/bmesh_iterators_inline.h +++ b/source/blender/bmesh/intern/bmesh_iterators_inline.h @@ -29,6 +29,8 @@ #ifndef __BMESH_ITERATORS_INLINE_H__ #define __BMESH_ITERATORS_INLINE_H__ +#include "BLI_mempool.h" + /* inline here optimizes out the switch statement when called with * constant values (which is very common), nicer for loop-in-loop situations */ @@ -43,7 +45,6 @@ BLI_INLINE void *BM_iter_step(BMIter *iter) return iter->step(iter); } - /** * \brief Iterator Init * diff --git a/source/blender/bmesh/intern/bmesh_mesh_conv.c b/source/blender/bmesh/intern/bmesh_mesh_conv.c index 24d70cefb2e..a0ef12c28db 100644 --- a/source/blender/bmesh/intern/bmesh_mesh_conv.c +++ b/source/blender/bmesh/intern/bmesh_mesh_conv.c @@ -98,6 +98,9 @@ #include "bmesh.h" #include "intern/bmesh_private.h" /* for element checking */ +/* XXX stupid hack: linker otherwise strips bmesh_strands_conv.c because it is not used inside bmesh */ +void *__dummy_hack__ = &BM_strands_count_psys_keys; + /** * Currently this is only used for Python scripts * which may fail to keep matching UV/TexFace layers. @@ -226,6 +229,17 @@ void BM_mesh_bm_from_me( BMesh *bm, Mesh *me, const bool calc_face_normal, const bool set_key, int act_key_nr) { + BM_mesh_bm_from_me_ex(bm, me, CD_MASK_BMESH, calc_face_normal, set_key, act_key_nr); +} + +/** + * \brief Mesh -> BMesh + * + * \warning This function doesn't calculate face normals. + */ +void BM_mesh_bm_from_me_ex(BMesh *bm, Mesh *me, CustomDataMask mask, + const bool calc_face_normal, const bool set_key, int act_key_nr) +{ MVert *mvert; MEdge *medge; MLoop *mloop; @@ -251,10 +265,10 @@ void BM_mesh_bm_from_me( if (!me || !me->totvert) { if (me) { /*no verts? still copy customdata layout*/ - CustomData_copy(&me->vdata, &bm->vdata, CD_MASK_BMESH, CD_ASSIGN, 0); - CustomData_copy(&me->edata, &bm->edata, CD_MASK_BMESH, CD_ASSIGN, 0); - CustomData_copy(&me->ldata, &bm->ldata, CD_MASK_BMESH, CD_ASSIGN, 0); - CustomData_copy(&me->pdata, &bm->pdata, CD_MASK_BMESH, CD_ASSIGN, 0); + CustomData_copy(&me->vdata, &bm->vdata, mask, CD_ASSIGN, 0); + CustomData_copy(&me->edata, &bm->edata, mask, CD_ASSIGN, 0); + CustomData_copy(&me->ldata, &bm->ldata, mask, CD_ASSIGN, 0); + CustomData_copy(&me->pdata, &bm->pdata, mask, CD_ASSIGN, 0); CustomData_bmesh_init_pool(&bm->vdata, me->totvert, BM_VERT); CustomData_bmesh_init_pool(&bm->edata, me->totedge, BM_EDGE); @@ -266,10 +280,10 @@ void BM_mesh_bm_from_me( vtable = MEM_mallocN(sizeof(void **) * me->totvert, "mesh to bmesh vtable"); - CustomData_copy(&me->vdata, &bm->vdata, CD_MASK_BMESH, CD_CALLOC, 0); - CustomData_copy(&me->edata, &bm->edata, CD_MASK_BMESH, CD_CALLOC, 0); - CustomData_copy(&me->ldata, &bm->ldata, CD_MASK_BMESH, CD_CALLOC, 0); - CustomData_copy(&me->pdata, &bm->pdata, CD_MASK_BMESH, CD_CALLOC, 0); + CustomData_copy(&me->vdata, &bm->vdata, mask, CD_CALLOC, 0); + CustomData_copy(&me->edata, &bm->edata, mask, CD_CALLOC, 0); + CustomData_copy(&me->ldata, &bm->ldata, mask, CD_CALLOC, 0); + CustomData_copy(&me->pdata, &bm->pdata, mask, CD_CALLOC, 0); /* make sure uv layer names are consisten */ totuv = CustomData_number_of_layers(&bm->pdata, CD_MTEXPOLY); @@ -570,6 +584,11 @@ BLI_INLINE void bmesh_quick_edgedraw_flag(MEdge *med, BMEdge *e) void BM_mesh_bm_to_me(BMesh *bm, Mesh *me, bool do_tessface) { + BM_mesh_bm_to_me_ex(bm, me, CD_MASK_MESH, do_tessface); +} + +void BM_mesh_bm_to_me_ex(BMesh *bm, Mesh *me, CustomDataMask mask, bool do_tessface) +{ MLoop *mloop; MPoly *mpoly; MVert *mvert, *oldverts; @@ -629,10 +648,10 @@ void BM_mesh_bm_to_me(BMesh *bm, Mesh *me, bool do_tessface) me->totface = 0; me->act_face = -1; - CustomData_copy(&bm->vdata, &me->vdata, CD_MASK_MESH, CD_CALLOC, me->totvert); - CustomData_copy(&bm->edata, &me->edata, CD_MASK_MESH, CD_CALLOC, me->totedge); - CustomData_copy(&bm->ldata, &me->ldata, CD_MASK_MESH, CD_CALLOC, me->totloop); - CustomData_copy(&bm->pdata, &me->pdata, CD_MASK_MESH, CD_CALLOC, me->totpoly); + CustomData_copy(&bm->vdata, &me->vdata, mask, CD_CALLOC, me->totvert); + CustomData_copy(&bm->edata, &me->edata, mask, CD_CALLOC, me->totedge); + CustomData_copy(&bm->ldata, &me->ldata, mask, CD_CALLOC, me->totloop); + CustomData_copy(&bm->pdata, &me->pdata, mask, CD_CALLOC, me->totpoly); CustomData_add_layer(&me->vdata, CD_MVERT, CD_ASSIGN, mvert, me->totvert); CustomData_add_layer(&me->edata, CD_MEDGE, CD_ASSIGN, medge, me->totedge); diff --git a/source/blender/bmesh/intern/bmesh_mesh_conv.h b/source/blender/bmesh/intern/bmesh_mesh_conv.h index ce286f6c662..0d235a128ab 100644 --- a/source/blender/bmesh/intern/bmesh_mesh_conv.h +++ b/source/blender/bmesh/intern/bmesh_mesh_conv.h @@ -32,7 +32,10 @@ * \ingroup bmesh */ +#include "BLI_sys_types.h" + struct Mesh; +typedef uint64_t CustomDataMask; void BM_mesh_cd_validate(BMesh *bm); void BM_mesh_cd_flag_ensure(BMesh *bm, struct Mesh *mesh, const char cd_flag); @@ -42,6 +45,9 @@ char BM_mesh_cd_flag_from_bmesh(BMesh *bm); void BM_mesh_bm_from_me( BMesh *bm, struct Mesh *me, const bool calc_face_normal, const bool set_key, int act_key_nr); +void BM_mesh_bm_from_me_ex(BMesh *bm, struct Mesh *me, CustomDataMask mask, + const bool calc_face_normal, const bool set_key, int act_key_nr); void BM_mesh_bm_to_me(BMesh *bm, struct Mesh *me, bool do_tessface); +void BM_mesh_bm_to_me_ex(BMesh *bm, struct Mesh *me, CustomDataMask mask, bool do_tessface); #endif /* __BMESH_MESH_CONV_H__ */ diff --git a/source/blender/bmesh/intern/bmesh_operators_private.h b/source/blender/bmesh/intern/bmesh_operators_private.h index 979f7d2640a..8b9c25bf95c 100644 --- a/source/blender/bmesh/intern/bmesh_operators_private.h +++ b/source/blender/bmesh/intern/bmesh_operators_private.h @@ -54,6 +54,7 @@ void bmo_create_icosphere_exec(BMesh *bm, BMOperator *op); void bmo_create_monkey_exec(BMesh *bm, BMOperator *op); void bmo_create_uvsphere_exec(BMesh *bm, BMOperator *op); void bmo_create_vert_exec(BMesh *bm, BMOperator *op); +//void bmo_create_strand_exec(BMesh *bm, BMOperator *op); void bmo_delete_exec(BMesh *bm, BMOperator *op); void bmo_dissolve_edges_exec(BMesh *bm, BMOperator *op); void bmo_dissolve_faces_exec(BMesh *bm, BMOperator *op); diff --git a/source/blender/bmesh/intern/bmesh_strands.c b/source/blender/bmesh/intern/bmesh_strands.c new file mode 100644 index 00000000000..ae76c4761ad --- /dev/null +++ b/source/blender/bmesh/intern/bmesh_strands.c @@ -0,0 +1,145 @@ +/* + * ***** 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Contributor(s): Lukas Toenne. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/bmesh/intern/bmesh_strands.c + * \ingroup bmesh + */ + +#include "MEM_guardedalloc.h" + +#include "BLI_utildefines.h" +#include "BLI_math.h" +#include "BLI_mempool.h" + +#include "bmesh.h" +#include "bmesh_private.h" + +/* + * STRANDS OF MESH CALLBACKS + */ + +void bmstranditer__strands_of_mesh_begin(struct BMIter__elem_of_mesh *iter) +{ + BLI_mempool_iternew(iter->pooliter.pool, &iter->pooliter); +} + +void *bmstranditer__strands_of_mesh_step(struct BMIter__elem_of_mesh *iter) +{ + BMVert *v; + + do { + v = BLI_mempool_iterstep(&iter->pooliter); + } while (v && !BM_strands_vert_is_root(v)); + + return v; +} + +/* + * VERTS OF STRAND CALLBACKS + */ + +/* BMIter__vert_of_strand is not included in the union in BMIter, just make sure it is big enough */ +BLI_STATIC_ASSERT(sizeof(BMIter__vert_of_strand) <= sizeof(BMIter), "BMIter must be at least as large as BMIter__vert_of_strand") + +void bmstranditer__verts_of_strand_begin(struct BMIter__vert_of_strand *iter) +{ + iter->e_next = iter->v_next->e; +} + +void *bmstranditer__verts_of_strand_step(struct BMIter__vert_of_strand *iter) +{ + BMVert *v_curr = iter->v_next; + + if (iter->e_next) { + BMEdge *e_first = iter->e_next; + + /* select the other vertex of the current edge */ + iter->v_next = (iter->v_next == iter->e_next->v1 ? iter->e_next->v2 : iter->e_next->v1); + + /* select the next edge of the current vertex */ + iter->e_next = bmesh_disk_edge_next(iter->e_next, iter->v_next); + if (iter->e_next == e_first) { + /* only one edge means the last segment, terminate */ + iter->e_next = NULL; + } + } + else + iter->v_next = NULL; /* last vertex, terminate */ + + return v_curr; +} + +/* ------------------------------------------------------------------------- */ + +int BM_strands_count(BMesh *bm) +{ + BMVert *v; + BMIter iter; + + int count = 0; + BM_ITER_STRANDS(v, &iter, bm, BM_STRANDS_OF_MESH) { + ++count; + } + + return count; +} + +int BM_strands_keys_count(BMVert *root) +{ + BMVert *v; + BMIter iter; + + int count = 0; + BM_ITER_STRANDS_ELEM(v, &iter, root, BM_VERTS_OF_STRAND) { + ++count; + } + + return count; +} + +/* ------------------------------------------------------------------------- */ + +/* Create a new strand */ +BMVert *BM_strands_create(BMesh *bm, int len, bool set_defaults) +{ + float co[3] = {0.0f, 0.0f, 0.0f}; + + BMVert *root, *v = NULL, *vprev; + int k; + + for (k = 0; k < len; ++k) { + vprev = v; + v = BM_vert_create(bm, co, NULL, set_defaults ? BM_CREATE_NOP : BM_CREATE_SKIP_CD); + + zero_v3(v->no); + + /* root */ + if (k == 0) { + root = v; + } + else { + /*BMEdge *e =*/ BM_edge_create(bm, vprev, v, NULL, set_defaults ? BM_CREATE_NOP : BM_CREATE_SKIP_CD); + } + } + + return root; +} diff --git a/source/blender/bmesh/intern/bmesh_strands.h b/source/blender/bmesh/intern/bmesh_strands.h new file mode 100644 index 00000000000..cd4267f0bb3 --- /dev/null +++ b/source/blender/bmesh/intern/bmesh_strands.h @@ -0,0 +1,215 @@ +/* + * ***** 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Contributor(s): Lukas Toenne. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#ifndef __BMESH_STRANDS_H__ +#define __BMESH_STRANDS_H__ + +/** \file blender/bmesh/intern/bmesh_strands.h + * \ingroup bmesh + */ + +#include "BLI_utildefines.h" + +#include "bmesh.h" +#include "bmesh_queries.h" +#include "bmesh_structure.h" + +/* True if v is the root of a strand */ +BLI_INLINE bool BM_strands_vert_is_root(BMVert *v) +{ + BMEdge *e_first = v->e; + BMEdge *e_next; + + if (!e_first) + return true; /* single vertex is both root and tip */ + e_next = bmesh_disk_edge_next(e_first, v); + + /* with a single edge, the vertex is either first or last of the curve; + * first vertex is defined as the root + */ + if (e_next == e_first) { + if (e_first->v1 == v) + return true; + } + return false; +} + +/* True if v is the tip of a strand */ +BLI_INLINE bool BM_strands_vert_is_tip(BMVert *v) +{ + BMEdge *e_first = v->e; + BMEdge *e_next; + + if (!e_first) + return true; /* single vertex is both root and tip */ + e_next = bmesh_disk_edge_next(e_first, v); + + /* with a single edge, the vertex is either first or last of the curve; + * last vertex is defined as the tip + */ + if (e_next == e_first) { + if (e_first->v2 == v) + return true; + } + return false; +} + +/* Next vertex on a strand */ +BLI_INLINE BMVert *BM_strands_vert_next(BMVert *v) +{ + BMEdge *e_first = v->e; + BMEdge *e_next; + + /* one of the edges leads to the previous vertex */ + if (e_first) { + if (e_first->v1 == v) + return e_first->v2; + + e_next = bmesh_disk_edge_next(e_first, v); + if (e_next->v1 == v) + return e_next->v2; + } + return NULL; +} + +/* Previous vertex on a strand */ +BLI_INLINE BMVert *BM_strands_vert_prev(BMVert *v) +{ + BMEdge *e_first = v->e; + BMEdge *e_next; + + /* one of the edges leads to the previous vertex */ + if (e_first) { + if (e_first->v2 == v) + return e_first->v1; + + e_next = bmesh_disk_edge_next(e_first, v); + if (e_next->v2 == v) + return e_next->v1; + } + return NULL; +} + +int BM_strands_count(BMesh *bm); +int BM_strands_keys_count(BMVert *root); + +/* Create a new strand */ +struct BMVert *BM_strands_create(struct BMesh *bm, int len, bool set_defaults); + +/* ==== Iterators ==== */ + +typedef enum BMStrandsIterType { + BM_STRANDS_OF_MESH, + BM_VERTS_OF_STRAND, +} BMStrandsIterType; + +#define BM_ITER_STRANDS(ele, iter, bm, itype) \ + for (BM_CHECK_TYPE_ELEM_ASSIGN(ele) = BM_strand_iter_new(iter, bm, itype, NULL); \ + ele; \ + BM_CHECK_TYPE_ELEM_ASSIGN(ele) = BM_iter_step(iter)) + +#define BM_ITER_STRANDS_INDEX(ele, iter, bm, itype, indexvar) \ + for (BM_CHECK_TYPE_ELEM_ASSIGN(ele) = BM_strand_iter_new(iter, bm, itype, NULL), indexvar = 0; \ + ele; \ + BM_CHECK_TYPE_ELEM_ASSIGN(ele) = BM_iter_step(iter), (indexvar)++) + +#define BM_ITER_STRANDS_ELEM(ele, iter, data, itype) \ + for (BM_CHECK_TYPE_ELEM_ASSIGN(ele) = BM_strand_iter_new(iter, NULL, itype, data); \ + ele; \ + BM_CHECK_TYPE_ELEM_ASSIGN(ele) = BM_iter_step(iter)) + +#define BM_ITER_STRANDS_ELEM_INDEX(ele, iter, data, itype, indexvar) \ + for (BM_CHECK_TYPE_ELEM_ASSIGN(ele) = BM_strand_iter_new(iter, NULL, itype, data), indexvar = 0; \ + ele; \ + BM_CHECK_TYPE_ELEM_ASSIGN(ele) = BM_iter_step(iter), (indexvar)++) + +typedef struct BMIter__vert_of_strand { + BMVert *v_next; + BMEdge *e_next; +} BMIter__vert_of_strand; + +void bmstranditer__strands_of_mesh_begin(struct BMIter__elem_of_mesh *iter); +void *bmstranditer__strands_of_mesh_step(struct BMIter__elem_of_mesh *iter); + +void bmstranditer__verts_of_strand_begin(struct BMIter__vert_of_strand *iter); +void *bmstranditer__verts_of_strand_step(struct BMIter__vert_of_strand *iter); + +BLI_INLINE bool BM_strand_iter_init(BMIter *iter, BMesh *bm, const char itype, void *data) +{ + /* int argtype; */ + iter->itype = itype; + + /* inlining optimizes out this switch when called with the defined type */ + switch ((BMStrandsIterType)itype) { + case BM_STRANDS_OF_MESH: + BLI_assert(bm != NULL); + BLI_assert(data == NULL); + iter->begin = (BMIter__begin_cb)bmstranditer__strands_of_mesh_begin; + iter->step = (BMIter__step_cb)bmstranditer__strands_of_mesh_step; + iter->data.elem_of_mesh.pooliter.pool = bm->vpool; + break; + case BM_VERTS_OF_STRAND: { + BMVert *root; + + BLI_assert(data != NULL); + BLI_assert(((BMElem *)data)->head.htype == BM_VERT); + root = (BMVert *)data; + BLI_assert(BM_strands_vert_is_root(root)); + iter->begin = (BMIter__begin_cb)bmstranditer__verts_of_strand_begin; + iter->step = (BMIter__step_cb)bmstranditer__verts_of_strand_step; + ((BMIter__vert_of_strand *)(&iter->data))->v_next = root; + break; + } + default: + /* fallback to regular bmesh iterator */ + return BM_iter_init(iter, bm, itype, data); + break; + } + + iter->begin(iter); + + return true; +} + +/** + * \brief Iterator New + * + * Takes a bmesh iterator structure and fills + * it with the appropriate function pointers based + * upon its type and then calls BMeshIter_step() + * to return the first element of the iterator. + * + */ +BLI_INLINE void *BM_strand_iter_new(BMIter *iter, BMesh *bm, const char itype, void *data) +{ + if (LIKELY(BM_strand_iter_init(iter, bm, itype, data))) { + return BM_iter_step(iter); + } + else { + return NULL; + } +} + +#define BM_strand_iter_new(iter, bm, itype, data) \ + (BM_ITER_CHECK_TYPE_DATA(data), BM_strand_iter_new(iter, bm, itype, data)) + +#endif /* __BMESH_STRANDS_H__ */ diff --git a/source/blender/bmesh/intern/bmesh_strands_conv.c b/source/blender/bmesh/intern/bmesh_strands_conv.c new file mode 100644 index 00000000000..50ea50c9299 --- /dev/null +++ b/source/blender/bmesh/intern/bmesh_strands_conv.c @@ -0,0 +1,1314 @@ +/* + * ***** 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Contributor(s): Lukas Toenne. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/bmesh/intern/bmesh_strands_conv.c + * \ingroup bmesh + * + * BM mesh conversion functions. + */ + +#include "DNA_cache_library_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_object_types.h" +#include "DNA_particle_types.h" +#include "DNA_key_types.h" +#include "DNA_strands_types.h" + +#include "MEM_guardedalloc.h" + +#include "BLI_listbase.h" +#include "BLI_math.h" + +#include "BKE_cache_library.h" +#include "BKE_customdata.h" +#include "BKE_key.h" +#include "BKE_main.h" +#include "BKE_mesh_sample.h" +#include "BKE_strands.h" +#include "BKE_particle.h" + +#include "bmesh.h" +#include "intern/bmesh_private.h" /* for element checking */ + +const char *CD_HAIR_SEGMENT_LENGTH = "HAIR_SEGMENT_LENGTH"; +const char *CD_HAIR_MASS = "HAIR_MASS"; +const char *CD_HAIR_WEIGHT = "HAIR_WEIGHT"; +const char *CD_HAIR_ROOT_LOCATION = "HAIR_ROOT_LOCATION"; + +/* ------------------------------------------------------------------------- */ + +/** + * Currently this is only used for Python scripts + * which may fail to keep matching UV/TexFace layers. + * + * \note This should only perform any changes in exceptional cases, + * if we need this to be faster we could inline #BM_data_layer_add and only + * call #update_data_blocks once at the end. + */ +void BM_strands_cd_validate(BMesh *UNUSED(bm)) +{ +} + +void BM_strands_cd_flag_ensure(BMesh *bm, const char cd_flag) +{ + const char cd_flag_all = BM_strands_cd_flag_from_bmesh(bm) | cd_flag; + BM_strands_cd_flag_apply(bm, cd_flag_all); +} + +void BM_strands_cd_flag_apply(BMesh *bm, const char UNUSED(cd_flag)) +{ + /* CustomData_bmesh_init_pool() must run first */ + BLI_assert(bm->vdata.totlayer == 0 || bm->vdata.pool != NULL); + BLI_assert(bm->edata.totlayer == 0 || bm->edata.pool != NULL); + + if (CustomData_get_named_layer_index(&bm->vdata, CD_PROP_FLT, CD_HAIR_MASS) < 0) { + BM_data_layer_add_named(bm, &bm->vdata, CD_PROP_FLT, CD_HAIR_MASS); + } + if (CustomData_get_named_layer_index(&bm->vdata, CD_PROP_FLT, CD_HAIR_WEIGHT) < 0) { + BM_data_layer_add_named(bm, &bm->vdata, CD_PROP_FLT, CD_HAIR_WEIGHT); + } + if (CustomData_get_named_layer_index(&bm->vdata, CD_MSURFACE_SAMPLE, CD_HAIR_ROOT_LOCATION) < 0) { + BM_data_layer_add_named(bm, &bm->vdata, CD_MSURFACE_SAMPLE, CD_HAIR_ROOT_LOCATION); + } + if (CustomData_get_named_layer_index(&bm->vdata, CD_PROP_FLT, CD_HAIR_SEGMENT_LENGTH) < 0) { + BM_data_layer_add_named(bm, &bm->vdata, CD_PROP_FLT, CD_HAIR_SEGMENT_LENGTH); + } +} + +char BM_strands_cd_flag_from_bmesh(BMesh *UNUSED(bm)) +{ + char cd_flag = 0; + return cd_flag; +} + +/* ------------------------------------------------------------------------- */ +/* CacheLibrary */ + +static KeyBlock *bm_set_shapekey_from_strands_key(BMesh *bm, Strands *strands, Key *key, int act_key_nr) +{ + int totvert = strands->totverts; + KeyBlock *actkey, *block; + int i, j; + + if (!key) { + return NULL; + } + + if (act_key_nr != 0) + actkey = BLI_findlink(&key->block, act_key_nr - 1); + else + actkey = NULL; + + CustomData_add_layer(&bm->vdata, CD_SHAPE_KEYINDEX, CD_ASSIGN, NULL, 0); + + if (actkey && actkey->totelem == totvert) { + bm->shapenr = act_key_nr; + } + + for (i = 0, block = key->block.first; block; block = block->next, i++) { + CustomData_add_layer_named(&bm->vdata, CD_SHAPEKEY, + CD_ASSIGN, NULL, 0, block->name); + + j = CustomData_get_layer_index_n(&bm->vdata, CD_SHAPEKEY, i); + bm->vdata.layers[j].uid = block->uid; + } + + return actkey; +} + +/* create vertex and edge data for BMesh based on strand data */ +static void bm_make_strands(BMesh *bm, Strands *strands, Key *key, struct DerivedMesh *UNUSED(emitter_dm), float mat[4][4], float (*keyco)[3], int cd_shape_keyindex_offset) +{ + KeyBlock *block; + StrandIterator it_strand; + + int vindex, eindex; + BMVert *v = NULL, *v_prev; + BMEdge *e; + + vindex = 0; + eindex = 0; + for (BKE_strand_iter_init(&it_strand, strands); BKE_strand_iter_valid(&it_strand); BKE_strand_iter_next(&it_strand)) { + StrandVertexIterator it_vert; + + for (BKE_strand_vertex_iter_init(&it_vert, &it_strand); BKE_strand_vertex_iter_valid(&it_vert); BKE_strand_vertex_iter_next(&it_vert)) { + float co[3]; + + copy_v3_v3(co, keyco ? keyco[vindex] : it_vert.vertex->co); + /* transform to duplicator local space */ + mul_m4_v3(mat, co); + + v_prev = v; + v = BM_vert_create(bm, co, NULL, BM_CREATE_SKIP_CD); + BM_elem_index_set(v, vindex); /* set_ok */ + + /* transfer flag */ +// v->head.hflag = BM_vert_flag_from_mflag(mvert->flag & ~SELECT); + + /* this is necessary for selection counts to work properly */ +// if (hkey->editflag & SELECT) { +// BM_vert_select_set(bm, v, true); +// } + +// normal_short_to_float_v3(v->no, mvert->no); + + /* Copy Custom Data */ +// CustomData_to_bmesh_block(&me->vdata, &bm->vdata, vindex, &v->head.data, true); + CustomData_bmesh_set_default(&bm->vdata, &v->head.data); + +// BM_elem_float_data_named_set(&bm->vdata, v, CD_PROP_FLT, CD_HAIR_MASS, mass); + BM_elem_float_data_named_set(&bm->vdata, v, CD_PROP_FLT, CD_HAIR_WEIGHT, it_vert.vertex->weight); + + /* root */ + BM_elem_meshsample_data_named_set(&bm->vdata, v, CD_MSURFACE_SAMPLE, CD_HAIR_ROOT_LOCATION, &it_strand.curve->msurf); + + /* set shapekey data */ + if (key) { + int k; + + /* set shape key original index */ + if (cd_shape_keyindex_offset != -1) + BM_ELEM_CD_SET_INT(v, cd_shape_keyindex_offset, vindex); + + for (block = key->block.first, k = 0; block; block = block->next, k++) { + float *co = CustomData_bmesh_get_n(&bm->vdata, v->head.data, CD_SHAPEKEY, k); + + if (co) { + mul_v3_m4v3(co, mat, ((float *)block->data) + 3 * vindex); + } + } + } + + vindex += 1; + + if (it_vert.index > 0) { + e = BM_edge_create(bm, v_prev, v, NULL, BM_CREATE_SKIP_CD); + BM_elem_index_set(e, eindex); /* set_ok; one less edge than vertices for each particle */ + + /* transfer flags */ +// e->head.hflag = BM_edge_flag_from_mflag(medge->flag & ~SELECT); + + /* this is necessary for selection counts to work properly */ +// if (medge->flag & SELECT) { +// BM_edge_select_set(bm, e, true); +// } + + /* Copy Custom Data */ +// CustomData_to_bmesh_block(&me->edata, &bm->edata, eindex, &e->head.data, true); + CustomData_bmesh_set_default(&bm->edata, &e->head.data); + + eindex += 1; + } + } + + } + + bm->elem_index_dirty &= ~(BM_VERT | BM_EDGE); /* added in order, clear dirty flag */ +} + +/** + * \brief ParticleSystem -> BMesh + */ +void BM_strands_bm_from_strands(BMesh *bm, Strands *strands, float mat[4][4], Key *key, struct DerivedMesh *emitter_dm, + const bool set_key, int act_key_nr) +{ + KeyBlock *actkey; + float (*keyco)[3] = NULL; + int totvert, totedge; + + int cd_shape_keyindex_offset; + + /* free custom data */ + /* this isnt needed in most cases but do just incase */ + CustomData_free(&bm->vdata, bm->totvert); + CustomData_free(&bm->edata, bm->totedge); + CustomData_free(&bm->ldata, bm->totloop); + CustomData_free(&bm->pdata, bm->totface); + + totvert = strands->totverts; + totedge = strands->totverts - strands->totcurves; + + if (!strands || !totvert || !totedge) { + if (strands) { /*no verts? still copy customdata layout*/ + CustomData_bmesh_init_pool(&bm->vdata, totvert, BM_VERT); + CustomData_bmesh_init_pool(&bm->edata, totedge, BM_EDGE); + CustomData_bmesh_init_pool(&bm->ldata, 0, BM_LOOP); + CustomData_bmesh_init_pool(&bm->pdata, 0, BM_FACE); + } + return; /* sanity check */ + } + + actkey = bm_set_shapekey_from_strands_key(bm, strands, key, act_key_nr); + if (actkey) + keyco = actkey->data; + + CustomData_bmesh_init_pool(&bm->vdata, totvert, BM_VERT); + CustomData_bmesh_init_pool(&bm->edata, totedge, BM_EDGE); + + BM_strands_cd_flag_apply(bm, 0); + + cd_shape_keyindex_offset = key ? CustomData_get_offset(&bm->vdata, CD_SHAPE_KEYINDEX) : -1; + + bm_make_strands(bm, strands, key, emitter_dm, mat, set_key ? keyco : NULL, cd_shape_keyindex_offset); + +#if 0 /* TODO */ + if (me->mselect && me->totselect != 0) { + + BMVert **vert_array = MEM_mallocN(sizeof(BMVert *) * bm->totvert, "VSelConv"); + BMEdge **edge_array = MEM_mallocN(sizeof(BMEdge *) * bm->totedge, "ESelConv"); + BMFace **face_array = MEM_mallocN(sizeof(BMFace *) * bm->totface, "FSelConv"); + MSelect *msel; + +#pragma omp parallel sections if (bm->totvert + bm->totedge + bm->totface >= BM_OMP_LIMIT) + { +#pragma omp section + { BM_iter_as_array(bm, BM_VERTS_OF_MESH, NULL, (void **)vert_array, bm->totvert); } +#pragma omp section + { BM_iter_as_array(bm, BM_EDGES_OF_MESH, NULL, (void **)edge_array, bm->totedge); } +#pragma omp section + { BM_iter_as_array(bm, BM_FACES_OF_MESH, NULL, (void **)face_array, bm->totface); } + } + + for (i = 0, msel = me->mselect; i < me->totselect; i++, msel++) { + switch (msel->type) { + case ME_VSEL: + BM_select_history_store(bm, (BMElem *)vert_array[msel->index]); + break; + case ME_ESEL: + BM_select_history_store(bm, (BMElem *)edge_array[msel->index]); + break; + case ME_FSEL: + BM_select_history_store(bm, (BMElem *)face_array[msel->index]); + break; + } + } + + MEM_freeN(vert_array); + MEM_freeN(edge_array); + MEM_freeN(face_array); + } + else { + me->totselect = 0; + if (me->mselect) { + MEM_freeN(me->mselect); + me->mselect = NULL; + } + } +#endif +} + +/* ------------------------------------------------------------------------- */ + +#if 0 +/** + * \brief BMesh -> Mesh + */ +static BMVert **bm_to_mesh_vertex_map(BMesh *bm, int ototvert) +{ + const int cd_shape_keyindex_offset = CustomData_get_offset(&bm->vdata, CD_SHAPE_KEYINDEX); + BMVert **vertMap = NULL; + BMVert *eve; + int i = 0; + BMIter iter; + + /* caller needs to ensure this */ + BLI_assert(ototvert > 0); + + vertMap = MEM_callocN(sizeof(*vertMap) * ototvert, "vertMap"); + if (cd_shape_keyindex_offset != -1) { + BM_ITER_MESH_INDEX (eve, &iter, bm, BM_VERTS_OF_MESH, i) { + const int keyi = BM_ELEM_CD_GET_INT(eve, cd_shape_keyindex_offset); + if ((keyi != ORIGINDEX_NONE) && (keyi < ototvert)) { + vertMap[keyi] = eve; + } + } + } + else { + BM_ITER_MESH_INDEX (eve, &iter, bm, BM_VERTS_OF_MESH, i) { + if (i < ototvert) { + vertMap[i] = eve; + } + else { + break; + } + } + } + + return vertMap; +} + +BLI_INLINE void bmesh_quick_edgedraw_flag(MEdge *med, BMEdge *e) +{ + /* this is a cheap way to set the edge draw, its not precise and will + * pick the first 2 faces an edge uses. + * The dot comparison is a little arbitrary, but set so that a 5 subd + * IcoSphere won't vanish but subd 6 will (as with pre-bmesh blender) */ + + + if ( /* (med->flag & ME_EDGEDRAW) && */ /* assume to be true */ + (e->l && (e->l != e->l->radial_next)) && + (dot_v3v3(e->l->f->no, e->l->radial_next->f->no) > 0.9995f)) + { + med->flag &= ~ME_EDGEDRAW; + } + else { + med->flag |= ME_EDGEDRAW; + } +} +#endif + +static void bm_strands_make_strand(BMesh *bm, BMVert *root, Strands *UNUSED(strands), float imat[4][4], Key *UNUSED(key), + struct DerivedMesh *emitter_dm, struct BVHTreeFromMesh *UNUSED(emitter_bvhtree), + StrandIterator *it_strand) +{ + int numverts = BM_strands_keys_count(root); + + BMVert *v; + BMIter iter; + StrandVertexIterator it_vert; + + it_strand->curve->numverts = numverts; + /* init root matrix, fully constructed below for non-degenerate strands */ + unit_m3(it_strand->curve->root_matrix); + + BKE_strand_vertex_iter_init(&it_vert, it_strand); + BM_ITER_STRANDS_ELEM(v, &iter, root, BM_VERTS_OF_STRAND) { + BLI_assert(BKE_strand_vertex_iter_valid(&it_vert)); + + /* root */ + if (it_vert.index == 0) { + float loc[3], nor[3], tang[3]; + BM_elem_meshsample_data_named_get(&bm->vdata, v, CD_MSURFACE_SAMPLE, CD_HAIR_ROOT_LOCATION, &it_strand->curve->msurf); + BKE_mesh_sample_eval(emitter_dm, &it_strand->curve->msurf, loc, nor, tang); + + /* construct root matrix */ + copy_v3_v3(it_strand->curve->root_matrix[2], nor); + copy_v3_v3(it_strand->curve->root_matrix[0], tang); + cross_v3_v3v3(it_strand->curve->root_matrix[1], it_strand->curve->root_matrix[2], it_strand->curve->root_matrix[0]); + + /* transform from edit space (duplicator local space) back to the original object space */ + mul_mat3_m4_v3(imat, it_strand->curve->root_matrix[0]); + mul_mat3_m4_v3(imat, it_strand->curve->root_matrix[1]); + mul_mat3_m4_v3(imat, it_strand->curve->root_matrix[2]); + } + + /* transform from edit space (duplicator local space) back to the original object space */ + mul_v3_m4v3(it_vert.vertex->co, imat, v->co); + it_vert.vertex->time = numverts > 0 ? (float)it_vert.index / (float)(numverts - 1) : 0.0f; + + if (it_vert.index == 0) { + /* weight 1.0 is used for pinning hair roots in particles */ + it_vert.vertex->weight = 1.0f; + } + else { + it_vert.vertex->weight = BM_elem_float_data_named_get(&bm->vdata, v, CD_PROP_FLT, CD_HAIR_WEIGHT); + } + + BKE_strand_vertex_iter_next(&it_vert); + + BM_CHECK_ELEMENT(v); + } +} + +/** + * returns customdata shapekey index from a keyblock or -1 + * \note could split this out into a more generic function */ +static int bm_shape_layer_index_from_kb(BMesh *bm, KeyBlock *currkey) +{ + int i; + int j = 0; + + for (i = 0; i < bm->vdata.totlayer; i++) { + if (bm->vdata.layers[i].type == CD_SHAPEKEY) { + if (currkey->uid == bm->vdata.layers[i].uid) { + return j; + } + j++; + } + } + return -1; +} + +/* go through and find any shapekey customdata layers + * that might not have corresponding KeyBlocks, and add them if + * necessary */ +static void bm_strands_add_missing_shapekeys(BMesh *bm, Key *key) +{ + KeyBlock *currkey; + int i; + + for (i = 0; i < bm->vdata.totlayer; i++) { + const CustomDataLayer *layer = &bm->vdata.layers[i]; + if (layer->type != CD_SHAPEKEY) + continue; + + for (currkey = key->block.first; currkey; currkey = currkey->next) { + if (currkey->uid == layer->uid) + break; + } + + if (!currkey) { + currkey = BKE_keyblock_add(key, layer->name); + currkey->uid = layer->uid; + } + } +} + +/* returns offset of the edit against the active shape, so other shapes can compensate accordingly to avoid deformation */ +static void bm_strands_get_basiskey_offset(BMesh *bm, Strands *strands, Key *key, int cd_shape_keyindex_offset, float (**r_offset)[3]) +{ + *r_offset = NULL; + + /* only need offsets for relative shape keys */ + if (key->type == KEY_RELATIVE) { + + KeyBlock *actkey = BLI_findlink(&key->block, bm->shapenr - 1); + /* unlikely, but the active key may not be valid if the bmesh and the mesh are out of sync */ + if (!actkey) + return; + + /* only if active key is a base */ + if (BKE_keyblock_is_basis(key, bm->shapenr - 1) && cd_shape_keyindex_offset >= 0) { + float (*fp)[3] = actkey->data; + float (*ofs)[3] = MEM_callocN(sizeof(float) * 3 * bm->totvert, "currkey->data"); + BMIter iter; + BMVert *eve; + StrandsVertex *svert; + int i; + + svert = strands->verts; + BM_ITER_MESH_INDEX (eve, &iter, bm, BM_VERTS_OF_MESH, i) { + const int keyi = BM_ELEM_CD_GET_INT(eve, cd_shape_keyindex_offset); + + if (keyi != ORIGINDEX_NONE) { + sub_v3_v3v3(ofs[i], svert->co, fp[keyi]); + } + else { + /* if there are new vertices in the mesh, we can't propagate the offset + * because it will only work for the existing vertices and not the new + * ones, creating a mess when doing e.g. subdivide + translate */ + MEM_freeN(ofs); + ofs = NULL; + break; + } + + svert++; + } + + *r_offset = ofs; + } + } +} + +static float *bm_strands_apply_keyblock(BMesh *bm, Strands *strands, StrandsVertex *oldverts, float imat[4][4], Key *key, int cd_shape_keyindex_offset, + KeyBlock *kb, KeyBlock *actkb, float (*oldkey)[3], float (*offset)[3]) +{ + const bool apply_offset = (offset && (kb != actkb) && (bm->shapenr - 1 == kb->relative)); + const int shape_layer_index = bm_shape_layer_index_from_kb(bm, kb); + const int cd_shape_offset = CustomData_get_n_offset(&bm->vdata, CD_SHAPEKEY, shape_layer_index); + + float *newkey, *fp; + BMIter iter; + BMVert *eve; + StrandsVertex *svert; + int keyi; + float (*ofs_pt)[3] = offset; + + fp = newkey = MEM_callocN(key->elemsize * bm->totvert, "currkey->data"); + + svert = strands->verts; + BM_ITER_MESH (eve, &iter, bm, BM_VERTS_OF_MESH) { + + if (kb == actkb) { + copy_v3_v3(fp, eve->co); + + if (actkb != key->refkey) { /* important see bug [#30771] */ + if (cd_shape_keyindex_offset != -1) { + if (oldverts) { + keyi = BM_ELEM_CD_GET_INT(eve, cd_shape_keyindex_offset); + if (keyi != ORIGINDEX_NONE && keyi < kb->totelem) { /* valid old vertex */ + copy_v3_v3(svert->co, oldverts[keyi].co); + } + } + } + } + } + else if (shape_layer_index != -1) { + /* in most cases this runs */ + copy_v3_v3(fp, BM_ELEM_CD_GET_VOID_P(eve, cd_shape_offset)); + } + else if ((oldkey != NULL) && + (cd_shape_keyindex_offset != -1) && + ((keyi = BM_ELEM_CD_GET_INT(eve, cd_shape_keyindex_offset)) != ORIGINDEX_NONE) && + (keyi < kb->totelem)) + { + /* old method of reconstructing keys via vertice's original key indices, + * currently used if the new method above fails (which is theoretically + * possible in certain cases of undo) */ + copy_v3_v3(fp, oldkey[keyi]); + } + else { + /* fail! fill in with dummy value */ + copy_v3_v3(fp, svert->co); + } + + /* propagate edited basis offsets to other shapes */ + if (apply_offset) { + add_v3_v3(fp, *ofs_pt++); + } + + /* transform from edit space (duplicator local space) back to the original object space */ + mul_m4_v3(imat, fp); + + fp += 3; + svert++; + } + + return newkey; +} + +static void bm_strands_apply_shapekeys(BMesh *bm, Strands *strands, StrandsVertex *oldverts, float imat[4][4], Key *key) +{ + const int cd_shape_keyindex_offset = CustomData_get_offset(&bm->vdata, CD_SHAPE_KEYINDEX); + KeyBlock *actkb = BLI_findlink(&key->block, bm->shapenr - 1); + KeyBlock *kb; + float (*offset)[3] = NULL; + + bm_strands_add_missing_shapekeys(bm, key); + + if (oldverts) + bm_strands_get_basiskey_offset(bm, strands, key, cd_shape_keyindex_offset, &offset); + + for (kb = key->block.first; kb; kb = kb->next) { + float *newkey; + + newkey = bm_strands_apply_keyblock(bm, strands, oldverts, imat, key, cd_shape_keyindex_offset, kb, actkb, kb->data, offset); + + kb->totelem = bm->totvert; + if (kb->data) { + MEM_freeN(kb->data); + } + kb->data = newkey; + } + + if (offset) + MEM_freeN(offset); +} + +Strands *BM_strands_bm_to_strands(BMesh *bm, Strands *strands, float mat[4][4], Key *key, struct DerivedMesh *emitter_dm, struct BVHTreeFromMesh *emitter_bvhtree) +{ + Strands *oldstrands; + int ntotcurves; + float imat[4][4]; + + BMVert *root; + BMIter iter; + StrandIterator it_strand; + + ntotcurves = BM_strands_count(bm); + + /* lets save the old strands just in case we are actually working on + * a key ... we now do processing of the keys at the end */ + oldstrands = strands; + + invert_m4_m4(imat, mat); + + strands = BKE_strands_new(ntotcurves, bm->totvert); + +// strands->cd_flag = BM_strands_cd_flag_from_bmesh(bm); + + BKE_strand_iter_init(&it_strand, strands); + BM_ITER_STRANDS(root, &iter, bm, BM_STRANDS_OF_MESH) { + BLI_assert(BKE_strand_iter_valid(&it_strand)); + + bm_strands_make_strand(bm, root, strands, imat, key, emitter_dm, emitter_bvhtree, &it_strand); + + BKE_strand_iter_next(&it_strand); + } + bm->elem_index_dirty &= ~BM_VERT; + + BKE_strands_ensure_normals(strands); + + +#if 0 // TODO + { + BMEditSelection *selected; + me->totselect = BLI_listbase_count(&(bm->selected)); + + if (me->mselect) MEM_freeN(me->mselect); + + me->mselect = MEM_callocN(sizeof(MSelect) * me->totselect, "Mesh selection history"); + + + for (i = 0, selected = bm->selected.first; selected; i++, selected = selected->next) { + if (selected->htype == BM_VERT) { + me->mselect[i].type = ME_VSEL; + + } + else if (selected->htype == BM_EDGE) { + me->mselect[i].type = ME_ESEL; + + } + else if (selected->htype == BM_FACE) { + me->mselect[i].type = ME_FSEL; + } + + me->mselect[i].index = BM_elem_index_get(selected->ele); + } + } +#endif + + if (key) { + bm_strands_apply_shapekeys(bm, strands, oldstrands ? oldstrands->verts : NULL, imat, key); + } + + if (oldstrands) { + BKE_strands_free(oldstrands); + } + + return strands; +} + +/* ------------------------------------------------------------------------- */ +/* ParticleSystem */ + +int BM_strands_count_psys_keys(ParticleSystem *psys) +{ + ParticleData *pa; + int p; + int totkeys = 0; + + for (p = 0, pa = psys->particles; p < psys->totpart; ++p, ++pa) + totkeys += pa->totkey; + + return totkeys; +} + +#if 0 +static KeyBlock *bm_set_shapekey_from_psys(BMesh *bm, ParticleSystem *psys, int totvert, int act_key_nr) +{ + KeyBlock *actkey, *block; + int i, j; + + if (!psys->key) { + return NULL; + } + + if (act_key_nr != 0) + actkey = BLI_findlink(&psys->key->block, act_key_nr - 1); + else + actkey = NULL; + + CustomData_add_layer(&bm->vdata, CD_SHAPE_KEYINDEX, CD_ASSIGN, NULL, 0); + + /* check if we need to generate unique ids for the shapekeys. + * this also exists in the file reading code, but is here for + * a sanity check */ + if (!psys->key->uidgen) { + fprintf(stderr, + "%s had to generate shape key uid's in a situation we shouldn't need to! " + "(bmesh internal error)\n", + __func__); + + psys->key->uidgen = 1; + for (block = psys->key->block.first; block; block = block->next) { + block->uid = psys->key->uidgen++; + } + } + + if (actkey && actkey->totelem == totvert) { + bm->shapenr = act_key_nr; + } + + for (i = 0, block = psys->key->block.first; block; block = block->next, i++) { + CustomData_add_layer_named(&bm->vdata, CD_SHAPEKEY, + CD_ASSIGN, NULL, 0, block->name); + + j = CustomData_get_layer_index_n(&bm->vdata, CD_SHAPEKEY, i); + bm->vdata.layers[j].uid = block->uid; + } + + return actkey; +} +#endif + +/* create vertex and edge data for BMesh based on particle hair keys */ +static void bm_make_particles(BMesh *bm, Object *ob, ParticleSystem *psys, struct DerivedMesh *emitter_dm, float (*keyco)[3], int cd_shape_keyindex_offset) +{ +// KeyBlock *block; + ParticleData *pa; + HairKey *hkey; + int p, k; + + int vindex, eindex; + BMVert *v = NULL, *v_prev; + BMEdge *e; + + float hairmat[4][4]; + + /* XXX currently all particles and keys have the same mass, this may change */ + float mass = psys->part->mass; + + vindex = 0; + eindex = 0; + for (p = 0, pa = psys->particles; p < psys->totpart; ++p, ++pa) { + + /* hair keys are in a local "hair space", but edit data should be in object space */ + psys_mat_hair_to_object(ob, emitter_dm, psys->part->from, pa, hairmat); + + for (k = 0, hkey = pa->hair; k < pa->totkey; ++k, ++hkey) { + float co[3]; + + copy_v3_v3(co, keyco ? keyco[vindex] : hkey->co); + mul_m4_v3(hairmat, co); + + v_prev = v; + v = BM_vert_create(bm, co, NULL, BM_CREATE_SKIP_CD); + BM_elem_index_set(v, vindex); /* set_ok */ + + /* transfer flag */ +// v->head.hflag = BM_vert_flag_from_mflag(mvert->flag & ~SELECT); + + /* this is necessary for selection counts to work properly */ +// if (hkey->editflag & SELECT) { +// BM_vert_select_set(bm, v, true); +// } + +// normal_short_to_float_v3(v->no, mvert->no); + + /* Copy Custom Data */ +// CustomData_to_bmesh_block(&me->vdata, &bm->vdata, vindex, &v->head.data, true); + CustomData_bmesh_set_default(&bm->vdata, &v->head.data); + + BM_elem_float_data_named_set(&bm->vdata, v, CD_PROP_FLT, CD_HAIR_MASS, mass); + BM_elem_float_data_named_set(&bm->vdata, v, CD_PROP_FLT, CD_HAIR_WEIGHT, hkey->weight); + + /* root */ + if (k == 0) { + MSurfaceSample root_loc; + if (BKE_mesh_sample_from_particle(&root_loc, psys, emitter_dm, pa)) { + BM_elem_meshsample_data_named_set(&bm->vdata, v, CD_MSURFACE_SAMPLE, CD_HAIR_ROOT_LOCATION, &root_loc); + } + } + +#if 0 + /* set shapekey data */ + if (psys->key) { + /* set shape key original index */ + if (cd_shape_keyindex_offset != -1) BM_ELEM_CD_SET_INT(v, cd_shape_keyindex_offset, vindex); + + for (block = psys->key->block.first, j = 0; block; block = block->next, j++) { + float *co = CustomData_bmesh_get_n(&bm->vdata, v->head.data, CD_SHAPEKEY, j); + + if (co) { + copy_v3_v3(co, ((float *)block->data) + 3 * vindex); + } + } + } +#else + (void)cd_shape_keyindex_offset; +#endif + + vindex += 1; + + if (k > 0) { + e = BM_edge_create(bm, v_prev, v, NULL, BM_CREATE_SKIP_CD); + BM_elem_index_set(e, eindex); /* set_ok; one less edge than vertices for each particle */ + + /* transfer flags */ +// e->head.hflag = BM_edge_flag_from_mflag(medge->flag & ~SELECT); + + /* this is necessary for selection counts to work properly */ +// if (medge->flag & SELECT) { +// BM_edge_select_set(bm, e, true); +// } + + /* Copy Custom Data */ +// CustomData_to_bmesh_block(&me->edata, &bm->edata, eindex, &e->head.data, true); + CustomData_bmesh_set_default(&bm->edata, &e->head.data); + + eindex += 1; + } + + } /* hair keys */ + + } /* particles */ + + bm->elem_index_dirty &= ~(BM_VERT | BM_EDGE); /* added in order, clear dirty flag */ +} + +/** + * \brief ParticleSystem -> BMesh + */ +void BM_strands_bm_from_psys(BMesh *bm, Object *ob, ParticleSystem *psys, struct DerivedMesh *emitter_dm, + const bool set_key, int act_key_nr) +{ + // KeyBlock *actkey; + float (*keyco)[3] = NULL; + int totvert, totedge; + + int cd_shape_keyindex_offset; + + /* free custom data */ + /* this isnt needed in most cases but do just incase */ + CustomData_free(&bm->vdata, bm->totvert); + CustomData_free(&bm->edata, bm->totedge); + CustomData_free(&bm->ldata, bm->totloop); + CustomData_free(&bm->pdata, bm->totface); + + totvert = BM_strands_count_psys_keys(psys); + totedge = totvert - psys->totpart; + + if (!psys || !totvert || !totedge) { + if (psys) { /*no verts? still copy customdata layout*/ + CustomData_bmesh_init_pool(&bm->vdata, totvert, BM_VERT); + CustomData_bmesh_init_pool(&bm->edata, totedge, BM_EDGE); + CustomData_bmesh_init_pool(&bm->ldata, 0, BM_LOOP); + CustomData_bmesh_init_pool(&bm->pdata, 0, BM_FACE); + } + return; /* sanity check */ + } + +#if 0 + actkey = bm_set_shapekey_from_psys(bm, psys, totvert, act_key_nr); + if (actkey) + keyco = actkey->data; +#else + (void)act_key_nr; +#endif + + CustomData_bmesh_init_pool(&bm->vdata, totvert, BM_VERT); + CustomData_bmesh_init_pool(&bm->edata, totedge, BM_EDGE); + + BM_strands_cd_flag_apply(bm, /*psys->cd_flag*/0); + + cd_shape_keyindex_offset = /*psys->key ? CustomData_get_offset(&bm->vdata, CD_SHAPE_KEYINDEX) :*/ -1; + + bm_make_particles(bm, ob, psys, emitter_dm, set_key ? keyco : NULL, cd_shape_keyindex_offset); + + +#if 0 /* TODO */ + if (me->mselect && me->totselect != 0) { + + BMVert **vert_array = MEM_mallocN(sizeof(BMVert *) * bm->totvert, "VSelConv"); + BMEdge **edge_array = MEM_mallocN(sizeof(BMEdge *) * bm->totedge, "ESelConv"); + BMFace **face_array = MEM_mallocN(sizeof(BMFace *) * bm->totface, "FSelConv"); + MSelect *msel; + +#pragma omp parallel sections if (bm->totvert + bm->totedge + bm->totface >= BM_OMP_LIMIT) + { +#pragma omp section + { BM_iter_as_array(bm, BM_VERTS_OF_MESH, NULL, (void **)vert_array, bm->totvert); } +#pragma omp section + { BM_iter_as_array(bm, BM_EDGES_OF_MESH, NULL, (void **)edge_array, bm->totedge); } +#pragma omp section + { BM_iter_as_array(bm, BM_FACES_OF_MESH, NULL, (void **)face_array, bm->totface); } + } + + for (i = 0, msel = me->mselect; i < me->totselect; i++, msel++) { + switch (msel->type) { + case ME_VSEL: + BM_select_history_store(bm, (BMElem *)vert_array[msel->index]); + break; + case ME_ESEL: + BM_select_history_store(bm, (BMElem *)edge_array[msel->index]); + break; + case ME_FSEL: + BM_select_history_store(bm, (BMElem *)face_array[msel->index]); + break; + } + } + + MEM_freeN(vert_array); + MEM_freeN(edge_array); + MEM_freeN(face_array); + } + else { + me->totselect = 0; + if (me->mselect) { + MEM_freeN(me->mselect); + me->mselect = NULL; + } + } +#endif +} + +#if 0 +/** + * \brief BMesh -> Mesh + */ +static BMVert **bm_to_mesh_vertex_map(BMesh *bm, int ototvert) +{ + const int cd_shape_keyindex_offset = CustomData_get_offset(&bm->vdata, CD_SHAPE_KEYINDEX); + BMVert **vertMap = NULL; + BMVert *eve; + int i = 0; + BMIter iter; + + /* caller needs to ensure this */ + BLI_assert(ototvert > 0); + + vertMap = MEM_callocN(sizeof(*vertMap) * ototvert, "vertMap"); + if (cd_shape_keyindex_offset != -1) { + BM_ITER_MESH_INDEX (eve, &iter, bm, BM_VERTS_OF_MESH, i) { + const int keyi = BM_ELEM_CD_GET_INT(eve, cd_shape_keyindex_offset); + if ((keyi != ORIGINDEX_NONE) && (keyi < ototvert)) { + vertMap[keyi] = eve; + } + } + } + else { + BM_ITER_MESH_INDEX (eve, &iter, bm, BM_VERTS_OF_MESH, i) { + if (i < ototvert) { + vertMap[i] = eve; + } + else { + break; + } + } + } + + return vertMap; +} + +/** + * returns customdata shapekey index from a keyblock or -1 + * \note could split this out into a more generic function */ +static int bm_to_mesh_shape_layer_index_from_kb(BMesh *bm, KeyBlock *currkey) +{ + int i; + int j = 0; + + for (i = 0; i < bm->vdata.totlayer; i++) { + if (bm->vdata.layers[i].type == CD_SHAPEKEY) { + if (currkey->uid == bm->vdata.layers[i].uid) { + return j; + } + j++; + } + } + return -1; +} + +BLI_INLINE void bmesh_quick_edgedraw_flag(MEdge *med, BMEdge *e) +{ + /* this is a cheap way to set the edge draw, its not precise and will + * pick the first 2 faces an edge uses. + * The dot comparison is a little arbitrary, but set so that a 5 subd + * IcoSphere won't vanish but subd 6 will (as with pre-bmesh blender) */ + + + if ( /* (med->flag & ME_EDGEDRAW) && */ /* assume to be true */ + (e->l && (e->l != e->l->radial_next)) && + (dot_v3v3(e->l->f->no, e->l->radial_next->f->no) > 0.9995f)) + { + med->flag &= ~ME_EDGEDRAW; + } + else { + med->flag |= ME_EDGEDRAW; + } +} +#endif + +static void make_particle_hair(BMesh *bm, BMVert *root, Object *ob, ParticleSystem *psys, struct DerivedMesh *emitter_dm, struct BVHTreeFromMesh *emitter_bvhtree, struct ParticleData *pa) +{ + int totkey = BM_strands_keys_count(root); + HairKey *hair; + + BMVert *v; + BMIter iter; + HairKey *hkey; + int k; + + float inv_hairmat[4][4]; + + pa->alive = PARS_ALIVE; + pa->flag = 0; + + pa->time = 0.0f; + pa->lifetime = 100.0f; + pa->dietime = 100.0f; + + pa->size = psys->part->size; + + // TODO define other particle stuff ... + + hair = MEM_callocN(totkey * sizeof(HairKey), "hair keys"); + + hkey = hair; + k = 0; + BM_ITER_STRANDS_ELEM(v, &iter, root, BM_VERTS_OF_STRAND) { + /* root */ + if (k == 0) { + MSurfaceSample root_loc; + BM_elem_meshsample_data_named_get(&bm->vdata, v, CD_MSURFACE_SAMPLE, CD_HAIR_ROOT_LOCATION, &root_loc); + if (!BKE_mesh_sample_to_particle(&root_loc, psys, emitter_dm, emitter_bvhtree, pa)) { + pa->num = 0; + pa->num_dmcache = DMCACHE_NOTFOUND; + zero_v4(pa->fuv); + pa->foffset = 0.0f; + } + + /* edit data is in object space, hair keys must be converted back into "hair space" */ + psys_mat_hair_to_object(ob, emitter_dm, psys->part->from, pa, inv_hairmat); + invert_m4(inv_hairmat); + } + + mul_v3_m4v3(hkey->co, inv_hairmat, v->co); + mul_v3_m4v3(hkey->world_co, ob->obmat, v->co); + + hkey->time = totkey > 0 ? (float)k / (float)(totkey - 1) : 0.0f; + if (k == 0) { + /* weight 1.0 is used for pinning hair roots in particles */ + hkey->weight = 1.0f; + } + else { + hkey->weight = BM_elem_float_data_named_get(&bm->vdata, v, CD_PROP_FLT, CD_HAIR_WEIGHT); + } + + ++hkey; + ++k; + + BM_CHECK_ELEMENT(v); + } + + if (pa->hair) + MEM_freeN(pa->hair); + + pa->hair = hair; + pa->totkey = totkey; +} + +void BM_strands_bm_to_psys(BMesh *bm, Object *ob, ParticleSystem *psys, struct DerivedMesh *emitter_dm, struct BVHTreeFromMesh *emitter_bvhtree) +{ + ParticleData *particles, *oldparticles; + int ototpart, ntotpart; + + BMVert *root; + BMIter iter; + ParticleData *pa; + int p; + + ototpart = psys->totpart; + + ntotpart = BM_strands_count(bm); + + /* new particles block */ + if (bm->totvert == 0) particles = NULL; + else particles = MEM_callocN(ntotpart * sizeof(ParticleData), "particles"); + + /* lets save the old particles just in case we are actually working on + * a key ... we now do processing of the keys at the end */ + oldparticles = psys->particles; + + psys->totpart = ntotpart; + +// psys->cd_flag = BM_strands_cd_flag_from_bmesh(bm); + + pa = particles; + p = 0; + BM_ITER_STRANDS(root, &iter, bm, BM_STRANDS_OF_MESH) { + + make_particle_hair(bm, root, ob, psys, emitter_dm, emitter_bvhtree, pa); + + ++pa; + ++p; + } + bm->elem_index_dirty &= ~BM_VERT; + + +#if 0 // TODO + { + BMEditSelection *selected; + me->totselect = BLI_listbase_count(&(bm->selected)); + + if (me->mselect) MEM_freeN(me->mselect); + + me->mselect = MEM_callocN(sizeof(MSelect) * me->totselect, "Mesh selection history"); + + + for (i = 0, selected = bm->selected.first; selected; i++, selected = selected->next) { + if (selected->htype == BM_VERT) { + me->mselect[i].type = ME_VSEL; + + } + else if (selected->htype == BM_EDGE) { + me->mselect[i].type = ME_ESEL; + + } + else if (selected->htype == BM_FACE) { + me->mselect[i].type = ME_FSEL; + } + + me->mselect[i].index = BM_elem_index_get(selected->ele); + } + } +#endif + +#if 0 // TODO + /* see comment below, this logic is in twice */ + + if (me->key) { + const int cd_shape_keyindex_offset = CustomData_get_offset(&bm->vdata, CD_SHAPE_KEYINDEX); + + KeyBlock *currkey; + KeyBlock *actkey = BLI_findlink(&me->key->block, bm->shapenr - 1); + + float (*ofs)[3] = NULL; + + /* go through and find any shapekey customdata layers + * that might not have corresponding KeyBlocks, and add them if + * necessary */ + j = 0; + for (i = 0; i < bm->vdata.totlayer; i++) { + if (bm->vdata.layers[i].type != CD_SHAPEKEY) + continue; + + for (currkey = me->key->block.first; currkey; currkey = currkey->next) { + if (currkey->uid == bm->vdata.layers[i].uid) + break; + } + + if (!currkey) { + currkey = BKE_keyblock_add(me->key, bm->vdata.layers[i].name); + currkey->uid = bm->vdata.layers[i].uid; + } + + j++; + } + + + /* editing the base key should update others */ + if ((me->key->type == KEY_RELATIVE) && /* only need offsets for relative shape keys */ + (actkey != NULL) && /* unlikely, but the active key may not be valid if the + * bmesh and the mesh are out of sync */ + (oldverts != NULL)) /* not used here, but 'oldverts' is used later for applying 'ofs' */ + { + const bool act_is_basis = BKE_keyblock_is_basis(me->key, bm->shapenr - 1); + + /* active key is a base */ + if (act_is_basis && (cd_shape_keyindex_offset != -1)) { + float (*fp)[3] = actkey->data; + + ofs = MEM_callocN(sizeof(float) * 3 * bm->totvert, "currkey->data"); + mvert = me->mvert; + BM_ITER_MESH_INDEX (eve, &iter, bm, BM_VERTS_OF_MESH, i) { + const int keyi = BM_ELEM_CD_GET_INT(eve, cd_shape_keyindex_offset); + + if (keyi != ORIGINDEX_NONE) { + sub_v3_v3v3(ofs[i], mvert->co, fp[keyi]); + } + else { + /* if there are new vertices in the mesh, we can't propagate the offset + * because it will only work for the existing vertices and not the new + * ones, creating a mess when doing e.g. subdivide + translate */ + MEM_freeN(ofs); + ofs = NULL; + break; + } + + mvert++; + } + } + } + + for (currkey = me->key->block.first; currkey; currkey = currkey->next) { + const bool apply_offset = (ofs && (currkey != actkey) && (bm->shapenr - 1 == currkey->relative)); + int cd_shape_offset; + int keyi; + float (*ofs_pt)[3] = ofs; + float *newkey, (*oldkey)[3], *fp; + + j = bm_to_mesh_shape_layer_index_from_kb(bm, currkey); + cd_shape_offset = CustomData_get_n_offset(&bm->vdata, CD_SHAPEKEY, j); + + + fp = newkey = MEM_callocN(me->key->elemsize * bm->totvert, "currkey->data"); + oldkey = currkey->data; + + mvert = me->mvert; + BM_ITER_MESH (eve, &iter, bm, BM_VERTS_OF_MESH) { + + if (currkey == actkey) { + copy_v3_v3(fp, eve->co); + + if (actkey != me->key->refkey) { /* important see bug [#30771] */ + if (cd_shape_keyindex_offset != -1) { + if (oldverts) { + keyi = BM_ELEM_CD_GET_INT(eve, cd_shape_keyindex_offset); + if (keyi != ORIGINDEX_NONE && keyi < currkey->totelem) { /* valid old vertex */ + copy_v3_v3(mvert->co, oldverts[keyi].co); + } + } + } + } + } + else if (j != -1) { + /* in most cases this runs */ + copy_v3_v3(fp, BM_ELEM_CD_GET_VOID_P(eve, cd_shape_offset)); + } + else if ((oldkey != NULL) && + (cd_shape_keyindex_offset != -1) && + ((keyi = BM_ELEM_CD_GET_INT(eve, cd_shape_keyindex_offset)) != ORIGINDEX_NONE) && + (keyi < currkey->totelem)) + { + /* old method of reconstructing keys via vertice's original key indices, + * currently used if the new method above fails (which is theoretically + * possible in certain cases of undo) */ + copy_v3_v3(fp, oldkey[keyi]); + } + else { + /* fail! fill in with dummy value */ + copy_v3_v3(fp, mvert->co); + } + + /* propagate edited basis offsets to other shapes */ + if (apply_offset) { + add_v3_v3(fp, *ofs_pt++); + } + + fp += 3; + mvert++; + } + + currkey->totelem = bm->totvert; + if (currkey->data) { + MEM_freeN(currkey->data); + } + currkey->data = newkey; + } + + if (ofs) MEM_freeN(ofs); + } +#else + psys->particles = particles; +#endif + + if (oldparticles) { + ParticleData *pa; + int p; + for (p = 0, pa = oldparticles; p < ototpart; ++p, ++pa) + if (pa->hair) + MEM_freeN(pa->hair); + MEM_freeN(oldparticles); + } +} diff --git a/source/blender/bmesh/intern/bmesh_strands_conv.h b/source/blender/bmesh/intern/bmesh_strands_conv.h new file mode 100644 index 00000000000..aa18b779b17 --- /dev/null +++ b/source/blender/bmesh/intern/bmesh_strands_conv.h @@ -0,0 +1,67 @@ +/* + * ***** 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2014 Blender Foundation. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): Lukas Toenne. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#ifndef __BMESH_STRANDS_CONV_H__ +#define __BMESH_STRANDS_CONV_H__ + +/** \file blender/bmesh/intern/bmesh_strands_conv.h + * \ingroup bmesh + */ + +struct BMesh; +struct Mesh; +struct Object; +struct ParticleSystem; +struct DerivedMesh; +struct BVHTreeFromMesh; +struct Strands; +struct Key; + +extern const char *CD_HAIR_SEGMENT_LENGTH; +extern const char *CD_HAIR_MASS; +extern const char *CD_HAIR_WEIGHT; +extern const char *CD_HAIR_ROOT_LOCATION; + +void BM_strands_cd_validate(struct BMesh *bm); +void BM_strands_cd_flag_ensure(struct BMesh *bm, const char cd_flag); +void BM_strands_cd_flag_apply(struct BMesh *bm, const char cd_flag); +char BM_strands_cd_flag_from_bmesh(struct BMesh *bm); + +void BM_strands_bm_from_strands(struct BMesh *bm, struct Strands *strands, float mat[4][4], struct Key *key, struct DerivedMesh *emitter_dm, const bool set_key, int act_key_nr); +struct Strands *BM_strands_bm_to_strands(struct BMesh *bm, struct Strands *strands, float mat[4][4], struct Key *key, struct DerivedMesh *emitter_dm, struct BVHTreeFromMesh *emitter_bvhtree); + +int BM_strands_count_psys_keys(struct ParticleSystem *psys); +void BM_strands_bm_from_psys(struct BMesh *bm, struct Object *ob, struct ParticleSystem *psys, struct DerivedMesh *emitter_dm, + const bool set_key, int act_key_nr); +void BM_strands_bm_to_psys(struct BMesh *bm, struct Object *ob, struct ParticleSystem *psys, struct DerivedMesh *emitter_dm, struct BVHTreeFromMesh *emitter_bvhtree); + +#define BMALLOC_TEMPLATE_FROM_STRANDS(strands) { (CHECK_TYPE_INLINE(strands, Strands *), \ + strands->totverts), (strands->totverts - strands->totcurves), 0, 0 } +#define BMALLOC_TEMPLATE_FROM_PSYS(psys) { (CHECK_TYPE_INLINE(psys, ParticleSystem *), \ + BM_strands_count_psys_keys(psys)), (BM_strands_count_psys_keys(psys) - (psys)->totpart), 0, 0 } + +#endif /* __BMESH_STRANDS_CONV_H__ */ diff --git a/source/blender/depsgraph/intern/depsgraph_build_relations.cc b/source/blender/depsgraph/intern/depsgraph_build_relations.cc index b59d845f60c..f51f51987d7 100644 --- a/source/blender/depsgraph/intern/depsgraph_build_relations.cc +++ b/source/blender/depsgraph/intern/depsgraph_build_relations.cc @@ -1064,7 +1064,7 @@ void DepsgraphRelationBuilder::build_particles(Scene *scene, Object *ob) #endif /* effectors */ - ListBase *effectors = pdInitEffectors(scene, ob, psys, part->effector_weights, false); + ListBase *effectors = pdInitEffectors_ex(scene, ob, psys, ob->lay, part->effector_weights, false); if (effectors) { for (EffectorCache *eff = (EffectorCache *)effectors->first; eff; eff = eff->next) { diff --git a/source/blender/editors/CMakeLists.txt b/source/blender/editors/CMakeLists.txt index 084006ce277..ec9ae775ad0 100644 --- a/source/blender/editors/CMakeLists.txt +++ b/source/blender/editors/CMakeLists.txt @@ -23,6 +23,7 @@ if(WITH_BLENDER) add_subdirectory(armature) add_subdirectory(curve) add_subdirectory(gpencil) + add_subdirectory(hair) add_subdirectory(interface) add_subdirectory(io) add_subdirectory(mask) diff --git a/source/blender/editors/SConscript b/source/blender/editors/SConscript index 1ea2bc0e4ef..0ef04739e9f 100644 --- a/source/blender/editors/SConscript +++ b/source/blender/editors/SConscript @@ -41,6 +41,7 @@ SConscript(['datafiles/SConscript', 'object/SConscript', 'curve/SConscript', 'gpencil/SConscript', + 'hair/SConscript', 'physics/SConscript', 'render/SConscript', 'sound/SConscript', diff --git a/source/blender/editors/animation/anim_channels_defines.c b/source/blender/editors/animation/anim_channels_defines.c index ebd05d8b16b..10af5b84509 100644 --- a/source/blender/editors/animation/anim_channels_defines.c +++ b/source/blender/editors/animation/anim_channels_defines.c @@ -865,7 +865,11 @@ static int acf_group_setting_flag(bAnimContext *ac, eAnimChannel_Settings settin case ACHANNEL_SETTING_MUTE: /* muted */ return AGRP_MUTED; - + + case ACHANNEL_SETTING_MOD_OFF: /* muted */ + *neg = 1; + return AGRP_MODIFIERS_OFF; + case ACHANNEL_SETTING_PROTECT: /* protected */ return AGRP_PROTECTED; @@ -983,6 +987,10 @@ static int acf_fcurve_setting_flag(bAnimContext *UNUSED(ac), eAnimChannel_Settin case ACHANNEL_SETTING_VISIBLE: /* visibility - graph editor */ return FCURVE_VISIBLE; + case ACHANNEL_SETTING_MOD_OFF: + *neg = 1; + return FCURVE_MOD_OFF; + default: /* unsupported */ return 0; } @@ -3977,6 +3985,7 @@ static void draw_setting_widget(bAnimContext *ac, bAnimListElem *ale, const bAni { short ptrsize, butType; bool negflag; + bool usetoggle = true; int flag, icon; void *ptr; const char *tooltip; @@ -3998,7 +4007,13 @@ static void draw_setting_widget(bAnimContext *ac, bAnimListElem *ale, const bAni else tooltip = TIP_("Channels are visible in Graph Editor for editing"); break; - + + case ACHANNEL_SETTING_MOD_OFF: /* modifiers disabled */ + icon = ICON_MODIFIER; + usetoggle = false; + tooltip = TIP_("F-Curve modifiers are disabled"); + break; + case ACHANNEL_SETTING_EXPAND: /* expanded triangle */ //icon = ((enabled) ? ICON_TRIA_DOWN : ICON_TRIA_RIGHT); icon = ICON_TRIA_RIGHT; @@ -4059,11 +4074,18 @@ static void draw_setting_widget(bAnimContext *ac, bAnimListElem *ale, const bAni } /* type of button */ - if (negflag) - butType = UI_BTYPE_ICON_TOGGLE_N; - else - butType = UI_BTYPE_ICON_TOGGLE; - + if (usetoggle) { + if (negflag) + butType = UI_BTYPE_ICON_TOGGLE_N; + else + butType = UI_BTYPE_ICON_TOGGLE; + } + else { + if (negflag) + butType = UI_BTYPE_TOGGLE_N; + else + butType = UI_BTYPE_TOGGLE; + } /* draw button for setting */ if (ptr && flag) { switch (ptrsize) { @@ -4091,6 +4113,7 @@ static void draw_setting_widget(bAnimContext *ac, bAnimListElem *ale, const bAni case ACHANNEL_SETTING_PROTECT: /* General - protection flags */ case ACHANNEL_SETTING_MUTE: /* General - muting flags */ case ACHANNEL_SETTING_PINNED: /* NLA Actions - 'map/nomap' */ + case ACHANNEL_SETTING_MOD_OFF: UI_but_funcN_set(but, achannel_setting_flush_widget_cb, MEM_dupallocN(ale), SET_INT_IN_POINTER(setting)); break; @@ -4258,7 +4281,13 @@ void ANIM_channel_draw_widgets(const bContext *C, bAnimContext *ac, bAnimListEle offset += ICON_WIDTH; draw_setting_widget(ac, ale, acf, block, (int)v2d->cur.xmax - offset, ymid, ACHANNEL_SETTING_MUTE); } - + + /* modifiers disable */ + if (acf->has_setting(ac, ale, ACHANNEL_SETTING_MOD_OFF)) { + offset += ICON_WIDTH; + draw_setting_widget(ac, ale, acf, block, (int)v2d->cur.xmax - offset, ymid, ACHANNEL_SETTING_MOD_OFF); + } + /* ----------- */ /* pinned... */ diff --git a/source/blender/editors/animation/anim_filter.c b/source/blender/editors/animation/anim_filter.c index a396b122f69..7739848e0d4 100644 --- a/source/blender/editors/animation/anim_filter.c +++ b/source/blender/editors/animation/anim_filter.c @@ -90,6 +90,7 @@ #include "BKE_modifier.h" #include "BKE_node.h" #include "BKE_mask.h" +#include "BKE_particle.h" #include "BKE_sequencer.h" #include "ED_anim_api.h" @@ -2377,6 +2378,7 @@ static size_t animdata_filter_dopesheet_ob(bAnimContext *ac, ListBase *anim_data BEGIN_ANIMFILTER_SUBCHANNELS(EXPANDED_OBJC(ob)) { Key *key = BKE_key_from_object(ob); + ParticleSystem *psys; /* object-level animation */ if ((ob->adt) && !(ads->filterflag & ADS_FILTER_NOOBJ)) { @@ -2387,6 +2389,11 @@ static size_t animdata_filter_dopesheet_ob(bAnimContext *ac, ListBase *anim_data if ((key && key->adt) && !(ads->filterflag & ADS_FILTER_NOSHAPEKEYS)) { tmp_items += animdata_filter_ds_keyanim(ac, &tmp_data, ads, ob, key, filter_mode); } + for (psys = ob->particlesystem.first; psys; psys = psys->next) { + if ((psys->key && psys->key->adt) && !(ads->filterflag & ADS_FILTER_NOSHAPEKEYS)) { + tmp_items += animdata_filter_ds_keyanim(ac, &tmp_data, ads, ob, psys->key, filter_mode); + } + } /* modifiers */ if ((ob->modifiers.first) && !(ads->filterflag & ADS_FILTER_NOMODIFIERS)) { diff --git a/source/blender/editors/animation/anim_ops.c b/source/blender/editors/animation/anim_ops.c index 2de42933dc0..86b368fe0d2 100644 --- a/source/blender/editors/animation/anim_ops.c +++ b/source/blender/editors/animation/anim_ops.c @@ -60,6 +60,8 @@ #include "anim_intern.h" +#include "MEM_guardedalloc.h" + /* ********************** frame change operator ***************************/ /* Check if the operator can be run from the current context */ @@ -90,10 +92,11 @@ static int change_frame_poll(bContext *C) } /* Set the new frame number */ -static void change_frame_apply(bContext *C, wmOperator *op) +static void change_frame_apply(bContext *C, wmOperator *op, bool final) { Main *bmain = CTX_data_main(C); Scene *scene = CTX_data_scene(C); + ARegion *ar_op = CTX_wm_region(C); int frame = RNA_int_get(op->ptr, "frame"); bool do_snap = RNA_boolean_get(op->ptr, "snap"); @@ -107,8 +110,44 @@ static void change_frame_apply(bContext *C, wmOperator *op) SUBFRA = 0.0f; /* do updates */ - BKE_sound_seek_scene(bmain, scene); - WM_event_add_notifier(C, NC_SCENE | ND_FRAME, scene); + + if (final) { + BKE_sound_seek_scene(bmain, scene); + WM_event_add_notifier(C, NC_SCENE | ND_FRAME, scene); + } + else + { + bScreen *screen = CTX_wm_screen(C); + wmWindowManager *wm = CTX_wm_manager(C); + wmWindow *window; + ScrArea *sa; + + BKE_sound_seek_scene(bmain, scene); + + ED_update_for_newframe(bmain, scene, 1); + + for (window = wm->windows.first; window; window = window->next) { + for (sa = window->screen->areabase.first; sa; sa = sa->next) { + ARegion *ar; + for (ar = sa->regionbase.first; ar; ar = ar->next) { + bool redraw = false; + if (ar == ar_op) { + redraw = true; + } + else if (ED_match_region_with_redraws(sa->spacetype, ar->regiontype, screen->redraws_flag)) { + redraw = true; + } + + if (redraw) { + ED_region_tag_redraw(ar); + } + } + + if (ED_match_area_with_refresh(sa->spacetype, SPACE_TIME)) + ED_area_tag_refresh(sa); + } + } + } } /* ---- */ @@ -116,8 +155,7 @@ static void change_frame_apply(bContext *C, wmOperator *op) /* Non-modal callback for running operator without user input */ static int change_frame_exec(bContext *C, wmOperator *op) { - change_frame_apply(C, op); - + change_frame_apply(C, op, true); return OPERATOR_FINISHED; } @@ -154,13 +192,35 @@ static void change_frame_seq_preview_begin(bContext *C, const wmEvent *event) } } } -static void change_frame_seq_preview_end(bContext *C) + +typedef struct ChangeFrameData { + wmTimer *timer; + double last_duration; + int frame; + int last_frame; +} ChangeFrameData; + +static void change_frame_seq_preview_end(bContext *C, wmOperator *op) { + wmWindowManager *wm = CTX_wm_manager(C); + wmWindow *win = CTX_wm_window(C); + if (ED_sequencer_special_preview_get() != NULL) { Scene *scene = CTX_data_scene(C); ED_sequencer_special_preview_clear(); WM_event_add_notifier(C, NC_SCENE | ND_FRAME, scene); } + + if (op->customdata) { + ChangeFrameData *data = op->customdata; + WM_event_remove_timer(wm, win, data->timer); + MEM_freeN(data); + op->customdata = NULL; + /* add here too to take care of cancelling */ + if (win->screen) + win->screen->scrubbing = false; + } + } /* Modal Operator init */ @@ -170,11 +230,22 @@ static int change_frame_invoke(bContext *C, wmOperator *op, const wmEvent *event * as user could click on a single frame (jump to frame) as well as * click-dragging over a range (modal scrubbing). */ + wmWindowManager *wm = CTX_wm_manager(C); + wmWindow *win = CTX_wm_window(C); + Scene *scene = CTX_data_scene(C); + ChangeFrameData *data = MEM_callocN(sizeof(ChangeFrameData), "changeframedata"); + + data->timer = WM_event_add_timer(wm, win, TIMER, FRA2TIME(1.0)); + RNA_int_set(op->ptr, "frame", frame_from_event(C, event)); + op->customdata = data; change_frame_seq_preview_begin(C, event); - change_frame_apply(C, op); + if (win->screen) + win->screen->scrubbing = true; + + change_frame_apply(C, op, false); /* add temp handler */ WM_event_add_modal_handler(C, op); @@ -182,24 +253,43 @@ static int change_frame_invoke(bContext *C, wmOperator *op, const wmEvent *event return OPERATOR_RUNNING_MODAL; } -static void change_frame_cancel(bContext *C, wmOperator *UNUSED(op)) +static void change_frame_cancel(bContext *C, wmOperator *op) { - change_frame_seq_preview_end(C); + change_frame_seq_preview_end(C, op); } /* Modal event handling of frame changing */ static int change_frame_modal(bContext *C, wmOperator *op, const wmEvent *event) { + Main *bmain = CTX_data_main(C); + Scene *scene = CTX_data_scene(C); + ChangeFrameData *data = op->customdata; + wmWindow *win = CTX_wm_window(C); + int ret = OPERATOR_RUNNING_MODAL; /* execute the events */ switch (event->type) { case ESCKEY: + WM_event_add_notifier(C, NC_SCENE | ND_FRAME, scene); + if (win->screen) + win->screen->scrubbing = false; + BKE_sound_seek_scene(bmain, scene); ret = OPERATOR_FINISHED; break; case MOUSEMOVE: - RNA_int_set(op->ptr, "frame", frame_from_event(C, event)); - change_frame_apply(C, op); + data->frame = frame_from_event(C, event); + break; + + case TIMER: + if (data->timer->duration - data->last_duration > FRA2TIME(1)) { + if (data->frame != data->last_frame) { + RNA_int_set(op->ptr, "frame", data->frame); + change_frame_apply(C, op, false); + data->last_frame = data->frame; + data->last_duration = data->timer->duration; + } + } break; case LEFTMOUSE: @@ -208,8 +298,14 @@ static int change_frame_modal(bContext *C, wmOperator *op, const wmEvent *event) /* we check for either mouse-button to end, as checking for ACTIONMOUSE (which is used to init * the modal op) doesn't work for some reason */ - if (event->val == KM_RELEASE) + if (event->val == KM_RELEASE) { + if (win->screen) + win->screen->scrubbing = false; + data->frame = frame_from_event(C, event); + RNA_int_set(op->ptr, "frame", data->frame); + change_frame_apply(C, op, true); ret = OPERATOR_FINISHED; + } break; case LEFTCTRLKEY: @@ -224,7 +320,7 @@ static int change_frame_modal(bContext *C, wmOperator *op, const wmEvent *event) } if (ret != OPERATOR_RUNNING_MODAL) { - change_frame_seq_preview_end(C); + change_frame_seq_preview_end(C, op); } return ret; @@ -247,7 +343,7 @@ static void ANIM_OT_change_frame(wmOperatorType *ot) ot->poll = change_frame_poll; /* flags */ - ot->flag = OPTYPE_BLOCKING | OPTYPE_UNDO | OPTYPE_GRAB_CURSOR; + ot->flag = OPTYPE_BLOCKING | OPTYPE_GRAB_CURSOR; /* rna */ ot->prop = RNA_def_int(ot->srna, "frame", 0, MINAFRAME, MAXFRAME, "Frame", "", MINAFRAME, MAXFRAME); diff --git a/source/blender/editors/animation/keyframes_general.c b/source/blender/editors/animation/keyframes_general.c index b3dc0021f00..5d2c0077dee 100644 --- a/source/blender/editors/animation/keyframes_general.c +++ b/source/blender/editors/animation/keyframes_general.c @@ -179,17 +179,37 @@ void duplicate_fcurve_keys(FCurve *fcu) /* **************************************************** */ /* Various Tools */ -/* Basic F-Curve 'cleanup' function that removes 'double points' and unnecessary keyframes on linear-segments only */ -void clean_fcurve(FCurve *fcu, float thresh) +static void copy_bezt_ipo(BezTriple *bezdst, BezTriple *bezsrc) { + bezdst->back = bezsrc->back; + bezdst->ipo = bezsrc->ipo; + bezdst->easing = bezsrc->easing; + bezdst->amplitude = bezsrc->amplitude; + bezdst->period = bezsrc->period; + bezdst->h1 = bezsrc->h1; + bezdst->h2 = bezsrc->h2; + bezdst->vec[0][0] = bezsrc->vec[0][0]; + bezdst->vec[0][1] = bezsrc->vec[0][1]; + bezdst->vec[2][0] = bezsrc->vec[2][0]; + bezdst->vec[2][1] = bezsrc->vec[2][1]; +} + +/* Basic F-Curve 'cleanup' function that removes 'double points' and unnecessary keyframes on linear-segments only + * optionally clears up curve if one keyframe with default value remains */ +void clean_fcurve(struct bAnimContext *ac, bAnimListElem *ale, float thresh, bool cleardefault) +{ + FCurve *fcu = (FCurve *)ale->key_data; BezTriple *old_bezts, *bezt, *beztn; BezTriple *lastb; int totCount, i; /* check if any points */ - if ((fcu == NULL) || (fcu->bezt == NULL) || (fcu->totvert <= 1)) + if ((fcu == NULL) || (fcu->bezt == NULL) || (fcu->totvert == 0) || + (!cleardefault && fcu->totvert == 1)) + { return; - + } + /* make a copy of the old BezTriples, and clear F-Curve */ old_bezts = fcu->bezt; totCount = fcu->totvert; @@ -227,7 +247,8 @@ void clean_fcurve(FCurve *fcu, float thresh) cur[0] = bezt->vec[1][0]; cur[1] = bezt->vec[1][1]; if (!(bezt->f2 & SELECT)) { - insert_vert_fcurve(fcu, cur[0], cur[1], 0); + insert_vert_fcurve(fcu, cur[0], cur[1], INSERTKEY_FAST); + copy_bezt_ipo((fcu->bezt + (fcu->totvert - 1)), bezt); lastb = (fcu->bezt + (fcu->totvert - 1)); lastb->f1 = lastb->f2 = lastb->f3 = 0; continue; @@ -246,7 +267,8 @@ void clean_fcurve(FCurve *fcu, float thresh) if (cur[1] > next[1]) { if (IS_EQT(cur[1], prev[1], thresh) == 0) { /* add new keyframe */ - insert_vert_fcurve(fcu, cur[0], cur[1], 0); + insert_vert_fcurve(fcu, cur[0], cur[1], INSERTKEY_FAST); + copy_bezt_ipo((fcu->bezt + (fcu->totvert - 1)), bezt); } } } @@ -254,7 +276,8 @@ void clean_fcurve(FCurve *fcu, float thresh) /* only add if values are a considerable distance apart */ if (IS_EQT(cur[1], prev[1], thresh) == 0) { /* add new keyframe */ - insert_vert_fcurve(fcu, cur[0], cur[1], 0); + insert_vert_fcurve(fcu, cur[0], cur[1], INSERTKEY_FAST); + copy_bezt_ipo((fcu->bezt + (fcu->totvert - 1)), bezt); } } } @@ -264,26 +287,60 @@ void clean_fcurve(FCurve *fcu, float thresh) /* does current have same value as previous and next? */ if (IS_EQT(cur[1], prev[1], thresh) == 0) { /* add new keyframe*/ - insert_vert_fcurve(fcu, cur[0], cur[1], 0); + insert_vert_fcurve(fcu, cur[0], cur[1], INSERTKEY_FAST); + copy_bezt_ipo((fcu->bezt + (fcu->totvert - 1)), bezt); } else if (IS_EQT(cur[1], next[1], thresh) == 0) { /* add new keyframe */ - insert_vert_fcurve(fcu, cur[0], cur[1], 0); + insert_vert_fcurve(fcu, cur[0], cur[1], INSERTKEY_FAST); + copy_bezt_ipo((fcu->bezt + (fcu->totvert - 1)), bezt); } } else { /* add if value doesn't equal that of previous */ if (IS_EQT(cur[1], prev[1], thresh) == 0) { /* add new keyframe */ - insert_vert_fcurve(fcu, cur[0], cur[1], 0); + insert_vert_fcurve(fcu, cur[0], cur[1], INSERTKEY_FAST); + copy_bezt_ipo((fcu->bezt + (fcu->totvert - 1)), bezt); } } } } - + + /* skip doing this, handles should be already sorted since we are copying in forward order only */ + /* calchandles_fcurve(fcu); */ + /* now free the memory used by the old BezTriples */ if (old_bezts) MEM_freeN(old_bezts); + + /* final step, if there is just one key in fcurve, check if it's + * the default value and if is, remove fcurve completely. */ + if (cleardefault && fcu->totvert == 1) { + float default_value = 0.0f; + PointerRNA id_ptr, ptr; + PropertyRNA *prop; + RNA_id_pointer_create(ale->id, &id_ptr); + + /* get property to read from, and get value as appropriate */ + if (RNA_path_resolve_property(&id_ptr, fcu->rna_path, &ptr, &prop)) { + if (RNA_property_type(prop) == PROP_FLOAT) + default_value = RNA_property_float_get_default_index(&ptr, prop, fcu->array_index); + } + + if (fcu->bezt->vec[1][1] == default_value) { + clear_fcurve_keys(fcu); + + /* check if curve is really unused and if it is, return signal for deletion */ + if ((list_has_suitable_fmodifier(&fcu->modifiers, 0, FMI_TYPE_GENERATE_CURVE) == 0) && + (fcu->driver == NULL)) + { + AnimData *adt = ale->adt; + ANIM_fcurve_delete_from_animdata(ac, adt, fcu); + ale->key_data = NULL; + } + } + } } /* ---------------- */ diff --git a/source/blender/editors/armature/armature_utils.c b/source/blender/editors/armature/armature_utils.c index 18351950462..eebdd706ab2 100644 --- a/source/blender/editors/armature/armature_utils.c +++ b/source/blender/editors/armature/armature_utils.c @@ -785,7 +785,7 @@ static void *get_armature_edit(bContext *C) void undo_push_armature(bContext *C, const char *name) { // XXX solve getdata() - undo_editmode_push(C, name, get_armature_edit, free_undoBones, undoBones_to_editBones, editBones_to_undoBones, NULL); + undo_editmode_push(C, name, CTX_data_edit_object, get_armature_edit, free_undoBones, undoBones_to_editBones, editBones_to_undoBones, NULL); } /* *************************************************************** */ diff --git a/source/blender/editors/curve/editcurve.c b/source/blender/editors/curve/editcurve.c index 06f0745cc44..37a39f7c272 100644 --- a/source/blender/editors/curve/editcurve.c +++ b/source/blender/editors/curve/editcurve.c @@ -7088,7 +7088,7 @@ static void *get_data(bContext *C) /* and this is all the undo system needs to know */ void undo_push_curve(bContext *C, const char *name) { - undo_editmode_push(C, name, get_data, free_undoCurve, undoCurve_to_editCurve, editCurve_to_undoCurve, NULL); + undo_editmode_push(C, name, CTX_data_edit_object, get_data, free_undoCurve, undoCurve_to_editCurve, editCurve_to_undoCurve, NULL); } void ED_curve_beztcpy(EditNurb *editnurb, BezTriple *dst, BezTriple *src, int count) diff --git a/source/blender/editors/curve/editfont.c b/source/blender/editors/curve/editfont.c index dd807828d9d..a4f5c250b0f 100644 --- a/source/blender/editors/curve/editfont.c +++ b/source/blender/editors/curve/editfont.c @@ -1879,7 +1879,7 @@ static void *get_undoFont(bContext *C) /* and this is all the undo system needs to know */ void undo_push_font(bContext *C, const char *name) { - undo_editmode_push(C, name, get_undoFont, free_undoFont, undoFont_to_editFont, editFont_to_undoFont, NULL); + undo_editmode_push(C, name, CTX_data_edit_object, get_undoFont, free_undoFont, undoFont_to_editFont, editFont_to_undoFont, NULL); } /** diff --git a/source/blender/editors/datafiles/CMakeLists.txt b/source/blender/editors/datafiles/CMakeLists.txt index 2a84ca7f297..2ac800ee6e0 100644 --- a/source/blender/editors/datafiles/CMakeLists.txt +++ b/source/blender/editors/datafiles/CMakeLists.txt @@ -78,6 +78,13 @@ if(WITH_BLENDER) data_to_c_simple(../../../../release/datafiles/brushicons/fill.png SRC) data_to_c_simple(../../../../release/datafiles/brushicons/flatten.png SRC) data_to_c_simple(../../../../release/datafiles/brushicons/grab.png SRC) + data_to_c_simple(../../../../release/datafiles/brushicons/hairadd.png SRC) + data_to_c_simple(../../../../release/datafiles/brushicons/haircomb.png SRC) + data_to_c_simple(../../../../release/datafiles/brushicons/haircut.png SRC) + data_to_c_simple(../../../../release/datafiles/brushicons/hairlength.png SRC) + data_to_c_simple(../../../../release/datafiles/brushicons/hairpuff.png SRC) + data_to_c_simple(../../../../release/datafiles/brushicons/hairsmooth.png SRC) + data_to_c_simple(../../../../release/datafiles/brushicons/hairweight.png SRC) data_to_c_simple(../../../../release/datafiles/brushicons/inflate.png SRC) data_to_c_simple(../../../../release/datafiles/brushicons/layer.png SRC) data_to_c_simple(../../../../release/datafiles/brushicons/lighten.png SRC) diff --git a/source/blender/editors/datafiles/SConscript b/source/blender/editors/datafiles/SConscript index 6bc8f21e384..e16d99ef15a 100644 --- a/source/blender/editors/datafiles/SConscript +++ b/source/blender/editors/datafiles/SConscript @@ -62,6 +62,13 @@ sources.extend(( os.path.join(env['DATA_SOURCES'], "fill.png.c"), os.path.join(env['DATA_SOURCES'], "flatten.png.c"), os.path.join(env['DATA_SOURCES'], "grab.png.c"), + os.path.join(env['DATA_SOURCES'], "hairadd.png.c"), + os.path.join(env['DATA_SOURCES'], "haircomb.png.c"), + os.path.join(env['DATA_SOURCES'], "haircut.png.c"), + os.path.join(env['DATA_SOURCES'], "hairlength.png.c"), + os.path.join(env['DATA_SOURCES'], "hairpuff.png.c"), + os.path.join(env['DATA_SOURCES'], "hairsmooth.png.c"), + os.path.join(env['DATA_SOURCES'], "hairweight.png.c"), os.path.join(env['DATA_SOURCES'], "inflate.png.c"), os.path.join(env['DATA_SOURCES'], "layer.png.c"), os.path.join(env['DATA_SOURCES'], "lighten.png.c"), diff --git a/source/blender/editors/hair/CMakeLists.txt b/source/blender/editors/hair/CMakeLists.txt new file mode 100644 index 00000000000..2a72ebb9aa4 --- /dev/null +++ b/source/blender/editors/hair/CMakeLists.txt @@ -0,0 +1,56 @@ +# ***** 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# Contributor(s): Jacques Beaurain. +# +# ***** END GPL LICENSE BLOCK ***** + +set(INC + ../include + ../sculpt_paint + ../../blenfont + ../../blenkernel + ../../blenlib + ../../bmesh + ../../gpu + ../../makesdna + ../../makesrna + ../../windowmanager + ../../../../intern/guardedalloc + ../../../../intern/glew-mx +) + +set(INC_SYS + ${GLEW_INCLUDE_PATH} +) + +set(SRC + hair_cursor.c + hair_edit.c + hair_mirror.c + hair_object_cachelib.c + hair_object_particles.c + hair_ops.c + hair_select.c + hair_stroke.c + hair_undo.c + + hair_intern.h +) + +add_definitions(${GL_DEFINITIONS}) + +blender_add_lib(bf_editor_hair "${SRC}" "${INC}" "${INC_SYS}") diff --git a/source/blender/editors/hair/SConscript b/source/blender/editors/hair/SConscript new file mode 100644 index 00000000000..d34bb48218d --- /dev/null +++ b/source/blender/editors/hair/SConscript @@ -0,0 +1,54 @@ +#!/usr/bin/env python +# +# ***** 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# The Original Code is Copyright (C) 2006, Blender Foundation +# All rights reserved. +# +# The Original Code is: all of this file. +# +# Contributor(s): Nathan Letwory. +# +# ***** END GPL LICENSE BLOCK ***** + +Import ('env') + +sources = env.Glob('*.c') + +incs = [ + '#/intern/guardedalloc', + env['BF_GLEW_INC'], + '#/intern/glew-mx', + '../include', + '../sculpt_paint', + '../../blenfont', + '../../blenkernel', + '../../blenlib', + '../../bmesh', + '../../gpu', + '../../makesdna', + '../../makesrna', + '../../windowmanager', + ] +incs = ' '.join(incs) + +defs = ['BF_GL_DEFINITIONS'] + +if env['OURPLATFORM'] in ('win32-vc', 'win32-mingw', 'linuxcross', 'win64-vc', 'win64-mingw'): + incs += ' ' + env['BF_PTHREADS_INC'] + +env.BlenderLib ( 'bf_editors_hair', sources, Split(incs), defs, libtype=['core'], priority=[344] ) diff --git a/source/blender/editors/hair/hair_cursor.c b/source/blender/editors/hair/hair_cursor.c new file mode 100644 index 00000000000..dadd0cf114e --- /dev/null +++ b/source/blender/editors/hair/hair_cursor.c @@ -0,0 +1,109 @@ +/* + * ***** 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) Blender Foundation + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): Lukas Toenne + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/editors/hair/hair_cursor.c + * \ingroup edhair + */ + +#include "MEM_guardedalloc.h" + +#include "BLI_utildefines.h" +#include "BLI_math.h" + +#include "DNA_brush_types.h" +#include "DNA_scene_types.h" +#include "DNA_screen_types.h" +#include "DNA_view3d_types.h" + +#include "BKE_brush.h" +#include "BKE_context.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "BIF_gl.h" +#include "BIF_glutil.h" + +#include "ED_view3d.h" + +#include "hair_intern.h" + +static void hair_draw_cursor(bContext *C, int x, int y, void *UNUSED(customdata)) +{ + Scene *scene = CTX_data_scene(C); + UnifiedPaintSettings *ups = &scene->toolsettings->unified_paint_settings; + HairEditSettings *settings = &scene->toolsettings->hair_edit; + Brush *brush = settings->brush; + + float final_radius; + float translation[2]; + float outline_alpha, *outline_col; + + if (!brush) + return; + + final_radius = BKE_brush_size_get(scene, brush); + + /* set various defaults */ + translation[0] = x; + translation[1] = y; + outline_alpha = 0.5; + outline_col = brush->add_col; + + /* make lines pretty */ + glEnable(GL_BLEND); + glEnable(GL_LINE_SMOOTH); + + /* set brush color */ + glColor4f(outline_col[0], outline_col[1], outline_col[2], outline_alpha); + + /* draw brush outline */ + glTranslatef(translation[0], translation[1], 0); + + /* draw an inner brush */ + if (ups->stroke_active && BKE_brush_use_size_pressure(scene, brush)) { + /* inner at full alpha */ + glutil_draw_lined_arc(0.0, M_PI*2.0f, final_radius * ups->size_pressure_value, 40); + /* outer at half alpha */ + glColor4f(outline_col[0], outline_col[1], outline_col[2], outline_alpha * 0.5f); + } + glutil_draw_lined_arc(0.0, M_PI*2.0f, final_radius, 40); + glTranslatef(-translation[0], -translation[1], 0); + + /* restore GL state */ + glDisable(GL_BLEND); + glDisable(GL_LINE_SMOOTH); +} + +void hair_edit_cursor_start(bContext *C, int (*poll)(bContext *C)) +{ + Scene *scene = CTX_data_scene(C); + HairEditSettings *settings = &scene->toolsettings->hair_edit; + + if (!settings->paint_cursor) + settings->paint_cursor = WM_paint_cursor_activate(CTX_wm_manager(C), poll, hair_draw_cursor, NULL); +} diff --git a/source/blender/editors/hair/hair_edit.c b/source/blender/editors/hair/hair_edit.c new file mode 100644 index 00000000000..e4aad528f33 --- /dev/null +++ b/source/blender/editors/hair/hair_edit.c @@ -0,0 +1,459 @@ +/* + * ***** 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) Blender Foundation + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): Lukas Toenne + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/editors/hair/hair_edit.c + * \ingroup edhair + */ + +#include <stdlib.h> + +#include "MEM_guardedalloc.h" + +#include "BLI_utildefines.h" +#include "BLI_math.h" + +#include "DNA_brush_types.h" +#include "DNA_mesh_types.h" +#include "DNA_object_types.h" +#include "DNA_scene_types.h" +#include "DNA_screen_types.h" +#include "DNA_view3d_types.h" + +#include "BKE_brush.h" +#include "BKE_cdderivedmesh.h" +#include "BKE_context.h" +#include "BKE_depsgraph.h" +#include "BKE_DerivedMesh.h" +#include "BKE_editstrands.h" +#include "BKE_paint.h" + +#include "bmesh.h" + +#include "RNA_access.h" +#include "RNA_define.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "ED_object.h" +#include "ED_view3d.h" + +#include "hair_intern.h" +#include "paint_intern.h" + +int hair_edit_poll(bContext *C) +{ + Object *obact; + + obact = CTX_data_active_object(C); + if ((obact && obact->mode & OB_MODE_HAIR_EDIT) && CTX_wm_region_view3d(C)) { + return true; + } + + return false; +} + +bool hair_use_mirror_x(Object *ob) +{ + if (ob->type == OB_MESH) + return ((Mesh *)ob->data)->editflag & ME_EDIT_MIRROR_X; + else + return false; +} + +bool hair_use_mirror_topology(Object *ob) +{ + if (ob->type == OB_MESH) + return ((Mesh *)ob->data)->editflag & ME_EDIT_MIRROR_TOPO; + else + return false; +} + + +/* ==== BMesh utilities ==== */ + +void hair_bm_min_max(BMEditStrands *edit, float min[3], float max[3]) +{ + BMVert *v; + BMIter iter; + + if (edit->bm->totvert > 0) { + INIT_MINMAX(min, max); + + BM_ITER_MESH(v, &iter, edit->bm, BM_VERTS_OF_MESH) { + minmax_v3v3_v3(min, max, v->co); + } + } + else { + zero_v3(min); + zero_v3(max); + } +} + + +/* ==== edit mode toggle ==== */ + +int hair_edit_toggle_poll(bContext *C) +{ + Object *ob = CTX_data_active_object(C); + + if (ob == NULL) + return false; + if (CTX_data_edit_object(C)) + return false; + + return (ob->data && !((ID *)ob->data)->lib && ED_hair_object_has_hair_particle_data(ob)) || + ED_hair_object_has_hair_cache_data(ob); +} + +static void toggle_hair_cursor(bContext *C, bool enable) +{ + wmWindowManager *wm = CTX_wm_manager(C); + Scene *scene = CTX_data_scene(C); + HairEditSettings *settings = &scene->toolsettings->hair_edit; + + if (enable) { + hair_edit_cursor_start(C, hair_edit_toggle_poll); + } + else { + if (settings->paint_cursor) { + WM_paint_cursor_end(wm, settings->paint_cursor); + settings->paint_cursor = NULL; + } + } +} + +static int hair_edit_toggle_exec(bContext *C, wmOperator *op) +{ + Scene *scene = CTX_data_scene(C); + Object *ob = CTX_data_active_object(C); + const int mode_flag = OB_MODE_HAIR_EDIT; + const bool is_mode_set = (ob->mode & mode_flag) != 0; + + if (!is_mode_set) { + if (!ED_object_mode_compat_set(C, ob, mode_flag, op->reports)) { + return OPERATOR_CANCELLED; + } + } + + if (!is_mode_set) { + if (ED_hair_object_init_particle_edit(scene, ob)) { + /* particle edit */ + } + else if (ED_hair_object_init_cache_edit(ob)) { + /* strands cache edit */ + } + + ob->mode |= mode_flag; + + toggle_hair_cursor(C, true); + WM_event_add_notifier(C, NC_SCENE|ND_MODE|NS_MODE_HAIR, NULL); + } + else { + if (ED_hair_object_apply_particle_edit(ob)) { + /* particle edit */ + } + else if (ED_hair_object_apply_cache_edit(ob)) { + /* strands cache edit */ + } + ob->mode &= ~mode_flag; + + toggle_hair_cursor(C, false); + WM_event_add_notifier(C, NC_SCENE|ND_MODE|NS_MODE_HAIR, NULL); + } + + DAG_id_tag_update(&ob->id, OB_RECALC_DATA); + + return OPERATOR_FINISHED; +} + +void HAIR_OT_hair_edit_toggle(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Hair Edit Toggle"; + ot->idname = "HAIR_OT_hair_edit_toggle"; + ot->description = "Toggle hair edit mode"; + + /* api callbacks */ + ot->exec = hair_edit_toggle_exec; + ot->poll = hair_edit_toggle_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + + +/* ==== brush stroke ==== */ + +void hair_init_viewdata(bContext *C, HairViewData *viewdata) +{ + View3D *v3d; + bool has_zbuf; + + view3d_set_viewcontext(C, &viewdata->vc); + + v3d = viewdata->vc.v3d; + has_zbuf = (v3d->drawtype > OB_WIRE) && (v3d->flag & V3D_ZBUF_SELECT); + + view3d_get_transformation(viewdata->vc.ar, viewdata->vc.rv3d, NULL, &viewdata->mats); + + if (has_zbuf) { + if (v3d->flag & V3D_INVALID_BACKBUF) { + /* needed or else the draw matrix can be incorrect */ + view3d_operator_needs_opengl(C); + + ED_view3d_backbuf_validate(&viewdata->vc); + /* we may need to force an update here by setting the rv3d as dirty + * for now it seems ok, but take care!: + * rv3d->depths->dirty = 1; */ + ED_view3d_depth_update(viewdata->vc.ar); + } + } +} + +typedef struct HairStroke { + Scene *scene; + Object *ob; + BMEditStrands *edit; + + bool first; + float lastmouse[2]; + float zfac; + + float smoothdir[2]; +} HairStroke; + +static int hair_stroke_init(bContext *C, wmOperator *op) +{ + Scene *scene = CTX_data_scene(C); + Object *ob = CTX_data_active_object(C); + BMEditStrands *edit = BKE_editstrands_from_object(ob); + ARegion *ar = CTX_wm_region(C); + + HairStroke *stroke; + float min[3], max[3], center[3]; + + /* set the 'distance factor' for grabbing (used in comb etc) */ + hair_bm_min_max(edit, min, max); + mid_v3_v3v3(center, min, max); + + stroke = MEM_callocN(sizeof(HairStroke), "HairStroke"); + stroke->first = true; + op->customdata = stroke; + + stroke->scene = scene; + stroke->ob = ob; + stroke->edit = edit; + + stroke->zfac = ED_view3d_calc_zfac(ar->regiondata, center, NULL); + + return 1; +} + +static bool hair_stroke_apply(bContext *C, wmOperator *op, PointerRNA *itemptr) +{ + HairStroke *stroke = op->customdata; + Scene *scene = stroke->scene; + Object *ob = stroke->ob; + BMEditStrands *edit = stroke->edit; + HairEditSettings *settings = &scene->toolsettings->hair_edit; + ARegion *ar = CTX_wm_region(C); + const float smoothfac = 0.9f; /* XXX should this be configurable? */ + + float mouse[2], mdelta[2], zvec[3], delta_max; + int totsteps, step; + HairToolData tool_data; + bool updated = false; + + RNA_float_get_array(itemptr, "mouse", mouse); + + if (stroke->first) { + copy_v2_v2(stroke->lastmouse, mouse); + zero_v2(stroke->smoothdir); + stroke->first = false; + } + + if (!settings->brush) + return false; + + sub_v2_v2v2(mdelta, mouse, stroke->lastmouse); + delta_max = max_ff(fabsf(mdelta[0]), fabsf(mdelta[1])); + + totsteps = delta_max / (0.2f * BKE_brush_size_get(scene, settings->brush)) + 1; + mul_v2_fl(mdelta, 1.0f / (float)totsteps); + + /* low-pass filter to smooth out jittery pixel increments in the direction */ + interp_v2_v2v2(stroke->smoothdir, mdelta, stroke->smoothdir, smoothfac); + + hair_init_viewdata(C, &tool_data.viewdata); + tool_data.scene = scene; + tool_data.ob = ob; + tool_data.edit = edit; + tool_data.settings = settings; + + invert_m4_m4(tool_data.imat, ob->obmat); + copy_v2_v2(tool_data.mval, mouse); + tool_data.mdepth = stroke->zfac; + + zvec[0] = 0.0f; zvec[1] = 0.0f; zvec[2] = stroke->zfac; + ED_view3d_win_to_3d(ar, zvec, mouse, tool_data.loc); + ED_view3d_win_to_delta(ar, stroke->smoothdir, tool_data.delta, stroke->zfac); + /* tools work in object space */ + mul_m4_v3(tool_data.imat, tool_data.loc); + mul_mat3_m4_v3(tool_data.imat, tool_data.delta); + + for (step = 0; step < totsteps; ++step) { + bool step_updated = hair_brush_step(&tool_data); + + if (step_updated) + BKE_editstrands_solve_constraints(ob, edit, NULL); + + updated |= step_updated; + } + + copy_v2_v2(stroke->lastmouse, mouse); + + return updated; +} + +static void hair_stroke_exit(wmOperator *op) +{ + HairStroke *stroke = op->customdata; + MEM_freeN(stroke); +} + +static int hair_stroke_exec(bContext *C, wmOperator *op) +{ + HairStroke *stroke = op->customdata; + Object *ob = stroke->ob; + + bool updated = false; + + if (!hair_stroke_init(C, op)) + return OPERATOR_CANCELLED; + + RNA_BEGIN (op->ptr, itemptr, "stroke") + { + updated |= hair_stroke_apply(C, op, &itemptr); + } + RNA_END; + + if (updated) { + DAG_id_tag_update(&ob->id, OB_RECALC_DATA); + WM_event_add_notifier(C, NC_OBJECT|ND_MODIFIER, ob); + } + + hair_stroke_exit(op); + + return OPERATOR_FINISHED; +} + +static void hair_stroke_apply_event(bContext *C, wmOperator *op, const wmEvent *event) +{ + HairStroke *stroke = op->customdata; + Object *ob = stroke->ob; + + PointerRNA itemptr; + float mouse[2]; + bool updated = false; + + mouse[0] = event->mval[0]; + mouse[1] = event->mval[1]; + + /* fill in stroke */ + RNA_collection_add(op->ptr, "stroke", &itemptr); + + RNA_float_set_array(&itemptr, "mouse", mouse); + + /* apply */ + updated |= hair_stroke_apply(C, op, &itemptr); + + if (updated) { + DAG_id_tag_update(&ob->id, OB_RECALC_DATA); + WM_event_add_notifier(C, NC_OBJECT|ND_MODIFIER, ob); + } + else { + /* even if nothing was changed, still trigger redraw + * for brush drawing during the modal operator + */ + WM_event_add_notifier(C, NC_OBJECT|ND_DRAW, ob); + } +} + +static int hair_stroke_invoke(bContext *C, wmOperator *op, const wmEvent *event) +{ + if (!hair_stroke_init(C, op)) + return OPERATOR_CANCELLED; + + hair_stroke_apply_event(C, op, event); + + WM_event_add_modal_handler(C, op); + + return OPERATOR_RUNNING_MODAL; +} + +static int hair_stroke_modal(bContext *C, wmOperator *op, const wmEvent *event) +{ + switch (event->type) { + case LEFTMOUSE: + case MIDDLEMOUSE: + case RIGHTMOUSE: // XXX hardcoded + hair_stroke_exit(op); + return OPERATOR_FINISHED; + case MOUSEMOVE: + hair_stroke_apply_event(C, op, event); + break; + } + + return OPERATOR_RUNNING_MODAL; +} + +static void hair_stroke_cancel(bContext *UNUSED(C), wmOperator *op) +{ + hair_stroke_exit(op); +} + +void HAIR_OT_stroke(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Hair Stroke"; + ot->idname = "HAIR_OT_stroke"; + ot->description = "Use a stroke tool on hair strands"; + + /* api callbacks */ + ot->exec = hair_stroke_exec; + ot->invoke = hair_stroke_invoke; + ot->modal = hair_stroke_modal; + ot->cancel = hair_stroke_cancel; + ot->poll = hair_edit_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO|OPTYPE_BLOCKING; + + /* properties */ + RNA_def_collection_runtime(ot->srna, "stroke", &RNA_OperatorStrokeElement, "Stroke", ""); +} diff --git a/source/blender/editors/hair/hair_intern.h b/source/blender/editors/hair/hair_intern.h new file mode 100644 index 00000000000..cc805136ace --- /dev/null +++ b/source/blender/editors/hair/hair_intern.h @@ -0,0 +1,124 @@ +/* + * ***** 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) Blender Foundation + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): Lukas Toenne + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/editors/hair/hair_intern.h + * \ingroup edhair + */ + +#ifndef __HAIR_INTERN_H__ +#define __HAIR_INTERN_H__ + +#include "BIF_glutil.h" + +#include "ED_view3d.h" + +struct ARegion; +struct bContext; +struct wmOperatorType; +struct rcti; + +struct Object; +struct Strands; +struct DupliObjectData; +struct StrandsKeyCacheModifier; +struct DerivedMesh; + +/* hair_edit.c */ +bool hair_use_mirror_x(struct Object *ob); +bool hair_use_mirror_topology(struct Object *ob); + +int hair_edit_toggle_poll(struct bContext *C); +int hair_edit_poll(struct bContext *C); + +void HAIR_OT_hair_edit_toggle(struct wmOperatorType *ot); + +/* hair_select.c */ +void HAIR_OT_select_all(struct wmOperatorType *ot); +void HAIR_OT_select_linked(struct wmOperatorType *ot); + +/* hair_stroke.c */ +void HAIR_OT_stroke(struct wmOperatorType *ot); + +/* hair_object_cachelib.c */ +bool ED_hair_object_has_hair_cache_data(struct Object *ob); +bool ED_hair_object_init_cache_edit(struct Object *ob); +bool ED_hair_object_apply_cache_edit(struct Object *ob); + +/* hair_object_particles.c */ +bool ED_hair_object_has_hair_particle_data(struct Object *ob); +bool ED_hair_object_init_particle_edit(struct Scene *scene, struct Object *ob); +bool ED_hair_object_apply_particle_edit(struct Object *ob); + + +/* ==== Hair Brush ==== */ + +typedef struct HairViewData { + ViewContext vc; + bglMats mats; +} HairViewData; + +void hair_init_viewdata(struct bContext *C, struct HairViewData *viewdata); + +bool hair_test_depth(struct HairViewData *viewdata, const float co[3], const int screen_co[2]); +bool hair_test_vertex_inside_circle(struct HairViewData *viewdata, const float mval[2], float radsq, + struct BMVert *v, float *r_dist); +bool hair_test_edge_inside_circle(struct HairViewData *viewdata, const float mval[2], float radsq, + struct BMVert *v1, struct BMVert *v2, float *r_dist, float *r_lambda); +bool hair_test_vertex_inside_rect(struct HairViewData *viewdata, struct rcti *rect, struct BMVert *v); +bool hair_test_vertex_inside_lasso(struct HairViewData *viewdata, const int mcoords[][2], short moves, struct BMVert *v); + +typedef struct HairToolData { + /* context */ + struct Scene *scene; + struct Object *ob; + struct BMEditStrands *edit; + struct HairEditSettings *settings; + HairViewData viewdata; + + /* view space */ + float mval[2]; /* mouse coordinates */ + float mdepth; /* mouse z depth */ + + /* object space */ + float imat[4][4]; /* obmat inverse */ + float loc[3]; /* start location */ + float delta[3]; /* stroke step */ +} HairToolData; + +bool hair_brush_step(struct HairToolData *data); + +/* ==== Cursor ==== */ + +void hair_edit_cursor_start(struct bContext *C, int (*poll)(struct bContext *C)); + +/* ==== BMesh utilities ==== */ + +struct BMEditStrands; + +void hair_bm_min_max(struct BMEditStrands *edit, float min[3], float max[3]); + +#endif diff --git a/source/blender/editors/hair/hair_mirror.c b/source/blender/editors/hair/hair_mirror.c new file mode 100644 index 00000000000..7f5b14e4b37 --- /dev/null +++ b/source/blender/editors/hair/hair_mirror.c @@ -0,0 +1,210 @@ +/* + * ***** 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) Blender Foundation + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): Lukas Toenne + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/editors/hair/hair_mirror.c + * \ingroup edhair + */ + +#include <stdlib.h> + +#include "MEM_guardedalloc.h" + +#include "BLI_utildefines.h" +#include "BLI_math.h" + +#include "BKE_editmesh_bvh.h" +#include "BKE_editstrands.h" + +#include "ED_physics.h" +#include "ED_util.h" + +#include "bmesh.h" + +#include "hair_intern.h" + +/* TODO use_topology is not yet implemented for strands. + * Native strand topology is not very useful for this. + * Instead, the topology of the scalp mesh should be used for finding mirrored strand roots, + * then the arc- or parametric length of a vertex from the root to find mirrored verts. + */ + +#define BM_SEARCH_MAXDIST_MIRR 0.00002f +#define BM_CD_LAYER_ID "__mirror_index" +/** + * \param edit Edit strands. + * \param use_self Allow a vertex to point to its self (middle verts). + * \param use_select Restrict to selected verts. + * \param use_topology Use topology mirror. + * \param maxdist Distance for close point test. + * \param r_index Optional array to write into, as an alternative to a customdata layer (length of total verts). + */ +void ED_strands_mirror_cache_begin_ex(BMEditStrands *edit, const int axis, const bool use_self, const bool use_select, + /* extra args */ + const bool UNUSED(use_topology), float maxdist, int *r_index) +{ + BMesh *bm = edit->bm; + BMIter iter; + BMVert *v; + int cd_vmirr_offset; + int i; + + /* one or the other is used depending if topo is enabled */ + struct BMBVHTree *tree = NULL; + + BM_mesh_elem_table_ensure(bm, BM_VERT); + + if (r_index == NULL) { + const char *layer_id = BM_CD_LAYER_ID; + edit->mirror_cdlayer = CustomData_get_named_layer_index(&bm->vdata, CD_PROP_INT, layer_id); + if (edit->mirror_cdlayer == -1) { + BM_data_layer_add_named(bm, &bm->vdata, CD_PROP_INT, layer_id); + edit->mirror_cdlayer = CustomData_get_named_layer_index(&bm->vdata, CD_PROP_INT, layer_id); + } + + cd_vmirr_offset = CustomData_get_n_offset(&bm->vdata, CD_PROP_INT, + edit->mirror_cdlayer - CustomData_get_layer_index(&bm->vdata, CD_PROP_INT)); + + bm->vdata.layers[edit->mirror_cdlayer].flag |= CD_FLAG_TEMPORARY; + } + + BM_mesh_elem_index_ensure(bm, BM_VERT); + + tree = BKE_bmbvh_new(edit->bm, NULL, 0, 0, NULL, false); + +#define VERT_INTPTR(_v, _i) r_index ? &r_index[_i] : BM_ELEM_CD_GET_VOID_P(_v, cd_vmirr_offset); + + BM_ITER_MESH_INDEX (v, &iter, bm, BM_VERTS_OF_MESH, i) { + BLI_assert(BM_elem_index_get(v) == i); + + /* temporary for testing, check for selection */ + if (use_select && !BM_elem_flag_test(v, BM_ELEM_SELECT)) { + /* do nothing */ + } + else { + BMVert *v_mirr; + float co[3]; + int *idx = VERT_INTPTR(v, i); + + copy_v3_v3(co, v->co); + co[axis] *= -1.0f; + v_mirr = BKE_bmbvh_find_vert_closest(tree, co, maxdist); + + if (v_mirr && (use_self || (v_mirr != v))) { + const int i_mirr = BM_elem_index_get(v_mirr); + *idx = i_mirr; + idx = VERT_INTPTR(v_mirr, i_mirr); + *idx = i; + } + else { + *idx = -1; + } + } + + } + +#undef VERT_INTPTR + + BKE_bmbvh_free(tree); +} + +void ED_strands_mirror_cache_begin(BMEditStrands *edit, const int axis, + const bool use_self, const bool use_select, + const bool use_topology) +{ + ED_strands_mirror_cache_begin_ex(edit, axis, + use_self, use_select, + /* extra args */ + use_topology, BM_SEARCH_MAXDIST_MIRR, NULL); +} + +BMVert *ED_strands_mirror_get(BMEditStrands *edit, BMVert *v) +{ + const int *mirr = CustomData_bmesh_get_layer_n(&edit->bm->vdata, v->head.data, edit->mirror_cdlayer); + + BLI_assert(edit->mirror_cdlayer != -1); /* invalid use */ + + if (mirr && *mirr >= 0 && *mirr < edit->bm->totvert) { + if (!edit->bm->vtable) { + printf("err: should only be called between " + "ED_strands_mirror_cache_begin and ED_strands_mirror_cache_end\n"); + return NULL; + } + + return edit->bm->vtable[*mirr]; + } + + return NULL; +} + +BMEdge *ED_strands_mirror_get_edge(BMEditStrands *edit, BMEdge *e) +{ + BMVert *v1_mirr = ED_strands_mirror_get(edit, e->v1); + if (v1_mirr) { + BMVert *v2_mirr = ED_strands_mirror_get(edit, e->v2); + if (v2_mirr) { + return BM_edge_exists(v1_mirr, v2_mirr); + } + } + + return NULL; +} + +void ED_strands_mirror_cache_clear(BMEditStrands *edit, BMVert *v) +{ + int *mirr = CustomData_bmesh_get_layer_n(&edit->bm->vdata, v->head.data, edit->mirror_cdlayer); + + BLI_assert(edit->mirror_cdlayer != -1); /* invalid use */ + + if (mirr) { + *mirr = -1; + } +} + +void ED_strands_mirror_cache_end(BMEditStrands *edit) +{ + edit->mirror_cdlayer = -1; +} + +void ED_strands_mirror_apply(BMEditStrands *edit, const int sel_from, const int sel_to) +{ + BMIter iter; + BMVert *v; + + BLI_assert((edit->bm->vtable != NULL) && ((edit->bm->elem_table_dirty & BM_VERT) == 0)); + + BM_ITER_MESH (v, &iter, edit->bm, BM_VERTS_OF_MESH) { + if (BM_elem_flag_test(v, BM_ELEM_SELECT) == sel_from) { + BMVert *mirr = ED_strands_mirror_get(edit, v); + if (mirr) { + if (BM_elem_flag_test(mirr, BM_ELEM_SELECT) == sel_to) { + copy_v3_v3(mirr->co, v->co); + mirr->co[0] *= -1.0f; + } + } + } + } +} diff --git a/source/blender/editors/hair/hair_object_cachelib.c b/source/blender/editors/hair/hair_object_cachelib.c new file mode 100644 index 00000000000..576d8cd2c9d --- /dev/null +++ b/source/blender/editors/hair/hair_object_cachelib.c @@ -0,0 +1,104 @@ +/* + * ***** 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) Blender Foundation + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): Lukas Toenne + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/editors/hair/hair_object_cachelib.c + * \ingroup edhair + */ + +#include <stdlib.h> + +#include "MEM_guardedalloc.h" + +#include "BLI_utildefines.h" + +#include "DNA_cache_library_types.h" +#include "DNA_mesh_types.h" +#include "DNA_object_types.h" +#include "DNA_scene_types.h" +#include "DNA_strands_types.h" + +#include "BKE_anim.h" +#include "BKE_cache_library.h" +#include "BKE_cdderivedmesh.h" +#include "BKE_DerivedMesh.h" +#include "BKE_editstrands.h" +#include "BKE_strands.h" + +#include "bmesh.h" + +#include "hair_intern.h" + +bool ED_hair_object_has_hair_cache_data(Object *ob) +{ + return BKE_cache_modifier_strands_key_get(ob, NULL, NULL, NULL, NULL, NULL, NULL); +} + +bool ED_hair_object_init_cache_edit(Object *ob) +{ + StrandsKeyCacheModifier *skmd; + DerivedMesh *dm; + Strands *strands; + float mat[4][4]; + + if (!BKE_cache_modifier_strands_key_get(ob, &skmd, &dm, &strands, NULL, NULL, mat)) + return false; + + if (!skmd->edit) { + BMesh *bm = BKE_cache_strands_to_bmesh(strands, skmd->key, mat, skmd->shapenr, dm); + + skmd->edit = BKE_editstrands_create(bm, dm, mat); + } + + return true; +} + +bool ED_hair_object_apply_cache_edit(Object *ob) +{ + StrandsKeyCacheModifier *skmd; + DerivedMesh *dm; + Strands *strands; + DupliObjectData *dobdata; + const char *name; + float mat[4][4]; + + if (!BKE_cache_modifier_strands_key_get(ob, &skmd, &dm, &strands, &dobdata, &name, mat)) + return false; + + if (skmd->edit) { + Strands *nstrands; + + nstrands = BKE_cache_strands_from_bmesh(skmd->edit, skmd->key, mat, dm); + + BKE_dupli_object_data_add_strands(dobdata, name, nstrands); + + BKE_editstrands_free(skmd->edit); + MEM_freeN(skmd->edit); + skmd->edit = NULL; + } + + return true; +} diff --git a/source/blender/editors/hair/hair_object_particles.c b/source/blender/editors/hair/hair_object_particles.c new file mode 100644 index 00000000000..a6d05327482 --- /dev/null +++ b/source/blender/editors/hair/hair_object_particles.c @@ -0,0 +1,101 @@ +/* + * ***** 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) Blender Foundation + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): Lukas Toenne + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/editors/hair/hair_object_particles.c + * \ingroup edhair + */ + +#include <stdlib.h> + +#include "MEM_guardedalloc.h" + +#include "BLI_utildefines.h" + +#include "DNA_mesh_types.h" +#include "DNA_object_types.h" +#include "DNA_particle_types.h" +#include "DNA_scene_types.h" + +#include "BKE_cdderivedmesh.h" +#include "BKE_DerivedMesh.h" +#include "BKE_editstrands.h" +#include "BKE_particle.h" + +#include "bmesh.h" + +#include "hair_intern.h" + +bool ED_hair_object_has_hair_particle_data(Object *ob) +{ + ParticleSystem *psys = psys_get_current(ob); + if (psys && psys->part->type == PART_HAIR) + return true; + + return false; +} + +bool ED_hair_object_init_particle_edit(Scene *scene, Object *ob) +{ + ParticleSystem *psys = psys_get_current(ob); + BMesh *bm; + DerivedMesh *dm; + + if (psys && psys->part->type == PART_HAIR) { + if (!psys->hairedit) { + bm = BKE_particles_to_bmesh(ob, psys); + + if (ob->type == OB_MESH || ob->derivedFinal) + dm = ob->derivedFinal ? ob->derivedFinal : mesh_get_derived_final(scene, ob, CD_MASK_BAREMESH); + else + dm = NULL; + + psys->hairedit = BKE_editstrands_create(bm, dm, NULL); + } + return true; + } + + return false; +} + +bool ED_hair_object_apply_particle_edit(Object *ob) +{ + ParticleSystem *psys = psys_get_current(ob); + if (psys && psys->part->type == PART_HAIR) { + if (psys->hairedit) { + BKE_particles_from_bmesh(ob, psys); + psys->flag |= PSYS_EDITED; + + BKE_editstrands_free(psys->hairedit); + MEM_freeN(psys->hairedit); + psys->hairedit = NULL; + } + + return true; + } + + return false; +} diff --git a/source/blender/editors/hair/hair_ops.c b/source/blender/editors/hair/hair_ops.c new file mode 100644 index 00000000000..bb3b027cf20 --- /dev/null +++ b/source/blender/editors/hair/hair_ops.c @@ -0,0 +1,143 @@ +/* + * ***** 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) Blender Foundation + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): Lukas Toenne + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/editors/hair/hair_ops.c + * \ingroup edhair + */ + +#include "BLI_utildefines.h" + +#include "DNA_object_types.h" + +#include "BKE_context.h" + +#include "RNA_access.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "ED_physics.h" + +#include "hair_intern.h" +#include "paint_intern.h" + +void ED_operatortypes_hair(void) +{ + WM_operatortype_append(HAIR_OT_hair_edit_toggle); + + WM_operatortype_append(HAIR_OT_select_all); + WM_operatortype_append(HAIR_OT_select_linked); + + WM_operatortype_append(HAIR_OT_stroke); +} + +static int hair_poll(bContext *C) +{ + if (hair_edit_toggle_poll(C)) + if (CTX_data_active_object(C)->mode & OB_MODE_HAIR_EDIT) + return true; + + return false; +} + +static void ed_keymap_hair_brush_switch(wmKeyMap *keymap, const char *mode) +{ + wmKeyMapItem *kmi; + int i; + /* index 0-9 (zero key is tenth), shift key for index 10-19 */ + for (i = 0; i < 20; i++) { + kmi = WM_keymap_add_item(keymap, "BRUSH_OT_active_index_set", + ZEROKEY + ((i + 1) % 10), KM_PRESS, i < 10 ? 0 : KM_SHIFT, 0); + RNA_string_set(kmi->ptr, "mode", mode); + RNA_int_set(kmi->ptr, "index", i); + } +} + +static void ed_keymap_hair_brush_size(wmKeyMap *keymap, const char *UNUSED(path)) +{ + wmKeyMapItem *kmi; + + kmi = WM_keymap_add_item(keymap, "BRUSH_OT_scale_size", LEFTBRACKETKEY, KM_PRESS, 0, 0); + RNA_float_set(kmi->ptr, "scalar", 0.9); + + kmi = WM_keymap_add_item(keymap, "BRUSH_OT_scale_size", RIGHTBRACKETKEY, KM_PRESS, 0, 0); + RNA_float_set(kmi->ptr, "scalar", 10.0 / 9.0); // 1.1111.... +} + +static void ed_keymap_hair_brush_radial_control(wmKeyMap *keymap, const char *settings, RCFlags flags) +{ + wmKeyMapItem *kmi; + /* only size needs to follow zoom, strength shows fixed size circle */ + int flags_nozoom = flags & (~RC_ZOOM); + int flags_noradial_secondary = flags & (~(RC_SECONDARY_ROTATION | RC_ZOOM)); + + kmi = WM_keymap_add_item(keymap, "WM_OT_radial_control", FKEY, KM_PRESS, 0, 0); + set_brush_rc_props(kmi->ptr, settings, "size", "use_unified_size", flags); + + kmi = WM_keymap_add_item(keymap, "WM_OT_radial_control", FKEY, KM_PRESS, KM_SHIFT, 0); + set_brush_rc_props(kmi->ptr, settings, "strength", "use_unified_strength", flags_nozoom); + + if (flags & RC_WEIGHT) { + kmi = WM_keymap_add_item(keymap, "WM_OT_radial_control", WKEY, KM_PRESS, 0, 0); + set_brush_rc_props(kmi->ptr, settings, "weight", "use_unified_weight", flags_nozoom); + } + + if (flags & RC_ROTATION) { + kmi = WM_keymap_add_item(keymap, "WM_OT_radial_control", FKEY, KM_PRESS, KM_CTRL, 0); + set_brush_rc_props(kmi->ptr, settings, "texture_slot.angle", NULL, flags_noradial_secondary); + } + + if (flags & RC_SECONDARY_ROTATION) { + kmi = WM_keymap_add_item(keymap, "WM_OT_radial_control", FKEY, KM_PRESS, KM_CTRL | KM_ALT, 0); + set_brush_rc_props(kmi->ptr, settings, "mask_texture_slot.angle", NULL, flags_nozoom); + } +} + +void ED_keymap_hair(wmKeyConfig *keyconf) +{ + wmKeyMap *keymap; + wmKeyMapItem *kmi; + + keymap = WM_keymap_find(keyconf, "Hair", 0, 0); + keymap->poll = hair_poll; + + kmi = WM_keymap_add_item(keymap, "HAIR_OT_select_all", AKEY, KM_PRESS, 0, 0); + RNA_enum_set(kmi->ptr, "action", SEL_TOGGLE); + kmi = WM_keymap_add_item(keymap, "HAIR_OT_select_all", IKEY, KM_PRESS, KM_CTRL, 0); + RNA_enum_set(kmi->ptr, "action", SEL_INVERT); + + kmi = WM_keymap_add_item(keymap, "HAIR_OT_select_linked", LKEY, KM_PRESS, 0, 0); + RNA_boolean_set(kmi->ptr, "deselect", false); + kmi = WM_keymap_add_item(keymap, "HAIR_OT_select_linked", LKEY, KM_PRESS, KM_SHIFT, 0); + RNA_boolean_set(kmi->ptr, "deselect", true); + + kmi = WM_keymap_add_item(keymap, "HAIR_OT_stroke", LEFTMOUSE, KM_PRESS, 0, 0); + + ed_keymap_hair_brush_switch(keymap, "hair_edit"); + ed_keymap_hair_brush_size(keymap, "tool_settings.hair_edit.brush.size"); + ed_keymap_hair_brush_radial_control(keymap, "hair_edit", 0); +} diff --git a/source/blender/editors/hair/hair_select.c b/source/blender/editors/hair/hair_select.c new file mode 100644 index 00000000000..986e669f3a7 --- /dev/null +++ b/source/blender/editors/hair/hair_select.c @@ -0,0 +1,502 @@ +/* + * ***** 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) Blender Foundation + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): Lukas Toenne + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/editors/hair/hair_select.c + * \ingroup edhair + */ + +#include <stdlib.h> + +#include "MEM_guardedalloc.h" + +#include "BLI_utildefines.h" +#include "BLI_math.h" +#include "BLI_rect.h" + +#include "DNA_object_types.h" +#include "DNA_scene_types.h" +#include "DNA_screen_types.h" +#include "DNA_view3d_types.h" + +#include "BKE_context.h" +#include "BKE_depsgraph.h" +#include "BKE_editstrands.h" + +#include "bmesh.h" + +#include "RNA_access.h" +#include "RNA_define.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "ED_object.h" +#include "ED_physics.h" +#include "ED_view3d.h" + +#include "hair_intern.h" + +BLI_INLINE bool apply_select_action_flag(BMVert *v, int action) +{ + bool cursel = BM_elem_flag_test_bool(v, BM_ELEM_SELECT); + bool newsel; + + switch (action) { + case SEL_SELECT: + newsel = true; + break; + case SEL_DESELECT: + newsel = false; + break; + case SEL_INVERT: + newsel = !cursel; + break; + case SEL_TOGGLE: + /* toggle case should be converted to SELECT or DESELECT based on global state */ + BLI_assert(false); + break; + } + + if (newsel != cursel) { + BM_elem_flag_set(v, BM_ELEM_SELECT, newsel); + return true; + } + else + return false; +} + +/* poll function */ +typedef bool (*PollVertexCb)(void *userdata, struct BMVert *v); +/* distance metric function */ +typedef bool (*DistanceVertexCb)(void *userdata, struct BMVert *v, float *dist); +typedef void (*ActionVertexCb)(void *userdata, struct BMVert *v, int action); + +static int hair_select_verts_filter(BMEditStrands *edit, HairEditSelectMode select_mode, int action, PollVertexCb cb, void *userdata) +{ + BMesh *bm = edit->bm; + + BMVert *v; + BMIter iter; + int tot = 0; + + bm->selectmode = BM_VERT; + + switch (select_mode) { + case HAIR_SELECT_STRAND: + break; + case HAIR_SELECT_VERTEX: + BM_ITER_MESH(v, &iter, edit->bm, BM_VERTS_OF_MESH) { + if (!cb(userdata, v)) + continue; + + if (apply_select_action_flag(v, action)) + ++tot; + } + break; + case HAIR_SELECT_TIP: + BM_ITER_MESH(v, &iter, edit->bm, BM_VERTS_OF_MESH) { + if (!BM_strands_vert_is_tip(v)) + continue; + if (!cb(userdata, v)) + continue; + + if (apply_select_action_flag(v, action)) + ++tot; + } + break; + } + + BM_mesh_select_mode_flush(bm); + + return tot; +} + +static bool hair_select_verts_closest(BMEditStrands *edit, HairEditSelectMode select_mode, int action, DistanceVertexCb cb, ActionVertexCb action_cb, void *userdata) +{ + BMesh *bm = edit->bm; + + BMVert *v; + BMIter iter; + + float dist; + BMVert *closest_v = NULL; + float closest_dist = FLT_MAX; + + bm->selectmode = BM_VERT; + + switch (select_mode) { + case HAIR_SELECT_STRAND: + break; + case HAIR_SELECT_VERTEX: + BM_ITER_MESH(v, &iter, edit->bm, BM_VERTS_OF_MESH) { + if (!cb(userdata, v, &dist)) + continue; + + if (dist < closest_dist) { + closest_v = v; + closest_dist = dist; + } + } + break; + case HAIR_SELECT_TIP: + BM_ITER_MESH(v, &iter, edit->bm, BM_VERTS_OF_MESH) { + if (!BM_strands_vert_is_tip(v)) + continue; + if (!cb(userdata, v, &dist)) + continue; + + if (dist < closest_dist) { + closest_v = v; + closest_dist = dist; + } + } + break; + } + + if (closest_v) { + action_cb(userdata, closest_v, action); + + BM_mesh_select_mode_flush(bm); + return true; + } + else + return false; +} + +static void hair_deselect_all(BMEditStrands *edit) +{ + BMVert *v; + BMIter iter; + + BM_ITER_MESH(v, &iter, edit->bm, BM_VERTS_OF_MESH) { + BM_elem_flag_set(v, BM_ELEM_SELECT, false); + } +} + +/* ------------------------------------------------------------------------- */ + +/************************ select/deselect all operator ************************/ + +static bool poll_vertex_all(void *UNUSED(userdata), struct BMVert *UNUSED(v)) +{ + return true; +} + +static int select_all_exec(bContext *C, wmOperator *op) +{ + Scene *scene = CTX_data_scene(C); + Object *ob = CTX_data_active_object(C); + BMEditStrands *edit = BKE_editstrands_from_object(ob); + HairEditSettings *settings = &scene->toolsettings->hair_edit; + int action = RNA_enum_get(op->ptr, "action"); + + if (!edit) + return 0; + + /* toggle action depends on current global selection state */ + if (action == SEL_TOGGLE) { + if (edit->bm->totvertsel == 0) + action = SEL_SELECT; + else + action = SEL_DESELECT; + } + + hair_select_verts_filter(edit, settings->select_mode, action, poll_vertex_all, NULL); + + WM_event_add_notifier(C, NC_OBJECT | ND_DRAW | NA_SELECTED, ob); + + return OPERATOR_FINISHED; +} + +void HAIR_OT_select_all(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Select/Deselect All"; + ot->idname = "HAIR_OT_select_all"; + ot->description = "Select/Deselect all hair vertices"; + + /* api callbacks */ + ot->exec = select_all_exec; + ot->poll = hair_edit_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + WM_operator_properties_select_all(ot); +} + +/************************ mouse select operator ************************/ + +typedef struct DistanceVertexCirleData { + HairViewData viewdata; + float mval[2]; + float radsq; +} DistanceVertexCirleData; + +static bool distance_vertex_circle(void *userdata, struct BMVert *v, float *dist) +{ + DistanceVertexCirleData *data = userdata; + + return hair_test_vertex_inside_circle(&data->viewdata, data->mval, data->radsq, v, dist); +} + +static void closest_vertex_select(void *UNUSED(userdata), struct BMVert *v, int action) +{ + apply_select_action_flag(v, action); +} + +int ED_hair_mouse_select(bContext *C, const int mval[2], bool extend, bool deselect, bool toggle) +{ + Scene *scene = CTX_data_scene(C); + Object *ob = CTX_data_active_object(C); + BMEditStrands *edit = BKE_editstrands_from_object(ob); + HairEditSettings *settings = &scene->toolsettings->hair_edit; + float select_radius = ED_view3d_select_dist_px(); + + DistanceVertexCirleData data; + int action; + + if (!extend && !deselect && !toggle) { + hair_deselect_all(edit); + } + + hair_init_viewdata(C, &data.viewdata); + data.mval[0] = mval[0]; + data.mval[1] = mval[1]; + data.radsq = select_radius * select_radius; + + if (extend) + action = SEL_SELECT; + else if (deselect) + action = SEL_DESELECT; + else + action = SEL_INVERT; + + hair_select_verts_closest(edit, settings->select_mode, action, distance_vertex_circle, closest_vertex_select, &data); + + WM_event_add_notifier(C, NC_OBJECT | ND_DRAW | NA_SELECTED, ob); + + return OPERATOR_FINISHED; +} + +/************************ select linked operator ************************/ + +static void linked_vertices_select(void *UNUSED(userdata), struct BMVert *v, int action) +{ + BMVert *lv; + + apply_select_action_flag(v, action); + + for (lv = BM_strands_vert_prev(v); lv; lv = BM_strands_vert_prev(lv)) + apply_select_action_flag(lv, action); + for (lv = BM_strands_vert_next(v); lv; lv = BM_strands_vert_next(lv)) + apply_select_action_flag(lv, action); +} + +static int select_linked_exec(bContext *C, wmOperator *op) +{ + Scene *scene = CTX_data_scene(C); + Object *ob = CTX_data_active_object(C); + BMEditStrands *edit = BKE_editstrands_from_object(ob); + HairEditSettings *settings = &scene->toolsettings->hair_edit; + float select_radius = ED_view3d_select_dist_px(); + + DistanceVertexCirleData data; + int location[2]; + int action; + + RNA_int_get_array(op->ptr, "location", location); + + hair_init_viewdata(C, &data.viewdata); + data.mval[0] = location[0]; + data.mval[1] = location[1]; + data.radsq = select_radius * select_radius; + + action = RNA_boolean_get(op->ptr, "deselect") ? SEL_DESELECT : SEL_SELECT; + + hair_select_verts_closest(edit, settings->select_mode, action, distance_vertex_circle, linked_vertices_select, &data); + + WM_event_add_notifier(C, NC_OBJECT | ND_DRAW | NA_SELECTED, ob); + + return OPERATOR_FINISHED; +} + +static int select_linked_invoke(bContext *C, wmOperator *op, const wmEvent *event) +{ + RNA_int_set_array(op->ptr, "location", event->mval); + return select_linked_exec(C, op); +} + +void HAIR_OT_select_linked(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Select Linked"; + ot->idname = "HAIR_OT_select_linked"; + ot->description = "Select connected vertices"; + + /* api callbacks */ + ot->exec = select_linked_exec; + ot->invoke = select_linked_invoke; + ot->poll = hair_edit_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* properties */ + RNA_def_boolean(ot->srna, "deselect", 0, "Deselect", "Deselect linked keys rather than selecting them"); + RNA_def_int_vector(ot->srna, "location", 2, NULL, 0, INT_MAX, "Location", "", 0, 16384); +} + +/************************ border select operator ************************/ + +typedef struct PollVertexRectData { + HairViewData viewdata; + rcti rect; +} PollVertexRectData; + +static bool poll_vertex_inside_rect(void *userdata, struct BMVert *v) +{ + PollVertexRectData *data = userdata; + + return hair_test_vertex_inside_rect(&data->viewdata, &data->rect, v); +} + +int ED_hair_border_select(bContext *C, rcti *rect, bool select, bool extend) +{ + Scene *scene = CTX_data_scene(C); + Object *ob = CTX_data_active_object(C); + BMEditStrands *edit = BKE_editstrands_from_object(ob); + HairEditSettings *settings = &scene->toolsettings->hair_edit; + + PollVertexRectData data; + int action; + + if (!extend && select) + hair_deselect_all(edit); + + hair_init_viewdata(C, &data.viewdata); + data.rect = *rect; + + if (extend) + action = SEL_SELECT; + else if (select) + action = SEL_INVERT; + else + action = SEL_DESELECT; + + hair_select_verts_filter(edit, settings->select_mode, action, poll_vertex_inside_rect, &data); + + WM_event_add_notifier(C, NC_OBJECT | ND_DRAW | NA_SELECTED, ob); + + return OPERATOR_FINISHED; +} + +/************************ circle select operator ************************/ + +typedef struct PollVertexCirleData { + HairViewData viewdata; + float mval[2]; + float radsq; +} PollVertexCirleData; + +static bool poll_vertex_inside_circle(void *userdata, struct BMVert *v) +{ + PollVertexCirleData *data = userdata; + float dist; + + return hair_test_vertex_inside_circle(&data->viewdata, data->mval, data->radsq, v, &dist); +} + +int ED_hair_circle_select(bContext *C, bool select, const int mval[2], float radius) +{ + Scene *scene = CTX_data_scene(C); + Object *ob = CTX_data_active_object(C); + BMEditStrands *edit = BKE_editstrands_from_object(ob); + HairEditSettings *settings = &scene->toolsettings->hair_edit; + int action = select ? SEL_SELECT : SEL_DESELECT; + + PollVertexCirleData data; + int tot; + + if (!edit) + return 0; + + hair_init_viewdata(C, &data.viewdata); + data.mval[0] = mval[0]; + data.mval[1] = mval[1]; + data.radsq = radius * radius; + + tot = hair_select_verts_filter(edit, settings->select_mode, action, poll_vertex_inside_circle, &data); + + return tot; +} + +/************************ lasso select operator ************************/ + +typedef struct PollVertexLassoData { + HairViewData viewdata; + const int (*mcoords)[2]; + short moves; +} PollVertexLassoData; + +static bool poll_vertex_inside_lasso(void *userdata, struct BMVert *v) +{ + PollVertexLassoData *data = userdata; + + return hair_test_vertex_inside_lasso(&data->viewdata, data->mcoords, data->moves, v); +} + +int ED_hair_lasso_select(bContext *C, const int mcoords[][2], const short moves, bool extend, bool select) +{ + Scene *scene = CTX_data_scene(C); + Object *ob = CTX_data_active_object(C); + BMEditStrands *edit = BKE_editstrands_from_object(ob); + HairEditSettings *settings = &scene->toolsettings->hair_edit; + + PollVertexLassoData data; + int action; + + if (!extend && select) + hair_deselect_all(edit); + + hair_init_viewdata(C, &data.viewdata); + data.mcoords = mcoords; + data.moves = moves; + + if (extend) + action = SEL_SELECT; + else if (select) + action = SEL_INVERT; + else + action = SEL_DESELECT; + + hair_select_verts_filter(edit, settings->select_mode, action, poll_vertex_inside_lasso, &data); + + WM_event_add_notifier(C, NC_OBJECT | ND_DRAW | NA_SELECTED, ob); + + return OPERATOR_FINISHED; +} diff --git a/source/blender/editors/hair/hair_stroke.c b/source/blender/editors/hair/hair_stroke.c new file mode 100644 index 00000000000..aeb460990db --- /dev/null +++ b/source/blender/editors/hair/hair_stroke.c @@ -0,0 +1,498 @@ +/* + * ***** 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) Blender Foundation + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): Lukas Toenne + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/editors/hair/hair_edit.c + * \ingroup edhair + */ + +#include <stdlib.h> + +#include "MEM_guardedalloc.h" + +#include "BLI_utildefines.h" +#include "BLI_lasso.h" +#include "BLI_math.h" +#include "BLI_rect.h" + +#include "DNA_brush_types.h" +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_object_types.h" +#include "DNA_scene_types.h" +#include "DNA_view3d_types.h" + +#include "BKE_brush.h" +#include "BKE_DerivedMesh.h" +#include "BKE_editstrands.h" +#include "BKE_effect.h" +#include "BKE_mesh_sample.h" + +#include "bmesh.h" + +#include "BIF_gl.h" +#include "BIF_glutil.h" + +#include "ED_physics.h" +#include "ED_view3d.h" + +#include "hair_intern.h" + +bool hair_test_depth(HairViewData *viewdata, const float co[3], const int screen_co[2]) +{ + View3D *v3d = viewdata->vc.v3d; + ViewDepths *vd = viewdata->vc.rv3d->depths; + const bool has_zbuf = (v3d->drawtype > OB_WIRE) && (v3d->flag & V3D_ZBUF_SELECT); + + double ux, uy, uz; + float depth; + + /* nothing to do */ + if (!has_zbuf) + return true; + + gluProject(co[0], co[1], co[2], + viewdata->mats.modelview, viewdata->mats.projection, viewdata->mats.viewport, + &ux, &uy, &uz); + + /* check if screen_co is within bounds because brush_cut uses out of screen coords */ + if (screen_co[0] >= 0 && screen_co[0] < vd->w && screen_co[1] >= 0 && screen_co[1] < vd->h) { + BLI_assert(vd && vd->depths); + /* we know its not clipped */ + depth = vd->depths[screen_co[1] * vd->w + screen_co[0]]; + + return ((float)uz - 0.00001f <= depth); + } + + return false; +} + +bool hair_test_vertex_inside_circle(HairViewData *viewdata, const float mval[2], float radsq, BMVert *v, float *r_dist) +{ + float (*obmat)[4] = viewdata->vc.obact->obmat; + float co_world[3]; + float dx, dy, distsq; + int screen_co[2]; + + mul_v3_m4v3(co_world, obmat, v->co); + + /* TODO, should this check V3D_PROJ_TEST_CLIP_BB too? */ + if (ED_view3d_project_int_global(viewdata->vc.ar, co_world, screen_co, V3D_PROJ_TEST_CLIP_WIN) != V3D_PROJ_RET_OK) + return false; + + dx = mval[0] - (float)screen_co[0]; + dy = mval[1] - (float)screen_co[1]; + distsq = dx * dx + dy * dy; + + if (distsq > radsq) + return false; + + if (hair_test_depth(viewdata, v->co, screen_co)) { + *r_dist = sqrtf(distsq); + return true; + } + else + return false; +} + +bool hair_test_edge_inside_circle(HairViewData *viewdata, const float mval[2], float radsq, BMVert *v1, BMVert *v2, float *r_dist, float *r_lambda) +{ + float (*obmat)[4] = viewdata->vc.obact->obmat; + float world_co1[3], world_co2[3]; + float dx, dy, distsq; + int screen_co1[2], screen_co2[2], screen_cp[2]; + float lambda, world_cp[3], screen_cpf[2], screen_co1f[2], screen_co2f[2]; + + mul_v3_m4v3(world_co1, obmat, v1->co); + mul_v3_m4v3(world_co2, obmat, v2->co); + + /* TODO, should this check V3D_PROJ_TEST_CLIP_BB too? */ + if (ED_view3d_project_int_global(viewdata->vc.ar, world_co1, screen_co1, V3D_PROJ_TEST_CLIP_WIN) != V3D_PROJ_RET_OK) + return false; + if (ED_view3d_project_int_global(viewdata->vc.ar, world_co2, screen_co2, V3D_PROJ_TEST_CLIP_WIN) != V3D_PROJ_RET_OK) + return false; + + screen_co1f[0] = screen_co1[0]; + screen_co1f[1] = screen_co1[1]; + screen_co2f[0] = screen_co2[0]; + screen_co2f[1] = screen_co2[1]; + lambda = closest_to_line_v2(screen_cpf, mval, screen_co1f, screen_co2f); + if (lambda < 0.0f || lambda > 1.0f) { + CLAMP(lambda, 0.0f, 1.0f); + interp_v2_v2v2(screen_cpf, screen_co1f, screen_co2f, lambda); + } + + dx = mval[0] - screen_cpf[0]; + dy = mval[1] - screen_cpf[1]; + distsq = dx * dx + dy * dy; + + if (distsq > radsq) + return false; + + interp_v3_v3v3(world_cp, world_co1, world_co2, lambda); + + screen_cp[0] = screen_cpf[0]; + screen_cp[1] = screen_cpf[1]; + if (hair_test_depth(viewdata, world_cp, screen_cp)) { + *r_dist = sqrtf(distsq); + *r_lambda = lambda; + return true; + } + else + return false; +} + +bool hair_test_vertex_inside_rect(HairViewData *viewdata, rcti *rect, BMVert *v) +{ + float (*obmat)[4] = viewdata->vc.obact->obmat; + float co_world[3]; + int screen_co[2]; + + mul_v3_m4v3(co_world, obmat, v->co); + + /* TODO, should this check V3D_PROJ_TEST_CLIP_BB too? */ + if (ED_view3d_project_int_global(viewdata->vc.ar, co_world, screen_co, V3D_PROJ_TEST_CLIP_WIN) != V3D_PROJ_RET_OK) + return false; + + if (!BLI_rcti_isect_pt_v(rect, screen_co)) + return false; + + if (hair_test_depth(viewdata, v->co, screen_co)) + return true; + else + return false; +} + +bool hair_test_vertex_inside_lasso(HairViewData *viewdata, const int mcoords[][2], short moves, BMVert *v) +{ + float (*obmat)[4] = viewdata->vc.obact->obmat; + float co_world[3]; + int screen_co[2]; + + mul_v3_m4v3(co_world, obmat, v->co); + + /* TODO, should this check V3D_PROJ_TEST_CLIP_BB too? */ + if (ED_view3d_project_int_global(viewdata->vc.ar, co_world, screen_co, V3D_PROJ_TEST_CLIP_WIN) != V3D_PROJ_RET_OK) + return false; + + if (!BLI_lasso_is_point_inside(mcoords, moves, screen_co[0], screen_co[1], IS_CLIPPED)) + return false; + + if (hair_test_depth(viewdata, v->co, screen_co)) + return true; + else + return false; +} + +/* ------------------------------------------------------------------------- */ + +typedef void (*VertexToolCb)(HairToolData *data, void *userdata, BMVert *v, float factor); + +/* apply tool directly to each vertex inside the filter area */ +static int UNUSED_FUNCTION(hair_tool_apply_vertex)(HairToolData *data, VertexToolCb cb, void *userdata) +{ + Scene *scene = data->scene; + Brush *brush = data->settings->brush; + const float rad = BKE_brush_size_get(scene, brush); + const float radsq = rad*rad; + const float threshold = 0.0f; /* XXX could be useful, is it needed? */ + const bool use_mirror = hair_use_mirror_x(data->ob); + + BMVert *v, *v_mirr; + BMIter iter; + int tot = 0; + float dist, factor; + + if (use_mirror) + ED_strands_mirror_cache_begin(data->edit, 0, false, false, hair_use_mirror_topology(data->ob)); + + BM_ITER_MESH(v, &iter, data->edit->bm, BM_VERTS_OF_MESH) { + if (!hair_test_vertex_inside_circle(&data->viewdata, data->mval, radsq, v, &dist)) + continue; + + factor = 1.0f - dist / rad; + if (factor > threshold) { + cb(data, userdata, v, factor); + ++tot; + + if (use_mirror) { + v_mirr = ED_strands_mirror_get(data->edit, v); + if (v_mirr) + cb(data, userdata, v_mirr, factor); + } + } + } + + /* apply mirror */ + if (use_mirror) + ED_strands_mirror_cache_end(data->edit); + + return tot; +} + +/* ------------------------------------------------------------------------- */ + +typedef void (*EdgeToolCb)(HairToolData *data, void *userdata, BMVert *v1, BMVert *v2, float factor, float edge_param); + +static int hair_tool_apply_strand_edges(HairToolData *data, EdgeToolCb cb, void *userdata, BMVert *root) +{ + Scene *scene = data->scene; + Brush *brush = data->settings->brush; + const float rad = BKE_brush_size_get(scene, brush); + const float radsq = rad*rad; + const float threshold = 0.0f; /* XXX could be useful, is it needed? */ + const bool use_mirror = hair_use_mirror_x(data->ob); + + BMVert *v, *vprev, *v_mirr, *vprev_mirr; + BMIter iter; + int k; + int tot = 0; + + BM_ITER_STRANDS_ELEM_INDEX(v, &iter, root, BM_VERTS_OF_STRAND, k) { + if (k > 0) { + float dist, lambda; + + if (hair_test_edge_inside_circle(&data->viewdata, data->mval, radsq, vprev, v, &dist, &lambda)) { + float factor = 1.0f - dist / rad; + if (factor > threshold) { + cb(data, userdata, vprev, v, factor, lambda); + ++tot; + + if (use_mirror) { + v_mirr = ED_strands_mirror_get(data->edit, v); + if (vprev_mirr && v_mirr) + cb(data, userdata, vprev_mirr, v_mirr, factor, lambda); + } + } + } + } + + vprev = v; + vprev_mirr = v_mirr; + } + + return tot; +} + +/* apply tool to vertices of edges inside the filter area, + * using the closest edge point for weighting + */ +static int hair_tool_apply_edge(HairToolData *data, EdgeToolCb cb, void *userdata) +{ + BMVert *root; + BMIter iter; + int tot = 0; + + if (hair_use_mirror_x(data->ob)) + ED_strands_mirror_cache_begin(data->edit, 0, false, false, hair_use_mirror_topology(data->ob)); + + BM_ITER_STRANDS(root, &iter, data->edit->bm, BM_STRANDS_OF_MESH) { + tot += hair_tool_apply_strand_edges(data, cb, userdata, root); + } + + /* apply mirror */ + if (hair_use_mirror_x(data->ob)) + ED_strands_mirror_cache_end(data->edit); + + return tot; +} + +/* ------------------------------------------------------------------------- */ + +typedef struct CombData { + float power; +} CombData; + +static void UNUSED_FUNCTION(hair_vertex_comb)(HairToolData *data, void *userdata, BMVert *v, float factor) +{ + CombData *combdata = userdata; + + float combfactor = powf(factor, combdata->power); + + madd_v3_v3fl(v->co, data->delta, combfactor); +} + +/* Edge-based combing tool: + * Unlike the vertex tool (which simply displaces vertices), the edge tool + * adjusts edge orientations to follow the stroke direction. + */ +static void hair_edge_comb(HairToolData *data, void *userdata, BMVert *v1, BMVert *v2, float factor, float UNUSED(edge_param)) +{ + CombData *combdata = userdata; + float strokedir[3], edge[3], edgedir[3], strokelen, edgelen; + float edge_proj[3]; + + float combfactor = powf(factor, combdata->power); + float effect; + + strokelen = normalize_v3_v3(strokedir, data->delta); + + sub_v3_v3v3(edge, v2->co, v1->co); + edgelen = normalize_v3_v3(edgedir, edge); + if (edgelen == 0.0f) + return; + + /* This factor prevents sudden changes in direction with small stroke lengths. + * The arctan maps the 0..inf range of the length ratio to 0..1 smoothly. + */ + effect = atan(strokelen / edgelen * 4.0f / (0.5f*M_PI)); + + mul_v3_v3fl(edge_proj, strokedir, edgelen); + + interp_v3_v3v3(edge, edge, edge_proj, combfactor * effect); + + add_v3_v3v3(v2->co, v1->co, edge); +} + + +BLI_INLINE void construct_m4_loc_nor_tan(float mat[4][4], const float loc[3], const float nor[3], const float tang[3]) +{ + float cotang[3]; + + cross_v3_v3v3(cotang, nor, tang); + + copy_v3_v3(mat[0], tang); + copy_v3_v3(mat[1], cotang); + copy_v3_v3(mat[2], nor); + copy_v3_v3(mat[3], loc); + mat[0][3] = 0.0f; + mat[1][3] = 0.0f; + mat[2][3] = 0.0f; + mat[3][3] = 1.0f; +} + +static void grow_hair(BMEditStrands *edit, MSurfaceSample *sample) +{ + DerivedMesh *dm = edit->root_dm; + const float len = 1.5f; + + float root_mat[4][4]; + BMVert *root, *v; + BMIter iter; + int i; + + { + float co[3], nor[3], tang[3]; + BKE_mesh_sample_eval(dm, sample, co, nor, tang); + construct_m4_loc_nor_tan(root_mat, co, nor, tang); + } + + root = BM_strands_create(edit->bm, 5, true); + + BM_elem_meshsample_data_named_set(&edit->bm->vdata, root, CD_MSURFACE_SAMPLE, CD_HAIR_ROOT_LOCATION, sample); + + BM_ITER_STRANDS_ELEM_INDEX(v, &iter, root, BM_VERTS_OF_STRAND, i) { + float co[3]; + + co[0] = co[1] = 0.0f; + co[2] = len * (float)i / (float)(len - 1); + + mul_m4_v3(root_mat, co); + + copy_v3_v3(v->co, co); + } + + BM_mesh_elem_index_ensure(edit->bm, BM_ALL); +} + +static bool hair_add_ray_cb(void *vdata, float ray_start[3], float ray_end[3]) +{ + HairToolData *data = vdata; + ViewContext *vc = &data->viewdata.vc; + + ED_view3d_win_to_segment(vc->ar, vc->v3d, data->mval, ray_start, ray_end, true); + + mul_m4_v3(data->imat, ray_start); + mul_m4_v3(data->imat, ray_end); + + return true; +} + +static bool hair_get_surface_sample(HairToolData *data, MSurfaceSample *sample) +{ + DerivedMesh *dm = data->edit->root_dm; + + MSurfaceSampleStorage dst; + int tot; + + BKE_mesh_sample_storage_single(&dst, sample); + tot = BKE_mesh_sample_generate_raycast(&dst, dm, hair_add_ray_cb, data, 1); + BKE_mesh_sample_storage_release(&dst); + + return tot > 0; +} + +static bool hair_add(HairToolData *data) +{ + MSurfaceSample sample; + + if (!hair_get_surface_sample(data, &sample)) + return false; + + grow_hair(data->edit, &sample); + + return true; +} + + +bool hair_brush_step(HairToolData *data) +{ + Brush *brush = data->settings->brush; + BrushHairTool hair_tool = brush->hair_tool; + BMEditStrands *edit = data->edit; + int tot = 0; + + switch (hair_tool) { + case HAIR_TOOL_COMB: { + CombData combdata; + combdata.power = (brush->alpha - 0.5f) * 2.0f; + if (combdata.power < 0.0f) + combdata.power = 1.0f - 9.0f * combdata.power; + else + combdata.power = 1.0f - combdata.power; + + /*tot = hair_tool_apply_vertex(data, hair_vertex_comb, &combdata);*/ /* UNUSED */ + tot = hair_tool_apply_edge(data, hair_edge_comb, &combdata); + break; + } + case HAIR_TOOL_CUT: + break; + case HAIR_TOOL_LENGTH: + break; + case HAIR_TOOL_PUFF: + break; + case HAIR_TOOL_ADD: + if (hair_add(data)) + edit->flag |= BM_STRANDS_DIRTY_SEGLEN; + break; + case HAIR_TOOL_SMOOTH: + break; + case HAIR_TOOL_WEIGHT: + break; + } + + return tot > 0; +} diff --git a/source/blender/editors/hair/hair_undo.c b/source/blender/editors/hair/hair_undo.c new file mode 100644 index 00000000000..a58b9c042f8 --- /dev/null +++ b/source/blender/editors/hair/hair_undo.c @@ -0,0 +1,190 @@ +/* + * ***** 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) Blender Foundation + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): Lukas Toenne + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/editors/hair/hair_undo.c + * \ingroup edhair + */ + +#include <stdlib.h> + +#include "MEM_guardedalloc.h" + +#include "BLI_utildefines.h" + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_object_types.h" +#include "DNA_scene_types.h" +#include "DNA_view3d_types.h" + +#include "BKE_context.h" +#include "BKE_DerivedMesh.h" +#include "BKE_editstrands.h" +#include "BKE_key.h" +#include "BKE_mesh.h" +#include "BKE_mesh_sample.h" + +#include "ED_physics.h" +#include "ED_util.h" + +#include "bmesh.h" + +#include "hair_intern.h" + +static void *strands_get_edit(bContext *C) +{ + Object *obact = CTX_data_active_object(C); + const int mode_flag = OB_MODE_HAIR_EDIT; + const bool is_mode_set = ((obact->mode & mode_flag) != 0); + + if (obact && is_mode_set) { + BMEditStrands *edit = BKE_editstrands_from_object(obact); + return edit; + } + return NULL; +} + +typedef struct UndoStrands { + Mesh me; /* Mesh supports all the customdata we need, easiest way to implement undo storage */ + int selectmode; + + /** \note + * this isn't a prefect solution, if you edit keys and change shapes this works well (fixing [#32442]), + * but editing shape keys, going into object mode, removing or changing their order, + * then go back into editmode and undo will give issues - where the old index will be out of sync + * with the new object index. + * + * There are a few ways this could be made to work but for now its a known limitation with mixing + * object and editmode operations - Campbell */ + int shapenr; +} UndoStrands; + +/* undo simply makes copies of a bmesh */ +static void *strands_edit_to_undo(void *editv, void *UNUSED(obdata)) +{ + BMEditStrands *edit = editv; +// Mesh *obme = obdata; + + UndoStrands *undo = MEM_callocN(sizeof(UndoStrands), "undo Strands"); + + /* make sure shape keys work */ +// um->me.key = obme->key ? BKE_key_copy_nolib(obme->key) : NULL; + + /* BM_mesh_validate(em->bm); */ /* for troubleshooting */ + + BM_mesh_bm_to_me_ex(edit->bm, &undo->me, CD_MASK_STRANDS, false); + + undo->selectmode = edit->bm->selectmode; + undo->shapenr = edit->bm->shapenr; + + return undo; +} + +static void strands_undo_to_edit(void *undov, void *editv, void *UNUSED(obdata)) +{ + UndoStrands *undo = undov; + BMEditStrands *edit = editv, *edit_tmp; + Object *ob = edit->ob; + DerivedMesh *dm = edit->root_dm; + BMesh *bm; +// Key *key = ((Mesh *) obdata)->key; + + const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_ME(&undo->me); + + edit->bm->shapenr = undo->shapenr; + + bm = BM_mesh_create(&allocsize); + BM_mesh_bm_from_me_ex(bm, &undo->me, CD_MASK_STRANDS_BMESH, false, false, undo->shapenr); + + /* note: have to create the new edit before freeing the old one, + * because it owns the root_dm and we have to copy it before + * it gets released when freeing the old edit. + */ + edit_tmp = BKE_editstrands_create(bm, dm, NULL); + BKE_editstrands_free(edit); + *edit = *edit_tmp; + + bm->selectmode = undo->selectmode; + edit->ob = ob; + +#if 0 + /* T35170: Restore the active key on the RealMesh. Otherwise 'fake' offset propagation happens + * if the active is a basis for any other. */ + if (key && (key->type == KEY_RELATIVE)) { + /* Since we can't add, remove or reorder keyblocks in editmode, it's safe to assume + * shapenr from restored bmesh and keyblock indices are in sync. */ + const int kb_act_idx = ob->shapenr - 1; + + /* If it is, let's patch the current mesh key block to its restored value. + * Else, the offsets won't be computed and it won't matter. */ + if (BKE_keyblock_is_basis(key, kb_act_idx)) { + KeyBlock *kb_act = BLI_findlink(&key->block, kb_act_idx); + + if (kb_act->totelem != undo->me.totvert) { + /* The current mesh has some extra/missing verts compared to the undo, adjust. */ + MEM_SAFE_FREE(kb_act->data); + kb_act->data = MEM_mallocN((size_t)(key->elemsize * bm->totvert), __func__); + kb_act->totelem = undo->me.totvert; + } + + BKE_keyblock_update_from_mesh(&undo->me, kb_act); + } + } +#endif + + ob->shapenr = undo->shapenr; + + MEM_freeN(edit_tmp); +} + +static void strands_free_undo(void *undov) +{ + UndoStrands *undo = undov; + + if (undo->me.key) { + BKE_key_free(undo->me.key); + MEM_freeN(undo->me.key); + } + + BKE_mesh_free(&undo->me, false); + MEM_freeN(undo); +} + +/* and this is all the undo system needs to know */ +void undo_push_strands(bContext *C, const char *name) +{ + /* edit->ob gets out of date and crashes on mesh undo, + * this is an easy way to ensure its OK + * though we could investigate the matter further. */ + Object *obact = CTX_data_active_object(C); + BMEditStrands *edit = BKE_editstrands_from_object(obact); + if (edit) { + edit->ob = obact; + + undo_editmode_push(C, name, CTX_data_active_object, strands_get_edit, strands_free_undo, strands_undo_to_edit, strands_edit_to_undo, NULL); + } +} diff --git a/source/blender/editors/include/BIF_glutil.h b/source/blender/editors/include/BIF_glutil.h index b904417cfcb..7292e8b171f 100644 --- a/source/blender/editors/include/BIF_glutil.h +++ b/source/blender/editors/include/BIF_glutil.h @@ -89,6 +89,9 @@ void glutil_draw_lined_arc(float start, float angle, float radius, int nsegments */ void glutil_draw_filled_arc(float start, float angle, float radius, int nsegments); + +void glutil_draw_filled_arc_part(float start, float angle, float radius, float radius_inn, int nsegments); + /** * Returns an integer value as obtained by glGetIntegerv. * The param must cause only one value to be gotten from GL. @@ -155,7 +158,8 @@ void glaDrawPixelsTex(float x, float y, int img_w, int img_h, int format, int ty * only RGBA * needs glaDefine2DArea to be set. */ -void glaDrawPixelsAuto(float x, float y, int img_w, int img_h, int format, int type, int zoomfilter, void *rect); +void glaDrawPixelsAuto(float x, float y, int img_w, int img_h, int format, + int type, int zoomfilter, float alpha, void *rect); void glaDrawPixelsTexScaled(float x, float y, int img_w, int img_h, int format, int type, int zoomfilter, void *rect, float scaleX, float scaleY); @@ -217,12 +221,12 @@ void bgl_get_mats(bglMats *mats); /* **** Color management helper functions for GLSL display/transform ***** */ /* Draw imbuf on a screen, preferably using GLSL display transform */ -void glaDrawImBuf_glsl(struct ImBuf *ibuf, float x, float y, int zoomfilter, +void glaDrawImBuf_glsl(struct ImBuf *ibuf, float x, float y, int zoomfilter, float alpha, struct ColorManagedViewSettings *view_settings, struct ColorManagedDisplaySettings *display_settings); /* Draw imbuf on a screen, preferably using GLSL display transform */ -void glaDrawImBuf_glsl_ctx(const struct bContext *C, struct ImBuf *ibuf, float x, float y, int zoomfilter); +void glaDrawImBuf_glsl_ctx(const struct bContext *C, struct ImBuf *ibuf, float x, float y, int zoomfilter, float alpha); void glaDrawBorderCorners(const struct rcti *border, float zoomx, float zoomy); diff --git a/source/blender/editors/include/ED_anim_api.h b/source/blender/editors/include/ED_anim_api.h index 0f70bf3c745..dfa667d095a 100644 --- a/source/blender/editors/include/ED_anim_api.h +++ b/source/blender/editors/include/ED_anim_api.h @@ -412,7 +412,8 @@ typedef enum eAnimChannel_Settings { ACHANNEL_SETTING_EXPAND = 3, ACHANNEL_SETTING_VISIBLE = 4, /* only for Graph Editor */ ACHANNEL_SETTING_SOLO = 5, /* only for NLA Tracks */ - ACHANNEL_SETTING_PINNED = 6 /* only for NLA Actions */ + ACHANNEL_SETTING_PINNED = 6, /* only for NLA Actions */ + ACHANNEL_SETTING_MOD_OFF = 7 } eAnimChannel_Settings; diff --git a/source/blender/editors/include/ED_datafiles.h b/source/blender/editors/include/ED_datafiles.h index 661ab58b98c..5e2c6bb8c44 100644 --- a/source/blender/editors/include/ED_datafiles.h +++ b/source/blender/editors/include/ED_datafiles.h @@ -165,6 +165,27 @@ extern char datatoc_twist_png[]; extern int datatoc_vertexdraw_png_size; extern char datatoc_vertexdraw_png[]; +extern int datatoc_haircomb_png_size; +extern char datatoc_haircomb_png[]; + +extern int datatoc_haircut_png_size; +extern char datatoc_haircut_png[]; + +extern int datatoc_hairlength_png_size; +extern char datatoc_hairlength_png[]; + +extern int datatoc_hairpuff_png_size; +extern char datatoc_hairpuff_png[]; + +extern int datatoc_hairadd_png_size; +extern char datatoc_hairadd_png[]; + +extern int datatoc_hairsmooth_png_size; +extern char datatoc_hairsmooth_png[]; + +extern int datatoc_hairweight_png_size; +extern char datatoc_hairweight_png[]; + /* Matcap files */ extern int datatoc_mc01_jpg_size; diff --git a/source/blender/editors/include/ED_keyframes_edit.h b/source/blender/editors/include/ED_keyframes_edit.h index adf82acb399..92375cbbbcf 100644 --- a/source/blender/editors/include/ED_keyframes_edit.h +++ b/source/blender/editors/include/ED_keyframes_edit.h @@ -261,7 +261,7 @@ bool delete_fcurve_keys(struct FCurve *fcu); void clear_fcurve_keys(struct FCurve *fcu); void duplicate_fcurve_keys(struct FCurve *fcu); -void clean_fcurve(struct FCurve *fcu, float thresh); +void clean_fcurve(struct bAnimContext *ac, struct bAnimListElem *ale, float thresh, bool cleardefault); void smooth_fcurve(struct FCurve *fcu); void sample_fcurve(struct FCurve *fcu); diff --git a/source/blender/editors/include/ED_mesh.h b/source/blender/editors/include/ED_mesh.h index 3f16055e762..7b66bf5cbb0 100644 --- a/source/blender/editors/include/ED_mesh.h +++ b/source/blender/editors/include/ED_mesh.h @@ -39,6 +39,7 @@ struct ID; struct View3D; struct ARegion; struct bContext; +struct bFaceMap; struct wmOperator; struct wmKeyConfig; struct ReportList; @@ -252,6 +253,10 @@ float ED_vgroup_vert_weight(struct Object *ob, struct bDeformGrou void ED_vgroup_vert_active_mirror(struct Object *ob, int def_nr); +/* object_fmap.c */ +void ED_fmap_face_add(struct Object *ob, struct bFaceMap *fmap, int facenum); +void ED_fmap_face_remove(struct Object *ob, struct bFaceMap *fmap, int facenum); + /* mesh_data.c */ // void ED_mesh_geometry_add(struct Mesh *mesh, struct ReportList *reports, int verts, int edges, int faces); void ED_mesh_polys_add(struct Mesh *mesh, struct ReportList *reports, int count); diff --git a/source/blender/editors/include/ED_particle.h b/source/blender/editors/include/ED_particle.h index 6cb8c0cfb19..26a8563bf52 100644 --- a/source/blender/editors/include/ED_particle.h +++ b/source/blender/editors/include/ED_particle.h @@ -42,6 +42,8 @@ struct Scene; /* particle edit mode */ void PE_free_ptcache_edit(struct PTCacheEdit *edit); int PE_start_edit(struct PTCacheEdit *edit); +bool PE_shapekey_load(struct Object *ob, struct ParticleSystem *psys); +bool PE_shapekey_apply(struct Object *ob, struct ParticleSystem *psys); /* access */ struct PTCacheEdit *PE_get_current(struct Scene *scene, struct Object *ob); diff --git a/source/blender/editors/include/ED_physics.h b/source/blender/editors/include/ED_physics.h index 584e9a92bb6..efa852797e1 100644 --- a/source/blender/editors/include/ED_physics.h +++ b/source/blender/editors/include/ED_physics.h @@ -35,9 +35,12 @@ struct bContext; struct ReportList; struct wmKeyConfig; +struct ViewContext; +struct rcti; struct Scene; struct Object; +struct BMEditStrands; /* particle_edit.c */ int PE_poll(struct bContext *C); @@ -56,5 +59,28 @@ void ED_rigidbody_constraint_remove(struct Scene *scene, struct Object *ob); void ED_operatortypes_physics(void); void ED_keymap_physics(struct wmKeyConfig *keyconf); +/* hair edit */ +void undo_push_strands(struct bContext *C, const char *name); + +void ED_strands_mirror_cache_begin_ex(struct BMEditStrands *edit, const int axis, + const bool use_self, const bool use_select, + const bool use_topology, float maxdist, int *r_index); +void ED_strands_mirror_cache_begin(struct BMEditStrands *edit, const int axis, + const bool use_self, const bool use_select, + const bool use_topology); +void ED_strands_mirror_apply(struct BMEditStrands *edit, const int sel_from, const int sel_to); +struct BMVert *ED_strands_mirror_get(struct BMEditStrands *edit, struct BMVert *v); +struct BMEdge *ED_strands_mirror_get_edge(struct BMEditStrands *edit, struct BMEdge *e); +void ED_strands_mirror_cache_clear(struct BMEditStrands *edit, struct BMVert *v); +void ED_strands_mirror_cache_end(struct BMEditStrands *edit); + +int ED_hair_mouse_select(struct bContext *C, const int mval[2], bool extend, bool deselect, bool toggle); +int ED_hair_border_select(struct bContext *C, struct rcti *rect, bool select, bool extend); +int ED_hair_circle_select(struct bContext *C, bool select, const int mval[2], float radius); +int ED_hair_lasso_select(struct bContext *C, const int mcoords[][2], short moves, bool extend, bool select); + +void ED_operatortypes_hair(void); +void ED_keymap_hair(struct wmKeyConfig *keyconf); + #endif /* __ED_PHYSICS_H__ */ diff --git a/source/blender/editors/include/ED_screen.h b/source/blender/editors/include/ED_screen.h index 441a9bdb1c1..1ea8a62da20 100644 --- a/source/blender/editors/include/ED_screen.h +++ b/source/blender/editors/include/ED_screen.h @@ -68,9 +68,15 @@ void ED_region_toggle_hidden(struct bContext *C, struct ARegion *ar); void ED_region_info_draw(struct ARegion *ar, const char *text, int block, float fill_color[4]); void ED_region_image_metadata_draw(int x, int y, struct ImBuf *ibuf, rctf frame, float zoomx, float zoomy); void ED_region_grid_draw(struct ARegion *ar, float zoomx, float zoomy); +void ED_region_draw_backdrop_view3d(const struct bContext *C, struct Object *camera, const float alpha, + const float width, const float height, const float x, const float y, + const float zoomx, const float zoomy, const bool draw_background); float ED_region_blend_factor(struct ARegion *ar); void ED_region_visible_rect(struct ARegion *ar, struct rcti *rect); +int ED_match_area_with_refresh(int spacetype, int refresh); +int ED_match_region_with_redraws(int spacetype, int regiontype, int redraws); + /* spaces */ void ED_spacetypes_keymap(struct wmKeyConfig *keyconf); @@ -121,6 +127,7 @@ void ED_update_for_newframe(struct Main *bmain, struct Scene *scene, int mute void ED_refresh_viewport_fps(struct bContext *C); int ED_screen_animation_play(struct bContext *C, int sync, int mode); bScreen *ED_screen_animation_playing(const struct wmWindowManager *wm); +bScreen *ED_screen_animation_no_scrub(const struct wmWindowManager *wm); /* screen keymaps */ void ED_operatortypes_screen(void); diff --git a/source/blender/editors/include/ED_space_api.h b/source/blender/editors/include/ED_space_api.h index d268c578cf2..fc812e01659 100644 --- a/source/blender/editors/include/ED_space_api.h +++ b/source/blender/editors/include/ED_space_api.h @@ -36,6 +36,7 @@ struct bContext; void ED_spacetypes_init(void); void ED_spacemacros_init(void); +void ED_spacedropwidgets_init(void); /* the pluginnable API for export to editors */ diff --git a/source/blender/editors/include/ED_transform.h b/source/blender/editors/include/ED_transform.h index 478cb927011..8382428ffa6 100644 --- a/source/blender/editors/include/ED_transform.h +++ b/source/blender/editors/include/ED_transform.h @@ -35,14 +35,18 @@ /* ******************* Registration Function ********************** */ struct ARegion; +struct EnumPropertyItem; struct ListBase; struct Object; struct View3D; struct bContext; +struct uiLayout; struct wmEvent; struct wmKeyConfig; struct wmKeyMap; struct wmOperatorType; +struct wmWindowManager; +struct PointerRNA; void transform_keymap_for_space(struct wmKeyConfig *keyconf, struct wmKeyMap *keymap, int spaceid); void transform_operatortypes(void); @@ -106,9 +110,12 @@ enum TfmMode { bool calculateTransformCenter(struct bContext *C, int centerMode, float cent3d[3], float cent2d[2]); struct TransInfo; +struct ScrArea; struct Base; struct Scene; struct Object; +struct wmWidget; +struct wmWidgetGroup; /* UNUSED */ // int BIF_snappingSupported(struct Object *obedit); @@ -150,9 +157,32 @@ void Transform_Properties(struct wmOperatorType *ot, int flags); /* view3d manipulators */ -int BIF_do_manipulator(struct bContext *C, const struct wmEvent *event, struct wmOperator *op); -void BIF_draw_manipulator(const struct bContext *C); +/* +typedef struct ManipulatorGroup { + struct wmWidget *translate_x; + struct wmWidget *translate_y; + struct wmWidget *translate_z; + + struct wmWidget *rotate_x; + struct wmWidget *rotate_y; + struct wmWidget *rotate_z; +} ManipulatorGroup; + +int WIDGET_manipulator_handler(struct bContext *C, const struct wmEvent *event, struct wmWidget *widget, struct wmOperator *ptr); +int WIDGET_manipulator_handler_trans(struct bContext *C, const struct wmEvent *event, struct wmWidget *widget, struct PointerRNA *ptr); +int WIDGET_manipulator_handler_rot(struct bContext *C, const struct wmEvent *event, struct wmWidget *widget, struct PointerRNA *ptr); + +void WIDGET_manipulator_render_3d_intersect(const struct bContext *C, struct wmWidget *widget, int selectionbase); +void WIDGET_manipulator_draw(struct wmWidget *widget, const struct bContext *C); +bool WIDGETGROUP_manipulator_poll(struct wmWidgetGroup *wgroup, const struct bContext *C); +void WIDGETGROUP_manipulator_update(struct wmWidgetGroup *wgroup, const struct bContext *C); +void WIDGETGROUP_manipulator_free(struct wmWidgetGroup *wgroup); +void WIDGETGROUP_manipulator_create(struct wmWidgetGroup *wgroup); +*/ + +void BIF_draw_manipulator(const struct bContext *C); +int BIF_do_manipulator(struct bContext *C, const struct wmEvent *event, struct wmOperator *op); /* Snapping */ diff --git a/source/blender/editors/include/ED_util.h b/source/blender/editors/include/ED_util.h index 496ce7f2c60..c46780a975a 100644 --- a/source/blender/editors/include/ED_util.h +++ b/source/blender/editors/include/ED_util.h @@ -32,6 +32,8 @@ #define __ED_UTIL_H__ struct bContext; +struct PackedFile; +struct SpaceLink; struct wmOperator; struct wmOperatorType; @@ -66,6 +68,7 @@ bool ED_undo_is_valid(const struct bContext *C, const char *undoname); /* undo_editmode.c */ void undo_editmode_push(struct bContext *C, const char *name, + struct Object *(*get_object)(const struct bContext * C), void * (*getdata)(struct bContext *C), void (*freedata)(void *), void (*to_editmode)(void *, void *, void *), diff --git a/source/blender/editors/include/ED_view3d.h b/source/blender/editors/include/ED_view3d.h index be4204e7cb7..b84d4099d46 100644 --- a/source/blender/editors/include/ED_view3d.h +++ b/source/blender/editors/include/ED_view3d.h @@ -41,6 +41,7 @@ struct Base; struct BezTriple; struct BoundBox; struct EditBone; +struct wmEvent; struct ImBuf; struct MVert; struct Main; @@ -62,6 +63,9 @@ struct wmOperator; struct wmOperatorType; struct wmWindow; struct wmWindowManager; +struct wmWidget; +struct wmWidgetGroup; +struct wmWidgetGroupType; struct GPUFX; struct GPUOffScreen; struct GPUFXSettings; @@ -319,6 +323,8 @@ void ED_view3d_check_mats_rv3d(struct RegionView3D *rv3d); #endif int ED_view3d_scene_layer_set(int lay, const int *values, int *active); +void ED_draw_object_facemap(struct Scene *scene, struct Object *ob, int facemap); + bool ED_view3d_context_activate(struct bContext *C); void ED_view3d_draw_offscreen_init(struct Scene *scene, struct View3D *v3d); void ED_view3d_draw_offscreen( @@ -387,6 +393,9 @@ void ED_view3d_operator_properties_viewmat_set(struct bContext *C, struct wmOper void ED_view3d_operator_properties_viewmat_get(struct wmOperator *op, int *winx, int *winy, float persmat[4][4]); #endif +int WIDGETGROUP_lamp_poll(const struct bContext *C, struct wmWidgetGroupType *wgrouptype); +void WIDGETGROUP_lamp_draw(const struct bContext *C, struct wmWidgetGroup *wgroup); + /* render */ void ED_view3d_stop_render_preview(struct wmWindowManager *wm, struct ARegion *ar); void ED_view3d_shade_update(struct Main *bmain, struct Scene *scene, struct View3D *v3d, struct ScrArea *sa); diff --git a/source/blender/editors/include/UI_icons.h b/source/blender/editors/include/UI_icons.h index bfb7a3420c9..35d5f264ae7 100644 --- a/source/blender/editors/include/UI_icons.h +++ b/source/blender/editors/include/UI_icons.h @@ -979,6 +979,13 @@ DEF_ICON(BRUSH_TEXMASK) DEF_ICON(BRUSH_THUMB) DEF_ICON(BRUSH_ROTATE) DEF_ICON(BRUSH_VERTEXDRAW) +DEF_ICON(BRUSH_HAIR_COMB) +DEF_ICON(BRUSH_HAIR_CUT) +DEF_ICON(BRUSH_HAIR_LENGTH) +DEF_ICON(BRUSH_HAIR_PUFF) +DEF_ICON(BRUSH_HAIR_ADD) +DEF_ICON(BRUSH_HAIR_SMOOTH) +DEF_ICON(BRUSH_HAIR_WEIGHT) /* Matcaps */ DEF_ICON(MATCAP_01) diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h index 1976d9953f9..73da39356d1 100644 --- a/source/blender/editors/include/UI_interface.h +++ b/source/blender/editors/include/UI_interface.h @@ -68,9 +68,12 @@ struct ImBuf; struct bNodeTree; struct bNode; struct bNodeSocket; +struct CacheLibrary; struct wmDropBox; struct wmDrag; struct wmEvent; +struct wmWidget; +struct wmWidgetGroup; typedef struct uiBut uiBut; typedef struct uiBlock uiBlock; @@ -179,6 +182,7 @@ enum { UI_BUT_TIP_FORCE = (1 << 28), /* force show tooltips when holding option/alt if U's USER_TOOLTIPS is off */ UI_BUT_TEXTEDIT_UPDATE = (1 << 29), /* when widget is in textedit mode, update value on each char stroke */ UI_BUT_SEARCH_UNLINK = (1 << 30), /* show unlink for search button */ + UI_BUT_MENU_ROOT = (1 << 31), /* but is root of a menu */ }; #define UI_PANEL_WIDTH 340 @@ -917,6 +921,8 @@ void uiTemplateReportsBanner(uiLayout *layout, struct bContext *C); void uiTemplateKeymapItemProperties(uiLayout *layout, struct PointerRNA *ptr); void uiTemplateComponentMenu(uiLayout *layout, struct PointerRNA *ptr, const char *propname, const char *name); void uiTemplateNodeSocket(uiLayout *layout, struct bContext *C, float *color); +uiLayout *uiTemplateCacheLibraryItem(uiLayout *layout, struct bContext *C, struct CacheLibrary *cachelib, + struct Object *ob, int type, int index, int enabled); /* Default UIList class name, keep in sync with its declaration in bl_ui/__init__.py */ #define UI_UL_DEFAULT_CLASS_NAME "UI_UL_list" diff --git a/source/blender/editors/interface/interface_draw.c b/source/blender/editors/interface/interface_draw.c index e2504bbc743..3f0154f8cc3 100644 --- a/source/blender/editors/interface/interface_draw.c +++ b/source/blender/editors/interface/interface_draw.c @@ -436,7 +436,7 @@ void ui_draw_but_IMAGE(ARegion *UNUSED(ar), uiBut *but, uiWidgetColors *UNUSED(w float facy = (float)h / (float)ibuf->y; glPixelZoom(facx, facy); } - glaDrawPixelsAuto((float)rect->xmin, (float)rect->ymin, ibuf->x, ibuf->y, GL_RGBA, GL_UNSIGNED_BYTE, GL_NEAREST, ibuf->rect); + glaDrawPixelsAuto((float)rect->xmin, (float)rect->ymin, ibuf->x, ibuf->y, GL_RGBA, GL_UNSIGNED_BYTE, GL_NEAREST, 1.0f, ibuf->rect); glPixelZoom(1.0f, 1.0f); diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c index 59a06b3113c..01296385cd3 100644 --- a/source/blender/editors/interface/interface_handlers.c +++ b/source/blender/editors/interface/interface_handlers.c @@ -3502,6 +3502,8 @@ static void ui_block_open_begin(bContext *C, uiBut *but, uiHandleButtonData *dat data->menu->popup = but->block->handle->popup; } + but->flag |= UI_BUT_MENU_ROOT; + #ifdef USE_ALLSELECT { wmWindow *win = CTX_wm_window(C); @@ -6448,6 +6450,33 @@ void ui_panel_menu(bContext *C, ARegion *ar, Panel *pa) UI_popup_menu_end(C, pup); } +static void ui_menu_scripting(bContext *UNUSED(C), uiLayout *layout, void *arg) +{ + uiBut *but = arg; + + if (!ui_block_is_menu(but->block)) { + uiItemFullO(layout, "UI_OT_editsource", NULL, ICON_NONE, NULL, WM_OP_INVOKE_DEFAULT, 0); + } + + uiItemBooleanO(layout, CTX_IFACE_(BLF_I18NCONTEXT_OPERATOR_DEFAULT, "Copy Data Path"), + ICON_NONE, "UI_OT_copy_data_path_button", "full", 0); + uiItemBooleanO(layout, CTX_IFACE_(BLF_I18NCONTEXT_OPERATOR_DEFAULT, "Copy Full Data Path"), + ICON_NONE, "UI_OT_copy_data_path_button", "full", 1); + + if (but->rnapoin.data && but->rnaprop) { + PointerRNA ptr_props; + char buf[512]; + + BLI_snprintf(buf, sizeof(buf), "%s.%s", + RNA_struct_identifier(but->rnapoin.type), RNA_property_identifier(but->rnaprop)); + + WM_operator_properties_create(&ptr_props, "WM_OT_doc_view"); + RNA_string_set(&ptr_props, "doc_id", buf); + uiItemFullO(layout, "WM_OT_doc_view", CTX_IFACE_(BLF_I18NCONTEXT_OPERATOR_DEFAULT, "Online Python Reference"), + ICON_NONE, ptr_props.data, WM_OP_EXEC_DEFAULT, 0); + } +} + static bool ui_but_menu(bContext *C, uiBut *but) { uiPopupMenu *pup; @@ -6462,7 +6491,9 @@ static bool ui_but_menu(bContext *C, uiBut *but) if (but->type == UI_BTYPE_IMAGE) { return false; } - + + but->flag |= UI_BUT_MENU_ROOT; + UI_but_tooltip_timer_remove(C, but); /* highly unlikely getting the label ever fails */ @@ -6632,15 +6663,16 @@ static bool ui_but_menu(bContext *C, uiBut *but) uiItemO(layout, CTX_IFACE_(BLF_I18NCONTEXT_OPERATOR_DEFAULT, "Unset"), ICON_NONE, "UI_OT_unset_property_button"); } - - uiItemO(layout, CTX_IFACE_(BLF_I18NCONTEXT_OPERATOR_DEFAULT, "Copy Data Path"), - ICON_NONE, "UI_OT_copy_data_path_button"); uiItemO(layout, CTX_IFACE_(BLF_I18NCONTEXT_OPERATOR_DEFAULT, "Copy To Selected"), ICON_NONE, "UI_OT_copy_to_selected_button"); uiItemS(layout); } + /* Scripting submenu */ + uiItemMenuF(layout, IFACE_("Scripting"), ICON_NONE, ui_menu_scripting, but); + uiItemS(layout); + /* Operator buttons */ if (but->optype) { uiBlock *block = uiLayoutGetBlock(layout); @@ -6689,17 +6721,12 @@ static bool ui_but_menu(bContext *C, uiBut *but) { /* Docs */ char buf[512]; - PointerRNA ptr_props; + // PointerRNA ptr_props; if (UI_but_online_manual_id(but, buf, sizeof(buf))) { uiItemO(layout, CTX_IFACE_(BLF_I18NCONTEXT_OPERATOR_DEFAULT, "Online Manual"), ICON_NONE, "WM_OT_doc_view_manual_ui_context"); - WM_operator_properties_create(&ptr_props, "WM_OT_doc_view"); - RNA_string_set(&ptr_props, "doc_id", buf); - uiItemFullO(layout, "WM_OT_doc_view", CTX_IFACE_(BLF_I18NCONTEXT_OPERATOR_DEFAULT, "Online Python Reference"), - ICON_NONE, ptr_props.data, WM_OP_EXEC_DEFAULT, 0); - /* XXX inactive option, not for public! */ #if 0 WM_operator_properties_create(&ptr_props, "WM_OT_doc_edit"); diff --git a/source/blender/editors/interface/interface_icons.c b/source/blender/editors/interface/interface_icons.c index ef246c5ac1f..626dbbb6f08 100644 --- a/source/blender/editors/interface/interface_icons.c +++ b/source/blender/editors/interface/interface_icons.c @@ -515,6 +515,13 @@ static void init_brush_icons(void) INIT_BRUSH_ICON(ICON_BRUSH_THUMB, thumb); INIT_BRUSH_ICON(ICON_BRUSH_ROTATE, twist); INIT_BRUSH_ICON(ICON_BRUSH_VERTEXDRAW, vertexdraw); + INIT_BRUSH_ICON(ICON_BRUSH_HAIR_COMB, haircomb); + INIT_BRUSH_ICON(ICON_BRUSH_HAIR_CUT, haircut); + INIT_BRUSH_ICON(ICON_BRUSH_HAIR_LENGTH, hairlength); + INIT_BRUSH_ICON(ICON_BRUSH_HAIR_PUFF, hairpuff); + INIT_BRUSH_ICON(ICON_BRUSH_HAIR_ADD, hairadd); + INIT_BRUSH_ICON(ICON_BRUSH_HAIR_SMOOTH, hairsmooth); + INIT_BRUSH_ICON(ICON_BRUSH_HAIR_WEIGHT, hairweight); #undef INIT_BRUSH_ICON } @@ -1302,6 +1309,8 @@ static int ui_id_brush_get_icon(const bContext *C, ID *id) mode = OB_MODE_VERTEX_PAINT; else if (ob->mode & OB_MODE_TEXTURE_PAINT) mode = OB_MODE_TEXTURE_PAINT; + else if (ob->mode & OB_MODE_HAIR_EDIT) + mode = OB_MODE_HAIR_EDIT; } else if ((sima = CTX_wm_space_image(C)) && (sima->mode == SI_MODE_PAINT)) @@ -1322,6 +1331,10 @@ static int ui_id_brush_get_icon(const bContext *C, ID *id) items = brush_image_tool_items; tool = br->imagepaint_tool; } + else if (mode == OB_MODE_HAIR_EDIT) { + items = brush_hair_tool_items; + tool = br->hair_tool; + } if (!items || !RNA_enum_icon_from_value(items, tool, &id->icon_id)) id->icon_id = 0; diff --git a/source/blender/editors/interface/interface_intern.h b/source/blender/editors/interface/interface_intern.h index 9461547a164..dd1937500ec 100644 --- a/source/blender/editors/interface/interface_intern.h +++ b/source/blender/editors/interface/interface_intern.h @@ -633,6 +633,7 @@ extern int ui_but_menu_direction(uiBut *but); extern void ui_but_text_password_hide(char password_str[UI_MAX_DRAW_STR], uiBut *but, const bool restore); extern uiBut *ui_but_find_select_in_enum(uiBut *but, int direction); extern uiBut *ui_but_find_active_in_region(struct ARegion *ar); +extern uiBut *ui_but_find_menu_root(struct bContext *C); bool ui_but_is_editable(const uiBut *but); bool ui_but_is_editable_as_text(const uiBut *but); void ui_but_pie_dir_visual(RadialDirection dir, float vec[2]); @@ -713,4 +714,8 @@ void UI_OT_eyedropper_color(struct wmOperatorType *ot); void UI_OT_eyedropper_id(struct wmOperatorType *ot); void UI_OT_eyedropper_depth(struct wmOperatorType *ot); + +/* interface_generic_widgets.c */ +void UI_OT_lamp_position(struct wmOperatorType *ot); + #endif /* __INTERFACE_INTERN_H__ */ diff --git a/source/blender/editors/interface/interface_ops.c b/source/blender/editors/interface/interface_ops.c index 23b20591275..88da91acc36 100644 --- a/source/blender/editors/interface/interface_ops.c +++ b/source/blender/editors/interface/interface_ops.c @@ -94,15 +94,15 @@ static void UI_OT_reset_default_theme(wmOperatorType *ot) static int copy_data_path_button_poll(bContext *C) { - PointerRNA ptr; - PropertyRNA *prop; - char *path; - int index; + uiBut *but = ui_but_find_menu_root(C); - UI_context_active_but_prop_get(C, &ptr, &prop, &index); + /* fallback to find but->active */ + if (but == NULL) { + but = UI_context_active_but_get(C); + } - if (ptr.id.data && ptr.data && prop) { - path = RNA_path_from_ID_to_property(&ptr, prop); + if (but && but->rnapoin.data) { + char *path = RNA_path_from_ID_to_property(&but->rnapoin, but->rnaprop); if (path) { MEM_freeN(path); @@ -113,19 +113,20 @@ static int copy_data_path_button_poll(bContext *C) return 0; } -static int copy_data_path_button_exec(bContext *C, wmOperator *UNUSED(op)) +static int copy_data_path_button_exec(bContext *C, wmOperator *op) { - PointerRNA ptr; - PropertyRNA *prop; - char *path; - int index; + uiBut *but = ui_but_find_menu_root(C); - /* try to create driver using property retrieved from UI */ - UI_context_active_but_prop_get(C, &ptr, &prop, &index); + /* fallback to find but->active */ + if (but == NULL) { + but = UI_context_active_but_get(C); + } + + if (but && but->rnapoin.data) { + const bool full = RNA_boolean_get(op->ptr, "full"); + char *path = full ? RNA_path_full_property_py(&but->rnapoin, but->rnaprop, -1) : + RNA_path_from_ID_to_property(&but->rnapoin, but->rnaprop); - if (ptr.id.data && ptr.data && prop) { - path = RNA_path_from_ID_to_property(&ptr, prop); - if (path) { WM_clipboard_text_set(path, false); MEM_freeN(path); @@ -149,6 +150,9 @@ static void UI_OT_copy_data_path_button(wmOperatorType *ot) /* flags */ ot->flag = OPTYPE_REGISTER; + + /* properties */ + RNA_def_boolean(ot->srna, "full", 0, "Full", "Copy the full RNA data path for this property to the clipboard"); } /* Reset to Default Values Button Operator ------------------------ */ diff --git a/source/blender/editors/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c index c3b58e2d1c1..5fcfbd253a2 100644 --- a/source/blender/editors/interface/interface_templates.c +++ b/source/blender/editors/interface/interface_templates.c @@ -31,6 +31,7 @@ #include "MEM_guardedalloc.h" +#include "DNA_cache_library_types.h" #include "DNA_node_types.h" #include "DNA_scene_types.h" #include "DNA_object_types.h" @@ -49,6 +50,7 @@ #include "BLF_api.h" #include "BLF_translation.h" +#include "BKE_cache_library.h" #include "BKE_colortools.h" #include "BKE_context.h" #include "BKE_depsgraph.h" @@ -73,6 +75,7 @@ #include "ED_util.h" #include "RNA_access.h" +#include "RNA_enum_types.h" #include "WM_api.h" #include "WM_types.h" @@ -357,6 +360,7 @@ static const char *template_id_browse_tip(StructRNA *type) case ID_MSK: return N_("Browse Mask to be linked"); case ID_PAL: return N_("Browse Palette Data to be linked"); case ID_PC: return N_("Browse Paint Curve Data to be linked"); + case ID_CL: return N_("Browse Cache Library Data to be linked"); } } return N_("Browse ID data to be linked"); @@ -3703,3 +3707,51 @@ void uiTemplateNodeSocket(uiLayout *layout, bContext *UNUSED(C), float *color) UI_block_align_end(block); } + +/************************* Cache Library Item **************************/ + +static int cache_item_indent(int type) +{ + switch (type) { + case CACHE_TYPE_OBJECT: + return 0; + + default: + return 1; + } +} + +uiLayout *uiTemplateCacheLibraryItem(uiLayout *layout, bContext *UNUSED(C), CacheLibrary *UNUSED(cachelib), + Object *ob, int datatype, int index, int enabled) +{ + uiLayout *split, *row, *col; + int i; + char name[2*MAX_NAME]; + int icon = ICON_NONE; + + row = uiLayoutRow(layout, false); + uiLayoutSetEnabled(row, enabled); + uiLayoutSetAlignment(row, UI_LAYOUT_ALIGN_LEFT); + + split = uiLayoutSplit(row, 0.0f, false); + + for (i = 0; i < cache_item_indent(datatype); ++i) + uiItemL(split, "", ICON_NONE); + + col = uiLayoutColumn(split, false); + + BKE_cache_item_name(ob, datatype, index, name); + RNA_enum_icon_from_value(cache_library_data_type_items, datatype, &icon); + uiItemL(split, name, icon); + + /* display read result */ + split = uiLayoutSplit(row, 0.9f, false); + // XXX TODO store cache read results in a hash table and display them here +// if (item && (item->flag & CACHE_ITEM_ENABLED)) +// RNA_enum_icon_from_value(cache_library_read_result_items, item->read_result, &icon); +// else +// icon = ICON_NONE; +// uiItemL(split, NULL, icon); + + return col; +} diff --git a/source/blender/editors/interface/interface_utils.c b/source/blender/editors/interface/interface_utils.c index 0823238fcb1..576e17727dd 100644 --- a/source/blender/editors/interface/interface_utils.c +++ b/source/blender/editors/interface/interface_utils.c @@ -43,6 +43,7 @@ #include "BLF_translation.h" +#include "BKE_context.h" #include "BKE_report.h" #include "MEM_guardedalloc.h" @@ -313,6 +314,25 @@ int UI_calc_float_precision(int prec, double value) return prec; } +uiBut *ui_but_find_menu_root(struct bContext *C) { + ScrArea *sa = CTX_wm_area(C); + ARegion *ar; + uiBlock *block; + uiBut *but; + + for (ar = sa->regionbase.first; ar; ar = ar->next) { + for (block = ar->uiblocks.first; block; block = block->next) { + for (but = block->buttons.first; but; but = but->next) { + if (but->flag & UI_BUT_MENU_ROOT) { + return but; + } + } + } + } + + return NULL; +} + bool UI_but_online_manual_id(const uiBut *but, char *r_str, size_t maxlength) { if (but->rnapoin.id.data && but->rnapoin.data && but->rnaprop) { diff --git a/source/blender/editors/interface/view2d.c b/source/blender/editors/interface/view2d.c index 4f4b5ab07ff..37b14318c16 100644 --- a/source/blender/editors/interface/view2d.c +++ b/source/blender/editors/interface/view2d.c @@ -1061,7 +1061,7 @@ static void view2d_map_cur_using_mask(View2D *v2d, rctf *curmasked) } } -/* Set view matrices to use 'cur' rect as viewing frame for View2D drawing */ +/* Set view matrices to use 'cur' rect as viewing frame for View2D drawing, return y/x aspect ratio */ void UI_view2d_view_ortho(View2D *v2d) { rctf curmasked; diff --git a/source/blender/editors/io/CMakeLists.txt b/source/blender/editors/io/CMakeLists.txt index f331dea5c5c..f8e4738ab38 100644 --- a/source/blender/editors/io/CMakeLists.txt +++ b/source/blender/editors/io/CMakeLists.txt @@ -26,8 +26,10 @@ set(INC ../../bmesh ../../makesdna ../../makesrna + ../../pointcache ../../windowmanager ../../collada + ../../../../intern/guardedalloc ) set(INC_SYS @@ -35,9 +37,12 @@ set(INC_SYS ) set(SRC + io_cache_library.c + io_cache_shapekey.c io_collada.c io_ops.c + io_cache_library.h io_collada.h io_ops.h ) diff --git a/source/blender/editors/io/SConscript b/source/blender/editors/io/SConscript index 0facb24e2c3..d1826784547 100644 --- a/source/blender/editors/io/SConscript +++ b/source/blender/editors/io/SConscript @@ -36,10 +36,13 @@ incs = [ '../../blenfont', '../../blenkernel', '../../blenlib', + '../../bmesh', '../../collada', + '../../makesdna', '../../makesrna', + '../../pointcache', '../../windowmanager', - '../../bmesh../../makesdna', + '../../../../intern/guardedalloc', ] if env['WITH_BF_COLLADA']: diff --git a/source/blender/editors/io/io_cache_library.c b/source/blender/editors/io/io_cache_library.c new file mode 100644 index 00000000000..f7076ae0cae --- /dev/null +++ b/source/blender/editors/io/io_cache_library.c @@ -0,0 +1,961 @@ +/* + * ***** 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2015 Blender Foundation. + * All rights reserved. + * + * Contributor(s): Blender Foundation + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/editors/io/io_cache_library.c + * \ingroup editor/io + */ + +#include <string.h> + +#include "MEM_guardedalloc.h" + +#include "BLF_translation.h" + +#include "BLI_blenlib.h" +#include "BLI_fileops.h" +#include "BLI_listbase.h" +#include "BLI_math.h" +#include "BLI_path_util.h" +#include "BLI_utildefines.h" + +#include "DNA_cache_library_types.h" +#include "DNA_group_types.h" +#include "DNA_listBase.h" +#include "DNA_modifier_types.h" +#include "DNA_object_force.h" +#include "DNA_object_types.h" +#include "DNA_particle_types.h" + +#include "BKE_anim.h" +#include "BKE_blender.h" +#include "BKE_depsgraph.h" +#include "BKE_cache_library.h" +#include "BKE_context.h" +#include "BKE_global.h" +#include "BKE_idprop.h" +#include "BKE_library.h" +#include "BKE_main.h" +#include "BKE_report.h" +#include "BKE_scene.h" +#include "BKE_screen.h" + +#include "ED_screen.h" + +#include "RNA_access.h" +#include "RNA_define.h" +#include "RNA_enum_types.h" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "io_cache_library.h" + +#include "PTC_api.h" + +static int ED_cache_library_active_object_poll(bContext *C) +{ + Object *ob = CTX_data_active_object(C); + if (!(ob && (ob->transflag & OB_DUPLIGROUP) && ob->dup_group && ob->cache_library)) + return false; + + return true; +} + +static int ED_cache_modifier_poll(bContext *C) +{ + if (!ED_cache_library_active_object_poll(C)) + return false; + if (!CTX_data_pointer_get_type(C, "cache_modifier", &RNA_CacheLibraryModifier).data) + return false; + + return true; +} + +/********************** new cache library operator *********************/ + +static int new_cachelib_exec(bContext *C, wmOperator *UNUSED(op)) +{ + Object *ob = CTX_data_active_object(C); + CacheLibrary *cachelib = ob->cache_library; + Main *bmain = CTX_data_main(C); + PointerRNA ptr, idptr; + PropertyRNA *prop; + + /* add or copy material */ + if (cachelib) { + cachelib = BKE_cache_library_copy(cachelib); + } + else { + cachelib = BKE_cache_library_add(bmain, DATA_("CacheLibrary")); + } + + /* enable fake user by default */ + cachelib->id.flag |= LIB_FAKEUSER; + + /* hook into UI */ + UI_context_active_but_prop_get_templateID(C, &ptr, &prop); + + if (prop) { + /* when creating new ID blocks, use is already 1, but RNA + * pointer se also increases user, so this compensates it */ + cachelib->id.us--; + + RNA_id_pointer_create(&cachelib->id, &idptr); + RNA_property_pointer_set(&ptr, prop, idptr); + RNA_property_update(C, &ptr, prop); + } + + WM_event_add_notifier(C, NC_SCENE, cachelib); + + return OPERATOR_FINISHED; +} + +void CACHELIBRARY_OT_new(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "New Cache Library"; + ot->idname = "CACHELIBRARY_OT_new"; + ot->description = "Add a new cache library"; + + /* api callbacks */ + ot->poll = ED_operator_object_active; + ot->exec = new_cachelib_exec; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; +} + +/********************** delete cache library operator *********************/ + +static int cache_library_delete_exec(bContext *C, wmOperator *UNUSED(op)) +{ + Main *bmain = CTX_data_main(C); + Object *ob = CTX_data_active_object(C); + CacheLibrary *cachelib = ob->cache_library; + + BKE_cache_library_unlink(cachelib); + BKE_libblock_free(bmain, cachelib); + + WM_event_add_notifier(C, NC_SCENE, cachelib); + + return OPERATOR_FINISHED; +} + +void CACHELIBRARY_OT_delete(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Delete Cache Library"; + ot->idname = "CACHELIBRARY_OT_delete"; + ot->description = "Delete a cache library data block"; + + /* api callbacks */ + ot->exec = cache_library_delete_exec; + ot->invoke = WM_operator_confirm; + ot->poll = ED_cache_library_active_object_poll; + + /* flags */ + ot->flag = OPTYPE_UNDO; +} + +/********************** bake cache operator *********************/ + +typedef enum eCacheLibraryBake_EvalMode { + CACHELIBRARY_BAKE_PREVIEW = (1 << 0), /* evaluate data with preview settings */ + CACHELIBRARY_BAKE_RENDER = (1 << 1), /* evaluate data with render settings */ +} eCacheLibraryBake_EvalMode; + +static int cache_library_bake_poll(bContext *C) +{ + Object *ob = CTX_data_active_object(C); + + if (!ob || !(ob->transflag & OB_DUPLIGROUP) || !ob->dup_group || !ob->cache_library) + return false; + /* disable when the result is not displayed, just to avoid confusing situations */ + if (ob->cache_library->display_mode != CACHE_LIBRARY_DISPLAY_RESULT) + return false; + + return true; +} + +typedef struct CacheLibraryBakeJob { + short *stop, *do_update; + float *progress; + + struct Main *bmain; + struct Scene *scene; + struct CacheLibrary *cachelib; + int lay; + float mat[4][4]; + struct Group *group; + + eCacheLibraryBake_EvalMode eval_mode; + EvaluationContext eval_ctx; + + struct PTCWriterArchive *archive; + struct PTCWriter *writer; + + int start_frame, end_frame; + int origfra; /* original frame to reset scene after export */ + float origframelen; /* original frame length to reset scene after export */ +} CacheLibraryBakeJob; + +static bool cache_library_bake_stop(CacheLibraryBakeJob *data) +{ + return (*data->stop) || G.is_break; +} + +static void cache_library_bake_set_progress(CacheLibraryBakeJob *data, float progress) +{ + *data->do_update = 1; + *data->progress = progress; +} + +static void cache_library_bake_set_particle_baking(Main *bmain, bool baking) +{ + /* XXX would be nicer to just loop over scene->base here, + * but this does not catch all objects included in dupli groups ... + */ + Object *ob; + for (ob = bmain->object.first; ob; ob = ob->id.next) { + ParticleSystem *psys; + + for (psys = ob->particlesystem.first; psys; psys = psys->next) { + if (baking) + psys->pointcache->flag |= PTCACHE_BAKING; + else + psys->pointcache->flag &= ~PTCACHE_BAKING; + } + } +} + +static void cache_library_bake_do(CacheLibraryBakeJob *data, bool use_render) +{ + Scene *scene = data->scene; + int frame, frame_prev, start_frame, end_frame; + CacheProcessData process_data; + + if (cache_library_bake_stop(data)) + return; + + /* === prepare === */ + + process_data.lay = data->lay; + copy_m4_m4(process_data.mat, data->mat); + process_data.dupcache = BKE_dupli_cache_new(); + + switch (data->cachelib->source_mode) { + case CACHE_LIBRARY_SOURCE_SCENE: + data->writer = PTC_writer_dupligroup(data->group->id.name, &data->eval_ctx, scene, data->group, data->cachelib); + break; + case CACHE_LIBRARY_SOURCE_CACHE: + data->writer = PTC_writer_duplicache(data->group->id.name, data->group, process_data.dupcache, data->cachelib->data_types, G.debug & G_DEBUG_SIMDATA); + break; + } + if (!data->writer) { + BKE_dupli_cache_free(process_data.dupcache); + return; + } + + data->cachelib->flag |= CACHE_LIBRARY_BAKING; + + PTC_writer_init(data->writer, data->archive); + + start_frame = data->start_frame; + end_frame = data->end_frame; + + /* === frame loop === */ + + cache_library_bake_set_progress(data, 0.0f); + for (frame = frame_prev = start_frame; frame <= end_frame; frame_prev = frame++) { + + const bool init_strands = (frame == start_frame); + + printf("Bake Cache '%s' | Frame %d\n", data->group->id.name+2, frame); + + /* XXX Ugly, but necessary to avoid particle caching of paths when not needed. + * This takes a lot of time, but is only needed in the first frame. + */ + cache_library_bake_set_particle_baking(data->bmain, !init_strands); + + scene->r.cfra = frame; + BKE_scene_update_group_for_newframe(&data->eval_ctx, data->bmain, scene, data->group, scene->lay); + + switch (data->cachelib->source_mode) { + case CACHE_LIBRARY_SOURCE_SCENE: + BKE_dupli_cache_from_group(scene, data->group, data->cachelib, process_data.dupcache, &data->eval_ctx, init_strands); + break; + case CACHE_LIBRARY_SOURCE_CACHE: + BKE_cache_read_dupli_cache(data->cachelib, process_data.dupcache, scene, data->group, frame, use_render, false); + break; + } + + BKE_cache_process_dupli_cache(data->cachelib, &process_data, scene, data->group, frame_prev, frame, true, false, true); + + PTC_write_sample(data->writer); + + cache_library_bake_set_progress(data, (float)(frame - start_frame + 1) / (float)(end_frame - start_frame + 1)); + if (cache_library_bake_stop(data)) + break; + } + + /* === cleanup === */ + + if (data->writer) { + PTC_writer_free(data->writer); + data->writer = NULL; + } + + data->cachelib->flag &= ~CACHE_LIBRARY_BAKING; + cache_library_bake_set_particle_baking(data->bmain, false); + + BKE_dupli_cache_free(process_data.dupcache); +} + +/* Warning! Deletes existing files if possible, operator should show confirm dialog! */ +static bool cache_library_bake_ensure_file_target(const char *filename) +{ + if (BLI_exists(filename)) { + if (BLI_is_dir(filename)) { + return false; + } + else if (BLI_is_file(filename)) { + if (BLI_file_is_writable(filename)) { + /* returns 0 on success */ + return (BLI_delete(filename, false, false) == 0); + } + else { + return false; + } + } + else { + return false; + } + } + return true; +} + +static void cache_library_bake_start(void *customdata, short *stop, short *do_update, float *progress) +{ + CacheLibraryBakeJob *data = (CacheLibraryBakeJob *)customdata; + const bool do_preview = data->eval_mode & CACHELIBRARY_BAKE_PREVIEW; + const bool do_render = data->eval_mode & CACHELIBRARY_BAKE_RENDER; + const PTCArchiveResolution archive_res = (do_preview ? PTC_RESOLUTION_PREVIEW : 0) | (do_render ? PTC_RESOLUTION_RENDER : 0); + Scene *scene = data->scene; + char filename[FILE_MAX]; + char app_name[MAX_NAME]; + IDProperty *metadata; + + data->stop = stop; + data->do_update = do_update; + data->progress = progress; + + data->origfra = scene->r.cfra; + data->origframelen = scene->r.framelen; + scene->r.framelen = 1.0f; + + BKE_cache_archive_output_path(data->cachelib, filename, sizeof(filename)); + BLI_snprintf(app_name, sizeof(app_name), "Blender %s", versionstr); + + metadata = BKE_cache_library_get_output_metadata(data->cachelib, false); + + data->archive = PTC_open_writer_archive(FPS, data->start_frame, filename, archive_res, app_name, data->cachelib->description, NULL, metadata); + + if (data->archive) { + + G.is_break = false; + + if (do_preview) { + data->eval_ctx.mode = DAG_EVAL_VIEWPORT; + PTC_writer_archive_use_render(data->archive, false); + cache_library_bake_do(data, false); + } + + if (do_render) { + data->eval_ctx.mode = DAG_EVAL_RENDER; + PTC_writer_archive_use_render(data->archive, true); + cache_library_bake_do(data, true); + } + + } + + *do_update = true; + *stop = 0; +} + +static void cache_library_bake_end(void *customdata) +{ + CacheLibraryBakeJob *data = (CacheLibraryBakeJob *)customdata; + Scene *scene = data->scene; + + G.is_rendering = false; + BKE_spacedata_draw_locks(false); + + if (data->writer) + PTC_writer_free(data->writer); + if (data->archive) + PTC_close_writer_archive(data->archive); + + /* reset scene frame */ + scene->r.cfra = data->origfra; + scene->r.framelen = data->origframelen; + BKE_scene_update_for_newframe(&data->eval_ctx, data->bmain, scene, scene->lay); +} + +static void cache_library_bake_init(CacheLibraryBakeJob *data, bContext *C, wmOperator *op) +{ + Object *ob = CTX_data_active_object(C); + CacheLibrary *cachelib = ob->cache_library; + Main *bmain = CTX_data_main(C); + Scene *scene = CTX_data_scene(C); + + /* make sure we can write */ + char filename[FILE_MAX]; + BKE_cache_archive_output_path(cachelib, filename, sizeof(filename)); + cache_library_bake_ensure_file_target(filename); + + /* XXX annoying hack: needed to prevent data corruption when changing + * scene frame in separate threads + */ + G.is_rendering = true; + + BKE_spacedata_draw_locks(true); + + /* setup data */ + data->bmain = bmain; + data->scene = scene; + data->cachelib = cachelib; + data->lay = ob->lay; + copy_m4_m4(data->mat, ob->obmat); + data->group = ob->dup_group; + + data->eval_mode = RNA_enum_get(op->ptr, "eval_mode"); + + if (RNA_struct_property_is_set(op->ptr, "start_frame")) + data->start_frame = RNA_int_get(op->ptr, "start_frame"); + else + data->start_frame = scene->r.sfra; + if (RNA_struct_property_is_set(op->ptr, "end_frame")) + data->end_frame = RNA_int_get(op->ptr, "end_frame"); + else + data->end_frame = scene->r.efra; +} + +static void cache_library_bake_freejob(void *customdata) +{ + CacheLibraryBakeJob *data= (CacheLibraryBakeJob *)customdata; + MEM_freeN(data); +} + +static int cache_library_bake_exec(bContext *C, wmOperator *op) +{ + const bool use_job = RNA_boolean_get(op->ptr, "use_job"); + + if (use_job) { + /* when running through invoke, run as a job */ + CacheLibraryBakeJob *data; + wmJob *wm_job; + + /* XXX set WM_JOB_EXCL_RENDER to prevent conflicts with render jobs, + * since we need to set G.is_rendering + */ + wm_job = WM_jobs_get(CTX_wm_manager(C), CTX_wm_window(C), CTX_data_scene(C), "Cache Library Bake", + WM_JOB_PROGRESS | WM_JOB_EXCL_RENDER, WM_JOB_TYPE_CACHELIBRARY_BAKE); + + /* setup data */ + data = MEM_callocN(sizeof(CacheLibraryBakeJob), "Cache Library Bake Job"); + cache_library_bake_init(data, C, op); + + WM_jobs_customdata_set(wm_job, data, cache_library_bake_freejob); + WM_jobs_timer(wm_job, 0.1, NC_SCENE|ND_FRAME, NC_SCENE|ND_FRAME); + WM_jobs_callbacks(wm_job, cache_library_bake_start, NULL, NULL, cache_library_bake_end); + + WM_jobs_start(CTX_wm_manager(C), wm_job); + WM_cursor_wait(0); + + /* add modal handler for ESC */ + WM_event_add_modal_handler(C, op); + + return OPERATOR_RUNNING_MODAL; + } + else { + /* in direct execution mode we run this operator blocking instead of using a job */ + CacheLibraryBakeJob data; + short stop = false, do_update = false; + float progress = 0.0f; + + cache_library_bake_init(&data, C, op); + + cache_library_bake_start(&data, &stop, &do_update, &progress); + cache_library_bake_end(&data); + + return OPERATOR_FINISHED; + } +} + +static int cache_library_bake_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) +{ + Object *ob = CTX_data_active_object(C); + CacheLibrary *cachelib = ob->cache_library; + + char filename[FILE_MAX]; + + if (!cachelib) + return OPERATOR_CANCELLED; + + /* make sure we run a job when exec is called after confirm popup */ + RNA_boolean_set(op->ptr, "use_job", true); + + BKE_cache_archive_output_path(cachelib, filename, sizeof(filename)); + + if (!BKE_cache_archive_path_test(cachelib, cachelib->output_filepath)) { + BKE_reportf(op->reports, RPT_ERROR, "Cannot create file path for cache library %200s", cachelib->id.name+2); + return OPERATOR_CANCELLED; + } + + if (BLI_exists(filename)) { + if (BLI_is_dir(filename)) { + BKE_reportf(op->reports, RPT_ERROR, "Cache Library target is a directory: %200s", filename); + return OPERATOR_CANCELLED; + } + else if (BLI_is_file(filename)) { + if (BLI_file_is_writable(filename)) { + return WM_operator_confirm_message(C, op, "Overwrite?"); + } + else { + BKE_reportf(op->reports, RPT_ERROR, "Cannot overwrite Cache Library target: %200s", filename); + return OPERATOR_CANCELLED; + } + + } + else { + BKE_reportf(op->reports, RPT_ERROR, "Invalid Cache Library target: %200s", filename); + return OPERATOR_CANCELLED; + } + } + else { + return cache_library_bake_exec(C, op); + } +} + +/* catch esc */ +static int cache_library_bake_modal(bContext *C, wmOperator *UNUSED(op), const wmEvent *event) +{ + /* no running job, remove handler and pass through */ + if (!WM_jobs_test(CTX_wm_manager(C), CTX_data_scene(C), WM_JOB_TYPE_CACHELIBRARY_BAKE)) + return OPERATOR_FINISHED | OPERATOR_PASS_THROUGH; + + /* running bake */ + switch (event->type) { + case ESCKEY: + return OPERATOR_RUNNING_MODAL; + } + return OPERATOR_PASS_THROUGH; +} + +void CACHELIBRARY_OT_bake(wmOperatorType *ot) +{ + PropertyRNA *prop; + + static EnumPropertyItem eval_mode_items[] = { + {CACHELIBRARY_BAKE_PREVIEW, "PREVIEW", ICON_RESTRICT_VIEW_OFF, "Preview", "Evaluate data with preview settings"}, + {CACHELIBRARY_BAKE_RENDER, "RENDER", ICON_RESTRICT_RENDER_OFF, "Render", "Evaluate data with render settings"}, + {0, NULL, 0, NULL, NULL} + }; + + /* identifiers */ + ot->name = "Bake"; + ot->description = "Bake cache library"; + ot->idname = "CACHELIBRARY_OT_bake"; + + /* api callbacks */ + ot->invoke = cache_library_bake_invoke; + ot->exec = cache_library_bake_exec; + ot->modal = cache_library_bake_modal; + ot->poll = cache_library_bake_poll; + + /* flags */ + /* no undo for this operator, cannot restore old cache files anyway */ + ot->flag = OPTYPE_REGISTER; + + prop = RNA_def_boolean(ot->srna, "use_job", false, "Use Job", "Run operator as a job"); + /* This is in internal property set by the invoke function. + * It allows the exec function to be called from both the confirm popup + * as well as a direct exec call for running a blocking operator in background mode. + */ + RNA_def_property_flag(prop, PROP_HIDDEN); + + prop = RNA_def_enum(ot->srna, "eval_mode", eval_mode_items, CACHELIBRARY_BAKE_RENDER, "Evaluation Mode", "Mode to use when evaluating data"); + RNA_def_property_flag(prop, PROP_ENUM_FLAG); + + RNA_def_int(ot->srna, "start_frame", 0, INT_MIN, INT_MAX, "Start Frame", "First frame to be cached", INT_MIN, INT_MAX); + RNA_def_int(ot->srna, "end_frame", 0, INT_MIN, INT_MAX, "End Frame", "Last frame to be cached", INT_MIN, INT_MAX); +} + +/* ========================================================================= */ + +static int cache_library_archive_slice_exec(bContext *C, wmOperator *op) +{ + Object *ob = CTX_data_active_object(C); + CacheLibrary *cachelib = ob->cache_library; + Scene *scene = CTX_data_scene(C); + + const int start_frame = RNA_int_get(op->ptr, "start_frame"); + const int end_frame = RNA_int_get(op->ptr, "end_frame"); + + char input_filepath[FILE_MAX], input_filename[FILE_MAX]; + char output_filepath[FILE_MAX], output_filename[FILE_MAX]; + struct PTCReaderArchive *input_archive; + struct PTCWriterArchive *output_archive; + PTCArchiveResolution archive_res; + CacheArchiveInfo info; + IDProperty *metadata; + + RNA_string_get(op->ptr, "input_filepath", input_filepath); + if (input_filepath[0] == '\0') + return OPERATOR_CANCELLED; + RNA_string_get(op->ptr, "output_filepath", output_filepath); + if (output_filepath[0] == '\0') + return OPERATOR_CANCELLED; + + BKE_cache_archive_path_ex(input_filepath, cachelib->id.lib, NULL, input_filename, sizeof(input_filename)); + BKE_cache_archive_path_ex(output_filepath, cachelib->id.lib, NULL, output_filename, sizeof(output_filename)); + + /* make sure we can write */ + cache_library_bake_ensure_file_target(output_filename); + + input_archive = PTC_open_reader_archive(scene, input_filename); + if (!input_archive) { + BKE_reportf(op->reports, RPT_ERROR, "Cannot open cache file at '%s'", input_filepath); + return OPERATOR_CANCELLED; + } + + archive_res = PTC_reader_archive_get_resolutions(input_archive); + { + IDPropertyTemplate val; + val.i = 0; + metadata = IDP_New(IDP_GROUP, &val, "cache input metadata"); + } + PTC_get_archive_info(input_archive, &info, metadata); + + output_archive = PTC_open_writer_archive(FPS, start_frame, output_filename, archive_res, info.app_name, info.description, NULL, metadata); + + IDP_FreeProperty(metadata); + MEM_freeN(metadata); + + if (!output_archive) { + BKE_reportf(op->reports, RPT_ERROR, "Cannot write to cache file at '%s'", output_filepath); + return OPERATOR_CANCELLED; + } + + PTC_archive_slice(input_archive, output_archive, start_frame, end_frame); + + PTC_close_reader_archive(input_archive); + PTC_close_writer_archive(output_archive); + + return OPERATOR_FINISHED; +} + +static int cache_library_archive_slice_invoke(bContext *C, wmOperator *op, const wmEvent *event) +{ + return WM_operator_props_popup_confirm(C, op, event); + +#if 0 + Object *ob = CTX_data_active_object(C); + CacheLibrary *cachelib = ob->cache_library; + + char output_filename[FILE_MAX]; + + if (!cachelib) + return OPERATOR_CANCELLED; + + /* make sure we run a job when exec is called after confirm popup */ + RNA_boolean_set(op->ptr, "use_job", true); + + RNA_string_get(op->ptr, "output_filepath", output_filepath); + if (output_filepath[0] == '\0') + return OPERATOR_CANCELLED; + BKE_cache_archive_output_path(cachelib, output_filename, sizeof(output_filename)); + + if (!BKE_cache_archive_path_test(cachelib, output_filepath)) { + BKE_reportf(op->reports, RPT_ERROR, "Cannot create file path for cache library %200s", cachelib->id.name+2); + return OPERATOR_CANCELLED; + } + + if (BLI_exists(output_filename)) { + if (BLI_is_dir(output_filename)) { + BKE_reportf(op->reports, RPT_ERROR, "Cache Library target is a directory: %200s", output_filename); + return OPERATOR_CANCELLED; + } + else if (BLI_is_file(output_filename)) { + if (BLI_file_is_writable(output_filename)) { + return WM_operator_confirm_message(C, op, "Overwrite?"); + } + else { + BKE_reportf(op->reports, RPT_ERROR, "Cannot overwrite Cache Library target: %200s", output_filename); + return OPERATOR_CANCELLED; + } + + } + else { + BKE_reportf(op->reports, RPT_ERROR, "Invalid Cache Library target: %200s", output_filename); + return OPERATOR_CANCELLED; + } + } + else { + return cache_library_bake_exec(C, op); + } +#endif +} + +void CACHELIBRARY_OT_archive_slice(wmOperatorType *ot) +{ + PropertyRNA *prop; + + /* identifiers */ + ot->name = "Archive Slice"; + ot->description = "Copy a range of frames to a new cache archive"; + ot->idname = "CACHELIBRARY_OT_archive_slice"; + + /* api callbacks */ + ot->exec = cache_library_archive_slice_exec; + ot->invoke = cache_library_archive_slice_invoke; + ot->poll = ED_cache_library_active_object_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + prop = RNA_def_boolean(ot->srna, "use_job", false, "Use Job", "Run operator as a job"); + /* This is in internal property set by the invoke function. + * It allows the exec function to be called from both the confirm popup + * as well as a direct exec call for running a blocking operator in background mode. + */ + RNA_def_property_flag(prop, PROP_HIDDEN); + + prop = RNA_def_string(ot->srna, "input_filepath", NULL, FILE_MAX, "Input File Path", "Path to the source cache archive"); + RNA_def_property_subtype(prop, PROP_FILEPATH); + prop = RNA_def_string(ot->srna, "output_filepath", NULL, FILE_MAX, "Output File Path", "Path to the target cache archive"); + RNA_def_property_subtype(prop, PROP_FILEPATH); + RNA_def_int(ot->srna, "start_frame", 1, INT_MIN, INT_MAX, "Start Frame", "First frame to copy", 1, 10000); + RNA_def_int(ot->srna, "end_frame", 250, INT_MIN, INT_MAX, "End Frame", "Last frame to copy", 1, 10000); +} + +/* ========================================================================= */ + +#if 0 +static void ui_item_nlabel(uiLayout *layout, const char *s, size_t len) +{ + char buf[256]; + + BLI_strncpy(buf, s, sizeof(buf)-1); + buf[min_ii(len, sizeof(buf)-1)] = '\0'; + + uiItemL(layout, buf, ICON_NONE); +} + +static void archive_info_labels(uiLayout *layout, const char *info) +{ + const char delim[] = {'\n', '\0'}; + const char *cur = info; + size_t linelen; + char *sep, *suf; + + linelen = BLI_str_partition(cur, delim, &sep, &suf); + while (sep) { + ui_item_nlabel(layout, cur, linelen); + cur = suf; + + linelen = BLI_str_partition(cur, delim, &sep, &suf); + } + ui_item_nlabel(layout, cur, linelen); +} + +static uiBlock *archive_info_popup_create(bContext *C, ARegion *ar, void *arg) +{ + const char *info = arg; + uiBlock *block; + uiLayout *layout; + + block = UI_block_begin(C, ar, "_popup", UI_EMBOSS); + UI_block_flag_disable(block, UI_BLOCK_LOOP); + UI_block_flag_enable(block, UI_BLOCK_KEEP_OPEN | UI_BLOCK_MOVEMOUSE_QUIT); + + layout = UI_block_layout(block, UI_LAYOUT_VERTICAL, UI_LAYOUT_PANEL, 0, 0, UI_UNIT_X * 20, UI_UNIT_Y, 0, UI_style_get()); + + archive_info_labels(layout, info); + + UI_block_bounds_set_centered(block, 0); + UI_block_direction_set(block, UI_DIR_DOWN); + + return block; +} +#endif + +static void print_stream(void *UNUSED(userdata), const char *s) +{ + printf("%s", s); +} + +static int cache_library_archive_info_exec(bContext *C, wmOperator *op) +{ + Object *ob = CTX_data_active_object(C); + CacheLibrary *cachelib = ob->cache_library; + Scene *scene = CTX_data_scene(C); + + const bool use_cache_info = RNA_boolean_get(op->ptr, "use_cache_info"); + const bool calc_bytes_size = RNA_boolean_get(op->ptr, "calc_bytes_size"); + const bool use_stdout = RNA_boolean_get(op->ptr, "use_stdout"); + const bool use_popup = RNA_boolean_get(op->ptr, "use_popup"); + const bool use_clipboard = RNA_boolean_get(op->ptr, "use_clipboard"); + + char filepath[FILE_MAX], filename[FILE_MAX]; + struct PTCReaderArchive *archive; + + RNA_string_get(op->ptr, "filepath", filepath); + if (filepath[0] == '\0') + return OPERATOR_CANCELLED; + + BKE_cache_archive_path_ex(filepath, cachelib->id.lib, NULL, filename, sizeof(filename)); + archive = PTC_open_reader_archive(scene, filename); + if (!archive) { + BKE_reportf(op->reports, RPT_ERROR, "Cannot open cache file at '%s'", filepath); + return OPERATOR_CANCELLED; + } + + if (use_cache_info) { + if (cachelib->archive_info) + BKE_cache_archive_info_clear(cachelib->archive_info); + else + cachelib->archive_info = BKE_cache_archive_info_new(); + + BLI_strncpy(cachelib->archive_info->filepath, filename, sizeof(cachelib->archive_info->filepath)); + + PTC_get_archive_info_nodes(archive, cachelib->archive_info, calc_bytes_size); + } + + if (use_stdout) { + PTC_get_archive_info_stream(archive, print_stream, NULL); + } + + if (use_popup) { +// UI_popup_block_invoke(C, archive_info_popup_create, info); + } + + if (use_clipboard) { +// WM_clipboard_text_set(info, false); + } + + PTC_close_reader_archive(archive); + + return OPERATOR_FINISHED; +} + +void CACHELIBRARY_OT_archive_info(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Archive Info"; + ot->description = "Get archive details from a cache library archive"; + ot->idname = "CACHELIBRARY_OT_archive_info"; + + /* api callbacks */ + ot->exec = cache_library_archive_info_exec; + ot->poll = ED_cache_library_active_object_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + RNA_def_string(ot->srna, "filepath", NULL, FILE_MAX, "File Path", "Path to the cache archive"); + RNA_def_boolean(ot->srna, "use_cache_info", false, "Use Cache Library Info", "Store info in the cache library"); + RNA_def_boolean(ot->srna, "calc_bytes_size", false, "Calculate Size", "Calculate overall size of nodes in bytes (can take a while)"); + RNA_def_boolean(ot->srna, "use_stdout", false, "Use stdout", "Print info in standard output"); + RNA_def_boolean(ot->srna, "use_popup", false, "Show Popup", "Display archive info in a popup"); + RNA_def_boolean(ot->srna, "use_clipboard", false, "Copy to Clipboard", "Copy archive info to the clipboard"); +} + +/* ------------------------------------------------------------------------- */ +/* Cache Modifiers */ + +static int cache_library_add_modifier_exec(bContext *C, wmOperator *op) +{ + Object *ob = CTX_data_active_object(C); + CacheLibrary *cachelib = ob->cache_library; + + eCacheModifier_Type type = RNA_enum_get(op->ptr, "type"); + if (type == eCacheModifierType_None) { + return OPERATOR_CANCELLED; + } + + BKE_cache_modifier_add(cachelib, NULL, type); + + return OPERATOR_FINISHED; +} + +void CACHELIBRARY_OT_add_modifier(struct wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Add Cache Modifier"; + ot->description = "Add a cache modifier"; + ot->idname = "CACHELIBRARY_OT_add_modifier"; + + /* api callbacks */ + ot->exec = cache_library_add_modifier_exec; + ot->poll = ED_cache_library_active_object_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + RNA_def_enum(ot->srna, "type", cache_modifier_type_items, eCacheModifierType_None, "Type", "Type of modifier to add"); +} + +static int cache_library_remove_modifier_exec(bContext *C, wmOperator *UNUSED(op)) +{ + PointerRNA md_ptr = CTX_data_pointer_get_type(C, "cache_modifier", &RNA_CacheLibraryModifier); + CacheModifier *md = md_ptr.data; + CacheLibrary *cachelib = md_ptr.id.data; + + if (!md) + return OPERATOR_CANCELLED; + + BKE_cache_modifier_remove(cachelib, md); + + return OPERATOR_FINISHED; +} + +void CACHELIBRARY_OT_remove_modifier(struct wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Remove Cache Modifier"; + ot->description = "Remove a cache modifier"; + ot->idname = "CACHELIBRARY_OT_remove_modifier"; + + /* api callbacks */ + ot->exec = cache_library_remove_modifier_exec; + ot->poll = ED_cache_modifier_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} diff --git a/source/blender/editors/io/io_cache_library.h b/source/blender/editors/io/io_cache_library.h new file mode 100644 index 00000000000..78959e6900c --- /dev/null +++ b/source/blender/editors/io/io_cache_library.h @@ -0,0 +1,56 @@ +/* + * ***** 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2015 Blender Foundation. + * All rights reserved. + * + * Contributor(s): Blender Foundation + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/editors/io/io_cache_library.h + * \ingroup editor/io + */ + +#ifndef __IO_CACHE_LIBRARY_H__ +#define __IO_CACHE_LIBRARY_H__ + +struct wmOperatorType; + +void CACHELIBRARY_OT_new(struct wmOperatorType *ot); +void CACHELIBRARY_OT_delete(struct wmOperatorType *ot); + +void CACHELIBRARY_OT_bake(struct wmOperatorType *ot); + +void CACHELIBRARY_OT_archive_info(struct wmOperatorType *ot); +void CACHELIBRARY_OT_archive_slice(struct wmOperatorType *ot); + +/* ------------------------------------------------------------------------- */ +/* Cache Modifiers */ + +void CACHELIBRARY_OT_add_modifier(struct wmOperatorType *ot); +void CACHELIBRARY_OT_remove_modifier(struct wmOperatorType *ot); + +/* cache_shapekey.c */ +void CACHELIBRARY_OT_shape_key_add(struct wmOperatorType *ot); +void CACHELIBRARY_OT_shape_key_remove(struct wmOperatorType *ot); +void CACHELIBRARY_OT_shape_key_clear(struct wmOperatorType *ot); +void CACHELIBRARY_OT_shape_key_retime(struct wmOperatorType *ot); +void CACHELIBRARY_OT_shape_key_move(struct wmOperatorType *ot); + +#endif diff --git a/source/blender/editors/io/io_cache_shapekey.c b/source/blender/editors/io/io_cache_shapekey.c new file mode 100644 index 00000000000..e28a16b8e55 --- /dev/null +++ b/source/blender/editors/io/io_cache_shapekey.c @@ -0,0 +1,417 @@ +/* + * ***** 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2015 Blender Foundation. + * All rights reserved. + * + * Contributor(s): Blender Foundation + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/editors/io/io_cache_shapekey.c + * \ingroup editor/io + */ + + +#include <math.h> +#include <string.h> + +#include "MEM_guardedalloc.h" + +#include "BLI_blenlib.h" +#include "BLI_math.h" +#include "BLI_utildefines.h" + +#include "DNA_cache_library_types.h" +#include "DNA_key_types.h" +#include "DNA_object_types.h" +#include "DNA_scene_types.h" +#include "DNA_strands_types.h" + +#include "BKE_cache_library.h" +#include "BKE_context.h" +#include "BKE_depsgraph.h" +#include "BKE_key.h" +#include "BKE_library.h" +#include "BKE_main.h" +#include "BKE_object.h" + +#include "RNA_access.h" +#include "RNA_define.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "io_cache_library.h" + +/*********************** add shape key ***********************/ + +static void ED_cache_shape_key_add(bContext *C, StrandsKeyCacheModifier *skmd, Strands *strands, const bool from_mix) +{ + KeyBlock *kb; + if ((kb = BKE_cache_modifier_strands_key_insert_key(skmd, strands, NULL, from_mix))) { + Key *key = skmd->key; + /* for absolute shape keys, new keys may not be added last */ + skmd->shapenr = BLI_findindex(&key->block, kb) + 1; + + WM_event_add_notifier(C, NC_WINDOW, NULL); + } +} + +/*********************** remove shape key ***********************/ + +static void keyblock_free(StrandsKeyCacheModifier *skmd, KeyBlock *kb) +{ + Key *key = skmd->key; + + BLI_remlink(&key->block, kb); + key->totkey--; + + if (kb->data) MEM_freeN(kb->data); + MEM_freeN(kb); +} + +static bool ED_cache_shape_key_remove_all(StrandsKeyCacheModifier *skmd, Strands *UNUSED(strands)) +{ + Key *key = skmd->key; + KeyBlock *kb, *kb_next; + + if (key == NULL) + return false; + + for (kb = key->block.first; kb; kb = kb_next) { + kb_next = kb->next; + + keyblock_free(skmd, kb); + } + + key->refkey = NULL; + skmd->shapenr = 0; + + return true; +} + +static bool ED_cache_shape_key_remove(StrandsKeyCacheModifier *skmd, Strands *strands) +{ + Key *key = skmd->key; + KeyBlock *kb, *rkb; + + if (key == NULL) + return false; + + kb = BLI_findlink(&key->block, skmd->shapenr - 1); + if (kb) { + for (rkb = key->block.first; rkb; rkb = rkb->next) { + if (rkb->relative == skmd->shapenr - 1) { + /* remap to the 'Basis' */ + rkb->relative = 0; + } + else if (rkb->relative >= skmd->shapenr) { + /* Fix positional shift of the keys when kb is deleted from the list */ + rkb->relative -= 1; + } + } + + keyblock_free(skmd, kb); + + if (key->refkey == kb) { + key->refkey = key->block.first; + + if (key->refkey) { + /* apply new basis key on original data */ + BKE_keyblock_convert_to_strands(key->refkey, strands, skmd->flag & eStrandsKeyCacheModifier_Flag_UseMotionState); + } + } + + if (skmd->shapenr > 1) { + skmd->shapenr--; + } + } + + return true; +} + +/********************** shape key operators *********************/ + +static bool shape_key_get_context(bContext *C, CacheLibrary **r_cachelib, StrandsKeyCacheModifier **r_skmd, Strands **r_strands) +{ + CacheLibrary *cachelib = CTX_data_pointer_get_type(C, "cache_library", &RNA_CacheLibrary).data; + CacheModifier *md = CTX_data_pointer_get_type(C, "cache_modifier", &RNA_CacheLibraryModifier).data; + StrandsKeyCacheModifier *skmd; + Object *ob = CTX_data_active_object(C); + Strands *strands; + + if (!(cachelib && !cachelib->id.lib && md && md->type == eCacheModifierType_StrandsKey)) + return false; + skmd = (StrandsKeyCacheModifier *)md; + + if (!(ob && ob->dup_cache && (ob->transflag & OB_DUPLIGROUP) && ob->dup_group)) + return false; + if (!BKE_cache_modifier_find_strands(ob->dup_cache, skmd->object, skmd->hair_system, NULL, &strands, NULL, NULL)) + return false; + + if (r_cachelib) *r_cachelib = cachelib; + if (r_skmd) *r_skmd = skmd; + if (r_strands) *r_strands = strands; + return true; +} + +static int shape_key_poll(bContext *C) +{ + return shape_key_get_context(C, NULL, NULL, NULL); +} + +static int shape_key_exists_poll(bContext *C) +{ + StrandsKeyCacheModifier *skmd; + + if (!shape_key_get_context(C, NULL, &skmd, NULL)) + return false; + + return (skmd->key && skmd->shapenr >= 0 && skmd->shapenr < skmd->key->totkey); +} + +static int shape_key_move_poll(bContext *C) +{ + StrandsKeyCacheModifier *skmd; + + if (!shape_key_get_context(C, NULL, &skmd, NULL)) + return false; + + return (skmd->key != NULL && skmd->key->totkey > 1); +} + +static int shape_key_add_exec(bContext *C, wmOperator *op) +{ + const bool from_mix = RNA_boolean_get(op->ptr, "from_mix"); + CacheLibrary *cachelib; + StrandsKeyCacheModifier *skmd; + Strands *strands; + + shape_key_get_context(C, &cachelib, &skmd, &strands); + + ED_cache_shape_key_add(C, skmd, strands, from_mix); + + DAG_id_tag_update(&cachelib->id, OB_RECALC_DATA); + + return OPERATOR_FINISHED; +} + +void CACHELIBRARY_OT_shape_key_add(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Add Shape Key"; + ot->idname = "CACHELIBRARY_OT_shape_key_add"; + ot->description = "Add shape key to the object"; + + /* api callbacks */ + ot->poll = shape_key_poll; + ot->exec = shape_key_add_exec; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* properties */ + RNA_def_boolean(ot->srna, "from_mix", true, "From Mix", "Create the new shape key from the existing mix of keys"); +} + +static int shape_key_remove_exec(bContext *C, wmOperator *op) +{ + CacheLibrary *cachelib; + StrandsKeyCacheModifier *skmd; + Strands *strands; + bool changed = false; + + shape_key_get_context(C, &cachelib, &skmd, &strands); + + if (RNA_boolean_get(op->ptr, "all")) { + changed = ED_cache_shape_key_remove_all(skmd, strands); + } + else { + changed = ED_cache_shape_key_remove(skmd, strands); + } + + if (changed) { + DAG_id_tag_update(&cachelib->id, OB_RECALC_DATA); + WM_event_add_notifier(C, NC_WINDOW, NULL); + + return OPERATOR_FINISHED; + } + else { + return OPERATOR_CANCELLED; + } +} + +void CACHELIBRARY_OT_shape_key_remove(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Remove Shape Key"; + ot->idname = "CACHELIBRARY_OT_shape_key_remove"; + ot->description = "Remove shape key from the object"; + + /* api callbacks */ + ot->poll = shape_key_exists_poll; + ot->exec = shape_key_remove_exec; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* properties */ + RNA_def_boolean(ot->srna, "all", 0, "All", "Remove all shape keys"); +} + +static int shape_key_clear_exec(bContext *C, wmOperator *UNUSED(op)) +{ + CacheLibrary *cachelib; + StrandsKeyCacheModifier *skmd; + Strands *strands; + KeyBlock *kb; + + shape_key_get_context(C, &cachelib, &skmd, &strands); + + for (kb = skmd->key->block.first; kb; kb = kb->next) + kb->curval = 0.0f; + + DAG_id_tag_update(&cachelib->id, OB_RECALC_DATA); + WM_event_add_notifier(C, NC_WINDOW, NULL); + + return OPERATOR_FINISHED; +} + +void CACHELIBRARY_OT_shape_key_clear(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Clear Shape Keys"; + ot->description = "Clear weights for all shape keys"; + ot->idname = "CACHELIBRARY_OT_shape_key_clear"; + + /* api callbacks */ + ot->poll = shape_key_poll; + ot->exec = shape_key_clear_exec; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +/* starting point and step size could be optional */ +static int shape_key_retime_exec(bContext *C, wmOperator *UNUSED(op)) +{ + CacheLibrary *cachelib; + StrandsKeyCacheModifier *skmd; + Strands *strands; + KeyBlock *kb; + float cfra = 0.0f; + + shape_key_get_context(C, &cachelib, &skmd, &strands); + + for (kb = skmd->key->block.first; kb; kb = kb->next) + kb->pos = (cfra += 0.1f); + + DAG_id_tag_update(&cachelib->id, OB_RECALC_DATA); + WM_event_add_notifier(C, NC_WINDOW, NULL); + + return OPERATOR_FINISHED; +} + +void CACHELIBRARY_OT_shape_key_retime(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Re-Time Shape Keys"; + ot->description = "Resets the timing for absolute shape keys"; + ot->idname = "CACHELIBRARY_OT_shape_key_retime"; + + /* api callbacks */ + ot->poll = shape_key_poll; + ot->exec = shape_key_retime_exec; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + + +enum { + KB_MOVE_TOP = -2, + KB_MOVE_UP = -1, + KB_MOVE_DOWN = 1, + KB_MOVE_BOTTOM = 2, +}; + +static int shape_key_move_exec(bContext *C, wmOperator *op) +{ + const int type = RNA_enum_get(op->ptr, "type"); + CacheLibrary *cachelib; + StrandsKeyCacheModifier *skmd; + Strands *strands; + Key *key; + int totkey, act_index, new_index; + + shape_key_get_context(C, &cachelib, &skmd, &strands); + key = skmd->key; + totkey = key->totkey; + act_index = skmd->shapenr - 1; + + switch (type) { + case KB_MOVE_TOP: + /* Replace the ref key only if we're at the top already (only for relative keys) */ + new_index = (ELEM(act_index, 0, 1) || key->type == KEY_NORMAL) ? 0 : 1; + break; + case KB_MOVE_BOTTOM: + new_index = totkey - 1; + break; + case KB_MOVE_UP: + case KB_MOVE_DOWN: + default: + new_index = (totkey + act_index + type) % totkey; + break; + } + + if (!BKE_keyblock_move_ex(key, &skmd->shapenr, act_index, new_index)) { + return OPERATOR_CANCELLED; + } + + DAG_id_tag_update(&cachelib->id, OB_RECALC_DATA); + WM_event_add_notifier(C, NC_WINDOW, NULL); + + return OPERATOR_FINISHED; +} + +void CACHELIBRARY_OT_shape_key_move(wmOperatorType *ot) +{ + static EnumPropertyItem slot_move[] = { + {KB_MOVE_TOP, "TOP", 0, "Top", "Top of the list"}, + {KB_MOVE_UP, "UP", 0, "Up", ""}, + {KB_MOVE_DOWN, "DOWN", 0, "Down", ""}, + {KB_MOVE_BOTTOM, "BOTTOM", 0, "Bottom", "Bottom of the list"}, + { 0, NULL, 0, NULL, NULL } + }; + + /* identifiers */ + ot->name = "Move Shape Key"; + ot->idname = "CACHELIBRARY_OT_shape_key_move"; + ot->description = "Move the active shape key up/down in the list"; + + /* api callbacks */ + ot->poll = shape_key_move_poll; + ot->exec = shape_key_move_exec; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + RNA_def_enum(ot->srna, "type", slot_move, 0, "Type", ""); +} + diff --git a/source/blender/editors/io/io_ops.c b/source/blender/editors/io/io_ops.c index a70a51a60be..49b1553ee78 100644 --- a/source/blender/editors/io/io_ops.c +++ b/source/blender/editors/io/io_ops.c @@ -30,13 +30,30 @@ #include "io_ops.h" /* own include */ +#include "io_cache_library.h" #ifdef WITH_COLLADA # include "io_collada.h" -# include "WM_api.h" #endif +#include "WM_api.h" + void ED_operatortypes_io(void) { + WM_operatortype_append(CACHELIBRARY_OT_new); + WM_operatortype_append(CACHELIBRARY_OT_delete); + WM_operatortype_append(CACHELIBRARY_OT_bake); + WM_operatortype_append(CACHELIBRARY_OT_archive_info); + WM_operatortype_append(CACHELIBRARY_OT_archive_slice); + + WM_operatortype_append(CACHELIBRARY_OT_add_modifier); + WM_operatortype_append(CACHELIBRARY_OT_remove_modifier); + + WM_operatortype_append(CACHELIBRARY_OT_shape_key_add); + WM_operatortype_append(CACHELIBRARY_OT_shape_key_remove); + WM_operatortype_append(CACHELIBRARY_OT_shape_key_clear); + WM_operatortype_append(CACHELIBRARY_OT_shape_key_retime); + WM_operatortype_append(CACHELIBRARY_OT_shape_key_move); + #ifdef WITH_COLLADA /* Collada operators: */ WM_operatortype_append(WM_OT_collada_export); diff --git a/source/blender/editors/mesh/editmesh_utils.c b/source/blender/editors/mesh/editmesh_utils.c index 2e43c9b0be8..517710405a0 100644 --- a/source/blender/editors/mesh/editmesh_utils.c +++ b/source/blender/editors/mesh/editmesh_utils.c @@ -609,7 +609,7 @@ void undo_push_mesh(bContext *C, const char *name) BMEditMesh *em = BKE_editmesh_from_object(obedit); em->ob = obedit; - undo_editmode_push(C, name, getEditMesh, free_undo, undoMesh_to_editbtMesh, editbtMesh_to_undoMesh, NULL); + undo_editmode_push(C, name, CTX_data_edit_object, getEditMesh, free_undo, undoMesh_to_editbtMesh, editbtMesh_to_undoMesh, NULL); } /** diff --git a/source/blender/editors/metaball/mball_edit.c b/source/blender/editors/metaball/mball_edit.c index 24cac5b9b70..4e4e3214798 100644 --- a/source/blender/editors/metaball/mball_edit.c +++ b/source/blender/editors/metaball/mball_edit.c @@ -740,5 +740,5 @@ static void *get_data(bContext *C) /* this is undo system for MetaBalls */ void undo_push_mball(bContext *C, const char *name) { - undo_editmode_push(C, name, get_data, free_undoMball, undoMball_to_editMball, editMball_to_undoMball, NULL); + undo_editmode_push(C, name, CTX_data_edit_object, get_data, free_undoMball, undoMball_to_editMball, editMball_to_undoMball, NULL); } diff --git a/source/blender/editors/object/CMakeLists.txt b/source/blender/editors/object/CMakeLists.txt index 5f244308dd3..618b8062d52 100644 --- a/source/blender/editors/object/CMakeLists.txt +++ b/source/blender/editors/object/CMakeLists.txt @@ -47,8 +47,10 @@ set(SRC object_bake_api.c object_constraint.c object_edit.c + object_fmap.c object_group.c object_hook.c + object_lamp.c object_lattice.c object_lod.c object_modifier.c diff --git a/source/blender/editors/object/object_edit.c b/source/blender/editors/object/object_edit.c index 2681a1f8de0..5f358ebde20 100644 --- a/source/blender/editors/object/object_edit.c +++ b/source/blender/editors/object/object_edit.c @@ -1519,6 +1519,7 @@ static EnumPropertyItem *object_mode_set_itemsf(bContext *C, PointerRNA *UNUSED( (input->value == OB_MODE_PARTICLE_EDIT && use_mode_particle_edit) || (ELEM(input->value, OB_MODE_SCULPT, OB_MODE_VERTEX_PAINT, OB_MODE_WEIGHT_PAINT, OB_MODE_TEXTURE_PAINT) && (ob->type == OB_MESH)) || + (input->value == OB_MODE_HAIR_EDIT) || /* XXX always on, testing for strand data is a bit involved */ (input->value == OB_MODE_OBJECT)) { RNA_enum_item_add(&item, &totitem, input); @@ -1552,6 +1553,8 @@ static const char *object_mode_op_string(int mode) return "PAINT_OT_texture_paint_toggle"; if (mode == OB_MODE_PARTICLE_EDIT) return "PARTICLE_OT_particle_edit_toggle"; + if (mode == OB_MODE_HAIR_EDIT) + return "HAIR_OT_hair_edit_toggle"; if (mode == OB_MODE_POSE) return "OBJECT_OT_posemode_toggle"; return NULL; @@ -1565,6 +1568,9 @@ static bool object_mode_compat_test(Object *ob, ObjectMode mode) if (ob) { if (mode == OB_MODE_OBJECT) return true; + /* XXX this is not nice, but testing for cached strands data + shape keys is a bit complicated */ + if (mode == OB_MODE_HAIR_EDIT) + return true; switch (ob->type) { case OB_MESH: diff --git a/source/blender/editors/object/object_fmap.c b/source/blender/editors/object/object_fmap.c new file mode 100644 index 00000000000..cef2b068fde --- /dev/null +++ b/source/blender/editors/object/object_fmap.c @@ -0,0 +1,495 @@ +/* + * ***** 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2008 Blender Foundation. + * All rights reserved. + * + * + * Contributor(s): Blender Foundation + * + * ***** END GPL LICENSE BLOCK ***** + */ + + +#include "DNA_object_types.h" +#include "DNA_mesh_types.h" + +#include "WM_types.h" +#include "WM_api.h" + +#include "BKE_context.h" +#include "BKE_customdata.h" +#include "BKE_facemap.h" +#include "BKE_editmesh.h" +#include "BKE_object.h" +#include "BKE_object_deform.h" + +#include "BKE_depsgraph.h" + +#include "BLI_utildefines.h" +#include "BLI_path_util.h" +#include "BLI_string.h" +#include "BLI_listbase.h" + +#include "BLF_translation.h" + +#include "MEM_guardedalloc.h" + +#include "ED_mesh.h" +#include "ED_object.h" + +#include "RNA_define.h" +#include "RNA_access.h" + +#include <string.h> + +#include "object_intern.h" + +/* called while not in editmode */ +void ED_fmap_face_add(Object *ob, bFaceMap *fmap, int facenum) +{ + int fmap_nr; + if (GS(((ID *)ob->data)->name) != ID_ME) + return; + + /* get the face map number, exit if it can't be found */ + fmap_nr = BLI_findindex(&ob->fmaps, fmap); + + if (fmap_nr != -1) { + int *facemap; + Mesh *me = ob->data; + + /* if there's is no facemap layer then create one */ + if ((facemap = CustomData_get_layer(&me->pdata, CD_FACEMAP)) == NULL) + facemap = CustomData_add_layer(&me->pdata, CD_FACEMAP, CD_DEFAULT, NULL, me->totpoly); + + facemap[facenum] = fmap_nr; + } +} + +/* called while not in editmode */ +void ED_fmap_face_remove(Object *ob, bFaceMap *fmap, int facenum) +{ + int fmap_nr; + if (GS(((ID *)ob->data)->name) != ID_ME) + return; + + /* get the face map number, exit if it can't be found */ + fmap_nr = BLI_findindex(&ob->fmaps, fmap); + + if (fmap_nr != -1) { + int *facemap; + Mesh *me = ob->data; + + /* if there's is no facemap layer then create one */ + if ((facemap = CustomData_get_layer(&me->pdata, CD_FACEMAP)) == NULL) + return; + + facemap[facenum] = -1; + } +} + +static void object_fmap_swap_edit_mode(Object *ob, int num1, int num2) +{ + if (ob->type == OB_MESH) { + Mesh *me = ob->data; + + if (me->edit_btmesh) { + BMEditMesh *em = me->edit_btmesh; + const int cd_fmap_offset = CustomData_get_offset(&em->bm->pdata, CD_FACEMAP); + + if (cd_fmap_offset != -1) { + BMFace *efa; + BMIter iter; + int *map; + + BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { + map = BM_ELEM_CD_GET_VOID_P(efa, cd_fmap_offset); + + if (map) { + if (num1 != -1) { + if (*map == num1) + *map = num2; + else if (*map == num2) + *map = num1; + } + } + } + } + } + } +} + +static void object_fmap_swap_object_mode(Object *ob, int num1, int num2) +{ + if (ob->type == OB_MESH) { + Mesh *me = ob->data; + + if (CustomData_has_layer(&me->pdata, CD_FACEMAP)) { + int *map = CustomData_get_layer(&me->pdata, CD_FACEMAP); + int i; + + if (map) { + for (i = 0; i < me->totpoly; i++) { + if (num1 != -1) { + if (map[i] == num1) + map[i] = num2; + else if (map[i]== num2) + map[i] = num1; + } + } + } + } + } +} + +static void object_facemap_swap(Object *ob, int num1, int num2) +{ + if (BKE_object_is_in_editmode(ob)) + object_fmap_swap_edit_mode(ob, num1, num2); + else + object_fmap_swap_object_mode(ob, num1, num2); +} + +static int face_map_supported_poll(bContext *C) +{ + Object *ob = ED_object_context(C); + ID *data = (ob) ? ob->data : NULL; + return (ob && !ob->id.lib && ob->type == OB_MESH && data && !data->lib); +} + +static int face_map_supported_edit_mode_poll(bContext *C) +{ + Object *ob = ED_object_context(C); + ID *data = (ob) ? ob->data : NULL; + return (ob && !ob->id.lib && ob->type == OB_MESH && data && !data->lib && ob->mode == OB_MODE_EDIT); +} + +static int face_map_add_exec(bContext *C, wmOperator *UNUSED(op)) +{ + Object *ob = ED_object_context(C); + + BKE_object_facemap_add(ob); + DAG_id_tag_update(&ob->id, OB_RECALC_DATA); + WM_event_add_notifier(C, NC_GEOM | ND_DATA, ob->data); + WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob); + + return OPERATOR_FINISHED; +} + +void OBJECT_OT_face_map_add(struct wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Add Face Map"; + ot->idname = "OBJECT_OT_face_map_add"; + ot->description = "Add a new face map to the active object"; + + /* api callbacks */ + ot->poll = face_map_supported_poll; + ot->exec = face_map_add_exec; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +static int face_map_remove_exec(bContext *C, wmOperator *UNUSED(op)) +{ + Object *ob = ED_object_context(C); + bFaceMap *fmap = BLI_findlink(&ob->fmaps, ob->actfmap - 1); + + if (fmap) { + BKE_object_facemap_remove(ob, fmap); + DAG_id_tag_update(&ob->id, OB_RECALC_DATA); + WM_event_add_notifier(C, NC_GEOM | ND_DATA, ob->data); + WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob); + } + return OPERATOR_FINISHED; +} + +void OBJECT_OT_face_map_remove(struct wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Remove Face Map"; + ot->idname = "OBJECT_OT_face_map_remove"; + ot->description = "Remove a face map from the active object"; + + /* api callbacks */ + ot->poll = face_map_supported_poll; + ot->exec = face_map_remove_exec; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +static int face_map_assign_exec(bContext *C, wmOperator *UNUSED(op)) +{ + Object *ob = ED_object_context(C); + bFaceMap *fmap = BLI_findlink(&ob->fmaps, ob->actfmap - 1); + + if (fmap) { + Mesh *me = ob->data; + BMEditMesh *em = me->edit_btmesh; + BMFace *efa; + BMIter iter; + int *map; + int cd_fmap_offset; + + if (!CustomData_has_layer(&em->bm->pdata, CD_FACEMAP)) + BM_data_layer_add(em->bm, &em->bm->pdata, CD_FACEMAP); + + cd_fmap_offset = CustomData_get_offset(&em->bm->pdata, CD_FACEMAP); + + BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { + map = BM_ELEM_CD_GET_VOID_P(efa, cd_fmap_offset); + + if (BM_elem_flag_test(efa, BM_ELEM_SELECT)) { + *map = ob->actfmap - 1; + } + } + + DAG_id_tag_update(&ob->id, OB_RECALC_DATA); + WM_event_add_notifier(C, NC_GEOM | ND_DATA, ob->data); + WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob); + } + return OPERATOR_FINISHED; +} + +void OBJECT_OT_face_map_assign(struct wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Assign Face Map"; + ot->idname = "OBJECT_OT_face_map_assign"; + ot->description = "Assign faces to a face map"; + + /* api callbacks */ + ot->poll = face_map_supported_edit_mode_poll; + ot->exec = face_map_assign_exec; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +static int face_map_remove_from_exec(bContext *C, wmOperator *UNUSED(op)) +{ + Object *ob = ED_object_context(C); + bFaceMap *fmap = BLI_findlink(&ob->fmaps, ob->actfmap - 1); + + if (fmap) { + Mesh *me = ob->data; + BMEditMesh *em = me->edit_btmesh; + BMFace *efa; + BMIter iter; + int *map; + int cd_fmap_offset; + int mapindex = ob->actfmap - 1; + + if (!CustomData_has_layer(&em->bm->pdata, CD_FACEMAP)) + return OPERATOR_CANCELLED; + + cd_fmap_offset = CustomData_get_offset(&em->bm->pdata, CD_FACEMAP); + + BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { + map = BM_ELEM_CD_GET_VOID_P(efa, cd_fmap_offset); + + if (BM_elem_flag_test(efa, BM_ELEM_SELECT) && *map == mapindex) { + *map = -1; + } + } + + DAG_id_tag_update(&ob->id, OB_RECALC_DATA); + WM_event_add_notifier(C, NC_GEOM | ND_DATA, ob->data); + WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob); + } + return OPERATOR_FINISHED; +} + +void OBJECT_OT_face_map_remove_from(struct wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Remove From Face Map"; + ot->idname = "OBJECT_OT_face_map_remove_from"; + ot->description = "Remove faces from a face map"; + + /* api callbacks */ + ot->poll = face_map_supported_edit_mode_poll; + ot->exec = face_map_remove_from_exec; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +static void fmap_select(Object *ob, bool select) +{ + Mesh *me = ob->data; + BMEditMesh *em = me->edit_btmesh; + BMFace *efa; + BMIter iter; + int *map; + int cd_fmap_offset; + int mapindex = ob->actfmap - 1; + + if (!CustomData_has_layer(&em->bm->pdata, CD_FACEMAP)) + BM_data_layer_add(em->bm, &em->bm->pdata, CD_FACEMAP); + + cd_fmap_offset = CustomData_get_offset(&em->bm->pdata, CD_FACEMAP); + + BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { + map = BM_ELEM_CD_GET_VOID_P(efa, cd_fmap_offset); + + if (*map == mapindex) { + BM_face_select_set(em->bm, efa, select); + } + } +} + +static int face_map_select_exec(bContext *C, wmOperator *UNUSED(op)) +{ + Object *ob = ED_object_context(C); + bFaceMap *fmap = BLI_findlink(&ob->fmaps, ob->actfmap - 1); + + if (fmap) { + fmap_select(ob, true); + + DAG_id_tag_update(&ob->id, OB_RECALC_DATA); + WM_event_add_notifier(C, NC_GEOM | ND_DATA, ob->data); + WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob); + } + return OPERATOR_FINISHED; +} + +void OBJECT_OT_face_map_select(struct wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Select Face Map Faces"; + ot->idname = "OBJECT_OT_face_map_select"; + ot->description = "Select faces belonging to a face map"; + + /* api callbacks */ + ot->poll = face_map_supported_edit_mode_poll; + ot->exec = face_map_select_exec; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +static int face_map_deselect_exec(bContext *C, wmOperator *UNUSED(op)) +{ + Object *ob = ED_object_context(C); + bFaceMap *fmap = BLI_findlink(&ob->fmaps, ob->actfmap - 1); + + if (fmap) { + fmap_select(ob, false); + + DAG_id_tag_update(&ob->id, OB_RECALC_DATA); + WM_event_add_notifier(C, NC_GEOM | ND_DATA, ob->data); + WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob); + } + return OPERATOR_FINISHED; +} + +void OBJECT_OT_face_map_deselect(struct wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Deselect Face Map Faces"; + ot->idname = "OBJECT_OT_face_map_deselect"; + ot->description = "Deselect faces belonging to a face map"; + + /* api callbacks */ + ot->poll = face_map_supported_edit_mode_poll; + ot->exec = face_map_deselect_exec; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + + +static int face_map_move_exec(bContext *C, wmOperator *op) +{ + Object *ob = ED_object_context(C); + bFaceMap *fmap; + int dir = RNA_enum_get(op->ptr, "direction"); + int pos1, pos2 = -1, count; + + fmap = BLI_findlink(&ob->fmaps, ob->actfmap - 1); + if (!fmap) { + return OPERATOR_CANCELLED; + } + + count = BLI_listbase_count(&ob->fmaps); + pos1 = BLI_findindex(&ob->fmaps, fmap); + + if (dir == 1) { /*up*/ + void *prev = fmap->prev; + + if (prev) { + pos2 = pos1 - 1; + } + else { + pos2 = count - 1; + } + + BLI_remlink(&ob->fmaps, fmap); + BLI_insertlinkbefore(&ob->fmaps, prev, fmap); + } + else { /*down*/ + void *next = fmap->next; + + if (next) { + pos2 = pos1 + 1; + } + else { + pos2 = 0; + } + + BLI_remlink(&ob->fmaps, fmap); + BLI_insertlinkafter(&ob->fmaps, next, fmap); + } + + /* iterate through mesh and substitute the indices as necessary */ + object_facemap_swap(ob, pos2, pos1); + + ob->actfmap = pos2 +1; + + DAG_id_tag_update(&ob->id, OB_RECALC_DATA); + WM_event_add_notifier(C, NC_GEOM | ND_VERTEX_GROUP, ob); + + return OPERATOR_FINISHED; +} + + +void OBJECT_OT_face_map_move(wmOperatorType *ot) +{ + static EnumPropertyItem fmap_slot_move[] = { + {1, "UP", 0, "Up", ""}, + {-1, "DOWN", 0, "Down", ""}, + {0, NULL, 0, NULL, NULL} + }; + + /* identifiers */ + ot->name = "Move Face Map"; + ot->idname = "OBJECT_OT_face_map_move"; + ot->description = "Move the active face map up/down in the list"; + + /* api callbacks */ + ot->poll = face_map_supported_poll; + ot->exec = face_map_move_exec; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + RNA_def_enum(ot->srna, "direction", fmap_slot_move, 0, "Direction", "Direction to move, UP or DOWN"); +} diff --git a/source/blender/editors/object/object_intern.h b/source/blender/editors/object/object_intern.h index 1edd7334767..d42b03cd30f 100644 --- a/source/blender/editors/object/object_intern.h +++ b/source/blender/editors/object/object_intern.h @@ -249,6 +249,15 @@ void OBJECT_OT_vertex_weight_set_active(struct wmOperatorType *ot); void OBJECT_OT_vertex_weight_normalize_active_vertex(struct wmOperatorType *ot); void OBJECT_OT_vertex_weight_copy(struct wmOperatorType *ot); +/* object_fmap.c */ +void OBJECT_OT_face_map_add(struct wmOperatorType *ot); +void OBJECT_OT_face_map_remove(struct wmOperatorType *ot); +void OBJECT_OT_face_map_assign(struct wmOperatorType *ot); +void OBJECT_OT_face_map_remove_from(struct wmOperatorType *ot); +void OBJECT_OT_face_map_select(struct wmOperatorType *ot); +void OBJECT_OT_face_map_deselect(struct wmOperatorType *ot); +void OBJECT_OT_face_map_move(struct wmOperatorType *ot); + /* object_warp.c */ void TRANSFORM_OT_vertex_warp(struct wmOperatorType *ot); @@ -282,5 +291,8 @@ void TRANSFORM_OT_vertex_random(struct wmOperatorType *ot); void OBJECT_OT_data_transfer(struct wmOperatorType *ot); void OBJECT_OT_datalayout_transfer(struct wmOperatorType *ot); +/* object_lamp.c */ +void LAMP_OT_lamp_position(struct wmOperatorType *ot); + #endif /* __OBJECT_INTERN_H__ */ diff --git a/source/blender/editors/object/object_lamp.c b/source/blender/editors/object/object_lamp.c new file mode 100644 index 00000000000..f60c8f3044e --- /dev/null +++ b/source/blender/editors/object/object_lamp.c @@ -0,0 +1,235 @@ +/* + * ***** 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. + * All rights reserved. + * + * Contributor(s): Blender Foundation + * + * ***** END GPL LICENSE BLOCK ***** + */ + + +/** \file blender/editors/object/object_lamp.c + * \ingroup edobj + */ + +#include "WM_types.h" + +#include "DNA_scene_types.h" +#include "DNA_screen_types.h" +#include "DNA_lamp_types.h" +#include "DNA_object_types.h" +#include "DNA_lamp_types.h" +#include "DNA_view3d_types.h" +#include "DNA_windowmanager_types.h" +#include "DNA_userdef_types.h" + +#include "BLI_utildefines.h" +#include "BLI_math_matrix.h" +#include "BLI_math.h" + +#include "BKE_context.h" +#include "BKE_depsgraph.h" +#include "BKE_object.h" + +#include "ED_view3d.h" +#include "ED_screen.h" + +#include "WM_types.h" +#include "WM_api.h" + +#include "BIF_glutil.h" + +#include "MEM_guardedalloc.h" + +#include "UI_interface.h" +#include "object_intern.h" + +#include "RNA_define.h" +#include "RNA_access.h" + +typedef struct LampPositionData { + int pos[2]; + float quat[4]; + float lvec[3]; +} LampPositionData; + +/* Modal Operator init */ +static int lamp_position_invoke(bContext *C, wmOperator *op, const wmEvent *event) +{ + Object *ob = CTX_data_active_object(C); + LampPositionData *data; + data = op->customdata = MEM_mallocN(sizeof (LampPositionData), "lamp_position_data"); + + copy_v2_v2_int(data->pos, event->mval); + + mat4_to_quat(data->quat, ob->obmat); + copy_v3_v3(data->lvec, ob->obmat[2]); + negate_v3(data->lvec); + normalize_v3(data->lvec); + + WM_event_add_modal_handler(C, op); + + return OPERATOR_RUNNING_MODAL; +} + +/* Repeat operator */ +static int lamp_position_modal(bContext *C, wmOperator *op, const wmEvent *event) +{ + + LampPositionData *data = op->customdata; + + switch (event->type) { + case MOUSEMOVE: + { + Object *ob = CTX_data_active_object(C); + Lamp *la = ob->data; + Scene *scene = CTX_data_scene(C); + ARegion *ar = CTX_wm_region(C); + View3D *v3d = CTX_wm_view3d(C); + float world_pos[3]; + int flag = v3d->flag2; + + v3d->flag2 |= V3D_RENDER_OVERRIDE; + + view3d_operator_needs_opengl(C); + if (ED_view3d_autodist(scene, ar, v3d, event->mval, world_pos, true, NULL)) { + float axis[3]; + + /* restore the floag here */ + v3d->flag2 = flag; + + sub_v3_v3(world_pos, ob->obmat[3]); + la->dist = normalize_v3(world_pos); + + cross_v3_v3v3(axis, data->lvec, world_pos); + if (normalize_v3(axis) > 0.0001) { + float mat[4][4]; + float quat[4], qfinal[4]; + float angle = saacos(dot_v3v3(world_pos, data->lvec)); + + /* transform the initial rotation quaternion to the new position and set the matrix to the lamp */ + axis_angle_to_quat(quat, axis, angle); + mul_qt_qtqt(qfinal, quat, data->quat); + quat_to_mat4(mat, qfinal); + copy_v3_v3(mat[3], ob->obmat[3]); + + BKE_object_apply_mat4(ob, mat, true, false); + } + + DAG_id_tag_update(&ob->id, OB_RECALC_OB); + + ED_region_tag_redraw(ar); + } + + v3d->flag2 = flag; + + break; + } + + case LEFTMOUSE: + if (event->val == KM_RELEASE) { + MEM_freeN(op->customdata); + return OPERATOR_FINISHED; + } + + case EVT_WIDGET_UPDATE: + { + ARegion *ar = CTX_wm_region(C); + Object *ob = CTX_data_active_object(C); + Lamp *la = ob->data; + float value[3], len; + + RNA_float_get_array(op->ptr, "value", value); + + sub_v3_v3(value, ob->obmat[3]); + + len = len_v3(value); + + la->spotsize = len * 0.1f; + DAG_id_tag_update(&ob->id, OB_RECALC_OB); + + ED_region_tag_redraw(ar); + break; + } + + case EVT_WIDGET_RELEASED: + { + MEM_freeN(op->customdata); + return OPERATOR_FINISHED; + } + } + + return OPERATOR_RUNNING_MODAL; +} + +static int lamp_position_poll(bContext *C) +{ + return CTX_wm_region_view3d(C) != NULL; +} + + +void LAMP_OT_lamp_position(struct wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Lamp Position"; + ot->idname = "UI_OT_lamp_position"; + ot->description = "Sample a color from the Blender Window to store in a property"; + + /* api callbacks */ + ot->invoke = lamp_position_invoke; + ot->modal = lamp_position_modal; + ot->poll = lamp_position_poll; + + /* flags */ + ot->flag = OPTYPE_BLOCKING | OPTYPE_UNDO; + + /* properties */ + RNA_def_float_vector_xyz(ot->srna, "value", 3, NULL, -FLT_MAX, FLT_MAX, "Vector", "", -FLT_MAX, FLT_MAX); +} + +int WIDGETGROUP_lamp_poll(const struct bContext *C, struct wmWidgetGroupType *UNUSED(wgrouptype)) +{ + Object *ob = CTX_data_active_object(C); + + if (ob && ob->type == OB_LAMP) { + Lamp *la = ob->data; + return (la->type == LA_SPOT); + } + return false; +} + +void WIDGETGROUP_lamp_draw(const struct bContext *C, struct wmWidgetGroup *wgroup) +{ + float color_lamp[4] = {0.5f, 0.5f, 1.0f, 1.0f}; + Object *ob = CTX_data_active_object(C); + Lamp *la = ob->data; + wmWidget *widget; + PointerRNA ptr; + float dir[3]; + + widget = WIDGET_arrow_new(wgroup, WIDGET_ARROW_STYLE_INVERTED); + + WIDGET_arrow_set_color(widget, color_lamp); + + RNA_pointer_create(&la->id, &RNA_Lamp, la, &ptr); + WM_widget_set_origin(widget, ob->obmat[3]); + WM_widget_property(widget, ARROW_SLOT_OFFSET_WORLD_SPACE, &ptr, "spot_size"); + negate_v3_v3(dir, ob->obmat[2]); + WIDGET_arrow_set_direction(widget, dir); +} diff --git a/source/blender/editors/object/object_lattice.c b/source/blender/editors/object/object_lattice.c index b577dab8a77..f269a7a5360 100644 --- a/source/blender/editors/object/object_lattice.c +++ b/source/blender/editors/object/object_lattice.c @@ -967,6 +967,6 @@ static void *get_editlatt(bContext *C) /* and this is all the undo system needs to know */ void undo_push_lattice(bContext *C, const char *name) { - undo_editmode_push(C, name, get_editlatt, free_undoLatt, undoLatt_to_editLatt, editLatt_to_undoLatt, validate_undoLatt); + undo_editmode_push(C, name, CTX_data_edit_object, get_editlatt, free_undoLatt, undoLatt_to_editLatt, editLatt_to_undoLatt, validate_undoLatt); } diff --git a/source/blender/editors/object/object_ops.c b/source/blender/editors/object/object_ops.c index ad14f788569..e24610c77be 100644 --- a/source/blender/editors/object/object_ops.c +++ b/source/blender/editors/object/object_ops.c @@ -203,6 +203,14 @@ void ED_operatortypes_object(void) WM_operatortype_append(OBJECT_OT_vertex_weight_normalize_active_vertex); WM_operatortype_append(OBJECT_OT_vertex_weight_copy); + WM_operatortype_append(OBJECT_OT_face_map_add); + WM_operatortype_append(OBJECT_OT_face_map_remove); + WM_operatortype_append(OBJECT_OT_face_map_assign); + WM_operatortype_append(OBJECT_OT_face_map_remove_from); + WM_operatortype_append(OBJECT_OT_face_map_select); + WM_operatortype_append(OBJECT_OT_face_map_deselect); + WM_operatortype_append(OBJECT_OT_face_map_move); + WM_operatortype_append(TRANSFORM_OT_vertex_warp); WM_operatortype_append(OBJECT_OT_game_property_new); @@ -258,6 +266,8 @@ void ED_operatortypes_object(void) WM_operatortype_append(OBJECT_OT_data_transfer); WM_operatortype_append(OBJECT_OT_datalayout_transfer); + + WM_operatortype_append(LAMP_OT_lamp_position); } void ED_operatormacros_object(void) diff --git a/source/blender/editors/object/object_shapekey.c b/source/blender/editors/object/object_shapekey.c index 8a98d0d8a1a..168891f6459 100644 --- a/source/blender/editors/object/object_shapekey.c +++ b/source/blender/editors/object/object_shapekey.c @@ -48,10 +48,13 @@ #include "DNA_lattice_types.h" #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" +#include "DNA_particle_types.h" +#include "DNA_scene_types.h" #include "DNA_object_types.h" #include "BKE_context.h" #include "BKE_depsgraph.h" +#include "BKE_editmesh.h" #include "BKE_key.h" #include "BKE_library.h" #include "BKE_main.h" @@ -496,4 +499,3 @@ void OBJECT_OT_shape_key_move(wmOperatorType *ot) RNA_def_enum(ot->srna, "type", slot_move, 0, "Type", ""); } - diff --git a/source/blender/editors/physics/CMakeLists.txt b/source/blender/editors/physics/CMakeLists.txt index 40d555226f3..f291af4239c 100644 --- a/source/blender/editors/physics/CMakeLists.txt +++ b/source/blender/editors/physics/CMakeLists.txt @@ -41,6 +41,7 @@ set(SRC particle_boids.c particle_edit.c particle_object.c + particle_shapekey.c physics_fluid.c physics_ops.c physics_pointcache.c diff --git a/source/blender/editors/physics/particle_edit.c b/source/blender/editors/physics/particle_edit.c index f25679986a5..cf02d2773c0 100644 --- a/source/blender/editors/physics/particle_edit.c +++ b/source/blender/editors/physics/particle_edit.c @@ -37,6 +37,7 @@ #include "MEM_guardedalloc.h" +#include "DNA_key_types.h" #include "DNA_scene_types.h" #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" @@ -56,6 +57,7 @@ #include "BKE_depsgraph.h" #include "BKE_DerivedMesh.h" #include "BKE_global.h" +#include "BKE_key.h" #include "BKE_object.h" #include "BKE_mesh.h" #include "BKE_modifier.h" @@ -1201,6 +1203,9 @@ void update_world_cos(Object *ob, PTCacheEdit *edit) mul_m4_v3(hairmat, key->world_co); } } + + /* apply hair changes to the active shape key */ + PE_shapekey_apply(ob, psys); } static void update_velocities(PTCacheEdit *edit) { @@ -3701,6 +3706,7 @@ typedef struct BrushEdit { int first; int lastmouse[2]; float zfac; + bool done; /* optional cached view settings to avoid setting on every mousemove */ PEData data; @@ -3726,6 +3732,7 @@ static int brush_edit_init(bContext *C, wmOperator *op) bedit= MEM_callocN(sizeof(BrushEdit), "BrushEdit"); bedit->first= 1; + bedit->done = false; op->customdata= bedit; bedit->scene= scene; @@ -3881,13 +3888,16 @@ static void brush_edit_apply(bContext *C, wmOperator *op, PointerRNA *itemptr) } case PE_BRUSH_ADD: { - if (edit->psys && edit->psys->part->from==PART_FROM_FACE) { + bool done = (brush->flag & PE_BRUSH_DATA_ADD_SINGLE) && bedit->done; + if (!done && edit->psys && edit->psys->part->from==PART_FROM_FACE) { data.mval= mval; added= brush_add(&data, brush->count); if (pset->flag & PE_KEEP_LENGTHS) recalc_lengths(edit); + + bedit->done = true; } else added= 0; @@ -4091,10 +4101,11 @@ static bool shape_cut_test_point(PEData *data, ParticleCacheKey *key) BVHTreeFromMesh *shape_bvh = &data->shape_bvh; const float dir[3] = {1.0f, 0.0f, 0.0f}; PointInsideBVH userdata; - userdata.bvhdata = data->shape_bvh; userdata.num_hits = 0; + userdata.bvhdata = data->shape_bvh; + userdata.num_hits = 0; BLI_bvhtree_ray_cast_all(shape_bvh->tree, key->co, dir, 0.0f, point_inside_bvh_cb, &userdata); /* for any point inside a watertight mesh the number of hits is uneven */ @@ -4560,6 +4571,44 @@ int PE_minmax(Scene *scene, float min[3], float max[3]) /************************ particle edit toggle operator ************************/ +bool PE_shapekey_load(Object *ob, ParticleSystem *psys) +{ + const int mode_flag = OB_MODE_PARTICLE_EDIT; + const bool is_mode_set = (ob->mode & mode_flag) != 0; + + if (!is_mode_set) + return false; + + if (psys->edit) { + /* set the active shape key */ + KeyBlock *actkb = BKE_keyblock_from_particles(psys); + + if (actkb) + BKE_keyblock_convert_to_hair_keys(actkb, ob, psys); + } + + return true; +} + +bool PE_shapekey_apply(struct Object *ob, struct ParticleSystem *psys) +{ + const int mode_flag = OB_MODE_PARTICLE_EDIT; + const bool is_mode_set = (ob->mode & mode_flag) != 0; + + if (!is_mode_set) + return false; + + if (psys->edit) { + /* define the active shape key */ + KeyBlock *actkb = BKE_keyblock_from_particles(psys); + + if (actkb) + BKE_keyblock_convert_from_hair_keys(ob, psys, actkb); + } + + return true; +} + /* initialize needed data for bake edit */ void PE_create_particle_edit(Scene *scene, Object *ob, PointCache *cache, ParticleSystem *psys) { @@ -4702,11 +4751,18 @@ static int particle_edit_toggle_exec(bContext *C, wmOperator *op) PTCacheEdit *edit; ob->mode |= mode_flag; edit= PE_create_current(scene, ob); - + /* mesh may have changed since last entering editmode. * note, this may have run before if the edit data was just created, so could avoid this and speed up a little */ - if (edit && edit->psys) + if (edit && edit->psys) { + /* set the active shape key */ + KeyBlock *actkb = BKE_keyblock_from_particles(edit->psys); + + if (actkb) + BKE_keyblock_convert_to_hair_keys(actkb, ob, edit->psys); + recalc_emitter_field(ob, edit->psys); + } toggle_particle_cursor(C, 1); WM_event_add_notifier(C, NC_SCENE|ND_MODE|NS_MODE_PARTICLE, NULL); @@ -4793,3 +4849,89 @@ void PARTICLE_OT_edited_clear(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO; } +/************************ Propagate Shape Key *************************/ + +static int PE_count_keys(PTCacheEdit *edit) +{ + int totkey = 0, p; + for (p = 0; p < edit->totpoint; ++p) { + totkey += edit->points[p].totkey; + } + return totkey; +} + +static void shape_propagate(PTCacheEdit *edit, int totkey, KeyBlock *kb, wmOperator *UNUSED(op)) +{ + PTCacheEditPoint *point; + float *fp; + int i, k; + + if (kb->totelem != totkey) + return; + + fp = kb->data; + point = edit->points; + for (i = 0; i < edit->totpoint; ++i, ++point) { + PTCacheEditKey *key = point->keys; + const bool use_point = !(point->flag & PEP_HIDE); + + for (k = 0; k < point->totkey; ++k, ++key) { + const bool use_key = (key->flag & PEK_SELECT) && !(key->flag & PEK_HIDE); + + if (use_point && use_key) { + copy_v3_v3(fp, key->co); + + point->flag |= PEP_EDIT_RECALC; + } + + fp += 3; + } + } +} + +static int particle_shape_propagate_to_all_exec(bContext *C, wmOperator *op) +{ + Scene *scene = CTX_data_scene(C); + ParticleEditSettings *pset = PE_settings(scene); + Object *ob = ED_object_context(C); + ParticleSystem *psys = psys_get_current(ob); + PTCacheEdit *edit = psys->edit; + Key *key = psys->key; + KeyBlock *kb; + const int totkey = PE_count_keys(edit); + + if (!key) + return OPERATOR_CANCELLED; + + /* we might need world space coordinates, update to be sure */ + update_world_cos(ob, edit); + + for (kb = key->block.first; kb; kb = kb->next) + shape_propagate(edit, totkey, kb, op); + + update_world_cos(ob, edit); + PE_update_object(scene, ob, 1); + + if (!(pset->flag & PE_KEEP_LENGTHS)) + recalc_lengths(edit); + + WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE|NA_EDITED, ob); + + return OPERATOR_FINISHED; +} + + +void PARTICLE_OT_shape_propagate_to_all(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Shape Propagate"; + ot->description = "Apply selected vertex locations to all other shape keys"; + ot->idname = "PARTICLE_OT_shape_propagate_to_all"; + + /* api callbacks */ + ot->exec = particle_shape_propagate_to_all_exec; + ot->poll = PE_hair_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} diff --git a/source/blender/editors/physics/particle_shapekey.c b/source/blender/editors/physics/particle_shapekey.c new file mode 100644 index 00000000000..3de3d56911a --- /dev/null +++ b/source/blender/editors/physics/particle_shapekey.c @@ -0,0 +1,416 @@ +/* + * ***** 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) Blender Foundation + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): Lukas Toenne + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/editors/physics/particle_key.c + * \ingroup edphys + */ + +#include "MEM_guardedalloc.h" + +#include "BLI_blenlib.h" +#include "BLI_math.h" +#include "BLI_utildefines.h" + +#include "DNA_key_types.h" +#include "DNA_scene_types.h" +#include "DNA_object_types.h" +#include "DNA_particle_types.h" + +#include "BKE_context.h" +#include "BKE_depsgraph.h" +#include "BKE_key.h" +#include "BKE_library.h" +#include "BKE_main.h" +#include "BKE_object.h" +#include "BKE_particle.h" + +#include "BLI_sys_types.h" // for intptr_t support + +#include "ED_object.h" + +#include "RNA_access.h" +#include "RNA_define.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "physics_intern.h" + +/*********************** add shape key ***********************/ + +static void ED_particles_shape_key_add(bContext *C, Scene *scene, Object *ob, ParticleSystem *psys, const bool from_mix) +{ + KeyBlock *kb; + if ((kb = BKE_psys_insert_shape_key(scene, ob, psys, NULL, from_mix))) { + Key *key = psys->key; + /* for absolute shape keys, new keys may not be added last */ + psys->shapenr = BLI_findindex(&key->block, kb) + 1; + + WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob); + } +} + +/*********************** remove shape key ***********************/ + +static bool ED_particles_shape_key_remove_all(Main *bmain, Object *UNUSED(ob), ParticleSystem *psys) +{ + Key *key = psys->key; + if (key == NULL) + return false; + + psys->key = NULL; + + BKE_libblock_free_us(bmain, key); + + return true; +} + +static bool ED_particles_shape_key_remove(Main *bmain, Object *ob, ParticleSystem *psys) +{ + KeyBlock *kb, *rkb; + Key *key = psys->key; + if (key == NULL) + return false; + + kb = BLI_findlink(&key->block, psys->shapenr - 1); + + if (kb) { + for (rkb = key->block.first; rkb; rkb = rkb->next) + if (rkb->relative == psys->shapenr - 1) + rkb->relative = 0; + + BLI_remlink(&key->block, kb); + key->totkey--; + if (key->refkey == kb) { + key->refkey = key->block.first; + + if (key->refkey) { + /* apply new basis key on original data */ + BKE_keyblock_convert_to_hair_keys(key->refkey, ob, psys); + } + } + + if (kb->data) MEM_freeN(kb->data); + MEM_freeN(kb); + + if (psys->shapenr > 1) { + psys->shapenr--; + } + } + + if (key->totkey == 0) { + psys->key = NULL; + + BKE_libblock_free_us(bmain, key); + } + + return true; +} + +/********************** shape key operators *********************/ + +static int shape_key_mode_poll(bContext *C) +{ + Object *ob = ED_object_context(C); + ParticleSystem *psys = psys_get_current(ob); + return (ob && !ob->id.lib && psys && ob->mode != OB_MODE_PARTICLE_EDIT); +} + +static int shape_key_mode_exists_poll(bContext *C) +{ + Object *ob = ED_object_context(C); + ParticleSystem *psys = psys_get_current(ob); + + /* same as shape_key_mode_poll */ + return (ob && !ob->id.lib && psys && ob->mode != OB_MODE_PARTICLE_EDIT) && + /* check a keyblock exists */ + (BKE_keyblock_from_particles(psys) != NULL); +} + +static int shape_key_poll(bContext *C) +{ + Object *ob = ED_object_context(C); + ParticleSystem *psys = psys_get_current(ob); + + return (ob && !ob->id.lib && psys); +} + +static int shape_key_add_exec(bContext *C, wmOperator *op) +{ + Scene *scene = CTX_data_scene(C); + Object *ob = ED_object_context(C); + ParticleSystem *psys = psys_get_current(ob); + const bool from_mix = RNA_boolean_get(op->ptr, "from_mix"); + + ED_particles_shape_key_add(C, scene, ob, psys, from_mix); + + return OPERATOR_FINISHED; +} + +void PARTICLE_OT_shape_key_add(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Add Shape Key"; + ot->idname = "PARTICLE_OT_shape_key_add"; + ot->description = "Add shape key to the object"; + + /* api callbacks */ + ot->poll = shape_key_mode_poll; + ot->exec = shape_key_add_exec; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* properties */ + RNA_def_boolean(ot->srna, "from_mix", true, "From Mix", "Create the new shape key from the existing mix of keys"); +} + +static int shape_key_remove_exec(bContext *C, wmOperator *op) +{ + Main *bmain = CTX_data_main(C); + Object *ob = ED_object_context(C); + ParticleSystem *psys = psys_get_current(ob); + bool changed = false; + + if (RNA_boolean_get(op->ptr, "all")) { + changed = ED_particles_shape_key_remove_all(bmain, ob, psys); + } + else { + changed = ED_particles_shape_key_remove(bmain, ob, psys); + } + + if (changed) { + DAG_id_tag_update(&ob->id, OB_RECALC_DATA); + WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob); + + return OPERATOR_FINISHED; + } + else { + return OPERATOR_CANCELLED; + } +} + +void PARTICLE_OT_shape_key_remove(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Remove Shape Key"; + ot->idname = "PARTICLE_OT_shape_key_remove"; + ot->description = "Remove shape key from the object"; + + /* api callbacks */ + ot->poll = shape_key_mode_poll; + ot->poll = shape_key_mode_exists_poll; + ot->exec = shape_key_remove_exec; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* properties */ + RNA_def_boolean(ot->srna, "all", 0, "All", "Remove all shape keys"); +} + +static int shape_key_clear_exec(bContext *C, wmOperator *UNUSED(op)) +{ + Object *ob = ED_object_context(C); + ParticleSystem *psys = psys_get_current(ob); + Key *key = psys->key; + KeyBlock *kb = BKE_keyblock_from_particles(psys); + + if (!key || !kb) + return OPERATOR_CANCELLED; + + for (kb = key->block.first; kb; kb = kb->next) + kb->curval = 0.0f; + + DAG_id_tag_update(&ob->id, OB_RECALC_DATA); + WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob); + + return OPERATOR_FINISHED; +} + +void PARTICLE_OT_shape_key_clear(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Clear Shape Keys"; + ot->description = "Clear weights for all shape keys"; + ot->idname = "PARTICLE_OT_shape_key_clear"; + + /* api callbacks */ + ot->poll = shape_key_poll; + ot->exec = shape_key_clear_exec; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +/* starting point and step size could be optional */ +static int shape_key_retime_exec(bContext *C, wmOperator *UNUSED(op)) +{ + Object *ob = ED_object_context(C); + ParticleSystem *psys = psys_get_current(ob); + Key *key = psys->key; + KeyBlock *kb = BKE_keyblock_from_particles(psys); + float cfra = 0.0f; + + if (!key || !kb) + return OPERATOR_CANCELLED; + + for (kb = key->block.first; kb; kb = kb->next) + kb->pos = (cfra += 0.1f); + + DAG_id_tag_update(&ob->id, OB_RECALC_DATA); + WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob); + + return OPERATOR_FINISHED; +} + +void PARTICLE_OT_shape_key_retime(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Re-Time Shape Keys"; + ot->description = "Resets the timing for absolute shape keys"; + ot->idname = "PARTICLE_OT_shape_key_retime"; + + /* api callbacks */ + ot->poll = shape_key_poll; + ot->exec = shape_key_retime_exec; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +static int shape_key_move_exec(bContext *C, wmOperator *op) +{ + Object *ob = ED_object_context(C); + ParticleSystem *psys = psys_get_current(ob); + Key *key = psys->key; + + if (!key) { + return OPERATOR_CANCELLED; + } + + { + KeyBlock *kb, *kb_other, *kb_iter; + const int type = RNA_enum_get(op->ptr, "type"); + const int shape_tot = key->totkey; + const int shapenr_act = psys->shapenr - 1; + const int shapenr_swap = (shape_tot + shapenr_act + type) % shape_tot; + + kb = BLI_findlink(&key->block, shapenr_act); + if (!kb || shape_tot == 1) { + return OPERATOR_CANCELLED; + } + + if (type == -1) { + /* move back */ + kb_other = kb->prev; + BLI_remlink(&key->block, kb); + BLI_insertlinkbefore(&key->block, kb_other, kb); + } + else { + /* move next */ + kb_other = kb->next; + BLI_remlink(&key->block, kb); + BLI_insertlinkafter(&key->block, kb_other, kb); + } + + psys->shapenr = shapenr_swap + 1; + + /* for relative shape keys */ + if (kb_other) { + for (kb_iter = key->block.first; kb_iter; kb_iter = kb_iter->next) { + if (kb_iter->relative == shapenr_act) { + kb_iter->relative = shapenr_swap; + } + else if (kb_iter->relative == shapenr_swap) { + kb_iter->relative = shapenr_act; + } + } + } + /* First key became last, or vice-versa, we have to change all keys' relative value. */ + else { + for (kb_iter = key->block.first; kb_iter; kb_iter = kb_iter->next) { + if (kb_iter->relative == shapenr_act) { + kb_iter->relative = shapenr_swap; + } + else { + kb_iter->relative += type; + } + } + } + + /* for absolute shape keys */ + if (kb_other) { + SWAP(float, kb_other->pos, kb->pos); + } + /* First key became last, or vice-versa, we have to change all keys' pos value. */ + else { + float pos = kb->pos; + if (type == -1) { + for (kb_iter = key->block.first; kb_iter; kb_iter = kb_iter->next) { + SWAP(float, kb_iter->pos, pos); + } + } + else { + for (kb_iter = key->block.last; kb_iter; kb_iter = kb_iter->prev) { + SWAP(float, kb_iter->pos, pos); + } + } + } + + /* First key is refkey, matches interface and BKE_key_sort */ + key->refkey = key->block.first; + } + + DAG_id_tag_update(&ob->id, OB_RECALC_DATA); + WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob); + + return OPERATOR_FINISHED; +} + +void PARTICLE_OT_shape_key_move(wmOperatorType *ot) +{ + static EnumPropertyItem slot_move[] = { + {-1, "UP", 0, "Up", ""}, + {1, "DOWN", 0, "Down", ""}, + {0, NULL, 0, NULL, NULL} + }; + + /* identifiers */ + ot->name = "Move Shape Key"; + ot->idname = "PARTICLE_OT_shape_key_move"; + ot->description = "Move the active shape key up/down in the list"; + + /* api callbacks */ + ot->poll = shape_key_mode_poll; + ot->exec = shape_key_move_exec; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + RNA_def_enum(ot->srna, "type", slot_move, 0, "Type", ""); +} diff --git a/source/blender/editors/physics/physics_intern.h b/source/blender/editors/physics/physics_intern.h index 666ed2397d2..1d68a429c06 100644 --- a/source/blender/editors/physics/physics_intern.h +++ b/source/blender/editors/physics/physics_intern.h @@ -61,6 +61,8 @@ void PARTICLE_OT_shape_cut(struct wmOperatorType *ot); void PARTICLE_OT_particle_edit_toggle(struct wmOperatorType *ot); void PARTICLE_OT_edited_clear(struct wmOperatorType *ot); +void PARTICLE_OT_shape_propagate_to_all(struct wmOperatorType *ot); + /* particle_object.c */ void OBJECT_OT_particle_system_add(struct wmOperatorType *ot); void OBJECT_OT_particle_system_remove(struct wmOperatorType *ot); @@ -79,6 +81,13 @@ void PARTICLE_OT_dupliob_remove(struct wmOperatorType *ot); void PARTICLE_OT_dupliob_move_up(struct wmOperatorType *ot); void PARTICLE_OT_dupliob_move_down(struct wmOperatorType *ot); +/* particle_shapekey.c */ +void PARTICLE_OT_shape_key_add(struct wmOperatorType *ot); +void PARTICLE_OT_shape_key_remove(struct wmOperatorType *ot); +void PARTICLE_OT_shape_key_clear(struct wmOperatorType *ot); +void PARTICLE_OT_shape_key_retime(struct wmOperatorType *ot); +void PARTICLE_OT_shape_key_move(struct wmOperatorType *ot); + /* particle_boids.c */ void BOID_OT_rule_add(struct wmOperatorType *ot); void BOID_OT_rule_del(struct wmOperatorType *ot); diff --git a/source/blender/editors/physics/physics_ops.c b/source/blender/editors/physics/physics_ops.c index c765bff796e..4d78e745606 100644 --- a/source/blender/editors/physics/physics_ops.c +++ b/source/blender/editors/physics/physics_ops.c @@ -69,6 +69,8 @@ static void operatortypes_particle(void) WM_operatortype_append(PARTICLE_OT_particle_edit_toggle); WM_operatortype_append(PARTICLE_OT_edited_clear); + WM_operatortype_append(PARTICLE_OT_shape_propagate_to_all); + WM_operatortype_append(OBJECT_OT_particle_system_add); WM_operatortype_append(OBJECT_OT_particle_system_remove); @@ -87,6 +89,12 @@ static void operatortypes_particle(void) WM_operatortype_append(PARTICLE_OT_dupliob_move_up); WM_operatortype_append(PARTICLE_OT_dupliob_move_down); + WM_operatortype_append(PARTICLE_OT_shape_key_add); + WM_operatortype_append(PARTICLE_OT_shape_key_remove); + WM_operatortype_append(PARTICLE_OT_shape_key_clear); + WM_operatortype_append(PARTICLE_OT_shape_key_retime); + WM_operatortype_append(PARTICLE_OT_shape_key_move); + WM_operatortype_append(RIGIDBODY_OT_object_add); WM_operatortype_append(RIGIDBODY_OT_object_remove); diff --git a/source/blender/editors/render/render_internal.c b/source/blender/editors/render/render_internal.c index 2ba1e615a9e..dc4c2c71878 100644 --- a/source/blender/editors/render/render_internal.c +++ b/source/blender/editors/render/render_internal.c @@ -1552,8 +1552,8 @@ void render_view3d_draw(RenderEngine *engine, const bContext *C) glColor4f(1.0f, 1.0f, 1.0f, 1.0f); glPixelZoom(scale_x, scale_y); glaDrawPixelsAuto(xof, yof, rres.rectx, rres.recty, - GL_RGBA, GL_UNSIGNED_BYTE, - GL_NEAREST, display_buffer); + GL_RGBA, GL_UNSIGNED_BYTE, GL_NEAREST, + 1.0f, display_buffer); glPixelZoom(1.0f, 1.0f); glDisable(GL_BLEND); diff --git a/source/blender/editors/render/render_opengl.c b/source/blender/editors/render/render_opengl.c index 5c69748e423..c265913f113 100644 --- a/source/blender/editors/render/render_opengl.c +++ b/source/blender/editors/render/render_opengl.c @@ -360,6 +360,7 @@ static void screen_opengl_render_doit(OGLRender *oglrender, RenderResult *rr) } else is_persp = true; + BKE_camera_to_gpu_dof(camera, &fx_settings); } else { diff --git a/source/blender/editors/screen/area.c b/source/blender/editors/screen/area.c index 7fe6518134e..541c7f21cdc 100644 --- a/source/blender/editors/screen/area.c +++ b/source/blender/editors/screen/area.c @@ -34,6 +34,7 @@ #include "MEM_guardedalloc.h" +#include "DNA_object_types.h" #include "DNA_userdef_types.h" #include "BLI_blenlib.h" @@ -44,6 +45,8 @@ #include "BKE_context.h" #include "BKE_global.h" +#include "BKE_main.h" +#include "BKE_scene.h" #include "BKE_screen.h" #include "RNA_access.h" @@ -56,6 +59,7 @@ #include "ED_screen.h" #include "ED_screen_types.h" #include "ED_space_api.h" +#include "ED_view3d.h" #include "BIF_gl.h" #include "BIF_glutil.h" @@ -69,6 +73,9 @@ #include "UI_resources.h" #include "UI_view2d.h" +#include "IMB_imbuf.h" +#include "IMB_imbuf_types.h" + #include "screen_intern.h" extern void ui_draw_anti_tria(float x1, float y1, float x2, float y2, float x3, float y3); /* xxx temp */ @@ -546,6 +553,100 @@ void ED_region_do_draw(bContext *C, ARegion *ar) * maybe silly, but let's try for now * to keep these tags protected * ********************************** */ +int ED_match_area_with_refresh(int spacetype, int refresh) +{ + switch (spacetype) { + case SPACE_TIME: + if (refresh & SPACE_TIME) + return 1; + break; + } + + return 0; +} + +int ED_match_region_with_redraws(int spacetype, int regiontype, int redraws) +{ + if (regiontype == RGN_TYPE_WINDOW) { + + switch (spacetype) { + case SPACE_VIEW3D: + if (redraws & TIME_ALL_3D_WIN) + return 1; + break; + case SPACE_IPO: + case SPACE_ACTION: + case SPACE_NLA: + if (redraws & TIME_ALL_ANIM_WIN) + return 1; + break; + case SPACE_TIME: + /* if only 1 window or 3d windows, we do timeline too */ + if (redraws & (TIME_ALL_ANIM_WIN | TIME_REGION | TIME_ALL_3D_WIN)) + return 1; + break; + case SPACE_BUTS: + if (redraws & TIME_ALL_BUTS_WIN) + return 1; + break; + case SPACE_SEQ: + if (redraws & (TIME_SEQ | TIME_ALL_ANIM_WIN)) + return 1; + break; + case SPACE_NODE: + if (redraws & (TIME_NODES)) + return 1; + break; + case SPACE_IMAGE: + if (redraws & TIME_ALL_IMAGE_WIN) + return 1; + break; + case SPACE_CLIP: + if (redraws & TIME_CLIPS) + return 1; + break; + + } + } + else if (regiontype == RGN_TYPE_CHANNELS) { + switch (spacetype) { + case SPACE_IPO: + case SPACE_ACTION: + case SPACE_NLA: + if (redraws & TIME_ALL_ANIM_WIN) + return 1; + break; + } + } + else if (regiontype == RGN_TYPE_UI) { + if (spacetype == SPACE_CLIP) { + /* Track Preview button is on Properties Editor in SpaceClip, + * and it's very common case when users want it be refreshing + * during playback, so asking people to enable special option + * for this is a bit tricky, so add exception here for refreshing + * Properties Editor for SpaceClip always */ + return 1; + } + + if (redraws & TIME_ALL_BUTS_WIN) + return 1; + } + else if (regiontype == RGN_TYPE_HEADER) { + if (spacetype == SPACE_TIME) + return 1; + } + else if (regiontype == RGN_TYPE_PREVIEW) { + switch (spacetype) { + case SPACE_SEQ: + if (redraws & (TIME_SEQ | TIME_ALL_ANIM_WIN)) + return 1; + break; + case SPACE_CLIP: + return 1; + } + } + return 0; +} void ED_region_tag_redraw(ARegion *ar) { @@ -1512,8 +1613,9 @@ void region_toggle_hidden(bContext *C, ARegion *ar, const bool do_fade) region_blend_start(C, sa, ar); } else { - if (ar->flag & RGN_FLAG_HIDDEN) + if (ar->flag & RGN_FLAG_HIDDEN) { WM_event_remove_handlers(C, &ar->handlers); + } ED_area_initialize(CTX_wm_manager(C), CTX_wm_window(C), sa); ED_area_tag_redraw(sa); @@ -2214,7 +2316,7 @@ void ED_region_image_metadata_draw(int x, int y, ImBuf *ibuf, rctf frame, float glTranslatef(x, y, 0.0f); glScalef(zoomx, zoomy, 1.0f); - BLF_size(blf_mono_font, style->widgetlabel.points * 1.5f, U.dpi); + BLF_size(blf_mono_font, style->widgetlabel.points, U.dpi); /* *** upper box*** */ @@ -2323,6 +2425,47 @@ void ED_region_grid_draw(ARegion *ar, float zoomx, float zoomy) glEnd(); } +/* uses the viewplane from the given camera and draws it as a backdrop */ +void ED_region_draw_backdrop_view3d(const bContext *C, struct Object *camera, const float alpha, + const float width, const float height, const float x, const float y, + const float zoomx, const float zoomy, const bool draw_background) +{ + Main *bmain = CTX_data_main(C); + Scene *scene = CTX_data_scene(C); + char err_out[256] = "unknown"; + struct ImBuf *ibuf; + + BKE_scene_update_for_newframe(bmain->eval_ctx, bmain, scene, scene->lay); + ibuf = ED_view3d_draw_offscreen_imbuf_simple(scene, camera, width, height, IB_rect, + OB_SOLID, false, false, false, + R_ADDSKY, NULL, err_out); + + if (ibuf == NULL) + return; + + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glPushMatrix(); + glScalef(zoomx, zoomy, 0.0f); + glTranslatef(x, y, 0.0f); + + /* draw background */ + if (draw_background) { + char col[4]; + + UI_GetThemeColorType4ubv(TH_HIGH_GRAD, SPACE_VIEW3D, col); + glColor4ub(UNPACK3(col), alpha * 255); + glRectf(0, 0, width, height); + } + /* draw the imbuf itself */ + glaDrawImBuf_glsl_ctx(C, ibuf, 0.0f, 0.0f, GL_NEAREST, alpha); + + glPopMatrix(); + glDisable(GL_BLEND); + + IMB_freeImBuf(ibuf); +} + /* If the area has overlapping regions, it returns visible rect for Region *ar */ /* rect gets returned in local region coordinates */ void ED_region_visible_rect(ARegion *ar, rcti *rect) diff --git a/source/blender/editors/screen/glutil.c b/source/blender/editors/screen/glutil.c index fd65d81baad..ca0fd17c5a7 100644 --- a/source/blender/editors/screen/glutil.c +++ b/source/blender/editors/screen/glutil.c @@ -422,6 +422,24 @@ void glutil_draw_filled_arc(float start, float angle, float radius, int nsegment glEnd(); } +void glutil_draw_filled_arc_part(float start, float angle, float radius, float radius_inn, int nsegments) +{ + int i; + + glBegin(GL_QUAD_STRIP); + glVertex2f(cosf(start) * radius_inn, sinf(start) * radius_inn); + glVertex2f(cosf(start) * radius, sinf(start) * radius); + for (i = 0; i < nsegments; i++) { + float t = (float) i / (nsegments - 1); + float cur = start + t * angle; + + glVertex2f(cosf(cur) * radius_inn, sinf(cur) * radius_inn); + glVertex2f(cosf(cur) * radius, sinf(cur) * radius); + } + glEnd(); +} + + void glutil_draw_lined_arc(float start, float angle, float radius, int nsegments) { int i; @@ -713,10 +731,11 @@ void glaDrawPixelsSafe(float x, float y, int img_w, int img_h, int row_w, int fo } /* uses either DrawPixelsSafe or DrawPixelsTex, based on user defined maximum */ -void glaDrawPixelsAuto(float x, float y, int img_w, int img_h, int format, int type, int zoomfilter, void *rect) +void glaDrawPixelsAuto(float x, float y, int img_w, int img_h, int format, + int type, int zoomfilter, float alpha, void *rect) { if (U.image_draw_method != IMAGE_DRAW_METHOD_DRAWPIXELS) { - glColor4f(1.0, 1.0, 1.0, 1.0); + glColor4f(1.0, 1.0, 1.0, alpha); glaDrawPixelsTex(x, y, img_w, img_h, format, type, zoomfilter, rect); } else { @@ -1057,7 +1076,7 @@ void bglFlush(void) /* **** Color management helper functions for GLSL display/transform ***** */ /* Draw given image buffer on a screen using GLSL for display transform */ -void glaDrawImBuf_glsl(ImBuf *ibuf, float x, float y, int zoomfilter, +void glaDrawImBuf_glsl(ImBuf *ibuf, float x, float y, int zoomfilter, float alpha, ColorManagedViewSettings *view_settings, ColorManagedDisplaySettings *display_settings) { @@ -1097,7 +1116,7 @@ void glaDrawImBuf_glsl(ImBuf *ibuf, float x, float y, int zoomfilter, if (ok) { glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); - glColor4f(1.0, 1.0, 1.0, 1.0); + glColor4f(1.0, 1.0, 1.0, alpha); if (ibuf->rect_float) { int format = 0; @@ -1135,20 +1154,20 @@ void glaDrawImBuf_glsl(ImBuf *ibuf, float x, float y, int zoomfilter, if (display_buffer) glaDrawPixelsAuto(x, y, ibuf->x, ibuf->y, GL_RGBA, GL_UNSIGNED_BYTE, - zoomfilter, display_buffer); + zoomfilter, alpha, display_buffer); IMB_display_buffer_release(cache_handle); } } -void glaDrawImBuf_glsl_ctx(const bContext *C, ImBuf *ibuf, float x, float y, int zoomfilter) +void glaDrawImBuf_glsl_ctx(const bContext *C, ImBuf *ibuf, float x, float y, int zoomfilter, float alpha) { ColorManagedViewSettings *view_settings; ColorManagedDisplaySettings *display_settings; IMB_colormanagement_display_settings_from_ctx(C, &view_settings, &display_settings); - glaDrawImBuf_glsl(ibuf, x, y, zoomfilter, view_settings, display_settings); + glaDrawImBuf_glsl(ibuf, x, y, zoomfilter, alpha, view_settings, display_settings); } void cpack(unsigned int x) diff --git a/source/blender/editors/screen/screen_context.c b/source/blender/editors/screen/screen_context.c index 87c0ce398e5..9b40b8b4464 100644 --- a/source/blender/editors/screen/screen_context.c +++ b/source/blender/editors/screen/screen_context.c @@ -80,7 +80,7 @@ const char *screen_context_dir[] = { "visible_pose_bones", "selected_pose_bones", "active_bone", "active_pose_bone", "active_base", "active_object", "object", "edit_object", "sculpt_object", "vertex_paint_object", "weight_paint_object", - "image_paint_object", "particle_edit_object", + "image_paint_object", "particle_edit_object", "hair_edit_object", "sequences", "selected_sequences", "selected_editable_sequences", /* sequencer */ "gpencil_data", "gpencil_data_owner", /* grease pencil data */ "visible_gpencil_layers", "editable_gpencil_layers", "editable_gpencil_strokes", @@ -378,6 +378,12 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult return 1; } + else if (CTX_data_equals(member, "hair_edit_object")) { + if (obact && (obact->mode & OB_MODE_HAIR_EDIT)) + CTX_data_id_pointer_set(result, &obact->id); + + return 1; + } else if (CTX_data_equals(member, "sequences")) { Editing *ed = BKE_sequencer_editing_get(scene, false); if (ed) { diff --git a/source/blender/editors/screen/screen_edit.c b/source/blender/editors/screen/screen_edit.c index 0c40c833c0d..a30b840a136 100644 --- a/source/blender/editors/screen/screen_edit.c +++ b/source/blender/editors/screen/screen_edit.c @@ -1079,6 +1079,8 @@ static void region_cursor_set(wmWindow *win, int swinid, int swin_changed) for (; ar; ar = ar->next) { if (ar->swinid == swinid) { if (swin_changed || (ar->type && ar->type->event_cursor)) { + if (WM_widgetmap_cursor_set(ar->widgetmaps.first, win)) + return; if (ar->type && ar->type->cursor) ar->type->cursor(win, sa, ar); else @@ -2202,7 +2204,7 @@ bool ED_screen_stereo3d_required(bScreen *screen) return true; } - if (sseq->draw_flag & SEQ_DRAW_BACKDROP) { + if (sseq->draw_flag & SEQ_DRAW_OVERDROP) { return true; } diff --git a/source/blender/editors/screen/screen_ops.c b/source/blender/editors/screen/screen_ops.c index 52eb14aab8d..586811031c1 100644 --- a/source/blender/editors/screen/screen_ops.c +++ b/source/blender/editors/screen/screen_ops.c @@ -3319,101 +3319,6 @@ static void SCREEN_OT_header_toolbox(wmOperatorType *ot) /* ****************** anim player, with timer ***************** */ -static int match_area_with_refresh(int spacetype, int refresh) -{ - switch (spacetype) { - case SPACE_TIME: - if (refresh & SPACE_TIME) - return 1; - break; - } - - return 0; -} - -static int match_region_with_redraws(int spacetype, int regiontype, int redraws) -{ - if (regiontype == RGN_TYPE_WINDOW) { - - switch (spacetype) { - case SPACE_VIEW3D: - if (redraws & TIME_ALL_3D_WIN) - return 1; - break; - case SPACE_IPO: - case SPACE_ACTION: - case SPACE_NLA: - if (redraws & TIME_ALL_ANIM_WIN) - return 1; - break; - case SPACE_TIME: - /* if only 1 window or 3d windows, we do timeline too */ - if (redraws & (TIME_ALL_ANIM_WIN | TIME_REGION | TIME_ALL_3D_WIN)) - return 1; - break; - case SPACE_BUTS: - if (redraws & TIME_ALL_BUTS_WIN) - return 1; - break; - case SPACE_SEQ: - if (redraws & (TIME_SEQ | TIME_ALL_ANIM_WIN)) - return 1; - break; - case SPACE_NODE: - if (redraws & (TIME_NODES)) - return 1; - break; - case SPACE_IMAGE: - if (redraws & TIME_ALL_IMAGE_WIN) - return 1; - break; - case SPACE_CLIP: - if (redraws & TIME_CLIPS) - return 1; - break; - - } - } - else if (regiontype == RGN_TYPE_CHANNELS) { - switch (spacetype) { - case SPACE_IPO: - case SPACE_ACTION: - case SPACE_NLA: - if (redraws & TIME_ALL_ANIM_WIN) - return 1; - break; - } - } - else if (regiontype == RGN_TYPE_UI) { - if (spacetype == SPACE_CLIP) { - /* Track Preview button is on Properties Editor in SpaceClip, - * and it's very common case when users want it be refreshing - * during playback, so asking people to enable special option - * for this is a bit tricky, so add exception here for refreshing - * Properties Editor for SpaceClip always */ - return 1; - } - - if (redraws & TIME_ALL_BUTS_WIN) - return 1; - } - else if (regiontype == RGN_TYPE_HEADER) { - if (spacetype == SPACE_TIME) - return 1; - } - else if (regiontype == RGN_TYPE_PREVIEW) { - switch (spacetype) { - case SPACE_SEQ: - if (redraws & (TIME_SEQ | TIME_ALL_ANIM_WIN)) - return 1; - break; - case SPACE_CLIP: - return 1; - } - } - return 0; -} - //#define PROFILE_AUDIO_SYNCH static int screen_animation_step(bContext *C, wmOperator *UNUSED(op), const wmEvent *event) @@ -3445,13 +3350,7 @@ static int screen_animation_step(bContext *C, wmOperator *UNUSED(op), const wmEv (sad->flag & ANIMPLAY_FLAG_REVERSE) == false && finite(time = BKE_sound_sync_scene(scene))) { - double newfra = (double)time * FPS; - - /* give some space here to avoid jumps */ - if (newfra + 0.5 > scene->r.cfra && newfra - 0.5 < scene->r.cfra) - scene->r.cfra++; - else - scene->r.cfra = newfra + 0.5; + scene->r.cfra = (double)time * FPS; #ifdef PROFILE_AUDIO_SYNCH newfra_int = scene->r.cfra; @@ -3548,7 +3447,7 @@ static int screen_animation_step(bContext *C, wmOperator *UNUSED(op), const wmEv if (ar == sad->ar) { redraw = true; } - else if (match_region_with_redraws(sa->spacetype, ar->regiontype, sad->redraws)) { + else if (ED_match_region_with_redraws(sa->spacetype, ar->regiontype, sad->redraws)) { redraw = true; } @@ -3574,7 +3473,7 @@ static int screen_animation_step(bContext *C, wmOperator *UNUSED(op), const wmEv } } - if (match_area_with_refresh(sa->spacetype, sad->refresh)) + if (ED_match_area_with_refresh(sa->spacetype, sad->refresh)) ED_area_tag_refresh(sa); } } @@ -3618,6 +3517,19 @@ bScreen *ED_screen_animation_playing(const wmWindowManager *wm) wmWindow *win; for (win = wm->windows.first; win; win = win->next) { + if (win->screen->animtimer || win->screen->scrubbing) { + return win->screen; + } + } + + return NULL; +} + +bScreen *ED_screen_animation_no_scrub(const wmWindowManager *wm) +{ + wmWindow *win; + + for (win = wm->windows.first; win; win = win->next) { if (win->screen->animtimer) { return win->screen; } @@ -3626,6 +3538,7 @@ bScreen *ED_screen_animation_playing(const wmWindowManager *wm) return NULL; } + /* toggle operator */ int ED_screen_animation_play(bContext *C, int sync, int mode) { @@ -4073,8 +3986,9 @@ void region_blend_start(bContext *C, ScrArea *sa, ARegion *ar) /* blend in, reinitialize regions because it got unhidden */ if (rgi->hidden == 0) ED_area_initialize(wm, win, sa); - else + else { WM_event_remove_handlers(C, &ar->handlers); + } if (ar->next) { if (ar->next->alignment & RGN_SPLIT_PREV) { diff --git a/source/blender/editors/sculpt_paint/paint_ops.c b/source/blender/editors/sculpt_paint/paint_ops.c index eebd49895ef..b95951a4d7a 100644 --- a/source/blender/editors/sculpt_paint/paint_ops.c +++ b/source/blender/editors/sculpt_paint/paint_ops.c @@ -44,6 +44,7 @@ #include "ED_screen.h" #include "ED_image.h" #include "UI_resources.h" +#include "UI_interface.h" #include "WM_api.h" #include "WM_types.h" @@ -60,19 +61,39 @@ #include <stddef.h> /* Brush operators */ + static int brush_add_exec(bContext *C, wmOperator *UNUSED(op)) { /*int type = RNA_enum_get(op->ptr, "type");*/ - Paint *paint = BKE_paint_get_active_from_context(C); - Brush *br = BKE_paint_brush(paint); Main *bmain = CTX_data_main(C); - + Scene *scene = CTX_data_scene(C); + Object *ob = CTX_data_active_object(C); + Paint *paint = NULL; + HairEditSettings *hair_edit = NULL; + Brush *br = NULL; + + /* get active brush context */ + if (ob->mode == OB_MODE_HAIR_EDIT) { + hair_edit = &scene->toolsettings->hair_edit; + br = hair_edit->brush; + } + else { + paint = BKE_paint_get_active_from_context(C); + br = BKE_paint_brush(paint); + } + if (br) br = BKE_brush_copy(br); else br = BKE_brush_add(bmain, "Brush"); - BKE_paint_brush_set(paint, br); + /* set new brush pointer in the context */ + if (ob->mode == OB_MODE_HAIR_EDIT) { + hair_edit->brush = br; + } + else { + BKE_paint_brush_set(paint, br); + } return OPERATOR_FINISHED; } @@ -95,11 +116,20 @@ static void BRUSH_OT_add(wmOperatorType *ot) static int brush_scale_size_exec(bContext *C, wmOperator *op) { Scene *scene = CTX_data_scene(C); - Paint *paint = BKE_paint_get_active_from_context(C); - Brush *brush = BKE_paint_brush(paint); - // Object *ob = CTX_data_active_object(C); + Object *ob = CTX_data_active_object(C); + Brush *brush; float scalar = RNA_float_get(op->ptr, "scalar"); + /* get active brush context */ + if (ob->mode == OB_MODE_HAIR_EDIT) { + HairEditSettings *hair_edit = &scene->toolsettings->hair_edit; + brush = hair_edit->brush; + } + else { + Paint *paint = BKE_paint_get_active_from_context(C); + brush = BKE_paint_brush(paint); + } + if (brush) { // pixel radius { @@ -467,7 +497,7 @@ static int brush_select_exec(bContext *C, wmOperator *op) Object *ob = CTX_data_active_object(C); if (ob) { /* select current paint mode */ - paint_mode = ob->mode & OB_MODE_ALL_PAINT; + paint_mode = ob->mode & OB_MODE_ALL_BRUSH; } else { return OPERATOR_CANCELLED; diff --git a/source/blender/editors/space_action/action_edit.c b/source/blender/editors/space_action/action_edit.c index 90c38426164..a28b4c1a4f2 100644 --- a/source/blender/editors/space_action/action_edit.c +++ b/source/blender/editors/space_action/action_edit.c @@ -898,7 +898,7 @@ void ACTION_OT_delete(wmOperatorType *ot) /* ******************** Clean Keyframes Operator ************************* */ -static void clean_action_keys(bAnimContext *ac, float thresh) +static void clean_action_keys(bAnimContext *ac, float thresh, bool clean_chan) { ListBase anim_data = {NULL, NULL}; bAnimListElem *ale; @@ -910,7 +910,7 @@ static void clean_action_keys(bAnimContext *ac, float thresh) /* loop through filtered data and clean curves */ for (ale = anim_data.first; ale; ale = ale->next) { - clean_fcurve((FCurve *)ale->key_data, thresh); + clean_fcurve(ac, ale, thresh, clean_chan); ale->update |= ANIM_UPDATE_DEFAULT; } @@ -925,6 +925,7 @@ static int actkeys_clean_exec(bContext *C, wmOperator *op) { bAnimContext ac; float thresh; + bool clean_chan; /* get editor data */ if (ANIM_animdata_get_context(C, &ac) == 0) @@ -937,9 +938,10 @@ static int actkeys_clean_exec(bContext *C, wmOperator *op) /* get cleaning threshold */ thresh = RNA_float_get(op->ptr, "threshold"); + clean_chan = RNA_boolean_get(op->ptr, "channels"); /* clean keyframes */ - clean_action_keys(&ac, thresh); + clean_action_keys(&ac, thresh, clean_chan); /* set notifier that keyframes have changed */ WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL); @@ -964,6 +966,7 @@ void ACTION_OT_clean(wmOperatorType *ot) /* properties */ ot->prop = RNA_def_float(ot->srna, "threshold", 0.001f, 0.0f, FLT_MAX, "Threshold", "", 0.0f, 1000.0f); + RNA_def_boolean(ot->srna, "channels", false, "Channels", ""); } /* ******************** Sample Keyframes Operator *********************** */ diff --git a/source/blender/editors/space_api/spacetypes.c b/source/blender/editors/space_api/spacetypes.c index 01f0d1ae54f..d827761f473 100644 --- a/source/blender/editors/space_api/spacetypes.c +++ b/source/blender/editors/space_api/spacetypes.c @@ -103,6 +103,7 @@ void ED_spacetypes_init(void) ED_operatortypes_anim(); ED_operatortypes_animchannels(); ED_operatortypes_gpencil(); + ED_operatortypes_hair(); ED_operatortypes_object(); ED_operatortypes_mesh(); ED_operatortypes_sculpt(); @@ -135,9 +136,6 @@ void ED_spacetypes_init(void) void ED_spacemacros_init(void) { - const ListBase *spacetypes; - SpaceType *type; - /* Macros's must go last since they reference other operators. * We need to have them go after python operators too */ ED_operatormacros_armature(); @@ -154,15 +152,24 @@ void ED_spacemacros_init(void) ED_operatormacros_sequencer(); ED_operatormacros_paint(); ED_operatormacros_gpencil(); +} + +void ED_spacedropwidgets_init(void) +{ + const ListBase *spacetypes; + SpaceType *type; /* register dropboxes (can use macros) */ spacetypes = BKE_spacetypes_list(); for (type = spacetypes->first; type; type = type->next) { if (type->dropboxes) type->dropboxes(); + if (type->widgets) + type->widgets(); } } + /* called in wm.c */ /* keymap definitions are registered only once per WM initialize, usually on file read, * using the keymap the actual areas/regions add the handlers */ @@ -176,6 +183,7 @@ void ED_spacetypes_keymap(wmKeyConfig *keyconf) ED_keymap_anim(keyconf); ED_keymap_animchannels(keyconf); ED_keymap_gpencil(keyconf); + ED_keymap_hair(keyconf); ED_keymap_object(keyconf); /* defines lattice also */ ED_keymap_mesh(keyconf); ED_keymap_uvedit(keyconf); diff --git a/source/blender/editors/space_clip/clip_draw.c b/source/blender/editors/space_clip/clip_draw.c index ce248f1eb10..6bd7788a958 100644 --- a/source/blender/editors/space_clip/clip_draw.c +++ b/source/blender/editors/space_clip/clip_draw.c @@ -307,7 +307,7 @@ static void draw_movieclip_buffer(const bContext *C, SpaceClip *sc, ARegion *ar, /* set zoom */ glPixelZoom(zoomx * width / ibuf->x, zoomy * height / ibuf->y); - glaDrawImBuf_glsl_ctx(C, ibuf, x, y, filter); + glaDrawImBuf_glsl_ctx(C, ibuf, x, y, filter, 1.0f); /* reset zoom */ glPixelZoom(1.0f, 1.0f); diff --git a/source/blender/editors/space_clip/clip_ops.c b/source/blender/editors/space_clip/clip_ops.c index 1d398177d9f..232b2fe7e1e 100644 --- a/source/blender/editors/space_clip/clip_ops.c +++ b/source/blender/editors/space_clip/clip_ops.c @@ -999,7 +999,7 @@ void CLIP_OT_change_frame(wmOperatorType *ot) ot->poll = change_frame_poll; /* flags */ - ot->flag = OPTYPE_BLOCKING | OPTYPE_UNDO; + ot->flag = OPTYPE_BLOCKING; /* rna */ RNA_def_int(ot->srna, "frame", 0, MINAFRAME, MAXFRAME, "Frame", "", MINAFRAME, MAXFRAME); diff --git a/source/blender/editors/space_file/file_draw.c b/source/blender/editors/space_file/file_draw.c index 0e4d8857663..db10aa14060 100644 --- a/source/blender/editors/space_file/file_draw.c +++ b/source/blender/editors/space_file/file_draw.c @@ -64,6 +64,7 @@ #include "UI_resources.h" #include "UI_view2d.h" +#include "WM_api.h" #include "WM_types.h" #include "filelist.h" @@ -254,8 +255,13 @@ static int get_file_icon(struct direntry *file) return ICON_FILE_BLEND; else if (file->flags & FILE_TYPE_BLENDER_BACKUP) return ICON_FILE_BACKUP; - else if (file->flags & FILE_TYPE_IMAGE) - return ICON_FILE_IMAGE; + else if (file->flags & FILE_TYPE_IMAGE) { + if (file->selflag & FILE_SEL_COLLAPSED) { + return ICON_FILE_MOVIE; + } + else + return ICON_FILE_IMAGE; + } else if (file->flags & FILE_TYPE_MOVIE) return ICON_FILE_MOVIE; else if (file->flags & FILE_TYPE_PYSCRIPT) @@ -330,7 +336,7 @@ void file_calc_previews(const bContext *C, ARegion *ar) UI_view2d_totRect_set(v2d, sfile->layout->width, sfile->layout->height); } -static void file_draw_preview(uiBlock *block, struct direntry *file, int sx, int sy, ImBuf *imb, FileLayout *layout, bool is_icon, bool drag) +static void file_draw_preview(uiBlock *block, struct direntry *file, int sx, int sy, ImBuf *imb, FileLayout *layout, bool is_icon, bool drag, bool play) { uiBut *but; float fx, fy; @@ -406,6 +412,24 @@ static void file_draw_preview(uiBlock *block, struct direntry *file, int sx, int UI_but_drag_set_image(but, file->path, get_file_icon(file), imb, scale); } + if (play && !is_icon) { + float r = MIN2(ey, ex) / 6.0f; + float yr = sqrt(3) / 2 * r; + float xr = r / 2; + glPushMatrix(); + glTranslatef(xco + ex / 2.0f, yco + ey / 2.0f, 0.0); + glColor4f(1.0f, 1.0f, 1.0f, 0.8); + glutil_draw_filled_arc(0.0f, 2.0f * M_PI, ey / 4.0f, 32); + glColor4f(0.3f, 0.3f, 1.0f, 0.8f); + glutil_draw_filled_arc_part(0.0f, 2.0f * M_PI, ey / 4.0f, ey / 5.0f, 32); + glBegin(GL_TRIANGLES); + glVertex2f(-xr, yr); + glVertex2f(-xr, -yr); + glVertex2f(r, 0.0f); + glEnd(); + glPopMatrix(); + } + glDisable(GL_BLEND); } @@ -503,6 +527,7 @@ void file_draw_list(const bContext *C, ARegion *ar) bool is_icon; short align; bool do_drag; + bool do_play; int column_space = 0.6f * UI_UNIT_X; numfiles = filelist_numfiles(files); @@ -560,14 +585,24 @@ void file_draw_list(const bContext *C, ARegion *ar) do_drag = !(FILENAME_IS_CURRPAR(file->relname)); if (FILE_IMGDISPLAY == params->display) { - is_icon = 0; + is_icon = false; imb = filelist_getimage(files, i); if (!imb) { imb = filelist_geticon(files, i); - is_icon = 1; + is_icon = true; } - file_draw_preview(block, file, sx, sy, imb, layout, is_icon, do_drag); + do_play = (file->selflag & FILE_SEL_COLLAPSED) && !(file->selflag & FILE_SEL_PLAYING); + + file_draw_preview(block, file, sx, sy, imb, layout, is_icon, do_drag, do_play); + + if ((file->selflag & FILE_SEL_COLLAPSED) && (file->selflag & FILE_SEL_PLAYING)) { + /* refresh to keep movie playing */ + file->collapsed_info.curfra++; + file->collapsed_info.curfra %= file->collapsed_info.totfiles; + + WM_event_add_notifier(C, NC_SPACE | ND_SPACE_FILE_PARAMS, NULL); + } } else { file_draw_icon(block, file->path, sx, sy - (UI_UNIT_Y / 6), get_file_icon(file), ICON_DEFAULT_WIDTH_SCALE, ICON_DEFAULT_HEIGHT_SCALE, do_drag); @@ -605,43 +640,71 @@ void file_draw_list(const bContext *C, ARegion *ar) if (!(file->selflag & FILE_SEL_EDITING)) { int tpos = (FILE_IMGDISPLAY == params->display) ? sy - layout->tile_h + layout->textheight : sy; - file_draw_string(sx + 1, tpos, file->relname, (float)textwidth, textheight, align); + if (file->selflag & FILE_SEL_COLLAPSED) { + char fname[PATH_MAX]; + char finalname[PATH_MAX]; + char ext[PATH_MAX]; + CollapsedEntry *collapsed = &file->collapsed_info; + BLI_strncpy(fname, file->relname, sizeof(fname)); + BLI_path_frame_strip(fname, false, ext); + BLI_snprintf(finalname, sizeof(finalname), "%s%.*d-%.*d%s", + fname, collapsed->numdigits, collapsed->minframe, collapsed->numdigits, collapsed->maxframe, ext); + file_draw_string(sx + 1, tpos, finalname, (float)textwidth, textheight, align); + } + else + file_draw_string(sx + 1, tpos, file->relname, (float)textwidth, textheight, align); } if (params->display == FILE_SHORTDISPLAY) { sx += (int)layout->column_widths[COLUMN_NAME] + column_space; if (!(file->type & S_IFDIR)) { - file_draw_string(sx, sy, file->size, layout->column_widths[COLUMN_SIZE], layout->tile_h, align); + if (file->selflag & FILE_SEL_COLLAPSED) { + CollapsedEntry *collapsed = &file->collapsed_info; + char sizestr[16]; + BLI_file_size_string(collapsed->totalsize, sizestr, sizeof(sizestr)); + file_draw_string(sx, sy, sizestr, layout->column_widths[COLUMN_SIZE], layout->tile_h, align); + } + else + file_draw_string(sx, sy, file->size, layout->column_widths[COLUMN_SIZE], layout->tile_h, align); sx += (int)layout->column_widths[COLUMN_SIZE] + column_space; } } else if (params->display == FILE_LONGDISPLAY) { sx += (int)layout->column_widths[COLUMN_NAME] + column_space; + /* for collapsed files it doesn't make sense to display all info */ + if (file->selflag & FILE_SEL_COLLAPSED) { + char sizestr[16]; + CollapsedEntry *collapsed = &file->collapsed_info; + BLI_file_size_string(collapsed->totalsize, sizestr, sizeof(sizestr)); + file_draw_string(sx, sy, sizestr, layout->column_widths[COLUMN_SIZE], layout->tile_h, align); + } + else { #ifndef WIN32 - /* rwx rwx rwx */ - file_draw_string(sx, sy, file->mode1, layout->column_widths[COLUMN_MODE1], layout->tile_h, align); - sx += layout->column_widths[COLUMN_MODE1] + column_space; + /* rwx rwx rwx */ + file_draw_string(sx, sy, file->mode1, layout->column_widths[COLUMN_MODE1], layout->tile_h, align); + sx += layout->column_widths[COLUMN_MODE1] + column_space; - file_draw_string(sx, sy, file->mode2, layout->column_widths[COLUMN_MODE2], layout->tile_h, align); - sx += layout->column_widths[COLUMN_MODE2] + column_space; + file_draw_string(sx, sy, file->mode2, layout->column_widths[COLUMN_MODE2], layout->tile_h, align); + sx += layout->column_widths[COLUMN_MODE2] + column_space; - file_draw_string(sx, sy, file->mode3, layout->column_widths[COLUMN_MODE3], layout->tile_h, align); - sx += layout->column_widths[COLUMN_MODE3] + column_space; + file_draw_string(sx, sy, file->mode3, layout->column_widths[COLUMN_MODE3], layout->tile_h, align); + sx += layout->column_widths[COLUMN_MODE3] + column_space; - file_draw_string(sx, sy, file->owner, layout->column_widths[COLUMN_OWNER], layout->tile_h, align); - sx += layout->column_widths[COLUMN_OWNER] + column_space; + file_draw_string(sx, sy, file->owner, layout->column_widths[COLUMN_OWNER], layout->tile_h, align); + sx += layout->column_widths[COLUMN_OWNER] + column_space; #endif - file_draw_string(sx, sy, file->date, layout->column_widths[COLUMN_DATE], layout->tile_h, align); - sx += (int)layout->column_widths[COLUMN_DATE] + column_space; + file_draw_string(sx, sy, file->date, layout->column_widths[COLUMN_DATE], layout->tile_h, align); + sx += (int)layout->column_widths[COLUMN_DATE] + column_space; - file_draw_string(sx, sy, file->time, layout->column_widths[COLUMN_TIME], layout->tile_h, align); - sx += (int)layout->column_widths[COLUMN_TIME] + column_space; + file_draw_string(sx, sy, file->time, layout->column_widths[COLUMN_TIME], layout->tile_h, align); + sx += (int)layout->column_widths[COLUMN_TIME] + column_space; - if (!(file->type & S_IFDIR)) { - file_draw_string(sx, sy, file->size, layout->column_widths[COLUMN_SIZE], layout->tile_h, align); - sx += (int)layout->column_widths[COLUMN_SIZE] + column_space; + if (!(file->type & S_IFDIR)) { + file_draw_string(sx, sy, file->size, layout->column_widths[COLUMN_SIZE], layout->tile_h, align); + sx += (int)layout->column_widths[COLUMN_SIZE] + column_space; + } } } } diff --git a/source/blender/editors/space_file/file_ops.c b/source/blender/editors/space_file/file_ops.c index f8d13bb6adb..19dbaefa258 100644 --- a/source/blender/editors/space_file/file_ops.c +++ b/source/blender/editors/space_file/file_ops.c @@ -224,7 +224,7 @@ static FileSelect file_select(bContext *C, const rcti *rect, FileSelType select, /* flag the files as selected in the filelist */ filelist_select(sfile->files, &sel, select, FILE_SEL_SELECTED, check_type); - + /* Don't act on multiple selected files */ if (sel.first != sel.last) select = 0; @@ -274,6 +274,7 @@ static int file_border_select_modal(bContext *C, wmOperator *op, const wmEvent * if (FILENAME_IS_CURRPAR(file->relname)) { file->selflag &= ~FILE_SEL_HIGHLIGHTED; + file->collapsed_info.curfra = 0; } /* active_file gets highlighted as well - make sure it is no readonly file */ @@ -370,7 +371,15 @@ static int file_select_invoke(bContext *C, wmOperator *op, const wmEvent *event) } else { /* single select, deselect all selected first */ - if (!extend) file_deselect_all(sfile, FILE_SEL_SELECTED); + if (!(file->type & S_IFDIR) && (file->selflag & FILE_SEL_COLLAPSED)&& (file->selflag & FILE_SEL_SELECTED)) { + if (file->selflag & FILE_SEL_PLAYING) { + file->collapsed_info.curfra = 0; + } + file->selflag ^= FILE_SEL_PLAYING; + } + if (!extend) { + file_deselect_all(sfile, FILE_SEL_SELECTED); + } } } } @@ -803,6 +812,17 @@ int file_cancel_exec(bContext *C, wmOperator *UNUSED(unused)) SpaceFile *sfile = CTX_wm_space_file(C); wmOperator *op = sfile->op; + if (op) { + PropertyRNA *prop; + if ((prop = RNA_struct_find_property(op->ptr, "collapse_images")) && + (sfile->params->flag & FILE_COLLAPSE_IMAGES_TMP)) + { + /* turn off collapsed flag if evoked from operator */ + sfile->params->flag &= ~FILE_COLLAPSE_IMAGES_TMP; + sfile->params->flag &= ~FILE_COLLAPSE_IMAGES; + } + } + sfile->op = NULL; WM_event_fileselect_event(wm, op, EVT_FILESELECT_CANCEL); @@ -868,9 +888,26 @@ void file_sfile_to_operator(wmOperator *op, SpaceFile *sfile, char *filepath) for (i = 0; i < numfiles; i++) { if (filelist_is_selected(sfile->files, i, CHECK_FILES)) { struct direntry *file = filelist_file(sfile->files, i); - RNA_property_collection_add(op->ptr, prop, &itemptr); - RNA_string_set(&itemptr, "name", file->relname); - num_files++; + + if (file->selflag & FILE_SEL_COLLAPSED) { + int j = 0; + CollapsedEntry *collapsed = &file->collapsed_info; + + for (; j < collapsed->totfiles; j++) { + struct direntry *file_tmp = collapsed->darray[j]; + RNA_property_collection_add(op->ptr, prop, &itemptr); + RNA_string_set(&itemptr, "name", file_tmp->relname); + num_files++; + } + + MEM_freeN(collapsed->darray); + collapsed->darray = NULL; + } + else { + RNA_property_collection_add(op->ptr, prop, &itemptr); + RNA_string_set(&itemptr, "name", file->relname); + num_files++; + } } } /* make sure the file specified in the filename button is added even if no files selected */ @@ -900,7 +937,13 @@ void file_sfile_to_operator(wmOperator *op, SpaceFile *sfile, char *filepath) } } - + if ((prop = RNA_struct_find_property(op->ptr, "collapse_images")) && + (sfile->params->flag & FILE_COLLAPSE_IMAGES_TMP)) + { + /* turn off collapsed flag if evoked from operator */ + sfile->params->flag &= ~FILE_COLLAPSE_IMAGES_TMP; + sfile->params->flag &= ~FILE_COLLAPSE_IMAGES; + } } } @@ -1673,9 +1716,11 @@ static int file_rename_exec(bContext *C, wmOperator *UNUSED(op)) int numfiles = filelist_numfiles(sfile->files); if ( (0 <= idx) && (idx < numfiles) ) { struct direntry *file = filelist_file(sfile->files, idx); - filelist_select_file(sfile->files, idx, FILE_SEL_ADD, FILE_SEL_EDITING, CHECK_ALL); - BLI_strncpy(sfile->params->renameedit, file->relname, FILE_MAXFILE); - sfile->params->renamefile[0] = '\0'; + if (!(file->selflag & FILE_SEL_COLLAPSED)) { + filelist_select_file(sfile->files, idx, FILE_SEL_ADD, FILE_SEL_EDITING, CHECK_ALL); + BLI_strncpy(sfile->params->renameedit, file->relname, FILE_MAXFILE); + sfile->params->renamefile[0] = '\0'; + } } ED_area_tag_redraw(sa); } diff --git a/source/blender/editors/space_file/filelist.c b/source/blender/editors/space_file/filelist.c index af65149ff9c..ee42e14c86c 100644 --- a/source/blender/editors/space_file/filelist.c +++ b/source/blender/editors/space_file/filelist.c @@ -35,6 +35,7 @@ #include <stdlib.h> #include <math.h> #include <string.h> +#include <ctype.h> #ifndef WIN32 # include <unistd.h> @@ -49,6 +50,7 @@ #include "BLI_fnmatch.h" #include "BLI_linklist.h" #include "BLI_utildefines.h" +#include "BLI_ghash.h" #ifdef WIN32 # include "BLI_winstuff.h" @@ -208,9 +210,11 @@ typedef struct FileImage { typedef struct FileListFilter { bool hide_dot; bool hide_parent; + bool collapse_ima_seq; unsigned int filter; char filter_glob[64]; - char filter_search[66]; /* + 2 for heading/trailing implicit '*' wildcards. */ + char filter_search[66]; /* + 2 for heading/trailing implicit '*' wildcards. */ + GHash *unique_image_list; /* hash that stores unique filename */ } FileListFilter; typedef struct FileList { @@ -464,6 +468,55 @@ static bool is_filtered_file(struct direntry *file, const char *UNUSED(root), Fi } } + if (is_filtered && !(file->type & S_IFDIR) && filter->collapse_ima_seq) { + if (file->relname) { + struct direntry *ofile; + int frame, numdigits; + + if (BLI_path_frame_get(file->relname, &frame, &numdigits)) { + char filename[PATH_MAX]; + + BLI_strncpy(filename, file->relname, sizeof(filename)); + BLI_path_frame_strip(filename, false, NULL); + + if ((ofile = BLI_ghash_lookup(filter->unique_image_list, filename)) && + numdigits == ofile->collapsed_info.numdigits) + { + CollapsedEntry *collapsed = &ofile->collapsed_info; + is_filtered = false; + ofile->selflag |= FILE_SEL_COLLAPSED; + file->selflag |= FILE_SEL_COLLAPSED; + file->frame = frame; + BLI_addhead(&collapsed->list, BLI_genericNodeN(file)); + collapsed->totalsize += file->realsize; + collapsed->maxframe = MAX2(frame, collapsed->maxframe); + collapsed->minframe = MIN2(frame, collapsed->minframe); + collapsed->totfiles++; + } + else { + if (file->collapsed_info.darray) { + MEM_freeN(file->collapsed_info.darray); + file->collapsed_info.darray = NULL; + } + BLI_ghash_insert(filter->unique_image_list, BLI_strdup(filename), file); + file->frame = frame; + file->collapsed_info.totalsize = file->realsize; + file->collapsed_info.maxframe = file->collapsed_info.minframe = frame; + file->collapsed_info.numdigits = numdigits; + file->collapsed_info.totfiles = 1; + } + } + } + } + else { + if (file->collapsed_info.darray) { + MEM_freeN(file->collapsed_info.darray); + file->collapsed_info.darray = NULL; + } + /* may have been set in a previous filtering iteration, so always clear */ + file->selflag &= ~FILE_SEL_COLLAPSED; + } + return is_filtered; } @@ -500,6 +553,17 @@ static void filelist_filter_clear(FileList *filelist) filelist->numfiltered = 0; } +static int compareFrame(const void *pa, const void *pb) +{ + struct direntry *a = *((struct direntry **)pa); + struct direntry *b = *((struct direntry **)pb); + if (a->frame < b->frame) + return -1; + if (a->frame > b->frame) + return 1; + return 0; +} + void filelist_filter(FileList *filelist) { int num_filtered = 0; @@ -517,6 +581,11 @@ void filelist_filter(FileList *filelist) fidx_tmp = MEM_mallocN(sizeof(*fidx_tmp) * (size_t)filelist->numfiles, __func__); + /* */ + if (filelist->filter_data.collapse_ima_seq) { + filelist->filter_data.unique_image_list = BLI_ghash_str_new("image_seq_hash"); + } + /* Filter remap & count how many files are left after filter in a single loop. */ for (i = 0; i < filelist->numfiles; ++i) { struct direntry *file = &filelist->filelist[i]; @@ -526,6 +595,33 @@ void filelist_filter(FileList *filelist) } } + if (filelist->filter_data.unique_image_list) { + BLI_ghash_free(filelist->filter_data.unique_image_list, MEM_freeN, NULL); + filelist->filter_data.unique_image_list = NULL; + } + + /* extra step, need to sort the file list according to frame */ + if (filelist->filter_data.collapse_ima_seq) { + for (i = 0; i < num_filtered; i++) { + struct direntry *file = &filelist->filelist[fidx_tmp[i]]; + if (file->selflag & FILE_SEL_COLLAPSED) { + LinkData *fdata = file->collapsed_info.list.first; + int j = 1; + file->collapsed_info.darray = + MEM_mallocN(sizeof(struct direntry *) * file->collapsed_info.totfiles, "collapsed files"); + file->collapsed_info.darray[0] = file; + + for (; fdata; fdata = fdata->next, j++) { + file->collapsed_info.darray[j] = fdata->data; + } + qsort(file->collapsed_info.darray, file->collapsed_info.totfiles, sizeof(struct direntry *), compareFrame); + if (file->collapsed_info.list.first) { + BLI_freelistN(&file->collapsed_info.list); + } + } + } + } + /* Note: maybe we could even accept filelist->fidx to be filelist->numfiles -len allocated? */ filelist->fidx = MEM_mallocN(sizeof(*filelist->fidx) * (size_t)num_filtered, __func__); memcpy(filelist->fidx, fidx_tmp, sizeof(*filelist->fidx) * (size_t)num_filtered); @@ -535,17 +631,20 @@ void filelist_filter(FileList *filelist) } void filelist_setfilter_options(FileList *filelist, const bool hide_dot, const bool hide_parent, + const bool collapse_ima_seq, const unsigned int filter, const char *filter_glob, const char *filter_search) { if ((filelist->filter_data.hide_dot != hide_dot) || (filelist->filter_data.hide_parent != hide_parent) || + (filelist->filter_data.collapse_ima_seq != collapse_ima_seq) || (filelist->filter_data.filter != filter) || !STREQ(filelist->filter_data.filter_glob, filter_glob) || (BLI_strcmp_ignore_pad(filelist->filter_data.filter_search, filter_search, '*') != 0)) { filelist->filter_data.hide_dot = hide_dot; filelist->filter_data.hide_parent = hide_parent; + filelist->filter_data.collapse_ima_seq = collapse_ima_seq; filelist->filter_data.filter = filter; BLI_strncpy(filelist->filter_data.filter_glob, filter_glob, sizeof(filelist->filter_data.filter_glob)); @@ -611,6 +710,7 @@ ImBuf *filelist_getimage(struct FileList *filelist, const int index) { ImBuf *ibuf = NULL; int fidx = 0; + struct direntry *file; BLI_assert(G.background == false); @@ -618,8 +718,15 @@ ImBuf *filelist_getimage(struct FileList *filelist, const int index) return NULL; } fidx = filelist->fidx[index]; - ibuf = filelist->filelist[fidx].image; + file = filelist->filelist + fidx; + if (file->selflag & FILE_SEL_COLLAPSED) { + int curfra = file->collapsed_info.curfra; + ibuf = file->collapsed_info.darray[curfra]->image; + } + else { + ibuf = filelist->filelist[fidx].image; + } return ibuf; } @@ -670,7 +777,10 @@ ImBuf *filelist_geticon(struct FileList *filelist, const int index) ibuf = gSpecialFileImages[SPECIAL_IMG_TEXTFILE]; } else if (file->flags & FILE_TYPE_IMAGE) { - ibuf = gSpecialFileImages[SPECIAL_IMG_LOADING]; + if (file->selflag & FILE_SEL_COLLAPSED) + ibuf = gSpecialFileImages[SPECIAL_IMG_MOVIEFILE]; + else + ibuf = gSpecialFileImages[SPECIAL_IMG_LOADING]; } else if (file->flags & FILE_TYPE_BLENDER_BACKUP) { ibuf = gSpecialFileImages[SPECIAL_IMG_BACKUP]; @@ -1189,29 +1299,30 @@ static void filelist_from_main(struct FileList *filelist) filelist->filelist[0].relname = BLI_strdup(FILENAME_PARENT); filelist->filelist[1].relname = BLI_strdup("Scene"); - filelist->filelist[2].relname = BLI_strdup("Object"); - filelist->filelist[3].relname = BLI_strdup("Mesh"); - filelist->filelist[4].relname = BLI_strdup("Curve"); - filelist->filelist[5].relname = BLI_strdup("Metaball"); - filelist->filelist[6].relname = BLI_strdup("Material"); - filelist->filelist[7].relname = BLI_strdup("Texture"); - filelist->filelist[8].relname = BLI_strdup("Image"); - filelist->filelist[9].relname = BLI_strdup("Ika"); - filelist->filelist[10].relname = BLI_strdup("Wave"); - filelist->filelist[11].relname = BLI_strdup("Lattice"); - filelist->filelist[12].relname = BLI_strdup("Lamp"); - filelist->filelist[13].relname = BLI_strdup("Camera"); - filelist->filelist[14].relname = BLI_strdup("Ipo"); - filelist->filelist[15].relname = BLI_strdup("World"); - filelist->filelist[16].relname = BLI_strdup("Screen"); - filelist->filelist[17].relname = BLI_strdup("VFont"); - filelist->filelist[18].relname = BLI_strdup("Text"); - filelist->filelist[19].relname = BLI_strdup("Armature"); - filelist->filelist[20].relname = BLI_strdup("Action"); - filelist->filelist[21].relname = BLI_strdup("NodeTree"); - filelist->filelist[22].relname = BLI_strdup("Speaker"); + filelist->filelist[2].relname = BLI_strdup("CacheLibrary"); + filelist->filelist[3].relname = BLI_strdup("Object"); + filelist->filelist[4].relname = BLI_strdup("Mesh"); + filelist->filelist[5].relname = BLI_strdup("Curve"); + filelist->filelist[6].relname = BLI_strdup("Metaball"); + filelist->filelist[7].relname = BLI_strdup("Material"); + filelist->filelist[8].relname = BLI_strdup("Texture"); + filelist->filelist[9].relname = BLI_strdup("Image"); + filelist->filelist[10].relname = BLI_strdup("Ika"); + filelist->filelist[11].relname = BLI_strdup("Wave"); + filelist->filelist[12].relname = BLI_strdup("Lattice"); + filelist->filelist[13].relname = BLI_strdup("Lamp"); + filelist->filelist[14].relname = BLI_strdup("Camera"); + filelist->filelist[15].relname = BLI_strdup("Ipo"); + filelist->filelist[16].relname = BLI_strdup("World"); + filelist->filelist[17].relname = BLI_strdup("Screen"); + filelist->filelist[18].relname = BLI_strdup("VFont"); + filelist->filelist[19].relname = BLI_strdup("Text"); + filelist->filelist[20].relname = BLI_strdup("Armature"); + filelist->filelist[21].relname = BLI_strdup("Action"); + filelist->filelist[22].relname = BLI_strdup("NodeTree"); + filelist->filelist[23].relname = BLI_strdup("Speaker"); #ifdef WITH_FREESTYLE - filelist->filelist[23].relname = BLI_strdup("FreestyleLineStyle"); + filelist->filelist[24].relname = BLI_strdup("FreestyleLineStyle"); #endif } else { diff --git a/source/blender/editors/space_file/filelist.h b/source/blender/editors/space_file/filelist.h index 44e0a5169fa..c6c9ed8dcc9 100644 --- a/source/blender/editors/space_file/filelist.h +++ b/source/blender/editors/space_file/filelist.h @@ -69,6 +69,7 @@ bool filelist_need_sorting(struct FileList *filelist); void filelist_sort(struct FileList *filelist); void filelist_setfilter_options(struct FileList *filelist, const bool hide_dot, const bool hide_parent, + const bool collapse_ima_seq, const unsigned int filter, const char *filter_glob, const char *filter_search); void filelist_filter(struct FileList *filelist); diff --git a/source/blender/editors/space_file/filesel.c b/source/blender/editors/space_file/filesel.c index 62b4bfaf179..231712b1858 100644 --- a/source/blender/editors/space_file/filesel.c +++ b/source/blender/editors/space_file/filesel.c @@ -230,6 +230,15 @@ short ED_fileselect_set_params(SpaceFile *sfile) } } + if (params->filter & FILE_TYPE_IMAGE) { + if ((prop = RNA_struct_find_property(op->ptr, "collapse_images"))) { + if (!(params->flag & FILE_COLLAPSE_IMAGES)) { + params->flag |= FILE_COLLAPSE_IMAGES_TMP; + params->flag |= FILE_COLLAPSE_IMAGES; + } + } + } + if (is_relative_path) { if ((prop = RNA_struct_find_property(op->ptr, "relative_path"))) { if (!RNA_property_is_set_ex(op->ptr, prop, false)) { @@ -446,7 +455,20 @@ static void column_widths(struct FileList *files, struct FileLayout *layout) struct direntry *file = filelist_file(files, i); if (file) { float len; - len = file_string_width(file->relname); + if (file->selflag & FILE_SEL_COLLAPSED) { + char fname[PATH_MAX]; + char finalname[PATH_MAX]; + char ext[PATH_MAX]; + CollapsedEntry *collapsed = &file->collapsed_info; + BLI_strncpy(fname, file->relname, sizeof(fname)); + BLI_path_frame_strip(fname, false, ext); + BLI_snprintf(finalname, sizeof(finalname), "%s%.*d-%.*d%s", + fname, collapsed->numdigits, collapsed->minframe, collapsed->numdigits, collapsed->maxframe, ext); + len = file_string_width(finalname); + } + else { + len = file_string_width(file->relname); + } if (len > layout->column_widths[COLUMN_NAME]) layout->column_widths[COLUMN_NAME] = len; len = file_string_width(file->date); if (len > layout->column_widths[COLUMN_DATE]) layout->column_widths[COLUMN_DATE] = len; diff --git a/source/blender/editors/space_file/space_file.c b/source/blender/editors/space_file/space_file.c index 782b318b8a2..07bf8131433 100644 --- a/source/blender/editors/space_file/space_file.c +++ b/source/blender/editors/space_file/space_file.c @@ -215,8 +215,9 @@ static void file_refresh(const bContext *C, ScrArea *sa) params->active_file = -1; /* added this so it opens nicer (ton) */ } filelist_setsorting(sfile->files, params->sort); - filelist_setfilter_options(sfile->files, params->flag & FILE_HIDE_DOT, + filelist_setfilter_options(sfile->files, (params->flag & FILE_HIDE_DOT) != 0, false, /* TODO hide_parent, should be controllable? */ + (params->flag & FILE_COLLAPSE_IMAGES) != 0, params->flag & FILE_FILTER ? params->filter : 0, params->filter_glob, params->filter_search); @@ -254,7 +255,7 @@ static void file_refresh(const bContext *C, ScrArea *sa) int idx = filelist_find(sfile->files, params->renamefile); if (idx >= 0) { struct direntry *file = filelist_file(sfile->files, idx); - if (file) { + if (file && !(file->selflag & FILE_SEL_COLLAPSED)) { file->selflag |= FILE_SEL_EDITING; } } diff --git a/source/blender/editors/space_graph/graph_buttons.c b/source/blender/editors/space_graph/graph_buttons.c index e0b62722d57..c1e3d855e7d 100644 --- a/source/blender/editors/space_graph/graph_buttons.c +++ b/source/blender/editors/space_graph/graph_buttons.c @@ -139,6 +139,13 @@ static void graph_panel_view(const bContext *C, Panel *pa) row = uiLayoutSplit(sub, 0.7f, true); uiItemR(row, &spaceptr, "cursor_position_y", 0, IFACE_("Cursor Y"), ICON_NONE); uiItemEnumO(row, "GRAPH_OT_snap", IFACE_("To Keys"), 0, "type", GRAPHKEYS_SNAP_VALUE); + + col = uiLayoutColumn(pa->layout, false); + uiItemR(col, &spaceptr, "show_backdrop", 0, NULL, ICON_NONE); + col = uiLayoutColumn(pa->layout, false); + uiLayoutSetActive(col, RNA_boolean_get(&spaceptr, "show_backdrop")); + uiItemR(col, &spaceptr, "backdrop_camera", 0, "Camera", ICON_NONE); + uiItemR(col, &spaceptr, "backdrop_opacity", 0, "Opacity", ICON_NONE); } /* ******************* active F-Curve ************** */ @@ -883,6 +890,7 @@ static void graph_panel_modifiers(const bContext *C, Panel *pa) FModifier *fcm; uiLayout *col, *row; uiBlock *block; + bool active; if (!graph_panel_context(C, &ale, &fcu)) return; @@ -907,9 +915,11 @@ static void graph_panel_modifiers(const bContext *C, Panel *pa) uiItemO(row, "", ICON_PASTEDOWN, "GRAPH_OT_fmodifier_paste"); } + active = !(fcu->flag & FCURVE_MOD_OFF); /* draw each modifier */ for (fcm = fcu->modifiers.first; fcm; fcm = fcm->next) { col = uiLayoutColumn(pa->layout, true); + uiLayoutSetActive(col, active); ANIM_uiTemplate_fmodifier_draw(col, ale->id, &fcu->modifiers, fcm); } @@ -917,6 +927,21 @@ static void graph_panel_modifiers(const bContext *C, Panel *pa) MEM_freeN(ale); } +/* ******************* Others ************************ */ + +/* Graph Editor Backdrop Settings */ +static void UNUSED_FUNCTION(graph_panel_backdrop)(const bContext *C, Panel *UNUSED(pa)) +{ + bScreen *sc = CTX_wm_screen(C); + SpaceIpo *sipo = CTX_wm_space_graph(C); + PointerRNA spaceptr; + // uiLayout *col; + + /* get RNA pointers for use when creating the UI elements */ + RNA_pointer_create(&sc->id, &RNA_SpaceGraphEditor, sipo, &spaceptr); + +} + /* ******************* general ******************************** */ void graph_buttons_register(ARegionType *art) @@ -928,7 +953,6 @@ void graph_buttons_register(ARegionType *art) strcpy(pt->label, N_("View Properties")); strcpy(pt->translation_context, BLF_I18NCONTEXT_DEFAULT_BPYRNA); pt->draw = graph_panel_view; - pt->flag |= PNL_DEFAULT_CLOSED; BLI_addtail(&art->paneltypes, pt); pt = MEM_callocN(sizeof(PanelType), "spacetype graph panel properties"); diff --git a/source/blender/editors/space_graph/graph_edit.c b/source/blender/editors/space_graph/graph_edit.c index 3de3ecebc44..f55a9dc45f3 100644 --- a/source/blender/editors/space_graph/graph_edit.c +++ b/source/blender/editors/space_graph/graph_edit.c @@ -59,6 +59,7 @@ #include "BKE_nla.h" #include "BKE_context.h" #include "BKE_report.h" +#include "BKE_screen.h" #include "UI_view2d.h" @@ -982,7 +983,7 @@ void GRAPH_OT_delete(wmOperatorType *ot) /* ******************** Clean Keyframes Operator ************************* */ -static void clean_graph_keys(bAnimContext *ac, float thresh) +static void clean_graph_keys(bAnimContext *ac, float thresh, bool clean_chan) { ListBase anim_data = {NULL, NULL}; bAnimListElem *ale; @@ -994,7 +995,7 @@ static void clean_graph_keys(bAnimContext *ac, float thresh) /* loop through filtered data and clean curves */ for (ale = anim_data.first; ale; ale = ale->next) { - clean_fcurve((FCurve *)ale->key_data, thresh); + clean_fcurve(ac, ale, thresh, clean_chan); ale->update |= ANIM_UPDATE_DEFAULT; } @@ -1009,6 +1010,7 @@ static int graphkeys_clean_exec(bContext *C, wmOperator *op) { bAnimContext ac; float thresh; + bool clean_chan; /* get editor data */ if (ANIM_animdata_get_context(C, &ac) == 0) @@ -1016,9 +1018,9 @@ static int graphkeys_clean_exec(bContext *C, wmOperator *op) /* get cleaning threshold */ thresh = RNA_float_get(op->ptr, "threshold"); - + clean_chan = RNA_boolean_get(op->ptr, "channels"); /* clean keyframes */ - clean_graph_keys(&ac, thresh); + clean_graph_keys(&ac, thresh, clean_chan); /* set notifier that keyframes have changed */ WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL); @@ -1043,6 +1045,7 @@ void GRAPH_OT_clean(wmOperatorType *ot) /* properties */ ot->prop = RNA_def_float(ot->srna, "threshold", 0.001f, 0.0f, FLT_MAX, "Threshold", "", 0.0f, 1000.0f); + RNA_def_boolean(ot->srna, "channels", false, "Channels", ""); } /* ******************** Bake F-Curve Operator *********************** */ @@ -2491,3 +2494,154 @@ void GRAPH_OT_fmodifier_paste(wmOperatorType *ot) } /* ************************************************************************** */ + +typedef struct BackDropTransformData { + float init_offset[2]; + float init_zoom; + short event_type; + wmWidgetGroupType *cagetype; +} BackDropTransformData; + +static int graph_widget_backdrop_transform_poll(bContext *C) +{ + SpaceIpo *sipo = CTX_wm_space_graph(C); + ARegion *ar = CTX_wm_region(C); + + return ((sipo != NULL) && + (ar->type->regionid == RGN_TYPE_WINDOW) && + (sipo->flag & SIPO_DRAW_BACKDROP) && + (sipo->backdrop_camera)); +} + +static void widgetgroup_backdrop_draw(const struct bContext *C, struct wmWidgetGroup *wgroup) +{ + ARegion *ar = CTX_wm_region(C); + wmOperator *op = wgroup->type->op; + Scene *scene = CTX_data_scene(C); + int width = (scene->r.size * scene->r.xsch) / 150.0f; + int height = (scene->r.size * scene->r.ysch) / 150.0f; + float origin[3]; + + wmWidget *cage = WIDGET_rect_transform_new(wgroup, WIDGET_RECT_TRANSFORM_STYLE_SCALE_UNIFORM | + WIDGET_RECT_TRANSFORM_STYLE_TRANSLATE, width, height); + WM_widget_property(cage, RECT_TRANSFORM_SLOT_OFFSET, op->ptr, "offset"); + WM_widget_property(cage, RECT_TRANSFORM_SLOT_SCALE, op->ptr, "scale"); + + origin[0] = BLI_rcti_size_x(&ar->winrct) / 2.0f; + origin[1] = BLI_rcti_size_y(&ar->winrct) / 2.0f; + + WM_widget_set_origin(cage, origin); +} + +static int graph_widget_backdrop_transform_invoke(bContext *C, wmOperator *op, const wmEvent *event) +{ + ScrArea *sa = CTX_wm_area(C); + SpaceIpo *sipo = CTX_wm_space_graph(C); + /* no poll, lives always for the duration of the operator */ + wmWidgetGroupType *cagetype = WM_widgetgrouptype_new(NULL, widgetgroup_backdrop_draw, CTX_data_main(C), "Graph_Canvas", SPACE_IPO, RGN_TYPE_WINDOW, false); + struct wmEventHandler *handler = WM_event_add_modal_handler(C, op); + BackDropTransformData *data = MEM_mallocN(sizeof(BackDropTransformData), "overdrop transform data"); + WM_modal_handler_attach_widgetgroup(C, handler, cagetype, op); + + RNA_float_set_array(op->ptr, "offset", sipo->backdrop_offset); + RNA_float_set(op->ptr, "scale", sipo->backdrop_zoom); + + copy_v2_v2(data->init_offset, sipo->backdrop_offset); + data->init_zoom = sipo->backdrop_zoom; + data->cagetype = cagetype; + data->event_type = event->type; + + op->customdata = data; + + ED_area_headerprint(sa, "Drag to place, and scale, Space/Enter/Caller key to confirm, R to recenter, RClick/Esc to cancel"); + + return OPERATOR_RUNNING_MODAL; +} + +static void graph_widget_backdrop_transform_finish(bContext *C, BackDropTransformData *data) +{ + ScrArea *sa = CTX_wm_area(C); + ED_area_headerprint(sa, NULL); + WM_widgetgrouptype_unregister(C, CTX_data_main(C), data->cagetype); + MEM_freeN(data); +} + +static void graph_widget_backdrop_transform_cancel(struct bContext *C, struct wmOperator *op) +{ + BackDropTransformData *data = op->customdata; + graph_widget_backdrop_transform_finish(C, data); +} + +static int graph_widget_backdrop_transform_modal(bContext *C, wmOperator *op, const wmEvent *event) +{ + BackDropTransformData *data = op->customdata; + + if (event->type == data->event_type && event->val == KM_PRESS) { + graph_widget_backdrop_transform_finish(C, data); + return OPERATOR_FINISHED; + } + + switch (event->type) { + case EVT_WIDGET_UPDATE: { + SpaceIpo *sipo = CTX_wm_space_graph(C); + RNA_float_get_array(op->ptr, "offset", sipo->backdrop_offset); + sipo->backdrop_zoom = RNA_float_get(op->ptr, "scale"); + break; + } + case RKEY: + { + SpaceIpo *sipo = CTX_wm_space_graph(C); + ARegion *ar = CTX_wm_region(C); + float zero[2] = {0.0f}; + RNA_float_set_array(op->ptr, "offset", zero); + RNA_float_set(op->ptr, "scale", 1.0f); + copy_v2_v2(sipo->backdrop_offset, zero); + sipo->backdrop_zoom = 1.0f; + ED_region_tag_redraw(ar); + /* add a mousemove to refresh the widget */ + WM_event_add_mousemove(C); + break; + } + case RETKEY: + case PADENTER: + case SPACEKEY: + { + graph_widget_backdrop_transform_finish(C, data); + return OPERATOR_FINISHED; + } + case ESCKEY: + case RIGHTMOUSE: + { + SpaceIpo *sipo = CTX_wm_space_graph(C); + copy_v2_v2(sipo->backdrop_offset, data->init_offset); + sipo->backdrop_zoom = data->init_zoom; + + graph_widget_backdrop_transform_finish(C, data); + return OPERATOR_CANCELLED; + } + } + + return OPERATOR_RUNNING_MODAL; +} + +void GRAPH_OT_widget_backdrop_transform(struct wmOperatorType *ot) +{ + float default_offset[2] = {0.0f, 0.0f}; + + /* identifiers */ + ot->name = "Transform Backdrop"; + ot->idname = "GRAPH_OT_widget_backdrop_transform"; + ot->description = ""; + + /* api callbacks */ + ot->invoke = graph_widget_backdrop_transform_invoke; + ot->modal = graph_widget_backdrop_transform_modal; + ot->poll = graph_widget_backdrop_transform_poll; + ot->cancel = graph_widget_backdrop_transform_cancel; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + RNA_def_float_array(ot->srna, "offset", 2, default_offset, FLT_MIN, FLT_MAX, "Offset", "Offset of the backdrop", FLT_MIN, FLT_MAX); + RNA_def_float(ot->srna, "scale", 1.0f, 0.0f, FLT_MAX, "Scale", "Scale of the backdrop", 0.0f, FLT_MAX); +} diff --git a/source/blender/editors/space_graph/graph_intern.h b/source/blender/editors/space_graph/graph_intern.h index a478a86a5e2..35267cbf999 100644 --- a/source/blender/editors/space_graph/graph_intern.h +++ b/source/blender/editors/space_graph/graph_intern.h @@ -117,6 +117,8 @@ void GRAPH_OT_frame_jump(struct wmOperatorType *ot); void GRAPH_OT_snap(struct wmOperatorType *ot); void GRAPH_OT_mirror(struct wmOperatorType *ot); +void GRAPH_OT_widget_backdrop_transform(struct wmOperatorType *ot); + /* defines for snap keyframes * NOTE: keep in sync with eEditKeyframes_Snap (in ED_keyframes_edit.h) */ diff --git a/source/blender/editors/space_graph/graph_ops.c b/source/blender/editors/space_graph/graph_ops.c index f4cce29bda8..9b5fa3b744a 100644 --- a/source/blender/editors/space_graph/graph_ops.c +++ b/source/blender/editors/space_graph/graph_ops.c @@ -139,12 +139,16 @@ static void graphview_cursor_setprops(bContext *C, wmOperator *op, const wmEvent /* Modal Operator init */ static int graphview_cursor_invoke(bContext *C, wmOperator *op, const wmEvent *event) { + wmWindow *win = CTX_wm_window(C); /* Change to frame that mouse is over before adding modal handler, * as user could click on a single frame (jump to frame) as well as * click-dragging over a range (modal scrubbing). */ graphview_cursor_setprops(C, op, event); - + + if (win->screen) + win->screen->scrubbing = true; + /* apply these changes first */ graphview_cursor_apply(C, op); @@ -156,9 +160,13 @@ static int graphview_cursor_invoke(bContext *C, wmOperator *op, const wmEvent *e /* Modal event handling of cursor changing */ static int graphview_cursor_modal(bContext *C, wmOperator *op, const wmEvent *event) { + wmWindow *win = CTX_wm_window(C); + /* execute the events */ switch (event->type) { case ESCKEY: + if (win->screen) + win->screen->scrubbing = false; return OPERATOR_FINISHED; case MOUSEMOVE: @@ -173,8 +181,12 @@ static int graphview_cursor_modal(bContext *C, wmOperator *op, const wmEvent *ev /* we check for either mouse-button to end, as checking for ACTIONMOUSE (which is used to init * the modal op) doesn't work for some reason */ - if (event->val == KM_RELEASE) + if (event->val == KM_RELEASE) { + if (win->screen) + win->screen->scrubbing = false; + return OPERATOR_FINISHED; + } break; } @@ -429,6 +441,8 @@ void graphedit_operatortypes(void) WM_operatortype_append(GRAPH_OT_fmodifier_add); WM_operatortype_append(GRAPH_OT_fmodifier_copy); WM_operatortype_append(GRAPH_OT_fmodifier_paste); + + WM_operatortype_append(GRAPH_OT_widget_backdrop_transform); } void ED_operatormacros_graph(void) @@ -667,5 +681,7 @@ void graphedit_keymap(wmKeyConfig *keyconf) /* keyframes */ keymap = WM_keymap_find(keyconf, "Graph Editor", SPACE_IPO, 0); graphedit_keymap_keyframes(keyconf, keymap); + + WM_keymap_add_item(keymap, "GRAPH_OT_widget_backdrop_transform", WKEY, KM_PRESS, 0, 0); } diff --git a/source/blender/editors/space_graph/space_graph.c b/source/blender/editors/space_graph/space_graph.c index ad6f3ff5c7e..4fd6276a28f 100644 --- a/source/blender/editors/space_graph/space_graph.c +++ b/source/blender/editors/space_graph/space_graph.c @@ -103,6 +103,10 @@ static SpaceLink *graph_new(const bContext *C) sipo->autosnap = SACTSNAP_FRAME; + sipo->backdrop_camera = scene->camera; + sipo->backdrop_zoom = 1.0f; + sipo->backdrop_opacity = 0.7f; + /* allocate DopeSheet data for Graph Editor */ sipo->ads = MEM_callocN(sizeof(bDopeSheet), "GraphEdit DopeSheet"); sipo->ads->source = (ID *)scene; @@ -219,18 +223,25 @@ static void graph_main_area_init(wmWindowManager *wm, ARegion *ar) WM_event_add_keymap_handler_bb(&ar->handlers, keymap, &ar->v2d.mask, &ar->winrct); keymap = WM_keymap_find(wm->defaultconf, "Graph Editor Generic", SPACE_IPO, 0); WM_event_add_keymap_handler(&ar->handlers, keymap); + + /* widgets */ + if (BLI_listbase_is_empty(&ar->widgetmaps)) { + BLI_addhead(&ar->widgetmaps, WM_widgetmap_from_type("Graph_Canvas", SPACE_IPO, RGN_TYPE_WINDOW, false)); + } } static void graph_main_area_draw(const bContext *C, ARegion *ar) { /* draw entirely, view changes should be handled here */ SpaceIpo *sipo = CTX_wm_space_graph(C); + Scene *scene = CTX_data_scene(C); bAnimContext ac; View2D *v2d = &ar->v2d; View2DGrid *grid; View2DScrollers *scrollers; float col[3]; short unitx = 0, unity = V2D_UNIT_VALUES, flag = 0; + const bool draw_backdrop = ((sipo->flag & SIPO_DRAW_BACKDROP) && (sipo->backdrop_camera != NULL)); /* clear and setup matrix */ UI_GetThemeColor3fv(TH_BACK, col); @@ -241,11 +252,26 @@ static void graph_main_area_draw(const bContext *C, ARegion *ar) /* grid */ unitx = (sipo->flag & SIPO_DRAWTIME) ? V2D_UNIT_SECONDS : V2D_UNIT_FRAMESCALE; - grid = UI_view2d_grid_calc(CTX_data_scene(C), v2d, unitx, V2D_GRID_NOCLAMP, unity, V2D_GRID_NOCLAMP, ar->winx, ar->winy); + grid = UI_view2d_grid_calc(scene, v2d, unitx, V2D_GRID_NOCLAMP, unity, V2D_GRID_NOCLAMP, ar->winx, ar->winy); UI_view2d_grid_draw(v2d, grid, V2D_GRIDLINES_ALL); ED_region_draw_cb_draw(C, ar, REGION_DRAW_PRE_VIEW); - + + if (draw_backdrop) { + int width = (scene->r.size * scene->r.xsch) / 150 * sipo->backdrop_zoom; + int height = (scene->r.size * scene->r.ysch) / 150 * sipo->backdrop_zoom; + float xofs = (BLI_rcti_size_x(&ar->winrct) - width) / 2.0f + sipo->backdrop_offset[0]; + float yofs = (BLI_rcti_size_y(&ar->winrct) - height) / 2.0f + sipo->backdrop_offset[1]; + + /* reset view matrix */ + UI_view2d_view_restore(C); + + ED_region_draw_backdrop_view3d(C, sipo->backdrop_camera, sipo->backdrop_opacity, + width, height, xofs, yofs, 1.0f, 1.0f, true); + + UI_view2d_view_ortho(v2d); + } + /* draw data */ if (ANIM_animdata_get_context(C, &ac)) { /* draw ghost curves */ @@ -306,6 +332,10 @@ static void graph_main_area_draw(const bContext *C, ARegion *ar) /* reset view matrix */ UI_view2d_view_restore(C); + /* finally draw any widgets here */ + WM_widgets_update(C, ar->widgetmaps.first); + WM_widgets_draw(C, ar->widgetmaps.first, false); + /* scrollers */ // FIXME: args for scrollers depend on the type of data being shown... scrollers = UI_view2d_scrollers_calc(C, v2d, unitx, V2D_GRID_NOCLAMP, unity, V2D_GRID_NOCLAMP); @@ -611,6 +641,14 @@ static void graph_refresh(const bContext *C, ScrArea *sa) } } +/* ************************************* */ + +static void graph_widgets(void) +{ + /* create the widgetmap for the area here */ + WM_widgetmaptype_find("Graph_Canvas", SPACE_IPO, RGN_TYPE_WINDOW, false, true); +} + /* only called once, from space/spacetypes.c */ void ED_spacetype_ipo(void) { @@ -628,6 +666,7 @@ void ED_spacetype_ipo(void) st->keymap = graphedit_keymap; st->listener = graph_listener; st->refresh = graph_refresh; + st->widgets = graph_widgets; /* regions: main window */ art = MEM_callocN(sizeof(ARegionType), "spacetype graphedit region"); diff --git a/source/blender/editors/space_image/image_draw.c b/source/blender/editors/space_image/image_draw.c index 36419ccd10d..34e32c150d6 100644 --- a/source/blender/editors/space_image/image_draw.c +++ b/source/blender/editors/space_image/image_draw.c @@ -513,7 +513,7 @@ static void draw_image_buffer(const bContext *C, SpaceImage *sima, ARegion *ar, fdrawcheckerboard(x, y, x + ibuf->x * zoomx, y + ibuf->y * zoomy); } - glaDrawImBuf_glsl_ctx(C, ibuf, x, y, GL_NEAREST); + glaDrawImBuf_glsl_ctx(C, ibuf, x, y, GL_NEAREST, 1.0f); if (sima->flag & SI_USE_ALPHA) glDisable(GL_BLEND); diff --git a/source/blender/editors/space_node/drawnode.c b/source/blender/editors/space_node/drawnode.c index 5bc67bd3cd3..15fc4e5db25 100644 --- a/source/blender/editors/space_node/drawnode.c +++ b/source/blender/editors/space_node/drawnode.c @@ -937,6 +937,27 @@ static void node_shader_buts_tex_voronoi(uiLayout *layout, bContext *UNUSED(C), uiItemR(layout, ptr, "coloring", 0, "", ICON_NONE); } +static void node_shader_buts_tex_pointdensity(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + bNode *node = ptr->data; + NodeShaderTexPointDensity *shader_point_density = node->storage; + + uiItemR(layout, ptr, "point_source", UI_ITEM_R_EXPAND, NULL, ICON_NONE); + uiItemR(layout, ptr, "object", 0, NULL, ICON_NONE); + + if (node->id && shader_point_density->point_source == SHD_POINTDENSITY_SOURCE_PSYS) { + PointerRNA dataptr; + RNA_id_pointer_create((ID *)node->id, &dataptr); + uiItemPointerR(layout, ptr, "particle_system", &dataptr, "particle_systems", NULL, ICON_NONE); + } + + uiItemR(layout, ptr, "space", 0, NULL, ICON_NONE); + uiItemR(layout, ptr, "radius", 0, NULL, ICON_NONE); + uiItemR(layout, ptr, "interpolation", 0, NULL, ICON_NONE); + uiItemR(layout, ptr, "resolution", 0, NULL, ICON_NONE); + uiItemR(layout, ptr, "color_source", 0, NULL, ICON_NONE); +} + static void node_shader_buts_tex_coord(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { uiItemR(layout, ptr, "object", 0, NULL, 0); @@ -1186,6 +1207,9 @@ static void node_shader_set_butfunc(bNodeType *ntype) case SH_NODE_TEX_VORONOI: ntype->draw_buttons = node_shader_buts_tex_voronoi; break; + case SH_NODE_TEX_POINTDENSITY: + ntype->draw_buttons = node_shader_buts_tex_pointdensity; + break; case SH_NODE_TEX_COORD: ntype->draw_buttons = node_shader_buts_tex_coord; break; @@ -2192,8 +2216,8 @@ static void node_composit_backdrop_viewer(SpaceNode *snode, ImBuf *backdrop, bNo if (node->custom1 == 0) { const float backdropWidth = backdrop->x; const float backdropHeight = backdrop->y; - const float cx = x + snode->zoom * backdropWidth * node->custom3; - const float cy = y + snode->zoom * backdropHeight * node->custom4; + const float cx = x + snode->backdrop_zoom * backdropWidth * node->custom3; + const float cy = y + snode->backdrop_zoom * backdropHeight * node->custom4; glColor3f(1.0, 1.0, 1.0); @@ -2224,17 +2248,17 @@ static void node_composit_backdrop_boxmask(SpaceNode *snode, ImBuf *backdrop, bN glColor3f(1.0, 1.0, 1.0); - cx = x + snode->zoom * backdropWidth * boxmask->x; - cy = y + snode->zoom * backdropHeight * boxmask->y; + cx = x + snode->backdrop_zoom * backdropWidth * boxmask->x; + cy = y + snode->backdrop_zoom * backdropHeight * boxmask->y; - x1 = cx - (cosine * halveBoxWidth + sine * halveBoxHeight) * snode->zoom; - x2 = cx - (cosine * -halveBoxWidth + sine * halveBoxHeight) * snode->zoom; - x3 = cx - (cosine * -halveBoxWidth + sine * -halveBoxHeight) * snode->zoom; - x4 = cx - (cosine * halveBoxWidth + sine * -halveBoxHeight) * snode->zoom; - y1 = cy - (-sine * halveBoxWidth + cosine * halveBoxHeight) * snode->zoom; - y2 = cy - (-sine * -halveBoxWidth + cosine * halveBoxHeight) * snode->zoom; - y3 = cy - (-sine * -halveBoxWidth + cosine * -halveBoxHeight) * snode->zoom; - y4 = cy - (-sine * halveBoxWidth + cosine * -halveBoxHeight) * snode->zoom; + x1 = cx - (cosine * halveBoxWidth + sine * halveBoxHeight) * snode->backdrop_zoom; + x2 = cx - (cosine * -halveBoxWidth + sine * halveBoxHeight) * snode->backdrop_zoom; + x3 = cx - (cosine * -halveBoxWidth + sine * -halveBoxHeight) * snode->backdrop_zoom; + x4 = cx - (cosine * halveBoxWidth + sine * -halveBoxHeight) * snode->backdrop_zoom; + y1 = cy - (-sine * halveBoxWidth + cosine * halveBoxHeight) * snode->backdrop_zoom; + y2 = cy - (-sine * -halveBoxWidth + cosine * halveBoxHeight) * snode->backdrop_zoom; + y3 = cy - (-sine * -halveBoxWidth + cosine * -halveBoxHeight) * snode->backdrop_zoom; + y4 = cy - (-sine * halveBoxWidth + cosine * -halveBoxHeight) * snode->backdrop_zoom; glBegin(GL_LINE_LOOP); glVertex2f(x1, y1); @@ -2262,17 +2286,17 @@ static void node_composit_backdrop_ellipsemask(SpaceNode *snode, ImBuf *backdrop glColor3f(1.0, 1.0, 1.0); - cx = x + snode->zoom * backdropWidth * ellipsemask->x; - cy = y + snode->zoom * backdropHeight * ellipsemask->y; + cx = x + snode->backdrop_zoom * backdropWidth * ellipsemask->x; + cy = y + snode->backdrop_zoom * backdropHeight * ellipsemask->y; - x1 = cx - (cosine * halveBoxWidth + sine * halveBoxHeight) * snode->zoom; - x2 = cx - (cosine * -halveBoxWidth + sine * halveBoxHeight) * snode->zoom; - x3 = cx - (cosine * -halveBoxWidth + sine * -halveBoxHeight) * snode->zoom; - x4 = cx - (cosine * halveBoxWidth + sine * -halveBoxHeight) * snode->zoom; - y1 = cy - (-sine * halveBoxWidth + cosine * halveBoxHeight) * snode->zoom; - y2 = cy - (-sine * -halveBoxWidth + cosine * halveBoxHeight) * snode->zoom; - y3 = cy - (-sine * -halveBoxWidth + cosine * -halveBoxHeight) * snode->zoom; - y4 = cy - (-sine * halveBoxWidth + cosine * -halveBoxHeight) * snode->zoom; + x1 = cx - (cosine * halveBoxWidth + sine * halveBoxHeight) * snode->backdrop_zoom; + x2 = cx - (cosine * -halveBoxWidth + sine * halveBoxHeight) * snode->backdrop_zoom; + x3 = cx - (cosine * -halveBoxWidth + sine * -halveBoxHeight) * snode->backdrop_zoom; + x4 = cx - (cosine * halveBoxWidth + sine * -halveBoxHeight) * snode->backdrop_zoom; + y1 = cy - (-sine * halveBoxWidth + cosine * halveBoxHeight) * snode->backdrop_zoom; + y2 = cy - (-sine * -halveBoxWidth + cosine * halveBoxHeight) * snode->backdrop_zoom; + y3 = cy - (-sine * -halveBoxWidth + cosine * -halveBoxHeight) * snode->backdrop_zoom; + y4 = cy - (-sine * halveBoxWidth + cosine * -halveBoxHeight) * snode->backdrop_zoom; glBegin(GL_LINE_LOOP); @@ -3207,8 +3231,8 @@ void draw_nodespace_back_pix(const bContext *C, ARegion *ar, SpaceNode *snode, b glaDefine2DArea(&ar->winrct); wmOrtho2_region_pixelspace(ar); - x = (ar->winx - snode->zoom * ibuf->x) / 2 + snode->xof; - y = (ar->winy - snode->zoom * ibuf->y) / 2 + snode->yof; + x = (ar->winx - snode->backdrop_zoom * ibuf->x) / 2 + snode->backdrop_offset[0]; + y = (ar->winy - snode->backdrop_zoom * ibuf->y) / 2 + snode->backdrop_offset[1]; if (ibuf->rect || ibuf->rect_float) { unsigned char *display_buffer = NULL; @@ -3229,7 +3253,7 @@ void draw_nodespace_back_pix(const bContext *C, ARegion *ar, SpaceNode *snode, b else ofs = 3; #endif - glPixelZoom(snode->zoom, snode->zoom); + glPixelZoom(snode->backdrop_zoom, snode->backdrop_zoom); /* swap bytes, so alpha is most significant one, then just draw it as luminance int */ glaDrawPixelsSafe(x, y, ibuf->x, ibuf->y, ibuf->x, GL_LUMINANCE, GL_UNSIGNED_INT, @@ -3240,7 +3264,7 @@ void draw_nodespace_back_pix(const bContext *C, ARegion *ar, SpaceNode *snode, b else if (snode->flag & SNODE_SHOW_ALPHA) { display_buffer = IMB_display_buffer_acquire_ctx(C, ibuf, &cache_handle); - glPixelZoom(snode->zoom, snode->zoom); + glPixelZoom(snode->backdrop_zoom, snode->backdrop_zoom); /* swap bytes, so alpha is most significant one, then just draw it as luminance int */ #ifdef __BIG_ENDIAN__ glPixelStorei(GL_UNPACK_SWAP_BYTES, 1); @@ -3255,17 +3279,17 @@ void draw_nodespace_back_pix(const bContext *C, ARegion *ar, SpaceNode *snode, b else if (snode->flag & SNODE_USE_ALPHA) { glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glPixelZoom(snode->zoom, snode->zoom); + glPixelZoom(snode->backdrop_zoom, snode->backdrop_zoom); - glaDrawImBuf_glsl_ctx(C, ibuf, x, y, GL_NEAREST); + glaDrawImBuf_glsl_ctx(C, ibuf, x, y, GL_NEAREST, 1.0f); glPixelZoom(1.0f, 1.0f); glDisable(GL_BLEND); } else { - glPixelZoom(snode->zoom, snode->zoom); + glPixelZoom(snode->backdrop_zoom, snode->backdrop_zoom); - glaDrawImBuf_glsl_ctx(C, ibuf, x, y, GL_NEAREST); + glaDrawImBuf_glsl_ctx(C, ibuf, x, y, GL_NEAREST, 1.0f); glPixelZoom(1.0f, 1.0f); } @@ -3294,10 +3318,10 @@ void draw_nodespace_back_pix(const bContext *C, ARegion *ar, SpaceNode *snode, b rcti pixel_border; UI_ThemeColor(TH_ACTIVE); BLI_rcti_init(&pixel_border, - x + snode->zoom * viewer_border->xmin * ibuf->x, - x + snode->zoom * viewer_border->xmax * ibuf->x, - y + snode->zoom * viewer_border->ymin * ibuf->y, - y + snode->zoom * viewer_border->ymax * ibuf->y); + x + snode->backdrop_zoom * viewer_border->xmin * ibuf->x, + x + snode->backdrop_zoom * viewer_border->xmax * ibuf->x, + y + snode->backdrop_zoom * viewer_border->ymin * ibuf->y, + y + snode->backdrop_zoom * viewer_border->ymax * ibuf->y); glaDrawBorderCorners(&pixel_border, 1.0f, 1.0f); } } diff --git a/source/blender/editors/space_node/node_draw.c b/source/blender/editors/space_node/node_draw.c index 34efba00b86..d27ddc99851 100644 --- a/source/blender/editors/space_node/node_draw.c +++ b/source/blender/editors/space_node/node_draw.c @@ -1352,7 +1352,23 @@ void drawnodespace(const bContext *C, ARegion *ar) /* backdrop */ draw_nodespace_back_pix(C, ar, snode, path->parent_key); + + glMatrixMode(GL_PROJECTION); + glPushMatrix(); + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glaDefine2DArea(&ar->winrct); + wmOrtho2_pixelspace(ar->winx, ar->winy); + + WM_widgets_update(C, ar->widgetmaps.first); + WM_widgets_draw(C, ar->widgetmaps.first, false); + + glMatrixMode(GL_PROJECTION); + glPopMatrix(); + glMatrixMode(GL_MODELVIEW); + glPopMatrix(); + draw_nodetree(C, ar, ntree, path->parent_key); } diff --git a/source/blender/editors/space_node/node_edit.c b/source/blender/editors/space_node/node_edit.c index ffd51bcc44e..2af1dc01a0e 100644 --- a/source/blender/editors/space_node/node_edit.c +++ b/source/blender/editors/space_node/node_edit.c @@ -2516,11 +2516,11 @@ static void viewer_border_corner_to_backdrop(SpaceNode *snode, ARegion *ar, int { float bufx, bufy; - bufx = backdrop_width * snode->zoom; - bufy = backdrop_height * snode->zoom; + bufx = backdrop_width * snode->backdrop_zoom; + bufy = backdrop_height * snode->backdrop_zoom; - *fx = (bufx > 0.0f ? ((float) x - 0.5f * ar->winx - snode->xof) / bufx + 0.5f : 0.0f); - *fy = (bufy > 0.0f ? ((float) y - 0.5f * ar->winy - snode->yof) / bufy + 0.5f : 0.0f); + *fx = (bufx > 0.0f ? ((float) x - 0.5f * ar->winx - snode->backdrop_offset[0]) / bufx + 0.5f : 0.0f); + *fy = (bufy > 0.0f ? ((float) y - 0.5f * ar->winy - snode->backdrop_offset[1]) / bufy + 0.5f : 0.0f); } static int viewer_border_exec(bContext *C, wmOperator *op) diff --git a/source/blender/editors/space_node/node_view.c b/source/blender/editors/space_node/node_view.c index 8c5d2d82468..e218776ac8f 100644 --- a/source/blender/editors/space_node/node_view.c +++ b/source/blender/editors/space_node/node_view.c @@ -137,8 +137,8 @@ static int node_view_all_exec(bContext *C, wmOperator *op) const int smooth_viewtx = WM_operator_smooth_viewtx_get(op); /* is this really needed? */ - snode->xof = 0; - snode->yof = 0; + snode->backdrop_offset[0] = 0; + snode->backdrop_offset[1] = 0; if (space_node_view_flag(C, snode, ar, 0, smooth_viewtx)) { return OPERATOR_FINISHED; @@ -208,14 +208,14 @@ static int snode_bg_viewmove_modal(bContext *C, wmOperator *op, const wmEvent *e switch (event->type) { case MOUSEMOVE: - snode->xof -= (nvm->mvalo[0] - event->mval[0]); - snode->yof -= (nvm->mvalo[1] - event->mval[1]); + snode->backdrop_offset[0] -= (nvm->mvalo[0] - event->mval[0]); + snode->backdrop_offset[1] -= (nvm->mvalo[1] - event->mval[1]); nvm->mvalo[0] = event->mval[0]; nvm->mvalo[1] = event->mval[1]; /* prevent dragging image outside of the window and losing it! */ - CLAMP(snode->xof, nvm->xmin, nvm->xmax); - CLAMP(snode->yof, nvm->ymin, nvm->ymax); + CLAMP(snode->backdrop_offset[0], nvm->xmin, nvm->xmax); + CLAMP(snode->backdrop_offset[1], nvm->ymin, nvm->ymax); ED_region_tag_redraw(ar); WM_main_add_notifier(NC_NODE | ND_DISPLAY, NULL); @@ -259,10 +259,10 @@ static int snode_bg_viewmove_invoke(bContext *C, wmOperator *op, const wmEvent * nvm->mvalo[0] = event->mval[0]; nvm->mvalo[1] = event->mval[1]; - nvm->xmin = -(ar->winx / 2) - (ibuf->x * (0.5f * snode->zoom)) + pad; - nvm->xmax = (ar->winx / 2) + (ibuf->x * (0.5f * snode->zoom)) - pad; - nvm->ymin = -(ar->winy / 2) - (ibuf->y * (0.5f * snode->zoom)) + pad; - nvm->ymax = (ar->winy / 2) + (ibuf->y * (0.5f * snode->zoom)) - pad; + nvm->xmin = -(ar->winx / 2) - (ibuf->x * (0.5f * snode->backdrop_zoom)) + pad; + nvm->xmax = (ar->winx / 2) + (ibuf->x * (0.5f * snode->backdrop_zoom)) - pad; + nvm->ymin = -(ar->winy / 2) - (ibuf->y * (0.5f * snode->backdrop_zoom)) + pad; + nvm->ymax = (ar->winy / 2) + (ibuf->y * (0.5f * snode->backdrop_zoom)) - pad; BKE_image_release_ibuf(ima, ibuf, lock); @@ -301,7 +301,8 @@ static int backimage_zoom_exec(bContext *C, wmOperator *op) ARegion *ar = CTX_wm_region(C); float fac = RNA_float_get(op->ptr, "factor"); - snode->zoom *= fac; + snode->backdrop_zoom *= fac; + snode->backdrop_zoom *= fac; ED_region_tag_redraw(ar); WM_main_add_notifier(NC_NODE | ND_DISPLAY, NULL); @@ -340,7 +341,7 @@ static int backimage_fit_exec(bContext *C, wmOperator *UNUSED(op)) void *lock; - float facx, facy; + float facx, facy, fac; ima = BKE_image_verify_viewer(IMA_TYPE_COMPOSITE, "Viewer Node"); ibuf = BKE_image_acquire_ibuf(ima, NULL, &lock); @@ -350,15 +351,18 @@ static int backimage_fit_exec(bContext *C, wmOperator *UNUSED(op)) return OPERATOR_CANCELLED; } - facx = 1.0f * (ar->sizex - pad) / (ibuf->x * snode->zoom); - facy = 1.0f * (ar->sizey - pad) / (ibuf->y * snode->zoom); + facx = 1.0f * (ar->sizex - pad) / (ibuf->x); + facy = 1.0f * (ar->sizey - pad) / (ibuf->y); BKE_image_release_ibuf(ima, ibuf, lock); + + fac = min_ff(facx, facy); - snode->zoom *= min_ff(facx, facy); - - snode->xof = 0; - snode->yof = 0; + snode->backdrop_zoom = fac; + snode->backdrop_zoom = fac; + + snode->backdrop_offset[0] = 0; + snode->backdrop_offset[1] = 0; ED_region_tag_redraw(ar); WM_main_add_notifier(NC_NODE | ND_DISPLAY, NULL); @@ -444,10 +448,10 @@ bool ED_space_node_color_sample(Scene *scene, SpaceNode *snode, ARegion *ar, int } /* map the mouse coords to the backdrop image space */ - bufx = ibuf->x * snode->zoom; - bufy = ibuf->y * snode->zoom; - fx = (bufx > 0.0f ? ((float)mval[0] - 0.5f * ar->winx - snode->xof) / bufx + 0.5f : 0.0f); - fy = (bufy > 0.0f ? ((float)mval[1] - 0.5f * ar->winy - snode->yof) / bufy + 0.5f : 0.0f); + bufx = ibuf->x * snode->backdrop_zoom; + bufy = ibuf->y * snode->backdrop_zoom; + fx = (bufx > 0.0f ? ((float)mval[0] - 0.5f * ar->winx - snode->backdrop_offset[0]) / bufx + 0.5f : 0.0f); + fy = (bufy > 0.0f ? ((float)mval[1] - 0.5f * ar->winy - snode->backdrop_offset[1]) / bufy + 0.5f : 0.0f); if (fx >= 0.0f && fy >= 0.0f && fx < 1.0f && fy < 1.0f) { const float *fp; @@ -502,10 +506,10 @@ static void sample_apply(bContext *C, wmOperator *op, const wmEvent *event) } /* map the mouse coords to the backdrop image space */ - bufx = ibuf->x * snode->zoom; - bufy = ibuf->y * snode->zoom; - fx = (bufx > 0.0f ? ((float)event->mval[0] - 0.5f * ar->winx - snode->xof) / bufx + 0.5f : 0.0f); - fy = (bufy > 0.0f ? ((float)event->mval[1] - 0.5f * ar->winy - snode->yof) / bufy + 0.5f : 0.0f); + bufx = ibuf->x * snode->backdrop_zoom; + bufy = ibuf->y * snode->backdrop_zoom; + fx = (bufx > 0.0f ? ((float)event->mval[0] - 0.5f * ar->winx - snode->backdrop_offset[0]) / bufx + 0.5f : 0.0f); + fy = (bufy > 0.0f ? ((float)event->mval[1] - 0.5f * ar->winy - snode->backdrop_offset[1]) / bufy + 0.5f : 0.0f); if (fx >= 0.0f && fy >= 0.0f && fx < 1.0f && fy < 1.0f) { const float *fp; diff --git a/source/blender/editors/space_node/space_node.c b/source/blender/editors/space_node/space_node.c index de90c417bdd..a1980c88d7e 100644 --- a/source/blender/editors/space_node/space_node.c +++ b/source/blender/editors/space_node/space_node.c @@ -38,7 +38,10 @@ #include "BLI_blenlib.h" #include "BLI_math.h" +#include "IMB_imbuf_types.h" + #include "BKE_context.h" +#include "BKE_image.h" #include "BKE_library.h" #include "BKE_scene.h" #include "BKE_screen.h" @@ -56,6 +59,8 @@ #include "RNA_access.h" +#include "WM_api.h" + #include "node_intern.h" /* own include */ @@ -300,7 +305,7 @@ static SpaceLink *node_new(const bContext *UNUSED(C)) snode->flag = SNODE_SHOW_GPENCIL | SNODE_USE_ALPHA; /* backdrop */ - snode->zoom = 1.0f; + snode->backdrop_zoom = 1.0f; /* select the first tree type for valid type */ NODE_TREE_TYPES_BEGIN (treetype) @@ -642,6 +647,13 @@ static void node_main_area_init(wmWindowManager *wm, ARegion *ar) UI_view2d_region_reinit(&ar->v2d, V2D_COMMONVIEW_CUSTOM, ar->winx, ar->winy); + /* widgets stay in the background for now - quick patchjob to make sure nodes themselves work */ + if (BLI_listbase_is_empty(&ar->widgetmaps)) { + BLI_addhead(&ar->widgetmaps, WM_widgetmap_from_type("Node_Canvas", SPACE_NODE, RGN_TYPE_WINDOW, false)); + } + + WM_event_add_area_widgetmap_handlers(ar); + /* own keymaps */ keymap = WM_keymap_find(wm->defaultconf, "Node Generic", SPACE_NODE, 0); WM_event_add_keymap_handler(&ar->handlers, keymap); @@ -821,6 +833,61 @@ static int node_context(const bContext *C, const char *member, bContextDataResul return 0; } +static int WIDGETGROUP_node_transform_poll(const struct bContext *C, struct wmWidgetGroupType *UNUSED(wgrouptype)) +{ + SpaceNode *snode = CTX_wm_space_node(C); + + if (snode && snode->edittree && snode->edittree->type == NTREE_COMPOSIT) { + bNode *node = nodeGetActive(snode->edittree); + + if (node && node->type == CMP_NODE_VIEWER) + return true; + } + + return false; +} + +static void WIDGETGROUP_node_transform_update(const struct bContext *C, struct wmWidgetGroup *wgroup) +{ + Image *ima; + ImBuf *ibuf; + void *lock; + + ima = BKE_image_verify_viewer(IMA_TYPE_COMPOSITE, "Viewer Node"); + ibuf = BKE_image_acquire_ibuf(ima, NULL, &lock); + if (ibuf) { + wmWidget *cage; + SpaceNode *snode = CTX_wm_space_node(C); + ARegion *ar = CTX_wm_region(C); + float origin[3]; + float w, h; + PointerRNA nodeptr; + + /* center is always at the origin */ + origin[0] = ar->winx / 2; + origin[1] = ar->winy / 2; + + w = (ibuf->x > 0) ? ibuf->x : 64.0f; + h = (ibuf->y > 0) ? ibuf->y : 64.0f; + + cage = WIDGET_rect_transform_new(wgroup, WIDGET_RECT_TRANSFORM_STYLE_TRANSLATE | WIDGET_RECT_TRANSFORM_STYLE_SCALE_UNIFORM, w, h); + RNA_pointer_create(snode->id, &RNA_SpaceNodeEditor, snode, &nodeptr); + + WM_widget_set_origin(cage, origin); + WM_widget_property(cage, RECT_TRANSFORM_SLOT_OFFSET, &nodeptr, "backdrop_offset"); + WM_widget_property(cage, RECT_TRANSFORM_SLOT_SCALE, &nodeptr, "backdrop_zoom"); + } + BKE_image_release_ibuf(ima, ibuf, lock); +} + +static void node_widgets(void) +{ + /* create the widgetmap for the area here */ + WM_widgetmaptype_find("Node_Canvas", SPACE_NODE, RGN_TYPE_WINDOW, false, true); + + WM_widgetgrouptype_new(WIDGETGROUP_node_transform_poll, WIDGETGROUP_node_transform_update, NULL, "Node_Canvas", SPACE_NODE, RGN_TYPE_WINDOW, false); +} + /* only called once, from space/spacetypes.c */ void ED_spacetype_node(void) { @@ -840,6 +907,7 @@ void ED_spacetype_node(void) st->refresh = node_area_refresh; st->context = node_context; st->dropboxes = node_dropboxes; + st->widgets = node_widgets; /* regions: main window */ art = MEM_callocN(sizeof(ARegionType), "spacetype node region"); diff --git a/source/blender/editors/space_sequencer/sequencer_add.c b/source/blender/editors/space_sequencer/sequencer_add.c index 86d1fe71ade..5b95a418dc2 100644 --- a/source/blender/editors/space_sequencer/sequencer_add.c +++ b/source/blender/editors/space_sequencer/sequencer_add.c @@ -940,6 +940,37 @@ void SEQUENCER_OT_image_strip_add(struct wmOperatorType *ot) } +void SEQUENCER_OT_image_sequence_add(struct wmOperatorType *ot) +{ + PropertyRNA *prop; + + /* identifiers */ + ot->name = "Add Image Strip"; + ot->idname = "SEQUENCER_OT_image_sequence_add"; + ot->description = "Add an sequence of images with identical names to the sequencer"; + + /* api callbacks */ + ot->invoke = sequencer_add_image_strip_invoke; + ot->exec = sequencer_add_image_strip_exec; + ot->cancel = sequencer_add_cancel; + ot->ui = sequencer_add_draw; + + ot->poll = ED_operator_sequencer_active_editable; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + WM_operator_properties_filesel(ot, FILE_TYPE_FOLDER | FILE_TYPE_IMAGE, FILE_SPECIAL, FILE_OPENFILE, + WM_FILESEL_DIRECTORY | WM_FILESEL_RELPATH | WM_FILESEL_FILES | WM_FILESEL_IMAGE_COLLAPSE, + FILE_DEFAULTDISPLAY); + sequencer_generic_props__internal(ot, SEQPROP_STARTFRAME | SEQPROP_ENDFRAME); + + /* hidden property always set to true */ + prop = RNA_def_boolean(ot->srna, "use_placeholders", true, "Use Placeholders", "Use placeholders for missing frames of the strip"); + RNA_def_property_flag(prop, PROP_HIDDEN); +} + + /* add_effect_strip operator */ static int sequencer_add_effect_strip_exec(bContext *C, wmOperator *op) { diff --git a/source/blender/editors/space_sequencer/sequencer_draw.c b/source/blender/editors/space_sequencer/sequencer_draw.c index 04f04e74616..b0d3fadf9d1 100644 --- a/source/blender/editors/space_sequencer/sequencer_draw.c +++ b/source/blender/editors/space_sequencer/sequencer_draw.c @@ -293,6 +293,20 @@ static void drawmeta_contents(Scene *scene, Sequence *seqm, float x1, float y1, int chan_range = 0; float draw_range = y2 - y1; float draw_height; + ListBase *seqbase; + int offset; + + seqbase = BKE_sequence_seqbase_get(seqm, &offset); + if (!seqbase || BLI_listbase_is_empty(seqbase)) { + return; + } + + if (seqm->type == SEQ_TYPE_SCENE) { + offset = seqm->start - offset; + } + else { + offset = 0; + } glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); @@ -300,7 +314,7 @@ static void drawmeta_contents(Scene *scene, Sequence *seqm, float x1, float y1, if (seqm->flag & SEQ_MUTE) drawmeta_stipple(1); - for (seq = seqm->seqbase.first; seq; seq = seq->next) { + for (seq = seqbase->first; seq; seq = seq->next) { chan_min = min_ii(chan_min, seq->machine); chan_max = max_ii(chan_max, seq->machine); } @@ -310,11 +324,14 @@ static void drawmeta_contents(Scene *scene, Sequence *seqm, float x1, float y1, col[3] = 196; /* alpha, used for all meta children */ - for (seq = seqm->seqbase.first; seq; seq = seq->next) { - if ((seq->startdisp > x2 || seq->enddisp < x1) == 0) { + for (seq = seqbase->first; seq; seq = seq->next) { + const int startdisp = seq->startdisp + offset; + const int enddisp = seq->enddisp + offset; + + if ((startdisp > x2 || enddisp < x1) == 0) { float y_chan = (seq->machine - chan_min) / (float)(chan_range) * draw_range; - float x1_chan = seq->startdisp; - float x2_chan = seq->enddisp; + float x1_chan = startdisp; + float x2_chan = enddisp; float y1_chan, y2_chan; if ((seqm->flag & SEQ_MUTE) == 0 && (seq->flag & SEQ_MUTE)) @@ -817,23 +834,27 @@ static void draw_seq_strip(const bContext *C, SpaceSeq *sseq, Scene *scene, AReg glDisable(GL_LINE_STIPPLE); } - if (seq->type == SEQ_TYPE_META) { + if ((seq->type == SEQ_TYPE_META) || + ((seq->type == SEQ_TYPE_SCENE) && (seq->flag & SEQ_SCENE_STRIPS))) + { drawmeta_contents(scene, seq, x1, y1, x2, y2); } - - /* calculate if seq is long enough to print a name */ - x1 = seq->startdisp + handsize_clamped; - x2 = seq->enddisp - handsize_clamped; - /* info text on the strip */ - if (x1 < v2d->cur.xmin) x1 = v2d->cur.xmin; - else if (x1 > v2d->cur.xmax) x1 = v2d->cur.xmax; - if (x2 < v2d->cur.xmin) x2 = v2d->cur.xmin; - else if (x2 > v2d->cur.xmax) x2 = v2d->cur.xmax; + if (!(sseq->flag & SEQ_NO_INFO)) { + /* calculate if seq is long enough to print a name */ + x1 = seq->startdisp + handsize_clamped; + x2 = seq->enddisp - handsize_clamped; - /* nice text here would require changing the view matrix for texture text */ - if ((x2 - x1) / pixelx > 32) { - draw_seq_text(v2d, seq, x1, x2, y1, y2, background_col); + /* info text on the strip */ + if (x1 < v2d->cur.xmin) x1 = v2d->cur.xmin; + else if (x1 > v2d->cur.xmax) x1 = v2d->cur.xmax; + if (x2 < v2d->cur.xmin) x2 = v2d->cur.xmin; + else if (x2 > v2d->cur.xmax) x2 = v2d->cur.xmax; + + /* nice text here would require changing the view matrix for texture text */ + if ((x2 - x1) / pixelx > 32) { + draw_seq_text(v2d, seq, x1, x2, y1, y2, background_col); + } } } @@ -955,7 +976,7 @@ static ImBuf *sequencer_make_scope(Scene *scene, ImBuf *ibuf, ImBuf *(*make_scop return scope; } -static void sequencer_display_size(Scene *scene, SpaceSeq *sseq, float r_viewrect[2]) +void sequencer_display_size(Scene *scene, SpaceSeq *sseq, float r_viewrect[2]) { float render_size, proxy_size; @@ -1051,7 +1072,7 @@ static void sequencer_draw_background(const SpaceSeq *sseq, View2D *v2d, const f } } -void draw_image_seq(const bContext *C, Scene *scene, ARegion *ar, SpaceSeq *sseq, int cfra, int frame_ofs, bool draw_overlay, bool draw_backdrop) +void draw_image_seq(const bContext *C, Scene *scene, ARegion *ar, SpaceSeq *sseq, int cfra, int frame_ofs, bool draw_overlay, bool draw_overdrop) { struct Main *bmain = CTX_data_main(C); struct ImBuf *ibuf = NULL; @@ -1070,6 +1091,7 @@ void draw_image_seq(const bContext *C, Scene *scene, ARegion *ar, SpaceSeq *sseq const bool draw_gpencil = ((sseq->flag & SEQ_SHOW_GPENCIL) && sseq->gpd); const char *names[2] = {STEREO_LEFT_NAME, STEREO_RIGHT_NAME}; bool draw_metadata = false; + rctf metadataframe; if (G.is_rendering == false && (scene->r.seq_flag & R_SEQ_GL_PREV) == 0) { /* stop all running jobs, except screen one. currently previews frustrate Render @@ -1085,7 +1107,7 @@ void draw_image_seq(const bContext *C, Scene *scene, ARegion *ar, SpaceSeq *sseq } } - if ((!draw_overlay || sseq->overlay_type == SEQ_DRAW_OVERLAY_REFERENCE) && !draw_backdrop) { + if ((!draw_overlay || sseq->overlay_type == SEQ_DRAW_OVERLAY_REFERENCE) && !draw_overdrop) { UI_GetThemeColor3fv(TH_SEQ_PREVIEW, col); glClearColor(col[0], col[1], col[2], 0.0); glClear(GL_COLOR_BUFFER_BIT); @@ -1122,7 +1144,7 @@ void draw_image_seq(const bContext *C, Scene *scene, ARegion *ar, SpaceSeq *sseq sequencer_display_size(scene, sseq, viewrect); - if (!draw_backdrop && (sseq->mainb != SEQ_DRAW_IMG_IMBUF || sseq->zebra != 0)) { + if (!draw_overdrop && (sseq->mainb != SEQ_DRAW_IMG_IMBUF || sseq->zebra != 0)) { SequencerScopes *scopes = &sseq->scopes; sequencer_check_scopes(scopes, ibuf); @@ -1181,8 +1203,26 @@ void draw_image_seq(const bContext *C, Scene *scene, ARegion *ar, SpaceSeq *sseq } } - if (!draw_backdrop) { - sequencer_draw_background(sseq, v2d, viewrect); + /* without this colors can flicker from previous opengl state */ + glColor4ub(255, 255, 255, 255); + + if (!draw_overdrop) { + UI_view2d_totRect_set(v2d, viewrect[0] + 0.5f, viewrect[1] + 0.5f); + UI_view2d_curRect_validate(v2d); + + /* setting up the view - actual drawing starts here */ + UI_view2d_view_ortho(v2d); + + /* only draw alpha for main buffer */ + if (sseq->mainb == SEQ_DRAW_IMG_IMBUF) { + if (sseq->flag & SEQ_USE_ALPHA) { + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + fdrawcheckerboard(v2d->tot.xmin, v2d->tot.ymin, v2d->tot.xmax, v2d->tot.ymax); + glColor4f(1.0, 1.0, 1.0, 1.0); + } + } } if (scope) { @@ -1274,13 +1314,8 @@ void draw_image_seq(const bContext *C, Scene *scene, ARegion *ar, SpaceSeq *sseq else glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, ibuf->x, ibuf->y, 0, format, type, display_buffer); - if (draw_backdrop) { - glMatrixMode(GL_PROJECTION); - glPushMatrix(); - glLoadIdentity(); - glMatrixMode(GL_MODELVIEW); - glPushMatrix(); - glLoadIdentity(); + if (draw_overdrop) { + UI_view2d_view_restore(C); } glBegin(GL_QUADS); @@ -1304,30 +1339,25 @@ void draw_image_seq(const bContext *C, Scene *scene, ARegion *ar, SpaceSeq *sseq glTexCoord2f(1.0f, 0.0f); glVertex2f(v2d->tot.xmax, v2d->tot.ymin); } } - else if (draw_backdrop) { - float aspect; - float image_aspect = viewrect[0] / viewrect[1]; - float imagex, imagey; + else if (draw_overdrop) { + float imagex = (scene->r.size * scene->r.xsch) / 200.0f * sseq->overdrop_zoom; + float imagey = (scene->r.size * scene->r.ysch) / 200.0f * sseq->overdrop_zoom; + float xofs = BLI_rcti_size_x(&ar->winrct)/2.0f + sseq->overdrop_offset[0]; + float yofs = BLI_rcti_size_y(&ar->winrct)/2.0f + sseq->overdrop_offset[1]; - aspect = BLI_rcti_size_x(&ar->winrct) / (float)BLI_rcti_size_y(&ar->winrct); + draw_metadata = ((sseq->flag & SEQ_SHOW_METADATA) != 0); - if (aspect >= image_aspect) { - imagex = image_aspect / aspect; - imagey = 1.0f; - } - else { - imagex = 1.0f; - imagey = aspect / image_aspect; - } + BLI_rctf_init(&metadataframe, -imagex + xofs, imagex + xofs, -imagey + yofs, imagey + yofs); - glTexCoord2f(0.0f, 0.0f); glVertex2f(-imagex, -imagey); - glTexCoord2f(0.0f, 1.0f); glVertex2f(-imagex, imagey); - glTexCoord2f(1.0f, 1.0f); glVertex2f(imagex, imagey); - glTexCoord2f(1.0f, 0.0f); glVertex2f(imagex, -imagey); + glTexCoord2f(0.0f, 0.0f); glVertex2f(-imagex + xofs, -imagey + yofs); + glTexCoord2f(0.0f, 1.0f); glVertex2f(-imagex + xofs, imagey + yofs); + glTexCoord2f(1.0f, 1.0f); glVertex2f(imagex + xofs, imagey + yofs); + glTexCoord2f(1.0f, 0.0f); glVertex2f(imagex + xofs, -imagey + yofs); } else { draw_metadata = ((sseq->flag & SEQ_SHOW_METADATA) != 0); + metadataframe = v2d->tot; glTexCoord2f(0.0f, 0.0f); glVertex2f(v2d->tot.xmin, v2d->tot.ymin); glTexCoord2f(0.0f, 1.0f); glVertex2f(v2d->tot.xmin, v2d->tot.ymax); glTexCoord2f(1.0f, 1.0f); glVertex2f(v2d->tot.xmax, v2d->tot.ymax); @@ -1351,10 +1381,10 @@ void draw_image_seq(const bContext *C, Scene *scene, ARegion *ar, SpaceSeq *sseq IMB_freeImBuf(ibuf); if (draw_metadata) { - ED_region_image_metadata_draw(0.0, 0.0, ibuf, v2d->tot, 1.0, 1.0); + ED_region_image_metadata_draw(0.0, 0.0, ibuf, metadataframe, 1.0, 1.0); } - if (draw_backdrop) { + if (draw_overdrop) { glPopMatrix(); glMatrixMode(GL_PROJECTION); glPopMatrix(); @@ -1592,11 +1622,13 @@ void draw_timeline_seq(const bContext *C, ARegion *ar) // NOTE: the gridlines are currently spaced every 25 frames, which is only fine for 25 fps, but maybe not for 30... UI_view2d_constant_grid_draw(v2d); + /* if (sseq->draw_flag & SEQ_DRAW_BACKDROP) { draw_image_seq(C, scene, ar, sseq, scene->r.cfra, 0, false, true); UI_view2d_view_ortho(v2d); } - + */ + ED_region_draw_cb_draw(C, ar, REGION_DRAW_PRE_VIEW); seq_draw_sfra_efra(scene, v2d); @@ -1636,12 +1668,22 @@ void draw_timeline_seq(const bContext *C, ARegion *ar) glEnd(); } - + /* callback */ ED_region_draw_cb_draw(C, ar, REGION_DRAW_POST_VIEW); + if (sseq->draw_flag & SEQ_DRAW_OVERDROP) { + draw_image_seq(C, scene, ar, sseq, scene->r.cfra, 0, false, true); + UI_SetTheme(SPACE_SEQ, RGN_TYPE_WINDOW); + UI_view2d_view_ortho(v2d); + } + /* reset view matrix */ UI_view2d_view_restore(C); + + /* finally draw any widgets here */ + WM_widgets_update(C, ar->widgetmaps.first); + WM_widgets_draw(C, ar->widgetmaps.first, false); /* scrollers */ unit = (sseq->flag & SEQ_DRAWFRAMES) ? V2D_UNIT_FRAMES : V2D_UNIT_SECONDS; diff --git a/source/blender/editors/space_sequencer/sequencer_edit.c b/source/blender/editors/space_sequencer/sequencer_edit.c index 8d5f1ea450d..6287c3f5785 100644 --- a/source/blender/editors/space_sequencer/sequencer_edit.c +++ b/source/blender/editors/space_sequencer/sequencer_edit.c @@ -1256,6 +1256,7 @@ void SEQUENCER_OT_snap(struct wmOperatorType *ot) RNA_def_int(ot->srna, "frame", 0, INT_MIN, INT_MAX, "Frame", "Frame where selected strips will be snapped", INT_MIN, INT_MAX); } + typedef struct SlipData { int init_mouse[2]; float init_mouseloc[2]; @@ -2467,7 +2468,16 @@ static int sequencer_meta_toggle_exec(bContext *C, wmOperator *UNUSED(op)) ed->seqbasep = &last_seq->seqbase; BKE_sequencer_active_set(scene, NULL); + } + /* scene strip */ + else if (last_seq && last_seq->type == SEQ_TYPE_SCENE && last_seq->scene && + (last_seq->flag & SEQ_SCENE_STRIPS) && (last_seq->flag & SELECT)) + { + ED_screen_set_scene(C, CTX_wm_screen(C), last_seq->scene); + + WM_event_add_notifier(C, NC_SCENE | ND_SCENEBROWSE, last_seq->scene); + return OPERATOR_FINISHED; } else { /* Exit Metastrip (if possible) */ diff --git a/source/blender/editors/space_sequencer/sequencer_intern.h b/source/blender/editors/space_sequencer/sequencer_intern.h index 0c6475cd10e..2cc7fe55eb8 100644 --- a/source/blender/editors/space_sequencer/sequencer_intern.h +++ b/source/blender/editors/space_sequencer/sequencer_intern.h @@ -58,6 +58,7 @@ void draw_image_seq(const struct bContext *C, struct Scene *scene, struct ARegi void color3ubv_from_seq(struct Scene *curscene, struct Sequence *seq, unsigned char col[3]); void draw_shadedstrip(struct Sequence *seq, unsigned char col[3], float x1, float y1, float x2, float y2); void draw_sequence_extensions(struct Scene *scene, struct ARegion *ar, struct Sequence *seq); +void sequencer_display_size(struct Scene *scene, struct SpaceSeq *sseq, float r_viewrect[2]); void sequencer_special_update_set(Sequence *seq); @@ -134,6 +135,9 @@ void SEQUENCER_OT_paste(struct wmOperatorType *ot); void SEQUENCER_OT_rebuild_proxy(struct wmOperatorType *ot); void SEQUENCER_OT_enable_proxies(struct wmOperatorType *ot); +void SEQUENCER_OT_overdrop_transform(struct wmOperatorType *ot); +void SEQUENCER_OT_image_transform_widget(struct wmOperatorType *ot); + /* preview specific operators */ void SEQUENCER_OT_view_all_preview(struct wmOperatorType *ot); @@ -157,6 +161,7 @@ void SEQUENCER_OT_movieclip_strip_add(struct wmOperatorType *ot); void SEQUENCER_OT_mask_strip_add(struct wmOperatorType *ot); void SEQUENCER_OT_sound_strip_add(struct wmOperatorType *ot); void SEQUENCER_OT_image_strip_add(struct wmOperatorType *ot); +void SEQUENCER_OT_image_sequence_add(struct wmOperatorType *ot); void SEQUENCER_OT_effect_strip_add(struct wmOperatorType *ot); enum { diff --git a/source/blender/editors/space_sequencer/sequencer_ops.c b/source/blender/editors/space_sequencer/sequencer_ops.c index 5bc7fea9a55..b55ee7987f8 100644 --- a/source/blender/editors/space_sequencer/sequencer_ops.c +++ b/source/blender/editors/space_sequencer/sequencer_ops.c @@ -112,6 +112,7 @@ void sequencer_operatortypes(void) WM_operatortype_append(SEQUENCER_OT_movie_strip_add); WM_operatortype_append(SEQUENCER_OT_sound_strip_add); WM_operatortype_append(SEQUENCER_OT_image_strip_add); + WM_operatortype_append(SEQUENCER_OT_image_sequence_add); WM_operatortype_append(SEQUENCER_OT_effect_strip_add); /* sequencer_buttons.c */ @@ -124,6 +125,8 @@ void sequencer_operatortypes(void) /* sequencer_view.h */ WM_operatortype_append(SEQUENCER_OT_sample); + WM_operatortype_append(SEQUENCER_OT_overdrop_transform); + WM_operatortype_append(SEQUENCER_OT_image_transform_widget); } @@ -199,6 +202,8 @@ void sequencer_keymap(wmKeyConfig *keyconf) WM_keymap_add_item(keymap, "SEQUENCER_OT_view_all", HOMEKEY, KM_PRESS, 0, 0); WM_keymap_add_item(keymap, "SEQUENCER_OT_view_all", NDOF_BUTTON_FIT, KM_PRESS, 0, 0); WM_keymap_add_item(keymap, "SEQUENCER_OT_view_selected", PADPERIOD, KM_PRESS, 0, 0); + + WM_keymap_add_item(keymap, "SEQUENCER_OT_overdrop_transform", VKEY, KM_PRESS, 0, 0); kmi = WM_keymap_add_item(keymap, "SEQUENCER_OT_strip_jump", PAGEUPKEY, KM_PRESS, 0, 0); RNA_boolean_set(kmi->ptr, "next", true); @@ -342,6 +347,8 @@ void sequencer_keymap(wmKeyConfig *keyconf) /* would prefer to use numpad keys for job */ RNA_float_set(WM_keymap_add_item(keymap, "SEQUENCER_OT_view_zoom_ratio", PAD1, KM_PRESS, 0, 0)->ptr, "ratio", 1.0f); +// WM_keymap_add_item(keymap, "SEQUENCER_OT_image_transform_widget", VKEY, KM_PRESS, 0, 0); + /* Setting zoom levels is not that useful, except for back to zoom level 1, removing keymap because of conflicts for now */ #if 0 RNA_float_set(WM_keymap_add_item(keymap, "SEQUENCER_OT_view_zoom_ratio", PAD8, KM_PRESS, KM_SHIFT, 0)->ptr, "ratio", 8.0f); diff --git a/source/blender/editors/space_sequencer/sequencer_view.c b/source/blender/editors/space_sequencer/sequencer_view.c index 4d6ea865b40..a0f0c80883d 100644 --- a/source/blender/editors/space_sequencer/sequencer_view.c +++ b/source/blender/editors/space_sequencer/sequencer_view.c @@ -33,8 +33,10 @@ #include "BLI_math.h" #include "BLI_utildefines.h" +#include "BLI_rect.h" #include "DNA_scene_types.h" +#include "DNA_widget_types.h" #include "BKE_context.h" #include "BKE_main.h" @@ -47,6 +49,7 @@ #include "ED_image.h" #include "ED_screen.h" #include "ED_space_api.h" +#include "ED_sequencer.h" #include "IMB_imbuf.h" #include "IMB_imbuf_types.h" @@ -54,6 +57,8 @@ #include "UI_view2d.h" +#include "RNA_define.h" + /* own include */ #include "sequencer_intern.h" @@ -241,3 +246,333 @@ void SEQUENCER_OT_sample(wmOperatorType *ot) /* flags */ ot->flag = OPTYPE_BLOCKING; } + +/******** Backdrop Transform *******/ + +typedef struct OverDropTransformData { + ImBuf *ibuf; /* image to be transformed (preview image transformation widget) */ + int init_size[2]; + float init_zoom; + float init_offset[2]; + int event_type; + wmWidgetGroupType *cagetype; +} OverDropTransformData; + +static int sequencer_overdrop_transform_poll(bContext *C) +{ + SpaceSeq *sseq = CTX_wm_space_seq(C); + ARegion *ar = CTX_wm_region(C); + + return (sseq && ar && ar->type->regionid == RGN_TYPE_WINDOW && (sseq->draw_flag & SEQ_DRAW_OVERDROP)); +} + +static void widgetgroup_overdrop_draw(const struct bContext *C, struct wmWidgetGroup *wgroup) +{ + ARegion *ar = CTX_wm_region(C); + wmOperator *op = wgroup->type->op; + Scene *sce = CTX_data_scene(C); + int sizex = (sce->r.size * sce->r.xsch) / 100; + int sizey = (sce->r.size * sce->r.ysch) / 100; + float origin[3]; + + wmWidget *cage = WIDGET_rect_transform_new(wgroup, WIDGET_RECT_TRANSFORM_STYLE_SCALE_UNIFORM | + WIDGET_RECT_TRANSFORM_STYLE_TRANSLATE, sizex, sizey); + WM_widget_property(cage, RECT_TRANSFORM_SLOT_OFFSET, op->ptr, "offset"); + WM_widget_property(cage, RECT_TRANSFORM_SLOT_SCALE, op->ptr, "scale"); + + origin[0] = BLI_rcti_size_x(&ar->winrct)/2.0f; + origin[1] = BLI_rcti_size_y(&ar->winrct)/2.0f; + + WM_widget_set_origin(cage, origin); +} + +static int sequencer_overdrop_transform_invoke(bContext *C, wmOperator *op, const wmEvent *event) +{ + ScrArea *sa = CTX_wm_area(C); + SpaceSeq *sseq = CTX_wm_space_seq(C); + /* no poll, lives always for the duration of the operator */ + wmWidgetGroupType *cagetype = WM_widgetgrouptype_new(NULL, widgetgroup_overdrop_draw, CTX_data_main(C), "Seq_Canvas", SPACE_SEQ, RGN_TYPE_WINDOW, false); + struct wmEventHandler *handler = WM_event_add_modal_handler(C, op); + OverDropTransformData *data = MEM_mallocN(sizeof(OverDropTransformData), "overdrop transform data"); + WM_modal_handler_attach_widgetgroup(C, handler, cagetype, op); + + RNA_float_set_array(op->ptr, "offset", sseq->overdrop_offset); + RNA_float_set(op->ptr, "scale", sseq->overdrop_zoom); + + copy_v2_v2(data->init_offset, sseq->overdrop_offset); + data->init_zoom = sseq->overdrop_zoom; + data->cagetype = cagetype; + data->event_type = event->type; + + op->customdata = data; + + ED_area_headerprint(sa, "Drag to place, and scale, Space/Enter/Caller key to confirm, R to recenter, RClick/Esc to cancel"); + + return OPERATOR_RUNNING_MODAL; +} + +static void sequencer_overdrop_finish(bContext *C, OverDropTransformData *data) +{ + ScrArea *sa = CTX_wm_area(C); + ED_area_headerprint(sa, NULL); + WM_widgetgrouptype_unregister(C, CTX_data_main(C), data->cagetype); + MEM_freeN(data); +} + +static void sequencer_overdrop_cancel(struct bContext *C, struct wmOperator *op) +{ + OverDropTransformData *data = op->customdata; + sequencer_overdrop_finish(C, data); +} + +static int sequencer_overdrop_transform_modal(bContext *C, wmOperator *op, const wmEvent *event) +{ + OverDropTransformData *data = op->customdata; + + if (event->type == data->event_type && event->val == KM_PRESS) { + sequencer_overdrop_finish(C, data); + return OPERATOR_FINISHED; + } + + switch (event->type) { + case EVT_WIDGET_UPDATE: { + SpaceSeq *sseq = CTX_wm_space_seq(C); + RNA_float_get_array(op->ptr, "offset", sseq->overdrop_offset); + sseq->overdrop_zoom = RNA_float_get(op->ptr, "scale"); + break; + } + + case RKEY: + { + SpaceSeq *sseq = CTX_wm_space_seq(C); + ARegion *ar = CTX_wm_region(C); + float zero[2] = {0.0f}; + RNA_float_set_array(op->ptr, "offset", zero); + RNA_float_set(op->ptr, "scale", 1.0f); + copy_v2_v2(sseq->overdrop_offset, zero); + sseq->overdrop_zoom = 1.0; + ED_region_tag_redraw(ar); + /* add a mousemove to refresh the widget */ + WM_event_add_mousemove(C); + break; + } + case RETKEY: + case PADENTER: + case SPACEKEY: + { + sequencer_overdrop_finish(C, data); + return OPERATOR_FINISHED; + } + + case ESCKEY: + case RIGHTMOUSE: + { + SpaceSeq *sseq = CTX_wm_space_seq(C); + copy_v2_v2(sseq->overdrop_offset, data->init_offset); + sseq->overdrop_zoom = data->init_zoom; + + sequencer_overdrop_finish(C, data); + return OPERATOR_CANCELLED; + } + } + + return OPERATOR_RUNNING_MODAL; +} + +void SEQUENCER_OT_overdrop_transform(struct wmOperatorType *ot) +{ + float default_offset[2] = {0.0f, 0.0f}; + + /* identifiers */ + ot->name = "Change Data/Files"; + ot->idname = "SEQUENCER_OT_overdrop_transform"; + ot->description = ""; + + /* api callbacks */ + ot->invoke = sequencer_overdrop_transform_invoke; + ot->modal = sequencer_overdrop_transform_modal; + ot->poll = sequencer_overdrop_transform_poll; + ot->cancel = sequencer_overdrop_cancel; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + RNA_def_float_array(ot->srna, "offset", 2, default_offset, FLT_MIN, FLT_MAX, "Offset", "Offset of the backdrop", FLT_MIN, FLT_MAX); + RNA_def_float(ot->srna, "scale", 1.0f, 0.0f, FLT_MAX, "Scale", "Scale of the backdrop", 0.0f, FLT_MAX); +} + +/******** transform widget (preview area) *******/ + +typedef struct ImageTransformData { + ImBuf *ibuf; /* image to be transformed (preview image transformation widget) */ + int init_size[2]; + int event_type; + wmWidgetGroupType *cagetype; +} ImageTransformData; + +static int sequencer_image_transform_widget_poll(bContext *C) +{ + SpaceSeq *sseq = CTX_wm_space_seq(C); + ARegion *ar = CTX_wm_region(C); + + return (sseq && ar && ar->type->regionid == RGN_TYPE_PREVIEW); +} + +static void widgetgroup_image_transform_draw(const struct bContext *C, struct wmWidgetGroup *wgroup) +{ + ARegion *ar = CTX_wm_region(C); + View2D *v2d = &ar->v2d; + wmOperator *op = wgroup->type->op; + wmWidget *cage; + float origin[3]; + float viewrect[2]; + float scale[2]; + + sequencer_display_size(CTX_data_scene(C), CTX_wm_space_seq(C), viewrect); + UI_view2d_scale_get(v2d, &scale[0], &scale[1]); + + cage = WIDGET_rect_transform_new(wgroup, WIDGET_RECT_TRANSFORM_STYLE_SCALE_UNIFORM | + WIDGET_RECT_TRANSFORM_STYLE_TRANSLATE, + viewrect[0] * scale[0], viewrect[1] * scale[1]); + WM_widget_property(cage, RECT_TRANSFORM_SLOT_SCALE, op->ptr, "scale"); + + origin[0] = -(v2d->cur.xmin * scale[0]); + origin[1] = -(v2d->cur.ymin * scale[1]); + WM_widget_set_origin(cage, origin); +} + +static int sequencer_image_transform_widget_invoke(bContext *C, wmOperator *op, const wmEvent *event) +{ + ScrArea *sa = CTX_wm_area(C); + SpaceSeq *sseq = CTX_wm_space_seq(C); + Scene *scene = CTX_data_scene(C); + /* no poll, lives always for the duration of the operator */ + wmWidgetGroupType *cagetype = WM_widgetgrouptype_new(NULL, widgetgroup_image_transform_draw, CTX_data_main(C), + "Seq_Canvas", SPACE_SEQ, RGN_TYPE_PREVIEW, false); + struct wmEventHandler *handler = WM_event_add_modal_handler(C, op); + ImageTransformData *data = MEM_mallocN(sizeof(ImageTransformData), "overdrop transform data"); + ImBuf *ibuf = sequencer_ibuf_get(CTX_data_main(C), scene, sseq, CFRA, 0, NULL); + + if (!ibuf || !ED_space_sequencer_check_show_imbuf(sseq)) { + return OPERATOR_CANCELLED; + } + + WM_modal_handler_attach_widgetgroup(C, handler, cagetype, op); + + copy_v2_v2_int(data->init_size, &ibuf->x); + data->cagetype = cagetype; + data->event_type = event->type; + data->ibuf = ibuf; + + op->customdata = data; + + ED_area_headerprint(sa, "Drag to place, and scale, Space/Enter/Caller key to confirm, R to recenter, RClick/Esc to cancel"); + + return OPERATOR_RUNNING_MODAL; +} + +static void sequencer_image_transform_widget_finish(bContext *C, ImageTransformData *data) +{ + ScrArea *sa = CTX_wm_area(C); + ED_area_headerprint(sa, NULL); + WM_widgetgrouptype_unregister(C, CTX_data_main(C), data->cagetype); + MEM_freeN(data); +} + +static void sequencer_image_transform_widget_cancel(struct bContext *C, struct wmOperator *op) +{ + ImageTransformData *data = op->customdata; + sequencer_image_transform_widget_finish(C, data); +} + +static int sequencer_image_transform_widget_modal(bContext *C, wmOperator *op, const wmEvent *event) +{ + ImageTransformData *data = op->customdata; + + if (event->type == data->event_type && event->val == KM_PRESS) { + sequencer_image_transform_widget_finish(C, data); + return OPERATOR_FINISHED; + } + + switch (event->type) { + case EVT_WIDGET_UPDATE: + { + ARegion *ar = CTX_wm_region(C); + Scene *scene = CTX_data_scene(C); + wmWidgetMap *wmap = ar->widgetmaps.first; + float scale_fac = RNA_float_get(op->ptr, "scale"); + float new_size[2]; + float offset[2]; + + new_size[0] = (float)data->init_size[0] * scale_fac; + new_size[1] = (float)data->init_size[1] * scale_fac; + + /* sale image */ + IMB_scalefastImBuf(data->ibuf, (unsigned int)new_size[0], (unsigned int)new_size[1]); + + /* update view */ + scene->r.xsch = (int)(new_size[0] / ((float)scene->r.size / 100)); + scene->r.ysch = (int)(new_size[1] / ((float)scene->r.size / 100)); + + /* no offset needed in this case */ + offset[0] = offset[1] = 0; + WIDGET_rect_transform_set_offset(wmap->active_widget, offset); + break; + } + + case RKEY: + { +// SpaceSeq *sseq = CTX_wm_space_seq(C); + ARegion *ar = CTX_wm_region(C); +// float zero[2] = {0.0f}; +// RNA_float_set_array(op->ptr, "offset", zero); +// RNA_float_set(op->ptr, "scale", 1.0f); +// copy_v2_v2(sseq->overdrop_offset, zero); +// sseq->overdrop_zoom = 1.0; + ED_region_tag_redraw(ar); + /* add a mousemove to refresh the widget */ + WM_event_add_mousemove(C); + break; + } + case RETKEY: + case PADENTER: + case SPACEKEY: + { + sequencer_image_transform_widget_finish(C, data); + return OPERATOR_FINISHED; + } + + case ESCKEY: + case RIGHTMOUSE: + { +// SpaceSeq *sseq = CTX_wm_space_seq(C); +// copy_v2_v2(sseq->overdrop_offset, data->init_offset); +// sseq->overdrop_zoom = data->init_zoom; + + sequencer_image_transform_widget_finish(C, data); + return OPERATOR_CANCELLED; + } + } + + return OPERATOR_RUNNING_MODAL; +} + +void SEQUENCER_OT_image_transform_widget(struct wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Image Transform"; + ot->idname = "SEQUENCER_OT_image_transform_widget"; + ot->description = "Transform the image using a widget"; + + /* api callbacks */ + ot->invoke = sequencer_image_transform_widget_invoke; + ot->modal = sequencer_image_transform_widget_modal; + ot->poll = sequencer_image_transform_widget_poll; + ot->cancel = sequencer_image_transform_widget_cancel; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + RNA_def_float(ot->srna, "scale", 1.0f, 0.0f, FLT_MAX, "Scale", "Scale of the backdrop", 0.0f, FLT_MAX); +} + diff --git a/source/blender/editors/space_sequencer/space_sequencer.c b/source/blender/editors/space_sequencer/space_sequencer.c index 769718e1567..92a16ce6b7b 100644 --- a/source/blender/editors/space_sequencer/space_sequencer.c +++ b/source/blender/editors/space_sequencer/space_sequencer.c @@ -121,6 +121,9 @@ static SpaceLink *sequencer_new(const bContext *C) sseq->mainb = SEQ_DRAW_IMG_IMBUF; sseq->flag = SEQ_SHOW_GPENCIL | SEQ_USE_ALPHA; + /* backdrop */ + sseq->overdrop_zoom = 1.0f; + /* header */ ar = MEM_callocN(sizeof(ARegion), "header for sequencer"); @@ -166,7 +169,7 @@ static SpaceLink *sequencer_new(const bContext *C) BLI_addtail(&sseq->regionbase, ar); ar->regiontype = RGN_TYPE_WINDOW; - + /* seq space goes from (0,8) to (0, efra) */ ar->v2d.tot.xmin = 0.0f; @@ -180,7 +183,7 @@ static SpaceLink *sequencer_new(const bContext *C) ar->v2d.min[1] = 0.5f; ar->v2d.max[0] = MAXFRAMEF; - ar->v2d.max[1] = MAXSEQ; + ar->v2d.max[1] = MAXSEQ * 4; ar->v2d.minzoom = 0.01f; ar->v2d.maxzoom = 100.0f; @@ -478,8 +481,13 @@ static void sequencer_main_area_init(wmWindowManager *wm, ARegion *ar) /* add drop boxes */ lb = WM_dropboxmap_find("Sequencer", SPACE_SEQ, RGN_TYPE_WINDOW); - + WM_event_add_dropbox_handler(&ar->handlers, lb); + + /* no modal keymap here, only operators use this currently */ + if (BLI_listbase_is_empty(&ar->widgetmaps)) { + BLI_addhead(&ar->widgetmaps, WM_widgetmap_from_type("Seq_Canvas", SPACE_SEQ, RGN_TYPE_WINDOW, false)); + } } static void sequencer_main_area_draw(const bContext *C, ARegion *ar) @@ -556,6 +564,10 @@ static void sequencer_preview_area_init(wmWindowManager *wm, ARegion *ar) /* own keymap */ keymap = WM_keymap_find(wm->defaultconf, "SequencerPreview", SPACE_SEQ, 0); WM_event_add_keymap_handler_bb(&ar->handlers, keymap, &ar->v2d.mask, &ar->winrct); + + if (BLI_listbase_is_empty(&ar->widgetmaps)) { + BLI_addhead(&ar->widgetmaps, WM_widgetmap_from_type("Seq_Canvas", SPACE_SEQ, RGN_TYPE_PREVIEW, false)); + } } static void sequencer_preview_area_draw(const bContext *C, ARegion *ar) @@ -592,6 +604,9 @@ static void sequencer_preview_area_draw(const bContext *C, ARegion *ar) ED_region_visible_rect(ar, &rect); ED_scene_draw_fps(scene, &rect); } + + WM_widgets_update(C, ar->widgetmaps.first); + WM_widgets_draw(C, ar->widgetmaps.first, false); } static void sequencer_preview_area_listener(bScreen *UNUSED(sc), ScrArea *UNUSED(sa), ARegion *ar, wmNotifier *wmn) @@ -687,8 +702,18 @@ static void sequencer_buttons_area_listener(bScreen *UNUSED(sc), ScrArea *UNUSED break; } } + /* ************************************* */ +static void sequencer_widgets(void) +{ + /* create the widgetmap for the area here */ + WM_widgetmaptype_find("Seq_Canvas", SPACE_SEQ, RGN_TYPE_WINDOW, false, true); + + WM_widgetmaptype_find("Seq_Canvas", SPACE_SEQ, RGN_TYPE_PREVIEW, false, true); +} + + /* only called once, from space/spacetypes.c */ void ED_spacetype_sequencer(void) { @@ -708,6 +733,7 @@ void ED_spacetype_sequencer(void) st->dropboxes = sequencer_dropboxes; st->refresh = sequencer_refresh; st->listener = sequencer_listener; + st->widgets = sequencer_widgets; /* regions: main window */ art = MEM_callocN(sizeof(ARegionType), "spacetype sequencer region"); diff --git a/source/blender/editors/space_view3d/CMakeLists.txt b/source/blender/editors/space_view3d/CMakeLists.txt index 967636c377f..dd81af5977c 100644 --- a/source/blender/editors/space_view3d/CMakeLists.txt +++ b/source/blender/editors/space_view3d/CMakeLists.txt @@ -47,6 +47,7 @@ set(SRC drawmesh.c drawobject.c drawsimdebug.c + drawstrands.c drawvolume.c space_view3d.c view3d_buttons.c diff --git a/source/blender/editors/space_view3d/drawarmature.c b/source/blender/editors/space_view3d/drawarmature.c index f33f074e6c5..863a2d64646 100644 --- a/source/blender/editors/space_view3d/drawarmature.c +++ b/source/blender/editors/space_view3d/drawarmature.c @@ -1739,6 +1739,8 @@ static void draw_pose_bones(Scene *scene, View3D *v3d, ARegion *ar, Base *base, { if (bone->layer & arm->layer) { const bool use_custom = (pchan->custom) && !(arm->flag & ARM_NO_CUSTOM); + + /* skip drawing in that case */ glPushMatrix(); if (use_custom && pchan->custom_tx) { @@ -2305,7 +2307,7 @@ static void draw_ebones(View3D *v3d, ARegion *ar, Object *ob, const short dt) /* draw bone paths * - in view space */ -static void draw_pose_paths(Scene *scene, View3D *v3d, ARegion *ar, Object *ob) +void draw_pose_paths(Scene *scene, View3D *v3d, ARegion *ar, Object *ob) { bAnimVizSettings *avs = &ob->pose->avs; bArmature *arm = ob->data; @@ -2313,7 +2315,7 @@ static void draw_pose_paths(Scene *scene, View3D *v3d, ARegion *ar, Object *ob) /* setup drawing environment for paths */ draw_motion_paths_init(v3d, ar); - + /* draw paths where they exist and they releated bone is visible */ for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) { if ((pchan->bone->layer & arm->layer) && (pchan->mpath)) @@ -2606,7 +2608,7 @@ bool draw_armature(Scene *scene, View3D *v3d, ARegion *ar, Base *base, if (v3d->flag2 & V3D_RENDER_OVERRIDE) return true; - + if (dt > OB_WIRE && !ELEM(arm->drawtype, ARM_LINE, ARM_WIRE)) { /* we use color for solid lighting */ const float white[4] = {1.0f, 1.0f, 1.0f, 1.0f}; @@ -2658,7 +2660,6 @@ bool draw_armature(Scene *scene, View3D *v3d, ARegion *ar, Base *base, if (ob == modifiers_isDeformedByArmature(OBACT)) arm->flag |= ARM_POSEMODE; } - draw_pose_paths(scene, v3d, ar, ob); } } } diff --git a/source/blender/editors/space_view3d/drawobject.c b/source/blender/editors/space_view3d/drawobject.c index 5c62534f028..57bff33e080 100644 --- a/source/blender/editors/space_view3d/drawobject.c +++ b/source/blender/editors/space_view3d/drawobject.c @@ -29,6 +29,7 @@ #include "MEM_guardedalloc.h" +#include "DNA_armature_types.h" #include "DNA_camera_types.h" #include "DNA_curve_types.h" #include "DNA_constraint_types.h" /* for drawing constraint */ @@ -48,6 +49,7 @@ #include "BLI_string.h" #include "BLI_math.h" #include "BLI_memarena.h" +#include "BLI_rand.h" #include "BKE_anim.h" /* for the where_on_path function */ #include "BKE_armature.h" @@ -58,6 +60,7 @@ #include "BKE_DerivedMesh.h" #include "BKE_deform.h" #include "BKE_displist.h" +#include "BKE_editstrands.h" #include "BKE_font.h" #include "BKE_global.h" #include "BKE_image.h" @@ -88,6 +91,7 @@ #include "GPU_draw.h" #include "GPU_extensions.h" #include "GPU_select.h" +#include "GPU_buffers.h" #include "ED_mesh.h" #include "ED_particle.h" @@ -313,9 +317,6 @@ bool draw_glsl_material(Scene *scene, Object *ob, View3D *v3d, const char dt) static bool check_alpha_pass(Base *base) { - if (base->flag & OB_FROMDUPLI) - return false; - if (G.f & G_PICKSEL) return false; @@ -3946,7 +3947,7 @@ static void draw_em_fancy(Scene *scene, ARegion *ar, View3D *v3d, static void draw_mesh_object_outline(View3D *v3d, Object *ob, DerivedMesh *dm) { if ((v3d->transp == false) && /* not when we draw the transparent pass */ - (ob->mode & OB_MODE_ALL_PAINT) == false) /* not when painting (its distracting) - campbell */ + (ob->mode & OB_MODE_ALL_BRUSH) == false) /* not when painting (its distracting) - campbell */ { glLineWidth(UI_GetThemeValuef(TH_OUTLINE_WIDTH) * 2.0f); glDepthMask(0); @@ -4318,21 +4319,29 @@ static bool draw_mesh_object(Scene *scene, ARegion *ar, View3D *v3d, RegionView3 } draw_mesh_fancy(scene, ar, v3d, rv3d, base, dt, ob_wire_col, dflag); - + GPU_end_object_materials(); if (me->totvert == 0) retval = true; } } - if ((dflag & DRAW_PICKING) == 0 && (base->flag & OB_FROMDUPLI) == 0 && (v3d->flag2 & V3D_RENDER_SHADOW) == 0) { + if ((dflag & DRAW_PICKING) == 0 && ((base->flag & OB_FROMDUPLI) == 0 || (ob->dtx & OB_DRAWTRANSP)) && (v3d->flag2 & V3D_RENDER_SHADOW) == 0) { /* GPU_begin_object_materials checked if this is needed */ if (do_alpha_after) { - if (ob->dtx & OB_DRAWXRAY) { + /* duplis only for transparency */ + if ((ob->dtx & OB_DRAWXRAY) && !(base->flag & OB_FROMDUPLI)) { ED_view3d_after_add(&v3d->afterdraw_xraytransp, base, dflag); } else { - ED_view3d_after_add(&v3d->afterdraw_transp, base, dflag); + if (base->flag & OB_FROMDUPLI) { + Base *nbase = MEM_mallocN(sizeof(Base), "dupli_trans"); + *nbase = *base; + ED_view3d_after_add(&v3d->afterdraw_transp, nbase, dflag); + } + else { + ED_view3d_after_add(&v3d->afterdraw_transp, base, dflag); + } } } else if (ob->dtx & OB_DRAWXRAY && ob->dtx & OB_DRAWTRANSP) { @@ -4795,6 +4804,377 @@ static bool drawDispList(Scene *scene, View3D *v3d, RegionView3D *rv3d, Base *ba } /* *********** drawing for particles ************* */ + +BLI_INLINE unsigned int hash_int_2d(unsigned int kx, unsigned int ky) +{ +#define rot(x,k) (((x)<<(k)) | ((x)>>(32-(k)))) + + unsigned int a, b, c; + + a = b = c = 0xdeadbeef + (2 << 2) + 13; + a += kx; + b += ky; + + c ^= b; c -= rot(b,14); + a ^= c; a -= rot(c,11); + b ^= a; b -= rot(a,25); + c ^= b; c -= rot(b,16); + a ^= c; a -= rot(c,4); + b ^= a; b -= rot(a,14); + c ^= b; c -= rot(b,24); + + return c; + +#undef rot +} + +BLI_INLINE unsigned int hash_int(unsigned int k) +{ + return hash_int_2d(k, 0); +} + +static void particle_path_color(int index, float col[3]) +{ + unsigned seed = hash_int(index); + + BLI_srandom(seed); + hsv_to_rgb(BLI_frand(), 1.0f, 1.0f, col+0, col+1, col+2); +} + +#ifdef USE_PARTICLE_HULL_DRAWING +static void draw_particle_hull_section(ParticleCacheKey *path, ParticleCacheKey *npath) +{ + int segments = max_ii(path->segments, npath->segments); + int k; + + for (k = 0; k < segments; ++k) { + int k0 = max_ii(k-1, 0); + int k1 = k; + int k2 = k+1; + int k3 = min_ii(k+2, path->segments); + int nk0 = max_ii(min_ii(k-1, npath->segments), 0); + int nk1 = min_ii(k, npath->segments); + int nk2 = min_ii(k+1, npath->segments); + int nk3 = min_ii(k+2, npath->segments); + float *co[2][4]; + + float nor01[3], nor11[3], nor02[3], nor12[3]; + + co[0][0] = path[k0].co; + co[0][1] = path[k1].co; + co[0][2] = path[k2].co; + co[0][3] = path[k3].co; + co[1][0] = npath[nk0].co; + co[1][1] = npath[nk1].co; + co[1][2] = npath[nk2].co; + co[1][3] = npath[nk3].co; + + normal_quad_v3(nor01, co[0][1], co[0][0], co[1][1], co[0][2]); + normal_quad_v3(nor02, co[0][2], co[0][1], co[1][2], co[0][3]); + normal_quad_v3(nor11, co[1][1], co[1][2], co[0][1], co[1][0]); + normal_quad_v3(nor12, co[1][2], co[1][3], co[0][2], co[1][1]); + + glNormal3fv(nor01); + glVertex3fv(path[k1].co); + glNormal3fv(nor11); + glVertex3fv(npath[nk1].co); + glNormal3fv(nor12); + glVertex3fv(npath[nk2].co); + glNormal3fv(nor02); + glVertex3fv(path[k2].co); + } +} + +static void draw_particle_hull_cap(ParticleCacheKey *a0, ParticleCacheKey *a1, ParticleCacheKey *a2, ParticleCacheKey *a3, + ParticleCacheKey *b0, ParticleCacheKey *b1, ParticleCacheKey *b2, ParticleCacheKey *b3) +{ + float *ca[4], *cb[4]; + + float na[4][3], nb[4][3]; + + if (a1 == b1) { + ca[1] = cb[1] = a1[a1->segments].co; + ca[2] = a2[a2->segments].co; + cb[2] = b2[b2->segments].co; + ca[3] = a3[a3->segments].co; + cb[3] = b3[b3->segments].co; + + normal_tri_v3(na[1], ca[1], cb[2], ca[2]); + normal_quad_v3(na[2], ca[2], ca[1], cb[2], ca[3]); + normal_quad_v3(nb[2], cb[2], cb[3], ca[2], cb[1]); + + glNormal3fv(na[2]); + glVertex3fv(ca[2]); + + glNormal3fv(na[1]); + glVertex3fv(ca[1]); + + glNormal3fv(nb[2]); + glVertex3fv(cb[2]); + } + else if (a2 == b2) { + ca[0] = a0[a0->segments].co; + cb[0] = b0[b0->segments].co; + ca[1] = a1[a1->segments].co; + cb[1] = b1[b1->segments].co; + ca[2] = cb[2] = a2[a2->segments].co; + + normal_quad_v3(na[1], ca[1], ca[0], cb[1], ca[2]); + normal_quad_v3(nb[1], cb[1], cb[2], ca[1], cb[0]); + normal_tri_v3(na[2], ca[2], ca[1], cb[1]); + + glNormal3fv(na[1]); + glVertex3fv(ca[1]); + + glNormal3fv(nb[1]); + glVertex3fv(cb[1]); + + glNormal3fv(nb[2]); + glVertex3fv(cb[2]); + } + else { + ca[0] = a0[a0->segments].co; + cb[0] = b0[b0->segments].co; + ca[1] = a1[a1->segments].co; + cb[1] = b1[b1->segments].co; + ca[2] = a2[a2->segments].co; + cb[2] = b2[b2->segments].co; + ca[3] = a3[a3->segments].co; + cb[3] = b3[b3->segments].co; + + normal_quad_v3(na[1], ca[1], ca[0], cb[1], ca[2]); + normal_quad_v3(na[2], ca[2], ca[1], cb[2], ca[3]); + normal_quad_v3(nb[1], cb[1], cb[2], ca[1], cb[0]); + normal_quad_v3(nb[2], cb[2], cb[3], ca[2], cb[1]); + + glNormal3fv(na[1]); + glVertex3fv(ca[1]); + + glNormal3fv(nb[1]); + glVertex3fv(cb[1]); + + glNormal3fv(nb[2]); + glVertex3fv(cb[2]); + + glNormal3fv(na[2]); + glVertex3fv(ca[2]); + + glNormal3fv(na[1]); + glVertex3fv(ca[1]); + + glNormal3fv(nb[2]); + glVertex3fv(cb[2]); + } +} + +BLI_INLINE bool particle_path_valid(ParticleCacheKey **cache, int p) +{ + return (cache[p]->segments >= 0 && cache[p]->hull_parent >= 0); +} + +BLI_INLINE int particle_path_next(ParticleCacheKey **cache, int pmax, int p) +{ + do { + ++p; + if (p >= pmax) + break; + if (particle_path_valid(cache, p)) + break; + } while (true); + + return p; +} + +BLI_INLINE int particle_path_prev(ParticleCacheKey **cache, int pmin, int p) +{ + do { + --p; + if (p < pmin) + break; + if (particle_path_valid(cache, p)) + break; + } while (true); + + return p; +} + +static void draw_particle_hair_hull(Scene *UNUSED(scene), View3D *v3d, RegionView3D *rv3d, + Base *base, ParticleSystem *psys, + const char UNUSED(ob_dt), const short dflag) +{ + Object *ob = base->object; + ParticleSettings *part = psys->part; + Material *ma = give_current_material(ob, part->omat); + unsigned char tcol[4] = {0, 0, 0, 255}; + GLint polygonmode[2]; + int totchild; + + bool draw_constcolor = dflag & DRAW_CONSTCOLOR; + + ParticleCacheKey **cache; + + if (part->type == PART_HAIR && !psys->childcache) + totchild = 0; + else + totchild = psys->totchild * part->disp / 100; + + if (v3d->zbuf) + glDepthMask(true); + + glGetIntegerv(GL_POLYGON_MODE, polygonmode); + + cache = psys->childcache; + + switch (part->draw_col) { + case PART_DRAW_COL_NONE: + draw_constcolor = true; + break; + case PART_DRAW_COL_MAT: + if (ma) + rgb_float_to_uchar(tcol, &(ma->r)); + else + tcol[0] = tcol[1] = tcol[2] = 1.0f; + break; + case PART_DRAW_COL_VEL: + tcol[0] = tcol[1] = tcol[2] = 1.0f; + break; + case PART_DRAW_COL_ACC: + tcol[0] = tcol[1] = tcol[2] = 1.0f; + break; + case PART_DRAW_COL_PARENT: + /* handled per child group */ + break; + default: + BLI_assert(0); /* should never happen */ + break; + } + + if (!draw_constcolor) { + glColor3ubv(tcol); + } + + /* draw child particles */ + { + int pstart; + float col[3]; + + glEnable(GL_LIGHTING); + glEnable(GL_COLOR_MATERIAL); + glColorMaterial(GL_FRONT_AND_BACK, GL_DIFFUSE); + glShadeModel(GL_SMOOTH); + + glBegin(GL_QUADS); + pstart = particle_path_next(cache, totchild, -1); + while (pstart < totchild) { + int p = pstart; + int np = particle_path_next(cache, totchild, p); + + if (part->draw_col == PART_DRAW_COL_PARENT) { + particle_path_color(pstart, col); + rgb_float_to_uchar(tcol, col); + glColor3ubv(tcol); + } + + while (np < totchild && cache[np]->hull_parent == cache[pstart]->hull_parent) { + draw_particle_hull_section(cache[p], cache[np]); + + p = np; + np = particle_path_next(cache, totchild, np); + } + if (p > pstart + 1) + draw_particle_hull_section(cache[p], cache[pstart]); + + pstart = np; + } + glEnd(); + + glBegin(GL_TRIANGLES); + pstart = particle_path_next(cache, totchild, -1); + while (pstart < totchild) { + int groupend; + + if (part->draw_col == PART_DRAW_COL_PARENT) { + particle_path_color(pstart, col); + rgb_float_to_uchar(tcol, col); + glColor3ubv(tcol); + } + + { + int p = particle_path_next(cache, totchild, pstart); + while (p < totchild && cache[p]->hull_parent == cache[pstart]->hull_parent) { + p = particle_path_next(cache, totchild, p); + } + groupend = p; + } + + { + int a[4], b[4]; + + #define NEXT \ + a[3] = a[2]; \ + b[3] = b[2]; \ + a[2] = a[1]; \ + b[2] = b[1]; \ + a[1] = a[0]; \ + b[1] = b[0]; \ + a[0] = particle_path_next(cache, groupend, a[0]); \ + b[0] = particle_path_prev(cache, pstart, b[0]); + + a[3] = pstart - 1; + b[3] = particle_path_prev(cache, pstart, groupend) + 1; + a[2] = particle_path_next(cache, groupend, a[3]); + b[2] = particle_path_prev(cache, pstart, b[3]); + a[1] = particle_path_next(cache, groupend, a[2]); + b[1] = particle_path_prev(cache, pstart, b[2]); + a[0] = particle_path_next(cache, groupend, a[1]); + b[0] = particle_path_prev(cache, pstart, b[1]); + + /* first element */ + if (a[1] <= b[1]) { + if (a[0] <= b[0]) + draw_particle_hull_cap(cache[a[0]], cache[a[1]], cache[a[2]], cache[a[2]], + cache[b[0]], cache[b[1]], cache[b[2]], cache[b[2]]); + else + draw_particle_hull_cap(cache[a[1]], cache[a[1]], cache[a[2]], cache[a[2]], + cache[b[1]], cache[b[1]], cache[b[2]], cache[b[2]]); + } + NEXT + + while (true) { + if (a[1] <= b[1]) { + if (a[0] <= b[0]) + draw_particle_hull_cap(cache[a[0]], cache[a[1]], cache[a[2]], cache[a[3]], + cache[b[0]], cache[b[1]], cache[b[2]], cache[b[3]]); + else + draw_particle_hull_cap(cache[a[1]], cache[a[1]], cache[a[2]], cache[a[3]], + cache[b[1]], cache[b[1]], cache[b[2]], cache[b[3]]); + } + else + break; + + NEXT + } + + #undef NEXT + } + + pstart = groupend; + } + glEnd(); + + glDisable(GL_COLOR_MATERIAL); + glDisable(GL_LIGHTING); + } + + glPolygonMode(GL_FRONT, polygonmode[0]); + glPolygonMode(GL_BACK, polygonmode[1]); + + if ((base->flag & OB_FROMDUPLI) && (ob->flag & OB_FROMGROUP)) { + glLoadMatrixf(rv3d->viewmat); + } +} +#endif + static void draw_particle_arrays(int draw_as, int totpoint, int ob_dt, int select) { /* draw created data arrays */ @@ -4819,6 +5199,7 @@ static void draw_particle_arrays(int draw_as, int totpoint, int ob_dt, int selec break; } } + static void draw_particle(ParticleKey *state, int draw_as, short draw, float pixsize, float imat[4][4], const float draw_line[2], ParticleBillboardData *bb, ParticleDrawData *pdd) { @@ -4969,6 +5350,7 @@ static void draw_particle(ParticleKey *state, int draw_as, short draw, float pix } } } + static void draw_particle_data(ParticleSystem *psys, RegionView3D *rv3d, ParticleKey *state, int draw_as, float imat[4][4], ParticleBillboardData *bb, ParticleDrawData *pdd, @@ -5032,7 +5414,7 @@ static void draw_new_particle_system(Scene *scene, View3D *v3d, RegionView3D *rv Material *ma; float vel[3], imat[4][4]; float timestep, pixsize_scale = 1.0f, pa_size, r_tilt, r_length; - float pa_time, pa_birthtime, pa_dietime, pa_health, intensity; + float pa_time, pa_birthtime, pa_dietime, pa_health; float cfra; float ma_col[3] = {0.0f, 0.0f, 0.0f}; int a, totpart, totpoint = 0, totve = 0, drawn, draw_as, totchild = 0; @@ -5051,14 +5433,17 @@ static void draw_new_particle_system(Scene *scene, View3D *v3d, RegionView3D *rv /* don't draw normal paths in edit mode */ if (psys_in_edit_mode(scene, psys) && (pset->flag & PE_DRAW_PART) == 0) return; - - if (part->draw_as == PART_DRAW_REND) - draw_as = part->ren_as; - else - draw_as = part->draw_as; - - if (draw_as == PART_DRAW_NOT) + + draw_as = part->draw_as == PART_DRAW_REND ? part->ren_as : part->draw_as; + if (draw_as == PART_DRAW_NOT) { + return; + } + else if (draw_as == PART_DRAW_HULL) { +#ifdef USE_PARTICLE_HULL_DRAWING + draw_particle_hair_hull(scene, v3d, rv3d, base, psys, ob_dt, dflag); +#endif return; + } /* prepare curvemapping tables */ if ((psys->part->child_flag & PART_CHILD_USE_CLUMP_CURVE) && psys->part->clumpcurve) @@ -5302,20 +5687,32 @@ static void draw_new_particle_system(Scene *scene, View3D *v3d, RegionView3D *rv r_length = psys_frand(psys, a + 22); if (part->draw_col > PART_DRAW_COL_MAT) { + float intensity; switch (part->draw_col) { case PART_DRAW_COL_VEL: intensity = len_v3(pa->state.vel) / part->color_vec_max; + CLAMP(intensity, 0.0f, 1.0f); + weight_to_rgb(ma_col, intensity); break; case PART_DRAW_COL_ACC: intensity = len_v3v3(pa->state.vel, pa->prev_state.vel) / ((pa->state.time - pa->prev_state.time) * part->color_vec_max); + CLAMP(intensity, 0.0f, 1.0f); + weight_to_rgb(ma_col, intensity); + break; + case PART_DRAW_COL_PARENT: + particle_path_color(a, ma_col); break; + case PART_DRAW_COL_TEX: { + ParticleTexture ptex; + psys_get_texture(&sim, pa, &ptex, PAMAP_COLOR, cfra); + copy_v3_v3(ma_col, ptex.color); + break; + } default: - intensity = 1.0f; /* should never happen */ + weight_to_rgb(ma_col, 1.0f); BLI_assert(0); break; } - CLAMP(intensity, 0.0f, 1.0f); - weight_to_rgb(ma_col, intensity); } } else { @@ -5623,8 +6020,24 @@ static void draw_new_particle_system(Scene *scene, View3D *v3d, RegionView3D *rv if (1) { //ob_dt > OB_WIRE) { glNormalPointer(GL_FLOAT, sizeof(ParticleCacheKey), path->vel); if ((dflag & DRAW_CONSTCOLOR) == 0) { - if (part->draw_col == PART_DRAW_COL_MAT) { - glColorPointer(3, GL_FLOAT, sizeof(ParticleCacheKey), path->col); + float col[3]; + + switch (part->draw_col) { + case PART_DRAW_COL_MAT: + glColorPointer(3, GL_FLOAT, sizeof(ParticleCacheKey), path->col); + break; + case PART_DRAW_COL_PARENT: + /* this switches colors to each new parent because new hull children come first */ + if (cache[a]->hull_parent >= 0) { + particle_path_color(cache[a]->hull_parent, col); + glColor3fv(col); + } + break; + case PART_DRAW_COL_TEX: + // TODO + break; + default: + break; } } } @@ -7499,6 +7912,72 @@ static void draw_object_wire_color(Scene *scene, Base *base, unsigned char r_ob_ r_ob_wire_col[3] = 255; } + +static float draw_object_wire_grey = -1.0f; +void draw_object_bg_wire_color_set(const float color[3]) +{ + draw_object_wire_grey = rgb_to_grayscale(color); +} + + +static void tint_neg(float rgb[3], float fac) +{ + mul_v3_fl(rgb, fac); +} + +static void tint_pos(float rgb[3], float fac) +{ + rgb[0] = 1.0 - rgb[0]; + rgb[1] = 1.0 - rgb[1]; + rgb[2] = 1.0 - rgb[2]; + + mul_v3_fl(rgb, fac); + + rgb[0] = 1.0 - rgb[0]; + rgb[1] = 1.0 - rgb[1]; + rgb[2] = 1.0 - rgb[2]; +} + +static void draw_object_wire_color_adjust_contrast( + unsigned char ob_wire_col[3], + /* 0 == normal, 1 == select, 2 == obact */ + const int select_state, + const short draw_type) +{ + float rgb[3]; + + BLI_assert(draw_object_wire_grey != -1.0); + + rgb_uchar_to_float(rgb, ob_wire_col); + + if (select_state == 0) { + tint_neg(rgb, 0.5f); + } + else { + tint_pos(rgb, select_state == 2 ? 0.15f : 0.35f); + } + + + /* when no solid --- ensure contrast */ + if (draw_type <= OB_WIRE) { + const float contrast = 0.1f; + + const float fill_bw = draw_object_wire_grey; + const float wire_bw = rgb_to_grayscale(rgb); + const float delta = wire_bw - fill_bw; + + if (fabsf(delta) < contrast) { + if (delta > 0.0f) { + add_v3_fl(rgb, (contrast - delta) / 2.0f); + } + else { + add_v3_fl(rgb, (contrast + delta) / -2.0f); + } + } + } + rgb_float_to_uchar(ob_wire_col, rgb); +} + static void draw_object_matcap_check(View3D *v3d, Object *ob) { /* fixed rule, active object draws as matcap */ @@ -7585,8 +8064,10 @@ void draw_object(Scene *scene, ARegion *ar, View3D *v3d, Base *base, const short bool zbufoff = false, is_paint = false, empty_object = false; const bool is_obact = (ob == OBACT); const bool render_override = (v3d->flag2 & V3D_RENDER_OVERRIDE) != 0; + const bool show_motionpaths = !(v3d->flag3 & V3D_HIDE_MOTIONPATHS) != 0; const bool is_picking = (G.f & G_PICKSEL) != 0; const bool has_particles = (ob->particlesystem.first != NULL); + const bool is_wire_color = V3D_IS_WIRECOLOR_OBJECT(scene, v3d, ob); bool skip_object = false; /* Draw particles but not their emitter object. */ SmokeModifierData *smd = NULL; @@ -7673,34 +8154,36 @@ void draw_object(Scene *scene, ARegion *ar, View3D *v3d, Base *base, const short view3d_cached_text_draw_begin(); /* draw motion paths (in view space) */ - if (ob->mpath && !render_override) { - bAnimVizSettings *avs = &ob->avs; - - /* setup drawing environment for paths */ - draw_motion_paths_init(v3d, ar); - - /* draw motion path for object */ - draw_motion_path_instance(scene, ob, NULL, avs, ob->mpath); - - /* cleanup after drawing */ - draw_motion_paths_cleanup(v3d); + if (ob->mpath && show_motionpaths && !(base->flag & OB_FROMDUPLI)) { + ED_view3d_after_add(&v3d->afterdraw_nodepth, base, 0); } - /* multiply view with object matrix. - * local viewmat and persmat, to calculate projections */ - ED_view3d_init_mats_rv3d_gl(ob, rv3d); - /* which wire color */ if ((dflag & DRAW_CONSTCOLOR) == 0) { ED_view3d_project_base(ar, base); - draw_object_wire_color(scene, base, _ob_wire_col); + if (is_wire_color) { + rgb_float_to_uchar(_ob_wire_col, ob->col); + _ob_wire_col[3] = 255; + + draw_object_wire_color_adjust_contrast( + _ob_wire_col, + (ob->flag & SELECT) ? (is_obact ? 2 : 1) : 0, + v3d->drawtype); + } + else { + draw_object_wire_color(scene, base, _ob_wire_col); + } ob_wire_col = _ob_wire_col; glColor3ubv(ob_wire_col); } + /* multiply view with object matrix. + * local viewmat and persmat, to calculate projections */ + ED_view3d_init_mats_rv3d_gl(ob, rv3d); + /* maximum drawtype */ dt = v3d->drawtype; if (dt == OB_RENDER) dt = OB_SOLID; @@ -7879,6 +8362,12 @@ void draw_object(Scene *scene, ARegion *ar, View3D *v3d, Base *base, const short GPU_disable_material(); } } + /* draw motion paths if forced */ + if (show_motionpaths && !(dflag & DRAW_SCENESET)) { + bArmature *arm = ob->data; + if (!arm->edbo && !(base->flag & OB_FROMDUPLI)) + ED_view3d_after_add(&v3d->afterdraw_nodepth, base, 0); + } break; default: if (!render_override) { @@ -7913,7 +8402,8 @@ void draw_object(Scene *scene, ARegion *ar, View3D *v3d, Base *base, const short /* code for new particle system */ if ((ob->particlesystem.first) && - (ob != scene->obedit)) + (ob != scene->obedit) && + !(ob->transflag & OB_IS_DUPLI_CACHE)) { ParticleSystem *psys; @@ -7930,14 +8420,16 @@ void draw_object(Scene *scene, ARegion *ar, View3D *v3d, Base *base, const short view3d_cached_text_draw_begin(); for (psys = ob->particlesystem.first; psys; psys = psys->next) { - /* run this so that possible child particles get cached */ - if (ob->mode & OB_MODE_PARTICLE_EDIT && is_obact) { - PTCacheEdit *edit = PE_create_current(scene, ob); - if (edit && edit->psys == psys) - draw_update_ptcache_edit(scene, ob, edit); + if (!(ob->mode & OB_MODE_HAIR_EDIT)) { + /* run this so that possible child particles get cached */ + if (ob->mode & OB_MODE_PARTICLE_EDIT && is_obact) { + PTCacheEdit *edit = PE_create_current(scene, ob); + if (edit && edit->psys == psys) + draw_update_ptcache_edit(scene, ob, edit); + } + + draw_new_particle_system(scene, v3d, rv3d, base, psys, dt, dflag); } - - draw_new_particle_system(scene, v3d, rv3d, base, psys, dt, dflag); } invert_m4_m4(ob->imat, ob->obmat); view3d_cached_text_draw_end(v3d, ar, 0, NULL); @@ -7962,6 +8454,13 @@ void draw_object(Scene *scene, ARegion *ar, View3D *v3d, Base *base, const short glMultMatrixf(ob->obmat); } } + + if (ob->mode & OB_MODE_HAIR_EDIT && is_obact) { + BMEditStrands *edit = BKE_editstrands_from_object(ob); + if (edit) { + draw_strands_edit_hair(scene, v3d, ar, edit); + } + } } /* draw code for smoke */ @@ -8190,7 +8689,7 @@ void draw_object(Scene *scene, ARegion *ar, View3D *v3d, Base *base, const short } /* object centers, need to be drawn in viewmat space for speed, but OK for picking select */ - if (!is_obact || !(ob->mode & OB_MODE_ALL_PAINT)) { + if (!is_obact || !(ob->mode & OB_MODE_ALL_BRUSH)) { int do_draw_center = -1; /* defines below are zero or positive... */ if (render_override) { @@ -8653,6 +9152,48 @@ static void draw_object_mesh_instance(Scene *scene, View3D *v3d, RegionView3D *r if (dm) dm->release(dm); } +void ED_draw_object_facemap(Scene *scene, struct Object *ob, int facemap) +{ + DerivedMesh *dm = NULL; + + dm = mesh_get_derived_final(scene, ob, CD_MASK_BAREMESH); + if (!dm || !CustomData_has_layer(&dm->polyData, CD_FACEMAP)) + return; + + DM_update_materials(dm, ob); + + glFrontFace((ob->transflag & OB_NEG_SCALE) ? GL_CW : GL_CCW); + + /* add polygon offset so we draw above the original surface */ + glPolygonOffset(1.0, 1.0); + + dm->totfmaps = BLI_listbase_count(&ob->fmaps); + + GPU_facemap_setup(dm); + + glColor4f(0.7, 1.0, 1.0, 0.5); + + glPushAttrib(GL_ENABLE_BIT); + glEnable(GL_BLEND); + glDisable(GL_LIGHTING); + + if (dm->drawObject->facemapindices) { + if (dm->drawObject->facemapindices->use_vbo) + glDrawElements(GL_TRIANGLES, dm->drawObject->facemap_count[facemap], GL_UNSIGNED_INT, + (int *)NULL + dm->drawObject->facemap_start[facemap]); + else + glDrawElements(GL_TRIANGLES, dm->drawObject->facemap_count[facemap], GL_UNSIGNED_INT, + (int *)dm->drawObject->facemapindices->pointer + dm->drawObject->facemap_start[facemap]); + } + glPopAttrib(); + + GPU_buffer_unbind(); + + glPolygonOffset(0.0, 0.0); + dm->release(dm); +} + + void draw_object_instance(Scene *scene, View3D *v3d, RegionView3D *rv3d, Object *ob, const char dt, int outline) { if (ob == NULL) diff --git a/source/blender/editors/space_view3d/drawstrands.c b/source/blender/editors/space_view3d/drawstrands.c new file mode 100644 index 00000000000..af2e04ee3a7 --- /dev/null +++ b/source/blender/editors/space_view3d/drawstrands.c @@ -0,0 +1,520 @@ +/* + * ***** 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2014 by the Blender Foundation. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): Lukas Toenne + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/editors/space_view3d/drawstrands.c + * \ingroup spview3d + */ + +#include "MEM_guardedalloc.h" + +#include "BLI_blenlib.h" +#include "BLI_utildefines.h" +#include "BLI_math.h" + +#include "DNA_object_types.h" +#include "DNA_scene_types.h" +#include "DNA_screen_types.h" +#include "DNA_view3d_types.h" + +#include "BKE_editstrands.h" +#include "BKE_global.h" +#include "BKE_main.h" +#include "BKE_strands.h" + +#include "bmesh.h" + +#include "ED_screen.h" +#include "ED_types.h" + +#include "UI_resources.h" +#include "UI_interface_icons.h" + +#include "BIF_gl.h" +#include "BIF_glutil.h" + +#include "GPU_draw.h" +#include "GPU_extensions.h" +#include "GPU_select.h" + +#include "view3d_intern.h" + +typedef struct StrandsDrawGLState { + GLint polygonmode[2]; +} StrandsDrawGLState; + +typedef enum StrandsShadeMode { + STRANDS_SHADE_FLAT, + STRANDS_SHADE_HAIR, +} StrandsShadeMode; + +static void draw_strands_begin(StrandsDrawGLState *state, short dflag) +{ + glGetIntegerv(GL_POLYGON_MODE, state->polygonmode); + glEnableClientState(GL_VERTEX_ARRAY); + + /* setup gl flags */ + glEnableClientState(GL_NORMAL_ARRAY); + + if ((dflag & DRAW_CONSTCOLOR) == 0) { +// if (part->draw_col == PART_DRAW_COL_MAT) +// glEnableClientState(GL_COLOR_ARRAY); + } + + glColor3f(1,1,1); + glEnable(GL_LIGHTING); +// glColorMaterial(GL_FRONT_AND_BACK, GL_DIFFUSE); +// glEnable(GL_COLOR_MATERIAL); +} + +static void draw_strands_end(StrandsDrawGLState *state) +{ + /* restore & clean up */ +// if (part->draw_col == PART_DRAW_COL_MAT) +// glDisableClientState(GL_COLOR_ARRAY); + glDisable(GL_LIGHTING); + glDisable(GL_COLOR_MATERIAL); + + glLineWidth(1.0f); + + glDisableClientState(GL_VERTEX_ARRAY); + glDisableClientState(GL_NORMAL_ARRAY); + + glPolygonMode(GL_FRONT, state->polygonmode[0]); + glPolygonMode(GL_BACK, state->polygonmode[1]); +} + +static void draw_strand_lines(Strands *strands, short dflag) +{ + const bool has_motion_state = strands->state; + StrandsDrawGLState gl_state; + StrandIterator it_strand; + + draw_strands_begin(&gl_state, dflag); + + for (BKE_strand_iter_init(&it_strand, strands); BKE_strand_iter_valid(&it_strand); BKE_strand_iter_next(&it_strand)) { + if (it_strand.tot <= 0) + continue; + + if (has_motion_state) { + glVertexPointer(3, GL_FLOAT, sizeof(StrandsMotionState), it_strand.state->co); + glNormalPointer(GL_FLOAT, sizeof(StrandsMotionState), it_strand.state->nor); + } + else { + glVertexPointer(3, GL_FLOAT, sizeof(StrandsVertex), it_strand.verts->co); + glNormalPointer(GL_FLOAT, sizeof(StrandsVertex), it_strand.verts->nor); + } + if ((dflag & DRAW_CONSTCOLOR) == 0) { +// if (part->draw_col == PART_DRAW_COL_MAT) { +// glColorPointer(3, GL_FLOAT, sizeof(ParticleCacheKey), path->col); +// } + } + + glDrawArrays(GL_LINE_STRIP, 0, it_strand.curve->numverts); + } + + draw_strands_end(&gl_state); +} + +static void draw_strand_child_lines(StrandsChildren *children, short dflag) +{ + StrandsDrawGLState gl_state; + StrandChildIterator it_strand; + + draw_strands_begin(&gl_state, dflag); + + for (BKE_strand_child_iter_init(&it_strand, children); BKE_strand_child_iter_valid(&it_strand); BKE_strand_child_iter_next(&it_strand)) { + StrandsChildCurve *curve = it_strand.curve; + const int numverts = curve->cutoff < 0.0f ? curve->numverts : min_ii(curve->numverts, (int)ceilf(curve->cutoff) + 1); + + if (it_strand.tot <= 0) + continue; + + glVertexPointer(3, GL_FLOAT, sizeof(StrandsChildVertex), it_strand.verts->co); + glNormalPointer(GL_FLOAT, sizeof(StrandsChildVertex), it_strand.verts->nor); + + if ((dflag & DRAW_CONSTCOLOR) == 0) { +// if (part->draw_col == PART_DRAW_COL_MAT) { +// glColorPointer(3, GL_FLOAT, sizeof(ParticleCacheKey), path->col); +// } + } + + + glDrawArrays(GL_LINE_STRIP, 0, numverts); + } + + draw_strands_end(&gl_state); +} + +void draw_strands(Scene *UNUSED(scene), View3D *UNUSED(v3d), ARegion *ar, Object *ob, Strands *strands, StrandsChildren *children, short dflag) +{ + RegionView3D *rv3d = ar->regiondata; + float imat[4][4]; + + invert_m4_m4(imat, rv3d->viewmatob); + +// glDepthMask(GL_FALSE); +// glEnable(GL_BLEND); + + glPushMatrix(); + + glLoadMatrixf(rv3d->viewmat); + glMultMatrixf(ob->obmat); + + if (children) + draw_strand_child_lines(children, dflag); + else + draw_strand_lines(strands, dflag); + + glPopMatrix(); + +// glDepthMask(GL_TRUE); +// glDisable(GL_BLEND); +} + +/* ------------------------------------------------------------------------- */ + +typedef struct StrandsDrawInfo { + bool has_zbuf; + bool use_zbuf_select; + + StrandsShadeMode shade_mode; + int select_mode; + + float col_base[4]; + float col_select[4]; +} StrandsDrawInfo; + +BLI_INLINE bool strands_use_normals(const StrandsDrawInfo *info) +{ + return ELEM(info->shade_mode, STRANDS_SHADE_HAIR); +} + +static void init_draw_info(StrandsDrawInfo *info, View3D *v3d, + StrandsShadeMode shade_mode, int select_mode) +{ + info->has_zbuf = v3d->zbuf; + info->use_zbuf_select = (v3d->flag & V3D_ZBUF_SELECT); + + info->shade_mode = shade_mode; + info->select_mode = select_mode; + + /* get selection theme colors */ + UI_GetThemeColor4fv(TH_VERTEX, info->col_base); + UI_GetThemeColor4fv(TH_VERTEX_SELECT, info->col_select); +} + +static void set_opengl_state_strands(const StrandsDrawInfo *info) +{ + if (!info->use_zbuf_select) + glDisable(GL_DEPTH_TEST); + glEnable(GL_BLEND); + + if (ELEM(info->shade_mode, STRANDS_SHADE_HAIR)) { + glEnable(GL_LIGHTING); + glColorMaterial(GL_FRONT_AND_BACK, GL_DIFFUSE); + glEnable(GL_COLOR_MATERIAL); + glShadeModel(GL_SMOOTH); + } + else { + glDisable(GL_LIGHTING); + } + + glEnableClientState(GL_VERTEX_ARRAY); + if (strands_use_normals(info)) + glEnableClientState(GL_NORMAL_ARRAY); +} + +static void set_opengl_state_dots(const StrandsDrawInfo *info) +{ + if (!info->use_zbuf_select) + glDisable(GL_DEPTH_TEST); + glEnable(GL_BLEND); + + glDisable(GL_LIGHTING); + + glEnableClientState(GL_VERTEX_ARRAY); + glPointSize(3.0); +} + +static void restore_opengl_state(const StrandsDrawInfo *info) +{ + glDisableClientState(GL_NORMAL_ARRAY); + glDisableClientState(GL_VERTEX_ARRAY); + + glDisable(GL_BLEND); + glDisable(GL_LIGHTING); + glDisable(GL_COLOR_MATERIAL); + glShadeModel(GL_FLAT); + if (info->has_zbuf) + glEnable(GL_DEPTH_TEST); + glLineWidth(1.0f); + glPointSize(1.0); +} + +/* ------------------------------------------------------------------------- */ +/* strands */ + +static void setup_gpu_buffers_strands(BMEditStrands *edit, const StrandsDrawInfo *info) +{ + const size_t size_v3 = sizeof(float) * 3; + const size_t size_vertex = (strands_use_normals(info) ? 2*size_v3 : size_v3); + +// int totstrands = BM_strands_count(edit->bm); + int totvert = edit->bm->totvert; + int totedge = edit->bm->totedge; + + if (!edit->vertex_glbuf) + glGenBuffers(1, &edit->vertex_glbuf); + if (!edit->elem_glbuf) + glGenBuffers(1, &edit->elem_glbuf); + + glBindBuffer(GL_ARRAY_BUFFER, edit->vertex_glbuf); + glBufferData(GL_ARRAY_BUFFER, size_vertex * totvert, NULL, GL_DYNAMIC_DRAW); + + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, edit->elem_glbuf); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(unsigned int) * totedge * 2, NULL, GL_DYNAMIC_DRAW); + + glVertexPointer(3, GL_FLOAT, size_vertex, NULL); + if (strands_use_normals(info)) + glNormalPointer(GL_FLOAT, size_vertex, (GLubyte *)NULL + size_v3); +} + +static void unbind_gpu_buffers_strands(void) +{ + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); +} + +static int write_gpu_buffers_strands(BMEditStrands *edit, const StrandsDrawInfo *info) +{ + const size_t size_v3 = sizeof(float) * 3; + const size_t size_vertex = (strands_use_normals(info) ? 2*size_v3 : size_v3); + + GLubyte *vertex_data; + unsigned int *elem_data; + BMVert *root, *v, *vprev; + BMIter iter, iter_strand; + int index, indexprev, index_edge; + int k; + + vertex_data = glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY); + elem_data = glMapBuffer(GL_ELEMENT_ARRAY_BUFFER, GL_WRITE_ONLY); + if (!vertex_data || !elem_data) + return 0; + + BM_mesh_elem_index_ensure(edit->bm, BM_VERT); + + index_edge = 0; + BM_ITER_STRANDS(root, &iter, edit->bm, BM_STRANDS_OF_MESH) { + BM_ITER_STRANDS_ELEM_INDEX(v, &iter_strand, root, BM_VERTS_OF_STRAND, k) { + size_t offset_co; + + index = BM_elem_index_get(v); + + offset_co = index * size_vertex; + copy_v3_v3((float *)(vertex_data + offset_co), v->co); + + if (k > 0) { + if (strands_use_normals(info)) { + size_t offset_nor = offset_co + size_v3; + float nor[3]; + sub_v3_v3v3(nor, v->co, vprev->co); + normalize_v3(nor); + copy_v3_v3((float *)(vertex_data + offset_nor), nor); + + if (k == 1) { + /* define root normal: same as first segment */ + size_t offset_root_nor = indexprev * size_vertex + size_v3; + copy_v3_v3((float *)(vertex_data + offset_root_nor), nor); + } + } + + { + elem_data[index_edge + 0] = indexprev; + elem_data[index_edge + 1] = index; + index_edge += 2; + } + } + + vprev = v; + indexprev = index; + } + } + + glUnmapBuffer(GL_ARRAY_BUFFER); + glUnmapBuffer(GL_ELEMENT_ARRAY_BUFFER); + + return index_edge; +} + +/* ------------------------------------------------------------------------- */ +/* dots */ + +static void setup_gpu_buffers_dots(BMEditStrands *edit, const StrandsDrawInfo *info, bool selected) +{ + const size_t size_v3 = sizeof(float) * 3; + const size_t size_vertex = size_v3; + BMesh *bm = edit->bm; + + BMVert *v; + BMIter iter; + int totvert; + + switch (info->select_mode) { + case HAIR_SELECT_STRAND: + totvert = 0; + break; + case HAIR_SELECT_VERTEX: + totvert = selected ? bm->totvertsel : bm->totvert - bm->totvertsel; + break; + case HAIR_SELECT_TIP: + totvert = 0; + BM_ITER_MESH(v, &iter, bm, BM_VERTS_OF_MESH) { + if (BM_elem_flag_test_bool(v, BM_ELEM_SELECT) != selected) + continue; + if (!BM_strands_vert_is_tip(v)) + continue; + + ++totvert; + } + break; + } + + if (totvert == 0) + return; + + if (!edit->dot_glbuf) + glGenBuffers(1, &edit->dot_glbuf); + + glBindBuffer(GL_ARRAY_BUFFER, edit->dot_glbuf); + glBufferData(GL_ARRAY_BUFFER, size_vertex * totvert, NULL, GL_DYNAMIC_DRAW); + + glVertexPointer(3, GL_FLOAT, size_vertex, NULL); +} + +static void unbind_gpu_buffers_dots(void) +{ + glBindBuffer(GL_ARRAY_BUFFER, 0); +} + +static int write_gpu_buffers_dots(BMEditStrands *edit, const StrandsDrawInfo *info, bool selected) +{ + const size_t size_v3 = sizeof(float) * 3; + const size_t size_vertex = size_v3; + + GLubyte *vertex_data; + BMVert *v; + BMIter iter; + int index_dot; + + if (info->select_mode == HAIR_SELECT_STRAND) + return 0; + + vertex_data = glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY); + if (!vertex_data) + return 0; + + BM_mesh_elem_index_ensure(edit->bm, BM_VERT); + + index_dot = 0; + switch (info->select_mode) { + case HAIR_SELECT_STRAND: + /* already exited, but keep the case for the compiler */ + break; + case HAIR_SELECT_VERTEX: + BM_ITER_MESH(v, &iter, edit->bm, BM_VERTS_OF_MESH) { + size_t offset_co; + + if (BM_elem_flag_test_bool(v, BM_ELEM_SELECT) != selected) + continue; + + offset_co = index_dot * size_vertex; + copy_v3_v3((float *)(vertex_data + offset_co), v->co); + ++index_dot; + } + break; + case HAIR_SELECT_TIP: + BM_ITER_MESH(v, &iter, edit->bm, BM_VERTS_OF_MESH) { + size_t offset_co; + + if (BM_elem_flag_test_bool(v, BM_ELEM_SELECT) != selected) + continue; + if (!BM_strands_vert_is_tip(v)) + continue; + + offset_co = index_dot * size_vertex; + copy_v3_v3((float *)(vertex_data + offset_co), v->co); + ++index_dot; + } + break; + } + + glUnmapBuffer(GL_ARRAY_BUFFER); + + return index_dot; +} + +/* ------------------------------------------------------------------------- */ + +static void draw_dots(BMEditStrands *edit, const StrandsDrawInfo *info, bool selected) +{ + int totelem; + + if (selected) + glColor3fv(info->col_select); + else + glColor3fv(info->col_base); + + setup_gpu_buffers_dots(edit, info, selected); + totelem = write_gpu_buffers_dots(edit, info, selected); + if (totelem > 0) + glDrawArrays(GL_POINTS, 0, totelem); +} + +void draw_strands_edit_hair(Scene *scene, View3D *v3d, ARegion *UNUSED(ar), BMEditStrands *edit) +{ + HairEditSettings *settings = &scene->toolsettings->hair_edit; + + StrandsDrawInfo info; + int totelem; + + init_draw_info(&info, v3d, STRANDS_SHADE_HAIR, settings->select_mode); + + set_opengl_state_strands(&info); + setup_gpu_buffers_strands(edit, &info); + totelem = write_gpu_buffers_strands(edit, &info); + if (totelem > 0) + glDrawElements(GL_LINES, totelem, GL_UNSIGNED_INT, NULL); + unbind_gpu_buffers_strands(); + + set_opengl_state_dots(&info); + draw_dots(edit, &info, false); + draw_dots(edit, &info, true); + unbind_gpu_buffers_dots(); + + restore_opengl_state(&info); +} diff --git a/source/blender/editors/space_view3d/drawvolume.c b/source/blender/editors/space_view3d/drawvolume.c index d6691f431dd..2ee1241a2a4 100644 --- a/source/blender/editors/space_view3d/drawvolume.c +++ b/source/blender/editors/space_view3d/drawvolume.c @@ -106,6 +106,7 @@ void draw_smoke_volume(SmokeDomainSettings *sds, Object *ob, int gl_depth = 0, gl_blend = 0; const bool use_fire = (sds->active_fields & SM_ACTIVE_FIRE) != 0; + const float thickness = sds->display_thickness; /* draw slices of smoke is adapted from c++ code authored * by: Johannes Schmid and Ingemar Rask, 2006, johnny@grob.org */ @@ -288,9 +289,9 @@ void draw_smoke_volume(SmokeDomainSettings *sds, Object *ob, GPU_program_parameter_4f(smoke_program, 0, dx, dx, dx, 1.0); /* custom parameter for smoke style (higher = thicker) */ if (sds->active_fields & SM_ACTIVE_COLORS) - GPU_program_parameter_4f(smoke_program, 1, 1.0, 1.0, 1.0, 10.0); + GPU_program_parameter_4f(smoke_program, 1, 1.0, 1.0, 1.0, 10.0 * thickness); else - GPU_program_parameter_4f(smoke_program, 1, sds->active_color[0], sds->active_color[1], sds->active_color[2], 10.0); + GPU_program_parameter_4f(smoke_program, 1, sds->active_color[0], sds->active_color[1], sds->active_color[2], 10.0 * thickness); } else printf("Your gfx card does not support 3D View smoke drawing.\n"); diff --git a/source/blender/editors/space_view3d/space_view3d.c b/source/blender/editors/space_view3d/space_view3d.c index 096d9e8a40a..e4f3e1d75ea 100644 --- a/source/blender/editors/space_view3d/space_view3d.c +++ b/source/blender/editors/space_view3d/space_view3d.c @@ -32,9 +32,13 @@ #include <string.h> #include <stdio.h> +#include "DNA_armature_types.h" #include "DNA_material_types.h" +#include "DNA_modifier_types.h" #include "DNA_object_types.h" #include "DNA_scene_types.h" +#include "DNA_camera_types.h" +#include "DNA_key_types.h" #include "MEM_guardedalloc.h" @@ -42,17 +46,22 @@ #include "BLI_math.h" #include "BLI_utildefines.h" +#include "BKE_action.h" #include "BKE_context.h" #include "BKE_depsgraph.h" #include "BKE_icons.h" +#include "BKE_key.h" #include "BKE_library.h" #include "BKE_main.h" +#include "BKE_modifier.h" #include "BKE_object.h" #include "BKE_scene.h" #include "BKE_screen.h" #include "ED_space_api.h" #include "ED_screen.h" +#include "ED_transform.h" +#include "ED_view3d.h" #include "GPU_extensions.h" #include "GPU_material.h" @@ -69,6 +78,7 @@ #include "RNA_access.h" #include "UI_resources.h" +#include "UI_interface.h" #ifdef WITH_PYTHON # include "BPY_extern.h" @@ -391,12 +401,13 @@ static SpaceLink *view3d_new(const bContext *C) ar->regiontype = RGN_TYPE_WINDOW; ar->regiondata = MEM_callocN(sizeof(RegionView3D), "region view3d"); + rv3d = ar->regiondata; rv3d->viewquat[0] = 1.0f; rv3d->persp = RV3D_PERSP; rv3d->view = RV3D_VIEW_PERSPORTHO; rv3d->dist = 10.0; - + return (SpaceLink *)v3d; } @@ -437,8 +448,7 @@ static void view3d_free(SpaceLink *sl) /* spacetype; init callback */ static void view3d_init(wmWindowManager *UNUSED(wm), ScrArea *UNUSED(sa)) -{ - +{ } static SpaceLink *view3d_duplicate(SpaceLink *sl) @@ -473,6 +483,7 @@ static SpaceLink *view3d_duplicate(SpaceLink *sl) } v3dn->properties_storage = NULL; + if (v3dn->fx_settings.dof) v3dn->fx_settings.dof = MEM_dupallocN(v3do->fx_settings.dof); if (v3dn->fx_settings.ssao) @@ -487,6 +498,12 @@ static void view3d_main_area_init(wmWindowManager *wm, ARegion *ar) ListBase *lb; wmKeyMap *keymap; + if (BLI_listbase_is_empty(&ar->widgetmaps)) { + BLI_addhead(&ar->widgetmaps, WM_widgetmap_from_type("View3D", SPACE_VIEW3D, RGN_TYPE_WINDOW, true)); + } + + WM_event_add_area_widgetmap_handlers(ar); + /* object ops. */ /* important to be before Pose keymap since they can both be enabled at once */ @@ -543,6 +560,9 @@ static void view3d_main_area_init(wmWindowManager *wm, ARegion *ar) keymap = WM_keymap_find(wm->defaultconf, "Particle", 0, 0); WM_event_add_keymap_handler(&ar->handlers, keymap); + keymap = WM_keymap_find(wm->defaultconf, "Hair", 0, 0); + WM_event_add_keymap_handler(&ar->handlers, keymap); + /* editfont keymap swallows all... */ keymap = WM_keymap_find(wm->defaultconf, "Font", 0, 0); WM_event_add_keymap_handler(&ar->handlers, keymap); @@ -564,7 +584,6 @@ static void view3d_main_area_init(wmWindowManager *wm, ARegion *ar) lb = WM_dropboxmap_find("View3D", SPACE_VIEW3D, RGN_TYPE_WINDOW); WM_event_add_dropbox_handler(&ar->handlers, lb); - } static void view3d_main_area_exit(wmWindowManager *wm, ARegion *ar) @@ -713,6 +732,162 @@ static void view3d_dropboxes(void) } +static int WIDGETGROUP_camera_poll(const struct bContext *C, struct wmWidgetGroupType *UNUSED(wgrouptype)) +{ + Object *ob = CTX_data_active_object(C); + + if (ob && ob->type == OB_CAMERA) { + Camera *ca = ob->data; + return (ca->flag & CAM_SHOWLIMITS) != 0; + } + return false; +} + +static void WIDGETGROUP_camera_draw(const struct bContext *C, struct wmWidgetGroup *wgroup) +{ + float color_camera[4] = {1.0f, 0.3f, 0.0f, 1.0f}; + Object *ob = CTX_data_active_object(C); + Camera *ca = ob->data; + wmWidget *widget; + PointerRNA cameraptr; + float dir[3]; + + widget = WIDGET_arrow_new(wgroup, WIDGET_ARROW_STYLE_CROSS); + WM_widget_set_draw_on_hover_only(widget, true); + WM_widget_set_3d_scale(widget, false); + WIDGET_arrow_set_color(widget, color_camera); + + RNA_pointer_create(&ca->id, &RNA_Camera, ca, &cameraptr); + WM_widget_set_origin(widget, ob->obmat[3]); + WM_widget_property(widget, ARROW_SLOT_OFFSET_WORLD_SPACE, &cameraptr, "dof_distance"); + negate_v3_v3(dir, ob->obmat[2]); + WIDGET_arrow_set_direction(widget, dir); + WIDGET_arrow_set_up_vector(widget, ob->obmat[1]); + WM_widget_set_scale(widget, ca->drawsize); +} + +#if 0 +static int WIDGETGROUP_shapekey_poll(const struct bContext *C, struct wmWidgetGroupType *UNUSED(wgrouptype)) +{ + Object *ob = CTX_data_active_object(C); + + if (ob && ob->type == OB_MESH) { + Key *key = BKE_key_from_object(ob); + KeyBlock *kb; + + if (key == NULL) + return false; + + kb = BLI_findlink(&key->block, ob->shapenr - 1); + + if (kb) + return true; + } + return false; +} + +static void WIDGETGROUP_shapekey_draw(const struct bContext *C, struct wmWidgetGroup *wgroup) +{ + float color_shape[4] = {1.0f, 0.3f, 0.0f, 1.0f}; + Object *ob = CTX_data_active_object(C); + Key *key = BKE_key_from_object(ob); + KeyBlock *kb = BLI_findlink(&key->block, ob->shapenr - 1); + wmWidget *widget; + PointerRNA shapeptr; + float dir[3]; + + widget = WIDGET_arrow_new(wgroup, WIDGET_ARROW_STYLE_NORMAL); + WM_widget_set_3d_scale(widget, false); + WIDGET_arrow_set_color(widget, color_shape); + RNA_pointer_create(&key->id, &RNA_ShapeKey, kb, &shapeptr); + WM_widget_set_origin(widget, ob->obmat[3]); + WM_widget_property(widget, ARROW_SLOT_OFFSET_WORLD_SPACE, &shapeptr, "value"); + negate_v3_v3(dir, ob->obmat[2]); + WIDGET_arrow_set_direction(widget, dir); +} +#endif + +static int WIDGETGROUP_armature_facemap_poll(const struct bContext *C, struct wmWidgetGroupType *UNUSED(wgrouptype)) +{ + Object *ob = CTX_data_active_object(C); + + if (ob && ob->type == OB_MESH && ob->fmaps.first) { + ModifierData *md; + VirtualModifierData virtualModifierData; + + md = modifiers_getVirtualModifierList(ob, &virtualModifierData); + + /* exception for shape keys because we can edit those */ + for (; md; md = md->next) { + if (modifier_isEnabled(CTX_data_scene(C), md, eModifierMode_Realtime) && md->type == eModifierType_Armature) { + ArmatureModifierData *amd = (ArmatureModifierData *) md; + if (amd->object && (amd->deformflag & ARM_DEF_FACEMAPS)) + return true; + } + } + } + return false; +} + +static void WIDGETGROUP_armature_facemap_draw(const struct bContext *C, struct wmWidgetGroup *wgroup) +{ + float color_shape[4] = {1.0f, 0.3f, 0.0f, 1.0f}; + Object *ob = CTX_data_active_object(C); + wmWidget *widget; + Object *armature; + PointerRNA famapptr; + PropertyRNA *prop; + ModifierData *md; + VirtualModifierData virtualModifierData; + int index = 0; + bFaceMap *fmap = ob->fmaps.first; + + md = modifiers_getVirtualModifierList(ob, &virtualModifierData); + + /* exception for shape keys because we can edit those */ + for (; md; md = md->next) { + if (modifier_isEnabled(CTX_data_scene(C), md, eModifierMode_Realtime) && md->type == eModifierType_Armature) { + ArmatureModifierData *amd = (ArmatureModifierData *) md; + if (amd->object && (amd->deformflag & ARM_DEF_FACEMAPS)) { + armature = amd->object; + break; + } + } + } + + + for (; fmap; fmap = fmap->next, index++) { + if (BKE_pose_channel_find_name(armature->pose, fmap->name)) { + PointerRNA *opptr; + widget = WIDGET_facemap_new(wgroup, 0, ob, index); + RNA_pointer_create(&ob->id, &RNA_FaceMap, fmap, &famapptr); + WM_widget_property(widget, FACEMAP_SLOT_FACEMAP, &famapptr, "name"); + opptr = WM_widget_operator(widget, "TRANSFORM_OT_translate"); + if ((prop = RNA_struct_find_property(opptr, "release_confirm"))) { + RNA_property_boolean_set(opptr, prop, true); + } + WIDGET_facemap_set_color(widget, color_shape); + WM_widget_set_draw_on_hover_only(widget, true); + } + } +} + + +static void view3d_widgets(void) +{ + WM_widgetmaptype_find("View3D", SPACE_VIEW3D, RGN_TYPE_WINDOW, true, true); + + WM_widgetgrouptype_new(WIDGETGROUP_lamp_poll, WIDGETGROUP_lamp_draw, NULL, "View3D", SPACE_VIEW3D, RGN_TYPE_WINDOW, true); + WM_widgetgrouptype_new(WIDGETGROUP_camera_poll, WIDGETGROUP_camera_draw, NULL, "View3D", SPACE_VIEW3D, RGN_TYPE_WINDOW, true); + WM_widgetgrouptype_new(WIDGETGROUP_armature_facemap_poll, WIDGETGROUP_armature_facemap_draw, NULL, "View3D", SPACE_VIEW3D, RGN_TYPE_WINDOW, true); + +#if 0 + wgroup_manipulator = WM_widgetgrouptype_new( + WIDGETGROUP_manipulator_poll, + WIDGETGROUP_manipulator_update); +#endif +} + /* type callback, not region itself */ static void view3d_main_area_free(ARegion *ar) @@ -1416,6 +1591,7 @@ void ED_spacetype_view3d(void) st->operatortypes = view3d_operatortypes; st->keymap = view3d_keymap; st->dropboxes = view3d_dropboxes; + st->widgets = view3d_widgets; st->context = view3d_context; /* regions: main window */ diff --git a/source/blender/editors/space_view3d/view3d_draw.c b/source/blender/editors/space_view3d/view3d_draw.c index fb0f437884a..5ccb16e514c 100644 --- a/source/blender/editors/space_view3d/view3d_draw.c +++ b/source/blender/editors/space_view3d/view3d_draw.c @@ -1947,16 +1947,19 @@ typedef struct View3DAfter { struct View3DAfter *next, *prev; struct Base *base; short dflag; + float obmat[4][4]; } View3DAfter; /* temp storage of Objects that need to be drawn as last */ void ED_view3d_after_add(ListBase *lb, Base *base, const short dflag) { View3DAfter *v3da = MEM_callocN(sizeof(View3DAfter), "View 3d after"); - BLI_assert((base->flag & OB_FROMDUPLI) == 0); BLI_addtail(lb, v3da); v3da->base = base; v3da->dflag = dflag; + if (base->flag & OB_FROMDUPLI) { + copy_m4_m4(v3da->obmat, base->object->obmat); + } } /* disables write in zbuffer and draws it over */ @@ -1968,8 +1971,17 @@ static void view3d_draw_transp(Scene *scene, ARegion *ar, View3D *v3d) v3d->transp = true; for (v3da = v3d->afterdraw_transp.first; v3da; v3da = next) { + float obmat[4][4]; next = v3da->next; + if (v3da->base->flag & OB_FROMDUPLI) { + copy_m4_m4(obmat, v3da->base->object->obmat); + copy_m4_m4(v3da->base->object->obmat, v3da->obmat); + } draw_object(scene, ar, v3d, v3da->base, v3da->dflag); + if (v3da->base->flag & OB_FROMDUPLI) { + copy_m4_m4(v3da->base->object->obmat, obmat); + MEM_freeN(v3da->base); + } BLI_remlink(&v3d->afterdraw_transp, v3da); MEM_freeN(v3da); } @@ -2004,6 +2016,7 @@ static void view3d_draw_xray(Scene *scene, ARegion *ar, View3D *v3d, bool *clear static void view3d_draw_xraytransp(Scene *scene, ARegion *ar, View3D *v3d, const bool clear) { View3DAfter *v3da, *next; + float obmat[4][4]; if (clear && v3d->zbuf) glClear(GL_DEPTH_BUFFER_BIT); @@ -2015,7 +2028,15 @@ static void view3d_draw_xraytransp(Scene *scene, ARegion *ar, View3D *v3d, const for (v3da = v3d->afterdraw_xraytransp.first; v3da; v3da = next) { next = v3da->next; + if (v3da->base->flag & OB_FROMDUPLI) { + copy_m4_m4(obmat, v3da->base->object->obmat); + copy_m4_m4(v3da->base->object->obmat, v3da->obmat); + } draw_object(scene, ar, v3d, v3da->base, v3da->dflag); + if (v3da->base->flag & OB_FROMDUPLI) { + copy_m4_m4(v3da->base->object->obmat, obmat); + MEM_freeN(v3da->base); + } BLI_remlink(&v3d->afterdraw_xraytransp, v3da); MEM_freeN(v3da); } @@ -2026,6 +2047,46 @@ static void view3d_draw_xraytransp(Scene *scene, ARegion *ar, View3D *v3d, const glDepthMask(GL_TRUE); } +static void view3d_draw_nodepth(Scene *scene, ARegion *ar, View3D *v3d) +{ + View3DAfter *v3da, *next; + + RegionView3D *rv3d = ar->regiondata; + + glDepthMask(GL_FALSE); + + /* setup drawing environment for paths */ + + for (v3da = v3d->afterdraw_nodepth.first; v3da; v3da = next) { + Object *ob = v3da->base->object; + next = v3da->next; + + glPushMatrix(); + ED_view3d_init_mats_rv3d_gl(ob, rv3d); + view3d_cached_text_draw_begin(); + if (ob->type == OB_MESH) { + bAnimVizSettings *avs = &ob->avs; + /* draw motion path for object */ + draw_motion_paths_init(v3d, ar); + draw_motion_path_instance(scene, ob, NULL, avs, ob->mpath); + draw_motion_paths_cleanup(v3d); + } + else if (ob->type == OB_ARMATURE) { + draw_pose_paths(scene, v3d, ar, ob); + } + view3d_cached_text_draw_end(v3d, ar, 1, NULL); + ED_view3d_clear_mats_rv3d(rv3d); + + glPopMatrix(); + + BLI_remlink(&v3d->afterdraw_nodepth, v3da); + MEM_freeN(v3da); + } + + /* cleanup after drawing */ + glDepthMask(GL_TRUE); +} + /* *********************** */ /* @@ -2046,6 +2107,27 @@ int dupli_ob_sort(void *arg1, void *arg2) } #endif +static void draw_dupli_object(Scene *scene, ARegion *ar, View3D *v3d, + Base *base, DupliObject *UNUSED(dob), DupliObjectData *dob_data, + short dflag, bool draw_dupli_strands) +{ + draw_object(scene, ar, v3d, base, dflag); + + if (dob_data) { + + /* draw strands only when not editing */ + if (draw_dupli_strands) { + DupliObjectDataStrands *link; + + for (link = dob_data->strands.first; link; link = link->next) { + struct Strands *strands = link->strands; + struct StrandsChildren *children = link->strands_children; + + draw_strands(scene, v3d, ar, base->object, strands, children, dflag); + } + } + } +} static DupliObject *dupli_step(DupliObject *dob) { @@ -2067,6 +2149,7 @@ static void draw_dupli_objects_color( GLuint displist = 0; unsigned char color_rgb[3]; const short dflag_dupli = dflag | DRAW_CONSTCOLOR; + const bool draw_dupli_strands = !(base->object->mode & OB_MODE_HAIR_EDIT); short transflag; bool use_displist = false; /* -1 is initialize */ char dt; @@ -2084,7 +2167,7 @@ static void draw_dupli_objects_color( UI_GetThemeColorBlend3ubv(color, TH_BACK, 0.5f, color_rgb); } - tbase.flag = OB_FROMDUPLI | base->flag; + tbase.flag |= OB_FROMDUPLI; lb = object_duplilist(G.main->eval_ctx, scene, base->object); // BLI_listbase_sort(lb, dupli_ob_sort); /* might be nice to have if we have a dupli list with mixed objects. */ @@ -2094,7 +2177,12 @@ static void draw_dupli_objects_color( if (dob) dob_next = dupli_step(dob->next); for (; dob; dob_prev = dob, dob = dob_next, dob_next = dob_next ? dupli_step(dob_next->next) : NULL) { + /* for restoring after override */ + DupliObjectData *dob_data = NULL; + DerivedMesh *store_final_dm; + tbase.object = dob->ob; + store_final_dm = dob->ob->derivedFinal; /* Make sure lod is updated from dupli's position */ @@ -2116,7 +2204,7 @@ static void draw_dupli_objects_color( * slow it down too much */ dtx = tbase.object->dtx; if (tbase.object->dt != OB_BOUNDBOX) - tbase.object->dtx = base->object->dtx; + tbase.object->dtx = base->object->dtx | (dtx & OB_DRAWTRANSP); /* negative scale flag has to propagate */ transflag = tbase.object->transflag; @@ -2131,6 +2219,21 @@ static void draw_dupli_objects_color( glColor3ubv(color_rgb); } + /* override final DM */ + bb_tmp = NULL; + tbase.object->transflag &= ~OB_IS_DUPLI_CACHE; + if (base->object->dup_cache) { + dob_data = BKE_dupli_cache_find_data(base->object->dup_cache, tbase.object); + if (dob_data && dob_data->dm) { + tbase.object->transflag |= OB_IS_DUPLI_CACHE; + + tbase.object->derivedFinal = dob_data->dm; + bb_tmp = &dob_data->bb; + } + } + if (!bb_tmp) + bb_tmp = BKE_object_boundbox_get(dob->ob); + /* generate displist, test for new object */ if (dob_prev && dob_prev->ob != dob->ob) { if (use_displist == true) @@ -2139,7 +2242,7 @@ static void draw_dupli_objects_color( use_displist = false; } - if ((bb_tmp = BKE_object_boundbox_get(dob->ob))) { + if (bb_tmp) { bb = *bb_tmp; /* must make a copy */ testbb = true; } @@ -2161,7 +2264,8 @@ static void draw_dupli_objects_color( !bb_tmp || draw_glsl_material(scene, dob->ob, v3d, dt) || check_object_draw_texture(scene, v3d, dt) || - (v3d->flag2 & V3D_SOLID_MATCAP) != 0) + (v3d->flag2 & V3D_SOLID_MATCAP) != 0 || + ((dtx & OB_DRAWTRANSP) != 0)) { // printf("draw_dupli_objects_color: skipping displist for %s\n", dob->ob->id.name + 2); use_displist = false; @@ -2176,7 +2280,7 @@ static void draw_dupli_objects_color( displist = glGenLists(1); glNewList(displist, GL_COMPILE); - draw_object(scene, ar, v3d, &tbase, dflag_dupli); + draw_dupli_object(scene, ar, v3d, &tbase, dob, dob_data, dflag_dupli, draw_dupli_strands); glEndList(); use_displist = true; @@ -2192,10 +2296,26 @@ static void draw_dupli_objects_color( } else { copy_m4_m4(dob->ob->obmat, dob->mat); - draw_object(scene, ar, v3d, &tbase, dflag_dupli); + draw_dupli_object(scene, ar, v3d, &tbase, dob, dob_data, dflag_dupli, draw_dupli_strands); } } + /* restore final DM */ + if (tbase.object->transflag & OB_IS_DUPLI_CACHE) { + DerivedMesh *cur = tbase.object->derivedFinal; + + /* in some cases drawing code can recreate the derivedFinal, + * make sure we free those first before restoring + */ + if (cur && cur != dob_data->dm) { + cur->needsFree = 1; + cur->release(cur); + } + + tbase.object->transflag &= ~OB_IS_DUPLI_CACHE; + tbase.object->derivedFinal = store_final_dm; + } + tbase.object->dt = dt; tbase.object->dtx = dtx; tbase.object->transflag = transflag; @@ -2213,17 +2333,27 @@ static void draw_dupli_objects_color( glDeleteLists(displist, 1); } -static void draw_dupli_objects(Scene *scene, ARegion *ar, View3D *v3d, Base *base) +static void draw_dupli_objects(Scene *scene, ARegion *ar, View3D *v3d, Base *base, const bool is_wire_color) { /* define the color here so draw_dupli_objects_color can be called * from the set loop */ - - int color = (base->flag & SELECT) ? TH_SELECT : TH_WIRE; - /* debug */ - if (base->object->dup_group && base->object->dup_group->id.us < 1) - color = TH_REDALERT; - - draw_dupli_objects_color(scene, ar, v3d, base, 0, color); + short dflag; + int color; + + if (is_wire_color) { + glColor3fv(base->object->col); + color = TH_UNDEFINED; + dflag = DRAW_CONSTCOLOR; + } + else { + color = (base->flag & SELECT) ? TH_SELECT : TH_WIRE; + /* debug */ + if (base->object->dup_group && base->object->dup_group->id.us < 1) + color = TH_REDALERT; + dflag = 0; + } + + draw_dupli_objects_color(scene, ar, v3d, base, dflag, color); } /* XXX warning, not using gpu offscreen here */ @@ -2724,6 +2854,7 @@ static void view3d_draw_objects( RegionView3D *rv3d = ar->regiondata; Base *base; const bool do_camera_frame = !draw_offscreen; + const bool is_wire_color = V3D_IS_WIRECOLOR(scene, v3d); const bool draw_grids = !draw_offscreen && (v3d->flag2 & V3D_RENDER_OVERRIDE) == 0; const bool draw_floor = (rv3d->view == RV3D_VIEW_USER) || (rv3d->persp != RV3D_ORTHO); /* only draw grids after in solid modes, else it hovers over mesh wires */ @@ -2814,7 +2945,7 @@ static void view3d_draw_objects( if (v3d->lay & base->lay) { /* dupli drawing */ if (base->object->transflag & OB_DUPLI) - draw_dupli_objects(scene, ar, v3d, base); + draw_dupli_objects(scene, ar, v3d, base, is_wire_color && (base->object->dtx & OB_DRAW_WIRECOLOR)); draw_object(scene, ar, v3d, base, 0); } @@ -2831,7 +2962,7 @@ static void view3d_draw_objects( /* dupli drawing */ if (base->object->transflag & OB_DUPLI) { - draw_dupli_objects(scene, ar, v3d, base); + draw_dupli_objects(scene, ar, v3d, base, is_wire_color && (base->object->dtx & OB_DRAW_WIRECOLOR)); } if ((base->flag & SELECT) == 0) { if (base->object != scene->obedit) @@ -2843,6 +2974,10 @@ static void view3d_draw_objects( /* mask out localview */ v3d->lay_used = lay_used & ((1 << 20) - 1); + if (is_wire_color && (v3d->drawtype <= OB_WIRE)) { + glClear(GL_DEPTH_BUFFER_BIT); + } + /* draw selected and editmode */ for (base = scene->base.first; base; base = base->next) { if (v3d->lay & base->lay) { @@ -2878,6 +3013,8 @@ static void view3d_draw_objects( if (v3d->afterdraw_xray.first) view3d_draw_xray(scene, ar, v3d, &xrayclear); if (v3d->afterdraw_xraytransp.first) view3d_draw_xraytransp(scene, ar, v3d, xrayclear); + if (v3d->afterdraw_nodepth.first) view3d_draw_nodepth(scene, ar, v3d); + if (fx && do_composite_xray) { GPU_fx_compositor_XRay_resolve(fx); } @@ -2894,10 +3031,6 @@ static void view3d_draw_objects( view3d_draw_bgpic_test(scene, ar, v3d, true, do_camera_frame); } - if (!draw_offscreen) { - BIF_draw_manipulator(C); - } - /* cleanup */ if (v3d->zbuf) { v3d->zbuf = false; @@ -2934,6 +3067,8 @@ void ED_view3d_draw_offscreen_init(Scene *scene, View3D *v3d) */ static void view3d_main_area_clear(Scene *scene, View3D *v3d, ARegion *ar) { + const bool is_wire_color = V3D_IS_WIRECOLOR(scene, v3d); + if (scene->world && (v3d->flag3 & V3D_SHOW_WORLD)) { bool glsl = GPU_glsl_support() && BKE_scene_use_new_shading_nodes(scene) && scene->world->nodetree && scene->world->use_nodes; @@ -3096,6 +3231,12 @@ static void view3d_main_area_clear(Scene *scene, View3D *v3d, ARegion *ar) #undef VIEWGRAD_RES_X #undef VIEWGRAD_RES_Y + + if (is_wire_color) { + float col_mid[3]; + mid_v3_v3v3(col_mid, col_hor, col_zen); + draw_object_bg_wire_color_set(col_mid); + } } else { /* solid sky */ float col_hor[3]; @@ -3104,10 +3245,18 @@ static void view3d_main_area_clear(Scene *scene, View3D *v3d, ARegion *ar) glClearColor(col_hor[0], col_hor[1], col_hor[2], 1.0); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + if (is_wire_color) { + draw_object_bg_wire_color_set(col_hor); + } } } else { if (UI_GetThemeValue(TH_SHOW_BACK_GRAD)) { + float col_low[3], col_high[3]; + + UI_GetThemeColor3fv(TH_HIGH_GRAD, col_high); + UI_GetThemeColor3fv(TH_LOW_GRAD, col_low); glMatrixMode(GL_PROJECTION); glPushMatrix(); glLoadIdentity(); @@ -3119,10 +3268,10 @@ static void view3d_main_area_clear(Scene *scene, View3D *v3d, ARegion *ar) glDepthFunc(GL_ALWAYS); glShadeModel(GL_SMOOTH); glBegin(GL_QUADS); - UI_ThemeColor(TH_LOW_GRAD); + glColor3fv(col_low); glVertex3f(-1.0, -1.0, 1.0); glVertex3f(1.0, -1.0, 1.0); - UI_ThemeColor(TH_HIGH_GRAD); + glColor3fv(col_high); glVertex3f(1.0, 1.0, 1.0); glVertex3f(-1.0, 1.0, 1.0); glEnd(); @@ -3136,10 +3285,23 @@ static void view3d_main_area_clear(Scene *scene, View3D *v3d, ARegion *ar) glMatrixMode(GL_MODELVIEW); glPopMatrix(); + + if (is_wire_color) { + float col_mid[3]; + mid_v3_v3v3(col_mid, col_low, col_high); + draw_object_bg_wire_color_set(col_mid); + } } else { + float col[3]; + + UI_GetThemeColor3fv(TH_HIGH_GRAD, col); UI_ThemeClearColorAlpha(TH_HIGH_GRAD, 1.0f); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + if (is_wire_color) { + draw_object_bg_wire_color_set(col); + } } } } @@ -3759,6 +3921,8 @@ static void view3d_main_area_draw_objects(const bContext *C, Scene *scene, View3 /* main drawing call */ view3d_draw_objects(C, scene, v3d, ar, grid_unit, true, false, do_compositing ? rv3d->compositor : NULL); + WM_widgets_draw(C, ar->widgetmaps.first, true); + /* post process */ if (do_compositing) { GPU_fx_do_composite_pass(rv3d->compositor, rv3d->winmat, rv3d->is_persp, scene, NULL); @@ -3872,7 +4036,7 @@ static void view3d_main_area_draw_info(const bContext *C, Scene *scene, if ((v3d->flag2 & V3D_RENDER_OVERRIDE) == 0) { wmWindowManager *wm = CTX_wm_manager(C); - if ((U.uiflag & USER_SHOW_FPS) && ED_screen_animation_playing(wm)) { + if ((U.uiflag & USER_SHOW_FPS) && ED_screen_animation_no_scrub(wm)) { ED_scene_draw_fps(scene, &rect); } else if (U.uiflag & USER_SHOW_VIEWPORTNAME) { @@ -3907,6 +4071,8 @@ void view3d_main_area_draw(const bContext *C, ARegion *ar) render_border = ED_view3d_calc_render_border(scene, v3d, ar, &border_rect); clip_border = (render_border && !BLI_rcti_compare(&ar->drawrct, &border_rect)); + WM_widgets_update(C, ar->widgetmaps.first); + /* draw viewport using opengl */ if (v3d->drawtype != OB_RENDER || !view3d_main_area_do_render_draw(scene) || clip_border) { view3d_main_area_draw_objects(C, scene, v3d, ar, &grid_unit); @@ -3916,14 +4082,18 @@ void view3d_main_area_draw(const bContext *C, ARegion *ar) #endif if (G.debug & G_DEBUG_SIMDATA) draw_sim_debug_data(scene, v3d, ar); - - ED_region_pixelspace(ar); } /* draw viewport using external renderer */ if (v3d->drawtype == OB_RENDER) view3d_main_area_draw_engine(C, scene, ar, v3d, clip_border, &border_rect); + view3d_main_area_setup_view(scene, v3d, ar, NULL, NULL); + glClear(GL_DEPTH_BUFFER_BIT); + WM_widgets_draw(C, ar->widgetmaps.first, false); + BIF_draw_manipulator(C); + ED_region_pixelspace(ar); + view3d_main_area_draw_info(C, scene, ar, v3d, grid_unit, render_border); v3d->flag |= V3D_INVALID_BACKBUF; diff --git a/source/blender/editors/space_view3d/view3d_edit.c b/source/blender/editors/space_view3d/view3d_edit.c index b7f2f48dece..b9f2c323af4 100644 --- a/source/blender/editors/space_view3d/view3d_edit.c +++ b/source/blender/editors/space_view3d/view3d_edit.c @@ -862,7 +862,7 @@ void viewrotate_modal_keymap(wmKeyConfig *keyconf) {VIEWROT_MODAL_AXIS_SNAP_ENABLE, "AXIS_SNAP_ENABLE", 0, "Enable Axis Snap", ""}, {VIEWROT_MODAL_AXIS_SNAP_DISABLE, "AXIS_SNAP_DISABLE", 0, "Disable Axis Snap", ""}, - + {VIEWROT_MODAL_SWITCH_ZOOM, "SWITCH_TO_ZOOM", 0, "Switch to Zoom"}, {VIEWROT_MODAL_SWITCH_MOVE, "SWITCH_TO_MOVE", 0, "Switch to Move"}, @@ -889,7 +889,7 @@ void viewrotate_modal_keymap(wmKeyConfig *keyconf) WM_modalkeymap_add_item(keymap, LEFTCTRLKEY, KM_PRESS, KM_ANY, 0, VIEWROT_MODAL_SWITCH_ZOOM); WM_modalkeymap_add_item(keymap, LEFTSHIFTKEY, KM_PRESS, KM_ANY, 0, VIEWROT_MODAL_SWITCH_MOVE); #endif - + /* assign map to operators */ WM_modalkeymap_assign(keymap, "VIEW3D_OT_rotate"); @@ -1627,7 +1627,7 @@ void view3d_ndof_fly( */ static int ndof_orbit_invoke(bContext *C, wmOperator *op, const wmEvent *event) { - + if (event->type != NDOF_MOTION) { return OPERATOR_CANCELLED; } @@ -1693,7 +1693,7 @@ void VIEW3D_OT_ndof_orbit(struct wmOperatorType *ot) static int ndof_orbit_zoom_invoke(bContext *C, wmOperator *op, const wmEvent *event) { - + if (event->type != NDOF_MOTION) { return OPERATOR_CANCELLED; } @@ -1889,7 +1889,7 @@ void viewmove_modal_keymap(wmKeyConfig *keyconf) { static EnumPropertyItem modal_items[] = { {VIEW_MODAL_CONFIRM, "CONFIRM", 0, "Confirm", ""}, - + {VIEWROT_MODAL_SWITCH_ZOOM, "SWITCH_TO_ZOOM", 0, "Switch to Zoom"}, {VIEWROT_MODAL_SWITCH_ROTATE, "SWITCH_TO_ROTATE", 0, "Switch to Rotate"}, @@ -1913,7 +1913,7 @@ void viewmove_modal_keymap(wmKeyConfig *keyconf) WM_modalkeymap_add_item(keymap, LEFTCTRLKEY, KM_PRESS, KM_ANY, 0, VIEWROT_MODAL_SWITCH_ZOOM); WM_modalkeymap_add_item(keymap, LEFTSHIFTKEY, KM_RELEASE, KM_ANY, 0, VIEWROT_MODAL_SWITCH_ROTATE); #endif - + /* assign map to operators */ WM_modalkeymap_assign(keymap, "VIEW3D_OT_move"); } @@ -2022,9 +2022,9 @@ static int viewmove_invoke(bContext *C, wmOperator *op, const wmEvent *event) /* invert it, trackpad scroll follows same principle as 2d windows this way */ viewmove_apply(vod, 2 * event->x - event->prevx, 2 * event->y - event->prevy); ED_view3d_depth_tag_update(vod->rv3d); - + viewops_data_free(C, op); - + return OPERATOR_FINISHED; } else { @@ -2066,7 +2066,7 @@ void viewzoom_modal_keymap(wmKeyConfig *keyconf) { static EnumPropertyItem modal_items[] = { {VIEW_MODAL_CONFIRM, "CONFIRM", 0, "Confirm", ""}, - + {VIEWROT_MODAL_SWITCH_ROTATE, "SWITCH_TO_ROTATE", 0, "Switch to Rotate"}, {VIEWROT_MODAL_SWITCH_MOVE, "SWITCH_TO_MOVE", 0, "Switch to Move"}, @@ -2090,7 +2090,7 @@ void viewzoom_modal_keymap(wmKeyConfig *keyconf) WM_modalkeymap_add_item(keymap, LEFTCTRLKEY, KM_RELEASE, KM_ANY, 0, VIEWROT_MODAL_SWITCH_ROTATE); WM_modalkeymap_add_item(keymap, LEFTSHIFTKEY, KM_PRESS, KM_ANY, 0, VIEWROT_MODAL_SWITCH_MOVE); #endif - + /* assign map to operators */ WM_modalkeymap_assign(keymap, "VIEW3D_OT_zoom"); } @@ -2482,7 +2482,7 @@ void viewdolly_modal_keymap(wmKeyConfig *keyconf) WM_modalkeymap_add_item(keymap, LEFTCTRLKEY, KM_RELEASE, KM_ANY, 0, VIEWROT_MODAL_SWITCH_ROTATE); WM_modalkeymap_add_item(keymap, LEFTSHIFTKEY, KM_PRESS, KM_ANY, 0, VIEWROT_MODAL_SWITCH_MOVE); #endif - + /* assign map to operators */ WM_modalkeymap_assign(keymap, "VIEW3D_OT_dolly"); } @@ -3202,7 +3202,7 @@ static int viewcenter_cursor_exec(bContext *C, wmOperator *op) View3D *v3d = CTX_wm_view3d(C); RegionView3D *rv3d = CTX_wm_region_view3d(C); Scene *scene = CTX_data_scene(C); - + if (rv3d) { ARegion *ar = CTX_wm_region(C); const int smooth_viewtx = WM_operator_smooth_viewtx_get(op); @@ -3213,10 +3213,10 @@ static int viewcenter_cursor_exec(bContext *C, wmOperator *op) ED_view3d_smooth_view(C, v3d, ar, NULL, NULL, new_ofs, NULL, NULL, NULL, smooth_viewtx); - + /* smooth view does viewlock RV3D_BOXVIEW copy */ } - + return OPERATOR_FINISHED; } @@ -3226,11 +3226,11 @@ void VIEW3D_OT_view_center_cursor(wmOperatorType *ot) ot->name = "Center View to Cursor"; ot->description = "Center the view so that the cursor is in the middle of the view"; ot->idname = "VIEW3D_OT_view_center_cursor"; - + /* api callbacks */ ot->exec = viewcenter_cursor_exec; ot->poll = ED_operator_view3d_active; - + /* flags */ ot->flag = 0; } @@ -3542,17 +3542,17 @@ static int view3d_zoom_border_exec(bContext *C, wmOperator *op) /* Get Z Depths, needed for perspective, nice for ortho */ bgl_get_mats(&mats); ED_view3d_draw_depth(scene, ar, v3d, true); - + { /* avoid allocating the whole depth buffer */ ViewDepths depth_temp = {0}; /* avoid view3d_update_depths() for speed. */ view3d_update_depths_rect(ar, &depth_temp, &rect); - + /* find the closest Z pixel */ depth_close = view3d_depth_near(&depth_temp); - + MEM_SAFE_FREE(depth_temp.depths); } @@ -3692,7 +3692,7 @@ static void view3d_set_1_to_1_viewborder(Scene *scene, ARegion *ar, View3D *v3d) RegionView3D *rv3d = ar->regiondata; float size[2]; int im_width = (scene->r.size * scene->r.xsch) / 100; - + ED_view3d_calc_camera_border_size(scene, ar, v3d, rv3d, size); rv3d->camzoom = BKE_screen_view3d_zoom_from_fac((float)im_width / size[0]); @@ -4056,7 +4056,7 @@ void VIEW3D_OT_view_orbit(wmOperatorType *ot) /* flags */ ot->flag = 0; - + /* properties */ prop = RNA_def_float(ot->srna, "angle", 0, -FLT_MAX, FLT_MAX, "Roll", "", -FLT_MAX, FLT_MAX); RNA_def_property_flag(prop, PROP_SKIP_SAVE); @@ -4338,7 +4338,7 @@ void VIEW3D_OT_view_pan(wmOperatorType *ot) /* flags */ ot->flag = 0; - + /* Properties */ ot->prop = RNA_def_enum(ot->srna, "type", prop_view_pan_items, 0, "Pan", "Direction of View Pan"); } @@ -4440,7 +4440,7 @@ static int background_image_add_invoke(bContext *C, wmOperator *op, const wmEven v3d->flag |= V3D_DISPBGPICS; WM_event_add_notifier(C, NC_SPACE | ND_SPACE_VIEW3D, v3d); - + return OPERATOR_FINISHED; } @@ -4460,7 +4460,7 @@ void VIEW3D_OT_background_image_add(wmOperatorType *ot) /* flags */ ot->flag = 0; - + /* properties */ RNA_def_string(ot->srna, "name", "Image", MAX_ID_NAME - 2, "Name", "Image name to assign"); WM_operator_properties_filesel(ot, FILE_TYPE_FOLDER | FILE_TYPE_IMAGE | FILE_TYPE_MOVIE, FILE_SPECIAL, FILE_OPENFILE, @@ -4507,7 +4507,7 @@ void VIEW3D_OT_background_image_remove(wmOperatorType *ot) /* flags */ ot->flag = 0; - + /* properties */ RNA_def_int(ot->srna, "index", 0, 0, INT_MAX, "Index", "Background image index to remove", 0, INT_MAX); } @@ -4610,14 +4610,14 @@ void ED_view3d_cursor3d_position(bContext *C, float fp[3], const int mval[2]) RegionView3D *rv3d = ar->regiondata; bool flip; bool depth_used = false; - + /* normally the caller should ensure this, * but this is called from areas that aren't already dealing with the viewport */ if (rv3d == NULL) return; ED_view3d_calc_zfac(rv3d, fp, &flip); - + /* reset the depth based on the view offset (we _know_ the offset is infront of us) */ if (flip) { negate_v3_v3(fp, rv3d->ofs); @@ -4742,14 +4742,14 @@ static int enable_manipulator_invoke(bContext *C, wmOperator *op, const wmEvent View3D *v3d = CTX_wm_view3d(C); v3d->twtype = 0; - + if (RNA_boolean_get(op->ptr, "translate")) v3d->twtype |= V3D_MANIP_TRANSLATE; if (RNA_boolean_get(op->ptr, "rotate")) v3d->twtype |= V3D_MANIP_ROTATE; if (RNA_boolean_get(op->ptr, "scale")) v3d->twtype |= V3D_MANIP_SCALE; - + WM_event_add_notifier(C, NC_SPACE | ND_SPACE_VIEW3D, v3d); return OPERATOR_FINISHED; @@ -4763,11 +4763,11 @@ void VIEW3D_OT_enable_manipulator(wmOperatorType *ot) ot->name = "Enable 3D Manipulator"; ot->description = "Enable the transform manipulator for use"; ot->idname = "VIEW3D_OT_enable_manipulator"; - + /* api callbacks */ ot->invoke = enable_manipulator_invoke; ot->poll = ED_operator_view3d_active; - + /* rna later */ prop = RNA_def_boolean(ot->srna, "translate", 0, "Translate", "Enable the translate manipulator"); RNA_def_property_flag(prop, PROP_SKIP_SAVE); @@ -4957,7 +4957,7 @@ float ED_view3d_offset_distance(float mat[4][4], const float ofs[3], const float float pos[4] = {0.0f, 0.0f, 0.0f, 1.0f}; float dir[4] = {0.0f, 0.0f, 1.0f, 0.0f}; float dist; - + mul_m4_v4(mat, pos); add_v3_v3(pos, ofs); mul_m4_v4(mat, dir); diff --git a/source/blender/editors/space_view3d/view3d_header.c b/source/blender/editors/space_view3d/view3d_header.c index f8912345e1c..95532bc09a5 100644 --- a/source/blender/editors/space_view3d/view3d_header.c +++ b/source/blender/editors/space_view3d/view3d_header.c @@ -340,7 +340,7 @@ void uiTemplateHeader3D(uiLayout *layout, struct bContext *C) } /* Manipulators aren't used in paint modes */ - if (!ELEM(ob->mode, OB_MODE_SCULPT, OB_MODE_PARTICLE_EDIT)) { + if (!ELEM(ob->mode, OB_MODE_SCULPT, OB_MODE_PARTICLE_EDIT, OB_MODE_HAIR_EDIT)) { /* masks aren't used for sculpt and particle painting */ PointerRNA meshptr; diff --git a/source/blender/editors/space_view3d/view3d_intern.h b/source/blender/editors/space_view3d/view3d_intern.h index 95c0ef92680..f4ecde79365 100644 --- a/source/blender/editors/space_view3d/view3d_intern.h +++ b/source/blender/editors/space_view3d/view3d_intern.h @@ -46,6 +46,9 @@ struct bContext; struct bMotionPath; struct bPoseChannel; struct Mesh; +struct BMEditStrands; +struct Strands; +struct StrandsChildren; struct wmNDOFMotionData; struct wmOperatorType; struct wmWindowManager; @@ -132,6 +135,7 @@ void draw_motion_paths_cleanup(View3D *v3d); /* drawobject.c */ +void draw_object_bg_wire_color_set(const float color[3]); void draw_object(Scene *scene, struct ARegion *ar, View3D *v3d, Base *base, const short dflag); bool draw_glsl_material(Scene *scene, struct Object *ob, View3D *v3d, const char dt); void draw_object_instance(Scene *scene, View3D *v3d, RegionView3D *rv3d, struct Object *ob, const char dt, int outline); @@ -158,6 +162,7 @@ enum { bool draw_armature(Scene *scene, View3D *v3d, ARegion *ar, Base *base, const short dt, const short dflag, const unsigned char ob_wire_col[4], const bool is_outline); +void draw_pose_paths(struct Scene *scene, struct View3D *v3d, struct ARegion *ar, struct Object *ob); /* drawmesh.c */ void draw_mesh_textured(Scene *scene, View3D *v3d, RegionView3D *rv3d, @@ -179,6 +184,10 @@ void draw_mesh_paint(View3D *v3d, RegionView3D *rv3d, /* drawsimdebug.c */ void draw_sim_debug_data(Scene *scene, View3D *v3d, ARegion *ar); +/* drawstrands.c */ +void draw_strands(Scene *scene, View3D *v3d, ARegion *ar, struct Object *ob, struct Strands *strands, struct StrandsChildren *children, short dflag); +void draw_strands_edit_hair(Scene *scene, View3D *v3d, ARegion *ar, struct BMEditStrands *edit); + /* view3d_draw.c */ void view3d_main_area_draw(const struct bContext *C, struct ARegion *ar); void ED_view3d_draw_depth(Scene *scene, struct ARegion *ar, View3D *v3d, bool alphaoverride); diff --git a/source/blender/editors/space_view3d/view3d_ops.c b/source/blender/editors/space_view3d/view3d_ops.c index 6a2c948aa8d..56faf945fb9 100644 --- a/source/blender/editors/space_view3d/view3d_ops.c +++ b/source/blender/editors/space_view3d/view3d_ops.c @@ -4,7 +4,7 @@ * 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. + * 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 @@ -18,7 +18,7 @@ * The Original Code is Copyright (C) 2008 Blender Foundation. * All rights reserved. * - * + * * Contributor(s): Blender Foundation * * ***** END GPL LICENSE BLOCK ***** @@ -68,9 +68,9 @@ static int view3d_copybuffer_exec(bContext *C, wmOperator *op) { Main *bmain = CTX_data_main(C); char str[FILE_MAX]; - + BKE_copybuffer_begin(bmain); - + /* context, selection, could be generalized */ CTX_DATA_BEGIN (C, Object *, ob, selected_objects) { @@ -80,7 +80,7 @@ static int view3d_copybuffer_exec(bContext *C, wmOperator *op) BLI_make_file_string("/", str, BKE_tempdir_base(), "copybuffer.blend"); BKE_copybuffer_save(str, op->reports); - + BKE_report(op->reports, RPT_INFO, "Copied selected objects to buffer"); return OPERATOR_FINISHED; @@ -88,12 +88,12 @@ static int view3d_copybuffer_exec(bContext *C, wmOperator *op) static void VIEW3D_OT_copybuffer(wmOperatorType *ot) { - + /* identifiers */ ot->name = "Copy Selection to Buffer"; ot->idname = "VIEW3D_OT_copybuffer"; ot->description = "Selected objects are saved in a temp file"; - + /* api callbacks */ ot->exec = view3d_copybuffer_exec; ot->poll = ED_operator_view3d_active; @@ -119,16 +119,16 @@ static int view3d_pastebuffer_exec(bContext *C, wmOperator *op) static void VIEW3D_OT_pastebuffer(wmOperatorType *ot) { - + /* identifiers */ ot->name = "Paste Selection from Buffer"; ot->idname = "VIEW3D_OT_pastebuffer"; ot->description = "Contents of copy buffer gets pasted"; - + /* api callbacks */ ot->exec = view3d_pastebuffer_exec; ot->poll = ED_operator_view3d_active; - + /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } @@ -186,17 +186,17 @@ void view3d_operatortypes(void) WM_operatortype_append(VIEW3D_OT_layers); WM_operatortype_append(VIEW3D_OT_copybuffer); WM_operatortype_append(VIEW3D_OT_pastebuffer); - + WM_operatortype_append(VIEW3D_OT_properties); WM_operatortype_append(VIEW3D_OT_toolshelf); - + WM_operatortype_append(VIEW3D_OT_snap_selected_to_grid); WM_operatortype_append(VIEW3D_OT_snap_selected_to_cursor); WM_operatortype_append(VIEW3D_OT_snap_cursor_to_grid); WM_operatortype_append(VIEW3D_OT_snap_cursor_to_center); WM_operatortype_append(VIEW3D_OT_snap_cursor_to_selected); WM_operatortype_append(VIEW3D_OT_snap_cursor_to_active); - + transform_operatortypes(); } @@ -204,12 +204,12 @@ void view3d_keymap(wmKeyConfig *keyconf) { wmKeyMap *keymap; wmKeyMapItem *kmi; - + keymap = WM_keymap_find(keyconf, "3D View Generic", SPACE_VIEW3D, 0); WM_keymap_add_item(keymap, "VIEW3D_OT_properties", NKEY, KM_PRESS, 0, 0); WM_keymap_add_item(keymap, "VIEW3D_OT_toolshelf", TKEY, KM_PRESS, 0, 0); - + /* only for region 3D window */ keymap = WM_keymap_find(keyconf, "3D View", SPACE_VIEW3D, 0); @@ -219,9 +219,9 @@ void view3d_keymap(wmKeyConfig *keyconf) * Doesn't work with KM_SHIFT, have to use KM_ANY and filter in invoke * */ // WM_keymap_verify_item(keymap, "VIEW3D_OT_manipulator", LEFTMOUSE, KM_PRESS, KM_SHIFT, 0); - + WM_keymap_verify_item(keymap, "VIEW3D_OT_cursor3d", ACTIONMOUSE, KM_PRESS, 0, 0); - + WM_keymap_verify_item(keymap, "VIEW3D_OT_rotate", MIDDLEMOUSE, KM_PRESS, 0, 0); WM_keymap_verify_item(keymap, "VIEW3D_OT_move", MIDDLEMOUSE, KM_PRESS, KM_SHIFT, 0); WM_keymap_verify_item(keymap, "VIEW3D_OT_zoom", MIDDLEMOUSE, KM_PRESS, KM_CTRL, 0); @@ -244,14 +244,14 @@ void view3d_keymap(wmKeyConfig *keyconf) WM_keymap_add_item(keymap, "VIEW3D_OT_move", MOUSEPAN, 0, KM_SHIFT, 0); WM_keymap_add_item(keymap, "VIEW3D_OT_zoom", MOUSEZOOM, 0, 0, 0); WM_keymap_add_item(keymap, "VIEW3D_OT_zoom", MOUSEPAN, 0, KM_CTRL, 0); - + /*numpad +/-*/ RNA_int_set(WM_keymap_add_item(keymap, "VIEW3D_OT_zoom", PADPLUSKEY, KM_PRESS, 0, 0)->ptr, "delta", 1); RNA_int_set(WM_keymap_add_item(keymap, "VIEW3D_OT_zoom", PADMINUS, KM_PRESS, 0, 0)->ptr, "delta", -1); /*ctrl +/-*/ RNA_int_set(WM_keymap_add_item(keymap, "VIEW3D_OT_zoom", EQUALKEY, KM_PRESS, KM_CTRL, 0)->ptr, "delta", 1); RNA_int_set(WM_keymap_add_item(keymap, "VIEW3D_OT_zoom", MINUSKEY, KM_PRESS, KM_CTRL, 0)->ptr, "delta", -1); - + /*wheel mouse forward/back*/ RNA_int_set(WM_keymap_add_item(keymap, "VIEW3D_OT_zoom", WHEELINMOUSE, KM_PRESS, 0, 0)->ptr, "delta", 1); RNA_int_set(WM_keymap_add_item(keymap, "VIEW3D_OT_zoom", WHEELOUTMOUSE, KM_PRESS, 0, 0)->ptr, "delta", -1); @@ -287,7 +287,7 @@ void view3d_keymap(wmKeyConfig *keyconf) RNA_enum_set(WM_keymap_add_item(keymap, "VIEW3D_OT_viewnumpad", PAD3, KM_PRESS, 0, 0)->ptr, "type", RV3D_VIEW_RIGHT); RNA_enum_set(WM_keymap_add_item(keymap, "VIEW3D_OT_view_orbit", PAD4, KM_PRESS, 0, 0)->ptr, "type", V3D_VIEW_STEPLEFT); WM_keymap_add_item(keymap, "VIEW3D_OT_view_persportho", PAD5, KM_PRESS, 0, 0); - + RNA_enum_set(WM_keymap_add_item(keymap, "VIEW3D_OT_view_orbit", PAD6, KM_PRESS, 0, 0)->ptr, "type", V3D_VIEW_STEPRIGHT); RNA_enum_set(WM_keymap_add_item(keymap, "VIEW3D_OT_viewnumpad", PAD7, KM_PRESS, 0, 0)->ptr, "type", RV3D_VIEW_TOP); RNA_enum_set(WM_keymap_add_item(keymap, "VIEW3D_OT_view_orbit", PAD8, KM_PRESS, 0, 0)->ptr, "type", V3D_VIEW_STEPUP); @@ -313,7 +313,7 @@ void view3d_keymap(wmKeyConfig *keyconf) RNA_enum_set(WM_keymap_add_item(keymap, "VIEW3D_OT_view_orbit", WHEELDOWNMOUSE, KM_PRESS, KM_CTRL | KM_ALT, 0)->ptr, "type", V3D_VIEW_STEPRIGHT); RNA_enum_set(WM_keymap_add_item(keymap, "VIEW3D_OT_view_orbit", WHEELUPMOUSE, KM_PRESS, KM_SHIFT | KM_ALT, 0)->ptr, "type", V3D_VIEW_STEPUP); RNA_enum_set(WM_keymap_add_item(keymap, "VIEW3D_OT_view_orbit", WHEELDOWNMOUSE, KM_PRESS, KM_SHIFT | KM_ALT, 0)->ptr, "type", V3D_VIEW_STEPDOWN); - + RNA_enum_set(WM_keymap_add_item(keymap, "VIEW3D_OT_view_roll", WHEELUPMOUSE, KM_PRESS, KM_CTRL | KM_SHIFT, 0)->ptr, "type", V3D_VIEW_STEPLEFT); RNA_enum_set(WM_keymap_add_item(keymap, "VIEW3D_OT_view_roll", WHEELDOWNMOUSE, KM_PRESS, KM_CTRL | KM_SHIFT, 0)->ptr, "type", V3D_VIEW_STEPRIGHT); @@ -338,7 +338,7 @@ void view3d_keymap(wmKeyConfig *keyconf) RNA_boolean_set(kmi->ptr, "align_active", true); WM_keymap_add_item(keymap, "VIEW3D_OT_localview", PADSLASHKEY, KM_PRESS, 0, 0); - + /* NDOF: begin */ /* note: positioned here so keymaps show keyboard keys if assigned */ /* 3D mouse */ @@ -358,7 +358,7 @@ void view3d_keymap(wmKeyConfig *keyconf) RNA_enum_set(WM_keymap_add_item(keymap, "VIEW3D_OT_viewnumpad", NDOF_BUTTON_RIGHT, KM_PRESS, 0, 0)->ptr, "type", RV3D_VIEW_RIGHT); RNA_enum_set(WM_keymap_add_item(keymap, "VIEW3D_OT_viewnumpad", NDOF_BUTTON_TOP, KM_PRESS, 0, 0)->ptr, "type", RV3D_VIEW_TOP); RNA_enum_set(WM_keymap_add_item(keymap, "VIEW3D_OT_viewnumpad", NDOF_BUTTON_BOTTOM, KM_PRESS, 0, 0)->ptr, "type", RV3D_VIEW_BOTTOM); - + /* 3D mouse align */ kmi = WM_keymap_add_item(keymap, "VIEW3D_OT_viewnumpad", NDOF_BUTTON_FRONT, KM_PRESS, KM_SHIFT, 0); RNA_enum_set(kmi->ptr, "type", RV3D_VIEW_FRONT); @@ -370,7 +370,7 @@ void view3d_keymap(wmKeyConfig *keyconf) RNA_enum_set(kmi->ptr, "type", RV3D_VIEW_TOP); RNA_boolean_set(kmi->ptr, "align_active", true); /* NDOF: end */ - + /* layers, shift + alt are properties set in invoke() */ RNA_int_set(WM_keymap_add_item(keymap, "VIEW3D_OT_layers", ACCENTGRAVEKEY, KM_PRESS, 0, 0)->ptr, "nr", 0); @@ -384,7 +384,7 @@ void view3d_keymap(wmKeyConfig *keyconf) RNA_int_set(WM_keymap_add_item(keymap, "VIEW3D_OT_layers", EIGHTKEY, KM_PRESS, KM_ANY, 0)->ptr, "nr", 8); RNA_int_set(WM_keymap_add_item(keymap, "VIEW3D_OT_layers", NINEKEY, KM_PRESS, KM_ANY, 0)->ptr, "nr", 9); RNA_int_set(WM_keymap_add_item(keymap, "VIEW3D_OT_layers", ZEROKEY, KM_PRESS, KM_ANY, 0)->ptr, "nr", 10); - + /* drawtype */ kmi = WM_keymap_add_item(keymap, "WM_OT_context_toggle_enum", ZKEY, KM_PRESS, 0, 0); @@ -468,7 +468,7 @@ void view3d_keymap(wmKeyConfig *keyconf) kmi = WM_keymap_add_item(keymap, "VIEW3D_OT_select_lasso", EVT_TWEAK_A, KM_ANY, KM_SHIFT | KM_CTRL, 0); RNA_boolean_set(kmi->ptr, "deselect", true); WM_keymap_add_item(keymap, "VIEW3D_OT_select_circle", CKEY, KM_PRESS, 0, 0); - + WM_keymap_add_item(keymap, "VIEW3D_OT_clip_border", BKEY, KM_PRESS, KM_ALT, 0); WM_keymap_add_item(keymap, "VIEW3D_OT_zoom_border", BKEY, KM_PRESS, KM_SHIFT, 0); @@ -478,19 +478,19 @@ void view3d_keymap(wmKeyConfig *keyconf) RNA_boolean_set(kmi->ptr, "camera_only", false); WM_keymap_add_item(keymap, "VIEW3D_OT_clear_render_border", BKEY, KM_PRESS, KM_CTRL | KM_ALT, 0); - + WM_keymap_add_item(keymap, "VIEW3D_OT_camera_to_view", PAD0, KM_PRESS, KM_ALT | KM_CTRL, 0); WM_keymap_add_item(keymap, "VIEW3D_OT_object_as_camera", PAD0, KM_PRESS, KM_CTRL, 0); - + WM_keymap_add_menu(keymap, "VIEW3D_MT_snap", SKEY, KM_PRESS, KM_SHIFT, 0); - + #ifdef __APPLE__ WM_keymap_add_item(keymap, "VIEW3D_OT_copybuffer", CKEY, KM_PRESS, KM_OSKEY, 0); WM_keymap_add_item(keymap, "VIEW3D_OT_pastebuffer", VKEY, KM_PRESS, KM_OSKEY, 0); #endif WM_keymap_add_item(keymap, "VIEW3D_OT_copybuffer", CKEY, KM_PRESS, KM_CTRL, 0); WM_keymap_add_item(keymap, "VIEW3D_OT_pastebuffer", VKEY, KM_PRESS, KM_CTRL, 0); - + /* context ops */ kmi = WM_keymap_add_item(keymap, "WM_OT_context_set_enum", COMMAKEY, KM_PRESS, 0, 0); RNA_string_set(kmi->ptr, "data_path", "space_data.pivot_point"); diff --git a/source/blender/editors/space_view3d/view3d_select.c b/source/blender/editors/space_view3d/view3d_select.c index 49e42cf164a..1847de3c6a2 100644 --- a/source/blender/editors/space_view3d/view3d_select.c +++ b/source/blender/editors/space_view3d/view3d_select.c @@ -85,9 +85,11 @@ #include "ED_armature.h" #include "ED_curve.h" +#include "ED_physics.h" #include "ED_particle.h" #include "ED_mesh.h" #include "ED_object.h" +#include "ED_physics.h" #include "ED_screen.h" #include "ED_sculpt.h" #include "ED_mball.h" @@ -834,6 +836,8 @@ static void view3d_lasso_select(bContext *C, ViewContext *vc, } else if (ob && (ob->mode & OB_MODE_PARTICLE_EDIT)) PE_lasso_select(C, mcords, moves, extend, select); + else if (ob && (ob->mode & OB_MODE_HAIR_EDIT)) + ED_hair_lasso_select(C, mcords, moves, extend, select); else { do_lasso_select_objects(vc, mcords, moves, extend, select); WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, vc->scene); @@ -2146,6 +2150,9 @@ static int view3d_borderselect_exec(bContext *C, wmOperator *op) else if (vc.obact && vc.obact->mode & OB_MODE_PARTICLE_EDIT) { ret = PE_border_select(C, &rect, select, extend); } + else if (vc.obact && vc.obact->mode & OB_MODE_HAIR_EDIT) { + ret = ED_hair_border_select(C, &rect, select, extend); + } else { /* object mode with none active */ ret = do_object_pose_box_select(C, &vc, &rect, select, extend); } @@ -2276,6 +2283,8 @@ static int view3d_select_exec(bContext *C, wmOperator *op) } else if (obact && obact->mode & OB_MODE_PARTICLE_EDIT) return PE_mouse_particles(C, location, extend, deselect, toggle); + else if (obact && obact->mode & OB_MODE_HAIR_EDIT) + return ED_hair_mouse_select(C, location, extend, deselect, toggle); else if (obact && BKE_paint_select_face_test(obact)) retval = paintface_mouse_select(C, obact, location, extend, deselect, toggle); else if (BKE_paint_select_vert_test(obact)) @@ -2791,7 +2800,7 @@ static int view3d_circle_select_exec(bContext *C, wmOperator *op) RNA_int_get(op->ptr, "y")}; if (CTX_data_edit_object(C) || BKE_paint_select_elem_test(obact) || - (obact && (obact->mode & (OB_MODE_PARTICLE_EDIT | OB_MODE_POSE))) ) + (obact && (obact->mode & (OB_MODE_PARTICLE_EDIT | OB_MODE_POSE | OB_MODE_HAIR_EDIT))) ) { ViewContext vc; @@ -2811,10 +2820,16 @@ static int view3d_circle_select_exec(bContext *C, wmOperator *op) paint_vertsel_circle_select(&vc, select, mval, (float)radius); WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obact->data); } - else if (obact->mode & OB_MODE_POSE) + else if (obact->mode & OB_MODE_POSE) { pose_circle_select(&vc, select, mval, (float)radius); - else + } + else if (obact->mode & OB_MODE_HAIR_EDIT) { + ED_hair_circle_select(C, select, mval, (float)radius); + WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obact->data); + } + else { return PE_circle_select(C, select, mval, (float)radius); + } } else if (obact && obact->mode & OB_MODE_SCULPT) { return OPERATOR_CANCELLED; diff --git a/source/blender/editors/space_view3d/view3d_view.c b/source/blender/editors/space_view3d/view3d_view.c index 529e5fcf756..e9cb6dcdc44 100644 --- a/source/blender/editors/space_view3d/view3d_view.c +++ b/source/blender/editors/space_view3d/view3d_view.c @@ -44,6 +44,7 @@ #include "BKE_camera.h" #include "BKE_context.h" #include "BKE_depsgraph.h" +#include "BKE_DerivedMesh.h" #include "BKE_object.h" #include "BKE_global.h" #include "BKE_main.h" @@ -1030,23 +1031,55 @@ static void view3d_select_loop(ViewContext *vc, Scene *scene, View3D *v3d, ARegi lb = object_duplilist(G.main->eval_ctx, scene, base->object); for (dob = lb->first; dob; dob = dob->next) { - float omat[4][4]; + /* for restoring after override */ + DupliObjectData *dob_data = NULL; + DerivedMesh *store_final_dm; + float store_obmat[4][4]; tbase.object = dob->ob; - copy_m4_m4(omat, dob->ob->obmat); + copy_m4_m4(store_obmat, dob->ob->obmat); copy_m4_m4(dob->ob->obmat, dob->mat); + store_final_dm = dob->ob->derivedFinal; /* extra service: draw the duplicator in drawtype of parent */ /* MIN2 for the drawtype to allow bounding box objects in groups for lods */ dt = tbase.object->dt; tbase.object->dt = MIN2(tbase.object->dt, base->object->dt); dtx = tbase.object->dtx; tbase.object->dtx = base->object->dtx; + /* override final DM */ + tbase.object->transflag &= ~OB_IS_DUPLI_CACHE; + if (base->object->dup_cache) { + dob_data = BKE_dupli_cache_find_data(base->object->dup_cache, tbase.object); + if (dob_data && dob_data->dm) { + tbase.object->transflag |= OB_IS_DUPLI_CACHE; + + tbase.object->derivedFinal = dob_data->dm; + } + } + draw_object(scene, ar, v3d, &tbase, DRAW_PICKING | DRAW_CONSTCOLOR); + /* restore final DM */ + if (tbase.object->transflag & OB_IS_DUPLI_CACHE) { + DerivedMesh *cur = tbase.object->derivedFinal; + + /* in some cases drawing code can recreate the derivedFinal, + * make sure we free those first before restoring + */ + if (cur && cur != dob_data->dm) { + cur->needsFree = 1; + cur->release(cur); + } + + tbase.object->transflag &= ~OB_IS_DUPLI_CACHE; + tbase.object->derivedFinal = store_final_dm; + } + tbase.object->dt = dt; tbase.object->dtx = dtx; - copy_m4_m4(dob->ob->obmat, omat); + /* restore obmat and final DM */ + copy_m4_m4(dob->ob->obmat, store_obmat); } free_object_duplilist(lb); } diff --git a/source/blender/editors/transform/SConscript b/source/blender/editors/transform/SConscript index 1a2e9ab074a..d1931399055 100644 --- a/source/blender/editors/transform/SConscript +++ b/source/blender/editors/transform/SConscript @@ -28,6 +28,7 @@ Import ('env') sources = env.Glob('*.c') +sources.remove('manipulator_widget.c') incs = [ '#/intern/guardedalloc', diff --git a/source/blender/editors/transform/manipulator_widget.c b/source/blender/editors/transform/manipulator_widget.c new file mode 100644 index 00000000000..eff9fda4378 --- /dev/null +++ b/source/blender/editors/transform/manipulator_widget.c @@ -0,0 +1,2082 @@ +/* + * ***** 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2005 Blender Foundation + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): none yet. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/editors/transform/transform_manipulator.c + * \ingroup edtransform + */ + + +#include <stdlib.h> +#include <string.h> +#include <math.h> +#include <float.h> + +#include "DNA_armature_types.h" +#include "DNA_curve_types.h" +#include "DNA_lattice_types.h" +#include "DNA_meta_types.h" +#include "DNA_screen_types.h" +#include "DNA_scene_types.h" +#include "DNA_view3d_types.h" + +#include "BLI_math.h" +#include "BLI_utildefines.h" +#include "BLI_listbase.h" + +#include "RNA_access.h" + +#include "BKE_action.h" +#include "BKE_context.h" +#include "BKE_curve.h" +#include "BKE_global.h" +#include "BKE_particle.h" +#include "BKE_pointcache.h" +#include "BKE_editmesh.h" +#include "BKE_lattice.h" + +#include "BIF_gl.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "ED_armature.h" +#include "ED_curve.h" +#include "ED_particle.h" +#include "ED_view3d.h" +#include "ED_screen.h" + +#include "UI_resources.h" +#include "UI_interface.h" + +/* local module include */ +#include "transform.h" + +#include "MEM_guardedalloc.h" + +#include "GPU_select.h" + +/* drawing flags */ + +#define MAN_TRANS_X (1 << 0) +#define MAN_TRANS_Y (1 << 1) +#define MAN_TRANS_Z (1 << 2) +#define MAN_TRANS_C (MAN_TRANS_X | MAN_TRANS_Y | MAN_TRANS_Z) + +#define MAN_ROT_X (1 << 3) +#define MAN_ROT_Y (1 << 4) +#define MAN_ROT_Z (1 << 5) +#define MAN_ROT_V (1 << 6) +#define MAN_ROT_T (1 << 7) +#define MAN_ROT_C (MAN_ROT_X | MAN_ROT_Y | MAN_ROT_Z | MAN_ROT_V | MAN_ROT_T) + +#define MAN_SCALE_X (1 << 8) +#define MAN_SCALE_Y (1 << 9) +#define MAN_SCALE_Z (1 << 10) +#define MAN_SCALE_C (MAN_SCALE_X | MAN_SCALE_Y | MAN_SCALE_Z) + +/* return codes for select */ +enum { + MAN_SEL_TRANS_X = 0, + MAN_SEL_TRANS_Y, + MAN_SEL_TRANS_Z, + + MAN_SEL_ROT_X, + MAN_SEL_ROT_Y, + MAN_SEL_ROT_Z, + MAN_SEL_ROT_V, + MAN_SEL_ROT_T, + + MAN_SEL_SCALE_X, + MAN_SEL_SCALE_Y, + MAN_SEL_SCALE_Z, + + /* those two stay at the end so the rest can be inferred with bitshifting */ + MAN_SEL_SCALE_C, + MAN_SEL_TRANS_C, + + MAN_SEL_MAX +}; + +/* color codes */ + +#define MAN_RGB 0 +#define MAN_GHOST 1 +#define MAN_MOVECOL 2 + +/* threshold for testing view aligned manipulator axis */ +#define TW_AXIS_DOT_MIN 0.02f +#define TW_AXIS_DOT_MAX 0.1f + +/* transform widget center calc helper for below */ +static void calc_tw_center(Scene *scene, const float co[3]) +{ + float *twcent = scene->twcent; + float *min = scene->twmin; + float *max = scene->twmax; + + minmax_v3v3_v3(min, max, co); + add_v3_v3(twcent, co); +} + +static void protectflag_to_drawflags(short protectflag, short *drawflags) +{ + if (protectflag & OB_LOCK_LOCX) + *drawflags &= ~MAN_TRANS_X; + if (protectflag & OB_LOCK_LOCY) + *drawflags &= ~MAN_TRANS_Y; + if (protectflag & OB_LOCK_LOCZ) + *drawflags &= ~MAN_TRANS_Z; + + if (protectflag & OB_LOCK_ROTX) + *drawflags &= ~MAN_ROT_X; + if (protectflag & OB_LOCK_ROTY) + *drawflags &= ~MAN_ROT_Y; + if (protectflag & OB_LOCK_ROTZ) + *drawflags &= ~MAN_ROT_Z; + + if (protectflag & OB_LOCK_SCALEX) + *drawflags &= ~MAN_SCALE_X; + if (protectflag & OB_LOCK_SCALEY) + *drawflags &= ~MAN_SCALE_Y; + if (protectflag & OB_LOCK_SCALEZ) + *drawflags &= ~MAN_SCALE_Z; +} + +/* for pose mode */ +static void stats_pose(Scene *scene, RegionView3D *rv3d, bPoseChannel *pchan) +{ + Bone *bone = pchan->bone; + + if (bone) { + calc_tw_center(scene, pchan->pose_head); + protectflag_to_drawflags(pchan->protectflag, &rv3d->twdrawflag); + } +} + +/* for editmode*/ +static void stats_editbone(RegionView3D *rv3d, EditBone *ebo) +{ + if (ebo->flag & BONE_EDITMODE_LOCKED) + protectflag_to_drawflags(OB_LOCK_LOC | OB_LOCK_ROT | OB_LOCK_SCALE, &rv3d->twdrawflag); +} + +/* could move into BLI_math however this is only useful for display/editing purposes */ +static void axis_angle_to_gimbal_axis(float gmat[3][3], const float axis[3], const float angle) +{ + /* X/Y are arbitrary axies, most importantly Z is the axis of rotation */ + + float cross_vec[3]; + float quat[4]; + + /* this is an un-scientific method to get a vector to cross with + * XYZ intentionally YZX */ + cross_vec[0] = axis[1]; + cross_vec[1] = axis[2]; + cross_vec[2] = axis[0]; + + /* X-axis */ + cross_v3_v3v3(gmat[0], cross_vec, axis); + normalize_v3(gmat[0]); + axis_angle_to_quat(quat, axis, angle); + mul_qt_v3(quat, gmat[0]); + + /* Y-axis */ + axis_angle_to_quat(quat, axis, M_PI / 2.0); + copy_v3_v3(gmat[1], gmat[0]); + mul_qt_v3(quat, gmat[1]); + + /* Z-axis */ + copy_v3_v3(gmat[2], axis); + + normalize_m3(gmat); +} + + +static int test_rotmode_euler(short rotmode) +{ + return (ELEM(rotmode, ROT_MODE_AXISANGLE, ROT_MODE_QUAT)) ? 0 : 1; +} + +bool gimbal_axis(Object *ob, float gmat[3][3]) +{ + if (ob) { + if (ob->mode & OB_MODE_POSE) { + bPoseChannel *pchan = BKE_pose_channel_active(ob); + + if (pchan) { + float mat[3][3], tmat[3][3], obmat[3][3]; + if (test_rotmode_euler(pchan->rotmode)) { + eulO_to_gimbal_axis(mat, pchan->eul, pchan->rotmode); + } + else if (pchan->rotmode == ROT_MODE_AXISANGLE) { + axis_angle_to_gimbal_axis(mat, pchan->rotAxis, pchan->rotAngle); + } + else { /* quat */ + return 0; + } + + + /* apply bone transformation */ + mul_m3_m3m3(tmat, pchan->bone->bone_mat, mat); + + if (pchan->parent) { + float parent_mat[3][3]; + + copy_m3_m4(parent_mat, pchan->parent->pose_mat); + mul_m3_m3m3(mat, parent_mat, tmat); + + /* needed if object transformation isn't identity */ + copy_m3_m4(obmat, ob->obmat); + mul_m3_m3m3(gmat, obmat, mat); + } + else { + /* needed if object transformation isn't identity */ + copy_m3_m4(obmat, ob->obmat); + mul_m3_m3m3(gmat, obmat, tmat); + } + + normalize_m3(gmat); + return 1; + } + } + else { + if (test_rotmode_euler(ob->rotmode)) { + eulO_to_gimbal_axis(gmat, ob->rot, ob->rotmode); + } + else if (ob->rotmode == ROT_MODE_AXISANGLE) { + axis_angle_to_gimbal_axis(gmat, ob->rotAxis, ob->rotAngle); + } + else { /* quat */ + return 0; + } + + if (ob->parent) { + float parent_mat[3][3]; + copy_m3_m4(parent_mat, ob->parent->obmat); + normalize_m3(parent_mat); + mul_m3_m3m3(gmat, parent_mat, gmat); + } + return 1; + } + } + + return 0; +} + + +/* centroid, boundbox, of selection */ +/* returns total items selected */ +static int calc_manipulator_stats(const bContext *C) +{ + ScrArea *sa = CTX_wm_area(C); + ARegion *ar = CTX_wm_region(C); + Scene *scene = CTX_data_scene(C); + Object *obedit = CTX_data_edit_object(C); + ToolSettings *ts = CTX_data_tool_settings(C); + View3D *v3d = sa->spacedata.first; + RegionView3D *rv3d = ar->regiondata; + Base *base; + Object *ob = OBACT; + int a, totsel = 0; + + /* transform widget matrix */ + unit_m4(rv3d->twmat); + + rv3d->twdrawflag = 0xFFFF; + + /* transform widget centroid/center */ + INIT_MINMAX(scene->twmin, scene->twmax); + zero_v3(scene->twcent); + + if (obedit) { + ob = obedit; + if ((ob->lay & v3d->lay) == 0) return 0; + + if (obedit->type == OB_MESH) { + BMEditMesh *em = BKE_editmesh_from_object(obedit); + BMEditSelection ese; + float vec[3] = {0, 0, 0}; + + /* USE LAST SELECTE WITH ACTIVE */ + if ((v3d->around == V3D_ACTIVE) && BM_select_history_active_get(em->bm, &ese)) { + BM_editselection_center(&ese, vec); + calc_tw_center(scene, vec); + totsel = 1; + } + else { + BMesh *bm = em->bm; + BMVert *eve; + + BMIter iter; + + /* do vertices/edges/faces for center depending on selection + * mode. note we can't use just vertex selection flag because + * it is not flush down on changes */ + if (ts->selectmode & SCE_SELECT_VERTEX) { + BM_ITER_MESH (eve, &iter, bm, BM_VERTS_OF_MESH) { + if (!BM_elem_flag_test(eve, BM_ELEM_HIDDEN)) { + if (BM_elem_flag_test(eve, BM_ELEM_SELECT)) { + totsel++; + calc_tw_center(scene, eve->co); + } + } + } + } + else if (ts->selectmode & SCE_SELECT_EDGE) { + BMIter itersub; + BMEdge *eed; + BM_ITER_MESH (eve, &iter, bm, BM_VERTS_OF_MESH) { + if (!BM_elem_flag_test(eve, BM_ELEM_HIDDEN)) { + /* check the vertex has a selected edge, only add it once */ + BM_ITER_ELEM (eed, &itersub, eve, BM_EDGES_OF_VERT) { + if (BM_elem_flag_test(eed, BM_ELEM_SELECT)) { + totsel++; + calc_tw_center(scene, eve->co); + break; + } + } + } + } + } + else { + BMIter itersub; + BMFace *efa; + BM_ITER_MESH (eve, &iter, bm, BM_VERTS_OF_MESH) { + if (!BM_elem_flag_test(eve, BM_ELEM_HIDDEN)) { + /* check the vertex has a selected face, only add it once */ + BM_ITER_ELEM (efa, &itersub, eve, BM_FACES_OF_VERT) { + if (BM_elem_flag_test(efa, BM_ELEM_SELECT)) { + totsel++; + calc_tw_center(scene, eve->co); + break; + } + } + } + } + } + } + } /* end editmesh */ + else if (obedit->type == OB_ARMATURE) { + bArmature *arm = obedit->data; + EditBone *ebo; + + if ((v3d->around == V3D_ACTIVE) && (ebo = arm->act_edbone)) { + /* doesn't check selection or visibility intentionally */ + if (ebo->flag & BONE_TIPSEL) { + calc_tw_center(scene, ebo->tail); + totsel++; + } + if ((ebo->flag & BONE_ROOTSEL) || + ((ebo->flag & BONE_TIPSEL) == false)) /* ensure we get at least one point */ + { + calc_tw_center(scene, ebo->head); + totsel++; + } + stats_editbone(rv3d, ebo); + } + else { + for (ebo = arm->edbo->first; ebo; ebo = ebo->next) { + if (EBONE_VISIBLE(arm, ebo)) { + if (ebo->flag & BONE_TIPSEL) { + calc_tw_center(scene, ebo->tail); + totsel++; + } + if (ebo->flag & BONE_ROOTSEL) { + calc_tw_center(scene, ebo->head); + totsel++; + } + if (ebo->flag & BONE_SELECTED) { + stats_editbone(rv3d, ebo); + } + } + } + } + } + else if (ELEM(obedit->type, OB_CURVE, OB_SURF)) { + Curve *cu = obedit->data; + float center[3]; + + if (v3d->around == V3D_ACTIVE && ED_curve_active_center(cu, center)) { + calc_tw_center(scene, center); + totsel++; + } + else { + Nurb *nu; + BezTriple *bezt; + BPoint *bp; + ListBase *nurbs = BKE_curve_editNurbs_get(cu); + + nu = nurbs->first; + while (nu) { + if (nu->type == CU_BEZIER) { + bezt = nu->bezt; + a = nu->pntsu; + while (a--) { + /* exceptions + * if handles are hidden then only check the center points. + * If the center knot is selected then only use this as the center point. + */ + if (cu->drawflag & CU_HIDE_HANDLES) { + if (bezt->f2 & SELECT) { + calc_tw_center(scene, bezt->vec[1]); + totsel++; + } + } + else if (bezt->f2 & SELECT) { + calc_tw_center(scene, bezt->vec[1]); + totsel++; + } + else { + if (bezt->f1 & SELECT) { + calc_tw_center(scene, bezt->vec[(v3d->around == V3D_LOCAL) ? 1 : 0]); + totsel++; + } + if (bezt->f3 & SELECT) { + calc_tw_center(scene, bezt->vec[(v3d->around == V3D_LOCAL) ? 1 : 2]); + totsel++; + } + } + bezt++; + } + } + else { + bp = nu->bp; + a = nu->pntsu * nu->pntsv; + while (a--) { + if (bp->f1 & SELECT) { + calc_tw_center(scene, bp->vec); + totsel++; + } + bp++; + } + } + nu = nu->next; + } + } + } + else if (obedit->type == OB_MBALL) { + MetaBall *mb = (MetaBall *)obedit->data; + MetaElem *ml; + + if ((v3d->around == V3D_ACTIVE) && (ml = mb->lastelem)) { + calc_tw_center(scene, &ml->x); + totsel++; + } + else { + for (ml = mb->editelems->first; ml; ml = ml->next) { + if (ml->flag & SELECT) { + calc_tw_center(scene, &ml->x); + totsel++; + } + } + } + } + else if (obedit->type == OB_LATTICE) { + Lattice *lt = ((Lattice *)obedit->data)->editlatt->latt; + BPoint *bp; + + if ((v3d->around == V3D_ACTIVE) && (bp = BKE_lattice_active_point_get(lt))) { + calc_tw_center(scene, bp->vec); + totsel++; + } + else { + bp = lt->def; + a = lt->pntsu * lt->pntsv * lt->pntsw; + while (a--) { + if (bp->f1 & SELECT) { + calc_tw_center(scene, bp->vec); + totsel++; + } + bp++; + } + } + } + + /* selection center */ + if (totsel) { + mul_v3_fl(scene->twcent, 1.0f / (float)totsel); // centroid! + mul_m4_v3(obedit->obmat, scene->twcent); + mul_m4_v3(obedit->obmat, scene->twmin); + mul_m4_v3(obedit->obmat, scene->twmax); + } + } + else if (ob && (ob->mode & OB_MODE_POSE)) { + bPoseChannel *pchan; + int mode = TFM_ROTATION; // mislead counting bones... bah. We don't know the manipulator mode, could be mixed + bool ok = false; + + if ((ob->lay & v3d->lay) == 0) return 0; + + if ((v3d->around == V3D_ACTIVE) && (pchan = BKE_pose_channel_active(ob))) { + /* doesn't check selection or visibility intentionally */ + Bone *bone = pchan->bone; + if (bone) { + stats_pose(scene, rv3d, pchan); + totsel = 1; + ok = true; + } + } + else { + totsel = count_set_pose_transflags(&mode, 0, ob); + + if (totsel) { + /* use channels to get stats */ + for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) { + Bone *bone = pchan->bone; + if (bone && (bone->flag & BONE_TRANSFORM)) { + stats_pose(scene, rv3d, pchan); + } + } + ok = true; + } + } + + if (ok) { + mul_v3_fl(scene->twcent, 1.0f / (float)totsel); // centroid! + mul_m4_v3(ob->obmat, scene->twcent); + mul_m4_v3(ob->obmat, scene->twmin); + mul_m4_v3(ob->obmat, scene->twmax); + } + } + else if (ob && (ob->mode & OB_MODE_ALL_PAINT)) { + /* pass */ + } + else if (ob && ob->mode & OB_MODE_PARTICLE_EDIT) { + PTCacheEdit *edit = PE_get_current(scene, ob); + PTCacheEditPoint *point; + PTCacheEditKey *ek; + int k; + + if (edit) { + point = edit->points; + for (a = 0; a < edit->totpoint; a++, point++) { + if (point->flag & PEP_HIDE) continue; + + for (k = 0, ek = point->keys; k < point->totkey; k++, ek++) { + if (ek->flag & PEK_SELECT) { + calc_tw_center(scene, ek->flag & PEK_USE_WCO ? ek->world_co : ek->co); + totsel++; + } + } + } + + /* selection center */ + if (totsel) + mul_v3_fl(scene->twcent, 1.0f / (float)totsel); // centroid! + } + } + else { + + /* we need the one selected object, if its not active */ + ob = OBACT; + if (ob && !(ob->flag & SELECT)) ob = NULL; + + for (base = scene->base.first; base; base = base->next) { + if (TESTBASELIB(v3d, base)) { + if (ob == NULL) + ob = base->object; + calc_tw_center(scene, base->object->obmat[3]); + protectflag_to_drawflags(base->object->protectflag, &rv3d->twdrawflag); + totsel++; + } + } + + /* selection center */ + if (totsel) { + mul_v3_fl(scene->twcent, 1.0f / (float)totsel); // centroid! + } + } + + /* global, local or normal orientation? */ + if (ob && totsel) { + + switch (v3d->twmode) { + + case V3D_MANIP_GLOBAL: + { + break; /* nothing to do */ + } + case V3D_MANIP_GIMBAL: + { + float mat[3][3]; + if (gimbal_axis(ob, mat)) { + copy_m4_m3(rv3d->twmat, mat); + break; + } + /* if not gimbal, fall through to normal */ + /* fall-through */ + } + case V3D_MANIP_NORMAL: + { + if (obedit || ob->mode & OB_MODE_POSE) { + float mat[3][3]; + ED_getTransformOrientationMatrix(C, mat, (v3d->around == V3D_ACTIVE)); + copy_m4_m3(rv3d->twmat, mat); + break; + } + /* no break we define 'normal' as 'local' in Object mode */ + /* fall-through */ + } + case V3D_MANIP_LOCAL: + { + if (ob->mode & OB_MODE_POSE) { + /* each bone moves on its own local axis, but to avoid confusion, + * use the active pones axis for display [#33575], this works as expected on a single bone + * and users who select many bones will understand whats going on and what local means + * when they start transforming */ + float mat[3][3]; + ED_getTransformOrientationMatrix(C, mat, (v3d->around == V3D_ACTIVE)); + copy_m4_m3(rv3d->twmat, mat); + break; + } + copy_m4_m4(rv3d->twmat, ob->obmat); + normalize_m4(rv3d->twmat); + break; + } + case V3D_MANIP_VIEW: + { + float mat[3][3]; + copy_m3_m4(mat, rv3d->viewinv); + normalize_m3(mat); + copy_m4_m3(rv3d->twmat, mat); + break; + } + default: /* V3D_MANIP_CUSTOM */ + { + float mat[3][3]; + if (applyTransformOrientation(C, mat, NULL)) { + copy_m4_m3(rv3d->twmat, mat); + } + break; + } + } + + } + + return totsel; +} + +/* don't draw axis perpendicular to the view */ +static void test_manipulator_axis(const bContext *C) +{ + RegionView3D *rv3d = CTX_wm_region_view3d(C); + float view_vec[3], axis_vec[3]; + float idot; + int i; + + const int twdrawflag_axis[3] = { + (MAN_TRANS_X | MAN_SCALE_X), + (MAN_TRANS_Y | MAN_SCALE_Y), + (MAN_TRANS_Z | MAN_SCALE_Z)}; + + ED_view3d_global_to_vector(rv3d, rv3d->twmat[3], view_vec); + + for (i = 0; i < 3; i++) { + normalize_v3_v3(axis_vec, rv3d->twmat[i]); + rv3d->tw_idot[i] = idot = 1.0f - fabsf(dot_v3v3(view_vec, axis_vec)); + if (idot < TW_AXIS_DOT_MIN) { + rv3d->twdrawflag &= ~twdrawflag_axis[i]; + } + } +} + + +/* ******************** DRAWING STUFFIES *********** */ + +static float screen_aligned(RegionView3D *rv3d, float mat[4][4]) +{ + glTranslatef(mat[3][0], mat[3][1], mat[3][2]); + + /* sets view screen aligned */ + glRotatef(-360.0f * saacos(rv3d->viewquat[0]) / (float)M_PI, rv3d->viewquat[1], rv3d->viewquat[2], rv3d->viewquat[3]); + + return len_v3(mat[0]); /* draw scale */ +} + + +/* radring = radius of doughnut rings + * radhole = radius hole + * start = starting segment (based on nrings) + * end = end segment + * nsides = amount of points in ring + * nrigns = amount of rings + */ +static void partial_doughnut(float radring, float radhole, int start, int end, int nsides, int nrings) +{ + float theta, phi, theta1; + float cos_theta, sin_theta; + float cos_theta1, sin_theta1; + float ring_delta, side_delta; + int i, j, do_caps = true; + + if (start == 0 && end == nrings) do_caps = false; + + ring_delta = 2.0f * (float)M_PI / (float)nrings; + side_delta = 2.0f * (float)M_PI / (float)nsides; + + theta = (float)M_PI + 0.5f * ring_delta; + cos_theta = cosf(theta); + sin_theta = sinf(theta); + + for (i = nrings - 1; i >= 0; i--) { + theta1 = theta + ring_delta; + cos_theta1 = cosf(theta1); + sin_theta1 = sinf(theta1); + + if (do_caps && i == start) { // cap + glBegin(GL_POLYGON); + phi = 0.0; + for (j = nsides; j >= 0; j--) { + float cos_phi, sin_phi, dist; + + phi += side_delta; + cos_phi = cosf(phi); + sin_phi = sinf(phi); + dist = radhole + radring * cos_phi; + + glVertex3f(cos_theta1 * dist, -sin_theta1 * dist, radring * sin_phi); + } + glEnd(); + } + if (i >= start && i <= end) { + glBegin(GL_QUAD_STRIP); + phi = 0.0; + for (j = nsides; j >= 0; j--) { + float cos_phi, sin_phi, dist; + + phi += side_delta; + cos_phi = cosf(phi); + sin_phi = sinf(phi); + dist = radhole + radring * cos_phi; + + glVertex3f(cos_theta1 * dist, -sin_theta1 * dist, radring * sin_phi); + glVertex3f(cos_theta * dist, -sin_theta * dist, radring * sin_phi); + } + glEnd(); + } + + if (do_caps && i == end) { // cap + glBegin(GL_POLYGON); + phi = 0.0; + for (j = nsides; j >= 0; j--) { + float cos_phi, sin_phi, dist; + + phi -= side_delta; + cos_phi = cosf(phi); + sin_phi = sinf(phi); + dist = radhole + radring * cos_phi; + + glVertex3f(cos_theta * dist, -sin_theta * dist, radring * sin_phi); + } + glEnd(); + } + + + theta = theta1; + cos_theta = cos_theta1; + sin_theta = sin_theta1; + } +} + +static char axisBlendAngle(float idot) +{ + if (idot > TW_AXIS_DOT_MAX) { + return 255; + } + else if (idot < TW_AXIS_DOT_MIN) { + return 0; + } + else { + return (char)(255.0f * (idot - TW_AXIS_DOT_MIN) / (TW_AXIS_DOT_MAX - TW_AXIS_DOT_MIN)); + } +} + +/* three colors can be set: + * gray for ghosting + * moving: in transform theme color + * else the red/green/blue + */ +static void manipulator_setcolor(View3D *v3d, char axis, int colcode, unsigned char alpha, bool highlight) +{ + unsigned char col[4] = {0}; + int offset = (highlight) ? 80 : 0; + col[3] = alpha; + + if (colcode == MAN_GHOST) { + col[3] = 70; + } + else if (colcode == MAN_MOVECOL) { + UI_GetThemeColor3ubv(TH_TRANSFORM, col); + } + else { + switch (axis) { + case 'C': + UI_GetThemeColor3ubv(TH_TRANSFORM, col); + if (v3d->twmode == V3D_MANIP_LOCAL) { + col[0] = col[0] > 200 ? 255 : col[0] + 55; + col[1] = col[1] > 200 ? 255 : col[1] + 55; + col[2] = col[2] > 200 ? 255 : col[2] + 55; + } + else if (v3d->twmode == V3D_MANIP_NORMAL) { + col[0] = col[0] < 55 ? 0 : col[0] - 55; + col[1] = col[1] < 55 ? 0 : col[1] - 55; + col[2] = col[2] < 55 ? 0 : col[2] - 55; + } + break; + case 'X': + UI_GetThemeColorShade3ubv(TH_AXIS_X, offset, col); + break; + case 'Y': + UI_GetThemeColorShade3ubv(TH_AXIS_Y, offset, col); + break; + case 'Z': + UI_GetThemeColorShade3ubv(TH_AXIS_Z, offset, col); + break; + default: + BLI_assert(0); + break; + } + } + + glColor4ubv(col); +} + +static void manipulator_axis_order(RegionView3D *rv3d, int r_axis_order[3]) +{ + float axis_values[3]; + float vec[3]; + + ED_view3d_global_to_vector(rv3d, rv3d->twmat[3], vec); + + axis_values[0] = -dot_v3v3(rv3d->twmat[0], vec); + axis_values[1] = -dot_v3v3(rv3d->twmat[1], vec); + axis_values[2] = -dot_v3v3(rv3d->twmat[2], vec); + + axis_sort_v3(axis_values, r_axis_order); +} + +/* viewmatrix should have been set OK, also no shademode! */ +static void draw_manipulator_axes_single(View3D *v3d, RegionView3D *rv3d, int colcode, + int flagx, int flagy, int flagz, int axis, + const int selectionbase, int highlight) +{ + switch (axis) { + case 0: + /* axes */ + if (flagx) { + if (selectionbase != -1) { + if (flagx & MAN_SCALE_X) GPU_select_load_id(selectionbase); + else if (flagx & MAN_TRANS_X) GPU_select_load_id(selectionbase); + } + else { + manipulator_setcolor(v3d, 'X', colcode, axisBlendAngle(rv3d->tw_idot[0]), (highlight & (MAN_TRANS_X | MAN_SCALE_X)) != 0); + } + glBegin(GL_LINES); + glVertex3f(0.2f, 0.0f, 0.0f); + glVertex3f(1.0f, 0.0f, 0.0f); + glEnd(); + } + break; + case 1: + if (flagy) { + if (selectionbase != -1) { + if (flagy & MAN_SCALE_Y) GPU_select_load_id(selectionbase); + else if (flagy & MAN_TRANS_Y) GPU_select_load_id(selectionbase); + } + else { + manipulator_setcolor(v3d, 'Y', colcode, axisBlendAngle(rv3d->tw_idot[1]), (highlight & (MAN_TRANS_Y | MAN_SCALE_Y)) != 0); + } + glBegin(GL_LINES); + glVertex3f(0.0f, 0.2f, 0.0f); + glVertex3f(0.0f, 1.0f, 0.0f); + glEnd(); + } + break; + case 2: + if (flagz) { + if (selectionbase != -1) { + if (flagz & MAN_SCALE_Z) GPU_select_load_id(selectionbase); + else if (flagz & MAN_TRANS_Z) GPU_select_load_id(selectionbase); + } + else { + manipulator_setcolor(v3d, 'Z', colcode, axisBlendAngle(rv3d->tw_idot[2]), (highlight & (MAN_TRANS_Z | MAN_SCALE_Z)) != 0); + } + glBegin(GL_LINES); + glVertex3f(0.0f, 0.0f, 0.2f); + glVertex3f(0.0f, 0.0f, 1.0f); + glEnd(); + } + break; + } +} +static void draw_manipulator_axes(View3D *v3d, RegionView3D *rv3d, int colcode, + int flagx, int flagy, int flagz, + const int axis_order[3], const int selectionbase, int highlight) +{ + int i; + for (i = 0; i < 3; i++) { + draw_manipulator_axes_single(v3d, rv3d, colcode, flagx, flagy, flagz, axis_order[i], selectionbase, highlight); + } +} + +static void preOrthoFront(const bool ortho, float twmat[4][4], int axis) +{ + if (ortho == false) { + float omat[4][4]; + copy_m4_m4(omat, twmat); + orthogonalize_m4(omat, axis); + glPushMatrix(); + glMultMatrixf(omat); + glFrontFace(is_negative_m4(omat) ? GL_CW : GL_CCW); + } +} + +static void postOrtho(const bool ortho) +{ + if (ortho == false) { + glPopMatrix(); + } +} + +static void draw_manipulator_rotate( + View3D *v3d, RegionView3D *rv3d, const int drawflags, int highlight, const int combo, + const bool is_moving, const int selectionbase) +{ + double plane[4]; + float matt[4][4]; + float size, unitmat[4][4]; + float cywid = 0.33f * 0.01f * (float)U.tw_handlesize; + float cusize = cywid * 0.65f; + int arcs = (G.debug_value != 2); + const int colcode = (is_moving) ? MAN_MOVECOL : MAN_RGB; + bool ortho; + + /* when called while moving in mixed mode, do not draw when... */ + if ((drawflags & MAN_ROT_C) == 0) return; + + /* Init stuff */ + glDisable(GL_DEPTH_TEST); + unit_m4(unitmat); + + /* prepare for screen aligned draw */ + size = len_v3(rv3d->twmat[0]); + glPushMatrix(); + glTranslatef(rv3d->twmat[3][0], rv3d->twmat[3][1], rv3d->twmat[3][2]); + + if (arcs) { + /* clipplane makes nice handles, calc here because of multmatrix but with translate! */ + copy_v3db_v3fl(plane, rv3d->viewinv[2]); + plane[3] = -0.02f * size; // clip just a bit more + glClipPlane(GL_CLIP_PLANE0, plane); + } + /* sets view screen aligned */ + glRotatef(-360.0f * saacos(rv3d->viewquat[0]) / (float)M_PI, rv3d->viewquat[1], rv3d->viewquat[2], rv3d->viewquat[3]); + + /* Screen aligned help circle */ + if (arcs) { + if (selectionbase == -1) { + UI_ThemeColorShade(TH_BACK, -30); + drawcircball(GL_LINE_LOOP, unitmat[3], size, unitmat); + } + } + + /* Screen aligned trackball rot circle */ + if (drawflags & MAN_ROT_T) { + if (selectionbase != -1) GPU_select_load_id(selectionbase); + else UI_ThemeColor(TH_TRANSFORM); + + drawcircball(GL_LINE_LOOP, unitmat[3], 0.2f * size, unitmat); + } + + /* Screen aligned view rot circle */ + if (drawflags & MAN_ROT_V) { + if (selectionbase != -1) GPU_select_load_id(selectionbase); + else UI_ThemeColor(TH_TRANSFORM); + drawcircball(GL_LINE_LOOP, unitmat[3], 1.2f * size, unitmat); + + if (is_moving) { + float vec[3]; + vec[0] = 0; // XXX (float)(t->imval[0] - t->center2d[0]); + vec[1] = 0; // XXX (float)(t->imval[1] - t->center2d[1]); + vec[2] = 0.0f; + normalize_v3(vec); + mul_v3_fl(vec, 1.2f * size); + glBegin(GL_LINES); + glVertex3f(0.0f, 0.0f, 0.0f); + glVertex3fv(vec); + glEnd(); + } + } + glPopMatrix(); + + + ortho = is_orthogonal_m4(rv3d->twmat); + + /* apply the transform delta */ + if (is_moving) { + copy_m4_m4(matt, rv3d->twmat); // to copy the parts outside of [3][3] + // XXX mul_m4_m3m4(matt, t->mat, rv3d->twmat); + if (ortho) { + glMultMatrixf(matt); + glFrontFace(is_negative_m4(matt) ? GL_CW : GL_CCW); + } + } + else { + if (ortho) { + glFrontFace(is_negative_m4(rv3d->twmat) ? GL_CW : GL_CCW); + glMultMatrixf(rv3d->twmat); + } + } + + /* axes */ + if (arcs == 0) { + if (selectionbase == -1) { + if ((combo & V3D_MANIP_SCALE) == 0) { + /* axis */ + if ((drawflags & MAN_ROT_X) || (is_moving && (drawflags & MAN_ROT_Z))) { + preOrthoFront(ortho, rv3d->twmat, 2); + manipulator_setcolor(v3d, 'X', colcode, 255, (highlight & MAN_ROT_X) != 0); + glBegin(GL_LINES); + glVertex3f(0.2f, 0.0f, 0.0f); + glVertex3f(1.0f, 0.0f, 0.0f); + glEnd(); + postOrtho(ortho); + } + if ((drawflags & MAN_ROT_Y) || (is_moving && (drawflags & MAN_ROT_X))) { + preOrthoFront(ortho, rv3d->twmat, 0); + manipulator_setcolor(v3d, 'Y', colcode, 255, (highlight & MAN_ROT_Y) != 0); + glBegin(GL_LINES); + glVertex3f(0.0f, 0.2f, 0.0f); + glVertex3f(0.0f, 1.0f, 0.0f); + glEnd(); + postOrtho(ortho); + } + if ((drawflags & MAN_ROT_Z) || (is_moving && (drawflags & MAN_ROT_Y))) { + preOrthoFront(ortho, rv3d->twmat, 1); + manipulator_setcolor(v3d, 'Z', colcode, 255, (highlight & MAN_ROT_Y) != 0); + glBegin(GL_LINES); + glVertex3f(0.0f, 0.0f, 0.2f); + glVertex3f(0.0f, 0.0f, 1.0f); + glEnd(); + postOrtho(ortho); + } + } + } + } + + if (arcs == 0 && is_moving) { + + /* Z circle */ + if (drawflags & MAN_ROT_Z) { + preOrthoFront(ortho, matt, 2); + if (selectionbase != -1) GPU_select_load_id(selectionbase); + else manipulator_setcolor(v3d, 'Z', colcode, 255, (highlight & MAN_ROT_Z) != 0); + drawcircball(GL_LINE_LOOP, unitmat[3], 1.0, unitmat); + postOrtho(ortho); + } + /* X circle */ + if (drawflags & MAN_ROT_X) { + preOrthoFront(ortho, matt, 0); + if (selectionbase != -1) GPU_select_load_id(selectionbase); + else manipulator_setcolor(v3d, 'X', colcode, 255, (highlight & MAN_ROT_X) != 0); + glRotatef(90.0, 0.0, 1.0, 0.0); + drawcircball(GL_LINE_LOOP, unitmat[3], 1.0, unitmat); + glRotatef(-90.0, 0.0, 1.0, 0.0); + postOrtho(ortho); + } + /* Y circle */ + if (drawflags & MAN_ROT_Y) { + preOrthoFront(ortho, matt, 1); + if (selectionbase != -1) GPU_select_load_id(selectionbase); + else manipulator_setcolor(v3d, 'Y', colcode, 255, (highlight & MAN_ROT_Y) != 0); + glRotatef(-90.0, 1.0, 0.0, 0.0); + drawcircball(GL_LINE_LOOP, unitmat[3], 1.0, unitmat); + glRotatef(90.0, 1.0, 0.0, 0.0); + postOrtho(ortho); + } + + if (arcs) glDisable(GL_CLIP_PLANE0); + } + // donut arcs + if (arcs) { + glEnable(GL_CLIP_PLANE0); + + /* Z circle */ + if (drawflags & MAN_ROT_Z) { + preOrthoFront(ortho, rv3d->twmat, 2); + if (selectionbase != -1) GPU_select_load_id(selectionbase); + else manipulator_setcolor(v3d, 'Z', colcode, 255, (highlight & MAN_ROT_Z) != 0); + partial_doughnut(cusize / 4.0f, 1.0f, 0, 48, 8, 48); + postOrtho(ortho); + } + /* X circle */ + if (drawflags & MAN_ROT_X) { + preOrthoFront(ortho, rv3d->twmat, 0); + if (selectionbase != -1) GPU_select_load_id(selectionbase); + else manipulator_setcolor(v3d, 'X', colcode, 255, (highlight & MAN_ROT_X) != 0); + glRotatef(90.0, 0.0, 1.0, 0.0); + partial_doughnut(cusize / 4.0f, 1.0f, 0, 48, 8, 48); + glRotatef(-90.0, 0.0, 1.0, 0.0); + postOrtho(ortho); + } + /* Y circle */ + if (drawflags & MAN_ROT_Y) { + preOrthoFront(ortho, rv3d->twmat, 1); + if (selectionbase != -1) GPU_select_load_id(selectionbase); + else manipulator_setcolor(v3d, 'Y', colcode, 255, (highlight & MAN_ROT_Y) != 0); + glRotatef(-90.0, 1.0, 0.0, 0.0); + partial_doughnut(cusize / 4.0f, 1.0f, 0, 48, 8, 48); + glRotatef(90.0, 1.0, 0.0, 0.0); + postOrtho(ortho); + } + + glDisable(GL_CLIP_PLANE0); + } + + if (arcs == 0) { + + /* Z handle on X axis */ + if (drawflags & MAN_ROT_Z) { + preOrthoFront(ortho, rv3d->twmat, 2); + glPushMatrix(); + if (selectionbase != -1) GPU_select_load_id(selectionbase); + else manipulator_setcolor(v3d, 'Z', colcode, 255, (highlight & MAN_ROT_Z) != 0); + + partial_doughnut(0.7f * cusize, 1.0f, 31, 33, 8, 64); + + glPopMatrix(); + postOrtho(ortho); + } + + /* Y handle on X axis */ + if (drawflags & MAN_ROT_Y) { + preOrthoFront(ortho, rv3d->twmat, 1); + glPushMatrix(); + if (selectionbase != -1) GPU_select_load_id(selectionbase); + else manipulator_setcolor(v3d, 'Y', colcode, 255, (highlight & MAN_ROT_Y) != 0); + + glRotatef(90.0, 1.0, 0.0, 0.0); + glRotatef(90.0, 0.0, 0.0, 1.0); + partial_doughnut(0.7f * cusize, 1.0f, 31, 33, 8, 64); + + glPopMatrix(); + postOrtho(ortho); + } + + /* X handle on Z axis */ + if (drawflags & MAN_ROT_X) { + preOrthoFront(ortho, rv3d->twmat, 0); + glPushMatrix(); + if (selectionbase != -1) GPU_select_load_id(selectionbase); + else manipulator_setcolor(v3d, 'X', colcode, 255, (highlight & MAN_ROT_X) != 0); + + glRotatef(-90.0, 0.0, 1.0, 0.0); + glRotatef(90.0, 0.0, 0.0, 1.0); + partial_doughnut(0.7f * cusize, 1.0f, 31, 33, 8, 64); + + glPopMatrix(); + postOrtho(ortho); + } + + } + + /* restore */ + glLoadMatrixf(rv3d->viewmat); + if (v3d->zbuf) glEnable(GL_DEPTH_TEST); + +} + +static void drawsolidcube(float size) +{ + const float cube[8][3] = { + {-1.0, -1.0, -1.0}, + {-1.0, -1.0, 1.0}, + {-1.0, 1.0, 1.0}, + {-1.0, 1.0, -1.0}, + { 1.0, -1.0, -1.0}, + { 1.0, -1.0, 1.0}, + { 1.0, 1.0, 1.0}, + { 1.0, 1.0, -1.0}, + }; + float n[3] = {0.0f}; + + glPushMatrix(); + glScalef(size, size, size); + + glBegin(GL_QUADS); + n[0] = -1.0; + glNormal3fv(n); + glVertex3fv(cube[0]); glVertex3fv(cube[1]); glVertex3fv(cube[2]); glVertex3fv(cube[3]); + n[0] = 0; + glEnd(); + + glBegin(GL_QUADS); + n[1] = -1.0; + glNormal3fv(n); + glVertex3fv(cube[0]); glVertex3fv(cube[4]); glVertex3fv(cube[5]); glVertex3fv(cube[1]); + n[1] = 0; + glEnd(); + + glBegin(GL_QUADS); + n[0] = 1.0; + glNormal3fv(n); + glVertex3fv(cube[4]); glVertex3fv(cube[7]); glVertex3fv(cube[6]); glVertex3fv(cube[5]); + n[0] = 0; + glEnd(); + + glBegin(GL_QUADS); + n[1] = 1.0; + glNormal3fv(n); + glVertex3fv(cube[7]); glVertex3fv(cube[3]); glVertex3fv(cube[2]); glVertex3fv(cube[6]); + n[1] = 0; + glEnd(); + + glBegin(GL_QUADS); + n[2] = 1.0; + glNormal3fv(n); + glVertex3fv(cube[1]); glVertex3fv(cube[5]); glVertex3fv(cube[6]); glVertex3fv(cube[2]); + n[2] = 0; + glEnd(); + + glBegin(GL_QUADS); + n[2] = -1.0; + glNormal3fv(n); + glVertex3fv(cube[7]); glVertex3fv(cube[4]); glVertex3fv(cube[0]); glVertex3fv(cube[3]); + glEnd(); + + glPopMatrix(); +} + + +static void draw_manipulator_scale( + View3D *v3d, RegionView3D *rv3d, const int drawflags, int highlight, const int combo, const int colcode, + const bool is_moving, const int selectionbase) +{ + float cywid = 0.25f * 0.01f * (float)U.tw_handlesize; + float cusize = cywid * 0.75f, dz; + int axis_order[3] = {2, 0, 1}; + int i; + + /* when called while moving in mixed mode, do not draw when... */ + if ((drawflags & MAN_SCALE_C) == 0) return; + + manipulator_axis_order(rv3d, axis_order); + + glDisable(GL_DEPTH_TEST); + + /* not in combo mode */ + if ((combo & (V3D_MANIP_TRANSLATE | V3D_MANIP_ROTATE)) == 0) { + float size, unitmat[4][4]; + int shift = 0; // XXX + + /* center circle, do not add to selection when shift is pressed (planar constraint) */ + if (selectionbase != -1 && shift == 0) GPU_select_load_id(selectionbase); + else manipulator_setcolor(v3d, 'C', colcode, 255, (highlight & MAN_SCALE_C) != 0); + + glPushMatrix(); + size = screen_aligned(rv3d, rv3d->twmat); + unit_m4(unitmat); + drawcircball(GL_LINE_LOOP, unitmat[3], 0.2f * size, unitmat); + glPopMatrix(); + + dz = 1.0; + } + else { + dz = 1.0f - 4.0f * cusize; + } + + if (is_moving) { + float matt[4][4]; + + copy_m4_m4(matt, rv3d->twmat); // to copy the parts outside of [3][3] + // XXX mul_m4_m3m4(matt, t->mat, rv3d->twmat); + glMultMatrixf(matt); + glFrontFace(is_negative_m4(matt) ? GL_CW : GL_CCW); + } + else { + glMultMatrixf(rv3d->twmat); + glFrontFace(is_negative_m4(rv3d->twmat) ? GL_CW : GL_CCW); + } + + /* axis */ + + /* in combo mode, this is always drawn as first type */ + draw_manipulator_axes(v3d, rv3d, colcode, + drawflags & MAN_SCALE_X, drawflags & MAN_SCALE_Y, drawflags & MAN_SCALE_Z, + axis_order, selectionbase, highlight); + + + for (i = 0; i < 3; i++) { + switch (axis_order[i]) { + case 0: /* X cube */ + if (drawflags & MAN_SCALE_X) { + glTranslatef(dz, 0.0, 0.0); + if (selectionbase != -1) GPU_select_load_id(selectionbase); + else manipulator_setcolor(v3d, 'X', colcode, axisBlendAngle(rv3d->tw_idot[0]), (highlight & MAN_SCALE_X) != 0); + drawsolidcube(cusize); + glTranslatef(-dz, 0.0, 0.0); + } + break; + case 1: /* Y cube */ + if (drawflags & MAN_SCALE_Y) { + glTranslatef(0.0, dz, 0.0); + if (selectionbase != -1) GPU_select_load_id(selectionbase); + else manipulator_setcolor(v3d, 'Y', colcode, axisBlendAngle(rv3d->tw_idot[1]), (highlight & MAN_SCALE_Y) != 0); + drawsolidcube(cusize); + glTranslatef(0.0, -dz, 0.0); + } + break; + case 2: /* Z cube */ + if (drawflags & MAN_SCALE_Z) { + glTranslatef(0.0, 0.0, dz); + if (selectionbase != -1) GPU_select_load_id(selectionbase); + else manipulator_setcolor(v3d, 'Z', colcode, axisBlendAngle(rv3d->tw_idot[2]), (highlight & MAN_SCALE_Z) != 0); + drawsolidcube(cusize); + glTranslatef(0.0, 0.0, -dz); + } + break; + } + } + + /* if shiftkey, center point as last, for selectbuffer order */ + if (selectionbase != -1) { + int shift = 0; // XXX + + if (shift) { + glTranslatef(0.0, -dz, 0.0); + GPU_select_load_id(selectionbase); + glBegin(GL_POINTS); + glVertex3f(0.0, 0.0, 0.0); + glEnd(); + } + } + + /* restore */ + glLoadMatrixf(rv3d->viewmat); + + if (v3d->zbuf) glEnable(GL_DEPTH_TEST); + glFrontFace(GL_CCW); +} + + +static void draw_cone(GLUquadricObj *qobj, float len, float width) +{ + glTranslatef(0.0, 0.0, -0.5f * len); + gluCylinder(qobj, width, 0.0, len, 8, 1); + gluQuadricOrientation(qobj, GLU_INSIDE); + gluDisk(qobj, 0.0, width, 8, 1); + gluQuadricOrientation(qobj, GLU_OUTSIDE); + glTranslatef(0.0, 0.0, 0.5f * len); +} + +static void draw_cylinder(GLUquadricObj *qobj, float len, float width) +{ + + width *= 0.8f; // just for beauty + + glTranslatef(0.0, 0.0, -0.5f * len); + gluCylinder(qobj, width, width, len, 8, 1); + gluQuadricOrientation(qobj, GLU_INSIDE); + gluDisk(qobj, 0.0, width, 8, 1); + gluQuadricOrientation(qobj, GLU_OUTSIDE); + glTranslatef(0.0, 0.0, len); + gluDisk(qobj, 0.0, width, 8, 1); + glTranslatef(0.0, 0.0, -0.5f * len); +} + + +static void draw_manipulator_translate( + View3D *v3d, RegionView3D *rv3d, int drawflags, int highlightflags, int combo, int colcode, + const bool UNUSED(is_moving), const int selectionbase) +{ + GLUquadricObj *qobj; + float cylen = 0.01f * (float)U.tw_handlesize; + float cywid = 0.25f * cylen, dz, size; + float unitmat[4][4]; + int shift = 0; // XXX + int axis_order[3] = {0, 1, 2}; + int i; + + /* when called while moving in mixed mode, do not draw when... */ + if ((drawflags & MAN_TRANS_C) == 0) return; + + manipulator_axis_order(rv3d, axis_order); + + // XXX if (moving) glTranslatef(t->vec[0], t->vec[1], t->vec[2]); + glDisable(GL_DEPTH_TEST); + + /* center circle, do not add to selection when shift is pressed (planar constraint) */ + if (selectionbase != -1 && shift == 0) GPU_select_load_id(selectionbase); + else manipulator_setcolor(v3d, 'C', colcode, 255, (highlightflags & MAN_TRANS_C) != 0); + + glPushMatrix(); + size = screen_aligned(rv3d, rv3d->twmat); + unit_m4(unitmat); + drawcircball(GL_LINE_LOOP, unitmat[3], 0.2f * size, unitmat); + glPopMatrix(); + + glPushMatrix(); + /* and now apply matrix, we move to local matrix drawing */ + glMultMatrixf(rv3d->twmat); + +#if 0 + // translate drawn as last, only axis when no combo with scale, or for ghosting + if ((combo & V3D_MANIP_SCALE) == 0 || colcode == MAN_GHOST) { + draw_manipulator_axes(v3d, rv3d, colcode, + drawflags & MAN_TRANS_X, 0, drawflags & MAN_TRANS_Z, + axis_order, selectionbase, highlightflags); + } + + /* offset in combo mode, for rotate a bit more */ + if (combo & (V3D_MANIP_ROTATE)) dz = 1.0f + 2.0f * cylen; + else if (combo & (V3D_MANIP_SCALE)) dz = 1.0f + 0.5f * cylen; + else dz = 1.0f; + + qobj = gluNewQuadric(); + gluQuadricDrawStyle(qobj, GLU_FILL); + + for (i = 0; i < 3; i++) { + switch (axis_order[i]) { + case 0: /* Z Cone */ + if (drawflags & MAN_TRANS_Z) { + glPushMatrix(); + glTranslatef(0.0, 0.0, dz); + if (selectionbase != -1) GPU_select_load_id(selectionbase); + else manipulator_setcolor(v3d, 'Z', colcode, axisBlendAngle(rv3d->tw_idot[2]), (highlightflags & MAN_TRANS_Z) != 0); + draw_cone(qobj, cylen, cywid); + glPopMatrix(); + } + break; + case 1: /* X Cone */ + if (drawflags & MAN_TRANS_X) { + glPushMatrix(); + glTranslatef(dz, 0.0, 0.0); + if (selectionbase != -1) GPU_select_load_id(selectionbase); + else manipulator_setcolor(v3d, 'X', colcode, axisBlendAngle(rv3d->tw_idot[0]), (highlightflags & MAN_TRANS_X) != 0); + glRotatef(90.0, 0.0, 1.0, 0.0); + draw_cone(qobj, cylen, cywid); + glPopMatrix(); + } + break; + case 2: /* Y Cone */ + if (drawflags & MAN_TRANS_Y) { + glPushMatrix(); + glTranslatef(0.0, dz, 0.0); + if (selectionbase != -1) GPU_select_load_id(selectionbase); + else manipulator_setcolor(v3d, 'Y', colcode, axisBlendAngle(rv3d->tw_idot[1]), (highlightflags & MAN_TRANS_Y) != 0); + glRotatef(-90.0, 1.0, 0.0, 0.0); + draw_cone(qobj, cylen, cywid); + glPopMatrix(); + } + break; + } + } + + gluDeleteQuadric(qobj); +#endif + + glPopMatrix(); + + if (v3d->zbuf) glEnable(GL_DEPTH_TEST); + +} + +static void draw_manipulator_rotate_cyl( + View3D *v3d, RegionView3D *rv3d, int drawflags, int highlight, const int combo, const int colcode, + const bool is_moving, const int selectionbase) +{ + GLUquadricObj *qobj; + float size; + float cylen = 0.01f * (float)U.tw_handlesize; + float cywid = 0.25f * cylen; + int axis_order[3] = {2, 0, 1}; + int i; + + /* when called while moving in mixed mode, do not draw when... */ + if ((drawflags & MAN_ROT_C) == 0) return; + + manipulator_axis_order(rv3d, axis_order); + + /* prepare for screen aligned draw */ + glPushMatrix(); + size = screen_aligned(rv3d, rv3d->twmat); + + glDisable(GL_DEPTH_TEST); + + qobj = gluNewQuadric(); + + /* Screen aligned view rot circle */ + if (drawflags & MAN_ROT_V) { + float unitmat[4][4]; + + unit_m4(unitmat); + + if (selectionbase != -1) GPU_select_load_id(selectionbase); + UI_ThemeColor(TH_TRANSFORM); + drawcircball(GL_LINE_LOOP, unitmat[3], 1.2f * size, unitmat); + + if (is_moving) { + float vec[3]; + vec[0] = 0; // XXX (float)(t->imval[0] - t->center2d[0]); + vec[1] = 0; // XXX (float)(t->imval[1] - t->center2d[1]); + vec[2] = 0.0f; + normalize_v3(vec); + mul_v3_fl(vec, 1.2f * size); + glBegin(GL_LINES); + glVertex3f(0.0, 0.0, 0.0); + glVertex3fv(vec); + glEnd(); + } + } + glPopMatrix(); + + /* apply the transform delta */ + if (is_moving) { + float matt[4][4]; + copy_m4_m4(matt, rv3d->twmat); // to copy the parts outside of [3][3] + // XXX if (t->flag & T_USES_MANIPULATOR) { + // XXX mul_m4_m3m4(matt, t->mat, rv3d->twmat); + // XXX } + glMultMatrixf(matt); + } + else { + glMultMatrixf(rv3d->twmat); + } + + glFrontFace(is_negative_m4(rv3d->twmat) ? GL_CW : GL_CCW); + + /* axis */ + if (selectionbase != -1) { + + // only draw axis when combo didn't draw scale axes + if ((combo & V3D_MANIP_SCALE) == 0) { + draw_manipulator_axes(v3d, rv3d, colcode, + drawflags & MAN_ROT_X, drawflags & MAN_ROT_Y, drawflags & MAN_ROT_Z, + axis_order, selectionbase, highlight); + } + + /* only has to be set when not in picking */ + gluQuadricDrawStyle(qobj, GLU_FILL); + } + + for (i = 0; i < 3; i++) { + switch (axis_order[i]) { + case 0: /* X cylinder */ + if (drawflags & MAN_ROT_X) { + glTranslatef(1.0, 0.0, 0.0); + if (selectionbase != -1) GPU_select_load_id(selectionbase); + glRotatef(90.0, 0.0, 1.0, 0.0); + manipulator_setcolor(v3d, 'X', colcode, 255, (highlight & MAN_ROT_X) != 0); + draw_cylinder(qobj, cylen, cywid); + glRotatef(-90.0, 0.0, 1.0, 0.0); + glTranslatef(-1.0, 0.0, 0.0); + } + break; + case 1: /* Y cylinder */ + if (drawflags & MAN_ROT_Y) { + glTranslatef(0.0, 1.0, 0.0); + if (selectionbase != -1) GPU_select_load_id(selectionbase); + glRotatef(-90.0, 1.0, 0.0, 0.0); + manipulator_setcolor(v3d, 'Y', colcode, 255, (highlight & MAN_ROT_Y) != 0); + draw_cylinder(qobj, cylen, cywid); + glRotatef(90.0, 1.0, 0.0, 0.0); + glTranslatef(0.0, -1.0, 0.0); + } + break; + case 2: /* Z cylinder */ + if (drawflags & MAN_ROT_Z) { + glTranslatef(0.0, 0.0, 1.0); + if (selectionbase != -1) GPU_select_load_id(selectionbase); + manipulator_setcolor(v3d, 'Z', colcode, 255, (highlight & MAN_ROT_Z) != 0); + draw_cylinder(qobj, cylen, cywid); + glTranslatef(0.0, 0.0, -1.0); + } + break; + } + } + + /* restore */ + + gluDeleteQuadric(qobj); + glLoadMatrixf(rv3d->viewmat); + + if (v3d->zbuf) glEnable(GL_DEPTH_TEST); + +} + + +/* ********************************************* */ + +/* main call, does calc centers & orientation too */ +static int drawflags = 0xFFFF; // only for the calls below, belongs in scene...? + +static int manipulator_flags_from_active(int active) +{ + int val; + + if (active != -1) { + if (active == MAN_SEL_TRANS_C) { + val = MAN_TRANS_C; + } + else if (active == MAN_SEL_SCALE_C) { + val = MAN_SCALE_C; + } + else { + val = 1 << active; + } + } + else + val = 0; + + return val; +} + +void WIDGET_manipulator_draw(wmWidget *UNUSED(widget), const bContext *C) +{ + ScrArea *sa = CTX_wm_area(C); + ARegion *ar = CTX_wm_region(C); + View3D *v3d = sa->spacedata.first; + RegionView3D *rv3d = ar->regiondata; + + if (v3d->twflag & V3D_DRAW_MANIPULATOR) { + + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glEnable(GL_BLEND); + if (v3d->twtype & V3D_MANIP_ROTATE) { + + if (G.debug_value == 3) { + if (G.moving & (G_TRANSFORM_OBJ | G_TRANSFORM_EDIT)) + draw_manipulator_rotate_cyl(v3d, rv3d, drawflags, 0, v3d->twtype, MAN_MOVECOL, true, -1); + else + draw_manipulator_rotate_cyl(v3d, rv3d, drawflags, 0, v3d->twtype, MAN_RGB, false, -1); + } + else { + draw_manipulator_rotate(v3d, rv3d, drawflags, 0, v3d->twtype, false, -1); + } + } + if (v3d->twtype & V3D_MANIP_SCALE) { + draw_manipulator_scale(v3d, rv3d, drawflags, 0, v3d->twtype, MAN_RGB, false, -1); + } + if (v3d->twtype & V3D_MANIP_TRANSLATE) { + draw_manipulator_translate(v3d, rv3d, drawflags, 0, v3d->twtype, MAN_RGB, false, -1); + } + + glDisable(GL_BLEND); + } +} + +void WIDGETGROUP_manipulator_update(struct wmWidgetGroup *wgroup, const struct bContext *C) +{ + ScrArea *sa = CTX_wm_area(C); + ARegion *ar = CTX_wm_region(C); + Scene *scene = CTX_data_scene(C); + View3D *v3d = sa->spacedata.first; + RegionView3D *rv3d = ar->regiondata; + ManipulatorGroup *manipulator = WM_widgetgroup_customdata(wgroup); + + int totsel; + + v3d->twflag &= ~V3D_DRAW_MANIPULATOR; + + totsel = calc_manipulator_stats(C); + if (totsel == 0) { + WM_widget_set_draw(manipulator->translate_x, false); + WM_widget_set_draw(manipulator->translate_y, false); + WM_widget_set_draw(manipulator->translate_z, false); + + WM_widget_set_draw(manipulator->rotate_x, false); + WM_widget_set_draw(manipulator->rotate_y, false); + WM_widget_set_draw(manipulator->rotate_z, false); + return; + } + v3d->twflag |= V3D_DRAW_MANIPULATOR; + + /* now we can define center */ + switch (v3d->around) { + case V3D_CENTER: + case V3D_ACTIVE: + { + Object *ob; + if (((v3d->around == V3D_ACTIVE) && (scene->obedit == NULL)) && + ((ob = OBACT) && !(ob->mode & OB_MODE_POSE))) + { + copy_v3_v3(rv3d->twmat[3], ob->obmat[3]); + } + else { + mid_v3_v3v3(rv3d->twmat[3], scene->twmin, scene->twmax); + } + break; + } + case V3D_LOCAL: + case V3D_CENTROID: + copy_v3_v3(rv3d->twmat[3], scene->twcent); + break; + case V3D_CURSOR: + copy_v3_v3(rv3d->twmat[3], ED_view3d_cursor3d_get(scene, v3d)); + break; + } + + mul_mat3_m4_fl(rv3d->twmat, ED_view3d_pixel_size(rv3d, rv3d->twmat[3]) * U.tw_size); + + /* when looking through a selected camera, the manipulator can be at the + * exact same position as the view, skip so we don't break selection */ + if (fabsf(mat4_to_scale(rv3d->twmat)) < 1e-7f) { + WM_widget_set_draw(manipulator->translate_x, false); + WM_widget_set_draw(manipulator->translate_y, false); + WM_widget_set_draw(manipulator->translate_z, false); + + WM_widget_set_draw(manipulator->rotate_x, false); + WM_widget_set_draw(manipulator->rotate_y, false); + WM_widget_set_draw(manipulator->rotate_z, false); + + return; + } + + test_manipulator_axis(C); + drawflags = rv3d->twdrawflag; /* set in calc_manipulator_stats */ + + WM_widget_operator(manipulator->translate_x, WIDGET_manipulator_handler_trans, "TRANSFORM_OT_translate", NULL); + WM_widget_operator(manipulator->translate_y, WIDGET_manipulator_handler_trans, "TRANSFORM_OT_translate", NULL); + WM_widget_operator(manipulator->translate_z, WIDGET_manipulator_handler_trans, "TRANSFORM_OT_translate", NULL); + WM_widget_operator(manipulator->rotate_x, WIDGET_manipulator_handler_rot, "TRANSFORM_OT_rotate", NULL); + WM_widget_operator(manipulator->rotate_y, WIDGET_manipulator_handler_rot, "TRANSFORM_OT_rotate", NULL); + WM_widget_operator(manipulator->rotate_z, WIDGET_manipulator_handler_rot, "TRANSFORM_OT_rotate", NULL); + + if (v3d->twtype & V3D_MANIP_TRANSLATE) { + /* should be added according to the order of axis */ + WM_widget_set_origin(manipulator->translate_x, rv3d->twmat[3]); + WIDGET_arrow_set_direction(manipulator->translate_x, rv3d->twmat[0]); + + WM_widget_set_origin(manipulator->translate_y, rv3d->twmat[3]); + WIDGET_arrow_set_direction(manipulator->translate_y, rv3d->twmat[1]); + + WM_widget_set_origin(manipulator->translate_z, rv3d->twmat[3]); + WIDGET_arrow_set_direction(manipulator->translate_z, rv3d->twmat[2]); + + WM_widget_set_draw(manipulator->translate_x, true); + WM_widget_set_draw(manipulator->translate_y, true); + WM_widget_set_draw(manipulator->translate_z, true); + } + else { + WM_widget_set_draw(manipulator->translate_x, false); + WM_widget_set_draw(manipulator->translate_y, false); + WM_widget_set_draw(manipulator->translate_z, false); + } + + if (v3d->twtype & V3D_MANIP_ROTATE) { + /* should be added according to the order of axis */ + + WM_widget_set_origin(manipulator->rotate_x, rv3d->twmat[3]); + WIDGET_dial_set_direction(manipulator->rotate_x, rv3d->twmat[0]); + + WM_widget_set_origin(manipulator->rotate_y, rv3d->twmat[3]); + WIDGET_dial_set_direction(manipulator->rotate_y, rv3d->twmat[1]); + + WM_widget_set_origin(manipulator->rotate_z, rv3d->twmat[3]); + WIDGET_dial_set_direction(manipulator->rotate_z, rv3d->twmat[2]); + + WM_widget_set_draw(manipulator->rotate_x, true); + WM_widget_set_draw(manipulator->rotate_y, true); + WM_widget_set_draw(manipulator->rotate_z, true); + } + else { + WM_widget_set_draw(manipulator->rotate_x, false); + WM_widget_set_draw(manipulator->rotate_y, false); + WM_widget_set_draw(manipulator->rotate_z, false); + } +} + + +bool WIDGETGROUP_manipulator_poll(wmWidgetGroup *UNUSED(wgroup), const struct bContext *C) +{ + /* it's a given we only use this in 3D view */ + ScrArea *sa = CTX_wm_area(C); + View3D *v3d = sa->spacedata.first; + + return ((v3d->twflag & V3D_USE_MANIPULATOR) != 0); +} + +void WIDGET_manipulator_render_3d_intersect(const bContext *C, wmWidget *UNUSED(widget), int selectionbase) +{ + ScrArea *sa = CTX_wm_area(C); + View3D *v3d = sa->spacedata.first; + ARegion *ar = CTX_wm_region(C); + RegionView3D *rv3d = ar->regiondata; + + /* do the drawing */ + if (v3d->twtype & V3D_MANIP_ROTATE) { + //if (G.debug_value == 3) draw_manipulator_rotate_cyl(v3d, rv3d, MAN_ROT_C & rv3d->twdrawflag, 0, v3d->twtype, MAN_RGB, false, selectionbase); + //else draw_manipulator_rotate(v3d, rv3d, MAN_ROT_C & rv3d->twdrawflag, 0, v3d->twtype, false, selectionbase); + } + if (v3d->twtype & V3D_MANIP_SCALE) + draw_manipulator_scale(v3d, rv3d, MAN_SCALE_C & rv3d->twdrawflag, 0, v3d->twtype, MAN_RGB, false, selectionbase); + //if (v3d->twtype & V3D_MANIP_TRANSLATE) + // draw_manipulator_translate(v3d, rv3d, MAN_TRANS_C & rv3d->twdrawflag, 0, v3d->twtype, MAN_RGB, false, selectionbase); +} + +/* return 0; nothing happened */ +int WIDGET_manipulator_handler(bContext *C, const struct wmEvent *event, wmWidget *UNUSED(widget), wmOperator *op) +{ + ScrArea *sa = CTX_wm_area(C); + View3D *v3d = sa->spacedata.first; + int constraint_axis[3] = {0, 0, 0}; + int val; + int shift = event->shift; + + struct IDProperty *properties = NULL; /* operator properties, assigned to ptr->data and can be written to a file */ + struct PointerRNA *ptr = NULL; /* rna pointer to access properties */ + + val = manipulator_flags_from_active(0); + + if (!((v3d->twflag & V3D_USE_MANIPULATOR) && (v3d->twflag & V3D_DRAW_MANIPULATOR)) || + !(event->keymodifier == 0 || event->keymodifier == KM_SHIFT) || + !((event->val == KM_PRESS) && (event->type == LEFTMOUSE))) + { + return OPERATOR_PASS_THROUGH; + } + + if (val) { + if (val & MAN_TRANS_C) { + switch (val) { + case MAN_TRANS_C: + break; + case MAN_TRANS_X: + if (shift) { + constraint_axis[1] = 1; + constraint_axis[2] = 1; + } + else + constraint_axis[0] = 1; + break; + case MAN_TRANS_Y: + if (shift) { + constraint_axis[0] = 1; + constraint_axis[2] = 1; + } + else + constraint_axis[1] = 1; + break; + case MAN_TRANS_Z: + if (shift) { + constraint_axis[0] = 1; + constraint_axis[1] = 1; + } + else + constraint_axis[2] = 1; + break; + } + WM_operator_properties_alloc(&ptr, &properties, "TRANSFORM_OT_translate"); + /* Force orientation */ + RNA_boolean_set(ptr, "release_confirm", true); + RNA_enum_set(ptr, "constraint_orientation", v3d->twmode); + RNA_boolean_set_array(ptr, "constraint_axis", constraint_axis); + WM_operator_name_call(C, "TRANSFORM_OT_translate", WM_OP_INVOKE_DEFAULT, ptr); + } + else if (val & MAN_SCALE_C) { + switch (val) { + case MAN_SCALE_X: + if (shift) { + constraint_axis[1] = 1; + constraint_axis[2] = 1; + } + else + constraint_axis[0] = 1; + break; + case MAN_SCALE_Y: + if (shift) { + constraint_axis[0] = 1; + constraint_axis[2] = 1; + } + else + constraint_axis[1] = 1; + break; + case MAN_SCALE_Z: + if (shift) { + constraint_axis[0] = 1; + constraint_axis[1] = 1; + } + else + constraint_axis[2] = 1; + break; + } + WM_operator_properties_alloc(&ptr, &properties, "TRANSFORM_OT_resize"); + /* Force orientation */ + RNA_boolean_set(ptr, "release_confirm", true); + RNA_enum_set(ptr, "constraint_orientation", v3d->twmode); + RNA_boolean_set_array(ptr, "constraint_axis", constraint_axis); + WM_operator_name_call(C, "TRANSFORM_OT_resize", WM_OP_INVOKE_DEFAULT, ptr); + } + else if (val == MAN_ROT_T) { /* trackball need special case, init is different */ + /* Do not pass op->ptr!!! trackball has no "constraint" properties! + * See [#34621], it's a miracle it did not cause more problems!!! */ + /* However, we need to copy the "release_confirm" property, but only if defined, see T41112. */ + PointerRNA props_ptr; + wmOperatorType *ot = WM_operatortype_find("TRANSFORM_OT_trackball", true); + WM_operator_properties_create_ptr(&props_ptr, ot); + RNA_boolean_set(&props_ptr, "release_confirm", true); + WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &props_ptr); + WM_operator_properties_free(&props_ptr); + } + else if (val & MAN_ROT_C) { + switch (val) { + case MAN_ROT_X: + constraint_axis[0] = 1; + break; + case MAN_ROT_Y: + constraint_axis[1] = 1; + break; + case MAN_ROT_Z: + constraint_axis[2] = 1; + break; + } + WM_operator_properties_alloc(&ptr, &properties, "TRANSFORM_OT_rotate"); + /* Force orientation */ + RNA_boolean_set(ptr, "release_confirm", true); + RNA_enum_set(ptr, "constraint_orientation", v3d->twmode); + RNA_boolean_set_array(ptr, "constraint_axis", constraint_axis); + WM_operator_name_call(C, "TRANSFORM_OT_rotate", WM_OP_INVOKE_DEFAULT, ptr); + } + } + + if (ptr) { + WM_operator_properties_free(ptr); + MEM_freeN(ptr); + } + + return (val) ? OPERATOR_FINISHED : OPERATOR_PASS_THROUGH; +} + +/* return 0; nothing happened */ +int WIDGET_manipulator_handler_trans(bContext *C, const struct wmEvent *event, wmWidget *widget, struct PointerRNA *ptr) +{ + ScrArea *sa = CTX_wm_area(C); + View3D *v3d = sa->spacedata.first; + int constraint_axis[3] = {0, 0, 0}; + int shift = event->shift; + int direction = GET_INT_FROM_POINTER(WM_widget_customdata(widget)); + + if (!((v3d->twflag & V3D_USE_MANIPULATOR) && (v3d->twflag & V3D_DRAW_MANIPULATOR)) || + !(event->keymodifier == 0 || event->keymodifier == KM_SHIFT)) + { + ;//return OPERATOR_PASS_THROUGH; + } + + if (shift) { + int d = 0; + for (; d < 3; d++) { + if (d != direction) + constraint_axis[d] = 1; + } + } + else + constraint_axis[direction] = 1; + + /* Force orientation */ + RNA_boolean_set(ptr, "release_confirm", true); + RNA_enum_set(ptr, "constraint_orientation", v3d->twmode); + RNA_boolean_set_array(ptr, "constraint_axis", constraint_axis); + RNA_boolean_set(ptr, "use_widget_input", true); + + return OPERATOR_FINISHED; +} + +/* return 0; nothing happened */ +int WIDGET_manipulator_handler_rot(bContext *C, const struct wmEvent *UNUSED(event), wmWidget *widget, struct PointerRNA *ptr) +{ + ScrArea *sa = CTX_wm_area(C); + View3D *v3d = sa->spacedata.first; + int constraint_axis[3] = {0, 0, 0}; + int direction = GET_INT_FROM_POINTER(WM_widget_customdata(widget)); + + if (!(v3d->twflag & V3D_USE_MANIPULATOR) && (v3d->twflag & V3D_DRAW_MANIPULATOR)) + { + ;//return OPERATOR_PASS_THROUGH; + } + + constraint_axis[direction] = 1; + + /* Force orientation */ + RNA_boolean_set(ptr, "release_confirm", true); + RNA_enum_set(ptr, "constraint_orientation", v3d->twmode); + RNA_boolean_set_array(ptr, "constraint_axis", constraint_axis); + RNA_boolean_set(ptr, "use_widget_input", true); + + return OPERATOR_FINISHED; +} + +void WIDGETGROUP_manipulator_free(struct wmWidgetGroup *wgroup) +{ + ManipulatorGroup *manipulator = WM_widgetgroup_customdata(wgroup); + + MEM_freeN(manipulator); +} + +void WIDGETGROUP_manipulator_create(struct wmWidgetGroup *wgroup) +{ + float color_green[4] = {0.0f, 1.0f, 0.0f, 1.0f}; + float color_red[4] = {1.0f, 0.0f, 0.0f, 1.0f}; + float color_blue[4] = {0.0f, 0.0f, 1.0f, 1.0f}; + + wmWidget *widget = NULL; + + ManipulatorGroup *manipulator = MEM_callocN(sizeof(ManipulatorGroup), "manipulator_data"); + + widget = WM_widget_new(WIDGET_manipulator_draw, + WIDGET_manipulator_render_3d_intersect, + NULL, + WIDGET_manipulator_handler, + NULL, false); + + WM_widget_register(wgroup, widget); + + manipulator->translate_x = WIDGET_arrow_new(0, NULL); + WIDGET_arrow_set_color(manipulator->translate_x, color_red); + WM_widget_register(wgroup, manipulator->translate_x); + + manipulator->translate_y = WIDGET_arrow_new(0, SET_INT_IN_POINTER(1)); + WIDGET_arrow_set_color(manipulator->translate_y, color_green); + WM_widget_register(wgroup, manipulator->translate_y); + + manipulator->translate_z = WIDGET_arrow_new(0, SET_INT_IN_POINTER(2)); + WIDGET_arrow_set_color(manipulator->translate_z, color_blue); + WM_widget_register(wgroup, manipulator->translate_z); + + manipulator->rotate_x = WIDGET_dial_new(UI_DIAL_STYLE_RING_CLIPPED, NULL); + WIDGET_dial_set_color(manipulator->rotate_x, color_red); + WM_widget_register(wgroup, manipulator->rotate_x); + + manipulator->rotate_y = WIDGET_dial_new(UI_DIAL_STYLE_RING_CLIPPED, SET_INT_IN_POINTER(1)); + WIDGET_dial_set_color(manipulator->rotate_y, color_green); + WM_widget_register(wgroup, manipulator->rotate_y); + + manipulator->rotate_z = WIDGET_dial_new(UI_DIAL_STYLE_RING_CLIPPED, SET_INT_IN_POINTER(2)); + WIDGET_dial_set_color(manipulator->rotate_z, color_blue); + WM_widget_register(wgroup, manipulator->rotate_z); + + WM_widgetgroup_customdata_set(wgroup, manipulator); +} + diff --git a/source/blender/editors/transform/transform.c b/source/blender/editors/transform/transform.c index 28b0d9711cc..34314e7ff3d 100644 --- a/source/blender/editors/transform/transform.c +++ b/source/blender/editors/transform/transform.c @@ -2038,6 +2038,35 @@ void saveTransform(bContext *C, TransInfo *t, wmOperator *op) } } +static void initSnappingAspect(TransInfo *t) +{ + if ((t->spacetype == SPACE_IMAGE) && (t->mode == TFM_TRANSLATION)) { + if (t->options & CTX_MASK) { + ED_space_image_get_aspect(t->sa->spacedata.first, t->snap_aspect, t->snap_aspect + 1); + } + else if (t->options & CTX_PAINT_CURVE) { + t->snap_aspect[0] = t->snap_aspect[1] = 1.0; + } + else { + ED_space_image_get_uv_aspect(t->sa->spacedata.first, t->snap_aspect, t->snap_aspect + 1); + } + } + else if ((t->spacetype == SPACE_IPO) && (t->mode == TFM_TRANSLATION)) { + View2D *v2d = &t->ar->v2d; + View2DGrid *grid; + SpaceIpo *sipo = t->sa->spacedata.first; + int unity = V2D_UNIT_VALUES; + int unitx = (sipo->flag & SIPO_DRAWTIME) ? V2D_UNIT_SECONDS : V2D_UNIT_FRAMESCALE; + + /* grid */ + grid = UI_view2d_grid_calc(t->scene, v2d, unitx, V2D_GRID_NOCLAMP, unity, V2D_GRID_NOCLAMP, t->ar->winx, t->ar->winy); + + UI_view2d_grid_size(grid, t->snap_aspect, t->snap_aspect + 1); + UI_view2d_grid_free(grid); + } +} + + /* note: caller needs to free 't' on a 0 return */ bool initTransform(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *event, int mode) { @@ -2123,6 +2152,11 @@ bool initTransform(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve else unit_m3(t->spacemtx); + /* initialize snapping factors */ + t->snap_aspect[0] = t->snap_aspect[1] = t->snap_aspect[2] = 1.0f; + + initSnappingAspect(t); + createTransData(C, t); // make TransData structs from selection if (t->total == 0) { diff --git a/source/blender/editors/transform/transform.h b/source/blender/editors/transform/transform.h index 418fb8e1961..2672df24d2e 100644 --- a/source/blender/editors/transform/transform.h +++ b/source/blender/editors/transform/transform.h @@ -363,6 +363,7 @@ typedef struct TransInfo { short event_type; /* event->type used to invoke transform */ short idx_max; /* maximum index on the input vector */ float snap[3]; /* Snapping Gears */ + float snap_aspect[3]; /* snapping aspect correction */ char frame_side; /* Mouse side of the cfra, 'L', 'R' or 'B' */ float viewmat[4][4]; /* copy from G.vd, prevents feedback, */ @@ -477,6 +478,8 @@ typedef struct TransInfo { /* alternative transformation. used to add offset to tracking markers */ #define T_ALT_TRANSFORM (1 << 24) +#define T_USE_WIDGET (1 << 24) + /* TransInfo->modifiers */ #define MOD_CONSTRAINT_SELECT 0x01 #define MOD_PRECISION 0x02 @@ -564,6 +567,7 @@ void flushTransGraphData(TransInfo *t); void remake_graph_transdata(TransInfo *t, struct ListBase *anim_data); void flushTransUVs(TransInfo *t); void flushTransParticles(TransInfo *t); +void flushTransStrands(TransInfo *t); bool clipUVTransform(TransInfo *t, float vec[2], const bool resize); void clipUVData(TransInfo *t); void flushTransNodes(TransInfo *t); diff --git a/source/blender/editors/transform/transform_conversions.c b/source/blender/editors/transform/transform_conversions.c index 4da3fa8a959..307fa86a56b 100644 --- a/source/blender/editors/transform/transform_conversions.c +++ b/source/blender/editors/transform/transform_conversions.c @@ -69,6 +69,7 @@ #include "BKE_crazyspace.h" #include "BKE_curve.h" #include "BKE_depsgraph.h" +#include "BKE_editstrands.h" #include "BKE_fcurve.h" #include "BKE_global.h" #include "BKE_gpencil.h" @@ -1930,6 +1931,211 @@ void flushTransParticles(TransInfo *t) PE_update_object(scene, OBACT, 1); } + +/* ******************* hair edit **************** */ + +static void editmesh_set_connectivity_distance(BMesh *bm, float mtx[3][3], float *dists); +static struct TransIslandData *bmesh_islands_info_calc(BMesh *bm, short selectmode, int *r_island_tot, int **r_island_vert_map); + +static void StrandVertsToTransData(TransInfo *t, TransData *td, + BMEditStrands *UNUSED(edit), BMVert *eve, const float nor[3], const float tang[3], + struct TransIslandData *v_island) +{ + BLI_assert(BM_elem_flag_test(eve, BM_ELEM_HIDDEN) == 0); + + td->flag = 0; + td->loc = eve->co; + copy_v3_v3(td->iloc, td->loc); + + if (v_island) { + copy_v3_v3(td->center, v_island->co); + copy_m3_m3(td->axismtx, v_island->axismtx); + } + else if (t->around == V3D_LOCAL) { + copy_v3_v3(td->center, td->loc); + createSpaceNormalTangent(td->axismtx, nor, tang); + } + else { + copy_v3_v3(td->center, td->loc); + + /* Setting normals */ + copy_v3_v3(td->axismtx[2], nor); + td->axismtx[0][0] = + td->axismtx[0][1] = + td->axismtx[0][2] = + td->axismtx[1][0] = + td->axismtx[1][1] = + td->axismtx[1][2] = 0.0f; + } + + td->ext = NULL; + td->val = NULL; + td->extra = NULL; +} + +static void createTransStrandVerts(TransInfo *t) +{ + Scene *scene = t->scene; + Object *ob = OBACT; + BMEditStrands *edit = BKE_editstrands_from_object(ob); + BMesh *bm = edit->bm; + TransData *tob = NULL; + BMVert *eve; + BMIter iter; + float mtx[3][3], smtx[3][3]; + float *dists = NULL; + int a; + int propmode = (t->flag & T_PROP_EDIT) ? (t->flag & T_PROP_EDIT_ALL) : 0; + int mirror = 0; + + struct TransIslandData *island_info = NULL; + int island_info_tot; + int *island_vert_map = NULL; + + if (t->flag & T_MIRROR) { +#if 0 // TODO mirror relies on EDBM functions which don't have an equivalent for editstrands yet + EDBM_verts_mirror_cache_begin(em, 0, false, (t->flag & T_PROP_EDIT) == 0, false); + mirror = 1; +#endif + } + + /* quick check if we can transform */ + /* note: in prop mode we need at least 1 selected */ + if (bm->totvertsel == 0) { + goto cleanup; + } + + if (propmode) { + unsigned int count = 0; + BM_ITER_MESH (eve, &iter, bm, BM_VERTS_OF_MESH) { + if (!BM_elem_flag_test(eve, BM_ELEM_HIDDEN)) { + count++; + } + } + + t->total = count; + + /* allocating scratch arrays */ + if (propmode & T_PROP_CONNECTED) + dists = MEM_mallocN(bm->totvert * sizeof(float), "scratch nears"); + } + else { + t->total = bm->totvertsel; + } + + tob = t->data = MEM_callocN(t->total * sizeof(TransData), "TransObData(Strands EditMode)"); + + copy_m3_m4(mtx, ob->obmat); + /* we use a pseudoinverse so that when one of the axes is scaled to 0, + * matrix inversion still works and we can still moving along the other */ + pseudoinverse_m3_m3(smtx, mtx, PSEUDOINVERSE_EPSILON); + + if (propmode & T_PROP_CONNECTED) { + editmesh_set_connectivity_distance(bm, mtx, dists); + } + + if (t->around == V3D_LOCAL) { + island_info = bmesh_islands_info_calc(edit->bm, SCE_SELECT_VERTEX, &island_info_tot, &island_vert_map); + } + + /* find out which half we do */ + if (mirror) { +#if 0 // TODO + BM_ITER_MESH (eve, &iter, bm, BM_VERTS_OF_MESH) { + if (BM_elem_flag_test(eve, BM_ELEM_SELECT) && eve->co[0] != 0.0f) { + if (eve->co[0] < 0.0f) { + t->mirror = -1; + mirror = -1; + } + break; + } + } +#endif + } + + t->customData = BKE_editstrands_get_locations(edit); + t->flag |= T_FREE_CUSTOMDATA; + + BM_ITER_MESH_INDEX (eve, &iter, bm, BM_VERTS_OF_MESH, a) { + if (!BM_elem_flag_test(eve, BM_ELEM_HIDDEN)) { + if (propmode || BM_elem_flag_test(eve, BM_ELEM_SELECT)) { + struct TransIslandData *v_island = (island_info && island_vert_map[a] != -1) ? + &island_info[island_vert_map[a]] : NULL; + /* XXX TODO calculate normal and tangent along the strand */ + float nor[3], tang[3]; + nor[0] = 0.0f; nor[1] = 0.0f; nor[2] = 1.0f; + tang[0] = 0.0f; tang[1] = 1.0f; tang[2] = 0.0f; + + StrandVertsToTransData(t, tob, edit, eve, nor, tang, v_island); + + /* selected */ + if (BM_elem_flag_test(eve, BM_ELEM_SELECT)) + tob->flag |= TD_SELECTED; + + if (propmode) { + if (propmode & T_PROP_CONNECTED) { + tob->dist = dists[a]; + } + else { + tob->flag |= TD_NOTCONNECTED; + tob->dist = FLT_MAX; + } + } + + copy_m3_m3(tob->smtx, smtx); + copy_m3_m3(tob->mtx, mtx); + + /* Mirror? */ + if ((mirror > 0 && tob->iloc[0] > 0.0f) || (mirror < 0 && tob->iloc[0] < 0.0f)) { +#if 0 // TODO + BMVert *vmir = EDBM_verts_mirror_get(em, eve); + if (vmir && vmir != eve) { + tob->extra = vmir; + } +#endif + } + tob++; + } + } + } + + if (island_info) { + MEM_freeN(island_info); + MEM_freeN(island_vert_map); + } + + if (mirror != 0) { +#if 0 // TODO + tob = t->data; + for (a = 0; a < t->total; a++, tob++) { + if (ABS(tob->loc[0]) <= 0.00001f) { + tob->flag |= TD_MIRROR_EDGE; + } + } +#endif + } + +cleanup: + if (dists) + MEM_freeN(dists); + + if (t->flag & T_MIRROR) { +#if 0 // TODO + EDBM_verts_mirror_cache_end(em); +#endif + } +} + +void flushTransStrands(TransInfo *t) +{ + Scene *scene = t->scene; + Object *ob = OBACT; + BMEditStrands *edit = BKE_editstrands_from_object(ob); + BMEditStrandsLocations origlocs = t->customData; + + BKE_editstrands_solve_constraints(ob, edit, origlocs); +} + /* ********************* mesh ****************** */ static bool bmesh_test_dist_add(BMVert *v, BMVert *v_other, @@ -2082,9 +2288,8 @@ static void editmesh_set_connectivity_distance(BMesh *bm, float mtx[3][3], float MEM_freeN(dists_prev); } -static struct TransIslandData *editmesh_islands_info_calc(BMEditMesh *em, int *r_island_tot, int **r_island_vert_map) +static struct TransIslandData *bmesh_islands_info_calc(BMesh *bm, short selectmode, int *r_island_tot, int **r_island_vert_map) { - BMesh *bm = em->bm; struct TransIslandData *trans_islands; char htype; char itype; @@ -2098,7 +2303,7 @@ static struct TransIslandData *editmesh_islands_info_calc(BMEditMesh *em, int *r int *vert_map; - if (em->selectmode & (SCE_SELECT_VERTEX | SCE_SELECT_EDGE)) { + if (selectmode & (SCE_SELECT_VERTEX | SCE_SELECT_EDGE)) { groups_array = MEM_mallocN(sizeof(*groups_array) * bm->totedgesel, __func__); group_tot = BM_mesh_calc_edge_groups(bm, groups_array, &group_index, NULL, NULL, @@ -2362,7 +2567,7 @@ static void createTransEditVerts(TransInfo *t) } if (t->around == V3D_LOCAL) { - island_info = editmesh_islands_info_calc(em, &island_info_tot, &island_vert_map); + island_info = bmesh_islands_info_calc(em->bm, em->selectmode, &island_info_tot, &island_vert_map); } /* detect CrazySpace [tm] */ @@ -4774,6 +4979,7 @@ static int SeqToTransData_Recursive(TransInfo *t, ListBase *seqbase, TransData * Sequence *seq; int recursive, count, flag; int tot = 0; + int max = INT32_MIN, min = INT32_MAX; for (seq = seqbase->first; seq; seq = seq->next) { @@ -4796,15 +5002,21 @@ static int SeqToTransData_Recursive(TransInfo *t, ListBase *seqbase, TransData * if (flag & SEQ_LEFTSEL) { SeqToTransData(td++, td2d++, tdsq++, seq, flag, SEQ_LEFTSEL); tot++; + min = min_ii(seq->startdisp, min); + max = max_ii(seq->startdisp, max); } if (flag & SEQ_RIGHTSEL) { SeqToTransData(td++, td2d++, tdsq++, seq, flag, SEQ_RIGHTSEL); tot++; + min = min_ii(seq->enddisp, min); + max = max_ii(seq->enddisp, max); } } else { SeqToTransData(td++, td2d++, tdsq++, seq, flag, SELECT); tot++; + min = min_ii(seq->startdisp, min); + max = max_ii(seq->enddisp, max); } } } @@ -6243,6 +6455,13 @@ void special_aftertrans_update(bContext *C, TransInfo *t) { /* do nothing */ } + else if ((t->scene->basact) && + (ob = t->scene->basact->object) && + (ob->mode & OB_MODE_HAIR_EDIT) && + BKE_editstrands_from_object(ob)) + { + /* do nothing */ + } else { /* Objects */ int i; @@ -8005,6 +8224,16 @@ void createTransData(bContext *C, TransInfo *t) sort_trans_data_dist(t); } } + else if (ob && (ob->mode & OB_MODE_HAIR_EDIT) && BKE_editstrands_from_object(ob)) { + createTransStrandVerts(t); + t->flag |= T_POINTS; + + if (t->data && t->flag & T_PROP_EDIT) { + sort_trans_data(t); // makes selected become first in array + set_prop_dist(t, 1); + sort_trans_data_dist(t); + } + } else if (ob && (ob->mode & OB_MODE_ALL_PAINT)) { if ((t->options & CTX_PAINT_CURVE) && !ELEM(t->mode, TFM_SHEAR, TFM_SHRINKFATTEN)) { t->flag |= T_POINTS | T_2D_EDIT; diff --git a/source/blender/editors/transform/transform_generics.c b/source/blender/editors/transform/transform_generics.c index f943ac5ee42..537e76baa7e 100644 --- a/source/blender/editors/transform/transform_generics.c +++ b/source/blender/editors/transform/transform_generics.c @@ -71,6 +71,7 @@ #include "BKE_armature.h" #include "BKE_curve.h" #include "BKE_depsgraph.h" +#include "BKE_editstrands.h" #include "BKE_fcurve.h" #include "BKE_lattice.h" #include "BKE_nla.h" @@ -893,6 +894,12 @@ static void recalcData_objects(TransInfo *t) } flushTransParticles(t); } + else if (base && (base->object->mode & OB_MODE_HAIR_EDIT) && BKE_editstrands_from_object(base->object)) { + if (t->state != TRANS_CANCEL) { + applyProject(t); + } + flushTransStrands(t); + } else { int i; @@ -1183,10 +1190,16 @@ void initTransInfo(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve t->view = v3d; t->animtimer = (animscreen) ? animscreen->animtimer : NULL; - + + if (op && ((prop = RNA_struct_find_property(op->ptr, "use_widget_input")) && + RNA_property_is_set(op->ptr, prop))) + { + if (RNA_property_boolean_get(op->ptr, prop)) + t->flag |= T_USE_WIDGET; + } + /* turn manipulator off during transform */ - // FIXME: but don't do this when USING the manipulator... - if (t->flag & T_MODAL) { + if ((t->flag & T_MODAL) && !(t->flag & T_USE_WIDGET)) { t->twtype = v3d->twtype; v3d->twtype = 0; } @@ -1466,7 +1479,7 @@ void postTrans(bContext *C, TransInfo *t) else if (t->spacetype == SPACE_VIEW3D) { View3D *v3d = t->sa->spacedata.first; /* restore manipulator */ - if (t->flag & T_MODAL) { + if ((t->flag & T_MODAL) && !(t->flag & T_USE_WIDGET)) { v3d->twtype = t->twtype; } } diff --git a/source/blender/editors/transform/transform_manipulator.c b/source/blender/editors/transform/transform_manipulator.c index acc6108f264..667c0f01c18 100644 --- a/source/blender/editors/transform/transform_manipulator.c +++ b/source/blender/editors/transform/transform_manipulator.c @@ -66,6 +66,7 @@ #include "ED_curve.h" #include "ED_particle.h" #include "ED_view3d.h" +#include "ED_transform.h" #include "UI_resources.h" @@ -549,7 +550,7 @@ static int calc_manipulator_stats(const bContext *C) if (ob && totsel) { switch (v3d->twmode) { - + case V3D_MANIP_GLOBAL: { break; /* nothing to do */ @@ -973,7 +974,7 @@ static void draw_manipulator_rotate( ortho = is_orthogonal_m4(rv3d->twmat); - + /* apply the transform delta */ if (is_moving) { copy_m4_m4(matt, rv3d->twmat); // to copy the parts outside of [3][3] @@ -1881,4 +1882,3 @@ int BIF_do_manipulator(bContext *C, const struct wmEvent *event, wmOperator *op) return val; } - diff --git a/source/blender/editors/transform/transform_ops.c b/source/blender/editors/transform/transform_ops.c index fe06f01e09c..62fa1b5a989 100644 --- a/source/blender/editors/transform/transform_ops.c +++ b/source/blender/editors/transform/transform_ops.c @@ -82,6 +82,7 @@ static const char OP_EDGE_CREASE[] = "TRANSFORM_OT_edge_crease"; static const char OP_EDGE_BWEIGHT[] = "TRANSFORM_OT_edge_bevelweight"; static const char OP_SEQ_SLIDE[] = "TRANSFORM_OT_seq_slide"; + static void TRANSFORM_OT_translate(struct wmOperatorType *ot); static void TRANSFORM_OT_rotate(struct wmOperatorType *ot); static void TRANSFORM_OT_tosphere(struct wmOperatorType *ot); diff --git a/source/blender/editors/transform/transform_orientations.c b/source/blender/editors/transform/transform_orientations.c index 7fea8e163fd..030e29d0ebb 100644 --- a/source/blender/editors/transform/transform_orientations.c +++ b/source/blender/editors/transform/transform_orientations.c @@ -1010,7 +1010,7 @@ int getTransformOrientation(const bContext *C, float normal[3], float plane[3], result = ORIENTATION_EDGE; } } - else if (ob && (ob->mode & (OB_MODE_ALL_PAINT | OB_MODE_PARTICLE_EDIT))) { + else if (ob && (ob->mode & (OB_MODE_ALL_BRUSH | OB_MODE_PARTICLE_EDIT))) { /* pass */ } else { diff --git a/source/blender/editors/transform/transform_snap.c b/source/blender/editors/transform/transform_snap.c index d9cc9588d49..fae5654db79 100644 --- a/source/blender/editors/transform/transform_snap.c +++ b/source/blender/editors/transform/transform_snap.c @@ -2450,7 +2450,6 @@ void snapSequenceBounds(TransInfo *t, const int mval[2]) static void applyGridIncrement(TransInfo *t, float *val, int max_index, const float fac[3], GearsType action) { int i; - float asp[3] = {1.0f, 1.0f, 1.0f}; // TODO: Remove hard coded limit here (3) if (max_index > 2) { printf("applyGridIncrement: invalid index %d, clamping\n", max_index); @@ -2460,34 +2459,8 @@ static void applyGridIncrement(TransInfo *t, float *val, int max_index, const fl // Early bailing out if no need to snap if (fac[action] == 0.0f) return; - - /* evil hack - snapping needs to be adapted for image aspect ratio */ - if ((t->spacetype == SPACE_IMAGE) && (t->mode == TFM_TRANSLATION)) { - if (t->options & CTX_MASK) { - ED_space_image_get_aspect(t->sa->spacedata.first, asp, asp + 1); - } - else if (t->options & CTX_PAINT_CURVE) { - asp[0] = asp[1] = 1.0; - } - else { - ED_space_image_get_uv_aspect(t->sa->spacedata.first, asp, asp + 1); - } - } - else if ((t->spacetype == SPACE_IPO) && (t->mode == TFM_TRANSLATION)) { - View2D *v2d = &t->ar->v2d; - View2DGrid *grid; - SpaceIpo *sipo = t->sa->spacedata.first; - int unity = V2D_UNIT_VALUES; - int unitx = (sipo->flag & SIPO_DRAWTIME) ? V2D_UNIT_SECONDS : V2D_UNIT_FRAMESCALE; - - /* grid */ - grid = UI_view2d_grid_calc(t->scene, v2d, unitx, V2D_GRID_NOCLAMP, unity, V2D_GRID_NOCLAMP, t->ar->winx, t->ar->winy); - - UI_view2d_grid_size(grid, &asp[0], &asp[1]); - UI_view2d_grid_free(grid); - } for (i = 0; i <= max_index; i++) { - val[i] = fac[action] * asp[i] * floorf(val[i] / (fac[action] * asp[i]) + 0.5f); + val[i] = fac[action] * t->snap_aspect[i] * floorf(val[i] / (fac[action] * t->snap_aspect[i]) + 0.5f); } } diff --git a/source/blender/editors/util/editmode_undo.c b/source/blender/editors/util/editmode_undo.c index bc7a8374c73..7c4db697b01 100644 --- a/source/blender/editors/util/editmode_undo.c +++ b/source/blender/editors/util/editmode_undo.c @@ -84,6 +84,7 @@ typedef struct UndoElem { void *undodata; uintptr_t undosize; char name[BKE_UNDO_STR_MAX]; + Object *(*get_object)(const bContext *C); void * (*getdata)(bContext * C); void (*freedata)(void *); void (*to_editmode)(void *, void *, void *); @@ -106,14 +107,15 @@ static void undo_restore(UndoElem *undo, void *editdata, void *obdata) /* name can be a dynamic string */ void undo_editmode_push(bContext *C, const char *name, - void * (*getdata)(bContext * C), + Object *(*get_object)(const bContext *C), + void * (*getdata)(bContext *C), void (*freedata)(void *), void (*to_editmode)(void *, void *, void *), void *(*from_editmode)(void *, void *), int (*validate_undo)(void *, void *)) { UndoElem *uel; - Object *obedit = CTX_data_edit_object(C); + Object *obedit = get_object(C); void *editdata; int nr; uintptr_t memused, totmem, maxmem; @@ -133,6 +135,7 @@ void undo_editmode_push(bContext *C, const char *name, BLI_strncpy(uel->name, name, sizeof(uel->name)); BLI_addtail(&undobase, uel); + uel->get_object = get_object; uel->getdata = getdata; uel->freedata = freedata; uel->to_editmode = to_editmode; @@ -193,19 +196,19 @@ void undo_editmode_push(bContext *C, const char *name, static void undo_clean_stack(bContext *C) { UndoElem *uel, *next; - Object *obedit = CTX_data_edit_object(C); /* global undo changes pointers, so we also allow identical names */ /* side effect: when deleting/renaming object and start editing new one with same name */ uel = undobase.first; while (uel) { + Object *obedit = uel->get_object(C); void *editdata = uel->getdata(C); bool is_valid = false; next = uel->next; /* for when objects are converted, renamed, or global undo changes pointers... */ - if (uel->type == obedit->type) { + if (obedit && uel->type == obedit->type) { if (STREQ(uel->id.name, obedit->id.name)) { if (uel->validate_undo == NULL) is_valid = true; @@ -232,12 +235,13 @@ static void undo_clean_stack(bContext *C) /* 1 = an undo, -1 is a redo. we have to make sure 'curundo' remains at current situation */ void undo_editmode_step(bContext *C, int step) { - Object *obedit = CTX_data_edit_object(C); + Object *obedit; /* prevent undo to happen on wrong object, stack can be a mix */ undo_clean_stack(C); if (step == 0) { + obedit = curundo->get_object(C); undo_restore(curundo, curundo->getdata(C), obedit->data); } else if (step == 1) { @@ -248,6 +252,7 @@ void undo_editmode_step(bContext *C, int step) else { if (G.debug & G_DEBUG) printf("undo %s\n", curundo->name); curundo = curundo->prev; + obedit = curundo->get_object(C); undo_restore(curundo, curundo->getdata(C), obedit->data); } } @@ -258,15 +263,19 @@ void undo_editmode_step(bContext *C, int step) error("No more steps to redo"); } else { + obedit = curundo->get_object(C); undo_restore(curundo->next, curundo->getdata(C), obedit->data); curundo = curundo->next; if (G.debug & G_DEBUG) printf("redo %s\n", curundo->name); } } + obedit = curundo->get_object(C); + /* special case for editmesh, mode must be copied back to the scene */ if (obedit->type == OB_MESH) { - EDBM_selectmode_to_scene(C); + if (obedit == CTX_data_edit_object(C)) + EDBM_selectmode_to_scene(C); } DAG_id_tag_update(&obedit->id, OB_RECALC_DATA); diff --git a/source/blender/editors/util/undo.c b/source/blender/editors/util/undo.c index ee6101d2952..3eef8401db9 100644 --- a/source/blender/editors/util/undo.c +++ b/source/blender/editors/util/undo.c @@ -55,6 +55,7 @@ #include "ED_mball.h" #include "ED_mesh.h" #include "ED_object.h" +#include "ED_physics.h" #include "ED_render.h" #include "ED_screen.h" #include "ED_paint.h" @@ -104,6 +105,9 @@ void ED_undo_push(bContext *C, const char *str) PE_undo_push(CTX_data_scene(C), str); } + else if (obact && obact->mode & OB_MODE_HAIR_EDIT) { + undo_push_strands(C, str); + } else if (obact && obact->mode & OB_MODE_SCULPT) { /* do nothing for now */ } @@ -189,6 +193,14 @@ static int ed_undo_step(bContext *C, int step, const char *undoname) else PE_redo(scene); } + else if (obact && obact->mode & OB_MODE_HAIR_EDIT) { + if (undoname) + undo_editmode_name(C, undoname); + else + undo_editmode_step(C, step); + + WM_event_add_notifier(C, NC_GEOM | ND_DATA, NULL); + } else if (U.uiflag & USER_GLOBALUNDO) { // note python defines not valid here anymore. //#ifdef WITH_PYTHON diff --git a/source/blender/gpu/GPU_buffers.h b/source/blender/gpu/GPU_buffers.h index 8b65666265d..cda7bffe93f 100644 --- a/source/blender/gpu/GPU_buffers.h +++ b/source/blender/gpu/GPU_buffers.h @@ -48,6 +48,9 @@ struct GSet; struct GPUVertPointLink; struct PBVH; +typedef void (*GPUBufferCopyFunc)(DerivedMesh *dm, float *varray, int *index, + int *mat_orig_to_new, void *user_data); + typedef struct GPUBuffer { int size; /* in bytes */ void *pointer; /* used with vertex arrays */ @@ -88,6 +91,7 @@ typedef struct GPUDrawObject { GPUBuffer *colors; GPUBuffer *edges; GPUBuffer *uvedges; + GPUBuffer *facemapindices; /* for each triangle, the original MFace index */ int *triangle_to_mface; @@ -113,6 +117,10 @@ typedef struct GPUDrawObject { /* caches of the original DerivedMesh values */ int totvert; int totedge; + + int totfacemaps; /* total facemaps */ + int *facemap_start; /* beginning of facemap */ + int *facemap_count; /* elements per facemap */ } GPUDrawObject; /* used for GLSL materials */ @@ -128,9 +136,21 @@ void GPU_global_buffer_pool_free_unused(void); GPUBuffer *GPU_buffer_alloc(int size, bool force_vertex_arrays); void GPU_buffer_free(GPUBuffer *buffer); -GPUDrawObject *GPU_drawobject_new(struct DerivedMesh *dm); void GPU_drawobject_free(struct DerivedMesh *dm); +/* flag that controls data type to fill buffer with, a modifier will prepare. */ +typedef enum { + GPU_BUFFER_VERTEX = 0, + GPU_BUFFER_NORMAL, + GPU_BUFFER_COLOR, + GPU_BUFFER_UV, + GPU_BUFFER_UV_TEXPAINT, + GPU_BUFFER_EDGE, + GPU_BUFFER_UVEDGE, + GPU_BUFFER_FACEMAP +} GPUBufferType; + + /* called before drawing */ void GPU_vertex_setup(struct DerivedMesh *dm); void GPU_normal_setup(struct DerivedMesh *dm); @@ -140,6 +160,7 @@ void GPU_texpaint_uv_setup(struct DerivedMesh *dm); void GPU_color_setup(struct DerivedMesh *dm, int colType); void GPU_edge_setup(struct DerivedMesh *dm); /* does not mix with other data */ void GPU_uvedge_setup(struct DerivedMesh *dm); +void GPU_facemap_setup(struct DerivedMesh *dm); int GPU_attrib_element_size(GPUAttrib data[], int numdata); void GPU_interleaved_attrib_setup(GPUBuffer *buffer, GPUAttrib data[], int numdata); diff --git a/source/blender/gpu/GPU_renderer.h b/source/blender/gpu/GPU_renderer.h new file mode 100644 index 00000000000..7ebc864c454 --- /dev/null +++ b/source/blender/gpu/GPU_renderer.h @@ -0,0 +1,58 @@ +/* + * ***** 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2014 Blender Foundation. + * All rights reserved. + * + * Contributor(s): Antony Riakiotakis. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#ifndef __GPU_RENDERER_H__ +#define __GPU_RENDERER_H__ + +#include "DNA_listBase.h" + +struct Object; + +/* batches per material sorted by some criterion, ie texture image changes */ +typedef struct SubBatch { + int start; + int len; + void *data; /* extra data relevant to the subbatch, such as image */ +} SubBatch; + + +/* stores data related to the object that can be used for rendering */ +typedef struct MaterialBatch { + int start; + int len; + ListBase subbatches; + struct Object *ob; + void *data; +} MaterialBatch; + + +/* new type of material */ +typedef struct GPUMaterialX { + int datarequest; /* type of data requested from objects */ +} GPUMaterialX; + +void GPU_renderer_material_draw(struct ListBase *materials); + +#endif // __GPU_RENDERER_H__ diff --git a/source/blender/gpu/intern/gpu_buffers.c b/source/blender/gpu/intern/gpu_buffers.c index 4b7dc7f5c9d..8554f2c08e0 100644 --- a/source/blender/gpu/intern/gpu_buffers.c +++ b/source/blender/gpu/intern/gpu_buffers.c @@ -69,6 +69,34 @@ typedef enum { GPU_BUFFER_ELEMENT_STATE = (1 << 5), } GPUBufferState; +typedef struct { + GPUBufferCopyFunc copy; + GLenum gl_buffer_type; + int vector_size; +} GPUBufferTypeSettings; + +static void GPU_buffer_copy_vertex(DerivedMesh *dm, float *varray, int *index, int *mat_orig_to_new, void *user); +static void GPU_buffer_copy_normal(DerivedMesh *dm, float *varray, int *index, int *mat_orig_to_new, void *user); +static void GPU_buffer_copy_mcol(DerivedMesh *dm, float *varray, int *index, int *mat_orig_to_new, void *user); +static void GPU_buffer_copy_uv(DerivedMesh *dm, float *varray, int *index, int *mat_orig_to_new, void *user); +static void GPU_buffer_copy_uv_texpaint(DerivedMesh *dm, float *varray, int *index, int *mat_orig_to_new, void *user); +static void GPU_buffer_copy_edge(DerivedMesh *dm, float *varray, int *index, int *mat_orig_to_new, void *user); +static void GPU_buffer_copy_uvedge(DerivedMesh *dm, float *varray, int *index, int *mat_orig_to_new, void *user); +static void GPU_buffer_copy_facemap(DerivedMesh *dm, float *varray, int *index, int *mat_orig_to_new, void *user); + +static int gpu_buffer_size_from_type(DerivedMesh *dm, GPUBufferType type); + +const GPUBufferTypeSettings gpu_buffer_type_settings[] = { + {GPU_buffer_copy_vertex, GL_ARRAY_BUFFER_ARB, 3}, + {GPU_buffer_copy_normal, GL_ARRAY_BUFFER_ARB, 3}, + {GPU_buffer_copy_mcol, GL_ARRAY_BUFFER_ARB, 3}, + {GPU_buffer_copy_uv, GL_ARRAY_BUFFER_ARB, 2}, + {GPU_buffer_copy_uv_texpaint, GL_ARRAY_BUFFER_ARB, 4}, + {GPU_buffer_copy_edge, GL_ELEMENT_ARRAY_BUFFER_ARB, 2}, + {GPU_buffer_copy_uvedge, GL_ELEMENT_ARRAY_BUFFER_ARB, 4}, + {GPU_buffer_copy_facemap, GL_ELEMENT_ARRAY_BUFFER_ARB, 3} +}; + #define MAX_GPU_ATTRIB_DATA 32 #define BUFFER_OFFSET(n) ((GLubyte *)NULL + (n)) @@ -505,7 +533,7 @@ static void gpu_drawobject_init_vert_points(GPUDrawObject *gdo, MFace *f, int to /* see GPUDrawObject's structure definition for a description of the * data being initialized here */ -GPUDrawObject *GPU_drawobject_new(DerivedMesh *dm) +static GPUDrawObject *GPU_drawobject_new(DerivedMesh *dm) { GPUDrawObject *gdo; MFace *mface; @@ -572,8 +600,14 @@ void GPU_drawobject_free(DerivedMesh *dm) return; MEM_freeN(gdo->materials); - MEM_freeN(gdo->triangle_to_mface); - MEM_freeN(gdo->vert_points); + if (gdo->triangle_to_mface) + MEM_freeN(gdo->triangle_to_mface); + if (gdo->vert_points) + MEM_freeN(gdo->vert_points); + if (gdo->facemap_count) + MEM_freeN(gdo->facemap_count); + if (gdo->facemap_start) + MEM_freeN(gdo->facemap_start); #ifdef USE_GPU_POINT_LINK MEM_freeN(gdo->vert_points_mem); #endif @@ -584,6 +618,7 @@ void GPU_drawobject_free(DerivedMesh *dm) GPU_buffer_free(gdo->colors); GPU_buffer_free(gdo->edges); GPU_buffer_free(gdo->uvedges); + GPU_buffer_free(gdo->facemapindices); MEM_freeN(gdo); dm->drawObject = NULL; @@ -609,8 +644,7 @@ typedef void (*GPUBufferCopyFunc)(DerivedMesh *dm, float *varray, int *index, int *mat_orig_to_new, void *user_data); static GPUBuffer *gpu_buffer_setup(DerivedMesh *dm, GPUDrawObject *object, - int vector_size, int size, GLenum target, - void *user, GPUBufferCopyFunc copy_f) + int type, void *user) { GPUBufferPool *pool; GPUBuffer *buffer; @@ -618,6 +652,10 @@ static GPUBuffer *gpu_buffer_setup(DerivedMesh *dm, GPUDrawObject *object, int *mat_orig_to_new; int *cur_index_per_mat; int i; + const GPUBufferTypeSettings *ts = &gpu_buffer_type_settings[type]; + GLenum target = ts->gl_buffer_type; + int vector_size = ts->vector_size; + int size = gpu_buffer_size_from_type(dm, type); bool use_VBOs = (GLEW_ARB_vertex_buffer_object) && !(U.gameflags & USER_DISABLE_VBO); GLboolean uploaded; @@ -674,7 +712,10 @@ static GPUBuffer *gpu_buffer_setup(DerivedMesh *dm, GPUDrawObject *object, uploaded = GL_FALSE; /* attempt to upload the data to the VBO */ while (uploaded == GL_FALSE) { - (*copy_f)(dm, varray, cur_index_per_mat, mat_orig_to_new, user); + if (dm->copy_gpu_data) + dm->copy_gpu_data(dm, type, varray, cur_index_per_mat, mat_orig_to_new, user); + else + ts->copy(dm, varray, cur_index_per_mat, mat_orig_to_new, user); /* glUnmapBuffer returns GL_FALSE if * the data store is corrupted; retry * in that case */ @@ -691,7 +732,10 @@ static GPUBuffer *gpu_buffer_setup(DerivedMesh *dm, GPUDrawObject *object, if (buffer) { varray = buffer->pointer; - (*copy_f)(dm, varray, cur_index_per_mat, mat_orig_to_new, user); + if (dm->copy_gpu_data) + dm->copy_gpu_data(dm, type, varray, cur_index_per_mat, mat_orig_to_new, user); + else + ts->copy(dm, varray, cur_index_per_mat, mat_orig_to_new, user); } } @@ -946,6 +990,8 @@ static void GPU_buffer_copy_edge(DerivedMesh *dm, float *varray_, int *UNUSED(in medge = dm->getEdgeArray(dm); totedge = dm->getNumEdges(dm); + + /* two passes, one to count number of faces per facemap, one to actually copy the data */ for (i = 0; i < totedge; i++, medge++) { varray[i * 2] = dm->drawObject->vert_points[medge->v1].point_index; @@ -987,31 +1033,63 @@ static void GPU_buffer_copy_uvedge(DerivedMesh *dm, float *varray, int *UNUSED(i } } -typedef enum { - GPU_BUFFER_VERTEX = 0, - GPU_BUFFER_NORMAL, - GPU_BUFFER_COLOR, - GPU_BUFFER_UV, - GPU_BUFFER_UV_TEXPAINT, - GPU_BUFFER_EDGE, - GPU_BUFFER_UVEDGE, -} GPUBufferType; +static void GPU_buffer_copy_facemap(DerivedMesh *dm, float *varray_, int *UNUSED(index), int *UNUSED(mat_orig_to_new), void *UNUSED(user)) +{ + GPUDrawObject *gdo = dm->drawObject; + int facemap; + int *facemap_po = CustomData_get_layer(&dm->polyData, CD_FACEMAP); + unsigned int *varray = (unsigned int *)varray_; + int i, totface, offset = 0; + MFace *f, *f_base = dm->getTessFaceArray(dm); + int *facemap_offset; + int *orig_index = dm->getTessFaceDataArray(dm, CD_ORIGINDEX); + + totface = dm->getNumTessFaces(dm); -typedef struct { - GPUBufferCopyFunc copy; - GLenum gl_buffer_type; - int vector_size; -} GPUBufferTypeSettings; + gdo->totfacemaps = dm->totfmaps; -const GPUBufferTypeSettings gpu_buffer_type_settings[] = { - {GPU_buffer_copy_vertex, GL_ARRAY_BUFFER_ARB, 3}, - {GPU_buffer_copy_normal, GL_ARRAY_BUFFER_ARB, 3}, - {GPU_buffer_copy_mcol, GL_ARRAY_BUFFER_ARB, 3}, - {GPU_buffer_copy_uv, GL_ARRAY_BUFFER_ARB, 2}, - {GPU_buffer_copy_uv_texpaint, GL_ARRAY_BUFFER_ARB, 4}, - {GPU_buffer_copy_edge, GL_ELEMENT_ARRAY_BUFFER_ARB, 2}, - {GPU_buffer_copy_uvedge, GL_ELEMENT_ARRAY_BUFFER_ARB, 4} -}; + gdo->facemap_start = MEM_callocN(gdo->totfacemaps * sizeof(*gdo->facemap_start), "GDO_facemap_start"); + gdo->facemap_count = MEM_callocN(gdo->totfacemaps * sizeof(*gdo->facemap_count), "GDO_facemap_count"); + facemap_offset = MEM_callocN(gdo->totfacemaps * sizeof(*facemap_offset), "facemap_offset"); + + f = f_base; + for (i = 0; i < totface; i++, f++) { + facemap = facemap_po[orig_index[i]]; + if (facemap != -1) + gdo->facemap_count[facemap] += (f->v4) ? 6 : 3; + } + + for (i = 0; i < gdo->totfacemaps; i++) { + gdo->facemap_start[i] = offset; + offset += gdo->facemap_count[i]; + } + + f = f_base; + for (i = 0; i < totface; i++, f++) { + int fmap_offset; + facemap = facemap_po[orig_index[i]]; + + if (facemap == -1) + continue; + fmap_offset = gdo->facemap_start[facemap] + facemap_offset[facemap]; + + varray[fmap_offset] = gdo->vert_points[f->v1].point_index; + varray[fmap_offset + 1] = gdo->vert_points[f->v2].point_index; + varray[fmap_offset + 2] = gdo->vert_points[f->v3].point_index; + + facemap_offset[facemap] += 3; + + if (f->v4) { + varray[fmap_offset + 3] = dm->drawObject->vert_points[f->v3].point_index; + varray[fmap_offset + 4] = dm->drawObject->vert_points[f->v4].point_index; + varray[fmap_offset + 5] = dm->drawObject->vert_points[f->v1].point_index; + + facemap_offset[facemap] += 3; + } + } + + MEM_freeN(facemap_offset); +} /* get the GPUDrawObject buffer associated with a type */ static GPUBuffer **gpu_drawobject_buffer_from_type(GPUDrawObject *gdo, GPUBufferType type) @@ -1031,6 +1109,8 @@ static GPUBuffer **gpu_drawobject_buffer_from_type(GPUDrawObject *gdo, GPUBuffer return &gdo->edges; case GPU_BUFFER_UVEDGE: return &gdo->uvedges; + case GPU_BUFFER_FACEMAP: + return &gdo->facemapindices; default: return NULL; } @@ -1059,6 +1139,8 @@ static int gpu_buffer_size_from_type(DerivedMesh *dm, GPUBufferType type) * less so here we can over allocate and assume all * tris. */ return sizeof(float) * 4 * dm->drawObject->tot_triangle_point; + case GPU_BUFFER_FACEMAP: + return sizeof(int) * 3 * dm->drawObject->tot_triangle_point; default: return -1; } @@ -1067,12 +1149,9 @@ static int gpu_buffer_size_from_type(DerivedMesh *dm, GPUBufferType type) /* call gpu_buffer_setup with settings for a particular type of buffer */ static GPUBuffer *gpu_buffer_setup_type(DerivedMesh *dm, GPUBufferType type) { - const GPUBufferTypeSettings *ts; void *user_data = NULL; GPUBuffer *buf; - ts = &gpu_buffer_type_settings[type]; - /* special handling for MCol and UV buffers */ if (type == GPU_BUFFER_COLOR) { if (!(user_data = DM_get_tessface_data_layer(dm, dm->drawObject->colType))) @@ -1083,9 +1162,7 @@ static GPUBuffer *gpu_buffer_setup_type(DerivedMesh *dm, GPUBufferType type) return NULL; } - buf = gpu_buffer_setup(dm, dm->drawObject, ts->vector_size, - gpu_buffer_size_from_type(dm, type), - ts->gl_buffer_type, user_data, ts->copy); + buf = gpu_buffer_setup(dm, dm->drawObject, type, user_data); return buf; } @@ -1096,9 +1173,13 @@ static GPUBuffer *gpu_buffer_setup_common(DerivedMesh *dm, GPUBufferType type) { GPUBuffer **buf; - if (!dm->drawObject) - dm->drawObject = GPU_drawobject_new(dm); - + if (!dm->drawObject) { + if (dm->gpuObjectNew) + dm->drawObject = dm->gpuObjectNew(dm); + else + dm->drawObject = GPU_drawobject_new(dm); + } + buf = gpu_drawobject_buffer_from_type(dm->drawObject, type); if (!(*buf)) *buf = gpu_buffer_setup_type(dm, type); @@ -1262,6 +1343,32 @@ void GPU_uvedge_setup(DerivedMesh *dm) GLStates |= GPU_BUFFER_VERTEX_STATE; } +void GPU_facemap_setup(DerivedMesh *dm) +{ + if (!gpu_buffer_setup_common(dm, GPU_BUFFER_FACEMAP)) + return; + + if (!gpu_buffer_setup_common(dm, GPU_BUFFER_VERTEX)) + return; + + glEnableClientState(GL_VERTEX_ARRAY); + if (dm->drawObject->points->use_vbo) { + glBindBufferARB(GL_ARRAY_BUFFER_ARB, dm->drawObject->points->id); + glVertexPointer(3, GL_FLOAT, 0, 0); + } + else { + glVertexPointer(3, GL_FLOAT, 0, dm->drawObject->points->pointer); + } + + GLStates |= GPU_BUFFER_VERTEX_STATE; + if (dm->drawObject->facemapindices->use_vbo) { + glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, dm->drawObject->facemapindices->id); + } + + GLStates |= GPU_BUFFER_ELEMENT_STATE; +} + + static int GPU_typesize(int type) { switch (type) { diff --git a/source/blender/gpu/intern/gpu_compositing.c b/source/blender/gpu/intern/gpu_compositing.c index 2bafee0fb52..d19be0a8b7e 100644 --- a/source/blender/gpu/intern/gpu_compositing.c +++ b/source/blender/gpu/intern/gpu_compositing.c @@ -294,7 +294,6 @@ static GPUTexture * create_jitter_texture(void) return GPU_texture_create_2D_procedural(64, 64, &jitter[0][0], true, NULL); } - bool GPU_fx_compositor_initialize_passes( GPUFX *fx, const rcti *rect, const rcti *scissor_rect, const GPUFXSettings *fx_settings) diff --git a/source/blender/gpu/intern/gpu_renderer.c b/source/blender/gpu/intern/gpu_renderer.c new file mode 100644 index 00000000000..6ca0c8b7254 --- /dev/null +++ b/source/blender/gpu/intern/gpu_renderer.c @@ -0,0 +1,36 @@ +/* + * ***** 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2006 Blender Foundation. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): Antony Riakiotakis. + * + * ***** END GPL LICENSE BLOCK ***** + */ + + +#include "GPU_renderer.h" + + +/* iterates through all added materials, prepares data if needed and draws their meshes */ +void GPU_renderer_material_draw(struct ListBase *materials) +{ + (void)materials; +} diff --git a/source/blender/imbuf/IMB_imbuf.h b/source/blender/imbuf/IMB_imbuf.h index ed16c670002..2a976092fd3 100644 --- a/source/blender/imbuf/IMB_imbuf.h +++ b/source/blender/imbuf/IMB_imbuf.h @@ -279,7 +279,7 @@ int IMB_anim_get_duration(struct anim *anim, IMB_Timecode_Type tc); * and frs_sec and frs_sec_base untouched if none available!) */ bool IMB_anim_get_fps(struct anim *anim, - short *frs_sec, float *frs_sec_base); + short *frs_sec, float *frs_sec_base, bool no_av_base); /** * diff --git a/source/blender/imbuf/intern/anim_movie.c b/source/blender/imbuf/intern/anim_movie.c index 0bb9f0fce51..aee83c39b29 100644 --- a/source/blender/imbuf/intern/anim_movie.c +++ b/source/blender/imbuf/intern/anim_movie.c @@ -1427,11 +1427,17 @@ int IMB_anim_get_duration(struct anim *anim, IMB_Timecode_Type tc) } bool IMB_anim_get_fps(struct anim *anim, - short *frs_sec, float *frs_sec_base) + short *frs_sec, float *frs_sec_base, bool no_av_base) { if (anim->frs_sec) { *frs_sec = anim->frs_sec; *frs_sec_base = anim->frs_sec_base; +#ifdef WITH_FFMPEG + if (no_av_base) + *frs_sec_base /= AV_TIME_BASE; +#else + (void)no_av_base; +#endif return true; } return false; diff --git a/source/blender/imbuf/intern/indexer.c b/source/blender/imbuf/intern/indexer.c index 150ea0995ac..0509dea89d5 100644 --- a/source/blender/imbuf/intern/indexer.c +++ b/source/blender/imbuf/intern/indexer.c @@ -1006,7 +1006,7 @@ static AviMovie *alloc_proxy_output_avi( * but sane defaults help anyways...*/ float frs_sec_base = 1.0; - IMB_anim_get_fps(anim, &frs_sec, &frs_sec_base); + IMB_anim_get_fps(anim, &frs_sec, &frs_sec_base, false); x = width; y = height; diff --git a/source/blender/makesdna/DNA_ID.h b/source/blender/makesdna/DNA_ID.h index f68de03614b..f9128363dee 100644 --- a/source/blender/makesdna/DNA_ID.h +++ b/source/blender/makesdna/DNA_ID.h @@ -238,6 +238,7 @@ typedef struct PreviewImage { #define ID_LS MAKE_ID2('L', 'S') /* FreestyleLineStyle */ #define ID_PAL MAKE_ID2('P', 'L') /* Palette */ #define ID_PC MAKE_ID2('P', 'C') /* PaintCurve */ +#define ID_CL MAKE_ID2('C', 'L') /* CacheLibrary */ /* NOTE! Fake IDs, needed for g.sipo->blocktype or outliner */ #define ID_SEQ MAKE_ID2('S', 'Q') diff --git a/source/blender/makesdna/DNA_action_types.h b/source/blender/makesdna/DNA_action_types.h index 761e76eb249..b8688e5e12a 100644 --- a/source/blender/makesdna/DNA_action_types.h +++ b/source/blender/makesdna/DNA_action_types.h @@ -471,6 +471,9 @@ typedef enum eActionGroup_Flag { AGRP_NOTVISIBLE = (1 << 5), /* for UI (Graph Editor), sub-channels are shown */ AGRP_EXPANDED_G = (1 << 6), + + /* sub channel modifiers off */ + AGRP_MODIFIERS_OFF = (1 << 7), AGRP_TEMP = (1 << 30), AGRP_MOVED = (1 << 31) diff --git a/source/blender/makesdna/DNA_anim_types.h b/source/blender/makesdna/DNA_anim_types.h index 68f80cb27d8..590179e0a0f 100644 --- a/source/blender/makesdna/DNA_anim_types.h +++ b/source/blender/makesdna/DNA_anim_types.h @@ -479,7 +479,7 @@ typedef enum eFCurve_Flags { /* fcurve uses 'auto-handles', which stay horizontal... */ // DEPRECATED FCURVE_AUTO_HANDLES = (1<<5), - + FCURVE_MOD_OFF = (1<<6), /* skip evaluation, as RNA-path cannot be resolved (similar to muting, but cannot be set by user) */ FCURVE_DISABLED = (1<<10), /* curve can only have whole-number values (integer types) */ diff --git a/source/blender/makesdna/DNA_armature_types.h b/source/blender/makesdna/DNA_armature_types.h index 6b7e70f454b..af4b91f5457 100644 --- a/source/blender/makesdna/DNA_armature_types.h +++ b/source/blender/makesdna/DNA_armature_types.h @@ -154,7 +154,8 @@ typedef enum eArmature_DeformFlag { ARM_DEF_ENVELOPE = (1<<1), ARM_DEF_QUATERNION = (1<<2), ARM_DEF_B_BONE_REST = (1<<3), /* deprecated */ - ARM_DEF_INVERT_VGROUP = (1<<4) + ARM_DEF_INVERT_VGROUP = (1<<4), + ARM_DEF_FACEMAPS = (1<<5) } eArmature_DeformFlag; #if (DNA_DEPRECATED_GCC_POISON == 1) diff --git a/source/blender/makesdna/DNA_brush_types.h b/source/blender/makesdna/DNA_brush_types.h index f83caea66a2..a799e851887 100644 --- a/source/blender/makesdna/DNA_brush_types.h +++ b/source/blender/makesdna/DNA_brush_types.h @@ -102,7 +102,10 @@ typedef struct Brush { char vertexpaint_tool; /* active vertex/weight paint blend mode (poorly named) */ char imagepaint_tool; /* active image paint tool */ char mask_tool; /* enum BrushMaskTool, only used if sculpt_tool is SCULPT_TOOL_MASK */ - + char hair_tool; /* active hair tool */ + char pad2[3]; + int pad3; + float autosmooth_factor; float crease_pinch_factor; @@ -295,6 +298,16 @@ typedef enum BrushImagePaintTool { PAINT_TOOL_MASK = 5 } BrushImagePaintTool; +typedef enum BrushHairTool { + HAIR_TOOL_COMB = 1, + HAIR_TOOL_CUT = 2, + HAIR_TOOL_LENGTH = 3, + HAIR_TOOL_PUFF = 4, + HAIR_TOOL_ADD = 5, + HAIR_TOOL_SMOOTH = 6, + HAIR_TOOL_WEIGHT = 7, +} BrushHairTool; + /* direction that the brush displaces along */ enum { SCULPT_DISP_DIR_AREA = 0, diff --git a/source/blender/makesdna/DNA_cache_library_types.h b/source/blender/makesdna/DNA_cache_library_types.h new file mode 100644 index 00000000000..0def355e7d5 --- /dev/null +++ b/source/blender/makesdna/DNA_cache_library_types.h @@ -0,0 +1,294 @@ +/* + * ***** 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2015 by Blender Foundation + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): none yet. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file DNA_cache_library_types.h + * \ingroup DNA + */ + +#ifndef __DNA_CACHE_LIBRARY_TYPES_H__ +#define __DNA_CACHE_LIBRARY_TYPES_H__ + +#include "DNA_defs.h" +#include "DNA_ID.h" +#include "DNA_listBase.h" + +#define MAX_CACHE_GROUP_LEVEL 8 + +typedef enum eCacheLibrary_SourceMode { + CACHE_LIBRARY_SOURCE_SCENE = 0, /* use generated scene data as input */ + CACHE_LIBRARY_SOURCE_CACHE = 1, /* use cached data as input*/ +} eCacheLibrary_SourceMode; + +typedef enum eCacheLibrary_DisplayMode { + CACHE_LIBRARY_DISPLAY_SOURCE = 0, /* display source data */ + CACHE_LIBRARY_DISPLAY_RESULT = 1, /* display result data */ + CACHE_LIBRARY_DISPLAY_MODIFIERS = 2, /* display input with modifiers */ +} eCacheLibrary_DisplayMode; + +typedef enum eCacheDataType { + CACHE_TYPE_OBJECT = (1 << 0), + CACHE_TYPE_DERIVED_MESH = (1 << 1), + CACHE_TYPE_HAIR = (1 << 2), + CACHE_TYPE_HAIR_PATHS = (1 << 3), + CACHE_TYPE_PARTICLES = (1 << 4), + + CACHE_TYPE_ALL = CACHE_TYPE_OBJECT | CACHE_TYPE_DERIVED_MESH | CACHE_TYPE_HAIR | CACHE_TYPE_HAIR_PATHS | CACHE_TYPE_PARTICLES, +} eCacheDataType; + +typedef enum eCacheReadSampleResult { + CACHE_READ_SAMPLE_INVALID = 0, /* no valid result can be retrieved */ + CACHE_READ_SAMPLE_EARLY = 1, /* request time before first sample */ + CACHE_READ_SAMPLE_LATE = 2, /* request time after last sample */ + CACHE_READ_SAMPLE_EXACT = 3, /* found sample for requested frame */ + CACHE_READ_SAMPLE_INTERPOLATED = 4, /* no exact sample, but found enclosing samples for interpolation */ +} eCacheReadSampleResult; + +typedef enum eCacheLibrary_Flag { + CACHE_LIBRARY_BAKING = (1 << 0), /* perform modifier evaluation when evaluating */ +} eCacheLibrary_Flag; + +typedef enum eCacheLibrary_DisplayFlag { + CACHE_LIBRARY_DISPLAY_MOTION = (1 << 0), /* display motion state result from simulation, if available */ + CACHE_LIBRARY_DISPLAY_CHILDREN = (1 << 1), /* display child strands, if available */ +} eCacheLibrary_DisplayFlag; + +typedef struct CacheLibrary { + ID id; + + int flag; + short eval_mode DNA_DEPRECATED; + short source_mode; + short display_mode; + short pad; + int display_flag; + int render_flag DNA_DEPRECATED; + int data_types; + struct Group *filter_group; + char description[256]; + + char input_filepath[1024]; /* 1024 = FILE_MAX */ + char output_filepath[1024]; /* 1024 = FILE_MAX */ + + ListBase modifiers; + + struct CacheArchiveInfo *archive_info; +} CacheLibrary; + +/* ========================================================================= */ + +/* These are runtime structs, included in DNA only for easier RNA parsing */ + +typedef struct CacheArchiveInfoNode { + struct CacheArchiveInfoNode *next, *prev; + + short type; + short flag; + int pad; + char name[256]; + + ListBase child_nodes; + + int64_t bytes_size; /* overall size of data stored in this node and children */ + + char datatype_name[64]; + short datatype_extent; + short pad2; + + int num_samples; + + /* array properties */ + int array_size; + int pad3; +} CacheArchiveInfoNode; + +typedef enum eCacheArchiveInfoNode_Flag { + eCacheArchiveInfoNode_Flag_Expand, +} eCacheArchiveInfoNode_Flag; + +typedef enum eCacheArchiveInfoNode_Type { + eCacheArchiveInfoNode_Type_Object, + eCacheArchiveInfoNode_Type_ScalarProperty, + eCacheArchiveInfoNode_Type_ArrayProperty, + eCacheArchiveInfoNode_Type_CompoundProperty, +} eCacheArchiveInfoNode_Type; + +typedef struct CacheArchiveInfo { + char filepath[1024]; /* FILE_MAX */ + + char app_name[64]; /* MAX_NAME */ + char date_written[64]; /* MAX_NAME */ + char description[256]; + + struct CacheArchiveInfoNode *root_node; +} CacheArchiveInfo; + +/* ========================================================================= */ + +/* XXX here be dragons ... + * stuff below is a production hack, + * should not be considered a permanent solution ... + */ +typedef struct CacheModifier { + struct CacheModifier *next, *prev; + + short type, pad; + int flag; + char name[64]; /* MAX_NAME */ +} CacheModifier; + +typedef enum eCacheModifier_Type { + eCacheModifierType_None = 0, + + eCacheModifierType_HairSimulation = 1, + eCacheModifierType_ForceField = 2, + eCacheModifierType_ShrinkWrap = 3, + eCacheModifierType_StrandsKey = 4, + eCacheModifierType_Haircut = 5, + + NUM_CACHE_MODIFIER_TYPES +} eCacheModifier_Type; + +typedef struct HairSimParams { + int flag; + float timescale; + int substeps; + int pad; + + struct EffectorWeights *effector_weights; + + float mass; + float drag; + float goal_stiffness, goal_damping; + struct CurveMapping *goal_stiffness_mapping; + float stretch_stiffness, stretch_damping; + float bend_stiffness, bend_damping; + struct CurveMapping *bend_stiffness_mapping; +} HairSimParams; + +typedef enum eHairSimParams_Flag { + eHairSimParams_Flag_UseGoalStiffnessCurve = (1 << 0), + eHairSimParams_Flag_UseBendStiffnessCurve = (1 << 1), + eHairSimParams_Flag_UseGoalDeflect = (1 << 2), +} eHairSimParams_Flag; + +typedef struct HairSimCacheModifier { + CacheModifier modifier; + + struct Object *object; + int hair_system; + int pad; + + HairSimParams sim_params; +} HairSimCacheModifier; + +/* cached mesh data for calculating velocities */ +typedef struct ForceFieldVertexCache { + float frame_prev; + int totvert; + float (*co_prev)[3]; + float (*vel)[3]; +} ForceFieldVertexCache; + +typedef struct ForceFieldCacheModifier { + CacheModifier modifier; + + struct Object *object; + + struct ForceFieldVertexCache *vertex_cache; + + int type; + int flag; + float strength; + float min_distance, max_distance; + float falloff; +} ForceFieldCacheModifier; + +typedef enum eForceFieldCacheModifier_Type { + eForceFieldCacheModifier_Type_Deflect = 0, + eForceFieldCacheModifier_Type_Drag = 1, +} eForceFieldCacheModifier_Type; + +typedef enum eForceFieldCacheModifier_Flag { + eForceFieldCacheModifier_Flag_DoubleSided = (1 << 0), +} eForceFieldCacheModifier_Flag; + +typedef struct ShrinkWrapCacheModifier { + CacheModifier modifier; + + struct Object *object; + int hair_system; + int flag; + + struct Object *target; +} ShrinkWrapCacheModifier; + +typedef enum eShrinkWrapCacheModifier_Flag { + eShrinkWrapCacheModifier_Flag_InternalTarget = (1 << 0), +} eShrinkWrapCacheModifier_Flag; + +typedef struct StrandsKeyCacheModifier { + CacheModifier modifier; + + struct Object *object; + int hair_system; + int flag; + + struct Key *key; + int shapenr; + int pad; + + struct BMEditStrands *edit; /* edit data (runtime) */ +} StrandsKeyCacheModifier; + +typedef enum eStrandsKeyCacheModifier_Flag { + eStrandsKeyCacheModifier_Flag_ShapeLock = (1 << 0), + eStrandsKeyCacheModifier_Flag_UseMotionState = (1 << 1), +} eStrandsKeyCacheModifier_Flag; + +typedef struct HaircutCacheModifier { + CacheModifier modifier; + + struct Object *object; + int hair_system; + int flag; + + short cut_mode; + short pad[3]; + + struct Object *target; +} HaircutCacheModifier; + +typedef enum eHaircutCacheModifier_Flag { + eHaircutCacheModifier_Flag_InternalTarget = (1 << 0), +} eHaircutCacheModifier_Flag; + +typedef enum eHaircutCacheModifier_CutMode { + eHaircutCacheModifier_CutMode_Enter = (1 << 0), + eHaircutCacheModifier_CutMode_Exit = (1 << 1), +} eHaircutCacheModifier_CutMode; + +#endif diff --git a/source/blender/makesdna/DNA_customdata_types.h b/source/blender/makesdna/DNA_customdata_types.h index 74f5967db13..47cae0f4134 100644 --- a/source/blender/makesdna/DNA_customdata_types.h +++ b/source/blender/makesdna/DNA_customdata_types.h @@ -63,10 +63,10 @@ typedef struct CustomDataExternal { * layers, each with a data type (e.g. MTFace, MDeformVert, etc.). */ typedef struct CustomData { CustomDataLayer *layers; /* CustomDataLayers, ordered by type */ - int typemap[42]; /* runtime only! - maps types to indices of first layer of that type, + int typemap[44]; /* runtime only! - maps types to indices of first layer of that type, * MUST be >= CD_NUMTYPES, but we cant use a define here. * Correct size is ensured in CustomData_update_typemap assert() */ - int pad_i1; + int pad; int totlayer, maxlayer; /* number of layers, size of layers array */ int totsize; /* in editmode, total size of all data layers */ struct BLI_mempool *pool; /* (BMesh Only): Memory pool for allocation of blocks */ @@ -121,8 +121,10 @@ typedef enum CustomDataType { CD_MLOOPTANGENT = 39, CD_TESSLOOPNORMAL = 40, CD_CUSTOMLOOPNORMAL = 41, + CD_FACEMAP = 42, /* exclusive face group, each face can only be part of one */ + CD_MSURFACE_SAMPLE = 43, - CD_NUMTYPES = 42 + CD_NUMTYPES = 44 } CustomDataType; /* Bits for CustomDataMask */ @@ -170,6 +172,8 @@ typedef enum CustomDataType { #define CD_MASK_MLOOPTANGENT (1LL << CD_MLOOPTANGENT) #define CD_MASK_TESSLOOPNORMAL (1LL << CD_TESSLOOPNORMAL) #define CD_MASK_CUSTOMLOOPNORMAL (1LL << CD_CUSTOMLOOPNORMAL) +#define CD_MASK_FACEMAP (1LL << CD_FACEMAP) +#define CD_MASK_MSURFACE_SAMPLE (1LL << CD_MSURFACE_SAMPLE) /* CustomData.flag */ enum { diff --git a/source/blender/makesdna/DNA_key_types.h b/source/blender/makesdna/DNA_key_types.h index 60ab01c901b..5ab67d286b6 100644 --- a/source/blender/makesdna/DNA_key_types.h +++ b/source/blender/makesdna/DNA_key_types.h @@ -62,13 +62,27 @@ typedef struct KeyBlock { void *data; /* array of shape key values, size is (Key->elemsize * KeyBlock->totelem) */ char name[64]; /* MAX_NAME (unique name, user assigned) */ char vgroup[64]; /* MAX_VGROUP_NAME (optional vertex group), array gets allocated into 'weights' when set */ + char facemap[64]; /* facemap name, if applicable */ /* ranges, for RNA and UI only to clamp 'curval' */ float slidermin; float slidermax; - } KeyBlock; +typedef enum eKeyOwnerType { + /* 0 used as 'undefined', for versioning */ + KEY_OWNER_MESH = 1, + KEY_OWNER_CURVE = 2, + KEY_OWNER_LATTICE = 3, + KEY_OWNER_PARTICLES = 4, + KEY_OWNER_CACHELIB = 5, +} eKeyOwnerType; + +/* DEPRECATED */ +typedef struct KeyFrom { + int type DNA_DEPRECATED; + int index DNA_DEPRECATED; /* index of owner in the id */ +} KeyFrom; typedef struct Key { ID id; @@ -90,6 +104,9 @@ typedef struct Key { struct Ipo *ipo DNA_DEPRECATED; /* old animation system, deprecated for 2.5 */ ID *from; + KeyFrom from_extra DNA_DEPRECATED; + int fromtype; /* supplementary type of the Key owner */ + int fromindex; /* index of owner in the id */ int totkey; /* (totkey == BLI_listbase_count(&key->block)) */ short flag; diff --git a/source/blender/makesdna/DNA_meshdata_types.h b/source/blender/makesdna/DNA_meshdata_types.h index b76f40c884d..e3eb04a5578 100644 --- a/source/blender/makesdna/DNA_meshdata_types.h +++ b/source/blender/makesdna/DNA_meshdata_types.h @@ -298,6 +298,13 @@ enum { FREESTYLE_FACE_MARK = 1, }; +typedef struct MSurfaceSample { + unsigned int orig_verts[3]; + float orig_weights[3]; + int orig_poly; + unsigned int orig_loops[3]; +} MSurfaceSample; + /* mvert->flag */ enum { /* SELECT = (1 << 0), */ diff --git a/source/blender/makesdna/DNA_modifier_types.h b/source/blender/makesdna/DNA_modifier_types.h index 1fc66b9b016..bf2d5e5fa6c 100644 --- a/source/blender/makesdna/DNA_modifier_types.h +++ b/source/blender/makesdna/DNA_modifier_types.h @@ -714,22 +714,31 @@ typedef enum { } ParticleSystemModifierFlag; typedef enum { - eParticleInstanceFlag_Parents = (1 << 0), - eParticleInstanceFlag_Children = (1 << 1), - eParticleInstanceFlag_Path = (1 << 2), - eParticleInstanceFlag_Unborn = (1 << 3), - eParticleInstanceFlag_Alive = (1 << 4), - eParticleInstanceFlag_Dead = (1 << 5), - eParticleInstanceFlag_KeepShape = (1 << 6), - eParticleInstanceFlag_UseSize = (1 << 7), + eParticleInstanceFlag_Parents = (1 << 0), + eParticleInstanceFlag_Children = (1 << 1), + eParticleInstanceFlag_Path = (1 << 2), + eParticleInstanceFlag_Unborn = (1 << 3), + eParticleInstanceFlag_Alive = (1 << 4), + eParticleInstanceFlag_Dead = (1 << 5), + eParticleInstanceFlag_KeepShape = (1 << 6), + eParticleInstanceFlag_UseSize = (1 << 7), } ParticleInstanceModifierFlag; +typedef enum { + eParticleInstanceSpace_World = 0, + eParticleInstanceSpace_Local = 1, +} ParticleInstanceModifierSpace; + typedef struct ParticleInstanceModifierData { ModifierData modifier; struct Object *ob; - short psys, flag, axis, pad; + short psys, flag, axis, space; float position, random_position; + float rotation, random_rotation; + float particle_amount, particle_offset; + char index_layer_name[64]; /* MAX_CUSTOMDATA_LAYER_NAME */ + char value_layer_name[64]; /* MAX_CUSTOMDATA_LAYER_NAME */ } ParticleInstanceModifierData; typedef enum { diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h index 55e18b73762..0f61a8f271c 100644 --- a/source/blender/makesdna/DNA_node_types.h +++ b/source/blender/makesdna/DNA_node_types.h @@ -788,6 +788,17 @@ typedef struct NodeShaderVectTransform { int pad; } NodeShaderVectTransform; +typedef struct NodeShaderTexPointDensity { + short point_source, pad; + int particle_system; + float radius; + int resolution; + short space; + short interpolation; + short color_source; + short pad2; +} NodeShaderTexPointDensity; + /* TEX_output */ typedef struct TexNodeOutput { char name[64]; @@ -1128,4 +1139,24 @@ enum { #define CMP_NODE_PLANETRACKDEFORM_MBLUR_SAMPLES_MAX 64 +/* Point Density shader node */ + +enum { + SHD_POINTDENSITY_SOURCE_PSYS = 0, + SHD_POINTDENSITY_SOURCE_OBJECT = 1, +}; + +enum { + SHD_POINTDENSITY_SPACE_OBJECT = 0, + SHD_POINTDENSITY_SPACE_WORLD = 1, +}; + +enum { + SHD_POINTDENSITY_COLOR_CONSTANT = 0, + SHD_POINTDENSITY_COLOR_PARTAGE = 1, + SHD_POINTDENSITY_COLOR_PARTSPEED = 2, + SHD_POINTDENSITY_COLOR_PARTVEL = 3, + SHD_POINTDENSITY_COLOR_PARTTEX = 4, +}; + #endif diff --git a/source/blender/makesdna/DNA_object_force.h b/source/blender/makesdna/DNA_object_force.h index c9c1f618e86..52ea42eb535 100644 --- a/source/blender/makesdna/DNA_object_force.h +++ b/source/blender/makesdna/DNA_object_force.h @@ -370,6 +370,7 @@ typedef struct SoftBody { #define PFIELD_DO_ROTATION (1<<15) #define PFIELD_GUIDE_PATH_WEIGHT (1<<16) /* apply curve weights */ #define PFIELD_SMOKE_DENSITY (1<<17) /* multiply smoke force by density */ +#define PFIELD_USE_SIGNED_DISTANCE (1<<18) /* surface shape: use negative distance on the interior */ /* pd->falloff */ #define PFIELD_FALL_SPHERE 0 diff --git a/source/blender/makesdna/DNA_object_types.h b/source/blender/makesdna/DNA_object_types.h index 50f94aa09e5..10e59618aa0 100644 --- a/source/blender/makesdna/DNA_object_types.h +++ b/source/blender/makesdna/DNA_object_types.h @@ -65,6 +65,14 @@ typedef struct bDeformGroup { /* need this flag for locking weights */ char flag, pad[7]; } bDeformGroup; + +/* Face Maps*/ +typedef struct bFaceMap { + struct bFaceMap *next, *prev; + char name[64]; /* MAX_VGROUP_NAME */ +} bFaceMap; + + #define MAX_VGROUP_NAME 64 /* bDeformGroup->flag */ @@ -142,7 +150,8 @@ typedef struct Object { ListBase effect DNA_DEPRECATED; // XXX deprecated... keep for readfile ListBase defbase; /* list of bDeformGroup (vertex groups) names and flag only */ ListBase modifiers; /* list of ModifierData structures */ - + ListBase fmaps; /* list of facemaps */ + int mode; /* Local object mode */ int restore_mode; /* Keep track of what mode to return to after toggling a mode */ @@ -180,7 +189,8 @@ typedef struct Object { short flag; /* copy of Base */ short colbits DNA_DEPRECATED; /* deprecated, use 'matbits' */ - short transflag, protectflag; /* transformation settings and transform locks */ + int transflag; /* transformation settings */ + short protectflag, pad; /* transform locks */ short trackflag, upflag; short nlaflag; /* used for DopeSheet filtering settings (expanded/collapsed) */ short ipoflag; // xxx deprecated... old animation system @@ -191,9 +201,6 @@ typedef struct Object { /* dupli-frame settings */ int dupon, dupoff, dupsta, dupend; - /* did last modifier stack generation need mapping support? */ - int lastNeedMapping; - /* during realtime */ /* note that inertia is only called inertia for historical reasons @@ -244,6 +251,12 @@ typedef struct Object { short index; /* custom index, for renderpasses */ unsigned short actdef; /* current deformation group, note: index starts at 1 */ + unsigned short actfmap; /* current face map, note: index starts at 1 */ + unsigned short pad2[2]; + + /* did last modifier stack generation need mapping support? */ + short lastNeedMapping; + float col[4]; /* object color */ int gameflag; @@ -264,6 +277,8 @@ typedef struct Object { struct PartDeflect *pd; /* particle deflector/attractor/collision data */ struct SoftBody *soft; /* if exists, saved in file */ struct Group *dup_group; /* object duplicator for group */ + struct DupliCache *dup_cache; /* cached dupli overrides */ + struct CacheLibrary *cache_library; /* cache library to use */ char body_type; /* for now used to temporarily holds the type of collision object */ char shapeflag; /* flag for pinning */ @@ -329,8 +344,43 @@ typedef struct DupliObject { /* particle this dupli was generated from */ struct ParticleSystem *particle_system; + + struct DupliObjectData *data; } DupliObject; +typedef struct DupliObjectDataStrands { + struct DupliObjectDataStrands *next, *prev; + + char name[64]; /* MAX_NAME */ + struct Strands *strands; + struct StrandsChildren *strands_children; +} DupliObjectDataStrands; + +/* data that can be shared by multiple DupliObject instances */ +typedef struct DupliObjectData { + /* XXX eventually it should be possible to construct dupli instances + * entirely without Objects in the DNA, but current drawing code and + * others make this too difficult + */ + struct Object *ob; + struct BoundBox bb; + struct DerivedMesh *dm; + ListBase strands; +} DupliObjectData; + +typedef struct DupliCache { + short flag; + short result; + float cfra; /* frame for which the cache was constructed */ + + struct GHash *ghash; + ListBase duplilist; +} DupliCache; + +typedef enum eDupliCache_Flag { + DUPCACHE_FLAG_DIRTY = (1 << 0), /* cache requires update */ +} eDupliCache_Flag; + /* **************** OBJECT ********************* */ /* used many places... should be specialized */ @@ -407,6 +457,8 @@ enum { OB_NO_CONSTRAINTS = 1 << 13, /* runtime constraints disable */ OB_NO_PSYS_UPDATE = 1 << 14, /* hack to work around particle issue */ + OB_IS_DUPLI_CACHE = 1 << 31, /* temporary flag: object data overridden from cache */ + OB_DUPLI = OB_DUPLIFRAMES | OB_DUPLIVERTS | OB_DUPLIGROUP | OB_DUPLIFACES | OB_DUPLIPARTS, }; @@ -455,6 +507,7 @@ enum { /* enable transparent draw */ OB_DRAWTRANSP = 1 << 7, OB_DRAW_ALL_EDGES = 1 << 8, /* only for meshes currently */ + OB_DRAW_WIRECOLOR = 1 << 9, }; /* empty_drawtype: no flags */ @@ -674,10 +727,12 @@ typedef enum ObjectMode { OB_MODE_TEXTURE_PAINT = 1 << 4, OB_MODE_PARTICLE_EDIT = 1 << 5, OB_MODE_POSE = 1 << 6, + OB_MODE_HAIR_EDIT = 1 << 7, } ObjectMode; /* any mode where the brush system is used */ #define OB_MODE_ALL_PAINT (OB_MODE_SCULPT | OB_MODE_VERTEX_PAINT | OB_MODE_WEIGHT_PAINT | OB_MODE_TEXTURE_PAINT) +#define OB_MODE_ALL_BRUSH (OB_MODE_ALL_PAINT | OB_MODE_HAIR_EDIT) #define MAX_DUPLI_RECUR 8 diff --git a/source/blender/makesdna/DNA_particle_types.h b/source/blender/makesdna/DNA_particle_types.h index 95cb5c84bed..07e944c360a 100644 --- a/source/blender/makesdna/DNA_particle_types.h +++ b/source/blender/makesdna/DNA_particle_types.h @@ -75,6 +75,7 @@ typedef struct ChildParticle { float w[4]; /* interpolation weights for the above particles */ float fuv[4], foffset; /* face vertex weights and offset */ float rt; + int hull_parent; /* parent index of hull children, -1 if child is not part of the convex hull */ } ChildParticle; typedef struct ParticleTarget { @@ -123,6 +124,9 @@ typedef struct ParticleData { int hair_index; short flag; short alive; /* the life state of a particle */ + + int blend_index[4]; /* particle indices for non-simulated hair */ + float blend_weight[4]; /* blending weights for non-simulated hair */ } ParticleData; typedef struct SPHFluidSettings { @@ -240,6 +244,7 @@ typedef struct ParticleSettings { struct CurveMapping *clumpcurve; struct CurveMapping *roughcurve; float clump_noise_size; + float clump_noise_random, clump_noise_random_size; /* hair dynamics */ float bending_random; @@ -268,12 +273,16 @@ typedef struct ParticleSystem { struct ParticleSystem *next, *prev; ParticleSettings *part; /* particle settings */ + struct Key *key; /* hair goal shape keys */ + short shapenr; /* current shape key for menu or pinned */ + short pad[3]; ParticleData *particles; /* (parent) particles */ ChildParticle *child; /* child particles */ struct PTCacheEdit *edit; /* particle editmode (runtime) */ void (*free_edit)(struct PTCacheEdit *edit); /* free callback */ + struct BMEditStrands *hairedit; /* hair edit data (runtime) */ struct ParticleCacheKey **pathcache; /* path cache (runtime) */ struct ParticleCacheKey **childcache; /* child cache (runtime) */ @@ -297,6 +306,8 @@ typedef struct ParticleSystem { int seed, child_seed; int flag, totpart, totunexist, totchild, totcached, totchildcache; short recalc, target_psys, totkeyed, bakespace; + float hair_preview_factor; /* ratio of simulated to overall hairs */ + int hair_num_simulated; /* current number of hairs tagged for simulation (for update check) */ char bb_uvname[3][64]; /* billboard uv name, MAX_CUSTOMDATA_LAYER_NAME */ @@ -321,7 +332,7 @@ typedef struct ParticleSystem { struct ParticleDrawData *pdd; float dt_frac; /* current time step, as a fraction of a frame */ - float _pad; /* spare capacity */ + float pad2; } ParticleSystem; typedef enum eParticleDrawFlag { @@ -436,11 +447,14 @@ typedef enum eParticleChildFlag { } eParticleChildFlag; /* part->draw_col */ -#define PART_DRAW_COL_NONE 0 -#define PART_DRAW_COL_MAT 1 -#define PART_DRAW_COL_VEL 2 -#define PART_DRAW_COL_ACC 3 - +typedef enum eParticleDrawColorMode { + PART_DRAW_COL_NONE = 0, + PART_DRAW_COL_MAT = 1, + PART_DRAW_COL_VEL = 2, + PART_DRAW_COL_ACC = 3, + PART_DRAW_COL_PARENT = 4, + PART_DRAW_COL_TEX = 5, +} eParticleDrawColorMode; /* part->simplify_flag */ #define PART_SIMPLIFY_ENABLE 1 @@ -469,18 +483,21 @@ typedef enum eParticleChildFlag { /* part->draw_as */ /* part->ren_as*/ -#define PART_DRAW_NOT 0 -#define PART_DRAW_DOT 1 -#define PART_DRAW_HALO 1 -#define PART_DRAW_CIRC 2 -#define PART_DRAW_CROSS 3 -#define PART_DRAW_AXIS 4 -#define PART_DRAW_LINE 5 -#define PART_DRAW_PATH 6 -#define PART_DRAW_OB 7 -#define PART_DRAW_GR 8 -#define PART_DRAW_BB 9 -#define PART_DRAW_REND 10 +typedef enum eParticleDrawMethod { + PART_DRAW_NOT = 0, + PART_DRAW_DOT = 1, + PART_DRAW_HALO = 1, + PART_DRAW_CIRC = 2, + PART_DRAW_CROSS = 3, + PART_DRAW_AXIS = 4, + PART_DRAW_LINE = 5, + PART_DRAW_PATH = 6, + PART_DRAW_OB = 7, + PART_DRAW_GR = 8, + PART_DRAW_BB = 9, + PART_DRAW_REND = 10, + PART_DRAW_HULL = 11, +} eParticleDrawMethod; /* part->integrator */ #define PART_INT_EULER 0 @@ -543,11 +560,13 @@ typedef enum eParticleChildFlag { #define PSYS_DISABLED 8192 #define PSYS_OB_ANIM_RESTORE 16384 /* runtime flag */ -/* pars->flag */ -#define PARS_UNEXIST 1 -#define PARS_NO_DISP 2 -//#define PARS_STICKY 4 /* deprecated */ -#define PARS_REKEY 8 +typedef enum eParticleDataFlag { + PARS_UNEXIST = 1, + PARS_NO_DISP = 2, +// PARS_STICKY = 4, /* deprecated */ + PARS_REKEY = 8, + PARS_HAIR_BLEND = 16, /* exclude from simulation and instead blend result from surrounding hairs */ +} eParticleDataFlag; /* pars->alive */ //#define PARS_KILLED 0 /* deprecated */ @@ -606,6 +625,9 @@ typedef enum eParticleTextureInfluence { PAMAP_ROUGH = (1<<9), PAMAP_LENGTH = (1<<4), PAMAP_CHILD = (PAMAP_CLUMP | PAMAP_KINK_FREQ | PAMAP_KINK_AMP | PAMAP_ROUGH | PAMAP_LENGTH), + PAMAP_SHAPEKEY = (1<<13), /* shapekey blend multiplier */ + /* color */ + PAMAP_COLOR = (1<<14), } eParticleTextureInfluence; #endif diff --git a/source/blender/makesdna/DNA_scene_types.h b/source/blender/makesdna/DNA_scene_types.h index ef0e98a14c9..94b609e9b9f 100644 --- a/source/blender/makesdna/DNA_scene_types.h +++ b/source/blender/makesdna/DNA_scene_types.h @@ -996,6 +996,32 @@ typedef struct ParticleEditSettings { } ParticleEditSettings; /* ------------------------------------------- */ +/* Hair Edit */ + +/* HairEditSettings->select_mode */ +typedef enum HairEditSelectMode { + HAIR_SELECT_STRAND = 0, + HAIR_SELECT_VERTEX = 1, + HAIR_SELECT_TIP = 2, +} HairEditSelectMode; + +/* HairEditSettings->flag */ +typedef enum HairEditFlag { + HAIR_EDIT_SHOW_DEBUG = (1 << 16), +} HairEditFlag; + +typedef struct HairEditSettings { + int flag; + int select_mode; + + struct Brush *brush; + struct Object *shape_object; + + /* WM Paint cursor */ + void *paint_cursor; +} HairEditSettings; + +/* ------------------------------------------- */ /* Sculpt */ /* Sculpt */ @@ -1221,7 +1247,10 @@ typedef struct ToolSettings { /* Particle Editing */ struct ParticleEditSettings particle; - + + /* Hair Editing */ + struct HairEditSettings hair_edit; + /* Transform Proportional Area of Effect */ float proportional_size; @@ -1946,6 +1975,7 @@ typedef enum eGPencil_Source_3D { /* ParticleBrushData->flag */ #define PE_BRUSH_DATA_PUFF_VOLUME 1 +#define PE_BRUSH_DATA_ADD_SINGLE 2 /* tooksettings->particle edittype */ #define PE_TYPE_PARTICLES 0 diff --git a/source/blender/makesdna/DNA_screen_types.h b/source/blender/makesdna/DNA_screen_types.h index eb76c90c0fc..5a707ef7c3b 100644 --- a/source/blender/makesdna/DNA_screen_types.h +++ b/source/blender/makesdna/DNA_screen_types.h @@ -44,6 +44,7 @@ struct PanelType; struct Scene; struct uiLayout; struct wmTimer; +struct wmWidgetMap; typedef struct bScreen { ID id; @@ -68,7 +69,8 @@ typedef struct bScreen { char do_draw_drag; /* notifier for dragging draw. */ char swap; /* indicator to survive swap-exchange systems */ char skip_handling; /* set to delay screen handling after switching back from maximized area */ - char pad[7]; + char scrubbing; /* set while scrubbing to skip some updates done while animating */ + char pad[6]; short mainwin; /* screensize subwindow, for screenedges and global menus */ short subwinactive; /* active subwindow */ @@ -256,6 +258,7 @@ typedef struct ARegion { ListBase ui_lists; /* uiList */ ListBase ui_previews; /* uiPreview */ ListBase handlers; /* wmEventHandler */ + ListBase widgetmaps; /* widgetmaps */ ListBase panels_category; /* Panel categories runtime */ struct wmTimer *regiontimer; /* blend in/out */ diff --git a/source/blender/makesdna/DNA_sequence_types.h b/source/blender/makesdna/DNA_sequence_types.h index 76c27fd3547..d45467373d6 100644 --- a/source/blender/makesdna/DNA_sequence_types.h +++ b/source/blender/makesdna/DNA_sequence_types.h @@ -212,12 +212,18 @@ typedef struct MetaStack { } MetaStack; typedef struct Editing { + + /* old data format */ ListBase *seqbasep; /* pointer to the current list of seq's being edited (can be within a meta strip) */ ListBase seqbase; /* pointer to the top-most seq's */ ListBase metastack; /* Context vars, used to be static */ Sequence *act_seq; + + /* new data */ + ListBase nstripbase; + char act_imagedir[1024]; /* 1024 = FILE_MAX */ char act_sounddir[1024]; /* 1024 = FILE_MAX */ char proxy_dir[1024]; /* 1024 = FILE_MAX */ @@ -385,6 +391,8 @@ enum { /* don't include Grease Pencil in OpenGL previews of Scene strips */ SEQ_SCENE_NO_GPENCIL = (1 << 28), SEQ_USE_VIEWS = (1 << 29), + /* access scene strips directly (like a metastrip) */ + SEQ_SCENE_STRIPS = (1 << 30), SEQ_INVALID_EFFECT = (1 << 31), }; @@ -502,4 +510,212 @@ enum { SEQUENCE_MASK_INPUT_ID = 1 }; +/************************* NEW DATA TYPES **********************************/ + +#define STRIP_NAME_MAXSTR 64 + +/* nstrip->type */ +enum { + NSTRIP_TYPE_DATA = 1, + NSTRIP_TYPE_FX, + NSTRIP_TYPE_CONTAINER, +}; + +/* nstrip->classtype */ +enum { + NSTRIP_CLASS_MOVIE = 1, + NSTRIP_CLASS_MOVIECLIP, + NSTRIP_CLASS_MASK, + NSTRIP_CLASS_IMAGE, + NSTRIP_CLASS_SOUND, + NSTRIP_CLASS_SCENE, + NSTRIP_CLASS_EFFECT, + NSTRIP_CLASS_CROSS, + NSTRIP_CLASS_ADD, + NSTRIP_CLASS_SUB, + NSTRIP_CLASS_ALPHAOVER, + NSTRIP_CLASS_ALPHAUNDER, + NSTRIP_CLASS_GAMCROSS, + NSTRIP_CLASS_MUL, + NSTRIP_CLASS_OVERDROP, + NSTRIP_CLASS_WIPE, + NSTRIP_CLASS_GLOW, + NSTRIP_CLASS_TRANSFORM, + NSTRIP_CLASS_COLOR, + NSTRIP_CLASS_SPEED, + NSTRIP_CLASS_MULTICAM, + NSTRIP_CLASS_ADJUSTMENT, + NSTRIP_CLASS_GAUSSIAN_BLUR, +}; + +/* nclip->flag */ +enum { + NSTRIP_LEFTSEL = (1 << 1), + NSTRIP_RIGHTSEL = (1 << 2), + NSTRIP_OVERLAP = (1 << 3), + NSTRIP_FILTERY = (1 << 4), + NSTRIP_MUTE = (1 << 5), + NSTRIP_REVERSE_FRAMES = (1 << 6), + NSTRIP_IPO_FRAME_LOCKED = (1 << 7), + NSTRIP_EFFECT_NOT_LOADED = (1 << 8), + NSTRIP_FLAG_DELETE = (1 << 9), + NSTRIP_FLIPX = (1 << 10), + NSTRIP_FLIPY = (1 << 11), + NSTRIP_MAKE_FLOAT = (1 << 12), + NSTRIP_LOCK = (1 << 13), + NSTRIP_USE_PROXY = (1 << 14), + NSTRIP_USE_TRANSFORM = (1 << 15), + NSTRIP_USE_CROP = (1 << 16), + NSTRIP_USE_PROXY_CUSTOM_DIR = (1 << 17), + + NSTRIP_USE_PROXY_CUSTOM_FILE = (1 << 18), + NSTRIP_USE_EFFECT_DEFAULT_FADE = (1 << 19), + NSTRIP_USE_LINEAR_MODIFIERS = (1 << 20), + + /* flags for whether those properties are animated or not */ + NSTRIP_AUDIO_VOLUME_ANIMATED = (1 << 21), + NSTRIP_AUDIO_PITCH_ANIMATED = (1 << 22), + NSTRIP_AUDIO_PAN_ANIMATED = (1 << 23), + NSTRIP_AUDIO_DRAW_WAVEFORM = (1 << 24), + + NCLIP_INVALID_EFFECT = (1 << 25), +}; +/* This is the base class and it's strictly related to the visual clip in the sequencer */ +typedef struct NStrip { + struct NStrip *next, *prev; + char name[64]; /* STRIP_NAME_MAXSTR - name, set by default and needs to be unique, for RNA paths */ + + /* general use flags */ + int flag; + + /* the track this clip exists in (used to be "machine" in the old code) */ + int track; + + /* depth in the sequence (how many levels of clip inclusion deep the strip is) */ + int depth; + + /* main type, data or fx */ + short type; + + /* classtype identifier, to quickly determine the type of strip */ + short classtype; + + /* frame position in the timeline */ + int start; + int end; + + /* attachments point of clip. if the clip moves, the attachments do too */ + ListBase attachments; +} NStrip; + +/* a data clip, it includes movies, sounds or image sequences */ +typedef struct NDataStrip { + NStrip clip; + + /* length of source data (depends on source data type) */ + int len; + + /* offset of data from start of the clip */ + int offset; +} NDataStrip; + +/* a data clip, it includes movies, sounds or image sequences */ +typedef struct NFXStrip { + NStrip clip; + + /* fader of the effect */ + float effect_fader; + float pad; + + /* specialize those per fx */ + struct NStrip *clip1, *clip2, *clip3; +} NFXStrip; + +/* old metastrip, a clip that contains other clips */ +typedef struct NContainerStrip { + NStrip clip; + + /* list of contained clips */ + ListBase clips; +} NContainerStrip; + +typedef struct NMovieStrip { + NDataStrip data; + + /* render flags */ + int render_flag; + + int blend_mode; + + /* saturation */ + float saturation; + + float blend_opacity; + + /* streamindex for sound files with several streams */ + short streamindex; + + /* animation movie preseek */ + short anim_preseek; + + int pad; + + /* modifiers */ + ListBase modifiers; +} NMovieStrip; + +typedef struct NAnimStrip { + NMovieStrip movie; + + /* for amination file */ + struct anim *anim; +} NAnimStrip; + +typedef struct NTrackerStrip { + NMovieStrip movie; + + /* source MovieClip strip */ + struct MovieClip *clip; +} NTrackerStrip; + +typedef struct NMaskStrip { + NDataStrip data; + + /* source mask */ + struct Mask *mask; +} NMaskStrip; + +/* sound clip */ +typedef struct NSoundStrip { + NDataStrip data; + + /* streamindex for sound files with several streams */ + int streamindex; + + /* sound volume */ + float volume; + + /* pitch (-0.1..10) */ + float pitch; + + /* pan -2..2 */ + float pan; + + /* the linked "bSound" object */ + struct bSound *sound; +} NSoundStrip; + +/* scene strip */ +typedef struct NSceneStrip { + NDataStrip data; + + /* the linked scene */ + struct Scene *scene; + + /* override of scene camera */ + struct Object *scene_camera; +} NSceneStrip; + + + #endif /* __DNA_SEQUENCE_TYPES_H__ */ diff --git a/source/blender/makesdna/DNA_smoke_types.h b/source/blender/makesdna/DNA_smoke_types.h index 2efa23f2d9c..6ca52f3288f 100644 --- a/source/blender/makesdna/DNA_smoke_types.h +++ b/source/blender/makesdna/DNA_smoke_types.h @@ -139,6 +139,8 @@ typedef struct SmokeDomainSettings { /* Smoke uses only one cache from now on (index [0]), but keeping the array for now for reading old files. */ struct PointCache *point_cache[2]; /* definition is in DNA_object_force.h */ struct ListBase ptcaches[2]; + int point_cache_offset; + int pad; struct EffectorWeights *effector_weights; int border_collisions; /* How domain border collisions are handled */ float time_scale; @@ -152,8 +154,12 @@ typedef struct SmokeDomainSettings { float flame_ignition, flame_max_temp; float flame_smoke_color[3]; + /* display */ + float display_thickness; + int pad2; + struct ListBase vdb_caches; - short use_openvdb, pad[3]; + short use_openvdb, pad3[3]; struct OpenVDBPrimitive *density, *density_high; } SmokeDomainSettings; @@ -199,6 +205,7 @@ enum { #define MOD_SMOKE_FLOW_INITVELOCITY (1<<2) /* passes particles speed to the smoke */ #define MOD_SMOKE_FLOW_TEXTUREEMIT (1<<3) /* use texture to control emission speed */ #define MOD_SMOKE_FLOW_USE_PART_SIZE (1<<4) /* use specific size for particles instead of closest cell */ +#define MOD_SMOKE_FLOW_USE_PART_TEXCOLOR (1<<5) /* passes particles texture color to the smoke */ typedef struct SmokeFlowSettings { struct SmokeModifierData *smd; /* for fast RNA access */ diff --git a/source/blender/makesdna/DNA_space_types.h b/source/blender/makesdna/DNA_space_types.h index 9c023628164..c7071d7566c 100644 --- a/source/blender/makesdna/DNA_space_types.h +++ b/source/blender/makesdna/DNA_space_types.h @@ -46,6 +46,7 @@ #include "DNA_node_types.h" /* for bNodeInstanceKey */ /* Hum ... Not really nice... but needed for spacebuts. */ #include "DNA_view2d_types.h" +#include "DNA_widget_types.h" struct ID; struct Text; @@ -323,6 +324,11 @@ typedef struct SpaceIpo { ListBase ghostCurves; /* sampled snapshots of F-Curves used as in-session guides */ + struct Object *backdrop_camera; /* the view from this camera is used to draw the backdrop */ + float backdrop_offset[2]; /* offset of the backdrop */ + float backdrop_zoom; /* zoom factor of the backdrop */ + float backdrop_opacity; /* opacity of the backdrop */ + short mode; /* mode for the Graph editor (eGraphEdit_Mode) */ short autosnap; /* time-transform autosnapping settings for Graph editor (eAnimEdit_AutoSnap in DNA_action_types.h) */ int flag; /* settings for Graph editor (eGraphEdit_Flag) */ @@ -367,6 +373,7 @@ typedef enum eGraphEdit_Flag { /* normalize curves on display */ SIPO_NORMALIZE = (1 << 14), SIPO_NORMALIZE_FREEZE = (1 << 15), + SIPO_DRAW_BACKDROP = (1 << 16), } eGraphEdit_Flag; /* SpaceIpo->mode (Graph Editor Mode) */ @@ -498,7 +505,8 @@ typedef struct SpaceSeq { int view; /* see SEQ_VIEW_* below */ int overlay_type; int draw_flag; /* overlay an image of the editing on below the strips */ - int pad; + float overdrop_zoom; + float overdrop_offset[2]; struct bGPdata *gpd; /* grease-pencil data */ @@ -520,7 +528,7 @@ typedef enum eSpaceSeq_RegionType { /* sseq->draw_flag */ typedef enum eSpaceSeq_DrawFlag { - SEQ_DRAW_BACKDROP = (1 << 0), + SEQ_DRAW_OVERDROP = (1 << 0), SEQ_DRAW_OFFSET_EXT = (1 << 1), } eSpaceSeq_DrawFlag; @@ -538,6 +546,7 @@ typedef enum eSpaceSeq_Flag { SEQ_NO_WAVEFORMS = (1 << 8), /* draw no waveforms */ SEQ_SHOW_SAFE_CENTER = (1 << 9), SEQ_SHOW_METADATA = (1 << 10), + SEQ_NO_INFO = (1 << 11), /* do not draw names on strips */ } eSpaceSeq_Flag; /* sseq->view */ @@ -694,17 +703,19 @@ typedef enum eFileSel_Action { /* sfile->params->flag and simasel->flag */ typedef enum eFileSel_Params_Flag { - FILE_SHOWSHORT = (1 << 0), - FILE_RELPATH = (1 << 1), /* was FILE_STRINGCODE */ - FILE_LINK = (1 << 2), - FILE_HIDE_DOT = (1 << 3), - FILE_AUTOSELECT = (1 << 4), - FILE_ACTIVELAY = (1 << 5), -/* FILE_ATCURSOR = (1 << 6), */ /* deprecated */ - FILE_DIRSEL_ONLY = (1 << 7), - FILE_FILTER = (1 << 8), - FILE_BOOKMARKS = (1 << 9), - FILE_GROUP_INSTANCE = (1 << 10), + FILE_SHOWSHORT = (1 << 0), + FILE_RELPATH = (1 << 1), /* was FILE_STRINGCODE */ + FILE_LINK = (1 << 2), + FILE_HIDE_DOT = (1 << 3), + FILE_AUTOSELECT = (1 << 4), + FILE_ACTIVELAY = (1 << 5), +/* FILE_ATCURSOR = (1 << 6), */ /* deprecated */ + FILE_DIRSEL_ONLY = (1 << 7), + FILE_FILTER = (1 << 8), + FILE_BOOKMARKS = (1 << 9), + FILE_GROUP_INSTANCE = (1 << 10), + FILE_COLLAPSE_IMAGES = (1 << 11), + FILE_COLLAPSE_IMAGES_TMP = (1 << 12), } eFileSel_Params_Flag; @@ -732,6 +743,8 @@ typedef enum eDirEntry_SelectFlag { FILE_SEL_HIGHLIGHTED = (1 << 2), FILE_SEL_SELECTED = (1 << 3), FILE_SEL_EDITING = (1 << 4), + FILE_SEL_COLLAPSED = (1 << 5), + FILE_SEL_PLAYING = (1 << 6), } eDirEntry_SelectFlag; /* Image/UV Editor ======================================== */ @@ -964,11 +977,13 @@ typedef struct SpaceNode { struct ID *id, *from; /* context, no need to save in file? well... pinning... */ short flag, pad1; /* menunr: browse id block in header */ - float aspect, pad2; /* internal state variables */ + float aspect; /* internal state variables */ - float xof, yof; /* offset for drawing the backdrop */ - float zoom; /* zoom for backdrop */ float cursor[2]; /* mouse pos for drawing socketless link and adding nodes */ + + float backdrop_offset[2]; + float backdrop_zoom; + float pad; /* XXX nodetree pointer info is all in the path stack now, * remove later on and use bNodeTreePath instead. For now these variables are set when pushing/popping diff --git a/source/blender/makesdna/DNA_strands_types.h b/source/blender/makesdna/DNA_strands_types.h new file mode 100644 index 00000000000..c5d58e3439a --- /dev/null +++ b/source/blender/makesdna/DNA_strands_types.h @@ -0,0 +1,99 @@ +/* + * Copyright 2015, Blender Foundation. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file DNA_strands_types.h + * \ingroup DNA + */ + +#ifndef __DNA_STRANDS_TYPES_H__ +#define __DNA_STRANDS_TYPES_H__ + +#include "DNA_defs.h" + +#include "DNA_meshdata_types.h" + +typedef struct StrandsVertex { + float co[3]; + float base[3]; /* base shape, defining deformation for children */ + float time; + float weight; + + /* utility data */ + float nor[3]; /* normals (edge directions) */ +} StrandsVertex; + +typedef struct StrandsMotionState { + float co[3]; + float vel[3]; + + /* utility data */ + float nor[3]; /* normals (edge directions) */ +} StrandsMotionState; + +typedef struct StrandsCurve { + int numverts; + float root_matrix[3][3]; + + MSurfaceSample msurf; +} StrandsCurve; + +typedef struct Strands { + StrandsCurve *curves; + StrandsVertex *verts; + int totcurves, totverts; + + /* optional */ + StrandsMotionState *state; +} Strands; + + +typedef struct StrandsChildCurve { + int numverts; + float root_matrix[4][4]; + int parents[4]; + float parent_weights[4]; + float cutoff; /* shortens the curve if 0 <= cutoff < numverts */ +} StrandsChildCurve; + +typedef struct StrandsChildCurveUV { + float uv[2]; +} StrandsChildCurveUV; + +typedef struct StrandsChildCurveVCol { + float vcol[3]; +} StrandsChildCurveVCol; + +typedef struct StrandsChildVertex { + float co[3]; + float base[3]; /* undeformed shape */ + float time; + + /* utility data */ + float nor[3]; /* normals (edge directions) */ +} StrandsChildVertex; + +typedef struct StrandsChildren { + StrandsChildCurve *curves; + StrandsChildCurveUV *curve_uvs; + StrandsChildCurveVCol *curve_vcols; + StrandsChildVertex *verts; + int totcurves, totverts; + int numuv, numvcol; /* number of uv/vcol per curve */ +} StrandsChildren; + +#endif /* __DNA_STRANDS_TYPES_H__ */ diff --git a/source/blender/makesdna/DNA_texture_types.h b/source/blender/makesdna/DNA_texture_types.h index af6adbecd83..a1b736124a2 100644 --- a/source/blender/makesdna/DNA_texture_types.h +++ b/source/blender/makesdna/DNA_texture_types.h @@ -85,8 +85,9 @@ typedef struct MTex { /* particles */ float timefac, lengthfac, clumpfac, dampfac; float kinkfac, kinkampfac, roughfac, padensfac, gravityfac; - float lifefac, sizefac, ivelfac, fieldfac; - int pad2; + float lifefac, sizefac, ivelfac, fieldfac, pacolfac, pad2; + float shapefac; + char shapekey[64]; /* MAX_NAME */ /* lamp */ float shadowfac; @@ -596,9 +597,11 @@ enum { #define TEX_PD_COLOR_PARTAGE 1 #define TEX_PD_COLOR_PARTSPEED 2 #define TEX_PD_COLOR_PARTVEL 3 +#define TEX_PD_COLOR_PARTTEX 4 #define POINT_DATA_VEL 1 #define POINT_DATA_LIFE 2 +#define POINT_DATA_COLOR 3 /******************** Voxel Data *****************************/ /* flag */ diff --git a/source/blender/makesdna/DNA_view3d_types.h b/source/blender/makesdna/DNA_view3d_types.h index a0516590d43..6841393ab98 100644 --- a/source/blender/makesdna/DNA_view3d_types.h +++ b/source/blender/makesdna/DNA_view3d_types.h @@ -212,6 +212,9 @@ typedef struct View3D { struct ListBase afterdraw_xray; struct ListBase afterdraw_xraytransp; + /* draw after scene, does not need depth info, so different than xray */ + struct ListBase afterdraw_nodepth; + /* drawflags, denoting state */ char zbuf, transp, xray; @@ -291,6 +294,7 @@ typedef struct View3D { ((view >= RV3D_VIEW_FRONT) && (view <= RV3D_VIEW_BOTTOM)) /* View3d->flag2 (short) */ +#define V3D_WIRE_COLOR_NOCUSTOM (1 << 1) #define V3D_RENDER_OVERRIDE (1 << 2) #define V3D_SOLID_TEX (1 << 3) #define V3D_SHOW_GPENCIL (1 << 4) @@ -309,6 +313,7 @@ typedef struct View3D { /* View3d->flag3 (short) */ #define V3D_SHOW_WORLD (1 << 0) +#define V3D_HIDE_MOTIONPATHS (1 << 1) /* View3D->around */ #define V3D_CENTER 0 @@ -348,9 +353,10 @@ typedef struct View3D { /* View3d->twflag */ /* USE = user setting, DRAW = based on selection */ -#define V3D_USE_MANIPULATOR 1 -#define V3D_DRAW_MANIPULATOR 2 -/* #define V3D_CALC_MANIPULATOR 4 */ /*UNUSED*/ +#define V3D_USE_MANIPULATOR (1 << 0) +#define V3D_DRAW_MANIPULATOR (1 << 1) +#define V3D_3D_WIDGETS (1 << 2) +#define V3D_SHADED_WIDGETS (1 << 3) /* BGPic->flag */ /* may want to use 1 for select ? */ @@ -371,6 +377,13 @@ enum { #define V3D_BGPIC_EXPANDED (V3D_BGPIC_EXPANDED | V3D_BGPIC_CAMERACLIP) +#define V3D_IS_WIRECOLOR(scene, v3d) \ + (((v3d)->drawtype <= OB_SOLID) && \ + (((v3d)->flag2 & V3D_WIRE_COLOR_NOCUSTOM) == 0)) + +#define V3D_IS_WIRECOLOR_OBJECT(scene, v3d, ob) \ + V3D_IS_WIRECOLOR(scene, v3d) && ((ob)->dtx & OB_DRAW_WIRECOLOR) + /* BGPic->source */ /* may want to use 1 for select ?*/ #define V3D_BGPIC_IMAGE 0 diff --git a/source/blender/makesdna/DNA_widget_types.h b/source/blender/makesdna/DNA_widget_types.h new file mode 100644 index 00000000000..5f261f52835 --- /dev/null +++ b/source/blender/makesdna/DNA_widget_types.h @@ -0,0 +1,50 @@ +/* + * ***** 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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 LICENSE BLOCK ***** + */ + +/** \file DNA_widget_types.h + * \ingroup DNA + */ + +#ifndef __DNA_WM_WIDGET_TYPES_H__ +#define __DNA_WM_WIDGET_TYPES_H__ + +#include "DNA_vec_types.h" + +struct wmWidgetGroup { + struct wmWidgetGroup *next, *prev; + + struct wmWidgetGroupType *type; + ListBase widgets; + + void *py_instance; /* python stores the class instance here */ + struct ReportList *reports; /* errors and warnings storage */ + + int flag; + int pad; +}; + +#endif diff --git a/source/blender/makesdna/DNA_windowmanager_types.h b/source/blender/makesdna/DNA_windowmanager_types.h index 51ed8bb6c0d..9c089ce0021 100644 --- a/source/blender/makesdna/DNA_windowmanager_types.h +++ b/source/blender/makesdna/DNA_windowmanager_types.h @@ -155,7 +155,8 @@ typedef struct wmWindowManager { struct wmTimer *autosavetimer; /* timer for auto save */ char is_interface_locked; /* indicates whether interface is locked for user interaction */ - char par[7]; + char par[3]; + int num_ogl_widgets; /* widgets requiring an OpenGL pass to detect */ } wmWindowManager; /* wmWindowManager.initialized */ diff --git a/source/blender/makesdna/intern/makesdna.c b/source/blender/makesdna/intern/makesdna.c index a7ff4244d5f..6333db331fb 100644 --- a/source/blender/makesdna/intern/makesdna.c +++ b/source/blender/makesdna/intern/makesdna.c @@ -125,6 +125,7 @@ static const char *includefiles[] = { /* if you add files here, please add them at the end * of makesdna.c (this file) as well */ "DNA_windowmanager_types.h", + "DNA_widget_types.h", "DNA_anim_types.h", "DNA_boid_types.h", "DNA_smoke_types.h", @@ -136,6 +137,8 @@ static const char *includefiles[] = { "DNA_rigidbody_types.h", "DNA_freestyle_types.h", "DNA_linestyle_types.h", + "DNA_cache_library_types.h", + "DNA_strands_types.h", /* empty string to indicate end of includefiles */ "" @@ -1281,6 +1284,7 @@ int main(int argc, char **argv) #include "DNA_cloth_types.h" #include "DNA_gpencil_types.h" #include "DNA_windowmanager_types.h" +#include "DNA_widget_types.h" #include "DNA_anim_types.h" #include "DNA_boid_types.h" #include "DNA_smoke_types.h" @@ -1292,4 +1296,6 @@ int main(int argc, char **argv) #include "DNA_rigidbody_types.h" #include "DNA_freestyle_types.h" #include "DNA_linestyle_types.h" +#include "DNA_cache_library_types.h" +#include "DNA_strands_types.h" /* end of list */ diff --git a/source/blender/makesrna/RNA_access.h b/source/blender/makesrna/RNA_access.h index f03e28ddd41..acbaae167db 100644 --- a/source/blender/makesrna/RNA_access.h +++ b/source/blender/makesrna/RNA_access.h @@ -95,6 +95,11 @@ extern StructRNA RNA_Brush; extern StructRNA RNA_BrushTextureSlot; extern StructRNA RNA_BuildModifier; extern StructRNA RNA_MeshCacheModifier; +extern StructRNA RNA_CacheArchiveInfo; +extern StructRNA RNA_CacheArchiveInfoNode; +extern StructRNA RNA_CacheItem; +extern StructRNA RNA_CacheLibrary; +extern StructRNA RNA_CacheLibraryModifier; extern StructRNA RNA_Camera; extern StructRNA RNA_CastModifier; extern StructRNA RNA_ChildOfConstraint; @@ -228,6 +233,7 @@ extern StructRNA RNA_EnvironmentMapTexture; extern StructRNA RNA_Event; extern StructRNA RNA_ExplodeModifier; extern StructRNA RNA_ExpressionController; +extern StructRNA RNA_FaceMap; extern StructRNA RNA_FCurve; extern StructRNA RNA_FCurveSample; extern StructRNA RNA_FFmpegSettings; diff --git a/source/blender/makesrna/RNA_enum_types.h b/source/blender/makesrna/RNA_enum_types.h index d3d3578e8b0..ea94056217c 100644 --- a/source/blender/makesrna/RNA_enum_types.h +++ b/source/blender/makesrna/RNA_enum_types.h @@ -56,6 +56,7 @@ extern EnumPropertyItem mesh_delimit_mode_items[]; extern EnumPropertyItem space_type_items[]; extern EnumPropertyItem region_type_items[]; extern EnumPropertyItem modifier_type_items[]; +extern EnumPropertyItem cache_modifier_type_items[]; extern EnumPropertyItem constraint_type_items[]; extern EnumPropertyItem boidrule_type_items[]; extern EnumPropertyItem sequence_modifier_type_items[]; @@ -109,6 +110,7 @@ extern EnumPropertyItem operator_return_items[]; extern EnumPropertyItem brush_sculpt_tool_items[]; extern EnumPropertyItem brush_vertex_tool_items[]; extern EnumPropertyItem brush_image_tool_items[]; +extern EnumPropertyItem brush_hair_tool_items[]; extern EnumPropertyItem symmetrize_direction_items[]; @@ -181,6 +183,9 @@ extern EnumPropertyItem prop_dynamicpaint_type_items[]; extern EnumPropertyItem clip_editor_mode_items[]; +extern EnumPropertyItem cache_library_data_type_items[]; +extern EnumPropertyItem cache_library_read_result_items[]; + extern EnumPropertyItem icon_items[]; extern EnumPropertyItem uilist_layout_type_items[]; @@ -220,5 +225,7 @@ EnumPropertyItem *RNA_movieclip_itemf(struct bContext *C, struct PointerRNA *ptr EnumPropertyItem *RNA_movieclip_local_itemf(struct bContext *C, struct PointerRNA *ptr, struct PropertyRNA *prop, bool *r_free); EnumPropertyItem *RNA_mask_itemf(struct bContext *C, struct PointerRNA *ptr, struct PropertyRNA *prop, bool *r_free); EnumPropertyItem *RNA_mask_local_itemf(struct bContext *C, struct PointerRNA *ptr, struct PropertyRNA *prop, bool *r_free); +EnumPropertyItem *RNA_cachelibrary_itemf(struct bContext *C, struct PointerRNA *ptr, struct PropertyRNA *prop, bool *r_free); +EnumPropertyItem *RNA_cachelibrary_local_itemf(struct bContext *C, struct PointerRNA *ptr, struct PropertyRNA *prop, bool *r_free); #endif /* __RNA_ENUM_TYPES_H__ */ diff --git a/source/blender/makesrna/intern/CMakeLists.txt b/source/blender/makesrna/intern/CMakeLists.txt index 5bdebf2891d..a05c1834ed1 100644 --- a/source/blender/makesrna/intern/CMakeLists.txt +++ b/source/blender/makesrna/intern/CMakeLists.txt @@ -38,6 +38,7 @@ set(DEFSRC rna_armature.c rna_boid.c rna_brush.c + rna_cache_library.c rna_camera.c rna_cloth.c rna_color.c @@ -60,6 +61,7 @@ set(DEFSRC rna_mask.c rna_material.c rna_mesh.c + rna_mesh_sample.c rna_meta.c rna_modifier.c rna_movieclip.c @@ -84,6 +86,7 @@ set(DEFSRC rna_sound.c rna_space.c rna_speaker.c + rna_strands.c rna_test.c rna_text.c rna_texture.c diff --git a/source/blender/makesrna/intern/makesrna.c b/source/blender/makesrna/intern/makesrna.c index dfd0f13ec1a..0c2082c9597 100644 --- a/source/blender/makesrna/intern/makesrna.c +++ b/source/blender/makesrna/intern/makesrna.c @@ -3287,6 +3287,7 @@ static RNAProcessItem PROCESS_ITEMS[] = { {"rna_boid.c", NULL, RNA_def_boid}, {"rna_brush.c", NULL, RNA_def_brush}, {"rna_camera.c", "rna_camera_api.c", RNA_def_camera}, + {"rna_cache_library.c", NULL, RNA_def_cache_library}, {"rna_cloth.c", NULL, RNA_def_cloth}, {"rna_color.c", NULL, RNA_def_color}, {"rna_constraint.c", NULL, RNA_def_constraint}, @@ -3307,6 +3308,7 @@ static RNAProcessItem PROCESS_ITEMS[] = { {"rna_main.c", "rna_main_api.c", RNA_def_main}, {"rna_material.c", "rna_material_api.c", RNA_def_material}, {"rna_mesh.c", "rna_mesh_api.c", RNA_def_mesh}, + {"rna_mesh_sample.c", NULL, RNA_def_mesh_sample}, {"rna_meta.c", "rna_meta_api.c", RNA_def_meta}, {"rna_modifier.c", NULL, RNA_def_modifier}, {"rna_nla.c", NULL, RNA_def_nla}, @@ -3328,6 +3330,7 @@ static RNAProcessItem PROCESS_ITEMS[] = { {"rna_smoke.c", NULL, RNA_def_smoke}, {"rna_space.c", "rna_space_api.c", RNA_def_space}, {"rna_speaker.c", NULL, RNA_def_speaker}, + {"rna_strands.c", NULL, RNA_def_strands}, {"rna_test.c", NULL, RNA_def_test}, {"rna_text.c", "rna_text_api.c", RNA_def_text}, {"rna_timeline.c", NULL, RNA_def_timeline_marker}, diff --git a/source/blender/makesrna/intern/rna_ID.c b/source/blender/makesrna/intern/rna_ID.c index b87b455b36f..d29450aa320 100644 --- a/source/blender/makesrna/intern/rna_ID.c +++ b/source/blender/makesrna/intern/rna_ID.c @@ -52,6 +52,7 @@ EnumPropertyItem id_type_items[] = { {ID_AR, "ARMATURE", ICON_ARMATURE_DATA, "Armature", ""}, {ID_BR, "BRUSH", ICON_BRUSH_DATA, "Brush", ""}, {ID_CA, "CAMERA", ICON_CAMERA_DATA, "Camera", ""}, + {ID_CL, "CACHELIBRARY", ICON_PHYSICS, "Cache Library", ""}, {ID_CU, "CURVE", ICON_CURVE_DATA, "Curve", ""}, {ID_VF, "FONT", ICON_FONT_DATA, "Font", ""}, {ID_GD, "GREASEPENCIL", ICON_GREASEPENCIL, "Grease Pencil", ""}, @@ -132,6 +133,7 @@ short RNA_type_to_ID_code(StructRNA *type) if (RNA_struct_is_a(type, &RNA_Action)) return ID_AC; if (RNA_struct_is_a(type, &RNA_Armature)) return ID_AR; if (RNA_struct_is_a(type, &RNA_Brush)) return ID_BR; + if (RNA_struct_is_a(type, &RNA_CacheLibrary)) return ID_CL; if (RNA_struct_is_a(type, &RNA_Camera)) return ID_CA; if (RNA_struct_is_a(type, &RNA_Curve)) return ID_CU; if (RNA_struct_is_a(type, &RNA_GreasePencil)) return ID_GD; @@ -172,6 +174,7 @@ StructRNA *ID_code_to_RNA_type(short idcode) case ID_AR: return &RNA_Armature; case ID_BR: return &RNA_Brush; case ID_CA: return &RNA_Camera; + case ID_CL: return &RNA_CacheLibrary; case ID_CU: return &RNA_Curve; case ID_GD: return &RNA_GreasePencil; case ID_GR: return &RNA_Group; diff --git a/source/blender/makesrna/intern/rna_brush.c b/source/blender/makesrna/intern/rna_brush.c index e9e4282772d..f6e60471a58 100644 --- a/source/blender/makesrna/intern/rna_brush.c +++ b/source/blender/makesrna/intern/rna_brush.c @@ -107,6 +107,17 @@ EnumPropertyItem brush_image_tool_items[] = { {0, NULL, 0, NULL, NULL} }; +EnumPropertyItem brush_hair_tool_items[] = { + {HAIR_TOOL_COMB, "COMB", ICON_BRUSH_HAIR_COMB, "Comb", "Align hairs to the stroke direction"}, + {HAIR_TOOL_CUT, "CUT", ICON_BRUSH_HAIR_CUT, "Cut", "Shorten and/or remove hairs"}, + {HAIR_TOOL_LENGTH, "LENGTH", ICON_BRUSH_HAIR_LENGTH, "Length", "Increase hair length"}, + {HAIR_TOOL_PUFF, "PUFF", ICON_BRUSH_HAIR_PUFF, "Puff", "Increase spacing between hairs"}, + {HAIR_TOOL_ADD, "ADD", ICON_BRUSH_HAIR_ADD, "Add", "Add more hairs on the object"}, + {HAIR_TOOL_SMOOTH, "SMOOTH", ICON_BRUSH_HAIR_SMOOTH, "Smooth", "Align hairs in the same direction"}, + {HAIR_TOOL_WEIGHT, "WEIGHT", ICON_BRUSH_HAIR_WEIGHT, "Weight", "Set hair vertex weights"}, + {0, NULL, 0, NULL, NULL} +}; + #ifdef RNA_RUNTIME #include "MEM_guardedalloc.h" @@ -397,6 +408,13 @@ static void rna_Brush_imagepaint_tool_update(Main *bmain, Scene *scene, PointerR rna_Brush_update(bmain, scene, ptr); } +static void rna_Brush_hair_tool_update(Main *bmain, Scene *scene, PointerRNA *ptr) +{ + Brush *br = (Brush *)ptr->data; + rna_Brush_reset_icon(br, "hair"); + rna_Brush_update(bmain, scene, ptr); +} + static void rna_Brush_stroke_update(Main *bmain, Scene *scene, PointerRNA *ptr) { WM_main_add_notifier(NC_SCENE | ND_TOOLSETTINGS, scene); @@ -879,6 +897,12 @@ static void rna_def_brush(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Image Paint Tool", ""); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_IMAGE, "rna_Brush_imagepaint_tool_update"); + prop = RNA_def_property(srna, "hair_tool", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "hair_tool"); + RNA_def_property_enum_items(prop, brush_hair_tool_items); + RNA_def_property_ui_text(prop, "Hair Tool", ""); + RNA_def_property_update(prop, 0, "rna_Brush_hair_tool_update"); + prop = RNA_def_property(srna, "direction", PROP_ENUM, PROP_NONE); RNA_def_property_enum_bitflag_sdna(prop, NULL, "flag"); RNA_def_property_enum_items(prop, prop_direction_items); @@ -1321,6 +1345,10 @@ static void rna_def_brush(BlenderRNA *brna) RNA_def_property_boolean_sdna(prop, NULL, "ob_mode", OB_MODE_TEXTURE_PAINT); RNA_def_property_ui_text(prop, "Use Texture", "Use this brush in texture paint mode"); + prop = RNA_def_property(srna, "use_hair_edit", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "ob_mode", OB_MODE_HAIR_EDIT); + RNA_def_property_ui_text(prop, "Use Hair", "Use this brush in hair edit mode"); + /* texture */ prop = RNA_def_property(srna, "texture_slot", PROP_POINTER, PROP_NONE); RNA_def_property_struct_type(prop, "BrushTextureSlot"); diff --git a/source/blender/makesrna/intern/rna_cache_library.c b/source/blender/makesrna/intern/rna_cache_library.c new file mode 100644 index 00000000000..4c8b8835491 --- /dev/null +++ b/source/blender/makesrna/intern/rna_cache_library.c @@ -0,0 +1,1049 @@ +/* + * ***** 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Contributor(s): Blender Foundation (2015). + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/makesrna/intern/rna_cache_library.c + * \ingroup RNA + */ + +#include <stdlib.h> +#include <assert.h> +#include <string.h> + +#include "DNA_cache_library_types.h" + +#include "RNA_define.h" +#include "RNA_enum_types.h" + +#include "rna_internal.h" + +#include "WM_types.h" + +EnumPropertyItem cache_library_data_type_items[] = { + {CACHE_TYPE_OBJECT, "OBJECT", ICON_OBJECT_DATA, "Object", "Object base properties"}, + {CACHE_TYPE_DERIVED_MESH, "DERIVED_MESH", ICON_OUTLINER_OB_MESH, "Derived Mesh", "Mesh result from modifiers"}, + {CACHE_TYPE_HAIR, "HAIR", ICON_PARTICLE_POINT, "Hair", "Hair parent strands"}, + {CACHE_TYPE_HAIR_PATHS, "HAIR_PATHS", ICON_PARTICLE_PATH, "Hair Paths", "Full hair paths"}, + {CACHE_TYPE_PARTICLES, "PARTICLES", ICON_PARTICLES, "Particles", "Emitter particles"}, + {0, NULL, 0, NULL, NULL} +}; + +EnumPropertyItem cache_library_read_result_items[] = { + {CACHE_READ_SAMPLE_INVALID, "INVALID", ICON_ERROR, "Invalid", "No valid sample found"}, + {CACHE_READ_SAMPLE_EXACT, "EXACT", ICON_SPACE3, "Exact", "Found sample for requested frame"}, + {CACHE_READ_SAMPLE_INTERPOLATED, "INTERPOLATED", ICON_TRIA_DOWN_BAR, "Interpolated", "Enclosing samples found for interpolation"}, + {CACHE_READ_SAMPLE_EARLY, "EARLY", ICON_TRIA_RIGHT_BAR, "Early", "Requested frame before the first sample"}, + {CACHE_READ_SAMPLE_LATE, "LATE", ICON_TRIA_LEFT_BAR, "Late", "Requested frame after the last sample"}, + {0, NULL, 0, NULL, NULL} +}; + +EnumPropertyItem cache_modifier_type_items[] = { + {eCacheModifierType_HairSimulation, "HAIR_SIMULATION", ICON_HAIR, "Hair Simulation", ""}, + {eCacheModifierType_ForceField, "FORCE_FIELD", ICON_FORCE_FORCE, "Force Field", ""}, + {eCacheModifierType_ShrinkWrap, "SHRINK_WRAP", ICON_MOD_SHRINKWRAP, "Shrink Wrap", ""}, + {eCacheModifierType_StrandsKey, "STRANDS_KEY", ICON_SHAPEKEY_DATA, "Strands Key", "Shape key for strands"}, + {eCacheModifierType_Haircut, "HAIRCUT", ICON_HAIR, "Hair Cut", "Cut strands where they intersect with an object"}, + {0, NULL, 0, NULL, NULL} +}; + +#ifdef RNA_RUNTIME + +#include "MEM_guardedalloc.h" + +#include "BLI_listbase.h" +#include "BLI_math.h" +#include "BLI_string.h" + +#include "DNA_key_types.h" +#include "DNA_object_types.h" +#include "DNA_particle_types.h" + +#include "BKE_animsys.h" +#include "BKE_cache_library.h" +#include "BKE_depsgraph.h" +#include "BKE_main.h" + +#include "RNA_access.h" + +#include "WM_api.h" + +/* ========================================================================= */ + +static void rna_CacheLibrary_update(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *ptr) +{ + CacheLibrary *cachelib = ptr->data; + DAG_id_tag_update(&cachelib->id, OB_RECALC_DATA); + WM_main_add_notifier(NC_WINDOW, NULL); +} + +static void rna_CacheArchiveInfo_update(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *UNUSED(ptr)) +{ +// CacheLibrary *cachelib = ptr->id.data; +// WM_main_add_notifier(NC_OBJECT | ND_DRAW, NULL); +} + +/* ========================================================================= */ + +static void rna_CacheModifier_update(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *UNUSED(ptr)) +{ +} + +#if 0 /* unused */ +static void rna_CacheModifier_dependency_update(Main *bmain, Scene *scene, PointerRNA *ptr) +{ + rna_CacheModifier_update(bmain, scene, ptr); + DAG_relations_tag_update(bmain); +} +#endif + + +static StructRNA *rna_CacheModifier_refine(struct PointerRNA *ptr) +{ + CacheModifier *md = (CacheModifier *)ptr->data; + + switch ((eCacheModifier_Type)md->type) { + case eCacheModifierType_HairSimulation: + return &RNA_HairSimulationCacheModifier; + case eCacheModifierType_ForceField: + return &RNA_ForceFieldCacheModifier; + case eCacheModifierType_ShrinkWrap: + return &RNA_ShrinkWrapCacheModifier; + case eCacheModifierType_StrandsKey: + return &RNA_StrandsKeyCacheModifier; + case eCacheModifierType_Haircut: + return &RNA_HaircutCacheModifier; + + /* Default */ + case eCacheModifierType_None: + case NUM_CACHE_MODIFIER_TYPES: + return &RNA_CacheLibraryModifier; + } + + return &RNA_CacheLibraryModifier; +} + +static void rna_CacheLibraryModifier_name_set(PointerRNA *ptr, const char *value) +{ + CacheModifier *md = ptr->data; + char oldname[sizeof(md->name)]; + + /* make a copy of the old name first */ + BLI_strncpy(oldname, md->name, sizeof(md->name)); + + /* copy the new name into the name slot */ + BLI_strncpy_utf8(md->name, value, sizeof(md->name)); + + /* make sure the name is truly unique */ + if (ptr->id.data) { + CacheLibrary *cachelib = ptr->id.data; + BKE_cache_modifier_unique_name(&cachelib->modifiers, md); + } + + /* fix all the animation data which may link to this */ + BKE_animdata_fix_paths_rename_all(NULL, "modifiers", oldname, md->name); +} + +static char *rna_CacheLibraryModifier_path(PointerRNA *ptr) +{ + CacheModifier *md = ptr->data; + char name_esc[sizeof(md->name) * 2]; + + BLI_strescape(name_esc, md->name, sizeof(name_esc)); + return BLI_sprintfN("modifiers[\"%s\"]", name_esc); +} + +static CacheModifier *rna_CacheLibrary_modifier_new(CacheLibrary *cachelib, bContext *UNUSED(C), ReportList *UNUSED(reports), + const char *name, int type) +{ + return BKE_cache_modifier_add(cachelib, name, type); +} + +static void rna_CacheLibrary_modifier_remove(CacheLibrary *cachelib, bContext *UNUSED(C), ReportList *UNUSED(reports), PointerRNA *md_ptr) +{ + CacheModifier *md = md_ptr->data; + + BKE_cache_modifier_remove(cachelib, md); + + RNA_POINTER_INVALIDATE(md_ptr); +} + +static void rna_CacheLibrary_modifier_clear(CacheLibrary *cachelib, bContext *UNUSED(C)) +{ + BKE_cache_modifier_clear(cachelib); +} + +/* ------------------------------------------------------------------------- */ + +static int rna_CacheLibraryModifier_mesh_object_poll(PointerRNA *UNUSED(ptr), PointerRNA value) +{ + /*HairSimCacheModifier *hsmd = ptr->data;*/ + Object *ob = value.data; + + return ob->type == OB_MESH && ob->data != NULL; +} + +static int rna_CacheLibraryModifier_hair_object_poll(PointerRNA *UNUSED(ptr), PointerRNA value) +{ + /*HairSimCacheModifier *hsmd = ptr->data;*/ + Object *ob = value.data; + ParticleSystem *psys; + bool has_hair_system = false; + + for (psys = ob->particlesystem.first; psys; psys = psys->next) { + if (psys->part && psys->part->type == PART_HAIR) { + has_hair_system = true; + break; + } + } + return has_hair_system; +} + +static PointerRNA rna_HairSimulationCacheModifier_hair_system_get(PointerRNA *ptr) +{ + HairSimCacheModifier *hsmd = ptr->data; + ParticleSystem *psys = hsmd->object ? BLI_findlink(&hsmd->object->particlesystem, hsmd->hair_system) : NULL; + PointerRNA value; + + RNA_pointer_create(ptr->id.data, &RNA_ParticleSystem, psys, &value); + return value; +} + +static void rna_HairSimulationCacheModifier_hair_system_set(PointerRNA *ptr, PointerRNA value) +{ + HairSimCacheModifier *hsmd = ptr->data; + ParticleSystem *psys = value.data; + hsmd->hair_system = hsmd->object ? BLI_findindex(&hsmd->object->particlesystem, psys) : -1; +} + +static int rna_HairSimulationCacheModifier_hair_system_poll(PointerRNA *ptr, PointerRNA value) +{ + HairSimCacheModifier *hsmd = ptr->data; + ParticleSystem *psys = value.data; + + if (!hsmd->object) + return false; + if (BLI_findindex(&hsmd->object->particlesystem, psys) == -1) + return false; + if (!psys->part || psys->part->type != PART_HAIR) + return false; + return true; +} + +static PointerRNA rna_ShrinkWrapCacheModifier_hair_system_get(PointerRNA *ptr) +{ + ShrinkWrapCacheModifier *smd = ptr->data; + ParticleSystem *psys = smd->object ? BLI_findlink(&smd->object->particlesystem, smd->hair_system) : NULL; + PointerRNA value; + + RNA_pointer_create(ptr->id.data, &RNA_ParticleSystem, psys, &value); + return value; +} + +static void rna_ShrinkWrapCacheModifier_hair_system_set(PointerRNA *ptr, PointerRNA value) +{ + ShrinkWrapCacheModifier *smd = ptr->data; + ParticleSystem *psys = value.data; + smd->hair_system = smd->object ? BLI_findindex(&smd->object->particlesystem, psys) : -1; +} + +static int rna_ShrinkWrapCacheModifier_hair_system_poll(PointerRNA *ptr, PointerRNA value) +{ + ShrinkWrapCacheModifier *smd = ptr->data; + ParticleSystem *psys = value.data; + + if (!smd->object) + return false; + if (BLI_findindex(&smd->object->particlesystem, psys) == -1) + return false; + if (!psys->part || psys->part->type != PART_HAIR) + return false; + return true; +} + +static PointerRNA rna_StrandsKeyCacheModifier_hair_system_get(PointerRNA *ptr) +{ + StrandsKeyCacheModifier *skmd = ptr->data; + ParticleSystem *psys = skmd->object ? BLI_findlink(&skmd->object->particlesystem, skmd->hair_system) : NULL; + PointerRNA value; + + RNA_pointer_create(ptr->id.data, &RNA_ParticleSystem, psys, &value); + return value; +} + +static void rna_StrandsKeyCacheModifier_hair_system_set(PointerRNA *ptr, PointerRNA value) +{ + StrandsKeyCacheModifier *skmd = ptr->data; + ParticleSystem *psys = value.data; + skmd->hair_system = skmd->object ? BLI_findindex(&skmd->object->particlesystem, psys) : -1; +} + +static int rna_StrandsKeyCacheModifier_hair_system_poll(PointerRNA *ptr, PointerRNA value) +{ + StrandsKeyCacheModifier *skmd = ptr->data; + ParticleSystem *psys = value.data; + + if (!skmd->object) + return false; + if (BLI_findindex(&skmd->object->particlesystem, psys) == -1) + return false; + if (!psys->part || psys->part->type != PART_HAIR) + return false; + return true; +} + +static void rna_StrandsKeyCacheModifier_active_shape_update(Main *bmain, Scene *scene, PointerRNA *ptr) +{ +#if 0 // TODO + StrandsKeyCacheModifier *skmd = ptr->data; + + if (scene->obedit == ob) { + /* exit/enter editmode to get new shape */ + switch (ob->type) { + case OB_MESH: + EDBM_mesh_load(ob); + EDBM_mesh_make(scene->toolsettings, ob); + EDBM_mesh_normals_update(((Mesh *)ob->data)->edit_btmesh); + BKE_editmesh_tessface_calc(((Mesh *)ob->data)->edit_btmesh); + break; + case OB_CURVE: + case OB_SURF: + load_editNurb(ob); + make_editNurb(ob); + break; + case OB_LATTICE: + load_editLatt(ob); + make_editLatt(ob); + break; + } + } +#endif + + rna_CacheModifier_update(bmain, scene, ptr); +} + +static void rna_StrandsKeyCacheModifier_active_shape_key_index_range(PointerRNA *ptr, int *min, int *max, int *UNUSED(softmin), int *UNUSED(softmax)) +{ + StrandsKeyCacheModifier *skmd = ptr->data; + Key *key = skmd->key; + + *min = 0; + if (key) { + *max = BLI_listbase_count(&key->block) - 1; + if (*max < 0) *max = 0; + } + else { + *max = 0; + } +} + +static int rna_StrandsKeyCacheModifier_active_shape_key_index_get(PointerRNA *ptr) +{ + StrandsKeyCacheModifier *skmd = ptr->data; + + return max_ii(skmd->shapenr - 1, 0); +} + +static void rna_StrandsKeyCacheModifier_active_shape_key_index_set(PointerRNA *ptr, int value) +{ + StrandsKeyCacheModifier *skmd = ptr->data; + + skmd->shapenr = value + 1; +} + +static PointerRNA rna_StrandsKeyCacheModifier_active_shape_key_get(PointerRNA *ptr) +{ + StrandsKeyCacheModifier *skmd = ptr->data; + Key *key = skmd->key; + KeyBlock *kb; + PointerRNA keyptr; + + if (key == NULL) + return PointerRNA_NULL; + + kb = BLI_findlink(&key->block, skmd->shapenr - 1); + RNA_pointer_create((ID *)key, &RNA_ShapeKey, kb, &keyptr); + return keyptr; +} + +static PointerRNA rna_HaircutCacheModifier_hair_system_get(PointerRNA *ptr) +{ + HaircutCacheModifier *hmd = ptr->data; + ParticleSystem *psys = hmd->object ? BLI_findlink(&hmd->object->particlesystem, hmd->hair_system) : NULL; + PointerRNA value; + + RNA_pointer_create(ptr->id.data, &RNA_ParticleSystem, psys, &value); + return value; +} + +static void rna_HaircutCacheModifier_hair_system_set(PointerRNA *ptr, PointerRNA value) +{ + HaircutCacheModifier *hmd = ptr->data; + ParticleSystem *psys = value.data; + hmd->hair_system = hmd->object ? BLI_findindex(&hmd->object->particlesystem, psys) : -1; +} + +static int rna_HaircutCacheModifier_hair_system_poll(PointerRNA *ptr, PointerRNA value) +{ + HaircutCacheModifier *hmd = ptr->data; + ParticleSystem *psys = value.data; + + if (!hmd->object) + return false; + if (BLI_findindex(&hmd->object->particlesystem, psys) == -1) + return false; + if (!psys->part || psys->part->type != PART_HAIR) + return false; + return true; +} + +static void rna_CacheArchiveInfoNode_bytes_size_get(PointerRNA *ptr, char *value) +{ + CacheArchiveInfoNode *node = ptr->data; + BLI_snprintf(value, MAX_NAME, "%lld", (long long int)node->bytes_size); +} + +static int rna_CacheArchiveInfoNode_bytes_size_length(PointerRNA *ptr) +{ + char buf[MAX_NAME]; + /* theoretically could do a dummy BLI_snprintf here, but BLI does not allow NULL buffer ... */ + CacheArchiveInfoNode *node = ptr->data; + return BLI_snprintf(buf, sizeof(buf), "%lld", (long long int)node->bytes_size); +} + +#else + +static void rna_def_hair_sim_params(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, "HairSimulationParameters", NULL); + RNA_def_struct_sdna(srna, "HairSimParams"); + RNA_def_struct_ui_text(srna, "Hair Simulation Parameters", "Simulation parameters for hair simulation"); + RNA_def_struct_ui_icon(srna, ICON_HAIR); + + prop = RNA_def_property(srna, "timescale", PROP_FLOAT, PROP_NONE); + RNA_def_property_range(prop, 0.0f, FLT_MAX); + RNA_def_property_ui_range(prop, 0.0f, 10.0f, 0.1f, 3); + RNA_def_property_ui_text(prop, "Time Scale", "Simulation time scale relative to scene time"); + RNA_def_property_update(prop, 0, "rna_CacheModifier_update"); + + prop = RNA_def_property(srna, "substeps", PROP_INT, PROP_NONE); + RNA_def_property_range(prop, 1, 80); + RNA_def_property_ui_text(prop, "Substeps", "Simulation steps per frame"); + RNA_def_property_update(prop, 0, "rna_CacheModifier_update"); + + prop = RNA_def_property(srna, "effector_weights", PROP_POINTER, PROP_NONE); + RNA_def_property_struct_type(prop, "EffectorWeights"); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text(prop, "Effector Weights", ""); + + prop = RNA_def_property(srna, "mass", PROP_FLOAT, PROP_NONE); + RNA_def_property_range(prop, 0.0f, 10.0f); + RNA_def_property_ui_text(prop, "Mass", "Mass of hair vertices"); + RNA_def_property_update(prop, 0, "rna_CacheModifier_update"); + + prop = RNA_def_property(srna, "drag", PROP_FLOAT, PROP_NONE); + RNA_def_property_range(prop, 0.0f, FLT_MAX); + RNA_def_property_ui_range(prop, 0.0f, 10.0f, 0.1f, 3); + RNA_def_property_ui_text(prop, "Drag", "Drag simulating friction with surrounding air"); + RNA_def_property_update(prop, 0, "rna_CacheModifier_update"); + + prop = RNA_def_property(srna, "goal_stiffness", PROP_FLOAT, PROP_NONE); + RNA_def_property_range(prop, 0.0f, FLT_MAX); + RNA_def_property_ui_range(prop, 0.0f, 10.0f, 0.1f, 3); + RNA_def_property_float_default(prop, 0.0f); + RNA_def_property_ui_text(prop, "Goal Strength", "Goal spring, pulling vertices toward their rest position"); + RNA_def_property_update(prop, 0, "rna_CacheModifier_update"); + + prop = RNA_def_property(srna, "goal_damping", PROP_FLOAT, PROP_FACTOR); + RNA_def_property_range(prop, 0.0f, FLT_MAX); + RNA_def_property_ui_range(prop, 0.0f, 1.0f, 0.1f, 3); + RNA_def_property_float_default(prop, 1.0f); + RNA_def_property_ui_text(prop, "Goal Damping", "Damping factor of goal springs"); + RNA_def_property_update(prop, 0, "rna_CacheModifier_update"); + + prop = RNA_def_property(srna, "use_goal_stiffness_curve", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", eHairSimParams_Flag_UseGoalStiffnessCurve); + RNA_def_property_ui_text(prop, "Use Goal Stiffness Curve", "Use a curve to define goal stiffness along the strand"); + RNA_def_property_update(prop, 0, "rna_CacheModifier_update"); + + prop = RNA_def_property(srna, "goal_stiffness_curve", PROP_POINTER, PROP_NONE); + RNA_def_property_pointer_sdna(prop, NULL, "goal_stiffness_mapping"); + RNA_def_property_struct_type(prop, "CurveMapping"); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text(prop, "Goal Stiffness Curve", "Stiffness of goal springs along the strand curves"); + RNA_def_property_update(prop, 0, "rna_CacheModifier_update"); + + prop = RNA_def_property(srna, "use_goal_deflect", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", eHairSimParams_Flag_UseGoalDeflect); + RNA_def_property_ui_text(prop, "Use Goal Deflect", "Disable goal springs inside deflectors, to avoid unstable deformations"); + RNA_def_property_update(prop, 0, "rna_CacheModifier_update"); + + prop = RNA_def_property(srna, "use_bend_stiffness_curve", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", eHairSimParams_Flag_UseBendStiffnessCurve); + RNA_def_property_ui_text(prop, "Use Bend Stiffness Curve", "Use a curve to define bend resistance along the strand"); + RNA_def_property_update(prop, 0, "rna_CacheModifier_update"); + + prop = RNA_def_property(srna, "bend_stiffness_curve", PROP_POINTER, PROP_NONE); + RNA_def_property_pointer_sdna(prop, NULL, "bend_stiffness_mapping"); + RNA_def_property_struct_type(prop, "CurveMapping"); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text(prop, "Bend Stiffness Curve", "Resistance to bending along the strand curves"); + RNA_def_property_update(prop, 0, "rna_CacheModifier_update"); + + prop = RNA_def_property(srna, "stretch_stiffness", PROP_FLOAT, PROP_NONE); + RNA_def_property_range(prop, 0.0f, FLT_MAX); + RNA_def_property_ui_range(prop, 0.0f, 10000.0f, 0.1f, 3); + RNA_def_property_float_default(prop, 10000.0f); + RNA_def_property_ui_text(prop, "Stretch Stiffness", "Resistance to stretching"); + RNA_def_property_update(prop, 0, "rna_CacheModifier_update"); + + prop = RNA_def_property(srna, "stretch_damping", PROP_FLOAT, PROP_FACTOR); + RNA_def_property_range(prop, 0.0f, FLT_MAX); + RNA_def_property_ui_range(prop, 0.0f, 1.0f, 0.1f, 3); + RNA_def_property_float_default(prop, 0.1f); + RNA_def_property_ui_text(prop, "Stretch Damping", "Damping factor of stretch springs"); + RNA_def_property_update(prop, 0, "rna_CacheModifier_update"); + + prop = RNA_def_property(srna, "bend_stiffness", PROP_FLOAT, PROP_NONE); + RNA_def_property_range(prop, 0.0f, FLT_MAX); + RNA_def_property_ui_range(prop, 0.0f, 1000.0f, 0.1f, 3); + RNA_def_property_float_default(prop, 100.0f); + RNA_def_property_ui_text(prop, "Bend Stiffness", "Resistance to bending of the rest shape"); + RNA_def_property_update(prop, 0, "rna_CacheModifier_update"); + + prop = RNA_def_property(srna, "bend_damping", PROP_FLOAT, PROP_FACTOR); + RNA_def_property_range(prop, 0.0f, FLT_MAX); + RNA_def_property_ui_range(prop, 0.0f, 1.0f, 0.1f, 3); + RNA_def_property_float_default(prop, 1.0f); + RNA_def_property_ui_text(prop, "Bend Damping", "Damping factor of bending springs"); + RNA_def_property_update(prop, 0, "rna_CacheModifier_update"); +} + +static void rna_def_cache_modifier_hair_simulation(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + rna_def_hair_sim_params(brna); + + srna = RNA_def_struct(brna, "HairSimulationCacheModifier", "CacheLibraryModifier"); + RNA_def_struct_sdna(srna, "HairSimCacheModifier"); + RNA_def_struct_ui_text(srna, "Hair Simulation Cache Modifier", "Apply hair dynamics simulation to the cache"); + RNA_def_struct_ui_icon(srna, ICON_HAIR); + + prop = RNA_def_property(srna, "object", PROP_POINTER, PROP_NONE); + RNA_def_property_pointer_sdna(prop, NULL, "object"); + RNA_def_property_struct_type(prop, "Object"); + RNA_def_property_pointer_funcs(prop, NULL, NULL, NULL, "rna_CacheLibraryModifier_hair_object_poll"); + RNA_def_property_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text(prop, "Object", "Object whose cache to simulate"); + RNA_def_property_update(prop, 0, "rna_CacheModifier_update"); + + prop = RNA_def_property(srna, "hair_system_index", PROP_INT, PROP_NONE); + RNA_def_property_int_sdna(prop, NULL, "hair_system"); + RNA_def_property_ui_text(prop, "Hair System Index", "Hair system cache to simulate"); + RNA_def_property_update(prop, 0, "rna_CacheModifier_update"); + + prop = RNA_def_property(srna, "hair_system", PROP_POINTER, PROP_NONE); + RNA_def_property_pointer_funcs(prop, "rna_HairSimulationCacheModifier_hair_system_get", "rna_HairSimulationCacheModifier_hair_system_set", NULL, "rna_HairSimulationCacheModifier_hair_system_poll"); + RNA_def_property_struct_type(prop, "ParticleSystem"); + RNA_def_property_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text(prop, "Hair System", "Hair system cache to simulate"); + RNA_def_property_update(prop, 0, "rna_CacheModifier_update"); + + prop = RNA_def_property(srna, "parameters", PROP_POINTER, PROP_NONE); + RNA_def_property_pointer_sdna(prop, NULL, "sim_params"); + RNA_def_property_struct_type(prop, "HairSimulationParameters"); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text(prop, "Simulation Parameters", "Parameters of the simulation"); +} + +static void rna_def_cache_modifier_force_field(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + static EnumPropertyItem force_type_items[] = { + {eForceFieldCacheModifier_Type_Deflect, "DEFLECT", ICON_FORCE_FORCE, "Deflect", "Push away from the surface"}, + {eForceFieldCacheModifier_Type_Drag, "DRAG", ICON_FORCE_DRAG, "Drag", "Adjust velocity to the surface"}, + {0, NULL, 0, NULL, NULL} + }; + + srna = RNA_def_struct(brna, "ForceFieldCacheModifier", "CacheLibraryModifier"); + RNA_def_struct_sdna(srna, "ForceFieldCacheModifier"); + RNA_def_struct_ui_text(srna, "Force Field Cache Modifier", "Use an object as a force field"); + RNA_def_struct_ui_icon(srna, ICON_FORCE_FORCE); + + prop = RNA_def_property(srna, "object", PROP_POINTER, PROP_NONE); + RNA_def_property_pointer_sdna(prop, NULL, "object"); + RNA_def_property_struct_type(prop, "Object"); + RNA_def_property_pointer_funcs(prop, NULL, NULL, NULL, "rna_CacheLibraryModifier_mesh_object_poll"); + RNA_def_property_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text(prop, "Object", "Object whose cache to simulate"); + RNA_def_property_update(prop, 0, "rna_CacheModifier_update"); + + prop = RNA_def_property(srna, "force_type", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "type"); + RNA_def_property_enum_items(prop, force_type_items); + RNA_def_property_ui_text(prop, "Force Type", "Type of force field"); + + prop = RNA_def_property(srna, "strength", PROP_FLOAT, PROP_NONE); + RNA_def_property_range(prop, -FLT_MAX, FLT_MAX); + RNA_def_property_ui_range(prop, -10000.0f, 10000.0f, 0.1, 3); + RNA_def_property_ui_text(prop, "Strength", ""); + RNA_def_property_update(prop, 0, "rna_CacheModifier_update"); + + prop = RNA_def_property(srna, "falloff", PROP_FLOAT, PROP_NONE); + RNA_def_property_range(prop, 0.0f, FLT_MAX); + RNA_def_property_ui_range(prop, 0.0f, 10.0f, 0.1, 3); + RNA_def_property_ui_text(prop, "Falloff", ""); + RNA_def_property_update(prop, 0, "rna_CacheModifier_update"); + + prop = RNA_def_property(srna, "min_distance", PROP_FLOAT, PROP_DISTANCE); + RNA_def_property_range(prop, -FLT_MAX, FLT_MAX); + RNA_def_property_ui_range(prop, -100.0f, 100.0f, 0.1, 4); + RNA_def_property_ui_text(prop, "Minimum Distance", ""); + RNA_def_property_update(prop, 0, "rna_CacheModifier_update"); + + prop = RNA_def_property(srna, "max_distance", PROP_FLOAT, PROP_DISTANCE); + RNA_def_property_range(prop, -FLT_MAX, FLT_MAX); + RNA_def_property_ui_range(prop, -100.0f, 100.0f, 0.1, 4); + RNA_def_property_ui_text(prop, "Maximum Distance", ""); + RNA_def_property_update(prop, 0, "rna_CacheModifier_update"); + + prop = RNA_def_property(srna, "use_double_sided", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", eForceFieldCacheModifier_Flag_DoubleSided); + RNA_def_property_ui_text(prop, "Use Double Sided", ""); + RNA_def_property_update(prop, 0, "rna_CacheModifier_update"); +} + +static void rna_def_cache_modifier_shrink_wrap(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, "ShrinkWrapCacheModifier", "CacheLibraryModifier"); + RNA_def_struct_sdna(srna, "ShrinkWrapCacheModifier"); + RNA_def_struct_ui_text(srna, "Shrink Wrap Cache Modifier", ""); + RNA_def_struct_ui_icon(srna, ICON_HAIR); + + prop = RNA_def_property(srna, "object", PROP_POINTER, PROP_NONE); + RNA_def_property_pointer_sdna(prop, NULL, "object"); + RNA_def_property_struct_type(prop, "Object"); + RNA_def_property_pointer_funcs(prop, NULL, NULL, NULL, "rna_CacheLibraryModifier_hair_object_poll"); + RNA_def_property_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text(prop, "Object", "Object whose cache to simulate"); + RNA_def_property_update(prop, 0, "rna_CacheModifier_update"); + + prop = RNA_def_property(srna, "hair_system_index", PROP_INT, PROP_NONE); + RNA_def_property_int_sdna(prop, NULL, "hair_system"); + RNA_def_property_ui_text(prop, "Hair System Index", "Hair system cache to simulate"); + RNA_def_property_update(prop, 0, "rna_CacheModifier_update"); + + prop = RNA_def_property(srna, "hair_system", PROP_POINTER, PROP_NONE); + RNA_def_property_pointer_funcs(prop, "rna_ShrinkWrapCacheModifier_hair_system_get", "rna_ShrinkWrapCacheModifier_hair_system_set", NULL, "rna_ShrinkWrapCacheModifier_hair_system_poll"); + RNA_def_property_struct_type(prop, "ParticleSystem"); + RNA_def_property_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text(prop, "Hair System", "Hair system cache to simulate"); + RNA_def_property_update(prop, 0, "rna_CacheModifier_update"); + + prop = RNA_def_property(srna, "target", PROP_POINTER, PROP_NONE); + RNA_def_property_pointer_sdna(prop, NULL, "target"); + RNA_def_property_struct_type(prop, "Object"); + RNA_def_property_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text(prop, "Target", "Mesh object to wrap onto"); + RNA_def_property_update(prop, 0, "rna_CacheModifier_update"); + + prop = RNA_def_property(srna, "use_internal_target", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", eShrinkWrapCacheModifier_Flag_InternalTarget); + RNA_def_property_ui_text(prop, "Use Internal Target", "Use a cached object from the group instead of an object in the scene"); + RNA_def_property_update(prop, 0, "rna_CacheModifier_update"); +} + +static void rna_def_cache_modifier_strands_key(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, "StrandsKeyCacheModifier", "CacheLibraryModifier"); + RNA_def_struct_sdna(srna, "StrandsKeyCacheModifier"); + RNA_def_struct_ui_text(srna, "Strands Key Cache Modifier", ""); + RNA_def_struct_ui_icon(srna, ICON_SHAPEKEY_DATA); + + prop = RNA_def_property(srna, "object", PROP_POINTER, PROP_NONE); + RNA_def_property_pointer_sdna(prop, NULL, "object"); + RNA_def_property_struct_type(prop, "Object"); + RNA_def_property_pointer_funcs(prop, NULL, NULL, NULL, "rna_CacheLibraryModifier_hair_object_poll"); + RNA_def_property_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text(prop, "Object", "Object whose cache to simulate"); + RNA_def_property_update(prop, 0, "rna_CacheModifier_update"); + + prop = RNA_def_property(srna, "hair_system_index", PROP_INT, PROP_NONE); + RNA_def_property_int_sdna(prop, NULL, "hair_system"); + RNA_def_property_ui_text(prop, "Hair System Index", "Hair system cache to simulate"); + RNA_def_property_update(prop, 0, "rna_CacheModifier_update"); + + prop = RNA_def_property(srna, "hair_system", PROP_POINTER, PROP_NONE); + RNA_def_property_pointer_funcs(prop, "rna_StrandsKeyCacheModifier_hair_system_get", "rna_StrandsKeyCacheModifier_hair_system_set", NULL, "rna_StrandsKeyCacheModifier_hair_system_poll"); + RNA_def_property_struct_type(prop, "ParticleSystem"); + RNA_def_property_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text(prop, "Hair System", "Hair system cache to simulate"); + RNA_def_property_update(prop, 0, "rna_CacheModifier_update"); + + /* shape keys */ + prop = RNA_def_property(srna, "shape_keys", PROP_POINTER, PROP_NONE); + RNA_def_property_pointer_sdna(prop, NULL, "key"); + RNA_def_property_ui_text(prop, "Shape Keys", ""); + + prop = RNA_def_property(srna, "use_motion_state", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", eStrandsKeyCacheModifier_Flag_UseMotionState); + RNA_def_property_ui_text(prop, "Use Motion State", "Apply the shape key to the motion state instead of the base shape"); + RNA_def_property_update(prop, 0, "rna_CacheModifier_update"); + + prop = RNA_def_property(srna, "show_only_shape_key", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", eStrandsKeyCacheModifier_Flag_ShapeLock); + RNA_def_property_ui_text(prop, "Shape Key Lock", "Always show the current Shape for this Object"); + RNA_def_property_ui_icon(prop, ICON_UNPINNED, 1); + RNA_def_property_update(prop, 0, "rna_CacheModifier_update"); + + prop = RNA_def_property(srna, "active_shape_key", PROP_POINTER, PROP_NONE); + RNA_def_property_struct_type(prop, "ShapeKey"); + RNA_def_property_pointer_funcs(prop, "rna_StrandsKeyCacheModifier_active_shape_key_get", NULL, NULL, NULL); + RNA_def_property_ui_text(prop, "Active Shape Key", "Current shape key"); + + prop = RNA_def_property(srna, "active_shape_key_index", PROP_INT, PROP_NONE); + RNA_def_property_int_sdna(prop, NULL, "shapenr"); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); /* XXX this is really unpredictable... */ + RNA_def_property_int_funcs(prop, "rna_StrandsKeyCacheModifier_active_shape_key_index_get", "rna_StrandsKeyCacheModifier_active_shape_key_index_set", + "rna_StrandsKeyCacheModifier_active_shape_key_index_range"); + RNA_def_property_ui_text(prop, "Active Shape Key Index", "Current shape key index"); + RNA_def_property_update(prop, 0, "rna_StrandsKeyCacheModifier_active_shape_update"); +} + +static void rna_def_cache_modifier_haircut(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + static EnumPropertyItem cut_mode_items[] = { + {eHaircutCacheModifier_CutMode_Enter, "ENTER", 0, "Enter", "Cut strands when entering the target mesh"}, + {eHaircutCacheModifier_CutMode_Exit, "EXIT", 0, "Exit", "Cut strands when exiting the target mesh"}, + {0, NULL, 0, NULL, NULL} + }; + + srna = RNA_def_struct(brna, "HaircutCacheModifier", "CacheLibraryModifier"); + RNA_def_struct_sdna(srna, "HaircutCacheModifier"); + RNA_def_struct_ui_text(srna, "Hair Cut Cache Modifier", ""); + RNA_def_struct_ui_icon(srna, ICON_HAIR); + + prop = RNA_def_property(srna, "object", PROP_POINTER, PROP_NONE); + RNA_def_property_pointer_sdna(prop, NULL, "object"); + RNA_def_property_struct_type(prop, "Object"); + RNA_def_property_pointer_funcs(prop, NULL, NULL, NULL, "rna_CacheLibraryModifier_hair_object_poll"); + RNA_def_property_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text(prop, "Object", "Object whose cache to simulate"); + RNA_def_property_update(prop, 0, "rna_CacheModifier_update"); + + prop = RNA_def_property(srna, "hair_system_index", PROP_INT, PROP_NONE); + RNA_def_property_int_sdna(prop, NULL, "hair_system"); + RNA_def_property_ui_text(prop, "Hair System Index", "Hair system cache to simulate"); + RNA_def_property_update(prop, 0, "rna_CacheModifier_update"); + + prop = RNA_def_property(srna, "hair_system", PROP_POINTER, PROP_NONE); + RNA_def_property_pointer_funcs(prop, "rna_HaircutCacheModifier_hair_system_get", "rna_HaircutCacheModifier_hair_system_set", NULL, "rna_HaircutCacheModifier_hair_system_poll"); + RNA_def_property_struct_type(prop, "ParticleSystem"); + RNA_def_property_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text(prop, "Hair System", "Hair system cache to simulate"); + RNA_def_property_update(prop, 0, "rna_CacheModifier_update"); + + prop = RNA_def_property(srna, "target", PROP_POINTER, PROP_NONE); + RNA_def_property_pointer_sdna(prop, NULL, "target"); + RNA_def_property_struct_type(prop, "Object"); + RNA_def_property_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text(prop, "Target", "Mesh object to wrap onto"); + RNA_def_property_update(prop, 0, "rna_CacheModifier_update"); + + prop = RNA_def_property(srna, "use_internal_target", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", eHaircutCacheModifier_Flag_InternalTarget); + RNA_def_property_ui_text(prop, "Use Internal Target", "Use a cached object from the group instead of an object in the scene"); + RNA_def_property_update(prop, 0, "rna_CacheModifier_update"); + + prop = RNA_def_property(srna, "cut_mode", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "cut_mode"); + RNA_def_property_enum_items(prop, cut_mode_items); + RNA_def_property_flag(prop, PROP_ENUM_FLAG); + RNA_def_property_ui_text(prop, "Cut Mode", "When to cut strands with the target"); + RNA_def_property_update(prop, 0, "rna_CacheModifier_update"); +} + +static void rna_def_cache_modifier(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, "CacheLibraryModifier", NULL); + RNA_def_struct_sdna(srna, "CacheModifier"); + RNA_def_struct_path_func(srna, "rna_CacheLibraryModifier_path"); + RNA_def_struct_refine_func(srna, "rna_CacheModifier_refine"); + RNA_def_struct_ui_text(srna, "Cache Modifier", "Cache Modifier"); + RNA_def_struct_ui_icon(srna, ICON_PHYSICS); + + prop = RNA_def_property(srna, "type", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "type"); + RNA_def_property_enum_items(prop, cache_modifier_type_items); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text(prop, "Type", "Type of the cache modifier"); + + prop = RNA_def_property(srna, "name", PROP_STRING, PROP_NONE); + RNA_def_property_string_funcs(prop, NULL, NULL, "rna_CacheLibraryModifier_name_set"); + RNA_def_property_ui_text(prop, "Name", "Modifier name"); + RNA_def_property_update(prop, NC_ID | NA_RENAME, NULL); + RNA_def_struct_name_property(srna, prop); + + rna_def_cache_modifier_hair_simulation(brna); + rna_def_cache_modifier_force_field(brna); + rna_def_cache_modifier_shrink_wrap(brna); + rna_def_cache_modifier_strands_key(brna); + rna_def_cache_modifier_haircut(brna); +} + +static void rna_def_cache_library_modifiers(BlenderRNA *brna, PropertyRNA *cprop) +{ + StructRNA *srna; + FunctionRNA *func; + PropertyRNA *parm; + + RNA_def_property_srna(cprop, "CacheLibraryModifiers"); + srna = RNA_def_struct(brna, "CacheLibraryModifiers", NULL); + RNA_def_struct_sdna(srna, "CacheLibrary"); + RNA_def_struct_ui_text(srna, "Cache Modifiers", "Collection of cache modifiers"); + + /* add modifier */ + func = RNA_def_function(srna, "new", "rna_CacheLibrary_modifier_new"); + RNA_def_function_flag(func, FUNC_USE_CONTEXT | FUNC_USE_REPORTS); + RNA_def_function_ui_description(func, "Add a new modifier"); + parm = RNA_def_string(func, "name", "Name", 0, "", "New name for the modifier"); + RNA_def_property_flag(parm, PROP_REQUIRED); + /* modifier to add */ + parm = RNA_def_enum(func, "type", cache_modifier_type_items, 1, "", "Modifier type to add"); + RNA_def_property_flag(parm, PROP_REQUIRED); + /* return type */ + parm = RNA_def_pointer(func, "modifier", "CacheLibraryModifier", "", "Newly created modifier"); + RNA_def_function_return(func, parm); + + /* remove modifier */ + func = RNA_def_function(srna, "remove", "rna_CacheLibrary_modifier_remove"); + RNA_def_function_flag(func, FUNC_USE_CONTEXT | FUNC_USE_REPORTS); + RNA_def_function_ui_description(func, "Remove an existing modifier"); + /* modifier to remove */ + parm = RNA_def_pointer(func, "modifier", "CacheLibraryModifier", "", "Modifier to remove"); + RNA_def_property_flag(parm, PROP_REQUIRED | PROP_NEVER_NULL | PROP_RNAPTR); + RNA_def_property_clear_flag(parm, PROP_THICK_WRAP); + + /* clear all modifiers */ + func = RNA_def_function(srna, "clear", "rna_CacheLibrary_modifier_clear"); + RNA_def_function_flag(func, FUNC_USE_CONTEXT); + RNA_def_function_ui_description(func, "Remove all modifiers"); +} + +static void rna_def_cache_library(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + static EnumPropertyItem source_mode_items[] = { + {CACHE_LIBRARY_SOURCE_SCENE, "SCENE", 0, "Scene", "Use generated scene data as source"}, + {CACHE_LIBRARY_SOURCE_CACHE, "CACHE", 0, "Cache", "Use cache data as source"}, + {0, NULL, 0, NULL, NULL} + }; + + static EnumPropertyItem display_mode_items[] = { + {CACHE_LIBRARY_DISPLAY_SOURCE, "SOURCE", 0, "Source", "Display source data unmodified"}, + {CACHE_LIBRARY_DISPLAY_MODIFIERS, "MODIFIERS", 0, "Modifiers", "Display source data with modifiers applied"}, + {CACHE_LIBRARY_DISPLAY_RESULT, "RESULT", 0, "Result", "Display resulting data"}, + {0, NULL, 0, NULL, NULL} + }; + + srna = RNA_def_struct(brna, "CacheLibrary", "ID"); + RNA_def_struct_ui_text(srna, "Cache Library", "Cache Library datablock for constructing an archive of caches"); + RNA_def_struct_ui_icon(srna, ICON_PHYSICS); + + prop = RNA_def_property(srna, "input_filepath", PROP_STRING, PROP_FILEPATH); + RNA_def_property_string_sdna(prop, NULL, "input_filepath"); + RNA_def_property_ui_text(prop, "Input File Path", "Path to a cache archive for reading input"); + RNA_def_property_update(prop, 0, "rna_CacheLibrary_update"); + + prop = RNA_def_property(srna, "output_filepath", PROP_STRING, PROP_FILEPATH); + RNA_def_property_string_sdna(prop, NULL, "output_filepath"); + RNA_def_property_ui_text(prop, "Output File Path", "Path where cache output is written"); + RNA_def_property_update(prop, 0, "rna_CacheLibrary_update"); + + prop = RNA_def_property(srna, "source_mode", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "source_mode"); + RNA_def_property_enum_items(prop, source_mode_items); + RNA_def_property_ui_text(prop, "Source Mode", "Source of the cache library data"); + RNA_def_property_update(prop, 0, "rna_CacheLibrary_update"); + + prop = RNA_def_property(srna, "display_mode", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "display_mode"); + RNA_def_property_enum_items(prop, display_mode_items); + RNA_def_property_ui_text(prop, "Display Mode", "What data to display in the viewport"); + RNA_def_property_update(prop, 0, "rna_CacheLibrary_update"); + + prop = RNA_def_property(srna, "display_motion", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "display_flag", CACHE_LIBRARY_DISPLAY_MOTION); + RNA_def_property_ui_text(prop, "Display Motion", "Display motion state result from simulation, if available"); + RNA_def_property_update(prop, 0, "rna_CacheLibrary_update"); + + prop = RNA_def_property(srna, "display_children", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "display_flag", CACHE_LIBRARY_DISPLAY_CHILDREN); + RNA_def_property_ui_text(prop, "Display Children", "Display child strands, if available"); + RNA_def_property_update(prop, 0, "rna_CacheLibrary_update"); + + prop = RNA_def_property(srna, "data_types", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "data_types"); + RNA_def_property_enum_items(prop, cache_library_data_type_items); + RNA_def_property_flag(prop, PROP_ENUM_FLAG); + RNA_def_property_ui_text(prop, "Data Types", "Types of data to store in the cache"); + + prop = RNA_def_property(srna, "filter_group", PROP_POINTER, PROP_NONE); + RNA_def_property_pointer_sdna(prop, NULL, "filter_group"); + RNA_def_property_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text(prop, "Filter Group", "If set, only objects in this group will be cached"); + + prop = RNA_def_property(srna, "description", PROP_STRING, PROP_NONE); + RNA_def_property_ui_text(prop, "Description", "Description of the output archive"); + + /* modifiers */ + prop = RNA_def_property(srna, "modifiers", PROP_COLLECTION, PROP_NONE); + RNA_def_property_struct_type(prop, "CacheLibraryModifier"); + RNA_def_property_ui_text(prop, "Modifiers", "Modifiers applying to the cached data"); + rna_def_cache_library_modifiers(brna, prop); + + prop = RNA_def_property(srna, "archive_info", PROP_POINTER, PROP_NONE); + RNA_def_property_struct_type(prop, "CacheArchiveInfo"); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text(prop, "Archive Info", "Information about structure and contents of the archive"); +} + +static void rna_def_cache_archive_info_node(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + static EnumPropertyItem type_items[] = { + {eCacheArchiveInfoNode_Type_Object, "OBJECT", 0, "Object", "Structural object node forming the hierarchy"}, + {eCacheArchiveInfoNode_Type_ScalarProperty, "SCALAR_PROPERTY", 0, "Scalar Property", "Property with a single value per sample"}, + {eCacheArchiveInfoNode_Type_ArrayProperty, "ARRAY_PROPERTY", 0, "Array Property", "Array property with an arbitrary number of values per sample"}, + {eCacheArchiveInfoNode_Type_CompoundProperty, "COMPOUND_PROPERTY", 0, "Compound Property", "Compound property containing other properties"}, + {0, NULL, 0, NULL, NULL} + }; + + srna = RNA_def_struct(brna, "CacheArchiveInfoNode", NULL); + RNA_def_struct_ui_text(srna, "Cache Archive Info Node", "Node in the structure of a cache archive"); + + prop = RNA_def_property(srna, "type", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, type_items); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text(prop, "Type", "Type of archive node"); + + prop = RNA_def_property(srna, "name", PROP_STRING, PROP_NONE); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text(prop, "Name", "Name of the archive node"); + RNA_def_struct_name_property(srna, prop); + + prop = RNA_def_property(srna, "child_nodes", PROP_COLLECTION, PROP_NONE); + RNA_def_property_struct_type(prop, "CacheArchiveInfoNode"); + RNA_def_property_ui_text(prop, "Child Nodes", "Nested archive nodes"); + + prop = RNA_def_property(srna, "expand", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", eCacheArchiveInfoNode_Flag_Expand); + RNA_def_property_ui_text(prop, "Expand", "Show contents of the node"); + RNA_def_property_update(prop, 0, "rna_CacheArchiveInfo_update"); + + /* XXX this is a 64bit integer, not supported nicely by RNA, + * but string encoding is sufficient for feedback + */ + prop = RNA_def_property(srna, "bytes_size", PROP_STRING, PROP_NONE); + RNA_def_property_string_funcs(prop, "rna_CacheArchiveInfoNode_bytes_size_get", "rna_CacheArchiveInfoNode_bytes_size_length", NULL); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text(prop, "Bytes Size", "Overall size of the node data in bytes"); + + prop = RNA_def_property(srna, "datatype", PROP_STRING, PROP_NONE); + RNA_def_property_string_sdna(prop, NULL, "datatype_name"); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text(prop, "Datatype", "Type of values stored in the property"); + + prop = RNA_def_property(srna, "datatype_extent", PROP_INT, PROP_NONE); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text(prop, "Datatype Extent", "Array extent of a single data element"); + + prop = RNA_def_property(srna, "samples", PROP_INT, PROP_NONE); + RNA_def_property_int_sdna(prop, NULL, "num_samples"); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text(prop, "Samples", "Number of samples stored for the property"); + + prop = RNA_def_property(srna, "array_size", PROP_INT, PROP_NONE); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text(prop, "Array Size", "Maximum array size for any sample of the property"); +} + +static void rna_def_cache_archive_info(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, "CacheArchiveInfo", NULL); + RNA_def_struct_ui_text(srna, "Cache Archive Info", "Information about structure and contents of a cache file"); + + prop = RNA_def_property(srna, "filepath", PROP_STRING, PROP_FILEPATH); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text(prop, "File Path", "Path to the cache archive"); + + prop = RNA_def_property(srna, "app_name", PROP_STRING, PROP_NONE); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text(prop, "Application", "Name of the application that created the archive"); + + prop = RNA_def_property(srna, "date_written", PROP_STRING, PROP_NONE); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text(prop, "Date", "Date and time when the archive was created"); + + prop = RNA_def_property(srna, "description", PROP_STRING, PROP_NONE); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text(prop, "Description", "Description of the archive"); + + prop = RNA_def_property(srna, "root_node", PROP_POINTER, PROP_NONE); + RNA_def_property_struct_type(prop, "CacheArchiveInfoNode"); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text(prop, "Root Node", "Root node of the archive"); +} + +void RNA_def_cache_library(BlenderRNA *brna) +{ + rna_def_cache_modifier(brna); + rna_def_cache_library(brna); + rna_def_cache_archive_info_node(brna); + rna_def_cache_archive_info(brna); +} + +#endif diff --git a/source/blender/makesrna/intern/rna_context.c b/source/blender/makesrna/intern/rna_context.c index d7a679e9702..a512f1d750f 100644 --- a/source/blender/makesrna/intern/rna_context.c +++ b/source/blender/makesrna/intern/rna_context.c @@ -148,6 +148,7 @@ void RNA_def_context(BlenderRNA *brna) {CTX_MODE_PAINT_VERTEX, "PAINT_VERTEX", 0, "Vertex Paint", ""}, {CTX_MODE_PAINT_TEXTURE, "PAINT_TEXTURE", 0, "Texture Paint", ""}, {CTX_MODE_PARTICLE, "PARTICLE", 0, "Particle", ""}, + {CTX_MODE_HAIR, "HAIR", 0, "Hair", ""}, {CTX_MODE_OBJECT, "OBJECT", 0, "Object", ""}, {0, NULL, 0, NULL, NULL} }; diff --git a/source/blender/makesrna/intern/rna_internal.h b/source/blender/makesrna/intern/rna_internal.h index eab14be9085..62fe71fe8a5 100644 --- a/source/blender/makesrna/intern/rna_internal.h +++ b/source/blender/makesrna/intern/rna_internal.h @@ -133,6 +133,7 @@ void RNA_def_armature(struct BlenderRNA *brna); void RNA_def_actuator(struct BlenderRNA *brna); void RNA_def_boid(struct BlenderRNA *brna); void RNA_def_brush(struct BlenderRNA *brna); +void RNA_def_cache_library(struct BlenderRNA *brna); void RNA_def_camera(struct BlenderRNA *brna); void RNA_def_cloth(struct BlenderRNA *brna); void RNA_def_color(struct BlenderRNA *brna); @@ -155,6 +156,7 @@ void RNA_def_linestyle(struct BlenderRNA *brna); void RNA_def_main(struct BlenderRNA *brna); void RNA_def_material(struct BlenderRNA *brna); void RNA_def_mesh(struct BlenderRNA *brna); +void RNA_def_mesh_sample(struct BlenderRNA *brna); void RNA_def_meta(struct BlenderRNA *brna); void RNA_def_modifier(struct BlenderRNA *brna); void RNA_def_nla(struct BlenderRNA *brna); @@ -176,6 +178,7 @@ void RNA_def_sequencer(struct BlenderRNA *brna); void RNA_def_smoke(struct BlenderRNA *brna); void RNA_def_space(struct BlenderRNA *brna); void RNA_def_speaker(struct BlenderRNA *brna); +void RNA_def_strands(struct BlenderRNA *brna); void RNA_def_test(struct BlenderRNA *brna); void RNA_def_text(struct BlenderRNA *brna); void RNA_def_texture(struct BlenderRNA *brna); @@ -264,6 +267,7 @@ void RNA_api_image(struct StructRNA *srna); void RNA_api_lattice(struct StructRNA *srna); void RNA_api_operator(struct StructRNA *srna); void RNA_api_macro(struct StructRNA *srna); +void RNA_api_widgetgroup(struct StructRNA *srna); void RNA_api_keyconfig(struct StructRNA *srna); void RNA_api_keyconfigs(struct StructRNA *srna); void RNA_api_keyingset(struct StructRNA *srna); @@ -330,6 +334,7 @@ void RNA_def_main_gpencil(BlenderRNA *brna, PropertyRNA *cprop); void RNA_def_main_movieclips(BlenderRNA *brna, PropertyRNA *cprop); void RNA_def_main_masks(BlenderRNA *brna, PropertyRNA *cprop); void RNA_def_main_linestyles(BlenderRNA *brna, PropertyRNA *cprop); +void RNA_def_main_cache_libraries(BlenderRNA *brna, PropertyRNA *cprop); /* ID Properties */ diff --git a/source/blender/makesrna/intern/rna_key.c b/source/blender/makesrna/intern/rna_key.c index 249d132068c..b462729657c 100644 --- a/source/blender/makesrna/intern/rna_key.c +++ b/source/blender/makesrna/intern/rna_key.c @@ -32,6 +32,7 @@ #include "DNA_key_types.h" #include "DNA_lattice_types.h" #include "DNA_mesh_types.h" +#include "DNA_particle_types.h" #include "BLI_utildefines.h" @@ -47,10 +48,12 @@ #include <stddef.h> +#include "DNA_cache_library_types.h" #include "DNA_object_types.h" #include "DNA_scene_types.h" #include "BKE_animsys.h" +#include "BKE_cache_library.h" #include "BKE_depsgraph.h" #include "BKE_key.h" #include "BKE_main.h" @@ -303,7 +306,7 @@ static void rna_ShapeKey_data_begin(CollectionPropertyIterator *iter, PointerRNA Nurb *nu; int tot = kb->totelem, size = key->elemsize; - if (GS(key->from->name) == ID_CU) { + if (key->from && key->fromtype == KEY_OWNER_CURVE) { cu = (Curve *)key->from; nu = cu->nurb.first; @@ -324,7 +327,7 @@ static int rna_ShapeKey_data_length(PointerRNA *ptr) Nurb *nu; int tot = kb->totelem; - if (GS(key->from->name) == ID_CU) { + if (key->from && key->fromtype == KEY_OWNER_CURVE) { cu = (Curve *)key->from; nu = cu->nurb.first; @@ -342,7 +345,7 @@ static PointerRNA rna_ShapeKey_data_get(CollectionPropertyIterator *iter) Curve *cu; Nurb *nu; - if (GS(key->from->name) == ID_CU) { + if (key->from && key->fromtype == KEY_OWNER_CURVE) { cu = (Curve *)key->from; nu = cu->nurb.first; @@ -375,11 +378,37 @@ static void rna_Key_update_data(Main *bmain, Scene *UNUSED(scene), PointerRNA *p { Key *key = ptr->id.data; Object *ob; - - for (ob = bmain->object.first; ob; ob = ob->id.next) { - if (BKE_key_from_object(ob) == key) { - DAG_id_tag_update(&ob->id, OB_RECALC_DATA); - WM_main_add_notifier(NC_OBJECT | ND_MODIFIER, ob); + CacheLibrary *cachelib; + + switch (key->fromtype) { + case KEY_OWNER_MESH: + case KEY_OWNER_CURVE: + case KEY_OWNER_LATTICE: + for (ob = bmain->object.first; ob; ob = ob->id.next) { + if (BKE_key_from_object(ob) == key) { + DAG_id_tag_update(&ob->id, OB_RECALC_DATA); + WM_main_add_notifier(NC_OBJECT | ND_MODIFIER, ob); + } + } + break; + case KEY_OWNER_PARTICLES: + for (ob = bmain->object.first; ob; ob = ob->id.next) { + ParticleSystem *psys; + for (psys = ob->particlesystem.first; psys; psys = psys->next) { + if (psys->key == key) { + psys->recalc |= PSYS_RECALC_REDO; + DAG_id_tag_update(&ob->id, OB_RECALC_DATA); + WM_main_add_notifier(NC_OBJECT | ND_PARTICLE | NA_EDITED, ob); + } + } + } + break; + } + + for (cachelib = bmain->cache_library.first; cachelib; cachelib = cachelib->id.next) { + if (BKE_cache_library_uses_key(cachelib, key)) { + DAG_id_tag_update(&cachelib->id, OB_RECALC_DATA); + WM_main_add_notifier(NC_WINDOW, NULL); } } } @@ -601,6 +630,11 @@ static void rna_def_keyblock(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Vertex Group", "Vertex weight group, to blend with basis shape"); RNA_def_property_update(prop, 0, "rna_Key_update_data"); + prop = RNA_def_property(srna, "face_map", PROP_STRING, PROP_NONE); + RNA_def_property_string_sdna(prop, NULL, "facemap"); + RNA_def_property_ui_text(prop, "Face Map", "Face Map used to initiate interpolation for this shapekey"); + RNA_def_property_update(prop, 0, "rna_Key_update_data"); + prop = RNA_def_property(srna, "relative_key", PROP_POINTER, PROP_NONE); RNA_def_property_struct_type(prop, "ShapeKey"); RNA_def_property_flag(prop, PROP_EDITABLE | PROP_NEVER_NULL); diff --git a/source/blender/makesrna/intern/rna_main.c b/source/blender/makesrna/intern/rna_main.c index 348fa3c8302..926c5db5ae6 100644 --- a/source/blender/makesrna/intern/rna_main.c +++ b/source/blender/makesrna/intern/rna_main.c @@ -287,6 +287,12 @@ static void rna_Main_linestyle_begin(CollectionPropertyIterator *iter, PointerRN rna_iterator_listbase_begin(iter, &bmain->linestyle, NULL); } +static void rna_Main_cachelibraries_begin(CollectionPropertyIterator *iter, PointerRNA *ptr) +{ + Main *bmain = (Main *)ptr->data; + rna_iterator_listbase_begin(iter, &bmain->cache_library, NULL); +} + static void rna_Main_version_get(PointerRNA *ptr, int *value) { Main *bmain = (Main *)ptr->data; @@ -361,6 +367,7 @@ void RNA_def_main(BlenderRNA *brna) {"movieclips", "MovieClip", "rna_Main_movieclips_begin", "Movie Clips", "Movie Clip datablocks", RNA_def_main_movieclips}, {"masks", "Mask", "rna_Main_masks_begin", "Masks", "Masks datablocks", RNA_def_main_masks}, {"linestyles", "FreestyleLineStyle", "rna_Main_linestyle_begin", "Line Styles", "Line Style datablocks", RNA_def_main_linestyles}, + {"cache_libraries", "CacheLibrary", "rna_Main_cachelibraries_begin", "Cache Libraries", "Cache Library datablocks", RNA_def_main_cache_libraries}, {NULL, NULL, NULL, NULL, NULL, NULL} }; diff --git a/source/blender/makesrna/intern/rna_main_api.c b/source/blender/makesrna/intern/rna_main_api.c index 8b5ed66e217..b9b3bb519ca 100644 --- a/source/blender/makesrna/intern/rna_main_api.c +++ b/source/blender/makesrna/intern/rna_main_api.c @@ -49,6 +49,8 @@ #ifdef RNA_RUNTIME #include "BKE_main.h" +#include "BKE_anim.h" +#include "BKE_cache_library.h" #include "BKE_camera.h" #include "BKE_curve.h" #include "BKE_DerivedMesh.h" @@ -83,6 +85,7 @@ #include "BKE_linestyle.h" #include "DNA_armature_types.h" +#include "DNA_cache_library_types.h" #include "DNA_camera_types.h" #include "DNA_curve_types.h" #include "DNA_lamp_types.h" @@ -312,6 +315,61 @@ Mesh *rna_Main_meshes_new_from_object( return BKE_mesh_new_from_object(bmain, sce, ob, apply_modifiers, settings, calc_tessface, calc_undeformed); } +/* copied from Mesh_getFromObject and adapted to RNA interface */ +/* settings: 1 - preview, 2 - render */ +Mesh *rna_Main_meshes_new_from_dupli( + Main *bmain, ReportList *reports, Scene *scene, Object *parent, DupliObject *dob, + int settings, int calc_tessface, int calc_undeformed) +{ + Mesh *mesh = NULL; + bool is_cached = parent->cache_library && (parent->cache_library->source_mode == CACHE_LIBRARY_SOURCE_CACHE || parent->cache_library->display_mode == CACHE_LIBRARY_DISPLAY_RESULT); + + switch (dob->ob->type) { + case OB_FONT: + case OB_CURVE: + case OB_SURF: + case OB_MBALL: + case OB_MESH: + break; + default: + BKE_report(reports, RPT_ERROR, "Object does not have geometry data"); + return NULL; + } + + if (is_cached) { + float frame = (float)scene->r.cfra + scene->r.subframe; + bool use_render = (settings == 2); + + if (!ELEM(settings, 1, 2)) + return NULL; + + if (settings == 1 && parent->dup_cache) { + DupliObjectData *data; + + /* use dupli cache for realtime dupli data if possible */ + data = BKE_dupli_cache_find_data(parent->dup_cache, dob->ob); + if (data) + mesh = BKE_mesh_new_from_dupli_data(bmain, data, calc_tessface, calc_undeformed); + } + else { + DupliObjectData data; + + memset(&data, 0, sizeof(data)); + if (BKE_cache_read_dupli_object(parent->cache_library, &data, scene, dob->ob, frame, use_render, true)) + mesh = BKE_mesh_new_from_dupli_data(bmain, &data, calc_tessface, calc_undeformed); + + BKE_dupli_object_data_clear(&data); + } + } + + /* default, and fallback in case no mesh data was stored in the cache */ + if (!mesh) { + mesh = BKE_mesh_new_from_object(bmain, scene, dob->ob, true, settings, calc_tessface, calc_undeformed); + } + + return mesh; +} + static void rna_Main_meshes_remove(Main *bmain, ReportList *reports, PointerRNA *mesh_ptr) { Mesh *mesh = mesh_ptr->data; @@ -744,6 +802,22 @@ static void rna_Main_linestyles_remove(Main *bmain, ReportList *reports, Freesty /* XXX python now has invalid pointer? */ } +static CacheLibrary *rna_Main_cachelibraries_new(Main *bmain, const char *name) +{ + CacheLibrary *cachelib = BKE_cache_library_add(bmain, name); + id_us_min(&cachelib->id); + return cachelib; +} + +static void rna_Main_cachelibraries_remove(Main *bmain, ReportList *reports, CacheLibrary *cachelib) +{ + if (ID_REAL_USERS(cachelib) <= 0) + BKE_libblock_free(bmain, cachelib); + else + BKE_reportf(reports, RPT_ERROR, "Cache library '%s' must have zero users to be removed, found %d", + cachelib->id.name + 2, ID_REAL_USERS(cachelib)); +} + /* tag functions, all the same */ static void rna_Main_cameras_tag(Main *bmain, int value) { BKE_main_id_tag_listbase(&bmain->camera, value); } static void rna_Main_scenes_tag(Main *bmain, int value) { BKE_main_id_tag_listbase(&bmain->scene, value); } @@ -777,6 +851,7 @@ static void rna_Main_gpencil_tag(Main *bmain, int value) { BKE_main_id_tag_listb static void rna_Main_movieclips_tag(Main *bmain, int value) { BKE_main_id_tag_listbase(&bmain->movieclip, value); } static void rna_Main_masks_tag(Main *bmain, int value) { BKE_main_id_tag_listbase(&bmain->mask, value); } static void rna_Main_linestyle_tag(Main *bmain, int value) { BKE_main_id_tag_listbase(&bmain->linestyle, value); } +static void rna_Main_cachelibraries_tag(Main *bmain, int value) { BKE_main_id_tag_listbase(&bmain->cache_library, value); } static int rna_Main_cameras_is_updated_get(PointerRNA *ptr) { return DAG_id_type_tagged(ptr->data, ID_CA) != 0; } static int rna_Main_scenes_is_updated_get(PointerRNA *ptr) { return DAG_id_type_tagged(ptr->data, ID_SCE) != 0; } @@ -806,6 +881,7 @@ static int rna_Main_particles_is_updated_get(PointerRNA *ptr) { return DAG_id_ty static int rna_Main_palettes_is_updated_get(PointerRNA *ptr) { return DAG_id_type_tagged(ptr->data, ID_PAL) != 0; } static int rna_Main_gpencil_is_updated_get(PointerRNA *ptr) { return DAG_id_type_tagged(ptr->data, ID_GD) != 0; } static int rna_Main_linestyle_is_updated_get(PointerRNA *ptr) { return DAG_id_type_tagged(ptr->data, ID_LS) != 0; } +static int rna_Main_cachelibraries_is_updated_get(PointerRNA *ptr) { return DAG_id_type_tagged(ptr->data, ID_CL) != 0; } #else @@ -1059,6 +1135,23 @@ void RNA_def_main_meshes(BlenderRNA *brna, PropertyRNA *cprop) "Mesh created from object, remove it if it is only used for export"); RNA_def_function_return(func, parm); + func = RNA_def_function(srna, "new_from_dupli", "rna_Main_meshes_new_from_dupli"); + RNA_def_function_ui_description(func, "Add a new mesh created from dupli cache data"); + RNA_def_function_flag(func, FUNC_USE_REPORTS); + parm = RNA_def_pointer(func, "scene", "Scene", "", "Scene within which to evaluate modifiers"); + RNA_def_property_flag(parm, PROP_REQUIRED | PROP_NEVER_NULL); + parm = RNA_def_pointer(func, "parent", "Object", "", "Duplicator parent of the object"); + RNA_def_property_flag(parm, PROP_REQUIRED | PROP_NEVER_NULL); + parm = RNA_def_pointer(func, "dupli_object", "DupliObject", "", "Dupli Object to create mesh from"); + RNA_def_property_flag(parm, PROP_REQUIRED | PROP_NEVER_NULL); + parm = RNA_def_enum(func, "settings", mesh_type_items, 0, "", "Modifier settings to apply"); + RNA_def_property_flag(parm, PROP_REQUIRED); + RNA_def_boolean(func, "calc_tessface", true, "Calculate Tessellation", "Calculate tessellation faces"); + RNA_def_boolean(func, "calc_undeformed", false, "Calculate Undeformed", "Calculate undeformed vertex coordinates"); + parm = RNA_def_pointer(func, "mesh", "Mesh", "", + "Mesh created from object, remove it if it is only used for export"); + RNA_def_function_return(func, parm); + func = RNA_def_function(srna, "remove", "rna_Main_meshes_remove"); RNA_def_function_flag(func, FUNC_USE_REPORTS); RNA_def_function_ui_description(func, "Remove a mesh from the current blendfile"); @@ -1906,4 +1999,39 @@ void RNA_def_main_linestyles(BlenderRNA *brna, PropertyRNA *cprop) RNA_def_property_boolean_funcs(prop, "rna_Main_linestyle_is_updated_get", NULL); } +void RNA_def_main_cache_libraries(BlenderRNA *brna, PropertyRNA *cprop) +{ + StructRNA *srna; + FunctionRNA *func; + PropertyRNA *parm; + PropertyRNA *prop; + + RNA_def_property_srna(cprop, "BlendDataCacheLibraries"); + srna = RNA_def_struct(brna, "BlendDataCacheLibraries", NULL); + RNA_def_struct_sdna(srna, "Main"); + RNA_def_struct_ui_text(srna, "Main Cache Libraries", "Collection of cache libraries"); + + func = RNA_def_function(srna, "tag", "rna_Main_cachelibraries_tag"); + parm = RNA_def_boolean(func, "value", 0, "Value", ""); + RNA_def_property_flag(parm, PROP_REQUIRED); + + func = RNA_def_function(srna, "new", "rna_Main_cachelibraries_new"); + RNA_def_function_ui_description(func, "Add a new cache library to the main database"); + parm = RNA_def_string(func, "name", "CacheLibrary", 0, "", "New name for the datablock"); + RNA_def_property_flag(parm, PROP_REQUIRED); + /* return type */ + parm = RNA_def_pointer(func, "cachelib", "CacheLibrary", "", "New cache library datablock"); + RNA_def_function_return(func, parm); + + func = RNA_def_function(srna, "remove", "rna_Main_cachelibraries_remove"); + RNA_def_function_flag(func, FUNC_USE_REPORTS); + RNA_def_function_ui_description(func, "Remove a cache library from the current blendfile"); + parm = RNA_def_pointer(func, "cachelib", "CacheLibrary", "", "Cache Library to remove"); + RNA_def_property_flag(parm, PROP_REQUIRED); + + prop = RNA_def_property(srna, "is_updated", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_boolean_funcs(prop, "rna_Main_cachelibraries_is_updated_get", NULL); +} + #endif diff --git a/source/blender/makesrna/intern/rna_mesh_sample.c b/source/blender/makesrna/intern/rna_mesh_sample.c new file mode 100644 index 00000000000..81a3f4bf5e4 --- /dev/null +++ b/source/blender/makesrna/intern/rna_mesh_sample.c @@ -0,0 +1,73 @@ +/* + * ***** 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/makesrna/intern/rna_mesh_sample.c + * \ingroup RNA + */ + +#include <stdlib.h> + +#include "MEM_guardedalloc.h" + +#include "DNA_meshdata_types.h" + +#include "BLI_utildefines.h" + +#include "BKE_mesh_sample.h" + +#include "RNA_access.h" +#include "RNA_define.h" +#include "RNA_types.h" + +#include "rna_internal.h" + +#include "WM_types.h" + + +#ifdef RNA_RUNTIME + +#include "WM_api.h" +#include "WM_types.h" + + + +#else + +static void rna_def_mesh_sample(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, "MeshSample", NULL); + RNA_def_struct_sdna(srna, "MSurfaceSample"); + RNA_def_struct_ui_text(srna, "Mesh Sample", "Point on a mesh that follows deformation"); + + prop = RNA_def_property(srna, "vertex_indices", PROP_INT, PROP_UNSIGNED); + RNA_def_property_int_sdna(prop, NULL, "orig_verts"); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text(prop, "Vertex Indices", "Index of the mesh vertices used for interpolation"); +} + +void RNA_def_mesh_sample(BlenderRNA *brna) +{ + rna_def_mesh_sample(brna); +} + +#endif diff --git a/source/blender/makesrna/intern/rna_modifier.c b/source/blender/makesrna/intern/rna_modifier.c index 2b3062f6874..859f2760b29 100644 --- a/source/blender/makesrna/intern/rna_modifier.c +++ b/source/blender/makesrna/intern/rna_modifier.c @@ -258,6 +258,8 @@ EnumPropertyItem DT_layers_select_dst_items[] = { #ifdef RNA_RUNTIME +#include "BLI_listbase.h" + #include "DNA_particle_types.h" #include "DNA_curve_types.h" #include "DNA_smoke_types.h" @@ -1065,6 +1067,43 @@ static int rna_CorrectiveSmoothModifier_is_bind_get(PointerRNA *ptr) return (csmd->bind_coords != NULL); } +static int rna_ParticleInstanceModifier_particle_system_poll(PointerRNA *ptr, const PointerRNA value) +{ + ParticleInstanceModifierData *psmd = ptr->data; + ParticleSystem *psys = value.data; + + if (!psmd->ob) + return false; + + /* make sure psys is in the object */ + return BLI_findindex(&psmd->ob->particlesystem, psys) >= 0; +} + +static PointerRNA rna_ParticleInstanceModifier_particle_system_get(PointerRNA *ptr) +{ + ParticleInstanceModifierData *psmd = ptr->data; + ParticleSystem *psys; + PointerRNA rptr; + + if (!psmd->ob) + return PointerRNA_NULL; + + psys = BLI_findlink(&psmd->ob->particlesystem, psmd->psys - 1); + RNA_pointer_create((ID *)psmd->ob, &RNA_ParticleSystem, psys, &rptr); + return rptr; +} + +static void rna_ParticleInstanceModifier_particle_system_set(PointerRNA *ptr, const PointerRNA value) +{ + ParticleInstanceModifierData *psmd = ptr->data; + + if (!psmd->ob) + return; + + psmd->psys = BLI_findindex(&psmd->ob->particlesystem, value.data) + 1; + CLAMP_MIN(psmd->psys, 1); +} + #else static PropertyRNA *rna_def_property_subdivision_common(StructRNA *srna, const char type[]) @@ -1685,6 +1724,11 @@ static void rna_def_modifier_armature(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Use Bone Envelopes", "Bind Bone envelopes to armature modifier"); RNA_def_property_update(prop, 0, "rna_Modifier_update"); + prop = RNA_def_property(srna, "use_face_maps", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "deformflag", ARM_DEF_FACEMAPS); + RNA_def_property_ui_text(prop, "Use Face Maps", "Create facemap widgets with same name as bones in the armature"); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); + prop = RNA_def_property(srna, "use_vertex_groups", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "deformflag", ARM_DEF_VGROUP); RNA_def_property_ui_text(prop, "Use Vertex Groups", "Bind vertex groups to armature modifier"); @@ -2467,6 +2511,12 @@ static void rna_def_modifier_particleinstance(BlenderRNA *brna) {0, NULL, 0, NULL, NULL} }; + static EnumPropertyItem particleinstance_space[] = { + {eParticleInstanceSpace_Local, "LOCAL", 0, "Local", "Use offset from the particle object in the instance object"}, + {eParticleInstanceSpace_World, "WORLD", 0, "World", "Use world space offset in the instance object"}, + {0, NULL, 0, NULL, NULL} + }; + srna = RNA_def_struct(brna, "ParticleInstanceModifier", "Modifier"); RNA_def_struct_ui_text(srna, "ParticleInstance Modifier", "Particle system instancing modifier"); RNA_def_struct_sdna(srna, "ParticleInstanceModifierData"); @@ -2481,16 +2531,30 @@ static void rna_def_modifier_particleinstance(BlenderRNA *brna) prop = RNA_def_property(srna, "particle_system_index", PROP_INT, PROP_NONE); RNA_def_property_int_sdna(prop, NULL, "psys"); - RNA_def_property_range(prop, 1, 10); + RNA_def_property_range(prop, 1, SHRT_MAX); RNA_def_property_ui_text(prop, "Particle System Number", ""); RNA_def_property_update(prop, 0, "rna_Modifier_update"); + prop = RNA_def_property(srna, "particle_system", PROP_POINTER, PROP_NONE); + RNA_def_property_struct_type(prop, "ParticleSystem"); + RNA_def_property_pointer_funcs(prop, "rna_ParticleInstanceModifier_particle_system_get", "rna_ParticleInstanceModifier_particle_system_set", + NULL, "rna_ParticleInstanceModifier_particle_system_poll"); + RNA_def_property_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text(prop, "Particle System", ""); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); + prop = RNA_def_property(srna, "axis", PROP_ENUM, PROP_NONE); RNA_def_property_enum_sdna(prop, NULL, "axis"); RNA_def_property_enum_items(prop, particleinstance_axis); RNA_def_property_ui_text(prop, "Axis", "Pole axis for rotation"); RNA_def_property_update(prop, 0, "rna_Modifier_update"); - + + prop = RNA_def_property(srna, "space", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "space"); + RNA_def_property_enum_items(prop, particleinstance_space); + RNA_def_property_ui_text(prop, "Space", "Space to use for copying mesh data"); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); + prop = RNA_def_property(srna, "use_normal", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", eParticleInstanceFlag_Parents); RNA_def_property_ui_text(prop, "Normal", "Create instances from normal particles"); @@ -2542,6 +2606,40 @@ static void rna_def_modifier_particleinstance(BlenderRNA *brna) RNA_def_property_range(prop, 0.0, 1.0); RNA_def_property_ui_text(prop, "Random Position", "Randomize position along path"); RNA_def_property_update(prop, 0, "rna_Modifier_update"); + + prop = RNA_def_property(srna, "rotation", PROP_FLOAT, PROP_FACTOR); + RNA_def_property_float_sdna(prop, NULL, "rotation"); + RNA_def_property_range(prop, 0.0, 1.0); + RNA_def_property_ui_text(prop, "Rotation", "Rotation around path"); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); + + prop = RNA_def_property(srna, "random_rotation", PROP_FLOAT, PROP_FACTOR); + RNA_def_property_float_sdna(prop, NULL, "random_rotation"); + RNA_def_property_range(prop, 0.0, 1.0); + RNA_def_property_ui_text(prop, "Random Rotation", "Randomize rotation around path"); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); + + prop = RNA_def_property(srna, "particle_amount", PROP_FLOAT, PROP_FACTOR); + RNA_def_property_range(prop, 0.0, 1.0); + RNA_def_property_ui_text(prop, "Particle Amount", "Amount of particles to use for instancing"); + RNA_def_property_float_default(prop, 1.0f); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); + + prop = RNA_def_property(srna, "particle_offset", PROP_FLOAT, PROP_FACTOR); + RNA_def_property_range(prop, 0.0, 1.0); + RNA_def_property_ui_text(prop, "Particle Offset", "Relative offset of particles to use for instancing, to avoid overlap of multiple instances"); + RNA_def_property_float_default(prop, 0.0f); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); + + prop = RNA_def_property(srna, "index_layer_name", PROP_STRING, PROP_NONE); + RNA_def_property_string_sdna(prop, NULL, "index_layer_name"); + RNA_def_property_ui_text(prop, "Index Layer Name", "Custom data layer name for the index"); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); + + prop = RNA_def_property(srna, "value_layer_name", PROP_STRING, PROP_NONE); + RNA_def_property_string_sdna(prop, NULL, "value_layer_name"); + RNA_def_property_ui_text(prop, "Value Layer Name", "Custom data layer name for the randomized value"); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); } static void rna_def_modifier_explode(BlenderRNA *brna) @@ -2620,7 +2718,7 @@ static void rna_def_modifier_cloth(BlenderRNA *brna) RNA_def_property_struct_type(prop, "ClothSolverResult"); RNA_def_property_pointer_sdna(prop, NULL, "solver_result"); RNA_def_property_ui_text(prop, "Solver Result", ""); - + prop = RNA_def_property(srna, "point_cache", PROP_POINTER, PROP_NONE); RNA_def_property_flag(prop, PROP_NEVER_NULL); RNA_def_property_ui_text(prop, "Point Cache", ""); diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c index b328fe6bc38..eb6c481f077 100644 --- a/source/blender/makesrna/intern/rna_nodetree.c +++ b/source/blender/makesrna/intern/rna_nodetree.c @@ -39,6 +39,7 @@ #include "DNA_mesh_types.h" #include "DNA_node_types.h" #include "DNA_object_types.h" +#include "DNA_particle_types.h" #include "DNA_scene_types.h" #include "DNA_text_types.h" #include "DNA_texture_types.h" @@ -64,6 +65,8 @@ #include "MEM_guardedalloc.h" +#include "RE_render_ext.h" + EnumPropertyItem node_socket_in_out_items[] = { { SOCK_IN, "IN", 0, "Input", "" }, { SOCK_OUT, "OUT", 0, "Output", "" }, @@ -2969,6 +2972,89 @@ static void rna_CompositorNodeScale_update(Main *bmain, Scene *scene, PointerRNA rna_Node_update(bmain, scene, ptr); } +static PointerRNA rna_ShaderNodePointDensity_psys_get(PointerRNA *ptr) +{ + bNode *node = ptr->data; + NodeShaderTexPointDensity *shader_point_density = node->storage; + Object *ob = (Object*)node->id; + ParticleSystem *psys = NULL; + PointerRNA value; + + if (ob && shader_point_density->particle_system) { + psys = BLI_findlink(&ob->particlesystem, shader_point_density->particle_system - 1); + } + + RNA_pointer_create(&ob->id, &RNA_ParticleSystem, psys, &value); + return value; +} + +static void rna_ShaderNodePointDensity_psys_set(PointerRNA *ptr, PointerRNA value) +{ + bNode *node = ptr->data; + NodeShaderTexPointDensity *shader_point_density = node->storage; + Object *ob = (Object*)node->id; + + if (ob && value.id.data == ob) { + shader_point_density->particle_system = BLI_findindex(&ob->particlesystem, value.data) + 1; + } + else { + shader_point_density->particle_system = 0; + } +} + +static int point_density_color_source_from_shader(NodeShaderTexPointDensity *shader_point_density) +{ + switch (shader_point_density->color_source) { + case SHD_POINTDENSITY_COLOR_CONSTANT: return TEX_PD_COLOR_CONSTANT; + case SHD_POINTDENSITY_COLOR_PARTAGE: return TEX_PD_COLOR_PARTAGE; + case SHD_POINTDENSITY_COLOR_PARTSPEED: return TEX_PD_COLOR_PARTSPEED; + case SHD_POINTDENSITY_COLOR_PARTVEL: return TEX_PD_COLOR_PARTVEL; + case SHD_POINTDENSITY_COLOR_PARTTEX: return TEX_PD_COLOR_PARTTEX; + default: BLI_assert(false); return TEX_PD_COLOR_CONSTANT; + } +} + +/* TODO(sergey): This functio nassumes allocated array was passed, + * works fine with Cycles via C++ RNA, but fails with call from python. + */ +void rna_ShaderNodePointDensity_density_calc(bNode *self, Scene *scene, int *length, float **values) +{ + NodeShaderTexPointDensity *shader_point_density = self->storage; + PointDensity pd; + + *length = 4 * shader_point_density->resolution * + shader_point_density->resolution * + shader_point_density->resolution; + + if (*values == NULL) { + *values = MEM_mallocN(sizeof(float) * (*length), "point density dynamic array"); + } + + /* Create PointDensity structure from node for sampling. */ + BKE_texture_pointdensity_init_data(&pd); + pd.object = (Object *)self->id; + pd.radius = shader_point_density->radius; + if (shader_point_density->point_source == SHD_POINTDENSITY_SOURCE_PSYS) { + pd.source = TEX_PD_PSYS; + pd.psys = shader_point_density->particle_system; + pd.psys_cache_space = TEX_PD_OBJECTSPACE; + } + else { + BLI_assert(shader_point_density->point_source == SHD_POINTDENSITY_SOURCE_OBJECT); + pd.source = TEX_PD_OBJECT; + pd.ob_cache_space = TEX_PD_OBJECTSPACE; + } + pd.color_source = point_density_color_source_from_shader(shader_point_density); + + /* Single-threaded sampling of the voxel domain. */ + RE_sample_point_density(scene, &pd, + shader_point_density->resolution, + *values); + + /* We're done, time to clean up. */ + BKE_texture_pointdensity_free_data(&pd); +} + static void rna_ShaderNodeOpenVDB_update(Main *bmain, Scene *scene, PointerRNA *ptr) { bNodeTree *ntree = (bNodeTree *)ptr->id.data; @@ -3807,6 +3893,105 @@ static void def_sh_tex_wireframe(StructRNA *srna) RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); } +static void def_sh_tex_pointdensity(StructRNA *srna) +{ + PropertyRNA *prop; + FunctionRNA *func; + + static EnumPropertyItem point_source_items[] = { + {SHD_POINTDENSITY_SOURCE_PSYS, "PARTICLE_SYSTEM", 0, "Particle System", + "Generate point density from a particle system"}, + {SHD_POINTDENSITY_SOURCE_OBJECT, "OBJECT", 0, "Object Vertices", + "Generate point density from an object's vertices"}, + {0, NULL, 0, NULL, NULL} + }; + + static const EnumPropertyItem prop_interpolation_items[] = { + {SHD_INTERP_CLOSEST, "Closest", 0, "Closest", + "No interpolation (sample closest texel)"}, + {SHD_INTERP_LINEAR, "Linear", 0, "Linear", + "Linear interpolation"}, + {SHD_INTERP_CUBIC, "Cubic", 0, "Cubic", + "Cubic interpolation (CPU only)"}, + {0, NULL, 0, NULL, NULL} + }; + + static EnumPropertyItem space_items[] = { + {SHD_POINTDENSITY_SPACE_OBJECT, "OBJECT", 0, "Object Space", ""}, + {SHD_POINTDENSITY_SPACE_WORLD, "WORLD", 0, "World Space", ""}, + {0, NULL, 0, NULL, NULL} + }; + + static EnumPropertyItem color_source_items[] = { + {SHD_POINTDENSITY_COLOR_CONSTANT, "CONSTANT", 0, "Constant", ""}, + {SHD_POINTDENSITY_COLOR_PARTAGE, "PARTICLE_AGE", 0, "Particle Age", + "Lifetime mapped as 0.0 - 1.0 intensity"}, + {SHD_POINTDENSITY_COLOR_PARTSPEED, "PARTICLE_SPEED", 0, "Particle Speed", + "Particle speed (absolute magnitude of velocity) mapped as 0.0-1.0 intensity"}, + {SHD_POINTDENSITY_COLOR_PARTVEL, "PARTICLE_VELOCITY", 0, "Particle Velocity", + "XYZ velocity mapped to RGB colors"}, + {SHD_POINTDENSITY_COLOR_PARTTEX, "PARTICLE_TEXTURE", 0, "Particle Texture", "Texture color of particles"}, + {0, NULL, 0, NULL, NULL} + }; + + prop = RNA_def_property(srna, "object", PROP_POINTER, PROP_NONE); + RNA_def_property_pointer_sdna(prop, NULL, "id"); + RNA_def_property_struct_type(prop, "Object"); + RNA_def_property_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text(prop, "Object", "Object to take point data from)"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); + + RNA_def_struct_sdna_from(srna, "NodeShaderTexPointDensity", "storage"); + + prop = RNA_def_property(srna, "point_source", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, point_source_items); + RNA_def_property_ui_text(prop, "Point Source", "Point data to use as renderable point density"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); + + prop = RNA_def_property(srna, "particle_system", PROP_POINTER, PROP_NONE); + RNA_def_property_ui_text(prop, "Particle System", "Particle System to render as points"); + RNA_def_property_struct_type(prop, "ParticleSystem"); + RNA_def_property_pointer_funcs(prop, "rna_ShaderNodePointDensity_psys_get", + "rna_ShaderNodePointDensity_psys_set", NULL, NULL); + RNA_def_property_flag(prop, PROP_EDITABLE); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); + + prop = RNA_def_property(srna, "resolution", PROP_INT, PROP_NONE); + RNA_def_property_range(prop, 1, 32768); + RNA_def_property_ui_text(prop, "Resolution", "Resolution used by the texture holding the point density"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); + + prop = RNA_def_property(srna, "radius", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "radius"); + RNA_def_property_range(prop, 0.001, FLT_MAX); + RNA_def_property_ui_text(prop, "Radius", "Radius from the shaded sample to look for points within"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); + + prop = RNA_def_property(srna, "space", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, space_items); + RNA_def_property_ui_text(prop, "Space", "Coordinate system to calculate voxels in"); + RNA_def_property_update(prop, 0, "rna_Node_update"); + + prop = RNA_def_property(srna, "interpolation", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, prop_interpolation_items); + RNA_def_property_ui_text(prop, "Interpolation", "Texture interpolation"); + RNA_def_property_update(prop, 0, "rna_Node_update"); + + prop = RNA_def_property(srna, "color_source", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "color_source"); + RNA_def_property_enum_items(prop, color_source_items); + RNA_def_property_ui_text(prop, "Color Source", "Data to derive color results from"); + RNA_def_property_update(prop, 0, "rna_Node_update"); + + func = RNA_def_function(srna, "calc_point_density", "rna_ShaderNodePointDensity_density_calc"); + RNA_def_function_ui_description(func, "Calculate point density"); + RNA_def_pointer(func, "scene", "Scene", "", ""); + /* TODO, See how array size of 0 works, this shouldnt be used. */ + prop = RNA_def_float_array(func, "rgba_values", 1, NULL, 0, 0, "", "RGBA Values", 0, 0); + RNA_def_property_flag(prop, PROP_DYNAMIC); + RNA_def_function_output(func, prop); +} + static void def_glossy(StructRNA *srna) { PropertyRNA *prop; diff --git a/source/blender/makesrna/intern/rna_object.c b/source/blender/makesrna/intern/rna_object.c index 2ef5c6ec12e..17c75da2c87 100644 --- a/source/blender/makesrna/intern/rna_object.c +++ b/source/blender/makesrna/intern/rna_object.c @@ -42,6 +42,7 @@ #include "BLI_utildefines.h" #include "BLI_listbase.h" +#include "BKE_facemap.h" #include "BKE_camera.h" #include "BKE_paint.h" #include "BKE_editmesh.h" @@ -70,6 +71,7 @@ EnumPropertyItem object_mode_items[] = { {OB_MODE_WEIGHT_PAINT, "WEIGHT_PAINT", ICON_WPAINT_HLT, "Weight Paint", ""}, {OB_MODE_TEXTURE_PAINT, "TEXTURE_PAINT", ICON_TPAINT_HLT, "Texture Paint", ""}, {OB_MODE_PARTICLE_EDIT, "PARTICLE_EDIT", ICON_PARTICLEMODE, "Particle Edit", ""}, + {OB_MODE_HAIR_EDIT, "HAIR_EDIT", ICON_PARTICLEMODE, "Hair Edit", ""}, {0, NULL, 0, NULL, NULL} }; @@ -182,12 +184,15 @@ EnumPropertyItem object_axis_unsigned_items[] = { #include "BLI_math.h" #include "DNA_key_types.h" +#include "DNA_cache_library_types.h" #include "DNA_constraint_types.h" #include "DNA_lattice_types.h" #include "DNA_node_types.h" +#include "BKE_anim.h" #include "BKE_armature.h" #include "BKE_bullet.h" +#include "BKE_cache_library.h" #include "BKE_constraint.h" #include "BKE_context.h" #include "BKE_curve.h" @@ -200,6 +205,7 @@ EnumPropertyItem object_axis_unsigned_items[] = { #include "BKE_mesh.h" #include "BKE_particle.h" #include "BKE_scene.h" +#include "BKE_strands.h" #include "BKE_deform.h" #include "ED_mesh.h" @@ -629,6 +635,87 @@ void rna_object_vgroup_name_set(PointerRNA *ptr, const char *value, char *result result[0] = '\0'; } +static void rna_FaceMap_name_set(PointerRNA *ptr, const char *value) +{ + Object *ob = (Object *)ptr->id.data; + bFaceMap *fmap = (bFaceMap *)ptr->data; + BLI_strncpy_utf8(fmap->name, value, sizeof(fmap->name)); + fmap_unique_name(fmap, ob); +} + +static int rna_FaceMap_index_get(PointerRNA *ptr) +{ + Object *ob = (Object *)ptr->id.data; + + return BLI_findindex(&ob->fmaps, ptr->data); +} + +static PointerRNA rna_Object_active_face_map_get(PointerRNA *ptr) +{ + Object *ob = (Object *)ptr->id.data; + return rna_pointer_inherit_refine(ptr, &RNA_FaceMap, BLI_findlink(&ob->fmaps, ob->actfmap - 1)); +} + +static int rna_Object_active_face_map_index_get(PointerRNA *ptr) +{ + Object *ob = (Object *)ptr->id.data; + return ob->actfmap - 1; +} + +static void rna_Object_active_face_map_index_set(PointerRNA *ptr, int value) +{ + Object *ob = (Object *)ptr->id.data; + ob->actfmap = value + 1; +} + +static void rna_Object_active_face_map_index_range(PointerRNA *ptr, int *min, int *max, + int *UNUSED(softmin), int *UNUSED(softmax)) +{ + Object *ob = (Object *)ptr->id.data; + + *min = 0; + *max = max_ii(0, BLI_listbase_count(&ob->fmaps) - 1); +} + +void rna_object_fmap_name_index_get(PointerRNA *ptr, char *value, int index) +{ + Object *ob = (Object *)ptr->id.data; + bFaceMap *fmap; + + fmap = BLI_findlink(&ob->fmaps, index - 1); + + if (fmap) BLI_strncpy(value, fmap->name, sizeof(fmap->name)); + else value[0] = '\0'; +} + +int rna_object_fmap_name_index_length(PointerRNA *ptr, int index) +{ + Object *ob = (Object *)ptr->id.data; + bFaceMap *fmap; + + fmap = BLI_findlink(&ob->fmaps, index - 1); + return (fmap) ? strlen(fmap->name) : 0; +} + +void rna_object_fmap_name_index_set(PointerRNA *ptr, const char *value, short *index) +{ + Object *ob = (Object *)ptr->id.data; + *index = fmap_name_index(ob, value) + 1; +} + +void rna_object_fmap_name_set(PointerRNA *ptr, const char *value, char *result, int maxlen) +{ + Object *ob = (Object *)ptr->id.data; + bFaceMap *fmap = fmap_find_name(ob, value); + if (fmap) { + BLI_strncpy(result, value, maxlen); /* no need for BLI_strncpy_utf8, since this matches an existing group */ + return; + } + + result[0] = '\0'; +} + + void rna_object_uvlayer_name_set(PointerRNA *ptr, const char *value, char *result, int maxlen) { Object *ob = (Object *)ptr->id.data; @@ -1464,6 +1551,69 @@ static float rna_VertexGroup_weight(ID *id, bDeformGroup *dg, ReportList *report return weight; } +static bFaceMap *rna_Object_fmap_new(Object *ob, const char *name) +{ + bFaceMap *fmap = BKE_object_facemap_add_name(ob, name); + + WM_main_add_notifier(NC_OBJECT | ND_DRAW, ob); + + return fmap; +} + +static void rna_Object_fmap_remove(Object *ob, ReportList *reports, PointerRNA *fmap_ptr) +{ + bFaceMap *fmap = fmap_ptr->data; + if (BLI_findindex(&ob->fmaps, fmap) == -1) { + BKE_reportf(reports, RPT_ERROR, "FaceMap '%s' not in object '%s'", fmap->name, ob->id.name + 2); + return; + } + + BKE_object_facemap_remove(ob, fmap); + RNA_POINTER_INVALIDATE(fmap_ptr); + + WM_main_add_notifier(NC_OBJECT | ND_DRAW, ob); +} + + +static void rna_Object_fmap_clear(Object *ob) +{ + BKE_object_fmap_remove_all(ob); + + WM_main_add_notifier(NC_OBJECT | ND_DRAW, ob); +} + + +static void rna_FaceMap_face_add(ID *id, bFaceMap *fmap, ReportList *reports, int index_len, + int *index) +{ + Object *ob = (Object *)id; + + if (BKE_object_is_in_editmode(ob)) { + BKE_report(reports, RPT_ERROR, "FaceMap.add(): cannot be called while object is in edit mode"); + return; + } + + while (index_len--) + ED_fmap_face_add(ob, fmap, *index++); + + WM_main_add_notifier(NC_GEOM | ND_DATA, (ID *)ob->data); +} + +static void rna_FaceMap_face_remove(ID *id, bFaceMap *fmap, ReportList *reports, int index_len, int *index) +{ + Object *ob = (Object *)id; + + if (BKE_object_is_in_editmode(ob)) { + BKE_report(reports, RPT_ERROR, "FaceMap.add(): cannot be called while object is in edit mode"); + return; + } + + while (index_len--) + ED_fmap_face_remove(ob, fmap, *index++); + + WM_main_add_notifier(NC_GEOM | ND_DATA, (ID *)ob->data); +} + /* generic poll functions */ int rna_Lattice_object_poll(PointerRNA *UNUSED(ptr), PointerRNA value) { @@ -1517,6 +1667,120 @@ static void rna_Object_lod_distance_update(Main *UNUSED(bmain), Scene *UNUSED(sc (void)ob; #endif } + +/* settings: 1 - preview, 2 - render */ +Strands *rna_DupliObject_strands_new(DupliObject *dob, ReportList *UNUSED(reports), Scene *scene, Object *parent, ParticleSystem *psys, int settings) +{ + Strands *strands = NULL; + bool is_cached = parent->cache_library && (parent->cache_library->source_mode == CACHE_LIBRARY_SOURCE_CACHE || parent->cache_library->display_mode == CACHE_LIBRARY_DISPLAY_RESULT); + + if (is_cached) { + float frame = (float)scene->r.cfra + scene->r.subframe; + bool use_render = (settings == 2); + + if (!ELEM(settings, 1, 2)) + return NULL; + + if (!use_render && parent->dup_cache) { + DupliObjectData *data; + + /* use dupli cache for realtime dupli data if possible */ + data = BKE_dupli_cache_find_data(parent->dup_cache, dob->ob); + if (data) { + /* TODO(sergey): Consider sharing the data between viewport and + * render engine. + */ + BKE_dupli_object_data_find_strands(data, psys->name, &strands, NULL); + if (strands) { + strands = BKE_strands_copy(strands); + } + } + } + else { + DupliObjectData data; + + memset(&data, 0, sizeof(data)); + if (BKE_cache_read_dupli_object(parent->cache_library, &data, scene, dob->ob, frame, use_render, true)) { + BKE_dupli_object_data_find_strands(&data, psys->name, &strands, NULL); + if (strands) + BKE_dupli_object_data_acquire_strands(&data, strands); + } + + BKE_dupli_object_data_clear(&data); + } + } + + return strands; +} + +static void rna_DupliObject_strands_free(DupliObject *UNUSED(dob), ReportList *UNUSED(reports), PointerRNA *strands_ptr) +{ + Strands *strands = strands_ptr->data; + if (strands) + BKE_strands_free(strands); +} + +/* settings: 1 - preview, 2 - render */ +StrandsChildren *rna_DupliObject_strands_children_new(DupliObject *dob, ReportList *UNUSED(reports), Scene *scene, Object *parent, ParticleSystem *psys, int settings) +{ + StrandsChildren *strands = NULL; + bool is_cached = parent->cache_library && (parent->cache_library->source_mode == CACHE_LIBRARY_SOURCE_CACHE || parent->cache_library->display_mode == CACHE_LIBRARY_DISPLAY_RESULT); + + if (is_cached) { + float frame = (float)scene->r.cfra + scene->r.subframe; + bool use_render = (settings == 2); + + if (!ELEM(settings, 1, 2)) + return NULL; + + if (!use_render && parent->dup_cache) { + DupliObjectData *data; + + /* use dupli cache for realtime dupli data if possible */ + data = BKE_dupli_cache_find_data(parent->dup_cache, dob->ob); + if (data) { + /* TODO(sergey): Consider sharing the data between viewport and + * render engine. + */ + BKE_dupli_object_data_find_strands(data, psys->name, NULL, &strands); + if (strands) { + strands = BKE_strands_children_copy(strands); + } + } + } + else { + DupliObjectData data; + + memset(&data, 0, sizeof(data)); + if (BKE_cache_read_dupli_object(parent->cache_library, &data, scene, dob->ob, frame, use_render, true)) { + Strands *parents; + BKE_dupli_object_data_find_strands(&data, psys->name, &parents, &strands); + if (strands) { + BKE_dupli_object_data_acquire_strands_children(&data, strands); + + /* Deform child strands to follow parent motion. + * Note that this is an optional feature for viewport/render display, + * strand motion is not applied to raw child data in caches. + */ + if (parents) + BKE_strands_children_deform(strands, parents, true); + } + } + + BKE_dupli_object_data_clear(&data); + } + } + + return strands; +} + +static void rna_DupliObject_strands_children_free(DupliObject *UNUSED(dob), ReportList *UNUSED(reports), PointerRNA *strands_ptr) +{ + StrandsChildren *strands = strands_ptr->data; + if (strands) + BKE_strands_children_free(strands); +} + #else static void rna_def_vertex_group(BlenderRNA *brna) @@ -1582,6 +1846,44 @@ static void rna_def_vertex_group(BlenderRNA *brna) RNA_def_function_return(func, prop); } +static void rna_def_face_map(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + FunctionRNA *func; + + srna = RNA_def_struct(brna, "FaceMap", NULL); + RNA_def_struct_sdna(srna, "bFaceMap"); + RNA_def_struct_ui_text(srna, "Face Map", "Group of faces, each face can only be part of one map"); + RNA_def_struct_ui_icon(srna, ICON_MOD_TRIANGULATE); + + prop = RNA_def_property(srna, "name", PROP_STRING, PROP_NONE); + RNA_def_property_ui_text(prop, "Name", "Face map name"); + RNA_def_struct_name_property(srna, prop); + RNA_def_property_string_funcs(prop, NULL, NULL, "rna_FaceMap_name_set"); + /* update data because modifiers may use [#24761] */ + RNA_def_property_update(prop, NC_GEOM | ND_DATA | NA_RENAME, "rna_Object_internal_update_data"); + + prop = RNA_def_property(srna, "index", PROP_INT, PROP_UNSIGNED); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_int_funcs(prop, "rna_FaceMap_index_get", NULL, NULL); + RNA_def_property_ui_text(prop, "Index", "Index number of the face map"); + + func = RNA_def_function(srna, "add", "rna_FaceMap_face_add"); + RNA_def_function_ui_description(func, "Add vertices to the group"); + RNA_def_function_flag(func, FUNC_USE_REPORTS | FUNC_USE_SELF_ID); + /* TODO, see how array size of 0 works, this shouldnt be used */ + prop = RNA_def_int_array(func, "index", 1, NULL, 0, 0, "", "Index List", 0, 0); + RNA_def_property_flag(prop, PROP_DYNAMIC | PROP_REQUIRED); + + func = RNA_def_function(srna, "remove", "rna_FaceMap_face_remove"); + RNA_def_function_ui_description(func, "Remove a vertex from the group"); + RNA_def_function_flag(func, FUNC_USE_REPORTS | FUNC_USE_SELF_ID); + /* TODO, see how array size of 0 works, this shouldnt be used */ + prop = RNA_def_int_array(func, "index", 1, NULL, 0, 0, "", "Index List", 0, 0); + RNA_def_property_flag(prop, PROP_DYNAMIC | PROP_REQUIRED); +} + static void rna_def_material_slot(BlenderRNA *brna) { StructRNA *srna; @@ -2086,6 +2388,54 @@ static void rna_def_object_vertex_groups(BlenderRNA *brna, PropertyRNA *cprop) RNA_def_function_ui_description(func, "Delete all vertex groups from object"); } +/* object.vertex_groups */ +static void rna_def_object_face_maps(BlenderRNA *brna, PropertyRNA *cprop) +{ + StructRNA *srna; + + PropertyRNA *prop; + + FunctionRNA *func; + PropertyRNA *parm; + + RNA_def_property_srna(cprop, "FaceMaps"); + srna = RNA_def_struct(brna, "FaceMaps", NULL); + RNA_def_struct_sdna(srna, "Object"); + RNA_def_struct_ui_text(srna, "Face Maps", "Collection of face maps"); + + prop = RNA_def_property(srna, "active", PROP_POINTER, PROP_NONE); + RNA_def_property_struct_type(prop, "FaceMap"); + RNA_def_property_pointer_funcs(prop, "rna_Object_active_face_map_get", + "rna_Object_active_face_map_set", NULL, NULL); + RNA_def_property_ui_text(prop, "Active Face Map", "Face maps of the object"); + RNA_def_property_update(prop, NC_GEOM | ND_DATA, "rna_Object_internal_update_data"); + + prop = RNA_def_property(srna, "active_index", PROP_INT, PROP_UNSIGNED); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_int_sdna(prop, NULL, "actfmap"); + RNA_def_property_int_funcs(prop, "rna_Object_active_face_map_index_get", + "rna_Object_active_face_map_index_set", + "rna_Object_active_face_map_index_range"); + RNA_def_property_ui_text(prop, "Active Face Map Index", "Active index in face map array"); + RNA_def_property_update(prop, NC_GEOM | ND_DATA, "rna_Object_internal_update_data"); + + /* face maps */ /* add_face_map */ + func = RNA_def_function(srna, "new", "rna_Object_fmap_new"); + RNA_def_function_ui_description(func, "Add face map to object"); + RNA_def_string(func, "name", "Map", 0, "", "face map name"); /* optional */ + parm = RNA_def_pointer(func, "fmap", "FaceMap", "", "New face map"); + RNA_def_function_return(func, parm); + + func = RNA_def_function(srna, "remove", "rna_Object_fmap_remove"); + RNA_def_function_flag(func, FUNC_USE_REPORTS); + RNA_def_function_ui_description(func, "Delete vertex group from object"); + parm = RNA_def_pointer(func, "group", "FaceMap", "", "Face map to remove"); + RNA_def_property_flag(parm, PROP_REQUIRED | PROP_NEVER_NULL | PROP_RNAPTR); + RNA_def_property_clear_flag(parm, PROP_THICK_WRAP); + + func = RNA_def_function(srna, "clear", "rna_Object_fmap_clear"); + RNA_def_function_ui_description(func, "Delete all vertex groups from object"); +} static void rna_def_object_lodlevel(BlenderRNA *brna) { @@ -2506,6 +2856,14 @@ static void rna_def_object(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Vertex Groups", "Vertex groups of the object"); rna_def_object_vertex_groups(brna, prop); + + /* vertex groups */ + prop = RNA_def_property(srna, "face_maps", PROP_COLLECTION, PROP_NONE); + RNA_def_property_collection_sdna(prop, NULL, "fmaps", NULL); + RNA_def_property_struct_type(prop, "FaceMap"); + RNA_def_property_ui_text(prop, "Face Maps", "Maps of faces of the object"); + rna_def_object_face_maps(brna, prop); + /* empty */ prop = RNA_def_property(srna, "empty_draw_type", PROP_ENUM, PROP_NONE); RNA_def_property_enum_sdna(prop, NULL, "empty_drawtype"); @@ -2666,6 +3024,12 @@ static void rna_def_object(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Dupli Group", "Instance an existing group"); RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_Object_dependency_update"); + prop = RNA_def_property(srna, "cache_library", PROP_POINTER, PROP_NONE); + RNA_def_property_pointer_sdna(prop, NULL, "cache_library"); + RNA_def_property_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text(prop, "Cache Library", "Cache Library to use"); + RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_Object_dependency_update"); + prop = RNA_def_property(srna, "dupli_frames_start", PROP_INT, PROP_NONE | PROP_UNIT_TIME); RNA_def_property_int_sdna(prop, NULL, "dupsta"); RNA_def_property_range(prop, MINAFRAME, MAXFRAME); @@ -2744,6 +3108,11 @@ static void rna_def_object(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Draw All Edges", "Display all edges for mesh objects"); RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, NULL); + prop = RNA_def_property(srna, "show_wire_color", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "dtx", OB_DRAW_WIRECOLOR); + RNA_def_property_ui_text(prop, "Draw Wire Color", "Use custom wire color"); + RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, NULL); + prop = RNA_def_property(srna, "show_transparent", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "dtx", OB_DRAWTRANSP); RNA_def_property_ui_text(prop, "Draw Transparent", @@ -2822,6 +3191,14 @@ static void rna_def_dupli_object(BlenderRNA *brna) { StructRNA *srna; PropertyRNA *prop; + FunctionRNA *func; + PropertyRNA *parm; + + static EnumPropertyItem strand_settings_items[] = { + {1, "PREVIEW", 0, "Preview", "Apply preview settings"}, + {2, "RENDER", 0, "Render", "Apply render settings"}, + {0, NULL, 0, NULL, NULL} + }; srna = RNA_def_struct(brna, "DupliObject", NULL); RNA_def_struct_sdna(srna, "DupliObject"); @@ -2870,6 +3247,60 @@ static void rna_def_dupli_object(BlenderRNA *brna) RNA_def_property_enum_items(prop, dupli_items); RNA_def_property_clear_flag(prop, PROP_ANIMATABLE | PROP_EDITABLE); RNA_def_property_ui_text(prop, "Dupli Type", "Duplicator type that generated this dupli object"); + + func = RNA_def_function(srna, "strands_new", "rna_DupliObject_strands_new"); + RNA_def_function_ui_description(func, "Add new strands created from dupli cache data"); + RNA_def_function_flag(func, FUNC_USE_REPORTS); + parm = RNA_def_pointer(func, "scene", "Scene", "", "Scene within which to evaluate modifiers"); + RNA_def_property_flag(parm, PROP_REQUIRED | PROP_NEVER_NULL); + parm = RNA_def_pointer(func, "parent", "Object", "", "Duplicator parent of the object"); + RNA_def_property_flag(parm, PROP_REQUIRED | PROP_NEVER_NULL); + parm = RNA_def_pointer(func, "particle_system", "ParticleSystem", "", "Particle System"); + RNA_def_property_flag(parm, PROP_REQUIRED | PROP_NEVER_NULL); + parm = RNA_def_enum(func, "settings", strand_settings_items, 0, "", "Modifier settings to apply"); + RNA_def_property_flag(parm, PROP_REQUIRED); + parm = RNA_def_pointer(func, "strands", "Strands", "", + "Strands created from object, remove it if it is only used for export"); + RNA_def_function_return(func, parm); + + func = RNA_def_function(srna, "strands_free", "rna_DupliObject_strands_free"); + RNA_def_function_flag(func, FUNC_USE_REPORTS); + RNA_def_function_ui_description(func, "Free strands data"); + parm = RNA_def_pointer(func, "strands", "Strands", "", "Strands to remove"); + RNA_def_property_flag(parm, PROP_REQUIRED | PROP_NEVER_NULL | PROP_RNAPTR); + RNA_def_property_clear_flag(parm, PROP_THICK_WRAP); + + func = RNA_def_function(srna, "strands_children_new", "rna_DupliObject_strands_children_new"); + RNA_def_function_ui_description(func, "Add new strands created from dupli cache data"); + RNA_def_function_flag(func, FUNC_USE_REPORTS); + parm = RNA_def_pointer(func, "scene", "Scene", "", "Scene within which to evaluate modifiers"); + RNA_def_property_flag(parm, PROP_REQUIRED | PROP_NEVER_NULL); + parm = RNA_def_pointer(func, "parent", "Object", "", "Duplicator parent of the object"); + RNA_def_property_flag(parm, PROP_REQUIRED | PROP_NEVER_NULL); + parm = RNA_def_pointer(func, "particle_system", "ParticleSystem", "", "Particle System"); + RNA_def_property_flag(parm, PROP_REQUIRED | PROP_NEVER_NULL); + parm = RNA_def_enum(func, "settings", strand_settings_items, 0, "", "Modifier settings to apply"); + RNA_def_property_flag(parm, PROP_REQUIRED); + parm = RNA_def_pointer(func, "strands", "StrandsChildren", "", + "Strands created from object, remove it if it is only used for export"); + RNA_def_function_return(func, parm); + + func = RNA_def_function(srna, "strands_children_free", "rna_DupliObject_strands_children_free"); + RNA_def_function_flag(func, FUNC_USE_REPORTS); + RNA_def_function_ui_description(func, "Free strands data"); + parm = RNA_def_pointer(func, "strands", "StrandsChildren", "", "Strands to remove"); + RNA_def_property_flag(parm, PROP_REQUIRED | PROP_NEVER_NULL | PROP_RNAPTR); + RNA_def_property_clear_flag(parm, PROP_THICK_WRAP); +} + +static void rna_def_dupli_object_data(BlenderRNA *brna) +{ + StructRNA *srna; + /*PropertyRNA *prop;*/ + + srna = RNA_def_struct(brna, "DupliObjectData", NULL); + RNA_def_struct_sdna(srna, "DupliObjectData"); + RNA_def_struct_ui_text(srna, "Object Duplicate Data", "Override of object data for duplis"); } static void rna_def_object_base(BlenderRNA *brna) @@ -2916,8 +3347,10 @@ void RNA_def_object(BlenderRNA *brna) rna_def_object_game_settings(brna); rna_def_object_base(brna); rna_def_vertex_group(brna); + rna_def_face_map(brna); rna_def_material_slot(brna); rna_def_dupli_object(brna); + rna_def_dupli_object_data(brna); RNA_define_animate_sdna(true); rna_def_object_lodlevel(brna); } diff --git a/source/blender/makesrna/intern/rna_object_api.c b/source/blender/makesrna/intern/rna_object_api.c index 4b3f34f46c0..c0aee13d37b 100644 --- a/source/blender/makesrna/intern/rna_object_api.c +++ b/source/blender/makesrna/intern/rna_object_api.c @@ -224,6 +224,18 @@ static void rna_Object_free_duplilist(Object *ob) } } +static PointerRNA rna_Object_find_dupli_cache(Object *ob, Object *dupob) +{ + DupliObjectData *data = NULL; + PointerRNA ptr; + + if (ob->dup_cache) + data = BKE_dupli_cache_find_data(ob->dup_cache, dupob); + + RNA_pointer_create((ID *)ob, &RNA_DupliObjectData, data, &ptr); + return ptr; +} + static PointerRNA rna_Object_shape_key_add(Object *ob, bContext *C, ReportList *reports, const char *name, int from_mix) { @@ -466,6 +478,12 @@ static int rna_Object_update_from_editmode(Object *ob) } return false; } + +static void rna_Object_cache_release(Object *object, int free_smoke_sim) +{ + BKE_object_free_caches(object, free_smoke_sim != 0); +} + #else /* RNA_RUNTIME */ void RNA_api_object(StructRNA *srna) @@ -572,6 +590,14 @@ void RNA_api_object(StructRNA *srna) func = RNA_def_function(srna, "dupli_list_clear", "rna_Object_free_duplilist"); RNA_def_function_ui_description(func, "Free the list of dupli objects"); + func = RNA_def_function(srna, "find_dupli_cache", "rna_Object_find_dupli_cache"); + RNA_def_function_ui_description(func, "Find cached data for a dupli object"); + parm = RNA_def_pointer(func, "object", "Object", "", "Object data to look up"); + RNA_def_property_flag(parm, PROP_REQUIRED | PROP_NEVER_NULL); + parm = RNA_def_pointer(func, "data", "DupliObjectData", "", "Cached object data"); + RNA_def_property_flag(parm, PROP_RNAPTR); + RNA_def_function_return(func, parm); + /* Armature */ func = RNA_def_function(srna, "find_armature", "modifiers_isDeformedByArmature"); RNA_def_function_ui_description(func, "Find armature influencing this object as a parent or via a modifier"); @@ -688,7 +714,8 @@ void RNA_api_object(StructRNA *srna) parm = RNA_def_boolean(func, "result", 0, "", "Success"); RNA_def_function_return(func, parm); - func = RNA_def_function(srna, "cache_release", "BKE_object_free_caches"); + func = RNA_def_function(srna, "cache_release", "rna_Object_cache_release"); + RNA_def_boolean(func, "free_smoke_sim", 0, "", "Free baked smoke simulation data"); RNA_def_function_ui_description(func, "Release memory used by caches associated with this object. Intended to be used by render engines only"); } diff --git a/source/blender/makesrna/intern/rna_object_force.c b/source/blender/makesrna/intern/rna_object_force.c index 75becb341b9..21f53f86d90 100644 --- a/source/blender/makesrna/intern/rna_object_force.c +++ b/source/blender/makesrna/intern/rna_object_force.c @@ -1337,6 +1337,11 @@ static void rna_def_field(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Use Max", "Use a maximum distance for the field to work"); RNA_def_property_update(prop, 0, "rna_FieldSettings_update"); + prop = RNA_def_property(srna, "use_signed_distance", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", PFIELD_USE_SIGNED_DISTANCE); + RNA_def_property_ui_text(prop, "Use Signed Distance", "Use negative distance on the interior of surface shapes"); + RNA_def_property_update(prop, 0, "rna_FieldSettings_update"); + prop = RNA_def_property(srna, "use_radial_min", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", PFIELD_USEMINR); RNA_def_property_ui_text(prop, "Use Min", "Use a minimum radial distance for the field's fall-off"); diff --git a/source/blender/makesrna/intern/rna_particle.c b/source/blender/makesrna/intern/rna_particle.c index c0ce8f21870..a6bbbfcb8b4 100644 --- a/source/blender/makesrna/intern/rna_particle.c +++ b/source/blender/makesrna/intern/rna_particle.c @@ -35,6 +35,7 @@ #include "DNA_meshdata_types.h" #include "DNA_modifier_types.h" #include "DNA_cloth_types.h" +#include "DNA_key_types.h" #include "DNA_particle_types.h" #include "DNA_object_force.h" #include "DNA_object_types.h" @@ -97,9 +98,12 @@ static EnumPropertyItem part_draw_as_items[] = { #ifdef RNA_RUNTIME static EnumPropertyItem part_hair_draw_as_items[] = { - {PART_DRAW_NOT, "NONE", 0, "None", ""}, - {PART_DRAW_REND, "RENDER", 0, "Rendered", ""}, - {PART_DRAW_PATH, "PATH", 0, "Path", ""}, + {PART_DRAW_NOT, "NONE", 0, "None", "No hair drawing"}, + {PART_DRAW_REND, "RENDER", 0, "Rendered", "Approximate render result in the viewport"}, + {PART_DRAW_PATH, "PATH", 0, "Path", "Show path of hair particles"}, +#ifdef USE_PARTICLE_HULL_DRAWING + {PART_DRAW_HULL, "HULL", 0, "Hull", "Show convex hull of child particle paths"}, +#endif {0, NULL, 0, NULL, NULL} }; #endif @@ -127,6 +131,7 @@ static EnumPropertyItem part_hair_ren_as_items[] = { #ifdef RNA_RUNTIME +#include "BLI_listbase.h" #include "BLI_math.h" #include "BKE_context.h" @@ -137,12 +142,17 @@ static EnumPropertyItem part_hair_ren_as_items[] = { #include "BKE_DerivedMesh.h" #include "BKE_cdderivedmesh.h" #include "BKE_effect.h" +#include "BKE_key.h" #include "BKE_material.h" #include "BKE_modifier.h" #include "BKE_particle.h" #include "BKE_pointcache.h" #include "BKE_texture.h" +#include "RNA_access.h" + +#include "ED_particle.h" + /* use for object space hair get/set */ static void rna_ParticleHairKey_location_object_info(PointerRNA *ptr, ParticleSystemModifierData **psmd_pt, ParticleData **pa_pt) @@ -735,6 +745,7 @@ static void rna_Particle_hair_dynamics(Main *bmain, Scene *scene, PointerRNA *pt DAG_id_tag_update(&ob->id, OB_RECALC_DATA); } + static PointerRNA rna_particle_settings_get(PointerRNA *ptr) { ParticleSystem *psys = (ParticleSystem *)ptr->data; @@ -763,6 +774,64 @@ static void rna_particle_settings_set(PointerRNA *ptr, PointerRNA value) psys->recalc |= PSYS_RECALC_TYPE; } } + +static void rna_Particle_active_shape_update(Main *bmain, Scene *scene, PointerRNA *ptr) +{ + Object *ob = ptr->id.data; + ParticleSystem *psys = ptr->data; + + if (PE_shapekey_load(ob, psys)) { + WM_main_add_notifier(NC_SCENE | ND_PARTICLE | NS_MODE_PARTICLE, ptr->id.data); + } + + rna_Particle_redo(bmain, scene, ptr); +} + +static void rna_Particle_active_shape_key_index_range(PointerRNA *ptr, int *min, int *max, + int *UNUSED(softmin), int *UNUSED(softmax)) +{ + ParticleSystem *psys = ptr->data; + Key *key = psys->key; + + *min = 0; + if (key) { + *max = BLI_listbase_count(&key->block) - 1; + if (*max < 0) *max = 0; + } + else { + *max = 0; + } +} + +static int rna_Particle_active_shape_key_index_get(PointerRNA *ptr) +{ + ParticleSystem *psys = ptr->data; + + return MAX2(psys->shapenr - 1, 0); +} + +static void rna_Particle_active_shape_key_index_set(PointerRNA *ptr, int value) +{ + ParticleSystem *psys = ptr->data; + + psys->shapenr = value + 1; +} + +static PointerRNA rna_Particle_active_shape_key_get(PointerRNA *ptr) +{ + ParticleSystem *psys = ptr->data; + Key *key = psys->key; + KeyBlock *kb; + PointerRNA keyptr; + + if (key == NULL) + return PointerRNA_NULL; + + kb = BLI_findlink(&key->block, psys->shapenr - 1); + RNA_pointer_create((ID *)key, &RNA_ShapeKey, kb, &keyptr); + return keyptr; +} + static void rna_Particle_abspathtime_update(Main *bmain, Scene *scene, PointerRNA *ptr) { ParticleSettings *settings = (ParticleSettings *)ptr->data; @@ -1834,6 +1903,16 @@ static void rna_def_particle_settings_mtex(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Length", "Affect the child hair length"); RNA_def_property_update(prop, 0, "rna_Particle_redo_child"); + prop = RNA_def_property(srna, "use_map_shapekey", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "mapto", PAMAP_SHAPEKEY); + RNA_def_property_ui_text(prop, "Shape Key", "Affect the blend factor of a hair shape key"); + RNA_def_property_update(prop, 0, "rna_Particle_reset"); + + prop = RNA_def_property(srna, "use_map_particle_color", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "mapto", PAMAP_COLOR); + RNA_def_property_ui_text(prop, "Color", "Affect the particle color"); + RNA_def_property_update(prop, 0, "rna_Particle_reset"); + /* influence factors */ prop = RNA_def_property(srna, "time_factor", PROP_FLOAT, PROP_NONE); @@ -1915,6 +1994,23 @@ static void rna_def_particle_settings_mtex(BlenderRNA *brna) RNA_def_property_ui_range(prop, 0, 1, 10, 3); RNA_def_property_ui_text(prop, "Rough Factor", "Amount texture affects child roughness"); RNA_def_property_update(prop, 0, "rna_Particle_redo_child"); + + prop = RNA_def_property(srna, "shapekey_factor", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "shapefac"); + RNA_def_property_ui_range(prop, 0, 1, 10, 3); + RNA_def_property_ui_text(prop, "Shape Key Factor", "Amount texture affects shape key blend value"); + RNA_def_property_update(prop, 0, "rna_Particle_reset"); + + prop = RNA_def_property(srna, "shapekey", PROP_STRING, PROP_NONE); + RNA_def_property_string_sdna(prop, NULL, "shapekey"); + RNA_def_property_ui_text(prop, "Shape Key", "Name of the shape key affected by the texture"); + RNA_def_property_update(prop, 0, "rna_Particle_reset"); + + prop = RNA_def_property(srna, "particle_color_factor", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "pacolfac"); + RNA_def_property_ui_range(prop, 0, 1, 10, 3); + RNA_def_property_ui_text(prop, "Particle Color Factor", "Amount texture affects particle color"); + RNA_def_property_update(prop, 0, "rna_Particle_redo_child"); } static void rna_def_particle_settings(BlenderRNA *brna) @@ -2033,6 +2129,10 @@ static void rna_def_particle_settings(BlenderRNA *brna) {PART_DRAW_COL_MAT, "MATERIAL", 0, "Material", ""}, {PART_DRAW_COL_VEL, "VELOCITY", 0, "Velocity", ""}, {PART_DRAW_COL_ACC, "ACCELERATION", 0, "Acceleration", ""}, +#ifdef USE_PARTICLE_HULL_DRAWING + {PART_DRAW_COL_PARENT, "PARENT", 0, "Parent", ""}, +#endif + {PART_DRAW_COL_TEX, "TEXTURE", 0, "Texture", ""}, {0, NULL, 0, NULL, NULL} }; @@ -2861,6 +2961,20 @@ static void rna_def_particle_settings(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Clump Noise Size", "Size of clump noise"); RNA_def_property_update(prop, 0, "rna_Particle_redo_child"); + prop = RNA_def_property(srna, "clump_noise_random", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "clump_noise_random"); + RNA_def_property_range(prop, -100000.0f, 100000.0f); + RNA_def_property_ui_range(prop, 0.0f, 10.0f, 0.1f, 3); + RNA_def_property_ui_text(prop, "Clump Noise Random", "Random offset of clump noise"); + RNA_def_property_update(prop, 0, "rna_Particle_redo_child"); + + prop = RNA_def_property(srna, "clump_noise_random_size", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "clump_noise_random_size"); + RNA_def_property_range(prop, 0.00001f, 100000.0f); + RNA_def_property_ui_range(prop, 0.01f, 10.0f, 0.1f, 3); + RNA_def_property_ui_text(prop, "Clump Noise Random Size", "Size of clump noise offset"); + RNA_def_property_update(prop, 0, "rna_Particle_redo_child"); + /* kink */ prop = RNA_def_property(srna, "kink_amplitude", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, NULL, "kink_amp"); @@ -3253,6 +3367,30 @@ static void rna_def_particle_system(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Hair Dynamics", "Enable hair dynamics using cloth simulation"); RNA_def_property_update(prop, 0, "rna_Particle_hair_dynamics"); + prop = RNA_def_property(srna, "hair_preview_factor", PROP_FLOAT, PROP_PERCENTAGE); + RNA_def_property_float_sdna(prop, NULL, "hair_preview_factor"); + RNA_def_property_range(prop, 0.0f, 100.0f); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_ui_text(prop, "Preview Factor", "Part of hair particles to use for simulation preview"); + RNA_def_property_update(prop, 0, "rna_Particle_reset"); + + prop = RNA_def_property(srna, "shape_keys", PROP_POINTER, PROP_NONE); + RNA_def_property_pointer_sdna(prop, NULL, "key"); + RNA_def_property_ui_text(prop, "Shape Keys", ""); + + prop = RNA_def_property(srna, "active_shape_key", PROP_POINTER, PROP_NONE); + RNA_def_property_struct_type(prop, "ShapeKey"); + RNA_def_property_pointer_funcs(prop, "rna_Particle_active_shape_key_get", NULL, NULL, NULL); + RNA_def_property_ui_text(prop, "Active Shape Key", "Current shape key"); + + prop = RNA_def_property(srna, "active_shape_key_index", PROP_INT, PROP_NONE); + RNA_def_property_int_sdna(prop, NULL, "shapenr"); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_int_funcs(prop, "rna_Particle_active_shape_key_index_get", "rna_Particle_active_shape_key_index_set", + "rna_Particle_active_shape_key_index_range"); + RNA_def_property_ui_text(prop, "Active Shape Key Index", "Current shape key index"); + RNA_def_property_update(prop, 0, "rna_Particle_active_shape_update"); + prop = RNA_def_property(srna, "cloth", PROP_POINTER, PROP_NONE); RNA_def_property_pointer_sdna(prop, NULL, "clmd"); RNA_def_property_struct_type(prop, "ClothModifier"); diff --git a/source/blender/makesrna/intern/rna_scene.c b/source/blender/makesrna/intern/rna_scene.c index 00114d1125c..4a05a56b37a 100644 --- a/source/blender/makesrna/intern/rna_scene.c +++ b/source/blender/makesrna/intern/rna_scene.c @@ -2123,6 +2123,10 @@ static void rna_def_tool_settings(BlenderRNA *brna) RNA_def_property_pointer_sdna(prop, NULL, "particle"); RNA_def_property_ui_text(prop, "Particle Edit", ""); + prop = RNA_def_property(srna, "hair_edit", PROP_POINTER, PROP_NONE); + RNA_def_property_pointer_sdna(prop, NULL, "hair_edit"); + RNA_def_property_ui_text(prop, "Hair Edit", ""); + prop = RNA_def_property(srna, "use_uv_sculpt", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "use_uv_sculpt", 1); RNA_def_property_ui_text(prop, "UV Sculpt", "Enable brush for UV sculpting"); diff --git a/source/blender/makesrna/intern/rna_sculpt_paint.c b/source/blender/makesrna/intern/rna_sculpt_paint.c index 7b30aa84cfb..acde663a799 100644 --- a/source/blender/makesrna/intern/rna_sculpt_paint.c +++ b/source/blender/makesrna/intern/rna_sculpt_paint.c @@ -77,6 +77,8 @@ EnumPropertyItem symmetrize_direction_items[] = { #include "BKE_context.h" #include "BKE_DerivedMesh.h" +#include "BKE_editstrands.h" +#include "BKE_effect.h" #include "BKE_pointcache.h" #include "BKE_particle.h" #include "BKE_depsgraph.h" @@ -221,6 +223,8 @@ static int rna_Brush_mode_poll(PointerRNA *ptr, PointerRNA value) mode = OB_MODE_VERTEX_PAINT; else if (ptr->data == ts->wpaint) mode = OB_MODE_WEIGHT_PAINT; + else if (ptr->data == &ts->hair_edit) + mode = OB_MODE_HAIR_EDIT; return brush->ob_mode & mode; } @@ -359,6 +363,30 @@ static int rna_ImaPaint_detect_data(ImagePaintSettings *imapaint) { return imapaint->missing_data == 0; } + +/* ==== Hair Edit ==== */ + +static char *rna_HairEdit_path(PointerRNA *UNUSED(ptr)) +{ + return BLI_strdup("tool_settings.hair_edit"); +} + +static void rna_HairEdit_update(Main *UNUSED(bmain), Scene *scene, PointerRNA *UNUSED(ptr)) +{ + Object *ob = OBACT; + + if (ob) + DAG_id_tag_update(&ob->id, OB_RECALC_DATA); +} + +static void rna_HairEdit_brush_update(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *ptr) +{ + HairEditSettings *settings = ptr->data; + Brush *brush = settings->brush; + BKE_paint_invalidate_overlay_all(); + WM_main_add_notifier(NC_BRUSH | NA_EDITED, brush); +} + #else static void rna_def_paint_curve(BlenderRNA *brna) @@ -911,6 +939,10 @@ static void rna_def_particle_edit(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Puff Volume", "Apply puff to unselected end-points (helps maintain hair volume when puffing root)"); + prop = RNA_def_property(srna, "use_add_stroke", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_negative_sdna(prop, NULL, "flag", PE_BRUSH_DATA_ADD_SINGLE); + RNA_def_property_ui_text(prop, "Add Stroke", "Add multiple particles per brush stroke"); + prop = RNA_def_property(srna, "length_mode", PROP_ENUM, PROP_NONE); RNA_def_property_enum_sdna(prop, NULL, "invert"); RNA_def_property_enum_items(prop, length_mode); @@ -923,6 +955,41 @@ static void rna_def_particle_edit(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Curve", ""); } +static void rna_def_hair_edit(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + static EnumPropertyItem select_mode_items[] = { + {HAIR_SELECT_STRAND, "STRAND", ICON_PARTICLE_PATH, "Strand", "Strand edit mode"}, + {HAIR_SELECT_VERTEX, "VERTEX", ICON_PARTICLE_POINT, "Vertex", "Vertex select mode"}, + {HAIR_SELECT_TIP, "TIP", ICON_PARTICLE_TIP, "Tip", "Tip select mode"}, + {0, NULL, 0, NULL, NULL} + }; + + srna = RNA_def_struct(brna, "HairEdit", NULL); + RNA_def_struct_sdna(srna, "HairEditSettings"); + RNA_def_struct_path_func(srna, "rna_HairEdit_path"); + RNA_def_struct_ui_text(srna, "Hair Edit", "Settings for hair editing mode"); + + prop = RNA_def_property(srna, "brush", PROP_POINTER, PROP_NONE); + RNA_def_property_flag(prop, PROP_EDITABLE); + RNA_def_property_pointer_funcs(prop, NULL, NULL, NULL, "rna_Brush_mode_poll"); + RNA_def_property_ui_text(prop, "Brush", "Active Brush"); + RNA_def_property_update(prop, 0, "rna_HairEdit_brush_update"); + + prop = RNA_def_property(srna, "select_mode", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_bitflag_sdna(prop, NULL, "select_mode"); + RNA_def_property_enum_items(prop, select_mode_items); + RNA_def_property_ui_text(prop, "Selection Mode", "Hair selection mode"); + RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_HairEdit_update"); + + prop = RNA_def_property(srna, "shape_object", PROP_POINTER, PROP_NONE); + RNA_def_property_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text(prop, "Shape Object", "Outer shape to use for tools"); + RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_HairEdit_update"); +} + void RNA_def_sculpt_paint(BlenderRNA *brna) { /* *** Non-Animated *** */ @@ -934,6 +1001,7 @@ void RNA_def_sculpt_paint(BlenderRNA *brna) rna_def_vertex_paint(brna); rna_def_image_paint(brna); rna_def_particle_edit(brna); + rna_def_hair_edit(brna); RNA_define_animate_sdna(true); } diff --git a/source/blender/makesrna/intern/rna_sequencer.c b/source/blender/makesrna/intern/rna_sequencer.c index df996c8f31a..b0d9c22887f 100644 --- a/source/blender/makesrna/intern/rna_sequencer.c +++ b/source/blender/makesrna/intern/rna_sequencer.c @@ -1901,7 +1901,12 @@ static void rna_def_scene(BlenderRNA *brna) RNA_def_property_pointer_funcs(prop, NULL, NULL, NULL, "rna_Camera_object_poll"); RNA_def_property_ui_text(prop, "Camera Override", "Override the scenes active camera"); RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_Sequence_update"); - + + prop = RNA_def_property(srna, "use_sequence", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", SEQ_SCENE_STRIPS); + RNA_def_property_ui_text(prop, "Use Sequence", "Use scenes sequence strips directly, instead of rendering"); + RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_Sequence_update"); + prop = RNA_def_property(srna, "use_grease_pencil", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_negative_sdna(prop, NULL, "flag", SEQ_SCENE_NO_GPENCIL); RNA_def_property_ui_text(prop, "Use Grease Pencil", "Show Grease Pencil strokes in OpenGL previews"); diff --git a/source/blender/makesrna/intern/rna_smoke.c b/source/blender/makesrna/intern/rna_smoke.c index e3e6f5b40ed..9c0fe27cda6 100644 --- a/source/blender/makesrna/intern/rna_smoke.c +++ b/source/blender/makesrna/intern/rna_smoke.c @@ -507,6 +507,14 @@ static void rna_def_smoke_domain_settings(BlenderRNA *brna) RNA_def_property_enum_items(prop, smoke_cache_comp_items); RNA_def_property_ui_text(prop, "Cache Compression", "Compression method to be used"); + prop = RNA_def_property(srna, "point_cache_offset", PROP_INT, PROP_NONE); + RNA_def_property_int_sdna(prop, NULL, "point_cache_offset"); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_range(prop, -10000, 10000); + RNA_def_property_ui_range(prop, -10000, 10000, 1, -1); + RNA_def_property_ui_text(prop, "Point Cache Offset", "Offset to add to cached frames"); + RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Smoke_update"); + prop = RNA_def_property(srna, "collision_extents", PROP_ENUM, PROP_NONE); RNA_def_property_enum_sdna(prop, NULL, "border_collisions"); RNA_def_property_enum_items(prop, smoke_domain_colli_items); @@ -638,6 +646,14 @@ static void rna_def_smoke_domain_settings(BlenderRNA *brna) "Maximum amount of fluid cell can contain before it is considered empty"); RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Smoke_resetCache"); + /* display */ + prop = RNA_def_property(srna, "display_thickness", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "display_thickness"); + RNA_def_property_range(prop, 0.001f, 1000.0f); + RNA_def_property_ui_range(prop, 0.1f, 10.0f, 0.1, 3); + RNA_def_property_ui_text(prop, "Thickness", "Thickness of smoke drawing in the viewport"); + RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, NULL); + prop = RNA_def_property(srna, "use_openvdb", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "use_openvdb", 1); RNA_def_property_ui_text(prop, "Use OpenVDB", "Use OpenVDB to cache the simulation"); @@ -791,6 +807,11 @@ static void rna_def_smoke_flow_settings(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Set Size", "Set particle size in simulation cells or use nearest cell"); RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Smoke_reset"); + prop = RNA_def_property(srna, "use_particle_texture_color", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flags", MOD_SMOKE_FLOW_USE_PART_TEXCOLOR); + RNA_def_property_ui_text(prop, "Set Texture Color", "Set particle texture color in simulation cells"); + RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Smoke_reset"); + prop = RNA_def_property(srna, "subframes", PROP_INT, PROP_NONE); RNA_def_property_range(prop, 0, 50); RNA_def_property_ui_range(prop, 0, 10, 1, -1); diff --git a/source/blender/makesrna/intern/rna_space.c b/source/blender/makesrna/intern/rna_space.c index 5cc768acd35..e7f1efd1eaa 100644 --- a/source/blender/makesrna/intern/rna_space.c +++ b/source/blender/makesrna/intern/rna_space.c @@ -2486,6 +2486,11 @@ static void rna_def_space_view3d(BlenderRNA *brna) RNA_def_property_ui_text(prop, "World Background", "Display world colors in the background"); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL); + prop = RNA_def_property(srna, "show_motionpaths", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_negative_sdna(prop, NULL, "flag3", V3D_HIDE_MOTIONPATHS); + RNA_def_property_ui_text(prop, "Motion Paths", "Display animation motion paths -even in only render mode"); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL); + prop = RNA_def_property(srna, "use_occlude_geometry", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", V3D_ZBUF_SELECT); RNA_def_property_ui_text(prop, "Occlude Geometry", "Limit selection to visible (clipped with depth buffer)"); @@ -2610,6 +2615,11 @@ static void rna_def_space_view3d(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Show 3D Marker Names", "Show names for reconstructed tracks objects"); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL); + prop = RNA_def_property(srna, "use_wire_color", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_negative_sdna(prop, NULL, "flag2", V3D_WIRE_COLOR_NOCUSTOM); + RNA_def_property_ui_text(prop, "Color Wire", "Draw wireframes using object color"); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL); + prop = RNA_def_property(srna, "use_matcap", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag2", V3D_SOLID_MATCAP); RNA_def_property_ui_text(prop, "Matcap", "Active Objects draw images mapped on normals, enhancing Solid Draw Mode"); @@ -3079,6 +3089,11 @@ static void rna_def_space_sequencer(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Show Metadata", "Show metadata of first visible strip"); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, NULL); + prop = RNA_def_property(srna, "show_info", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_negative_sdna(prop, NULL, "flag", SEQ_NO_INFO); + RNA_def_property_ui_text(prop, "Show Strip Info", "Show source info on the strip"); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, NULL); + prop = RNA_def_property(srna, "show_seconds", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_negative_sdna(prop, NULL, "flag", SEQ_DRAWFRAMES); RNA_def_property_ui_text(prop, "Show Seconds", "Show timing in seconds not frames"); @@ -3136,10 +3151,22 @@ static void rna_def_space_sequencer(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Overlay Type", "Overlay draw type"); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, NULL); - prop = RNA_def_property(srna, "show_backdrop", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "draw_flag", SEQ_DRAW_BACKDROP); - RNA_def_property_ui_text(prop, "Use Backdrop", "Display result under strips"); + prop = RNA_def_property(srna, "show_overdrop", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "draw_flag", SEQ_DRAW_OVERDROP); + RNA_def_property_ui_text(prop, "Use Overdrop", "Display result under strips"); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, NULL); + + prop = RNA_def_property(srna, "overdrop_zoom", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_default(prop, 1.0f); + RNA_def_property_range(prop, 0.01f, FLT_MAX); + RNA_def_property_ui_range(prop, 0.01, 100, 1, 2); + RNA_def_property_ui_text(prop, "Overdrop Zoom", "Overdrop zoom factor"); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_NODE_VIEW, NULL); + + prop = RNA_def_property(srna, "overdrop_offset", PROP_FLOAT, PROP_NONE); + RNA_def_property_array(prop, 2); + RNA_def_property_ui_text(prop, "Overdrop Offset", "Overdrop offset"); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_NODE_VIEW, NULL); prop = RNA_def_property(srna, "show_strip_offset", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "draw_flag", SEQ_DRAW_OFFSET_EXT); @@ -3498,6 +3525,23 @@ static void rna_def_space_graph(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Auto Normalization", "Automatically recalculate curve normalization on every curve edit"); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_GRAPH, NULL); + + prop = RNA_def_property(srna, "show_backdrop", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", SIPO_DRAW_BACKDROP); + RNA_def_property_ui_text(prop, "Show Backdrop", "Draw a backdrop showing the content of the 3D View"); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_GRAPH, NULL); + + prop = RNA_def_property(srna, "backdrop_camera", PROP_POINTER, PROP_NONE); + RNA_def_property_flag(prop, PROP_EDITABLE); + RNA_def_property_pointer_funcs(prop, NULL, NULL, NULL, "rna_Camera_object_poll"); + RNA_def_property_ui_text(prop, "Backdrop Camera", "The camera that is used to project the backdrop"); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_GRAPH, NULL); + + prop = RNA_def_property(srna, "backdrop_opacity", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "backdrop_opacity"); + RNA_def_property_range(prop, 0.0f, 1.0f); + RNA_def_property_ui_text(prop, "Backdrop Opacity", "Opacity of the backdrop"); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_GRAPH, NULL); } static void rna_def_space_nla(BlenderRNA *brna) @@ -3745,6 +3789,11 @@ static void rna_def_fileselect_params(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Show Hidden", "Show hidden dot files"); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_FILE_PARAMS, NULL); + prop = RNA_def_property(srna, "collapse_seq", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", FILE_COLLAPSE_IMAGES); + RNA_def_property_ui_text(prop, "Collapse Image Sequences", "Collapse image sequences"); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_FILE_PARAMS, NULL); + prop = RNA_def_property(srna, "sort_method", PROP_ENUM, PROP_NONE); RNA_def_property_enum_sdna(prop, NULL, "sort"); RNA_def_property_enum_items(prop, file_sort_items); @@ -4174,21 +4223,15 @@ static void rna_def_space_node(BlenderRNA *brna) RNA_def_property_update(prop, NC_SPACE | ND_SPACE_NODE_VIEW, NULL); prop = RNA_def_property(srna, "backdrop_zoom", PROP_FLOAT, PROP_NONE); - RNA_def_property_float_sdna(prop, NULL, "zoom"); RNA_def_property_float_default(prop, 1.0f); RNA_def_property_range(prop, 0.01f, FLT_MAX); RNA_def_property_ui_range(prop, 0.01, 100, 1, 2); RNA_def_property_ui_text(prop, "Backdrop Zoom", "Backdrop zoom factor"); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_NODE_VIEW, NULL); - prop = RNA_def_property(srna, "backdrop_x", PROP_FLOAT, PROP_NONE); - RNA_def_property_float_sdna(prop, NULL, "xof"); - RNA_def_property_ui_text(prop, "Backdrop X", "Backdrop X offset"); - RNA_def_property_update(prop, NC_SPACE | ND_SPACE_NODE_VIEW, NULL); - - prop = RNA_def_property(srna, "backdrop_y", PROP_FLOAT, PROP_NONE); - RNA_def_property_float_sdna(prop, NULL, "yof"); - RNA_def_property_ui_text(prop, "Backdrop Y", "Backdrop Y offset"); + prop = RNA_def_property(srna, "backdrop_offset", PROP_FLOAT, PROP_NONE); + RNA_def_property_array(prop, 2); + RNA_def_property_ui_text(prop, "Backdrop Offset", "Backdrop offset"); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_NODE_VIEW, NULL); prop = RNA_def_property(srna, "backdrop_channels", PROP_ENUM, PROP_NONE); diff --git a/source/blender/makesrna/intern/rna_strands.c b/source/blender/makesrna/intern/rna_strands.c new file mode 100644 index 00000000000..f6154fd1e01 --- /dev/null +++ b/source/blender/makesrna/intern/rna_strands.c @@ -0,0 +1,312 @@ +/* + * Copyright 2015, Blender Foundation. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file blender/makesrna/intern/rna_strands.c + * \ingroup RNA + */ + +#include <stdlib.h> + +#include "BLI_utildefines.h" + +#include "RNA_define.h" +#include "RNA_access.h" + +#include "rna_internal.h" + +#include "WM_types.h" + +#ifdef RNA_RUNTIME + +#include "BLI_math.h" + +#include "BKE_strands.h" +#include "BKE_report.h" + +static int rna_Strands_has_motion_state_get(PointerRNA *ptr) +{ + Strands *strands = ptr->data; + return (bool)(strands->state != NULL); +} + +static int rna_StrandsChildCurve_render_size_get(PointerRNA *ptr) +{ + StrandsChildCurve *curve = ptr->data; + return curve->cutoff < 0.0f ? curve->numverts : min_ii(curve->numverts, (int)ceilf(curve->cutoff) + 1); +} + +static void rna_StrandsChildren_curve_uvs_begin(CollectionPropertyIterator *iter, PointerRNA *ptr) +{ + StrandsChildren *strands = ptr->data; + rna_iterator_array_begin(iter, strands->curve_uvs, sizeof(StrandsChildCurveUV), strands->totcurves * strands->numuv, false, NULL); +} + +static int rna_StrandsChildren_curve_uvs_length(PointerRNA *ptr) +{ + StrandsChildren *strands = ptr->data; + return strands->totcurves * strands->numuv; +} + +static int rna_StrandsChildren_curve_uvs_lookup_int(PointerRNA *ptr, int index, PointerRNA *r_ptr) +{ + StrandsChildren *strands = ptr->data; + if (index >= 0 && index < strands->totcurves * strands->numuv) { + RNA_pointer_create(ptr->id.data, &RNA_StrandsChildCurveUV, strands->curve_uvs + index, r_ptr); + return true; + } + else { + RNA_pointer_create(ptr->id.data, &RNA_StrandsChildCurveUV, NULL, r_ptr); + return false; + } +} + +static void rna_StrandsChildren_curve_vcols_begin(CollectionPropertyIterator *iter, PointerRNA *ptr) +{ + StrandsChildren *strands = ptr->data; + rna_iterator_array_begin(iter, strands->curve_vcols, sizeof(StrandsChildCurveVCol), strands->totcurves * strands->numvcol, false, NULL); +} + +static int rna_StrandsChildren_curve_vcols_length(PointerRNA *ptr) +{ + StrandsChildren *strands = ptr->data; + return strands->totcurves * strands->numvcol; +} + +static int rna_StrandsChildren_curve_vcols_lookup_int(PointerRNA *ptr, int index, PointerRNA *r_ptr) +{ + StrandsChildren *strands = ptr->data; + if (index >= 0 && index < strands->totcurves * strands->numvcol) { + RNA_pointer_create(ptr->id.data, &RNA_StrandsChildCurveVCol, strands->curve_vcols + index, r_ptr); + return true; + } + else { + RNA_pointer_create(ptr->id.data, &RNA_StrandsChildCurveVCol, NULL, r_ptr); + return false; + } +} + +#else + +static void rna_def_strands_curve(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, "StrandsCurve", NULL); + RNA_def_struct_sdna(srna, "StrandsCurve"); + RNA_def_struct_ui_text(srna, "Strand Curve", "Strand curve"); + + prop = RNA_def_property(srna, "size", PROP_INT, PROP_NONE); + RNA_def_property_int_sdna(prop, NULL, "numverts"); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text(prop, "Size", "Number of vertices of the curve"); + + /* same as "size", defined for consistency */ + prop = RNA_def_property(srna, "render_size", PROP_INT, PROP_NONE); + RNA_def_property_int_sdna(prop, NULL, "numverts"); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text(prop, "Render Size", "Number of vertices of the curve for rendering based on cutoff length"); +} + +static void rna_def_strands_vertex(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, "StrandsVertex", NULL); + RNA_def_struct_sdna(srna, "StrandsVertex"); + RNA_def_struct_ui_text(srna, "Strand Vertex", "Strand vertex"); + + prop = RNA_def_property(srna, "location", PROP_FLOAT, PROP_TRANSLATION); + RNA_def_property_float_sdna(prop, NULL, "co"); + RNA_def_property_array(prop, 3); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text(prop, "Location", ""); +} + +static void rna_def_strands_motion_state(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, "StrandsMotionState", NULL); + RNA_def_struct_sdna(srna, "StrandsMotionState"); + RNA_def_struct_ui_text(srna, "Strand Vertex Motion State", "Physical motion state of a vertex"); + + prop = RNA_def_property(srna, "location", PROP_FLOAT, PROP_TRANSLATION); + RNA_def_property_float_sdna(prop, NULL, "co"); + RNA_def_property_array(prop, 3); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text(prop, "Location", ""); +} + +static void rna_def_strands(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, "Strands", NULL); + RNA_def_struct_sdna(srna, "Strands"); + RNA_def_struct_ui_text(srna, "Strands", "Strand geometry to represent hair and similar linear structures"); + + prop = RNA_def_property(srna, "curves", PROP_COLLECTION, PROP_NONE); + RNA_def_property_collection_sdna(prop, NULL, "curves", "totcurves"); + RNA_def_property_struct_type(prop, "StrandsCurve"); + RNA_def_property_ui_text(prop, "Strand Curves", ""); + + prop = RNA_def_property(srna, "vertices", PROP_COLLECTION, PROP_NONE); + RNA_def_property_collection_sdna(prop, NULL, "verts", "totverts"); + RNA_def_property_struct_type(prop, "StrandsVertex"); + RNA_def_property_ui_text(prop, "Strand Vertex", ""); + + prop = RNA_def_property(srna, "has_motion_state", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_funcs(prop, "rna_Strands_has_motion_state_get", NULL); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text(prop, "Has Motion State", "Strands have physical motion data associated with vertices"); + + prop = RNA_def_property(srna, "motion_state", PROP_COLLECTION, PROP_NONE); + RNA_def_property_collection_sdna(prop, NULL, "state", "totverts"); + RNA_def_property_struct_type(prop, "StrandsMotionState"); + RNA_def_property_ui_text(prop, "Strand Motion State", ""); +} + +static void rna_def_strands_child_curve(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, "StrandsChildCurve", NULL); + RNA_def_struct_sdna(srna, "StrandsChildCurve"); + RNA_def_struct_ui_text(srna, "Strand Child Curve", "Strand child curve"); + + prop = RNA_def_property(srna, "size", PROP_INT, PROP_NONE); + RNA_def_property_int_sdna(prop, NULL, "numverts"); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text(prop, "Size", "Number of vertices of the curve"); + + prop = RNA_def_property(srna, "render_size", PROP_INT, PROP_NONE); + RNA_def_property_int_funcs(prop, "rna_StrandsChildCurve_render_size_get", NULL, NULL); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text(prop, "Render Size", "Number of vertices of the curve for rendering based on cutoff length"); + + prop = RNA_def_property(srna, "cutoff", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "cutoff"); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text(prop, "Cutoff", "Curve parameter at which the curve is cut short for rendering"); +} + +static void rna_def_strands_child_curve_uv(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, "StrandsChildCurveUV", NULL); + RNA_def_struct_sdna(srna, "StrandsChildCurveUV"); + RNA_def_struct_ui_text(srna, "Strand Child Curve UV", "UV data for child strand curves"); + + prop = RNA_def_property(srna, "uv", PROP_FLOAT, PROP_NONE); + RNA_def_property_array(prop, 2); + RNA_def_property_ui_text(prop, "UV", ""); +} + +static void rna_def_strands_child_curve_vcol(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, "StrandsChildCurveVCol", NULL); + RNA_def_struct_sdna(srna, "StrandsChildCurveVCol"); + RNA_def_struct_ui_text(srna, "Strand Child Curve Vertex Color", "Vertex color data for child strand curves"); + + prop = RNA_def_property(srna, "vcol", PROP_FLOAT, PROP_NONE); + RNA_def_property_array(prop, 3); + RNA_def_property_ui_text(prop, "Vertex Color", ""); +} + +static void rna_def_strands_child_vertex(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, "StrandsChildVertex", NULL); + RNA_def_struct_sdna(srna, "StrandsChildVertex"); + RNA_def_struct_ui_text(srna, "Strand Child Vertex", "Strand child vertex"); + + prop = RNA_def_property(srna, "location", PROP_FLOAT, PROP_TRANSLATION); + RNA_def_property_float_sdna(prop, NULL, "co"); + RNA_def_property_array(prop, 3); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text(prop, "Location", ""); +} + +static void rna_def_strands_children(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, "StrandsChildren", NULL); + RNA_def_struct_sdna(srna, "StrandsChildren"); + RNA_def_struct_ui_text(srna, "Child Strands", "Strand geometry to represent hair and similar linear structures"); + + prop = RNA_def_property(srna, "curves", PROP_COLLECTION, PROP_NONE); + RNA_def_property_collection_sdna(prop, NULL, "curves", "totcurves"); + RNA_def_property_struct_type(prop, "StrandsChildCurve"); + RNA_def_property_ui_text(prop, "Strand Child Curves", ""); + + prop = RNA_def_property(srna, "curve_uvs", PROP_COLLECTION, PROP_NONE); + RNA_def_property_collection_funcs(prop, "rna_StrandsChildren_curve_uvs_begin", "rna_iterator_array_next", "rna_iterator_array_end", "rna_iterator_array_get", + "rna_StrandsChildren_curve_uvs_length", "rna_StrandsChildren_curve_uvs_lookup_int", NULL, NULL); + RNA_def_property_struct_type(prop, "StrandsChildCurveUV"); + RNA_def_property_ui_text(prop, "Strand Child Curves UV", ""); + + prop = RNA_def_property(srna, "num_curve_uv_layers", PROP_INT, PROP_NONE); + RNA_def_property_int_sdna(prop, NULL, "numuv"); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text(prop, "UV Layers", "Number of UV layers"); + + prop = RNA_def_property(srna, "curve_vcols", PROP_COLLECTION, PROP_NONE); + RNA_def_property_collection_funcs(prop, "rna_StrandsChildren_curve_vcols_begin", "rna_iterator_array_next", "rna_iterator_array_end", "rna_iterator_array_get", + "rna_StrandsChildren_curve_vcols_length", "rna_StrandsChildren_curve_vcols_lookup_int", NULL, NULL); + RNA_def_property_struct_type(prop, "StrandsChildCurveVCol"); + RNA_def_property_ui_text(prop, "Strand Child Curves Vertex Colors", ""); + + prop = RNA_def_property(srna, "num_curve_vcol_layers", PROP_INT, PROP_NONE); + RNA_def_property_int_sdna(prop, NULL, "numvcol"); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text(prop, "Vertex Color Layers", "Number of Vertex Color layers"); + + prop = RNA_def_property(srna, "vertices", PROP_COLLECTION, PROP_NONE); + RNA_def_property_collection_sdna(prop, NULL, "verts", "totverts"); + RNA_def_property_struct_type(prop, "StrandsChildVertex"); + RNA_def_property_ui_text(prop, "Strand Child Vertex", ""); +} + +void RNA_def_strands(BlenderRNA *brna) +{ + rna_def_strands_curve(brna); + rna_def_strands_vertex(brna); + rna_def_strands_motion_state(brna); + rna_def_strands(brna); + rna_def_strands_child_curve(brna); + rna_def_strands_child_curve_uv(brna); + rna_def_strands_child_curve_vcol(brna); + rna_def_strands_child_vertex(brna); + rna_def_strands_children(brna); +} + +#endif diff --git a/source/blender/makesrna/intern/rna_texture.c b/source/blender/makesrna/intern/rna_texture.c index 726744782ed..88f164193d4 100644 --- a/source/blender/makesrna/intern/rna_texture.c +++ b/source/blender/makesrna/intern/rna_texture.c @@ -1674,6 +1674,7 @@ static void rna_def_texture_pointdensity(BlenderRNA *brna) {TEX_PD_COLOR_PARTSPEED, "PARTICLE_SPEED", 0, "Particle Speed", "Particle speed (absolute magnitude of velocity) mapped as 0.0-1.0 intensity"}, {TEX_PD_COLOR_PARTVEL, "PARTICLE_VELOCITY", 0, "Particle Velocity", "XYZ velocity mapped to RGB colors"}, + {TEX_PD_COLOR_PARTTEX, "PARTICLE_TEXTURE", 0, "Particle Texture", "Texture color of particles"}, {0, NULL, 0, NULL, NULL} }; diff --git a/source/blender/makesrna/intern/rna_ui_api.c b/source/blender/makesrna/intern/rna_ui_api.c index cf387644d66..843a560bcf7 100644 --- a/source/blender/makesrna/intern/rna_ui_api.c +++ b/source/blender/makesrna/intern/rna_ui_api.c @@ -910,6 +910,19 @@ void RNA_api_ui_layout(StructRNA *srna) RNA_def_function_ui_description(func, "Node Socket Icon"); RNA_def_function_flag(func, FUNC_USE_CONTEXT); RNA_def_float_array(func, "color", 4, node_socket_color_default, 0.0f, 1.0f, "Color", "", 0.0f, 1.0f); + + /* cache library item */ + func = RNA_def_function(srna, "template_cache_library_item", "uiTemplateCacheLibraryItem"); + RNA_def_function_ui_description(func, "Cache Library Item"); + RNA_def_function_flag(func, FUNC_USE_CONTEXT); + RNA_def_pointer(func, "cachelib", "CacheLibrary", "Cache Library", "Cache library containing the item"); + RNA_def_pointer(func, "object", "Object", "Object", "Object to cache"); + parm = RNA_def_enum(func, "datatype", cache_library_data_type_items, 0, "Data Type", "Type of cached data"); + RNA_def_property_flag(parm, PROP_REQUIRED); + RNA_def_int(func, "index", -1, -1, INT_MAX, "Index", "Index of cached data", -1, INT_MAX); + RNA_def_boolean(func, "enabled", true, "Enabled", "Enable the item"); + parm = RNA_def_pointer(func, "layout", "UILayout", "", "Sub-layout to put items in"); + RNA_def_function_return(func, parm); } #endif diff --git a/source/blender/makesrna/intern/rna_userdef.c b/source/blender/makesrna/intern/rna_userdef.c index 82afcfdbf29..535ade75e13 100644 --- a/source/blender/makesrna/intern/rna_userdef.c +++ b/source/blender/makesrna/intern/rna_userdef.c @@ -3470,6 +3470,16 @@ static void rna_def_userdef_view(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Manipulator", "Use 3D transform manipulator"); RNA_def_property_update(prop, 0, "rna_userdef_show_manipulator_update"); + prop = RNA_def_property(srna, "shaded_widgets", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "tw_flag", V3D_SHADED_WIDGETS); + RNA_def_property_ui_text(prop, "Shaded Widgets", "3D shading for widgets"); + RNA_def_property_update(prop, 0, "rna_userdef_update"); + + prop = RNA_def_property(srna, "widgets_3d", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "tw_flag", V3D_3D_WIDGETS); + RNA_def_property_ui_text(prop, "3D widgets", "Widgets size stays constant in world space"); + RNA_def_property_update(prop, 0, "rna_userdef_update"); + prop = RNA_def_property(srna, "manipulator_size", PROP_INT, PROP_PIXEL); RNA_def_property_int_sdna(prop, NULL, "tw_size"); RNA_def_property_range(prop, 10, 200); diff --git a/source/blender/makesrna/intern/rna_wm.c b/source/blender/makesrna/intern/rna_wm.c index 4446f5d9a2b..c23b56582b6 100644 --- a/source/blender/makesrna/intern/rna_wm.c +++ b/source/blender/makesrna/intern/rna_wm.c @@ -30,6 +30,7 @@ #include "DNA_space_types.h" #include "DNA_userdef_types.h" #include "DNA_windowmanager_types.h" +#include "DNA_widget_types.h" #include "BLI_utildefines.h" @@ -522,6 +523,26 @@ static PointerRNA rna_Operator_properties_get(PointerRNA *ptr) return rna_pointer_inherit_refine(ptr, op->type->srna, op->properties); } +static void rna_WidgetGroup_name_get(PointerRNA *ptr, char *value) +{ + wmWidgetGroup *wgroup = ptr->data; + strcpy(value, "Dummy_XXX" /*wgroup->type->name*/); + (void)wgroup; +} + +static int rna_WidgetGroup_name_length(PointerRNA *ptr) +{ + wmWidgetGroup *wgroup = ptr->data; + return strlen("Dummy_XXX" /*wgroup->type->name*/); + (void)wgroup; +} + +static int rna_WidgetGroup_has_reports_get(PointerRNA *ptr) +{ + wmWidgetGroup *wgroup = ptr->data; + return (wgroup->reports && wgroup->reports->list.first); +} + static PointerRNA rna_OperatorMacro_properties_get(PointerRNA *ptr) { wmOperatorTypeMacro *otmacro = (wmOperatorTypeMacro *)ptr->data; @@ -1387,6 +1408,180 @@ static void rna_Operator_bl_description_set(PointerRNA *ptr, const char *value) assert(!"setting the bl_description on a non-builtin operator"); } +#ifdef WITH_PYTHON +static void rna_WidgetGroup_unregister(struct Main *bmain, StructRNA *type) +{ + //const char *idname; + wmWidgetGroupType *wgrouptype = RNA_struct_blender_type_get(type); + //wmWindowManager *wm; + //wmWidgetMapType *wmap = NULL; + + if (!wgrouptype) + return; + + WM_main_add_notifier(NC_SCREEN | NA_EDITED, NULL); + + //RNA_struct_free_extension(type, &wgrouptype->ext); + + WM_widgetgrouptype_unregister(NULL, bmain, wgrouptype); + //WM_operatortype_remove_ptr(ot); + + /* not to be confused with the RNA_struct_free that WM_operatortype_remove calls, they are 2 different srna's */ + RNA_struct_free(&BLENDER_RNA, type); +} + +static int widgetgroup_poll(const bContext *C, wmWidgetGroupType *wgrouptype) +{ + + extern FunctionRNA rna_WidgetGroup_poll_func; + + PointerRNA ptr; + ParameterList list; + FunctionRNA *func; + void *ret; + int visible; + + RNA_pointer_create(NULL, wgrouptype->ext.srna, NULL, &ptr); /* dummy */ + func = &rna_WidgetGroup_poll_func; /* RNA_struct_find_function(&ptr, "poll"); */ + + RNA_parameter_list_create(&list, &ptr, func); + RNA_parameter_set_lookup(&list, "context", &C); + wgrouptype->ext.call((bContext *)C, &ptr, func, &list); + + RNA_parameter_get_lookup(&list, "visible", &ret); + visible = *(int *)ret; + + RNA_parameter_list_free(&list); + + return visible; +} + +static void widgetgroup_draw(const bContext *C, wmWidgetGroup *wgroup) +{ + extern FunctionRNA rna_WidgetGroup_draw_func; + + PointerRNA wgroup_ptr; + ParameterList list; + FunctionRNA *func; + + RNA_pointer_create(NULL, wgroup->type->ext.srna, wgroup, &wgroup_ptr); + func = &rna_WidgetGroup_draw_func; /* RNA_struct_find_function(&wgroupr, "draw"); */ + + RNA_parameter_list_create(&list, &wgroup_ptr, func); + RNA_parameter_set_lookup(&list, "context", &C); + wgroup->type->ext.call((bContext *)C, &wgroup_ptr, func, &list); + + RNA_parameter_list_free(&list); +} +#if 0 + +/* same as exec(), but call cancel */ +static void operator_cancel(bContext *C, wmWidgetGroup *op) +{ + extern FunctionRNA rna_WidgetGroup_cancel_func; + + PointerRNA opr; + ParameterList list; + FunctionRNA *func; + + RNA_pointer_create(NULL, op->type->ext.srna, op, &opr); + func = &rna_WidgetGroup_cancel_func; /* RNA_struct_find_function(&opr, "cancel"); */ + + RNA_parameter_list_create(&list, &opr, func); + RNA_parameter_set_lookup(&list, "context", &C); + op->type->ext.call(C, &opr, func, &list); + + RNA_parameter_list_free(&list); +} +#endif + +void widgetgroup_wrapper(wmWidgetGroupType *ot, void *userdata); + +static char _widgetgroup_idname[OP_MAX_TYPENAME]; +//static char _widgetgroup_name[OP_MAX_TYPENAME]; +//static char _widgetgroup_descr[RNA_DYN_DESCR_MAX]; +//static char _widgetgroup_ctxt[RNA_DYN_DESCR_MAX]; +static StructRNA *rna_WidgetGroup_register(Main *bmain, ReportList *reports, void *data, const char *identifier, + StructValidateFunc validate, StructCallbackFunc call, StructFreeFunc free) +{ + + wmWidgetGroupType *wgrouptype, dummywgt = {NULL}; + wmWidgetGroup dummywg = {NULL}; + PointerRNA wgptr; + int have_function[2]; + + /* setup dummy widgetgroup & widgetgroup type to store static properties in */ + dummywg.type = &dummywgt; + RNA_pointer_create(NULL, &RNA_WidgetGroup, &dummywg, &wgptr); + + /* clear in case they are left unset */ + _widgetgroup_idname[0] = '\0'; + + /* validate the python class */ + if (validate(&wgptr, data, have_function) != 0) + return NULL; + + if (strlen(identifier) >= sizeof(dummywgt.idname)) { + BKE_reportf(reports, RPT_ERROR, "Registering widgetgroup class: '%s' is too long, maximum length is %d", + identifier, (int)sizeof(dummywgt.idname)); + return NULL; + } + + /* check if the area supports widgets */ + if (!WM_widgetmaptype_find(dummywgt.mapidname ,dummywgt.spaceid, dummywgt.regionid, dummywgt.is_3d, false)) { + BKE_reportf(reports, RPT_ERROR, "Area type does not support widgets"); + return NULL; + } + +#if 0 + /* check if we have registered this widgetgroup type before, and remove it */ + { + //wmWidgetGroupType *ot = WM_widgetgrouptype_find(dummywgt.idname, true); + if (ot && ot->ext.srna) + rna_WidgetGroup_unregister(bmain, ot->ext.srna); + } + +#endif + /* XXX, this doubles up with the widgetgroup name [#29666] + * for now just remove from dir(bpy.types) */ + + /* create a new widgetgroup type */ + dummywgt.ext.srna = RNA_def_struct_ptr(&BLENDER_RNA, dummywgt.idname, &RNA_WidgetGroup); + RNA_def_struct_flag(dummywgt.ext.srna, STRUCT_NO_IDPROPERTIES); /* widgetgroup properties are registered separately */ + dummywgt.ext.data = data; + dummywgt.ext.call = call; + dummywgt.ext.free = free; + + dummywgt.poll = (have_function[0]) ? widgetgroup_poll : NULL; + dummywgt.draw = (have_function[1]) ? widgetgroup_draw : NULL; + + wgrouptype = WM_widgetgrouptype_new(dummywgt.poll, dummywgt.draw, bmain, dummywgt.mapidname, dummywgt.spaceid, dummywgt.regionid, dummywgt.is_3d); + memcpy(wgrouptype, &dummywgt, sizeof(dummywgt)); + + /* update while blender is running */ + WM_main_add_notifier(NC_SCREEN | NA_EDITED, NULL); + + dummywgt.ext.srna = RNA_def_struct_ptr(&BLENDER_RNA, dummywgt.idname, &RNA_WidgetGroup); + + return dummywgt.ext.srna; +} + +//RNA_struct_blender_type_set(pt->ext.srna, pt); + +static void **rna_WidgetGroup_instance(PointerRNA *ptr) +{ + wmWidgetGroup *wgroup = ptr->data; + return &wgroup->py_instance; +} + +static StructRNA *rna_WidgetGroup_refine(PointerRNA *wgroup_ptr) +{ + wmWidgetGroup *wgroup = wgroup_ptr->data; + return (wgroup->type && wgroup->type->ext.srna) ? wgroup->type->ext.srna : &RNA_WidgetGroup; +} + +#endif + static void rna_KeyMapItem_update(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *ptr) { wmKeyMapItem *kmi = ptr->data; @@ -1635,7 +1830,62 @@ static void rna_def_operator_filelist_element(BlenderRNA *brna) RNA_def_property_flag(prop, PROP_IDPROPERTY); RNA_def_property_ui_text(prop, "Name", "Name of a file or directory within a file list"); } - + +static void rna_def_widgetgroup(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, "WidgetGroup", NULL); + RNA_def_struct_ui_text(srna, "WidgetGroup", "Storage of an operator being executed, or registered after execution"); + RNA_def_struct_sdna(srna, "wmWidgetGroup"); + RNA_def_struct_refine_func(srna, "rna_WidgetGroup_refine"); +#ifdef WITH_PYTHON + RNA_def_struct_register_funcs(srna, "rna_WidgetGroup_register", "rna_WidgetGroup_unregister", "rna_WidgetGroup_instance"); +#endif + RNA_def_struct_translation_context(srna, BLF_I18NCONTEXT_OPERATOR_DEFAULT); + + prop = RNA_def_property(srna, "name", PROP_STRING, PROP_NONE); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_string_funcs(prop, "rna_WidgetGroup_name_get", "rna_WidgetGroup_name_length", NULL); + RNA_def_property_ui_text(prop, "Name", ""); + + prop = RNA_def_property(srna, "has_reports", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); /* this is 'virtual' property */ + RNA_def_property_boolean_funcs(prop, "rna_WidgetGroup_has_reports_get", NULL); + RNA_def_property_ui_text(prop, "Has Reports", + "WidgetGroup has a set of reports (warnings and errors) from last execution"); + + /* Registration */ + prop = RNA_def_property(srna, "bl_idname", PROP_STRING, PROP_NONE); + RNA_def_property_string_sdna(prop, NULL, "type->idname"); + /* RNA_def_property_clear_flag(prop, PROP_EDITABLE); */ + RNA_def_property_flag(prop, PROP_REGISTER); + RNA_def_struct_name_property(srna, prop); + + prop = RNA_def_property(srna, "bl_space_type", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "type->spaceid"); + RNA_def_property_enum_items(prop, space_type_items); + RNA_def_property_flag(prop, PROP_REGISTER); + RNA_def_property_ui_text(prop, "Space type", "The space where the panel is going to be used in"); + + prop = RNA_def_property(srna, "bl_region_type", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "type->regionid"); + RNA_def_property_enum_items(prop, region_type_items); + RNA_def_property_flag(prop, PROP_REGISTER); + RNA_def_property_ui_text(prop, "Region Type", "The region where the panel is going to be used in"); + +#if 0 + prop = RNA_def_property(srna, "bl_options", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "type->flag"); + RNA_def_property_enum_items(prop, operator_flag_items); + RNA_def_property_flag(prop, PROP_REGISTER_OPTIONAL | PROP_ENUM_FLAG); + RNA_def_property_ui_text(prop, "Options", "Options for this operator type"); +#endif + + RNA_api_widgetgroup(srna); +} + static void rna_def_event(BlenderRNA *brna) { StructRNA *srna; @@ -2217,6 +2467,7 @@ void RNA_def_wm(BlenderRNA *brna) rna_def_operator_filelist_element(brna); rna_def_macro_operator(brna); rna_def_operator_type_macro(brna); + rna_def_widgetgroup(brna); rna_def_event(brna); rna_def_timer(brna); rna_def_popupmenu(brna); diff --git a/source/blender/makesrna/intern/rna_wm_api.c b/source/blender/makesrna/intern/rna_wm_api.c index e819d331124..2e0bd8f491a 100644 --- a/source/blender/makesrna/intern/rna_wm_api.c +++ b/source/blender/makesrna/intern/rna_wm_api.c @@ -616,6 +616,39 @@ void RNA_api_macro(StructRNA *srna) RNA_def_property_flag(parm, PROP_REQUIRED | PROP_NEVER_NULL); } +void RNA_api_widgetgroup(StructRNA *srna) +{ + FunctionRNA *func; + PropertyRNA *parm; + +#if 0 + /* utility, not for registering */ + func = RNA_def_function(srna, "report", "rna_Operator_report"); + parm = RNA_def_enum_flag(func, "type", wm_report_items, 0, "Type", ""); + RNA_def_property_flag(parm, PROP_REQUIRED); + parm = RNA_def_string(func, "message", NULL, 0, "Report Message", ""); + RNA_def_property_flag(parm, PROP_REQUIRED); +#endif + + + /* Registration */ + + /* poll */ + func = RNA_def_function(srna, "poll", NULL); + RNA_def_function_ui_description(func, "Test if the operator can be called or not"); + RNA_def_function_flag(func, FUNC_NO_SELF | FUNC_REGISTER_OPTIONAL); + RNA_def_function_return(func, RNA_def_boolean(func, "visible", 1, "", "")); + parm = RNA_def_pointer(func, "context", "Context", "", ""); + RNA_def_property_flag(parm, PROP_REQUIRED | PROP_NEVER_NULL); + + /* draw */ + func = RNA_def_function(srna, "draw", NULL); + RNA_def_function_ui_description(func, "Draw function for the operator"); + RNA_def_function_flag(func, FUNC_REGISTER); + parm = RNA_def_pointer(func, "context", "Context", "", ""); + RNA_def_property_flag(parm, PROP_REQUIRED | PROP_NEVER_NULL); +} + void RNA_api_keyconfig(StructRNA *UNUSED(srna)) { /* FunctionRNA *func; */ diff --git a/source/blender/modifiers/CMakeLists.txt b/source/blender/modifiers/CMakeLists.txt index fde0c773ef0..5306f6d34ba 100644 --- a/source/blender/modifiers/CMakeLists.txt +++ b/source/blender/modifiers/CMakeLists.txt @@ -34,6 +34,7 @@ set(INC ../makesdna ../makesrna ../bmesh + ../pointcache ../render/extern/include ../../../intern/elbeem/extern ../../../intern/guardedalloc diff --git a/source/blender/modifiers/SConscript b/source/blender/modifiers/SConscript index a7d17609ab7..761342b79d6 100644 --- a/source/blender/modifiers/SConscript +++ b/source/blender/modifiers/SConscript @@ -45,6 +45,7 @@ incs = [ '../makesrna', '../blenkernel', '../gpu', + '../pointcache', env['BF_ZLIB_INC'], ] diff --git a/source/blender/modifiers/intern/MOD_particleinstance.c b/source/blender/modifiers/intern/MOD_particleinstance.c index cb6234d50b7..218a74ec9fb 100644 --- a/source/blender/modifiers/intern/MOD_particleinstance.c +++ b/source/blender/modifiers/intern/MOD_particleinstance.c @@ -40,6 +40,7 @@ #include "BLI_math.h" #include "BLI_listbase.h" #include "BLI_rand.h" +#include "BLI_string.h" #include "BLI_utildefines.h" #include "BKE_cdderivedmesh.h" @@ -62,7 +63,12 @@ static void initData(ModifierData *md) pimd->psys = 1; pimd->position = 1.0f; pimd->axis = 2; - + pimd->space = eParticleInstanceSpace_Local; + pimd->particle_amount = 1.0f; + pimd->particle_offset = 0.0f; + + BLI_strncpy(pimd->index_layer_name, "particle_index", sizeof(pimd->index_layer_name)); + BLI_strncpy(pimd->value_layer_name, "particle_value", sizeof(pimd->value_layer_name)); } static void copyData(ModifierData *md, ModifierData *target) { @@ -147,9 +153,10 @@ static void foreachObjectLink(ModifierData *md, Object *ob, walk(userData, ob, &pimd->ob); } -static int particle_skip(ParticleInstanceModifierData *pimd, ParticleSystem *psys, int p) +static bool particle_skip(ParticleInstanceModifierData *pimd, ParticleSystem *psys, int p) { ParticleData *pa; + int totpart, randp, minp, maxp; if (pimd->flag & eParticleInstanceFlag_Parents) { if (p >= psys->totpart) { @@ -174,12 +181,29 @@ static int particle_skip(ParticleInstanceModifierData *pimd, ParticleSystem *psy } if (pa) { - if (pa->alive == PARS_UNBORN && (pimd->flag & eParticleInstanceFlag_Unborn) == 0) return 1; - if (pa->alive == PARS_ALIVE && (pimd->flag & eParticleInstanceFlag_Alive) == 0) return 1; - if (pa->alive == PARS_DEAD && (pimd->flag & eParticleInstanceFlag_Dead) == 0) return 1; + if (pa->alive == PARS_UNBORN && (pimd->flag & eParticleInstanceFlag_Unborn) == 0) return true; + if (pa->alive == PARS_ALIVE && (pimd->flag & eParticleInstanceFlag_Alive) == 0) return true; + if (pa->alive == PARS_DEAD && (pimd->flag & eParticleInstanceFlag_Dead) == 0) return true; + } + + totpart = psys->totpart + psys->totchild; + + /* TODO make randomization optional? */ + randp = (int)(psys_frand(psys, 3578 + p) * totpart) % totpart; + + minp = (int)(totpart * pimd->particle_offset) % (totpart+1); + maxp = (int)(totpart * (pimd->particle_offset + pimd->particle_amount)) % (totpart+1); + + if (maxp > minp) { + return randp < minp || randp >= maxp; + } + else if (maxp < minp) { + return randp < minp && randp >= maxp; } + else + return true; - return 0; + return false; } static DerivedMesh *applyModifier(ModifierData *md, Object *ob, @@ -200,6 +224,9 @@ static DerivedMesh *applyModifier(ModifierData *md, Object *ob, short track = ob->trackflag % 3, trackneg, axis = pimd->axis; float max_co = 0.0, min_co = 0.0, temp_co[3]; float *size = NULL; + float spacemat[4][4]; + int *cd_index = NULL; + float *cd_value = NULL; trackneg = ((ob->trackflag > 2) ? 1 : 0); @@ -251,6 +278,21 @@ static DerivedMesh *applyModifier(ModifierData *md, Object *ob, } } + switch (pimd->space) { + case eParticleInstanceSpace_World: + /* particle states are in world space already */ + unit_m4(spacemat); + break; + case eParticleInstanceSpace_Local: + /* get particle states in the particle object's local space */ + invert_m4_m4(spacemat, pimd->ob->obmat); + break; + default: + /* should not happen */ + BLI_assert(false); + break; + } + totvert = dm->getNumVerts(dm); totpoly = dm->getNumPolys(dm); totloop = dm->getNumLoops(dm); @@ -290,9 +332,18 @@ static DerivedMesh *applyModifier(ModifierData *md, Object *ob, mloop = result->getLoopArray(result); orig_mloop = dm->getLoopArray(dm); + /* create customdata layer for particle index storage */ + if (pimd->index_layer_name[0] != '\0') + cd_index = CustomData_add_layer_named(&result->vertData, CD_PROP_INT, CD_CALLOC, + NULL, maxvert, pimd->index_layer_name); + if (pimd->value_layer_name[0] != '\0') + cd_value = CustomData_add_layer_named(&result->vertData, CD_PROP_FLT, CD_CALLOC, + NULL, maxvert, pimd->value_layer_name); + for (p = 0, p_skip = 0; p < totpart; p++) { float prev_dir[3]; float frame[4]; /* frame orientation quaternion */ + float p_random = psys_frand(psys, 77091 + 283*p); /* skip particle? */ if (particle_skip(pimd, psys, p)) @@ -302,12 +353,18 @@ static DerivedMesh *applyModifier(ModifierData *md, Object *ob, for (k = 0; k < totvert; k++) { ParticleKey state; MVert *inMV; - MVert *mv = mvert + p_skip * totvert + k; + int vindex = p_skip * totvert + k; + MVert *mv = mvert + vindex; inMV = orig_mvert + k; DM_copy_vert_data(dm, result, k, p_skip * totvert + k, 1); *mv = *inMV; + if (cd_index) + cd_index[vindex] = p; + if (cd_value) + cd_value[vindex] = p_random; + /*change orientation based on object trackflag*/ copy_v3_v3(temp_co, mv->co); mv->co[axis] = temp_co[track]; @@ -355,6 +412,15 @@ static DerivedMesh *applyModifier(ModifierData *md, Object *ob, /* to quaternion */ mat3_to_quat(frame, mat); + if (pimd->rotation > 0.0f || pimd->random_rotation > 0.0f) { + float angle = 2.0f*M_PI * (pimd->rotation + pimd->random_rotation * (psys_frand(psys, 19957323 + p) - 0.5f)); + float eul[3] = { 0.0f, 0.0f, angle }; + float rot[4]; + + eul_to_quat(rot, eul); + mul_qt_qtqt(frame, frame, rot); + } + /* note: direction is same as normal vector currently, * but best to keep this separate so the frame can be * rotated later if necessary @@ -398,6 +464,8 @@ static DerivedMesh *applyModifier(ModifierData *md, Object *ob, if (pimd->flag & eParticleInstanceFlag_UseSize) mul_v3_fl(mv->co, size[p]); add_v3_v3(mv->co, state.co); + + mul_m4_v3(spacemat, mv->co); } /* create polys and loops */ diff --git a/source/blender/modifiers/intern/MOD_surface.c b/source/blender/modifiers/intern/MOD_surface.c index 3d998f2d95a..31ff8dee562 100644 --- a/source/blender/modifiers/intern/MOD_surface.c +++ b/source/blender/modifiers/intern/MOD_surface.c @@ -111,6 +111,7 @@ static void deformVerts(ModifierData *md, Object *ob, float *vec; MVert *x, *v; + DM_ensure_tessface(surmd->dm); CDDM_apply_vert_coords(surmd->dm, vertexCos); CDDM_calc_normals(surmd->dm); diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt index fd247e356fe..fc2e5f791c3 100644 --- a/source/blender/nodes/CMakeLists.txt +++ b/source/blender/nodes/CMakeLists.txt @@ -202,6 +202,7 @@ set(SRC shader/nodes/node_shader_tex_magic.c shader/nodes/node_shader_tex_musgrave.c shader/nodes/node_shader_tex_noise.c + shader/nodes/node_shader_tex_pointdensity.c shader/nodes/node_shader_tex_sky.c shader/nodes/node_shader_tex_voronoi.c shader/nodes/node_shader_tex_wave.c diff --git a/source/blender/nodes/NOD_shader.h b/source/blender/nodes/NOD_shader.h index 58c30e4ad98..183e4c92416 100644 --- a/source/blender/nodes/NOD_shader.h +++ b/source/blender/nodes/NOD_shader.h @@ -75,6 +75,7 @@ void register_node_type_sh_sepxyz(void); void register_node_type_sh_combxyz(void); void register_node_type_sh_hue_sat(void); void register_node_type_sh_tex_brick(void); +void register_node_type_sh_tex_pointdensity(void); void register_node_type_sh_attribute(void); void register_node_type_sh_geometry(void); diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h index dc27dddc9ca..47808fbec77 100644 --- a/source/blender/nodes/NOD_static_types.h +++ b/source/blender/nodes/NOD_static_types.h @@ -116,6 +116,7 @@ DefNode( ShaderNode, SH_NODE_TEX_MUSGRAVE, def_sh_tex_musgrave, "TE DefNode( ShaderNode, SH_NODE_TEX_VORONOI, def_sh_tex_voronoi, "TEX_VORONOI", TexVoronoi, "Voronoi Texture", "" ) DefNode( ShaderNode, SH_NODE_TEX_CHECKER, def_sh_tex_checker, "TEX_CHECKER", TexChecker, "Checker Texture", "" ) DefNode( ShaderNode, SH_NODE_TEX_BRICK, def_sh_tex_brick, "TEX_BRICK", TexBrick, "Brick Texture", "" ) +DefNode( ShaderNode, SH_NODE_TEX_POINTDENSITY, def_sh_tex_pointdensity,"TEX_POINTDENSITY", TexPointDensity, "Point Density", "" ) DefNode( ShaderNode, SH_NODE_TEX_COORD, def_sh_tex_coord, "TEX_COORD", TexCoord, "Texture Coordinate","" ) DefNode( ShaderNode, SH_NODE_VECT_TRANSFORM, def_sh_vect_transform, "VECT_TRANSFORM", VectorTransform, "Vector Transform", "" ) DefNode( ShaderNode, SH_NODE_SEPHSV, 0, "SEPHSV", SeparateHSV, "Separate HSV", "" ) diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_pointdensity.c b/source/blender/nodes/shader/nodes/node_shader_tex_pointdensity.c new file mode 100644 index 00000000000..3cdbfacb95c --- /dev/null +++ b/source/blender/nodes/shader/nodes/node_shader_tex_pointdensity.c @@ -0,0 +1,75 @@ +/* + * ***** 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2015 Blender Foundation. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): Sergey Sharybin. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#include "../node_shader_util.h" + +/* **************** OUTPUT ******************** */ + +static bNodeSocketTemplate sh_node_tex_pointdensity_in[] = { + {SOCK_VECTOR, 1, N_("Vector"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_NONE, SOCK_HIDE_VALUE}, + {-1, 0, ""} +}; + +static bNodeSocketTemplate sh_node_tex_pointdensity_out[] = { + {SOCK_FLOAT, 0, N_("Density"), 0.0f, 0.0f, 0.0f, 0.0f, -10000.0f, 10000.0f}, + {SOCK_RGBA, 0, N_("Color"), 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f}, + {-1, 0, ""} +}; + +static void node_shader_init_tex_pointdensity(bNodeTree *UNUSED(ntree), + bNode *node) +{ + NodeShaderTexPointDensity *point_density = + MEM_callocN(sizeof(NodeShaderTexPointDensity), "new pd node"); + point_density->resolution = 100; + point_density->radius = 0.3f; + point_density->space = SHD_POINTDENSITY_SPACE_OBJECT; + node->storage = point_density; +} + +/* node type definition */ +void register_node_type_sh_tex_pointdensity(void) +{ + static bNodeType ntype; + + sh_node_type_base(&ntype, + SH_NODE_TEX_POINTDENSITY, + "Point Density", + NODE_CLASS_TEXTURE, + 0); + node_type_compatibility(&ntype, NODE_NEW_SHADING); + node_type_socket_templates(&ntype, + sh_node_tex_pointdensity_in, + sh_node_tex_pointdensity_out); + node_type_init(&ntype, node_shader_init_tex_pointdensity); + node_type_storage(&ntype, + "NodeShaderTexPointDensity", + node_free_standard_storage, + node_copy_standard_storage); + + nodeRegisterType(&ntype); +} diff --git a/source/blender/physics/BPH_mass_spring.h b/source/blender/physics/BPH_mass_spring.h index de5fdd2b81d..db355c18f2e 100644 --- a/source/blender/physics/BPH_mass_spring.h +++ b/source/blender/physics/BPH_mass_spring.h @@ -40,7 +40,10 @@ struct Implicit_Data; struct Object; struct ClothModifierData; struct ListBase; +struct Strands; +struct HairSimParams; struct VoxelData; +struct CacheEffector; typedef enum eMassSpringSolverStatus { BPH_SOLVER_SUCCESS = (1 << 0), @@ -51,14 +54,19 @@ typedef enum eMassSpringSolverStatus { struct Implicit_Data *BPH_mass_spring_solver_create(int numverts, int numsprings); void BPH_mass_spring_solver_free(struct Implicit_Data *id); +int BPH_mass_spring_solver_numvert(struct Implicit_Data *id); int BPH_cloth_solver_init(struct Object *ob, struct ClothModifierData *clmd); void BPH_cloth_solver_free(struct ClothModifierData *clmd); int BPH_cloth_solve(struct Object *ob, float frame, struct ClothModifierData *clmd, struct ListBase *effectors); -void BKE_cloth_solver_set_positions(struct ClothModifierData *clmd); +void BPH_cloth_solver_set_positions(struct ClothModifierData *clmd); bool BPH_cloth_solver_get_texture_data(struct Object *ob, struct ClothModifierData *clmd, struct VoxelData *vd); +struct Implicit_Data *BPH_strands_solver_create(struct Strands *strands, struct HairSimParams *params); +bool BPH_strands_solve(struct Strands *strands, float mat[4][4], struct Implicit_Data *id, struct HairSimParams *params, float frame, float frame_prev, struct Scene *scene, + struct ListBase *effectors, struct CacheEffector *cache_effectors, int tot_cache_effectors); + #ifdef __cplusplus } #endif diff --git a/source/blender/physics/BPH_strands.h b/source/blender/physics/BPH_strands.h new file mode 100644 index 00000000000..068c47f1c4c --- /dev/null +++ b/source/blender/physics/BPH_strands.h @@ -0,0 +1,44 @@ +/* + * ***** 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) Blender Foundation + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): Lukas Toenne + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#ifndef __BPH_STRANDS_H__ +#define __BPH_STRANDS_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +struct Object; +struct BMEditStrands; + +void BPH_strands_solve_constraints(struct Object *ob, struct BMEditStrands *es, float (*orig)[3]); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/source/blender/physics/CMakeLists.txt b/source/blender/physics/CMakeLists.txt index 08ff1faca49..12080567585 100644 --- a/source/blender/physics/CMakeLists.txt +++ b/source/blender/physics/CMakeLists.txt @@ -28,6 +28,7 @@ set(INC intern ../blenlib ../blenkernel + ../bmesh ../imbuf ../makesdna ../../../intern/guardedalloc @@ -46,8 +47,10 @@ set(SRC intern/implicit_blender.c intern/implicit_eigen.cpp intern/eigen_utils.h + intern/strands.cpp BPH_mass_spring.h + BPH_strands.h ) blender_add_lib(bf_physics "${SRC}" "${INC}" "${INC_SYS}") diff --git a/source/blender/physics/SConscript b/source/blender/physics/SConscript index c8165886cab..4156cecd1a3 100644 --- a/source/blender/physics/SConscript +++ b/source/blender/physics/SConscript @@ -35,6 +35,7 @@ incs = [ 'intern', '../blenlib', '../blenkernel', + '../bmesh', '../imbuf', '../makesdna', '../../../intern/guardedalloc', diff --git a/source/blender/physics/intern/BPH_mass_spring.cpp b/source/blender/physics/intern/BPH_mass_spring.cpp index 9f6050527ef..b0c0f4fa4df 100644 --- a/source/blender/physics/intern/BPH_mass_spring.cpp +++ b/source/blender/physics/intern/BPH_mass_spring.cpp @@ -1,1117 +1,1724 @@ -/*
- * ***** 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- * The Original Code is Copyright (C) Blender Foundation
- * All rights reserved.
- *
- * The Original Code is: all of this file.
- *
- * Contributor(s): Lukas Toenne
- *
- * ***** END GPL LICENSE BLOCK *****
- */
-
-/** \file blender/physics/intern/BPH_mass_spring.cpp
- * \ingroup bph
- */
-
-extern "C" {
-#include "MEM_guardedalloc.h"
-
-#include "DNA_cloth_types.h"
-#include "DNA_scene_types.h"
-#include "DNA_object_force.h"
-#include "DNA_object_types.h"
-#include "DNA_meshdata_types.h"
-#include "DNA_modifier_types.h"
-
-#include "BLI_math.h"
-#include "BLI_linklist.h"
-#include "BLI_utildefines.h"
-
-#include "BKE_cloth.h"
-#include "BKE_collision.h"
-#include "BKE_effect.h"
-}
-
-#include "BPH_mass_spring.h"
-#include "implicit.h"
-
-static float I3[3][3] = {{1.0, 0.0, 0.0}, {0.0, 1.0, 0.0}, {0.0, 0.0, 1.0}};
-
-/* Number of off-diagonal non-zero matrix blocks.
- * Basically there is one of these for each vertex-vertex interaction.
- */
-static int cloth_count_nondiag_blocks(Cloth *cloth)
-{
- LinkNode *link;
- int nondiag = 0;
-
- for (link = cloth->springs; link; link = link->next) {
- ClothSpring *spring = (ClothSpring *)link->link;
- switch (spring->type) {
- case CLOTH_SPRING_TYPE_BENDING_ANG:
- /* angular bending combines 3 vertices */
- nondiag += 3;
- break;
-
- default:
- /* all other springs depend on 2 vertices only */
- nondiag += 1;
- break;
- }
- }
-
- return nondiag;
-}
-
-int BPH_cloth_solver_init(Object *UNUSED(ob), ClothModifierData *clmd)
-{
- Cloth *cloth = clmd->clothObject;
- ClothVertex *verts = cloth->verts;
- const float ZERO[3] = {0.0f, 0.0f, 0.0f};
- Implicit_Data *id;
- unsigned int i, nondiag;
-
- nondiag = cloth_count_nondiag_blocks(cloth);
- cloth->implicit = id = BPH_mass_spring_solver_create(cloth->numverts, nondiag);
-
- for (i = 0; i < cloth->numverts; i++) {
- BPH_mass_spring_set_vertex_mass(id, i, verts[i].mass);
- }
-
- for (i = 0; i < cloth->numverts; i++) {
- BPH_mass_spring_set_motion_state(id, i, verts[i].x, ZERO);
- }
-
- return 1;
-}
-
-void BPH_cloth_solver_free(ClothModifierData *clmd)
-{
- Cloth *cloth = clmd->clothObject;
-
- if (cloth->implicit) {
- BPH_mass_spring_solver_free(cloth->implicit);
- cloth->implicit = NULL;
- }
-}
-
-void BKE_cloth_solver_set_positions(ClothModifierData *clmd)
-{
- Cloth *cloth = clmd->clothObject;
- ClothVertex *verts = cloth->verts;
- unsigned int numverts = cloth->numverts, i;
- ClothHairData *cloth_hairdata = clmd->hairdata;
- Implicit_Data *id = cloth->implicit;
-
- for (i = 0; i < numverts; i++) {
- if (cloth_hairdata) {
- ClothHairData *root = &cloth_hairdata[i];
- BPH_mass_spring_set_rest_transform(id, i, root->rot);
- }
- else
- BPH_mass_spring_set_rest_transform(id, i, I3);
-
- BPH_mass_spring_set_motion_state(id, i, verts[i].x, verts[i].v);
- }
-}
-
-static bool collision_response(ClothModifierData *clmd, CollisionModifierData *collmd, CollPair *collpair, float dt, float restitution, float r_impulse[3])
-{
- Cloth *cloth = clmd->clothObject;
- int index = collpair->ap1;
- bool result = false;
-
- float v1[3], v2_old[3], v2_new[3], v_rel_old[3], v_rel_new[3];
- float epsilon2 = BLI_bvhtree_getepsilon(collmd->bvhtree);
-
- float margin_distance = (float)collpair->distance - epsilon2;
- float mag_v_rel;
-
- zero_v3(r_impulse);
-
- if (margin_distance > 0.0f)
- return false; /* XXX tested before already? */
-
- /* only handle static collisions here */
- if ( collpair->flag & COLLISION_IN_FUTURE )
- return false;
-
- /* velocity */
- copy_v3_v3(v1, cloth->verts[index].v);
- collision_get_collider_velocity(v2_old, v2_new, collmd, collpair);
- /* relative velocity = velocity of the cloth point relative to the collider */
- sub_v3_v3v3(v_rel_old, v1, v2_old);
- sub_v3_v3v3(v_rel_new, v1, v2_new);
- /* normal component of the relative velocity */
- mag_v_rel = dot_v3v3(v_rel_old, collpair->normal);
-
- /* only valid when moving toward the collider */
- if (mag_v_rel < -ALMOST_ZERO) {
- float v_nor_old, v_nor_new;
- float v_tan_old[3], v_tan_new[3];
- float bounce, repulse;
-
- /* Collision response based on
- * "Simulating Complex Hair with Robust Collision Handling" (Choe, Choi, Ko, ACM SIGGRAPH 2005)
- * http://graphics.snu.ac.kr/publications/2005-choe-HairSim/Choe_2005_SCA.pdf
- */
-
- v_nor_old = mag_v_rel;
- v_nor_new = dot_v3v3(v_rel_new, collpair->normal);
-
- madd_v3_v3v3fl(v_tan_old, v_rel_old, collpair->normal, -v_nor_old);
- madd_v3_v3v3fl(v_tan_new, v_rel_new, collpair->normal, -v_nor_new);
-
- bounce = -v_nor_old * restitution;
-
- repulse = -margin_distance / dt; /* base repulsion velocity in normal direction */
- /* XXX this clamping factor is quite arbitrary ...
- * not sure if there is a more scientific approach, but seems to give good results
- */
- CLAMP(repulse, 0.0f, 4.0f * bounce);
-
- if (margin_distance < -epsilon2) {
- mul_v3_v3fl(r_impulse, collpair->normal, max_ff(repulse, bounce) - v_nor_new);
- }
- else {
- bounce = 0.0f;
- mul_v3_v3fl(r_impulse, collpair->normal, repulse - v_nor_new);
- }
-
- result = true;
- }
-
- return result;
-}
-
-/* Init constraint matrix
- * This is part of the modified CG method suggested by Baraff/Witkin in
- * "Large Steps in Cloth Simulation" (Siggraph 1998)
- */
-static void cloth_setup_constraints(ClothModifierData *clmd, ColliderContacts *contacts, int totcolliders, float dt)
-{
- Cloth *cloth = clmd->clothObject;
- Implicit_Data *data = cloth->implicit;
- ClothVertex *verts = cloth->verts;
- int numverts = cloth->numverts;
- int i, j, v;
-
- const float ZERO[3] = {0.0f, 0.0f, 0.0f};
-
- BPH_mass_spring_clear_constraints(data);
-
- for (v = 0; v < numverts; v++) {
- if (verts[v].flags & CLOTH_VERT_FLAG_PINNED) {
- /* pinned vertex constraints */
- BPH_mass_spring_add_constraint_ndof0(data, v, ZERO); /* velocity is defined externally */
- }
-
- verts[v].impulse_count = 0;
- }
-
- for (i = 0; i < totcolliders; ++i) {
- ColliderContacts *ct = &contacts[i];
- for (j = 0; j < ct->totcollisions; ++j) {
- CollPair *collpair = &ct->collisions[j];
-// float restitution = (1.0f - clmd->coll_parms->damping) * (1.0f - ct->ob->pd->pdef_sbdamp);
- float restitution = 0.0f;
- int v = collpair->face1;
- float impulse[3];
-
- /* pinned verts handled separately */
- if (verts[v].flags & CLOTH_VERT_FLAG_PINNED)
- continue;
-
- /* XXX cheap way of avoiding instability from multiple collisions in the same step
- * this should eventually be supported ...
- */
- if (verts[v].impulse_count > 0)
- continue;
-
- /* calculate collision response */
- if (!collision_response(clmd, ct->collmd, collpair, dt, restitution, impulse))
- continue;
-
- BPH_mass_spring_add_constraint_ndof2(data, v, collpair->normal, impulse);
- ++verts[v].impulse_count;
- }
- }
-}
-
-/* computes where the cloth would be if it were subject to perfectly stiff edges
- * (edge distance constraints) in a lagrangian solver. then add forces to help
- * guide the implicit solver to that state. this function is called after
- * collisions*/
-static int UNUSED_FUNCTION(cloth_calc_helper_forces)(Object *UNUSED(ob), ClothModifierData *clmd, float (*initial_cos)[3], float UNUSED(step), float dt)
-{
- Cloth *cloth= clmd->clothObject;
- float (*cos)[3] = (float (*)[3])MEM_callocN(sizeof(float)*3*cloth->numverts, "cos cloth_calc_helper_forces");
- float *masses = (float *)MEM_callocN(sizeof(float)*cloth->numverts, "cos cloth_calc_helper_forces");
- LinkNode *node;
- ClothSpring *spring;
- ClothVertex *cv;
- int i, steps;
-
- cv = cloth->verts;
- for (i=0; i<cloth->numverts; i++, cv++) {
- copy_v3_v3(cos[i], cv->tx);
-
- if (cv->goal == 1.0f || len_squared_v3v3(initial_cos[i], cv->tx) != 0.0f) {
- masses[i] = 1e+10;
- }
- else {
- masses[i] = cv->mass;
- }
- }
-
- steps = 55;
- for (i=0; i<steps; i++) {
- for (node=cloth->springs; node; node=node->next) {
- /* ClothVertex *cv1, *cv2; */ /* UNUSED */
- int v1, v2;
- float len, c, l, vec[3];
-
- spring = (ClothSpring *)node->link;
- if (spring->type != CLOTH_SPRING_TYPE_STRUCTURAL && spring->type != CLOTH_SPRING_TYPE_SHEAR)
- continue;
-
- v1 = spring->ij; v2 = spring->kl;
- /* cv1 = cloth->verts + v1; */ /* UNUSED */
- /* cv2 = cloth->verts + v2; */ /* UNUSED */
- len = len_v3v3(cos[v1], cos[v2]);
-
- sub_v3_v3v3(vec, cos[v1], cos[v2]);
- normalize_v3(vec);
-
- c = (len - spring->restlen);
- if (c == 0.0f)
- continue;
-
- l = c / ((1.0f / masses[v1]) + (1.0f / masses[v2]));
-
- mul_v3_fl(vec, -(1.0f / masses[v1]) * l);
- add_v3_v3(cos[v1], vec);
-
- sub_v3_v3v3(vec, cos[v2], cos[v1]);
- normalize_v3(vec);
-
- mul_v3_fl(vec, -(1.0f / masses[v2]) * l);
- add_v3_v3(cos[v2], vec);
- }
- }
-
- cv = cloth->verts;
- for (i=0; i<cloth->numverts; i++, cv++) {
- float vec[3];
-
- /*compute forces*/
- sub_v3_v3v3(vec, cos[i], cv->tx);
- mul_v3_fl(vec, cv->mass*dt*20.0f);
- add_v3_v3(cv->tv, vec);
- //copy_v3_v3(cv->tx, cos[i]);
- }
-
- MEM_freeN(cos);
- MEM_freeN(masses);
-
- return 1;
-}
-
-BLI_INLINE void cloth_calc_spring_force(ClothModifierData *clmd, ClothSpring *s, float time)
-{
- Cloth *cloth = clmd->clothObject;
- ClothSimSettings *parms = clmd->sim_parms;
- Implicit_Data *data = cloth->implicit;
- ClothVertex *verts = cloth->verts;
-
- bool no_compress = parms->flags & CLOTH_SIMSETTINGS_FLAG_NO_SPRING_COMPRESS;
-
- zero_v3(s->f);
- zero_m3(s->dfdx);
- zero_m3(s->dfdv);
-
- s->flags &= ~CLOTH_SPRING_FLAG_NEEDED;
-
- // calculate force of structural + shear springs
- if ((s->type & CLOTH_SPRING_TYPE_STRUCTURAL) || (s->type & CLOTH_SPRING_TYPE_SHEAR) || (s->type & CLOTH_SPRING_TYPE_SEWING) ) {
-#ifdef CLOTH_FORCE_SPRING_STRUCTURAL
- float k, scaling;
-
- s->flags |= CLOTH_SPRING_FLAG_NEEDED;
-
- scaling = parms->structural + s->stiffness * fabsf(parms->max_struct - parms->structural);
- k = scaling / (parms->avg_spring_len + FLT_EPSILON);
-
- if (s->type & CLOTH_SPRING_TYPE_SEWING) {
- // TODO: verify, half verified (couldn't see error)
- // sewing springs usually have a large distance at first so clamp the force so we don't get tunnelling through colission objects
- BPH_mass_spring_force_spring_linear(data, s->ij, s->kl, s->restlen, k, parms->Cdis, no_compress, parms->max_sewing, s->f, s->dfdx, s->dfdv);
- }
- else {
- BPH_mass_spring_force_spring_linear(data, s->ij, s->kl, s->restlen, k, parms->Cdis, no_compress, 0.0f, s->f, s->dfdx, s->dfdv);
- }
-#endif
- }
- else if (s->type & CLOTH_SPRING_TYPE_GOAL) {
-#ifdef CLOTH_FORCE_SPRING_GOAL
- float goal_x[3], goal_v[3];
- float k, scaling;
-
- s->flags |= CLOTH_SPRING_FLAG_NEEDED;
-
- // current_position = xold + t * (newposition - xold)
- interp_v3_v3v3(goal_x, verts[s->ij].xold, verts[s->ij].xconst, time);
- sub_v3_v3v3(goal_v, verts[s->ij].xconst, verts[s->ij].xold); // distance covered over dt==1
-
- scaling = parms->goalspring + s->stiffness * fabsf(parms->max_struct - parms->goalspring);
- k = verts[s->ij].goal * scaling / (parms->avg_spring_len + FLT_EPSILON);
-
- BPH_mass_spring_force_spring_goal(data, s->ij, goal_x, goal_v, k, parms->goalfrict * 0.01f, s->f, s->dfdx, s->dfdv);
-#endif
- }
- else if (s->type & CLOTH_SPRING_TYPE_BENDING) { /* calculate force of bending springs */
-#ifdef CLOTH_FORCE_SPRING_BEND
- float kb, cb, scaling;
-
- s->flags |= CLOTH_SPRING_FLAG_NEEDED;
-
- scaling = parms->bending + s->stiffness * fabsf(parms->max_bend - parms->bending);
- kb = scaling / (20.0f * (parms->avg_spring_len + FLT_EPSILON));
-
- scaling = parms->bending_damping;
- cb = scaling / (20.0f * (parms->avg_spring_len + FLT_EPSILON));
-
- BPH_mass_spring_force_spring_bending(data, s->ij, s->kl, s->restlen, kb, cb, s->f, s->dfdx, s->dfdv);
-#endif
- }
- else if (s->type & CLOTH_SPRING_TYPE_BENDING_ANG) {
-#ifdef CLOTH_FORCE_SPRING_BEND
- float kb, cb, scaling;
-
- s->flags |= CLOTH_SPRING_FLAG_NEEDED;
-
- /* XXX WARNING: angular bending springs for hair apply stiffness factor as an overall factor, unlike cloth springs!
- * this is crap, but needed due to cloth/hair mixing ...
- * max_bend factor is not even used for hair, so ...
- */
- scaling = s->stiffness * parms->bending;
- kb = scaling / (20.0f * (parms->avg_spring_len + FLT_EPSILON));
-
- scaling = parms->bending_damping;
- cb = scaling / (20.0f * (parms->avg_spring_len + FLT_EPSILON));
-
- /* XXX assuming same restlen for ij and jk segments here, this can be done correctly for hair later */
- BPH_mass_spring_force_spring_bending_angular(data, s->ij, s->kl, s->mn, s->target, kb, cb);
-
-#if 0
- {
- float x_kl[3], x_mn[3], v[3], d[3];
-
- BPH_mass_spring_get_motion_state(data, s->kl, x_kl, v);
- BPH_mass_spring_get_motion_state(data, s->mn, x_mn, v);
-
- BKE_sim_debug_data_add_dot(clmd->debug_data, x_kl, 0.9, 0.9, 0.9, "target", 7980, s->kl);
- BKE_sim_debug_data_add_line(clmd->debug_data, x_kl, x_mn, 0.8, 0.8, 0.8, "target", 7981, s->kl);
-
- copy_v3_v3(d, s->target);
- BKE_sim_debug_data_add_vector(clmd->debug_data, x_kl, d, 0.8, 0.8, 0.2, "target", 7982, s->kl);
-
-// copy_v3_v3(d, s->target_ij);
-// BKE_sim_debug_data_add_vector(clmd->debug_data, x, d, 1, 0.4, 0.4, "target", 7983, s->kl);
- }
-#endif
-#endif
- }
-}
-
-static void hair_get_boundbox(ClothModifierData *clmd, float gmin[3], float gmax[3])
-{
- Cloth *cloth = clmd->clothObject;
- Implicit_Data *data = cloth->implicit;
- unsigned int numverts = cloth->numverts;
- int i;
-
- INIT_MINMAX(gmin, gmax);
- for (i = 0; i < numverts; i++) {
- float x[3];
- BPH_mass_spring_get_motion_state(data, i, x, NULL);
- DO_MINMAX(x, gmin, gmax);
- }
-}
-
-static void cloth_calc_force(ClothModifierData *clmd, float UNUSED(frame), ListBase *effectors, float time)
-{
- /* Collect forces and derivatives: F, dFdX, dFdV */
- Cloth *cloth = clmd->clothObject;
- Implicit_Data *data = cloth->implicit;
- unsigned int i = 0;
- float drag = clmd->sim_parms->Cvi * 0.01f; /* viscosity of air scaled in percent */
- float gravity[3] = {0.0f, 0.0f, 0.0f};
- MFace *mfaces = cloth->mfaces;
- unsigned int numverts = cloth->numverts;
- ClothVertex *vert;
-
-#ifdef CLOTH_FORCE_GRAVITY
- /* global acceleration (gravitation) */
- if (clmd->scene->physics_settings.flag & PHYS_GLOBAL_GRAVITY) {
- /* scale gravity force */
- mul_v3_v3fl(gravity, clmd->scene->physics_settings.gravity, 0.001f * clmd->sim_parms->effector_weights->global_gravity);
- }
- vert = cloth->verts;
- for (i = 0; i < cloth->numverts; i++, vert++) {
- BPH_mass_spring_force_gravity(data, i, vert->mass, gravity);
- }
-#endif
-
- /* cloth_calc_volume_force(clmd); */
-
-#ifdef CLOTH_FORCE_DRAG
- BPH_mass_spring_force_drag(data, drag);
-#endif
-
- /* handle external forces like wind */
- if (effectors) {
- /* cache per-vertex forces to avoid redundant calculation */
- float (*winvec)[3] = (float (*)[3])MEM_callocN(sizeof(float) * 3 * numverts, "effector forces");
- for (i = 0; i < cloth->numverts; i++) {
- float x[3], v[3];
- EffectedPoint epoint;
-
- BPH_mass_spring_get_motion_state(data, i, x, v);
- pd_point_from_loc(clmd->scene, x, v, i, &epoint);
- pdDoEffectors(effectors, NULL, clmd->sim_parms->effector_weights, &epoint, winvec[i], NULL);
- }
-
- for (i = 0; i < cloth->numfaces; i++) {
- MFace *mf = &mfaces[i];
- BPH_mass_spring_force_face_wind(data, mf->v1, mf->v2, mf->v3, mf->v4, winvec);
- }
-
- /* Hair has only edges */
- if (cloth->numfaces == 0) {
-#if 0
- ClothHairData *hairdata = clmd->hairdata;
- ClothHairData *hair_ij, *hair_kl;
-
- for (LinkNode *link = cloth->springs; link; link = link->next) {
- ClothSpring *spring = (ClothSpring *)link->link;
- if (spring->type == CLOTH_SPRING_TYPE_STRUCTURAL) {
- if (hairdata) {
- hair_ij = &hairdata[spring->ij];
- hair_kl = &hairdata[spring->kl];
- BPH_mass_spring_force_edge_wind(data, spring->ij, spring->kl, hair_ij->radius, hair_kl->radius, winvec);
- }
- else
- BPH_mass_spring_force_edge_wind(data, spring->ij, spring->kl, 1.0f, 1.0f, winvec);
- }
- }
-#else
- ClothHairData *hairdata = clmd->hairdata;
-
- vert = cloth->verts;
- for (i = 0; i < cloth->numverts; i++, vert++) {
- if (hairdata) {
- ClothHairData *hair = &hairdata[i];
- BPH_mass_spring_force_vertex_wind(data, i, hair->radius, winvec);
- }
- else
- BPH_mass_spring_force_vertex_wind(data, i, 1.0f, winvec);
- }
- }
-#endif
-
- MEM_freeN(winvec);
- }
-
- // calculate spring forces
- for (LinkNode *link = cloth->springs; link; link = link->next) {
- ClothSpring *spring = (ClothSpring *)link->link;
- // only handle active springs
- if (!(spring->flags & CLOTH_SPRING_FLAG_DEACTIVATE))
- cloth_calc_spring_force(clmd, spring, time);
- }
-}
-
-/* returns vertexes' motion state */
-BLI_INLINE void cloth_get_grid_location(Implicit_Data *data, float cell_scale, const float cell_offset[3],
- int index, float x[3], float v[3])
-{
- BPH_mass_spring_get_position(data, index, x);
- BPH_mass_spring_get_new_velocity(data, index, v);
-
- mul_v3_fl(x, cell_scale);
- add_v3_v3(x, cell_offset);
-}
-
-/* returns next spring forming a continous hair sequence */
-BLI_INLINE LinkNode *hair_spring_next(LinkNode *spring_link)
-{
- ClothSpring *spring = (ClothSpring *)spring_link->link;
- LinkNode *next = spring_link->next;
- if (next) {
- ClothSpring *next_spring = (ClothSpring *)next->link;
- if (next_spring->type == CLOTH_SPRING_TYPE_STRUCTURAL && next_spring->kl == spring->ij)
- return next;
- }
- return NULL;
-}
-
-/* XXX this is nasty: cloth meshes do not explicitly store
- * the order of hair segments!
- * We have to rely on the spring build function for now,
- * which adds structural springs in reverse order:
- * (3,4), (2,3), (1,2)
- * This is currently the only way to figure out hair geometry inside this code ...
- */
-static LinkNode *cloth_continuum_add_hair_segments(HairGrid *grid, const float cell_scale, const float cell_offset[3], Cloth *cloth, LinkNode *spring_link)
-{
- Implicit_Data *data = cloth->implicit;
- LinkNode *next_spring_link = NULL; /* return value */
- ClothSpring *spring1, *spring2, *spring3;
- // ClothVertex *verts = cloth->verts;
- // ClothVertex *vert3, *vert4;
- float x1[3], v1[3], x2[3], v2[3], x3[3], v3[3], x4[3], v4[3];
- float dir1[3], dir2[3], dir3[3];
-
- spring1 = NULL;
- spring2 = NULL;
- spring3 = (ClothSpring *)spring_link->link;
-
- zero_v3(x1); zero_v3(v1);
- zero_v3(dir1);
- zero_v3(x2); zero_v3(v2);
- zero_v3(dir2);
-
- // vert3 = &verts[spring3->kl];
- cloth_get_grid_location(data, cell_scale, cell_offset, spring3->kl, x3, v3);
- // vert4 = &verts[spring3->ij];
- cloth_get_grid_location(data, cell_scale, cell_offset, spring3->ij, x4, v4);
- sub_v3_v3v3(dir3, x4, x3);
- normalize_v3(dir3);
-
- while (spring_link) {
- /* move on */
- spring1 = spring2;
- spring2 = spring3;
-
- // vert3 = vert4;
-
- copy_v3_v3(x1, x2); copy_v3_v3(v1, v2);
- copy_v3_v3(x2, x3); copy_v3_v3(v2, v3);
- copy_v3_v3(x3, x4); copy_v3_v3(v3, v4);
-
- copy_v3_v3(dir1, dir2);
- copy_v3_v3(dir2, dir3);
-
- /* read next segment */
- next_spring_link = spring_link->next;
- spring_link = hair_spring_next(spring_link);
-
- if (spring_link) {
- spring3 = (ClothSpring *)spring_link->link;
- // vert4 = &verts[spring3->ij];
- cloth_get_grid_location(data, cell_scale, cell_offset, spring3->ij, x4, v4);
- sub_v3_v3v3(dir3, x4, x3);
- normalize_v3(dir3);
- }
- else {
- spring3 = NULL;
- // vert4 = NULL;
- zero_v3(x4); zero_v3(v4);
- zero_v3(dir3);
- }
-
- BPH_hair_volume_add_segment(grid, x1, v1, x2, v2, x3, v3, x4, v4,
- spring1 ? dir1 : NULL,
- dir2,
- spring3 ? dir3 : NULL);
- }
-
- return next_spring_link;
-}
-
-static void cloth_continuum_fill_grid(HairGrid *grid, Cloth *cloth)
-{
-#if 0
- Implicit_Data *data = cloth->implicit;
- int numverts = cloth->numverts;
- ClothVertex *vert;
- int i;
-
- for (i = 0, vert = cloth->verts; i < numverts; i++, vert++) {
- float x[3], v[3];
-
- cloth_get_vertex_motion_state(data, vert, x, v);
- BPH_hair_volume_add_vertex(grid, x, v);
- }
-#else
- LinkNode *link;
- float cellsize, gmin[3], cell_scale, cell_offset[3];
-
- /* scale and offset for transforming vertex locations into grid space
- * (cell size is 0..1, gmin becomes origin)
- */
- BPH_hair_volume_grid_geometry(grid, &cellsize, NULL, gmin, NULL);
- cell_scale = cellsize > 0.0f ? 1.0f / cellsize : 0.0f;
- mul_v3_v3fl(cell_offset, gmin, cell_scale);
- negate_v3(cell_offset);
-
- link = cloth->springs;
- while (link) {
- ClothSpring *spring = (ClothSpring *)link->link;
- if (spring->type == CLOTH_SPRING_TYPE_STRUCTURAL)
- link = cloth_continuum_add_hair_segments(grid, cell_scale, cell_offset, cloth, link);
- else
- link = link->next;
- }
-#endif
- BPH_hair_volume_normalize_vertex_grid(grid);
-}
-
-static void cloth_continuum_step(ClothModifierData *clmd, float dt)
-{
- ClothSimSettings *parms = clmd->sim_parms;
- Cloth *cloth = clmd->clothObject;
- Implicit_Data *data = cloth->implicit;
- int numverts = cloth->numverts;
- ClothVertex *vert;
-
- const float fluid_factor = 0.95f; /* blend between PIC and FLIP methods */
- float smoothfac = parms->velocity_smooth;
- /* XXX FIXME arbitrary factor!!! this should be based on some intuitive value instead,
- * like number of hairs per cell and time decay instead of "strength"
- */
- float density_target = parms->density_target;
- float density_strength = parms->density_strength;
- float gmin[3], gmax[3];
- int i;
-
- /* clear grid info */
- zero_v3_int(clmd->hair_grid_res);
- zero_v3(clmd->hair_grid_min);
- zero_v3(clmd->hair_grid_max);
- clmd->hair_grid_cellsize = 0.0f;
-
- hair_get_boundbox(clmd, gmin, gmax);
-
- /* gather velocities & density */
- if (smoothfac > 0.0f || density_strength > 0.0f) {
- HairGrid *grid = BPH_hair_volume_create_vertex_grid(clmd->sim_parms->voxel_cell_size, gmin, gmax);
-
- cloth_continuum_fill_grid(grid, cloth);
-
- /* main hair continuum solver */
- BPH_hair_volume_solve_divergence(grid, dt, density_target, density_strength);
-
- for (i = 0, vert = cloth->verts; i < numverts; i++, vert++) {
- float x[3], v[3], nv[3];
-
- /* calculate volumetric velocity influence */
- BPH_mass_spring_get_position(data, i, x);
- BPH_mass_spring_get_new_velocity(data, i, v);
-
- BPH_hair_volume_grid_velocity(grid, x, v, fluid_factor, nv);
-
- interp_v3_v3v3(nv, v, nv, smoothfac);
-
- /* apply on hair data */
- BPH_mass_spring_set_new_velocity(data, i, nv);
- }
-
- /* store basic grid info in the modifier data */
- BPH_hair_volume_grid_geometry(grid, &clmd->hair_grid_cellsize, clmd->hair_grid_res, clmd->hair_grid_min, clmd->hair_grid_max);
-
-#if 0 /* DEBUG hair velocity vector field */
- {
- const int size = 64;
- int i, j;
- float offset[3], a[3], b[3];
- const int axis = 0;
- const float shift = 0.0f;
-
- copy_v3_v3(offset, clmd->hair_grid_min);
- zero_v3(a);
- zero_v3(b);
-
- offset[axis] = shift * clmd->hair_grid_cellsize;
- a[(axis+1) % 3] = clmd->hair_grid_max[(axis+1) % 3] - clmd->hair_grid_min[(axis+1) % 3];
- b[(axis+2) % 3] = clmd->hair_grid_max[(axis+2) % 3] - clmd->hair_grid_min[(axis+2) % 3];
-
- BKE_sim_debug_data_clear_category(clmd->debug_data, "grid velocity");
- for (j = 0; j < size; ++j) {
- for (i = 0; i < size; ++i) {
- float x[3], v[3], gvel[3], gvel_smooth[3], gdensity;
-
- madd_v3_v3v3fl(x, offset, a, (float)i / (float)(size-1));
- madd_v3_v3fl(x, b, (float)j / (float)(size-1));
- zero_v3(v);
-
- BPH_hair_volume_grid_interpolate(grid, x, &gdensity, gvel, gvel_smooth, NULL, NULL);
-
-// BKE_sim_debug_data_add_circle(clmd->debug_data, x, gdensity, 0.7, 0.3, 1, "grid density", i, j, 3111);
- if (!is_zero_v3(gvel) || !is_zero_v3(gvel_smooth)) {
- float dvel[3];
- sub_v3_v3v3(dvel, gvel_smooth, gvel);
-// BKE_sim_debug_data_add_vector(clmd->debug_data, x, gvel, 0.4, 0, 1, "grid velocity", i, j, 3112);
-// BKE_sim_debug_data_add_vector(clmd->debug_data, x, gvel_smooth, 0.6, 1, 1, "grid velocity", i, j, 3113);
- BKE_sim_debug_data_add_vector(clmd->debug_data, x, dvel, 0.4, 1, 0.7, "grid velocity", i, j, 3114);
-#if 0
- if (gdensity > 0.0f) {
- float col0[3] = {0.0, 0.0, 0.0};
- float col1[3] = {0.0, 1.0, 0.0};
- float col[3];
-
- interp_v3_v3v3(col, col0, col1, CLAMPIS(gdensity * clmd->sim_parms->density_strength, 0.0, 1.0));
-// BKE_sim_debug_data_add_circle(clmd->debug_data, x, gdensity * clmd->sim_parms->density_strength, 0, 1, 0.4, "grid velocity", i, j, 3115);
-// BKE_sim_debug_data_add_dot(clmd->debug_data, x, col[0], col[1], col[2], "grid velocity", i, j, 3115);
- BKE_sim_debug_data_add_circle(clmd->debug_data, x, 0.01f, col[0], col[1], col[2], "grid velocity", i, j, 3115);
- }
-#endif
- }
- }
- }
- }
-#endif
-
- BPH_hair_volume_free_vertex_grid(grid);
- }
-}
-
-#if 0
-static void cloth_calc_volume_force(ClothModifierData *clmd)
-{
- ClothSimSettings *parms = clmd->sim_parms;
- Cloth *cloth = clmd->clothObject;
- Implicit_Data *data = cloth->implicit;
- int numverts = cloth->numverts;
- ClothVertex *vert;
-
- /* 2.0f is an experimental value that seems to give good results */
- float smoothfac = 2.0f * parms->velocity_smooth;
- float collfac = 2.0f * parms->collider_friction;
- float pressfac = parms->pressure;
- float minpress = parms->pressure_threshold;
- float gmin[3], gmax[3];
- int i;
-
- hair_get_boundbox(clmd, gmin, gmax);
-
- /* gather velocities & density */
- if (smoothfac > 0.0f || pressfac > 0.0f) {
- HairVertexGrid *vertex_grid = BPH_hair_volume_create_vertex_grid(clmd->sim_parms->voxel_res, gmin, gmax);
-
- vert = cloth->verts;
- for (i = 0; i < numverts; i++, vert++) {
- float x[3], v[3];
-
- if (vert->solver_index < 0) {
- copy_v3_v3(x, vert->x);
- copy_v3_v3(v, vert->v);
- }
- else {
- BPH_mass_spring_get_motion_state(data, vert->solver_index, x, v);
- }
- BPH_hair_volume_add_vertex(vertex_grid, x, v);
- }
- BPH_hair_volume_normalize_vertex_grid(vertex_grid);
-
- vert = cloth->verts;
- for (i = 0; i < numverts; i++, vert++) {
- float x[3], v[3], f[3], dfdx[3][3], dfdv[3][3];
-
- if (vert->solver_index < 0)
- continue;
-
- /* calculate volumetric forces */
- BPH_mass_spring_get_motion_state(data, vert->solver_index, x, v);
- BPH_hair_volume_vertex_grid_forces(vertex_grid, x, v, smoothfac, pressfac, minpress, f, dfdx, dfdv);
- /* apply on hair data */
- BPH_mass_spring_force_extern(data, vert->solver_index, f, dfdx, dfdv);
- }
-
- BPH_hair_volume_free_vertex_grid(vertex_grid);
- }
-}
-#endif
-
-/* old collision stuff for cloth, use for continuity
- * until a good replacement is ready
- */
-static void cloth_collision_solve_extra(Object *ob, ClothModifierData *clmd, ListBase *effectors, float frame, float step, float dt)
-{
- Cloth *cloth = clmd->clothObject;
- Implicit_Data *id = cloth->implicit;
- ClothVertex *verts = cloth->verts;
- int numverts = cloth->numverts;
- const float spf = (float)clmd->sim_parms->stepsPerFrame / clmd->sim_parms->timescale;;
-
- bool do_extra_solve;
- int i;
-
- if (!(clmd->coll_parms->flags & CLOTH_COLLSETTINGS_FLAG_ENABLED))
- return;
- if (!clmd->clothObject->bvhtree)
- return;
-
- // update verts to current positions
- for (i = 0; i < numverts; i++) {
- BPH_mass_spring_get_new_position(id, i, verts[i].tx);
-
- sub_v3_v3v3(verts[i].tv, verts[i].tx, verts[i].txold);
- copy_v3_v3(verts[i].v, verts[i].tv);
- }
-
-#if 0 /* unused */
- for (i=0, cv=cloth->verts; i<cloth->numverts; i++, cv++) {
- copy_v3_v3(initial_cos[i], cv->tx);
- }
-#endif
-
- // call collision function
- // TODO: check if "step" or "step+dt" is correct - dg
- do_extra_solve = cloth_bvh_objcollision(ob, clmd, step / clmd->sim_parms->timescale, dt / clmd->sim_parms->timescale);
-
- // copy corrected positions back to simulation
- for (i = 0; i < numverts; i++) {
- float curx[3];
- BPH_mass_spring_get_position(id, i, curx);
- // correct velocity again, just to be sure we had to change it due to adaptive collisions
- sub_v3_v3v3(verts[i].tv, verts[i].tx, curx);
- }
-
- if (do_extra_solve) {
-// cloth_calc_helper_forces(ob, clmd, initial_cos, step/clmd->sim_parms->timescale, dt/clmd->sim_parms->timescale);
-
- for (i = 0; i < numverts; i++) {
-
- float newv[3];
-
- if ((clmd->sim_parms->flags & CLOTH_SIMSETTINGS_FLAG_GOAL) && (verts [i].flags & CLOTH_VERT_FLAG_PINNED))
- continue;
-
- BPH_mass_spring_set_new_position(id, i, verts[i].tx);
- mul_v3_v3fl(newv, verts[i].tv, spf);
- BPH_mass_spring_set_new_velocity(id, i, newv);
- }
- }
-
- // X = Xnew;
- BPH_mass_spring_apply_result(id);
-
- if (do_extra_solve) {
- ImplicitSolverResult result;
-
- /* initialize forces to zero */
- BPH_mass_spring_clear_forces(id);
-
- // calculate forces
- cloth_calc_force(clmd, frame, effectors, step);
-
- // calculate new velocity and position
- BPH_mass_spring_solve_velocities(id, dt, &result);
-// cloth_record_result(clmd, &result, clmd->sim_parms->stepsPerFrame);
-
- /* note: positions are advanced only once in the main solver step! */
-
- BPH_mass_spring_apply_result(id);
- }
-}
-
-static void cloth_clear_result(ClothModifierData *clmd)
-{
- ClothSolverResult *sres = clmd->solver_result;
-
- sres->status = 0;
- sres->max_error = sres->min_error = sres->avg_error = 0.0f;
- sres->max_iterations = sres->min_iterations = 0;
- sres->avg_iterations = 0.0f;
-}
-
-static void cloth_record_result(ClothModifierData *clmd, ImplicitSolverResult *result, int steps)
-{
- ClothSolverResult *sres = clmd->solver_result;
-
- if (sres->status) { /* already initialized ? */
- /* error only makes sense for successful iterations */
- if (result->status == BPH_SOLVER_SUCCESS) {
- sres->min_error = min_ff(sres->min_error, result->error);
- sres->max_error = max_ff(sres->max_error, result->error);
- sres->avg_error += result->error / (float)steps;
- }
-
- sres->min_iterations = min_ii(sres->min_iterations, result->iterations);
- sres->max_iterations = max_ii(sres->max_iterations, result->iterations);
- sres->avg_iterations += (float)result->iterations / (float)steps;
- }
- else {
- /* error only makes sense for successful iterations */
- if (result->status == BPH_SOLVER_SUCCESS) {
- sres->min_error = sres->max_error = result->error;
- sres->avg_error += result->error / (float)steps;
- }
-
- sres->min_iterations = sres->max_iterations = result->iterations;
- sres->avg_iterations += (float)result->iterations / (float)steps;
- }
-
- sres->status |= result->status;
-}
-
-int BPH_cloth_solve(Object *ob, float frame, ClothModifierData *clmd, ListBase *effectors)
-{
- /* Hair currently is a cloth sim in disguise ...
- * Collision detection and volumetrics work differently then.
- * Bad design, TODO
- */
- const bool is_hair = (clmd->hairdata != NULL);
-
- unsigned int i=0;
- float step=0.0f, tf=clmd->sim_parms->timescale;
- Cloth *cloth = clmd->clothObject;
- ClothVertex *verts = cloth->verts/*, *cv*/;
- unsigned int numverts = cloth->numverts;
- float dt = clmd->sim_parms->timescale / clmd->sim_parms->stepsPerFrame;
- Implicit_Data *id = cloth->implicit;
- ColliderContacts *contacts = NULL;
- int totcolliders = 0;
-
- BKE_sim_debug_data_clear_category("collision");
-
- if (!clmd->solver_result)
- clmd->solver_result = (ClothSolverResult *)MEM_callocN(sizeof(ClothSolverResult), "cloth solver result");
- cloth_clear_result(clmd);
-
- if (clmd->sim_parms->flags & CLOTH_SIMSETTINGS_FLAG_GOAL) { /* do goal stuff */
- for (i = 0; i < numverts; i++) {
- // update velocities with constrained velocities from pinned verts
- if (verts [i].flags & CLOTH_VERT_FLAG_PINNED) {
- float v[3];
- sub_v3_v3v3(v, verts[i].xconst, verts[i].xold);
- // mul_v3_fl(v, clmd->sim_parms->stepsPerFrame);
- BPH_mass_spring_set_velocity(id, i, v);
- }
- }
- }
-
- while (step < tf) {
- ImplicitSolverResult result;
-
- /* copy velocities for collision */
- for (i = 0; i < numverts; i++) {
- BPH_mass_spring_get_motion_state(id, i, NULL, verts[i].tv);
- copy_v3_v3(verts[i].v, verts[i].tv);
- }
-
- if (is_hair) {
- /* determine contact points */
- if (clmd->coll_parms->flags & CLOTH_COLLSETTINGS_FLAG_ENABLED) {
- cloth_find_point_contacts(ob, clmd, 0.0f, tf, &contacts, &totcolliders);
- }
-
- /* setup vertex constraints for pinned vertices and contacts */
- cloth_setup_constraints(clmd, contacts, totcolliders, dt);
- }
- else {
- /* setup vertex constraints for pinned vertices */
- cloth_setup_constraints(clmd, NULL, 0, dt);
- }
-
- /* initialize forces to zero */
- BPH_mass_spring_clear_forces(id);
-
- // damping velocity for artistic reasons
- // this is a bad way to do it, should be removed imo - lukas_t
- if (clmd->sim_parms->vel_damping != 1.0f) {
- for (i = 0; i < numverts; i++) {
- float v[3];
- BPH_mass_spring_get_motion_state(id, i, NULL, v);
- mul_v3_fl(v, clmd->sim_parms->vel_damping);
- BPH_mass_spring_set_velocity(id, i, v);
- }
- }
-
- // calculate forces
- cloth_calc_force(clmd, frame, effectors, step);
-
- // calculate new velocity and position
- BPH_mass_spring_solve_velocities(id, dt, &result);
- cloth_record_result(clmd, &result, clmd->sim_parms->stepsPerFrame);
-
- if (is_hair) {
- cloth_continuum_step(clmd, dt);
- }
-
- BPH_mass_spring_solve_positions(id, dt);
-
- if (!is_hair) {
- cloth_collision_solve_extra(ob, clmd, effectors, frame, step, dt);
- }
-
- BPH_mass_spring_apply_result(id);
-
- /* move pinned verts to correct position */
- for (i = 0; i < numverts; i++) {
- if (clmd->sim_parms->flags & CLOTH_SIMSETTINGS_FLAG_GOAL) {
- if (verts[i].flags & CLOTH_VERT_FLAG_PINNED) {
- float x[3];
- interp_v3_v3v3(x, verts[i].xold, verts[i].xconst, step + dt);
- BPH_mass_spring_set_position(id, i, x);
- }
- }
-
- BPH_mass_spring_get_motion_state(id, i, verts[i].txold, NULL);
- }
-
- /* free contact points */
- if (contacts) {
- cloth_free_contacts(contacts, totcolliders);
- }
-
- step += dt;
- }
-
- /* copy results back to cloth data */
- for (i = 0; i < numverts; i++) {
- BPH_mass_spring_get_motion_state(id, i, verts[i].x, verts[i].v);
- copy_v3_v3(verts[i].txold, verts[i].x);
- }
-
- return 1;
-}
-
-bool BPH_cloth_solver_get_texture_data(Object *UNUSED(ob), ClothModifierData *clmd, VoxelData *vd)
-{
- Cloth *cloth = clmd->clothObject;
- HairGrid *grid;
- float gmin[3], gmax[3];
-
- if (!clmd->clothObject || !clmd->clothObject->implicit)
- return false;
-
- hair_get_boundbox(clmd, gmin, gmax);
-
- grid = BPH_hair_volume_create_vertex_grid(clmd->sim_parms->voxel_cell_size, gmin, gmax);
- cloth_continuum_fill_grid(grid, cloth);
-
- BPH_hair_volume_get_texture_data(grid, vd);
-
- BPH_hair_volume_free_vertex_grid(grid);
-
- return true;
-}
+/* + * ***** 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) Blender Foundation + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): Lukas Toenne + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/physics/intern/BPH_mass_spring.cpp + * \ingroup bph + */ + +extern "C" { +#include "MEM_guardedalloc.h" + +#include "DNA_cache_library_types.h" +#include "DNA_cloth_types.h" +#include "DNA_scene_types.h" +#include "DNA_object_force.h" +#include "DNA_object_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_modifier_types.h" + +#include "BLI_math.h" +#include "BLI_linklist.h" +#include "BLI_utildefines.h" + +#include "BKE_cache_library.h" +#include "BKE_cloth.h" +#include "BKE_collision.h" +#include "BKE_colortools.h" +#include "BKE_effect.h" +#include "BKE_strands.h" +} + +#include "BPH_mass_spring.h" +#include "implicit.h" + +static float I3[3][3] = {{1.0, 0.0, 0.0}, {0.0, 1.0, 0.0}, {0.0, 0.0, 1.0}}; + +/* Number of off-diagonal non-zero matrix blocks. + * Basically there is one of these for each vertex-vertex interaction. + */ +static int cloth_count_nondiag_blocks(Cloth *cloth) +{ + LinkNode *link; + int nondiag = 0; + + for (link = cloth->springs; link; link = link->next) { + ClothSpring *spring = (ClothSpring *)link->link; + + switch (spring->type) { + case CLOTH_SPRING_TYPE_BENDING_ANG: + /* angular bending combines 3 vertices */ + nondiag += 3; + break; + + default: + /* all other springs depend on 2 vertices only */ + nondiag += 1; + break; + } + } + + return nondiag; +} + +static struct Implicit_Data *cloth_solver_init_data(Cloth *cloth) +{ + int totvert = cloth->numverts; + + if (cloth->implicit && totvert != BPH_mass_spring_solver_numvert(cloth->implicit)) { + BPH_mass_spring_solver_free(cloth->implicit); + cloth->implicit = NULL; + } + + if (!cloth->implicit) { + int nondiag = cloth_count_nondiag_blocks(cloth); + cloth->implicit = BPH_mass_spring_solver_create(totvert, nondiag); + } + + return cloth->implicit; +} + +int BPH_cloth_solver_init(Object *UNUSED(ob), ClothModifierData *clmd) +{ + Cloth *cloth = clmd->clothObject; + ClothVertex *vert; + const float ZERO[3] = {0.0f, 0.0f, 0.0f}; + Implicit_Data *id = cloth_solver_init_data(cloth); + unsigned int i; + + vert = cloth->verts; + for (i = 0; i < cloth->numverts; ++i, ++vert) { + BPH_mass_spring_set_vertex_mass(id, i, vert->mass); + BPH_mass_spring_set_motion_state(id, i, vert->x, ZERO); + } + + return 1; +} + +void BPH_cloth_solver_free(ClothModifierData *clmd) +{ + Cloth *cloth = clmd->clothObject; + + if (cloth->implicit) { + BPH_mass_spring_solver_free(cloth->implicit); + cloth->implicit = NULL; + } +} + +void BPH_cloth_solver_set_positions(ClothModifierData *clmd) +{ + Cloth *cloth = clmd->clothObject; + ClothVertex *vert; + unsigned int numverts = cloth->numverts, i; + ClothHairData *cloth_hairdata = clmd->hairdata; + Implicit_Data *id = cloth_solver_init_data(cloth); + + vert = cloth->verts; + for (i = 0; i < numverts; ++i, ++vert) { + if (cloth_hairdata) { + ClothHairData *root = &cloth_hairdata[i]; + BPH_mass_spring_set_rest_transform(id, i, root->rot); + } + else + BPH_mass_spring_set_rest_transform(id, i, I3); + + BPH_mass_spring_set_motion_state(id, i, vert->x, vert->v); + } +} + +static bool collision_response(ClothModifierData *clmd, CollisionModifierData *collmd, CollPair *collpair, float dt, float restitution, float r_impulse[3]) +{ + Cloth *cloth = clmd->clothObject; + int index = collpair->ap1; + bool result = false; + + float v1[3], v2_old[3], v2_new[3], v_rel_old[3], v_rel_new[3]; + float epsilon2 = BLI_bvhtree_getepsilon(collmd->bvhtree); + + float margin_distance = (float)collpair->distance - epsilon2; + float mag_v_rel; + + zero_v3(r_impulse); + + if (margin_distance > 0.0f) + return false; /* XXX tested before already? */ + + /* only handle static collisions here */ + if ( collpair->flag & COLLISION_IN_FUTURE ) + return false; + + /* velocity */ + copy_v3_v3(v1, cloth->verts[index].v); + collision_get_collider_velocity(v2_old, v2_new, collmd, collpair); + /* relative velocity = velocity of the cloth point relative to the collider */ + sub_v3_v3v3(v_rel_old, v1, v2_old); + sub_v3_v3v3(v_rel_new, v1, v2_new); + /* normal component of the relative velocity */ + mag_v_rel = dot_v3v3(v_rel_old, collpair->normal); + + /* only valid when moving toward the collider */ + if (mag_v_rel < -ALMOST_ZERO) { + float v_nor_old, v_nor_new; + float v_tan_old[3], v_tan_new[3]; + float bounce, repulse; + + /* Collision response based on + * "Simulating Complex Hair with Robust Collision Handling" (Choe, Choi, Ko, ACM SIGGRAPH 2005) + * http://graphics.snu.ac.kr/publications/2005-choe-HairSim/Choe_2005_SCA.pdf + */ + + v_nor_old = mag_v_rel; + v_nor_new = dot_v3v3(v_rel_new, collpair->normal); + + madd_v3_v3v3fl(v_tan_old, v_rel_old, collpair->normal, -v_nor_old); + madd_v3_v3v3fl(v_tan_new, v_rel_new, collpair->normal, -v_nor_new); + + bounce = -v_nor_old * restitution; + + repulse = -margin_distance / dt; /* base repulsion velocity in normal direction */ + /* XXX this clamping factor is quite arbitrary ... + * not sure if there is a more scientific approach, but seems to give good results + */ + CLAMP(repulse, 0.0f, 4.0f * bounce); + + if (margin_distance < -epsilon2) { + mul_v3_v3fl(r_impulse, collpair->normal, max_ff(repulse, bounce) - v_nor_new); + } + else { + bounce = 0.0f; + mul_v3_v3fl(r_impulse, collpair->normal, repulse - v_nor_new); + } + + result = true; + } + + return result; +} + +/* Init constraint matrix + * This is part of the modified CG method suggested by Baraff/Witkin in + * "Large Steps in Cloth Simulation" (Siggraph 1998) + */ +static void cloth_setup_constraints(ClothModifierData *clmd, ColliderContacts *contacts, int totcolliders, float dt) +{ + Cloth *cloth = clmd->clothObject; + Implicit_Data *data = cloth->implicit; + ClothVertex *vert; + int numverts = cloth->numverts; + int i, j, v; + + const float ZERO[3] = {0.0f, 0.0f, 0.0f}; + + BPH_mass_spring_clear_constraints(data); + + vert = cloth->verts; + for (v = 0; v < numverts; ++v, ++vert) { + if (vert->flags & CLOTH_VERT_FLAG_PINNED) { + /* pinned vertex constraints */ + BPH_mass_spring_add_constraint_ndof0(data, v, ZERO); /* velocity is defined externally */ + } + + vert->impulse_count = 0; + } + + for (i = 0; i < totcolliders; ++i) { + ColliderContacts *ct = &contacts[i]; + for (j = 0; j < ct->totcollisions; ++j) { + CollPair *collpair = &ct->collisions[j]; + int v = collpair->face1; + float impulse[3]; + +// float restitution = (1.0f - clmd->coll_parms->damping) * (1.0f - ct->ob->pd->pdef_sbdamp); + float restitution = 0.0f; + + vert = &cloth->verts[v]; + /* pinned verts handled separately */ + if (vert->flags & CLOTH_VERT_FLAG_PINNED) + continue; + + /* XXX cheap way of avoiding instability from multiple collisions in the same step + * this should eventually be supported ... + */ + if (vert->impulse_count > 0) + continue; + + /* calculate collision response */ + if (!collision_response(clmd, ct->collmd, collpair, dt, restitution, impulse)) + continue; + + BPH_mass_spring_add_constraint_ndof2(data, i, collpair->normal, impulse); + ++vert->impulse_count; + } + } +} + +/* computes where the cloth would be if it were subject to perfectly stiff edges + * (edge distance constraints) in a lagrangian solver. then add forces to help + * guide the implicit solver to that state. this function is called after + * collisions*/ +static int UNUSED_FUNCTION(cloth_calc_helper_forces)(Object *UNUSED(ob), ClothModifierData *clmd, float (*initial_cos)[3], float UNUSED(step), float dt) +{ + Cloth *cloth= clmd->clothObject; + float (*cos)[3] = (float (*)[3])MEM_callocN(sizeof(float)*3*cloth->numverts, "cos cloth_calc_helper_forces"); + float *masses = (float *)MEM_callocN(sizeof(float)*cloth->numverts, "cos cloth_calc_helper_forces"); + LinkNode *node; + ClothSpring *spring; + ClothVertex *vert; + int i, steps; + + vert = cloth->verts; + for (i=0; i<cloth->numverts; i++, vert++) { + copy_v3_v3(cos[i], vert->tx); + + if (vert->goal == 1.0f || len_squared_v3v3(initial_cos[i], vert->tx) != 0.0f) { + masses[i] = 1e+10; + } + else { + masses[i] = vert->mass; + } + } + + steps = 55; + for (i=0; i<steps; i++) { + for (node=cloth->springs; node; node=node->next) { + /* ClothVertex *cv1, *cv2; */ /* UNUSED */ + int v1, v2; + float len, c, l, vec[3]; + + spring = (ClothSpring *)node->link; + if (spring->type != CLOTH_SPRING_TYPE_STRUCTURAL && spring->type != CLOTH_SPRING_TYPE_SHEAR) + continue; + + v1 = spring->ij; v2 = spring->kl; + /* cv1 = cloth->verts + v1; */ /* UNUSED */ + /* cv2 = cloth->verts + v2; */ /* UNUSED */ + len = len_v3v3(cos[v1], cos[v2]); + + sub_v3_v3v3(vec, cos[v1], cos[v2]); + normalize_v3(vec); + + c = (len - spring->restlen); + if (c == 0.0f) + continue; + + l = c / ((1.0f / masses[v1]) + (1.0f / masses[v2])); + + mul_v3_fl(vec, -(1.0f / masses[v1]) * l); + add_v3_v3(cos[v1], vec); + + sub_v3_v3v3(vec, cos[v2], cos[v1]); + normalize_v3(vec); + + mul_v3_fl(vec, -(1.0f / masses[v2]) * l); + add_v3_v3(cos[v2], vec); + } + } + + vert = cloth->verts; + for (i=0; i<cloth->numverts; i++, vert++) { + float vec[3]; + + /*compute forces*/ + sub_v3_v3v3(vec, cos[i], vert->tx); + mul_v3_fl(vec, vert->mass*dt*20.0f); + add_v3_v3(vert->tv, vec); + //copy_v3_v3(cv->tx, cos[i]); + } + + MEM_freeN(cos); + MEM_freeN(masses); + + return 1; +} + +BLI_INLINE void cloth_calc_spring_force(ClothModifierData *clmd, ClothSpring *s, float time) +{ + Cloth *cloth = clmd->clothObject; + ClothSimSettings *parms = clmd->sim_parms; + Implicit_Data *data = cloth->implicit; + ClothVertex *verts = cloth->verts; + + bool no_compress = parms->flags & CLOTH_SIMSETTINGS_FLAG_NO_SPRING_COMPRESS; + + zero_v3(s->f); + zero_m3(s->dfdx); + zero_m3(s->dfdv); + + s->flags &= ~CLOTH_SPRING_FLAG_NEEDED; + + // calculate force of structural + shear springs + if ((s->type & CLOTH_SPRING_TYPE_STRUCTURAL) || (s->type & CLOTH_SPRING_TYPE_SHEAR) || (s->type & CLOTH_SPRING_TYPE_SEWING) ) { +#ifdef CLOTH_FORCE_SPRING_STRUCTURAL + float k, scaling; + + s->flags |= CLOTH_SPRING_FLAG_NEEDED; + + scaling = parms->structural + s->stiffness * fabsf(parms->max_struct - parms->structural); + k = scaling / (parms->avg_spring_len + FLT_EPSILON); + + if (s->type & CLOTH_SPRING_TYPE_SEWING) { + // TODO: verify, half verified (couldn't see error) + // sewing springs usually have a large distance at first so clamp the force so we don't get tunnelling through colission objects + BPH_mass_spring_force_spring_linear(data, s->ij, s->kl, s->restlen, k, parms->Cdis, no_compress, parms->max_sewing, s->f, s->dfdx, s->dfdv); + } + else { + BPH_mass_spring_force_spring_linear(data, s->ij, s->kl, s->restlen, k, parms->Cdis, no_compress, 0.0f, s->f, s->dfdx, s->dfdv); + } +#endif + } + else if (s->type & CLOTH_SPRING_TYPE_GOAL) { +#ifdef CLOTH_FORCE_SPRING_GOAL + float goal_x[3], goal_v[3]; + float k, scaling; + + s->flags |= CLOTH_SPRING_FLAG_NEEDED; + + // current_position = xold + t * (newposition - xold) + interp_v3_v3v3(goal_x, verts[s->ij].xold, verts[s->ij].xconst, time); + sub_v3_v3v3(goal_v, verts[s->ij].xconst, verts[s->ij].xold); // distance covered over dt==1 + + scaling = parms->goalspring + s->stiffness * fabsf(parms->max_struct - parms->goalspring); + k = verts[s->ij].goal * scaling / (parms->avg_spring_len + FLT_EPSILON); + + BPH_mass_spring_force_spring_goal(data, s->ij, goal_x, goal_v, k, parms->goalfrict * 0.01f, s->f, s->dfdx, s->dfdv); +#endif + } + else if (s->type & CLOTH_SPRING_TYPE_BENDING) { /* calculate force of bending springs */ +#ifdef CLOTH_FORCE_SPRING_BEND + float kb, cb, scaling; + + s->flags |= CLOTH_SPRING_FLAG_NEEDED; + + scaling = parms->bending + s->stiffness * fabsf(parms->max_bend - parms->bending); + kb = scaling / (20.0f * (parms->avg_spring_len + FLT_EPSILON)); + + scaling = parms->bending_damping; + cb = scaling / (20.0f * (parms->avg_spring_len + FLT_EPSILON)); + + BPH_mass_spring_force_spring_bending(data, s->ij, s->kl, s->restlen, kb, cb, s->f, s->dfdx, s->dfdv); +#endif + } + else if (s->type & CLOTH_SPRING_TYPE_BENDING_ANG) { +#ifdef CLOTH_FORCE_SPRING_BEND + float kb, cb, scaling; + + s->flags |= CLOTH_SPRING_FLAG_NEEDED; + + /* XXX WARNING: angular bending springs for hair apply stiffness factor as an overall factor, unlike cloth springs! + * this is crap, but needed due to cloth/hair mixing ... + * max_bend factor is not even used for hair, so ... + */ + scaling = s->stiffness * parms->bending; + kb = scaling / (20.0f * (parms->avg_spring_len + FLT_EPSILON)); + + scaling = parms->bending_damping; + cb = scaling / (20.0f * (parms->avg_spring_len + FLT_EPSILON)); + + /* XXX assuming same restlen for ij and jk segments here, this can be done correctly for hair later */ + BPH_mass_spring_force_spring_bending_angular(data, s->ij, s->kl, s->mn, s->target, kb, cb, NULL, NULL, NULL); + +#if 0 + { + float x_kl[3], x_mn[3], v[3], d[3]; + + BPH_mass_spring_get_motion_state(data, s->kl, x_kl, v); + BPH_mass_spring_get_motion_state(data, s->mn, x_mn, v); + + BKE_sim_debug_data_add_dot(clmd->debug_data, x_kl, 0.9, 0.9, 0.9, "target", 7980, s->kl); + BKE_sim_debug_data_add_line(clmd->debug_data, x_kl, x_mn, 0.8, 0.8, 0.8, "target", 7981, s->kl); + + copy_v3_v3(d, s->target); + BKE_sim_debug_data_add_vector(clmd->debug_data, x_kl, d, 0.8, 0.8, 0.2, "target", 7982, s->kl); + +// copy_v3_v3(d, s->target_ij); +// BKE_sim_debug_data_add_vector(clmd->debug_data, x, d, 1, 0.4, 0.4, "target", 7983, s->kl); + } +#endif +#endif + } +} + +static void hair_get_boundbox(ClothModifierData *clmd, float gmin[3], float gmax[3]) +{ + Cloth *cloth = clmd->clothObject; + Implicit_Data *data = cloth->implicit; + unsigned int numverts = cloth->numverts; + ClothVertex *vert; + int i; + + INIT_MINMAX(gmin, gmax); + vert = cloth->verts; + for (i = 0; i < numverts; i++, vert++) { + float x[3]; + BPH_mass_spring_get_motion_state(data, i, x, NULL); + DO_MINMAX(x, gmin, gmax); + } +} + +static void cloth_calc_force(ClothModifierData *clmd, float UNUSED(frame), ListBase *effectors, float time) +{ + /* Collect forces and derivatives: F, dFdX, dFdV */ + Cloth *cloth = clmd->clothObject; + Implicit_Data *data = cloth->implicit; + unsigned int i = 0; + float drag = clmd->sim_parms->Cvi * 0.01f; /* viscosity of air scaled in percent */ + float gravity[3] = {0.0f, 0.0f, 0.0f}; + MFace *mfaces = cloth->mfaces; + unsigned int numverts = cloth->numverts; + ClothVertex *vert; + +#ifdef CLOTH_FORCE_GRAVITY + /* global acceleration (gravitation) */ + if (clmd->scene->physics_settings.flag & PHYS_GLOBAL_GRAVITY) { + /* scale gravity force */ + mul_v3_v3fl(gravity, clmd->scene->physics_settings.gravity, 0.001f * clmd->sim_parms->effector_weights->global_gravity); + } + vert = cloth->verts; + for (i = 0; i < cloth->numverts; i++, vert++) { + BPH_mass_spring_force_gravity(data, i, vert->mass, gravity); + } +#endif + + /* cloth_calc_volume_force(clmd); */ + +#ifdef CLOTH_FORCE_DRAG + BPH_mass_spring_force_drag(data, drag); +#endif + + /* handle external forces like wind */ + if (effectors) { + /* cache per-vertex forces to avoid redundant calculation */ + float (*winvec)[3] = (float (*)[3])MEM_callocN(sizeof(float) * 3 * numverts, "effector forces"); + + vert = cloth->verts; + for (i = 0; i < cloth->numverts; i++, vert++) { + float x[3], v[3]; + EffectedPoint epoint; + + BPH_mass_spring_get_motion_state(data, i, x, v); + pd_point_from_loc(clmd->scene, x, v, i, &epoint); + pdDoEffectors(effectors, NULL, clmd->sim_parms->effector_weights, &epoint, winvec[i], NULL); + } + + for (i = 0; i < cloth->numfaces; i++) { + MFace *mf = &mfaces[i]; + BPH_mass_spring_force_face_wind(data, mf->v1, mf->v2, mf->v3, mf->v4, winvec); + } + + /* Hair has only edges */ + if (cloth->numfaces == 0) { + const float density = 0.01f; /* XXX arbitrary value, corresponds to effect of air density */ +#if 0 + ClothHairData *hairdata = clmd->hairdata; + ClothHairData *hair_ij, *hair_kl; + + for (LinkNode *link = cloth->springs; link; link = link->next) { + ClothSpring *spring = (ClothSpring *)link->link; + if (spring->type == CLOTH_SPRING_TYPE_STRUCTURAL) { + ClothVertex *verts = cloth->verts; + int si_ij = verts[spring->ij].solver_index; + int si_kl = verts[spring->kl].solver_index; + + if (si_ij < 0 || si_kl < 0) + continue; + + if (hairdata) { + hair_ij = &hairdata[spring->ij]; + hair_kl = &hairdata[spring->kl]; + BPH_mass_spring_force_edge_wind(data, si_ij, si_kl, hair_ij->radius, hair_kl->radius, winvec); + } + else + BPH_mass_spring_force_edge_wind(data, si_ij, si_kl, 1.0f, 1.0f, winvec); + } + } +#else + ClothHairData *hairdata = clmd->hairdata; + + vert = cloth->verts; + for (i = 0; i < cloth->numverts; i++, vert++) { + if (hairdata) { + ClothHairData *hair = &hairdata[i]; + BPH_mass_spring_force_vertex_wind(data, i, hair->radius * density, winvec); + } + else + BPH_mass_spring_force_vertex_wind(data, i, density, winvec); + } +#endif + } + + MEM_freeN(winvec); + } + + // calculate spring forces + for (LinkNode *link = cloth->springs; link; link = link->next) { + ClothSpring *spring = (ClothSpring *)link->link; + // only handle active springs + if (!(spring->flags & CLOTH_SPRING_FLAG_DEACTIVATE)) + cloth_calc_spring_force(clmd, spring, time); + } +} + +/* returns vertexes' motion state */ +BLI_INLINE void cloth_get_grid_location(Implicit_Data *data, float cell_scale, const float cell_offset[3], + int index, float x[3], float v[3]) +{ + BPH_mass_spring_get_position(data, index, x); + BPH_mass_spring_get_new_velocity(data, index, v); + + mul_v3_fl(x, cell_scale); + add_v3_v3(x, cell_offset); +} + +/* returns next spring forming a continous hair sequence */ +BLI_INLINE LinkNode *hair_spring_next(LinkNode *spring_link) +{ + ClothSpring *spring = (ClothSpring *)spring_link->link; + LinkNode *next = spring_link->next; + if (next) { + ClothSpring *next_spring = (ClothSpring *)next->link; + if (next_spring->type == CLOTH_SPRING_TYPE_STRUCTURAL && next_spring->kl == spring->ij) + return next; + } + return NULL; +} + +/* XXX this is nasty: cloth meshes do not explicitly store + * the order of hair segments! + * We have to rely on the spring build function for now, + * which adds structural springs in reverse order: + * (3,4), (2,3), (1,2) + * This is currently the only way to figure out hair geometry inside this code ... + */ +static LinkNode *cloth_continuum_add_hair_segments(HairGrid *grid, const float cell_scale, const float cell_offset[3], Cloth *cloth, LinkNode *spring_link) +{ + Implicit_Data *data = cloth->implicit; + LinkNode *next_spring_link = NULL; /* return value */ + ClothSpring *spring1, *spring2, *spring3; + float x1[3], v1[3], x2[3], v2[3], x3[3], v3[3], x4[3], v4[3]; + float dir1[3], dir2[3], dir3[3]; + + spring1 = NULL; + spring2 = NULL; + spring3 = (ClothSpring *)spring_link->link; + + zero_v3(x1); zero_v3(v1); + zero_v3(dir1); + zero_v3(x2); zero_v3(v2); + zero_v3(dir2); + + cloth_get_grid_location(data, cell_scale, cell_offset, spring3->kl, x3, v3); + cloth_get_grid_location(data, cell_scale, cell_offset, spring3->ij, x4, v4); + sub_v3_v3v3(dir3, x4, x3); + normalize_v3(dir3); + + while (spring_link) { + /* move on */ + spring1 = spring2; + spring2 = spring3; + + copy_v3_v3(x1, x2); copy_v3_v3(v1, v2); + copy_v3_v3(x2, x3); copy_v3_v3(v2, v3); + copy_v3_v3(x3, x4); copy_v3_v3(v3, v4); + + copy_v3_v3(dir1, dir2); + copy_v3_v3(dir2, dir3); + + /* read next segment */ + next_spring_link = spring_link->next; + spring_link = hair_spring_next(spring_link); + + if (spring_link) { + spring3 = (ClothSpring *)spring_link->link; + cloth_get_grid_location(data, cell_scale, cell_offset, spring3->ij, x4, v4); + sub_v3_v3v3(dir3, x4, x3); + normalize_v3(dir3); + } + else { + spring3 = NULL; + zero_v3(x4); zero_v3(v4); + zero_v3(dir3); + } + + BPH_hair_volume_add_segment(grid, x1, v1, x2, v2, x3, v3, x4, v4, + spring1 ? dir1 : NULL, + dir2, + spring3 ? dir3 : NULL); + } + + return next_spring_link; +} + +static void cloth_continuum_fill_grid(HairGrid *grid, Cloth *cloth) +{ +#if 0 + Implicit_Data *data = cloth->implicit; + int numverts = cloth->numverts; + ClothVertex *vert; + int i; + + for (i = 0, vert = cloth->verts; i < numverts; i++, vert++) { + float x[3], v[3]; + + cloth_get_vertex_motion_state(data, vert, x, v); + BPH_hair_volume_add_vertex(grid, x, v); + } +#else + LinkNode *link; + float cellsize, gmin[3], cell_scale, cell_offset[3]; + + /* scale and offset for transforming vertex locations into grid space + * (cell size is 0..1, gmin becomes origin) + */ + BPH_hair_volume_grid_geometry(grid, &cellsize, NULL, gmin, NULL); + cell_scale = cellsize > 0.0f ? 1.0f / cellsize : 0.0f; + mul_v3_v3fl(cell_offset, gmin, cell_scale); + negate_v3(cell_offset); + + link = cloth->springs; + while (link) { + ClothSpring *spring = (ClothSpring *)link->link; + if (spring->type == CLOTH_SPRING_TYPE_STRUCTURAL) + link = cloth_continuum_add_hair_segments(grid, cell_scale, cell_offset, cloth, link); + else + link = link->next; + } +#endif + BPH_hair_volume_normalize_vertex_grid(grid); +} + +static void cloth_continuum_step(ClothModifierData *clmd, float dt) +{ + ClothSimSettings *parms = clmd->sim_parms; + Cloth *cloth = clmd->clothObject; + Implicit_Data *data = cloth->implicit; + int numverts = cloth->numverts; + ClothVertex *vert; + + const float fluid_factor = 0.95f; /* blend between PIC and FLIP methods */ + float smoothfac = parms->velocity_smooth; + /* XXX FIXME arbitrary factor!!! this should be based on some intuitive value instead, + * like number of hairs per cell and time decay instead of "strength" + */ + float density_target = parms->density_target; + float density_strength = parms->density_strength; + float gmin[3], gmax[3]; + int i; + + /* clear grid info */ + zero_v3_int(clmd->hair_grid_res); + zero_v3(clmd->hair_grid_min); + zero_v3(clmd->hair_grid_max); + clmd->hair_grid_cellsize = 0.0f; + + hair_get_boundbox(clmd, gmin, gmax); + + /* gather velocities & density */ + if (smoothfac > 0.0f || density_strength > 0.0f) { + HairGrid *grid = BPH_hair_volume_create_vertex_grid(clmd->sim_parms->voxel_cell_size, gmin, gmax); + + cloth_continuum_fill_grid(grid, cloth); + + /* main hair continuum solver */ + BPH_hair_volume_solve_divergence(grid, dt, density_target, density_strength); + + for (i = 0, vert = cloth->verts; i < numverts; i++, vert++) { + float x[3], v[3], nv[3]; + + /* calculate volumetric velocity influence */ + BPH_mass_spring_get_position(data, i, x); + BPH_mass_spring_get_new_velocity(data, i, v); + + BPH_hair_volume_grid_velocity(grid, x, v, fluid_factor, nv); + + interp_v3_v3v3(nv, v, nv, smoothfac); + + /* apply on hair data */ + BPH_mass_spring_set_new_velocity(data, i, nv); + } + + /* store basic grid info in the modifier data */ + BPH_hair_volume_grid_geometry(grid, &clmd->hair_grid_cellsize, clmd->hair_grid_res, clmd->hair_grid_min, clmd->hair_grid_max); + +#if 0 /* DEBUG hair velocity vector field */ + { + const int size = 64; + int i, j; + float offset[3], a[3], b[3]; + const int axis = 0; + const float shift = 0.0f; + + copy_v3_v3(offset, clmd->hair_grid_min); + zero_v3(a); + zero_v3(b); + + offset[axis] = shift * clmd->hair_grid_cellsize; + a[(axis+1) % 3] = clmd->hair_grid_max[(axis+1) % 3] - clmd->hair_grid_min[(axis+1) % 3]; + b[(axis+2) % 3] = clmd->hair_grid_max[(axis+2) % 3] - clmd->hair_grid_min[(axis+2) % 3]; + + BKE_sim_debug_data_clear_category(clmd->debug_data, "grid velocity"); + for (j = 0; j < size; ++j) { + for (i = 0; i < size; ++i) { + float x[3], v[3], gvel[3], gvel_smooth[3], gdensity; + + madd_v3_v3v3fl(x, offset, a, (float)i / (float)(size-1)); + madd_v3_v3fl(x, b, (float)j / (float)(size-1)); + zero_v3(v); + + BPH_hair_volume_grid_interpolate(grid, x, &gdensity, gvel, gvel_smooth, NULL, NULL); + +// BKE_sim_debug_data_add_circle(clmd->debug_data, x, gdensity, 0.7, 0.3, 1, "grid density", i, j, 3111); + if (!is_zero_v3(gvel) || !is_zero_v3(gvel_smooth)) { + float dvel[3]; + sub_v3_v3v3(dvel, gvel_smooth, gvel); +// BKE_sim_debug_data_add_vector(clmd->debug_data, x, gvel, 0.4, 0, 1, "grid velocity", i, j, 3112); +// BKE_sim_debug_data_add_vector(clmd->debug_data, x, gvel_smooth, 0.6, 1, 1, "grid velocity", i, j, 3113); + BKE_sim_debug_data_add_vector(clmd->debug_data, x, dvel, 0.4, 1, 0.7, "grid velocity", i, j, 3114); +#if 0 + if (gdensity > 0.0f) { + float col0[3] = {0.0, 0.0, 0.0}; + float col1[3] = {0.0, 1.0, 0.0}; + float col[3]; + + interp_v3_v3v3(col, col0, col1, CLAMPIS(gdensity * clmd->sim_parms->density_strength, 0.0, 1.0)); +// BKE_sim_debug_data_add_circle(clmd->debug_data, x, gdensity * clmd->sim_parms->density_strength, 0, 1, 0.4, "grid velocity", i, j, 3115); +// BKE_sim_debug_data_add_dot(clmd->debug_data, x, col[0], col[1], col[2], "grid velocity", i, j, 3115); + BKE_sim_debug_data_add_circle(clmd->debug_data, x, 0.01f, col[0], col[1], col[2], "grid velocity", i, j, 3115); + } +#endif + } + } + } + } +#endif + + BPH_hair_volume_free_vertex_grid(grid); + } +} + +/* old collision stuff for cloth, use for continuity + * until a good replacement is ready + */ +static void cloth_collision_solve_extra(Object *ob, ClothModifierData *clmd, ListBase *effectors, float frame, float step, float dt) +{ + Cloth *cloth = clmd->clothObject; + Implicit_Data *id = cloth->implicit; + ClothVertex *verts = cloth->verts; + int numverts = cloth->numverts; + const float spf = (float)clmd->sim_parms->stepsPerFrame / clmd->sim_parms->timescale;; + + bool do_extra_solve; + int i; + + if (!(clmd->coll_parms->flags & CLOTH_COLLSETTINGS_FLAG_ENABLED)) + return; + if (!clmd->clothObject->bvhtree) + return; + + // update verts to current positions + for (i = 0; i < numverts; i++) { + BPH_mass_spring_get_new_position(id, i, verts[i].tx); + + sub_v3_v3v3(verts[i].tv, verts[i].tx, verts[i].txold); + copy_v3_v3(verts[i].v, verts[i].tv); + } + +#if 0 /* unused */ + for (i=0, cv=cloth->verts; i<cloth->numverts; i++, cv++) { + copy_v3_v3(initial_cos[i], cv->tx); + } +#endif + + // call collision function + // TODO: check if "step" or "step+dt" is correct - dg + do_extra_solve = cloth_bvh_objcollision(ob, clmd, step / clmd->sim_parms->timescale, dt / clmd->sim_parms->timescale); + + // copy corrected positions back to simulation + for (i = 0; i < numverts; i++) { + float curx[3]; + BPH_mass_spring_get_position(id, i, curx); + // correct velocity again, just to be sure we had to change it due to adaptive collisions + sub_v3_v3v3(verts[i].tv, verts[i].tx, curx); + } + + if (do_extra_solve) { +// cloth_calc_helper_forces(ob, clmd, initial_cos, step/clmd->sim_parms->timescale, dt/clmd->sim_parms->timescale); + + for (i = 0; i < numverts; i++) { + + float newv[3]; + + if ((clmd->sim_parms->flags & CLOTH_SIMSETTINGS_FLAG_GOAL) && (verts [i].flags & CLOTH_VERT_FLAG_PINNED)) + continue; + + BPH_mass_spring_set_new_position(id, i, verts[i].tx); + mul_v3_v3fl(newv, verts[i].tv, spf); + BPH_mass_spring_set_new_velocity(id, i, newv); + } + } + + // X = Xnew; + BPH_mass_spring_apply_result(id); + + if (do_extra_solve) { + ImplicitSolverResult result; + + /* initialize forces to zero */ + BPH_mass_spring_clear_forces(id); + + // calculate forces + cloth_calc_force(clmd, frame, effectors, step); + + // calculate new velocity and position + BPH_mass_spring_solve_velocities(id, dt, &result); +// cloth_record_result(clmd, &result, clmd->sim_parms->stepsPerFrame); + + /* note: positions are advanced only once in the main solver step! */ + + BPH_mass_spring_apply_result(id); + } +} + +static void cloth_clear_result(ClothModifierData *clmd) +{ + ClothSolverResult *sres = clmd->solver_result; + + sres->status = 0; + sres->max_error = sres->min_error = sres->avg_error = 0.0f; + sres->max_iterations = sres->min_iterations = 0; + sres->avg_iterations = 0.0f; +} + +static void cloth_record_result(ClothModifierData *clmd, ImplicitSolverResult *result, int steps) +{ + ClothSolverResult *sres = clmd->solver_result; + + if (sres->status) { /* already initialized ? */ + /* error only makes sense for successful iterations */ + if (result->status == BPH_SOLVER_SUCCESS) { + sres->min_error = min_ff(sres->min_error, result->error); + sres->max_error = max_ff(sres->max_error, result->error); + sres->avg_error += result->error / (float)steps; + } + + sres->min_iterations = min_ii(sres->min_iterations, result->iterations); + sres->max_iterations = max_ii(sres->max_iterations, result->iterations); + sres->avg_iterations += (float)result->iterations / (float)steps; + } + else { + /* error only makes sense for successful iterations */ + if (result->status == BPH_SOLVER_SUCCESS) { + sres->min_error = sres->max_error = result->error; + sres->avg_error += result->error / (float)steps; + } + + sres->min_iterations = sres->max_iterations = result->iterations; + sres->avg_iterations += (float)result->iterations / (float)steps; + } + + sres->status |= result->status; +} + +int BPH_cloth_solve(Object *ob, float frame, ClothModifierData *clmd, ListBase *effectors) +{ + /* Hair currently is a cloth sim in disguise ... + * Collision detection and volumetrics work differently then. + * Bad design, TODO + */ + const bool is_hair = (clmd->hairdata != NULL); + + unsigned int i=0; + float step=0.0f, tf=clmd->sim_parms->timescale; + Cloth *cloth = clmd->clothObject; + ClothVertex *verts = cloth->verts, *vert; + unsigned int numverts = cloth->numverts; + float dt = clmd->sim_parms->timescale / clmd->sim_parms->stepsPerFrame; + Implicit_Data *id = cloth->implicit; + ColliderContacts *contacts = NULL; + int totcolliders = 0; + + BKE_sim_debug_data_clear_category("collision"); + + if (!clmd->solver_result) + clmd->solver_result = (ClothSolverResult *)MEM_callocN(sizeof(ClothSolverResult), "cloth solver result"); + cloth_clear_result(clmd); + + if (clmd->sim_parms->flags & CLOTH_SIMSETTINGS_FLAG_GOAL) { /* do goal stuff */ + vert = verts; + for (i = 0; i < numverts; i++, vert++) { + // update velocities with constrained velocities from pinned verts + if (vert->flags & CLOTH_VERT_FLAG_PINNED) { + float v[3]; + + sub_v3_v3v3(v, verts[i].xconst, verts[i].xold); + // mul_v3_fl(v, clmd->sim_parms->stepsPerFrame); + BPH_mass_spring_set_velocity(id, i, v); + } + } + } + + while (step < tf) { + ImplicitSolverResult result; + + /* copy velocities for collision */ + vert = verts; + for (i = 0; i < numverts; i++, vert++) { + BPH_mass_spring_get_motion_state(id, i, NULL, verts[i].tv); + copy_v3_v3(verts[i].v, verts[i].tv); + } + + if (is_hair) { + /* determine contact points */ + if (clmd->coll_parms->flags & CLOTH_COLLSETTINGS_FLAG_ENABLED) { + cloth_find_point_contacts(ob, clmd, 0.0f, tf, &contacts, &totcolliders); + } + + /* setup vertex constraints for pinned vertices and contacts */ + cloth_setup_constraints(clmd, contacts, totcolliders, dt); + } + else { + /* setup vertex constraints for pinned vertices */ + cloth_setup_constraints(clmd, NULL, 0, dt); + } + + /* initialize forces to zero */ + BPH_mass_spring_clear_forces(id); + + // damping velocity for artistic reasons + // this is a bad way to do it, should be removed imo - lukas_t + if (clmd->sim_parms->vel_damping != 1.0f) { + vert = verts; + for (i = 0; i < numverts; i++, vert++) { + float v[3]; + BPH_mass_spring_get_motion_state(id, i, NULL, v); + mul_v3_fl(v, clmd->sim_parms->vel_damping); + BPH_mass_spring_set_velocity(id, i, v); + } + } + + // calculate forces + cloth_calc_force(clmd, frame, effectors, step); + + // calculate new velocity and position + BPH_mass_spring_solve_velocities(id, dt, &result); + cloth_record_result(clmd, &result, clmd->sim_parms->stepsPerFrame); + + if (is_hair) { + cloth_continuum_step(clmd, dt); + } + + BPH_mass_spring_solve_positions(id, dt); + + if (!is_hair) { + cloth_collision_solve_extra(ob, clmd, effectors, frame, step, dt); + } + + BPH_mass_spring_apply_result(id); + + /* move pinned verts to correct position */ + vert = verts; + for (i = 0; i < numverts; i++, vert++) { + if (clmd->sim_parms->flags & CLOTH_SIMSETTINGS_FLAG_GOAL) { + if (vert->flags & CLOTH_VERT_FLAG_PINNED) { + float x[3]; + interp_v3_v3v3(x, verts[i].xold, verts[i].xconst, step + dt); + BPH_mass_spring_set_position(id, i, x); + } + } + + BPH_mass_spring_get_motion_state(id, i, verts[i].txold, NULL); + } + + /* free contact points */ + if (contacts) { + cloth_free_contacts(contacts, totcolliders); + } + + step += dt; + } + + /* copy results back to cloth data */ + vert = verts; + for (i = 0; i < numverts; i++, vert++) { + BPH_mass_spring_get_motion_state(id, i, vert->x, vert->v); + copy_v3_v3(vert->txold, vert->x); + } + + return 1; +} + +bool BPH_cloth_solver_get_texture_data(Object *UNUSED(ob), ClothModifierData *clmd, VoxelData *vd) +{ + Cloth *cloth = clmd->clothObject; + HairGrid *grid; + float gmin[3], gmax[3]; + + if (!clmd->clothObject || !clmd->clothObject->implicit) + return false; + + hair_get_boundbox(clmd, gmin, gmax); + + grid = BPH_hair_volume_create_vertex_grid(clmd->sim_parms->voxel_cell_size, gmin, gmax); + cloth_continuum_fill_grid(grid, cloth); + + BPH_hair_volume_get_texture_data(grid, vd); + + BPH_hair_volume_free_vertex_grid(grid); + + return true; +} + +/* ========================================================================= */ + +struct Implicit_Data *BPH_strands_solver_create(struct Strands *strands, struct HairSimParams *params) +{ + static float I3[3][3] = { {1.0f, 0.0f, 0.0f}, {0.0f, 1.0f, 0.0f}, {0.0f, 0.0f, 1.0f} }; + + struct Implicit_Data *id; + int numverts = strands->totverts; + int numcurves = strands->totcurves; + int numgoalverts = numverts - numcurves; /* extra virtual vertices to act as goal spring targets */ + int numedges = max_ii(numverts - numcurves, 0); + int numbends = max_ii(numverts - 2*numcurves, 0); + + /* goal springs: 1 per vertex, except roots + * stretch springs: 1 per edge + * bending sprints: 3 per bend // XXX outdated, 1 is enough + */ + int numsprings = numgoalverts + numedges + 3*numbends; + int i; + + id = BPH_mass_spring_solver_create(numverts + numgoalverts, numsprings); + + for (i = 0; i < numverts; i++) { + float mass = params->mass; + BPH_mass_spring_set_vertex_mass(id, i, mass); + } + for (i = numverts; i < numverts + numgoalverts; i++) { + BPH_mass_spring_set_vertex_mass(id, i, 1.0f); + } + + for (i = 0; i < numverts; i++) { + BPH_mass_spring_set_rest_transform(id, i, I3); + } + for (i = numverts; i < numverts + numgoalverts; i++) { + BPH_mass_spring_set_rest_transform(id, i, I3); + } + + return id; +} + +/* Init constraint matrix + * This is part of the modified CG method suggested by Baraff/Witkin in + * "Large Steps in Cloth Simulation" (Siggraph 1998) + */ +static void strands_setup_constraints(Strands *strands, Implicit_Data *data, ColliderContacts *UNUSED(contacts), int UNUSED(totcolliders), float UNUSED(dt)) +{ + static const float ZERO[3] = { 0.0f, 0.0f, 0.0f }; + + BPH_mass_spring_clear_constraints(data); + + StrandIterator it_strand; + for (BKE_strand_iter_init(&it_strand, strands); BKE_strand_iter_valid(&it_strand); BKE_strand_iter_next(&it_strand)) { + int index = BKE_strand_iter_vertex_offset(strands, &it_strand); + + /* pin strand roots */ + BPH_mass_spring_add_constraint_ndof0(data, index, ZERO); /* velocity is defined externally */ + +// StrandVertexIterator it_vert; +// for (BKE_strand_vertex_iter_init(&it_vert, &it_strand); BKE_strand_vertex_iter_valid(&it_vert); BKE_strand_vertex_iter_next(&it_vert)) { +// } + } + + /* pin virtual goal targets */ + { + int goalstart = strands->totverts; + int goalend = goalstart + strands->totverts - strands->totcurves; + for (int i = goalstart; i < goalend; ++i) { + BPH_mass_spring_add_constraint_ndof0(data, i, ZERO); /* velocity is defined externally */ + } + } + +#if 0 + for (i = 0; i < totcolliders; ++i) { + ColliderContacts *ct = &contacts[i]; + for (j = 0; j < ct->totcollisions; ++j) { + CollPair *collpair = &ct->collisions[j]; +// float restitution = (1.0f - clmd->coll_parms->damping) * (1.0f - ct->ob->pd->pdef_sbdamp); + float restitution = 0.0f; + int v = collpair->face1; + float impulse[3]; + + /* pinned verts handled separately */ + if (verts[v].flags & CLOTH_VERT_FLAG_PINNED) + continue; + + /* XXX cheap way of avoiding instability from multiple collisions in the same step + * this should eventually be supported ... + */ + if (verts[v].impulse_count > 0) + continue; + + /* calculate collision response */ + if (!collision_response(clmd, ct->collmd, collpair, dt, restitution, impulse)) + continue; + + BPH_mass_spring_add_constraint_ndof2(data, v, collpair->normal, impulse); + ++verts[v].impulse_count; + } + } +#endif +} + +/* stretch forces are created between 2 vertices of each segment */ +static void strands_calc_curve_stretch_forces(Strands *strands, float UNUSED(space[4][4]), HairSimParams *params, Implicit_Data *data, StrandIterator *it_strand) +{ + StrandEdgeIterator it_edge; + + for (BKE_strand_edge_iter_init(&it_edge, it_strand); BKE_strand_edge_iter_valid(&it_edge); BKE_strand_edge_iter_next(&it_edge)) { + int vi = BKE_strand_edge_iter_vertex0_offset(strands, &it_edge); + int vj = BKE_strand_edge_iter_vertex1_offset(strands, &it_edge); + float restlen = len_v3v3(it_edge.vertex0->co, it_edge.vertex1->co); + + float stiffness = params->stretch_stiffness; + float damping = stiffness * params->stretch_damping; + float f[3]; + BPH_mass_spring_force_spring_linear(data, vi, vj, restlen, stiffness, damping, true, 0.0f, f, NULL, NULL); + } +} + +static float strands_bending_stiffness(Strands *UNUSED(strands), HairSimParams *params, StrandsVertex *UNUSED(vert), float t) +{ + float weight; + + if (params->flag & eHairSimParams_Flag_UseBendStiffnessCurve) + weight = curvemapping_evaluateF(params->bend_stiffness_mapping, 0, t); + else + weight = 1.0f; + CLAMP(weight, 0.0f, 1.0f); + + return params->bend_stiffness * weight; +} + +/* bending forces aim to restore the rest shape of each strand locally */ +static void strands_calc_curve_bending_forces(Strands *strands, float space[4][4], HairSimParams *params, Implicit_Data *data, StrandIterator *it_strand) +{ + StrandBendIterator it_bend; + + float length = 0.0f; + StrandEdgeIterator it_edge; + for (BKE_strand_edge_iter_init(&it_edge, it_strand); BKE_strand_edge_iter_valid(&it_edge); BKE_strand_edge_iter_next(&it_edge)) + length += len_v3v3(it_edge.vertex1->co, it_edge.vertex0->co); + float length_inv = length > 0.0f ? 1.0f / length : 0.0f; + + float t = 0.0f; + BKE_strand_bend_iter_init(&it_bend, it_strand); + if (!BKE_strand_bend_iter_valid(&it_bend)) + return; + + /* The 'mat' matrix (here: A) contains the relative transform between the local rest and motion state coordinate systems. + * In the beginning both systems are the root matrix R, so the relative transform is the unit matrix. + * + * A = M_state * M_rest^T + * = R * R^T + * = I + * + * With each bend the matrices are rotated along the curvature, described by matrix B^T. Since we are only + * interested in the combined transform however, the resulting operation becomes + * + * A' = M_state' * M_rest' + * = (B_state^T * M_state) * (B_rest^T * M_rest)^T + * = B_state^T * M_state * M_rest^T * B_rest + * = B_state^T * A * B_rest + * + * The target vector is originally the direction of the first segment. For each bend, the target vector + * is the _previous_ segment's direction, i.e. the target vector is rotated by B with a 1-step delay. + * + * The target vector in the current motion state system for each segment could thus be calculated by multiplying + * + * t_state = M * t_rest + * + * but using the edge vector directly is more practical. + * + */ + float mat[3][3]; +// float Mrest[3][3], Mstate[3][3]; + + { /* initialize using the first edge deviation from the rest direction */ + float edge_rest[3], edge_state[3], rot[3][3]; + sub_v3_v3v3(edge_rest, it_strand->verts[1].co, it_strand->verts[0].co); + sub_v3_v3v3(edge_state, it_strand->state[1].co, it_strand->state[0].co); + normalize_v3(edge_rest); + normalize_v3(edge_state); + rotation_between_vecs_to_mat3(rot, edge_rest, edge_state); + + copy_m3_m3(mat, rot); +// copy_m3_m3(Mrest, it_strand->curve->root_matrix); +// mul_m3_m3m3(Mstate, rot, Mrest); + } + + { /* apply force */ + /* Note: applying forces to the first segment is necessary to equalize forces on the root, + * otherwise energy gets introduced at the root and can destabilize the simulation. + */ + float target[3]; + sub_v3_v3v3(target, it_strand->verts[1].co, it_strand->verts[0].co); + mul_mat3_m4_v3(space, target); /* to solver space (world space) */ + + float target_state[3]; + mul_v3_m3v3(target_state, mat, target); + + const float stiffness = strands_bending_stiffness(strands, params, it_bend.vertex0, t * length_inv); + const float damping = stiffness * params->bend_damping; + + int vroot = BKE_strand_bend_iter_vertex0_offset(strands, &it_bend); /* root velocity used as goal velocity */ + int vj = BKE_strand_bend_iter_vertex1_offset(strands, &it_bend); + float goal[3], rootvel[3]; + mul_v3_m4v3(goal, space, it_strand->verts[1].co); + BPH_mass_spring_get_velocity(data, vroot, rootvel); + BPH_mass_spring_force_spring_goal(data, vj, goal, rootvel, stiffness, damping, NULL, NULL, NULL); + } + + do { + float restlen = len_v3v3(it_bend.vertex1->co, it_bend.vertex0->co); + t += restlen; + + { /* advance the coordinate frame */ + float rotrest[3][3], rotrest_inv[3][3], rotstate[3][3], rotstate_inv[3][3]; + BKE_strand_bend_iter_transform_rest(&it_bend, rotrest); + BKE_strand_bend_iter_transform_state(&it_bend, rotstate); + transpose_m3_m3(rotrest_inv, rotrest); + transpose_m3_m3(rotstate_inv, rotstate); + +// mul_m3_m3m3(Mrest, rotrest_inv, Mrest); +// mul_m3_m3m3(Mstate, rotstate_inv, Mstate); + + mul_m3_m3m3(mat, mat, rotrest); + mul_m3_m3m3(mat, rotstate_inv, mat); + } + + { /* apply force */ + float target[3]; + sub_v3_v3v3(target, it_bend.vertex1->co, it_bend.vertex0->co); + mul_mat3_m4_v3(space, target); /* to solver space (world space) */ + + float target_state[3]; + mul_v3_m3v3(target_state, mat, target); + + const float stiffness = strands_bending_stiffness(strands, params, it_bend.vertex1, t * length_inv); + const float damping = stiffness * params->bend_damping; + + int vi = BKE_strand_bend_iter_vertex0_offset(strands, &it_bend); + int vj = BKE_strand_bend_iter_vertex1_offset(strands, &it_bend); + int vk = BKE_strand_bend_iter_vertex2_offset(strands, &it_bend); + float f[3]; + BPH_mass_spring_force_spring_bending_angular(data, vi, vj, vk, target_state, stiffness, damping, f, NULL, NULL); + +#if 0 /* debug */ + { + float mscale = 0.1f; + + float x[3]; + BPH_mass_spring_get_position(data, vj, x); + BKE_sim_debug_data_add_vector(x, target, 0,0,1, "hairsim", 2598, vi, vj, vk); + BKE_sim_debug_data_add_vector(x, target_state, 0.4,0.4,1, "hairsim", 2599, vi, vj, vk); + + float mr[3][3]; + copy_m3_m3(mr, Mrest); + mul_m3_fl(mr, mscale); + BKE_sim_debug_data_add_vector(x, mr[0], 0.7,0.0,0.0, "hairsim", 1957, vi, vj, vk); + BKE_sim_debug_data_add_vector(x, mr[1], 0.0,0.7,0.0, "hairsim", 1958, vi, vj, vk); + BKE_sim_debug_data_add_vector(x, mr[2], 0.0,0.0,0.7, "hairsim", 1959, vi, vj, vk); + + float ms[3][3]; + copy_m3_m3(ms, Mstate); + mul_m3_fl(ms, mscale); + BKE_sim_debug_data_add_vector(x, ms[0], 1.0,0.4,0.4, "hairsim", 1857, vi, vj, vk); + BKE_sim_debug_data_add_vector(x, ms[1], 0.4,1.0,0.4, "hairsim", 1858, vi, vj, vk); + BKE_sim_debug_data_add_vector(x, ms[2], 0.4,0.4,1.0, "hairsim", 1859, vi, vj, vk); + } +#endif + } + + BKE_strand_bend_iter_next(&it_bend); + } while (BKE_strand_bend_iter_valid(&it_bend)); +} + +static float strands_goal_stiffness(Strands *UNUSED(strands), HairSimParams *params, StrandsVertex *vert, float t) +{ + /* XXX There is no possibility of tweaking them in linked data currently, + * so the original workflow of painting weights in particle edit mode is virtually useless. + */ + float weight; + + if (params->flag & eHairSimParams_Flag_UseGoalStiffnessCurve) + weight = curvemapping_evaluateF(params->goal_stiffness_mapping, 0, t); + else + weight = vert->weight; + CLAMP(weight, 0.0f, 1.0f); + + return params->goal_stiffness * weight; +} + +static bool strands_deflector_filter(void *UNUSED(data), CacheEffector *eff) +{ + return eff->type == eCacheEffector_Type_Deflect; +} + +static bool strands_test_deflector(StrandsVertex *UNUSED(vertex), int index, CacheEffector *cache_effectors, int tot_cache_effectors, Implicit_Data *data) +{ + CacheEffectorPoint point; + point.index = index; + BPH_mass_spring_get_motion_state(data, index, point.x, point.v); + + CacheEffectorResult result; + if (BKE_cache_effectors_eval_ex(cache_effectors, tot_cache_effectors, &point, &result, strands_deflector_filter, NULL) > 0) { + return true; + } + + return false; +} + +/* goal forces pull vertices toward their rest position */ +static void strands_calc_vertex_goal_forces(Strands *strands, float UNUSED(space[4][4]), HairSimParams *params, CacheEffector *cache_effectors, int tot_cache_effectors, + Implicit_Data *data, StrandIterator *it_strand) +{ + const int goalstart = strands->totverts; + StrandEdgeIterator it_edge; + + float length = 0.0f; + for (BKE_strand_edge_iter_init(&it_edge, it_strand); BKE_strand_edge_iter_valid(&it_edge); BKE_strand_edge_iter_next(&it_edge)) + length += len_v3v3(it_edge.vertex1->co, it_edge.vertex0->co); + float length_inv = length > 0.0f ? 1.0f / length : 0.0f; + + float t = 0.0f; + for (BKE_strand_edge_iter_init(&it_edge, it_strand); BKE_strand_edge_iter_valid(&it_edge); BKE_strand_edge_iter_next(&it_edge)) { + int vj = BKE_strand_edge_iter_vertex1_offset(strands, &it_edge); + int numroots = it_strand->index + 1; /* roots don't have goal verts, skip this many */ + int goalj = goalstart + vj - numroots; + + if (params->flag & eHairSimParams_Flag_UseGoalDeflect) { + if (strands_test_deflector(it_edge.vertex1, vj, cache_effectors, tot_cache_effectors, data)) + continue; + } + + float restlen = len_v3v3(it_edge.vertex1->co, it_edge.vertex0->co); + t += restlen; + + float stiffness = strands_goal_stiffness(strands, params, it_edge.vertex1, t * length_inv); + float damping = stiffness * params->goal_damping; + + float f[3]; + BPH_mass_spring_force_spring_linear(data, vj, goalj, 0.0f, stiffness, damping, true, 0.0f, f, NULL, NULL); + } + +#if 0 + StrandEdgeIterator it_edge; + + float rootvel[3]; + BPH_mass_spring_get_velocity(data, BKE_strand_iter_vertex_offset(strands, it_strand), rootvel); + + float length = 0.0f; + for (BKE_strand_edge_iter_init(&it_edge, it_strand); BKE_strand_edge_iter_valid(&it_edge); BKE_strand_edge_iter_next(&it_edge)) + length += len_v3v3(it_edge.vertex1->co, it_edge.vertex0->co); + float length_inv = length > 0.0f ? 1.0f / length : 0.0f; + + float t = 0.0f; + for (BKE_strand_edge_iter_init(&it_edge, it_strand); BKE_strand_edge_iter_valid(&it_edge); BKE_strand_edge_iter_next(&it_edge)) { + int vj = BKE_strand_edge_iter_vertex1_offset(strands, &it_edge); + t += len_v3v3(it_edge.vertex1->co, it_edge.vertex0->co); + + float stiffness = strands_goal_stiffness(strands, params, it_edge.vertex1, t * length_inv); + float damping = stiffness * params->goal_damping; + + float goal[3]; + mul_v3_m4v3(goal, space, it_edge.vertex1->co); + + float f[3]; + BPH_mass_spring_force_spring_goal(data, vj, goal, rootvel, stiffness, damping, f, NULL, NULL); + } +#endif +} + +/* calculates internal forces for a single strand curve */ +static void strands_calc_curve_forces(Strands *strands, float space[4][4], HairSimParams *params, CacheEffector *cache_effectors, int tot_cache_effectors, + Implicit_Data *data, StrandIterator *it_strand) +{ + strands_calc_curve_stretch_forces(strands, space, params, data, it_strand); + strands_calc_curve_bending_forces(strands, space, params, data, it_strand); + strands_calc_vertex_goal_forces(strands, space, params, cache_effectors, tot_cache_effectors, data, it_strand); +} + +/* Collect forces and derivatives: F, dFdX, dFdV */ +static void strands_calc_force(Strands *strands, float space[4][4], HairSimParams *params, Implicit_Data *data, float UNUSED(frame), Scene *scene, + ListBase *effectors, CacheEffector *cache_effectors, int tot_cache_effectors) +{ + unsigned int numverts = strands->totverts; + + int i = 0; + float gravity[3] = {0.0f, 0.0f, 0.0f}; + + /* global acceleration (gravitation) */ + if (scene->physics_settings.flag & PHYS_GLOBAL_GRAVITY) { + /* scale gravity force */ + mul_v3_v3fl(gravity, scene->physics_settings.gravity, params->effector_weights->global_gravity); + } + for (i = 0; i < numverts; i++) { + float mass = params->mass; + BPH_mass_spring_force_gravity(data, i, mass, gravity); + } + + BPH_mass_spring_force_drag(data, params->drag); + + /* handle external forces like wind */ + if (effectors || (cache_effectors && tot_cache_effectors > 0)) { + /* cache per-vertex forces to avoid redundant calculation */ + float (*ext_forces)[3] = (float (*)[3])MEM_callocN(sizeof(float) * 3 * numverts, "effector forces"); + + if (effectors) { + for (i = 0; i < numverts; ++i) { + float x[3], v[3]; + EffectedPoint epoint; + + BPH_mass_spring_get_motion_state(data, i, x, v); + pd_point_from_loc(scene, x, v, i, &epoint); + pdDoEffectors(effectors, NULL, params->effector_weights, &epoint, ext_forces[i], NULL); + } + } + + if (cache_effectors && tot_cache_effectors > 0) { + for (i = 0; i < numverts; ++i) { + CacheEffectorPoint point; + point.index = i; + BPH_mass_spring_get_motion_state(data, i, point.x, point.v); + + CacheEffectorResult result; + if (BKE_cache_effectors_eval(cache_effectors, tot_cache_effectors, &point, &result) > 0) { + add_v3_v3(ext_forces[i], result.f); + } + } + } + + for (i = 0; i < numverts; ++i) { + BPH_mass_spring_force_vertex_wind(data, i, 1.0f, ext_forces); + } + + MEM_freeN(ext_forces); + } + + /* spring forces */ + StrandIterator it_strand; + for (BKE_strand_iter_init(&it_strand, strands); BKE_strand_iter_valid(&it_strand); BKE_strand_iter_next(&it_strand)) { + strands_calc_curve_forces(strands, space, params, cache_effectors, tot_cache_effectors, data, &it_strand); + } +} + +/* calculates the velocity of strand roots using the new rest location (verts->co) and the current motion state */ +static void strands_calc_root_velocity(Strands *strands, float mat[4][4], Implicit_Data *data, float timestep) +{ + StrandIterator it_strand; + for (BKE_strand_iter_init(&it_strand, strands); BKE_strand_iter_valid(&it_strand); BKE_strand_iter_next(&it_strand)) { + if (it_strand.curve->numverts > 0) { + int index = BKE_strand_iter_vertex_offset(strands, &it_strand); + + float vel[3]; + sub_v3_v3v3(vel, it_strand.verts[0].co, it_strand.state[0].co); + mul_v3_fl(vel, 1.0f/timestep); + mul_mat3_m4_v3(mat, vel); + + BPH_mass_spring_set_velocity(data, index, vel); + } + } +} + +/* calculates the location of strand roots using the new rest location (verts->co) and the current motion state */ +static void strands_calc_root_location(Strands *strands, float mat[4][4], Implicit_Data *data, float step) +{ + StrandIterator it_strand; + for (BKE_strand_iter_init(&it_strand, strands); BKE_strand_iter_valid(&it_strand); BKE_strand_iter_next(&it_strand)) { + if (it_strand.curve->numverts > 0) { + int index = BKE_strand_iter_vertex_offset(strands, &it_strand); + + float co[3]; + interp_v3_v3v3(co, it_strand.state[0].co, it_strand.verts[0].co, step); + mul_m4_v3(mat, co); + + BPH_mass_spring_set_position(data, index, co); + } + } +} + +/* calculates the location of virtual goal vertices */ +static void strands_calc_goal_velocities(Strands *strands, float mat[4][4], Implicit_Data *data, float timestep) +{ + const int goalstart = strands->totverts; + int goalindex = goalstart; + + StrandIterator it_strand; + for (BKE_strand_iter_init(&it_strand, strands); BKE_strand_iter_valid(&it_strand); BKE_strand_iter_next(&it_strand)) { + StrandVertexIterator it_vert; + for (BKE_strand_vertex_iter_init(&it_vert, &it_strand); BKE_strand_vertex_iter_valid(&it_vert); BKE_strand_vertex_iter_next(&it_vert)) { + /* skip root */ + if (it_vert.index == 0) + continue; + + int index = BKE_strand_vertex_iter_vertex_offset(strands, &it_vert); + + float vel[3]; + sub_v3_v3v3(vel, strands->verts[index].co, strands->state[index].co); + mul_v3_fl(vel, 1.0f/timestep); + mul_mat3_m4_v3(mat, vel); + + BPH_mass_spring_set_velocity(data, goalindex, vel); + + ++goalindex; + } + } +} + +/* calculates the location of virtual goal vertices */ +static void strands_calc_goal_locations(Strands *strands, float mat[4][4], Implicit_Data *data, float step) +{ + const int goalstart = strands->totverts; + int goalindex = goalstart; + + StrandIterator it_strand; + for (BKE_strand_iter_init(&it_strand, strands); BKE_strand_iter_valid(&it_strand); BKE_strand_iter_next(&it_strand)) { + StrandVertexIterator it_vert; + for (BKE_strand_vertex_iter_init(&it_vert, &it_strand); BKE_strand_vertex_iter_valid(&it_vert); BKE_strand_vertex_iter_next(&it_vert)) { + /* skip root */ + if (it_vert.index == 0) + continue; + + int index = BKE_strand_vertex_iter_vertex_offset(strands, &it_vert); + + float co[3]; + interp_v3_v3v3(co, strands->state[index].co, strands->verts[index].co, step); + mul_m4_v3(mat, co); + + BPH_mass_spring_set_position(data, goalindex, co); + + ++goalindex; + } + } +} + +/* XXX Do we need to take fictitious forces from the moving and/or accelerated frame of reference into account? + * This would mean we pass not only the basic world transform mat, but also linear/angular velocity and acceleration. + */ +bool BPH_strands_solve(Strands *strands, float mat[4][4], Implicit_Data *id, HairSimParams *params, float frame, float frame_prev, Scene *scene, + ListBase *effectors, CacheEffector *cache_effectors, int tot_cache_effectors) +{ + if (params->timescale == 0.0f || params->substeps < 1) + return false; + + float timestep = (FRA2TIME(frame) - FRA2TIME(frame_prev)) * params->timescale; + float dstep = 1.0f / params->substeps; + float dtime = timestep * dstep; + int numverts = strands->totverts; + + int i; + ColliderContacts *contacts = NULL; + int totcolliders = 0; + + float imat[4][4]; + invert_m4_m4(imat, mat); + +// if (!clmd->solver_result) +// clmd->solver_result = (ClothSolverResult *)MEM_callocN(sizeof(ClothSolverResult), "cloth solver result"); +// cloth_clear_result(clmd); + + /* initialize solver data */ + for (i = 0; i < numverts; i++) { + float wco[3], wvel[3]; + copy_v3_v3(wco, strands->state[i].co); + copy_v3_v3(wvel, strands->state[i].vel); + mul_m4_v3(mat, wco); + mul_mat3_m4_v3(mat, wvel); + BPH_mass_spring_set_motion_state(id, i, wco, wvel); + } + strands_calc_root_velocity(strands, mat, id, timestep); + + strands_calc_goal_locations(strands, mat, id, 0.0f); + strands_calc_goal_velocities(strands, mat, id, timestep); + + for (float step = 0.0f; step < 1.0f; step += dstep) { + ImplicitSolverResult result; + +#if 0 + /* determine contact points */ + if (clmd->coll_parms->flags & CLOTH_COLLSETTINGS_FLAG_ENABLED) { + cloth_find_point_contacts(ob, clmd, 0.0f, tf, &contacts, &totcolliders); + } +#endif + + /* setup vertex constraints for pinned vertices and contacts */ + strands_setup_constraints(strands, id, contacts, totcolliders, dtime); + + /* initialize forces to zero */ + BPH_mass_spring_clear_forces(id); + + // calculate forces + strands_calc_force(strands, mat, params, id, frame, scene, effectors, cache_effectors, tot_cache_effectors); + + // calculate new velocity and position + BPH_mass_spring_solve_velocities(id, dtime, &result); +// cloth_record_result(clmd, &result, clmd->sim_parms->stepsPerFrame); + +#if 0 + if (is_hair) { + cloth_continuum_step(clmd, dtime); + } +#endif + + BPH_mass_spring_solve_positions(id, dtime); + + BPH_mass_spring_apply_result(id); + + /* move pinned verts to correct position */ + strands_calc_root_location(strands, mat, id, step + dstep); + strands_calc_goal_locations(strands, mat, id, step + dstep); + +#if 0 + /* free contact points */ + if (contacts) { + cloth_free_contacts(contacts, totcolliders); + } +#endif + + } + + /* copy results back to strand data */ + for (i = 0; i < numverts; i++) { + float co[3], vel[3]; + BPH_mass_spring_get_motion_state(id, i, co, vel); + mul_m4_v3(imat, co); + mul_mat3_m4_v3(imat, vel); + copy_v3_v3(strands->state[i].co, co); + copy_v3_v3(strands->state[i].vel, vel); + } + + return true; +} diff --git a/source/blender/physics/intern/eigen_utils.h b/source/blender/physics/intern/eigen_utils.h index e4a4f306aeb..ebfed7cb690 100644 --- a/source/blender/physics/intern/eigen_utils.h +++ b/source/blender/physics/intern/eigen_utils.h @@ -39,6 +39,7 @@ #endif #include <Eigen/Sparse> +#include <Eigen/SVD> #include <Eigen/src/Core/util/DisableStupidWarnings.h> #ifdef __GNUC__ @@ -113,6 +114,7 @@ public: }; typedef Eigen::VectorXf lVector; +typedef Eigen::VectorXf VectorX; /* Extension of dense Eigen vectors, * providing 3-float block access for blenlib math functions @@ -146,6 +148,7 @@ public: typedef Eigen::Triplet<Scalar> Triplet; typedef std::vector<Triplet> TripletList; +typedef Eigen::MatrixXf MatrixX; typedef Eigen::SparseMatrix<Scalar> lMatrix; /* Constructor type that provides more convenient handling of Eigen triplets @@ -201,6 +204,33 @@ typedef Eigen::ConjugateGradient<lMatrix, Eigen::Lower, Eigen::DiagonalPrecondit using Eigen::ComputationInfo; +template <typename MatrixType> +BLI_INLINE MatrixType pseudo_inverse(const MatrixType& mat, double epsilon = std::numeric_limits<double>::epsilon()) +{ + typedef Eigen::JacobiSVD<MatrixType> SVD; +// typedef typename SVD::SingularValuesType SingularValues; + + SVD svd(mat, Eigen::ComputeThinU | Eigen::ComputeThinV); + + double tolerance = epsilon * std::max(mat.cols(), mat.rows()) * svd.singularValues().array().abs()(0); + return svd.matrixV() * (svd.singularValues().array().abs() > tolerance).select(svd.singularValues().array().inverse(), 0).matrix().asDiagonal() * svd.matrixU().adjoint(); + +// const double pinvtoler=1.e-6; // choose your tolerance wisely! +// SingularValues singularValues_inv = svd.singularValues(); +// for (long i = 0; i < mat.cols(); ++i) { +// if (svd.singularValues(i) > pinvtoler ) +// singularValues_inv(i) = 1.0 / svd.singularValues(i); +// else singularValues_inv(i) = 0; +// } + +// return (svd.matrixV() * singularValues_inv.asDiagonal() * svd.matrixU().transpose()); +} + +BLI_INLINE void print_matrix_elem(float v) +{ + printf("%-8.3f", v); +} + BLI_INLINE void print_lvector(const lVector3f &v) { for (int i = 0; i < v.rows(); ++i) { @@ -221,7 +251,7 @@ BLI_INLINE void print_lmatrix(const lMatrix &m) if (i > 0 && i % 3 == 0) printf(" "); - implicit_print_matrix_elem(m.coeff(j, i)); + print_matrix_elem(m.coeff(j, i)); } printf("\n"); } diff --git a/source/blender/physics/intern/implicit.h b/source/blender/physics/intern/implicit.h index dba1cd11ae0..79cf35ffb49 100644 --- a/source/blender/physics/intern/implicit.h +++ b/source/blender/physics/intern/implicit.h @@ -67,11 +67,6 @@ typedef struct ImplicitSolverResult { float error; } ImplicitSolverResult; -BLI_INLINE void implicit_print_matrix_elem(float v) -{ - printf("%-8.3f", v); -} - void BPH_mass_spring_set_vertex_mass(struct Implicit_Data *data, int index, float mass); void BPH_mass_spring_set_rest_transform(struct Implicit_Data *data, int index, float rot[3][3]); @@ -80,6 +75,7 @@ void BPH_mass_spring_set_position(struct Implicit_Data *data, int index, const f void BPH_mass_spring_set_velocity(struct Implicit_Data *data, int index, const float v[3]); void BPH_mass_spring_get_motion_state(struct Implicit_Data *data, int index, float x[3], float v[3]); void BPH_mass_spring_get_position(struct Implicit_Data *data, int index, float x[3]); +void BPH_mass_spring_get_velocity(struct Implicit_Data *data, int index, float v[3]); /* access to modified motion state during solver step */ void BPH_mass_spring_get_new_position(struct Implicit_Data *data, int index, float x[3]); @@ -111,7 +107,7 @@ void BPH_mass_spring_force_face_wind(struct Implicit_Data *data, int v1, int v2, /* Wind force, acting on an edge */ void BPH_mass_spring_force_edge_wind(struct Implicit_Data *data, int v1, int v2, float radius1, float radius2, const float (*winvec)[3]); /* Wind force, acting on a vertex */ -void BPH_mass_spring_force_vertex_wind(struct Implicit_Data *data, int v, float radius, const float (*winvec)[3]); +void BPH_mass_spring_force_vertex_wind(struct Implicit_Data *data, int v, float factor, const float (*winvec)[3]); /* Linear spring force between two points */ bool BPH_mass_spring_force_spring_linear(struct Implicit_Data *data, int i, int j, float restlen, float stiffness, float damping, bool no_compress, float clamp_force, @@ -122,7 +118,8 @@ bool BPH_mass_spring_force_spring_bending(struct Implicit_Data *data, int i, int float r_f[3], float r_dfdx[3][3], float r_dfdv[3][3]); /* Angular bending force based on local target vectors */ bool BPH_mass_spring_force_spring_bending_angular(struct Implicit_Data *data, int i, int j, int k, - const float target[3], float stiffness, float damping); + const float target[3], float stiffness, float damping, + float r_f[3], float r_dfdx[3][3], float r_dfdv[3][3]); /* Global goal spring */ bool BPH_mass_spring_force_spring_goal(struct Implicit_Data *data, int i, const float goal_x[3], const float goal_v[3], float stiffness, float damping, diff --git a/source/blender/physics/intern/implicit_blender.c b/source/blender/physics/intern/implicit_blender.c index 5b1d83a3eef..a2dd1ea76b4 100644 --- a/source/blender/physics/intern/implicit_blender.c +++ b/source/blender/physics/intern/implicit_blender.c @@ -324,9 +324,7 @@ static void print_sparse_matrix(fmatrix3x3 *m) } } } -#endif -#if 0 static void print_lvector(lfVector *v, int numverts) { int i; @@ -339,9 +337,7 @@ static void print_lvector(lfVector *v, int numverts) printf("%f,\n", v[i][2]); } } -#endif -#if 0 static void print_bfmatrix(fmatrix3x3 *m) { int tot = m[0].vcount + m[0].scount; @@ -377,7 +373,7 @@ static void print_bfmatrix(fmatrix3x3 *m) if (i > 0 && i % 3 == 0) printf(" "); - implicit_print_matrix_elem(t[i + j * size]); + print_matrix_elem(t[i + j * size]); } printf("\n"); } @@ -754,6 +750,14 @@ void BPH_mass_spring_solver_free(Implicit_Data *id) MEM_freeN(id); } +int BPH_mass_spring_solver_numvert(Implicit_Data *id) +{ + if (id) + return id->A[0].vcount; + else + return 0; +} + /* ==== Transformation from/to root reference frames ==== */ BLI_INLINE void world_to_root_v3(Implicit_Data *data, int index, float r[3], const float v[3]) @@ -1225,6 +1229,11 @@ void BPH_mass_spring_get_position(struct Implicit_Data *data, int index, float x root_to_world_v3(data, index, x, data->X[index]); } +void BPH_mass_spring_get_velocity(struct Implicit_Data *data, int index, float v[3]) +{ + root_to_world_v3(data, index, v, data->V[index]); +} + void BPH_mass_spring_get_new_position(struct Implicit_Data *data, int index, float x[3]) { root_to_world_v3(data, index, x, data->Xnew[index]); @@ -1503,15 +1512,13 @@ void BPH_mass_spring_force_edge_wind(Implicit_Data *data, int v1, int v2, float add_v3_v3(data->F[v2], f); } -void BPH_mass_spring_force_vertex_wind(Implicit_Data *data, int v, float UNUSED(radius), const float (*winvec)[3]) +void BPH_mass_spring_force_vertex_wind(Implicit_Data *data, int v, float factor, const float (*winvec)[3]) { - const float density = 0.01f; /* XXX arbitrary value, corresponds to effect of air density */ - float wind[3]; float f[3]; world_to_root_v3(data, v, wind, winvec[v]); - mul_v3_v3fl(f, wind, density); + mul_v3_v3fl(f, wind, factor); add_v3_v3(data->F[v], f); } @@ -1519,26 +1526,19 @@ BLI_INLINE void dfdx_spring(float to[3][3], const float dir[3], float length, fl { // dir is unit length direction, rest is spring's restlength, k is spring constant. //return ( (I-outerprod(dir, dir))*Min(1.0f, rest/length) - I) * -k; - outerproduct(to, dir, dir); - sub_m3_m3m3(to, I, to); + if (L > ALMOST_ZERO) { + outerproduct(to, dir, dir); + sub_m3_m3m3(to, I, to); + + mul_m3_fl(to, (L / length)); + } + else + zero_m3(to); - mul_m3_fl(to, (L/length)); sub_m3_m3m3(to, to, I); mul_m3_fl(to, k); } -/* unused */ -#if 0 -BLI_INLINE void dfdx_damp(float to[3][3], const float dir[3], float length, const float vel[3], float rest, float damping) -{ - // inner spring damping vel is the relative velocity of the endpoints. - // return (I-outerprod(dir, dir)) * (-damping * -(dot(dir, vel)/Max(length, rest))); - mul_fvectorT_fvector(to, dir, dir); - sub_fmatrix_fmatrix(to, I, to); - mul_fmatrix_S(to, (-damping * -(dot_v3v3(dir, vel)/MAX2(length, rest)))); -} -#endif - BLI_INLINE void dfdv_damp(float to[3][3], const float dir[3], float damping) { // derivative of force wrt velocity @@ -1548,7 +1548,7 @@ BLI_INLINE void dfdv_damp(float to[3][3], const float dir[3], float damping) BLI_INLINE float fb(float length, float L) { - float x = length / L; + float x = L > ALMOST_ZERO ? length / L : 0.0f; float xx = x * x; float xxx = xx * x; float xxxx = xxx * x; @@ -1557,7 +1557,7 @@ BLI_INLINE float fb(float length, float L) BLI_INLINE float fbderiv(float length, float L) { - float x = length/L; + float x = L > ALMOST_ZERO ? length / L : 0.0f; float xx = x * x; float xxx = xx * x; return (-46.164f * xxx + 102.579f * xx - 78.166f * x + 23.116f); @@ -1854,7 +1854,8 @@ BLI_INLINE void spring_angbend_estimate_dfdv(Implicit_Data *data, int i, int j, * See "Artistic Simulation of Curly Hair" (Pixar technical memo #12-03a) */ bool BPH_mass_spring_force_spring_bending_angular(Implicit_Data *data, int i, int j, int k, - const float target[3], float stiffness, float damping) + const float target[3], float stiffness, float damping, + float r_f[3], float r_dfdx[3][3], float r_dfdv[3][3]) { float goal[3]; float fj[3], fk[3]; @@ -1991,6 +1992,13 @@ bool BPH_mass_spring_force_spring_bending_angular(Implicit_Data *data, int i, in add_m3_m3m3(data->dFdX[block_ik].m, data->dFdX[block_ik].m, dfk_dxi); #endif + if (r_f) + copy_v3_v3(r_f, fj); + if (r_dfdx) + copy_m3_m3(r_dfdx, dfj_dxj); + if (r_dfdv) + copy_m3_m3(r_dfdx, dfj_dvj); + return true; } diff --git a/source/blender/physics/intern/implicit_eigen.cpp b/source/blender/physics/intern/implicit_eigen.cpp index 0ea8ccb0a49..69feb3c19e2 100644 --- a/source/blender/physics/intern/implicit_eigen.cpp +++ b/source/blender/physics/intern/implicit_eigen.cpp @@ -269,7 +269,7 @@ static void print_lmatrix(const lMatrix &m) if (i > 0 && i % 3 == 0) printf(" "); - implicit_print_matrix_elem(m.coeff(j, i)); + print_matrix_elem(m.coeff(j, i)); } printf("\n"); } diff --git a/source/blender/physics/intern/strands.cpp b/source/blender/physics/intern/strands.cpp new file mode 100644 index 00000000000..f8bba6a615c --- /dev/null +++ b/source/blender/physics/intern/strands.cpp @@ -0,0 +1,472 @@ +/* + * ***** 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) Blender Foundation + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): Lukas Toenne + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/physics/intern/strands.c + * \ingroup bke + */ + +extern "C" { +#include "MEM_guardedalloc.h" + +#include "BLI_math.h" + +#include "DNA_customdata_types.h" +#include "DNA_object_types.h" + +#include "BKE_bvhutils.h" +#include "BKE_customdata.h" +#include "BKE_cdderivedmesh.h" +#include "BKE_DerivedMesh.h" +#include "BKE_editstrands.h" +#include "BKE_effect.h" +#include "BKE_mesh_sample.h" + +#include "bmesh.h" +} + +#include "BPH_strands.h" + +#include "eigen_utils.h" + +/* === constraints === */ + +static bool strand_get_root_vectors(BMEditStrands *edit, BMVert *root, float loc[3], float nor[3], float tang[3]) +{ + BMesh *bm = edit->bm; + DerivedMesh *root_dm = edit->root_dm; + MSurfaceSample root_sample; + + if (!CustomData_has_layer(&bm->vdata, CD_MSURFACE_SAMPLE)) + return false; + + BM_elem_meshsample_data_named_get(&bm->vdata, root, CD_MSURFACE_SAMPLE, CD_HAIR_ROOT_LOCATION, &root_sample); + return BKE_mesh_sample_eval(root_dm, &root_sample, loc, nor, tang); +} + +static int strand_count_vertices(BMVert *root) +{ + BMVert *v; + BMIter iter; + + int len = 0; + BM_ITER_STRANDS_ELEM(v, &iter, root, BM_VERTS_OF_STRAND) { + ++len; + } + return len; +} + +static int UNUSED_FUNCTION(strands_get_max_length)(BMEditStrands *edit) +{ + BMVert *root; + BMIter iter; + int maxlen = 0; + + BM_ITER_STRANDS(root, &iter, edit->bm, BM_STRANDS_OF_MESH) { + int len = strand_count_vertices(root); + if (len > maxlen) + maxlen = len; + } + return maxlen; +} + +static void strands_apply_root_locations(BMEditStrands *edit) +{ + BMVert *root; + BMIter iter; + + if (!edit->root_dm) + return; + + BM_ITER_STRANDS(root, &iter, edit->bm, BM_STRANDS_OF_MESH) { + float loc[3], nor[3], tang[3]; + + if (strand_get_root_vectors(edit, root, loc, nor, tang)) { + copy_v3_v3(root->co, loc); + } + } +} + +static void strands_adjust_segment_lengths(BMesh *bm) +{ + BMVert *root; + BMIter iter; + + BM_ITER_STRANDS(root, &iter, bm, BM_STRANDS_OF_MESH) { + BMVert *v, *vprev = NULL; + BMIter iter_strand; + BM_ITER_STRANDS_ELEM(v, &iter_strand, root, BM_VERTS_OF_STRAND) { + if (vprev) { + float base_length = BM_elem_float_data_named_get(&bm->vdata, vprev, CD_PROP_FLT, CD_HAIR_SEGMENT_LENGTH); + float dist[3]; + float length; + + sub_v3_v3v3(dist, v->co, vprev->co); + length = len_v3(dist); + if (length > 0.0f) + madd_v3_v3v3fl(v->co, vprev->co, dist, base_length / length); + } + vprev = v; + } + } +} + +static void strands_vertex_relax(BMesh *bm, float relax_factor, const BMVert *vprev, const BMVert *v, const BMVert *vnext, float relax[3]) +{ + float D_pos[3], D_neg[3]; + + if (vprev) { + sub_v3_v3v3(D_neg, vprev->co, v->co); + float len = len_v3(D_neg); + if (len > 0.0f) { + float Lprev = BM_elem_float_data_named_get(&bm->vdata, (void *)vprev, CD_PROP_FLT, CD_HAIR_SEGMENT_LENGTH); + mul_v3_fl(D_neg, (1.0f - Lprev / len) * relax_factor); + } + else + zero_v3(D_neg); + } + else + zero_v3(D_neg); + + if (vnext) { + sub_v3_v3v3(D_pos, vnext->co, v->co); + float len = len_v3(D_pos); + if (len > 0.0f) { + float L = BM_elem_float_data_named_get(&bm->vdata, (void *)v, CD_PROP_FLT, CD_HAIR_SEGMENT_LENGTH); + mul_v3_fl(D_pos, (1.0f - L / len) * relax_factor); + } + else + zero_v3(D_pos); + } + else + zero_v3(D_pos); + + add_v3_v3v3(relax, D_neg, D_pos); +} + +/* single relaxation iteration, must be repeated totkey times for complete relaxation */ +static void strands_relax(BMesh *bm, BMVert *root, bool skip_first) +{ + const int numvert = BM_strands_keys_count(root); + const float relax_factor = numvert > 0 ? 1.0f / numvert : 0.0f; + + BMVert *vert_next, *vert = NULL, *vert_prev = NULL; + float relax_prev[3], relax[3]; + BMIter iter; + + BM_ITER_STRANDS_ELEM(vert_next, &iter, root, BM_VERTS_OF_STRAND) { + + if (vert) { + /* note: relaxation is applied *after* calculating the next segment, so vertex location can be modified safely */ + strands_vertex_relax(bm, relax_factor, vert_prev, vert, vert_next, relax); + + /* don't modify fixed root */ + if (vert_prev && !(skip_first && vert_prev == root)) { + add_v3_v3(vert_prev->co, relax_prev); + } + } + + vert_prev = vert; + vert = vert_next; + copy_v3_v3(relax_prev, relax); + } + + /* last segment */ + { + /* don't modify fixed root */ + if (vert_prev && !(skip_first && vert_prev == root)) { + add_v3_v3(vert_prev->co, relax_prev); + } + } +} + +/* try to find a nice solution to keep distances between neighboring keys */ +/* XXX Stub implementation ported from particles: + * Successively relax each segment starting from the root, + * repeat this for every vertex (O(n^2)) + * This should be replaced by a more advanced method using a least-squares + * error metric with length and root location constraints (IK solver) + */ +static void strands_solve_edge_relaxation(BMEditStrands *edit) +{ + BMesh *bm = edit->bm; + BMVert *root; + BMIter iter; + + if (!edit) + return; +// if (!(pset->flag & PE_KEEP_LENGTHS)) // XXX TODO +// return; + + BM_ITER_STRANDS(root, &iter, bm, BM_STRANDS_OF_MESH) { + BMVert *vj; + BMIter iterj; + int j; + + BM_ITER_STRANDS_ELEM_INDEX(vj, &iterj, root, BM_VERTS_OF_STRAND, j) { + + if (j < 1) + continue; + + /* XXX particles use PE_LOCK_FIRST option */ + bool skip_first = true; + + strands_relax(bm, root, skip_first); + } + } +} + +typedef struct IKTarget { + BMVert *vertex; + float weight; +} IKTarget; + +static int strand_find_ik_targets(BMVert *root, IKTarget *targets) +{ + BMVert *v; + BMIter iter; + int k, index; + + index = 0; + BM_ITER_STRANDS_ELEM_INDEX(v, &iter, root, BM_VERTS_OF_STRAND, k) { + /* XXX TODO allow multiple targets and do weight calculation here */ + if (BM_strands_vert_is_tip(v)) { + IKTarget *target = &targets[index]; + target->vertex = v; + target->weight = 1.0f; + ++index; + } + } + + return index; +} + +static void calc_jacobian_entry(Object *ob, BMEditStrands * /*edit*/, IKTarget *target, int index_target, int index_angle, + const float point[3], const float axis1[3], const float axis2[3], MatrixX &J) +{ + float (*obmat)[4] = ob->obmat; + + float dist[3], jac1[3], jac2[3]; + + sub_v3_v3v3(dist, target->vertex->co, point); + + cross_v3_v3v3(jac1, axis1, dist); + cross_v3_v3v3(jac2, axis2, dist); + + for (int i = 0; i < 3; ++i) { + J.coeffRef(index_target + i, index_angle + 0) = jac1[i]; + J.coeffRef(index_target + i, index_angle + 1) = jac2[i]; + } + +#if 1 + { + float wco[3], wdir[3]; + + mul_v3_m4v3(wco, obmat, point); + + mul_v3_m4v3(wdir, obmat, jac1); + BKE_sim_debug_data_add_vector(wco, wdir, 1,1,0, "strands", index_angle, 1); + mul_v3_m4v3(wdir, obmat, jac2); + BKE_sim_debug_data_add_vector(wco, wdir, 0,1,1, "strands", index_angle + 1, 2); + } +#endif +} + +static MatrixX strand_calc_target_jacobian(Object *ob, BMEditStrands *edit, BMVert *root, int numjoints, IKTarget *targets, int numtargets) +{ + BMVert *v, *vprev; + BMIter iter_strand; + int k; + + float loc[3], axis[3], dir[3]; + + MatrixX J(3 * numtargets, 2 * numjoints); + if (!strand_get_root_vectors(edit, root, loc, dir, axis)) { + return J; + } + + BM_ITER_STRANDS_ELEM_INDEX(v, &iter_strand, root, BM_VERTS_OF_STRAND, k) { + float dirprev[3]; + + if (k > 0) { + float rot[3][3]; + + copy_v3_v3(dirprev, dir); + sub_v3_v3v3(dir, v->co, vprev->co); + normalize_v3(dir); + + rotation_between_vecs_to_mat3(rot, dirprev, dir); + mul_m3_v3(rot, axis); + } + + calc_jacobian_entry(ob, edit, &targets[0], 0, 2*k, v->co, axis, dir, J); + +#if 0 + { + float (*obmat)[4] = ob->obmat; + float wco[3], wdir[3]; + + mul_v3_m4v3(wco, obmat, v->co); + + mul_v3_m4v3(wdir, obmat, axis); + BKE_sim_debug_data_add_vector(edit->debug_data, wco, wdir, 1,0,0, "strands", BM_elem_index_get(v), 1); + mul_v3_m4v3(wdir, obmat, dir); + BKE_sim_debug_data_add_vector(edit->debug_data, wco, wdir, 0,1,0, "strands", BM_elem_index_get(v), 2); + cross_v3_v3v3(wdir, axis, dir); + mul_m4_v3(obmat, wdir); + BKE_sim_debug_data_add_vector(edit->debug_data, wco, wdir, 0,0,1, "strands", BM_elem_index_get(v), 3); + } +#endif + + vprev = v; + } + + return J; +} + +static VectorX strand_angles_to_loc(Object * /*ob*/, BMEditStrands *edit, BMVert *root, int numjoints, const VectorX &angles) +{ + BMVert *v, *vprev; + BMIter iter_strand; + int k; + + float loc[3], axis[3], dir[3]; + float mat_theta[3][3], mat_phi[3][3]; + + if (!strand_get_root_vectors(edit, root, loc, dir, axis)) + return VectorX(); + + VectorX result(3*numjoints); + + BM_ITER_STRANDS_ELEM_INDEX(v, &iter_strand, root, BM_VERTS_OF_STRAND, k) { + float dirprev[3]; + + if (k > 0) { + const float base_length = BM_elem_float_data_named_get(&edit->bm->vdata, v, CD_PROP_FLT, CD_HAIR_SEGMENT_LENGTH); + float rot[3][3]; + + copy_v3_v3(dirprev, dir); + sub_v3_v3v3(dir, v->co, vprev->co); + normalize_v3(dir); + + rotation_between_vecs_to_mat3(rot, dirprev, dir); + mul_m3_v3(rot, axis); + + /* apply rotations from previous joint on the vertex */ + float vec[3]; + mul_v3_v3fl(vec, dir, base_length); + + mul_m3_v3(mat_theta, vec); + mul_m3_v3(mat_phi, vec); + add_v3_v3v3(&result.coeffRef(3*k), &result.coeff(3*(k-1)), vec); + } + else { + copy_v3_v3(&result.coeffRef(3*k), v->co); + } + + float theta = angles[2*k + 0]; + float phi = angles[2*k + 1]; + axis_angle_normalized_to_mat3(mat_theta, axis, theta); + axis_angle_normalized_to_mat3(mat_phi, dir, phi); + + vprev = v; + } + + return result; +} + +static void UNUSED_FUNCTION(strand_apply_ik_result)(Object *UNUSED(ob), BMEditStrands *UNUSED(edit), BMVert *root, const VectorX &solution) +{ + BMVert *v; + BMIter iter_strand; + int k; + + BM_ITER_STRANDS_ELEM_INDEX(v, &iter_strand, root, BM_VERTS_OF_STRAND, k) { + copy_v3_v3(v->co, &solution.coeff(3*k)); + } +} + +static void strands_solve_inverse_kinematics(Object *ob, BMEditStrands *edit, float (*orig)[3]) +{ + BMesh *bm = edit->bm; + + BMVert *root; + BMIter iter; + + BM_ITER_STRANDS(root, &iter, bm, BM_STRANDS_OF_MESH) { + int numjoints = strand_count_vertices(root); + if (numjoints <= 0) + continue; + + IKTarget targets[1]; /* XXX placeholder, later should be allocated to max. strand length */ + int numtargets = strand_find_ik_targets(root, targets); + + MatrixX J = strand_calc_target_jacobian(ob, edit, root, numjoints, targets, numtargets); + MatrixX Jinv = pseudo_inverse(J, 1.e-6); + + VectorX x(3 * numtargets); + for (int i = 0; i < numtargets; ++i) { + sub_v3_v3v3(&x.coeffRef(3*i), targets[i].vertex->co, orig[i]); + /* TODO calculate deviation of vertices from their origin (whatever that is) */ +// x[3*i + 0] = 0.0f; +// x[3*i + 1] = 0.0f; +// x[3*i + 2] = 0.0f; + } + VectorX angles = Jinv * x; + VectorX solution = strand_angles_to_loc(ob, edit, root, numjoints, angles); + +// strand_apply_ik_result(ob, edit, root, solution); + +#if 1 + { + BMVert *v; + BMIter iter_strand; + int k; + float wco[3]; + + BM_ITER_STRANDS_ELEM_INDEX(v, &iter_strand, root, BM_VERTS_OF_STRAND, k) { + mul_v3_m4v3(wco, ob->obmat, &solution.coeff(3*k)); + BKE_sim_debug_data_add_circle(wco, 0.05f, 1,0,1, "strands", k, BM_elem_index_get(root), 2344); + } + } +#endif + } +} + +void BPH_strands_solve_constraints(Object *ob, BMEditStrands *edit, float (*orig)[3]) +{ + strands_apply_root_locations(edit); + + if (true) { + strands_solve_edge_relaxation(edit); + } + else { + if (orig) + strands_solve_inverse_kinematics(ob, edit, orig); + } + + strands_adjust_segment_lengths(edit->bm); +} diff --git a/source/blender/pointcache/CMakeLists.txt b/source/blender/pointcache/CMakeLists.txt new file mode 100644 index 00000000000..2898785c11e --- /dev/null +++ b/source/blender/pointcache/CMakeLists.txt @@ -0,0 +1,66 @@ +# ***** 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# The Original Code is Copyright (C) 2013, Blender Foundation +# All rights reserved. +# +# The Original Code is: all of this file. +# +# ***** END GPL LICENSE BLOCK ***** + +set(INC + . + intern + util + ../blenkernel + ../blenlib + ../makesdna + ../makesrna + ../../../intern/guardedalloc +) + +set(INC_SYS +) + +set(SRC + intern/ptc_types.h + intern/ptc_types.cpp + intern/reader.h + intern/reader.cpp + intern/writer.h + intern/writer.cpp + + util/util_error_handler.h + util/util_error_handler.cpp + util/util_task.cpp + util/util_task.h + util/util_thread.h + util/util_types.h + + PTC_api.h + PTC_api.cpp +) + +if(NOT WITH_CPP11) + list(APPEND INC_SYS ${BOOST_INCLUDE_DIR}) +endif() + +if(WITH_ALEMBIC) + add_definitions(-DWITH_PTC_ALEMBIC) + add_subdirectory(alembic) +endif() + +blender_add_lib(bf_pointcache "${SRC}" "${INC}" "${INC_SYS}") diff --git a/source/blender/pointcache/PTC_api.cpp b/source/blender/pointcache/PTC_api.cpp new file mode 100644 index 00000000000..3e659399970 --- /dev/null +++ b/source/blender/pointcache/PTC_api.cpp @@ -0,0 +1,417 @@ +/* + * ***** 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright 2013, Blender Foundation. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#include "MEM_guardedalloc.h" + +#include "PTC_api.h" + +#include "util/util_error_handler.h" + +#include "reader.h" +#include "writer.h" + +#include "ptc_types.h" + +extern "C" { +#include "BLI_listbase.h" +#include "BLI_math.h" + +#include "DNA_listBase.h" +#include "DNA_modifier_types.h" +#include "DNA_scene_types.h" + +#include "BKE_DerivedMesh.h" +#include "BKE_modifier.h" +#include "BKE_report.h" +#include "BKE_scene.h" + +#include "RNA_access.h" +} + +using namespace PTC; + +class StubFactory : public Factory { + const std::string &get_default_extension() { static std::string ext = ""; return ext; } + WriterArchive *open_writer_archive(double /*fps*/, float /*start_frame*/, const std::string &/*name*/, PTCArchiveResolution /*resolutions*/, + const char */*app_name*/, const char */*description*/, const struct tm */*time*/, struct IDProperty */*metadata*/, ErrorHandler */*error_handler*/) { return NULL; } + ReaderArchive *open_reader_archive(double /*fps*/, float /*start_frame*/, const std::string &/*name*/, ErrorHandler * /*error_handler*/) { return NULL; } + void slice(ReaderArchive * /*in*/, WriterArchive * /*out*/, float /*start_frame*/, float /*end_frame*/) {} + Writer *create_writer_object(const std::string &/*name*/, Scene */*scene*/, Object */*ob*/) { return NULL; } + Reader *create_reader_object(const std::string &/*name*/, Object */*ob*/) { return NULL; } + Writer *create_writer_group(const std::string &/*name*/, Group */*group*/) { return NULL; } + Reader *create_reader_group(const std::string &/*name*/, Group */*group*/) { return NULL; } + Writer *create_writer_cloth(const std::string &/*name*/, Object */*ob*/, ClothModifierData */*clmd*/) { return NULL; } + Reader *create_reader_cloth(const std::string &/*name*/, Object */*ob*/, ClothModifierData */*clmd*/) { return NULL; } + Writer *create_writer_derived_mesh(const std::string &/*name*/, Object */*ob*/, DerivedMesh **/*dm_ptr*/) { return NULL; } + Reader *create_reader_derived_mesh(const std::string &/*name*/, Object */*ob*/) { return NULL; } + Writer *create_writer_derived_final_realtime(const std::string &/*name*/, Object */*ob*/) { return NULL; } + Writer *create_writer_derived_final_render(const std::string &/*name*/, Scene */*scene*/, Object */*ob*/, DerivedMesh **/*render_dm_ptr*/) { return NULL; } + Writer *create_writer_dupligroup(const std::string &/*name*/, EvaluationContext */*eval_ctx*/, Scene */*scene*/, Group */*group*/, CacheLibrary */*cachelib*/) { return NULL; } + Writer *create_writer_duplicache(const std::string &/*name*/, Group */*group*/, DupliCache */*dupcache*/, int /*datatypes*/, bool /*do_sim_debug*/) { return NULL; } + Reader *create_reader_duplicache(const std::string &/*name*/, Group */*group*/, DupliCache */*dupcache*/, bool /*read_strands_motion*/, bool /*read_strands_children*/, bool /*do_sim_debug*/) { return NULL; } + Reader *create_reader_duplicache_object(const std::string &/*name*/, Object */*ob*/, DupliObjectData */*data*/, bool /*read_strands_motion*/, bool /*read_strands_children*/) { return NULL; } +}; + +#ifndef WITH_PTC_ALEMBIC +void PTC_alembic_init() +{ + static StubFactory stub_factory; + PTC::Factory::alembic = &stub_factory; +} +#endif + +void PTC_error_handler_std(void) +{ + ErrorHandler::clear_default_handler(); +} + +void PTC_error_handler_callback(PTCErrorCallback cb, void *userdata) +{ + ErrorHandler::set_default_handler(new CallbackErrorHandler(cb, userdata)); +} + +static ReportType report_type_from_error_level(PTCErrorLevel level) +{ + switch (level) { + case PTC_ERROR_NONE: return RPT_DEBUG; + case PTC_ERROR_INFO: return RPT_INFO; + case PTC_ERROR_WARNING: return RPT_WARNING; + case PTC_ERROR_CRITICAL: return RPT_ERROR; + } + return RPT_ERROR; +} + +static void error_handler_reports_cb(void *vreports, PTCErrorLevel level, const char *message) +{ + ReportList *reports = (ReportList *)vreports; + + BKE_report(reports, report_type_from_error_level(level), message); +} + +void PTC_error_handler_reports(struct ReportList *reports) +{ + ErrorHandler::set_default_handler(new CallbackErrorHandler(error_handler_reports_cb, reports)); +} + +static void error_handler_modifier_cb(void *vmd, PTCErrorLevel UNUSED(level), const char *message) +{ + ModifierData *md = (ModifierData *)vmd; + + modifier_setError(md, "%s", message); +} + +void PTC_error_handler_modifier(struct ModifierData *md) +{ + ErrorHandler::set_default_handler(new CallbackErrorHandler(error_handler_modifier_cb, md)); +} + + +const char *PTC_get_default_archive_extension(void) +{ + return PTC::Factory::alembic->get_default_extension().c_str(); +} + +PTCWriterArchive *PTC_open_writer_archive(double fps, float start_frame, const char *path, PTCArchiveResolution resolutions, + const char *app_name, const char *description, const struct tm *time, struct IDProperty *metadata) +{ + return (PTCWriterArchive *)PTC::Factory::alembic->open_writer_archive(fps, start_frame, path, resolutions, app_name, description, time, metadata, NULL); +} + +void PTC_close_writer_archive(PTCWriterArchive *_archive) +{ + PTC::WriterArchive *archive = (PTC::WriterArchive *)_archive; + delete archive; +} + +void PTC_writer_archive_use_render(PTCWriterArchive *_archive, bool enable) +{ + PTC::WriterArchive *archive = (PTC::WriterArchive *)_archive; + archive->use_render(enable); +} + +PTCReaderArchive *PTC_open_reader_archive(Scene *scene, const char *path) +{ + double fps = FPS; + float start_frame = scene->r.sfra; + return PTC_open_reader_archive_ex(fps, start_frame, path); +} + +PTCReaderArchive *PTC_open_reader_archive_ex(double fps, float start_frame, const char *path) +{ + return (PTCReaderArchive *)PTC::Factory::alembic->open_reader_archive(fps, start_frame, path, NULL); +} + +void PTC_close_reader_archive(PTCReaderArchive *_archive) +{ + PTC::ReaderArchive *archive = (PTC::ReaderArchive *)_archive; + delete archive; +} + +PTCArchiveResolution PTC_reader_archive_get_resolutions(PTCReaderArchive *_archive) +{ + PTC::ReaderArchive *archive = (PTC::ReaderArchive *)_archive; + return archive->get_resolutions(); +} + +void PTC_reader_archive_use_render(PTCReaderArchive *_archive, bool enable) +{ + PTC::ReaderArchive *archive = (PTC::ReaderArchive *)_archive; + archive->use_render(enable); +} + +void PTC_writer_init(PTCWriter *_writer, PTCWriterArchive *_archive) +{ + PTC::Writer *writer = (PTC::Writer *)_writer; + PTC::WriterArchive *archive = (PTC::WriterArchive *)_archive; + writer->init(archive); +} + +void PTC_writer_create_refs(PTCWriter *_writer) +{ + PTC::Writer *writer = (PTC::Writer *)_writer; + writer->create_refs(); +} + +void PTC_reader_init(PTCReader *_reader, PTCReaderArchive *_archive) +{ + PTC::Reader *reader = (PTC::Reader *)_reader; + PTC::ReaderArchive *archive = (PTC::ReaderArchive *)_archive; + reader->init(archive); +} + +/* ========================================================================= */ + +void PTC_writer_free(PTCWriter *_writer) +{ + PTC::Writer *writer = (PTC::Writer *)_writer; + delete writer; +} + +void PTC_write_sample(struct PTCWriter *_writer) +{ + PTC::Writer *writer = (PTC::Writer *)_writer; + writer->write_sample(); +} + + +void PTC_reader_free(PTCReader *_reader) +{ + PTC::Reader *reader = (PTC::Reader *)_reader; + delete reader; +} + +bool PTC_reader_get_frame_range(PTCReader *_reader, int *start_frame, int *end_frame) +{ + PTC::Reader *reader = (PTC::Reader *)_reader; + int sfra, efra; + if (reader->get_frame_range(sfra, efra)) { + if (start_frame) *start_frame = sfra; + if (end_frame) *end_frame = efra; + return true; + } + else { + return false; + } +} + +PTCReadSampleResult PTC_read_sample(PTCReader *_reader, float frame) +{ + PTC::Reader *reader = (PTC::Reader *)_reader; + return reader->read_sample(frame); +} + +PTCReadSampleResult PTC_test_sample(PTCReader *_reader, float frame) +{ + PTC::Reader *reader = (PTC::Reader *)_reader; + return reader->test_sample(frame); +} + +void PTC_get_archive_info_stream(PTCReaderArchive *_archive, void (*stream)(void *, const char *), void *userdata) +{ + PTC::ReaderArchive *archive = (PTC::ReaderArchive *)_archive; + archive->get_info_stream(stream, userdata); +} + +void PTC_get_archive_info(PTCReaderArchive *_archive, struct CacheArchiveInfo *info, IDProperty *metadata) +{ + PTC::ReaderArchive *archive = (PTC::ReaderArchive *)_archive; + archive->get_info(info, metadata); +} + +void PTC_get_archive_info_nodes(PTCReaderArchive *_archive, struct CacheArchiveInfo *info, bool calc_bytes_size) +{ + PTC::ReaderArchive *archive = (PTC::ReaderArchive *)_archive; + archive->get_info_nodes(info, calc_bytes_size); +} + +void PTC_archive_slice(PTCReaderArchive *_in, PTCWriterArchive *_out, float start_frame, float end_frame) +{ + PTC::ReaderArchive *in = (PTC::ReaderArchive *)_in; + PTC::WriterArchive *out = (PTC::WriterArchive *)_out; + + PTC::Factory::alembic->slice(in, out, start_frame, end_frame); +} + + +PTCWriter *PTC_writer_dupligroup(const char *name, struct EvaluationContext *eval_ctx, struct Scene *scene, struct Group *group, struct CacheLibrary *cachelib) +{ + return (PTCWriter *)PTC::Factory::alembic->create_writer_dupligroup(name, eval_ctx, scene, group, cachelib); +} + +PTCWriter *PTC_writer_duplicache(const char *name, struct Group *group, struct DupliCache *dupcache, int datatypes, bool do_sim_debug) +{ + return (PTCWriter *)PTC::Factory::alembic->create_writer_duplicache(name, group, dupcache, datatypes, do_sim_debug); +} + +PTCReader *PTC_reader_duplicache(const char *name, struct Group *group, struct DupliCache *dupcache, + bool read_strands_motion, bool read_strands_children, bool read_sim_debug) +{ + return (PTCReader *)PTC::Factory::alembic->create_reader_duplicache(name, group, dupcache, + read_strands_motion, read_strands_children, read_sim_debug); +} + +PTCReader *PTC_reader_duplicache_object(const char *name, struct Object *ob, struct DupliObjectData *data, + bool read_strands_motion, bool read_strands_children) +{ + return (PTCReader *)PTC::Factory::alembic->create_reader_duplicache_object(name, ob, data, read_strands_motion, read_strands_children); +} + + +/* get writer/reader from RNA type */ +PTCWriter *PTC_writer_from_rna(Scene */*scene*/, PointerRNA */*ptr*/) +{ +#if 0 +#if 0 + if (RNA_struct_is_a(ptr->type, &RNA_ParticleSystem)) { + Object *ob = (Object *)ptr->id.data; + ParticleSystem *psys = (ParticleSystem *)ptr->data; + return PTC_writer_particles_combined(scene, ob, psys); + } +#endif + if (RNA_struct_is_a(ptr->type, &RNA_ClothModifier)) { + Object *ob = (Object *)ptr->id.data; + ClothModifierData *clmd = (ClothModifierData *)ptr->data; + return PTC_writer_cloth(scene, ob, clmd); + } +#endif + return NULL; +} + +PTCReader *PTC_reader_from_rna(Scene */*scene*/, PointerRNA */*ptr*/) +{ +#if 0 + if (RNA_struct_is_a(ptr->type, &RNA_ParticleSystem)) { + Object *ob = (Object *)ptr->id.data; + ParticleSystem *psys = (ParticleSystem *)ptr->data; + /* XXX particles are bad ... + * this can be either the actual particle cache or the hair dynamics cache, + * which is actually the cache of the internal cloth modifier + */ + bool use_cloth_cache = psys->part->type == PART_HAIR && (psys->flag & PSYS_HAIR_DYNAMICS); + if (use_cloth_cache && psys->clmd) + return PTC_reader_cloth(scene, ob, psys->clmd); + else + return PTC_reader_particles(scene, ob, psys); + } + if (RNA_struct_is_a(ptr->type, &RNA_ClothModifier)) { + Object *ob = (Object *)ptr->id.data; + ClothModifierData *clmd = (ClothModifierData *)ptr->data; + return PTC_reader_cloth(scene, ob, clmd); + } +#endif + return NULL; +} + + +/* ==== CLOTH ==== */ + +PTCWriter *PTC_writer_cloth(const char *name, Object *ob, ClothModifierData *clmd) +{ + return (PTCWriter *)PTC::Factory::alembic->create_writer_cloth(name, ob, clmd); +} + +PTCReader *PTC_reader_cloth(const char *name, Object *ob, ClothModifierData *clmd) +{ + return (PTCReader *)PTC::Factory::alembic->create_reader_cloth(name, ob, clmd); +} + + +/* ==== MESH ==== */ + +PTCWriter *PTC_writer_derived_mesh(const char *name, Object *ob, DerivedMesh **dm_ptr) +{ + return (PTCWriter *)PTC::Factory::alembic->create_writer_derived_mesh(name, ob, dm_ptr); +} + +PTCReader *PTC_reader_derived_mesh(const char *name, Object *ob) +{ + return (PTCReader *)PTC::Factory::alembic->create_reader_derived_mesh(name, ob); +} + +struct DerivedMesh *PTC_reader_derived_mesh_acquire_result(PTCReader *_reader) +{ + DerivedMeshReader *reader = (DerivedMeshReader *)_reader; + return reader->acquire_result(); +} + +void PTC_reader_derived_mesh_discard_result(PTCReader *_reader) +{ + DerivedMeshReader *reader = (DerivedMeshReader *)_reader; + reader->discard_result(); +} + + +PTCWriter *PTC_writer_derived_final_realtime(const char *name, Object *ob) +{ + return (PTCWriter *)PTC::Factory::alembic->create_writer_derived_final_realtime(name, ob); +} + +PTCWriter *PTC_writer_derived_final_render(const char *name, Scene *scene, Object *ob, DerivedMesh **render_dm_ptr) +{ + return (PTCWriter *)PTC::Factory::alembic->create_writer_derived_final_render(name, scene, ob, render_dm_ptr); +} + + +/* ==== OBJECT ==== */ + +PTCWriter *PTC_writer_object(const char *name, Scene *scene, Object *ob) +{ + return (PTCWriter *)PTC::Factory::alembic->create_writer_object(name, scene, ob); +} + +PTCReader *PTC_reader_object(const char *name, Object *ob) +{ + return (PTCReader *)PTC::Factory::alembic->create_reader_object(name, ob); +} + + +/* ==== GROUP ==== */ + +PTCWriter *PTC_writer_group(const char *name, Group *group) +{ + return (PTCWriter *)PTC::Factory::alembic->create_writer_group(name, group); +} + +PTCReader *PTC_reader_group(const char *name, Group *group) +{ + return (PTCReader *)PTC::Factory::alembic->create_writer_group(name, group); +} diff --git a/source/blender/pointcache/PTC_api.h b/source/blender/pointcache/PTC_api.h new file mode 100644 index 00000000000..1795d2b68c6 --- /dev/null +++ b/source/blender/pointcache/PTC_api.h @@ -0,0 +1,132 @@ +/* + * Copyright 2013, Blender Foundation. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __PTC_API_H__ +#define __PTC_API_H__ + +#include "util/util_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct tm; + +struct Main; +struct Scene; +struct EvaluationContext; +struct ListBase; +struct PointerRNA; +struct ReportList; +struct CacheArchiveInfo; + +struct DupliCache; +struct ClothModifierData; +struct DerivedMesh; +struct Group; +struct IDProperty; +struct ModifierData; +struct Object; +struct ParticleSystem; +struct SoftBody; + +struct PTCWriterArchive; +struct PTCReaderArchive; +struct PTCWriter; +struct PTCReader; + +void PTC_alembic_init(void); + +/*** Error Handling ***/ +void PTC_error_handler_std(void); +void PTC_error_handler_callback(PTCErrorCallback cb, void *userdata); +void PTC_error_handler_reports(struct ReportList *reports); +void PTC_error_handler_modifier(struct ModifierData *md); + +/*** Archive ***/ + +const char *PTC_get_default_archive_extension(void); + +struct PTCWriterArchive *PTC_open_writer_archive(double fps, float start_frame, const char *path, PTCArchiveResolution resolutions, + const char *app_name, const char *description, const struct tm *time, struct IDProperty *metadata); +void PTC_close_writer_archive(struct PTCWriterArchive *archive); +void PTC_writer_archive_use_render(struct PTCWriterArchive *archive, bool enable); + +struct PTCReaderArchive *PTC_open_reader_archive(struct Scene *scene, const char *path); +struct PTCReaderArchive *PTC_open_reader_archive_ex(double fps, float start_frame, const char *path); +void PTC_close_reader_archive(struct PTCReaderArchive *archive); +PTCArchiveResolution PTC_reader_archive_get_resolutions(struct PTCReaderArchive *archive); +void PTC_reader_archive_use_render(struct PTCReaderArchive *archive, bool enable); + +void PTC_writer_init(struct PTCWriter *writer, struct PTCWriterArchive *archive); +void PTC_writer_create_refs(struct PTCWriter *writer); +void PTC_reader_init(struct PTCReader *reader, struct PTCReaderArchive *archive); + +/*** Reader/Writer Interface ***/ + +void PTC_writer_free(struct PTCWriter *writer); +void PTC_write_sample(struct PTCWriter *writer); + +void PTC_reader_free(struct PTCReader *reader); +bool PTC_reader_get_frame_range(struct PTCReader *reader, int *start_frame, int *end_frame); +PTCReadSampleResult PTC_read_sample(struct PTCReader *reader, float frame); +PTCReadSampleResult PTC_test_sample(struct PTCReader *reader, float frame); + +void PTC_get_archive_info_stream(struct PTCReaderArchive *archive, void (*stream)(void *, const char *), void *userdata); +void PTC_get_archive_info(struct PTCReaderArchive *_archive, struct CacheArchiveInfo *info, struct IDProperty *metadata); +void PTC_get_archive_info_nodes(struct PTCReaderArchive *_archive, struct CacheArchiveInfo *info, bool calc_bytes_size); + +void PTC_archive_slice(struct PTCReaderArchive *in, struct PTCWriterArchive *out, float start_frame, float end_frame); + +struct PTCWriter *PTC_writer_dupligroup(const char *name, struct EvaluationContext *eval_ctx, struct Scene *scene, struct Group *group, struct CacheLibrary *cachelib); +struct PTCWriter *PTC_writer_duplicache(const char *name, struct Group *group, struct DupliCache *dupcache, int datatypes, bool do_sim_debug); + +struct PTCReader *PTC_reader_duplicache(const char *name, struct Group *group, struct DupliCache *dupcache, + bool read_strands_motion, bool read_strands_children, bool read_sim_debug); +struct PTCReader *PTC_reader_duplicache_object(const char *name, struct Object *ob, struct DupliObjectData *data, + bool read_strands_motion, bool read_strands_children); + +/* get writer/reader from RNA type */ +struct PTCWriter *PTC_writer_from_rna(struct Scene *scene, struct PointerRNA *ptr); +struct PTCReader *PTC_reader_from_rna(struct Scene *scene, struct PointerRNA *ptr); + +/* Object */ +struct PTCWriter *PTC_writer_object(const char *name, struct Scene *scene, struct Object *ob); +struct PTCReader *PTC_reader_object(const char *name, struct Object *ob); + +/* Group */ +struct PTCWriter *PTC_writer_group(const char *name, struct Group *group); +struct PTCReader *PTC_reader_group(const char *name, struct Group *group); + +/* Cloth */ +struct PTCWriter *PTC_writer_cloth(const char *name, struct Object *ob, struct ClothModifierData *clmd); +struct PTCReader *PTC_reader_cloth(const char *name, struct Object *ob, struct ClothModifierData *clmd); + +struct PTCWriter *PTC_writer_derived_mesh(const char *name, struct Object *ob, struct DerivedMesh **dm_ptr); +struct PTCReader *PTC_reader_derived_mesh(const char *name, struct Object *ob); +struct DerivedMesh *PTC_reader_derived_mesh_acquire_result(struct PTCReader *reader); +void PTC_reader_derived_mesh_discard_result(struct PTCReader *reader); + +struct PTCWriter *PTC_writer_derived_final_realtime(const char *name, struct Object *ob); +struct PTCWriter *PTC_writer_derived_final_render(const char *name, struct Scene *scene, struct Object *ob, struct DerivedMesh **render_dm_ptr); + +#ifdef __cplusplus +} /* extern C */ +#endif + +#endif /* __PTC_API_H__ */ diff --git a/source/blender/pointcache/SConscript b/source/blender/pointcache/SConscript new file mode 100644 index 00000000000..f94bbd43668 --- /dev/null +++ b/source/blender/pointcache/SConscript @@ -0,0 +1,58 @@ +#!/usr/bin/env python +# +# ***** 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# The Original Code is Copyright (C) 2014, Blender Foundation +# All rights reserved. +# +# The Original Code is: all of this file. +# +# Contributor(s): Lukas Toenne. +# +# ***** END GPL LICENSE BLOCK ***** + +Import ('env') + +sources = env.Glob('intern/*.cpp') + env.Glob('util/*.cpp') + env.Glob('*.cpp') + +incs = [ + '.', + 'intern', + 'util', + '../blenkernel', + '../blenlib', + '../makesdna', + '../makesrna', + '../../../intern/guardedalloc', + ] + +defs = [] + +if not env['WITH_BF_CPP11']: + incs.append(env['BF_BOOST_INC']) + +if env['WITH_BF_INTERNATIONAL']: + defs.append('WITH_INTERNATIONAL') + +if env['OURPLATFORM'] in ('win32-vc', 'win32-mingw', 'linuxcross', 'win64-vc', 'win64-mingw'): + incs.append(env['BF_PTHREADS_INC']) + +if env['WITH_BF_ALEMBIC']: + defs.append('WITH_PTC_ALEMBIC') + SConscript(['alembic/SConscript']) + +env.BlenderLib('bf_pointcache', sources, incs, defines=defs, libtype=['core','player'], priority=[902,902]) diff --git a/source/blender/pointcache/alembic/CMakeLists.txt b/source/blender/pointcache/alembic/CMakeLists.txt new file mode 100644 index 00000000000..d7391428d5d --- /dev/null +++ b/source/blender/pointcache/alembic/CMakeLists.txt @@ -0,0 +1,75 @@ +# ***** 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# The Original Code is Copyright (C) 2013, Blender Foundation +# All rights reserved. +# +# The Original Code is: all of this file. +# +# ***** END GPL LICENSE BLOCK ***** + +set(INC + . + ../ + ../intern + ../util + ../../blenkernel + ../../blenlib + ../../makesdna + ../../makesrna + ../../../../intern/guardedalloc +) + +set(INC_SYS + ${ALEMBIC_INCLUDE_DIRS} + ${OPENEXR_INCLUDE_DIR}/OpenEXR +) + +set(SRC + alembic.cpp + alembic.h + + abc_frame_mapper.cpp + abc_frame_mapper.h + abc_info.cpp + abc_interpolate.cpp + abc_interpolate.h + abc_reader.cpp + abc_reader.h + abc_schema.h + abc_split.cpp + abc_writer.cpp + abc_writer.h + + abc_cloth.cpp + abc_cloth.h + abc_customdata.cpp + abc_customdata.h + abc_group.cpp + abc_group.h + abc_mesh.cpp + abc_mesh.h + abc_object.cpp + abc_object.h + abc_particles.cpp + abc_particles.h + abc_simdebug.cpp + abc_simdebug.h +) + +add_definitions(-DWITH_ALEMBIC) + +blender_add_lib(bf_pointcache_alembic "${SRC}" "${INC}" "${INC_SYS}") diff --git a/source/blender/pointcache/alembic/SConscript b/source/blender/pointcache/alembic/SConscript new file mode 100644 index 00000000000..b9da2df57c2 --- /dev/null +++ b/source/blender/pointcache/alembic/SConscript @@ -0,0 +1,62 @@ +#!/usr/bin/env python +# +# ***** 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# The Original Code is Copyright (C) 2014, Blender Foundation +# All rights reserved. +# +# The Original Code is: all of this file. +# +# Contributor(s): Lukas Toenne. +# +# ***** END GPL LICENSE BLOCK ***** + +Import ('env') + +sources = env.Glob('*.cpp') + +incs = [ + '.', + '../', + '../intern', + '../util', + '../../blenkernel', + '../../blenlib', + '../../makesdna', + '../../makesrna', + '../../../../intern/guardedalloc', + ] + +incs += Split(env['BF_OPENEXR_INC']) + +defs = [] + +if env['WITH_BF_INTERNATIONAL']: + defs.append('WITH_INTERNATIONAL') + +if env['OURPLATFORM'] in ('win32-vc', 'win32-mingw', 'linuxcross', 'win64-vc', 'win64-mingw'): + incs.append(env['BF_PTHREADS_INC']) + +incs.append(env['BF_OPENEXR_INC']) +incs.append(env['BF_ALEMBIC_INC']) + +if not env['WITH_BF_CPP11']: + incs.append(env['BF_BOOST_INC']) + +defs.append('WITH_ALEMBIC') + +env.BlenderLib('bf_pointcache_alembic', sources, incs, defines=defs, libtype=['core','player'], priority=[901, 901]) diff --git a/source/blender/pointcache/alembic/abc_cloth.cpp b/source/blender/pointcache/alembic/abc_cloth.cpp new file mode 100644 index 00000000000..a722bc2b725 --- /dev/null +++ b/source/blender/pointcache/alembic/abc_cloth.cpp @@ -0,0 +1,237 @@ +/* + * Copyright 2013, Blender Foundation. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "abc_cloth.h" + +extern "C" { +#include "BLI_math.h" + +#include "DNA_cloth_types.h" +#include "DNA_object_types.h" +#include "DNA_modifier_types.h" + +#include "BKE_cloth.h" +} + +#include "PTC_api.h" + +namespace PTC { + +using namespace Abc; +using namespace AbcGeom; + +AbcClothWriter::AbcClothWriter(const std::string &name, Object *ob, ClothModifierData *clmd) : + ClothWriter(ob, clmd, name) +{ + set_error_handler(new ModifierErrorHandler(&clmd->modifier)); +} + +AbcClothWriter::~AbcClothWriter() +{ +} + +void AbcClothWriter::init_abc(OObject parent) +{ + if (m_points) + return; + + m_points = OPoints(parent, m_name, abc_archive()->frame_sampling_index()); + + OPointsSchema &schema = m_points.getSchema(); + OCompoundProperty geom_params = schema.getArbGeomParams(); + + m_param_velocities = OV3fGeomParam(geom_params, "velocities", false, kVaryingScope, 1, 0); + m_param_goal_positions = OP3fGeomParam(geom_params, "goal_positions", false, kVaryingScope, 1, 0); +} + +static V3fArraySample create_sample_velocities(Cloth *cloth, std::vector<V3f> &data) +{ + ClothVertex *vert; + int i, totvert = cloth->numverts; + + data.reserve(totvert); + for (i = 0, vert = cloth->verts; i < totvert; ++i, ++vert) { + float *co = vert->v; + data.push_back(V3f(co[0], co[1], co[2])); + } + + return V3fArraySample(data); +} + +static P3fArraySample create_sample_goal_positions(Cloth *cloth, std::vector<V3f> &data) +{ + ClothVertex *vert; + int i, totvert = cloth->numverts; + + data.reserve(totvert); + for (i = 0, vert = cloth->verts; i < totvert; ++i, ++vert) { + float *co = vert->xconst; + data.push_back(V3f(co[0], co[1], co[2])); + } + + return P3fArraySample(data); +} + +void AbcClothWriter::write_sample() +{ + if (!m_points) + return; + + Cloth *cloth = m_clmd->clothObject; + if (!cloth) + return; + + OPointsSchema &schema = m_points.getSchema(); + + int totpoint = cloth->numverts; + ClothVertex *vert; + int i; + + /* XXX TODO only needed for the first frame/sample */ + std::vector<Util::uint64_t> ids; + ids.reserve(totpoint); + for (i = 0, vert = cloth->verts; i < totpoint; ++i, ++vert) + ids.push_back(i); + + std::vector<V3f> positions; + positions.reserve(totpoint); + for (i = 0, vert = cloth->verts; i < totpoint; ++i, ++vert) { + float *co = vert->x; + positions.push_back(V3f(co[0], co[1], co[2])); + } + + std::vector<V3f> velocities_buffer; + std::vector<V3f> goal_positions_buffer; + V3fArraySample velocities = create_sample_velocities(cloth, velocities_buffer); + P3fArraySample goal_positions = create_sample_goal_positions(cloth, goal_positions_buffer); + + OPointsSchema::Sample sample = OPointsSchema::Sample(V3fArraySample(positions), UInt64ArraySample(ids)); + schema.set(sample); + + m_param_velocities.set(OV3fGeomParam::Sample(velocities, kVaryingScope)); + m_param_goal_positions.set(OP3fGeomParam::Sample(goal_positions, kVaryingScope)); +} + + +AbcClothReader::AbcClothReader(const std::string &name, Object *ob, ClothModifierData *clmd) : + ClothReader(ob, clmd, name) +{ + set_error_handler(new ModifierErrorHandler(&clmd->modifier)); +} + +AbcClothReader::~AbcClothReader() +{ +} + +void AbcClothReader::init_abc(IObject object) +{ + if (m_points) + return; + m_points = IPoints(object, kWrapExisting); + + IPointsSchema &schema = m_points.getSchema(); + ICompoundProperty geom_params = schema.getArbGeomParams(); + + m_param_velocities = IV3fGeomParam(geom_params, "velocities", 0); + m_param_goal_positions = IP3fGeomParam(geom_params, "goal_positions", 0); +} + +static void apply_sample_positions(Cloth *cloth, P3fArraySamplePtr sample) +{ + ClothVertex *vert; + int i, totvert = cloth->numverts; + + BLI_assert(sample->size() == totvert); + + const V3f *data = sample->get(); + for (i = 0, vert = cloth->verts; i < totvert; ++i, ++vert) { + const V3f &co = data[i]; + copy_v3_v3(vert->x, co.getValue()); + } +} + +static void apply_sample_velocities(Cloth *cloth, V3fArraySamplePtr sample) +{ + ClothVertex *vert; + int i, totvert = cloth->numverts; + + BLI_assert(sample->size() == totvert); + + const V3f *data = sample->get(); + for (i = 0, vert = cloth->verts; i < totvert; ++i, ++vert) { + const V3f &vel = data[i]; + copy_v3_v3(vert->v, vel.getValue()); + } +} + +static void apply_sample_goal_positions(Cloth *cloth, P3fArraySamplePtr sample) +{ + ClothVertex *vert; + int i, totvert = cloth->numverts; + + BLI_assert(sample->size() == totvert); + + const V3f *data = sample->get(); + for (i = 0, vert = cloth->verts; i < totvert; ++i, ++vert) { + const V3f &co = data[i]; + copy_v3_v3(vert->xconst, co.getValue()); + } +} + +PTCReadSampleResult AbcClothReader::read_sample_abc(chrono_t time) +{ + Cloth *cloth = m_clmd->clothObject; + + if (!m_points) + return PTC_READ_SAMPLE_INVALID; + + IPointsSchema &schema = m_points.getSchema(); + if (schema.getNumSamples() == 0) + return PTC_READ_SAMPLE_INVALID; + + ISampleSelector ss = get_frame_sample_selector(time); + + IPointsSchema::Sample sample; + schema.get(sample, ss); + + P3fArraySamplePtr positions = sample.getPositions(); + + V3fArraySamplePtr velocities; + if (m_param_velocities && m_param_velocities.getNumSamples() > 0) { + IV3fGeomParam::Sample sample_velocities; + m_param_velocities.getExpanded(sample_velocities, ss); + velocities = sample_velocities.getVals(); + } + + P3fArraySamplePtr goal_positions; + if (m_param_goal_positions && m_param_goal_positions.getNumSamples() > 0) { + IP3fGeomParam::Sample sample_goal_positions; + m_param_goal_positions.getExpanded(sample_goal_positions, ss); + goal_positions = sample_goal_positions.getVals(); + } + + apply_sample_positions(cloth, positions); + if (velocities) + apply_sample_velocities(cloth, velocities); + if (goal_positions) + apply_sample_goal_positions(cloth, goal_positions); + + return PTC_READ_SAMPLE_EXACT; +} + +} /* namespace PTC */ diff --git a/source/blender/pointcache/alembic/abc_cloth.h b/source/blender/pointcache/alembic/abc_cloth.h new file mode 100644 index 00000000000..9b30b183a6c --- /dev/null +++ b/source/blender/pointcache/alembic/abc_cloth.h @@ -0,0 +1,68 @@ +/* + * Copyright 2013, Blender Foundation. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef PTC_ABC_CLOTH_H +#define PTC_ABC_CLOTH_H + +#include <Alembic/AbcGeom/IPoints.h> +#include <Alembic/AbcGeom/OPoints.h> + +#include "ptc_types.h" + +#include "abc_reader.h" +#include "abc_schema.h" +#include "abc_writer.h" + +struct Object; +struct ClothModifierData; + +namespace PTC { + +class AbcClothWriter : public ClothWriter, public AbcWriter { +public: + AbcClothWriter(const std::string &name, Object *ob, ClothModifierData *clmd); + ~AbcClothWriter(); + + void init_abc(Abc::OObject parent); + + void write_sample(); + +private: + AbcGeom::OPoints m_points; + AbcGeom::OV3fGeomParam m_param_velocities; + AbcGeom::OP3fGeomParam m_param_goal_positions; +}; + +class AbcClothReader : public ClothReader, public AbcReader { +public: + AbcClothReader(const std::string &name, Object *ob, ClothModifierData *clmd); + ~AbcClothReader(); + + void init_abc(Abc::IObject parent); + + PTCReadSampleResult read_sample_abc(chrono_t time); + +private: + AbcGeom::IPoints m_points; + AbcGeom::IV3fGeomParam m_param_velocities; + AbcGeom::IP3fGeomParam m_param_goal_positions; +}; + +} /* namespace PTC */ + +#endif /* PTC_CLOTH_H */ diff --git a/source/blender/pointcache/alembic/abc_customdata.cpp b/source/blender/pointcache/alembic/abc_customdata.cpp new file mode 100644 index 00000000000..77a9ea73a5b --- /dev/null +++ b/source/blender/pointcache/alembic/abc_customdata.cpp @@ -0,0 +1,804 @@ +/* + * Copyright 2015, Blender Foundation. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include <sstream> + +#include <Alembic/AbcGeom/IGeomParam.h> +#include <Alembic/AbcGeom/OGeomParam.h> + +#include "abc_customdata.h" + +extern "C" { +#include "MEM_guardedalloc.h" + +#include "BLI_math.h" +#include "BLI_utildefines.h" + +#include "DNA_customdata_types.h" +#include "DNA_meshdata_types.h" + +#include "BKE_customdata.h" +} + +namespace PTC { + +using namespace Abc; +using namespace AbcGeom; + +/* DEBUG */ +BLI_INLINE void print_writer_compound(OCompoundProperty &prop) +{ + CompoundPropertyWriterPtr ptr = prop.getPtr()->asCompoundPtr(); + printf("compound %s: [%p] (%d)\n", ptr->getName().c_str(), ptr.get(), (int)ptr->getNumProperties()); + for (int i = 0; i < ptr->getNumProperties(); ++i) { + printf(" %d: [%p]\n", i, prop.getProperty(i).getPtr().get()); + printf(" %s\n", prop.getProperty(i).getName().c_str()); + } +} + +/* ========================================================================= */ + +template <CustomDataType CDTYPE> +static void write_sample(CustomDataWriter */*writer*/, OCompoundProperty &/*parent*/, const std::string &/*name*/, void */*data*/, int /*num_data*/) +{ + /* no implementation available, should not happen */ + printf("ERROR: CustomData type %s has no write_sample implementation\n", CustomData_layertype_name((int)CDTYPE)); +} + +template <> +void write_sample<CD_MDEFORMVERT>(CustomDataWriter *writer, OCompoundProperty &parent, const std::string &name, void *data, int num_data) +{ + OCompoundProperty prop = writer->add_compound_property<OCompoundProperty>(name, parent); + + OInt32ArrayProperty totweight_prop = writer->add_array_property<OInt32ArrayProperty>(name + ":totweight", prop); + OInt32ArrayProperty flag_prop = writer->add_array_property<OInt32ArrayProperty>(name + ":flag", prop); + OInt32ArrayProperty def_nr_prop = writer->add_array_property<OInt32ArrayProperty>(name + ":def_nr", prop); + OFloatArrayProperty weight_prop = writer->add_array_property<OFloatArrayProperty>(name + ":weight", prop); + + MDeformVert *mdef = (MDeformVert *)data; + + /* sum all totweight for the sample size */ + int num_mdefweight = 0; + for (int i = 0; i < num_data; ++i) + num_mdefweight += mdef[i].totweight; + + std::vector<int32_t> totweight_data; + std::vector<int32_t> flag_data; + std::vector<int32_t> def_nr_data; + std::vector<float> weight_data; + totweight_data.reserve(num_data); + flag_data.reserve(num_data); + def_nr_data.reserve(num_mdefweight); + weight_data.reserve(num_mdefweight); + + for (int i = 0; i < num_data; ++i) { + totweight_data.push_back(mdef->totweight); + flag_data.push_back(mdef->flag); + + MDeformWeight *mw = mdef->dw; + for (int j = 0; j < mdef->totweight; ++j) { + def_nr_data.push_back(mw->def_nr); + weight_data.push_back(mw->weight); + + ++mw; + } + + ++mdef; + } + + totweight_prop.set(Int32ArraySample(totweight_data)); + flag_prop.set(Int32ArraySample(flag_data)); + def_nr_prop.set(Int32ArraySample(def_nr_data)); + weight_prop.set(FloatArraySample(weight_data)); +} + +template <> +void write_sample<CD_MTFACE>(CustomDataWriter *writer, OCompoundProperty &parent, const std::string &name, void */*data*/, int /*num_data*/) +{ + /* XXX this is a dummy layer, to have access to active render layers etc. */ + writer->add_compound_property<OCompoundProperty>(name, parent); +} + +template <> +void write_sample<CD_MCOL>(CustomDataWriter *writer, OCompoundProperty &parent, const std::string &name, void *data, int num_data) +{ + OC4fArrayProperty prop = writer->add_array_property<OC4fArrayProperty>(name, parent); + + MCol *mcol = (MCol *)data; + + std::vector<C4f> mcol_data; + mcol_data.reserve(num_data); + for (int i = 0; i < num_data; ++i) { + unsigned char icol[4] = {mcol->r, mcol->g, mcol->b, mcol->a}; + C4f fcol; + rgba_uchar_to_float(fcol.getValue(), icol); + mcol_data.push_back(fcol); + + ++mcol; + } + prop.set(OC4fArrayProperty::sample_type(mcol_data)); +} + +template <> +void write_sample<CD_ORIGINDEX>(CustomDataWriter *writer, OCompoundProperty &parent, const std::string &name, void *data, int num_data) +{ + OInt32ArrayProperty prop = writer->add_array_property<OInt32ArrayProperty>(name, parent); + + prop.set(OInt32ArrayProperty::sample_type((int *)data, num_data)); +} + +template <> +void write_sample<CD_NORMAL>(CustomDataWriter *writer, OCompoundProperty &parent, const std::string &name, void *data, int num_data) +{ + ON3fArrayProperty prop = writer->add_array_property<ON3fArrayProperty>(name, parent); + + prop.set(ON3fArrayProperty::sample_type((N3f *)data, num_data)); +} + +template <> +void write_sample<CD_ORIGSPACE>(CustomDataWriter *writer, OCompoundProperty &parent, const std::string &name, void *data, int num_data) +{ + OCompoundProperty prop = writer->add_compound_property<OCompoundProperty>(name, parent); + + OV2fArrayProperty uv_prop[4]; + uv_prop[0] = writer->add_array_property<OV2fArrayProperty>(name + ":uv0", prop); + uv_prop[1] = writer->add_array_property<OV2fArrayProperty>(name + ":uv1", prop); + uv_prop[2] = writer->add_array_property<OV2fArrayProperty>(name + ":uv2", prop); + uv_prop[3] = writer->add_array_property<OV2fArrayProperty>(name + ":uv3", prop); + + OrigSpaceFace *ospace = (OrigSpaceFace *)data; + std::vector<V2f> uv_data[4]; + uv_data[0].reserve(num_data); + uv_data[1].reserve(num_data); + uv_data[2].reserve(num_data); + uv_data[3].reserve(num_data); + for (int i = 0; i < num_data; ++i) { + uv_data[0].push_back(V2f(ospace->uv[0][0], ospace->uv[0][1])); + uv_data[1].push_back(V2f(ospace->uv[1][0], ospace->uv[1][1])); + uv_data[2].push_back(V2f(ospace->uv[2][0], ospace->uv[2][1])); + uv_data[3].push_back(V2f(ospace->uv[3][0], ospace->uv[3][1])); + + ++ospace; + } + uv_prop[0].set(V2fArraySample(uv_data[0])); + uv_prop[1].set(V2fArraySample(uv_data[1])); + uv_prop[2].set(V2fArraySample(uv_data[2])); + uv_prop[3].set(V2fArraySample(uv_data[3])); +} + +template <> +void write_sample<CD_ORCO>(CustomDataWriter *writer, OCompoundProperty &parent, const std::string &name, void *data, int num_data) +{ + OV3fArrayProperty prop = writer->add_array_property<OV3fArrayProperty>(name, parent); + + prop.set(OV3fArrayProperty::sample_type((V3f *)data, num_data)); +} + +template <> +void write_sample<CD_MTEXPOLY>(CustomDataWriter *writer, OCompoundProperty &parent, const std::string &name, void */*data*/, int /*num_data*/) +{ + /* XXX this is a dummy layer, to have access to active render layers etc. */ + writer->add_compound_property<OCompoundProperty>(name, parent); +} + +template <> +void write_sample<CD_MLOOPUV>(CustomDataWriter *writer, OCompoundProperty &parent, const std::string &name, void *data, int num_data) +{ + OCompoundProperty prop = writer->add_compound_property<OCompoundProperty>(name, parent); + + OV2fArrayProperty prop_uv = writer->add_array_property<OV2fArrayProperty>(name + ":uv", prop); + OInt32ArrayProperty prop_flag = writer->add_array_property<OInt32ArrayProperty>(name + ":flag", prop); + + MLoopUV *loop_uv = (MLoopUV *)data; + std::vector<V2f> uv_data; + std::vector<int32_t> flag_data; + uv_data.reserve(num_data); + flag_data.reserve(num_data); + for (int i = 0; i < num_data; ++i) { + uv_data.push_back(V2f(loop_uv->uv[0], loop_uv->uv[1])); + flag_data.push_back(loop_uv->flag); + + ++loop_uv; + } + prop_uv.set(V2fArraySample(uv_data)); + prop_flag.set(Int32ArraySample(flag_data)); +} + +template <> +void write_sample<CD_MLOOPCOL>(CustomDataWriter *writer, OCompoundProperty &parent, const std::string &name, void *data, int num_data) +{ + OCompoundProperty prop = writer->add_compound_property<OCompoundProperty>(name, parent); + + OC4fArrayProperty prop_col = writer->add_array_property<OC4fArrayProperty>(name + ":color", prop); + + MLoopCol *loop_col = (MLoopCol *)data; + std::vector<C4f> col_data; + col_data.reserve(num_data); + for (int i = 0; i < num_data; ++i) { + col_data.push_back(C4f(loop_col->r, loop_col->g, loop_col->b, loop_col->a)); + + ++loop_col; + } + prop_col.set(C4fArraySample(col_data)); +} + +template <> +void write_sample<CD_ORIGSPACE_MLOOP>(CustomDataWriter *writer, OCompoundProperty &parent, const std::string &name, void *data, int num_data) +{ + OCompoundProperty prop = writer->add_compound_property<OCompoundProperty>(name, parent); + + OV2fArrayProperty prop_uv = writer->add_array_property<OV2fArrayProperty>(name + ":uv", prop); + + OrigSpaceLoop *ospaceloop = (OrigSpaceLoop *)data; + std::vector<V2f> uv_data; + uv_data.reserve(num_data); + for (int i = 0; i < num_data; ++i) { + uv_data.push_back(V2f(ospaceloop->uv[0], ospaceloop->uv[1])); + + ++ospaceloop; + } + prop_uv.set(V2fArraySample(uv_data)); +} + +template <> +void write_sample<CD_MSURFACE_SAMPLE>(CustomDataWriter *writer, OCompoundProperty &parent, const std::string &name, void *data, int num_data) +{ + OCompoundProperty prop = writer->add_compound_property<OCompoundProperty>(name, parent); + + OUInt32ArrayProperty prop_orig_verts = writer->add_array_property<OUInt32ArrayProperty>(name + ":orig_verts", prop); + OFloatArrayProperty prop_orig_weights = writer->add_array_property<OFloatArrayProperty>(name + ":orig_weights", prop); + OInt32ArrayProperty prop_orig_poly = writer->add_array_property<OInt32ArrayProperty>(name + ":orig_poly", prop); + OUInt32ArrayProperty prop_orig_loops = writer->add_array_property<OUInt32ArrayProperty>(name + ":orig_loops", prop); + + MSurfaceSample *surf = (MSurfaceSample *)data; + std::vector<uint32_t> orig_verts_data; + std::vector<float32_t> orig_weights_data; + std::vector<int32_t> orig_poly_data; + std::vector<uint32_t> orig_loops_data; + orig_verts_data.reserve(num_data * 3); + orig_weights_data.reserve(num_data * 3); + orig_poly_data.reserve(num_data); + orig_loops_data.reserve(num_data * 3); + for (int i = 0; i < num_data; ++i) { + orig_verts_data.push_back(surf->orig_verts[0]); + orig_verts_data.push_back(surf->orig_verts[1]); + orig_verts_data.push_back(surf->orig_verts[2]); + orig_weights_data.push_back(surf->orig_weights[0]); + orig_weights_data.push_back(surf->orig_weights[1]); + orig_weights_data.push_back(surf->orig_weights[2]); + orig_poly_data.push_back(surf->orig_poly); + orig_loops_data.push_back(surf->orig_loops[0]); + orig_loops_data.push_back(surf->orig_loops[1]); + orig_loops_data.push_back(surf->orig_loops[2]); + + ++surf; + } + prop_orig_verts.set(UInt32ArraySample(orig_verts_data)); + prop_orig_weights.set(FloatArraySample(orig_weights_data)); + prop_orig_poly.set(Int32ArraySample(orig_poly_data)); + prop_orig_loops.set(UInt32ArraySample(orig_loops_data)); +} + +/* ------------------------------------------------------------------------- */ + +template <CustomDataType CDTYPE> +static PTCReadSampleResult read_sample(CustomDataReader */*reader*/, ICompoundProperty &/*parent*/, const ISampleSelector &/*ss*/, const std::string &/*name*/, void */*data*/, int /*num_data*/) +{ + /* no implementation available, should not happen */ + printf("ERROR: CustomData type %s has no read_sample implementation\n", CustomData_layertype_name((int)CDTYPE)); + return PTC_READ_SAMPLE_INVALID; +} + +template <> +PTCReadSampleResult read_sample<CD_MDEFORMVERT>(CustomDataReader *reader, ICompoundProperty &parent, const ISampleSelector &ss, const std::string &name, void *data, int num_data) +{ + ICompoundProperty prop = reader->add_compound_property<ICompoundProperty>(name, parent); + + IInt32ArrayProperty totweight_prop = reader->add_array_property<IInt32ArrayProperty>(name + ":totweight", prop); + IInt32ArrayProperty flag_prop = reader->add_array_property<IInt32ArrayProperty>(name + ":flag", prop); + IInt32ArrayProperty def_nr_prop = reader->add_array_property<IInt32ArrayProperty>(name + ":def_nr", prop); + IFloatArrayProperty weight_prop = reader->add_array_property<IFloatArrayProperty>(name + ":weight", prop); + + Int32ArraySamplePtr sample_totweight = totweight_prop.getValue(ss); + Int32ArraySamplePtr sample_flag = flag_prop.getValue(ss); + Int32ArraySamplePtr sample_def_nr = def_nr_prop.getValue(ss); + FloatArraySamplePtr sample_weight = weight_prop.getValue(ss); + + if (sample_totweight->size() != num_data || + sample_flag->size() != num_data) + { + return PTC_READ_SAMPLE_INVALID; + } + + const int32_t *data_totweight = (const int32_t *)sample_totweight->getData(); + const int32_t *data_flag = (const int32_t *)sample_flag->getData(); + const int32_t *data_def_nr = (const int32_t *)sample_def_nr->getData(); + const float *data_weight = (const float *)sample_weight->getData(); + + MDeformVert *mdef = (MDeformVert *)data; + for (int i = 0; i < num_data; ++i) { + + mdef->totweight = *data_totweight; + mdef->flag = *data_flag; + + MDeformWeight *mw = mdef->dw = (MDeformWeight *)MEM_mallocN(sizeof(MDeformWeight) * mdef->totweight, "deformWeight"); + for (int j = 0; j < mdef->totweight; ++j) { + mw->def_nr = *data_def_nr; + mw->weight = *data_weight; + + ++data_def_nr; + ++data_weight; + ++mw; + } + + ++data_totweight; + ++data_flag; + ++mdef; + } + + return PTC_READ_SAMPLE_EXACT; +} + +template <> +PTCReadSampleResult read_sample<CD_MTFACE>(CustomDataReader */*reader*/, ICompoundProperty &/*parent*/, const ISampleSelector &/*ss*/, const std::string &/*name*/, void */*data*/, int /*num_data*/) +{ + /* XXX this is a dummy layer, to have access to active render layers etc. */ + return PTC_READ_SAMPLE_EXACT; +} + +template <> +PTCReadSampleResult read_sample<CD_MCOL>(CustomDataReader *reader, ICompoundProperty &parent, const ISampleSelector &ss, const std::string &name, void *data, int num_data) +{ + IC4fArrayProperty prop = reader->add_array_property<IC4fArrayProperty>(name, parent); + + C4fArraySamplePtr sample = prop.getValue(ss); + + if (sample->size() != num_data) + return PTC_READ_SAMPLE_INVALID; + + MCol *mcol = (MCol *)data; + C4f *data_mcol = (C4f *)sample->getData(); + for (int i = 0; i < num_data; ++i) { + unsigned char icol[4]; + rgba_float_to_uchar(icol, data_mcol->getValue()); + mcol->r = icol[0]; + mcol->g = icol[1]; + mcol->b = icol[2]; + mcol->a = icol[3]; + + ++data_mcol; + ++mcol; + } + return PTC_READ_SAMPLE_EXACT; +} + +template <> +PTCReadSampleResult read_sample<CD_ORIGINDEX>(CustomDataReader *reader, ICompoundProperty &parent, const ISampleSelector &ss, const std::string &name, void *data, int num_data) +{ + IInt32ArrayProperty prop = reader->add_array_property<IInt32ArrayProperty>(name, parent); + + Int32ArraySamplePtr sample = prop.getValue(ss); + + if (sample->size() != num_data) + return PTC_READ_SAMPLE_INVALID; + + memcpy(data, sample->getData(), sizeof(int32_t) * num_data); + return PTC_READ_SAMPLE_EXACT; +} + +template <> +PTCReadSampleResult read_sample<CD_NORMAL>(CustomDataReader *reader, ICompoundProperty &parent, const ISampleSelector &ss, const std::string &name, void *data, int num_data) +{ + IN3fArrayProperty prop = reader->add_array_property<IN3fArrayProperty>(name, parent); + + N3fArraySamplePtr sample = prop.getValue(ss); + + if (sample->size() != num_data) + return PTC_READ_SAMPLE_INVALID; + + memcpy(data, sample->getData(), sizeof(N3f) * num_data); + return PTC_READ_SAMPLE_EXACT; +} + +template <> +PTCReadSampleResult read_sample<CD_ORIGSPACE>(CustomDataReader *reader, ICompoundProperty &parent, const ISampleSelector &ss, const std::string &name, void *data, int num_data) +{ + ICompoundProperty prop = reader->add_compound_property<ICompoundProperty>(name, parent); + + IV2fArrayProperty uv_prop[4]; + uv_prop[0] = reader->add_array_property<IV2fArrayProperty>(name + ":uv0", prop); + uv_prop[1] = reader->add_array_property<IV2fArrayProperty>(name + ":uv1", prop); + uv_prop[2] = reader->add_array_property<IV2fArrayProperty>(name + ":uv2", prop); + uv_prop[3] = reader->add_array_property<IV2fArrayProperty>(name + ":uv3", prop); + + V2fArraySamplePtr sample0 = uv_prop[0].getValue(ss); + V2fArraySamplePtr sample1 = uv_prop[1].getValue(ss); + V2fArraySamplePtr sample2 = uv_prop[2].getValue(ss); + V2fArraySamplePtr sample3 = uv_prop[3].getValue(ss); + + if (sample0->size() != num_data || + sample1->size() != num_data || + sample2->size() != num_data || + sample3->size() != num_data) + { + return PTC_READ_SAMPLE_INVALID; + } + + OrigSpaceFace *ospace = (OrigSpaceFace *)data; + const V2f *data0 = (const V2f *)sample0->getData(); + const V2f *data1 = (const V2f *)sample1->getData(); + const V2f *data2 = (const V2f *)sample2->getData(); + const V2f *data3 = (const V2f *)sample3->getData(); + for (int i = 0; i < num_data; ++i) { + copy_v2_v2(ospace->uv[0], data0->getValue()); + copy_v2_v2(ospace->uv[1], data1->getValue()); + copy_v2_v2(ospace->uv[2], data2->getValue()); + copy_v2_v2(ospace->uv[3], data3->getValue()); + + ++data0; + ++data1; + ++data2; + ++data3; + ++ospace; + } + + return PTC_READ_SAMPLE_EXACT; +} + +template <> +PTCReadSampleResult read_sample<CD_ORCO>(CustomDataReader *reader, ICompoundProperty &parent, const ISampleSelector &ss, const std::string &name, void *data, int num_data) +{ + IV3fArrayProperty prop = reader->add_array_property<IV3fArrayProperty>(name, parent); + + V3fArraySamplePtr sample = prop.getValue(ss); + + if (sample->size() != num_data) + return PTC_READ_SAMPLE_INVALID; + + memcpy(data, sample->getData(), sizeof(V3f) * num_data); + return PTC_READ_SAMPLE_EXACT; +} + +template <> +PTCReadSampleResult read_sample<CD_MTEXPOLY>(CustomDataReader */*reader*/, ICompoundProperty &/*parent*/, const ISampleSelector &/*ss*/, const std::string &/*name*/, void */*data*/, int /*num_data*/) +{ + /* XXX this is a dummy layer, to have access to active render layers etc. */ + return PTC_READ_SAMPLE_EXACT; +} + +template <> +PTCReadSampleResult read_sample<CD_MLOOPUV>(CustomDataReader *reader, ICompoundProperty &parent, const ISampleSelector &ss, const std::string &name, void *data, int num_data) +{ + ICompoundProperty prop = reader->add_compound_property<ICompoundProperty>(name, parent); + + IV2fArrayProperty uv_prop = reader->add_array_property<IV2fArrayProperty>(name + ":uv", prop); + IInt32ArrayProperty flag_prop = reader->add_array_property<IInt32ArrayProperty>(name + ":flag", prop); + + V2fArraySamplePtr uv_sample = uv_prop.getValue(ss); + Int32ArraySamplePtr flag_sample = flag_prop.getValue(ss); + + if (uv_sample->size() != num_data || flag_sample->size() != num_data) + return PTC_READ_SAMPLE_INVALID; + + MLoopUV *loop_uv = (MLoopUV *)data; + const V2f *uv_data = (const V2f *)uv_sample->getData(); + const int32_t *flag_data = (const int32_t *)flag_sample->getData(); + for (int i = 0; i < num_data; ++i) { + copy_v2_v2(loop_uv->uv, uv_data->getValue()); + loop_uv->flag = *flag_data; + + ++uv_data; + ++flag_data; + ++loop_uv; + } + + return PTC_READ_SAMPLE_EXACT; +} + +template <> +PTCReadSampleResult read_sample<CD_MLOOPCOL>(CustomDataReader *reader, ICompoundProperty &parent, const ISampleSelector &ss, const std::string &name, void *data, int num_data) +{ + ICompoundProperty prop = reader->add_compound_property<ICompoundProperty>(name, parent); + + IC4fArrayProperty col_prop = reader->add_array_property<IC4fArrayProperty>(name + ":color", prop); + + C4fArraySamplePtr col_sample = col_prop.getValue(ss); + + if (col_sample->size() != num_data) + return PTC_READ_SAMPLE_INVALID; + + MLoopCol *loop_col = (MLoopCol *)data; + const C4f *col_data = (const C4f *)col_sample->getData(); + for (int i = 0; i < num_data; ++i) { + loop_col->r = col_data->r; + loop_col->g = col_data->g; + loop_col->b = col_data->b; + loop_col->a = col_data->a; + + ++col_data; + ++loop_col; + } + + return PTC_READ_SAMPLE_EXACT; +} + +template <> +PTCReadSampleResult read_sample<CD_ORIGSPACE_MLOOP>(CustomDataReader *reader, ICompoundProperty &parent, const ISampleSelector &ss, const std::string &name, void *data, int num_data) +{ + ICompoundProperty prop = reader->add_compound_property<ICompoundProperty>(name, parent); + + IV2fArrayProperty uv_prop = reader->add_array_property<IV2fArrayProperty>(name + ":uv", prop); + + V2fArraySamplePtr sample = uv_prop.getValue(ss); + + if (sample->size() != num_data) + return PTC_READ_SAMPLE_INVALID; + + OrigSpaceLoop *ospace = (OrigSpaceLoop *)data; + const V2f *sample_data = (const V2f *)sample->getData(); + for (int i = 0; i < num_data; ++i) { + copy_v2_v2(ospace->uv, sample_data->getValue()); + + ++sample_data; + ++ospace; + } + + return PTC_READ_SAMPLE_EXACT; +} + +template <> +PTCReadSampleResult read_sample<CD_MSURFACE_SAMPLE>(CustomDataReader *reader, ICompoundProperty &parent, const ISampleSelector &ss, const std::string &name, void *data, int num_data) +{ + ICompoundProperty prop = reader->add_compound_property<ICompoundProperty>(name, parent); + + IUInt32ArrayProperty orig_verts_prop = reader->add_array_property<IUInt32ArrayProperty>(name + ":orig_verts", prop); + IFloatArrayProperty orig_weights_prop = reader->add_array_property<IFloatArrayProperty>(name + ":orig_weights", prop); + IInt32ArrayProperty orig_poly_prop = reader->add_array_property<IInt32ArrayProperty>(name + ":orig_poly", prop); + IUInt32ArrayProperty orig_loops_prop = reader->add_array_property<IUInt32ArrayProperty>(name + ":orig_loops", prop); + + UInt32ArraySamplePtr orig_verts_sample = orig_verts_prop.getValue(ss); + FloatArraySamplePtr orig_weights_sample = orig_weights_prop.getValue(ss); + Int32ArraySamplePtr orig_poly_sample = orig_poly_prop.getValue(ss); + UInt32ArraySamplePtr orig_loops_sample = orig_loops_prop.getValue(ss); + + if (orig_verts_sample->size() != num_data*3 || + orig_weights_sample->size() != num_data*3 || + orig_poly_sample->size() != num_data || + orig_loops_sample->size() != num_data*3) + return PTC_READ_SAMPLE_INVALID; + + MSurfaceSample *surf = (MSurfaceSample *)data; + const uint32_t *orig_verts_data = (const uint32_t *)orig_verts_sample->getData(); + const float32_t *orig_weights_data = (const float32_t *)orig_weights_sample->getData(); + const int32_t *orig_poly_data = (const int32_t *)orig_poly_sample->getData(); + const uint32_t *orig_loops_data = (const uint32_t *)orig_loops_sample->getData(); + for (int i = 0; i < num_data; ++i) { + surf->orig_verts[0] = orig_verts_data[0]; + surf->orig_verts[1] = orig_verts_data[1]; + surf->orig_verts[2] = orig_verts_data[2]; + surf->orig_weights[0] = orig_weights_data[0]; + surf->orig_weights[1] = orig_weights_data[1]; + surf->orig_weights[2] = orig_weights_data[2]; + surf->orig_poly = *orig_poly_data; + surf->orig_loops[0] = orig_loops_data[0]; + surf->orig_loops[1] = orig_loops_data[1]; + surf->orig_loops[2] = orig_loops_data[2]; + + orig_verts_data += 3; + orig_weights_data += 3; + orig_poly_data += 1; + orig_loops_data += 3; + ++surf; + } + + return PTC_READ_SAMPLE_EXACT; +} + +/* ========================================================================= */ + +/* recursive template that handles dispatch by CD layer type */ +template <int CDTYPE> +BLI_INLINE void write_sample_call(CustomDataWriter *writer, OCompoundProperty &parent, CustomDataType type, const std::string &name, void *data, int num_data) +{ + if (type == CDTYPE) + write_sample<(CustomDataType)CDTYPE>(writer, parent, name, data, num_data); + else + write_sample_call<CDTYPE + 1>(writer, parent, type, name, data, num_data); +} + +/* terminator specialization */ +template <> +void write_sample_call<CD_NUMTYPES>(CustomDataWriter */*writer*/, OCompoundProperty &/*parent*/, CustomDataType /*type*/, const std::string &/*name*/, void */*data*/, int /*num_data*/) +{ +} + +/* ------------------------------------------------------------------------- */ + +/* recursive template that handles dispatch by CD layer type */ +template <int CDTYPE> +BLI_INLINE PTCReadSampleResult read_sample_call(CustomDataReader *reader, ICompoundProperty &parent, const ISampleSelector &ss, CustomDataType type, const std::string &name, void *data, int num_data) +{ + if (type == CDTYPE) + return read_sample<(CustomDataType)CDTYPE>(reader, parent, ss, name, data, num_data); + else + return read_sample_call<CDTYPE + 1>(reader, parent, ss, type, name, data, num_data); +} + +/* terminator specialization */ +template <> +PTCReadSampleResult read_sample_call<CD_NUMTYPES>(CustomDataReader */*reader*/, ICompoundProperty &/*parent*/, const ISampleSelector &/*ss*/, CustomDataType /*type*/, const std::string &/*name*/, void */*data*/, int /*num_data*/) +{ + return PTC_READ_SAMPLE_INVALID; +} + +/* ========================================================================= */ + +CustomDataWriter::CustomDataWriter(const std::string &name, CustomDataMask cdmask) : + m_name(name), + m_cdmask(cdmask) +{ +} + +CustomDataWriter::~CustomDataWriter() +{ + for (LayerPropsMap::iterator it = m_layer_props.begin(); it != m_layer_props.end(); ++it) { + BasePropertyWriterPtr prop = it->second; + if (prop) + prop.reset(); + } +} + +void CustomDataWriter::init(TimeSamplingPtr time_sampling) +{ + m_time_sampling = time_sampling; +} + +/* unique property name based on either layer name or index */ +std::string CustomDataWriter::cdtype_to_name(CustomData *cdata, CustomDataType type, int n) +{ + const char *layertype_name = CustomData_layertype_name(type); + const char *layer_name = CustomData_get_layer_name(cdata, type, n); + std::string name; + if (layer_name && layer_name[0] != '\0') { + name = m_name + ":" + std::string(layertype_name) + ":S" + std::string(layer_name); + } + else { + std::stringstream ss; ss << n; + name = m_name + ":" + std::string(layertype_name) + ":N" + ss.str(); + } + return name; +} + +/* parse property name to CD layer name based on S or N prefix for named/unnamed layers */ +void CustomDataReader::cdtype_from_name(CustomData */*cdata*/, const std::string &name, int type, int *n, char *layer_name, int max_layer_name) +{ + const char *layertype_name = CustomData_layertype_name(type); + /* We can safely assume all properties in the compound share the correct prefix + * <m_name>:<layertype_name>: + * The layertype_name is only prepended to avoid name collisions + */ + const size_t start = m_name.size() + 1 + strlen(layertype_name) + 1; + + if (name.size() <= start) { + printf("ERROR: invalid CustomData layer property name '%s'\n", name.c_str()); + *n = -1; + layer_name[0] = '\0'; + } + else if (name[start] == 'S') { + /* named layer */ + *n = -1; + BLI_strncpy(layer_name, name.c_str() + start + 1, max_layer_name); + } + else if (name[start] == 'N') { + /* unnamed layer */ + std::istringstream ss(name.c_str() + start + 1); + ss >> (*n); + layer_name[0] = '\0'; + } + else { + *n = -1; + layer_name[0] = '\0'; + } +} + +void CustomDataWriter::write_sample(CustomData *cdata, int num_data, OCompoundProperty &parent) +{ + /* compound property for all CD layers in the CustomData instance */ + m_props = add_compound_property<OCompoundProperty>(m_name, parent); + + for (int type = 0; type < CD_NUMTYPES; ++type) { + CustomDataMask mask = (1ull << type); + /* only use specified types */ + if (!(mask & m_cdmask)) + continue; + + const char *layertype_name = CustomData_layertype_name(type); + int num = CustomData_number_of_layers(cdata, type); + + bool has_props = false; + OCompoundProperty layertype_props; + for (int n = 0; n < num; ++n) { + /* compound for all CD layers of the same type */ + if (!has_props) { + has_props = true; + layertype_props = add_compound_property<OCompoundProperty>(m_name + ":" + layertype_name, m_props); + } + + std::string name = cdtype_to_name(cdata, (CustomDataType)type, n); + void *data = CustomData_get_layer_n(cdata, type, n); + write_sample_call<0>(this, layertype_props, (CustomDataType)type, name, data, num_data); + } + } +} + +/* ------------------------------------------------------------------------- */ + +CustomDataReader::CustomDataReader(const std::string &name, CustomDataMask cdmask) : + m_name(name), + m_cdmask(cdmask) +{ +} + +CustomDataReader::~CustomDataReader() +{ + for (LayerPropsMap::iterator it = m_layer_props.begin(); it != m_layer_props.end(); ++it) { + BasePropertyReaderPtr prop = it->second; + if (prop) + prop.reset(); + } +} + +PTCReadSampleResult CustomDataReader::read_sample(const ISampleSelector &ss, CustomData *cdata, int num_data, ICompoundProperty &parent) +{ + m_props = add_compound_property<ICompoundProperty>(m_name, parent); + + for (int type = 0; type < CD_NUMTYPES; ++type) { + CustomDataMask mask = (1ull << type); + /* only use specified types */ + if (!(mask & m_cdmask)) + continue; + + const char *layertype_name = CustomData_layertype_name(type); + + BasePropertyReaderPtr ptr = m_props.getPtr()->asCompoundPtr()->getProperty(m_name + ":" + layertype_name); + if (!ptr) { + /* no layer of this type stored */ + continue; + } + ICompoundProperty layertype_props(ptr->asCompoundPtr(), kWrapExisting); + + for (int i = 0; i < layertype_props.getNumProperties(); ++i) { + const std::string &name = layertype_props.getPropertyHeader(i).getName(); + char layer_name[MAX_CUSTOMDATA_LAYER_NAME]; + int n; + void *data; + + cdtype_from_name(cdata, name, type, &n, layer_name, sizeof(layer_name)); + if (layer_name[0] == '\0') + data = CustomData_add_layer(cdata, type, CD_DEFAULT, NULL, num_data); + else + data = CustomData_add_layer_named(cdata, type, CD_DEFAULT, NULL, num_data, layer_name); + + read_sample_call<0>(this, layertype_props, ss, (CustomDataType)type, name, data, num_data); + } + } + + return PTC_READ_SAMPLE_EXACT; +} + +} /* namespace PTC */ diff --git a/source/blender/pointcache/alembic/abc_customdata.h b/source/blender/pointcache/alembic/abc_customdata.h new file mode 100644 index 00000000000..9da3b92c584 --- /dev/null +++ b/source/blender/pointcache/alembic/abc_customdata.h @@ -0,0 +1,175 @@ +/* + * Copyright 2015, Blender Foundation. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef PTC_ABC_CUSTOMDATA_H +#define PTC_ABC_CUSTOMDATA_H + +#include <map> + +#include <Alembic/AbcGeom/IGeomParam.h> +#include <Alembic/AbcGeom/OGeomParam.h> +#include <Alembic/Abc/IBaseProperty.h> +#include <Alembic/Abc/TypedPropertyTraits.h> + +#include "abc_reader.h" +#include "abc_writer.h" + +extern "C" { +#include "BKE_customdata.h" + +#include "DNA_customdata_types.h" +} + +namespace PTC { + +using namespace Alembic; + +std::string abc_customdata_layer_name(CustomData *cdata, CustomDataType type, int n); + +struct CustomDataWriter { + typedef std::map<std::string, Abc::BasePropertyWriterPtr> LayerPropsMap; + typedef std::pair<std::string, Abc::BasePropertyWriterPtr> LayerPropsPair; + + CustomDataWriter(const std::string &name, CustomDataMask cdmask); + ~CustomDataWriter(); + + void init(Abc::TimeSamplingPtr time_sampling); + + void write_sample(CustomData *cdata, int num_data, Abc::OCompoundProperty &parent); + + Abc::OCompoundProperty &props() { return m_props; } + + template <typename PropertyT, typename ParentT> + PropertyT add_scalar_property(const std::string &name, ParentT &parent) + { + LayerPropsMap::iterator it = m_layer_props.find(name); + if (it == m_layer_props.end()) { + PropertyT prop = PropertyT(parent, name, m_time_sampling); + m_layer_props.insert(LayerPropsPair(name, prop.getPtr())); + return prop; + } + else { + return PropertyT(it->second->asScalarPtr(), Abc::kWrapExisting); + } + } + + template <typename PropertyT, typename ParentT> + PropertyT add_array_property(const std::string &name, ParentT &parent) + { + LayerPropsMap::iterator it = m_layer_props.find(name); + if (it == m_layer_props.end()) { + PropertyT prop = PropertyT(parent, name, m_time_sampling); + m_layer_props.insert(LayerPropsPair(name, prop.getPtr())); + return prop; + } + else { + return PropertyT(it->second->asArrayPtr(), Abc::kWrapExisting); + } + } + + template <typename PropertyT, typename ParentT> + PropertyT add_compound_property(const std::string &name, ParentT &parent) + { + LayerPropsMap::iterator it = m_layer_props.find(name); + if (it == m_layer_props.end()) { + PropertyT prop = PropertyT(parent, name, m_time_sampling); + m_layer_props.insert(LayerPropsPair(name, prop.getPtr())); + return prop; + } + else { + return PropertyT(it->second->asCompoundPtr(), Abc::kWrapExisting); + } + } + + std::string cdtype_to_name(CustomData *cdata, CustomDataType type, int n); + +private: + std::string m_name; + CustomDataMask m_cdmask; + + Abc::TimeSamplingPtr m_time_sampling; + Abc::OCompoundProperty m_props; + LayerPropsMap m_layer_props; +}; + +struct CustomDataReader { + typedef std::map<std::string, Abc::BasePropertyReaderPtr> LayerPropsMap; + typedef std::pair<std::string, Abc::BasePropertyReaderPtr> LayerPropsPair; + + CustomDataReader(const std::string &name, CustomDataMask cdmask); + ~CustomDataReader(); + + PTCReadSampleResult read_sample(const Abc::ISampleSelector &ss, CustomData *cdata, int num_data, Abc::ICompoundProperty &parent); + + Abc::ICompoundProperty &props() { return m_props; } + + template <typename PropertyT, typename ParentT> + PropertyT add_scalar_property(const std::string &name, ParentT &parent) + { + LayerPropsMap::iterator it = m_layer_props.find(name); + if (it == m_layer_props.end()) { + PropertyT prop = PropertyT(parent, name, 0); + m_layer_props.insert(LayerPropsPair(name, prop.getPtr())); + return prop; + } + else { + return PropertyT(it->second->asScalarPtr(), Abc::kWrapExisting); + } + } + + template <typename PropertyT, typename ParentT> + PropertyT add_array_property(const std::string &name, ParentT &parent) + { + LayerPropsMap::iterator it = m_layer_props.find(name); + if (it == m_layer_props.end()) { + PropertyT prop = PropertyT(parent, name, 0); + m_layer_props.insert(LayerPropsPair(name, prop.getPtr())); + return prop; + } + else { + return PropertyT(it->second->asArrayPtr(), Abc::kWrapExisting); + } + } + + template <typename PropertyT, typename ParentT> + PropertyT add_compound_property(const std::string &name, ParentT &parent) + { + LayerPropsMap::iterator it = m_layer_props.find(name); + if (it == m_layer_props.end()) { + PropertyT prop = PropertyT(parent, name, 0); + m_layer_props.insert(LayerPropsPair(name, prop.getPtr())); + return prop; + } + else { + return PropertyT(it->second->asCompoundPtr(), Abc::kWrapExisting); + } + } + + void cdtype_from_name(CustomData *cdata, const std::string &name, int type, int *n, char *layer_name, int max_layer_name); + +private: + std::string m_name; + CustomDataMask m_cdmask; + + Abc::ICompoundProperty m_props; + LayerPropsMap m_layer_props; +}; + +} /* namespace PTC */ + +#endif /* PTC_CLOTH_H */ diff --git a/source/blender/pointcache/alembic/abc_frame_mapper.cpp b/source/blender/pointcache/alembic/abc_frame_mapper.cpp new file mode 100644 index 00000000000..ba5ded10b6e --- /dev/null +++ b/source/blender/pointcache/alembic/abc_frame_mapper.cpp @@ -0,0 +1,52 @@ +/* + * Copyright 2013, Blender Foundation. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "abc_frame_mapper.h" + +extern "C" { +#include "DNA_scene_types.h" +} + +namespace PTC { + +#ifdef WITH_ALEMBIC + +using namespace Abc; +using namespace AbcCoreAbstract; + +FrameMapper::FrameMapper(double fps, float start_frame) +{ + m_frames_per_sec = fps; + m_sec_per_frame = (fps == 0.0 ? 0.0 : 1.0 / fps); + m_start_frame = start_frame; + m_start_time = start_frame * m_sec_per_frame; +} + +chrono_t FrameMapper::frame_to_time(float frame) const +{ + return (double)frame * m_sec_per_frame; +} + +float FrameMapper::time_to_frame(chrono_t time) const +{ + return (float)(time * m_frames_per_sec); +} + +#endif /* WITH_ALEMBIC */ + +} /* namespace PTC */ diff --git a/source/blender/pointcache/alembic/abc_frame_mapper.h b/source/blender/pointcache/alembic/abc_frame_mapper.h new file mode 100644 index 00000000000..b61baeeb4a7 --- /dev/null +++ b/source/blender/pointcache/alembic/abc_frame_mapper.h @@ -0,0 +1,57 @@ +/* + * Copyright 2013, Blender Foundation. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef PTC_ABC_FRAME_MAPPER_H +#define PTC_ABC_FRAME_MAPPER_H + +#ifdef WITH_ALEMBIC +#include <Alembic/AbcCoreAbstract/Foundation.h> +#include <Alembic/Abc/ISampleSelector.h> +#endif + +struct Scene; + +namespace PTC { + +#ifdef WITH_ALEMBIC + +using namespace Alembic; +using Alembic::AbcCoreAbstract::chrono_t; + +class FrameMapper { +public: + FrameMapper(double fps, float start_frame); + + double frames_per_second() const { return m_frames_per_sec; } + double seconds_per_frame() const { return m_sec_per_frame; } + double start_frame() const { return m_start_frame; } + double start_time() const { return m_start_time; } + + chrono_t frame_to_time(float frame) const; + float time_to_frame(chrono_t time) const; + +private: + double m_frames_per_sec, m_sec_per_frame; + double m_start_frame, m_start_time; +}; + +#endif /* WITH_ALEMBIC */ + +} /* namespace PTC */ + +#endif /* PTC_UTIL_FRAME_MAPPER_H */ diff --git a/source/blender/pointcache/alembic/abc_group.cpp b/source/blender/pointcache/alembic/abc_group.cpp new file mode 100644 index 00000000000..38cbc19694e --- /dev/null +++ b/source/blender/pointcache/alembic/abc_group.cpp @@ -0,0 +1,770 @@ +/* + * Copyright 2013, Blender Foundation. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include <map> +#include <sstream> +#include <string> + +#include <Alembic/Abc/IObject.h> +#include <Alembic/Abc/OObject.h> + +#include "abc_mesh.h" +#include "abc_group.h" +#include "abc_interpolate.h" +#include "abc_object.h" +#include "abc_particles.h" +#include "abc_simdebug.h" + +extern "C" { +#include "BLI_listbase.h" +#include "BLI_math.h" + +#include "DNA_group_types.h" +#include "DNA_object_types.h" + +#include "BKE_anim.h" +#include "BKE_cache_library.h" +#include "BKE_global.h" +#include "BKE_group.h" +#include "BKE_library.h" +#include "BKE_strands.h" +} + +#include "util_task.h" + +namespace PTC { + +using namespace Abc; +using namespace AbcGeom; + +AbcGroupWriter::AbcGroupWriter(const std::string &name, Group *group) : + GroupWriter(group, name) +{ +} + +void AbcGroupWriter::init_abc() +{ + if (m_abc_object) + return; + + m_abc_object = abc_archive()->add_id_object<OObject>((ID *)m_group); +} + +void AbcGroupWriter::create_refs() +{ + GroupObject *gob = (GroupObject *)m_group->gobject.first; + int i = 0; + for (; gob; gob = gob->next, ++i) { + OObject abc_object = abc_archive()->get_id_object((ID *)gob->ob); + if (abc_object) { + std::stringstream ss; + ss << i; + m_abc_object.addChildInstance(abc_object, std::string("group_object") + ss.str()); + } + } +} + +void AbcGroupWriter::write_sample() +{ + if (!m_abc_object) + return; +} + + +AbcGroupReader::AbcGroupReader(const std::string &name, Group *group) : + GroupReader(group, name) +{ +} + +void AbcGroupReader::init_abc(IObject object) +{ + if (m_abc_object) + return; + m_abc_object = object; +} + +PTCReadSampleResult AbcGroupReader::read_sample_abc(chrono_t /*time*/) +{ + if (!m_abc_object) + return PTC_READ_SAMPLE_INVALID; + + return PTC_READ_SAMPLE_EXACT; +} + +/* ========================================================================= */ + +AbcDupligroupWriter::AbcDupligroupWriter(const std::string &name, EvaluationContext *eval_ctx, Scene *scene, Group *group, CacheLibrary *cachelib) : + GroupWriter(group, name), + m_eval_ctx(eval_ctx), + m_scene(scene), + m_cachelib(cachelib) +{ +} + +AbcDupligroupWriter::~AbcDupligroupWriter() +{ + for (IDWriterMap::iterator it = m_id_writers.begin(); it != m_id_writers.end(); ++it) { + if (it->second) + delete it->second; + } +} + +void AbcDupligroupWriter::init_abc() +{ + if (m_abc_group) + return; + + m_abc_group = abc_archive()->add_id_object<OObject>((ID *)m_group); +} + +void AbcDupligroupWriter::write_sample_object(Object *ob, bool write_data) +{ + AbcWriter *ob_writer; + + /* TODO(sergey): Optimize this out using RW mutex. */ + { + thread_scoped_lock lock(m_init_mutex); + ob_writer = find_id_writer((ID *)ob); + if (!ob_writer) { + bool do_mesh = write_data && (m_cachelib->data_types & CACHE_TYPE_DERIVED_MESH); + bool do_hair = write_data && (m_cachelib->data_types & CACHE_TYPE_HAIR); + + ob_writer = new AbcObjectWriter(ob->id.name, m_scene, ob, do_mesh, do_hair); + ob_writer->init(abc_archive()); + m_id_writers.insert(IDWriterPair((ID *)ob, ob_writer)); + } + } + + ob_writer->write_sample(); +} + +void AbcDupligroupWriter::write_sample_dupli(DupliObject *dob, int index) +{ + OObject abc_object = abc_archive()->get_id_object((ID *)dob->ob); + if (!abc_object) + return; + + std::stringstream ss; + ss << "DupliObject" << index; + std::string name = ss.str(); + + OObject abc_dupli = m_abc_group.getChild(name); + OCompoundProperty props; + OM44fProperty prop_matrix; + OBoolProperty prop_visible; + if (!abc_dupli) { + abc_dupli = OObject(m_abc_group, name, 0); + m_object_writers.push_back(abc_dupli.getPtr()); + props = abc_dupli.getProperties(); + + abc_dupli.addChildInstance(abc_object, "object"); + + prop_matrix = OM44fProperty(props, "matrix", abc_archive()->frame_sampling()); + m_property_writers.push_back(prop_matrix.getPtr()); + prop_visible = OBoolProperty(props, "visible", abc_archive()->frame_sampling()); + m_property_writers.push_back(prop_visible.getPtr()); + } + else { + props = abc_dupli.getProperties(); + + prop_matrix = OM44fProperty(props.getProperty("matrix").getPtr()->asScalarPtr(), kWrapExisting); + prop_visible = OBoolProperty(props.getProperty("visible").getPtr()->asScalarPtr(), kWrapExisting); + } + + prop_matrix.set(M44f(dob->mat)); + + bool show_object = (abc_archive()->use_render())? !(dob->ob->restrictflag & OB_RESTRICT_RENDER) : !(dob->ob->restrictflag & OB_RESTRICT_VIEW); + bool visible = show_object && (!dob->no_draw); + prop_visible.set(visible); +} + +void AbcDupligroupWriter::write_sample() +{ + if (!m_abc_group) + return; + + ListBase *duplilist = group_duplilist_ex(m_eval_ctx, m_scene, m_group, true); + DupliObject *dob; + int i; + + /* use a set to ensure each object is handled only once */ + std::set<Object*> objects; + for (dob = (DupliObject *)duplilist->first; dob; dob = dob->next) { + if (dob->ob) + objects.insert(dob->ob); + } + + /* tag objects for which to store data */ + BKE_cache_library_tag_used_objects(m_cachelib); + + UtilTaskPool pool; + /* write actual object data: duplicator itself + all instanced objects */ + for (std::set<Object*>::const_iterator it = objects.begin(); it != objects.end(); ++it) { + Object *ob = *it; + bool write_data = (ob->id.flag & LIB_DOIT); + pool.push(function_bind(&AbcDupligroupWriter::write_sample_object, this, ob, write_data)); + } + + pool.wait_work(); + + /* write dupli instances */ + for (dob = (DupliObject *)duplilist->first, i = 0; dob; dob = dob->next, ++i) { + write_sample_dupli(dob, i); + } + + free_object_duplilist(duplilist); +} + +AbcWriter *AbcDupligroupWriter::find_id_writer(ID *id) const +{ + IDWriterMap::const_iterator it = m_id_writers.find(id); + if (it == m_id_writers.end()) + return NULL; + else + return it->second; +} + +/* ------------------------------------------------------------------------- */ + +AbcDupliCacheWriter::AbcDupliCacheWriter(const std::string &name, Group *group, DupliCache *dupcache, int data_types, bool do_sim_debug) : + GroupWriter(group, name), + m_dupcache(dupcache), + m_data_types(data_types), + m_simdebug_writer(NULL) +{ + if (do_sim_debug) { + BKE_sim_debug_data_set_enabled(true); + if (_sim_debug_data) + m_simdebug_writer = new AbcSimDebugWriter("sim_debug", _sim_debug_data); + } +} + +AbcDupliCacheWriter::~AbcDupliCacheWriter() +{ + for (IDWriterMap::iterator it = m_id_writers.begin(); it != m_id_writers.end(); ++it) { + if (it->second) + delete it->second; + } + + if (m_simdebug_writer) + delete m_simdebug_writer; +} + +void AbcDupliCacheWriter::init_abc() +{ + if (m_abc_group) + return; + + m_abc_group = abc_archive()->add_id_object<OObject>((ID *)m_group); + + if (m_simdebug_writer) { + m_simdebug_writer->init(abc_archive()); + m_simdebug_writer->init_abc(abc_archive()->root()); + } +} + +void AbcDupliCacheWriter::write_sample_object_data(DupliObjectData *data) +{ + AbcWriter *ob_writer = find_id_writer((ID *)data->ob); + if (!ob_writer) { + bool do_mesh = (m_data_types & CACHE_TYPE_DERIVED_MESH); + bool do_hair = (m_data_types & CACHE_TYPE_HAIR); + + ob_writer = new AbcDupliObjectWriter(data->ob->id.name, data, do_mesh, do_hair); + ob_writer->init(abc_archive()); + m_id_writers.insert(IDWriterPair((ID *)data->ob, ob_writer)); + } + + ob_writer->write_sample(); +} + +void AbcDupliCacheWriter::write_sample_dupli(DupliObject *dob, int index) +{ + OObject abc_object = abc_archive()->get_id_object((ID *)dob->ob); + if (!abc_object) + return; + + std::stringstream ss; + ss << "DupliObject" << index; + std::string name = ss.str(); + + OObject abc_dupli = m_abc_group.getChild(name); + OCompoundProperty props; + OBoolProperty prop_visible; + OM44fProperty prop_matrix; + if (!abc_dupli) { + abc_dupli = OObject(m_abc_group, name, 0); + m_object_writers.push_back(abc_dupli.getPtr()); + props = abc_dupli.getProperties(); + + abc_dupli.addChildInstance(abc_object, "object"); + + prop_matrix = OM44fProperty(props, "matrix", abc_archive()->frame_sampling()); + m_property_writers.push_back(prop_matrix.getPtr()); + prop_visible = OBoolProperty(props, "visible", abc_archive()->frame_sampling()); + m_property_writers.push_back(prop_visible.getPtr()); + } + else { + props = abc_dupli.getProperties(); + + prop_matrix = OM44fProperty(props.getProperty("matrix").getPtr()->asScalarPtr(), kWrapExisting); + prop_visible = OBoolProperty(props.getProperty("visible").getPtr()->asScalarPtr(), kWrapExisting); + } + + prop_matrix.set(M44f(dob->mat)); + + bool show_object = (abc_archive()->use_render())? !(dob->ob->restrictflag & OB_RESTRICT_RENDER) : !(dob->ob->restrictflag & OB_RESTRICT_VIEW); + bool visible = show_object && (!dob->no_draw); + prop_visible.set(visible); +} + +void AbcDupliCacheWriter::write_sample() +{ + if (!m_abc_group) + return; + + DupliObject *dob; + int i; + + struct DupliCacheIterator *iter = BKE_dupli_cache_iter_new(m_dupcache); + for (; BKE_dupli_cache_iter_valid(iter); BKE_dupli_cache_iter_next(iter)) { + DupliObjectData *data = BKE_dupli_cache_iter_get(iter); + + write_sample_object_data(data); + } + BKE_dupli_cache_iter_free(iter); + + /* write dupli instances */ + for (dob = (DupliObject *)m_dupcache->duplilist.first, i = 0; dob; dob = dob->next, ++i) { + write_sample_dupli(dob, i); + } + + if (m_simdebug_writer) { + m_simdebug_writer->write_sample(); + } +} + +AbcWriter *AbcDupliCacheWriter::find_id_writer(ID *id) const +{ + IDWriterMap::const_iterator it = m_id_writers.find(id); + if (it == m_id_writers.end()) + return NULL; + else + return it->second; +} + +/* ------------------------------------------------------------------------- */ + +AbcDupliCacheReader::AbcDupliCacheReader(const std::string &name, Group *group, DupliCache *dupli_cache, + bool read_strands_motion, bool read_strands_children, bool read_sim_debug) : + GroupReader(group, name), + dupli_cache(dupli_cache), + m_read_strands_motion(read_strands_motion), + m_read_strands_children(read_strands_children), + m_simdebug_reader(NULL) +{ + /* XXX this mapping allows fast lookup of existing objects in Blender data + * to associate with duplis. Later i may be possible to create instances of + * non-DNA data, but for the time being this is a requirement due to other code parts (drawing, rendering) + */ + build_object_map(G.main, group); + + if (read_sim_debug) { + BKE_sim_debug_data_set_enabled(true); + if (_sim_debug_data) + m_simdebug_reader = new AbcSimDebugReader(_sim_debug_data); + } +} + +AbcDupliCacheReader::~AbcDupliCacheReader() +{ + if (m_simdebug_reader) + delete m_simdebug_reader; +} + +void AbcDupliCacheReader::init_abc(IObject /*object*/) +{ +} + +void AbcDupliCacheReader::read_dupligroup_object(IObject object, chrono_t time) +{ + if (GS(object.getName().c_str()) == ID_OB) { + /* instances are handled later, we create true object data here */ + if (object.isInstanceDescendant()) + return; + + Object *b_ob = find_object(object.getName()); + if (!b_ob) + return; + + /* Always add dupli data, even if no geometry is stored. + * Any missing geometry data will be replaced by the original uncached data in drawing/rendering if available. + */ + DupliObjectData *dupli_data = BKE_dupli_cache_add_object(dupli_cache, b_ob); + insert_dupli_data(object.getPtr(), dupli_data); + + for (int i = 0; i < object.getNumChildren(); ++i) { + IObject child = object.getChild(i); + const MetaData &metadata = child.getMetaData(); + + if (IPolyMeshSchema::matches(metadata)) { + AbcDerivedMeshReader dm_reader("mesh", b_ob); + dm_reader.init(abc_archive()); + dm_reader.init_abc(child); + if (dm_reader.read_sample_abc(time) != PTC_READ_SAMPLE_INVALID) { + BKE_dupli_object_data_set_mesh(dupli_data, dm_reader.acquire_result()); + } + else { + dm_reader.discard_result(); + } + } + else if (ICurvesSchema::matches(metadata)) { + Strands *strands; + StrandsChildren *children; + BKE_dupli_object_data_find_strands(dupli_data, child.getName().c_str(), &strands, &children); + + AbcStrandsReader strands_reader(strands, children, m_read_strands_motion, m_read_strands_children); + strands_reader.init(abc_archive()); + strands_reader.init_abc(child); + if (strands_reader.read_sample_abc(time) != PTC_READ_SAMPLE_INVALID) { + Strands *newstrands = strands_reader.acquire_result(); + if (strands && strands != newstrands) { + /* reader can replace strands internally if topology does not match */ + BKE_strands_free(strands); + } + BKE_dupli_object_data_add_strands(dupli_data, child.getName().c_str(), newstrands); + + StrandsChildren *newchildren = strands_reader.child_reader().acquire_result(); + if (children && children != newchildren) { + /* reader can replace strands internally if topology does not match */ + BKE_strands_children_free(children); + } + BKE_dupli_object_data_add_strands_children(dupli_data, child.getName().c_str(), newchildren); + } + else { + strands_reader.discard_result(); + strands_reader.child_reader().discard_result(); + } + } + } + } +} + +void AbcDupliCacheReader::read_dupligroup_group(IObject abc_group, chrono_t time) +{ + ISampleSelector ss = get_frame_sample_selector(time); + + if (GS(abc_group.getName().c_str()) == ID_GR) { + size_t num_child = abc_group.getNumChildren(); + + for (size_t i = 0; i < num_child; ++i) { + IObject abc_dupli = abc_group.getChild(i); + ICompoundProperty props = abc_dupli.getProperties(); + + IM44fProperty prop_matrix(props, "matrix", 0); + M44f abc_matrix = abc_interpolate_sample_linear(prop_matrix, time); + float matrix[4][4]; + memcpy(matrix, abc_matrix.getValue(), sizeof(matrix)); + + IBoolProperty prop_visible(props, "visible", 0); + bool visible = prop_visible.getValue(ss); + + IObject abc_dupli_object = abc_dupli.getChild("object"); + if (abc_dupli_object.isInstanceRoot()) { + DupliObjectData *dupli_data = find_dupli_data(abc_dupli_object.getPtr()); + if (dupli_data) { + DupliObject *dob = BKE_dupli_cache_add_instance(dupli_cache, matrix, dupli_data); + dob->no_draw = !visible; + } + } + } + } +} + +PTCReadSampleResult AbcDupliCacheReader::read_sample_abc(chrono_t time) +{ + IObject abc_top = abc_archive()->root(); + IObject abc_group = abc_archive()->get_id_object((ID *)m_group); + if (!abc_group) + return PTC_READ_SAMPLE_INVALID; + + /* first create shared object data */ + for (size_t i = 0; i < abc_top.getNumChildren(); ++i) { + read_dupligroup_object(abc_top.getChild(i), time); + } + + BKE_dupli_cache_clear_instances(dupli_cache); + + /* now generate dupli instances for the group */ + read_dupligroup_group(abc_group, time); + + // XXX reader init is a mess ... + if (m_simdebug_reader) { + if (abc_top.getChildHeader("sim_debug")) { + m_simdebug_reader->init(abc_archive()); + m_simdebug_reader->init_abc(abc_top.getChild("sim_debug")); + + m_simdebug_reader->read_sample_abc(time); + } + } + + return PTC_READ_SAMPLE_EXACT; +} + +DupliObjectData *AbcDupliCacheReader::find_dupli_data(ObjectReaderPtr ptr) const +{ + DupliMap::const_iterator it = dupli_map.find(ptr); + if (it == dupli_map.end()) + return NULL; + else + return it->second; +} + +void AbcDupliCacheReader::insert_dupli_data(ObjectReaderPtr ptr, DupliObjectData *data) +{ + dupli_map.insert(DupliPair(ptr, data)); +} + +void AbcDupliCacheReader::build_object_map(Main *bmain, Group *group) +{ + BKE_main_id_tag_idcode(bmain, ID_OB, false); + BKE_main_id_tag_idcode(bmain, ID_GR, false); + object_map.clear(); + + build_object_map_add_group(group); +} + +Object *AbcDupliCacheReader::find_object(const std::string &name) const +{ + ObjectMap::const_iterator it = object_map.find(name); + if (it == object_map.end()) + return NULL; + else + return it->second; +} + +void AbcDupliCacheReader::build_object_map_add_group(Group *group) +{ + if (group->id.flag & LIB_DOIT) + return; + group->id.flag |= LIB_DOIT; + + for (GroupObject *gob = (GroupObject *)group->gobject.first; gob; gob = gob->next) { + Object *ob = gob->ob; + if (ob->id.flag & LIB_DOIT) + continue; + ob->id.flag |= LIB_DOIT; + object_map.insert(ObjectPair(ob->id.name, ob)); + + if ((ob->transflag & OB_DUPLIGROUP) && ob->dup_group) { + build_object_map_add_group(ob->dup_group); + } + } +} + +/* ------------------------------------------------------------------------- */ + +AbcDupliObjectWriter::AbcDupliObjectWriter(const std::string &name, DupliObjectData *dupdata, bool do_mesh, bool do_strands) : + ObjectWriter(dupdata->ob, name), + m_dupdata(dupdata), + m_do_strands(do_strands), + m_dm_writer(0) +{ + if (do_mesh) { + if (m_ob && m_ob->type == OB_MESH) { + m_dm_writer = new AbcDerivedMeshWriter("mesh", dupdata->ob, &dupdata->dm); + } + } +} + +AbcStrandsWriter *AbcDupliObjectWriter::find_strands_writer(const std::string &name) const +{ + StrandsWriters::const_iterator it = m_strands_writers.find(name); + return (it != m_strands_writers.end()) ? it->second : NULL; +} + +AbcStrandsWriter *AbcDupliObjectWriter::add_strands_writer(const std::string &name) +{ + StrandsWriters::const_iterator it = m_strands_writers.find(name); + if (it != m_strands_writers.end()) { + return it->second; + } + else { + AbcStrandsWriter *writer = new AbcStrandsWriter(name, m_dupdata); + m_strands_writers.insert(StrandsWritersPair(name, writer)); + + writer->init(abc_archive()); + writer->init_abc(m_abc_object); + + return writer; + } +} + +AbcDupliObjectWriter::~AbcDupliObjectWriter() +{ + if (m_dm_writer) + delete m_dm_writer; + for (StrandsWriters::iterator it = m_strands_writers.begin(); it != m_strands_writers.end(); ++it) { + if (it->second) + delete it->second; + } +} + +void AbcDupliObjectWriter::init_abc() +{ + if (m_abc_object) + return; + + m_abc_object = abc_archive()->add_id_object<OObject>((ID *)m_ob); + + if (m_dm_writer) { + /* XXX not nice */ + m_dm_writer->init(abc_archive()); + m_dm_writer->init_abc(m_abc_object); + } +} + +void AbcDupliObjectWriter::write_sample() +{ + if (!m_abc_object) + return; + + if (m_dm_writer) { + m_dm_writer->write_sample(); + } + + if (m_do_strands) { + int index = 0; + for (DupliObjectDataStrands *link = (DupliObjectDataStrands *)m_dupdata->strands.first; + link; + link = link->next, ++index) { + AbcStrandsWriter *writer = add_strands_writer(link->name); + writer->write_sample(); + } + } +} + +/* ------------------------------------------------------------------------- */ + +AbcDupliObjectReader::AbcDupliObjectReader(const std::string &name, Object *ob, DupliObjectData *dupli_data, + bool read_strands_motion, bool read_strands_children) : + ObjectReader(ob, name), + dupli_data(dupli_data), + m_read_strands_motion(read_strands_motion), + m_read_strands_children(read_strands_children) +{ +} + +AbcDupliObjectReader::~AbcDupliObjectReader() +{ +} + +void AbcDupliObjectReader::init(ReaderArchive *archive) +{ + AbcReader::init(archive); + + if (abc_archive()->root().getChildHeader(m_name)) + m_abc_object = abc_archive()->root().getChild(m_name); +} + +void AbcDupliObjectReader::init_abc(IObject object) +{ + m_abc_object = object; +} + +void AbcDupliObjectReader::read_dupligroup_object(IObject object, chrono_t time) +{ + if (GS(object.getName().c_str()) == ID_OB) { + /* instances are handled later, we create true object data here */ + if (object.isInstanceDescendant()) + return; + + BKE_dupli_object_data_init(dupli_data, m_ob); + + for (int i = 0; i < object.getNumChildren(); ++i) { + IObject child = object.getChild(i); + const MetaData &metadata = child.getMetaData(); + + if (IPolyMeshSchema::matches(metadata)) { + AbcDerivedMeshReader dm_reader("mesh", m_ob); + dm_reader.init(abc_archive()); + dm_reader.init_abc(child); + if (dm_reader.read_sample_abc(time) != PTC_READ_SAMPLE_INVALID) { + BKE_dupli_object_data_set_mesh(dupli_data, dm_reader.acquire_result()); + } + else { + dm_reader.discard_result(); + } + } + else if (ICurvesSchema::matches(metadata)) { + Strands *strands; + StrandsChildren *children; + BKE_dupli_object_data_find_strands(dupli_data, child.getName().c_str(), &strands, &children); + + AbcStrandsReader strands_reader(strands, children, m_read_strands_motion, m_read_strands_children); + strands_reader.init(abc_archive()); + strands_reader.init_abc(child); + if (strands_reader.read_sample_abc(time) != PTC_READ_SAMPLE_INVALID) { + Strands *newstrands = strands_reader.acquire_result(); + if (strands && strands != newstrands) { + /* reader can replace strands internally if topology does not match */ + BKE_strands_free(strands); + } + BKE_dupli_object_data_add_strands(dupli_data, child.getName().c_str(), newstrands); + + StrandsChildren *newchildren = strands_reader.child_reader().acquire_result(); + if (children && children != newchildren) { + /* reader can replace strands internally if topology does not match */ + BKE_strands_children_free(children); + } + BKE_dupli_object_data_add_strands_children(dupli_data, child.getName().c_str(), newchildren); + } + else { + strands_reader.discard_result(); + strands_reader.child_reader().discard_result(); + } + } + } + } +} + +PTCReadSampleResult AbcDupliObjectReader::read_sample_abc(chrono_t time) +{ + if (!m_abc_object) + return PTC_READ_SAMPLE_INVALID; + + read_dupligroup_object(m_abc_object, time); + + return PTC_READ_SAMPLE_EXACT; +} + +DupliObjectData *AbcDupliObjectReader::find_dupli_data(ObjectReaderPtr ptr) const +{ + DupliMap::const_iterator it = dupli_map.find(ptr); + if (it == dupli_map.end()) + return NULL; + else + return it->second; +} + +void AbcDupliObjectReader::insert_dupli_data(ObjectReaderPtr ptr, DupliObjectData *data) +{ + dupli_map.insert(DupliPair(ptr, data)); +} + +} /* namespace PTC */ diff --git a/source/blender/pointcache/alembic/abc_group.h b/source/blender/pointcache/alembic/abc_group.h new file mode 100644 index 00000000000..ae8c58edc45 --- /dev/null +++ b/source/blender/pointcache/alembic/abc_group.h @@ -0,0 +1,226 @@ +/* + * Copyright 2013, Blender Foundation. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef PTC_ABC_GROUP_H +#define PTC_ABC_GROUP_H + +#include "ptc_types.h" + +#include "abc_reader.h" +#include "abc_schema.h" +#include "abc_writer.h" + +#include "util_thread.h" + +struct CacheLibrary; +struct DupliCache; +struct DupliObject; +struct DupliObjectData; +struct Group; +struct Object; +struct Scene; + +namespace PTC { + +class AbcDerivedMeshWriter; +class AbcStrandsWriter; +class AbcSimDebugWriter; +class AbcSimDebugReader; + +class AbcGroupWriter : public GroupWriter, public AbcWriter { +public: + AbcGroupWriter(const std::string &name, Group *group); + + void init_abc(); + void create_refs(); + + void write_sample(); + +private: + Abc::OObject m_abc_object; +}; + +class AbcGroupReader : public GroupReader, public AbcReader { +public: + AbcGroupReader(const std::string &name, Group *group); + + void init_abc(Abc::IObject object); + + PTCReadSampleResult read_sample_abc(chrono_t time); + +private: + Abc::IObject m_abc_object; +}; + +/* ========================================================================= */ + +class AbcDupligroupWriter : public GroupWriter, public AbcWriter { +public: + typedef std::vector<Abc::ObjectWriterPtr> ObjectWriterList; + typedef std::vector<Abc::BasePropertyWriterPtr> PropertyWriterList; + + typedef std::map<ID *, AbcWriter *> IDWriterMap; + typedef std::pair<ID *, AbcWriter *> IDWriterPair; + + AbcDupligroupWriter(const std::string &name, EvaluationContext *eval_ctx, Scene *scene, Group *group, CacheLibrary *cachelib); + ~AbcDupligroupWriter(); + + void init_abc(); + + void write_sample(); + void write_sample_object(Object *ob, bool write_data); + void write_sample_dupli(DupliObject *dob, int index); + + AbcWriter *find_id_writer(ID *id) const; + +private: + EvaluationContext *m_eval_ctx; + Scene *m_scene; + CacheLibrary *m_cachelib; + + Abc::OObject m_abc_group; + ObjectWriterList m_object_writers; + PropertyWriterList m_property_writers; + IDWriterMap m_id_writers; + thread_mutex m_init_mutex; +}; + +class AbcDupliCacheWriter : public GroupWriter, public AbcWriter { +public: + typedef std::vector<Abc::ObjectWriterPtr> ObjectWriterList; + typedef std::vector<Abc::BasePropertyWriterPtr> PropertyWriterList; + + typedef std::map<ID *, AbcWriter *> IDWriterMap; + typedef std::pair<ID *, AbcWriter *> IDWriterPair; + + AbcDupliCacheWriter(const std::string &name, Group *group, DupliCache *dupcache, int data_types, bool do_sim_debug = false); + ~AbcDupliCacheWriter(); + + void init_abc(); + + void write_sample(); + void write_sample_object_data(DupliObjectData *data); + void write_sample_dupli(DupliObject *dob, int index); + + AbcWriter *find_id_writer(ID *id) const; + +private: + DupliCache *m_dupcache; + int m_data_types; + + Abc::OObject m_abc_group; + ObjectWriterList m_object_writers; + PropertyWriterList m_property_writers; + IDWriterMap m_id_writers; + AbcSimDebugWriter *m_simdebug_writer; +}; + +class AbcDupliCacheReader : public GroupReader, public AbcReader { +public: + typedef std::map<Abc::ObjectReaderPtr, DupliObjectData*> DupliMap; + typedef std::pair<Abc::ObjectReaderPtr, DupliObjectData*> DupliPair; + + typedef std::map<std::string, Object*> ObjectMap; + typedef std::pair<std::string, Object*> ObjectPair; + +public: + AbcDupliCacheReader(const std::string &name, Group *group, DupliCache *dupcache, + bool read_strands_motion, bool read_strands_children, bool read_sim_debug); + ~AbcDupliCacheReader(); + + void init_abc(Abc::IObject object); + + PTCReadSampleResult read_sample_abc(chrono_t time); + +protected: + void read_dupligroup_object(Abc::IObject object, chrono_t time); + void read_dupligroup_group(Abc::IObject abc_group, chrono_t time); + + DupliObjectData *find_dupli_data(Abc::ObjectReaderPtr ptr) const; + void insert_dupli_data(Abc::ObjectReaderPtr ptr, DupliObjectData *data); + + void build_object_map(Main *bmain, Group *group); + void build_object_map_add_group(Group *group); + Object *find_object(const std::string &name) const; + +private: + DupliMap dupli_map; + DupliCache *dupli_cache; + + ObjectMap object_map; + bool m_read_strands_motion, m_read_strands_children; + AbcSimDebugReader *m_simdebug_reader; +}; + + +class AbcDupliObjectWriter : public ObjectWriter, public AbcWriter { +public: + typedef std::map<std::string, AbcStrandsWriter *> StrandsWriters; + typedef std::pair<std::string, AbcStrandsWriter *> StrandsWritersPair; + + AbcDupliObjectWriter(const std::string &name, DupliObjectData *dupdata, bool do_mesh, bool do_strands); + ~AbcDupliObjectWriter(); + + void init_abc(); + + void write_sample(); + + AbcStrandsWriter *find_strands_writer(const std::string &name) const; + AbcStrandsWriter *add_strands_writer(const std::string &name); + +private: + DupliObjectData *m_dupdata; + bool m_do_strands; + + Abc::OObject m_abc_object; + AbcDerivedMeshWriter *m_dm_writer; + StrandsWriters m_strands_writers; +}; + +class AbcDupliObjectReader : public ObjectReader, public AbcReader { +public: + typedef std::map<Abc::ObjectReaderPtr, DupliObjectData*> DupliMap; + typedef std::pair<Abc::ObjectReaderPtr, DupliObjectData*> DupliPair; + +public: + AbcDupliObjectReader(const std::string &name, Object *ob, DupliObjectData *dupli_data, + bool read_strands_motion, bool read_strands_children); + ~AbcDupliObjectReader(); + + void init(ReaderArchive *archive); + void init_abc(Abc::IObject object); + + PTCReadSampleResult read_sample_abc(chrono_t time); + +protected: + void read_dupligroup_object(Abc::IObject object, chrono_t time); + + DupliObjectData *find_dupli_data(Abc::ObjectReaderPtr ptr) const; + void insert_dupli_data(Abc::ObjectReaderPtr ptr, DupliObjectData *data); + +private: + DupliMap dupli_map; + DupliObjectData *dupli_data; + bool m_read_strands_motion, m_read_strands_children; + + Abc::IObject m_abc_object; +}; + +} /* namespace PTC */ + +#endif /* PTC_OBJECT_H */ diff --git a/source/blender/pointcache/alembic/abc_info.cpp b/source/blender/pointcache/alembic/abc_info.cpp new file mode 100644 index 00000000000..35d30cb6d0c --- /dev/null +++ b/source/blender/pointcache/alembic/abc_info.cpp @@ -0,0 +1,516 @@ +//-***************************************************************************** +// +// Copyright (c) 2009-2013, +// Sony Pictures Imageworks, Inc. and +// Industrial Light & Magic, a division of Lucasfilm Entertainment Company Ltd. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Sony Pictures Imageworks, nor +// Industrial Light & Magic nor the names of their contributors may be used +// to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +//-***************************************************************************** + +/* + * Copyright 2015, Blender Foundation. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include <Alembic/AbcGeom/All.h> +#include <Alembic/AbcCoreAbstract/All.h> +#include <Alembic/AbcCoreFactory/All.h> +#include <Alembic/Util/All.h> +#include <Alembic/Abc/TypedPropertyTraits.h> + +#include <sstream> + +#include "alembic.h" + +extern "C" { +#include "BLI_string.h" +#include "BLI_utildefines.h" + +#include "DNA_ID.h" + +#include "BKE_cache_library.h" +#include "BKE_idprop.h" +} + +using namespace ::Alembic::AbcGeom; + +namespace PTC { + +static void metadata_from_idprops(MetaData &md, IDProperty *prop); + +void abc_metadata_from_idprops_group(MetaData &md, IDProperty *prop) +{ + if (!prop) + return; + + IDProperty *child; + for (child = (IDProperty *)prop->data.group.first; child; child = child->next) + metadata_from_idprops(md, child); +} + +static void metadata_from_idprops(MetaData &md, IDProperty *prop) +{ + /* skip default metadata entries, these are set explicitly */ + std::string key(prop->name); + if (key.compare(kApplicationNameKey)==0 || key.compare(kDateWrittenKey)==0 || key.compare(kUserDescriptionKey)==0) + return; + + switch (prop->type) { +#if 0 /* don't support recursion yet */ + case IDP_GROUP: { + metadata_from_idprops_group(md, child); + break; + } + + case IDP_ARRAY: { + if (prop->data.pointer) { + IDProperty **array = (IDProperty **)prop->data.pointer; + for (int a = 0; a < prop->len; a++) + metadata_from_idprops(md, array[a]); + } + break; + } +#endif + + /* only string properties are used */ + case IDP_STRING: { + md.set(prop->name, IDP_String(prop)); + break; + } + } +} + +void abc_metadata_to_idprops_group(const MetaData &md, IDProperty *prop) +{ + if (!prop) + return; + + for (MetaData::const_iterator it = md.begin(); it != md.end(); ++it) { + const std::string &key = it->first; + const std::string &value = it->second; + + /* skip default metadata entries, these are stored in CacheArchiveInfo */ + if (key.compare(kApplicationNameKey)==0 || key.compare(kDateWrittenKey)==0 || key.compare(kUserDescriptionKey)==0) + continue; + + IDPropertyTemplate val; + val.string.str = value.c_str(); + val.string.len = value.length(); + IDP_ReplaceInGroup(prop, IDP_New(IDP_STRING, &val, key.c_str())); + } +} + +MetaData abc_create_archive_info(const char *app_name, const char *description, const struct tm *t, IDProperty *props) +{ + MetaData md; + + md.set(kApplicationNameKey, app_name); + md.set(kUserDescriptionKey, description); + + if (!t) { + time_t curtime = time(NULL); + t = localtime(&curtime); + } + + if (t) { + char buf[256]; + strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M", t); + md.set(kDateWrittenKey, buf); + } + + /* store custom properties as metadata */ + if (props && props->type == IDP_GROUP) + abc_metadata_from_idprops_group(md, props); + + return md; +} + +/* ========================================================================= */ + +struct stringstream { + stringstream(void (*cb)(void *, const char *), void *userdata) : + cb(cb), + userdata(userdata) + { + } + + void (*cb)(void *, const char *); + void *userdata; + + template <typename T> + friend stringstream& operator << (stringstream &stream, T s); +}; + +template <typename T> +stringstream& operator << (stringstream &stream, T s) +{ + std::stringstream ss; + ss << s; + stream.cb(stream.userdata, ss.str().c_str()); + return stream; +} + +static const std::string g_sep(";"); +static const std::string g_endl("\n"); + +static void info_stream_properties(stringstream &ss, ICompoundProperty, std::string &); + +template <class PROP> +static void info_stream_array_property(stringstream &ss, PROP iProp, const std::string &iIndent) +{ + std::string ptype = "ArrayProperty "; + size_t asize = 0; + + AbcA::ArraySamplePtr samp; + index_t maxSamples = iProp.getNumSamples(); + for (index_t i = 0 ; i < maxSamples; ++i) { + iProp.get(samp, ISampleSelector(i)); + asize = samp->size(); + } + + std::string mdstring = "interpretation="; + mdstring += iProp.getMetaData().get("interpretation"); + + std::stringstream dtype; + dtype << "datatype="; + dtype << iProp.getDataType(); + + std::stringstream asizestr; + asizestr << ";arraysize="; + asizestr << asize; + + mdstring += g_sep; + + mdstring += dtype.str(); + + mdstring += asizestr.str(); + + ss << iIndent << " " << ptype << "name=" << iProp.getName() + << g_sep << mdstring << g_sep << "numsamps=" + << iProp.getNumSamples() << g_endl; +} + +template <class PROP> +static void info_stream_scalar_property(stringstream &ss, PROP iProp, const std::string &iIndent) +{ + std::string ptype = "ScalarProperty "; + size_t asize = 0; + + const AbcA::DataType &dt = iProp.getDataType(); + const Alembic::Util ::uint8_t extent = dt.getExtent(); + Alembic::Util::Dimensions dims(extent); + AbcA::ArraySamplePtr samp = AbcA::AllocateArraySample( dt, dims ); + index_t maxSamples = iProp.getNumSamples(); + for (index_t i = 0 ; i < maxSamples; ++i) { + iProp.get(const_cast<void *>(samp->getData()), ISampleSelector(i)); + asize = samp->size(); + } + + std::string mdstring = "interpretation="; + mdstring += iProp.getMetaData().get("interpretation"); + + std::stringstream dtype; + dtype << "datatype="; + dtype << dt; + + std::stringstream asizestr; + asizestr << ";arraysize="; + asizestr << asize; + + mdstring += g_sep; + + mdstring += dtype.str(); + + mdstring += asizestr.str(); + + ss << iIndent << " " << ptype << "name=" << iProp.getName() + << g_sep << mdstring << g_sep << "numsamps=" + << iProp.getNumSamples() << g_endl; +} + +static void info_stream_compound_property(stringstream &ss, ICompoundProperty iProp, std::string &ioIndent) +{ + std::string oldIndent = ioIndent; + ioIndent += " "; + + std::string interp = "schema="; + interp += iProp.getMetaData().get("schema"); + + ss << ioIndent << "CompoundProperty " << "name=" << iProp.getName() + << g_sep << interp << g_endl; + + info_stream_properties(ss, iProp, ioIndent); + + ioIndent = oldIndent; +} + +static void info_stream_properties(stringstream &ss, ICompoundProperty iParent, std::string &ioIndent ) +{ + std::string oldIndent = ioIndent; + for (size_t i = 0 ; i < iParent.getNumProperties() ; i++) { + PropertyHeader header = iParent.getPropertyHeader(i); + + if (header.isCompound()) { + info_stream_compound_property(ss, ICompoundProperty(iParent, header.getName()), ioIndent); + } + else if (header.isScalar()) { + info_stream_scalar_property(ss, IScalarProperty(iParent, header.getName()), ioIndent); + } + else { + BLI_assert(header.isArray()); + info_stream_array_property(ss, IArrayProperty(iParent, header.getName()), ioIndent); + } + } + + ioIndent = oldIndent; +} + +static void info_stream_object(stringstream &ss, IObject iObj, std::string iIndent) +{ + // Object has a name, a full name, some meta data, + // and then it has a compound property full of properties. + std::string path = iObj.getFullName(); + + if (iObj.isInstanceRoot()) { + if (path != "/") { + ss << "Object " << "name=" << path + << " [Instance " << iObj.instanceSourcePath() << "]" + << g_endl; + } + } + else if (iObj.isInstanceDescendant()) { + /* skip non-root instances to avoid repetition */ + return; + } + else { + if (path != "/") { + ss << "Object " << "name=" << path << g_endl; + } + + // Get the properties. + ICompoundProperty props = iObj.getProperties(); + info_stream_properties(ss, props, iIndent); + + // now the child objects + for (size_t i = 0 ; i < iObj.getNumChildren() ; i++) { + info_stream_object(ss, IObject(iObj, iObj.getChildHeader(i).getName()), iIndent); + } + } +} + +void abc_archive_info_stream(IArchive &archive, void (*stream)(void *, const char *), void *userdata) +{ + stringstream ss(stream, userdata); + + ss << "Alembic Archive Info for " + << Alembic::AbcCoreAbstract::GetLibraryVersion() + << g_endl; + + std::string appName; + std::string libraryVersionString; + Alembic::Util::uint32_t libraryVersion; + std::string whenWritten; + std::string userDescription; + GetArchiveInfo(archive, + appName, + libraryVersionString, + libraryVersion, + whenWritten, + userDescription); + + if (appName != "") { + ss << " file written by: " << appName << g_endl; + ss << " using Alembic : " << libraryVersionString << g_endl; + ss << " written on : " << whenWritten << g_endl; + ss << " user description : " << userDescription << g_endl; + ss << g_endl; + } + else { +// ss << argv[1] << g_endl; + ss << " (file doesn't have any ArchiveInfo)" + << g_endl; + ss << g_endl; + } + + info_stream_object(ss, archive.getTop(), ""); +} + +/* ========================================================================= */ + +static void info_nodes_properties(CacheArchiveInfo *info, ICompoundProperty, CacheArchiveInfoNode *parent, bool calc_bytes_size); + +template <class PROP> +static void info_nodes_array_property(CacheArchiveInfo *info, PROP iProp, CacheArchiveInfoNode *parent, bool calc_bytes_size) +{ + CacheArchiveInfoNode *node = BKE_cache_archive_info_add_node(info, parent, eCacheArchiveInfoNode_Type_ArrayProperty, iProp.getName().c_str()); + + index_t num_samples = iProp.getNumSamples(); + + const DataType &datatype = iProp.getDataType(); + + node->num_samples = num_samples; + BLI_strncpy(node->datatype_name, PODName(datatype.getPod()), sizeof(node->datatype_name)); + node->datatype_extent = (short)datatype.getExtent(); + + if (calc_bytes_size) { + size_t max_array_size = 0; + size_t tot_array_size = 0; + for (index_t i = 0; i < num_samples; ++i) { + AbcA::ArraySamplePtr samp; + iProp.get(samp, ISampleSelector(i)); + size_t array_size = samp->size(); + max_array_size = std::max(max_array_size, array_size); + tot_array_size += array_size; + } + node->bytes_size = datatype.getNumBytes() * tot_array_size; + node->array_size = max_array_size; + + if (parent) + parent->bytes_size += node->bytes_size; + } +} + +template <class PROP> +static void info_nodes_scalar_property(CacheArchiveInfo *info, PROP iProp, CacheArchiveInfoNode *parent, bool calc_bytes_size) +{ + CacheArchiveInfoNode *node = BKE_cache_archive_info_add_node(info, parent, eCacheArchiveInfoNode_Type_ScalarProperty, iProp.getName().c_str()); + + index_t num_samples = iProp.getNumSamples(); + + const DataType &datatype = iProp.getDataType(); + + node->num_samples = num_samples; + BLI_strncpy(node->datatype_name, PODName(datatype.getPod()), sizeof(node->datatype_name)); + node->datatype_extent = (short)datatype.getExtent(); + + if (calc_bytes_size) { + node->bytes_size = datatype.getNumBytes() * num_samples; + + if (parent) + parent->bytes_size += node->bytes_size; + } +} + +static void info_nodes_compound_property(CacheArchiveInfo *info, ICompoundProperty iProp, CacheArchiveInfoNode *parent, bool calc_bytes_size) +{ + CacheArchiveInfoNode *node = BKE_cache_archive_info_add_node(info, parent, eCacheArchiveInfoNode_Type_CompoundProperty, iProp.getName().c_str()); + + info_nodes_properties(info, iProp, node, calc_bytes_size); + + if (calc_bytes_size && parent) + parent->bytes_size += node->bytes_size; +} + +static void info_nodes_properties(CacheArchiveInfo *info, ICompoundProperty iParent, CacheArchiveInfoNode *parent, bool calc_bytes_size) +{ + for (size_t i = 0 ; i < iParent.getNumProperties() ; i++) { + PropertyHeader header = iParent.getPropertyHeader(i); + + if (header.isCompound()) { + info_nodes_compound_property(info, ICompoundProperty(iParent, header.getName()), parent, calc_bytes_size); + } + else if (header.isScalar()) { + info_nodes_scalar_property(info, IScalarProperty(iParent, header.getName()), parent, calc_bytes_size); + } + else { + BLI_assert(header.isArray()); + info_nodes_array_property(info, IArrayProperty(iParent, header.getName()), parent, calc_bytes_size); + } + } +} + +static void info_nodes_object(CacheArchiveInfo *info, IObject iObj, CacheArchiveInfoNode *parent, bool calc_bytes_size) +{ + CacheArchiveInfoNode *node = BKE_cache_archive_info_add_node(info, parent, eCacheArchiveInfoNode_Type_Object, iObj.getName().c_str()); + + if (iObj.isInstanceRoot()) { + } + else if (iObj.isInstanceDescendant()) { + } + else { + // Get the properties. + ICompoundProperty props = iObj.getProperties(); + info_nodes_properties(info, props, node, calc_bytes_size); + + // now the child objects + for (size_t i = 0 ; i < iObj.getNumChildren() ; i++) { + info_nodes_object(info, IObject(iObj, iObj.getChildHeader(i).getName()), node, calc_bytes_size); + } + } + + if (calc_bytes_size && parent) + parent->bytes_size += node->bytes_size; +} + +void abc_archive_info_nodes(IArchive &archive, CacheArchiveInfo *info, IDProperty *metadata, bool calc_nodes, bool calc_bytes_size) +{ +// ss << "Alembic Archive Info for " +// << Alembic::AbcCoreAbstract::GetLibraryVersion() +// << g_endl; + + std::string appName; + std::string libraryVersionString; + uint32_t libraryVersion; + std::string whenWritten; + std::string userDescription; + GetArchiveInfo(archive, appName, libraryVersionString, libraryVersion, whenWritten, userDescription); + + if (appName != "") { + BLI_strncpy(info->app_name, appName.c_str(), sizeof(info->app_name)); + BLI_strncpy(info->date_written, whenWritten.c_str(), sizeof(info->date_written)); + BLI_strncpy(info->description, userDescription.c_str(), sizeof(info->description)); + } + else { + info->app_name[0] = '\0'; + info->date_written[0] = '\0'; + info->description[0] = '\0'; + } + + if (metadata) + abc_metadata_to_idprops_group(archive.getPtr()->getMetaData(), metadata); + + if (calc_nodes) + info_nodes_object(info, archive.getTop(), NULL, calc_bytes_size); +} + +} /* namespace PTC */ diff --git a/source/blender/pointcache/alembic/abc_interpolate.cpp b/source/blender/pointcache/alembic/abc_interpolate.cpp new file mode 100644 index 00000000000..989734f3c56 --- /dev/null +++ b/source/blender/pointcache/alembic/abc_interpolate.cpp @@ -0,0 +1,32 @@ +/* + * Copyright 2015, Blender Foundation. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "abc_interpolate.h" + +extern "C" { +#include "BLI_math.h" +#include "BLI_utildefines.h" +} + +namespace PTC { + +using namespace Abc; + + + +} /* namespace PTC */ diff --git a/source/blender/pointcache/alembic/abc_interpolate.h b/source/blender/pointcache/alembic/abc_interpolate.h new file mode 100644 index 00000000000..102f4680c81 --- /dev/null +++ b/source/blender/pointcache/alembic/abc_interpolate.h @@ -0,0 +1,236 @@ +/* + * Copyright 2015, Blender Foundation. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef PTC_ABC_INTERPOLATE_H +#define PTC_ABC_INTERPOLATE_H + +#include <Alembic/Abc/ISampleSelector.h> +#include <Alembic/Abc/IScalarProperty.h> +#include <Alembic/Abc/TypedPropertyTraits.h> +#include <Alembic/Abc/ITypedArrayProperty.h> +#include <Alembic/Abc/ITypedScalarProperty.h> +#include <Alembic/Abc/TypedArraySample.h> + +extern "C" { +#include "BLI_math.h" +#include "BLI_utildefines.h" +} + +namespace PTC { + +using namespace Alembic; +using namespace Abc; + +using Alembic::Util::shared_ptr; + +enum InterpolateSemanticDefault { + InterpolateSemanticDefault_None = 0, +}; + +enum InterpolateSemanticVector { + InterpolateSemanticVector_Linear = 0, + InterpolateSemanticVector_Slerp = 1, +}; + +/* linear blending for primitive types */ +template <typename T> +BLI_INLINE T interpolate_sample(const T &val0, const T &val1, float t) +{ + return val0 * (1.0f-t) + val1 * t; +} + +/* vector semantics */ +BLI_INLINE V3f interpolate_sample(const V3f &val0, const V3f &val1, float t, InterpolateSemanticVector semantic) +{ + switch (semantic) { + case InterpolateSemanticVector_Linear: + return val0 * (1.0f-t) + val1 * t; + case InterpolateSemanticVector_Slerp: + V3f result; + interp_v3_v3v3_slerp_safe(result.getValue(), val0.getValue(), val1.getValue(), t); + return result; + } + return V3f(0.0f, 0.0f, 0.0f); +} + +BLI_INLINE Quatf interpolate_sample(const Quatf &val0, const Quatf &val1, float t) +{ + float qt0[4] = {val0.r, val0.v.x, val0.v.y, val0.v.z}; + float qt1[4] = {val1.r, val1.v.x, val1.v.y, val1.v.z}; + float result[4]; + interp_qt_qtqt(result, qt0, qt1, t); + return Quatf(result[0], result[1], result[2], result[3]); +} + +BLI_INLINE M44f interpolate_sample(const M44f &val0, const M44f &val1, float t) +{ + float loc[3], quat[4], size[3]; + float loc0[3], quat0[4], size0[3]; + float loc1[3], quat1[4], size1[3]; + mat4_decompose(loc0, quat0, size0, (float (*)[4])val0.getValue()); + mat4_decompose(loc1, quat1, size1, (float (*)[4])val1.getValue()); + + /* linear interpolation for rotation and scale */ + interp_v3_v3v3(loc, loc0, loc1, t); + + /* use simpe nlerp instead of slerp. it's faster and almost the same */ + interp_v4_v4v4(quat, quat0, quat1, t); + normalize_qt(quat); + + interp_v3_v3v3(size, size0, size1, t); + + M44f result; + loc_quat_size_to_mat4((float (*)[4])result.getValue(), loc, quat, size); + + return result; +} + +/* ------------------------------------------------------------------------- */ + +/* These wrapper types allow calling all the interpolate_sample variants without knowing the semantics type + * structs are required for this, since partial specialization does not work with functions. + */ + +/* forward declaration */ +template <typename ArraySampleT, typename SemanticT> +BLI_INLINE shared_ptr<ArraySampleT> interpolate_sample(const ArraySampleT &val0, const ArraySampleT &val1, float t, SemanticT semantic); + +template <typename T, typename SemanticT> +struct InterpolateSampleCaller { + BLI_INLINE T call(const T &val0, const T &val1, float t, SemanticT semantic) + { + return interpolate_sample(val0, val1, t, semantic); + } +}; + +template <typename T> +struct InterpolateSampleCaller<T, InterpolateSemanticDefault> { + BLI_INLINE T call(const T &val0, const T &val1, float t, InterpolateSemanticDefault) + { + return interpolate_sample(val0, val1, t); + } +}; + +/* ------------------------------------------------------------------------- */ + +/* Scalar Properties */ + +template <typename PropT, typename SemanticT> +typename PropT::value_type abc_interpolate_sample_linear(const PropT &prop, chrono_t time, SemanticT semantic) +{ + typedef typename PropT::value_type value_type; + + ISampleSelector ss0(time, ISampleSelector::kFloorIndex); + ISampleSelector ss1(time, ISampleSelector::kCeilIndex); + + index_t index0 = ss0.getIndex(prop.getTimeSampling(), prop.getNumSamples()); + index_t index1 = ss1.getIndex(prop.getTimeSampling(), prop.getNumSamples()); + if (index0 == index1) { + /* no interpolation needed */ + return prop.getValue(ss0); + } + else { + chrono_t time0 = prop.getTimeSampling()->getSampleTime(index0); + chrono_t time1 = prop.getTimeSampling()->getSampleTime(index1); + + float t = (time1 > time0) ? (time - time0) / (time1 - time0) : 0.0f; + return InterpolateSampleCaller<value_type, SemanticT>::call(prop.getValue(ss0), prop.getValue(ss1), t, semantic); + } +} + +template <typename PropT> +typename PropT::value_type abc_interpolate_sample_linear(const PropT &prop, chrono_t time) +{ + return abc_interpolate_sample_linear(prop, time, InterpolateSemanticDefault_None); +} + + +/* Array Properties */ + +template <typename ArraySampleT, typename SemanticT> +BLI_INLINE shared_ptr<ArraySampleT> interpolate_array_sample(const ArraySampleT &val0, const ArraySampleT &val1, float t, SemanticT semantic) +{ + typedef ArraySampleT sample_type; + typedef shared_ptr<ArraySampleT> sample_ptr_type; + typedef typename ArraySampleT::value_type value_type; + + size_t size0 = val0.size(); + size_t size1 = val1.size(); + size_t maxsize = size0 > size1 ? size0 : size1; + size_t minsize = size0 < size1 ? size0 : size1; + + const value_type *data0 = val0.get(); + const value_type *data1 = val1.get(); + value_type *result = new value_type[maxsize]; + value_type *data = result; + + for (size_t i = 0; i < minsize; ++i) { + *data = InterpolateSampleCaller<value_type, SemanticT>::call(*data0, *data1, t, semantic); + ++data; + ++data0; + ++data1; + } + + if (size0 > minsize) { + for (size_t i = minsize; i < size0; ++i) { + *data = *data0; + ++data; + ++data0; + } + } + else if (size1 > minsize) { + for (size_t i = minsize; i < size1; ++i) { + *data = *data1; + ++data; + ++data1; + } + } + + return sample_ptr_type(new sample_type(result, maxsize)); +} + +template <typename TraitsT, typename SemanticT> +shared_ptr<typename ITypedArrayProperty<TraitsT>::sample_type> abc_interpolate_sample_linear(const ITypedArrayProperty<TraitsT> &prop, chrono_t time, SemanticT semantic) +{ + ISampleSelector ss0(time, ISampleSelector::kFloorIndex); + ISampleSelector ss1(time, ISampleSelector::kCeilIndex); + + index_t index0 = ss0.getIndex(prop.getTimeSampling(), prop.getNumSamples()); + index_t index1 = ss1.getIndex(prop.getTimeSampling(), prop.getNumSamples()); + if (index0 == index1) { + /* no interpolation needed */ + return prop.getValue(ss0); + } + else { + chrono_t time0 = prop.getTimeSampling()->getSampleTime(index0); + chrono_t time1 = prop.getTimeSampling()->getSampleTime(index1); + + float t = (time1 > time0) ? (time - time0) / (time1 - time0) : 0.0f; + return interpolate_array_sample(*prop.getValue(ss0), *prop.getValue(ss1), t, semantic); + } +} + +template <typename TraitsT> +shared_ptr<typename ITypedArrayProperty<TraitsT>::sample_type> abc_interpolate_sample_linear(const ITypedArrayProperty<TraitsT> &prop, chrono_t time) +{ + return abc_interpolate_sample_linear(prop, time, InterpolateSemanticDefault_None); +} + +} /* namespace PTC */ + +#endif /* PTC_ABC_INTERPOLATE_H */ diff --git a/source/blender/pointcache/alembic/abc_mesh.cpp b/source/blender/pointcache/alembic/abc_mesh.cpp new file mode 100644 index 00000000000..57f07d47099 --- /dev/null +++ b/source/blender/pointcache/alembic/abc_mesh.cpp @@ -0,0 +1,630 @@ +/* + * Copyright 2014, Blender Foundation. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "abc_interpolate.h" +#include "abc_mesh.h" + +extern "C" { +#include "BLI_math.h" + +#include "DNA_object_types.h" +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_modifier_types.h" + +#include "BKE_DerivedMesh.h" +#include "BKE_cdderivedmesh.h" +#include "BKE_mesh.h" + +#include "PIL_time.h" +} + +#include "PTC_api.h" + +//#define USE_TIMING + +namespace PTC { + +using namespace Abc; +using namespace AbcGeom; + +/* CD layers that are stored in generic customdata arrays created with CD_ALLOC */ +/* XXX CD_MASK_MTFACE and CD_MASK_MTEXPOLY are currently still needed as dummies for syncing + * particle UV and MCol layers to the mesh shader attributes ... + */ +static CustomDataMask CD_MASK_CACHE_EXCLUDE = + CD_MASK_MVERT | CD_MASK_MEDGE | CD_MASK_MFACE | CD_MASK_MPOLY | CD_MASK_MLOOP | + /*CD_MASK_MTFACE | CD_MASK_MTEXPOLY |*/ + CD_MASK_PROP_STR | + CD_MASK_SHAPEKEY | CD_MASK_SHAPE_KEYINDEX | + CD_MASK_MDISPS | CD_MASK_CREASE | CD_MASK_BWEIGHT | CD_MASK_RECAST | CD_MASK_PAINT_MASK | + CD_MASK_GRID_PAINT_MASK | CD_MASK_MVERT_SKIN | CD_MASK_FREESTYLE_EDGE | CD_MASK_FREESTYLE_FACE; + +static CustomDataMask CD_MASK_CACHE_VERT = ~(CD_MASK_CACHE_EXCLUDE | CD_MASK_NORMAL); +static CustomDataMask CD_MASK_CACHE_EDGE = ~(CD_MASK_CACHE_EXCLUDE); +static CustomDataMask CD_MASK_CACHE_FACE = ~(CD_MASK_CACHE_EXCLUDE); +static CustomDataMask CD_MASK_CACHE_POLY = ~(CD_MASK_CACHE_EXCLUDE); +static CustomDataMask CD_MASK_CACHE_LOOP = ~(CD_MASK_CACHE_EXCLUDE); + +struct MVertSample { + std::vector<V3f> co; + std::vector<N3f> no; + std::vector<int8_t> flag; + std::vector<int8_t> bweight; +}; + +struct MEdgeSample { + std::vector<uint32_t> verts; + std::vector<int16_t> flag; + std::vector<int8_t> crease; + std::vector<int8_t> bweight; +}; + +struct MPolySample { + /*std::vector<int32_t> loopstart;*/ /* loopstart is not stored explicitly */ + std::vector<int32_t> totloop; + std::vector<int16_t> mat_nr; + std::vector<int8_t> flag; +}; + +struct MLoopSample { + /* XXX these are unsigned int in DNA, but Alembic expects signed int */ + std::vector<int32_t> verts; + std::vector<int32_t> edges; +}; + +AbcDerivedMeshWriter::AbcDerivedMeshWriter(const std::string &name, Object *ob, DerivedMesh **dm_ptr) : + DerivedMeshWriter(ob, dm_ptr, name), + m_vert_data_writer("vertex_data", CD_MASK_CACHE_VERT), + m_edge_data_writer("edge_data", CD_MASK_CACHE_EDGE), + m_face_data_writer("face_data", CD_MASK_CACHE_FACE), + m_poly_data_writer("poly_data", CD_MASK_CACHE_POLY), + m_loop_data_writer("loop_data", CD_MASK_CACHE_LOOP) +{ +} + +AbcDerivedMeshWriter::~AbcDerivedMeshWriter() +{ +} + +void AbcDerivedMeshWriter::init_abc(OObject parent) +{ + if (m_mesh) + return; + + m_mesh = OPolyMesh(parent, m_name, abc_archive()->frame_sampling_index()); + + OPolyMeshSchema &schema = m_mesh.getSchema(); +// OCompoundProperty geom_props = schema.getArbGeomParams(); + OCompoundProperty user_props = schema.getUserProperties(); + + m_prop_vert_normals = ON3fArrayProperty(user_props, "vertex_normals", frame_sampling()); + m_prop_vert_flag = OCharArrayProperty(user_props, "vertex_flag", frame_sampling()); + m_prop_vert_bweight = OCharArrayProperty(user_props, "vertex_bweight", frame_sampling()); + + m_prop_edge_verts = OUInt32ArrayProperty(user_props, "edge_verts", frame_sampling()); + m_prop_edge_flag = OInt16ArrayProperty(user_props, "edge_flag", frame_sampling()); + m_prop_edge_crease = OCharArrayProperty(user_props, "edge_crease", frame_sampling()); + m_prop_edge_bweight = OCharArrayProperty(user_props, "edge_bweight", frame_sampling()); + + m_prop_poly_mat_nr = OInt16ArrayProperty(user_props, "poly_mat_nr", frame_sampling()); + m_prop_poly_flag = OCharArrayProperty(user_props, "poly_flag", frame_sampling()); + + m_prop_loop_verts = OInt32ArrayProperty(user_props, "loop_verts", frame_sampling()); + m_prop_loop_edges = OInt32ArrayProperty(user_props, "loop_edges", frame_sampling()); + + m_vert_data_writer.init(frame_sampling()); + m_edge_data_writer.init(frame_sampling()); + m_face_data_writer.init(frame_sampling()); + m_poly_data_writer.init(frame_sampling()); + m_loop_data_writer.init(frame_sampling()); +} + +/* XXX modifiers are not allowed to generate poly normals on their own! + * see assert in DerivedMesh.c : dm_ensure_display_normals + */ +#if 0 +static void ensure_normal_data(DerivedMesh *dm) +{ + MVert *mverts = dm->getVertArray(dm); + MLoop *mloops = dm->getLoopArray(dm); + MPoly *mpolys = dm->getPolyArray(dm); + CustomData *cdata = dm->getPolyDataLayout(dm); + float (*polynors)[3]; + int totvert = dm->getNumVerts(dm); + int totloop = dm->getNumLoops(dm); + int totpoly = dm->getNumPolys(dm); + + if (CustomData_has_layer(cdata, CD_NORMAL)) + polynors = (float (*)[3])CustomData_get_layer(cdata, CD_NORMAL); + else + polynors = (float (*)[3])CustomData_add_layer(cdata, CD_NORMAL, CD_CALLOC, NULL, totpoly); + + BKE_mesh_calc_normals_poly(mverts, totvert, mloops, mpolys, totloop, totpoly, polynors, false); +} +#endif + +static void create_sample_verts(DerivedMesh *dm, MVertSample &sample) +{ + MVert *mv, *mverts = dm->getVertArray(dm); + int i, totvert = dm->getNumVerts(dm); + + sample.co.reserve(totvert); + sample.no.reserve(totvert); + sample.flag.reserve(totvert); + sample.bweight.reserve(totvert); + for (i = 0, mv = mverts; i < totvert; ++i, ++mv) { + float nor[3]; + + sample.co.push_back(V3f(mv->co[0], mv->co[1], mv->co[2])); + + normal_short_to_float_v3(nor, mv->no); + sample.no.push_back(N3f(nor[0], nor[1], nor[2])); + + sample.flag.push_back(mv->flag); + sample.bweight.push_back(mv->bweight); + } +} + +static void create_sample_edges(DerivedMesh *dm, MEdgeSample &sample) +{ + MEdge *me, *medges = dm->getEdgeArray(dm); + int i, totedge = dm->getNumEdges(dm); + + sample.verts.reserve(totedge * 2); + sample.flag.reserve(totedge); + sample.crease.reserve(totedge); + sample.bweight.reserve(totedge); + + for (i = 0, me = medges; i < totedge; ++i, ++me) { + sample.verts.push_back(me->v1); + sample.verts.push_back(me->v2); + sample.flag.push_back(me->flag); + sample.crease.push_back(me->crease); + sample.bweight.push_back(me->bweight); + } +} + +static void create_sample_polys(DerivedMesh *dm, MPolySample &sample) +{ + MPoly *mp, *mpolys = dm->getPolyArray(dm); + int i, totpoly = dm->getNumPolys(dm); + + sample.totloop.reserve(totpoly); + sample.mat_nr.reserve(totpoly); + sample.flag.reserve(totpoly); + + for (i = 0, mp = mpolys; i < totpoly; ++i, ++mp) { + sample.totloop.push_back(mp->totloop); + sample.mat_nr.push_back(mp->mat_nr); + sample.flag.push_back(mp->flag); + } +} + +static void create_sample_loops(DerivedMesh *dm, MLoopSample &sample) +{ + MLoop *ml, *mloops = dm->getLoopArray(dm); + int i, totloop = dm->getNumLoops(dm); + + sample.verts.reserve(totloop); + sample.edges.reserve(totloop); + + for (i = 0, ml = mloops; i < totloop; ++i, ++ml) { + sample.verts.push_back(ml->v); + sample.edges.push_back(ml->e); + } +} + +static N3fArraySample create_sample_loop_normals(DerivedMesh *dm, std::vector<N3f> &data) +{ + CustomData *cdata = dm->getLoopDataLayout(dm); + float (*nor)[3], (*loopnors)[3]; + int i, totloop = dm->getNumLoops(dm); + + if (!CustomData_has_layer(cdata, CD_NORMAL)) + return N3fArraySample(); + + loopnors = (float (*)[3])CustomData_get_layer(cdata, CD_NORMAL); + + data.reserve(totloop); + for (i = 0, nor = loopnors; i < totloop; ++i, ++nor) { + float *vec = *nor; + data.push_back(N3f(vec[0], vec[1], vec[2])); + } + + return N3fArraySample(data); +} + +void AbcDerivedMeshWriter::write_sample() +{ + if (!m_mesh) + return; + + DerivedMesh *output_dm = *m_dm_ptr; + if (!output_dm) + return; + + /* TODO make this optional by a flag? */ + /* XXX does not work atm, see comment above */ + /*ensure_normal_data(output_dm);*/ + + OPolyMeshSchema &schema = m_mesh.getSchema(); + OCompoundProperty user_props = schema.getUserProperties(); + + MVertSample vert_sample; + MEdgeSample edge_sample; + MPolySample poly_sample; + MLoopSample loop_sample; + + std::vector<N3f> loop_normals_buffer; + + // TODO decide how to handle vertex/face normals, in caching vs. export ... + DM_ensure_normals(output_dm); + + create_sample_verts(output_dm, vert_sample); + create_sample_edges(output_dm, edge_sample); + create_sample_polys(output_dm, poly_sample); + create_sample_loops(output_dm, loop_sample); + + N3fArraySample lnormals = create_sample_loop_normals(output_dm, loop_normals_buffer); + + OPolyMeshSchema::Sample sample = OPolyMeshSchema::Sample( + P3fArraySample(vert_sample.co), + Int32ArraySample(loop_sample.verts), + Int32ArraySample(poly_sample.totloop), + OV2fGeomParam::Sample(), /* XXX define how/which UV map should be considered primary for the alembic schema */ + ON3fGeomParam::Sample(lnormals, kFacevaryingScope) + ); + schema.set(sample); + + m_prop_vert_normals.set(N3fArraySample(vert_sample.no)); + m_prop_vert_flag.set(CharArraySample(vert_sample.flag)); + m_prop_vert_bweight.set(CharArraySample(vert_sample.bweight)); + + m_prop_edge_verts.set(UInt32ArraySample(edge_sample.verts)); + m_prop_edge_flag.set(Int16ArraySample(edge_sample.flag)); + m_prop_edge_crease.set(CharArraySample(edge_sample.crease)); + m_prop_edge_bweight.set(CharArraySample(edge_sample.bweight)); + + m_prop_poly_mat_nr.set(Int16ArraySample(poly_sample.mat_nr)); + m_prop_poly_flag.set(CharArraySample(poly_sample.flag)); + + m_prop_loop_verts.set(Int32ArraySample(loop_sample.verts)); + m_prop_loop_edges.set(Int32ArraySample(loop_sample.edges)); + + CustomData *vdata = output_dm->getVertDataLayout(output_dm); + int num_vdata = output_dm->getNumVerts(output_dm); + m_vert_data_writer.write_sample(vdata, num_vdata, user_props); + + CustomData *edata = output_dm->getEdgeDataLayout(output_dm); + int num_edata = output_dm->getNumEdges(output_dm); + m_edge_data_writer.write_sample(edata, num_edata, user_props); + + CustomData *pdata = output_dm->getPolyDataLayout(output_dm); + int num_pdata = output_dm->getNumPolys(output_dm); + m_poly_data_writer.write_sample(pdata, num_pdata, user_props); + + CustomData *ldata = output_dm->getLoopDataLayout(output_dm); + int num_ldata = output_dm->getNumLoops(output_dm); + m_loop_data_writer.write_sample(ldata, num_ldata, user_props); + + DM_ensure_tessface(output_dm); + CustomData *fdata = output_dm->getTessFaceDataLayout(output_dm); + int num_fdata = output_dm->getNumTessFaces(output_dm); + m_face_data_writer.write_sample(fdata, num_fdata, user_props); +} + +/* ========================================================================= */ + +AbcDerivedMeshReader::AbcDerivedMeshReader(const std::string &name, Object *ob) : + DerivedMeshReader(ob, name), + m_vert_data_reader("vertex_data", CD_MASK_CACHE_VERT), + m_edge_data_reader("edge_data", CD_MASK_CACHE_EDGE), + m_face_data_reader("face_data", CD_MASK_CACHE_FACE), + m_poly_data_reader("poly_data", CD_MASK_CACHE_POLY), + m_loop_data_reader("loop_data", CD_MASK_CACHE_LOOP) +{ +} + +AbcDerivedMeshReader::~AbcDerivedMeshReader() +{ +} + +void AbcDerivedMeshReader::init_abc(IObject object) +{ + if (m_mesh) + return; + m_mesh = IPolyMesh(object, kWrapExisting); + + IPolyMeshSchema &schema = m_mesh.getSchema(); + ICompoundProperty geom_props = schema.getArbGeomParams(); + ICompoundProperty user_props = schema.getUserProperties(); + + m_prop_vert_normals = IN3fArrayProperty(user_props, "vertex_normals", 0); + m_prop_vert_flag = ICharArrayProperty(user_props, "vertex_flag", 0); + m_prop_vert_bweight = ICharArrayProperty(user_props, "vertex_bweight", 0); + + m_prop_edge_verts = IUInt32ArrayProperty(user_props, "edge_verts", 0); + m_prop_edge_flag = IInt16ArrayProperty(user_props, "edge_flag", 0); + m_prop_edge_crease = ICharArrayProperty(user_props, "edge_crease", 0); + m_prop_edge_bweight = ICharArrayProperty(user_props, "edge_bweight", 0); + + m_prop_poly_mat_nr = IInt16ArrayProperty(user_props, "poly_mat_nr", 0); + m_prop_poly_flag = ICharArrayProperty(user_props, "poly_flag", 0); + + m_prop_loop_verts = IInt32ArrayProperty(user_props, "loop_verts", 0); + m_prop_loop_edges = IInt32ArrayProperty(user_props, "loop_edges", 0); +} + +static PTCReadSampleResult apply_sample_verts(DerivedMesh *dm, P3fArraySamplePtr sample_co, N3fArraySamplePtr sample_no, + CharArraySamplePtr sample_flag, CharArraySamplePtr sample_bweight) +{ + int totvert = dm->getNumVerts(dm); + + if (sample_co->size() != totvert || + sample_no->size() != totvert || + sample_flag->size() != totvert || + sample_bweight->size() != totvert) + { + return PTC_READ_SAMPLE_INVALID; + } + + MVert *mv = dm->getVertArray(dm); + for (int i = 0; i < totvert; ++i) { + copy_v3_v3(mv->co, (*sample_co)[i].getValue()); + normal_float_to_short_v3(mv->no, (*sample_no)[i].getValue()); + mv->flag = (*sample_flag)[i]; + mv->bweight = (*sample_bweight)[i]; + + ++mv; + } + + return PTC_READ_SAMPLE_EXACT; +} + +static PTCReadSampleResult apply_sample_edges(DerivedMesh *dm, UInt32ArraySamplePtr sample_verts, Int16ArraySamplePtr sample_flag, + CharArraySamplePtr sample_crease, CharArraySamplePtr sample_bweight, bool &has_edges) +{ + int totedge = dm->getNumEdges(dm); + if (sample_verts->size() != totedge * 2 || + sample_flag->size() != totedge || + sample_crease->size() != totedge || + sample_bweight->size() != totedge) + { + has_edges = false; + return PTC_READ_SAMPLE_INVALID; + } + + const uint32_t *data_verts = sample_verts->get(); + const int16_t *data_flag = sample_flag->get(); + const int8_t *data_crease = sample_crease->get(); + const int8_t *data_bweight = sample_bweight->get(); + + MEdge *me = dm->getEdgeArray(dm); + for (int i = 0; i < totedge; ++i) { + me->v1 = data_verts[0]; + me->v2 = data_verts[1]; + me->flag = *data_flag; + me->crease = *data_crease; + me->bweight = *data_bweight; + + ++me; + data_verts += 2; + ++data_flag; + ++data_crease; + ++data_bweight; + } + + return PTC_READ_SAMPLE_EXACT; +} + +static PTCReadSampleResult apply_sample_polys(DerivedMesh *dm, Int32ArraySamplePtr sample_totloop, Int16ArraySamplePtr sample_mat_nr, CharArraySamplePtr sample_flag) +{ + int totpoly = dm->getNumPolys(dm); + if (sample_totloop->size() != totpoly || + sample_mat_nr->size() != totpoly || + sample_flag->size() != totpoly) + { + return PTC_READ_SAMPLE_INVALID; + } + + const int32_t *data_totloop = sample_totloop->get(); + const int16_t *data_mat_nr = sample_mat_nr->get(); + const int8_t *data_flag = sample_flag->get(); + + int loopstart = 0; + MPoly *mp = dm->getPolyArray(dm); + for (int i = 0; i < totpoly; ++i) { + mp->totloop = *data_totloop; + mp->loopstart = loopstart; + mp->mat_nr = *data_mat_nr; + mp->flag = *data_flag; + + loopstart += mp->totloop; + + ++mp; + ++data_totloop; + ++data_mat_nr; + ++data_flag; + } + + return PTC_READ_SAMPLE_EXACT; +} + +static PTCReadSampleResult apply_sample_loops(DerivedMesh *dm, Int32ArraySamplePtr sample_verts, Int32ArraySamplePtr sample_edges, bool &has_edges) +{ + int totloop = dm->getNumLoops(dm); + if (sample_verts->size() != totloop) + return PTC_READ_SAMPLE_INVALID; + + const int32_t *data_verts = sample_verts->get(); + + MLoop *ml = dm->getLoopArray(dm); + for (int i = 0; i < totloop; ++i) { + ml->v = *data_verts; + + ++ml; + ++data_verts; + } + + /* edge data is optional, if not available the edges must be recalculated */ + if (sample_edges->size() == totloop) { + const int32_t *data_edges = sample_edges->get(); + + MLoop *ml = dm->getLoopArray(dm); + for (int i = 0; i < totloop; ++i) { + ml->e = *data_edges; + + ++ml; + ++data_edges; + } + } + else { + has_edges = false; + } + + return PTC_READ_SAMPLE_EXACT; +} + +PTCReadSampleResult AbcDerivedMeshReader::read_sample_abc(chrono_t time) +{ +#ifdef USE_TIMING + double start_time; + double time_get_sample, time_build_mesh, time_calc_edges, time_calc_normals; + +#define PROFILE_START \ + start_time = PIL_check_seconds_timer(); +#define PROFILE_END(var) \ + var = PIL_check_seconds_timer() - start_time; +#else +#define PROFILE_START ; +#define PROFILE_END(var) ; +#endif + + /* discard existing result data */ + discard_result(); + + if (!m_mesh) + return PTC_READ_SAMPLE_INVALID; + + IPolyMeshSchema &schema = m_mesh.getSchema(); + if (schema.getNumSamples() == 0) + return PTC_READ_SAMPLE_INVALID; + ICompoundProperty user_props = schema.getUserProperties(); + + ISampleSelector ss = get_frame_sample_selector(time); + + PROFILE_START; + IPolyMeshSchema::Sample sample; + schema.get(sample, ss); + + P3fArraySamplePtr vert_co = abc_interpolate_sample_linear(schema.getPositionsProperty(), time); + Int32ArraySamplePtr loop_verts = sample.getFaceIndices(); + Int32ArraySamplePtr poly_totloop = sample.getFaceCounts(); + + N3fArraySamplePtr vnormals; + bool has_normals = false; + if (m_prop_vert_normals && m_prop_vert_normals.getNumSamples() > 0) { + vnormals = abc_interpolate_sample_linear(m_prop_vert_normals, time, InterpolateSemanticVector_Slerp); + has_normals = vnormals->valid(); + } + + UInt32ArraySamplePtr edge_verts = m_prop_edge_verts.getValue(ss); + Int32ArraySamplePtr loop_edges = m_prop_loop_edges.getValue(ss); + PROFILE_END(time_get_sample); + + PROFILE_START; + bool has_edges = true; // XXX do we have to check for existing sample in advance? + int totverts = vert_co->size(); + int totloops = loop_verts->size(); + int totpolys = poly_totloop->size(); + int totedges = has_edges ? edge_verts->size() >> 1 : 0; + m_result = CDDM_new(totverts, totedges, 0, totloops, totpolys); + + apply_sample_verts(m_result, vert_co, vnormals, m_prop_vert_flag.getValue(ss), m_prop_vert_bweight.getValue(ss)); + apply_sample_edges(m_result, edge_verts, m_prop_edge_flag.getValue(ss), m_prop_edge_crease.getValue(ss), m_prop_edge_bweight.getValue(ss), has_edges); + apply_sample_polys(m_result, poly_totloop, m_prop_poly_mat_nr.getValue(ss), m_prop_poly_flag.getValue(ss)); + apply_sample_loops(m_result, loop_verts, loop_edges, has_edges); + PROFILE_END(time_build_mesh); + + CustomData *vdata = m_result->getVertDataLayout(m_result); + int num_vdata = totverts; + m_vert_data_reader.read_sample(ss, vdata, num_vdata, user_props); + + CustomData *edata = m_result->getEdgeDataLayout(m_result); + int num_edata = totedges; + m_edge_data_reader.read_sample(ss, edata, num_edata, user_props); + + CustomData *pdata = m_result->getPolyDataLayout(m_result); + int num_pdata = totpolys; + m_poly_data_reader.read_sample(ss, pdata, num_pdata, user_props); + + CustomData *ldata = m_result->getLoopDataLayout(m_result); + int num_ldata = totloops; + m_loop_data_reader.read_sample(ss, ldata, num_ldata, user_props); + + DM_ensure_tessface(m_result); + CustomData *fdata = m_result->getTessFaceDataLayout(m_result); + int num_fdata = m_result->getNumTessFaces(m_result); + m_face_data_reader.read_sample(ss, fdata, num_fdata, user_props); + + PROFILE_START; + if (!has_edges) + CDDM_calc_edges(m_result); + PROFILE_END(time_calc_edges); + + PROFILE_START; + /* we need all normal properties defined, otherwise have to recalculate */ + has_normals &= CustomData_has_layer(pdata, CD_NORMAL); + if (!has_normals) { + /* make sure normals are recalculated if there is no sample data */ + m_result->dirty = (DMDirtyFlag)((int)m_result->dirty | DM_DIRTY_NORMALS); + } + DM_ensure_normals(m_result); /* only recalculates normals if no valid samples were found (has_normals == false) */ + PROFILE_END(time_calc_normals); + +// BLI_assert(DM_is_valid(m_result)); + +#ifdef USE_TIMING + printf("-------- Point Cache Timing --------\n"); + printf("read sample: %f seconds\n", time_get_sample); + printf("build mesh: %f seconds\n", time_build_mesh); + printf("calculate edges: %f seconds\n", time_calc_edges); + printf("calculate normals: %f seconds\n", time_calc_normals); + printf("------------------------------------\n"); +#endif + + return PTC_READ_SAMPLE_EXACT; +} + +/* ========================================================================= */ + +AbcDerivedFinalRealtimeWriter::AbcDerivedFinalRealtimeWriter(const std::string &name, Object *ob) : + AbcDerivedMeshWriter(name, ob, &ob->derivedFinal) +{ +} + + +AbcDerivedFinalRenderWriter::AbcDerivedFinalRenderWriter(const std::string &name, Scene *scene, Object *ob, DerivedMesh **render_dm_ptr) : + AbcDerivedMeshWriter(name, ob, render_dm_ptr), + m_scene(scene) +{ +} + + +} /* namespace PTC */ diff --git a/source/blender/pointcache/alembic/abc_mesh.h b/source/blender/pointcache/alembic/abc_mesh.h new file mode 100644 index 00000000000..e00ccc3eef1 --- /dev/null +++ b/source/blender/pointcache/alembic/abc_mesh.h @@ -0,0 +1,153 @@ +/* + * Copyright 2014, Blender Foundation. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef PTC_ABC_MESH_H +#define PTC_ABC_MESH_H + +#include <Alembic/AbcGeom/IPolyMesh.h> +#include <Alembic/AbcGeom/OPolyMesh.h> + +#include "ptc_types.h" + +#include "abc_customdata.h" +#include "abc_reader.h" +#include "abc_schema.h" +#include "abc_writer.h" + +extern "C" { +#include "DNA_modifier_types.h" + +#include "BKE_DerivedMesh.h" +} + +struct Object; +struct DerivedMesh; + +namespace PTC { + +class AbcDerivedMeshWriter : public DerivedMeshWriter, public AbcWriter { +public: + AbcDerivedMeshWriter(const std::string &name, Object *ob, DerivedMesh **dm_ptr); + ~AbcDerivedMeshWriter(); + + void init_abc(Abc::OObject parent); + + void write_sample(); + +private: + AbcGeom::OPolyMesh m_mesh; + + /* MVert attributes */ + AbcGeom::ON3fArrayProperty m_prop_vert_normals; + AbcGeom::OCharArrayProperty m_prop_vert_flag; + AbcGeom::OCharArrayProperty m_prop_vert_bweight; + + /* MEdge attributes */ + AbcGeom::OUInt32ArrayProperty m_prop_edge_verts; + AbcGeom::OInt16ArrayProperty m_prop_edge_flag; + AbcGeom::OCharArrayProperty m_prop_edge_crease; + AbcGeom::OCharArrayProperty m_prop_edge_bweight; + + /* MPoly attributes */ + AbcGeom::OInt16ArrayProperty m_prop_poly_mat_nr; + AbcGeom::OCharArrayProperty m_prop_poly_flag; + + /* MLoop attributes */ + AbcGeom::OInt32ArrayProperty m_prop_loop_verts; + AbcGeom::OInt32ArrayProperty m_prop_loop_edges; + + CustomDataWriter m_vert_data_writer; + CustomDataWriter m_edge_data_writer; + CustomDataWriter m_face_data_writer; + CustomDataWriter m_poly_data_writer; + CustomDataWriter m_loop_data_writer; +}; + +class AbcDerivedMeshReader : public DerivedMeshReader, public AbcReader { +public: + AbcDerivedMeshReader(const std::string &name, Object *ob); + ~AbcDerivedMeshReader(); + + void init_abc(Abc::IObject object); + + PTCReadSampleResult read_sample_abc(chrono_t time); + +private: + AbcGeom::IPolyMesh m_mesh; + + /* MVert attributes */ + AbcGeom::IN3fArrayProperty m_prop_vert_normals; + AbcGeom::ICharArrayProperty m_prop_vert_flag; + AbcGeom::ICharArrayProperty m_prop_vert_bweight; + + /* MEdge attributes */ + AbcGeom::IUInt32ArrayProperty m_prop_edge_verts; + AbcGeom::IInt16ArrayProperty m_prop_edge_flag; + AbcGeom::ICharArrayProperty m_prop_edge_crease; + AbcGeom::ICharArrayProperty m_prop_edge_bweight; + + /* MPoly attributes */ + AbcGeom::IInt16ArrayProperty m_prop_poly_mat_nr; + AbcGeom::ICharArrayProperty m_prop_poly_flag; + + /* MLoop attributes */ + AbcGeom::IInt32ArrayProperty m_prop_loop_verts; + AbcGeom::IInt32ArrayProperty m_prop_loop_edges; + + CustomDataReader m_vert_data_reader; + CustomDataReader m_edge_data_reader; + CustomDataReader m_face_data_reader; + CustomDataReader m_poly_data_reader; + CustomDataReader m_loop_data_reader; +}; + + +/* ------------------------------------------------------------------------- + * Writing derived mesh results requires different variants + * depending on viewport/render output and whether a cache modifier is used. + * + * Render DMs are constructed on-the-fly for each sample write, since they + * are not constructed immediately during scene frame updates. The writer is + * expected to only be called once per frame and object. + * + * If a cache modifier is used it must be have be activate at the time when + * the DM is built. For viewport output this means it should activate the + * modifier during it's whole lifetime, so that it caches meshes during the + * scene frame update. For render output the modifier should only be active + * during the render DM construction. + * ------------------------------------------------------------------------- */ + + +class AbcDerivedFinalRealtimeWriter : public AbcDerivedMeshWriter { +public: + AbcDerivedFinalRealtimeWriter(const std::string &name, Object *ob); +}; + + +class AbcDerivedFinalRenderWriter : public AbcDerivedMeshWriter { +public: + AbcDerivedFinalRenderWriter(const std::string &name, Scene *scene, Object *ob, DerivedMesh **render_dm_ptr); + +private: + Scene *m_scene; +}; + + +} /* namespace PTC */ + +#endif /* PTC_MESH_H */ diff --git a/source/blender/pointcache/alembic/abc_object.cpp b/source/blender/pointcache/alembic/abc_object.cpp new file mode 100644 index 00000000000..6d4fcdb5b1e --- /dev/null +++ b/source/blender/pointcache/alembic/abc_object.cpp @@ -0,0 +1,161 @@ +/* + * Copyright 2013, Blender Foundation. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "abc_mesh.h" +#include "abc_object.h" +#include "abc_particles.h" + +extern "C" { +#include "BLI_math.h" + +#include "DNA_object_types.h" + +#include "BKE_object.h" +} + +namespace PTC { + +using namespace Abc; +using namespace AbcGeom; + +thread_mutex AbcObjectWriter::m_sample_write_mutex; + +AbcObjectWriter::AbcObjectWriter(const std::string &name, Scene *scene, Object *ob, bool do_mesh, bool do_hair) : + ObjectWriter(ob, name), + m_scene(scene), + m_final_dm(NULL), + m_dm_writer(0) +{ + if (do_mesh) { + if (m_ob->type == OB_MESH) { + m_dm_writer = new AbcDerivedMeshWriter("mesh", ob, &m_final_dm); + } + } + + if (do_hair) { + for (ParticleSystem *psys = (ParticleSystem *)ob->particlesystem.first; psys; psys = psys->next) { + if (psys->part && psys->part->type == PART_HAIR) { + m_hair_writers.push_back(new AbcHairWriter(psys->name, ob, psys)); + } + } + } +} + +AbcObjectWriter::~AbcObjectWriter() +{ + if (m_dm_writer) + delete m_dm_writer; + for (int i = 0; i < m_hair_writers.size(); ++i) + if (m_hair_writers[i]) + delete m_hair_writers[i]; +} + +void AbcObjectWriter::init_abc() +{ + if (m_abc_object) + return; + + m_abc_object = abc_archive()->add_id_object<OObject>((ID *)m_ob); + + if (m_dm_writer) { + /* XXX not nice */ + m_dm_writer->init(abc_archive()); + m_dm_writer->init_abc(m_abc_object); + } + + for (int i = 0; i < m_hair_writers.size(); ++i) { + AbcHairWriter *hair_writer = m_hair_writers[i]; + if (hair_writer) { + hair_writer->init(abc_archive()); + hair_writer->init_abc(m_abc_object); + } + } +} + +#if 0 +void AbcObjectWriter::create_refs() +{ + if ((m_ob->transflag & OB_DUPLIGROUP) && m_ob->dup_group) { + OObject abc_group = abc_archive()->get_id_object((ID *)m_ob->dup_group); + if (abc_group) + m_abc_object.addChildInstance(abc_group, "dup_group"); + } +} +#endif + +void AbcObjectWriter::write_sample() +{ + if (!m_abc_object) + return; + + if (m_dm_writer) { + if (abc_archive()->use_render()) { + m_final_dm = mesh_create_derived_render(m_scene, m_ob, CD_MASK_BAREMESH); + + if (m_final_dm) { + { + thread_scoped_lock lock(m_sample_write_mutex); + m_dm_writer->write_sample(); + } + + m_final_dm->release(m_final_dm); + } + } + else { + m_final_dm = m_ob->derivedFinal; + if (!m_final_dm) + m_final_dm = mesh_get_derived_final(m_scene, m_ob, CD_MASK_BAREMESH); + + if (m_final_dm) { + thread_scoped_lock lock(m_sample_write_mutex); + m_dm_writer->write_sample(); + } + } + } + + for (int i = 0; i < m_hair_writers.size(); ++i) { + AbcHairWriter *hair_writer = m_hair_writers[i]; + if (hair_writer) { + thread_scoped_lock lock(m_sample_write_mutex); + hair_writer->write_sample(); + } + } +} + + +AbcObjectReader::AbcObjectReader(const std::string &name, Object *ob) : + ObjectReader(ob, name) +{ +} + +void AbcObjectReader::init_abc(IObject object) +{ + if (m_abc_object) + return; + m_abc_object = object; +} + +PTCReadSampleResult AbcObjectReader::read_sample_abc(chrono_t /*time*/) +{ + if (!m_abc_object) + return PTC_READ_SAMPLE_INVALID; + + return PTC_READ_SAMPLE_EXACT; +} + +} /* namespace PTC */ diff --git a/source/blender/pointcache/alembic/abc_object.h b/source/blender/pointcache/alembic/abc_object.h new file mode 100644 index 00000000000..973a3312d1b --- /dev/null +++ b/source/blender/pointcache/alembic/abc_object.h @@ -0,0 +1,84 @@ +/* + * Copyright 2013, Blender Foundation. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef PTC_ABC_OBJECT_H +#define PTC_ABC_OBJECT_H + +#include <vector> + +#include <Alembic/Abc/IObject.h> +#include <Alembic/Abc/OObject.h> +#include <Alembic/AbcGeom/IXform.h> +#include <Alembic/AbcGeom/OXform.h> + +#include "ptc_types.h" + +#include "abc_reader.h" +#include "abc_schema.h" +#include "abc_writer.h" + +#include "util_thread.h" + +struct DerivedMesh; +struct Object; +struct Scene; + +namespace PTC { + +class AbcDerivedMeshWriter; +class AbcHairWriter; + +class AbcObjectWriter : public ObjectWriter, public AbcWriter { +public: + typedef std::vector<AbcHairWriter *> HairWriters; + + AbcObjectWriter(const std::string &name, Scene *scene, Object *ob, bool do_mesh, bool do_hair); + ~AbcObjectWriter(); + + void init_abc(); +#if 0 + void create_refs(); +#endif + + void write_sample(); + +private: + Scene *m_scene; + DerivedMesh *m_final_dm; + + Abc::OObject m_abc_object; + AbcDerivedMeshWriter *m_dm_writer; + HairWriters m_hair_writers; + static thread_mutex m_sample_write_mutex; +}; + +class AbcObjectReader : public ObjectReader, public AbcReader { +public: + AbcObjectReader(const std::string &name, Object *ob); + + void init_abc(Abc::IObject object); + + PTCReadSampleResult read_sample_abc(chrono_t time); + +private: + Abc::IObject m_abc_object; +}; + +} /* namespace PTC */ + +#endif /* PTC_OBJECT_H */ diff --git a/source/blender/pointcache/alembic/abc_particles.cpp b/source/blender/pointcache/alembic/abc_particles.cpp new file mode 100644 index 00000000000..10e3ee55d3a --- /dev/null +++ b/source/blender/pointcache/alembic/abc_particles.cpp @@ -0,0 +1,1284 @@ +/* + * Copyright 2013, Blender Foundation. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "abc_cloth.h" +#include "abc_interpolate.h" +#include "abc_mesh.h" +#include "abc_particles.h" + +extern "C" { +#include "BLI_listbase.h" +#include "BLI_math_color.h" +#include "BLI_math.h" + +#include "DNA_listBase.h" +#include "DNA_modifier_types.h" +#include "DNA_object_types.h" +#include "DNA_particle_types.h" + +#include "BKE_anim.h" +#include "BKE_mesh_sample.h" +#include "BKE_particle.h" +#include "BKE_strands.h" +} + +namespace PTC { + +using namespace Abc; +using namespace AbcGeom; + + +struct StrandsChildrenSample { + std::vector<int32_t> numverts; + std::vector<Quatf> root_rotations; + std::vector<V3f> root_positions; + std::vector<float32_t> cutoff; + + std::vector<V3f> positions; + std::vector<float32_t> times; + std::vector<int32_t> parents; + std::vector<float32_t> parent_weights; + std::vector<V2f> curve_uvs; + std::vector<C3f> curve_vcols; +}; + +struct StrandsSample { + std::vector<int32_t> numverts; + std::vector<Quatf> root_rotations; + std::vector<uint32_t> root_orig_verts; + std::vector<float32_t> root_orig_weights; + std::vector<int32_t> root_orig_poly; + std::vector<uint32_t> root_orig_loops; + + std::vector<V3f> positions; + std::vector<float32_t> times; + std::vector<float32_t> weights; + + std::vector<V3f> motion_co; + std::vector<V3f> motion_vel; +}; + +AbcHairChildrenWriter::AbcHairChildrenWriter(const std::string &name, Object *ob, ParticleSystem *psys) : + ParticlesWriter(ob, psys, name) +{ + m_psmd = psys_get_modifier(ob, psys); +} + +AbcHairChildrenWriter::~AbcHairChildrenWriter() +{ +} + +void AbcHairChildrenWriter::init_abc(OObject parent) +{ + if (m_curves) + return; + + /* XXX non-escaped string construction here ... */ + m_curves = OCurves(parent, m_name, abc_archive()->frame_sampling_index()); + + OCurvesSchema &schema = m_curves.getSchema(); + OCompoundProperty geom_props = schema.getArbGeomParams(); + OCompoundProperty user_props = schema.getUserProperties(); + + m_prop_root_rot = OQuatfArrayProperty(user_props, "root_rotations", abc_archive()->frame_sampling()); + m_prop_root_positions = OV3fArrayProperty(user_props, "root_positions", abc_archive()->frame_sampling()); + m_param_cutoff = OFloatGeomParam(geom_props, "cutoff", false, kUniformScope, 1, 0); + m_param_times = OFloatGeomParam(geom_props, "times", false, kVertexScope, 1, 0); + m_prop_parents = OInt32ArrayProperty(user_props, "parents", abc_archive()->frame_sampling()); + m_prop_parent_weights = OFloatArrayProperty(user_props, "parent_weights", abc_archive()->frame_sampling()); + m_prop_curve_uvs = AbcGeom::OV2fArrayProperty(user_props, "curve_uvs", abc_archive()->frame_sampling()); + m_prop_curve_vcols = AbcGeom::OC3fArrayProperty(user_props, "curve_vcols", abc_archive()->frame_sampling()); +} + +static int hair_children_count_totkeys(ParticleCacheKey **pathcache, int totpart) +{ + int p; + int totkeys = 0; + + if (pathcache) { + for (p = 0; p < totpart; ++p) { + ParticleCacheKey *keys = pathcache[p]; + totkeys += keys->segments + 1; + } + } + + return totkeys; +} + +#if 0 +static int hair_children_parent_advance(HairKey *keys, int totkeys, float time, int k) +{ + for (; k + 1 < totkeys; ++k) { + if (keys[k+1].time > time) + break; + } + return k; +} + +static void hair_children_calc_strand(Object *ob, ParticleSystem *psys, ParticleSystemModifierData *psmd, ChildParticle *cpa, ParticleCacheKey *keys, int maxkeys, StrandsChildrenSample &sample) +{ + const bool between = (psys->part->childtype == PART_CHILD_FACES); + ParticleData *parent[4]; + float weight[4]; + float hairmat[4][4][4]; + int parent_key[4] = {0,0,0,0}; + + int i, k; + + if (between) { + for (i = 0; i < 4; ++i) { + parent[i] = &psys->particles[cpa->pa[i]]; + weight[i] = cpa->w[i]; + if (parent[i]) + psys_mat_hair_to_global(ob, psmd->dm, psys->part->from, parent[i], hairmat[i]); + } + } + else { + parent[0] = &psys->particles[cpa->parent]; + parent[1] = parent[2] = parent[3] = NULL; + weight[0] = 1.0f; + weight[1] = weight[2] = weight[3] = 0.0f; + if (parent[0]) + psys_mat_hair_to_global(ob, psmd->dm, psys->part->from, parent[0], hairmat[0]); + } + + int numkeys = keys->segments + 1; + for (k = 0; k < numkeys; ++k) { + ParticleCacheKey *key = &keys[k]; + /* XXX particle time values are too messy and confusing, recalculate */ + float time = maxkeys > 1 ? (float)k / (float)(maxkeys-1) : 0.0f; + + float parent_co[3]; + zero_v3(parent_co); + for (i = 0; i < 4; ++i) { + if (!parent[i] || weight[i] <= 0.0f) + continue; + parent_key[i] = hair_children_parent_advance(parent[i]->hair, parent[i]->totkey, time, parent_key[i]); + + float key_co[3]; + if (parent_key[i] + 1 < parent[i]->totkey) { + HairKey *key0 = &parent[i]->hair[parent_key[i]]; + HairKey *key1 = &parent[i]->hair[parent_key[i] + 1]; + float x = (key1->time > key0->time) ? (time - key0->time) / (key1->time - key0->time) : 0.0f; + interp_v3_v3v3(key_co, key0->co, key1->co, x); + } + else { + HairKey *key0 = &parent[i]->hair[parent_key[i]]; + copy_v3_v3(key_co, key0->co); + } + + madd_v3_v3fl(parent_co, key_co, weight[i]); + + /* Hair keys are in hair root space, pathcache keys are in world space, + * transform both to world space to calculate the offset + */ + mul_m4_v3(hairmat[i], parent_co); + } + + /* child position is an offset from the parent */ + float co[3]; + sub_v3_v3v3(co, key->co, parent_co); + + sample.positions.push_back(V3f(parent_co[0], parent_co[1], parent_co[2])); + sample.times.push_back(time); + } +} +#endif + +BLI_INLINE bool particle_get_face(ParticleSystem *psys, int num_tessface, ChildParticle *cpa, int *r_num, float **r_fuv) +{ + ParticleSettings *part = psys->part; + const bool between = (part->childtype == PART_CHILD_FACES); + + int num = DMCACHE_NOTFOUND; + float *fuv; + if (between) { + num = cpa->num; + fuv = cpa->fuv; + } + else if (part->from == PART_FROM_FACE) { + ParticleData *pa = psys->particles + cpa->pa[0]; + num = pa->num_dmcache; + if (num == DMCACHE_NOTFOUND) + num = pa->num; + if (num >= num_tessface) { + /* happens when simplify is enabled gives invalid coords but would crash otherwise */ + num = DMCACHE_NOTFOUND; + } + fuv = pa->fuv; + } + + if (ELEM(num, DMCACHE_NOTFOUND, DMCACHE_ISCHILD)) { + return false; + } + else { + *r_num = num; + *r_fuv = fuv; + return true; + } +} + +static void hair_children_get_uvs(ParticleSystem *psys, ParticleSystemModifierData *psmd, int totpart, StrandsChildrenSample &sample) +{ + const int num_tessface = psmd->dm->getNumTessFaces(psmd->dm); + + MFace *mface = (MFace *)psmd->dm->getTessFaceArray(psmd->dm); + const CustomData *facedata = psmd->dm->getTessFaceDataLayout(psmd->dm); + int tot_layers = CustomData_number_of_layers(facedata, CD_MTFACE); + + sample.curve_uvs.reserve(tot_layers * totpart); + + for (int num_uv = 0; num_uv < tot_layers; ++num_uv) { + MTFace *mtface = (MTFace *)CustomData_get_layer_n(facedata, CD_MTFACE, num_uv); + if (!mtface) + continue; + + for (int p = 0; p < totpart; ++p) { + ChildParticle *cpa = &psys->child[p]; + + int num; + float *fuv; + if (particle_get_face(psys, num_tessface, cpa, &num, &fuv)) { + MFace *mf = mface + num; + MTFace *mtf = mtface + num; + + float uv[2]; + psys_interpolate_uvs(mtf, mf->v4, fuv, uv); + sample.curve_uvs.push_back(V2f(uv[0], uv[1])); + } + else { + sample.curve_uvs.push_back(V2f(0.0f, 0.0f)); + } + } + } +} + +static void hair_children_get_vcols(ParticleSystem *psys, ParticleSystemModifierData *psmd, int totpart, StrandsChildrenSample &sample) +{ + const int num_tessface = psmd->dm->getNumTessFaces(psmd->dm); + + MFace *mface = (MFace *)psmd->dm->getTessFaceArray(psmd->dm); + const CustomData *facedata = psmd->dm->getTessFaceDataLayout(psmd->dm); + int tot_layers = CustomData_number_of_layers(facedata, CD_MCOL); + + sample.curve_vcols.reserve(tot_layers * totpart); + + for (int num_vcol = 0; num_vcol < tot_layers; ++num_vcol) { + MCol *mcol = (MCol *)CustomData_get_layer_n(facedata, CD_MCOL, num_vcol); + if (!mcol) + continue; + + for (int p = 0; p < totpart; ++p) { + ChildParticle *cpa = &psys->child[p]; + + int num; + float *fuv; + if (particle_get_face(psys, num_tessface, cpa, &num, &fuv)) { + MFace *mf = mface + num; + /* XXX another legacy thing: MCol are tessface data, but 4 values per face ... */ + MCol *mc = mcol + 4 * num; + + MCol col; + psys_interpolate_mcol(mc, mf->v4, fuv, &col); + /* XXX stupid legacy code: MCol stores values are BGR */ + unsigned char icol[3] = {col.b, col.g, col.r}; + C3f fcol; + rgb_uchar_to_float(fcol.getValue(), icol); + sample.curve_vcols.push_back(fcol); + } + else { + sample.curve_vcols.push_back(C3f(0.0f, 0.0f, 0.0f)); + } + } + } +} + +static void hair_children_create_sample(Object *ob, ParticleSystem *psys, ParticleSystemModifierData *psmd, ParticleCacheKey **pathcache, int totpart, int totkeys, int maxkeys, + StrandsChildrenSample &sample, bool write_constants) +{ + ParticleSettings *part = psys->part; + const bool between = (part->childtype == PART_CHILD_FACES); + + int p, k; + + if (write_constants) { + sample.numverts.reserve(totpart); + sample.parents.reserve(4*totpart); + sample.parent_weights.reserve(4*totpart); + + sample.positions.reserve(totkeys); + sample.times.reserve(totkeys); + } + + sample.root_rotations.reserve(totpart); + sample.root_positions.reserve(totpart); + + for (p = 0; p < totpart; ++p) { + ChildParticle *cpa = &psys->child[p]; + + float hairmat[4][4]; + psys_child_mat_to_object(ob, psys, psmd, cpa, hairmat); + + if (pathcache) { + ParticleCacheKey *keys = pathcache[p]; + int numkeys = keys->segments + 1; + + if (write_constants) { + sample.numverts.push_back(numkeys); + if (between) { + sample.parents.push_back(cpa->pa[0]); + sample.parents.push_back(cpa->pa[1]); + sample.parents.push_back(cpa->pa[2]); + sample.parents.push_back(cpa->pa[3]); + sample.parent_weights.push_back(cpa->w[0]); + sample.parent_weights.push_back(cpa->w[1]); + sample.parent_weights.push_back(cpa->w[2]); + sample.parent_weights.push_back(cpa->w[3]); + } + else { + sample.parents.push_back(cpa->parent); + sample.parents.push_back(-1); + sample.parents.push_back(-1); + sample.parents.push_back(-1); + sample.parent_weights.push_back(1.0f); + sample.parent_weights.push_back(0.0f); + sample.parent_weights.push_back(0.0f); + sample.parent_weights.push_back(0.0f); + } + + float imat[4][4]; + mul_m4_m4m4(imat, ob->obmat, hairmat); + invert_m4(imat); + + for (k = 0; k < numkeys; ++k) { + ParticleCacheKey *key = &keys[k]; + + /* pathcache keys are in world space, transform to hair root space */ + float co[3]; + mul_v3_m4v3(co, imat, key->co); + + sample.positions.push_back(V3f(co[0], co[1], co[2])); + /* XXX particle time values are too messy and confusing, recalculate */ + sample.times.push_back(maxkeys > 1 ? (float)k / (float)(maxkeys-1) : 0.0f); + } + } + } + + float qt[4]; + mat4_to_quat(qt, hairmat); + sample.root_rotations.push_back(Quatf(qt[0], qt[1], qt[2], qt[3])); + float *co = hairmat[3]; + sample.root_positions.push_back(V3f(co[0], co[1], co[2])); + sample.cutoff.push_back(-1.0f); + } + + if (write_constants) { + DM_ensure_tessface(psmd->dm); + hair_children_get_uvs(psys, psmd, totpart, sample); + hair_children_get_vcols(psys, psmd, totpart, sample); + } +} + +void AbcHairChildrenWriter::write_sample() +{ + if (!m_curves) + return; + + int totkeys = hair_children_count_totkeys(m_psys->childcache, m_psys->totchild); + + int keysteps = abc_archive()->use_render() ? m_psys->part->ren_step : m_psys->part->draw_step; + int maxkeys = (1 << keysteps) + 1 + (m_psys->part->kink); + if (ELEM(m_psys->part->kink, PART_KINK_SPIRAL)) + maxkeys += m_psys->part->kink_extra_steps; + + OCurvesSchema &schema = m_curves.getSchema(); + + StrandsChildrenSample child_sample; + OCurvesSchema::Sample sample; + if (schema.getNumSamples() == 0) { + /* write curve sizes only first time, assuming they are constant! */ + hair_children_create_sample(m_ob, m_psys, m_psmd, m_psys->childcache, m_psys->totchild, totkeys, maxkeys, child_sample, true); + sample = OCurvesSchema::Sample(child_sample.positions, child_sample.numverts); + + m_prop_parents.set(Int32ArraySample(child_sample.parents)); + m_prop_parent_weights.set(FloatArraySample(child_sample.parent_weights)); + m_prop_curve_uvs.set(V2fArraySample(child_sample.curve_uvs)); + m_prop_curve_vcols.set(C3fArraySample(child_sample.curve_vcols)); + + m_param_times.set(OFloatGeomParam::Sample(FloatArraySample(child_sample.times), kVertexScope)); + + schema.set(sample); + } + else { + hair_children_create_sample(m_ob, m_psys, m_psmd, m_psys->childcache, m_psys->totchild, totkeys, maxkeys, child_sample, false); + } + + m_prop_root_rot.set(QuatfArraySample(child_sample.root_rotations)); + m_prop_root_positions.set(V3fArraySample(child_sample.root_positions)); + m_param_cutoff.set(OFloatGeomParam::Sample(FloatArraySample(child_sample.cutoff), kUniformScope)); +} + + +AbcHairWriter::AbcHairWriter(const std::string &name, Object *ob, ParticleSystem *psys) : + ParticlesWriter(ob, psys, name), + m_child_writer("children", ob, psys) +{ + m_psmd = psys_get_modifier(ob, psys); +} + +AbcHairWriter::~AbcHairWriter() +{ +} + +void AbcHairWriter::init(WriterArchive *archive) +{ + AbcWriter::init(archive); + m_child_writer.init(archive); +} + +void AbcHairWriter::init_abc(OObject parent) +{ + if (m_curves) + return; + m_curves = OCurves(parent, m_name, abc_archive()->frame_sampling_index()); + + OCurvesSchema &schema = m_curves.getSchema(); + OCompoundProperty geom_props = schema.getArbGeomParams(); + + m_param_root_rot = OQuatfGeomParam(geom_props, "root_rotations", false, kUniformScope, 1, 0); + m_param_root_orig_verts = OUInt32GeomParam(geom_props, "root_orig_verts", false, kUniformScope, 1, 0); + m_param_root_orig_weights = OFloatGeomParam(geom_props, "root_orig_weights", false, kUniformScope, 1, 0); + m_param_root_orig_poly = OInt32GeomParam(geom_props, "root_orig_poly", false, kUniformScope, 1, 0); + m_param_root_orig_loops = OUInt32GeomParam(geom_props, "root_orig_loops", false, kUniformScope, 1, 0); + + m_param_times = OFloatGeomParam(geom_props, "times", false, kVertexScope, 1, 0); + m_param_weights = OFloatGeomParam(geom_props, "weights", false, kVertexScope, 1, 0); + + m_child_writer.init_abc(m_curves); +} + +static int hair_count_totverts(ParticleSystem *psys) +{ + int p; + int totverts = 0; + + for (p = 0; p < psys->totpart; ++p) { + ParticleData *pa = &psys->particles[p]; + totverts += pa->totkey; + } + + return totverts; +} + +static void hair_create_sample(Object *ob, DerivedMesh *dm, ParticleSystem *psys, StrandsSample &sample, bool do_numverts) +{ + int totpart = psys->totpart; + int totverts = hair_count_totverts(psys); + int p, k; + + if (totverts == 0) + return; + + if (do_numverts) + sample.numverts.reserve(totpart); + sample.root_rotations.reserve(totpart); + sample.root_orig_verts.reserve(totpart * 3); + sample.root_orig_weights.reserve(totpart * 3); + sample.root_orig_poly.reserve(totpart); + sample.root_orig_loops.reserve(totpart * 3); + sample.positions.reserve(totverts); + sample.times.reserve(totverts); + sample.weights.reserve(totverts); + + for (p = 0; p < totpart; ++p) { + ParticleData *pa = &psys->particles[p]; + int numverts = pa->totkey; + float hairmat[4][4]; + + if (do_numverts) + sample.numverts.push_back(numverts); + + psys_mat_hair_to_object(ob, dm, psys->part->from, pa, hairmat); + float root_qt[4]; + mat4_to_quat(root_qt, hairmat); + sample.root_rotations.push_back(Quatf(root_qt[0], root_qt[1], root_qt[2], root_qt[3])); + + MSurfaceSample surf; + BKE_mesh_sample_from_particle(&surf, psys, dm, pa); + sample.root_orig_verts.push_back(surf.orig_verts[0]); + sample.root_orig_verts.push_back(surf.orig_verts[1]); + sample.root_orig_verts.push_back(surf.orig_verts[2]); + sample.root_orig_weights.push_back(surf.orig_weights[0]); + sample.root_orig_weights.push_back(surf.orig_weights[1]); + sample.root_orig_weights.push_back(surf.orig_weights[2]); + sample.root_orig_poly.push_back(surf.orig_poly); + sample.root_orig_loops.push_back(surf.orig_loops[0]); + sample.root_orig_loops.push_back(surf.orig_loops[1]); + sample.root_orig_loops.push_back(surf.orig_loops[2]); + + for (k = 0; k < numverts; ++k) { + HairKey *key = &pa->hair[k]; + + /* hair keys are in "hair space" relative to the mesh, + * store them in object space for compatibility and to avoid + * complexities of how particles work. + */ + float co[3]; + mul_v3_m4v3(co, hairmat, key->co); + + sample.positions.push_back(V3f(co[0], co[1], co[2])); + /* XXX particle time values are too messy and confusing, recalculate */ + sample.times.push_back(numverts > 1 ? (float)k / (float)(numverts-1) : 0.0f); + sample.weights.push_back(key->weight); + } + } +} + +void AbcHairWriter::write_sample() +{ + if (!m_curves) + return; + if (!m_psmd || !m_psmd->dm) + return; + + OCurvesSchema &schema = m_curves.getSchema(); + + StrandsSample hair_sample; + OCurvesSchema::Sample sample; + if (schema.getNumSamples() == 0) { + /* write curve sizes only first time, assuming they are constant! */ + hair_create_sample(m_ob, m_psmd->dm, m_psys, hair_sample, true); + sample = OCurvesSchema::Sample(hair_sample.positions, hair_sample.numverts); + } + else { + hair_create_sample(m_ob, m_psmd->dm, m_psys, hair_sample, false); + sample = OCurvesSchema::Sample(hair_sample.positions); + } + schema.set(sample); + + m_param_root_rot.set(OQuatfGeomParam::Sample(QuatfArraySample(hair_sample.root_rotations), kUniformScope)); + m_param_root_orig_verts.set(OUInt32GeomParam::Sample(UInt32ArraySample(hair_sample.root_orig_verts), kUniformScope)); + m_param_root_orig_weights.set(OFloatGeomParam::Sample(FloatArraySample(hair_sample.root_orig_weights), kUniformScope)); + m_param_root_orig_poly.set(OInt32GeomParam::Sample(Int32ArraySample(hair_sample.root_orig_poly), kUniformScope)); + m_param_root_orig_loops.set(OUInt32GeomParam::Sample(UInt32ArraySample(hair_sample.root_orig_loops), kUniformScope)); + + m_param_times.set(OFloatGeomParam::Sample(FloatArraySample(hair_sample.times), kVertexScope)); + m_param_weights.set(OFloatGeomParam::Sample(FloatArraySample(hair_sample.weights), kVertexScope)); + + m_child_writer.write_sample(); +} + + +AbcStrandsChildrenWriter::AbcStrandsChildrenWriter(const std::string &name, const std::string &abc_name, DupliObjectData *dobdata) : + m_name(name), + m_abc_name(abc_name), + m_dobdata(dobdata) +{ +} + +StrandsChildren *AbcStrandsChildrenWriter::get_strands() const +{ + StrandsChildren *children; + BKE_dupli_object_data_find_strands(m_dobdata, m_name.c_str(), NULL, &children); + return children; +} + +void AbcStrandsChildrenWriter::init_abc(OObject parent) +{ + if (m_curves) + return; + m_curves = OCurves(parent, m_abc_name, abc_archive()->frame_sampling_index()); + + OCurvesSchema &schema = m_curves.getSchema(); + OCompoundProperty geom_props = schema.getArbGeomParams(); + OCompoundProperty user_props = schema.getUserProperties(); + + m_prop_root_rot = OQuatfArrayProperty(user_props, "root_rotations", abc_archive()->frame_sampling()); + m_prop_root_positions = OV3fArrayProperty(user_props, "root_positions", abc_archive()->frame_sampling()); + m_param_cutoff = OFloatGeomParam(geom_props, "cutoff", false, kUniformScope, 1, abc_archive()->frame_sampling()); + m_param_times = OFloatGeomParam(geom_props, "times", false, kVertexScope, 1, abc_archive()->frame_sampling()); + m_prop_parents = OInt32ArrayProperty(user_props, "parents", abc_archive()->frame_sampling()); + m_prop_parent_weights = OFloatArrayProperty(user_props, "parent_weights", abc_archive()->frame_sampling()); + m_prop_curve_uvs = AbcGeom::OV2fArrayProperty(user_props, "curve_uvs", abc_archive()->frame_sampling()); + m_prop_curve_vcols = AbcGeom::OC3fArrayProperty(user_props, "curve_vcols", abc_archive()->frame_sampling()); +} + +static void strands_children_get_uvs(StrandsChildren *strands, StrandsChildrenSample &sample) +{ + int totuv = strands->numuv * strands->totcurves; + + sample.curve_uvs.reserve(totuv); + + for (int i = 0; i < totuv; ++i) { + StrandsChildCurveUV *uv = &strands->curve_uvs[i]; + sample.curve_uvs.push_back(V2f(uv->uv[0], uv->uv[1])); + } +} + +static void strands_children_get_vcols(StrandsChildren *strands, StrandsChildrenSample &sample) +{ + int totvcol = strands->numvcol * strands->totcurves; + + sample.curve_vcols.reserve(totvcol); + + for (int i = 0; i < totvcol; ++i) { + StrandsChildCurveVCol *vcol = &strands->curve_vcols[i]; + sample.curve_vcols.push_back(C3f(vcol->vcol[0], vcol->vcol[1], vcol->vcol[2])); + } +} + +static void strands_children_create_sample(StrandsChildren *strands, StrandsChildrenSample &sample, bool write_constants) +{ + int totcurves = strands->totcurves; + int totverts = strands->totverts; + + if (write_constants) { + sample.numverts.reserve(totcurves); + sample.parents.reserve(4*totcurves); + sample.parent_weights.reserve(4*totcurves); + + sample.positions.reserve(totverts); + sample.times.reserve(totverts); + } + + sample.root_rotations.reserve(totcurves); + sample.root_positions.reserve(totcurves); + + StrandChildIterator it_strand; + for (BKE_strand_child_iter_init(&it_strand, strands); BKE_strand_child_iter_valid(&it_strand); BKE_strand_child_iter_next(&it_strand)) { + int numverts = it_strand.curve->numverts; + + if (write_constants) { + sample.numverts.push_back(numverts); + + sample.parents.push_back(it_strand.curve->parents[0]); + sample.parents.push_back(it_strand.curve->parents[1]); + sample.parents.push_back(it_strand.curve->parents[2]); + sample.parents.push_back(it_strand.curve->parents[3]); + sample.parent_weights.push_back(it_strand.curve->parent_weights[0]); + sample.parent_weights.push_back(it_strand.curve->parent_weights[1]); + sample.parent_weights.push_back(it_strand.curve->parent_weights[2]); + sample.parent_weights.push_back(it_strand.curve->parent_weights[3]); + + StrandChildVertexIterator it_vert; + for (BKE_strand_child_vertex_iter_init(&it_vert, &it_strand); BKE_strand_child_vertex_iter_valid(&it_vert); BKE_strand_child_vertex_iter_next(&it_vert)) { + const float *co = it_vert.vertex->base; + sample.positions.push_back(V3f(co[0], co[1], co[2])); + sample.times.push_back(it_vert.vertex->time); + } + } + + float qt[4]; + mat4_to_quat(qt, it_strand.curve->root_matrix); + sample.root_rotations.push_back(Quatf(qt[0], qt[1], qt[2], qt[3])); + float *co = it_strand.curve->root_matrix[3]; + sample.root_positions.push_back(V3f(co[0], co[1], co[2])); + sample.cutoff.push_back(it_strand.curve->cutoff); + } + + if (write_constants) { + strands_children_get_uvs(strands, sample); + strands_children_get_vcols(strands, sample); + } +} + +void AbcStrandsChildrenWriter::write_sample() +{ + if (!m_curves) + return; + StrandsChildren *strands = get_strands(); + if (!strands) + return; + + OCurvesSchema &schema = m_curves.getSchema(); + + StrandsChildrenSample strands_sample; + OCurvesSchema::Sample sample; + if (schema.getNumSamples() == 0) { + /* write curve sizes only first time, assuming they are constant! */ + strands_children_create_sample(strands, strands_sample, true); + sample = OCurvesSchema::Sample(strands_sample.positions, strands_sample.numverts); + + m_prop_parents.set(Int32ArraySample(strands_sample.parents)); + m_prop_parent_weights.set(FloatArraySample(strands_sample.parent_weights)); + m_prop_curve_uvs.set(V2fArraySample(strands_sample.curve_uvs)); + m_prop_curve_vcols.set(C3fArraySample(strands_sample.curve_vcols)); + + m_param_times.set(OFloatGeomParam::Sample(FloatArraySample(strands_sample.times), kVertexScope)); + + schema.set(sample); + } + else { + strands_children_create_sample(strands, strands_sample, false); + } + + m_prop_root_rot.set(QuatfArraySample(strands_sample.root_rotations)); + m_prop_root_positions.set(V3fArraySample(strands_sample.root_positions)); + m_param_cutoff.set(OFloatGeomParam::Sample(FloatArraySample(strands_sample.cutoff), kUniformScope)); +} + + +AbcStrandsWriter::AbcStrandsWriter(const std::string &name, DupliObjectData *dobdata) : + m_name(name), + m_dobdata(dobdata), + m_child_writer(name, "children", dobdata) +{ +} + +Strands *AbcStrandsWriter::get_strands() const +{ + Strands *strands; + BKE_dupli_object_data_find_strands(m_dobdata, m_name.c_str(), &strands, NULL); + return strands; +} + +void AbcStrandsWriter::init(WriterArchive *archive) +{ + AbcWriter::init(archive); + m_child_writer.init(archive); +} + +void AbcStrandsWriter::init_abc(OObject parent) +{ + if (m_curves) + return; + m_curves = OCurves(parent, m_name, abc_archive()->frame_sampling_index()); + + OCurvesSchema &schema = m_curves.getSchema(); + OCompoundProperty geom_props = schema.getArbGeomParams(); + + m_param_root_rot = OQuatfGeomParam(geom_props, "root_rotations", false, kUniformScope, 1, abc_archive()->frame_sampling()); + m_param_root_orig_verts = OUInt32GeomParam(geom_props, "root_orig_verts", false, kUniformScope, 1, 0); + m_param_root_orig_weights = OFloatGeomParam(geom_props, "root_orig_weights", false, kUniformScope, 1, 0); + m_param_root_orig_poly = OInt32GeomParam(geom_props, "root_orig_poly", false, kUniformScope, 1, 0); + m_param_root_orig_loops = OUInt32GeomParam(geom_props, "root_orig_loops", false, kUniformScope, 1, 0); + + m_param_times = OFloatGeomParam(geom_props, "times", false, kVertexScope, 1, abc_archive()->frame_sampling()); + m_param_weights = OFloatGeomParam(geom_props, "weights", false, kVertexScope, 1, abc_archive()->frame_sampling()); + + m_param_motion_state = OCompoundProperty(geom_props, "motion_state", abc_archive()->frame_sampling()); + m_param_motion_co = OP3fGeomParam(m_param_motion_state, "position", false, kVertexScope, 1, abc_archive()->frame_sampling()); + m_param_motion_vel = OV3fGeomParam(m_param_motion_state, "velocity", false, kVertexScope, 1, abc_archive()->frame_sampling()); + + m_child_writer.init_abc(m_curves); +} + +static void strands_create_sample(Strands *strands, StrandsSample &sample, bool do_numverts) +{ + const bool do_state = strands->state; + + int totcurves = strands->totcurves; + int totverts = strands->totverts; + + if (totverts == 0) + return; + + if (do_numverts) + sample.numverts.reserve(totcurves); + sample.root_rotations.reserve(totcurves); + sample.root_orig_verts.reserve(totcurves * 3); + sample.root_orig_weights.reserve(totcurves * 3); + sample.root_orig_poly.reserve(totcurves); + sample.root_orig_loops.reserve(totcurves * 3); + + sample.positions.reserve(totverts); + sample.times.reserve(totverts); + sample.weights.reserve(totverts); + if (do_state) { + sample.motion_co.reserve(totverts); + sample.motion_vel.reserve(totverts); + } + + StrandIterator it_strand; + for (BKE_strand_iter_init(&it_strand, strands); BKE_strand_iter_valid(&it_strand); BKE_strand_iter_next(&it_strand)) { + int numverts = it_strand.curve->numverts; + + if (do_numverts) + sample.numverts.push_back(numverts); + float qt[4]; + mat3_to_quat(qt, it_strand.curve->root_matrix); + sample.root_rotations.push_back(Quatf(qt[0], qt[1], qt[2], qt[3])); + + sample.root_orig_verts.push_back(it_strand.curve->msurf.orig_verts[0]); + sample.root_orig_verts.push_back(it_strand.curve->msurf.orig_verts[1]); + sample.root_orig_verts.push_back(it_strand.curve->msurf.orig_verts[2]); + sample.root_orig_weights.push_back(it_strand.curve->msurf.orig_weights[0]); + sample.root_orig_weights.push_back(it_strand.curve->msurf.orig_weights[1]); + sample.root_orig_weights.push_back(it_strand.curve->msurf.orig_weights[2]); + sample.root_orig_poly.push_back(it_strand.curve->msurf.orig_poly); + sample.root_orig_loops.push_back(it_strand.curve->msurf.orig_loops[0]); + sample.root_orig_loops.push_back(it_strand.curve->msurf.orig_loops[1]); + sample.root_orig_loops.push_back(it_strand.curve->msurf.orig_loops[2]); + + StrandVertexIterator it_vert; + for (BKE_strand_vertex_iter_init(&it_vert, &it_strand); BKE_strand_vertex_iter_valid(&it_vert); BKE_strand_vertex_iter_next(&it_vert)) { + const float *co = it_vert.vertex->co; + sample.positions.push_back(V3f(co[0], co[1], co[2])); + sample.times.push_back(it_vert.vertex->time); + sample.weights.push_back(it_vert.vertex->weight); + + if (do_state) { + float *co = it_vert.state->co; + float *vel = it_vert.state->vel; + sample.motion_co.push_back(V3f(co[0], co[1], co[2])); + sample.motion_vel.push_back(V3f(vel[0], vel[1], vel[2])); + } + } + } +} + +void AbcStrandsWriter::write_sample() +{ + if (!m_curves) + return; + Strands *strands = get_strands(); + if (!strands) + return; + + OCurvesSchema &schema = m_curves.getSchema(); + + StrandsSample strands_sample; + OCurvesSchema::Sample sample; + if (schema.getNumSamples() == 0) { + /* write curve sizes only first time, assuming they are constant! */ + strands_create_sample(strands, strands_sample, true); + sample = OCurvesSchema::Sample(strands_sample.positions, strands_sample.numverts); + } + else { + strands_create_sample(strands, strands_sample, false); + sample = OCurvesSchema::Sample(strands_sample.positions); + } + schema.set(sample); + + m_param_root_rot.set(OQuatfGeomParam::Sample(QuatfArraySample(strands_sample.root_rotations), kUniformScope)); + m_param_root_orig_verts.set(OUInt32GeomParam::Sample(UInt32ArraySample(strands_sample.root_orig_verts), kUniformScope)); + m_param_root_orig_weights.set(OFloatGeomParam::Sample(FloatArraySample(strands_sample.root_orig_weights), kUniformScope)); + m_param_root_orig_poly.set(OInt32GeomParam::Sample(Int32ArraySample(strands_sample.root_orig_poly), kUniformScope)); + m_param_root_orig_loops.set(OUInt32GeomParam::Sample(UInt32ArraySample(strands_sample.root_orig_loops), kUniformScope)); + + m_param_times.set(OFloatGeomParam::Sample(FloatArraySample(strands_sample.times), kVertexScope)); + m_param_weights.set(OFloatGeomParam::Sample(FloatArraySample(strands_sample.weights), kVertexScope)); + + if (strands->state) { + m_param_motion_co.set(OP3fGeomParam::Sample(P3fArraySample(strands_sample.motion_co), kVertexScope)); + m_param_motion_vel.set(OV3fGeomParam::Sample(V3fArraySample(strands_sample.motion_vel), kVertexScope)); + } + + m_child_writer.write_sample(); +} + +#if 0 +#define PRINT_M3_FORMAT "((%.3f, %.3f, %.3f), (%.3f, %.3f, %.3f), (%.3f, %.3f, %.3f))" +#define PRINT_M3_ARGS(m) (double)m[0][0], (double)m[0][1], (double)m[0][2], (double)m[1][0], (double)m[1][1], (double)m[1][2], (double)m[2][0], (double)m[2][1], (double)m[2][2] +#define PRINT_M4_FORMAT "((%.3f, %.3f, %.3f, %.3f), (%.3f, %.3f, %.3f, %.3f), (%.3f, %.3f, %.3f, %.3f), (%.3f, %.3f, %.3f, %.3f))" +#define PRINT_M4_ARGS(m) (double)m[0][0], (double)m[0][1], (double)m[0][2], (double)m[0][3], (double)m[1][0], (double)m[1][1], (double)m[1][2], (double)m[1][3], \ + (double)m[2][0], (double)m[2][1], (double)m[2][2], (double)m[2][3], (double)m[3][0], (double)m[3][1], (double)m[3][2], (double)m[3][3] +#endif + +AbcStrandsChildrenReader::AbcStrandsChildrenReader(StrandsChildren *strands) : + m_strands(strands) +{ +} + +AbcStrandsChildrenReader::~AbcStrandsChildrenReader() +{ + discard_result(); +} + +void AbcStrandsChildrenReader::init_abc(IObject object) +{ + if (m_curves) + return; + m_curves = ICurves(object, kWrapExisting); + + ICurvesSchema &schema = m_curves.getSchema(); + ICompoundProperty geom_props = schema.getArbGeomParams(); + ICompoundProperty user_props = schema.getUserProperties(); + + m_prop_root_rot = IQuatfArrayProperty(user_props, "root_rotations"); + m_prop_root_positions = IV3fArrayProperty(user_props, "root_positions"); + if (geom_props.getPropertyHeader("cutoff")) + m_param_cutoff = IFloatGeomParam(geom_props, "cutoff"); + m_param_times = IFloatGeomParam(geom_props, "times"); + m_prop_parents = IInt32ArrayProperty(user_props, "parents", 0); + m_prop_parent_weights = IFloatArrayProperty(user_props, "parent_weights", 0); + m_prop_curve_uvs = IV2fArrayProperty(user_props, "curve_uvs", 0); + m_prop_curve_vcols = IC3fArrayProperty(user_props, "curve_vcols", 0); +} + +PTCReadSampleResult AbcStrandsChildrenReader::read_sample_abc(chrono_t time) +{ + ISampleSelector ss = get_frame_sample_selector(time); + + if (!m_curves.valid()) { + return PTC_READ_SAMPLE_INVALID; + } + + ICurvesSchema &schema = m_curves.getSchema(); + if (schema.getNumSamples() == 0) { + return PTC_READ_SAMPLE_INVALID; + } + + ICurvesSchema::Sample sample; + schema.get(sample, ss); + + P3fArraySamplePtr sample_co = sample.getPositions(); + Int32ArraySamplePtr sample_numvert = sample.getCurvesNumVertices(); + QuatfArraySamplePtr sample_root_rotations = abc_interpolate_sample_linear(m_prop_root_rot, time); + V3fArraySamplePtr sample_root_positions = abc_interpolate_sample_linear(m_prop_root_positions, time); + IFloatGeomParam::Sample sample_cutoff; + if (m_param_cutoff) + sample_cutoff = m_param_cutoff.getExpandedValue(ss); + IFloatGeomParam::Sample sample_time = m_param_times.getExpandedValue(ss); + Int32ArraySamplePtr sample_parents = m_prop_parents.getValue(ss); + FloatArraySamplePtr sample_parent_weights = m_prop_parent_weights.getValue(ss); + V2fArraySamplePtr sample_curve_uvs = m_prop_curve_uvs.getValue(ss); + C3fArraySamplePtr sample_curve_vcols = m_prop_curve_vcols.getValue(ss); + + if (!sample_co || !sample_numvert) { + return PTC_READ_SAMPLE_INVALID; + } + + int totcurves = sample_numvert->size(); + int totverts = sample_co->size(); + + if (!totcurves) { + return PTC_READ_SAMPLE_INVALID; + } + + if (sample_root_rotations->size() != totcurves || + sample_root_positions->size() != totcurves || + sample_parents->size() != 4 * totcurves || + sample_parent_weights->size() != 4 * totcurves) + return PTC_READ_SAMPLE_INVALID; + + if (m_strands && (m_strands->totcurves != totcurves || m_strands->totverts != totverts)) + m_strands = NULL; + if (!m_strands) + m_strands = BKE_strands_children_new(totcurves, totverts); + + const int32_t *numvert = sample_numvert->get(); + const Quatf *root_rot = sample_root_rotations->get(); + const V3f *root_positions = sample_root_positions->get(); + const float32_t *cutoff = sample_cutoff ? sample_cutoff.getVals()->get() : NULL; + const int32_t *parents = sample_parents->get(); + const float32_t *parent_weights = sample_parent_weights->get(); + for (int i = 0; i < sample_numvert->size(); ++i) { + StrandsChildCurve *scurve = &m_strands->curves[i]; + scurve->numverts = *numvert; + scurve->cutoff = -1.0f; + + float qt[4] = {root_rot->r, root_rot->v.x, root_rot->v.y, root_rot->v.z}; + quat_to_mat4(scurve->root_matrix, qt); + copy_v3_v3(scurve->root_matrix[3], root_positions->getValue()); + + scurve->cutoff = cutoff ? *cutoff : -1.0f; + + scurve->parents[0] = parents[0]; + scurve->parents[1] = parents[1]; + scurve->parents[2] = parents[2]; + scurve->parents[3] = parents[3]; + scurve->parent_weights[0] = parent_weights[0]; + scurve->parent_weights[1] = parent_weights[1]; + scurve->parent_weights[2] = parent_weights[2]; + scurve->parent_weights[3] = parent_weights[3]; + + ++numvert; + ++root_rot; + ++root_positions; + parents += 4; + parent_weights += 4; + if (cutoff) ++cutoff; + } + + if (sample_curve_uvs->size() > 0 && sample_curve_uvs->size() % totcurves == 0) { + int num_layers = sample_curve_uvs->size() / totcurves; + + const V2f *uvs = sample_curve_uvs->get(); + + BKE_strands_children_add_uvs(m_strands, num_layers); + + StrandsChildCurveUV *scurve_uv = m_strands->curve_uvs; + for (int j = 0; j < num_layers; ++j) { + for (int i = 0; i < totcurves; ++i) { + copy_v2_v2(scurve_uv->uv, uvs->getValue()); + + ++uvs; + ++scurve_uv; + } + } + } + + if (sample_curve_vcols->size() > 0 && sample_curve_vcols->size() % totcurves == 0) { + int num_layers = sample_curve_vcols->size() / totcurves; + + const C3f *vcols = sample_curve_vcols->get(); + + BKE_strands_children_add_vcols(m_strands, num_layers); + + StrandsChildCurveVCol *scurve_vcol = m_strands->curve_vcols; + for (int j = 0; j < num_layers; ++j) { + for (int i = 0; i < totcurves; ++i) { + copy_v3_v3(scurve_vcol->vcol, vcols->getValue()); + + ++vcols; + ++scurve_vcol; + } + } + } + + const V3f *co = sample_co->get(); + const float32_t *curve_time = sample_time.getVals()->get(); + for (int i = 0; i < sample_co->size(); ++i) { + StrandsChildVertex *svert = &m_strands->verts[i]; + copy_v3_v3(svert->co, co->getValue()); + copy_v3_v3(svert->base, svert->co); + svert->time = *curve_time; + + ++co; + ++curve_time; + } + + BKE_strands_children_ensure_normals(m_strands); + + return PTC_READ_SAMPLE_EXACT; +} + +StrandsChildren *AbcStrandsChildrenReader::acquire_result() +{ + StrandsChildren *strands = m_strands; + m_strands = NULL; + return strands; +} + +void AbcStrandsChildrenReader::discard_result() +{ + BKE_strands_children_free(m_strands); + m_strands = NULL; +} + + +AbcStrandsReader::AbcStrandsReader(Strands *strands, StrandsChildren *children, bool read_motion, bool read_children) : + m_read_motion(read_motion), + m_read_children(read_children), + m_strands(strands), + m_child_reader(children) +{ +} + +AbcStrandsReader::~AbcStrandsReader() +{ + discard_result(); +} + +void AbcStrandsReader::init(ReaderArchive *archive) +{ + AbcReader::init(archive); + m_child_reader.init(archive); +} + +void AbcStrandsReader::init_abc(IObject object) +{ + if (m_curves) + return; + m_curves = ICurves(object, kWrapExisting); + + ICurvesSchema &schema = m_curves.getSchema(); + ICompoundProperty geom_props = schema.getArbGeomParams(); + + m_param_root_rot = IQuatfGeomParam(geom_props, "root_rotations"); + m_param_root_orig_verts = IUInt32GeomParam(geom_props, "root_orig_verts"); + m_param_root_orig_weights = IFloatGeomParam(geom_props, "root_orig_weights"); + m_param_root_orig_poly = IInt32GeomParam(geom_props, "root_orig_poly"); + m_param_root_orig_loops = IUInt32GeomParam(geom_props, "root_orig_loops"); + + m_param_times = IFloatGeomParam(geom_props, "times"); + m_param_weights = IFloatGeomParam(geom_props, "weights"); + + if (m_read_motion && geom_props.getPropertyHeader("motion_state")) { + m_param_motion_state = ICompoundProperty(geom_props, "motion_state"); + m_param_motion_co = IP3fGeomParam(m_param_motion_state, "position"); + m_param_motion_vel = IV3fGeomParam(m_param_motion_state, "velocity"); + } + + if (m_read_children && m_curves.getChildHeader("children")) { + IObject child = m_curves.getChild("children"); + m_child_reader.init_abc(child); + } +} + +PTCReadSampleResult AbcStrandsReader::read_sample_abc(chrono_t time) +{ + ISampleSelector ss = get_frame_sample_selector(time); + + if (!m_curves.valid()) + return PTC_READ_SAMPLE_INVALID; + + ICurvesSchema &schema = m_curves.getSchema(); + if (schema.getNumSamples() == 0) + return PTC_READ_SAMPLE_INVALID; + + ICurvesSchema::Sample sample, sample_base; + schema.get(sample, ss); + schema.get(sample_base, ISampleSelector((index_t)0)); + + P3fArraySamplePtr sample_co = sample.getPositions(); + P3fArraySamplePtr sample_co_base = sample_base.getPositions(); + Int32ArraySamplePtr sample_numvert = sample.getCurvesNumVertices(); + IQuatfGeomParam::Sample sample_root_rotations = m_param_root_rot.getExpandedValue(ss); + IUInt32GeomParam::Sample sample_root_orig_verts = m_param_root_orig_verts.getExpandedValue(ss); + IFloatGeomParam::Sample sample_root_orig_weights = m_param_root_orig_weights.getExpandedValue(ss); + IInt32GeomParam::Sample sample_root_orig_poly = m_param_root_orig_poly.getExpandedValue(ss); + IUInt32GeomParam::Sample sample_root_orig_loops = m_param_root_orig_loops.getExpandedValue(ss); + IQuatfGeomParam::Sample sample_root_rotations_base = m_param_root_rot.getExpandedValue(ISampleSelector((index_t)0)); + IFloatGeomParam::Sample sample_time = m_param_times.getExpandedValue(ss); + IFloatGeomParam::Sample sample_weight = m_param_weights.getExpandedValue(ss); + + if (!sample_co || !sample_numvert || !sample_co_base || sample_co_base->size() != sample_co->size()) + return PTC_READ_SAMPLE_INVALID; + + if (m_strands && (m_strands->totcurves != sample_numvert->size() || m_strands->totverts != sample_co->size())) + m_strands = NULL; + if (!m_strands) + m_strands = BKE_strands_new(sample_numvert->size(), sample_co->size()); + + const int32_t *numvert = sample_numvert->get(); + const Quatf *root_rot = sample_root_rotations.getVals()->get(); + const uint32_t *orig_verts = sample_root_orig_verts.getVals()->get(); + const float32_t *orig_weights = sample_root_orig_weights.getVals()->get(); + const int32_t *orig_poly = sample_root_orig_poly.getVals()->get(); + const uint32_t *orig_loops = sample_root_orig_loops.getVals()->get(); + for (int i = 0; i < sample_numvert->size(); ++i) { + StrandsCurve *scurve = &m_strands->curves[i]; + scurve->numverts = *numvert; + float qt[4] = {root_rot->r, root_rot->v.x, root_rot->v.y, root_rot->v.z}; + quat_to_mat3(scurve->root_matrix, qt); + scurve->msurf.orig_verts[0] = orig_verts[0]; + scurve->msurf.orig_verts[1] = orig_verts[1]; + scurve->msurf.orig_verts[2] = orig_verts[2]; + scurve->msurf.orig_weights[0] = orig_weights[0]; + scurve->msurf.orig_weights[1] = orig_weights[1]; + scurve->msurf.orig_weights[2] = orig_weights[2]; + scurve->msurf.orig_poly = *orig_poly; + scurve->msurf.orig_loops[0] = orig_loops[0]; + scurve->msurf.orig_loops[1] = orig_loops[1]; + scurve->msurf.orig_loops[2] = orig_loops[2]; + + ++numvert; + ++root_rot; + orig_verts += 3; + orig_weights += 3; + orig_poly += 1; + orig_loops += 3; + } + + const V3f *co = sample_co->get(); + const float32_t *curve_time = sample_time.getVals()->get(); + const float32_t *weight = sample_weight.getVals()->get(); + for (int i = 0; i < sample_co->size(); ++i) { + StrandsVertex *svert = &m_strands->verts[i]; + copy_v3_v3(svert->co, co->getValue()); + svert->time = *curve_time; + svert->weight = *weight; + + ++co; + ++curve_time; + ++weight; + } + + /* Correction for base coordinates: these are in object space of frame 1, + * but we want the relative shape. Offset them to the current root location. + */ + const Quatf *root_rot_base = sample_root_rotations_base.getVals()->get(); + const V3f *co_base = sample_co_base->get(); + StrandIterator it_strand; + for (BKE_strand_iter_init(&it_strand, m_strands); BKE_strand_iter_valid(&it_strand); BKE_strand_iter_next(&it_strand)) { + if (it_strand.curve->numverts <= 0) + continue; + + float hairmat_base[4][4]; + float qt[4] = {root_rot_base->r, root_rot_base->v.x, root_rot_base->v.y, root_rot_base->v.z}; + quat_to_mat4(hairmat_base, qt); + copy_v3_v3(hairmat_base[3], co_base[0].getValue()); + + float hairmat[4][4]; + copy_m4_m3(hairmat, it_strand.curve->root_matrix); + copy_v3_v3(hairmat[3], it_strand.verts[0].co); + + float mat[4][4]; + invert_m4_m4(mat, hairmat_base); + mul_m4_m4m4(mat, hairmat, mat); + + StrandVertexIterator it_vert; + for (BKE_strand_vertex_iter_init(&it_vert, &it_strand); BKE_strand_vertex_iter_valid(&it_vert); BKE_strand_vertex_iter_next(&it_vert)) { +// mul_v3_m4v3(it_vert.vertex->base, mat, co_base->getValue()); + copy_v3_v3(it_vert.vertex->base, it_vert.vertex->co); + + ++co_base; + } + + ++root_rot_base; + } + + if (m_read_motion && + m_param_motion_co && m_param_motion_co.getNumSamples() > 0 && + m_param_motion_vel && m_param_motion_vel.getNumSamples() > 0) + { + IP3fGeomParam::Sample sample_motion_co = m_param_motion_co.getExpandedValue(ss); + IV3fGeomParam::Sample sample_motion_vel = m_param_motion_vel.getExpandedValue(ss); + + const V3f *co = sample_motion_co.getVals()->get(); + const V3f *vel = sample_motion_vel.getVals()->get(); + if (co && vel) { + BKE_strands_add_motion_state(m_strands); + + for (int i = 0; i < m_strands->totverts; ++i) { + StrandsMotionState *ms = &m_strands->state[i]; + copy_v3_v3(ms->co, co->getValue()); + copy_v3_v3(ms->vel, vel->getValue()); + + ++co; + ++vel; + } + } + } + + BKE_strands_ensure_normals(m_strands); + + if (m_read_children) { + m_child_reader.read_sample_abc(time); + } + + return PTC_READ_SAMPLE_EXACT; +} + +Strands *AbcStrandsReader::acquire_result() +{ + Strands *strands = m_strands; + m_strands = NULL; + return strands; +} + +void AbcStrandsReader::discard_result() +{ + BKE_strands_free(m_strands); + m_strands = NULL; +} + + +} /* namespace PTC */ diff --git a/source/blender/pointcache/alembic/abc_particles.h b/source/blender/pointcache/alembic/abc_particles.h new file mode 100644 index 00000000000..26d1bce78bd --- /dev/null +++ b/source/blender/pointcache/alembic/abc_particles.h @@ -0,0 +1,222 @@ +/* + * Copyright 2013, Blender Foundation. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef PTC_ABC_PARTICLES_H +#define PTC_ABC_PARTICLES_H + +#include <Alembic/AbcGeom/IPoints.h> +#include <Alembic/AbcGeom/OPoints.h> +#include <Alembic/AbcGeom/ICurves.h> +#include <Alembic/AbcGeom/OCurves.h> + +#include "ptc_types.h" + +#include "PTC_api.h" + +#include "abc_reader.h" +#include "abc_schema.h" +#include "abc_writer.h" +#include "abc_cloth.h" + +struct ListBase; +struct Object; +struct ParticleSystem; +struct ParticleCacheKey; +struct Strands; +struct StrandsChildren; + +namespace PTC { + +class AbcDerivedMeshWriter; +class AbcDerivedMeshReader; + + +class AbcHairChildrenWriter : public ParticlesWriter, public AbcWriter { +public: + AbcHairChildrenWriter(const std::string &name, Object *ob, ParticleSystem *psys); + ~AbcHairChildrenWriter(); + + void init_abc(Abc::OObject parent); + + void write_sample(); + +private: + ParticleSystemModifierData *m_psmd; + + AbcGeom::OCurves m_curves; + AbcGeom::OQuatfArrayProperty m_prop_root_rot; + AbcGeom::OV3fArrayProperty m_prop_root_positions; + AbcGeom::OFloatGeomParam m_param_cutoff; + AbcGeom::OFloatGeomParam m_param_times; + AbcGeom::OInt32ArrayProperty m_prop_parents; + AbcGeom::OFloatArrayProperty m_prop_parent_weights; + AbcGeom::OV2fArrayProperty m_prop_curve_uvs; + AbcGeom::OC3fArrayProperty m_prop_curve_vcols; +}; + + +class AbcHairWriter : public ParticlesWriter, public AbcWriter { +public: + AbcHairWriter(const std::string &name, Object *ob, ParticleSystem *psys); + ~AbcHairWriter(); + + void init(WriterArchive *archive); + void init_abc(Abc::OObject parent); + + void write_sample(); + +private: + ParticleSystemModifierData *m_psmd; + + AbcGeom::OCurves m_curves; + AbcGeom::OQuatfGeomParam m_param_root_rot; + AbcGeom::OUInt32GeomParam m_param_root_orig_verts; + AbcGeom::OFloatGeomParam m_param_root_orig_weights; + AbcGeom::OInt32GeomParam m_param_root_orig_poly; + AbcGeom::OUInt32GeomParam m_param_root_orig_loops; + AbcGeom::OFloatGeomParam m_param_times; + AbcGeom::OFloatGeomParam m_param_weights; + + AbcHairChildrenWriter m_child_writer; +}; + + +class AbcStrandsChildrenWriter : public AbcWriter { +public: + AbcStrandsChildrenWriter(const std::string &name, const std::string &abc_name, DupliObjectData *dobdata); + + StrandsChildren *get_strands() const; + + void init_abc(Abc::OObject parent); + + void write_sample(); + +private: + std::string m_name; + std::string m_abc_name; + DupliObjectData *m_dobdata; + + AbcGeom::OCurves m_curves; + AbcGeom::OQuatfArrayProperty m_prop_root_rot; + AbcGeom::OV3fArrayProperty m_prop_root_positions; + AbcGeom::OFloatGeomParam m_param_cutoff; + AbcGeom::OFloatGeomParam m_param_times; + AbcGeom::OInt32ArrayProperty m_prop_parents; + AbcGeom::OFloatArrayProperty m_prop_parent_weights; + AbcGeom::OV2fArrayProperty m_prop_curve_uvs; + AbcGeom::OC3fArrayProperty m_prop_curve_vcols; +}; + + +class AbcStrandsWriter : public AbcWriter { +public: + AbcStrandsWriter(const std::string &name, DupliObjectData *dobdata); + + Strands *get_strands() const; + + void init(WriterArchive *archive); + void init_abc(Abc::OObject parent); + + void write_sample(); + +private: + std::string m_name; + DupliObjectData *m_dobdata; + + AbcGeom::OCurves m_curves; + AbcGeom::OQuatfGeomParam m_param_root_rot; + AbcGeom::OUInt32GeomParam m_param_root_orig_verts; + AbcGeom::OFloatGeomParam m_param_root_orig_weights; + AbcGeom::OInt32GeomParam m_param_root_orig_poly; + AbcGeom::OUInt32GeomParam m_param_root_orig_loops; + AbcGeom::OFloatGeomParam m_param_times; + AbcGeom::OFloatGeomParam m_param_weights; + AbcGeom::OCompoundProperty m_param_motion_state; + AbcGeom::OP3fGeomParam m_param_motion_co; + AbcGeom::OV3fGeomParam m_param_motion_vel; + + AbcStrandsChildrenWriter m_child_writer; +}; + + +class AbcStrandsChildrenReader : public AbcReader { +public: + AbcStrandsChildrenReader(StrandsChildren *strands); + ~AbcStrandsChildrenReader(); + + void init_abc(Abc::IObject object); + + PTCReadSampleResult read_sample_abc(chrono_t time); + + StrandsChildren *get_result() { return m_strands; } + StrandsChildren *acquire_result(); + void discard_result(); + +private: + StrandsChildren *m_strands; + + AbcGeom::ICurves m_curves; + AbcGeom::IQuatfArrayProperty m_prop_root_rot; + AbcGeom::IV3fArrayProperty m_prop_root_positions; + AbcGeom::IFloatGeomParam m_param_cutoff; + AbcGeom::IFloatGeomParam m_param_times; + AbcGeom::IInt32ArrayProperty m_prop_parents; + AbcGeom::IFloatArrayProperty m_prop_parent_weights; + AbcGeom::IV2fArrayProperty m_prop_curve_uvs; + AbcGeom::IC3fArrayProperty m_prop_curve_vcols; +}; + + +class AbcStrandsReader : public AbcReader { +public: + AbcStrandsReader(Strands *strands, StrandsChildren *children, bool read_motion, bool read_children); + ~AbcStrandsReader(); + + void init(ReaderArchive *archive); + void init_abc(Abc::IObject object); + + PTCReadSampleResult read_sample_abc(chrono_t time); + + Strands *acquire_result(); + void discard_result(); + + AbcStrandsChildrenReader &child_reader() { return m_child_reader; } + +private: + bool m_read_motion, m_read_children; + Strands *m_strands; + + AbcGeom::ICurves m_curves; + AbcGeom::IQuatfGeomParam m_param_root_rot; + AbcGeom::IUInt32GeomParam m_param_root_orig_verts; + AbcGeom::IFloatGeomParam m_param_root_orig_weights; + AbcGeom::IInt32GeomParam m_param_root_orig_poly; + AbcGeom::IUInt32GeomParam m_param_root_orig_loops; + AbcGeom::IFloatGeomParam m_param_times; + AbcGeom::IFloatGeomParam m_param_weights; + AbcGeom::ICompoundProperty m_param_motion_state; + AbcGeom::IP3fGeomParam m_param_motion_co; + AbcGeom::IV3fGeomParam m_param_motion_vel; + + AbcStrandsChildrenReader m_child_reader; +}; + + +} /* namespace PTC */ + +#endif /* PTC_PARTICLES_H */ diff --git a/source/blender/pointcache/alembic/abc_reader.cpp b/source/blender/pointcache/alembic/abc_reader.cpp new file mode 100644 index 00000000000..17f574af7ac --- /dev/null +++ b/source/blender/pointcache/alembic/abc_reader.cpp @@ -0,0 +1,194 @@ +/* + * Copyright 2013, Blender Foundation. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +//#include <Alembic/AbcCoreHDF5/ReadWrite.h> +#include <Alembic/AbcCoreOgawa/ReadWrite.h> +#include <Alembic/Abc/ArchiveInfo.h> +#include <Alembic/Abc/IArchive.h> +#include <Alembic/Abc/IObject.h> + +#include "alembic.h" +#include "abc_reader.h" + +#include "util_error_handler.h" + +extern "C" { +#include "DNA_ID.h" +} + +namespace PTC { + +using namespace Abc; + +AbcReaderArchive *AbcReaderArchive::open(double fps, float start_frame, const std::string &filename, ErrorHandler *error_handler) +{ + IArchive abc_archive; + PTC_SAFE_CALL_BEGIN +// abc_archive = IArchive(AbcCoreHDF5::ReadArchive(), filename, Abc::ErrorHandler::kThrowPolicy); + abc_archive = IArchive(AbcCoreOgawa::ReadArchive(), filename, Abc::ErrorHandler::kThrowPolicy); + PTC_SAFE_CALL_END_HANDLER(error_handler) + + if (abc_archive) + return new AbcReaderArchive(fps, start_frame, error_handler, abc_archive); + else + return NULL; +} + +AbcReaderArchive::AbcReaderArchive(double fps, float start_frame, ErrorHandler *error_handler, IArchive abc_archive) : + FrameMapper(fps, start_frame), + m_error_handler(error_handler), + m_use_render(false), + m_abc_archive(abc_archive) +{ + if (m_abc_archive.getTop().getChildHeader("root")) + m_abc_root = IObject(m_abc_archive.getTop(), "root"); + else + m_abc_root = IObject(); + + if (m_abc_archive.getTop().getChildHeader("root_render")) + m_abc_root_render = IObject(m_abc_archive.getTop(), "root_render"); + else + m_abc_root_render = IObject(); +} + +AbcReaderArchive::~AbcReaderArchive() +{ +} + +PTCArchiveResolution AbcReaderArchive::get_resolutions() +{ + int res = 0; + if (m_abc_root) + res |= PTC_RESOLUTION_PREVIEW; + if (m_abc_root_render) + res |= PTC_RESOLUTION_RENDER; + return (PTCArchiveResolution)res; +} + +Abc::IObject AbcReaderArchive::root() +{ + if (m_use_render) + return m_abc_root_render ? m_abc_root_render : m_abc_root; + else + return m_abc_root ? m_abc_root : m_abc_root_render; +} + +IObject AbcReaderArchive::get_id_object(ID *id) +{ + if (!m_abc_archive) + return IObject(); + + IObject root = this->root(); + return root.getChild(id->name); +} + +bool AbcReaderArchive::has_id_object(ID *id) +{ + if (!m_abc_archive) + return false; + + IObject root = this->root(); + return root.getChild(id->name).valid(); +} + +bool AbcReaderArchive::get_frame_range(int &start_frame, int &end_frame) +{ + if (m_abc_archive) { + double start_time, end_time; + GetArchiveStartAndEndTime(m_abc_archive, start_time, end_time); + start_frame = (int)time_to_frame(start_time); + end_frame = (int)time_to_frame(end_time); + return true; + } + else { + start_frame = end_frame = 1; + return false; + } +} + +void AbcReaderArchive::get_info_stream(void (*stream)(void *, const char *), void *userdata) +{ + if (m_abc_archive) + abc_archive_info_stream(m_abc_archive, stream, userdata); +} + +void AbcReaderArchive::get_info(CacheArchiveInfo *info, IDProperty *metadata) +{ + if (m_abc_archive) + abc_archive_info_nodes(m_abc_archive, info, metadata, false, false); +} + +void AbcReaderArchive::get_info_nodes(CacheArchiveInfo *info, bool calc_bytes_size) +{ + if (m_abc_archive) + abc_archive_info_nodes(m_abc_archive, info, NULL, true, calc_bytes_size); +} + +ISampleSelector AbcReaderArchive::get_frame_sample_selector(float frame) +{ + return ISampleSelector(frame_to_time(frame), ISampleSelector::kFloorIndex); +} + +ISampleSelector AbcReaderArchive::get_frame_sample_selector(chrono_t time) +{ + return ISampleSelector(time, ISampleSelector::kFloorIndex); +} + + +bool AbcReader::get_frame_range(int &start_frame, int &end_frame) +{ + return m_abc_archive->get_frame_range(start_frame, end_frame); +} + +PTCReadSampleResult AbcReader::test_sample(float frame) +{ + if (m_abc_archive) { + int start_frame, end_frame; + m_abc_archive->get_frame_range(start_frame, end_frame); + + if (frame < start_frame) + return PTC_READ_SAMPLE_EARLY; + else if (frame > end_frame) + return PTC_READ_SAMPLE_LATE; + else { + /* TODO could also be EXACT, but INTERPOLATED is more general + * do we need to support this? + * checking individual time samplings is also possible, but more involved. + */ + return PTC_READ_SAMPLE_INTERPOLATED; + } + } + else { + return PTC_READ_SAMPLE_INVALID; + } +} + +PTCReadSampleResult AbcReader::read_sample(float frame) +{ + + try { + return read_sample_abc(m_abc_archive->frame_to_time(frame)); + } + catch (Alembic::Util::Exception e) { + handle_alembic_exception(ErrorHandler::get_default_handler(), PTC_ERROR_CRITICAL, e); + return PTC_READ_SAMPLE_INVALID; + } + return PTC_READ_SAMPLE_INVALID; +} + +} /* namespace PTC */ diff --git a/source/blender/pointcache/alembic/abc_reader.h b/source/blender/pointcache/alembic/abc_reader.h new file mode 100644 index 00000000000..86cfdd91d51 --- /dev/null +++ b/source/blender/pointcache/alembic/abc_reader.h @@ -0,0 +1,108 @@ +/* + * Copyright 2013, Blender Foundation. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef PTC_ABC_READER_H +#define PTC_ABC_READER_H + +#include <string> + +#include <Alembic/Abc/IArchive.h> +#include <Alembic/Abc/IObject.h> +#include <Alembic/Abc/ISampleSelector.h> + +#include "reader.h" + +#include "abc_frame_mapper.h" + +#include "util_error_handler.h" +#include "util_types.h" + +namespace PTC { + +using namespace Alembic; + +using Abc::chrono_t; + +class AbcReaderArchive : public ReaderArchive, public FrameMapper { +public: + virtual ~AbcReaderArchive(); + + static AbcReaderArchive *open(double fps, float start_frame, const std::string &filename, ErrorHandler *error_handler); + + PTCArchiveResolution get_resolutions(); + bool use_render() const { return m_use_render; } + void use_render(bool enable) { m_use_render = enable; } + + Abc::IArchive abc_archive() const { return m_abc_archive; } + Abc::IObject root(); + + Abc::IObject get_id_object(ID *id); + bool has_id_object(ID *id); + + bool get_frame_range(int &start_frame, int &end_frame); + Abc::ISampleSelector get_frame_sample_selector(float frame); + Abc::ISampleSelector get_frame_sample_selector(chrono_t time); + + void get_info_stream(void (*stream)(void *, const char *), void *userdata); + void get_info(CacheArchiveInfo *info, IDProperty *metadata); + void get_info_nodes(CacheArchiveInfo *info, bool calc_bytes_size); + +protected: + AbcReaderArchive(double fps, float start_frame, ErrorHandler *error_handler, Abc::IArchive abc_archive); + +protected: + ErrorHandler *m_error_handler; + bool m_use_render; + + Abc::IArchive m_abc_archive; + Abc::IObject m_abc_root; + Abc::IObject m_abc_root_render; +}; + +class AbcReader : public Reader { +public: + AbcReader() : + m_abc_archive(0) + {} + + void init(ReaderArchive *archive) + { + BLI_assert(dynamic_cast<AbcReaderArchive*>(archive)); + m_abc_archive = static_cast<AbcReaderArchive*>(archive); + } + + virtual void init_abc(Abc::IObject /*object*/) {} + + AbcReaderArchive *abc_archive() const { return m_abc_archive; } + + bool get_frame_range(int &start_frame, int &end_frame); + + Abc::ISampleSelector get_frame_sample_selector(float frame) { return m_abc_archive->get_frame_sample_selector(frame); } + Abc::ISampleSelector get_frame_sample_selector(chrono_t time) { return m_abc_archive->get_frame_sample_selector(time); } + + PTCReadSampleResult test_sample(float frame); + PTCReadSampleResult read_sample(float frame); + virtual PTCReadSampleResult read_sample_abc(chrono_t time) = 0; + +private: + AbcReaderArchive *m_abc_archive; +}; + +} /* namespace PTC */ + +#endif /* PTC_READER_H */ diff --git a/source/blender/pointcache/alembic/abc_schema.h b/source/blender/pointcache/alembic/abc_schema.h new file mode 100644 index 00000000000..d8d70ddac05 --- /dev/null +++ b/source/blender/pointcache/alembic/abc_schema.h @@ -0,0 +1,107 @@ +/* + * Copyright 2013, Blender Foundation. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef PTC_ABC_SCHEMA_H +#define PTC_ABC_SCHEMA_H + +#include <Alembic/AbcGeom/SchemaInfoDeclarations.h> +#include <Alembic/AbcGeom/IGeomBase.h> +#include <Alembic/AbcGeom/OGeomBase.h> + +namespace PTC { + +#if 0 +#define PTC_SCHEMA_INFO ALEMBIC_ABCGEOM_DECLARE_SCHEMA_INFO + +using namespace Alembic::AbcGeom; +#endif + + +#if 0 +/* XXX We define an extended schema class to implement the wrapper constructor. + * This was removed in Alembic 1.1 for some reason ... + */ +template <class SCHEMA> +class OSchemaObject : public Abc::OSchemaObject<SCHEMA> +{ +public: + //! The default constructor creates an empty OSchemaObject function set. + //! ... + OSchemaObject() : Abc::OSchemaObject<SCHEMA>() {} + + //! The primary constructor creates an OSchemaObject as a child of the + //! first argument, which is any Abc or AbcCoreAbstract (or other) + //! object which can be intrusively cast to an ObjectWriterPtr. + template <class OBJECT_PTR> + OSchemaObject(OBJECT_PTR iParentObject, + const std::string &iName, + + const Argument &iArg0 = Argument(), + const Argument &iArg1 = Argument(), + const Argument &iArg2 = Argument()) + : Abc::OSchemaObject<SCHEMA>(iParentObject, iName, iArg0, iArg1, iArg2) + {} + + //! Wrap an existing schema object. + //! ... + template <class OBJECT_PTR> + OSchemaObject(OBJECT_PTR iThisObject, + WrapExistingFlag iFlag, + const Argument &iArg0 = Argument(), + const Argument &iArg1 = Argument(), + const Argument &iArg2 = Argument() ); +}; + +//-***************************************************************************** +template<class SCHEMA> +template<class OBJECT_PTR> +inline OSchemaObject<SCHEMA>::OSchemaObject( + OBJECT_PTR iObject, + WrapExistingFlag iFlag, + const Argument &iArg0, + const Argument &iArg1, + const Argument &iArg2 ) + : OObject(iObject, + iFlag, + GetErrorHandlerPolicy(iObject, + iArg0, iArg1, iArg2)) +{ + ALEMBIC_ABC_SAFE_CALL_BEGIN("OSchemaObject::OSchemaObject( wrap )"); + + const AbcA::ObjectHeader &oheader = this->getHeader(); + + Abc::OSchemaObject<SCHEMA>::m_schema = SCHEMA( + this->getProperties().getProperty(SCHEMA::getDefaultSchemaName()).getPtr()->asCompoundPtr(), + iFlag, + this->getErrorHandlerPolicy(), + GetSchemaInterpMatching(iArg0, iArg1, iArg2)); + + /* XXX TODO gives compiler error atm */ +// ABCA_ASSERT(matches(oheader, GetSchemaInterpMatching(iArg0, iArg1, iArg2)), +// "Incorrect match of schema: " +// << oheader.getMetaData().get( "schemaObjTitle" ) +// << " to expected: " +// << getSchemaObjTitle()); + + ALEMBIC_ABC_SAFE_CALL_END_RESET(); +} +#endif + +} /* namespace PTC */ + +#endif /* PTC_SCHEMA_H */ diff --git a/source/blender/pointcache/alembic/abc_simdebug.cpp b/source/blender/pointcache/alembic/abc_simdebug.cpp new file mode 100644 index 00000000000..191b2856dc9 --- /dev/null +++ b/source/blender/pointcache/alembic/abc_simdebug.cpp @@ -0,0 +1,185 @@ +/* + * Copyright 2014, Blender Foundation. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "abc_simdebug.h" + +extern "C" { +#include "BLI_ghash.h" +#include "BLI_math.h" +} + +#include "PTC_api.h" + +namespace PTC { + +using namespace Abc; + +struct SimDebugSample { + std::vector<uint32_t> category_hash; + std::vector<uint32_t> hash; + + std::vector<int32_t> type; + std::vector<C3f> color; + std::vector<V3f> v1; + std::vector<V3f> v2; +}; + +AbcSimDebugWriter::AbcSimDebugWriter(const std::string &name, SimDebugData *data) : + m_name(name), + m_data(data) +{ +} + +AbcSimDebugWriter::~AbcSimDebugWriter() +{ +} + +void AbcSimDebugWriter::init_abc(OObject parent) +{ + if (m_object) + return; + + m_object = OObject(parent, m_name, abc_archive()->frame_sampling_index()); + OCompoundProperty props = m_object.getProperties(); + + m_prop_category_hash = OUInt32ArrayProperty(props, "category_hash", abc_archive()->frame_sampling_index()); + m_prop_hash = OUInt32ArrayProperty(props, "hash", abc_archive()->frame_sampling_index()); + m_prop_type = OInt32ArrayProperty(props, "type", abc_archive()->frame_sampling_index()); + m_prop_color = OC3fArrayProperty(props, "color", abc_archive()->frame_sampling_index()); + m_prop_v1 = OV3fArrayProperty(props, "v1", abc_archive()->frame_sampling_index()); + m_prop_v2 = OV3fArrayProperty(props, "v2", abc_archive()->frame_sampling_index()); +} + +static void create_sample(SimDebugData *data, SimDebugSample &sample) +{ + int numelem = BLI_ghash_size(data->gh); + GHashIterator iter; + + sample.category_hash.reserve(numelem); + sample.hash.reserve(numelem); + sample.type.reserve(numelem); + sample.color.reserve(numelem); + sample.v1.reserve(numelem); + sample.v2.reserve(numelem); + + for (BLI_ghashIterator_init(&iter, data->gh); !BLI_ghashIterator_done(&iter); BLI_ghashIterator_step(&iter)) { + SimDebugElement *elem = (SimDebugElement *)BLI_ghashIterator_getValue(&iter); + + sample.category_hash.push_back(elem->category_hash); + sample.hash.push_back(elem->hash); + sample.type.push_back(elem->type); + sample.color.push_back(C3f(elem->color[0], elem->color[1], elem->color[2])); + sample.v1.push_back(V3f(elem->v1[0], elem->v1[1], elem->v1[2])); + sample.v2.push_back(V3f(elem->v2[0], elem->v2[1], elem->v2[2])); + } +} + +void AbcSimDebugWriter::write_sample() +{ + if (!m_object) + return; + + SimDebugSample sample; + + create_sample(m_data, sample); + + m_prop_category_hash.set(UInt32ArraySample(sample.category_hash)); + m_prop_hash.set(UInt32ArraySample(sample.hash)); + m_prop_type.set(Int32ArraySample(sample.type)); + m_prop_color.set(C3fArraySample(sample.color)); + m_prop_v1.set(V3fArraySample(sample.v1)); + m_prop_v2.set(V3fArraySample(sample.v2)); +} + +/* ========================================================================= */ + +AbcSimDebugReader::AbcSimDebugReader(SimDebugData *data) : + m_data(data) +{ +} + +AbcSimDebugReader::~AbcSimDebugReader() +{ +} + +void AbcSimDebugReader::init_abc(IObject object) +{ + if (m_object) + return; + m_object = IObject(object, kWrapExisting); + ICompoundProperty props = m_object.getProperties(); + + m_prop_category_hash = IUInt32ArrayProperty(props, "category_hash"); + m_prop_hash = IUInt32ArrayProperty(props, "hash"); + m_prop_type = IInt32ArrayProperty(props, "type"); + m_prop_color = IC3fArrayProperty(props, "color"); + m_prop_v1 = IV3fArrayProperty(props, "v1"); + m_prop_v2 = IV3fArrayProperty(props, "v2"); +} + +static PTCReadSampleResult apply_sample(SimDebugData *data, + UInt32ArraySamplePtr sample_category_hash, UInt32ArraySamplePtr sample_hash, + Int32ArraySamplePtr sample_type, C3fArraySamplePtr sample_color, + V3fArraySamplePtr sample_v1, V3fArraySamplePtr sample_v2) +{ + int numelem = sample_hash->size(); + + if (sample_category_hash->size() != numelem || + sample_type->size() != numelem || + sample_color->size() != numelem || + sample_v1->size() != numelem || + sample_v2->size() != numelem) + { + return PTC_READ_SAMPLE_INVALID; + } + + const uint32_t *data_category_hash = sample_category_hash->get(); + const uint32_t *data_hash = sample_hash->get(); + const int32_t *data_type = sample_type->get(); + const C3f *data_color = sample_color->get(); + const V3f *data_v1 = sample_v1->get(); + const V3f *data_v2 = sample_v2->get(); + + for (int i = 0; i < numelem; ++i) { + BKE_sim_debug_data_add_element_ex(data, *data_type, data_v1->getValue(), data_v2->getValue(), data_color->x, data_color->y, data_color->z, *data_category_hash, *data_hash); + + ++data_category_hash; + ++data_hash; + ++data_type; + ++data_color; + ++data_v1; + ++data_v2; + } + + return PTC_READ_SAMPLE_EXACT; +} + +PTCReadSampleResult AbcSimDebugReader::read_sample_abc(chrono_t time) +{ + if (!m_object) + return PTC_READ_SAMPLE_INVALID; + + ISampleSelector ss = get_frame_sample_selector(time); + + apply_sample(m_data, m_prop_category_hash.getValue(ss), m_prop_hash.getValue(ss), m_prop_type.getValue(ss), + m_prop_color.getValue(ss), m_prop_v1.getValue(ss), m_prop_v2.getValue(ss)); + + return PTC_READ_SAMPLE_EXACT; +} + +} /* namespace PTC */ diff --git a/source/blender/pointcache/alembic/abc_simdebug.h b/source/blender/pointcache/alembic/abc_simdebug.h new file mode 100644 index 00000000000..b549a31cf34 --- /dev/null +++ b/source/blender/pointcache/alembic/abc_simdebug.h @@ -0,0 +1,82 @@ +/* + * Copyright 2014, Blender Foundation. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef PTC_ABC_SIMDEBUG_H +#define PTC_ABC_SIMDEBUG_H + +#include <Alembic/Abc/IObject.h> +#include <Alembic/Abc/OObject.h> +#include <Alembic/AbcGeom/Foundation.h> + +#include "ptc_types.h" + +#include "abc_reader.h" +#include "abc_writer.h" + +extern "C" { +#include "BKE_effect.h" +} + +namespace PTC { + +class AbcSimDebugWriter : public AbcWriter { +public: + AbcSimDebugWriter(const std::string &name, SimDebugData *data); + ~AbcSimDebugWriter(); + + void init_abc(Abc::OObject parent); + + void write_sample(); + +private: + std::string m_name; + SimDebugData *m_data; + + Abc::OObject m_object; + AbcGeom::OUInt32ArrayProperty m_prop_category_hash; + AbcGeom::OUInt32ArrayProperty m_prop_hash; + AbcGeom::OInt32ArrayProperty m_prop_type; + AbcGeom::OC3fArrayProperty m_prop_color; + AbcGeom::OV3fArrayProperty m_prop_v1; + AbcGeom::OV3fArrayProperty m_prop_v2; +}; + +class AbcSimDebugReader : public AbcReader { +public: + AbcSimDebugReader(SimDebugData *data); + ~AbcSimDebugReader(); + + void init_abc(Abc::IObject object); + + PTCReadSampleResult read_sample_abc(chrono_t time); + +private: + SimDebugData *m_data; + + Abc::IObject m_object; + AbcGeom::IUInt32ArrayProperty m_prop_category_hash; + AbcGeom::IUInt32ArrayProperty m_prop_hash; + AbcGeom::IInt32ArrayProperty m_prop_type; + AbcGeom::IC3fArrayProperty m_prop_color; + AbcGeom::IV3fArrayProperty m_prop_v1; + AbcGeom::IV3fArrayProperty m_prop_v2; +}; + +} /* namespace PTC */ + +#endif /* PTC_SIMDEBUG_H */ diff --git a/source/blender/pointcache/alembic/abc_split.cpp b/source/blender/pointcache/alembic/abc_split.cpp new file mode 100644 index 00000000000..bbca2862d30 --- /dev/null +++ b/source/blender/pointcache/alembic/abc_split.cpp @@ -0,0 +1,239 @@ +//-***************************************************************************** +// +// Copyright (c) 2009-2013, +// Sony Pictures Imageworks, Inc. and +// Industrial Light & Magic, a division of Lucasfilm Entertainment Company Ltd. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Sony Pictures Imageworks, nor +// Industrial Light & Magic nor the names of their contributors may be used +// to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +//-***************************************************************************** + +/* + * Copyright 2015, Blender Foundation. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include <map> + +#include <Alembic/AbcGeom/All.h> +#include <Alembic/AbcCoreAbstract/All.h> +#include <Alembic/AbcCoreFactory/All.h> +#include <Alembic/Util/All.h> +#include <Alembic/Abc/TypedPropertyTraits.h> + +#include "alembic.h" + +extern "C" { +#include "BLI_string.h" +#include "BLI_utildefines.h" + +#include "BKE_cache_library.h" +} + +using namespace ::Alembic::AbcGeom; + +namespace PTC { + +static void slice_properties(ICompoundProperty iParent, OCompoundProperty out_parent, TimeSamplingPtr time_sampling, chrono_t start, chrono_t end); + +static void slice_array_property(IArrayProperty iProp, OCompoundProperty out_parent, TimeSamplingPtr time_sampling, chrono_t start, chrono_t end) +{ + OArrayProperty out(out_parent, iProp.getName(), iProp.getDataType(), iProp.getMetaData(), time_sampling); + + ArrayPropertyReaderPtr reader = iProp.getPtr(); + ArrayPropertyWriterPtr writer = out.getPtr(); + + size_t num_samples = iProp.getNumSamples(); + if (num_samples == 0) + return; + + index_t istart = reader->getFloorIndex(start).first; + index_t iend = reader->getFloorIndex(end).first; +// index_t ostart = time_sampling->getFloorIndex(start).first; +// index_t oend = time_sampling->getFloorIndex(end).first; + + char *buf = NULL; + +#if 0 + if (istart > ostart) { + /* fill the gap between start indices with the first sample, + * so that output sample times match input sample times as close as possible. + */ + for (index_t index = istart) + } +#endif + + for (index_t index = istart; index <= iend; ++index) { + + ArraySamplePtr sample_ptr; + reader->getSample(index, sample_ptr); + + writer->setSample(*sample_ptr); + } + + if (buf) + delete[] buf; +} + +static void slice_scalar_property(IScalarProperty iProp, OCompoundProperty out_parent, TimeSamplingPtr time_sampling, chrono_t start, chrono_t end) +{ + OScalarProperty out(out_parent, iProp.getName(), iProp.getDataType(), iProp.getMetaData(), time_sampling); + + ScalarPropertyReaderPtr reader = iProp.getPtr(); + ScalarPropertyWriterPtr writer = out.getPtr(); + size_t num_bytes = reader->getDataType().getNumBytes(); + + size_t num_samples = iProp.getNumSamples(); + if (num_samples == 0) + return; + + index_t istart = reader->getFloorIndex(start).first; + index_t iend = reader->getFloorIndex(end).first; +// index_t ostart = time_sampling->getFloorIndex(start).first; +// index_t oend = time_sampling->getFloorIndex(end).first; + + char *buf = new char[num_bytes]; + +#if 0 + if (istart > ostart) { + /* fill the gap between start indices with the first sample, + * so that output sample times match input sample times as close as possible. + */ + for (index_t index = istart) + } +#endif + + for (index_t index = istart; index <= iend; ++index) { + + reader->getSample(index, (void*)buf); + + writer->setSample((void*)buf); + } + + delete[] buf; +} + +static void slice_compound_property(ICompoundProperty iProp, OCompoundProperty out_parent, TimeSamplingPtr time_sampling, chrono_t start, chrono_t end) +{ + OCompoundProperty out(out_parent, iProp.getName(), iProp.getMetaData()); + + slice_properties(iProp, out, time_sampling, start, end); +} + +static void slice_properties(ICompoundProperty iParent, OCompoundProperty out_parent, TimeSamplingPtr time_sampling, chrono_t start, chrono_t end) +{ + for (size_t i = 0 ; i < iParent.getNumProperties() ; i++) { + PropertyHeader header = iParent.getPropertyHeader(i); + + if (header.isCompound()) { + slice_compound_property(ICompoundProperty(iParent, header.getName()), out_parent, time_sampling, start, end); + } + else if (header.isScalar()) { + slice_scalar_property(IScalarProperty(iParent, header.getName()), out_parent, time_sampling, start, end); + } + else { + BLI_assert(header.isArray()); + slice_array_property(IArrayProperty(iParent, header.getName()), out_parent, time_sampling, start, end); + } + } +} + +typedef std::map<ObjectReaderPtr, ObjectWriterPtr> ObjectMap; +typedef std::pair<ObjectReaderPtr, ObjectWriterPtr> ObjectPair; + +static void slice_object(IObject iObj, OObject out, TimeSamplingPtr time_sampling, chrono_t start, chrono_t end, ObjectMap &object_map) +{ + // Get the properties. + slice_properties(iObj.getProperties(), out.getProperties(), time_sampling, start, end); + + // now the child objects + for (size_t i = 0 ; i < iObj.getNumChildren() ; i++) { + const ObjectHeader &child_header = iObj.getChildHeader(i); + IObject child = IObject(iObj, child_header.getName()); + + /* Note: child instances are added later, once all actual objects have been copied */ + if (!child.isInstanceRoot()) { + /* XXX reuse if the output object already exists. + * This should not happen, but currently some root objects are created + * in advance when opening writer archives. In the future these will not be needed + * and this check will become unnecessary. + */ + OObject out_child = out.getChild(child_header.getName()); + if (!out_child) + out_child = OObject(out, child_header.getName(), child_header.getMetaData()); + object_map[child.getPtr()] = out_child.getPtr(); + + slice_object(child, out_child, time_sampling, start, end, object_map); + } + } +} + +static void slice_object_instances(IObject iObj, OObject out, const ObjectMap &object_map) +{ + // now the child objects + for (size_t i = 0 ; i < iObj.getNumChildren() ; i++) { + const ObjectHeader &child_header = iObj.getChildHeader(i); + IObject child = IObject(iObj, child_header.getName()); + + if (child.isInstanceRoot()) { + ObjectMap::const_iterator it = object_map.find(child.getPtr()); + BLI_assert(it != object_map.end()); + OObject out_target(it->second, kWrapExisting); + + out.addChildInstance(out_target, child_header.getName()); + } + else { + OObject out_child(out.getChild(child_header.getName()).getPtr(), kWrapExisting); + slice_object_instances(child, out_child, object_map); + } + } +} + +void abc_archive_slice(IArchive in, OArchive out, TimeSamplingPtr time_sampling, chrono_t start, chrono_t end) +{ + ObjectMap object_map; + + slice_object(in.getTop(), out.getTop(), time_sampling, start, end, object_map); + slice_object_instances(in.getTop(), out.getTop(), object_map); +} + +} /* namespace PTC */ diff --git a/source/blender/pointcache/alembic/abc_writer.cpp b/source/blender/pointcache/alembic/abc_writer.cpp new file mode 100644 index 00000000000..a294858f2e5 --- /dev/null +++ b/source/blender/pointcache/alembic/abc_writer.cpp @@ -0,0 +1,132 @@ +/* + * Copyright 2013, Blender Foundation. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +//#include <Alembic/AbcCoreHDF5/ReadWrite.h> +#include <Alembic/AbcCoreOgawa/ReadWrite.h> +#include <Alembic/Abc/OObject.h> +#include <Alembic/Abc/ArchiveInfo.h> + +#include "alembic.h" +#include "abc_writer.h" + +#include "util_error_handler.h" + +extern "C" { +#include <ctime> + +#include "BLI_fileops.h" +#include "BLI_path_util.h" +} + +namespace PTC { + +using namespace Abc; + +/* make sure the file's directory exists */ +static void ensure_directory(const char *filename) +{ + char dir[FILE_MAXDIR]; + BLI_split_dir_part(filename, dir, sizeof(dir)); + BLI_dir_create_recursive(dir); +} + +AbcWriterArchive *AbcWriterArchive::open(double fps, float start_frame, const std::string &filename, PTCArchiveResolution resolutions, + const char *app_name, const char *description, const struct tm *time, IDProperty *metadata, ErrorHandler *error_handler) +{ + ensure_directory(filename.c_str()); + + MetaData md = abc_create_archive_info(app_name, description, time, metadata); + + OArchive abc_archive; + PTC_SAFE_CALL_BEGIN +// abc_archive = OArchive(AbcCoreHDF5::WriteArchive(), filename, md, Abc::ErrorHandler::kThrowPolicy); + abc_archive = OArchive(AbcCoreOgawa::WriteArchive(), filename, md, Abc::ErrorHandler::kThrowPolicy); + PTC_SAFE_CALL_END_HANDLER(error_handler) + + if (abc_archive) + return new AbcWriterArchive(fps, start_frame, resolutions, error_handler, abc_archive); + else + return NULL; +} + +AbcWriterArchive::AbcWriterArchive(double fps, float start_frame, PTCArchiveResolution resolutions, ErrorHandler *error_handler, OArchive abc_archive) : + FrameMapper(fps, start_frame), + m_error_handler(error_handler), + m_use_render(false), + m_abc_archive(abc_archive) +{ + if (m_abc_archive) { + chrono_t cycle_time = this->seconds_per_frame(); + chrono_t start_time = this->start_time(); + m_frame_sampling = m_abc_archive.addTimeSampling(TimeSampling(cycle_time, start_time)); + + if (resolutions & PTC_RESOLUTION_PREVIEW) + m_abc_root = OObject(m_abc_archive.getTop(), "root"); + if (resolutions & PTC_RESOLUTION_RENDER) + m_abc_root_render = OObject(m_abc_archive.getTop(), "root_render"); + } +} + +AbcWriterArchive::~AbcWriterArchive() +{ +} + +OObject AbcWriterArchive::get_id_object(ID *id) +{ + if (!m_abc_archive) + return OObject(); + + ObjectWriterPtr root_ptr = root().getPtr(); + + ObjectWriterPtr child = root_ptr->getChild(id->name); + if (child) + return OObject(child, kWrapExisting); + else { + const ObjectHeader *child_header = root_ptr->getChildHeader(id->name); + if (child_header) + return OObject(root_ptr->createChild(*child_header), kWrapExisting); + else { + return OObject(); + } + } +} + +OObject AbcWriterArchive::root() +{ + if (m_use_render) + return m_abc_root_render ? m_abc_root_render : OObject(); + else + return m_abc_root ? m_abc_root : OObject(); +} + +bool AbcWriterArchive::has_id_object(ID *id) +{ + if (!m_abc_archive) + return false; + + ObjectWriterPtr root_ptr = root().getPtr(); + + return root_ptr->getChildHeader(id->name) != NULL; +} + +TimeSamplingPtr AbcWriterArchive::frame_sampling() +{ + return m_abc_archive.getTimeSampling(m_frame_sampling); +} + +} /* namespace PTC */ diff --git a/source/blender/pointcache/alembic/abc_writer.h b/source/blender/pointcache/alembic/abc_writer.h new file mode 100644 index 00000000000..60a25ff1e0a --- /dev/null +++ b/source/blender/pointcache/alembic/abc_writer.h @@ -0,0 +1,130 @@ +/* + * Copyright 2013, Blender Foundation. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef PTC_ABC_WRITER_H +#define PTC_ABC_WRITER_H + +#include <string> + +#include <Alembic/Abc/OArchive.h> +#include <Alembic/Abc/OObject.h> + +#include "writer.h" + +#include "abc_frame_mapper.h" + +#include "util_error_handler.h" +#include "util_types.h" + +extern "C" { +#include "BLI_utildefines.h" + +#include "DNA_ID.h" +} + +struct tm; + +namespace PTC { + +using namespace Alembic; + +class AbcWriterArchive : public WriterArchive, public FrameMapper { +public: + virtual ~AbcWriterArchive(); + + static AbcWriterArchive *open(double fps, float start_frame, const std::string &filename, PTCArchiveResolution resolutions, + const char *app_name, const char *description, const struct tm *time, IDProperty *metadata, ErrorHandler *error_handler); + + bool use_render() const { return m_use_render; } + void use_render(bool enable) { m_use_render = enable; } + + Abc::OArchive abc_archive() const { return m_abc_archive; } + Abc::OObject root(); + + Abc::OObject get_id_object(ID *id); + bool has_id_object(ID *id); + + template <class OObjectT> + OObjectT add_id_object(ID *id); + + uint32_t frame_sampling_index() const { return m_frame_sampling; } + Abc::TimeSamplingPtr frame_sampling(); + +protected: + AbcWriterArchive(double fps, float start_frame, PTCArchiveResolution resolutions, ErrorHandler *error_handler, Abc::OArchive abc_archive); + +protected: + ErrorHandler *m_error_handler; + uint32_t m_frame_sampling; + bool m_use_render; + + Abc::OArchive m_abc_archive; + Abc::OObject m_abc_root; + Abc::OObject m_abc_root_render; +}; + +class AbcWriter : public Writer { +public: + Abc::TimeSamplingPtr frame_sampling() { return m_abc_archive->frame_sampling(); } + + void init(WriterArchive *archive) + { + BLI_assert(dynamic_cast<AbcWriterArchive*>(archive)); + m_abc_archive = static_cast<AbcWriterArchive*>(archive); + + init_abc(); + } + + /* one of these should be implemented by subclasses */ + virtual void init_abc() {} + virtual void init_abc(Abc::OObject /*parent*/) {} + + AbcWriterArchive *abc_archive() const { return m_abc_archive; } + +private: + AbcWriterArchive *m_abc_archive; +}; + +/* ------------------------------------------------------------------------- */ + +template <class OObjectT> +OObjectT AbcWriterArchive::add_id_object(ID *id) +{ + using namespace Abc; + + if (!m_abc_archive) + return OObjectT(); + + ObjectWriterPtr root_ptr = this->root().getPtr(); + + ObjectWriterPtr child = root_ptr->getChild(id->name); + if (child) + return OObjectT(child, kWrapExisting); + else { + const ObjectHeader *child_header = root_ptr->getChildHeader(id->name); + if (child_header) + return OObjectT(root_ptr->createChild(*child_header), kWrapExisting); + else { + return OObjectT(root_ptr, id->name, frame_sampling_index()); + } + } +} + +} /* namespace PTC */ + +#endif /* PTC_WRITER_H */ diff --git a/source/blender/pointcache/alembic/alembic.cpp b/source/blender/pointcache/alembic/alembic.cpp new file mode 100644 index 00000000000..669b3a33662 --- /dev/null +++ b/source/blender/pointcache/alembic/alembic.cpp @@ -0,0 +1,147 @@ +/* + * Copyright 2015, Blender Foundation. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "PTC_api.h" + +#include "ptc_types.h" + +#include "abc_reader.h" +#include "abc_writer.h" +#include "abc_cloth.h" +#include "abc_group.h" +#include "abc_mesh.h" +#include "abc_object.h" +#include "abc_particles.h" + +#include "alembic.h" + +namespace PTC { + +class AbcFactory : public Factory { + const std::string &get_default_extension() + { + static std::string ext = "abc"; + return ext; + } + + WriterArchive *open_writer_archive(double fps, float start_frame, const std::string &name, PTCArchiveResolution resolutions, + const char *app_name, const char *description, const struct tm *time, struct IDProperty *metadata, ErrorHandler *error_handler) + { + return AbcWriterArchive::open(fps, start_frame, name, resolutions, app_name, description, time, metadata, error_handler); + } + + ReaderArchive *open_reader_archive(double fps, float start_frame, const std::string &name, ErrorHandler *error_handler) + { + return AbcReaderArchive::open(fps, start_frame, name, error_handler); + } + + void slice(ReaderArchive *in, WriterArchive *out, float start_frame, float end_frame) + { + BLI_assert(dynamic_cast<AbcReaderArchive*>(in)); + BLI_assert(dynamic_cast<AbcWriterArchive*>(out)); + AbcReaderArchive *abc_in = static_cast<AbcReaderArchive*>(in); + AbcWriterArchive *abc_out = static_cast<AbcWriterArchive*>(out); + + abc_archive_slice(abc_in->abc_archive(), abc_out->abc_archive(), abc_out->frame_sampling(), abc_in->frame_to_time(start_frame), abc_in->frame_to_time(end_frame)); + } + + Writer *create_writer_object(const std::string &name, Scene *scene, Object *ob) + { + return new AbcObjectWriter(name, scene, ob, true, true); + } + + Reader *create_reader_object(const std::string &name, Object *ob) + { + return new AbcObjectReader(name, ob); + } + + Writer *create_writer_group(const std::string &name, Group *group) + { + return new AbcGroupWriter(name, group); + } + + Reader *create_reader_group(const std::string &name, Group *group) + { + return new AbcGroupReader(name, group); + } + + + /* Cloth */ + Writer *create_writer_cloth(const std::string &name, Object *ob, ClothModifierData *clmd) + { + return new AbcClothWriter(name, ob, clmd); + } + + Reader *create_reader_cloth(const std::string &name, Object *ob, ClothModifierData *clmd) + { + return new AbcClothReader(name, ob, clmd); + } + + /* Modifier Stack */ + Writer *create_writer_derived_mesh(const std::string &name, Object *ob, DerivedMesh **dm_ptr) + { + return new AbcDerivedMeshWriter(name, ob, dm_ptr); + } + + Reader *create_reader_derived_mesh(const std::string &name, Object *ob) + { + return new AbcDerivedMeshReader(name, ob); + } + + Writer *create_writer_derived_final_realtime(const std::string &name, Object *ob) + { + return new AbcDerivedFinalRealtimeWriter(name, ob); + } + + Writer *create_writer_derived_final_render(const std::string &name, Scene *scene, Object *ob, DerivedMesh **render_dm_ptr) + { + return new AbcDerivedFinalRenderWriter(name, scene, ob, render_dm_ptr); + } + + + Writer *create_writer_dupligroup(const std::string &name, EvaluationContext *eval_ctx, Scene *scene, Group *group, CacheLibrary *cachelib) + { + return new AbcDupligroupWriter(name, eval_ctx, scene, group, cachelib); + } + + Writer *create_writer_duplicache(const std::string &name, Group *group, DupliCache *dupcache, int datatypes, bool do_sim_debug) + { + return new AbcDupliCacheWriter(name, group, dupcache, datatypes, do_sim_debug); + } + + Reader *create_reader_duplicache(const std::string &name, Group *group, DupliCache *dupcache, + bool read_strands_motion, bool read_strands_children, bool read_sim_debug) + { + return new AbcDupliCacheReader(name, group, dupcache, read_strands_motion, read_strands_children, read_sim_debug); + } + + Reader *create_reader_duplicache_object(const std::string &name, Object *ob, DupliObjectData *data, + bool read_strands_motion, bool read_strands_children) + { + return new AbcDupliObjectReader(name, ob, data, read_strands_motion, read_strands_children); + } +}; + +} + +void PTC_alembic_init() +{ + static PTC::AbcFactory abc_factory; + + PTC::Factory::alembic = &abc_factory; +} diff --git a/source/blender/pointcache/alembic/alembic.h b/source/blender/pointcache/alembic/alembic.h new file mode 100644 index 00000000000..c73b095933f --- /dev/null +++ b/source/blender/pointcache/alembic/alembic.h @@ -0,0 +1,44 @@ +/* + * Copyright 2015, Blender Foundation. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef PTC_ALEMBIC_H +#define PTC_ALEMBIC_H + +#include <string> + +#include <Alembic/Abc/IArchive.h> + +struct CacheArchiveInfo; +struct IDProperty; + +namespace PTC { + +using namespace Alembic; + +void abc_metadata_from_idprops_group(Abc::MetaData &md, IDProperty *prop); +void abc_metadata_to_idprops_group(const Abc::MetaData &md, IDProperty *prop); +Abc::MetaData abc_create_archive_info(const char *app_name, const char *description, const struct tm *t, struct IDProperty *props); + +void abc_archive_info_stream(Alembic::Abc::IArchive &archive, void (*stream)(void *, const char *), void *userdata); +void abc_archive_info_nodes(Alembic::Abc::IArchive &archive, CacheArchiveInfo *info, IDProperty *metadata, bool calc_nodes, bool calc_bytes_size); + +void abc_archive_slice(Alembic::Abc::IArchive in, Alembic::Abc::OArchive out, Alembic::Abc::TimeSamplingPtr time_sampling, Alembic::Abc::chrono_t start, Alembic::Abc::chrono_t end); + +} /* namespace PTC */ + +#endif /* PTC_CLOTH_H */ diff --git a/source/blender/pointcache/intern/ptc_types.cpp b/source/blender/pointcache/intern/ptc_types.cpp new file mode 100644 index 00000000000..f2df356ca5b --- /dev/null +++ b/source/blender/pointcache/intern/ptc_types.cpp @@ -0,0 +1,44 @@ +/* + * Copyright 2013, Blender Foundation. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "ptc_types.h" + +extern "C" { +#include "BKE_DerivedMesh.h" +} + +namespace PTC { + +Factory *Factory::alembic = NULL; + +DerivedMesh *DerivedMeshReader::acquire_result() +{ + DerivedMesh *dm = m_result; + m_result = NULL; + return dm; +} + +void DerivedMeshReader::discard_result() +{ + if (m_result) { + m_result->release(m_result); + m_result = NULL; + } +} + +} /* namespace PTC */ diff --git a/source/blender/pointcache/intern/ptc_types.h b/source/blender/pointcache/intern/ptc_types.h new file mode 100644 index 00000000000..c9e63d7abb6 --- /dev/null +++ b/source/blender/pointcache/intern/ptc_types.h @@ -0,0 +1,237 @@ +/* + * Copyright 2013, Blender Foundation. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef PTC_TYPES_H +#define PTC_TYPES_H + +#include "reader.h" +#include "writer.h" + +extern "C" { +#include "DNA_group_types.h" +#include "DNA_modifier_types.h" +#include "DNA_object_types.h" +#include "DNA_object_force.h" +#include "DNA_particle_types.h" +} + +struct CacheLibrary; +struct IDProperty; + +namespace PTC { + +class ClothWriter { +public: + ClothWriter(Object *ob, ClothModifierData *clmd, const std::string &name) : + m_ob(ob), + m_clmd(clmd), + m_name(name) + {} + + ~ClothWriter() + {} + +protected: + Object *m_ob; + ClothModifierData *m_clmd; + std::string m_name; +}; + +class ClothReader { +public: + ClothReader(Object *ob, ClothModifierData *clmd, const std::string &name) : + m_ob(ob), + m_clmd(clmd), + m_name(name) + {} + + ~ClothReader() + {} + +protected: + Object *m_ob; + ClothModifierData *m_clmd; + std::string m_name; +}; + + +class DerivedMeshWriter { +public: + /** \note Targeted DerivedMesh at \a dm_ptr must be available only on \fn write_sample calls */ + DerivedMeshWriter(Object *ob, DerivedMesh **dm_ptr, const std::string &name) : + m_ob(ob), + m_dm_ptr(dm_ptr), + m_name(name) + {} + + ~DerivedMeshWriter() + {} + +protected: + Object *m_ob; + DerivedMesh **m_dm_ptr; + std::string m_name; +}; + +class DerivedMeshReader { +public: + DerivedMeshReader(Object *ob, const std::string &name) : + m_ob(ob), + m_result(0), + m_name(name) + {} + + ~DerivedMeshReader() + { + discard_result(); + } + + DerivedMesh *acquire_result(); + void discard_result(); + +protected: + Object *m_ob; + DerivedMesh *m_result; + std::string m_name; +}; + +class GroupWriter { +public: + GroupWriter(Group *group, const std::string &name) : + m_group(group), + m_name(name) + {} + +protected: + Group *m_group; + std::string m_name; +}; + +class GroupReader { +public: + GroupReader(Group *group, const std::string &name) : + m_group(group), + m_name(name) + {} + +protected: + Group *m_group; + std::string m_name; +}; + +class ObjectWriter { +public: + ObjectWriter(Object *ob, const std::string &name) : + m_ob(ob), + m_name(name) + {} + +protected: + Object *m_ob; + std::string m_name; +}; + +class ObjectReader { +public: + ObjectReader(Object *ob, const std::string &name) : + m_ob(ob), + m_name(name) + {} + +protected: + Object *m_ob; + std::string m_name; +}; + +class ParticlesWriter { +public: + ParticlesWriter(Object *ob, ParticleSystem *psys, const std::string &name) : + m_ob(ob), + m_psys(psys), + m_name(name) + {} + + ~ParticlesWriter() + {} + +protected: + Object *m_ob; + ParticleSystem *m_psys; + std::string m_name; +}; + +class ParticlesReader { +public: + ParticlesReader(Object *ob, ParticleSystem *psys, const std::string &name) : + m_ob(ob), + m_psys(psys), + m_name(name), + m_totpoint(0) + {} + + ~ParticlesReader() + {} + + int totpoint() const { return m_totpoint; } + +protected: + Object *m_ob; + ParticleSystem *m_psys; + std::string m_name; + + int m_totpoint; +}; + +struct Factory { + virtual const std::string &get_default_extension() = 0; + virtual WriterArchive *open_writer_archive(double fps, float start_frame, const std::string &name, PTCArchiveResolution resolutions, + const char *app_name, const char *description, const struct tm *time, struct IDProperty *metadata, ErrorHandler *error_handler) = 0; + virtual ReaderArchive *open_reader_archive(double fps, float start_frame, const std::string &name, ErrorHandler *error_handler) = 0; + + virtual void slice(ReaderArchive *in, WriterArchive *out, float start_frame, float end_frame) = 0; + + virtual Writer *create_writer_object(const std::string &name, Scene *scene, Object *ob) = 0; + virtual Reader *create_reader_object(const std::string &name, Object *ob) = 0; + + virtual Writer *create_writer_group(const std::string &name, Group *group) = 0; + virtual Reader *create_reader_group(const std::string &name, Group *group) = 0; + + /* Cloth */ + virtual Writer *create_writer_cloth(const std::string &name, Object *ob, ClothModifierData *clmd) = 0; + virtual Reader *create_reader_cloth(const std::string &name, Object *ob, ClothModifierData *clmd) = 0; + + /* Modifier Stack */ + virtual Writer *create_writer_derived_mesh(const std::string &name, Object *ob, DerivedMesh **dm_ptr) = 0; + virtual Reader *create_reader_derived_mesh(const std::string &name, Object *ob) = 0; + + virtual Writer *create_writer_derived_final_realtime(const std::string &name, Object *ob) = 0; + virtual Writer *create_writer_derived_final_render(const std::string &name, Scene *scene, Object *ob, DerivedMesh **render_dm_ptr) = 0; + + virtual Writer *create_writer_dupligroup(const std::string &name, EvaluationContext *eval_ctx, Scene *scene, Group *group, CacheLibrary *cachelib) = 0; + virtual Writer *create_writer_duplicache(const std::string &name, Group *group, DupliCache *dupcache, int datatypes, bool do_sim_debug) = 0; + virtual Reader *create_reader_duplicache(const std::string &name, Group *group, DupliCache *dupcache, + bool read_strands_motion, bool read_strands_children, bool read_sim_debug) = 0; + virtual Reader *create_reader_duplicache_object(const std::string &name, Object *ob, DupliObjectData *data, + bool read_strands_motion, bool read_strands_children) = 0; + + static Factory *alembic; +}; + +} /* namespace PTC */ + +#endif /* PTC_EXPORT_H */ diff --git a/source/blender/pointcache/intern/reader.cpp b/source/blender/pointcache/intern/reader.cpp new file mode 100644 index 00000000000..56b0f54e982 --- /dev/null +++ b/source/blender/pointcache/intern/reader.cpp @@ -0,0 +1,57 @@ +/* + * Copyright 2013, Blender Foundation. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "reader.h" +#include "util_error_handler.h" + +extern "C" { +#include "DNA_scene_types.h" +} + +namespace PTC { + +Reader::Reader() : + m_error_handler(0) +{ +} + +Reader::Reader(ErrorHandler *error_handler) : + m_error_handler(error_handler) +{ +} + +Reader::~Reader() +{ + if (m_error_handler) + delete m_error_handler; +} + +void Reader::set_error_handler(ErrorHandler *handler) +{ + if (m_error_handler) + delete m_error_handler; + + m_error_handler = handler; +} + +bool Reader::valid() const +{ + return m_error_handler ? m_error_handler->max_error_level() >= PTC_ERROR_CRITICAL : true; +} + +} /* namespace PTC */ diff --git a/source/blender/pointcache/intern/reader.h b/source/blender/pointcache/intern/reader.h new file mode 100644 index 00000000000..39d0ab20197 --- /dev/null +++ b/source/blender/pointcache/intern/reader.h @@ -0,0 +1,69 @@ +/* + * Copyright 2013, Blender Foundation. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef PTC_READER_H +#define PTC_READER_H + +#include <string> + +#include "util_error_handler.h" +#include "util_types.h" +#include "PTC_api.h" + +struct ID; +struct IDProperty; +struct CacheArchiveInfo; + +namespace PTC { + +class ReaderArchive { +public: + virtual ~ReaderArchive() {} + + virtual PTCArchiveResolution get_resolutions() = 0; + virtual void use_render(bool enable) = 0; + + virtual bool get_frame_range(int &start_frame, int &end_frame) = 0; + virtual void get_info_stream(void (*stream)(void *, const char *), void *userdata) = 0; + virtual void get_info(CacheArchiveInfo *info, IDProperty *metadata) = 0; + virtual void get_info_nodes(CacheArchiveInfo *info, bool calc_bytes_size) = 0; +}; + +class Reader { +public: + Reader(); + Reader(ErrorHandler *error_handler); + virtual ~Reader(); + + virtual void init(ReaderArchive *archive) = 0; + + void set_error_handler(ErrorHandler *handler); + ErrorHandler *get_error_handler() const { return m_error_handler; } + bool valid() const; + + virtual bool get_frame_range(int &start_frame, int &end_frame) = 0; + virtual PTCReadSampleResult test_sample(float frame) = 0; + virtual PTCReadSampleResult read_sample(float frame) = 0; + +protected: + ErrorHandler *m_error_handler; +}; + +} /* namespace PTC */ + +#endif /* PTC_READER_H */ diff --git a/source/blender/pointcache/intern/writer.cpp b/source/blender/pointcache/intern/writer.cpp new file mode 100644 index 00000000000..97a6b9ad405 --- /dev/null +++ b/source/blender/pointcache/intern/writer.cpp @@ -0,0 +1,56 @@ +/* + * Copyright 2013, Blender Foundation. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "writer.h" + +extern "C" { +#include "DNA_scene_types.h" +} + +namespace PTC { + +Writer::Writer() : + m_error_handler(0) +{ +} + +Writer::Writer(ErrorHandler *handler) : + m_error_handler(handler) +{ +} + +Writer::~Writer() +{ + if (m_error_handler) + delete m_error_handler; +} + +void Writer::set_error_handler(ErrorHandler *handler) +{ + if (m_error_handler) + delete m_error_handler; + + m_error_handler = handler; +} + +bool Writer::valid() const +{ + return m_error_handler ? m_error_handler->max_error_level() >= PTC_ERROR_CRITICAL : true; +} + +} /* namespace PTC */ diff --git a/source/blender/pointcache/intern/writer.h b/source/blender/pointcache/intern/writer.h new file mode 100644 index 00000000000..008c391bffc --- /dev/null +++ b/source/blender/pointcache/intern/writer.h @@ -0,0 +1,59 @@ +/* + * Copyright 2013, Blender Foundation. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef PTC_WRITER_H +#define PTC_WRITER_H + +#include <string> + +#include "util_error_handler.h" + +struct ID; + +namespace PTC { + +class WriterArchive { +public: + virtual ~WriterArchive() {} + + virtual void use_render(bool enable) = 0; +}; + +class Writer { +public: + Writer(); + Writer(ErrorHandler *handler); + virtual ~Writer(); + + void set_error_handler(ErrorHandler *handler); + bool valid() const; + + virtual void init(WriterArchive *archive) = 0; + + /* create references to other objects */ + virtual void create_refs() {} + + virtual void write_sample() = 0; + +protected: + ErrorHandler *m_error_handler; +}; + +} /* namespace PTC */ + +#endif /* PTC_WRITER_H */ diff --git a/source/blender/pointcache/util/util_error_handler.cpp b/source/blender/pointcache/util/util_error_handler.cpp new file mode 100644 index 00000000000..e1a326e1713 --- /dev/null +++ b/source/blender/pointcache/util/util_error_handler.cpp @@ -0,0 +1,102 @@ +/* + * Copyright 2013, Blender Foundation. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include <iostream> + +#include "util_error_handler.h" + +extern "C" { +#include "BKE_modifier.h" +} + +namespace PTC { + +ErrorHandler *ErrorHandler::m_default_handler = new StdErrorHandler(PTC_ERROR_INFO); + +ErrorHandler::ErrorHandler() : + m_max_level(PTC_ERROR_NONE) +{ +} + +ErrorHandler::~ErrorHandler() +{ +} + +void ErrorHandler::set_error_level(PTCErrorLevel level) +{ + if (level > m_max_level) + m_max_level = level; +} + +void ErrorHandler::set_default_handler(ErrorHandler *handler) +{ + if (m_default_handler) + delete m_default_handler; + + if (handler) + m_default_handler = handler; + else + m_default_handler = new StdErrorHandler(PTC_ERROR_INFO); +} + +void ErrorHandler::clear_default_handler() +{ + if (m_default_handler) + delete m_default_handler; + + m_default_handler = new StdErrorHandler(PTC_ERROR_INFO); +} + + +StdErrorHandler::StdErrorHandler(PTCErrorLevel level) : + m_verbosity(level) +{ +} + +void StdErrorHandler::handle(PTCErrorLevel level, const char *message) +{ + /* ignore levels below the verbosity setting */ + if (level >= m_verbosity) { + std::cerr << message << std::endl; + } +} + + +CallbackErrorHandler::CallbackErrorHandler(PTCErrorCallback cb, void *userdata) : + m_callback(cb), + m_userdata(userdata) +{ +} + +void CallbackErrorHandler::handle(PTCErrorLevel level, const char *message) +{ + m_callback(m_userdata, level, message); +} + + +ModifierErrorHandler::ModifierErrorHandler(ModifierData *md) : + m_modifier(md) +{ +} + +void ModifierErrorHandler::handle(PTCErrorLevel UNUSED(level), const char *message) +{ + modifier_setError(m_modifier, "%s", message); +} + +} /* namespace PTC */ diff --git a/source/blender/pointcache/util/util_error_handler.h b/source/blender/pointcache/util/util_error_handler.h new file mode 100644 index 00000000000..61baa03a357 --- /dev/null +++ b/source/blender/pointcache/util/util_error_handler.h @@ -0,0 +1,201 @@ +/* + * Copyright 2013, Blender Foundation. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef PTC_UTIL_ERROR_HANDLER_H +#define PTC_UTIL_ERROR_HANDLER_H + +#include <stdio.h> + +#ifdef WITH_ALEMBIC +#include <Alembic/Abc/ErrorHandler.h> +#endif + +extern "C" { +#include "BLI_utildefines.h" +#include "BLI_string.h" +} + +#include "util_types.h" + +struct ModifierData; +struct ReportList; + +namespace PTC { + +class ErrorHandler +{ +public: + ErrorHandler(); + virtual ~ErrorHandler(); + + virtual void handle(PTCErrorLevel level, const char *message) = 0; + void set_error_level(PTCErrorLevel level); + PTCErrorLevel max_error_level() const { return m_max_level; } + + static ErrorHandler *get_default_handler() { return m_default_handler; } + static void set_default_handler(ErrorHandler *handler); + static void clear_default_handler(); + +private: + PTCErrorLevel m_max_level; + + static ErrorHandler *m_default_handler; +}; + + +class StdErrorHandler : public ErrorHandler +{ +public: + StdErrorHandler(PTCErrorLevel level); + + void handle(PTCErrorLevel level, const char *message); + + PTCErrorLevel get_verbosity() const { return m_verbosity; } + void set_verbosity(PTCErrorLevel level) { m_verbosity = level; } + +private: + PTCErrorLevel m_verbosity; +}; + + +/* Use Blender reports system to log Alembic errors */ +class CallbackErrorHandler : public ErrorHandler +{ +public: + CallbackErrorHandler(PTCErrorCallback cb, void *userdata); + + void handle(PTCErrorLevel level, const char *message); + +private: + PTCErrorCallback m_callback; + void *m_userdata; +}; + + +class ModifierErrorHandler : public ErrorHandler +{ +public: + ModifierErrorHandler(ModifierData *md); + + void handle(PTCErrorLevel level, const char *message); + +private: + ModifierData *m_modifier; +}; + +/* -------------------------------- */ + +#ifdef WITH_ALEMBIC + +/* XXX With current Alembic version 1.5 we only get a combined error message. + * This function try to extract some more information and return a nicer message format. + */ +BLI_INLINE void split_alembic_error_message(const char *msg, const char **origin, const char **base_msg) +{ + const char delim[] = {'\n', '\0'}; + char *sep, *suffix; + + BLI_str_partition(msg, delim, &sep, &suffix); + if (suffix) { + *origin = msg; + BLI_str_partition(suffix, delim, &sep, &suffix); + if (suffix) { + *base_msg = suffix; + } + else { + *base_msg = msg; + } + } + else { + *origin = *base_msg = msg; + } +} + +/* wrapper templates so the exception macro can be used with references as well as pointers */ + +template <typename T> +void handle_alembic_exception(T &handler, PTCErrorLevel level, const Alembic::Util::Exception &e) +{ + const char *origin, *msg; + split_alembic_error_message(e.what(), &origin, &msg); + + handler.set_error_level(level); + handler.handle(level, msg); +} + +template <typename T> +void handle_alembic_exception(T *handler, PTCErrorLevel level, const Alembic::Util::Exception &e) +{ + static StdErrorHandler default_handler(PTC_ERROR_WARNING); + if (!handler) + handler = &default_handler; + + const char *origin, *msg; + split_alembic_error_message(e.what(), &origin, &msg); + + handler->set_error_level(level); + handler->handle(level, msg); +} + +#endif + +/* -------------------------------- */ + +/* macros for convenient exception handling */ + +#define PTC_SAFE_CALL_BEGIN \ + try { + +#ifdef WITH_ALEMBIC +#define PTC_SAFE_CALL_END_HANDLER(handler) \ + } \ + catch (Alembic::Util::Exception e) { \ + handle_alembic_exception((handler), PTC_ERROR_CRITICAL, e); \ + } +#else +#define PTC_SAFE_CALL_END_HANDLER(handler) \ + } +#endif + +#ifdef WITH_ALEMBIC +#define PTC_SAFE_CALL_END_HANDLER_LEVEL(handler, level) \ + } \ + catch (Alembic::Util::Exception e) { \ + handle_alembic_exception((handler), (level), e); \ + } +#else +#define PTC_SAFE_CALL_END_HANDLER_LEVEL(handler, level) \ + } +#endif + +#ifdef WITH_ALEMBIC +#define PTC_SAFE_CALL_END \ + } \ + catch (Alembic::Util::Exception e) { \ + handle_alembic_exception(ErrorHandler::get_default_handler(), PTC_ERROR_CRITICAL, e); \ + } +#else +#define PTC_SAFE_CALL_END \ + } +#endif + +/* -------------------------------- */ + +} /* namespace PTC */ + +#endif /* PTC_UTIL_ERROR_HANDLER_H */ diff --git a/source/blender/pointcache/util/util_function.h b/source/blender/pointcache/util/util_function.h new file mode 100644 index 00000000000..97f0278237d --- /dev/null +++ b/source/blender/pointcache/util/util_function.h @@ -0,0 +1,48 @@ +/* + * Copyright 2015, Blender Foundation. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef PTC_UTIL_FUNCTION +#define PTC_UTIL_FUNCTION + +#if __cplusplus > 199711L + +#include <functional> + +namespace PTC { + +using std::function; +using namespace std::placeholders; +#define function_bind std::bind + +} /* namespace PTC */ + +#else + +#include <boost/bind.hpp> +#include <boost/function.hpp> + +namespace PTC { + +using boost::function; +#define function_bind boost::bind + +} /* namespace PTC */ + +#endif + +#endif /* PTC_UTIL_FUNCTION */ diff --git a/source/blender/pointcache/util/util_task.cpp b/source/blender/pointcache/util/util_task.cpp new file mode 100644 index 00000000000..feecdd54b97 --- /dev/null +++ b/source/blender/pointcache/util/util_task.cpp @@ -0,0 +1,87 @@ +/* + * Copyright 2015, Blender Foundation. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "util_task.h" + +#include "BLI_task.h" +#include "BLI_threads.h" + +namespace PTC { + +class UtilTask { +public: + explicit UtilTask(const UtilTaskFunction& run) : run_(run) {} + void run() + { + run_(); + } +protected: + UtilTaskFunction run_; +}; + +static void task_function(TaskPool * /*pool*/, + void *taskdata, + int /*threadid*/) +{ + UtilTask *task = reinterpret_cast<UtilTask*>(taskdata); + task->run(); + delete task; +} + +UtilTaskPool::UtilTaskPool() +{ + scheduler_ = BLI_task_scheduler_get(); + pool_ = BLI_task_pool_create(scheduler_, NULL); +} + +UtilTaskPool::~UtilTaskPool() +{ + BLI_task_pool_free(pool_); +} + +void UtilTaskPool::push(const UtilTaskFunction& run, bool front) +{ + UtilTask *task = new UtilTask(run); + BLI_task_pool_push(pool_, + task_function, + task, + false, + front? TASK_PRIORITY_HIGH: TASK_PRIORITY_LOW); +} + +void UtilTaskPool::wait_work() +{ + BLI_task_pool_work_and_wait(pool_); +} + +void UtilTaskPool::cancel() +{ + BLI_task_pool_cancel(pool_); +} + +void UtilTaskPool::stop() +{ + BLI_task_pool_stop(pool_); +} + +bool UtilTaskPool::cancelled() +{ + return BLI_task_pool_canceled(pool_); +} + +} /* namespace PTC */ diff --git a/source/blender/pointcache/util/util_task.h b/source/blender/pointcache/util/util_task.h new file mode 100644 index 00000000000..be2f1cca84f --- /dev/null +++ b/source/blender/pointcache/util/util_task.h @@ -0,0 +1,51 @@ +/* + * Copyright 2015, Blender Foundation. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef PTC_UTIL_TASK_H +#define PTC_UTIL_TASK_H + +#include "util_function.h" + +struct TaskScheduler; +struct TaskPool; + +namespace PTC { + +typedef function<void(void)> UtilTaskFunction; + +class UtilTaskPool { +public: + UtilTaskPool(); + ~UtilTaskPool(); + + void push(const UtilTaskFunction& run, bool front = false); + + void wait_work(); + void cancel(); + void stop(); + + bool cancelled(); + +protected: + struct TaskScheduler *scheduler_; + struct TaskPool *pool_; +}; + +} /* namespace PTC */ + +#endif /* PTC_UTIL_TASK_H */ diff --git a/source/blender/pointcache/util/util_thread.h b/source/blender/pointcache/util/util_thread.h new file mode 100644 index 00000000000..6b41a35ee3f --- /dev/null +++ b/source/blender/pointcache/util/util_thread.h @@ -0,0 +1,74 @@ +/* + * Copyright 2015, Blender Foundation. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef PTC_UTIL_THREAD_H +#define PTC_UTIL_THREAD_H + +#include "BLI_threads.h" + +namespace PTC { + +class thread_mutex { +public: + thread_mutex() + { + BLI_mutex_init(&mutex_); + } + + ~thread_mutex() + { + BLI_mutex_end(&mutex_); + } + + void lock() + { + BLI_mutex_lock(&mutex_); + } + + bool trylock() + { + return BLI_mutex_trylock(&mutex_); + } + + void unlock() + { + BLI_mutex_unlock(&mutex_); + } + +protected: + ThreadMutex mutex_; +}; + +class thread_scoped_lock { +public: + explicit thread_scoped_lock(thread_mutex& mutex) + : mutex_(mutex) + { + mutex_.lock(); + } + + ~thread_scoped_lock() { + mutex_.unlock(); + } +protected: + thread_mutex& mutex_; +}; + +} /* namespace PTC */ + +#endif /* PTC_UTIL_THREAD_H */ diff --git a/source/blender/pointcache/util/util_types.h b/source/blender/pointcache/util/util_types.h new file mode 100644 index 00000000000..0ee027c7700 --- /dev/null +++ b/source/blender/pointcache/util/util_types.h @@ -0,0 +1,53 @@ +/* + * Copyright 2013, Blender Foundation. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef PTC_UTIL_TYPES_H +#define PTC_UTIL_TYPES_H + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum PTCArchiveResolution { + PTC_RESOLUTION_NONE = 0, + PTC_RESOLUTION_PREVIEW = (1 << 0), + PTC_RESOLUTION_RENDER = (1 << 1), +} PTCArchiveResolution; + +typedef enum PTCErrorLevel { + PTC_ERROR_NONE = 0, + PTC_ERROR_INFO = 1, + PTC_ERROR_WARNING = 2, + PTC_ERROR_CRITICAL = 3, +} PTCErrorLevel; + +typedef void (*PTCErrorCallback)(void *userdata, PTCErrorLevel level, const char *message); + +typedef enum PTCReadSampleResult { + PTC_READ_SAMPLE_INVALID = 0, /* no valid result can be retrieved */ + PTC_READ_SAMPLE_EARLY, /* request time before first sample */ + PTC_READ_SAMPLE_LATE, /* request time after last sample */ + PTC_READ_SAMPLE_EXACT, /* found sample for requested frame */ + PTC_READ_SAMPLE_INTERPOLATED /* no exact sample, but found enclosing samples for interpolation */ +} PTCReadSampleResult; + +#ifdef __cplusplus +} +#endif + +#endif /* PTC_UTIL_TYPES_H */ diff --git a/source/blender/python/bmesh/bmesh_py_types_customdata.c b/source/blender/python/bmesh/bmesh_py_types_customdata.c index 3adf37f78f0..1d5f0d7ac37 100644 --- a/source/blender/python/bmesh/bmesh_py_types_customdata.c +++ b/source/blender/python/bmesh/bmesh_py_types_customdata.c @@ -987,8 +987,8 @@ PyObject *BPy_BMLayerItem_GetItem(BPy_BMElem *py_ele, BPy_BMLayerItem *py_layer) ret = BPy_BMDeformVert_CreatePyObject(value); break; } - case CD_PROP_FLT: case CD_PAINT_MASK: + case CD_PROP_FLT: { ret = PyFloat_FromDouble(*(float *)value); break; @@ -1065,8 +1065,8 @@ int BPy_BMLayerItem_SetItem(BPy_BMElem *py_ele, BPy_BMLayerItem *py_layer, PyObj ret = BPy_BMDeformVert_AssignPyObject(value, py_value); break; } - case CD_PROP_FLT: case CD_PAINT_MASK: + case CD_PROP_FLT: { float tmp_val = PyFloat_AsDouble(py_value); if (UNLIKELY(tmp_val == -1 && PyErr_Occurred())) { diff --git a/source/blender/render/extern/include/RE_render_ext.h b/source/blender/render/extern/include/RE_render_ext.h index 6adbb6d0e62..9d8a1a66771 100644 --- a/source/blender/render/extern/include/RE_render_ext.h +++ b/source/blender/render/extern/include/RE_render_ext.h @@ -59,6 +59,12 @@ void RE_free_sample_material(struct Material *mat); void RE_sample_material_color(struct Material *mat, float color[3], float *alpha, const float volume_co[3], const float surface_co[3], int face_index, short hit_quad, struct DerivedMesh *orcoDm, struct Object *ob); +/* pointdensity.c */ + +struct PointDensity; + +void RE_sample_point_density(struct Scene *scene, struct PointDensity *pd, int resolution, float *values); + void RE_init_texture_rng(void); void RE_exit_texture_rng(void); #endif /* __RE_RENDER_EXT_H__ */ diff --git a/source/blender/render/intern/source/pipeline.c b/source/blender/render/intern/source/pipeline.c index 001120d883f..0a1aab8feca 100644 --- a/source/blender/render/intern/source/pipeline.c +++ b/source/blender/render/intern/source/pipeline.c @@ -2810,6 +2810,8 @@ static bool check_valid_camera_multiview(Scene *scene, Object *camera, ReportLis static int check_valid_camera(Scene *scene, Object *camera_override, ReportList *reports) { + const char *err_msg = "No camera found in scene \"%s\""; + if (camera_override == NULL && scene->camera == NULL) scene->camera = BKE_scene_camera_find(scene); @@ -2821,14 +2823,17 @@ static int check_valid_camera(Scene *scene, Object *camera_override, ReportList Sequence *seq = scene->ed->seqbase.first; while (seq) { - if (seq->type == SEQ_TYPE_SCENE && seq->scene) { + if ((seq->type == SEQ_TYPE_SCENE) && + ((seq->flag & SEQ_SCENE_STRIPS) == 0) && + (seq->scene != NULL)) + { if (!seq->scene_camera) { if (!seq->scene->camera && !BKE_scene_camera_find(seq->scene)) { /* camera could be unneeded due to composite nodes */ Object *override = (seq->scene == scene) ? camera_override : NULL; if (!check_valid_compositing_camera(seq->scene, override)) { - BKE_reportf(reports, RPT_ERROR, "No camera found in scene \"%s\"", seq->scene->id.name+2); + BKE_reportf(reports, RPT_ERROR, err_msg, seq->scene->id.name + 2); return false; } } @@ -2842,7 +2847,7 @@ static int check_valid_camera(Scene *scene, Object *camera_override, ReportList } } else if (!check_valid_compositing_camera(scene, camera_override)) { - BKE_report(reports, RPT_ERROR, "No camera found in scene"); + BKE_reportf(reports, RPT_ERROR, err_msg, scene->id.name + 2); return false; } diff --git a/source/blender/render/intern/source/pointdensity.c b/source/blender/render/intern/source/pointdensity.c index 3a1f1fbb744..5cd8a739125 100644 --- a/source/blender/render/intern/source/pointdensity.c +++ b/source/blender/render/intern/source/pointdensity.c @@ -45,6 +45,7 @@ #include "BKE_DerivedMesh.h" #include "BKE_lattice.h" #include "BKE_main.h" +#include "BKE_object.h" #include "BKE_particle.h" #include "BKE_scene.h" #include "BKE_texture.h" @@ -58,6 +59,8 @@ #include "texture.h" #include "pointdensity.h" +#include "RE_render_ext.h" + /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ /* defined in pipeline.c, is hardcopy of active dynamic allocated Render */ /* only to be used here in this file, it's for speed */ @@ -83,6 +86,10 @@ static int point_data_used(PointDensity *pd) { pd_bitflag |= POINT_DATA_LIFE; } + if ((pd->color_source == TEX_PD_COLOR_PARTTEX)) + { + pd_bitflag |= POINT_DATA_COLOR; + } } return pd_bitflag; @@ -104,6 +111,10 @@ static void alloc_point_data(PointDensity *pd, int total_particles, int point_da /* store 1 channel of lifetime data */ data_size += 1; } + if (point_data_used & POINT_DATA_COLOR) { + /* store 3 channels of color data */ + data_size += 3; + } if (data_size) { pd->point_data = MEM_mallocN(sizeof(float) * data_size * total_particles, @@ -111,6 +122,19 @@ static void alloc_point_data(PointDensity *pd, int total_particles, int point_da } } +/* offset of age and color data in the common point data array */ +static void point_data_get_offset(int total_particles, int point_data_used, int *offset_life, int *offset_color) +{ + *offset_life = *offset_color = 0; + if (point_data_used & POINT_DATA_VEL) { + *offset_life += total_particles * 3; + *offset_color += total_particles * 3; + } + if (point_data_used & POINT_DATA_LIFE) { + *offset_color += total_particles; + } +} + static void pointdensity_cache_psys(Scene *scene, PointDensity *pd, Object *ob, @@ -126,7 +150,7 @@ static void pointdensity_cache_psys(Scene *scene, ParticleData *pa = NULL; float cfra = BKE_scene_frame_get(scene); int i /*, childexists*/ /* UNUSED */; - int total_particles, offset = 0; + int total_particles, offset_life = 0, offset_color = 0; int data_used = point_data_used(pd); float partco[3]; @@ -148,6 +172,7 @@ static void pointdensity_cache_psys(Scene *scene, sim.scene = scene; sim.ob = ob; sim.psys = psys; + sim.psmd = psys_get_modifier(ob, psys); /* in case ob->imat isn't up-to-date */ invert_m4_m4(ob->imat, ob->obmat); @@ -158,9 +183,7 @@ static void pointdensity_cache_psys(Scene *scene, pd->point_tree = BLI_bvhtree_new(total_particles, 0.0, 4, 6); alloc_point_data(pd, total_particles, data_used); pd->totpoints = total_particles; - if (data_used & POINT_DATA_VEL) { - offset = pd->totpoints * 3; - } + point_data_get_offset(total_particles, data_used, &offset_life, &offset_color); #if 0 /* UNUSED */ if (psys->totchild > 0 && !(psys->part->draw & PART_DRAW_PARENT)) @@ -223,7 +246,12 @@ static void pointdensity_cache_psys(Scene *scene, pd->point_data[i * 3 + 2] = state.vel[2]; } if (data_used & POINT_DATA_LIFE) { - pd->point_data[offset + i] = state.time; + pd->point_data[offset_life + i] = state.time; + } + if (data_used & POINT_DATA_COLOR) { + ParticleTexture ptex; + psys_get_texture(&sim, pa, &ptex, PAMAP_COLOR, cfra); + copy_v3_v3(&pd->point_data[offset_color + i * 3], ptex.color); } } @@ -385,8 +413,9 @@ typedef struct PointDensityRangeData { short falloff_type; short noise_influence; float *age; + float *col; int point_data_used; - int offset; + int offset_life, offset_color; struct CurveMapping *density_curve; float velscale; } PointDensityRangeData; @@ -403,7 +432,10 @@ static void accum_density(void *userdata, int index, float squared_dist) pdr->vec[2] += pdr->point_data[index * 3 + 2]; // * density; } if (pdr->point_data_used & POINT_DATA_LIFE) { - *pdr->age += pdr->point_data[pdr->offset + index]; // * density; + *pdr->age += pdr->point_data[pdr->offset_life + index]; // * density; + } + if (pdr->point_data_used & POINT_DATA_COLOR) { + add_v3_v3(pdr->col, &pdr->point_data[pdr->offset_color + index]); // * density; } if (pdr->falloff_type == TEX_PD_FALLOFF_STD) @@ -418,7 +450,7 @@ static void accum_density(void *userdata, int index, float squared_dist) density = sqrtf(dist); else if (pdr->falloff_type == TEX_PD_FALLOFF_PARTICLE_AGE) { if (pdr->point_data_used & POINT_DATA_LIFE) - density = dist * MIN2(pdr->point_data[pdr->offset + index], 1.0f); + density = dist * MIN2(pdr->point_data[pdr->offset_life + index], 1.0f); else density = dist; } @@ -439,7 +471,7 @@ static void accum_density(void *userdata, int index, float squared_dist) static void init_pointdensityrangedata(PointDensity *pd, PointDensityRangeData *pdr, - float *density, float *vec, float *age, struct CurveMapping *density_curve, float velscale) + float *density, float *vec, float *age, float *col, struct CurveMapping *density_curve, float velscale) { pdr->squared_radius = pd->radius * pd->radius; pdr->density = density; @@ -447,23 +479,26 @@ static void init_pointdensityrangedata(PointDensity *pd, PointDensityRangeData * pdr->falloff_type = pd->falloff_type; pdr->vec = vec; pdr->age = age; + pdr->col = col; pdr->softness = pd->falloff_softness; pdr->noise_influence = pd->noise_influence; pdr->point_data_used = point_data_used(pd); - pdr->offset = (pdr->point_data_used & POINT_DATA_VEL) ? pd->totpoints * 3 : 0; + point_data_get_offset(pd->totpoints, pdr->point_data_used, &pdr->offset_life, &pdr->offset_color); pdr->density_curve = density_curve; pdr->velscale = velscale; } -int pointdensitytex(Tex *tex, const float texvec[3], TexResult *texres) +static int pointdensity(PointDensity *pd, + const float texvec[3], + TexResult *texres, + float *r_age, + float r_vec[3]) { int retval = TEX_INT; - PointDensity *pd = tex->pd; PointDensityRangeData pdr; float density = 0.0f, age = 0.0f, time = 0.0f; - float vec[3] = {0.0f, 0.0f, 0.0f}, co[3]; - float col[4]; + float vec[3] = {0.0f, 0.0f, 0.0f}, color[3] = {0.0f, 0.0f, 0.0f}, co[3]; float turb, noise_fac; int num = 0; @@ -472,7 +507,7 @@ int pointdensitytex(Tex *tex, const float texvec[3], TexResult *texres) if ((!pd) || (!pd->point_tree)) return 0; - init_pointdensityrangedata(pd, &pdr, &density, vec, &age, + init_pointdensityrangedata(pd, &pdr, &density, vec, &age, color, (pd->flag & TEX_PD_FALLOFF_CURVE ? pd->falloff_curve : NULL), pd->falloff_speed_scale * 0.001f); noise_fac = pd->noise_fac * 0.5f; /* better default */ @@ -525,11 +560,24 @@ int pointdensitytex(Tex *tex, const float texvec[3], TexResult *texres) } texres->tin = density; - BRICONT; + texres->tr = color[0]; + texres->tg = color[1]; + texres->tb = color[2]; + if (r_age != NULL) { + *r_age = age; + } + if (r_vec != NULL) { + copy_v3_v3(r_vec, vec); + } - if (pd->color_source == TEX_PD_COLOR_CONSTANT) - return retval; + return retval; +} +static int pointdensity_color(PointDensity *pd, TexResult *texres, float age, const float vec[3]) +{ + int retval = 0; + float col[4]; + retval |= TEX_RGB; switch (pd->color_source) { @@ -559,8 +607,12 @@ int pointdensitytex(Tex *tex, const float texvec[3], TexResult *texres) } case TEX_PD_COLOR_PARTVEL: texres->talpha = true; - mul_v3_fl(vec, pd->speed_scale); - copy_v3_v3(&texres->tr, vec); + mul_v3_v3fl(&texres->tr, vec, pd->speed_scale); + texres->ta = texres->tin; + break; + case TEX_PD_COLOR_PARTTEX: + /* texres already has the particle color */ + texres->talpha = true; texres->ta = texres->tin; break; case TEX_PD_COLOR_CONSTANT: @@ -568,6 +620,20 @@ int pointdensitytex(Tex *tex, const float texvec[3], TexResult *texres) texres->tr = texres->tg = texres->tb = texres->ta = 1.0f; break; } + + return retval; +} + +int pointdensitytex(Tex *tex, const float texvec[3], TexResult *texres) +{ + PointDensity *pd = tex->pd; + float age = 0.0f; + float vec[3] = {0.0f, 0.0f, 0.0f}; + int retval = pointdensity(pd, texvec, texres, &age, vec); + + BRICONT; + + retval |= pointdensity_color(pd, texres, age, vec); BRICONTRGB; return retval; @@ -578,3 +644,104 @@ int pointdensitytex(Tex *tex, const float texvec[3], TexResult *texres) } #endif } + +static void sample_dummy_point_density(int resolution, float *values) +{ + memset(values, 0, sizeof(float) * 4 * resolution * resolution * resolution); +} + +static void particle_system_minmax(Object *object, + ParticleSystem *psys, + float radius, + float min[3], float max[3]) +{ + ParticleSettings *part = psys->part; + float imat[4][4]; + float size[3] = {radius, radius, radius}; + PARTICLE_P; + INIT_MINMAX(min, max); + if (part->type == PART_HAIR) { + /* TOOD(sergey): Not supported currently. */ + return; + } + invert_m4_m4(imat, object->obmat); + LOOP_PARTICLES { + float co_object[3], co_min[3], co_max[3]; + mul_v3_m4v3(co_object, imat, pa->state.co); + sub_v3_v3v3(co_min, co_object, size); + add_v3_v3v3(co_max, co_object, size); + minmax_v3v3_v3(min, max, co_min); + minmax_v3v3_v3(min, max, co_max); + } +} + +void RE_sample_point_density(Scene *scene, PointDensity *pd, + int resolution, float *values) +{ + const size_t resolution2 = resolution * resolution; + Object *object = pd->object; + size_t x, y, z; + float min[3], max[3], dim[3], mat[4][4]; + + if (object == NULL) { + sample_dummy_point_density(resolution, values); + return; + } + + if (pd->source == TEX_PD_PSYS) { + ParticleSystem *psys; + if (pd->psys == 0) { + sample_dummy_point_density(resolution, values); + return; + } + psys = BLI_findlink(&object->particlesystem, pd->psys - 1); + if (psys == NULL) { + sample_dummy_point_density(resolution, values); + return; + } + particle_system_minmax(object, psys, pd->radius, min, max); + } + else { + float radius[3] = {pd->radius, pd->radius, pd->radius}; + float *loc, *size; + BKE_object_obdata_texspace_get(pd->object, NULL, &loc, &size, NULL); + sub_v3_v3v3(min, loc, size); + add_v3_v3v3(max, loc, size); + /* Adjust texture space to include density points on the boundaries. */ + sub_v3_v3(min, radius); + add_v3_v3(max, radius); + } + + sub_v3_v3v3(dim, max, min); + if (dim[0] <= 0.0f || dim[1] <= 0.0f || dim[2] <= 0.0f) { + sample_dummy_point_density(resolution, values); + return; + } + + /* Same matricies/resolution as dupli_render_particle_set(). */ + unit_m4(mat); + cache_pointdensity_ex(scene, pd, mat, mat, 1, 1); + + for (z = 0; z < resolution; ++z) { + for (y = 0; y < resolution; ++y) { + for (x = 0; x < resolution; ++x) { + size_t index = z * resolution2 + y * resolution + x; + float texvec[3]; + float age, vec[3]; + TexResult texres; + + copy_v3_v3(texvec, min); + texvec[0] += dim[0] * (float)x / (float)resolution; + texvec[1] += dim[1] * (float)y / (float)resolution; + texvec[2] += dim[2] * (float)z / (float)resolution; + + pointdensity(pd, texvec, &texres, &age, vec); + pointdensity_color(pd, &texres, age, vec); + + copy_v3_v3(&values[index*4 + 0], &texres.tr); + values[index*4 + 3] = texres.tin; + } + } + } + free_pointdensity(pd); +} diff --git a/source/blender/windowmanager/3d_widgets/arrow_widget.c b/source/blender/windowmanager/3d_widgets/arrow_widget.c new file mode 100644 index 00000000000..6cd427a019a --- /dev/null +++ b/source/blender/windowmanager/3d_widgets/arrow_widget.c @@ -0,0 +1,249 @@ +int _WIDGET_nverts_arrow = 81; +int _WIDGET_ntris_arrow = 76; + +float _WIDGET_verts_arrow[][3] = { + {0.023005, 0.023005, 0.000524}, + {0.030057, 0.012450, 0.000524}, + {0.032533, 0.000000, 0.000524}, + {0.030057, -0.012450, 0.000524}, + {0.023005, -0.023005, 0.000524}, + {0.012450, -0.030057, 0.000524}, + {-0.000000, -0.032533, 0.000524}, + {-0.012450, -0.030057, 0.000524}, + {-0.023005, -0.023005, 0.000524}, + {-0.030057, -0.012450, 0.000524}, + {-0.032533, -0.000000, 0.000524}, + {-0.030057, 0.012450, 0.000524}, + {-0.023005, 0.023005, 0.000524}, + {-0.012450, 0.030057, 0.000524}, + {-0.000000, 0.032533, 0.000524}, + {0.012450, 0.030057, 0.000524}, + {-0.000000, 0.131111, 0.666568}, + {-0.050174, 0.121131, 0.666568}, + {-0.092709, 0.092709, 0.666568}, + {-0.121131, 0.050174, 0.666568}, + {-0.131111, -0.000000, 0.666568}, + {-0.121131, -0.050174, 0.666568}, + {-0.092709, -0.092709, 0.666568}, + {-0.050174, -0.121131, 0.666568}, + {-0.000000, -0.131111, 0.666568}, + {0.050174, -0.121131, 0.666568}, + {0.092709, -0.092709, 0.666568}, + {0.121131, -0.050174, 0.666568}, + {0.131111, 0.000000, 0.666568}, + {0.121131, 0.050174, 0.666568}, + {0.092709, 0.092709, 0.666568}, + {0.050174, 0.121131, 0.666568}, + {-0.000000, 0.131111, 0.666568}, + {-0.050174, 0.121131, 0.666568}, + {-0.092709, 0.092709, 0.666568}, + {-0.121131, 0.050174, 0.666568}, + {-0.131111, -0.000000, 0.666568}, + {-0.121131, -0.050174, 0.666568}, + {-0.092709, -0.092709, 0.666568}, + {-0.050174, -0.121131, 0.666568}, + {-0.000000, -0.131111, 0.666568}, + {0.050174, -0.121131, 0.666568}, + {0.092709, -0.092709, 0.666568}, + {0.121131, -0.050174, 0.666568}, + {0.131111, 0.000000, 0.666568}, + {0.121131, 0.050174, 0.666568}, + {0.092709, 0.092709, 0.666568}, + {0.050174, 0.121131, 0.666568}, + {-0.000000, -0.000000, 0.999876}, + {0.023005, 0.023005, 0.000524}, + {0.030057, 0.012450, 0.000524}, + {0.032533, 0.000000, 0.000524}, + {0.030057, -0.012450, 0.000524}, + {0.023005, -0.023005, 0.000524}, + {0.012450, -0.030057, 0.000524}, + {-0.000000, -0.032533, 0.000524}, + {-0.012450, -0.030057, 0.000524}, + {-0.023005, -0.023005, 0.000524}, + {-0.030057, -0.012450, 0.000524}, + {-0.032533, -0.000000, 0.000524}, + {-0.030057, 0.012450, 0.000524}, + {-0.023005, 0.023005, 0.000524}, + {-0.012450, 0.030057, 0.000524}, + {-0.000000, 0.032533, 0.000524}, + {0.012450, 0.030057, 0.000524}, + {0.023005, 0.023005, 0.673867}, + {0.030057, 0.012450, 0.673867}, + {0.032533, 0.000000, 0.673867}, + {0.030057, -0.012450, 0.673867}, + {0.023005, -0.023005, 0.673867}, + {0.012450, -0.030057, 0.673867}, + {-0.000000, -0.032533, 0.673867}, + {-0.012450, -0.030057, 0.673867}, + {-0.023005, -0.023005, 0.673867}, + {-0.030057, -0.012450, 0.673867}, + {-0.032533, -0.000000, 0.673867}, + {-0.030057, 0.012450, 0.673867}, + {-0.023005, 0.023005, 0.673867}, + {-0.012450, 0.030057, 0.673867}, + {-0.000000, 0.032533, 0.673867}, + {0.012450, 0.030057, 0.673867}, +}; + +float _WIDGET_normals_arrow[][3] = { + {0.000000, 0.000000, -1.000000}, + {0.000000, 0.000000, -1.000000}, + {0.000000, 0.000000, -1.000000}, + {0.000000, 0.000000, -1.000000}, + {0.000000, 0.000000, -1.000000}, + {0.000000, 0.000000, -1.000000}, + {0.000000, 0.000000, -1.000000}, + {0.000000, 0.000000, -1.000000}, + {0.000000, 0.000000, -1.000000}, + {0.000000, 0.000000, -1.000000}, + {0.000000, 0.000000, -1.000000}, + {0.000000, 0.000000, -1.000000}, + {0.000000, 0.000000, -1.000000}, + {0.000000, 0.000000, -1.000000}, + {0.000000, 0.000000, -1.000000}, + {0.000000, 0.000000, -1.000000}, + {0.000000, 0.000000, -1.000000}, + {0.000000, 0.000000, -1.000000}, + {0.000000, 0.000000, -1.000000}, + {0.000000, 0.000000, -1.000000}, + {0.000000, 0.000000, -1.000000}, + {0.000000, 0.000000, -1.000000}, + {0.000000, 0.000000, -1.000000}, + {0.000000, 0.000000, -1.000000}, + {0.000000, 0.000000, -1.000000}, + {0.000000, 0.000000, -1.000000}, + {0.000000, 0.000000, -1.000000}, + {0.000000, 0.000000, -1.000000}, + {0.000000, 0.000000, -1.000000}, + {0.000000, 0.000000, -1.000000}, + {0.000000, 0.000000, -1.000000}, + {0.000000, 0.000000, -1.000000}, + {0.000000, 0.930570, 0.366039}, + {-0.356120, 0.859737, 0.366039}, + {-0.658010, 0.658010, 0.366039}, + {-0.859737, 0.356120, 0.366039}, + {-0.930570, 0.000000, 0.366039}, + {-0.859737, -0.356120, 0.366039}, + {-0.658010, -0.658010, 0.366039}, + {-0.356120, -0.859737, 0.366039}, + {0.000000, -0.930570, 0.366039}, + {0.356120, -0.859737, 0.366039}, + {0.658010, -0.658010, 0.366039}, + {0.859737, -0.356120, 0.366039}, + {0.930570, 0.000000, 0.366039}, + {0.859737, 0.356120, 0.366039}, + {0.658010, 0.658010, 0.366039}, + {0.356120, 0.859737, 0.366039}, + {0.000000, 0.000000, 1.000000}, + {0.707083, 0.707083, 0.000000}, + {0.923856, 0.382672, 0.000000}, + {1.000000, 0.000000, 0.000000}, + {0.923856, -0.382672, 0.000000}, + {0.707083, -0.707083, 0.000000}, + {0.382672, -0.923856, 0.000000}, + {0.000000, -1.000000, 0.000000}, + {-0.382672, -0.923856, 0.000000}, + {-0.707083, -0.707083, 0.000000}, + {-0.923856, -0.382672, 0.000000}, + {-1.000000, 0.000000, 0.000000}, + {-0.923856, 0.382672, 0.000000}, + {-0.707083, 0.707083, 0.000000}, + {-0.382672, 0.923856, 0.000000}, + {0.000000, 1.000000, 0.000000}, + {0.382672, 0.923856, 0.000000}, + {0.707083, 0.707083, 0.000000}, + {0.923856, 0.382672, 0.000000}, + {1.000000, 0.000000, 0.000000}, + {0.923856, -0.382672, 0.000000}, + {0.707083, -0.707083, 0.000000}, + {0.382672, -0.923856, 0.000000}, + {0.000000, -0.999969, 0.000000}, + {-0.382672, -0.923856, 0.000000}, + {-0.707083, -0.707083, 0.000000}, + {-0.923856, -0.382672, 0.000000}, + {-1.000000, 0.000000, 0.000000}, + {-0.923856, 0.382672, 0.000000}, + {-0.707083, 0.707083, 0.000000}, + {-0.382672, 0.923856, 0.000000}, + {0.000000, 1.000000, 0.000000}, + {0.382672, 0.923856, 0.000000}, +}; + +unsigned short _WIDGET_indices_arrow[] = { + 13, 1, 9, + 49, 65, 66, + 62, 78, 79, + 51, 67, 68, + 53, 69, 70, + 55, 71, 72, + 57, 73, 74, + 59, 75, 76, + 64, 80, 65, + 61, 77, 78, + 50, 66, 67, + 52, 68, 69, + 54, 70, 71, + 56, 72, 73, + 30, 29, 28, + 39, 40, 48, + 37, 38, 48, + 35, 36, 48, + 46, 47, 48, + 33, 34, 48, + 44, 45, 48, + 42, 43, 48, + 40, 41, 48, + 38, 39, 48, + 36, 37, 48, + 47, 32, 48, + 34, 35, 48, + 45, 46, 48, + 32, 33, 48, + 43, 44, 48, + 41, 42, 48, + 60, 76, 77, + 63, 79, 80, + 58, 74, 75, + 11, 12, 13, + 9, 10, 11, + 7, 8, 9, + 7, 9, 6, + 5, 6, 4, + 1, 2, 3, + 1, 14, 0, + 63, 62, 79, + 9, 11, 13, + 50, 49, 66, + 14, 15, 0, + 6, 9, 1, + 1, 13, 14, + 6, 3, 4, + 3, 6, 1, + 52, 51, 68, + 54, 53, 70, + 56, 55, 72, + 58, 57, 74, + 60, 59, 76, + 49, 64, 65, + 62, 61, 78, + 51, 50, 67, + 53, 52, 69, + 55, 54, 71, + 57, 56, 73, + 17, 20, 18, + 61, 60, 77, + 21, 20, 22, + 23, 20, 24, + 26, 25, 24, + 16, 31, 30, + 64, 63, 80, + 28, 27, 26, + 17, 26, 20, + 20, 26, 24, + 28, 26, 16, + 16, 30, 28, + 20, 23, 22, + 16, 26, 17, + 20, 19, 18, + 59, 58, 75, +}; diff --git a/source/blender/windowmanager/3d_widgets/dial_widget.c b/source/blender/windowmanager/3d_widgets/dial_widget.c new file mode 100644 index 00000000000..002bd029dac --- /dev/null +++ b/source/blender/windowmanager/3d_widgets/dial_widget.c @@ -0,0 +1,779 @@ +int _WIDGET_nverts_dial = 192; +int _WIDGET_ntris_dial = 384; + +float _WIDGET_verts_dial[][3] = { + {1.034000, 0.000000, 0.000000}, + {1.017000, 0.000000, 0.029445}, + {0.983000, 0.000000, 0.029445}, + {0.966000, 0.000000, 0.000000}, + {0.983000, 0.000000, -0.029445}, + {1.017000, 0.000000, -0.029445}, + {1.014132, 0.201723, 0.000000}, + {0.997459, 0.198407, 0.029445}, + {0.964112, 0.191774, 0.029445}, + {0.947439, 0.188457, 0.000000}, + {0.964112, 0.191774, -0.029445}, + {0.997459, 0.198407, -0.029445}, + {0.955292, 0.395695, 0.000000}, + {0.939586, 0.389189, 0.029445}, + {0.908174, 0.376178, 0.029445}, + {0.892468, 0.369672, 0.000000}, + {0.908174, 0.376178, -0.029445}, + {0.939586, 0.389189, -0.029445}, + {0.859740, 0.574460, 0.000000}, + {0.845605, 0.565015, 0.029445}, + {0.817335, 0.546126, 0.029445}, + {0.803200, 0.536681, 0.000000}, + {0.817335, 0.546126, -0.029445}, + {0.845605, 0.565015, -0.029445}, + {0.731148, 0.731148, 0.000000}, + {0.719128, 0.719128, 0.029445}, + {0.695086, 0.695086, 0.029445}, + {0.683065, 0.683065, 0.000000}, + {0.695086, 0.695086, -0.029445}, + {0.719128, 0.719128, -0.029445}, + {0.574460, 0.859740, 0.000000}, + {0.565015, 0.845605, 0.029445}, + {0.546125, 0.817335, 0.029445}, + {0.536681, 0.803200, 0.000000}, + {0.546125, 0.817335, -0.029445}, + {0.565015, 0.845605, -0.029445}, + {0.395695, 0.955291, 0.000000}, + {0.389189, 0.939585, 0.029445}, + {0.376178, 0.908173, 0.029445}, + {0.369672, 0.892467, 0.000000}, + {0.376178, 0.908173, -0.029445}, + {0.389189, 0.939585, -0.029445}, + {0.201724, 1.014132, 0.000000}, + {0.198407, 0.997459, 0.029445}, + {0.191774, 0.964112, 0.029445}, + {0.188457, 0.947439, 0.000000}, + {0.191774, 0.964112, -0.029445}, + {0.198407, 0.997459, -0.029445}, + {0.000000, 1.034000, 0.000000}, + {0.000000, 1.017000, 0.029445}, + {0.000000, 0.983000, 0.029445}, + {0.000000, 0.966000, 0.000000}, + {0.000000, 0.983000, -0.029445}, + {0.000000, 1.017000, -0.029445}, + {-0.201723, 1.014132, 0.000000}, + {-0.198407, 0.997459, 0.029445}, + {-0.191774, 0.964112, 0.029445}, + {-0.188457, 0.947439, 0.000000}, + {-0.191774, 0.964112, -0.029445}, + {-0.198407, 0.997459, -0.029445}, + {-0.395695, 0.955291, 0.000000}, + {-0.389189, 0.939585, 0.029445}, + {-0.376178, 0.908174, 0.029445}, + {-0.369672, 0.892468, 0.000000}, + {-0.376178, 0.908174, -0.029445}, + {-0.389189, 0.939585, -0.029445}, + {-0.574459, 0.859740, 0.000000}, + {-0.565015, 0.845605, 0.029445}, + {-0.546125, 0.817335, 0.029445}, + {-0.536681, 0.803200, 0.000000}, + {-0.546125, 0.817335, -0.029445}, + {-0.565015, 0.845605, -0.029445}, + {-0.731149, 0.731148, 0.000000}, + {-0.719128, 0.719127, 0.029445}, + {-0.695086, 0.695086, 0.029445}, + {-0.683065, 0.683065, 0.000000}, + {-0.695086, 0.695086, -0.029445}, + {-0.719128, 0.719127, -0.029445}, + {-0.859740, 0.574460, 0.000000}, + {-0.845604, 0.565015, 0.029445}, + {-0.817335, 0.546126, 0.029445}, + {-0.803200, 0.536681, 0.000000}, + {-0.817335, 0.546126, -0.029445}, + {-0.845604, 0.565015, -0.029445}, + {-0.955291, 0.395695, 0.000000}, + {-0.939585, 0.389189, 0.029445}, + {-0.908173, 0.376178, 0.029445}, + {-0.892468, 0.369672, 0.000000}, + {-0.908173, 0.376178, -0.029445}, + {-0.939585, 0.389189, -0.029445}, + {-1.014132, 0.201723, 0.000000}, + {-0.997459, 0.198407, 0.029445}, + {-0.964112, 0.191774, 0.029445}, + {-0.947439, 0.188457, 0.000000}, + {-0.964112, 0.191774, -0.029445}, + {-0.997459, 0.198407, -0.029445}, + {-1.034000, 0.000000, 0.000000}, + {-1.017000, 0.000000, 0.029445}, + {-0.983000, 0.000000, 0.029445}, + {-0.966000, 0.000000, 0.000000}, + {-0.983000, 0.000000, -0.029445}, + {-1.017000, 0.000000, -0.029445}, + {-1.014132, -0.201723, 0.000000}, + {-0.997459, -0.198407, 0.029445}, + {-0.964112, -0.191774, 0.029445}, + {-0.947439, -0.188457, 0.000000}, + {-0.964112, -0.191774, -0.029445}, + {-0.997459, -0.198407, -0.029445}, + {-0.955292, -0.395694, 0.000000}, + {-0.939586, -0.389189, 0.029445}, + {-0.908174, -0.376177, 0.029445}, + {-0.892468, -0.369672, 0.000000}, + {-0.908174, -0.376177, -0.029445}, + {-0.939586, -0.389189, -0.029445}, + {-0.859740, -0.574460, 0.000000}, + {-0.845604, -0.565015, 0.029445}, + {-0.817335, -0.546126, 0.029445}, + {-0.803200, -0.536681, 0.000000}, + {-0.817335, -0.546126, -0.029445}, + {-0.845604, -0.565015, -0.029445}, + {-0.731149, -0.731148, 0.000000}, + {-0.719128, -0.719127, 0.029445}, + {-0.695086, -0.695086, 0.029445}, + {-0.683065, -0.683065, 0.000000}, + {-0.695086, -0.695086, -0.029445}, + {-0.719128, -0.719127, -0.029445}, + {-0.574460, -0.859739, 0.000000}, + {-0.565015, -0.845604, 0.029445}, + {-0.546126, -0.817334, 0.029445}, + {-0.536681, -0.803199, 0.000000}, + {-0.546126, -0.817334, -0.029445}, + {-0.565015, -0.845604, -0.029445}, + {-0.395695, -0.955291, 0.000000}, + {-0.389189, -0.939585, 0.029445}, + {-0.376178, -0.908174, 0.029445}, + {-0.369672, -0.892468, 0.000000}, + {-0.376178, -0.908174, -0.029445}, + {-0.389189, -0.939585, -0.029445}, + {-0.201724, -1.014132, 0.000000}, + {-0.198407, -0.997459, 0.029445}, + {-0.191774, -0.964112, 0.029445}, + {-0.188458, -0.947438, 0.000000}, + {-0.191774, -0.964112, -0.029445}, + {-0.198407, -0.997459, -0.029445}, + {0.000000, -1.034000, 0.000000}, + {0.000000, -1.017000, 0.029445}, + {0.000000, -0.983000, 0.029445}, + {0.000000, -0.966000, 0.000000}, + {0.000000, -0.983000, -0.029445}, + {0.000000, -1.017000, -0.029445}, + {0.201723, -1.014132, 0.000000}, + {0.198407, -0.997459, 0.029445}, + {0.191773, -0.964112, 0.029445}, + {0.188457, -0.947439, 0.000000}, + {0.191773, -0.964112, -0.029445}, + {0.198407, -0.997459, -0.029445}, + {0.395695, -0.955291, 0.000000}, + {0.389189, -0.939585, 0.029445}, + {0.376178, -0.908173, 0.029445}, + {0.369672, -0.892467, 0.000000}, + {0.376178, -0.908173, -0.029445}, + {0.389189, -0.939585, -0.029445}, + {0.574460, -0.859740, 0.000000}, + {0.565015, -0.845605, 0.029445}, + {0.546125, -0.817335, 0.029445}, + {0.536681, -0.803200, 0.000000}, + {0.546125, -0.817335, -0.029445}, + {0.565015, -0.845605, -0.029445}, + {0.731148, -0.731149, 0.000000}, + {0.719127, -0.719128, 0.029445}, + {0.695086, -0.695086, 0.029445}, + {0.683065, -0.683066, 0.000000}, + {0.695086, -0.695086, -0.029445}, + {0.719127, -0.719128, -0.029445}, + {0.859740, -0.574460, 0.000000}, + {0.845605, -0.565015, 0.029445}, + {0.817335, -0.546126, 0.029445}, + {0.803200, -0.536681, 0.000000}, + {0.817335, -0.546126, -0.029445}, + {0.845605, -0.565015, -0.029445}, + {0.955291, -0.395695, 0.000000}, + {0.939585, -0.389189, 0.029445}, + {0.908173, -0.376178, 0.029445}, + {0.892467, -0.369673, 0.000000}, + {0.908173, -0.376178, -0.029445}, + {0.939585, -0.389189, -0.029445}, + {1.014132, -0.201723, 0.000000}, + {0.997459, -0.198407, 0.029445}, + {0.964112, -0.191774, 0.029445}, + {0.947439, -0.188457, 0.000000}, + {0.964112, -0.191774, -0.029445}, + {0.997459, -0.198407, -0.029445}, +}; + +float _WIDGET_normals_dial[][3] = { + {1.000000, 0.000000, 0.000000}, + {0.522691, 0.000000, 0.852504}, + {-0.475845, 0.000000, 0.879513}, + {-1.000000, 0.000000, 0.000000}, + {-0.475845, 0.000000, -0.879513}, + {0.522691, 0.000000, -0.852504}, + {0.980773, 0.195074, 0.000000}, + {0.512650, 0.101962, 0.852504}, + {-0.466689, -0.092807, 0.879513}, + {-0.980773, -0.195074, 0.000000}, + {-0.466689, -0.092807, -0.879513}, + {0.512650, 0.101962, -0.852504}, + {0.923856, 0.382672, 0.000000}, + {0.482894, 0.200018, 0.852504}, + {-0.439619, -0.182073, 0.879513}, + {-0.923856, -0.382672, 0.000000}, + {-0.439619, -0.182073, -0.879513}, + {0.482894, 0.200018, -0.852504}, + {0.831446, 0.555559, 0.000000}, + {0.434614, 0.290384, 0.852504}, + {-0.395642, -0.264351, 0.879513}, + {-0.831446, -0.555559, 0.000000}, + {-0.395642, -0.264351, -0.879513}, + {0.434614, 0.290384, -0.852504}, + {0.707083, 0.707083, 0.000000}, + {0.369610, 0.369610, 0.852504}, + {-0.336467, -0.336467, 0.879513}, + {-0.707083, -0.707083, 0.000000}, + {-0.336467, -0.336467, -0.879513}, + {0.369610, 0.369610, -0.852504}, + {0.555559, 0.831446, 0.000000}, + {0.290384, 0.434614, 0.852504}, + {-0.264351, -0.395642, 0.879513}, + {-0.555559, -0.831446, 0.000000}, + {-0.264351, -0.395642, -0.879513}, + {0.290384, 0.434614, -0.852504}, + {0.382672, 0.923856, 0.000000}, + {0.200018, 0.482894, 0.852504}, + {-0.182073, -0.439619, 0.879513}, + {-0.382672, -0.923856, 0.000000}, + {-0.182073, -0.439619, -0.879513}, + {0.200018, 0.482894, -0.852504}, + {0.195074, 0.980773, 0.000000}, + {0.101962, 0.512650, 0.852504}, + {-0.092807, -0.466689, 0.879513}, + {-0.195074, -0.980773, 0.000000}, + {-0.092807, -0.466689, -0.879513}, + {0.101962, 0.512650, -0.852504}, + {0.000000, 1.000000, 0.000000}, + {0.000000, 0.522691, 0.852504}, + {0.000000, -0.475845, 0.879513}, + {0.000000, -1.000000, 0.000000}, + {0.000000, -0.475845, -0.879513}, + {0.000000, 0.522691, -0.852504}, + {-0.195074, 0.980773, 0.000000}, + {-0.101962, 0.512650, 0.852504}, + {0.092807, -0.466689, 0.879513}, + {0.195074, -0.980773, 0.000000}, + {0.092807, -0.466689, -0.879513}, + {-0.101962, 0.512650, -0.852504}, + {-0.382672, 0.923856, 0.000000}, + {-0.200018, 0.482894, 0.852504}, + {0.182073, -0.439619, 0.879513}, + {0.382672, -0.923856, 0.000000}, + {0.182073, -0.439619, -0.879513}, + {-0.200018, 0.482894, -0.852504}, + {-0.555559, 0.831446, 0.000000}, + {-0.290384, 0.434614, 0.852504}, + {0.264351, -0.395642, 0.879513}, + {0.555559, -0.831446, 0.000000}, + {0.264351, -0.395642, -0.879513}, + {-0.290384, 0.434614, -0.852504}, + {-0.707083, 0.707083, 0.000000}, + {-0.369610, 0.369610, 0.852504}, + {0.336467, -0.336467, 0.879513}, + {0.707083, -0.707083, 0.000000}, + {0.336467, -0.336467, -0.879513}, + {-0.369610, 0.369610, -0.852504}, + {-0.831446, 0.555559, 0.000000}, + {-0.434614, 0.290384, 0.852504}, + {0.395642, -0.264351, 0.879513}, + {0.831446, -0.555559, 0.000000}, + {0.395642, -0.264351, -0.879513}, + {-0.434614, 0.290384, -0.852504}, + {-0.923856, 0.382672, 0.000000}, + {-0.482894, 0.200018, 0.852504}, + {0.439619, -0.182073, 0.879513}, + {0.923856, -0.382672, 0.000000}, + {0.439619, -0.182073, -0.879513}, + {-0.482894, 0.200018, -0.852504}, + {-0.980773, 0.195074, 0.000000}, + {-0.512650, 0.101962, 0.852504}, + {0.466689, -0.092807, 0.879513}, + {0.980773, -0.195074, 0.000000}, + {0.466689, -0.092807, -0.879513}, + {-0.512650, 0.101962, -0.852504}, + {-1.000000, 0.000000, 0.000000}, + {-0.522691, 0.000000, 0.852504}, + {0.475845, 0.000000, 0.879513}, + {1.000000, 0.000000, 0.000000}, + {0.475845, 0.000000, -0.879513}, + {-0.522691, 0.000000, -0.852504}, + {-0.980773, -0.195074, 0.000000}, + {-0.512650, -0.101962, 0.852504}, + {0.466689, 0.092807, 0.879513}, + {0.980773, 0.195074, 0.000000}, + {0.466689, 0.092807, -0.879513}, + {-0.512650, -0.101962, -0.852504}, + {-0.923856, -0.382672, 0.000000}, + {-0.482894, -0.200018, 0.852504}, + {0.439619, 0.182073, 0.879513}, + {0.923856, 0.382672, 0.000000}, + {0.439619, 0.182073, -0.879513}, + {-0.482894, -0.200018, -0.852504}, + {-0.831446, -0.555559, 0.000000}, + {-0.434614, -0.290384, 0.852504}, + {0.395642, 0.264351, 0.879513}, + {0.831446, 0.555559, 0.000000}, + {0.395642, 0.264351, -0.879513}, + {-0.434614, -0.290384, -0.852504}, + {-0.707083, -0.707083, 0.000000}, + {-0.369610, -0.369610, 0.852504}, + {0.336467, 0.336467, 0.879513}, + {0.707083, 0.707083, 0.000000}, + {0.336467, 0.336467, -0.879513}, + {-0.369610, -0.369610, -0.852504}, + {-0.555559, -0.831446, 0.000000}, + {-0.290384, -0.434614, 0.852504}, + {0.264351, 0.395642, 0.879513}, + {0.555559, 0.831446, 0.000000}, + {0.264351, 0.395642, -0.879513}, + {-0.290384, -0.434614, -0.852504}, + {-0.382672, -0.923856, 0.000000}, + {-0.200018, -0.482894, 0.852504}, + {0.182073, 0.439619, 0.879513}, + {0.382672, 0.923856, 0.000000}, + {0.182073, 0.439619, -0.879513}, + {-0.200018, -0.482894, -0.852504}, + {-0.195074, -0.980773, 0.000000}, + {-0.101962, -0.512650, 0.852504}, + {0.092807, 0.466689, 0.879513}, + {0.195074, 0.980773, 0.000000}, + {0.092807, 0.466689, -0.879513}, + {-0.101962, -0.512650, -0.852504}, + {0.000000, -1.000000, 0.000000}, + {0.000000, -0.522691, 0.852504}, + {0.000000, 0.475845, 0.879513}, + {0.000000, 1.000000, 0.000000}, + {0.000000, 0.475845, -0.879513}, + {0.000000, -0.522691, -0.852504}, + {0.195074, -0.980773, 0.000000}, + {0.101962, -0.512650, 0.852504}, + {-0.092807, 0.466689, 0.879513}, + {-0.195074, 0.980773, 0.000000}, + {-0.092807, 0.466689, -0.879513}, + {0.101962, -0.512650, -0.852504}, + {0.382672, -0.923856, 0.000000}, + {0.200018, -0.482894, 0.852504}, + {-0.182073, 0.439619, 0.879513}, + {-0.382672, 0.923856, 0.000000}, + {-0.182073, 0.439619, -0.879513}, + {0.200018, -0.482894, -0.852504}, + {0.555559, -0.831446, 0.000000}, + {0.290384, -0.434614, 0.852504}, + {-0.264351, 0.395642, 0.879513}, + {-0.555559, 0.831446, 0.000000}, + {-0.264351, 0.395642, -0.879513}, + {0.290384, -0.434614, -0.852504}, + {0.707083, -0.707083, 0.000000}, + {0.369610, -0.369610, 0.852504}, + {-0.336467, 0.336467, 0.879513}, + {-0.707083, 0.707083, 0.000000}, + {-0.336467, 0.336467, -0.879513}, + {0.369610, -0.369610, -0.852504}, + {0.831446, -0.555559, 0.000000}, + {0.434614, -0.290384, 0.852504}, + {-0.395642, 0.264351, 0.879513}, + {-0.831446, 0.555559, 0.000000}, + {-0.395642, 0.264351, -0.879513}, + {0.434614, -0.290384, -0.852504}, + {0.923856, -0.382672, 0.000000}, + {0.482894, -0.200018, 0.852504}, + {-0.439619, 0.182073, 0.879513}, + {-0.923856, 0.382672, 0.000000}, + {-0.439619, 0.182073, -0.879513}, + {0.482894, -0.200018, -0.852504}, + {0.980773, -0.195074, 0.000000}, + {0.512650, -0.101962, 0.852504}, + {-0.466689, 0.092807, 0.879513}, + {-0.980773, 0.195074, 0.000000}, + {-0.466689, 0.092807, -0.879513}, + {0.512650, -0.101962, -0.852504}, +}; + +unsigned short _WIDGET_indices_dial[] = { + 6, 7, 1, + 7, 8, 2, + 8, 9, 3, + 9, 10, 4, + 10, 11, 5, + 5, 11, 6, + 12, 13, 7, + 13, 14, 8, + 14, 15, 9, + 15, 16, 10, + 16, 17, 11, + 11, 17, 12, + 18, 19, 13, + 13, 19, 20, + 20, 21, 15, + 15, 21, 22, + 22, 23, 17, + 17, 23, 18, + 24, 25, 19, + 19, 25, 26, + 26, 27, 21, + 21, 27, 28, + 28, 29, 23, + 23, 29, 24, + 30, 31, 25, + 25, 31, 32, + 26, 32, 33, + 27, 33, 34, + 34, 35, 29, + 29, 35, 30, + 36, 37, 31, + 31, 37, 38, + 38, 39, 33, + 39, 40, 34, + 40, 41, 35, + 35, 41, 36, + 36, 42, 43, + 43, 44, 38, + 44, 45, 39, + 45, 46, 40, + 46, 47, 41, + 47, 42, 36, + 48, 49, 43, + 49, 50, 44, + 50, 51, 45, + 51, 52, 46, + 52, 53, 47, + 47, 53, 48, + 54, 55, 49, + 49, 55, 56, + 50, 56, 57, + 57, 58, 52, + 58, 59, 53, + 53, 59, 54, + 60, 61, 55, + 55, 61, 62, + 56, 62, 63, + 63, 64, 58, + 64, 65, 59, + 59, 65, 60, + 66, 67, 61, + 61, 67, 68, + 68, 69, 63, + 69, 70, 64, + 70, 71, 65, + 71, 66, 60, + 72, 73, 67, + 73, 74, 68, + 68, 74, 75, + 75, 76, 70, + 76, 77, 71, + 71, 77, 72, + 78, 79, 73, + 79, 80, 74, + 74, 80, 81, + 81, 82, 76, + 82, 83, 77, + 83, 78, 72, + 78, 84, 85, + 85, 86, 80, + 80, 86, 87, + 87, 88, 82, + 82, 88, 89, + 89, 84, 78, + 90, 91, 85, + 91, 92, 86, + 86, 92, 93, + 93, 94, 88, + 88, 94, 95, + 95, 90, 84, + 96, 97, 91, + 97, 98, 92, + 98, 99, 93, + 99, 100, 94, + 100, 101, 95, + 101, 96, 90, + 102, 103, 97, + 103, 104, 98, + 104, 105, 99, + 99, 105, 106, + 106, 107, 101, + 101, 107, 102, + 108, 109, 103, + 103, 109, 110, + 110, 111, 105, + 105, 111, 112, + 112, 113, 107, + 107, 113, 108, + 114, 115, 109, + 115, 116, 110, + 116, 117, 111, + 111, 117, 118, + 112, 118, 119, + 113, 119, 114, + 114, 120, 121, + 121, 122, 116, + 122, 123, 117, + 117, 123, 124, + 124, 125, 119, + 125, 120, 114, + 126, 127, 121, + 121, 127, 128, + 128, 129, 123, + 123, 129, 130, + 130, 131, 125, + 125, 131, 126, + 132, 133, 127, + 133, 134, 128, + 128, 134, 135, + 135, 136, 130, + 136, 137, 131, + 131, 137, 132, + 132, 138, 139, + 133, 139, 140, + 134, 140, 141, + 141, 142, 136, + 142, 143, 137, + 143, 138, 132, + 138, 144, 145, + 139, 145, 146, + 146, 147, 141, + 141, 147, 148, + 148, 149, 143, + 149, 144, 138, + 144, 150, 151, + 151, 152, 146, + 146, 152, 153, + 153, 154, 148, + 154, 155, 149, + 155, 150, 144, + 156, 157, 151, + 151, 157, 158, + 158, 159, 153, + 159, 160, 154, + 160, 161, 155, + 155, 161, 156, + 156, 162, 163, + 163, 164, 158, + 158, 164, 165, + 165, 166, 160, + 160, 166, 167, + 167, 162, 156, + 162, 168, 169, + 169, 170, 164, + 164, 170, 171, + 165, 171, 172, + 166, 172, 173, + 173, 168, 162, + 174, 175, 169, + 175, 176, 170, + 170, 176, 177, + 177, 178, 172, + 172, 178, 179, + 173, 179, 174, + 174, 180, 181, + 181, 182, 176, + 176, 182, 183, + 183, 184, 178, + 178, 184, 185, + 179, 185, 180, + 186, 187, 181, + 187, 188, 182, + 188, 189, 183, + 183, 189, 190, + 190, 191, 185, + 191, 186, 180, + 0, 1, 187, + 1, 2, 188, + 2, 3, 189, + 3, 4, 190, + 190, 4, 5, + 191, 5, 0, + 0, 6, 1, + 1, 7, 2, + 2, 8, 3, + 3, 9, 4, + 4, 10, 5, + 0, 5, 6, + 6, 12, 7, + 7, 13, 8, + 8, 14, 9, + 9, 15, 10, + 10, 16, 11, + 6, 11, 12, + 12, 18, 13, + 14, 13, 20, + 14, 20, 15, + 16, 15, 22, + 16, 22, 17, + 12, 17, 18, + 18, 24, 19, + 20, 19, 26, + 20, 26, 21, + 22, 21, 28, + 22, 28, 23, + 18, 23, 24, + 24, 30, 25, + 26, 25, 32, + 27, 26, 33, + 28, 27, 34, + 28, 34, 29, + 24, 29, 30, + 30, 36, 31, + 32, 31, 38, + 32, 38, 33, + 33, 39, 34, + 34, 40, 35, + 30, 35, 36, + 37, 36, 43, + 37, 43, 38, + 38, 44, 39, + 39, 45, 40, + 40, 46, 41, + 41, 47, 36, + 42, 48, 43, + 43, 49, 44, + 44, 50, 45, + 45, 51, 46, + 46, 52, 47, + 42, 47, 48, + 48, 54, 49, + 50, 49, 56, + 51, 50, 57, + 51, 57, 52, + 52, 58, 53, + 48, 53, 54, + 54, 60, 55, + 56, 55, 62, + 57, 56, 63, + 57, 63, 58, + 58, 64, 59, + 54, 59, 60, + 60, 66, 61, + 62, 61, 68, + 62, 68, 63, + 63, 69, 64, + 64, 70, 65, + 65, 71, 60, + 66, 72, 67, + 67, 73, 68, + 69, 68, 75, + 69, 75, 70, + 70, 76, 71, + 66, 71, 72, + 72, 78, 73, + 73, 79, 74, + 75, 74, 81, + 75, 81, 76, + 76, 82, 77, + 77, 83, 72, + 79, 78, 85, + 79, 85, 80, + 81, 80, 87, + 81, 87, 82, + 83, 82, 89, + 83, 89, 78, + 84, 90, 85, + 85, 91, 86, + 87, 86, 93, + 87, 93, 88, + 89, 88, 95, + 89, 95, 84, + 90, 96, 91, + 91, 97, 92, + 92, 98, 93, + 93, 99, 94, + 94, 100, 95, + 95, 101, 90, + 96, 102, 97, + 97, 103, 98, + 98, 104, 99, + 100, 99, 106, + 100, 106, 101, + 96, 101, 102, + 102, 108, 103, + 104, 103, 110, + 104, 110, 105, + 106, 105, 112, + 106, 112, 107, + 102, 107, 108, + 108, 114, 109, + 109, 115, 110, + 110, 116, 111, + 112, 111, 118, + 113, 112, 119, + 108, 113, 114, + 115, 114, 121, + 115, 121, 116, + 116, 122, 117, + 118, 117, 124, + 118, 124, 119, + 119, 125, 114, + 120, 126, 121, + 122, 121, 128, + 122, 128, 123, + 124, 123, 130, + 124, 130, 125, + 120, 125, 126, + 126, 132, 127, + 127, 133, 128, + 129, 128, 135, + 129, 135, 130, + 130, 136, 131, + 126, 131, 132, + 133, 132, 139, + 134, 133, 140, + 135, 134, 141, + 135, 141, 136, + 136, 142, 137, + 137, 143, 132, + 139, 138, 145, + 140, 139, 146, + 140, 146, 141, + 142, 141, 148, + 142, 148, 143, + 143, 149, 138, + 145, 144, 151, + 145, 151, 146, + 147, 146, 153, + 147, 153, 148, + 148, 154, 149, + 149, 155, 144, + 150, 156, 151, + 152, 151, 158, + 152, 158, 153, + 153, 159, 154, + 154, 160, 155, + 150, 155, 156, + 157, 156, 163, + 157, 163, 158, + 159, 158, 165, + 159, 165, 160, + 161, 160, 167, + 161, 167, 156, + 163, 162, 169, + 163, 169, 164, + 165, 164, 171, + 166, 165, 172, + 167, 166, 173, + 167, 173, 162, + 168, 174, 169, + 169, 175, 170, + 171, 170, 177, + 171, 177, 172, + 173, 172, 179, + 168, 173, 174, + 175, 174, 181, + 175, 181, 176, + 177, 176, 183, + 177, 183, 178, + 179, 178, 185, + 174, 179, 180, + 180, 186, 181, + 181, 187, 182, + 182, 188, 183, + 184, 183, 190, + 184, 190, 185, + 185, 191, 180, + 186, 0, 187, + 187, 1, 188, + 188, 2, 189, + 189, 3, 190, + 191, 190, 5, + 186, 191, 0, +}; diff --git a/source/blender/windowmanager/3d_widgets/ui_widget_library.h b/source/blender/windowmanager/3d_widgets/ui_widget_library.h new file mode 100644 index 00000000000..6bf7ed6b659 --- /dev/null +++ b/source/blender/windowmanager/3d_widgets/ui_widget_library.h @@ -0,0 +1,16 @@ + +/* arrow widget */ +extern int _WIDGET_nverts_arrow; +extern int _WIDGET_ntris_arrow; + +extern float _WIDGET_verts_arrow[][3]; +extern float _WIDGET_normals_arrow[][3]; +extern unsigned short _WIDGET_indices_arrow[]; + +/* dial widget */ +extern int _WIDGET_nverts_dial; +extern int _WIDGET_ntris_dial; + +extern float _WIDGET_verts_dial[][3]; +extern float _WIDGET_normals_dial[][3]; +extern unsigned short _WIDGET_indices_dial[]; diff --git a/source/blender/windowmanager/CMakeLists.txt b/source/blender/windowmanager/CMakeLists.txt index 86f5fff4aef..d5e360de3d1 100644 --- a/source/blender/windowmanager/CMakeLists.txt +++ b/source/blender/windowmanager/CMakeLists.txt @@ -65,6 +65,10 @@ set(SRC intern/wm_subwindow.c intern/wm_window.c intern/wm_stereo.c + intern/wm_widgets.c + intern/wm_generic_widgets.c + 3d_widgets/arrow_widget.c + 3d_widgets/dial_widget.c WM_api.h WM_keymap.h @@ -77,8 +81,16 @@ set(SRC wm_files.h wm_subwindow.h wm_window.h + 3d_widgets/ui_widget_library.h ) +if(WITH_AUDASPACE) + list(APPEND INC + ../../../intern/audaspace/intern + ) + add_definitions(-DWITH_AUDASPACE) +endif() + add_definitions(${GL_DEFINITIONS}) if(WITH_INTERNATIONAL) diff --git a/source/blender/windowmanager/SConscript b/source/blender/windowmanager/SConscript index a6f64f7cdae..fc9690c9076 100644 --- a/source/blender/windowmanager/SConscript +++ b/source/blender/windowmanager/SConscript @@ -29,12 +29,14 @@ Import ('env') import os sources = env.Glob('intern/*.c') +sources += env.Glob('3d_widgets/*.c') incs = [ '.', '#/intern/ghost', '#/intern/guardedalloc', '#/intern/memutil', + '#/intern/audaspace/intern', '#/source/gameengine/BlenderRoutines', env['BF_GLEW_INC'], '#/intern/glew-mx', @@ -51,6 +53,7 @@ incs = [ '../nodes', '../python', '../render/extern/include', + '3d_widgets', env['BF_ZLIB_INC'], ] incs = ' '.join(incs) diff --git a/source/blender/windowmanager/WM_api.h b/source/blender/windowmanager/WM_api.h index baf36f3d5ce..cafd2c0f625 100644 --- a/source/blender/windowmanager/WM_api.h +++ b/source/blender/windowmanager/WM_api.h @@ -39,6 +39,7 @@ /* dna-savable wmStructs here */ #include "DNA_windowmanager_types.h" +#include "DNA_listBase.h" #include "WM_keymap.h" #include "BLI_compiler_attrs.h" @@ -55,6 +56,11 @@ struct wmGesture; struct wmJob; struct wmOperatorType; struct wmOperator; +struct wmWidget; +struct wmWidgetGroup; +struct wmWidgetMap; +struct wmWidgetGroupType; +struct wmWidgetMapType; struct rcti; struct PointerRNA; struct PropertyRNA; @@ -65,6 +71,8 @@ struct ImBuf; struct ImageFormatData; struct ARegion; struct wmNDOFMotionData; +struct Main; +struct Object; typedef struct wmJob wmJob; @@ -301,11 +309,11 @@ bool WM_operator_last_properties_store(struct wmOperator *op); /* flags for WM_operator_properties_filesel */ #define WM_FILESEL_RELPATH (1 << 0) -#define WM_FILESEL_DIRECTORY (1 << 1) -#define WM_FILESEL_FILENAME (1 << 2) -#define WM_FILESEL_FILEPATH (1 << 3) -#define WM_FILESEL_FILES (1 << 4) - +#define WM_FILESEL_DIRECTORY (1 << 1) +#define WM_FILESEL_FILENAME (1 << 2) +#define WM_FILESEL_FILEPATH (1 << 3) +#define WM_FILESEL_FILES (1 << 4) +#define WM_FILESEL_IMAGE_COLLAPSE (1 << 5) /* operator as a python command (resultuing string must be freed) */ char *WM_operator_pystring_ex(struct bContext *C, struct wmOperator *op, @@ -410,6 +418,8 @@ enum { WM_JOB_TYPE_OBJECT_SIM_FLUID, WM_JOB_TYPE_OBJECT_BAKE_TEXTURE, WM_JOB_TYPE_OBJECT_BAKE, + WM_JOB_TYPE_PTCACHE_EXPORT, + WM_JOB_TYPE_CACHELIBRARY_BAKE, WM_JOB_TYPE_FILESEL_THUMBNAIL, WM_JOB_TYPE_CLIP_BUILD_PROXY, WM_JOB_TYPE_CLIP_TRACK_MARKERS, @@ -481,6 +491,90 @@ void WM_event_ndof_to_quat(const struct wmNDOFMotionData *ndof, float q[4 float WM_event_tablet_data(const struct wmEvent *event, int *pen_flip, float tilt[2]); bool WM_event_is_tablet(const struct wmEvent *event); +/* widget API */ +struct wmWidget *WM_widget_new(void (*draw)(struct wmWidget *, const struct bContext *), + void (*render_3d_intersection)(const struct bContext *, struct wmWidget *, int), + int (*intersect)(struct bContext *C, const struct wmEvent *event, struct wmWidget *customdata), + int (*handler)(struct bContext *, const struct wmEvent *, struct wmWidget *)); + +void WM_widget_property(struct wmWidget *, int slot, struct PointerRNA *ptr, const char *propname); +struct PointerRNA *WM_widget_operator(struct wmWidget *, const char *opname); +void WM_widgets_update(const struct bContext *C, struct wmWidgetMap *wmap); +void WM_widgets_draw(const struct bContext *C, struct wmWidgetMap *wmap, bool in_scene); +void WM_event_add_area_widgetmap_handlers(struct ARegion *ar); +void WM_modal_handler_attach_widgetgroup(struct bContext *C, struct wmEventHandler *handler, struct wmWidgetGroupType *wgrouptype, struct wmOperator *op); + +void WM_widget_set_origin(struct wmWidget *widget, float origin[3]); +void WM_widget_set_3d_scale(struct wmWidget *widget, bool scale); +void WM_widget_set_draw_on_hover_only(struct wmWidget *widget, bool draw); +void WM_widget_set_scene_depth(struct wmWidget *widget, bool scene); +void WM_widget_set_scale(struct wmWidget *widget, float scale); + +struct wmWidgetMapType *WM_widgetmaptype_find(const char *idname, int spaceid, int regionid, bool is_3d, bool create); +struct wmWidgetGroupType *WM_widgetgrouptype_new(int (*poll)(const struct bContext *, struct wmWidgetGroupType *), + void (*draw)(const struct bContext *, struct wmWidgetGroup *), + struct Main *bmain, const char *mapidname, short spaceid, short regionid, bool is_3d); +void WM_widgetgrouptype_unregister(struct bContext *C, struct Main *bmain, struct wmWidgetGroupType *wgroup); + +/* creates a widgetmap with all registered widgets for that type */ +struct wmWidgetMap *WM_widgetmap_from_type(const char *idname, int spaceid, int regionid, bool is_3d); +void WM_widgetmap_delete(struct wmWidgetMap *wmap); +bool WM_widgetmap_cursor_set(struct wmWidgetMap *wmap, struct wmWindow *win); + +void WM_widgetmaptypes_free(void); + +/* wm_generic_widgets.c */ + +enum { + WIDGET_ARROW_STYLE_NORMAL = 1, + WIDGET_ARROW_STYLE_NO_AXIS = (1 << 1), + WIDGET_ARROW_STYLE_CROSS = (1 << 2), + WIDGET_ARROW_STYLE_INVERTED = (1 << 3), /* inverted offset during interaction - if set it also sets constrained below */ + WIDGET_ARROW_STYLE_CONSTRAINED = (1 << 4), /* clamp arrow interaction to property width */ +}; + +enum { + WIDGET_DIAL_STYLE_RING = 0, + WIDGET_DIAL_STYLE_RING_CLIPPED = 1, +}; + +enum { + WIDGET_RECT_TRANSFORM_STYLE_TRANSLATE = 1, /* widget translates */ + WIDGET_RECT_TRANSFORM_STYLE_ROTATE = (1 << 1), /* widget rotates */ + WIDGET_RECT_TRANSFORM_STYLE_SCALE = (1 << 2), /* widget scales */ + WIDGET_RECT_TRANSFORM_STYLE_SCALE_UNIFORM = (1 << 3), /* widget scales uniformly */ +}; + +/* slots for properties */ +enum { + ARROW_SLOT_OFFSET_WORLD_SPACE = 0 +}; + +enum { + RECT_TRANSFORM_SLOT_OFFSET = 0, + RECT_TRANSFORM_SLOT_SCALE = 1 +}; + +enum { + FACEMAP_SLOT_FACEMAP = 0, +}; + +struct wmWidget *WIDGET_arrow_new(struct wmWidgetGroup *wgroup, int style); +void WIDGET_arrow_set_color(struct wmWidget *widget, float color[4]); +void WIDGET_arrow_set_direction(struct wmWidget *widget, float direction[3]); +void WIDGET_arrow_set_up_vector(struct wmWidget *widget, float direction[3]); +void WIDGET_arrow_set_scale(struct wmWidget *widget, float scale); + +struct wmWidget *WIDGET_dial_new(int style); +void WIDGET_dial_set_color(struct wmWidget *widget, float color[4]); +void WIDGET_dial_set_direction(struct wmWidget *widget, float direction[3]); + +struct wmWidget *WIDGET_rect_transform_new(struct wmWidgetGroup *wgroup, int style, float width, float height); +void WIDGET_rect_transform_set_offset(struct wmWidget *widget, float offset[2]); + +struct wmWidget *WIDGET_facemap_new(struct wmWidgetGroup *wgroup, int style, struct Object *ob, int facemap); +void WIDGET_facemap_set_color(struct wmWidget *widget, float color[4]); + #ifdef WITH_INPUT_IME bool WM_event_is_ime_switch(const struct wmEvent *event); #endif diff --git a/source/blender/windowmanager/WM_types.h b/source/blender/windowmanager/WM_types.h index 109ccc27d79..3a984fdc4d2 100644 --- a/source/blender/windowmanager/WM_types.h +++ b/source/blender/windowmanager/WM_types.h @@ -377,6 +377,7 @@ typedef struct wmNotifier { #define NS_EDITMODE_ARMATURE (8<<8) #define NS_MODE_POSE (9<<8) #define NS_MODE_PARTICLE (10<<8) +#define NS_MODE_HAIR (11<<8) /* subtype 3d view editing */ #define NS_VIEW3D_GPU (16<<8) @@ -669,6 +670,59 @@ typedef struct wmDropBox { } wmDropBox; + +/* WidgetGroups store and manage groups of widgets. + * They are responsible for drawing necessary widgets and updating their state and position. + * Also they */ +typedef struct wmWidget wmWidget; +typedef struct wmWidgetGroup wmWidgetGroup; +typedef struct wmWidgetMapType wmWidgetMapType; + +/* factory class for a widgetgroup type, gets called every time a new area is spawned */ +typedef struct wmWidgetGroupType { + struct wmWidgetGroupType *next, *prev; + + char idname[64]; + + /* poll if widgetmap should be active */ + int (*poll)(const struct bContext *C, struct wmWidgetGroupType *wgrouptype) ATTR_WARN_UNUSED_RESULT; + + /* update widgets, called right before drawing */ + void (*draw)(const struct bContext *C, struct wmWidgetGroup *wgroup); + + /* rna for properties */ + struct StructRNA *srna; + + /* RNA integration */ + ExtensionRNA ext; + + /* general flag */ + int flag; + + /* if type is spawned from operator this is set here */ + void *op; + + /* same as widgetmaps, so registering/unregistering goes to the correct region */ + short spaceid, regionid; + char mapidname[64]; + bool is_3d; +} wmWidgetGroupType; + +typedef struct wmWidgetMap { + struct wmWidgetMap *next, *prev; + + struct wmWidgetMapType *type; + ListBase widgetgroups; + + /* highlighted widget for this map. We redraw the widgetmap when this changes */ + struct wmWidget *highlighted_widget; + /* active widget for this map. User has clicked currently this widget and it gets all input */ + struct wmWidget *active_widget; + + /* active group is overriding all other widgets while active */ + struct wmWidgetGroup *activegroup; +} wmWidgetMap; + /* *************** migrated stuff, clean later? ************** */ typedef struct RecentFile { diff --git a/source/blender/windowmanager/intern/wm.c b/source/blender/windowmanager/intern/wm.c index c8ff6dac754..b43425ef6cb 100644 --- a/source/blender/windowmanager/intern/wm.c +++ b/source/blender/windowmanager/intern/wm.c @@ -459,7 +459,7 @@ void wm_close_and_free(bContext *C, wmWindowManager *wm) BLI_freelistN(&wm->paintcursors); WM_drag_free_list(&wm->drags); - + wm_reports_free(wm); if (C && CTX_wm_manager(C) == wm) CTX_wm_manager_set(C, NULL); diff --git a/source/blender/windowmanager/intern/wm_event_system.c b/source/blender/windowmanager/intern/wm_event_system.c index 889a36b6953..15d0a74097e 100644 --- a/source/blender/windowmanager/intern/wm_event_system.c +++ b/source/blender/windowmanager/intern/wm_event_system.c @@ -1447,6 +1447,40 @@ static void wm_handler_op_context(bContext *C, wmEventHandler *handler, const wm } } +static void wm_handler_widgetmap_context(bContext *C, wmEventHandler *handler) +{ + bScreen *screen = CTX_wm_screen(C); + + if (screen) { + if (handler->op_area == NULL) { + /* do nothing in this context */ + } + else { + ScrArea *sa; + + for (sa = screen->areabase.first; sa; sa = sa->next) + if (sa == handler->op_area) + break; + if (sa == NULL) { + /* when changing screen layouts with running modal handlers (like render display), this + * is not an error to print */ + if (handler->widgetmap == NULL) + printf("internal error: modal widgetmap handler has invalid area\n"); + } + else { + ARegion *ar; + CTX_wm_area_set(C, sa); + for (ar = sa->regionbase.first; ar; ar = ar->next) + if (ar == handler->op_region) + break; + /* XXX no warning print here, after full-area and back regions are remade */ + if (ar) + CTX_wm_region_set(C, ar); + } + } + } +} + /* called on exit or remove area, only here call cancel callback */ void WM_event_remove_handlers(bContext *C, ListBase *handlers) { @@ -1470,7 +1504,7 @@ void WM_event_remove_handlers(bContext *C, ListBase *handlers) if (handler->op->type->flag & OPTYPE_UNDO) wm->op_undo_depth--; - + CTX_wm_area_set(C, area); CTX_wm_region_set(C, region); } @@ -1657,8 +1691,8 @@ static int wm_handler_operator_call(bContext *C, ListBase *handlers, wmEventHand /* warning, after this call all context data and 'event' may be freed. see check below */ retval = ot->modal(C, op, event); + OPERATOR_RETVAL_CHECK(retval); - /* when this is _not_ the case the modal modifier may have loaded * a new blend file (demo mode does this), so we have to assume * the event, operator etc have all been freed. - campbell */ @@ -2052,7 +2086,72 @@ static int wm_handlers_do_intern(bContext *C, wmEvent *event, ListBase *handlers } } } + else if (handler->widgetmap) { + struct wmWidgetMap *wmap = handler->widgetmap; + unsigned char part; + short event_processed = 0; + wmWidget *widget = wm_widgetmap_get_active_widget(wmap); + ScrArea *area = CTX_wm_area(C); + ARegion *region = CTX_wm_region(C); + + wm_handler_widgetmap_context(C, handler); + wm_region_mouse_co(C, event); + + switch (event->type) { + case MOUSEMOVE: + if (widget) { + widget->handler(C, event, widget); + event_processed = EVT_WIDGET_UPDATE; + action |= WM_HANDLER_BREAK; + } + else if (wm_widgetmap_is_3d(wmap)) { + widget = wm_widget_find_highlighted_3D(wmap, C, event, &part); + wm_widgetmap_set_highlighted_widget(wmap, C, widget, part); + } + else { + widget = wm_widget_find_highlighted(wmap, C, event, &part); + wm_widgetmap_set_highlighted_widget(wmap, C, widget, part); + } + break; + + case LEFTMOUSE: + { + if (widget) { + if (event->val == KM_RELEASE) { + wm_widgetmap_set_active_widget(wmap, C, event, NULL, false); + event_processed = EVT_WIDGET_RELEASED; + action |= WM_HANDLER_BREAK; + } + else { + action |= WM_HANDLER_BREAK; + } + } + else if (event->val == KM_PRESS) { + widget = wm_widgetmap_get_highlighted_widget(wmap); + + if (widget) { + wm_widgetmap_set_active_widget(wmap, C, event, widget, handler->op == NULL); + action |= WM_HANDLER_BREAK; + } + } + break; + } + } + + /* restore the area */ + CTX_wm_area_set(C, area); + CTX_wm_region_set(C, region); + + if (handler->op) { + /* if event was processed by an active widget pass the modified event to the operator */ + if (event_processed) { + event->type = event_processed; + } + action |= wm_handler_operator_call(C, handlers, handler, event, NULL); + } + } else { + /* handle the widget first, before passing the event down */ /* modal, swallows all */ action |= wm_handler_operator_call(C, handlers, handler, event, NULL); } @@ -2605,7 +2704,7 @@ wmEventHandler *WM_event_add_modal_handler(bContext *C, wmOperator *op) { wmEventHandler *handler = MEM_callocN(sizeof(wmEventHandler), "event modal handler"); wmWindow *win = CTX_wm_window(C); - + /* operator was part of macro */ if (op->opm) { /* give the mother macro to the handler */ @@ -3570,6 +3669,7 @@ void WM_event_ndof_to_quat(const struct wmNDOFMotionData *ndof, float q[4]) angle = WM_event_ndof_to_axis_angle(ndof, axis); axis_angle_to_quat(q, axis, angle); } +/** \} */ /* if this is a tablet event, return tablet pressure and set *pen_flip * to 1 if the eraser tool is being used, 0 otherwise */ diff --git a/source/blender/windowmanager/intern/wm_generic_widgets.c b/source/blender/windowmanager/intern/wm_generic_widgets.c new file mode 100644 index 00000000000..247bd6a601b --- /dev/null +++ b/source/blender/windowmanager/intern/wm_generic_widgets.c @@ -0,0 +1,1109 @@ +/* + * ***** 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2009 Blender Foundation. + * All rights reserved. + * + * Contributor(s): Blender Foundation + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/editors/interface/interface_generic_widgets.c + * \ingroup edinterface + */ + +#include "RNA_types.h" +#include "RNA_access.h" + +#include "DNA_scene_types.h" +#include "DNA_screen_types.h" +#include "DNA_object_types.h" +#include "DNA_view3d_types.h" +#include "DNA_windowmanager_types.h" +#include "DNA_userdef_types.h" +#include "DNA_widget_types.h" + +#include "BLI_utildefines.h" +#include "BLI_math_matrix.h" +#include "BLI_math.h" +#include "BLI_rect.h" + +#include "BKE_context.h" +#include "BKE_depsgraph.h" +#include "BKE_object.h" + +#include "ED_view3d.h" +#include "ED_screen.h" + +#include "WM_types.h" +#include "WM_api.h" + +#include "GL/glew.h" +#include "GPU_select.h" + +#include "BIF_glutil.h" + +#include "MEM_guardedalloc.h" + +#include "UI_interface.h" + +#include "3d_widgets/ui_widget_library.h" + +#include "wm.h" +#include "WM_types.h" + + +/****************************************************** + * GENERIC WIDGET LIBRARY * + ******************************************************/ + +typedef struct WidgetDrawInfo { + int nverts; + int ntris; + float (*verts)[3]; + float (*normals)[3]; + unsigned short *indices; + bool init; +} WidgetDrawInfo; + + +WidgetDrawInfo arraw_head_draw_info = {0}; +WidgetDrawInfo dial_draw_info = {0}; + +static void widget_draw_intern(WidgetDrawInfo *info, bool select) +{ + GLuint buf[3]; + + bool use_lighting = !select && ((U.tw_flag & V3D_SHADED_WIDGETS) != 0); + + if (use_lighting) + glGenBuffers(3, buf); + else + glGenBuffers(2, buf); + + glEnableClientState(GL_VERTEX_ARRAY); + glBindBuffer(GL_ARRAY_BUFFER, buf[0]); + glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 3 * info->nverts, info->verts, GL_STATIC_DRAW); + glVertexPointer(3, GL_FLOAT, 0, NULL); + + if (use_lighting) { + glEnableClientState(GL_NORMAL_ARRAY); + glBindBuffer(GL_ARRAY_BUFFER, buf[2]); + glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 3 * info->nverts, info->normals, GL_STATIC_DRAW); + glNormalPointer(GL_FLOAT, 0, NULL); + glShadeModel(GL_SMOOTH); + } + + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buf[1]); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(unsigned short) * (3 * info->ntris), info->indices, GL_STATIC_DRAW); + + glEnable(GL_CULL_FACE); + glEnable(GL_DEPTH_TEST); + + glDrawElements(GL_TRIANGLES, info->ntris * 3, GL_UNSIGNED_SHORT, NULL); + + glDisable(GL_DEPTH_TEST); + glDisable(GL_CULL_FACE); + + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + + glDisableClientState(GL_VERTEX_ARRAY); + + if (use_lighting) { + glDisableClientState(GL_NORMAL_ARRAY); + glShadeModel(GL_FLAT); + glDeleteBuffers(3, buf); + } + else { + glDeleteBuffers(2, buf); + } +} + +/********* Arrow widget ************/ + +#define ARROW_UP_VECTOR_SET 1 + +typedef struct ArrowWidget { + wmWidget widget; + int style; + int flag; + float direction[3]; + float up[3]; + float color[4]; + float offset; + /* property range and minimum for constrained arrows */ + float range, min; +} ArrowWidget; + +typedef struct ArrowInteraction { + float orig_origin[3]; + float orig_mouse[2]; + float orig_offset; + float orig_scale; + + /* direction vector, projected in screen space */ + float proj_direction[2]; +} ArrowInteraction; + + +static void widget_arrow_get_final_pos(struct wmWidget *widget, float pos[3]) +{ + ArrowWidget *arrow = (ArrowWidget *)widget; + + mul_v3_v3fl(pos, arrow->direction, arrow->offset); + add_v3_v3(pos, arrow->widget.origin); +} + +static void arrow_draw_geom(ArrowWidget *arrow, bool select) +{ + if (arrow->style & WIDGET_ARROW_STYLE_CROSS) { + glPushAttrib(GL_ENABLE_BIT); + glDisable(GL_LIGHTING); + glBegin(GL_LINES); + glVertex2f(-1.0, 0.f); + glVertex2f(1.0, 0.f); + glVertex2f(0.f, -1.0); + glVertex2f(0.f, 1.0); + glEnd(); + + glPopAttrib(); + } + else { + widget_draw_intern(&arraw_head_draw_info, select); + } +} + +static void arrow_draw_intern(ArrowWidget *arrow, bool select, bool highlight) +{ + float rot[3][3]; + float mat[4][4]; + float up[3] = {0.0f, 0.0f, 1.0f}; + float final_pos[3]; + + widget_arrow_get_final_pos(&arrow->widget, final_pos); + + if (arrow->flag & ARROW_UP_VECTOR_SET) { + copy_v3_v3(rot[2], arrow->direction); + copy_v3_v3(rot[1], arrow->up); + cross_v3_v3v3(rot[0], arrow->up, arrow->direction); + } + else { + rotation_between_vecs_to_mat3(rot, up, arrow->direction); + } + copy_m4_m3(mat, rot); + copy_v3_v3(mat[3], final_pos); + mul_mat3_m4_fl(mat, arrow->widget.scale); + + glPushMatrix(); + glMultMatrixf(&mat[0][0]); + + if (highlight && !(arrow->widget.flag & WM_WIDGET_DRAW_HOVER)) + glColor4f(1.0, 1.0, 0.0, 1.0); + else + glColor4fv(arrow->color); + + arrow_draw_geom(arrow, select); + + glPopMatrix(); + + if (arrow->widget.interaction_data) { + ArrowInteraction *data = arrow->widget.interaction_data; + + copy_m4_m3(mat, rot); + copy_v3_v3(mat[3], data->orig_origin); + mul_mat3_m4_fl(mat, data->orig_scale); + + glPushMatrix(); + glMultMatrixf(&mat[0][0]); + + glEnable(GL_BLEND); + glColor4f(0.5f, 0.5f, 0.5f, 0.5f); + arrow_draw_geom(arrow, select); + + glDisable(GL_BLEND); + + glPopMatrix(); + } +} + +static void widget_arrow_render_3d_intersect(const struct bContext *UNUSED(C), struct wmWidget *widget, int selectionbase) +{ + GPU_select_load_id(selectionbase); + arrow_draw_intern((ArrowWidget *)widget, true, false); +} + +static void widget_arrow_draw(struct wmWidget *widget, const struct bContext *UNUSED(C)) +{ + arrow_draw_intern((ArrowWidget *)widget, false, (widget->flag & WM_WIDGET_HIGHLIGHT) != 0); +} + +#define ARROW_RANGE 1.5f + +static int widget_arrow_handler(struct bContext *C, const struct wmEvent *event, struct wmWidget *widget) +{ + ArrowWidget *arrow = (ArrowWidget *)widget; + ArrowInteraction *data = widget->interaction_data; + ARegion *ar = CTX_wm_region(C); + RegionView3D *rv3d = ar->regiondata; + + float orig_origin[4]; + float viewvec[3], tangent[3], plane[3]; + float offset[4]; + float m_diff[2]; + float dir_2d[2], dir2d_final[2]; + float fac, zfac; + float facdir = 1.0f; + bool use_vertical = false; + + copy_v3_v3(orig_origin, data->orig_origin); + orig_origin[3] = 1.0f; + add_v3_v3v3(offset, orig_origin, arrow->direction); + offset[3] = 1.0f; + + /* calculate view vector */ + if (rv3d->is_persp) { + sub_v3_v3v3(viewvec, orig_origin, rv3d->viewinv[3]); + } + else { + copy_v3_v3(viewvec, rv3d->viewinv[2]); + } + normalize_v3(viewvec); + + zfac = ED_view3d_calc_zfac(rv3d, orig_origin, NULL); + + /* first determine if view vector is really close to the direction. If it is, we use vertical movement to determine offset, + * just like transform system does */ + if (RAD2DEG(acos(dot_v3v3(viewvec, arrow->direction))) > 5.0f) { + /* multiply to projection space */ + mul_m4_v4(rv3d->persmat, orig_origin); + mul_v4_fl(orig_origin, 1.0f/orig_origin[3]); + mul_m4_v4(rv3d->persmat, offset); + mul_v4_fl(offset, 1.0f/offset[3]); + + sub_v2_v2v2(dir_2d, offset, orig_origin); + dir_2d[0] *= ar->winx; + dir_2d[1] *= ar->winy; + normalize_v2(dir_2d); + } + else { + dir_2d[0] = 0.0f; + dir_2d[1] = 1.0f; + use_vertical = true; + } + + /* find mouse difference */ + m_diff[0] = event->mval[0] - data->orig_mouse[0]; + m_diff[1] = event->mval[1] - data->orig_mouse[1]; + + /* project the displacement on the screen space arrow direction */ + project_v2_v2v2(dir2d_final, m_diff, dir_2d); + + ED_view3d_win_to_delta(ar, dir2d_final, offset, zfac); + + add_v3_v3v3(orig_origin, offset, data->orig_origin); + + /* calculate view vector for the new position */ + if (rv3d->is_persp) { + sub_v3_v3v3(viewvec, orig_origin, rv3d->viewinv[3]); + } + else { + copy_v3_v3(viewvec, rv3d->viewinv[2]); + } + + normalize_v3(viewvec); + if (!use_vertical) { + /* now find a plane parallel to the view vector so we can intersect with the arrow direction */ + cross_v3_v3v3(tangent, viewvec, offset); + cross_v3_v3v3(plane, tangent, viewvec); + fac = dot_v3v3(plane, offset) / dot_v3v3(arrow->direction, plane); + + facdir = (fac < 0.0) ? -1.0 : 1.0; + mul_v3_v3fl(offset, arrow->direction, fac); + } + else { + facdir = (m_diff[1] < 0.0) ? -1.0 : 1.0; + } + + /* set the property for the operator and call its modal function */ + if (widget->props[ARROW_SLOT_OFFSET_WORLD_SPACE]) { + float value; + + value = data->orig_offset + facdir * len_v3(offset); + if (arrow->style & WIDGET_ARROW_STYLE_CONSTRAINED) { + if (arrow->style & WIDGET_ARROW_STYLE_INVERTED) + value = arrow->min + arrow->range - (value * arrow->range / ARROW_RANGE); + else + value = arrow->min + (value * arrow->range / ARROW_RANGE); + } + + RNA_property_float_set(&widget->ptr[ARROW_SLOT_OFFSET_WORLD_SPACE], widget->props[ARROW_SLOT_OFFSET_WORLD_SPACE], value); + RNA_property_update(C, &widget->ptr[ARROW_SLOT_OFFSET_WORLD_SPACE], widget->props[ARROW_SLOT_OFFSET_WORLD_SPACE]); + + /* accounts for clamping properly */ + if (arrow->style & WIDGET_ARROW_STYLE_CONSTRAINED) { + if (arrow->style & WIDGET_ARROW_STYLE_INVERTED) + arrow->offset = ARROW_RANGE * (arrow->min + arrow->range - (RNA_property_float_get(&widget->ptr[ARROW_SLOT_OFFSET_WORLD_SPACE], widget->props[ARROW_SLOT_OFFSET_WORLD_SPACE]))) / arrow->range; + else + arrow->offset = ARROW_RANGE * ((RNA_property_float_get(&widget->ptr[ARROW_SLOT_OFFSET_WORLD_SPACE], widget->props[ARROW_SLOT_OFFSET_WORLD_SPACE]) - arrow->min) / arrow->range); + } + else + arrow->offset = RNA_property_float_get(&widget->ptr[ARROW_SLOT_OFFSET_WORLD_SPACE], widget->props[ARROW_SLOT_OFFSET_WORLD_SPACE]); + } + else { + arrow->offset = facdir * len_v3(offset); + } + + /* tag the region for redraw */ + ED_region_tag_redraw(ar); + + return OPERATOR_PASS_THROUGH; +} + + +static int widget_arrow_invoke(struct bContext *UNUSED(C), const struct wmEvent *event, struct wmWidget *widget) +{ + ArrowWidget *arrow = (ArrowWidget *) widget; + ArrowInteraction *data = MEM_callocN(sizeof (ArrowInteraction), "arrow_interaction"); + + data->orig_offset = arrow->offset; + + data->orig_mouse[0] = event->mval[0]; + data->orig_mouse[1] = event->mval[1]; + + data->orig_scale = widget->scale; + + widget_arrow_get_final_pos(widget, data->orig_origin); + + widget->interaction_data = data; + + return OPERATOR_RUNNING_MODAL; +} + +static void widget_arrow_bind_to_prop(struct wmWidget *widget, int UNUSED(slot)) +{ + ArrowWidget *arrow = (ArrowWidget *) widget; + + if (widget->props[ARROW_SLOT_OFFSET_WORLD_SPACE]) { + if (arrow->style & WIDGET_ARROW_STYLE_CONSTRAINED) { + float min, max, step, precision; + + RNA_property_float_ui_range(&widget->ptr[ARROW_SLOT_OFFSET_WORLD_SPACE], widget->props[ARROW_SLOT_OFFSET_WORLD_SPACE], &min, &max, &step, &precision); + arrow->range = max - min; + arrow->min = min; + if (arrow->style & WIDGET_ARROW_STYLE_INVERTED) + arrow->offset = ARROW_RANGE * (max - (RNA_property_float_get(&widget->ptr[ARROW_SLOT_OFFSET_WORLD_SPACE], widget->props[ARROW_SLOT_OFFSET_WORLD_SPACE]))) / arrow->range; + else + arrow->offset = ARROW_RANGE * ((RNA_property_float_get(&widget->ptr[ARROW_SLOT_OFFSET_WORLD_SPACE], widget->props[ARROW_SLOT_OFFSET_WORLD_SPACE]) - arrow->min) / arrow->range); + } + else { + /* we'd need to check the property type here but for now assume always float */ + arrow->offset = RNA_property_float_get(&widget->ptr[ARROW_SLOT_OFFSET_WORLD_SPACE], widget->props[ARROW_SLOT_OFFSET_WORLD_SPACE]); + } + } + else + arrow->offset = 0.0f; +} + +wmWidget *WIDGET_arrow_new(wmWidgetGroup *wgroup, int style) +{ + float dir_default[3] = {0.0f, 0.0f, 1.0f}; + ArrowWidget *arrow; + + if (!arraw_head_draw_info.init) { + arraw_head_draw_info.nverts = _WIDGET_nverts_arrow, + arraw_head_draw_info.ntris = _WIDGET_ntris_arrow, + arraw_head_draw_info.verts = _WIDGET_verts_arrow, + arraw_head_draw_info.normals = _WIDGET_normals_arrow, + arraw_head_draw_info.indices = _WIDGET_indices_arrow, + arraw_head_draw_info.init = true; + } + + /* inverted only makes sense in a constrained arrow */ + if (style & WIDGET_ARROW_STYLE_INVERTED) + style |= WIDGET_ARROW_STYLE_CONSTRAINED; + + arrow = MEM_callocN(sizeof(ArrowWidget), "arrowwidget"); + + + arrow->widget.draw = widget_arrow_draw; + arrow->widget.get_final_position = widget_arrow_get_final_pos; + arrow->widget.intersect = NULL; + arrow->widget.handler = widget_arrow_handler; + arrow->widget.invoke = widget_arrow_invoke; + arrow->widget.render_3d_intersection = widget_arrow_render_3d_intersect; + arrow->widget.bind_to_prop = widget_arrow_bind_to_prop; + arrow->widget.flag |= WM_WIDGET_SCALE_3D; + arrow->style = style; + copy_v3_v3(arrow->direction, dir_default); + + wm_widget_register(wgroup, &arrow->widget); + + return (wmWidget *)arrow; +} + +void WIDGET_arrow_set_color(struct wmWidget *widget, float color[4]) +{ + ArrowWidget *arrow = (ArrowWidget *)widget; + + copy_v4_v4(arrow->color, color); +} + +void WIDGET_arrow_set_direction(struct wmWidget *widget, float direction[3]) +{ + ArrowWidget *arrow = (ArrowWidget *)widget; + + copy_v3_v3(arrow->direction, direction); + normalize_v3(arrow->direction); +} + +void WIDGET_arrow_set_up_vector(struct wmWidget *widget, float direction[3]) +{ + ArrowWidget *arrow = (ArrowWidget *)widget; + + if (direction) { + copy_v3_v3(arrow->up, direction); + normalize_v3(arrow->up); + arrow->flag |= ARROW_UP_VECTOR_SET; + } + else { + arrow->flag &= ~ARROW_UP_VECTOR_SET; + } +} + + +/********* Dial widget ************/ + +typedef struct DialWidget { + wmWidget widget; + int style; + float direction[3]; + float color[4]; +} DialWidget; + +static void dial_draw_intern(DialWidget *dial, bool select, bool highlight, float scale) +{ + float rot[3][3]; + float mat[4][4]; + float up[3] = {0.0f, 0.0f, 1.0f}; + + rotation_between_vecs_to_mat3(rot, up, dial->direction); + copy_m4_m3(mat, rot); + copy_v3_v3(mat[3], dial->widget.origin); + mul_mat3_m4_fl(mat, scale); + + glPushMatrix(); + glMultMatrixf(&mat[0][0]); + + if (highlight) + glColor4f(1.0, 1.0, 0.0, 1.0); + else + glColor4fv(dial->color); + + widget_draw_intern(&dial_draw_info, select); + + glPopMatrix(); + +} + +static void widget_dial_render_3d_intersect(const struct bContext *C, struct wmWidget *widget, int selectionbase) +{ + DialWidget *dial = (DialWidget *)widget; + ARegion *ar = CTX_wm_region(C); + RegionView3D *rv3d = ar->regiondata; + + /* enable clipping if needed */ + if (dial->style == WIDGET_DIAL_STYLE_RING_CLIPPED) { + double plane[4]; + copy_v3db_v3fl(plane, rv3d->viewinv[2]); + plane[3] = -dot_v3v3(rv3d->viewinv[2], widget->origin); + glClipPlane(GL_CLIP_PLANE0, plane); + glEnable(GL_CLIP_PLANE0); + } + + GPU_select_load_id(selectionbase); + dial_draw_intern(dial, true, false, dial->widget.scale); + + if (dial->style == WIDGET_DIAL_STYLE_RING_CLIPPED) { + glDisable(GL_CLIP_PLANE0); + } +} + +static void widget_dial_draw(struct wmWidget *widget, const struct bContext *C) +{ + DialWidget *dial = (DialWidget *)widget; + ARegion *ar = CTX_wm_region(C); + RegionView3D *rv3d = ar->regiondata; + + /* enable clipping if needed */ + if (dial->style == WIDGET_DIAL_STYLE_RING_CLIPPED) { + double plane[4]; + copy_v3db_v3fl(plane, rv3d->viewinv[2]); + plane[3] = -dot_v3v3(rv3d->viewinv[2], widget->origin); + glClipPlane(GL_CLIP_PLANE0, plane); + glEnable(GL_CLIP_PLANE0); + } + + dial_draw_intern(dial, false, (widget->flag & WM_WIDGET_HIGHLIGHT) != 0, widget->scale); + + if (dial->style == WIDGET_DIAL_STYLE_RING_CLIPPED) { + glDisable(GL_CLIP_PLANE0); + } +} + +wmWidget *WIDGET_dial_new(int style) +{ + float dir_default[3] = {0.0f, 0.0f, 1.0f}; + DialWidget *dial; + + if (!dial_draw_info.init) { + dial_draw_info.nverts = _WIDGET_nverts_dial, + dial_draw_info.ntris = _WIDGET_ntris_dial, + dial_draw_info.verts = _WIDGET_verts_dial, + dial_draw_info.normals = _WIDGET_normals_dial, + dial_draw_info.indices = _WIDGET_indices_dial, + dial_draw_info.init = true; + } + + dial = MEM_callocN(sizeof(ArrowWidget), "arrowwidget"); + + dial->widget.draw = widget_dial_draw; + dial->widget.intersect = NULL; + dial->widget.render_3d_intersection = widget_dial_render_3d_intersect; + + dial->style = style; + copy_v3_v3(dial->direction, dir_default); + + return (wmWidget *)dial; +} + +void WIDGET_dial_set_color(struct wmWidget *widget, float color[4]) +{ + DialWidget *arrow = (DialWidget *)widget; + + copy_v4_v4(arrow->color, color); +} + +void WIDGET_dial_set_direction(struct wmWidget *widget, float direction[3]) +{ + DialWidget *dial = (DialWidget *)widget; + + copy_v3_v3(dial->direction, direction); + normalize_v3(dial->direction); +} + +/********* Cage widget ************/ + +enum { + WIDGET_RECT_TRANSFORM_INTERSECT_TRANSLATE = 1, + WIDGET_RECT_TRANSFORM_INTERSECT_SCALEX_LEFT = 2, + WIDGET_RECT_TRANSFORM_INTERSECT_SCALEX_RIGHT = 3, + WIDGET_RECT_TRANSFORM_INTERSECT_SCALEY_UP = 4, + WIDGET_RECT_TRANSFORM_INTERSECT_SCALEY_DOWN = 5 +}; + +#define WIDGET_RECT_MIN_WIDTH 15.0f +#define WIDGET_RESIZER_WIDTH 20.0f + +typedef struct RectTransformWidget { + wmWidget widget; + float offset[2]; /* position of widget */ + float w, h; /* dimensions of widget */ + float rotation; /* rotation of the rectangle */ + float scale[2]; /* scaling for the widget for non-destructive editing. */ + int style; +} RectTransformWidget; + +static void rect_transform_draw_corners(rctf *r, float offsetx, float offsety) +{ + glBegin(GL_LINES); + glVertex2f(r->xmin, r->ymin + offsety); + glVertex2f(r->xmin, r->ymin); + glVertex2f(r->xmin, r->ymin); + glVertex2f(r->xmin + offsetx, r->ymin); + + glVertex2f(r->xmax, r->ymin + offsety); + glVertex2f(r->xmax, r->ymin); + glVertex2f(r->xmax, r->ymin); + glVertex2f(r->xmax - offsetx, r->ymin); + + glVertex2f(r->xmax, r->ymax - offsety); + glVertex2f(r->xmax, r->ymax); + glVertex2f(r->xmax, r->ymax); + glVertex2f(r->xmax - offsetx, r->ymax); + + glVertex2f(r->xmin, r->ymax - offsety); + glVertex2f(r->xmin, r->ymax); + glVertex2f(r->xmin, r->ymax); + glVertex2f(r->xmin + offsetx, r->ymax); + glEnd(); +} + +static void rect_transform_draw_interaction(int highlighted, float half_w, float half_h, float w, float h) +{ + float verts[4][2]; +#if 0 + unsigned short elems[4] = {0, 1, 3, 2}; +#endif + + switch (highlighted) { + case WIDGET_RECT_TRANSFORM_INTERSECT_SCALEX_LEFT: + verts[0][0] = -half_w + w; + verts[0][1] = -half_h; + verts[1][0] = -half_w; + verts[1][1] = -half_h; + verts[2][0] = -half_w; + verts[2][1] = half_h; + verts[3][0] = -half_w + w; + verts[3][1] = half_h; + break; + + case WIDGET_RECT_TRANSFORM_INTERSECT_SCALEX_RIGHT: + verts[0][0] = half_w - w; + verts[0][1] = -half_h; + verts[1][0] = half_w; + verts[1][1] = -half_h; + verts[2][0] = half_w; + verts[2][1] = half_h; + verts[3][0] = half_w - w; + verts[3][1] = half_h; + break; + + case WIDGET_RECT_TRANSFORM_INTERSECT_SCALEY_DOWN: + verts[0][0] = -half_w; + verts[0][1] = -half_h + h; + verts[1][0] = -half_w; + verts[1][1] = -half_h; + verts[2][0] = half_w; + verts[2][1] = -half_h; + verts[3][0] = half_w; + verts[3][1] = -half_h + h; + break; + + case WIDGET_RECT_TRANSFORM_INTERSECT_SCALEY_UP: + verts[0][0] = -half_w; + verts[0][1] = half_h - h; + verts[1][0] = -half_w; + verts[1][1] = half_h; + verts[2][0] = half_w; + verts[2][1] = half_h; + verts[3][0] = half_w; + verts[3][1] = half_h - h; + break; + + default: + return; + } + + glEnableClientState(GL_VERTEX_ARRAY); + glVertexPointer(2, GL_FLOAT, 0, verts); + glLineWidth(3.0); + glColor3f(0.0, 0.0, 0.0); + glDrawArrays(GL_LINE_STRIP, 0, 3); + glLineWidth(1.0); + glColor3f(1.0, 1.0, 1.0); + glDrawArrays(GL_LINE_STRIP, 0, 3); +} + +static void widget_rect_transform_draw(struct wmWidget *widget, const struct bContext *UNUSED(C)) +{ + RectTransformWidget *cage = (RectTransformWidget *)widget; + rctf r; + float w = cage->w; + float h = cage->h; + float half_w = w / 2.0f; + float half_h = h / 2.0f; + float aspx = 1.0f, aspy = 1.0f; + + r.xmin = -half_w; + r.ymin = -half_h; + r.xmax = half_w; + r.ymax = half_h; + + glPushMatrix(); + glTranslatef(widget->origin[0] + cage->offset[0], widget->origin[1] + cage->offset[1], 0.0f); + if (cage->style & WIDGET_RECT_TRANSFORM_STYLE_SCALE_UNIFORM) + glScalef(cage->scale[0], cage->scale[0], 1.0); + else + glScalef(cage->scale[0], cage->scale[1], 1.0); + + if (w > h) + aspx = h / w; + else + aspy = w / h; + w = min_ff(aspx * w / WIDGET_RESIZER_WIDTH, WIDGET_RESIZER_WIDTH / cage->scale[0]); + h = min_ff(aspy * h / WIDGET_RESIZER_WIDTH, WIDGET_RESIZER_WIDTH / + ((cage->style & WIDGET_RECT_TRANSFORM_STYLE_SCALE_UNIFORM) ? cage->scale[0] : cage->scale[1])); + + /* corner widgets */ + glColor3f(0.0, 0.0, 0.0); + glLineWidth(3.0); + + rect_transform_draw_corners(&r, w, h); + + /* corner widgets */ + glColor3f(1.0, 1.0, 1.0); + glLineWidth(1.0); + rect_transform_draw_corners(&r, w, h); + + rect_transform_draw_interaction(widget->highlighted_part, half_w, half_h, w, h); + glPopMatrix(); +} + +static int widget_rect_tranfrorm_get_cursor(wmWidget *widget) +{ + switch (widget->highlighted_part) { + case WIDGET_RECT_TRANSFORM_INTERSECT_TRANSLATE: + return BC_HANDCURSOR; + case WIDGET_RECT_TRANSFORM_INTERSECT_SCALEX_LEFT: + case WIDGET_RECT_TRANSFORM_INTERSECT_SCALEX_RIGHT: + return CURSOR_X_MOVE; + case WIDGET_RECT_TRANSFORM_INTERSECT_SCALEY_DOWN: + case WIDGET_RECT_TRANSFORM_INTERSECT_SCALEY_UP: + return CURSOR_Y_MOVE; + default: + return CURSOR_STD; + } +} + +static int widget_rect_tranfrorm_intersect(struct bContext *UNUSED(C), const struct wmEvent *event, struct wmWidget *widget) +{ + RectTransformWidget *cage = (RectTransformWidget *)widget; + float mouse[2] = {event->mval[0], event->mval[1]}; + float point_local[2]; + float w = cage->w; + float h = cage->h; + float half_w = w / 2.0f; + float half_h = h / 2.0f; + //float matrot[2][2]; + bool isect; + rctf r; + float aspx = 1.0f, aspy = 1.0f; + + /* rotate mouse in relation to the center and relocate it */ + sub_v2_v2v2(point_local, mouse, widget->origin); + point_local[0] -= cage->offset[0]; + point_local[1] -= cage->offset[1]; + //rotate_m2(matrot, -cage->transform.rotation); + + if (cage->style & WIDGET_RECT_TRANSFORM_STYLE_SCALE_UNIFORM) + mul_v2_fl(point_local, 1.0f/cage->scale[0]); + else { + point_local[0] /= cage->scale[0]; + point_local[1] /= cage->scale[0]; + } + + if (cage->w > cage->h) + aspx = h / w; + else + aspy = w / h; + w = min_ff(aspx * w / WIDGET_RESIZER_WIDTH, WIDGET_RESIZER_WIDTH / cage->scale[0]); + h = min_ff(aspy * h / WIDGET_RESIZER_WIDTH, WIDGET_RESIZER_WIDTH / + ((cage->style & WIDGET_RECT_TRANSFORM_STYLE_SCALE_UNIFORM) ? cage->scale[0] : cage->scale[1])); + + r.xmin = -half_w + w; + r.ymin = -half_h + h; + r.xmax = half_w - w; + r.ymax = half_h - h; + + isect = BLI_rctf_isect_pt_v(&r, point_local); + + if (isect) + return WIDGET_RECT_TRANSFORM_INTERSECT_TRANSLATE; + + /* if widget does not have a scale intersection, don't do it */ + if (cage->style & (WIDGET_RECT_TRANSFORM_STYLE_SCALE | WIDGET_RECT_TRANSFORM_STYLE_SCALE_UNIFORM)) { + r.xmin = -half_w; + r.ymin = -half_h; + r.xmax = -half_w + w; + r.ymax = half_h; + + isect = BLI_rctf_isect_pt_v(&r, point_local); + + if (isect) + return WIDGET_RECT_TRANSFORM_INTERSECT_SCALEX_LEFT; + + r.xmin = half_w - w; + r.ymin = -half_h; + r.xmax = half_w; + r.ymax = half_h; + + isect = BLI_rctf_isect_pt_v(&r, point_local); + + if (isect) + return WIDGET_RECT_TRANSFORM_INTERSECT_SCALEX_RIGHT; + + r.xmin = -half_w; + r.ymin = -half_h; + r.xmax = half_w; + r.ymax = -half_h + h; + + isect = BLI_rctf_isect_pt_v(&r, point_local); + + if (isect) + return WIDGET_RECT_TRANSFORM_INTERSECT_SCALEY_DOWN; + + r.xmin = -half_w; + r.ymin = half_h - h; + r.xmax = half_w; + r.ymax = half_h; + + isect = BLI_rctf_isect_pt_v(&r, point_local); + + if (isect) + return WIDGET_RECT_TRANSFORM_INTERSECT_SCALEY_UP; + } + + return 0; +} + +typedef struct RectTransformInteraction { + float orig_mouse[2]; + float orig_offset[2]; + float orig_scale[2]; +} RectTransformInteraction; + +static bool widget_rect_transform_get_property(struct wmWidget *widget, int slot, float *value) +{ + PropertyType type = RNA_property_type(widget->props[slot]); + + if (type != PROP_FLOAT) { + fprintf(stderr, "Rect Transform widget can only be bound to float properties"); + return false; + } + else { + if (slot == RECT_TRANSFORM_SLOT_OFFSET) { + if (RNA_property_array_length(&widget->ptr[slot], widget->props[slot]) != 2) { + fprintf(stderr, "Rect Transform widget offset not only be bound to array float property"); + return false; + } + + RNA_property_float_get_array(&widget->ptr[slot], widget->props[slot], value); + } + else if (slot == RECT_TRANSFORM_SLOT_SCALE) { + RectTransformWidget *cage = (RectTransformWidget *)widget; + if (cage->style & WIDGET_RECT_TRANSFORM_STYLE_SCALE_UNIFORM) + *value = RNA_property_float_get(&widget->ptr[slot], widget->props[slot]); + else { + if (RNA_property_array_length(&widget->ptr[slot], widget->props[slot]) != 2) { + fprintf(stderr, "Rect Transform widget scale not only be bound to array float property"); + return false; + } + RNA_property_float_get_array(&widget->ptr[slot], widget->props[slot], value); + } + } + } + + return true; +} + +static int widget_rect_transform_invoke(struct bContext *UNUSED(C), const struct wmEvent *event, struct wmWidget *widget) +{ + RectTransformWidget *cage = (RectTransformWidget *) widget; + RectTransformInteraction *data = MEM_callocN(sizeof (RectTransformInteraction), "cage_interaction"); + + copy_v2_v2(data->orig_offset, cage->offset); + copy_v2_v2(data->orig_scale, cage->scale); + + data->orig_mouse[0] = event->mval[0]; + data->orig_mouse[1] = event->mval[1]; + + widget->interaction_data = data; + + return OPERATOR_RUNNING_MODAL; +} + +static int widget_rect_transform_handler(struct bContext *C, const struct wmEvent *event, struct wmWidget *widget) +{ + RectTransformWidget *cage = (RectTransformWidget *) widget; + RectTransformInteraction *data = widget->interaction_data; + ARegion *ar = CTX_wm_region(C); + float valuex, valuey; + /* needed here as well in case clamping occurs */ + float orig_ofx = cage->offset[0], orig_ofy = cage->offset[1]; + + valuex = (event->mval[0] - data->orig_mouse[0]); + valuey = (event->mval[1] - data->orig_mouse[1]); + + if (widget->highlighted_part == WIDGET_RECT_TRANSFORM_INTERSECT_TRANSLATE) { + cage->offset[0] = data->orig_offset[0] + valuex; + cage->offset[1] = data->orig_offset[1] + valuey; + } + else if (widget->highlighted_part == WIDGET_RECT_TRANSFORM_INTERSECT_SCALEX_LEFT) { + cage->offset[0] = data->orig_offset[0] + valuex / 2.0; + cage->scale[0] = (cage->w * data->orig_scale[0] - valuex) / cage->w; + } + else if (widget->highlighted_part == WIDGET_RECT_TRANSFORM_INTERSECT_SCALEX_RIGHT) { + cage->offset[0] = data->orig_offset[0] + valuex / 2.0; + cage->scale[0] = (cage->w * data->orig_scale[0] + valuex) / cage->w; + } + else if (widget->highlighted_part == WIDGET_RECT_TRANSFORM_INTERSECT_SCALEY_DOWN) { + cage->offset[1] = data->orig_offset[1] + valuey / 2.0; + + if (cage->style & WIDGET_RECT_TRANSFORM_STYLE_SCALE_UNIFORM) { + cage->scale[0] = (cage->h * data->orig_scale[0] - valuey) / cage->h; + } + else { + cage->scale[1] = (cage->h * data->orig_scale[1] - valuey) / cage->h; + } + } + else if (widget->highlighted_part == WIDGET_RECT_TRANSFORM_INTERSECT_SCALEY_UP) { + cage->offset[1] = data->orig_offset[1] + valuey / 2.0; + + if (cage->style & WIDGET_RECT_TRANSFORM_STYLE_SCALE_UNIFORM) { + cage->scale[0] = (cage->h * data->orig_scale[0] + valuey) / cage->h; + } + else { + cage->scale[1] = (cage->h * data->orig_scale[1] + valuey) / cage->h; + } + } + + /* clamping - make sure widget is at least 5 pixels wide */ + if (cage->style & WIDGET_RECT_TRANSFORM_STYLE_SCALE_UNIFORM) { + if (cage->scale[0] < WIDGET_RECT_MIN_WIDTH / cage->h || + cage->scale[0] < WIDGET_RECT_MIN_WIDTH / cage->w) + { + cage->scale[0] = max_ff(WIDGET_RECT_MIN_WIDTH / cage->h, WIDGET_RECT_MIN_WIDTH / cage->w); + cage->offset[0] = orig_ofx; + cage->offset[1] = orig_ofy; + } + } + else { + if (cage->scale[0] < WIDGET_RECT_MIN_WIDTH / cage->w) { + cage->scale[0] = WIDGET_RECT_MIN_WIDTH / cage->w; + cage->offset[0] = orig_ofx; + } + if (cage->scale[1] < WIDGET_RECT_MIN_WIDTH / cage->h) { + cage->scale[1] = WIDGET_RECT_MIN_WIDTH / cage->h; + cage->offset[1] = orig_ofy; + } + } + + if (widget->props[RECT_TRANSFORM_SLOT_OFFSET]) { + RNA_property_float_set_array(&widget->ptr[RECT_TRANSFORM_SLOT_OFFSET], widget->props[RECT_TRANSFORM_SLOT_OFFSET], cage->offset); + RNA_property_update(C, &widget->ptr[RECT_TRANSFORM_SLOT_OFFSET], widget->props[RECT_TRANSFORM_SLOT_OFFSET]); + } + + if (widget->props[RECT_TRANSFORM_SLOT_SCALE]) { + if (cage->style & WIDGET_RECT_TRANSFORM_STYLE_SCALE_UNIFORM) + RNA_property_float_set(&widget->ptr[RECT_TRANSFORM_SLOT_SCALE], widget->props[RECT_TRANSFORM_SLOT_SCALE], cage->scale[0]); + else + RNA_property_float_set_array(&widget->ptr[RECT_TRANSFORM_SLOT_SCALE], widget->props[RECT_TRANSFORM_SLOT_SCALE], cage->scale); + RNA_property_update(C, &widget->ptr[RECT_TRANSFORM_SLOT_SCALE], widget->props[RECT_TRANSFORM_SLOT_SCALE]); + } + + /* tag the region for redraw */ + ED_region_tag_redraw(ar); + + return OPERATOR_PASS_THROUGH; +} + +static void widget_rect_transform_bind_to_prop(struct wmWidget *widget, int slot) +{ + RectTransformWidget *cage = (RectTransformWidget *) widget; + + if (slot == RECT_TRANSFORM_SLOT_OFFSET) + widget_rect_transform_get_property(widget, RECT_TRANSFORM_SLOT_OFFSET, cage->offset); + if (slot == RECT_TRANSFORM_SLOT_SCALE) + widget_rect_transform_get_property(widget, RECT_TRANSFORM_SLOT_SCALE, cage->scale); +} + +struct wmWidget *WIDGET_rect_transform_new(struct wmWidgetGroup *wgroup, int style, float width, float height) +{ + RectTransformWidget *cage = MEM_callocN(sizeof(RectTransformWidget), "CageWidget"); + + cage->widget.draw = widget_rect_transform_draw; + cage->widget.invoke = widget_rect_transform_invoke; + cage->widget.bind_to_prop = widget_rect_transform_bind_to_prop; + cage->widget.handler = widget_rect_transform_handler; + cage->widget.intersect = widget_rect_tranfrorm_intersect; + cage->widget.get_cursor = widget_rect_tranfrorm_get_cursor; + cage->widget.max_prop = 2; + cage->scale[0] = cage->scale[1] = 1.0f; + cage->style = style; + cage->w = width; + cage->h = height; + + wm_widget_register(wgroup, &cage->widget); + + return (wmWidget *)cage; +} + +void WIDGET_rect_transform_set_offset(struct wmWidget *widget, float offset[2]) +{ + RectTransformWidget *cage = (RectTransformWidget *)widget; + + copy_v2_v2(cage->offset, offset); +} + +/********* Facemap widget ************/ + +typedef struct FacemapWidget { + wmWidget widget; + Object *ob; + int facemap; + int style; + float color[4]; +} FacemapWidget; + + +static void widget_facemap_draw(struct wmWidget *widget, const struct bContext *C) +{ + FacemapWidget *fmap_widget = (FacemapWidget *)widget; + glPushMatrix(); + glMultMatrixf(&fmap_widget->ob->obmat[0][0]); + ED_draw_object_facemap(CTX_data_scene(C), fmap_widget->ob, fmap_widget->facemap); + glPopMatrix(); +} + +static void widget_facemap_render_3d_intersect(const struct bContext *C, struct wmWidget *widget, int selectionbase) +{ + GPU_select_load_id(selectionbase); + widget_facemap_draw(widget, C); +} + + +void WIDGET_facemap_set_color(struct wmWidget *widget, float color[4]) +{ + FacemapWidget *fmap_widget = (FacemapWidget *)widget; + copy_v4_v4(fmap_widget->color, color); +} + +struct wmWidget *WIDGET_facemap_new(struct wmWidgetGroup *wgroup, int style, struct Object *ob, int facemap) +{ + FacemapWidget *fmap_widget = MEM_callocN(sizeof(RectTransformWidget), "CageWidget"); + + fmap_widget->widget.draw = widget_facemap_draw; +// fmap_widget->widget.invoke = NULL; +// fmap_widget->widget.bind_to_prop = NULL; +// fmap_widget->widget.handler = NULL; + fmap_widget->widget.render_3d_intersection = widget_facemap_render_3d_intersect; + fmap_widget->ob = ob; + fmap_widget->facemap = facemap; + fmap_widget->style = style; + + wm_widget_register(wgroup, &fmap_widget->widget); + + return (wmWidget *)fmap_widget; +} + + +void fix_linking_widget_lib(void) +{ + (void) 0; +} diff --git a/source/blender/windowmanager/intern/wm_init_exit.c b/source/blender/windowmanager/intern/wm_init_exit.c index c7544f21372..6c55d17dcb2 100644 --- a/source/blender/windowmanager/intern/wm_init_exit.c +++ b/source/blender/windowmanager/intern/wm_init_exit.c @@ -168,6 +168,8 @@ void WM_init(bContext *C, int argc, const char **argv) /* Enforce loading the UI for the initial homefile */ G.fileflags &= ~G_FILE_NO_UI; + ED_spacedropwidgets_init(); + /* get the default database, plus a wm */ wm_homefile_read(C, NULL, G.factory_startup, NULL); @@ -492,6 +494,9 @@ void WM_exit_ext(bContext *C, const bool do_python) ED_clipboard_posebuf_free(); BKE_node_clipboard_clear(); + /* widgetmaps after freeing blender, so no deleted data get accessed during cleaning up of areas */ + WM_widgetmaptypes_free(); + BLF_exit(); #ifdef WITH_INTERNATIONAL diff --git a/source/blender/windowmanager/intern/wm_operators.c b/source/blender/windowmanager/intern/wm_operators.c index cd97293516d..ed26203bec0 100644 --- a/source/blender/windowmanager/intern/wm_operators.c +++ b/source/blender/windowmanager/intern/wm_operators.c @@ -1271,6 +1271,11 @@ void WM_operator_properties_filesel(wmOperatorType *ot, int filter, short type, if (flag & WM_FILESEL_RELPATH) RNA_def_boolean(ot->srna, "relative_path", true, "Relative Path", "Select the file relative to the blend file"); + if (flag & WM_FILESEL_IMAGE_COLLAPSE) { + prop = RNA_def_boolean(ot->srna, "collapse_images", true, "Collapse Images", "Colapse Images with the same base name"); + RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); + } + if ((filter & FILE_TYPE_IMAGE) || (filter & FILE_TYPE_MOVIE)) { prop = RNA_def_boolean(ot->srna, "show_multiview", 0, "Enable Multi-View", ""); RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); @@ -4592,6 +4597,36 @@ static void WM_OT_radial_control(wmOperatorType *ot) RNA_def_boolean(ot->srna, "secondary_tex", false, "Secondary Texture", "Tweak brush secondary/mask texture"); } +/* ************************** widget property tweak *************** */ + +static int widget_tweak_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) +{ + WM_event_add_modal_handler(C, op); + return OPERATOR_RUNNING_MODAL; +} + +static int widget_tweak_modal(bContext *UNUSED(C), wmOperator *UNUSED(op), const wmEvent *event) +{ + if (event->type == EVT_WIDGET_RELEASED) + return OPERATOR_FINISHED; + + return OPERATOR_RUNNING_MODAL; +} + +static void WM_OT_widget_tweak(wmOperatorType *ot) +{ + ot->name = "Widget Tweak"; + ot->idname = "WM_OT_widget_tweak"; + ot->description = "Tweak property attached to a widget"; + + ot->invoke = widget_tweak_invoke; + ot->modal = widget_tweak_modal; + + ot->flag = OPTYPE_REGISTER | OPTYPE_BLOCKING | OPTYPE_UNDO; +} + + + /* ************************** timer for testing ***************** */ /* uses no type defines, fully local testing function anyway... ;) */ @@ -4981,6 +5016,7 @@ void wm_operatortype_init(void) WM_operatortype_append(WM_OT_call_menu); WM_operatortype_append(WM_OT_call_menu_pie); WM_operatortype_append(WM_OT_radial_control); + WM_operatortype_append(WM_OT_widget_tweak); WM_operatortype_append(WM_OT_stereo3d_set); #if defined(WIN32) WM_operatortype_append(WM_OT_console_toggle); @@ -5373,3 +5409,11 @@ EnumPropertyItem *RNA_mask_local_itemf(bContext *C, PointerRNA *ptr, PropertyRNA return rna_id_itemf(C, ptr, r_free, C ? (ID *)CTX_data_main(C)->mask.first : NULL, true); } +EnumPropertyItem *RNA_cachelibrary_itemf(bContext *C, PointerRNA *ptr, PropertyRNA *UNUSED(prop), bool *r_free) +{ + return rna_id_itemf(C, ptr, r_free, C ? (ID *)CTX_data_main(C)->cache_library.first : NULL, false); +} +EnumPropertyItem *RNA_cachelibrary_local_itemf(bContext *C, PointerRNA *ptr, PropertyRNA *UNUSED(prop), bool *r_free) +{ + return rna_id_itemf(C, ptr, r_free, C ? (ID *)CTX_data_main(C)->cache_library.first : NULL, true); +} diff --git a/source/blender/windowmanager/intern/wm_playanim.c b/source/blender/windowmanager/intern/wm_playanim.c index de2ecc941fe..cc846584d97 100644 --- a/source/blender/windowmanager/intern/wm_playanim.c +++ b/source/blender/windowmanager/intern/wm_playanim.c @@ -71,6 +71,14 @@ #include "WM_api.h" /* only for WM_main_playanim */ +#ifdef WITH_AUDASPACE +#include "AUD_C-API.h" + +AUD_Sound *source = NULL; +AUD_Handle *playback_handle = NULL; +AUD_Handle *scrub_handle = NULL; +#endif + /* simple limiter to avoid flooding memory */ #define USE_FRAME_CACHE_LIMIT #ifdef USE_FRAME_CACHE_LIMIT @@ -96,6 +104,7 @@ typedef struct PlayState { bool turbo; bool pingpong; bool noskip; + bool indicator; bool sstep; bool wait2; bool stopped; @@ -231,6 +240,7 @@ static struct ListBase picsbase = {NULL, NULL}; /* frames in memory - store them here to for easy deallocation later */ static bool fromdisk = false; static double ptottime = 0.0, swaptime = 0.04; +static double fps_movie; #ifdef USE_FRAME_CACHE_LIMIT static struct ListBase inmempicsbase = {NULL, NULL}; @@ -334,6 +344,30 @@ static void playanim_toscreen(PlayState *ps, PlayAnimPict *picture, struct ImBuf BLF_draw(fontid, str, sizeof(str)); } + if (ps->indicator) { + float fac = ps->picture->frame / (double)(((PlayAnimPict *)picsbase.last)->frame - ((PlayAnimPict *)picsbase.first)->frame); + + fac = 2.0f * fac - 1.0f; + glMatrixMode(GL_PROJECTION); + glPushMatrix(); + glLoadIdentity(); + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glLoadIdentity(); + + glColor4f(0.0f, 1.0f, 0.0f, 1.0f); + + glBegin(GL_LINES); + glVertex2f(fac, -1.0f); + glVertex2f(fac, 1.0f); + glEnd(); + + glPopMatrix(); + glMatrixMode(GL_PROJECTION); + glPopMatrix(); + glMatrixMode(GL_MODELVIEW); + } + GHOST_SwapWindowBuffers(g_WS.ghost_window); } @@ -441,6 +475,7 @@ static void build_pict_list_ex(PlayState *ps, const char *first, int totframes, picture->mem = mem; picture->name = strdup(filepath); + picture->frame = count; /* not exact but should work for positioning */ close(file); BLI_addtail(&picsbase, picture); count++; @@ -488,6 +523,76 @@ static void build_pict_list(PlayState *ps, const char *first, int totframes, int ps->loading = false; } +static void update_sound_fps(void) +{ +#ifdef WITH_AUDASPACE + if (playback_handle) { + /* swaptime stores the 1.0/fps ratio */ + double speed = 1.0 / (swaptime * fps_movie); + + AUD_setSoundPitch(playback_handle, speed); + } +#endif +} + +static void change_frame(PlayState *ps, int cx) +{ + int sizex, sizey; + int i; + + playanim_window_get_size(&sizex, &sizey); + ps->picture = picsbase.first; + /* TODO - store in ps direct? */ + i = 0; + while (ps->picture) { + i++; + ps->picture = ps->picture->next; + } + i = (i * cx) / sizex; + +#ifdef WITH_AUDASPACE + if (scrub_handle) { + AUD_stop(scrub_handle); + scrub_handle = NULL; + } + + if (playback_handle) { + AUD_Status status = AUD_getStatus(playback_handle); + if (status != AUD_STATUS_PLAYING) { + AUD_stop(playback_handle); + playback_handle = AUD_play(source, 1); + if (playback_handle) { + AUD_seek(playback_handle, i / fps_movie); + scrub_handle = AUD_pauseAfter(playback_handle, 1 / fps_movie); + } + update_sound_fps(); + } + else { + AUD_seek(playback_handle, i / fps_movie); + scrub_handle = AUD_pauseAfter(playback_handle, 1 / fps_movie); + } + } + else if (source) { + playback_handle = AUD_play(source, 1); + if (playback_handle) { + AUD_seek(playback_handle, i / fps_movie); + scrub_handle = AUD_pauseAfter(playback_handle, 1 / fps_movie); + } + update_sound_fps(); + } +#endif + + ps->picture = picsbase.first; + for (; i > 0; i--) { + if (ps->picture->next == NULL) break; + ps->picture = ps->picture->next; + } + + ps->sstep = true; + ps->wait2 = false; + ps->next_frame = 0; +} + static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr ps_void) { PlayState *ps = (PlayState *)ps_void; @@ -501,7 +606,6 @@ static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr ps_void) /* convert ghost event into value keyboard or mouse */ val = ELEM(type, GHOST_kEventKeyDown, GHOST_kEventButtonDown); - /* first check if we're busy loading files */ if (ps->loading) { switch (type) { @@ -547,6 +651,9 @@ static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr ps_void) case GHOST_kKeyA: if (val) ps->noskip = !ps->noskip; break; + case GHOST_kKeyI: + if (val) ps->indicator = !ps->indicator; + break; case GHOST_kKeyP: if (val) ps->pingpong = !ps->pingpong; break; @@ -560,42 +667,70 @@ static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr ps_void) } case GHOST_kKey1: case GHOST_kKeyNumpad1: - if (val) swaptime = ps->fstep / 60.0; + if (val) { + swaptime = ps->fstep / 60.0; + update_sound_fps(); + } break; case GHOST_kKey2: case GHOST_kKeyNumpad2: - if (val) swaptime = ps->fstep / 50.0; + if (val) { + swaptime = ps->fstep / 50.0; + update_sound_fps(); + } break; case GHOST_kKey3: case GHOST_kKeyNumpad3: - if (val) swaptime = ps->fstep / 30.0; + if (val) { + swaptime = ps->fstep / 30.0; + update_sound_fps(); + } break; case GHOST_kKey4: case GHOST_kKeyNumpad4: - if (g_WS.qual & WS_QUAL_SHIFT) + if (g_WS.qual & WS_QUAL_SHIFT) { swaptime = ps->fstep / 24.0; - else + update_sound_fps(); + } + else { swaptime = ps->fstep / 25.0; + update_sound_fps(); + } break; case GHOST_kKey5: case GHOST_kKeyNumpad5: - if (val) swaptime = ps->fstep / 20.0; + if (val) { + swaptime = ps->fstep / 20.0; + update_sound_fps(); + } break; case GHOST_kKey6: case GHOST_kKeyNumpad6: - if (val) swaptime = ps->fstep / 15.0; + if (val) { + swaptime = ps->fstep / 15.0; + update_sound_fps(); + } break; case GHOST_kKey7: case GHOST_kKeyNumpad7: - if (val) swaptime = ps->fstep / 12.0; + if (val) { + swaptime = ps->fstep / 12.0; + update_sound_fps(); + } break; case GHOST_kKey8: case GHOST_kKeyNumpad8: - if (val) swaptime = ps->fstep / 10.0; + if (val) { + swaptime = ps->fstep / 10.0; + update_sound_fps(); + } break; case GHOST_kKey9: case GHOST_kKeyNumpad9: - if (val) swaptime = ps->fstep / 6.0; + if (val) { + swaptime = ps->fstep / 6.0; + update_sound_fps(); + } break; case GHOST_kKeyLeftArrow: if (val) { @@ -658,6 +793,7 @@ static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr ps_void) } else { swaptime = ps->fstep / 5.0; + update_sound_fps(); } } break; @@ -674,10 +810,63 @@ static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr ps_void) } } break; + + case GHOST_kKeySpace: + if (val) { + if (ps->wait2 || ps->sstep) { + ps->wait2 = ps->sstep = false; +#ifdef WITH_AUDASPACE + { + PlayAnimPict *picture = picsbase.first; + /* TODO - store in ps direct? */ + int i = 0; + + while (picture && picture != ps->picture) { + i++; + picture = picture->next; + } + if (playback_handle) + AUD_stop(playback_handle); + playback_handle = AUD_play(source, 1); + if (playback_handle) + AUD_seek(playback_handle, i / fps_movie); + update_sound_fps(); + } +#endif + } + else { + ps->sstep = true; + ps->wait2 = true; +#ifdef WITH_AUDASPACE + if (playback_handle) { + AUD_stop(playback_handle); + playback_handle = NULL; + } +#endif + } + } + break; case GHOST_kKeyEnter: case GHOST_kKeyNumpadEnter: if (val) { ps->wait2 = ps->sstep = false; +#ifdef WITH_AUDASPACE + { + PlayAnimPict *picture = picsbase.first; + /* TODO - store in ps direct? */ + int i = 0; + while (picture && picture != ps->picture) { + i++; + picture = picture->next; + } + if (playback_handle) + AUD_stop(playback_handle); + playback_handle = AUD_play(source, 1); + if (playback_handle) + AUD_seek(playback_handle, i / fps_movie); + update_sound_fps(); + } +#endif } break; case GHOST_kKeyPeriod: @@ -689,6 +878,12 @@ static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr ps_void) else { ps->sstep = true; ps->wait2 = !ps->wait2; +#ifdef WITH_AUDASPACE + if (playback_handle) { + AUD_stop(playback_handle); + playback_handle = NULL; + } +#endif } } break; @@ -700,8 +895,10 @@ static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr ps_void) playanim_window_zoom(ps, 1.0f); } else { - swaptime /= 1.1; - swaptime = MAX2(ps->fstep / 60.0, swaptime); + if (swaptime > ps->fstep / 60.0) { + swaptime /= 1.1; + update_sound_fps(); + } } break; } @@ -713,8 +910,10 @@ static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr ps_void) playanim_window_zoom(ps, -1.0f); } else { - swaptime *= 1.1; - swaptime = MIN2(ps->fstep / 5.0, swaptime); + if (swaptime < ps->fstep / 5.0) { + swaptime *= 1.1; + update_sound_fps(); + } } break; } @@ -740,8 +939,10 @@ static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr ps_void) if (bd->button == GHOST_kButtonMaskLeft) { if (type == GHOST_kEventButtonDown) { - if (inside_window) + if (inside_window) { g_WS.qual |= WS_QUAL_LMOUSE; + change_frame(ps, cx); + } } else g_WS.qual &= ~WS_QUAL_LMOUSE; @@ -767,31 +968,12 @@ static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr ps_void) case GHOST_kEventCursorMove: { if (g_WS.qual & WS_QUAL_LMOUSE) { - int sizex, sizey; - int i; - GHOST_TEventCursorData *cd = GHOST_GetEventData(evt); int cx, cy; GHOST_ScreenToClient(g_WS.ghost_window, cd->x, cd->y, &cx, &cy); - playanim_window_get_size(&sizex, &sizey); - ps->picture = picsbase.first; - /* TODO - store in ps direct? */ - i = 0; - while (ps->picture) { - i++; - ps->picture = ps->picture->next; - } - i = (i * cx) / sizex; - ps->picture = picsbase.first; - for (; i > 0; i--) { - if (ps->picture->next == NULL) break; - ps->picture = ps->picture->next; - } - ps->sstep = true; - ps->wait2 = false; - ps->next_frame = 0; + change_frame(ps, cx); } break; } @@ -913,6 +1095,18 @@ static char *wm_main_playanim_intern(int argc, const char **argv) PlayState ps = {0}; +#ifdef WITH_AUDASPACE + AUD_DeviceSpecs specs; + + specs.rate = AUD_RATE_44100; + specs.format = AUD_FORMAT_S16; + specs.channels = AUD_CHANNELS_STEREO; + + if (!AUD_init(AUD_OPENAL_DEVICE, specs, AUD_DEFAULT_BUFFER_SIZE)) + AUD_init(AUD_NULL_DEVICE, specs, AUD_DEFAULT_BUFFER_SIZE); + +#endif + /* ps.doubleb = true;*/ /* UNUSED */ ps.go = true; ps.direction = true; @@ -926,6 +1120,7 @@ static char *wm_main_playanim_intern(int argc, const char **argv) ps.stopped = false; ps.loading = false; ps.picture = NULL; + ps.indicator = false; ps.dropped_file[0] = 0; ps.zoom = 1.0f; /* resetmap = false */ @@ -1083,6 +1278,23 @@ static char *wm_main_playanim_intern(int argc, const char **argv) build_pict_list(&ps, filepath, (efra - sfra) + 1, ps.fstep, ps.fontid); +#ifdef WITH_AUDASPACE + source = AUD_load(filepath); + { + struct anim *anim_movie = ((struct PlayAnimPict *)picsbase.first)->anim; + if (anim_movie) { + short frs_sec = 25; + float frs_sec_base = 1.0; + + IMB_anim_get_fps(anim_movie, &frs_sec, &frs_sec_base, true); + + fps_movie = (double) frs_sec / (double) frs_sec_base; + /* enforce same fps for movie as sound */ + swaptime = ps.fstep / fps_movie; + } + } +#endif + for (i = 2; i < argc; i++) { BLI_strncpy(filepath, argv[i], sizeof(filepath)); build_pict_list(&ps, filepath, (efra - sfra) + 1, ps.fstep, ps.fontid); @@ -1122,6 +1334,13 @@ static char *wm_main_playanim_intern(int argc, const char **argv) } if (ptottime > 0.0) ptottime = 0.0; +#ifdef WITH_AUDASPACE + if (playback_handle) + AUD_stop(playback_handle); + playback_handle = AUD_play(source, 1); + update_sound_fps(); +#endif + while (ps.picture) { int hasevent; #ifndef USE_IMB_CACHE @@ -1283,9 +1502,18 @@ static char *wm_main_playanim_intern(int argc, const char **argv) #endif BLI_freelistN(&picsbase); - BLI_freelistN(&inmempicsbase); added_images = 0; + +#ifdef WITH_AUDASPACE + if (playback_handle) + AUD_stop(playback_handle); + if (scrub_handle) + AUD_stop(scrub_handle); + AUD_unload(source); + AUD_exit(); +#endif + #if 0 // XXX25 free_blender(); #else diff --git a/source/blender/windowmanager/intern/wm_widgets.c b/source/blender/windowmanager/intern/wm_widgets.c new file mode 100644 index 00000000000..ad915311015 --- /dev/null +++ b/source/blender/windowmanager/intern/wm_widgets.c @@ -0,0 +1,909 @@ +/* + * ***** 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2007 Blender Foundation but based + * on ghostwinlay.c (C) 2001-2002 by NaN Holding BV + * All rights reserved. + * + * Contributor(s): Blender Foundation, 2008 + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/windowmanager/intern/wm_widgets.c + * \ingroup wm + * + * Window management, widget API. + */ + +#include <stdlib.h> +#include <string.h> + +#include "DNA_screen_types.h" +#include "DNA_scene_types.h" +#include "DNA_windowmanager_types.h" +#include "DNA_view3d_types.h" +#include "DNA_userdef_types.h" + +#include "MEM_guardedalloc.h" + +#include "BLI_listbase.h" +#include "BLI_string.h" +#include "BLI_math.h" + +#include "BKE_context.h" +#include "BKE_idprop.h" +#include "BKE_global.h" +#include "BKE_main.h" +#include "BKE_report.h" +#include "BKE_scene.h" +#include "BKE_screen.h" + +#include "ED_view3d.h" +#include "ED_screen.h" +#include "ED_util.h" + +#include "WM_api.h" +#include "WM_types.h" +#include "wm.h" +#include "wm_window.h" +#include "wm_event_system.h" +#include "wm_event_types.h" +#include "wm_draw.h" + +#include "GL/glew.h" +#include "GPU_select.h" + +#include "RNA_access.h" +#include "BPY_extern.h" + +/** + * This is a container for all widget types that can be instantiated in a region. + * (similar to dropboxes). + * + * \note There is only ever one of these for every (area, region) combination. + */ +typedef struct wmWidgetMapType { + struct wmWidgetMapType *next, *prev; + char idname[64]; + short spaceid, regionid; + /** + * Check if widgetmap does 3D drawing + * (uses a different kind of interaction), + * - 3d: use glSelect buffer. + * - 2d: use simple cursor position intersection test. */ + bool is_3d; + /* types of widgetgroups for this widgetmap type */ + ListBase widgetgrouptypes; +} wmWidgetMapType; + + +/* store all widgetboxmaps here. Anyone who wants to register a widget for a certain + * area type can query the widgetbox to do so */ +static ListBase widgetmaptypes = {NULL, NULL}; + + +struct wmWidgetGroupType *WM_widgetgrouptype_new( + int (*poll)(const struct bContext *C, struct wmWidgetGroupType *), + void (*draw)(const struct bContext *, struct wmWidgetGroup *), + struct Main *bmain, const char *mapidname, short spaceid, short regionid, bool is_3d + ) +{ + bScreen *sc; + struct wmWidgetMapType *wmaptype = WM_widgetmaptype_find(mapidname, spaceid, regionid, is_3d, false); + wmWidgetGroupType *wgrouptype; + + if (!wmaptype) { + fprintf(stderr, "widgetgrouptype creation: widgetmap type does not exist"); + return NULL; + } + + wgrouptype = MEM_callocN(sizeof(wmWidgetGroupType), "widgetgroup"); + + wgrouptype->poll = poll; + wgrouptype->draw = draw; + wgrouptype->spaceid = spaceid; + wgrouptype->regionid = regionid; + wgrouptype->is_3d = is_3d; + BLI_strncpy(wgrouptype->mapidname, mapidname, 64); + + /* add the type for future created areas of the same type */ + BLI_addtail(&wmaptype->widgetgrouptypes, wgrouptype); + + /* now create a widget for all existing areas. (main is missing when we create new areas so not needed) */ + if (bmain) { + for (sc = bmain->screen.first; sc; sc = sc->id.next) { + ScrArea *sa; + for (sa = sc->areabase.first; sa; sa = sa->next) { + SpaceLink *sl; + + for (sl = sa->spacedata.first; sl; sl = sl->next) { + ARegion *ar; + ListBase *lb = (sl == sa->spacedata.first) ? &sa->regionbase : &sl->regionbase; + + for (ar = lb->first; ar; ar = ar->next) { + wmWidgetMap *wmap; + for (wmap = ar->widgetmaps.first; wmap; wmap = wmap->next) { + if (wmap->type == wmaptype) { + wmWidgetGroup *wgroup = MEM_callocN(sizeof(wmWidgetGroup), "widgetgroup"); + wgroup->type = wgrouptype; + + /* just add here, drawing will occur on next update */ + BLI_addtail(&wmap->widgetgroups, wgroup); + wm_widgetmap_set_highlighted_widget(wmap, NULL, NULL, 0); + ED_region_tag_redraw(ar); + } + } + } + } + } + } + } + + return wgrouptype; +} + +wmWidget *WM_widget_new(void (*draw)(struct wmWidget *customdata, const struct bContext *C), + void (*render_3d_intersection)(const struct bContext *C, struct wmWidget *customdata, int selectionbase), + int (*intersect)(struct bContext *C, const struct wmEvent *event, struct wmWidget *widget), + int (*handler)(struct bContext *C, const struct wmEvent *event, struct wmWidget *widget)) +{ + wmWidget *widget; + + widget = MEM_callocN(sizeof(wmWidget), "widget"); + + widget->draw = draw; + widget->handler = handler; + widget->intersect = intersect; + widget->render_3d_intersection = render_3d_intersection; + + return widget; +} + +void WM_widget_property(struct wmWidget *widget, int slot, struct PointerRNA *ptr, const char *propname) +{ + if (slot < 0 || slot >= widget->max_prop) { + fprintf(stderr, "invalid index %d when binding property for widget type %s\n", slot, widget->idname); + return; + } + + /* if widget evokes an operator we cannot use it for property manipulation */ + widget->opname = NULL; + widget->ptr[slot] = *ptr; + widget->props[slot] = RNA_struct_find_property(ptr, propname); + + if (widget->bind_to_prop) + widget->bind_to_prop(widget, slot); +} + +PointerRNA *WM_widget_operator(struct wmWidget *widget, const char *opname) +{ + wmOperatorType *ot = WM_operatortype_find(opname, 0); + + if (ot) { + widget->opname = opname; + + WM_operator_properties_create_ptr(&widget->opptr, ot); + + return &widget->opptr; + } + else { + fprintf(stderr, "Error binding operator to widget: operator %s not found!\n", opname); + } + + return NULL; +} + + +static void wm_widget_delete(ListBase *widgetlist, wmWidget *widget) +{ + if (widget->opptr.data) { + WM_operator_properties_free(&widget->opptr); + } + + MEM_freeN(widget->props); + MEM_freeN(widget->ptr); + + BLI_freelinkN(widgetlist, widget); +} + + +static void widget_calculate_scale(wmWidget *widget, const bContext *C) +{ + float scale = 1.0f; + RegionView3D *rv3d = CTX_wm_region_view3d(C); + if (rv3d && !(U.tw_flag & V3D_3D_WIDGETS) && (widget->flag & WM_WIDGET_SCALE_3D)) { + if (widget->get_final_position) { + float position[3]; + + widget->get_final_position(widget, position); + scale = ED_view3d_pixel_size(rv3d, position) * U.tw_size; + } + else { + scale = ED_view3d_pixel_size(rv3d, widget->origin) * U.tw_size; + } + } + + widget->scale = scale * widget->user_scale; +} + +static bool widgets_compare(wmWidget *widget, wmWidget *widget2) +{ + int i; + + if (widget->max_prop != widget2->max_prop) + return false; + + for (i = 0; i < widget->max_prop; i++) { + if (widget->props[i] != widget2->props[i] || widget->ptr[i].data != widget2->ptr[i].data) + return false; + } + + return true; +} + +void WM_widgets_update(const bContext *C, wmWidgetMap *wmap) +{ + wmWidget *widget; + + if (!wmap) + return; + + widget = wmap->active_widget; + + if (widget) { + widget_calculate_scale(widget, C); + } + else if (wmap->widgetgroups.first) { + wmWidgetGroup *wgroup; + + for (wgroup = wmap->widgetgroups.first; wgroup; wgroup = wgroup->next) { + if (!wgroup->type->poll || wgroup->type->poll(C, wgroup->type)) + { + wmWidget *highlighted = NULL; + + /* first delete and recreate the widgets */ + for (widget = wgroup->widgets.first; widget;) { + wmWidget *widget_next = widget->next; + + /* do not delete the highlighted widget, instead keep it to compare with the new one */ + if (widget->flag & WM_WIDGET_HIGHLIGHT) { + highlighted = widget; + BLI_remlink(&wgroup->widgets, widget); + widget->next = widget->prev = NULL; + } + else { + wm_widget_delete(&wgroup->widgets, widget); + } + widget = widget_next; + } + + if (wgroup->type->draw) { + wgroup->type->draw(C, wgroup); + } + + if (highlighted) { + for (widget = wgroup->widgets.first; widget; widget = widget->next) { + if (widgets_compare(widget, highlighted)) + { + widget->flag |= WM_WIDGET_HIGHLIGHT; + wmap->highlighted_widget = widget; + widget->highlighted_part = highlighted->highlighted_part; + wm_widget_delete(&wgroup->widgets, highlighted); + highlighted = NULL; + break; + } + } + } + + /* if we don't find a highlighted widget, delete the old one here */ + if (highlighted) { + MEM_freeN(highlighted); + highlighted = NULL; + wmap->highlighted_widget = NULL; + } + + for (widget = wgroup->widgets.first; widget; widget = widget->next) { + widget_calculate_scale(widget, C); + } + } + } + } +} + +void WM_widgets_draw(const bContext *C, wmWidgetMap *wmap, bool in_scene) +{ + wmWidget *widget; + bool use_lighting; + + if (!wmap) + return; + + use_lighting = (U.tw_flag & V3D_SHADED_WIDGETS) != 0; + + if (use_lighting) { + float lightpos[4] = {0.0, 0.0, 1.0, 0.0}; + float diffuse[4] = {1.0, 1.0, 1.0, 0.0}; + + glPushAttrib(GL_LIGHTING_BIT | GL_ENABLE_BIT); + glEnable(GL_LIGHTING); + glEnable(GL_LIGHT0); + glEnable(GL_COLOR_MATERIAL); + glColorMaterial(GL_FRONT_AND_BACK, GL_DIFFUSE); + glPushMatrix(); + glLoadIdentity(); + glLightfv(GL_LIGHT0, GL_POSITION, lightpos); + glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse); + glPopMatrix(); + } + + widget = wmap->active_widget; + + if (widget && in_scene == ((widget->flag & WM_WIDGET_SCENE_DEPTH)!= 0)) { + /* notice that we don't update the widgetgroup, widget is now on its own, it should have all + * relevant data to update itself */ + widget->draw(widget, C); + } + else if (wmap->widgetgroups.first) { + wmWidgetGroup *wgroup; + + for (wgroup = wmap->widgetgroups.first; wgroup; wgroup = wgroup->next) { + if (!wgroup->type->poll || wgroup->type->poll(C, wgroup->type)) + { + for (widget = wgroup->widgets.first; widget; widget = widget->next) { + if ((!(widget->flag & WM_WIDGET_DRAW_HOVER) || (widget->flag & WM_WIDGET_HIGHLIGHT)) && + ((widget->flag & WM_WIDGET_SCENE_DEPTH) != 0) == in_scene) + { + widget->draw(widget, C); + } + } + } + } + } + + if (use_lighting) + glPopAttrib(); +} + +void WM_event_add_area_widgetmap_handlers(ARegion *ar) +{ + wmWidgetMap *wmap; + wmEventHandler *handler; + + for (wmap = ar->widgetmaps.first; wmap; wmap = wmap->next) { + handler = MEM_callocN(sizeof(wmEventHandler), "widget handler"); + + handler->widgetmap = wmap; + BLI_addtail(&ar->handlers, handler); + } +} + +void WM_modal_handler_attach_widgetgroup(bContext *C, wmEventHandler *handler, wmWidgetGroupType *wgrouptype, wmOperator *op) +{ + /* maybe overly careful, but widgetgrouptype could come from a failed creation */ + if (!wgrouptype) { + return; + } + + /* now instantiate the widgetmap */ + wgrouptype->op = op; + + if (handler->op_region && handler->op_region->widgetmaps.first) { + wmWidgetMap *wmap; + for (wmap = handler->op_region->widgetmaps.first; wmap; wmap = wmap->next) { + wmWidgetMapType *wmaptype = wmap->type; + + if (wmaptype->spaceid == wgrouptype->spaceid && wmaptype->regionid == wgrouptype->regionid) { + handler->widgetmap = wmap; + } + } + } + + WM_event_add_mousemove(C); +} + +bool wm_widget_register(struct wmWidgetGroup *wgroup, wmWidget *widget) +{ + widget->user_scale = 1.0f; + + /* create at least one property for interaction */ + if (widget->max_prop == 0) { + widget->max_prop = 1; + } + + widget->props = MEM_callocN(sizeof(PropertyRNA *) * widget->max_prop, "widget->props"); + widget->ptr = MEM_callocN(sizeof(PointerRNA) * widget->max_prop, "widget->ptr"); + + BLI_addtail(&wgroup->widgets, widget); + return true; +} + +void WM_widget_set_origin(struct wmWidget *widget, float origin[3]) +{ + copy_v3_v3(widget->origin, origin); +} + +void WM_widget_set_3d_scale(struct wmWidget *widget, bool scale) +{ + if (scale) { + widget->flag |= WM_WIDGET_SCALE_3D; + } + else { + widget->flag &= ~WM_WIDGET_SCALE_3D; + } +} + + +void WM_widget_set_draw_on_hover_only(struct wmWidget *widget, bool draw) +{ + if (draw) { + widget->flag |= WM_WIDGET_DRAW_HOVER; + } + else { + widget->flag &= ~WM_WIDGET_DRAW_HOVER; + } +} + +void WM_widget_set_scene_depth(struct wmWidget *widget, bool scene) +{ + if (scene) { + widget->flag |= WM_WIDGET_SCENE_DEPTH; + } + else { + widget->flag &= ~WM_WIDGET_SCENE_DEPTH; + } +} + + +void WM_widget_set_scale(struct wmWidget *widget, float scale) +{ + widget->user_scale = scale; +} + + +wmWidgetMapType *WM_widgetmaptype_find(const char *idname, int spaceid, int regionid, bool is_3d, bool create) +{ + wmWidgetMapType *wmaptype; + + for (wmaptype = widgetmaptypes.first; wmaptype; wmaptype = wmaptype->next) { + if (wmaptype->spaceid == spaceid && wmaptype->regionid == regionid && wmaptype->is_3d == is_3d + && strcmp(wmaptype->idname, idname) == 0) { + return wmaptype; + } + } + + if (!create) return NULL; + + wmaptype = MEM_callocN(sizeof(wmWidgetMapType), "widgettype list"); + wmaptype->spaceid = spaceid; + wmaptype->regionid = regionid; + wmaptype->is_3d = is_3d; + BLI_strncpy(wmaptype->idname, idname, 64); + BLI_addhead(&widgetmaptypes, wmaptype); + + return wmaptype; +} + +void WM_widgetmaptypes_free(void) +{ + wmWidgetMapType *wmaptype; + + for (wmaptype = widgetmaptypes.first; wmaptype; wmaptype = wmaptype->next) { + BLI_freelistN(&wmaptype->widgetgrouptypes); + } + BLI_freelistN(&widgetmaptypes); + + fix_linking_widget_lib(); +} + +bool wm_widgetmap_is_3d(struct wmWidgetMap *wmap) +{ + return wmap->type->is_3d; +} + +static void widget_find_active_3D_loop(bContext *C, ListBase *visible_widgets) +{ + int selectionbase = 0; + LinkData *link; + wmWidget *widget; + + for (link = visible_widgets->first; link; link = link->next) { + widget = link->data; + /* pass the selection id shifted by 8 bits. Last 8 bits are used for selected widget part id */ + widget->render_3d_intersection(C, widget, selectionbase << 8); + + selectionbase++; + } +} + +static int wm_widget_find_highlighted_3D_intern (ListBase *visible_widgets, bContext *C, const struct wmEvent *event, float hotspot) +{ + ScrArea *sa = CTX_wm_area(C); + ARegion *ar = CTX_wm_region(C); + View3D *v3d = sa->spacedata.first; + RegionView3D *rv3d = ar->regiondata; + rctf rect, selrect; + GLuint buffer[64]; // max 4 items per select, so large enuf + short hits; + const bool do_passes = GPU_select_query_check_active(); + + extern void view3d_winmatrix_set(ARegion *ar, View3D *v3d, rctf *rect); + + rect.xmin = event->mval[0] - hotspot; + rect.xmax = event->mval[0] + hotspot; + rect.ymin = event->mval[1] - hotspot; + rect.ymax = event->mval[1] + hotspot; + + selrect = rect; + + view3d_winmatrix_set(ar, v3d, &rect); + mul_m4_m4m4(rv3d->persmat, rv3d->winmat, rv3d->viewmat); + + if (do_passes) + GPU_select_begin(buffer, 64, &selrect, GPU_SELECT_NEAREST_FIRST_PASS, 0); + else + GPU_select_begin(buffer, 64, &selrect, GPU_SELECT_ALL, 0); + /* do the drawing */ + widget_find_active_3D_loop(C, visible_widgets); + + hits = GPU_select_end(); + + if (do_passes) { + GPU_select_begin(buffer, 64, &selrect, GPU_SELECT_NEAREST_SECOND_PASS, hits); + widget_find_active_3D_loop(C, visible_widgets); + GPU_select_end(); + } + + view3d_winmatrix_set(ar, v3d, NULL); + mul_m4_m4m4(rv3d->persmat, rv3d->winmat, rv3d->viewmat); + + if (hits == 1) { + return buffer[3]; + + } + /* find the widget the value belongs to */ + else if (hits > 1) { + GLuint val, dep, mindep = 0, minval = -1; + int a; + + /* we compare the hits in buffer, but value centers highest */ + /* we also store the rotation hits separate (because of arcs) and return hits on other widgets if there are */ + + for (a = 0; a < hits; a++) { + dep = buffer[4 * a + 1]; + val = buffer[4 * a + 3]; + + if (minval == -1 || dep < mindep) { + mindep = dep; + minval = val; + } + } + + return minval; + } + return -1; +} + +static void wm_prepare_visible_widgets_3D(struct wmWidgetMap *wmap, ListBase *visible_widgets, bContext *C) +{ + wmWidget *widget; + wmWidgetGroup *wgroup; + + for (wgroup = wmap->widgetgroups.first; wgroup; wgroup = wgroup->next) { + if (!wgroup->type->poll || wgroup->type->poll(C, wgroup->type)) { + for (widget = wgroup->widgets.first; widget; widget = widget->next) { + if (widget->render_3d_intersection) { + BLI_addhead(visible_widgets, BLI_genericNodeN(widget)); + } + } + } + } +} + +wmWidget *wm_widget_find_highlighted_3D(struct wmWidgetMap *wmap, bContext *C, const struct wmEvent *event, unsigned char *part) +{ + int ret; + wmWidget *result = NULL; + + ListBase visible_widgets = {0}; + + wm_prepare_visible_widgets_3D(wmap, &visible_widgets, C); + + *part = 0; + /* set up view matrices */ + view3d_operator_needs_opengl(C); + + ret = wm_widget_find_highlighted_3D_intern(&visible_widgets, C, event, 0.5f * (float)U.tw_hotspot); + + if (ret != -1) { + LinkData *link; + int retsec; + retsec = wm_widget_find_highlighted_3D_intern(&visible_widgets, C, event, 0.2f * (float)U.tw_hotspot); + + if (retsec != -1) + ret = retsec; + + link = BLI_findlink(&visible_widgets, ret >> 8); + *part = ret & 255; + result = link->data; + } + + BLI_freelistN(&visible_widgets); + + return result; +} + +wmWidget *wm_widget_find_highlighted(struct wmWidgetMap *wmap, bContext *C, const struct wmEvent *event, unsigned char *part) +{ + wmWidget *widget; + wmWidgetGroup *wgroup; + + for (wgroup = wmap->widgetgroups.first; wgroup; wgroup = wgroup->next) { + if (!wgroup->type->poll || wgroup->type->poll(C, wgroup->type)) { + for (widget = wgroup->widgets.first; widget; widget = widget->next) { + if (widget->intersect) { + if ((*part = widget->intersect(C, event, widget))) + return widget; + } + } + } + } + + return NULL; +} + +bool WM_widgetmap_cursor_set(wmWidgetMap *wmap, wmWindow *win) +{ + for (; wmap; wmap = wmap->next) { + wmWidget *widget = wmap->highlighted_widget; + if (widget && widget->get_cursor) { + WM_cursor_set(win, widget->get_cursor(widget)); + return true; + } + } + + return false; +} + +void wm_widgetmap_set_highlighted_widget(struct wmWidgetMap *wmap, struct bContext *C, struct wmWidget *widget, unsigned char part) +{ + if ((widget != wmap->highlighted_widget) || (widget && part != widget->highlighted_part)) { + if (wmap->highlighted_widget) { + wmap->highlighted_widget->flag &= ~WM_WIDGET_HIGHLIGHT; + wmap->highlighted_widget->highlighted_part = 0; + } + + wmap->highlighted_widget = widget; + + if (widget) { + widget->flag |= WM_WIDGET_HIGHLIGHT; + widget->highlighted_part = part; + + if (C && widget->get_cursor) { + wmWindow *win = CTX_wm_window(C); + WM_cursor_set(win, widget->get_cursor(widget)); + } + } + else if (C) { + wmWindow *win = CTX_wm_window(C); + WM_cursor_set(win, CURSOR_STD); + } + + /* tag the region for redraw */ + if (C) { + ARegion *ar = CTX_wm_region(C); + ED_region_tag_redraw(ar); + } + } +} + +struct wmWidget *wm_widgetmap_get_highlighted_widget(struct wmWidgetMap *wmap) +{ + return wmap->highlighted_widget; +} + +void wm_widgetmap_set_active_widget(struct wmWidgetMap *wmap, struct bContext *C, struct wmEvent *event, struct wmWidget *widget, bool call_op) +{ + if (widget) { + if (call_op) { + wmOperatorType *ot; + const char *opname = (widget->opname) ? widget->opname : "WM_OT_widget_tweak"; + + ot = WM_operatortype_find(opname, 0); + + if (ot) { + /* first activate the widget itself */ + if (widget->invoke && widget->handler) { + widget->flag |= WM_WIDGET_ACTIVE; + widget->invoke(C, event, widget); + wmap->active_widget = widget; + } + + /* if operator runs modal, we will need to activate the current widgetmap on the operator handler, so it can + * process events first, then pass them on to the operator */ + if (WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &widget->opptr) == OPERATOR_RUNNING_MODAL) { + /* check if operator added a a modal event handler */ + wmEventHandler *handler = CTX_wm_window(C)->modalhandlers.first; + + if (handler && handler->op && handler->op->type == ot) { + handler->widgetmap = wmap; + } + } + + /* we failed to hook the widget to the operator handler or operator was cancelled, return */ + if (!wmap->active_widget) { + widget->flag &= ~WM_WIDGET_ACTIVE; + /* first activate the widget itself */ + if (widget->interaction_data) { + MEM_freeN(widget->interaction_data); + widget->interaction_data = NULL; + } + } + return; + } + else { + printf("Widget error: operator not found"); + wmap->active_widget = NULL; + return; + } + } + else { + if (widget->invoke && widget->handler) { + widget->flag |= WM_WIDGET_ACTIVE; + widget->invoke(C, event, widget); + wmap->active_widget = widget; + } + } + } + else { + widget = wmap->active_widget; + + /* deactivate, widget but first take care of some stuff */ + if (widget) { + widget->flag &= ~WM_WIDGET_ACTIVE; + /* first activate the widget itself */ + if (widget->interaction_data) { + MEM_freeN(widget->interaction_data); + widget->interaction_data = NULL; + } + } + wmap->active_widget = NULL; + + if (C) { + ARegion *ar = CTX_wm_region(C); + ED_region_tag_redraw(ar); + WM_event_add_mousemove(C); + } + } +} + +struct wmWidget *wm_widgetmap_get_active_widget(struct wmWidgetMap *wmap) +{ + return wmap->active_widget; +} + + +struct wmWidgetMap *WM_widgetmap_from_type(const char *idname, int spaceid, int regionid, bool is_3d) +{ + wmWidgetMapType *wmaptype = WM_widgetmaptype_find(idname, spaceid, regionid, is_3d, true); + wmWidgetGroupType *wgrouptype = wmaptype->widgetgrouptypes.first; + wmWidgetMap *wmap; + + wmap = MEM_callocN(sizeof(wmWidgetMap), "WidgetMap"); + wmap->type = wmaptype; + + /* create all widgetgroups for this widgetmap. We may create an empty one too in anticipation of widgets from operators etc */ + for (; wgrouptype; wgrouptype = wgrouptype->next) { + wmWidgetGroup *wgroup = MEM_callocN(sizeof(wmWidgetGroup), "widgetgroup"); + wgroup->type = wgrouptype; + BLI_addtail(&wmap->widgetgroups, wgroup); + } + + return wmap; +} + +void WM_widgetmap_delete(struct wmWidgetMap *wmap) +{ + wmWidgetGroup *wgroup; + + if (!wmap) + return; + + for (wgroup = wmap->widgetgroups.first; wgroup; wgroup = wgroup->next) { + wmWidget *widget; + + for (widget = wgroup->widgets.first; widget;) { + wmWidget *widget_next = widget->next; + wm_widget_delete(&wgroup->widgets, widget); + widget = widget_next; + } + } + BLI_freelistN(&wmap->widgetgroups); + + MEM_freeN(wmap); +} + +static void wm_widgetgroup_free(bContext *C, wmWidgetMap *wmap, wmWidgetGroup *wgroup) +{ + wmWidget *widget; + + for (widget = wgroup->widgets.first; widget;) { + wmWidget *widget_next = widget->next; + if (widget->flag & WM_WIDGET_HIGHLIGHT) { + wm_widgetmap_set_highlighted_widget(wmap, C, NULL, 0); + } + if (widget->flag & WM_WIDGET_ACTIVE) { + wm_widgetmap_set_active_widget(wmap, C, NULL, NULL, false); + } + wm_widget_delete(&wgroup->widgets, widget); + widget = widget_next; + } + +#ifdef WITH_PYTHON + if (wgroup->py_instance) { + /* do this first in case there are any __del__ functions or + * similar that use properties */ + BPY_DECREF_RNA_INVALIDATE(wgroup->py_instance); + } +#endif + + if (wgroup->reports && (wgroup->reports->flag & RPT_FREE)) { + BKE_reports_clear(wgroup->reports); + MEM_freeN(wgroup->reports); + } + + BLI_remlink(&wmap->widgetgroups, wgroup); + MEM_freeN(wgroup); +} + +void WM_widgetgrouptype_unregister(bContext *C, Main *bmain, wmWidgetGroupType *wgrouptype) +{ + bScreen *sc; + wmWidgetMapType *wmaptype; + + for (sc = bmain->screen.first; sc; sc = sc->id.next) { + ScrArea *sa; + for (sa = sc->areabase.first; sa; sa = sa->next) { + SpaceLink *sl; + + for (sl = sa->spacedata.first; sl; sl = sl->next) { + ARegion *ar; + ListBase *lb = (sl == sa->spacedata.first) ? &sa->regionbase : &sl->regionbase; + + for (ar = lb->first; ar; ar = ar->next) { + wmWidgetMap *wmap; + for (wmap = ar->widgetmaps.first; wmap; wmap = wmap->next) { + wmWidgetGroup *wgroup, *wgroup_tmp; + for (wgroup = wmap->widgetgroups.first; wgroup; wgroup = wgroup_tmp) { + wgroup_tmp = wgroup->next; + if (wgroup->type == wgrouptype) { + wm_widgetgroup_free(C, wmap, wgroup); + ED_region_tag_redraw(ar); + } + } + } + } + } + } + } + + wmaptype = WM_widgetmaptype_find(wgrouptype->mapidname, wgrouptype->spaceid, wgrouptype->regionid, wgrouptype->is_3d, false); + BLI_remlink(&wmaptype->widgetgrouptypes, wgrouptype); + wgrouptype->prev = wgrouptype->next = NULL; + MEM_freeN(wgrouptype); +} + diff --git a/source/blender/windowmanager/wm.h b/source/blender/windowmanager/wm.h index 2f06ddab1e8..a4dad8d48ee 100644 --- a/source/blender/windowmanager/wm.h +++ b/source/blender/windowmanager/wm.h @@ -33,6 +33,12 @@ struct wmWindow; struct ReportList; +struct wmEvent; +struct wmWidgetMap; +struct wmOperatorType; +struct PointerRNA; +struct PropertyRNA; +struct wmOperator; typedef struct wmPaintCursor { struct wmPaintCursor *next, *prev; @@ -43,6 +49,77 @@ typedef struct wmPaintCursor { void (*draw)(bContext *C, int, int, void *customdata); } wmPaintCursor; +/* widgets are set per screen/area/region by registering them on widgetmaps */ +typedef struct wmWidget { + struct wmWidget *next, *prev; + + char idname[64]; + + /* draw widget */ + void (*draw)(struct wmWidget *widget, const struct bContext *C); + /* determine if the mouse intersects with the widget. The calculation should be done in the callback itself */ + int (*intersect)(struct bContext *C, const struct wmEvent *event, struct wmWidget *widget); + + /* determines 3d intersection by rendering the widget in a selection routine. */ + void (*render_3d_intersection)(const struct bContext *C, struct wmWidget *widget, int selectionbase); + + /* handler used by the widget. Usually handles interaction tied to a widget type */ + int (*handler)(struct bContext *C, const struct wmEvent *event, struct wmWidget *widget); + + /* widget-specific handler to update widget attributes when a property is bound */ + void (*bind_to_prop)(struct wmWidget *widget, int slot); + + /* returns the final position which may be different from the origin, depending on the widget. + * used in calculations of scale */ + void (*get_final_position)(struct wmWidget *widget, float vec[3]); + + /* activate a widget state when the user clicks on it */ + int (*invoke)(struct bContext *C, const struct wmEvent *event, struct wmWidget *widget); + + int (*get_cursor)(struct wmWidget *widget); + + int flag; /* flags set by drawing and interaction, such as highlighting */ + + unsigned char highlighted_part; + + /* center of widget in space, 2d or 3d */ + float origin[3]; + + /* runtime property, set the scale while drawing on the viewport */ + float scale; + + /* user defined scale, in addition to the original one */ + float user_scale; + + /* data used during interaction */ + void *interaction_data; + + /* name of operator to spawn when activating the widget */ + const char *opname; + + /* operator properties if widget spawns and controls an operator, or owner pointer if widget spawns and controls a property */ + struct PointerRNA opptr; + + /* maximum number of properties attached to the widget */ + int max_prop; + + /* arrays of properties attached to various widget parameters. As the widget is interacted with, those properties get updated */ + struct PointerRNA *ptr; + struct PropertyRNA **props; +} wmWidget; + +/* wmWidget->flag */ +enum widgetflags { + /* states */ + WM_WIDGET_HIGHLIGHT = (1 << 0), + WM_WIDGET_ACTIVE = (1 << 1), + + WM_WIDGET_DRAW_HOVER = (1 << 2), + + WM_WIDGET_SCALE_3D = (1 << 3), + WM_WIDGET_SCENE_DEPTH = (1 << 4) /* widget is depth culled with scene objects*/ +}; + extern void wm_close_and_free(bContext *C, wmWindowManager *); extern void wm_close_and_free_all(bContext *C, ListBase *); @@ -88,6 +165,11 @@ void wm_stereo3d_set_cancel(bContext *C, wmOperator *op); void wm_open_init_load_ui(wmOperator *op, bool use_prefs); void wm_open_init_use_scripts(wmOperator *op, bool use_prefs); +/* wm_widgets.c */ +bool wm_widgetmap_is_3d(struct wmWidgetMap *wmap); +bool wm_widget_register(struct wmWidgetGroup *wgroup, struct wmWidget *widget); + + /* hack to store circle select size - campbell, must replace with nice operator memory */ #define GESTURE_MEMORY @@ -95,5 +177,7 @@ void wm_open_init_use_scripts(wmOperator *op, bool use_prefs); extern int circle_select_size; #endif +void fix_linking_widget_lib(void); + #endif /* __WM_H__ */ diff --git a/source/blender/windowmanager/wm_event_system.h b/source/blender/windowmanager/wm_event_system.h index efc01b1f8a8..9127d3ec6f0 100644 --- a/source/blender/windowmanager/wm_event_system.h +++ b/source/blender/windowmanager/wm_event_system.h @@ -39,6 +39,8 @@ struct ScrArea; struct ARegion; +struct wmWidgetMap; +struct wmWidget; /* wmKeyMap is in DNA_windowmanager.h, it's savable */ @@ -68,6 +70,8 @@ typedef struct wmEventHandler { /* drop box handler */ ListBase *dropboxes; + /* widget handler */ + struct wmWidgetMap *widgetmap; } wmEventHandler; @@ -96,5 +100,14 @@ void wm_dropbox_free(void); void wm_drags_check_ops(bContext *C, wmEvent *event); void wm_drags_draw(bContext *C, wmWindow *win, rcti *rect); +/* wm_widgets.c */ +struct wmWidget *wm_widget_find_highlighted_3D(struct wmWidgetMap *wmap, struct bContext *C, const struct wmEvent *event, unsigned char *part); +wmWidget *wm_widget_find_highlighted(struct wmWidgetMap *wmap, bContext *C, const struct wmEvent *event, unsigned char *part); +void wm_widgetmap_set_highlighted_widget(struct wmWidgetMap *wmap, struct bContext *C, struct wmWidget *widget, unsigned char part); +struct wmWidget *wm_widgetmap_get_highlighted_widget(struct wmWidgetMap *wmap); + +void wm_widgetmap_set_active_widget(struct wmWidgetMap *wmap, struct bContext *C, struct wmEvent *event, struct wmWidget *widget, bool call_op); +struct wmWidget *wm_widgetmap_get_active_widget(struct wmWidgetMap *wmap); + #endif /* __WM_EVENT_SYSTEM_H__ */ diff --git a/source/blender/windowmanager/wm_event_types.h b/source/blender/windowmanager/wm_event_types.h index ecc29de0e7d..ff4c1874b59 100644 --- a/source/blender/windowmanager/wm_event_types.h +++ b/source/blender/windowmanager/wm_event_types.h @@ -330,6 +330,8 @@ enum { EVT_DROP = 0x5023, EVT_BUT_CANCEL = 0x5024, + EVT_WIDGET_UPDATE = 0x5024, + EVT_WIDGET_RELEASED = 0x5025, /* ********** End of Blender internal events. ********** */ }; diff --git a/source/blenderplayer/CMakeLists.txt b/source/blenderplayer/CMakeLists.txt index 895c3d465fc..4be578e7cd8 100644 --- a/source/blenderplayer/CMakeLists.txt +++ b/source/blenderplayer/CMakeLists.txt @@ -150,6 +150,8 @@ endif() bf_dna ge_videotex bf_blenfont + bf_pointcache_alembic + bf_pointcache bf_intern_audaspace blenkernel_blc bf_bmesh diff --git a/source/blenderplayer/bad_level_call_stubs/stubs.c b/source/blenderplayer/bad_level_call_stubs/stubs.c index 38b58fbb4f7..3310abbc64c 100644 --- a/source/blenderplayer/bad_level_call_stubs/stubs.c +++ b/source/blenderplayer/bad_level_call_stubs/stubs.c @@ -82,6 +82,7 @@ struct HookModifierData; struct NodeBlurData; struct Nurb; struct Object; +struct ParticleSystem; struct PBVHNode; struct PyObject; struct Render; @@ -111,6 +112,7 @@ struct bConstraint; struct bConstraintOb; struct bConstraintTarget; struct bContextDataResult; +struct bFaceMap; struct bNode; struct bNodeType; struct bNodeSocket; @@ -128,6 +130,7 @@ struct wmOperator; struct wmOperatorType; struct wmWindow; struct wmWindowManager; +struct wmWidgetMap; /* -------------------------------------------------------------------- */ @@ -144,6 +147,7 @@ struct wmWindowManager; #include "../../intern/elbeem/extern/elbeem.h" #include "../blender/blenkernel/BKE_modifier.h" #include "../blender/blenkernel/BKE_paint.h" +#include "../blender/blenkernel/BKE_object_deform.h" #include "../blender/collada/collada.h" #include "../blender/compositor/COM_compositor.h" #include "../blender/editors/include/ED_armature.h" @@ -308,6 +312,13 @@ void WM_cursor_modal_restore(struct wmWindow *win) RET_NONE void WM_cursor_time(struct wmWindow *win, int nr) RET_NONE void WM_cursor_warp(struct wmWindow *win, int x, int y) RET_NONE +void WM_widgetmap_delete(struct wmWidgetMap *wmap) RET_NONE +struct wmWidgetMapType *WM_widgetmaptype_find(const char *idname, int spaceid, int regionid, bool is_3d, bool create) RET_NULL +struct wmWidgetGroupType *WM_widgetgrouptype_new(int (*poll)(const struct bContext *, struct wmWidgetGroupType *), + void (*draw)(const struct bContext *, struct wmWidgetGroup *), + struct Main *bmain, const char *mapidname, short spaceid, short regionid, bool is_3d) RET_NULL +void WM_widgetgrouptype_unregister(struct bContext *C, struct Main *bmain, struct wmWidgetGroupType *wgroup) RET_NONE + void WM_uilisttype_init(void) RET_NONE struct uiListType *WM_uilisttype_find(const char *idname, bool quiet) RET_NULL bool WM_uilisttype_add(struct uiListType *ult) RET_ZERO @@ -379,6 +390,8 @@ void ED_fsmenu_entry_set_name(struct FSMenuEntry *fsentry, const char *name) RET struct PTCacheEdit *PE_get_current(struct Scene *scene, struct Object *ob) RET_NULL void PE_current_changed(struct Scene *scene, struct Object *ob) RET_NONE +bool PE_shapekey_load(struct Object *ob, struct ParticleSystem *psys) RET_ZERO +bool PE_shapekey_apply(struct Object *ob, struct ParticleSystem *psys) RET_ZERO /* rna keymap */ struct wmKeyMap *WM_keymap_active(struct wmWindowManager *wm, struct wmKeyMap *keymap) RET_NULL @@ -493,6 +506,8 @@ void ED_object_constraint_tag_update(struct Object *ob, struct bConstraint *con) void ED_vgroup_vert_add(struct Object *ob, struct bDeformGroup *dg, int vertnum, float weight, int assignmode) RET_NONE void ED_vgroup_vert_remove(struct Object *ob, struct bDeformGroup *dg, int vertnum) RET_NONE float ED_vgroup_vert_weight(struct Object *ob, struct bDeformGroup *dg, int vertnum) RET_ZERO +void ED_fmap_face_add(struct Object *ob, struct bFaceMap *fmap, int facenum) RET_NONE +void ED_fmap_face_remove(struct Object *ob, struct bFaceMap *fmap, int facenum) RET_NONE int ED_mesh_mirror_topo_table(struct Object *ob, char mode) RET_ZERO int ED_mesh_mirror_spatial_table(struct Object *ob, struct BMEditMesh *em, const float co[3], char mode) RET_ZERO @@ -596,6 +611,8 @@ void uiTemplateComponentMenu(struct uiLayout *layout, struct PointerRNA *ptr, co void uiTemplateNodeSocket(struct uiLayout *layout, struct bContext *C, float *color) RET_NONE void uiTemplatePalette(struct uiLayout *layout, struct PointerRNA *ptr, const char *propname, int color) RET_NONE void uiTemplateImageStereo3d(struct uiLayout *layout, struct PointerRNA *stereo3d_format_ptr) RET_NONE +struct uiLayout *uiTemplateCacheLibraryItem(struct uiLayout *layout, struct bContext *C, struct CacheLibrary *cachelib, + struct Object *ob, int type, int index, int enabled) RET_NULL /* rna render */ struct RenderResult *RE_engine_begin_result(RenderEngine *engine, int x, int y, int w, int h, const char *layername, const char *viewname) RET_NULL @@ -624,6 +641,7 @@ void RE_engine_update_memory_stats(struct RenderEngine *engine, float mem_used, struct RenderEngine *RE_engine_create(struct RenderEngineType *type) RET_NULL void RE_engine_frame_set(struct RenderEngine *engine, int frame, float subframe) RET_NONE void RE_FreePersistentData(void) RET_NONE +void RE_sample_point_density(struct Scene *scene, struct PointDensity *pd, int resolution, float *values) RET_NONE; /* python */ struct wmOperatorType *WM_operatortype_find(const char *idname, bool quiet) RET_NULL diff --git a/source/creator/CMakeLists.txt b/source/creator/CMakeLists.txt index 1b2dd216904..b70ee0fac70 100644 --- a/source/creator/CMakeLists.txt +++ b/source/creator/CMakeLists.txt @@ -37,6 +37,7 @@ blender_include_dirs( ../blender/imbuf ../blender/render/extern/include ../blender/makesdna + ../blender/pointcache ../blender/gpu ../blender/windowmanager ) diff --git a/source/creator/creator.c b/source/creator/creator.c index 2a96988a0bd..9b88cbe2070 100644 --- a/source/creator/creator.c +++ b/source/creator/creator.c @@ -97,6 +97,8 @@ #include "IMB_imbuf.h" /* for IMB_init */ +#include "PTC_api.h" + #ifdef WITH_PYTHON #include "BPY_extern.h" #endif @@ -1724,6 +1726,9 @@ int main( BKE_brush_system_init(); RE_init_texture_rng(); + /* Initialize ffmpeg if built in, also needed for bg mode if videos are + * rendered via ffmpeg */ + BKE_sound_init_once(); BLI_callback_global_init(); @@ -1768,6 +1773,7 @@ int main( RE_engines_init(); init_nodesystem(); psys_init_rng(); + PTC_alembic_init(); /* end second init */ @@ -1783,10 +1789,6 @@ int main( /* background render uses this font too */ BKE_vfont_builtin_register(datatoc_bfont_pfb, datatoc_bfont_pfb_size); - /* Initialize ffmpeg if built in, also needed for bg mode if videos are - * rendered via ffmpeg */ - BKE_sound_init_once(); - init_def_material(); if (G.background == 0) { diff --git a/source/gameengine/Converter/BL_ShapeDeformer.cpp b/source/gameengine/Converter/BL_ShapeDeformer.cpp index 5e31dabfab1..1dc07b8573d 100644 --- a/source/gameengine/Converter/BL_ShapeDeformer.cpp +++ b/source/gameengine/Converter/BL_ShapeDeformer.cpp @@ -191,7 +191,7 @@ bool BL_ShapeDeformer::Update(void) /* store verts locally */ VerifyStorage(); - per_keyblock_weights = BKE_keyblock_get_per_block_weights(blendobj, m_key, &cache); + per_keyblock_weights = BKE_keyblock_get_per_block_object_weights(blendobj, m_key, &cache); BKE_key_evaluate_relative(0, m_bmesh->totvert, m_bmesh->totvert, (char *)(float *)m_transverts, m_key, NULL, per_keyblock_weights, 0); /* last arg is ignored */ BKE_keyblock_free_per_block_weights(m_key, per_keyblock_weights, &cache); diff --git a/source/gameengine/GamePlayer/ghost/CMakeLists.txt b/source/gameengine/GamePlayer/ghost/CMakeLists.txt index a1bc7e88840..dfa115e085c 100644 --- a/source/gameengine/GamePlayer/ghost/CMakeLists.txt +++ b/source/gameengine/GamePlayer/ghost/CMakeLists.txt @@ -46,6 +46,7 @@ set(INC ../../../blender/imbuf ../../../blender/makesdna ../../../blender/makesrna + ../../../blender/pointcache ../../../../intern/container ../../../../intern/ghost ../../../../intern/glew-mx diff --git a/source/gameengine/GamePlayer/ghost/GPG_ghost.cpp b/source/gameengine/GamePlayer/ghost/GPG_ghost.cpp index faa29e15b65..6d97447dff0 100644 --- a/source/gameengine/GamePlayer/ghost/GPG_ghost.cpp +++ b/source/gameengine/GamePlayer/ghost/GPG_ghost.cpp @@ -80,6 +80,8 @@ extern "C" #include "IMB_imbuf.h" #include "IMB_moviecache.h" + +#include "PTC_api.h" #ifdef __APPLE__ int GHOST_HACK_getFirstFile(char buf[]); @@ -452,6 +454,7 @@ int main(int argc, char** argv) RNA_init(); init_nodesystem(); + PTC_alembic_init(); initglobals(); diff --git a/source/gameengine/GamePlayer/ghost/SConscript b/source/gameengine/GamePlayer/ghost/SConscript index 610abcd0357..27adc000413 100644 --- a/source/gameengine/GamePlayer/ghost/SConscript +++ b/source/gameengine/GamePlayer/ghost/SConscript @@ -57,6 +57,7 @@ incs = [ '#source/blender/include', '#source/blender/makesdna', '#source/blender/makesrna', + '#source/blender/pointcache', '#source/gameengine/Rasterizer', '#source/gameengine/GameLogic', '#source/gameengine/Expressions', |