1
1

Compare commits

...

381 Commits

Author SHA1 Message Date
dc2d841b7c Merge branch 'blender2.8' into hair_guides 2018-08-12 12:52:49 +01:00
27b28e437d Merge branch 'tmp_hair_curves' into hair_guides 2018-08-12 11:05:36 +01:00
f854fefa17 Separate buffer texture for hair index for each vertex.
This is needed for allowing variable length hair strands, where the hair index can not
simply be calculated from the vertex index based on a fixed-length strand.
2018-08-12 10:33:21 +01:00
b60dcd85f9 Fix the hairStrandsRes uniform for particle hair. 2018-07-22 13:54:37 +01:00
58979947bc Correct point and element counts for particle hair. 2018-07-22 12:46:26 +01:00
1dba28221d Post merge fixes. 2018-07-15 13:07:29 +01:00
4a92042916 Post merge fixes. 2018-07-15 12:44:04 +01:00
31f371b200 Merge branch 'hair_guides' into tmp_hair_curves 2018-07-15 12:28:34 +01:00
a9be42dfa2 Merge branch 'blender2.8' into hair_guides 2018-07-15 12:16:27 +01:00
8915d9ed0d Change the uniform strand_res value to strands_len, point_len and elem_len counts. 2018-07-15 11:41:13 +01:00
47912ef922 Rename proc_buf to proc_point_buf to make it consistent with the main control point buffer. 2018-07-15 09:12:33 +01:00
51500addaa Merge branch 'hair_guides' into tmp_hair_curves 2018-07-10 07:53:09 +01:00
8a8b9d7ea9 Merge branch 'blender2.8' into hair_guides 2018-07-10 07:34:05 +01:00
a754d222ba Merge branch 'hair_guides' into tmp_hair_curves 2018-07-03 07:42:12 +01:00
08836b73fe Merge branch 'blender2.8' into hair_guides 2018-07-03 07:38:08 +01:00
54aaceaabc Construct hair strands data from hair systems (no real UV/MCol yet). 2018-07-03 07:33:38 +01:00
287968377f Extend default material function for Eevee to work with hair systems. 2018-07-03 07:32:36 +01:00
d09822c876 Fix a few minor crashes from unfinished hair shader code. 2018-07-02 08:22:04 +01:00
7ad71174fe Remove the parent interpolation hair shader and use the transform-feedback shader instead. 2018-07-02 07:42:51 +01:00
0042eb1077 Remove the mesh sample from fiber curve data.
Fiber curves are simple shapes for fibers now, not tied to a particular
place on the scalp mesh (cf. shape keys).
2018-06-30 13:01:52 +01:00
6751627797 Fix fur curve generation function. 2018-06-30 11:36:54 +01:00
487775dd87 Simple hair curve generation for testing per-follicle curves. 2018-06-30 10:56:08 +01:00
142112faec Add common ui draw function for hair display settings. 2018-06-30 10:42:39 +01:00
9b06c23dc3 Remove unused properties_hair.py file. 2018-06-30 10:20:18 +01:00
0875d8b569 Fix ui script for the hair modifier. 2018-06-30 09:51:01 +01:00
aa85cbee00 Replace parent indices and weights with single curve index.
Curves can be shared between follicles, but there will only be one
curve per follicle for defining the shape.
2018-06-30 08:02:15 +01:00
7cd45e1713 Rename "guide" curves to "fiber" curves.
Guide curves should be a higher-level concept. All fibers should have their
own curve control points, with subdivision on the GPU.
2018-06-29 16:42:29 +01:00
614868b571 Generic modifier copy already deletes target data, explicit freeing is redundant. 2018-06-29 13:32:34 +01:00
dc766bfffc Rename the fur modifier to hair modifier. 2018-06-29 13:30:11 +01:00
6af06e3c91 Merge branch 'blender2.8' into hair_guides 2018-06-26 22:23:49 +01:00
94f3cf23ec Merge branch 'blender2.8' into hair_guides 2018-06-21 18:56:13 +01:00
a927dbd17a Merge branch 'blender2.8' into hair_guides 2018-06-19 07:06:33 +01:00
cd71dd6382 Merge branch 'blender2.8' into hair_guides 2018-06-15 07:02:23 +01:00
dec92e6ba1 Backport hair drawing improvements and workbench support from the grooming branch. 2018-06-15 06:45:51 +01:00
08cdc98b54 Backport a few changes from the grooming branch.
material_index in hair system has been removed. The material/shader
for hair should be defined externally it doesn't make sense to rely
on the scalp object material slots for this.
2018-06-09 10:16:01 +01:00
2d8c39c97c Make the hair binding function more robust when no guide curves are defined. 2018-06-09 09:57:36 +01:00
34d61bfd83 Add missing include for mesh runtime. 2018-06-09 06:56:53 +01:00
b7b6f72cb5 Merge branch 'blender2.8' into hair_guides 2018-06-09 06:43:15 +01:00
4f2532e88c Use common_view_lib.glsl instead of including the ProjectionMatrix uniform directly in hair shaders. 2018-06-09 06:41:43 +01:00
7d5297305a Fix hair fiber prepass shader and add missin HAIR_SHADER define to compile it. 2018-06-06 21:34:40 +01:00
4e71eb53d1 Fix hair fiber vertex shaders and bring them closer to the particle hair shader. 2018-06-06 20:40:46 +01:00
b28c35daef Merge branch 'blender2.8' into hair_guides 2018-06-05 20:59:15 +01:00
4c2d7767cf Merge branch 'blender2.8' into hair_guides 2018-05-27 08:42:53 +01:00
38c28972c1 Add an offset to ensure hair guide curves start on the scalp in export caches. 2018-05-27 07:45:55 +01:00
d7977c7667 Properly set loop and poly indices in mesh samples where possible. 2018-05-27 07:45:28 +01:00
e288e3d368 Replace callback for loop weights with a simple array, since these are cached anyway. 2018-05-24 18:43:27 +01:00
a6dbdd5282 Extended hair follicle distribution function that allows geometry filtering. 2018-05-24 18:43:10 +01:00
4e05dd1dc5 Replace DerivedMesh with Mesh throughout hair code, 2018-05-24 17:29:38 +01:00
ba3a650dde Replace per-vertex sampling weights with per-loop for more control (e.g. face maps). 2018-05-24 17:28:53 +01:00
5a75ada839 Fix indexing for hair guide curve drawing. 2018-05-24 11:41:30 +01:00
6885b90564 Fix hair cache update flag combination, have to include dependencies regardless of cache state. 2018-05-24 10:50:59 +01:00
fe5b74c36c Only pass export cache to lower level draw functions, do export on higher levels. 2018-05-24 10:27:28 +01:00
9036a6e60b Draw mode for hair guide curves. 2018-05-24 10:26:42 +01:00
3cef615645 Handle hair cache dependencies internally during invalidation to make partial updates more flexible. 2018-05-24 10:26:42 +01:00
23cb7e81b0 Remove unused 'Axes' mode for follicle drawing. 2018-05-24 10:26:42 +01:00
4fd377990b Fix typo. 2018-05-23 09:04:11 +01:00
671ff29220 Missing forward declaration. 2018-05-22 13:26:44 +01:00
64e3744e8b Make Mesh based eval the default for mesh sampling (generators unchanged). 2018-05-22 10:39:11 +01:00
93177bce3e Don't do implicit update in the hair export cache constructor. 2018-05-22 09:48:09 +01:00
d3c5d74382 Change particle conversion for mesh samples from DerivedMesh to Mesh. 2018-05-22 05:51:19 +01:00
9c9ea7c75f Function for removing all guide curves in a hair system. 2018-05-21 14:47:56 +01:00
8a3861e5d9 Add assert in psys function to ensure DM->Mesh conversion is not forgotten. 2018-05-21 13:21:15 +01:00
b5fa901697 Cleanup: Temporarily removed all groom object type code, move it to a separate branch. 2018-05-21 10:53:45 +01:00
64f109026b Merge branch 'blender2.8' into hair_guides 2018-05-21 09:39:27 +01:00
6797d4f776 Revert "Add a hair system to particles to eventually replace the current pathcaches."
This reverts commit 3ba96b7c4d.
2018-05-19 15:59:54 +01:00
52d6f25d82 Comment on return value. 2018-05-18 16:30:17 +01:00
b2db8c07b9 More detailed API for partially updating hair export caches.
This will allow proper caching of draw data as well as optimization
of GPU buffer uploads when only guide curve deform changes.
2018-05-18 16:27:37 +01:00
3ba96b7c4d Add a hair system to particles to eventually replace the current pathcaches. 2018-05-17 09:37:23 +01:00
8a6b26cf5d Modifier copy function uses const now. 2018-05-15 08:01:58 +01:00
28b17a7fb6 Remove the simple hair guide generator, can test through the API now. 2018-05-15 08:00:05 +01:00
029ac8dc15 Merge branch 'blender2.8' into hair_guides 2018-05-15 07:20:55 +01:00
0ac09ed0da Merge branch 'blender2.8' into hair_guides 2018-05-13 10:20:05 +01:00
25a83cb3cb Fix indexing of vertex buffers for Cycles hair. 2018-05-13 09:46:31 +01:00
ec4ec90934 Render support for hair systems in Cycles. 2018-05-12 14:37:13 +01:00
383c6bc172 Also cache guide tangents and normals and fiber root positions in export data. 2018-05-12 10:42:16 +01:00
b44af40eec New HairExportCache struct to unify pre-computation of hair data for OpenGL, render, and eventual data export. 2018-05-12 09:41:58 +01:00
c155f3bc95 Cycles: split off particle data gathering into separate function.
The general particle caching "Obtain" functions can be used for any
curve-type data in objects, including the new hair system data.
2018-05-07 10:59:11 +01:00
c67adabdba Fix changed signature of applyModifier callback. 2018-05-07 09:58:29 +01:00
e7992b1279 Merge branch 'blender2.8' into hair_guides 2018-05-07 09:31:45 +01:00
d6e5d9a578 Add new modifier callbacks for switching from DerivedMesh. 2018-05-06 10:54:49 +01:00
1447fecbbf Merge branch 'blender2.8' into hair_guides 2018-05-06 10:29:52 +01:00
99b9636bf0 Depsgraph update after changing guide curves through the fur modifier. 2018-04-26 06:57:46 +01:00
298fd140f2 Make sure hair follicle binding is updated before using invalid indices in drawing. 2018-04-21 13:35:14 +01:00
58a70542e0 Merge branch 'blender2.8' into hair_guides 2018-04-21 12:36:52 +01:00
dcd9cb72f5 Test API in the fur modifier for creating custom guide curves with python.
The fur modifier is just a testbed for the underlying hair system.
The API is intended to allow specifying guide curves with python scripts
without having to worry about inconsistent state. All curves are first
defined in a python-friendly way, then applied to the hair system in one step.
2018-04-18 08:57:52 +01:00
974e55e3e6 Alternative guide curve setter function that works better for RNA. 2018-04-15 13:30:38 +01:00
e9cf9776f8 Move guide curve into a nested struct, so it can be replace in one operation. 2018-04-15 12:54:39 +01:00
25bb3669cc Disable unfinished code. 2018-04-15 11:17:07 +01:00
7e1832c8d5 Merge branch 'blender2.8' into hair_guides 2018-04-15 11:16:55 +01:00
c0c8df3f2c Initial system for generating guide curves from groom. 2018-04-15 09:30:15 +01:00
8fa55d3885 Popup for setting hair distribution operator properties. 2018-01-28 15:25:28 +00:00
471b410e43 Fix outdated usage of BLI_task_parallel_range. 2018-01-28 14:41:04 +00:00
9e56f6357f New operator for distributing hair follicles for a groom object. 2018-01-28 14:32:50 +00:00
49c19ca679 Merge branch 'blender2.8' into hair_guides 2018-01-28 13:18:10 +00:00
e15dd419d4 Depsgraph cleanup for groom curve cache eval. 2018-01-21 16:06:27 +00:00
4443f052f6 Operator for binding groom bundles to scalp regions. 2018-01-21 13:25:34 +00:00
57b3e8a137 Remove scalp binding from generic depsgraph groom updates, this should be an operator. 2018-01-21 12:49:23 +00:00
1e8139d912 Merge branch 'blender2.8' into hair_guides 2018-01-21 12:00:35 +00:00
95d8653c08 Disable rebinding during every update to prevent invalidating transform data. 2018-01-08 11:09:03 +00:00
7f2a7d97d6 Add back section edges drawing. 2018-01-08 10:57:10 +00:00
75f9c888f3 Calculate individual curves for each shape vertex.
This avoids overlap which happens on sharp turns when simply extending
the curve linearly from the center. It also simplifies the curve cache
data structure.
2018-01-08 10:47:17 +00:00
bbe8311948 Sparser edge drawing for grooms to reduce visual clutter. 2018-01-07 13:01:49 +00:00
3ee9d04db3 Shape binding for groom curves to the scalp mesh surface using face groups.
Face groups are used for now to define the origin of a groom bundle. This
is probably going to be replaced at some point with a dedicated surface
graph/mesh, but is much easier to implement and gives sufficient functionality.

When a face map is selected as the basis of a hair bundle it will update
the curve shape automatically. In the future this should perhaps be an
optional step (user operator) because it destructively changes existing
shapes.
2018-01-07 12:31:42 +00:00
1d26d50a46 Use existence of a bind value array instead of a flag to detect bound groom data. 2018-01-04 11:08:58 +00:00
22e7d5cbaf Validity flag in bundles to indicate when they are validly bound to the scalp.
This is updated through the depsgraph. Currently just checks for existing scalp
object and vertex groups, eventually will perform actual tracking of the mesh
surface to ensure the groom follows changes of the scalp mesh.
2018-01-04 09:40:56 +00:00
fb5d222217 Display the scalp vertex group as the main identifier of bundles in the list. 2018-01-04 09:14:29 +00:00
2228585912 RNA collection and UI list for groom bundles. 2018-01-04 08:57:12 +00:00
82b1143afa Merge branch 'blender2.8' into hair_guides 2018-01-03 13:17:06 +00:00
bf919ab830 Add missing context mode string for groom edit mode. 2018-01-03 13:13:10 +00:00
0206d49960 Static assert for correct strings from context mode enum.
Adding new context modes requires adding a string in CTX_data_mode_string,
but there is no error when omitting this other than panels using
incorrect contexts. The static assert should help detect simple
missing strings at least to avoid confusing errors.
2018-01-03 13:10:42 +00:00
dc34c0df3d Add missing switch break.
No effect so far for subsequent code.
2018-01-03 12:48:33 +00:00
36fac1e27c Merge branch 'blender2.8' into hair_guides 2018-01-03 10:24:05 +00:00
2a7dfcd927 Add missing editmode menu for groom objects. 2018-01-02 14:24:18 +00:00
be6965011b Use derivedFinal as scalp surface (for the time being).
Eventually DerivedMesh will be deprecated and Mesh used instead,
but until then the surface sampling algorithms use DerivedMesh.
2018-01-02 14:18:51 +00:00
8f46ae5ec9 New object pointer in groom ID to use as a "scalp" object for attaching hair. 2018-01-02 13:36:57 +00:00
359bef03df RNA and UI scripts for the hair system and draw settings in groom objects. 2018-01-02 12:54:36 +00:00
5ad90a874f Add a hair system in the groom ID block for creating renderable hairs. 2018-01-02 12:45:48 +00:00
5d17484d92 Fix transform recalc, 2D data only exists for section vertex transforms. 2018-01-02 12:44:25 +00:00
1d92d84bb1 Transform operator support for groom vertices. 2018-01-02 09:57:15 +00:00
ff381505fa Some tentative iterator macros for groom data. 2018-01-01 11:26:58 +00:00
a881edff7a Circle select implementation for groom objects. 2017-12-31 09:14:56 +00:00
a7f6ddba07 Box select implementation for groom objects. 2017-12-31 09:04:33 +00:00
160696a21d Lasso select implementation for groom object. 2017-12-31 08:59:42 +00:00
207d442496 Use a secondary cache for interpolated shape loops. 2017-12-30 15:20:17 +00:00
afdcd07b32 Mouse-picking operator for groom vertex selection. 2017-12-30 08:44:35 +00:00
71e731ef1e Curve cache for main groom bundle curves.
This stores interpolated cubic spline values for smooth curve rendering.
Local coordinate frames for sections are also computed alongside the cache.
2017-12-30 08:08:11 +00:00
ed27d3a398 Add UI script for groom data. 2017-12-30 08:03:34 +00:00
b3c4ee5266 Redraw notifier after changing groom edit sub-mode. 2017-12-27 11:04:49 +00:00
69bebcf8b5 Store section and vertex data in arrays instead of linked lists. 2017-12-25 16:20:44 +00:00
067c890b36 Simplify class name. 2017-12-25 15:30:41 +00:00
00dc4813c3 Vertex array for closed curves forming 2D groom sections. 2017-12-25 14:34:51 +00:00
1b971b0058 Depsgraph tag after adding new groom regions. 2017-12-25 11:22:53 +00:00
fe72fea762 Use the edit data for drawing when groom is in edit mode. 2017-12-25 11:18:45 +00:00
c673982b22 Select/Deselect all operator for groom vertices. 2017-12-25 10:29:02 +00:00
d1258f92f8 Use edit submode for groom overlay rendering. 2017-12-23 11:09:50 +00:00
c7414140a4 Sub-mode for groom editing, for editing different parts of the topology. 2017-12-22 14:41:41 +00:00
66f0df6907 Revert unused displist changes. 2017-12-22 12:40:14 +00:00
d70547b666 In object mode just draw simple wires for groom objects for now. 2017-12-22 11:17:33 +00:00
acfdf08ea2 Simple operator for adding a single groom curve in edit mode. 2017-12-22 11:02:08 +00:00
f70b6c96c5 Clean up after exiting groom object edit mode. 2017-12-21 11:17:59 +00:00
9660a2e2dd New draw engine for groom edit mode. 2017-12-21 11:08:53 +00:00
61fae8440d Edit mode data functions for creating, applying a freeing groom edit data. 2017-12-19 14:35:00 +00:00
b3c59d51e0 BKE_object_is_in_editmode case for groom object type. 2017-12-19 12:55:45 +00:00
da9fb8496c Proper freeing of groom data. 2017-12-19 12:01:37 +00:00
02ae16fd6e BoundBox recalc for groom. 2017-12-19 11:50:03 +00:00
e266d9d2b5 Serialization for groom internals. 2017-12-19 11:46:38 +00:00
5665c1b411 Stub edit data for groom. 2017-12-19 11:33:08 +00:00
5f41014a4c Stub displist function for groom. 2017-12-19 10:30:16 +00:00
1014b23cea Tentative DNA structs for groom internals. 2017-12-19 10:28:21 +00:00
f6c5948c5c Menu entries for adding groom objects in space_view3d. 2017-12-18 10:56:56 +00:00
6c28125efe Dummy icons for groom data. 2017-12-18 10:50:10 +00:00
eba518c040 Add groom data path to buttons context. 2017-12-18 10:25:53 +00:00
43e840b947 Merge branch 'blender2.8' into hair_guides 2017-12-18 10:19:01 +00:00
d2a75fb6dc Add basic outliner support for the groom object type. 2017-12-11 09:08:12 +00:00
3fbbef51c8 "add" operator for groom objects. 2017-12-10 12:46:11 +00:00
4e0d7349e8 writefile function for Groom. 2017-12-10 12:46:02 +00:00
56c3dbb656 Implement BKE_libblock_free_datablock for groom ID. 2017-12-10 11:52:23 +00:00
12200ce0b1 Add groom object id in a few more functions that need it. 2017-12-10 11:48:07 +00:00
91864d9bbe New RNA file for the groom ID. 2017-12-10 11:21:12 +00:00
e9a4bb925c Merge branch 'blender2.8' into hair_guides 2017-12-10 10:24:03 +00:00
7bdbc7e6f3 Add translation context and idcode entry for the new ID type. 2017-12-10 10:01:43 +00:00
7a8d3e623d Make sure MAX_LIBARRAY is large enough (also add a static assert for this). 2017-12-10 10:01:10 +00:00
047c05ed68 Add new Groom ID type to use for groom objects. 2017-12-10 09:42:45 +00:00
267f0e521e New OB_GROOM type for hair editing objects. 2017-12-07 08:37:35 +00:00
c53c6e99b3 Use material slot index instead of a direct Material pointer.
A Material pointer would be a nicer way to assign a hair material,
but unfortunately that does not work well with the context-based material
lookup of the node editor. There is no selection of an "active" modifier,
so the node editor will never display the material (unless it is placed
into a material stack as well).
2017-11-26 10:33:19 +00:00
56c1388ee4 Material pointer in hair systems. 2017-11-25 14:58:01 +00:00
5be3403a1d Simplified API for generating hair follicles with a fixed max count.
The final count is inversely proportional to the min_distance, so using that
as a user variable is difficult to control. Instead we just use the overall
count as the single variable now.

The actual final count will be less than this maximum count, because the
theoretical limit is never reached in practice. The min. distance could also
be stored as a result of the hair generation as feedback for the user.
2017-11-25 13:46:59 +00:00
08b8b4465d Merge branch 'blender2.8' into hair_guides 2017-11-25 11:17:08 +00:00
6855c5dab6 Fix strand vertex init loop, use correct stop index. 2017-11-25 10:49:51 +00:00
21c913f957 Need to call the subdiv function also for subdiv==0 to initialize vert positions. 2017-11-25 09:45:41 +00:00
2d24189369 Extend the fur generate modifier to also create guide curves. 2017-11-23 20:29:08 +00:00
f47f7383ce More meaningful flag names for hair system changes. 2017-11-23 08:55:55 +00:00
b4b07115cc Remove unused code, this has been replaced since. 2017-11-23 08:55:06 +00:00
cf3fce96b1 Fix for overflow of cell_index in the Poisson disk sample generator.
Using a single uint as combined cell index only leaves ~10 bits per
coordinate axis, which quickly leads to overflow for higher densities.
Now use 3 ints so that the sampling grid can have sufficiently small
cells.
2017-11-20 20:02:58 +00:00
ae84236ac3 Additional pass in object mode for drawing hair follicles and guide curves. 2017-11-19 17:23:49 +00:00
01968f95ed Merge branch 'blender2.8' into hair_guides 2017-11-19 10:56:04 +00:00
593fee0e7d Fix memleak from unfreed local array. 2017-11-19 10:05:17 +00:00
c6ddb6fdd9 Struct for hair draw settings.
This is outside of the hair system itself since it only affects drawing.
2017-11-19 10:00:59 +00:00
c8ef6904d1 Operator for generating hair follicles of the fur modifier. 2017-11-17 09:30:22 +00:00
a19f945476 Fix mesh sample inner loop for poisson disk generator, returned false always. 2017-11-17 08:58:49 +00:00
4276d302f6 Follicle generation function for hair systems. 2017-11-16 09:20:14 +00:00
d7ba494283 Rename "Hair" modifier to "Fur" modifier.
This will be a simple testbed for generating hair geometry.

The plan here is to use different modifiers for different editing methods
for hair. Each of these will use a HairSystem instance, but vary
in the editable data used to generate guide curves. The hair system
underneath is agnostic to the editing method used.
2017-11-15 08:40:51 +00:00
78428043d6 Remove the guide_object pointer from HairSystem.
Scalp mesh now is passed explicitly to the hair API functions again.

The idea is that hair systems can be created in any object and use
another object as the scalp. Sharing the same hair pattern should be
implemented at a later time, pending a good design for assigning
follicles to specific hair systems.
2017-11-14 09:12:24 +00:00
a5d69d81e8 Merge branch 'blender2.8' into hair_guides 2017-11-12 10:11:46 +00:00
b81ce32a59 Minor flag rename. 2017-11-12 10:10:28 +00:00
22c5c78c02 Free draw data when deleting a hair system. 2017-11-12 09:03:40 +00:00
a007de04b1 Remove deprecated UI code for the hair modifier. 2017-11-11 16:21:29 +00:00
cf11953996 Fix null pointer reads when there are no hair follicles (which is allowed). 2017-11-11 16:18:05 +00:00
e421ba7144 Parent indices and weights for hair follicles and a binding function. 2017-11-11 15:57:31 +00:00
1ddbde24ff Add fields for curve storage in hair systems and use it in draw code. 2017-11-11 14:13:47 +00:00
2d4694678a Implementation for BKE_hair_get_scalp. 2017-11-11 09:51:08 +00:00
45f30a9fdb Restructure the hair modifier so external tools define guide curves. 2017-11-10 09:31:42 +00:00
3aab7537e8 Remove the new hair edit mode.
The purpose of this branch is to create a new hair system with a dedicated
grooming tool, unlike the current direct editing of hair guide curves.

The modified bmesh-based strand edit mode is quite complex and distracts
from the main purpose of the branch, so its better to remove it for now
and implement a new tool.
2017-11-09 19:22:42 +00:00
9f74b66ab7 Merge branch 'blender2.8' into hair_guides 2017-11-09 08:12:04 +00:00
5e1888965b DNA data for explicitly storing hair guide curves.
The idea is to separate guide curves from the various methods of
generating them. Guides could be created explicitly (as hair
strands), or automatically from vertices or external mesh data.

A guide then forms the basis for interpolating the render data
(fibers).
2017-09-10 09:07:26 +01:00
fb7c003e99 Simple CSV mesh loading for tests.
File paths are crude and preliminary.
2017-08-28 08:21:57 +01:00
caded470a6 Fix neighbor conflict checks for Poisson disk distribution.
The cell size is chosen as r/sqrt(3) (i.e. max. 1 sample per cell),
which means that the influence of each cell extends beyond the 1st
neighbor. a 5x5x5 box (except the center and corners) needs to be
examined to find collisions.
2017-08-26 15:32:09 +01:00
d9a4945bb2 Added a 1-cell margin to the grid dimensions to simplify neighbor lookups. 2017-08-26 12:41:58 +01:00
188874af2f Separate step for mesh sample generators to bind them to a specific mesh. 2017-08-26 12:13:52 +01:00
e3033693f9 Initial implementation of Poisson disk sampling on meshes.
This is still buggy (uneven sample coverage) and needs changes to the
sampling system for better threading support.

The implementation is based on

Bowers, John, et al. "Parallel Poisson disk sampling with spectrum analysis on surfaces." ACM Transactions on Graphics (TOG). Vol. 29. No. 6. ACM, 2010.
2017-08-25 09:19:49 +01:00
ef67e13bdb Thread-safe implementation of the volume bounding-box raycast sampler. 2017-08-23 09:44:00 +01:00
9e8a41aadf Include other sample generators in tests.
Volume bbray method is currently excluded because the implementation
is not yet thread-safe.
2017-08-23 09:07:45 +01:00
8b607d77e5 New gtests for the mesh sampling system.
This should test general functionality and consistency of different methods
for generating samples. In particular it shoud ensure that threaded variants
produce the same samples as unthreaded variants when using the same generator
and seeds.
2017-08-22 18:49:01 +01:00
0d4fa23414 Use a consistent number of RNG steps in both sample generation and skipping.
This is to ensure that threaded generator usage creates the exact same results
as the non-threaded base version. Further improvements and testing will be
needed.
2017-08-21 06:42:59 +01:00
e3b80d6202 Multithreading support for the mesh sampling library.
Samples can be generated either one-by-one using the existing "make_sample"
function (which uses a default context), or use the more efficient batch
generation functions. These optionally support threading via the task scheduler
now.
2017-08-20 18:50:58 +01:00
ead00baab1 Fix hair follicle array write/read. 2017-08-19 12:26:02 +01:00
45f0f3dc04 Merge branch 'blender2.8' into strand_editmode 2017-08-19 12:02:03 +01:00
0d67f8d5c4 Removed unused shaders in the draw modes module. 2017-08-19 08:56:04 +01:00
438b3f95a1 Implement control strands generation for basic hair types along surface normals.
The default hair group type uses vertex normals to generate guide strands automatically.
2017-08-18 16:13:34 +01:00
1b837226ce Store the scalp mesh as part of the hair drawing interface.
The scalp mesh is always needed for evaluating root positions anyway.
2017-08-14 08:46:10 +01:00
6faa4d77c1 Removed fiber drawing from the strand edit mode to simplify code.
For rendering hair fibers the edit mode should just use the underlying data.
2017-08-14 08:19:54 +01:00
b0717ad91e Hair drawing data based on the new DNA hair groups. 2017-08-14 07:58:02 +01:00
91fd0a487d Store pointer into the hair follicle array in hair groups directly.
This creates a dependency of the hair groups on the validity of the pattern,
so care has to be taken to always update the groups when the pattern changes.
2017-08-11 08:57:04 +01:00
e7df5f8528 Split off hair drawing code into a separate file. 2017-08-11 08:17:03 +01:00
6fa838f6d3 List of 'Hair Groups' in hair patterns that will generate actual fibers for rendering.
Each group affects a subset of the hair follicles, so that multiple effects can be combined
without overlapping sample sets.
2017-08-07 18:04:30 +01:00
41e8bd9337 Merge branch 'blender2.8' of git.blender.org:blender into strand_editmode 2017-08-07 13:06:02 +01:00
77802b21a6 Operator for generating a hair follicle distribution. 2017-08-07 12:29:12 +01:00
8abe6745a4 DNA data + modifier for storing persistent hair data.
This stores basic hair follicle data, rather than full fiber caches
(which should only be generated for render data).

Note that deformation by strands will just be one possible way to generate
hair fibers, so strands should be kept separate from the hair follicle data somewhat.
2017-08-07 10:54:02 +01:00
53eb011e3d Prototype hair deformers for generated detail like clumping and curling.
Currently only clumping and curls are implemented. There is no proper input
for the parameters yet, these are just hardcoded atm. Eventually the hair system
should either define uniform values from hair system settings or enable per-fiber
values using a material or other node-based input.
2017-08-06 12:41:16 +01:00
801c20cebc Some optional timing for strand data buffer construction. 2017-08-04 08:41:20 +01:00
9feec51214 Subdivision of hair fibers for smoother shading.
Subdivision works on the parent strands for efficiency. The fibers lengths
are based on the final subdivided length of parents, so no changes to the
shader are required.

This would be nicer with a tesselation shader, but this feature is not
available in Blender 2.8.
2017-08-03 20:27:33 +01:00
e34ba9fb7a Use a 2D texture for the hair interpolation data instead of 1D for larger number of hairs.
It turns out that 1D textures have the same size limit on their 1 axis as 2D textures.
This limits the potential number of hair dramatically, even though the actual size of
the texture is very small. Using a 2D texture and wrapping the index avoids this problem.
2017-08-03 08:11:48 +01:00
c92457a87a Default settings for hair edit mode tool settings. 2017-08-02 22:35:57 +01:00
bf0f058954 Disable selection outline drawing on the object during hair edit mode. 2017-08-02 22:28:25 +01:00
e35f24fb28 Removed leftover hair fiber code from edit mode drawing (all in Eevee now). 2017-08-02 21:54:01 +01:00
e8ff620325 Moved the hair fiber shader from strand edit mode into Eevee.
This allows the shader to use proper lighting from the standard Eevee shaders.

The code for interpolating hair strands is in a glsl library file to facilitate
use in other engines later.
2017-08-02 21:40:52 +01:00
3a438c675f Hair fiber (child hair) drawing during edit mode, with heavy GPU leveraging.
Hair drawing requires fairly large vertex buffers. Vertices also need to be interpolated
and displaced according to the control (parent) hairs and hair-style features like curling
and clumping.

Instead of doing this in advance on the CPU and uploading a large vertex buffer every frame,
this work can be done on the GPU for realtime display, because interpolation and displacement
are highly parallelizable. The vertex buffers then only need to be uploaded once (as long
as strand lengths remain the same). The vertex format can also be very lightweight, requiring
only a curve parameter and index (half the size needed for full positions).

