Compare commits
186 Commits
temp-ui-po
...
strand_edi
Author | SHA1 | Date | |
---|---|---|---|
fb7c003e99 | |||
caded470a6 | |||
d9a4945bb2 | |||
188874af2f | |||
e3033693f9 | |||
ef67e13bdb | |||
9e8a41aadf | |||
8b607d77e5 | |||
0d4fa23414 | |||
e3b80d6202 | |||
ead00baab1 | |||
45f0f3dc04 | |||
0d67f8d5c4 | |||
438b3f95a1 | |||
1b837226ce | |||
6faa4d77c1 | |||
b0717ad91e | |||
91fd0a487d | |||
e7df5f8528 | |||
6fa838f6d3 | |||
41e8bd9337 | |||
77802b21a6 | |||
8abe6745a4 | |||
53eb011e3d | |||
801c20cebc | |||
9feec51214 | |||
e34ba9fb7a | |||
c92457a87a | |||
bf0f058954 | |||
e35f24fb28 | |||
e8ff620325 | |||
3a438c675f | |||
57cbaa15b6 | |||
679113fbd5 | |||
65d2374c80 | |||
d3349e5e3f | |||
5a7efaf287 | |||
f3bc942370 | |||
07cffae7ea | |||
1b431c3cfe | |||
dd1211ecb8 | |||
1d28579daf | |||
48fc8b76b2 | |||
bd4ca21b54 | |||
28270f0953 | |||
2f596756b7 | |||
56ad4520cd | |||
4e95617769 | |||
548dfdbd31 | |||
73140ca283 | |||
32455e230a | |||
af594b7b85 | |||
5f67ac83bf | |||
b2cb4c448e | |||
216aacbcba | |||
ec51e83798 | |||
9a20f68295 | |||
a85c9b241a | |||
07221e980b | |||
56d8228713 | |||
892529029f | |||
d820eb1265 | |||
3aaca08aad | |||
5f6d9e301e | |||
05281abca1 | |||
38d9301674 | |||
fbc70fa51c | |||
d52bdb6d3d | |||
16b6d6c676 | |||
bf7e8b42fa | |||
20e960215a | |||
9cafd3ae56 | |||
0dccffb7aa | |||
7719a6365a | |||
3a3327dcd5 | |||
8ffcd1f434 | |||
ed1dc43657 | |||
5b9b779cab | |||
4798464e8b | |||
299859e40b | |||
5e42aff397 | |||
3f539c987f | |||
6146b90312 | |||
8b2ec99f57 | |||
410998cdc6 | |||
b6f4e0932d | |||
62fa5c4a84 | |||
2cddc3cda8 | |||
9120df6cb3 | |||
48a86af388 | |||
eacc24ccf1 | |||
ac54ded29b | |||
f7511b3d01 | |||
208ddcde2c | |||
f3b22c5769 | |||
b70c815ac2 | |||
d0a1fc8bb0 | |||
2d1d909817 | |||
e9737da5f2 | |||
1e047231c6 | |||
142ef0b2e9 | |||
56a09434c5 | |||
8672304e30 | |||
f95dde244e | |||
79e649a3bf | |||
e5b25e3c07 | |||
a4664ef88d | |||
be312b1399 | |||
3edc512888 | |||
b2ea8c1022 | |||
339a8b7521 | |||
77da317cb1 | |||
2c0616b034 | |||
a9fbd3b23b | |||
687b5a1ec0 | |||
59efe23830 | |||
d16599ea04 | |||
37d3a3331e | |||
5254df30ba | |||
2590231315 | |||
83b20c7ddf | |||
0e06a60168 | |||
121677d7c8 | |||
1615da133f | |||
7ae6492c85 | |||
7779166313 | |||
833a11f891 | |||
79959152e6 | |||
4fd6a7a696 | |||
98b15d9a7c | |||
a889a1458c | |||
7b6332f634 | |||
0a27e97fb7 | |||
eb5257002b | |||
23b284348b | |||
2d79994e39 | |||
fd34e3efbc | |||
dc43df89cd | |||
4e47239de2 | |||
991ee8a570 | |||
e43ef55e7f | |||
1afd5be044 | |||
d550a29558 | |||
5f44f4a2ff | |||
b0a9e48a19 | |||
afb947c00c | |||
f1e4d35489 | |||
1c7053f7a0 | |||
7bb90a06e1 | |||
a9001adbb4 | |||
dd69a3dbcf | |||
c65f1b2e15 | |||
09a7dbc29a | |||
6078e79cea | |||
6783b15453 | |||
82132fc5b0 | |||
4b4f24607e | |||
fe48c353f1 | |||
308af3bfb3 | |||
3f7d66ece4 | |||
a952800ffc | |||
71e34dad2b | |||
24b77359ae | |||
62aabdd550 | |||
2204bb092b | |||
8374e08894 | |||
822949a3e2 | |||
a86869af8f | |||
ca31ce144e | |||
f61f7e2d57 | |||
3036cedd90 | |||
cc5b3c2ff7 | |||
a30f9ffa87 | |||
3361788b1c | |||
21ab9ac9e3 | |||
18c6bb2436 | |||
a93841e3d4 | |||
728b6ed8e6 | |||
fa0163b8c7 | |||
7112a531b2 | |||
60cde6ca4a | |||
717db95210 | |||
a944cf8450 | |||
a1dd14c764 | |||
47d024c2b0 | |||
30e02b9a19 |
@@ -591,6 +591,7 @@ function(SETUP_BLENDER_SORTED_LIBS)
|
||||
bf_editor_object
|
||||
bf_editor_armature
|
||||
bf_editor_physics
|
||||
bf_editor_hair
|
||||
bf_editor_render
|
||||
bf_editor_scene
|
||||
bf_editor_screen
|
||||
|
BIN
release/datafiles/brushicons/hairadd.png
Normal file
After Width: | Height: | Size: 3.3 KiB |
BIN
release/datafiles/brushicons/haircomb.png
Normal file
After Width: | Height: | Size: 3.3 KiB |
BIN
release/datafiles/brushicons/haircut.png
Normal file
After Width: | Height: | Size: 3.3 KiB |
BIN
release/datafiles/brushicons/hairlength.png
Normal file
After Width: | Height: | Size: 3.3 KiB |
BIN
release/datafiles/brushicons/hairpuff.png
Normal file
After Width: | Height: | Size: 3.3 KiB |
BIN
release/datafiles/brushicons/hairsmooth.png
Normal file
After Width: | Height: | Size: 3.3 KiB |
BIN
release/datafiles/brushicons/hairweight.png
Normal file
After Width: | Height: | Size: 3.3 KiB |
@@ -163,6 +163,7 @@ class BRUSH_OT_active_index_set(Operator):
|
||||
"vertex_paint": "use_paint_vertex",
|
||||
"weight_paint": "use_paint_weight",
|
||||
"image_paint": "use_paint_image",
|
||||
"hair_edit": "use_hair_edit",
|
||||
}
|
||||
|
||||
def execute(self, context):
|
||||
|
@@ -43,6 +43,7 @@ _modules = [
|
||||
"properties_data_lightprobe",
|
||||
"properties_data_speaker",
|
||||
"properties_game",
|
||||
"properties_hair",
|
||||
"properties_mask_common",
|
||||
"properties_material",
|
||||
"properties_object",
|
||||
|
@@ -1528,6 +1528,24 @@ class DATA_PT_modifiers(ModifierButtonsPanel, Panel):
|
||||
if md.rest_source == 'BIND':
|
||||
layout.operator("object.correctivesmooth_bind", text="Unbind" if is_bind else "Bind")
|
||||
|
||||
def HAIR(self, layout, ob, md):
|
||||
hair = md.hair
|
||||
|
||||
col = layout.column()
|
||||
col.label(text="Follicles:")
|
||||
col.label(text="Count: %d" % len(hair.follicles))
|
||||
col.operator("object.hair_follicles_generate", text="Generate")
|
||||
|
||||
col = layout.column()
|
||||
col.template_list("HAIR_UL_groups", "", hair, "groups", hair, "active_group_index")
|
||||
|
||||
layout.separator()
|
||||
|
||||
group = hair.active_group
|
||||
if group:
|
||||
col = layout.column()
|
||||
col.prop(group, "type")
|
||||
|
||||
|
||||
classes = (
|
||||
DATA_PT_modifiers,
|
||||
|
40
release/scripts/startup/bl_ui/properties_hair.py
Normal file
@@ -0,0 +1,40 @@
|
||||
# ##### 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 UIList
|
||||
|
||||
class HAIR_UL_groups(UIList):
|
||||
def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index):
|
||||
group = item
|
||||
if self.layout_type in {'DEFAULT', 'COMPACT'}:
|
||||
layout.prop(group, "name", text="", emboss=False, icon_value=icon)
|
||||
elif self.layout_type == 'GRID':
|
||||
layout.alignment = 'CENTER'
|
||||
layout.label(text="", icon_value=icon)
|
||||
|
||||
|
||||
classes = (
|
||||
HAIR_UL_groups,
|
||||
)
|
||||
|
||||
if __name__ == "__main__": # only for live edit.
|
||||
from bpy.utils import register_class
|
||||
for cls in classes:
|
||||
register_class(cls)
|
@@ -38,10 +38,10 @@ class UnifiedPaintPanel:
|
||||
elif context.image_paint_object:
|
||||
if (toolsettings.image_paint and toolsettings.image_paint.detect_data()):
|
||||
return toolsettings.image_paint
|
||||
|
||||
return None
|
||||
elif context.particle_edit_object:
|
||||
return toolsettings.particle_edit
|
||||
elif context.hair_edit_object:
|
||||
return toolsettings.hair_edit
|
||||
|
||||
return None
|
||||
|
||||
|
@@ -52,6 +52,12 @@ class VIEW3D_HT_header(Header):
|
||||
# Particle edit
|
||||
if mode == 'PARTICLE_EDIT':
|
||||
row.prop(toolsettings.particle_edit, "select_mode", text="", expand=True)
|
||||
elif mode == 'HAIR_EDIT':
|
||||
row.prop(toolsettings.hair_edit, "select_mode", text="", expand=True)
|
||||
row.prop(toolsettings.hair_edit, "hair_draw_mode", text="", expand=True)
|
||||
if toolsettings.hair_edit.hair_draw_mode == 'FIBERS':
|
||||
row.prop(toolsettings.hair_edit, "hair_draw_size", text="Size")
|
||||
row.prop(toolsettings.hair_edit, "hair_draw_subdivision", text="Subdivide")
|
||||
|
||||
# Occlude geometry
|
||||
if ((view.viewport_shade not in {'BOUNDBOX', 'WIREFRAME'} and (mode == 'PARTICLE_EDIT' or (mode == 'EDIT' and obj.type == 'MESH'))) or
|
||||
@@ -172,7 +178,7 @@ class VIEW3D_MT_editor_menus(Menu):
|
||||
layout.menu("VIEW3D_MT_select_paint_mask")
|
||||
elif mesh.use_paint_mask_vertex and mode_string == 'PAINT_WEIGHT':
|
||||
layout.menu("VIEW3D_MT_select_paint_mask_vertex")
|
||||
elif mode_string != 'SCULPT':
|
||||
elif mode_string not in {'SCULPT'}:
|
||||
layout.menu("VIEW3D_MT_select_%s" % mode_string.lower())
|
||||
|
||||
if gp_edit:
|
||||
@@ -197,7 +203,7 @@ class VIEW3D_MT_editor_menus(Menu):
|
||||
elif obj:
|
||||
if mode_string != 'PAINT_TEXTURE':
|
||||
layout.menu("VIEW3D_MT_%s" % mode_string.lower())
|
||||
if mode_string in {'SCULPT', 'PAINT_VERTEX', 'PAINT_WEIGHT', 'PAINT_TEXTURE'}:
|
||||
if mode_string in {'SCULPT', 'PAINT_VERTEX', 'PAINT_WEIGHT', 'PAINT_TEXTURE', 'HAIR'}:
|
||||
layout.menu("VIEW3D_MT_brush")
|
||||
if mode_string == 'SCULPT':
|
||||
layout.menu("VIEW3D_MT_hide_mask")
|
||||
@@ -1072,6 +1078,13 @@ class VIEW3D_MT_select_paint_mask_vertex(Menu):
|
||||
layout.operator("paint.vert_select_ungrouped", text="Ungrouped Verts")
|
||||
|
||||
|
||||
class VIEW3D_MT_select_hair(Menu):
|
||||
bl_label = "Select"
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
|
||||
|
||||
class VIEW3D_MT_angle_control(Menu):
|
||||
bl_label = "Angle Control"
|
||||
|
||||
@@ -1712,7 +1725,7 @@ class VIEW3D_MT_brush(Menu):
|
||||
return
|
||||
|
||||
# brush paint modes
|
||||
layout.menu("VIEW3D_MT_brush_paint_modes")
|
||||
layout.menu("VIEW3D_MT_brush_object_modes")
|
||||
|
||||
# brush tool
|
||||
if context.sculpt_object:
|
||||
@@ -1722,6 +1735,8 @@ class VIEW3D_MT_brush(Menu):
|
||||
layout.prop_menu_enum(brush, "image_tool")
|
||||
elif context.vertex_paint_object or context.weight_paint_object:
|
||||
layout.prop_menu_enum(brush, "vertex_tool")
|
||||
elif context.hair_edit_object:
|
||||
layout.prop_menu_enum(brush, "hair_tool")
|
||||
|
||||
# TODO: still missing a lot of brush options here
|
||||
|
||||
@@ -1745,7 +1760,7 @@ class VIEW3D_MT_brush(Menu):
|
||||
layout.operator("sculpt.set_persistent_base")
|
||||
|
||||
|
||||
class VIEW3D_MT_brush_paint_modes(Menu):
|
||||
class VIEW3D_MT_brush_object_modes(Menu):
|
||||
bl_label = "Enabled Modes"
|
||||
|
||||
def draw(self, context):
|
||||
@@ -1758,6 +1773,7 @@ class VIEW3D_MT_brush_paint_modes(Menu):
|
||||
layout.prop(brush, "use_paint_vertex", text="Vertex Paint")
|
||||
layout.prop(brush, "use_paint_weight", text="Weight Paint")
|
||||
layout.prop(brush, "use_paint_image", text="Texture Paint")
|
||||
layout.prop(brush, "use_hair_edit", text="Hair Edit")
|
||||
|
||||
|
||||
# ********** Vertex paint menu **********
|
||||
@@ -2022,6 +2038,14 @@ class VIEW3D_MT_particle_specials(Menu):
|
||||
class VIEW3D_MT_particle_showhide(ShowHideMenu, Menu):
|
||||
_operator_name = "particle"
|
||||
|
||||
# ********** Hair menu **********
|
||||
|
||||
class VIEW3D_MT_hair(Menu):
|
||||
bl_label = "Hair"
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
|
||||
|
||||
# ********** Pose Menu **********
|
||||
|
||||
@@ -3774,6 +3798,7 @@ classes = (
|
||||
VIEW3D_MT_select_pose,
|
||||
VIEW3D_MT_select_pose_more_less,
|
||||
VIEW3D_MT_select_particle,
|
||||
VIEW3D_MT_select_hair,
|
||||
VIEW3D_MT_edit_mesh,
|
||||
VIEW3D_MT_edit_mesh_select_similar,
|
||||
VIEW3D_MT_edit_mesh_select_by_trait,
|
||||
@@ -3814,7 +3839,7 @@ classes = (
|
||||
VIEW3D_MT_make_links,
|
||||
VIEW3D_MT_object_game,
|
||||
VIEW3D_MT_brush,
|
||||
VIEW3D_MT_brush_paint_modes,
|
||||
VIEW3D_MT_brush_object_modes,
|
||||
VIEW3D_MT_paint_vertex,
|
||||
VIEW3D_MT_hook,
|
||||
VIEW3D_MT_vertex_group,
|
||||
@@ -3824,6 +3849,7 @@ classes = (
|
||||
VIEW3D_MT_particle,
|
||||
VIEW3D_MT_particle_specials,
|
||||
VIEW3D_MT_particle_showhide,
|
||||
VIEW3D_MT_hair,
|
||||
VIEW3D_MT_pose,
|
||||
VIEW3D_MT_pose_transform,
|
||||
VIEW3D_MT_pose_slide,
|
||||
|
@@ -974,6 +974,21 @@ class VIEW3D_PT_tools_brush(Panel, View3DPaintPanel):
|
||||
layout.row().prop(brush, "puff_mode", expand=True)
|
||||
layout.prop(brush, "use_puff_volume")
|
||||
|
||||
# Hair Mode #
|
||||
|
||||
elif context.hair_edit_object and brush:
|
||||
col = layout.column()
|
||||
|
||||
row = col.row(align=True)
|
||||
self.prop_unified_size(row, context, brush, "size", slider=True, text="Radius")
|
||||
self.prop_unified_size(row, context, brush, "use_pressure_size")
|
||||
|
||||
row = col.row(align=True)
|
||||
self.prop_unified_strength(row, context, brush, "strength", text="Strength")
|
||||
self.prop_unified_strength(row, context, brush, "use_pressure_strength")
|
||||
|
||||
col.prop(brush, "hair_tool", text="Tool")
|
||||
|
||||
# Sculpt Mode #
|
||||
|
||||
elif context.sculpt_object and brush:
|
||||
@@ -1958,6 +1973,23 @@ class VIEW3D_PT_tools_particlemode(View3DPanel, Panel):
|
||||
sub.prop(pe, "fade_frames", slider=True)
|
||||
|
||||
|
||||
class VIEW3D_PT_tools_hairmode(View3DPanel, Panel):
|
||||
"""Tools for hair mode"""
|
||||
bl_context = "hairmode"
|
||||
bl_label = "Options"
|
||||
bl_category = "Tools"
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
|
||||
settings = context.tool_settings.hair_edit
|
||||
ob = context.active_object
|
||||
|
||||
if ob.data:
|
||||
col = layout.column(align=True)
|
||||
col.prop(ob.data, "use_mirror_x")
|
||||
|
||||
|
||||
# Grease Pencil drawing tools
|
||||
class VIEW3D_PT_tools_grease_pencil_draw(GreasePencilDrawingToolsPanel, Panel):
|
||||
bl_space_type = 'VIEW_3D'
|
||||
|
@@ -116,6 +116,7 @@ enum {
|
||||
CTX_MODE_PAINT_VERTEX,
|
||||
CTX_MODE_PAINT_TEXTURE,
|
||||
CTX_MODE_PARTICLE,
|
||||
CTX_MODE_HAIR,
|
||||
CTX_MODE_OBJECT
|
||||
};
|
||||
|
||||
|
@@ -57,6 +57,8 @@ extern const CustomDataMask CD_MASK_EDITMESH;
|
||||
extern const CustomDataMask CD_MASK_DERIVEDMESH;
|
||||
extern const CustomDataMask CD_MASK_BMESH;
|
||||
extern const CustomDataMask CD_MASK_FACECORNERS;
|
||||
extern const CustomDataMask CD_MASK_STRANDS;
|
||||
extern const CustomDataMask CD_MASK_STRANDS_BMESH;
|
||||
extern const CustomDataMask CD_MASK_EVERYTHING;
|
||||
|
||||
/* for ORIGINDEX layer type, indicates no original index for this element */
|
||||
@@ -269,6 +271,7 @@ void *CustomData_get(const struct CustomData *data, int index, int type);
|
||||
void *CustomData_get_n(const struct CustomData *data, int type, int index, int n);
|
||||
void *CustomData_bmesh_get(const struct CustomData *data, void *block, int type);
|
||||
void *CustomData_bmesh_get_n(const struct CustomData *data, void *block, int type, int n);
|
||||
void *CustomData_bmesh_get_named(const struct CustomData *data, void *block, int type, const char *name);
|
||||
|
||||
/* gets the layer at physical index n, with no type checking.
|
||||
*/
|
||||
|
106
source/blender/blenkernel/BKE_editstrands.h
Normal file
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
* ***** BEGIN GPL LICENSE BLOCK *****
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* The Original Code is Copyright (C) Blender Foundation
|
||||
* All rights reserved.
|
||||
*
|
||||
* The Original Code is: all of this file.
|
||||
*
|
||||
* Contributor(s): Lukas Toenne
|
||||
*
|
||||
* ***** END GPL LICENSE BLOCK *****
|
||||
*/
|
||||
|
||||
#ifndef __BKE_EDITSTRANDS_H__
|
||||
#define __BKE_EDITSTRANDS_H__
|
||||
|
||||
/** \file blender/blenkernel/BKE_editstrands.h
|
||||
* \ingroup bke
|
||||
*/
|
||||
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
#include "DNA_customdata_types.h"
|
||||
|
||||
#include "BKE_customdata.h"
|
||||
#include "BKE_editmesh.h"
|
||||
|
||||
#include "bmesh.h"
|
||||
|
||||
struct BMesh;
|
||||
struct DerivedMesh;
|
||||
struct Mesh;
|
||||
struct Object;
|
||||
|
||||
typedef struct BMEditStrands {
|
||||
BMEditMesh base;
|
||||
|
||||
/* Scalp mesh for fixing root vertices */
|
||||
struct DerivedMesh *root_dm;
|
||||
|
||||
int flag;
|
||||
|
||||
unsigned int vertex_glbuf; // legacy gpu code
|
||||
unsigned int elem_glbuf; // legacy gpu code
|
||||
unsigned int dot_glbuf; // legacy gpu code
|
||||
void *batch_cache;
|
||||
} BMEditStrands;
|
||||
|
||||
/* BMEditStrands->flag */
|
||||
typedef enum BMEditStrandsFlag {
|
||||
BM_STRANDS_DIRTY_SEGLEN = (1 << 0),
|
||||
BM_STRANDS_DIRTY_ROOTS = (1 << 1),
|
||||
} BMEditStrandsFlag;
|
||||
|
||||
struct BMEditStrands *BKE_editstrands_create(struct BMesh *bm, struct DerivedMesh *root_dm);
|
||||
struct BMEditStrands *BKE_editstrands_copy(struct BMEditStrands *es);
|
||||
struct BMEditStrands *BKE_editstrands_from_object_particles(struct Object *ob, struct ParticleSystem **r_psys);
|
||||
struct BMEditStrands *BKE_editstrands_from_object(struct Object *ob);
|
||||
void BKE_editstrands_update_linked_customdata(struct BMEditStrands *es);
|
||||
void BKE_editstrands_free(struct BMEditStrands *es);
|
||||
|
||||
/* === Constraints === */
|
||||
|
||||
/* Stores vertex locations for temporary reference:
|
||||
* Vertex locations get modified by tools, but then need to be corrected
|
||||
* by calculating a smooth solution based on the difference to original pre-tool locations.
|
||||
*/
|
||||
typedef float (*BMEditStrandsLocations)[3];
|
||||
BMEditStrandsLocations BKE_editstrands_get_locations(struct BMEditStrands *edit);
|
||||
void BKE_editstrands_free_locations(BMEditStrandsLocations locs);
|
||||
|
||||
void BKE_editstrands_solve_constraints(struct Object *ob, struct BMEditStrands *es, BMEditStrandsLocations orig);
|
||||
void BKE_editstrands_ensure(struct BMEditStrands *es);
|
||||
|
||||
/* === Particle Conversion === */
|
||||
|
||||
struct BMesh *BKE_editstrands_particles_to_bmesh(struct Object *ob, struct ParticleSystem *psys);
|
||||
void BKE_editstrands_particles_from_bmesh(struct Object *ob, struct ParticleSystem *psys);
|
||||
|
||||
/* === Mesh Conversion === */
|
||||
struct BMesh *BKE_editstrands_mesh_to_bmesh(struct Object *ob, struct Mesh *me);
|
||||
void BKE_editstrands_mesh_from_bmesh(struct Object *ob);
|
||||
|
||||
/* === Draw Cache === */
|
||||
enum {
|
||||
BKE_STRANDS_BATCH_DIRTY_ALL = 0,
|
||||
BKE_STRANDS_BATCH_DIRTY_SELECT = 1,
|
||||
};
|
||||
void BKE_editstrands_batch_cache_dirty(struct BMEditStrands *es, int mode);
|
||||
void BKE_editstrands_batch_cache_free(struct BMEditStrands *es);
|
||||
|
||||
#endif
|
94
source/blender/blenkernel/BKE_hair.h
Normal file
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* ***** BEGIN GPL LICENSE BLOCK *****
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* The Original Code is Copyright (C) Blender Foundation
|
||||
* All rights reserved.
|
||||
*
|
||||
* The Original Code is: all of this file.
|
||||
*
|
||||
* Contributor(s): Lukas Toenne
|
||||
*
|
||||
* ***** END GPL LICENSE BLOCK *****
|
||||
*/
|
||||
|
||||
#ifndef __BKE_HAIR_H__
|
||||
#define __BKE_HAIR_H__
|
||||
|
||||
/** \file blender/blenkernel/BKE_hair.h
|
||||
* \ingroup bke
|
||||
*/
|
||||
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
struct HairFollicle;
|
||||
struct HairPattern;
|
||||
struct HairGroup;
|
||||
struct DerivedMesh;
|
||||
|
||||
static const unsigned int STRAND_INDEX_NONE = 0xFFFFFFFF;
|
||||
|
||||
struct HairPattern* BKE_hair_new(void);
|
||||
struct HairPattern* BKE_hair_copy(struct HairPattern *hair);
|
||||
void BKE_hair_free(struct HairPattern *hair);
|
||||
|
||||
void BKE_hair_set_num_follicles(struct HairPattern *hair, int count);
|
||||
void BKE_hair_follicles_generate(struct HairPattern *hair, struct DerivedMesh *scalp, int count, unsigned int seed);
|
||||
|
||||
struct HairGroup* BKE_hair_group_new(struct HairPattern *hair, int type);
|
||||
void BKE_hair_group_remove(struct HairPattern *hair, struct HairGroup *group);
|
||||
struct HairGroup* BKE_hair_group_copy(struct HairPattern *hair, struct HairGroup *group);
|
||||
void BKE_hair_group_moveto(struct HairPattern *hair, struct HairGroup *group, int position);
|
||||
|
||||
void BKE_hair_group_name_set(struct HairPattern *hair, struct HairGroup *group, const char *name);
|
||||
|
||||
void BKE_hair_update_groups(struct HairPattern *hair);
|
||||
|
||||
/* === Draw Buffer Texture === */
|
||||
|
||||
typedef struct HairDrawDataInterface {
|
||||
const struct HairGroup *group;
|
||||
struct DerivedMesh *scalp;
|
||||
|
||||
int (*get_num_strands)(const struct HairDrawDataInterface* hairdata);
|
||||
int (*get_num_verts)(const struct HairDrawDataInterface* hairdata);
|
||||
|
||||
void (*get_strand_lengths)(const struct HairDrawDataInterface* hairdata, int *r_lengths);
|
||||
void (*get_strand_roots)(const struct HairDrawDataInterface* hairdata, struct MeshSample *r_roots);
|
||||
void (*get_strand_vertices)(const struct HairDrawDataInterface* hairdata, float (*r_positions)[3]);
|
||||
} HairDrawDataInterface;
|
||||
|
||||
int* BKE_hair_strands_get_fiber_lengths(const struct HairDrawDataInterface *hairdata, int subdiv);
|
||||
void BKE_hair_strands_get_texture_buffer_size(const struct HairDrawDataInterface *hairdata, int subdiv,
|
||||
int *r_size, int *r_strand_map_start,
|
||||
int *r_strand_vertex_start, int *r_fiber_start);
|
||||
void BKE_hair_strands_get_texture_buffer(const struct HairDrawDataInterface *hairdata, int subdiv, void *texbuffer);
|
||||
|
||||
/* === Draw Cache === */
|
||||
|
||||
enum {
|
||||
BKE_HAIR_BATCH_DIRTY_ALL = 0,
|
||||
};
|
||||
void BKE_hair_batch_cache_dirty(struct HairGroup *group, int mode);
|
||||
void BKE_hair_batch_cache_all_dirty(struct HairPattern *hair, int mode);
|
||||
void BKE_hair_batch_cache_free(struct HairGroup *group);
|
||||
|
||||
int* BKE_hair_group_get_fiber_lengths(struct HairGroup *group, struct DerivedMesh *scalp, int subdiv);
|
||||
void BKE_hair_group_get_texture_buffer_size(struct HairGroup *group, struct DerivedMesh *scalp, int subdiv,
|
||||
int *r_size, int *r_strand_map_start, int *r_strand_vertex_start, int *r_fiber_start);
|
||||
void BKE_hair_group_get_texture_buffer(struct HairGroup *group, struct DerivedMesh *scalp, int subdiv, void *texbuffer);
|
||||
|
||||
#endif
|
110
source/blender/blenkernel/BKE_mesh_sample.h
Normal file
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
* ***** BEGIN GPL LICENSE BLOCK *****
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* ***** END GPL LICENSE BLOCK *****
|
||||
*/
|
||||
|
||||
#ifndef __BKE_MESH_SAMPLE_H__
|
||||
#define __BKE_MESH_SAMPLE_H__
|
||||
|
||||
/** \file BKE_mesh_sample.h
|
||||
* \ingroup bke
|
||||
*/
|
||||
|
||||
struct DerivedMesh;
|
||||
struct Key;
|
||||
struct KeyBlock;
|
||||
struct MFace;
|
||||
struct MVert;
|
||||
|
||||
struct MeshSample;
|
||||
struct MeshSampleGenerator;
|
||||
|
||||
typedef struct MeshSampleGenerator MeshSampleGenerator;
|
||||
typedef float (*MeshSampleVertexWeightFp)(struct DerivedMesh *dm, struct MVert *vert, unsigned int index, void *userdata);
|
||||
typedef void* (*MeshSampleThreadContextCreateFp)(void *userdata, int start);
|
||||
typedef void (*MeshSampleThreadContextFreeFp)(void *userdata, void *thread_ctx);
|
||||
typedef bool (*MeshSampleRayFp)(void *userdata, void *thread_ctx, float ray_start[3], float ray_end[3]);
|
||||
|
||||
/* ==== Utility Functions ==== */
|
||||
|
||||
float* BKE_mesh_sample_calc_triangle_weights(struct DerivedMesh *dm, MeshSampleVertexWeightFp vertex_weight_cb, void *userdata, float *r_area);
|
||||
|
||||
|
||||
/* ==== Evaluate ==== */
|
||||
|
||||
bool BKE_mesh_sample_is_volume_sample(const struct MeshSample *sample);
|
||||
|
||||
bool BKE_mesh_sample_eval(struct DerivedMesh *dm, const struct MeshSample *sample, float loc[3], float nor[3], float tang[3]);
|
||||
bool BKE_mesh_sample_shapekey(struct Key *key, struct KeyBlock *kb, const struct MeshSample *sample, float loc[3]);
|
||||
|
||||
void BKE_mesh_sample_clear(struct MeshSample *sample);
|
||||
|
||||
|
||||
/* ==== Generator Types ==== */
|
||||
|
||||
struct MeshSampleGenerator *BKE_mesh_sample_gen_surface_vertices(void);
|
||||
|
||||
/* vertex_weight_cb is optional */
|
||||
struct MeshSampleGenerator *BKE_mesh_sample_gen_surface_random(unsigned int seed, bool use_area_weight,
|
||||
MeshSampleVertexWeightFp vertex_weight_cb, void *userdata);
|
||||
|
||||
struct MeshSampleGenerator *BKE_mesh_sample_gen_surface_raycast(
|
||||
MeshSampleThreadContextCreateFp thread_context_create_cb,
|
||||
MeshSampleThreadContextFreeFp thread_context_free_cb,
|
||||
MeshSampleRayFp ray_cb,
|
||||
void *userdata);
|
||||
|
||||
struct MeshSampleGenerator *BKE_mesh_sample_gen_surface_poissondisk(unsigned int seed, float mindist, unsigned int max_samples,
|
||||
MeshSampleVertexWeightFp vertex_weight_cb, void *userdata);
|
||||
|
||||
struct MeshSampleGenerator *BKE_mesh_sample_gen_volume_random_bbray(unsigned int seed, float density);
|
||||
|
||||
void BKE_mesh_sample_free_generator(struct MeshSampleGenerator *gen);
|
||||
|
||||
|
||||
/* ==== Sampling ==== */
|
||||
|
||||
void BKE_mesh_sample_generator_bind(struct MeshSampleGenerator *gen, struct DerivedMesh *dm);
|
||||
void BKE_mesh_sample_generator_unbind(struct MeshSampleGenerator *gen);
|
||||
|
||||
unsigned int BKE_mesh_sample_gen_get_max_samples(const struct MeshSampleGenerator *gen);
|
||||
|
||||
/* Generate a single sample.
|
||||
* Not threadsafe!
|
||||
*/
|
||||
bool BKE_mesh_sample_generate(struct MeshSampleGenerator *gen, struct MeshSample *sample);
|
||||
|
||||
/* Generate a large number of samples.
|
||||
*/
|
||||
int BKE_mesh_sample_generate_batch_ex(struct MeshSampleGenerator *gen,
|
||||
void *output_buffer, int output_stride, int count,
|
||||
bool use_threads);
|
||||
|
||||
int BKE_mesh_sample_generate_batch(struct MeshSampleGenerator *gen,
|
||||
MeshSample *output_buffer, int count);
|
||||
|
||||
/* ==== Utilities ==== */
|
||||
|
||||
struct ParticleSystem;
|
||||
struct ParticleData;
|
||||
struct BVHTreeFromMesh;
|
||||
|
||||
bool BKE_mesh_sample_from_particle(struct MeshSample *sample, struct ParticleSystem *psys, struct DerivedMesh *dm, struct ParticleData *pa);
|
||||
bool BKE_mesh_sample_to_particle(struct MeshSample *sample, struct ParticleSystem *psys, struct DerivedMesh *dm, struct BVHTreeFromMesh *bvhtree, struct ParticleData *pa);
|
||||
|
||||
#endif /* __BKE_MESH_SAMPLE_H__ */
|
@@ -420,6 +420,7 @@ void psys_interpolate_face(struct MVert *mvert, struct MFace *mface, struct MTFa
|
||||
float orco[3], float ornor[3]);
|
||||
float psys_particle_value_from_verts(struct DerivedMesh *dm, short from, struct ParticleData *pa, float *values);
|
||||
void psys_get_from_key(struct ParticleKey *key, float loc[3], float vel[3], float rot[4], float *time);
|
||||
int psys_get_index_on_dm(struct ParticleSystem *psys, struct DerivedMesh *dm, ParticleData *pa, int *mapindex, float mapfw[4]);
|
||||
|
||||
/* BLI_bvhtree_ray_cast callback */
|
||||
void BKE_psys_collision_neartest_cb(void *userdata, int index, const struct BVHTreeRay *ray, struct BVHTreeRayHit *hit);
|
||||
|
@@ -103,6 +103,7 @@ set(SRC
|
||||
intern/editmesh.c
|
||||
intern/editmesh_bvh.c
|
||||
intern/editmesh_tangent.c
|
||||
intern/editstrands.c
|
||||
intern/effect.c
|
||||
intern/fcurve.c
|
||||
intern/fluidsim.c
|
||||
@@ -111,6 +112,8 @@ set(SRC
|
||||
intern/freestyle.c
|
||||
intern/gpencil.c
|
||||
intern/group.c
|
||||
intern/hair.c
|
||||
intern/hair_draw.c
|
||||
intern/icons.c
|
||||
intern/idcode.c
|
||||
intern/idprop.c
|
||||
@@ -135,6 +138,7 @@ set(SRC
|
||||
intern/mesh_evaluate.c
|
||||
intern/mesh_mapping.c
|
||||
intern/mesh_remap.c
|
||||
intern/mesh_sample.c
|
||||
intern/mesh_tangent.c
|
||||
intern/mesh_validate.c
|
||||
intern/modifier.c
|
||||
@@ -233,6 +237,7 @@ set(SRC
|
||||
BKE_deform.h
|
||||
BKE_displist.h
|
||||
BKE_dynamicpaint.h
|
||||
BKE_editstrands.h
|
||||
BKE_editmesh.h
|
||||
BKE_editmesh_bvh.h
|
||||
BKE_editmesh_tangent.h
|
||||
@@ -244,6 +249,7 @@ set(SRC
|
||||
BKE_global.h
|
||||
BKE_gpencil.h
|
||||
BKE_group.h
|
||||
BKE_hair.h
|
||||
BKE_icons.h
|
||||
BKE_idcode.h
|
||||
BKE_idprop.h
|
||||
@@ -265,6 +271,7 @@ set(SRC
|
||||
BKE_mesh.h
|
||||
BKE_mesh_mapping.h
|
||||
BKE_mesh_remap.h
|
||||
BKE_mesh_sample.h
|
||||
BKE_mesh_tangent.h
|
||||
BKE_modifier.h
|
||||
BKE_movieclip.h
|
||||
|
@@ -70,7 +70,7 @@ static void brush_defaults(Brush *brush)
|
||||
brush->blend = 0;
|
||||
brush->flag = 0;
|
||||
|
||||
brush->ob_mode = OB_MODE_ALL_PAINT;
|
||||
brush->ob_mode = OB_MODE_ALL_BRUSH;
|
||||
|
||||
/* BRUSH SCULPT TOOL SETTINGS */
|
||||
brush->weight = 1.0f; /* weight of brush 0 - 1.0 */
|
||||
|
@@ -1002,6 +1002,7 @@ int CTX_data_mode_enum_ex(const Object *obedit, const Object *ob)
|
||||
else if (ob->mode & OB_MODE_VERTEX_PAINT) return CTX_MODE_PAINT_VERTEX;
|
||||
else if (ob->mode & OB_MODE_TEXTURE_PAINT) return CTX_MODE_PAINT_TEXTURE;
|
||||
else if (ob->mode & OB_MODE_PARTICLE_EDIT) return CTX_MODE_PARTICLE;
|
||||
else if (ob->mode & OB_MODE_HAIR_EDIT) return CTX_MODE_HAIR;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1031,6 +1032,7 @@ static const char *data_mode_strings[] = {
|
||||
"vertexpaint",
|
||||
"imagepaint",
|
||||
"particlemode",
|
||||
"hairmode",
|
||||
"objectmode",
|
||||
NULL
|
||||
};
|
||||
|
@@ -52,6 +52,7 @@
|
||||
|
||||
#include "BKE_customdata.h"
|
||||
#include "BKE_customdata_file.h"
|
||||
#include "BKE_editstrands.h"
|
||||
#include "BKE_global.h"
|
||||
#include "BKE_main.h"
|
||||
#include "BKE_mesh_mapping.h"
|
||||
@@ -1295,6 +1296,8 @@ static const LayerTypeInfo LAYERTYPEINFO[CD_NUMTYPES] = {
|
||||
{sizeof(short[4][3]), "", 0, NULL, NULL, NULL, NULL, layerSwap_flnor, NULL},
|
||||
/* 41: CD_CUSTOMLOOPNORMAL */
|
||||
{sizeof(short[2]), "vec2s", 1, NULL, NULL, NULL, NULL, NULL, NULL},
|
||||
/* 42: CD_MESH_SAMPLE */
|
||||
{sizeof(MeshSample), "MeshSample", 1, NULL, NULL, NULL, NULL, NULL, NULL},
|
||||
};
|
||||
|
||||
|
||||
@@ -1310,7 +1313,7 @@ static const char *LAYERTYPENAMES[CD_NUMTYPES] = {
|
||||
/* 30-34 */ "CDSubSurfCrease", "CDOrigSpaceLoop", "CDPreviewLoopCol", "CDBMElemPyPtr", "CDPaintMask",
|
||||
/* 35-36 */ "CDGridPaintMask", "CDMVertSkin",
|
||||
/* 37-38 */ "CDFreestyleEdge", "CDFreestyleFace",
|
||||
/* 39-41 */ "CDMLoopTangent", "CDTessLoopNormal", "CDCustomLoopNormal",
|
||||
/* 39-42 */ "CDMLoopTangent", "CDTessLoopNormal", "CDCustomLoopNormal", "CDMeshSample",
|
||||
};
|
||||
|
||||
|
||||
@@ -1355,6 +1358,17 @@ const CustomDataMask CD_MASK_FACECORNERS =
|
||||
CD_MASK_ORIGSPACE | CD_MASK_ORIGSPACE_MLOOP |
|
||||
CD_MASK_TESSLOOPNORMAL | CD_MASK_NORMAL |
|
||||
CD_MASK_TANGENT | CD_MASK_MLOOPTANGENT;
|
||||
const CustomDataMask CD_MASK_STRANDS =
|
||||
CD_MASK_MVERT | CD_MASK_MEDGE |
|
||||
CD_MASK_MDEFORMVERT | CD_MASK_MCOL |
|
||||
CD_MASK_PROP_FLT | CD_MASK_PROP_INT | CD_MASK_PROP_STR | CD_MASK_MDISPS |
|
||||
CD_MASK_MVERT_SKIN | CD_MASK_FREESTYLE_EDGE |
|
||||
CD_MASK_MSURFACE_SAMPLE;
|
||||
const CustomDataMask CD_MASK_STRANDS_BMESH =
|
||||
CD_MASK_MDEFORMVERT | CD_MASK_PROP_FLT | CD_MASK_PROP_INT |
|
||||
CD_MASK_PROP_STR | CD_MASK_SHAPEKEY | CD_MASK_SHAPE_KEYINDEX | CD_MASK_MDISPS |
|
||||
CD_MASK_MVERT_SKIN | CD_MASK_FREESTYLE_EDGE |
|
||||
CD_MASK_MSURFACE_SAMPLE;
|
||||
const CustomDataMask CD_MASK_EVERYTHING =
|
||||
CD_MASK_MVERT | CD_MASK_MDEFORMVERT | CD_MASK_MEDGE | CD_MASK_MFACE |
|
||||
CD_MASK_MTFACE | CD_MASK_MCOL | CD_MASK_ORIGINDEX | CD_MASK_NORMAL /* | CD_MASK_POLYINDEX */ | CD_MASK_PROP_FLT |
|
||||
@@ -2900,6 +2914,18 @@ void *CustomData_bmesh_get_layer_n(const CustomData *data, void *block, int n)
|
||||
return POINTER_OFFSET(block, data->layers[n].offset);
|
||||
}
|
||||
|
||||
/*Bmesh Custom Data Functions. Should replace editmesh ones with these as well, due to more effecient memory alloc*/
|
||||
void *CustomData_bmesh_get_named(const CustomData *data, void *block, int type, const char *name)
|
||||
{
|
||||
int layer_index;
|
||||
|
||||
/* get the layer index of the named layer of type */
|
||||
layer_index = CustomData_get_named_layer_index(data, type, name);
|
||||
if (layer_index == -1) return NULL;
|
||||
|
||||
return (char *)block + data->layers[layer_index].offset;
|
||||
}
|
||||
|
||||
bool CustomData_layer_has_math(const struct CustomData *data, int layer_n)
|
||||
{
|
||||
const LayerTypeInfo *typeInfo = layerType_getInfo(data->layers[layer_n].type);
|
||||
|
300
source/blender/blenkernel/intern/editstrands.c
Normal file
@@ -0,0 +1,300 @@
|
||||
/*
|
||||
* ***** BEGIN GPL LICENSE BLOCK *****
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* The Original Code is Copyright (C) Blender Foundation
|
||||
* All rights reserved.
|
||||
*
|
||||
* The Original Code is: all of this file.
|
||||
*
|
||||
* Contributor(s): Lukas Toenne
|
||||
*
|
||||
* ***** END GPL LICENSE BLOCK *****
|
||||
*/
|
||||
|
||||
/** \file blender/blenkernel/intern/editstrands.c
|
||||
* \ingroup bke
|
||||
*/
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "BLI_listbase.h"
|
||||
#include "BLI_math.h"
|
||||
#include "BLI_mempool.h"
|
||||
|
||||
#include "DNA_customdata_types.h"
|
||||
#include "DNA_key_types.h"
|
||||
#include "DNA_mesh_types.h"
|
||||
#include "DNA_modifier_types.h"
|
||||
#include "DNA_object_types.h"
|
||||
#include "DNA_particle_types.h"
|
||||
|
||||
#include "BKE_bvhutils.h"
|
||||
#include "BKE_customdata.h"
|
||||
#include "BKE_cdderivedmesh.h"
|
||||
#include "BKE_DerivedMesh.h"
|
||||
#include "BKE_editstrands.h"
|
||||
#include "BKE_effect.h"
|
||||
#include "BKE_hair.h"
|
||||
#include "BKE_mesh_sample.h"
|
||||
#include "BKE_object.h"
|
||||
#include "BKE_particle.h"
|
||||
|
||||
#include "BPH_strands.h"
|
||||
|
||||
#include "intern/bmesh_mesh_conv.h"
|
||||
#include "intern/bmesh_strands_conv.h"
|
||||
|
||||
BMEditStrands *BKE_editstrands_create(BMesh *bm, DerivedMesh *root_dm)
|
||||
{
|
||||
BMEditStrands *es = MEM_callocN(sizeof(BMEditStrands), __func__);
|
||||
|
||||
es->base.bm = bm;
|
||||
es->root_dm = CDDM_copy(root_dm);
|
||||
|
||||
BKE_editstrands_batch_cache_dirty(es, BKE_STRANDS_BATCH_DIRTY_ALL);
|
||||
|
||||
return es;
|
||||
}
|
||||
|
||||
BMEditStrands *BKE_editstrands_copy(BMEditStrands *es)
|
||||
{
|
||||
BMEditStrands *es_copy = MEM_callocN(sizeof(BMEditStrands), __func__);
|
||||
*es_copy = *es;
|
||||
|
||||
es_copy->base.bm = BM_mesh_copy(es->base.bm);
|
||||
es_copy->root_dm = CDDM_copy(es->root_dm);
|
||||
|
||||
BKE_editstrands_batch_cache_dirty(es_copy, BKE_STRANDS_BATCH_DIRTY_ALL);
|
||||
|
||||
return es_copy;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Return the BMEditStrands for a given object's particle systems
|
||||
*/
|
||||
BMEditStrands *BKE_editstrands_from_object_particles(Object *ob, ParticleSystem **r_psys)
|
||||
{
|
||||
ParticleSystem *psys = psys_get_current(ob);
|
||||
if (psys && psys->hairedit) {
|
||||
if (r_psys) {
|
||||
*r_psys = psys;
|
||||
}
|
||||
return psys->hairedit;
|
||||
}
|
||||
|
||||
if (r_psys) {
|
||||
*r_psys = NULL;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Return the BMEditStrands for a given object
|
||||
*/
|
||||
BMEditStrands *BKE_editstrands_from_object(Object *ob)
|
||||
{
|
||||
if (ob && ob->type == OB_MESH) {
|
||||
Mesh *me = ob->data;
|
||||
if (me->edit_strands)
|
||||
return me->edit_strands;
|
||||
}
|
||||
|
||||
return BKE_editstrands_from_object_particles(ob, NULL);
|
||||
}
|
||||
|
||||
void BKE_editstrands_update_linked_customdata(BMEditStrands *UNUSED(es))
|
||||
{
|
||||
}
|
||||
|
||||
/*does not free the BMEditStrands struct itself*/
|
||||
void BKE_editstrands_free(BMEditStrands *es)
|
||||
{
|
||||
BKE_editstrands_batch_cache_free(es);
|
||||
|
||||
if (es->base.bm)
|
||||
BM_mesh_free(es->base.bm);
|
||||
if (es->root_dm)
|
||||
es->root_dm->release(es->root_dm);
|
||||
}
|
||||
|
||||
/* === Constraints === */
|
||||
|
||||
BMEditStrandsLocations BKE_editstrands_get_locations(BMEditStrands *edit)
|
||||
{
|
||||
BMesh *bm = edit->base.bm;
|
||||
BMEditStrandsLocations locs = MEM_mallocN(3*sizeof(float) * bm->totvert, "editstrands locations");
|
||||
|
||||
BMVert *v;
|
||||
BMIter iter;
|
||||
int i;
|
||||
|
||||
BM_ITER_MESH_INDEX(v, &iter, bm, BM_VERTS_OF_MESH, i) {
|
||||
copy_v3_v3(locs[i], v->co);
|
||||
}
|
||||
|
||||
return locs;
|
||||
}
|
||||
|
||||
void BKE_editstrands_free_locations(BMEditStrandsLocations locs)
|
||||
{
|
||||
MEM_freeN(locs);
|
||||
}
|
||||
|
||||
void BKE_editstrands_solve_constraints(Object *ob, BMEditStrands *es, BMEditStrandsLocations orig)
|
||||
{
|
||||
BKE_editstrands_ensure(es);
|
||||
|
||||
BPH_strands_solve_constraints(ob, es, orig);
|
||||
|
||||
BKE_editstrands_batch_cache_dirty(es, BKE_STRANDS_BATCH_DIRTY_ALL);
|
||||
}
|
||||
|
||||
static void editstrands_calc_segment_lengths(BMesh *bm)
|
||||
{
|
||||
BMVert *root, *v, *vprev;
|
||||
BMIter iter, iter_strand;
|
||||
int k;
|
||||
|
||||
BM_ITER_STRANDS(root, &iter, bm, BM_STRANDS_OF_MESH) {
|
||||
BM_ITER_STRANDS_ELEM_INDEX(v, &iter_strand, root, BM_VERTS_OF_STRAND, k) {
|
||||
if (k > 0) {
|
||||
float length = len_v3v3(v->co, vprev->co);
|
||||
BM_elem_float_data_named_set(&bm->vdata, v, CD_PROP_FLT, CD_HAIR_SEGMENT_LENGTH, length);
|
||||
}
|
||||
vprev = v;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BKE_editstrands_ensure(BMEditStrands *es)
|
||||
{
|
||||
BM_strands_cd_flag_ensure(es->base.bm, 0);
|
||||
|
||||
if (es->flag & BM_STRANDS_DIRTY_SEGLEN) {
|
||||
editstrands_calc_segment_lengths(es->base.bm);
|
||||
|
||||
es->flag &= ~BM_STRANDS_DIRTY_SEGLEN;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* === Particle Conversion === */
|
||||
|
||||
BMesh *BKE_editstrands_particles_to_bmesh(Object *ob, ParticleSystem *psys)
|
||||
{
|
||||
ParticleSystemModifierData *psmd = psys_get_modifier(ob, psys);
|
||||
|
||||
const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_PSYS(psys);
|
||||
BMesh *bm;
|
||||
|
||||
bm = BM_mesh_create(&allocsize,
|
||||
&((struct BMeshCreateParams){.use_toolflags = false,}));
|
||||
|
||||
if (psmd && psmd->dm_final) {
|
||||
DM_ensure_tessface(psmd->dm_final);
|
||||
|
||||
BM_strands_bm_from_psys(bm, ob, psys, psmd->dm_final, true, /*psys->shapenr*/ -1);
|
||||
|
||||
editstrands_calc_segment_lengths(bm);
|
||||
}
|
||||
|
||||
return bm;
|
||||
}
|
||||
|
||||
void BKE_editstrands_particles_from_bmesh(Object *ob, ParticleSystem *psys)
|
||||
{
|
||||
ParticleSystemModifierData *psmd = psys_get_modifier(ob, psys);
|
||||
BMesh *bm = psys->hairedit ? psys->hairedit->base.bm : NULL;
|
||||
|
||||
if (bm) {
|
||||
if (psmd && psmd->dm_final) {
|
||||
BVHTreeFromMesh bvhtree = {NULL};
|
||||
|
||||
DM_ensure_tessface(psmd->dm_final);
|
||||
|
||||
bvhtree_from_mesh_faces(&bvhtree, psmd->dm_final, 0.0, 2, 6);
|
||||
|
||||
BM_strands_bm_to_psys(bm, ob, psys, psmd->dm_final, &bvhtree);
|
||||
|
||||
free_bvhtree_from_mesh(&bvhtree);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* === Mesh Conversion === */
|
||||
|
||||
BMesh *BKE_editstrands_mesh_to_bmesh(Object *ob, Mesh *me)
|
||||
{
|
||||
const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_ME(me);
|
||||
BMesh *bm;
|
||||
struct BMeshFromMeshParams params = {0};
|
||||
|
||||
bm = BM_mesh_create(&allocsize,
|
||||
&((struct BMeshCreateParams){.use_toolflags = false,}));
|
||||
|
||||
params.use_shapekey = true;
|
||||
params.active_shapekey = ob->shapenr;
|
||||
BM_mesh_bm_from_me(bm, me, ¶ms);
|
||||
BM_strands_cd_flag_ensure(bm, 0);
|
||||
|
||||
editstrands_calc_segment_lengths(bm);
|
||||
|
||||
return bm;
|
||||
}
|
||||
|
||||
void BKE_editstrands_mesh_from_bmesh(Object *ob)
|
||||
{
|
||||
Mesh *me = ob->data;
|
||||
BMesh *bm = me->edit_strands->base.bm;
|
||||
struct BMeshToMeshParams params = {0};
|
||||
|
||||
/* Workaround for T42360, 'ob->shapenr' should be 1 in this case.
|
||||
* however this isn't synchronized between objects at the moment. */
|
||||
if (UNLIKELY((ob->shapenr == 0) && (me->key && !BLI_listbase_is_empty(&me->key->block)))) {
|
||||
bm->shapenr = 1;
|
||||
}
|
||||
|
||||
BM_mesh_bm_to_me(bm, me, ¶ms);
|
||||
|
||||
#ifdef USE_TESSFACE_DEFAULT
|
||||
BKE_mesh_tessface_calc(me);
|
||||
#endif
|
||||
|
||||
/* free derived mesh. usually this would happen through depsgraph but there
|
||||
* are exceptions like file save that will not cause this, and we want to
|
||||
* avoid ending up with an invalid derived mesh then */
|
||||
BKE_object_free_derived_caches(ob);
|
||||
}
|
||||
|
||||
/* === Draw Cache === */
|
||||
void (*BKE_editstrands_batch_cache_dirty_cb)(BMEditStrands *es, int mode) = NULL;
|
||||
void (*BKE_editstrands_batch_cache_free_cb)(BMEditStrands *es) = NULL;
|
||||
|
||||
void BKE_editstrands_batch_cache_dirty(BMEditStrands *es, int mode)
|
||||
{
|
||||
if (es->batch_cache) {
|
||||
BKE_editstrands_batch_cache_dirty_cb(es, mode);
|
||||
}
|
||||
}
|
||||
|
||||
void BKE_editstrands_batch_cache_free(BMEditStrands *es)
|
||||
{
|
||||
if (es->batch_cache) {
|
||||
BKE_editstrands_batch_cache_free_cb(es);
|
||||
}
|
||||
}
|
455
source/blender/blenkernel/intern/hair.c
Normal file
@@ -0,0 +1,455 @@
|
||||
/*
|
||||
* ***** BEGIN GPL LICENSE BLOCK *****
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* The Original Code is Copyright (C) Blender Foundation
|
||||
* All rights reserved.
|
||||
*
|
||||
* The Original Code is: all of this file.
|
||||
*
|
||||
* Contributor(s): Lukas Toenne
|
||||
*
|
||||
* ***** END GPL LICENSE BLOCK *****
|
||||
*/
|
||||
|
||||
/** \file blender/blenkernel/intern/hair.c
|
||||
* \ingroup bke
|
||||
*/
|
||||
|
||||
#include <limits.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "BLI_math.h"
|
||||
#include "BLI_kdtree.h"
|
||||
#include "BLI_listbase.h"
|
||||
#include "BLI_rand.h"
|
||||
#include "BLI_sort.h"
|
||||
#include "BLI_string_utf8.h"
|
||||
#include "BLI_string_utils.h"
|
||||
|
||||
#include "DNA_hair_types.h"
|
||||
|
||||
#include "BKE_DerivedMesh.h"
|
||||
#include "BKE_mesh_sample.h"
|
||||
#include "BKE_hair.h"
|
||||
|
||||
#include "BLT_translation.h"
|
||||
|
||||
HairPattern* BKE_hair_new(void)
|
||||
{
|
||||
HairPattern *hair = MEM_callocN(sizeof(HairPattern), "hair");
|
||||
|
||||
/* add a default hair group */
|
||||
BKE_hair_group_new(hair, HAIR_GROUP_TYPE_NORMALS);
|
||||
|
||||
return hair;
|
||||
}
|
||||
|
||||
HairPattern* BKE_hair_copy(HairPattern *hair)
|
||||
{
|
||||
HairPattern *nhair = MEM_dupallocN(hair);
|
||||
|
||||
nhair->follicles = MEM_dupallocN(hair->follicles);
|
||||
|
||||
BLI_duplicatelist(&nhair->groups, &hair->groups);
|
||||
|
||||
return nhair;
|
||||
}
|
||||
|
||||
static void hair_group_free(HairGroup *group)
|
||||
{
|
||||
BKE_hair_batch_cache_free(group);
|
||||
|
||||
if (group->strands_parent_index) {
|
||||
MEM_freeN(group->strands_parent_index);
|
||||
}
|
||||
if (group->strands_parent_weight) {
|
||||
MEM_freeN(group->strands_parent_weight);
|
||||
}
|
||||
}
|
||||
|
||||
void BKE_hair_free(struct HairPattern *hair)
|
||||
{
|
||||
if (hair->follicles) {
|
||||
MEM_freeN(hair->follicles);
|
||||
}
|
||||
|
||||
for (HairGroup *group = hair->groups.first; group; group = group->next) {
|
||||
hair_group_free(group);
|
||||
}
|
||||
BLI_freelistN(&hair->groups);
|
||||
|
||||
MEM_freeN(hair);
|
||||
}
|
||||
|
||||
void BKE_hair_set_num_follicles(HairPattern *hair, int count)
|
||||
{
|
||||
if (hair->num_follicles != count) {
|
||||
if (count > 0) {
|
||||
if (hair->follicles) {
|
||||
hair->follicles = MEM_reallocN_id(hair->follicles, sizeof(HairFollicle) * count, "hair follicles");
|
||||
}
|
||||
else {
|
||||
hair->follicles = MEM_callocN(sizeof(HairFollicle) * count, "hair follicles");
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (hair->follicles) {
|
||||
MEM_freeN(hair->follicles);
|
||||
hair->follicles = NULL;
|
||||
}
|
||||
}
|
||||
hair->num_follicles = count;
|
||||
}
|
||||
}
|
||||
|
||||
void BKE_hair_follicles_generate(HairPattern *hair, DerivedMesh *scalp, int count, unsigned int seed)
|
||||
{
|
||||
BKE_hair_set_num_follicles(hair, count);
|
||||
if (count == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
MeshSampleGenerator *gen = BKE_mesh_sample_gen_surface_random(seed, true, NULL, NULL);
|
||||
BKE_mesh_sample_generator_bind(gen, scalp);
|
||||
unsigned int i;
|
||||
|
||||
HairFollicle *foll = hair->follicles;
|
||||
for (i = 0; i < count; ++i, ++foll) {
|
||||
bool ok = BKE_mesh_sample_generate(gen, &foll->mesh_sample);
|
||||
if (!ok) {
|
||||
/* clear remaining samples */
|
||||
memset(foll, 0, sizeof(HairFollicle) * (count - i));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
BKE_mesh_sample_free_generator(gen);
|
||||
|
||||
BKE_hair_batch_cache_all_dirty(hair, BKE_HAIR_BATCH_DIRTY_ALL);
|
||||
|
||||
BKE_hair_update_groups(hair);
|
||||
}
|
||||
|
||||
HairGroup* BKE_hair_group_new(HairPattern *hair, int type)
|
||||
{
|
||||
HairGroup *group = MEM_callocN(sizeof(HairGroup), "hair group");
|
||||
|
||||
group->type = type;
|
||||
BKE_hair_group_name_set(hair, group, DATA_("Group"));
|
||||
|
||||
switch (type) {
|
||||
case HAIR_GROUP_TYPE_NORMALS:
|
||||
group->normals_max_length = 0.1f;
|
||||
break;
|
||||
case HAIR_GROUP_TYPE_STRANDS:
|
||||
break;
|
||||
}
|
||||
|
||||
BLI_addtail(&hair->groups, group);
|
||||
|
||||
return group;
|
||||
}
|
||||
|
||||
void BKE_hair_group_remove(HairPattern *hair, HairGroup *group)
|
||||
{
|
||||
if (!group) {
|
||||
return;
|
||||
}
|
||||
BLI_assert(BLI_findindex(&hair->groups, group) >= 0);
|
||||
|
||||
BLI_remlink(&hair->groups, group);
|
||||
|
||||
hair_group_free(group);
|
||||
MEM_freeN(group);
|
||||
}
|
||||
|
||||
HairGroup* BKE_hair_group_copy(HairPattern *hair, HairGroup *group)
|
||||
{
|
||||
if (!group) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
HairGroup *ngroup = MEM_dupallocN(group);
|
||||
|
||||
BLI_insertlinkafter(&hair->groups, group, ngroup);
|
||||
return ngroup;
|
||||
}
|
||||
|
||||
void BKE_hair_group_moveto(HairPattern *hair, HairGroup *group, int position)
|
||||
{
|
||||
if (!group) {
|
||||
return;
|
||||
}
|
||||
BLI_assert(BLI_findindex(&hair->groups, group) >= 0);
|
||||
|
||||
BLI_remlink(&hair->groups, group);
|
||||
BLI_insertlinkbefore(&hair->groups, BLI_findlink(&hair->groups, position), group);
|
||||
}
|
||||
|
||||
void BKE_hair_group_name_set(HairPattern *hair, HairGroup *group, const char *name)
|
||||
{
|
||||
BLI_strncpy_utf8(group->name, name, sizeof(group->name));
|
||||
BLI_uniquename(&hair->groups, group, DATA_("Group"), '.', offsetof(HairGroup, name), sizeof(group->name));
|
||||
}
|
||||
|
||||
#define HAIR_FOLLICLE_GROUP_NONE INT_MAX
|
||||
|
||||
static void hair_claim_group_follicle(HairGroup *group, int group_index, int *follicle_group, int i)
|
||||
{
|
||||
if (follicle_group[i] == HAIR_FOLLICLE_GROUP_NONE) {
|
||||
follicle_group[i] = group_index;
|
||||
++group->num_follicles;
|
||||
}
|
||||
}
|
||||
|
||||
static void hair_group_follicles_normals(HairPattern *hair, HairGroup *group, int group_index, int *follicle_group)
|
||||
{
|
||||
const int num_follicles = hair->num_follicles;
|
||||
for (int i = 0; i < num_follicles; ++i) {
|
||||
// claim all
|
||||
hair_claim_group_follicle(group, group_index, follicle_group, i);
|
||||
}
|
||||
}
|
||||
|
||||
static void hair_group_follicles_strands(HairPattern *hair, HairGroup *group, int group_index, int *follicle_group)
|
||||
{
|
||||
// TODO
|
||||
UNUSED_VARS(hair, group, group_index, follicle_group);
|
||||
}
|
||||
|
||||
typedef struct HairFollicleSortContext {
|
||||
const HairFollicle *start;
|
||||
const int *follicle_group;
|
||||
} HairFollicleSortContext;
|
||||
|
||||
static int cmpHairFollicleByGroup(const void *a, const void *b, void *ctx_)
|
||||
{
|
||||
const HairFollicleSortContext *ctx = (const HairFollicleSortContext *)ctx_;
|
||||
const size_t ia = (const HairFollicle *)a - ctx->start;
|
||||
const size_t ib = (const HairFollicle *)b - ctx->start;
|
||||
return ctx->follicle_group[ib] - ctx->follicle_group[ia];
|
||||
}
|
||||
|
||||
void BKE_hair_update_groups(HairPattern *hair)
|
||||
{
|
||||
const int num_follicles = hair->num_follicles;
|
||||
int *follicle_group = MEM_mallocN(sizeof(int) * num_follicles, "hair follicle group index");
|
||||
for (int i = 0; i < num_follicles; ++i) {
|
||||
follicle_group[i] = HAIR_FOLLICLE_GROUP_NONE;
|
||||
}
|
||||
|
||||
int group_index = 0;
|
||||
for (HairGroup *group = hair->groups.first; group; group = group->next, ++group_index) {
|
||||
// Note: follicles array is sorted below
|
||||
if (group->prev) {
|
||||
group->follicles = group->prev->follicles + group->prev->num_follicles;
|
||||
}
|
||||
else {
|
||||
group->follicles = hair->follicles;
|
||||
}
|
||||
|
||||
group->num_follicles = 0;
|
||||
switch (group->type) {
|
||||
case HAIR_GROUP_TYPE_NORMALS:
|
||||
hair_group_follicles_normals(hair, group, group_index, follicle_group);
|
||||
break;
|
||||
case HAIR_GROUP_TYPE_STRANDS:
|
||||
hair_group_follicles_strands(hair, group, group_index, follicle_group);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
HairFollicleSortContext ctx;
|
||||
ctx.start = hair->follicles;
|
||||
ctx.follicle_group = follicle_group;
|
||||
BLI_qsort_r(hair->follicles, num_follicles, sizeof(HairFollicle), cmpHairFollicleByGroup, &ctx);
|
||||
}
|
||||
|
||||
MEM_freeN(follicle_group);
|
||||
|
||||
BKE_hair_batch_cache_all_dirty(hair, BKE_HAIR_BATCH_DIRTY_ALL);
|
||||
}
|
||||
|
||||
/* ================================= */
|
||||
|
||||
typedef struct HairGroupDrawDataInterface {
|
||||
HairDrawDataInterface base;
|
||||
int numstrands;
|
||||
int numverts_orig;
|
||||
} HairGroupDrawDataInterface;
|
||||
|
||||
static int get_num_strands(const HairDrawDataInterface *hairdata_)
|
||||
{
|
||||
const HairGroupDrawDataInterface *hairdata = (HairGroupDrawDataInterface *)hairdata_;
|
||||
return hairdata->numstrands;
|
||||
}
|
||||
|
||||
static int get_num_verts(const HairDrawDataInterface *hairdata_)
|
||||
{
|
||||
const HairGroupDrawDataInterface *hairdata = (HairGroupDrawDataInterface *)hairdata_;
|
||||
return hairdata->numverts_orig;
|
||||
}
|
||||
|
||||
static void get_strand_lengths_normals(const HairDrawDataInterface* hairdata_, int *r_lengths)
|
||||
{
|
||||
const HairGroupDrawDataInterface *hairdata = (HairGroupDrawDataInterface *)hairdata_;
|
||||
for (int i = 0; i < hairdata->numstrands; ++i)
|
||||
{
|
||||
r_lengths[i] = 2;
|
||||
}
|
||||
}
|
||||
|
||||
static void get_strand_roots_normals(const HairDrawDataInterface* hairdata_, struct MeshSample *r_roots)
|
||||
{
|
||||
const HairGroupDrawDataInterface *hairdata = (HairGroupDrawDataInterface *)hairdata_;
|
||||
DerivedMesh *scalp = hairdata->base.scalp;
|
||||
|
||||
MeshSampleGenerator *gen = BKE_mesh_sample_gen_surface_vertices();
|
||||
BKE_mesh_sample_generator_bind(gen, scalp);
|
||||
|
||||
int i = 0;
|
||||
for (; i < hairdata->numstrands; ++i)
|
||||
{
|
||||
if (!BKE_mesh_sample_generate(gen, &r_roots[i])) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
// clear remaining samples, if any
|
||||
for (; i < hairdata->numstrands; ++i)
|
||||
{
|
||||
BKE_mesh_sample_clear(&r_roots[i]);
|
||||
}
|
||||
|
||||
BKE_mesh_sample_free_generator(gen);
|
||||
}
|
||||
|
||||
static void get_strand_vertices_normals(const HairDrawDataInterface* hairdata_, float (*r_verts)[3])
|
||||
{
|
||||
const HairGroupDrawDataInterface *hairdata = (HairGroupDrawDataInterface *)hairdata_;
|
||||
DerivedMesh *scalp = hairdata->base.scalp;
|
||||
|
||||
MeshSampleGenerator *gen = BKE_mesh_sample_gen_surface_vertices();
|
||||
BKE_mesh_sample_generator_bind(gen, scalp);
|
||||
|
||||
int i = 0;
|
||||
for (; i < hairdata->numstrands; ++i)
|
||||
{
|
||||
MeshSample sample;
|
||||
if (!BKE_mesh_sample_generate(gen, &sample)) {
|
||||
break;
|
||||
}
|
||||
|
||||
float co[3], nor[3], tang[3];
|
||||
BKE_mesh_sample_eval(scalp, &sample, co, nor, tang);
|
||||
|
||||
copy_v3_v3(r_verts[i << 1], co);
|
||||
madd_v3_v3v3fl(r_verts[(i << 1) + 1], co, nor, hairdata->base.group->normals_max_length);
|
||||
}
|
||||
// clear remaining data, if any
|
||||
for (; i < hairdata->numstrands; ++i)
|
||||
{
|
||||
zero_v3(r_verts[i << 1]);
|
||||
zero_v3(r_verts[(i << 1) + 1]);
|
||||
}
|
||||
|
||||
BKE_mesh_sample_free_generator(gen);
|
||||
}
|
||||
|
||||
static void get_strand_lengths_strands(const HairDrawDataInterface* hairdata_, int *r_lengths)
|
||||
{
|
||||
const HairGroupDrawDataInterface *hairdata = (HairGroupDrawDataInterface *)hairdata_;
|
||||
for (int i = 0; i < hairdata->numstrands; ++i)
|
||||
{
|
||||
// TODO
|
||||
r_lengths[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void get_strand_roots_strands(const HairDrawDataInterface* hairdata_, struct MeshSample *r_roots)
|
||||
{
|
||||
const HairGroupDrawDataInterface *hairdata = (HairGroupDrawDataInterface *)hairdata_;
|
||||
for (int i = 0; i < hairdata->numstrands; ++i)
|
||||
{
|
||||
// TODO
|
||||
memset(&r_roots[i], 0, sizeof(MeshSample));
|
||||
}
|
||||
}
|
||||
|
||||
static void get_strand_vertices_strands(const HairDrawDataInterface* hairdata_, float (*r_verts)[3])
|
||||
{
|
||||
const HairGroupDrawDataInterface *hairdata = (HairGroupDrawDataInterface *)hairdata_;
|
||||
for (int i = 0; i < hairdata->numverts_orig; ++i)
|
||||
{
|
||||
// TODO
|
||||
zero_v3(r_verts[i]);
|
||||
}
|
||||
}
|
||||
|
||||
static HairGroupDrawDataInterface hair_group_get_interface(HairGroup *group, DerivedMesh *scalp)
|
||||
{
|
||||
HairGroupDrawDataInterface hairdata;
|
||||
hairdata.base.group = group;
|
||||
hairdata.base.scalp = scalp;
|
||||
hairdata.base.get_num_strands = get_num_strands;
|
||||
hairdata.base.get_num_verts = get_num_verts;
|
||||
|
||||
switch (group->type) {
|
||||
case HAIR_GROUP_TYPE_NORMALS: {
|
||||
hairdata.numstrands = scalp->getNumVerts(scalp);
|
||||
hairdata.numverts_orig = 2 * hairdata.numstrands;
|
||||
hairdata.base.get_strand_lengths = get_strand_lengths_normals;
|
||||
hairdata.base.get_strand_roots = get_strand_roots_normals;
|
||||
hairdata.base.get_strand_vertices = get_strand_vertices_normals;
|
||||
break;
|
||||
}
|
||||
case HAIR_GROUP_TYPE_STRANDS: {
|
||||
// TODO
|
||||
hairdata.numstrands = 0;
|
||||
hairdata.numverts_orig = 0;
|
||||
hairdata.base.get_strand_lengths = get_strand_lengths_strands;
|
||||
hairdata.base.get_strand_roots = get_strand_roots_strands;
|
||||
hairdata.base.get_strand_vertices = get_strand_vertices_strands;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return hairdata;
|
||||
}
|
||||
|
||||
int* BKE_hair_group_get_fiber_lengths(HairGroup *group, DerivedMesh *scalp, int subdiv)
|
||||
{
|
||||
HairGroupDrawDataInterface hairdata = hair_group_get_interface(group, scalp);
|
||||
return BKE_hair_strands_get_fiber_lengths(&hairdata.base, subdiv);
|
||||
}
|
||||
|
||||
void BKE_hair_group_get_texture_buffer_size(HairGroup *group, DerivedMesh *scalp, int subdiv,
|
||||
int *r_size, int *r_strand_map_start,
|
||||
int *r_strand_vertex_start, int *r_fiber_start)
|
||||
{
|
||||
HairGroupDrawDataInterface hairdata = hair_group_get_interface(group, scalp);
|
||||
BKE_hair_strands_get_texture_buffer_size(&hairdata.base, subdiv,
|
||||
r_size, r_strand_map_start, r_strand_vertex_start, r_fiber_start);
|
||||
}
|
||||
|
||||
void BKE_hair_group_get_texture_buffer(HairGroup *group, DerivedMesh *scalp, int subdiv, void *buffer)
|
||||
{
|
||||
HairGroupDrawDataInterface hairdata = hair_group_get_interface(group, scalp);
|
||||
BKE_hair_strands_get_texture_buffer(&hairdata.base, subdiv, buffer);
|
||||
}
|
606
source/blender/blenkernel/intern/hair_draw.c
Normal file
@@ -0,0 +1,606 @@
|
||||
/*
|
||||
* ***** BEGIN GPL LICENSE BLOCK *****
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* The Original Code is Copyright (C) Blender Foundation
|
||||
* All rights reserved.
|
||||
*
|
||||
* The Original Code is: all of this file.
|
||||
*
|
||||
* Contributor(s): Lukas Toenne
|
||||
*
|
||||
* ***** END GPL LICENSE BLOCK *****
|
||||
*/
|
||||
|
||||
/** \file blender/blenkernel/intern/hair_draw.c
|
||||
* \ingroup bke
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "BLI_math.h"
|
||||
#include "BLI_kdtree.h"
|
||||
#include "BLI_rand.h"
|
||||
|
||||
#include "DNA_hair_types.h"
|
||||
|
||||
#include "BKE_DerivedMesh.h"
|
||||
#include "BKE_mesh_sample.h"
|
||||
#include "BKE_hair.h"
|
||||
|
||||
#if 0
|
||||
bool BKE_hair_fiber_get_location(const HairFiber *fiber, DerivedMesh *root_dm, float loc[3])
|
||||
{
|
||||
float nor[3], tang[3];
|
||||
if (BKE_mesh_sample_eval(root_dm, &fiber->root, loc, nor, tang)) {
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
zero_v3(loc);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool BKE_hair_fiber_get_vectors(const HairFiber *fiber, DerivedMesh *root_dm,
|
||||
float loc[3], float nor[3], float tang[3])
|
||||
{
|
||||
if (BKE_mesh_sample_eval(root_dm, &fiber->root, loc, nor, tang)) {
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
zero_v3(loc);
|
||||
zero_v3(nor);
|
||||
zero_v3(tang);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool BKE_hair_fiber_get_matrix(const HairFiber *fiber, DerivedMesh *root_dm, float mat[4][4])
|
||||
{
|
||||
if (BKE_mesh_sample_eval(root_dm, &fiber->root, mat[3], mat[2], mat[0])) {
|
||||
cross_v3_v3v3(mat[1], mat[2], mat[0]);
|
||||
mat[0][3] = 0.0f;
|
||||
mat[1][3] = 0.0f;
|
||||
mat[2][3] = 0.0f;
|
||||
mat[3][3] = 1.0f;
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
unit_m4(mat);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
BLI_INLINE void verify_fiber_weights(HairFiber *fiber)
|
||||
{
|
||||
const float *w = fiber->parent_weight;
|
||||
|
||||
BLI_assert(w[0] >= 0.0f && w[1] >= 0.0f && w[2] >= 0.0f && w[3] >= 0.0f);
|
||||
float sum = w[0] + w[1] + w[2] + w[3];
|
||||
float epsilon = 1.0e-2;
|
||||
BLI_assert(sum > 1.0f - epsilon && sum < 1.0f + epsilon);
|
||||
UNUSED_VARS(sum, epsilon);
|
||||
|
||||
BLI_assert(w[0] >= w[1] && w[1] >= w[2] && w[2] >= w[3]);
|
||||
}
|
||||
|
||||
static void sort_fiber_weights(HairFiber *fiber)
|
||||
{
|
||||
unsigned int *idx = fiber->parent_index;
|
||||
float *w = fiber->parent_weight;
|
||||
|
||||
#define FIBERSWAP(a, b) \
|
||||
SWAP(unsigned int, idx[a], idx[b]); \
|
||||
SWAP(float, w[a], w[b]);
|
||||
|
||||
for (int k = 0; k < 3; ++k) {
|
||||
int maxi = k;
|
||||
float maxw = w[k];
|
||||
for (int i = k+1; i < 4; ++i) {
|
||||
if (w[i] > maxw) {
|
||||
maxi = i;
|
||||
maxw = w[i];
|
||||
}
|
||||
}
|
||||
if (maxi != k)
|
||||
FIBERSWAP(k, maxi);
|
||||
}
|
||||
|
||||
#undef FIBERSWAP
|
||||
}
|
||||
|
||||
static void strand_find_closest(HairFiber *fiber, const float loc[3],
|
||||
const KDTree *tree, const float (*strandloc)[3])
|
||||
{
|
||||
/* Use the 3 closest strands for interpolation.
|
||||
* Note that we have up to 4 possible weights, but we
|
||||
* only look for a triangle with this method.
|
||||
*/
|
||||
KDTreeNearest nearest[3];
|
||||
const float *sloc[3] = {NULL};
|
||||
int k, found = BLI_kdtree_find_nearest_n(tree, loc, nearest, 3);
|
||||
for (k = 0; k < found; ++k) {
|
||||
fiber->parent_index[k] = nearest[k].index;
|
||||
sloc[k] = strandloc[nearest[k].index];
|
||||
}
|
||||
for (; k < 4; ++k) {
|
||||
fiber->parent_index[k] = STRAND_INDEX_NONE;
|
||||
fiber->parent_weight[k] = 0.0f;
|
||||
}
|
||||
|
||||
/* calculate barycentric interpolation weights */
|
||||
if (found == 3) {
|
||||
float closest[3];
|
||||
closest_on_tri_to_point_v3(closest, loc, sloc[0], sloc[1], sloc[2]);
|
||||
|
||||
float w[3];
|
||||
interp_weights_tri_v3(w, sloc[0], sloc[1], sloc[2], closest);
|
||||
copy_v3_v3(fiber->parent_weight, w);
|
||||
/* float precisions issues can cause slightly negative weights */
|
||||
CLAMP3(fiber->parent_weight, 0.0f, 1.0f);
|
||||
}
|
||||
else if (found == 2) {
|
||||
fiber->parent_weight[1] = line_point_factor_v3(loc, sloc[0], sloc[1]);
|
||||
fiber->parent_weight[0] = 1.0f - fiber->parent_weight[1];
|
||||
/* float precisions issues can cause slightly negative weights */
|
||||
CLAMP2(fiber->parent_weight, 0.0f, 1.0f);
|
||||
}
|
||||
else if (found == 1) {
|
||||
fiber->parent_weight[0] = 1.0f;
|
||||
}
|
||||
|
||||
sort_fiber_weights(fiber);
|
||||
}
|
||||
|
||||
static void strand_calc_root_distance(HairFiber *fiber, const float loc[3], const float nor[3], const float tang[3],
|
||||
const float (*strandloc)[3])
|
||||
{
|
||||
if (fiber->parent_index[0] == STRAND_INDEX_NONE)
|
||||
return;
|
||||
|
||||
float cotang[3];
|
||||
cross_v3_v3v3(cotang, nor, tang);
|
||||
|
||||
const float *sloc0 = strandloc[fiber->parent_index[0]];
|
||||
float dist[3];
|
||||
sub_v3_v3v3(dist, loc, sloc0);
|
||||
fiber->root_distance[0] = dot_v3v3(dist, tang);
|
||||
fiber->root_distance[1] = dot_v3v3(dist, cotang);
|
||||
}
|
||||
|
||||
static void strands_calc_weights(const HairDrawDataInterface *hairdata, struct DerivedMesh *scalp, HairFiber *fibers, int num_fibers)
|
||||
{
|
||||
const int num_strands = hairdata->get_num_strands(hairdata);
|
||||
if (num_strands == 0)
|
||||
return;
|
||||
|
||||
float (*strandloc)[3] = MEM_mallocN(sizeof(float) * 3 * num_strands, "strand locations");
|
||||
{
|
||||
MeshSample *roots = MEM_mallocN(sizeof(MeshSample) * num_strands, "strand roots");
|
||||
hairdata->get_strand_roots(hairdata, roots);
|
||||
for (int i = 0; i < num_strands; ++i) {
|
||||
float nor[3], tang[3];
|
||||
if (!BKE_mesh_sample_eval(scalp, &roots[i], strandloc[i], nor, tang)) {
|
||||
zero_v3(strandloc[i]);
|
||||
}
|
||||
}
|
||||
MEM_freeN(roots);
|
||||
}
|
||||
|
||||
KDTree *tree = BLI_kdtree_new(num_strands);
|
||||
for (int c = 0; c < num_strands; ++c) {
|
||||
BLI_kdtree_insert(tree, c, strandloc[c]);
|
||||
}
|
||||
BLI_kdtree_balance(tree);
|
||||
|
||||
HairFiber *fiber = fibers;
|
||||
for (int i = 0; i < num_fibers; ++i, ++fiber) {
|
||||
float loc[3], nor[3], tang[3];
|
||||
if (BKE_mesh_sample_eval(scalp, &fiber->root, loc, nor, tang)) {
|
||||
|
||||
strand_find_closest(fiber, loc, tree, strandloc);
|
||||
verify_fiber_weights(fiber);
|
||||
|
||||
strand_calc_root_distance(fiber, loc, nor, tang, strandloc);
|
||||
}
|
||||
}
|
||||
|
||||
BLI_kdtree_free(tree);
|
||||
MEM_freeN(strandloc);
|
||||
}
|
||||
|
||||
HairFiber* BKE_hair_fibers_create(const HairDrawDataInterface *hairdata,
|
||||
struct DerivedMesh *scalp, unsigned int amount,
|
||||
unsigned int seed)
|
||||
{
|
||||
MeshSampleGenerator *gen = BKE_mesh_sample_gen_surface_random(scalp, seed);
|
||||
unsigned int i;
|
||||
|
||||
HairFiber *fibers = MEM_mallocN(sizeof(HairFiber) * amount, "HairFiber");
|
||||
HairFiber *fiber;
|
||||
|
||||
for (i = 0, fiber = fibers; i < amount; ++i, ++fiber) {
|
||||
if (BKE_mesh_sample_generate(gen, &fiber->root)) {
|
||||
int k;
|
||||
/* influencing control strands are determined later */
|
||||
for (k = 0; k < 4; ++k) {
|
||||
fiber->parent_index[k] = STRAND_INDEX_NONE;
|
||||
fiber->parent_weight[k] = 0.0f;
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* clear remaining samples */
|
||||
memset(fiber, 0, sizeof(HairFiber) * (amount - i));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
BKE_mesh_sample_free_generator(gen);
|
||||
|
||||
strands_calc_weights(hairdata, scalp, fibers, amount);
|
||||
|
||||
return fibers;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int hair_get_strand_subdiv_numverts(int numstrands, int numverts, int subdiv)
|
||||
{
|
||||
return ((numverts - numstrands) << subdiv) + numstrands;
|
||||
}
|
||||
|
||||
BLI_INLINE int hair_get_strand_subdiv_length(int orig_length, int subdiv)
|
||||
{
|
||||
return ((orig_length - 1) << subdiv) + 1;
|
||||
}
|
||||
|
||||
static void hair_get_strand_subdiv_lengths(int *lengths, const int *orig_lengths, int num_strands, int subdiv)
|
||||
{
|
||||
for (int i = 0; i < num_strands; ++i) {
|
||||
lengths[i] = hair_get_strand_subdiv_length(orig_lengths[i], subdiv);
|
||||
}
|
||||
}
|
||||
|
||||
int* BKE_hair_strands_get_fiber_lengths(const HairDrawDataInterface *hairdata, int subdiv)
|
||||
{
|
||||
if (!hairdata->group) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const int totfibers = hairdata->group->num_follicles;
|
||||
int *fiber_length = MEM_mallocN(sizeof(int) * totfibers, "fiber length");
|
||||
|
||||
switch (hairdata->group->type) {
|
||||
case HAIR_GROUP_TYPE_NORMALS: {
|
||||
const int length = hair_get_strand_subdiv_length(2, subdiv);
|
||||
for (int i = 0; i < totfibers; ++i) {
|
||||
fiber_length[i] = length;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case HAIR_GROUP_TYPE_STRANDS: {
|
||||
const int num_strands = hairdata->get_num_strands(hairdata);
|
||||
int *lengths = MEM_mallocN(sizeof(int) * num_strands, "strand length");
|
||||
hairdata->get_strand_lengths(hairdata, lengths);
|
||||
hair_get_strand_subdiv_lengths(lengths, lengths, num_strands, subdiv);
|
||||
|
||||
for (int i = 0; i < totfibers; ++i) {
|
||||
// Calculate the length of the fiber from the weighted average of its control strands
|
||||
float fiblen = 0.0f;
|
||||
const int *parent_index = hairdata->group->strands_parent_index[i];
|
||||
const float *parent_weight = hairdata->group->strands_parent_weight[i];
|
||||
|
||||
for (int k = 0; k < 4; ++k) {
|
||||
int si = parent_index[k];
|
||||
float sw = parent_weight[k];
|
||||
if (si == STRAND_INDEX_NONE || sw == 0.0f) {
|
||||
break;
|
||||
}
|
||||
BLI_assert(si < num_strands);
|
||||
|
||||
fiblen += (float)lengths[si] * sw;
|
||||
}
|
||||
|
||||
// use rounded number of segments
|
||||
fiber_length[i] = (int)(fiblen + 0.5f);
|
||||
}
|
||||
|
||||
MEM_freeN(lengths);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return fiber_length;
|
||||
}
|
||||
|
||||
typedef struct HairFiberTextureBuffer {
|
||||
unsigned int parent_index[4];
|
||||
float parent_weight[4];
|
||||
float root_position[3];
|
||||
int pad;
|
||||
} HairFiberTextureBuffer;
|
||||
BLI_STATIC_ASSERT_ALIGN(HairFiberTextureBuffer, 8)
|
||||
|
||||
typedef struct HairStrandVertexTextureBuffer {
|
||||
float co[3];
|
||||
float nor[3];
|
||||
float tang[3];
|
||||
int pad;
|
||||
} HairStrandVertexTextureBuffer;
|
||||
BLI_STATIC_ASSERT_ALIGN(HairStrandVertexTextureBuffer, 8)
|
||||
|
||||
typedef struct HairStrandMapTextureBuffer {
|
||||
unsigned int vertex_start;
|
||||
unsigned int vertex_count;
|
||||
} HairStrandMapTextureBuffer;
|
||||
BLI_STATIC_ASSERT_ALIGN(HairStrandMapTextureBuffer, 8)
|
||||
|
||||
static void hair_get_texture_buffer_size(int numstrands, int numverts_orig, int subdiv, int numfibers,
|
||||
int *r_size, int *r_strand_map_start,
|
||||
int *r_strand_vertex_start, int *r_fiber_start)
|
||||
{
|
||||
const int numverts = hair_get_strand_subdiv_numverts(numstrands, numverts_orig, subdiv);
|
||||
*r_strand_map_start = 0;
|
||||
*r_strand_vertex_start = *r_strand_map_start + numstrands * sizeof(HairStrandMapTextureBuffer);
|
||||
*r_fiber_start = *r_strand_vertex_start + numverts * sizeof(HairStrandVertexTextureBuffer);
|
||||
*r_size = *r_fiber_start + numfibers * sizeof(HairFiberTextureBuffer);
|
||||
}
|
||||
|
||||
static void hair_strand_transport_frame(const float co1[3], const float co2[3],
|
||||
float prev_tang[3], float prev_nor[3],
|
||||
float r_tang[3], float r_nor[3])
|
||||
{
|
||||
/* segment direction */
|
||||
sub_v3_v3v3(r_tang, co2, co1);
|
||||
normalize_v3(r_tang);
|
||||
|
||||
/* rotate the frame */
|
||||
float rot[3][3];
|
||||
rotation_between_vecs_to_mat3(rot, prev_tang, r_tang);
|
||||
mul_v3_m3v3(r_nor, rot, prev_nor);
|
||||
|
||||
copy_v3_v3(prev_tang, r_tang);
|
||||
copy_v3_v3(prev_nor, r_nor);
|
||||
}
|
||||
|
||||
static void hair_strand_calc_vectors(const float (*positions)[3], int num_verts, float rootmat[3][3],
|
||||
HairStrandVertexTextureBuffer *strand)
|
||||
{
|
||||
for (int i = 0; i < num_verts; ++i) {
|
||||
copy_v3_v3(strand[i].co, positions[i]);
|
||||
}
|
||||
|
||||
// Calculate tangent and normal vectors
|
||||
{
|
||||
BLI_assert(num_verts >= 2);
|
||||
|
||||
float prev_tang[3], prev_nor[3];
|
||||
|
||||
copy_v3_v3(prev_tang, rootmat[2]);
|
||||
copy_v3_v3(prev_nor, rootmat[0]);
|
||||
|
||||
hair_strand_transport_frame(strand[0].co, strand[1].co,
|
||||
prev_tang, prev_nor,
|
||||
strand[0].tang, strand[0].nor);
|
||||
|
||||
for (int i = 1; i < num_verts - 1; ++i) {
|
||||
hair_strand_transport_frame(strand[i-1].co, strand[i+1].co,
|
||||
prev_tang, prev_nor,
|
||||
strand[i].tang, strand[i].nor);
|
||||
}
|
||||
|
||||
hair_strand_transport_frame(strand[num_verts-2].co, strand[num_verts-1].co,
|
||||
prev_tang, prev_nor,
|
||||
strand[num_verts-1].tang, strand[num_verts-1].nor);
|
||||
}
|
||||
}
|
||||
|
||||
static int hair_strand_subdivide(float (*verts)[3], const float (*verts_orig)[3], int numverts_orig, int subdiv)
|
||||
{
|
||||
{
|
||||
/* Move vertex positions from the dense array to their initial configuration for subdivision. */
|
||||
const int step = (1 << subdiv);
|
||||
const float (*src)[3] = verts_orig;
|
||||
float (*dst)[3] = verts;
|
||||
for (int i = 0; i < numverts_orig; ++i) {
|
||||
copy_v3_v3(*dst, *src);
|
||||
|
||||
++src;
|
||||
dst += step;
|
||||
}
|
||||
}
|
||||
|
||||
/* Subdivide */
|
||||
for (int d = 0; d < subdiv; ++d) {
|
||||
const int num_edges = (numverts_orig - 1) << d;
|
||||
const int hstep = 1 << (subdiv - d - 1);
|
||||
const int step = 1 << (subdiv - d);
|
||||
|
||||
/* Calculate edge points */
|
||||
{
|
||||
int index = 0;
|
||||
for (int k = 0; k < num_edges; ++k, index += step) {
|
||||
add_v3_v3v3(verts[index + hstep], verts[index], verts[index + step]);
|
||||
mul_v3_fl(verts[index + hstep], 0.5f);
|
||||
}
|
||||
}
|
||||
|
||||
/* Move original points */
|
||||
{
|
||||
int index = step;
|
||||
for (int k = 1; k < num_edges; ++k, index += step) {
|
||||
add_v3_v3v3(verts[index], verts[index - hstep], verts[index + hstep]);
|
||||
mul_v3_fl(verts[index], 0.5f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const int num_verts = ((numverts_orig - 1) << subdiv) + 1;
|
||||
return num_verts;
|
||||
}
|
||||
|
||||
static void hair_get_strand_buffer(DerivedMesh *scalp, int numstrands, int numverts_orig, int subdiv,
|
||||
const int *lengths_orig, const float (*vertco_orig)[3], const MeshSample *roots,
|
||||
HairStrandMapTextureBuffer *strand_map_buffer,
|
||||
HairStrandVertexTextureBuffer *strand_vertex_buffer)
|
||||
{
|
||||
const int numverts = hair_get_strand_subdiv_numverts(numstrands, numverts_orig, subdiv);
|
||||
|
||||
const int *lengths;
|
||||
const float (*vertco)[3];
|
||||
int *lengths_subdiv = NULL;
|
||||
float (*vertco_subdiv)[3] = NULL;
|
||||
if (subdiv > 0) {
|
||||
lengths = lengths_subdiv = MEM_mallocN(sizeof(int) * numstrands, "strand lengths subdivided");
|
||||
hair_get_strand_subdiv_lengths(lengths_subdiv, lengths_orig, numstrands, subdiv);
|
||||
|
||||
vertco = vertco_subdiv = MEM_mallocN(sizeof(float[3]) * numverts, "strand vertex positions subdivided");
|
||||
}
|
||||
else {
|
||||
lengths = lengths_orig;
|
||||
vertco = vertco_orig;
|
||||
}
|
||||
|
||||
HairStrandMapTextureBuffer *smap = strand_map_buffer;
|
||||
HairStrandVertexTextureBuffer *svert = strand_vertex_buffer;
|
||||
int vertex_orig_start = 0;
|
||||
int vertex_start = 0;
|
||||
for (int i = 0; i < numstrands; ++i) {
|
||||
const int len_orig = lengths_orig[i];
|
||||
const int len = lengths[i];
|
||||
smap->vertex_start = vertex_start;
|
||||
smap->vertex_count = len;
|
||||
|
||||
if (subdiv > 0) {
|
||||
hair_strand_subdivide(vertco_subdiv + vertex_start, vertco_orig + vertex_orig_start, len_orig, subdiv);
|
||||
}
|
||||
|
||||
{
|
||||
float pos[3];
|
||||
float matrix[3][3];
|
||||
BKE_mesh_sample_eval(scalp, &roots[i], pos, matrix[2], matrix[0]);
|
||||
cross_v3_v3v3(matrix[1], matrix[2], matrix[0]);
|
||||
hair_strand_calc_vectors(vertco + vertex_start, len, matrix, svert);
|
||||
}
|
||||
|
||||
vertex_orig_start += len_orig;
|
||||
vertex_start += len;
|
||||
++smap;
|
||||
svert += len;
|
||||
}
|
||||
|
||||
if (subdiv > 0) {
|
||||
MEM_freeN(lengths_subdiv);
|
||||
MEM_freeN(vertco_subdiv);
|
||||
}
|
||||
}
|
||||
|
||||
static void hair_get_fiber_buffer(const HairGroup *group, DerivedMesh *scalp,
|
||||
HairFiberTextureBuffer *fiber_buf)
|
||||
{
|
||||
const int totfibers = group->num_follicles;
|
||||
HairFiberTextureBuffer *fb = fiber_buf;
|
||||
float nor[3], tang[3];
|
||||
switch (group->type) {
|
||||
case HAIR_GROUP_TYPE_NORMALS: {
|
||||
HairFollicle *foll = group->follicles;
|
||||
for (int i = 0; i < totfibers; ++i, ++fb, ++foll) {
|
||||
for (int k = 0; k < 3; ++k) {
|
||||
fb->parent_index[k] = foll->mesh_sample.orig_verts[k];
|
||||
fb->parent_weight[k] = foll->mesh_sample.orig_weights[k];
|
||||
}
|
||||
fb->parent_index[3] = STRAND_INDEX_NONE;
|
||||
fb->parent_weight[3] = 0.0f;
|
||||
|
||||
BKE_mesh_sample_eval(scalp, &group->follicles[i].mesh_sample, fb->root_position, nor, tang);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case HAIR_GROUP_TYPE_STRANDS: {
|
||||
BLI_assert(group->strands_parent_index != NULL);
|
||||
BLI_assert(group->strands_parent_weight != NULL);
|
||||
HairFollicle *foll = group->follicles;
|
||||
for (int i = 0; i < totfibers; ++i, ++fb, ++foll) {
|
||||
memcpy(fb->parent_index, group->strands_parent_index[i], sizeof(fb->parent_index));
|
||||
memcpy(fb->parent_weight, group->strands_parent_weight[i], sizeof(fb->parent_weight));
|
||||
|
||||
BKE_mesh_sample_eval(scalp, &foll->mesh_sample, fb->root_position, nor, tang);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BKE_hair_strands_get_texture_buffer_size(const HairDrawDataInterface *hairdata, int subdiv,
|
||||
int *r_size, int *r_strand_map_start,
|
||||
int *r_strand_vertex_start, int *r_fiber_start)
|
||||
{
|
||||
const int totstrands = hairdata->get_num_strands(hairdata);
|
||||
const int totverts = hairdata->get_num_verts(hairdata);
|
||||
hair_get_texture_buffer_size(totstrands, totverts, subdiv, hairdata->group->num_follicles,
|
||||
r_size, r_strand_map_start, r_strand_vertex_start, r_fiber_start);
|
||||
}
|
||||
|
||||
void BKE_hair_strands_get_texture_buffer(const HairDrawDataInterface *hairdata, int subdiv, void *buffer)
|
||||
{
|
||||
const int totstrands = hairdata->get_num_strands(hairdata);
|
||||
const int totverts_orig = hairdata->get_num_verts(hairdata);
|
||||
int size, strand_map_start, strand_vertex_start, fiber_start;
|
||||
hair_get_texture_buffer_size(totstrands, totverts_orig, subdiv, hairdata->group->num_follicles,
|
||||
&size, &strand_map_start, &strand_vertex_start, &fiber_start);
|
||||
|
||||
int *lengths_orig = MEM_mallocN(sizeof(int) * totstrands, "strand lengths");
|
||||
float (*vertco_orig)[3] = MEM_mallocN(sizeof(float[3]) * totverts_orig, "strand vertex positions");
|
||||
MeshSample *roots = MEM_mallocN(sizeof(MeshSample) * totstrands, "strand roots");
|
||||
hairdata->get_strand_lengths(hairdata, lengths_orig);
|
||||
hairdata->get_strand_vertices(hairdata, vertco_orig);
|
||||
hairdata->get_strand_roots(hairdata, roots);
|
||||
|
||||
hair_get_strand_buffer(hairdata->scalp, totstrands, totverts_orig, subdiv,
|
||||
lengths_orig, vertco_orig, roots,
|
||||
(HairStrandMapTextureBuffer*)((char*)buffer + strand_map_start),
|
||||
(HairStrandVertexTextureBuffer*)((char*)buffer + strand_vertex_start));
|
||||
hair_get_fiber_buffer(hairdata->group, hairdata->scalp, (HairFiberTextureBuffer*)((char*)buffer + fiber_start));
|
||||
|
||||
MEM_freeN(lengths_orig);
|
||||
MEM_freeN(vertco_orig);
|
||||
MEM_freeN(roots);
|
||||
}
|
||||
|
||||
void (*BKE_hair_batch_cache_dirty_cb)(HairGroup *group, int mode) = NULL;
|
||||
void (*BKE_hair_batch_cache_free_cb)(HairGroup *group) = NULL;
|
||||
|
||||
void BKE_hair_batch_cache_dirty(HairGroup *group, int mode)
|
||||
{
|
||||
if (group->draw_batch_cache) {
|
||||
BKE_hair_batch_cache_dirty_cb(group, mode);
|
||||
}
|
||||
}
|
||||
|
||||
void BKE_hair_batch_cache_all_dirty(struct HairPattern *hair, int mode)
|
||||
{
|
||||
for (HairGroup *group = hair->groups.first; group; group = group->next) {
|
||||
BKE_hair_batch_cache_dirty(group, mode);
|
||||
}
|
||||
}
|
||||
|
||||
void BKE_hair_batch_cache_free(HairGroup *group)
|
||||
{
|
||||
if (group->draw_batch_cache || group->draw_texture_cache) {
|
||||
BKE_hair_batch_cache_free_cb(group);
|
||||
}
|
||||
}
|
1635
source/blender/blenkernel/intern/mesh_sample.c
Normal file
@@ -977,6 +977,7 @@ ParticleSystem *BKE_object_copy_particlesystem(ParticleSystem *psys, const int f
|
||||
psysn->pathcache = NULL;
|
||||
psysn->childcache = NULL;
|
||||
psysn->edit = NULL;
|
||||
psysn->hairedit = NULL;
|
||||
psysn->pdd = NULL;
|
||||
psysn->effectors = NULL;
|
||||
psysn->tree = NULL;
|
||||
|
@@ -65,6 +65,7 @@
|
||||
#include "BKE_boids.h"
|
||||
#include "BKE_cloth.h"
|
||||
#include "BKE_colortools.h"
|
||||
#include "BKE_editstrands.h"
|
||||
#include "BKE_effect.h"
|
||||
#include "BKE_global.h"
|
||||
#include "BKE_group.h"
|
||||
@@ -568,6 +569,11 @@ void psys_free(Object *ob, ParticleSystem *psys)
|
||||
|
||||
if (psys->edit && psys->free_edit)
|
||||
psys->free_edit(psys->edit);
|
||||
if (psys->hairedit) {
|
||||
BKE_editstrands_free(psys->hairedit);
|
||||
MEM_freeN(psys->hairedit);
|
||||
psys->hairedit = NULL;
|
||||
}
|
||||
|
||||
if (psys->child) {
|
||||
MEM_freeN(psys->child);
|
||||
@@ -1606,6 +1612,11 @@ static int psys_map_index_on_dm(DerivedMesh *dm, int from, int index, int index_
|
||||
return 1;
|
||||
}
|
||||
|
||||
int psys_get_index_on_dm(ParticleSystem *psys, DerivedMesh *dm, ParticleData *pa, int *mapindex, float mapfw[4])
|
||||
{
|
||||
return psys_map_index_on_dm(dm, psys->part->from, pa->num, pa->num_dmcache, pa->fuv, pa->foffset, mapindex, mapfw);
|
||||
}
|
||||
|
||||
/* interprets particle data to get a point on a mesh in object space */
|
||||
void psys_particle_on_dm(DerivedMesh *dm_final, int from, int index, int index_dmcache,
|
||||
const float fw[4], float foffset, float vec[3], float nor[3], float utan[3], float vtan[3],
|
||||
@@ -3207,7 +3218,7 @@ void object_remove_particle_system(Scene *UNUSED(scene), Object *ob)
|
||||
if (ob->particlesystem.first)
|
||||
((ParticleSystem *) ob->particlesystem.first)->flag |= PSYS_CURRENT;
|
||||
else
|
||||
ob->mode &= ~OB_MODE_PARTICLE_EDIT;
|
||||
ob->mode &= ~(OB_MODE_PARTICLE_EDIT | OB_MODE_HAIR_EDIT);
|
||||
|
||||
DEG_relations_tag_update(G.main);
|
||||
DEG_id_tag_update(&ob->id, OB_RECALC_DATA);
|
||||
|
@@ -913,6 +913,11 @@ void BKE_scene_init(Scene *sce)
|
||||
sce->toolsettings->imapaint.normal_angle = 80;
|
||||
sce->toolsettings->imapaint.seam_bleed = 2;
|
||||
|
||||
sce->toolsettings->hair_edit.select_mode = HAIR_SELECT_VERTEX;
|
||||
sce->toolsettings->hair_edit.hair_draw_mode = HAIR_DRAW_FIBERS;
|
||||
sce->toolsettings->hair_edit.hair_draw_size = 2.5f;
|
||||
sce->toolsettings->hair_edit.hair_draw_subdiv = 2;
|
||||
|
||||
sce->physics_settings.gravity[0] = 0.0f;
|
||||
sce->physics_settings.gravity[1] = 0.0f;
|
||||
sce->physics_settings.gravity[2] = -9.81f;
|
||||
|
@@ -318,6 +318,8 @@ bool clip_segment_v3_plane_n(
|
||||
/****************************** Interpolation ********************************/
|
||||
void interp_weights_tri_v3(float w[3], const float a[3], const float b[3], const float c[3], const float p[3]);
|
||||
void interp_weights_quad_v3(float w[4], const float a[3], const float b[3], const float c[3], const float d[3], const float p[3]);
|
||||
/* also returns three indices of the triangle actually used */
|
||||
void interp_weights_quad_v3_index(int tri[3], float w[4], const float v1[3], const float v2[3], const float v3[3], const float v4[3], const float co[3]);
|
||||
void interp_weights_poly_v3(float w[], float v[][3], const int n, const float co[3]);
|
||||
void interp_weights_poly_v2(float w[], float v[][2], const int n, const float co[2]);
|
||||
|
||||
|
@@ -2830,6 +2830,71 @@ void interp_weights_quad_v3(float w[4], const float v1[3], const float v2[3], co
|
||||
}
|
||||
}
|
||||
|
||||
void interp_weights_quad_v3_index(int tri[3], float w[4], const float v1[3], const float v2[3], const float v3[3], const float v4[3], const float co[3])
|
||||
{
|
||||
float w2[3];
|
||||
|
||||
w[0] = w[1] = w[2] = w[3] = 0.0f;
|
||||
tri[0] = tri[1] = tri[2] = -1;
|
||||
|
||||
/* first check for exact match */
|
||||
if (equals_v3v3(co, v1)) {
|
||||
w[0] = 1.0f;
|
||||
tri[0] = 0; tri[1] = 1; tri[2] = 3;
|
||||
}
|
||||
else if (equals_v3v3(co, v2)) {
|
||||
w[1] = 1.0f;
|
||||
tri[0] = 0; tri[1] = 1; tri[2] = 3;
|
||||
}
|
||||
else if (equals_v3v3(co, v3)) {
|
||||
w[2] = 1.0f;
|
||||
tri[0] = 1; tri[1] = 2; tri[2] = 3;
|
||||
}
|
||||
else if (v4 && equals_v3v3(co, v4)) {
|
||||
w[3] = 1.0f;
|
||||
tri[0] = 1; tri[1] = 2; tri[2] = 3;
|
||||
}
|
||||
else {
|
||||
/* otherwise compute barycentric interpolation weights */
|
||||
float n1[3], n2[3], n[3];
|
||||
bool degenerate;
|
||||
|
||||
sub_v3_v3v3(n1, v1, v3);
|
||||
if (v4) {
|
||||
sub_v3_v3v3(n2, v2, v4);
|
||||
}
|
||||
else {
|
||||
sub_v3_v3v3(n2, v2, v3);
|
||||
}
|
||||
cross_v3_v3v3(n, n1, n2);
|
||||
|
||||
/* OpenGL seems to split this way, so we do too */
|
||||
if (v4) {
|
||||
degenerate = barycentric_weights(v1, v2, v4, co, n, w);
|
||||
SWAP(float, w[2], w[3]);
|
||||
tri[0] = 0; tri[1] = 1; tri[2] = 3;
|
||||
|
||||
if (degenerate || (w[0] < 0.0f)) {
|
||||
/* if w[1] is negative, co is on the other side of the v1-v3 edge,
|
||||
* so we interpolate using the other triangle */
|
||||
degenerate = barycentric_weights(v2, v3, v4, co, n, w2);
|
||||
|
||||
if (!degenerate) {
|
||||
w[0] = 0.0f;
|
||||
w[1] = w2[0];
|
||||
w[2] = w2[1];
|
||||
w[3] = w2[2];
|
||||
tri[0] = 1; tri[1] = 2; tri[2] = 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
barycentric_weights(v1, v2, v3, co, n, w);
|
||||
tri[0] = 0; tri[1] = 1; tri[2] = 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* return 1 of point is inside triangle, 2 if it's on the edge, 0 if point is outside of triangle */
|
||||
int barycentric_inside_triangle_v2(const float w[3])
|
||||
{
|
||||
|
@@ -73,6 +73,7 @@
|
||||
#include "DNA_genfile.h"
|
||||
#include "DNA_group_types.h"
|
||||
#include "DNA_gpencil_types.h"
|
||||
#include "DNA_hair_types.h"
|
||||
#include "DNA_ipo_types.h"
|
||||
#include "DNA_key_types.h"
|
||||
#include "DNA_lattice_types.h"
|
||||
@@ -4423,6 +4424,7 @@ static void direct_link_particlesystems(FileData *fd, ListBase *particles)
|
||||
|
||||
psys->edit = NULL;
|
||||
psys->free_edit = NULL;
|
||||
psys->hairedit = NULL;
|
||||
psys->pathcache = NULL;
|
||||
psys->childcache = NULL;
|
||||
BLI_listbase_clear(&psys->pathcachebufs);
|
||||
@@ -4665,6 +4667,7 @@ static void direct_link_mesh(FileData *fd, Mesh *mesh)
|
||||
|
||||
mesh->bb = NULL;
|
||||
mesh->edit_btmesh = NULL;
|
||||
mesh->edit_strands = NULL;
|
||||
mesh->batch_cache = NULL;
|
||||
|
||||
/* happens with old files */
|
||||
@@ -5117,6 +5120,28 @@ static void direct_link_pose(FileData *fd, bPose *pose)
|
||||
}
|
||||
}
|
||||
|
||||
static void direct_link_hair(FileData *fd, HairPattern *hair)
|
||||
{
|
||||
if (!hair) {
|
||||
return;
|
||||
}
|
||||
|
||||
// cache the old pointer to calculate offsets for groups
|
||||
const HairFollicle *old_follicles = hair->follicles;
|
||||
hair->follicles = newdataadr(fd, hair->follicles);
|
||||
|
||||
link_list(fd, &hair->groups);
|
||||
for (HairGroup *group = hair->groups.first; group; group = group->next) {
|
||||
group->follicles = hair->follicles + (int)(group->follicles - old_follicles);
|
||||
|
||||
group->strands_parent_index = newdataadr(fd, group->strands_parent_index);
|
||||
group->strands_parent_weight = newdataadr(fd, group->strands_parent_weight);
|
||||
|
||||
group->draw_batch_cache = NULL;
|
||||
group->draw_texture_cache = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void direct_link_modifiers(FileData *fd, ListBase *lb)
|
||||
{
|
||||
ModifierData *md;
|
||||
@@ -5438,6 +5463,14 @@ static void direct_link_modifiers(FileData *fd, ListBase *lb)
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (md->type == eModifierType_Hair) {
|
||||
HairModifierData *hmd = (HairModifierData *)md;
|
||||
|
||||
hmd->hair = newdataadr(fd, hmd->hair);
|
||||
direct_link_hair(fd, hmd->hair);
|
||||
|
||||
hmd->edit = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5467,7 +5500,7 @@ static void direct_link_object(FileData *fd, Object *ob)
|
||||
* See [#34776, #42780] for more information.
|
||||
*/
|
||||
if (fd->memfile || (ob->id.tag & (LIB_TAG_EXTERN | LIB_TAG_INDIRECT))) {
|
||||
ob->mode &= ~(OB_MODE_EDIT | OB_MODE_PARTICLE_EDIT);
|
||||
ob->mode &= ~(OB_MODE_EDIT | OB_MODE_PARTICLE_EDIT | OB_MODE_HAIR_EDIT);
|
||||
if (!fd->memfile) {
|
||||
ob->mode &= ~OB_MODE_POSE;
|
||||
}
|
||||
@@ -5818,6 +5851,14 @@ static void lib_link_scene(FileData *fd, Main *main)
|
||||
|
||||
sce->toolsettings->particle.shape_object = newlibadr(fd, sce->id.lib, sce->toolsettings->particle.shape_object);
|
||||
|
||||
{
|
||||
HairEditSettings *hair_edit = &sce->toolsettings->hair_edit;
|
||||
if (hair_edit->brush)
|
||||
hair_edit->brush = newlibadr(fd, sce->id.lib, hair_edit->brush);
|
||||
if (hair_edit->shape_object)
|
||||
hair_edit->shape_object = newlibadr(fd, sce->id.lib, hair_edit->shape_object);
|
||||
}
|
||||
|
||||
for (BaseLegacy *base_legacy_next, *base_legacy = sce->base.first; base_legacy; base_legacy = base_legacy_next) {
|
||||
base_legacy_next = base_legacy->next;
|
||||
|
||||
@@ -6130,7 +6171,8 @@ static void direct_link_scene(FileData *fd, Scene *sce, Main *bmain)
|
||||
sce->toolsettings->particle.scene_layer = NULL;
|
||||
sce->toolsettings->particle.object = NULL;
|
||||
sce->toolsettings->gp_sculpt.paintcursor = NULL;
|
||||
|
||||
sce->toolsettings->hair_edit.paint_cursor = NULL;
|
||||
|
||||
/* in rare cases this is needed, see [#33806] */
|
||||
if (sce->toolsettings->vpaint) {
|
||||
sce->toolsettings->vpaint->vpaint_prev = NULL;
|
||||
|
@@ -121,6 +121,7 @@
|
||||
#include "DNA_group_types.h"
|
||||
#include "DNA_gpencil_types.h"
|
||||
#include "DNA_fileglobal_types.h"
|
||||
#include "DNA_hair_types.h"
|
||||
#include "DNA_key_types.h"
|
||||
#include "DNA_lattice_types.h"
|
||||
#include "DNA_lamp_types.h"
|
||||
@@ -1706,6 +1707,23 @@ static void write_fmaps(WriteData *wd, ListBase *fbase)
|
||||
}
|
||||
}
|
||||
|
||||
static void write_hair(WriteData *wd, HairPattern *hair)
|
||||
{
|
||||
writestruct(wd, DATA, HairFollicle, hair->num_follicles, hair->follicles);
|
||||
|
||||
writelist(wd, DATA, HairGroup, &hair->groups);
|
||||
for (HairGroup *group = hair->groups.first; group; group = group->next) {
|
||||
const int (*parent_index)[4] = group->strands_parent_index;
|
||||
const float (*parent_weight)[4] = group->strands_parent_weight;
|
||||
if (parent_index) {
|
||||
writedata(wd, DATA, sizeof(*parent_index) * group->num_follicles, parent_index);
|
||||
}
|
||||
if (parent_weight) {
|
||||
writedata(wd, DATA, sizeof(*parent_weight) * group->num_follicles, parent_weight);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void write_modifiers(WriteData *wd, ListBase *modbase)
|
||||
{
|
||||
ModifierData *md;
|
||||
@@ -1877,6 +1895,14 @@ static void write_modifiers(WriteData *wd, ListBase *modbase)
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (md->type == eModifierType_Hair) {
|
||||
HairModifierData *hmd = (HairModifierData *)md;
|
||||
|
||||
if (hmd->hair) {
|
||||
writestruct(wd, DATA, HairPattern, 1, hmd->hair);
|
||||
write_hair(wd, hmd->hair);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2259,6 +2285,7 @@ static void write_mesh(WriteData *wd, Mesh *mesh)
|
||||
CustomData_reset(&mesh->pdata);
|
||||
CustomData_reset(&mesh->ldata);
|
||||
mesh->edit_btmesh = NULL;
|
||||
mesh->edit_strands = NULL;
|
||||
|
||||
/* now fill in polys to mfaces */
|
||||
/* XXX This breaks writing design, by using temp allocated memory, which will likely generate
|
||||
|
@@ -119,6 +119,10 @@ set(SRC
|
||||
intern/bmesh_queries.c
|
||||
intern/bmesh_queries.h
|
||||
intern/bmesh_queries_inline.h
|
||||
intern/bmesh_strands.c
|
||||
intern/bmesh_strands.h
|
||||
intern/bmesh_strands_conv.c
|
||||
intern/bmesh_strands_conv.h
|
||||
intern/bmesh_structure.c
|
||||
intern/bmesh_structure.h
|
||||
intern/bmesh_structure_inline.h
|
||||
|
@@ -244,6 +244,8 @@ extern "C" {
|
||||
#include "intern/bmesh_mesh_validate.h"
|
||||
#include "intern/bmesh_mods.h"
|
||||
#include "intern/bmesh_operators.h"
|
||||
#include "intern/bmesh_strands.h"
|
||||
#include "intern/bmesh_strands_conv.h"
|
||||
#include "intern/bmesh_polygon.h"
|
||||
#include "intern/bmesh_polygon_edgenet.h"
|
||||
#include "intern/bmesh_queries.h"
|
||||
|
@@ -900,6 +900,34 @@ void BM_elem_float_data_set(CustomData *cd, void *element, int type, const float
|
||||
if (f) *f = val;
|
||||
}
|
||||
|
||||
float BM_elem_float_data_named_get(CustomData *cd, void *element, int type, const char *name)
|
||||
{
|
||||
const float *f = CustomData_bmesh_get_named(cd, ((BMHeader *)element)->data, type, name);
|
||||
return f ? *f : 0.0f;
|
||||
}
|
||||
|
||||
void BM_elem_float_data_named_set(CustomData *cd, void *element, int type, const char *name, const float val)
|
||||
{
|
||||
float *f = CustomData_bmesh_get_named(cd, ((BMHeader *)element)->data, type, name);
|
||||
if (f) *f = val;
|
||||
}
|
||||
|
||||
void BM_elem_meshsample_data_named_get(CustomData *cd, void *element, int type, const char *name, MeshSample *val)
|
||||
{
|
||||
const MeshSample *s = CustomData_bmesh_get_named(cd, ((BMHeader *)element)->data, type, name);
|
||||
if (s)
|
||||
memcpy(val, s, sizeof(MeshSample));
|
||||
else
|
||||
memset(val, 0, sizeof(MeshSample));
|
||||
}
|
||||
|
||||
void BM_elem_meshsample_data_named_set(CustomData *cd, void *element, int type, const char *name, const MeshSample *val)
|
||||
{
|
||||
MeshSample *s = CustomData_bmesh_get_named(cd, ((BMHeader *)element)->data, type, name);
|
||||
if (s)
|
||||
memcpy(s, val, sizeof(MeshSample));
|
||||
}
|
||||
|
||||
/** \name Loop interpolation functions: BM_vert_loop_groups_data_layer_***
|
||||
*
|
||||
* Handling loop custom-data such as UV's, while keeping contiguous fans is rather tedious.
|
||||
|
@@ -29,6 +29,8 @@
|
||||
|
||||
struct LinkNode;
|
||||
struct MemArena;
|
||||
struct MeshSample;
|
||||
|
||||
|
||||
void BM_loop_interp_multires_ex(
|
||||
BMesh *bm, BMLoop *l_dst, const BMFace *f_src,
|
||||
@@ -54,6 +56,10 @@ void BM_data_layer_copy(BMesh *bm, CustomData *data, int type, int src_n, int d
|
||||
|
||||
float BM_elem_float_data_get(CustomData *cd, void *element, int type);
|
||||
void BM_elem_float_data_set(CustomData *cd, void *element, int type, const float val);
|
||||
float BM_elem_float_data_named_get(CustomData *cd, void *element, int type, const char *name);
|
||||
void BM_elem_float_data_named_set(CustomData *cd, void *element, int type, const char *name, const float val);
|
||||
void BM_elem_meshsample_data_named_get(CustomData *cd, void *element, int type, const char *name, struct MeshSample *val);
|
||||
void BM_elem_meshsample_data_named_set(CustomData *cd, void *element, int type, const char *name, const struct MeshSample *val);
|
||||
|
||||
void BM_face_interp_from_face_ex(
|
||||
BMesh *bm, BMFace *f_dst, const BMFace *f_src, const bool do_vertex,
|
||||
|
@@ -29,6 +29,8 @@
|
||||
#ifndef __BMESH_ITERATORS_INLINE_H__
|
||||
#define __BMESH_ITERATORS_INLINE_H__
|
||||
|
||||
#include "BLI_mempool.h"
|
||||
|
||||
/* inline here optimizes out the switch statement when called with
|
||||
* constant values (which is very common), nicer for loop-in-loop situations */
|
||||
|
||||
@@ -43,7 +45,6 @@ BLI_INLINE void *BM_iter_step(BMIter *iter)
|
||||
return iter->step(iter);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \brief Iterator Init
|
||||
*
|
||||
|
@@ -98,6 +98,9 @@
|
||||
#include "bmesh.h"
|
||||
#include "intern/bmesh_private.h" /* for element checking */
|
||||
|
||||
/* XXX stupid hack: linker otherwise strips bmesh_strands_conv.c because it is not used inside bmesh */
|
||||
void *__dummy_hack__ = &BM_strands_count_psys_keys;
|
||||
|
||||
void BM_mesh_cd_flag_ensure(BMesh *bm, Mesh *mesh, const char cd_flag)
|
||||
{
|
||||
const char cd_flag_all = BM_mesh_cd_flag_from_bmesh(bm) | cd_flag;
|
||||
@@ -200,6 +203,7 @@ void BM_mesh_bm_from_me(
|
||||
BMFace *f;
|
||||
float (*keyco)[3] = NULL;
|
||||
int totloops, i, j;
|
||||
CustomDataMask mask = CD_MASK_BMESH | params->cd_mask_extra;
|
||||
|
||||
/* free custom data */
|
||||
/* this isnt needed in most cases but do just incase */
|
||||
@@ -210,10 +214,10 @@ void BM_mesh_bm_from_me(
|
||||
|
||||
if (!me || !me->totvert) {
|
||||
if (me) { /*no verts? still copy customdata layout*/
|
||||
CustomData_copy(&me->vdata, &bm->vdata, CD_MASK_BMESH, CD_ASSIGN, 0);
|
||||
CustomData_copy(&me->edata, &bm->edata, CD_MASK_BMESH, CD_ASSIGN, 0);
|
||||
CustomData_copy(&me->ldata, &bm->ldata, CD_MASK_BMESH, CD_ASSIGN, 0);
|
||||
CustomData_copy(&me->pdata, &bm->pdata, CD_MASK_BMESH, CD_ASSIGN, 0);
|
||||
CustomData_copy(&me->vdata, &bm->vdata, mask, CD_ASSIGN, 0);
|
||||
CustomData_copy(&me->edata, &bm->edata, mask, CD_ASSIGN, 0);
|
||||
CustomData_copy(&me->ldata, &bm->ldata, mask, CD_ASSIGN, 0);
|
||||
CustomData_copy(&me->pdata, &bm->pdata, mask, CD_ASSIGN, 0);
|
||||
|
||||
CustomData_bmesh_init_pool(&bm->vdata, me->totvert, BM_VERT);
|
||||
CustomData_bmesh_init_pool(&bm->edata, me->totedge, BM_EDGE);
|
||||
@@ -225,10 +229,10 @@ void BM_mesh_bm_from_me(
|
||||
|
||||
vtable = MEM_mallocN(sizeof(void **) * me->totvert, "mesh to bmesh vtable");
|
||||
|
||||
CustomData_copy(&me->vdata, &bm->vdata, CD_MASK_BMESH, CD_CALLOC, 0);
|
||||
CustomData_copy(&me->edata, &bm->edata, CD_MASK_BMESH, CD_CALLOC, 0);
|
||||
CustomData_copy(&me->ldata, &bm->ldata, CD_MASK_BMESH, CD_CALLOC, 0);
|
||||
CustomData_copy(&me->pdata, &bm->pdata, CD_MASK_BMESH, CD_CALLOC, 0);
|
||||
CustomData_copy(&me->vdata, &bm->vdata, mask, CD_CALLOC, 0);
|
||||
CustomData_copy(&me->edata, &bm->edata, mask, CD_CALLOC, 0);
|
||||
CustomData_copy(&me->ldata, &bm->ldata, mask, CD_CALLOC, 0);
|
||||
CustomData_copy(&me->pdata, &bm->pdata, mask, CD_CALLOC, 0);
|
||||
|
||||
if ((params->active_shapekey != 0) && (me->key != NULL)) {
|
||||
actkey = BLI_findlink(&me->key->block, params->active_shapekey - 1);
|
||||
|
@@ -32,7 +32,10 @@
|
||||
* \ingroup bmesh
|
||||
*/
|
||||
|
||||
#include "BLI_sys_types.h"
|
||||
|
||||
struct Mesh;
|
||||
typedef uint64_t CustomDataMask;
|
||||
|
||||
void BM_mesh_cd_validate(BMesh *bm);
|
||||
void BM_mesh_cd_flag_ensure(BMesh *bm, struct Mesh *mesh, const char cd_flag);
|
||||
@@ -48,6 +51,7 @@ struct BMeshFromMeshParams {
|
||||
uint use_shapekey : 1;
|
||||
/* define the active shape key (index + 1) */
|
||||
int active_shapekey;
|
||||
int64_t cd_mask_extra;
|
||||
};
|
||||
void BM_mesh_bm_from_me(
|
||||
BMesh *bm, struct Mesh *me,
|
||||
|
@@ -54,6 +54,7 @@ void bmo_create_icosphere_exec(BMesh *bm, BMOperator *op);
|
||||
void bmo_create_monkey_exec(BMesh *bm, BMOperator *op);
|
||||
void bmo_create_uvsphere_exec(BMesh *bm, BMOperator *op);
|
||||
void bmo_create_vert_exec(BMesh *bm, BMOperator *op);
|
||||
//void bmo_create_strand_exec(BMesh *bm, BMOperator *op);
|
||||
void bmo_delete_exec(BMesh *bm, BMOperator *op);
|
||||
void bmo_dissolve_edges_exec(BMesh *bm, BMOperator *op);
|
||||
void bmo_dissolve_faces_exec(BMesh *bm, BMOperator *op);
|
||||
|
158
source/blender/bmesh/intern/bmesh_strands.c
Normal file
@@ -0,0 +1,158 @@
|
||||
/*
|
||||
* ***** BEGIN GPL LICENSE BLOCK *****
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* Contributor(s): Lukas Toenne.
|
||||
*
|
||||
* ***** END GPL LICENSE BLOCK *****
|
||||
*/
|
||||
|
||||
/** \file blender/bmesh/intern/bmesh_strands.c
|
||||
* \ingroup bmesh
|
||||
*/
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "BLI_utildefines.h"
|
||||
#include "BLI_math.h"
|
||||
#include "BLI_mempool.h"
|
||||
|
||||
#include "bmesh.h"
|
||||
#include "bmesh_private.h"
|
||||
|
||||
/*
|
||||
* STRANDS OF MESH CALLBACKS
|
||||
*/
|
||||
|
||||
void bmstranditer__strands_of_mesh_begin(struct BMIter__elem_of_mesh *iter)
|
||||
{
|
||||
BLI_mempool_iternew(iter->pooliter.pool, &iter->pooliter);
|
||||
}
|
||||
|
||||
void *bmstranditer__strands_of_mesh_step(struct BMIter__elem_of_mesh *iter)
|
||||
{
|
||||
BMVert *v;
|
||||
|
||||
do {
|
||||
v = BLI_mempool_iterstep(&iter->pooliter);
|
||||
} while (v && !BM_strands_vert_is_root(v));
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
/*
|
||||
* VERTS OF STRAND CALLBACKS
|
||||
*/
|
||||
|
||||
/* BMIter__vert_of_strand is not included in the union in BMIter, just make sure it is big enough */
|
||||
BLI_STATIC_ASSERT(sizeof(BMIter__vert_of_strand) <= sizeof(BMIter), "BMIter must be at least as large as BMIter__vert_of_strand")
|
||||
|
||||
void bmstranditer__verts_of_strand_begin(struct BMIter__vert_of_strand *iter)
|
||||
{
|
||||
iter->e_next = iter->v_next->e;
|
||||
}
|
||||
|
||||
void *bmstranditer__verts_of_strand_step(struct BMIter__vert_of_strand *iter)
|
||||
{
|
||||
BMVert *v_curr = iter->v_next;
|
||||
|
||||
if (iter->e_next) {
|
||||
BMEdge *e_first = iter->e_next;
|
||||
|
||||
/* select the other vertex of the current edge */
|
||||
iter->v_next = (iter->v_next == iter->e_next->v1 ? iter->e_next->v2 : iter->e_next->v1);
|
||||
|
||||
/* select the next edge of the current vertex */
|
||||
iter->e_next = bmesh_disk_edge_next(iter->e_next, iter->v_next);
|
||||
if (iter->e_next == e_first) {
|
||||
/* only one edge means the last segment, terminate */
|
||||
iter->e_next = NULL;
|
||||
}
|
||||
}
|
||||
else
|
||||
iter->v_next = NULL; /* last vertex, terminate */
|
||||
|
||||
return v_curr;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
int BM_strands_count(BMesh *bm)
|
||||
{
|
||||
BMVert *v;
|
||||
BMIter iter;
|
||||
|
||||
int count = 0;
|
||||
BM_ITER_STRANDS(v, &iter, bm, BM_STRANDS_OF_MESH) {
|
||||
++count;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
int BM_strands_keys_count(BMVert *root)
|
||||
{
|
||||
BMVert *v;
|
||||
BMIter iter;
|
||||
|
||||
int count = 0;
|
||||
BM_ITER_STRANDS_ELEM(v, &iter, root, BM_VERTS_OF_STRAND) {
|
||||
++count;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
int BM_strands_keys_count_max(BMesh *bm)
|
||||
{
|
||||
BMVert *v;
|
||||
BMIter iter;
|
||||
int maxkeys = 0;
|
||||
BM_ITER_STRANDS(v, &iter, bm, BM_STRANDS_OF_MESH) {
|
||||
int n = BM_strands_keys_count(v);
|
||||
if (n > maxkeys)
|
||||
maxkeys = n;
|
||||
}
|
||||
return maxkeys;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
/* Create a new strand */
|
||||
BMVert *BM_strands_create(BMesh *bm, int len, bool set_defaults)
|
||||
{
|
||||
float co[3] = {0.0f, 0.0f, 0.0f};
|
||||
|
||||
BMVert *root, *v, *vprev;
|
||||
int k;
|
||||
|
||||
for (k = 0; k < len; ++k) {
|
||||
vprev = v;
|
||||
v = BM_vert_create(bm, co, NULL, set_defaults ? BM_CREATE_NOP : BM_CREATE_SKIP_CD);
|
||||
|
||||
zero_v3(v->no);
|
||||
|
||||
/* root */
|
||||
if (k == 0) {
|
||||
root = v;
|
||||
}
|
||||
else {
|
||||
/*BMEdge *e =*/ BM_edge_create(bm, vprev, v, NULL, set_defaults ? BM_CREATE_NOP : BM_CREATE_SKIP_CD);
|
||||
}
|
||||
}
|
||||
|
||||
return root;
|
||||
}
|
216
source/blender/bmesh/intern/bmesh_strands.h
Normal file
@@ -0,0 +1,216 @@
|
||||
/*
|
||||
* ***** BEGIN GPL LICENSE BLOCK *****
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* Contributor(s): Lukas Toenne.
|
||||
*
|
||||
* ***** END GPL LICENSE BLOCK *****
|
||||
*/
|
||||
|
||||
#ifndef __BMESH_STRANDS_H__
|
||||
#define __BMESH_STRANDS_H__
|
||||
|
||||
/** \file blender/bmesh/intern/bmesh_strands.h
|
||||
* \ingroup bmesh
|
||||
*/
|
||||
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
#include "bmesh.h"
|
||||
#include "bmesh_queries.h"
|
||||
#include "bmesh_structure.h"
|
||||
|
||||
/* True if v is the root of a strand */
|
||||
BLI_INLINE bool BM_strands_vert_is_root(BMVert *v)
|
||||
{
|
||||
BMEdge *e_first = v->e;
|
||||
BMEdge *e_next;
|
||||
|
||||
if (!e_first)
|
||||
return true; /* single vertex is both root and tip */
|
||||
e_next = bmesh_disk_edge_next(e_first, v);
|
||||
|
||||
/* with a single edge, the vertex is either first or last of the curve;
|
||||
* first vertex is defined as the root
|
||||
*/
|
||||
if (e_next == e_first) {
|
||||
if (e_first->v1 == v)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/* True if v is the tip of a strand */
|
||||
BLI_INLINE bool BM_strands_vert_is_tip(BMVert *v)
|
||||
{
|
||||
BMEdge *e_first = v->e;
|
||||
BMEdge *e_next;
|
||||
|
||||
if (!e_first)
|
||||
return true; /* single vertex is both root and tip */
|
||||
e_next = bmesh_disk_edge_next(e_first, v);
|
||||
|
||||
/* with a single edge, the vertex is either first or last of the curve;
|
||||
* last vertex is defined as the tip
|
||||
*/
|
||||
if (e_next == e_first) {
|
||||
if (e_first->v2 == v)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Next vertex on a strand */
|
||||
BLI_INLINE BMVert *BM_strands_vert_next(BMVert *v)
|
||||
{
|
||||
BMEdge *e_first = v->e;
|
||||
BMEdge *e_next;
|
||||
|
||||
/* one of the edges leads to the previous vertex */
|
||||
if (e_first) {
|
||||
if (e_first->v1 == v)
|
||||
return e_first->v2;
|
||||
|
||||
e_next = bmesh_disk_edge_next(e_first, v);
|
||||
if (e_next->v1 == v)
|
||||
return e_next->v2;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Previous vertex on a strand */
|
||||
BLI_INLINE BMVert *BM_strands_vert_prev(BMVert *v)
|
||||
{
|
||||
BMEdge *e_first = v->e;
|
||||
BMEdge *e_next;
|
||||
|
||||
/* one of the edges leads to the previous vertex */
|
||||
if (e_first) {
|
||||
if (e_first->v2 == v)
|
||||
return e_first->v1;
|
||||
|
||||
e_next = bmesh_disk_edge_next(e_first, v);
|
||||
if (e_next->v2 == v)
|
||||
return e_next->v1;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int BM_strands_count(BMesh *bm);
|
||||
int BM_strands_keys_count(BMVert *root);
|
||||
int BM_strands_keys_count_max(BMesh *bm);
|
||||
|
||||
/* Create a new strand */
|
||||
struct BMVert *BM_strands_create(struct BMesh *bm, int len, bool set_defaults);
|
||||
|
||||
/* ==== Iterators ==== */
|
||||
|
||||
typedef enum BMStrandsIterType {
|
||||
BM_STRANDS_OF_MESH,
|
||||
BM_VERTS_OF_STRAND,
|
||||
} BMStrandsIterType;
|
||||
|
||||
#define BM_ITER_STRANDS(ele, iter, bm, itype) \
|
||||
for (BM_CHECK_TYPE_ELEM_ASSIGN(ele) = BM_strand_iter_new(iter, bm, itype, NULL); \
|
||||
ele; \
|
||||
BM_CHECK_TYPE_ELEM_ASSIGN(ele) = BM_iter_step(iter))
|
||||
|
||||
#define BM_ITER_STRANDS_INDEX(ele, iter, bm, itype, indexvar) \
|
||||
for (BM_CHECK_TYPE_ELEM_ASSIGN(ele) = BM_strand_iter_new(iter, bm, itype, NULL), indexvar = 0; \
|
||||
ele; \
|
||||
BM_CHECK_TYPE_ELEM_ASSIGN(ele) = BM_iter_step(iter), (indexvar)++)
|
||||
|
||||
#define BM_ITER_STRANDS_ELEM(ele, iter, data, itype) \
|
||||
for (BM_CHECK_TYPE_ELEM_ASSIGN(ele) = BM_strand_iter_new(iter, NULL, itype, data); \
|
||||
ele; \
|
||||
BM_CHECK_TYPE_ELEM_ASSIGN(ele) = BM_iter_step(iter))
|
||||
|
||||
#define BM_ITER_STRANDS_ELEM_INDEX(ele, iter, data, itype, indexvar) \
|
||||
for (BM_CHECK_TYPE_ELEM_ASSIGN(ele) = BM_strand_iter_new(iter, NULL, itype, data), indexvar = 0; \
|
||||
ele; \
|
||||
BM_CHECK_TYPE_ELEM_ASSIGN(ele) = BM_iter_step(iter), (indexvar)++)
|
||||
|
||||
typedef struct BMIter__vert_of_strand {
|
||||
BMVert *v_next;
|
||||
BMEdge *e_next;
|
||||
} BMIter__vert_of_strand;
|
||||
|
||||
void bmstranditer__strands_of_mesh_begin(struct BMIter__elem_of_mesh *iter);
|
||||
void *bmstranditer__strands_of_mesh_step(struct BMIter__elem_of_mesh *iter);
|
||||
|
||||
void bmstranditer__verts_of_strand_begin(struct BMIter__vert_of_strand *iter);
|
||||
void *bmstranditer__verts_of_strand_step(struct BMIter__vert_of_strand *iter);
|
||||
|
||||
BLI_INLINE bool BM_strand_iter_init(BMIter *iter, BMesh *bm, const char itype, void *data)
|
||||
{
|
||||
/* int argtype; */
|
||||
iter->itype = itype;
|
||||
|
||||
/* inlining optimizes out this switch when called with the defined type */
|
||||
switch ((BMStrandsIterType)itype) {
|
||||
case BM_STRANDS_OF_MESH:
|
||||
BLI_assert(bm != NULL);
|
||||
BLI_assert(data == NULL);
|
||||
iter->begin = (BMIter__begin_cb)bmstranditer__strands_of_mesh_begin;
|
||||
iter->step = (BMIter__step_cb)bmstranditer__strands_of_mesh_step;
|
||||
iter->data.elem_of_mesh.pooliter.pool = bm->vpool;
|
||||
break;
|
||||
case BM_VERTS_OF_STRAND: {
|
||||
BMVert *root;
|
||||
|
||||
BLI_assert(data != NULL);
|
||||
BLI_assert(((BMElem *)data)->head.htype == BM_VERT);
|
||||
root = (BMVert *)data;
|
||||
BLI_assert(BM_strands_vert_is_root(root));
|
||||
iter->begin = (BMIter__begin_cb)bmstranditer__verts_of_strand_begin;
|
||||
iter->step = (BMIter__step_cb)bmstranditer__verts_of_strand_step;
|
||||
((BMIter__vert_of_strand *)(&iter->data))->v_next = root;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
/* fallback to regular bmesh iterator */
|
||||
return BM_iter_init(iter, bm, itype, data);
|
||||
break;
|
||||
}
|
||||
|
||||
iter->begin(iter);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Iterator New
|
||||
*
|
||||
* Takes a bmesh iterator structure and fills
|
||||
* it with the appropriate function pointers based
|
||||
* upon its type and then calls BMeshIter_step()
|
||||
* to return the first element of the iterator.
|
||||
*
|
||||
*/
|
||||
BLI_INLINE void *BM_strand_iter_new(BMIter *iter, BMesh *bm, const char itype, void *data)
|
||||
{
|
||||
if (LIKELY(BM_strand_iter_init(iter, bm, itype, data))) {
|
||||
return BM_iter_step(iter);
|
||||
}
|
||||
else {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
#define BM_strand_iter_new(iter, bm, itype, data) \
|
||||
(BM_ITER_CHECK_TYPE_DATA(data), BM_strand_iter_new(iter, bm, itype, data))
|
||||
|
||||
#endif /* __BMESH_STRANDS_H__ */
|
720
source/blender/bmesh/intern/bmesh_strands_conv.c
Normal file
@@ -0,0 +1,720 @@
|
||||
/*
|
||||
* ***** BEGIN GPL LICENSE BLOCK *****
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* Contributor(s): Lukas Toenne.
|
||||
*
|
||||
* ***** END GPL LICENSE BLOCK *****
|
||||
*/
|
||||
|
||||
/** \file blender/bmesh/intern/bmesh_strands_conv.c
|
||||
* \ingroup bmesh
|
||||
*
|
||||
* BM mesh conversion functions.
|
||||
*/
|
||||
|
||||
#include "DNA_meshdata_types.h"
|
||||
#include "DNA_object_types.h"
|
||||
#include "DNA_particle_types.h"
|
||||
#include "DNA_key_types.h"
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "BLI_listbase.h"
|
||||
#include "BLI_math.h"
|
||||
|
||||
#include "BKE_customdata.h"
|
||||
#include "BKE_key.h"
|
||||
#include "BKE_main.h"
|
||||
#include "BKE_mesh_sample.h"
|
||||
#include "BKE_particle.h"
|
||||
|
||||
#include "bmesh.h"
|
||||
#include "intern/bmesh_private.h" /* for element checking */
|
||||
|
||||
const char *CD_HAIR_SEGMENT_LENGTH = "HAIR_SEGMENT_LENGTH";
|
||||
const char *CD_HAIR_MASS = "HAIR_MASS";
|
||||
const char *CD_HAIR_WEIGHT = "HAIR_WEIGHT";
|
||||
const char *CD_HAIR_ROOT_LOCATION = "HAIR_ROOT_LOCATION";
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Currently this is only used for Python scripts
|
||||
* which may fail to keep matching UV/TexFace layers.
|
||||
*
|
||||
* \note This should only perform any changes in exceptional cases,
|
||||
* if we need this to be faster we could inline #BM_data_layer_add and only
|
||||
* call #update_data_blocks once at the end.
|
||||
*/
|
||||
void BM_strands_cd_validate(BMesh *UNUSED(bm))
|
||||
{
|
||||
}
|
||||
|
||||
void BM_strands_cd_flag_ensure(BMesh *bm, const char cd_flag)
|
||||
{
|
||||
const char cd_flag_all = BM_strands_cd_flag_from_bmesh(bm) | cd_flag;
|
||||
BM_strands_cd_flag_apply(bm, cd_flag_all);
|
||||
}
|
||||
|
||||
void BM_strands_cd_flag_apply(BMesh *bm, const char UNUSED(cd_flag))
|
||||
{
|
||||
/* CustomData_bmesh_init_pool() must run first */
|
||||
BLI_assert(bm->vdata.totlayer == 0 || bm->vdata.pool != NULL);
|
||||
BLI_assert(bm->edata.totlayer == 0 || bm->edata.pool != NULL);
|
||||
|
||||
if (CustomData_get_named_layer_index(&bm->vdata, CD_PROP_FLT, CD_HAIR_MASS) < 0) {
|
||||
BM_data_layer_add_named(bm, &bm->vdata, CD_PROP_FLT, CD_HAIR_MASS);
|
||||
}
|
||||
if (CustomData_get_named_layer_index(&bm->vdata, CD_PROP_FLT, CD_HAIR_WEIGHT) < 0) {
|
||||
BM_data_layer_add_named(bm, &bm->vdata, CD_PROP_FLT, CD_HAIR_WEIGHT);
|
||||
}
|
||||
if (CustomData_get_named_layer_index(&bm->vdata, CD_MSURFACE_SAMPLE, CD_HAIR_ROOT_LOCATION) < 0) {
|
||||
BM_data_layer_add_named(bm, &bm->vdata, CD_MSURFACE_SAMPLE, CD_HAIR_ROOT_LOCATION);
|
||||
}
|
||||
if (CustomData_get_named_layer_index(&bm->vdata, CD_PROP_FLT, CD_HAIR_SEGMENT_LENGTH) < 0) {
|
||||
BM_data_layer_add_named(bm, &bm->vdata, CD_PROP_FLT, CD_HAIR_SEGMENT_LENGTH);
|
||||
}
|
||||
}
|
||||
|
||||
char BM_strands_cd_flag_from_bmesh(BMesh *UNUSED(bm))
|
||||
{
|
||||
char cd_flag = 0;
|
||||
return cd_flag;
|
||||
}
|
||||
|
||||
/* particles */
|
||||
|
||||
int BM_strands_count_psys_keys(ParticleSystem *psys)
|
||||
{
|
||||
ParticleData *pa;
|
||||
int p;
|
||||
int totkeys = 0;
|
||||
|
||||
for (p = 0, pa = psys->particles; p < psys->totpart; ++p, ++pa)
|
||||
totkeys += pa->totkey;
|
||||
|
||||
return totkeys;
|
||||
}
|
||||
|
||||
#if 0
|
||||
static KeyBlock *bm_set_shapekey_from_psys(BMesh *bm, ParticleSystem *psys, int totvert, int act_key_nr)
|
||||
{
|
||||
KeyBlock *actkey, *block;
|
||||
int i, j;
|
||||
|
||||
if (!psys->key) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (act_key_nr != 0)
|
||||
actkey = BLI_findlink(&psys->key->block, act_key_nr - 1);
|
||||
else
|
||||
actkey = NULL;
|
||||
|
||||
CustomData_add_layer(&bm->vdata, CD_SHAPE_KEYINDEX, CD_ASSIGN, NULL, 0);
|
||||
|
||||
/* check if we need to generate unique ids for the shapekeys.
|
||||
* this also exists in the file reading code, but is here for
|
||||
* a sanity check */
|
||||
if (!psys->key->uidgen) {
|
||||
fprintf(stderr,
|
||||
"%s had to generate shape key uid's in a situation we shouldn't need to! "
|
||||
"(bmesh internal error)\n",
|
||||
__func__);
|
||||
|
||||
psys->key->uidgen = 1;
|
||||
for (block = psys->key->block.first; block; block = block->next) {
|
||||
block->uid = psys->key->uidgen++;
|
||||
}
|
||||
}
|
||||
|
||||
if (actkey && actkey->totelem == totvert) {
|
||||
bm->shapenr = act_key_nr;
|
||||
}
|
||||
|
||||
for (i = 0, block = psys->key->block.first; block; block = block->next, i++) {
|
||||
CustomData_add_layer_named(&bm->vdata, CD_SHAPEKEY,
|
||||
CD_ASSIGN, NULL, 0, block->name);
|
||||
|
||||
j = CustomData_get_layer_index_n(&bm->vdata, CD_SHAPEKEY, i);
|
||||
bm->vdata.layers[j].uid = block->uid;
|
||||
}
|
||||
|
||||
return actkey;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* create vertex and edge data for BMesh based on particle hair keys */
|
||||
static void bm_make_particles(BMesh *bm, Object *ob, ParticleSystem *psys, struct DerivedMesh *emitter_dm, float (*keyco)[3], int UNUSED(cd_shape_keyindex_offset))
|
||||
{
|
||||
// KeyBlock *block;
|
||||
ParticleData *pa;
|
||||
HairKey *hkey;
|
||||
int p, k;
|
||||
|
||||
int vindex, eindex;
|
||||
BMVert *v = NULL, *v_prev;
|
||||
BMEdge *e;
|
||||
|
||||
float hairmat[4][4];
|
||||
|
||||
/* XXX currently all particles and keys have the same mass, this may change */
|
||||
float mass = psys->part->mass;
|
||||
|
||||
vindex = 0;
|
||||
eindex = 0;
|
||||
for (p = 0, pa = psys->particles; p < psys->totpart; ++p, ++pa) {
|
||||
|
||||
/* hair keys are in a local "hair space", but edit data should be in object space */
|
||||
psys_mat_hair_to_object(ob, emitter_dm, psys->part->from, pa, hairmat);
|
||||
|
||||
for (k = 0, hkey = pa->hair; k < pa->totkey; ++k, ++hkey) {
|
||||
float co[3];
|
||||
|
||||
copy_v3_v3(co, keyco ? keyco[vindex] : hkey->co);
|
||||
mul_m4_v3(hairmat, co);
|
||||
|
||||
v_prev = v;
|
||||
v = BM_vert_create(bm, co, NULL, BM_CREATE_SKIP_CD);
|
||||
BM_elem_index_set(v, vindex); /* set_ok */
|
||||
|
||||
/* transfer flag */
|
||||
// v->head.hflag = BM_vert_flag_from_mflag(mvert->flag & ~SELECT);
|
||||
|
||||
/* this is necessary for selection counts to work properly */
|
||||
// if (hkey->editflag & SELECT) {
|
||||
// BM_vert_select_set(bm, v, true);
|
||||
// }
|
||||
|
||||
// normal_short_to_float_v3(v->no, mvert->no);
|
||||
|
||||
/* Copy Custom Data */
|
||||
// CustomData_to_bmesh_block(&me->vdata, &bm->vdata, vindex, &v->head.data, true);
|
||||
CustomData_bmesh_set_default(&bm->vdata, &v->head.data);
|
||||
|
||||
BM_elem_float_data_named_set(&bm->vdata, v, CD_PROP_FLT, CD_HAIR_MASS, mass);
|
||||
BM_elem_float_data_named_set(&bm->vdata, v, CD_PROP_FLT, CD_HAIR_WEIGHT, hkey->weight);
|
||||
|
||||
/* root */
|
||||
if (k == 0) {
|
||||
MeshSample root_loc;
|
||||
if (BKE_mesh_sample_from_particle(&root_loc, psys, emitter_dm, pa)) {
|
||||
BM_elem_meshsample_data_named_set(&bm->vdata, v, CD_MSURFACE_SAMPLE, CD_HAIR_ROOT_LOCATION, &root_loc);
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
/* set shapekey data */
|
||||
if (psys->key) {
|
||||
/* set shape key original index */
|
||||
if (cd_shape_keyindex_offset != -1) BM_ELEM_CD_SET_INT(v, cd_shape_keyindex_offset, vindex);
|
||||
|
||||
for (block = psys->key->block.first, j = 0; block; block = block->next, j++) {
|
||||
float *co = CustomData_bmesh_get_n(&bm->vdata, v->head.data, CD_SHAPEKEY, j);
|
||||
|
||||
if (co) {
|
||||
copy_v3_v3(co, ((float *)block->data) + 3 * vindex);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
vindex += 1;
|
||||
|
||||
if (k > 0) {
|
||||
e = BM_edge_create(bm, v_prev, v, NULL, BM_CREATE_SKIP_CD);
|
||||
BM_elem_index_set(e, eindex); /* set_ok; one less edge than vertices for each particle */
|
||||
|
||||
/* transfer flags */
|
||||
// e->head.hflag = BM_edge_flag_from_mflag(medge->flag & ~SELECT);
|
||||
|
||||
/* this is necessary for selection counts to work properly */
|
||||
// if (medge->flag & SELECT) {
|
||||
// BM_edge_select_set(bm, e, true);
|
||||
// }
|
||||
|
||||
/* Copy Custom Data */
|
||||
// CustomData_to_bmesh_block(&me->edata, &bm->edata, eindex, &e->head.data, true);
|
||||
CustomData_bmesh_set_default(&bm->edata, &e->head.data);
|
||||
|
||||
eindex += 1;
|
||||
}
|
||||
|
||||
} /* hair keys */
|
||||
|
||||
} /* particles */
|
||||
|
||||
bm->elem_index_dirty &= ~(BM_VERT | BM_EDGE); /* added in order, clear dirty flag */
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief ParticleSystem -> BMesh
|
||||
*/
|
||||
void BM_strands_bm_from_psys(BMesh *bm, Object *ob, ParticleSystem *psys, struct DerivedMesh *emitter_dm,
|
||||
const bool set_key, int UNUSED(act_key_nr))
|
||||
{
|
||||
/*KeyBlock *actkey;*/
|
||||
float (*keyco)[3] = NULL;
|
||||
int totvert, totedge;
|
||||
|
||||
int cd_shape_keyindex_offset;
|
||||
|
||||
/* free custom data */
|
||||
/* this isnt needed in most cases but do just incase */
|
||||
CustomData_free(&bm->vdata, bm->totvert);
|
||||
CustomData_free(&bm->edata, bm->totedge);
|
||||
CustomData_free(&bm->ldata, bm->totloop);
|
||||
CustomData_free(&bm->pdata, bm->totface);
|
||||
|
||||
totvert = BM_strands_count_psys_keys(psys);
|
||||
totedge = totvert - psys->totpart;
|
||||
|
||||
if (!psys || !totvert || !totedge) {
|
||||
if (psys) { /*no verts? still copy customdata layout*/
|
||||
// CustomData_copy(&me->vdata, &bm->vdata, CD_MASK_BMESH, CD_ASSIGN, 0);
|
||||
// CustomData_copy(&me->edata, &bm->edata, CD_MASK_BMESH, CD_ASSIGN, 0);
|
||||
|
||||
CustomData_bmesh_init_pool(&bm->vdata, totvert, BM_VERT);
|
||||
CustomData_bmesh_init_pool(&bm->edata, totedge, BM_EDGE);
|
||||
CustomData_bmesh_init_pool(&bm->ldata, 0, BM_LOOP);
|
||||
CustomData_bmesh_init_pool(&bm->pdata, 0, BM_FACE);
|
||||
}
|
||||
return; /* sanity check */
|
||||
}
|
||||
|
||||
#if 0
|
||||
actkey = bm_set_shapekey_from_psys(bm, psys, totvert, act_key_nr);
|
||||
if (actkey)
|
||||
keyco = actkey->data;
|
||||
#endif
|
||||
|
||||
CustomData_bmesh_init_pool(&bm->vdata, totvert, BM_VERT);
|
||||
CustomData_bmesh_init_pool(&bm->edata, totedge, BM_EDGE);
|
||||
|
||||
BM_strands_cd_flag_apply(bm, /*psys->cd_flag*/0);
|
||||
|
||||
cd_shape_keyindex_offset = /*psys->key ? CustomData_get_offset(&bm->vdata, CD_SHAPE_KEYINDEX) :*/ -1;
|
||||
|
||||
bm_make_particles(bm, ob, psys, emitter_dm, set_key ? keyco : NULL, cd_shape_keyindex_offset);
|
||||
|
||||
|
||||
#if 0 /* TODO */
|
||||
if (me->mselect && me->totselect != 0) {
|
||||
|
||||
BMVert **vert_array = MEM_mallocN(sizeof(BMVert *) * bm->totvert, "VSelConv");
|
||||
BMEdge **edge_array = MEM_mallocN(sizeof(BMEdge *) * bm->totedge, "ESelConv");
|
||||
BMFace **face_array = MEM_mallocN(sizeof(BMFace *) * bm->totface, "FSelConv");
|
||||
MSelect *msel;
|
||||
|
||||
#pragma omp parallel sections if (bm->totvert + bm->totedge + bm->totface >= BM_OMP_LIMIT)
|
||||
{
|
||||
#pragma omp section
|
||||
{ BM_iter_as_array(bm, BM_VERTS_OF_MESH, NULL, (void **)vert_array, bm->totvert); }
|
||||
#pragma omp section
|
||||
{ BM_iter_as_array(bm, BM_EDGES_OF_MESH, NULL, (void **)edge_array, bm->totedge); }
|
||||
#pragma omp section
|
||||
{ BM_iter_as_array(bm, BM_FACES_OF_MESH, NULL, (void **)face_array, bm->totface); }
|
||||
}
|
||||
|
||||
for (i = 0, msel = me->mselect; i < me->totselect; i++, msel++) {
|
||||
switch (msel->type) {
|
||||
case ME_VSEL:
|
||||
BM_select_history_store(bm, (BMElem *)vert_array[msel->index]);
|
||||
break;
|
||||
case ME_ESEL:
|
||||
BM_select_history_store(bm, (BMElem *)edge_array[msel->index]);
|
||||
break;
|
||||
case ME_FSEL:
|
||||
BM_select_history_store(bm, (BMElem *)face_array[msel->index]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
MEM_freeN(vert_array);
|
||||
MEM_freeN(edge_array);
|
||||
MEM_freeN(face_array);
|
||||
}
|
||||
else {
|
||||
me->totselect = 0;
|
||||
if (me->mselect) {
|
||||
MEM_freeN(me->mselect);
|
||||
me->mselect = NULL;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#if 0
|
||||
/**
|
||||
* \brief BMesh -> Mesh
|
||||
*/
|
||||
static BMVert **bm_to_mesh_vertex_map(BMesh *bm, int ototvert)
|
||||
{
|
||||
const int cd_shape_keyindex_offset = CustomData_get_offset(&bm->vdata, CD_SHAPE_KEYINDEX);
|
||||
BMVert **vertMap = NULL;
|
||||
BMVert *eve;
|
||||
int i = 0;
|
||||
BMIter iter;
|
||||
|
||||
/* caller needs to ensure this */
|
||||
BLI_assert(ototvert > 0);
|
||||
|
||||
vertMap = MEM_callocN(sizeof(*vertMap) * ototvert, "vertMap");
|
||||
if (cd_shape_keyindex_offset != -1) {
|
||||
BM_ITER_MESH_INDEX (eve, &iter, bm, BM_VERTS_OF_MESH, i) {
|
||||
const int keyi = BM_ELEM_CD_GET_INT(eve, cd_shape_keyindex_offset);
|
||||
if ((keyi != ORIGINDEX_NONE) && (keyi < ototvert)) {
|
||||
vertMap[keyi] = eve;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
BM_ITER_MESH_INDEX (eve, &iter, bm, BM_VERTS_OF_MESH, i) {
|
||||
if (i < ototvert) {
|
||||
vertMap[i] = eve;
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return vertMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* returns customdata shapekey index from a keyblock or -1
|
||||
* \note could split this out into a more generic function */
|
||||
static int bm_to_mesh_shape_layer_index_from_kb(BMesh *bm, KeyBlock *currkey)
|
||||
{
|
||||
int i;
|
||||
int j = 0;
|
||||
|
||||
for (i = 0; i < bm->vdata.totlayer; i++) {
|
||||
if (bm->vdata.layers[i].type == CD_SHAPEKEY) {
|
||||
if (currkey->uid == bm->vdata.layers[i].uid) {
|
||||
return j;
|
||||
}
|
||||
j++;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
BLI_INLINE void bmesh_quick_edgedraw_flag(MEdge *med, BMEdge *e)
|
||||
{
|
||||
/* this is a cheap way to set the edge draw, its not precise and will
|
||||
* pick the first 2 faces an edge uses.
|
||||
* The dot comparison is a little arbitrary, but set so that a 5 subd
|
||||
* IcoSphere won't vanish but subd 6 will (as with pre-bmesh blender) */
|
||||
|
||||
|
||||
if ( /* (med->flag & ME_EDGEDRAW) && */ /* assume to be true */
|
||||
(e->l && (e->l != e->l->radial_next)) &&
|
||||
(dot_v3v3(e->l->f->no, e->l->radial_next->f->no) > 0.9995f))
|
||||
{
|
||||
med->flag &= ~ME_EDGEDRAW;
|
||||
}
|
||||
else {
|
||||
med->flag |= ME_EDGEDRAW;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static void make_particle_hair(BMesh *bm, BMVert *root, Object *ob, ParticleSystem *psys, struct DerivedMesh *emitter_dm, struct BVHTreeFromMesh *emitter_bvhtree, struct ParticleData *pa)
|
||||
{
|
||||
int totkey = BM_strands_keys_count(root);
|
||||
HairKey *hair;
|
||||
|
||||
BMVert *v;
|
||||
BMIter iter;
|
||||
HairKey *hkey;
|
||||
int k;
|
||||
|
||||
float inv_hairmat[4][4];
|
||||
|
||||
pa->alive = PARS_ALIVE;
|
||||
pa->flag = 0;
|
||||
|
||||
pa->time = 0.0f;
|
||||
pa->lifetime = 100.0f;
|
||||
pa->dietime = 100.0f;
|
||||
|
||||
pa->size = psys->part->size;
|
||||
|
||||
// TODO define other particle stuff ...
|
||||
|
||||
hair = MEM_callocN(totkey * sizeof(HairKey), "hair keys");
|
||||
|
||||
hkey = hair;
|
||||
k = 0;
|
||||
BM_ITER_STRANDS_ELEM(v, &iter, root, BM_VERTS_OF_STRAND) {
|
||||
/* root */
|
||||
if (k == 0) {
|
||||
MeshSample root_loc;
|
||||
BM_elem_meshsample_data_named_get(&bm->vdata, v, CD_MSURFACE_SAMPLE, CD_HAIR_ROOT_LOCATION, &root_loc);
|
||||
if (!BKE_mesh_sample_to_particle(&root_loc, psys, emitter_dm, emitter_bvhtree, pa)) {
|
||||
pa->num = 0;
|
||||
pa->num_dmcache = DMCACHE_NOTFOUND;
|
||||
zero_v4(pa->fuv);
|
||||
pa->foffset = 0.0f;
|
||||
}
|
||||
|
||||
/* edit data is in object space, hair keys must be converted back into "hair space" */
|
||||
psys_mat_hair_to_object(ob, emitter_dm, psys->part->from, pa, inv_hairmat);
|
||||
invert_m4(inv_hairmat);
|
||||
}
|
||||
|
||||
mul_v3_m4v3(hkey->co, inv_hairmat, v->co);
|
||||
mul_v3_m4v3(hkey->world_co, ob->obmat, v->co);
|
||||
|
||||
hkey->time = totkey > 0 ? (float)k / (float)(totkey - 1) : 0.0f;
|
||||
if (k == 0) {
|
||||
/* weight 1.0 is used for pinning hair roots in particles */
|
||||
hkey->weight = 1.0f;
|
||||
}
|
||||
else {
|
||||
hkey->weight = BM_elem_float_data_named_get(&bm->vdata, v, CD_PROP_FLT, CD_HAIR_WEIGHT);
|
||||
}
|
||||
|
||||
++hkey;
|
||||
++k;
|
||||
|
||||
BM_CHECK_ELEMENT(v);
|
||||
}
|
||||
|
||||
if (pa->hair)
|
||||
MEM_freeN(pa->hair);
|
||||
|
||||
pa->hair = hair;
|
||||
pa->totkey = totkey;
|
||||
}
|
||||
|
||||
void BM_strands_bm_to_psys(BMesh *bm, Object *ob, ParticleSystem *psys, struct DerivedMesh *emitter_dm, struct BVHTreeFromMesh *emitter_bvhtree)
|
||||
{
|
||||
ParticleData *particles, *oldparticles;
|
||||
int ototpart, ntotpart;
|
||||
|
||||
BMVert *root;
|
||||
BMIter iter;
|
||||
ParticleData *pa;
|
||||
int p;
|
||||
|
||||
ototpart = psys->totpart;
|
||||
|
||||
ntotpart = BM_strands_count(bm);
|
||||
|
||||
/* new particles block */
|
||||
if (bm->totvert == 0) particles = NULL;
|
||||
else particles = MEM_callocN(ntotpart * sizeof(ParticleData), "particles");
|
||||
|
||||
/* lets save the old particles just in case we are actually working on
|
||||
* a key ... we now do processing of the keys at the end */
|
||||
oldparticles = psys->particles;
|
||||
|
||||
psys->totpart = ntotpart;
|
||||
|
||||
// psys->cd_flag = BM_strands_cd_flag_from_bmesh(bm);
|
||||
|
||||
pa = particles;
|
||||
p = 0;
|
||||
BM_ITER_STRANDS(root, &iter, bm, BM_STRANDS_OF_MESH) {
|
||||
|
||||
make_particle_hair(bm, root, ob, psys, emitter_dm, emitter_bvhtree, pa);
|
||||
|
||||
++pa;
|
||||
++p;
|
||||
}
|
||||
bm->elem_index_dirty &= ~BM_VERT;
|
||||
|
||||
|
||||
#if 0 // TODO
|
||||
{
|
||||
BMEditSelection *selected;
|
||||
me->totselect = BLI_listbase_count(&(bm->selected));
|
||||
|
||||
if (me->mselect) MEM_freeN(me->mselect);
|
||||
|
||||
me->mselect = MEM_callocN(sizeof(MSelect) * me->totselect, "Mesh selection history");
|
||||
|
||||
|
||||
for (i = 0, selected = bm->selected.first; selected; i++, selected = selected->next) {
|
||||
if (selected->htype == BM_VERT) {
|
||||
me->mselect[i].type = ME_VSEL;
|
||||
|
||||
}
|
||||
else if (selected->htype == BM_EDGE) {
|
||||
me->mselect[i].type = ME_ESEL;
|
||||
|
||||
}
|
||||
else if (selected->htype == BM_FACE) {
|
||||
me->mselect[i].type = ME_FSEL;
|
||||
}
|
||||
|
||||
me->mselect[i].index = BM_elem_index_get(selected->ele);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if 0 // TODO
|
||||
/* see comment below, this logic is in twice */
|
||||
|
||||
if (me->key) {
|
||||
const int cd_shape_keyindex_offset = CustomData_get_offset(&bm->vdata, CD_SHAPE_KEYINDEX);
|
||||
|
||||
KeyBlock *currkey;
|
||||
KeyBlock *actkey = BLI_findlink(&me->key->block, bm->shapenr - 1);
|
||||
|
||||
float (*ofs)[3] = NULL;
|
||||
|
||||
/* go through and find any shapekey customdata layers
|
||||
* that might not have corresponding KeyBlocks, and add them if
|
||||
* necessary */
|
||||
j = 0;
|
||||
for (i = 0; i < bm->vdata.totlayer; i++) {
|
||||
if (bm->vdata.layers[i].type != CD_SHAPEKEY)
|
||||
continue;
|
||||
|
||||
for (currkey = me->key->block.first; currkey; currkey = currkey->next) {
|
||||
if (currkey->uid == bm->vdata.layers[i].uid)
|
||||
break;
|
||||
}
|
||||
|
||||
if (!currkey) {
|
||||
currkey = BKE_keyblock_add(me->key, bm->vdata.layers[i].name);
|
||||
currkey->uid = bm->vdata.layers[i].uid;
|
||||
}
|
||||
|
||||
j++;
|
||||
}
|
||||
|
||||
|
||||
/* editing the base key should update others */
|
||||
if ((me->key->type == KEY_RELATIVE) && /* only need offsets for relative shape keys */
|
||||
(actkey != NULL) && /* unlikely, but the active key may not be valid if the
|
||||
* bmesh and the mesh are out of sync */
|
||||
(oldverts != NULL)) /* not used here, but 'oldverts' is used later for applying 'ofs' */
|
||||
{
|
||||
const bool act_is_basis = BKE_keyblock_is_basis(me->key, bm->shapenr - 1);
|
||||
|
||||
/* active key is a base */
|
||||
if (act_is_basis && (cd_shape_keyindex_offset != -1)) {
|
||||
float (*fp)[3] = actkey->data;
|
||||
|
||||
ofs = MEM_callocN(sizeof(float) * 3 * bm->totvert, "currkey->data");
|
||||
mvert = me->mvert;
|
||||
BM_ITER_MESH_INDEX (eve, &iter, bm, BM_VERTS_OF_MESH, i) {
|
||||
const int keyi = BM_ELEM_CD_GET_INT(eve, cd_shape_keyindex_offset);
|
||||
|
||||
if (keyi != ORIGINDEX_NONE) {
|
||||
sub_v3_v3v3(ofs[i], mvert->co, fp[keyi]);
|
||||
}
|
||||
else {
|
||||
/* if there are new vertices in the mesh, we can't propagate the offset
|
||||
* because it will only work for the existing vertices and not the new
|
||||
* ones, creating a mess when doing e.g. subdivide + translate */
|
||||
MEM_freeN(ofs);
|
||||
ofs = NULL;
|
||||
break;
|
||||
}
|
||||
|
||||
mvert++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (currkey = me->key->block.first; currkey; currkey = currkey->next) {
|
||||
const bool apply_offset = (ofs && (currkey != actkey) && (bm->shapenr - 1 == currkey->relative));
|
||||
int cd_shape_offset;
|
||||
int keyi;
|
||||
float (*ofs_pt)[3] = ofs;
|
||||
float *newkey, (*oldkey)[3], *fp;
|
||||
|
||||
j = bm_to_mesh_shape_layer_index_from_kb(bm, currkey);
|
||||
cd_shape_offset = CustomData_get_n_offset(&bm->vdata, CD_SHAPEKEY, j);
|
||||
|
||||
|
||||
fp = newkey = MEM_callocN(me->key->elemsize * bm->totvert, "currkey->data");
|
||||
oldkey = currkey->data;
|
||||
|
||||
mvert = me->mvert;
|
||||
BM_ITER_MESH (eve, &iter, bm, BM_VERTS_OF_MESH) {
|
||||
|
||||
if (currkey == actkey) {
|
||||
copy_v3_v3(fp, eve->co);
|
||||
|
||||
if (actkey != me->key->refkey) { /* important see bug [#30771] */
|
||||
if (cd_shape_keyindex_offset != -1) {
|
||||
if (oldverts) {
|
||||
keyi = BM_ELEM_CD_GET_INT(eve, cd_shape_keyindex_offset);
|
||||
if (keyi != ORIGINDEX_NONE && keyi < currkey->totelem) { /* valid old vertex */
|
||||
copy_v3_v3(mvert->co, oldverts[keyi].co);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (j != -1) {
|
||||
/* in most cases this runs */
|
||||
copy_v3_v3(fp, BM_ELEM_CD_GET_VOID_P(eve, cd_shape_offset));
|
||||
}
|
||||
else if ((oldkey != NULL) &&
|
||||
(cd_shape_keyindex_offset != -1) &&
|
||||
((keyi = BM_ELEM_CD_GET_INT(eve, cd_shape_keyindex_offset)) != ORIGINDEX_NONE) &&
|
||||
(keyi < currkey->totelem))
|
||||
{
|
||||
/* old method of reconstructing keys via vertice's original key indices,
|
||||
* currently used if the new method above fails (which is theoretically
|
||||
* possible in certain cases of undo) */
|
||||
copy_v3_v3(fp, oldkey[keyi]);
|
||||
}
|
||||
else {
|
||||
/* fail! fill in with dummy value */
|
||||
copy_v3_v3(fp, mvert->co);
|
||||
}
|
||||
|
||||
/* propagate edited basis offsets to other shapes */
|
||||
if (apply_offset) {
|
||||
add_v3_v3(fp, *ofs_pt++);
|
||||
}
|
||||
|
||||
fp += 3;
|
||||
mvert++;
|
||||
}
|
||||
|
||||
currkey->totelem = bm->totvert;
|
||||
if (currkey->data) {
|
||||
MEM_freeN(currkey->data);
|
||||
}
|
||||
currkey->data = newkey;
|
||||
}
|
||||
|
||||
if (ofs) MEM_freeN(ofs);
|
||||
}
|
||||
#else
|
||||
psys->particles = particles;
|
||||
#endif
|
||||
|
||||
if (oldparticles) {
|
||||
ParticleData *opa;
|
||||
int op;
|
||||
for (op = 0, opa = oldparticles; op < ototpart; ++op, ++opa)
|
||||
if (opa->hair)
|
||||
MEM_freeN(opa->hair);
|
||||
MEM_freeN(oldparticles);
|
||||
}
|
||||
}
|
62
source/blender/bmesh/intern/bmesh_strands_conv.h
Normal file
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* ***** BEGIN GPL LICENSE BLOCK *****
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* The Original Code is Copyright (C) 2014 Blender Foundation.
|
||||
* All rights reserved.
|
||||
*
|
||||
* The Original Code is: all of this file.
|
||||
*
|
||||
* Contributor(s): Lukas Toenne.
|
||||
*
|
||||
* ***** END GPL LICENSE BLOCK *****
|
||||
*/
|
||||
|
||||
#ifndef __BMESH_STRANDS_CONV_H__
|
||||
#define __BMESH_STRANDS_CONV_H__
|
||||
|
||||
/** \file blender/bmesh/intern/bmesh_strands_conv.h
|
||||
* \ingroup bmesh
|
||||
*/
|
||||
|
||||
struct BMesh;
|
||||
struct Mesh;
|
||||
struct Object;
|
||||
struct ParticleSystem;
|
||||
struct DerivedMesh;
|
||||
struct BVHTreeFromMesh;
|
||||
|
||||
extern const char *CD_HAIR_SEGMENT_LENGTH;
|
||||
extern const char *CD_HAIR_MASS;
|
||||
extern const char *CD_HAIR_WEIGHT;
|
||||
extern const char *CD_HAIR_ROOT_LOCATION;
|
||||
|
||||
void BM_strands_cd_validate(struct BMesh *bm);
|
||||
void BM_strands_cd_flag_ensure(struct BMesh *bm, const char cd_flag);
|
||||
void BM_strands_cd_flag_apply(struct BMesh *bm, const char cd_flag);
|
||||
char BM_strands_cd_flag_from_bmesh(struct BMesh *bm);
|
||||
|
||||
/* particles */
|
||||
|
||||
int BM_strands_count_psys_keys(struct ParticleSystem *psys);
|
||||
void BM_strands_bm_from_psys(struct BMesh *bm, struct Object *ob, struct ParticleSystem *psys, struct DerivedMesh *emitter_dm,
|
||||
const bool set_key, int act_key_nr);
|
||||
void BM_strands_bm_to_psys(struct BMesh *bm, struct Object *ob, struct ParticleSystem *psys, struct DerivedMesh *emitter_dm, struct BVHTreeFromMesh *emitter_bvhtree);
|
||||
|
||||
#define BMALLOC_TEMPLATE_FROM_PSYS(psys) { (CHECK_TYPE_INLINE(psys, ParticleSystem *), \
|
||||
BM_strands_count_psys_keys(psys)), (BM_strands_count_psys_keys(psys) - (psys)->totpart), 0, 0 }
|
||||
|
||||
#endif /* __BMESH_STRANDS_CONV_H__ */
|
@@ -57,10 +57,13 @@ set(SRC
|
||||
intern/draw_cache.c
|
||||
intern/draw_cache_impl_curve.c
|
||||
intern/draw_cache_impl_displist.c
|
||||
intern/draw_cache_impl_hair.c
|
||||
intern/draw_cache_impl_lattice.c
|
||||
intern/draw_cache_impl_mesh.c
|
||||
intern/draw_cache_impl_particles.c
|
||||
intern/draw_cache_impl_strands.c
|
||||
intern/draw_common.c
|
||||
intern/draw_hair.c
|
||||
intern/draw_manager.c
|
||||
intern/draw_manager_text.c
|
||||
intern/draw_manager_profiling.c
|
||||
@@ -70,6 +73,7 @@ set(SRC
|
||||
modes/edit_lattice_mode.c
|
||||
modes/edit_mesh_mode.c
|
||||
modes/edit_metaball_mode.c
|
||||
modes/edit_strands_mode.c
|
||||
modes/edit_surface_mode.c
|
||||
modes/edit_text_mode.c
|
||||
modes/object_mode.c
|
||||
@@ -122,6 +126,7 @@ data_to_c_simple(engines/eevee/shaders/default_world_frag.glsl SRC)
|
||||
data_to_c_simple(engines/eevee/shaders/background_vert.glsl SRC)
|
||||
data_to_c_simple(engines/eevee/shaders/ambient_occlusion_lib.glsl SRC)
|
||||
data_to_c_simple(engines/eevee/shaders/effect_minmaxz_frag.glsl SRC)
|
||||
data_to_c_simple(engines/eevee/shaders/hair_lib.glsl SRC)
|
||||
data_to_c_simple(engines/eevee/shaders/lamps_lib.glsl SRC)
|
||||
data_to_c_simple(engines/eevee/shaders/lightprobe_lib.glsl SRC)
|
||||
data_to_c_simple(engines/eevee/shaders/lightprobe_filter_glossy_frag.glsl SRC)
|
||||
@@ -185,6 +190,7 @@ 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)
|
||||
data_to_c_simple(modes/shaders/edit_normals_geom.glsl SRC)
|
||||
data_to_c_simple(modes/shaders/edit_strands_vert.glsl SRC)
|
||||
data_to_c_simple(modes/shaders/object_empty_image_frag.glsl SRC)
|
||||
data_to_c_simple(modes/shaders/object_empty_image_vert.glsl SRC)
|
||||
data_to_c_simple(modes/shaders/object_outline_resolve_frag.glsl SRC)
|
||||
|
@@ -25,19 +25,25 @@
|
||||
|
||||
#include "DRW_render.h"
|
||||
|
||||
#include "DNA_world_types.h"
|
||||
#include "DNA_hair_types.h"
|
||||
#include "DNA_modifier_types.h"
|
||||
#include "DNA_view3d_types.h"
|
||||
#include "DNA_world_types.h"
|
||||
|
||||
#include "BLI_dynstr.h"
|
||||
#include "BLI_ghash.h"
|
||||
#include "BLI_alloca.h"
|
||||
|
||||
#include "BKE_DerivedMesh.h"
|
||||
#include "BKE_editstrands.h"
|
||||
#include "BKE_particle.h"
|
||||
#include "BKE_paint.h"
|
||||
#include "BKE_pbvh.h"
|
||||
|
||||
#include "DEG_depsgraph.h"
|
||||
|
||||
#include "GPU_material.h"
|
||||
#include "GPU_texture.h"
|
||||
|
||||
#include "eevee_engine.h"
|
||||
#include "eevee_lut.h"
|
||||
@@ -70,6 +76,8 @@ static struct {
|
||||
|
||||
struct GPUShader *default_prepass_sh;
|
||||
struct GPUShader *default_prepass_clip_sh;
|
||||
struct GPUShader *default_prepass_hair_fiber_sh;
|
||||
struct GPUShader *default_prepass_hair_fiber_clip_sh;
|
||||
struct GPUShader *default_lit[VAR_MAT_MAX];
|
||||
|
||||
struct GPUShader *default_background;
|
||||
@@ -106,6 +114,7 @@ extern char datatoc_lightprobe_geom_glsl[];
|
||||
extern char datatoc_lightprobe_vert_glsl[];
|
||||
extern char datatoc_background_vert_glsl[];
|
||||
extern char datatoc_volumetric_frag_glsl[];
|
||||
extern char datatoc_hair_lib_glsl[];
|
||||
|
||||
extern Material defmaterial;
|
||||
extern GlobalsUboStorage ts;
|
||||
@@ -282,6 +291,9 @@ static char *eevee_get_defines(int options)
|
||||
if ((options & VAR_MAT_HAIR) != 0) {
|
||||
BLI_dynstr_appendf(ds, "#define HAIR_SHADER\n");
|
||||
}
|
||||
if ((options & VAR_MAT_HAIR_FIBERS) != 0) {
|
||||
BLI_dynstr_append(ds, DRW_hair_shader_defines());
|
||||
}
|
||||
if ((options & VAR_MAT_PROBE) != 0) {
|
||||
BLI_dynstr_appendf(ds, "#define PROBE_CAPTURE\n");
|
||||
}
|
||||
@@ -397,60 +409,87 @@ static void add_standard_uniforms(
|
||||
|
||||
static void create_default_shader(int options)
|
||||
{
|
||||
DynStr *ds_frag = BLI_dynstr_new();
|
||||
BLI_dynstr_append(ds_frag, e_data.frag_shader_lib);
|
||||
BLI_dynstr_append(ds_frag, datatoc_default_frag_glsl);
|
||||
char *frag_str = BLI_dynstr_get_cstring(ds_frag);
|
||||
BLI_dynstr_free(ds_frag);
|
||||
char *vert_str = NULL;
|
||||
{
|
||||
DynStr *ds_vert = BLI_dynstr_new();
|
||||
BLI_dynstr_append(ds_vert, datatoc_hair_lib_glsl);
|
||||
BLI_dynstr_append(ds_vert, datatoc_lit_surface_vert_glsl);
|
||||
vert_str = BLI_dynstr_get_cstring(ds_vert);
|
||||
BLI_dynstr_free(ds_vert);
|
||||
}
|
||||
|
||||
char *frag_str = NULL;
|
||||
{
|
||||
DynStr *ds_frag = BLI_dynstr_new();
|
||||
BLI_dynstr_append(ds_frag, e_data.frag_shader_lib);
|
||||
BLI_dynstr_append(ds_frag, datatoc_default_frag_glsl);
|
||||
frag_str = BLI_dynstr_get_cstring(ds_frag);
|
||||
BLI_dynstr_free(ds_frag);
|
||||
}
|
||||
|
||||
char *defines = eevee_get_defines(options);
|
||||
|
||||
e_data.default_lit[options] = DRW_shader_create(datatoc_lit_surface_vert_glsl, NULL, frag_str, defines);
|
||||
e_data.default_lit[options] = DRW_shader_create(vert_str, NULL, frag_str, defines);
|
||||
|
||||
MEM_freeN(defines);
|
||||
MEM_freeN(vert_str);
|
||||
MEM_freeN(frag_str);
|
||||
}
|
||||
|
||||
void EEVEE_materials_init(EEVEE_StorageList *stl)
|
||||
{
|
||||
if (!e_data.frag_shader_lib) {
|
||||
char *frag_str = NULL;
|
||||
|
||||
/* Shaders */
|
||||
DynStr *ds_frag = BLI_dynstr_new();
|
||||
BLI_dynstr_append(ds_frag, datatoc_bsdf_common_lib_glsl);
|
||||
{
|
||||
DynStr *ds_frag = BLI_dynstr_new();
|
||||
BLI_dynstr_append(ds_frag, datatoc_bsdf_common_lib_glsl);
|
||||
BLI_dynstr_append(ds_frag, datatoc_bsdf_sampling_lib_glsl);
|
||||
BLI_dynstr_append(ds_frag, datatoc_ambient_occlusion_lib_glsl);
|
||||
BLI_dynstr_append(ds_frag, datatoc_ambient_occlusion_lib_glsl);
|
||||
BLI_dynstr_append(ds_frag, datatoc_raytrace_lib_glsl);
|
||||
BLI_dynstr_append(ds_frag, datatoc_ssr_lib_glsl);
|
||||
BLI_dynstr_append(ds_frag, datatoc_octahedron_lib_glsl);
|
||||
BLI_dynstr_append(ds_frag, datatoc_irradiance_lib_glsl);
|
||||
BLI_dynstr_append(ds_frag, datatoc_lightprobe_lib_glsl);
|
||||
BLI_dynstr_append(ds_frag, datatoc_ltc_lib_glsl);
|
||||
BLI_dynstr_append(ds_frag, datatoc_bsdf_direct_lib_glsl);
|
||||
BLI_dynstr_append(ds_frag, datatoc_lamps_lib_glsl);
|
||||
BLI_dynstr_append(ds_frag, datatoc_lit_surface_frag_glsl);
|
||||
e_data.frag_shader_lib = BLI_dynstr_get_cstring(ds_frag);
|
||||
BLI_dynstr_free(ds_frag);
|
||||
BLI_dynstr_append(ds_frag, datatoc_octahedron_lib_glsl);
|
||||
BLI_dynstr_append(ds_frag, datatoc_irradiance_lib_glsl);
|
||||
BLI_dynstr_append(ds_frag, datatoc_lightprobe_lib_glsl);
|
||||
BLI_dynstr_append(ds_frag, datatoc_ltc_lib_glsl);
|
||||
BLI_dynstr_append(ds_frag, datatoc_bsdf_direct_lib_glsl);
|
||||
BLI_dynstr_append(ds_frag, datatoc_lamps_lib_glsl);
|
||||
BLI_dynstr_append(ds_frag, datatoc_lit_surface_frag_glsl);
|
||||
e_data.frag_shader_lib = BLI_dynstr_get_cstring(ds_frag);
|
||||
BLI_dynstr_free(ds_frag);
|
||||
}
|
||||
|
||||
{
|
||||
DynStr *ds_frag = BLI_dynstr_new();
|
||||
BLI_dynstr_append(ds_frag, datatoc_bsdf_common_lib_glsl);
|
||||
BLI_dynstr_append(ds_frag, datatoc_ambient_occlusion_lib_glsl);
|
||||
BLI_dynstr_append(ds_frag, datatoc_octahedron_lib_glsl);
|
||||
BLI_dynstr_append(ds_frag, datatoc_irradiance_lib_glsl);
|
||||
BLI_dynstr_append(ds_frag, datatoc_lightprobe_lib_glsl);
|
||||
BLI_dynstr_append(ds_frag, datatoc_ltc_lib_glsl);
|
||||
BLI_dynstr_append(ds_frag, datatoc_bsdf_direct_lib_glsl);
|
||||
BLI_dynstr_append(ds_frag, datatoc_lamps_lib_glsl);
|
||||
BLI_dynstr_append(ds_frag, datatoc_volumetric_frag_glsl);
|
||||
e_data.volume_shader_lib = BLI_dynstr_get_cstring(ds_frag);
|
||||
BLI_dynstr_free(ds_frag);
|
||||
}
|
||||
|
||||
ds_frag = BLI_dynstr_new();
|
||||
BLI_dynstr_append(ds_frag, datatoc_bsdf_common_lib_glsl);
|
||||
BLI_dynstr_append(ds_frag, datatoc_ambient_occlusion_lib_glsl);
|
||||
BLI_dynstr_append(ds_frag, datatoc_octahedron_lib_glsl);
|
||||
BLI_dynstr_append(ds_frag, datatoc_irradiance_lib_glsl);
|
||||
BLI_dynstr_append(ds_frag, datatoc_lightprobe_lib_glsl);
|
||||
BLI_dynstr_append(ds_frag, datatoc_ltc_lib_glsl);
|
||||
BLI_dynstr_append(ds_frag, datatoc_bsdf_direct_lib_glsl);
|
||||
BLI_dynstr_append(ds_frag, datatoc_lamps_lib_glsl);
|
||||
BLI_dynstr_append(ds_frag, datatoc_volumetric_frag_glsl);
|
||||
e_data.volume_shader_lib = BLI_dynstr_get_cstring(ds_frag);
|
||||
BLI_dynstr_free(ds_frag);
|
||||
char *hair_fiber_vert_str = NULL;
|
||||
{
|
||||
DynStr *ds_vert = BLI_dynstr_new();
|
||||
BLI_dynstr_append(ds_vert, datatoc_hair_lib_glsl);
|
||||
BLI_dynstr_append(ds_vert, datatoc_prepass_vert_glsl);
|
||||
hair_fiber_vert_str = BLI_dynstr_get_cstring(ds_vert);
|
||||
BLI_dynstr_free(ds_vert);
|
||||
}
|
||||
|
||||
ds_frag = BLI_dynstr_new();
|
||||
BLI_dynstr_append(ds_frag, e_data.frag_shader_lib);
|
||||
BLI_dynstr_append(ds_frag, datatoc_default_frag_glsl);
|
||||
frag_str = BLI_dynstr_get_cstring(ds_frag);
|
||||
BLI_dynstr_free(ds_frag);
|
||||
char *frag_str = NULL;
|
||||
{
|
||||
DynStr *ds_frag = BLI_dynstr_new();
|
||||
BLI_dynstr_append(ds_frag, e_data.frag_shader_lib);
|
||||
BLI_dynstr_append(ds_frag, datatoc_default_frag_glsl);
|
||||
frag_str = BLI_dynstr_get_cstring(ds_frag);
|
||||
BLI_dynstr_free(ds_frag);
|
||||
}
|
||||
|
||||
e_data.default_background = DRW_shader_create(
|
||||
datatoc_background_vert_glsl, NULL, datatoc_default_world_frag_glsl,
|
||||
@@ -464,7 +503,19 @@ void EEVEE_materials_init(EEVEE_StorageList *stl)
|
||||
datatoc_prepass_vert_glsl, NULL, datatoc_prepass_frag_glsl,
|
||||
"#define CLIP_PLANES\n");
|
||||
|
||||
e_data.default_prepass_hair_fiber_sh = DRW_shader_create(
|
||||
hair_fiber_vert_str, NULL, datatoc_prepass_frag_glsl, DRW_hair_shader_defines());
|
||||
|
||||
{
|
||||
char defines[256];
|
||||
BLI_snprintf(defines, sizeof(defines), "#define CLIP_PLANES\n%s",
|
||||
DRW_hair_shader_defines());
|
||||
e_data.default_prepass_hair_fiber_clip_sh = DRW_shader_create(
|
||||
hair_fiber_vert_str, NULL, datatoc_prepass_frag_glsl, defines);
|
||||
}
|
||||
|
||||
MEM_freeN(frag_str);
|
||||
MEM_freeN(hair_fiber_vert_str);
|
||||
|
||||
/* Textures */
|
||||
const int layers = 3 + 16;
|
||||
@@ -680,23 +731,35 @@ struct GPUMaterial *EEVEE_material_mesh_depth_get(
|
||||
}
|
||||
|
||||
struct GPUMaterial *EEVEE_material_hair_get(
|
||||
struct Scene *scene, Material *ma)
|
||||
struct Scene *scene, Material *ma, bool use_fibers)
|
||||
{
|
||||
const void *engine = &DRW_engine_viewport_eevee_type;
|
||||
int options = VAR_MAT_MESH | VAR_MAT_HAIR;
|
||||
|
||||
int options = VAR_MAT_HAIR | VAR_MAT_MESH;
|
||||
if (use_fibers) {
|
||||
options |= VAR_MAT_HAIR_FIBERS;
|
||||
}
|
||||
GPUMaterial *mat = GPU_material_from_nodetree_find(&ma->gpumaterial, engine, options);
|
||||
if (mat) {
|
||||
return mat;
|
||||
}
|
||||
|
||||
char *vert_str = NULL;
|
||||
{
|
||||
DynStr *ds_vert = BLI_dynstr_new();
|
||||
BLI_dynstr_append(ds_vert, datatoc_hair_lib_glsl);
|
||||
BLI_dynstr_append(ds_vert, datatoc_lit_surface_vert_glsl);
|
||||
vert_str = BLI_dynstr_get_cstring(ds_vert);
|
||||
BLI_dynstr_free(ds_vert);
|
||||
}
|
||||
|
||||
char *defines = eevee_get_defines(options);
|
||||
|
||||
mat = GPU_material_from_nodetree(
|
||||
scene, ma->nodetree, &ma->gpumaterial, engine, options,
|
||||
datatoc_lit_surface_vert_glsl, NULL, e_data.frag_shader_lib,
|
||||
vert_str, NULL, e_data.frag_shader_lib,
|
||||
defines);
|
||||
|
||||
MEM_freeN(vert_str);
|
||||
MEM_freeN(defines);
|
||||
|
||||
return mat;
|
||||
@@ -707,13 +770,14 @@ struct GPUMaterial *EEVEE_material_hair_get(
|
||||
**/
|
||||
static struct DRWShadingGroup *EEVEE_default_shading_group_create(
|
||||
EEVEE_SceneLayerData *sldata, EEVEE_Data *vedata, DRWPass *pass,
|
||||
bool is_hair, bool is_flat_normal, bool use_blend, bool use_ssr)
|
||||
bool is_hair, bool is_hair_fibers, bool is_flat_normal, bool use_blend, bool use_ssr)
|
||||
{
|
||||
static int ssr_id;
|
||||
ssr_id = (use_ssr) ? 0 : -1;
|
||||
int options = VAR_MAT_MESH;
|
||||
|
||||
if (is_hair) options |= VAR_MAT_HAIR;
|
||||
if (is_hair_fibers) options |= VAR_MAT_HAIR_FIBERS;
|
||||
if (is_flat_normal) options |= VAR_MAT_FLAT;
|
||||
if (use_blend) options |= VAR_MAT_BLEND;
|
||||
|
||||
@@ -732,13 +796,14 @@ static struct DRWShadingGroup *EEVEE_default_shading_group_create(
|
||||
**/
|
||||
static struct DRWShadingGroup *EEVEE_default_shading_group_get(
|
||||
EEVEE_SceneLayerData *sldata, EEVEE_Data *vedata,
|
||||
bool is_hair, bool is_flat_normal, bool use_ssr)
|
||||
bool is_hair, bool is_hair_fibers, bool is_flat_normal, bool use_ssr)
|
||||
{
|
||||
static int ssr_id;
|
||||
ssr_id = (use_ssr) ? 0 : -1;
|
||||
int options = VAR_MAT_MESH;
|
||||
|
||||
if (is_hair) options |= VAR_MAT_HAIR;
|
||||
if (is_hair_fibers) options |= VAR_MAT_HAIR_FIBERS;
|
||||
if (is_flat_normal) options |= VAR_MAT_FLAT;
|
||||
|
||||
if (e_data.default_lit[options] == NULL) {
|
||||
@@ -746,7 +811,8 @@ static struct DRWShadingGroup *EEVEE_default_shading_group_get(
|
||||
}
|
||||
|
||||
if (vedata->psl->default_pass[options] == NULL) {
|
||||
DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_EQUAL | DRW_STATE_CLIP_PLANES | DRW_STATE_WIRE;
|
||||
//DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_EQUAL | DRW_STATE_CLIP_PLANES | DRW_STATE_WIRE;
|
||||
DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_EQUAL;
|
||||
vedata->psl->default_pass[options] = DRW_pass_create("Default Lit Pass", state);
|
||||
|
||||
DRWShadingGroup *shgrp = DRW_shgroup_create(e_data.default_lit[options], vedata->psl->default_pass[options]);
|
||||
@@ -822,18 +888,22 @@ void EEVEE_materials_cache_init(EEVEE_Data *vedata)
|
||||
DRWState state = DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS | DRW_STATE_WIRE;
|
||||
psl->depth_pass = DRW_pass_create("Depth Pass", state);
|
||||
stl->g_data->depth_shgrp = DRW_shgroup_create(e_data.default_prepass_sh, psl->depth_pass);
|
||||
stl->g_data->hair_fibers_depth_shgrp = DRW_shgroup_create(e_data.default_prepass_hair_fiber_sh, psl->depth_pass);
|
||||
|
||||
state = DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS | DRW_STATE_CULL_BACK;
|
||||
psl->depth_pass_cull = DRW_pass_create("Depth Pass Cull", state);
|
||||
stl->g_data->depth_shgrp_cull = DRW_shgroup_create(e_data.default_prepass_sh, psl->depth_pass_cull);
|
||||
stl->g_data->hair_fibers_depth_shgrp_cull = DRW_shgroup_create(e_data.default_prepass_hair_fiber_sh, psl->depth_pass_cull);
|
||||
|
||||
state = DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS | DRW_STATE_CLIP_PLANES | DRW_STATE_WIRE;
|
||||
psl->depth_pass_clip = DRW_pass_create("Depth Pass Clip", state);
|
||||
stl->g_data->depth_shgrp_clip = DRW_shgroup_create(e_data.default_prepass_clip_sh, psl->depth_pass_clip);
|
||||
stl->g_data->hair_fibers_depth_shgrp_clip = DRW_shgroup_create(e_data.default_prepass_hair_fiber_clip_sh, psl->depth_pass_clip);
|
||||
|
||||
state = DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS | DRW_STATE_CLIP_PLANES | DRW_STATE_CULL_BACK;
|
||||
psl->depth_pass_clip_cull = DRW_pass_create("Depth Pass Cull Clip", state);
|
||||
stl->g_data->depth_shgrp_clip_cull = DRW_shgroup_create(e_data.default_prepass_clip_sh, psl->depth_pass_clip_cull);
|
||||
stl->g_data->hair_fibers_depth_shgrp_clip_cull = DRW_shgroup_create(e_data.default_prepass_hair_fiber_clip_sh, psl->depth_pass_clip_cull);
|
||||
}
|
||||
|
||||
{
|
||||
@@ -970,7 +1040,7 @@ static void material_opaque(
|
||||
|
||||
/* Fallback to default shader */
|
||||
if (*shgrp == NULL) {
|
||||
*shgrp = EEVEE_default_shading_group_get(sldata, vedata, false, use_flat_nor, stl->effects->use_ssr);
|
||||
*shgrp = EEVEE_default_shading_group_get(sldata, vedata, false, false, use_flat_nor, stl->effects->use_ssr);
|
||||
DRW_shgroup_uniform_vec3(*shgrp, "basecol", color_p, 1);
|
||||
DRW_shgroup_uniform_float(*shgrp, "metallic", metal_p, 1);
|
||||
DRW_shgroup_uniform_float(*shgrp, "specular", spec_p, 1);
|
||||
@@ -1035,7 +1105,7 @@ static void material_transparent(
|
||||
if (*shgrp == NULL) {
|
||||
*shgrp = EEVEE_default_shading_group_create(
|
||||
sldata, vedata, psl->transparent_pass,
|
||||
false, use_flat_nor, true, false);
|
||||
false, false, use_flat_nor, true, false);
|
||||
DRW_shgroup_uniform_vec3(*shgrp, "basecol", color_p, 1);
|
||||
DRW_shgroup_uniform_float(*shgrp, "metallic", metal_p, 1);
|
||||
DRW_shgroup_uniform_float(*shgrp, "specular", spec_p, 1);
|
||||
@@ -1082,6 +1152,179 @@ static void material_transparent(
|
||||
}
|
||||
}
|
||||
|
||||
static void material_particle_hair(EEVEE_SceneLayerData *sldata, EEVEE_Data *vedata,
|
||||
Object *ob, ParticleSystem *psys, ModifierData *md)
|
||||
{
|
||||
EEVEE_PassList *psl = ((EEVEE_Data *)vedata)->psl;
|
||||
EEVEE_StorageList *stl = ((EEVEE_Data *)vedata)->stl;
|
||||
const DRWContextState *draw_ctx = DRW_context_state_get();
|
||||
Scene *scene = draw_ctx->scene;
|
||||
GHash *material_hash = stl->g_data->hair_material_hash;
|
||||
|
||||
if (!psys_check_enabled(ob, psys, false)) {
|
||||
return;
|
||||
}
|
||||
|
||||
ParticleSettings *part = psys->part;
|
||||
float mat[4][4];
|
||||
unit_m4(mat);
|
||||
|
||||
bool use_hair = false;
|
||||
struct Gwn_Batch *hair_geom = NULL;
|
||||
if ((ob->mode & OB_MODE_HAIR_EDIT) == 0) {
|
||||
int draw_as = (part->draw_as == PART_DRAW_REND) ? part->ren_as : part->draw_as;
|
||||
if (draw_as == PART_DRAW_PATH && (psys->pathcache || psys->childcache)) {
|
||||
use_hair = true;
|
||||
hair_geom = DRW_cache_particles_get_hair(psys, md);
|
||||
}
|
||||
}
|
||||
|
||||
if (use_hair) {
|
||||
Material *ma = give_current_material(ob, part->omat);
|
||||
if (ma == NULL) {
|
||||
ma = &defmaterial;
|
||||
}
|
||||
|
||||
DRW_shgroup_call_add(stl->g_data->depth_shgrp, hair_geom, mat);
|
||||
DRW_shgroup_call_add(stl->g_data->depth_shgrp_clip, hair_geom, mat);
|
||||
|
||||
DRWShadingGroup *shgrp = BLI_ghash_lookup(material_hash, (const void *)ma);
|
||||
if (!shgrp) {
|
||||
float *color_p = &ma->r;
|
||||
float *metal_p = &ma->ray_mirror;
|
||||
float *spec_p = &ma->spec;
|
||||
float *rough_p = &ma->gloss_mir;
|
||||
|
||||
if (ma->use_nodes && ma->nodetree) {
|
||||
struct GPUMaterial *gpumat = EEVEE_material_hair_get(scene, ma, false);
|
||||
|
||||
shgrp = DRW_shgroup_material_create(gpumat, psl->material_pass);
|
||||
if (shgrp) {
|
||||
add_standard_uniforms(shgrp, sldata, vedata, NULL, NULL, false);
|
||||
|
||||
BLI_ghash_insert(material_hash, ma, shgrp);
|
||||
}
|
||||
else {
|
||||
/* Shader failed : pink color */
|
||||
static float col[3] = {1.0f, 0.0f, 1.0f};
|
||||
static float half = 0.5f;
|
||||
|
||||
color_p = col;
|
||||
metal_p = spec_p = rough_p = ½
|
||||
}
|
||||
}
|
||||
|
||||
/* Fallback to default shader */
|
||||
if (shgrp == NULL) {
|
||||
shgrp = EEVEE_default_shading_group_get(sldata, vedata, true, false, false, stl->effects->use_ssr);
|
||||
DRW_shgroup_uniform_vec3(shgrp, "basecol", color_p, 1);
|
||||
DRW_shgroup_uniform_float(shgrp, "metallic", metal_p, 1);
|
||||
DRW_shgroup_uniform_float(shgrp, "specular", spec_p, 1);
|
||||
DRW_shgroup_uniform_float(shgrp, "roughness", rough_p, 1);
|
||||
|
||||
BLI_ghash_insert(material_hash, ma, shgrp);
|
||||
}
|
||||
}
|
||||
|
||||
if (shgrp) {
|
||||
DRW_shgroup_call_add(shgrp, hair_geom, mat);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void material_hair(EEVEE_SceneLayerData *sldata, EEVEE_Data *vedata, Object *ob, HairGroup *group)
|
||||
{
|
||||
EEVEE_PassList *psl = ((EEVEE_Data *)vedata)->psl;
|
||||
EEVEE_StorageList *stl = ((EEVEE_Data *)vedata)->stl;
|
||||
const DRWContextState *draw_ctx = DRW_context_state_get();
|
||||
Scene *scene = draw_ctx->scene;
|
||||
GHash *material_hash = stl->g_data->hair_material_hash;
|
||||
const HairEditSettings *tsettings = &scene->toolsettings->hair_edit;
|
||||
|
||||
float mat[4][4];
|
||||
copy_m4_m4(mat, ob->obmat);
|
||||
|
||||
const DRWHairFiberTextureBuffer *fiber_buffer = NULL;
|
||||
struct Gwn_Batch *hair_geom;
|
||||
{
|
||||
DerivedMesh *scalp = NULL;
|
||||
if (ob->derivedFinal) {
|
||||
scalp = ob->derivedFinal;
|
||||
}
|
||||
else {
|
||||
EvaluationContext eval_ctx = {0};
|
||||
DEG_evaluation_context_init(&eval_ctx, DAG_EVAL_VIEWPORT);
|
||||
scalp = mesh_get_derived_final(&eval_ctx, scene, ob, CD_MASK_BAREMESH);
|
||||
}
|
||||
hair_geom = DRW_cache_hair_get_fibers(group, tsettings->hair_draw_subdiv, scalp, &fiber_buffer);
|
||||
}
|
||||
|
||||
if (!group->draw_texture_cache) {
|
||||
group->draw_texture_cache = DRW_texture_create_2D(fiber_buffer->width, fiber_buffer->height,
|
||||
DRW_TEX_RG_32, 0, fiber_buffer->data);
|
||||
}
|
||||
GPUTexture **fiber_texture = (GPUTexture **)(&group->draw_texture_cache);
|
||||
|
||||
// TODO
|
||||
Material *ma = NULL;/*give_current_material(ob, omat);*/
|
||||
if (ma == NULL) {
|
||||
ma = &defmaterial;
|
||||
}
|
||||
|
||||
DRW_shgroup_call_add(stl->g_data->hair_fibers_depth_shgrp, hair_geom, mat);
|
||||
DRW_hair_shader_uniforms(stl->g_data->hair_fibers_depth_shgrp, scene,
|
||||
fiber_texture, fiber_buffer);
|
||||
|
||||
DRW_shgroup_call_add(stl->g_data->hair_fibers_depth_shgrp_clip, hair_geom, mat);
|
||||
DRW_hair_shader_uniforms(stl->g_data->hair_fibers_depth_shgrp_clip, scene,
|
||||
fiber_texture, fiber_buffer);
|
||||
|
||||
DRWShadingGroup *shgrp = BLI_ghash_lookup(material_hash, (const void *)ma);
|
||||
if (!shgrp) {
|
||||
float *color_p = &ma->r;
|
||||
float *metal_p = &ma->ray_mirror;
|
||||
float *spec_p = &ma->spec;
|
||||
float *rough_p = &ma->gloss_mir;
|
||||
|
||||
if (ma->use_nodes && ma->nodetree) {
|
||||
struct GPUMaterial *gpumat = EEVEE_material_hair_get(scene, ma, true);
|
||||
|
||||
shgrp = DRW_shgroup_material_create(gpumat, psl->material_pass);
|
||||
if (shgrp) {
|
||||
add_standard_uniforms(shgrp, sldata, vedata, NULL, NULL, false);
|
||||
|
||||
BLI_ghash_insert(material_hash, ma, shgrp);
|
||||
}
|
||||
else {
|
||||
/* Shader failed : pink color */
|
||||
static float col[3] = {1.0f, 0.0f, 1.0f};
|
||||
static float half = 0.5f;
|
||||
|
||||
color_p = col;
|
||||
metal_p = spec_p = rough_p = ½
|
||||
}
|
||||
}
|
||||
|
||||
/* Fallback to default shader */
|
||||
if (shgrp == NULL) {
|
||||
shgrp = EEVEE_default_shading_group_get(sldata, vedata, true, true, false, stl->effects->use_ssr);
|
||||
DRW_shgroup_uniform_vec3(shgrp, "basecol", color_p, 1);
|
||||
DRW_shgroup_uniform_float(shgrp, "metallic", metal_p, 1);
|
||||
DRW_shgroup_uniform_float(shgrp, "specular", spec_p, 1);
|
||||
DRW_shgroup_uniform_float(shgrp, "roughness", rough_p, 1);
|
||||
|
||||
BLI_ghash_insert(material_hash, ma, shgrp);
|
||||
}
|
||||
}
|
||||
|
||||
if (shgrp) {
|
||||
DRW_shgroup_call_add(shgrp, hair_geom, mat);
|
||||
|
||||
DRW_hair_shader_uniforms(shgrp, scene,
|
||||
fiber_texture, fiber_buffer);
|
||||
}
|
||||
}
|
||||
|
||||
void EEVEE_materials_cache_populate(EEVEE_Data *vedata, EEVEE_SceneLayerData *sldata, Object *ob)
|
||||
{
|
||||
EEVEE_PassList *psl = ((EEVEE_Data *)vedata)->psl;
|
||||
@@ -1211,77 +1454,15 @@ void EEVEE_materials_cache_populate(EEVEE_Data *vedata, EEVEE_SceneLayerData *sl
|
||||
|
||||
if (ob->type == OB_MESH) {
|
||||
if (ob != draw_ctx->scene->obedit) {
|
||||
material_hash = stl->g_data->hair_material_hash;
|
||||
|
||||
for (ModifierData *md = ob->modifiers.first; md; md = md->next) {
|
||||
if (md->type == eModifierType_ParticleSystem) {
|
||||
ParticleSystem *psys = ((ParticleSystemModifierData *)md)->psys;
|
||||
|
||||
if (psys_check_enabled(ob, psys, false)) {
|
||||
ParticleSettings *part = psys->part;
|
||||
int draw_as = (part->draw_as == PART_DRAW_REND) ? part->ren_as : part->draw_as;
|
||||
|
||||
if (draw_as == PART_DRAW_PATH && (psys->pathcache || psys->childcache)) {
|
||||
struct Gwn_Batch *hair_geom = DRW_cache_particles_get_hair(psys, md);
|
||||
DRWShadingGroup *shgrp = NULL;
|
||||
Material *ma = give_current_material(ob, part->omat);
|
||||
static float mat[4][4];
|
||||
|
||||
unit_m4(mat);
|
||||
|
||||
if (ma == NULL) {
|
||||
ma = &defmaterial;
|
||||
}
|
||||
|
||||
float *color_p = &ma->r;
|
||||
float *metal_p = &ma->ray_mirror;
|
||||
float *spec_p = &ma->spec;
|
||||
float *rough_p = &ma->gloss_mir;
|
||||
|
||||
DRW_shgroup_call_add(stl->g_data->depth_shgrp, hair_geom, mat);
|
||||
DRW_shgroup_call_add(stl->g_data->depth_shgrp_clip, hair_geom, mat);
|
||||
|
||||
shgrp = BLI_ghash_lookup(material_hash, (const void *)ma);
|
||||
|
||||
if (shgrp) {
|
||||
DRW_shgroup_call_add(shgrp, hair_geom, mat);
|
||||
}
|
||||
else {
|
||||
if (ma->use_nodes && ma->nodetree) {
|
||||
struct GPUMaterial *gpumat = EEVEE_material_hair_get(scene, ma);
|
||||
|
||||
shgrp = DRW_shgroup_material_create(gpumat, psl->material_pass);
|
||||
if (shgrp) {
|
||||
add_standard_uniforms(shgrp, sldata, vedata, NULL, NULL, false);
|
||||
|
||||
BLI_ghash_insert(material_hash, ma, shgrp);
|
||||
|
||||
DRW_shgroup_call_add(shgrp, hair_geom, mat);
|
||||
}
|
||||
else {
|
||||
/* Shader failed : pink color */
|
||||
static float col[3] = {1.0f, 0.0f, 1.0f};
|
||||
static float half = 0.5f;
|
||||
|
||||
color_p = col;
|
||||
metal_p = spec_p = rough_p = ½
|
||||
}
|
||||
}
|
||||
|
||||
/* Fallback to default shader */
|
||||
if (shgrp == NULL) {
|
||||
shgrp = EEVEE_default_shading_group_get(sldata, vedata, true, false, stl->effects->use_ssr);
|
||||
DRW_shgroup_uniform_vec3(shgrp, "basecol", color_p, 1);
|
||||
DRW_shgroup_uniform_float(shgrp, "metallic", metal_p, 1);
|
||||
DRW_shgroup_uniform_float(shgrp, "specular", spec_p, 1);
|
||||
DRW_shgroup_uniform_float(shgrp, "roughness", rough_p, 1);
|
||||
|
||||
BLI_ghash_insert(material_hash, ma, shgrp);
|
||||
|
||||
DRW_shgroup_call_add(shgrp, hair_geom, mat);
|
||||
}
|
||||
}
|
||||
}
|
||||
material_particle_hair(sldata, vedata, ob, psys, md);
|
||||
}
|
||||
else if (md->type == eModifierType_Hair) {
|
||||
HairModifierData *hmd = (HairModifierData *)md;
|
||||
for (HairGroup *group = hmd->hair->groups.first; group; group = group->next) {
|
||||
material_hair(sldata, vedata, ob, group);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1306,6 +1487,8 @@ void EEVEE_materials_free(void)
|
||||
MEM_SAFE_FREE(e_data.volume_shader_lib);
|
||||
DRW_SHADER_FREE_SAFE(e_data.default_prepass_sh);
|
||||
DRW_SHADER_FREE_SAFE(e_data.default_prepass_clip_sh);
|
||||
DRW_SHADER_FREE_SAFE(e_data.default_prepass_hair_fiber_sh);
|
||||
DRW_SHADER_FREE_SAFE(e_data.default_prepass_hair_fiber_clip_sh);
|
||||
DRW_SHADER_FREE_SAFE(e_data.default_background);
|
||||
DRW_TEXTURE_FREE_SAFE(e_data.util_tex);
|
||||
}
|
||||
|
@@ -60,21 +60,22 @@ enum {
|
||||
|
||||
/* Material shader variations */
|
||||
enum {
|
||||
VAR_MAT_MESH = (1 << 0),
|
||||
VAR_MAT_PROBE = (1 << 1),
|
||||
VAR_MAT_HAIR = (1 << 2),
|
||||
VAR_MAT_FLAT = (1 << 3),
|
||||
VAR_MAT_BLEND = (1 << 4),
|
||||
VAR_MAT_MESH = (1 << 0),
|
||||
VAR_MAT_PROBE = (1 << 1),
|
||||
VAR_MAT_HAIR = (1 << 2),
|
||||
VAR_MAT_FLAT = (1 << 3),
|
||||
VAR_MAT_BLEND = (1 << 4),
|
||||
VAR_MAT_HAIR_FIBERS = (1 << 5),
|
||||
/* Max number of variation */
|
||||
/* IMPORTANT : Leave it last and set
|
||||
* it's value accordingly. */
|
||||
VAR_MAT_MAX = (1 << 5),
|
||||
VAR_MAT_MAX = (1 << 6),
|
||||
/* These are options that are not counted in VAR_MAT_MAX
|
||||
* because they are not cumulative with the others above. */
|
||||
VAR_MAT_CLIP = (1 << 8),
|
||||
VAR_MAT_HASH = (1 << 9),
|
||||
VAR_MAT_MULT = (1 << 10),
|
||||
VAR_MAT_SHADOW = (1 << 11),
|
||||
VAR_MAT_CLIP = (1 << 9),
|
||||
VAR_MAT_HASH = (1 << 10),
|
||||
VAR_MAT_MULT = (1 << 11),
|
||||
VAR_MAT_SHADOW = (1 << 12),
|
||||
VAR_MAT_REFRACT = (1 << 12),
|
||||
};
|
||||
|
||||
@@ -476,6 +477,10 @@ typedef struct EEVEE_PrivateData {
|
||||
struct DRWShadingGroup *depth_shgrp_cull;
|
||||
struct DRWShadingGroup *depth_shgrp_clip;
|
||||
struct DRWShadingGroup *depth_shgrp_clip_cull;
|
||||
struct DRWShadingGroup *hair_fibers_depth_shgrp;
|
||||
struct DRWShadingGroup *hair_fibers_depth_shgrp_cull;
|
||||
struct DRWShadingGroup *hair_fibers_depth_shgrp_clip;
|
||||
struct DRWShadingGroup *hair_fibers_depth_shgrp_clip_cull;
|
||||
struct DRWShadingGroup *refract_depth_shgrp;
|
||||
struct DRWShadingGroup *refract_depth_shgrp_cull;
|
||||
struct DRWShadingGroup *refract_depth_shgrp_clip;
|
||||
@@ -519,7 +524,7 @@ struct GPUMaterial *EEVEE_material_world_volume_get(
|
||||
struct GPUMaterial *EEVEE_material_mesh_get(
|
||||
struct Scene *scene, Material *ma, bool use_blend, bool use_multiply, bool use_refract);
|
||||
struct GPUMaterial *EEVEE_material_mesh_depth_get(struct Scene *scene, Material *ma, bool use_hashed_alpha, bool is_shadow);
|
||||
struct GPUMaterial *EEVEE_material_hair_get(struct Scene *scene, Material *ma);
|
||||
struct GPUMaterial *EEVEE_material_hair_get(struct Scene *scene, Material *ma, bool use_fibers);
|
||||
void EEVEE_materials_free(void);
|
||||
void EEVEE_draw_default_passes(EEVEE_PassList *psl);
|
||||
|
||||
|
330
source/blender/draw/engines/eevee/shaders/hair_lib.glsl
Normal file
@@ -0,0 +1,330 @@
|
||||
#ifdef HAIR_SHADER_FIBERS
|
||||
|
||||
#define M_PI 3.1415926535897932384626433832795
|
||||
|
||||
mat4 translate(vec3 co)
|
||||
{
|
||||
return mat4(1.0, 0.0, 0.0, 0.0,
|
||||
0.0, 1.0, 0.0, 0.0,
|
||||
0.0, 0.0, 1.0, 0.0,
|
||||
co.x, co.y, co.z, 1.0);
|
||||
}
|
||||
|
||||
mat4 rotateX(float angle)
|
||||
{
|
||||
float ca = cos(angle);
|
||||
float sa = sin(angle);
|
||||
return mat4(1.0, 0.0, 0.0, 0.0,
|
||||
0.0, ca, sa, 0.0,
|
||||
0.0, -sa, ca, 0.0,
|
||||
0.0, 0.0, 0.0, 1.0);
|
||||
}
|
||||
|
||||
mat4 rotateY(float angle)
|
||||
{
|
||||
float ca = cos(angle);
|
||||
float sa = sin(angle);
|
||||
return mat4(ca, 0.0, sa, 0.0,
|
||||
0.0, 1.0, 0.0, 0.0,
|
||||
-sa, 0.0, ca, 0.0,
|
||||
0.0, 0.0, 0.0, 1.0);
|
||||
}
|
||||
|
||||
mat4 rotateZ(float angle)
|
||||
{
|
||||
float ca = cos(angle);
|
||||
float sa = sin(angle);
|
||||
return mat4(ca, sa, 0.0, 0.0,
|
||||
-sa, ca, 0.0, 0.0,
|
||||
0.0, 0.0, 1.0, 0.0,
|
||||
0.0, 0.0, 0.0, 1.0);
|
||||
}
|
||||
|
||||
/* Hair Displacement */
|
||||
|
||||
/* Note: The deformer functions below calculate a new location vector
|
||||
* as well as a new direction (aka "normal"), using the partial derivatives of the transformation.
|
||||
*
|
||||
* Each transformation function can depend on the location L as well as the curve parameter t:
|
||||
*
|
||||
* Lnew = f(L, t)
|
||||
* => dLnew/dt = del f/del L * dL/dt + del f/del t
|
||||
*
|
||||
* The first term is the Jacobian of the function f, dL/dt is the original direction vector.
|
||||
* Some more information can be found here:
|
||||
* https://developer.nvidia.com/gpugems/GPUGems/gpugems_ch42.html
|
||||
*/
|
||||
|
||||
/* Hairs tend to stick together and run in parallel.
|
||||
* The effect increases with distance from the root,
|
||||
* as the stresses pulling fibers apart decrease.
|
||||
*/
|
||||
struct ClumpParams
|
||||
{
|
||||
/* Relative strand thickness at the tip.
|
||||
* (0.0, 1.0]
|
||||
* 0.0 : Strand clumps into a single line
|
||||
* 1.0 : Strand does not clump at all
|
||||
* (> 1.0 is possible but not recommended)
|
||||
*/
|
||||
float thickness;
|
||||
};
|
||||
|
||||
/* Hairs often don't have a circular cross section, but are somewhat flattened.
|
||||
* This creates the local bending which results in the typical curly hair geometry.
|
||||
*/
|
||||
struct CurlParams
|
||||
{
|
||||
/* Radius of the curls.
|
||||
* >= 0.0
|
||||
*/
|
||||
float radius;
|
||||
/* Steepness of curls
|
||||
* < 0.0 : Clockwise curls
|
||||
* > 0.0 : Anti-clockwise curls
|
||||
*/
|
||||
float angle;
|
||||
};
|
||||
|
||||
struct DeformParams
|
||||
{
|
||||
/* Strand tapering with distance from the root.
|
||||
* < 1.0 : Taper is concave (recommended)
|
||||
* = 1.0 : Taper is linear
|
||||
* > 1.0 : Taper is convex (not recommended)
|
||||
*/
|
||||
float taper;
|
||||
|
||||
ClumpParams clump;
|
||||
CurlParams curl;
|
||||
};
|
||||
|
||||
void deform_taper(DeformParams params, float t, out float taper, out float dtaper)
|
||||
{
|
||||
taper = pow(t, params.taper);
|
||||
dtaper = (t > 0.0) ? taper * params.taper / t : 0.0;
|
||||
}
|
||||
|
||||
void deform_clump(DeformParams params,
|
||||
float t, float tscale, mat4 target_matrix,
|
||||
inout vec3 co, inout vec3 tang)
|
||||
{
|
||||
float taper, dtaper;
|
||||
deform_taper(params, t, taper, dtaper);
|
||||
float factor = (1.0 - params.clump.thickness) * taper;
|
||||
float dfactor = (1.0 - params.clump.thickness) * dtaper;
|
||||
|
||||
vec3 target_co = target_matrix[3].xyz;
|
||||
vec3 target_tang = target_matrix[0].xyz;
|
||||
vec3 nco = co + (target_co - co) * factor;
|
||||
vec3 ntang = normalize(tang + (target_tang - tang) * factor + (target_co - co) * dfactor);
|
||||
|
||||
co = nco;
|
||||
tang = ntang;
|
||||
}
|
||||
|
||||
void deform_curl(DeformParams params,
|
||||
float t, float tscale,
|
||||
inout mat4 target_matrix)
|
||||
{
|
||||
float pitch = 2.0*M_PI * params.curl.radius * tan(params.curl.angle);
|
||||
float turns = tscale / (params.curl.radius * tan(params.curl.angle));
|
||||
float angle = t * turns;
|
||||
mat4 local_mat = rotateX(angle) * translate(vec3(0.0, params.curl.radius, 0.0)) * rotateY(params.curl.angle);
|
||||
target_matrix = target_matrix * local_mat;
|
||||
}
|
||||
|
||||
void deform_fiber(DeformParams params,
|
||||
float t, float tscale, mat4 target_matrix,
|
||||
inout vec3 loc, inout vec3 tang)
|
||||
{
|
||||
deform_curl(params, t, tscale, target_matrix);
|
||||
deform_clump(params, t, tscale, target_matrix, loc, tang);
|
||||
}
|
||||
|
||||
/*===================================*/
|
||||
/* Hair Interpolation */
|
||||
|
||||
#define FIBER_RIBBON
|
||||
|
||||
uniform sampler2D fiber_data;
|
||||
|
||||
uniform int fiber_start;
|
||||
uniform int strand_map_start;
|
||||
uniform int strand_vertex_start;
|
||||
|
||||
uniform float ribbon_width;
|
||||
uniform vec2 viewport_size;
|
||||
|
||||
#define INDEX_INVALID -1
|
||||
|
||||
vec2 read_texdata(int offset)
|
||||
{
|
||||
ivec2 offset2 = ivec2(offset % HAIR_SHADER_TEX_WIDTH, offset / HAIR_SHADER_TEX_WIDTH);
|
||||
return texelFetch(fiber_data, offset2, 0).rg;
|
||||
}
|
||||
|
||||
mat4 mat4_from_vectors(vec3 nor, vec3 tang, vec3 co)
|
||||
{
|
||||
tang = normalize(tang);
|
||||
vec3 xnor = normalize(cross(nor, tang));
|
||||
return mat4(vec4(tang, 0.0), vec4(xnor, 0.0), vec4(cross(tang, xnor), 0.0), vec4(co, 1.0));
|
||||
}
|
||||
|
||||
void get_strand_data(int index, out int start, out int count)
|
||||
{
|
||||
int offset = strand_map_start + index;
|
||||
vec2 a = read_texdata(offset);
|
||||
|
||||
start = floatBitsToInt(a.r);
|
||||
count = floatBitsToInt(a.g);
|
||||
}
|
||||
|
||||
void get_strand_vertex(int index, out vec3 co, out vec3 nor, out vec3 tang)
|
||||
{
|
||||
int offset = strand_vertex_start + index * 5;
|
||||
vec2 a = read_texdata(offset);
|
||||
vec2 b = read_texdata(offset + 1);
|
||||
vec2 c = read_texdata(offset + 2);
|
||||
vec2 d = read_texdata(offset + 3);
|
||||
vec2 e = read_texdata(offset + 4);
|
||||
|
||||
co = vec3(a.rg, b.r);
|
||||
nor = vec3(b.g, c.rg);
|
||||
tang = vec3(d.rg, e.r);
|
||||
}
|
||||
|
||||
void get_strand_root(int index, out vec3 co)
|
||||
{
|
||||
int offset = strand_vertex_start + index * 5;
|
||||
vec2 a = read_texdata(offset);
|
||||
vec2 b = read_texdata(offset + 1);
|
||||
|
||||
co = vec3(a.rg, b.r);
|
||||
}
|
||||
|
||||
void get_fiber_data(int fiber_index, out ivec4 parent_index, out vec4 parent_weight, out vec3 pos)
|
||||
{
|
||||
int offset = fiber_start + fiber_index * 6;
|
||||
vec2 a = read_texdata(offset);
|
||||
vec2 b = read_texdata(offset + 1);
|
||||
vec2 c = read_texdata(offset + 2);
|
||||
vec2 d = read_texdata(offset + 3);
|
||||
vec2 e = read_texdata(offset + 4);
|
||||
vec2 f = read_texdata(offset + 5);
|
||||
|
||||
parent_index = ivec4(floatBitsToInt(a.rg), floatBitsToInt(b.rg));
|
||||
parent_weight = vec4(c.rg, d.rg);
|
||||
pos = vec3(e.rg, f.r);
|
||||
}
|
||||
|
||||
void interpolate_parent_curve_full(int index, float curve_param, out vec3 co, out vec3 nor, out vec3 tang, out vec3 rootco)
|
||||
{
|
||||
int start, count;
|
||||
get_strand_data(index, start, count);
|
||||
|
||||
get_strand_root(start, rootco);
|
||||
|
||||
#if 0 // Don't have to worry about out-of-bounds segment here, as long as lerpfac becomes 0.0 when curve_param==1.0
|
||||
float maxlen = float(count - 1);
|
||||
float arclength = curve_param * maxlen;
|
||||
int segment = min(int(arclength), count - 2);
|
||||
float lerpfac = arclength - min(floor(arclength), maxlen - 1.0);
|
||||
#else
|
||||
float maxlen = float(count - 1);
|
||||
float arclength = curve_param * maxlen;
|
||||
int segment = int(arclength);
|
||||
float lerpfac = arclength - floor(arclength);
|
||||
#endif
|
||||
|
||||
vec3 co0, nor0, tang0;
|
||||
vec3 co1, nor1, tang1;
|
||||
get_strand_vertex(start + segment, co0, nor0, tang0);
|
||||
get_strand_vertex(start + segment + 1, co1, nor1, tang1);
|
||||
|
||||
co = mix(co0, co1, lerpfac) - rootco;
|
||||
nor = mix(nor0, nor1, lerpfac);
|
||||
tang = mix(tang0, tang1, lerpfac);
|
||||
}
|
||||
|
||||
void interpolate_parent_curve(int index, float curve_param, out vec3 co, out vec3 tang)
|
||||
{
|
||||
vec3 nor;
|
||||
vec3 rootco;
|
||||
interpolate_parent_curve_full(index, curve_param, co, nor, tang, rootco);
|
||||
}
|
||||
|
||||
void interpolate_vertex(int fiber_index, float curve_param,
|
||||
out vec3 co, out vec3 tang,
|
||||
out mat4 target_matrix)
|
||||
{
|
||||
co = vec3(0.0);
|
||||
tang = vec3(0.0);
|
||||
target_matrix = mat4(1.0);
|
||||
|
||||
ivec4 parent_index;
|
||||
vec4 parent_weight;
|
||||
vec3 rootco;
|
||||
get_fiber_data(fiber_index, parent_index, parent_weight, rootco);
|
||||
|
||||
if (parent_index.x != INDEX_INVALID) {
|
||||
vec3 pco, pnor, ptang, prootco;
|
||||
interpolate_parent_curve_full(parent_index.x, curve_param, pco, pnor, ptang, prootco);
|
||||
co += parent_weight.x * pco;
|
||||
tang += parent_weight.x * normalize(ptang);
|
||||
|
||||
target_matrix = mat4_from_vectors(pnor, ptang, pco + prootco);
|
||||
}
|
||||
if (parent_index.y != INDEX_INVALID) {
|
||||
vec3 pco, ptang;
|
||||
interpolate_parent_curve(parent_index.y, curve_param, pco, ptang);
|
||||
co += parent_weight.y * pco;
|
||||
tang += parent_weight.y * normalize(ptang);
|
||||
}
|
||||
if (parent_index.z != INDEX_INVALID) {
|
||||
vec3 pco, ptang;
|
||||
interpolate_parent_curve(parent_index.z, curve_param, pco, ptang);
|
||||
co += parent_weight.z * pco;
|
||||
tang += parent_weight.z * normalize(ptang);
|
||||
}
|
||||
if (parent_index.w != INDEX_INVALID) {
|
||||
vec3 pco, ptang;
|
||||
interpolate_parent_curve(parent_index.w, curve_param, pco, ptang);
|
||||
co += parent_weight.w * pco;
|
||||
tang += parent_weight.w * normalize(ptang);
|
||||
}
|
||||
|
||||
co += rootco;
|
||||
tang = normalize(tang);
|
||||
}
|
||||
|
||||
void hair_fiber_get_vertex(int fiber_index, float curve_param, mat4 ModelViewMatrix, out vec3 pos, out vec3 nor, out vec2 view_offset)
|
||||
{
|
||||
vec3 target_loc;
|
||||
mat4 target_matrix;
|
||||
interpolate_vertex(fiber_index, curve_param, pos, nor, target_matrix);
|
||||
|
||||
DeformParams deform_params;
|
||||
deform_params.taper = 2.0;
|
||||
deform_params.clump.thickness = 0.15;
|
||||
deform_params.curl.radius = 0.1;
|
||||
deform_params.curl.angle = 0.2;
|
||||
// TODO define proper curve scale, independent of subdivision!
|
||||
//deform_fiber(deform_params, curve_param, 1.0, target_matrix, pos, nor);
|
||||
|
||||
#ifdef FIBER_RIBBON
|
||||
float ribbon_side = (float(gl_VertexID % 2) - 0.5) * ribbon_width;
|
||||
{
|
||||
vec4 view_nor = ModelViewMatrix * vec4(nor, 0.0);
|
||||
view_offset = vec2(view_nor.y, -view_nor.x);
|
||||
float L = length(view_offset);
|
||||
if (L > 0.0) {
|
||||
view_offset *= ribbon_side / (L * viewport_size);
|
||||
}
|
||||
}
|
||||
#else
|
||||
view_offset = vec2(0.0);
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif /*HAIR_SHADER_FIBERS*/
|
@@ -7,8 +7,13 @@ uniform mat3 WorldNormalMatrix;
|
||||
uniform mat3 NormalMatrix;
|
||||
#endif
|
||||
|
||||
#ifndef HAIR_SHADER_FIBERS
|
||||
in vec3 pos;
|
||||
in vec3 nor;
|
||||
#else
|
||||
in int fiber_index;
|
||||
in float curve_param;
|
||||
#endif
|
||||
|
||||
out vec3 worldPosition;
|
||||
out vec3 viewPosition;
|
||||
@@ -25,7 +30,17 @@ out vec3 viewNormal;
|
||||
#endif
|
||||
|
||||
void main() {
|
||||
#ifndef HAIR_SHADER_FIBERS
|
||||
gl_Position = ModelViewProjectionMatrix * vec4(pos, 1.0);
|
||||
#else
|
||||
vec3 pos;
|
||||
vec3 nor;
|
||||
vec2 view_offset;
|
||||
hair_fiber_get_vertex(fiber_index, curve_param, ModelViewMatrix, pos, nor, view_offset);
|
||||
gl_Position = ModelViewProjectionMatrix * vec4(pos, 1.0);
|
||||
gl_Position.xy += view_offset * gl_Position.w;
|
||||
#endif
|
||||
|
||||
viewPosition = (ModelViewMatrix * vec4(pos, 1.0)).xyz;
|
||||
worldPosition = (ModelMatrix * vec4(pos, 1.0)).xyz;
|
||||
viewNormal = normalize(NormalMatrix * nor);
|
||||
@@ -37,4 +52,4 @@ void main() {
|
||||
#ifdef ATTRIB
|
||||
pass_attrib(pos);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
@@ -1,15 +1,31 @@
|
||||
|
||||
uniform mat4 ModelViewProjectionMatrix;
|
||||
uniform mat4 ModelMatrix;
|
||||
uniform mat4 ModelViewMatrix;
|
||||
#ifdef CLIP_PLANES
|
||||
uniform vec4 ClipPlanes[1];
|
||||
#endif
|
||||
|
||||
#ifndef HAIR_SHADER_FIBERS
|
||||
in vec3 pos;
|
||||
#else
|
||||
in int fiber_index;
|
||||
in float curve_param;
|
||||
#endif
|
||||
|
||||
void main()
|
||||
{
|
||||
#ifndef HAIR_SHADER_FIBERS
|
||||
gl_Position = ModelViewProjectionMatrix * vec4(pos, 1.0);
|
||||
#else
|
||||
vec3 pos;
|
||||
vec3 nor;
|
||||
vec2 view_offset;
|
||||
hair_fiber_get_vertex(fiber_index, curve_param, ModelViewMatrix, pos, nor, view_offset);
|
||||
gl_Position = ModelViewProjectionMatrix * vec4(pos, 1.0);
|
||||
gl_Position.xy += view_offset * gl_Position.w;
|
||||
#endif
|
||||
|
||||
#ifdef CLIP_PLANES
|
||||
vec4 worldPosition = (ModelMatrix * vec4(pos, 1.0));
|
||||
gl_ClipDistance[0] = dot(worldPosition, ClipPlanes[0]);
|
||||
|
@@ -2543,3 +2543,38 @@ Gwn_Batch *DRW_cache_particles_get_prim(int type)
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
|
||||
/** \name Strands
|
||||
* \{ */
|
||||
|
||||
Gwn_Batch *DRW_cache_editstrands_get_tips(struct BMEditStrands *es)
|
||||
{
|
||||
return DRW_editstrands_batch_cache_get_tips(es);
|
||||
}
|
||||
|
||||
Gwn_Batch *DRW_cache_editstrands_get_roots(struct BMEditStrands *es)
|
||||
{
|
||||
return DRW_editstrands_batch_cache_get_roots(es);
|
||||
}
|
||||
|
||||
Gwn_Batch *DRW_cache_editstrands_get_points(struct BMEditStrands *es)
|
||||
{
|
||||
return DRW_editstrands_batch_cache_get_points(es);
|
||||
}
|
||||
|
||||
Gwn_Batch *DRW_cache_editstrands_get_wires(struct BMEditStrands *es)
|
||||
{
|
||||
return DRW_editstrands_batch_cache_get_wires(es);
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
|
||||
/** \name Hair */
|
||||
|
||||
Gwn_Batch *DRW_cache_hair_get_fibers(struct HairGroup *group, int subdiv, struct DerivedMesh *scalp,
|
||||
const struct DRWHairFiberTextureBuffer **r_buffer)
|
||||
{
|
||||
return DRW_hair_batch_cache_get_fibers(group, subdiv, scalp, r_buffer);
|
||||
}
|
||||
|
@@ -30,6 +30,10 @@ struct Gwn_Batch;
|
||||
struct GPUMaterial;
|
||||
struct Object;
|
||||
struct ModifierData;
|
||||
struct BMEditStrands;
|
||||
struct HairGroup;
|
||||
struct DRWHairFiberTextureBuffer;
|
||||
struct DerivedMesh;
|
||||
|
||||
void DRW_shape_cache_free(void);
|
||||
|
||||
@@ -156,4 +160,14 @@ struct Gwn_Batch *DRW_cache_particles_get_hair(struct ParticleSystem *psys, stru
|
||||
struct Gwn_Batch *DRW_cache_particles_get_dots(struct ParticleSystem *psys);
|
||||
struct Gwn_Batch *DRW_cache_particles_get_prim(int type);
|
||||
|
||||
/* Strands */
|
||||
struct Gwn_Batch *DRW_cache_editstrands_get_tips(struct BMEditStrands *es);
|
||||
struct Gwn_Batch *DRW_cache_editstrands_get_roots(struct BMEditStrands *es);
|
||||
struct Gwn_Batch *DRW_cache_editstrands_get_points(struct BMEditStrands *es);
|
||||
struct Gwn_Batch *DRW_cache_editstrands_get_wires(struct BMEditStrands *es);
|
||||
|
||||
/* Hair */
|
||||
struct Gwn_Batch *DRW_cache_hair_get_fibers(struct HairGroup *group, int subdiv, struct DerivedMesh *scalp,
|
||||
const struct DRWHairFiberTextureBuffer **r_buffer);
|
||||
|
||||
#endif /* __DRAW_CACHE_H__ */
|
||||
|
@@ -32,6 +32,10 @@ struct ListBase;
|
||||
struct CurveCache;
|
||||
struct ParticleSystem;
|
||||
struct ModifierData;
|
||||
struct BMEditStrands;
|
||||
struct HairGroup;
|
||||
struct DRWHairFiberTextureBuffer;
|
||||
struct DerivedMesh;
|
||||
|
||||
struct Curve;
|
||||
struct Lattice;
|
||||
@@ -50,6 +54,12 @@ void DRW_lattice_batch_cache_free(struct Lattice *lt);
|
||||
void DRW_particle_batch_cache_dirty(struct ParticleSystem *psys, int mode);
|
||||
void DRW_particle_batch_cache_free(struct ParticleSystem *psys);
|
||||
|
||||
void DRW_hair_batch_cache_dirty(struct HairGroup *group, int mode);
|
||||
void DRW_hair_batch_cache_free(struct HairGroup *group);
|
||||
|
||||
void DRW_editstrands_batch_cache_dirty(struct BMEditStrands *es, int mode);
|
||||
void DRW_editstrands_batch_cache_free(struct BMEditStrands *es);
|
||||
|
||||
/* Curve */
|
||||
struct Gwn_Batch *DRW_curve_batch_cache_get_wire_edge(struct Curve *cu, struct CurveCache *ob_curve_cache);
|
||||
struct Gwn_Batch *DRW_curve_batch_cache_get_normal_edge(
|
||||
@@ -107,4 +117,14 @@ void DRW_mesh_cache_sculpt_coords_ensure(struct Mesh *me);
|
||||
struct Gwn_Batch *DRW_particles_batch_cache_get_hair(struct ParticleSystem *psys, struct ModifierData *md);
|
||||
struct Gwn_Batch *DRW_particles_batch_cache_get_dots(struct ParticleSystem *psys);
|
||||
|
||||
/* Strands */
|
||||
struct Gwn_Batch *DRW_editstrands_batch_cache_get_wires(struct BMEditStrands *es);
|
||||
struct Gwn_Batch *DRW_editstrands_batch_cache_get_tips(struct BMEditStrands *es);
|
||||
struct Gwn_Batch *DRW_editstrands_batch_cache_get_roots(struct BMEditStrands *es);
|
||||
struct Gwn_Batch *DRW_editstrands_batch_cache_get_points(struct BMEditStrands *es);
|
||||
|
||||
/* Hair */
|
||||
struct Gwn_Batch *DRW_hair_batch_cache_get_fibers(struct HairGroup *group, int subdiv, struct DerivedMesh *scalp,
|
||||
const struct DRWHairFiberTextureBuffer **r_buffer);
|
||||
|
||||
#endif /* __DRAW_CACHE_IMPL_H__ */
|
||||
|
310
source/blender/draw/intern/draw_cache_impl_hair.c
Normal file
@@ -0,0 +1,310 @@
|
||||
/*
|
||||
* ***** BEGIN GPL LICENSE BLOCK *****
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* The Original Code is Copyright (C) 2017 by Blender Foundation.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Contributor(s): Blender Foundation, Mike Erwin, Dalai Felinto
|
||||
*
|
||||
* ***** END GPL LICENSE BLOCK *****
|
||||
*/
|
||||
|
||||
/** \file draw_cache_impl_strands.c
|
||||
* \ingroup draw
|
||||
*
|
||||
* \brief Strands API for render engines
|
||||
*/
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "BLI_utildefines.h"
|
||||
#include "BLI_math_vector.h"
|
||||
#include "BLI_ghash.h"
|
||||
|
||||
#include "DNA_hair_types.h"
|
||||
#include "DNA_scene_types.h"
|
||||
|
||||
#include "BKE_hair.h"
|
||||
|
||||
#include "GPU_batch.h"
|
||||
#include "GPU_extensions.h"
|
||||
#include "GPU_texture.h"
|
||||
|
||||
#include "draw_common.h"
|
||||
#include "draw_cache_impl.h" /* own include */
|
||||
#include "DRW_render.h"
|
||||
|
||||
// timing
|
||||
//#define DEBUG_TIME
|
||||
#ifdef DEBUG_TIME
|
||||
# include "PIL_time_utildefines.h"
|
||||
#else
|
||||
# define TIMEIT_START(var)
|
||||
# define TIMEIT_VALUE(var)
|
||||
# define TIMEIT_VALUE_PRINT(var)
|
||||
# define TIMEIT_END(var)
|
||||
# define TIMEIT_BENCH(expr, id) (expr)
|
||||
# define TIMEIT_BLOCK_INIT(var)
|
||||
# define TIMEIT_BLOCK_START(var)
|
||||
# define TIMEIT_BLOCK_END(var)
|
||||
# define TIMEIT_BLOCK_STATS(var)
|
||||
#endif
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
/* Hair Gwn_Batch Cache */
|
||||
|
||||
typedef struct HairBatchCache {
|
||||
Gwn_VertBuf *verts;
|
||||
Gwn_IndexBuf *segments;
|
||||
|
||||
Gwn_Batch *fibers;
|
||||
|
||||
DRWHairFiberTextureBuffer texbuffer;
|
||||
|
||||
/* settings to determine if cache is invalid */
|
||||
bool is_dirty;
|
||||
} HairBatchCache;
|
||||
|
||||
/* Gwn_Batch cache management. */
|
||||
|
||||
static void hair_batch_cache_clear(HairGroup *group);
|
||||
|
||||
static bool hair_batch_cache_valid(HairGroup *group)
|
||||
{
|
||||
HairBatchCache *cache = group->draw_batch_cache;
|
||||
|
||||
if (cache == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (cache->is_dirty) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void hair_batch_cache_init(HairGroup *group)
|
||||
{
|
||||
HairBatchCache *cache = group->draw_batch_cache;
|
||||
|
||||
if (!cache) {
|
||||
cache = group->draw_batch_cache = MEM_callocN(sizeof(*cache), __func__);
|
||||
}
|
||||
else {
|
||||
memset(cache, 0, sizeof(*cache));
|
||||
}
|
||||
|
||||
cache->is_dirty = false;
|
||||
}
|
||||
|
||||
static HairBatchCache *hair_batch_cache_get(HairGroup *group)
|
||||
{
|
||||
if (!hair_batch_cache_valid(group)) {
|
||||
hair_batch_cache_clear(group);
|
||||
hair_batch_cache_init(group);
|
||||
}
|
||||
return group->draw_batch_cache;
|
||||
}
|
||||
|
||||
void DRW_hair_batch_cache_dirty(HairGroup *group, int mode)
|
||||
{
|
||||
HairBatchCache *cache = group->draw_batch_cache;
|
||||
if (cache == NULL) {
|
||||
return;
|
||||
}
|
||||
switch (mode) {
|
||||
case BKE_HAIR_BATCH_DIRTY_ALL:
|
||||
cache->is_dirty = true;
|
||||
break;
|
||||
default:
|
||||
BLI_assert(0);
|
||||
}
|
||||
}
|
||||
|
||||
static void hair_batch_cache_clear(HairGroup *group)
|
||||
{
|
||||
HairBatchCache *cache = group->draw_batch_cache;
|
||||
|
||||
if (group->draw_texture_cache) {
|
||||
GPU_texture_free(group->draw_texture_cache);
|
||||
group->draw_texture_cache = NULL;
|
||||
}
|
||||
|
||||
if (cache) {
|
||||
GWN_BATCH_DISCARD_SAFE(cache->fibers);
|
||||
GWN_VERTBUF_DISCARD_SAFE(cache->verts);
|
||||
GWN_INDEXBUF_DISCARD_SAFE(cache->segments);
|
||||
|
||||
{
|
||||
DRWHairFiberTextureBuffer *buffer = &cache->texbuffer;
|
||||
if (buffer->data) {
|
||||
MEM_freeN(buffer->data);
|
||||
buffer->data = NULL;
|
||||
}
|
||||
buffer->fiber_start = 0;
|
||||
buffer->strand_map_start = 0;
|
||||
buffer->strand_vertex_start = 0;
|
||||
buffer->width = 0;
|
||||
buffer->height = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DRW_hair_batch_cache_free(HairGroup *group)
|
||||
{
|
||||
hair_batch_cache_clear(group);
|
||||
MEM_SAFE_FREE(group->draw_batch_cache);
|
||||
}
|
||||
|
||||
static void hair_batch_cache_ensure_fibers(HairGroup *group, struct DerivedMesh *scalp, int subdiv, HairBatchCache *cache)
|
||||
{
|
||||
TIMEIT_START(hair_batch_cache_ensure_fibers);
|
||||
|
||||
GWN_VERTBUF_DISCARD_SAFE(cache->verts);
|
||||
GWN_INDEXBUF_DISCARD_SAFE(cache->segments);
|
||||
|
||||
const int totfibers = group->num_follicles;
|
||||
int *fiber_lengths = BKE_hair_group_get_fiber_lengths(group, scalp, subdiv);
|
||||
int totpoint = 0;
|
||||
for (int i = 0; i < totfibers; ++i) {
|
||||
totpoint += fiber_lengths[i];
|
||||
}
|
||||
const int totseg = totpoint - totfibers;
|
||||
|
||||
static Gwn_VertFormat format = { 0 };
|
||||
static unsigned curve_param_id, fiber_index_id;
|
||||
|
||||
/* initialize vertex format */
|
||||
if (format.attrib_ct == 0) {
|
||||
fiber_index_id = GWN_vertformat_attr_add(&format, "fiber_index", GWN_COMP_I32, 1, GWN_FETCH_INT);
|
||||
curve_param_id = GWN_vertformat_attr_add(&format, "curve_param", GWN_COMP_F32, 1, GWN_FETCH_FLOAT);
|
||||
}
|
||||
|
||||
cache->verts = GWN_vertbuf_create_with_format(&format);
|
||||
|
||||
Gwn_IndexBufBuilder elb;
|
||||
{
|
||||
TIMEIT_START(data_alloc);
|
||||
Gwn_PrimType prim_type;
|
||||
unsigned prim_ct, vert_ct;
|
||||
prim_type = GWN_PRIM_TRIS;
|
||||
prim_ct = 2 * totseg;
|
||||
vert_ct = 2 * totpoint;
|
||||
|
||||
GWN_vertbuf_data_alloc(cache->verts, vert_ct);
|
||||
GWN_indexbuf_init(&elb, prim_type, prim_ct, vert_ct);
|
||||
TIMEIT_END(data_alloc);
|
||||
}
|
||||
|
||||
TIMEIT_START(data_fill);
|
||||
TIMEIT_BLOCK_INIT(GWN_vertbuf_attr_set);
|
||||
TIMEIT_BLOCK_INIT(GWN_indexbuf_add_tri_verts);
|
||||
int vi = 0;
|
||||
for (int i = 0; i < totfibers; ++i) {
|
||||
const int fiblen = fiber_lengths[i];
|
||||
const float da = fiblen > 1 ? 1.0f / (fiblen-1) : 0.0f;
|
||||
|
||||
float a = 0.0f;
|
||||
for (int k = 0; k < fiblen; ++k) {
|
||||
TIMEIT_BLOCK_START(GWN_vertbuf_attr_set);
|
||||
GWN_vertbuf_attr_set(cache->verts, fiber_index_id, vi, &i);
|
||||
GWN_vertbuf_attr_set(cache->verts, curve_param_id, vi, &a);
|
||||
GWN_vertbuf_attr_set(cache->verts, fiber_index_id, vi+1, &i);
|
||||
GWN_vertbuf_attr_set(cache->verts, curve_param_id, vi+1, &a);
|
||||
TIMEIT_BLOCK_END(GWN_vertbuf_attr_set);
|
||||
|
||||
if (k > 0) {
|
||||
TIMEIT_BLOCK_START(GWN_indexbuf_add_tri_verts);
|
||||
GWN_indexbuf_add_tri_verts(&elb, vi-2, vi-1, vi+1);
|
||||
GWN_indexbuf_add_tri_verts(&elb, vi+1, vi, vi-2);
|
||||
TIMEIT_BLOCK_END(GWN_indexbuf_add_tri_verts);
|
||||
}
|
||||
|
||||
vi += 2;
|
||||
a += da;
|
||||
}
|
||||
}
|
||||
TIMEIT_BLOCK_STATS(GWN_vertbuf_attr_set);
|
||||
TIMEIT_BLOCK_STATS(GWN_indexbuf_add_tri_verts);
|
||||
#ifdef DEBUG_TIME
|
||||
printf("Total GWN time: %f\n", _timeit_var_GWN_vertbuf_attr_set + _timeit_var_GWN_indexbuf_add_tri_verts);
|
||||
#endif
|
||||
fflush(stdout);
|
||||
TIMEIT_END(data_fill);
|
||||
|
||||
MEM_freeN(fiber_lengths);
|
||||
|
||||
TIMEIT_BENCH(cache->segments = GWN_indexbuf_build(&elb), indexbuf_build);
|
||||
|
||||
TIMEIT_END(hair_batch_cache_ensure_fibers);
|
||||
}
|
||||
|
||||
static void hair_batch_cache_ensure_texbuffer(HairGroup *group, struct DerivedMesh *scalp, int subdiv, HairBatchCache *cache)
|
||||
{
|
||||
DRWHairFiberTextureBuffer *buffer = &cache->texbuffer;
|
||||
static const int elemsize = 8;
|
||||
const int width = GPU_max_texture_size();
|
||||
const int align = width * elemsize;
|
||||
|
||||
// Offsets in bytes
|
||||
int b_size, b_strand_map_start, b_strand_vertex_start, b_fiber_start;
|
||||
BKE_hair_group_get_texture_buffer_size(group, scalp, subdiv, &b_size,
|
||||
&b_strand_map_start, &b_strand_vertex_start, &b_fiber_start);
|
||||
// Pad for alignment
|
||||
b_size += align - b_size % align;
|
||||
|
||||
// Convert to element size as texture offsets
|
||||
const int size = b_size / elemsize;
|
||||
const int height = size / width;
|
||||
|
||||
buffer->data = MEM_mallocN(b_size, "hair fiber texture buffer");
|
||||
BKE_hair_group_get_texture_buffer(group, scalp, subdiv, buffer->data);
|
||||
|
||||
buffer->width = width;
|
||||
buffer->height = height;
|
||||
buffer->strand_map_start = b_strand_map_start / elemsize;
|
||||
buffer->strand_vertex_start = b_strand_vertex_start / elemsize;
|
||||
buffer->fiber_start = b_fiber_start / elemsize;
|
||||
}
|
||||
|
||||
Gwn_Batch *DRW_hair_batch_cache_get_fibers(HairGroup *group, int subdiv, struct DerivedMesh *scalp,
|
||||
const DRWHairFiberTextureBuffer **r_buffer)
|
||||
{
|
||||
HairBatchCache *cache = hair_batch_cache_get(group);
|
||||
|
||||
TIMEIT_START(DRW_hair_batch_cache_get_fibers);
|
||||
|
||||
if (cache->fibers == NULL) {
|
||||
TIMEIT_BENCH(hair_batch_cache_ensure_fibers(group, scalp, subdiv, cache),
|
||||
hair_batch_cache_ensure_fibers);
|
||||
|
||||
TIMEIT_BENCH(cache->fibers = GWN_batch_create(GWN_PRIM_TRIS, cache->verts, cache->segments),
|
||||
GWN_batch_create);
|
||||
|
||||
TIMEIT_BENCH(hair_batch_cache_ensure_texbuffer(group, scalp, subdiv, cache),
|
||||
hair_batch_cache_ensure_texbuffer);
|
||||
}
|
||||
|
||||
if (r_buffer) {
|
||||
*r_buffer = &cache->texbuffer;
|
||||
}
|
||||
|
||||
TIMEIT_END(DRW_hair_batch_cache_get_fibers);
|
||||
|
||||
return cache->fibers;
|
||||
}
|
341
source/blender/draw/intern/draw_cache_impl_strands.c
Normal file
@@ -0,0 +1,341 @@
|
||||
/*
|
||||
* ***** BEGIN GPL LICENSE BLOCK *****
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* The Original Code is Copyright (C) 2017 by Blender Foundation.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Contributor(s): Blender Foundation, Mike Erwin, Dalai Felinto
|
||||
*
|
||||
* ***** END GPL LICENSE BLOCK *****
|
||||
*/
|
||||
|
||||
/** \file draw_cache_impl_strands.c
|
||||
* \ingroup draw
|
||||
*
|
||||
* \brief Strands API for render engines
|
||||
*/
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "BLI_utildefines.h"
|
||||
#include "BLI_math_vector.h"
|
||||
#include "BLI_ghash.h"
|
||||
|
||||
#include "DNA_hair_types.h"
|
||||
#include "DNA_scene_types.h"
|
||||
|
||||
#include "BKE_editstrands.h"
|
||||
|
||||
#include "GPU_batch.h"
|
||||
#include "GPU_extensions.h"
|
||||
#include "GPU_texture.h"
|
||||
|
||||
#include "draw_common.h"
|
||||
#include "draw_cache_impl.h" /* own include */
|
||||
#include "DRW_render.h"
|
||||
|
||||
// timing
|
||||
//#define DEBUG_TIME
|
||||
#ifdef DEBUG_TIME
|
||||
# include "PIL_time_utildefines.h"
|
||||
#else
|
||||
# define TIMEIT_START(var)
|
||||
# define TIMEIT_VALUE(var)
|
||||
# define TIMEIT_VALUE_PRINT(var)
|
||||
# define TIMEIT_END(var)
|
||||
# define TIMEIT_BENCH(expr, id) (expr)
|
||||
# define TIMEIT_BLOCK_INIT(var)
|
||||
# define TIMEIT_BLOCK_START(var)
|
||||
# define TIMEIT_BLOCK_END(var)
|
||||
# define TIMEIT_BLOCK_STATS(var)
|
||||
#endif
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
/* Strands Gwn_Batch Cache */
|
||||
|
||||
typedef enum VertexDrawFlags
|
||||
{
|
||||
STRANDS_VERTEX_SELECT = (1 << 0),
|
||||
} VertexDrawFlags;
|
||||
|
||||
typedef struct StrandsBatchCache {
|
||||
Gwn_VertBuf *pos;
|
||||
Gwn_IndexBuf *segments;
|
||||
Gwn_IndexBuf *tips_idx;
|
||||
Gwn_IndexBuf *roots_idx;
|
||||
|
||||
Gwn_Batch *wires;
|
||||
Gwn_Batch *tips;
|
||||
Gwn_Batch *roots;
|
||||
Gwn_Batch *points;
|
||||
|
||||
int segment_count;
|
||||
int point_count;
|
||||
|
||||
/* settings to determine if cache is invalid */
|
||||
bool is_dirty;
|
||||
} StrandsBatchCache;
|
||||
|
||||
/* Gwn_Batch cache management. */
|
||||
|
||||
static void editstrands_batch_cache_clear(BMEditStrands *es);
|
||||
|
||||
static bool editstrands_batch_cache_valid(BMEditStrands *es)
|
||||
{
|
||||
StrandsBatchCache *cache = es->batch_cache;
|
||||
|
||||
if (cache == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (cache->is_dirty) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void editstrands_batch_cache_init(BMEditStrands *es)
|
||||
{
|
||||
StrandsBatchCache *cache = es->batch_cache;
|
||||
|
||||
if (!cache) {
|
||||
cache = es->batch_cache = MEM_callocN(sizeof(*cache), __func__);
|
||||
}
|
||||
else {
|
||||
memset(cache, 0, sizeof(*cache));
|
||||
}
|
||||
|
||||
cache->is_dirty = false;
|
||||
}
|
||||
|
||||
static StrandsBatchCache *editstrands_batch_cache_get(BMEditStrands *es)
|
||||
{
|
||||
if (!editstrands_batch_cache_valid(es)) {
|
||||
editstrands_batch_cache_clear(es);
|
||||
editstrands_batch_cache_init(es);
|
||||
}
|
||||
return es->batch_cache;
|
||||
}
|
||||
|
||||
void DRW_editstrands_batch_cache_dirty(BMEditStrands *es, int mode)
|
||||
{
|
||||
StrandsBatchCache *cache = es->batch_cache;
|
||||
if (cache == NULL) {
|
||||
return;
|
||||
}
|
||||
switch (mode) {
|
||||
case BKE_STRANDS_BATCH_DIRTY_ALL:
|
||||
case BKE_STRANDS_BATCH_DIRTY_SELECT:
|
||||
cache->is_dirty = true;
|
||||
break;
|
||||
default:
|
||||
BLI_assert(0);
|
||||
}
|
||||
}
|
||||
|
||||
static void editstrands_batch_cache_clear(BMEditStrands *es)
|
||||
{
|
||||
StrandsBatchCache *cache = es->batch_cache;
|
||||
|
||||
if (cache) {
|
||||
GWN_BATCH_DISCARD_SAFE(cache->wires);
|
||||
GWN_BATCH_DISCARD_SAFE(cache->points);
|
||||
GWN_BATCH_DISCARD_SAFE(cache->tips);
|
||||
GWN_BATCH_DISCARD_SAFE(cache->roots);
|
||||
GWN_VERTBUF_DISCARD_SAFE(cache->pos);
|
||||
GWN_INDEXBUF_DISCARD_SAFE(cache->segments);
|
||||
}
|
||||
}
|
||||
|
||||
void DRW_editstrands_batch_cache_free(BMEditStrands *es)
|
||||
{
|
||||
editstrands_batch_cache_clear(es);
|
||||
MEM_SAFE_FREE(es->batch_cache);
|
||||
}
|
||||
|
||||
static void editstrands_batch_cache_ensure_pos(BMEditStrands *es, StrandsBatchCache *cache)
|
||||
{
|
||||
if (cache->pos) {
|
||||
return;
|
||||
}
|
||||
|
||||
GWN_VERTBUF_DISCARD_SAFE(cache->pos);
|
||||
|
||||
static Gwn_VertFormat format = { 0 };
|
||||
static unsigned pos_id, flag_id;
|
||||
|
||||
/* initialize vertex format */
|
||||
if (format.attrib_ct == 0) {
|
||||
pos_id = GWN_vertformat_attr_add(&format, "pos", GWN_COMP_F32, 3, GWN_FETCH_FLOAT);
|
||||
flag_id = GWN_vertformat_attr_add(&format, "flag", GWN_COMP_U8, 1, GWN_FETCH_INT);
|
||||
}
|
||||
|
||||
BMesh *bm = es->base.bm;
|
||||
BMVert *vert;
|
||||
BMIter iter;
|
||||
int curr_point;
|
||||
|
||||
cache->pos = GWN_vertbuf_create_with_format(&format);
|
||||
GWN_vertbuf_data_alloc(cache->pos, bm->totvert);
|
||||
|
||||
BM_ITER_MESH_INDEX(vert, &iter, bm, BM_VERTS_OF_MESH, curr_point) {
|
||||
GWN_vertbuf_attr_set(cache->pos, pos_id, curr_point, vert->co);
|
||||
|
||||
uint8_t flag = 0;
|
||||
if (BM_elem_flag_test(vert, BM_ELEM_SELECT))
|
||||
flag |= STRANDS_VERTEX_SELECT;
|
||||
GWN_vertbuf_attr_set(cache->pos, flag_id, curr_point, &flag);
|
||||
}
|
||||
}
|
||||
|
||||
static void editstrands_batch_cache_ensure_segments(BMEditStrands *es, StrandsBatchCache *cache)
|
||||
{
|
||||
if (cache->segments) {
|
||||
return;
|
||||
}
|
||||
|
||||
GWN_INDEXBUF_DISCARD_SAFE(cache->segments);
|
||||
|
||||
BMesh *bm = es->base.bm;
|
||||
BMEdge *edge;
|
||||
BMIter iter;
|
||||
|
||||
Gwn_IndexBufBuilder elb;
|
||||
GWN_indexbuf_init(&elb, GWN_PRIM_LINES, bm->totedge, bm->totvert);
|
||||
|
||||
BM_mesh_elem_index_ensure(bm, BM_VERT);
|
||||
|
||||
BM_ITER_MESH(edge, &iter, bm, BM_EDGES_OF_MESH) {
|
||||
GWN_indexbuf_add_line_verts(&elb, BM_elem_index_get(edge->v1), BM_elem_index_get(edge->v2));
|
||||
}
|
||||
|
||||
cache->segments = GWN_indexbuf_build(&elb);
|
||||
}
|
||||
|
||||
static void editstrands_batch_cache_ensure_tips_idx(BMEditStrands *es, StrandsBatchCache *cache)
|
||||
{
|
||||
if (cache->tips_idx) {
|
||||
return;
|
||||
}
|
||||
|
||||
GWN_INDEXBUF_DISCARD_SAFE(cache->tips_idx);
|
||||
|
||||
BMesh *bm = es->base.bm;
|
||||
int totstrands = BM_strands_count(bm);
|
||||
BMVert *root, *vert;
|
||||
BMIter iter, iter_strand;
|
||||
|
||||
Gwn_IndexBufBuilder elb;
|
||||
GWN_indexbuf_init(&elb, GWN_PRIM_POINTS, totstrands, bm->totvert);
|
||||
|
||||
BM_mesh_elem_index_ensure(bm, BM_VERT);
|
||||
|
||||
BM_ITER_STRANDS(root, &iter, bm, BM_STRANDS_OF_MESH) {
|
||||
BM_ITER_STRANDS_ELEM(vert, &iter_strand, root, BM_VERTS_OF_STRAND)
|
||||
{
|
||||
if (BM_strands_vert_is_tip(vert)) {
|
||||
GWN_indexbuf_add_point_vert(&elb, BM_elem_index_get(vert));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cache->tips_idx = GWN_indexbuf_build(&elb);
|
||||
}
|
||||
|
||||
static void editstrands_batch_cache_ensure_roots_idx(BMEditStrands *es, StrandsBatchCache *cache)
|
||||
{
|
||||
if (cache->roots_idx) {
|
||||
return;
|
||||
}
|
||||
|
||||
GWN_INDEXBUF_DISCARD_SAFE(cache->roots_idx);
|
||||
|
||||
BMesh *bm = es->base.bm;
|
||||
int totstrands = BM_strands_count(bm);
|
||||
BMVert *root, *vert;
|
||||
BMIter iter, iter_strand;
|
||||
|
||||
Gwn_IndexBufBuilder elb;
|
||||
GWN_indexbuf_init(&elb, GWN_PRIM_POINTS, totstrands, bm->totvert);
|
||||
|
||||
BM_mesh_elem_index_ensure(bm, BM_VERT);
|
||||
|
||||
BM_ITER_STRANDS(root, &iter, bm, BM_STRANDS_OF_MESH) {
|
||||
BM_ITER_STRANDS_ELEM(vert, &iter_strand, root, BM_VERTS_OF_STRAND)
|
||||
{
|
||||
if (BM_strands_vert_is_root(vert)) {
|
||||
GWN_indexbuf_add_point_vert(&elb, BM_elem_index_get(vert));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cache->roots_idx = GWN_indexbuf_build(&elb);
|
||||
}
|
||||
|
||||
Gwn_Batch *DRW_editstrands_batch_cache_get_wires(BMEditStrands *es)
|
||||
{
|
||||
StrandsBatchCache *cache = editstrands_batch_cache_get(es);
|
||||
|
||||
if (cache->wires == NULL) {
|
||||
editstrands_batch_cache_ensure_pos(es, cache);
|
||||
editstrands_batch_cache_ensure_segments(es, cache);
|
||||
cache->wires = GWN_batch_create(GWN_PRIM_LINES, cache->pos, cache->segments);
|
||||
}
|
||||
|
||||
return cache->wires;
|
||||
}
|
||||
|
||||
Gwn_Batch *DRW_editstrands_batch_cache_get_tips(BMEditStrands *es)
|
||||
{
|
||||
StrandsBatchCache *cache = editstrands_batch_cache_get(es);
|
||||
|
||||
if (cache->tips == NULL) {
|
||||
editstrands_batch_cache_ensure_pos(es, cache);
|
||||
editstrands_batch_cache_ensure_tips_idx(es, cache);
|
||||
cache->tips = GWN_batch_create(GWN_PRIM_POINTS, cache->pos, cache->tips_idx);
|
||||
}
|
||||
|
||||
return cache->tips;
|
||||
}
|
||||
|
||||
Gwn_Batch *DRW_editstrands_batch_cache_get_roots(BMEditStrands *es)
|
||||
{
|
||||
StrandsBatchCache *cache = editstrands_batch_cache_get(es);
|
||||
|
||||
if (cache->roots == NULL) {
|
||||
editstrands_batch_cache_ensure_pos(es, cache);
|
||||
editstrands_batch_cache_ensure_roots_idx(es, cache);
|
||||
cache->roots = GWN_batch_create(GWN_PRIM_POINTS, cache->pos, cache->roots_idx);
|
||||
}
|
||||
|
||||
return cache->roots;
|
||||
}
|
||||
|
||||
Gwn_Batch *DRW_editstrands_batch_cache_get_points(BMEditStrands *es)
|
||||
{
|
||||
StrandsBatchCache *cache = editstrands_batch_cache_get(es);
|
||||
|
||||
if (cache->points == NULL) {
|
||||
editstrands_batch_cache_ensure_pos(es, cache);
|
||||
cache->points = GWN_batch_create(GWN_PRIM_POINTS, cache->pos, NULL);
|
||||
}
|
||||
|
||||
return cache->points;
|
||||
}
|
@@ -29,7 +29,9 @@
|
||||
struct DRWPass;
|
||||
struct DRWShadingGroup;
|
||||
struct Gwn_Batch;
|
||||
struct GPUTexture;
|
||||
struct Object;
|
||||
struct Scene;
|
||||
struct SceneLayer;
|
||||
|
||||
/* Used as ubo but colors can be directly referenced as well */
|
||||
@@ -128,4 +130,19 @@ void DRW_shgroup_armature_edit(
|
||||
bool DRW_pose_mode_armature(
|
||||
struct Object *ob, struct Object *active_ob);
|
||||
|
||||
/* hair drawing */
|
||||
typedef struct DRWHairFiberTextureBuffer {
|
||||
void *data;
|
||||
int strand_map_start;
|
||||
int strand_vertex_start;
|
||||
int fiber_start;
|
||||
int width;
|
||||
int height;
|
||||
} DRWHairFiberTextureBuffer;
|
||||
|
||||
const char* DRW_hair_shader_defines(void);
|
||||
|
||||
void DRW_hair_shader_uniforms(struct DRWShadingGroup *shgrp, struct Scene *scene,
|
||||
struct GPUTexture **fibertex, const struct DRWHairFiberTextureBuffer *texbuffer);
|
||||
|
||||
#endif /* __DRAW_COMMON_H__ */
|
||||
|
61
source/blender/draw/intern/draw_hair.c
Normal file
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright 2016, Blender Foundation.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* Contributor(s): Blender Institute
|
||||
*
|
||||
*/
|
||||
|
||||
/** \file draw_hair.c
|
||||
* \ingroup draw
|
||||
*/
|
||||
|
||||
#include "DNA_scene_types.h"
|
||||
|
||||
#include "DRW_render.h"
|
||||
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
#include "GPU_extensions.h"
|
||||
#include "GPU_texture.h"
|
||||
|
||||
#include "draw_common.h"
|
||||
|
||||
const char* DRW_hair_shader_defines(void)
|
||||
{
|
||||
static char str[256];
|
||||
|
||||
BLI_snprintf(str, sizeof(str), "#define HAIR_SHADER_FIBERS\n#define HAIR_SHADER_TEX_WIDTH %d\n",
|
||||
GPU_max_texture_size());
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
void DRW_hair_shader_uniforms(DRWShadingGroup *shgrp, Scene *scene,
|
||||
GPUTexture **fibertex, const DRWHairFiberTextureBuffer *texbuffer)
|
||||
{
|
||||
const HairEditSettings *tsettings = &scene->toolsettings->hair_edit;
|
||||
|
||||
DRW_shgroup_uniform_vec2(shgrp, "viewport_size", DRW_viewport_size_get(), 1);
|
||||
//DRW_shgroup_uniform_float(shgrp, "ribbon_width", &tsettings->hair_draw_size, 1);
|
||||
static float test = 2.5f;
|
||||
DRW_shgroup_uniform_float(shgrp, "ribbon_width", &test, 1);
|
||||
|
||||
DRW_shgroup_uniform_buffer(shgrp, "fiber_data", fibertex);
|
||||
DRW_shgroup_uniform_int(shgrp, "strand_map_start", &texbuffer->strand_map_start, 1);
|
||||
DRW_shgroup_uniform_int(shgrp, "strand_vertex_start", &texbuffer->strand_vertex_start, 1);
|
||||
DRW_shgroup_uniform_int(shgrp, "fiber_start", &texbuffer->fiber_start, 1);
|
||||
}
|
@@ -2917,6 +2917,9 @@ static void DRW_engines_enable_from_mode(int mode)
|
||||
case CTX_MODE_PARTICLE:
|
||||
use_drw_engine(&draw_engine_particle_type);
|
||||
break;
|
||||
case CTX_MODE_HAIR:
|
||||
use_drw_engine(&draw_engine_edit_strands_type);
|
||||
break;
|
||||
case CTX_MODE_OBJECT:
|
||||
break;
|
||||
default:
|
||||
@@ -3579,6 +3582,7 @@ void DRW_engines_register(void)
|
||||
DRW_engine_register(&draw_engine_edit_lattice_type);
|
||||
DRW_engine_register(&draw_engine_edit_mesh_type);
|
||||
DRW_engine_register(&draw_engine_edit_metaball_type);
|
||||
DRW_engine_register(&draw_engine_edit_strands_type);
|
||||
DRW_engine_register(&draw_engine_edit_surface_type);
|
||||
DRW_engine_register(&draw_engine_edit_text_type);
|
||||
DRW_engine_register(&draw_engine_paint_texture_type);
|
||||
@@ -3602,6 +3606,12 @@ void DRW_engines_register(void)
|
||||
/* BKE: particle.c */
|
||||
extern void *BKE_particle_batch_cache_dirty_cb;
|
||||
extern void *BKE_particle_batch_cache_free_cb;
|
||||
/* BKE: editstrands.c */
|
||||
extern void *BKE_editstrands_batch_cache_dirty_cb;
|
||||
extern void *BKE_editstrands_batch_cache_free_cb;
|
||||
/* BKE: hair.c */
|
||||
extern void *BKE_hair_batch_cache_dirty_cb;
|
||||
extern void *BKE_hair_batch_cache_free_cb;
|
||||
|
||||
BKE_curve_batch_cache_dirty_cb = DRW_curve_batch_cache_dirty;
|
||||
BKE_curve_batch_cache_free_cb = DRW_curve_batch_cache_free;
|
||||
@@ -3614,6 +3624,12 @@ void DRW_engines_register(void)
|
||||
|
||||
BKE_particle_batch_cache_dirty_cb = DRW_particle_batch_cache_dirty;
|
||||
BKE_particle_batch_cache_free_cb = DRW_particle_batch_cache_free;
|
||||
|
||||
BKE_editstrands_batch_cache_dirty_cb = DRW_editstrands_batch_cache_dirty;
|
||||
BKE_editstrands_batch_cache_free_cb = DRW_editstrands_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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -32,6 +32,7 @@ extern DrawEngineType draw_engine_edit_curve_type;
|
||||
extern DrawEngineType draw_engine_edit_lattice_type;
|
||||
extern DrawEngineType draw_engine_edit_mesh_type;
|
||||
extern DrawEngineType draw_engine_edit_metaball_type;
|
||||
extern DrawEngineType draw_engine_edit_strands_type;
|
||||
extern DrawEngineType draw_engine_edit_surface_type;
|
||||
extern DrawEngineType draw_engine_edit_text_type;
|
||||
extern DrawEngineType draw_engine_paint_texture_type;
|
||||
@@ -41,4 +42,4 @@ extern DrawEngineType draw_engine_particle_type;
|
||||
extern DrawEngineType draw_engine_pose_type;
|
||||
extern DrawEngineType draw_engine_sculpt_type;
|
||||
|
||||
#endif /* __DRAW_MODE_ENGINES_H__ */
|
||||
#endif /* __DRAW_MODE_ENGINES_H__ */
|
||||
|
308
source/blender/draw/modes/edit_strands_mode.c
Normal file
@@ -0,0 +1,308 @@
|
||||
/*
|
||||
* Copyright 2016, Blender Foundation.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* Contributor(s): Blender Institute
|
||||
*
|
||||
*/
|
||||
|
||||
/** \file blender/draw/modes/particle_mode.c
|
||||
* \ingroup draw
|
||||
*/
|
||||
|
||||
#include "DRW_engine.h"
|
||||
#include "DRW_render.h"
|
||||
|
||||
#include "BLI_ghash.h"
|
||||
|
||||
#include "DNA_view3d_types.h"
|
||||
|
||||
#include "BKE_editstrands.h"
|
||||
|
||||
#include "GPU_shader.h"
|
||||
#include "GPU_texture.h"
|
||||
|
||||
#include "draw_common.h"
|
||||
|
||||
#include "draw_mode_engines.h"
|
||||
|
||||
extern GlobalsUboStorage ts;
|
||||
|
||||
extern char datatoc_edit_strands_vert_glsl[];
|
||||
extern char datatoc_gpu_shader_point_varying_color_frag_glsl[];
|
||||
extern char datatoc_gpu_shader_3D_smooth_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_STRANDS_engine_init() to
|
||||
* initialize most of them and EDIT_STRANDS_cache_init()
|
||||
* for EDIT_STRANDS_PassList */
|
||||
|
||||
typedef struct EDIT_STRANDS_PassList {
|
||||
/* Declare all passes here and init them in
|
||||
* EDIT_STRANDS_cache_init().
|
||||
* Only contains (DRWPass *) */
|
||||
struct DRWPass *wires;
|
||||
struct DRWPass *tips;
|
||||
struct DRWPass *roots;
|
||||
struct DRWPass *points;
|
||||
} EDIT_STRANDS_PassList;
|
||||
|
||||
typedef struct EDIT_STRANDS_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_STRANDS_PrivateData *g_data;
|
||||
} EDIT_STRANDS_StorageList;
|
||||
|
||||
typedef struct EDIT_STRANDS_Data {
|
||||
/* Struct returned by DRW_viewport_engine_data_get.
|
||||
* If you don't use one of these, just make it a (void *) */
|
||||
// void *fbl;
|
||||
void *engine_type; /* Required */
|
||||
DRWViewportEmptyList *fbl;
|
||||
DRWViewportEmptyList *txl;
|
||||
EDIT_STRANDS_PassList *psl;
|
||||
EDIT_STRANDS_StorageList *stl;
|
||||
} EDIT_STRANDS_Data;
|
||||
|
||||
/* *********** STATIC *********** */
|
||||
|
||||
static struct {
|
||||
/* Custom shaders :
|
||||
* Add sources to source/blender/draw/modes/shaders
|
||||
* init in EDIT_STRANDS_engine_init();
|
||||
* free in EDIT_STRANDS_engine_free(); */
|
||||
struct GPUShader *edit_point_shader;
|
||||
struct GPUShader *edit_wire_shader;
|
||||
} e_data = {NULL}; /* Engine data */
|
||||
|
||||
typedef struct EDIT_STRANDS_PrivateData {
|
||||
/* resulting curve as 'wire' for fast editmode drawing */
|
||||
DRWShadingGroup *wires_shgrp;
|
||||
DRWShadingGroup *tips_shgrp;
|
||||
DRWShadingGroup *roots_shgrp;
|
||||
DRWShadingGroup *points_shgrp;
|
||||
} EDIT_STRANDS_PrivateData; /* Transient data */
|
||||
|
||||
/* *********** FUNCTIONS *********** */
|
||||
|
||||
/* Init Textures, Framebuffers, Storage and Shaders.
|
||||
* It is called for every frames.
|
||||
* (Optional) */
|
||||
static void EDIT_STRANDS_engine_init(void *vedata)
|
||||
{
|
||||
EDIT_STRANDS_StorageList *stl = ((EDIT_STRANDS_Data *)vedata)->stl;
|
||||
|
||||
UNUSED_VARS(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.edit_point_shader) {
|
||||
e_data.edit_point_shader = DRW_shader_create(
|
||||
datatoc_edit_strands_vert_glsl,
|
||||
NULL,
|
||||
datatoc_gpu_shader_point_varying_color_frag_glsl,
|
||||
NULL);
|
||||
}
|
||||
|
||||
if (!e_data.edit_wire_shader) {
|
||||
e_data.edit_wire_shader = DRW_shader_create(
|
||||
datatoc_edit_strands_vert_glsl,
|
||||
NULL,
|
||||
datatoc_gpu_shader_3D_smooth_color_frag_glsl,
|
||||
NULL);
|
||||
}
|
||||
}
|
||||
|
||||
/* Cleanup when destroying the engine.
|
||||
* This is not per viewport ! only when quitting blender.
|
||||
* Mostly used for freeing shaders */
|
||||
static void EDIT_STRANDS_engine_free(void)
|
||||
{
|
||||
DRW_SHADER_FREE_SAFE(e_data.edit_point_shader);
|
||||
DRW_SHADER_FREE_SAFE(e_data.edit_wire_shader);
|
||||
}
|
||||
|
||||
/* Here init all passes and shading groups
|
||||
* Assume that all Passes are NULL */
|
||||
static void EDIT_STRANDS_cache_init(void *vedata)
|
||||
{
|
||||
EDIT_STRANDS_PassList *psl = ((EDIT_STRANDS_Data *)vedata)->psl;
|
||||
EDIT_STRANDS_StorageList *stl = ((EDIT_STRANDS_Data *)vedata)->stl;
|
||||
|
||||
if (!stl->g_data) {
|
||||
/* Alloc transient pointers */
|
||||
stl->g_data = MEM_mallocN(sizeof(*stl->g_data), __func__);
|
||||
}
|
||||
|
||||
{
|
||||
/* Strand wires */
|
||||
DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS | DRW_STATE_BLEND;
|
||||
psl->wires = DRW_pass_create("Strand Wire Verts Pass", state);
|
||||
|
||||
stl->g_data->wires_shgrp = DRW_shgroup_create(e_data.edit_wire_shader, psl->wires);
|
||||
DRW_shgroup_uniform_vec4(stl->g_data->wires_shgrp, "color", ts.colorWireEdit, 1);
|
||||
DRW_shgroup_uniform_vec4(stl->g_data->wires_shgrp, "colorSelect", ts.colorEdgeSelect, 1);
|
||||
}
|
||||
|
||||
{
|
||||
/* Tip vertices */
|
||||
DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_LESS | DRW_STATE_BLEND;
|
||||
psl->tips = DRW_pass_create("Strand Tip Verts Pass", state);
|
||||
|
||||
stl->g_data->tips_shgrp = DRW_shgroup_create(e_data.edit_point_shader, psl->tips);
|
||||
DRW_shgroup_uniform_vec4(stl->g_data->tips_shgrp, "color", ts.colorVertex, 1);
|
||||
DRW_shgroup_uniform_vec4(stl->g_data->tips_shgrp, "colorSelect", ts.colorVertexSelect, 1);
|
||||
DRW_shgroup_uniform_float(stl->g_data->tips_shgrp, "sizeVertex", &ts.sizeVertex, 1);
|
||||
}
|
||||
|
||||
{
|
||||
/* Root vertices */
|
||||
DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_LESS | DRW_STATE_BLEND;
|
||||
psl->roots = DRW_pass_create("Strand Root Verts Pass", state);
|
||||
|
||||
stl->g_data->roots_shgrp = DRW_shgroup_create(e_data.edit_point_shader, psl->roots);
|
||||
DRW_shgroup_uniform_vec4(stl->g_data->roots_shgrp, "color", ts.colorVertex, 1);
|
||||
DRW_shgroup_uniform_vec4(stl->g_data->roots_shgrp, "colorSelect", ts.colorVertexSelect, 1);
|
||||
DRW_shgroup_uniform_float(stl->g_data->roots_shgrp, "sizeVertex", &ts.sizeVertex, 1);
|
||||
}
|
||||
|
||||
{
|
||||
/* Interior vertices */
|
||||
DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_LESS | DRW_STATE_BLEND;
|
||||
psl->points = DRW_pass_create("Strand Interior Verts Pass", state);
|
||||
|
||||
stl->g_data->points_shgrp = DRW_shgroup_create(e_data.edit_point_shader, psl->points);
|
||||
DRW_shgroup_uniform_vec4(stl->g_data->points_shgrp, "color", ts.colorVertex, 1);
|
||||
DRW_shgroup_uniform_vec4(stl->g_data->points_shgrp, "colorSelect", ts.colorVertexSelect, 1);
|
||||
DRW_shgroup_uniform_float(stl->g_data->points_shgrp, "sizeVertex", &ts.sizeVertex, 1);
|
||||
}
|
||||
}
|
||||
|
||||
static void edit_strands_add_ob_to_pass(
|
||||
Scene *scene, Object *ob, BMEditStrands *edit,
|
||||
DRWShadingGroup *tips_shgrp,
|
||||
DRWShadingGroup *roots_shgrp,
|
||||
DRWShadingGroup *points_shgrp,
|
||||
DRWShadingGroup *wires_shgrp)
|
||||
{
|
||||
HairEditSettings *tsettings = &scene->toolsettings->hair_edit;
|
||||
|
||||
{
|
||||
struct Gwn_Batch *geom = DRW_cache_editstrands_get_wires(edit);
|
||||
DRW_shgroup_call_add(wires_shgrp, geom, ob->obmat);
|
||||
}
|
||||
|
||||
switch (tsettings->select_mode) {
|
||||
case HAIR_SELECT_TIP: {
|
||||
struct Gwn_Batch *geom = DRW_cache_editstrands_get_tips(edit);
|
||||
DRW_shgroup_call_add(tips_shgrp, geom, ob->obmat);
|
||||
break;
|
||||
}
|
||||
|
||||
case HAIR_SELECT_STRAND: {
|
||||
#if 0
|
||||
struct Gwn_Batch *geom = DRW_cache_editstrands_get_roots(edit);
|
||||
DRW_shgroup_call_add(roots_shgrp, geom, ob->obmat);
|
||||
#else
|
||||
UNUSED_VARS(roots_shgrp);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
|
||||
case HAIR_SELECT_VERTEX: {
|
||||
struct Gwn_Batch *geom = DRW_cache_editstrands_get_points(edit);
|
||||
DRW_shgroup_call_add(points_shgrp, geom, ob->obmat);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Add geometry to shadingGroups. Execute for each objects */
|
||||
static void EDIT_STRANDS_cache_populate(void *vedata, Object *ob)
|
||||
{
|
||||
EDIT_STRANDS_StorageList *stl = ((EDIT_STRANDS_Data *)vedata)->stl;
|
||||
|
||||
BMEditStrands *edit = BKE_editstrands_from_object(ob);
|
||||
const DRWContextState *draw_ctx = DRW_context_state_get();
|
||||
Scene *scene = draw_ctx->scene;
|
||||
|
||||
// Don't draw strands while editing the object itself
|
||||
if (ob == scene->obedit)
|
||||
return;
|
||||
|
||||
if (edit) {
|
||||
edit_strands_add_ob_to_pass(scene, ob, edit,
|
||||
stl->g_data->tips_shgrp, stl->g_data->roots_shgrp,
|
||||
stl->g_data->points_shgrp, stl->g_data->wires_shgrp);
|
||||
}
|
||||
}
|
||||
|
||||
/* Optional: Post-cache_populate callback */
|
||||
static void EDIT_STRANDS_cache_finish(void *vedata)
|
||||
{
|
||||
EDIT_STRANDS_PassList *psl = ((EDIT_STRANDS_Data *)vedata)->psl;
|
||||
EDIT_STRANDS_StorageList *stl = ((EDIT_STRANDS_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_STRANDS_draw_scene(void *vedata)
|
||||
{
|
||||
EDIT_STRANDS_PassList *psl = ((EDIT_STRANDS_Data *)vedata)->psl;
|
||||
|
||||
DRW_draw_pass(psl->wires);
|
||||
DRW_draw_pass(psl->points);
|
||||
DRW_draw_pass(psl->roots);
|
||||
DRW_draw_pass(psl->tips);
|
||||
|
||||
/* If you changed framebuffer, double check you rebind
|
||||
* the default one with its textures attached before finishing */
|
||||
}
|
||||
|
||||
static const DrawEngineDataSize STRANDS_data_size = DRW_VIEWPORT_DATA_SIZE(EDIT_STRANDS_Data);
|
||||
|
||||
DrawEngineType draw_engine_edit_strands_type = {
|
||||
NULL, NULL,
|
||||
N_("EditStrandsMode"),
|
||||
&STRANDS_data_size,
|
||||
EDIT_STRANDS_engine_init,
|
||||
EDIT_STRANDS_engine_free,
|
||||
&EDIT_STRANDS_cache_init,
|
||||
&EDIT_STRANDS_cache_populate,
|
||||
&EDIT_STRANDS_cache_finish,
|
||||
NULL, /* draw_background but not needed by mode engines */
|
||||
&EDIT_STRANDS_draw_scene
|
||||
};
|
@@ -1674,6 +1674,8 @@ static void OBJECT_cache_populate(void *vedata, Object *ob)
|
||||
SceneLayer *sl = draw_ctx->scene_layer;
|
||||
View3D *v3d = draw_ctx->v3d;
|
||||
int theme_id = TH_UNDEFINED;
|
||||
const bool is_edited = (ob == scene->obedit) ||
|
||||
((ob == draw_ctx->obact) && (ob->mode & OB_MODE_ALL_BRUSH));
|
||||
|
||||
//CollectionEngineSettings *ces_mode_ob = BKE_layer_collection_engine_evaluated_get(ob, COLLECTION_MODE_OBJECT, "");
|
||||
|
||||
@@ -1681,8 +1683,7 @@ static void OBJECT_cache_populate(void *vedata, Object *ob)
|
||||
bool do_outlines = ((ob->base_flag & BASE_SELECTED) != 0);
|
||||
|
||||
if (do_outlines) {
|
||||
Object *obedit = scene->obedit;
|
||||
if (ob != obedit && !((ob == draw_ctx->obact) && (ob->mode & OB_MODE_ALL_PAINT))) {
|
||||
if (!is_edited) {
|
||||
struct Gwn_Batch *geom = DRW_cache_object_surface_get(ob);
|
||||
if (geom) {
|
||||
theme_id = DRW_object_wire_theme_get(ob, sl, NULL);
|
||||
@@ -1699,8 +1700,7 @@ static void OBJECT_cache_populate(void *vedata, Object *ob)
|
||||
{
|
||||
Mesh *me = ob->data;
|
||||
if (me->totpoly == 0) {
|
||||
Object *obedit = scene->obedit;
|
||||
if (ob != obedit) {
|
||||
if (!is_edited) {
|
||||
struct Gwn_Batch *geom = DRW_cache_mesh_edges_get(ob);
|
||||
if (geom) {
|
||||
if (theme_id == TH_UNDEFINED) {
|
||||
@@ -1720,8 +1720,7 @@ static void OBJECT_cache_populate(void *vedata, Object *ob)
|
||||
break;
|
||||
case OB_LATTICE:
|
||||
{
|
||||
Object *obedit = scene->obedit;
|
||||
if (ob != obedit) {
|
||||
if (!is_edited) {
|
||||
struct Gwn_Batch *geom = DRW_cache_lattice_wire_get(ob, false);
|
||||
if (theme_id == TH_UNDEFINED) {
|
||||
theme_id = DRW_object_wire_theme_get(ob, sl, NULL);
|
||||
@@ -1735,8 +1734,7 @@ static void OBJECT_cache_populate(void *vedata, Object *ob)
|
||||
|
||||
case OB_CURVE:
|
||||
{
|
||||
Object *obedit = scene->obedit;
|
||||
if (ob != obedit) {
|
||||
if (!is_edited) {
|
||||
struct Gwn_Batch *geom = DRW_cache_curve_edge_wire_get(ob);
|
||||
if (theme_id == TH_UNDEFINED) {
|
||||
theme_id = DRW_object_wire_theme_get(ob, sl, NULL);
|
||||
|
28
source/blender/draw/modes/shaders/edit_strands_vert.glsl
Normal file
@@ -0,0 +1,28 @@
|
||||
|
||||
/* Draw Curve Vertices */
|
||||
|
||||
uniform mat4 ModelViewProjectionMatrix;
|
||||
uniform vec2 viewportSize;
|
||||
uniform vec4 color;
|
||||
uniform vec4 colorSelect;
|
||||
uniform float sizeVertex;
|
||||
|
||||
in vec3 pos;
|
||||
in int flag;
|
||||
|
||||
out vec4 finalColor;
|
||||
|
||||
#define VERTEX_SELECTED (1 << 0)
|
||||
|
||||
void main()
|
||||
{
|
||||
gl_Position = ModelViewProjectionMatrix * vec4(pos, 1.0);
|
||||
gl_PointSize = sizeVertex;
|
||||
|
||||
if ((flag & VERTEX_SELECTED) != 0) {
|
||||
finalColor = colorSelect;
|
||||
}
|
||||
else {
|
||||
finalColor = color;
|
||||
}
|
||||
}
|
@@ -27,6 +27,7 @@ if(WITH_BLENDER)
|
||||
add_subdirectory(armature)
|
||||
add_subdirectory(curve)
|
||||
add_subdirectory(gpencil)
|
||||
add_subdirectory(hair)
|
||||
add_subdirectory(interface)
|
||||
add_subdirectory(io)
|
||||
add_subdirectory(manipulator_library)
|
||||
|
@@ -833,7 +833,7 @@ static void *get_armature_edit(bContext *C)
|
||||
void undo_push_armature(bContext *C, const char *name)
|
||||
{
|
||||
// XXX solve getdata()
|
||||
undo_editmode_push(C, name, get_armature_edit, free_undoBones, undoBones_to_editBones, editBones_to_undoBones, NULL);
|
||||
undo_editmode_push(C, name, CTX_data_edit_object, get_armature_edit, free_undoBones, undoBones_to_editBones, editBones_to_undoBones, NULL);
|
||||
}
|
||||
|
||||
/* *************************************************************** */
|
||||
|
@@ -6227,7 +6227,7 @@ static void *get_data(bContext *C)
|
||||
/* and this is all the undo system needs to know */
|
||||
void undo_push_curve(bContext *C, const char *name)
|
||||
{
|
||||
undo_editmode_push(C, name, get_data, free_undoCurve, undoCurve_to_editCurve, editCurve_to_undoCurve, NULL);
|
||||
undo_editmode_push(C, name, CTX_data_edit_object, get_data, free_undoCurve, undoCurve_to_editCurve, editCurve_to_undoCurve, NULL);
|
||||
}
|
||||
|
||||
void ED_curve_beztcpy(EditNurb *editnurb, BezTriple *dst, BezTriple *src, int count)
|
||||
|
@@ -307,5 +307,5 @@ static void *get_undoFont(bContext *C)
|
||||
/* and this is all the undo system needs to know */
|
||||
void undo_push_font(bContext *C, const char *name)
|
||||
{
|
||||
undo_editmode_push(C, name, get_undoFont, free_undoFont, undoFont_to_editFont, editFont_to_undoFont, NULL);
|
||||
undo_editmode_push(C, name, CTX_data_edit_object, get_undoFont, free_undoFont, undoFont_to_editFont, editFont_to_undoFont, NULL);
|
||||
}
|
||||
|
@@ -78,6 +78,13 @@ if(WITH_BLENDER)
|
||||
data_to_c_simple(../../../../release/datafiles/brushicons/fill.png SRC)
|
||||
data_to_c_simple(../../../../release/datafiles/brushicons/flatten.png SRC)
|
||||
data_to_c_simple(../../../../release/datafiles/brushicons/grab.png SRC)
|
||||
data_to_c_simple(../../../../release/datafiles/brushicons/hairadd.png SRC)
|
||||
data_to_c_simple(../../../../release/datafiles/brushicons/haircomb.png SRC)
|
||||
data_to_c_simple(../../../../release/datafiles/brushicons/haircut.png SRC)
|
||||
data_to_c_simple(../../../../release/datafiles/brushicons/hairlength.png SRC)
|
||||
data_to_c_simple(../../../../release/datafiles/brushicons/hairpuff.png SRC)
|
||||
data_to_c_simple(../../../../release/datafiles/brushicons/hairsmooth.png SRC)
|
||||
data_to_c_simple(../../../../release/datafiles/brushicons/hairweight.png SRC)
|
||||
data_to_c_simple(../../../../release/datafiles/brushicons/inflate.png SRC)
|
||||
data_to_c_simple(../../../../release/datafiles/brushicons/layer.png SRC)
|
||||
data_to_c_simple(../../../../release/datafiles/brushicons/lighten.png SRC)
|
||||
|
57
source/blender/editors/hair/CMakeLists.txt
Normal file
@@ -0,0 +1,57 @@
|
||||
# ***** BEGIN GPL LICENSE BLOCK *****
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software Foundation,
|
||||
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
#
|
||||
# Contributor(s): Jacques Beaurain.
|
||||
#
|
||||
# ***** END GPL LICENSE BLOCK *****
|
||||
|
||||
set(INC
|
||||
../include
|
||||
../sculpt_paint
|
||||
../../blenfont
|
||||
../../blenkernel
|
||||
../../blenlib
|
||||
../../bmesh
|
||||
../../depsgraph
|
||||
../../gpu
|
||||
../../makesdna
|
||||
../../makesrna
|
||||
../../windowmanager
|
||||
../../../../intern/guardedalloc
|
||||
../../../../intern/glew-mx
|
||||
)
|
||||
|
||||
set(INC_SYS
|
||||
${GLEW_INCLUDE_PATH}
|
||||
)
|
||||
|
||||
set(SRC
|
||||
hair_cursor.c
|
||||
hair_edit.c
|
||||
hair_mirror.c
|
||||
hair_object_mesh.c
|
||||
hair_object_particles.c
|
||||
hair_ops.c
|
||||
hair_select.c
|
||||
hair_stroke.c
|
||||
hair_undo.c
|
||||
|
||||
hair_intern.h
|
||||
)
|
||||
|
||||
add_definitions(${GL_DEFINITIONS})
|
||||
|
||||
blender_add_lib(bf_editor_hair "${SRC}" "${INC}" "${INC_SYS}")
|
54
source/blender/editors/hair/SConscript
Normal file
@@ -0,0 +1,54 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# ***** BEGIN GPL LICENSE BLOCK *****
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software Foundation,
|
||||
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
#
|
||||
# The Original Code is Copyright (C) 2006, Blender Foundation
|
||||
# All rights reserved.
|
||||
#
|
||||
# The Original Code is: all of this file.
|
||||
#
|
||||
# Contributor(s): Nathan Letwory.
|
||||
#
|
||||
# ***** END GPL LICENSE BLOCK *****
|
||||
|
||||
Import ('env')
|
||||
|
||||
sources = env.Glob('*.c')
|
||||
|
||||
incs = [
|
||||
'#/intern/guardedalloc',
|
||||
env['BF_GLEW_INC'],
|
||||
'#/intern/glew-mx',
|
||||
'../include',
|
||||
'../sculpt_paint',
|
||||
'../../blenfont',
|
||||
'../../blenkernel',
|
||||
'../../blenlib',
|
||||
'../../bmesh',
|
||||
'../../gpu',
|
||||
'../../makesdna',
|
||||
'../../makesrna',
|
||||
'../../windowmanager',
|
||||
]
|
||||
incs = ' '.join(incs)
|
||||
|
||||
defs = ['BF_GL_DEFINITIONS']
|
||||
|
||||
if env['OURPLATFORM'] in ('win32-vc', 'win32-mingw', 'linuxcross', 'win64-vc', 'win64-mingw'):
|
||||
incs += ' ' + env['BF_PTHREADS_INC']
|
||||
|
||||
env.BlenderLib ( 'bf_editors_hair', sources, Split(incs), defs, libtype=['core'], priority=[45] )
|
100
source/blender/editors/hair/hair_cursor.c
Normal file
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
* ***** BEGIN GPL LICENSE BLOCK *****
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* The Original Code is Copyright (C) Blender Foundation
|
||||
* All rights reserved.
|
||||
*
|
||||
* The Original Code is: all of this file.
|
||||
*
|
||||
* Contributor(s): Lukas Toenne
|
||||
*
|
||||
* ***** END GPL LICENSE BLOCK *****
|
||||
*/
|
||||
|
||||
/** \file blender/editors/hair/hair_cursor.c
|
||||
* \ingroup edhair
|
||||
*/
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "BLI_utildefines.h"
|
||||
#include "BLI_math.h"
|
||||
|
||||
#include "DNA_brush_types.h"
|
||||
#include "DNA_scene_types.h"
|
||||
#include "DNA_screen_types.h"
|
||||
#include "DNA_view3d_types.h"
|
||||
|
||||
#include "BKE_brush.h"
|
||||
#include "BKE_context.h"
|
||||
|
||||
#include "GPU_immediate.h"
|
||||
#include "GPU_immediate_util.h"
|
||||
|
||||
#include "WM_api.h"
|
||||
#include "WM_types.h"
|
||||
|
||||
#include "BIF_gl.h"
|
||||
#include "BIF_glutil.h"
|
||||
|
||||
#include "ED_view3d.h"
|
||||
|
||||
#include "hair_intern.h"
|
||||
|
||||
static void hair_draw_cursor(bContext *C, int x, int y, void *UNUSED(customdata))
|
||||
{
|
||||
Scene *scene = CTX_data_scene(C);
|
||||
UnifiedPaintSettings *ups = &scene->toolsettings->unified_paint_settings;
|
||||
HairEditSettings *settings = &scene->toolsettings->hair_edit;
|
||||
Brush *brush = settings->brush;
|
||||
if (!brush)
|
||||
return;
|
||||
|
||||
float final_radius = BKE_brush_size_get(scene, brush);
|
||||
float col[4];
|
||||
|
||||
/* set various defaults */
|
||||
copy_v3_v3(col, brush->add_col);
|
||||
col[3] = 0.5f;
|
||||
|
||||
Gwn_VertFormat *format = immVertexFormat();
|
||||
uint pos = GWN_vertformat_attr_add(format, "pos", GWN_COMP_F32, 2, GWN_FETCH_FLOAT);
|
||||
|
||||
immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
|
||||
|
||||
immUniformColor4fv(col);
|
||||
|
||||
/* draw an inner brush */
|
||||
if (ups->stroke_active && BKE_brush_use_size_pressure(scene, brush)) {
|
||||
/* inner at full alpha */
|
||||
imm_draw_circle_wire(pos, x, y, final_radius * ups->size_pressure_value, 40);
|
||||
/* outer at half alpha */
|
||||
immUniformColor3fvAlpha(col, col[3]*0.5f);
|
||||
}
|
||||
imm_draw_circle_wire(pos, x, y, final_radius, 40);
|
||||
|
||||
immUnbindProgram();
|
||||
}
|
||||
|
||||
void hair_edit_cursor_start(bContext *C, int (*poll)(bContext *C))
|
||||
{
|
||||
Scene *scene = CTX_data_scene(C);
|
||||
HairEditSettings *settings = &scene->toolsettings->hair_edit;
|
||||
|
||||
if (!settings->paint_cursor)
|
||||
settings->paint_cursor = WM_paint_cursor_activate(CTX_wm_manager(C), poll, hair_draw_cursor, NULL);
|
||||
}
|
465
source/blender/editors/hair/hair_edit.c
Normal file
@@ -0,0 +1,465 @@
|
||||
/*
|
||||
* ***** BEGIN GPL LICENSE BLOCK *****
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* The Original Code is Copyright (C) Blender Foundation
|
||||
* All rights reserved.
|
||||
*
|
||||
* The Original Code is: all of this file.
|
||||
*
|
||||
* Contributor(s): Lukas Toenne
|
||||
*
|
||||
* ***** END GPL LICENSE BLOCK *****
|
||||
*/
|
||||
|
||||
/** \file blender/editors/hair/hair_edit.c
|
||||
* \ingroup edhair
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "BLI_utildefines.h"
|
||||
#include "BLI_math.h"
|
||||
|
||||
#include "DNA_brush_types.h"
|
||||
#include "DNA_mesh_types.h"
|
||||
#include "DNA_object_types.h"
|
||||
#include "DNA_scene_types.h"
|
||||
#include "DNA_screen_types.h"
|
||||
#include "DNA_view3d_types.h"
|
||||
|
||||
#include "BKE_brush.h"
|
||||
#include "BKE_cdderivedmesh.h"
|
||||
#include "BKE_context.h"
|
||||
#include "BKE_DerivedMesh.h"
|
||||
#include "BKE_editstrands.h"
|
||||
#include "BKE_paint.h"
|
||||
|
||||
#include "DEG_depsgraph.h"
|
||||
|
||||
#include "bmesh.h"
|
||||
|
||||
#include "RNA_access.h"
|
||||
#include "RNA_define.h"
|
||||
|
||||
#include "WM_api.h"
|
||||
#include "WM_types.h"
|
||||
|
||||
#include "ED_object.h"
|
||||
#include "ED_view3d.h"
|
||||
|
||||
#include "hair_intern.h"
|
||||
#include "paint_intern.h"
|
||||
|
||||
#define USE_PARTICLES 1
|
||||
|
||||
int hair_edit_poll(bContext *C)
|
||||
{
|
||||
Object *obact;
|
||||
|
||||
obact = CTX_data_active_object(C);
|
||||
if ((obact && obact->mode & OB_MODE_HAIR_EDIT) && CTX_wm_region_view3d(C)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool hair_use_mirror_x(Object *ob)
|
||||
{
|
||||
if (ob->type == OB_MESH)
|
||||
return ((Mesh *)ob->data)->editflag & ME_EDIT_MIRROR_X;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
bool hair_use_mirror_topology(Object *ob)
|
||||
{
|
||||
if (ob->type == OB_MESH)
|
||||
return ((Mesh *)ob->data)->editflag & ME_EDIT_MIRROR_TOPO;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/* ==== BMesh utilities ==== */
|
||||
|
||||
void hair_bm_min_max(BMEditStrands *edit, float min[3], float max[3])
|
||||
{
|
||||
BMesh *bm = edit->base.bm;
|
||||
BMVert *v;
|
||||
BMIter iter;
|
||||
|
||||
if (bm->totvert > 0) {
|
||||
INIT_MINMAX(min, max);
|
||||
|
||||
BM_ITER_MESH(v, &iter, bm, BM_VERTS_OF_MESH) {
|
||||
minmax_v3v3_v3(min, max, v->co);
|
||||
}
|
||||
}
|
||||
else {
|
||||
zero_v3(min);
|
||||
zero_v3(max);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* ==== edit mode toggle ==== */
|
||||
|
||||
int hair_edit_toggle_poll(bContext *C)
|
||||
{
|
||||
Object *ob = CTX_data_active_object(C);
|
||||
|
||||
if (ob == NULL)
|
||||
return false;
|
||||
if (!ob->data || ((ID *)ob->data)->lib)
|
||||
return false;
|
||||
if (CTX_data_edit_object(C))
|
||||
return false;
|
||||
|
||||
#if USE_PARTICLES
|
||||
return ED_hair_object_has_hair_particle_data(ob);
|
||||
#else
|
||||
return ob->type == OB_MESH;
|
||||
#endif
|
||||
}
|
||||
|
||||
static void toggle_hair_cursor(bContext *C, bool enable)
|
||||
{
|
||||
wmWindowManager *wm = CTX_wm_manager(C);
|
||||
Scene *scene = CTX_data_scene(C);
|
||||
HairEditSettings *settings = &scene->toolsettings->hair_edit;
|
||||
|
||||
if (enable) {
|
||||
hair_edit_cursor_start(C, hair_edit_toggle_poll);
|
||||
}
|
||||
else {
|
||||
if (settings->paint_cursor) {
|
||||
WM_paint_cursor_end(wm, settings->paint_cursor);
|
||||
settings->paint_cursor = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int hair_edit_toggle_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
Scene *scene = CTX_data_scene(C);
|
||||
Object *ob = CTX_data_active_object(C);
|
||||
const int mode_flag = OB_MODE_HAIR_EDIT;
|
||||
const bool is_mode_set = (ob->mode & mode_flag) != 0;
|
||||
EvaluationContext eval_ctx;
|
||||
CTX_data_eval_ctx(C, &eval_ctx);
|
||||
|
||||
if (!is_mode_set) {
|
||||
if (!ED_object_mode_compat_set(C, ob, mode_flag, op->reports)) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
}
|
||||
|
||||
if (!is_mode_set) {
|
||||
#if USE_PARTICLES
|
||||
ED_hair_object_init_particle_edit(&eval_ctx, scene, ob);
|
||||
#else
|
||||
ED_hair_object_init_mesh_edit(&eval_ctx, scene, ob);
|
||||
#endif
|
||||
ob->mode |= mode_flag;
|
||||
|
||||
toggle_hair_cursor(C, true);
|
||||
WM_event_add_notifier(C, NC_SCENE|ND_MODE|NS_MODE_HAIR, NULL);
|
||||
}
|
||||
else {
|
||||
#if USE_PARTICLES
|
||||
ED_hair_object_apply_particle_edit(ob);
|
||||
#else
|
||||
ED_hair_object_apply_mesh_edit(ob);
|
||||
#endif
|
||||
ob->mode &= ~mode_flag;
|
||||
|
||||
toggle_hair_cursor(C, false);
|
||||
WM_event_add_notifier(C, NC_SCENE|ND_MODE|NS_MODE_HAIR, NULL);
|
||||
}
|
||||
|
||||
DEG_id_tag_update(&ob->id, OB_RECALC_DATA);
|
||||
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
void HAIR_OT_hair_edit_toggle(wmOperatorType *ot)
|
||||
{
|
||||
/* identifiers */
|
||||
ot->name = "Hair Edit Toggle";
|
||||
ot->idname = "HAIR_OT_hair_edit_toggle";
|
||||
ot->description = "Toggle hair edit mode";
|
||||
|
||||
/* api callbacks */
|
||||
ot->exec = hair_edit_toggle_exec;
|
||||
ot->poll = hair_edit_toggle_poll;
|
||||
|
||||
/* flags */
|
||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
||||
}
|
||||
|
||||
|
||||
/* ==== brush stroke ==== */
|
||||
|
||||
void hair_init_viewcontext(bContext *C, ViewContext *vc)
|
||||
{
|
||||
View3D *v3d;
|
||||
bool has_zbuf;
|
||||
|
||||
view3d_set_viewcontext(C, vc);
|
||||
|
||||
v3d = vc->v3d;
|
||||
has_zbuf = (v3d->drawtype > OB_WIRE) && (v3d->flag & V3D_ZBUF_SELECT);
|
||||
|
||||
if (has_zbuf) {
|
||||
if (v3d->flag & V3D_INVALID_BACKBUF) {
|
||||
/* needed or else the draw matrix can be incorrect */
|
||||
view3d_operator_needs_opengl(C);
|
||||
|
||||
ED_view3d_backbuf_validate(C, vc);
|
||||
/* we may need to force an update here by setting the rv3d as dirty
|
||||
* for now it seems ok, but take care!:
|
||||
* rv3d->depths->dirty = 1; */
|
||||
ED_view3d_depth_update(vc->ar);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
typedef struct HairStroke {
|
||||
Scene *scene;
|
||||
Object *ob;
|
||||
BMEditStrands *edit;
|
||||
|
||||
bool first;
|
||||
float lastmouse[2];
|
||||
float zfac;
|
||||
|
||||
float smoothdir[2];
|
||||
} HairStroke;
|
||||
|
||||
static int hair_stroke_init(bContext *C, wmOperator *op)
|
||||
{
|
||||
Scene *scene = CTX_data_scene(C);
|
||||
Object *ob = CTX_data_active_object(C);
|
||||
BMEditStrands *edit = BKE_editstrands_from_object(ob);
|
||||
ARegion *ar = CTX_wm_region(C);
|
||||
|
||||
HairStroke *stroke;
|
||||
float min[3], max[3], center[3];
|
||||
|
||||
/* set the 'distance factor' for grabbing (used in comb etc) */
|
||||
hair_bm_min_max(edit, min, max);
|
||||
mid_v3_v3v3(center, min, max);
|
||||
|
||||
stroke = MEM_callocN(sizeof(HairStroke), "HairStroke");
|
||||
stroke->first = true;
|
||||
op->customdata = stroke;
|
||||
|
||||
stroke->scene = scene;
|
||||
stroke->ob = ob;
|
||||
stroke->edit = edit;
|
||||
|
||||
stroke->zfac = ED_view3d_calc_zfac(ar->regiondata, center, NULL);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static bool hair_stroke_apply(bContext *C, wmOperator *op, PointerRNA *itemptr)
|
||||
{
|
||||
HairStroke *stroke = op->customdata;
|
||||
Scene *scene = stroke->scene;
|
||||
Object *ob = stroke->ob;
|
||||
BMEditStrands *edit = stroke->edit;
|
||||
HairEditSettings *settings = &scene->toolsettings->hair_edit;
|
||||
ARegion *ar = CTX_wm_region(C);
|
||||
const float smoothfac = 0.9f; /* XXX should this be configurable? */
|
||||
|
||||
float mouse[2], mdelta[2], zvec[3], delta_max;
|
||||
int totsteps, step;
|
||||
HairToolData tool_data;
|
||||
bool updated = false;
|
||||
|
||||
RNA_float_get_array(itemptr, "mouse", mouse);
|
||||
|
||||
if (stroke->first) {
|
||||
copy_v2_v2(stroke->lastmouse, mouse);
|
||||
zero_v2(stroke->smoothdir);
|
||||
stroke->first = false;
|
||||
}
|
||||
|
||||
if (!settings->brush)
|
||||
return false;
|
||||
|
||||
sub_v2_v2v2(mdelta, mouse, stroke->lastmouse);
|
||||
delta_max = max_ff(fabsf(mdelta[0]), fabsf(mdelta[1]));
|
||||
|
||||
totsteps = delta_max / (0.2f * BKE_brush_size_get(scene, settings->brush)) + 1;
|
||||
mul_v2_fl(mdelta, 1.0f / (float)totsteps);
|
||||
|
||||
/* low-pass filter to smooth out jittery pixel increments in the direction */
|
||||
interp_v2_v2v2(stroke->smoothdir, mdelta, stroke->smoothdir, smoothfac);
|
||||
|
||||
hair_init_viewcontext(C, &tool_data.vc);
|
||||
tool_data.scene = scene;
|
||||
tool_data.ob = ob;
|
||||
tool_data.edit = edit;
|
||||
tool_data.settings = settings;
|
||||
|
||||
invert_m4_m4(tool_data.imat, ob->obmat);
|
||||
copy_v2_v2(tool_data.mval, mouse);
|
||||
tool_data.mdepth = stroke->zfac;
|
||||
|
||||
zvec[0] = 0.0f; zvec[1] = 0.0f; zvec[2] = stroke->zfac;
|
||||
ED_view3d_win_to_3d(tool_data.vc.v3d, ar, zvec, mouse, tool_data.loc);
|
||||
ED_view3d_win_to_delta(ar, stroke->smoothdir, tool_data.delta, stroke->zfac);
|
||||
/* tools work in object space */
|
||||
mul_m4_v3(tool_data.imat, tool_data.loc);
|
||||
mul_mat3_m4_v3(tool_data.imat, tool_data.delta);
|
||||
|
||||
for (step = 0; step < totsteps; ++step) {
|
||||
bool step_updated = hair_brush_step(&tool_data);
|
||||
|
||||
if (step_updated)
|
||||
BKE_editstrands_solve_constraints(ob, edit, NULL);
|
||||
|
||||
updated |= step_updated;
|
||||
}
|
||||
|
||||
copy_v2_v2(stroke->lastmouse, mouse);
|
||||
|
||||
return updated;
|
||||
}
|
||||
|
||||
static void hair_stroke_exit(wmOperator *op)
|
||||
{
|
||||
HairStroke *stroke = op->customdata;
|
||||
MEM_freeN(stroke);
|
||||
}
|
||||
|
||||
static int hair_stroke_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
HairStroke *stroke = op->customdata;
|
||||
Object *ob = stroke->ob;
|
||||
|
||||
bool updated = false;
|
||||
|
||||
if (!hair_stroke_init(C, op))
|
||||
return OPERATOR_CANCELLED;
|
||||
|
||||
RNA_BEGIN (op->ptr, itemptr, "stroke")
|
||||
{
|
||||
updated |= hair_stroke_apply(C, op, &itemptr);
|
||||
}
|
||||
RNA_END;
|
||||
|
||||
if (updated) {
|
||||
DEG_id_tag_update(&ob->id, OB_RECALC_DATA);
|
||||
WM_event_add_notifier(C, NC_OBJECT|ND_MODIFIER, ob);
|
||||
}
|
||||
|
||||
hair_stroke_exit(op);
|
||||
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
static void hair_stroke_apply_event(bContext *C, wmOperator *op, const wmEvent *event)
|
||||
{
|
||||
HairStroke *stroke = op->customdata;
|
||||
Object *ob = stroke->ob;
|
||||
|
||||
PointerRNA itemptr;
|
||||
float mouse[2];
|
||||
bool updated = false;
|
||||
|
||||
mouse[0] = event->mval[0];
|
||||
mouse[1] = event->mval[1];
|
||||
|
||||
/* fill in stroke */
|
||||
RNA_collection_add(op->ptr, "stroke", &itemptr);
|
||||
|
||||
RNA_float_set_array(&itemptr, "mouse", mouse);
|
||||
|
||||
/* apply */
|
||||
updated |= hair_stroke_apply(C, op, &itemptr);
|
||||
|
||||
if (updated) {
|
||||
DEG_id_tag_update(&ob->id, OB_RECALC_DATA);
|
||||
WM_event_add_notifier(C, NC_OBJECT|ND_MODIFIER, ob);
|
||||
}
|
||||
else {
|
||||
/* even if nothing was changed, still trigger redraw
|
||||
* for brush drawing during the modal operator
|
||||
*/
|
||||
WM_event_add_notifier(C, NC_OBJECT|ND_DRAW, ob);
|
||||
}
|
||||
}
|
||||
|
||||
static int hair_stroke_invoke(bContext *C, wmOperator *op, const wmEvent *event)
|
||||
{
|
||||
if (!hair_stroke_init(C, op))
|
||||
return OPERATOR_CANCELLED;
|
||||
|
||||
hair_stroke_apply_event(C, op, event);
|
||||
|
||||
WM_event_add_modal_handler(C, op);
|
||||
|
||||
return OPERATOR_RUNNING_MODAL;
|
||||
}
|
||||
|
||||
static int hair_stroke_modal(bContext *C, wmOperator *op, const wmEvent *event)
|
||||
{
|
||||
switch (event->type) {
|
||||
case LEFTMOUSE:
|
||||
case MIDDLEMOUSE:
|
||||
case RIGHTMOUSE: // XXX hardcoded
|
||||
hair_stroke_exit(op);
|
||||
return OPERATOR_FINISHED;
|
||||
case MOUSEMOVE:
|
||||
hair_stroke_apply_event(C, op, event);
|
||||
break;
|
||||
}
|
||||
|
||||
return OPERATOR_RUNNING_MODAL;
|
||||
}
|
||||
|
||||
static void hair_stroke_cancel(bContext *UNUSED(C), wmOperator *op)
|
||||
{
|
||||
hair_stroke_exit(op);
|
||||
}
|
||||
|
||||
void HAIR_OT_stroke(wmOperatorType *ot)
|
||||
{
|
||||
/* identifiers */
|
||||
ot->name = "Hair Stroke";
|
||||
ot->idname = "HAIR_OT_stroke";
|
||||
ot->description = "Use a stroke tool on hair strands";
|
||||
|
||||
/* api callbacks */
|
||||
ot->exec = hair_stroke_exec;
|
||||
ot->invoke = hair_stroke_invoke;
|
||||
ot->modal = hair_stroke_modal;
|
||||
ot->cancel = hair_stroke_cancel;
|
||||
ot->poll = hair_edit_poll;
|
||||
|
||||
/* flags */
|
||||
ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO|OPTYPE_BLOCKING;
|
||||
|
||||
/* properties */
|
||||
RNA_def_collection_runtime(ot->srna, "stroke", &RNA_OperatorStrokeElement, "Stroke", "");
|
||||
}
|
115
source/blender/editors/hair/hair_intern.h
Normal file
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
* ***** BEGIN GPL LICENSE BLOCK *****
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* The Original Code is Copyright (C) Blender Foundation
|
||||
* All rights reserved.
|
||||
*
|
||||
* The Original Code is: all of this file.
|
||||
*
|
||||
* Contributor(s): Lukas Toenne
|
||||
*
|
||||
* ***** END GPL LICENSE BLOCK *****
|
||||
*/
|
||||
|
||||
/** \file blender/editors/hair/hair_intern.h
|
||||
* \ingroup edhair
|
||||
*/
|
||||
|
||||
#ifndef __HAIR_INTERN_H__
|
||||
#define __HAIR_INTERN_H__
|
||||
|
||||
#include "BIF_glutil.h"
|
||||
|
||||
#include "ED_view3d.h"
|
||||
|
||||
struct ARegion;
|
||||
struct bContext;
|
||||
struct wmOperatorType;
|
||||
struct rcti;
|
||||
struct EvaluationContext;
|
||||
|
||||
struct Object;
|
||||
|
||||
/* hair_edit.c */
|
||||
bool hair_use_mirror_x(struct Object *ob);
|
||||
bool hair_use_mirror_topology(struct Object *ob);
|
||||
|
||||
int hair_edit_toggle_poll(struct bContext *C);
|
||||
int hair_edit_poll(struct bContext *C);
|
||||
|
||||
void HAIR_OT_hair_edit_toggle(struct wmOperatorType *ot);
|
||||
|
||||
/* hair_select.c */
|
||||
void HAIR_OT_select_all(struct wmOperatorType *ot);
|
||||
void HAIR_OT_select_linked(struct wmOperatorType *ot);
|
||||
|
||||
/* hair_stroke.c */
|
||||
void HAIR_OT_stroke(struct wmOperatorType *ot);
|
||||
|
||||
/* hair_object_mesh.c */
|
||||
bool ED_hair_object_init_mesh_edit(struct EvaluationContext *eval_ctx, struct Scene *scene, struct Object *ob);
|
||||
bool ED_hair_object_apply_mesh_edit(struct Object *ob);
|
||||
|
||||
/* hair_object_particles.c */
|
||||
bool ED_hair_object_has_hair_particle_data(struct Object *ob);
|
||||
bool ED_hair_object_init_particle_edit(struct EvaluationContext *eval_ctx, struct Scene *scene, struct Object *ob);
|
||||
bool ED_hair_object_apply_particle_edit(struct Object *ob);
|
||||
|
||||
|
||||
/* ==== Hair Brush ==== */
|
||||
|
||||
void hair_init_viewcontext(struct bContext *C, struct ViewContext *vc);
|
||||
|
||||
bool hair_test_depth(struct ViewContext *vc, const float co[3], const int screen_co[2]);
|
||||
bool hair_test_vertex_inside_circle(struct ViewContext *vc, const float mval[2], float radsq,
|
||||
struct BMVert *v, float *r_dist);
|
||||
bool hair_test_edge_inside_circle(struct ViewContext *vc, const float mval[2], float radsq,
|
||||
struct BMVert *v1, struct BMVert *v2, float *r_dist, float *r_lambda);
|
||||
bool hair_test_vertex_inside_rect(struct ViewContext *vc, struct rcti *rect, struct BMVert *v);
|
||||
bool hair_test_vertex_inside_lasso(struct ViewContext *vc, const int mcoords[][2], short moves, struct BMVert *v);
|
||||
|
||||
typedef struct HairToolData {
|
||||
/* context */
|
||||
struct Scene *scene;
|
||||
struct Object *ob;
|
||||
struct BMEditStrands *edit;
|
||||
struct HairEditSettings *settings;
|
||||
ViewContext vc;
|
||||
|
||||
/* view space */
|
||||
float mval[2]; /* mouse coordinates */
|
||||
float mdepth; /* mouse z depth */
|
||||
|
||||
/* object space */
|
||||
float imat[4][4]; /* obmat inverse */
|
||||
float loc[3]; /* start location */
|
||||
float delta[3]; /* stroke step */
|
||||
} HairToolData;
|
||||
|
||||
bool hair_brush_step(struct HairToolData *data);
|
||||
|
||||
/* ==== Cursor ==== */
|
||||
|
||||
void hair_edit_cursor_start(struct bContext *C, int (*poll)(struct bContext *C));
|
||||
|
||||
/* ==== BMesh utilities ==== */
|
||||
|
||||
struct BMEditStrands;
|
||||
|
||||
void hair_bm_min_max(struct BMEditStrands *edit, float min[3], float max[3]);
|
||||
|
||||
#endif
|
217
source/blender/editors/hair/hair_mirror.c
Normal file
@@ -0,0 +1,217 @@
|
||||
/*
|
||||
* ***** BEGIN GPL LICENSE BLOCK *****
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* The Original Code is Copyright (C) Blender Foundation
|
||||
* All rights reserved.
|
||||
*
|
||||
* The Original Code is: all of this file.
|
||||
*
|
||||
* Contributor(s): Lukas Toenne
|
||||
*
|
||||
* ***** END GPL LICENSE BLOCK *****
|
||||
*/
|
||||
|
||||
/** \file blender/editors/hair/hair_mirror.c
|
||||
* \ingroup edhair
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "BLI_utildefines.h"
|
||||
#include "BLI_math.h"
|
||||
|
||||
#include "BKE_editmesh_bvh.h"
|
||||
#include "BKE_editstrands.h"
|
||||
|
||||
#include "ED_physics.h"
|
||||
#include "ED_util.h"
|
||||
|
||||
#include "bmesh.h"
|
||||
|
||||
#include "hair_intern.h"
|
||||
|
||||
/* TODO use_topology is not yet implemented for strands.
|
||||
* Native strand topology is not very useful for this.
|
||||
* Instead, the topology of the scalp mesh should be used for finding mirrored strand roots,
|
||||
* then the arc- or parametric length of a vertex from the root to find mirrored verts.
|
||||
*/
|
||||
|
||||
#define BM_SEARCH_MAXDIST_MIRR 0.00002f
|
||||
#define BM_CD_LAYER_ID "__mirror_index"
|
||||
/**
|
||||
* \param edit Edit strands.
|
||||
* \param use_self Allow a vertex to point to its self (middle verts).
|
||||
* \param use_select Restrict to selected verts.
|
||||
* \param use_topology Use topology mirror.
|
||||
* \param maxdist Distance for close point test.
|
||||
* \param r_index Optional array to write into, as an alternative to a customdata layer (length of total verts).
|
||||
*/
|
||||
void ED_strands_mirror_cache_begin_ex(BMEditStrands *edit, const int axis, const bool use_self, const bool use_select,
|
||||
/* extra args */
|
||||
const bool UNUSED(use_topology), float maxdist, int *r_index)
|
||||
{
|
||||
BMesh *bm = edit->base.bm;
|
||||
BMIter iter;
|
||||
BMVert *v;
|
||||
int cd_vmirr_offset;
|
||||
int i;
|
||||
|
||||
/* one or the other is used depending if topo is enabled */
|
||||
struct BMBVHTree *tree = NULL;
|
||||
|
||||
BM_mesh_elem_table_ensure(bm, BM_VERT);
|
||||
|
||||
if (r_index == NULL) {
|
||||
const char *layer_id = BM_CD_LAYER_ID;
|
||||
int mirror_cdlayer = CustomData_get_named_layer_index(&bm->vdata, CD_PROP_INT, layer_id);
|
||||
if (mirror_cdlayer == -1) {
|
||||
BM_data_layer_add_named(bm, &bm->vdata, CD_PROP_INT, layer_id);
|
||||
mirror_cdlayer = CustomData_get_named_layer_index(&bm->vdata, CD_PROP_INT, layer_id);
|
||||
}
|
||||
|
||||
cd_vmirr_offset = CustomData_get_n_offset(&bm->vdata, CD_PROP_INT,
|
||||
mirror_cdlayer - CustomData_get_layer_index(&bm->vdata, CD_PROP_INT));
|
||||
|
||||
bm->vdata.layers[mirror_cdlayer].flag |= CD_FLAG_TEMPORARY;
|
||||
|
||||
edit->base.mirror_cdlayer = mirror_cdlayer;
|
||||
}
|
||||
|
||||
BM_mesh_elem_index_ensure(bm, BM_VERT);
|
||||
|
||||
tree = BKE_bmbvh_new(edit->base.bm, NULL, 0, 0, NULL, false);
|
||||
|
||||
#define VERT_INTPTR(_v, _i) r_index ? &r_index[_i] : BM_ELEM_CD_GET_VOID_P(_v, cd_vmirr_offset);
|
||||
|
||||
BM_ITER_MESH_INDEX (v, &iter, bm, BM_VERTS_OF_MESH, i) {
|
||||
BLI_assert(BM_elem_index_get(v) == i);
|
||||
|
||||
/* temporary for testing, check for selection */
|
||||
if (use_select && !BM_elem_flag_test(v, BM_ELEM_SELECT)) {
|
||||
/* do nothing */
|
||||
}
|
||||
else {
|
||||
BMVert *v_mirr;
|
||||
float co[3];
|
||||
int *idx = VERT_INTPTR(v, i);
|
||||
|
||||
copy_v3_v3(co, v->co);
|
||||
co[axis] *= -1.0f;
|
||||
v_mirr = BKE_bmbvh_find_vert_closest(tree, co, maxdist);
|
||||
|
||||
if (v_mirr && (use_self || (v_mirr != v))) {
|
||||
const int i_mirr = BM_elem_index_get(v_mirr);
|
||||
*idx = i_mirr;
|
||||
idx = VERT_INTPTR(v_mirr, i_mirr);
|
||||
*idx = i;
|
||||
}
|
||||
else {
|
||||
*idx = -1;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#undef VERT_INTPTR
|
||||
|
||||
BKE_bmbvh_free(tree);
|
||||
}
|
||||
|
||||
void ED_strands_mirror_cache_begin(BMEditStrands *edit, const int axis,
|
||||
const bool use_self, const bool use_select,
|
||||
const bool use_topology)
|
||||
{
|
||||
ED_strands_mirror_cache_begin_ex(edit, axis,
|
||||
use_self, use_select,
|
||||
/* extra args */
|
||||
use_topology, BM_SEARCH_MAXDIST_MIRR, NULL);
|
||||
}
|
||||
|
||||
BMVert *ED_strands_mirror_get(BMEditStrands *edit, BMVert *v)
|
||||
{
|
||||
BMesh *bm = edit->base.bm;
|
||||
int mirror_cdlayer = edit->base.mirror_cdlayer;
|
||||
const int *mirr = CustomData_bmesh_get_layer_n(&bm->vdata, v->head.data, mirror_cdlayer);
|
||||
|
||||
BLI_assert(mirror_cdlayer != -1); /* invalid use */
|
||||
|
||||
if (mirr && *mirr >= 0 && *mirr < bm->totvert) {
|
||||
if (!bm->vtable) {
|
||||
printf("err: should only be called between "
|
||||
"ED_strands_mirror_cache_begin and ED_strands_mirror_cache_end\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return bm->vtable[*mirr];
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
BMEdge *ED_strands_mirror_get_edge(BMEditStrands *edit, BMEdge *e)
|
||||
{
|
||||
BMVert *v1_mirr = ED_strands_mirror_get(edit, e->v1);
|
||||
if (v1_mirr) {
|
||||
BMVert *v2_mirr = ED_strands_mirror_get(edit, e->v2);
|
||||
if (v2_mirr) {
|
||||
return BM_edge_exists(v1_mirr, v2_mirr);
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void ED_strands_mirror_cache_clear(BMEditStrands *edit, BMVert *v)
|
||||
{
|
||||
BMesh *bm = edit->base.bm;
|
||||
int mirror_cdlayer = edit->base.mirror_cdlayer;
|
||||
int *mirr = CustomData_bmesh_get_layer_n(&bm->vdata, v->head.data, mirror_cdlayer);
|
||||
|
||||
BLI_assert(mirror_cdlayer != -1); /* invalid use */
|
||||
|
||||
if (mirr) {
|
||||
*mirr = -1;
|
||||
}
|
||||
}
|
||||
|
||||
void ED_strands_mirror_cache_end(BMEditStrands *edit)
|
||||
{
|
||||
edit->base.mirror_cdlayer = -1;
|
||||
}
|
||||
|
||||
void ED_strands_mirror_apply(BMEditStrands *edit, const int sel_from, const int sel_to)
|
||||
{
|
||||
BMesh *bm = edit->base.bm;
|
||||
BMIter iter;
|
||||
BMVert *v;
|
||||
|
||||
BLI_assert((bm->vtable != NULL) && ((bm->elem_table_dirty & BM_VERT) == 0));
|
||||
|
||||
BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) {
|
||||
if (BM_elem_flag_test(v, BM_ELEM_SELECT) == sel_from) {
|
||||
BMVert *mirr = ED_strands_mirror_get(edit, v);
|
||||
if (mirr) {
|
||||
if (BM_elem_flag_test(mirr, BM_ELEM_SELECT) == sel_to) {
|
||||
copy_v3_v3(mirr->co, v->co);
|
||||
mirr->co[0] *= -1.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
86
source/blender/editors/hair/hair_object_mesh.c
Normal file
@@ -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/hair/hair_object_mesh.c
|
||||
* \ingroup edhair
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
#include "DNA_mesh_types.h"
|
||||
#include "DNA_object_types.h"
|
||||
#include "DNA_scene_types.h"
|
||||
|
||||
#include "BKE_cdderivedmesh.h"
|
||||
#include "BKE_DerivedMesh.h"
|
||||
#include "BKE_editstrands.h"
|
||||
|
||||
#include "bmesh.h"
|
||||
|
||||
#include "hair_intern.h"
|
||||
|
||||
bool ED_hair_object_init_mesh_edit(struct EvaluationContext *UNUSED(eval_ctx), Scene *UNUSED(scene), Object *ob)
|
||||
{
|
||||
if (ob->type == OB_MESH) {
|
||||
Mesh *me = ob->data;
|
||||
|
||||
if (!me->edit_strands) {
|
||||
BMesh *bm = BKE_editstrands_mesh_to_bmesh(ob, me);
|
||||
DerivedMesh *root_dm = CDDM_new(0, 0, 0, 0, 0);
|
||||
|
||||
me->edit_strands = BKE_editstrands_create(bm, root_dm);
|
||||
|
||||
root_dm->release(root_dm);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ED_hair_object_apply_mesh_edit(Object *ob)
|
||||
{
|
||||
if (ob->type == OB_MESH) {
|
||||
Mesh *me = ob->data;
|
||||
|
||||
if (me->edit_strands) {
|
||||
BKE_editstrands_mesh_from_bmesh(ob);
|
||||
|
||||
BKE_editstrands_free(me->edit_strands);
|
||||
MEM_freeN(me->edit_strands);
|
||||
me->edit_strands = NULL;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
101
source/blender/editors/hair/hair_object_particles.c
Normal file
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* ***** BEGIN GPL LICENSE BLOCK *****
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* The Original Code is Copyright (C) Blender Foundation
|
||||
* All rights reserved.
|
||||
*
|
||||
* The Original Code is: all of this file.
|
||||
*
|
||||
* Contributor(s): Lukas Toenne
|
||||
*
|
||||
* ***** END GPL LICENSE BLOCK *****
|
||||
*/
|
||||
|
||||
/** \file blender/editors/hair/hair_object_particles.c
|
||||
* \ingroup edhair
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
#include "DNA_mesh_types.h"
|
||||
#include "DNA_object_types.h"
|
||||
#include "DNA_particle_types.h"
|
||||
#include "DNA_scene_types.h"
|
||||
|
||||
#include "BKE_cdderivedmesh.h"
|
||||
#include "BKE_DerivedMesh.h"
|
||||
#include "BKE_editstrands.h"
|
||||
#include "BKE_particle.h"
|
||||
|
||||
#include "bmesh.h"
|
||||
|
||||
#include "hair_intern.h"
|
||||
|
||||
bool ED_hair_object_has_hair_particle_data(Object *ob)
|
||||
{
|
||||
ParticleSystem *psys = psys_get_current(ob);
|
||||
if (psys && psys->part->type == PART_HAIR)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ED_hair_object_init_particle_edit(struct EvaluationContext *eval_ctx, Scene *scene, Object *ob)
|
||||
{
|
||||
ParticleSystem *psys = psys_get_current(ob);
|
||||
BMesh *bm;
|
||||
DerivedMesh *dm;
|
||||
|
||||
if (psys && psys->part->type == PART_HAIR) {
|
||||
if (!psys->hairedit) {
|
||||
bm = BKE_editstrands_particles_to_bmesh(ob, psys);
|
||||
|
||||
if (ob->type == OB_MESH || ob->derivedFinal)
|
||||
dm = ob->derivedFinal ? ob->derivedFinal : mesh_get_derived_final(eval_ctx, scene, ob, CD_MASK_BAREMESH);
|
||||
else
|
||||
dm = NULL;
|
||||
|
||||
psys->hairedit = BKE_editstrands_create(bm, dm);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ED_hair_object_apply_particle_edit(Object *ob)
|
||||
{
|
||||
ParticleSystem *psys = psys_get_current(ob);
|
||||
if (psys->part->type == PART_HAIR) {
|
||||
if (psys->hairedit) {
|
||||
BKE_editstrands_particles_from_bmesh(ob, psys);
|
||||
psys->flag |= PSYS_EDITED;
|
||||
|
||||
BKE_editstrands_free(psys->hairedit);
|
||||
MEM_freeN(psys->hairedit);
|
||||
psys->hairedit = NULL;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
143
source/blender/editors/hair/hair_ops.c
Normal file
@@ -0,0 +1,143 @@
|
||||
/*
|
||||
* ***** BEGIN GPL LICENSE BLOCK *****
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* The Original Code is Copyright (C) Blender Foundation
|
||||
* All rights reserved.
|
||||
*
|
||||
* The Original Code is: all of this file.
|
||||
*
|
||||
* Contributor(s): Lukas Toenne
|
||||
*
|
||||
* ***** END GPL LICENSE BLOCK *****
|
||||
*/
|
||||
|
||||
/** \file blender/editors/hair/hair_ops.c
|
||||
* \ingroup edhair
|
||||
*/
|
||||
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
#include "DNA_object_types.h"
|
||||
|
||||
#include "BKE_context.h"
|
||||
|
||||
#include "RNA_access.h"
|
||||
|
||||
#include "WM_api.h"
|
||||
#include "WM_types.h"
|
||||
|
||||
#include "ED_physics.h"
|
||||
|
||||
#include "hair_intern.h"
|
||||
#include "paint_intern.h"
|
||||
|
||||
void ED_operatortypes_hair(void)
|
||||
{
|
||||
WM_operatortype_append(HAIR_OT_hair_edit_toggle);
|
||||
|
||||
WM_operatortype_append(HAIR_OT_select_all);
|
||||
WM_operatortype_append(HAIR_OT_select_linked);
|
||||
|
||||
WM_operatortype_append(HAIR_OT_stroke);
|
||||
}
|
||||
|
||||
static int hair_poll(bContext *C)
|
||||
{
|
||||
if (hair_edit_toggle_poll(C))
|
||||
if (CTX_data_active_object(C)->mode & OB_MODE_HAIR_EDIT)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void ed_keymap_hair_brush_switch(wmKeyMap *keymap, const char *mode)
|
||||
{
|
||||
wmKeyMapItem *kmi;
|
||||
int i;
|
||||
/* index 0-9 (zero key is tenth), shift key for index 10-19 */
|
||||
for (i = 0; i < 20; i++) {
|
||||
kmi = WM_keymap_add_item(keymap, "BRUSH_OT_active_index_set",
|
||||
ZEROKEY + ((i + 1) % 10), KM_PRESS, i < 10 ? 0 : KM_SHIFT, 0);
|
||||
RNA_string_set(kmi->ptr, "mode", mode);
|
||||
RNA_int_set(kmi->ptr, "index", i);
|
||||
}
|
||||
}
|
||||
|
||||
static void ed_keymap_hair_brush_size(wmKeyMap *keymap, const char *UNUSED(path))
|
||||
{
|
||||
wmKeyMapItem *kmi;
|
||||
|
||||
kmi = WM_keymap_add_item(keymap, "BRUSH_OT_scale_size", LEFTBRACKETKEY, KM_PRESS, 0, 0);
|
||||
RNA_float_set(kmi->ptr, "scalar", 0.9);
|
||||
|
||||
kmi = WM_keymap_add_item(keymap, "BRUSH_OT_scale_size", RIGHTBRACKETKEY, KM_PRESS, 0, 0);
|
||||
RNA_float_set(kmi->ptr, "scalar", 10.0 / 9.0); // 1.1111....
|
||||
}
|
||||
|
||||
static void ed_keymap_hair_brush_radial_control(wmKeyMap *keymap, const char *settings, RCFlags flags)
|
||||
{
|
||||
wmKeyMapItem *kmi;
|
||||
/* only size needs to follow zoom, strength shows fixed size circle */
|
||||
int flags_nozoom = flags & (~RC_ZOOM);
|
||||
int flags_noradial_secondary = flags & (~(RC_SECONDARY_ROTATION | RC_ZOOM));
|
||||
|
||||
kmi = WM_keymap_add_item(keymap, "WM_OT_radial_control", FKEY, KM_PRESS, 0, 0);
|
||||
set_brush_rc_props(kmi->ptr, settings, "size", "use_unified_size", flags);
|
||||
|
||||
kmi = WM_keymap_add_item(keymap, "WM_OT_radial_control", FKEY, KM_PRESS, KM_SHIFT, 0);
|
||||
set_brush_rc_props(kmi->ptr, settings, "strength", "use_unified_strength", flags_nozoom);
|
||||
|
||||
if (flags & RC_WEIGHT) {
|
||||
kmi = WM_keymap_add_item(keymap, "WM_OT_radial_control", WKEY, KM_PRESS, 0, 0);
|
||||
set_brush_rc_props(kmi->ptr, settings, "weight", "use_unified_weight", flags_nozoom);
|
||||
}
|
||||
|
||||
if (flags & RC_ROTATION) {
|
||||
kmi = WM_keymap_add_item(keymap, "WM_OT_radial_control", FKEY, KM_PRESS, KM_CTRL, 0);
|
||||
set_brush_rc_props(kmi->ptr, settings, "texture_slot.angle", NULL, flags_noradial_secondary);
|
||||
}
|
||||
|
||||
if (flags & RC_SECONDARY_ROTATION) {
|
||||
kmi = WM_keymap_add_item(keymap, "WM_OT_radial_control", FKEY, KM_PRESS, KM_CTRL | KM_ALT, 0);
|
||||
set_brush_rc_props(kmi->ptr, settings, "mask_texture_slot.angle", NULL, flags_nozoom);
|
||||
}
|
||||
}
|
||||
|
||||
void ED_keymap_hair(wmKeyConfig *keyconf)
|
||||
{
|
||||
wmKeyMap *keymap;
|
||||
wmKeyMapItem *kmi;
|
||||
|
||||
keymap = WM_keymap_find(keyconf, "Hair", 0, 0);
|
||||
keymap->poll = hair_poll;
|
||||
|
||||
kmi = WM_keymap_add_item(keymap, "HAIR_OT_select_all", AKEY, KM_PRESS, 0, 0);
|
||||
RNA_enum_set(kmi->ptr, "action", SEL_TOGGLE);
|
||||
kmi = WM_keymap_add_item(keymap, "HAIR_OT_select_all", IKEY, KM_PRESS, KM_CTRL, 0);
|
||||
RNA_enum_set(kmi->ptr, "action", SEL_INVERT);
|
||||
|
||||
kmi = WM_keymap_add_item(keymap, "HAIR_OT_select_linked", LKEY, KM_PRESS, 0, 0);
|
||||
RNA_boolean_set(kmi->ptr, "deselect", false);
|
||||
kmi = WM_keymap_add_item(keymap, "HAIR_OT_select_linked", LKEY, KM_PRESS, KM_SHIFT, 0);
|
||||
RNA_boolean_set(kmi->ptr, "deselect", true);
|
||||
|
||||
kmi = WM_keymap_add_item(keymap, "HAIR_OT_stroke", LEFTMOUSE, KM_PRESS, 0, 0);
|
||||
|
||||
ed_keymap_hair_brush_switch(keymap, "hair_edit");
|
||||
ed_keymap_hair_brush_size(keymap, "tool_settings.hair_edit.brush.size");
|
||||
ed_keymap_hair_brush_radial_control(keymap, "hair_edit", 0);
|
||||
}
|
506
source/blender/editors/hair/hair_select.c
Normal file
@@ -0,0 +1,506 @@
|
||||
/*
|
||||
* ***** BEGIN GPL LICENSE BLOCK *****
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* The Original Code is Copyright (C) Blender Foundation
|
||||
* All rights reserved.
|
||||
*
|
||||
* The Original Code is: all of this file.
|
||||
*
|
||||
* Contributor(s): Lukas Toenne
|
||||
*
|
||||
* ***** END GPL LICENSE BLOCK *****
|
||||
*/
|
||||
|
||||
/** \file blender/editors/hair/hair_select.c
|
||||
* \ingroup edhair
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "BLI_utildefines.h"
|
||||
#include "BLI_math.h"
|
||||
#include "BLI_rect.h"
|
||||
|
||||
#include "DNA_object_types.h"
|
||||
#include "DNA_scene_types.h"
|
||||
#include "DNA_screen_types.h"
|
||||
#include "DNA_view3d_types.h"
|
||||
|
||||
#include "BKE_context.h"
|
||||
#include "BKE_editstrands.h"
|
||||
|
||||
#include "DEG_depsgraph.h"
|
||||
|
||||
#include "bmesh.h"
|
||||
|
||||
#include "RNA_access.h"
|
||||
#include "RNA_define.h"
|
||||
|
||||
#include "WM_api.h"
|
||||
#include "WM_types.h"
|
||||
|
||||
#include "ED_object.h"
|
||||
#include "ED_physics.h"
|
||||
#include "ED_view3d.h"
|
||||
|
||||
#include "hair_intern.h"
|
||||
|
||||
BLI_INLINE bool apply_select_action_flag(BMVert *v, int action)
|
||||
{
|
||||
bool cursel = BM_elem_flag_test_bool(v, BM_ELEM_SELECT);
|
||||
bool newsel;
|
||||
|
||||
switch (action) {
|
||||
case SEL_SELECT:
|
||||
newsel = true;
|
||||
break;
|
||||
case SEL_DESELECT:
|
||||
newsel = false;
|
||||
break;
|
||||
case SEL_INVERT:
|
||||
newsel = !cursel;
|
||||
break;
|
||||
case SEL_TOGGLE:
|
||||
/* toggle case should be converted to SELECT or DESELECT based on global state */
|
||||
BLI_assert(false);
|
||||
break;
|
||||
}
|
||||
|
||||
if (newsel != cursel) {
|
||||
BM_elem_flag_set(v, BM_ELEM_SELECT, newsel);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
/* poll function */
|
||||
typedef bool (*PollVertexCb)(void *userdata, struct BMVert *v);
|
||||
/* distance metric function */
|
||||
typedef bool (*DistanceVertexCb)(void *userdata, struct BMVert *v, float *dist);
|
||||
typedef void (*ActionVertexCb)(void *userdata, struct BMVert *v, int action);
|
||||
|
||||
static int hair_select_verts_filter(bContext *C, Object *ob, BMEditStrands *edit,
|
||||
HairEditSelectMode select_mode, int action,
|
||||
PollVertexCb cb, void *userdata)
|
||||
{
|
||||
BMesh *bm = edit->base.bm;
|
||||
|
||||
BMVert *v;
|
||||
BMIter iter;
|
||||
int tot = 0;
|
||||
|
||||
bm->selectmode = BM_VERT;
|
||||
|
||||
switch (select_mode) {
|
||||
case HAIR_SELECT_STRAND:
|
||||
break;
|
||||
case HAIR_SELECT_VERTEX:
|
||||
BM_ITER_MESH(v, &iter, bm, BM_VERTS_OF_MESH) {
|
||||
if (!cb(userdata, v))
|
||||
continue;
|
||||
|
||||
if (apply_select_action_flag(v, action))
|
||||
++tot;
|
||||
}
|
||||
break;
|
||||
case HAIR_SELECT_TIP:
|
||||
BM_ITER_MESH(v, &iter, bm, BM_VERTS_OF_MESH) {
|
||||
if (!BM_strands_vert_is_tip(v))
|
||||
continue;
|
||||
if (!cb(userdata, v))
|
||||
continue;
|
||||
|
||||
if (apply_select_action_flag(v, action))
|
||||
++tot;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
BM_mesh_select_mode_flush(bm);
|
||||
|
||||
if (tot > 0) {
|
||||
BKE_editstrands_batch_cache_dirty(edit, BKE_STRANDS_BATCH_DIRTY_SELECT);
|
||||
WM_event_add_notifier(C, NC_GEOM | ND_SELECT, ob->data);
|
||||
}
|
||||
|
||||
return tot;
|
||||
}
|
||||
|
||||
static bool hair_select_verts_closest(bContext *C, Object *ob, BMEditStrands *edit, HairEditSelectMode select_mode, int action, DistanceVertexCb cb, ActionVertexCb action_cb, void *userdata)
|
||||
{
|
||||
BMesh *bm = edit->base.bm;
|
||||
|
||||
BMVert *v;
|
||||
BMIter iter;
|
||||
|
||||
float dist;
|
||||
BMVert *closest_v = NULL;
|
||||
float closest_dist = FLT_MAX;
|
||||
|
||||
bm->selectmode = BM_VERT;
|
||||
|
||||
switch (select_mode) {
|
||||
case HAIR_SELECT_STRAND:
|
||||
break;
|
||||
case HAIR_SELECT_VERTEX:
|
||||
BM_ITER_MESH(v, &iter, bm, BM_VERTS_OF_MESH) {
|
||||
if (!cb(userdata, v, &dist))
|
||||
continue;
|
||||
|
||||
if (dist < closest_dist) {
|
||||
closest_v = v;
|
||||
closest_dist = dist;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case HAIR_SELECT_TIP:
|
||||
BM_ITER_MESH(v, &iter, bm, BM_VERTS_OF_MESH) {
|
||||
if (!BM_strands_vert_is_tip(v))
|
||||
continue;
|
||||
if (!cb(userdata, v, &dist))
|
||||
continue;
|
||||
|
||||
if (dist < closest_dist) {
|
||||
closest_v = v;
|
||||
closest_dist = dist;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (closest_v) {
|
||||
action_cb(userdata, closest_v, action);
|
||||
|
||||
BM_mesh_select_mode_flush(bm);
|
||||
|
||||
BKE_editstrands_batch_cache_dirty(edit, BKE_STRANDS_BATCH_DIRTY_SELECT);
|
||||
WM_event_add_notifier(C, NC_GEOM | ND_SELECT, ob->data);
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
static void hair_deselect_all(BMEditStrands *edit)
|
||||
{
|
||||
BMesh *bm = edit->base.bm;
|
||||
|
||||
BMVert *v;
|
||||
BMIter iter;
|
||||
|
||||
BM_ITER_MESH(v, &iter, bm, BM_VERTS_OF_MESH) {
|
||||
BM_elem_flag_set(v, BM_ELEM_SELECT, false);
|
||||
}
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
/************************ select/deselect all operator ************************/
|
||||
|
||||
static bool poll_vertex_all(void *UNUSED(userdata), struct BMVert *UNUSED(v))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
static int select_all_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
Scene *scene = CTX_data_scene(C);
|
||||
Object *ob = CTX_data_active_object(C);
|
||||
BMEditStrands *edit = BKE_editstrands_from_object(ob);
|
||||
HairEditSettings *settings = &scene->toolsettings->hair_edit;
|
||||
int action = RNA_enum_get(op->ptr, "action");
|
||||
|
||||
if (!edit)
|
||||
return 0;
|
||||
|
||||
/* toggle action depends on current global selection state */
|
||||
if (action == SEL_TOGGLE) {
|
||||
if (edit->base.bm->totvertsel == 0)
|
||||
action = SEL_SELECT;
|
||||
else
|
||||
action = SEL_DESELECT;
|
||||
}
|
||||
|
||||
hair_select_verts_filter(C, ob, edit, settings->select_mode, action, poll_vertex_all, NULL);
|
||||
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
void HAIR_OT_select_all(wmOperatorType *ot)
|
||||
{
|
||||
/* identifiers */
|
||||
ot->name = "Select/Deselect All";
|
||||
ot->idname = "HAIR_OT_select_all";
|
||||
ot->description = "Select/Deselect all hair vertices";
|
||||
|
||||
/* api callbacks */
|
||||
ot->exec = select_all_exec;
|
||||
ot->poll = hair_edit_poll;
|
||||
|
||||
/* flags */
|
||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
||||
|
||||
WM_operator_properties_select_all(ot);
|
||||
}
|
||||
|
||||
/************************ mouse select operator ************************/
|
||||
|
||||
typedef struct DistanceVertexCirleData {
|
||||
ViewContext vc;
|
||||
float mval[2];
|
||||
float radsq;
|
||||
} DistanceVertexCirleData;
|
||||
|
||||
static bool distance_vertex_circle(void *userdata, struct BMVert *v, float *dist)
|
||||
{
|
||||
DistanceVertexCirleData *data = userdata;
|
||||
|
||||
return hair_test_vertex_inside_circle(&data->vc, data->mval, data->radsq, v, dist);
|
||||
}
|
||||
|
||||
static void closest_vertex_select(void *UNUSED(userdata), struct BMVert *v, int action)
|
||||
{
|
||||
apply_select_action_flag(v, action);
|
||||
}
|
||||
|
||||
int ED_hair_mouse_select(bContext *C, const int mval[2], bool extend, bool deselect, bool toggle)
|
||||
{
|
||||
Scene *scene = CTX_data_scene(C);
|
||||
Object *ob = CTX_data_active_object(C);
|
||||
BMEditStrands *edit = BKE_editstrands_from_object(ob);
|
||||
HairEditSettings *settings = &scene->toolsettings->hair_edit;
|
||||
float select_radius = ED_view3d_select_dist_px();
|
||||
|
||||
DistanceVertexCirleData data;
|
||||
int action;
|
||||
|
||||
if (!extend && !deselect && !toggle) {
|
||||
hair_deselect_all(edit);
|
||||
}
|
||||
|
||||
hair_init_viewcontext(C, &data.vc);
|
||||
data.mval[0] = mval[0];
|
||||
data.mval[1] = mval[1];
|
||||
data.radsq = select_radius * select_radius;
|
||||
|
||||
if (extend)
|
||||
action = SEL_SELECT;
|
||||
else if (deselect)
|
||||
action = SEL_DESELECT;
|
||||
else
|
||||
action = SEL_INVERT;
|
||||
|
||||
hair_select_verts_closest(C, ob, edit, settings->select_mode, action, distance_vertex_circle, closest_vertex_select, &data);
|
||||
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
/************************ select linked operator ************************/
|
||||
|
||||
static void linked_vertices_select(void *UNUSED(userdata), struct BMVert *v, int action)
|
||||
{
|
||||
BMVert *lv;
|
||||
|
||||
apply_select_action_flag(v, action);
|
||||
|
||||
for (lv = BM_strands_vert_prev(v); lv; lv = BM_strands_vert_prev(lv))
|
||||
apply_select_action_flag(lv, action);
|
||||
for (lv = BM_strands_vert_next(v); lv; lv = BM_strands_vert_next(lv))
|
||||
apply_select_action_flag(lv, action);
|
||||
}
|
||||
|
||||
static int select_linked_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
Scene *scene = CTX_data_scene(C);
|
||||
Object *ob = CTX_data_active_object(C);
|
||||
BMEditStrands *edit = BKE_editstrands_from_object(ob);
|
||||
HairEditSettings *settings = &scene->toolsettings->hair_edit;
|
||||
float select_radius = ED_view3d_select_dist_px();
|
||||
|
||||
DistanceVertexCirleData data;
|
||||
int location[2];
|
||||
int action;
|
||||
|
||||
RNA_int_get_array(op->ptr, "location", location);
|
||||
|
||||
hair_init_viewcontext(C, &data.vc);
|
||||
data.mval[0] = location[0];
|
||||
data.mval[1] = location[1];
|
||||
data.radsq = select_radius * select_radius;
|
||||
|
||||
action = RNA_boolean_get(op->ptr, "deselect") ? SEL_DESELECT : SEL_SELECT;
|
||||
|
||||
hair_select_verts_closest(C, ob, edit, settings->select_mode, action, distance_vertex_circle, linked_vertices_select, &data);
|
||||
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
static int select_linked_invoke(bContext *C, wmOperator *op, const wmEvent *event)
|
||||
{
|
||||
RNA_int_set_array(op->ptr, "location", event->mval);
|
||||
return select_linked_exec(C, op);
|
||||
}
|
||||
|
||||
void HAIR_OT_select_linked(wmOperatorType *ot)
|
||||
{
|
||||
/* identifiers */
|
||||
ot->name = "Select Linked";
|
||||
ot->idname = "HAIR_OT_select_linked";
|
||||
ot->description = "Select connected vertices";
|
||||
|
||||
/* api callbacks */
|
||||
ot->exec = select_linked_exec;
|
||||
ot->invoke = select_linked_invoke;
|
||||
ot->poll = hair_edit_poll;
|
||||
|
||||
/* flags */
|
||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
||||
|
||||
/* properties */
|
||||
RNA_def_boolean(ot->srna, "deselect", 0, "Deselect", "Deselect linked keys rather than selecting them");
|
||||
RNA_def_int_vector(ot->srna, "location", 2, NULL, 0, INT_MAX, "Location", "", 0, 16384);
|
||||
}
|
||||
|
||||
/************************ border select operator ************************/
|
||||
|
||||
typedef struct PollVertexRectData {
|
||||
ViewContext vc;
|
||||
rcti rect;
|
||||
} PollVertexRectData;
|
||||
|
||||
static bool poll_vertex_inside_rect(void *userdata, struct BMVert *v)
|
||||
{
|
||||
PollVertexRectData *data = userdata;
|
||||
|
||||
return hair_test_vertex_inside_rect(&data->vc, &data->rect, v);
|
||||
}
|
||||
|
||||
int ED_hair_border_select(bContext *C, rcti *rect, bool select, bool extend)
|
||||
{
|
||||
Scene *scene = CTX_data_scene(C);
|
||||
Object *ob = CTX_data_active_object(C);
|
||||
BMEditStrands *edit = BKE_editstrands_from_object(ob);
|
||||
HairEditSettings *settings = &scene->toolsettings->hair_edit;
|
||||
|
||||
PollVertexRectData data;
|
||||
int action;
|
||||
|
||||
if (!extend && select)
|
||||
hair_deselect_all(edit);
|
||||
|
||||
hair_init_viewcontext(C, &data.vc);
|
||||
data.rect = *rect;
|
||||
|
||||
if (extend)
|
||||
action = SEL_SELECT;
|
||||
else if (select)
|
||||
action = SEL_INVERT;
|
||||
else
|
||||
action = SEL_DESELECT;
|
||||
|
||||
hair_select_verts_filter(C, ob, edit, settings->select_mode, action, poll_vertex_inside_rect, &data);
|
||||
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
/************************ circle select operator ************************/
|
||||
|
||||
typedef struct PollVertexCirleData {
|
||||
ViewContext vc;
|
||||
float mval[2];
|
||||
float radsq;
|
||||
} PollVertexCirleData;
|
||||
|
||||
static bool poll_vertex_inside_circle(void *userdata, struct BMVert *v)
|
||||
{
|
||||
PollVertexCirleData *data = userdata;
|
||||
float dist;
|
||||
|
||||
return hair_test_vertex_inside_circle(&data->vc, data->mval, data->radsq, v, &dist);
|
||||
}
|
||||
|
||||
int ED_hair_circle_select(bContext *C, bool select, const int mval[2], float radius)
|
||||
{
|
||||
Scene *scene = CTX_data_scene(C);
|
||||
Object *ob = CTX_data_active_object(C);
|
||||
BMEditStrands *edit = BKE_editstrands_from_object(ob);
|
||||
HairEditSettings *settings = &scene->toolsettings->hair_edit;
|
||||
int action = select ? SEL_SELECT : SEL_DESELECT;
|
||||
|
||||
PollVertexCirleData data;
|
||||
int tot;
|
||||
|
||||
if (!edit)
|
||||
return 0;
|
||||
|
||||
hair_init_viewcontext(C, &data.vc);
|
||||
data.mval[0] = mval[0];
|
||||
data.mval[1] = mval[1];
|
||||
data.radsq = radius * radius;
|
||||
|
||||
tot = hair_select_verts_filter(C, ob, edit, settings->select_mode, action, poll_vertex_inside_circle, &data);
|
||||
|
||||
return tot;
|
||||
}
|
||||
|
||||
/************************ lasso select operator ************************/
|
||||
|
||||
typedef struct PollVertexLassoData {
|
||||
ViewContext vc;
|
||||
const int (*mcoords)[2];
|
||||
short moves;
|
||||
} PollVertexLassoData;
|
||||
|
||||
static bool poll_vertex_inside_lasso(void *userdata, struct BMVert *v)
|
||||
{
|
||||
PollVertexLassoData *data = userdata;
|
||||
|
||||
return hair_test_vertex_inside_lasso(&data->vc, data->mcoords, data->moves, v);
|
||||
}
|
||||
|
||||
int ED_hair_lasso_select(bContext *C, const int mcoords[][2], const short moves, bool extend, bool select)
|
||||
{
|
||||
Scene *scene = CTX_data_scene(C);
|
||||
Object *ob = CTX_data_active_object(C);
|
||||
BMEditStrands *edit = BKE_editstrands_from_object(ob);
|
||||
HairEditSettings *settings = &scene->toolsettings->hair_edit;
|
||||
|
||||
PollVertexLassoData data;
|
||||
int action;
|
||||
|
||||
if (!extend && select)
|
||||
hair_deselect_all(edit);
|
||||
|
||||
hair_init_viewcontext(C, &data.vc);
|
||||
data.mcoords = mcoords;
|
||||
data.moves = moves;
|
||||
|
||||
if (extend)
|
||||
action = SEL_SELECT;
|
||||
else if (select)
|
||||
action = SEL_INVERT;
|
||||
else
|
||||
action = SEL_DESELECT;
|
||||
|
||||
hair_select_verts_filter(C, ob, edit, settings->select_mode, action, poll_vertex_inside_lasso, &data);
|
||||
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
501
source/blender/editors/hair/hair_stroke.c
Normal file
@@ -0,0 +1,501 @@
|
||||
/*
|
||||
* ***** BEGIN GPL LICENSE BLOCK *****
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* The Original Code is Copyright (C) Blender Foundation
|
||||
* All rights reserved.
|
||||
*
|
||||
* The Original Code is: all of this file.
|
||||
*
|
||||
* Contributor(s): Lukas Toenne
|
||||
*
|
||||
* ***** END GPL LICENSE BLOCK *****
|
||||
*/
|
||||
|
||||
/** \file blender/editors/hair/hair_edit.c
|
||||
* \ingroup edhair
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "BLI_utildefines.h"
|
||||
#include "BLI_lasso.h"
|
||||
#include "BLI_math.h"
|
||||
#include "BLI_rect.h"
|
||||
|
||||
#include "DNA_brush_types.h"
|
||||
#include "DNA_mesh_types.h"
|
||||
#include "DNA_meshdata_types.h"
|
||||
#include "DNA_object_types.h"
|
||||
#include "DNA_scene_types.h"
|
||||
#include "DNA_view3d_types.h"
|
||||
|
||||
#include "BKE_brush.h"
|
||||
#include "BKE_DerivedMesh.h"
|
||||
#include "BKE_editstrands.h"
|
||||
#include "BKE_effect.h"
|
||||
#include "BKE_mesh_sample.h"
|
||||
|
||||
#include "bmesh.h"
|
||||
|
||||
#include "BIF_gl.h"
|
||||
#include "BIF_glutil.h"
|
||||
|
||||
#include "ED_physics.h"
|
||||
#include "ED_view3d.h"
|
||||
|
||||
#include "hair_intern.h"
|
||||
|
||||
bool hair_test_depth(ViewContext *vc, const float co[3], const int screen_co[2])
|
||||
{
|
||||
View3D *v3d = vc->v3d;
|
||||
ViewDepths *vd = vc->rv3d->depths;
|
||||
const bool has_zbuf = (v3d->drawtype > OB_WIRE) && (v3d->flag & V3D_ZBUF_SELECT);
|
||||
|
||||
float uco[3];
|
||||
float depth;
|
||||
|
||||
/* nothing to do */
|
||||
if (!has_zbuf)
|
||||
return true;
|
||||
|
||||
ED_view3d_project(vc->ar, co, uco);
|
||||
|
||||
/* check if screen_co is within bounds because brush_cut uses out of screen coords */
|
||||
if (screen_co[0] >= 0 && screen_co[0] < vd->w && screen_co[1] >= 0 && screen_co[1] < vd->h) {
|
||||
BLI_assert(vd && vd->depths);
|
||||
/* we know its not clipped */
|
||||
depth = vd->depths[screen_co[1] * vd->w + screen_co[0]];
|
||||
|
||||
return ((float)uco[2] - 0.00001f <= depth);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool hair_test_vertex_inside_circle(ViewContext *vc, const float mval[2], float radsq, BMVert *v, float *r_dist)
|
||||
{
|
||||
float (*obmat)[4] = vc->obact->obmat;
|
||||
float co_world[3];
|
||||
float dx, dy, distsq;
|
||||
int screen_co[2];
|
||||
|
||||
mul_v3_m4v3(co_world, obmat, v->co);
|
||||
|
||||
/* TODO, should this check V3D_PROJ_TEST_CLIP_BB too? */
|
||||
if (ED_view3d_project_int_global(vc->ar, co_world, screen_co, V3D_PROJ_TEST_CLIP_WIN) != V3D_PROJ_RET_OK)
|
||||
return false;
|
||||
|
||||
dx = mval[0] - (float)screen_co[0];
|
||||
dy = mval[1] - (float)screen_co[1];
|
||||
distsq = dx * dx + dy * dy;
|
||||
|
||||
if (distsq > radsq)
|
||||
return false;
|
||||
|
||||
if (hair_test_depth(vc, v->co, screen_co)) {
|
||||
*r_dist = sqrtf(distsq);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
bool hair_test_edge_inside_circle(ViewContext *vc, const float mval[2], float radsq, BMVert *v1, BMVert *v2, float *r_dist, float *r_lambda)
|
||||
{
|
||||
float (*obmat)[4] = vc->obact->obmat;
|
||||
float world_co1[3], world_co2[3];
|
||||
float dx, dy, distsq;
|
||||
int screen_co1[2], screen_co2[2], screen_cp[2];
|
||||
float lambda, world_cp[3], screen_cpf[2], screen_co1f[2], screen_co2f[2];
|
||||
|
||||
mul_v3_m4v3(world_co1, obmat, v1->co);
|
||||
mul_v3_m4v3(world_co2, obmat, v2->co);
|
||||
|
||||
/* TODO, should this check V3D_PROJ_TEST_CLIP_BB too? */
|
||||
if (ED_view3d_project_int_global(vc->ar, world_co1, screen_co1, V3D_PROJ_TEST_CLIP_WIN) != V3D_PROJ_RET_OK)
|
||||
return false;
|
||||
if (ED_view3d_project_int_global(vc->ar, world_co2, screen_co2, V3D_PROJ_TEST_CLIP_WIN) != V3D_PROJ_RET_OK)
|
||||
return false;
|
||||
|
||||
screen_co1f[0] = screen_co1[0];
|
||||
screen_co1f[1] = screen_co1[1];
|
||||
screen_co2f[0] = screen_co2[0];
|
||||
screen_co2f[1] = screen_co2[1];
|
||||
lambda = closest_to_line_v2(screen_cpf, mval, screen_co1f, screen_co2f);
|
||||
if (lambda < 0.0f || lambda > 1.0f) {
|
||||
CLAMP(lambda, 0.0f, 1.0f);
|
||||
interp_v2_v2v2(screen_cpf, screen_co1f, screen_co2f, lambda);
|
||||
}
|
||||
|
||||
dx = mval[0] - screen_cpf[0];
|
||||
dy = mval[1] - screen_cpf[1];
|
||||
distsq = dx * dx + dy * dy;
|
||||
|
||||
if (distsq > radsq)
|
||||
return false;
|
||||
|
||||
interp_v3_v3v3(world_cp, world_co1, world_co2, lambda);
|
||||
|
||||
screen_cp[0] = screen_cpf[0];
|
||||
screen_cp[1] = screen_cpf[1];
|
||||
if (hair_test_depth(vc, world_cp, screen_cp)) {
|
||||
*r_dist = sqrtf(distsq);
|
||||
*r_lambda = lambda;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
bool hair_test_vertex_inside_rect(ViewContext *vc, rcti *rect, BMVert *v)
|
||||
{
|
||||
float (*obmat)[4] = vc->obact->obmat;
|
||||
float co_world[3];
|
||||
int screen_co[2];
|
||||
|
||||
mul_v3_m4v3(co_world, obmat, v->co);
|
||||
|
||||
/* TODO, should this check V3D_PROJ_TEST_CLIP_BB too? */
|
||||
if (ED_view3d_project_int_global(vc->ar, co_world, screen_co, V3D_PROJ_TEST_CLIP_WIN) != V3D_PROJ_RET_OK)
|
||||
return false;
|
||||
|
||||
if (!BLI_rcti_isect_pt_v(rect, screen_co))
|
||||
return false;
|
||||
|
||||
if (hair_test_depth(vc, v->co, screen_co))
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
bool hair_test_vertex_inside_lasso(ViewContext *vc, const int mcoords[][2], short moves, BMVert *v)
|
||||
{
|
||||
float (*obmat)[4] = vc->obact->obmat;
|
||||
float co_world[3];
|
||||
int screen_co[2];
|
||||
|
||||
mul_v3_m4v3(co_world, obmat, v->co);
|
||||
|
||||
/* TODO, should this check V3D_PROJ_TEST_CLIP_BB too? */
|
||||
if (ED_view3d_project_int_global(vc->ar, co_world, screen_co, V3D_PROJ_TEST_CLIP_WIN) != V3D_PROJ_RET_OK)
|
||||
return false;
|
||||
|
||||
if (!BLI_lasso_is_point_inside(mcoords, moves, screen_co[0], screen_co[1], IS_CLIPPED))
|
||||
return false;
|
||||
|
||||
if (hair_test_depth(vc, v->co, screen_co))
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
typedef void (*VertexToolCb)(HairToolData *data, void *userdata, BMVert *v, float factor);
|
||||
|
||||
/* apply tool directly to each vertex inside the filter area */
|
||||
static int UNUSED_FUNCTION(hair_tool_apply_vertex)(HairToolData *data, VertexToolCb cb, void *userdata)
|
||||
{
|
||||
BMesh *bm = data->edit->base.bm;
|
||||
Scene *scene = data->scene;
|
||||
Brush *brush = data->settings->brush;
|
||||
const float rad = BKE_brush_size_get(scene, brush);
|
||||
const float radsq = rad*rad;
|
||||
const float threshold = 0.0f; /* XXX could be useful, is it needed? */
|
||||
const bool use_mirror = hair_use_mirror_x(data->ob);
|
||||
|
||||
BMVert *v, *v_mirr;
|
||||
BMIter iter;
|
||||
int tot = 0;
|
||||
float dist, factor;
|
||||
|
||||
if (use_mirror)
|
||||
ED_strands_mirror_cache_begin(data->edit, 0, false, false, hair_use_mirror_topology(data->ob));
|
||||
|
||||
BM_ITER_MESH(v, &iter, bm, BM_VERTS_OF_MESH) {
|
||||
if (!hair_test_vertex_inside_circle(&data->vc, data->mval, radsq, v, &dist))
|
||||
continue;
|
||||
|
||||
factor = 1.0f - dist / rad;
|
||||
if (factor > threshold) {
|
||||
cb(data, userdata, v, factor);
|
||||
++tot;
|
||||
|
||||
if (use_mirror) {
|
||||
v_mirr = ED_strands_mirror_get(data->edit, v);
|
||||
if (v_mirr)
|
||||
cb(data, userdata, v_mirr, factor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* apply mirror */
|
||||
if (use_mirror)
|
||||
ED_strands_mirror_cache_end(data->edit);
|
||||
|
||||
return tot;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
typedef void (*EdgeToolCb)(HairToolData *data, void *userdata, BMVert *v1, BMVert *v2, float factor, float edge_param);
|
||||
|
||||
static int hair_tool_apply_strand_edges(HairToolData *data, EdgeToolCb cb, void *userdata, BMVert *root)
|
||||
{
|
||||
Scene *scene = data->scene;
|
||||
Brush *brush = data->settings->brush;
|
||||
const float rad = BKE_brush_size_get(scene, brush);
|
||||
const float radsq = rad*rad;
|
||||
const float threshold = 0.0f; /* XXX could be useful, is it needed? */
|
||||
const bool use_mirror = hair_use_mirror_x(data->ob);
|
||||
|
||||
BMVert *v, *v_mirr, *vprev, *vprev_mirr;
|
||||
BMIter iter;
|
||||
int k;
|
||||
int tot = 0;
|
||||
|
||||
BM_ITER_STRANDS_ELEM_INDEX(v, &iter, root, BM_VERTS_OF_STRAND, k) {
|
||||
if (use_mirror)
|
||||
v_mirr = ED_strands_mirror_get(data->edit, v);
|
||||
|
||||
if (k > 0) {
|
||||
float dist, lambda;
|
||||
|
||||
if (hair_test_edge_inside_circle(&data->vc, data->mval, radsq, vprev, v, &dist, &lambda)) {
|
||||
float factor = 1.0f - dist / rad;
|
||||
if (factor > threshold) {
|
||||
cb(data, userdata, vprev, v, factor, lambda);
|
||||
++tot;
|
||||
|
||||
if (use_mirror) {
|
||||
if (vprev_mirr && v_mirr)
|
||||
cb(data, userdata, vprev_mirr, v_mirr, factor, lambda);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
vprev = v;
|
||||
vprev_mirr = v_mirr;
|
||||
}
|
||||
|
||||
return tot;
|
||||
}
|
||||
|
||||
/* apply tool to vertices of edges inside the filter area,
|
||||
* using the closest edge point for weighting
|
||||
*/
|
||||
static int hair_tool_apply_edge(HairToolData *data, EdgeToolCb cb, void *userdata)
|
||||
{
|
||||
BMesh *bm = data->edit->base.bm;
|
||||
BMVert *root;
|
||||
BMIter iter;
|
||||
int tot = 0;
|
||||
|
||||
if (hair_use_mirror_x(data->ob))
|
||||
ED_strands_mirror_cache_begin(data->edit, 0, false, false, hair_use_mirror_topology(data->ob));
|
||||
|
||||
BM_ITER_STRANDS(root, &iter, bm, BM_STRANDS_OF_MESH) {
|
||||
tot += hair_tool_apply_strand_edges(data, cb, userdata, root);
|
||||
}
|
||||
|
||||
/* apply mirror */
|
||||
if (hair_use_mirror_x(data->ob))
|
||||
ED_strands_mirror_cache_end(data->edit);
|
||||
|
||||
return tot;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
typedef struct CombData {
|
||||
float power;
|
||||
} CombData;
|
||||
|
||||
static void UNUSED_FUNCTION(hair_vertex_comb)(HairToolData *data, void *userdata, BMVert *v, float factor)
|
||||
{
|
||||
CombData *combdata = userdata;
|
||||
|
||||
float combfactor = powf(factor, combdata->power);
|
||||
|
||||
madd_v3_v3fl(v->co, data->delta, combfactor);
|
||||
}
|
||||
|
||||
/* Edge-based combing tool:
|
||||
* Unlike the vertex tool (which simply displaces vertices), the edge tool
|
||||
* adjusts edge orientations to follow the stroke direction.
|
||||
*/
|
||||
static void hair_edge_comb(HairToolData *data, void *userdata, BMVert *v1, BMVert *v2, float factor, float UNUSED(edge_param))
|
||||
{
|
||||
CombData *combdata = userdata;
|
||||
float strokedir[3], edge[3], edgedir[3], strokelen, edgelen;
|
||||
float edge_proj[3];
|
||||
|
||||
float combfactor = powf(factor, combdata->power);
|
||||
float effect;
|
||||
|
||||
strokelen = normalize_v3_v3(strokedir, data->delta);
|
||||
|
||||
sub_v3_v3v3(edge, v2->co, v1->co);
|
||||
edgelen = normalize_v3_v3(edgedir, edge);
|
||||
if (edgelen == 0.0f)
|
||||
return;
|
||||
|
||||
/* This factor prevents sudden changes in direction with small stroke lengths.
|
||||
* The arctan maps the 0..inf range of the length ratio to 0..1 smoothly.
|
||||
*/
|
||||
effect = atan(strokelen / edgelen * 4.0f / (0.5f*M_PI));
|
||||
|
||||
mul_v3_v3fl(edge_proj, strokedir, edgelen);
|
||||
|
||||
interp_v3_v3v3(edge, edge, edge_proj, combfactor * effect);
|
||||
|
||||
add_v3_v3v3(v2->co, v1->co, edge);
|
||||
}
|
||||
|
||||
|
||||
BLI_INLINE void construct_m4_loc_nor_tan(float mat[4][4], const float loc[3], const float nor[3], const float tang[3])
|
||||
{
|
||||
float cotang[3];
|
||||
|
||||
cross_v3_v3v3(cotang, nor, tang);
|
||||
|
||||
copy_v3_v3(mat[0], tang);
|
||||
copy_v3_v3(mat[1], cotang);
|
||||
copy_v3_v3(mat[2], nor);
|
||||
copy_v3_v3(mat[3], loc);
|
||||
mat[0][3] = 0.0f;
|
||||
mat[1][3] = 0.0f;
|
||||
mat[2][3] = 0.0f;
|
||||
mat[3][3] = 1.0f;
|
||||
}
|
||||
|
||||
static void grow_hair(BMEditStrands *edit, MeshSample *sample)
|
||||
{
|
||||
BMesh *bm = edit->base.bm;
|
||||
DerivedMesh *dm = edit->root_dm;
|
||||
const float len = 1.5f;
|
||||
|
||||
float root_mat[4][4];
|
||||
BMVert *root, *v;
|
||||
BMIter iter;
|
||||
int i;
|
||||
|
||||
{
|
||||
float co[3], nor[3], tang[3];
|
||||
BKE_mesh_sample_eval(dm, sample, co, nor, tang);
|
||||
construct_m4_loc_nor_tan(root_mat, co, nor, tang);
|
||||
}
|
||||
|
||||
root = BM_strands_create(bm, 5, true);
|
||||
|
||||
BM_elem_meshsample_data_named_set(&bm->vdata, root, CD_MSURFACE_SAMPLE, CD_HAIR_ROOT_LOCATION, sample);
|
||||
|
||||
BM_ITER_STRANDS_ELEM_INDEX(v, &iter, root, BM_VERTS_OF_STRAND, i) {
|
||||
float co[3];
|
||||
|
||||
co[0] = co[1] = 0.0f;
|
||||
co[2] = len * (float)i / (float)(len - 1);
|
||||
|
||||
mul_m4_v3(root_mat, co);
|
||||
|
||||
copy_v3_v3(v->co, co);
|
||||
}
|
||||
|
||||
BM_mesh_elem_index_ensure(bm, BM_ALL);
|
||||
}
|
||||
|
||||
static bool hair_add_ray_cb(void *vdata, void *UNUSED(thread_ctx), float ray_start[3], float ray_end[3])
|
||||
{
|
||||
HairToolData *data = vdata;
|
||||
ViewContext *vc = &data->vc;
|
||||
|
||||
ED_view3d_win_to_segment(vc->ar, vc->v3d, data->mval, ray_start, ray_end, true);
|
||||
|
||||
mul_m4_v3(data->imat, ray_start);
|
||||
mul_m4_v3(data->imat, ray_end);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool hair_get_surface_sample(HairToolData *data, MeshSample *sample)
|
||||
{
|
||||
DerivedMesh *dm = data->edit->root_dm;
|
||||
|
||||
MeshSampleGenerator *gen;
|
||||
bool ok;
|
||||
|
||||
gen = BKE_mesh_sample_gen_surface_raycast(NULL, NULL, hair_add_ray_cb, data);
|
||||
BKE_mesh_sample_generator_bind(gen, dm);
|
||||
ok = BKE_mesh_sample_generate(gen, sample);
|
||||
BKE_mesh_sample_free_generator(gen);
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
static bool hair_add(HairToolData *data)
|
||||
{
|
||||
MeshSample sample;
|
||||
|
||||
if (!hair_get_surface_sample(data, &sample))
|
||||
return false;
|
||||
|
||||
grow_hair(data->edit, &sample);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool hair_brush_step(HairToolData *data)
|
||||
{
|
||||
Brush *brush = data->settings->brush;
|
||||
BrushHairTool hair_tool = brush->hair_tool;
|
||||
BMEditStrands *edit = data->edit;
|
||||
int tot = 0;
|
||||
|
||||
switch (hair_tool) {
|
||||
case HAIR_TOOL_COMB: {
|
||||
CombData combdata;
|
||||
combdata.power = (brush->alpha - 0.5f) * 2.0f;
|
||||
if (combdata.power < 0.0f)
|
||||
combdata.power = 1.0f - 9.0f * combdata.power;
|
||||
else
|
||||
combdata.power = 1.0f - combdata.power;
|
||||
|
||||
tot = hair_tool_apply_edge(data, hair_edge_comb, &combdata);
|
||||
break;
|
||||
}
|
||||
case HAIR_TOOL_CUT:
|
||||
break;
|
||||
case HAIR_TOOL_LENGTH:
|
||||
break;
|
||||
case HAIR_TOOL_PUFF:
|
||||
break;
|
||||
case HAIR_TOOL_ADD:
|
||||
if (hair_add(data))
|
||||
edit->flag |= BM_STRANDS_DIRTY_SEGLEN;
|
||||
break;
|
||||
case HAIR_TOOL_SMOOTH:
|
||||
break;
|
||||
case HAIR_TOOL_WEIGHT:
|
||||
break;
|
||||
}
|
||||
|
||||
return tot > 0;
|
||||
}
|
195
source/blender/editors/hair/hair_undo.c
Normal file
@@ -0,0 +1,195 @@
|
||||
/*
|
||||
* ***** BEGIN GPL LICENSE BLOCK *****
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* The Original Code is Copyright (C) Blender Foundation
|
||||
* All rights reserved.
|
||||
*
|
||||
* The Original Code is: all of this file.
|
||||
*
|
||||
* Contributor(s): Lukas Toenne
|
||||
*
|
||||
* ***** END GPL LICENSE BLOCK *****
|
||||
*/
|
||||
|
||||
/** \file blender/editors/hair/hair_undo.c
|
||||
* \ingroup edhair
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
#include "DNA_mesh_types.h"
|
||||
#include "DNA_meshdata_types.h"
|
||||
#include "DNA_object_types.h"
|
||||
#include "DNA_scene_types.h"
|
||||
#include "DNA_view3d_types.h"
|
||||
|
||||
#include "BKE_context.h"
|
||||
#include "BKE_DerivedMesh.h"
|
||||
#include "BKE_editstrands.h"
|
||||
#include "BKE_key.h"
|
||||
#include "BKE_mesh.h"
|
||||
#include "BKE_mesh_sample.h"
|
||||
|
||||
#include "ED_physics.h"
|
||||
#include "ED_util.h"
|
||||
|
||||
#include "bmesh.h"
|
||||
|
||||
#include "hair_intern.h"
|
||||
|
||||
static void *strands_get_edit(bContext *C)
|
||||
{
|
||||
Object *obact = CTX_data_active_object(C);
|
||||
const int mode_flag = OB_MODE_HAIR_EDIT;
|
||||
const bool is_mode_set = ((obact->mode & mode_flag) != 0);
|
||||
|
||||
if (obact && is_mode_set) {
|
||||
BMEditStrands *edit = BKE_editstrands_from_object(obact);
|
||||
return edit;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
typedef struct UndoStrands {
|
||||
Mesh me; /* Mesh supports all the customdata we need, easiest way to implement undo storage */
|
||||
int selectmode;
|
||||
|
||||
/** \note
|
||||
* this isn't a prefect solution, if you edit keys and change shapes this works well (fixing [#32442]),
|
||||
* but editing shape keys, going into object mode, removing or changing their order,
|
||||
* then go back into editmode and undo will give issues - where the old index will be out of sync
|
||||
* with the new object index.
|
||||
*
|
||||
* There are a few ways this could be made to work but for now its a known limitation with mixing
|
||||
* object and editmode operations - Campbell */
|
||||
int shapenr;
|
||||
} UndoStrands;
|
||||
|
||||
/* undo simply makes copies of a bmesh */
|
||||
static void *strands_edit_to_undo(void *editv, void *UNUSED(obdata))
|
||||
{
|
||||
BMEditStrands *edit = editv;
|
||||
BMesh *bm = edit->base.bm;
|
||||
// Mesh *obme = obdata;
|
||||
struct BMeshToMeshParams params = {0};
|
||||
|
||||
UndoStrands *undo = MEM_callocN(sizeof(UndoStrands), "undo Strands");
|
||||
|
||||
/* make sure shape keys work */
|
||||
// um->me.key = obme->key ? BKE_key_copy_nolib(obme->key) : NULL;
|
||||
|
||||
/* BM_mesh_validate(em->bm); */ /* for troubleshooting */
|
||||
|
||||
params.cd_mask_extra = CD_MASK_STRANDS;
|
||||
BM_mesh_bm_to_me(bm, &undo->me, ¶ms);
|
||||
|
||||
undo->selectmode = bm->selectmode;
|
||||
undo->shapenr = bm->shapenr;
|
||||
|
||||
return undo;
|
||||
}
|
||||
|
||||
static void strands_undo_to_edit(void *undov, void *editv, void *UNUSED(obdata))
|
||||
{
|
||||
UndoStrands *undo = undov;
|
||||
BMEditStrands *edit = editv, *edit_tmp;
|
||||
Object *ob = edit->base.ob;
|
||||
DerivedMesh *dm = edit->root_dm;
|
||||
BMesh *bm;
|
||||
// Key *key = ((Mesh *) obdata)->key;
|
||||
struct BMeshFromMeshParams params = {0};
|
||||
|
||||
const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_ME(&undo->me);
|
||||
|
||||
edit->base.bm->shapenr = undo->shapenr;
|
||||
|
||||
bm = BM_mesh_create(&allocsize,
|
||||
&((struct BMeshCreateParams){.use_toolflags = false,}));
|
||||
params.cd_mask_extra = CD_MASK_STRANDS_BMESH;
|
||||
params.active_shapekey = undo->shapenr;
|
||||
BM_mesh_bm_from_me(bm, &undo->me, ¶ms);
|
||||
|
||||
/* note: have to create the new edit before freeing the old one,
|
||||
* because it owns the root_dm and we have to copy it before
|
||||
* it gets released when freeing the old edit.
|
||||
*/
|
||||
edit_tmp = BKE_editstrands_create(bm, dm);
|
||||
BKE_editstrands_free(edit);
|
||||
*edit = *edit_tmp;
|
||||
|
||||
bm->selectmode = undo->selectmode;
|
||||
edit->base.ob = ob;
|
||||
|
||||
#if 0
|
||||
/* T35170: Restore the active key on the RealMesh. Otherwise 'fake' offset propagation happens
|
||||
* if the active is a basis for any other. */
|
||||
if (key && (key->type == KEY_RELATIVE)) {
|
||||
/* Since we can't add, remove or reorder keyblocks in editmode, it's safe to assume
|
||||
* shapenr from restored bmesh and keyblock indices are in sync. */
|
||||
const int kb_act_idx = ob->shapenr - 1;
|
||||
|
||||
/* If it is, let's patch the current mesh key block to its restored value.
|
||||
* Else, the offsets won't be computed and it won't matter. */
|
||||
if (BKE_keyblock_is_basis(key, kb_act_idx)) {
|
||||
KeyBlock *kb_act = BLI_findlink(&key->block, kb_act_idx);
|
||||
|
||||
if (kb_act->totelem != undo->me.totvert) {
|
||||
/* The current mesh has some extra/missing verts compared to the undo, adjust. */
|
||||
MEM_SAFE_FREE(kb_act->data);
|
||||
kb_act->data = MEM_mallocN((size_t)(key->elemsize * bm->totvert), __func__);
|
||||
kb_act->totelem = undo->me.totvert;
|
||||
}
|
||||
|
||||
BKE_keyblock_update_from_mesh(&undo->me, kb_act);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
ob->shapenr = undo->shapenr;
|
||||
|
||||
MEM_freeN(edit_tmp);
|
||||
}
|
||||
|
||||
static void strands_free_undo(void *undov)
|
||||
{
|
||||
UndoStrands *undo = undov;
|
||||
|
||||
if (undo->me.key) {
|
||||
BKE_key_free(undo->me.key);
|
||||
MEM_freeN(undo->me.key);
|
||||
}
|
||||
|
||||
BKE_mesh_free(&undo->me);
|
||||
MEM_freeN(undo);
|
||||
}
|
||||
|
||||
/* and this is all the undo system needs to know */
|
||||
void undo_push_strands(bContext *C, const char *name)
|
||||
{
|
||||
/* edit->ob gets out of date and crashes on mesh undo,
|
||||
* this is an easy way to ensure its OK
|
||||
* though we could investigate the matter further. */
|
||||
Object *obact = CTX_data_active_object(C);
|
||||
BMEditStrands *edit = BKE_editstrands_from_object(obact);
|
||||
edit->base.ob = obact;
|
||||
|
||||
undo_editmode_push(C, name, CTX_data_active_object, strands_get_edit, strands_free_undo, strands_undo_to_edit, strands_edit_to_undo, NULL);
|
||||
}
|
@@ -165,6 +165,27 @@ extern char datatoc_twist_png[];
|
||||
extern int datatoc_vertexdraw_png_size;
|
||||
extern char datatoc_vertexdraw_png[];
|
||||
|
||||
extern int datatoc_haircomb_png_size;
|
||||
extern char datatoc_haircomb_png[];
|
||||
|
||||
extern int datatoc_haircut_png_size;
|
||||
extern char datatoc_haircut_png[];
|
||||
|
||||
extern int datatoc_hairlength_png_size;
|
||||
extern char datatoc_hairlength_png[];
|
||||
|
||||
extern int datatoc_hairpuff_png_size;
|
||||
extern char datatoc_hairpuff_png[];
|
||||
|
||||
extern int datatoc_hairadd_png_size;
|
||||
extern char datatoc_hairadd_png[];
|
||||
|
||||
extern int datatoc_hairsmooth_png_size;
|
||||
extern char datatoc_hairsmooth_png[];
|
||||
|
||||
extern int datatoc_hairweight_png_size;
|
||||
extern char datatoc_hairweight_png[];
|
||||
|
||||
/* Matcap files */
|
||||
|
||||
extern int datatoc_mc01_jpg_size;
|
||||
|
@@ -35,9 +35,13 @@
|
||||
struct bContext;
|
||||
struct ReportList;
|
||||
struct wmKeyConfig;
|
||||
struct ViewContext;
|
||||
struct rcti;
|
||||
|
||||
struct Main;
|
||||
struct Scene;
|
||||
struct Object;
|
||||
struct BMEditStrands;
|
||||
|
||||
/* particle_edit.c */
|
||||
int PE_poll(struct bContext *C);
|
||||
@@ -56,5 +60,28 @@ void ED_rigidbody_constraint_remove(struct Main *bmain, struct Scene *scene, str
|
||||
void ED_operatortypes_physics(void);
|
||||
void ED_keymap_physics(struct wmKeyConfig *keyconf);
|
||||
|
||||
/* hair edit */
|
||||
void undo_push_strands(struct bContext *C, const char *name);
|
||||
|
||||
void ED_strands_mirror_cache_begin_ex(struct BMEditStrands *edit, const int axis,
|
||||
const bool use_self, const bool use_select,
|
||||
const bool use_topology, float maxdist, int *r_index);
|
||||
void ED_strands_mirror_cache_begin(struct BMEditStrands *edit, const int axis,
|
||||
const bool use_self, const bool use_select,
|
||||
const bool use_topology);
|
||||
void ED_strands_mirror_apply(struct BMEditStrands *edit, const int sel_from, const int sel_to);
|
||||
struct BMVert *ED_strands_mirror_get(struct BMEditStrands *edit, struct BMVert *v);
|
||||
struct BMEdge *ED_strands_mirror_get_edge(struct BMEditStrands *edit, struct BMEdge *e);
|
||||
void ED_strands_mirror_cache_clear(struct BMEditStrands *edit, struct BMVert *v);
|
||||
void ED_strands_mirror_cache_end(struct BMEditStrands *edit);
|
||||
|
||||
int ED_hair_mouse_select(struct bContext *C, const int mval[2], bool extend, bool deselect, bool toggle);
|
||||
int ED_hair_border_select(struct bContext *C, struct rcti *rect, bool select, bool extend);
|
||||
int ED_hair_circle_select(struct bContext *C, bool select, const int mval[2], float radius);
|
||||
int ED_hair_lasso_select(struct bContext *C, const int mcoords[][2], short moves, bool extend, bool select);
|
||||
|
||||
void ED_operatortypes_hair(void);
|
||||
void ED_keymap_hair(struct wmKeyConfig *keyconf);
|
||||
|
||||
#endif /* __ED_PHYSICS_H__ */
|
||||
|
||||
|
@@ -32,6 +32,8 @@
|
||||
#define __ED_UTIL_H__
|
||||
|
||||
struct bContext;
|
||||
struct PackedFile;
|
||||
struct ScrArea;
|
||||
struct SpaceLink;
|
||||
struct wmOperator;
|
||||
struct wmOperatorType;
|
||||
@@ -72,6 +74,7 @@ bool ED_undo_is_valid(const struct bContext *C, const char *undoname);
|
||||
|
||||
/* undo_editmode.c */
|
||||
void undo_editmode_push(struct bContext *C, const char *name,
|
||||
struct Object *(*get_object)(const struct bContext * C),
|
||||
void * (*getdata)(struct bContext *C),
|
||||
void (*freedata)(void *),
|
||||
void (*to_editmode)(void *, void *, void *),
|
||||
|
@@ -977,6 +977,13 @@ DEF_ICON(BRUSH_TEXMASK)
|
||||
DEF_ICON(BRUSH_THUMB)
|
||||
DEF_ICON(BRUSH_ROTATE)
|
||||
DEF_ICON(BRUSH_VERTEXDRAW)
|
||||
DEF_ICON(BRUSH_HAIR_COMB)
|
||||
DEF_ICON(BRUSH_HAIR_CUT)
|
||||
DEF_ICON(BRUSH_HAIR_LENGTH)
|
||||
DEF_ICON(BRUSH_HAIR_PUFF)
|
||||
DEF_ICON(BRUSH_HAIR_ADD)
|
||||
DEF_ICON(BRUSH_HAIR_SMOOTH)
|
||||
DEF_ICON(BRUSH_HAIR_WEIGHT)
|
||||
|
||||
/* Matcaps */
|
||||
DEF_ICON(MATCAP_01)
|
||||
|
@@ -415,6 +415,13 @@ static void init_brush_icons(void)
|
||||
INIT_BRUSH_ICON(ICON_BRUSH_THUMB, thumb);
|
||||
INIT_BRUSH_ICON(ICON_BRUSH_ROTATE, twist);
|
||||
INIT_BRUSH_ICON(ICON_BRUSH_VERTEXDRAW, vertexdraw);
|
||||
INIT_BRUSH_ICON(ICON_BRUSH_HAIR_COMB, haircomb);
|
||||
INIT_BRUSH_ICON(ICON_BRUSH_HAIR_CUT, haircut);
|
||||
INIT_BRUSH_ICON(ICON_BRUSH_HAIR_LENGTH, hairlength);
|
||||
INIT_BRUSH_ICON(ICON_BRUSH_HAIR_PUFF, hairpuff);
|
||||
INIT_BRUSH_ICON(ICON_BRUSH_HAIR_ADD, hairadd);
|
||||
INIT_BRUSH_ICON(ICON_BRUSH_HAIR_SMOOTH, hairsmooth);
|
||||
INIT_BRUSH_ICON(ICON_BRUSH_HAIR_WEIGHT, hairweight);
|
||||
|
||||
#undef INIT_BRUSH_ICON
|
||||
}
|
||||
@@ -1209,6 +1216,8 @@ static int ui_id_brush_get_icon(const bContext *C, ID *id)
|
||||
mode = OB_MODE_VERTEX_PAINT;
|
||||
else if (ob->mode & OB_MODE_TEXTURE_PAINT)
|
||||
mode = OB_MODE_TEXTURE_PAINT;
|
||||
else if (ob->mode & OB_MODE_HAIR_EDIT)
|
||||
mode = OB_MODE_HAIR_EDIT;
|
||||
}
|
||||
else if ((sima = CTX_wm_space_image(C)) &&
|
||||
(sima->mode == SI_MODE_PAINT))
|
||||
@@ -1229,6 +1238,10 @@ static int ui_id_brush_get_icon(const bContext *C, ID *id)
|
||||
items = rna_enum_brush_image_tool_items;
|
||||
tool = br->imagepaint_tool;
|
||||
}
|
||||
else if (mode == OB_MODE_HAIR_EDIT) {
|
||||
items = brush_hair_tool_items;
|
||||
tool = br->hair_tool;
|
||||
}
|
||||
|
||||
if (!items || !RNA_enum_icon_from_value(items, tool, &id->icon_id))
|
||||
id->icon_id = 0;
|
||||
|
@@ -665,5 +665,5 @@ void undo_push_mesh(bContext *C, const char *name)
|
||||
BMEditMesh *em = BKE_editmesh_from_object(obedit);
|
||||
em->ob = obedit;
|
||||
|
||||
undo_editmode_push(C, name, getEditMesh, free_undo, undoMesh_to_editbtMesh, editbtMesh_to_undoMesh, NULL);
|
||||
undo_editmode_push(C, name, CTX_data_edit_object, getEditMesh, free_undo, undoMesh_to_editbtMesh, editbtMesh_to_undoMesh, NULL);
|
||||
}
|
||||
|
@@ -752,5 +752,5 @@ static void *get_data(bContext *C)
|
||||
/* this is undo system for MetaBalls */
|
||||
void undo_push_mball(bContext *C, const char *name)
|
||||
{
|
||||
undo_editmode_push(C, name, get_data, free_undoMball, undoMball_to_editMball, editMball_to_undoMball, NULL);
|
||||
undo_editmode_push(C, name, CTX_data_edit_object, get_data, free_undoMball, undoMball_to_editMball, editMball_to_undoMball, NULL);
|
||||
}
|
||||
|
@@ -49,6 +49,7 @@ set(SRC
|
||||
object_edit.c
|
||||
object_facemap_ops.c
|
||||
object_group.c
|
||||
object_hair.c
|
||||
object_hook.c
|
||||
object_lattice.c
|
||||
object_lod.c
|
||||
|
@@ -1411,7 +1411,7 @@ static EnumPropertyItem *object_mode_set_itemsf(bContext *C, PointerRNA *UNUSED(
|
||||
(input->value == OB_MODE_POSE && (ob->type == OB_ARMATURE)) ||
|
||||
(input->value == OB_MODE_PARTICLE_EDIT && use_mode_particle_edit) ||
|
||||
(ELEM(input->value, OB_MODE_SCULPT, OB_MODE_VERTEX_PAINT,
|
||||
OB_MODE_WEIGHT_PAINT, OB_MODE_TEXTURE_PAINT) && (ob->type == OB_MESH)) ||
|
||||
OB_MODE_WEIGHT_PAINT, OB_MODE_TEXTURE_PAINT, OB_MODE_HAIR_EDIT) && (ob->type == OB_MESH)) ||
|
||||
(input->value == OB_MODE_OBJECT))
|
||||
{
|
||||
RNA_enum_item_add(&item, &totitem, input);
|
||||
@@ -1453,6 +1453,8 @@ static const char *object_mode_op_string(int mode)
|
||||
return "PAINT_OT_texture_paint_toggle";
|
||||
if (mode == OB_MODE_PARTICLE_EDIT)
|
||||
return "PARTICLE_OT_particle_edit_toggle";
|
||||
if (mode == OB_MODE_HAIR_EDIT)
|
||||
return "HAIR_OT_hair_edit_toggle";
|
||||
if (mode == OB_MODE_POSE)
|
||||
return "OBJECT_OT_posemode_toggle";
|
||||
if (mode == OB_MODE_GPENCIL)
|
||||
@@ -1474,7 +1476,7 @@ static bool object_mode_compat_test(Object *ob, ObjectMode mode)
|
||||
switch (ob->type) {
|
||||
case OB_MESH:
|
||||
if (mode & (OB_MODE_EDIT | OB_MODE_SCULPT | OB_MODE_VERTEX_PAINT | OB_MODE_WEIGHT_PAINT |
|
||||
OB_MODE_TEXTURE_PAINT | OB_MODE_PARTICLE_EDIT))
|
||||
OB_MODE_TEXTURE_PAINT | OB_MODE_PARTICLE_EDIT | OB_MODE_HAIR_EDIT))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
117
source/blender/editors/object/object_hair.c
Normal file
@@ -0,0 +1,117 @@
|
||||
/*
|
||||
* ***** 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/object/object_hair.c
|
||||
* \ingroup edobj
|
||||
*/
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
#include "DNA_hair_types.h"
|
||||
#include "DNA_modifier_types.h"
|
||||
#include "DNA_object_types.h"
|
||||
#include "DNA_scene_types.h"
|
||||
|
||||
#include "RNA_access.h"
|
||||
#include "RNA_define.h"
|
||||
|
||||
#include "BKE_context.h"
|
||||
#include "BKE_DerivedMesh.h"
|
||||
#include "BKE_hair.h"
|
||||
#include "DEG_depsgraph.h"
|
||||
|
||||
#include "ED_object.h"
|
||||
|
||||
#include "WM_api.h"
|
||||
#include "WM_types.h"
|
||||
|
||||
#include "object_intern.h"
|
||||
|
||||
/************************ Hair Follicle Generation Operator *********************/
|
||||
|
||||
static int hair_follicles_generate_poll(bContext *C)
|
||||
{
|
||||
return edit_modifier_poll_generic(C, &RNA_HairModifier, 0);
|
||||
}
|
||||
|
||||
static int hair_follicles_generate_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
Scene *scene = CTX_data_scene(C);
|
||||
Object *ob = ED_object_active_context(C);
|
||||
HairModifierData *hmd = (HairModifierData *)edit_modifier_property_get(op, ob, eModifierType_Hair);
|
||||
EvaluationContext eval_ctx;
|
||||
CTX_data_eval_ctx(C, &eval_ctx);
|
||||
|
||||
if (!hmd)
|
||||
return OPERATOR_CANCELLED;
|
||||
|
||||
CustomDataMask mask = CD_MASK_BAREMESH;
|
||||
DerivedMesh *scalp = mesh_get_derived_final(&eval_ctx, scene, ob, mask);
|
||||
if (!scalp)
|
||||
return OPERATOR_CANCELLED;
|
||||
|
||||
int count = RNA_int_get(op->ptr, "count");
|
||||
unsigned int seed = RNA_int_get(op->ptr, "seed");
|
||||
|
||||
BKE_hair_follicles_generate(hmd->hair, scalp, count, seed);
|
||||
|
||||
DEG_id_tag_update(&ob->id, OB_RECALC_DATA);
|
||||
WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob);
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
static int hair_follicles_generate_invoke(bContext *C, wmOperator *op, const wmEvent *event)
|
||||
{
|
||||
if (edit_modifier_invoke_properties(C, op)) {
|
||||
return WM_operator_props_popup_confirm(C, op, event);
|
||||
}
|
||||
else {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
}
|
||||
|
||||
void OBJECT_OT_hair_follicles_generate(wmOperatorType *ot)
|
||||
{
|
||||
/* identifiers */
|
||||
ot->name = "Generate Hair Follicles";
|
||||
ot->description = "Generate hair follicle data";
|
||||
ot->idname = "OBJECT_OT_hair_follicles_generate";
|
||||
|
||||
/* api callbacks */
|
||||
ot->poll = hair_follicles_generate_poll;
|
||||
ot->invoke = hair_follicles_generate_invoke;
|
||||
ot->exec = hair_follicles_generate_exec;
|
||||
|
||||
/* flags */
|
||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL;
|
||||
|
||||
edit_modifier_properties(ot);
|
||||
RNA_def_int(ot->srna, "count", 1000, 0, INT_MAX, "Count", "Number of hair follicles to generate", 1, 1000000);
|
||||
RNA_def_int(ot->srna, "seed", 0, 0, INT_MAX, "Seed", "Seed value for randomization", 0, INT_MAX);
|
||||
}
|
@@ -286,5 +286,8 @@ void TRANSFORM_OT_vertex_random(struct wmOperatorType *ot);
|
||||
void OBJECT_OT_data_transfer(struct wmOperatorType *ot);
|
||||
void OBJECT_OT_datalayout_transfer(struct wmOperatorType *ot);
|
||||
|
||||
/* object_hair.c */
|
||||
void OBJECT_OT_hair_follicles_generate(struct wmOperatorType *ot);
|
||||
|
||||
#endif /* __OBJECT_INTERN_H__ */
|
||||
|
||||
|
@@ -982,6 +982,6 @@ static void *get_editlatt(bContext *C)
|
||||
/* and this is all the undo system needs to know */
|
||||
void undo_push_lattice(bContext *C, const char *name)
|
||||
{
|
||||
undo_editmode_push(C, name, get_editlatt, free_undoLatt, undoLatt_to_editLatt, editLatt_to_undoLatt, validate_undoLatt);
|
||||
undo_editmode_push(C, name, CTX_data_edit_object, get_editlatt, free_undoLatt, undoLatt_to_editLatt, editLatt_to_undoLatt, validate_undoLatt);
|
||||
}
|
||||
|
||||
|
@@ -260,6 +260,8 @@ void ED_operatortypes_object(void)
|
||||
WM_operatortype_append(OBJECT_OT_data_transfer);
|
||||
WM_operatortype_append(OBJECT_OT_datalayout_transfer);
|
||||
WM_operatortype_append(OBJECT_OT_surfacedeform_bind);
|
||||
|
||||
WM_operatortype_append(OBJECT_OT_hair_follicles_generate);
|
||||
}
|
||||
|
||||
void ED_operatormacros_object(void)
|
||||
|
@@ -72,7 +72,7 @@ const char *screen_context_dir[] = {
|
||||
"visible_pose_bones", "selected_pose_bones", "active_bone", "active_pose_bone",
|
||||
"active_base", "active_object", "object", "edit_object",
|
||||
"sculpt_object", "vertex_paint_object", "weight_paint_object",
|
||||
"image_paint_object", "particle_edit_object",
|
||||
"image_paint_object", "particle_edit_object", "hair_edit_object",
|
||||
"sequences", "selected_sequences", "selected_editable_sequences", /* sequencer */
|
||||
"gpencil_data", "gpencil_data_owner", /* grease pencil data */
|
||||
"visible_gpencil_layers", "editable_gpencil_layers", "editable_gpencil_strokes",
|
||||
@@ -399,6 +399,12 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult
|
||||
|
||||
return 1;
|
||||
}
|
||||
else if (CTX_data_equals(member, "hair_edit_object")) {
|
||||
if (obact && (obact->mode & OB_MODE_HAIR_EDIT))
|
||||
CTX_data_id_pointer_set(result, &obact->id);
|
||||
|
||||
return 1;
|
||||
}
|
||||
else if (CTX_data_equals(member, "sequences")) {
|
||||
Editing *ed = BKE_sequencer_editing_get(scene, false);
|
||||
if (ed) {
|
||||
|