diff options
Diffstat (limited to 'source/blender')
114 files changed, 9510 insertions, 128 deletions
diff --git a/source/blender/CMakeLists.txt b/source/blender/CMakeLists.txt index 316ab531a05..8a93e9c94d6 100644 --- a/source/blender/CMakeLists.txt +++ b/source/blender/CMakeLists.txt @@ -48,7 +48,9 @@ set(SRC_DNA_INC ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_genfile.h ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_gpencil_types.h ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_gpu_types.h + ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_groom_types.h ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_group_types.h + ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_hair_types.h ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_image_types.h ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_ipo_types.h ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_key_types.h diff --git a/source/blender/blenkernel/BKE_context.h b/source/blender/blenkernel/BKE_context.h index 34192db318e..947ed7cd600 100644 --- a/source/blender/blenkernel/BKE_context.h +++ b/source/blender/blenkernel/BKE_context.h @@ -114,6 +114,7 @@ enum { CTX_MODE_EDIT_ARMATURE, CTX_MODE_EDIT_METABALL, CTX_MODE_EDIT_LATTICE, + CTX_MODE_EDIT_GROOM, CTX_MODE_POSE, CTX_MODE_SCULPT, CTX_MODE_PAINT_WEIGHT, diff --git a/source/blender/blenkernel/BKE_groom.h b/source/blender/blenkernel/BKE_groom.h new file mode 100644 index 00000000000..e88513b5480 --- /dev/null +++ b/source/blender/blenkernel/BKE_groom.h @@ -0,0 +1,125 @@ +/* + * ***** 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_GROOM_H__ +#define __BKE_GROOM_H__ + +/** \file BKE_groom.h + * \ingroup bke + */ + +struct EvaluationContext; +struct Groom; +struct GroomBundle; +struct Main; +struct Object; +struct Scene; + +void BKE_groom_init(struct Groom *groom); +void *BKE_groom_add(struct Main *bmain, const char *name); + +void BKE_groom_free(struct Groom *groom); +void BKE_groom_bundle_curve_cache_clear(struct GroomBundle *bundle); + +void BKE_groom_copy_data(struct Main *bmain, struct Groom *groom_dst, const struct Groom *groom_src, const int flag); +struct Groom *BKE_groom_copy(struct Main *bmain, const struct Groom *groom); + +void BKE_groom_make_local(struct Main *bmain, struct Groom *groom, const bool lib_local); + +bool BKE_groom_minmax(struct Groom *groom, float min[3], float max[3]); +void BKE_groom_boundbox_calc(struct Groom *groom, float r_loc[3], float r_size[3]); + + +/* === Curve cache === */ + +void BKE_groom_curve_cache_update(struct Groom *groom); +void BKE_groom_curve_cache_clear(struct Groom *groom); + + +/* === Scalp regions === */ + +/* Try to bind bundles to their scalp regions */ +void BKE_groom_bind_scalp_regions(struct Groom *groom, bool force_rebind); + +bool BKE_groom_bundle_bind(struct Groom *groom, struct GroomBundle *bundle, bool force_rebind); +void BKE_groom_bundle_unbind(struct GroomBundle *bundle); + + +/* === Hair System === */ + +/* Create follicles and guide curve origins on the scalp surface for hair fiber rendering */ +void BKE_groom_hair_distribute(struct Groom *groom, unsigned int seed, int hair_count, int guide_curve_count); + +/* Calculate guide curve shapes based on groom bundle deformation */ +void BKE_groom_hair_update_guide_curves(struct Groom *groom); + + +/* === Depsgraph evaluation === */ + +void BKE_groom_eval_geometry(const struct EvaluationContext *eval_ctx, struct Groom *groom); + + +/* === Draw Cache === */ + +enum { + BKE_GROOM_BATCH_DIRTY_ALL = 0, + BKE_GROOM_BATCH_DIRTY_SELECT, +}; +void BKE_groom_batch_cache_dirty(struct Groom *groom, int mode); +void BKE_groom_batch_cache_free(struct Groom *groom); + +/* === Iterators === */ + +/* Utility class for iterating over groom elements */ +typedef struct GroomIterator +{ + int isection; /* section index */ + struct GroomSection *section; /* section data pointer */ + + int ivertex; /* vertex index */ + int isectionvertex; /* vertex index for the inner loop */ + struct GroomSectionVertex *vertex; /* vertex data pointer */ +} GroomIterator; + +#define GROOM_ITER_SECTIONS(iter, bundle) \ + for (iter.isection = 0, iter.section = (bundle)->sections; \ + iter.isection < (bundle)->totsections; \ + ++iter.isection, ++iter.section) + +#define GROOM_ITER_SECTION_LOOPS(iter, sectionvar, vertexvar, bundle) \ + for (iter.isection = 0, iter.section = (bundle)->sections, iter.ivertex = 0, iter.vertex = (bundle)->verts; \ + iter.isection < (bundle)->totsections; \ + ++iter.isection, ++iter.section) \ + for (iter.isectionvertex = 0; \ + iter.isectionvertex < (bundle)->numloopverts; \ + ++iter.isectionvertex, ++iter.vertex) + +/* === Utility functions (DerivedMesh SOON TO BE DEPRECATED!) === */ +struct DerivedMesh; +struct DerivedMesh* BKE_groom_get_scalp(struct Groom *groom); + +#endif /* __BKE_GROOM_H__ */ diff --git a/source/blender/blenkernel/BKE_hair.h b/source/blender/blenkernel/BKE_hair.h new file mode 100644 index 00000000000..01e4d3e0abe --- /dev/null +++ b/source/blender/blenkernel/BKE_hair.h @@ -0,0 +1,132 @@ +/* + * ***** 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_HAIR_H__ +#define __BKE_HAIR_H__ + +/** \file blender/blenkernel/BKE_hair.h + * \ingroup bke + */ + +#include "BLI_utildefines.h" + +static const unsigned int HAIR_STRAND_INDEX_NONE = 0xFFFFFFFF; + +struct HairFollicle; +struct HairPattern; +struct HairSystem; +struct HairDrawSettings; +struct DerivedMesh; +struct MeshSample; +struct Object; + +/* Create a new hair system instance */ +struct HairSystem* BKE_hair_new(void); +/* Copy an existing hair system */ +struct HairSystem* BKE_hair_copy(struct HairSystem *hsys); +/* Delete a hair system */ +void BKE_hair_free(struct HairSystem *hsys); + +/* === Guide Strands === */ + +/* Allocate buffers for defining guide curves + * \param totcurves Number of guide curves to allocate + */ +void BKE_hair_guide_curves_begin(struct HairSystem *hsys, int totcurves); + +/* Set properties of a guide curve + * \param index Index of the guide guide curve + * \param mesh_sample Origin of the guide curve on the scalp mesh. + * \param numverts Number of vertices in this guide curve + */ +void BKE_hair_set_guide_curve(struct HairSystem *hsys, int index, const struct MeshSample *mesh_sample, int numverts); + +/* Finalize guide curve update */ +void BKE_hair_guide_curves_end(struct HairSystem *hsys); + +/* Set properties of a guide curve vertex + * \param index Index of the guide curve vertex. + * \param flag Flags to set on the vertex. + * \param co Location of the vertex in object space. + */ +void BKE_hair_set_guide_vertex(struct HairSystem *hsys, int index, int flag, const float co[3]); + +/* === Follicles === */ + +/* Calculate surface area of a scalp mesh */ +float BKE_hair_calc_surface_area(struct DerivedMesh *scalp); + +/* Calculate a density value based on surface area and sample count */ +float BKE_hair_calc_density_from_count(float area, int count); +/* Calculate maximum sample count based on surface area and density */ +int BKE_hair_calc_max_count_from_density(float area, float density); + +/* Calculate a density value based on a minimum distance */ +float BKE_hair_calc_density_from_min_distance(float min_distance); +/* Calculate a minimum distance based on density */ +float BKE_hair_calc_min_distance_from_density(float density); + +/* Distribute hair follicles on a scalp mesh */ +void BKE_hair_generate_follicles( + struct HairSystem* hsys, + struct DerivedMesh *scalp, + unsigned int seed, + int count); + +void BKE_hair_bind_follicles(struct HairSystem *hsys, struct DerivedMesh *scalp); + +/* === Draw Settings === */ + +struct HairDrawSettings* BKE_hair_draw_settings_new(void); +struct HairDrawSettings* BKE_hair_draw_settings_copy(struct HairDrawSettings *draw_settings); +void BKE_hair_draw_settings_free(struct HairDrawSettings *draw_settings); + +/* === Draw Cache === */ + +enum { + BKE_HAIR_BATCH_DIRTY_FIBERS = (1 << 0), + BKE_HAIR_BATCH_DIRTY_STRANDS = (1 << 1), + BKE_HAIR_BATCH_DIRTY_ALL = 0xFFFF, +}; +void BKE_hair_batch_cache_dirty(struct HairSystem* hsys, int mode); +void BKE_hair_batch_cache_free(struct HairSystem* hsys); + +int* BKE_hair_get_fiber_lengths(const struct HairSystem* hsys, int subdiv); +void BKE_hair_get_texture_buffer_size( + const struct HairSystem* hsys, + int subdiv, + int *r_size, + int *r_strand_map_start, + int *r_strand_vertex_start, + int *r_fiber_start); +void BKE_hair_get_texture_buffer( + const struct HairSystem* hsys, + struct DerivedMesh *scalp, + int subdiv, + void *texbuffer); + +#endif diff --git a/source/blender/blenkernel/BKE_library.h b/source/blender/blenkernel/BKE_library.h index f3df8b9b363..5abf9139226 100644 --- a/source/blender/blenkernel/BKE_library.h +++ b/source/blender/blenkernel/BKE_library.h @@ -159,7 +159,7 @@ void id_clear_lib_data_ex(struct Main *bmain, struct ID *id, const bool id_in_ma struct ListBase *which_libbase(struct Main *mainlib, short type); -#define MAX_LIBARRAY 37 +#define MAX_LIBARRAY 38 int set_listbasepointers(struct Main *main, struct ListBase *lb[MAX_LIBARRAY]); /* Main API */ diff --git a/source/blender/blenkernel/BKE_main.h b/source/blender/blenkernel/BKE_main.h index aac43768acf..3e767e85142 100644 --- a/source/blender/blenkernel/BKE_main.h +++ b/source/blender/blenkernel/BKE_main.h @@ -127,6 +127,7 @@ typedef struct Main { ListBase linestyle; ListBase cachefiles; ListBase workspaces; + ListBase grooms; char id_tag_update[MAX_LIBARRAY]; diff --git a/source/blender/blenkernel/BKE_mesh_sample.h b/source/blender/blenkernel/BKE_mesh_sample.h new file mode 100644 index 00000000000..94560d8c017 --- /dev/null +++ b/source/blender/blenkernel/BKE_mesh_sample.h @@ -0,0 +1,113 @@ +/* + * ***** 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 MFace; +struct MVert; + +struct MeshSample; +struct MeshSampleGenerator; + +typedef struct MeshSampleGenerator MeshSampleGenerator; +typedef float (*MeshSampleVertexWeightFp)(struct DerivedMesh *dm, struct MVert *vert, unsigned int index, void *userdata); +typedef void* (*MeshSampleThreadContextCreateFp)(void *userdata, int start); +typedef void (*MeshSampleThreadContextFreeFp)(void *userdata, void *thread_ctx); +typedef bool (*MeshSampleRayFp)(void *userdata, void *thread_ctx, float ray_start[3], float ray_end[3]); + +/* ==== Utility Functions ==== */ + +float* BKE_mesh_sample_calc_triangle_weights(struct DerivedMesh *dm, MeshSampleVertexWeightFp vertex_weight_cb, void *userdata, float *r_area); + +void BKE_mesh_sample_weights_from_loc(struct MeshSample *sample, struct DerivedMesh *dm, int face_index, const float loc[3]); + + +/* ==== Evaluate ==== */ + +bool BKE_mesh_sample_is_valid(const struct MeshSample *sample); +bool BKE_mesh_sample_is_volume_sample(const struct MeshSample *sample); + +bool BKE_mesh_sample_eval(struct DerivedMesh *dm, const struct MeshSample *sample, float loc[3], float nor[3], float tang[3]); +bool BKE_mesh_sample_shapekey(struct Key *key, struct KeyBlock *kb, const struct MeshSample *sample, float loc[3]); + +void BKE_mesh_sample_clear(struct MeshSample *sample); + + +/* ==== Generator Types ==== */ + +struct MeshSampleGenerator *BKE_mesh_sample_gen_surface_vertices(void); + +/* vertex_weight_cb is optional */ +struct MeshSampleGenerator *BKE_mesh_sample_gen_surface_random(unsigned int seed, bool use_area_weight, + MeshSampleVertexWeightFp vertex_weight_cb, void *userdata); + +struct MeshSampleGenerator *BKE_mesh_sample_gen_surface_raycast( + MeshSampleThreadContextCreateFp thread_context_create_cb, + MeshSampleThreadContextFreeFp thread_context_free_cb, + MeshSampleRayFp ray_cb, + void *userdata); + +struct MeshSampleGenerator *BKE_mesh_sample_gen_surface_poissondisk(unsigned int seed, float mindist, unsigned int max_samples, + MeshSampleVertexWeightFp vertex_weight_cb, void *userdata); + +struct MeshSampleGenerator *BKE_mesh_sample_gen_volume_random_bbray(unsigned int seed, float density); + +void BKE_mesh_sample_free_generator(struct MeshSampleGenerator *gen); + + +/* ==== Sampling ==== */ + +void BKE_mesh_sample_generator_bind(struct MeshSampleGenerator *gen, struct DerivedMesh *dm); +void BKE_mesh_sample_generator_unbind(struct MeshSampleGenerator *gen); + +unsigned int BKE_mesh_sample_gen_get_max_samples(const struct MeshSampleGenerator *gen); + +/* Generate a single sample. + * Not threadsafe! + */ +bool BKE_mesh_sample_generate(struct MeshSampleGenerator *gen, struct MeshSample *sample); + +/* Generate a large number of samples. + */ +int BKE_mesh_sample_generate_batch_ex(struct MeshSampleGenerator *gen, + void *output_buffer, int output_stride, int count, + bool use_threads); + +int BKE_mesh_sample_generate_batch(struct MeshSampleGenerator *gen, + MeshSample *output_buffer, int count); + +/* ==== Utilities ==== */ + +struct ParticleSystem; +struct ParticleData; +struct BVHTreeFromMesh; + +bool BKE_mesh_sample_from_particle(struct MeshSample *sample, struct ParticleSystem *psys, struct DerivedMesh *dm, struct ParticleData *pa); +bool BKE_mesh_sample_to_particle(struct MeshSample *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_particle.h b/source/blender/blenkernel/BKE_particle.h index 8cd76097401..dd563617674 100644 --- a/source/blender/blenkernel/BKE_particle.h +++ b/source/blender/blenkernel/BKE_particle.h @@ -425,6 +425,7 @@ void psys_interpolate_face(struct MVert *mvert, struct MFace *mface, struct MTFa 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/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt index 6a1c3ea883c..b6250ad86f4 100644 --- a/source/blender/blenkernel/CMakeLists.txt +++ b/source/blender/blenkernel/CMakeLists.txt @@ -115,7 +115,10 @@ set(SRC intern/font.c intern/freestyle.c intern/gpencil.c + intern/groom.c intern/group.c + intern/hair.c + intern/hair_draw.c intern/icons.c intern/idcode.c intern/idprop.c @@ -141,6 +144,7 @@ set(SRC intern/mesh_evaluate.c intern/mesh_mapping.c intern/mesh_remap.c + intern/mesh_sample.c intern/mesh_tangent.c intern/mesh_validate.c intern/modifier.c @@ -252,7 +256,9 @@ set(SRC BKE_freestyle.h BKE_global.h BKE_gpencil.h + BKE_groom.h BKE_group.h + BKE_hair.h BKE_icons.h BKE_idcode.h BKE_idprop.h @@ -275,6 +281,7 @@ set(SRC BKE_mesh.h BKE_mesh_mapping.h BKE_mesh_remap.h + BKE_mesh_sample.h BKE_mesh_tangent.h BKE_modifier.h BKE_movieclip.h diff --git a/source/blender/blenkernel/intern/context.c b/source/blender/blenkernel/intern/context.c index d550f1945db..809db09eb86 100644 --- a/source/blender/blenkernel/intern/context.c +++ b/source/blender/blenkernel/intern/context.c @@ -1016,6 +1016,8 @@ int CTX_data_mode_enum_ex(const Object *obedit, const Object *ob, const eObjectM return CTX_MODE_EDIT_METABALL; case OB_LATTICE: return CTX_MODE_EDIT_LATTICE; + case OB_GROOM: + return CTX_MODE_EDIT_GROOM; } } else { @@ -1050,6 +1052,7 @@ static const char *data_mode_strings[] = { "armature_edit", "mball_edit", "lattice_edit", + "groom_edit", "posemode", "sculpt_mode", "weightpaint", diff --git a/source/blender/blenkernel/intern/groom.c b/source/blender/blenkernel/intern/groom.c new file mode 100644 index 00000000000..e64fd5ca297 --- /dev/null +++ b/source/blender/blenkernel/intern/groom.c @@ -0,0 +1,865 @@ +/* + * ***** 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/gpencil.c + * \ingroup bke + */ + + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + +#include "MEM_guardedalloc.h" + +#include "BLI_blenlib.h" +#include "BLI_math.h" +#include "BLI_utildefines.h" +#include "BLI_string_utils.h" + +#include "BLT_translation.h" + +#include "DNA_groom_types.h" +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_object_types.h" +#include "DNA_scene_types.h" + +#include "BKE_animsys.h" +#include "BKE_customdata.h" +#include "BKE_cdderivedmesh.h" +#include "BKE_global.h" +#include "BKE_groom.h" +#include "BKE_hair.h" +#include "BKE_bvhutils.h" +#include "BKE_library.h" +#include "BKE_main.h" +#include "BKE_mesh_sample.h" +#include "BKE_object.h" +#include "BKE_object_facemap.h" + +#include "DEG_depsgraph.h" + +#include "bmesh.h" + + +void BKE_groom_init(Groom *groom) +{ + BLI_assert(MEMCMP_STRUCT_OFS_IS_ZERO(groom, id)); + + groom->bb = BKE_boundbox_alloc_unit(); + + groom->curve_res = 12; + + groom->hair_system = BKE_hair_new(); + groom->hair_draw_settings = BKE_hair_draw_settings_new(); +} + +void *BKE_groom_add(Main *bmain, const char *name) +{ + Groom *groom = BKE_libblock_alloc(bmain, ID_GM, name, 0); + + BKE_groom_init(groom); + + return groom; +} + +void BKE_groom_bundle_curve_cache_clear(GroomBundle *bundle) +{ + if (bundle->curvecache) + { + MEM_freeN(bundle->curvecache); + bundle->curvecache = NULL; + bundle->curvesize = 0; + bundle->totcurvecache = 0; + } +} + +static void groom_bundles_free(ListBase *bundles) +{ + for (GroomBundle *bundle = bundles->first; bundle; bundle = bundle->next) + { + BKE_groom_bundle_curve_cache_clear(bundle); + + if (bundle->sections) + { + MEM_freeN(bundle->sections); + } + if (bundle->verts) + { + MEM_freeN(bundle->verts); + } + } + BLI_freelistN(bundles); +} + +/** Free (or release) any data used by this groom (does not free the groom itself). */ +void BKE_groom_free(Groom *groom) +{ + BKE_groom_batch_cache_free(groom); + + if (groom->editgroom) + { + EditGroom *edit = groom->editgroom; + + groom_bundles_free(&edit->bundles); + + MEM_freeN(edit); + groom->editgroom = NULL; + } + + MEM_SAFE_FREE(groom->bb); + + if (groom->hair_system) + { + BKE_hair_free(groom->hair_system); + } + if (groom->hair_draw_settings) + { + BKE_hair_draw_settings_free(groom->hair_draw_settings); + } + + groom_bundles_free(&groom->bundles); + + BKE_animdata_free(&groom->id, false); +} + +/** + * Only copy internal data of Groom ID from source to already allocated/initialized destination. + * You probably never want to use that directly, use id_copy or BKE_id_copy_ex for typical needs. + * + * WARNING! This function will not handle ID user count! + * + * \param flag Copying options (see BKE_library.h's LIB_ID_COPY_... flags for more). + */ +void BKE_groom_copy_data(Main *UNUSED(bmain), Groom *groom_dst, const Groom *groom_src, const int UNUSED(flag)) +{ + groom_dst->bb = MEM_dupallocN(groom_src->bb); + + BLI_duplicatelist(&groom_dst->bundles, &groom_src->bundles); + for (GroomBundle *bundle = groom_dst->bundles.first; bundle; bundle = bundle->next) + { + if (bundle->curvecache) + { + bundle->curvecache = MEM_dupallocN(bundle->curvecache); + } + if (bundle->sections) + { + bundle->sections = MEM_dupallocN(bundle->sections); + } + if (bundle->verts) + { + bundle->verts = MEM_dupallocN(bundle->verts); + } + } + + groom_dst->editgroom = NULL; + + if (groom_dst->hair_system) + { + groom_dst->hair_system = BKE_hair_copy(groom_dst->hair_system); + } + if (groom_dst->hair_draw_settings) + { + groom_dst->hair_draw_settings = BKE_hair_draw_settings_copy(groom_dst->hair_draw_settings); + } +} + +Groom *BKE_groom_copy(Main *bmain, const Groom *groom) +{ + Groom *groom_copy; + BKE_id_copy_ex(bmain, &groom->id, (ID **)&groom_copy, 0, false); + return groom_copy; +} + +void BKE_groom_make_local(Main *bmain, Groom *groom, const bool lib_local) +{ + BKE_id_make_local_generic(bmain, &groom->id, true, lib_local); +} + + +bool BKE_groom_minmax(Groom *groom, float min[3], float max[3]) +{ + // TODO + UNUSED_VARS(groom, min, max); + return true; +} + +void BKE_groom_boundbox_calc(Groom *groom, float r_loc[3], float r_size[3]) +{ + if (groom->bb == NULL) + { + groom->bb = MEM_callocN(sizeof(BoundBox), "boundbox"); + } + + float mloc[3], msize[3]; + if (!r_loc) + { + r_loc = mloc; + } + if (!r_size) + { + r_size = msize; + } + + float min[3], max[3]; + INIT_MINMAX(min, max); + if (!BKE_groom_minmax(groom, min, max)) { + min[0] = min[1] = min[2] = -1.0f; + max[0] = max[1] = max[2] = 1.0f; + } + + mid_v3_v3v3(r_loc, min, max); + + r_size[0] = (max[0] - min[0]) / 2.0f; + r_size[1] = (max[1] - min[1]) / 2.0f; + r_size[2] = (max[2] - min[2]) / 2.0f; + + BKE_boundbox_init_from_minmax(groom->bb, min, max); + groom->bb->flag &= ~BOUNDBOX_DIRTY; +} + + +/* === Scalp regions === */ + +void BKE_groom_bind_scalp_regions(Groom *groom, bool force_rebind) +{ + if (groom->editgroom) + { + for (GroomBundle *bundle = groom->editgroom->bundles.first; bundle; bundle = bundle->next) + { + BKE_groom_bundle_bind(groom, bundle, force_rebind); + } + } + else + { + for (GroomBundle *bundle = groom->bundles.first; bundle; bundle = bundle->next) + { + BKE_groom_bundle_bind(groom, bundle, force_rebind); + } + } +} + +static bool groom_shape_rebuild(GroomBundle *bundle, int numshapeverts, Object *scalp_ob) +{ + BLI_assert(bundle->scalp_region != NULL); + BLI_assert(scalp_ob->type == OB_MESH); + + bool result = true; + float (*shape)[2] = MEM_mallocN(sizeof(*shape) * numshapeverts, "groom section shape"); + + Mesh *me = scalp_ob->data; + // XXX MeshSample will use Mesh instead of DerivedMesh in the future + DerivedMesh *dm = CDDM_from_mesh(me); + + /* last sample is the center position */ + MeshSample *center_sample = &bundle->scalp_region[numshapeverts]; + float center_co[3], center_nor[3], center_tang[3], center_binor[3]; + if (!BKE_mesh_sample_eval(dm, center_sample, center_co, center_nor, center_tang)) + { + result = false; + goto cleanup; + } + cross_v3_v3v3(center_binor, center_nor, center_tang); + + MeshSample *sample = bundle->scalp_region; + GroomSectionVertex *vert0 = bundle->verts; + for (int i = 0; i < numshapeverts; ++i, ++sample, ++vert0) + { + /* 3D position of the shape vertex origin on the mesh */ + float co[3], nor[3], tang[3]; + if (!BKE_mesh_sample_eval(dm, sample, co, nor, tang)) + { + result = false; + goto cleanup; + } + /* Get relative offset from the center */ + sub_v3_v3(co, center_co); + /* Convert mesh surface positions to 2D shape + * by projecting onto the normal plane + */ + shape[i][0] = dot_v3v3(co, center_binor); + shape[i][1] = dot_v3v3(co, center_tang); + } + + bundle->numshapeverts = numshapeverts; + bundle->totverts = numshapeverts * bundle->totsections; + bundle->verts = MEM_reallocN_id(bundle->verts, sizeof(*bundle->verts) * bundle->totverts, "groom bundle vertices"); + /* Set the shape for all sections */ + GroomSectionVertex *vert = bundle->verts; + for (int i = 0; i < bundle->totsections; ++i) + { + for (int j = 0; j < numshapeverts; ++j, ++vert) + { + copy_v2_v2(vert->co, shape[j]); + vert->flag = 0; + } + } + +cleanup: + MEM_freeN(shape); + dm->release(dm); + + return result; +} + +static BMesh *groom_create_scalp_bmesh(Mesh *me) +{ + const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_ME(me); + + BMesh *bm = BM_mesh_create(&allocsize, &((struct BMeshCreateParams){ + .use_toolflags = true, + })); + + BM_mesh_bm_from_me(bm, me, (&(struct BMeshFromMeshParams){ + .calc_face_normal = true, .use_shapekey = false, + })); + + return bm; +} + +static bool groom_bundle_region_from_mesh_fmap(GroomBundle *bundle, Object *scalp_ob) +{ + BLI_assert(scalp_ob->type == OB_MESH); + + BKE_groom_bundle_curve_cache_clear(bundle); + + Mesh *me = scalp_ob->data; + const int scalp_fmap_nr = BKE_object_facemap_name_index(scalp_ob, bundle->scalp_facemap_name); + const int cd_fmap_offset = CustomData_get_offset(&me->pdata, CD_FACEMAP); + if (scalp_fmap_nr < 0 || cd_fmap_offset < 0) + { + return false; + } + + BMesh *bm = groom_create_scalp_bmesh(me); + bool result = true; + + /* Tag faces in the face map for the BMO walker */ + { + BMFace *f; + BMIter iter; + BM_ITER_MESH(f, &iter, bm, BM_FACES_OF_MESH) + { + int fmap = BM_ELEM_CD_GET_INT(f, cd_fmap_offset); + BM_elem_flag_set(f, BM_ELEM_TAG, fmap == scalp_fmap_nr); + } + } + + BMOperator op; + BMO_op_initf(bm, &op, (BMO_FLAG_DEFAULTS & ~BMO_FLAG_RESPECT_HIDE), + "face_island_boundary faces=%hf", BM_ELEM_TAG); + + BMO_op_exec(bm, &op); + if (BMO_error_occurred(bm)) + { + result = false; + goto finalize; + } + + const int numshapeverts = BMO_slot_buffer_count(op.slots_out, "boundary"); + bundle->scalp_region = MEM_callocN(sizeof(*bundle->scalp_region) * (numshapeverts + 1), "groom bundle scalp region"); + + float center_co[3]; /* average vertex location for placing the center */ + { + BMLoop *l; + BMOIter oiter; + MeshSample *sample = bundle->scalp_region; + zero_v3(center_co); + BMO_ITER (l, &oiter, op.slots_out, "boundary", BM_LOOP) + { + sample->orig_poly = BM_elem_index_get(l->f); + sample->orig_loops[0] = BM_elem_index_get(l); + sample->orig_verts[0] = BM_elem_index_get(l->v); + sample->orig_weights[0] = 1.0f; + BLI_assert(BKE_mesh_sample_is_valid(sample)); + + add_v3_v3(center_co, l->v->co); + + ++sample; + } + if (numshapeverts > 0) + { + mul_v3_fl(center_co, 1.0f / (float)(numshapeverts)); + } + } + + { + /* BVH tree for binding the region center location */ + DerivedMesh *dm = CDDM_from_mesh(me); + DM_ensure_tessface(dm); + BVHTreeFromMesh bvhtree; + //bvhtree_from_mesh_looptri(&bvhtree, dm, 0.0f, 4, 6); + bvhtree_from_mesh_faces(&bvhtree, dm, 0.0f, 4, 6); + if (bvhtree.tree != NULL) { + BVHTreeNearest nearest; + nearest.index = -1; + nearest.dist_sq = FLT_MAX; + + BLI_bvhtree_find_nearest(bvhtree.tree, center_co, &nearest, bvhtree.nearest_callback, &bvhtree); + if (nearest.index >= 0) + { + /* last sample is the center position */ + MeshSample *center_sample = &bundle->scalp_region[numshapeverts]; + BKE_mesh_sample_weights_from_loc(center_sample, dm, nearest.index, nearest.co); + BLI_assert(BKE_mesh_sample_is_valid(center_sample)); + } + } + else + { + result = false; + } + + free_bvhtree_from_mesh(&bvhtree); + dm->release(dm); + } + +finalize: + if (result == true) + { + groom_shape_rebuild(bundle, numshapeverts, scalp_ob); + } + else + { + if (bundle->scalp_region) + { + MEM_freeN(bundle->scalp_region); + bundle->scalp_region = NULL; + } + } + + BMO_op_finish(bm, &op); + BM_mesh_free(bm); + + return result; +} + +bool BKE_groom_bundle_bind(Groom *groom, GroomBundle *bundle, bool force_rebind) +{ + if (bundle->scalp_region && !force_rebind) + { + return true; + } + + BKE_groom_bundle_unbind(bundle); + if (!groom->scalp_object) + { + return false; + } + if (!BKE_object_facemap_find_name(groom->scalp_object, bundle->scalp_facemap_name)) + { + return false; + } + + if (groom->scalp_object->type == OB_MESH) + { + groom_bundle_region_from_mesh_fmap(bundle, groom->scalp_object); + } + + return (bundle->scalp_region != NULL); +} + +void BKE_groom_bundle_unbind(GroomBundle *bundle) +{ + if (bundle->scalp_region) + { + MEM_freeN(bundle->scalp_region); + bundle->scalp_region = NULL; + } +} + + +/* === Hair System === */ + +/* Distribute points on the scalp to use as guide curve origins, + * then interpolate guide curves from bundles + */ +static void groom_generate_guide_curves( + Groom *groom, + DerivedMesh *scalp, + unsigned int seed, + int guide_curve_count) +{ + struct HairSystem *hsys = groom->hair_system; + + MeshSample *guide_samples = MEM_mallocN(sizeof(*guide_samples) * guide_curve_count, "guide samples"); + int num_guides; + { + /* Random distribution of points on the scalp mesh */ + + float scalp_area = BKE_hair_calc_surface_area(scalp); + float density = BKE_hair_calc_density_from_count(scalp_area, guide_curve_count); + float min_distance = BKE_hair_calc_min_distance_from_density(density); + MeshSampleGenerator *gen = BKE_mesh_sample_gen_surface_poissondisk( + seed, + min_distance, + guide_curve_count, + NULL, + NULL); + + BKE_mesh_sample_generator_bind(gen, scalp); + + static const bool use_threads = false; + num_guides = BKE_mesh_sample_generate_batch_ex( + gen, + guide_samples, + sizeof(MeshSample), + guide_curve_count, + use_threads); + + BKE_mesh_sample_free_generator(gen); + } + + BKE_hair_guide_curves_begin(hsys, num_guides); + + for (int i = 0; i < num_guides; ++i) + { + BKE_hair_set_guide_curve(hsys, i, &guide_samples[i], ); + } + + BKE_hair_guide_curves_end(hsys); + + MEM_freeN(guide_samples); +} + +void BKE_groom_hair_distribute(Groom *groom, unsigned int seed, int hair_count, int guide_curve_count) +{ + struct HairSystem *hsys = groom->hair_system; + + BLI_assert(groom->scalp_object); + DerivedMesh *scalp = object_get_derived_final(groom->scalp_object, false); + if (!scalp) + { + return; + } + + BKE_hair_generate_follicles(hsys, scalp, seed, hair_count); + + unsigned int guide_seed = BLI_ghashutil_combine_hash(seed, BLI_ghashutil_strhash("groom guide curves")); + groom_bundle_generate_guide_curves(groom, scalp, guide_seed, guide_curve_count); +} + + +/* === Curve cache === */ + +/* forward differencing method for cubic polynomial eval */ +static void groom_forward_diff_cubic(float a, float b, float c, float d, float *p, int it, int stride) +{ + float f = (float)it; + a *= 1.0f / (f*f*f); + b *= 1.0f / (f*f); + c *= 1.0f / (f); + + float q0 = d; + float q1 = a + b + c; + float q2 = 6 * a + 2 * b; + float q3 = 6 * a; + + for (int i = 0; i <= it; i++) { + *p = q0; + p = POINTER_OFFSET(p, stride); + q0 += q1; + q1 += q2; + q2 += q3; + } +} + +/* cubic bspline section eval */ +static void groom_eval_curve_cache_section(GroomCurveCache *cache, int curve_res, const float *co0, const float *co1, const float *co2, const float *co3) +{ + float a, b, c, d; + for (int k = 0; k < 3; ++k) + { + /* define tangents from segment direction */ + float n1, n2; + + if (co0) + { + n1 = 0.5f * (co2[k] - co0[k]); + } + else + { + n1 = co2[k] - co1[k]; + } + + if (co3) + { + n2 = 0.5f * (co3[k] - co1[k]); + } + else + { + n2 = co2[k] - co1[k]; + } + + /* Hermite spline interpolation */ + a = 2.0f * (co1[k] - co2[k]) + n1 + n2; + b = 3.0f * (co2[k] - co1[k]) - 2.0f * n1 - n2; + c = n1; + d = co1[k]; + + groom_forward_diff_cubic(a, b, c, d, cache->co + k, curve_res, sizeof(*cache)); + } +} + +static void groom_eval_center_curve_section( + GroomBundle *bundle, + int curve_res) +{ + BLI_assert(bundle->totsections >= 2); + BLI_assert(curve_res >= 1); + + /* last cache curve is the center curve */ + GroomCurveCache *cache = bundle->curvecache + bundle->curvesize * bundle->numshapeverts; + for (int i = 0; i < bundle->totsections-1; ++i, cache += curve_res) + { + const GroomSection *section = &bundle->sections[i]; + const float *co0 = (i > 0) ? section[-1].center : NULL; + const float *co1 = section[0].center; + const float *co2 = section[1].center; + const float *co3 = (i < bundle->totsections - 2) ? section[2].center : NULL; + groom_eval_curve_cache_section(cache, curve_res, co0, co1, co2, co3); + } +} + +static void groom_eval_shape_curves( + GroomBundle *bundle, + int curve_res) +{ + BLI_assert(bundle->totsections >= 2); + BLI_assert(curve_res >= 1); + + for (int i = 0; i < bundle->numshapeverts; ++i) + { + GroomCurveCache *cache = bundle->curvecache + i * bundle->curvesize; + for (int j = 0; j < bundle->totsections-1; ++j, cache += curve_res) + { + const GroomSection *section = &bundle->sections[j]; + const float *co0 = NULL, *co1 = NULL, *co2 =NULL, *co3 = NULL; + + float vec0[3], vec1[3], vec2[3], vec3[3]; + if (j > 0) + { + const GroomSectionVertex *v0 = &bundle->verts[(j-1) * bundle->numshapeverts + i]; + float tmp[3] = {0.0f, 0.0f, 0.0f}; + copy_v2_v2(tmp, v0->co); + mul_v3_m3v3(vec0, section[-1].mat, tmp); + add_v3_v3(vec0, section[-1].center); + co0 = vec0; + } + { + const GroomSectionVertex *v1 = &bundle->verts[(j) * bundle->numshapeverts + i]; + float tmp[3] = {0.0f, 0.0f, 0.0f}; + copy_v2_v2(tmp, v1->co); + mul_v3_m3v3(vec1, section[0].mat, tmp); + add_v3_v3(vec1, section[0].center); + co1 = vec1; + } + { + const GroomSectionVertex *v2 = &bundle->verts[(j+1) * bundle->numshapeverts + i]; + float tmp[3] = {0.0f, 0.0f, 0.0f}; + copy_v2_v2(tmp, v2->co); + mul_v3_m3v3(vec2, section[+1].mat, tmp); + add_v3_v3(vec2, section[+1].center); + co2 = vec2; + } + if (j < bundle->totsections - 2) + { + const GroomSectionVertex *v3 = &bundle->verts[(j+2) * bundle->numshapeverts + i]; + float tmp[3] = {0.0f, 0.0f, 0.0f}; + copy_v2_v2(tmp, v3->co); + mul_v3_m3v3(vec3, section[+2].mat, tmp); + add_v3_v3(vec3, section[+2].center); + co3 = vec3; + } + + groom_eval_curve_cache_section(cache, curve_res, co0, co1, co2, co3); + } + } +} + +static void groom_eval_curve_step(float mat[3][3], const float mat_prev[3][3], const float co0[3], const float co1[3]) +{ + float dir[3]; + sub_v3_v3v3(dir, co1, co0); + normalize_v3(dir); + + float dir_prev[3]; + normalize_v3_v3(dir_prev, mat_prev[2]); + float rot[3][3]; + rotation_between_vecs_to_mat3(rot, dir_prev, dir); + + mul_m3_m3m3(mat, rot, mat_prev); +} + +static void groom_eval_section_mats(GroomBundle *bundle, int curve_res) +{ + const int curvesize = bundle->curvesize; + const int numshapeverts = bundle->numshapeverts; + + float mat[3][3]; + unit_m3(mat); // TODO take from scalp mesh sample + + GroomSection *section = bundle->sections; + /* last curve cache is center curve */ + const GroomCurveCache *cache = bundle->curvecache + bundle->curvesize * numshapeverts; + + /* align to first segment */ + groom_eval_curve_step(mat, mat, cache[0].co, cache[1].co); + copy_m3_m3(section->mat, mat); + ++cache; + ++section; + + for (int i = 1; i < curvesize - 1; ++i, ++cache) + { + /* align interior points to average of prev and next segment */ + groom_eval_curve_step(mat, mat, cache[-1].co, cache[+1].co); + + if (i % curve_res == 0) + { + /* set section matrix */ + copy_m3_m3(section->mat, mat); + ++section; + } + } + + /* align to last segment */ + groom_eval_curve_step(mat, mat, cache[-1].co, cache[0].co); + /* last section is not handled in the loop */ + copy_m3_m3(section->mat, mat); +} + +void BKE_groom_curve_cache_update(Groom *groom) +{ + ListBase *bundles = (groom->editgroom ? &groom->editgroom->bundles : &groom->bundles); + + for (GroomBundle *bundle = bundles->first; bundle; bundle = bundle->next) + { + const int totsections = bundle->totsections; + const int numshapeverts = bundle->numshapeverts; + const int curve_res = groom->curve_res; + if (totsections == 0) + { + BKE_groom_bundle_curve_cache_clear(bundle); + + /* nothing to do */ + continue; + } + + bundle->curvesize = (totsections-1) * curve_res + 1; + bundle->totcurvecache = bundle->curvesize * (numshapeverts + 1); + bundle->curvecache = MEM_reallocN_id(bundle->curvecache, sizeof(GroomCurveCache) * bundle->totcurvecache, "groom bundle curve cache"); + + if (totsections == 1) + { + /* degenerate case */ + copy_v3_v3(bundle->curvecache[numshapeverts].co, bundle->sections[0].center); + + unit_m3(bundle->sections[0].mat); + + for (int i = 0; i < numshapeverts; ++i) + { + copy_v2_v2(bundle->curvecache[i].co, bundle->verts[i].co); + bundle->curvecache[i].co[2] = 0.0f; + } + + continue; + } + + /* Calculate center curve */ + groom_eval_center_curve_section(bundle, curve_res); + + /* Calculate coordinate frames for sections */ + groom_eval_section_mats(bundle, curve_res); + + /* Calculate shape curves */ + groom_eval_shape_curves(bundle, curve_res); + } +} + +void BKE_groom_curve_cache_clear(Groom *groom) +{ + for (GroomBundle *bundle = groom->bundles.first; bundle; bundle = bundle->next) + { + BKE_groom_bundle_curve_cache_clear(bundle); + } + if (groom->editgroom) + { + for (GroomBundle *bundle = groom->editgroom->bundles.first; bundle; bundle = bundle->next) + { + BKE_groom_bundle_curve_cache_clear(bundle); + } + } +} + + +/* === Depsgraph evaluation === */ + +void BKE_groom_eval_geometry(const EvaluationContext *UNUSED(eval_ctx), Groom *groom) +{ + if (G.debug & G_DEBUG_DEPSGRAPH) { + printf("%s on %s\n", __func__, groom->id.name); + } + + /* calculate curves for interpolating shapes */ + BKE_groom_curve_cache_update(groom); + + /* generate actual guide curves for hair */ + BKE_groom_hair_update_guide_curves(groom); + + if (groom->bb == NULL || (groom->bb->flag & BOUNDBOX_DIRTY)) { + BKE_groom_boundbox_calc(groom, NULL, NULL); + } +} + + +/* === Draw Cache === */ + +void (*BKE_groom_batch_cache_dirty_cb)(Groom* groom, int mode) = NULL; +void (*BKE_groom_batch_cache_free_cb)(Groom* groom) = NULL; + +void BKE_groom_batch_cache_dirty(Groom* groom, int mode) +{ + if (groom->batch_cache) + { + BKE_groom_batch_cache_dirty_cb(groom, mode); + } +} + +void BKE_groom_batch_cache_free(Groom *groom) +{ + if (groom->batch_cache) + { + BKE_groom_batch_cache_free_cb(groom); + } +} + +/* === Utility functions (DerivedMesh SOON TO BE DEPRECATED!) === */ + +struct DerivedMesh* BKE_groom_get_scalp(struct Groom *groom) +{ + return groom->scalp_object ? groom->scalp_object->derivedFinal : NULL; +} diff --git a/source/blender/blenkernel/intern/hair.c b/source/blender/blenkernel/intern/hair.c new file mode 100644 index 00000000000..5a9af8aba58 --- /dev/null +++ b/source/blender/blenkernel/intern/hair.c @@ -0,0 +1,388 @@ +/* + * ***** 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/hair.c + * \ingroup bke + */ + +#include <limits.h> +#include <string.h> + +#include "MEM_guardedalloc.h" + +#include "BLI_kdtree.h" +#include "BLI_listbase.h" +#include "BLI_math.h" +#include "BLI_rand.h" +#include "BLI_sort.h" +#include "BLI_string_utf8.h" +#include "BLI_string_utils.h" + +#include "DNA_hair_types.h" +#include "DNA_object_types.h" + +#include "BKE_DerivedMesh.h" +#include "BKE_hair.h" +#include "BKE_library.h" +#include "BKE_mesh.h" +#include "BKE_mesh_sample.h" + +#include "BLT_translation.h" + +HairSystem* BKE_hair_new(void) +{ + HairSystem *hair = MEM_callocN(sizeof(HairSystem), "hair system"); + + hair->pattern = MEM_callocN(sizeof(HairPattern), "hair pattern"); + + hair->material_index = 1; + + return hair; +} + +HairSystem* BKE_hair_copy(HairSystem *hsys) +{ + HairSystem *nhsys = MEM_dupallocN(hsys); + + if (hsys->pattern) + { + nhsys->pattern = MEM_dupallocN(hsys->pattern); + nhsys->pattern->follicles = MEM_dupallocN(hsys->pattern->follicles); + } + + if (hsys->curves) + { + nhsys->curves = MEM_dupallocN(hsys->curves); + } + if (hsys->verts) + { + nhsys->verts = MEM_dupallocN(hsys->verts); + } + + nhsys->draw_batch_cache = NULL; + nhsys->draw_texture_cache = NULL; + + return nhsys; +} + +void BKE_hair_free(HairSystem *hsys) +{ + BKE_hair_batch_cache_free(hsys); + + if (hsys->curves) + { + MEM_freeN(hsys->curves); + } + if (hsys->verts) + { + MEM_freeN(hsys->verts); + } + + if (hsys->pattern) + { + if (hsys->pattern->follicles) + { + MEM_freeN(hsys->pattern->follicles); + } + MEM_freeN(hsys->pattern); + } + + MEM_freeN(hsys); +} + +/* Calculate surface area of a scalp mesh */ +float BKE_hair_calc_surface_area(struct DerivedMesh *scalp) +{ + BLI_assert(scalp != NULL); + + int numpolys = scalp->getNumPolys(scalp); + MPoly *mpolys = scalp->getPolyArray(scalp); + MLoop *mloops = scalp->getLoopArray(scalp); + MVert *mverts = scalp->getVertArray(scalp); + + float area = 0.0f; + for (int i = 0; i < numpolys; ++i) + { + area += BKE_mesh_calc_poly_area(&mpolys[i], mloops + mpolys[i].loopstart, mverts); + } + return area; +} + +/* Calculate a density value based on surface area and sample count */ +float BKE_hair_calc_density_from_count(float area, int count) +{ + return area > 0.0f ? count / area : 0.0f; +} + +/* Calculate maximum sample count based on surface area and density */ +int BKE_hair_calc_max_count_from_density(float area, float density) +{ + return (int)(density * area); +} + +/* Calculate a density value based on a minimum distance */ +float BKE_hair_calc_density_from_min_distance(float min_distance) +{ + // max. circle packing density (sans pi factor): 1 / (2 * sqrt(3)) + static const float max_factor = 0.288675135; + + return min_distance > 0.0f ? max_factor / (min_distance * min_distance) : 0.0f; +} + +/* Calculate a minimum distance based on density */ +float BKE_hair_calc_min_distance_from_density(float density) +{ + // max. circle packing density (sans pi factor): 1 / (2 * sqrt(3)) + static const float max_factor = 0.288675135; + + return density > 0.0f ? sqrt(max_factor / density) : 0.0f; +} + +/* Distribute hair follicles on a scalp mesh */ +void BKE_hair_generate_follicles( + HairSystem* hsys, + struct DerivedMesh *scalp, + unsigned int seed, + int count) +{ + HairPattern *pattern = hsys->pattern; + + // Limit max_count to theoretical limit based on area + float scalp_area = BKE_hair_calc_surface_area(scalp); + float density = BKE_hair_calc_density_from_count(scalp_area, count); + float min_distance = BKE_hair_calc_min_distance_from_density(density); + + if (pattern->follicles) + { + MEM_freeN(pattern->follicles); + } + pattern->follicles = MEM_callocN(sizeof(HairFollicle) * count, "hair follicles"); + + { + MeshSampleGenerator *gen = BKE_mesh_sample_gen_surface_poissondisk(seed, min_distance, count, NULL, NULL); + + BKE_mesh_sample_generator_bind(gen, scalp); + + static const bool use_threads = false; + pattern->num_follicles = BKE_mesh_sample_generate_batch_ex( + gen, + &pattern->follicles->mesh_sample, + sizeof(HairFollicle), + count, + use_threads); + + BKE_mesh_sample_free_generator(gen); + } + + hsys->flag |= HAIR_SYSTEM_UPDATE_FOLLICLE_BINDING; + BKE_hair_batch_cache_dirty(hsys, BKE_HAIR_BATCH_DIRTY_ALL); +} + +/* ================================= */ + +void BKE_hair_guide_curves_begin(HairSystem *hsys, int totcurves) +{ + if (totcurves != hsys->totcurves) + { + hsys->curves = MEM_reallocN(hsys->curves, sizeof(HairGuideCurve) * totcurves); + hsys->totcurves = totcurves; + + hsys->flag |= HAIR_SYSTEM_UPDATE_FOLLICLE_BINDING; + BKE_hair_batch_cache_dirty(hsys, BKE_HAIR_BATCH_DIRTY_ALL); + } +} + +void BKE_hair_set_guide_curve(HairSystem *hsys, int index, const MeshSample *mesh_sample, int numverts) +{ + BLI_assert(index <= hsys->totcurves); + + HairGuideCurve *curve = &hsys->curves[index]; + memcpy(&curve->mesh_sample, mesh_sample, sizeof(MeshSample)); + curve->numverts = numverts; + + hsys->flag |= HAIR_SYSTEM_UPDATE_FOLLICLE_BINDING; + BKE_hair_batch_cache_dirty(hsys, BKE_HAIR_BATCH_DIRTY_ALL); +} + +void BKE_hair_guide_curves_end(HairSystem *hsys) +{ + /* Recalculate vertex count and start offsets in curves */ + int vertstart = 0; + for (int i = 0; i < hsys->totcurves; ++i) + { + hsys->curves[i].vertstart = vertstart; + vertstart += hsys->curves[i].numverts; + } + + if (vertstart != hsys->totverts) + { + hsys->verts = MEM_reallocN(hsys->verts, sizeof(HairGuideVertex) * vertstart); + hsys->totverts = vertstart; + + BKE_hair_batch_cache_dirty(hsys, BKE_HAIR_BATCH_DIRTY_ALL); + } +} + +void BKE_hair_set_guide_vertex(HairSystem *hsys, int index, int flag, const float co[3]) +{ + BLI_assert(index <= hsys->totverts); + + HairGuideVertex *vertex = &hsys->verts[index]; + vertex->flag = flag; + copy_v3_v3(vertex->co, co); + + BKE_hair_batch_cache_dirty(hsys, BKE_HAIR_BATCH_DIRTY_ALL); +} + +/* ================================= */ + +BLI_INLINE void hair_fiber_verify_weights(HairFollicle *follicle) +{ + const float *w = follicle->parent_weight; + + BLI_assert(w[0] >= 0.0f && w[1] >= 0.0f && w[2] >= 0.0f && w[3] >= 0.0f); + float sum = w[0] + w[1] + w[2] + w[3]; + float epsilon = 1.0e-2; + BLI_assert(sum > 1.0f - epsilon && sum < 1.0f + epsilon); + UNUSED_VARS(sum, epsilon); + + BLI_assert(w[0] >= w[1] && w[1] >= w[2] && w[2] >= w[3]); +} + +static void hair_fiber_sort_weights(HairFollicle *follicle) +{ + unsigned int *idx = follicle->parent_index; + float *w = follicle->parent_weight; + +#define FIBERSWAP(a, b) \ + SWAP(unsigned int, idx[a], idx[b]); \ + SWAP(float, w[a], w[b]); + + for (int k = 0; k < 3; ++k) { + int maxi = k; + float maxw = w[k]; + for (int i = k+1; i < 4; ++i) { + if (w[i] > maxw) { + maxi = i; + maxw = w[i]; + } + } + if (maxi != k) + FIBERSWAP(k, maxi); + } + +#undef FIBERSWAP +} + +static void hair_fiber_find_closest_strand( + HairFollicle *follicle, + const float loc[3], + const KDTree *tree, + const float (*strandloc)[3]) +{ + /* Use the 3 closest strands for interpolation. + * Note that we have up to 4 possible weights, but we + * only look for a triangle with this method. + */ + KDTreeNearest nearest[3]; + const float *sloc[3] = {NULL}; + int k, found = BLI_kdtree_find_nearest_n(tree, loc, nearest, 3); + for (k = 0; k < found; ++k) { + follicle->parent_index[k] = (unsigned int)nearest[k].index; + sloc[k] = strandloc[nearest[k].index]; + } + for (; k < 4; ++k) { + follicle->parent_index[k] = HAIR_STRAND_INDEX_NONE; + follicle->parent_weight[k] = 0.0f; + } + + /* calculate barycentric interpolation weights */ + if (found == 3) { + float closest[3]; + closest_on_tri_to_point_v3(closest, loc, sloc[0], sloc[1], sloc[2]); + + float w[3]; + interp_weights_tri_v3(w, sloc[0], sloc[1], sloc[2], closest); + copy_v3_v3(follicle->parent_weight, w); + /* float precisions issues can cause slightly negative weights */ + CLAMP3(follicle->parent_weight, 0.0f, 1.0f); + } + else if (found == 2) { + follicle->parent_weight[1] = line_point_factor_v3(loc, sloc[0], sloc[1]); + follicle->parent_weight[0] = 1.0f - follicle->parent_weight[1]; + /* float precisions issues can cause slightly negative weights */ + CLAMP2(follicle->parent_weight, 0.0f, 1.0f); + } + else if (found == 1) { + follicle->parent_weight[0] = 1.0f; + } + + hair_fiber_sort_weights(follicle); +} + +void BKE_hair_bind_follicles(HairSystem *hsys, DerivedMesh *scalp) +{ + if (!(hsys->flag & HAIR_SYSTEM_UPDATE_FOLLICLE_BINDING)) + { + return; + } + hsys->flag &= ~HAIR_SYSTEM_UPDATE_FOLLICLE_BINDING; + + HairPattern *pattern = hsys->pattern; + const int num_strands = hsys->totcurves; + if (num_strands == 0 || !pattern) + return; + + float (*strandloc)[3] = MEM_mallocN(sizeof(float) * 3 * num_strands, "strand locations"); + { + for (int i = 0; i < num_strands; ++i) { + float nor[3], tang[3]; + if (!BKE_mesh_sample_eval(scalp, &hsys->curves[i].mesh_sample, strandloc[i], nor, tang)) { + zero_v3(strandloc[i]); + } + } + } + + KDTree *tree = BLI_kdtree_new(num_strands); + for (int c = 0; c < num_strands; ++c) { + BLI_kdtree_insert(tree, c, strandloc[c]); + } + BLI_kdtree_balance(tree); + + HairFollicle *follicle = pattern->follicles; + for (int i = 0; i < pattern->num_follicles; ++i, ++follicle) { + float loc[3], nor[3], tang[3]; + if (BKE_mesh_sample_eval(scalp, &follicle->mesh_sample, loc, nor, tang)) { + hair_fiber_find_closest_strand(follicle, loc, tree, strandloc); + hair_fiber_verify_weights(follicle); + } + } + + BLI_kdtree_free(tree); + MEM_freeN(strandloc); +} + diff --git a/source/blender/blenkernel/intern/hair_draw.c b/source/blender/blenkernel/intern/hair_draw.c new file mode 100644 index 00000000000..37de8bc742d --- /dev/null +++ b/source/blender/blenkernel/intern/hair_draw.c @@ -0,0 +1,360 @@ +/* + * ***** 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/hair_draw.c + * \ingroup bke + */ + +#include <string.h> + +#include "MEM_guardedalloc.h" + +#include "BLI_math.h" +#include "BLI_kdtree.h" +#include "BLI_rand.h" + +#include "DNA_hair_types.h" + +#include "BKE_DerivedMesh.h" +#include "BKE_mesh_sample.h" +#include "BKE_hair.h" + +/* === Draw Settings === */ + +HairDrawSettings* BKE_hair_draw_settings_new(void) +{ + HairDrawSettings *draw_settings = MEM_callocN(sizeof(HairDrawSettings), "hair draw settings"); + + draw_settings->follicle_mode = HAIR_DRAW_FOLLICLE_NONE; + + return draw_settings; +} + +HairDrawSettings* BKE_hair_draw_settings_copy(HairDrawSettings *draw_settings) +{ + HairDrawSettings *ndraw_settings = MEM_dupallocN(draw_settings); + return ndraw_settings; +} + +void BKE_hair_draw_settings_free(HairDrawSettings *draw_settings) +{ + MEM_freeN(draw_settings); +} + +/* === Draw Cache === */ + +static int hair_get_strand_subdiv_numverts(int numstrands, int numverts, int subdiv) +{ + return ((numverts - numstrands) << subdiv) + numstrands; +} + +BLI_INLINE int hair_get_strand_subdiv_length(int orig_length, int subdiv) +{ + return ((orig_length - 1) << subdiv) + 1; +} + +int* BKE_hair_get_fiber_lengths(const HairSystem *hsys, int subdiv) +{ + if (!hsys->pattern) { + return NULL; + } + + const int totfibers = hsys->pattern->num_follicles; + int *fiber_length = MEM_mallocN(sizeof(int) * totfibers, "fiber length"); + + const int num_strands = hsys->totcurves; + /* Cache subdivided lengths for repeated lookup */ + int *lengths = MEM_mallocN(sizeof(int) * num_strands, "strand length"); + for (int i = 0; i < hsys->totcurves; ++i) { + lengths[i] = hair_get_strand_subdiv_length(hsys->curves[i].numverts, subdiv); + } + + // Calculate the length of the fiber from the weighted average of its guide strands + HairFollicle *follicle = hsys->pattern->follicles; + for (int i = 0; i < totfibers; ++i, ++follicle) { + float fiblen = 0.0f; + + for (int k = 0; k < 4; ++k) { + int si = follicle->parent_index[k]; + float sw = follicle->parent_weight[k]; + if (si == HAIR_STRAND_INDEX_NONE || sw == 0.0f) { + break; + } + BLI_assert(si < num_strands); + + fiblen += (float)lengths[si] * sw; + } + + // use rounded number of segments + fiber_length[i] = (int)(fiblen + 0.5f); + } + + MEM_freeN(lengths); + + return fiber_length; +} + +typedef struct HairFiberTextureBuffer { + unsigned int parent_index[4]; + float parent_weight[4]; + float root_position[3]; + int pad; +} HairFiberTextureBuffer; +BLI_STATIC_ASSERT_ALIGN(HairFiberTextureBuffer, 8) + +typedef struct HairStrandVertexTextureBuffer { + float co[3]; + float nor[3]; + float tang[3]; + int pad; +} HairStrandVertexTextureBuffer; +BLI_STATIC_ASSERT_ALIGN(HairStrandVertexTextureBuffer, 8) + +typedef struct HairStrandMapTextureBuffer { + unsigned int vertex_start; + unsigned int vertex_count; +} HairStrandMapTextureBuffer; +BLI_STATIC_ASSERT_ALIGN(HairStrandMapTextureBuffer, 8) + +static void hair_strand_transport_frame(const float co1[3], const float co2[3], + float prev_tang[3], float prev_nor[3], + float r_tang[3], float r_nor[3]) +{ + /* segment direction */ + sub_v3_v3v3(r_tang, co2, co1); + normalize_v3(r_tang); + + /* rotate the frame */ + float rot[3][3]; + rotation_between_vecs_to_mat3(rot, prev_tang, r_tang); + mul_v3_m3v3(r_nor, rot, prev_nor); + + copy_v3_v3(prev_tang, r_tang); + copy_v3_v3(prev_nor, r_nor); +} + +static void hair_strand_calc_vectors(const float (*positions)[3], int num_verts, float rootmat[3][3], + HairStrandVertexTextureBuffer *strand) +{ + for (int i = 0; i < num_verts; ++i) { + copy_v3_v3(strand[i].co, positions[i]); + } + + // Calculate tangent and normal vectors + { + BLI_assert(num_verts >= 2); + + float prev_tang[3], prev_nor[3]; + + copy_v3_v3(prev_tang, rootmat[2]); + copy_v3_v3(prev_nor, rootmat[0]); + + hair_strand_transport_frame(strand[0].co, strand[1].co, + prev_tang, prev_nor, + strand[0].tang, strand[0].nor); + + for (int i = 1; i < num_verts - 1; ++i) + { + hair_strand_transport_frame(strand[i-1].co, strand[i+1].co, + prev_tang, prev_nor, + strand[i].tang, strand[i].nor); + } + + hair_strand_transport_frame(strand[num_verts-2].co, strand[num_verts-1].co, + prev_tang, prev_nor, + strand[num_verts-1].tang, strand[num_verts-1].nor); + } +} + +static int hair_strand_subdivide(const HairSystem *hsys, const HairGuideCurve* curve, int subdiv, float (*verts)[3]) +{ + { + /* Move vertex positions from the dense array to their initial configuration for subdivision. */ + const int step = (1 << subdiv); + float (*dst)[3] = verts; + int vertend = curve->vertstart + curve->numverts; + for (int i = curve->vertstart; i < vertend; ++i) { + copy_v3_v3(*dst, hsys->verts[i].co); + dst += step; + } + } + + /* Subdivide */ + for (int d = 0; d < subdiv; ++d) { + const int num_edges = (curve->numverts - 1) << d; + const int hstep = 1 << (subdiv - d - 1); + const int step = 1 << (subdiv - d); + + /* Calculate edge points */ + { + int index = 0; + for (int k = 0; k < num_edges; ++k, index += step) { + add_v3_v3v3(verts[index + hstep], verts[index], verts[index + step]); + mul_v3_fl(verts[index + hstep], 0.5f); + } + } + + /* Move original points */ + { + int index = step; + for (int k = 1; k < num_edges; ++k, index += step) { + add_v3_v3v3(verts[index], verts[index - hstep], verts[index + hstep]); + mul_v3_fl(verts[index], 0.5f); + } + } + } + + const int num_verts = ((curve->numverts - 1) << subdiv) + 1; + return num_verts; +} + +static void hair_get_strand_buffer( + const HairSystem *hsys, + DerivedMesh *scalp, + int subdiv, + HairStrandMapTextureBuffer *strand_map_buffer, + HairStrandVertexTextureBuffer *strand_vertex_buffer) +{ + const int numverts = hair_get_strand_subdiv_numverts(hsys->totcurves, hsys->totverts, subdiv); + + float (*vertco)[3] = MEM_mallocN(sizeof(float[3]) * numverts, "strand vertex positions subdivided"); + + HairStrandMapTextureBuffer *smap = strand_map_buffer; + HairStrandVertexTextureBuffer *svert = strand_vertex_buffer; + int vertex_start = 0; + for (int i = 0; i < hsys->totcurves; ++i) { + const HairGuideCurve *curve = &hsys->curves[i]; + const int len_orig = curve->numverts; + const int len = hair_get_strand_subdiv_length(len_orig, subdiv); + smap->vertex_start = vertex_start; + smap->vertex_count = len; + + hair_strand_subdivide(hsys, curve, subdiv, vertco + vertex_start); + + { + float pos[3]; + float matrix[3][3]; + BKE_mesh_sample_eval(scalp, &hsys->curves[i].mesh_sample, pos, matrix[2], matrix[0]); + cross_v3_v3v3(matrix[1], matrix[2], matrix[0]); + hair_strand_calc_vectors(vertco + vertex_start, len, matrix, svert); + } + + vertex_start += len; + ++smap; + svert += len; + } + + MEM_freeN(vertco); +} + +static void hair_get_fiber_buffer(const HairSystem* hsys, DerivedMesh *scalp, + HairFiberTextureBuffer *fiber_buf) +{ + if (hsys->pattern) + { + const int totfibers = hsys->pattern->num_follicles; + HairFiberTextureBuffer *fb = fiber_buf; + + HairFollicle *follicle = hsys->pattern->follicles; + float nor[3], tang[3]; + for (int i = 0; i < totfibers; ++i, ++fb, ++follicle) { + BKE_mesh_sample_eval(scalp, &follicle->mesh_sample, fb->root_position, nor, tang); + for (int k = 0; k < 4; ++k) + { + fb->parent_index[k] = follicle->parent_index[k]; + fb->parent_weight[k] = follicle->parent_weight[k]; + } + } + } +} + +void BKE_hair_get_texture_buffer_size( + const HairSystem *hsys, + int subdiv, + int *r_size, + int *r_strand_map_start, + int *r_strand_vertex_start, + int *r_fiber_start) +{ + int numstrands = hsys->totcurves; + int numverts_orig = hsys->totverts; + int numfibers = hsys->pattern ? hsys->pattern->num_follicles : 0; + const int numverts = hair_get_strand_subdiv_numverts(numstrands, numverts_orig, subdiv); + *r_strand_map_start = 0; + *r_strand_vertex_start = *r_strand_map_start + numstrands * sizeof(HairStrandMapTextureBuffer); + *r_fiber_start = *r_strand_vertex_start + numverts * sizeof(HairStrandVertexTextureBuffer); + *r_size = *r_fiber_start + numfibers * sizeof(HairFiberTextureBuffer); +} + +void BKE_hair_get_texture_buffer( + const HairSystem *hsys, + DerivedMesh *scalp, + int subdiv, + void *buffer) +{ + int size, strand_map_start, strand_vertex_start, fiber_start; + BKE_hair_get_texture_buffer_size(hsys, subdiv, &size, &strand_map_start, &strand_vertex_start, &fiber_start); + + if (scalp) + { + HairStrandMapTextureBuffer *strand_map = (HairStrandMapTextureBuffer*)((char*)buffer + strand_map_start); + HairStrandVertexTextureBuffer *strand_verts = (HairStrandVertexTextureBuffer*)((char*)buffer + strand_vertex_start); + HairFiberTextureBuffer *fibers = (HairFiberTextureBuffer*)((char*)buffer + fiber_start); + + hair_get_strand_buffer( + hsys, + scalp, + subdiv, + strand_map, + strand_verts); + hair_get_fiber_buffer( + hsys, + scalp, + fibers); + } + else + { + memset(buffer, 0, size); + } +} + +void (*BKE_hair_batch_cache_dirty_cb)(HairSystem* hsys, int mode) = NULL; +void (*BKE_hair_batch_cache_free_cb)(HairSystem* hsys) = NULL; + +void BKE_hair_batch_cache_dirty(HairSystem* hsys, int mode) +{ + if (hsys->draw_batch_cache) { + BKE_hair_batch_cache_dirty_cb(hsys, mode); + } +} + +void BKE_hair_batch_cache_free(HairSystem* hsys) +{ + if (hsys->draw_batch_cache || hsys->draw_texture_cache) { + BKE_hair_batch_cache_free_cb(hsys); + } +} diff --git a/source/blender/blenkernel/intern/idcode.c b/source/blender/blenkernel/intern/idcode.c index 487635f06ad..2c1684557d4 100644 --- a/source/blender/blenkernel/intern/idcode.c +++ b/source/blender/blenkernel/intern/idcode.c @@ -63,6 +63,7 @@ static IDType idtypes[] = { { ID_CF, "CacheFile", "cache_files", BLT_I18NCONTEXT_ID_CACHEFILE, IDTYPE_FLAGS_ISLINKABLE }, { ID_CU, "Curve", "curves", BLT_I18NCONTEXT_ID_CURVE, IDTYPE_FLAGS_ISLINKABLE }, { ID_GD, "GPencil", "grease_pencil", BLT_I18NCONTEXT_ID_GPENCIL, IDTYPE_FLAGS_ISLINKABLE }, /* rename gpencil */ + { ID_GM, "Groom", "grooms", BLT_I18NCONTEXT_ID_GROOM, IDTYPE_FLAGS_ISLINKABLE }, { ID_GR, "Group", "groups", BLT_I18NCONTEXT_ID_GROUP, IDTYPE_FLAGS_ISLINKABLE }, { ID_IM, "Image", "images", BLT_I18NCONTEXT_ID_IMAGE, IDTYPE_FLAGS_ISLINKABLE }, { ID_IP, "Ipo", "ipos", "", IDTYPE_FLAGS_ISLINKABLE }, /* deprecated */ @@ -281,6 +282,7 @@ int BKE_idcode_to_index(const short idcode) CASE_IDINDEX(CF); CASE_IDINDEX(CU); CASE_IDINDEX(GD); + CASE_IDINDEX(GM); CASE_IDINDEX(GR); CASE_IDINDEX(IM); CASE_IDINDEX(KE); diff --git a/source/blender/blenkernel/intern/library.c b/source/blender/blenkernel/intern/library.c index 6ec2d223e84..f7f0991238f 100644 --- a/source/blender/blenkernel/intern/library.c +++ b/source/blender/blenkernel/intern/library.c @@ -47,8 +47,9 @@ #include "DNA_brush_types.h" #include "DNA_cachefile_types.h" #include "DNA_camera_types.h" -#include "DNA_group_types.h" #include "DNA_gpencil_types.h" +#include "DNA_groom_types.h" +#include "DNA_group_types.h" #include "DNA_ipo_types.h" #include "DNA_key_types.h" #include "DNA_lamp_types.h" @@ -97,6 +98,7 @@ #include "BKE_global.h" #include "BKE_group.h" #include "BKE_gpencil.h" +#include "BKE_groom.h" #include "BKE_idcode.h" #include "BKE_idprop.h" #include "BKE_image.h" @@ -475,6 +477,9 @@ bool id_make_local(Main *bmain, ID *id, const bool test, const bool lib_local) case ID_CF: if (!test) BKE_cachefile_make_local(bmain, (CacheFile *)id, lib_local); return true; + case ID_GM: + if (!test) BKE_groom_make_local(bmain, (Groom *)id, lib_local); + return true; case ID_WS: case ID_SCR: /* A bit special: can be appended but not linked. Return false @@ -659,6 +664,9 @@ bool BKE_id_copy_ex(Main *bmain, const ID *id, ID **r_newid, const int flag, con case ID_VF: BKE_vfont_copy_data(bmain, (VFont *)*r_newid, (VFont *)id, flag); break; + case ID_GM: + BKE_groom_copy_data(bmain, (Groom *)*r_newid, (Groom *)id, flag); + break; case ID_LI: case ID_SCR: case ID_WM: @@ -744,6 +752,7 @@ void BKE_id_swap(Main *bmain, ID *id_a, ID *id_b) CASE_SWAP(ID_PAL, Palette); CASE_SWAP(ID_PC, PaintCurve); CASE_SWAP(ID_CF, CacheFile); + CASE_SWAP(ID_GM, Groom); case ID_IP: break; /* Deprecated. */ } @@ -964,6 +973,8 @@ ListBase *which_libbase(Main *mainlib, short type) return &(mainlib->cachefiles); case ID_WS: return &(mainlib->workspaces); + case ID_GM: + return &(mainlib->grooms); } return NULL; } @@ -1056,6 +1067,8 @@ void BKE_main_lib_objects_recalc_all(Main *bmain) DEG_id_type_tag(bmain, ID_OB); } +BLI_STATIC_ASSERT(MAX_LIBARRAY == INDEX_ID_NULL + 1, "MAX_LIBARRAY must be large enough for all ID types") + /** * puts into array *lb pointers to all the ListBase structs in main, * and returns the number of them as the function result. This is useful for @@ -1089,6 +1102,7 @@ int set_listbasepointers(Main *main, ListBase **lb) lb[INDEX_ID_ME] = &(main->mesh); lb[INDEX_ID_CU] = &(main->curve); lb[INDEX_ID_MB] = &(main->mball); + lb[INDEX_ID_GM] = &(main->grooms); lb[INDEX_ID_LT] = &(main->latt); lb[INDEX_ID_LA] = &(main->lamp); @@ -1180,6 +1194,7 @@ size_t BKE_libblock_get_alloc_info(short type, const char **name) CASE_RETURN(ID_PC, PaintCurve); CASE_RETURN(ID_CF, CacheFile); CASE_RETURN(ID_WS, WorkSpace); + CASE_RETURN(ID_GM, Groom); } return 0; #undef CASE_RETURN diff --git a/source/blender/blenkernel/intern/library_query.c b/source/blender/blenkernel/intern/library_query.c index bed2244702f..953db6f3cb0 100644 --- a/source/blender/blenkernel/intern/library_query.c +++ b/source/blender/blenkernel/intern/library_query.c @@ -38,6 +38,7 @@ #include "DNA_camera_types.h" #include "DNA_constraint_types.h" #include "DNA_controller_types.h" +#include "DNA_groom_types.h" #include "DNA_group_types.h" #include "DNA_gpencil_types.h" #include "DNA_key_types.h" @@ -659,6 +660,13 @@ void BKE_library_foreach_ID_link(Main *bmain, ID *id, LibraryIDLinkCallback call break; } + case ID_GM: + { + Groom *groom = (Groom *) id; + CALLBACK_INVOKE(groom->scalp_object, IDWALK_CB_NOP); + break; + } + case ID_MA: { Material *material = (Material *) id; @@ -1133,6 +1141,8 @@ bool BKE_library_id_can_use_idtype(ID *id_owner, const short id_type_used) return (ELEM(id_type_used, ID_TE, ID_OB)); case ID_LP: return ELEM(id_type_used, ID_IM); + case ID_GM: + return true; case ID_WS: case ID_IM: case ID_VF: diff --git a/source/blender/blenkernel/intern/library_remap.c b/source/blender/blenkernel/intern/library_remap.c index 483500cf67d..2c6df24f762 100644 --- a/source/blender/blenkernel/intern/library_remap.c +++ b/source/blender/blenkernel/intern/library_remap.c @@ -41,6 +41,7 @@ #include "DNA_cachefile_types.h" #include "DNA_group_types.h" #include "DNA_gpencil_types.h" +#include "DNA_groom_types.h" #include "DNA_ipo_types.h" #include "DNA_key_types.h" #include "DNA_lamp_types.h" @@ -79,6 +80,7 @@ #include "BKE_font.h" #include "BKE_group.h" #include "BKE_gpencil.h" +#include "BKE_groom.h" #include "BKE_idprop.h" #include "BKE_image.h" #include "BKE_ipo.h" @@ -893,6 +895,9 @@ void BKE_libblock_free_datablock(ID *id, const int UNUSED(flag)) case ID_WS: BKE_workspace_free((WorkSpace *)id); break; + case ID_GM: + BKE_groom_free((Groom *)id); + break; } } diff --git a/source/blender/blenkernel/intern/mesh_sample.c b/source/blender/blenkernel/intern/mesh_sample.c new file mode 100644 index 00000000000..fac28da63fb --- /dev/null +++ b/source/blender/blenkernel/intern/mesh_sample.c @@ -0,0 +1,1683 @@ +/* + * ***** 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 <limits.h> + +#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_ghash.h" +#include "BLI_math.h" +#include "BLI_rand.h" +#include "BLI_sort.h" +#include "BLI_task.h" + +#include "BKE_bvhutils.h" +#include "BKE_mesh_sample.h" +#include "BKE_customdata.h" +#include "BKE_DerivedMesh.h" + +#include "BLI_strict_flags.h" + +#define SAMPLE_INDEX_INVALID 0xFFFFFFFF + +#define DEFAULT_TASK_SIZE 1024 + +/* ==== Utility Functions ==== */ + +static float calc_mesh_area(DerivedMesh *dm) +{ + int numtris = dm->getNumLoopTri(dm); + MVert *mverts = dm->getVertArray(dm); + MLoop *mloops = dm->getLoopArray(dm); + + float totarea = 0.0; + const MLoopTri *tri = dm->getLoopTriArray(dm); + for (int i = 0; i < numtris; ++i, ++tri) { + unsigned int index1 = mloops[tri->tri[0]].v; + unsigned int index2 = mloops[tri->tri[1]].v; + unsigned int index3 = mloops[tri->tri[2]].v; + MVert *v1 = &mverts[index1]; + MVert *v2 = &mverts[index2]; + MVert *v3 = &mverts[index3]; + totarea += area_tri_v3(v1->co, v2->co, v3->co); + } + + return totarea; +} + +BLI_INLINE float triangle_weight(DerivedMesh *dm, const MLoopTri *tri, const float *vert_weights, float *r_area) +{ + MVert *mverts = dm->getVertArray(dm); + MLoop *mloops = dm->getLoopArray(dm); + unsigned int index1 = mloops[tri->tri[0]].v; + unsigned int index2 = mloops[tri->tri[1]].v; + unsigned int index3 = mloops[tri->tri[2]].v; + MVert *v1 = &mverts[index1]; + MVert *v2 = &mverts[index2]; + MVert *v3 = &mverts[index3]; + + float weight = area_tri_v3(v1->co, v2->co, v3->co); + if (r_area) { + *r_area = weight; + } + + if (vert_weights) { + float w1 = vert_weights[index1]; + float w2 = vert_weights[index2]; + float w3 = vert_weights[index3]; + + weight *= (w1 + w2 + w3) / 3.0f; + } + + return weight; +} + +float* BKE_mesh_sample_calc_triangle_weights(DerivedMesh *dm, MeshSampleVertexWeightFp vertex_weight_cb, void *userdata, float *r_area) +{ + int numverts = dm->getNumVerts(dm); + int numtris = dm->getNumLoopTri(dm); + int numweights = numtris; + + float *vert_weights = NULL; + if (vertex_weight_cb) { + vert_weights = MEM_mallocN(sizeof(float) * (size_t)numverts, "mesh sample vertex weights"); + { + MVert *mv = dm->getVertArray(dm); + for (int i = 0; i < numtris; ++i, ++mv) { + vert_weights[i] = vertex_weight_cb(dm, mv, (unsigned int)i, userdata); + } + } + } + + float *tri_weights = MEM_mallocN(sizeof(float) * (size_t)numweights, "mesh sample triangle weights"); + /* accumulate weights */ + float totarea = 0.0; + float totweight = 0.0f; + { + const MLoopTri *mt = dm->getLoopTriArray(dm); + for (int i = 0; i < numtris; ++i, ++mt) { + tri_weights[i] = totweight; + + float triarea; + float triweight = triangle_weight(dm, mt, vert_weights, &triarea); + totarea += triarea; + totweight += triweight; + } + } + + if (vert_weights) { + MEM_freeN(vert_weights); + } + + /* normalize */ + if (totweight > 0.0f) { + float norm = 1.0f / totweight; + const MLoopTri *mt = dm->getLoopTriArray(dm); + for (int i = 0; i < numtris; ++i, ++mt) { + tri_weights[i] *= norm; + } + } + else { + /* invalid weights, remove to avoid invalid binary search */ + MEM_freeN(tri_weights); + tri_weights = NULL; + } + + if (r_area) { + *r_area = totarea; + } + return tri_weights; +} + +void BKE_mesh_sample_weights_from_loc(MeshSample *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_quad_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]]; +} + +/* ==== Evaluate ==== */ + +bool BKE_mesh_sample_is_valid(const struct MeshSample *sample) +{ + const unsigned int *v = sample->orig_verts; + + if (BKE_mesh_sample_is_volume_sample(sample)) + { + /* volume sample stores position in the weight vector directly */ + return true; + } + + if (v[0] == SAMPLE_INDEX_INVALID || v[1] == SAMPLE_INDEX_INVALID || v[2] == SAMPLE_INDEX_INVALID) + { + /* must have 3 valid indices */ + return false; + } + + return true; +} + +bool BKE_mesh_sample_is_volume_sample(const MeshSample *sample) +{ + const unsigned int *v = sample->orig_verts; + return v[0] == SAMPLE_INDEX_INVALID && v[1] == SAMPLE_INDEX_INVALID && v[2] == SAMPLE_INDEX_INVALID; +} + +bool BKE_mesh_sample_eval(DerivedMesh *dm, const MeshSample *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 (BKE_mesh_sample_is_volume_sample(sample)) { + /* VOLUME SAMPLE */ + + if (is_zero_v3(sample->orig_weights)) + return false; + + copy_v3_v3(loc, sample->orig_weights); + return true; + } + else { + /* SURFACE SAMPLE */ + 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 MeshSample *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; +} + +void BKE_mesh_sample_clear(MeshSample *sample) +{ + memset(sample, 0, sizeof(MeshSample)); +} + + +/* ==== Generator Types ==== */ + +typedef void (*GeneratorFreeFp)(struct MeshSampleGenerator *gen); + +typedef void (*GeneratorBindFp)(struct MeshSampleGenerator *gen); +typedef void (*GeneratorUnbindFp)(struct MeshSampleGenerator *gen); + +typedef void* (*GeneratorThreadContextCreateFp)(const struct MeshSampleGenerator *gen, int start); +typedef void (*GeneratorThreadContextFreeFp)(const struct MeshSampleGenerator *gen, void *thread_ctx); +typedef bool (*GeneratorMakeSampleFp)(const struct MeshSampleGenerator *gen, void *thread_ctx, struct MeshSample *sample); +typedef unsigned int (*GeneratorGetMaxSamplesFp)(const struct MeshSampleGenerator *gen); + +typedef struct MeshSampleGenerator +{ + GeneratorFreeFp free; + + GeneratorBindFp bind; + GeneratorUnbindFp unbind; + + GeneratorThreadContextCreateFp thread_context_create; + GeneratorThreadContextFreeFp thread_context_free; + GeneratorMakeSampleFp make_sample; + GeneratorGetMaxSamplesFp get_max_samples; + + /* bind target */ + DerivedMesh *dm; + + void *default_ctx; + int task_size; +} MeshSampleGenerator; + +static void sample_generator_init(MeshSampleGenerator *gen, + GeneratorFreeFp free, + GeneratorBindFp bind, + GeneratorUnbindFp unbind, + GeneratorThreadContextCreateFp thread_context_create, + GeneratorThreadContextFreeFp thread_context_free, + GeneratorMakeSampleFp make_sample, + GeneratorGetMaxSamplesFp get_max_samples) +{ + gen->free = free; + gen->bind = bind; + gen->unbind = unbind; + gen->thread_context_create = thread_context_create; + gen->thread_context_free = thread_context_free; + gen->make_sample = make_sample; + gen->get_max_samples = get_max_samples; + + gen->dm = NULL; + + gen->default_ctx = NULL; + gen->task_size = DEFAULT_TASK_SIZE; +} + +/* ------------------------------------------------------------------------- */ + +typedef struct MSurfaceSampleGenerator_Vertices { + MeshSampleGenerator base; + + /* bind data */ + int (*vert_loop_map)[3]; +} MSurfaceSampleGenerator_Vertices; + +static void generator_vertices_free(MSurfaceSampleGenerator_Vertices *gen) +{ + MEM_freeN(gen); +} + +static void generator_vertices_bind(MSurfaceSampleGenerator_Vertices *gen) +{ + DerivedMesh *dm = gen->base.dm; + const int num_verts = dm->getNumVerts(dm); + + DM_ensure_normals(dm); + + int (*vert_loop_map)[3] = MEM_mallocN(sizeof(int) * 3 * (unsigned int)num_verts, "vertex loop map"); + for (int i = 0; i < num_verts; ++i) { + vert_loop_map[i][0] = -1; + vert_loop_map[i][1] = -1; + vert_loop_map[i][2] = -1; + } + + const int num_polys = dm->getNumPolys(dm); + const MLoop *mloops = dm->getLoopArray(dm); + const MPoly *mp = dm->getPolyArray(dm); + for (int i = 0; i < num_polys; ++i, ++mp) { + if (mp->totloop < 3) { + continue; + } + + const MLoop *ml = mloops + mp->loopstart; + for (int k = 0; k < mp->totloop; ++k, ++ml) { + int *vmap = vert_loop_map[ml->v]; + if (vmap[0] < 0) { + vmap[0] = mp->loopstart + k; + vmap[1] = mp->loopstart + (k + 1) % mp->totloop; + vmap[2] = mp->loopstart + (k + 2) % mp->totloop; + } + } + } + + gen->vert_loop_map = vert_loop_map; +} + +static void generator_vertices_unbind(MSurfaceSampleGenerator_Vertices *gen) +{ + if (gen->vert_loop_map) { + MEM_freeN(gen->vert_loop_map); + } +} + +static void* generator_vertices_thread_context_create(const MSurfaceSampleGenerator_Vertices *UNUSED(gen), int start) +{ + int *cur_vert = MEM_callocN(sizeof(int), "generator_vertices_thread_context"); + *cur_vert = start; + return cur_vert; +} + +static void generator_vertices_thread_context_free(const MSurfaceSampleGenerator_Vertices *UNUSED(gen), void *thread_ctx) +{ + MEM_freeN(thread_ctx); +} + +static bool generator_vertices_make_loop_sample(DerivedMesh *dm, const int *loops, MeshSample *sample) +{ + const MLoop *mloops = dm->getLoopArray(dm); + + if (loops[0] < 0) { + return false; + } + + sample->orig_poly = -1; + + sample->orig_loops[0] = (unsigned int)loops[0]; + sample->orig_loops[1] = (unsigned int)loops[1]; + sample->orig_loops[2] = (unsigned int)loops[2]; + + sample->orig_verts[0] = mloops[loops[0]].v; + sample->orig_verts[1] = mloops[loops[1]].v; + sample->orig_verts[2] = mloops[loops[2]].v; + + sample->orig_weights[0] = 1.0f; + sample->orig_weights[1] = 0.0f; + sample->orig_weights[2] = 0.0f; + + return true; +} + +static bool generator_vertices_make_sample(const MSurfaceSampleGenerator_Vertices *gen, void *thread_ctx, MeshSample *sample) +{ + DerivedMesh *dm = gen->base.dm; + const int num_verts = dm->getNumVerts(dm); + + int cur_vert = *(int *)thread_ctx; + bool found_vert = false; + for (; cur_vert < num_verts && !found_vert; ++cur_vert) { + found_vert |= generator_vertices_make_loop_sample(dm, gen->vert_loop_map[cur_vert], sample); + } + + *(int *)thread_ctx = cur_vert; + return found_vert; +} + +static unsigned int generator_vertices_get_max_samples(const MSurfaceSampleGenerator_Vertices *gen) +{ + return (unsigned int)gen->base.dm->getNumVerts(gen->base.dm); +} + +MeshSampleGenerator *BKE_mesh_sample_gen_surface_vertices(void) +{ + MSurfaceSampleGenerator_Vertices *gen; + + gen = MEM_callocN(sizeof(MSurfaceSampleGenerator_Vertices), "MSurfaceSampleGenerator_Vertices"); + sample_generator_init(&gen->base, + (GeneratorFreeFp)generator_vertices_free, + (GeneratorBindFp)generator_vertices_bind, + (GeneratorUnbindFp) generator_vertices_unbind, + (GeneratorThreadContextCreateFp)generator_vertices_thread_context_create, + (GeneratorThreadContextFreeFp)generator_vertices_thread_context_free, + (GeneratorMakeSampleFp)generator_vertices_make_sample, + (GeneratorGetMaxSamplesFp)generator_vertices_get_max_samples); + + return &gen->base; +} + +/* ------------------------------------------------------------------------- */ + +//#define USE_DEBUG_COUNT + +typedef struct MSurfaceSampleGenerator_Random { + MeshSampleGenerator base; + + unsigned int seed; + bool use_area_weight; + MeshSampleVertexWeightFp vertex_weight_cb; + void *userdata; + + /* bind data */ + float *tri_weights; + float *vertex_weights; + +#ifdef USE_DEBUG_COUNT + int *debug_count; +#endif +} MSurfaceSampleGenerator_Random; + +static void generator_random_free(MSurfaceSampleGenerator_Random *gen) +{ + MEM_freeN(gen); +} + +static void generator_random_bind(MSurfaceSampleGenerator_Random *gen) +{ + DerivedMesh *dm = gen->base.dm; + + DM_ensure_normals(dm); + + if (gen->use_area_weight) { + gen->tri_weights = BKE_mesh_sample_calc_triangle_weights(dm, gen->vertex_weight_cb, gen->userdata, NULL); + +#ifdef USE_DEBUG_COUNT + gen->debug_count = MEM_callocN(sizeof(int) * (size_t)dm->getNumLoopTri(dm), "surface sample debug counts"); +#endif + } +} + +static void generator_random_unbind(MSurfaceSampleGenerator_Random *gen) +{ +#ifdef USE_DEBUG_COUNT + if (gen->debug_count) { + if (gen->tri_weights) { + int num = gen->dm->getNumLoopTri(gen->dm); + int i; + int totsamples = 0; + + printf("Surface Sampling (n=%d):\n", num); + for (i = 0; i < num; ++i) + totsamples += gen->debug_count[i]; + + for (i = 0; i < num; ++i) { + float weight = i > 0 ? gen->tri_weights[i] - gen->tri_weights[i-1] : gen->tri_weights[i]; + int samples = gen->debug_count[i]; + printf(" %d: W = %f, N = %d/%d = %f\n", i, weight, samples, totsamples, (float)samples / (float)totsamples); + } + } + MEM_freeN(gen->debug_count); + } +#endif + if (gen->tri_weights) + MEM_freeN(gen->tri_weights); + if (gen->vertex_weights) + MEM_freeN(gen->vertex_weights); +} + +static void* generator_random_thread_context_create(const MSurfaceSampleGenerator_Random *gen, int start) +{ + RNG *rng = BLI_rng_new(gen->seed); + // 3 RNG gets per sample + BLI_rng_skip(rng, start * 3); + return rng; +} + +static void generator_random_thread_context_free(const MSurfaceSampleGenerator_Random *UNUSED(gen), void *thread_ctx) +{ + BLI_rng_free(thread_ctx); +} + +/* Find the index in "sum" array before "value" is crossed. */ +BLI_INLINE int weight_array_binary_search(const float *sum, int size, float value) +{ + int mid, low = 0, high = size - 1; + + if (value <= 0.0f) + return 0; + + while (low < high) { + mid = (low + high) >> 1; + + if (sum[mid] < value && value <= sum[mid+1]) + return mid; + + if (sum[mid] >= value) + high = mid - 1; + else if (sum[mid] < value) + low = mid + 1; + else + return mid; + } + + return low; +} + +static bool generator_random_make_sample(const MSurfaceSampleGenerator_Random *gen, void *thread_ctx, MeshSample *sample) +{ + DerivedMesh *dm = gen->base.dm; + RNG *rng = thread_ctx; + const MLoop *mloops = dm->getLoopArray(dm); + const MLoopTri *mtris = dm->getLoopTriArray(dm); + int tottris = dm->getNumLoopTri(dm); + int totweights = tottris; + + int triindex; + float a, b; + const MLoopTri *mtri; + + if (gen->tri_weights) + triindex = weight_array_binary_search(gen->tri_weights, totweights, BLI_rng_get_float(rng)); + else + triindex = BLI_rng_get_int(rng) % totweights; +#ifdef USE_DEBUG_COUNT + if (gen->debug_count) + gen->debug_count[triindex] += 1; +#endif + a = BLI_rng_get_float(rng); + b = BLI_rng_get_float(rng); + + mtri = &mtris[triindex]; + + sample->orig_verts[0] = mloops[mtri->tri[0]].v; + sample->orig_verts[1] = mloops[mtri->tri[1]].v; + sample->orig_verts[2] = mloops[mtri->tri[2]].v; + + 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; + + return true; +} + +MeshSampleGenerator *BKE_mesh_sample_gen_surface_random(unsigned int seed, bool use_area_weight, + MeshSampleVertexWeightFp vertex_weight_cb, void *userdata) +{ + MSurfaceSampleGenerator_Random *gen; + + gen = MEM_callocN(sizeof(MSurfaceSampleGenerator_Random), "MSurfaceSampleGenerator_Random"); + sample_generator_init(&gen->base, + (GeneratorFreeFp)generator_random_free, + (GeneratorBindFp)generator_random_bind, + (GeneratorUnbindFp) generator_random_unbind, + (GeneratorThreadContextCreateFp)generator_random_thread_context_create, + (GeneratorThreadContextFreeFp)generator_random_thread_context_free, + (GeneratorMakeSampleFp)generator_random_make_sample, + NULL); + + gen->seed = seed; + gen->use_area_weight = use_area_weight; + gen->vertex_weight_cb = vertex_weight_cb; + gen->userdata = userdata; + + return &gen->base; +} + +/* ------------------------------------------------------------------------- */ + +typedef struct MSurfaceSampleGenerator_RayCast { + MeshSampleGenerator base; + + MeshSampleRayFp ray_cb; + MeshSampleThreadContextCreateFp thread_context_create_cb; + MeshSampleThreadContextFreeFp thread_context_free_cb; + void *userdata; + + /* bind data */ + BVHTreeFromMesh bvhdata; +} MSurfaceSampleGenerator_RayCast; + +static void generator_raycast_free(MSurfaceSampleGenerator_RayCast *gen) +{ + MEM_freeN(gen); +} + +static void generator_raycast_bind(MSurfaceSampleGenerator_RayCast *gen) +{ + DerivedMesh *dm = gen->base.dm; + + DM_ensure_tessface(dm); + + memset(&gen->bvhdata, 0, sizeof(gen->bvhdata)); + + if (dm->getNumTessFaces(dm) == 0) + return; + + bvhtree_from_mesh_faces(&gen->bvhdata, dm, 0.0f, 4, 6); +} + +static void generator_raycast_unbind(MSurfaceSampleGenerator_RayCast *gen) +{ + free_bvhtree_from_mesh(&gen->bvhdata); +} + +static void* generator_raycast_thread_context_create(const MSurfaceSampleGenerator_RayCast *gen, int start) +{ + if (gen->thread_context_create_cb) { + return gen->thread_context_create_cb(gen->userdata, start); + } + else { + return NULL; + } +} + +static void generator_raycast_thread_context_free(const MSurfaceSampleGenerator_RayCast *gen, void *thread_ctx) +{ + if (gen->thread_context_free_cb) { + return gen->thread_context_free_cb(gen->userdata, thread_ctx); + } +} + +static bool generator_raycast_make_sample(const MSurfaceSampleGenerator_RayCast *gen, void *thread_ctx, MeshSample *sample) +{ + float ray_start[3], ray_end[3], ray_dir[3], dist; + BVHTreeRayHit hit; + + if (!gen->ray_cb(gen->userdata, thread_ctx, ray_start, ray_end)) + return false; + + sub_v3_v3v3(ray_dir, ray_end, ray_start); + dist = normalize_v3(ray_dir); + + hit.index = -1; + hit.dist = dist; + + if (BLI_bvhtree_ray_cast(gen->bvhdata.tree, ray_start, ray_dir, 0.0f, + &hit, gen->bvhdata.raycast_callback, (BVHTreeFromMesh *)(&gen->bvhdata)) >= 0) { + + BKE_mesh_sample_weights_from_loc(sample, gen->base.dm, hit.index, hit.co); + + return true; + } + else + return false; +} + +MeshSampleGenerator *BKE_mesh_sample_gen_surface_raycast( + MeshSampleThreadContextCreateFp thread_context_create_cb, + MeshSampleThreadContextFreeFp thread_context_free_cb, + MeshSampleRayFp ray_cb, + void *userdata) +{ + MSurfaceSampleGenerator_RayCast *gen; + + gen = MEM_callocN(sizeof(MSurfaceSampleGenerator_RayCast), "MSurfaceSampleGenerator_RayCast"); + sample_generator_init(&gen->base, + (GeneratorFreeFp)generator_raycast_free, + (GeneratorBindFp)generator_raycast_bind, + (GeneratorUnbindFp) generator_raycast_unbind, + (GeneratorThreadContextCreateFp)generator_raycast_thread_context_create, + (GeneratorThreadContextFreeFp)generator_raycast_thread_context_free, + (GeneratorMakeSampleFp)generator_raycast_make_sample, + NULL); + + gen->thread_context_create_cb = thread_context_create_cb; + gen->thread_context_free_cb = thread_context_free_cb; + gen->ray_cb = ray_cb; + gen->userdata = userdata; + + return &gen->base; +} + +/* ------------------------------------------------------------------------- */ + +#define MAX_CIRCLE_PACKING 0.906899682 +#define SQRT_3 1.732050808 + +typedef struct IndexedMeshSample { + unsigned int orig_verts[3]; + float orig_weights[3]; + float co[3]; + int cell_index[3]; +} IndexedMeshSample; + +typedef struct MSurfaceSampleGenerator_PoissonDisk { + MeshSampleGenerator base; + + MeshSampleGenerator *uniform_gen; + unsigned int max_samples; + float mindist_squared; + /* Size of grid cells is mindist/sqrt(3), + * so that each cell contains at most one valid sample. + */ + float cellsize; + /* Transform mesh space to grid space */ + float grid_scale; + + /* bind data */ + + /* offset and size of the grid */ + int grid_offset[3]; + int grid_size[3]; + + IndexedMeshSample *uniform_samples; + unsigned int num_uniform_samples; + + struct GHash *cell_table; +} MSurfaceSampleGenerator_PoissonDisk; + +typedef struct MeshSampleCell { + int cell_index[3]; + unsigned int sample_start; + unsigned int sample; +} MeshSampleCell; + +typedef struct MSurfaceSampleGenerator_PoissonDisk_ThreadContext { + unsigned int trial; + GHashIterator iter; +} MSurfaceSampleGenerator_PoissonDisk_ThreadContext; + +BLI_INLINE void poissondisk_loc_from_grid(const MSurfaceSampleGenerator_PoissonDisk *gen, float loc[3], const int grid[3]) +{ + copy_v3_fl3(loc, grid[0] + gen->grid_offset[0], grid[1] + gen->grid_offset[1], grid[2] + gen->grid_offset[2]); + mul_v3_fl(loc, gen->cellsize); +} + +BLI_INLINE void poissondisk_grid_from_loc(const MSurfaceSampleGenerator_PoissonDisk *gen, int grid[3], const float loc[3]) +{ + float gridco[3]; + mul_v3_v3fl(gridco, loc, gen->grid_scale); + grid[0] = (int)floorf(gridco[0]) - gen->grid_offset[0]; + grid[1] = (int)floorf(gridco[1]) - gen->grid_offset[1]; + grid[2] = (int)floorf(gridco[2]) - gen->grid_offset[2]; +} + +static void generator_poissondisk_free(MSurfaceSampleGenerator_PoissonDisk *gen) +{ + BKE_mesh_sample_free_generator(gen->uniform_gen); + MEM_freeN(gen); +} + +static void generator_poissondisk_uniform_sample_eval( + void *__restrict userdata, + const int iter, + const ParallelRangeTLS *__restrict UNUSED(tls)) +{ + void *(*ptrs)[3] = userdata; + MSurfaceSampleGenerator_PoissonDisk *gen = (*ptrs)[0]; + const MeshSample *samples = (*ptrs)[1]; + DerivedMesh *dm = (*ptrs)[2]; + + IndexedMeshSample *isample = &gen->uniform_samples[iter]; + const MeshSample *sample = &samples[iter]; + + memcpy(isample->orig_verts, sample->orig_verts, sizeof(isample->orig_verts)); + memcpy(isample->orig_weights, sample->orig_weights, sizeof(isample->orig_weights)); + float nor[3], tang[3]; + BKE_mesh_sample_eval(dm, sample, isample->co, nor, tang); + + poissondisk_grid_from_loc(gen, isample->cell_index, isample->co); +} + +BLI_INLINE void copy_cell_index(int r[3], const int a[3]) +{ + r[0] = a[0]; + r[1] = a[1]; + r[2] = a[2]; +} + +BLI_INLINE int cmp_cell_index(const int a[3], const int b[3]) +{ + int d0 = a[0] - b[0]; + int d1 = a[1] - b[1]; + int d2 = a[2] - b[2]; + if (d0 == 0) + { + if (d1 == 0) + { + if (d2 == 0) + { + return 0; + } + else + { + return d2 > 0 ? 1 : -1; + } + } + else + { + return d1 > 0 ? 1 : -1; + } + } + else + { + return d0 > 0 ? 1 : -1; + } +} + +static int cmp_indexed_mesh_sample(const void *a, const void *b) +{ + return cmp_cell_index(((const IndexedMeshSample *)a)->cell_index, ((const IndexedMeshSample *)b)->cell_index); +} + +BLI_INLINE bool cell_index_eq(const int *a, const int *b) +{ + return a[0] == b[0] && a[1] == b[1] && a[2] == b[2]; +} + +/* hash key function */ +static unsigned int cell_hash_key(const void *key) +{ + const int *cell_index = (const int *)key; + unsigned int hash0 = BLI_ghashutil_inthash(cell_index[0]); + unsigned int hash1 = BLI_ghashutil_inthash(cell_index[1]); + unsigned int hash2 = BLI_ghashutil_inthash(cell_index[2]); + return BLI_ghashutil_combine_hash(hash0, BLI_ghashutil_combine_hash(hash1, hash2)); +} + +/* hash function: return false when equal */ +static bool cell_hash_neq(const void *a, const void *b) +{ + return !cell_index_eq((const int *)a, (const int *)b); +} + +static unsigned int generator_poissondisk_get_max_samples(const MSurfaceSampleGenerator_PoissonDisk *gen) +{ + static const unsigned int hard_max = UINT_MAX; + + const double usable_area = calc_mesh_area(gen->base.dm) * MAX_CIRCLE_PACKING; + const double circle_area = M_PI * gen->mindist_squared; + if (circle_area * (double)hard_max < usable_area) { + return hard_max; + } + + return (unsigned int)(usable_area / circle_area); +} + +static void generator_poissondisk_bind(MSurfaceSampleGenerator_PoissonDisk *gen) +{ + DerivedMesh *dm = gen->base.dm; + static const unsigned int uniform_sample_ratio = 10; + + // Determine cell size + { + float min[3], max[3]; + INIT_MINMAX(min, max); + dm->getMinMax(dm, min, max); + mul_v3_fl(min, gen->grid_scale); + mul_v3_fl(max, gen->grid_scale); + /* grid size gets an empty 2 cell margin to simplify neighbor lookups */ + gen->grid_offset[0] = (int)floorf(min[0]) - 2; + gen->grid_offset[1] = (int)floorf(min[1]) - 2; + gen->grid_offset[2] = (int)floorf(min[2]) - 2; + gen->grid_size[0] = (int)floorf(max[0]) - gen->grid_offset[0] + 4; + gen->grid_size[1] = (int)floorf(max[1]) - gen->grid_offset[1] + 4; + gen->grid_size[2] = (int)floorf(max[2]) - gen->grid_offset[2] + 4; + } + + // Generate initial uniform random point set + unsigned int max_pd_samples = generator_poissondisk_get_max_samples(gen); + gen->num_uniform_samples = MIN2(max_pd_samples * uniform_sample_ratio, gen->max_samples); + if (gen->num_uniform_samples > 0) { + BKE_mesh_sample_generator_bind(gen->uniform_gen, dm); + + gen->uniform_samples = MEM_mallocN(sizeof(IndexedMeshSample) * gen->num_uniform_samples, "poisson disk uniform samples"); + + MeshSample *samples = MEM_mallocN(sizeof(MeshSample) * gen->num_uniform_samples, "poisson disk uniform samples"); + BKE_mesh_sample_generate_batch(gen->uniform_gen, samples, (int)gen->num_uniform_samples); + void *ptrs[3] = { gen, samples, dm }; + { + ParallelRangeSettings settings; + BLI_parallel_range_settings_defaults(&settings); + settings.use_threading = true; + BLI_task_parallel_range(0, (int)gen->num_uniform_samples, &ptrs, generator_poissondisk_uniform_sample_eval, &settings); + } + MEM_freeN(samples); + + BKE_mesh_sample_generator_unbind(gen->uniform_gen); + } + + // Sort points by cell hash + { + qsort(gen->uniform_samples, gen->num_uniform_samples, sizeof(IndexedMeshSample), cmp_indexed_mesh_sample); + } + + // Build a hash table for indexing cells + { + gen->cell_table = BLI_ghash_new(cell_hash_key, cell_hash_neq, "MeshSampleCell hash table"); + int cur_cell_index[3] = {-1, -1, -1}; + const IndexedMeshSample *sample = gen->uniform_samples; + for (unsigned int i = 0; i < gen->num_uniform_samples; ++i, ++sample) { + BLI_assert(cmp_cell_index(cur_cell_index, sample->cell_index) <= 0); + if (cmp_cell_index(cur_cell_index, sample->cell_index) < 0) { + copy_cell_index(cur_cell_index, sample->cell_index); + + MeshSampleCell *cell = MEM_mallocN(sizeof(*cell), "MeshSampleCell"); + copy_cell_index(cell->cell_index, cur_cell_index); + cell->sample_start = (unsigned int)i; + cell->sample = SAMPLE_INDEX_INVALID; + BLI_ghash_insert(gen->cell_table, cell->cell_index, cell); + } + } + } + +#if 0 + for (unsigned int i = 0; i < gen->num_uniform_samples; ++i) { + const IndexedMeshSample *s = &gen->uniform_samples[i]; + printf("%d: (%.3f, %.3f, %.3f) | %d\n", i, s->co[0], s->co[1], s->co[2], (int)s->cell_hash); + } +#endif +} + +static void generator_poissondisk_unbind(MSurfaceSampleGenerator_PoissonDisk *gen) +{ + if (gen->cell_table) { + BLI_ghash_free(gen->cell_table, NULL, MEM_freeN); + } + + if (gen->uniform_samples) { + MEM_freeN(gen->uniform_samples); + } +} + +static void* generator_poissondisk_thread_context_create(const MSurfaceSampleGenerator_PoissonDisk *gen, int UNUSED(start)) +{ + MSurfaceSampleGenerator_PoissonDisk_ThreadContext *ctx = MEM_mallocN(sizeof(*ctx), "thread context"); + ctx->trial = 0; + BLI_ghashIterator_init(&ctx->iter, gen->cell_table); + return ctx; +} + +static void generator_poissondisk_thread_context_free(const MSurfaceSampleGenerator_PoissonDisk *UNUSED(gen), void *thread_ctx) +{ + MEM_freeN(thread_ctx); +} + +static bool generator_poissondisk_make_sample(const MSurfaceSampleGenerator_PoissonDisk *gen, void *thread_ctx, MeshSample *sample) +{ + static const unsigned int max_trials = 5; + + MSurfaceSampleGenerator_PoissonDisk_ThreadContext *ctx = thread_ctx; + + // Offset of cells whose samples can potentially overlap a given cell + // Four corners are excluded because their samples can never overlap + const int neighbors[][3] = { + {-1, -2, -2}, { 0, -2, -2}, { 1, -2, -2}, + {-2, -1, -2}, {-1, -1, -2}, { 0, -1, -2}, { 1, -1, -2}, { 2, -1, -2}, + {-2, 0, -2}, {-1, 0, -2}, { 0, 0, -2}, { 1, 0, -2}, { 2, 0, -2}, + {-2, 1, -2}, {-1, 1, -2}, { 0, 1, -2}, { 1, -2, -2}, { 2, 1, -2}, + {-2, 2, -2}, {-1, 2, -2}, { 0, 2, -2}, { 1, -2, -2}, { 2, 2, -2}, + + {-2, -2, -1}, {-1, -2, -1}, { 0, -2, -1}, { 1, -2, -1}, { 2, -2, -1}, + {-2, -1, -1}, {-1, -1, -1}, { 0, -1, -1}, { 1, -1, -1}, { 2, -1, -1}, + {-2, 0, -1}, {-1, 0, -1}, { 0, 0, -1}, { 1, 0, -1}, { 2, 0, -1}, + {-2, 1, -1}, {-1, 1, -1}, { 0, 1, -1}, { 1, -2, -1}, { 2, 1, -1}, + {-2, 2, -1}, {-1, 2, -1}, { 0, 2, -1}, { 1, -2, -1}, { 2, 2, -1}, + + {-2, -2, 0}, {-1, -2, 0}, { 0, -2, 0}, { 1, -2, 0}, { 2, -2, 0}, + {-2, -1, 0}, {-1, -1, 0}, { 0, -1, 0}, { 1, -1, 0}, { 2, -1, 0}, + {-2, 0, 0}, {-1, 0, 0}, { 1, 0, 0}, { 2, 0, 0}, + {-2, 1, 0}, {-1, 1, 0}, { 0, 1, 0}, { 1, -2, 0}, { 2, 1, 0}, + {-2, 2, 0}, {-1, 2, 0}, { 0, 2, 0}, { 1, -2, 0}, { 2, 2, 0}, + + {-2, -2, 1}, {-1, -2, 1}, { 0, -2, 1}, { 1, -2, 1}, { 2, -2, 1}, + {-2, -1, 1}, {-1, -1, 1}, { 0, -1, 1}, { 1, -1, 1}, { 2, -1, 1}, + {-2, 0, 1}, {-1, 0, 1}, { 0, 0, 1}, { 1, 0, 1}, { 2, 0, 1}, + {-2, 1, 1}, {-1, 1, 1}, { 0, 1, 1}, { 1, -2, 1}, { 2, 1, 1}, + {-2, 2, 1}, {-1, 2, 1}, { 0, 2, 1}, { 1, -2, 1}, { 2, 2, 1}, + + {-2, -2, 2}, {-1, -2, 2}, { 0, -2, 2}, { 1, -2, 2}, { 2, -2, 2}, + {-2, -1, 2}, {-1, -1, 2}, { 0, -1, 2}, { 1, -1, 2}, { 2, -1, 2}, + {-2, 0, 2}, {-1, 0, 2}, { 0, 0, 2}, { 1, 0, 2}, { 2, 0, 2}, + {-2, 1, 2}, {-1, 1, 2}, { 0, 1, 2}, { 1, -2, 2}, { 2, 1, 2}, + {-1, 2, 2}, { 0, 2, 2}, { 1, -2, 2} + }; + const int num_neighbors = ARRAY_SIZE(neighbors); + + bool found_sample = false; + for (; ctx->trial < max_trials; ++ctx->trial) { + while (!BLI_ghashIterator_done(&ctx->iter)) { + MeshSampleCell *cell = BLI_ghashIterator_getValue(&ctx->iter); + BLI_ghashIterator_step(&ctx->iter); + + if (cell->sample != SAMPLE_INDEX_INVALID) { + continue; + } + + bool cell_valid = true; + + unsigned int sample_index = cell->sample_start + ctx->trial; + const IndexedMeshSample *isample = &gen->uniform_samples[sample_index]; + /* Check if we ran out of sample candidates for this cell */ + if (sample_index >= (unsigned int)gen->num_uniform_samples || + !cell_index_eq(isample->cell_index, cell->cell_index)) { + + cell_valid = false; + // TODO remove from hash table? + UNUSED_VARS(cell_valid); + + } + else { + + /* Check the sample candidate */ + const int *idx = cell->cell_index; + + bool conflict = false; + for (int i = 0; i < num_neighbors; ++i) { + const int *idx_offset = neighbors[i]; + const int nidx[3] = {idx[0] + idx_offset[0], idx[1] + idx_offset[1], idx[2] + idx_offset[2]}; + const MeshSampleCell *ncell = BLI_ghash_lookup(gen->cell_table, nidx); + if (ncell) { + if (ncell->sample != SAMPLE_INDEX_INVALID) { + const IndexedMeshSample *nsample = &gen->uniform_samples[ncell->sample]; + BLI_assert(cell_index_eq(nsample->cell_index, ncell->cell_index)); + if (len_squared_v3v3(isample->co, nsample->co) < gen->mindist_squared) { + conflict = true; + break; + } + } + } + } + if (!conflict) { + cell->sample = sample_index; + + memcpy(sample->orig_verts, isample->orig_verts, sizeof(sample->orig_verts)); + memcpy(sample->orig_weights, isample->orig_weights, sizeof(sample->orig_weights)); + memset(sample->orig_loops, 0, sizeof(sample->orig_loops)); + sample->orig_poly = 0; + + found_sample = true; + } + + } + + if (found_sample) { + break; + } + } + + if (found_sample) { + break; + } + else { + BLI_ghashIterator_init(&ctx->iter, gen->cell_table); + } + } + + return found_sample; +} + +MeshSampleGenerator *BKE_mesh_sample_gen_surface_poissondisk(unsigned int seed, float mindist, unsigned int max_samples, + MeshSampleVertexWeightFp vertex_weight_cb, void *userdata) +{ + MSurfaceSampleGenerator_PoissonDisk *gen; + + gen = MEM_callocN(sizeof(MSurfaceSampleGenerator_PoissonDisk), "MSurfaceSampleGenerator_PoissonDisk"); + sample_generator_init(&gen->base, + (GeneratorFreeFp)generator_poissondisk_free, + (GeneratorBindFp)generator_poissondisk_bind, + (GeneratorUnbindFp) generator_poissondisk_unbind, + (GeneratorThreadContextCreateFp)generator_poissondisk_thread_context_create, + (GeneratorThreadContextFreeFp)generator_poissondisk_thread_context_free, + (GeneratorMakeSampleFp)generator_poissondisk_make_sample, + (GeneratorGetMaxSamplesFp)generator_poissondisk_get_max_samples); + + gen->uniform_gen = BKE_mesh_sample_gen_surface_random(seed, true, vertex_weight_cb, userdata); + gen->max_samples = max_samples; + gen->mindist_squared = mindist * mindist; + gen->cellsize = mindist / SQRT_3; + gen->grid_scale = SQRT_3 / mindist; + + return &gen->base; +} + +/* ------------------------------------------------------------------------- */ + +typedef struct MVolumeSampleGenerator_Random { + MeshSampleGenerator base; + + unsigned int seed; + float density; + + /* bind data */ + BVHTreeFromMesh bvhdata; + float min[3], max[3], extent[3], volume; + int max_samples_per_ray; +} MVolumeSampleGenerator_Random; + +typedef struct MVolumeSampleGenerator_Random_ThreadContext { + const BVHTreeFromMesh *bvhdata; + RNG *rng; + + /* current ray intersections */ + BVHTreeRayHit *ray_hits; + int tothits, allochits; + + /* current segment index and sample number */ + int cur_seg, cur_tot, cur_sample; +} MVolumeSampleGenerator_Random_ThreadContext; + +static void generator_volume_random_free(MVolumeSampleGenerator_Random *gen) +{ + MEM_freeN(gen); +} + +static void generator_volume_random_bind(MVolumeSampleGenerator_Random *gen) +{ + DerivedMesh *dm = gen->base.dm; + + memset(&gen->bvhdata, 0, sizeof(gen->bvhdata)); + + DM_ensure_tessface(dm); + + if (dm->getNumTessFaces(dm) == 0) + return; + + bvhtree_from_mesh_faces(&gen->bvhdata, dm, 0.0f, 4, 6); + + INIT_MINMAX(gen->min, gen->max); + dm->getMinMax(dm, gen->min, gen->max); + sub_v3_v3v3(gen->extent, gen->max, gen->min); + gen->volume = gen->extent[0] * gen->extent[1] * gen->extent[2]; + gen->max_samples_per_ray = max_ii(1, (int)powf(gen->volume, 1.0f/3.0f)) >> 1; +} + +static void generator_volume_random_unbind(MVolumeSampleGenerator_Random *gen) +{ + free_bvhtree_from_mesh(&gen->bvhdata); +} + +BLI_INLINE unsigned int hibit(unsigned int n) { + n |= (n >> 1); + n |= (n >> 2); + n |= (n >> 4); + n |= (n >> 8); + n |= (n >> 16); + return n ^ (n >> 1); +} + +static void generator_volume_hits_reserve(MVolumeSampleGenerator_Random_ThreadContext *ctx, int tothits) +{ + if (tothits > ctx->allochits) { + ctx->allochits = (int)hibit((unsigned int)tothits) << 1; + ctx->ray_hits = MEM_reallocN(ctx->ray_hits, (size_t)ctx->allochits * sizeof(BVHTreeRayHit)); + } +} + +static void *generator_volume_random_thread_context_create(const MVolumeSampleGenerator_Random *gen, int start) +{ + MVolumeSampleGenerator_Random_ThreadContext *ctx = MEM_callocN(sizeof(*ctx), "thread context"); + + ctx->bvhdata = &gen->bvhdata; + + ctx->rng = BLI_rng_new(gen->seed); + // 11 RNG gets per sample + BLI_rng_skip(ctx->rng, start * 11); + + generator_volume_hits_reserve(ctx, 64); + + return ctx; +} + +static void generator_volume_random_thread_context_free(const MVolumeSampleGenerator_Random *UNUSED(gen), void *thread_ctx) +{ + MVolumeSampleGenerator_Random_ThreadContext *ctx = thread_ctx; + BLI_rng_free(ctx->rng); + + if (ctx->ray_hits) { + MEM_freeN(ctx->ray_hits); + } + + MEM_freeN(ctx); +} + +static void generator_volume_ray_cb(void *userdata, int index, const BVHTreeRay *ray, BVHTreeRayHit *hit) +{ + MVolumeSampleGenerator_Random_ThreadContext *ctx = userdata; + + ctx->bvhdata->raycast_callback((void *)ctx->bvhdata, index, ray, hit); + + if (hit->index >= 0) { + ++ctx->tothits; + generator_volume_hits_reserve(ctx, ctx->tothits); + + memcpy(&ctx->ray_hits[ctx->tothits-1], hit, sizeof(BVHTreeRayHit)); + } +} + +typedef struct Ray { + float start[3]; + float end[3]; +} Ray; + +static void generator_volume_random_cast_ray(MVolumeSampleGenerator_Random_ThreadContext *ctx, const Ray* ray) +{ + float dir[3]; + + sub_v3_v3v3(dir, ray->end, ray->start); + normalize_v3(dir); + + ctx->tothits = 0; + BLI_bvhtree_ray_cast_all(ctx->bvhdata->tree, ray->start, dir, 0.0f, BVH_RAYCAST_DIST_MAX, + generator_volume_ray_cb, ctx); + + ctx->cur_seg = 0; + ctx->cur_tot = 0; + ctx->cur_sample = 0; +} + +static void generator_volume_init_segment(const MVolumeSampleGenerator_Random *gen, MVolumeSampleGenerator_Random_ThreadContext *ctx) +{ + BVHTreeRayHit *a, *b; + float length; + + BLI_assert(ctx->cur_seg + 1 < ctx->tothits); + a = &ctx->ray_hits[ctx->cur_seg]; + b = &ctx->ray_hits[ctx->cur_seg + 1]; + + length = len_v3v3(a->co, b->co); + ctx->cur_tot = min_ii(gen->max_samples_per_ray, (int)ceilf(length * gen->density)); + ctx->cur_sample = 0; +} + +static void generator_volume_get_ray(RNG *rng, Ray *ray) +{ + /* bounding box margin to get clean ray intersections */ + static const float margin = 0.01f; + + ray->start[0] = BLI_rng_get_float(rng); + ray->start[1] = BLI_rng_get_float(rng); + ray->start[2] = 0.0f; + ray->end[0] = BLI_rng_get_float(rng); + ray->end[1] = BLI_rng_get_float(rng); + ray->end[2] = 1.0f; + + int axis = BLI_rng_get_int(rng) % 3; + switch (axis) { + case 0: break; + case 1: + SHIFT3(float, ray->start[0], ray->start[1], ray->start[2]); + SHIFT3(float, ray->end[0], ray->end[1], ray->end[2]); + break; + case 2: + SHIFT3(float, ray->start[2], ray->start[1], ray->start[0]); + SHIFT3(float, ray->end[2], ray->end[1], ray->end[0]); + break; + } + + mul_v3_fl(ray->start, 1.0f + 2.0f*margin); + add_v3_fl(ray->start, -margin); + mul_v3_fl(ray->end, 1.0f + 2.0f*margin); + add_v3_fl(ray->end, -margin); +} + +static void generator_volume_ray_to_bbox(const MVolumeSampleGenerator_Random *gen, Ray *ray) +{ + madd_v3_v3v3v3(ray->start, gen->min, ray->start, gen->extent); + madd_v3_v3v3v3(ray->end, gen->min, ray->end, gen->extent); +} + +static bool generator_volume_random_make_sample(const MVolumeSampleGenerator_Random *gen, void *thread_ctx, MeshSample *sample) +{ + MVolumeSampleGenerator_Random_ThreadContext *ctx = thread_ctx; + + Ray ray1, ray2; + // Do all RNG gets at the beggining for keeping consistent state + generator_volume_get_ray(ctx->rng, &ray1); + generator_volume_get_ray(ctx->rng, &ray2); + float t = BLI_rng_get_float(ctx->rng); + + if (ctx->cur_seg + 1 >= ctx->tothits) { + generator_volume_ray_to_bbox(gen, &ray1); + generator_volume_random_cast_ray(ctx, &ray1); + if (ctx->tothits < 2) + return false; + } + + if (ctx->cur_sample >= ctx->cur_tot) { + ctx->cur_seg += 2; + + if (ctx->cur_seg + 1 >= ctx->tothits) { + generator_volume_ray_to_bbox(gen, &ray2); + generator_volume_random_cast_ray(ctx, &ray2); + if (ctx->tothits < 2) + return false; + } + + generator_volume_init_segment(gen, ctx); + } + BVHTreeRayHit *a = &ctx->ray_hits[ctx->cur_seg]; + BVHTreeRayHit *b = &ctx->ray_hits[ctx->cur_seg + 1]; + + if (ctx->cur_sample < ctx->cur_tot) { + + sample->orig_verts[0] = SAMPLE_INDEX_INVALID; + sample->orig_verts[1] = SAMPLE_INDEX_INVALID; + sample->orig_verts[2] = SAMPLE_INDEX_INVALID; + + interp_v3_v3v3(sample->orig_weights, a->co, b->co, t); + + ctx->cur_sample += 1; + + return true; + } + + return false; +} + +MeshSampleGenerator *BKE_mesh_sample_gen_volume_random_bbray(unsigned int seed, float density) +{ + MVolumeSampleGenerator_Random *gen; + + gen = MEM_callocN(sizeof(MVolumeSampleGenerator_Random), "MVolumeSampleGenerator_Random"); + sample_generator_init(&gen->base, + (GeneratorFreeFp)generator_volume_random_free, + (GeneratorBindFp)generator_volume_random_bind, + (GeneratorUnbindFp) generator_volume_random_unbind, + (GeneratorThreadContextCreateFp)generator_volume_random_thread_context_create, + (GeneratorThreadContextFreeFp)generator_volume_random_thread_context_free, + (GeneratorMakeSampleFp)generator_volume_random_make_sample, + NULL); + + gen->seed = seed; + gen->density = density; + + return &gen->base; +} + +/* ------------------------------------------------------------------------- */ + +void BKE_mesh_sample_free_generator(MeshSampleGenerator *gen) +{ + if (gen->default_ctx) { + if (gen->thread_context_free) { + gen->thread_context_free(gen, gen->default_ctx); + } + } + + BKE_mesh_sample_generator_unbind(gen); + + gen->free(gen); +} + + +/* ==== Sampling ==== */ + +void BKE_mesh_sample_generator_bind(MeshSampleGenerator *gen, DerivedMesh *dm) +{ + BLI_assert(gen->dm == NULL && "Generator already bound"); + + gen->dm = dm; + if (gen->bind) { + gen->bind(gen); + } +} + +void BKE_mesh_sample_generator_unbind(MeshSampleGenerator *gen) +{ + if (gen->dm) { + if (gen->unbind) { + gen->unbind(gen); + } + gen->dm = NULL; + } +} + +unsigned int BKE_mesh_sample_gen_get_max_samples(const MeshSampleGenerator *gen) +{ + if (gen->get_max_samples) { + return gen->get_max_samples(gen); + } + return 0; +} + +bool BKE_mesh_sample_generate(MeshSampleGenerator *gen, struct MeshSample *sample) +{ + if (!gen->default_ctx && gen->thread_context_create) { + gen->default_ctx = gen->thread_context_create(gen, 0); + } + + return gen->make_sample(gen, gen->default_ctx, sample); +} + +typedef struct MeshSamplePoolData { + const MeshSampleGenerator *gen; + int output_stride; +} MeshSamplePoolData; + +typedef struct MeshSampleTaskData { + void *thread_ctx; + void *output_buffer; + int count; + int result; +} MeshSampleTaskData; + +static void mesh_sample_generate_task_run(TaskPool * __restrict pool, void *taskdata_, int UNUSED(threadid)) +{ + MeshSamplePoolData *pooldata = BLI_task_pool_userdata(pool); + const MeshSampleGenerator *gen = pooldata->gen; + const int output_stride = pooldata->output_stride; + + GeneratorMakeSampleFp make_sample = gen->make_sample; + MeshSampleTaskData *taskdata = taskdata_; + void *thread_ctx = taskdata->thread_ctx; + const int count = taskdata->count; + MeshSample *sample = taskdata->output_buffer; + + int i = 0; + for (; i < count; ++i, sample = (MeshSample *)((char *)sample + output_stride)) { + if (!make_sample(gen, thread_ctx, sample)) { + break; + } + } + + taskdata->result = i; +} + +int BKE_mesh_sample_generate_batch_ex(MeshSampleGenerator *gen, + void *output_buffer, int output_stride, int count, + bool use_threads) +{ + if (use_threads) { + TaskScheduler *scheduler = BLI_task_scheduler_get(); + + MeshSamplePoolData pool_data; + pool_data.gen = gen; + pool_data.output_stride = output_stride; + TaskPool *task_pool = BLI_task_pool_create(scheduler, &pool_data); + + const int num_tasks = (count + gen->task_size - 1) / gen->task_size; + MeshSampleTaskData *task_data = MEM_callocN(sizeof(MeshSampleTaskData) * (unsigned int)num_tasks, "mesh sample task data"); + + { + MeshSampleTaskData *td = task_data; + int start = 0; + for (int i = 0; i < num_tasks; ++i, ++td, start += gen->task_size) { + if (gen->thread_context_create) { + td->thread_ctx = gen->thread_context_create(gen, start); + } + td->output_buffer = (char *)output_buffer + start * output_stride; + td->count = min_ii(count - start, gen->task_size); + + BLI_task_pool_push(task_pool, mesh_sample_generate_task_run, td, false, TASK_PRIORITY_LOW); + } + } + + BLI_task_pool_work_and_wait(task_pool); + BLI_task_pool_free(task_pool); + + int totresult = 0; + { + MeshSampleTaskData *td = task_data; + for (int i = 0; i < num_tasks; ++i, ++td) { + totresult += td->result; + } + } + + if (gen->thread_context_free) { + MeshSampleTaskData *td = task_data; + for (int i = 0; i < num_tasks; ++i, ++td) { + if (td->thread_ctx) { + gen->thread_context_free(gen, td->thread_ctx); + } + } + } + MEM_freeN(task_data); + + return totresult; + } + else { + void *thread_ctx = NULL; + if (gen->thread_context_create) { + thread_ctx = gen->thread_context_create(gen, 0); + } + + MeshSample *sample = output_buffer; + int i = 0; + for (; i < count; ++i, sample = (MeshSample *)((char *)sample + output_stride)) { + if (!gen->make_sample(gen, thread_ctx, sample)) { + break; + } + } + + if (thread_ctx && gen->thread_context_free) { + gen->thread_context_free(gen, thread_ctx); + } + + return i; + } +} + +int BKE_mesh_sample_generate_batch(MeshSampleGenerator *gen, + MeshSample *output_buffer, int count) +{ + return BKE_mesh_sample_generate_batch_ex(gen, output_buffer, sizeof(MeshSample), count, true); +} + +/* ==== Utilities ==== */ + +#include "DNA_particle_types.h" + +#include "BKE_bvhutils.h" +#include "BKE_particle.h" + +bool BKE_mesh_sample_from_particle(MeshSample *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_tri_v3(w, co1, co2, co3, 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_tri_v3(w, co3, co4, co1, 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(MeshSample *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_quad_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/object.c b/source/blender/blenkernel/intern/object.c index 875d716305f..2cb4356a52a 100644 --- a/source/blender/blenkernel/intern/object.c +++ b/source/blender/blenkernel/intern/object.c @@ -42,6 +42,7 @@ #include "DNA_constraint_types.h" #include "DNA_gpencil_types.h" #include "DNA_group_types.h" +#include "DNA_groom_types.h" #include "DNA_key_types.h" #include "DNA_lamp_types.h" #include "DNA_lattice_types.h" @@ -74,6 +75,7 @@ #include "BKE_pbvh.h" #include "BKE_main.h" #include "BKE_global.h" +#include "BKE_groom.h" #include "BKE_idprop.h" #include "BKE_armature.h" #include "BKE_action.h" @@ -337,6 +339,13 @@ void BKE_object_free_derived_caches(Object *ob) atomic_fetch_and_or_int32(&cu->bb->flag, BOUNDBOX_DIRTY); } } + else if (ELEM(ob->type, OB_GROOM)) { + Groom *groom = ob->data; + + if (groom && groom->bb) { + atomic_fetch_and_or_int32(&groom->bb->flag, BOUNDBOX_DIRTY); + } + } if (ob->bb) { MEM_freeN(ob->bb); @@ -527,6 +536,11 @@ bool BKE_object_is_in_editmode(const Object *ob) if (cu->editnurb) return true; } + else if (ob->type == OB_GROOM) { + Groom *groom = ob->data; + if (groom->editgroom) + return true; + } return false; } @@ -602,6 +616,7 @@ static const char *get_obdata_defname(int type) case OB_SURF: return DATA_("Surf"); case OB_FONT: return DATA_("Text"); case OB_MBALL: return DATA_("Mball"); + case OB_GROOM: return DATA_("Groom"); case OB_CAMERA: return DATA_("Camera"); case OB_LAMP: return DATA_("Lamp"); case OB_LATTICE: return DATA_("Lattice"); @@ -632,6 +647,7 @@ void *BKE_object_obdata_add_from_type(Main *bmain, int type, const char *name) case OB_ARMATURE: return BKE_armature_add(bmain, name); case OB_SPEAKER: return BKE_speaker_add(bmain, name); case OB_LIGHTPROBE:return BKE_lightprobe_add(bmain, name); + case OB_GROOM: return BKE_groom_add(bmain, name); case OB_EMPTY: return NULL; default: printf("%s: Internal error, bad type: %d\n", __func__, type); diff --git a/source/blender/blenkernel/intern/object_update.c b/source/blender/blenkernel/intern/object_update.c index c70e07e6c4c..10ccc9fb672 100644 --- a/source/blender/blenkernel/intern/object_update.c +++ b/source/blender/blenkernel/intern/object_update.c @@ -61,6 +61,7 @@ #include "BKE_mball.h" #include "BKE_mesh.h" #include "BKE_image.h" +#include "BKE_groom.h" #include "MEM_guardedalloc.h" #include "DEG_depsgraph.h" @@ -323,6 +324,9 @@ void BKE_object_eval_uber_data(const EvaluationContext *eval_ctx, case OB_MBALL: BKE_mball_batch_cache_dirty(ob->data, BKE_MBALL_BATCH_DIRTY_ALL); break; + case OB_GROOM: + BKE_groom_batch_cache_dirty(ob->data, BKE_GROOM_BATCH_DIRTY_ALL); + break; } if (DEG_depsgraph_use_copy_on_write()) { @@ -428,6 +432,10 @@ void BKE_object_data_select_update(const EvaluationContext *UNUSED(eval_ctx), BKE_lattice_batch_cache_dirty((struct Lattice *)object_data, BKE_CURVE_BATCH_DIRTY_SELECT); break; + case ID_GM: + BKE_groom_batch_cache_dirty((struct Groom *)object_data, + BKE_GROOM_BATCH_DIRTY_SELECT); + break; default: break; } diff --git a/source/blender/blenkernel/intern/particle.c b/source/blender/blenkernel/intern/particle.c index 1917e370aa9..713d523cb1e 100644 --- a/source/blender/blenkernel/intern/particle.c +++ b/source/blender/blenkernel/intern/particle.c @@ -1420,6 +1420,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_final, 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], diff --git a/source/blender/blenlib/BLI_math_geom.h b/source/blender/blenlib/BLI_math_geom.h index ffe0ce11cef..d58e3022e27 100644 --- a/source/blender/blenlib/BLI_math_geom.h +++ b/source/blender/blenlib/BLI_math_geom.h @@ -339,6 +339,8 @@ bool clip_segment_v3_plane_n( /****************************** Interpolation ********************************/ void interp_weights_tri_v3(float w[3], const float a[3], const float b[3], const float c[3], const float p[3]); void interp_weights_quad_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_quad_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/intern/math_geom.c b/source/blender/blenlib/intern/math_geom.c index e179447936a..171af1c8264 100644 --- a/source/blender/blenlib/intern/math_geom.c +++ b/source/blender/blenlib/intern/math_geom.c @@ -2976,6 +2976,71 @@ void interp_weights_quad_v3(float w[4], const float v1[3], const float v2[3], co } } +void interp_weights_quad_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/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c index 4308fe0fee3..ac5673e1402 100644 --- a/source/blender/blenloader/intern/readfile.c +++ b/source/blender/blenloader/intern/readfile.c @@ -72,8 +72,10 @@ #include "DNA_effect_types.h" #include "DNA_fileglobal_types.h" #include "DNA_genfile.h" +#include "DNA_groom_types.h" #include "DNA_group_types.h" #include "DNA_gpencil_types.h" +#include "DNA_hair_types.h" #include "DNA_ipo_types.h" #include "DNA_key_types.h" #include "DNA_lattice_types.h" @@ -5214,6 +5216,25 @@ static void direct_link_pose(FileData *fd, bPose *pose) } } +static void direct_link_hair(FileData *fd, HairSystem* hsys) +{ + if (!hsys) { + return; + } + + hsys->pattern = newdataadr(fd, hsys->pattern); + if ( hsys->pattern ) + { + hsys->pattern->follicles = newdataadr(fd, hsys->pattern->follicles); + } + + hsys->curves = newdataadr(fd, hsys->curves); + hsys->verts = newdataadr(fd, hsys->verts); + + hsys->draw_batch_cache = NULL; + hsys->draw_texture_cache = NULL; +} + static void direct_link_modifiers(FileData *fd, ListBase *lb) { ModifierData *md; @@ -5536,6 +5557,14 @@ static void direct_link_modifiers(FileData *fd, ListBase *lb) } } } + else if (md->type == eModifierType_Fur) { + FurModifierData *fmd = (FurModifierData *)md; + + fmd->hair_system = newdataadr(fd, fmd->hair_system); + direct_link_hair(fd, fmd->hair_system); + + fmd->draw_settings = newdataadr(fd, fmd->draw_settings); + } } } @@ -8394,6 +8423,50 @@ static void direct_link_linestyle(FileData *fd, FreestyleLineStyle *linestyle) } } +/* ************ READ GROOM *************** */ + +static void lib_link_groom(FileData *fd, Main *bmain) +{ + for (Groom *groom = bmain->grooms.first; groom; groom = groom->id.next) { + ID *id = (ID *)groom; + + if ((id->tag & LIB_TAG_NEED_LINK) == 0) { + continue; + } + IDP_LibLinkProperty(id->properties, fd); + id_us_ensure_real(id); + + groom->scalp_object = newlibadr(fd, id->lib, groom->scalp_object); + + id->tag &= ~LIB_TAG_NEED_LINK; + } +} + +static void direct_link_groom(FileData *fd, Groom *groom) +{ + groom->adt= newdataadr(fd, groom->adt); + direct_link_animdata(fd, groom->adt); + + link_list(fd, &groom->bundles); + for (GroomBundle *bundle = groom->bundles.first; bundle; bundle = bundle->next) + { + bundle->sections = newdataadr(fd, bundle->sections); + bundle->verts = newdataadr(fd, bundle->verts); + bundle->scalp_region = newdataadr(fd, bundle->scalp_region); + bundle->curvecache = NULL; + bundle->curvesize = 0; + bundle->totcurvecache = 0; + } + + groom->hair_system = newdataadr(fd, groom->hair_system); + direct_link_hair(fd, groom->hair_system); + groom->hair_draw_settings = newdataadr(fd, groom->hair_draw_settings); + + groom->bb = NULL; + groom->editgroom = NULL; + groom->batch_cache = NULL; +} + /* ************** GENERAL & MAIN ******************** */ @@ -8691,6 +8764,9 @@ static BHead *read_libblock(FileData *fd, Main *main, BHead *bhead, const short case ID_WS: direct_link_workspace(fd, (WorkSpace *)id, main); break; + case ID_GM: + direct_link_groom(fd, (Groom *)id); + break; } oldnewmap_free_unused(fd->datamap); @@ -8873,6 +8949,7 @@ static void lib_link_all(FileData *fd, Main *main) lib_link_gpencil(fd, main); lib_link_cachefiles(fd, main); lib_link_workspaces(fd, main); + lib_link_groom(fd, main); lib_link_library(fd, main); /* only init users */ } @@ -9502,6 +9579,11 @@ static void expand_particlesettings(FileData *fd, Main *mainvar, ParticleSetting } } +static void expand_groom(FileData *fd, Main *mainvar, Groom *groom) +{ + expand_doit(fd, mainvar, groom->scalp_object); +} + static void expand_group(FileData *fd, Main *mainvar, Group *group) { GroupObject *go; @@ -10265,6 +10347,9 @@ void BLO_expand_main(void *fdhandle, Main *mainvar) case ID_AC: expand_action(fd, mainvar, (bAction *)id); // XXX deprecated - old animation system break; + case ID_GM: + expand_groom(fd, mainvar, (Groom *)id); + break; case ID_GR: expand_group(fd, mainvar, (Group *)id); break; diff --git a/source/blender/blenloader/intern/writefile.c b/source/blender/blenloader/intern/writefile.c index 7f4e7fdc646..973fac1d13a 100644 --- a/source/blender/blenloader/intern/writefile.c +++ b/source/blender/blenloader/intern/writefile.c @@ -118,9 +118,11 @@ #include "DNA_controller_types.h" #include "DNA_dynamicpaint_types.h" #include "DNA_genfile.h" +#include "DNA_groom_types.h" #include "DNA_group_types.h" #include "DNA_gpencil_types.h" #include "DNA_fileglobal_types.h" +#include "DNA_hair_types.h" #include "DNA_key_types.h" #include "DNA_lattice_types.h" #include "DNA_lamp_types.h" @@ -1765,6 +1767,18 @@ static void write_fmaps(WriteData *wd, ListBase *fbase) } } +static void write_hair(WriteData *wd, HairSystem *hsys) +{ + if ( hsys->pattern ) + { + writestruct(wd, DATA, HairPattern, 1, hsys->pattern); + writestruct(wd, DATA, HairFollicle, hsys->pattern->num_follicles, hsys->pattern->follicles); + } + + writestruct(wd, DATA, HairGuideCurve, hsys->totcurves, hsys->curves); + writestruct(wd, DATA, HairGuideVertex, hsys->totverts, hsys->verts); +} + static void write_modifiers(WriteData *wd, ListBase *modbase) { ModifierData *md; @@ -1936,6 +1950,18 @@ static void write_modifiers(WriteData *wd, ListBase *modbase) } } } + else if (md->type == eModifierType_Fur) { + FurModifierData *fmd = (FurModifierData *)md; + + if (fmd->hair_system) { + writestruct(wd, DATA, HairSystem, 1, fmd->hair_system); + write_hair(wd, fmd->hair_system); + } + if (fmd->draw_settings) + { + writestruct(wd, DATA, HairDrawSettings, 1, fmd->draw_settings); + } + } } } @@ -3822,6 +3848,32 @@ static void write_workspace(WriteData *wd, WorkSpace *workspace) writelist(wd, DATA, TransformOrientation, transform_orientations); } +static void write_groom(WriteData *wd, Groom *groom) +{ + writestruct(wd, ID_GM, Groom, 1, groom); + write_iddata(wd, &groom->id); + if (groom->adt) { + write_animdata(wd, groom->adt); + } + + writelist(wd, DATA, GroomBundle, &groom->bundles); + for (GroomBundle *bundle = groom->bundles.first; bundle; bundle = bundle->next) + { + writestruct(wd, DATA, GroomSection, bundle->totsections, bundle->sections); + writestruct(wd, DATA, GroomSectionVertex, bundle->totverts, bundle->verts); + writestruct(wd, DATA, MeshSample, bundle->numshapeverts + 1, bundle->scalp_region); + } + + if (groom->hair_system) { + writestruct(wd, DATA, HairSystem, 1, groom->hair_system); + write_hair(wd, groom->hair_system); + } + if (groom->hair_draw_settings) + { + writestruct(wd, DATA, HairDrawSettings, 1, groom->hair_draw_settings); + } +} + /* Keep it last of write_foodata functions. */ static void write_libraries(WriteData *wd, Main *main) { @@ -4124,6 +4176,9 @@ static bool write_file_handle( case ID_CF: write_cachefile(wd, (CacheFile *)id); break; + case ID_GM: + write_groom(wd, (Groom *)id); + break; case ID_LI: /* Do nothing, handled below - and should never be reached. */ BLI_assert(0); diff --git a/source/blender/blentranslation/BLT_translation.h b/source/blender/blentranslation/BLT_translation.h index ed1993015f6..870375936aa 100644 --- a/source/blender/blentranslation/BLT_translation.h +++ b/source/blender/blentranslation/BLT_translation.h @@ -154,6 +154,7 @@ bool BLT_lang_is_ime_supported(void); #define BLT_I18NCONTEXT_ID_WINDOWMANAGER "WindowManager" #define BLT_I18NCONTEXT_ID_MOVIECLIP "MovieClip" #define BLT_I18NCONTEXT_ID_MASK "Mask" +#define BLT_I18NCONTEXT_ID_GROOM "Groom" /* Helper for bpy.app.i18n object... */ typedef struct { @@ -178,6 +179,7 @@ typedef struct { BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_ID_CURVE, "id_curve"), \ BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_ID_FREESTYLELINESTYLE, "id_fs_linestyle"), \ BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_ID_GPENCIL, "id_gpencil"), \ + BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_ID_GROOM, "id_groom"), \ BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_ID_GROUP, "id_group"), \ BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_ID_ID, "id_id"), \ BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_ID_IMAGE, "id_image"), \ diff --git a/source/blender/bmesh/CMakeLists.txt b/source/blender/bmesh/CMakeLists.txt index 5245d24a075..a4992222216 100644 --- a/source/blender/bmesh/CMakeLists.txt +++ b/source/blender/bmesh/CMakeLists.txt @@ -54,6 +54,7 @@ set(SRC operators/bmo_dupe.c operators/bmo_edgenet.c operators/bmo_extrude.c + operators/bmo_face_island.c operators/bmo_fill_attribute.c operators/bmo_fill_edgeloop.c operators/bmo_fill_grid.c diff --git a/source/blender/bmesh/intern/bmesh_opdefines.c b/source/blender/bmesh/intern/bmesh_opdefines.c index 4847ae4be42..37a4fb5c29c 100644 --- a/source/blender/bmesh/intern/bmesh_opdefines.c +++ b/source/blender/bmesh/intern/bmesh_opdefines.c @@ -2017,6 +2017,26 @@ static BMOpDefine bmo_symmetrize_def = { BMO_OPTYPE_FLAG_SELECT_VALIDATE), }; +/* + * Face island boundary. + */ +static BMOpDefine bmo_face_island_boundary_def = { + "face_island_boundary", + /* slots_in */ + {{"faces", BMO_OP_SLOT_ELEMENT_BUF, {BM_FACE}}, + {{'\0'}}, + }, + /* slots_out */ + {{"boundary", BMO_OP_SLOT_ELEMENT_BUF, {BM_LOOP}}, + {{'\0'}}, + }, + bmo_face_island_boundary_exec, + (BMO_OPTYPE_FLAG_UNTAN_MULTIRES | + BMO_OPTYPE_FLAG_NORMALS_CALC | + BMO_OPTYPE_FLAG_SELECT_FLUSH | + BMO_OPTYPE_FLAG_SELECT_VALIDATE), +}; + const BMOpDefine *bmo_opdefines[] = { &bmo_automerge_def, &bmo_average_vert_facedata_def, @@ -2052,6 +2072,7 @@ const BMOpDefine *bmo_opdefines[] = { &bmo_duplicate_def, &bmo_holes_fill_def, &bmo_face_attribute_fill_def, + &bmo_face_island_boundary_def, &bmo_offset_edgeloops_def, &bmo_edgeloop_fill_def, &bmo_edgenet_fill_def, diff --git a/source/blender/bmesh/intern/bmesh_operators_private.h b/source/blender/bmesh/intern/bmesh_operators_private.h index 5548ee7c361..22a9edab321 100644 --- a/source/blender/bmesh/intern/bmesh_operators_private.h +++ b/source/blender/bmesh/intern/bmesh_operators_private.h @@ -63,6 +63,7 @@ void bmo_dissolve_degenerate_exec(BMesh *bm, BMOperator *op); void bmo_duplicate_exec(BMesh *bm, BMOperator *op); void bmo_edgeloop_fill_exec(BMesh *bm, BMOperator *op); void bmo_face_attribute_fill_exec(BMesh *bm, BMOperator *op); +void bmo_face_island_boundary_exec(BMesh *bm, BMOperator *op); void bmo_holes_fill_exec(BMesh *bm, BMOperator *op); void bmo_edgenet_fill_exec(BMesh *bm, BMOperator *op); void bmo_edgenet_prepare_exec(BMesh *bm, BMOperator *op); diff --git a/source/blender/bmesh/intern/bmesh_walkers_impl.c b/source/blender/bmesh/intern/bmesh_walkers_impl.c index 279440984bb..45288f1350d 100644 --- a/source/blender/bmesh/intern/bmesh_walkers_impl.c +++ b/source/blender/bmesh/intern/bmesh_walkers_impl.c @@ -698,8 +698,6 @@ static void *bmw_IslandboundWalker_step(BMWalker *walker) { BMwIslandboundWalker *iwalk, owalk; BMVert *v; - BMEdge *e; - BMFace *f; BMLoop *l; /* int found = 0; */ @@ -708,31 +706,24 @@ static void *bmw_IslandboundWalker_step(BMWalker *walker) iwalk = &owalk; l = iwalk->curloop; - e = l->e; - v = BM_edge_other_vert(e, iwalk->lastv); + v = BM_edge_other_vert(l->e, iwalk->lastv); /* pop off current state */ BMW_state_remove(walker); - f = l->f; - while (1) { l = BM_loop_other_edge_loop(l, v); if (BM_loop_is_manifold(l)) { l = l->radial_next; - f = l->f; - e = l->e; - if (!bmw_mask_check_face(walker, f)) { + if (!bmw_mask_check_face(walker, l->f)) { l = l->radial_next; break; } } else { /* treat non-manifold edges as boundaries */ - f = l->f; - e = l->e; break; } } diff --git a/source/blender/bmesh/operators/bmo_face_island.c b/source/blender/bmesh/operators/bmo_face_island.c new file mode 100644 index 00000000000..e52052a28d3 --- /dev/null +++ b/source/blender/bmesh/operators/bmo_face_island.c @@ -0,0 +1,238 @@ +/* + * ***** 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/bmesh/operators/bmo_face_island.c + * \ingroup bmesh + * + * Face island search. + */ + +#include "MEM_guardedalloc.h" + +#include "BLI_array.h" +#include "BLI_math.h" + +#include "bmesh.h" + +#include "intern/bmesh_operators_private.h" + + +#define FACE_MARK 1 + +static BMLoop* bmo_face_island_find_start_loop(BMesh *bm, BMOperator *op) +{ + BMFace *f; + BMOIter oiter; + BMO_ITER (f, &oiter, op->slots_in, "faces", BM_FACE) + { + BMLoop *l; + BMIter l_iter; + BM_ITER_ELEM(l, &l_iter, f, BM_LOOPS_OF_FACE) + { + BMLoop *lr = l; + do + { + if (!BM_loop_is_manifold(lr)) { + /* treat non-manifold edges as boundaries */ + return lr; + } + if (!BMO_face_flag_test(bm, lr->f, FACE_MARK)) + { + return lr; + } + lr = lr->radial_next; + } + while (lr != l); + } + } + return NULL; +} + +void bmo_face_island_boundary_exec(BMesh *bm, BMOperator *op) +{ + BMO_slot_buffer_flag_enable(bm, op->slots_in, "faces", BM_FACE, FACE_MARK); + + BMLoop *l_start = bmo_face_island_find_start_loop(bm, op); + if (!l_start) + { + return; + } + + BMLoop **boundary = NULL; + BLI_array_declare(boundary); + + BMWalker walker; + BMW_init(&walker, bm, BMW_ISLANDBOUND, + BMW_MASK_NOP, BMW_MASK_NOP, FACE_MARK, + BMW_FLAG_NOP, /* no need to check BMW_FLAG_TEST_HIDDEN, faces are already marked by the bmo */ + BMW_NIL_LAY); + + for (BMLoop *l_iter = BMW_begin(&walker, l_start); l_iter; l_iter = BMW_step(&walker)) + { + BLI_array_append(boundary, l_iter); + } + BMW_end(&walker); + + { + BMOpSlot *slot = BMO_slot_get(op->slots_out, "boundary"); + BMO_slot_buffer_from_array(op, slot, (BMHeader **)boundary, BLI_array_len(boundary)); + BLI_array_free(boundary); + } + +#if 0 + BMOIter oiter; + BMFace *f; + BMFace ***regions = NULL; + BMFace **faces = NULL; + BLI_array_declare(regions); + BLI_array_declare(faces); + BMFace *act_face = bm->act_face; + BMWalker regwalker; + int i; + + const bool use_verts = BMO_slot_bool_get(op->slots_in, "use_verts"); + + if (use_verts) { + /* tag verts that start out with only 2 edges, + * don't remove these later */ + BMIter viter; + BMVert *v; + + BM_ITER_MESH (v, &viter, bm, BM_VERTS_OF_MESH) { + BMO_vert_flag_set(bm, v, VERT_MARK, !BM_vert_is_edge_pair(v)); + } + } + + BMO_slot_buffer_flag_enable(bm, op->slots_in, "faces", BM_FACE, FACE_MARK | FACE_TAG); + + /* collect region */ + BMO_ITER (f, &oiter, op->slots_in, "faces", BM_FACE) { + BMFace *f_iter; + if (!BMO_face_flag_test(bm, f, FACE_TAG)) { + continue; + } + + BLI_array_empty(faces); + faces = NULL; /* forces different allocatio */ + + BMW_init(®walker, bm, BMW_ISLAND_MANIFOLD, + BMW_MASK_NOP, BMW_MASK_NOP, FACE_MARK, + BMW_FLAG_NOP, /* no need to check BMW_FLAG_TEST_HIDDEN, faces are already marked by the bmo */ + BMW_NIL_LAY); + + for (f_iter = BMW_begin(®walker, f); f_iter; f_iter = BMW_step(®walker)) { + BLI_array_append(faces, f_iter); + } + BMW_end(®walker); + + for (i = 0; i < BLI_array_count(faces); i++) { + f_iter = faces[i]; + BMO_face_flag_disable(bm, f_iter, FACE_TAG); + BMO_face_flag_enable(bm, f_iter, FACE_ORIG); + } + + if (BMO_error_occurred(bm)) { + BMO_error_clear(bm); + BMO_error_raise(bm, op, BMERR_DISSOLVEFACES_FAILED, NULL); + goto cleanup; + } + + BLI_array_append(faces, NULL); + BLI_array_append(regions, faces); + } + + /* track how many faces we should end up with */ + int totface_target = bm->totface; + + for (i = 0; i < BLI_array_count(regions); i++) { + BMFace *f_new; + int tot = 0; + + faces = regions[i]; + if (!faces[0]) { + BMO_error_raise(bm, op, BMERR_DISSOLVEFACES_FAILED, + "Could not find boundary of dissolve region"); + goto cleanup; + } + + while (faces[tot]) + tot++; + + f_new = BM_faces_join(bm, faces, tot, true); + + if (f_new) { + /* maintain active face */ + if (act_face && bm->act_face == NULL) { + bm->act_face = f_new; + } + totface_target -= tot - 1; + } + else { + BMO_error_raise(bm, op, BMERR_DISSOLVEFACES_FAILED, + "Could not create merged face"); + goto cleanup; + } + + /* if making the new face failed (e.g. overlapping test) + * unmark the original faces for deletion */ + BMO_face_flag_disable(bm, f_new, FACE_ORIG); + BMO_face_flag_enable(bm, f_new, FACE_NEW); + } + + /* Typically no faces need to be deleted */ + if (totface_target != bm->totface) { + BMO_op_callf(bm, op->flag, "delete geom=%ff context=%i", FACE_ORIG, DEL_FACES); + } + + if (use_verts) { + BMIter viter; + BMVert *v, *v_next; + + BM_ITER_MESH_MUTABLE (v, v_next, &viter, bm, BM_VERTS_OF_MESH) { + if (BMO_vert_flag_test(bm, v, VERT_MARK)) { + if (BM_vert_is_edge_pair(v)) { + BM_vert_collapse_edge(bm, v->e, v, true, true); + } + } + } + } + + if (BMO_error_occurred(bm)) { + goto cleanup; + } + + BMO_slot_buffer_from_enabled_flag(bm, op, op->slots_out, "region.out", BM_FACE, FACE_NEW); + +cleanup: + /* free/cleanup */ + for (i = 0; i < BLI_array_count(regions); i++) { + if (regions[i]) MEM_freeN(regions[i]); + } + + BLI_array_free(regions); +#endif +} diff --git a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc index a5a28ce0a8c..994674f95fe 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc @@ -51,6 +51,7 @@ extern "C" { #include "DNA_curve_types.h" #include "DNA_effect_types.h" #include "DNA_gpencil_types.h" +#include "DNA_groom_types.h" #include "DNA_group_types.h" #include "DNA_key_types.h" #include "DNA_lamp_types.h" @@ -76,6 +77,7 @@ extern "C" { #include "BKE_effect.h" #include "BKE_fcurve.h" #include "BKE_idcode.h" +#include "BKE_groom.h" #include "BKE_group.h" #include "BKE_key.h" #include "BKE_lattice.h" @@ -515,6 +517,7 @@ void DepsgraphNodeBuilder::build_object_data(Object *object) case OB_SURF: case OB_MBALL: case OB_LATTICE: + case OB_GROOM: build_obdata_geom(object); /* TODO(sergey): Only for until we support granular * update of curves. @@ -1099,6 +1102,19 @@ void DepsgraphNodeBuilder::build_obdata_geom(Object *object) op_node->set_as_entry(); break; } + + case OB_GROOM: + { + /* Groom evaluation operations. */ + op_node = add_operation_node(obdata, DEG_NODE_TYPE_GEOMETRY, + function_bind(BKE_groom_eval_geometry, + _1, + (Groom *)obdata_cow), + DEG_OPCODE_PLACEHOLDER, + "Geometry Eval"); + op_node->set_as_entry(); + break; + } } op_node = add_operation_node(obdata, DEG_NODE_TYPE_GEOMETRY, NULL, diff --git a/source/blender/depsgraph/intern/builder/deg_builder_nodes.h b/source/blender/depsgraph/intern/builder/deg_builder_nodes.h index df94671b4c6..c933e139758 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_nodes.h +++ b/source/blender/depsgraph/intern/builder/deg_builder_nodes.h @@ -43,6 +43,7 @@ struct GHash; struct ID; struct Image; struct FCurve; +struct Groom; struct Group; struct Key; struct LayerCollection; diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc index ff1728eb89f..7da2c4f54a1 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc @@ -51,6 +51,7 @@ extern "C" { #include "DNA_curve_types.h" #include "DNA_effect_types.h" #include "DNA_gpencil_types.h" +#include "DNA_groom_types.h" #include "DNA_group_types.h" #include "DNA_key_types.h" #include "DNA_lamp_types.h" @@ -77,6 +78,7 @@ extern "C" { #include "BKE_effect.h" #include "BKE_collision.h" #include "BKE_fcurve.h" +#include "BKE_groom.h" #include "BKE_group.h" #include "BKE_key.h" #include "BKE_library.h" @@ -528,6 +530,7 @@ void DepsgraphRelationBuilder::build_object_data(Object *object) case OB_SURF: case OB_MBALL: case OB_LATTICE: + case OB_GROOM: { build_obdata_geom(object); break; @@ -1748,6 +1751,19 @@ void DepsgraphRelationBuilder::build_obdata_geom(Object *object) { break; } + + case OB_GROOM: /* Groom */ + { + Groom *groom = (Groom *)obdata; + ComponentKey geometry_key(&groom->id, DEG_NODE_TYPE_GEOMETRY); + + if (groom->scalp_object) + { + ID *scalp_id = &groom->scalp_object->id; + add_relation(ComponentKey(scalp_id, DEG_NODE_TYPE_GEOMETRY), geometry_key, "Scalp Object -> Groom"); + } + break; + } } /* ShapeKeys */ diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations.h b/source/blender/depsgraph/intern/builder/deg_builder_relations.h index df6fb100d22..4ddad462a5a 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_relations.h +++ b/source/blender/depsgraph/intern/builder/deg_builder_relations.h @@ -75,6 +75,7 @@ struct ViewLayer; struct Tex; struct World; struct EffectorWeights; +struct Groom; struct PropertyRNA; diff --git a/source/blender/depsgraph/intern/depsgraph_tag.cc b/source/blender/depsgraph/intern/depsgraph_tag.cc index 6a6ebd1be44..2091d597ffa 100644 --- a/source/blender/depsgraph/intern/depsgraph_tag.cc +++ b/source/blender/depsgraph/intern/depsgraph_tag.cc @@ -97,6 +97,7 @@ void depsgraph_geometry_tag_to_component(const ID *id, case OB_FONT: case OB_LATTICE: case OB_MBALL: + case OB_GROOM: *component_type = DEG_NODE_TYPE_GEOMETRY; break; case OB_ARMATURE: diff --git a/source/blender/draw/CMakeLists.txt b/source/blender/draw/CMakeLists.txt index eb66e9c20ea..1529ee7022e 100644 --- a/source/blender/draw/CMakeLists.txt +++ b/source/blender/draw/CMakeLists.txt @@ -57,11 +57,14 @@ set(SRC intern/draw_cache.c intern/draw_cache_impl_curve.c intern/draw_cache_impl_displist.c + intern/draw_cache_impl_groom.c + intern/draw_cache_impl_hair.c intern/draw_cache_impl_lattice.c intern/draw_cache_impl_mesh.c intern/draw_cache_impl_metaball.c intern/draw_cache_impl_particles.c intern/draw_common.c + intern/draw_hair.c intern/draw_instance_data.c intern/draw_manager.c intern/draw_manager_data.c @@ -73,6 +76,7 @@ set(SRC intern/draw_view.c modes/edit_armature_mode.c modes/edit_curve_mode.c + modes/edit_groom_mode.c modes/edit_lattice_mode.c modes/edit_mesh_mode.c modes/edit_mesh_mode_text.c @@ -146,6 +150,7 @@ data_to_c_simple(engines/eevee/shaders/default_world_frag.glsl SRC) data_to_c_simple(engines/eevee/shaders/background_vert.glsl SRC) data_to_c_simple(engines/eevee/shaders/concentric_samples_lib.glsl SRC) data_to_c_simple(engines/eevee/shaders/common_uniforms_lib.glsl SRC) +data_to_c_simple(engines/eevee/shaders/hair_lib.glsl SRC) data_to_c_simple(engines/eevee/shaders/lamps_lib.glsl SRC) data_to_c_simple(engines/eevee/shaders/lightprobe_lib.glsl SRC) data_to_c_simple(engines/eevee/shaders/lightprobe_filter_glossy_frag.glsl SRC) @@ -221,6 +226,8 @@ data_to_c_simple(modes/shaders/edit_mesh_overlay_facefill_frag.glsl SRC) data_to_c_simple(modes/shaders/edit_curve_overlay_frag.glsl SRC) data_to_c_simple(modes/shaders/edit_curve_overlay_handle_geom.glsl SRC) data_to_c_simple(modes/shaders/edit_curve_overlay_loosevert_vert.glsl SRC) +data_to_c_simple(modes/shaders/edit_groom_overlay_frag.glsl SRC) +data_to_c_simple(modes/shaders/edit_groom_overlay_loosevert_vert.glsl SRC) data_to_c_simple(modes/shaders/edit_lattice_overlay_frag.glsl SRC) data_to_c_simple(modes/shaders/edit_lattice_overlay_loosevert_vert.glsl SRC) data_to_c_simple(modes/shaders/edit_normals_vert.glsl SRC) diff --git a/source/blender/draw/engines/eevee/eevee_materials.c b/source/blender/draw/engines/eevee/eevee_materials.c index fc4439a253c..d146fda5373 100644 --- a/source/blender/draw/engines/eevee/eevee_materials.c +++ b/source/blender/draw/engines/eevee/eevee_materials.c @@ -31,6 +31,8 @@ #include "BLI_rand.h" #include "BLI_string_utils.h" +#include "BKE_DerivedMesh.h" +#include "BKE_groom.h" #include "BKE_particle.h" #include "BKE_paint.h" #include "BKE_pbvh.h" @@ -38,8 +40,11 @@ #include "DNA_world_types.h" #include "DNA_modifier_types.h" #include "DNA_view3d_types.h" +#include "DNA_groom_types.h" +#include "DNA_hair_types.h" #include "GPU_material.h" +#include "GPU_texture.h" #include "eevee_engine.h" #include "eevee_lut.h" @@ -53,6 +58,8 @@ static struct { struct GPUShader *default_prepass_sh; struct GPUShader *default_prepass_clip_sh; + struct GPUShader *default_prepass_hair_fiber_sh; + struct GPUShader *default_prepass_hair_fiber_clip_sh; struct GPUShader *default_lit[VAR_MAT_MAX]; struct GPUShader *default_background; struct GPUShader *update_noise_sh; @@ -99,6 +106,7 @@ extern char datatoc_volumetric_vert_glsl[]; extern char datatoc_volumetric_geom_glsl[]; extern char datatoc_volumetric_frag_glsl[]; extern char datatoc_volumetric_lib_glsl[]; +extern char datatoc_hair_lib_glsl[]; extern Material defmaterial; extern GlobalsUboStorage ts; @@ -280,6 +288,9 @@ static char *eevee_get_defines(int options) if ((options & VAR_MAT_HAIR) != 0) { BLI_dynstr_appendf(ds, "#define HAIR_SHADER\n"); } + if ((options & VAR_MAT_HAIR_FIBERS) != 0) { + BLI_dynstr_append(ds, DRW_hair_shader_defines()); + } if ((options & VAR_MAT_PROBE) != 0) { BLI_dynstr_appendf(ds, "#define PROBE_CAPTURE\n"); } @@ -411,15 +422,20 @@ static void add_standard_uniforms( static void create_default_shader(int options) { + char *vert_str = BLI_string_joinN( + datatoc_hair_lib_glsl, + datatoc_lit_surface_vert_glsl); + char *frag_str = BLI_string_joinN( e_data.frag_shader_lib, datatoc_default_frag_glsl); char *defines = eevee_get_defines(options); - e_data.default_lit[options] = DRW_shader_create(datatoc_lit_surface_vert_glsl, NULL, frag_str, defines); + e_data.default_lit[options] = DRW_shader_create(vert_str, NULL, frag_str, defines); MEM_freeN(defines); + MEM_freeN(vert_str); MEM_freeN(frag_str); } @@ -529,8 +545,6 @@ static void EEVEE_update_viewvecs(float invproj[4][4], float winmat[4][4], float void EEVEE_materials_init(EEVEE_ViewLayerData *sldata, EEVEE_StorageList *stl, EEVEE_FramebufferList *fbl) { if (!e_data.frag_shader_lib) { - char *frag_str = NULL; - /* Shaders */ e_data.shadow_shader_lib = BLI_string_joinN( datatoc_common_uniforms_lib_glsl, @@ -555,7 +569,7 @@ void EEVEE_materials_init(EEVEE_ViewLayerData *sldata, EEVEE_StorageList *stl, E datatoc_lit_surface_frag_glsl, datatoc_lit_surface_frag_glsl, datatoc_volumetric_lib_glsl); - + e_data.frag_shader_lib = BLI_string_joinN( datatoc_common_view_lib_glsl, e_data.shadow_shader_lib); @@ -574,7 +588,11 @@ void EEVEE_materials_init(EEVEE_ViewLayerData *sldata, EEVEE_StorageList *stl, E datatoc_volumetric_lib_glsl, datatoc_volumetric_frag_glsl); - frag_str = BLI_string_joinN( + char *hair_fiber_vert_str = BLI_string_joinN( + datatoc_hair_lib_glsl, + datatoc_prepass_vert_glsl); + + char *frag_str = BLI_string_joinN( e_data.frag_shader_lib, datatoc_default_frag_glsl); @@ -590,7 +608,19 @@ void EEVEE_materials_init(EEVEE_ViewLayerData *sldata, EEVEE_StorageList *stl, E datatoc_prepass_vert_glsl, NULL, datatoc_prepass_frag_glsl, "#define CLIP_PLANES\n"); + e_data.default_prepass_hair_fiber_sh = DRW_shader_create( + hair_fiber_vert_str, NULL, datatoc_prepass_frag_glsl, DRW_hair_shader_defines()); + + { + char defines[256]; + BLI_snprintf(defines, sizeof(defines), "#define CLIP_PLANES\n%s", + DRW_hair_shader_defines()); + e_data.default_prepass_hair_fiber_clip_sh = DRW_shader_create( + hair_fiber_vert_str, NULL, datatoc_prepass_frag_glsl, defines); + } + MEM_freeN(frag_str); + MEM_freeN(hair_fiber_vert_str); e_data.update_noise_sh = DRW_shader_create_fullscreen( datatoc_update_noise_frag_glsl, NULL); @@ -779,25 +809,36 @@ struct GPUMaterial *EEVEE_material_mesh_depth_get( } struct GPUMaterial *EEVEE_material_hair_get( - struct Scene *scene, Material *ma, int shadow_method) + struct Scene *scene, Material *ma, int shadow_method, bool use_fibers) { const void *engine = &DRW_engine_viewport_eevee_type; - int options = VAR_MAT_MESH | VAR_MAT_HAIR; - + int options = VAR_MAT_HAIR | VAR_MAT_MESH; options |= eevee_material_shadow_option(shadow_method); - + if (use_fibers) { + options |= VAR_MAT_HAIR_FIBERS; + } GPUMaterial *mat = DRW_shader_find_from_material(ma, engine, options); if (mat) { return mat; } + char *vert_str = NULL; + { + DynStr *ds_vert = BLI_dynstr_new(); + BLI_dynstr_append(ds_vert, datatoc_hair_lib_glsl); + BLI_dynstr_append(ds_vert, datatoc_lit_surface_vert_glsl); + vert_str = BLI_dynstr_get_cstring(ds_vert); + BLI_dynstr_free(ds_vert); + } + char *defines = eevee_get_defines(options); mat = DRW_shader_create_from_material( scene, ma, engine, options, - datatoc_lit_surface_vert_glsl, NULL, e_data.frag_shader_lib, + vert_str, NULL, e_data.frag_shader_lib, defines); + MEM_freeN(vert_str); MEM_freeN(defines); return mat; @@ -808,7 +849,7 @@ struct GPUMaterial *EEVEE_material_hair_get( **/ static struct DRWShadingGroup *EEVEE_default_shading_group_create( EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata, DRWPass *pass, - bool is_hair, bool is_flat_normal, bool use_blend, bool use_ssr, int shadow_method) + bool is_hair, bool is_hair_fibers, bool is_flat_normal, bool use_blend, bool use_ssr, int shadow_method) { EEVEE_EffectsInfo *effects = vedata->stl->effects; static int ssr_id; @@ -816,6 +857,7 @@ static struct DRWShadingGroup *EEVEE_default_shading_group_create( int options = VAR_MAT_MESH; if (is_hair) options |= VAR_MAT_HAIR; + if (is_hair_fibers) options |= VAR_MAT_HAIR_FIBERS; if (is_flat_normal) options |= VAR_MAT_FLAT; if (use_blend) options |= VAR_MAT_BLEND; if (((effects->enabled_effects & EFFECT_VOLUMETRIC) != 0) && use_blend) options |= VAR_MAT_VOLUME; @@ -837,13 +879,14 @@ static struct DRWShadingGroup *EEVEE_default_shading_group_create( **/ static struct DRWShadingGroup *EEVEE_default_shading_group_get( EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata, - bool is_hair, bool is_flat_normal, bool use_ssr, int shadow_method) + bool is_hair, bool is_hair_fibers, bool is_flat_normal, bool use_ssr, int shadow_method) { static int ssr_id; ssr_id = (use_ssr) ? 1 : -1; int options = VAR_MAT_MESH; if (is_hair) options |= VAR_MAT_HAIR; + if (is_hair_fibers) options |= VAR_MAT_HAIR_FIBERS; if (is_flat_normal) options |= VAR_MAT_FLAT; options |= eevee_material_shadow_option(shadow_method); @@ -853,7 +896,8 @@ static struct DRWShadingGroup *EEVEE_default_shading_group_get( } if (vedata->psl->default_pass[options] == NULL) { - DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_EQUAL | DRW_STATE_CLIP_PLANES | DRW_STATE_WIRE; + //DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_EQUAL | DRW_STATE_CLIP_PLANES | DRW_STATE_WIRE; + DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_EQUAL; vedata->psl->default_pass[options] = DRW_pass_create("Default Lit Pass", state); DRWShadingGroup *shgrp = DRW_shgroup_create(e_data.default_lit[options], vedata->psl->default_pass[options]); @@ -928,20 +972,26 @@ void EEVEE_materials_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata) DRWState state = DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS | DRW_STATE_WIRE; psl->depth_pass = DRW_pass_create("Depth Pass", state); stl->g_data->depth_shgrp = DRW_shgroup_create(e_data.default_prepass_sh, psl->depth_pass); + stl->g_data->hair_fibers_depth_shgrp = DRW_shgroup_create(e_data.default_prepass_hair_fiber_sh, psl->depth_pass); state = DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS | DRW_STATE_CULL_BACK; psl->depth_pass_cull = DRW_pass_create("Depth Pass Cull", state); stl->g_data->depth_shgrp_cull = DRW_shgroup_create(e_data.default_prepass_sh, psl->depth_pass_cull); + stl->g_data->hair_fibers_depth_shgrp_cull = DRW_shgroup_create(e_data.default_prepass_hair_fiber_sh, psl->depth_pass_cull); state = DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS | DRW_STATE_CLIP_PLANES | DRW_STATE_WIRE; psl->depth_pass_clip = DRW_pass_create("Depth Pass Clip", state); stl->g_data->depth_shgrp_clip = DRW_shgroup_create(e_data.default_prepass_clip_sh, psl->depth_pass_clip); + stl->g_data->hair_fibers_depth_shgrp_clip = DRW_shgroup_create(e_data.default_prepass_hair_fiber_clip_sh, psl->depth_pass_clip); DRW_shgroup_uniform_block(stl->g_data->depth_shgrp_clip, "clip_block", sldata->clip_ubo); + DRW_shgroup_uniform_block(stl->g_data->hair_fibers_depth_shgrp_clip, "clip_block", sldata->clip_ubo); state = DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS | DRW_STATE_CLIP_PLANES | DRW_STATE_CULL_BACK; psl->depth_pass_clip_cull = DRW_pass_create("Depth Pass Cull Clip", state); stl->g_data->depth_shgrp_clip_cull = DRW_shgroup_create(e_data.default_prepass_clip_sh, psl->depth_pass_clip_cull); + stl->g_data->hair_fibers_depth_shgrp_clip_cull = DRW_shgroup_create(e_data.default_prepass_hair_fiber_clip_sh, psl->depth_pass_clip_cull); DRW_shgroup_uniform_block(stl->g_data->depth_shgrp_clip_cull, "clip_block", sldata->clip_ubo); + DRW_shgroup_uniform_block(stl->g_data->hair_fibers_depth_shgrp_clip_cull, "clip_block", sldata->clip_ubo); } { @@ -1159,7 +1209,7 @@ static void material_opaque( /* Fallback to default shader */ if (*shgrp == NULL) { bool use_ssr = ((effects->enabled_effects & EFFECT_SSR) != 0); - *shgrp = EEVEE_default_shading_group_get(sldata, vedata, false, use_flat_nor, use_ssr, linfo->shadow_method); + *shgrp = EEVEE_default_shading_group_get(sldata, vedata, false, false, use_flat_nor, use_ssr, linfo->shadow_method); DRW_shgroup_uniform_vec3(*shgrp, "basecol", color_p, 1); DRW_shgroup_uniform_float(*shgrp, "metallic", metal_p, 1); DRW_shgroup_uniform_float(*shgrp, "specular", spec_p, 1); @@ -1241,7 +1291,7 @@ static void material_transparent( if (*shgrp == NULL) { *shgrp = EEVEE_default_shading_group_create( sldata, vedata, psl->transparent_pass, - false, use_flat_nor, true, false, linfo->shadow_method); + false, false, use_flat_nor, true, false, linfo->shadow_method); DRW_shgroup_uniform_vec3(*shgrp, "basecol", color_p, 1); DRW_shgroup_uniform_float(*shgrp, "metallic", metal_p, 1); DRW_shgroup_uniform_float(*shgrp, "specular", spec_p, 1); @@ -1289,6 +1339,178 @@ static void material_transparent( } } +static void material_particle_hair( + EEVEE_Data *vedata, + EEVEE_ViewLayerData *sldata, + Object *ob, + ParticleSystem *psys, + ModifierData *md) +{ + EEVEE_PassList *psl = ((EEVEE_Data *)vedata)->psl; + EEVEE_StorageList *stl = ((EEVEE_Data *)vedata)->stl; + const DRWContextState *draw_ctx = DRW_context_state_get(); + Scene *scene = draw_ctx->scene; + GHash *material_hash = stl->g_data->hair_material_hash; + + if (!psys_check_enabled(ob, psys, false)) { + return; + } + + ParticleSettings *part = psys->part; + float mat[4][4]; + unit_m4(mat); + + bool use_hair = false; + struct Gwn_Batch *hair_geom = NULL; + { + int draw_as = (part->draw_as == PART_DRAW_REND) ? part->ren_as : part->draw_as; + if (draw_as == PART_DRAW_PATH && (psys->pathcache || psys->childcache)) { + use_hair = true; + hair_geom = DRW_cache_particles_get_hair(psys, md); + } + } + + if (use_hair) { + Material *ma = give_current_material(ob, part->omat); + if (ma == NULL) { + ma = &defmaterial; + } + + DRW_shgroup_call_add(stl->g_data->depth_shgrp, hair_geom, mat); + DRW_shgroup_call_add(stl->g_data->depth_shgrp_clip, hair_geom, mat); + + DRWShadingGroup *shgrp = BLI_ghash_lookup(material_hash, (const void *)ma); + if (!shgrp) { + float *color_p = &ma->r; + float *metal_p = &ma->ray_mirror; + float *spec_p = &ma->spec; + float *rough_p = &ma->gloss_mir; + + if (ma->use_nodes && ma->nodetree) { + struct GPUMaterial *gpumat = EEVEE_material_hair_get(scene, ma, false, sldata->lamps->shadow_method); + + shgrp = DRW_shgroup_material_create(gpumat, psl->material_pass); + if (shgrp) { + add_standard_uniforms(shgrp, sldata, vedata, NULL, NULL, false, false); + + BLI_ghash_insert(material_hash, ma, shgrp); + } + else { + /* Shader failed : pink color */ + static float col[3] = {1.0f, 0.0f, 1.0f}; + static float half = 0.5f; + + color_p = col; + metal_p = spec_p = rough_p = ½ + } + } + + /* Fallback to default shader */ + if (shgrp == NULL) { + bool use_ssr = ((stl->effects->enabled_effects & EFFECT_SSR) != 0); + shgrp = EEVEE_default_shading_group_get(sldata, vedata, true, false, false, use_ssr, + sldata->lamps->shadow_method); + DRW_shgroup_uniform_vec3(shgrp, "basecol", color_p, 1); + DRW_shgroup_uniform_float(shgrp, "metallic", metal_p, 1); + DRW_shgroup_uniform_float(shgrp, "specular", spec_p, 1); + DRW_shgroup_uniform_float(shgrp, "roughness", rough_p, 1); + + BLI_ghash_insert(material_hash, ma, shgrp); + } + } + + if (shgrp) { + DRW_shgroup_call_add(shgrp, hair_geom, mat); + } + } +} + +static void material_hair( + EEVEE_Data *vedata, + EEVEE_ViewLayerData *sldata, + Object *ob, + HairSystem *hsys, + DerivedMesh *scalp) +{ + EEVEE_PassList *psl = ((EEVEE_Data *)vedata)->psl; + EEVEE_StorageList *stl = ((EEVEE_Data *)vedata)->stl; + const DRWContextState *draw_ctx = DRW_context_state_get(); + Scene *scene = draw_ctx->scene; + GHash *material_hash = stl->g_data->hair_material_hash; + /* TODO */ + const int subdiv = 0; + float mat[4][4]; + copy_m4_m4(mat, ob->obmat); + + const DRWHairFiberTextureBuffer *fiber_buffer = NULL; + struct Gwn_Batch *hair_geom = DRW_cache_hair_get_fibers(hsys, scalp, subdiv, &fiber_buffer); + + if (!hsys->draw_texture_cache) { + hsys->draw_texture_cache = DRW_texture_create_2D(fiber_buffer->width, fiber_buffer->height, + DRW_TEX_RG_32, 0, fiber_buffer->data); + } + GPUTexture **fiber_texture = (GPUTexture **)(&hsys->draw_texture_cache); + + Material *ma = give_current_material(ob, hsys->material_index); + if (ma == NULL) { + ma = &defmaterial; + } + + DRW_shgroup_call_add(stl->g_data->hair_fibers_depth_shgrp, hair_geom, mat); + DRW_hair_shader_uniforms(stl->g_data->hair_fibers_depth_shgrp, scene, + fiber_texture, fiber_buffer); + + DRW_shgroup_call_add(stl->g_data->hair_fibers_depth_shgrp_clip, hair_geom, mat); + DRW_hair_shader_uniforms(stl->g_data->hair_fibers_depth_shgrp_clip, scene, + fiber_texture, fiber_buffer); + + DRWShadingGroup *shgrp = BLI_ghash_lookup(material_hash, (const void *)ma); + if (!shgrp) { + float *color_p = &ma->r; + float *metal_p = &ma->ray_mirror; + float *spec_p = &ma->spec; + float *rough_p = &ma->gloss_mir; + + if (ma->use_nodes && ma->nodetree) { + struct GPUMaterial *gpumat = EEVEE_material_hair_get(scene, ma, sldata->lamps->shadow_method, true); + + shgrp = DRW_shgroup_material_create(gpumat, psl->material_pass); + if (shgrp) { + add_standard_uniforms(shgrp, sldata, vedata, NULL, NULL, false, false); + BLI_ghash_insert(material_hash, ma, shgrp); + } + else { + /* Shader failed : pink color */ + static float col[3] = {1.0f, 0.0f, 1.0f}; + static float half = 0.5f; + + color_p = col; + metal_p = spec_p = rough_p = ½ + } + } + + /* Fallback to default shader */ + if (shgrp == NULL) { + bool use_ssr = ((stl->effects->enabled_effects & EFFECT_SSR) != 0); + shgrp = EEVEE_default_shading_group_get(sldata, vedata, true, true, false, use_ssr, + sldata->lamps->shadow_method); + DRW_shgroup_uniform_vec3(shgrp, "basecol", color_p, 1); + DRW_shgroup_uniform_float(shgrp, "metallic", metal_p, 1); + DRW_shgroup_uniform_float(shgrp, "specular", spec_p, 1); + DRW_shgroup_uniform_float(shgrp, "roughness", rough_p, 1); + + BLI_ghash_insert(material_hash, ma, shgrp); + } + } + + if (shgrp) { + DRW_shgroup_call_add(shgrp, hair_geom, mat); + + DRW_hair_shader_uniforms(shgrp, scene, + fiber_texture, fiber_buffer); + } +} + void EEVEE_materials_cache_populate(EEVEE_Data *vedata, EEVEE_ViewLayerData *sldata, Object *ob) { EEVEE_PassList *psl = vedata->psl; @@ -1437,84 +1659,22 @@ void EEVEE_materials_cache_populate(EEVEE_Data *vedata, EEVEE_ViewLayerData *sld if (ob->type == OB_MESH) { if (ob != draw_ctx->object_edit) { - material_hash = stl->g_data->hair_material_hash; - for (ModifierData *md = ob->modifiers.first; md; md = md->next) { if (md->type == eModifierType_ParticleSystem) { ParticleSystem *psys = ((ParticleSystemModifierData *)md)->psys; - - if (psys_check_enabled(ob, psys, false)) { - ParticleSettings *part = psys->part; - int draw_as = (part->draw_as == PART_DRAW_REND) ? part->ren_as : part->draw_as; - - if (draw_as == PART_DRAW_PATH && (psys->pathcache || psys->childcache)) { - struct Gwn_Batch *hair_geom = DRW_cache_particles_get_hair(psys, md); - DRWShadingGroup *shgrp = NULL; - Material *ma = give_current_material(ob, part->omat); - static float mat[4][4]; - - unit_m4(mat); - - if (ma == NULL) { - ma = &defmaterial; - } - - float *color_p = &ma->r; - float *metal_p = &ma->ray_mirror; - float *spec_p = &ma->spec; - float *rough_p = &ma->gloss_mir; - - DRW_shgroup_call_add(stl->g_data->depth_shgrp, hair_geom, mat); - DRW_shgroup_call_add(stl->g_data->depth_shgrp_clip, hair_geom, mat); - - shgrp = BLI_ghash_lookup(material_hash, (const void *)ma); - - if (shgrp) { - DRW_shgroup_call_add(shgrp, hair_geom, mat); - } - else { - if (ma->use_nodes && ma->nodetree) { - struct GPUMaterial *gpumat = EEVEE_material_hair_get(scene, ma, sldata->lamps->shadow_method); - - shgrp = DRW_shgroup_material_create(gpumat, psl->material_pass); - if (shgrp) { - add_standard_uniforms(shgrp, sldata, vedata, NULL, NULL, false, false); - - BLI_ghash_insert(material_hash, ma, shgrp); - - DRW_shgroup_call_add(shgrp, hair_geom, mat); - } - else { - /* Shader failed : pink color */ - static float col[3] = {1.0f, 0.0f, 1.0f}; - static float half = 0.5f; - - color_p = col; - metal_p = spec_p = rough_p = ½ - } - } - - /* Fallback to default shader */ - if (shgrp == NULL) { - bool use_ssr = ((stl->effects->enabled_effects & EFFECT_SSR) != 0); - shgrp = EEVEE_default_shading_group_get(sldata, vedata, true, false, use_ssr, - sldata->lamps->shadow_method); - DRW_shgroup_uniform_vec3(shgrp, "basecol", color_p, 1); - DRW_shgroup_uniform_float(shgrp, "metallic", metal_p, 1); - DRW_shgroup_uniform_float(shgrp, "specular", spec_p, 1); - DRW_shgroup_uniform_float(shgrp, "roughness", rough_p, 1); - - BLI_ghash_insert(material_hash, ma, shgrp); - - DRW_shgroup_call_add(shgrp, hair_geom, mat); - } - } - } - } + material_particle_hair(vedata, sldata, ob, psys, md); + } + else if (md->type == eModifierType_Fur) { + FurModifierData *fmd = (FurModifierData *)md; + material_hair(vedata, sldata, ob, fmd->hair_system, ob->derivedFinal); } } } } + else if (ob->type == OB_GROOM) { + Groom *groom = ob->data; + material_hair(vedata, sldata, ob, groom->hair_system, BKE_groom_get_scalp(groom)); + } } void EEVEE_materials_cache_finish(EEVEE_Data *vedata) @@ -1535,6 +1695,8 @@ void EEVEE_materials_free(void) MEM_SAFE_FREE(e_data.volume_shader_lib); DRW_SHADER_FREE_SAFE(e_data.default_prepass_sh); DRW_SHADER_FREE_SAFE(e_data.default_prepass_clip_sh); + DRW_SHADER_FREE_SAFE(e_data.default_prepass_hair_fiber_sh); + DRW_SHADER_FREE_SAFE(e_data.default_prepass_hair_fiber_clip_sh); DRW_SHADER_FREE_SAFE(e_data.default_background); DRW_SHADER_FREE_SAFE(e_data.update_noise_sh); DRW_TEXTURE_FREE_SAFE(e_data.util_tex); diff --git a/source/blender/draw/engines/eevee/eevee_private.h b/source/blender/draw/engines/eevee/eevee_private.h index bf77914832e..d203fadc073 100644 --- a/source/blender/draw/engines/eevee/eevee_private.h +++ b/source/blender/draw/engines/eevee/eevee_private.h @@ -101,28 +101,29 @@ enum { /* Material shader variations */ enum { - VAR_MAT_MESH = (1 << 0), - VAR_MAT_PROBE = (1 << 1), - VAR_MAT_HAIR = (1 << 2), - VAR_MAT_FLAT = (1 << 3), - VAR_MAT_BLEND = (1 << 4), - VAR_MAT_VSM = (1 << 5), - VAR_MAT_ESM = (1 << 6), - VAR_MAT_VOLUME = (1 << 7), + VAR_MAT_MESH = (1 << 0), + VAR_MAT_PROBE = (1 << 1), + VAR_MAT_HAIR = (1 << 2), + VAR_MAT_FLAT = (1 << 3), + VAR_MAT_BLEND = (1 << 4), + VAR_MAT_VSM = (1 << 5), + VAR_MAT_ESM = (1 << 6), + VAR_MAT_VOLUME = (1 << 7), + VAR_MAT_HAIR_FIBERS = (1 << 8), /* Max number of variation */ /* IMPORTANT : Leave it last and set * it's value accordingly. */ - VAR_MAT_MAX = (1 << 8), + VAR_MAT_MAX = (1 << 9), /* These are options that are not counted in VAR_MAT_MAX * because they are not cumulative with the others above. */ - VAR_MAT_CLIP = (1 << 9), - VAR_MAT_HASH = (1 << 10), - VAR_MAT_MULT = (1 << 11), - VAR_MAT_SHADOW = (1 << 12), - VAR_MAT_REFRACT = (1 << 13), - VAR_MAT_SSS = (1 << 14), - VAR_MAT_TRANSLUC = (1 << 15), - VAR_MAT_SSSALBED = (1 << 16), + VAR_MAT_CLIP = (1 << 10), + VAR_MAT_HASH = (1 << 11), + VAR_MAT_MULT = (1 << 12), + VAR_MAT_SHADOW = (1 << 13), + VAR_MAT_REFRACT = (1 << 14), + VAR_MAT_SSS = (1 << 15), + VAR_MAT_TRANSLUC = (1 << 16), + VAR_MAT_SSSALBED = (1 << 17), }; /* Shadow Technique */ @@ -746,6 +747,10 @@ typedef struct EEVEE_PrivateData { struct DRWShadingGroup *depth_shgrp_cull; struct DRWShadingGroup *depth_shgrp_clip; struct DRWShadingGroup *depth_shgrp_clip_cull; + struct DRWShadingGroup *hair_fibers_depth_shgrp; + struct DRWShadingGroup *hair_fibers_depth_shgrp_cull; + struct DRWShadingGroup *hair_fibers_depth_shgrp_clip; + struct DRWShadingGroup *hair_fibers_depth_shgrp_clip_cull; struct DRWShadingGroup *refract_depth_shgrp; struct DRWShadingGroup *refract_depth_shgrp_cull; struct DRWShadingGroup *refract_depth_shgrp_clip; @@ -792,7 +797,7 @@ struct GPUMaterial *EEVEE_material_mesh_get( bool use_blend, bool use_multiply, bool use_refract, bool use_sss, bool use_translucency, int shadow_method); struct GPUMaterial *EEVEE_material_mesh_volume_get(struct Scene *scene, Material *ma); struct GPUMaterial *EEVEE_material_mesh_depth_get(struct Scene *scene, Material *ma, bool use_hashed_alpha, bool is_shadow); -struct GPUMaterial *EEVEE_material_hair_get(struct Scene *scene, Material *ma, int shadow_method); +struct GPUMaterial *EEVEE_material_hair_get(struct Scene *scene, Material *ma, int shadow_method, bool use_fibers); void EEVEE_materials_free(void); void EEVEE_draw_default_passes(EEVEE_PassList *psl); void EEVEE_update_noise(EEVEE_PassList *psl, EEVEE_FramebufferList *fbl, const double offsets[3]); diff --git a/source/blender/draw/engines/eevee/shaders/hair_lib.glsl b/source/blender/draw/engines/eevee/shaders/hair_lib.glsl new file mode 100644 index 00000000000..489ee44dd0f --- /dev/null +++ b/source/blender/draw/engines/eevee/shaders/hair_lib.glsl @@ -0,0 +1,330 @@ +#ifdef HAIR_SHADER_FIBERS + +#define M_PI 3.1415926535897932384626433832795 + +mat4 translate(vec3 co) +{ + return mat4(1.0, 0.0, 0.0, 0.0, + 0.0, 1.0, 0.0, 0.0, + 0.0, 0.0, 1.0, 0.0, + co.x, co.y, co.z, 1.0); +} + +mat4 rotateX(float angle) +{ + float ca = cos(angle); + float sa = sin(angle); + return mat4(1.0, 0.0, 0.0, 0.0, + 0.0, ca, sa, 0.0, + 0.0, -sa, ca, 0.0, + 0.0, 0.0, 0.0, 1.0); +} + +mat4 rotateY(float angle) +{ + float ca = cos(angle); + float sa = sin(angle); + return mat4(ca, 0.0, sa, 0.0, + 0.0, 1.0, 0.0, 0.0, + -sa, 0.0, ca, 0.0, + 0.0, 0.0, 0.0, 1.0); +} + +mat4 rotateZ(float angle) +{ + float ca = cos(angle); + float sa = sin(angle); + return mat4(ca, sa, 0.0, 0.0, + -sa, ca, 0.0, 0.0, + 0.0, 0.0, 1.0, 0.0, + 0.0, 0.0, 0.0, 1.0); +} + +/* Hair Displacement */ + +/* Note: The deformer functions below calculate a new location vector + * as well as a new direction (aka "normal"), using the partial derivatives of the transformation. + * + * Each transformation function can depend on the location L as well as the curve parameter t: + * + * Lnew = f(L, t) + * => dLnew/dt = del f/del L * dL/dt + del f/del t + * + * The first term is the Jacobian of the function f, dL/dt is the original direction vector. + * Some more information can be found here: + * https://developer.nvidia.com/gpugems/GPUGems/gpugems_ch42.html + */ + +/* Hairs tend to stick together and run in parallel. + * The effect increases with distance from the root, + * as the stresses pulling fibers apart decrease. + */ +struct ClumpParams +{ + /* Relative strand thickness at the tip. + * (0.0, 1.0] + * 0.0 : Strand clumps into a single line + * 1.0 : Strand does not clump at all + * (> 1.0 is possible but not recommended) + */ + float thickness; +}; + +/* Hairs often don't have a circular cross section, but are somewhat flattened. + * This creates the local bending which results in the typical curly hair geometry. + */ +struct CurlParams +{ + /* Radius of the curls. + * >= 0.0 + */ + float radius; + /* Steepness of curls + * < 0.0 : Clockwise curls + * > 0.0 : Anti-clockwise curls + */ + float angle; +}; + +struct DeformParams +{ + /* Strand tapering with distance from the root. + * < 1.0 : Taper is concave (recommended) + * = 1.0 : Taper is linear + * > 1.0 : Taper is convex (not recommended) + */ + float taper; + + ClumpParams clump; + CurlParams curl; +}; + +void deform_taper(DeformParams params, float t, out float taper, out float dtaper) +{ + taper = pow(t, params.taper); + dtaper = (t > 0.0) ? taper * params.taper / t : 0.0; +} + +void deform_clump(DeformParams params, + float t, float tscale, mat4 target_matrix, + inout vec3 co, inout vec3 tang) +{ + float taper, dtaper; + deform_taper(params, t, taper, dtaper); + float factor = (1.0 - params.clump.thickness) * taper; + float dfactor = (1.0 - params.clump.thickness) * dtaper; + + vec3 target_co = target_matrix[3].xyz; + vec3 target_tang = target_matrix[0].xyz; + vec3 nco = co + (target_co - co) * factor; + vec3 ntang = normalize(tang + (target_tang - tang) * factor + (target_co - co) * dfactor); + + co = nco; + tang = ntang; +} + +void deform_curl(DeformParams params, + float t, float tscale, + inout mat4 target_matrix) +{ + float pitch = 2.0*M_PI * params.curl.radius * tan(params.curl.angle); + float turns = tscale / (params.curl.radius * tan(params.curl.angle)); + float angle = t * turns; + mat4 local_mat = rotateX(angle) * translate(vec3(0.0, params.curl.radius, 0.0)) * rotateY(params.curl.angle); + target_matrix = target_matrix * local_mat; +} + +void deform_fiber(DeformParams params, + float t, float tscale, mat4 target_matrix, + inout vec3 loc, inout vec3 tang) +{ + deform_curl(params, t, tscale, target_matrix); + deform_clump(params, t, tscale, target_matrix, loc, tang); +} + +/*===================================*/ +/* Hair Interpolation */ + +#define FIBER_RIBBON + +uniform sampler2D fiber_data; + +uniform int fiber_start; +uniform int strand_map_start; +uniform int strand_vertex_start; + +uniform float ribbon_width; +uniform vec2 viewport_size; + +#define INDEX_INVALID -1 + +vec2 read_texdata(int offset) +{ + ivec2 offset2 = ivec2(offset % HAIR_SHADER_TEX_WIDTH, offset / HAIR_SHADER_TEX_WIDTH); + return texelFetch(fiber_data, offset2, 0).rg; +} + +mat4 mat4_from_vectors(vec3 nor, vec3 tang, vec3 co) +{ + tang = normalize(tang); + vec3 xnor = normalize(cross(nor, tang)); + return mat4(vec4(tang, 0.0), vec4(xnor, 0.0), vec4(cross(tang, xnor), 0.0), vec4(co, 1.0)); +} + +void get_strand_data(int index, out int start, out int count) +{ + int offset = strand_map_start + index; + vec2 a = read_texdata(offset); + + start = floatBitsToInt(a.r); + count = floatBitsToInt(a.g); +} + +void get_strand_vertex(int index, out vec3 co, out vec3 nor, out vec3 tang) +{ + int offset = strand_vertex_start + index * 5; + vec2 a = read_texdata(offset); + vec2 b = read_texdata(offset + 1); + vec2 c = read_texdata(offset + 2); + vec2 d = read_texdata(offset + 3); + vec2 e = read_texdata(offset + 4); + + co = vec3(a.rg, b.r); + nor = vec3(b.g, c.rg); + tang = vec3(d.rg, e.r); +} + +void get_strand_root(int index, out vec3 co) +{ + int offset = strand_vertex_start + index * 5; + vec2 a = read_texdata(offset); + vec2 b = read_texdata(offset + 1); + + co = vec3(a.rg, b.r); +} + +void get_fiber_data(int fiber_index, out ivec4 parent_index, out vec4 parent_weight, out vec3 pos) +{ + int offset = fiber_start + fiber_index * 6; + vec2 a = read_texdata(offset); + vec2 b = read_texdata(offset + 1); + vec2 c = read_texdata(offset + 2); + vec2 d = read_texdata(offset + 3); + vec2 e = read_texdata(offset + 4); + vec2 f = read_texdata(offset + 5); + + parent_index = ivec4(floatBitsToInt(a.rg), floatBitsToInt(b.rg)); + parent_weight = vec4(c.rg, d.rg); + pos = vec3(e.rg, f.r); +} + +void interpolate_parent_curve_full(int index, float curve_param, out vec3 co, out vec3 nor, out vec3 tang, out vec3 rootco) +{ + int start, count; + get_strand_data(index, start, count); + + get_strand_root(start, rootco); + +#if 0 // Don't have to worry about out-of-bounds segment here, as long as lerpfac becomes 0.0 when curve_param==1.0 + float maxlen = float(count - 1); + float arclength = curve_param * maxlen; + int segment = min(int(arclength), count - 2); + float lerpfac = arclength - min(floor(arclength), maxlen - 1.0); +#else + float maxlen = float(count - 1); + float arclength = curve_param * maxlen; + int segment = int(arclength); + float lerpfac = arclength - floor(arclength); +#endif + + vec3 co0, nor0, tang0; + vec3 co1, nor1, tang1; + get_strand_vertex(start + segment, co0, nor0, tang0); + get_strand_vertex(start + segment + 1, co1, nor1, tang1); + + co = mix(co0, co1, lerpfac) - rootco; + nor = mix(nor0, nor1, lerpfac); + tang = mix(tang0, tang1, lerpfac); +} + +void interpolate_parent_curve(int index, float curve_param, out vec3 co, out vec3 tang) +{ + vec3 nor; + vec3 rootco; + interpolate_parent_curve_full(index, curve_param, co, nor, tang, rootco); +} + +void interpolate_vertex(int fiber_index, float curve_param, + out vec3 co, out vec3 tang, + out mat4 target_matrix) +{ + co = vec3(0.0); + tang = vec3(0.0); + target_matrix = mat4(1.0); + + ivec4 parent_index; + vec4 parent_weight; + vec3 rootco; + get_fiber_data(fiber_index, parent_index, parent_weight, rootco); + + if (parent_index.x != INDEX_INVALID) { + vec3 pco, pnor, ptang, prootco; + interpolate_parent_curve_full(parent_index.x, curve_param, pco, pnor, ptang, prootco); + co += parent_weight.x * pco; + tang += parent_weight.x * normalize(ptang); + + target_matrix = mat4_from_vectors(pnor, ptang, pco + prootco); + } + if (parent_index.y != INDEX_INVALID) { + vec3 pco, ptang; + interpolate_parent_curve(parent_index.y, curve_param, pco, ptang); + co += parent_weight.y * pco; + tang += parent_weight.y * normalize(ptang); + } + if (parent_index.z != INDEX_INVALID) { + vec3 pco, ptang; + interpolate_parent_curve(parent_index.z, curve_param, pco, ptang); + co += parent_weight.z * pco; + tang += parent_weight.z * normalize(ptang); + } + if (parent_index.w != INDEX_INVALID) { + vec3 pco, ptang; + interpolate_parent_curve(parent_index.w, curve_param, pco, ptang); + co += parent_weight.w * pco; + tang += parent_weight.w * normalize(ptang); + } + + co += rootco; + tang = normalize(tang); +} + +void hair_fiber_get_vertex(int fiber_index, float curve_param, mat4 ModelViewMatrix, out vec3 pos, out vec3 nor, out vec2 view_offset) +{ + vec3 target_loc; + mat4 target_matrix; + interpolate_vertex(fiber_index, curve_param, pos, nor, target_matrix); + + DeformParams deform_params; + deform_params.taper = 2.0; + deform_params.clump.thickness = 0.15; + deform_params.curl.radius = 0.1; + deform_params.curl.angle = 0.2; + // TODO define proper curve scale, independent of subdivision! + //deform_fiber(deform_params, curve_param, 1.0, target_matrix, pos, nor); + +#ifdef FIBER_RIBBON + float ribbon_side = (float(gl_VertexID % 2) - 0.5) * ribbon_width; + { + vec4 view_nor = ModelViewMatrix * vec4(nor, 0.0); + view_offset = vec2(view_nor.y, -view_nor.x); + float L = length(view_offset); + if (L > 0.0) { + view_offset *= ribbon_side / (L * viewport_size); + } + } +#else + view_offset = vec2(0.0); +#endif +} + +#endif /*HAIR_SHADER_FIBERS*/ diff --git a/source/blender/draw/engines/eevee/shaders/lit_surface_vert.glsl b/source/blender/draw/engines/eevee/shaders/lit_surface_vert.glsl index d25c49098a6..d53852193d5 100644 --- a/source/blender/draw/engines/eevee/shaders/lit_surface_vert.glsl +++ b/source/blender/draw/engines/eevee/shaders/lit_surface_vert.glsl @@ -7,8 +7,13 @@ uniform mat3 WorldNormalMatrix; uniform mat3 NormalMatrix; #endif +#ifndef HAIR_SHADER_FIBERS in vec3 pos; in vec3 nor; +#else +in int fiber_index; +in float curve_param; +#endif out vec3 worldPosition; out vec3 viewPosition; @@ -28,7 +33,17 @@ out vec3 viewNormal; #endif void main() { +#ifndef HAIR_SHADER_FIBERS + gl_Position = ModelViewProjectionMatrix * vec4(pos, 1.0); +#else + vec3 pos; + vec3 nor; + vec2 view_offset; + hair_fiber_get_vertex(fiber_index, curve_param, ModelViewMatrix, pos, nor, view_offset); gl_Position = ModelViewProjectionMatrix * vec4(pos, 1.0); + gl_Position.xy += view_offset * gl_Position.w; +#endif + viewPosition = (ModelViewMatrix * vec4(pos, 1.0)).xyz; worldPosition = (ModelMatrix * vec4(pos, 1.0)).xyz; viewNormal = normalize(NormalMatrix * nor); @@ -40,4 +55,4 @@ void main() { #ifdef ATTRIB pass_attrib(pos); #endif -}
\ No newline at end of file +} diff --git a/source/blender/draw/engines/eevee/shaders/prepass_vert.glsl b/source/blender/draw/engines/eevee/shaders/prepass_vert.glsl index c756e061d70..974c5724842 100644 --- a/source/blender/draw/engines/eevee/shaders/prepass_vert.glsl +++ b/source/blender/draw/engines/eevee/shaders/prepass_vert.glsl @@ -1,17 +1,33 @@ uniform mat4 ModelViewProjectionMatrix; uniform mat4 ModelMatrix; +uniform mat4 ModelViewMatrix; /* keep in sync with DRWManager.view_data */ layout(std140) uniform clip_block { vec4 ClipPlanes[1]; }; +#ifndef HAIR_SHADER_FIBERS in vec3 pos; +#else +in int fiber_index; +in float curve_param; +#endif void main() { +#ifndef HAIR_SHADER_FIBERS + gl_Position = ModelViewProjectionMatrix * vec4(pos, 1.0); +#else + vec3 pos; + vec3 nor; + vec2 view_offset; + hair_fiber_get_vertex(fiber_index, curve_param, ModelViewMatrix, pos, nor, view_offset); gl_Position = ModelViewProjectionMatrix * vec4(pos, 1.0); + gl_Position.xy += view_offset * gl_Position.w; +#endif + #ifdef CLIP_PLANES vec4 worldPosition = (ModelMatrix * vec4(pos, 1.0)); gl_ClipDistance[0] = dot(vec4(worldPosition.xyz, 1.0), ClipPlanes[0]); diff --git a/source/blender/draw/intern/draw_cache.c b/source/blender/draw/intern/draw_cache.c index 301a39d053f..06fb71b7e67 100644 --- a/source/blender/draw/intern/draw_cache.c +++ b/source/blender/draw/intern/draw_cache.c @@ -2565,6 +2565,37 @@ Gwn_Batch *DRW_cache_lattice_vert_overlay_get(Object *ob) /* -------------------------------------------------------------------- */ +/** \name Groom + * \{ */ + +Gwn_Batch *DRW_cache_groom_verts_get(Object *ob) +{ + BLI_assert(ob->type == OB_GROOM); + + struct Groom *groom = ob->data; + return DRW_groom_batch_cache_get_all_verts(groom); +} + +Gwn_Batch *DRW_cache_groom_wire_get(Object *ob) +{ + BLI_assert(ob->type == OB_GROOM); + + struct Groom *groom = ob->data; + return DRW_groom_batch_cache_get_all_edges(groom); +} + +Gwn_Batch *DRW_cache_groom_vert_overlay_get(Object *ob, int mode) +{ + BLI_assert(ob->type == OB_GROOM); + + struct Groom *groom = ob->data; + return DRW_groom_batch_cache_get_overlay_verts(groom, mode); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ + /** \name Particles * \{ */ @@ -2713,6 +2744,36 @@ Gwn_Batch *DRW_cache_particles_get_prim(int type) return NULL; } +/* -------------------------------------------------------------------- */ + +/** \name Hair */ + +Gwn_Batch *DRW_cache_hair_get_fibers(struct HairSystem *hsys, struct DerivedMesh *scalp, int subdiv, + const struct DRWHairFiberTextureBuffer **r_buffer) +{ + return DRW_hair_batch_cache_get_fibers(hsys, scalp, subdiv, r_buffer); +} + +Gwn_Batch *DRW_cache_hair_get_follicle_points(struct HairSystem *hsys, struct DerivedMesh *scalp) +{ + return DRW_hair_batch_cache_get_follicle_points(hsys, scalp); +} + +Gwn_Batch *DRW_cache_hair_get_follicle_axes(struct HairSystem *hsys, struct DerivedMesh *scalp) +{ + return DRW_hair_batch_cache_get_follicle_axes(hsys, scalp); +} + +Gwn_Batch *DRW_cache_hair_get_guide_curve_points(struct HairSystem *hsys, struct DerivedMesh *scalp, int subdiv) +{ + return DRW_hair_batch_cache_get_guide_curve_points(hsys, scalp, subdiv); +} + +Gwn_Batch *DRW_cache_hair_get_guide_curve_edges(struct HairSystem *hsys, struct DerivedMesh *scalp, int subdiv) +{ + return DRW_hair_batch_cache_get_guide_curve_edges(hsys, scalp, subdiv); +} + /* 3D cursor */ Gwn_Batch *DRW_cache_cursor_get(void) { diff --git a/source/blender/draw/intern/draw_cache.h b/source/blender/draw/intern/draw_cache.h index 2ef57884a44..bae59ff3604 100644 --- a/source/blender/draw/intern/draw_cache.h +++ b/source/blender/draw/intern/draw_cache.h @@ -30,6 +30,10 @@ struct Gwn_Batch; struct GPUMaterial; struct Object; struct ModifierData; +struct Groom; +struct HairSystem; +struct DRWHairFiberTextureBuffer; +struct DerivedMesh; void DRW_shape_cache_free(void); @@ -163,11 +167,24 @@ struct Gwn_Batch *DRW_cache_lattice_verts_get(struct Object *ob); struct Gwn_Batch *DRW_cache_lattice_wire_get(struct Object *ob, bool use_weight); struct Gwn_Batch *DRW_cache_lattice_vert_overlay_get(struct Object *ob); +/* Groom */ +struct Gwn_Batch *DRW_cache_groom_verts_get(struct Object *ob); +struct Gwn_Batch *DRW_cache_groom_wire_get(struct Object *ob); +struct Gwn_Batch *DRW_cache_groom_vert_overlay_get(struct Object *ob, int mode); + /* Particles */ struct Gwn_Batch *DRW_cache_particles_get_hair(struct ParticleSystem *psys, struct ModifierData *md); struct Gwn_Batch *DRW_cache_particles_get_dots(struct Object *object, struct ParticleSystem *psys); struct Gwn_Batch *DRW_cache_particles_get_prim(int type); +/* Hair */ +struct Gwn_Batch *DRW_cache_hair_get_fibers(struct HairSystem *hsys, struct DerivedMesh *scalp, int subdiv, + const struct DRWHairFiberTextureBuffer **r_buffer); +struct Gwn_Batch *DRW_cache_hair_get_follicle_points(struct HairSystem *hsys, struct DerivedMesh *scalp); +struct Gwn_Batch *DRW_cache_hair_get_follicle_axes(struct HairSystem *hsys, struct DerivedMesh *scalp); +struct Gwn_Batch *DRW_cache_hair_get_guide_curve_points(struct HairSystem *hsys, struct DerivedMesh *scalp, int subdiv); +struct Gwn_Batch *DRW_cache_hair_get_guide_curve_edges(struct HairSystem *hsys, struct DerivedMesh *scalp, int subdiv); + /* Metaball */ struct Gwn_Batch *DRW_cache_mball_surface_get(struct Object *ob); diff --git a/source/blender/draw/intern/draw_cache_impl.h b/source/blender/draw/intern/draw_cache_impl.h index 83cc87307b5..0ac2f11849a 100644 --- a/source/blender/draw/intern/draw_cache_impl.h +++ b/source/blender/draw/intern/draw_cache_impl.h @@ -34,6 +34,10 @@ struct Gwn_VertBuf; struct ListBase; struct ModifierData; struct ParticleSystem; +struct Groom; +struct HairSystem; +struct DRWHairFiberTextureBuffer; +struct DerivedMesh; struct Curve; struct Lattice; @@ -56,6 +60,12 @@ void DRW_lattice_batch_cache_free(struct Lattice *lt); void DRW_particle_batch_cache_dirty(struct ParticleSystem *psys, int mode); void DRW_particle_batch_cache_free(struct ParticleSystem *psys); +void DRW_groom_batch_cache_dirty(struct Groom *groom, int mode); +void DRW_groom_batch_cache_free(struct Groom *groom); + +void DRW_hair_batch_cache_dirty(struct HairSystem *hsys, int mode); +void DRW_hair_batch_cache_free(struct HairSystem *hsys); + /* Curve */ struct Gwn_Batch *DRW_curve_batch_cache_get_wire_edge(struct Curve *cu, struct CurveCache *ob_curve_cache); struct Gwn_Batch *DRW_curve_batch_cache_get_normal_edge( @@ -88,6 +98,11 @@ struct Gwn_Batch *DRW_lattice_batch_cache_get_all_edges(struct Lattice *lt, bool struct Gwn_Batch *DRW_lattice_batch_cache_get_all_verts(struct Lattice *lt); struct Gwn_Batch *DRW_lattice_batch_cache_get_overlay_verts(struct Lattice *lt); +/* Groom */ +struct Gwn_Batch *DRW_groom_batch_cache_get_all_edges(struct Groom *groom); +struct Gwn_Batch *DRW_groom_batch_cache_get_all_verts(struct Groom *groom); +struct Gwn_Batch *DRW_groom_batch_cache_get_overlay_verts(struct Groom *groom, int mode); + /* Mesh */ struct Gwn_Batch **DRW_mesh_batch_cache_get_surface_shaded( @@ -124,4 +139,12 @@ void DRW_mesh_cache_sculpt_coords_ensure(struct Mesh *me); struct Gwn_Batch *DRW_particles_batch_cache_get_hair(struct ParticleSystem *psys, struct ModifierData *md); struct Gwn_Batch *DRW_particles_batch_cache_get_dots(struct Object *object, struct ParticleSystem *psys); +/* Hair */ +struct Gwn_Batch *DRW_hair_batch_cache_get_fibers(struct HairSystem *hsys, struct DerivedMesh *scalp, int subdiv, + const struct DRWHairFiberTextureBuffer **r_buffer); +struct Gwn_Batch *DRW_hair_batch_cache_get_follicle_points(struct HairSystem *hsys, struct DerivedMesh *scalp); +struct Gwn_Batch *DRW_hair_batch_cache_get_follicle_axes(struct HairSystem *hsys, struct DerivedMesh *scalp); +struct Gwn_Batch *DRW_hair_batch_cache_get_guide_curve_points(struct HairSystem *hsys, struct DerivedMesh *scalp, int subdiv); +struct Gwn_Batch *DRW_hair_batch_cache_get_guide_curve_edges(struct HairSystem *hsys, struct DerivedMesh *scalp, int subdiv); + #endif /* __DRAW_CACHE_IMPL_H__ */ diff --git a/source/blender/draw/intern/draw_cache_impl_groom.c b/source/blender/draw/intern/draw_cache_impl_groom.c new file mode 100644 index 00000000000..581dc707372 --- /dev/null +++ b/source/blender/draw/intern/draw_cache_impl_groom.c @@ -0,0 +1,610 @@ +/* + * ***** 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 draw_cache_impl_groom.c + * \ingroup draw + * + * \brief Groom API for render engines + */ + +#include <string.h> + +#include "MEM_guardedalloc.h" + +#include "BLI_utildefines.h" +#include "BLI_listbase.h" +#include "BLI_math.h" + +#include "DNA_groom_types.h" +#include "DNA_scene_types.h" +#include "DNA_userdef_types.h" + +#include "BKE_groom.h" + +#include "GPU_batch.h" + +#include "draw_cache_impl.h" /* own include */ + +#define SELECT 1 + +static void groom_batch_cache_clear(Groom *groom); + +/* ---------------------------------------------------------------------- */ +/* Groom Gwn_Batch Cache */ + +typedef struct GroomBatchCache { + Gwn_VertBuf *pos; + Gwn_IndexBuf *edges; + + Gwn_Batch *all_verts; + Gwn_Batch *all_edges; + + Gwn_Batch *overlay_verts; + + /* settings to determine if cache is invalid */ + bool is_dirty; + + bool is_editmode; +} GroomBatchCache; + +/* Gwn_Batch cache management. */ + +static bool groom_batch_cache_valid(Groom *groom) +{ + GroomBatchCache *cache = groom->batch_cache; + + if (cache == NULL) { + return false; + } + + if (cache->is_editmode != (groom->editgroom != NULL)) { + return false; + } + + if (cache->is_dirty == false) { + return true; + } + else { + if (cache->is_editmode) { + return false; + } + } + + return true; +} + +static void groom_batch_cache_init(Groom *groom) +{ + GroomBatchCache *cache = groom->batch_cache; + + if (!cache) { + cache = groom->batch_cache = MEM_callocN(sizeof(*cache), __func__); + } + else { + memset(cache, 0, sizeof(*cache)); + } + + cache->is_editmode = (groom->editgroom != NULL); + + cache->is_dirty = false; +} + +static GroomBatchCache *groom_batch_cache_get(Groom *groom) +{ + if (!groom_batch_cache_valid(groom)) { + groom_batch_cache_clear(groom); + groom_batch_cache_init(groom); + } + return groom->batch_cache; +} + +void DRW_groom_batch_cache_dirty(Groom *groom, int mode) +{ + GroomBatchCache *cache = groom->batch_cache; + if (cache == NULL) { + return; + } + switch (mode) { + case BKE_GROOM_BATCH_DIRTY_ALL: + cache->is_dirty = true; + break; + case BKE_GROOM_BATCH_DIRTY_SELECT: + /* TODO Separate Flag vbo */ + GWN_BATCH_DISCARD_SAFE(cache->overlay_verts); + break; + default: + BLI_assert(0); + } +} + +static void groom_batch_cache_clear(Groom *groom) +{ + GroomBatchCache *cache = groom->batch_cache; + if (!cache) { + return; + } + + GWN_BATCH_DISCARD_SAFE(cache->all_verts); + GWN_BATCH_DISCARD_SAFE(cache->all_edges); + GWN_BATCH_DISCARD_SAFE(cache->overlay_verts); + + GWN_VERTBUF_DISCARD_SAFE(cache->pos); + GWN_INDEXBUF_DISCARD_SAFE(cache->edges); +} + +void DRW_groom_batch_cache_free(Groom *groom) +{ + groom_batch_cache_clear(groom); + MEM_SAFE_FREE(groom->batch_cache); +} + +enum { + VFLAG_VERTEX_SELECTED = 1 << 0, + VFLAG_VERTEX_ACTIVE = 1 << 1, +}; + +BLI_INLINE char make_vertex_flag(bool active, bool selected) +{ + char vflag = 0; + if (active) + { + vflag |= VFLAG_VERTEX_ACTIVE; + } + if (selected) + { + vflag |= VFLAG_VERTEX_SELECTED; + } + return vflag; +} + +/* Parts of the groom object to render */ +typedef enum GroomRenderPart +{ + GM_RENDER_REGIONS = (1 << 0), /* Draw scalp regions */ + GM_RENDER_CURVES = (1 << 1), /* Draw center curves of bundles */ + GM_RENDER_SECTIONS = (1 << 2), /* Draw section curves */ + + GM_RENDER_ALL = GM_RENDER_REGIONS | GM_RENDER_CURVES | GM_RENDER_SECTIONS, +} GroomRenderPart; + +static int groom_count_verts(Groom *groom, int parts, bool use_curve_cache) +{ + const ListBase *bundles = groom->editgroom ? &groom->editgroom->bundles : &groom->bundles; + int vert_len = 0; + + if (parts & GM_RENDER_REGIONS) + { + // TODO + } + if (parts & GM_RENDER_CURVES) + { + for (GroomBundle *bundle = bundles->first; bundle; bundle = bundle->next) + { + if (use_curve_cache) + { + vert_len += bundle->curvesize; + } + else + { + vert_len += bundle->totsections; + } + } + } + if (parts & GM_RENDER_SECTIONS) + { + for (GroomBundle *bundle = bundles->first; bundle; bundle = bundle->next) + { + if (use_curve_cache) + { + vert_len += bundle->curvesize * bundle->numshapeverts; + } + else + { + vert_len += bundle->totverts; + } + } + } + + return vert_len; +} + +static int groom_count_edges(Groom *groom, int parts, bool use_curve_cache) +{ + const ListBase *bundles = groom->editgroom ? &groom->editgroom->bundles : &groom->bundles; + int edge_len = 0; + + if (parts & GM_RENDER_REGIONS) + { + // TODO + } + if (parts & GM_RENDER_CURVES) + { + for (GroomBundle *bundle = bundles->first; bundle; bundle = bundle->next) + { + if (use_curve_cache) + { + if (bundle->curvesize > 0) + { + edge_len += bundle->curvesize - 1; + } + } + else + { + if (bundle->totsections > 0) + { + edge_len += bundle->totsections - 1; + } + } + } + } + if (parts & GM_RENDER_SECTIONS) + { + for (GroomBundle *bundle = bundles->first; bundle; bundle = bundle->next) + { + if (bundle->numshapeverts > 1) + { + // Closed edge loop, 1 edge per vertex + if (use_curve_cache) + { + /* a curve for each shape vertex */ + int numedges_curves = (bundle->curvesize - 1) * bundle->numshapeverts; + /* a loop for each section */ + int numedges_sections = bundle->numshapeverts * bundle->totsections; + edge_len += numedges_curves + numedges_sections; + } + else + { + edge_len += bundle->totverts; + } + } + } + } + + return edge_len; +} + +#define GM_ATTR_ID_UNUSED 0xFFFFFFFF + +static void groom_get_verts( + Groom *groom, + int parts, + bool use_curve_cache, + Gwn_VertBuf *vbo, + uint id_pos, + uint id_flag) +{ + int vert_len = groom_count_verts(groom, parts, use_curve_cache); + const ListBase *bundles = groom->editgroom ? &groom->editgroom->bundles : &groom->bundles; + + GWN_vertbuf_data_alloc(vbo, vert_len); + + uint idx = 0; + if (parts & GM_RENDER_REGIONS) + { + // TODO + } + if (parts & GM_RENDER_CURVES) + { + for (GroomBundle *bundle = bundles->first; bundle; bundle = bundle->next) + { + if (use_curve_cache) + { + GroomCurveCache *cache = bundle->curvecache; + for (int i = 0; i < bundle->curvesize; ++i, ++cache) + { + if (id_pos != GM_ATTR_ID_UNUSED) + { + GWN_vertbuf_attr_set(vbo, id_pos, idx, cache->co); + } + if (id_flag != GM_ATTR_ID_UNUSED) + { + char vflag = make_vertex_flag(false, false); + GWN_vertbuf_attr_set(vbo, id_flag, idx, &vflag); + } + + ++idx; + } + } + else + { + const bool active = bundle->flag & GM_BUNDLE_SELECT; + GroomSection *section = bundle->sections; + for (int i = 0; i < bundle->totsections; ++i, ++section) + { + if (id_pos != GM_ATTR_ID_UNUSED) + { + GWN_vertbuf_attr_set(vbo, id_pos, idx, section->center); + } + if (id_flag != GM_ATTR_ID_UNUSED) + { + char vflag = make_vertex_flag(active, section->flag & GM_SECTION_SELECT); + GWN_vertbuf_attr_set(vbo, id_flag, idx, &vflag); + } + + ++idx; + } + } + } + } + if (parts & GM_RENDER_SECTIONS) + { + for (GroomBundle *bundle = bundles->first; bundle; bundle = bundle->next) + { + if (use_curve_cache) + { + GroomCurveCache *cache = bundle->curvecache; + for (int i = 0; i < bundle->numshapeverts; ++i) + { + for (int j = 0; j < bundle->curvesize; ++j, ++cache) + { + if (id_pos != GM_ATTR_ID_UNUSED) + { + GWN_vertbuf_attr_set(vbo, id_pos, idx, cache->co); + } + if (id_flag != GM_ATTR_ID_UNUSED) + { + char vflag = make_vertex_flag(false, false); + GWN_vertbuf_attr_set(vbo, id_flag, idx, &vflag); + } + + ++idx; + } + } + } + else + { + GroomSection *section = bundle->sections; + GroomSectionVertex *vertex = bundle->verts; + for (int i = 0; i < bundle->totsections; ++i, ++section) + { + const bool active = (bundle->flag & GM_BUNDLE_SELECT) && (section->flag & GM_SECTION_SELECT); + + for (int j = 0; j < bundle->numshapeverts; ++j, ++vertex) + { + if (id_pos != GM_ATTR_ID_UNUSED) + { + float co[3] = {vertex->co[0], vertex->co[1], 0.0f}; + mul_m3_v3(section->mat, co); + add_v3_v3(co, section->center); + GWN_vertbuf_attr_set(vbo, id_pos, idx, co); + } + if (id_flag != GM_ATTR_ID_UNUSED) + { + char vflag = make_vertex_flag(active, vertex->flag & GM_VERTEX_SELECT); + GWN_vertbuf_attr_set(vbo, id_flag, idx, &vflag); + } + + ++idx; + } + } + } + } + } +} + +static void groom_get_edges( + Groom *groom, + int parts, + bool use_curve_cache, + Gwn_IndexBuf **ibo) +{ + Gwn_IndexBufBuilder elb; + + int vert_len = groom_count_verts(groom, parts, use_curve_cache); + int edge_len = groom_count_edges(groom, parts, use_curve_cache); + const ListBase *bundles = groom->editgroom ? &groom->editgroom->bundles : &groom->bundles; + + GWN_indexbuf_init(&elb, GWN_PRIM_LINES, edge_len, vert_len); + + uint idx = 0; + if (parts & GM_RENDER_REGIONS) + { + // TODO + } + if (parts & GM_RENDER_CURVES) + { + for (GroomBundle *bundle = bundles->first; bundle; bundle = bundle->next) + { + if (use_curve_cache) + { + GroomCurveCache *cache = bundle->curvecache; + for (int i = 0; i < bundle->curvesize - 1; ++i, ++cache) + { + GWN_indexbuf_add_line_verts(&elb, idx + i, idx + i + 1); + } + idx += bundle->curvesize; + } + else + { + GroomSection *section = bundle->sections; + for (int i = 0; i < bundle->totsections - 1; ++i, ++section) + { + GWN_indexbuf_add_line_verts(&elb, idx + i, idx + i + 1); + } + idx += bundle->totsections; + } + } + } + if (parts & GM_RENDER_SECTIONS) + { + const int curve_res = groom->curve_res; + for (GroomBundle *bundle = bundles->first; bundle; bundle = bundle->next) + { + const int numshapeverts = bundle->numshapeverts; + if (numshapeverts > 1) + { + if (use_curve_cache) + { + const int curvesize = bundle->curvesize; + if (curvesize == 0) + { + continue; + } + + /* a curve for each shape vertex */ + for (int i = 0; i < numshapeverts; ++i) + { + uint idx0 = idx + i * bundle->curvesize; + for (int j = 0; j < bundle->curvesize - 1; ++j) + { + GWN_indexbuf_add_line_verts( + &elb, + idx0 + j, + idx0 + (j+1)); + } + } + + /* a loop for each section */ + for (int i = 0; i < bundle->totsections; ++i) + { + uint idx0 = idx + i * curve_res; + for (int j = 0; j < numshapeverts - 1; ++j) + { + GWN_indexbuf_add_line_verts(&elb, idx0 + j * curvesize, idx0 + (j + 1) * curvesize); + } + // close the loop + GWN_indexbuf_add_line_verts(&elb, idx0 + (numshapeverts-1) * curvesize, idx0); + } + + idx += bundle->curvesize * bundle->numshapeverts; + } + else + { + for (int i = 0; i < bundle->totsections; ++i) + { + uint idx0 = idx + i * numshapeverts; + for (int j = 0; j < numshapeverts - 1; ++j) + { + GWN_indexbuf_add_line_verts(&elb, idx0 + j, idx0 + j + 1); + } + // close the loop + GWN_indexbuf_add_line_verts(&elb, idx0 + (numshapeverts-1), idx0); + } + + idx += bundle->totverts; + } + } + } + } + + *ibo = GWN_indexbuf_build(&elb); +} + +/* Gwn_Batch cache usage. */ +static Gwn_VertBuf *groom_batch_cache_get_pos(Groom *groom, GroomBatchCache *cache, int parts) +{ + if (cache->pos == NULL) { + static Gwn_VertFormat format = { 0 }; + static struct { uint pos, col; } attr_id; + + GWN_vertformat_clear(&format); + + /* initialize vertex format */ + attr_id.pos = GWN_vertformat_attr_add(&format, "pos", GWN_COMP_F32, 3, GWN_FETCH_FLOAT); + + cache->pos = GWN_vertbuf_create_with_format(&format); + + groom_get_verts(groom, parts, true, cache->pos, attr_id.pos, GM_ATTR_ID_UNUSED); + } + + return cache->pos; +} + +static Gwn_IndexBuf *groom_batch_cache_get_edges(Groom *groom, GroomBatchCache *cache, int parts) +{ + if (cache->edges == NULL) { + groom_get_edges(groom, parts, true, &cache->edges); + } + + return cache->edges; +} + +static void groom_batch_cache_create_overlay_batches(Groom *groom, GroomBatchCache *cache, int parts) +{ + if (cache->overlay_verts == NULL) { + static Gwn_VertFormat format = { 0 }; + static struct { uint pos, data; } attr_id; + if (format.attrib_ct == 0) { + /* initialize vertex format */ + attr_id.pos = GWN_vertformat_attr_add(&format, "pos", GWN_COMP_F32, 3, GWN_FETCH_FLOAT); + attr_id.data = GWN_vertformat_attr_add(&format, "data", GWN_COMP_U8, 1, GWN_FETCH_INT); + } + + Gwn_VertBuf *vbo = GWN_vertbuf_create_with_format(&format); + + groom_get_verts(groom, parts, false, vbo, attr_id.pos, attr_id.data); + + cache->overlay_verts = GWN_batch_create_ex(GWN_PRIM_POINTS, vbo, NULL, GWN_BATCH_OWNS_VBO); + } +} + +Gwn_Batch *DRW_groom_batch_cache_get_all_edges(Groom *groom) +{ + GroomBatchCache *cache = groom_batch_cache_get(groom); + + if (cache->all_edges == NULL) { + cache->all_edges = GWN_batch_create( + GWN_PRIM_LINES, + groom_batch_cache_get_pos(groom, cache, GM_RENDER_ALL), + groom_batch_cache_get_edges(groom, cache, GM_RENDER_ALL)); + } + + return cache->all_edges; +} + +Gwn_Batch *DRW_groom_batch_cache_get_all_verts(Groom *groom) +{ + GroomBatchCache *cache = groom_batch_cache_get(groom); + + if (cache->all_verts == NULL) { + cache->all_verts = GWN_batch_create( + GWN_PRIM_POINTS, + groom_batch_cache_get_pos(groom, cache, GM_RENDER_ALL), + NULL); + } + + return cache->all_verts; +} + +Gwn_Batch *DRW_groom_batch_cache_get_overlay_verts(Groom *groom, int mode) +{ + GroomBatchCache *cache = groom_batch_cache_get(groom); + + if (cache->overlay_verts == NULL) { + GroomRenderPart parts = 0; + switch ((GroomEditMode)mode) + { + case GM_EDIT_MODE_REGIONS: parts |= GM_RENDER_REGIONS; break; + case GM_EDIT_MODE_CURVES: parts |= GM_RENDER_CURVES; break; + case GM_EDIT_MODE_SECTIONS: parts |= GM_RENDER_SECTIONS; break; + } + + groom_batch_cache_create_overlay_batches(groom, cache, parts); + } + + return cache->overlay_verts; +} diff --git a/source/blender/draw/intern/draw_cache_impl_hair.c b/source/blender/draw/intern/draw_cache_impl_hair.c new file mode 100644 index 00000000000..b25d6986b81 --- /dev/null +++ b/source/blender/draw/intern/draw_cache_impl_hair.c @@ -0,0 +1,404 @@ +/* + * ***** 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) 2017 by Blender Foundation. + * All rights reserved. + * + * Contributor(s): Blender Foundation, Mike Erwin, Dalai Felinto + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file draw_cache_impl_strands.c + * \ingroup draw + * + * \brief Strands API for render engines + */ + +#include "MEM_guardedalloc.h" + +#include "BLI_utildefines.h" +#include "BLI_math_vector.h" +#include "BLI_ghash.h" + +#include "DNA_hair_types.h" +#include "DNA_scene_types.h" + +#include "BKE_hair.h" +#include "BKE_mesh_sample.h" + +#include "DEG_depsgraph.h" + +#include "GPU_batch.h" +#include "GPU_extensions.h" +#include "GPU_texture.h" + +#include "draw_common.h" +#include "draw_cache_impl.h" /* own include */ +#include "DRW_render.h" + +// timing +//#define DEBUG_TIME +#ifdef DEBUG_TIME +# include "PIL_time_utildefines.h" +#else +# define TIMEIT_START(var) +# define TIMEIT_VALUE(var) +# define TIMEIT_VALUE_PRINT(var) +# define TIMEIT_END(var) +# define TIMEIT_BENCH(expr, id) (expr) +# define TIMEIT_BLOCK_INIT(var) +# define TIMEIT_BLOCK_START(var) +# define TIMEIT_BLOCK_END(var) +# define TIMEIT_BLOCK_STATS(var) +#endif + +/* ---------------------------------------------------------------------- */ +/* Hair Gwn_Batch Cache */ + +typedef struct HairBatchCache { + Gwn_VertBuf *fiber_verts; + Gwn_IndexBuf *fiber_edges; + Gwn_Batch *fibers; + DRWHairFiberTextureBuffer texbuffer; + + Gwn_VertBuf *follicle_verts; + Gwn_IndexBuf *follicle_edges; + Gwn_Batch *follicles; + + Gwn_VertBuf *guide_curve_verts; + Gwn_IndexBuf *guide_curve_edges; + Gwn_Batch *guide_curves; + + /* settings to determine if cache is invalid */ + bool is_dirty; +} HairBatchCache; + +/* Gwn_Batch cache management. */ + +static void hair_batch_cache_clear(HairSystem *hsys); + +static bool hair_batch_cache_valid(HairSystem *hsys) +{ + HairBatchCache *cache = hsys->draw_batch_cache; + + if (cache == NULL) { + return false; + } + + if (cache->is_dirty) { + return false; + } + + return true; +} + +static void hair_batch_cache_init(HairSystem *hsys) +{ + HairBatchCache *cache = hsys->draw_batch_cache; + + if (!cache) { + cache = hsys->draw_batch_cache = MEM_callocN(sizeof(*cache), __func__); + } + else { + memset(cache, 0, sizeof(*cache)); + } + + cache->is_dirty = false; +} + +static HairBatchCache *hair_batch_cache_get(HairSystem *hsys) +{ + if (!hair_batch_cache_valid(hsys)) { + hair_batch_cache_clear(hsys); + hair_batch_cache_init(hsys); + } + return hsys->draw_batch_cache; +} + +void DRW_hair_batch_cache_dirty(HairSystem *hsys, int mode) +{ + HairBatchCache *cache = hsys->draw_batch_cache; + if (cache == NULL) { + return; + } + switch (mode) { + case BKE_HAIR_BATCH_DIRTY_ALL: + cache->is_dirty = true; + break; + default: + BLI_assert(0); + } +} + +static void hair_batch_cache_clear(HairSystem *hsys) +{ + HairBatchCache *cache = hsys->draw_batch_cache; + + if (hsys->draw_texture_cache) { + GPU_texture_free(hsys->draw_texture_cache); + hsys->draw_texture_cache = NULL; + } + + if (cache) { + GWN_BATCH_DISCARD_SAFE(cache->fibers); + GWN_VERTBUF_DISCARD_SAFE(cache->fiber_verts); + GWN_INDEXBUF_DISCARD_SAFE(cache->fiber_edges); + + GWN_BATCH_DISCARD_SAFE(cache->follicles); + GWN_VERTBUF_DISCARD_SAFE(cache->follicle_verts); + GWN_INDEXBUF_DISCARD_SAFE(cache->follicle_edges); + + GWN_BATCH_DISCARD_SAFE(cache->guide_curves); + GWN_VERTBUF_DISCARD_SAFE(cache->guide_curve_verts); + GWN_INDEXBUF_DISCARD_SAFE(cache->guide_curve_edges); + + { + DRWHairFiberTextureBuffer *buffer = &cache->texbuffer; + if (buffer->data) { + MEM_freeN(buffer->data); + buffer->data = NULL; + } + buffer->fiber_start = 0; + buffer->strand_map_start = 0; + buffer->strand_vertex_start = 0; + buffer->width = 0; + buffer->height = 0; + } + } +} + +void DRW_hair_batch_cache_free(HairSystem *hsys) +{ + hair_batch_cache_clear(hsys); + MEM_SAFE_FREE(hsys->draw_batch_cache); +} + +static void hair_batch_cache_ensure_fibers(HairSystem *hsys, int subdiv, HairBatchCache *cache) +{ + TIMEIT_START(hair_batch_cache_ensure_fibers); + + GWN_VERTBUF_DISCARD_SAFE(cache->fiber_verts); + GWN_INDEXBUF_DISCARD_SAFE(cache->fiber_edges); + + const int totfibers = hsys->pattern ? hsys->pattern->num_follicles : 0; + int *fiber_lengths = BKE_hair_get_fiber_lengths(hsys, subdiv); + int totpoint = 0; + for (int i = 0; i < totfibers; ++i) { + totpoint += fiber_lengths[i]; + } + const int totseg = totpoint - totfibers; + + static Gwn_VertFormat format = { 0 }; + static unsigned curve_param_id, fiber_index_id; + + /* initialize vertex format */ + if (format.attrib_ct == 0) { + fiber_index_id = GWN_vertformat_attr_add(&format, "fiber_index", GWN_COMP_I32, 1, GWN_FETCH_INT); + curve_param_id = GWN_vertformat_attr_add(&format, "curve_param", GWN_COMP_F32, 1, GWN_FETCH_FLOAT); + } + + cache->fiber_verts = GWN_vertbuf_create_with_format(&format); + + Gwn_IndexBufBuilder elb; + { + TIMEIT_START(data_alloc); + Gwn_PrimType prim_type; + unsigned prim_ct, vert_ct; + prim_type = GWN_PRIM_TRIS; + prim_ct = 2 * totseg; + vert_ct = 2 * totpoint; + + GWN_vertbuf_data_alloc(cache->fiber_verts, vert_ct); + GWN_indexbuf_init(&elb, prim_type, prim_ct, vert_ct); + TIMEIT_END(data_alloc); + } + + TIMEIT_START(data_fill); + TIMEIT_BLOCK_INIT(GWN_vertbuf_attr_set); + TIMEIT_BLOCK_INIT(GWN_indexbuf_add_tri_verts); + int vi = 0; + for (int i = 0; i < totfibers; ++i) { + const int fiblen = fiber_lengths[i]; + const float da = fiblen > 1 ? 1.0f / (fiblen-1) : 0.0f; + + float a = 0.0f; + for (int k = 0; k < fiblen; ++k) { + TIMEIT_BLOCK_START(GWN_vertbuf_attr_set); + GWN_vertbuf_attr_set(cache->fiber_verts, fiber_index_id, vi, &i); + GWN_vertbuf_attr_set(cache->fiber_verts, curve_param_id, vi, &a); + GWN_vertbuf_attr_set(cache->fiber_verts, fiber_index_id, vi+1, &i); + GWN_vertbuf_attr_set(cache->fiber_verts, curve_param_id, vi+1, &a); + TIMEIT_BLOCK_END(GWN_vertbuf_attr_set); + + if (k > 0) { + TIMEIT_BLOCK_START(GWN_indexbuf_add_tri_verts); + GWN_indexbuf_add_tri_verts(&elb, vi-2, vi-1, vi+1); + GWN_indexbuf_add_tri_verts(&elb, vi+1, vi, vi-2); + TIMEIT_BLOCK_END(GWN_indexbuf_add_tri_verts); + } + + vi += 2; + a += da; + } + } + TIMEIT_BLOCK_STATS(GWN_vertbuf_attr_set); + TIMEIT_BLOCK_STATS(GWN_indexbuf_add_tri_verts); +#ifdef DEBUG_TIME + printf("Total GWN time: %f\n", _timeit_var_GWN_vertbuf_attr_set + _timeit_var_GWN_indexbuf_add_tri_verts); +#endif + fflush(stdout); + TIMEIT_END(data_fill); + + if (fiber_lengths) + { + MEM_freeN(fiber_lengths); + } + + TIMEIT_BENCH(cache->fiber_edges = GWN_indexbuf_build(&elb), indexbuf_build); + + TIMEIT_END(hair_batch_cache_ensure_fibers); +} + +static void hair_batch_cache_ensure_fiber_texbuffer(HairSystem *hsys, struct DerivedMesh *scalp, int subdiv, HairBatchCache *cache) +{ + DRWHairFiberTextureBuffer *buffer = &cache->texbuffer; + static const int elemsize = 8; + const int width = GPU_max_texture_size(); + const int align = width * elemsize; + + // Offsets in bytes + int b_size, b_strand_map_start, b_strand_vertex_start, b_fiber_start; + BKE_hair_get_texture_buffer_size(hsys, subdiv, &b_size, + &b_strand_map_start, &b_strand_vertex_start, &b_fiber_start); + // Pad for alignment + b_size += align - b_size % align; + + // Convert to element size as texture offsets + const int size = b_size / elemsize; + const int height = size / width; + + buffer->data = MEM_mallocN(b_size, "hair fiber texture buffer"); + BKE_hair_get_texture_buffer(hsys, scalp, subdiv, buffer->data); + + buffer->width = width; + buffer->height = height; + buffer->strand_map_start = b_strand_map_start / elemsize; + buffer->strand_vertex_start = b_strand_vertex_start / elemsize; + buffer->fiber_start = b_fiber_start / elemsize; +} + +Gwn_Batch *DRW_hair_batch_cache_get_fibers( + HairSystem *hsys, + struct DerivedMesh *scalp, + int subdiv, + const DRWHairFiberTextureBuffer **r_buffer) +{ + HairBatchCache *cache = hair_batch_cache_get(hsys); + + TIMEIT_START(DRW_hair_batch_cache_get_fibers); + + if (cache->fibers == NULL) { + TIMEIT_BENCH(hair_batch_cache_ensure_fibers(hsys, subdiv, cache), + hair_batch_cache_ensure_fibers); + + TIMEIT_BENCH(cache->fibers = GWN_batch_create(GWN_PRIM_TRIS, cache->fiber_verts, cache->fiber_edges), + GWN_batch_create); + + TIMEIT_BENCH(hair_batch_cache_ensure_fiber_texbuffer(hsys, scalp, subdiv, cache), + hair_batch_cache_ensure_fiber_texbuffer); + } + + if (r_buffer) { + *r_buffer = &cache->texbuffer; + } + + TIMEIT_END(DRW_hair_batch_cache_get_fibers); + + return cache->fibers; +} + +static void hair_batch_cache_ensure_follicles( + HairSystem *hsys, + struct DerivedMesh *scalp, + eHairDrawFollicleMode mode, + HairBatchCache *cache) +{ + GWN_VERTBUF_DISCARD_SAFE(cache->follicle_verts); + GWN_INDEXBUF_DISCARD_SAFE(cache->follicle_edges); + + const HairPattern *pattern = hsys->pattern; + + static Gwn_VertFormat format = { 0 }; + static unsigned pos_id; + + /* initialize vertex format */ + if (format.attrib_ct == 0) { + pos_id = GWN_vertformat_attr_add(&format, "pos", GWN_COMP_F32, 3, GWN_FETCH_FLOAT); + } + + cache->follicle_verts = GWN_vertbuf_create_with_format(&format); + + GWN_vertbuf_data_alloc(cache->follicle_verts, pattern->num_follicles); + + HairFollicle *follicle = pattern->follicles; + for (int i = 0; i < pattern->num_follicles; ++i, ++follicle) { + float co[3], nor[3], tang[3]; + BKE_mesh_sample_eval(scalp, &follicle->mesh_sample, co, nor, tang); + + GWN_vertbuf_attr_set(cache->follicle_verts, pos_id, (unsigned int)i, co); + } +} + +Gwn_Batch *DRW_hair_batch_cache_get_follicle_points( + HairSystem *hsys, + struct DerivedMesh *scalp) +{ + HairBatchCache *cache = hair_batch_cache_get(hsys); + + if (cache->follicles == NULL) { + hair_batch_cache_ensure_follicles(hsys, scalp, HAIR_DRAW_FOLLICLE_POINTS, cache); + + cache->follicles = GWN_batch_create(GWN_PRIM_POINTS, cache->follicle_verts, NULL); + } + + return cache->follicles; + +} + +Gwn_Batch *DRW_hair_batch_cache_get_follicle_axes( + HairSystem *hsys, + struct DerivedMesh *scalp) +{ + return NULL; +} + +Gwn_Batch *DRW_hair_batch_cache_get_guide_curve_points( + HairSystem *hsys, + struct DerivedMesh *scalp, + int subdiv) +{ + return NULL; +} + +Gwn_Batch *DRW_hair_batch_cache_get_guide_curve_edges( + HairSystem *hsys, + struct DerivedMesh *scalp, + int subdiv) +{ + return NULL; +} diff --git a/source/blender/draw/intern/draw_common.h b/source/blender/draw/intern/draw_common.h index ec646693207..3d498cba8b6 100644 --- a/source/blender/draw/intern/draw_common.h +++ b/source/blender/draw/intern/draw_common.h @@ -26,10 +26,15 @@ #ifndef __DRAW_COMMON_H__ #define __DRAW_COMMON_H__ +struct DerivedMesh; struct DRWPass; struct DRWShadingGroup; struct Gwn_Batch; +struct GPUTexture; +struct HairDrawSettings; +struct HairSystem; struct Object; +struct Scene; struct ViewLayer; /* Used as ubo but colors can be directly referenced as well */ @@ -145,4 +150,27 @@ void DRW_shgroup_armature_edit( bool DRW_pose_mode_armature( struct Object *ob, struct Object *active_ob); +/* hair drawing */ +typedef struct DRWHairFiberTextureBuffer { + void *data; + int strand_map_start; + int strand_vertex_start; + int fiber_start; + int width; + int height; +} DRWHairFiberTextureBuffer; + +const char* DRW_hair_shader_defines(void); + +void DRW_hair_shader_uniforms(struct DRWShadingGroup *shgrp, struct Scene *scene, + struct GPUTexture **fibertex, const struct DRWHairFiberTextureBuffer *texbuffer); + +void DRW_shgroup_hair( + struct Object *ob, + struct HairSystem *hsys, + struct HairDrawSettings *draw_settings, + struct DerivedMesh *scalp, + struct DRWShadingGroup *shgrp_verts, + struct DRWShadingGroup *shgrp_edges); + #endif /* __DRAW_COMMON_H__ */ diff --git a/source/blender/draw/intern/draw_hair.c b/source/blender/draw/intern/draw_hair.c new file mode 100644 index 00000000000..8f61c46479c --- /dev/null +++ b/source/blender/draw/intern/draw_hair.c @@ -0,0 +1,87 @@ +/* + * Copyright 2016, 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. + * + * Contributor(s): Blender Institute + * + */ + +/** \file draw_hair.c + * \ingroup draw + */ + +#include "BLI_utildefines.h" + +#include "DNA_hair_types.h" +#include "DNA_scene_types.h" + +#include "BKE_hair.h" + +#include "DRW_render.h" + +#include "GPU_extensions.h" +#include "GPU_texture.h" + +#include "draw_common.h" + +const char* DRW_hair_shader_defines(void) +{ + static char str[256]; + + BLI_snprintf(str, sizeof(str), "#define HAIR_SHADER_FIBERS\n#define HAIR_SHADER_TEX_WIDTH %d\n", + GPU_max_texture_size()); + + return str; +} + +void DRW_hair_shader_uniforms(DRWShadingGroup *shgrp, Scene *UNUSED(scene), + GPUTexture **fibertex, const DRWHairFiberTextureBuffer *texbuffer) +{ + DRW_shgroup_uniform_vec2(shgrp, "viewport_size", DRW_viewport_size_get(), 1); + //DRW_shgroup_uniform_float(shgrp, "ribbon_width", &tsettings->hair_draw_size, 1); + static float test = 2.5f; + DRW_shgroup_uniform_float(shgrp, "ribbon_width", &test, 1); + + DRW_shgroup_uniform_texture_ref(shgrp, "fiber_data", fibertex); + DRW_shgroup_uniform_int(shgrp, "strand_map_start", &texbuffer->strand_map_start, 1); + DRW_shgroup_uniform_int(shgrp, "strand_vertex_start", &texbuffer->strand_vertex_start, 1); + DRW_shgroup_uniform_int(shgrp, "fiber_start", &texbuffer->fiber_start, 1); +} + +void DRW_shgroup_hair( + Object *ob, + HairSystem *hsys, + HairDrawSettings *draw_settings, + struct DerivedMesh *scalp, + DRWShadingGroup *shgrp_verts, + DRWShadingGroup *UNUSED(shgrp_edges)) +{ + switch (draw_settings->follicle_mode) + { + case HAIR_DRAW_FOLLICLE_NONE: + break; + case HAIR_DRAW_FOLLICLE_POINTS: + { + struct Gwn_Batch *geom = DRW_cache_hair_get_follicle_points(hsys, scalp); + DRW_shgroup_call_add(shgrp_verts, geom, ob->obmat); + break; + } + case HAIR_DRAW_FOLLICLE_AXES: + { + break; + } + } +} diff --git a/source/blender/draw/intern/draw_manager.c b/source/blender/draw/intern/draw_manager.c index 8090bcb28ca..752fc52b9b7 100644 --- a/source/blender/draw/intern/draw_manager.c +++ b/source/blender/draw/intern/draw_manager.c @@ -947,6 +947,9 @@ static void drw_engines_enable_from_mode(int mode) case CTX_MODE_EDIT_LATTICE: use_drw_engine(&draw_engine_edit_lattice_type); break; + case CTX_MODE_EDIT_GROOM: + use_drw_engine(&draw_engine_edit_groom_type); + break; case CTX_MODE_POSE: use_drw_engine(&draw_engine_pose_type); break; @@ -1927,6 +1930,7 @@ void DRW_engines_register(void) DRW_engine_register(&draw_engine_object_type); DRW_engine_register(&draw_engine_edit_armature_type); DRW_engine_register(&draw_engine_edit_curve_type); + DRW_engine_register(&draw_engine_edit_groom_type); DRW_engine_register(&draw_engine_edit_lattice_type); DRW_engine_register(&draw_engine_edit_mesh_type); DRW_engine_register(&draw_engine_edit_metaball_type); @@ -1956,6 +1960,12 @@ void DRW_engines_register(void) /* BKE: particle.c */ extern void *BKE_particle_batch_cache_dirty_cb; extern void *BKE_particle_batch_cache_free_cb; + /* BKE: groom.c */ + extern void *BKE_groom_batch_cache_dirty_cb; + extern void *BKE_groom_batch_cache_free_cb; + /* BKE: hair.c */ + extern void *BKE_hair_batch_cache_dirty_cb; + extern void *BKE_hair_batch_cache_free_cb; BKE_mball_batch_cache_dirty_cb = DRW_mball_batch_cache_dirty; BKE_mball_batch_cache_free_cb = DRW_mball_batch_cache_free; @@ -1971,6 +1981,12 @@ void DRW_engines_register(void) BKE_particle_batch_cache_dirty_cb = DRW_particle_batch_cache_dirty; BKE_particle_batch_cache_free_cb = DRW_particle_batch_cache_free; + + BKE_groom_batch_cache_dirty_cb = DRW_groom_batch_cache_dirty; + BKE_groom_batch_cache_free_cb = DRW_groom_batch_cache_free; + + BKE_hair_batch_cache_dirty_cb = DRW_hair_batch_cache_dirty; + BKE_hair_batch_cache_free_cb = DRW_hair_batch_cache_free; } } diff --git a/source/blender/draw/modes/draw_mode_engines.h b/source/blender/draw/modes/draw_mode_engines.h index 23fedbba5a5..e0f50312c7f 100644 --- a/source/blender/draw/modes/draw_mode_engines.h +++ b/source/blender/draw/modes/draw_mode_engines.h @@ -29,6 +29,7 @@ extern DrawEngineType draw_engine_object_type; extern DrawEngineType draw_engine_edit_armature_type; extern DrawEngineType draw_engine_edit_curve_type; +extern DrawEngineType draw_engine_edit_groom_type; extern DrawEngineType draw_engine_edit_lattice_type; extern DrawEngineType draw_engine_edit_mesh_type; extern DrawEngineType draw_engine_edit_metaball_type; @@ -41,4 +42,4 @@ extern DrawEngineType draw_engine_particle_type; extern DrawEngineType draw_engine_pose_type; extern DrawEngineType draw_engine_sculpt_type; -#endif /* __DRAW_MODE_ENGINES_H__ */
\ No newline at end of file +#endif /* __DRAW_MODE_ENGINES_H__ */ diff --git a/source/blender/draw/modes/edit_groom_mode.c b/source/blender/draw/modes/edit_groom_mode.c new file mode 100644 index 00000000000..d12f918c0bc --- /dev/null +++ b/source/blender/draw/modes/edit_groom_mode.c @@ -0,0 +1,304 @@ +/* + * ***** 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/draw/modes/edit_groom_mode.c + * \ingroup draw + */ + +#include "DRW_engine.h" +#include "DRW_render.h" + +/* If builtin shaders are needed */ +#include "GPU_shader.h" + +#include "draw_common.h" + +#include "draw_mode_engines.h" + +/* If needed, contains all global/Theme colors + * Add needed theme colors / values to DRW_globals_update() and update UBO + * Not needed for constant color. */ +extern struct GPUUniformBuffer *globals_ubo; /* draw_common.c */ +extern struct GlobalsUboStorage ts; /* draw_common.c */ + +extern char datatoc_common_globals_lib_glsl[]; +extern char datatoc_edit_groom_overlay_loosevert_vert_glsl[]; +extern char datatoc_edit_groom_overlay_frag_glsl[]; + +extern char datatoc_gpu_shader_3D_vert_glsl[]; +extern char datatoc_gpu_shader_uniform_color_frag_glsl[]; +extern char datatoc_gpu_shader_point_uniform_color_frag_glsl[]; + +/* *********** LISTS *********** */ +/* All lists are per viewport specific datas. + * They are all free when viewport changes engines + * or is free itself. Use EDIT_GROOM_engine_init() to + * initialize most of them and EDIT_GROOM_cache_init() + * for EDIT_GROOM_PassList */ + +typedef struct EDIT_GROOM_PassList { + /* Declare all passes here and init them in + * EDIT_GROOM_cache_init(). + * Only contains (DRWPass *) */ + struct DRWPass *wire_pass; + struct DRWPass *vert_pass; +} EDIT_GROOM_PassList; + +typedef struct EDIT_GROOM_FramebufferList { + /* Contains all framebuffer objects needed by this engine. + * Only contains (GPUFrameBuffer *) */ + struct GPUFrameBuffer *fb; +} EDIT_GROOM_FramebufferList; + +typedef struct EDIT_GROOM_TextureList { + /* Contains all framebuffer textures / utility textures + * needed by this engine. Only viewport specific textures + * (not per object). Only contains (GPUTexture *) */ + struct GPUTexture *texture; +} EDIT_GROOM_TextureList; + +typedef struct EDIT_GROOM_StorageList { + /* Contains any other memory block that the engine needs. + * Only directly MEM_(m/c)allocN'ed blocks because they are + * free with MEM_freeN() when viewport is freed. + * (not per object) */ + struct CustomStruct *block; + struct EDIT_GROOM_PrivateData *g_data; +} EDIT_GROOM_StorageList; + +typedef struct EDIT_GROOM_Data { + /* Struct returned by DRW_viewport_engine_data_ensure. + * If you don't use one of these, just make it a (void *) */ + // void *fbl; + void *engine_type; /* Required */ + EDIT_GROOM_FramebufferList *fbl; + EDIT_GROOM_TextureList *txl; + EDIT_GROOM_PassList *psl; + EDIT_GROOM_StorageList *stl; +} EDIT_GROOM_Data; + +/* *********** STATIC *********** */ + +static struct { + /* Custom shaders : + * Add sources to source/blender/draw/modes/shaders + * init in EDIT_GROOM_engine_init(); + * free in EDIT_GROOM_engine_free(); */ + GPUShader *wire_sh; + + GPUShader *overlay_vert_sh; + +} e_data = {NULL}; /* Engine data */ + +typedef struct EDIT_GROOM_PrivateData { + /* This keeps the references of the shading groups for + * easy access in EDIT_GROOM_cache_populate() */ + DRWShadingGroup *wire_shgrp; + DRWShadingGroup *vert_shgrp; +} EDIT_GROOM_PrivateData; /* Transient data */ + +/* *********** FUNCTIONS *********** */ + +/* Init Textures, Framebuffers, Storage and Shaders. + * It is called for every frames. + * (Optional) */ +static void EDIT_GROOM_engine_init(void *vedata) +{ + EDIT_GROOM_TextureList *txl = ((EDIT_GROOM_Data *)vedata)->txl; + EDIT_GROOM_FramebufferList *fbl = ((EDIT_GROOM_Data *)vedata)->fbl; + EDIT_GROOM_StorageList *stl = ((EDIT_GROOM_Data *)vedata)->stl; + + UNUSED_VARS(txl, fbl, stl); + + /* Init Framebuffers like this: order is attachment order (for color texs) */ + /* + * DRWFboTexture tex[2] = {{&txl->depth, DRW_TEX_DEPTH_24, 0}, + * {&txl->color, DRW_TEX_RGBA_8, DRW_TEX_FILTER}}; + */ + + /* DRW_framebuffer_init takes care of checking if + * the framebuffer is valid and has the right size*/ + /* + * float *viewport_size = DRW_viewport_size_get(); + * DRW_framebuffer_init(&fbl->occlude_wire_fb, + * (int)viewport_size[0], (int)viewport_size[1], + * tex, 2); + */ + + if (!e_data.wire_sh) { + e_data.wire_sh = GPU_shader_get_builtin_shader(GPU_SHADER_3D_SMOOTH_COLOR); + } + + if (!e_data.overlay_vert_sh) { + e_data.overlay_vert_sh = DRW_shader_create_with_lib( + datatoc_edit_groom_overlay_loosevert_vert_glsl, NULL, + datatoc_edit_groom_overlay_frag_glsl, + datatoc_common_globals_lib_glsl, NULL); + } +} + +/* Here init all passes and shading groups + * Assume that all Passes are NULL */ +static void EDIT_GROOM_cache_init(void *vedata) +{ + EDIT_GROOM_PassList *psl = ((EDIT_GROOM_Data *)vedata)->psl; + EDIT_GROOM_StorageList *stl = ((EDIT_GROOM_Data *)vedata)->stl; + + if (!stl->g_data) { + /* Alloc transient pointers */ + stl->g_data = MEM_mallocN(sizeof(*stl->g_data), __func__); + } + + { + psl->wire_pass = DRW_pass_create( + "Groom Wire", + DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS | DRW_STATE_WIRE); + stl->g_data->wire_shgrp = DRW_shgroup_create(e_data.wire_sh, psl->wire_pass); + + psl->vert_pass = DRW_pass_create( + "Groom Verts", + DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH | DRW_STATE_POINT); + stl->g_data->vert_shgrp = DRW_shgroup_create(e_data.overlay_vert_sh, psl->vert_pass); + + DRW_shgroup_uniform_block(stl->g_data->vert_shgrp, "globalsBlock", globals_ubo); + } +} + +/* Add geometry to shadingGroups. Execute for each objects */ +static void EDIT_GROOM_cache_populate(void *vedata, Object *ob) +{ + EDIT_GROOM_PassList *psl = ((EDIT_GROOM_Data *)vedata)->psl; + EDIT_GROOM_StorageList *stl = ((EDIT_GROOM_Data *)vedata)->stl; + const DRWContextState *draw_ctx = DRW_context_state_get(); + Scene *scene = draw_ctx->scene; + Object *obedit = draw_ctx->object_edit; + GroomEditSettings *editsettings = &scene->toolsettings->groom_edit_settings; + + UNUSED_VARS(psl); + + if (ob->type == OB_GROOM) { + if (ob == obedit) { + /* Get geometry cache */ + struct Gwn_Batch *geom; + + geom = DRW_cache_groom_wire_get(ob); + DRW_shgroup_call_add(stl->g_data->wire_shgrp, geom, ob->obmat); + + geom = DRW_cache_groom_vert_overlay_get(ob, editsettings->mode); + DRW_shgroup_call_add(stl->g_data->vert_shgrp, geom, ob->obmat); + } + } +} + +/* Optional: Post-cache_populate callback */ +static void EDIT_GROOM_cache_finish(void *vedata) +{ + EDIT_GROOM_PassList *psl = ((EDIT_GROOM_Data *)vedata)->psl; + EDIT_GROOM_StorageList *stl = ((EDIT_GROOM_Data *)vedata)->stl; + + /* Do something here! dependant on the objects gathered */ + UNUSED_VARS(psl, stl); +} + +/* Draw time ! Control rendering pipeline from here */ +static void EDIT_GROOM_draw_scene(void *vedata) +{ + EDIT_GROOM_PassList *psl = ((EDIT_GROOM_Data *)vedata)->psl; + EDIT_GROOM_FramebufferList *fbl = ((EDIT_GROOM_Data *)vedata)->fbl; + + /* Default framebuffer and texture */ + DefaultFramebufferList *dfbl = DRW_viewport_framebuffer_list_get(); + DefaultTextureList *dtxl = DRW_viewport_texture_list_get(); + + UNUSED_VARS(fbl, dtxl); + + MULTISAMPLE_SYNC_ENABLE(dfbl) + + /* Show / hide entire passes, swap framebuffers ... whatever you fancy */ + /* + * DRW_framebuffer_texture_detach(dtxl->depth); + * DRW_framebuffer_bind(fbl->custom_fb); + * DRW_draw_pass(psl->pass); + * DRW_framebuffer_texture_attach(dfbl->default_fb, dtxl->depth, 0, 0); + * DRW_framebuffer_bind(dfbl->default_fb); + */ + + /* ... or just render passes on default framebuffer. */ + DRW_draw_pass(psl->wire_pass); + DRW_draw_pass(psl->vert_pass); + + MULTISAMPLE_SYNC_DISABLE(dfbl) + + /* If you changed framebuffer, double check you rebind + * the default one with its textures attached before finishing */ +} + +/* Cleanup when destroying the engine. + * This is not per viewport ! only when quitting blender. + * Mostly used for freeing shaders */ +static void EDIT_GROOM_engine_free(void) +{ + // Currently built-in, dont free + DRW_SHADER_FREE_SAFE(e_data.overlay_vert_sh); +} + +/* Create collection settings here. + * + * Be sure to add this function there : + * source/blender/draw/DRW_engine.h + * source/blender/blenkernel/intern/layer.c + * source/blenderplayer/bad_level_call_stubs/stubs.c + * + * And relevant collection settings to : + * source/blender/makesrna/intern/rna_scene.c + * source/blender/blenkernel/intern/layer.c + */ +#if 0 +void EDIT_GROOM_collection_settings_create(CollectionEngineSettings *ces) +{ + BLI_assert(ces); + // BKE_collection_engine_property_add_int(ces, "my_bool_prop", false); + // BKE_collection_engine_property_add_int(ces, "my_int_prop", 0); + // BKE_collection_engine_property_add_float(ces, "my_float_prop", 0.0f); +} +#endif + +static const DrawEngineDataSize EDIT_GROOM_data_size = DRW_VIEWPORT_DATA_SIZE(EDIT_GROOM_Data); + +DrawEngineType draw_engine_edit_groom_type = { + NULL, NULL, + N_("EditGroomMode"), + &EDIT_GROOM_data_size, + &EDIT_GROOM_engine_init, + &EDIT_GROOM_engine_free, + &EDIT_GROOM_cache_init, + &EDIT_GROOM_cache_populate, + &EDIT_GROOM_cache_finish, + NULL, /* draw_background but not needed by mode engines */ + &EDIT_GROOM_draw_scene, + NULL, +}; diff --git a/source/blender/draw/modes/object_mode.c b/source/blender/draw/modes/object_mode.c index 939507a4f21..52db4092fbc 100644 --- a/source/blender/draw/modes/object_mode.c +++ b/source/blender/draw/modes/object_mode.c @@ -30,8 +30,10 @@ #include "DNA_armature_types.h" #include "DNA_camera_types.h" #include "DNA_curve_types.h" +#include "DNA_groom_types.h" #include "DNA_mesh_types.h" #include "DNA_meta_types.h" +#include "DNA_modifier_types.h" #include "DNA_object_force_types.h" #include "DNA_lightprobe_types.h" #include "DNA_particle_types.h" @@ -44,7 +46,9 @@ #include "BKE_camera.h" #include "BKE_curve.h" #include "BKE_global.h" +#include "BKE_groom.h" #include "BKE_mball.h" +#include "BKE_modifier.h" #include "BKE_object.h" #include "BKE_particle.h" #include "BKE_image.h" @@ -100,6 +104,7 @@ typedef struct OBJECT_PassList { struct DRWPass *bone_wire; struct DRWPass *bone_envelope; struct DRWPass *particle; + struct DRWPass *hair; struct DRWPass *lightprobes; /* use for empty/background images */ struct DRWPass *reference_image; @@ -223,6 +228,10 @@ typedef struct OBJECT_PrivateData { DRWShadingGroup *points_select; DRWShadingGroup *points_select_group; DRWShadingGroup *points_transform; + + /* Hair Systems */ + DRWShadingGroup *hair_verts; + DRWShadingGroup *hair_edges; } OBJECT_PrivateData; /* Transient data */ static struct { @@ -1172,6 +1181,24 @@ static void OBJECT_cache_init(void *vedata) } { + /* Hair */ + psl->hair = DRW_pass_create( + "Hair Pass", + DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS | DRW_STATE_BLEND | + DRW_STATE_POINT | DRW_STATE_WIRE); + + GPUShader *sh_verts = GPU_shader_get_builtin_shader(GPU_SHADER_3D_POINT_UNIFORM_SIZE_UNIFORM_COLOR_AA); + stl->g_data->hair_verts = DRW_shgroup_create(sh_verts, psl->hair); + DRW_shgroup_uniform_vec4(stl->g_data->hair_verts, "color", ts.colorVertex, 1); + DRW_shgroup_uniform_float(stl->g_data->hair_verts, "size", &ts.sizeVertex, 1); + DRW_shgroup_state_enable(stl->g_data->hair_verts, DRW_STATE_POINT); + + GPUShader *sh_edges = GPU_shader_get_builtin_shader(GPU_SHADER_3D_UNIFORM_COLOR); + stl->g_data->hair_edges = DRW_shgroup_create(sh_edges, psl->hair); + DRW_shgroup_uniform_vec4(stl->g_data->hair_edges, "color", ts.colorWire, 1); + } + + { /* Empty/Background Image Pass */ psl->reference_image = DRW_pass_create( "Refrence Image Pass", @@ -1973,6 +2000,21 @@ static void OBJECT_cache_populate(void *vedata, Object *ob) } break; } + case OB_GROOM: + { + if (ob != draw_ctx->object_edit) { + Groom *groom = ob->data; + struct Gwn_Batch *geom = DRW_cache_groom_wire_get(ob); + if (theme_id == TH_UNDEFINED) { + theme_id = DRW_object_wire_theme_get(ob, view_layer, NULL); + } + DRWShadingGroup *shgroup = shgroup_theme_id_to_wire_or(stl, theme_id, stl->g_data->wire); + DRW_shgroup_call_add(shgroup, geom, ob->obmat); + + DRW_shgroup_hair(ob, groom->hair_system, groom->hair_draw_settings, BKE_groom_get_scalp(groom), stl->g_data->hair_verts, stl->g_data->hair_edges); + } + break; + } case OB_LAMP: DRW_shgroup_lamp(stl, ob, view_layer); break; @@ -2004,6 +2046,27 @@ static void OBJECT_cache_populate(void *vedata, Object *ob) break; } + { + struct DerivedMesh *scalp = ob->derivedFinal; + if (scalp) + { + for (ModifierData *md = ob->modifiers.first; md; md = md->next) + { + if (md->type == eModifierType_Fur) + { + FurModifierData *fmd = (FurModifierData*)md; + + if (!modifier_isEnabled(draw_ctx->scene, md, eModifierMode_Realtime)) + { + continue; + } + + DRW_shgroup_hair(ob, fmd->hair_system, fmd->draw_settings, scalp, stl->g_data->hair_verts, stl->g_data->hair_edges); + } + } + } + } + if (ob->pd && ob->pd->forcefield) { DRW_shgroup_forcefield(stl, ob, view_layer); } @@ -2081,6 +2144,7 @@ static void OBJECT_draw_scene(void *vedata) DRW_draw_pass(psl->bone_solid); DRW_draw_pass(psl->non_meshes); DRW_draw_pass(psl->particle); + DRW_draw_pass(psl->hair); DRW_draw_pass(psl->reference_image); MULTISAMPLE_SYNC_DISABLE(dfbl) diff --git a/source/blender/draw/modes/shaders/edit_groom_overlay_frag.glsl b/source/blender/draw/modes/shaders/edit_groom_overlay_frag.glsl new file mode 100644 index 00000000000..e78462b6915 --- /dev/null +++ b/source/blender/draw/modes/shaders/edit_groom_overlay_frag.glsl @@ -0,0 +1,22 @@ + +flat in int vertFlag; + +#define VERTEX_SELECTED (1 << 0) +#define VERTEX_ACTIVE (1 << 1) + +out vec4 FragColor; + +void main() +{ + /* TODO: vertex size */ + + if ((vertFlag & VERTEX_SELECTED) != 0) { + FragColor = colorVertexSelect; + } + else if ((vertFlag & VERTEX_ACTIVE) != 0) { + FragColor = colorEditMeshActive; + } + else { + FragColor = colorVertex; + } +} diff --git a/source/blender/draw/modes/shaders/edit_groom_overlay_loosevert_vert.glsl b/source/blender/draw/modes/shaders/edit_groom_overlay_loosevert_vert.glsl new file mode 100644 index 00000000000..6a26ad70524 --- /dev/null +++ b/source/blender/draw/modes/shaders/edit_groom_overlay_loosevert_vert.glsl @@ -0,0 +1,39 @@ + +/* Draw Groom Vertices */ + +uniform mat4 ModelViewProjectionMatrix; +uniform vec2 viewportSize; + +in vec3 pos; +in int data; + +/* these are the same for all vertices + * and does not need interpolation */ +flat out int vertFlag; +flat out int clipCase; + +/* See fragment shader */ +noperspective out vec4 eData1; +flat out vec4 eData2; + +/* project to screen space */ +vec2 proj(vec4 pos) +{ + return (0.5 * (pos.xy / pos.w) + 0.5) * viewportSize; +} + +void main() +{ + clipCase = 0; + + vec4 pPos = ModelViewProjectionMatrix * vec4(pos, 1.0); + + /* only vertex position 0 is used */ + eData1 = eData2 = vec4(1e10); + eData2.zw = proj(pPos); + + vertFlag = data; + + gl_PointSize = sizeVertex; + gl_Position = pPos; +} diff --git a/source/blender/editors/CMakeLists.txt b/source/blender/editors/CMakeLists.txt index 06f412b7019..e626636e1ab 100644 --- a/source/blender/editors/CMakeLists.txt +++ b/source/blender/editors/CMakeLists.txt @@ -27,6 +27,7 @@ if(WITH_BLENDER) add_subdirectory(armature) add_subdirectory(curve) add_subdirectory(gpencil) + add_subdirectory(groom) add_subdirectory(interface) add_subdirectory(io) add_subdirectory(lattice) diff --git a/source/blender/editors/groom/CMakeLists.txt b/source/blender/editors/groom/CMakeLists.txt new file mode 100644 index 00000000000..e145faf96fc --- /dev/null +++ b/source/blender/editors/groom/CMakeLists.txt @@ -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. +# +# Contributor(s): Lukas Toenne. +# +# ***** END GPL LICENSE BLOCK ***** + +set(INC + ../include + ../../blenkernel + ../../blenlib + ../../blentranslation + ../../depsgraph + ../../makesdna + ../../makesrna + ../../windowmanager + ../../../../intern/guardedalloc +) + +set(INC_SYS +) + +set(SRC + groom_hair.c + groom_ops.c + editgroom.c + editgroom_region.c + editgroom_select.c + + groom_intern.h +) + +if(WITH_INTERNATIONAL) + add_definitions(-DWITH_INTERNATIONAL) +endif() + +blender_add_lib(bf_editor_groom "${SRC}" "${INC}" "${INC_SYS}") diff --git a/source/blender/editors/groom/editgroom.c b/source/blender/editors/groom/editgroom.c new file mode 100644 index 00000000000..47f5d40c09b --- /dev/null +++ b/source/blender/editors/groom/editgroom.c @@ -0,0 +1,137 @@ +/* + * ***** 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/groom/editgroom.c + * \ingroup edgroom + */ + +#include "DNA_groom_types.h" +#include "DNA_object_types.h" +#include "DNA_scene_types.h" + +#include "MEM_guardedalloc.h" + +#include "BLI_array_utils.h" +#include "BLI_blenlib.h" + +#include "BKE_context.h" +#include "BKE_global.h" +#include "BKE_groom.h" +#include "BKE_library.h" +#include "BKE_main.h" +#include "BKE_report.h" + +#include "DEG_depsgraph.h" +#include "DEG_depsgraph_build.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "ED_groom.h" +#include "ED_object.h" +#include "ED_screen.h" +#include "ED_types.h" +#include "ED_util.h" + +#include "groom_intern.h" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "RNA_access.h" +#include "RNA_define.h" +#include "RNA_enum_types.h" + +/********************** Load/Make/Free ********************/ + +static void groom_bundles_free(ListBase *bundles) +{ + for (GroomBundle *bundle = bundles->first; bundle; bundle = bundle->next) + { + BKE_groom_bundle_curve_cache_clear(bundle); + + if (bundle->verts) + { + MEM_freeN(bundle->verts); + } + if (bundle->sections) + { + MEM_freeN(bundle->sections); + } + } + BLI_freelistN(bundles); +} + +static void groom_bundles_copy(ListBase *bundles_dst, ListBase *bundles_src) +{ + BLI_duplicatelist(bundles_dst, bundles_src); + for (GroomBundle *bundle = bundles_dst->first; bundle; bundle = bundle->next) + { + if (bundle->curvecache) + { + bundle->curvecache = MEM_dupallocN(bundle->curvecache); + } + if (bundle->sections) + { + bundle->sections = MEM_dupallocN(bundle->sections); + } + if (bundle->verts) + { + bundle->verts = MEM_dupallocN(bundle->verts); + } + } +} + +void ED_groom_editgroom_make(Object *obedit) +{ + Groom *groom = obedit->data; + + ED_groom_editgroom_free(obedit); + + groom->editgroom = MEM_callocN(sizeof(EditGroom), "editgroom"); + groom_bundles_copy(&groom->editgroom->bundles, &groom->bundles); +} + +void ED_groom_editgroom_load(Object *obedit) +{ + Groom *groom = obedit->data; + + groom_bundles_free(&groom->bundles); + groom_bundles_copy(&groom->bundles, &groom->editgroom->bundles); +} + +void ED_groom_editgroom_free(Object *ob) +{ + Groom *groom = ob->data; + + if (groom->editgroom) { + groom_bundles_free(&groom->editgroom->bundles); + + MEM_freeN(groom->editgroom); + groom->editgroom = NULL; + } +} diff --git a/source/blender/editors/groom/editgroom_region.c b/source/blender/editors/groom/editgroom_region.c new file mode 100644 index 00000000000..f138eff322a --- /dev/null +++ b/source/blender/editors/groom/editgroom_region.c @@ -0,0 +1,194 @@ +/* + * ***** 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/groom/editgroom_region.c + * \ingroup edgroom + */ + +#include "DNA_groom_types.h" +#include "DNA_object_types.h" +#include "DNA_scene_types.h" + +#include "MEM_guardedalloc.h" + +#include "BLI_blenlib.h" +#include "BLI_math.h" + +#include "BLT_translation.h" + +#include "BKE_context.h" +#include "BKE_groom.h" +#include "BKE_library.h" + +#include "DEG_depsgraph.h" + +#include "RNA_access.h" +#include "RNA_define.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "ED_object.h" +#include "ED_screen.h" +#include "ED_util.h" +#include "ED_view3d.h" +#include "ED_groom.h" + +#include "groom_intern.h" + +/* GROOM_OT_region_add */ + +static void groom_bundle_section_init( + GroomSection *section, + GroomSectionVertex *verts, + int numverts, + float mat[4][4], + float x, + float y, + float z) +{ + section->center[0] = x; + section->center[1] = y; + section->center[2] = z; + mul_m4_v3(mat, section->center); + + { + const float radius = 0.5f; + GroomSectionVertex *vertex = verts; + for (int i = 0; i < numverts; ++i, ++vertex) + { + float angle = 2*M_PI * (float)i / (float)numverts; + vertex->co[0] = cos(angle) * radius; + vertex->co[1] = sin(angle) * radius; + } + } +} + +static GroomBundle* groom_add_bundle(float mat[4][4]) +{ + GroomBundle *bundle = MEM_callocN(sizeof(GroomBundle), "groom bundle"); + + bundle->numshapeverts = 6; + bundle->totsections = 4; + bundle->totverts = bundle->numshapeverts * bundle->totsections; + bundle->sections = MEM_mallocN(sizeof(GroomSection) * bundle->totsections, "groom bundle sections"); + bundle->verts = MEM_mallocN(sizeof(GroomSectionVertex) * bundle->totverts, "groom bundle vertices"); + + int numverts = bundle->numshapeverts; + groom_bundle_section_init(&bundle->sections[0], &bundle->verts[numverts * 0], numverts, mat, 0.0, 0.0, 0.0); + groom_bundle_section_init(&bundle->sections[1], &bundle->verts[numverts * 1], numverts, mat, 0.0, 0.0, 1.0); + groom_bundle_section_init(&bundle->sections[2], &bundle->verts[numverts * 2], numverts, mat, 0.4, -0.2, 1.2); + groom_bundle_section_init(&bundle->sections[3], &bundle->verts[numverts * 3], numverts, mat, 0.01, 0.7, 1.6); + + return bundle; +} + +static int region_add_exec(bContext *C, wmOperator *op) +{ + Object *obedit = CTX_data_edit_object(C); + Groom *groom = obedit->data; + EditGroom *editgroom = groom->editgroom; + + WM_operator_view3d_unit_defaults(C, op); + + float loc[3], rot[3]; + unsigned int layer; + if (!ED_object_add_generic_get_opts(C, op, 'Z', loc, rot, NULL, &layer, NULL)) + return OPERATOR_CANCELLED; + + float mat[4][4]; + ED_object_new_primitive_matrix(C, obedit, loc, rot, mat); + + GroomBundle *bundle = groom_add_bundle(mat); + BLI_addtail(&editgroom->bundles, bundle); + + WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, obedit); + DEG_id_tag_update(&obedit->id, OB_RECALC_DATA); + + return OPERATOR_FINISHED; +} + +void GROOM_OT_region_add(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Add Region"; + ot->description = "Add a new region to the groom object"; + ot->idname = "GROOM_OT_region_add"; + + /* api callbacks */ + ot->exec = region_add_exec; + ot->poll = ED_operator_editgroom; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + ED_object_add_generic_props(ot, false); +} + +/* GROOM_OT_region_bind */ + +static int region_bind_exec(bContext *C, wmOperator *op) +{ + Object *ob = ED_object_context(C); + Groom *groom = ob->data; + const bool force_rebind = RNA_int_get(op->ptr, "force_rebind"); + + GroomBundle *bundle = CTX_data_pointer_get_type(C, "groom_bundle", &RNA_GroomBundle).data; + if (!bundle) + { + bundle = BLI_findlink(&groom->bundles, groom->active_bundle); + if (!bundle) + { + return OPERATOR_CANCELLED; + } + } + + BKE_groom_bundle_bind(groom, bundle, force_rebind); + + WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob); + DEG_id_tag_update(&ob->id, OB_RECALC_DATA); + + return OPERATOR_FINISHED; +} + +void GROOM_OT_region_bind(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Bind Region"; + ot->description = "Bind a groom bundle to its scalp region"; + ot->idname = "GROOM_OT_region_bind"; + + /* api callbacks */ + ot->exec = region_bind_exec; + ot->poll = ED_operator_scene_editable; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + RNA_def_boolean(ot->srna, "force_rebind", true, "Force Rebind", + "Force rebinding of the groom region even if a binding already exists"); +} diff --git a/source/blender/editors/groom/editgroom_select.c b/source/blender/editors/groom/editgroom_select.c new file mode 100644 index 00000000000..9592d3e21fe --- /dev/null +++ b/source/blender/editors/groom/editgroom_select.c @@ -0,0 +1,442 @@ +/* + * ***** 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/groom/editgroom_select.c + * \ingroup edgroom + */ + +#include "DNA_groom_types.h" +#include "DNA_object_types.h" +#include "DNA_scene_types.h" + +#include "MEM_guardedalloc.h" + +#include "BLI_math.h" + +#include "BKE_context.h" +#include "BKE_groom.h" +#include "BKE_report.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "ED_screen.h" +#include "ED_types.h" +#include "ED_view3d.h" +#include "ED_groom.h" + +#include "groom_intern.h" + +#include "RNA_access.h" +#include "RNA_define.h" + +bool ED_groom_select_check_regions(const EditGroom *edit) +{ + for (GroomBundle* bundle = edit->bundles.first; bundle; bundle = bundle->next) + { + if (bundle->flag & GM_BUNDLE_SELECT) + { + return true; + } + } + + return false; +} + +bool ED_groom_select_check_curves(const EditGroom *edit) +{ + for (GroomBundle* bundle = edit->bundles.first; bundle; bundle = bundle->next) + { + GroomSection *section = bundle->sections; + for (int i = 0; i < bundle->totsections; ++i, ++section) + { + if (section->flag & GM_SECTION_SELECT) { + return true; + } + } + } + + return false; +} + +bool ED_groom_select_check_sections(const EditGroom *edit) +{ + for (GroomBundle* bundle = edit->bundles.first; bundle; bundle = bundle->next) + { + GroomSectionVertex *vertex = bundle->verts; + for (int i = 0; i < bundle->totverts; ++i, ++vertex) + { + if (vertex->flag & GM_VERTEX_SELECT) + { + return true; + } + } + } + + return false; +} + +void ED_groom_select_regions(EditGroom *edit, EditGroomSelectCb select_cb, void *userdata) +{ + for (GroomBundle* bundle = edit->bundles.first; bundle; bundle = bundle->next) + { + const bool select = select_cb(userdata, bundle->flag & GM_BUNDLE_SELECT); + if (select) + { + bundle->flag |= GM_BUNDLE_SELECT; + } + else + { + bundle->flag &= ~GM_BUNDLE_SELECT; + } + } +} + +void ED_groom_select_curves(EditGroom *edit, EditGroomSelectCb select_cb, void *userdata) +{ + for (GroomBundle* bundle = edit->bundles.first; bundle; bundle = bundle->next) + { + GroomSection *section = bundle->sections; + for (int i = 0; i < bundle->totsections; ++i, ++section) + { + const bool select = select_cb(userdata, section->flag & GM_SECTION_SELECT); + if (select) + { + section->flag |= GM_SECTION_SELECT; + } + else + { + section->flag &= ~GM_SECTION_SELECT; + } + } + } +} + +void ED_groom_select_sections(EditGroom *edit, EditGroomSelectCb select_cb, void *userdata) +{ + for (GroomBundle* bundle = edit->bundles.first; bundle; bundle = bundle->next) + { + GroomSectionVertex *vertex = bundle->verts; + for (int i = 0; i < bundle->totverts; ++i, ++vertex) + { + const bool select = select_cb(userdata, vertex->flag & GM_VERTEX_SELECT); + if (select) + { + vertex->flag |= GM_VERTEX_SELECT; + } + else + { + vertex->flag &= ~GM_VERTEX_SELECT; + } + } + } +} + +static bool groom_select_all_cb(void *UNUSED(userdata), bool UNUSED(is_selected)) +{ + return true; +} + +static bool groom_deselect_all_cb(void *UNUSED(userdata), bool UNUSED(is_selected)) +{ + return false; +} + +static bool groom_select_swap_cb(void *UNUSED(userdata), bool is_selected) +{ + return !is_selected; +} + +static bool groom_has_selected(EditGroom *edit, GroomEditMode mode) +{ + switch (mode) + { + case GM_EDIT_MODE_REGIONS: + return ED_groom_select_check_regions(edit); + case GM_EDIT_MODE_CURVES: + return ED_groom_select_check_curves(edit); + case GM_EDIT_MODE_SECTIONS: + return ED_groom_select_check_sections(edit); + } + return false; +} + +static int de_select_all_exec(bContext *C, wmOperator *op) +{ + GroomEditMode mode = CTX_data_tool_settings(C)->groom_edit_settings.mode; + Object *obedit = CTX_data_edit_object(C); + Groom *groom = obedit->data; + int action = RNA_enum_get(op->ptr, "action"); + + EditGroomSelectCb cb; + switch (action) { + case SEL_SELECT: + cb = groom_select_all_cb; + break; + case SEL_DESELECT: + cb = groom_deselect_all_cb; + break; + case SEL_INVERT: + cb = groom_select_swap_cb; + break; + case SEL_TOGGLE: + { + if (groom_has_selected(groom->editgroom, mode)) { + cb = groom_deselect_all_cb; + } + else + { + cb = groom_select_all_cb; + } + } + } + + switch (mode) + { + case GM_EDIT_MODE_REGIONS: + ED_groom_select_regions(groom->editgroom, cb, NULL); + break; + case GM_EDIT_MODE_CURVES: + ED_groom_select_curves(groom->editgroom, cb, NULL); + break; + case GM_EDIT_MODE_SECTIONS: + ED_groom_select_sections(groom->editgroom, cb, NULL); + break; + } + + WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data); + + return OPERATOR_FINISHED; +} + +void GROOM_OT_select_all(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "(De)select All"; + ot->idname = "GROOM_OT_select_all"; + ot->description = "(De)select all control points"; + + /* api callbacks */ + ot->exec = de_select_all_exec; + ot->poll = ED_operator_editgroom; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* properties */ + WM_operator_properties_select_all(ot); +} + +/****************************** Mouse Selection *************************/ + +static void select_pick_findnearest_cb( + void *userdata, + GroomBundle *bundle, + GroomSection *section, + GroomSectionVertex *vertex, + const float screen_co[2]) +{ + struct + { + GroomBundle *bundle; + GroomSection *section; + GroomSectionVertex *vertex; + float dist; + bool select; + float mval_fl[2]; + } *data = userdata; + + float dist_test = len_manhattan_v2v2(data->mval_fl, screen_co); + + /* bias towards unselected items */ + if (data->select && + ((vertex && vertex->flag & GM_VERTEX_SELECT) || + (section && section->flag & GM_SECTION_SELECT) || + (bundle && bundle->flag & GM_BUNDLE_SELECT))) + { + dist_test += 5.0f; + } + + if (dist_test < data->dist) { + data->dist = dist_test; + data->bundle = bundle; + data->section = section; + data->vertex = vertex; + } +} + +static void groom_set_region_select_flags(Groom *groom, int flag) +{ + for (GroomBundle *bundle = groom->editgroom->bundles.first; bundle; bundle = bundle->next) + { + bundle->flag = (bundle->flag & ~GM_BUNDLE_SELECT) | (flag & GM_BUNDLE_SELECT); + } +} + +static void groom_set_curve_select_flags(Groom *groom, int flag) +{ + for (GroomBundle *bundle = groom->editgroom->bundles.first; bundle; bundle = bundle->next) + { + GroomSection *section = bundle->sections; + for (int i = 0; i < bundle->totsections; ++i, ++section) + { + section->flag = (section->flag & ~GM_SECTION_SELECT) | (flag & GM_SECTION_SELECT); + } + } +} + +static void groom_set_section_select_flags(Groom *groom, int flag) +{ + for (GroomBundle *bundle = groom->editgroom->bundles.first; bundle; bundle = bundle->next) + { + GroomSectionVertex *vertex = bundle->verts; + for (int i = 0; i < bundle->totverts; ++i, ++vertex) + { + vertex->flag = (vertex->flag & ~GM_VERTEX_SELECT) | (flag & GM_VERTEX_SELECT); + } + } +} + +bool ED_groom_select_pick(bContext *C, const int mval[2], bool extend, bool deselect, bool toggle) +{ + ViewContext vc; + ED_view3d_viewcontext_init(C, &vc); + Groom *groom = vc.obedit->data; + + struct + { + GroomBundle *bundle; + GroomSection *section; + GroomSectionVertex *vertex; + float dist; + bool select; + float mval_fl[2]; + } data = {NULL}; + + data.dist = ED_view3d_select_dist_px(); + data.select = true; + data.mval_fl[0] = mval[0]; + data.mval_fl[1] = mval[1]; + + ED_view3d_init_mats_rv3d(vc.obedit, vc.rv3d); + groom_foreachScreenVert(&vc, select_pick_findnearest_cb, &data, V3D_PROJ_TEST_CLIP_DEFAULT); + + bool found = false; + if (data.vertex) + { + if (extend) + { + data.vertex->flag |= GM_VERTEX_SELECT; + } + else if (deselect) + { + data.vertex->flag &= ~GM_VERTEX_SELECT; + } + else if (toggle) + { + data.vertex->flag ^= GM_VERTEX_SELECT; + } + else + { + /* deselect all other verts */ + groom_set_section_select_flags(groom, 0); + data.vertex->flag |= GM_VERTEX_SELECT; + } + + if (data.vertex->flag & GM_VERTEX_SELECT) + { + /* set active section */ + groom_set_region_select_flags(groom, 0); + groom_set_curve_select_flags(groom, 0); + data.section->flag |= GM_SECTION_SELECT; + data.bundle->flag |= GM_BUNDLE_SELECT; + } + + found = true; + } + else if (data.section) + { + if (extend) + { + data.section->flag |= GM_SECTION_SELECT; + } + else if (deselect) + { + data.section->flag &= ~GM_SECTION_SELECT; + } + else if (toggle) + { + data.section->flag ^= GM_SECTION_SELECT; + } + else + { + /* deselect all other sections */ + groom_set_curve_select_flags(groom, 0); + data.section->flag |= GM_SECTION_SELECT; + } + + if (data.section->flag & GM_SECTION_SELECT) + { + /* set active region */ + groom_set_region_select_flags(groom, 0); + data.bundle->flag |= GM_BUNDLE_SELECT; + } + + found = true; + } + else if (data.bundle) + { + if (extend) + { + data.bundle->flag |= GM_BUNDLE_SELECT; + } + else if (deselect) + { + data.bundle->flag &= ~GM_BUNDLE_SELECT; + } + else if (toggle) + { + data.bundle->flag ^= GM_BUNDLE_SELECT; + } + else + { + /* deselect all other regions */ + groom_set_region_select_flags(groom, 0); + data.bundle->flag |= GM_BUNDLE_SELECT; + } + + found = true; + } + + if (found) + { + WM_event_add_notifier(C, NC_GEOM | ND_SELECT, vc.obedit->data); + return true; + } + + return false; +} diff --git a/source/blender/editors/groom/groom_hair.c b/source/blender/editors/groom/groom_hair.c new file mode 100644 index 00000000000..0a179a9b20e --- /dev/null +++ b/source/blender/editors/groom/groom_hair.c @@ -0,0 +1,114 @@ +/* + * ***** 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/groom/groom_hair.c + * \ingroup groom + */ + +#include "DNA_groom_types.h" +#include "DNA_object_types.h" +#include "DNA_scene_types.h" + +#include "MEM_guardedalloc.h" + +#include "BLI_listbase.h" +#include "BLI_math.h" + +#include "BLT_translation.h" + +#include "BKE_context.h" +#include "BKE_groom.h" +#include "BKE_report.h" + +#include "DEG_depsgraph.h" + +#include "RNA_access.h" +#include "RNA_define.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "ED_object.h" +#include "ED_screen.h" +#include "ED_util.h" +#include "ED_view3d.h" +#include "ED_groom.h" + +#include "groom_intern.h" + +static int groom_object_poll(bContext *C) +{ + Object *ob = ED_object_context(C); + return ob->type == OB_GROOM; +} + +/* GROOM_OT_hair_distribute */ + +static int hair_distribute_exec(bContext *C, wmOperator *op) +{ + Object *ob = ED_object_context(C); + Groom *groom = ob->data; + int hair_count = RNA_int_get(op->ptr, "hair_count"); + int guide_curve_count = RNA_int_get(op->ptr, "guide_curve_count"); + unsigned int seed = (unsigned int)RNA_int_get(op->ptr, "seed"); + + if (!groom->scalp_object) + { + BKE_reportf(op->reports, RPT_ERROR, "Scalp object needed for creating hair follicles"); + return OPERATOR_CANCELLED; + } + + BKE_groom_hair_distribute(groom, seed, hair_count, guide_curve_count); + + WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob); + DEG_id_tag_update(&ob->id, OB_RECALC_DATA); + + return OPERATOR_FINISHED; +} + +void GROOM_OT_hair_distribute(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Distribute Hair"; + ot->description = "Distribute hair follicles and guide curves on the scalp"; + ot->idname = "GROOM_OT_hair_distribute"; + + /* api callbacks */ + ot->invoke = WM_operator_props_popup_confirm; + ot->exec = hair_distribute_exec; + ot->poll = groom_object_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + RNA_def_int(ot->srna, "hair_count", 1000, 0, INT_MAX, + "Hair Count", "Number of hairs to generate", 1, 1e6); + RNA_def_int(ot->srna, "guide_curve_count", 10, 0, INT_MAX, + "Guide Curve Count", "Number of guide curves to generate", 1, 1e4); + RNA_def_int(ot->srna, "seed", 0, 0, INT_MAX, + "Seed", "Seed value for randomized follicle distribution", 0, INT_MAX); +} diff --git a/source/blender/editors/groom/groom_intern.h b/source/blender/editors/groom/groom_intern.h new file mode 100644 index 00000000000..b398408ffd2 --- /dev/null +++ b/source/blender/editors/groom/groom_intern.h @@ -0,0 +1,48 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 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/groom/groom_intern.h + * \ingroup edgroom + */ + + +#ifndef __GROOM_INTERN_H__ +#define __GROOM_INTERN_H__ + +struct wmOperatorType; + +/* editgroom_region.c */ +void GROOM_OT_region_add(struct wmOperatorType *ot); +void GROOM_OT_region_bind(struct wmOperatorType *ot); + +/* editgroom_select.c */ +void GROOM_OT_select_all(struct wmOperatorType *ot); + +/* groom_hair.c */ +void GROOM_OT_hair_distribute(struct wmOperatorType *ot); + +#endif /* __GROOM_INTERN_H__ */ diff --git a/source/blender/editors/groom/groom_ops.c b/source/blender/editors/groom/groom_ops.c new file mode 100644 index 00000000000..f5ff8989a6b --- /dev/null +++ b/source/blender/editors/groom/groom_ops.c @@ -0,0 +1,86 @@ +/* + * ***** 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/groom/groom_ops.c + * \ingroup edgroom + */ + + +#include <stdlib.h> +#include <math.h> + +#include "DNA_groom_types.h" +#include "DNA_scene_types.h" + +#include "RNA_access.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "ED_groom.h" +#include "ED_object.h" +#include "ED_screen.h" +#include "ED_transform.h" + +#include "groom_intern.h" + +/************************* registration ****************************/ + +void ED_operatortypes_groom(void) +{ + WM_operatortype_append(GROOM_OT_region_add); + WM_operatortype_append(GROOM_OT_region_bind); + + WM_operatortype_append(GROOM_OT_select_all); + + WM_operatortype_append(GROOM_OT_hair_distribute); +} + +void ED_operatormacros_groom(void) +{ + wmOperatorType *ot; + wmOperatorTypeMacro *otmacro; + + UNUSED_VARS(ot, otmacro); +} + +void ED_keymap_groom(wmKeyConfig *keyconf) +{ + wmKeyMap *keymap; + wmKeyMapItem *kmi; + + keymap = WM_keymap_find(keyconf, "Groom", 0, 0); + keymap->poll = ED_operator_editgroom; + + kmi = WM_keymap_add_item(keymap, "GROOM_OT_select_all", AKEY, KM_PRESS, 0, 0); + RNA_enum_set(kmi->ptr, "action", SEL_TOGGLE); + kmi = WM_keymap_add_item(keymap, "GROOM_OT_select_all", IKEY, KM_PRESS, KM_CTRL, 0); + RNA_enum_set(kmi->ptr, "action", SEL_INVERT); + + ED_keymap_proportional_cycle(keyconf, keymap); + ED_keymap_proportional_editmode(keyconf, keymap, true); +} diff --git a/source/blender/editors/include/ED_groom.h b/source/blender/editors/include/ED_groom.h new file mode 100644 index 00000000000..079d2ce3681 --- /dev/null +++ b/source/blender/editors/include/ED_groom.h @@ -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) Blender Foundation + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): Lukas Toenne + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file ED_groom.h + * \ingroup editors + */ + +#ifndef __ED_GROOM_H__ +#define __ED_GROOM_H__ + +struct bContext; +struct Groom; +struct Object; +struct wmOperator; +struct wmKeyConfig; +struct EditGroom; + +/* groom_ops.c */ +void ED_operatortypes_groom(void); +void ED_operatormacros_groom(void); +void ED_keymap_groom(struct wmKeyConfig *keyconf); + +/* editgroom.c */ +void undo_push_groom(struct bContext *C, const char *name); + +void ED_groom_editgroom_load(struct Object *obedit); +void ED_groom_editgroom_make(struct Object *obedit); +void ED_groom_editgroom_free(struct Object *obedit); + +/* editgroom_select.c */ +bool ED_groom_select_check_regions(const struct EditGroom *edit); +bool ED_groom_select_check_curves(const struct EditGroom *edit); +bool ED_groom_select_check_sections(const struct EditGroom *edit); + +typedef bool (*EditGroomSelectCb)(void *userdata, bool is_selected); +void ED_groom_select_regions(struct EditGroom *edit, EditGroomSelectCb select_cb, void *userdata); +void ED_groom_select_curves(struct EditGroom *edit, EditGroomSelectCb select_cb, void *userdata); +void ED_groom_select_sections(struct EditGroom *edit, EditGroomSelectCb select_cb, void *userdata); + +bool ED_groom_select_pick(struct bContext *C, const int mval[2], bool extend, bool deselect, bool toggle); + +#endif /* __ED_GROOM_H__ */ diff --git a/source/blender/editors/include/ED_screen.h b/source/blender/editors/include/ED_screen.h index 3523e27f6ba..ea8a7c217a2 100644 --- a/source/blender/editors/include/ED_screen.h +++ b/source/blender/editors/include/ED_screen.h @@ -254,6 +254,7 @@ int ED_operator_editsurfcurve_region_view3d(struct bContext *C); int ED_operator_editfont(struct bContext *C); int ED_operator_editlattice(struct bContext *C); int ED_operator_editmball(struct bContext *C); +int ED_operator_editgroom(struct bContext *C); int ED_operator_uvedit(struct bContext *C); int ED_operator_uvedit_space_image(struct bContext *C); int ED_operator_uvmap(struct bContext *C); diff --git a/source/blender/editors/include/ED_view3d.h b/source/blender/editors/include/ED_view3d.h index 7f18c10f970..1d4fc8891f5 100644 --- a/source/blender/editors/include/ED_view3d.h +++ b/source/blender/editors/include/ED_view3d.h @@ -44,6 +44,9 @@ struct Camera; struct Depsgraph; struct EditBone; struct EvaluationContext; +struct GroomBundle; +struct GroomSection; +struct GroomSectionVertex; struct ImBuf; struct MVert; struct Main; @@ -197,6 +200,15 @@ void pose_foreachScreenBone( void (*func)(void *userData, struct bPoseChannel *pchan, const float screen_co_a[2], const float screen_co_b[2]), void *userData, const eV3DProjTest clip_flag); +void groom_foreachScreenVert( + struct ViewContext *vc, + void (*func)( + void *userData, + struct GroomBundle *bundle, + struct GroomSection *section, + struct GroomSectionVertex *vert, + const float screen_co[2]), + void *userData, const eV3DProjTest clip_flag); /* *** end iterators *** */ diff --git a/source/blender/editors/include/UI_icons.h b/source/blender/editors/include/UI_icons.h index 923038a7490..b6714e80e80 100644 --- a/source/blender/editors/include/UI_icons.h +++ b/source/blender/editors/include/UI_icons.h @@ -262,8 +262,8 @@ DEF_ICON(GROUP_BONE) DEF_ICON(GROUP_VERTEX) DEF_ICON(GROUP_VCOL) DEF_ICON(GROUP_UVS) +DEF_ICON(GROOM_DATA) #ifndef DEF_ICON_BLANK_SKIP - DEF_ICON(BLANK089) DEF_ICON(BLANK090) #endif DEF_ICON(RNA) @@ -314,6 +314,7 @@ DEF_ICON(OUTLINER_OB_SPEAKER) DEF_ICON(OUTLINER_OB_FORCE_FIELD) DEF_ICON(OUTLINER_OB_GROUP_INSTANCE) DEF_ICON(OUTLINER_OB_GREASEPENCIL) +DEF_ICON(OUTLINER_OB_GROOM) #ifndef DEF_ICON_BLANK_SKIP DEF_ICON(BLANK123) DEF_ICON(BLANK124) diff --git a/source/blender/editors/object/object_add.c b/source/blender/editors/object/object_add.c index d3f6a42c3c1..3a12d56d9c2 100644 --- a/source/blender/editors/object/object_add.c +++ b/source/blender/editors/object/object_add.c @@ -36,6 +36,7 @@ #include "DNA_anim_types.h" #include "DNA_camera_types.h" #include "DNA_curve_types.h" +#include "DNA_groom_types.h" #include "DNA_group_types.h" #include "DNA_lamp_types.h" #include "DNA_key_types.h" @@ -72,6 +73,7 @@ #include "BKE_displist.h" #include "BKE_effect.h" #include "BKE_font.h" +#include "BKE_groom.h" #include "BKE_group.h" #include "BKE_lamp.h" #include "BKE_lattice.h" @@ -776,6 +778,41 @@ void OBJECT_OT_metaball_add(wmOperatorType *ot) ED_object_add_generic_props(ot, true); } +/********************* Add Groom Operator ********************/ + +static int object_groom_add_exec(bContext *C, wmOperator *op) +{ + bool enter_editmode; + unsigned int layer; + float loc[3], rot[3]; + if (!ED_object_add_generic_get_opts(C, op, 'Z', loc, rot, &enter_editmode, &layer, NULL)) + return OPERATOR_CANCELLED; + + Object *ob = ED_object_add_type(C, OB_GROOM, NULL, loc, rot, false, layer); + + Groom *groom = ob->data; + UNUSED_VARS(groom); + + return OPERATOR_FINISHED; +} + +void OBJECT_OT_groom_add(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Add Groom"; + ot->description = "Add a groom object to the scene"; + ot->idname = "OBJECT_OT_groom_add"; + + /* api callbacks */ + ot->exec = object_groom_add_exec; + ot->poll = ED_operator_objectmode; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + ED_object_add_generic_props(ot, true); +} + /********************* Add Text Operator ********************/ static int object_add_text_exec(bContext *C, wmOperator *op) diff --git a/source/blender/editors/object/object_edit.c b/source/blender/editors/object/object_edit.c index f4066360805..b4fa27c3fd9 100644 --- a/source/blender/editors/object/object_edit.c +++ b/source/blender/editors/object/object_edit.c @@ -95,6 +95,7 @@ #include "ED_armature.h" #include "ED_curve.h" +#include "ED_groom.h" #include "ED_mesh.h" #include "ED_mball.h" #include "ED_lattice.h" @@ -258,6 +259,10 @@ static bool ED_object_editmode_load_ex(Main *bmain, Object *obedit, const bool f ED_mball_editmball_free(obedit); } } + else if (obedit->type == OB_GROOM) { + ED_groom_editgroom_load(obedit); + if (freedata) ED_groom_editgroom_free(obedit); + } return true; } @@ -435,6 +440,12 @@ void ED_object_editmode_enter(bContext *C, int flag) WM_event_add_notifier(C, NC_SCENE | ND_MODE | NS_EDITMODE_CURVE, scene); } + else if (ob->type == OB_GROOM) { + ok = 1; + ED_groom_editgroom_make(ob); + + WM_event_add_notifier(C, NC_SCENE | ND_MODE | NS_EDITMODE_GROOM, scene); + } if (ok) { DEG_id_tag_update(&ob->id, OB_RECALC_DATA); diff --git a/source/blender/editors/object/object_intern.h b/source/blender/editors/object/object_intern.h index 75117ccdce3..944912ad6b6 100644 --- a/source/blender/editors/object/object_intern.h +++ b/source/blender/editors/object/object_intern.h @@ -123,6 +123,7 @@ void OBJECT_OT_lamp_add(struct wmOperatorType *ot); void OBJECT_OT_effector_add(struct wmOperatorType *ot); void OBJECT_OT_camera_add(struct wmOperatorType *ot); void OBJECT_OT_speaker_add(struct wmOperatorType *ot); +void OBJECT_OT_groom_add(struct wmOperatorType *ot); void OBJECT_OT_group_instance_add(struct wmOperatorType *ot); void OBJECT_OT_duplicates_make_real(struct wmOperatorType *ot); @@ -178,6 +179,7 @@ void OBJECT_OT_skin_radii_equalize(struct wmOperatorType *ot); void OBJECT_OT_skin_armature_create(struct wmOperatorType *ot); void OBJECT_OT_laplaciandeform_bind(struct wmOperatorType *ot); void OBJECT_OT_surfacedeform_bind(struct wmOperatorType *ot); +void OBJECT_OT_fur_generate_follicles(struct wmOperatorType *ot); /* object_constraint.c */ void OBJECT_OT_constraint_add(struct wmOperatorType *ot); diff --git a/source/blender/editors/object/object_modes.c b/source/blender/editors/object/object_modes.c index f074a56fb86..9e6e6947320 100644 --- a/source/blender/editors/object/object_modes.c +++ b/source/blender/editors/object/object_modes.c @@ -99,6 +99,7 @@ bool ED_object_mode_compat_test(const Object *ob, eObjectMode mode) case OB_SURF: case OB_FONT: case OB_MBALL: + case OB_GROOM: if (mode & (OB_MODE_EDIT)) return true; break; diff --git a/source/blender/editors/object/object_modifier.c b/source/blender/editors/object/object_modifier.c index 04243660440..5458e4c173f 100644 --- a/source/blender/editors/object/object_modifier.c +++ b/source/blender/editors/object/object_modifier.c @@ -58,11 +58,13 @@ #include "BKE_DerivedMesh.h" #include "BKE_effect.h" #include "BKE_global.h" +#include "BKE_hair.h" #include "BKE_key.h" #include "BKE_lattice.h" #include "BKE_main.h" #include "BKE_mesh.h" #include "BKE_mesh_mapping.h" +#include "BKE_mesh_sample.h" #include "BKE_modifier.h" #include "BKE_multires.h" #include "BKE_report.h" @@ -2369,3 +2371,122 @@ void OBJECT_OT_surfacedeform_bind(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; edit_modifier_properties(ot); } + +/************************ Fur follicle generate operator *********************/ + +static void fur_create_guide_curves(struct HairSystem *hsys, unsigned int seed, DerivedMesh *scalp, int count) +{ + float area = BKE_hair_calc_surface_area(scalp); + float density = BKE_hair_calc_density_from_count(area, count); + float min_distance = BKE_hair_calc_min_distance_from_density(density); + MeshSampleGenerator *gen = BKE_mesh_sample_gen_surface_poissondisk(seed, min_distance, count, NULL, NULL); + + BKE_mesh_sample_generator_bind(gen, scalp); + + { + MeshSample *buffer = MEM_mallocN(sizeof(MeshSample) * count, "mesh sample buffer"); + int totguides = BKE_mesh_sample_generate_batch(gen, buffer, count); + + { + BKE_hair_guide_curves_begin(hsys, totguides); + + MeshSample *sample = buffer; + for (int i = 0; i < totguides; ++i, ++sample) + { + BKE_hair_set_guide_curve(hsys, i, sample, 2); + } + + BKE_hair_guide_curves_end(hsys); + } + + { + int vertstart = 0; + MeshSample *sample = buffer; + for (int i = 0; i < totguides; ++i, ++sample) + { + float co[3], nor[3], tang[3]; + BKE_mesh_sample_eval(scalp, sample, co, nor, tang); + BKE_hair_set_guide_vertex(hsys, vertstart, 0, co); + + madd_v3_v3fl(co, nor, 0.1f); + BKE_hair_set_guide_vertex(hsys, vertstart + 1, 0, co); + + vertstart += 2; + } + } + + MEM_freeN(buffer); + } + + BKE_mesh_sample_free_generator(gen); + + BKE_hair_bind_follicles(hsys, scalp); +} + +static int fur_generate_follicles_poll(bContext *C) +{ + return edit_modifier_poll_generic(C, &RNA_FurModifier, 0); +} + +static int fur_generate_follicles_exec(bContext *C, wmOperator *op) +{ + Object *ob = ED_object_active_context(C); + FurModifierData *fmd = (FurModifierData *)edit_modifier_property_get(op, ob, eModifierType_Fur); + + if (!fmd) + return OPERATOR_CANCELLED; + + BLI_assert(fmd->hair_system != NULL); + + struct Scene *scene = CTX_data_scene(C); + EvaluationContext eval_ctx; + CTX_data_eval_ctx(C, &eval_ctx); + + CustomDataMask datamask = CD_MASK_BAREMESH; + DerivedMesh *dm = mesh_get_derived_final(&eval_ctx, scene, ob, datamask); + + BKE_hair_generate_follicles( + fmd->hair_system, + dm, + (unsigned int)fmd->follicle_seed, + fmd->follicle_count); + + { + unsigned int guides_seed = fmd->follicle_seed ^ 0xFFFF; + fur_create_guide_curves( + fmd->hair_system, + guides_seed, + dm, + fmd->guides_count); + } + + DEG_id_tag_update(&ob->id, OB_RECALC_DATA); + WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob); + + return OPERATOR_FINISHED; +} + +static int fur_generate_follicles_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) +{ + if (edit_modifier_invoke_properties(C, op)) + return fur_generate_follicles_exec(C, op); + else + return OPERATOR_CANCELLED; +} + +void OBJECT_OT_fur_generate_follicles(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Fur Follicles Generate"; + ot->description = "Generate hair follicles for a fur modifier"; + ot->idname = "OBJECT_OT_fur_generate_follicles"; + + /* api callbacks */ + ot->poll = fur_generate_follicles_poll; + ot->invoke = fur_generate_follicles_invoke; + ot->exec = fur_generate_follicles_exec; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; + edit_modifier_properties(ot); +} diff --git a/source/blender/editors/object/object_ops.c b/source/blender/editors/object/object_ops.c index 66dc17fe77d..a35a54001e8 100644 --- a/source/blender/editors/object/object_ops.c +++ b/source/blender/editors/object/object_ops.c @@ -117,6 +117,7 @@ void ED_operatortypes_object(void) WM_operatortype_append(OBJECT_OT_lamp_add); WM_operatortype_append(OBJECT_OT_camera_add); WM_operatortype_append(OBJECT_OT_speaker_add); + WM_operatortype_append(OBJECT_OT_groom_add); WM_operatortype_append(OBJECT_OT_add); WM_operatortype_append(OBJECT_OT_add_named); WM_operatortype_append(OBJECT_OT_effector_add); @@ -257,6 +258,7 @@ void ED_operatortypes_object(void) WM_operatortype_append(OBJECT_OT_data_transfer); WM_operatortype_append(OBJECT_OT_datalayout_transfer); WM_operatortype_append(OBJECT_OT_surfacedeform_bind); + WM_operatortype_append(OBJECT_OT_fur_generate_follicles); } void ED_operatormacros_object(void) diff --git a/source/blender/editors/object/object_relations.c b/source/blender/editors/object/object_relations.c index 3443a268ef2..fee39c8f0fb 100644 --- a/source/blender/editors/object/object_relations.c +++ b/source/blender/editors/object/object_relations.c @@ -70,6 +70,7 @@ #include "BKE_DerivedMesh.h" #include "BKE_displist.h" #include "BKE_global.h" +#include "BKE_groom.h" #include "BKE_group.h" #include "BKE_fcurve.h" #include "BKE_idprop.h" @@ -1837,6 +1838,9 @@ static void single_obdata_users(Main *bmain, Scene *scene, ViewLayer *view_layer case OB_SPEAKER: ob->data = ID_NEW_SET(ob->data, BKE_speaker_copy(bmain, ob->data)); break; + case OB_GROOM: + ob->data = ID_NEW_SET(ob->data, BKE_groom_copy(bmain, ob->data)); + break; case OB_LIGHTPROBE: ob->data = ID_NEW_SET(ob->data, BKE_lightprobe_copy(bmain, ob->data)); break; diff --git a/source/blender/editors/screen/screen_ops.c b/source/blender/editors/screen/screen_ops.c index 488f240ba48..898010858b9 100644 --- a/source/blender/editors/screen/screen_ops.c +++ b/source/blender/editors/screen/screen_ops.c @@ -44,6 +44,7 @@ #include "DNA_object_types.h" #include "DNA_curve_types.h" #include "DNA_gpencil_types.h" +#include "DNA_groom_types.h" #include "DNA_scene_types.h" #include "DNA_meta_types.h" #include "DNA_mask_types.h" @@ -530,6 +531,14 @@ int ED_operator_editmball(bContext *C) return 0; } +int ED_operator_editgroom(bContext *C) +{ + Object *obedit = CTX_data_edit_object(C); + if (obedit && obedit->type == OB_GROOM) + return NULL != ((Groom *)obedit->data)->editgroom; + return 0; +} + int ED_operator_mask(bContext *C) { ScrArea *sa = CTX_wm_area(C); diff --git a/source/blender/editors/space_api/spacetypes.c b/source/blender/editors/space_api/spacetypes.c index 4455b117370..2f85b915813 100644 --- a/source/blender/editors/space_api/spacetypes.c +++ b/source/blender/editors/space_api/spacetypes.c @@ -48,6 +48,7 @@ #include "ED_curve.h" #include "ED_fileselect.h" #include "ED_gpencil.h" +#include "ED_groom.h" #include "ED_markers.h" #include "ED_mesh.h" #include "ED_node.h" @@ -124,7 +125,8 @@ void ED_spacetypes_init(void) ED_operatortypes_logic(); ED_operatortypes_mask(); ED_operatortypes_io(); - + ED_operatortypes_groom(); + ED_operatortypes_view2d(); ED_operatortypes_ui(); @@ -175,6 +177,7 @@ void ED_spacemacros_init(void) ED_operatormacros_sequencer(); ED_operatormacros_paint(); ED_operatormacros_gpencil(); + ED_operatormacros_groom(); /* register dropboxes (can use macros) */ spacetypes = BKE_spacetypes_list(); @@ -208,6 +211,7 @@ void ED_spacetypes_keymap(wmKeyConfig *keyconf) ED_keymap_paint(keyconf); ED_keymap_mask(keyconf); ED_keymap_marker(keyconf); + ED_keymap_groom(keyconf); ED_keymap_view2d(keyconf); ED_keymap_ui(keyconf); diff --git a/source/blender/editors/space_buttons/buttons_context.c b/source/blender/editors/space_buttons/buttons_context.c index 8866c6b6c40..4b5f01897c1 100644 --- a/source/blender/editors/space_buttons/buttons_context.c +++ b/source/blender/editors/space_buttons/buttons_context.c @@ -249,6 +249,7 @@ static int buttons_context_path_data(ButsContextPath *path, int type) else if (RNA_struct_is_a(ptr->type, &RNA_Lamp) && (type == -1 || type == OB_LAMP)) return 1; else if (RNA_struct_is_a(ptr->type, &RNA_Speaker) && (type == -1 || type == OB_SPEAKER)) return 1; else if (RNA_struct_is_a(ptr->type, &RNA_LightProbe) && (type == -1 || type == OB_LIGHTPROBE)) return 1; + else if (RNA_struct_is_a(ptr->type, &RNA_Groom) && (type == -1 || type == OB_GROOM)) return 1; /* try to get an object in the path, no pinning supported here */ else if (buttons_context_path_object(path)) { ob = path->ptr[path->len - 1].data; @@ -809,7 +810,7 @@ const char *buttons_context_dir[] = { "texture", "texture_user", "texture_user_property", "bone", "edit_bone", "pose_bone", "particle_system", "particle_system_editable", "particle_settings", "cloth", "soft_body", "fluid", "smoke", "collision", "brush", "dynamic_paint", - "line_style", "collection", "workspace", NULL + "line_style", "collection", "workspace", "groom", NULL }; int buttons_context(const bContext *C, const char *member, bContextDataResult *result) @@ -1141,6 +1142,10 @@ int buttons_context(const bContext *C, const char *member, bContextDataResult *r set_pointer_type(path, result, &RNA_LayerCollection); return 1; } + else if (CTX_data_equals(member, "groom")) { + set_pointer_type(path, result, &RNA_Groom); + return 1; + } else { return 0; /* not found */ } diff --git a/source/blender/editors/space_outliner/outliner_draw.c b/source/blender/editors/space_outliner/outliner_draw.c index 15b411280a5..0f9723dea4b 100644 --- a/source/blender/editors/space_outliner/outliner_draw.c +++ b/source/blender/editors/space_outliner/outliner_draw.c @@ -1046,6 +1046,9 @@ static void tselem_draw_icon(uiBlock *block, int xmax, float x, float y, TreeSto case eModifierType_NormalEdit: ICON_DRAW(ICON_MOD_NORMALEDIT); break; + case eModifierType_Fur: + ICON_DRAW(ICON_STRANDS); + break; /* Default */ case eModifierType_None: case eModifierType_ShapeKey: @@ -1138,6 +1141,8 @@ static void tselem_draw_icon(uiBlock *block, int xmax, float x, float y, TreeSto tselem_draw_icon_uibut(&arg, ICON_OUTLINER_OB_CURVE); break; case OB_MBALL: tselem_draw_icon_uibut(&arg, ICON_OUTLINER_OB_META); break; + case OB_GROOM: + tselem_draw_icon_uibut(&arg, ICON_OUTLINER_OB_CURVE); break; case OB_LATTICE: tselem_draw_icon_uibut(&arg, ICON_OUTLINER_OB_LATTICE); break; case OB_ARMATURE: diff --git a/source/blender/editors/space_outliner/outliner_intern.h b/source/blender/editors/space_outliner/outliner_intern.h index bedd2f09f3b..13567c2a5a2 100644 --- a/source/blender/editors/space_outliner/outliner_intern.h +++ b/source/blender/editors/space_outliner/outliner_intern.h @@ -115,7 +115,7 @@ typedef struct TreeElement { #define TREESTORE_ID_TYPE(_id) \ (ELEM(GS((_id)->name), ID_SCE, ID_LI, ID_OB, ID_ME, ID_CU, ID_MB, ID_NT, ID_MA, ID_TE, ID_IM, ID_LT, ID_LA, ID_CA) || \ ELEM(GS((_id)->name), ID_KE, ID_WO, ID_SPK, ID_GR, ID_AR, ID_AC, ID_BR, ID_PA, ID_GD, ID_LS, ID_LP) || \ - ELEM(GS((_id)->name), ID_SCR, ID_WM, ID_TXT, ID_VF, ID_SO, ID_CF, ID_PAL, ID_WS)) /* Only in 'blendfile' mode ... :/ */ + ELEM(GS((_id)->name), ID_SCR, ID_WM, ID_TXT, ID_VF, ID_SO, ID_CF, ID_PAL, ID_WS, ID_GM)) /* Only in 'blendfile' mode ... :/ */ /* TreeElement->flag */ enum { diff --git a/source/blender/editors/space_outliner/outliner_tree.c b/source/blender/editors/space_outliner/outliner_tree.c index eff726868c7..9f44b5bfc02 100644 --- a/source/blender/editors/space_outliner/outliner_tree.c +++ b/source/blender/editors/space_outliner/outliner_tree.c @@ -40,6 +40,7 @@ #include "DNA_camera_types.h" #include "DNA_cachefile_types.h" #include "DNA_gpencil_types.h" +#include "DNA_groom_types.h" #include "DNA_group_types.h" #include "DNA_key_types.h" #include "DNA_lamp_types.h" @@ -747,6 +748,14 @@ static void outliner_add_id_contents(SpaceOops *soops, TreeElement *te, TreeStor outliner_add_element(soops, &te->subtree, mb->mat[a], te, 0, a); break; } + case ID_GM: + { + Groom *groom = (Groom *)id; + + if (outliner_animdata_test(groom->adt)) + outliner_add_element(soops, &te->subtree, groom, te, TSE_ANIM_DATA, 0); + break; + } case ID_MA: { Material *ma = (Material *)id; diff --git a/source/blender/editors/space_view3d/space_view3d.c b/source/blender/editors/space_view3d/space_view3d.c index 9fb602c81d6..74087e6f27b 100644 --- a/source/blender/editors/space_view3d/space_view3d.c +++ b/source/blender/editors/space_view3d/space_view3d.c @@ -532,6 +532,9 @@ static void view3d_main_region_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, "Groom", 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); diff --git a/source/blender/editors/space_view3d/view3d_iterators.c b/source/blender/editors/space_view3d/view3d_iterators.c index 4f80270e1e7..d3bee637fd7 100644 --- a/source/blender/editors/space_view3d/view3d_iterators.c +++ b/source/blender/editors/space_view3d/view3d_iterators.c @@ -31,9 +31,11 @@ #include "DNA_armature_types.h" #include "DNA_object_types.h" #include "DNA_scene_types.h" +#include "DNA_groom_types.h" #include "BLI_utildefines.h" #include "BLI_rect.h" +#include "BLI_math.h" #include "BKE_armature.h" #include "BKE_curve.h" @@ -41,6 +43,7 @@ #include "BKE_displist.h" #include "BKE_editmesh.h" #include "BKE_context.h" +#include "BKE_groom.h" #include "DEG_depsgraph.h" @@ -399,6 +402,75 @@ void lattice_foreachScreenVert( /* ------------------------------------------------------------------------ */ +void groom_foreachScreenVert( + ViewContext *vc, + void (*func)( + void *userData, + GroomBundle *bundle, + GroomSection *section, + GroomSectionVertex *vert, + const float screen_co[2]), + void *userData, const eV3DProjTest clip_flag) +{ + GroomEditSettings *edit_settings = &vc->scene->toolsettings->groom_edit_settings; + Object *obedit = vc->obedit; + Groom *groom = obedit->data; + ListBase *bundles = &groom->editgroom->bundles; + + ED_view3d_check_mats_rv3d(vc->rv3d); + + if (clip_flag & V3D_PROJ_TEST_CLIP_BB) { + ED_view3d_clipping_local(vc->rv3d, obedit->obmat); /* for local clipping lookups */ + } + + switch (edit_settings->mode) + { + case GM_EDIT_MODE_REGIONS: + // TODO + break; + + case GM_EDIT_MODE_CURVES: + for (GroomBundle *bundle = bundles->first; bundle; bundle = bundle->next) + { + GroomSection *section = bundle->sections; + for (int i = 0; i < bundle->totsections; ++i, ++section) + { + float screen_co[2]; + if (ED_view3d_project_float_object(vc->ar, section->center, screen_co, clip_flag) == V3D_PROJ_RET_OK) + { + func(userData, bundle, section, NULL, screen_co); + } + } + } + break; + + case GM_EDIT_MODE_SECTIONS: + for (GroomBundle *bundle = bundles->first; bundle; bundle = bundle->next) + { + GroomSectionVertex *vertex = bundle->verts; + GroomSection *section = bundle->sections; + for (int i = 0; i < bundle->totsections; ++i, ++section) + { + for (int j = 0; j < bundle->numshapeverts; ++j, ++vertex) + { + float co[3] = {vertex->co[0], vertex->co[1], 0.0f}; + mul_m3_v3(section->mat, co); + add_v3_v3(co, section->center); + + float screen_co[2]; + if (ED_view3d_project_float_object(vc->ar, co, screen_co, clip_flag) == V3D_PROJ_RET_OK) + { + func(userData, bundle, section, vertex, screen_co); + } + } + } + } + break; + } +} + +/* ------------------------------------------------------------------------ */ + /* ED_view3d_init_mats_rv3d must be called first */ void armature_foreachScreenBone( struct ViewContext *vc, diff --git a/source/blender/editors/space_view3d/view3d_select.c b/source/blender/editors/space_view3d/view3d_select.c index be8e63dffe6..21219bced3c 100644 --- a/source/blender/editors/space_view3d/view3d_select.c +++ b/source/blender/editors/space_view3d/view3d_select.c @@ -38,6 +38,7 @@ #include "DNA_action_types.h" #include "DNA_armature_types.h" #include "DNA_curve_types.h" +#include "DNA_groom_types.h" #include "DNA_meta_types.h" #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" @@ -63,6 +64,7 @@ #include "BKE_armature.h" #include "BKE_context.h" #include "BKE_curve.h" +#include "BKE_groom.h" #include "BKE_layer.h" #include "BKE_mball.h" #include "BKE_mesh.h" @@ -90,6 +92,7 @@ #include "ED_screen.h" #include "ED_sculpt.h" #include "ED_mball.h" +#include "ED_groom.h" #include "UI_interface.h" @@ -594,6 +597,49 @@ static void do_lasso_select_lattice(ViewContext *vc, const int mcords[][2], shor lattice_foreachScreenVert(vc, do_lasso_select_lattice__doSelect, &data, V3D_PROJ_TEST_CLIP_DEFAULT); } +static void do_lasso_select_groom__doSelect( + void *userData, + GroomBundle *bundle, + GroomSection *section, + GroomSectionVertex *vertex, + const float screen_co[2]) +{ + LassoSelectUserData *data = userData; + + if (BLI_rctf_isect_pt_v(data->rect_fl, screen_co) && + BLI_lasso_is_point_inside(data->mcords, data->moves, screen_co[0], screen_co[1], IS_CLIPPED)) + { + if (vertex) + { + vertex->flag = data->select ? (vertex->flag | GM_VERTEX_SELECT) : (vertex->flag & ~GM_VERTEX_SELECT); + } + else if (section) + { + section->flag = data->select ? (section->flag | GM_SECTION_SELECT) : (section->flag & ~GM_SECTION_SELECT); + } + else if (bundle) + { + bundle->flag = data->select ? (bundle->flag | GM_BUNDLE_SELECT) : (bundle->flag & ~GM_BUNDLE_SELECT); + } + } +} + +static void do_lasso_select_groom(ViewContext *vc, const int mcords[][2], short moves, bool extend, bool select) +{ + LassoSelectUserData data; + rcti rect; + + BLI_lasso_boundbox(&rect, mcords, moves); + + view3d_userdata_lassoselect_init(&data, vc, &rect, mcords, moves, select); + + if (extend == false && select) + ED_lattice_flags_set(vc->obedit, 0); + + ED_view3d_init_mats_rv3d(vc->obedit, vc->rv3d); /* for foreach's screen/vert projection */ + groom_foreachScreenVert(vc, do_lasso_select_groom__doSelect, &data, V3D_PROJ_TEST_CLIP_DEFAULT); +} + static void do_lasso_select_armature__doSelectBone(void *userData, struct EditBone *ebone, const float screen_co_a[2], const float screen_co_b[2]) { LassoSelectUserData *data = userData; @@ -851,6 +897,9 @@ static void view3d_lasso_select( case OB_MBALL: do_lasso_select_meta(vc, mcords, moves, extend, select); break; + case OB_GROOM: + do_lasso_select_groom(vc, mcords, moves, extend, select); + break; default: assert(!"lasso select on incorrect object type"); break; @@ -1774,6 +1823,47 @@ static int do_lattice_box_select(ViewContext *vc, rcti *rect, bool select, bool return OPERATOR_FINISHED; } +static void do_groom_box_select__doSelect( + void *userData, + GroomBundle *bundle, + GroomSection *section, + GroomSectionVertex *vertex, + const float screen_co[2]) +{ + BoxSelectUserData *data = userData; + + if (BLI_rctf_isect_pt_v(data->rect_fl, screen_co)) + { + if (vertex) + { + vertex->flag = data->select ? (vertex->flag | GM_VERTEX_SELECT) : (vertex->flag & ~GM_VERTEX_SELECT); + } + else if (section) + { + section->flag = data->select ? (section->flag | GM_SECTION_SELECT) : (section->flag & ~GM_SECTION_SELECT); + } + else if (bundle) + { + bundle->flag = data->select ? (bundle->flag | GM_BUNDLE_SELECT) : (bundle->flag & ~GM_BUNDLE_SELECT); + } + } +} + +static int do_groom_box_select(ViewContext *vc, rcti *rect, bool select, bool extend) +{ + BoxSelectUserData data; + + view3d_userdata_boxselect_init(&data, vc, rect, select); + + if (extend == false && select) + ED_lattice_flags_set(vc->obedit, 0); + + ED_view3d_init_mats_rv3d(vc->obedit, vc->rv3d); /* for foreach's screen/vert projection */ + groom_foreachScreenVert(vc, do_groom_box_select__doSelect, &data, V3D_PROJ_TEST_CLIP_DEFAULT); + + return OPERATOR_FINISHED; +} + static void do_mesh_box_select__doSelectVert(void *userData, BMVert *eve, const float screen_co[2], int UNUSED(index)) { BoxSelectUserData *data = userData; @@ -2158,6 +2248,12 @@ static int view3d_borderselect_exec(bContext *C, wmOperator *op) WM_event_add_notifier(C, NC_GEOM | ND_SELECT, vc.obedit->data); } break; + case OB_GROOM: + ret = do_groom_box_select(&vc, &rect, select, extend); + if (ret & OPERATOR_FINISHED) { + WM_event_add_notifier(C, NC_GEOM | ND_SELECT, vc.obedit->data); + } + break; default: assert(!"border select on incorrect object type"); break; @@ -2304,6 +2400,8 @@ static int view3d_select_exec(bContext *C, wmOperator *op) retval = ED_mball_select_pick(C, location, extend, deselect, toggle); else if (obedit->type == OB_FONT) retval = ED_curve_editfont_select_pick(C, location, extend, deselect, toggle); + else if (obedit->type == OB_GROOM) + retval = ED_groom_select_pick(C, location, extend, deselect, toggle); } else if (obact && obact->mode & OB_MODE_PARTICLE_EDIT) @@ -2579,6 +2677,43 @@ static void lattice_circle_select(ViewContext *vc, const bool select, const int } +static void groom_circle_select__doSelect( + void *userData, + GroomBundle *bundle, + GroomSection *section, + GroomSectionVertex *vertex, + const float screen_co[2]) +{ + CircleSelectUserData *data = userData; + + if (len_squared_v2v2(data->mval_fl, screen_co) <= data->radius_squared) + { + if (vertex) + { + vertex->flag = data->select ? (vertex->flag | GM_VERTEX_SELECT) : (vertex->flag & ~GM_VERTEX_SELECT); + } + else if (section) + { + section->flag = data->select ? (section->flag | GM_SECTION_SELECT) : (section->flag & ~GM_SECTION_SELECT); + } + else if (bundle) + { + bundle->flag = data->select ? (bundle->flag | GM_BUNDLE_SELECT) : (bundle->flag & ~GM_BUNDLE_SELECT); + } + } +} + +static void groom_circle_select(ViewContext *vc, const bool select, const int mval[2], float rad) +{ + CircleSelectUserData data; + + view3d_userdata_circleselect_init(&data, vc, select, mval, rad); + + ED_view3d_init_mats_rv3d(vc->obedit, vc->rv3d); /* for foreach's screen/vert projection */ + groom_foreachScreenVert(vc, groom_circle_select__doSelect, &data, V3D_PROJ_TEST_CLIP_DEFAULT); +} + + /* NOTE: pose-bone case is copied from editbone case... */ static bool pchan_circle_doSelectJoint(void *userData, bPoseChannel *pchan, const float screen_co[2]) { @@ -2785,6 +2920,9 @@ static void obedit_circle_select( case OB_MBALL: mball_circle_select(vc, select, mval, rad); break; + case OB_GROOM: + groom_circle_select(vc, select, mval, rad); + break; default: return; } diff --git a/source/blender/editors/transform/transform.h b/source/blender/editors/transform/transform.h index fe05207e645..40f6b64b055 100644 --- a/source/blender/editors/transform/transform.h +++ b/source/blender/editors/transform/transform.h @@ -648,6 +648,7 @@ void flushTransSeq(TransInfo *t); void flushTransTracking(TransInfo *t); void flushTransMasking(TransInfo *t); void flushTransPaintCurve(TransInfo *t); +void flushTransGroom(TransInfo *t); void restoreBones(TransInfo *t); /*********************** transform_manipulator.c ********** */ diff --git a/source/blender/editors/transform/transform_conversions.c b/source/blender/editors/transform/transform_conversions.c index 2d7ff1eb523..3a1c60e4a65 100644 --- a/source/blender/editors/transform/transform_conversions.c +++ b/source/blender/editors/transform/transform_conversions.c @@ -36,6 +36,7 @@ #include "DNA_anim_types.h" #include "DNA_brush_types.h" #include "DNA_armature_types.h" +#include "DNA_groom_types.h" #include "DNA_lattice_types.h" #include "DNA_mesh_types.h" #include "DNA_meta_types.h" @@ -71,6 +72,7 @@ #include "BKE_fcurve.h" #include "BKE_global.h" #include "BKE_gpencil.h" +#include "BKE_groom.h" #include "BKE_layer.h" #include "BKE_key.h" #include "BKE_main.h" @@ -1886,6 +1888,235 @@ static void createTransLatticeVerts(TransInfo *t) } } +/* ********************* groom *************** */ + +static int groom_trans_count_regions(EditGroom *edit, bool is_prop_edit) +{ + // TODO + UNUSED_VARS(edit, is_prop_edit); + return 0; +} + +static void groom_transdata_init_regions( + EditGroom *edit, + bool is_prop_edit, + float obmat[4][4], + TransData *tdata) +{ + // TODO + UNUSED_VARS(edit, is_prop_edit, obmat, tdata); +} + +static int groom_trans_count_curves(EditGroom *edit, bool is_prop_edit) +{ + int count = 0, countsel = 0; + for (GroomBundle *bundle = edit->bundles.first; bundle; bundle = bundle->next) + { + GroomSection *section = bundle->sections; + for (int i = 0; i < bundle->totsections; ++i, ++section) + { + ++count; + if (section->flag & GM_SECTION_SELECT) + { + ++countsel; + } + } + } + + /* note: in prop mode we need at least 1 selected */ + if (countsel > 0) + { + return is_prop_edit ? count : countsel; + } + return 0; +} + +static void groom_transdata_init_curves( + EditGroom *edit, + bool is_prop_edit, + float obmat[4][4], + TransData *tdata) +{ + float mtx[3][3], smtx[3][3]; + copy_m3_m4(mtx, obmat); + pseudoinverse_m3_m3(smtx, mtx, PSEUDOINVERSE_EPSILON); + + TransData *td = tdata; + for (GroomBundle *bundle = edit->bundles.first; bundle; bundle = bundle->next) + { + GroomSection *section = bundle->sections; + for (int i = 0; i < bundle->totsections; ++i, ++section) + { + if (is_prop_edit || (section->flag & GM_SECTION_SELECT)) + { + copy_v3_v3(td->iloc, section->center); + copy_v3_v3(td->center, section->center); + td->loc = section->center; + + if (section->flag & GM_SECTION_SELECT) + { + td->flag = TD_SELECTED; + } + else + { + td->flag = 0; + } + + copy_m3_m3(td->smtx, smtx); + copy_m3_m3(td->mtx, mtx); + + td->ext = NULL; + td->val = NULL; + + ++td; + } + } + } +} + +static int groom_trans_count_verts(EditGroom *edit, bool is_prop_edit) +{ + int count = 0, countsel = 0; + for (GroomBundle *bundle = edit->bundles.first; bundle; bundle = bundle->next) + { + GroomSectionVertex *vertex = bundle->verts; + for (int i = 0; i < bundle->totverts; ++i, ++vertex) + { + ++count; + if (vertex->flag & GM_VERTEX_SELECT) + { + ++countsel; + } + } + } + + /* note: in prop mode we need at least 1 selected */ + if (countsel > 0) + { + return is_prop_edit ? count : countsel; + } + return 0; +} + +static void groom_transdata_init_verts( + EditGroom *edit, + bool is_prop_edit, + float obmat[4][4], + TransData *tdata, + TransData2D *tdata2d) +{ + float obmat3[3][3]; + copy_m3_m4(obmat3, obmat); + + TransData *td = tdata; + TransData2D *td2d = tdata2d; + for (GroomBundle *bundle = edit->bundles.first; bundle; bundle = bundle->next) + { + GroomSection *section = bundle->sections; + GroomSectionVertex *vertex = bundle->verts; + for (int i = 0; i < bundle->totsections; ++i, ++section) + { + /* local coordinate frame for the section */ + float mtx[3][3], smtx[3][3]; + mul_m3_m3m3(mtx, obmat3, section->mat); + pseudoinverse_m3_m3(smtx, mtx, PSEUDOINVERSE_EPSILON); + + for (int j = 0; j < bundle->numshapeverts; ++j, ++vertex) + { + if (is_prop_edit || (vertex->flag & GM_VERTEX_SELECT)) + { + copy_v2_v2(td2d->loc, vertex->co); + td2d->loc2d = vertex->co; + + td->loc = td2d->loc; + copy_v3_v3(td->iloc, td->loc); + /* section verts are centered around the curve */ + zero_v3(td->center); + + if (vertex->flag & GM_VERTEX_SELECT) + { + td->flag = TD_SELECTED; + } + else + { + td->flag = 0; + } + + copy_m3_m3(td->smtx, smtx); + copy_m3_m3(td->mtx, mtx); + + memset(td->axismtx, 0, sizeof(td->axismtx)); + td->axismtx[2][2] = 1.0f; + + td->ext = NULL; + td->val = NULL; + + ++td; + ++td2d; + } + } + } + } +} + +static void createTransGroomVerts(TransInfo *t) +{ + const ToolSettings *tsettings = t->scene->toolsettings; + const bool is_prop_edit = t->flag & T_PROP_EDIT; + EditGroom *edit = ((Groom *)t->obedit->data)->editgroom; + + switch (tsettings->groom_edit_settings.mode) + { + case GM_EDIT_MODE_REGIONS: + t->total = groom_trans_count_regions(edit, is_prop_edit); + if (t->total > 0) + { + // TODO + groom_transdata_init_regions(edit, is_prop_edit, t->obedit->obmat, t->data); + } + break; + case GM_EDIT_MODE_CURVES: + t->total = groom_trans_count_curves(edit, is_prop_edit); + if (t->total > 0) + { + t->data = MEM_callocN(t->total * sizeof(TransData), "TransData(Groom EditMode)"); + + groom_transdata_init_curves(edit, is_prop_edit, t->obedit->obmat, t->data); + } + break; + case GM_EDIT_MODE_SECTIONS: + t->total = groom_trans_count_verts(edit, is_prop_edit); + if (t->total > 0) + { + t->data = MEM_callocN(t->total * sizeof(TransData), "TransData(Groom EditMode)"); + t->data2d = MEM_callocN(t->total * sizeof(TransData2D), "TransData2D(Groom EditMode)"); + + groom_transdata_init_verts(edit, is_prop_edit, t->obedit->obmat, t->data, t->data2d); + } + break; + } +} + +void flushTransGroom(TransInfo *t) +{ + switch (t->scene->toolsettings->groom_edit_settings.mode) + { + case GM_EDIT_MODE_REGIONS: + break; + case GM_EDIT_MODE_CURVES: + break; + case GM_EDIT_MODE_SECTIONS: + { + TransData2D *td2d = t->data2d; + for (int i = 0; i < t->total; ++i, ++td2d) + { + copy_v2_v2(td2d->loc2d, td2d->loc); + } + break; + } + } +} + /* ******************* particle edit **************** */ static void createTransParticleVerts(bContext *C, TransInfo *t) { @@ -8252,6 +8483,9 @@ void createTransData(bContext *C, TransInfo *t) else if (t->obedit->type == OB_MBALL) { createTransMBallVerts(t); } + else if (t->obedit->type == OB_GROOM) { + createTransGroomVerts(t); + } else if (t->obedit->type == OB_ARMATURE) { t->flag &= ~T_PROP_EDIT; createTransArmatureVerts(t); diff --git a/source/blender/editors/transform/transform_generics.c b/source/blender/editors/transform/transform_generics.c index 6a813eb2a55..32cd02c59e6 100644 --- a/source/blender/editors/transform/transform_generics.c +++ b/source/blender/editors/transform/transform_generics.c @@ -755,6 +755,11 @@ static void recalcData_objects(TransInfo *t) if (la->editlatt->latt->flag & LT_OUTSIDE) outside_lattice(la->editlatt->latt); } + else if (t->obedit->type == OB_GROOM) { + flushTransGroom(t); + + DEG_id_tag_update(t->obedit->data, 0); /* sets recalc flags */ + } else if (t->obedit->type == OB_MESH) { BMEditMesh *em = BKE_editmesh_from_object(t->obedit); /* mirror modifier clipping? */ diff --git a/source/blender/gpu/GPU_texture.h b/source/blender/gpu/GPU_texture.h index 57185d2f39e..7c33153ee01 100644 --- a/source/blender/gpu/GPU_texture.h +++ b/source/blender/gpu/GPU_texture.h @@ -106,10 +106,10 @@ typedef enum GPUTextureFormat { /* Texture only format */ GPU_RGB16F, + GPU_RGB32F, #if 0 GPU_RGBA16_SNORM, GPU_RGBA8_SNORM, - GPU_RGB32F, GPU_RGB32I, GPU_RGB32UI, GPU_RGB16_SNORM, diff --git a/source/blender/makesdna/DNA_ID.h b/source/blender/makesdna/DNA_ID.h index 778aaec7d19..72e4597a06e 100644 --- a/source/blender/makesdna/DNA_ID.h +++ b/source/blender/makesdna/DNA_ID.h @@ -347,6 +347,7 @@ typedef enum ID_Type { ID_CF = MAKE_ID2('C', 'F'), /* CacheFile */ ID_WS = MAKE_ID2('W', 'S'), /* WorkSpace */ ID_LP = MAKE_ID2('L', 'P'), /* LightProbe */ + ID_GM = MAKE_ID2('G', 'M'), /* Groom */ } ID_Type; /* Only used as 'placeholder' in .blend files for directly linked datablocks. */ @@ -534,6 +535,7 @@ enum { INDEX_ID_ME, INDEX_ID_CU, INDEX_ID_MB, + INDEX_ID_GM, INDEX_ID_LT, INDEX_ID_LA, INDEX_ID_CA, diff --git a/source/blender/makesdna/DNA_groom_types.h b/source/blender/makesdna/DNA_groom_types.h new file mode 100644 index 00000000000..e1f6fbec7d0 --- /dev/null +++ b/source/blender/makesdna/DNA_groom_types.h @@ -0,0 +1,136 @@ +/* + * ***** 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 DNA_groom_types.h + * \ingroup DNA + */ + +#ifndef __DNA_GROOM_TYPES_H__ +#define __DNA_GROOM_TYPES_H__ + +#include "DNA_defs.h" +#include "DNA_listBase.h" +#include "DNA_ID.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Vertex in a closed curve for a bundle section */ +typedef struct GroomSectionVertex +{ + int flag; + float co[2]; /* Location in the section plane */ +} GroomSectionVertex; + +typedef enum GroomVertexFlag +{ + GM_VERTEX_SELECT = (1 << 0), +} GroomVertexFlag; + +/* Cross-section of a bundle */ +typedef struct GroomSection { + int flag; + int pad; + + float center[3]; /* Center point */ + + float mat[3][3]; /* Local coordinate frame */ +} GroomSection; + +typedef enum GroomSectionFlag +{ + GM_SECTION_SELECT = (1 << 0), +} GroomSectionFlag; + +/* Single interpolated step along a groom curve */ +typedef struct GroomCurveCache +{ + float co[3]; /* Location vector */ +} GroomCurveCache; + +/* Bundle of hair strands following the same curve path */ +typedef struct GroomBundle { + struct GroomBundle *next, *prev; /* Pointers for ListBase element */ + + int flag; + + int numshapeverts; /* Vertices per section loop */ + int totsections; /* Number of sections along the curve */ + int totverts; /* Number of vertices of all sections combined */ + int curvesize; /* Number of verticess in a curve = (totsections - 1) * curve_res + 1 */ + int totcurvecache; /* Number of cached curve steps = curve_size * (numshapeverts + 1) */ + + struct GroomSection *sections; /* List of sections [totsections] */ + struct GroomSectionVertex *verts; /* List of vertices [totsections][numloopverts] */ + struct GroomCurveCache *curvecache; /* Cached curve steps [numshapeverts + 1][curve_size], last is center curve */ + struct MeshSample *scalp_region; /* Mesh samples bind to a scalp region [numloopverts + 1], last is center position */ + + /* Scalp Region */ + /* XXX Face maps are used temporarily for creating regions, + * eventually should be replaced by a fully fledged 2D loop mesh */ + char scalp_facemap_name[64]; /* Scalp face map to use as region, MAX_VGROUP_NAME */ +} GroomBundle; + +typedef enum GroomBundleFlag +{ + GM_BUNDLE_SELECT = (1 << 0), +} GroomBundleFlag; + +/* Editable groom data */ +typedef struct EditGroom { + ListBase bundles; /* List of GroomBundle */ +} EditGroom; + +/* Groom curves for creating hair styles */ +typedef struct Groom { + ID id; /* Groom data is a datablock */ + struct AnimData *adt; /* Animation data - for animating settings */ + + int curve_res; /* Curve resolution */ + int pad; + + ListBase bundles; /* List of GroomBundle */ + int active_bundle; /* Index of active bundle in bundles list */ + int pad2; + + struct HairSystem *hair_system; /* Renderable hair geometry */ + struct HairDrawSettings *hair_draw_settings; /* Draw settings for hair geometry */ + + struct Object *scalp_object; /* Surface for attaching hairs */ + + struct BoundBox *bb; + + EditGroom *editgroom; + void *batch_cache; +} Groom; + +#ifdef __cplusplus +} +#endif + +#endif /* __DNA_GROOM_TYPES_H__ */ diff --git a/source/blender/makesdna/DNA_hair_types.h b/source/blender/makesdna/DNA_hair_types.h new file mode 100644 index 00000000000..8a95077c7f6 --- /dev/null +++ b/source/blender/makesdna/DNA_hair_types.h @@ -0,0 +1,121 @@ +/* + * ***** 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 + * + * ***** END GPL LICENSE BLOCK ***** + * + */ + +/** \file DNA_hair_types.h + * \ingroup DNA + */ + +#ifndef __DNA_HAIR_TYPES_H__ +#define __DNA_HAIR_TYPES_H__ + +#include "DNA_defs.h" +#include "DNA_listBase.h" +#include "DNA_ID.h" +#include "DNA_meshdata_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Root point (follicle) of a hair on a surface */ +typedef struct HairFollicle { + /* Sample on the scalp mesh for the root vertex */ + MeshSample mesh_sample; + /* Parent curve indices for shape interpolation */ + unsigned int parent_index[4]; + /* Parent curve weights for shape interpolation */ + float parent_weight[4]; +} HairFollicle; + +/* Collection of hair roots on a surface */ +typedef struct HairPattern { + struct HairFollicle *follicles; + int num_follicles; + int pad; +} HairPattern; + +typedef struct HairGuideCurve { + /* Sample on the scalp mesh for the root vertex */ + MeshSample mesh_sample; + /* Offset in the vertex array where the curve starts */ + int vertstart; + /* Number of vertices in the curve */ + int numverts; +} HairGuideCurve; + +typedef struct HairGuideVertex { + int flag; + float co[3]; +} HairGuideVertex; + +typedef struct HairSystem { + int flag; + int pad; + + /* Set of hair follicles on the scalp mesh */ + struct HairPattern *pattern; + + /* Curves for guiding hair fibers */ + struct HairGuideCurve *curves; + /* Control vertices on guide curves */ + struct HairGuideVertex *verts; + /* Number of guide curves */ + int totcurves; + /* Number of guide curve vertices */ + int totverts; + + /* Material used for drawing and rendering hair fibers */ + int material_index; + int pad2; + + /* Data buffers for drawing */ + void *draw_batch_cache; + /* Texture buffer for drawing */ + void *draw_texture_cache; +} HairSystem; + +typedef enum eHairSystemFlag +{ + /* Guide curve positions have changed, rebind hair follicles */ + HAIR_SYSTEM_UPDATE_FOLLICLE_BINDING = (1 << 8), +} eHairSystemFlag; + +typedef struct HairDrawSettings +{ + short follicle_mode; + short pad1; + int pad2; +} HairDrawSettings; + +typedef enum eHairDrawFollicleMode +{ + HAIR_DRAW_FOLLICLE_NONE = 0, + HAIR_DRAW_FOLLICLE_POINTS = 1, + HAIR_DRAW_FOLLICLE_AXES = 2, +} eHairDrawFollicleMode; + +#ifdef __cplusplus +} +#endif + +#endif /* __DNA_HAIR_TYPES_H__ */ diff --git a/source/blender/makesdna/DNA_meshdata_types.h b/source/blender/makesdna/DNA_meshdata_types.h index 6d38fe22ea1..1cbeee79326 100644 --- a/source/blender/makesdna/DNA_meshdata_types.h +++ b/source/blender/makesdna/DNA_meshdata_types.h @@ -380,6 +380,13 @@ enum { FREESTYLE_FACE_MARK = 1, }; +typedef struct MeshSample { + unsigned int orig_verts[3]; + float orig_weights[3]; /* also used as volume sample location */ + int orig_poly; + unsigned int orig_loops[3]; +} MeshSample; + /* 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 283e801ea7a..44627812a21 100644 --- a/source/blender/makesdna/DNA_modifier_types.h +++ b/source/blender/makesdna/DNA_modifier_types.h @@ -87,6 +87,7 @@ typedef enum ModifierType { eModifierType_CorrectiveSmooth = 51, eModifierType_MeshSequenceCache = 52, eModifierType_SurfaceDeform = 53, + eModifierType_Fur = 54, NUM_MODIFIER_TYPES } ModifierType; @@ -1622,4 +1623,22 @@ enum { #define MOD_MESHSEQ_READ_ALL \ (MOD_MESHSEQ_READ_VERT | MOD_MESHSEQ_READ_POLY | MOD_MESHSEQ_READ_UV | MOD_MESHSEQ_READ_COLOR) +/* Fur modifier */ +typedef struct FurModifierData { + ModifierData modifier; + + int flag; + int pad; + + struct HairSystem *hair_system; + struct HairDrawSettings *draw_settings; + + /* Follicle distribution parameters */ + int follicle_seed; + int follicle_count; + + int guides_count; + int pad2; +} FurModifierData; + #endif /* __DNA_MODIFIER_TYPES_H__ */ diff --git a/source/blender/makesdna/DNA_object_types.h b/source/blender/makesdna/DNA_object_types.h index 2cbc266ea0c..17dc7ebd2ad 100644 --- a/source/blender/makesdna/DNA_object_types.h +++ b/source/blender/makesdna/DNA_object_types.h @@ -397,6 +397,7 @@ enum { OB_SURF = 3, OB_FONT = 4, OB_MBALL = 5, + OB_GROOM = 6, OB_LAMP = 10, OB_CAMERA = 11, @@ -417,16 +418,16 @@ enum { #define OB_TYPE_SUPPORT_VGROUP(_type) \ (ELEM(_type, OB_MESH, OB_LATTICE)) #define OB_TYPE_SUPPORT_EDITMODE(_type) \ - (ELEM(_type, OB_MESH, OB_FONT, OB_CURVE, OB_SURF, OB_MBALL, OB_LATTICE, OB_ARMATURE)) + (ELEM(_type, OB_MESH, OB_FONT, OB_CURVE, OB_SURF, OB_MBALL, OB_LATTICE, OB_ARMATURE, OB_GROOM)) #define OB_TYPE_SUPPORT_PARVERT(_type) \ (ELEM(_type, OB_MESH, OB_SURF, OB_CURVE, OB_LATTICE)) /* is this ID type used as object data */ #define OB_DATA_SUPPORT_ID(_id_type) \ - (ELEM(_id_type, ID_ME, ID_CU, ID_MB, ID_LA, ID_SPK, ID_LP, ID_CA, ID_LT, ID_AR)) + (ELEM(_id_type, ID_ME, ID_CU, ID_MB, ID_LA, ID_SPK, ID_LP, ID_CA, ID_LT, ID_AR, ID_GM)) #define OB_DATA_SUPPORT_ID_CASE \ - ID_ME: case ID_CU: case ID_MB: case ID_LA: case ID_SPK: case ID_LP: case ID_CA: case ID_LT: case ID_AR + ID_ME: case ID_CU: case ID_MB: case ID_LA: case ID_SPK: case ID_LP: case ID_CA: case ID_LT: case ID_AR: case ID_GM /* partype: first 4 bits: type */ enum { diff --git a/source/blender/makesdna/DNA_scene_types.h b/source/blender/makesdna/DNA_scene_types.h index 696918d97a5..4b27190d79f 100644 --- a/source/blender/makesdna/DNA_scene_types.h +++ b/source/blender/makesdna/DNA_scene_types.h @@ -1102,6 +1102,22 @@ typedef struct ParticleEditSettings { } ParticleEditSettings; /* ------------------------------------------- */ +/* Groom Edit */ + +/* GroomEditSettings.mode */ +typedef enum GroomEditMode { + GM_EDIT_MODE_REGIONS, + GM_EDIT_MODE_CURVES, + GM_EDIT_MODE_SECTIONS, +} GroomEditMode; + +/* Groom Edit Mode Settings */ +typedef struct GroomEditSettings { + int mode; + int pad; +} GroomEditSettings; + +/* ------------------------------------------- */ /* Sculpt */ /* Sculpt */ @@ -1491,7 +1507,10 @@ typedef struct ToolSettings { /* Particle Editing */ struct ParticleEditSettings particle; - + + /* Groom Editing */ + struct GroomEditSettings groom_edit_settings; + /* Transform Proportional Area of Effect */ float proportional_size; diff --git a/source/blender/makesdna/intern/makesdna.c b/source/blender/makesdna/intern/makesdna.c index f0f1c2210f0..e66ea248e7c 100644 --- a/source/blender/makesdna/intern/makesdna.c +++ b/source/blender/makesdna/intern/makesdna.c @@ -133,6 +133,8 @@ static const char *includefiles[] = { "DNA_layer_types.h", "DNA_workspace_types.h", "DNA_lightprobe_types.h", + "DNA_hair_types.h", + "DNA_groom_types.h", /* see comment above before editing! */ @@ -1360,5 +1362,7 @@ int main(int argc, char **argv) #include "DNA_layer_types.h" #include "DNA_workspace_types.h" #include "DNA_lightprobe_types.h" +#include "DNA_hair_types.h" +#include "DNA_groom_types.h" /* end of list */ diff --git a/source/blender/makesrna/RNA_access.h b/source/blender/makesrna/RNA_access.h index 562855c01cc..d5752ff3e5e 100644 --- a/source/blender/makesrna/RNA_access.h +++ b/source/blender/makesrna/RNA_access.h @@ -267,6 +267,7 @@ extern StructRNA RNA_FreestyleLineSet; extern StructRNA RNA_FreestyleModuleSettings; extern StructRNA RNA_FreestyleSettings; extern StructRNA RNA_Function; +extern StructRNA RNA_FurModifier; extern StructRNA RNA_GPencilFrame; extern StructRNA RNA_GPencilLayer; extern StructRNA RNA_GPencilPalette; @@ -288,7 +289,11 @@ extern StructRNA RNA_GameTimerProperty; extern StructRNA RNA_GaussianBlurSequence; extern StructRNA RNA_GlowSequence; extern StructRNA RNA_GreasePencil; +extern StructRNA RNA_Groom; +extern StructRNA RNA_GroomBundle; extern StructRNA RNA_Group; +extern StructRNA RNA_HairGroup; +extern StructRNA RNA_HairPattern; extern StructRNA RNA_Header; extern StructRNA RNA_HemiLamp; extern StructRNA RNA_Histogram; diff --git a/source/blender/makesrna/intern/CMakeLists.txt b/source/blender/makesrna/intern/CMakeLists.txt index 74c36c456b0..bac0ecd1e4d 100644 --- a/source/blender/makesrna/intern/CMakeLists.txt +++ b/source/blender/makesrna/intern/CMakeLists.txt @@ -51,7 +51,9 @@ set(DEFSRC rna_fcurve.c rna_fluidsim.c rna_gpencil.c + rna_groom.c rna_group.c + rna_hair.c rna_image.c rna_key.c rna_lamp.c @@ -62,6 +64,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 diff --git a/source/blender/makesrna/intern/makesrna.c b/source/blender/makesrna/intern/makesrna.c index 9745ca39872..ab1eaca52e4 100644 --- a/source/blender/makesrna/intern/makesrna.c +++ b/source/blender/makesrna/intern/makesrna.c @@ -3366,7 +3366,9 @@ static RNAProcessItem PROCESS_ITEMS[] = { {"rna_fcurve.c", "rna_fcurve_api.c", RNA_def_fcurve}, {"rna_fluidsim.c", NULL, RNA_def_fluidsim}, {"rna_gpencil.c", NULL, RNA_def_gpencil}, + {"rna_groom.c", NULL, RNA_def_groom}, {"rna_group.c", NULL, RNA_def_group}, + {"rna_hair.c", NULL, RNA_def_hair}, {"rna_image.c", "rna_image_api.c", RNA_def_image}, {"rna_key.c", NULL, RNA_def_key}, {"rna_lamp.c", NULL, RNA_def_lamp}, @@ -3376,6 +3378,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}, diff --git a/source/blender/makesrna/intern/rna_ID.c b/source/blender/makesrna/intern/rna_ID.c index 088b2b67af5..dd062ac5ace 100644 --- a/source/blender/makesrna/intern/rna_ID.c +++ b/source/blender/makesrna/intern/rna_ID.c @@ -198,6 +198,7 @@ StructRNA *ID_code_to_RNA_type(short idcode) case ID_CF: return &RNA_CacheFile; case ID_CU: return &RNA_Curve; case ID_GD: return &RNA_GreasePencil; + case ID_GM: return &RNA_Groom; case ID_GR: return &RNA_Group; case ID_IM: return &RNA_Image; case ID_KE: return &RNA_Key; diff --git a/source/blender/makesrna/intern/rna_groom.c b/source/blender/makesrna/intern/rna_groom.c new file mode 100644 index 00000000000..67c0b3a02dd --- /dev/null +++ b/source/blender/makesrna/intern/rna_groom.c @@ -0,0 +1,231 @@ +/* + * ***** 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/makesrna/intern/rna_groom.c + * \ingroup RNA + */ + +#include <stdlib.h> + +#include "DNA_groom_types.h" +#include "DNA_scene_types.h" + +#include "MEM_guardedalloc.h" + +#include "BLI_string_utils.h" +#include "BLI_utildefines.h" + +#include "BLT_translation.h" + +#include "RNA_access.h" +#include "RNA_define.h" + +#include "rna_internal.h" + +#include "WM_types.h" +#include "DNA_object_types.h" + + +#ifdef RNA_RUNTIME + +#include "BLI_listbase.h" +#include "BLI_math.h" + +#include "WM_api.h" + +#include "BKE_groom.h" +#include "BKE_object_facemap.h" + +#include "DEG_depsgraph.h" + +static void UNUSED_FUNCTION(rna_Groom_update)(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *UNUSED(ptr)) +{ + WM_main_add_notifier(NC_GROOM | NA_EDITED, NULL); +} + +static void rna_Groom_update_data(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *ptr) +{ + DEG_id_tag_update(ptr->id.data, 0); + WM_main_add_notifier(NC_GROOM | ND_DATA, ptr->id.data); +} + +static int rna_GroomBundle_is_bound_get(PointerRNA *ptr) +{ + GroomBundle *bundle = (GroomBundle *)ptr->data; + return (bundle->scalp_region != NULL); +} + +static void rna_GroomBundle_scalp_facemap_name_set(PointerRNA *ptr, const char *value) +{ + Groom *groom = (Groom *)ptr->id.data; + GroomBundle *bundle = (GroomBundle *)ptr->data; + + if (groom->scalp_object) + { + bFaceMap *fm = BKE_object_facemap_find_name(groom->scalp_object, value); + if (fm) { + /* no need for BLI_strncpy_utf8, since this matches an existing facemap */ + BLI_strncpy(bundle->scalp_facemap_name, value, sizeof(bundle->scalp_facemap_name)); + return; + } + } + + bundle->scalp_facemap_name[0] = '\0'; +} + +static PointerRNA rna_Groom_active_bundle_get(PointerRNA *ptr) +{ + Groom *groom = (Groom *)ptr->id.data; + PointerRNA r_ptr; + RNA_pointer_create(&groom->id, &RNA_GroomBundle, BLI_findlink(&groom->bundles, groom->active_bundle), &r_ptr); + return r_ptr; +} + +static int rna_Groom_active_bundle_index_get(PointerRNA *ptr) +{ + Groom *groom = (Groom *)ptr->id.data; + return groom->active_bundle; +} + +static void rna_Groom_active_bundle_index_set(PointerRNA *ptr, int value) +{ + Groom *groom = (Groom *)ptr->id.data; + groom->active_bundle = value; +} + +static void rna_Groom_active_bundle_index_range( + PointerRNA *ptr, + int *min, + int *max, + int *UNUSED(softmin), + int *UNUSED(softmax)) +{ + Groom *groom = (Groom *)ptr->id.data; + *min = 0; + *max = max_ii(0, BLI_listbase_count(&groom->bundles) - 1); +} + +#else + +static void rna_def_groom_bundle(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, "GroomBundle", NULL); + RNA_def_struct_sdna(srna, "GroomBundle"); + RNA_def_struct_ui_text(srna, "Groom Bundle", "Bundle of hair originating from a scalp region"); + + prop = RNA_def_property(srna, "is_bound", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_funcs(prop, "rna_GroomBundle_is_bound_get", NULL); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text(prop, "Bound", "Bundle was successfully bound to a scalp region"); + RNA_def_property_update(prop, NC_GROOM | ND_DRAW, NULL); + + prop = RNA_def_property(srna, "scalp_facemap", PROP_STRING, PROP_NONE); + RNA_def_property_string_sdna(prop, NULL, "scalp_facemap_name"); + RNA_def_property_ui_text(prop, "Scalp Vertex Group", "Face map name of the scalp region"); + RNA_def_property_string_funcs(prop, NULL, NULL, "rna_GroomBundle_scalp_facemap_name_set"); + RNA_def_property_update(prop, 0, "rna_Groom_update_data"); +} + +/* groom.bundles */ +static void rna_def_groom_bundles(BlenderRNA *brna, PropertyRNA *cprop) +{ + StructRNA *srna; + PropertyRNA *prop; + + RNA_def_property_srna(cprop, "GroomBundles"); + srna = RNA_def_struct(brna, "GroomBundles", NULL); + RNA_def_struct_sdna(srna, "Groom"); + RNA_def_struct_ui_text(srna, "Groom Bundles", "Collection of groom bundles"); + + prop = RNA_def_property(srna, "active", PROP_POINTER, PROP_NONE); + RNA_def_property_struct_type(prop, "GroomBundle"); + RNA_def_property_pointer_funcs(prop, "rna_Groom_active_bundle_get", NULL, NULL, NULL); + RNA_def_property_ui_text(prop, "Active Groom Bundle", "Active groom bundle being displayed"); + RNA_def_property_update(prop, NC_GROOM | ND_DRAW, NULL); + + prop = RNA_def_property(srna, "active_index", PROP_INT, PROP_UNSIGNED); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_int_funcs(prop, "rna_Groom_active_bundle_index_get", + "rna_Groom_active_bundle_index_set", + "rna_Groom_active_bundle_index_range"); + RNA_def_property_ui_text(prop, "Active Groom Bundle Index", "Index of active groom bundle"); + RNA_def_property_update(prop, NC_GROOM | ND_DRAW, NULL); +} + +static void rna_def_groom(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, "Groom", "ID"); + RNA_def_struct_sdna(srna, "Groom"); + RNA_def_struct_ui_text(srna, "Groom", "Guide curve geometry for hair"); + RNA_def_struct_ui_icon(srna, ICON_NONE); + + /* Animation Data */ + rna_def_animdata_common(srna); + + prop = RNA_def_property(srna, "bundles", PROP_COLLECTION, PROP_NONE); + RNA_def_property_collection_sdna(prop, NULL, "bundles", NULL); + RNA_def_property_struct_type(prop, "GroomBundle"); + RNA_def_property_ui_text(prop, "Bundles", "Bundles of hair"); + rna_def_groom_bundles(brna, prop); + + prop = RNA_def_property(srna, "curve_resolution", PROP_INT, PROP_NONE); + RNA_def_property_int_sdna(prop, NULL, "curve_res"); + RNA_def_property_range(prop, 1, 1024); + RNA_def_property_ui_range(prop, 1, 64, 1, -1); + RNA_def_property_ui_text(prop, "Curve Resolution", "Curve subdivisions per segment"); + RNA_def_property_update(prop, 0, "rna_Groom_update_data"); + + prop = RNA_def_property(srna, "hair_system", PROP_POINTER, PROP_NONE); + RNA_def_property_ui_text(prop, "Hair", "Hair data"); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + + prop = RNA_def_property(srna, "hair_draw_settings", PROP_POINTER, PROP_NONE); + RNA_def_property_ui_text(prop, "Hair Draw Settings", "Hair draw settings"); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + + prop = RNA_def_property(srna, "scalp_object", PROP_POINTER, PROP_NONE); + RNA_def_property_pointer_sdna(prop, NULL, "scalp_object"); + RNA_def_property_ui_text(prop, "Scalp Object", "Surface for attaching hairs"); + RNA_def_property_flag(prop, PROP_EDITABLE); + RNA_def_property_update(prop, 0, "rna_Groom_update_data"); + + UNUSED_VARS(prop); +} + +void RNA_def_groom(BlenderRNA *brna) +{ + rna_def_groom(brna); + rna_def_groom_bundle(brna); +} + +#endif diff --git a/source/blender/makesrna/intern/rna_hair.c b/source/blender/makesrna/intern/rna_hair.c new file mode 100644 index 00000000000..079ce5c6a07 --- /dev/null +++ b/source/blender/makesrna/intern/rna_hair.c @@ -0,0 +1,231 @@ +/* + * ***** 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. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/makesrna/intern/rna_hair.c + * \ingroup RNA + */ + +#include <stdlib.h> + +#include "RNA_define.h" +#include "RNA_enum_types.h" + +#include "rna_internal.h" + +#include "DNA_hair_types.h" + +#include "WM_types.h" + +#ifdef RNA_RUNTIME + +#include "MEM_guardedalloc.h" + +#include "BLI_listbase.h" + +#include "DNA_object_types.h" + +#include "BKE_context.h" +#include "BKE_DerivedMesh.h" +#include "BKE_hair.h" +#include "BKE_main.h" +#include "BKE_material.h" + +#include "DEG_depsgraph.h" + +#include "RNA_access.h" + +#include "WM_api.h" +#include "WM_types.h" + +static void rna_HairSystem_update(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *ptr) +{ + DEG_id_tag_update(ptr->id.data, OB_RECALC_DATA); +} + +static void rna_HairSystem_generate_follicles( + HairSystem *hsys, + struct bContext *C, + Object *scalp, + int seed, + int count) +{ + if (!scalp) + { + return; + } + + struct Scene *scene = CTX_data_scene(C); + EvaluationContext eval_ctx; + CTX_data_eval_ctx(C, &eval_ctx); + + CustomDataMask datamask = CD_MASK_BAREMESH; + DerivedMesh *dm = mesh_get_derived_final(&eval_ctx, scene, scalp, datamask); + + BKE_hair_generate_follicles(hsys, dm, (unsigned int)seed, count); +} + +static const EnumPropertyItem *rna_HairSystem_material_slot_itemf( + bContext *C, + PointerRNA *UNUSED(ptr), + PropertyRNA *UNUSED(prop), + bool *r_free) +{ + Object *ob = CTX_data_pointer_get(C, "object").data; + Material *ma; + EnumPropertyItem *item = NULL; + EnumPropertyItem tmp = {0, "", 0, "", ""}; + int totitem = 0; + int i; + + if (ob && ob->totcol > 0) { + for (i = 1; i <= ob->totcol; i++) { + ma = give_current_material(ob, i); + tmp.value = i; + tmp.icon = ICON_MATERIAL_DATA; + if (ma) { + tmp.name = ma->id.name + 2; + tmp.identifier = tmp.name; + } + else { + tmp.name = "Default Material"; + tmp.identifier = tmp.name; + } + RNA_enum_item_add(&item, &totitem, &tmp); + } + } + else { + tmp.value = 1; + tmp.icon = ICON_MATERIAL_DATA; + tmp.name = "Default Material"; + tmp.identifier = tmp.name; + RNA_enum_item_add(&item, &totitem, &tmp); + } + + RNA_enum_item_end(&item, &totitem); + *r_free = true; + + return item; +} + +#else + +static void rna_def_hair_follicle(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, "HairFollicle", NULL); + RNA_def_struct_ui_text(srna, "Hair Follicle", "Single follicle on a surface"); + RNA_def_struct_sdna(srna, "HairFollicle"); + + prop = RNA_def_property(srna, "mesh_sample", PROP_POINTER, PROP_NONE); + RNA_def_property_struct_type(prop, "MeshSample"); +} + +static void rna_def_hair_pattern(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, "HairPattern", NULL); + RNA_def_struct_ui_text(srna, "Hair Pattern", "Set of hair follicles distributed on a surface"); + RNA_def_struct_sdna(srna, "HairPattern"); + RNA_def_struct_ui_icon(srna, ICON_STRANDS); + + prop = RNA_def_property(srna, "follicles", PROP_COLLECTION, PROP_NONE); + RNA_def_property_collection_sdna(prop, NULL, "follicles", "num_follicles"); + RNA_def_property_struct_type(prop, "HairFollicle"); + RNA_def_property_ui_text(prop, "Follicles", "Hair fiber follicles"); +} + +static void rna_def_hair_system(BlenderRNA *brna) +{ + StructRNA *srna; + FunctionRNA *func; + PropertyRNA *prop, *parm; + + static const EnumPropertyItem material_slot_items[] = { + {0, "DUMMY", 0, "Dummy", ""}, + {0, NULL, 0, NULL, NULL} + }; + + srna = RNA_def_struct(brna, "HairSystem", NULL); + RNA_def_struct_ui_text(srna, "Hair System", "Hair rendering and deformation data"); + RNA_def_struct_sdna(srna, "HairSystem"); + RNA_def_struct_ui_icon(srna, ICON_STRANDS); + + prop = RNA_def_property(srna, "pattern", PROP_POINTER, PROP_NONE); + RNA_def_property_struct_type(prop, "HairPattern"); + RNA_def_property_ui_text(prop, "Pattern", "Hair pattern"); + + prop = RNA_def_property(srna, "material_index", PROP_INT, PROP_NONE); + RNA_def_property_int_sdna(prop, NULL, "material_index"); + RNA_def_property_range(prop, 1, 32767); + RNA_def_property_ui_text(prop, "Material Index", "Index of material slot used for rendering hair fibers"); + RNA_def_property_update(prop, 0, "rna_HairSystem_update"); + + prop = RNA_def_property(srna, "material_slot", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "material_index"); + RNA_def_property_enum_items(prop, material_slot_items); + RNA_def_property_enum_funcs(prop, NULL, NULL, "rna_HairSystem_material_slot_itemf"); + RNA_def_property_ui_text(prop, "Material Slot", "Material slot used for rendering particles"); + RNA_def_property_update(prop, 0, "rna_HairSystem_update"); + + func = RNA_def_function(srna, "generate_follicles", "rna_HairSystem_generate_follicles"); + RNA_def_function_flag(func, FUNC_USE_CONTEXT); + parm = RNA_def_pointer(func, "scalp", "Object", "Scalp", "Scalp object on which to place hair follicles"); + RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); + parm = RNA_def_int(func, "seed", 0, 0, INT_MAX, "Seed", "Seed value for random numbers", 0, INT_MAX); + parm = RNA_def_int(func, "count", 0, 0, INT_MAX, "Count", "Maximum number of follicles to generate", 1, 1e5); + RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); +} + +static void rna_def_hair_draw_settings(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + static const EnumPropertyItem follicle_mode_items[] = { + {HAIR_DRAW_FOLLICLE_NONE, "NONE", 0, "None", ""}, + {HAIR_DRAW_FOLLICLE_POINTS, "POINTS", 0, "Points", "Draw a point for each follicle"}, + {HAIR_DRAW_FOLLICLE_AXES, "AXES", 0, "Axes", "Draw direction of hair for each follicle"}, + {0, NULL, 0, NULL, NULL} + }; + + srna = RNA_def_struct(brna, "HairDrawSettings", NULL); + RNA_def_struct_ui_text(srna, "Hair Draw Settings", "Settings for drawing hair systems"); + RNA_def_struct_sdna(srna, "HairDrawSettings"); + + prop = RNA_def_property(srna, "follicle_mode", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, follicle_mode_items); + RNA_def_property_ui_text(prop, "Follicle Mode", "Draw follicles on the scalp surface"); +} + +void RNA_def_hair(BlenderRNA *brna) +{ + rna_def_hair_follicle(brna); + rna_def_hair_pattern(brna); + rna_def_hair_system(brna); + rna_def_hair_draw_settings(brna); +} + +#endif diff --git a/source/blender/makesrna/intern/rna_internal.h b/source/blender/makesrna/intern/rna_internal.h index c1f82bab300..8bc82e90999 100644 --- a/source/blender/makesrna/intern/rna_internal.h +++ b/source/blender/makesrna/intern/rna_internal.h @@ -153,6 +153,7 @@ void RNA_def_fluidsim(struct BlenderRNA *brna); void RNA_def_fcurve(struct BlenderRNA *brna); void RNA_def_gameproperty(struct BlenderRNA *brna); void RNA_def_gpencil(struct BlenderRNA *brna); +void RNA_def_groom(struct BlenderRNA *brna); void RNA_def_group(struct BlenderRNA *brna); void RNA_def_image(struct BlenderRNA *brna); void RNA_def_key(struct BlenderRNA *brna); @@ -162,6 +163,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); @@ -200,6 +202,7 @@ void RNA_def_world(struct BlenderRNA *brna); void RNA_def_movieclip(struct BlenderRNA *brna); void RNA_def_tracking(struct BlenderRNA *brna); void RNA_def_mask(struct BlenderRNA *brna); +void RNA_def_hair(struct BlenderRNA *brna); /* Common Define functions */ diff --git a/source/blender/makesrna/intern/rna_main_api.c b/source/blender/makesrna/intern/rna_main_api.c index 077dac262b6..42c8eceeb16 100644 --- a/source/blender/makesrna/intern/rna_main_api.c +++ b/source/blender/makesrna/intern/rna_main_api.c @@ -234,6 +234,9 @@ static Object *rna_Main_objects_new(Main *bmain, ReportList *reports, const char case ID_AR: type = OB_ARMATURE; break; + case ID_GM: + type = OB_GROOM; + break; default: { const char *idname; 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..05e22fc48a2 --- /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, "MeshSample"); + 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 99417ee7b1b..579c865e679 100644 --- a/source/blender/makesrna/intern/rna_modifier.c +++ b/source/blender/makesrna/intern/rna_modifier.c @@ -114,6 +114,7 @@ const EnumPropertyItem rna_enum_object_modifier_type_items[] = { {eModifierType_DynamicPaint, "DYNAMIC_PAINT", ICON_MOD_DYNAMICPAINT, "Dynamic Paint", ""}, {eModifierType_Explode, "EXPLODE", ICON_MOD_EXPLODE, "Explode", ""}, {eModifierType_Fluidsim, "FLUID_SIMULATION", ICON_MOD_FLUIDSIM, "Fluid Simulation", ""}, + {eModifierType_Fur, "FUR", ICON_STRANDS, "Fur", ""}, {eModifierType_Ocean, "OCEAN", ICON_MOD_OCEAN, "Ocean", ""}, {eModifierType_ParticleInstance, "PARTICLE_INSTANCE", ICON_MOD_PARTICLES, "Particle Instance", ""}, {eModifierType_ParticleSystem, "PARTICLE_SYSTEM", ICON_MOD_PARTICLES, "Particle System", ""}, @@ -413,6 +414,8 @@ static StructRNA *rna_Modifier_refine(struct PointerRNA *ptr) return &RNA_MeshSequenceCacheModifier; case eModifierType_SurfaceDeform: return &RNA_SurfaceDeformModifier; + case eModifierType_Fur: + return &RNA_FurModifier; /* Default */ case eModifierType_None: case eModifierType_ShapeKey: @@ -4806,6 +4809,41 @@ static void rna_def_modifier_surfacedeform(BlenderRNA *brna) RNA_def_property_clear_flag(prop, PROP_EDITABLE); } +static void rna_def_modifier_fur(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, "FurModifier", "Modifier"); + RNA_def_struct_ui_text(srna, "Fur Modifier", ""); + RNA_def_struct_sdna(srna, "FurModifierData"); + RNA_def_struct_ui_icon(srna, ICON_STRANDS); + + prop = RNA_def_property(srna, "hair_system", PROP_POINTER, PROP_NONE); + RNA_def_property_ui_text(prop, "Hair", "Hair data"); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + + prop = RNA_def_property(srna, "follicle_seed", PROP_INT, PROP_NONE); + RNA_def_property_range(prop, 0, INT_MAX); + RNA_def_property_ui_text(prop, "Seed", "Follicle distribution random seed value"); + + prop = RNA_def_property(srna, "follicle_count", PROP_INT, PROP_NONE); + RNA_def_property_int_default(prop, 100000); + RNA_def_property_range(prop, 0, INT_MAX); + RNA_def_property_ui_range(prop, 1, 1e5, 1, 1); + RNA_def_property_ui_text(prop, "Follicle Count", "Maximum number of follicles"); + + prop = RNA_def_property(srna, "guides_count", PROP_INT, PROP_NONE); + RNA_def_property_int_default(prop, 1000); + RNA_def_property_range(prop, 0, INT_MAX); + RNA_def_property_ui_range(prop, 1, 1e3, 1, 1); + RNA_def_property_ui_text(prop, "Guides Count", "Maximum number of guide curves"); + + prop = RNA_def_property(srna, "draw_settings", PROP_POINTER, PROP_NONE); + RNA_def_property_ui_text(prop, "Draw Settings", "Hair draw settings"); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); +} + void RNA_def_modifier(BlenderRNA *brna) { StructRNA *srna; @@ -4924,6 +4962,7 @@ void RNA_def_modifier(BlenderRNA *brna) rna_def_modifier_normaledit(brna); rna_def_modifier_meshseqcache(brna); rna_def_modifier_surfacedeform(brna); + rna_def_modifier_fur(brna); } #endif diff --git a/source/blender/makesrna/intern/rna_object.c b/source/blender/makesrna/intern/rna_object.c index 1932390d0fd..43001d6bd66 100644 --- a/source/blender/makesrna/intern/rna_object.c +++ b/source/blender/makesrna/intern/rna_object.c @@ -162,6 +162,7 @@ const EnumPropertyItem rna_enum_object_type_items[] = { {OB_LAMP, "LAMP", 0, "Lamp", ""}, {OB_SPEAKER, "SPEAKER", 0, "Speaker", ""}, {OB_LIGHTPROBE, "LIGHT_PROBE", 0, "Probe", ""}, + {OB_GROOM, "GROOM", 0, "Groom", ""}, {0, NULL, 0, NULL, NULL} }; @@ -407,6 +408,7 @@ static StructRNA *rna_Object_data_typef(PointerRNA *ptr) case OB_ARMATURE: return &RNA_Armature; case OB_SPEAKER: return &RNA_Speaker; case OB_LIGHTPROBE: return &RNA_LightProbe; + case OB_GROOM: return &RNA_Groom; default: return &RNA_ID; } } diff --git a/source/blender/makesrna/intern/rna_scene.c b/source/blender/makesrna/intern/rna_scene.c index 3c600f6e367..3782e694d73 100644 --- a/source/blender/makesrna/intern/rna_scene.c +++ b/source/blender/makesrna/intern/rna_scene.c @@ -2441,6 +2441,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, "groom_edit_settings", PROP_POINTER, PROP_NONE); + RNA_def_property_struct_type(prop, "GroomEditSettings"); + RNA_def_property_ui_text(prop, "Groom Edit Settings", ""); + 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 49edc742a4b..13833c86d9a 100644 --- a/source/blender/makesrna/intern/rna_sculpt_paint.c +++ b/source/blender/makesrna/intern/rna_sculpt_paint.c @@ -242,6 +242,23 @@ static char *rna_ParticleEdit_path(PointerRNA *UNUSED(ptr)) return BLI_strdup("tool_settings.particle_edit"); } +static char *rna_GroomEditSettings_path(PointerRNA *UNUSED(ptr)) +{ + return BLI_strdup("tool_settings.groom_edit_settings"); +} + +static void rna_GroomEditSettings_update(bContext *C, PointerRNA *UNUSED(ptr)) +{ + ViewLayer *view_layer = CTX_data_view_layer(C); + Object *ob = OBACT(view_layer); + + if (ob) + { + DEG_id_tag_update(&ob->id, OB_RECALC_DATA); + WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob); + } +} + static int rna_Brush_mode_poll(PointerRNA *ptr, PointerRNA value) { Scene *scene = (Scene *)ptr->id.data; @@ -1052,6 +1069,29 @@ static void rna_def_particle_edit(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Curve", ""); } +static void rna_def_groom_edit_settings(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + static const EnumPropertyItem mode_items[] = { + {GM_EDIT_MODE_REGIONS, "REGIONS", ICON_NONE, "Regions", "Region edit mode"}, + {GM_EDIT_MODE_CURVES, "CURVES", ICON_NONE, "Curves", "Curve edit mode"}, + {GM_EDIT_MODE_SECTIONS, "SECTIONS", ICON_NONE, "Sections", "Section edit mode"}, + {0, NULL, 0, NULL, NULL} + }; + + srna = RNA_def_struct(brna, "GroomEditSettings", NULL); + RNA_def_struct_path_func(srna, "rna_GroomEditSettings_path"); + RNA_def_struct_ui_text(srna, "Groom Edit", "Properties of groom editing mode"); + + prop = RNA_def_property(srna, "mode", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, mode_items); + RNA_def_property_ui_text(prop, "Mode", "Select mode"); + RNA_def_property_flag(prop, PROP_CONTEXT_UPDATE); + RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_GroomEditSettings_update"); +} + static void rna_def_gpencil_sculpt(BlenderRNA *brna) { static const EnumPropertyItem prop_direction_items[] = { @@ -1165,6 +1205,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_groom_edit_settings(brna); rna_def_gpencil_sculpt(brna); RNA_define_animate_sdna(true); } diff --git a/source/blender/modifiers/CMakeLists.txt b/source/blender/modifiers/CMakeLists.txt index 397a3263e22..cf1021ec945 100644 --- a/source/blender/modifiers/CMakeLists.txt +++ b/source/blender/modifiers/CMakeLists.txt @@ -63,6 +63,7 @@ set(SRC intern/MOD_explode.c intern/MOD_fluidsim.c intern/MOD_fluidsim_util.c + intern/MOD_fur.c intern/MOD_hook.c intern/MOD_laplaciandeform.c intern/MOD_laplaciansmooth.c diff --git a/source/blender/modifiers/MOD_modifiertypes.h b/source/blender/modifiers/MOD_modifiertypes.h index bf121af2bd1..ff486287d7e 100644 --- a/source/blender/modifiers/MOD_modifiertypes.h +++ b/source/blender/modifiers/MOD_modifiertypes.h @@ -86,6 +86,7 @@ extern ModifierTypeInfo modifierType_NormalEdit; extern ModifierTypeInfo modifierType_CorrectiveSmooth; extern ModifierTypeInfo modifierType_MeshSequenceCache; extern ModifierTypeInfo modifierType_SurfaceDeform; +extern ModifierTypeInfo modifierType_Fur; /* MOD_util.c */ void modifier_type_init(ModifierTypeInfo *types[]); diff --git a/source/blender/modifiers/intern/MOD_fur.c b/source/blender/modifiers/intern/MOD_fur.c new file mode 100644 index 00000000000..3334a60d735 --- /dev/null +++ b/source/blender/modifiers/intern/MOD_fur.c @@ -0,0 +1,162 @@ +/* + * ***** 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 by the Blender Foundation. + * All rights reserved. + * + * Contributor(s): Daniel Dunbar + * Ton Roosendaal, + * Ben Batt, + * Brecht Van Lommel, + * Campbell Barton + * + * ***** END GPL LICENSE BLOCK ***** + * + */ + +/** \file blender/modifiers/intern/MOD_fur.c + * \ingroup modifiers + */ + +#include "MEM_guardedalloc.h" + +#include "BLI_utildefines.h" + +#include "DNA_object_types.h" +#include "DNA_hair_types.h" + +#include "BKE_cdderivedmesh.h" +#include "BKE_hair.h" +#include "BKE_library.h" +#include "BKE_library_query.h" +#include "BKE_modifier.h" + +#include "DEG_depsgraph_build.h" + +#include "MOD_util.h" + + +static void initData(ModifierData *md) +{ + FurModifierData *fmd = (FurModifierData *) md; + + fmd->hair_system = BKE_hair_new(); + + fmd->flag |= 0; + + fmd->follicle_count = 100000; + fmd->guides_count = 1000; + + fmd->draw_settings = BKE_hair_draw_settings_new(); +} + +static void copyData(ModifierData *md, ModifierData *target) +{ + FurModifierData *fmd = (FurModifierData *) md; + FurModifierData *tfmd = (FurModifierData *) target; + + if (tfmd->hair_system) { + BKE_hair_free(tfmd->hair_system); + } + if (tfmd->draw_settings) + { + BKE_hair_draw_settings_free(tfmd->draw_settings); + } + + modifier_copyData_generic(md, target); + + if (fmd->hair_system) { + tfmd->hair_system = BKE_hair_copy(fmd->hair_system); + } + if (fmd->draw_settings) + { + tfmd->draw_settings = BKE_hair_draw_settings_copy(fmd->draw_settings); + } +} + +static void freeData(ModifierData *md) +{ + FurModifierData *fmd = (FurModifierData *) md; + + if (fmd->hair_system) { + BKE_hair_free(fmd->hair_system); + } + if (fmd->draw_settings) + { + BKE_hair_draw_settings_free(fmd->draw_settings); + } +} + +static DerivedMesh *applyModifier(ModifierData *md, const struct EvaluationContext *UNUSED(eval_ctx), + Object *UNUSED(ob), DerivedMesh *dm, + ModifierApplyFlag UNUSED(flag)) +{ + FurModifierData *fmd = (FurModifierData *) md; + + UNUSED_VARS(fmd); + + return dm; +} + +static void foreachObjectLink( + ModifierData *md, + Object *ob, + ObjectWalkFunc walk, + void *userData) +{ + FurModifierData *fmd = (FurModifierData *) md; + UNUSED_VARS(ob, walk, userData, fmd); +} + +static void foreachIDLink( + ModifierData *md, + Object *ob, + IDWalkFunc walk, + void *userData) +{ + FurModifierData *fmd = (FurModifierData *) md; + UNUSED_VARS(fmd); + + foreachObjectLink(md, ob, (ObjectWalkFunc)walk, userData); +} + +ModifierTypeInfo modifierType_Fur = { + /* name */ "Fur", + /* structName */ "FurModifierData", + /* structSize */ sizeof(FurModifierData), + /* type */ eModifierTypeType_NonGeometrical, + /* flags */ eModifierTypeFlag_AcceptsMesh | + eModifierTypeFlag_SupportsEditmode, + + /* copyData */ copyData, + /* deformVerts */ NULL, + /* deformMatrices */ NULL, + /* deformVertsEM */ NULL, + /* deformMatricesEM */ NULL, + /* applyModifier */ applyModifier, + /* applyModifierEM */ NULL, + /* initData */ initData, + /* requiredDataMask */ NULL, + /* freeData */ freeData, + /* isDisabled */ NULL, + /* updateDepsgraph */ NULL, + /* dependsOnTime */ NULL, + /* dependsOnNormals */ NULL, + /* foreachObjectLink */ foreachObjectLink, + /* foreachIDLink */ foreachIDLink, + /* foreachTexLink */ NULL, +}; diff --git a/source/blender/modifiers/intern/MOD_util.c b/source/blender/modifiers/intern/MOD_util.c index 5b19bcf4817..54ba6320a29 100644 --- a/source/blender/modifiers/intern/MOD_util.c +++ b/source/blender/modifiers/intern/MOD_util.c @@ -288,5 +288,6 @@ void modifier_type_init(ModifierTypeInfo *types[]) INIT_TYPE(CorrectiveSmooth); INIT_TYPE(MeshSequenceCache); INIT_TYPE(SurfaceDeform); + INIT_TYPE(Fur); #undef INIT_TYPE } diff --git a/source/blender/windowmanager/WM_types.h b/source/blender/windowmanager/WM_types.h index 4bd5bcfc056..383744b898a 100644 --- a/source/blender/windowmanager/WM_types.h +++ b/source/blender/windowmanager/WM_types.h @@ -256,6 +256,7 @@ typedef struct wmNotifier { #define NC_GPENCIL (22<<24) #define NC_LINESTYLE (23<<24) #define NC_CAMERA (24<<24) +#define NC_GROOM (25<<24) /* data type, 256 entries is enough, it can overlap */ #define NOTE_DATA 0x00FF0000 @@ -391,6 +392,7 @@ typedef struct wmNotifier { #define NS_EDITMODE_ARMATURE (8<<8) #define NS_MODE_POSE (9<<8) #define NS_MODE_PARTICLE (10<<8) +#define NS_EDITMODE_GROOM (11<<8) /* subtype 3d view editing */ #define NS_VIEW3D_GPU (16<<8) diff --git a/source/blender/windowmanager/intern/wm_keymap.c b/source/blender/windowmanager/intern/wm_keymap.c index 0db67e0b199..6f42de73e61 100644 --- a/source/blender/windowmanager/intern/wm_keymap.c +++ b/source/blender/windowmanager/intern/wm_keymap.c @@ -1796,6 +1796,9 @@ wmKeyMap *WM_keymap_guess_opname(const bContext *C, const char *opname) else if (STRPREFIX(opname, "PARTICLE_OT")) { km = WM_keymap_find_all(C, "Particle", 0, 0); } + else if (STRPREFIX(opname, "GROOM_OT")) { + km = WM_keymap_find_all(C, "Groom", 0, 0); + } else if (STRPREFIX(opname, "FONT_OT")) { km = WM_keymap_find_all(C, "Font", 0, 0); } |