For interpolating a buffer texture is used in the shader, which contains all information
about the control curves (parents) from which to interpolate final vertex positions.
2017-08-01 08:10:05 +01:00
57cbaa15b6 Custom shaders for drawing strand selection. 2017-07-23 08:32:29 +01:00
679113fbd5 Basic drawing code for hair edit mode in the new 2.8 draw engine system. 2017-07-22 12:35:40 +01:00
65d2374c80 Merge branch 'blender2.8' into strand_editmode2.8 2017-07-16 11:48:39 +01:00
d3349e5e3f Merge branch 'master' into strand_editmode 2016-08-09 15:52:02 +02:00
5a7efaf287 Added "show_brush" option for hair edit settings, expected by common paint UI.
This setting has no effect yet for hair editing, but the UI script expects it.
2016-07-27 09:50:45 +02:00
f3bc942370 Merge branch 'master' into strand_editmode 2016-07-25 09:33:48 +02:00
07cffae7ea Fix for uninitialized v_mirr variable when using edge strokes with mirroring. 2016-07-20 09:44:39 +02:00
1b431c3cfe Merge branch 'master' into strand_editmode 2016-07-19 11:42:52 +02:00
dd1211ecb8 Merge branch 'master' into strand_editmode 2016-07-19 10:32:23 +02:00
1d28579daf Merge branch 'mesh_samples' into strand_editmode 2016-07-14 09:59:52 +02:00
48fc8b76b2 Merge branch 'master' into strand_editmode 2016-07-14 09:57:38 +02:00
bd4ca21b54 Merge branch 'master' into mesh_samples 2016-07-14 09:56:58 +02:00
28270f0953 Replace the deprecated TessFace usage for mesh sampling by LoopTri.
Also make sure a valid generator instance for random sampling is always
returned, regardless of triangle counts.
2016-07-14 09:55:18 +02:00
2f596756b7 Merge branch 'master' into strand_editmode 2016-07-13 16:50:23 +02:00
56ad4520cd Merge branch 'master' into strand_editmode 2016-07-09 09:09:47 +02:00
4e95617769 Merge branch 'master' into strand_editmode 2016-07-05 09:53:24 +02:00
548dfdbd31 Merge branch 'master' into mesh_samples 2016-06-28 11:17:44 +02:00
73140ca283 Merge branch 'master' into strand_editmode 2016-06-27 10:52:10 +02:00
32455e230a Fix unused function warning. 2016-04-20 12:34:04 +02:00
af594b7b85 Fix meamleak from unfreed edit data when exiting in hair edit mode. 2016-04-19 14:19:54 +02:00
5f67ac83bf Fix unfreed temporary buffers in strand relaxation. 2016-04-19 14:05:35 +02:00
b2cb4c448e Define BMEditStrands struct as an extension of BMEditMesh.
The advantage is that we can now use this struct in the (rather involved)
process of generating a display DerivedMesh and apply modifiers to it.
2016-04-19 14:01:41 +02:00
216aacbcba Disable regular mesh drawing in hair edit mode. 2016-04-19 13:27:20 +02:00
ec51e83798 Proof-of-concept: Hair edit data from regular meshes.
This adds the possibility to edit a regular mesh like strand data.
It's more of a test for independence from particles than a real use case,
but could come in handy anyway.
2016-04-18 18:13:26 +02:00
9a20f68295 Check mesh samples for zero weights on eval to detect invalid/unusable samples. 2016-04-18 17:48:25 +02:00
a85c9b241a Corrected implementation of the strand relaxation method ported from particles.
This is still the same algorithm as in particle edit mode, but cleaned up
some unnecessary complications and use much more meaningful variable names.
2016-04-17 15:08:46 +02:00
07221e980b Merge branch 'master' into strand_editmode 2016-04-16 18:10:37 +02:00
56d8228713 Merge branch 'master' into mesh_samples 2015-10-11 15:51:07 +02:00
892529029f Port over the improved mesh sampling API from the mesh_samples branch. 2015-09-19 17:33:37 +02:00
d820eb1265 Merge branch 'master' into mesh_samples 2015-09-19 16:59:26 +02:00
3aaca08aad Merge branch 'master' into strand_editmode
Conflicts:
	source/blender/editors/sculpt_paint/paint_ops.c
2015-09-19 16:29:04 +02:00
5f6d9e301e Implementation of Volume Sampling on meshes.
Based on the ray casting technique described here: http://www.joesfer.com/?p=84

Volume samples currently are not bound to mesh deformation in any way, they are
simple object-space locations. Potentially a harmonic weight approach as in
mesh cages could be used.
2015-08-21 16:45:36 +02:00
05281abca1 Merge branch 'master' into mesh_samples
Conflicts:
	release/datafiles/locale
	release/scripts/addons
	source/blender/blenkernel/CMakeLists.txt
2015-08-21 15:07:47 +02:00
38d9301674 Another fix for mirror editing of strand data.
Currently strand edit mode tool use the object data 'x mirror' flag.
This flag is only available on mesh objects, but the strand editing
can be used for dupli override caches etc. as well. Eventually strand
editing should get it's own independent flag to avoid this problem.
2015-05-05 14:27:44 +02:00
fbc70fa51c Fix for call to mirror functions when mirroring is not enabled, giving
warnings.
2015-05-05 14:03:59 +02:00
d52bdb6d3d Moved particle related functions of the strands edit mode into a
separate file.

This may seem a bit like overkill, but it helps ensure that no particle
depedencies messes up the strand editing code. The same will be done for
other use cases of the strand editing code in the future.
2015-05-04 18:37:45 +02:00
16b6d6c676 Added missing CD layer type name for MSurfaceSample. 2015-05-04 17:52:13 +02:00
bf7e8b42fa Merge branch 'master' into strand_editmode
Conflicts:
	source/blender/bmesh/bmesh_class.h
	source/blender/bmesh/intern/bmesh_mesh_conv.h
2015-05-04 17:14:25 +02:00
20e960215a Particle shapekey code is only in the gooseberry branch, disabled here. 2015-04-20 12:59:01 +02:00
9cafd3ae56 SimDebugData is now global, for easier usage and less intrusive code. 2015-04-20 12:58:16 +02:00
0dccffb7aa Fix for CustomDataType syntax error from merging. 2015-04-20 12:37:21 +02:00
7719a6365a Use the X mirror option in new strand edit stroke brush tools.
Note that currently this has virtually no effect, because the mirror
option relies on exact positions of vertices, which does not happen with
random hair placement (the add brush has no mirror option yet).

Eventually topological mirroring should help with this case, but is not
implemented for either old or new strand edit yet.
2015-04-20 12:25:18 +02:00
3a3327dcd5 Cleanup: rename function argument for consistency. 2015-04-20 12:25:18 +02:00
8ffcd1f434 Utility methods for mirrored strand editing.
These are a modified version of their BMEditMesh counterparts.

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.

Conflicts:
	source/blender/blenkernel/BKE_editstrands.h
2015-04-20 12:25:15 +02:00
ed1dc43657 Fix for strands undo creating an invalid BMesh.
When copying mesh data to bmesh the MVERT and similar customdata types
have to be omitted. Otherwise the bmesh instance ends up with NULL
pointers in customdata layers, but entries in the typemap != -1. The
effect was that when storing new steps after one or more undo, the
resulting original data would be copied, and subsequent undo steps
are ignored.
2015-04-20 12:24:34 +02:00
5b9b779cab Minor code reshuffling. 2015-04-20 12:24:34 +02:00
4798464e8b Extended Mesh <-> BMesh copy function versions for specifying custom
data masks explicitly.

A dummy mesh is used for strand edit undo storage like in mesh edit
to prevent unnecessary code duplication. However, when copying from/to
BMesh only the mesh data layers are copied by default, omitting the new
data layers for strands (currently only MSurfaceSample hair root data).
2015-04-20 12:24:33 +02:00
299859e40b Sanity check: if there is no edited/active object for an undo step,
consider it invalid and clean up.
2015-04-20 12:24:33 +02:00
5e42aff397 Basic undo support in the new strand edit mode.
This uses the generalized undo stack system which is also used for
object data edit. An extension is necessary to tell the undo functions
which object is specifically used for generating undo data and
identifying the stack, since strand editing does not edit the obdata
itself.
2015-04-20 12:24:33 +02:00
3f539c987f Removed deprecated UI button for the hair debug flag. 2015-04-20 12:24:33 +02:00
6146b90312 Select Linked operator for hair edit mode (select all vertices of a
strand).
2015-04-20 12:24:03 +02:00
8b2ec99f57 Lasso Select operator for hair edit mode. 2015-04-20 12:24:03 +02:00
410998cdc6 Border Select operator for hair edit mode. 2015-04-20 12:24:03 +02:00
b6f4e0932d Mouse Select operator for hair edit mode. 2015-04-20 12:24:03 +02:00
62fa5c4a84 Select/Deselect All operator for hair edit mode. 2015-04-20 12:24:03 +02:00
2cddc3cda8 Disabled the partial IK strand relaxation for the time being.
First goal is to reproduce current particle edit tools in the strand
edit mode, then make improvements.
2015-04-20 12:24:03 +02:00
9120df6cb3 Missing bmesh include path in scons files. 2015-04-20 12:24:02 +02:00
48a86af388 IK solver for hair strands that provides a better solution for keeping
consistent segment lengths when transforming vertices.

Warning: The implementation is not correct yet, but all the steps should
be there.

The main idea is to treat strands as a sequence of joints that are
displaced out of their original locations by a transform or other tool.
The solver then tries to find a global per-strand solution that keeps
the segment lengths unmodified, with a minimum change in angles from
the original starting shape. Such a solution is much more usable and
efficient than the current O(n^2) attempt of "spreading the error"
across the strand.

The inverse kinematics method is very flexible. It can also include
stretching, which would be very welcome for features like the length
tool. Different parts of the strand could be weighted separately using
scaling factors for the angle/stretch parameters.

Conflicts:
	source/blender/physics/intern/implicit.h
2015-04-20 12:24:00 +02:00
eacc24ccf1 Ported over the relaxation method for hair vertices from particle edit
mode.

This method is simple, but not really very usable. It works by
successively relaxing segments that are too long or too short, moving
both vertices along the edge between them. This is repeated N^2 times
(N: number of vertices on the strand).

A true IK solver could give a lot better results, as well as providing
many opportunities to apply weighting for targets (e.g. preferring to
move non-selected over selected vertices). Many different methods for
simple IK solvers exist, so there should be one that works well for
large number of simple strands. See e.g.
http://www.math.ucsd.edu/~sbuss/ResearchWeb/ikmethods/iksurvey.pdf
2015-04-20 12:23:49 +02:00
ac54ded29b Primitive transform operator support for strand vertices.
This does not yet prevent root transforms and/or length changes.
2015-04-20 12:23:49 +02:00
f7511b3d01 Improved hair combing tool, adjusting edge directions instead of
vertex positions.

This works a lot better with strokes perpendicular to the general hair
strand direction. With the previous comb tool such strokes would only
make a small dent in the hair curve and then vertices would slip out
of the tool circle. The edge combing affects the local direction of
strands, which acts as a kind of grabbing functionality by moving
further vertices in front of the tool circle. The result is that
drawing a curvy hair shape with the comb becomes much easier.

In addition, the new tool also uses edge filtering and weighting rather
than vertices. This means that small brushes also work well, instead
of having no effect when hitting the edge segments between vertices.

Further improvement could be achieved later by using a global strand
solver, which adjusts vertex positions based on an error metric along
the whole of the strand.
2015-04-20 12:23:48 +02:00
208ddcde2c Use a simple low-pass filter to generate a smoother, more stable
direction vector for the hair stroke tool.

This is necessary for directional tools such as combing, where the
stroke direction can lead to unwanted results if it changes too
abruptly.
2015-04-20 12:23:48 +02:00
f3b22c5769 Support for sim_debug drawing in hair edit mode (dev feature). 2015-04-20 12:23:48 +02:00
b70c815ac2 Renamed the edithair files in BKE to editstrands.
The rationale behind this is that the BKE code could be used for
modeling hair and fur as well as a number of other features such as
grass. The primary addition to BMesh is the limitation to strand-like
topology (simple vertex chains, optionally rooted on a mesh surface).

The editor code OTOH is quite hair specific, since the result should
be suitable for hair simulation, and the workflow should mimick actual
hair grooming intuitively. Eventually the hair edit mode could become
a generalized strand edit mode with slightly different tool sets for
various purposes, but for now it is quite specifically built for hair.

Conflicts:
	source/blender/blenkernel/CMakeLists.txt
	source/blender/blenkernel/intern/particle.c
2015-04-20 12:23:46 +02:00
d0a1fc8bb0 Sanity check for strand root/tip checks, now supports single vertices. 2015-04-20 12:23:23 +02:00
2d1d909817 MSVC compiler cannot handle standard C code.
Conflicts:
	source/blender/editors/physics/particle_edit.c
2015-04-20 12:23:20 +02:00
e9737da5f2 Fix scons build files for new hair edit code parts. 2015-04-20 12:23:16 +02:00
1e047231c6 Fix for hair-to-particle export: need to set weight=1.0 for hair roots,
since the particle system uses this for pinning them to the mesh.
2015-04-20 12:23:16 +02:00
142ef0b2e9 Fixed lib-linking for hair edit settings brush and object pointers. 2015-04-20 12:23:15 +02:00
56a09434c5 Basic Add tool for creating new strands in hair edit mode. 2015-04-20 12:23:15 +02:00
8672304e30 Fix for uninitialized return value. 2015-04-20 12:23:15 +02:00
f95dde244e Better feedback about valid sample from mesh sampling methods.
Conflicts:
	source/blender/editors/object/object_shapekey.c
2015-04-20 12:23:15 +02:00
79e649a3bf New simple storage method for single sample values. 2015-04-20 12:23:15 +02:00
e5b25e3c07 Generic raycast method for generating mesh surface samples.
Conflicts:
	source/blender/blenkernel/intern/mesh_sample.c
2015-04-20 12:23:15 +02:00
a4664ef88d New mesh sample evaluation function for shape key data. 2015-04-20 12:23:15 +02:00
be312b1399 First selection operator implementation for hair edit: circle select. 2015-04-20 12:23:14 +02:00
3edc512888 Show selection state of hair vertices with the usual theme colors. 2015-04-20 12:23:14 +02:00
b2ea8c1022 Added back different selection modes (strands, verts, tips) for hair
editing.
2015-04-20 12:23:14 +02:00
339a8b7521 Fix for OpenlGL state restoration after strand drawing, disable color
material option.
2015-04-20 12:23:14 +02:00
77da317cb1 Apply the same weight profile in the comb tool as in particle edit mode.
The comb tool will likely get a complete overhaul, but until then it
should just behave like the old tool.
2015-04-20 12:23:14 +02:00
2c0616b034 Support for common operators, keymap entries and radial controls for
hair brush settings.
2015-04-20 12:23:14 +02:00
a9fbd3b23b Fix for brush radius, the Brush.size property is actually the radius and
not the diameter.
2015-04-20 12:23:14 +02:00
687b5a1ec0 Implemented basic cursor drawing for hair edit mode brushes. 2015-04-20 12:23:13 +02:00
59efe23830 Primitive hair lighting in OpenGL solid shading, cleanup of strands
drawing code for edit mode.

The lighting uses the same method as the particle hair, interpreting
strand direction as the normal. This is nowhere near realistic hair
shading, but doing this with line shading alone in the fixed-function
pipeline is probably not possible. A GLSL shader could be used instead
for a more realistic alternative as part of the viewport project. At
least this simple shading mode gives some orientation feedback while
editing hair.
2015-04-20 12:23:13 +02:00
d16599ea04 Display basic brush properties in the toolbar for hair edit mode. 2015-04-20 12:23:13 +02:00
37d3a3331e Use the "official" brush size calculated by blenkernel and subdivide
the stroke step to avoid tunneling.
2015-04-20 12:23:13 +02:00
5254df30ba Filter affected hair vertices in edit mode based on the brush size and
z depth.
2015-04-20 12:23:13 +02:00
2590231315 Constrain hair root vertices to their mesh location after applying tools.
Conflicts:
	source/blender/bmesh/intern/bmesh_interp.c
	source/blender/bmesh/intern/bmesh_interp.h
2015-04-20 12:23:11 +02:00
83b20c7ddf Enforce hair constraints (constant segment length) after applying a tool.
Currently uses the same approach as old particle edit mode (rescale
hair segments from the root on). A more sophisticated approach using
least-square error minimization of the displacement could yield better
results.
2015-04-20 12:22:10 +02:00
0e06a60168 Make sure tool input data is in object space. 2015-04-20 12:22:10 +02:00
121677d7c8 Nicer vector math for mouse coordinates. 2015-04-20 12:22:10 +02:00
1615da133f Simplified first version of the hair stroke combing functionality.
Stroke tools will be categorized by the hair elements they operate on:
vertices, segments or strands (roots). In addition to that a filter
function defines the influence of the brush. This should be defined by
the other brush settings and be largely independent of the main tool
mode.
2015-04-20 12:22:10 +02:00
7ae6492c85 Lots of small fixes for paint system quirks to make hair brushes work
in the UI.

Conflicts:
	release/scripts/startup/bl_ui/space_view3d.py
	source/blender/editors/transform/manipulator_widget.c
2015-04-20 12:21:58 +02:00
7779166313 Define hair edit settings and brush types in the RNA, similar to paint
and sculpt settings.

Conflicts:
	source/blender/makesdna/DNA_brush_types.h
2015-04-20 12:19:28 +02:00
833a11f891 New image file icons for hair brush tools and an according tool enum in
the Brush RNA.
2015-04-20 12:17:16 +02:00
79959152e6 Calculate some basic info about mouse strokes and context and pass it
on to a general hair tool function.
2015-04-20 12:17:16 +02:00
4fd6a7a696 Revert previous hair edit operator scaffolding and port over the old
operator for strokes instead.

The paint system is much too complicated and does not add any real
advantage at this point.
2015-04-20 12:17:16 +02:00
98b15d9a7c Copied high-level operator code from the paint/sculpt system as a basis
for brush-stroke operators in hair edit mode.

Hopefully this can help avoid some redundant code and ensure consistent
keymapping etc.
2015-04-20 12:17:16 +02:00
a889a1458c Missing bmesh include folder for hair editor in scons. 2015-04-20 12:17:16 +02:00
7b6332f634 Apply the hair matrices when converting from particle keys to bmesh,
so the edit data is consistently in object space.
2015-04-20 12:17:16 +02:00
0a27e97fb7 Fix for GL buffer size for edges, needs to 2x for two vertex indices. 2015-04-20 12:17:16 +02:00
eb5257002b New drawing code for hair edit mode.
Conflicts:
	source/blender/editors/space_view3d/drawobject.c
	source/blender/editors/space_view3d/view3d_intern.h
2015-04-20 12:17:14 +02:00
23b284348b Removed deprecated code. 2015-04-20 12:15:17 +02:00
2d79994e39 Free strand edit memory after use. 2015-04-20 12:15:17 +02:00
fd34e3efbc Fix for verts-of-strand iterator: This requires a second pointer in
addition to the edge, otherwise the last point is omitted.
2015-04-20 12:15:17 +02:00
dc43df89cd CustomData layer for storing hair root locations as MSurfaceSamples.
This requires converting the old messy particle num/num_dmcache/fuv/foffset
data into the new mesh samples, which can potentially introduce floating
point errors and inaccuracies due to lack of face index mapping in the
new system. However, in any well-constructed particle system the hair
roots should be nearest to their num face, so mapping would be accurate
enough. If necessary face index data could be added to samples as a
legacy code hack, but probably it's best to eventually replace the
hair system as a whole anyway.
2015-04-20 12:15:17 +02:00
4e47239de2 Store the hair key weight in edit mode.
Currently particles only have a single weight value, controlling
pinning in the simulation (for root and virtual root verts) and goal
force scaling. This will be replaced by a full vertex group weight
system eventually.
2015-04-20 12:15:17 +02:00
991ee8a570 First customdata layer for particle mass.
This is more for testing purposes, since currently there is only a
single mass property for the psys as a whole. This should change in the
future though, to allow variable mass per strand or vertex.

Conflicts:
	source/blender/bmesh/intern/bmesh_interp.c
2015-04-20 12:15:15 +02:00
e43ef55e7f Basic particle data reconstruction when exiting edit mode. 2015-04-20 12:14:34 +02:00
1afd5be044 Fix for verts-of-strand iterator: After last edge step has to set the
edge to NULL to avoid infinite looping over the same edge (1-elem disk).
2015-04-20 12:14:34 +02:00
d550a29558 Fix for particle-to-bmesh: only was looping over totvert instead of
pa->totkey.
2015-04-20 12:14:34 +02:00
5f44f4a2ff Intermediate commit: switching strand edit data to BMesh.
Hair/Strand editing will only use a subset of the bmesh topology and
expect a specific topology that needs to be verified and enforced.
However, this extra requirement is much less work than reimplementing a
whole edit data system with the same feature set as bmesh and avoids
much redundant code.

Conflicts:
	source/blender/blenkernel/intern/customdata.c
	source/blender/makesdna/DNA_customdata_types.h
2015-04-20 12:14:30 +02:00
b0a9e48a19 New customdata type for mesh surface samples.
Conflicts:
	source/blender/makesdna/DNA_customdata_types.h
2015-04-20 12:14:23 +02:00
afb947c00c Switched the hair edit data to a bmesh-like structure for consistency.
This means using mempools to store curve and vertex data, which allows
arbitrary addition and removal of data more easily. Also this includes
an iterator system similar to bmesh iterators (although the simpler
topology makes it a lot less complex).

Conflicts:
	source/blender/blenkernel/intern/customdata.c
	source/blender/makesdna/DNA_customdata_types.h
2015-04-20 12:13:35 +02:00
f1e4d35489 Reconstruct most basic particle data when applying the hair edit, so
particle hairs remain visible.

Note that currently the hair root location (num/num_dmcache, fuv, foffset)
is not stored from edit data, so all hairs end up in a default location.
2015-04-20 12:12:01 +02:00
1c7053f7a0 Store hair edit data in particle systems and manage it in the operator
for entering/exiting the edit mode.

Conflicts:
	source/blender/blenkernel/intern/particle.c
2015-04-20 12:11:59 +02:00
7bb90a06e1 Moved code for the hair edit data structures to blenkernel.
This makes it work more like editmesh, and avoid the awkward and
basically bad-level approach in particles, where the edit data is an
anonymous pointer in particle systems together with a callback for
freeing.

Conflicts:
	source/blender/blenkernel/CMakeLists.txt
2015-04-20 12:11:44 +02:00
a9001adbb4 New edit mode for hair.
Conflicts:
	source/blender/blenloader/intern/readfile.c
2015-04-20 12:11:05 +02:00
dd69a3dbcf Finished the particle-to-edit conversion function. 2015-04-20 12:10:33 +02:00
c65f1b2e15 More work on particle conversion methods for hair edit mode. 2015-04-20 12:10:33 +02:00
09a7dbc29a New editor library for hair edit mode. 2015-04-20 12:10:33 +02:00
6078e79cea Minor syntax fix. 2015-04-20 12:10:33 +02:00
6783b15453 Shifted the dispatch from the algorithm side to the storage side of the
sampling system to keep the code simple.

Now there is a MSurfaceSampleStorage struct that encodes the storage
details, which the algorithms don't have to care about.
2015-04-20 12:10:33 +02:00
82132fc5b0 Added generalized method for creating an array of mesh samples with
arbitrary stride.
2015-04-20 12:10:32 +02:00
4b4f24607e Changed mesh sample definition to use 3 vertex weights instead of a
face index. This is easier to sample uniformly and avoids the need for
tesselation for evaluating.
2015-04-20 12:10:32 +02:00
fe48c353f1 Normalize the normal vector after sample eval to account for
interpolation.
2015-04-20 12:10:32 +02:00
308af3bfb3 Added a bool return to the eval function to give feedback on invalid
samples.
2015-04-20 12:10:32 +02:00
3f7d66ece4 Added a basic RNA definition for the mesh sampling system. 2015-04-20 12:10:32 +02:00
a952800ffc Eval function to get a location and normal vector from mesh samples. 2015-04-20 12:10:32 +02:00
71e34dad2b Usable random distribution algorithm.
Does not include area weighting yet.
2015-04-20 12:10:32 +02:00
24b77359ae Skeleton code for sampling meshes.
Conflicts:
	source/blender/blenkernel/CMakeLists.txt
2015-04-20 12:10:29 +02:00
62aabdd550 Merge remote-tracking branch 'github_lukastoenne/mesh_samples' into mesh_samples 2014-12-05 12:17:53 +01:00
2204bb092b Merge branch 'master' into mesh_samples 2014-12-05 11:23:38 +01:00
8374e08894 Mesh sampling system for unified point-on-mesh handling.
This code is not yet used by any feature in master, but has proved very useful in the Gooseberry branch and several WIP patches. Inclusion into master was requested to avoid diverging code in branches and get part of the Gooseberry code merged early on.

The core idea is to have a common way of identifying points on mesh surfaces. Efficient evaluation of such points after deformation and mappable modifiers (e.g. subdivision) tracks positions, normals and other surface attributes. This is used extensively in the particle/hair systems and could be useful for certain tools, duplicators, etc.. Currently the particle systems in particular are very fragile and succeptible to mesh changes because of the unreliable way mesh surface mapping is stored. While the new system at this point uses essentially the same method, it at least provides a centralized DNA type that can be improved easily without too much effort and compatiblity issues.

In addition to evaluating surface samples the system also provides a central place to implement surface sampling algorithms, such as random sampling, ray casting, paint-like tools, etc.

Differential Revision: https://developer.blender.org/D922
2014-12-01 15:10:16 +01:00
822949a3e2 Fix for uninitialized return value. 2014-12-01 14:42:18 +01:00
a86869af8f Better feedback about valid sample from mesh sampling methods.
Conflicts:
	source/blender/editors/object/object_shapekey.c
2014-12-01 14:41:02 +01:00
ca31ce144e New simple storage method for single sample values. 2014-12-01 14:38:03 +01:00
f61f7e2d57 Generic raycast method for generating mesh surface samples. 2014-12-01 14:36:10 +01:00
3036cedd90 New mesh sample evaluation function for shape key data. 2014-12-01 14:34:59 +01:00
cc5b3c2ff7 Merge branch 'mesh_samples' of github.com:lukastoenne/blender into mesh_samples 2014-12-01 14:29:41 +01:00
a30f9ffa87 Merge branch 'master' into mesh_samples
Conflicts:
	release/datafiles/locale
	release/scripts/addons
2014-12-01 14:29:09 +01:00
3361788b1c Merge branch 'master' into mesh_samples
Conflicts:
	release/datafiles/locale
	release/scripts/addons
2014-11-30 10:08:09 +01:00
21ab9ac9e3 Minor syntax fix. 2014-09-24 17:35:41 +02:00
18c6bb2436 Shifted the dispatch from the algorithm side to the storage side of the
sampling system to keep the code simple.

Now there is a MSurfaceSampleStorage struct that encodes the storage
details, which the algorithms don't have to care about.
2014-09-24 17:04:41 +02:00
a93841e3d4 Added generalized method for creating an array of mesh samples with
arbitrary stride.
2014-09-24 15:58:53 +02:00
728b6ed8e6 Merge branch 'master' into mesh_samples 2014-09-24 15:54:21 +02:00
fa0163b8c7 Changed mesh sample definition to use 3 vertex weights instead of a
face index. This is easier to sample uniformly and avoids the need for
tesselation for evaluating.
2014-03-04 15:39:48 +01:00
7112a531b2 Normalize the normal vector after sample eval to account for
interpolation.
2014-03-04 13:42:51 +01:00
60cde6ca4a Added a bool return to the eval function to give feedback on invalid
samples.
2014-03-04 13:42:06 +01:00
717db95210 Added a basic RNA definition for the mesh sampling system. 2014-03-03 17:57:56 +01:00
a944cf8450 Eval function to get a location and normal vector from mesh samples. 2014-03-03 14:10:58 +01:00
a1dd14c764 Usable random distribution algorithm.
Does not include area weighting yet.
2014-03-03 12:36:02 +01:00
47d024c2b0 Merge branch 'master' into mesh_samples 2014-03-03 11:56:02 +01:00
30e02b9a19 Skeleton code for sampling meshes. 2014-03-03 10:59:36 +01:00
62 changed files with 6415 additions and 396 deletions

View File

