diff options
90 files changed, 4840 insertions, 21 deletions
diff --git a/build_files/cmake/macros.cmake b/build_files/cmake/macros.cmake index 3bb435fa892..818a4f1764e 100644 --- a/build_files/cmake/macros.cmake +++ b/build_files/cmake/macros.cmake @@ -610,6 +610,7 @@ function(SETUP_BLENDER_SORTED_LIBS) bf_editor_datafiles bf_editor_mask bf_editor_io + bf_editor_groom bf_render bf_python diff --git a/release/scripts/startup/bl_ui/__init__.py b/release/scripts/startup/bl_ui/__init__.py index ae0e8dac6bb..09094c941e2 100644 --- a/release/scripts/startup/bl_ui/__init__.py +++ b/release/scripts/startup/bl_ui/__init__.py @@ -34,6 +34,7 @@ _modules = [ "properties_data_camera", "properties_data_curve", "properties_data_empty", + "properties_data_groom", "properties_data_lamp", "properties_data_lattice", "properties_data_mesh", diff --git a/release/scripts/startup/bl_ui/properties_data_groom.py b/release/scripts/startup/bl_ui/properties_data_groom.py new file mode 100644 index 00000000000..2e6a9661714 --- /dev/null +++ b/release/scripts/startup/bl_ui/properties_data_groom.py @@ -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. +# +# ##### END GPL LICENSE BLOCK ##### + +# <pep8 compliant> +import bpy +from bpy.types import Menu, Panel +from rna_prop_ui import PropertyPanel + + +class GROOM_UL_bundles(bpy.types.UIList): + def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index, flt_flag): + groom = data + bundle = item + + if self.layout_type in {'DEFAULT', 'COMPACT'}: + row = layout.row(align=True) + if not bundle.is_bound: + row.label(icon='ERROR') + if groom.scalp_object: + row.prop_search(bundle, "scalp_facemap", groom.scalp_object, "face_maps", text="") + else: + row.prop(bundle, "scalp_facemap", text="") + + elif self.layout_type == 'GRID': + layout.alignment = 'CENTER' + layout.label(text="", icon_value=icon) + + +class DataButtonsPanel: + bl_space_type = 'PROPERTIES' + bl_region_type = 'WINDOW' + bl_context = "data" + + @classmethod + def poll(cls, context): + return context.groom + + +class DATA_PT_context_groom(DataButtonsPanel, Panel): + bl_label = "" + bl_options = {'HIDE_HEADER'} + + def draw(self, context): + layout = self.layout + ob = context.object + groom = context.groom + space = context.space_data + + split = layout.split(percentage=0.65) + + if ob: + split.template_ID(ob, "data") + elif groom: + split.template_ID(space, "pin_id") + + +class DATA_PT_groom(DataButtonsPanel, Panel): + bl_label = "Groom" + + def draw(self, context): + layout = self.layout + + groom = context.groom + + layout.template_list("GROOM_UL_bundles", "bundles", + groom, "bundles", + groom.bundles, "active_index") + + split = layout.split() + + col = split.column() + col.label("Scalp Object:") + col.prop(groom, "scalp_object", "") + + col = split.column() + col.label("Curves:") + col.prop(groom, "curve_resolution", "Resolution") + + +class DATA_PT_groom_hair(DataButtonsPanel, Panel): + bl_label = "Hair" + + def draw(self, context): + layout = self.layout + groom = context.groom + + layout.operator("groom.hair_distribute") + + +class DATA_PT_groom_draw_settings(DataButtonsPanel, Panel): + bl_label = "Draw Settings" + + def draw(self, context): + layout = self.layout + groom = context.groom + ds = groom.hair_draw_settings + + split = layout.split() + col = split.column() + col.label("Follicles:") + col.prop(ds, "follicle_mode", expand=True) + + +class DATA_PT_custom_props_groom(DataButtonsPanel, PropertyPanel, Panel): + _context_path = "object.data" + _property_type = bpy.types.Groom + + +classes = ( + GROOM_UL_bundles, + DATA_PT_context_groom, + DATA_PT_groom, + DATA_PT_groom_hair, + DATA_PT_groom_draw_settings, + DATA_PT_custom_props_groom, +) + +if __name__ == "__main__": # only for live edit. + from bpy.utils import register_class + for cls in classes: + register_class(cls) diff --git a/release/scripts/startup/bl_ui/space_view3d.py b/release/scripts/startup/bl_ui/space_view3d.py index f20e9021e28..097ba2f73e7 100644 --- a/release/scripts/startup/bl_ui/space_view3d.py +++ b/release/scripts/startup/bl_ui/space_view3d.py @@ -77,7 +77,10 @@ class VIEW3D_HT_header(Header): # mode = obj.mode # Particle edit - if mode == 'PARTICLE_EDIT': + if mode == 'EDIT' and obj.type == 'GROOM': + row = layout.row() + row.prop(toolsettings.groom_edit_settings, "mode", text="", expand=True) + elif mode == 'PARTICLE_EDIT': row = layout.row() row.prop(toolsettings.particle_edit, "select_mode", text="", expand=True) @@ -150,6 +153,8 @@ class VIEW3D_MT_editor_menus(Menu): layout.menu("INFO_MT_surface_add", text="Add") elif mode_string == 'EDIT_METABALL': layout.menu("INFO_MT_metaball_add", text="Add") + elif mode_string == 'EDIT_GROOM': + layout.menu("INFO_MT_groom_add", text="Add") elif mode_string == 'EDIT_ARMATURE': layout.menu("INFO_MT_edit_armature_add", text="Add") @@ -1221,6 +1226,17 @@ class INFO_MT_metaball_add(Menu): layout.operator_enum("object.metaball_add", "type") +class INFO_MT_groom_add(Menu): + bl_idname = "INFO_MT_groom_add" + bl_label = "Groom" + + def draw(self, context): + layout = self.layout + + layout.operator_context = 'INVOKE_REGION_WIN' + layout.operator("object.groom_add") + + class INFO_MT_edit_curve_add(Menu): bl_idname = "INFO_MT_edit_curve_add" bl_label = "Add" @@ -1311,6 +1327,7 @@ class INFO_MT_add(Menu): # layout.operator_menu_enum("object.surface_add", "type", text="Surface", icon='OUTLINER_OB_SURFACE') layout.menu("INFO_MT_surface_add", icon='OUTLINER_OB_SURFACE') layout.menu("INFO_MT_metaball_add", text="Metaball", icon='OUTLINER_OB_META') + layout.menu("INFO_MT_groom_add", text="Groom", icon='OUTLINER_OB_GROOM') layout.operator("object.text_add", text="Text", icon='OUTLINER_OB_FONT') layout.separator() @@ -3153,6 +3170,28 @@ class VIEW3D_MT_edit_lattice(Menu): layout.menu("VIEW3D_MT_edit_proportional") +class VIEW3D_MT_edit_groom(Menu): + bl_label = "Groom" + + def draw(self, context): + layout = self.layout + + edit_object = context.edit_object + groom = edit_object.data + + layout.menu("VIEW3D_MT_undo_redo") + + layout.separator() + + layout.menu("VIEW3D_MT_transform") + layout.menu("VIEW3D_MT_mirror") + layout.menu("VIEW3D_MT_snap") + + layout.separator() + + layout.operator("groom.region_add") + + class VIEW3D_MT_edit_armature(Menu): bl_label = "Armature" @@ -3956,6 +3995,7 @@ classes = ( INFO_MT_curve_add, INFO_MT_surface_add, INFO_MT_metaball_add, + INFO_MT_groom_add, INFO_MT_edit_curve_add, INFO_MT_edit_armature_add, INFO_MT_armature_add, @@ -4035,6 +4075,7 @@ classes = ( VIEW3D_MT_edit_meta, VIEW3D_MT_edit_meta_showhide, VIEW3D_MT_edit_lattice, + VIEW3D_MT_edit_groom, VIEW3D_MT_edit_armature, VIEW3D_MT_armature_specials, VIEW3D_MT_edit_armature_parent, diff --git a/source/blender/CMakeLists.txt b/source/blender/CMakeLists.txt index c3b15845cf7..b88e5e88cfe 100644 --- a/source/blender/CMakeLists.txt +++ b/source/blender/CMakeLists.txt @@ -46,6 +46,7 @@ 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 diff --git a/source/blender/blenkernel/BKE_context.h b/source/blender/blenkernel/BKE_context.h index e224863a27f..4277e957aa9 100644 --- a/source/blender/blenkernel/BKE_context.h +++ b/source/blender/blenkernel/BKE_context.h @@ -113,6 +113,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..ef15223dd45 --- /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 Depsgraph; +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(struct Depsgraph *depsgraph, 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_library.h b/source/blender/blenkernel/BKE_library.h index 57b509da3a4..e80a324bd14 100644 --- a/source/blender/blenkernel/BKE_library.h +++ b/source/blender/blenkernel/BKE_library.h @@ -165,7 +165,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 efcff9a9382..b17a96a45a1 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/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt index b303d7e4d5f..e6386d573ae 100644 --- a/source/blender/blenkernel/CMakeLists.txt +++ b/source/blender/blenkernel/CMakeLists.txt @@ -114,6 +114,7 @@ set(SRC intern/font.c intern/freestyle.c intern/gpencil.c + intern/groom.c intern/hair.c intern/hair_draw.c intern/icons.c @@ -255,6 +256,7 @@ set(SRC BKE_freestyle.h BKE_global.h BKE_gpencil.h + BKE_groom.h BKE_hair.h BKE_icons.h BKE_idcode.h diff --git a/source/blender/blenkernel/intern/context.c b/source/blender/blenkernel/intern/context.c index 6314486d809..158d0564716 100644 --- a/source/blender/blenkernel/intern/context.c +++ b/source/blender/blenkernel/intern/context.c @@ -995,6 +995,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 { @@ -1029,6 +1031,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..3f607916169 --- /dev/null +++ b/source/blender/blenkernel/intern/groom.c @@ -0,0 +1,870 @@ +/* + * ***** 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_get(&bvhtree, dm, BVHTREE_FROM_FACES, 2); + 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_generate_guide_curves(groom, scalp, guide_seed, guide_curve_count); +} + +void BKE_groom_hair_update_guide_curves(Groom *groom) +{ + UNUSED_VARS(groom); +} + + +/* === 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(struct Depsgraph *UNUSED(depsgraph), 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/idcode.c b/source/blender/blenkernel/intern/idcode.c index 4860f5a896d..005702f3ed3 100644 --- a/source/blender/blenkernel/intern/idcode.c +++ b/source/blender/blenkernel/intern/idcode.c @@ -64,6 +64,7 @@ static IDType idtypes[] = { { ID_GR, "Collection", "collections", BLT_I18NCONTEXT_ID_COLLECTION, 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_IM, "Image", "images", BLT_I18NCONTEXT_ID_IMAGE, IDTYPE_FLAGS_ISLINKABLE }, { ID_IP, "Ipo", "ipos", "", IDTYPE_FLAGS_ISLINKABLE }, /* deprecated */ { ID_KE, "Key", "shape_keys", BLT_I18NCONTEXT_ID_SHAPEKEY, 0 }, @@ -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 c59036295af..0b4ba2a386e 100644 --- a/source/blender/blenkernel/intern/library.c +++ b/source/blender/blenkernel/intern/library.c @@ -48,6 +48,7 @@ #include "DNA_cachefile_types.h" #include "DNA_camera_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" @@ -97,6 +98,7 @@ #include "BKE_font.h" #include "BKE_global.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; } @@ -1091,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); @@ -1182,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 dbfe619153d..0cd1ce2b6c7 100644 --- a/source/blender/blenkernel/intern/library_query.c +++ b/source/blender/blenkernel/intern/library_query.c @@ -36,6 +36,7 @@ #include "DNA_brush_types.h" #include "DNA_camera_types.h" #include "DNA_constraint_types.h" +#include "DNA_groom_types.h" #include "DNA_group_types.h" #include "DNA_gpencil_types.h" #include "DNA_key_types.h" @@ -630,6 +631,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; @@ -1079,6 +1087,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 cf2a001daaa..7843a028aea 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" @@ -78,6 +79,7 @@ #include "BKE_fcurve.h" #include "BKE_font.h" #include "BKE_gpencil.h" +#include "BKE_groom.h" #include "BKE_idprop.h" #include "BKE_image.h" #include "BKE_ipo.h" @@ -869,6 +871,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/object.c b/source/blender/blenkernel/intern/object.c index 1e6d3041f3f..65eb85114b4 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" @@ -73,6 +74,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" @@ -323,6 +325,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); @@ -498,6 +507,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; } @@ -612,6 +626,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"); @@ -642,6 +657,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 c1cc6bf0bdc..de1743b6c24 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" @@ -333,6 +334,9 @@ void BKE_object_eval_uber_data(Depsgraph *depsgraph, 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()) { @@ -437,6 +441,10 @@ void BKE_object_data_select_update(Depsgraph *depsgraph, ID *object_data) 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/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c index 2a42fdfabc1..aab14a07241 100644 --- a/source/blender/blenloader/intern/readfile.c +++ b/source/blender/blenloader/intern/readfile.c @@ -70,6 +70,7 @@ #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" @@ -8195,6 +8196,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 ******************** */ @@ -8492,6 +8537,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); @@ -8674,6 +8722,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 */ } @@ -9304,6 +9353,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_collection(FileData *fd, Main *mainvar, Collection *collection) { for (CollectionObject *cob = collection->gobject.first; cob; cob = cob->next) { @@ -9942,6 +9996,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_collection(fd, mainvar, (Collection *)id); break; diff --git a/source/blender/blenloader/intern/writefile.c b/source/blender/blenloader/intern/writefile.c index f43763ef04e..2732b5996cb 100644 --- a/source/blender/blenloader/intern/writefile.c +++ b/source/blender/blenloader/intern/writefile.c @@ -116,6 +116,7 @@ #include "DNA_constraint_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" @@ -3629,6 +3630,32 @@ static void write_workspace(WriteData *wd, WorkSpace *workspace) writelist(wd, DATA, bToolRef, &workspace->tools); } +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) { @@ -3931,6 +3958,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 ba4ae964842..cdd33309489 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 { @@ -179,6 +180,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_ID, "id_id"), \ BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_ID_IMAGE, "id_image"), \ /*BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_ID_IPO, "id_ipo"),*/ \ 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 b5e6fe168e5..d98d69ed40f 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 98881ba3e57..70584151df1 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" @@ -77,6 +78,7 @@ extern "C" { #include "BKE_effect.h" #include "BKE_fcurve.h" #include "BKE_idcode.h" +#include "BKE_groom.h" #include "BKE_key.h" #include "BKE_lattice.h" #include "BKE_library.h" @@ -547,6 +549,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. @@ -1138,6 +1141,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 488f4f273b9..0e2913ebfbb 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_nodes.h +++ b/source/blender/depsgraph/intern/builder/deg_builder_nodes.h @@ -44,6 +44,7 @@ struct ID; struct Image; struct FCurve; struct Collection; +struct Groom; struct Key; struct LayerCollection; struct Main; diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc index edb2969fa63..82f0984eb2e 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" @@ -78,6 +79,7 @@ extern "C" { #include "BKE_effect.h" #include "BKE_collision.h" #include "BKE_fcurve.h" +#include "BKE_groom.h" #include "BKE_key.h" #include "BKE_library.h" #include "BKE_main.h" @@ -589,6 +591,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; @@ -1820,6 +1823,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 dbfaff4dc18..5c58bfc35cb 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 466e6053ef3..5f746e40115 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 f6f8fe4eb14..37489dca8d3 100644 --- a/source/blender/draw/CMakeLists.txt +++ b/source/blender/draw/CMakeLists.txt @@ -57,6 +57,7 @@ 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 @@ -75,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 @@ -255,6 +257,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 e4c93d50fe2..405d5bb12f2 100644 --- a/source/blender/draw/engines/eevee/eevee_materials.c +++ b/source/blender/draw/engines/eevee/eevee_materials.c @@ -32,6 +32,7 @@ #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" @@ -39,6 +40,7 @@ #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" @@ -1709,6 +1711,10 @@ void EEVEE_materials_cache_populate(EEVEE_Data *vedata, EEVEE_ViewLayerData *sld } } } + 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) diff --git a/source/blender/draw/intern/draw_cache.c b/source/blender/draw/intern/draw_cache.c index daa6ecc9740..472fc25fb88 100644 --- a/source/blender/draw/intern/draw_cache.c +++ b/source/blender/draw/intern/draw_cache.c @@ -2809,6 +2809,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 * \{ */ diff --git a/source/blender/draw/intern/draw_cache.h b/source/blender/draw/intern/draw_cache.h index 2193ca89c21..75d62d27631 100644 --- a/source/blender/draw/intern/draw_cache.h +++ b/source/blender/draw/intern/draw_cache.h @@ -31,6 +31,7 @@ struct GPUMaterial; struct ModifierData; struct Object; struct PTCacheEdit; +struct Groom; struct HairSystem; struct DRWHairFiberTextureBuffer; struct DerivedMesh; @@ -168,6 +169,11 @@ 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 Object *object, struct ParticleSystem *psys, struct ModifierData *md); struct Gwn_Batch *DRW_cache_particles_get_dots(struct Object *object, struct ParticleSystem *psys); diff --git a/source/blender/draw/intern/draw_cache_impl.h b/source/blender/draw/intern/draw_cache_impl.h index 2a1ee0547be..75787161ff8 100644 --- a/source/blender/draw/intern/draw_cache_impl.h +++ b/source/blender/draw/intern/draw_cache_impl.h @@ -35,6 +35,7 @@ struct ListBase; struct ModifierData; struct ParticleSystem; struct PTCacheEdit; +struct Groom; struct HairSystem; struct DRWHairFiberTextureBuffer; struct DerivedMesh; @@ -60,6 +61,9 @@ 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); @@ -95,6 +99,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( 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_manager.c b/source/blender/draw/intern/draw_manager.c index d27e06fb7b9..3dae27ab369 100644 --- a/source/blender/draw/intern/draw_manager.c +++ b/source/blender/draw/intern/draw_manager.c @@ -1070,6 +1070,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; @@ -2027,6 +2030,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); @@ -2057,6 +2061,9 @@ 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; @@ -2076,6 +2083,9 @@ 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 9522e3cddc6..c33c0648d27 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; 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..05764240dbd --- /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, dtxl) + + /* 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, dtxl) + + /* 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 cc60a0f9067..324dd10ceb5 100644 --- a/source/blender/draw/modes/object_mode.c +++ b/source/blender/draw/modes/object_mode.c @@ -30,6 +30,7 @@ #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" @@ -47,6 +48,7 @@ #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" @@ -2115,6 +2117,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; 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 358f82499c2..57a46aecfdc 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 3d17afc2878..381e2d843e3 100644 --- a/source/blender/editors/include/ED_screen.h +++ b/source/blender/editors/include/ED_screen.h @@ -288,6 +288,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 bb86c7ba6c5..ad7348722f2 100644 --- a/source/blender/editors/include/ED_view3d.h +++ b/source/blender/editors/include/ED_view3d.h @@ -43,6 +43,9 @@ struct BoundBox; struct Camera; struct Depsgraph; struct EditBone; +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 6a9067ef95d..35d69dd8d7f 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) DEF_ICON(OUTLINER_OB_LIGHTPROBE) #ifndef DEF_ICON_BLANK_SKIP DEF_ICON(BLANK124) diff --git a/source/blender/editors/object/object_add.c b/source/blender/editors/object/object_add.c index 8d64d8b67e9..dd884edc1b1 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" @@ -71,6 +72,7 @@ #include "BKE_displist.h" #include "BKE_effect.h" #include "BKE_font.h" +#include "BKE_groom.h" #include "BKE_lamp.h" #include "BKE_lattice.h" #include "BKE_layer.h" @@ -763,6 +765,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 364a85c4248..ec08be97321 100644 --- a/source/blender/editors/object/object_edit.c +++ b/source/blender/editors/object/object_edit.c @@ -92,6 +92,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" @@ -257,6 +258,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; } @@ -405,6 +410,12 @@ void ED_object_editmode_enter_ex(Scene *scene, Object *ob, int flag) WM_main_add_notifier(NC_SCENE | ND_MODE | NS_EDITMODE_CURVE, scene); } + else if (ob->type == OB_GROOM) { + ok = 1; + ED_groom_editgroom_make(ob); + + WM_main_add_notifier(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 f25f4bca237..53901486ef2 100644 --- a/source/blender/editors/object/object_intern.h +++ b/source/blender/editors/object/object_intern.h @@ -116,6 +116,7 @@ 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_collection_instance_add(struct wmOperatorType *ot); +void OBJECT_OT_groom_add(struct wmOperatorType *ot); void OBJECT_OT_duplicates_make_real(struct wmOperatorType *ot); void OBJECT_OT_duplicate(struct wmOperatorType *ot); diff --git a/source/blender/editors/object/object_modes.c b/source/blender/editors/object/object_modes.c index 7a30c154f23..3894e819ce0 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_ops.c b/source/blender/editors/object/object_ops.c index 495e336d23e..20c4295d43a 100644 --- a/source/blender/editors/object/object_ops.c +++ b/source/blender/editors/object/object_ops.c @@ -116,6 +116,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); diff --git a/source/blender/editors/object/object_relations.c b/source/blender/editors/object/object_relations.c index c5cf946cfb3..8d991a7a207 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_fcurve.h" #include "BKE_idprop.h" #include "BKE_lamp.h" @@ -1812,6 +1813,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 68c05900520..6f9c5f67138 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" @@ -520,6 +521,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 12f16f1fe42..d7b3552a46f 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" @@ -122,7 +123,8 @@ void ED_spacetypes_init(void) ED_operatortypes_render(); ED_operatortypes_mask(); ED_operatortypes_io(); - + ED_operatortypes_groom(); + ED_operatortypes_view2d(); ED_operatortypes_ui(); @@ -170,6 +172,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(); @@ -203,6 +206,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 207e08c9a44..a72dcce23ac 100644 --- a/source/blender/editors/space_buttons/buttons_context.c +++ b/source/blender/editors/space_buttons/buttons_context.c @@ -229,6 +229,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; @@ -673,7 +674,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", NULL + "line_style", "collection", "groom", NULL }; int buttons_context(const bContext *C, const char *member, bContextDataResult *result) @@ -945,6 +946,9 @@ int buttons_context(const bContext *C, const char *member, bContextDataResult *r set_pointer_type(path, result, &RNA_FreestyleLineStyle); return 1; } + else if (CTX_data_equals(member, "groom")) { + set_pointer_type(path, result, &RNA_Groom); + } 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 e51f96e7d7a..732e7aeb5cc 100644 --- a/source/blender/editors/space_outliner/outliner_draw.c +++ b/source/blender/editors/space_outliner/outliner_draw.c @@ -1092,6 +1092,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 73494b890ed..b1c29343dde 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 393b40d1097..94f9074f0aa 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" @@ -652,6 +653,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 f26da3ad07e..c1f4f9b8043 100644 --- a/source/blender/editors/space_view3d/space_view3d.c +++ b/source/blender/editors/space_view3d/space_view3d.c @@ -503,6 +503,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 35127c7c8c6..99c67172dc5 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 93ecb8a4b61..5db76b68c0b 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" @@ -68,6 +69,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" @@ -95,6 +97,7 @@ #include "ED_screen.h" #include "ED_sculpt.h" #include "ED_mball.h" +#include "ED_groom.h" #include "UI_interface.h" @@ -619,6 +622,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; @@ -877,6 +923,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; @@ -1806,6 +1855,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; @@ -2239,6 +2329,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; @@ -2394,6 +2490,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) @@ -2669,6 +2767,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]) { @@ -2875,6 +3010,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 892187f11db..97152fb7029 100644 --- a/source/blender/editors/transform/transform.h +++ b/source/blender/editors/transform/transform.h @@ -700,6 +700,7 @@ void flushTransSeq(TransInfo *t); void flushTransTracking(TransInfo *t); void flushTransMasking(TransInfo *t); void flushTransPaintCurve(TransInfo *t); +void flushTransGroom(TransInfo *t); void restoreBones(TransDataContainer *tc); /*********************** transform_manipulator.c ********** */ diff --git a/source/blender/editors/transform/transform_conversions.c b/source/blender/editors/transform/transform_conversions.c index 73a722d897d..a82a34c475e 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" @@ -1956,6 +1958,241 @@ 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) +{ + FOREACH_TRANS_DATA_CONTAINER(t, tc) + { + const ToolSettings *tsettings = t->scene->toolsettings; + const bool is_prop_edit = t->flag & T_PROP_EDIT; + EditGroom *edit = ((Groom *)tc->obedit->data)->editgroom; + + switch (tsettings->groom_edit_settings.mode) + { + case GM_EDIT_MODE_REGIONS: + tc->data_len = groom_trans_count_regions(edit, is_prop_edit); + if (tc->data_len > 0) + { + // TODO + groom_transdata_init_regions(edit, is_prop_edit, tc->obedit->obmat, tc->data); + } + break; + case GM_EDIT_MODE_CURVES: + tc->data_len = groom_trans_count_curves(edit, is_prop_edit); + if (tc->data_len > 0) + { + tc->data = MEM_callocN(tc->data_len * sizeof(TransData), "TransData(Groom EditMode)"); + + groom_transdata_init_curves(edit, is_prop_edit, tc->obedit->obmat, tc->data); + } + break; + case GM_EDIT_MODE_SECTIONS: + tc->data_len = groom_trans_count_verts(edit, is_prop_edit); + if (tc->data_len > 0) + { + tc->data = MEM_callocN(tc->data_len * sizeof(TransData), "TransData(Groom EditMode)"); + tc->data_2d = MEM_callocN(tc->data_len * sizeof(TransData2D), "TransData2D(Groom EditMode)"); + + groom_transdata_init_verts(edit, is_prop_edit, tc->obedit->obmat, tc->data, tc->data_2d); + } + break; + } + } +} + +void flushTransGroom(TransInfo *t) +{ + FOREACH_TRANS_DATA_CONTAINER(t, tc) + { + 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 = tc->data_2d; + for (int i = 0; i < tc->data_len; ++i, ++td2d) + { + copy_v2_v2(td2d->loc2d, td2d->loc); + } + break; + } + } + } +} + /* ******************* particle edit **************** */ static void createTransParticleVerts(bContext *C, TransInfo *t) { @@ -8480,6 +8717,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 ed146eb3227..e7a9cbd2ab2 100644 --- a/source/blender/editors/transform/transform_generics.c +++ b/source/blender/editors/transform/transform_generics.c @@ -798,6 +798,13 @@ static void recalcData_objects(TransInfo *t) } } } + else if (t->obedit_type == OB_GROOM) { + flushTransGroom(t); + + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + DEG_id_tag_update(tc->obedit->data, 0); /* sets recalc flags */ + } + } else if (t->obedit_type == OB_MESH) { /* mirror modifier clipping? */ if (t->state != TRANS_CANCEL) { diff --git a/source/blender/makesdna/DNA_ID.h b/source/blender/makesdna/DNA_ID.h index acf054a94d5..97f01213fd9 100644 --- a/source/blender/makesdna/DNA_ID.h +++ b/source/blender/makesdna/DNA_ID.h @@ -356,6 +356,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. */ @@ -551,6 +552,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_object_types.h b/source/blender/makesdna/DNA_object_types.h index 64c67f7d325..a46daabf819 100644 --- a/source/blender/makesdna/DNA_object_types.h +++ b/source/blender/makesdna/DNA_object_types.h @@ -360,6 +360,7 @@ enum { OB_SURF = 3, OB_FONT = 4, OB_MBALL = 5, + OB_GROOM = 6, OB_LAMP = 10, OB_CAMERA = 11, @@ -385,16 +386,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 7b717107df1..315d53203ab 100644 --- a/source/blender/makesdna/DNA_scene_types.h +++ b/source/blender/makesdna/DNA_scene_types.h @@ -861,6 +861,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 */ @@ -1251,7 +1267,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 aa74d66075b..f14f895203b 100644 --- a/source/blender/makesdna/intern/makesdna.c +++ b/source/blender/makesdna/intern/makesdna.c @@ -130,6 +130,7 @@ static const char *includefiles[] = { "DNA_workspace_types.h", "DNA_lightprobe_types.h", "DNA_hair_types.h", + "DNA_groom_types.h", /* see comment above before editing! */ @@ -1355,5 +1356,6 @@ int main(int argc, char **argv) #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 53d13e04c25..3fd432ef15a 100644 --- a/source/blender/makesrna/RNA_access.h +++ b/source/blender/makesrna/RNA_access.h @@ -281,6 +281,8 @@ extern StructRNA RNA_GPencilSculptBrush; extern StructRNA RNA_GaussianBlurSequence; extern StructRNA RNA_GlowSequence; extern StructRNA RNA_GreasePencil; +extern StructRNA RNA_Groom; +extern StructRNA RNA_GroomBundle; extern StructRNA RNA_HairGroup; extern StructRNA RNA_HairPattern; extern StructRNA RNA_Header; diff --git a/source/blender/makesrna/intern/CMakeLists.txt b/source/blender/makesrna/intern/CMakeLists.txt index a4aeca62dad..d16ea2ae48e 100644 --- a/source/blender/makesrna/intern/CMakeLists.txt +++ b/source/blender/makesrna/intern/CMakeLists.txt @@ -49,6 +49,7 @@ set(DEFSRC rna_fcurve.c rna_fluidsim.c rna_gpencil.c + rna_groom.c rna_group.c rna_hair.c rna_image.c diff --git a/source/blender/makesrna/intern/makesrna.c b/source/blender/makesrna/intern/makesrna.c index e949d4afe9e..2cd699b4e5c 100644 --- a/source/blender/makesrna/intern/makesrna.c +++ b/source/blender/makesrna/intern/makesrna.c @@ -3381,6 +3381,7 @@ static RNAProcessItem PROCESS_ITEMS[] = { {"rna_fluidsim.c", NULL, RNA_def_fluidsim}, {"rna_gpencil.c", NULL, RNA_def_gpencil}, {"rna_group.c", NULL, RNA_def_collections}, + {"rna_groom.c", NULL, RNA_def_groom}, {"rna_hair.c", NULL, RNA_def_hair}, {"rna_image.c", "rna_image_api.c", RNA_def_image}, {"rna_key.c", NULL, RNA_def_key}, diff --git a/source/blender/makesrna/intern/rna_ID.c b/source/blender/makesrna/intern/rna_ID.c index 952d27fa18d..30182008c1c 100644 --- a/source/blender/makesrna/intern/rna_ID.c +++ b/source/blender/makesrna/intern/rna_ID.c @@ -224,6 +224,7 @@ StructRNA *ID_code_to_RNA_type(short idcode) case ID_CU: return &RNA_Curve; case ID_GD: return &RNA_GreasePencil; case ID_GR: return &RNA_Collection; + case ID_GM: return &RNA_Groom; case ID_IM: return &RNA_Image; case ID_KE: return &RNA_Key; case ID_LA: return &RNA_Lamp; 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_internal.h b/source/blender/makesrna/intern/rna_internal.h index ea720a93612..8a08aa34078 100644 --- a/source/blender/makesrna/intern/rna_internal.h +++ b/source/blender/makesrna/intern/rna_internal.h @@ -153,6 +153,7 @@ void RNA_def_dynamic_paint(struct BlenderRNA *brna); void RNA_def_fluidsim(struct BlenderRNA *brna); void RNA_def_fcurve(struct BlenderRNA *brna); void RNA_def_gpencil(struct BlenderRNA *brna); +void RNA_def_groom(struct BlenderRNA *brna); void RNA_def_image(struct BlenderRNA *brna); void RNA_def_key(struct BlenderRNA *brna); void RNA_def_lamp(struct BlenderRNA *brna); diff --git a/source/blender/makesrna/intern/rna_main_api.c b/source/blender/makesrna/intern/rna_main_api.c index 56035f8239c..9e1112e1e9a 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_object.c b/source/blender/makesrna/intern/rna_object.c index d9074aed29e..ea35ff7e9a9 100644 --- a/source/blender/makesrna/intern/rna_object.c +++ b/source/blender/makesrna/intern/rna_object.c @@ -148,6 +148,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} }; @@ -385,6 +386,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 1c8e48b94df..3b6bb8900df 100644 --- a/source/blender/makesrna/intern/rna_scene.c +++ b/source/blender/makesrna/intern/rna_scene.c @@ -2530,6 +2530,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 12b080a9284..9bf535af9c3 100644 --- a/source/blender/makesrna/intern/rna_sculpt_paint.c +++ b/source/blender/makesrna/intern/rna_sculpt_paint.c @@ -245,6 +245,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/windowmanager/WM_types.h b/source/blender/windowmanager/WM_types.h index 825948a13a9..ab26518f852 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 @@ -392,6 +393,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 9d26c7e92d5..d1501b2de68 100644 --- a/source/blender/windowmanager/intern/wm_keymap.c +++ b/source/blender/windowmanager/intern/wm_keymap.c @@ -1808,6 +1808,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); } |