@@ -110,95 +110,342 @@ static void InterpolateKeySegments(int seg,
curveinterp_v3_v3v3v3v3(keyloc, &ckey_loc1, &ckey_loc2, &ckey_loc3, &ckey_loc4, t);
}
static bool ObtainCacheParticleData(Mesh *mesh,
BL::Mesh *b_mesh,
BL::Object *b_ob,
ParticleCurveData *CData,
bool background)
static void ObtainCacheDataFromParticleSystem(Mesh *mesh,
BL::Object *b_ob,
BL::ParticleSystemModifier *b_psmd,
ParticleCurveData *CData,
bool background,
int *curvenum,
int *keyno)
{
int curvenum = 0;
int keyno = 0;
if(!(mesh && b_mesh && b_ob && CData))
return false;
BL::ParticleSystem b_psys((const PointerRNA)b_psmd->particle_system().ptr);
BL::ParticleSettings b_part((const PointerRNA)b_psys.settings().ptr);
Transform tfm = get_transform(b_ob->matrix_world());
Transform itfm = transform_quick_inverse(tfm);
if((b_part.type() != BL::ParticleSettings::type_HAIR) ||
(b_part.render_type() != BL::ParticleSettings::render_type_PATH))
{
return;
}
int shader = clamp(b_part.material()-1, 0, mesh->used_shaders.size()-1);
int draw_step = background ? b_part.render_step() : b_part.draw_step();
int totparts = b_psys.particles.length();
int totchild = background ? b_psys.child_particles.length() : (int)((float)b_psys.child_particles.length() * (float)b_part.draw_percentage() / 100.0f);
int totcurves = totchild;
if(b_part.child_type() == 0 || totchild == 0)
totcurves += totparts;
if(totcurves == 0)
return;
int ren_step = (1 << draw_step) + 1;
if(b_part.kink() == BL::ParticleSettings::kink_SPIRAL)
ren_step += b_part.kink_extra_steps();
CData->psys_firstcurve.push_back_slow(*curvenum);
CData->psys_curvenum.push_back_slow(totcurves);
CData->psys_shader.push_back_slow(shader);
float radius = b_part.radius_scale() * 0.5f;
CData->psys_rootradius.push_back_slow(radius * b_part.root_radius());
CData->psys_tipradius.push_back_slow(radius * b_part.tip_radius());
CData->psys_shape.push_back_slow(b_part.shape());
CData->psys_closetip.push_back_slow(b_part.use_close_tip());
int pa_no = 0;
if(!(b_part.child_type() == 0) && totchild != 0)
pa_no = totparts;
int num_add = (totparts+totchild - pa_no);
CData->curve_firstkey.reserve(CData->curve_firstkey.size() + num_add);
CData->curve_keynum.reserve(CData->curve_keynum.size() + num_add);
CData->curve_length.reserve(CData->curve_length.size() + num_add);
CData->curvekey_co.reserve(CData->curvekey_co.size() + num_add*ren_step);
CData->curvekey_time.reserve(CData->curvekey_time.size() + num_add*ren_step);
for(; pa_no < totparts+totchild; pa_no++) {
int keynum = 0;
CData->curve_firstkey.push_back_slow(*keyno);
float curve_length = 0.0f;
float3 pcKey;
for(int step_no = 0; step_no < ren_step; step_no++) {
float nco[3];
b_psys.co_hair(*b_ob, pa_no, step_no, nco);
float3 cKey = make_float3(nco[0], nco[1], nco[2]);
cKey = transform_point(&itfm, cKey);
if(step_no > 0) {
float step_length = len(cKey - pcKey);
if(step_length == 0.0f)
continue;
curve_length += step_length;
}
CData->curvekey_co.push_back_slow(cKey);
CData->curvekey_time.push_back_slow(curve_length);
pcKey = cKey;
keynum++;
}
*keyno += keynum;
CData->curve_keynum.push_back_slow(keynum);
CData->curve_length.push_back_slow(curve_length);
(*curvenum)++;
}
}
static void ObtainCacheUVFromParticleSystem(BL::Mesh *b_mesh,
BL::ParticleSystemModifier *b_psmd,
ParticleCurveData *CData,
bool background,
int uv_num)
{
BL::ParticleSystem b_psys((const PointerRNA)b_psmd->particle_system().ptr);
BL::ParticleSettings b_part((const PointerRNA)b_psys.settings().ptr);
if((b_part.type() != BL::ParticleSettings::type_HAIR) ||
(b_part.render_type() != BL::ParticleSettings::render_type_PATH))
{
return;
}
int totparts = b_psys.particles.length();
int totchild = background ? b_psys.child_particles.length() : (int)((float)b_psys.child_particles.length() * (float)b_part.draw_percentage() / 100.0f);
int totcurves = totchild;
if(b_part.child_type() == 0 || totchild == 0)
totcurves += totparts;
if(totcurves == 0)
return;
int pa_no = 0;
if(!(b_part.child_type() == 0) && totchild != 0)
pa_no = totparts;
int num_add = (totparts+totchild - pa_no);
CData->curve_uv.reserve(CData->curve_uv.size() + num_add);
BL::ParticleSystem::particles_iterator b_pa;
b_psys.particles.begin(b_pa);
for(; pa_no < totparts+totchild; pa_no++) {
/* Add UVs */
BL::Mesh::tessface_uv_textures_iterator l;
b_mesh->tessface_uv_textures.begin(l);
float3 uv = make_float3(0.0f, 0.0f, 0.0f);
if(b_mesh->tessface_uv_textures.length())
b_psys.uv_on_emitter(*b_psmd, *b_pa, pa_no, uv_num, &uv.x);
CData->curve_uv.push_back_slow(uv);
if(pa_no < totparts && b_pa != b_psys.particles.end())
++b_pa;
}
}
static void ObtainCacheVColFromParticleSystem(BL::Mesh *b_mesh,
BL::ParticleSystemModifier *b_psmd,
ParticleCurveData *CData,
bool background,
int vcol_num)
{
BL::ParticleSystem b_psys((const PointerRNA)b_psmd->particle_system().ptr);
BL::ParticleSettings b_part((const PointerRNA)b_psys.settings().ptr);
if((b_part.type() != BL::ParticleSettings::type_HAIR) ||
(b_part.render_type() != BL::ParticleSettings::render_type_PATH))
{
return;
}
int totparts = b_psys.particles.length();
int totchild = background ? b_psys.child_particles.length() : (int)((float)b_psys.child_particles.length() * (float)b_part.draw_percentage() / 100.0f);
int totcurves = totchild;
if(b_part.child_type() == 0 || totchild == 0)
totcurves += totparts;
if(totcurves == 0)
return;
int pa_no = 0;
if(!(b_part.child_type() == 0) && totchild != 0)
pa_no = totparts;
int num_add = (totparts+totchild - pa_no);
CData->curve_vcol.reserve(CData->curve_vcol.size() + num_add);
BL::ParticleSystem::particles_iterator b_pa;
b_psys.particles.begin(b_pa);
for(; pa_no < totparts+totchild; pa_no++) {
/* Add vertex colors */
BL::Mesh::tessface_vertex_colors_iterator l;
b_mesh->tessface_vertex_colors.begin(l);
float3 vcol = make_float3(0.0f, 0.0f, 0.0f);
if(b_mesh->tessface_vertex_colors.length())
b_psys.mcol_on_emitter(*b_psmd, *b_pa, pa_no, vcol_num, &vcol.x);
CData->curve_vcol.push_back_slow(vcol);
if(pa_no < totparts && b_pa != b_psys.particles.end())
++b_pa;
}
}
static void ObtainCacheDataFromHairSystem(BL::Object *b_ob,
BL::HairSystem *b_hsys,
BL::Mesh *b_scalp,
int shader,
bool /*background*/,
ParticleCurveData *CData,
int *curvenum,
int *keyno)
{
Transform tfm = get_transform(b_ob->matrix_world());
Transform itfm = transform_quick_inverse(tfm);
void *hair_cache = BKE_hair_export_cache_new();
BKE_hair_export_cache_update(hair_cache, b_hsys->ptr.data, 0, b_scalp->ptr.data, 0xFFFFFFFF);
int totcurves, totverts;
BKE_hair_render_get_buffer_size(hair_cache, &totcurves, &totverts);
if(totcurves == 0)
{
BKE_hair_export_cache_free(hair_cache);
return;
}
CData->psys_firstcurve.push_back_slow(*curvenum);
CData->psys_curvenum.push_back_slow(totcurves);
// Material
CData->psys_shader.push_back_slow(shader);
{
// Cycles settings
// PointerRNA cpsys = RNA_pointer_get(&b_hsys->ptr, "cycles");
// float radius = get_float(cpsys, "radius_scale") * 0.5f;
// CData->psys_rootradius.push_back_slow(radius * get_float(cpsys, "root_width"));
// CData->psys_tipradius.push_back_slow(radius * get_float(cpsys, "tip_width"));
// CData->psys_shape.push_back_slow(get_float(cpsys, "shape"));
// CData->psys_closetip.push_back_slow(get_boolean(cpsys, "use_closetip"));
float radius = 0.01f * 0.5f;
CData->psys_rootradius.push_back_slow(radius * 1.0f);
CData->psys_tipradius.push_back_slow(radius * 0.0f);
CData->psys_shape.push_back_slow(0.0f);
CData->psys_closetip.push_back_slow(true);
}
// Allocate buffers
int *firstkey_data;
int *keynum_data;
float *length_data;
float3 *co_data;
float *time_data;
{
const size_t firstkey_start = CData->curve_firstkey.size();
const size_t keynum_start = CData->curve_keynum.size();
const size_t length_start = CData->curve_length.size();
const size_t co_start = CData->curvekey_co.size();
const size_t time_start = CData->curvekey_time.size();
CData->curve_firstkey.resize(firstkey_start + totcurves);
CData->curve_keynum.resize(keynum_start + totcurves);
CData->curve_length.resize(length_start + totcurves);
CData->curvekey_co.resize(co_start + totverts);
CData->curvekey_time.resize(time_start + totverts);
firstkey_data = CData->curve_firstkey.data() + firstkey_start;
keynum_data = CData->curve_keynum.data() + keynum_start;
length_data = CData->curve_length.data() + length_start;
co_data = CData->curvekey_co.data() + co_start;
time_data = CData->curvekey_time.data() + time_start;
}
// Import render curves from hair system
BKE_hair_render_fill_buffers(
hair_cache,
(int)sizeof(float3),
firstkey_data,
keynum_data,
(float*)co_data);
// Compute curve length and key times
for(int c = 0; c < totcurves; ++c) {
const int firstkey = firstkey_data[c];
const int keynum = keynum_data[c];
float curve_length = 0.0f;
float3 pcKey;
for(int v = 0; v < keynum; v++) {
float3 cKey = co_data[firstkey + v];
cKey = transform_point(&itfm, cKey);
if(v > 0) {
float step_length = len(cKey - pcKey);
if(step_length == 0.0f)
continue;
curve_length += step_length;
}
co_data[firstkey + v] = cKey;
time_data[v] = curve_length;
pcKey = cKey;
}
firstkey_data[c] = *keyno;
length_data[c] = curve_length;
*keyno += keynum;
}
*curvenum += totcurves;
BKE_hair_export_cache_free(hair_cache);
}
static bool ObtainCacheDataFromObject(Mesh *mesh,
BL::Object *b_ob,
ParticleCurveData *CData,
bool background)
{
int curvenum = 0;
int keyno = 0;
if(!(mesh && b_ob && CData))
return false;
BL::Object::modifiers_iterator b_mod;
for(b_ob->modifiers.begin(b_mod); b_mod != b_ob->modifiers.end(); ++b_mod) {
if((b_mod->type() == b_mod->type_PARTICLE_SYSTEM) && (background ? b_mod->show_render() : b_mod->show_viewport())) {
BL::ParticleSystemModifier psmd((const PointerRNA)b_mod->ptr);
BL::ParticleSystem b_psys((const PointerRNA)psmd.particle_system().ptr);
BL::ParticleSettings b_part((const PointerRNA)b_psys.settings().ptr);
if((b_part.render_type() == BL::ParticleSettings::render_type_PATH) && (b_part.type() == BL::ParticleSettings::type_HAIR)) {
int shader = clamp(b_part.material()-1, 0, mesh->used_shaders.size()-1);
int draw_step = background ? b_part.render_step() : b_part.draw_step();
int totparts = b_psys.particles.length();
int totchild = background ? b_psys.child_particles.length() : (int)((float)b_psys.child_particles.length() * (float)b_part.draw_percentage() / 100.0f);
int totcurves = totchild;
if(b_part.child_type() == 0 || totchild == 0)
totcurves += totparts;
if(totcurves == 0)
continue;
int ren_step = (1 << draw_step) + 1;
if(b_part.kink() == BL::ParticleSettings::kink_SPIRAL)
ren_step += b_part.kink_extra_steps();
CData->psys_firstcurve.push_back_slow(curvenum);
CData->psys_curvenum.push_back_slow(totcurves);
CData->psys_shader.push_back_slow(shader);
float radius = b_part.radius_scale() * 0.5f;
CData->psys_rootradius.push_back_slow(radius * b_part.root_radius());
CData->psys_tipradius.push_back_slow(radius * b_part.tip_radius());
CData->psys_shape.push_back_slow(b_part.shape());
CData->psys_closetip.push_back_slow(b_part.use_close_tip());
int pa_no = 0;
if(!(b_part.child_type() == 0) && totchild != 0)
pa_no = totparts;
int num_add = (totparts+totchild - pa_no);
CData->curve_firstkey.reserve(CData->curve_firstkey.size() + num_add);
CData->curve_keynum.reserve(CData->curve_keynum.size() + num_add);
CData->curve_length.reserve(CData->curve_length.size() + num_add);
CData->curvekey_co.reserve(CData->curvekey_co.size() + num_add*ren_step);
CData->curvekey_time.reserve(CData->curvekey_time.size() + num_add*ren_step);
for(; pa_no < totparts+totchild; pa_no++) {
int keynum = 0;
CData->curve_firstkey.push_back_slow(keyno);
float curve_length = 0.0f;
float3 pcKey;
for(int step_no = 0; step_no < ren_step; step_no++) {
float nco[3];
b_psys.co_hair(*b_ob, pa_no, step_no, nco);
float3 cKey = make_float3(nco[0], nco[1], nco[2]);
cKey = transform_point(&itfm, cKey);
if(step_no > 0) {
float step_length = len(cKey - pcKey);
if(step_length == 0.0f)
continue;
curve_length += step_length;
}
CData->curvekey_co.push_back_slow(cKey);
CData->curvekey_time.push_back_slow(curve_length);
pcKey = cKey;
keynum++;
}
keyno += keynum;
CData->curve_keynum.push_back_slow(keynum);
CData->curve_length.push_back_slow(curve_length);
curvenum++;
}
if (background ? b_mod->show_render() : b_mod->show_viewport())
{
if((b_mod->type() == b_mod->type_PARTICLE_SYSTEM)) {
BL::ParticleSystemModifier b_psmd((const PointerRNA)b_mod->ptr);
ObtainCacheDataFromParticleSystem(mesh,
b_ob,
&b_psmd,
CData,
background,
&curvenum,
&keyno);
}
if((b_mod->type() == b_mod->type_HAIR)) {
BL::HairModifier b_hmd((const PointerRNA)b_mod->ptr);
BL::HairSystem b_hsys = b_hmd.hair_system();
const int material_index = 1; /* TODO */
int shader = clamp(material_index - 1, 0, mesh->used_shaders.size()-1);
BL::Mesh b_scalp(b_ob->data());
ObtainCacheDataFromHairSystem(b_ob,
&b_hsys,
&b_scalp,
shader,
background,
CData,
&curvenum,
&keyno);
}
}
}
@@ -206,12 +453,12 @@ static bool ObtainCacheParticleData(Mesh *mesh,
return true;
}
static bool ObtainCacheParticleUV(Mesh *mesh,
BL::Mesh *b_mesh,
BL::Object *b_ob,
ParticleCurveData *CData,
bool background,
int uv_num)
static bool ObtainCacheUVFromObject(Mesh *mesh,
BL::Mesh *b_mesh,
BL::Object *b_ob,
ParticleCurveData *CData,
bool background,
int uv_num)
{
if(!(mesh && b_mesh && b_ob && CData))
return false;
@@ -220,44 +467,11 @@ static bool ObtainCacheParticleUV(Mesh *mesh,
BL::Object::modifiers_iterator b_mod;
for(b_ob->modifiers.begin(b_mod); b_mod != b_ob->modifiers.end(); ++b_mod) {
if((b_mod->type() == b_mod->type_PARTICLE_SYSTEM) && (background ? b_mod->show_render() : b_mod->show_viewport())) {
BL::ParticleSystemModifier psmd((const PointerRNA)b_mod->ptr);
BL::ParticleSystem b_psys((const PointerRNA)psmd.particle_system().ptr);
BL::ParticleSettings b_part((const PointerRNA)b_psys.settings().ptr);
if((b_part.render_type() == BL::ParticleSettings::render_type_PATH) && (b_part.type() == BL::ParticleSettings::type_HAIR)) {
int totparts = b_psys.particles.length();
int totchild = background ? b_psys.child_particles.length() : (int)((float)b_psys.child_particles.length() * (float)b_part.draw_percentage() / 100.0f);
int totcurves = totchild;
if(b_part.child_type() == 0 || totchild == 0)
totcurves += totparts;
if(totcurves == 0)
continue;
int pa_no = 0;
if(!(b_part.child_type() == 0) && totchild != 0)
pa_no = totparts;
int num_add = (totparts+totchild - pa_no);
CData->curve_uv.reserve(CData->curve_uv.size() + num_add);
BL::ParticleSystem::particles_iterator b_pa;
b_psys.particles.begin(b_pa);
for(; pa_no < totparts+totchild; pa_no++) {
/* Add UVs */
BL::Mesh::tessface_uv_textures_iterator l;
b_mesh->tessface_uv_textures.begin(l);
float3 uv = make_float3(0.0f, 0.0f, 0.0f);
if(b_mesh->tessface_uv_textures.length())
b_psys.uv_on_emitter(psmd, *b_pa, pa_no, uv_num, &uv.x);
CData->curve_uv.push_back_slow(uv);
if(pa_no < totparts && b_pa != b_psys.particles.end())
++b_pa;
}
if (background ? b_mod->show_render() : b_mod->show_viewport())
{
if((b_mod->type() == b_mod->type_PARTICLE_SYSTEM)) {
BL::ParticleSystemModifier b_psmd((const PointerRNA)b_mod->ptr);
ObtainCacheUVFromParticleSystem(b_mesh, &b_psmd, CData, background, uv_num);
}
}
}
@@ -265,12 +479,12 @@ static bool ObtainCacheParticleUV(Mesh *mesh,
return true;
}
static bool ObtainCacheParticleVcol(Mesh *mesh,
BL::Mesh *b_mesh,
BL::Object *b_ob,
ParticleCurveData *CData,
bool background,
int vcol_num)
static bool ObtainCacheVcolFromObject(Mesh *mesh,
BL::Mesh *b_mesh,
BL::Object *b_ob,
ParticleCurveData *CData,
bool background,
int vcol_num)
{
if(!(mesh && b_mesh && b_ob && CData))
return false;
@@ -279,44 +493,12 @@ static bool ObtainCacheParticleVcol(Mesh *mesh,
BL::Object::modifiers_iterator b_mod;
for(b_ob->modifiers.begin(b_mod); b_mod != b_ob->modifiers.end(); ++b_mod) {
if((b_mod->type() == b_mod->type_PARTICLE_SYSTEM) && (background ? b_mod->show_render() : b_mod->show_viewport())) {
BL::ParticleSystemModifier psmd((const PointerRNA)b_mod->ptr);
BL::ParticleSystem b_psys((const PointerRNA)psmd.particle_system().ptr);
BL::ParticleSettings b_part((const PointerRNA)b_psys.settings().ptr);
if((b_part.render_type() == BL::ParticleSettings::render_type_PATH) && (b_part.type() == BL::ParticleSettings::type_HAIR)) {
int totparts = b_psys.particles.length();
int totchild = background ? b_psys.child_particles.length() : (int)((float)b_psys.child_particles.length() * (float)b_part.draw_percentage() / 100.0f);
int totcurves = totchild;
if(b_part.child_type() == 0 || totchild == 0)
totcurves += totparts;
if(totcurves == 0)
continue;
int pa_no = 0;
if(!(b_part.child_type() == 0) && totchild != 0)
pa_no = totparts;
int num_add = (totparts+totchild - pa_no);
CData->curve_vcol.reserve(CData->curve_vcol.size() + num_add);
BL::ParticleSystem::particles_iterator b_pa;
b_psys.particles.begin(b_pa);
for(; pa_no < totparts+totchild; pa_no++) {
/* Add vertex colors */
BL::Mesh::tessface_vertex_colors_iterator l;
b_mesh->tessface_vertex_colors.begin(l);
float3 vcol = make_float3(0.0f, 0.0f, 0.0f);
if(b_mesh->tessface_vertex_colors.length())
b_psys.mcol_on_emitter(psmd, *b_pa, pa_no, vcol_num, &vcol.x);
CData->curve_vcol.push_back_slow(vcol);
if(pa_no < totparts && b_pa != b_psys.particles.end())
++b_pa;
}
if (background ? b_mod->show_render() : b_mod->show_viewport())
{
if((b_mod->type() == b_mod->type_PARTICLE_SYSTEM))
{
BL::ParticleSystemModifier b_psmd((const PointerRNA)b_mod->ptr);
ObtainCacheVColFromParticleSystem(b_mesh, &b_psmd, CData, background, vcol_num);
}
}
}
@@ -906,7 +1088,7 @@ void BlenderSync::sync_curves(Mesh *mesh,
ParticleCurveData CData;
ObtainCacheParticleData(mesh, &b_mesh, &b_ob, &CData, !preview);
ObtainCacheDataFromObject(mesh, &b_ob, &CData, !preview);
/* add hair geometry to mesh */
if(primitive == CURVE_TRIANGLES) {
@@ -942,7 +1124,7 @@ void BlenderSync::sync_curves(Mesh *mesh,
/* generated coordinates from first key. we should ideally get this from
* blender to handle deforming objects */
if(!motion) {
if(b_mesh && !motion) {
if(mesh->need_attribute(scene, ATTR_STD_GENERATED)) {
float3 loc, size;
mesh_texture_space(b_mesh, loc, size);
@@ -967,7 +1149,7 @@ void BlenderSync::sync_curves(Mesh *mesh,
}
/* create vertex color attributes */
if(!motion) {
if(b_mesh && !motion) {
BL::Mesh::tessface_vertex_colors_iterator l;
int vcol_num = 0;
@@ -975,7 +1157,7 @@ void BlenderSync::sync_curves(Mesh *mesh,
if(!mesh->need_attribute(scene, ustring(l->name().c_str())))
continue;
ObtainCacheParticleVcol(mesh, &b_mesh, &b_ob, &CData, !preview, vcol_num);
ObtainCacheVcolFromObject(mesh, &b_mesh, &b_ob, &CData, !preview, vcol_num);
if(primitive == CURVE_TRIANGLES) {
Attribute *attr_vcol = mesh->attributes.add(
@@ -1004,7 +1186,7 @@ void BlenderSync::sync_curves(Mesh *mesh,
}
/* create UV attributes */
if(!motion) {
if(b_mesh && !motion) {
BL::Mesh::tessface_uv_textures_iterator l;
int uv_num = 0;
@@ -1017,7 +1199,7 @@ void BlenderSync::sync_curves(Mesh *mesh,
if(mesh->need_attribute(scene, name) || mesh->need_attribute(scene, std)) {
Attribute *attr_uv;
ObtainCacheParticleUV(mesh, &b_mesh, &b_ob, &CData, !preview, uv_num);
ObtainCacheUVFromObject(mesh, &b_mesh, &b_ob, &CData, !preview, uv_num);
if(primitive == CURVE_TRIANGLES) {
if(active_render)

View File

@@ -1188,10 +1188,12 @@ Mesh *BlenderSync::sync_mesh(BL::Depsgraph& b_depsgraph,
create_mesh_volume_attributes(scene, b_ob, mesh, b_scene.frame_current());
}
}
if(view_layer.use_hair && mesh->subdivision_type == Mesh::SUBDIVISION_NONE)
sync_curves(mesh, b_mesh, b_ob, false);
if(view_layer.use_hair && mesh->subdivision_type == Mesh::SUBDIVISION_NONE)
sync_curves(mesh, b_mesh, b_ob, false);
if(b_mesh) {
/* free derived mesh */
b_data.meshes.remove(b_mesh, false, true, false);
}

View File

@@ -36,6 +36,11 @@ void BKE_image_user_frame_calc(void *iuser, int cfra, int fieldnr);
void BKE_image_user_file_path(void *iuser, void *ima, char *path);
unsigned char *BKE_image_get_pixels_for_frame(void *image, int frame);
float *BKE_image_get_float_pixels_for_frame(void *image, int frame);
void* BKE_hair_export_cache_new(void);
int BKE_hair_export_cache_update(void *cache, const void *hsys, int subdiv, void *scalp, int requested_data);
void BKE_hair_export_cache_free(void *hcache);
void BKE_hair_render_get_buffer_size(void* hcache, int *r_totcurves, int *r_totverts);
void BKE_hair_render_fill_buffers(void* hcache, int vertco_stride, int *r_curvestart, int *r_curvelen, float *r_vertco);
}
CCL_NAMESPACE_BEGIN

View File

@@ -44,6 +44,7 @@ _modules = [
"properties_data_lightprobe",
"properties_data_speaker",
"properties_data_workspace",
"properties_hair_common",
"properties_mask_common",
"properties_material",
"properties_material_gpencil",

View File

@@ -20,7 +20,7 @@
import bpy
from bpy.types import Panel
from bpy.app.translations import pgettext_iface as iface_
from .properties_hair_common import draw_hair_display_settings
class ModifierButtonsPanel:
bl_space_type = 'PROPERTIES'
@@ -1577,6 +1577,27 @@ 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):
hsys = md.hair_system
split = layout.split()
col = split.column()
col.label("Follicles:")
col.prop(md, "follicle_seed")
col.prop(md, "follicle_count")
col.operator("object.hair_generate_follicles", text="Generate")
col = split.column()
col.separator()
col.prop(hsys, "material_slot", text="")
col = layout.column()
col.label("Display Settings:")
draw_hair_display_settings(col, md.draw_settings)
def WEIGHTED_NORMAL(self, layout, ob, md):
layout.label("Weighting Mode:")
split = layout.split(align=True)

View File

@@ -0,0 +1,61 @@
# ##### 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-80 compliant>
import bpy
def draw_hair_display_settings(layout, settings):
col = layout.column(align=True)
col.label("Follicles:")
col.prop(settings, "follicle_mode", expand=True)
col = layout.column(align=True)
col.label("Guide Curves:")
col.prop(settings, "guide_mode", expand=True)
layout.prop(settings, "shape")
col = layout.column(align=True)
col.prop(settings, "root_radius")
col.prop(settings, "tip_radius")
col = layout.column()
col.prop(settings, "radius_scale")
col.prop(settings, "use_close_tip")
class HAIR_PT_display_settings:
# subclasses must define...
# ~ bl_space_type = 'PROPERTIES'
# ~ bl_region_type = 'WINDOW'
bl_label = "Hair Display Settings"
def draw(self, context):
settings = context.draw_hair_display_settings
draw_hair_display_settings(self.layout, hair_display_settings)
classes = (
)
if __name__ == "__main__": # only for live edit.
from bpy.utils import register_class
for cls in classes:
register_class(cls)

View File

@@ -78,7 +78,6 @@ def is_not_gpencil_edit_mode(context):
)
return not is_gpmode
# ********** default tools for editmode_mesh ****************

View File

@@ -49,6 +49,7 @@ set(SRC_DNA_INC
${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_shader_fx_types.h
${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_gpu_types.h
${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_group_types.h
${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_hair_types.h
${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_image_types.h
${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_ipo_types.h
${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_key_types.h

View File

@@ -0,0 +1,238 @@
/*
* ***** BEGIN GPL LICENSE BLOCK *****
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* The Original Code is Copyright (C) Blender Foundation
* All rights reserved.
*
* The Original Code is: all of this file.
*
* Contributor(s): Lukas Toenne
*
* ***** END GPL LICENSE BLOCK *****
*/
#ifndef __BKE_HAIR_H__
#define __BKE_HAIR_H__
/** \file blender/blenkernel/BKE_hair.h
* \ingroup bke
*/
#include "BLI_utildefines.h"
static const unsigned int HAIR_CURVE_INDEX_NONE = 0xFFFFFFFF;
struct HairFollicle;
struct HairPattern;
struct HairSystem;
struct HairDrawSettings;
struct HairCurveData;
struct Mesh;
struct MeshSample;
struct MLoop;
struct Object;
/* Create a new hair system instance */
struct HairSystem* BKE_hair_new(void);
/* Copy an existing hair system */
struct HairSystem* BKE_hair_copy(struct HairSystem *hsys);
/* Delete a hair system */
void BKE_hair_free(struct HairSystem *hsys);
/* === Fiber curves === */
/* Allocate buffers for defining fiber curves
* \param totcurves Number of fiber curves to allocate
*/
void BKE_hair_fiber_curves_begin(struct HairSystem *hsys, int totcurves);
/* Set properties of a fiber curve
* \param index Index of the fiber curve
* \param mesh_sample Origin of the fiber curve on the scalp mesh.
* \param numverts Number of vertices in this fiber curve
*/
void BKE_hair_set_fiber_curve(struct HairSystem *hsys, int index, int numverts,
float taper_length, float taper_thickness);
/* Finalize fiber curve update */
void BKE_hair_fiber_curves_end(struct HairSystem *hsys);
/* Set properties of a fiber curve vertex
* \param index Index of the fiber curve vertex.
* \param flag Flags to set on the vertex.
* \param co Location of the vertex in object space.
*/
void BKE_hair_set_fiber_vertex(struct HairSystem *hsys, int index, int flag, const float co[3]);
/* Set the hair fiber curve data used by the hair system.
*/
void BKE_hair_set_fiber_curves(struct HairSystem *hsys, struct HairCurveData *curves);
/* Remove all fiber curves.
*/
void BKE_hair_clear_fiber_curves(struct HairSystem *hsys);
/* === Follicles === */
/* Calculate surface area of a scalp mesh */
float BKE_hair_calc_surface_area(const struct Mesh *scalp);
/* Calculate a density value based on surface area and sample count */
float BKE_hair_calc_density_from_count(float area, int count);
/* Calculate maximum sample count based on surface area and density */
int BKE_hair_calc_max_count_from_density(float area, float density);
/* Calculate a density value based on a minimum distance */
float BKE_hair_calc_density_from_min_distance(float min_distance);
/* Calculate a minimum distance based on density */
float BKE_hair_calc_min_distance_from_density(float density);
/* Distribute hair follicles on a scalp mesh */
void BKE_hair_generate_follicles(
struct HairSystem* hsys,
struct Mesh *scalp,
unsigned int seed,
int count);
/* Distribute hair follicles on a scalp mesh.
* Optional per-loop weights control follicle density on the scalp.
*/
void BKE_hair_generate_follicles_ex(
struct HairSystem* hsys,
struct Mesh *scalp,
unsigned int seed,
int count,
const float *loop_weights);
bool BKE_hair_bind_follicles(struct HairSystem *hsys, const struct Mesh *scalp);
/* === Draw Settings === */
struct HairDrawSettings* BKE_hair_draw_settings_new(void);
struct HairDrawSettings* BKE_hair_draw_settings_copy(struct HairDrawSettings *draw_settings);
void BKE_hair_draw_settings_free(struct HairDrawSettings *draw_settings);
/* === Export === */
/* Intermediate data for export */
typedef struct HairExportCache
{
/* Per fiber curve data */
int totcurves;
struct HairFiberCurve *fiber_curves;
/* Per fiber vertex data */
int totverts;
struct HairFiberVertex *fiber_verts;
float (*fiber_tangents)[3]; /* Tangent vectors on fiber curves */
float (*fiber_normals)[3]; /* Normal vectors on fiber curves */
/* Per follicle data */
int totfollicles;
float (*follicle_root_position)[3]; /* Root position of each follicle */
const struct HairFollicle *follicles;
} HairExportCache;
/* Identifiers for data stored in hair export caches */
typedef enum eHairExportCacheUpdateFlags
{
/* Follicle placement on the scalp mesh */
HAIR_EXPORT_FOLLICLE_ROOT_POSITIONS = (1 << 0),
/* Follicle curve index */
HAIR_EXPORT_FOLLICLE_BINDING = (1 << 1),
/* Fiber vertex positions (deform only) */
HAIR_EXPORT_FIBER_VERTICES = (1 << 2),
/* Fiber curve number and vertex counts (topology changes) */
HAIR_EXPORT_FIBER_CURVES = (1 << 3),
HAIR_EXPORT_ALL =
HAIR_EXPORT_FOLLICLE_ROOT_POSITIONS |
HAIR_EXPORT_FOLLICLE_BINDING |
HAIR_EXPORT_FIBER_VERTICES |
HAIR_EXPORT_FIBER_CURVES,
HAIR_EXPORT_FIBERS =
HAIR_EXPORT_FIBER_VERTICES |
HAIR_EXPORT_FIBER_CURVES,
HAIR_EXPORT_FOLLICLES =
HAIR_EXPORT_FOLLICLE_ROOT_POSITIONS |
HAIR_EXPORT_FOLLICLE_BINDING,
} eHairExportCacheUpdateFlags;
/* Create a new export cache.
* This can be used to construct full fiber data for rendering.
*/
struct HairExportCache* BKE_hair_export_cache_new(void);
/* Update an existing export cache to ensure it contains the requested data.
* Returns flags for data that has been updated.
*/
int BKE_hair_export_cache_update(struct HairExportCache *cache, const struct HairSystem *hsys,
int subdiv, struct Mesh *scalp, int requested_data);
/* Free the given export cache */
void BKE_hair_export_cache_free(struct HairExportCache *cache);
/* Invalidate all data in a hair export cache */
void BKE_hair_export_cache_clear(struct HairExportCache *cache);
/* Invalidate part of the data in a hair export cache.
*
* Note some parts may get invalidated automatically based on internal dependencies.
*/
void BKE_hair_export_cache_invalidate(struct HairExportCache *cache, int invalidate);
/* === Draw Cache === */
enum {
BKE_HAIR_BATCH_DIRTY_FIBERS = (1 << 0),
BKE_HAIR_BATCH_DIRTY_STRANDS = (1 << 1),
BKE_HAIR_BATCH_DIRTY_ALL = 0xFFFF,
};
void BKE_hair_batch_cache_dirty(struct HairSystem* hsys, int mode);
void BKE_hair_batch_cache_free(struct HairSystem* hsys);
void BKE_hair_get_texture_buffer_size(
const struct HairExportCache *cache,
int *r_size,
int *r_strand_map_start,
int *r_strand_vertex_start,
int *r_fiber_start);
void BKE_hair_get_texture_buffer(
const struct HairExportCache *cache,
void *texbuffer);
/* === Render API === */
/* Calculate required size for render buffers. */
void BKE_hair_render_get_buffer_size(
const struct HairExportCache* cache,
int subdiv,
int *r_totcurves,
int *r_totverts);
/* Create render data in existing buffers.
* Buffers must be large enough according to BKE_hair_get_render_buffer_size.
*/
void BKE_hair_render_fill_buffers(
const struct HairExportCache* cache,
int subdiv,
int vertco_stride,
int *r_curvestart,
int *r_curvelen,
float *r_vertco);
#endif

View File

@@ -0,0 +1,116 @@
/*
* ***** 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 Mesh;
struct Key;
struct KeyBlock;
struct Mesh;
struct MFace;
struct MVert;
struct MPoly;
struct MeshSample;
struct MeshSampleGenerator;
typedef struct MeshSampleGenerator MeshSampleGenerator;
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 Mesh *mesh, const float *loop_weights, float *r_area);
void BKE_mesh_sample_weights_from_loc(struct MeshSample *sample, struct Mesh *mesh, int looptri_index, const float loc[3]);
/* ==== Evaluate ==== */
bool BKE_mesh_sample_is_valid(const struct MeshSample *sample);
bool BKE_mesh_sample_is_volume_sample(const struct MeshSample *sample);
/* Evaluate position and normal on the given mesh */
bool BKE_mesh_sample_eval(const struct Mesh *mesh, const struct MeshSample *sample, float loc[3], float nor[3], float tang[3]);
/* Evaluate position for the given shapekey */
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, const float *loop_weights);
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, const float *loop_weights);
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 Mesh *mesh);
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 Mesh *mesh, struct ParticleData *pa);
bool BKE_mesh_sample_to_particle(struct MeshSample *sample, struct ParticleSystem *psys, struct Mesh *mesh, struct BVHTreeFromMesh *bvhtree, struct ParticleData *pa);
#endif /* __BKE_MESH_SAMPLE_H__ */

View File

@@ -50,6 +50,7 @@ struct Main;
struct Object;
struct Scene;
struct Depsgraph;
struct Mesh;
struct ModifierData;
struct MTFace;
struct MCol;
@@ -433,6 +434,7 @@ void psys_interpolate_face(struct MVert *mvert, struct MFace *mface, struct MTFa
float orco[3]);
float psys_particle_value_from_verts(struct Mesh *mesh, 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_mesh(struct ParticleSystem *psys, struct Mesh *mesh, 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);

View File

@@ -118,6 +118,8 @@ set(SRC
intern/freestyle.c
intern/gpencil.c
intern/gpencil_modifier.c
intern/hair.c
intern/hair_draw.c
intern/icons.c
intern/icons_rasterize.c
intern/idcode.c
@@ -149,6 +151,7 @@ set(SRC
intern/mesh_merge.c
intern/mesh_remap.c
intern/mesh_runtime.c
intern/mesh_sample.c
intern/mesh_tangent.c
intern/mesh_validate.c
intern/modifier.c
@@ -264,6 +267,7 @@ set(SRC
BKE_global.h
BKE_gpencil.h
BKE_gpencil_modifier.h
BKE_hair.h
BKE_icons.h
BKE_idcode.h
BKE_idprop.h
@@ -288,6 +292,7 @@ set(SRC
BKE_mesh_mapping.h
BKE_mesh_remap.h
BKE_mesh_runtime.h
BKE_mesh_sample.h
BKE_mesh_tangent.h
BKE_modifier.h
BKE_movieclip.h

View File

@@ -0,0 +1,721 @@
/*
* ***** BEGIN GPL LICENSE BLOCK *****
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* The Original Code is Copyright (C) Blender Foundation
* All rights reserved.
*
* The Original Code is: all of this file.
*
* Contributor(s): Lukas Toenne
*
* ***** END GPL LICENSE BLOCK *****
*/
/** \file blender/blenkernel/intern/hair.c
* \ingroup bke
*/
#include <limits.h>
#include <string.h>
#include "MEM_guardedalloc.h"
#include "BLI_kdtree.h"
#include "BLI_listbase.h"
#include "BLI_math.h"
#include "BLI_rand.h"
#include "BLI_sort.h"
#include "BLI_string_utf8.h"
#include "BLI_string_utils.h"
#include "DNA_hair_types.h"
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
#include "DNA_object_types.h"
#include "BKE_hair.h"
#include "BKE_library.h"
#include "BKE_mesh.h"
#include "BKE_mesh_sample.h"
#include "BLT_translation.h"
HairSystem* BKE_hair_new(void)
{
HairSystem *hair = MEM_callocN(sizeof(HairSystem), "hair system");
hair->pattern = MEM_callocN(sizeof(HairPattern), "hair pattern");
return hair;
}
HairSystem* BKE_hair_copy(HairSystem *hsys)
{
HairSystem *nhsys = MEM_dupallocN(hsys);
if (hsys->pattern)
{
nhsys->pattern = MEM_dupallocN(hsys->pattern);
nhsys->pattern->follicles = MEM_dupallocN(hsys->pattern->follicles);
}
if (hsys->curve_data.curves)
{
nhsys->curve_data.curves = MEM_dupallocN(hsys->curve_data.curves);
}
if (hsys->curve_data.verts)
{
nhsys->curve_data.verts = MEM_dupallocN(hsys->curve_data.verts);
}
nhsys->draw_batch_cache = NULL;
nhsys->draw_texture_cache = NULL;
return nhsys;
}
void BKE_hair_free(HairSystem *hsys)
{
BKE_hair_batch_cache_free(hsys);
if (hsys->curve_data.curves)
{
MEM_freeN(hsys->curve_data.curves);
}
if (hsys->curve_data.verts)
{
MEM_freeN(hsys->curve_data.verts);
}
if (hsys->pattern)
{
if (hsys->pattern->follicles)
{
MEM_freeN(hsys->pattern->follicles);
}
MEM_freeN(hsys->pattern);
}
MEM_freeN(hsys);
}
/* Calculate surface area of a scalp mesh */
float BKE_hair_calc_surface_area(const Mesh *scalp)
{
BLI_assert(scalp != NULL);
int numpolys = scalp->totpoly;
MPoly *mpolys = scalp->mpoly;
MLoop *mloops = scalp->mloop;
MVert *mverts = scalp->mvert;
float area = 0.0f;
for (int i = 0; i < numpolys; ++i)
{
area += BKE_mesh_calc_poly_area(&mpolys[i], mloops + mpolys[i].loopstart, mverts);
}
return area;
}
/* Calculate a density value based on surface area and sample count */
float BKE_hair_calc_density_from_count(float area, int count)
{
return area > 0.0f ? count / area : 0.0f;
}
/* Calculate maximum sample count based on surface area and density */
int BKE_hair_calc_max_count_from_density(float area, float density)
{
return (int)(density * area);
}
/* Calculate a density value based on a minimum distance */
float BKE_hair_calc_density_from_min_distance(float min_distance)
{
// max. circle packing density (sans pi factor): 1 / (2 * sqrt(3))
static const float max_factor = 0.288675135;
return min_distance > 0.0f ? max_factor / (min_distance * min_distance) : 0.0f;
}
/* Calculate a minimum distance based on density */
float BKE_hair_calc_min_distance_from_density(float density)
{
// max. circle packing density (sans pi factor): 1 / (2 * sqrt(3))
static const float max_factor = 0.288675135;
return density > 0.0f ? sqrt(max_factor / density) : 0.0f;
}
/* Distribute hair follicles on a scalp mesh */
void BKE_hair_generate_follicles(
HairSystem* hsys,
struct Mesh *scalp,
unsigned int seed,
int count)
{
BKE_hair_generate_follicles_ex(hsys, scalp, seed, count, NULL);
}
/* Distribute hair follicles on a scalp mesh.
* Optional per-loop weights control follicle density on the scalp.
*/
void BKE_hair_generate_follicles_ex(
HairSystem* hsys,
struct Mesh *scalp,
unsigned int seed,
int count,
const float *loop_weights)
{
HairPattern *pattern = hsys->pattern;
// Limit max_count to theoretical limit based on area
float scalp_area = BKE_hair_calc_surface_area(scalp);
float density = BKE_hair_calc_density_from_count(scalp_area, count);
float min_distance = BKE_hair_calc_min_distance_from_density(density);
if (pattern->follicles)
{
MEM_freeN(pattern->follicles);
}
pattern->follicles = MEM_callocN(sizeof(HairFollicle) * count, "hair follicles");
{
MeshSampleGenerator *gen = BKE_mesh_sample_gen_surface_poissondisk(seed, min_distance, count, loop_weights);
BKE_mesh_sample_generator_bind(gen, scalp);
static const bool use_threads = false;
pattern->num_follicles = BKE_mesh_sample_generate_batch_ex(
gen,
&pattern->follicles->mesh_sample,
sizeof(HairFollicle),
count,
use_threads);
BKE_mesh_sample_free_generator(gen);
}
hsys->flag |= HAIR_SYSTEM_UPDATE_FOLLICLE_BINDING;
BKE_hair_batch_cache_dirty(hsys, BKE_HAIR_BATCH_DIRTY_ALL);
}
/* ================================= */
void BKE_hair_fiber_curves_begin(HairSystem *hsys, int totcurves)
{
if (totcurves != hsys->curve_data.totcurves)
{
hsys->curve_data.curves = MEM_reallocN(hsys->curve_data.curves, sizeof(HairFiberCurve) * totcurves);
hsys->curve_data.totcurves = totcurves;
hsys->flag |= HAIR_SYSTEM_UPDATE_FOLLICLE_BINDING;
BKE_hair_batch_cache_dirty(hsys, BKE_HAIR_BATCH_DIRTY_ALL);
}
}
void BKE_hair_set_fiber_curve(HairSystem *hsys, int index, int numverts,
float taper_length, float taper_thickness)
{
BLI_assert(index <= hsys->curve_data.totcurves);
HairFiberCurve *curve = &hsys->curve_data.curves[index];
curve->numverts = numverts;
curve->taper_length = taper_length;
curve->taper_thickness = taper_thickness;
hsys->flag |= HAIR_SYSTEM_UPDATE_FOLLICLE_BINDING;
BKE_hair_batch_cache_dirty(hsys, BKE_HAIR_BATCH_DIRTY_ALL);
}
/* Calculate vertex start indices on all curves based on length.
* Returns the total number of vertices.
*/
static int hair_curve_calc_vertstart(HairSystem *hsys)
{
/* Recalculate vertex count and start offsets in curves */
int vertstart = 0;
for (int i = 0; i < hsys->curve_data.totcurves; ++i)
{
hsys->curve_data.curves[i].vertstart = vertstart;
vertstart += hsys->curve_data.curves[i].numverts;
}
return vertstart;
}
void BKE_hair_fiber_curves_end(HairSystem *hsys)
{
const int totverts = hair_curve_calc_vertstart(hsys);
if (totverts != hsys->curve_data.totverts)
{
hsys->curve_data.verts = MEM_reallocN(hsys->curve_data.verts, sizeof(HairFiberVertex) * totverts);
hsys->curve_data.totverts = totverts;
BKE_hair_batch_cache_dirty(hsys, BKE_HAIR_BATCH_DIRTY_ALL);
}
}
void BKE_hair_set_fiber_vertex(HairSystem *hsys, int index, int flag, const float co[3])
{
BLI_assert(index <= hsys->curve_data.totverts);
HairFiberVertex *vertex = &hsys->curve_data.verts[index];
vertex->flag = flag;
copy_v3_v3(vertex->co, co);
BKE_hair_batch_cache_dirty(hsys, BKE_HAIR_BATCH_DIRTY_ALL);
}
void BKE_hair_set_fiber_curves(HairSystem *hsys, HairCurveData *curves)
{
if (hsys->curve_data.curves)
{
MEM_freeN(hsys->curve_data.curves);
}
hsys->curve_data.curves = MEM_dupallocN(hsys->curve_data.curves);
hsys->curve_data.totcurves = curves->totcurves;
if (hsys->curve_data.verts)
{
MEM_freeN(hsys->curve_data.verts);
}
hsys->curve_data.verts = MEM_dupallocN(hsys->curve_data.verts);
hsys->curve_data.totverts = curves->totverts;
#ifndef NDEBUG
const int vertcount = hair_curve_calc_vertstart(hsys);
BLI_assert(vertcount <= hsys->curve_data.totverts);
#endif
hsys->flag |= HAIR_SYSTEM_UPDATE_FOLLICLE_BINDING;
BKE_hair_batch_cache_dirty(hsys, BKE_HAIR_BATCH_DIRTY_ALL);
}
void BKE_hair_clear_fiber_curves(HairSystem *hsys)
{
if (hsys->curve_data.curves)
{
MEM_freeN(hsys->curve_data.curves);
hsys->curve_data.curves = NULL;
}
hsys->curve_data.totcurves = 0;
if (hsys->curve_data.verts)
{
MEM_freeN(hsys->curve_data.verts);
hsys->curve_data.verts = NULL;
}
hsys->curve_data.totverts = 0;
hsys->flag &= ~HAIR_SYSTEM_UPDATE_FOLLICLE_BINDING;
BKE_hair_batch_cache_dirty(hsys, BKE_HAIR_BATCH_DIRTY_ALL);
}
/* ================================= */
bool BKE_hair_bind_follicles(HairSystem *hsys, const Mesh *scalp)
{
if (!(hsys->flag & HAIR_SYSTEM_UPDATE_FOLLICLE_BINDING))
{
return true;
}
hsys->flag &= ~HAIR_SYSTEM_UPDATE_FOLLICLE_BINDING;
HairPattern *pattern = hsys->pattern;
if (!pattern)
{
return true;
}
const int num_strands = hsys->curve_data.totcurves;
/* Need at least one curve for binding */
if (num_strands == 0)
{
HairFollicle *follicle = pattern->follicles;
for (int i = 0; i < pattern->num_follicles; ++i, ++follicle)
{
for (int k = 0; k < 4; ++k)
{
follicle->curve = HAIR_CURVE_INDEX_NONE;
}
}
return false;
}
KDTree *tree = BLI_kdtree_new(num_strands);
for (int c = 0; c < num_strands; ++c)
{
const int vertstart = hsys->curve_data.curves[c].vertstart;
const float *rootco = hsys->curve_data.verts[vertstart].co;
BLI_kdtree_insert(tree, c, rootco);
}
BLI_kdtree_balance(tree);
{
HairFollicle *follicle = pattern->follicles;
for (int i = 0; i < pattern->num_follicles; ++i, ++follicle)
{
float loc[3], nor[3], tang[3];
if (BKE_mesh_sample_eval(scalp, &follicle->mesh_sample, loc, nor, tang))
{
follicle->curve = BLI_kdtree_find_nearest(tree, loc, NULL);
}
}
}
BLI_kdtree_free(tree);
return true;
}
/* === Export === */
/* Returns number of vertices in a curve after subdivision */
BLI_INLINE int hair_get_strand_subdiv_length(int orig_length, int subdiv)
{
return ((orig_length - 1) << subdiv) + 1;
}
/* Returns total number of vertices after subdivision */
BLI_INLINE int hair_get_strand_subdiv_numverts(int numstrands, int numverts, int subdiv)
{
return ((numverts - numstrands) << subdiv) + numstrands;
}
/* Subdivide a curve */
static int hair_curve_subdivide(const HairFiberCurve* curve, const HairFiberVertex* verts,
int subdiv, HairFiberVertex *r_verts)
{
{
/* Move vertex positions from the dense array to their initial configuration for subdivision.
* Also add offset to ensure the curve starts on the scalp surface.
*/
const int step = (1 << subdiv);
BLI_assert(curve->numverts > 0);
HairFiberVertex *dst = r_verts;
for (int i = 0; i < curve->numverts; ++i) {
copy_v3_v3(dst->co, verts[i].co);
dst += step;
}
}
/* Subdivide */
for (int d = 0; d < subdiv; ++d) {
const int num_edges = (curve->numverts - 1) << d;
const int hstep = 1 << (subdiv - d - 1);
const int step = 1 << (subdiv - d);
/* Calculate edge points */
{
int index = 0;
for (int k = 0; k < num_edges; ++k, index += step) {
add_v3_v3v3(r_verts[index + hstep].co, r_verts[index].co, r_verts[index + step].co);
mul_v3_fl(r_verts[index + hstep].co, 0.5f);
}
}
/* Move original points */
{
int index = step;
for (int k = 1; k < num_edges; ++k, index += step) {
add_v3_v3v3(r_verts[index].co, r_verts[index - hstep].co, r_verts[index + hstep].co);
mul_v3_fl(r_verts[index].co, 0.5f);
}
}
}
const int num_verts = ((curve->numverts - 1) << subdiv) + 1;
return num_verts;
}
/* Calculate tangent and normal vector changes from one segment to the next */
static void hair_curve_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);
}
/* Calculate tangent and normal vectors for all vertices on a curve */
static void hair_curve_calc_vectors(const HairFiberVertex* verts, int numverts,
float (*r_tangents)[3], float (*r_normals)[3])
{
BLI_assert(numverts >= 2);
float prev_tang[3] = {0.0f, 0.0f, 1.0f};
float prev_nor[3] = {1.0f, 0.0f, 0.0f};
hair_curve_transport_frame(
verts[0].co, verts[1].co,
prev_tang, prev_nor,
r_tangents[0], r_normals[0]);
for (int i = 1; i < numverts - 1; ++i)
{
hair_curve_transport_frame(
verts[i-1].co, verts[i+1].co,
prev_tang, prev_nor,
r_tangents[i], r_normals[i]);
}
hair_curve_transport_frame(
verts[numverts-2].co, verts[numverts-1].co,
prev_tang, prev_nor,
r_tangents[numverts-1], r_normals[numverts-1]);
}
/* Create a new export cache.
* This can be used to construct full fiber data for rendering.
*/
HairExportCache* BKE_hair_export_cache_new(void)
{
HairExportCache *cache = MEM_callocN(sizeof(HairExportCache), "hair export cache");
return cache;
}
/* Returns flags for missing data parts */
static int hair_export_cache_get_required_updates(const HairExportCache *cache)
{
int data = 0;
if (!cache->fiber_curves)
{
data |= HAIR_EXPORT_FIBER_CURVES;
}
if (!cache->fiber_verts || !cache->fiber_normals || !cache->fiber_tangents)
{
data |= HAIR_EXPORT_FIBER_VERTICES;
}
if (!cache->follicles)
{
data |= HAIR_EXPORT_FOLLICLE_BINDING;
}
if (!cache->follicle_root_position)
{
data |= HAIR_EXPORT_FOLLICLE_ROOT_POSITIONS;
}
return data;
}
/* Include data dependencies of the given flags */
static int hair_export_cache_get_dependencies(int data)
{
/* Ordering here is important to account for recursive dependencies */
if (data & HAIR_EXPORT_FIBER_CURVES)
data |= HAIR_EXPORT_FIBER_VERTICES | HAIR_EXPORT_FOLLICLE_BINDING;
if (data & HAIR_EXPORT_FOLLICLE_BINDING)
data |= HAIR_EXPORT_FOLLICLE_ROOT_POSITIONS;
return data;
}
/* Update an existing export cache to ensure it contains the requested data.
* Returns flags for data that has been updated.
*/
int BKE_hair_export_cache_update(HairExportCache *cache, const HairSystem *hsys,
int subdiv, Mesh *scalp, int requested_data)
{
/* Include dependencies */
int data = hair_export_cache_get_dependencies(requested_data);
int uncached = hair_export_cache_get_required_updates(cache);
/* Invalid data should already include all dependencies */
BLI_assert(uncached == hair_export_cache_get_dependencies(uncached));
/* Only update invalidated parts */
data &= uncached;
if (data & HAIR_EXPORT_FIBER_CURVES)
{
/* Cache subdivided curves */
const int totcurves = cache->totcurves = hsys->curve_data.totcurves;
cache->fiber_curves = MEM_reallocN_id(cache->fiber_curves, sizeof(HairFiberCurve) * totcurves, "hair export curves");
int totverts = 0;
for (int i = 0; i < totcurves; ++i) {
const HairFiberCurve *curve_orig = &hsys->curve_data.curves[i];
HairFiberCurve *curve = &cache->fiber_curves[i];
memcpy(curve, curve_orig, sizeof(HairFiberCurve));
curve->numverts = hair_get_strand_subdiv_length(curve_orig->numverts, subdiv);
curve->vertstart = totverts;
totverts += curve->numverts;
}
cache->totverts = totverts;
}
if (data & HAIR_EXPORT_FIBER_VERTICES)
{
const int totcurves = cache->totcurves;
const int totverts = cache->totverts;
cache->fiber_verts = MEM_reallocN_id(cache->fiber_verts, sizeof(HairFiberVertex) * totverts, "hair export verts");
cache->fiber_tangents = MEM_reallocN_id(cache->fiber_tangents, sizeof(float[3]) * totverts, "hair export tangents");
cache->fiber_normals = MEM_reallocN_id(cache->fiber_normals, sizeof(float[3]) * totverts, "hair export normals");
for (int i = 0; i < totcurves; ++i) {
const HairFiberCurve *curve_orig = &hsys->curve_data.curves[i];
const HairFiberVertex *verts_orig = &hsys->curve_data.verts[curve_orig->vertstart];
const HairFiberCurve *curve = &cache->fiber_curves[i];
HairFiberVertex *verts = &cache->fiber_verts[curve->vertstart];
float (*tangents)[3] = &cache->fiber_tangents[curve->vertstart];
float (*normals)[3] = &cache->fiber_normals[curve->vertstart];
hair_curve_subdivide(curve_orig, verts_orig, subdiv, verts);
hair_curve_calc_vectors(verts, curve->numverts, tangents, normals);
}
}
if (hsys->pattern)
{
if (data & HAIR_EXPORT_FOLLICLE_BINDING)
{
cache->follicles = hsys->pattern->follicles;
cache->totfollicles = hsys->pattern->num_follicles;
}
if (data & HAIR_EXPORT_FOLLICLE_ROOT_POSITIONS)
{
const int totfibercurves = cache->totfollicles;
cache->follicle_root_position = MEM_reallocN_id(cache->follicle_root_position, sizeof(float[3]) * totfibercurves, "fiber root position");
const HairFollicle *follicle = hsys->pattern->follicles;
for (int i = 0; i < totfibercurves; ++i, ++follicle) {
/* Cache fiber root position */
float nor[3], tang[3];
BKE_mesh_sample_eval(scalp, &follicle->mesh_sample, cache->follicle_root_position[i], nor, tang);
}
}
}
else
{
cache->follicles = NULL;
cache->totfollicles = 0;
if (cache->follicle_root_position)
{
MEM_freeN(cache->follicle_root_position);
cache->follicle_root_position = NULL;
}
}
return data;
}
/* Free the given export cache */
void BKE_hair_export_cache_free(HairExportCache *cache)
{
if (cache->fiber_curves)
{
MEM_freeN(cache->fiber_curves);
}
if (cache->fiber_verts)
{
MEM_freeN(cache->fiber_verts);
}
if (cache->fiber_tangents)
{
MEM_freeN(cache->fiber_tangents);
}
if (cache->fiber_normals)
{
MEM_freeN(cache->fiber_normals);
}
if (cache->follicle_root_position)
{
MEM_freeN(cache->follicle_root_position);
}
MEM_freeN(cache);
}
/* Invalidate all data in a hair export cache */
void BKE_hair_export_cache_clear(HairExportCache *cache)
{
/* Invalidate everything */
BKE_hair_export_cache_invalidate(cache, HAIR_EXPORT_ALL);
}
/* Invalidate part of the data in a hair export cache.
*
* Note some parts may get invalidated automatically based on internal dependencies.
*/
void BKE_hair_export_cache_invalidate(HairExportCache *cache, int invalidate)
{
/* Include dependencies */
int data = hair_export_cache_get_dependencies(invalidate);
if (data & HAIR_EXPORT_FIBER_CURVES)
{
if (cache->fiber_curves)
{
MEM_freeN(cache->fiber_curves);
cache->fiber_curves = 0;
}
}
if (data & HAIR_EXPORT_FIBER_VERTICES)
{
if (cache->fiber_verts)
{
MEM_freeN(cache->fiber_verts);
cache->fiber_verts = NULL;
}
if (cache->fiber_tangents)
{
MEM_freeN(cache->fiber_tangents);
cache->fiber_tangents = NULL;
}
if (cache->fiber_normals)
{
MEM_freeN(cache->fiber_normals);
cache->fiber_tangents = NULL;
}
}
if (data & HAIR_EXPORT_FOLLICLE_BINDING)
{
cache->follicles = NULL;
}
if (data & HAIR_EXPORT_FOLLICLE_ROOT_POSITIONS)
{
if (cache->follicle_root_position)
{
MEM_freeN(cache->follicle_root_position);
cache->follicle_root_position = NULL;
}
}
}

View File

@@ -0,0 +1,314 @@
/*
* ***** 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_mesh_sample.h"
#include "BKE_hair.h"
/* === Draw Settings === */
HairDrawSettings* BKE_hair_draw_settings_new(void)
{
HairDrawSettings *draw_settings = MEM_callocN(sizeof(HairDrawSettings), "hair draw settings");
draw_settings->follicle_mode = HAIR_DRAW_FOLLICLE_POINTS;
draw_settings->fiber_mode = HAIR_DRAW_FIBER_CURVES;
draw_settings->shape_flag = HAIR_DRAW_CLOSE_TIP;
draw_settings->shape = 0.0f;
draw_settings->root_radius = 1.0f;
draw_settings->tip_radius = 0.0f;
draw_settings->radius_scale = 0.01f;
return draw_settings;
}
HairDrawSettings* BKE_hair_draw_settings_copy(HairDrawSettings *draw_settings)
{
HairDrawSettings *ndraw_settings = MEM_dupallocN(draw_settings);
return ndraw_settings;
}
void BKE_hair_draw_settings_free(HairDrawSettings *draw_settings)
{
MEM_freeN(draw_settings);
}
/* === Draw Cache === */
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];
float len;
} HairStrandVertexTextureBuffer;
BLI_STATIC_ASSERT_ALIGN(HairStrandVertexTextureBuffer, 8)
typedef struct HairStrandMapTextureBuffer {
unsigned int vertex_start;
unsigned int vertex_count;
/* Shape attributes */
float taper_length; /* Distance at which final thickness is reached */
float taper_thickness; /* Relative thickness of the strand */
} HairStrandMapTextureBuffer;
BLI_STATIC_ASSERT_ALIGN(HairStrandMapTextureBuffer, 8)
static void hair_get_strand_buffer(
const HairExportCache *cache,
HairStrandMapTextureBuffer *strand_map_buffer,
HairStrandVertexTextureBuffer *strand_vertex_buffer)
{
for (int i = 0; i < cache->totcurves; ++i) {
const HairFiberCurve *curve = &cache->fiber_curves[i];
const HairFiberVertex *verts = &cache->fiber_verts[curve->vertstart];
const float (*tangents)[3] = &cache->fiber_tangents[curve->vertstart];
const float (*normals)[3] = &cache->fiber_normals[curve->vertstart];
HairStrandMapTextureBuffer *smap = &strand_map_buffer[i];
HairStrandVertexTextureBuffer *svert = &strand_vertex_buffer[curve->vertstart];
smap->vertex_start = curve->vertstart;
smap->vertex_count = curve->numverts;
smap->taper_length = curve->taper_length;
smap->taper_thickness = curve->taper_thickness;
float len = 0.0f;
for (int j = 0; j < curve->numverts; ++j)
{
copy_v3_v3(svert[j].co, verts[j].co);
copy_v3_v3(svert[j].tang, tangents[j]);
copy_v3_v3(svert[j].nor, normals[j]);
if (j > 0)
{
len += len_v3v3(verts[j-1].co, verts[j].co);
}
svert[j].len = len;
}
}
}
static void hair_get_fiber_buffer(const HairExportCache *cache,
HairFiberTextureBuffer *fiber_buf)
{
const int totfibers = cache->totfollicles;
const HairFollicle *follicle = cache->follicles;
HairFiberTextureBuffer *fb = fiber_buf;
for (int i = 0; i < totfibers; ++i, ++fb, ++follicle) {
copy_v3_v3(fb->root_position, cache->follicle_root_position[i]);
fb->parent_index[0] = follicle->curve;
fb->parent_index[1] = HAIR_CURVE_INDEX_NONE;
fb->parent_index[2] = HAIR_CURVE_INDEX_NONE;
fb->parent_index[3] = HAIR_CURVE_INDEX_NONE;
fb->parent_weight[0] = 1.0f;
fb->parent_weight[1] = 0.0f;
fb->parent_weight[2] = 0.0f;
fb->parent_weight[3] = 0.0f;
}
}
void BKE_hair_get_texture_buffer_size(
const HairExportCache *cache,
int *r_size,
int *r_strand_map_start,
int *r_strand_vertex_start,
int *r_fiber_start)
{
*r_strand_map_start = 0;
*r_strand_vertex_start = *r_strand_map_start + cache->totcurves * sizeof(HairStrandMapTextureBuffer);
*r_fiber_start = *r_strand_vertex_start + cache->totverts * sizeof(HairStrandVertexTextureBuffer);
*r_size = *r_fiber_start + cache->totfollicles * sizeof(HairFiberTextureBuffer);
}
void BKE_hair_get_texture_buffer(
const HairExportCache *cache,
void *buffer)
{
int size, strand_map_start, strand_vertex_start, fiber_start;
BKE_hair_get_texture_buffer_size(cache, &size, &strand_map_start, &strand_vertex_start, &fiber_start);
HairStrandMapTextureBuffer *strand_map = (HairStrandMapTextureBuffer*)((char*)buffer + strand_map_start);
HairStrandVertexTextureBuffer *strand_verts = (HairStrandVertexTextureBuffer*)((char*)buffer + strand_vertex_start);
HairFiberTextureBuffer *fibers = (HairFiberTextureBuffer*)((char*)buffer + fiber_start);
hair_get_strand_buffer(
cache,
strand_map,
strand_verts);
hair_get_fiber_buffer(
cache,
fibers);
}
void (*BKE_hair_batch_cache_dirty_cb)(HairSystem* hsys, int mode) = NULL;
void (*BKE_hair_batch_cache_free_cb)(HairSystem* hsys) = NULL;
void BKE_hair_batch_cache_dirty(HairSystem* hsys, int mode)
{
if (hsys->draw_batch_cache) {
BKE_hair_batch_cache_dirty_cb(hsys, mode);
}
}
void BKE_hair_batch_cache_free(HairSystem* hsys)
{
if (hsys->draw_batch_cache || hsys->draw_texture_cache) {
BKE_hair_batch_cache_free_cb(hsys);
}
}
/* === Fiber Curve Interpolation === */
/* NOTE: Keep this code in sync with the GLSL version!
* see common_hair_fibers_lib.glsl
*/
/* Subdivide a curve */
static int hair_curve_subdivide(
const HairFiberCurve* curve,
const HairFiberVertex* verts,
int subdiv,
int vertco_stride,
float *r_vertco)
{
{
/* Move vertex positions from the dense array to their initial configuration for subdivision.
* Also add offset to ensure the curve starts on the scalp surface.
*/
const int step = (1 << subdiv) * vertco_stride;
BLI_assert(curve->numverts > 0);
float *dst = r_vertco;
for (int i = 0; i < curve->numverts; ++i) {
copy_v3_v3(dst, verts[i].co);
dst = POINTER_OFFSET(dst, step);
}
}
/* Subdivide */
for (int d = 0; d < subdiv; ++d) {
const int num_edges = (curve->numverts - 1) << d;
const int hstep = (1 << (subdiv - d - 1)) * vertco_stride;
const int step = (1 << (subdiv - d)) * vertco_stride;
/* Calculate edge points */
{
float *p = r_vertco;
for (int k = 0; k < num_edges; ++k) {
float *ps = POINTER_OFFSET(p, step);
float *ph = POINTER_OFFSET(p, hstep);
add_v3_v3v3(ph, p, ps);
mul_v3_fl(ph, 0.5f);
p = ps;
}
}
/* Move original points */
{
float *p = r_vertco;
for (int k = 1; k < num_edges; ++k) {
float *ps = POINTER_OFFSET(p, step);
float *ph = POINTER_OFFSET(p, hstep);
float *hp = POINTER_OFFSET(p, -hstep);
add_v3_v3v3(p, hp, ph);
mul_v3_fl(p, 0.5f);
p = ps;
}
}
}
const int num_verts = ((curve->numverts - 1) << subdiv) + 1;
return num_verts;
}
/* === Render API === */
/* Calculate required size for render buffers. */
void BKE_hair_render_get_buffer_size(
const HairExportCache* cache,
int subdiv,
int *r_totcurves,
int *r_totverts)
{
*r_totcurves = cache->totfollicles;
const int subdiv_factor = 1 << subdiv;
for (int i = 0; i < cache->totfollicles; ++i)
{
const int numverts = cache->fiber_curves[cache->follicles[i].curve].numverts;
*r_totverts = (numverts - 1) * subdiv_factor + 1;
}
}
/* Create render data in existing buffers.
* Buffers must be large enough according to BKE_hair_get_render_buffer_size.
*/
void BKE_hair_render_fill_buffers(
const HairExportCache* cache,
int subdiv,
int vertco_stride,
int *r_curvestart,
int *r_curvelen,
float *r_vertco)
{
int vertstart = 0;
float *vert = r_vertco;
for (int i = 0; i < cache->totfollicles; ++i)
{
const HairFiberCurve *curve = &cache->fiber_curves[cache->follicles[i].curve];
const HairFiberVertex *verts = &cache->fiber_verts[curve->vertstart];
const int numverts = curve->numverts;
r_curvestart[i] = vertstart;
r_curvelen[i] = numverts;
hair_curve_subdivide(curve, verts, subdiv, vertco_stride, vert);
vertstart += numverts;
vert = POINTER_OFFSET(vert, vertco_stride * numverts);
}
}

View File

@@ -47,8 +47,8 @@
#include "DNA_brush_types.h"
#include "DNA_cachefile_types.h"
#include "DNA_camera_types.h"
#include "DNA_group_types.h"
#include "DNA_gpencil_types.h"
#include "DNA_group_types.h"
#include "DNA_ipo_types.h"
#include "DNA_key_types.h"
#include "DNA_lamp_types.h"
@@ -1057,6 +1057,8 @@ void BKE_main_lib_objects_recalc_all(Main *bmain)
DEG_id_type_tag(bmain, ID_OB);
}
BLI_STATIC_ASSERT(MAX_LIBARRAY == INDEX_ID_NULL + 1, "MAX_LIBARRAY must be large enough for all ID types")
/**
* puts into array *lb pointers to all the ListBase structs in main,
* and returns the number of them as the function result. This is useful for

File diff suppressed because it is too large Load Diff

View File

@@ -1437,7 +1437,7 @@ int psys_particle_dm_face_lookup(
return DMCACHE_NOTFOUND;
}
static int psys_map_index_on_dm(Mesh *mesh, int from, int index, int index_dmcache, const float fw[4], float UNUSED(foffset), int *mapindex, float mapfw[4])
static int psys_map_index_on_mesh(Mesh *mesh, int from, int index, int index_dmcache, const float fw[4], float UNUSED(foffset), int *mapindex, float mapfw[4])
{
if (index < 0)
return 0;
@@ -1497,6 +1497,11 @@ static int psys_map_index_on_dm(Mesh *mesh, int from, int index, int index_dmcac
return 1;
}
int psys_get_index_on_mesh(ParticleSystem *psys, Mesh *mesh, ParticleData *pa, int *mapindex, float mapfw[4])
{
return psys_map_index_on_mesh(mesh, 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(Mesh *mesh_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],
@@ -1506,7 +1511,7 @@ void psys_particle_on_dm(Mesh *mesh_final, int from, int index, int index_dmcach
float (*orcodata)[3];
int mapindex;
if (!psys_map_index_on_dm(mesh_final, from, index, index_dmcache, fw, foffset, &mapindex, mapfw)) {
if (!psys_map_index_on_mesh(mesh_final, from, index, index_dmcache, fw, foffset, &mapindex, mapfw)) {
if (vec) { vec[0] = vec[1] = vec[2] = 0.0; }
if (nor) { nor[0] = nor[1] = 0.0; nor[2] = 1.0; }
if (orco) { orco[0] = orco[1] = orco[2] = 0.0; }
@@ -1571,7 +1576,7 @@ float psys_particle_value_from_verts(Mesh *mesh, short from, ParticleData *pa, f
float mapfw[4];
int mapindex;
if (!psys_map_index_on_dm(mesh, from, pa->num, pa->num_dmcache, pa->fuv, pa->foffset, &mapindex, mapfw))
if (!psys_map_index_on_mesh(mesh, from, pa->num, pa->num_dmcache, pa->fuv, pa->foffset, &mapindex, mapfw))
return 0.0f;
return psys_interpolate_value_from_verts(mesh, from, mapindex, mapfw, values);

View File

@@ -376,6 +376,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]);

View File

@@ -3272,6 +3272,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])
{

View File

@@ -73,6 +73,7 @@
#include "DNA_group_types.h"
#include "DNA_gpencil_types.h"
#include "DNA_gpencil_modifier_types.h"
#include "DNA_hair_types.h"
#include "DNA_shader_fx_types.h"
#include "DNA_ipo_types.h"
#include "DNA_key_types.h"
@@ -5095,6 +5096,25 @@ static void direct_link_pose(FileData *fd, bPose *pose)
}
}
static void direct_link_hair(FileData *fd, HairSystem* hsys)
{
if (!hsys) {
return;
}
hsys->pattern = newdataadr(fd, hsys->pattern);
if ( hsys->pattern )
{
hsys->pattern->follicles = newdataadr(fd, hsys->pattern->follicles);
}
hsys->curve_data.curves = newdataadr(fd, hsys->curve_data.curves);
hsys->curve_data.verts = newdataadr(fd, hsys->curve_data.verts);
hsys->draw_batch_cache = NULL;
hsys->draw_texture_cache = NULL;
}
static void direct_link_modifiers(FileData *fd, ListBase *lb)
{
ModifierData *md;
@@ -5418,6 +5438,16 @@ static void direct_link_modifiers(FileData *fd, ListBase *lb)
}
}
}
else if (md->type == eModifierType_Hair) {
HairModifierData *hmd = (HairModifierData *)md;
hmd->hair_system = newdataadr(fd, hmd->hair_system);
direct_link_hair(fd, hmd->hair_system);
hmd->draw_settings = newdataadr(fd, hmd->draw_settings);
BLI_listbase_clear(&hmd->fiber_curves); // runtime
}
}
}

View File

@@ -121,6 +121,7 @@
#include "DNA_gpencil_modifier_types.h"
#include "DNA_shader_fx_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"
@@ -1617,6 +1618,18 @@ static void write_fmaps(WriteData *wd, ListBase *fbase)
}
}
static void write_hair(WriteData *wd, HairSystem *hsys)
{
if ( hsys->pattern )
{
writestruct(wd, DATA, HairPattern, 1, hsys->pattern);
writestruct(wd, DATA, HairFollicle, hsys->pattern->num_follicles, hsys->pattern->follicles);
}
writestruct(wd, DATA, HairFiberCurve, hsys->curve_data.totcurves, hsys->curve_data.curves);
writestruct(wd, DATA, HairFiberVertex, hsys->curve_data.totverts, hsys->curve_data.verts);
}
static void write_modifiers(WriteData *wd, ListBase *modbase)
{
ModifierData *md;
@@ -1788,6 +1801,18 @@ static void write_modifiers(WriteData *wd, ListBase *modbase)
}
}
}
else if (md->type == eModifierType_Hair) {
HairModifierData *hmd = (HairModifierData *)md;
if (hmd->hair_system) {
writestruct(wd, DATA, HairSystem, 1, hmd->hair_system);
write_hair(wd, hmd->hair_system);
}
if (hmd->draw_settings)
{
writestruct(wd, DATA, HairDrawSettings, 1, hmd->draw_settings);
}
}
}
}

View File

@@ -58,6 +58,7 @@ 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_metaball.c

View File

@@ -31,6 +31,7 @@
#include "BLI_rand.h"
#include "BLI_string_utils.h"
#include "BKE_hair.h"
#include "BKE_particle.h"
#include "BKE_paint.h"
#include "BKE_pbvh.h"
@@ -39,6 +40,7 @@
#include "DNA_world_types.h"
#include "DNA_modifier_types.h"
#include "DNA_view3d_types.h"
#include "DNA_hair_types.h"
#include "GPU_material.h"
@@ -812,6 +814,7 @@ struct GPUMaterial *EEVEE_material_hair_get(
options |= eevee_material_shadow_option(shadow_method);
GPUMaterial *mat = DRW_shader_find_from_material(ma, engine, options, true);
if (mat) {
return mat;
}
@@ -862,14 +865,16 @@ static struct DRWShadingGroup *EEVEE_default_shading_group_create(
**/
static struct DRWShadingGroup *EEVEE_default_shading_group_get(
EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata,
Object *ob, ParticleSystem *psys, ModifierData *md,
Object *ob,
ParticleSystem *psys, ModifierData *md,
HairSystem *hsys, const HairDrawSettings *hdraw, struct Mesh *scalp,
bool is_hair, bool is_flat_normal, bool use_ssr, int shadow_method)
{
static int ssr_id;
ssr_id = (use_ssr) ? 1 : -1;
int options = VAR_MAT_MESH;
BLI_assert(!is_hair || (ob && psys && md));
BLI_assert(!is_hair || (ob && psys && md) || (ob && hsys && hdraw && scalp));
if (is_hair) options |= VAR_MAT_HAIR;
if (is_flat_normal) options |= VAR_MAT_FLAT;
@@ -894,10 +899,19 @@ static struct DRWShadingGroup *EEVEE_default_shading_group_get(
}
if (is_hair) {
DRWShadingGroup *shgrp = DRW_shgroup_hair_create(ob, psys, md,
vedata->psl->default_pass[options],
e_data.default_lit[options]);
DRWShadingGroup *shgrp = NULL;
if (psys && md) {
shgrp = DRW_shgroup_particle_hair_create(ob, psys, md,
vedata->psl->default_pass[options],
e_data.default_lit[options]);
}
else if (hsys && hdraw && scalp) {
shgrp = DRW_shgroup_hair_create(ob, hsys, scalp, hdraw,
vedata->psl->default_pass[options],
e_data.default_lit[options]);
}
add_standard_uniforms(shgrp, sldata, vedata, &ssr_id, NULL, true, true, false, false, false);
return shgrp;
}
else {
@@ -1266,7 +1280,7 @@ static void material_opaque(
if (*shgrp == NULL) {
bool use_ssr = ((effects->enabled_effects & EFFECT_SSR) != 0);
*shgrp = EEVEE_default_shading_group_get(sldata, vedata,
NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL,
false, use_flat_nor, use_ssr, linfo->shadow_method);
DRW_shgroup_uniform_vec3(*shgrp, "basecol", color_p, 1);
DRW_shgroup_uniform_float(*shgrp, "metallic", metal_p, 1);
@@ -1409,6 +1423,217 @@ static void material_transparent(
}
}
static void material_particle_hair(
EEVEE_Data *vedata,
EEVEE_ViewLayerData *sldata,
Object *ob,
ParticleSystem *psys,
ModifierData *md)
{
EEVEE_PassList *psl = ((EEVEE_Data *)vedata)->psl;
EEVEE_StorageList *stl = ((EEVEE_Data *)vedata)->stl;
const DRWContextState *draw_ctx = DRW_context_state_get();
Scene *scene = draw_ctx->scene;
bool use_ssr = ((stl->effects->enabled_effects & EFFECT_SSR) != 0);
if (!psys_check_enabled(ob, psys, false)) {
return;
}
if (!DRW_check_psys_visible_within_active_context(ob, psys)) {
return;
}
ParticleSettings *part = psys->part;
const int draw_as = (part->draw_as == PART_DRAW_REND) ? part->ren_as : part->draw_as;
if (draw_as != PART_DRAW_PATH) {
return;
}
DRWShadingGroup *shgrp = NULL;
Material *ma = give_current_material(ob, part->omat);
if (ma == NULL) {
ma = &defmaterial;
}
float *color_p = &ma->r;
float *metal_p = &ma->metallic;
float *spec_p = &ma->spec;
float *rough_p = &ma->roughness;
shgrp = DRW_shgroup_particle_hair_create(
ob, psys, md,
psl->depth_pass,
e_data.default_hair_prepass_sh);
shgrp = DRW_shgroup_particle_hair_create(
ob, psys, md,
psl->depth_pass_clip,
e_data.default_hair_prepass_clip_sh);
DRW_shgroup_uniform_block(shgrp, "clip_block", sldata->clip_ubo);
shgrp = NULL;
if (ma->use_nodes && ma->nodetree) {
static int ssr_id;
ssr_id = (use_ssr) ? 1 : -1;
static float half = 0.5f;
static float error_col[3] = {1.0f, 0.0f, 1.0f};
static float compile_col[3] = {0.5f, 0.5f, 0.5f};
struct GPUMaterial *gpumat = EEVEE_material_hair_get(scene, ma, sldata->lamps->shadow_method);
switch (GPU_material_status(gpumat)) {
case GPU_MAT_SUCCESS:
{
bool use_diffuse = GPU_material_flag_get(gpumat, GPU_MATFLAG_DIFFUSE);
bool use_glossy = GPU_material_flag_get(gpumat, GPU_MATFLAG_GLOSSY);
bool use_refract = GPU_material_flag_get(gpumat, GPU_MATFLAG_REFRACT);
shgrp = DRW_shgroup_material_particle_hair_create(
ob, psys, md,
psl->material_pass,
gpumat);
add_standard_uniforms(shgrp, sldata, vedata, &ssr_id, NULL,
use_diffuse, use_glossy, use_refract, false, false);
break;
}
case GPU_MAT_QUEUED:
{
color_p = compile_col;
metal_p = spec_p = rough_p = &half;
break;
}
case GPU_MAT_FAILED:
default:
color_p = error_col;
metal_p = spec_p = rough_p = &half;
break;
}
}
/* Fallback to default shader */
if (shgrp == NULL) {
shgrp = EEVEE_default_shading_group_get(sldata, vedata,
ob, psys, md, NULL, NULL, NULL,
true, false, use_ssr,
sldata->lamps->shadow_method);
DRW_shgroup_uniform_vec3(shgrp, "basecol", color_p, 1);
DRW_shgroup_uniform_float(shgrp, "metallic", metal_p, 1);
DRW_shgroup_uniform_float(shgrp, "specular", spec_p, 1);
DRW_shgroup_uniform_float(shgrp, "roughness", rough_p, 1);
}
/* Shadows */
DRW_shgroup_particle_hair_create(
ob, psys, md,
psl->shadow_pass,
e_data.default_hair_prepass_sh);
}
static void material_hair(
EEVEE_Data *vedata,
EEVEE_ViewLayerData *sldata,
Object *ob,
HairSystem *hsys,
const HairDrawSettings *draw_set,
Material *ma,
struct Mesh *scalp)
{
EEVEE_PassList *psl = ((EEVEE_Data *)vedata)->psl;
EEVEE_StorageList *stl = ((EEVEE_Data *)vedata)->stl;
const bool use_ssr = ((stl->effects->enabled_effects & EFFECT_SSR) != 0);
const DRWContextState *draw_ctx = DRW_context_state_get();
Scene *scene = draw_ctx->scene;
float mat[4][4];
copy_m4_m4(mat, ob->obmat);
if (draw_set->fiber_mode != HAIR_DRAW_FIBER_CURVES)
{
return;
}
if (ma == NULL) {
ma = &defmaterial;
}
{
/*DRWShadingGroup *shgrp =*/ DRW_shgroup_hair_create(
ob, hsys, scalp, draw_set,
psl->depth_pass,
e_data.default_hair_prepass_sh);
}
{
DRWShadingGroup *shgrp = DRW_shgroup_hair_create(
ob, hsys, scalp, draw_set,
psl->depth_pass_clip,
e_data.default_hair_prepass_clip_sh);
DRW_shgroup_uniform_block(shgrp, "clip_block", sldata->clip_ubo);
}
{
float *color_p = &ma->r;
float *metal_p = &ma->metallic;
float *spec_p = &ma->spec;
float *rough_p = &ma->roughness;
DRWShadingGroup *shgrp = NULL;
if (ma->use_nodes && ma->nodetree) {
static int ssr_id;
ssr_id = (use_ssr) ? 1 : -1;
static float half = 0.5f;
static float error_col[3] = {1.0f, 0.0f, 1.0f};
static float compile_col[3] = {0.5f, 0.5f, 0.5f};
struct GPUMaterial *gpumat = EEVEE_material_hair_get(scene, ma, sldata->lamps->shadow_method);
switch (GPU_material_status(gpumat)) {
case GPU_MAT_SUCCESS:
{
bool use_diffuse = GPU_material_flag_get(gpumat, GPU_MATFLAG_DIFFUSE);
bool use_glossy = GPU_material_flag_get(gpumat, GPU_MATFLAG_GLOSSY);
bool use_refract = GPU_material_flag_get(gpumat, GPU_MATFLAG_REFRACT);
shgrp = DRW_shgroup_material_hair_create(
ob, hsys, scalp, draw_set,
psl->material_pass,
gpumat);
add_standard_uniforms(shgrp, sldata, vedata, &ssr_id, NULL,
use_diffuse, use_glossy, use_refract, false, false);
break;
}
case GPU_MAT_QUEUED:
{
color_p = compile_col;
metal_p = spec_p = rough_p = &half;
break;
}
case GPU_MAT_FAILED:
default:
color_p = error_col;
metal_p = spec_p = rough_p = &half;
break;
}
}
/* Fallback to default shader */
if (shgrp == NULL) {
shgrp = EEVEE_default_shading_group_get(sldata, vedata,
ob, NULL, NULL, hsys, draw_set, scalp,
true, false, use_ssr,
sldata->lamps->shadow_method);
DRW_shgroup_uniform_vec3(shgrp, "basecol", color_p, 1);
DRW_shgroup_uniform_float(shgrp, "metallic", metal_p, 1);
DRW_shgroup_uniform_float(shgrp, "specular", spec_p, 1);
DRW_shgroup_uniform_float(shgrp, "roughness", rough_p, 1);
}
}
/* Shadows */
DRW_shgroup_hair_create(
ob, hsys, scalp, draw_set,
psl->shadow_pass,
e_data.default_hair_prepass_sh);
}
void EEVEE_materials_cache_populate(EEVEE_Data *vedata, EEVEE_ViewLayerData *sldata, Object *ob, bool *cast_shadow)
{
EEVEE_PassList *psl = vedata->psl;
@@ -1600,112 +1825,24 @@ void EEVEE_materials_cache_populate(EEVEE_Data *vedata, EEVEE_ViewLayerData *sld
void EEVEE_hair_cache_populate(EEVEE_Data *vedata, EEVEE_ViewLayerData *sldata, Object *ob, bool *cast_shadow)
{
EEVEE_PassList *psl = vedata->psl;
EEVEE_StorageList *stl = vedata->stl;
const DRWContextState *draw_ctx = DRW_context_state_get();
Scene *scene = draw_ctx->scene;
bool use_ssr = ((stl->effects->enabled_effects & EFFECT_SSR) != 0);
if (ob->type == OB_MESH) {
if (ob != draw_ctx->object_edit) {
for (ModifierData *md = ob->modifiers.first; md; md = md->next) {
if (md->type != eModifierType_ParticleSystem) {
continue;
if (md->type == eModifierType_ParticleSystem) {
ParticleSystem *psys = ((ParticleSystemModifierData *)md)->psys;
material_particle_hair(vedata, sldata, ob, psys, md);
*cast_shadow = true;
}
ParticleSystem *psys = ((ParticleSystemModifierData *)md)->psys;
if (!psys_check_enabled(ob, psys, false)) {
continue;
else if (md->type == eModifierType_Hair) {
HairModifierData *hmd = (HairModifierData *)md;
const int material_index = 1; /* TODO */
Material *material = give_current_material(ob, material_index);
material_hair(vedata, sldata, ob, hmd->hair_system, hmd->draw_settings, material, ob->data);
}
if (!DRW_check_psys_visible_within_active_context(ob, psys)) {
continue;
}
ParticleSettings *part = psys->part;
const int draw_as = (part->draw_as == PART_DRAW_REND) ? part->ren_as : part->draw_as;
if (draw_as != PART_DRAW_PATH) {
continue;
}
DRWShadingGroup *shgrp = NULL;
Material *ma = give_current_material(ob, part->omat);
if (ma == NULL) {
ma = &defmaterial;
}
float *color_p = &ma->r;
float *metal_p = &ma->metallic;
float *spec_p = &ma->spec;
float *rough_p = &ma->roughness;
shgrp = DRW_shgroup_hair_create(
ob, psys, md,
psl->depth_pass,
e_data.default_hair_prepass_sh);
shgrp = DRW_shgroup_hair_create(
ob, psys, md,
psl->depth_pass_clip,
e_data.default_hair_prepass_clip_sh);
DRW_shgroup_uniform_block(shgrp, "clip_block", sldata->clip_ubo);
shgrp = NULL;
if (ma->use_nodes && ma->nodetree) {
static int ssr_id;
ssr_id = (use_ssr) ? 1 : -1;
static float half = 0.5f;
static float error_col[3] = {1.0f, 0.0f, 1.0f};
static float compile_col[3] = {0.5f, 0.5f, 0.5f};
struct GPUMaterial *gpumat = EEVEE_material_hair_get(scene, ma, sldata->lamps->shadow_method);
switch (GPU_material_status(gpumat)) {
case GPU_MAT_SUCCESS:
{
bool use_diffuse = GPU_material_flag_get(gpumat, GPU_MATFLAG_DIFFUSE);
bool use_glossy = GPU_material_flag_get(gpumat, GPU_MATFLAG_GLOSSY);
bool use_refract = GPU_material_flag_get(gpumat, GPU_MATFLAG_REFRACT);
shgrp = DRW_shgroup_material_hair_create(
ob, psys, md,
psl->material_pass,
gpumat);
add_standard_uniforms(shgrp, sldata, vedata, &ssr_id, NULL,
use_diffuse, use_glossy, use_refract, false, false);
break;
}
case GPU_MAT_QUEUED:
{
color_p = compile_col;
metal_p = spec_p = rough_p = &half;
break;
}
case GPU_MAT_FAILED:
default:
color_p = error_col;
metal_p = spec_p = rough_p = &half;
break;
}
}
/* Fallback to default shader */
if (shgrp == NULL) {
shgrp = EEVEE_default_shading_group_get(sldata, vedata,
ob, psys, md,
true, false, use_ssr,
sldata->lamps->shadow_method);
DRW_shgroup_uniform_vec3(shgrp, "basecol", color_p, 1);
DRW_shgroup_uniform_float(shgrp, "metallic", metal_p, 1);
DRW_shgroup_uniform_float(shgrp, "specular", spec_p, 1);
DRW_shgroup_uniform_float(shgrp, "roughness", rough_p, 1);
}
/* Shadows */
DRW_shgroup_hair_create(
ob, psys, md,
psl->shadow_pass,
e_data.default_hair_prepass_sh);
*cast_shadow = true;
}
}
}

View File

@@ -23,6 +23,8 @@ out vec3 normal_viewport;
out vec2 uv_interp;
#endif
out int DEBUG;
/* From http://libnoise.sourceforge.net/noisegen/index.html */
float integer_noise(int n)
{
@@ -34,6 +36,7 @@ float integer_noise(int n)
void main()
{
#ifdef HAIR_SHADER
DEBUG = hair_get_strand_id();
# ifdef V3D_SHADING_TEXTURE_COLOR
vec2 uv = hair_get_customdata_vec2(u);
# endif

View File

@@ -633,7 +633,7 @@ static void workbench_cache_populate_particles(WORKBENCH_Data *vedata, Object *o
struct GPUShader *shader = (color_type != V3D_SHADING_TEXTURE_COLOR) ?
wpd->prepass_solid_hair_sh :
wpd->prepass_texture_hair_sh;
DRWShadingGroup *shgrp = DRW_shgroup_hair_create(
DRWShadingGroup *shgrp = DRW_shgroup_particle_hair_create(
ob, psys, md,
psl->prepass_hair_pass,
shader);

View File

@@ -425,7 +425,7 @@ static void workbench_forward_cache_populate_particles(WORKBENCH_Data *vedata, O
struct GPUShader *shader = (color_type != V3D_SHADING_TEXTURE_COLOR)
? wpd->transparent_accum_hair_sh
: wpd->transparent_accum_texture_hair_sh;
DRWShadingGroup *shgrp = DRW_shgroup_hair_create(
DRWShadingGroup *shgrp = DRW_shgroup_particle_hair_create(
ob, psys, md,
psl->transparent_accum_pass,
shader);
@@ -444,7 +444,7 @@ static void workbench_forward_cache_populate_particles(WORKBENCH_Data *vedata, O
if (SPECULAR_HIGHLIGHT_ENABLED(wpd) || MATCAP_ENABLED(wpd)) {
DRW_shgroup_uniform_vec2(shgrp, "invertedViewportSize", DRW_viewport_invert_size_get(), 1);
}
shgrp = DRW_shgroup_hair_create(ob, psys, md,
shgrp = DRW_shgroup_particle_hair_create(ob, psys, md,
vedata->psl->object_outline_pass,
e_data.object_outline_hair_sh);
DRW_shgroup_uniform_int(shgrp, "object_id", &material->object_id, 1);

View File

@@ -3366,6 +3366,20 @@ GPUBatch *DRW_cache_particles_get_prim(int type)
return NULL;
}
/* -------------------------------------------------------------------- */
/** \name Hair */
GPUBatch *DRW_cache_hair_get_fibers(struct HairSystem *hsys, const struct HairExportCache *hair_export)
{
return DRW_hair_batch_cache_get_fibers(hsys, hair_export);
}
GPUBatch *DRW_cache_hair_get_follicle_points(struct HairSystem *hsys, const struct HairExportCache *hair_export)
{
return DRW_hair_batch_cache_get_follicle_points(hsys, hair_export);
}
/* 3D cursor */
GPUBatch *DRW_cache_cursor_get(bool crosshair_lines)
{

View File

@@ -31,6 +31,8 @@ struct GPUMaterial;
struct ModifierData;
struct Object;
struct PTCacheEdit;
struct HairSystem;
struct HairExportCache;
void DRW_shape_cache_free(void);
void DRW_shape_cache_reset(void);
@@ -193,6 +195,10 @@ struct GPUBatch *DRW_cache_particles_get_edit_tip_points(
struct Object *object, struct ParticleSystem *psys, struct PTCacheEdit *edit);
struct GPUBatch *DRW_cache_particles_get_prim(int type);
/* Hair */
struct GPUBatch *DRW_cache_hair_get_fibers(struct HairSystem *hsys, const struct HairExportCache *hair_export);
struct GPUBatch *DRW_cache_hair_get_follicle_points(struct HairSystem *hsys, const struct HairExportCache *hair_export);
/* Metaball */
struct GPUBatch *DRW_cache_mball_surface_get(struct Object *ob);
struct GPUBatch **DRW_cache_mball_surface_shaded_get(struct Object *ob, struct GPUMaterial **gpumat_array, uint gpumat_array_len);

View File

@@ -36,6 +36,8 @@ struct ListBase;
struct ModifierData;
struct ParticleSystem;
struct PTCacheEdit;
struct HairSystem;
struct HairExportCache;
struct Curve;
struct Lattice;
@@ -62,6 +64,9 @@ void DRW_particle_batch_cache_free(struct ParticleSystem *psys);
void DRW_gpencil_batch_cache_dirty(struct bGPdata *gpd);
void DRW_gpencil_batch_cache_free(struct bGPdata *gpd);
void DRW_hair_batch_cache_dirty(struct HairSystem *hsys, int mode);
void DRW_hair_batch_cache_free(struct HairSystem *hsys);
/* Curve */
struct GPUBatch *DRW_curve_batch_cache_get_wire_edge(struct Curve *cu, struct CurveCache *ob_curve_cache);
struct GPUBatch *DRW_curve_batch_cache_get_normal_edge(
@@ -146,4 +151,8 @@ struct GPUBatch *DRW_particles_batch_cache_get_edit_inner_points(
struct GPUBatch *DRW_particles_batch_cache_get_edit_tip_points(
struct Object *object, struct ParticleSystem *psys, struct PTCacheEdit *edit);
/* Hair */
struct GPUBatch *DRW_hair_batch_cache_get_fibers(struct HairSystem *hsys, const struct HairExportCache *hair_export);
struct GPUBatch *DRW_hair_batch_cache_get_follicle_points(struct HairSystem *hsys, const struct HairExportCache *hair_export);
#endif /* __DRAW_CACHE_IMPL_H__ */

View File

@@ -0,0 +1,556 @@
/*
* ***** BEGIN GPL LICENSE BLOCK *****
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* The Original Code is Copyright (C) 2017 by Blender Foundation.
* All rights reserved.
*
* Contributor(s): Blender Foundation, Mike Erwin, Dalai Felinto
*
* ***** END GPL LICENSE BLOCK *****
*/
/** \file draw_cache_impl_strands.c
* \ingroup draw
*
* \brief Strands API for render engines
*/
#include "MEM_guardedalloc.h"
#include "BLI_utildefines.h"
#include "BLI_math_vector.h"
#include "BLI_ghash.h"
#include "DNA_hair_types.h"
#include "DNA_scene_types.h"
#include "BKE_hair.h"
#include "BKE_mesh_sample.h"
#include "DEG_depsgraph.h"
#include "GPU_batch.h"
#include "GPU_extensions.h"
#include "GPU_texture.h"
#include "draw_common.h"
#include "draw_cache_impl.h" /* own include */
#include "draw_hair_private.h"
#include "DRW_render.h"
/* ---------------------------------------------------------------------- */
/* Hair GPUBatch Cache */
/* GPUBatch cache management. */
typedef struct HairBatchCache {
ParticleHairCache hair;
bool is_dirty;
} HairBatchCache;
static void hair_batch_cache_clear(HairSystem *hsys);
static bool hair_batch_cache_valid(HairSystem *hsys)
{
HairBatchCache *cache = hsys->draw_batch_cache;
if (cache == NULL) {
return false;
}
if (cache->is_dirty) {
return false;
}
return true;
}
static void hair_batch_cache_init(HairSystem *hsys)
{
HairBatchCache *cache = hsys->draw_batch_cache;
if (!cache) {
cache = hsys->draw_batch_cache = MEM_callocN(sizeof(*cache), __func__);
}
else {
memset(cache, 0, sizeof(*cache));
}
cache->is_dirty = false;
}
static HairBatchCache *hair_batch_cache_get(HairSystem *hsys)
{
// Hair follicle binding needs to be updated after changes
BLI_assert(!(hsys->flag & HAIR_SYSTEM_UPDATE_FOLLICLE_BINDING));
if (!hair_batch_cache_valid(hsys)) {
hair_batch_cache_clear(hsys);
hair_batch_cache_init(hsys);
}
return hsys->draw_batch_cache;
}
void DRW_hair_batch_cache_dirty(HairSystem *hsys, int mode)
{
HairBatchCache *cache = hsys->draw_batch_cache;
if (cache == NULL) {
return;
}
switch (mode) {
case BKE_HAIR_BATCH_DIRTY_ALL:
cache->is_dirty = true;
break;
default:
BLI_assert(0);
}
}
static void hair_batch_cache_clear(HairSystem *hsys)
{
HairBatchCache *cache = hsys->draw_batch_cache;
if (cache) {
particle_batch_cache_clear_hair(&cache->hair);
}
}
void DRW_hair_batch_cache_free(HairSystem *hsys)
{
hair_batch_cache_clear(hsys);
MEM_SAFE_FREE(hsys->draw_batch_cache);
}
static void hair_batch_cache_ensure_count(
const HairExportCache *hair_export,
ParticleHairCache *cache)
{
cache->strands_len = hair_export->totfollicles;
cache->elems_len = hair_export->totverts - hair_export->totcurves;
cache->point_len = hair_export->totverts;
}
static void hair_batch_cache_fill_segments_proc_pos(
const HairExportCache *hair_export,
GPUVertBufRaw *attr_step)
{
for (int i = 0; i < hair_export->totcurves; i++) {
const HairFiberCurve *curve = &hair_export->fiber_curves[i];
const HairFiberVertex *verts = &hair_export->fiber_verts[curve->vertstart];
if (curve->numverts < 2) {
continue;
}
float total_len = 0.0f;
const float *co_prev = NULL;
float *seg_data_first;
for (int j = 0; j < curve->numverts; j++) {
float *seg_data = (float *)GPU_vertbuf_raw_step(attr_step);
copy_v3_v3(seg_data, verts[j].co);
if (co_prev) {
total_len += len_v3v3(co_prev, verts[j].co);
}
else {
seg_data_first = seg_data;
}
seg_data[3] = total_len;
co_prev = verts[j].co;
}
if (total_len > 0.0f) {
/* Divide by total length to have a [0-1] number. */
for (int j = 0; j < curve->numverts; j++, seg_data_first += 4) {
seg_data_first[3] /= total_len;
}
}
}
}
static void hair_batch_cache_ensure_procedural_pos(
const HairExportCache *hair_export,
ParticleHairCache *cache)
{
if (cache->proc_point_buf != NULL) {
return;
}
/* initialize vertex format */
GPUVertFormat format = {0};
uint pos_id = GPU_vertformat_attr_add(&format, "posTime", GPU_COMP_F32, 4, GPU_FETCH_FLOAT);
cache->proc_point_buf = GPU_vertbuf_create_with_format(&format);
GPU_vertbuf_data_alloc(cache->proc_point_buf, cache->point_len);
GPUVertBufRaw pos_step;
GPU_vertbuf_attr_get_raw_data(cache->proc_point_buf, pos_id, &pos_step);
hair_batch_cache_fill_segments_proc_pos(hair_export, &pos_step);
/* Create vbo immediatly to bind to texture buffer. */
GPU_vertbuf_use(cache->proc_point_buf);
cache->point_tex = GPU_texture_create_from_vertbuf(cache->proc_point_buf);
}
static void hair_pack_mcol(MCol *mcol, unsigned short r_scol[3])
{
/* Convert to linear ushort and swizzle */
r_scol[0] = unit_float_to_ushort_clamp(BLI_color_from_srgb_table[mcol->b]);
r_scol[1] = unit_float_to_ushort_clamp(BLI_color_from_srgb_table[mcol->g]);
r_scol[2] = unit_float_to_ushort_clamp(BLI_color_from_srgb_table[mcol->r]);
}
static int hair_batch_cache_fill_strands_data(
const HairExportCache *hair_export,
GPUVertBufRaw *data_step,
GPUVertBufRaw *uv_step, int num_uv_layers,
GPUVertBufRaw *col_step, int num_col_layers)
{
int curr_point = 0;
for (int i = 0; i < hair_export->totcurves; i++) {
const HairFiberCurve *curve = &hair_export->fiber_curves[i];
if (curve->numverts < 2) {
continue;
}
uint *seg_data = (uint *)GPU_vertbuf_raw_step(data_step);
const uint numseg = curve->numverts - 1;
*seg_data = (curr_point & 0xFFFFFF) | (numseg << 24);
curr_point += curve->numverts;
float (*uv)[2] = NULL;
MCol *mcol = NULL;
#if 0
particle_calculate_uvs(
psys, psmd,
is_simple, num_uv_layers,
is_child ? psys->child[i].parent : i,
is_child ? i : -1,
mtfaces,
*r_parent_uvs, &uv);
particle_calculate_mcol(
psys, psmd,
is_simple, num_col_layers,
is_child ? psys->child[i].parent : i,
is_child ? i : -1,
mcols,
*r_parent_mcol, &mcol);
#else
/* XXX dummy uvs and mcols, TODO */
uv = MEM_mallocN(sizeof(*uv) * num_uv_layers, __func__);
mcol = MEM_mallocN(sizeof(*mcol) * num_col_layers, __func__);
for (int k = 0; k < num_uv_layers; k++) {
zero_v3(uv[k]);
}
for (int k = 0; k < num_col_layers; k++) {
mcol[k].a = 255;
mcol[k].r = 255;
mcol[k].g = 0;
mcol[k].b = 255;
}
#endif
for (int k = 0; k < num_uv_layers; k++) {
float *t_uv = (float *)GPU_vertbuf_raw_step(uv_step + k);
copy_v2_v2(t_uv, uv[k]);
}
for (int k = 0; k < num_col_layers; k++) {
unsigned short *scol = (unsigned short *)GPU_vertbuf_raw_step(col_step + k);
hair_pack_mcol(&mcol[k], scol);
}
if (uv) {
MEM_freeN(uv);
}
if (mcol) {
MEM_freeN(mcol);
}
}
return curr_point;
}
static void hair_batch_cache_ensure_procedural_strand_data(
const HairExportCache *hair_export,
ParticleHairCache *cache)
{
int active_uv = 0;
int active_col = 0;
#if 0 // TODO
ParticleSystemModifierData *psmd = (ParticleSystemModifierData *)md;
if (psmd != NULL && psmd->mesh_final != NULL) {
if (CustomData_has_layer(&psmd->mesh_final->ldata, CD_MLOOPUV)) {
cache->num_uv_layers = CustomData_number_of_layers(&psmd->mesh_final->ldata, CD_MLOOPUV);
active_uv = CustomData_get_active_layer(&psmd->mesh_final->ldata, CD_MLOOPUV);
}
if (CustomData_has_layer(&psmd->mesh_final->ldata, CD_MLOOPCOL)) {
cache->num_col_layers = CustomData_number_of_layers(&psmd->mesh_final->ldata, CD_MLOOPCOL);
active_col = CustomData_get_active_layer(&psmd->mesh_final->ldata, CD_MLOOPCOL);
}
}
#endif
GPUVertBufRaw data_step;
GPUVertBufRaw uv_step[MAX_MTFACE];
GPUVertBufRaw col_step[MAX_MCOL];
MTFace *mtfaces[MAX_MTFACE] = {NULL};
MCol *mcols[MAX_MCOL] = {NULL};
GPUVertFormat format_data = {0};
uint data_id = GPU_vertformat_attr_add(&format_data, "data", GPU_COMP_U32, 1, GPU_FETCH_INT);
GPUVertFormat format_uv = {0};
uint uv_id = GPU_vertformat_attr_add(&format_uv, "uv", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
GPUVertFormat format_col = {0};
uint col_id = GPU_vertformat_attr_add(&format_col, "col", GPU_COMP_U16, 4, GPU_FETCH_INT_TO_FLOAT_UNIT);
memset(cache->uv_layer_names, 0, sizeof(cache->uv_layer_names));
memset(cache->col_layer_names, 0, sizeof(cache->col_layer_names));
/* Strand Data */
cache->proc_strand_buf = GPU_vertbuf_create_with_format(&format_data);
GPU_vertbuf_data_alloc(cache->proc_strand_buf, cache->strands_len);
GPU_vertbuf_attr_get_raw_data(cache->proc_strand_buf, data_id, &data_step);
#if 0 // TODO
/* UV layers */
for (int i = 0; i < cache->num_uv_layers; i++) {
cache->proc_uv_buf[i] = GPU_vertbuf_create_with_format(&format_uv);
GPU_vertbuf_data_alloc(cache->proc_uv_buf[i], cache->strands_len);
GPU_vertbuf_attr_get_raw_data(cache->proc_uv_buf[i], uv_id, &uv_step[i]);
const char *name = CustomData_get_layer_name(&psmd->mesh_final->ldata, CD_MLOOPUV, i);
uint hash = BLI_ghashutil_strhash_p(name);
int n = 0;
BLI_snprintf(cache->uv_layer_names[i][n++], MAX_LAYER_NAME_LEN, "u%u", hash);
BLI_snprintf(cache->uv_layer_names[i][n++], MAX_LAYER_NAME_LEN, "a%u", hash);
if (i == active_uv) {
BLI_snprintf(cache->uv_layer_names[i][n], MAX_LAYER_NAME_LEN, "u");
}
}
/* Vertex colors */
for (int i = 0; i < cache->num_col_layers; i++) {
cache->proc_col_buf[i] = GPU_vertbuf_create_with_format(&format_col);
GPU_vertbuf_data_alloc(cache->proc_col_buf[i], cache->strands_len);
GPU_vertbuf_attr_get_raw_data(cache->proc_col_buf[i], col_id, &col_step[i]);
const char *name = CustomData_get_layer_name(&psmd->mesh_final->ldata, CD_MLOOPCOL, i);
uint hash = BLI_ghashutil_strhash_p(name);
int n = 0;
BLI_snprintf(cache->col_layer_names[i][n++], MAX_LAYER_NAME_LEN, "c%u", hash);
/* We only do vcols auto name that are not overridden by uvs */
if (CustomData_get_named_layer_index(&psmd->mesh_final->ldata, CD_MLOOPUV, name) == -1) {
BLI_snprintf(cache->col_layer_names[i][n++], MAX_LAYER_NAME_LEN, "a%u", hash);
}
if (i == active_col) {
BLI_snprintf(cache->col_layer_names[i][n], MAX_LAYER_NAME_LEN, "c");
}
}
if (cache->num_uv_layers || cache->num_col_layers) {
BKE_mesh_tessface_ensure(psmd->mesh_final);
if (cache->num_uv_layers) {
for (int j = 0; j < cache->num_uv_layers; j++) {
mtfaces[j] = (MTFace *)CustomData_get_layer_n(&psmd->mesh_final->fdata, CD_MTFACE, j);
}
}
if (cache->num_col_layers) {
for (int j = 0; j < cache->num_col_layers; j++) {
mcols[j] = (MCol *)CustomData_get_layer_n(&psmd->mesh_final->fdata, CD_MCOL, j);
}
}
}
#endif
hair_batch_cache_fill_strands_data(
hair_export,
&data_step,
uv_step, cache->num_uv_layers,
col_step, cache->num_col_layers);
/* Create vbo immediatly to bind to texture buffer. */
GPU_vertbuf_use(cache->proc_strand_buf);
cache->strand_tex = GPU_texture_create_from_vertbuf(cache->proc_strand_buf);
for (int i = 0; i < cache->num_uv_layers; i++) {
GPU_vertbuf_use(cache->proc_uv_buf[i]);
cache->uv_tex[i] = GPU_texture_create_from_vertbuf(cache->proc_uv_buf[i]);
}
for (int i = 0; i < cache->num_col_layers; i++) {
GPU_vertbuf_use(cache->proc_col_buf[i]);
cache->col_tex[i] = GPU_texture_create_from_vertbuf(cache->proc_col_buf[i]);
}
}
static void hair_batch_cache_ensure_final_count(
const HairExportCache *hair_export,
ParticleHairFinalCache *cache,
int subdiv,
int thickness_res)
{
const int totverts = hair_export->totverts;
const int totcurves = hair_export->totcurves;
cache->strands_len = hair_export->totfollicles;
/* +1 for primitive restart */
cache->elems_len = (((totverts - totcurves) << subdiv) + totcurves) * thickness_res;
cache->point_len = ((totverts - totcurves) << subdiv) + totcurves;
}
static void hair_batch_cache_ensure_procedural_final_points(
ParticleHairCache *cache,
int subdiv)
{
/* Same format as point_tex. */
GPUVertFormat format = { 0 };
GPU_vertformat_attr_add(&format, "pos", GPU_COMP_F32, 4, GPU_FETCH_FLOAT);
cache->final[subdiv].proc_point_buf = GPU_vertbuf_create_with_format(&format);
/* Create a destination buffer for the tranform feedback. Sized appropriately */
/* Thoses are points! not line segments. */
GPU_vertbuf_data_alloc(cache->final[subdiv].proc_point_buf, cache->final[subdiv].point_len);
/* Create vbo immediatly to bind to texture buffer. */
GPU_vertbuf_use(cache->final[subdiv].proc_point_buf);
cache->final[subdiv].proc_tex = GPU_texture_create_from_vertbuf(cache->final[subdiv].proc_point_buf);
}
static int hair_batch_cache_fill_segments_indices(
const HairExportCache *hair_export,
const int subdiv,
const int thickness_res,
GPUIndexBufBuilder *elb)
{
int curr_point = 0;
for (int i = 0; i < hair_export->totcurves; i++) {
const HairFiberCurve *curve = &hair_export->fiber_curves[i];
if (curve->numverts < 2) {
continue;
}
const int res = (((curve->numverts - 1) << subdiv) + 1) * thickness_res;
for (int k = 0; k < res; k++) {
GPU_indexbuf_add_generic_vert(elb, curr_point++);
}
GPU_indexbuf_add_primitive_restart(elb);
}
return curr_point;
}
static void hair_batch_cache_ensure_procedural_indices(
const HairExportCache *hair_export,
ParticleHairCache *cache,
int thickness_res,
int subdiv)
{
BLI_assert(thickness_res <= MAX_THICKRES); /* Cylinder strip not currently supported. */
if (cache->final[subdiv].proc_hairs[thickness_res - 1] != NULL) {
return;
}
int element_count = cache->final[subdiv].elems_len;
GPUPrimType prim_type = (thickness_res == 1) ? GPU_PRIM_LINE_STRIP : GPU_PRIM_TRI_STRIP;
static GPUVertFormat format = { 0 };
GPU_vertformat_clear(&format);
/* initialize vertex format */
GPU_vertformat_attr_add(&format, "dummy", GPU_COMP_U8, 1, GPU_FETCH_INT_TO_FLOAT_UNIT);
GPUVertBuf *vbo = GPU_vertbuf_create_with_format(&format);
GPU_vertbuf_data_alloc(vbo, 1);
GPUIndexBufBuilder elb;
GPU_indexbuf_init_ex(&elb, prim_type, element_count, element_count, true);
hair_batch_cache_fill_segments_indices(hair_export, subdiv, thickness_res, &elb);
cache->final[subdiv].proc_hairs[thickness_res - 1] = GPU_batch_create_ex(
prim_type,
vbo,
GPU_indexbuf_build(&elb),
GPU_BATCH_OWNS_VBO | GPU_BATCH_OWNS_INDEX);
}
/* Ensure all textures and buffers needed for GPU accelerated drawing. */
bool hair_ensure_procedural_data(
Object *UNUSED(object),
HairSystem *hsys,
struct Mesh *scalp,
ParticleHairCache **r_hair_cache,
int subdiv,
int thickness_res)
{
bool need_ft_update = false;
HairExportCache *hair_export = BKE_hair_export_cache_new();
BKE_hair_export_cache_update(hair_export, hsys, subdiv, scalp, HAIR_EXPORT_ALL);
HairBatchCache *cache = hair_batch_cache_get(hsys);
*r_hair_cache = &cache->hair;
/* Refreshed on combing and simulation. */
if (cache->hair.proc_point_buf == NULL) {
hair_batch_cache_ensure_count(hair_export, &cache->hair);
hair_batch_cache_ensure_procedural_pos(hair_export, &cache->hair);
need_ft_update = true;
}
/* Refreshed if active layer or custom data changes. */
if (cache->hair.strand_tex == NULL) {
hair_batch_cache_ensure_procedural_strand_data(hair_export, &cache->hair);
}
/* Refreshed only on subdiv count change. */
if (cache->hair.final[subdiv].proc_point_buf == NULL) {
hair_batch_cache_ensure_final_count(hair_export, &cache->hair.final[subdiv], subdiv, thickness_res);
hair_batch_cache_ensure_procedural_final_points(&cache->hair, subdiv);
need_ft_update = true;
}
if (cache->hair.final[subdiv].proc_hairs[thickness_res - 1] == NULL) {
hair_batch_cache_ensure_procedural_indices(hair_export, &cache->hair, thickness_res, subdiv);
}
BKE_hair_export_cache_free(hair_export);
return need_ft_update;
}
GPUBatch *DRW_hair_batch_cache_get_fibers(HairSystem *hsys, const HairExportCache *hair_export)
{
// TODO
UNUSED_VARS(hsys, hair_export);
return NULL;
}
GPUBatch *DRW_hair_batch_cache_get_follicle_points(HairSystem *hsys, const HairExportCache *hair_export)
{
// TODO
UNUSED_VARS(hsys, hair_export);
return NULL;
}

View File

@@ -162,37 +162,6 @@ static void particle_batch_cache_clear_point(ParticlePointCache *point_cache)
GPU_VERTBUF_DISCARD_SAFE(point_cache->pos);
}
static void particle_batch_cache_clear_hair(ParticleHairCache *hair_cache)
{
/* TODO more granular update tagging. */
GPU_VERTBUF_DISCARD_SAFE(hair_cache->proc_point_buf);
DRW_TEXTURE_FREE_SAFE(hair_cache->point_tex);
GPU_VERTBUF_DISCARD_SAFE(hair_cache->proc_strand_buf);
DRW_TEXTURE_FREE_SAFE(hair_cache->strand_tex);
for (int i = 0; i < MAX_MTFACE; ++i) {
GPU_VERTBUF_DISCARD_SAFE(hair_cache->proc_uv_buf[i]);
DRW_TEXTURE_FREE_SAFE(hair_cache->uv_tex[i]);
}
for (int i = 0; i < MAX_MCOL; ++i) {
GPU_VERTBUF_DISCARD_SAFE(hair_cache->proc_col_buf[i]);
DRW_TEXTURE_FREE_SAFE(hair_cache->col_tex[i]);
}
for (int i = 0; i < MAX_HAIR_SUBDIV; ++i) {
GPU_VERTBUF_DISCARD_SAFE(hair_cache->final[i].proc_buf);
DRW_TEXTURE_FREE_SAFE(hair_cache->final[i].proc_tex);
for (int j = 0; j < MAX_THICKRES; ++j) {
GPU_BATCH_DISCARD_SAFE(hair_cache->final[i].proc_hairs[j]);
}
}
/* "Normal" legacy hairs */
GPU_BATCH_DISCARD_SAFE(hair_cache->hairs);
GPU_VERTBUF_DISCARD_SAFE(hair_cache->pos);
GPU_INDEXBUF_DISCARD_SAFE(hair_cache->indices);
}
static void particle_batch_cache_clear(ParticleSystem *psys)
{
ParticleBatchCache *cache = psys->batch_cache;
@@ -654,19 +623,26 @@ static void particle_batch_cache_fill_segments_proc_pos(
}
static int particle_batch_cache_fill_segments_indices(
const ParticleSystem *psys,
ParticleCacheKey **path_cache,
const int start_index,
const int num_path_keys,
const int res,
const int subdiv,
const int thickness_res,
GPUIndexBufBuilder *elb)
{
const ParticleSettings *part = psys->part;
const int points_per_curve = (1 << (part->draw_step + subdiv)) + 1;
const int points_per_hair = points_per_curve * thickness_res;
int curr_point = start_index;
for (int i = 0; i < num_path_keys; i++) {
ParticleCacheKey *path = path_cache[i];
if (path->segments <= 0) {
continue;
}
for (int k = 0; k < res; k++) {
for (int k = 0; k < points_per_hair; k++) {
GPU_indexbuf_add_generic_vert(elb, curr_point++);
}
GPU_indexbuf_add_primitive_restart(elb);
@@ -749,24 +725,93 @@ static int particle_batch_cache_fill_strands_data(
return curr_point;
}
static void ensure_seg_pt_final_count(
const ParticleSystem *psys,
ParticleHairCache *hair_cache,
int subdiv,
int thickness_res)
{
ParticleHairFinalCache *final_cache = &hair_cache->final[subdiv];
const ParticleSettings *part = psys->part;
const int points_per_curve = (1 << (part->draw_step + subdiv)) + 1;
final_cache->strands_len = hair_cache->strands_len;
final_cache->point_len = points_per_curve * final_cache->strands_len;
/* +1 for primitive restart */
final_cache->elems_len = (points_per_curve * thickness_res + 1) * final_cache->strands_len;
}
#define USE_POSITION_HAIR_INDEX
static void particle_batch_cache_ensure_procedural_final_points(
const ParticleSystem *psys,
ParticleHairCache *cache,
int subdiv)
{
/* Same format as point_tex. */
#ifdef USE_POSITION_HAIR_INDEX
static GPUVertFormat format = { 0 };
GPU_vertformat_clear(&format);
uint pos_id = GPU_vertformat_attr_add(&format, "pos", GPU_COMP_F32, 4, GPU_FETCH_FLOAT);
#else
static GPUVer format = { 0 };
GPU_vertformat_attr_add(&format, "pos", GPU_COMP_F32, 4, GPU_FETCH_FLOAT);
#endif
cache->final[subdiv].proc_point_buf = GPU_vertbuf_create_with_format(&format);
/* Create a destination buffer for the tranform feedback. Sized appropriately */
/* Thoses are points! not line segments. */
GPU_vertbuf_data_alloc(cache->final[subdiv].proc_point_buf, cache->final[subdiv].point_len);
#ifdef USE_POSITION_HAIR_INDEX
GPUVertBufRaw data_step;
GPU_vertbuf_attr_get_raw_data(cache->final[subdiv].proc_point_buf, pos_id, &data_step);
const int points_per_curve = (1 << (psys->part->draw_step + subdiv)) + 1;
for (int i = 0; i < cache->final[subdiv].strands_len; i++) {
for (int j = 0; j < points_per_curve; ++j) {
uint *data = (uint *)GPU_vertbuf_raw_step(&data_step);
*data = (uint)i;
}
}
#endif
/* Create vbo immediatly to bind to texture buffer. */
GPU_vertbuf_use(cache->final[subdiv].proc_point_buf);
cache->final[subdiv].proc_tex = GPU_texture_create_from_vertbuf(cache->final[subdiv].proc_point_buf);
}
static void particle_batch_cache_ensure_procedural_final_hair_index(
const ParticleSystem *psys,
ParticleHairCache *cache,
int subdiv)
{
/* Same format as point_tex. */
GPUVertFormat format = { 0 };
GPU_vertformat_attr_add(&format, "pos", GPU_COMP_F32, 4, GPU_FETCH_FLOAT);
uint hair_index_id = GPU_vertformat_attr_add(&format, "hair_index", GPU_COMP_U32, 1, GPU_FETCH_INT);
cache->final[subdiv].proc_buf = GPU_vertbuf_create_with_format(&format);
cache->final[subdiv].proc_hair_index_buf = GPU_vertbuf_create_with_format(&format);
/* Create a destination buffer for the tranform feedback. Sized appropriately */
/* Thoses are points! not line segments. */
GPU_vertbuf_data_alloc(cache->final[subdiv].proc_buf, cache->final[subdiv].strands_res * cache->strands_len);
GPU_vertbuf_data_alloc(cache->final[subdiv].proc_hair_index_buf, cache->final[subdiv].point_len);
GPUVertBufRaw data_step;
GPU_vertbuf_attr_get_raw_data(cache->final[subdiv].proc_hair_index_buf, hair_index_id, &data_step);
const int points_per_curve = (1 << (psys->part->draw_step + subdiv)) + 1;
for (int i = 0; i < cache->final[subdiv].strands_len; i++) {
for (int j = 0; j < points_per_curve; ++j) {
uint *data = (uint *)GPU_vertbuf_raw_step(&data_step);
*data = (uint)i;
}
}
/* Create vbo immediatly to bind to texture buffer. */
GPU_vertbuf_use(cache->final[subdiv].proc_buf);
GPU_vertbuf_use(cache->final[subdiv].proc_hair_index_buf);
cache->final[subdiv].proc_tex = GPU_texture_create_from_vertbuf(cache->final[subdiv].proc_buf);
cache->final[subdiv].hair_index_tex = GPU_texture_create_from_vertbuf(cache->final[subdiv].proc_hair_index_buf);
}
static void particle_batch_cache_ensure_procedural_strand_data(
@@ -940,9 +985,7 @@ static void particle_batch_cache_ensure_procedural_indices(
return;
}
int verts_per_hair = cache->final[subdiv].strands_res * thickness_res;
/* +1 for primitive restart */
int element_count = (verts_per_hair + 1) * cache->strands_len;
int element_count = cache->final[subdiv].elems_len;
GPUPrimType prim_type = (thickness_res == 1) ? GPU_PRIM_LINE_STRIP : GPU_PRIM_TRI_STRIP;
static GPUVertFormat format = { 0 };
@@ -959,7 +1002,7 @@ static void particle_batch_cache_ensure_procedural_indices(
if (edit != NULL && edit->pathcache != NULL) {
particle_batch_cache_fill_segments_indices(
edit->pathcache, 0, edit->totcached, verts_per_hair, &elb);
psys, edit->pathcache, 0, edit->totcached, subdiv, thickness_res, &elb);
}
else {
int curr_point = 0;
@@ -967,12 +1010,12 @@ static void particle_batch_cache_ensure_procedural_indices(
(!psys->childcache || (psys->part->draw & PART_DRAW_PARENT)))
{
curr_point = particle_batch_cache_fill_segments_indices(
psys->pathcache, 0, psys->totpart, verts_per_hair, &elb);
psys, psys->pathcache, 0, psys->totpart, subdiv, thickness_res, &elb);
}
if (psys->childcache) {
const int child_count = psys->totchild * psys->part->disp / 100;
curr_point = particle_batch_cache_fill_segments_indices(
psys->childcache, curr_point, child_count, verts_per_hair, &elb);
psys, psys->childcache, curr_point, child_count, subdiv, thickness_res, &elb);
}
}
@@ -1555,12 +1598,9 @@ bool particles_ensure_procedural_data(
ParticleDrawSource source;
drw_particle_get_hair_source(object, psys, md, NULL, &source);
ParticleSettings *part = source.psys->part;
ParticleBatchCache *cache = particle_batch_cache_get(source.psys);
*r_hair_cache = &cache->hair;
(*r_hair_cache)->final[subdiv].strands_res = 1 << (part->draw_step + subdiv);
/* Refreshed on combing and simulation. */
if ((*r_hair_cache)->proc_point_buf == NULL) {
ensure_seg_pt_count(source.edit, source.psys, &cache->hair);
@@ -1574,8 +1614,10 @@ bool particles_ensure_procedural_data(
}
/* Refreshed only on subdiv count change. */
if ((*r_hair_cache)->final[subdiv].proc_buf == NULL) {
particle_batch_cache_ensure_procedural_final_points(&cache->hair, subdiv);
if ((*r_hair_cache)->final[subdiv].proc_point_buf == NULL) {
ensure_seg_pt_final_count(psys, &cache->hair, subdiv, thickness_res);
particle_batch_cache_ensure_procedural_final_points(psys, &cache->hair, subdiv);
particle_batch_cache_ensure_procedural_final_hair_index(psys, &cache->hair, subdiv);
need_ft_update = true;
}
if ((*r_hair_cache)->final[subdiv].proc_hairs[thickness_res - 1] == NULL) {

View File

@@ -26,11 +26,17 @@
#ifndef __DRAW_COMMON_H__
#define __DRAW_COMMON_H__
struct Mesh;
struct DRWPass;
struct DRWShadingGroup;
struct GPUBatch;
struct GPUMaterial;
struct GPUShader;
struct GPUTexture;
struct HairDrawSettings;
struct HairSystem;
struct Object;
struct Scene;
struct ViewLayer;
struct ModifierData;
struct ParticleSystem;
@@ -162,16 +168,28 @@ void DRW_shgroup_armature_edit(struct Object *ob, struct DRWArmaturePasses passe
/* This creates a shading group with display hairs.
* The draw call is already added by this function, just add additional uniforms. */
struct DRWShadingGroup *DRW_shgroup_hair_create(
struct DRWShadingGroup *DRW_shgroup_particle_hair_create(
struct Object *object, struct ParticleSystem *psys, struct ModifierData *md,
struct DRWPass *hair_pass,
struct GPUShader *shader);
struct DRWShadingGroup *DRW_shgroup_material_hair_create(
struct DRWShadingGroup *DRW_shgroup_material_particle_hair_create(
struct Object *object, struct ParticleSystem *psys, struct ModifierData *md,
struct DRWPass *hair_pass,
struct GPUMaterial *material);
struct DRWShadingGroup *DRW_shgroup_hair_create(
struct Object *object, struct HairSystem *hsys,
struct Mesh *scalp, const struct HairDrawSettings *draw_set,
struct DRWPass *hair_pass,
struct GPUShader *shader);
struct DRWShadingGroup *DRW_shgroup_material_hair_create(
struct Object *object, struct HairSystem *hsys,
struct Mesh *scalp, const struct HairDrawSettings *draw_set,
struct DRWPass *hair_pass,
struct GPUMaterial *material);
void DRW_hair_init(void);
void DRW_hair_update(void);
void DRW_hair_free(void);

View File

@@ -34,12 +34,14 @@
#include "BLI_utildefines.h"
#include "BLI_string_utils.h"
#include "DNA_hair_types.h"
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
#include "DNA_modifier_types.h"
#include "DNA_particle_types.h"
#include "DNA_customdata_types.h"
#include "BKE_hair.h"
#include "BKE_mesh.h"
#include "BKE_particle.h"
#include "BKE_pointcache.h"
@@ -85,7 +87,38 @@ void DRW_hair_init(void)
g_tf_pass = DRW_pass_create("Update Hair Pass", DRW_STATE_TRANS_FEEDBACK);
}
static DRWShadingGroup *drw_shgroup_create_hair_procedural_ex(
void particle_batch_cache_clear_hair(ParticleHairCache *hair_cache)
{
/* TODO more granular update tagging. */
GPU_VERTBUF_DISCARD_SAFE(hair_cache->proc_point_buf);
DRW_TEXTURE_FREE_SAFE(hair_cache->point_tex);
GPU_VERTBUF_DISCARD_SAFE(hair_cache->proc_strand_buf);
DRW_TEXTURE_FREE_SAFE(hair_cache->strand_tex);
for (int i = 0; i < MAX_MTFACE; ++i) {
GPU_VERTBUF_DISCARD_SAFE(hair_cache->proc_uv_buf[i]);
DRW_TEXTURE_FREE_SAFE(hair_cache->uv_tex[i]);
}
for (int i = 0; i < MAX_MCOL; ++i) {
GPU_VERTBUF_DISCARD_SAFE(hair_cache->proc_col_buf[i]);
DRW_TEXTURE_FREE_SAFE(hair_cache->col_tex[i]);
}
for (int i = 0; i < MAX_HAIR_SUBDIV; ++i) {
GPU_VERTBUF_DISCARD_SAFE(hair_cache->final[i].proc_point_buf);
DRW_TEXTURE_FREE_SAFE(hair_cache->final[i].proc_tex);
for (int j = 0; j < MAX_THICKRES; ++j) {
GPU_BATCH_DISCARD_SAFE(hair_cache->final[i].proc_hairs[j]);
}
}
/* "Normal" legacy hairs */
GPU_BATCH_DISCARD_SAFE(hair_cache->hairs);
GPU_VERTBUF_DISCARD_SAFE(hair_cache->pos);
GPU_INDEXBUF_DISCARD_SAFE(hair_cache->indices);
}
static DRWShadingGroup *drw_shgroup_create_particle_hair_procedural_ex(
Object *object, ParticleSystem *psys, ModifierData *md,
DRWPass *hair_pass,
struct GPUMaterial *gpu_mat, GPUShader *gpu_shader)
@@ -126,7 +159,7 @@ static DRWShadingGroup *drw_shgroup_create_hair_procedural_ex(
}
DRW_shgroup_uniform_texture(shgrp, "hairPointBuffer", hair_cache->final[subdiv].proc_tex);
DRW_shgroup_uniform_int(shgrp, "hairStrandsRes", &hair_cache->final[subdiv].strands_res, 1);
DRW_shgroup_uniform_texture(shgrp, "hairIndexBuffer", hair_cache->final[subdiv].hair_index_tex);
DRW_shgroup_uniform_int_copy(shgrp, "hairThicknessRes", thickness_res);
DRW_shgroup_uniform_float(shgrp, "hairRadShape", &part->shape, 1);
DRW_shgroup_uniform_float_copy(shgrp, "hairRadRoot", part->rad_root * part->rad_scale * 0.5f);
@@ -137,33 +170,110 @@ static DRWShadingGroup *drw_shgroup_create_hair_procedural_ex(
/* Transform Feedback subdiv. */
if (need_ft_update) {
int final_points_len = hair_cache->final[subdiv].strands_res * hair_cache->strands_len;
GPUShader *tf_shader = hair_refine_shader_get(PART_REFINE_CATMULL_ROM);
DRWShadingGroup *tf_shgrp = DRW_shgroup_transform_feedback_create(tf_shader, g_tf_pass,
hair_cache->final[subdiv].proc_buf);
hair_cache->final[subdiv].proc_point_buf);
DRW_shgroup_uniform_texture(tf_shgrp, "hairPointBuffer", hair_cache->point_tex);
DRW_shgroup_uniform_texture(tf_shgrp, "hairStrandBuffer", hair_cache->strand_tex);
DRW_shgroup_uniform_int(tf_shgrp, "hairStrandsRes", &hair_cache->final[subdiv].strands_res, 1);
DRW_shgroup_call_procedural_points_add(tf_shgrp, final_points_len, NULL);
DRW_shgroup_call_procedural_points_add(tf_shgrp, hair_cache->final[subdiv].point_len, NULL);
}
return shgrp;
}
DRWShadingGroup *DRW_shgroup_hair_create(
DRWShadingGroup *DRW_shgroup_particle_hair_create(
Object *object, ParticleSystem *psys, ModifierData *md,
DRWPass *hair_pass,
GPUShader *shader)
{
return drw_shgroup_create_hair_procedural_ex(object, psys, md, hair_pass, NULL, shader);
return drw_shgroup_create_particle_hair_procedural_ex(object, psys, md, hair_pass, NULL, shader);
}
DRWShadingGroup *DRW_shgroup_material_hair_create(
DRWShadingGroup *DRW_shgroup_material_particle_hair_create(
Object *object, ParticleSystem *psys, ModifierData *md,
DRWPass *hair_pass,
struct GPUMaterial *material)
{
return drw_shgroup_create_hair_procedural_ex(object, psys, md, hair_pass, material, NULL);
return drw_shgroup_create_particle_hair_procedural_ex(object, psys, md, hair_pass, material, NULL);
}
static DRWShadingGroup *drw_shgroup_create_hair_procedural_ex(
Object *object, HairSystem *hsys, Mesh *scalp, const HairDrawSettings *draw_set,
DRWPass *hair_pass,
struct GPUMaterial *gpu_mat, GPUShader *gpu_shader)
{
/* TODO(fclem): Pass the scene as parameter */
const DRWContextState *draw_ctx = DRW_context_state_get();
Scene *scene = draw_ctx->scene;
int subdiv = scene->r.hair_subdiv;
int thickness_res = (scene->r.hair_type == SCE_HAIR_SHAPE_STRAND) ? 1 : 2;
ParticleHairCache *hair_cache;
bool need_ft_update = hair_ensure_procedural_data(object, hsys, scalp, &hair_cache, subdiv, thickness_res);
DRWShadingGroup *shgrp;
if (gpu_mat) {
shgrp = DRW_shgroup_material_create(gpu_mat, hair_pass);
}
else if (gpu_shader) {
shgrp = DRW_shgroup_create(gpu_shader, hair_pass);
}
else {
shgrp = NULL;
BLI_assert(0);
}
/* TODO optimize this. Only bind the ones GPUMaterial needs. */
for (int i = 0; i < hair_cache->num_uv_layers; ++i) {
for (int n = 0; hair_cache->uv_layer_names[i][n][0] != '\0'; ++n) {
DRW_shgroup_uniform_texture(shgrp, hair_cache->uv_layer_names[i][n], hair_cache->uv_tex[i]);
}
}
for (int i = 0; i < hair_cache->num_col_layers; ++i) {
for (int n = 0; hair_cache->col_layer_names[i][n][0] != '\0'; ++n) {
DRW_shgroup_uniform_texture(shgrp, hair_cache->col_layer_names[i][n], hair_cache->col_tex[i]);
}
}
DRW_shgroup_uniform_texture(shgrp, "hairPointBuffer", hair_cache->final[subdiv].proc_tex);
DRW_shgroup_uniform_int_copy(shgrp, "hairThicknessRes", thickness_res);
DRW_shgroup_uniform_float(shgrp, "hairRadShape", &draw_set->shape, 1);
DRW_shgroup_uniform_float_copy(shgrp, "hairRadRoot", draw_set->root_radius * draw_set->radius_scale * 0.5f);
DRW_shgroup_uniform_float_copy(shgrp, "hairRadTip", draw_set->tip_radius * draw_set->radius_scale * 0.5f);
DRW_shgroup_uniform_bool_copy(shgrp, "hairCloseTip", (draw_set->shape_flag & PART_SHAPE_CLOSE_TIP) != 0);
/* TODO(fclem): Until we have a better way to cull the hair and render with orco, bypass culling test. */
DRW_shgroup_call_object_add_no_cull(shgrp, hair_cache->final[subdiv].proc_hairs[thickness_res - 1], object);
/* Transform Feedback subdiv. */
if (need_ft_update) {
GPUShader *tf_shader = hair_refine_shader_get(PART_REFINE_CATMULL_ROM);
DRWShadingGroup *tf_shgrp = DRW_shgroup_transform_feedback_create(tf_shader, g_tf_pass,
hair_cache->final[subdiv].proc_point_buf);
DRW_shgroup_uniform_texture(tf_shgrp, "hairPointBuffer", hair_cache->point_tex);
DRW_shgroup_uniform_texture(tf_shgrp, "hairStrandBuffer", hair_cache->strand_tex);
DRW_shgroup_call_procedural_points_add(tf_shgrp, hair_cache->final[subdiv].point_len, NULL);
}
return shgrp;
}
DRWShadingGroup *DRW_shgroup_hair_create(
Object *object, HairSystem *hsys,
Mesh *scalp, const HairDrawSettings *draw_set,
DRWPass *hair_pass,
GPUShader *shader)
{
return drw_shgroup_create_hair_procedural_ex(object, hsys, scalp, draw_set, hair_pass, NULL, shader);
}
DRWShadingGroup *DRW_shgroup_material_hair_create(
Object *object, HairSystem *hsys,
Mesh *scalp, const HairDrawSettings *draw_set,
DRWPass *hair_pass,
struct GPUMaterial *material)
{
return drw_shgroup_create_hair_procedural_ex(object, hsys, scalp, draw_set, hair_pass, material, NULL);
}
void DRW_hair_update(void)

View File

@@ -39,16 +39,22 @@ struct Object;
struct ParticleSystem;
struct ModifierData;
struct ParticleHairCache;
struct HairSystem;
typedef struct ParticleHairFinalCache {
/* Output of the subdivision stage: vertex buff sized to subdiv level. */
GPUVertBuf *proc_buf;
GPUVertBuf *proc_point_buf;
GPUTexture *proc_tex;
/* Just contains a huge index buffer used to draw the final hair. */
GPUVertBuf *proc_hair_index_buf; /* Hair strand index for each vertex */
GPUTexture *hair_index_tex;
/* Just contains a huge index buffer used to draw the final hair. */
GPUBatch *proc_hairs[MAX_THICKRES];
int strands_res; /* points per hair, at least 2 */
int strands_len;
int elems_len;
int point_len;
} ParticleHairFinalCache;
typedef struct ParticleHairCache {
@@ -81,6 +87,8 @@ typedef struct ParticleHairCache {
int point_len;
} ParticleHairCache;
void particle_batch_cache_clear_hair(struct ParticleHairCache *hair_cache);
bool particles_ensure_procedural_data(
struct Object *object,
struct ParticleSystem *psys,
@@ -89,4 +97,12 @@ bool particles_ensure_procedural_data(
int subdiv,
int thickness_res);
bool hair_ensure_procedural_data(
struct Object *object,
struct HairSystem *hsys,
struct Mesh *scalp,
struct ParticleHairCache **r_hair_cache,
int subdiv,
int thickness_res);
#endif /* __DRAW_HAIR_PRIVATE_H__ */

View File

@@ -2464,6 +2464,9 @@ void DRW_engines_register(void)
/* BKE: gpencil.c */
extern void *BKE_gpencil_batch_cache_dirty_cb;
extern void *BKE_gpencil_batch_cache_free_cb;
/* BKE: hair.c */
extern void *BKE_hair_batch_cache_dirty_cb;
extern void *BKE_hair_batch_cache_free_cb;
BKE_mball_batch_cache_dirty_cb = DRW_mball_batch_cache_dirty;
BKE_mball_batch_cache_free_cb = DRW_mball_batch_cache_free;
@@ -2482,6 +2485,9 @@ void DRW_engines_register(void)
BKE_gpencil_batch_cache_dirty_cb = DRW_gpencil_batch_cache_dirty;
BKE_gpencil_batch_cache_free_cb = DRW_gpencil_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;
}
}

View File

@@ -114,6 +114,7 @@ typedef struct OBJECT_PassList {
struct DRWPass *bone_envelope;
struct DRWPass *bone_axes;
struct DRWPass *particle;
struct DRWPass *hair;
struct DRWPass *lightprobes;
/* use for empty/background images */
struct DRWPass *reference_image;
@@ -247,6 +248,9 @@ typedef struct OBJECT_PrivateData {
/* Texture Space */
DRWShadingGroup *texspace;
/* Hair Systems */
DRWShadingGroup *hair_verts;
/* Outlines id offset */
int id_ofs_active;
int id_ofs_select;
@@ -1350,6 +1354,20 @@ static void OBJECT_cache_init(void *vedata)
DRW_STATE_POINT | DRW_STATE_BLEND);
}
{
/* Hair */
psl->hair = DRW_pass_create(
"Hair Pass",
DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS | DRW_STATE_BLEND |
DRW_STATE_POINT | DRW_STATE_WIRE);
GPUShader *sh_verts = GPU_shader_get_builtin_shader(GPU_SHADER_3D_POINT_UNIFORM_SIZE_UNIFORM_COLOR_AA);
stl->g_data->hair_verts = DRW_shgroup_create(sh_verts, psl->hair);
DRW_shgroup_uniform_vec4(stl->g_data->hair_verts, "color", ts.colorVertex, 1);
DRW_shgroup_uniform_float(stl->g_data->hair_verts, "size", &ts.sizeVertex, 1);
DRW_shgroup_state_enable(stl->g_data->hair_verts, DRW_STATE_POINT);
}
{
/* Empty/Background Image Pass */
psl->reference_image = DRW_pass_create(
@@ -2623,6 +2641,7 @@ static void OBJECT_draw_scene(void *vedata)
DRW_draw_pass(psl->lightprobes);
}
DRW_draw_pass(psl->hair);
DRW_draw_pass(psl->ob_center);
if (DRW_state_is_fbo()) {

View File

@@ -5,13 +5,6 @@
* of data the CPU has to precompute and transfert for each update.
**/
/**
* hairStrandsRes: Number of points per hair strand.
* 2 - no subdivision
* 3+ - 1 or more interpolated points per hair.
**/
uniform int hairStrandsRes = 8;
/**
* hairThicknessRes : Subdiv around the hair.
* 1 - Wire Hair: Only one pixel thick, independant of view distance.
@@ -33,6 +26,7 @@ uniform samplerBuffer hairPointBuffer; /* RGBA32F */
/* -- Per strands data -- */
uniform usamplerBuffer hairStrandBuffer; /* R32UI */
uniform usamplerBuffer hairIndexBuffer; /* R32UI */
/* Not used, use one buffer per uv layer */
//uniform samplerBuffer hairUVBuffer; /* RG32F */
@@ -49,6 +43,13 @@ void unpack_strand_data(uint data, out int strand_offset, out int strand_segment
#endif
}
int hair_get_strand_id(void)
{
//return gl_VertexID / (hairStrandsRes * hairThicknessRes);
uint strand_index = texelFetch(hairIndexBuffer, gl_VertexID).x;
return int(strand_index);
}
/* -- Subdivision stage -- */
/**
* We use a transform feedback to preprocess the strands and add more subdivision to it.
@@ -59,40 +60,40 @@ void unpack_strand_data(uint data, out int strand_offset, out int strand_segment
**/
#ifdef HAIR_PHASE_SUBDIV
int hair_get_base_id(float local_time, int strand_segments, out float interp_time)
/**
* Calculate segment and local time for interpolation
*/
void hair_get_interp_time(float local_time, int strand_segments, out int interp_segment, out float interp_time)
{
float time_per_strand_seg = 1.0 / float(strand_segments);
float ratio = local_time / time_per_strand_seg;
interp_segment = int(ratio);
interp_time = fract(ratio);
return int(ratio);
}
void hair_get_interp_attribs(out vec4 data0, out vec4 data1, out vec4 data2, out vec4 data3, out float interp_time)
{
float local_time = float(gl_VertexID % hairStrandsRes) / float(hairStrandsRes - 1);
int hair_id = gl_VertexID / hairStrandsRes;
uint strand_data = texelFetch(hairStrandBuffer, hair_id).x;
int strand_index = hair_get_strand_id();
uint strand_data = texelFetch(hairStrandBuffer, strand_index).x;
int strand_offset, strand_segments;
unpack_strand_data(strand_data, strand_offset, strand_segments);
int id = hair_get_base_id(local_time, strand_segments, interp_time);
float local_time = float(gl_VertexID - strand_offset) / float(strand_segments);
int interp_segment;
hair_get_interp_time(local_time, strand_segments, interp_segment, interp_time);
int interp_point = interp_segment + strand_offset;
int ofs_id = id + strand_offset;
data0 = texelFetch(hairPointBuffer, interp_point - 1);
data1 = texelFetch(hairPointBuffer, interp_point);
data2 = texelFetch(hairPointBuffer, interp_point + 1);
data3 = texelFetch(hairPointBuffer, interp_point + 2);
data0 = texelFetch(hairPointBuffer, ofs_id - 1);
data1 = texelFetch(hairPointBuffer, ofs_id);
data2 = texelFetch(hairPointBuffer, ofs_id + 1);
data3 = texelFetch(hairPointBuffer, ofs_id + 2);
if (id <= 0) {
if (interp_segment <= 0) {
/* root points. Need to reconstruct previous data. */
data0 = data1 * 2.0 - data2;
}
if (id + 1 >= strand_segments) {
if (interp_segment + 1 >= strand_segments) {
/* tip points. Need to reconstruct next data. */
data3 = data2 * 2.0 - data1;
}
@@ -105,11 +106,6 @@ void hair_get_interp_attribs(out vec4 data0, out vec4 data1, out vec4 data2, out
**/
#ifndef HAIR_PHASE_SUBDIV
int hair_get_strand_id(void)
{
return gl_VertexID / (hairStrandsRes * hairThicknessRes);
}
int hair_get_base_id(void)
{
return gl_VertexID / hairThicknessRes;
@@ -165,25 +161,25 @@ void hair_get_pos_tan_binor_time(
vec2 hair_get_customdata_vec2(const samplerBuffer cd_buf)
{
int id = hair_get_strand_id();
return texelFetch(cd_buf, id).rg;
int strand_index = hair_get_strand_id();
return texelFetch(cd_buf, strand_index).rg;
}
vec3 hair_get_customdata_vec3(const samplerBuffer cd_buf)
{
int id = hair_get_strand_id();
return texelFetch(cd_buf, id).rgb;
int strand_index = hair_get_strand_id();
return texelFetch(cd_buf, strand_index).rgb;
}
vec4 hair_get_customdata_vec4(const samplerBuffer cd_buf)
{
int id = hair_get_strand_id();
return texelFetch(cd_buf, id).rgba;
int strand_index = hair_get_strand_id();
return texelFetch(cd_buf, strand_index).rgba;
}
vec3 hair_get_strand_pos(void)
vec3 hair_get_strand_pos()
{
int id = hair_get_strand_id() * hairStrandsRes;
int id = hair_get_base_id();
return texelFetch(hairPointBuffer, id).point_position;
}

View File

@@ -175,6 +175,7 @@ void OBJECT_OT_skin_radii_equalize(struct wmOperatorType *ot);
void OBJECT_OT_skin_armature_create(struct wmOperatorType *ot);
void OBJECT_OT_laplaciandeform_bind(struct wmOperatorType *ot);
void OBJECT_OT_surfacedeform_bind(struct wmOperatorType *ot);
void OBJECT_OT_hair_generate_follicles(struct wmOperatorType *ot);
/* grease pencil modifiers */
void OBJECT_OT_gpencil_modifier_add(struct wmOperatorType *ot);

View File

@@ -37,6 +37,7 @@
#include "DNA_anim_types.h"
#include "DNA_armature_types.h"
#include "DNA_curve_types.h"
#include "DNA_hair_types.h"
#include "DNA_key_types.h"
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
@@ -58,12 +59,14 @@
#include "BKE_DerivedMesh.h"
#include "BKE_effect.h"
#include "BKE_global.h"
#include "BKE_hair.h"
#include "BKE_key.h"
#include "BKE_lattice.h"
#include "BKE_main.h"
#include "BKE_mesh.h"
#include "BKE_mesh_mapping.h"
#include "BKE_mesh_runtime.h"
#include "BKE_mesh_sample.h"
#include "BKE_modifier.h"
#include "BKE_multires.h"
#include "BKE_report.h"
@@ -2417,3 +2420,88 @@ void OBJECT_OT_surfacedeform_bind(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL;
edit_modifier_properties(ot);
}
/************************ Hair follicle generate operator *********************/
static int hair_generate_follicles_poll(bContext *C)
{
return edit_modifier_poll_generic(C, &RNA_HairModifier, 0);
}
static int hair_generate_follicles_exec(bContext *C, wmOperator *op)
{
Object *ob = ED_object_active_context(C);
HairModifierData *hmd = (HairModifierData *)edit_modifier_property_get(op, ob, eModifierType_Hair);
if (!hmd)
return OPERATOR_CANCELLED;
BLI_assert(hmd->hair_system != NULL);
struct Depsgraph *depsgraph = CTX_data_depsgraph(C);
BLI_assert(ob && ob->type == OB_MESH);
Mesh *scalp = (Mesh *)DEG_get_evaluated_id(depsgraph, ob->data);
HairSystem *hsys = hmd->hair_system;
BKE_hair_generate_follicles(
hsys,
scalp,
(unsigned int)hmd->follicle_seed,
hmd->follicle_count);
{
const int numverts = 5;
const float hairlen = 0.05f;
const float taper_length = 0.02f;
const float taper_thickness = 0.8f;
BKE_hair_fiber_curves_begin(hsys, hsys->pattern->num_follicles);
for (int i = 0; i < hsys->pattern->num_follicles; ++i)
{
BKE_hair_set_fiber_curve(hsys, i, numverts, taper_length, taper_thickness);
}
BKE_hair_fiber_curves_end(hsys);
for (int i = 0; i < hsys->pattern->num_follicles; ++i)
{
float loc[3], nor[3], tan[3];
BKE_mesh_sample_eval(scalp, &hsys->pattern->follicles[i].mesh_sample, loc, nor, tan);
for (int j = 0; j < numverts; ++j)
{
madd_v3_v3fl(loc, nor, hairlen / (numverts-1));
BKE_hair_set_fiber_vertex(hsys, i * numverts + j, 0, loc);
}
}
}
BKE_hair_bind_follicles(hmd->hair_system, scalp);
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_generate_follicles_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
{
if (edit_modifier_invoke_properties(C, op))
return hair_generate_follicles_exec(C, op);
else
return OPERATOR_CANCELLED;
}
void OBJECT_OT_hair_generate_follicles(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Hair Follicles Generate";
ot->description = "Generate hair follicles for a hair modifier";
ot->idname = "OBJECT_OT_hair_generate_follicles";
/* api callbacks */
ot->poll = hair_generate_follicles_poll;
ot->invoke = hair_generate_follicles_invoke;
ot->exec = hair_generate_follicles_exec;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL;
edit_modifier_properties(ot);
}

View File

@@ -263,6 +263,7 @@ void ED_operatortypes_object(void)
WM_operatortype_append(OBJECT_OT_data_transfer);
WM_operatortype_append(OBJECT_OT_datalayout_transfer);
WM_operatortype_append(OBJECT_OT_surfacedeform_bind);
WM_operatortype_append(OBJECT_OT_hair_generate_follicles);
WM_operatortype_append(OBJECT_OT_hide_view_clear);
WM_operatortype_append(OBJECT_OT_hide_view_set);

View File

@@ -1039,6 +1039,9 @@ TreeElementIcon tree_element_get_icon(TreeStoreElem *tselem, TreeElement *te)
case eModifierType_WeightedNormal:
data.icon = ICON_MOD_NORMALEDIT;
break;
case eModifierType_Hair:
data.icon = ICON_STRANDS;
break;
/* Default */
case eModifierType_None:
case eModifierType_ShapeKey:

View File

@@ -107,10 +107,10 @@ typedef enum GPUTextureFormat {
/* Texture only format */
GPU_RGB16F,
GPU_RGB32F,
#if 0
GPU_RGBA16_SNORM,
GPU_RGBA8_SNORM,
GPU_RGB32F,
GPU_RGB32I,
GPU_RGB32UI,
GPU_RGB16_SNORM,

View File

@@ -0,0 +1,136 @@
/*
* ***** BEGIN GPL LICENSE BLOCK *****
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributor(s): Blender Foundation
*
* ***** END GPL LICENSE BLOCK *****
*
*/
/** \file DNA_hair_types.h
* \ingroup DNA
*/
#ifndef __DNA_HAIR_TYPES_H__
#define __DNA_HAIR_TYPES_H__
#include "DNA_defs.h"
#include "DNA_listBase.h"
#include "DNA_ID.h"
#include "DNA_meshdata_types.h"
#ifdef __cplusplus
extern "C" {
#endif
/* Root point (follicle) of a hair on a surface */
typedef struct HairFollicle {
MeshSample mesh_sample; /* Sample on the scalp mesh for the root vertex */
unsigned int curve; /* Index of the curve used by the fiber */
int pad;
} HairFollicle;
/* Collection of hair roots on a surface */
typedef struct HairPattern {
struct HairFollicle *follicles;
int num_follicles;
int pad;
} HairPattern;
typedef struct HairFiberCurve {
int vertstart; /* Offset in the vertex array where the curve starts */
int numverts; /* Number of vertices in the curve */
/* Shape */
float taper_length; /* Distance at which final thickness is reached */
float taper_thickness; /* Relative thickness of the strand */
} HairFiberCurve;
typedef struct HairFiberVertex {
int flag;
float co[3];
} HairFiberVertex;
/* Hair curve data */
typedef struct HairCurveData
{
/* Curves for shaping hair fibers */
struct HairFiberCurve *curves;
/* Control vertices on curves */
struct HairFiberVertex *verts;
/* Number of curves */
int totcurves;
/* Number of curve vertices */
int totverts;
} HairCurveData;
typedef struct HairSystem {
int flag;
int pad;
/* Set of hair follicles on the scalp mesh */
struct HairPattern *pattern;
/* Curve data */
HairCurveData curve_data;
/* Data buffers for drawing */
void *draw_batch_cache;
/* Texture buffer for drawing */
void *draw_texture_cache;
} HairSystem;
typedef enum eHairSystemFlag
{
/* Curve positions have changed, rebind hair follicles */
HAIR_SYSTEM_UPDATE_FOLLICLE_BINDING = (1 << 8),
} eHairSystemFlag;
typedef struct HairDrawSettings
{
short follicle_mode;
short fiber_mode;
short shape_flag;
short pad;
float shape;
float root_radius;
float tip_radius;
float radius_scale;
} HairDrawSettings;
typedef enum eHairDrawFollicleMode
{
HAIR_DRAW_FOLLICLE_NONE = 0,
HAIR_DRAW_FOLLICLE_POINTS = 1,
} eHairDrawFollicleMode;
typedef enum eHairDrawFiberMode
{
HAIR_DRAW_FIBER_NONE = 0,
HAIR_DRAW_FIBER_CURVES = 1,
} eHairDrawFiberMode;
typedef enum eHairDrawShapeFlag {
HAIR_DRAW_CLOSE_TIP = (1<<0),
} eHairDrawShapeFlag;
#ifdef __cplusplus
}
#endif
#endif /* __DNA_HAIR_TYPES_H__ */

View File

@@ -380,6 +380,13 @@ enum {
FREESTYLE_FACE_MARK = 1,
};
typedef struct MeshSample {
unsigned int orig_verts[3];
float orig_weights[3]; /* also used as volume sample location */
unsigned int orig_poly;
unsigned int orig_loops[3];
} MeshSample;
/* mvert->flag */
enum {
/* SELECT = (1 << 0), */

View File

@@ -91,6 +91,7 @@ typedef enum ModifierType {
eModifierType_MeshSequenceCache = 52,
eModifierType_SurfaceDeform = 53,
eModifierType_WeightedNormal = 54,
eModifierType_Hair = 55,
NUM_MODIFIER_TYPES
} ModifierType;
@@ -1692,4 +1693,32 @@ enum {
#define MOD_MESHSEQ_READ_ALL \
(MOD_MESHSEQ_READ_VERT | MOD_MESHSEQ_READ_POLY | MOD_MESHSEQ_READ_UV | MOD_MESHSEQ_READ_COLOR)
/* Hair modifier */
typedef struct HairModifierFiberCurve {
struct HairModifierFiberCurve *next, *prev;
/* Index for the mesh sample buffer */
int mesh_sample_index;
/* Number of vertices in the curve */
int numverts;
/* Vertex array */
struct HairFiberVertex *verts;
} HairModifierFiberCurve;
typedef struct HairModifierData {
ModifierData modifier;
int flag;
int pad;
struct HairSystem *hair_system;
struct HairDrawSettings *draw_settings;
/* Follicle distribution parameters */
int follicle_seed;
int follicle_count;
ListBase fiber_curves;
} HairModifierData;
#endif /* __DNA_MODIFIER_TYPES_H__ */

View File

@@ -131,6 +131,7 @@ static const char *includefiles[] = {
"DNA_layer_types.h",
"DNA_workspace_types.h",
"DNA_lightprobe_types.h",
"DNA_hair_types.h",
/* see comment above before editing! */
@@ -1357,5 +1358,6 @@ int main(int argc, char **argv)
#include "DNA_layer_types.h"
#include "DNA_workspace_types.h"
#include "DNA_lightprobe_types.h"
#include "DNA_hair_types.h"
/* end of list */

View File

@@ -277,6 +277,9 @@ extern StructRNA RNA_GPencilSculptBrush;
extern StructRNA RNA_GaussianBlurSequence;
extern StructRNA RNA_GlowSequence;
extern StructRNA RNA_GreasePencil;
extern StructRNA RNA_HairGroup;
extern StructRNA RNA_HairModifier;
extern StructRNA RNA_HairPattern;
extern StructRNA RNA_Header;
extern StructRNA RNA_HemiLight;
extern StructRNA RNA_Histogram;

View File

@@ -52,6 +52,7 @@ set(DEFSRC
rna_gpencil_modifier.c
rna_shader_fx.c
rna_group.c
rna_hair.c
rna_image.c
rna_key.c
rna_lamp.c
@@ -62,6 +63,7 @@ set(DEFSRC
rna_mask.c
rna_material.c
rna_mesh.c
rna_mesh_sample.c
rna_meta.c
rna_modifier.c
rna_movieclip.c

View File

@@ -3405,6 +3405,7 @@ static RNAProcessItem PROCESS_ITEMS[] = {
{"rna_fluidsim.c", NULL, RNA_def_fluidsim},
{"rna_gpencil.c", NULL, RNA_def_gpencil},
{"rna_group.c", NULL, RNA_def_collections},
{"rna_hair.c", NULL, RNA_def_hair},
{"rna_image.c", "rna_image_api.c", RNA_def_image},
{"rna_key.c", NULL, RNA_def_key},
{"rna_lamp.c", NULL, RNA_def_light},
@@ -3414,6 +3415,7 @@ static RNAProcessItem PROCESS_ITEMS[] = {
{"rna_main.c", "rna_main_api.c", RNA_def_main},
{"rna_material.c", "rna_material_api.c", RNA_def_material},
{"rna_mesh.c", "rna_mesh_api.c", RNA_def_mesh},
{"rna_mesh_sample.c", NULL, RNA_def_mesh_sample},
{"rna_meta.c", "rna_meta_api.c", RNA_def_meta},
{"rna_modifier.c", NULL, RNA_def_modifier},
{"rna_gpencil_modifier.c", NULL, RNA_def_greasepencil_modifier},

View File

@@ -0,0 +1,222 @@
/*
* ***** BEGIN GPL LICENSE BLOCK *****
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributor(s): Blender Foundation.
*
* ***** END GPL LICENSE BLOCK *****
*/
/** \file blender/makesrna/intern/rna_hair.c
* \ingroup RNA
*/
#include <stdlib.h>
#include "RNA_define.h"
#include "RNA_enum_types.h"
#include "rna_internal.h"
#include "DNA_hair_types.h"
#include "WM_types.h"
#ifdef RNA_RUNTIME
#include "MEM_guardedalloc.h"
#include "BLI_listbase.h"
#include "DNA_mesh_types.h"
#include "DNA_object_types.h"
#include "BKE_context.h"
#include "BKE_hair.h"
#include "BKE_main.h"
#include "DEG_depsgraph.h"
#include "DEG_depsgraph_query.h"
#include "RNA_access.h"
#include "WM_api.h"
#include "WM_types.h"
static void UNUSED_FUNCTION(rna_HairSystem_update)(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *ptr)
{
DEG_id_tag_update(ptr->id.data, OB_RECALC_DATA);
}
static void rna_HairDrawSettings_update(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *ptr)
{
#if 0
/* XXX Only need to update render engines
* However, that requires finding all hair systems using these draw settings,
* then flagging the cache as dirty.
*/
BKE_hair_batch_cache_dirty(hsys, BKE_HAIR_BATCH_DIRTY_ALL);
#else
DEG_id_tag_update(ptr->id.data, OB_RECALC_DATA);
#endif
WM_main_add_notifier(NC_OBJECT | ND_DRAW, ptr->id.data);
}
static void rna_HairSystem_generate_follicles(
HairSystem *hsys,
struct bContext *C,
Object *scalp,
int seed,
int count)
{
if (!scalp)
{
return;
}
struct Depsgraph *depsgraph = CTX_data_depsgraph(C);
BLI_assert(scalp && scalp->type == OB_MESH);
Mesh *scalp_mesh = (Mesh *)DEG_get_evaluated_id(depsgraph, scalp->data);
BKE_hair_generate_follicles(hsys, scalp_mesh, (unsigned int)seed, count);
}
#else
static void rna_def_hair_follicle(BlenderRNA *brna)
{
StructRNA *srna;
PropertyRNA *prop;
srna = RNA_def_struct(brna, "HairFollicle", NULL);
RNA_def_struct_ui_text(srna, "Hair Follicle", "Single follicle on a surface");
RNA_def_struct_sdna(srna, "HairFollicle");
prop = RNA_def_property(srna, "mesh_sample", PROP_POINTER, PROP_NONE);
RNA_def_property_struct_type(prop, "MeshSample");
}
static void rna_def_hair_pattern(BlenderRNA *brna)
{
StructRNA *srna;
PropertyRNA *prop;
srna = RNA_def_struct(brna, "HairPattern", NULL);
RNA_def_struct_ui_text(srna, "Hair Pattern", "Set of hair follicles distributed on a surface");
RNA_def_struct_sdna(srna, "HairPattern");
RNA_def_struct_ui_icon(srna, ICON_STRANDS);
prop = RNA_def_property(srna, "follicles", PROP_COLLECTION, PROP_NONE);
RNA_def_property_collection_sdna(prop, NULL, "follicles", "num_follicles");
RNA_def_property_struct_type(prop, "HairFollicle");
RNA_def_property_ui_text(prop, "Follicles", "Hair fiber follicles");
}
static void rna_def_hair_system(BlenderRNA *brna)
{
StructRNA *srna;
FunctionRNA *func;
PropertyRNA *prop, *parm;
srna = RNA_def_struct(brna, "HairSystem", NULL);
RNA_def_struct_ui_text(srna, "Hair System", "Hair rendering and deformation data");
RNA_def_struct_sdna(srna, "HairSystem");
RNA_def_struct_ui_icon(srna, ICON_STRANDS);
prop = RNA_def_property(srna, "pattern", PROP_POINTER, PROP_NONE);
RNA_def_property_struct_type(prop, "HairPattern");
RNA_def_property_ui_text(prop, "Pattern", "Hair pattern");
func = RNA_def_function(srna, "generate_follicles", "rna_HairSystem_generate_follicles");
RNA_def_function_flag(func, FUNC_USE_CONTEXT);
parm = RNA_def_pointer(func, "scalp", "Object", "Scalp", "Scalp object on which to place hair follicles");
RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
parm = RNA_def_int(func, "seed", 0, 0, INT_MAX, "Seed", "Seed value for random numbers", 0, INT_MAX);
parm = RNA_def_int(func, "count", 0, 0, INT_MAX, "Count", "Maximum number of follicles to generate", 1, 1e5);
RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
}
static void rna_def_hair_draw_settings(BlenderRNA *brna)
{
StructRNA *srna;
PropertyRNA *prop;
static const EnumPropertyItem follicle_mode_items[] = {
{HAIR_DRAW_FOLLICLE_NONE, "NONE", 0, "None", ""},
{HAIR_DRAW_FOLLICLE_POINTS, "POINTS", 0, "Points", "Draw a point for each follicle"},
{0, NULL, 0, NULL, NULL}
};
static const EnumPropertyItem fiber_mode_items[] = {
{HAIR_DRAW_FIBER_NONE, "NONE", 0, "None", ""},
{HAIR_DRAW_FIBER_CURVES, "CURVES", 0, "Curves", "Draw fiber curves"},
{0, NULL, 0, NULL, NULL}
};
srna = RNA_def_struct(brna, "HairDrawSettings", NULL);
RNA_def_struct_ui_text(srna, "Hair Draw Settings", "Settings for drawing hair systems");
RNA_def_struct_sdna(srna, "HairDrawSettings");
prop = RNA_def_property(srna, "follicle_mode", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_items(prop, follicle_mode_items);
RNA_def_property_ui_text(prop, "Follicle Mode", "Draw follicles on the scalp surface");
RNA_def_property_update(prop, 0, "rna_HairDrawSettings_update");
prop = RNA_def_property(srna, "fiber_mode", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_items(prop, fiber_mode_items);
RNA_def_property_ui_text(prop, "Fiber Mode", "Draw fiber curves");
RNA_def_property_update(prop, 0, "rna_HairDrawSettings_update");
/* hair shape */
prop = RNA_def_property(srna, "use_close_tip", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "shape_flag", HAIR_DRAW_CLOSE_TIP);
RNA_def_property_ui_text(prop, "Close Tip", "Set tip radius to zero");
RNA_def_property_update(prop, 0, "rna_HairDrawSettings_update");
prop = RNA_def_property(srna, "shape", PROP_FLOAT, PROP_FACTOR);
RNA_def_property_range(prop, -1.0f, 1.0f);
RNA_def_property_ui_text(prop, "Shape", "Strand shape parameter");
RNA_def_property_update(prop, 0, "rna_HairDrawSettings_update");
prop = RNA_def_property(srna, "root_radius", PROP_FLOAT, PROP_NONE);
RNA_def_property_range(prop, 0.0f, FLT_MAX);
RNA_def_property_ui_range(prop, 0.0f, 10.0f, 0.1, 2);
RNA_def_property_ui_text(prop, "Root", "Strand width at the root");
RNA_def_property_update(prop, 0, "rna_HairDrawSettings_update");
prop = RNA_def_property(srna, "tip_radius", PROP_FLOAT, PROP_NONE);
RNA_def_property_range(prop, 0.0f, FLT_MAX);
RNA_def_property_ui_range(prop, 0.0f, 10.0f, 0.1, 2);
RNA_def_property_ui_text(prop, "Tip", "Strand width at the tip");
RNA_def_property_update(prop, 0, "rna_HairDrawSettings_update");
prop = RNA_def_property(srna, "radius_scale", PROP_FLOAT, PROP_NONE);
RNA_def_property_range(prop, 0.0f, FLT_MAX);
RNA_def_property_ui_range(prop, 0.0f, 10.0f, 0.1, 2);
RNA_def_property_ui_text(prop, "Scaling", "Multiplier of radius properties");
RNA_def_property_update(prop, 0, "rna_HairDrawSettings_update");
}
void RNA_def_hair(BlenderRNA *brna)
{
rna_def_hair_follicle(brna);
rna_def_hair_pattern(brna);
rna_def_hair_system(brna);
rna_def_hair_draw_settings(brna);
}
#endif

View File

@@ -163,6 +163,7 @@ void RNA_def_linestyle(struct BlenderRNA *brna);
void RNA_def_main(struct BlenderRNA *brna);
void RNA_def_material(struct BlenderRNA *brna);
void RNA_def_mesh(struct BlenderRNA *brna);
void RNA_def_mesh_sample(struct BlenderRNA *brna);
void RNA_def_meta(struct BlenderRNA *brna);
void RNA_def_modifier(struct BlenderRNA *brna);
void RNA_def_nla(struct BlenderRNA *brna);
@@ -201,6 +202,7 @@ void RNA_def_world(struct BlenderRNA *brna);
void RNA_def_movieclip(struct BlenderRNA *brna);
void RNA_def_tracking(struct BlenderRNA *brna);
void RNA_def_mask(struct BlenderRNA *brna);
void RNA_def_hair(struct BlenderRNA *brna);
/* Common Define functions */

View File

@@ -0,0 +1,73 @@
/*
* ***** BEGIN GPL LICENSE BLOCK *****
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* ***** END GPL LICENSE BLOCK *****
*/
/** \file blender/makesrna/intern/rna_mesh_sample.c
* \ingroup RNA
*/
#include <stdlib.h>
#include "MEM_guardedalloc.h"
#include "DNA_meshdata_types.h"
#include "BLI_utildefines.h"
#include "BKE_mesh_sample.h"
#include "RNA_access.h"
#include "RNA_define.h"
#include "RNA_types.h"
#include "rna_internal.h"
#include "WM_types.h"
#ifdef RNA_RUNTIME
#include "WM_api.h"
#include "WM_types.h"
#else
static void rna_def_mesh_sample(BlenderRNA *brna)
{
StructRNA *srna;
PropertyRNA *prop;
srna = RNA_def_struct(brna, "MeshSample", NULL);
RNA_def_struct_sdna(srna, "MeshSample");
RNA_def_struct_ui_text(srna, "Mesh Sample", "Point on a mesh that follows deformation");
prop = RNA_def_property(srna, "vertex_indices", PROP_INT, PROP_UNSIGNED);
RNA_def_property_int_sdna(prop, NULL, "orig_verts");
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
RNA_def_property_ui_text(prop, "Vertex Indices", "Index of the mesh vertices used for interpolation");
}
void RNA_def_mesh_sample(BlenderRNA *brna)
{
rna_def_mesh_sample(brna);
}
#endif

View File

@@ -115,6 +115,7 @@ const EnumPropertyItem rna_enum_object_modifier_type_items[] = {
{eModifierType_DynamicPaint, "DYNAMIC_PAINT", ICON_MOD_DYNAMICPAINT, "Dynamic Paint", ""},
{eModifierType_Explode, "EXPLODE", ICON_MOD_EXPLODE, "Explode", ""},
{eModifierType_Fluidsim, "FLUID_SIMULATION", ICON_MOD_FLUIDSIM, "Fluid Simulation", ""},
{eModifierType_Hair, "HAIR", ICON_STRANDS, "Hair", ""},
{eModifierType_Ocean, "OCEAN", ICON_MOD_OCEAN, "Ocean", ""},
{eModifierType_ParticleInstance, "PARTICLE_INSTANCE", ICON_MOD_PARTICLES, "Particle Instance", ""},
{eModifierType_ParticleSystem, "PARTICLE_SYSTEM", ICON_MOD_PARTICLES, "Particle System", ""},
@@ -281,12 +282,17 @@ const EnumPropertyItem rna_enum_axis_flag_xyz_items[] = {
#ifdef RNA_RUNTIME
#include "BLI_listbase.h"
#include "DNA_particle_types.h"
#include "DNA_curve_types.h"
#include "DNA_smoke_types.h"
#include "DNA_hair_types.h"
#include "DNA_meshdata_types.h"
#include "BKE_cachefile.h"
#include "BKE_context.h"
#include "BKE_hair.h"
#include "BKE_library.h"
#include "BKE_mesh_runtime.h"
#include "BKE_modifier.h"
@@ -295,6 +301,7 @@ const EnumPropertyItem rna_enum_axis_flag_xyz_items[] = {
#include "DEG_depsgraph.h"
#include "DEG_depsgraph_build.h"
#include "DEG_depsgraph_query.h"
#ifdef WITH_ALEMBIC
# include "ABC_alembic.h"
@@ -417,6 +424,8 @@ static StructRNA *rna_Modifier_refine(struct PointerRNA *ptr)
return &RNA_SurfaceDeformModifier;
case eModifierType_WeightedNormal:
return &RNA_WeightedNormalModifier;
case eModifierType_Hair:
return &RNA_HairModifier;
/* Default */
case eModifierType_None:
case eModifierType_ShapeKey:
@@ -1174,6 +1183,58 @@ static void rna_ParticleInstanceModifier_particle_system_set(PointerRNA *ptr, co
CLAMP_MIN(psmd->psys, 1);
}
static void rna_Hair_fiber_curves_clear(HairModifierData *hmd)
{
for (HairModifierFiberCurve* curve = hmd->fiber_curves.first; curve; curve = curve->next)
{
if (curve->verts)
{
MEM_freeN(curve->verts);
}
}
BLI_freelistN(&hmd->fiber_curves);
}
static void rna_Hair_fiber_curves_new(HairModifierData *hmd, ReportList *UNUSED(reports), int numverts)
{
HairModifierFiberCurve *curve = MEM_callocN(sizeof(HairModifierFiberCurve), "hair fiber curve");
curve->numverts = numverts;
curve->verts = MEM_callocN(sizeof(HairFiberVertex) * numverts, "hair fiber curve vertices");
BLI_addtail(&hmd->fiber_curves, curve);
}
static void rna_Hair_fiber_curves_apply(ID *id, HairModifierData *hmd, bContext *C, ReportList *UNUSED(reports))
{
const int totcurves = BLI_listbase_count(&hmd->fiber_curves);
int i = 0;
BKE_hair_fiber_curves_begin(hmd->hair_system, totcurves);
i = 0;
for (HairModifierFiberCurve *curve = hmd->fiber_curves.first; curve; curve = curve->next, ++i)
{
BKE_hair_set_fiber_curve(hmd->hair_system, i, curve->numverts, 0.1, 1.0);
}
BKE_hair_fiber_curves_end(hmd->hair_system);
i = 0;
for (HairModifierFiberCurve *curve = hmd->fiber_curves.first; curve; curve = curve->next)
{
for (int j = 0; j < curve->numverts; ++j, ++i)
{
BKE_hair_set_fiber_vertex(hmd->hair_system, i, curve->verts[j].flag, curve->verts[j].co);
}
}
{
Mesh *scalp = (Mesh *)DEG_get_evaluated_id(CTX_data_depsgraph(C), ((Object*)id)->data);
BKE_hair_bind_follicles(hmd->hair_system, scalp);
}
DEG_id_tag_update(id, OB_RECALC_DATA);
WM_main_add_notifier(NC_OBJECT | ND_MODIFIER, id);
}
#else
static PropertyRNA *rna_def_property_subdivision_common(StructRNA *srna, const char type[])
@@ -4916,6 +4977,52 @@ static void rna_def_modifier_surfacedeform(BlenderRNA *brna)
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
}
static void rna_def_modifier_hair_fiber_curve(BlenderRNA *brna)
{
StructRNA *srna;
PropertyRNA *prop;
srna = RNA_def_struct(brna, "HairModifierFiberCurve", NULL);
RNA_def_struct_ui_text(srna, "Hair Modifier Fiber Curve", "");
RNA_def_struct_sdna(srna, "HairModifierFiberCurve");
prop = RNA_def_property(srna, "vertices", PROP_COLLECTION, PROP_NONE);
RNA_def_property_collection_sdna(prop, NULL, "verts", "numverts");
RNA_def_property_struct_type(prop, "HairModifierFiberVertex");
RNA_def_property_ui_text(prop, "Vertices", "Fiber vertices");
srna = RNA_def_struct(brna, "HairModifierFiberVertex", NULL);
RNA_def_struct_ui_text(srna, "Hair Modifier Fiber Vertex", "");
RNA_def_struct_sdna(srna, "HairFiberVertex");
prop = RNA_def_property(srna, "location", PROP_FLOAT, PROP_TRANSLATION);
RNA_def_property_float_sdna(prop, NULL, "co");
RNA_def_property_ui_text(prop, "Location", "Location of the vertex relative to the root");
}
static void rna_def_modifier_hair_fiber_curves_api(BlenderRNA *brna, PropertyRNA *cprop)
{
StructRNA *srna;
FunctionRNA *func;
PropertyRNA *parm;
RNA_def_property_srna(cprop, "HairModifierFiberCurves");
srna = RNA_def_struct(brna, "HairModifierFiberCurves", NULL);
RNA_def_struct_ui_text(srna, "Hair Modifier Fiber Curves", "");
RNA_def_struct_sdna(srna, "HairModifierData");
/*func =*/ RNA_def_function(srna, "clear", "rna_Hair_fiber_curves_clear");
func = RNA_def_function(srna, "new", "rna_Hair_fiber_curves_new");
RNA_def_function_flag(func, FUNC_USE_REPORTS);
parm = RNA_def_int(func, "vertex_count", 2, 0, INT_MAX, "Vertex Count", "Number of vertices", 2, 1000);
RNA_def_property_flag(parm, PARM_REQUIRED);
func = RNA_def_function(srna, "apply", "rna_Hair_fiber_curves_apply");
RNA_def_function_flag(func, FUNC_USE_SELF_ID | FUNC_USE_CONTEXT | FUNC_USE_REPORTS);
}
static void rna_def_modifier_weightednormal(BlenderRNA *brna)
{
StructRNA *srna;
@@ -4978,6 +5085,43 @@ static void rna_def_modifier_weightednormal(BlenderRNA *brna)
RNA_def_property_update(prop, 0, "rna_Modifier_update");
}
static void rna_def_modifier_hair(BlenderRNA *brna)
{
StructRNA *srna;
PropertyRNA *prop;
srna = RNA_def_struct(brna, "HairModifier", "Modifier");
RNA_def_struct_ui_text(srna, "Hair Modifier", "");
RNA_def_struct_sdna(srna, "HairModifierData");
RNA_def_struct_ui_icon(srna, ICON_STRANDS);
prop = RNA_def_property(srna, "hair_system", PROP_POINTER, PROP_NONE);
RNA_def_property_ui_text(prop, "Hair", "Hair data");
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
prop = RNA_def_property(srna, "follicle_seed", PROP_INT, PROP_NONE);
RNA_def_property_range(prop, 0, INT_MAX);
RNA_def_property_ui_text(prop, "Seed", "Follicle distribution random seed value");
prop = RNA_def_property(srna, "follicle_count", PROP_INT, PROP_NONE);
RNA_def_property_int_default(prop, 100000);
RNA_def_property_range(prop, 0, INT_MAX);
RNA_def_property_ui_range(prop, 1, 1e5, 1, 1);
RNA_def_property_ui_text(prop, "Follicle Count", "Maximum number of follicles");
prop = RNA_def_property(srna, "draw_settings", PROP_POINTER, PROP_NONE);
RNA_def_property_ui_text(prop, "Draw Settings", "Hair draw settings");
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
prop = RNA_def_property(srna, "fiber_curves", PROP_COLLECTION, PROP_NONE);
RNA_def_property_collection_sdna(prop, NULL, "fiber_curves", NULL);
RNA_def_property_struct_type(prop, "HairModifierFiberCurve");
RNA_def_property_ui_text(prop, "Fiber Curves", "Fiber curve data");
rna_def_modifier_hair_fiber_curves_api(brna, prop);
rna_def_modifier_hair_fiber_curve(brna);
}
void RNA_def_modifier(BlenderRNA *brna)
{
StructRNA *srna;
@@ -5100,6 +5244,7 @@ void RNA_def_modifier(BlenderRNA *brna)
rna_def_modifier_meshseqcache(brna);
rna_def_modifier_surfacedeform(brna);
rna_def_modifier_weightednormal(brna);
rna_def_modifier_hair(brna);
}
#endif

View File

@@ -63,6 +63,7 @@ set(SRC
intern/MOD_explode.c
intern/MOD_fluidsim.c
intern/MOD_fluidsim_util.c
intern/MOD_hair.c
intern/MOD_hook.c
intern/MOD_laplaciandeform.c
intern/MOD_laplaciansmooth.c

View File

@@ -87,6 +87,7 @@ extern ModifierTypeInfo modifierType_CorrectiveSmooth;
extern ModifierTypeInfo modifierType_MeshSequenceCache;
extern ModifierTypeInfo modifierType_SurfaceDeform;
extern ModifierTypeInfo modifierType_WeightedNormal;
extern ModifierTypeInfo modifierType_Hair;
/* MOD_util.c */
void modifier_type_init(ModifierTypeInfo *types[]);

View File

@@ -0,0 +1,169 @@
/*
* ***** BEGIN GPL LICENSE BLOCK *****
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* The Original Code is Copyright (C) 2005 by the Blender Foundation.
* All rights reserved.
*
* Contributor(s): Daniel Dunbar
* Ton Roosendaal,
* Ben Batt,
* Brecht Van Lommel,
* Campbell Barton
*
* ***** END GPL LICENSE BLOCK *****
*
*/
/** \file blender/modifiers/intern/MOD_hair.c
* \ingroup modifiers
*/
#include "MEM_guardedalloc.h"
#include "BLI_utildefines.h"
#include "BLI_listbase.h"
#include "DNA_object_types.h"
#include "DNA_hair_types.h"
#include "BKE_hair.h"
#include "BKE_library.h"
#include "BKE_library_query.h"
#include "BKE_modifier.h"
#include "DEG_depsgraph_build.h"
#include "MOD_util.h"
static void initData(ModifierData *md)
{
HairModifierData *hmd = (HairModifierData *) md;
hmd->hair_system = BKE_hair_new();
hmd->flag |= 0;
hmd->follicle_count = 100000;
hmd->draw_settings = BKE_hair_draw_settings_new();
}
static void copyData(const ModifierData *md, ModifierData *target, const int flag)
{
const HairModifierData *hmd = (HairModifierData *) md;
HairModifierData *tfmd = (HairModifierData *) target;
modifier_copyData_generic(md, target, flag);
if (hmd->hair_system) {
tfmd->hair_system = BKE_hair_copy(hmd->hair_system);
}
if (hmd->draw_settings)
{
tfmd->draw_settings = BKE_hair_draw_settings_copy(hmd->draw_settings);
}
}
static void freeData(ModifierData *md)
{
HairModifierData *hmd = (HairModifierData *) md;
if (hmd->hair_system) {
BKE_hair_free(hmd->hair_system);
}
if (hmd->draw_settings)
{
BKE_hair_draw_settings_free(hmd->draw_settings);
}
for (HairModifierFiberCurve *curve = hmd->fiber_curves.first; curve; curve = curve->next)
{
if (curve->verts)
{
MEM_freeN(curve->verts);
}
}
BLI_freelistN(&hmd->fiber_curves);
}
static struct Mesh *applyModifier(ModifierData *md, const ModifierEvalContext *ctx,
struct Mesh *mesh)
{
HairModifierData *hmd = (HairModifierData *) md;
UNUSED_VARS(hmd, ctx);
return mesh;
}
static void foreachObjectLink(
ModifierData *md,
Object *ob,
ObjectWalkFunc walk,
void *userData)
{
HairModifierData *hmd = (HairModifierData *) md;
UNUSED_VARS(ob, walk, userData, hmd);
}
static void foreachIDLink(
ModifierData *md,
Object *ob,
IDWalkFunc walk,
void *userData)
{
HairModifierData *hmd = (HairModifierData *) md;
UNUSED_VARS(hmd);
foreachObjectLink(md, ob, (ObjectWalkFunc)walk, userData);
}
ModifierTypeInfo modifierType_Hair = {
/* name */ "Hair",
/* structName */ "HairModifierData",
/* structSize */ sizeof(HairModifierData),
/* type */ eModifierTypeType_NonGeometrical,
/* flags */ eModifierTypeFlag_AcceptsMesh |
eModifierTypeFlag_SupportsEditmode,
/* copyData */ copyData,
/* deformVerts_DM */ NULL,
/* deformMatrices_DM */ NULL,
/* deformVertsEM_DM */ NULL,
/* deformMatricesEM_DM*/NULL,
/* applyModifier_DM */ NULL,
/* applyModifierEM_DM */NULL,
/* deformVerts */ NULL,
/* deformMatrices */ NULL,
/* deformVertsEM */ NULL,
/* deformMatricesEM */ NULL,
/* applyModifier */ applyModifier,
/* applyModifierEM */ NULL,
/* initData */ initData,
/* requiredDataMask */ NULL,
/* freeData */ freeData,
/* isDisabled */ NULL,
/* updateDepsgraph */ NULL,
/* dependsOnTime */ NULL,
/* dependsOnNormals */ NULL,
/* foreachObjectLink */ foreachObjectLink,
/* foreachIDLink */ foreachIDLink,
/* foreachTexLink */ NULL,
};

View File

@@ -291,5 +291,6 @@ void modifier_type_init(ModifierTypeInfo *types[])
INIT_TYPE(MeshSequenceCache);
INIT_TYPE(SurfaceDeform);
INIT_TYPE(WeightedNormal);
INIT_TYPE(Hair);
#undef INIT_TYPE
}

View File

@@ -13,6 +13,7 @@ if(WITH_GTESTS)
add_subdirectory(testing)
add_subdirectory(blenlib)
add_subdirectory(blenkernel)
add_subdirectory(guardedalloc)
add_subdirectory(bmesh)
if(WITH_ALEMBIC)

View File

@@ -0,0 +1,334 @@
/* Apache License, Version 2.0 */
#include <iostream>
#include <fstream>
#include <sstream>
#include "testing/testing.h"
#include "BKE_mesh_test_util.h"
extern "C" {
#include "MEM_guardedalloc.h"
#include "BLI_math.h"
#include "BLI_path_util.h"
#include "BLI_rand.h"
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
#include "BKE_appdir.h"
#include "BKE_global.h"
#include "BKE_main.h"
#include "BKE_mesh.h"
#include "BKE_mesh_sample.h"
}
#define TEST_MESH_OUTPUT_FILE "mesh_dump_"
static const float verts[][3] = { {-1, -1, -1}, {1, -1, -1}, {-1, 1, -1}, {1, 1, -1},
{-1, -1, 1}, {1, -1, 1}, {-1, 1, 1}, {1, 1, 1} };
static const int faces[] = { 0, 1, 3, 2,
4, 5, 7, 6,
0, 1, 5, 4,
2, 3, 7, 6,
0, 2, 6, 4,
1, 3, 7, 5,
};
static const int face_lengths[] = { 4, 4, 4, 4, 4, 4 };
class MeshSampleTest : public ::testing::Test
{
public:
static const unsigned int m_seed;
static const int m_numsamples;
std::string get_testname() const;
void load_mesh(const float (*verts)[3], int numverts,
const int (*edges)[2], int numedges,
const int *faces, const int *face_lengths, int numfaces);
void load_mesh(const char *filename);
void unload_mesh();
void generate_samples_simple(struct MeshSampleGenerator *gen);
void generate_samples_batch(struct MeshSampleGenerator *gen);
void generate_samples_batch_threaded(struct MeshSampleGenerator *gen);
void compare_samples(const struct MeshSample *ground_truth);
void test_samples(struct MeshSampleGenerator *gen, const struct MeshSample *ground_truth, int count);
void dump_samples();
protected:
void SetUp() override;
void TearDown() override;
protected:
Mesh *m_mesh;
MeshSample *m_samples;
};
const unsigned int MeshSampleTest::m_seed = 8343;
const int MeshSampleTest::m_numsamples = 100000;
std::string MeshSampleTest::get_testname() const
{
std::stringstream testname;
testname << ::testing::UnitTest::GetInstance()->current_test_info()->name();
return testname.str();
}
void MeshSampleTest::load_mesh(const float (*verts)[3], int numverts,
const int (*edges)[2], int numedges,
const int *faces, const int *face_lengths, int numfaces)
{
m_mesh = BKE_mesh_test_from_data(verts, numverts, edges, numedges, faces, face_lengths, numfaces);
}
void MeshSampleTest::load_mesh(const char *filename)
{
const char *folder = BKE_appdir_folder_id(BLENDER_DATAFILES, "tests");
char path[FILE_MAX];
BLI_make_file_string(G.main->name, path, folder, filename);
if (path[0]) {
m_mesh = BKE_mesh_test_from_csv(path);
}
}
void MeshSampleTest::unload_mesh()
{
if (m_mesh) {
BKE_mesh_free(m_mesh);
MEM_freeN(m_mesh);
m_mesh = NULL;
}
}
void MeshSampleTest::SetUp()
{
load_mesh("suzanne.csv");
if (!m_mesh) {
int numverts = ARRAY_SIZE(verts);
int numfaces = ARRAY_SIZE(face_lengths);
load_mesh(verts, numverts, NULL, 0, faces, face_lengths, numfaces);
}
m_samples = (MeshSample *)MEM_mallocN(sizeof(MeshSample) * m_numsamples, "mesh samples");
}
void MeshSampleTest::TearDown()
{
if (m_samples) {
MEM_freeN(m_samples);
m_samples = NULL;
}
unload_mesh();
}
void MeshSampleTest::dump_samples()
{
#ifdef TEST_MESH_OUTPUT_FILE
int numverts = m_mesh->totvert;
int dbg_numverts = numverts + m_numsamples;
float (*dbg_verts)[3] = (float (*)[3])MEM_mallocN(sizeof(float[3]) * dbg_numverts, "vertices");
for (int i = 0; i < numverts; ++i) {
copy_v3_v3(dbg_verts[i], m_mesh->mvert[i].co);
}
for (int i = 0; i < m_numsamples; ++i) {
float nor[3], tang[3];
BKE_mesh_sample_eval(m_mesh, &m_samples[i], dbg_verts[numverts + i], nor, tang);
}
int *dbg_faces = (int *)MEM_mallocN(sizeof(int) * m_mesh->totloop, "faces");
int *dbg_face_lengths = (int *)MEM_mallocN(sizeof(int) * m_mesh->totpoly, "face_lengths");
int loopstart = 0;
for (int i = 0; i < m_mesh->totpoly; ++i) {
const MPoly *mp = &m_mesh->mpoly[i];
dbg_face_lengths[i] = mp->totloop;
for (int k = 0; k < mp->totloop; ++k) {
dbg_faces[loopstart + k] = m_mesh->mloop[mp->loopstart + k].v;
}
loopstart += mp->totloop;
}
Mesh *dbg_mesh = BKE_mesh_test_from_data(dbg_verts, dbg_numverts, NULL, 0, dbg_faces, dbg_face_lengths, m_mesh->totpoly);
MEM_freeN(dbg_verts);
MEM_freeN(dbg_faces);
MEM_freeN(dbg_face_lengths);
std::stringstream filename;
filename << TEST_MESH_OUTPUT_FILE << get_testname() << ".py";
std::fstream s(filename.str(), s.trunc | s.out);
BKE_mesh_test_dump_mesh(dbg_mesh, get_testname().c_str(), s);
BKE_mesh_free(dbg_mesh);
MEM_freeN(dbg_mesh);
#endif
}
void MeshSampleTest::compare_samples(const MeshSample *ground_truth)
{
for (int i = 0; i < m_numsamples; ++i) {
EXPECT_EQ(ground_truth[i].orig_verts[0], m_samples[i].orig_verts[0]);
EXPECT_EQ(ground_truth[i].orig_verts[1], m_samples[i].orig_verts[1]);
EXPECT_EQ(ground_truth[i].orig_verts[2], m_samples[i].orig_verts[2]);
EXPECT_EQ(ground_truth[i].orig_weights[0], m_samples[i].orig_weights[0]);
EXPECT_EQ(ground_truth[i].orig_weights[1], m_samples[i].orig_weights[1]);
EXPECT_EQ(ground_truth[i].orig_weights[2], m_samples[i].orig_weights[2]);
}
}
void MeshSampleTest::generate_samples_simple(MeshSampleGenerator *gen)
{
for (int i = 0; i < m_numsamples; ++i) {
BKE_mesh_sample_generate(gen, &m_samples[i]);
}
}
void MeshSampleTest::generate_samples_batch(MeshSampleGenerator *gen)
{
BKE_mesh_sample_generate_batch_ex(gen, m_samples, sizeof(MeshSample), m_numsamples, false);
}
void MeshSampleTest::generate_samples_batch_threaded(MeshSampleGenerator *gen)
{
BKE_mesh_sample_generate_batch_ex(gen, m_samples, sizeof(MeshSample), m_numsamples, true);
}
void MeshSampleTest::test_samples(MeshSampleGenerator *gen, const MeshSample *ground_truth, int count)
{
BKE_mesh_sample_generator_bind(gen, m_mesh);
if (ground_truth) {
EXPECT_EQ(count, m_numsamples) << "Ground truth size does not match number of samples";
if (count != m_numsamples) {
return;
}
generate_samples_simple(gen);
compare_samples(ground_truth);
}
else {
generate_samples_simple(gen);
// Use simple sample generation as ground truth if not provided explicitly
ground_truth = m_samples;
}
generate_samples_batch(gen);
compare_samples(ground_truth);
generate_samples_batch_threaded(gen);
compare_samples(ground_truth);
BKE_mesh_sample_generator_unbind(gen);
}
TEST_F(MeshSampleTest, SurfaceVertices)
{
MeshSampleGenerator *gen = BKE_mesh_sample_gen_surface_vertices();
ASSERT_TRUE(gen != NULL) << "No generator created";
test_samples(gen, NULL, 0);
dump_samples();
BKE_mesh_sample_free_generator(gen);
}
TEST_F(MeshSampleTest, SurfaceRandom)
{
MeshSampleGenerator *gen = BKE_mesh_sample_gen_surface_random(m_seed, true, NULL, NULL);
ASSERT_TRUE(gen != NULL) << "No generator created";
test_samples(gen, NULL, 0);
dump_samples();
BKE_mesh_sample_free_generator(gen);
}
const float poisson_disk_mindist = 0.01f;
TEST_F(MeshSampleTest, SurfacePoissonDisk)
{
MeshSampleGenerator *gen = BKE_mesh_sample_gen_surface_poissondisk(m_seed, poisson_disk_mindist, 10000000, NULL, NULL);
ASSERT_TRUE(gen != NULL) << "No generator created";
test_samples(gen, NULL, 0);
dump_samples();
BKE_mesh_sample_free_generator(gen);
}
extern "C" {
static const unsigned int raycast_seed = 85344;
static const float raycast_radius = 100.0f;
static void* raycast_thread_context_create(void *UNUSED(userdata), int start)
{
RNG *rng = BLI_rng_new(raycast_seed);
BLI_rng_skip(rng, start * 2);
return rng;
}
static void raycast_thread_context_free(void *UNUSED(userdata), void *thread_ctx)
{
BLI_rng_free((RNG *)thread_ctx);
}
static bool raycast_ray(void *UNUSED(userdata), void *thread_ctx, float ray_start[3], float ray_end[3])
{
RNG *rng = (RNG *)thread_ctx;
float v[3];
{
float r;
v[2] = (2.0f * BLI_rng_get_float(rng)) - 1.0f;
float a = (float)(M_PI * 2.0) * BLI_rng_get_float(rng);
if ((r = 1.0f - (v[2] * v[2])) > 0.0f) {
r = sqrtf(r);
v[0] = r * cosf(a);
v[1] = r * sinf(a);
}
else {
v[2] = 1.0f;
}
}
mul_v3_fl(v, raycast_radius);
copy_v3_v3(ray_start, v);
negate_v3_v3(ray_end, v);
return true;
}
} /*extern "C"*/
TEST_F(MeshSampleTest, SurfaceRaycast)
{
MeshSampleGenerator *gen = BKE_mesh_sample_gen_surface_raycast(
raycast_thread_context_create, raycast_thread_context_free, raycast_ray, NULL);
ASSERT_TRUE(gen != NULL) << "No generator created";
test_samples(gen, NULL, 0);
dump_samples();
BKE_mesh_sample_free_generator(gen);
}
static const float volume_bbray_density = 0.1f;
TEST_F(MeshSampleTest, VolumeBBRay)
{
MeshSampleGenerator *gen = BKE_mesh_sample_gen_volume_random_bbray(m_seed, volume_bbray_density);
ASSERT_TRUE(gen != NULL) << "No generator created";
test_samples(gen, NULL, 0);
dump_samples();
BKE_mesh_sample_free_generator(gen);
}

View File

@@ -0,0 +1,319 @@
/* Apache License, Version 2.0 */
#include <fstream>
#include <iostream>
#include <iomanip>
#include "BKE_mesh_test_util.h"
extern "C" {
#include "MEM_guardedalloc.h"
#include "BLI_math.h"
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
#include "BKE_customdata.h"
#include "BKE_mesh.h"
}
static void mesh_update(Mesh *mesh, int calc_edges, int calc_tessface)
{
bool tessface_input = false;
if (mesh->totface > 0 && mesh->totpoly == 0) {
BKE_mesh_convert_mfaces_to_mpolys(mesh);
/* would only be converting back again, don't bother */
tessface_input = true;
}
if (calc_edges || ((mesh->totpoly || mesh->totface) && mesh->totedge == 0))
BKE_mesh_calc_edges(mesh, calc_edges, true);
if (calc_tessface) {
if (tessface_input == false) {
BKE_mesh_tessface_calc(mesh);
}
}
else {
/* default state is not to have tessface's so make sure this is the case */
BKE_mesh_tessface_clear(mesh);
}
BKE_mesh_calc_normals(mesh);
}
static void mesh_add_verts(Mesh *mesh, int len)
{
CustomData vdata;
if (len == 0)
return;
int totvert = mesh->totvert + len;
CustomData_copy(&mesh->vdata, &vdata, CD_MASK_MESH, CD_DEFAULT, totvert);
CustomData_copy_data(&mesh->vdata, &vdata, 0, 0, mesh->totvert);
if (!CustomData_has_layer(&vdata, CD_MVERT))
CustomData_add_layer(&vdata, CD_MVERT, CD_CALLOC, NULL, totvert);
CustomData_free(&mesh->vdata, mesh->totvert);
mesh->vdata = vdata;
BKE_mesh_update_customdata_pointers(mesh, false);
/* scan the input list and insert the new vertices */
/* set final vertex list size */
mesh->totvert = totvert;
}
static void mesh_add_edges(Mesh *mesh, int len)
{
CustomData edata;
MEdge *medge;
int i, totedge;
if (len == 0)
return;
totedge = mesh->totedge + len;
/* update customdata */
CustomData_copy(&mesh->edata, &edata, CD_MASK_MESH, CD_DEFAULT, totedge);
CustomData_copy_data(&mesh->edata, &edata, 0, 0, mesh->totedge);
if (!CustomData_has_layer(&edata, CD_MEDGE))
CustomData_add_layer(&edata, CD_MEDGE, CD_CALLOC, NULL, totedge);
CustomData_free(&mesh->edata, mesh->totedge);
mesh->edata = edata;
BKE_mesh_update_customdata_pointers(mesh, false); /* new edges don't change tessellation */
/* set default flags */
medge = &mesh->medge[mesh->totedge];
for (i = 0; i < len; i++, medge++)
medge->flag = ME_EDGEDRAW | ME_EDGERENDER;
mesh->totedge = totedge;
}
static void mesh_add_loops(Mesh *mesh, int len)
{
CustomData ldata;
int totloop;
if (len == 0)
return;
totloop = mesh->totloop + len; /* new face count */
/* update customdata */
CustomData_copy(&mesh->ldata, &ldata, CD_MASK_MESH, CD_DEFAULT, totloop);
CustomData_copy_data(&mesh->ldata, &ldata, 0, 0, mesh->totloop);
if (!CustomData_has_layer(&ldata, CD_MLOOP))
CustomData_add_layer(&ldata, CD_MLOOP, CD_CALLOC, NULL, totloop);
CustomData_free(&mesh->ldata, mesh->totloop);
mesh->ldata = ldata;
BKE_mesh_update_customdata_pointers(mesh, true);
mesh->totloop = totloop;
}
static void mesh_add_polys(Mesh *mesh, int len)
{
CustomData pdata;
MPoly *mpoly;
int i, totpoly;
if (len == 0)
return;
totpoly = mesh->totpoly + len; /* new face count */
/* update customdata */
CustomData_copy(&mesh->pdata, &pdata, CD_MASK_MESH, CD_DEFAULT, totpoly);
CustomData_copy_data(&mesh->pdata, &pdata, 0, 0, mesh->totpoly);
if (!CustomData_has_layer(&pdata, CD_MPOLY))
CustomData_add_layer(&pdata, CD_MPOLY, CD_CALLOC, NULL, totpoly);
CustomData_free(&mesh->pdata, mesh->totpoly);
mesh->pdata = pdata;
BKE_mesh_update_customdata_pointers(mesh, true);
/* set default flags */
mpoly = &mesh->mpoly[mesh->totpoly];
for (i = 0; i < len; i++, mpoly++)
mpoly->flag = ME_FACE_SEL;
mesh->totpoly = totpoly;
}
Mesh* BKE_mesh_test_from_data(
const float (*verts)[3], int numverts,
const int (*edges)[2], int numedges,
const int *loops, const int *face_lengths, int numfaces)
{
Mesh *me = (Mesh *)MEM_callocN(sizeof(Mesh), "Mesh");
BKE_mesh_init(me);
int numloops = 0;
for (int i = 0; i < numfaces; ++i) {
numloops += face_lengths[i];
}
mesh_add_verts(me, numverts);
mesh_add_edges(me, numedges);
mesh_add_loops(me, numloops);
mesh_add_polys(me, numfaces);
{
MVert *v = me->mvert;
for (int i = 0; i < numverts; ++i, ++v) {
copy_v3_v3(v->co, verts[i]);
}
}
{
MEdge *e = me->medge;
for (int i = 0; i < numedges; ++i, ++e) {
e->v1 = edges[i][0];
e->v2 = edges[i][1];
}
}
{
MLoop *l = me->mloop;
for (int i = 0; i < numloops; ++i, ++l) {
l->v = loops[i];
}
}
{
MPoly *p = me->mpoly;
int loopstart = 0;
for (int i = 0; i < numfaces; ++i, ++p) {
int totloop = face_lengths[i];
p->loopstart = loopstart;
p->totloop = totloop;
loopstart += totloop;
}
}
if (numfaces > 0 && numedges == 0) {
mesh_update(me, true, false);
}
return me;
}
Mesh* BKE_mesh_test_from_csv(const char *filename)
{
std::ifstream ifs (filename, std::ifstream::in);
char delim;
int numverts, numloops, numfaces;
float (*verts)[3] = NULL;
int *loops = NULL;
int *face_lengths = NULL;
ifs >> numverts;
ifs >> delim;
verts = (float (*)[3])MEM_mallocN(sizeof(float[3]) * numverts, "verts");
for (int i = 0; i < numverts; ++i) {
ifs >> verts[i][0];
ifs >> delim;
ifs >> verts[i][1];
ifs >> delim;
ifs >> verts[i][2];
ifs >> delim;
}
ifs >> numloops;
ifs >> delim;
loops = (int *)MEM_mallocN(sizeof(int) * numloops, "loops");
for (int i = 0; i < numloops; ++i) {
ifs >> loops[i];
ifs >> delim;
}
ifs >> numfaces;
ifs >> delim;
face_lengths = (int *)MEM_mallocN(sizeof(int) * numfaces, "face_lengths");
for (int i = 0; i < numfaces; ++i) {
ifs >> face_lengths[i];
ifs >> delim;
}
return BKE_mesh_test_from_data(verts, numverts, NULL, 0, loops, face_lengths, numfaces);
}
void BKE_mesh_test_dump_verts(Mesh *me, std::ostream &str)
{
int numverts = me->totvert;
str << std::setprecision(5);
MVert *v = me->mvert;
str << "[";
for (int i = 0; i < numverts; ++i, ++v) {
str << "(" << v->co[0] << ", " << v->co[1] << ", " << v->co[2] << "), ";
}
str << "]";
}
void BKE_mesh_test_dump_edges(Mesh *me, std::ostream &str)
{
int numedges = me->totedge;
MEdge *e = me->medge;
str << "[";
for (int i = 0; i < numedges; ++i, ++e) {
str << "(" << e->v1 << ", " << e->v2 << "), ";
}
str << "]";
}
void BKE_mesh_test_dump_faces(Mesh *me, std::ostream &str)
{
int numpolys = me->totpoly;
MPoly *p = me->mpoly;
str << "[";
for (int i = 0; i < numpolys; ++i, ++p) {
int totloop = p->totloop;
MLoop *l = me->mloop + p->loopstart;
str << "(";
for (int k = 0; k < totloop; ++k, ++l) {
str << l->v << ", ";
}
str << "), ";
}
str << "]";
}
void BKE_mesh_test_dump_mesh(Mesh *me, const char *name, std::ostream &str)
{
str << "import bpy\n";
str << "from bpy_extras.object_utils import object_data_add\n";
str << "mesh = bpy.data.meshes.new(name=\"" << name << "\")\n";
str << "mesh.from_pydata(";
str << "vertices=";
BKE_mesh_test_dump_verts(me, str);
str << ", edges=";
BKE_mesh_test_dump_edges(me, str);
str << ", faces=";
BKE_mesh_test_dump_faces(me, str);
str << ")\n";
str << "object_data_add(bpy.context, mesh)\n";
}

View File

@@ -0,0 +1,17 @@
/* Apache License, Version 2.0 */
#include <iosfwd>
struct Mesh;
struct Mesh* BKE_mesh_test_from_data(
const float (*verts)[3], int numverts,
const int (*edges)[2], int numedges,
const int *loops, const int *face_lengths, int numfaces);
struct Mesh* BKE_mesh_test_from_csv(const char *filename);
void BKE_mesh_test_dump_verts(struct Mesh *me, std::ostream &str);
void BKE_mesh_test_dump_edges(struct Mesh *me, std::ostream &str);
void BKE_mesh_test_dump_faces(struct Mesh *me, std::ostream &str);
void BKE_mesh_test_dump_mesh(struct Mesh *me, const char *name, std::ostream &str);

View 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.
#
# The Original Code is Copyright (C) 2014, Blender Foundation
# All rights reserved.
#
# Contributor(s): Sergey Sharybin
#
# ***** END GPL LICENSE BLOCK *****
set(INC
.
..
../../../source/blender/blenlib
../../../source/blender/blenkernel
../../../source/blender/makesdna
../../../source/blender/depsgraph
../../../intern/guardedalloc
)
include_directories(${INC})
set(SRC
BKE_mesh_test_util.cc
BKE_mesh_sample_test.cc
BKE_mesh_test_util.h
)
setup_libdirs()
get_property(BLENDER_SORTED_LIBS GLOBAL PROPERTY BLENDER_SORTED_LIBS_PROP)
if(WITH_BUILDINFO)
set(_buildinfo_src "$<TARGET_OBJECTS:buildinfoobj>")
else()
set(_buildinfo_src "")
endif()
# For motivation on doubling BLENDER_SORTED_LIBS, see ../bmesh/CMakeLists.txt
BLENDER_SRC_GTEST(blenkernel "${SRC};${_buildinfo_src}" "${BLENDER_SORTED_LIBS};${BLENDER_SORTED_LIBS}")
unset(_buildinfo_src)
setup_liblinks(blenkernel_test)